diff --git a/firstleg/src/main/java/com/baiyue/firstleg/workflow/WorkflowTabService.java b/firstleg/src/main/java/com/baiyue/firstleg/workflow/WorkflowTabService.java index 1e6a102dc..6a8b56e57 100644 --- a/firstleg/src/main/java/com/baiyue/firstleg/workflow/WorkflowTabService.java +++ b/firstleg/src/main/java/com/baiyue/firstleg/workflow/WorkflowTabService.java @@ -27,7 +27,7 @@ public class WorkflowTabService { if(facts.stream().noneMatch(f->f.tenantId()==fee.tenantId() && f.orderId()==fee.orderId())) throw unavailable("费用确认事实不属于本批入仓来源"); result.putIfAbsent(fee.orderId()+":"+fee.mainStatus(),fee); } - var sources=facts.stream().filter(f->f.departed() || f.clearanceUploaded()).map(f->new WorkflowFacts.Source(f.tenantId(),f.inboundId())).distinct().toList(); + var sources=facts.stream().filter(WorkflowFacts.Fact::departed).map(f->new WorkflowFacts.Source(f.tenantId(),f.inboundId())).distinct().toList(); Map overseas=new HashMap<>(); if(!sources.isEmpty()) { try { @@ -61,11 +61,11 @@ public class WorkflowTabService { Set keys=new LinkedHashSet<>(); if("MEASURE_PENDING".equals(process) && "PENDING".equals(f.reviewStatus())) keys.add("MEASUREMENT_REVIEW"); boolean signed="DELIVERED".equals(process); - // 报关完成即进入待清关;不再等待船期接口补齐实际离港节点。 - boolean clearanceStage=f.customsCompleted() || f.departed() || f.clearanceUploaded(); + // 人工补录或船司查询的实际开船节点才推进阶段,报关完成和提前上传资料不能代替开船。 + boolean clearanceStage=f.departed(); if(!clearanceStage && !signed) { if(f.missingCustoms()) keys.add("WAIT_CUSTOMS_DOCS"); - if(f.exportCustomsReady()) keys.add("EXPORT_CUSTOMS"); + if(f.exportCustomsReady() || f.customsCompleted()) keys.add("EXPORT_CUSTOMS"); } if(signed) keys.add("SIGNED"); if(clearanceStage && !signed) { diff --git a/firstleg/src/test/java/com/baiyue/firstleg/workflow/WorkflowBoundaryTest.java b/firstleg/src/test/java/com/baiyue/firstleg/workflow/WorkflowBoundaryTest.java index ca565dcbe..006955233 100644 --- a/firstleg/src/test/java/com/baiyue/firstleg/workflow/WorkflowBoundaryTest.java +++ b/firstleg/src/test/java/com/baiyue/firstleg/workflow/WorkflowBoundaryTest.java @@ -36,8 +36,8 @@ class WorkflowBoundaryTest { assertThatThrownBy(()->service.rows(List.of(inbound))).isInstanceOf(BizException.class).hasCause(failure); } - /** 未记录离港时,报关完成即可待清关;上传清关资料后也必须查询精确海外来源。 */ - @Test void completedCustomsAndClearanceUploadDoNotWaitForDeparture() { + /** 报关完成或提前上传资料仍留在出口报关,实际开船后才查询精确海外来源。 */ + @Test void completedCustomsAndClearanceUploadWaitForActualDeparture() { var repository=mock(WorkflowFactRepository.class); var downstream=mock(LastlegWorkflowClient.class); var service=new WorkflowTabService(repository,downstream); @@ -45,12 +45,21 @@ class WorkflowBoundaryTest { inbound.setOrderId(101L);inbound.setProcessStatus("LOADED"); when(repository.facts(List.of(1L))).thenReturn(List.of( new WorkflowFacts.Fact(11,1,101,"APPROVED",false,false,false,true,true))); - assertThat(service.rows(List.of(inbound))).extracting(r->r.mainStatus()).containsExactly("SAILING_CLEARANCE"); + assertThat(service.rows(List.of(inbound))).extracting(r->r.mainStatus()).containsExactly("EXPORT_CUSTOMS"); verifyNoInteractions(downstream); when(repository.facts(List.of(1L))).thenReturn(List.of( new WorkflowFacts.Fact(11,1,101,"APPROVED",false,true,false,true,true))); + assertThat(service.rows(List.of(inbound))).extracting(r->r.mainStatus()).containsExactly("EXPORT_CUSTOMS"); + verifyNoInteractions(downstream); + when(repository.facts(List.of(1L))).thenReturn(List.of( + new WorkflowFacts.Fact(11,1,101,"APPROVED",true,false,false,true,true))); when(downstream.read(any(),any())).thenReturn(ApiResponse.success(List.of( new WorkflowFacts.Overseas(11,1,false,false,false,false)))); + assertThat(service.rows(List.of(inbound))).extracting(r->r.mainStatus()).containsExactly("SAILING_CLEARANCE"); + verify(downstream).read(any(),eq(new WorkflowFacts.Request(List.of(new WorkflowFacts.Source(11,1))))); + clearInvocations(downstream); + when(repository.facts(List.of(1L))).thenReturn(List.of( + new WorkflowFacts.Fact(11,1,101,"APPROVED",true,true,false,true,true))); assertThat(service.rows(List.of(inbound))).extracting(r->r.mainStatus()).containsExactly("WAIT_UNLOAD","WAIT_DISPATCH"); verify(downstream).read(any(),eq(new WorkflowFacts.Request(List.of(new WorkflowFacts.Source(11,1))))); } diff --git a/firstleg/src/test/java/com/baiyue/firstleg/workflow/WorkflowBusinessTest.java b/firstleg/src/test/java/com/baiyue/firstleg/workflow/WorkflowBusinessTest.java index e0aa37b6e..fa85c1fdc 100644 --- a/firstleg/src/test/java/com/baiyue/firstleg/workflow/WorkflowBusinessTest.java +++ b/firstleg/src/test/java/com/baiyue/firstleg/workflow/WorkflowBusinessTest.java @@ -3,7 +3,7 @@ import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.*; /** 取消回显、分柜隔离和资料页签回归。 */ class WorkflowBusinessTest { - /** 报关完成进入待清关,清关资料保存后才进入拆柜与排车待办。 */ + /** 实际开船前保留出口报关,实际开船后再按清关资料进入海外待办。 */ @Test void customsDocumentsDepartureAndClearanceDriveTabs() { var missing=new WorkflowFacts.Fact(11,1,101,"APPROVED",false,false,true,false,false); var uploaded=new WorkflowFacts.Fact(11,1,101,"APPROVED",false,false,false,true,false); @@ -18,9 +18,9 @@ class WorkflowBusinessTest { assertThat(WorkflowTabService.classify(uploaded,"LOADED",null)) .contains("EXPORT_CUSTOMS").doesNotContain("WAIT_CUSTOMS_DOCS","SAILING_CLEARANCE","WAIT_UNLOAD"); assertThat(WorkflowTabService.classify(completed,"LOADED",null)) - .containsExactly("SAILING_CLEARANCE"); + .containsExactly("EXPORT_CUSTOMS"); assertThat(WorkflowTabService.classify(uploadedBeforeDeparture,"LOADED",pendingUnload)) - .containsExactly("WAIT_UNLOAD","WAIT_DISPATCH"); + .containsExactly("EXPORT_CUSTOMS"); assertThat(WorkflowTabService.classify(departed,"LOADED",pendingUnload)) .containsExactly("SAILING_CLEARANCE"); assertThat(WorkflowTabService.classify(cleared,"LOADED",pendingUnload))