fix: 按物理箱贯通一件代发履约箱数

This commit is contained in:
2026-09-16 20:49:35 +08:00
parent 8797c0bef9
commit 545ad2083d
34 changed files with 779 additions and 132 deletions
@@ -13,20 +13,22 @@ class DropshipOutboundCandidateMapperContractTest {
String sql = select("listOutboundCandidates", String.class, String.class);
for (String token : new String[] {"p.customer_id=#{customerId}", "p.status='ACTIVE'",
"o.order_type='ONE_CLICK_DROPSHIP'", "COALESCE(o.detained,0)=0", "pos.status='OCCUPIED'",
"pos.available_packages>0", "s.package_count>0", "c.status='AVAILABLE'",
"pos.available_packages>0", "c.status='AVAILABLE'", "COUNT(*)=1", "ELSE 'MIXED'",
"ll_dispatch_pallet_lock", "ll_warehouse_transfer_inventory_allocation",
"ll_outbound_pallet_item", "pos.overseas_warehouse_id=#{warehouseId}", "LIMIT 500"}) {
assertTrue(sql.contains(token), token);
}
assertTrue(sql.contains("LEAST(pos.available_packages,s.package_count"));
assertTrue(sql.contains("LEAST(pos.available_packages,(SELECT COUNT(*) FROM ll_dropship_carton"));
assertTrue(sql.contains("#{customerId} IS NULL OR #{customerId}='' OR p.customer_id=#{customerId}"));
assertTrue(sql.contains("ORDER BY b.overseas_warehouse_name,p.pallet_no,sku_code"));
assertTrue(!sql.contains("ORDER BY b.overseas_warehouse_name,p.pallet_no,s.sku_code"));
}
@Test
void creationLockUsesTheSameEligibilityContractAndLocksOneSkuDimension() throws Exception {
void creationLockUsesTheSameEligibilityContractAndLocksOnePhysicalPalletDimension() throws Exception {
String sql = select("lockOutboundCandidate", String.class, Long.class, String.class);
assertTrue(sql.contains("AND pos.id=#{positionId}"));
assertTrue(sql.contains("AND s.sku_code=#{skuCode}"));
assertTrue(sql.contains("AND #{skuCode}=(SELECT CASE WHEN COUNT(*)=1"));
assertTrue(sql.contains("LIMIT 1 FOR UPDATE"));
assertTrue(sql.contains("ll_dispatch_pallet_lock"));
assertTrue(sql.contains("ll_outbound_pallet_item"));
@@ -34,8 +34,10 @@ import com.baiyue.lastleg.inventory.mapper.InventoryBalanceMapper;
import com.baiyue.lastleg.transfer.mapper.WarehouseTransferPalletMapper;
import com.baiyue.lastleg.dropship.entity.DropshipOrderProfile;
import com.baiyue.lastleg.dropship.entity.DropshipCarton;
import com.baiyue.lastleg.dropship.entity.DropshipCartonSku;
import com.baiyue.lastleg.dropship.mapper.DropshipOrderProfileMapper;
import com.baiyue.lastleg.dropship.mapper.DropshipCartonMapper;
import com.baiyue.lastleg.dropship.mapper.DropshipCartonSkuMapper;
import com.baiyue.lastleg.dropship.mapper.DropshipSkuInventoryMapper;
import com.baiyue.lastleg.dropship.model.DropshipOutboundCandidate;
import org.junit.jupiter.api.BeforeEach;
@@ -83,6 +85,7 @@ class DeliveryTransferServiceTest {
@Mock InventoryBalanceMapper inventoryBalances;
@Mock DropshipOrderProfileMapper dropshipProfiles;
@Mock DropshipCartonMapper dropshipCartons;
@Mock DropshipCartonSkuMapper dropshipCartonSkus;
@Mock DropshipSkuInventoryMapper dropshipSkuInventory;
@Mock WarehouseTransferPalletMapper warehouseTransferPallets;
@@ -112,6 +115,7 @@ class DeliveryTransferServiceTest {
ReflectionTestUtils.setField(service, "inventoryBalances", inventoryBalances);
ReflectionTestUtils.setField(service, "dropshipProfiles", dropshipProfiles);
ReflectionTestUtils.setField(service, "dropshipCartons", dropshipCartons);
ReflectionTestUtils.setField(service, "dropshipCartonSkus", dropshipCartonSkus);
ReflectionTestUtils.setField(service, "dropshipSkuInventory", dropshipSkuInventory);
ReflectionTestUtils.setField(service, "warehouseTransferPallets", warehouseTransferPallets);
}
@@ -458,7 +462,7 @@ class DeliveryTransferServiceTest {
for (long i = 1; i <= 2; i++) {
when(dropshipProfiles.lockOutboundCandidate("TENANT-A", 900L + i, "SKU-1"))
.thenReturn(dropshipCandidate(900L + i, 1000L + i, "WH-1", "DP-SHARED", "SKU-1", 1L));
when(dropshipCartons.lockAvailable("TENANT-A", 1000L + i, "SKU-1", 1L))
when(dropshipCartons.lockAvailablePhysical("TENANT-A", 1000L + i, 1L))
.thenReturn(List.of(new DropshipCarton()));
InventoryPosition position = new InventoryPosition();
position.setId(900L + i); position.setCustomerId("TENANT-A"); position.setBalanceId(800L + i);
@@ -489,7 +493,7 @@ class DeliveryTransferServiceTest {
when(inventoryPositions.selectById(901L)).thenReturn(position);
DropshipOutboundCandidate candidate = dropshipCandidate(901L, 1001L, "WH-1", "PLT-501", "MIXED", 1L);
when(dropshipProfiles.lockOutboundCandidate("TENANT-A", 901L, "MIXED")).thenReturn(candidate);
when(dropshipCartons.lockAvailable("TENANT-A", 1001L, "MIXED", 1L)).thenReturn(List.of());
when(dropshipCartons.lockAvailablePhysical("TENANT-A", 1001L, 1L)).thenReturn(List.of());
ResponseStatusException exception = assertThrows(ResponseStatusException.class, () -> service.createOneClick(
"TENANT-A", "U-1", new DeliveryRequests.OneClickOutbound("recipient", List.of(
@@ -807,15 +811,15 @@ class DeliveryTransferServiceTest {
OutboundPdaTask task = new OutboundPdaTask(); task.setId(701L); task.setCustomerId("TENANT-A");
task.setOutboundOrderId(301L); task.setTaskNo("PDA-701"); task.setStatus("PENDING");
OutboundPalletItem item = pallet(501L, "DP-1", "ONT8", "Ontario", "IN-A", 2);
item.setSkuCode("SKU-1"); item.setCartonMarks("BOX-A,BOX-B");
item.setSkuCode("MIXED"); item.setCartonMarks("BOX-A,BOX-B");
InventoryPosition position = new InventoryPosition(); position.setId(901L); position.setCustomerId("TENANT-A");
position.setBalanceId(801L); position.setPalletId(501L); position.setOverseasWarehouseId("WH-1");
InventoryBalance balance = new InventoryBalance(); balance.setCustomerId("TENANT-A"); balance.setInboundCode("IN-A");
DropshipOrderProfile profile = new DropshipOrderProfile(); profile.setId(1001L);
DropshipCarton first = new DropshipCarton(); first.setId(1101L); first.setProductQuantity(7L);
DropshipCarton second = new DropshipCarton(); second.setId(1102L); second.setProductQuantity(13L);
first.setStatus("AVAILABLE"); first.setSkuCode("SKU-1"); first.setCartonMark("BOX-A");
second.setStatus("AVAILABLE"); second.setSkuCode("SKU-1"); second.setCartonMark("BOX-B");
first.setStatus("AVAILABLE"); first.setSkuCode("MIXED"); first.setCartonMark("BOX-A");
second.setStatus("AVAILABLE"); second.setSkuCode("MIXED"); second.setCartonMark("BOX-B");
when(pdaTasks.lockByNoAndWarehouse("PDA-701", "WH-1")).thenReturn(task);
when(outboundOrders.selectById(301L)).thenReturn(outbound);
when(outboundOrders.lockByNo("TENANT-A", "OUT-301")).thenReturn(outbound);
@@ -825,8 +829,14 @@ class DeliveryTransferServiceTest {
when(dropshipProfiles.findPosition("TENANT-A", 901L)).thenReturn(profile);
when(dropshipCartons.findByProfileAndMark("TENANT-A", 1001L, "BOX-A")).thenReturn(first);
when(dropshipCartons.findByProfileAndMark("TENANT-A", 1001L, "BOX-B")).thenReturn(second);
when(dropshipCartonSkus.listCarton("TENANT-A", 1101L)).thenReturn(List.of(
cartonSku("SKU-1", 7), cartonSku("SKU-2", 3)));
when(dropshipCartonSkus.listCarton("TENANT-A", 1102L)).thenReturn(List.of(
cartonSku("SKU-1", 13), cartonSku("SKU-2", 2)));
when(dropshipSkuInventory.deduct("TENANT-A", 1001L, "SKU-1", 1L, 7L)).thenReturn(1);
when(dropshipSkuInventory.deduct("TENANT-A", 1001L, "SKU-2", 1L, 3L)).thenReturn(1);
when(dropshipSkuInventory.deduct("TENANT-A", 1001L, "SKU-1", 1L, 13L)).thenReturn(1);
when(dropshipSkuInventory.deduct("TENANT-A", 1001L, "SKU-2", 1L, 2L)).thenReturn(1);
when(outboundPallets.findByOutbound("TENANT-A", 301L)).thenReturn(List.of(item));
assertThrows(ResponseStatusException.class, () -> service.pdaPick("WH-1", "U-1", "PDA-701",
new DeliveryRequests.PdaPick(item.getId(), 901L, 1L, "wrong-mark", List.of("WRONG-BOX"))));
@@ -837,7 +847,9 @@ class DeliveryTransferServiceTest {
new DeliveryRequests.PdaPick(item.getId(), 901L, 1L, "repeat-box-new-key", List.of("BOX-A"))));
service.pdaPick("WH-1", "U-1", "PDA-701", new DeliveryRequests.PdaPick(item.getId(), 901L, 1L, "pick-13", List.of("BOX-B")));
verify(dropshipSkuInventory).deduct("TENANT-A", 1001L, "SKU-1", 1L, 7L);
verify(dropshipSkuInventory).deduct("TENANT-A", 1001L, "SKU-2", 1L, 3L);
verify(dropshipSkuInventory).deduct("TENANT-A", 1001L, "SKU-1", 1L, 13L);
verify(dropshipSkuInventory).deduct("TENANT-A", 1001L, "SKU-2", 1L, 2L);
assertEquals("SHIPPED", first.getStatus()); assertEquals("SHIPPED", second.getStatus());
assertEquals(2, item.getPickedPackageCount());
}
@@ -962,4 +974,10 @@ class DeliveryTransferServiceTest {
value.setOverseasWarehouseId(warehouseId); value.setPalletNo(palletNo); value.setSkuCode(sku);
value.setAvailablePackages(packages); return value;
}
private static DropshipCartonSku cartonSku(String sku, long quantity) {
DropshipCartonSku value = new DropshipCartonSku();
value.setSkuCode(sku); value.setProductQuantity(quantity);
return value;
}
}
@@ -0,0 +1,41 @@
package com.baiyue.lastleg.discard;
import com.baiyue.lastleg.discard.mapper.CargoDiscardInventoryMapper;
import org.apache.ibatis.annotations.Select;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
class CargoDiscardPhysicalCartonMapperContractTest {
@Test
void candidatesJoinSkuFactsButCountAvailableOrdersPhysically() throws Exception {
String candidates = select("candidates", String.class, String.class);
assertTrue(candidates.contains("JOIN ll_dropship_carton_sku cs"));
assertTrue(candidates.contains("cs.carton_id=c.id"));
assertTrue(candidates.contains("COUNT(c.id) available_packages"));
String physicalTotal = select("countAvailableOrder", String.class, Long.class);
assertTrue(physicalTotal.contains("COUNT(*) FROM ll_dropship_carton"));
assertTrue(!physicalTotal.contains("ll_dropship_carton_sku"));
}
@Test
void skuSelectionResolvesThroughPhysicalCartonSkuFacts() throws Exception {
String count = select("countAvailable", String.class, Long.class, String.class);
String lock = select("lockAvailable", String.class, Long.class, String.class, int.class);
for (String sql : new String[] {count, lock}) {
assertTrue(sql.contains("EXISTS (SELECT 1 FROM ll_dropship_carton_sku cs"));
assertTrue(sql.contains("cs.carton_id=c.id"));
assertTrue(sql.contains("cs.sku_code=#{skuCode}"));
}
assertTrue(lock.contains("FOR UPDATE"));
}
private String select(String method, Class<?>... parameterTypes) throws Exception {
Select annotation = CargoDiscardInventoryMapper.class.getMethod(method, parameterTypes)
.getAnnotation(Select.class);
assertNotNull(annotation);
return String.join("", annotation.value());
}
}
@@ -6,8 +6,16 @@ import com.baiyue.lastleg.discard.entity.CargoDiscardItem;
import com.baiyue.lastleg.discard.mapper.CargoDiscardInventoryMapper;
import com.baiyue.lastleg.dropship.entity.DropshipCarton;
import com.baiyue.lastleg.dropship.entity.DropshipCartonLedger;
import com.baiyue.lastleg.dropship.entity.DropshipCartonSku;
import com.baiyue.lastleg.dropship.entity.DropshipOrderProfile;
import com.baiyue.lastleg.dropship.entity.DropshipSkuInventory;
import com.baiyue.lastleg.dropship.mapper.DropshipCartonLedgerMapper;
import com.baiyue.lastleg.dropship.mapper.DropshipCartonSkuMapper;
import com.baiyue.lastleg.dropship.mapper.DropshipOrderProfileMapper;
import com.baiyue.lastleg.dropship.mapper.DropshipSkuInventoryMapper;
import com.baiyue.lastleg.inventory.entity.InventoryPosition;
import com.baiyue.lastleg.inventory.mapper.InventoryBalanceMapper;
import com.baiyue.lastleg.inventory.mapper.InventoryPositionMapper;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@@ -37,6 +45,11 @@ import static org.mockito.Mockito.when;
class CargoDiscardInventoryAdjustmentServiceTest {
@Mock CargoDiscardInventoryMapper inventory;
@Mock DropshipCartonLedgerMapper ledgers;
@Mock DropshipCartonSkuMapper cartonSkus;
@Mock DropshipSkuInventoryMapper skuInventories;
@Mock DropshipOrderProfileMapper profiles;
@Mock InventoryPositionMapper positions;
@Mock InventoryBalanceMapper balances;
private CargoDiscardInventoryAdjustmentService service;
@BeforeEach
@@ -44,6 +57,45 @@ class CargoDiscardInventoryAdjustmentServiceTest {
service = new CargoDiscardInventoryAdjustmentService();
ReflectionTestUtils.setField(service, "inventory", inventory);
ReflectionTestUtils.setField(service, "cartonLedgers", ledgers);
ReflectionTestUtils.setField(service, "cartonSkus", cartonSkus);
ReflectionTestUtils.setField(service, "skuInventories", skuInventories);
ReflectionTestUtils.setField(service, "profiles", profiles);
ReflectionTestUtils.setField(service, "positions", positions);
ReflectionTestUtils.setField(service, "balances", balances);
}
@Test
void pdaDiscardDeductsEverySkuInsideOnePhysicalCartonOnce() {
CargoDiscardApplication application = application(1);
CargoDiscardItem item = item(1);
item.setProfileId(201L);
DropshipCarton carton = carton(1L);
carton.setSkuCode("MIXED"); carton.setProductQuantity(5L);
DropshipCartonSku skuA = cartonSku("SKU-A", 2L);
DropshipCartonSku skuB = cartonSku("SKU-B", 3L);
DropshipOrderProfile profile = new DropshipOrderProfile();
profile.setId(201L); profile.setCustomerId("100"); profile.setInventoryPositionId(801L);
InventoryPosition position = new InventoryPosition();
position.setId(801L); position.setBalanceId(901L);
when(inventory.lockSkuInventory("100", 301L)).thenReturn(sku(5));
when(inventory.lockCarton("100", 1L)).thenReturn(carton);
when(cartonSkus.listCarton("100", 1L)).thenReturn(List.of(skuA, skuB));
when(skuInventories.deduct("100", 201L, "SKU-A", 1, 2)).thenReturn(1);
when(skuInventories.deduct("100", 201L, "SKU-B", 1, 3)).thenReturn(1);
when(profiles.selectById(201L)).thenReturn(profile);
when(positions.findTenantPosition("100", 801L)).thenReturn(position);
when(positions.deductAvailable("100", 801L, 1)).thenReturn(1);
when(balances.deductAvailable("100", 901L, 1)).thenReturn(1);
when(inventory.changeCartonStatus("100", 1L, "DISCARD_PENDING", "DISCARDED")).thenReturn(1);
when(inventory.skuPackages("100", 301L)).thenReturn(4L);
var result = service.discardPalletItem("100", application, item, List.of(link(1L)),
1, "operator", LocalDateTime.now());
assertEquals(1, result.discardedPackages());
verify(skuInventories).deduct("100", 201L, "SKU-A", 1, 2);
verify(skuInventories).deduct("100", 201L, "SKU-B", 1, 3);
verify(positions).deductAvailable("100", 801L, 1);
}
@Test
@@ -105,4 +157,8 @@ class CargoDiscardInventoryAdjustmentServiceTest {
private DropshipCarton carton(long id) {
DropshipCarton value = new DropshipCarton(); value.setId(id); value.setCartonMark("C-" + id); return value;
}
private DropshipCartonSku cartonSku(String skuCode, long quantity) {
DropshipCartonSku value = new DropshipCartonSku();
value.setSkuCode(skuCode); value.setProductQuantity(quantity); return value;
}
}
@@ -10,6 +10,7 @@ import com.baiyue.lastleg.discard.mapper.CargoDiscardCartonMapper;
import com.baiyue.lastleg.discard.mapper.CargoDiscardInventoryMapper;
import com.baiyue.lastleg.discard.mapper.CargoDiscardItemMapper;
import com.baiyue.lastleg.discard.mapper.CargoDiscardStatusHistoryMapper;
import com.baiyue.lastleg.discard.model.CargoDiscardCandidateRow;
import com.baiyue.lastleg.dropship.entity.DropshipCarton;
import com.baiyue.lastleg.dropship.entity.DropshipSkuInventory;
import com.baiyue.lastleg.entity.LastlegOrder;
@@ -102,6 +103,20 @@ class CargoDiscardServiceTest {
verify(inventory, never()).deductSkuInventory(any(), anyLong(), anyInt());
}
@Test
void candidateTotalCountsPhysicalCartonsInsteadOfSkuRows() {
CargoDiscardCandidateRow skuA = candidate("SKU-A", 20L);
CargoDiscardCandidateRow skuB = candidate("SKU-B", 20L);
when(inventory.candidates("100", "RK-1")).thenReturn(List.of(skuA, skuB));
when(inventory.countAvailableOrder("100", 101L)).thenReturn(20L);
var result = service.candidates("100", "RK-1");
assertEquals(1, result.size());
assertEquals(20, result.get(0).availablePackages());
assertEquals(2, result.get(0).skus().size());
}
@Test
void approvalPushesToPdaWithoutDeductingInventory() {
CargoDiscardApplication application = pendingApplication();
@@ -179,4 +194,12 @@ class CargoDiscardServiceTest {
value.setCustomerId("100"); value.setCartonId(cartonId); value.setCartonMark("C-" + cartonId);
return value;
}
private CargoDiscardCandidateRow candidate(String skuCode, long packages) {
CargoDiscardCandidateRow value = new CargoDiscardCandidateRow();
value.setOrderId(101L); value.setInboundCode("RK-1"); value.setInboundOrderNo("IN-1");
value.setOverseasWarehouseId("WH-1"); value.setOverseasWarehouseName("LA");
value.setSkuCode(skuCode); value.setProductName(skuCode); value.setAvailablePackages(packages);
return value;
}
}
@@ -5,8 +5,10 @@ import com.baiyue.lastleg.dropship.entity.DropshipOrderProfile;
import com.baiyue.lastleg.dropship.entity.DropshipSkuInventory;
import com.baiyue.lastleg.dropship.entity.DropshipCarton;
import com.baiyue.lastleg.dropship.entity.DropshipCartonLedger;
import com.baiyue.lastleg.dropship.entity.DropshipCartonSku;
import com.baiyue.lastleg.dropship.mapper.DropshipCartonLedgerMapper;
import com.baiyue.lastleg.dropship.mapper.DropshipCartonMapper;
import com.baiyue.lastleg.dropship.mapper.DropshipCartonSkuMapper;
import com.baiyue.lastleg.dropship.mapper.DropshipOrderProfileMapper;
import com.baiyue.lastleg.dropship.mapper.DropshipSkuInventoryMapper;
import com.baiyue.lastleg.entity.LastlegOrder;
@@ -32,6 +34,7 @@ class DropshipOrderServiceTest {
@Mock private DropshipOrderProfileMapper profiles;
@Mock private DropshipSkuInventoryMapper skuInventory;
@Mock private DropshipCartonMapper cartons;
@Mock private DropshipCartonSkuMapper cartonSkus;
@Mock private DropshipCartonLedgerMapper ledger;
@Mock private WarehouseResourceGateway warehouse;
@InjectMocks private DropshipOrderService service;
@@ -62,6 +65,7 @@ class DropshipOrderServiceTest {
verify(profiles, times(2)).insert(any(DropshipOrderProfile.class));
verify(skuInventory, times(2)).insert(any(DropshipSkuInventory.class));
verify(cartons, times(3)).insert(any(DropshipCarton.class));
verify(cartonSkus, times(3)).insert(any(DropshipCartonSku.class));
verify(ledger, times(3)).insert(any(DropshipCartonLedger.class));
ArgumentCaptor<DropshipOrderProfile> profilesCaptor = ArgumentCaptor.forClass(DropshipOrderProfile.class);
@@ -88,6 +92,33 @@ class DropshipOrderServiceTest {
.containsOnly(0L);
}
@Test
void multiSkuCartonsAreStoredOnceWithSeparateSkuFacts() {
LastlegOrder order = new LastlegOrder();
order.setId(10L); order.setCustomerId("C-1"); order.setOrderType("ONE_CLICK_DROPSHIP");
order.setOrderNo("LL-10"); order.setPackageCount(2); order.setProductQuantity(30);
when(orders.selectById(10L)).thenReturn(order);
when(profiles.listOrder("C-1", 10L)).thenReturn(List.of());
DropshipRequests.Receive request = new DropshipRequests.Receive("C-1", 10L, 20L, "EVENT-MIXED",
List.of(new DropshipRequests.Pallet("SOURCE-P-1", List.of(
new DropshipRequests.SkuPackages("SKU-A", "A", 2, 20,
List.of("BOX-1", "BOX-2"), java.util.Map.of("BOX-1", 10L, "BOX-2", 10L)),
new DropshipRequests.SkuPackages("SKU-B", "B", 2, 10,
List.of("BOX-1", "BOX-2"), java.util.Map.of("BOX-1", 5L, "BOX-2", 5L))),
1, 2, 1, "2".repeat(64))));
assertThat(service.receive(request)).isEqualTo(2);
ArgumentCaptor<DropshipCarton> physical = ArgumentCaptor.forClass(DropshipCarton.class);
verify(cartons, times(2)).insert(physical.capture());
assertThat(physical.getAllValues()).extracting(DropshipCarton::getCartonMark)
.containsExactly("BOX-1", "BOX-2");
assertThat(physical.getAllValues()).extracting(DropshipCarton::getSkuCode).containsOnly("MIXED");
assertThat(physical.getAllValues()).extracting(DropshipCarton::getProductQuantity).containsOnly(15L);
verify(cartonSkus, times(4)).insert(any(DropshipCartonSku.class));
verify(ledger, times(2)).insert(any(DropshipCartonLedger.class));
}
@Test
void activatesMultiplePalletsOnlyAfterPdaStockIn() {
DropshipOrderProfile first = profile(1L, "SOURCE-P-1");
@@ -245,6 +245,29 @@ class InboundStockReceiptServiceTest {
assertEquals("WH-PALLET-21", activationReceipts.getValue().get(0).getPalletNo());
}
@Test
void multiSkuDropshipPalletStocksPhysicalCartonsOnce() {
Fixture fixture = fixture(5, 5, 5, 10);
fixture.order.setOrderType("ONE_CLICK_DROPSHIP");
when(dropshipOrders.palletSnapshots("100", 101L)).thenReturn(List.of(
new DropshipPalletSnapshot("PLT-21", 5, List.of(
new DropshipPalletSnapshot.Sku("SKU-A", "A", 5, 50),
new DropshipPalletSnapshot.Sku("SKU-B", "B", 5, 25)))));
when(orders.selectBatchIds(any())).thenReturn(List.of(fixture.order));
when(inventory.stockIn(eq("100"), eq("operator"), any(InventoryCommandRequests.StockIn.class)))
.thenReturn(new InventoryCommandResponse(8L, "STOCK_IN", 9L, 10L, 5, 0, 0, 0, false, false));
service.stockIn("100", fixture.plan, fixture.task, List.of(fixture.line),
List.of(fixture.allocation), List.of(fixture.pallet), LocalDateTime.now());
ArgumentCaptor<InventoryCommandRequests.StockIn> request =
ArgumentCaptor.forClass(InventoryCommandRequests.StockIn.class);
verify(inventory, times(1)).stockIn(eq("100"), eq("operator"), request.capture());
assertEquals("MIXED", request.getValue().skuCode());
assertEquals(5, request.getValue().packages());
verify(receipts, times(1)).insert(any(InboundStockReceipt.class));
}
@Test
void rejectsDomesticSourceWarehouseAsActualOverseasWarehouse() {
Fixture fixture = fixture(5, 5, 5, 10);
@@ -37,8 +37,8 @@ class SchemaChineseCommentContractTest {
String overseasUnified = Files.readString(
Path.of("..", "deploy", "sql", "海外区域统一增量-MySQL8.sql"), StandardCharsets.UTF_8);
List<TableDefinition> baselineTables = assertAllChineseComments(baseline, "基线 SQL", 55);
assertAllChineseComments(incremental, "增量 SQL", 49);
List<TableDefinition> baselineTables = assertAllChineseComments(baseline, "基线 SQL", 56);
assertAllChineseComments(incremental, "增量 SQL", 50);
assertAllChineseComments(overseasUnified, "海外统一增量 SQL", 10);
assertCommentMigrationCovers(incremental, overseasUnified, baselineTables);
}
@@ -35,22 +35,23 @@ class LoadingCompletedConsumerTest {
}
@Test void oneDropshipInboundWithMultipleSkusCreatesOneOrderAndMultipleSkuDetails(){
var itemA=new FirstLegLoadingPlanCompletedV1.Item("d-a","SKU-A","产品A","Product A",2,20,1,BigDecimal.ONE,BigDecimal.ONE,BigDecimal.ONE,BigDecimal.ONE,"wh-1","ONT8","ONT8",null,"SHIP-A","MARK-A",null,List.of("BOX-A1","BOX-A2"));
var itemB=new FirstLegLoadingPlanCompletedV1.Item("d-b","SKU-B","产品B","Product B",3,15,1,BigDecimal.ONE,BigDecimal.ONE,BigDecimal.ONE,BigDecimal.ONE,"wh-1","ONT8","ONT8",null,"SHIP-B","MARK-B",null,List.of("BOX-B1","BOX-B2","BOX-B3"));
var itemB=new FirstLegLoadingPlanCompletedV1.Item("d-b","SKU-B","产品B","Product B",2,10,1,BigDecimal.ZERO,BigDecimal.ZERO,BigDecimal.ZERO,BigDecimal.ZERO,"wh-1","ONT8","ONT8",null,"SHIP-B","MARK-B",null,List.of("BOX-A1","BOX-A2"));
var pallet=new FirstLegLoadingPlanCompletedV1.PalletSnapshot("PLT-1",List.of(
new FirstLegLoadingPlanCompletedV1.SkuPackageSnapshot("SKU-A","产品A",2,20,List.of("BOX-A1","BOX-A2")),
new FirstLegLoadingPlanCompletedV1.SkuPackageSnapshot("SKU-B","产品B",3,15,List.of("BOX-B1","BOX-B2","BOX-B3"))));
var order=new FirstLegLoadingPlanCompletedV1.Order("in-multi","root-multi","ord-multi","FL-MULTI","root-ord-multi","RK-MULTI","wh-1","ONT8","ONT8",5,35,1,BigDecimal.valueOf(2),BigDecimal.valueOf(2),BigDecimal.valueOf(2),List.of(itemA,itemB),"100","Customer 100","ONE_CLICK_DROPSHIP",List.of(pallet),"FBA");
new FirstLegLoadingPlanCompletedV1.SkuPackageSnapshot("SKU-B","产品B",2,10,List.of("BOX-A1","BOX-A2"))));
var order=new FirstLegLoadingPlanCompletedV1.Order("in-multi","root-multi","ord-multi","FL-MULTI","root-ord-multi","RK-MULTI","wh-1","ONT8","ONT8",2,30,1,BigDecimal.ONE,BigDecimal.ONE,BigDecimal.ONE,List.of(itemA,itemB),"100","Customer 100","ONE_CLICK_DROPSHIP",List.of(pallet),"FBA");
EventEnvelope event=eventForPayload(new FirstLegLoadingPlanCompletedV1("plan-multi","LP-MULTI","book-multi","MSCU6639870","CAB-MULTI","92","Warehouse",List.of(order)),2);
assertEquals(1,service.consume(event));
verify(orders,times(1)).insert(any(LastlegOrder.class));
ArgumentCaptor<LastlegOrderDetail> detailCaptor=ArgumentCaptor.forClass(LastlegOrderDetail.class);
verify(details,times(2)).insert(detailCaptor.capture());
assertEquals(Map.of("SKU-A",2,"SKU-B",3),detailCaptor.getAllValues().stream().collect(java.util.stream.Collectors.toMap(LastlegOrderDetail::getSkuCode,LastlegOrderDetail::getPackageCount)));
assertEquals(Map.of("SKU-A",2,"SKU-B",2),detailCaptor.getAllValues().stream().collect(java.util.stream.Collectors.toMap(LastlegOrderDetail::getSkuCode,LastlegOrderDetail::getPackageCount)));
ArgumentCaptor<DropshipRequests.Receive> receiveCaptor=ArgumentCaptor.forClass(DropshipRequests.Receive.class);
verify(dropshipOrders).receive(receiveCaptor.capture());
assertEquals(2,receiveCaptor.getValue().pallets().get(0).skuPackages().size());
assertEquals(Map.of("SKU-A",2L,"SKU-B",3L),receiveCaptor.getValue().pallets().get(0).skuPackages().stream().collect(java.util.stream.Collectors.toMap(DropshipRequests.SkuPackages::skuCode,DropshipRequests.SkuPackages::packageCount)));
assertEquals(2,receiveCaptor.getValue().pallets().get(0).manifestPackageCount());
assertEquals(Map.of("SKU-A",2L,"SKU-B",2L),receiveCaptor.getValue().pallets().get(0).skuPackages().stream().collect(java.util.stream.Collectors.toMap(DropshipRequests.SkuPackages::skuCode,DropshipRequests.SkuPackages::packageCount)));
}
@Test void consumesV2LoadingEventAndKeepsDropshipSnapshot(){
EventEnvelope source=event();