贴标申请,仓储转运

This commit is contained in:
2026-10-09 15:20:15 +08:00
parent c4a7c2a22d
commit b4b3ae452c
35 changed files with 945 additions and 41 deletions
@@ -169,6 +169,18 @@ class DeliveryTransferServiceTest {
org.mockito.Mockito.lenient().when(returnStock.matchesReturnSource(any(), any())).thenCallRealMethod();
}
@Test
void uncountedPalletBoxCannotCreateReturnOutbound() {
ReturnInventoryCarton carton = returnCarton("RC-UNCOUNTED", "PAL-1");
carton.setCountStatus("PENDING");
when(returnCartons.lockMark("RC-UNCOUNTED")).thenReturn(carton);
var error = assertThrows(ResponseStatusException.class, () -> service.createReturnOutbound("TENANT-A", "U-1",
new DeliveryRequests.ReturnOutbound("receiver", "WH-1", "RT-1", List.of("RC-UNCOUNTED"), null, "uncounted-1")));
assertEquals(409, error.getStatusCode().value());
org.mockito.Mockito.verifyNoInteractions(returnCartonItems, outboundPallets);
verify(outboundOrders, org.mockito.Mockito.never()).insert(any(OutboundOrder.class));
}
@Test
void returnBoxOutboundCanSelectOneCartonFromAPallet() {
ReturnInventoryCarton first = returnCarton("RC-1", "PAL-1");
@@ -895,6 +895,25 @@ class RelabelApplicationServiceTest {
return app;
}
@Test
void uncountedPalletBoxCannotCreateRelabelApplication() {
var cartonMapper = org.mockito.Mockito.mock(com.baiyue.lastleg.returnreceipt.mapper.ReturnInventoryCartonMapper.class);
ReflectionTestUtils.setField(service, "returnCartons", cartonMapper);
var carton = new com.baiyue.lastleg.returnreceipt.entity.ReturnInventoryCarton();
carton.setId(901L); carton.setCustomerId("10001"); carton.setOverseasWarehouseId("WH-1");
carton.setCartonMark("RETURN-BOX-1"); carton.setStatus("AVAILABLE"); carton.setSourceUnitType("PALLET"); carton.setCountStatus("PENDING");
when(cartonMapper.lockId(901L)).thenReturn(carton);
var detail = new RelabelRequests.Item(null, null, null, "SKU-A", null, null, null,
"Return goods", 1, 1, "RT-1", null, "NEW-A", null, null,
null, null, null, "RETURN-BOX-1", null, null, "SKU_LABEL",
List.of(new RelabelRequests.SkuItem("SKU-A", "NEW-A", 1, null)), 901L);
var request = new RelabelRequests.Create("RT-1", "WH-1", LocalDateTime.now().plusDays(1),
"Li", "13800000000", null, null, "return-uncounted", List.of(detail), null, false);
var error = assertThrows(ResponseStatusException.class, () -> service.create("10001", "operator-1", request));
assertTrue(error.getReason().contains("清点"));
verify(applications, never()).insert(any(RelabelApplication.class));
}
@Test
void returnSkuRelabelRejectsQuantityBeyondSelectedSourceBox() {
var cartonMapper = org.mockito.Mockito.mock(com.baiyue.lastleg.returnreceipt.mapper.ReturnInventoryCartonMapper.class);
@@ -934,6 +953,19 @@ class RelabelApplicationServiceTest {
"SKU-NEW", "Label on carton", "file-1", "relabel.pdf", "https://files/relabel.pdf")));
}
@Test
void rejectsDifferentOperationTypesInOneApplicationBeforeStockIsHeld() {
var first = carton("A", "RC-A", "FBA-A", "ABE8");
var second = new RelabelRequests.Item(101L, 202L, 301L, "B", null, null, null,
"商品B", 1, 1, "SHP-2", null, "B-NEW", null, null,
null, null, null, "RC-B", null, null, "SKU_LABEL", List.of());
var request = new RelabelRequests.Create("CUSTOMER-1", "WH-1", LocalDateTime.now().plusDays(1),
"Li", "13800000000", new BigDecimal("20.00"), null, "mixed-types", List.of(first, second));
var error = assertThrows(ResponseStatusException.class, () -> service.create("10001", "operator-1", request));
assertEquals(400, error.getStatusCode().value());
assertTrue(error.getReason().contains("只能选择一种"));
}
private static RelabelRequests.Item carton(String sku, String cartonMark, String fbaCartonMark,
String destinationWarehouseId) {
long positionId = "ABE8".equals(destinationWarehouseId) ? 202L : 201L;
@@ -0,0 +1,168 @@
package com.baiyue.lastleg.returnreceipt.service;
import com.baiyue.lastleg.pda.security.PdaWarehouseIdentity;
import com.baiyue.lastleg.returnreceipt.dto.ReturnCartonCountDtos.*;
import com.baiyue.lastleg.returnreceipt.entity.*;
import com.baiyue.lastleg.returnreceipt.mapper.*;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.web.server.ResponseStatusException;
import java.util.List;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.*;
import static org.mockito.ArgumentMatchers.*;
@ExtendWith(MockitoExtension.class)
class ReturnCartonCountServiceTest {
@Mock ReturnReceiptMapper receipts;
@Mock ReturnReceiptItemMapper receiptItems;
@Mock ReturnInventoryCartonMapper cartons;
@Mock ReturnInventoryCartonItemMapper items;
@Mock ReturnInventoryBalanceMapper balances;
@Mock ReturnInventoryOperationMapper operations;
@Mock ReturnCartonCountSessionMapper sessions;
ReturnCartonCountService service;
PdaWarehouseIdentity identity = new PdaWarehouseIdentity("op", "operator", 123L);
ReturnReceipt receipt;
ReturnInventoryCarton a, b;
@BeforeEach void setup() {
service = new ReturnCartonCountService();
ReflectionTestUtils.setField(service,"receipts",receipts);
ReflectionTestUtils.setField(service,"receiptItems",receiptItems);
ReflectionTestUtils.setField(service,"cartons",cartons);
ReflectionTestUtils.setField(service,"items",items);
ReflectionTestUtils.setField(service,"balances",balances);
ReflectionTestUtils.setField(service,"operations",operations);
ReflectionTestUtils.setField(service,"sessions",sessions);
lenient().when(sessions.lockOperator("op")).thenReturn(new ReturnCartonCountSessionMapper.Session("op","123",1L,null));
lenient().when(sessions.lockOperator("other")).thenReturn(new ReturnCartonCountSessionMapper.Session("other","999",null,null));
lenient().when(sessions.release("op",1L)).thenReturn(1);
ReflectionTestUtils.setField(service,"objectMapper",new ObjectMapper().findAndRegisterModules());
receipt = new ReturnReceipt(); receipt.setId(1L); receipt.setReceiptNo("RR-1"); receipt.setCustomerId("C1"); receipt.setPalletCode("PLT-1");
a = box(2L,"RC#2-1"); b = box(3L,"RC#2-2");
}
private ReturnInventoryCarton box(long id, String mark) {
var box = new ReturnInventoryCarton(); box.setId(id); box.setCartonMark(mark); box.setCustomerId("C1"); box.setOverseasWarehouseId("123");
box.setCountStatus("PENDING"); box.setStatus("AVAILABLE"); box.setSourceReceiptId(1L); box.setSourceUnitType("PALLET"); return box;
}
private ReturnReceiptItem expected(String sku, long qty) {
var item = new ReturnReceiptItem(); item.setSkuCode(sku); item.setReceivedQuantity(qty); return item;
}
private ReturnInventoryCartonItem provisional(long box, String sku, long qty) {
var item = new ReturnInventoryCartonItem(); item.setCartonId(box); item.setSkuCode(sku); item.setQuantity(qty); item.setPackingReservedQuantity(0L); return item;
}
private void prepare(List<ReturnReceiptItem> expected) {
when(receipts.lockCountReceipt("RR-1")).thenReturn(receipt);
when(cartons.lockReceipt(1L)).thenReturn(List.of(a,b));
when(receiptItems.listReceipt(1L)).thenReturn(expected);
}
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)))));
}
@Test void unequalBoxesAreConfirmedWithoutChangingSkuUnitBalanceAndRetryIsStable() {
prepare(List.of(expected("SKU1",10)));
when(items.lockCarton(2L)).thenReturn(List.of(provisional(2,"SKU1",5)));
when(items.lockCarton(3L)).thenReturn(List.of(provisional(3,"SKU1",5)));
when(items.listCarton(2L)).thenReturn(List.of(provisional(2,"SKU1",3)));
when(items.listCarton(3L)).thenReturn(List.of(provisional(3,"SKU1",7)));
var result = service.confirm(identity,"RR-1",unequal(3,7));
assertThat(result.status()).isEqualTo("CONFIRMED");
assertThat(a.getTotalQuantity()).isEqualTo(3); assertThat(b.getTotalQuantity()).isEqualTo(7);
assertThat(a.getCountedBy()).isEqualTo("op"); assertThat(a.getCountedAt()).isNotNull();
verify(items).deleteCarton(2L); verify(items).deleteCarton(3L); verify(items,times(2)).insert(any(ReturnInventoryCartonItem.class));
verifyNoInteractions(balances);
var audit = org.mockito.ArgumentCaptor.forClass(ReturnInventoryOperation.class); verify(operations).insert(audit.capture());
when(operations.lockKey("op","retry")).thenReturn(audit.getValue());
assertThat(service.confirm(identity,"RR-1",unequal(3,7))).isEqualTo(result);
assertThatThrownBy(() -> service.confirm(identity,"RR-1",unequal(4,6))).isInstanceOf(ResponseStatusException.class).hasMessageContaining("幂等键");
verify(items,times(2)).deleteCarton(anyLong());
}
@Test void mixedBoxesAdjustOnlyContainingBoxCounts() {
prepare(List.of(expected("SKU1",6),expected("SKU2",4)));
when(items.lockCarton(2L)).thenReturn(List.of(provisional(2,"SKU1",6)));
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)))));
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);
}
@Test void totalsMismatchDoesNotDeleteOrReleaseAnyBox() {
prepare(List.of(expected("SKU1",10)));
assertThatThrownBy(() -> service.confirm(identity,"RR-1",unequal(3,6))).isInstanceOf(ResponseStatusException.class).hasMessageContaining("汇总");
verify(items,never()).deleteCarton(anyLong()); verify(cartons,never()).updateById(any(ReturnInventoryCarton.class)); verifyNoInteractions(balances);
assertThat(a.getCountStatus()).isEqualTo("PENDING");
}
@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)))));
assertThatThrownBy(() -> service.confirm(identity,"RR-1",request)).isInstanceOf(ResponseStatusException.class).hasMessageContaining("重复");
verify(items,never()).deleteCarton(anyLong());
}
@Test void warehouseScopeCannotBeSuppliedByAnotherOperator() {
when(receipts.lockCountReceipt("RR-1")).thenReturn(receipt); when(cartons.lockReceipt(1L)).thenReturn(List.of(a,b));
assertThatThrownBy(() -> service.confirm(new PdaWarehouseIdentity("other","other",999),"RR-1",unequal(3,7))).isInstanceOf(ResponseStatusException.class).hasMessageContaining("当前仓");
verifyNoInteractions(items,receiptItems,operations);
}
@Test void scanDoesNotExposeProvisionalAverageAsActualCount() {
when(cartons.findMark(a.getCartonMark())).thenReturn(a); when(receipts.selectById(1L)).thenReturn(receipt);
when(cartons.listReceipt(1L)).thenReturn(List.of(a,b)); when(receiptItems.listReceipt(1L)).thenReturn(List.of(expected("SKU1",10)));
when(items.listCarton(2L)).thenReturn(List.of(provisional(2,"SKU1",5))); when(items.listCarton(3L)).thenReturn(List.of(provisional(3,"SKU1",5)));
var task = service.scan(identity,a.getCartonMark());
assertThat(task.boxes()).allSatisfy(box -> assertThat(box.items()).allSatisfy(item -> assertThat(item.quantity()).isZero()));
assertThatThrownBy(() -> ReturnCartonCountGuard.requireConfirmed(a)).isInstanceOf(ResponseStatusException.class).hasMessageContaining("清点");
a.setCountStatus("CONFIRMED"); assertThatCode(() -> ReturnCartonCountGuard.requireConfirmed(a)).doesNotThrowAnyException();
}
@Test void unifiedScanUsesExactCodeAndWarehouseAndReturnsAllPendingPalletBatches() {
var second = new ReturnReceipt(); second.setId(4L); second.setReceiptNo("RR-2"); second.setPalletCode("PLT-1");
when(receipts.scanPendingContents("123","PLT-1")).thenReturn(List.of(receipt,second));
when(cartons.listReceipt(1L)).thenReturn(List.of(a,b));
var tasks = service.scanTasks(identity," PLT-1 ");
assertThat(tasks).extracting(Task::receiptNo).containsExactly("RR-1","RR-2");
verify(receipts).scanPendingContents("123","PLT-1");
verifyNoInteractions(operations,balances);
}
@Test void unifiedScanWithNoPendingBoxesReturnsEmptyInsteadOfCompletedContents() {
when(receipts.scanPendingContents("123","RC-DONE")).thenReturn(List.of());
assertThat(service.scanTasks(identity,"RC-DONE")).isEmpty();
verifyNoInteractions(cartons,items,receiptItems);
}
@Test void beginClaimsAndRetriesSameTaskWithoutClaimingAgain() {
when(sessions.lockOperator("op")).thenReturn(new ReturnCartonCountSessionMapper.Session("op","123",null,null),new ReturnCartonCountSessionMapper.Session("op","123",1L,null));
when(receipts.lockCountReceipt("RR-1")).thenReturn(receipt); when(cartons.lockReceipt(1L)).thenReturn(List.of(a,b));
when(sessions.claim("op","123",1L)).thenReturn(1);
when(sessions.findReceipt(1L)).thenReturn(null,new ReturnCartonCountSessionMapper.Session("op","123",1L,null));
service.begin(identity,"RR-1"); service.begin(identity,"RR-1");
verify(sessions,times(1)).claim("op","123",1L);
verify(sessions,never()).release(anyString(),anyLong());
}
@Test void activeTaskBlocksAnotherTaskEvenAfterLeavingThePage() {
var another = new ReturnReceipt(); another.setId(4L); another.setReceiptNo("RR-2");
when(receipts.lockCountReceipt("RR-2")).thenReturn(another);
assertThatThrownBy(() -> service.begin(identity,"RR-2")).isInstanceOf(ResponseStatusException.class).hasMessageContaining("一次只能清点一单");
verify(sessions,never()).claim(anyString(),anyString(),anyLong()); verifyNoInteractions(cartons);
}
@Test void anotherOperatorCannotTakeClaimedTask() {
when(sessions.lockOperator("op")).thenReturn(new ReturnCartonCountSessionMapper.Session("op","123",null,null));
when(receipts.lockCountReceipt("RR-1")).thenReturn(receipt); when(cartons.lockReceipt(1L)).thenReturn(List.of(a,b));
when(sessions.findReceipt(1L)).thenReturn(new ReturnCartonCountSessionMapper.Session("someone","123",1L,null));
assertThatThrownBy(() -> service.begin(identity,"RR-1")).isInstanceOf(ResponseStatusException.class).hasMessageContaining("其他人员");
verify(sessions,never()).claim(anyString(),anyString(),anyLong());
}
@Test void currentRestoresClaimedTaskAndDirectConfirmRequiresBegin() {
when(sessions.findOperator("op")).thenReturn(new ReturnCartonCountSessionMapper.Session("op","123",1L,null));
when(sessions.findReceipt(1L)).thenReturn(new ReturnCartonCountSessionMapper.Session("op","123",1L,null));
when(receipts.selectById(1L)).thenReturn(receipt); when(cartons.listReceipt(1L)).thenReturn(List.of(a,b));
assertThat(service.current(identity).status()).isEqualTo("COUNTING");
when(sessions.lockOperator("op")).thenReturn(new ReturnCartonCountSessionMapper.Session("op","123",null,null));
when(receipts.lockCountReceipt("RR-1")).thenReturn(receipt); when(cartons.lockReceipt(1L)).thenReturn(List.of(a,b));
assertThatThrownBy(() -> service.confirm(identity,"RR-1",unequal(3,7))).isInstanceOf(ResponseStatusException.class).hasMessageContaining("先开始");
verify(items,never()).deleteCarton(anyLong());
}
}
@@ -84,6 +84,46 @@ class ReturnInventoryServiceTest {
verify(packingPlans).listPlatform(null, "PLAN-1", "C1", "W1", "SKU1", 20L, 20L);
}
@Test
void receiptDetailsUseCountSnapshotEvenAfterInventoryChanges() throws Exception {
var receipt = new ReturnReceipt(); receipt.setId(21L); receipt.setCustomerId("C1"); receipt.setReceiptNo("RR-1"); receipt.setUnitType("PALLET"); receipt.setPalletCode("PLT-1");
var snapshot = new com.baiyue.lastleg.returnreceipt.dto.ReturnCartonCountDtos.Task("RR-1", "RT-1", "PLT-1", "CONFIRMED", List.of(), List.of(
new com.baiyue.lastleg.returnreceipt.dto.ReturnCartonCountDtos.Box("RC#2-1", List.of(new com.baiyue.lastleg.returnreceipt.dto.ReturnCartonCountDtos.Sku("A", "商品A", 3))),
new com.baiyue.lastleg.returnreceipt.dto.ReturnCartonCountDtos.Box("RC#2-2", List.of(new com.baiyue.lastleg.returnreceipt.dto.ReturnCartonCountDtos.Sku("A", "商品A", 7)))));
var operation = new com.baiyue.lastleg.returnreceipt.entity.ReturnInventoryOperation();
operation.setResultJson(new com.fasterxml.jackson.databind.ObjectMapper().findAndRegisterModules().writeValueAsString(snapshot));
when(operations.receiptContents("C1", 21L)).thenReturn(operation);
var detail = service.receiptContents(receipt);
assertThat(detail).hasSize(2);
assertThat(detail.get(0).items().get(0).quantity()).isEqualTo(3);
assertThat(detail.get(1).items().get(0).quantity()).isEqualTo(7);
assertThat(detail.get(0).countStatus()).isEqualTo("CONFIRMED");
verifyNoInteractions(cartons, cartonItems);
}
@Test
void pendingReceiptDetailsDoNotExposeProvisionalAveragesAndParcelsHaveNoBoxes() {
var receipt = new ReturnReceipt(); receipt.setId(21L); receipt.setCustomerId("C1"); receipt.setReceiptNo("RR-1"); receipt.setUnitType("PALLET");
var box = new ReturnInventoryCarton(); box.setCartonMark("RC#2-1"); box.setCountStatus("PENDING"); box.setTotalQuantity(5L);
when(cartons.listReceipt(21L)).thenReturn(List.of(box));
var detail = service.receiptContents(receipt);
assertThat(detail.get(0).countStatus()).isEqualTo("PENDING");
assertThat(detail.get(0).items()).isEmpty();
receipt.setUnitType("PARCEL");
assertThat(service.receiptContents(receipt)).isEmpty();
verifyNoInteractions(cartonItems);
}
@Test
void boxSigningStoresActualContentsAsImmutableReceiptSnapshot() {
var receipt = new ReturnReceipt(); receipt.setId(21L); receipt.setReceiptNo("RR-BOX"); receipt.setUnitType("BOX"); receipt.setUnitCode("RC-BOX"); receipt.setOperatorId("operator-1");
service.createReceiptCarton(completedOrder(), receipt, "RC-BOX", List.of(new ReturnInventoryService.ReceiptLine("A", "商品A", 3), new ReturnInventoryService.ReceiptLine("B", "商品B", 7)));
var capture = ArgumentCaptor.forClass(com.baiyue.lastleg.returnreceipt.entity.ReturnInventoryOperation.class);
verify(operations).insert(capture.capture());
assertThat(capture.getValue().getOperationType()).isEqualTo("SIGN_CONTENTS");
assertThat(capture.getValue().getResultJson()).contains("RC-BOX", "RR-BOX", "商品A", "\"quantity\":3", "\"quantity\":7");
}
@Test
void cartonDetailsExposeImmutableMergeHistoryWithinCustomerScope() {
ReturnInventoryCarton carton = new ReturnInventoryCarton();
@@ -141,7 +181,8 @@ class ReturnInventoryServiceTest {
when(cartons.findCustomerMark(carton.getCustomerId(), carton.getCartonMark())).thenReturn(carton);
when(cartonItems.listCarton(31L)).thenReturn(List.of(item(41L, "SKU-A", 2L)));
String html = service.label(carton.getCustomerId(), carton.getCartonMark()).printableHtml();
assertThat(html).contains("<svg", carton.getCartonMark(), "SKU-A", "box-sizing:border-box");
assertThat(html).contains("<svg", carton.getCartonMark(), "box-sizing:border-box");
assertThat(html).doesNotContain("SKU-A", "class=\"sku\"");
assertThat(html).doesNotContain("RT-1", "RB-1,RB-2", "—");
assertThat(html).doesNotContain("repeating-linear-gradient", "null", carton.getCustomerId(), carton.getOverseasWarehouseId());
}
@@ -309,7 +350,7 @@ class ReturnInventoryServiceTest {
assertThat(result.totalQuantity()).isEqualTo(1L);
assertThat(result.label().items()).hasSize(1);
assertThat(result.label().items().get(0).quantity()).isEqualTo(1L);
assertThat(result.label().printableHtml()).contains("RC-WINNER", "SKU-A");
assertThat(result.label().printableHtml()).contains("RC-WINNER").doesNotContain("SKU-A");
verify(operations).findKey("operator-2", "concurrent-key");
verify(operations).lockKey("operator-2", "concurrent-key");
verify(cartons, never()).selectById(anyLong());
@@ -299,7 +299,6 @@ class ReturnReceiptServiceTest {
AtomicReference<ReturnOrder> saved = new AtomicReference<>();
when(orders.findKey("customer-1", "create-key")).thenReturn(null);
when(numbers.next(eq("RETURN"), eq("10"), anyLong())).thenReturn("RT-1");
when(numbers.next(eq("RETURN_RELABEL"), eq("10"), anyLong())).thenReturn("RETU-1");
when(orders.insert(any(ReturnOrder.class))).thenAnswer(invocation -> { saved.set(invocation.getArgument(0)); return 1; });
when(orders.findNo("customer-1", "RT-1")).thenAnswer(invocation -> saved.get());
when(orderItems.listOrder(anyLong())).thenReturn(List.of());
@@ -313,7 +312,9 @@ class ReturnReceiptServiceTest {
assertEquals("RT-1", tail.getValue().getSourceInboundId());
assertEquals("RETURN_ORDER:RT-1", tail.getValue().getSourceEventId());
assertEquals(saved.get().getRequestHash(), tail.getValue().getSourcePayloadHash());
assertEquals("RETU-1", result.order().lastlegOrderNo());
assertEquals("RT-1", tail.getValue().getOrderNo());
assertEquals("RT-1", result.order().lastlegOrderNo());
verify(numbers, never()).next(eq("RETURN_RELABEL"), anyString(), anyLong());
}
@Test
@@ -75,6 +75,7 @@ class LastlegOrderServiceTest {
@Mock WarehouseResourceGateway warehouseResourceGateway;
@Mock ContainerShippingGateMapper containerShippingGates;
@Mock ReturnOrderReferenceResolver returnOrderReferences;
@Mock com.baiyue.lastleg.returnreceipt.service.ReturnReceiptService returnReceipts;
LastlegOrderService service;
@Test
@@ -87,6 +88,13 @@ class LastlegOrderServiceTest {
assertEquals("WT-WH-20260911-020225", LastlegOrderService.displayOrderNo(order));
}
@Test
void returnTailOrderUsesSourceReturnNumber() {
LastlegOrder order = new LastlegOrder(); order.setOrderType("RETURN_RELABEL");
order.setOrderNo("RETU-OLD"); order.setSourceOrderNo("RT-1");
assertEquals("RT-1", LastlegOrderService.displayOrderNo(order));
}
@Test
void exposesStableContainerBookingIdInOrderSummary() {
LastlegOrder order = new LastlegOrder();
@@ -236,6 +244,18 @@ class LastlegOrderServiceTest {
.noneMatch(component -> "eventId".equals(component.getName())));
}
@Test
void returnTailDetailReadsReceiptFactsWithOwnedCustomerAndSourceReturnNumber() {
ReflectionTestUtils.setField(service, "returnReceipts", returnReceipts);
LastlegOrder order = order(11L, "100", "INBOUND_COMPLETED", null);
order.setOrderType("RETURN_RELABEL"); order.setSourceOrderNo("RT-SOURCE");
when(orders.selectOne(any())).thenReturn(order);
var receiptDetail = new com.baiyue.lastleg.returnreceipt.dto.ReturnOrderResponses.Detail(null, null, List.of(), List.of(), List.of(), List.of());
when(returnReceipts.detail("100", "RT-SOURCE")).thenReturn(receiptDetail);
assertSame(receiptDetail, service.detail("100", 11L).returnReceipt());
verify(returnReceipts).detail("100", "RT-SOURCE");
}
@Test
void detailReadsContainerNodeFromTheContainerShippingGate() {
LastlegOrder order = order(11L, "100", "PENDING_ASSIGNMENT", null);