fix(dropship): preserve carton quantities and shared pallet outbound ownership
This commit is contained in:
+1
-1
@@ -27,7 +27,7 @@ class DeliverySchemaContractTest {
|
||||
assertTrue(schema.contains("unique key uk_ll_outbound_idempotency (customer_id,idempotency_key)"));
|
||||
assertTrue(schema.contains("unique key uk_ll_outbound_dispatch (customer_id,dispatch_order_id)"));
|
||||
assertTrue(schema.contains("create table if not exists ll_outbound_pallet_item"));
|
||||
assertTrue(schema.contains("unique key uk_ll_outbound_pallet (customer_id,outbound_order_id,warehouse_pallet_id)"));
|
||||
assertTrue(schema.contains("unique key uk_ll_outbound_pallet (customer_id,outbound_order_id,warehouse_pallet_id,inbound_code,sku_code)"));
|
||||
assertTrue(schema.contains("destination_warehouse_code varchar(128) null"));
|
||||
assertTrue(schema.contains("order_refs varchar(2000) null"));
|
||||
assertTrue(schema.contains("inbound_code varchar(128) null"));
|
||||
|
||||
+97
-1
@@ -279,6 +279,31 @@ class DeliveryTransferServiceTest {
|
||||
verify(outboundOrders, never()).insert((OutboundOrder) any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void dedicatedOutboundDetailDoesNotRequireADeliveryDocument() {
|
||||
OutboundOrder outbound = outbound(301L, null, null, "OUT-301", "dropship-301");
|
||||
outbound.setOutboundType("ONE_CLICK_DROPSHIP");
|
||||
outbound.setStatus("OUTBOUNDED");
|
||||
when(outboundOrders.findGlobalByNo("OUT-301")).thenReturn(outbound);
|
||||
when(outboundPallets.findByOutbound("TENANT-A", 301L)).thenReturn(List.of(
|
||||
pallet(501L, "DP-260914-0001", "ONT8", "Ontario", "IN-A", 4)));
|
||||
|
||||
DeliveryResponses.OutboundDetail detail = service.outboundDetail("OUT-301");
|
||||
|
||||
assertEquals("OUTBOUNDED", detail.outbound().status());
|
||||
org.junit.jupiter.api.Assertions.assertNull(detail.delivery());
|
||||
assertEquals("IN-A", detail.pallets().get(0).inboundCode());
|
||||
assertEquals(4, detail.pallets().get(0).packageCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
void dispatchOutboundStillRequiresItsDeliveryDocument() {
|
||||
OutboundOrder outbound = outbound(301L, 101L, "DSP-101", "OUT-301", "dispatch-301");
|
||||
outbound.setOutboundType("DISPATCH_DELIVERY");
|
||||
when(outboundOrders.findGlobalByNo("OUT-301")).thenReturn(outbound);
|
||||
assertThrows(IllegalStateException.class, () -> service.outboundDetail("OUT-301"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void outboundDetailIncludesPalletCodesForDisplay() {
|
||||
OutboundOrder outbound = outbound(301L, 101L, "DSP-101", "OUT-301", "transfer-101");
|
||||
@@ -406,6 +431,33 @@ class DeliveryTransferServiceTest {
|
||||
verify(outboundPallets, never()).findByOutbound(any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void sharedPalletOutboundFreezesEachInboundAndCountsOnePhysicalPallet() {
|
||||
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))
|
||||
.thenReturn(List.of(new DropshipCarton()));
|
||||
InventoryPosition position = new InventoryPosition();
|
||||
position.setId(900L + i); position.setCustomerId("TENANT-A"); position.setBalanceId(800L + i);
|
||||
position.setPalletId(501L); position.setPalletNo("DP-SHARED");
|
||||
InventoryBalance balance = new InventoryBalance();
|
||||
balance.setCustomerId("TENANT-A"); balance.setInboundCode("IN-" + i);
|
||||
when(inventoryPositions.selectById(900L + i)).thenReturn(position);
|
||||
when(inventoryBalances.selectById(800L + i)).thenReturn(balance);
|
||||
}
|
||||
service.createOneClick("TENANT-A", "U-1", new DeliveryRequests.OneClickOutbound("recipient", List.of(
|
||||
new DeliveryRequests.OneClickItem(901L, 1L, "SKU-1"),
|
||||
new DeliveryRequests.OneClickItem(902L, 1L, "SKU-1")), null, "shared-pallet"));
|
||||
ArgumentCaptor<OutboundPalletItem> items = ArgumentCaptor.forClass(OutboundPalletItem.class);
|
||||
verify(outboundPallets, org.mockito.Mockito.times(2)).insert(items.capture());
|
||||
assertEquals(List.of("IN-1", "IN-2"), items.getAllValues().stream().map(OutboundPalletItem::getInboundCode).toList());
|
||||
ArgumentCaptor<OutboundOrder> order = ArgumentCaptor.forClass(OutboundOrder.class);
|
||||
verify(outboundOrders).updateById(order.capture());
|
||||
assertEquals(1, order.getValue().getPalletCount());
|
||||
assertEquals(2, order.getValue().getPackageCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
void oneClickOutboundRejectsSummarySkuWithoutAvailableDropshipCartons() {
|
||||
DropshipOutboundCandidate candidate = dropshipCandidate(901L, 1001L, "WH-1", "PLT-501", "MIXED", 1L);
|
||||
@@ -539,17 +591,26 @@ class DeliveryTransferServiceTest {
|
||||
DropshipOrderProfile profile = new DropshipOrderProfile(); profile.setId(1001L);
|
||||
DropshipCarton first = new DropshipCarton(); first.setStatus("AVAILABLE"); first.setSkuCode("SKU-1"); first.setCartonMark("CTN-A");
|
||||
DropshipCarton second = new DropshipCarton(); second.setStatus("AVAILABLE"); second.setSkuCode("SKU-1"); second.setCartonMark("CTN-B");
|
||||
InventoryPosition otherInbound = new InventoryPosition();
|
||||
otherInbound.setId(902L); otherInbound.setCustomerId("TENANT-A"); otherInbound.setBalanceId(802L);
|
||||
otherInbound.setOverseasWarehouseId("WH-1"); otherInbound.setPalletId(501L);
|
||||
otherInbound.setAvailablePackages(2L);
|
||||
InventoryBalance otherBalance = new InventoryBalance();
|
||||
otherBalance.setId(802L); otherBalance.setCustomerId("TENANT-A"); otherBalance.setInboundCode("IN-B");
|
||||
otherBalance.setSkuCode("SKU-1");
|
||||
when(pdaTasks.findByNoAndWarehouse("PDA-701", "WH-1")).thenReturn(task);
|
||||
when(outboundOrders.selectById(301L)).thenReturn(outbound);
|
||||
when(outboundPallets.findByOutbound("TENANT-A", 301L)).thenReturn(List.of(item));
|
||||
when(inventoryPositions.listPallet("TENANT-A", 501L)).thenReturn(List.of(position));
|
||||
when(inventoryPositions.listPallet("TENANT-A", 501L)).thenReturn(List.of(position, otherInbound));
|
||||
when(inventoryBalances.selectById(801L)).thenReturn(balance);
|
||||
when(inventoryBalances.selectById(802L)).thenReturn(otherBalance);
|
||||
when(dropshipProfiles.findPosition("TENANT-A", 901L)).thenReturn(profile);
|
||||
when(dropshipCartons.listProfile("TENANT-A", 1001L)).thenReturn(List.of(first, second));
|
||||
|
||||
DeliveryResponses.PdaTaskDetail detail = service.pdaTaskDetail("WH-1", "PDA-701");
|
||||
|
||||
assertEquals(2, detail.items().get(0).positions().size());
|
||||
verify(dropshipProfiles, never()).findPosition("TENANT-A", 902L);
|
||||
assertEquals(List.of("CTN-A", "CTN-B"), detail.items().get(0).positions().stream()
|
||||
.map(DeliveryResponses.PdaPickPosition::cartonMark).toList());
|
||||
DeliveryResponses.PdaPickPosition projected = detail.items().get(0).positions().get(0);
|
||||
@@ -588,6 +649,41 @@ class DeliveryTransferServiceTest {
|
||||
assertEquals("CTN-A", detail.items().get(0).positions().get(0).cartonMark());
|
||||
}
|
||||
|
||||
@Test
|
||||
void dropshipPartialPicksDeductExactCartonProductQuantities() {
|
||||
var commands = org.mockito.Mockito.mock(com.baiyue.lastleg.inventory.command.service.InventoryCommandService.class);
|
||||
ReflectionTestUtils.setField(service, "inventoryCommands", commands);
|
||||
OutboundOrder outbound = outbound(301L, null, null, "OUT-301", "quantity-test");
|
||||
outbound.setOverseasWarehouseId("WH-1"); outbound.setOutboundType("ONE_CLICK_DROPSHIP");
|
||||
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");
|
||||
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);
|
||||
when(pdaTasks.lockByNoAndWarehouse("PDA-701", "WH-1")).thenReturn(task);
|
||||
when(outboundOrders.selectById(301L)).thenReturn(outbound);
|
||||
when(outboundOrders.lockByNo("TENANT-A", "OUT-301")).thenReturn(outbound);
|
||||
when(outboundPallets.selectById(item.getId())).thenReturn(item);
|
||||
when(inventoryPositions.selectById(901L)).thenReturn(position);
|
||||
when(inventoryBalances.selectById(801L)).thenReturn(balance);
|
||||
when(dropshipProfiles.findPosition("TENANT-A", 901L)).thenReturn(profile);
|
||||
when(dropshipCartons.lockAvailable("TENANT-A", 1001L, "SKU-1", 1L)).thenReturn(List.of(first), List.of(second));
|
||||
when(dropshipSkuInventory.deduct("TENANT-A", 1001L, "SKU-1", 1L, 7L)).thenReturn(1);
|
||||
when(dropshipSkuInventory.deduct("TENANT-A", 1001L, "SKU-1", 1L, 13L)).thenReturn(1);
|
||||
when(outboundPallets.findByOutbound("TENANT-A", 301L)).thenReturn(List.of(item));
|
||||
service.pdaPick("WH-1", "U-1", "PDA-701", new DeliveryRequests.PdaPick(item.getId(), 901L, 1L, "pick-7"));
|
||||
service.pdaPick("WH-1", "U-1", "PDA-701", new DeliveryRequests.PdaPick(item.getId(), 901L, 1L, "pick-13"));
|
||||
verify(dropshipSkuInventory).deduct("TENANT-A", 1001L, "SKU-1", 1L, 7L);
|
||||
verify(dropshipSkuInventory).deduct("TENANT-A", 1001L, "SKU-1", 1L, 13L);
|
||||
assertEquals("SHIPPED", first.getStatus()); assertEquals("SHIPPED", second.getStatus());
|
||||
assertEquals(2, item.getPickedPackageCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
void outboundPdaPickReplayDoesNotDeductInventoryAgainAndRejectsChangedRequest() {
|
||||
OutboundOrder outbound = outbound(301L, null, null, "OUT-301", "one-click-key");
|
||||
|
||||
Reference in New Issue
Block a user