This commit is contained in:
2026-09-18 09:59:07 +08:00
parent 13a8dde0a5
commit 4f59f7bd28
45 changed files with 358 additions and 157 deletions
@@ -153,6 +153,8 @@ class ReceivingContainerContractTest {
assertTrue(yaml.contains("enum: [LICENSE_PLATE, CONTAINER_SEAL, DOOR_OPEN]"));
assertTrue(yaml.contains("photos: { type: array, minItems: 3, maxItems: 3"));
assertTrue(yaml.contains("required: [role, fileId, downloadPath]"));
assertTrue(yaml.contains("packageCount: { type: integer, minimum: 1, maximum: 66 }"));
assertTrue(receivingPaths.contains("单托最多 66 箱"));
assertTrue(yaml.contains("overseasWarehouseId: { type: string"));
assertTrue(yaml.contains("overseasWarehouseName: { type: string"));
assertFalse(receivingPaths.contains("photoFileIds"));
@@ -49,6 +49,7 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
@@ -422,6 +423,55 @@ class ReceivingContainerServiceBehaviorTest {
verifyNoInteractions(workflow);
}
@Test
void fbaPalletAccepts66PackagesAndRejects67BeforeReceivingStarts() {
stubCurrent("FBA", "CONT-A");
InboundPlanLine fbaLine = line("FBA");
fbaLine.setRequiredPackageCount(66);
when(lines.selectList(any())).thenReturn(List.of(fbaLine));
when(orderDetails.selectList(any())).thenReturn(List.of(detail("SKU-1", 66)));
when(skuReceipts.listLineSku("100", 10L, "SKU-1")).thenReturn(List.of());
stubReservedFbaPallet();
when(workflow.receiveBulkResolved(any(), any(), any(), any(), anyInt(), any(), any()))
.thenAnswer(invocation -> invocation.getArgument(4));
when(fbaPalletCounts.insert(any(ReceivingFbaPalletCount.class))).thenReturn(1);
when(sessions.updateById(any(ReceivingContainerSession.class))).thenReturn(1);
when(operations.insert(any(ReceivingContainerOperation.class))).thenReturn(1);
var accepted = service.receiveBulk("7", "receiver",
new ReceivingContainerRequests.BulkReceive("IN-1", "PAL-1", 66, "capacity-66"));
assertThat(accepted.acceptedPackages()).isEqualTo(66);
org.mockito.Mockito.reset(sessions, workflow, warehouse);
assertThatThrownBy(() -> service.receiveBulk("7", "receiver",
new ReceivingContainerRequests.BulkReceive("IN-1", "PAL-1", 67, "capacity-67")))
.isInstanceOf(ResponseStatusException.class)
.hasMessageContaining("66箱");
verifyNoInteractions(sessions, workflow, warehouse);
}
@Test
void overseasCartonIsRejectedWhenPhysicalPalletAlreadyContains66Packages() {
stubCurrent("OVERSEAS", "CONT-A");
when(cartons.selectOne(any())).thenReturn(carton(1L, "IN-1#67"));
InboundPlanPallet reserved = planPallet();
reserved.setPalletNo("PAL-1"); reserved.setWarehousePalletId(30L); reserved.setWarehouseLocationId(40L);
reserved.setWarehouseResourceLockId(50L); reserved.setAssignmentStatus("RESERVED");
when(pallets.selectList(any())).thenReturn(List.of(reserved));
when(warehouse.resolveReservedPallet("PAL-1", "100", 100L, 30L, 40L, 50L))
.thenReturn(resource(100L, null));
when(cartons.selectCount(any())).thenReturn(66L);
assertThatThrownBy(() -> service.receiveCarton("7", "receiver",
new ReceivingContainerRequests.CartonReceive("IN-1#67", "PAL-1", "carton-capacity")))
.isInstanceOf(ResponseStatusException.class)
.hasMessageContaining("66箱");
verify(workflow, never()).validateCartonResolved(any(), any(), any(), any(), any());
verify(workflow, never()).assignResolvedPallet(any(), any(), any(), any());
}
@Test
void crossContainerCartonAndMismatchedPalletAreRejectedBeforeReservation() {
stubCurrent("OVERSEAS", "CONT-A");
@@ -950,6 +1000,29 @@ class ReceivingContainerServiceBehaviorTest {
verifyNoInteractions(warehouse);
}
@Test
void dropshipWholePalletOver66PackagesIsRejectedBeforePicking() {
stubCurrent("OVERSEAS", "CONT-A");
LastlegOrder dropship = order("OVERSEAS"); dropship.setOrderType("ONE_CLICK_DROPSHIP");
when(orders.selectById(1L)).thenReturn(dropship);
InboundPlanPallet pallet = planPallet();
pallet.setPalletNo("DP-260909-0067"); pallet.setSourcePalletNo("DP-260909-0067");
pallet.setWarehousePalletId(30L);
InboundPlanPalletAllocation pending = new InboundPlanPalletAllocation();
pending.setId(67L); pending.setCustomerId("100"); pending.setPlanId(2L);
pending.setLineId(10L); pending.setPalletId(20L); pending.setPlannedPackageCount(67);
when(pallets.selectList(any())).thenReturn(List.of(pallet));
when(allocations.selectList(any())).thenReturn(List.of(pending));
stubCompleteManifest("DP-260909-0067", 67);
assertThatThrownBy(() -> service.receiveWholePallet("7", "receiver",
new ReceivingContainerRequests.WholePalletReceive("DP-260909-0067", "whole-capacity")))
.isInstanceOf(ResponseStatusException.class)
.hasMessageContaining("66箱");
verify(workflow, never()).pickWholePallet(any(), any(), anyLong(), any());
}
@Test
void domesticLoadingPhotoDownloadIsScopedToTheCurrentPhysicalContainer() {
ReceivingContainerSession current = session("CONT-A", "SIGNED");