fix: preserve dropship pallets in container plans

This commit is contained in:
2026-09-02 01:17:00 +08:00
parent fd9a070efc
commit cc02037f78
3 changed files with 67 additions and 15 deletions
@@ -13,6 +13,8 @@ import com.baiyue.lastleg.inboundplan.mapper.InboundPlanPalletAllocationMapper;
import com.baiyue.lastleg.inboundplan.mapper.InboundPlanPalletMapper;
import com.baiyue.lastleg.inboundplan.model.SourceOrderSnapshot;
import com.baiyue.lastleg.inventory.mapper.InventoryBalanceMapper;
import com.baiyue.lastleg.dropship.model.DropshipPalletSnapshot;
import com.baiyue.lastleg.dropship.service.DropshipOrderService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@@ -44,6 +46,7 @@ class InboundPlanServiceTest {
@Mock InboundPlanPalletAllocationMapper allocationMapper;
@Mock InboundPlanOrderMapper orderMapper;
@Mock InventoryBalanceMapper inventoryBalanceMapper;
@Mock DropshipOrderService dropshipOrders;
private InboundPlanService service;
@@ -56,6 +59,7 @@ class InboundPlanServiceTest {
ReflectionTestUtils.setField(service, "allocationMapper", allocationMapper);
ReflectionTestUtils.setField(service, "orderMapper", orderMapper);
ReflectionTestUtils.setField(service, "inventoryBalanceMapper", inventoryBalanceMapper);
ReflectionTestUtils.setField(service, "dropshipOrders", dropshipOrders);
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());
@@ -278,8 +282,8 @@ class InboundPlanServiceTest {
assertNotNull(InboundPlanService.class.getMethod("convertContainer", String.class)
.getAnnotation(Transactional.class));
when(orderMapper.lockContainerOrders("BOOKING-1")).thenReturn(List.of(
new InboundPlanOrderMapper.ContainerOrderRef("100", 1L, "PENDING_ASSIGNMENT", "MSCU6639870"),
new InboundPlanOrderMapper.ContainerOrderRef("200", 2L, "DESTINATION_ASSIGNED", "MSCU6639870")));
new InboundPlanOrderMapper.ContainerOrderRef("100", 1L, "PENDING_ASSIGNMENT", "MSCU6639870", "STANDARD"),
new InboundPlanOrderMapper.ContainerOrderRef("200", 2L, "DESTINATION_ASSIGNED", "MSCU6639870", "STANDARD")));
when(orderMapper.lockOrder("100", 1L)).thenReturn(List.of(order("100", 1L, "RK-1", "WH-A")));
when(orderMapper.lockOrder("200", 2L)).thenReturn(List.of(order("200", 2L, "RK-2", "WH-B")));
when(orderMapper.markPlanned(any(), any(), any(), org.mockito.ArgumentMatchers.anyInt(), any()))
@@ -296,11 +300,36 @@ class InboundPlanServiceTest {
verify(orderMapper).lockOrder("200", 2L);
}
@Test
void containerConversionPreservesDropshipSourcePallets() {
when(orderMapper.lockContainerOrders("BOOKING-1")).thenReturn(List.of(
new InboundPlanOrderMapper.ContainerOrderRef("100", 1L, "PENDING_ASSIGNMENT",
"MSCU6639870", "ONE_CLICK_DROPSHIP")));
when(orderMapper.lockOrder("100", 1L)).thenReturn(List.of(order(1L, "RK-1", "1.0000", "WH-A")));
when(dropshipOrders.palletSnapshots("100", 1L)).thenReturn(List.of(
new DropshipPalletSnapshot("SOURCE-P-1", 3, List.of()),
new DropshipPalletSnapshot("SOURCE-P-2", 7, List.of())));
when(orderMapper.markPlanned(any(), any(), any(), org.mockito.ArgumentMatchers.anyInt(), any()))
.thenReturn(1);
service.convertContainer("BOOKING-1");
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 invalidLaterOrderCausesNoWritesBecauseWholeContainerIsPrevalidated() {
when(orderMapper.lockContainerOrders("BOOKING-1")).thenReturn(List.of(
new InboundPlanOrderMapper.ContainerOrderRef("100", 1L, "PENDING_ASSIGNMENT", "MSCU6639870"),
new InboundPlanOrderMapper.ContainerOrderRef("100", 2L, "PENDING_ASSIGNMENT", "MSCU6639870")));
new InboundPlanOrderMapper.ContainerOrderRef("100", 1L, "PENDING_ASSIGNMENT", "MSCU6639870", "STANDARD"),
new InboundPlanOrderMapper.ContainerOrderRef("100", 2L, "PENDING_ASSIGNMENT", "MSCU6639870", "STANDARD")));
when(orderMapper.lockOrder("100", 1L)).thenReturn(List.of(order(1L, "RK-1", "1", "WH-A")));
when(orderMapper.lockOrder("100", 2L)).thenReturn(List.of(new SourceOrderSnapshot(2L, "100", "MSCU6639870", "RK-2",
"PENDING_ASSIGNMENT", null, BigDecimal.ONE, 1, null, null, null, 1, 0)));
@@ -315,8 +344,8 @@ class InboundPlanServiceTest {
@Test
void completeContainerReplayReturnsStableExistingPlansWithoutWriting() {
var refs = List.of(
new InboundPlanOrderMapper.ContainerOrderRef("100", 1L, "INBOUND_PLANNED", "MSCU6639870"),
new InboundPlanOrderMapper.ContainerOrderRef("200", 2L, "INBOUND_COMPLETED", "MSCU6639870"));
new InboundPlanOrderMapper.ContainerOrderRef("100", 1L, "INBOUND_PLANNED", "MSCU6639870", "STANDARD"),
new InboundPlanOrderMapper.ContainerOrderRef("200", 2L, "INBOUND_COMPLETED", "MSCU6639870", "STANDARD"));
when(orderMapper.lockContainerOrders("BOOKING-1")).thenReturn(refs);
when(planMapper.findBySourceOrder("100", 1L)).thenReturn(existingPlan("100", 1L, "LIP-1"));
when(planMapper.findBySourceOrder("200", 2L)).thenReturn(existingPlan("200", 2L, "LIP-2"));
@@ -331,8 +360,8 @@ class InboundPlanServiceTest {
@Test
void partialExistingPlansAreRejectedBeforeAnyWrite() {
when(orderMapper.lockContainerOrders("BOOKING-1")).thenReturn(List.of(
new InboundPlanOrderMapper.ContainerOrderRef("100", 1L, "INBOUND_PLANNED", "MSCU6639870"),
new InboundPlanOrderMapper.ContainerOrderRef("100", 2L, "PENDING_ASSIGNMENT", "MSCU6639870")));
new InboundPlanOrderMapper.ContainerOrderRef("100", 1L, "INBOUND_PLANNED", "MSCU6639870", "STANDARD"),
new InboundPlanOrderMapper.ContainerOrderRef("100", 2L, "PENDING_ASSIGNMENT", "MSCU6639870", "STANDARD")));
when(planMapper.findBySourceOrder("100", 1L)).thenReturn(existingPlan("100", 1L, "LIP-1"));
assertThrows(ResponseStatusException.class, () -> service.convertContainer("BOOKING-1"));
@@ -344,8 +373,8 @@ class InboundPlanServiceTest {
@Test
void rejectsContainerDriftAcrossCustomersBeforeWritingAnyPlan() {
when(orderMapper.lockContainerOrders("BOOKING-1")).thenReturn(List.of(
new InboundPlanOrderMapper.ContainerOrderRef("100", 1L, "PENDING_ASSIGNMENT", "MSCU6639870"),
new InboundPlanOrderMapper.ContainerOrderRef("200", 2L, "PENDING_ASSIGNMENT", "CSQU3054383")));
new InboundPlanOrderMapper.ContainerOrderRef("100", 1L, "PENDING_ASSIGNMENT", "MSCU6639870", "STANDARD"),
new InboundPlanOrderMapper.ContainerOrderRef("200", 2L, "PENDING_ASSIGNMENT", "CSQU3054383", "STANDARD")));
ResponseStatusException error = assertThrows(ResponseStatusException.class,
() -> service.convertContainer("BOOKING-1"));
@@ -358,8 +387,8 @@ class InboundPlanServiceTest {
@Test
void rejectsReplayWhenExistingPlanContainerSnapshotHasDrifted() {
when(orderMapper.lockContainerOrders("BOOKING-1")).thenReturn(List.of(
new InboundPlanOrderMapper.ContainerOrderRef("100", 1L, "INBOUND_PLANNED", "MSCU6639870"),
new InboundPlanOrderMapper.ContainerOrderRef("200", 2L, "INBOUND_PLANNED", "MSCU6639870")));
new InboundPlanOrderMapper.ContainerOrderRef("100", 1L, "INBOUND_PLANNED", "MSCU6639870", "STANDARD"),
new InboundPlanOrderMapper.ContainerOrderRef("200", 2L, "INBOUND_PLANNED", "MSCU6639870", "STANDARD")));
InboundPlan first = existingPlan("100", 1L, "LIP-1");
InboundPlan drifted = existingPlan("200", 2L, "LIP-2");
drifted.setContainerNo("CSQU3054383");