✅ 待回单确认:补查询分档、数字态、两套名称表与已到达口径的红测试
This commit is contained in:
+176
@@ -0,0 +1,176 @@
|
||||
package org.springblade.transport.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.MybatisConfiguration;
|
||||
import com.baomidou.mybatisplus.core.conditions.Wrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
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.ArgumentCaptor;
|
||||
import org.mockito.MockedStatic;
|
||||
import org.springblade.core.secure.utils.AuthUtil;
|
||||
import org.springblade.system.feign.IMpUserClient;
|
||||
import org.springblade.transport.mapper.ExceptionDisposalMapper;
|
||||
import org.springblade.transport.mapper.WaybillEnroutePunchMapper;
|
||||
import org.springblade.transport.mapper.WaybillNodePunchMapper;
|
||||
import org.springblade.transport.pojo.entity.Driver;
|
||||
import org.springblade.transport.pojo.entity.Waybill;
|
||||
import org.springblade.transport.pojo.entity.WaybillNodePunch;
|
||||
import org.springblade.transport.pojo.vo.DriverWaybillCardVO;
|
||||
import org.springblade.transport.pojo.vo.DriverWaybillTabCountsVO;
|
||||
import org.springblade.transport.service.IDriverService;
|
||||
import org.springblade.transport.service.IProcessConfigService;
|
||||
import org.springblade.transport.service.ITransportVehicleService;
|
||||
import org.springblade.transport.service.ITripQueryService;
|
||||
import org.springblade.transport.service.IWaybillService;
|
||||
import org.springblade.transport.support.CertificationGate;
|
||||
import org.springblade.transport.support.WaybillProcessSupport;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
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.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* 司机端四个 Tab 的状态分档。
|
||||
* <p>
|
||||
* 待回单确认是「等业务员动手」的进行中:数字态沿用 1(进行中),查询口径也必须认它。
|
||||
* 漏掉的话司机打完末位卡,这张单从他的列表里彻底消失——不是展示问题,是单子不见了。
|
||||
*/
|
||||
class DriverWaybillTabStatusTest {
|
||||
|
||||
private static final Long WAYBILL_ID = 5901L;
|
||||
private static final Long USER_ID = 7001L;
|
||||
private static final String PLATE = "桂AAW3256";
|
||||
private static final String PHONE = "13800138002";
|
||||
|
||||
private IWaybillService waybillService;
|
||||
private IDriverService driverService;
|
||||
private WaybillNodePunchMapper nodePunchMapper;
|
||||
private ExceptionDisposalMapper exceptionDisposalMapper;
|
||||
private DriverWaybillServiceImpl service;
|
||||
|
||||
@BeforeAll
|
||||
static void initTableInfo() {
|
||||
// 构造 LambdaQueryWrapper 需要 MyBatis-Plus 的实体元数据缓存
|
||||
MybatisConfiguration configuration = new MybatisConfiguration();
|
||||
MapperBuilderAssistant assistant = new MapperBuilderAssistant(configuration, "");
|
||||
assistant.setCurrentNamespace("test");
|
||||
TableInfoHelper.initTableInfo(assistant, Waybill.class);
|
||||
TableInfoHelper.initTableInfo(assistant, Driver.class);
|
||||
TableInfoHelper.initTableInfo(assistant, WaybillNodePunch.class);
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
waybillService = mock(IWaybillService.class);
|
||||
driverService = mock(IDriverService.class);
|
||||
nodePunchMapper = mock(WaybillNodePunchMapper.class);
|
||||
exceptionDisposalMapper = mock(ExceptionDisposalMapper.class);
|
||||
service = new DriverWaybillServiceImpl(
|
||||
waybillService,
|
||||
driverService,
|
||||
mock(IMpUserClient.class),
|
||||
mock(IProcessConfigService.class),
|
||||
mock(ITransportVehicleService.class),
|
||||
mock(CertificationGate.class),
|
||||
mock(WaybillEnroutePunchMapper.class),
|
||||
nodePunchMapper,
|
||||
exceptionDisposalMapper,
|
||||
mock(ITripQueryService.class));
|
||||
|
||||
when(driverService.getOne(any())).thenReturn(driver());
|
||||
when(waybillService.syncDriverAcceptState(any())).thenAnswer(invocation -> invocation.getArgument(0));
|
||||
when(waybillService.list(anyWrapper())).thenReturn(List.of(pendingReturnConfirmWaybill()));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("待回单确认落在「进行中」档:列表查得到,数字态是 1")
|
||||
void pendingReturnConfirmIsInProgressTier() {
|
||||
IPage<DriverWaybillCardVO> page = runAsDriver(() -> service.page(1, 10, 1, null));
|
||||
|
||||
assertEquals(1, page.getRecords().size(), "进行中档必须包含待回单确认,否则司机看不到这张单");
|
||||
assertEquals(Integer.valueOf(1), page.getRecords().get(0).getStatus(), "数字态沿用 1(进行中),不新增档位");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("「进行中」查询口径在库层就带上待回单确认")
|
||||
void inProgressQueryCarriesPendingReturnConfirm() {
|
||||
runAsDriver(() -> service.page(1, 10, 1, null));
|
||||
|
||||
assertTrue(queriedStatuses().contains(WaybillProcessSupport.STATUS_PENDING_RETURN_CONFIRM));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("「全部」tab 不漏掉待回单确认的运单")
|
||||
void allTabKeepsPendingReturnConfirm() {
|
||||
IPage<DriverWaybillCardVO> page = runAsDriver(() -> service.page(1, 10, null, null));
|
||||
|
||||
assertEquals(1, page.getRecords().size(), "打完末位卡的单不能从全部 tab 消失");
|
||||
assertEquals(Integer.valueOf(1), page.getRecords().get(0).getStatus());
|
||||
assertTrue(queriedStatuses().contains(WaybillProcessSupport.STATUS_PENDING_RETURN_CONFIRM));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Tab 计数把它算进进行中,不算已完成")
|
||||
void tabCountsCountsItAsDoing() {
|
||||
DriverWaybillTabCountsVO counts = runAsDriver(service::tabCounts);
|
||||
|
||||
assertEquals(1, counts.getDoing(), "待回单确认仍是进行中,不是已完成");
|
||||
assertEquals(0, counts.getDone());
|
||||
assertEquals(1, counts.getAll());
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------ 夹具
|
||||
|
||||
private <T> T runAsDriver(Supplier<T> action) {
|
||||
try (MockedStatic<AuthUtil> authUtil = mockStatic(AuthUtil.class)) {
|
||||
authUtil.when(AuthUtil::getUserId).thenReturn(USER_ID);
|
||||
return action.get();
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Collection<Object> queriedStatuses() {
|
||||
ArgumentCaptor<Wrapper<Waybill>> captor = ArgumentCaptor.forClass(Wrapper.class);
|
||||
verify(waybillService).list(captor.capture());
|
||||
return ((LambdaQueryWrapper<Waybill>) captor.getValue()).getParamNameValuePairs().values();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static Wrapper<Waybill> anyWrapper() {
|
||||
return org.mockito.ArgumentMatchers.<Wrapper<Waybill>>any();
|
||||
}
|
||||
|
||||
private Waybill pendingReturnConfirmWaybill() {
|
||||
Waybill waybill = new Waybill();
|
||||
waybill.setId(WAYBILL_ID);
|
||||
waybill.setWaybillNo("YD202610080002");
|
||||
waybill.setVehicleNo(PLATE);
|
||||
waybill.setDriverPhone(PHONE);
|
||||
waybill.setBusinessStatus(WaybillProcessSupport.STATUS_PENDING_RETURN_CONFIRM);
|
||||
waybill.setIsDeleted(0);
|
||||
return waybill;
|
||||
}
|
||||
|
||||
private Driver driver() {
|
||||
Driver driver = new Driver();
|
||||
driver.setId(9001L);
|
||||
driver.setUserId(USER_ID);
|
||||
driver.setDrivingVehicle(PLATE);
|
||||
driver.setMobile(PHONE);
|
||||
return driver;
|
||||
}
|
||||
|
||||
}
|
||||
+150
@@ -0,0 +1,150 @@
|
||||
package org.springblade.transport.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.MybatisConfiguration;
|
||||
import com.baomidou.mybatisplus.core.conditions.Wrapper;
|
||||
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.springblade.transport.mapper.MasterOrderMapper;
|
||||
import org.springblade.transport.pojo.entity.MasterOrder;
|
||||
import org.springblade.transport.pojo.entity.TransportPlan;
|
||||
import org.springblade.transport.pojo.entity.Waybill;
|
||||
import org.springblade.transport.pojo.vo.MasterOrderVO;
|
||||
import org.springblade.transport.service.IContractManageService;
|
||||
import org.springblade.transport.service.IProjectApplyService;
|
||||
import org.springblade.transport.service.ITransportPlanService;
|
||||
import org.springblade.transport.service.IWaybillService;
|
||||
import org.springblade.transport.support.WaybillProcessSupport;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* 多联总单分段进度的「已到达数量」口径。
|
||||
* <p>
|
||||
* 到货但回单待确认的运单算到达:货物确实到了,回单只是单据。
|
||||
* 不算会让下一段被「调度总量不能大于上一段已到达数量」挡住,整条线路卡在第二段。
|
||||
*/
|
||||
class MasterOrderProgressTest {
|
||||
|
||||
private static final Long MASTER_ID = 6101L;
|
||||
private static final String MASTER_NO = "ZD20261008001";
|
||||
private static final String ROUTE_JSON = """
|
||||
[{"segmentNo":"1","transportType":"公路运输","departureName":"南宁","arrivalName":"北海"},
|
||||
{"segmentNo":"2","transportType":"铁路运输","departureName":"北海","arrivalName":"昆明"}]
|
||||
""";
|
||||
|
||||
private MasterOrderMapper masterOrderMapper;
|
||||
private IWaybillService waybillService;
|
||||
private ITransportPlanService transportPlanService;
|
||||
private MasterOrderServiceImpl service;
|
||||
|
||||
@BeforeAll
|
||||
static void initTableInfo() {
|
||||
// 构造 LambdaQueryWrapper 需要 MyBatis-Plus 的实体元数据缓存
|
||||
MybatisConfiguration configuration = new MybatisConfiguration();
|
||||
MapperBuilderAssistant assistant = new MapperBuilderAssistant(configuration, "");
|
||||
assistant.setCurrentNamespace("test");
|
||||
TableInfoHelper.initTableInfo(assistant, Waybill.class);
|
||||
TableInfoHelper.initTableInfo(assistant, TransportPlan.class);
|
||||
TableInfoHelper.initTableInfo(assistant, MasterOrder.class);
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
void setUp() throws Exception {
|
||||
masterOrderMapper = mock(MasterOrderMapper.class);
|
||||
waybillService = mock(IWaybillService.class);
|
||||
transportPlanService = mock(ITransportPlanService.class);
|
||||
service = new MasterOrderServiceImpl(
|
||||
waybillService,
|
||||
transportPlanService,
|
||||
mock(IProjectApplyService.class),
|
||||
mock(IContractManageService.class));
|
||||
inject("baseMapper", masterOrderMapper);
|
||||
|
||||
when(transportPlanService.list(anyWrapper())).thenReturn(List.of());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("到货但回单待确认的运单计入「已到达数量」")
|
||||
void arrivedQuantityCountsPendingReturnConfirm() {
|
||||
when(masterOrderMapper.selectById(MASTER_ID)).thenReturn(masterOrder());
|
||||
when(waybillService.list(anyWrapper())).thenReturn(List.of(
|
||||
child(1L, "1", WaybillProcessSupport.STATUS_PENDING_RETURN_CONFIRM, "8")));
|
||||
|
||||
MasterOrderVO vo = service.detail(MASTER_ID);
|
||||
|
||||
assertEquals(0, decimalOf(vo.getRouteProgress().get(0).get("arrivedQuantity"))
|
||||
.compareTo(new BigDecimal("8")), "回单待确认不等于没到货");
|
||||
assertEquals(0, decimalOf(vo.getRouteProgress().get(1).get("arrivedQuantity")).signum(),
|
||||
"第二段还没有到货的运单");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("已到达口径:已完成 + 待回单确认,进行中不算")
|
||||
void arrivedQuantityIgnoresRunning() {
|
||||
when(masterOrderMapper.selectById(MASTER_ID)).thenReturn(masterOrder());
|
||||
when(waybillService.list(anyWrapper())).thenReturn(List.of(
|
||||
child(1L, "1", "running", "8"),
|
||||
child(2L, "1", "completed", "5"),
|
||||
child(3L, "1", WaybillProcessSupport.STATUS_PENDING_RETURN_CONFIRM, "3")));
|
||||
|
||||
MasterOrderVO vo = service.detail(MASTER_ID);
|
||||
|
||||
assertEquals(0, decimalOf(vo.getRouteProgress().get(0).get("arrivedQuantity"))
|
||||
.compareTo(new BigDecimal("8")), "5 + 3:进行中的 8 吨还没到,不能算到达");
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------ 夹具
|
||||
|
||||
private MasterOrder masterOrder() {
|
||||
MasterOrder masterOrder = new MasterOrder();
|
||||
masterOrder.setId(MASTER_ID);
|
||||
masterOrder.setMasterNo(MASTER_NO);
|
||||
masterOrder.setRouteJson(ROUTE_JSON);
|
||||
return masterOrder;
|
||||
}
|
||||
|
||||
private Waybill child(Long id, String segmentNo, String businessStatus, String quantity) {
|
||||
Waybill waybill = new Waybill();
|
||||
waybill.setId(id);
|
||||
waybill.setMasterNo(MASTER_NO);
|
||||
waybill.setRelationNo(segmentNo);
|
||||
waybill.setBusinessStatus(businessStatus);
|
||||
waybill.setGoodsJson("[{\"cargoName\":\"钢材\",\"quantity\":" + quantity + "}]");
|
||||
return waybill;
|
||||
}
|
||||
|
||||
private BigDecimal decimalOf(Object value) {
|
||||
return value == null ? BigDecimal.ZERO : (BigDecimal) value;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <T> Wrapper<T> anyWrapper() {
|
||||
return org.mockito.ArgumentMatchers.<Wrapper<T>>any();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
+17
@@ -193,6 +193,23 @@ class TripAggregationTest {
|
||||
assertTrue(TripAggregation.CHILD_STATUSES.containsAll(TripAggregation.TERMINAL_STATUSES));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("待回单确认的子单仍进运次,且不会把运次判成已完成")
|
||||
void pendingReturnConfirmChildKeepsTripRunning() {
|
||||
String pendingReturnConfirm = org.springblade.transport.support.WaybillProcessSupport.STATUS_PENDING_RETURN_CONFIRM;
|
||||
|
||||
assertTrue(TripAggregation.CHILD_STATUSES.contains(pendingReturnConfirm),
|
||||
"漏掉这个状态,子单会从运次读服务消失,配载单被当成全部完成");
|
||||
|
||||
// 与一张已完成的子单拼载:还有子单没走完,运次只能是运输中
|
||||
List<Waybill> children = List.of(
|
||||
waybill("completed", null, null),
|
||||
waybill(pendingReturnConfirm, null, null));
|
||||
String status = TripAggregation.statusOf(children, false, false);
|
||||
assertEquals("running", status);
|
||||
assertEquals(1, TripAggregation.appStatus(status), "运次不进已完成档");
|
||||
}
|
||||
|
||||
private Waybill waybill(String businessStatus, String acceptStatus, String processJson) {
|
||||
Waybill waybill = new Waybill();
|
||||
waybill.setBusinessStatus(businessStatus);
|
||||
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
package org.springblade.transport.service.impl;
|
||||
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springblade.transport.mapper.MasterOrderMapper;
|
||||
import org.springblade.transport.mapper.ReceivablePayableCargoFeeMapper;
|
||||
import org.springblade.transport.mapper.ReceivablePayableChangeRecordMapper;
|
||||
import org.springblade.transport.service.IContractManageService;
|
||||
import org.springblade.transport.service.ICommonAddressService;
|
||||
import org.springblade.transport.service.IFormalSettlementService;
|
||||
import org.springblade.transport.service.IPreSettlementService;
|
||||
import org.springblade.transport.service.IWaybillService;
|
||||
import org.springblade.transport.support.WaybillProcessSupport;
|
||||
import org.springblade.transport.wrapper.WaybillWrapper;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* 运单业务状态的中文名,两套表并存(历史结构,不合并):运单管理一套、应收应付一套。
|
||||
* <p>
|
||||
* 两套都要认待回单确认:漏一套就在页面上露出原始英文码。
|
||||
*/
|
||||
class WaybillBusinessStatusNameTest {
|
||||
|
||||
private static final String PENDING_RETURN_CONFIRM = WaybillProcessSupport.STATUS_PENDING_RETURN_CONFIRM;
|
||||
|
||||
@Test
|
||||
@DisplayName("运单管理名称表给出「待回单确认」——导出与详情共用这张表")
|
||||
void waybillWrapperNamesPendingReturnConfirm() {
|
||||
assertEquals("待回单确认", WaybillWrapper.businessStatusName(PENDING_RETURN_CONFIRM));
|
||||
// 相邻档位不受影响
|
||||
assertEquals("进行中", WaybillWrapper.businessStatusName("running"));
|
||||
assertEquals("已完成", WaybillWrapper.businessStatusName("completed"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("应收应付的第二套名称表同步给出「待回单确认」")
|
||||
void receivablePayableNamesPendingReturnConfirm() throws Exception {
|
||||
assertEquals("待回单确认", receivableStatusName(PENDING_RETURN_CONFIRM));
|
||||
}
|
||||
|
||||
/**
|
||||
* 应收应付那张表只被 waybillMap 用,而 waybillMap 当前没有调用方,
|
||||
* 没有公共接缝;它是纯函数,按既有做法直接取私有方法(同 LoadingManageChildStatusWriteTest)。
|
||||
*/
|
||||
private String receivableStatusName(String status) throws Exception {
|
||||
ReceivablePayableDetailServiceImpl service = new ReceivablePayableDetailServiceImpl(
|
||||
mock(ReceivablePayableCargoFeeMapper.class),
|
||||
mock(ReceivablePayableChangeRecordMapper.class),
|
||||
mock(MasterOrderMapper.class),
|
||||
mock(IWaybillService.class),
|
||||
mock(IContractManageService.class),
|
||||
mock(ICommonAddressService.class),
|
||||
mock(IPreSettlementService.class),
|
||||
mock(IFormalSettlementService.class));
|
||||
Method method = ReceivablePayableDetailServiceImpl.class
|
||||
.getDeclaredMethod("waybillStatusName", String.class);
|
||||
method.setAccessible(true);
|
||||
return (String) method.invoke(service, status);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user