贴标申请,仓储转运

This commit is contained in:
2026-10-08 15:55:47 +08:00
parent e09f8785e1
commit 16d2d9111a
160 changed files with 5704 additions and 4799 deletions
@@ -40,6 +40,11 @@ import com.baiyue.lastleg.dropship.mapper.DropshipCartonMapper;
import com.baiyue.lastleg.dropship.mapper.DropshipCartonSkuMapper;
import com.baiyue.lastleg.dropship.mapper.DropshipSkuInventoryMapper;
import com.baiyue.lastleg.dropship.model.DropshipOutboundCandidate;
import com.baiyue.lastleg.returnreceipt.entity.ReturnInventoryCarton;
import com.baiyue.lastleg.returnreceipt.entity.ReturnInventoryCartonItem;
import com.baiyue.lastleg.returnreceipt.mapper.ReturnInventoryBalanceMapper;
import com.baiyue.lastleg.returnreceipt.mapper.ReturnInventoryCartonItemMapper;
import com.baiyue.lastleg.returnreceipt.mapper.ReturnInventoryCartonMapper;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@@ -58,9 +63,11 @@ import java.util.Map;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
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.anyLong;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -113,6 +120,9 @@ class DeliveryTransferServiceTest {
@Mock DropshipCartonSkuMapper dropshipCartonSkus;
@Mock DropshipSkuInventoryMapper dropshipSkuInventory;
@Mock WarehouseTransferPalletMapper warehouseTransferPallets;
@Mock ReturnInventoryCartonMapper returnCartons;
@Mock ReturnInventoryCartonItemMapper returnCartonItems;
@Mock ReturnInventoryBalanceMapper returnBalances;
private DeliveryTransferService service;
@Mock com.baiyue.lastleg.fleet.trip.FleetDispatchBindingGuard fleetBindings;
@@ -152,6 +162,228 @@ class DeliveryTransferServiceTest {
ReflectionTestUtils.setField(service, "dropshipCartonSkus", dropshipCartonSkus);
ReflectionTestUtils.setField(service, "dropshipSkuInventory", dropshipSkuInventory);
ReflectionTestUtils.setField(service, "warehouseTransferPallets", warehouseTransferPallets);
ReflectionTestUtils.setField(service, "returnCartons", returnCartons);
ReflectionTestUtils.setField(service, "returnCartonItems", returnCartonItems);
ReflectionTestUtils.setField(service, "returnBalances", returnBalances);
}
@Test
void returnBoxOutboundCanSelectOneCartonFromAPallet() {
ReturnInventoryCarton first = returnCarton("RC-1", "PAL-1");
ReturnInventoryCarton second = returnCarton("RC-2", "PAL-1");
when(returnCartons.lockMark("RC-1")).thenReturn(first);
when(returnCartons.listActivePallet("WH-1", "PAL-1")).thenReturn(List.of(first, second));
service.createReturnOutbound("TENANT-A", "U-1",
new DeliveryRequests.ReturnOutbound("receiver", "WH-1", "RT-1", List.of("RC-1"), null, "return-1"));
ArgumentCaptor<OutboundPalletItem> item = ArgumentCaptor.forClass(OutboundPalletItem.class);
verify(outboundPallets).insert(item.capture());
assertNull(item.getValue().getPalletNo());
verify(outboundOrders).insert(any(OutboundOrder.class));
}
@Test
void receivedPalletIsEligibleForWholePalletOutboundButParcelIsNot() {
ReturnInventoryCarton pallet = returnCarton("RC-PAL", "PAL-1");
pallet.setSourceUnitType("PALLET");
ReturnInventoryCarton parcel = returnCarton("RC-PARCEL", null);
parcel.setSourceUnitType("PARCEL");
when(returnCartons.findAvailable("TENANT-A", "WH-1")).thenReturn(List.of(pallet, parcel));
var candidates = service.returnOutboundCandidates("TENANT-A", "WH-1");
assertEquals(List.of("RC-PAL"), candidates.stream().map(DeliveryResponses.ReturnOutboundCandidate::cartonMark).toList());
}
@Test
void singleSkuReturnOutboundReservesParcelAndCreatesDelivery() {
ReturnInventoryCarton parcel = returnCarton("RC-PARCEL", null);
parcel.setId(901L); parcel.setSourceUnitType("PARCEL"); parcel.setTotalQuantity(3L);
ReturnInventoryCartonItem line = new ReturnInventoryCartonItem();
line.setSkuCode("SKU-A"); line.setQuantity(3L);
when(returnCartons.lockMark("RC-PARCEL")).thenReturn(parcel);
when(returnCartonItems.lockCarton(901L)).thenReturn(List.of(line));
service.createReturnOutbound("TENANT-A", "U-1",
new DeliveryRequests.ReturnOutbound("receiver", "WH-1", "RT-1",
List.of("RC-PARCEL"), null, "sku-return-1", "SKU-A"));
ArgumentCaptor<OutboundPalletItem> item = ArgumentCaptor.forClass(OutboundPalletItem.class);
ArgumentCaptor<DeliveryOrder> delivery = ArgumentCaptor.forClass(DeliveryOrder.class);
verify(outboundPallets).insert(item.capture());
verify(deliveryOrders).insert(delivery.capture());
assertEquals("SKU-A", item.getValue().getSkuCode());
assertEquals("RC-PARCEL", item.getValue().getCartonMarks());
assertEquals("OUTBOUND_PENDING", parcel.getStatus());
assertEquals("WAIT_DELIVERY", delivery.getValue().getStatus());
assertEquals(1, delivery.getValue().getPackageCount());
}
@Test
void singleSkuReturnOutboundRejectsMixedCarton() {
ReturnInventoryCarton mixed = returnCarton("RC-MIXED", null);
mixed.setId(901L); mixed.setTotalQuantity(5L);
ReturnInventoryCartonItem skuA = new ReturnInventoryCartonItem(); skuA.setSkuCode("SKU-A"); skuA.setQuantity(3L);
ReturnInventoryCartonItem skuB = new ReturnInventoryCartonItem(); skuB.setSkuCode("SKU-B"); skuB.setQuantity(2L);
when(returnCartons.lockMark("RC-MIXED")).thenReturn(mixed);
when(returnCartonItems.lockCarton(901L)).thenReturn(List.of(skuA, skuB));
assertEquals(409, assertThrows(ResponseStatusException.class, () -> service.createReturnOutbound("TENANT-A", "U-1",
new DeliveryRequests.ReturnOutbound("receiver", "WH-1", "RT-1",
List.of("RC-MIXED"), null, "sku-return-2", "SKU-A"))).getStatusCode().value());
verify(outboundOrders, never()).insert(any(OutboundOrder.class));
}
@Test
void singleSkuReturnPickAcceptsSkuScanAndRejectsOtherSku() {
OutboundOrder outbound = outbound(301L, null, null, "OUT-301", "sku-return-1");
outbound.setOverseasWarehouseId("WH-1"); outbound.setOutboundType("RETURN_OUTBOUND"); outbound.setStatus("WAIT_PICK");
OutboundPdaTask task = new OutboundPdaTask(); task.setId(701L); task.setCustomerId("TENANT-A");
task.setOutboundOrderId(301L); task.setTaskNo("PDA-701"); task.setStatus("PENDING");
OutboundPalletItem item = new OutboundPalletItem(); item.setId(801L); item.setCustomerId("TENANT-A");
item.setOutboundOrderId(301L); item.setSkuCode("SKU-A"); item.setCartonMarks("RC-1");
item.setPackageCount(1); item.setPickedPackageCount(0);
ReturnInventoryCarton carton = returnCarton("RC-1", null); carton.setId(901L);
carton.setStatus("OUTBOUND_PENDING"); carton.setTotalQuantity(3L);
ReturnInventoryCartonItem line = new ReturnInventoryCartonItem(); line.setSkuCode("SKU-A"); line.setQuantity(3L);
when(pdaTasks.lockByNoAndWarehouse("PDA-701", "WH-1")).thenReturn(task);
when(outboundOrders.selectById(301L)).thenReturn(outbound);
when(outboundOrders.lockByNo("TENANT-A", "OUT-301")).thenReturn(outbound);
when(outboundPallets.selectById(801L)).thenReturn(item);
when(outboundPallets.findByOutbound("TENANT-A", 301L)).thenReturn(List.of(item));
when(returnCartons.lockMark("RC-1")).thenReturn(carton);
when(returnCartonItems.lockCarton(901L)).thenReturn(List.of(line));
when(returnBalances.shipCartonSku("TENANT-A", "WH-1", "SKU-A", 3L)).thenReturn(1);
assertEquals(409, assertThrows(ResponseStatusException.class, () -> service.pdaPick("WH-1", "U-1", "PDA-701",
new DeliveryRequests.PdaPick(801L, null, 1L, "wrong-sku", List.of(), "SKU-B"))).getStatusCode().value());
service.pdaPick("WH-1", "U-1", "PDA-701",
new DeliveryRequests.PdaPick(801L, null, 1L, "correct-sku", List.of(), "SKU-A"));
assertEquals("OUTBOUNDED", carton.getStatus());
verify(returnBalances).shipCartonSku("TENANT-A", "WH-1", "SKU-A", 3L);
}
@Test
void returnDeliveryDetailShowsFrozenCartonSkuAndQuantity() {
DeliveryOrder delivery = new DeliveryOrder(); delivery.setId(501L); delivery.setCustomerId("TENANT-A");
delivery.setDeliveryNo("DL-501"); delivery.setOutboundOrderId(301L);
delivery.setOutboundNo("OUT-301"); delivery.setOverseasWarehouseId("WH-1");
delivery.setDeliveryGroupKey("RETURN:RT-1"); delivery.setStatus("WAIT_DELIVERY");
OutboundOrder outbound = outbound(301L, null, null, "OUT-301", "return-1");
outbound.setOverseasWarehouseId("WH-1"); outbound.setOutboundType("RETURN_OUTBOUND");
OutboundPalletItem item = new OutboundPalletItem(); item.setCartonMarks("RC-1");
ReturnInventoryCarton carton = returnCarton("RC-1", null); carton.setId(901L);
ReturnInventoryCartonItem line = new ReturnInventoryCartonItem();
line.setSkuCode("SKU-A"); line.setProductNameZh("测试品"); line.setQuantity(3L);
when(deliveryOrders.findByNo("TENANT-A", "DL-501")).thenReturn(delivery);
when(outboundOrders.selectById(301L)).thenReturn(outbound);
when(outboundPallets.findByOutbound("TENANT-A", 301L)).thenReturn(List.of(item));
when(returnCartons.findCustomerMark("TENANT-A", "RC-1")).thenReturn(carton);
when(returnCartonItems.listCarton(901L)).thenReturn(List.of(line));
var detail = service.deliveryDetail("TENANT-A", "DL-501");
assertEquals(1, detail.skus().size());
assertEquals("SKU-A", detail.skus().get(0).skuCode());
assertEquals(3L, detail.skus().get(0).productQuantity());
assertEquals(1, detail.skus().get(0).packageCount());
}
@Test
void returnCartonPickDeductsEverySkuOnce() {
OutboundOrder outbound = outbound(301L, null, null, "OUT-301", "return-1");
outbound.setCustomerId("TENANT-A"); outbound.setOverseasWarehouseId("WH-1");
outbound.setOutboundType("RETURN_OUTBOUND"); outbound.setStatus("WAIT_PICK");
OutboundPdaTask task = new OutboundPdaTask(); task.setId(701L); task.setCustomerId("TENANT-A");
task.setOutboundOrderId(301L); task.setTaskNo("PDA-701"); task.setStatus("PENDING");
OutboundPalletItem item = new OutboundPalletItem(); item.setId(801L); item.setCustomerId("TENANT-A");
item.setOutboundOrderId(301L); item.setPackageCount(1); item.setPickedPackageCount(0);
item.setCartonMarks("RC-1"); item.setStatus("WAIT_PICK");
ReturnInventoryCarton carton = returnCarton("RC-1", null); carton.setStatus("OUTBOUND_PENDING");
carton.setId(901L);
ReturnInventoryCartonItem skuA = new ReturnInventoryCartonItem(); skuA.setSkuCode("SKU-A"); skuA.setQuantity(3L);
ReturnInventoryCartonItem skuB = new ReturnInventoryCartonItem(); skuB.setSkuCode("SKU-B"); skuB.setQuantity(2L);
when(pdaTasks.lockByNoAndWarehouse("PDA-701", "WH-1")).thenReturn(task);
when(outboundOrders.selectById(301L)).thenReturn(outbound);
when(outboundOrders.lockByNo("TENANT-A", "OUT-301")).thenReturn(outbound);
when(outboundPallets.selectById(801L)).thenReturn(item);
when(outboundPallets.findByOutbound("TENANT-A", 301L)).thenReturn(List.of(item));
when(returnCartons.lockMark("RC-1")).thenReturn(carton);
when(returnCartonItems.lockCarton(901L)).thenReturn(List.of(skuA, skuB));
when(returnBalances.shipCartonSku("TENANT-A", "WH-1", "SKU-A", 3L)).thenReturn(1);
when(returnBalances.shipCartonSku("TENANT-A", "WH-1", "SKU-B", 2L)).thenReturn(1);
service.pdaPick("WH-1", "U-1", "PDA-701",
new DeliveryRequests.PdaPick(801L, null, 1L, "scan-rc-1", List.of("RC-1")));
verify(returnBalances).shipCartonSku("TENANT-A", "WH-1", "SKU-A", 3L);
verify(returnBalances).shipCartonSku("TENANT-A", "WH-1", "SKU-B", 2L);
assertEquals("OUTBOUNDED", carton.getStatus());
assertEquals("PICKED", item.getStatus());
}
@Test
void returnPalletRejectsIndividualCartonScan() {
OutboundOrder outbound = outbound(301L, null, null, "OUT-301", "return-2");
outbound.setOverseasWarehouseId("WH-1"); outbound.setOutboundType("RETURN_OUTBOUND");
OutboundPdaTask task = new OutboundPdaTask(); task.setId(701L); task.setCustomerId("TENANT-A");
task.setOutboundOrderId(301L); task.setTaskNo("PDA-701"); task.setStatus("PENDING");
OutboundPalletItem item = new OutboundPalletItem(); item.setId(801L); item.setCustomerId("TENANT-A");
item.setOutboundOrderId(301L); item.setPalletNo("PAL-1"); item.setCartonMarks("RC-1");
item.setPackageCount(1); item.setPickedPackageCount(0);
when(pdaTasks.lockByNoAndWarehouse("PDA-701", "WH-1")).thenReturn(task);
when(outboundOrders.selectById(301L)).thenReturn(outbound);
when(outboundOrders.lockByNo("TENANT-A", "OUT-301")).thenReturn(outbound);
when(outboundPallets.selectById(801L)).thenReturn(item);
assertEquals(409, assertThrows(ResponseStatusException.class, () -> service.pdaPick("WH-1", "U-1", "PDA-701",
new DeliveryRequests.PdaPick(801L, null, 1L, "scan-rc-1", List.of("RC-1")))).getStatusCode().value());
verify(returnBalances, never()).shipCartonSku(any(), any(), any(), anyLong());
}
@Test
void returnPalletScanShipsEveryCartonAndRejectsChangedPalletContents() {
OutboundOrder outbound = outbound(301L, null, null, "OUT-301", "return-3");
outbound.setOverseasWarehouseId("WH-1"); outbound.setOutboundType("RETURN_OUTBOUND");
OutboundPdaTask task = new OutboundPdaTask(); task.setId(701L); task.setCustomerId("TENANT-A");
task.setOutboundOrderId(301L); task.setTaskNo("PDA-701"); task.setStatus("PENDING");
OutboundPalletItem item = new OutboundPalletItem(); item.setId(801L); item.setCustomerId("TENANT-A");
item.setOutboundOrderId(301L); item.setPalletNo("PAL-1"); item.setCartonMarks("RC-1,RC-2");
item.setPackageCount(2); item.setPickedPackageCount(0);
ReturnInventoryCarton first = returnCarton("RC-1", "PAL-1"); first.setId(901L); first.setStatus("OUTBOUND_PENDING");
ReturnInventoryCarton second = returnCarton("RC-2", "PAL-1"); second.setId(902L); second.setStatus("OUTBOUND_PENDING");
ReturnInventoryCartonItem firstSku = new ReturnInventoryCartonItem(); firstSku.setSkuCode("SKU-A"); firstSku.setQuantity(3L);
ReturnInventoryCartonItem secondSku = new ReturnInventoryCartonItem(); secondSku.setSkuCode("SKU-B"); secondSku.setQuantity(2L);
when(pdaTasks.lockByNoAndWarehouse("PDA-701", "WH-1")).thenReturn(task);
when(outboundOrders.selectById(301L)).thenReturn(outbound);
when(outboundOrders.lockByNo("TENANT-A", "OUT-301")).thenReturn(outbound);
when(outboundPallets.findByOutbound("TENANT-A", 301L)).thenReturn(List.of(item));
when(returnCartons.listActivePallet("WH-1", "PAL-1")).thenReturn(List.of(first));
assertEquals(409, assertThrows(ResponseStatusException.class, () -> service.pdaPickPallet("WH-1", "U-1", "PDA-701",
"PAL-1", new DeliveryRequests.PdaPalletPick("scan-pal-1"))).getStatusCode().value());
verify(returnBalances, never()).shipCartonSku(any(), any(), any(), anyLong());
when(returnCartons.listActivePallet("WH-1", "PAL-1")).thenReturn(List.of(first, second));
when(returnCartons.lockMark("RC-1")).thenReturn(first);
when(returnCartons.lockMark("RC-2")).thenReturn(second);
when(returnCartonItems.lockCarton(901L)).thenReturn(List.of(firstSku));
when(returnCartonItems.lockCarton(902L)).thenReturn(List.of(secondSku));
when(returnBalances.shipCartonSku("TENANT-A", "WH-1", "SKU-A", 3L)).thenReturn(1);
when(returnBalances.shipCartonSku("TENANT-A", "WH-1", "SKU-B", 2L)).thenReturn(1);
service.pdaPickPallet("WH-1", "U-1", "PDA-701", "PAL-1", new DeliveryRequests.PdaPalletPick("scan-pal-2"));
assertEquals("OUTBOUNDED", first.getStatus()); assertEquals("OUTBOUNDED", second.getStatus());
assertEquals(2, item.getPickedPackageCount());
verify(returnBalances).shipCartonSku("TENANT-A", "WH-1", "SKU-A", 3L);
verify(returnBalances).shipCartonSku("TENANT-A", "WH-1", "SKU-B", 2L);
}
private static ReturnInventoryCarton returnCarton(String mark, String palletCode) {
ReturnInventoryCarton carton = new ReturnInventoryCarton();
carton.setCustomerId("TENANT-A"); carton.setOverseasWarehouseId("WH-1");
carton.setSourceReturnNo("RT-1"); carton.setSourceUnitType("BOX");
carton.setCartonMark(mark); carton.setPalletCode(palletCode); carton.setStatus("AVAILABLE");
return carton;
}
@Test
@@ -0,0 +1,146 @@
package com.baiyue.lastleg.delivery.service;
import com.baiyue.lastleg.delivery.dto.DeliveryRequests;
import com.baiyue.lastleg.delivery.entity.*;
import com.baiyue.lastleg.delivery.mapper.*;
import com.baiyue.lastleg.returnreceipt.entity.*;
import com.baiyue.lastleg.returnreceipt.mapper.*;
import com.baiyue.lastleg.returnreceipt.service.ReturnInventoryService;
import com.baiyue.lastleg.service.LastlegOrderNoGenerator;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.web.server.ResponseStatusException;
import java.util.List;
import static org.mockito.Mockito.*;
import static org.mockito.ArgumentMatchers.*;
import static org.assertj.core.api.Assertions.*;
class ReturnOutboundClosureTest {
final DeliveryTransferService service = new DeliveryTransferService();
final OutboundOrderMapper outbounds = mock(OutboundOrderMapper.class);
final OutboundPalletItemMapper items = mock(OutboundPalletItemMapper.class);
final OutboundPdaTaskMapper tasks = mock(OutboundPdaTaskMapper.class);
final DeliveryOrderMapper deliveries = mock(DeliveryOrderMapper.class);
final ReturnInventoryCartonMapper cartons = mock(ReturnInventoryCartonMapper.class);
final ReturnInventoryCartonItemMapper contents = mock(ReturnInventoryCartonItemMapper.class);
final ReturnInventoryService stock = mock(ReturnInventoryService.class);
final OutboundPdaPickMapper picks = mock(OutboundPdaPickMapper.class);
final com.baiyue.lastleg.relabel.mapper.RelabelApplicationMapper relabelApplications = mock(com.baiyue.lastleg.relabel.mapper.RelabelApplicationMapper.class);
final com.baiyue.lastleg.relabel.service.RelabelApplicationService relabelService = mock(com.baiyue.lastleg.relabel.service.RelabelApplicationService.class);
ReturnInventoryCarton carton;
@BeforeEach void setup() {
ReflectionTestUtils.setField(service, "outboundOrders", outbounds);
ReflectionTestUtils.setField(service, "outboundPallets", items);
ReflectionTestUtils.setField(service, "pdaTasks", tasks);
ReflectionTestUtils.setField(service, "deliveryOrders", deliveries);
ReflectionTestUtils.setField(service, "returnCartons", cartons);
ReflectionTestUtils.setField(service, "returnCartonItems", contents);
ReflectionTestUtils.setField(service, "returnStock", stock);
ReflectionTestUtils.setField(service, "pdaPicks", picks);
ReflectionTestUtils.setField(service, "relabelApplications", relabelApplications);
ReflectionTestUtils.setField(service, "relabelService", relabelService);
ReflectionTestUtils.setField(service, "orderNoGenerator", new LastlegOrderNoGenerator());
ReflectionTestUtils.setField(service, "objectMapper", new ObjectMapper().findAndRegisterModules());
carton = new ReturnInventoryCarton(); carton.setId(10L); carton.setCartonMark("RC#1-1");
carton.setCustomerId("C1"); carton.setOverseasWarehouseId("WH1"); carton.setSourceReturnNo("RT1,RT2");
carton.setStatus("AVAILABLE"); carton.setSourceUnitType("BOX"); carton.setTotalQuantity(6L); carton.setSkuTypes(1);
when(cartons.lockMark(carton.getCartonMark())).thenReturn(carton);
when(stock.matchesReturnSource(carton, "RT1")).thenReturn(true);
ReturnInventoryCartonItem line = new ReturnInventoryCartonItem(); line.setSkuCode("SKU1"); line.setQuantity(6L);
when(contents.lockCarton(10L)).thenReturn(List.of(line));
}
DeliveryRequests.ReturnOutbound command(String sku) {
return new DeliveryRequests.ReturnOutbound("address", "WH1", "RT1", List.of("RC#1-1"), null, "KEY1", sku);
}
@Test void reservesWholeMergedBoxAndCreatesDelivery() {
service.createReturnOutbound("C1", "USER", command(null));
assertThat(carton.getStatus()).isEqualTo("OUTBOUND_PENDING");
verify(items).insert(argThat((OutboundPalletItem i) -> i.getPackageCount() == 1 && "RT1,RT2".equals(i.getInboundCode())));
verify(deliveries).insert(argThat((DeliveryOrder d) -> "WAIT_DELIVERY".equals(d.getStatus()) && d.getPackageCount() == 1));
}
@Test void refusesSecondCommandForReservedBox() {
service.createReturnOutbound("C1", "USER", command(null));
assertThatThrownBy(() -> service.createReturnOutbound("C1", "USER", command(null)))
.isInstanceOf(ResponseStatusException.class).hasMessageContaining("占用");
verify(outbounds, times(1)).insert(any(OutboundOrder.class));
}
@Test void preventsSingleSkuBypassingExistingBox() {
assertThatThrownBy(() -> service.createReturnOutbound("C1", "USER", command("SKU1")))
.isInstanceOf(ResponseStatusException.class).hasMessageContaining("按箱唛");
verify(outbounds, never()).insert(any(OutboundOrder.class));
}
@Test void singleSkuParcelReservesAndKeepsScanSku() {
carton.setSourceUnitType("PARCEL");
service.createReturnOutbound("C1", "USER", command("SKU1"));
verify(items).insert(argThat((OutboundPalletItem i) -> "SKU1".equals(i.getSkuCode())));
assertThat(carton.getStatus()).isEqualTo("OUTBOUND_PENDING");
}
@Test void refusesPackingReservedQuantity() {
ReturnInventoryCartonItem line = new ReturnInventoryCartonItem(); line.setSkuCode("SKU1");
line.setQuantity(6L); line.setPackingReservedQuantity(1L);
when(contents.lockCarton(10L)).thenReturn(List.of(line));
assertThatThrownBy(() -> service.createReturnOutbound("C1", "USER", command(null)))
.isInstanceOf(ResponseStatusException.class).hasMessageContaining("合箱计划");
verify(outbounds, never()).insert(any(OutboundOrder.class));
}
@Test void exactScanIsRequiredOnReplayAsWell() {
OutboundPdaTask task = new OutboundPdaTask(); task.setId(20L); task.setCustomerId("C1"); task.setOutboundOrderId(30L);
when(tasks.lockByNoAndWarehouse("TASK1", "WH1")).thenReturn(task);
OutboundPdaPick pick = new OutboundPdaPick(); pick.setTaskId(20L); pick.setOutboundPalletItemId(40L); pick.setPackages(1L);
when(picks.findByKey("C1", "PICK1")).thenReturn(pick);
OutboundOrder outbound = new OutboundOrder(); outbound.setId(30L); outbound.setCustomerId("C1"); outbound.setOutboundType("RETURN_OUTBOUND");
when(outbounds.selectById(30L)).thenReturn(outbound);
OutboundPalletItem item = new OutboundPalletItem(); item.setId(40L); item.setCustomerId("C1"); item.setOutboundOrderId(30L);
item.setPackageCount(1); item.setCartonMarks("RC#1-1"); when(items.selectById(40L)).thenReturn(item);
assertThatThrownBy(() -> service.pdaPick("WH1", "USER", "TASK1",
new DeliveryRequests.PdaPick(40L, null, 1L, "PICK1", List.of("WRONG"))))
.isInstanceOf(ResponseStatusException.class).hasMessageContaining("指定的箱唛");
verify(stock, never()).shipCarton(anyString(), anyString(), anyString(), anyString(), anyString(), anyLong());
}
@Test void cancelsUntouchedNativeRelabelWithItsOwningApplication() {
OutboundOrder outbound = new OutboundOrder(); outbound.setId(30L); outbound.setCustomerId("C1");
outbound.setOutboundNo("OUT1"); outbound.setOutboundType("RELABEL_OUTBOUND"); outbound.setStatus("WAIT_PICK");
when(outbounds.findGlobalByNo("OUT1")).thenReturn(outbound);
when(outbounds.lockGlobalByNo("OUT1")).thenReturn(outbound);
var application = new com.baiyue.lastleg.relabel.entity.RelabelApplication();
application.setId(50L); application.setCustomerId("C1"); application.setRelabelNo("REL1");
application.setOutboundOrderNo("OUT1"); application.setStatus("READY_FOR_OUTBOUND");
when(relabelApplications.lockByOutbound("C1", "OUT1")).thenReturn(application);
OutboundPalletItem item = new OutboundPalletItem(); item.setCartonMarks("RC#1-1"); item.setStatus("WAIT_PICK");
item.setPickedPackageCount(0); when(items.findByOutbound("C1", 30L)).thenReturn(List.of(item));
carton.setStatus("OUTBOUND_PENDING"); carton.setRelabelApplicationId(50L);
service.cancelOutbound("OUT1", "OP1", new DeliveryRequests.Cancel("cancel"));
assertThat(carton.getStatus()).isEqualTo("RELABEL_PENDING");
assertThat(outbound.getStatus()).isEqualTo("CANCELLED");
verify(relabelService).changeStatus(eq("C1"), eq("REL1"), eq("OP1"),
argThat(request -> "CANCELLED".equals(request.status())));
var sequence = inOrder(relabelApplications, outbounds, cartons, relabelService);
sequence.verify(relabelApplications).lockByOutbound("C1", "OUT1");
sequence.verify(outbounds).lockGlobalByNo("OUT1");
sequence.verify(cartons).lockMark("RC#1-1");
sequence.verify(relabelService).changeStatus(anyString(), anyString(), anyString(), any());
}
@Test void cancellationRejectsPickedNativeBoxWithoutReleasingItsReservation() {
OutboundOrder outbound = new OutboundOrder(); outbound.setId(30L); outbound.setCustomerId("C1");
outbound.setOutboundType("RELABEL_OUTBOUND"); outbound.setStatus("WAIT_PICK");
when(outbounds.lockGlobalByNo("OUT1")).thenReturn(outbound);
OutboundPalletItem item = new OutboundPalletItem(); item.setStatus("WAIT_PICK"); item.setPickedPackageCount(1);
when(items.findByOutbound("C1", 30L)).thenReturn(List.of(item));
assertThatThrownBy(() -> service.cancelOutbound("OUT1", "OP1", new DeliveryRequests.Cancel("cancel")))
.isInstanceOf(ResponseStatusException.class).hasMessageContaining("execution has started");
verifyNoInteractions(relabelService);
verify(cartons, never()).updateById(any(ReturnInventoryCarton.class));
}
}