This commit is contained in:
2026-09-10 17:11:33 +08:00
parent 1ee32b7a6f
commit 2e6a18c58e
54 changed files with 2094 additions and 167 deletions
@@ -1,9 +1,8 @@
package com.baiyue.lastleg.transfer.service;
import com.baiyue.lastleg.entity.LastlegOrder;
import com.baiyue.lastleg.entity.LastlegInboundOrder;
import com.baiyue.lastleg.relabel.entity.RelabelApplication;
import com.baiyue.lastleg.relabel.entity.RelabelItem;
import com.baiyue.lastleg.relabel.entity.RelabelStatusHistory;
import com.baiyue.lastleg.inventory.command.dto.InventoryCommandResponse;
import com.baiyue.lastleg.inventory.command.mapper.InventoryCommandMapper;
import com.baiyue.lastleg.inventory.command.service.InventoryCommandService;
@@ -12,16 +11,17 @@ import com.baiyue.lastleg.inventory.entity.InventoryPosition;
import com.baiyue.lastleg.mapper.LastlegOrderDetailMapper;
import com.baiyue.lastleg.mapper.LastlegOrderMapper;
import com.baiyue.lastleg.mapper.LastlegOrderStatusHistoryMapper;
import com.baiyue.lastleg.mapper.LastlegInboundOrderMapper;
import com.baiyue.lastleg.relabel.mapper.RelabelApplicationMapper;
import com.baiyue.lastleg.relabel.mapper.RelabelItemMapper;
import com.baiyue.lastleg.relabel.mapper.RelabelStatusHistoryMapper;
import com.baiyue.lastleg.service.LastlegOrderNoGenerator;
import com.baiyue.lastleg.transfer.dto.WarehouseTransferRequests;
import com.baiyue.lastleg.transfer.entity.WarehouseTransferOrder;
import com.baiyue.lastleg.transfer.entity.WarehouseTransferCarton;
import com.baiyue.lastleg.transfer.entity.WarehouseTransferInventoryAllocation;
import com.baiyue.lastleg.transfer.entity.WarehouseTransferSourceOrder;
import com.baiyue.lastleg.transfer.mapper.InboundSnapshotMapper;
import com.baiyue.lastleg.transfer.mapper.WarehouseTransferItemMapper;
import com.baiyue.lastleg.transfer.mapper.WarehouseTransferCartonMapper;
import com.baiyue.lastleg.transfer.mapper.WarehouseTransferInventoryAllocationMapper;
import com.baiyue.lastleg.transfer.mapper.WarehouseTransferOrderMapper;
import com.baiyue.lastleg.transfer.mapper.WarehouseTransferReceiptMapper;
@@ -58,21 +58,22 @@ import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.times;
@ExtendWith(MockitoExtension.class)
class WarehouseTransferServiceTest {
@Mock WarehouseTransferOrderMapper transfers;
@Mock WarehouseTransferItemMapper transferItems;
@Mock WarehouseTransferCartonMapper transferCartons;
@Mock WarehouseTransferSourceOrderMapper sourceOrders;
@Mock WarehouseTransferInventoryAllocationMapper inventoryAllocations;
@Mock WarehouseTransferReceiptMapper transferReceipts;
@Mock InboundSnapshotMapper inboundSnapshots;
@Mock LastlegInboundOrderMapper inboundOrders;
@Mock LastlegOrderMapper orders;
@Mock LastlegOrderDetailMapper orderDetails;
@Mock LastlegOrderStatusHistoryMapper orderHistories;
@Mock RelabelApplicationMapper relabelApplications;
@Mock RelabelItemMapper relabelItems;
@Mock RelabelStatusHistoryMapper relabelHistories;
@Spy LastlegOrderNoGenerator orderNoGenerator = new LastlegOrderNoGenerator();
@Mock InventoryCommandService inventory;
@Mock InventoryCommandMapper inventoryCommands;
@@ -83,16 +84,16 @@ class WarehouseTransferServiceTest {
service = new WarehouseTransferService();
ReflectionTestUtils.setField(service, "transfers", transfers);
ReflectionTestUtils.setField(service, "transferItems", transferItems);
ReflectionTestUtils.setField(service, "transferCartons", transferCartons);
ReflectionTestUtils.setField(service, "sourceOrders", sourceOrders);
ReflectionTestUtils.setField(service, "inventoryAllocations", inventoryAllocations);
ReflectionTestUtils.setField(service, "transferReceipts", transferReceipts);
ReflectionTestUtils.setField(service, "inboundSnapshots", inboundSnapshots);
ReflectionTestUtils.setField(service, "inboundOrders", inboundOrders);
ReflectionTestUtils.setField(service, "orders", orders);
ReflectionTestUtils.setField(service, "orderDetails", orderDetails);
ReflectionTestUtils.setField(service, "orderHistories", orderHistories);
ReflectionTestUtils.setField(service, "relabelApplications", relabelApplications);
ReflectionTestUtils.setField(service, "relabelItems", relabelItems);
ReflectionTestUtils.setField(service, "relabelHistories", relabelHistories);
ReflectionTestUtils.setField(service, "orderNoGenerator", orderNoGenerator);
ReflectionTestUtils.setField(service, "inventory", inventory);
ReflectionTestUtils.setField(service, "inventoryCommands", inventoryCommands);
@@ -101,10 +102,11 @@ class WarehouseTransferServiceTest {
.thenReturn("WT-WH-20260901-000001");
lenient().when(inventoryAllocations.selectList(any())).thenReturn(List.of());
lenient().when(transferReceipts.selectList(any())).thenReturn(List.of());
lenient().when(transferCartons.listOrder(anyString(), anyLong())).thenReturn(List.of());
}
@Test
void relabelTransferLinksHistoricalOrderAndInboundAndCreatesOnePendingReviewTailOrder() {
void relabelTransferOnlyMarksPendingBindingUntilInboundIsComplete() {
WarehouseTransferRequests.Create request = request(true);
LastlegOrder historical = historicalOrder();
when(inboundSnapshots.findId("TENANT-A", "IN-1")).thenReturn(101L);
@@ -117,18 +119,15 @@ class WarehouseTransferServiceTest {
ArgumentCaptor<LastlegOrder> tailOrder = ArgumentCaptor.forClass(LastlegOrder.class);
verify(orders).insert(tailOrder.capture());
assertEquals(WarehouseTransferService.RETURN_RELABEL_ORDER_TYPE, tailOrder.getValue().getOrderType());
assertEquals("PENDING_ASSIGNMENT", tailOrder.getValue().getCurrentStatus());
assertEquals("WAREHOUSE_TRANSFER:PENDING_REVIEW;RELABEL:PENDING_PROCESSING", tailOrder.getValue().getSubOrderStatus());
assertEquals(WarehouseTransferService.ORDER_TYPE, tailOrder.getValue().getOrderType());
assertEquals(WarehouseTransferService.PENDING_REVIEW, tailOrder.getValue().getCurrentStatus());
assertEquals("WAREHOUSE_TRANSFER:PENDING_REVIEW;RELABEL:PENDING_BINDING", tailOrder.getValue().getSubOrderStatus());
assertTrue(tailOrder.getValue().getOrderNo().matches("WT-WH-\\d{8}-\\d{6}"));
assertNull(tailOrder.getValue().getTransferWarehouseId());
verify(sourceOrders).insert(any(WarehouseTransferSourceOrder.class));
verify(relabelApplications).insert(any(RelabelApplication.class));
verify(relabelItems).insert(any(RelabelItem.class));
verify(relabelHistories).insert(any(RelabelStatusHistory.class));
verify(orders).updateById(tailOrder.getValue());
assertNotNull(tailOrder.getValue().getRelabelOrderNo());
assertEquals("PENDING_PROCESSING", tailOrder.getValue().getRelabelStatus());
verify(relabelApplications, never()).insert(any(RelabelApplication.class));
assertNull(tailOrder.getValue().getRelabelOrderNo());
assertEquals("PENDING_BINDING", tailOrder.getValue().getRelabelStatus());
assertEquals(WarehouseTransferService.PENDING_REVIEW, result.status());
assertEquals(WarehouseTransferService.PENDING_REVIEW, result.orderStatus());
}
@@ -144,11 +143,71 @@ class WarehouseTransferServiceTest {
var result = service.create("TENANT-A", "USER-1", request(false));
verify(relabelApplications, never()).insert(any(RelabelApplication.class));
verify(relabelItems, never()).insert(any(RelabelItem.class));
verify(relabelHistories, never()).insert(any(RelabelStatusHistory.class));
assertNull(result.relabelNo());
}
@Test
void transferCanEnterTheTransitWarehouseBeforeFbaDestinationIsKnown() {
WarehouseTransferRequests.Create request = new WarehouseTransferRequests.Create(
"IN-1", List.of(11L), "WH-1", null, "123 Pickup Road", 5,
new BigDecimal("0.2500"), "WAREHOUSE_PICKUP", LocalDateTime.of(2026, 9, 12, 10, 0),
null, null, null, "FBA destination will be supplied later", false, "key-no-fba",
List.of(new WarehouseTransferRequests.Item("SKU-1", "Product", 5, "MARK-1")));
when(inboundSnapshots.findId("TENANT-A", "IN-1")).thenReturn(101L);
when(orders.selectList(any())).thenReturn(List.of(historicalOrder()));
when(transfers.findNo(eq("TENANT-A"), org.mockito.ArgumentMatchers.anyString())).thenAnswer(invocation -> persistedTransfer());
when(transferItems.selectList(any())).thenReturn(List.of());
when(sourceOrders.selectList(any())).thenReturn(List.of());
service.create("TENANT-A", "USER-1", request);
ArgumentCaptor<WarehouseTransferOrder> transfer = ArgumentCaptor.forClass(WarehouseTransferOrder.class);
ArgumentCaptor<LastlegOrder> tailOrder = ArgumentCaptor.forClass(LastlegOrder.class);
verify(transfers).insert(transfer.capture());
verify(orders).insert(tailOrder.capture());
assertNull(transfer.getValue().getFbaWarehouseCode());
assertNull(transfer.getValue().getFbaShipmentNo());
assertNull(tailOrder.getValue().getFbaWarehouseCode());
assertEquals("WAREHOUSE_PICKUP", transfer.getValue().getDeliveryMethod());
}
@Test
void newTransitInboundCreatesSnapshotAndImmutableCartonMarksWithoutHistoricalInventory() {
WarehouseTransferRequests.Create request = new WarehouseTransferRequests.Create(
null, null, "WH-1", null, "123 Pickup Road", 5,
new BigDecimal("0.2500"), "WAREHOUSE_PICKUP", LocalDateTime.of(2026, 9, 12, 10, 0),
null, "TRACK-MUST-NOT-BE-SAVED", null, "Incoming transit cargo", true, "key-incoming",
List.of(new WarehouseTransferRequests.Item("SKU-1", "Product A", 2, null),
new WarehouseTransferRequests.Item("SKU-2", "Product B", 3, null)));
when(transfers.findNo(eq("TENANT-A"), anyString())).thenAnswer(invocation -> persistedTransfer());
when(transferItems.selectList(any())).thenReturn(List.of());
when(sourceOrders.selectList(any())).thenReturn(List.of());
service.create("TENANT-A", "USER-1", request);
ArgumentCaptor<LastlegInboundOrder> inbound = ArgumentCaptor.forClass(LastlegInboundOrder.class);
ArgumentCaptor<WarehouseTransferOrder> transfer = ArgumentCaptor.forClass(WarehouseTransferOrder.class);
ArgumentCaptor<WarehouseTransferCarton> cartons = ArgumentCaptor.forClass(WarehouseTransferCarton.class);
ArgumentCaptor<LastlegOrder> tailOrder = ArgumentCaptor.forClass(LastlegOrder.class);
verify(inboundOrders).insert(inbound.capture());
verify(transfers).insert(transfer.capture());
verify(transferCartons, times(5)).insert(cartons.capture());
verify(orders).insert(tailOrder.capture());
verify(sourceOrders, never()).insert(any(WarehouseTransferSourceOrder.class));
verify(relabelApplications, never()).insert(any(RelabelApplication.class));
assertEquals(transfer.getValue().getTransferNo(), transfer.getValue().getInboundCode());
assertEquals(transfer.getValue().getTransferNo(), inbound.getValue().getInboundCode());
assertEquals("OVERSEAS", inbound.getValue().getDestinationType());
assertEquals(WarehouseTransferService.PENDING_REVIEW, inbound.getValue().getStatus());
assertNull(transfer.getValue().getTrackingNo());
assertEquals("PENDING_BINDING", tailOrder.getValue().getRelabelStatus());
assertEquals(List.of(1, 2, 3, 4, 5), cartons.getAllValues().stream()
.map(WarehouseTransferCarton::getCartonIndex).toList());
assertTrue(cartons.getAllValues().stream().allMatch(value -> value.getCartonMark()
.matches("WT-WH-\\d{8}-\\d{6}#5-[1-5]")));
}
@Test
void approvedTransferWithoutRelabelEntersPendingAssignment() {
WarehouseTransferOrder transfer = persistedTransfer();