diff --git a/blade-service/blade-transport/src/test/java/org/springblade/transport/service/impl/WaybillConfirmReturnTest.java b/blade-service/blade-transport/src/test/java/org/springblade/transport/service/impl/WaybillConfirmReturnTest.java index 3cfe106f..b753a03b 100644 --- a/blade-service/blade-transport/src/test/java/org/springblade/transport/service/impl/WaybillConfirmReturnTest.java +++ b/blade-service/blade-transport/src/test/java/org/springblade/transport/service/impl/WaybillConfirmReturnTest.java @@ -23,6 +23,7 @@ import org.springblade.transport.pojo.entity.WaybillNodePunch; import org.springblade.transport.service.ILoadingManageService; import org.springblade.transport.service.IProcessConfigService; import org.springblade.transport.service.IReceivablePayableDetailService; +import org.springblade.transport.support.WaybillProcessSupport; import java.lang.reflect.Field; import java.util.List; @@ -37,15 +38,17 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; /** - * 确认回单:回单节点要求业务员确认时,内部人员确认后把运单置为已完成。 + * 确认回单:停在待回单确认的运单,内部人员确认后把运单置为已完成。 *

- * 完整入口不校验节点打卡,故这里必须自己挡住两件事——该运单的司机打卡节点(在途除外)全部已打卡、 - * 无未了结异常;不挡住就成了绕过打卡门禁与异常门禁的通道。回单不要求确认时不能走这个入口。 + * 完整入口不校验节点打卡,故这里必须自己挡住三件事——运单确实停在待回单确认、该运单的司机打卡节点 + * (在途除外)全部已打卡、无未了结异常;不挡住就成了绕过打卡门禁与异常门禁的通道。 + * 判据只看运单状态,不看回单配置当前值。 */ class WaybillConfirmReturnTest { private static final Long WAYBILL_ID = 5701L; private static final String DEPT_ID = "1123598813738675201"; + private static final String PENDING_RETURN_CONFIRM = WaybillProcessSupport.STATUS_PENDING_RETURN_CONFIRM; private static final String RETURN_NEED_CONFIRM_JSON = """ [{"key":"load","name":"装货","type":"cargo","enabled":true,"punch":true}, @@ -118,9 +121,9 @@ class WaybillConfirmReturnTest { } @Test - @DisplayName("回单要求确认且节点都打完:置为已完成,并照常生成应收应付、尝试完成配载单") + @DisplayName("待回单确认且节点都打完:置为已完成,并照常生成应收应付、尝试完成配载单") void confirmsReturnAndCompletesWaybill() { - stubWaybill(RETURN_NEED_CONFIRM_JSON); + stubWaybill(RETURN_NEED_CONFIRM_JSON, PENDING_RETURN_CONFIRM); stubPunches("load", "sign"); boolean result = service.confirmReturnReceipt(WAYBILL_ID); @@ -135,16 +138,57 @@ class WaybillConfirmReturnTest { @Test @DisplayName("在途节点没打卡也放行:它不是必经一步") void transitNodeDoesNotBlockConfirmation() { - stubWaybill(WITH_TRANSIT_JSON); + stubWaybill(WITH_TRANSIT_JSON, PENDING_RETURN_CONFIRM); stubPunches("load", "sign"); assertEquals(true, service.confirmReturnReceipt(WAYBILL_ID)); } + @Test + @DisplayName("进行中的运单不能确认回单:状态校验先于打卡校验") + void runningWaybillCannotConfirmReturn() { + stubWaybill(RETURN_NEED_CONFIRM_JSON, "running"); + // 故意一条打卡都不补:若状态校验排在打卡校验之后,这里报的会是缺哪个节点 + stubPunches(); + + ServiceException exception = assertThrows(ServiceException.class, + () -> service.confirmReturnReceipt(WAYBILL_ID)); + + assertEquals("当前状态不允许确认回单", exception.getMessage()); + verify(waybillMapper, never()).updateById(any(Waybill.class)); + verify(receivablePayableDetailService, never()).generateForCompletedWaybills(any()); + } + + @Test + @DisplayName("已完成的运单不能确认回单") + void completedWaybillCannotConfirmReturn() { + stubWaybill(RETURN_NEED_CONFIRM_JSON, "completed"); + stubPunches("load", "sign"); + + ServiceException exception = assertThrows(ServiceException.class, + () -> service.confirmReturnReceipt(WAYBILL_ID)); + + assertEquals("当前状态不允许确认回单", exception.getMessage()); + verify(waybillMapper, never()).updateById(any(Waybill.class)); + } + + @Test + @DisplayName("待执行的运单不能确认回单") + void pendingWaybillCannotConfirmReturn() { + stubWaybill(RETURN_NEED_CONFIRM_JSON, "pending"); + stubPunches("load", "sign"); + + ServiceException exception = assertThrows(ServiceException.class, + () -> service.confirmReturnReceipt(WAYBILL_ID)); + + assertEquals("当前状态不允许确认回单", exception.getMessage()); + verify(waybillMapper, never()).updateById(any(Waybill.class)); + } + @Test @DisplayName("还有节点没打卡:拒绝并点名缺哪个节点,不改状态") void unfinishedPunchNodeBlocksConfirmation() { - stubWaybill(RETURN_NEED_CONFIRM_JSON); + stubWaybill(RETURN_NEED_CONFIRM_JSON, PENDING_RETURN_CONFIRM); stubPunches("load"); ServiceException exception = assertThrows(ServiceException.class, @@ -157,7 +201,7 @@ class WaybillConfirmReturnTest { @Test @DisplayName("有未了结异常:拒绝,不改状态") void unresolvedExceptionBlocksConfirmation() { - stubWaybill(RETURN_NEED_CONFIRM_JSON); + stubWaybill(RETURN_NEED_CONFIRM_JSON, PENDING_RETURN_CONFIRM); stubPunches("load", "sign"); when(exceptionDisposalMapper.selectCount(any())).thenReturn(1L); @@ -169,31 +213,59 @@ class WaybillConfirmReturnTest { } @Test - @DisplayName("回单不要求业务员确认:这个入口不适用,说清而不是静默完成") - void returnWithoutConfirmationIsRejected() { - stubWaybill(RETURN_NO_CONFIRM_JSON); + @DisplayName("回单配置已改成无需确认、运单停在待回单确认:仍能确认回单") + void confirmStillWorksAfterConfigSwitchedToNoConfirm() { + stubWaybill(RETURN_NO_CONFIRM_JSON, PENDING_RETURN_CONFIRM); + stubPunches("load", "sign"); + + assertEquals(true, service.confirmReturnReceipt(WAYBILL_ID)); + + verify(waybillMapper).updateById(org.mockito.ArgumentMatchers.argThat(waybill -> + "completed".equals(waybill.getBusinessStatus()))); + verify(receivablePayableDetailService).generateForCompletedWaybills(List.of(WAYBILL_ID)); + } + + @Test + @DisplayName("回单配置无需确认且运单还在进行中:这个入口不适用,报状态不符") + void noConfirmConfigRunningWaybillCannotConfirmReturn() { + stubWaybill(RETURN_NO_CONFIRM_JSON, "running"); stubPunches("load", "sign"); ServiceException exception = assertThrows(ServiceException.class, () -> service.confirmReturnReceipt(WAYBILL_ID)); - assertEquals("该运单的回单不需要业务员确认", exception.getMessage()); + assertEquals("当前状态不允许确认回单", exception.getMessage()); verify(waybillMapper, never()).updateById(any(Waybill.class)); } + @Test + @DisplayName("待回单确认可以取消:置为已取消,不生成应收应付") + void cancelPendingReturnConfirmWaybill() { + // 取消接口不允许已配载的运单 + stubWaybill(RETURN_NEED_CONFIRM_JSON, PENDING_RETURN_CONFIRM).setLoadingNo(null); + + assertEquals(true, service.cancel(WAYBILL_ID)); + + verify(waybillMapper).updateById(org.mockito.ArgumentMatchers.argThat(waybill -> + "cancelled".equals(waybill.getBusinessStatus()) && WAYBILL_ID.equals(waybill.getId()))); + verify(receivablePayableDetailService, never()).generateForCompletedWaybills(any()); + verify(loadingManageService, never()).completeIfAllWaybillsCompleted(any()); + } + // ------------------------------------------------------------ 夹具 - private void stubWaybill(String processJson) { + private Waybill stubWaybill(String processJson, String businessStatus) { Waybill waybill = new Waybill(); waybill.setId(WAYBILL_ID); waybill.setWaybillNo("YD202610180002"); waybill.setDeptId(Long.valueOf(DEPT_ID)); waybill.setProjectId(7001L); waybill.setProcessJson(processJson); - waybill.setBusinessStatus("running"); + waybill.setBusinessStatus(businessStatus); waybill.setLoadingNo("PZ-20261018-001"); waybill.setIsDeleted(0); when(waybillMapper.selectById(WAYBILL_ID)).thenReturn(waybill); + return waybill; } private void stubPunches(String... nodeCodes) { diff --git a/blade-service/blade-transport/src/test/java/org/springblade/transport/service/impl/WaybillPunchRecordListTest.java b/blade-service/blade-transport/src/test/java/org/springblade/transport/service/impl/WaybillPunchRecordListTest.java index c61dd1ec..6f055f13 100644 --- a/blade-service/blade-transport/src/test/java/org/springblade/transport/service/impl/WaybillPunchRecordListTest.java +++ b/blade-service/blade-transport/src/test/java/org/springblade/transport/service/impl/WaybillPunchRecordListTest.java @@ -14,6 +14,7 @@ import org.springblade.transport.pojo.entity.Waybill; import org.springblade.transport.pojo.entity.WaybillEnroutePunch; import org.springblade.transport.pojo.entity.WaybillNodePunch; import org.springblade.transport.pojo.vo.WaybillPunchRecordItemVO; +import org.springblade.transport.support.WaybillProcessSupport; import java.lang.reflect.Field; import java.text.ParseException; @@ -126,6 +127,19 @@ class WaybillPunchRecordListTest { assertEquals("", accept.getPunchTime(), "没有接单动作就没有完成时刻,不得拿别的字段编一个"); } + @Test + @DisplayName("待回单确认的无需确认接单运单:接单节点已打卡,但没有完成时刻") + void acceptNodePunchedWhenPendingReturnConfirm() { + stubWaybill(waybill(NO_CONFIRM_ACCEPT_JSON, + WaybillProcessSupport.STATUS_PENDING_RETURN_CONFIRM, null)); + + WaybillPunchRecordItemVO accept = acceptRecord(); + + assertTrue(accept.getPunched()); + assertEquals("已打卡", accept.getStatusName()); + assertEquals("", accept.getPunchTime()); + } + @Test @DisplayName("无需确认接单但仍是草稿:接单节点未打卡") void acceptNodeUnpunchedWhenDraft() { diff --git a/blade-service/blade-transport/src/test/java/org/springblade/transport/service/impl/WaybillPunchRecordSaveTest.java b/blade-service/blade-transport/src/test/java/org/springblade/transport/service/impl/WaybillPunchRecordSaveTest.java index 1778a850..fc7b0f38 100644 --- a/blade-service/blade-transport/src/test/java/org/springblade/transport/service/impl/WaybillPunchRecordSaveTest.java +++ b/blade-service/blade-transport/src/test/java/org/springblade/transport/service/impl/WaybillPunchRecordSaveTest.java @@ -68,6 +68,7 @@ class WaybillPunchRecordSaveTest { assistant.setCurrentNamespace("test"); TableInfoHelper.initTableInfo(assistant, WaybillNodePunch.class); TableInfoHelper.initTableInfo(assistant, WaybillEnroutePunch.class); + TableInfoHelper.initTableInfo(assistant, Waybill.class); } @BeforeEach @@ -79,6 +80,7 @@ class WaybillPunchRecordSaveTest { inject("baseMapper", waybillMapper); inject("waybillNodePunchMapper", nodePunchMapper); inject("waybillEnroutePunchMapper", enroutePunchMapper); + when(waybillMapper.updateById(any(Waybill.class))).thenReturn(1); } // ------------------------------------------------------------ 过程节点打卡 @@ -202,6 +204,61 @@ class WaybillPunchRecordSaveTest { assertEquals(Set.of(7001L), new HashSet<>(captor.getValue())); } + @Test + @DisplayName("待回单确认:取消打卡后退回进行中——进入该状态的前提已被撤掉") + void revertToRunningWhenCancelPunchOnPendingReturnConfirm() { + Waybill waybill = waybill(); + waybill.setBusinessStatus(WaybillProcessSupport.STATUS_PENDING_RETURN_CONFIRM); + when(waybillMapper.selectById(WAYBILL_ID)).thenReturn(waybill); + when(nodePunchMapper.selectList(any())).thenReturn(List.of(nodePunch(7001L, "load", "装货"))); + + WaybillPunchRecordSaveRequest request = request("node", "load", "装货"); + request.setPunched(false); + + assertTrue(service.savePunchRecord(request)); + + ArgumentCaptor captor = ArgumentCaptor.forClass(Waybill.class); + verify(waybillMapper).updateById(captor.capture()); + assertEquals(WaybillProcessSupport.STATUS_RUNNING, captor.getValue().getBusinessStatus()); + assertEquals(WAYBILL_ID, captor.getValue().getId()); + } + + @Test + @DisplayName("进行中:取消打卡不动运单状态") + void keepStatusWhenCancelPunchOnRunningWaybill() { + Waybill waybill = waybill(); + waybill.setBusinessStatus(WaybillProcessSupport.STATUS_RUNNING); + when(waybillMapper.selectById(WAYBILL_ID)).thenReturn(waybill); + when(nodePunchMapper.selectList(any())).thenReturn(List.of(nodePunch(7001L, "load", "装货"))); + + WaybillPunchRecordSaveRequest request = request("node", "load", "装货"); + request.setPunched(false); + + assertTrue(service.savePunchRecord(request)); + + verify(waybillMapper, never()).updateById(any(Waybill.class)); + } + + @Test + @DisplayName("待回单确认:取消在途打卡同样退回进行中") + void revertToRunningWhenCancelEnroutePunchOnPendingReturnConfirm() { + Waybill waybill = waybill(); + waybill.setBusinessStatus(WaybillProcessSupport.STATUS_PENDING_RETURN_CONFIRM); + when(waybillMapper.selectById(WAYBILL_ID)).thenReturn(waybill); + WaybillEnroutePunch existed = new WaybillEnroutePunch(); + existed.setId(8001L); + when(enroutePunchMapper.selectList(any())).thenReturn(List.of(existed)); + + WaybillPunchRecordSaveRequest request = request("enroute", "transit", "在途"); + request.setPunched(false); + + assertTrue(service.savePunchRecord(request)); + + ArgumentCaptor captor = ArgumentCaptor.forClass(Waybill.class); + verify(waybillMapper).updateById(captor.capture()); + assertEquals(WaybillProcessSupport.STATUS_RUNNING, captor.getValue().getBusinessStatus()); + } + @Test @DisplayName("已完成运单仍可补录——门禁只对待回单确认生效") void keepBackfillOnCompletedWaybill() {