Merge branch 'feat/pending-return-confirm' into feat/rc-12
This commit is contained in:
+3
@@ -52,4 +52,7 @@ public class DriverNodePunchVO implements Serializable {
|
|||||||
@Schema(description = "本次打卡是否使运单自动完成")
|
@Schema(description = "本次打卡是否使运单自动完成")
|
||||||
private Boolean waybillCompleted;
|
private Boolean waybillCompleted;
|
||||||
|
|
||||||
|
@Schema(description = "本次打卡后的运单业务状态:running / pending_return_confirm / completed")
|
||||||
|
private String waybillBusinessStatus;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+16
-3
@@ -520,19 +520,32 @@ public class DriverWaybillServiceImpl implements IDriverWaybillService {
|
|||||||
nodePunchMapper.insert(punch);
|
nodePunchMapper.insert(punch);
|
||||||
|
|
||||||
advanceCurrentProcessNode(waybill, nodeCfg);
|
advanceCurrentProcessNode(waybill, nodeCfg);
|
||||||
// 末位打卡节点是到货/卸货/签收、且回单无需确认完成时,本次打卡一并把运单置为完成;
|
// 末位打卡节点是到货/卸货/签收、且运单本身允许完成时,本次打卡就是运单的去向决定点:
|
||||||
|
// 回单无需确认 → 一并完成;回单需业务员确认 → 停在待回单确认等业务员确认。
|
||||||
// isLastDriverPunchNode 与 returnNeedsNoConfirmation 传的就是上面那份 processJson,未再调 resolveProcessJson 取过程配置。
|
// isLastDriverPunchNode 与 returnNeedsNoConfirmation 传的就是上面那份 processJson,未再调 resolveProcessJson 取过程配置。
|
||||||
String nodeKey = WaybillProcessSupport.nodeKey(nodeCfg);
|
String nodeKey = WaybillProcessSupport.nodeKey(nodeCfg);
|
||||||
String nodeName = WaybillProcessSupport.nodeName(nodeCfg);
|
String nodeName = WaybillProcessSupport.nodeName(nodeCfg);
|
||||||
|
String waybillBusinessStatus = waybill.getBusinessStatus();
|
||||||
boolean completed = false;
|
boolean completed = false;
|
||||||
if (WaybillProcessSupport.isLastDriverPunchNode(processJson, nodeKey)
|
if (WaybillProcessSupport.isLastDriverPunchNode(processJson, nodeKey)
|
||||||
&& WaybillProcessSupport.isDeliveryNode(nodeKey, nodeName)
|
&& WaybillProcessSupport.isDeliveryNode(nodeKey, nodeName)
|
||||||
&& WaybillProcessSupport.returnNeedsNoConfirmation(processJson)
|
|
||||||
&& canAutoCompleteWaybill(waybill)) {
|
&& canAutoCompleteWaybill(waybill)) {
|
||||||
completed = waybillService.completeWithoutDeptCheck(waybill.getId());
|
if (WaybillProcessSupport.returnNeedsNoConfirmation(processJson)) {
|
||||||
|
completed = waybillService.completeWithoutDeptCheck(waybill.getId());
|
||||||
|
if (completed) {
|
||||||
|
waybillBusinessStatus = STATUS_COMPLETED;
|
||||||
|
}
|
||||||
|
} else if (WaybillProcessSupport.STATUS_RUNNING.equals(waybill.getBusinessStatus())) {
|
||||||
|
// 只有进行中能进入:canAutoCompleteWaybill 的状态口径含待执行/待调度等,不能替代这一条;
|
||||||
|
// 待回单确认在 isPunchEnded 里算已结束,不能从结束态重进。
|
||||||
|
waybill.setBusinessStatus(WaybillProcessSupport.STATUS_PENDING_RETURN_CONFIRM);
|
||||||
|
waybillService.updateById(waybill);
|
||||||
|
waybillBusinessStatus = WaybillProcessSupport.STATUS_PENDING_RETURN_CONFIRM;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
DriverNodePunchVO vo = toNodePunchVO(punch);
|
DriverNodePunchVO vo = toNodePunchVO(punch);
|
||||||
vo.setWaybillCompleted(completed);
|
vo.setWaybillCompleted(completed);
|
||||||
|
vo.setWaybillBusinessStatus(waybillBusinessStatus);
|
||||||
return vo;
|
return vo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+20
-2
@@ -531,6 +531,12 @@ public class WaybillServiceImpl extends BaseServiceImpl<WaybillMapper, Waybill>
|
|||||||
}
|
}
|
||||||
// 只有显式传 false 才是取消打卡,未传按已打卡处理
|
// 只有显式传 false 才是取消打卡,未传按已打卡处理
|
||||||
boolean punched = !Boolean.FALSE.equals(request.getPunched());
|
boolean punched = !Boolean.FALSE.equals(request.getPunched());
|
||||||
|
// 待回单确认的运单司机已无待打卡节点,再补一条打卡流水与状态含义矛盾;
|
||||||
|
// 漏打的出路是先取消该节点的打卡退回进行中(punched=false),再重新补录。
|
||||||
|
if (punched && WaybillProcessSupport.STATUS_PENDING_RETURN_CONFIRM
|
||||||
|
.equalsIgnoreCase(waybill.getBusinessStatus())) {
|
||||||
|
throw new ServiceException("运单待业务员确认回单,不能补录打卡");
|
||||||
|
}
|
||||||
if (ENROUTE_PUNCH_TYPE.equalsIgnoreCase(TransportBusinessSupport.trimToEmpty(request.getType()))) {
|
if (ENROUTE_PUNCH_TYPE.equalsIgnoreCase(TransportBusinessSupport.trimToEmpty(request.getType()))) {
|
||||||
return saveEnroutePunchRecord(waybill, request, punched);
|
return saveEnroutePunchRecord(waybill, request, punched);
|
||||||
}
|
}
|
||||||
@@ -1062,10 +1068,11 @@ public class WaybillServiceImpl extends BaseServiceImpl<WaybillMapper, Waybill>
|
|||||||
waybill.setMasterNo(null);
|
waybill.setMasterNo(null);
|
||||||
waybill.setMileageRemark(null);
|
waybill.setMileageRemark(null);
|
||||||
}
|
}
|
||||||
// 新建若已带业务状态(如复制提交已完成)则在过程校正后回写,保证与原运单一致
|
// 新建若已带业务状态(如复制提交已完成)则在过程校正后回写,保证与原运单一致;
|
||||||
|
// 编辑保存只改资料,business_status 以库里的值为准,请求体里的值不回写
|
||||||
String preservedBusinessStatus = created
|
String preservedBusinessStatus = created
|
||||||
? TransportBusinessSupport.trimToNull(waybill.getBusinessStatus())
|
? TransportBusinessSupport.trimToNull(waybill.getBusinessStatus())
|
||||||
: null;
|
: editPreservedBusinessStatus(oldRecord);
|
||||||
if (isCompletedBusinessStatus(preservedBusinessStatus)) {
|
if (isCompletedBusinessStatus(preservedBusinessStatus)) {
|
||||||
preservedBusinessStatus = "completed";
|
preservedBusinessStatus = "completed";
|
||||||
}
|
}
|
||||||
@@ -1087,6 +1094,17 @@ public class WaybillServiceImpl extends BaseServiceImpl<WaybillMapper, Waybill>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 编辑保存要回写的业务状态:取库里的值。
|
||||||
|
* 草稿返回 null —— submit 即正式提交,草稿须由过程校正提升为待执行 / 进行中。
|
||||||
|
*/
|
||||||
|
private String editPreservedBusinessStatus(Waybill oldRecord) {
|
||||||
|
String currentStatus = oldRecord == null
|
||||||
|
? null
|
||||||
|
: TransportBusinessSupport.trimToNull(oldRecord.getBusinessStatus());
|
||||||
|
return STATUS_DRAFT.equals(currentStatus) ? null : currentStatus;
|
||||||
|
}
|
||||||
|
|
||||||
private void fillCustomerName(Waybill waybill) {
|
private void fillCustomerName(Waybill waybill) {
|
||||||
if (Func.isNotEmpty(waybill.getContractId())) {
|
if (Func.isNotEmpty(waybill.getContractId())) {
|
||||||
ContractManage contract = contractManageService.getById(waybill.getContractId());
|
ContractManage contract = contractManageService.getById(waybill.getContractId());
|
||||||
|
|||||||
+13
-5
@@ -49,6 +49,8 @@ public final class WaybillProcessSupport {
|
|||||||
|
|
||||||
public static final String STATUS_PENDING = "pending";
|
public static final String STATUS_PENDING = "pending";
|
||||||
public static final String STATUS_RUNNING = "running";
|
public static final String STATUS_RUNNING = "running";
|
||||||
|
/** 司机已打完末位卡、回单需业务员确认,尚未生成应收应付 */
|
||||||
|
public static final String STATUS_PENDING_RETURN_CONFIRM = "pending_return_confirm";
|
||||||
public static final String ACCEPT_PENDING = "pending";
|
public static final String ACCEPT_PENDING = "pending";
|
||||||
public static final String ACCEPT_ACCEPTED = "accepted";
|
public static final String ACCEPT_ACCEPTED = "accepted";
|
||||||
public static final String ACCEPT_REJECTED = "rejected";
|
public static final String ACCEPT_REJECTED = "rejected";
|
||||||
@@ -134,14 +136,16 @@ public final class WaybillProcessSupport {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 运单已结束:已完成或已取消。此后不再接受司机打卡。
|
* 运单已结束:已完成、已取消,或已打完末位卡停在待回单确认。此后不再接受司机打卡。
|
||||||
* <p>
|
* <p>
|
||||||
* 只认这两个状态,大小写不敏感。草稿、待执行、待调度、调度中的运单司机仍要能打卡,
|
* 只认这三个状态,大小写不敏感。草稿、待执行、待调度、调度中的运单司机仍要能打卡,
|
||||||
* 所以不能按 {@link #isTerminalBusinessStatus} 的「终态或调度中间态」口径判定。
|
* 所以不能按 {@link #isTerminalBusinessStatus} 的「终态或调度中间态」口径判定。
|
||||||
*/
|
*/
|
||||||
public static boolean isPunchEnded(String businessStatus) {
|
public static boolean isPunchEnded(String businessStatus) {
|
||||||
String status = stringVal(businessStatus);
|
String status = stringVal(businessStatus);
|
||||||
return "completed".equalsIgnoreCase(status) || "cancelled".equalsIgnoreCase(status);
|
return "completed".equalsIgnoreCase(status)
|
||||||
|
|| "cancelled".equalsIgnoreCase(status)
|
||||||
|
|| STATUS_PENDING_RETURN_CONFIRM.equalsIgnoreCase(status);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -155,6 +159,9 @@ public final class WaybillProcessSupport {
|
|||||||
if ("cancelled".equalsIgnoreCase(status)) {
|
if ("cancelled".equalsIgnoreCase(status)) {
|
||||||
return "运单已取消";
|
return "运单已取消";
|
||||||
}
|
}
|
||||||
|
if (STATUS_PENDING_RETURN_CONFIRM.equalsIgnoreCase(status)) {
|
||||||
|
return "运单待业务员确认回单";
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -195,7 +202,7 @@ public final class WaybillProcessSupport {
|
|||||||
* 上报异常前的运单状态门禁原因;可上报时返回 null。
|
* 上报异常前的运单状态门禁原因;可上报时返回 null。
|
||||||
* <p>
|
* <p>
|
||||||
* 与打卡共用「已开始运输」判据,但文案说的是能不能报,不是能不能打。
|
* 与打卡共用「已开始运输」判据,但文案说的是能不能报,不是能不能打。
|
||||||
* 结束优先于未开始:已完成/已取消的运单先说结束,不再提「等你上路」。
|
* 结束优先于未开始:已结束的运单先说结束,不再提「等你上路」。
|
||||||
*/
|
*/
|
||||||
public static String reportBlockedReason(String businessStatus) {
|
public static String reportBlockedReason(String businessStatus) {
|
||||||
String endedReason = punchEndedReason(businessStatus);
|
String endedReason = punchEndedReason(businessStatus);
|
||||||
@@ -213,7 +220,8 @@ public final class WaybillProcessSupport {
|
|||||||
|| "completed".equals(businessStatus)
|
|| "completed".equals(businessStatus)
|
||||||
|| "cancelled".equals(businessStatus)
|
|| "cancelled".equals(businessStatus)
|
||||||
|| "waiting_dispatch".equals(businessStatus)
|
|| "waiting_dispatch".equals(businessStatus)
|
||||||
|| "dispatching".equals(businessStatus);
|
|| "dispatching".equals(businessStatus)
|
||||||
|
|| STATUS_PENDING_RETURN_CONFIRM.equals(businessStatus);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+96
-10
@@ -7,6 +7,7 @@ import org.junit.jupiter.api.BeforeAll;
|
|||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.DisplayName;
|
import org.junit.jupiter.api.DisplayName;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
import org.mockito.MockedStatic;
|
import org.mockito.MockedStatic;
|
||||||
import org.springblade.core.log.exception.ServiceException;
|
import org.springblade.core.log.exception.ServiceException;
|
||||||
import org.springblade.core.secure.utils.AuthUtil;
|
import org.springblade.core.secure.utils.AuthUtil;
|
||||||
@@ -28,6 +29,7 @@ import org.springblade.transport.service.IWaybillService;
|
|||||||
import org.springblade.transport.service.ITripQueryService;
|
import org.springblade.transport.service.ITripQueryService;
|
||||||
import org.springblade.transport.support.CertificationGate;
|
import org.springblade.transport.support.CertificationGate;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
@@ -35,6 +37,7 @@ import static org.junit.jupiter.api.Assertions.assertFalse;
|
|||||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.Mockito.atLeastOnce;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.mockStatic;
|
import static org.mockito.Mockito.mockStatic;
|
||||||
import static org.mockito.Mockito.never;
|
import static org.mockito.Mockito.never;
|
||||||
@@ -42,11 +45,13 @@ import static org.mockito.Mockito.verify;
|
|||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 司机打完末位打卡节点后运单自动完成。
|
* 司机打完末位打卡节点后运单的去向。
|
||||||
* <p>
|
* <p>
|
||||||
* 命中要四个条件同时成立:本次打卡的是「司机可打卡节点」的末位、该节点是到货/卸货/签收、
|
* 末位打卡是到货/卸货/签收、运单本身允许完成时,回单是否需业务员确认决定去向:
|
||||||
* 回单无需确认完成、运单本身允许完成。任一不成立都只落打卡流水,
|
* 无需确认则本次一并完成运单,需确认则把运单置为待回单确认等业务员动手。
|
||||||
* 响应里的 waybillCompleted 始终取完成接口的返回值,前端据此决定是否提示「运单已完成」。
|
* 任一条件不成立都只落打卡流水。
|
||||||
|
* 响应里的 waybillCompleted 始终取完成接口的返回值,前端据此决定是否提示「运单已完成」;
|
||||||
|
* 待回单确认不是已完成,只能靠 waybillBusinessStatus 分辨。
|
||||||
*/
|
*/
|
||||||
class DriverWaybillAutoCompleteTest {
|
class DriverWaybillAutoCompleteTest {
|
||||||
|
|
||||||
@@ -72,6 +77,12 @@ class DriverWaybillAutoCompleteTest {
|
|||||||
[{"key":"dispatch","name":"发货","type":"cargo","enabled":true,"punch":true}]
|
[{"key":"dispatch","name":"发货","type":"cargo","enabled":true,"punch":true}]
|
||||||
""";
|
""";
|
||||||
|
|
||||||
|
/** 末位司机打卡节点是「发货」,回单启用且需确认完成 */
|
||||||
|
private static final String DISPATCH_LAST_RETURN_CONFIRM_JSON = """
|
||||||
|
[{"key":"dispatch","name":"发货","type":"cargo","enabled":true,"punch":true},
|
||||||
|
{"key":"return","name":"回单","type":"confirm","enabled":true,"punch":true,"confirmMode":"yes"}]
|
||||||
|
""";
|
||||||
|
|
||||||
private IWaybillService waybillService;
|
private IWaybillService waybillService;
|
||||||
private IDriverService driverService;
|
private IDriverService driverService;
|
||||||
private WaybillNodePunchMapper nodePunchMapper;
|
private WaybillNodePunchMapper nodePunchMapper;
|
||||||
@@ -118,7 +129,7 @@ class DriverWaybillAutoCompleteTest {
|
|||||||
// ------------------------------------------------------------ 命中
|
// ------------------------------------------------------------ 命中
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("末位签收打卡且回单无需确认时调完成接口,响应标记 waybillCompleted")
|
@DisplayName("末位签收打卡且回单无需确认时调完成接口,响应标记 waybillCompleted 与 completed")
|
||||||
void lastDeliveryPunchCompletesWaybill() {
|
void lastDeliveryPunchCompletesWaybill() {
|
||||||
givenWaybill(SIGN_LAST_JSON, "running");
|
givenWaybill(SIGN_LAST_JSON, "running");
|
||||||
when(waybillService.completeWithoutDeptCheck(WAYBILL_ID)).thenReturn(true);
|
when(waybillService.completeWithoutDeptCheck(WAYBILL_ID)).thenReturn(true);
|
||||||
@@ -127,6 +138,7 @@ class DriverWaybillAutoCompleteTest {
|
|||||||
|
|
||||||
verify(waybillService).completeWithoutDeptCheck(WAYBILL_ID);
|
verify(waybillService).completeWithoutDeptCheck(WAYBILL_ID);
|
||||||
assertTrue(vo.getWaybillCompleted(), "完成接口返回成功,响应要告诉前端运单已完成");
|
assertTrue(vo.getWaybillCompleted(), "完成接口返回成功,响应要告诉前端运单已完成");
|
||||||
|
assertEquals("completed", vo.getWaybillBusinessStatus(), "完成后运单业务状态要如实回报");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -139,6 +151,25 @@ class DriverWaybillAutoCompleteTest {
|
|||||||
|
|
||||||
verify(waybillService).completeWithoutDeptCheck(WAYBILL_ID);
|
verify(waybillService).completeWithoutDeptCheck(WAYBILL_ID);
|
||||||
assertFalse(vo.getWaybillCompleted(), "完成接口自己说不算完成时,前端不能提示运单已完成");
|
assertFalse(vo.getWaybillCompleted(), "完成接口自己说不算完成时,前端不能提示运单已完成");
|
||||||
|
assertEquals("running", vo.getWaybillBusinessStatus(), "完成没落库,运单仍在进行中");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------ 待回单确认
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("回单需业务员确认时末位打卡进入待回单确认,落库并回报,且不调完成接口")
|
||||||
|
void returnNeedingConfirmationEntersPendingReturnConfirm() {
|
||||||
|
givenWaybill(SIGN_LAST_RETURN_CONFIRM_JSON, "running");
|
||||||
|
|
||||||
|
DriverNodePunchVO vo = submit("sign");
|
||||||
|
|
||||||
|
verify(waybillService, never()).completeWithoutDeptCheck(any());
|
||||||
|
verify(nodePunchMapper).insert(any(WaybillNodePunch.class));
|
||||||
|
assertFalse(vo.getWaybillCompleted(), "待回单确认不是已完成");
|
||||||
|
assertEquals("pending_return_confirm", vo.getWaybillBusinessStatus(),
|
||||||
|
"客户端要知道打卡已提交、等业务员确认回单");
|
||||||
|
assertEquals("pending_return_confirm", writtenBusinessStatus(),
|
||||||
|
"状态要落库,否则下次查询又回到进行中");
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------ 不命中
|
// ------------------------------------------------------------ 不命中
|
||||||
@@ -153,18 +184,31 @@ class DriverWaybillAutoCompleteTest {
|
|||||||
verify(waybillService, never()).completeWithoutDeptCheck(any());
|
verify(waybillService, never()).completeWithoutDeptCheck(any());
|
||||||
verify(nodePunchMapper).insert(any(WaybillNodePunch.class));
|
verify(nodePunchMapper).insert(any(WaybillNodePunch.class));
|
||||||
assertFalse(vo.getWaybillCompleted());
|
assertFalse(vo.getWaybillCompleted());
|
||||||
|
assertEquals("running", vo.getWaybillBusinessStatus());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("回单启用且需确认完成时只打卡不完成")
|
@DisplayName("回单需确认但不是末位打卡:运单留在进行中")
|
||||||
void returnNeedingConfirmationDoesNotComplete() {
|
void nonLastPunchDoesNotEnterReturnConfirmation() {
|
||||||
givenWaybill(SIGN_LAST_RETURN_CONFIRM_JSON, "running");
|
givenWaybill(SIGN_LAST_RETURN_CONFIRM_JSON, "running");
|
||||||
|
|
||||||
DriverNodePunchVO vo = submit("sign");
|
DriverNodePunchVO vo = submit("load");
|
||||||
|
|
||||||
|
verify(nodePunchMapper).insert(any(WaybillNodePunch.class));
|
||||||
|
assertEquals("running", vo.getWaybillBusinessStatus(), "后面还有签收要打,不能提前停在待回单确认");
|
||||||
|
assertEquals("running", writtenBusinessStatus());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("回单需确认但末位不是到货/卸货/签收:运单留在进行中")
|
||||||
|
void nonDeliveryLastNodeDoesNotEnterReturnConfirmation() {
|
||||||
|
givenWaybill(DISPATCH_LAST_RETURN_CONFIRM_JSON, "running");
|
||||||
|
|
||||||
|
DriverNodePunchVO vo = submit("dispatch");
|
||||||
|
|
||||||
verify(waybillService, never()).completeWithoutDeptCheck(any());
|
verify(waybillService, never()).completeWithoutDeptCheck(any());
|
||||||
verify(nodePunchMapper).insert(any(WaybillNodePunch.class));
|
assertEquals("running", vo.getWaybillBusinessStatus(), "发货不是到货类节点,打完不该停在等待回单");
|
||||||
assertFalse(vo.getWaybillCompleted());
|
assertEquals("running", writtenBusinessStatus());
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------ 前置:运单本身能否完成
|
// ------------------------------------------------------------ 前置:运单本身能否完成
|
||||||
@@ -208,8 +252,50 @@ class DriverWaybillAutoCompleteTest {
|
|||||||
assertFalse(vo.getWaybillCompleted());
|
assertFalse(vo.getWaybillCompleted());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("有未了结异常时回单需确认也不进待回单确认——四条进入条件共同成立才进")
|
||||||
|
void unresolvedExceptionBlocksEnteringReturnConfirmation() {
|
||||||
|
givenWaybill(SIGN_LAST_RETURN_CONFIRM_JSON, "running");
|
||||||
|
when(exceptionDisposalMapper.selectList(any())).thenReturn(List.of(unresolvedException("unload")));
|
||||||
|
|
||||||
|
DriverNodePunchVO vo = submit("sign");
|
||||||
|
|
||||||
|
assertEquals("running", vo.getWaybillBusinessStatus(), "异常未了结,不能把运单停在等待回单");
|
||||||
|
assertEquals("running", writtenBusinessStatus());
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------ 并发:只从进行中进入
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("打卡期间运单已被推到终态时不再写待回单确认——只从进行中进入")
|
||||||
|
void concurrentTerminalStatusPreventsEnteringReturnConfirmation() {
|
||||||
|
givenWaybill(SIGN_LAST_RETURN_CONFIRM_JSON, "running");
|
||||||
|
List<String> statusesWritten = new ArrayList<>();
|
||||||
|
// 模拟另一事务在这次打卡推进过程节点时把运单置为已完成(管理端强制完成)
|
||||||
|
when(waybillService.updateById(any())).thenAnswer(invocation -> {
|
||||||
|
Waybill entity = invocation.getArgument(0);
|
||||||
|
statusesWritten.add(entity.getBusinessStatus());
|
||||||
|
entity.setBusinessStatus("completed");
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
DriverNodePunchVO vo = submit("sign");
|
||||||
|
|
||||||
|
assertFalse(statusesWritten.contains("pending_return_confirm"),
|
||||||
|
"运单已被完成,不能再改写回待回单确认");
|
||||||
|
assertEquals("completed", vo.getWaybillBusinessStatus());
|
||||||
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------ 夹具
|
// ------------------------------------------------------------ 夹具
|
||||||
|
|
||||||
|
/** 打卡流程最后一次落库时运单的业务状态;状态写库走 updateById,各次传的是同一个实体引用 */
|
||||||
|
private String writtenBusinessStatus() {
|
||||||
|
ArgumentCaptor<Waybill> captor = ArgumentCaptor.forClass(Waybill.class);
|
||||||
|
verify(waybillService, atLeastOnce()).updateById(captor.capture());
|
||||||
|
List<Waybill> written = captor.getAllValues();
|
||||||
|
return written.get(written.size() - 1).getBusinessStatus();
|
||||||
|
}
|
||||||
|
|
||||||
private DriverNodePunchVO submit(String nodeCode) {
|
private DriverNodePunchVO submit(String nodeCode) {
|
||||||
NodeSubmitDTO dto = new NodeSubmitDTO();
|
NodeSubmitDTO dto = new NodeSubmitDTO();
|
||||||
dto.setWaybillId(WAYBILL_ID);
|
dto.setWaybillId(WAYBILL_ID);
|
||||||
|
|||||||
+198
@@ -0,0 +1,198 @@
|
|||||||
|
package org.springblade.transport.service.impl;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.MybatisConfiguration;
|
||||||
|
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
|
||||||
|
import org.apache.ibatis.builder.MapperBuilderAssistant;
|
||||||
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.DisplayName;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.mockito.MockedStatic;
|
||||||
|
import org.springblade.core.secure.utils.AuthUtil;
|
||||||
|
import org.springblade.transport.mapper.WaybillMapper;
|
||||||
|
import org.springblade.transport.pojo.entity.Waybill;
|
||||||
|
import org.springblade.transport.support.WaybillProcessSupport;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.mockStatic;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 运单编辑保存的业务状态来源测试。
|
||||||
|
* <p>
|
||||||
|
* 编辑保存只改运单资料,business_status 一律以库里的值为准:请求体不回传(null / 空串)
|
||||||
|
* 不得把库里的待执行、进行中、已完成改写成别的状态,回传陈旧值也不得覆盖。
|
||||||
|
* 唯一例外是草稿——submit 即正式提交,草稿须按过程配置提升为待执行 / 进行中。
|
||||||
|
* <p>
|
||||||
|
* 过程配置用「无需确认接单」的快照({@code []}):无过程校正时待执行会被提升为进行中,
|
||||||
|
* 正是编辑保存最容易把库里状态写坏的场景。
|
||||||
|
*/
|
||||||
|
class WaybillEditSaveBusinessStatusTest {
|
||||||
|
|
||||||
|
private static final Long WAYBILL_ID = 7001L;
|
||||||
|
private static final Long DEPT_ID = 100L;
|
||||||
|
/** 无接单节点的过程配置:normalizeBusinessStatus 会把空 / pending 提升为 running */
|
||||||
|
private static final String PROCESS_JSON_NO_ACCEPT = "[]";
|
||||||
|
|
||||||
|
private WaybillMapper waybillMapper;
|
||||||
|
private WaybillServiceImpl service;
|
||||||
|
|
||||||
|
@BeforeAll
|
||||||
|
static void initTableInfo() {
|
||||||
|
// saveOrUpdate 判主键需要 MyBatis-Plus 的实体元数据缓存
|
||||||
|
MybatisConfiguration configuration = new MybatisConfiguration();
|
||||||
|
MapperBuilderAssistant assistant = new MapperBuilderAssistant(configuration, "");
|
||||||
|
assistant.setCurrentNamespace("test");
|
||||||
|
TableInfoHelper.initTableInfo(assistant, Waybill.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() throws Exception {
|
||||||
|
waybillMapper = mock(WaybillMapper.class);
|
||||||
|
service = new WaybillServiceImpl();
|
||||||
|
inject("baseMapper", waybillMapper);
|
||||||
|
when(waybillMapper.updateById(any(Waybill.class))).thenReturn(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------ 编辑保存:以库里的状态为准
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("编辑保存:请求体不回传业务状态,库里的待执行不得被提升为进行中")
|
||||||
|
void editSaveKeepsPendingWhenRequestBodyOmitsStatus() {
|
||||||
|
Waybill request = editableRequest();
|
||||||
|
|
||||||
|
submit(request, "pending");
|
||||||
|
|
||||||
|
assertEquals("pending", request.getBusinessStatus(),
|
||||||
|
"编辑保存时请求体没有 business_status,不得按过程配置把库里的待执行改写成进行中");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("编辑保存:请求体不回传业务状态,库里的进行中不得被改写")
|
||||||
|
void editSaveKeepsRunningWhenRequestBodyOmitsStatus() {
|
||||||
|
Waybill request = editableRequest();
|
||||||
|
|
||||||
|
submit(request, "running");
|
||||||
|
|
||||||
|
assertEquals("running", request.getBusinessStatus());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("编辑保存:请求体不回传业务状态,库里的已完成不得被改写")
|
||||||
|
void editSaveKeepsCompletedWhenRequestBodyOmitsStatus() {
|
||||||
|
Waybill request = editableRequest();
|
||||||
|
|
||||||
|
submit(request, "completed");
|
||||||
|
|
||||||
|
assertEquals("completed", request.getBusinessStatus());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("编辑保存:请求体回传陈旧状态,仍以库里的值为准")
|
||||||
|
void editSaveKeepsDatabaseStatusWhenRequestBodyCarriesStaleValue() {
|
||||||
|
Waybill request = editableRequest();
|
||||||
|
request.setBusinessStatus("running");
|
||||||
|
|
||||||
|
submit(request, "completed");
|
||||||
|
|
||||||
|
assertEquals("completed", request.getBusinessStatus(),
|
||||||
|
"库里已完成,请求体带的是打开表单时的旧值,不得据此回退状态");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("编辑保存:请求体回传空串,库里的状态不得被清空")
|
||||||
|
void editSaveKeepsDatabaseStatusWhenRequestBodyIsBlank() {
|
||||||
|
Waybill request = editableRequest();
|
||||||
|
request.setBusinessStatus("");
|
||||||
|
|
||||||
|
submit(request, "running");
|
||||||
|
|
||||||
|
assertEquals("running", request.getBusinessStatus());
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------ 新增状态
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("编辑保存:待回单确认的运单不得被编辑保存改成进行中")
|
||||||
|
void editSaveKeepsPendingReturnConfirm() {
|
||||||
|
Waybill request = editableRequest();
|
||||||
|
|
||||||
|
submit(request, WaybillProcessSupport.STATUS_PENDING_RETURN_CONFIRM);
|
||||||
|
|
||||||
|
assertEquals(WaybillProcessSupport.STATUS_PENDING_RETURN_CONFIRM, request.getBusinessStatus(),
|
||||||
|
"司机已打完末位卡,编辑一次运单资料不得把它退回进行中");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------ 草稿例外
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("提交草稿:草稿仍按过程配置提升为进行中")
|
||||||
|
void submitDraftStillPromotesToRunning() {
|
||||||
|
Waybill request = editableRequest();
|
||||||
|
|
||||||
|
submit(request, "draft");
|
||||||
|
|
||||||
|
assertEquals("running", request.getBusinessStatus(),
|
||||||
|
"submit 即正式提交,草稿必须脱离 draft,不能被「以库里的值为准」锁死");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------ 夹具
|
||||||
|
|
||||||
|
private Waybill editableRequest() {
|
||||||
|
Waybill waybill = new Waybill();
|
||||||
|
waybill.setId(WAYBILL_ID);
|
||||||
|
waybill.setProjectName("项目A");
|
||||||
|
waybill.setContractName("客户合同A");
|
||||||
|
waybill.setCustomerName("客户A");
|
||||||
|
waybill.setTransportType("公路运输");
|
||||||
|
waybill.setCargoName("钢材");
|
||||||
|
waybill.setCargoType("普货");
|
||||||
|
waybill.setQuantityUnit("吨");
|
||||||
|
waybill.setCarrierType("自运");
|
||||||
|
waybill.setVehicleNo("桂A12345");
|
||||||
|
waybill.setDriverName("张三");
|
||||||
|
waybill.setCarrierJson("{}");
|
||||||
|
waybill.setQuantity(BigDecimal.TEN);
|
||||||
|
waybill.setProcessJson(PROCESS_JSON_NO_ACCEPT);
|
||||||
|
return waybill;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 让库里的同一条运单处于 {@code databaseStatus},再走一次编辑保存(submit)。
|
||||||
|
* 司机与车牌保持一致,避免接单记录被判定为「换了司机」而清空。
|
||||||
|
*/
|
||||||
|
private void submit(Waybill request, String databaseStatus) {
|
||||||
|
Waybill oldRecord = editableRequest();
|
||||||
|
oldRecord.setBusinessStatus(databaseStatus);
|
||||||
|
oldRecord.setDeptId(DEPT_ID);
|
||||||
|
oldRecord.setDeptName("测试组织");
|
||||||
|
when(waybillMapper.selectById(WAYBILL_ID)).thenReturn(oldRecord);
|
||||||
|
|
||||||
|
try (MockedStatic<AuthUtil> authUtil = mockStatic(AuthUtil.class)) {
|
||||||
|
authUtil.when(AuthUtil::getDeptId).thenReturn(DEPT_ID);
|
||||||
|
assertTrue(service.submit(request));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void inject(String field, Object value) throws Exception {
|
||||||
|
Class<?> type = service.getClass();
|
||||||
|
while (type != null) {
|
||||||
|
try {
|
||||||
|
Field declared = type.getDeclaredField(field);
|
||||||
|
declared.setAccessible(true);
|
||||||
|
declared.set(service, value);
|
||||||
|
return;
|
||||||
|
} catch (NoSuchFieldException ignored) {
|
||||||
|
type = type.getSuperclass();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new IllegalStateException("未找到字段:" + field);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+54
@@ -18,6 +18,7 @@ import org.springblade.transport.pojo.dto.WaybillPunchRecordSaveRequest;
|
|||||||
import org.springblade.transport.pojo.entity.Waybill;
|
import org.springblade.transport.pojo.entity.Waybill;
|
||||||
import org.springblade.transport.pojo.entity.WaybillEnroutePunch;
|
import org.springblade.transport.pojo.entity.WaybillEnroutePunch;
|
||||||
import org.springblade.transport.pojo.entity.WaybillNodePunch;
|
import org.springblade.transport.pojo.entity.WaybillNodePunch;
|
||||||
|
import org.springblade.transport.support.WaybillProcessSupport;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -165,6 +166,59 @@ class WaybillPunchRecordSaveTest {
|
|||||||
verify(nodePunchMapper, never()).updateById(any(WaybillNodePunch.class));
|
verify(nodePunchMapper, never()).updateById(any(WaybillNodePunch.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("待回单确认:补录打卡被拒——司机已无待打卡,漏打应先取消打卡退回进行中")
|
||||||
|
void rejectBackfillOnPendingReturnConfirm() {
|
||||||
|
Waybill waybill = waybill();
|
||||||
|
waybill.setBusinessStatus(WaybillProcessSupport.STATUS_PENDING_RETURN_CONFIRM);
|
||||||
|
when(waybillMapper.selectById(WAYBILL_ID)).thenReturn(waybill);
|
||||||
|
when(nodePunchMapper.selectList(any())).thenReturn(List.of());
|
||||||
|
|
||||||
|
WaybillPunchRecordSaveRequest request = request("node", "load", "装货");
|
||||||
|
request.setPunched(true);
|
||||||
|
|
||||||
|
ServiceException exception = assertThrows(ServiceException.class,
|
||||||
|
() -> service.savePunchRecord(request));
|
||||||
|
|
||||||
|
assertEquals("运单待业务员确认回单,不能补录打卡", exception.getMessage());
|
||||||
|
verify(nodePunchMapper, never()).insert(any(WaybillNodePunch.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("待回单确认:取消打卡仍放行——那是退回进行中的唯一路径")
|
||||||
|
void keepCancelPunchOnPendingReturnConfirm() {
|
||||||
|
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<Collection<Long>> captor = ArgumentCaptor.forClass(Collection.class);
|
||||||
|
verify(nodePunchMapper).deleteBatchIds(captor.capture());
|
||||||
|
assertEquals(Set.of(7001L), new HashSet<>(captor.getValue()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("已完成运单仍可补录——门禁只对待回单确认生效")
|
||||||
|
void keepBackfillOnCompletedWaybill() {
|
||||||
|
Waybill waybill = waybill();
|
||||||
|
waybill.setBusinessStatus("completed");
|
||||||
|
when(waybillMapper.selectById(WAYBILL_ID)).thenReturn(waybill);
|
||||||
|
when(nodePunchMapper.selectList(any())).thenReturn(List.of());
|
||||||
|
|
||||||
|
WaybillPunchRecordSaveRequest request = request("node", "load", "装货");
|
||||||
|
request.setPunched(true);
|
||||||
|
request.setPunchTime("2026-10-06 09:30:00");
|
||||||
|
|
||||||
|
assertTrue(service.savePunchRecord(request));
|
||||||
|
|
||||||
|
verify(nodePunchMapper).insert(any(WaybillNodePunch.class));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("接单节点:补录与取消打卡都拒绝——它的完成由运单的接单事实决定")
|
@DisplayName("接单节点:补录与取消打卡都拒绝——它的完成由运单的接单事实决定")
|
||||||
void rejectAcceptNodePunchEdit() {
|
void rejectAcceptNodePunchEdit() {
|
||||||
|
|||||||
+39
-4
@@ -334,15 +334,31 @@ class WaybillProcessSupportTest {
|
|||||||
"TRANSPORT_JSON 的回单启用且没有 confirmMode:缺字段当没配「无需确认完成」,否则会跳过本该人工确认的回单");
|
"TRANSPORT_JSON 的回单启用且没有 confirmMode:缺字段当没配「无需确认完成」,否则会跳过本该人工确认的回单");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------ 待回单确认
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("待回单确认落库字面量为 pending_return_confirm,且接单归一不得改写它")
|
||||||
|
void pendingReturnConfirmIsTerminalStatus() {
|
||||||
|
assertEquals("pending_return_confirm", WaybillProcessSupport.STATUS_PENDING_RETURN_CONFIRM,
|
||||||
|
"库里存的是这个字面量,web 与小程序也按它判断,不能改名");
|
||||||
|
assertEquals(WaybillProcessSupport.STATUS_PENDING_RETURN_CONFIRM,
|
||||||
|
WaybillProcessSupport.normalizeBusinessStatus(WaybillProcessSupport.STATUS_PENDING_RETURN_CONFIRM, TRANSPORT_JSON),
|
||||||
|
"TRANSPORT_JSON 要求确认接单,未接单时归一结果本是 pending;运单已跑完全程,不能被改回待执行");
|
||||||
|
assertEquals(WaybillProcessSupport.STATUS_PENDING_RETURN_CONFIRM,
|
||||||
|
WaybillProcessSupport.normalizeBusinessStatus(WaybillProcessSupport.STATUS_PENDING_RETURN_CONFIRM, TRANSPORT_JSON, "accepted"));
|
||||||
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------ 结束门禁
|
// ------------------------------------------------------------ 结束门禁
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("结束门禁:只认已完成与已取消,大小写不敏感")
|
@DisplayName("结束门禁:已完成、已取消与待回单确认都算结束,大小写不敏感")
|
||||||
void punchEndedOnlyForCompletedAndCancelled() {
|
void punchEndedForCompletedCancelledAndPendingReturnConfirm() {
|
||||||
assertTrue(WaybillProcessSupport.isPunchEnded("completed"));
|
assertTrue(WaybillProcessSupport.isPunchEnded("completed"));
|
||||||
assertTrue(WaybillProcessSupport.isPunchEnded("COMPLETED"));
|
assertTrue(WaybillProcessSupport.isPunchEnded("COMPLETED"));
|
||||||
assertTrue(WaybillProcessSupport.isPunchEnded("cancelled"));
|
assertTrue(WaybillProcessSupport.isPunchEnded("cancelled"));
|
||||||
assertTrue(WaybillProcessSupport.isPunchEnded("Cancelled"));
|
assertTrue(WaybillProcessSupport.isPunchEnded("Cancelled"));
|
||||||
|
assertTrue(WaybillProcessSupport.isPunchEnded("pending_return_confirm"),
|
||||||
|
"司机已打完末位卡,不能再打卡");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -358,11 +374,12 @@ class WaybillProcessSupportTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("结束门禁文案:完成与取消各一句,非结束态没有文案")
|
@DisplayName("结束门禁文案:完成、取消与待回单确认各一句,非结束态没有文案")
|
||||||
void punchEndedReasonOnlyForEndedStatuses() {
|
void punchEndedReasonOnlyForEndedStatuses() {
|
||||||
assertEquals("运单已完成", WaybillProcessSupport.punchEndedReason("completed"));
|
assertEquals("运单已完成", WaybillProcessSupport.punchEndedReason("completed"));
|
||||||
assertEquals("运单已完成", WaybillProcessSupport.punchEndedReason(" COMPLETED "));
|
assertEquals("运单已完成", WaybillProcessSupport.punchEndedReason(" COMPLETED "));
|
||||||
assertEquals("运单已取消", WaybillProcessSupport.punchEndedReason("cancelled"));
|
assertEquals("运单已取消", WaybillProcessSupport.punchEndedReason("cancelled"));
|
||||||
|
assertEquals("运单待业务员确认回单", WaybillProcessSupport.punchEndedReason("pending_return_confirm"));
|
||||||
assertNull(WaybillProcessSupport.punchEndedReason(null));
|
assertNull(WaybillProcessSupport.punchEndedReason(null));
|
||||||
assertNull(WaybillProcessSupport.punchEndedReason(""));
|
assertNull(WaybillProcessSupport.punchEndedReason(""));
|
||||||
assertNull(WaybillProcessSupport.punchEndedReason(" "));
|
assertNull(WaybillProcessSupport.punchEndedReason(" "));
|
||||||
@@ -421,6 +438,22 @@ class WaybillProcessSupportTest {
|
|||||||
// 结束态由结束文案负责,两句话不能抢
|
// 结束态由结束文案负责,两句话不能抢
|
||||||
assertNull(WaybillProcessSupport.punchNotStartedReason("completed"));
|
assertNull(WaybillProcessSupport.punchNotStartedReason("completed"));
|
||||||
assertNull(WaybillProcessSupport.punchNotStartedReason("cancelled"));
|
assertNull(WaybillProcessSupport.punchNotStartedReason("cancelled"));
|
||||||
|
assertNull(WaybillProcessSupport.punchNotStartedReason("pending_return_confirm"),
|
||||||
|
"司机已打完末位卡,不能再被说成「尚未开始运输」");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("在途卡:待回单确认的运单与已完成同样不可打卡")
|
||||||
|
void transitCardOnPendingReturnConfirmWaybillIsVisibleButNotPunchable() {
|
||||||
|
LocalDateTime now = LocalDateTime.of(2026, 10, 20, 12, 0);
|
||||||
|
|
||||||
|
WaybillProcessSupport.TransitCheckinDecision decision =
|
||||||
|
WaybillProcessSupport.evaluateTransitCheckin(TRANSPORT_JSON, "pending_return_confirm", null, now);
|
||||||
|
|
||||||
|
assertTrue(decision.visible(), "卡片仍要下发,司机能回看");
|
||||||
|
assertFalse(decision.punchEnabled(), "末位卡已打完,在途卡不能再提交");
|
||||||
|
assertFalse(decision.doneToday());
|
||||||
|
assertFalse(decision.dueToday());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -455,9 +488,11 @@ class WaybillProcessSupportTest {
|
|||||||
assertNull(WaybillProcessSupport.reportBlockedReason("running"));
|
assertNull(WaybillProcessSupport.reportBlockedReason("running"));
|
||||||
assertEquals("运单尚未开始运输,暂不可上报异常", WaybillProcessSupport.reportBlockedReason("pending"));
|
assertEquals("运单尚未开始运输,暂不可上报异常", WaybillProcessSupport.reportBlockedReason("pending"));
|
||||||
assertEquals("运单尚未开始运输,暂不可上报异常", WaybillProcessSupport.reportBlockedReason("dispatching"));
|
assertEquals("运单尚未开始运输,暂不可上报异常", WaybillProcessSupport.reportBlockedReason("dispatching"));
|
||||||
// 结束优先:已完成/已取消先说结束,不提「等你上路」
|
// 结束优先:已结束态先说结束,不提「等你上路」
|
||||||
assertEquals("运单已完成,不能上报异常", WaybillProcessSupport.reportBlockedReason("completed"));
|
assertEquals("运单已完成,不能上报异常", WaybillProcessSupport.reportBlockedReason("completed"));
|
||||||
assertEquals("运单已取消,不能上报异常", WaybillProcessSupport.reportBlockedReason("cancelled"));
|
assertEquals("运单已取消,不能上报异常", WaybillProcessSupport.reportBlockedReason("cancelled"));
|
||||||
|
assertEquals("运单待业务员确认回单,不能上报异常",
|
||||||
|
WaybillProcessSupport.reportBlockedReason("pending_return_confirm"));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user