🔀 合并确认回单与取消打卡回退
This commit is contained in:
+29
-9
@@ -538,9 +538,26 @@ public class WaybillServiceImpl extends BaseServiceImpl<WaybillMapper, Waybill>
|
||||
throw new ServiceException("运单待业务员确认回单,不能补录打卡");
|
||||
}
|
||||
if (ENROUTE_PUNCH_TYPE.equalsIgnoreCase(TransportBusinessSupport.trimToEmpty(request.getType()))) {
|
||||
return saveEnroutePunchRecord(waybill, request, punched);
|
||||
return revertIfReturnConfirmPreconditionLost(waybill, punched,
|
||||
saveEnroutePunchRecord(waybill, request, punched));
|
||||
}
|
||||
return saveNodePunchRecord(waybill, request, punched);
|
||||
return revertIfReturnConfirmPreconditionLost(waybill, punched,
|
||||
saveNodePunchRecord(waybill, request, punched));
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消打卡撤掉了「司机已无待打卡节点」这个前提,停在待回单确认的运单退回进行中。
|
||||
* <p>
|
||||
* 只认取消打卡后的待回单确认,其它状态一律不动。
|
||||
*/
|
||||
private boolean revertIfReturnConfirmPreconditionLost(Waybill waybill, boolean punched, boolean saved) {
|
||||
if (!saved || punched
|
||||
|| !WaybillProcessSupport.STATUS_PENDING_RETURN_CONFIRM
|
||||
.equalsIgnoreCase(waybill.getBusinessStatus())) {
|
||||
return saved;
|
||||
}
|
||||
waybill.setBusinessStatus(WaybillProcessSupport.STATUS_RUNNING);
|
||||
return updateById(waybill);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -968,13 +985,16 @@ public class WaybillServiceImpl extends BaseServiceImpl<WaybillMapper, Waybill>
|
||||
/**
|
||||
* 运单是否已越过接单阶段。
|
||||
* <p>
|
||||
* 正向列举进行中/已完成/已取消,不能用「不是终态」取反:终态里还包含草稿、待调度、
|
||||
* 正向列举进行中/已完成/已取消/待回单确认,不能用「不是终态」取反:终态里还包含草稿、待调度、
|
||||
* 调度中这些并未越过接单的阶段,取反会把它们判成已接单。
|
||||
* <p>
|
||||
* 待回单确认下司机已打完末位卡,无需接单配置时接单节点不该再显示未打卡。
|
||||
*/
|
||||
private static boolean passedAcceptStage(String businessStatus) {
|
||||
return "running".equals(businessStatus)
|
||||
|| "completed".equals(businessStatus)
|
||||
|| "cancelled".equals(businessStatus);
|
||||
|| "cancelled".equals(businessStatus)
|
||||
|| WaybillProcessSupport.STATUS_PENDING_RETURN_CONFIRM.equals(businessStatus);
|
||||
}
|
||||
|
||||
private String formatPunchTime(Date time) {
|
||||
@@ -1525,14 +1545,14 @@ public class WaybillServiceImpl extends BaseServiceImpl<WaybillMapper, Waybill>
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean confirmReturnReceipt(Long id) {
|
||||
Waybill waybill = loadEditable(id, true);
|
||||
// 判据只看运单当前状态,不看回单配置当前值:配置改回「无需确认」后,
|
||||
// 已停在待回单确认的运单仍要能确认回单
|
||||
if (!WaybillProcessSupport.STATUS_PENDING_RETURN_CONFIRM.equalsIgnoreCase(waybill.getBusinessStatus())) {
|
||||
throw new ServiceException("当前状态不允许确认回单");
|
||||
}
|
||||
String processJson = LiveProcessConfigSupport.resolve(
|
||||
LiveProcessConfigSupport.byProject(processConfigService, waybill.getProjectId()),
|
||||
waybill.getProcessJson());
|
||||
if (WaybillProcessSupport.returnNeedsNoConfirmation(processJson)) {
|
||||
// 不要求确认时司机打完末位卡就已经完成了,运单不会停在进行中等确认;配置对不上要说清,
|
||||
// 否则用户会以为是确认没生效
|
||||
throw new ServiceException("该运单的回单不需要业务员确认");
|
||||
}
|
||||
assertPunchNodesFinished(waybill.getId(), processJson);
|
||||
assertNoUnresolvedException(waybill.getId());
|
||||
return doComplete(waybill);
|
||||
|
||||
+86
-14
@@ -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
@@ -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() {
|
||||
|
||||
+57
@@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user