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,6 +13,10 @@ class LoadingModelBridgeMapperContractTest {
.getMethod("allocatedPackages", long.class, long.class, long.class)
.getAnnotation(Select.class).value()).replaceAll("\\s+", " ");
assertThat(sql).contains("inbound_id=#{inboundId} AND deleted=0");
assertThat(sql)
.contains("item.inbound_id=#{inboundId} AND item.deleted=0")
.contains("COUNT(DISTINCT carton.id)")
.contains("pallet_item.pallet_id=carton.pallet_id")
.contains("pallet_item.deleted=0");
}
}
@@ -300,6 +300,8 @@ class LoadingCompletedOutboxServiceTest {
assertThat(actualPalletsSql)
.contains("pallet.pallet_no AS pallet_code")
.contains("item.pallet_id=pallet.id")
.contains("LEFT JOIN fl_dropship_pallet_carton_sku carton_sku")
.contains("COALESCE(carton_sku.inbound_detail_id,carton.inbound_detail_id)")
.doesNotContain("pallet.pallet_code");
assertThat(workPalletCountSql).contains("i.pallet_id=p.id");
assertThat(openWorkPalletCountSql).contains("i.pallet_id=p.id");
@@ -374,6 +376,33 @@ class LoadingCompletedOutboxServiceTest {
assertThat(event.payload().at("/orders/0/palletCount").asInt()).isEqualTo(2);
}
@Test
void multiSkuCartonsKeepSkuSnapshotsButCountPhysicalPackagesOnce() throws Exception {
Row first = row(11, 100, 101); first.setFulfillmentType("ONE_CLICK_DROPSHIP");
first.setPackageCount(2); first.setProductQuantity(20); first.setSkuCode("SKU-A");
Row second = row(11, 100, 102); second.setFulfillmentType("ONE_CLICK_DROPSHIP");
second.setPackageCount(2); second.setProductQuantity(10); second.setSkuCode("SKU-B");
when(snapshots.rows(90L)).thenReturn(List.of(first, second));
when(snapshots.workPalletCount(90L)).thenReturn(1);
when(snapshots.actualPalletRows(90L)).thenReturn(List.of(
actualPallet(11,100,"DP-260916-0009",101,"SKU-A","BOX-001",10),
actualPallet(11,100,"DP-260916-0009",102,"SKU-B","BOX-001",5),
actualPallet(11,100,"DP-260916-0009",101,"SKU-A","BOX-002",10),
actualPallet(11,100,"DP-260916-0009",102,"SKU-B","BOX-002",5)));
service.publish(90L);
ArgumentCaptor<InboundOutboxEntity> captor = ArgumentCaptor.forClass(InboundOutboxEntity.class);
verify(outbox).insert(captor.capture());
EventEnvelope event = mapper.readValue(captor.getValue().getPayloadJson(), EventEnvelope.class);
assertThat(event.payload().at("/orders/0/packageCount").asInt()).isEqualTo(2);
assertThat(event.payload().at("/orders/0/items")).hasSize(2);
assertThat(event.payload().at("/orders/0/pallets/0/manifestPackageCount").asInt()).isEqualTo(2);
assertThat(event.payload().at("/orders/0/pallets/0/skuPackages")).hasSize(2);
assertThat(event.payload().at("/orders/0/pallets/0/skuPackages/0/cartonMarks")).hasSize(2);
assertThat(event.payload().at("/orders/0/pallets/0/skuPackages/1/cartonMarks")).hasSize(2);
}
@Test void publishesExactPdaCartonToPalletBindingsWithoutSequentialRedistribution() throws Exception {
Row first = row(11, 100, 101); first.setFulfillmentType("ONE_CLICK_DROPSHIP");
first.setPackageCount(2); first.setProductQuantity(20); first.setSkuCode("SKU-A");
@@ -155,6 +155,9 @@ class LoadingPickServiceTest {
new LoadingPickItemMapper.InboundOwner(101L, 50L)));
when(repository.inbound(anyLong(), anyLong())).thenAnswer(invocation ->
new LoadingInboundMapper.Snapshot(invocation.getArgument(1), invocation.getArgument(0), "LOADING", 1));
when(dropshipPallets.physicalMetricsByPalletIds(any())).thenReturn(List.of(
new DropshipPalletizationMapper.PalletMetrics(801L, 2,
java.math.BigDecimal.ZERO, java.math.BigDecimal.ZERO, java.math.BigDecimal.ZERO)));
}
@Test
@@ -482,6 +485,39 @@ class LoadingPickServiceTest {
verify(completionCommands, never()).latest(20L);
}
@Test
void taskDetailCollapsesMultiSkuRowsIntoOnePhysicalPalletCard() {
LoadingPickItemEntity first = item();
LoadingPickItemEntity second = item();
second.setId(31L);
second.setLoadingPlanItemId(41L);
second.setInboundDetailId(61L);
for (LoadingPickItemEntity item : List.of(first, second)) {
item.setPlannedPackageCount(11);
item.setUnitType("SEALED_PALLET");
item.setPalletId(801L);
item.setPalletNo("DP-260916-0009");
item.setIndivisible(true);
}
when(dropshipPallets.physicalMetricsByPalletIds(any())).thenReturn(List.of(
new DropshipPalletizationMapper.PalletMetrics(801L, 11,
java.math.BigDecimal.ZERO, java.math.BigDecimal.ZERO, java.math.BigDecimal.ZERO)));
when(repository.task(20L)).thenReturn(task());
when(repository.items(20L, 101L)).thenReturn(List.of(first, second));
when(repository.cartonLabels(20L)).thenReturn(List.of());
when(repository.inboundOwners(20L)).thenReturn(List.of(
new LoadingPickItemMapper.InboundOwner(101L, 50L)));
when(palletAllocations.list(101L, 50L)).thenReturn(List.of(allocation(50L, 20)));
Task result = service.task(20L, 101L);
assertThat(result.items()).singleElement().satisfies(item -> {
assertThat(item.palletNo()).isEqualTo("DP-260916-0009");
assertThat(item.plannedPackageCount()).isEqualTo(11);
assertThat(item.pickedPackageCount()).isZero();
});
}
@Test
void loadingTaskDetailDoesNotDependOnCompletionCommandStorage() {
LoadingPickTaskEntity task = task();
@@ -9,6 +9,7 @@ import com.baiyue.firstleg.entity.LoadingPickTaskEntity;
import com.baiyue.firstleg.mapper.InboundSplitSettlementMapper;
import com.baiyue.firstleg.mapper.InboundSplitSettlementMapper.MeasurementItemRow;
import com.baiyue.firstleg.mapper.InboundSplitSettlementMapper.ParentRow;
import com.baiyue.firstleg.mapper.DropshipPalletizationMapper;
import com.baiyue.firstleg.repository.InboundPalletAllocationRepository;
import com.baiyue.firstleg.repository.LoadingPickRepository;
import jakarta.annotation.Resource;
@@ -44,6 +45,7 @@ class PdaPickSettlementServiceTest {
@MockBean private WarehousePalletLocationClient warehouse;
@MockBean private OrderFulfillmentSplitClient orderSplits;
@MockBean private InboundHoldGuard inboundHoldGuard;
@MockBean private DropshipPalletizationMapper dropshipPallets;
@Resource private PdaPickSettlementService service;
@Test
@@ -205,7 +207,7 @@ class PdaPickSettlementServiceTest {
}
@Test
void settlesWholeDropshipPalletWithSkuFactsInsteadOfAggregateMeasurementDetail() {
void settlesWholeDropshipUsingUnifiedPhysicalCartonMeasurement() {
List<LoadingPickItemEntity> items = List.of(
item(11L, 21L, 31L, 41L, 1, 1, "PICKED"),
item(12L, 22L, 31L, 42L, 1, 1, "PICKED"),
@@ -223,14 +225,47 @@ class PdaPickSettlementServiceTest {
service.settle(1L, 9L, "operator");
verify(orderSplits).split(eq("internal-token"), eq(9L),
org.mockito.ArgumentMatchers.argThat(request -> request.lines().size() == 4
&& request.lines().stream().allMatch(line -> line.packageCount() == 1)
&& request.lines().stream().map(OrderFulfillmentSplitClient.QuantityLine::sourceDetailId)
.collect(java.util.stream.Collectors.toSet()).equals(java.util.Set.of(41L, 42L, 43L, 44L))));
org.mockito.ArgumentMatchers.argThat(request -> request.lines().size() == 1
&& request.lines().get(0).sourceDetailId() == 41L
&& request.lines().get(0).packageCount() == 4));
verify(splits).updateMeasurementItemForLoaded(10001L, 51L, 4);
verify(splits).markSourceLoaded(10001L, 31L, 4, 9L, 3);
}
@Test
void multiSkuCartonsCountOnceWhenSettlingWholeDropshipPallet() {
LoadingPickItemEntity first = item(11L, 21L, 31L, 41L, 20, 20, "PICKED");
LoadingPickItemEntity second = item(12L, 22L, 31L, 42L, 20, 20, "PICKED");
for (LoadingPickItemEntity item : List.of(first, second)) {
item.setUnitType("SEALED_PALLET");
item.setPalletId(801L);
item.setPalletNo("DP-260916-0009");
item.setIndivisible(true);
}
resolved(List.of(first, second));
DropshipPalletizationMapper.PlanPalletMemberRow physical =
new DropshipPalletizationMapper.PlanPalletMemberRow();
physical.setPalletId(801L);
physical.setAccountId(10001L);
physical.setInboundId(31L);
physical.setAvailablePackageCount(20);
when(dropshipPallets.palletInboundMembersByPalletIds(any())).thenReturn(List.of(physical));
when(splits.lockParent(31L)).thenReturn(parent(20));
when(splits.fulfillmentType(10001L, 31L)).thenReturn("ONE_CLICK_DROPSHIP");
when(splits.lockLatestApprovedItemsForInbound(10001L, 31L))
.thenReturn(List.of(new MeasurementItemRow(51L, 61L, 41L, 20)));
when(allocations.lockConfirmed(10001L, 31L))
.thenReturn(List.of(allocation(1L, 1, 501L, 20)));
writes(0, 20);
service.settle(1L, 9L, "operator");
verify(splits).markSourceLoaded(10001L, 31L, 20, 9L, 3);
verify(orderSplits).split(eq("internal-token"), eq(9L),
org.mockito.ArgumentMatchers.argThat(request -> request.loadedPackageCount() == 20
&& request.lines().size() == 1 && request.lines().get(0).packageCount() == 20));
}
@Test
void onlyPureSkippedInboundRestoresCandidate() {
LoadingPickItemEntity item = item(12L, 22L, 32L, 42L, 8, 0, "SKIPPED");