修改提交

This commit is contained in:
2026-08-22 10:16:45 +08:00
parent af8724dd70
commit 93feb7da67
16 changed files with 150 additions and 44 deletions
@@ -28,7 +28,7 @@ class InventorySchemaContractTest {
}
assertTrue(schema.contains("create table if not exists ll_inventory_balance"));
assertTrue(schema.contains("unique key uk_ll_inventory_dimension (customer_id,overseas_warehouse_id,destination_warehouse_code,inbound_order_id,inbound_code,sku_code)"));
assertTrue(schema.contains("unique key uk_ll_inventory_dimension (customer_id,overseas_warehouse_id,destination_warehouse_code,inbound_order_id,inbound_code)"));
assertTrue(schema.contains("inbound_order_id bigint not null"));
assertTrue(schema.contains("inbound_code varchar(128) not null"));
assertTrue(schema.contains("available_packages bigint not null default 0"));
@@ -89,6 +89,9 @@ class InventorySchemaContractTest {
assertTrue(migration.contains("alter table ll_inventory_ledger add column inbound_order_id"));
assertTrue(migration.contains("update ll_inventory_ledger"));
assertTrue(migration.contains("alter table ll_dispatch_order add column partial_completion_reason"));
assertTrue(migration.contains("2026-08-22 / 030"));
assertTrue(migration.contains("drop index uk_ll_inventory_dimension"));
assertTrue(migration.contains("customer_id,overseas_warehouse_id,destination_warehouse_code,inbound_order_id,inbound_code)"));
assertFalse(migration.contains("drop column sku_code"));
assertFalse(migration.contains("drop column product_quantity"));
}
@@ -63,7 +63,7 @@ class InventoryCommandServiceTest {
void stockInUpdatesBalancePositionAndLedgerInOneTransaction() throws Exception {
assertTrue(InventoryCommandService.class.getMethod("stockIn", String.class, String.class,
InventoryCommandRequests.StockIn.class).getAnnotation(Transactional.class) != null);
when(balances.lockDimension("100", "WH-1", "ONT8", 101L, "IN-CODE-1", "SKU-1")).thenReturn(null);
when(balances.lockDimension("100", "WH-1", "ONT8", 101L, "IN-CODE-1")).thenReturn(null);
var result = service.stockIn("100", "7", stockIn("IN-1", "stock-in-1", 12, 24));
assertEquals(12, result.availablePackages());
@@ -79,7 +79,7 @@ class InventoryCommandServiceTest {
@Test
void registersInTransitWithoutCreatingAPalletPosition() {
when(balances.lockDimension("100", "WH-1", "ONT8", 101L, "IN-CODE-1", "SKU-1")).thenReturn(null);
when(balances.lockDimension("100", "WH-1", "ONT8", 101L, "IN-CODE-1")).thenReturn(null);
InventoryCommandResponse result = service.registerInTransit("100", "SYSTEM", inTransit("transit-1", 12));
@@ -95,8 +95,9 @@ class InventoryCommandServiceTest {
@Test
void stockInTransfersPackagesFromInTransitToAvailable() {
InventoryBalance balance = balance(200L, 0);
balance.setSkuCode("SKU-1");
balance.setInTransitPackages(12L);
when(balances.lockDimension("100", "WH-1", "ONT8", 101L, "IN-CODE-1", "SKU-1")).thenReturn(balance);
when(balances.lockDimension("100", "WH-1", "ONT8", 101L, "IN-CODE-1")).thenReturn(balance);
InventoryCommandResponse result = service.stockIn("100", "7", stockIn("IN-1", "stock-in-transit", 12, 24));
@@ -105,9 +106,28 @@ class InventoryCommandServiceTest {
assertEquals(12L, balance.getAvailablePackages());
}
@Test
void differentSkusShareInboundBalanceAndKeepSkuInLedger() {
InventoryBalance balance = balance(200L, 4);
balance.setSkuCode("SKU-1");
when(balances.lockDimension("100", "WH-1", "ONT8", 101L, "IN-CODE-1")).thenReturn(balance);
InventoryCommandRequests.StockIn request = new InventoryCommandRequests.StockIn(
"100", "Customer 100", "WH-1", "WH1", "Warehouse 1", "DEST-1", "ONT8", "ONT8",
101L, "IN-1", "IN-CODE-1", "SKU-2", "Product 2", null, 300L, "PLT-1", null,
null, null, 3, "ORD-1", "EVT-1", "stock-in-sku-2", null);
service.stockIn("100", "7", request);
assertEquals("MIXED", balance.getSkuCode());
assertEquals(7L, balance.getAvailablePackages());
ArgumentCaptor<InventoryLedger> ledger = ArgumentCaptor.forClass(InventoryLedger.class);
verify(ledgers).insert(ledger.capture());
assertEquals("SKU-2", ledger.getValue().getSkuCode());
}
@Test
void stockInAllowsPalletPositionWithZeroOptionalPackages() {
when(balances.lockDimension("100", "WH-1", "ONT8", 101L, "IN-CODE-1", "SKU-1")).thenReturn(null);
when(balances.lockDimension("100", "WH-1", "ONT8", 101L, "IN-CODE-1")).thenReturn(null);
var result = service.stockIn("100", "7", stockIn("IN-1", "stock-in-zero", 0, 0));
@@ -133,7 +153,7 @@ class InventoryCommandServiceTest {
@Test
void replayUsesPersistedResultWithoutMutatingInventoryAgain() throws Exception {
var request = stockIn("IN-1", "stock-in-1", 12, 24);
when(balances.lockDimension("100", "WH-1", "ONT8", 101L, "IN-CODE-1", "SKU-1")).thenReturn(null);
when(balances.lockDimension("100", "WH-1", "ONT8", 101L, "IN-CODE-1")).thenReturn(null);
service.stockIn("100", "7", request);
ArgumentCaptor<InventoryOperation> operation = ArgumentCaptor.forClass(InventoryOperation.class);
verify(operations).insert(operation.capture());
@@ -47,6 +47,7 @@ class LoadingCompletedConsumerTest {
verify(cartons,times(8)).insert(cartonCaptor.capture());
assertEquals("SKU-1",cartonCaptor.getAllValues().get(0).getSkuCode());
assertEquals("BOX-1",cartonCaptor.getAllValues().get(0).getCartonMark());
assertNotNull(cartonCaptor.getAllValues().get(0).getOrderId());
}
@Test void rejectsV2EventWithoutSkuCartonMapping(){
EventEnvelope source=event(); JsonNode node=source.payload().deepCopy();
@@ -55,7 +56,7 @@ class LoadingCompletedConsumerTest {
source.targetRegion(),source.tenantId(),source.aggregateType(),source.aggregateId(),source.aggregateVersion(),
2L,source.traceId(),source.correlationId(),source.causationId(),node,hash);
IllegalArgumentException error=assertThrows(IllegalArgumentException.class,()->service.consume(v2));
assertTrue(error.getMessage().contains("箱唛数量少于实装箱数"));
assertTrue(error.getMessage().contains("箱唛数量必须等于实装箱数"));
verify(orders,never()).insert(any(LastlegOrder.class));
}
private EventEnvelope event(){ var item1=new FirstLegLoadingPlanCompletedV1.Item("d-1","SKU-1","产品","Product",5,50,1,new BigDecimal("10"),new BigDecimal("9"),new BigDecimal("12"),new BigDecimal("1.2"),"wh-1","ONT8","FBA ONT8","FBA-1","SHIP-1","MARK-1"); var item2=new FirstLegLoadingPlanCompletedV1.Item("d-2","SKU-2","产品2","Product2",3,30,2,new BigDecimal("8"),new BigDecimal("7"),new BigDecimal("9"),new BigDecimal("0.8"),"wh-2","LAX9","FBA LAX9","FBA-2","SHIP-2","MARK-2"); var p=new FirstLegLoadingPlanCompletedV1("plan-1","LP1","book-1","MSCU1","CAB1","ow-1","洛杉矶海外仓",List.of(new FirstLegLoadingPlanCompletedV1.Order("in-1","root-1","ord-1","FL1","root-o1","RK1","wh-1","ONT8","FBA ONT8",5,50,1,new BigDecimal("10"),new BigDecimal("9"),new BigDecimal("1.2"),List.of(item1)),new FirstLegLoadingPlanCompletedV1.Order("in-2","root-2","ord-2","FL2","root-o2","RK2","wh-2","LAX9","FBA LAX9",3,30,2,new BigDecimal("8"),new BigDecimal("7"),new BigDecimal("0.8"),List.of(item2)))); JsonNode node=json.valueToTree(p); String hash=EventPayloadHash.sha256(EventPayloadCanonicalizer.canonicalize(node)); return new EventEnvelope("123e4567-e89b-12d3-a456-426614174000",EventTypes.FIRST_LEG_LOADING_PLAN_COMPLETED,1,Instant.now(),RegionCode.CN,RegionCode.OVERSEAS,"100","LOADING_PLAN","plan-1",2,1L,"123e4567-e89b-12d3-a456-426614174001","FIRSTLEG_LOADING_COMPLETED:plan-1:100:1",null,node,hash); }