This commit is contained in:
2026-09-01 11:09:14 +08:00
191 changed files with 6486 additions and 403 deletions
@@ -0,0 +1,21 @@
package com.baiyue.lastleg.inboundplan.mapper;
import org.apache.ibatis.annotations.Select;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertTrue;
class InboundPlanOrderMapperContractTest {
@Test
void transferWarehouseIsTheEffectivePalletDestination() throws Exception {
Select select = InboundPlanOrderMapper.class
.getMethod("lockOrder", String.class, Long.class)
.getAnnotation(Select.class);
String sql = String.join(" ", select.value());
assertTrue(sql.contains("COALESCE(NULLIF(o.transfer_warehouse_id,''),MIN(NULLIF(d.destination_warehouse_id,'')))"));
assertTrue(sql.contains("COALESCE(NULLIF(o.transfer_warehouse_code,''),MIN(NULLIF(d.destination_warehouse_code,'')))"));
assertTrue(sql.contains("THEN 1 ELSE COUNT(DISTINCT"));
assertTrue(sql.contains("0 AS destinationMismatchCount"));
}
}
@@ -0,0 +1,21 @@
package com.baiyue.lastleg.inboundplan.mapper;
import org.apache.ibatis.annotations.Select;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertTrue;
class InboundPlanQueryMapperContractTest {
@Test
void platformListReadsVersionAndDoesNotRequireANullDispatchCustomer() throws Exception {
Select select = InboundPlanQueryMapper.class
.getMethod("rows", String.class,
com.baiyue.lastleg.inboundplan.dto.InboundPlanRequests.Query.class,
long.class, long.class)
.getAnnotation(Select.class);
String sql = String.join(" ", select.value());
assertTrue(sql.contains("p.version version"));
assertTrue(sql.contains("WHERE <if test='customerId != null and customerId != \"\"'>di.customer_id=#{customerId} AND </if>di.order_id=l.order_id"));
}
}
@@ -12,6 +12,8 @@ import com.baiyue.lastleg.inboundplan.pda.entity.InboundPdaTask;
import com.baiyue.lastleg.inboundplan.pda.gateway.WarehouseResourceGateway;
import com.baiyue.lastleg.inboundplan.pda.gateway.UnloadPhotoFileGateway;
import com.baiyue.lastleg.inboundplan.pda.repository.PdaWorkflowRepository;
import com.baiyue.lastleg.dropship.service.DropshipOrderService;
import com.baiyue.lastleg.dropship.model.DropshipPalletSnapshot;
import com.baiyue.lastleg.entity.LastlegOrder;
import com.baiyue.lastleg.mapper.LastlegOrderMapper;
import com.fasterxml.jackson.databind.ObjectMapper;
@@ -51,6 +53,7 @@ class InboundPdaWorkflowServiceTest {
@Mock InboundUnloadCompletedOutboxService unloadCompletedOutbox;
@Mock LastlegOrderMapper orders;
@Mock UnloadPhotoFileGateway unloadPhotoFiles;
@Mock DropshipOrderService dropshipOrders;
private InboundPdaWorkflowService service;
private InboundPlan plan;
@@ -73,6 +76,7 @@ class InboundPdaWorkflowServiceTest {
ReflectionTestUtils.setField(service, "unloadCompletedOutbox", unloadCompletedOutbox);
ReflectionTestUtils.setField(service, "orders", orders);
ReflectionTestUtils.setField(service, "unloadPhotoFiles", unloadPhotoFiles);
ReflectionTestUtils.setField(service, "dropshipOrders", dropshipOrders);
ReflectionTestUtils.setField(service, "sourceObjectMapper", new ObjectMapper().findAndRegisterModules());
service.initializeObjectMapper();
plan = plan();
@@ -189,6 +193,20 @@ class InboundPdaWorkflowServiceTest {
assertEquals(3, targetPallet.getActualPackageCount());
}
@Test
void cartonScanRejectsBatchQuantityAndRequiresOneRealCartonPerScan() {
InboundPlanCarton first = carton("BOX-BATCH-1", "UNASSIGNED", targetLine.getId(), "WH-A");
InboundPlanCarton second = carton("BOX-BATCH-2", "UNASSIGNED", targetLine.getId(), "WH-A");
second.setId(42L);
activeTask();
when(repository.lockCarton("100", "BOX-BATCH-1")).thenReturn(first);
assertThrows(ResponseStatusException.class, () -> service.scanCarton("100", "LIP1",
new PdaRequests.ScanCarton("BOX-BATCH-1", null, 2, "scan-batch", "7", "worker")));
assertEquals("UNASSIGNED", first.getStatus());
assertEquals("UNASSIGNED", second.getStatus());
}
@Test
void cartonScanRejectsDuplicateAndWrongDestination() {
InboundPlanCarton repeated = carton("BOX-002", "ON_PALLET", targetLine.getId(), "WH-A");
@@ -225,7 +243,9 @@ class InboundPdaWorkflowServiceTest {
@Test
void countMustPrecedePickAndPickedBoxesDriveActualVolume() {
targetLine.setFbaWarehouseCode("ONT8");
activeTask();
when(repository.lines(plan.getId())).thenReturn(List.of(targetLine));
target.setCountedPackageCount(0);
when(repository.lockAllocation(plan.getId(), target.getId())).thenReturn(target);
when(repository.line(plan.getId(), targetLine.getId())).thenReturn(targetLine);
@@ -248,7 +268,9 @@ class InboundPdaWorkflowServiceTest {
@Test
void overPickReturnsConfirmationWithoutChangingAllocationOrPallet() {
targetLine.setFbaWarehouseCode("ONT8");
activeTask();
when(repository.lines(plan.getId())).thenReturn(List.of(targetLine));
when(repository.lockAllocation(plan.getId(), target.getId())).thenReturn(target);
when(repository.line(plan.getId(), targetLine.getId())).thenReturn(targetLine);
candidateQueries();
@@ -459,8 +481,95 @@ class InboundPdaWorkflowServiceTest {
verify(repository, never()).update(task);
}
@Test
void partialCompleteStocksOnlyActualPickedPackagesAndKeepsPartialStatus() {
target.setPickedPackageCount(2);
activeTask();
when(repository.lines(plan.getId())).thenReturn(List.of(targetLine));
when(repository.allocations(plan.getId())).thenReturn(List.of(target));
when(repository.pallets(plan.getId())).thenReturn(List.of(targetPallet));
when(repository.cartons(plan.getId())).thenReturn(List.of());
var result = service.partialComplete("100", "LIP1",
new PdaRequests.ForceComplete("partial-1", "7", "worker"));
assertEquals("PARTIAL_INBOUND", result.status());
assertEquals(5, result.expectedPackageCount());
assertEquals(2, result.pickedPackageCount());
assertEquals("PARTIAL_INBOUND", plan.getStatus());
verify(stockReceipts).stockIn(eq("100"), eq(plan), eq(task), eq(List.of(targetLine)),
eq(List.of(target)), eq(List.of(targetPallet)), any(), eq(true));
verify(unloadCompletedOutbox).publish(eq(plan), eq(task), eq(List.of(101L)));
}
@Test
void partialCompleteRequiresUnloadPhotosBeforeStockIn() {
target.setPickedPackageCount(2);
task.setUnloadPhotoFileIds(null);
task.setUnloadPhotoUploadedAt(null);
task.setEstimatedEmptyReturnAt(null);
activeTask();
ResponseStatusException exception = assertThrows(ResponseStatusException.class,
() -> service.partialComplete("100", "LIP1",
new PdaRequests.ForceComplete("partial-no-photo", "7", "worker")));
assertEquals(409, exception.getStatusCode().value());
verify(stockReceipts, never()).stockIn(any(), any(), any(), any(), any(), any(), any(), eq(true));
verify(unloadCompletedOutbox, never()).publish(any(), any(), any());
}
@Test
void dropshipTaskIsMarkedAndWholePalletPickReceivesThePlannedPalletAtOnce() {
plan.setSourceOrderId(101L);
LastlegOrder order = new LastlegOrder();
order.setId(101L); order.setOrderType("ONE_CLICK_DROPSHIP");
when(repository.order(101L)).thenReturn(order);
when(dropshipOrders.palletSnapshots("100", 101L)).thenReturn(List.of(
new DropshipPalletSnapshot("PLT-21", 3,
List.of(new DropshipPalletSnapshot.Sku("SKU-A", "Product A", 3, 30)))));
when(repository.lines(plan.getId())).thenReturn(List.of(targetLine));
when(repository.allocations(plan.getId())).thenReturn(List.of(target));
when(repository.pallets(plan.getId())).thenReturn(List.of(targetPallet));
when(repository.task("100", plan.getId())).thenReturn(task);
activeTask();
when(repository.lockAllocation(plan.getId(), target.getId())).thenReturn(target);
when(repository.line(plan.getId(), targetLine.getId())).thenReturn(targetLine);
when(repository.lockPallet(plan.getId(), targetPallet.getId())).thenReturn(targetPallet);
var taskResult = service.task("100", "LIP1");
var pickResult = service.pickWholePallet("100", "LIP1", target.getId(),
new PdaRequests.WholePalletOperation("whole-1", "7", "worker"));
assertEquals("ONE_CLICK_DROPSHIP", taskResult.orderType());
assertTrue(taskResult.wholePalletInbound());
assertEquals("SKU-A", taskResult.dropshipPallets().get(0).skus().get(0).skuCode());
assertEquals(targetPallet.getId(), taskResult.dropshipPallets().get(0).planPalletId());
assertEquals(target.getId(), taskResult.dropshipPallets().get(0).allocationId());
assertEquals("WHOLE_PALLET_PICKED", pickResult.result());
assertEquals(3, target.getPickedPackageCount());
assertEquals(3, targetPallet.getActualPackageCount());
}
@Test
void dropshipPlanRejectsOrdinaryPackagePicking() {
plan.setSourceOrderId(101L);
LastlegOrder order = new LastlegOrder();
order.setId(101L); order.setOrderType("ONE_CLICK_DROPSHIP");
when(repository.order(101L)).thenReturn(order);
ResponseStatusException exception = assertThrows(ResponseStatusException.class,
() -> service.pick("100", "LIP1", target.getId(),
new PdaRequests.PackageOperation(1, "wrong-mode", "7", "worker")));
assertEquals(409, exception.getStatusCode().value());
verify(repository, never()).lockAllocation(plan.getId(), target.getId());
}
@Test
void rejectsReuseOfIdempotencyKeyWithDifferentActionOrPayload() {
targetLine.setFbaWarehouseCode("ONT8");
when(repository.lines(plan.getId())).thenReturn(List.of(targetLine));
InboundPdaOperation existing = new InboundPdaOperation();
existing.setAction("COUNT");
existing.setRequestHash("different");
@@ -472,7 +581,9 @@ class InboundPdaWorkflowServiceTest {
@Test
void completedTaskStillReplaysAnAlreadyPersistedPick() {
targetLine.setFbaWarehouseCode("ONT8");
activeTask();
when(repository.lines(plan.getId())).thenReturn(List.of(targetLine));
when(repository.lockAllocation(plan.getId(), target.getId())).thenReturn(target);
when(repository.line(plan.getId(), targetLine.getId())).thenReturn(targetLine);
when(repository.lockPallet(plan.getId(), targetPallet.getId())).thenReturn(targetPallet);
@@ -19,6 +19,7 @@ import com.baiyue.lastleg.mapper.LastlegOrderDetailMapper;
import com.baiyue.lastleg.mapper.LastlegOrderStatusHistoryMapper;
import com.baiyue.lastleg.entity.LastlegOrderStatusHistory;
import com.baiyue.lastleg.dropship.service.DropshipOrderService;
import com.baiyue.lastleg.dropship.model.DropshipPalletSnapshot;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@@ -157,6 +158,21 @@ class InboundStockReceiptServiceTest {
assertEquals(5L, requests.getAllValues().stream().mapToLong(InventoryCommandRequests.StockIn::packages).sum());
}
@Test
void stocksInToHeldInventoryWhenInboundOrderIsDetained() {
Fixture fixture = fixture(5, 5, 5, 10);
fixture.order.setDetained(true);
when(orders.selectBatchIds(any())).thenReturn(List.of(fixture.order));
when(inventory.stockInHeld(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());
verify(inventory).stockInHeld(eq("100"), eq("operator"), any(InventoryCommandRequests.StockIn.class));
verify(inventory, never()).stockIn(any(), any(), any());
}
@Test
void existingTaskReceiptMakesStockInReplayANoOp() {
Fixture fixture = fixture(10, 5, 5, 10);
@@ -200,6 +216,7 @@ class InboundStockReceiptServiceTest {
void activatesDropshipAfterGenericStockInReceiptIsCreated() {
Fixture fixture = fixture(5, 5, 5, 10);
fixture.order.setOrderType("ONE_CLICK_DROPSHIP");
when(dropshipOrders.palletSnapshots("100", 101L)).thenReturn(List.of(dropshipPallet("PLT-21", 5)));
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));
@@ -227,6 +244,45 @@ class InboundStockReceiptServiceTest {
verify(receipts, never()).insert(any(InboundStockReceipt.class));
}
@Test
void partialStockInSynchronizesOrderWithoutActivatingDropship() {
Fixture fixture = fixture(5, 2, 2, 10);
fixture.order.setOrderType("ONE_CLICK_DROPSHIP");
when(dropshipOrders.palletSnapshots("100", 101L)).thenReturn(List.of(dropshipPallet("PLT-21", 2)));
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, 2, 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(), true);
assertEquals("INBOUND_COMPLETED", fixture.order.getCurrentStatus());
assertEquals("INBOUND_PLAN:PARTIAL_INBOUND", fixture.order.getSubOrderStatus());
verify(orders).updateById(fixture.order);
verify(orderHistories).insert(any(LastlegOrderStatusHistory.class));
verify(dropshipOrders, never()).activateAfterStockIn(any(), any(), any(), any());
}
@Test
void rejectsPartiallyPickedDropshipSourcePallet() {
Fixture fixture = fixture(5, 5, 2, 10);
fixture.order.setOrderType("ONE_CLICK_DROPSHIP");
when(orders.selectBatchIds(any())).thenReturn(List.of(fixture.order));
when(dropshipOrders.palletSnapshots("100", 101L)).thenReturn(List.of(dropshipPallet("PLT-21", 5)));
ResponseStatusException exception = assertThrows(ResponseStatusException.class,
() -> service.stockIn("100", fixture.plan, fixture.task, List.of(fixture.line),
List.of(fixture.allocation), List.of(fixture.pallet), LocalDateTime.now(), true));
assertEquals(409, exception.getStatusCode().value());
verify(inventory, never()).stockIn(any(), any(), any());
}
private DropshipPalletSnapshot dropshipPallet(String palletNo, long packages) {
return new DropshipPalletSnapshot(palletNo, packages,
List.of(new DropshipPalletSnapshot.Sku("SKU-1", "Product", packages, packages * 10)));
}
private Fixture fixture(int required, int planned, int picked, int quantity) {
InboundPlan plan = new InboundPlan(); plan.setId(1L); plan.setPlanNo("IP-1"); plan.setOverseasWarehouseId(900L);
InboundPdaTask task = new InboundPdaTask(); task.setId(2L); task.setOperatorId("operator");
@@ -502,6 +502,20 @@ class ReceivingContainerServiceBehaviorTest {
verifyNoInteractions(workflow, operations);
}
@Test
void cartonResolutionReturnsInboundAndPalletMergeReminderFromTailLegOrder() {
stubCurrent("FBA", "CONT-A");
LastlegOrder marked = order("FBA");
marked.setPalletMergeFlag("REQUIRED");
when(orders.selectById(1L)).thenReturn(marked);
var resolved = service.resolveCarton("7", new ReceivingContainerRequests.CartonResolve("IN-1#2-1"));
assertThat(resolved.inboundCode()).isEqualTo("IN-1");
assertThat(resolved.palletMergeRequired()).isTrue();
assertThat(resolved.palletMergeHint()).contains("合托");
}
@Test
void fbaCartonResolutionRejectsCrossContainerAndInvalidPrefixesAsConflicts() {
stubCurrent("FBA", "CONT-A");
@@ -70,7 +70,7 @@ class InboundPlanPaginationTest {
.getMethod("rows", String.class, InboundPlanRequests.Query.class, long.class, long.class)
.getAnnotation(org.apache.ibatis.annotations.Select.class).value()) + InboundPlanQueryMapper.FROM_AND_FILTER;
assertThat(sql).contains("a2.customer_id=#{customerId}", "ORDER BY a2.created_at DESC,ai2.id DESC LIMIT 1");
assertThat(sql).contains("di.customer_id=#{customerId} AND di.order_id=l.order_id");
assertThat(sql).contains("di.customer_id=#{customerId} AND </if>di.order_id=l.order_id");
assertThat(sql).contains("q.planNo", "q.inboundCode", "q.status", "q.overseasWarehouseId",
"q.destinationWarehouseCode", "q.warehouse",
"q.fbaWarehouseCode", "q.appointmentStatus", "q.dispatchStatus", "q.createdFrom", "q.createdTo");
@@ -2,6 +2,7 @@ package com.baiyue.lastleg.inboundplan.service;
import com.baiyue.lastleg.inboundplan.entity.InboundPlan;
import com.baiyue.lastleg.inboundplan.dto.InboundPlanRequests;
import com.baiyue.lastleg.inboundplan.dto.UpdateInboundPlanRequest;
import com.baiyue.lastleg.inboundplan.entity.InboundPlanLine;
import com.baiyue.lastleg.inboundplan.entity.InboundPlanPallet;
import com.baiyue.lastleg.inboundplan.entity.InboundPlanPalletAllocation;
@@ -55,6 +56,7 @@ class InboundPlanServiceTest {
ReflectionTestUtils.setField(service, "allocationMapper", allocationMapper);
ReflectionTestUtils.setField(service, "orderMapper", orderMapper);
ReflectionTestUtils.setField(service, "inventoryBalanceMapper", inventoryBalanceMapper);
ReflectionTestUtils.setField(service, "orderNoGenerator", new com.baiyue.lastleg.service.LastlegOrderNoGenerator());
org.mockito.Mockito.lenient().when(lineMapper.selectList(any())).thenReturn(List.of());
org.mockito.Mockito.lenient().when(palletMapper.selectList(any())).thenReturn(List.of());
org.mockito.Mockito.lenient().when(allocationMapper.selectList(any())).thenReturn(List.of());
@@ -182,13 +184,93 @@ class InboundPlanServiceTest {
}
@Test
void rejectsTransferWarehouseThatDiffersFromCargoSnapshots() {
SourceOrderSnapshot invalid = new SourceOrderSnapshot(1L, "100", "MSCU6639870", "RK-1",
void transferWarehouseOverridesOriginalCargoDestination() {
SourceOrderSnapshot transferred = new SourceOrderSnapshot(1L, "100", "MSCU6639870", "RK-1",
"DESTINATION_ASSIGNED", null, BigDecimal.ONE, 1, "WH-B", "WH-B", "Warehouse B", 1, 1);
when(orderMapper.lockOrder("100", 1L)).thenReturn(List.of(invalid));
when(orderMapper.lockOrder("100", 1L)).thenReturn(List.of(transferred));
when(orderMapper.markPlanned(any(), any(), any(), org.mockito.ArgumentMatchers.anyInt(), any()))
.thenReturn(1);
assertThrows(ResponseStatusException.class, () -> service.convert("100", 1L));
verify(planMapper, never()).insert(any(InboundPlan.class));
service.convert("100", 1L);
ArgumentCaptor<InboundPlanPallet> pallet = ArgumentCaptor.forClass(InboundPlanPallet.class);
verify(palletMapper).insert(pallet.capture());
assertEquals("WH-B", pallet.getValue().getDestinationWarehouseId());
assertEquals("WH-B", pallet.getValue().getDestinationWarehouseCode());
}
@Test
void convertsDropshipSourcePalletsWithoutRedistributingTheirPackages() {
when(orderMapper.lockOrder("100", 2L)).thenReturn(List.of(order(2L, "RK-2", "1.0000", "WH-A")));
when(orderMapper.markPlanned(any(), any(), any(), org.mockito.ArgumentMatchers.anyInt(), any()))
.thenReturn(1);
service.convertDropship("100", 2L, List.of(
new InboundPlanService.WholePalletInput("SOURCE-P-1", 3),
new InboundPlanService.WholePalletInput("SOURCE-P-2", 7)));
ArgumentCaptor<InboundPlanPallet> pallets = ArgumentCaptor.forClass(InboundPlanPallet.class);
verify(palletMapper, times(2)).insert(pallets.capture());
assertEquals(List.of("SOURCE-P-1", "SOURCE-P-2"),
pallets.getAllValues().stream().map(InboundPlanPallet::getPalletNo).toList());
ArgumentCaptor<InboundPlanPalletAllocation> allocations =
ArgumentCaptor.forClass(InboundPlanPalletAllocation.class);
verify(allocationMapper, times(2)).insert(allocations.capture());
assertEquals(List.of(3, 7), allocations.getAllValues().stream()
.map(InboundPlanPalletAllocation::getPlannedPackageCount).toList());
}
@Test
void updateSynchronizesDestinationSnapshotToLinesAndPallets() {
InboundPlan plan = new InboundPlan();
plan.setId(20L);
plan.setPlanNo("IP-20");
plan.setCustomerId("100");
plan.setStatus("PENDING_ASSIGNMENT");
plan.setLineCount(1);
plan.setPalletCount(1);
plan.setVersion(3);
InboundPlanLine line = new InboundPlanLine();
line.setId(21L);
line.setCustomerId("100");
line.setPlanId(20L);
line.setRequiredPackageCount(1);
line.setPlannedPalletCount(1);
line.setDestinationWarehouseId("OLD-WH");
line.setDestinationWarehouseCode("OLD-CODE");
line.setDestinationWarehouseName("Old warehouse");
InboundPlanPallet pallet = new InboundPlanPallet();
pallet.setId(22L);
pallet.setCustomerId("100");
pallet.setPlanId(20L);
pallet.setSequenceNo(1);
pallet.setActualPackageCount(0);
pallet.setDestinationWarehouseId("OLD-WH");
pallet.setDestinationWarehouseCode("OLD-CODE");
pallet.setDestinationWarehouseName("Old warehouse");
when(planMapper.selectOne(any())).thenReturn(plan);
when(lineMapper.selectList(any())).thenReturn(List.of(line), List.of(line));
when(palletMapper.selectList(any())).thenReturn(List.of(pallet), List.of(pallet));
when(allocationMapper.selectList(any())).thenReturn(List.of());
service.update("100", "IP-20",
new UpdateInboundPlanRequest("100", 193000000000200003L,
" US-LAX-01 ", " Los Angeles Warehouse ", 3));
ArgumentCaptor<InboundPlanLine> updatedLine = ArgumentCaptor.forClass(InboundPlanLine.class);
verify(lineMapper).updateById(updatedLine.capture());
assertEquals("193000000000200003", updatedLine.getValue().getDestinationWarehouseId());
assertEquals("US-LAX-01", updatedLine.getValue().getDestinationWarehouseCode());
assertEquals("Los Angeles Warehouse", updatedLine.getValue().getDestinationWarehouseName());
ArgumentCaptor<InboundPlanPallet> updatedPallet = ArgumentCaptor.forClass(InboundPlanPallet.class);
verify(palletMapper).updateById(updatedPallet.capture());
assertEquals("193000000000200003", updatedPallet.getValue().getDestinationWarehouseId());
assertEquals("US-LAX-01", updatedPallet.getValue().getDestinationWarehouseCode());
assertEquals("Los Angeles Warehouse", updatedPallet.getValue().getDestinationWarehouseName());
}
@Test
@@ -49,6 +49,16 @@ class PalletAllocatorTest {
.map(PalletAllocator.AllocationDraft::plannedPackageCount).toList());
}
@Test
void keepsAtLeastTheFirstLegRecommendedPalletCount() {
PalletAllocator.Result result = PalletAllocator.allocate(List.of(
new PalletAllocator.LineInput(1L, "WH-A", new BigDecimal("1.5000"), 6, 3)));
assertEquals(3, result.pallets().size());
assertEquals(6, result.allocations().stream()
.mapToInt(PalletAllocator.AllocationDraft::plannedPackageCount).sum());
}
private PalletAllocator.Result allocate(String volume) {
return PalletAllocator.allocate(List.of(
new PalletAllocator.LineInput(1L, "WH-A", new BigDecimal(volume),