Merge remote-tracking branch 'origin/test' into test

This commit is contained in:
2026-10-12 20:27:50 +08:00
24 changed files with 303 additions and 50 deletions
@@ -92,6 +92,88 @@ class PdaPalletOperationServiceTest {
service.configureTransactions(new NoOpTransactionManager());
}
@Test void returnPalletTotalsFollowCurrentBindingsWithoutCountingRetriesTwice() {
var first = new ReturnInventoryCarton(); first.setWeight(new java.math.BigDecimal("5"));
first.setLength(new java.math.BigDecimal("30")); first.setWidth(new java.math.BigDecimal("20"));
first.setHeight(new java.math.BigDecimal("20")); first.setDestinationWarehouseCode("SCK8");
var second = new ReturnInventoryCarton(); second.setWeight(new java.math.BigDecimal("11"));
second.setLength(new java.math.BigDecimal("40")); second.setWidth(new java.math.BigDecimal("30"));
second.setHeight(new java.math.BigDecimal("30")); second.setDestinationWarehouseCode("SCK8");
var pallet = new PalletPhysicalGateway.PalletReference(10L, "TARGET", 88L, null, "OCCUPIED", "SCK8", 2);
when(returnCartons.palletCartons("88", "TARGET")).thenReturn(List.of(first), List.of(first, second), List.of(first, second), List.of(first));
var before = (com.baiyue.lastleg.palletops.dto.PalletOperationResponses.PalletContent) ReflectionTestUtils.invokeMethod(service, "content", 88L, pallet);
assertEquals(0, before.grossWeightKg().compareTo(new java.math.BigDecimal("5")));
var added = (com.baiyue.lastleg.palletops.dto.PalletOperationResponses.PalletContent) ReflectionTestUtils.invokeMethod(service, "content", 88L, pallet);
assertEquals(2, added.totalPackages());
assertEquals(0, added.grossWeightKg().compareTo(new java.math.BigDecimal("16")));
assertEquals(0, added.volumeCbm().compareTo(new java.math.BigDecimal("0.048")));
assertEquals(added, ReflectionTestUtils.invokeMethod(service, "content", 88L, pallet));
assertEquals(before, ReflectionTestUtils.invokeMethod(service, "content", 88L, pallet));
}
@Test void returnRebindingRequiresCurrentRoundBindingAndAddsToExistingPallet() {
var workTasks = mock(com.baiyue.lastleg.relabel.pda.mapper.PdaRelabelTaskMapper.class);
var workHistory = mock(com.baiyue.lastleg.relabel.pda.mapper.PdaRelabelHistoryMapper.class);
ReflectionTestUtils.setField(service, "relabelWorkTasks", workTasks);
ReflectionTestUtils.setField(service, "relabelWorkHistory", workHistory);
var app = new RelabelApplication(); app.setId(600L); app.setCustomerId("100");
var task = new com.baiyue.lastleg.relabel.pda.entity.PdaRelabelTask(); task.setId(700L);
when(workTasks.find("100", 600L)).thenReturn(task);
var item = new RelabelItem(); item.setReturnCartonId(801L); item.setCartonMark("BOX-1");
item.setDestinationFbaWarehouseCode("SCK8"); item.setNotifiedPackageCount(1);
var box = new ReturnInventoryCarton(); box.setId(801L); box.setCustomerId("100");
box.setPalletCode("SOURCE"); box.setRelabelApplicationId(600L); box.setStatus("RELABEL_PENDING");
box.setTotalQuantity(10L); box.setWeight(new java.math.BigDecimal("11"));
box.setLength(new java.math.BigDecimal("40")); box.setWidth(new java.math.BigDecimal("30"));
box.setHeight(new java.math.BigDecimal("30")); box.setDestinationWarehouseCode("SCK8");
var existing = new ReturnInventoryCarton(); existing.setWeight(new java.math.BigDecimal("5"));
existing.setLength(new java.math.BigDecimal("30")); existing.setWidth(new java.math.BigDecimal("20"));
existing.setHeight(new java.math.BigDecimal("20")); existing.setDestinationWarehouseCode("SCK8");
var source = new PalletPhysicalGateway.PalletReference(20L, "SOURCE", 88L, 1020L, "OCCUPIED", "SCK8", 1);
var target = new PalletPhysicalGateway.PalletReference(10L, "TARGET", 88L, 1010L, "OCCUPIED", "SCK8", 2);
when(returnCartons.lockId(801L)).thenReturn(box);
when(returnCartons.palletCartons("88", "TARGET")).thenAnswer(call ->
"TARGET".equals(box.getPalletCode()) ? List.of(existing, box) : List.of(existing));
var request = new PalletOperationRequests.RelabelDisassemble("rebind-current", "RL-1",
new PalletOperationRequests.PalletExpectation("SOURCE", 1),
new PalletOperationRequests.PalletExpectation("TARGET", 2), List.of("BOX-1"), "rebind");
assertThrows(ResponseStatusException.class, () -> ReflectionTestUtils.invokeMethod(service,
"bindReturnRelabelPallet", identity(), app, List.of(item), source, target, request));
verifyNoInteractions(physical);
var binding = new com.baiyue.lastleg.relabel.pda.entity.PdaRelabelHistory();
binding.setDetailJson("{\"round\":\"INITIAL\",\"cartonMarks\":[\"BOX-1\"]}");
when(workHistory.selectList(any())).thenReturn(List.of(binding));
task.setReviewedAt(LocalDateTime.of(2026, 10, 12, 12, 0));
assertThrows(ResponseStatusException.class, () -> ReflectionTestUtils.invokeMethod(service,
"bindReturnRelabelPallet", identity(), app, List.of(item), source, target, request));
task.setReviewedAt(null);
when(physical.resolveLocation(88L, 1020L)).thenReturn(
new PalletPhysicalGateway.LocationReference(1020L, "SOURCE-LOC", "STORAGE", "ACTIVE"));
when(physical.resolveLocation(88L, 1010L)).thenReturn(
new PalletPhysicalGateway.LocationReference(1010L, "TARGET-LOC", "STORAGE", "ACTIVE"));
when(physical.resolve("SOURCE")).thenReturn(source);
when(physical.resolve("TARGET")).thenReturn(target);
var result = (com.baiyue.lastleg.palletops.dto.PalletOperationResponses.Operation)
ReflectionTestUtils.invokeMethod(service, "bindReturnRelabelPallet", identity(), app,
List.of(item), source, target, request);
assertEquals("TARGET", box.getPalletCode());
assertEquals(0, result.pallets().get(0).totalPackages());
assertEquals(2, result.pallets().get(1).totalPackages());
assertEquals(0, result.pallets().get(1).grossWeightKg().compareTo(new java.math.BigDecimal("16")));
assertEquals(0, result.pallets().get(1).volumeCbm().compareTo(new java.math.BigDecimal("0.048")));
verify(returnCartons).updateById(box);
verify(workHistory).insert(any(com.baiyue.lastleg.relabel.pda.entity.PdaRelabelHistory.class));
}
@Test void missingCartonMeasurementDoesNotBecomeZeroWeight() {
var box = new ReturnInventoryCarton(); box.setWeight(new java.math.BigDecimal("5"));
when(returnCartons.palletCartons("88", "TARGET")).thenReturn(List.of(box));
var pallet = new PalletPhysicalGateway.PalletReference(10L, "TARGET", 88L, null, "OCCUPIED", "SCK8", 2);
var result = (com.baiyue.lastleg.palletops.dto.PalletOperationResponses.PalletContent) ReflectionTestUtils.invokeMethod(service, "content", 88L, pallet);
assertNull(result.volumeCbm());
assertEquals(0, result.grossWeightKg().compareTo(new java.math.BigDecimal("5")));
}
@Test void partialReturnRelabelKeepsUnlabelledRemainderOnOriginalPallet() {
RelabelApplication app = new RelabelApplication(); app.setId(600L); app.setCustomerId("100"); app.setRelabelNo("RL-1");
RelabelItem item = new RelabelItem(); item.setId(701L); item.setSkuCode("SKU-1");
@@ -82,6 +82,31 @@ class PdaRelabelServiceTest {
lenient().when(tasks.updateWithVersion(any(), anyInt())).thenReturn(1);
}
@Test
void detailDistinguishesInventoryReferenceFromRecordedReturnPallet() {
when(applications.findAnyByRelabelNo("FBR-1")).thenReturn(application);
var positions = org.mockito.Mockito.mock(com.baiyue.lastleg.inventory.mapper.InventoryPositionMapper.class);
ReflectionTestUtils.setField(service, "sourcePositions", positions);
item.setSourceInventoryPositionId(30L);
var position = new com.baiyue.lastleg.inventory.entity.InventoryPosition();
position.setPalletNo("PLT-REFERENCE");
when(positions.findTenantPosition("100", 30L)).thenReturn(position);
var ordinary = service.detail("7", 900L, "FBR-1").items().get(0);
assertEquals("PLT-REFERENCE", ordinary.sourcePalletCode());
assertEquals("INVENTORY_REFERENCE", ordinary.sourcePalletBasis());
var cartons = org.mockito.Mockito.mock(com.baiyue.lastleg.returnreceipt.mapper.ReturnInventoryCartonMapper.class);
ReflectionTestUtils.setField(service, "returnCartons", cartons);
item.setReturnCartonId(40L);
var carton = new com.baiyue.lastleg.returnreceipt.entity.ReturnInventoryCarton();
carton.setCustomerId("100"); carton.setPalletCode("PLT-RETURN"); carton.setTotalQuantity(20L);
when(cartons.selectById(40L)).thenReturn(carton);
var returned = service.detail("7", 900L, "FBR-1").items().get(0);
assertEquals("PLT-RETURN", returned.sourcePalletCode());
assertEquals("RETURN_CARTON", returned.sourcePalletBasis());
carton.setCustomerId("OTHER");
assertEquals(null, service.detail("7", 900L, "FBR-1").items().get(0).sourcePalletCode());
}
@Test
void candidatesUseCustomerNameOnlyFromMatchingCustomerOrder() {
var orders = org.mockito.Mockito.mock(com.baiyue.lastleg.mapper.LastlegOrderMapper.class);
@@ -65,8 +65,9 @@ class ReturnCartonCountServiceTest {
when(cartons.lockReceipt(1L)).thenReturn(List.of(a,b));
when(receiptItems.listReceipt(1L)).thenReturn(expected);
}
private CountBox measuredBox(String mark, List<CountItem> lines) { return new CountBox(mark, lines, java.math.BigDecimal.ONE, java.math.BigDecimal.ONE, java.math.BigDecimal.ONE, java.math.BigDecimal.ONE); }
private Confirm unequal(long first, long second) {
return new Confirm("retry", List.of(new CountBox(a.getCartonMark(),List.of(new CountItem("SKU1",first))),new CountBox(b.getCartonMark(),List.of(new CountItem("SKU1",second)))));
return new Confirm("retry", List.of(measuredBox(a.getCartonMark(),List.of(new CountItem("SKU1",first))),measuredBox(b.getCartonMark(),List.of(new CountItem("SKU1",second)))));
}
@Test void unequalBoxesAreConfirmedWithoutChangingSkuUnitBalanceAndRetryIsStable() {
prepare(List.of(expected("SKU1",10)));
@@ -97,7 +98,7 @@ class ReturnCartonCountServiceTest {
when(items.lockCarton(3L)).thenReturn(List.of(provisional(3,"SKU2",4)));
when(balances.adjustCartonQuantity("C1","123","SKU1",1)).thenReturn(1);
when(balances.adjustCartonQuantity("C1","123","SKU2",1)).thenReturn(1);
var request = new Confirm("mixed",List.of(new CountBox(a.getCartonMark(),List.of(new CountItem("SKU1",2),new CountItem("SKU2",1))),new CountBox(b.getCartonMark(),List.of(new CountItem("SKU1",4),new CountItem("SKU2",3)))));
var request = new Confirm("mixed",List.of(measuredBox(a.getCartonMark(),List.of(new CountItem("SKU1",2),new CountItem("SKU2",1))),measuredBox(b.getCartonMark(),List.of(new CountItem("SKU1",4),new CountItem("SKU2",3)))));
service.confirm(identity,"RR-1",request);
verify(balances).adjustCartonQuantity("C1","123","SKU1",1); verify(balances).adjustCartonQuantity("C1","123","SKU2",1);
verifyNoMoreInteractions(balances); assertThat(a.getSkuTypes()).isEqualTo(2);
@@ -110,7 +111,7 @@ class ReturnCartonCountServiceTest {
}
@Test void duplicateOrForeignBoxesCannotBeSubmitted() {
prepare(List.of(expected("SKU1",10)));
var request = new Confirm("dup",List.of(new CountBox(a.getCartonMark(),List.of(new CountItem("SKU1",5))),new CountBox(a.getCartonMark(),List.of(new CountItem("SKU1",5)))));
var request = new Confirm("dup",List.of(measuredBox(a.getCartonMark(),List.of(new CountItem("SKU1",5))),measuredBox(a.getCartonMark(),List.of(new CountItem("SKU1",5)))));
assertThatThrownBy(() -> service.confirm(identity,"RR-1",request)).isInstanceOf(ResponseStatusException.class).hasMessageContaining("重复");
verify(items,never()).deleteCarton(anyLong());
}
@@ -454,7 +454,7 @@ class ReturnReceiptServiceTest {
stubNewReceipt(order, List.of(line), "SP-SOURCE");
var request = new PdaReturnReceiptRequests.Confirm("RT-1", null, "SKU", null,
List.of(new PdaReturnReceiptRequests.Item("SKU-A", 1)), "parcel-key",
"PARCEL", null, null, null, null, null, null);
"PARCEL", null, java.math.BigDecimal.ONE, java.math.BigDecimal.ONE, java.math.BigDecimal.ONE, java.math.BigDecimal.ONE, null, true);
var result = service.confirm(identity, request);
var capture = ArgumentCaptor.forClass(ReturnReceipt.class);
verify(receipts).insert(capture.capture());
@@ -611,6 +611,6 @@ class ReturnReceiptServiceTest {
// These focused tests exercise receipt/idempotency behavior at the
// carton level. Pallet receipts have a separate multi-carton contract.
return new PdaReturnReceiptRequests.Confirm("RT-1", trackingNo, startedBy, palletCode, boxed, key,
"BOX", palletCode, null, null, null, null, 1L);
"BOX", palletCode, java.math.BigDecimal.ONE, java.math.BigDecimal.ONE, java.math.BigDecimal.ONE, java.math.BigDecimal.ONE, 1L, true);
}
}
@@ -47,6 +47,9 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.NullSource;
import org.junit.jupiter.params.provider.ValueSource;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
@@ -438,20 +441,65 @@ class WarehouseTransferServiceTest {
verifyNoInteractions(transfers);
}
@Test
void transferRejectsRequestWithoutDestination() {
@ParameterizedTest
@NullSource
@ValueSource(strings = {"NONE"})
void transferWithoutDestinationCanBeCreatedAndSubmitted(String destinationType) {
WarehouseTransferRequests.Create request = new WarehouseTransferRequests.Create(
"IN-1", List.of(11L), "WH-1", null, "123 Pickup Road", 5,
"IN-1", List.of(11L), "WH-1", null, destinationType, null, null, null,
null, null, null, null, "123 Pickup Road", null, null, 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")));
null, null, null, null, "Destination will be supplied before outbound", false,
null, null, null, null, null, "key-no-fba",
List.of(new WarehouseTransferRequests.Item("SKU-1", "Product", 5, "MARK-1")), null);
ResponseStatusException exception = assertThrows(ResponseStatusException.class,
() -> service.create("TENANT-A", "USER-1", request));
when(inboundSnapshots.findId("TENANT-A", "IN-1")).thenReturn(101L);
when(orders.selectList(any())).thenReturn(List.of(historicalOrder()));
WarehouseTransferOrder draft = transfer(WarehouseTransferService.DRAFT);
draft.setFbaWarehouseCode(null);
when(transfers.findNo(eq("TENANT-A"), anyString())).thenReturn(draft);
when(transfers.lockNo("TENANT-A", "WTR-1")).thenReturn(draft);
when(orders.selectById(draft.getLastlegOrderId())).thenReturn(tailOrder());
when(transferItems.selectList(any())).thenReturn(List.of());
when(sourceOrders.selectList(any())).thenReturn(List.of());
assertEquals(400, exception.getStatusCode().value());
assertEquals("仓储转运必须选择 FBA 仓或私人仓", exception.getReason());
verifyNoInteractions(transfers);
service.create("TENANT-A", "USER-1", request);
var result = service.submit("TENANT-A", "WTR-1", "USER-1");
ArgumentCaptor<WarehouseTransferOrder> created = ArgumentCaptor.forClass(WarehouseTransferOrder.class);
verify(transfers).insert(created.capture());
assertNull(created.getValue().getDestinationType());
assertNull(created.getValue().getFbaWarehouseCode());
assertEquals(WarehouseTransferService.PENDING_REVIEW, result.status());
verify(relabelApplications, never()).insert(any(RelabelApplication.class));
}
@Test
void merchantCanClearDestinationWhenEditingDraft() {
WarehouseTransferOrder transfer = transfer(WarehouseTransferService.DRAFT);
transfer.setDestinationType("FBA");
transfer.setDestinationWarehouseCode("ONT8");
LastlegOrder order = tailOrder();
when(transfers.lockNo("TENANT-A", "WTR-1")).thenReturn(transfer);
when(transfers.findNo("TENANT-A", "WTR-1")).thenReturn(transfer);
when(orders.selectById(transfer.getLastlegOrderId())).thenReturn(order);
when(transferItems.selectList(any())).thenReturn(List.of());
when(sourceOrders.selectList(any())).thenReturn(List.of());
WarehouseTransferRequests.Create request = new WarehouseTransferRequests.Create(
"IN-1", List.of(11L), "WH-1", null, "NONE", null, null, null,
null, null, null, null, "123 Pickup Road", null, null, 5,
new BigDecimal("0.2500"), "WAREHOUSE_PICKUP", LocalDateTime.of(2026, 9, 12, 10, 0),
null, null, null, null, null, false, null, null, null, null, null, "edit-no-fba",
List.of(new WarehouseTransferRequests.Item("SKU-1", "Product", 5, "MARK-1")), null);
var result = service.updateDraft("TENANT-A", "WTR-1", "USER-1", request);
assertEquals(WarehouseTransferService.DRAFT, result.status());
assertNull(transfer.getDestinationType());
assertNull(transfer.getDestinationWarehouseCode());
assertNull(transfer.getFbaWarehouseCode());
assertNull(order.getFbaWarehouseCode());
verify(transfers).updateById(transfer);
}
@Test