Merge remote-tracking branch 'origin/test' into test
This commit is contained in:
+32
@@ -236,6 +236,38 @@ class DeliveryTransferServiceTest {
|
||||
assertEquals(1, delivery.getValue().getPackageCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
void partialSkuOutboundDistributesSixPiecesAcrossTwoFivePieceSources() {
|
||||
var first = returnCarton("RC-1", null); first.setId(901L); first.setTotalQuantity(5L);
|
||||
var second = returnCarton("RC-2", null); second.setId(902L); second.setTotalQuantity(5L);
|
||||
when(returnCartons.lockMark("RC-1")).thenReturn(first); when(returnCartons.lockMark("RC-2")).thenReturn(second);
|
||||
var line = new ReturnInventoryCartonItem(); line.setSkuCode("SKU-A"); line.setQuantity(5L);
|
||||
when(returnCartonItems.lockCarton(901L)).thenReturn(List.of(line));
|
||||
when(returnCartonItems.lockCarton(902L)).thenReturn(List.of(line));
|
||||
service.createReturnOutbound("TENANT-A", "U-1", new DeliveryRequests.ReturnOutbound("receiver", "WH-1", "RT-1",
|
||||
List.of("RC-1", "RC-2"), null, "partial-six", "SKU-A", "BOX", 6L));
|
||||
var capture = ArgumentCaptor.forClass(OutboundPalletItem.class);
|
||||
verify(outboundPallets, org.mockito.Mockito.times(2)).insert(capture.capture());
|
||||
assertEquals(List.of(5L, 1L), capture.getAllValues().stream().map(OutboundPalletItem::getReturnSkuQuantity).toList());
|
||||
}
|
||||
|
||||
@Test
|
||||
void singleSkuReturnOutboundAcceptsPackedSingleSkuButKeepsCartonScanning() {
|
||||
ReturnInventoryCarton carton = returnCarton("RC-BOX", "PAL-1");
|
||||
carton.setSourceUnitType("BOX"); carton.setTotalQuantity(5L);
|
||||
ReturnInventoryCartonItem line = new ReturnInventoryCartonItem();
|
||||
line.setSkuCode("SKU-A"); line.setQuantity(5L);
|
||||
when(returnCartons.lockMark("RC-BOX")).thenReturn(carton);
|
||||
when(returnCartonItems.lockCarton(carton.getId())).thenReturn(List.of(line));
|
||||
service.createReturnOutbound("TENANT-A", "U-1",
|
||||
new DeliveryRequests.ReturnOutbound("receiver", "WH-1", "RT-1", List.of("RC-BOX"), null, "packed-sku", "SKU-A"));
|
||||
ArgumentCaptor<OutboundPalletItem> item = ArgumentCaptor.forClass(OutboundPalletItem.class);
|
||||
verify(outboundPallets).insert(item.capture());
|
||||
assertNull(item.getValue().getSkuCode());
|
||||
assertEquals("RC-BOX", item.getValue().getCartonMarks());
|
||||
assertEquals("OUTBOUND_PENDING", carton.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
void singleSkuReturnOutboundRejectsMixedCarton() {
|
||||
ReturnInventoryCarton mixed = returnCarton("RC-MIXED", null);
|
||||
|
||||
+11
-2
@@ -71,9 +71,18 @@ class ReturnOutboundClosureTest {
|
||||
verify(outbounds, times(1)).insert(any(OutboundOrder.class));
|
||||
}
|
||||
|
||||
@Test void preventsSingleSkuBypassingExistingBox() {
|
||||
@Test void singleSkuPackedBoxKeepsCartonScanAndPieceSnapshot() {
|
||||
service.createReturnOutbound("C1", "USER", command("SKU1"));
|
||||
verify(items).insert(argThat((OutboundPalletItem i) -> i.getSkuCode() == null && Long.valueOf(6L).equals(i.getReturnSkuQuantity())));
|
||||
assertThat(carton.getStatus()).isEqualTo("OUTBOUND_PENDING");
|
||||
}
|
||||
|
||||
@Test void singleSkuCannotConsumeMixedSkuCarton() {
|
||||
ReturnInventoryCartonItem other = new ReturnInventoryCartonItem(); other.setSkuCode("SKU2"); other.setQuantity(1L);
|
||||
var first = contents.lockCarton(10L).get(0);
|
||||
when(contents.lockCarton(10L)).thenReturn(List.of(first, other));
|
||||
assertThatThrownBy(() -> service.createReturnOutbound("C1", "USER", command("SKU1")))
|
||||
.isInstanceOf(ResponseStatusException.class).hasMessageContaining("按箱唛");
|
||||
.isInstanceOf(ResponseStatusException.class).hasMessageContaining("SKU");
|
||||
verify(outbounds, never()).insert(any(OutboundOrder.class));
|
||||
}
|
||||
|
||||
|
||||
+18
@@ -82,6 +82,24 @@ class PdaRelabelServiceTest {
|
||||
lenient().when(tasks.updateWithVersion(any(), anyInt())).thenReturn(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void candidatesUseCustomerNameOnlyFromMatchingCustomerOrder() {
|
||||
var orders = org.mockito.Mockito.mock(com.baiyue.lastleg.mapper.LastlegOrderMapper.class);
|
||||
var directory = org.mockito.Mockito.mock(com.baiyue.lastleg.fleet.customer.FleetCustomerDirectory.class);
|
||||
ReflectionTestUtils.setField(service, "customerDirectory", directory);
|
||||
ReflectionTestUtils.setField(service, "tailOrders", orders);
|
||||
application.setLastlegOrderId(20L);
|
||||
var order = new com.baiyue.lastleg.entity.LastlegOrder();
|
||||
order.setId(20L); order.setCustomerId("100"); order.setCustomerName("测试客户");
|
||||
when(applications.selectList(any())).thenReturn(List.of(application));
|
||||
when(orders.selectBatchIds(any())).thenReturn(List.of(order));
|
||||
assertEquals("测试客户", service.candidates("7", 900L).get(0).customerName());
|
||||
order.setCustomerId("OTHER");
|
||||
when(directory.find("100")).thenReturn(new com.baiyue.lastleg.fleet.customer.FleetCustomerDirectory.Customer("100", "C100", "所属公司", null, null));
|
||||
assertEquals("所属公司", service.candidates("7", 900L).get(0).customerName());
|
||||
verify(orders, org.mockito.Mockito.times(2)).selectBatchIds(List.of(20L));
|
||||
}
|
||||
|
||||
@Test
|
||||
void claimUsesFrozenWarehouseAndAdvancesExistingApplicationStateMachine() {
|
||||
when(applications.lockAnyByRelabelNo("FBR-1")).thenReturn(application);
|
||||
|
||||
+15
@@ -54,6 +54,7 @@ import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.anyLong;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.ArgumentMatchers.isNull;
|
||||
@@ -643,6 +644,18 @@ class RelabelApplicationServiceTest {
|
||||
assertEquals(6L, move.getValue().packages());
|
||||
}
|
||||
|
||||
@Test
|
||||
void pdaCompletionSettlesBeforeCustomerConfirmationAndDoesNotSettleTwice() {
|
||||
var app = confirmationApplication("TRUCK", false);
|
||||
when(applications.lockNo("10001", "FBR-1")).thenReturn(app);
|
||||
when(orders.selectById(21L)).thenReturn(order(21L, "10001", "LMO-1", "WAIT_RELABEL"));
|
||||
service.settlePdaCompletion("10001", "FBR-1", "picker");
|
||||
service.settlePdaCompletion("10001", "FBR-1", "picker");
|
||||
verify(walletCommands).publish(eq("SETTLE"), eq(10001L), eq("RELABEL_ORDER"), eq("FBR-1"), isNull(), anyString(), eq(2L));
|
||||
assertEquals("SETTLE_PENDING", app.getWalletStatus());
|
||||
assertNull(app.getCustomerConfirmedAt());
|
||||
}
|
||||
|
||||
@Test
|
||||
void truckCustomerConfirmationIsIdempotentAndWaitsForPreDispatch() {
|
||||
RelabelApplication application = confirmationApplication("TRUCK", false);
|
||||
@@ -662,6 +675,7 @@ class RelabelApplicationServiceTest {
|
||||
assertEquals("CONFIRMED", review.getValue().getReviewDecision());
|
||||
assertEquals("WAIT_OUTBOUND", linked.getCurrentStatus());
|
||||
assertNull(application.getOutboundOrderNo());
|
||||
verify(walletCommands, never()).publish(eq("FREEZE"), anyLong(), anyString(), anyString(), any(), anyString(), anyLong());
|
||||
verify(outboundOrders, never()).insert(any(OutboundOrder.class));
|
||||
|
||||
service.confirmCompletion("10001", "FBR-1", "customer-1",
|
||||
@@ -838,6 +852,7 @@ class RelabelApplicationServiceTest {
|
||||
RelabelApplication application = application(11L, "FBR-1", "10001", "key-1", "hash",
|
||||
RelabelApplicationService.CUSTOMER_CONFIRM_PENDING);
|
||||
application.setLastlegOrderId(21L);
|
||||
application.setOverseasWarehouseId("900");
|
||||
application.setDeliveryMethod(deliveryMethod);
|
||||
application.setPrintShippingLabel(printShippingLabel);
|
||||
return application;
|
||||
|
||||
+38
-2
@@ -30,6 +30,7 @@ import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.anyLong;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
@@ -75,6 +76,30 @@ class ReturnInventoryServiceTest {
|
||||
ReflectionTestUtils.setField(service, "objectMapper", new com.fasterxml.jackson.databind.ObjectMapper().findAndRegisterModules());
|
||||
}
|
||||
|
||||
@Test
|
||||
void partialSkuShipmentKeepsOriginalCartonAndDoesNotSubtractCartonCount() {
|
||||
var carton = new ReturnInventoryCarton(); carton.setId(31L); carton.setCartonMark("RC-1");
|
||||
carton.setCustomerId("10001"); carton.setOverseasWarehouseId("42"); carton.setSourceReturnNo("RT-1");
|
||||
carton.setStatus("OUTBOUND_PENDING"); carton.setCountStatus("CONFIRMED"); carton.setSkuTypes(1); carton.setTotalQuantity(5L);
|
||||
var line = item(32L, "SKU-A", 5);
|
||||
var balance = new com.baiyue.lastleg.returnreceipt.entity.ReturnInventoryBalance();
|
||||
balance.setId(41L); balance.setTotalQuantity(5L); balance.setAvailableQuantity(5L); balance.setCartonQuantity(1L);
|
||||
when(cartons.lockMark("RC-1")).thenReturn(carton);
|
||||
when(cartonItems.lockCarton(31L)).thenReturn(List.of(line));
|
||||
when(balances.lockSku("10001", "42", "SKU-A")).thenReturn(balance);
|
||||
when(balances.deductOutboundPieces(41L, "10001", "42", 2L)).thenReturn(1);
|
||||
when(movements.insert(any(com.baiyue.lastleg.returnreceipt.entity.ReturnInventoryOutboundMovement.class))).thenReturn(1);
|
||||
when(cartonItems.updateById(line)).thenReturn(1); when(cartons.updateById(carton)).thenReturn(1);
|
||||
service.shipCartonQuantity("10001", "42", "RT-1", "RC-1", "OB-1", 51L, 2L);
|
||||
assertThat(carton.getStatus()).isEqualTo("AVAILABLE"); assertThat(carton.getTotalQuantity()).isEqualTo(3L);
|
||||
assertThat(line.getQuantity()).isEqualTo(3L);
|
||||
var capture = org.mockito.ArgumentCaptor.forClass(com.baiyue.lastleg.returnreceipt.entity.ReturnInventoryOutboundMovement.class);
|
||||
verify(movements).insert(capture.capture());
|
||||
assertThat(capture.getValue().getQuantity()).isEqualTo(2L);
|
||||
assertThat(capture.getValue().getCartonsAfter()).isEqualTo(1L);
|
||||
verify(balances, never()).deductOutbound(anyLong(), anyString(), anyString(), anyLong());
|
||||
}
|
||||
|
||||
@Test
|
||||
void platformPlanNumberFilterIsTrimmedAndUsedForCountAndPage() {
|
||||
when(packingPlans.countPlatform(null, "PLAN-1", "C1", "W1", "SKU1")).thenReturn(1L);
|
||||
@@ -124,6 +149,17 @@ class ReturnInventoryServiceTest {
|
||||
assertThat(capture.getValue().getResultJson()).contains("RC-BOX", "RR-BOX", "商品A", "\"quantity\":3", "\"quantity\":7");
|
||||
}
|
||||
|
||||
@Test
|
||||
void parcelSigningStoresLooseSkuSourceWithoutGeneratingCartonNumberOrIncreasingBoxCount() {
|
||||
var receipt = new ReturnReceipt(); receipt.setId(21L); receipt.setUnitType("PARCEL"); receipt.setUnitCode("PK-1");
|
||||
var source = service.createReceiptCarton(completedOrder(), receipt, null,
|
||||
List.of(new ReturnInventoryService.ReceiptLine("A", "商品A", 3)));
|
||||
assertThat(source.getCartonMark()).startsWith("SP");
|
||||
assertThat(source.getSourceUnitType()).isEqualTo("PARCEL");
|
||||
verifyNoInteractions(numbers, operations);
|
||||
verify(balances).adjust(anyLong(), anyString(), anyString(), eq("A"), eq("商品A"), eq(3L), eq(3L), eq(0L));
|
||||
}
|
||||
|
||||
@Test
|
||||
void cartonDetailsExposeImmutableMergeHistoryWithinCustomerScope() {
|
||||
ReturnInventoryCarton carton = new ReturnInventoryCarton();
|
||||
@@ -320,7 +356,7 @@ class ReturnInventoryServiceTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void fullyConsumedSourceIsMergedAndSkuCartonCountRemainsOne() {
|
||||
void fullyConsumedParcelSourceIsMergedAndCreatesOnePhysicalCarton() {
|
||||
ReturnInventoryCarton locked = prepareConcurrentSource();
|
||||
when(cartonItems.lockCarton(31L)).thenReturn(List.of(item(41L, "SKU-A", 2L)));
|
||||
when(numbers.next(anyString(), anyString(), anyLong())).thenReturn("RC-TARGET");
|
||||
@@ -330,7 +366,7 @@ class ReturnInventoryServiceTest {
|
||||
assertThat(locked.getSkuTypes()).isZero();
|
||||
assertThat(locked.getStatus()).isEqualTo(ReturnInventoryService.MERGED);
|
||||
verify(cartonItems).deleteById(41L);
|
||||
verify(balances).adjustCartonQuantity("10001", "42", "SKU-A", 0L);
|
||||
verify(balances).adjustCartonQuantity("10001", "42", "SKU-A", 1L);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+24
@@ -433,6 +433,30 @@ class ReturnReceiptServiceTest {
|
||||
verify(receipts, times(1)).insert(any(ReturnReceipt.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void parcelWithoutTrackingGeneratesParcelCodeAndNeverReturnsBoxLabelsOnReplay() {
|
||||
ReturnOrder order = order("RT-1", "10", ReturnReceiptService.PENDING_RECEIPT, 3, 0);
|
||||
ReturnOrderItem line = item(11L, "SKU-A", 3, 0);
|
||||
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);
|
||||
var result = service.confirm(identity, request);
|
||||
var capture = ArgumentCaptor.forClass(ReturnReceipt.class);
|
||||
verify(receipts).insert(capture.capture());
|
||||
assertThat(result.unitCode()).startsWith("PK");
|
||||
assertThat(result.cartonMark()).isNull();
|
||||
assertThat(result.printableHtml()).isNull();
|
||||
assertThat(result.labels()).isEmpty();
|
||||
assertThat(capture.getValue().getTrackingNo()).isNull();
|
||||
when(receipts.lockKey("operator-1", "parcel-key")).thenReturn(capture.getValue());
|
||||
var replay = service.confirm(identity, request);
|
||||
assertThat(replay.unitCode()).isEqualTo(result.unitCode());
|
||||
assertThat(replay.labels()).isEmpty();
|
||||
verify(inventory, never()).label(anyString(), anyString());
|
||||
verify(inventory, times(1)).createReceiptCarton(any(), any(), any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void rejectsOverReceiptBeforeCreatingReceiptInventoryOrBatch() {
|
||||
ReturnOrder order = order("RT-1", "10", ReturnReceiptService.PARTIALLY_RECEIVED, 2, 1);
|
||||
|
||||
Reference in New Issue
Block a user