Merge branch 'test' into dev

This commit is contained in:
2026-09-14 17:04:24 +08:00
118 changed files with 2210 additions and 614 deletions
@@ -233,11 +233,9 @@ class DispatchPdaWorkflowServiceTest {
order.setVehiclePlate("CA-100");
DispatchPdaTask task = task(11L, "100", "WH-1", "COMPLETED");
DispatchItem item = item(21L, "100", 6);
DispatchPdaPick pick = new DispatchPdaPick();
pick.setPackages(6L);
DispatchPdaPick pick = new DispatchPdaPick(); pick.setPackages(6L);
OutboundOrder outbound = new OutboundOrder();
outbound.setOutboundNo("OB-1");
outbound.setStatus("WAIT_OUTBOUND");
outbound.setOutboundNo("OB-1"); outbound.setStatus("WAIT_OUTBOUND");
when(orders.findGlobalByDispatchNo("DSP-1")).thenReturn(order);
when(orders.lockById("100", 1L)).thenReturn(order);
when(outboundOrders.lockByDispatch("100", 1L)).thenReturn(outbound);
@@ -245,17 +243,17 @@ class DispatchPdaWorkflowServiceTest {
when(items.findByDispatchOrder("100", 1L)).thenReturn(List.of(item));
when(picks.totalItem("100", 11L, 21L)).thenReturn(6L);
when(picks.listTask("100", 11L)).thenReturn(List.of(pick));
DispatchPdaResponses.Departed result = service.depart("WH-1", "DSP-1", "7",
new DispatchPdaRequests.Depart("depart-success", null));
when(palletLocks.selectList(any())).thenReturn(List.of());
DispatchPdaResponses.Departed result = service.depart(
"WH-1", "DSP-1", "7", new DispatchPdaRequests.Depart("depart-1", null));
assertEquals("DEPARTED", result.status());
assertEquals("DEPARTED", order.getStatus());
assertTrue(order.getDepartAt() != null);
assertEquals("OUTBOUNDED", outbound.getStatus());
when(palletLocks.selectList(any())).thenReturn(List.of());
assertNotNull(order.getDepartAt());
assertEquals(order.getDepartAt(), order.getUpdatedAt());
verify(orders).updateById(order);
assertEquals("DEPARTED", order.getStatus());
assertEquals("OUTBOUNDED", outbound.getStatus());
}
@Test
@@ -120,6 +120,25 @@ class ContainerInboundPlanPreviewTest {
assertEquals("RESOURCE_PENDING", result.groups().get(0).orders().get(0).readiness());
}
@Test
void blocksExistingDropshipPlanWhosePalletCountDiffersFromFrozenSourcePallets() {
when(orderMapper.containerPreviewOrders("BOOKING-DROPSHIP-MISMATCH")).thenReturn(List.of(
row(1L, "LM-DROPSHIP", "ONE_CLICK_DROPSHIP", "OVERSEAS", "US-LAX-01", "洛杉矶仓", 2,
"IP-DROPSHIP", 2, 1, "PENDING_ASSIGNMENT", 0)
));
when(dropshipOrders.palletSnapshots("100", 1L)).thenReturn(List.of(
new DropshipPalletSnapshot("DP-260914-0001", 10, List.of())));
var result = service.previewContainer("BOOKING-DROPSHIP-MISMATCH");
var order = result.groups().get(0).orders().get(0);
assertFalse(result.ready());
assertEquals("BLOCKED", order.readiness());
assertEquals(List.of("DROPSHIP_PALLET_COUNT_MISMATCH"), order.blockers());
assertEquals(1, order.sourcePalletCount());
assertEquals(2, order.targetPalletCount());
}
@Test
void exposesHeadLegExistingSupplementAndFinalPalletCountsWithoutBlocking() {
when(orderMapper.containerPreviewOrders("BOOKING-SUPPLEMENT")).thenReturn(List.of(
@@ -183,6 +183,43 @@ class InboundPlanServiceTest {
verify(commandMapper, never()).insert(any(com.baiyue.lastleg.entity.LastlegOrderCommandRecord.class));
}
@Test
void oneClickDropshipManualPlanMustKeepTheFrozenSourcePalletCount() {
SourceOrderSnapshot source = order(2L, "RK-2", "3.3650", "WH-A");
when(orderMapper.lockOrder("100", 2L)).thenReturn(List.of(source));
when(orderMapper.findOrderType("100", 2L)).thenReturn("ONE_CLICK_DROPSHIP");
when(dropshipOrders.palletSnapshots("100", 2L)).thenReturn(List.of(
new DropshipPalletSnapshot("DP-260914-0001", 10L, List.of())));
ResponseStatusException error = assertThrows(ResponseStatusException.class, () -> service.convertManual(
new ManualInboundPlanRequest("100", 2L, 0, LocalDate.of(2026, 9, 15),
2, "WOOD", false, false, false, null), "dropship-pallet-mismatch", "900"));
assertEquals(400, error.getStatusCode().value());
assertEquals("一件代发人工计划托数必须与头程封托托盘数一致:1", error.getReason());
verify(planMapper, never()).insert(any(InboundPlan.class));
verify(commandMapper, never()).insert(any(com.baiyue.lastleg.entity.LastlegOrderCommandRecord.class));
}
@Test
void oneClickDropshipUsesFrozenSourcePalletCountInsteadOfVolumeSuggestion() {
SourceOrderSnapshot source = order(2L, "RK-2", "3.3650", "WH-A");
when(orderMapper.lockOrder("100", 2L)).thenReturn(List.of(source));
when(orderMapper.findOrderType("100", 2L)).thenReturn("ONE_CLICK_DROPSHIP");
when(dropshipOrders.palletSnapshots("100", 2L)).thenReturn(List.of(
new DropshipPalletSnapshot("DP-260914-0001", 10L, List.of())));
when(orderMapper.markPlannedManual(eq("100"), eq(2L), any(), eq(1), eq(false), eq(0), any()))
.thenReturn(1);
var result = service.convertManual(new ManualInboundPlanRequest("100", 2L, 0,
LocalDate.of(2026, 9, 15), 1, "WOOD", false, false, false, null),
"dropship-exact-pallet-count", "900");
assertEquals(1, result.palletCount());
verify(planMapper).insert(any(InboundPlan.class));
verify(palletMapper).insert(any(InboundPlanPallet.class));
}
@Test
void containerConversionBindsRealWarehousePalletsBackToSignedManualPlanBeforeReceiving() {
InboundPlan plan = existingPlan("100", 2L, "IP-MANUAL");
@@ -12,6 +12,28 @@ import java.util.Map;
import static org.assertj.core.api.Assertions.assertThat;
class ReturnCartonQueryTest {
@Test
void baseMapperReplayColumnExistsInEveryReturnCartonBaseline() throws Exception {
var table = com.baomidou.mybatisplus.core.metadata.TableInfoHelper.initTableInfo(
new org.apache.ibatis.builder.MapperBuilderAssistant(
new com.baomidou.mybatisplus.core.MybatisConfiguration(), "return-carton-contract"),
com.baiyue.lastleg.returnreceipt.entity.ReturnInventoryCarton.class);
assertThat(table.getAllSqlSelect()).contains("new_carton_mark");
for (var path : java.util.List.of(java.nio.file.Path.of("src/main/resources/db/lastleg.sql"),
java.nio.file.Path.of("../deploy/sql/海外区域统一增量-MySQL8.sql"))) {
String sql = java.nio.file.Files.readString(path);
var matcher = java.util.regex.Pattern.compile(
"CREATE TABLE IF NOT EXISTS ll_return_inventory_carton \\(([\\s\\S]*?)\\) ENGINE")
.matcher(sql);
int definitions = 0;
while (matcher.find()) {
definitions++;
assertThat(matcher.group(1)).as(path.toString()).contains("new_carton_mark VARCHAR(255)");
}
assertThat(definitions).isPositive();
}
}
@Test
void bothQueriesUseExactCustomerScopeAndSkuExistenceWithoutAnOuterJoin() throws Exception {
var query = new ReturnInventoryRequests.Query(2, 1, "42", "SKU", "RC", "AVAILABLE", "RT");
@@ -147,6 +147,32 @@ class LoadingCompletedConsumerTest {
verify(cartons,never()).insert(any(com.baiyue.lastleg.inboundplan.entity.InboundPlanCarton.class));
verify(orders,never()).insert(any(LastlegOrder.class));
}
@Test void consumesOneDropshipSourceDetailSplitAcrossTwoPallets() {
EventEnvelope original=overseasDropshipEvent(false);
var node=(com.fasterxml.jackson.databind.node.ObjectNode)original.payload().deepCopy();
var order=(com.fasterxml.jackson.databind.node.ObjectNode)node.path("orders").get(0);
var secondItem=(com.fasterxml.jackson.databind.node.ObjectNode)order.path("items").get(0).deepCopy();
((com.fasterxml.jackson.databind.node.ArrayNode)order.path("items")).add(secondItem);
order.put("packageCount",10).put("productQuantity",100).put("palletCount",2);
var secondPallet=(com.fasterxml.jackson.databind.node.ObjectNode)order.path("pallets").get(0).deepCopy();
secondPallet.put("palletNo","DP-260914-0022");
var secondSku=(com.fasterxml.jackson.databind.node.ObjectNode)secondPallet.path("skuPackages").get(0);
var marks=secondSku.putArray("cartonMarks"); for(int n=6;n<=10;n++)marks.add("RK1#10-"+n);
((com.fasterxml.jackson.databind.node.ArrayNode)order.path("pallets")).add(secondPallet);
service.consume(rehash(original,node));
var detailCaptor=ArgumentCaptor.forClass(LastlegOrderDetail.class);
verify(details).insert(detailCaptor.capture());
assertEquals("d-1",detailCaptor.getValue().getSourceInboundDetailId());
assertEquals(10,detailCaptor.getValue().getPackageCount());
assertEquals(100,detailCaptor.getValue().getProductQuantity());
verify(cartons,times(10)).insert(any(com.baiyue.lastleg.inboundplan.entity.InboundPlanCarton.class));
var received=ArgumentCaptor.forClass(DropshipRequests.Receive.class);
verify(dropshipOrders).receive(received.capture());
assertEquals(List.of("DP-260909-0143","DP-260914-0022"),
received.getValue().pallets().stream().map(DropshipRequests.Pallet::palletNo).toList());
}
@Test void mapsSameSkuDifferentSourceDetailsWithoutGuessing() {
EventEnvelope original=overseasDropshipEvent(false);
var node=(com.fasterxml.jackson.databind.node.ObjectNode)original.payload().deepCopy();
@@ -222,7 +222,7 @@ class WarehouseTransferServiceTest {
when(sourceOrders.selectList(any())).thenReturn(List.of());
var result = service.review("TENANT-A", "WTR-1", "USER-1",
new WarehouseTransferRequests.Review("APPROVE", "approved"));
new WarehouseTransferRequests.Review("APPROVE", null));
assertEquals(WarehouseTransferService.APPROVED, transfer.getStatus());
assertEquals(WarehouseTransferService.PENDING_ASSIGNMENT, tailOrder.getCurrentStatus());
@@ -233,6 +233,41 @@ class WarehouseTransferServiceTest {
verifyNoInteractions(inventory);
}
@Test
void rejectionRequiresNonBlankReasonBeforeAnyStateOrInventoryChange() {
for (String decision : List.of("REJECT", "REJECTED")) {
for (String reason : new String[] { null, "", " \t\n" }) {
var error = assertThrows(ResponseStatusException.class, () -> service.review(
"TENANT-A", "WTR-1", "USER-1", new WarehouseTransferRequests.Review(decision, reason)));
assertEquals(400, error.getStatusCode().value());
assertEquals("驳回原因不能为空", error.getReason());
}
}
verifyNoInteractions(transfers, orders, orderHistories, inventory);
}
@Test
void rejectionWithReasonCancelsLinkedOrderAndPreservesReasonInHistory() {
WarehouseTransferOrder transfer = persistedTransfer();
transfer.setStatus(WarehouseTransferService.PENDING_REVIEW);
LastlegOrder tailOrder = historicalOrder();
tailOrder.setId(31L); tailOrder.setCurrentStatus(WarehouseTransferService.PENDING_REVIEW);
when(transfers.lockNo("TENANT-A", "WTR-1")).thenReturn(transfer);
when(transfers.findNo("TENANT-A", "WTR-1")).thenReturn(transfer);
when(orders.selectById(31L)).thenReturn(tailOrder);
when(transferItems.selectList(any())).thenReturn(List.of());
when(sourceOrders.selectList(any())).thenReturn(List.of());
var result = service.review("TENANT-A", "WTR-1", "USER-1",
new WarehouseTransferRequests.Review("REJECT", " 地址不完整 "));
assertEquals("REJECTED", result.status());
assertEquals("CANCELLED", tailOrder.getCurrentStatus());
var history = ArgumentCaptor.forClass(com.baiyue.lastleg.entity.LastlegOrderStatusHistory.class);
verify(orderHistories).insert(history.capture());
assertTrue(history.getValue().getRemark().endsWith("; 地址不完整"));
verifyNoInteractions(inventory);
}
@Test
void approvedTransferAllocatesExactSourceInboundPalletWithoutMovingInventory() {
WarehouseTransferOrder transfer = transfer("APPROVED");