贴标申请,仓储转运
This commit is contained in:
@@ -44,6 +44,12 @@ class RelabelContractTest {
|
||||
String migration = Files.readString(Path.of("..", "scripts", "db", "incremental.sql"), StandardCharsets.UTF_8).toLowerCase();
|
||||
String deployment = Files.readString(Path.of("..", "deploy", "sql", "海外区域统一增量-MySQL8.sql"),
|
||||
StandardCharsets.UTF_8).toLowerCase();
|
||||
for (String schema : new String[] { baseline, deployment }) {
|
||||
assertTrue(schema.contains("key idx_ll_relabel_application_order (lastleg_order_id)"));
|
||||
assertFalse(schema.contains("unique key uk_ll_relabel_application_order"),
|
||||
"One received return order must support multiple relabel applications for remaining stock");
|
||||
assertTrue(schema.contains("unique key uk_ll_relabel_application_idempotency"));
|
||||
}
|
||||
for (String schema : new String[] { baseline, migration }) {
|
||||
assertTrue(schema.contains("ll_relabel_application"));
|
||||
assertTrue(schema.contains("ll_relabel_item"));
|
||||
|
||||
+29
-2
@@ -466,12 +466,13 @@ class RelabelApplicationServiceTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void completedReturnRelabelOrderEntersPendingAssignment() {
|
||||
void completedReturnRelabelKeepsParentInboundCompleted() {
|
||||
RelabelApplication application = application(11L, "FBR-1", "10001", "key-1", "hash", RelabelApplicationService.READY_FOR_OUTBOUND);
|
||||
application.setLastlegOrderId(21L);
|
||||
application.setFulfillmentStatus("PRE_DISPATCH_CREATED");
|
||||
LastlegOrder order = order(21L, "10001", "LMO-RELABEL-1", "PROCESSING");
|
||||
order.setOrderType("RETURN_RELABEL");
|
||||
order.setRelabelOrderNo("FBR-1");
|
||||
when(applications.lockNo("10001", "FBR-1")).thenReturn(application);
|
||||
stubDetail(application);
|
||||
when(orders.selectById(21L)).thenReturn(order);
|
||||
@@ -481,10 +482,32 @@ class RelabelApplicationServiceTest {
|
||||
|
||||
assertEquals("COMPLETED", application.getStatus());
|
||||
assertEquals("OUTBOUNDED", application.getFulfillmentStatus());
|
||||
assertEquals("PENDING_ASSIGNMENT", order.getCurrentStatus());
|
||||
assertEquals("INBOUND_COMPLETED", order.getCurrentStatus());
|
||||
assertEquals("COMPLETED", order.getRelabelStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
void completingEarlierReturnRelabelDoesNotOverwriteLaterApplicationStatus() {
|
||||
RelabelApplication application = application(11L, "FBR-FIRST", "10001", "key-1", "hash", RelabelApplicationService.READY_FOR_OUTBOUND);
|
||||
application.setLastlegOrderId(21L);
|
||||
application.setFulfillmentStatus("PRE_DISPATCH_CREATED");
|
||||
LastlegOrder order = order(21L, "10001", "LMO-RELABEL-1", "INBOUND_COMPLETED");
|
||||
order.setOrderType("RETURN_RELABEL");
|
||||
order.setRelabelOrderNo("FBR-SECOND");
|
||||
order.setRelabelStatus("PROCESSING");
|
||||
when(applications.lockNo("10001", "FBR-FIRST")).thenReturn(application);
|
||||
stubDetail(application);
|
||||
when(orders.selectById(21L)).thenReturn(order);
|
||||
|
||||
service.changeStatus("10001", "FBR-FIRST", "operator-2",
|
||||
new RelabelRequests.ChangeStatus("COMPLETED", "finished"));
|
||||
|
||||
assertEquals("COMPLETED", application.getStatus());
|
||||
assertEquals("INBOUND_COMPLETED", order.getCurrentStatus());
|
||||
assertEquals("PROCESSING", order.getRelabelStatus());
|
||||
assertEquals("FBR-SECOND", order.getRelabelOrderNo());
|
||||
}
|
||||
|
||||
@Test
|
||||
void completingPartialRelabelReleasesOnlyNotifiedPackages() {
|
||||
RelabelApplication application = application(11L, "FBR-PARTIAL", "10001", "key-partial", "hash", RelabelApplicationService.READY_FOR_OUTBOUND);
|
||||
@@ -508,8 +531,10 @@ class RelabelApplicationServiceTest {
|
||||
@Test
|
||||
void truckCustomerConfirmationIsIdempotentAndWaitsForPreDispatch() {
|
||||
RelabelApplication application = confirmationApplication("TRUCK", false);
|
||||
LastlegOrder linked = order(21L, "10001", "LMO-RELABEL-1", "WAIT_RELABEL");
|
||||
when(applications.lockNo("10001", "FBR-1")).thenReturn(application);
|
||||
stubDetail(application);
|
||||
when(orders.selectById(21L)).thenReturn(linked);
|
||||
|
||||
service.confirmCompletion("10001", "FBR-1", "customer-1",
|
||||
new RelabelRequests.CustomerConfirm("confirm-1", "photos accepted"));
|
||||
@@ -520,6 +545,7 @@ class RelabelApplicationServiceTest {
|
||||
verify(pdaTasks).updateWithVersion(review.capture(), eq(1));
|
||||
assertEquals("COMPLETED", review.getValue().getStatus());
|
||||
assertEquals("CONFIRMED", review.getValue().getReviewDecision());
|
||||
assertEquals("WAIT_OUTBOUND", linked.getCurrentStatus());
|
||||
assertNull(application.getOutboundOrderNo());
|
||||
verify(outboundOrders, never()).insert(any(OutboundOrder.class));
|
||||
|
||||
@@ -555,6 +581,7 @@ class RelabelApplicationServiceTest {
|
||||
@Test
|
||||
void transferRelabelConfirmationReleasesTransferForDispatch() {
|
||||
RelabelApplication application = confirmationApplication("TRUCK", false);
|
||||
application.setLastlegOrderId(88L);
|
||||
WarehouseTransferOrder transfer = new WarehouseTransferOrder();
|
||||
transfer.setId(77L); transfer.setCustomerId("10001"); transfer.setRelabelNo("FBR-1");
|
||||
transfer.setStatus("WAIT_RELABEL"); transfer.setLastlegOrderId(88L); transfer.setFbaWarehouseCode("ONT8");
|
||||
|
||||
+3
-2
@@ -38,6 +38,7 @@ class RelabelReturnCartonCandidateTest {
|
||||
cartonItem.setProductNameZh("退货商品"); cartonItem.setQuantity(12L);
|
||||
when(cartons.findAvailable("10001", "WH-1")).thenReturn(List.of(carton));
|
||||
when(cartonItems.listCarton(9001L)).thenReturn(List.of(cartonItem));
|
||||
when(cartons.relabelledQuantity(9001L, "SKU-RETURN")).thenReturn(1L);
|
||||
|
||||
List<RelabelResponses.InventoryCandidate> result = service.candidates("10001", "WH-1", "RC-WH");
|
||||
|
||||
@@ -45,8 +46,8 @@ class RelabelReturnCartonCandidateTest {
|
||||
assertEquals("RETURN_CARTON", result.get(0).sourceType());
|
||||
assertEquals(9001L, result.get(0).returnCartonId());
|
||||
assertEquals("RC-WH-0001", result.get(0).cartonMark());
|
||||
assertEquals(12, result.get(0).availablePackages());
|
||||
assertEquals(11, result.get(0).availablePackages());
|
||||
assertEquals("SKU-RETURN", result.get(0).skuItems().get(0).skuCode());
|
||||
assertEquals(12, result.get(0).skuItems().get(0).quantity());
|
||||
assertEquals(11, result.get(0).skuItems().get(0).quantity());
|
||||
}
|
||||
}
|
||||
|
||||
+71
-1
@@ -93,6 +93,22 @@ class ReturnReceiptServiceTest {
|
||||
identity = new PdaWarehouseIdentity("operator-1", "仓管员", 10L);
|
||||
}
|
||||
|
||||
@Test
|
||||
void detailKeepsEveryRelabelApplicationAndItsOwnStatus() {
|
||||
ReturnOrder order = order("RT-MULTI", "10", "COMPLETED", 90, 90);
|
||||
order.setLastlegOrderId(99L);
|
||||
when(orders.findNo("customer-1", "RT-MULTI")).thenReturn(order);
|
||||
var completed = new com.baiyue.lastleg.dto.RelabelSubOrder(99L, "RL-FIRST", "COMPLETED", 10, null);
|
||||
var processing = new com.baiyue.lastleg.dto.RelabelSubOrder(99L, "RL-SECOND", "PROCESSING", 5, null);
|
||||
when(lastlegOrders.findRelabelSubOrders(List.of(99L))).thenReturn(List.of(completed, processing));
|
||||
|
||||
var result = service.detail("customer-1", "RT-MULTI").order();
|
||||
|
||||
assertEquals("COMPLETED", result.status());
|
||||
assertEquals(List.of(completed, processing), result.relabelApplications());
|
||||
assertEquals(90L, result.receivedQuantity());
|
||||
}
|
||||
|
||||
@Test
|
||||
void merchantPhotoDownloadUsesAuthenticatedCustomerAndResolvedReturnOrder() {
|
||||
ReflectionTestUtils.setField(service, "photoFiles", photoFiles);
|
||||
@@ -128,12 +144,26 @@ class ReturnReceiptServiceTest {
|
||||
}
|
||||
|
||||
private void verifyMultiSkuRelabel(long firstQuantity, long secondQuantity) {
|
||||
verifyMultiSkuRelabel(firstQuantity, secondQuantity, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
void previouslyRelabelledReturnCanLabelItsRemainingAvailableGoods() {
|
||||
verifyMultiSkuRelabel(10, 5, true);
|
||||
}
|
||||
|
||||
private void verifyMultiSkuRelabel(long firstQuantity, long secondQuantity, boolean previousRelabel) {
|
||||
ReturnOrder order = order("RT-MULTI", "10", "COMPLETED", 90, 90);
|
||||
if (previousRelabel) {
|
||||
order.setRelabelApplicationId(88L);
|
||||
order.setRelabelNo("RL-PREVIOUS");
|
||||
order.setRelabelStatus("COMPLETED");
|
||||
}
|
||||
order.setLastlegOrderId(99L);
|
||||
LastlegOrder tail = new LastlegOrder(); tail.setId(99L);
|
||||
ReturnInventoryCarton first = returnCarton(101L, "RC-A", "P-1", 50);
|
||||
ReturnInventoryCarton second = returnCarton(102L, "RC-B", "P-2", 40);
|
||||
when(orders.findNo("customer-1", "RT-MULTI")).thenReturn(order);
|
||||
when(orders.lockCustomerNo("customer-1", "RT-MULTI")).thenReturn(order);
|
||||
when(orderItems.lockOrder(1L)).thenReturn(List.of(item(11L, "SKU-A", 50, 50), item(12L, "SKU-B", 40, 40)));
|
||||
when(fbaWarehouseValidator.required("200", "XLX6", null)).thenReturn(
|
||||
new FbaWarehouseValidator.VerifiedFbaWarehouse(200L, "XLX6", "FBA XLX6", "US", "CA"));
|
||||
@@ -152,6 +182,7 @@ class ReturnReceiptServiceTest {
|
||||
"SKU_TOTAL", "200", "XLX6", "multi-key", "multi SKU acceptance"));
|
||||
|
||||
assertEquals("RL-MULTI", result.relabelNo());
|
||||
assertEquals("RL-MULTI", order.getRelabelNo());
|
||||
var application = ArgumentCaptor.forClass(RelabelApplication.class);
|
||||
verify(relabelApplications).insert(application.capture());
|
||||
assertEquals(firstQuantity + secondQuantity, application.getValue().getNotifiedPackageCount().longValue());
|
||||
@@ -174,6 +205,45 @@ class ReturnReceiptServiceTest {
|
||||
return carton;
|
||||
}
|
||||
|
||||
@Test
|
||||
void secondRelabelCannotAllocateAlreadyLabelledQuantity() {
|
||||
ReturnOrder order = order("RT-MULTI", "10", "COMPLETED", 100, 100);
|
||||
order.setLastlegOrderId(99L); order.setRelabelNo("RL-PREVIOUS");
|
||||
LastlegOrder tail = new LastlegOrder(); tail.setId(99L);
|
||||
ReturnInventoryCarton carton = returnCarton(101L, "RC-A", "P-1", 100);
|
||||
when(orders.lockCustomerNo("customer-1", "RT-MULTI")).thenReturn(order);
|
||||
when(orderItems.lockOrder(1L)).thenReturn(List.of(item(11L, "SKU-A", 100, 100)));
|
||||
when(fbaWarehouseValidator.required("200", "XLX6", null)).thenReturn(
|
||||
new FbaWarehouseValidator.VerifiedFbaWarehouse(200L, "XLX6", "FBA XLX6", "US", "CA"));
|
||||
when(numbers.next(eq("RELABEL"), eq("10"), anyLong())).thenReturn("RL-SECOND");
|
||||
when(lastlegOrders.selectById(99L)).thenReturn(tail);
|
||||
when(cartons.findAvailable("customer-1", "10")).thenReturn(List.of(carton));
|
||||
when(cartonItems.listCarton(101L)).thenReturn(List.of(returnCartonItem("SKU-A", 100)));
|
||||
when(cartons.relabelledQuantity(101L, "SKU-A")).thenReturn(100L);
|
||||
var error = assertThrows(ResponseStatusException.class, () -> service.createRelabel(
|
||||
"customer-1", "merchant-1", "RT-MULTI", new ReturnOrderRequests.RelabelCreate(
|
||||
List.of(new ReturnOrderRequests.RelabelItem("SKU-A", 1)),
|
||||
"SKU_TOTAL", "200", "XLX6", "second-key", "second attempt")));
|
||||
assertEquals(409, error.getStatusCode().value());
|
||||
assertTrue(error.getReason().contains("可用实收数量不足"));
|
||||
verify(relabelItems, never()).insert(any(RelabelItem.class));
|
||||
verify(cartons, never()).updateById(any(ReturnInventoryCarton.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void relabelReplayRejectsKeyFromAnotherReturnOrder() {
|
||||
ReturnOrder order = order("RT-MULTI", "10", "COMPLETED", 100, 100);
|
||||
when(orders.lockCustomerNo("customer-1", "RT-MULTI")).thenReturn(order);
|
||||
RelabelApplication old = new RelabelApplication(); old.setCustomerOrderNo("RT-OTHER");
|
||||
when(relabelApplications.findKey("customer-1", "used-key")).thenReturn(old);
|
||||
var error = assertThrows(ResponseStatusException.class, () -> service.createRelabel(
|
||||
"customer-1", "merchant-1", "RT-MULTI", new ReturnOrderRequests.RelabelCreate(
|
||||
List.of(new ReturnOrderRequests.RelabelItem("SKU-A", 1)),
|
||||
"SKU_TOTAL", "200", "XLX6", "used-key", null)));
|
||||
assertEquals(409, error.getStatusCode().value());
|
||||
verifyNoInteractions(cartons, cartonItems, relabelItems, fbaWarehouseValidator);
|
||||
}
|
||||
|
||||
private static ReturnInventoryCartonItem returnCartonItem(String sku, long quantity) {
|
||||
ReturnInventoryCartonItem line = new ReturnInventoryCartonItem();
|
||||
line.setSkuCode(sku); line.setQuantity(quantity); return line;
|
||||
|
||||
+1
-1
@@ -95,7 +95,7 @@ class LastlegOrderServiceTest {
|
||||
|
||||
LastlegOrderSummary summary = ReflectionTestUtils.invokeMethod(
|
||||
service, "summary", order, List.of(), null, "UNSCHEDULED",
|
||||
new LastlegOrderMapper.OrderWeightRow(11L, new java.math.BigDecimal("30"), new java.math.BigDecimal("22")));
|
||||
new LastlegOrderMapper.OrderWeightRow(11L, new java.math.BigDecimal("30"), new java.math.BigDecimal("22")), List.of());
|
||||
|
||||
assertNotNull(summary);
|
||||
assertEquals("BOOKING-1", summary.sourceContainerBookingId());
|
||||
|
||||
Reference in New Issue
Block a user