✅ 确认回单改看运单状态、取消打卡回退与取消运单的测试

This commit is contained in:
2026-10-22 13:53:21 +08:00
parent 2484a3f7d1
commit f0872c7882
3 changed files with 157 additions and 14 deletions
@@ -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;
/**
* 确认回单:回单节点要求业务员确认时,内部人员确认后把运单置为已完成。
* 确认回单:停在待回单确认的运单,内部人员确认后把运单置为已完成。
* <p>
* 完整入口不校验节点打卡,故这里必须自己挡住两件事——该运单的司机打卡节点(在途除外)全部已打卡、
* 无未了结异常;不挡住就成了绕过打卡门禁与异常门禁的通道。回单不要求确认时不能走这个入口。
* 完整入口不校验节点打卡,故这里必须自己挡住三件事——运单确实停在待回单确认、该运单的司机打卡节点
* (在途除外)全部已打卡、无未了结异常;不挡住就成了绕过打卡门禁与异常门禁的通道。
* 判据只看运单状态,不看回单配置当前值。
*/
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.<Waybill>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.<Waybill>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) {
@@ -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() {
@@ -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<Waybill> 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<Waybill> captor = ArgumentCaptor.forClass(Waybill.class);
verify(waybillMapper).updateById(captor.capture());
assertEquals(WaybillProcessSupport.STATUS_RUNNING, captor.getValue().getBusinessStatus());
}
@Test
@DisplayName("已完成运单仍可补录——门禁只对待回单确认生效")
void keepBackfillOnCompletedWaybill() {