fix(selfop): publish proven manual sales windows for configured warehouses

This commit is contained in:
2026-10-13 00:30:48 +08:00
parent ae1a809357
commit a84a9b730e
6 changed files with 63 additions and 5 deletions
@@ -115,7 +115,7 @@ public interface OutboundAggregateMapper {
@Select("""
<script>
SELECT id,query_from_utc,query_until_utc FROM ll_selfop_sync_task WHERE scope_id=#{scopeId} AND scope_hash=#{scopeHash}
AND execution_purpose='BUSINESS' AND apply_state='APPLIED'
AND execution_purpose='BUSINESS' AND validation_root_task_id IS NULL AND task_role='WAREHOUSE_SYNC' AND apply_state='APPLIED'
AND query_from_utc IS NOT NULL AND query_until_utc IS NOT NULL
AND JSON_UNQUOTE(JSON_EXTRACT(routing_manifest_json,'$.directoryComplete'))='true'
<if test='id != null'>AND (query_from_utc > #{from} OR (query_from_utc=#{from} AND id > #{id}))</if>
@@ -125,6 +125,15 @@ public interface OutboundAggregateMapper {
List<SelfopSyncTask> coverageBatch(@Param("scopeId") Long scopeId,@Param("scopeHash") String scopeHash,
@Param("from") LocalDateTime from,@Param("id") Long id);
@Select("""
SELECT MIN(query_from_utc) FROM ll_selfop_sync_task
WHERE scope_id=#{scopeId} AND scope_hash=#{scopeHash}
AND execution_purpose='BUSINESS' AND validation_root_task_id IS NULL AND task_role='WAREHOUSE_SYNC' AND apply_state='APPLIED'
AND query_from_utc IS NOT NULL AND query_until_utc > query_from_utc
AND JSON_UNQUOTE(JSON_EXTRACT(routing_manifest_json,'$.directoryComplete'))='true'
""")
LocalDateTime firstProvenCoverageFrom(@Param("scopeId") Long scopeId,@Param("scopeHash") String scopeHash);
@Select("""
<script>
SELECT CAST(#{initial} AS DATETIME) AS covered_from_utc,MAX(until_at) AS covered_until_utc
@@ -136,7 +145,7 @@ public interface OutboundAggregateMapper {
OVER (ORDER BY from_at,until_at ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING) AS previous_until
FROM (
SELECT query_from_utc AS from_at,query_until_utc AS until_at FROM ll_selfop_sync_task
WHERE scope_id=#{scopeId} AND scope_hash=#{scopeHash} AND execution_purpose='BUSINESS' AND apply_state='APPLIED'
WHERE scope_id=#{scopeId} AND scope_hash=#{scopeHash} AND execution_purpose='BUSINESS' AND validation_root_task_id IS NULL AND task_role='WAREHOUSE_SYNC' AND apply_state='APPLIED'
AND query_from_utc IS NOT NULL AND query_until_utc > query_from_utc AND query_until_utc >= #{initial}
AND JSON_UNQUOTE(JSON_EXTRACT(routing_manifest_json,'$.directoryComplete'))='true'
UNION ALL SELECT CAST(#{initial} AS DATETIME),CAST(#{initial} AS DATETIME)
@@ -136,8 +136,12 @@ public class OutboundCandidateTransaction {
SelfopSupport.require(aggregates.invalidFacts(scope.getSourceAccount(),scope.getWarehouseCode(),scope.getSourceTimezone(),scope.getBusinessTimezone(),unit)==0,"FACT_TIME_UNIT_OR_EVIDENCE_INVALID");
}
private OutboundCandidateBuilder.Coverage coverage(SelfopSyncScope scope) {
if(scope.getInitialFromUtc()==null)return new OutboundCandidateBuilder.Coverage(null,null);
SelfopSyncRun result=aggregates.contiguousCoverage(scope.getId(),scope.getScopeHash(),scope.getInitialFromUtc(),scope.getDiscoveryFromUtc(),scope.getDiscoveryUntilUtc());
// A manual first slice has no automatic backfill anchor. Only accepted, fully applied
// coverage may establish its start; an existing planned anchor must never move past gaps.
LocalDateTime initial=scope.getInitialFromUtc();
if(initial==null)initial=aggregates.firstProvenCoverageFrom(scope.getId(),scope.getScopeHash());
if(initial==null)return new OutboundCandidateBuilder.Coverage(null,null);
SelfopSyncRun result=aggregates.contiguousCoverage(scope.getId(),scope.getScopeHash(),initial,scope.getDiscoveryFromUtc(),scope.getDiscoveryUntilUtc());
return result==null?new OutboundCandidateBuilder.Coverage(null,null):new OutboundCandidateBuilder.Coverage(result.getCoveredFromUtc(),result.getCoveredUntilUtc());
}
private Checkpoint checkpoint(SelfopSyncTask task) {
@@ -208,6 +208,7 @@ public class SelfopDiscoveryService {
if(read(parent.getQueryScopeJson(),Query.class).parentTaskId()!=null)return;
tx.executeWithoutResult(s->{leases.verify(lease);if(!manifest.isDirectoryComplete())return;
for(var scope:scopes.selectList(new QueryWrapper<SelfopSyncScope>().eq("source_account",parent.getSourceAccount()).eq("data_family","OUTBOUND"))){
if(!config.getWarehouses().containsKey(scope.getWarehouseCode()))continue;
var own=new SelfopRoutingManifest();own.setDirectoryComplete(true);own.setTotal(0L);own.setPages(manifest.getPages());
for(var e:manifest.getObjects().entrySet()){
var ref=e.getValue();var previous=orders.selectOne(new QueryWrapper<SelfopOutboundOrder>().eq("source_account",parent.getSourceAccount()).eq("customer_code",ref.getCustomerCode()).eq("delivery_no",ref.getDeliveryNo()));