贴标申请,仓储转运

This commit is contained in:
2026-10-09 11:49:17 +08:00
parent 7fd7699f6e
commit 2ec5414b39
13 changed files with 335 additions and 11 deletions
@@ -895,6 +895,30 @@ class RelabelApplicationServiceTest {
return app;
}
@Test
void returnSkuRelabelRejectsQuantityBeyondSelectedSourceBox() {
var cartonMapper = org.mockito.Mockito.mock(com.baiyue.lastleg.returnreceipt.mapper.ReturnInventoryCartonMapper.class);
var lineMapper = org.mockito.Mockito.mock(com.baiyue.lastleg.returnreceipt.mapper.ReturnInventoryCartonItemMapper.class);
ReflectionTestUtils.setField(service, "returnCartons", cartonMapper);
ReflectionTestUtils.setField(service, "returnCartonItems", lineMapper);
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("BOX");
var line = new com.baiyue.lastleg.returnreceipt.entity.ReturnInventoryCartonItem();
line.setSkuCode("SKU-A"); line.setQuantity(5L); line.setPackingReservedQuantity(0L);
when(cartonMapper.lockId(901L)).thenReturn(carton);
when(lineMapper.lockCarton(901L)).thenReturn(List.of(line));
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", 6, null)), 901L);
var request = new RelabelRequests.Create("RT-1", "WH-1", LocalDateTime.now().plusDays(1),
"Li", "13800000000", null, null, "return-over-quantity", 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));
}
private void stubDetail(RelabelApplication application) {
when(applications.findNo(application.getCustomerId(), application.getRelabelNo())).thenReturn(application);
when(items.selectList(any())).thenReturn(List.of(item(31L, application.getId(), "SKU-A", 10, 6)));
@@ -0,0 +1,52 @@
package com.baiyue.lastleg.returnreceipt.controller;
import com.baiyue.common.security.AuthenticatedUser;
import com.baiyue.lastleg.returnreceipt.dto.ReturnOrderRequests;
import com.baiyue.lastleg.returnreceipt.service.ReturnReceiptService;
import org.junit.jupiter.api.Test;
import org.springframework.core.MethodParameter;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import java.util.Set;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
class MerchantReturnOrderControllerTest {
@Test
void putRoutesEditedForecastToAuthenticatedCustomer() throws Exception {
var service = mock(ReturnReceiptService.class);
var controller = new MerchantReturnOrderController();
ReflectionTestUtils.setField(controller, "service", service);
var user = new AuthenticatedUser(1L, 100L, 100L, 1L, "tester", "MERCHANT", "WEB", Set.of());
var mvc = MockMvcBuilders.standaloneSetup(controller).setCustomArgumentResolvers(new HandlerMethodArgumentResolver() {
public boolean supportsParameter(MethodParameter parameter) {
return parameter.getParameterType() == AuthenticatedUser.class;
}
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer container,
NativeWebRequest request, WebDataBinderFactory factory) {
return user;
}
}).build();
mvc.perform(put("/api/merchant/v1/last-leg/return-orders/RT-TEST")
.contentType("application/json")
.content("""
{"overseasWarehouseId":"WH-1","warehouseContactName":"Updated contact",
"warehouseContactPhone":"123456","customerReferenceNo":"REF-NEW",
"needRelabel":true,"remark":"Updated remark","idempotencyKey":"edit-test",
"items":[{"skuCode":"SKU-NEW","productNameZh":"Updated product","expectedQuantity":25}]}
"""))
.andExpect(status().isOk());
verify(service).updatePending(eq("100"), eq("RT-TEST"), eq(new ReturnOrderRequests.Create(
"WH-1", "Updated contact", "123456", "REF-NEW", true, "Updated remark", "edit-test",
java.util.List.of(new ReturnOrderRequests.Item("SKU-NEW", "Updated product", 25)))));
verifyNoMoreInteractions(service);
}
}
@@ -70,6 +70,7 @@ class ReturnReceiptServiceTest {
@Mock RelabelItemMapper relabelItems;
@Mock FbaWarehouseValidator fbaWarehouseValidator;
@Mock RelabelPhotoFileGateway photoFiles;
@Mock com.baiyue.lastleg.service.ReturnOrderReferenceResolver returnOrderReferences;
ReturnReceiptService service;
PdaWarehouseIdentity identity;
@@ -91,6 +92,7 @@ class ReturnReceiptServiceTest {
ReflectionTestUtils.setField(service, "relabelApplications", relabelApplications);
ReflectionTestUtils.setField(service, "relabelItems", relabelItems);
ReflectionTestUtils.setField(service, "fbaWarehouseValidator", fbaWarehouseValidator);
ReflectionTestUtils.setField(service, "returnOrderReferences", returnOrderReferences);
identity = new PdaWarehouseIdentity("operator-1", "仓管员", 10L);
}
@@ -110,6 +112,32 @@ class ReturnReceiptServiceTest {
assertEquals(90L, result.receivedQuantity());
}
@Test
void detailReturnsMeasurementsForBothPalletsAndDoesNotAddRepeatedCarrierVolume() {
ReturnOrder order = order("RT-TWO-PALLETS", "10", "COMPLETED", 10, 10);
when(orders.findNo("customer-1", "RT-TWO-PALLETS")).thenReturn(order);
ReturnReceipt first = measuredReceipt(1L, "PLT-A", "111", "11", "12");
ReturnReceipt second = measuredReceipt(2L, "PLT-B", "11", "21", "32");
ReturnReceipt earlier = measuredReceipt(3L, "PLT-A", "100", "10", "10");
when(receipts.listOrder(order.getId())).thenReturn(List.of(second, first, earlier));
var result = service.detail("customer-1", "RT-TWO-PALLETS");
assertEquals(2, result.pallets().size());
var palletA = result.pallets().stream().filter(row -> "PLT-A".equals(row.palletCode())).findFirst().orElseThrow();
var palletB = result.pallets().stream().filter(row -> "PLT-B".equals(row.palletCode())).findFirst().orElseThrow();
assertThat(palletA.volumeCbm()).isEqualByComparingTo("0.014652");
assertThat(palletB.volumeCbm()).isEqualByComparingTo("0.007392");
assertThat(palletA.length()).isEqualByComparingTo("111");
assertThat(palletB.weight()).isEqualByComparingTo("11");
}
private ReturnReceipt measuredReceipt(long id, String code, String length, String width, String height) {
ReturnReceipt receipt = new ReturnReceipt(); receipt.setId(id); receipt.setUnitType("PALLET"); receipt.setUnitCode(code);
receipt.setPalletCode(code); receipt.setTotalBoxCount(2L); receipt.setReceiptNo("RR-" + id);
receipt.setLength(new java.math.BigDecimal(length)); receipt.setWidth(new java.math.BigDecimal(width));
receipt.setHeight(new java.math.BigDecimal(height)); receipt.setWeight(new java.math.BigDecimal("11"));
return receipt;
}
@Test
void merchantPhotoDownloadUsesAuthenticatedCustomerAndResolvedReturnOrder() {
ReflectionTestUtils.setField(service, "photoFiles", photoFiles);
@@ -74,6 +74,7 @@ class LastlegOrderServiceTest {
@Mock ContainerArrivalTimeOperationMapper containerArrivalTimeOperations;
@Mock WarehouseResourceGateway warehouseResourceGateway;
@Mock ContainerShippingGateMapper containerShippingGates;
@Mock ReturnOrderReferenceResolver returnOrderReferences;
LastlegOrderService service;
@Test
@@ -105,7 +106,10 @@ class LastlegOrderServiceTest {
@BeforeEach
void setUp() {
com.baomidou.mybatisplus.core.metadata.TableInfoHelper.initTableInfo(
new org.apache.ibatis.builder.MapperBuilderAssistant(new org.apache.ibatis.session.Configuration(), ""), LastlegOrder.class);
service = new LastlegOrderService();
ReflectionTestUtils.setField(service, "returnOrderReferences", returnOrderReferences);
ReflectionTestUtils.setField(service, "orderMapper", orders);
ReflectionTestUtils.setField(service, "detailMapper", details);
ReflectionTestUtils.setField(service, "historyMapper", histories);
@@ -0,0 +1,49 @@
package com.baiyue.lastleg.service;
import com.baiyue.lastleg.entity.LastlegOrder;
import com.baiyue.lastleg.fleet.customer.FleetCustomerDirectory;
import com.baiyue.lastleg.inboundplan.pda.gateway.WarehouseResourceGateway;
import org.junit.jupiter.api.Test;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.*;
class ReturnOrderReferenceResolverTest {
@Test
void directoryFailureBlocksNewSnapshotButDoesNotHideHistoricalOrders() {
var customers = mock(FleetCustomerDirectory.class);
var warehouses = mock(WarehouseResourceGateway.class);
when(customers.find("1")).thenThrow(new org.springframework.web.server.ResponseStatusException(org.springframework.http.HttpStatus.SERVICE_UNAVAILABLE));
when(warehouses.requireActiveSharedWarehouse(10L)).thenReturn(new WarehouseResourceGateway.WarehouseReference(10L, "USLAX01", "洛杉矶仓", "ACTIVE"));
var resolver = new ReturnOrderReferenceResolver(customers, warehouses);
org.junit.jupiter.api.Assertions.assertThrows(org.springframework.web.server.ResponseStatusException.class, () -> resolver.populate(List.of(order())));
var historical = order();
resolver.populateForRead(List.of(historical));
assertEquals("洛杉矶仓", historical.getOverseasWarehouseName());
}
@Test
void resolvesMissingReturnReferencesOncePerCustomerAndWarehouseAndPreservesSnapshots() {
var customers = mock(FleetCustomerDirectory.class);
var warehouses = mock(WarehouseResourceGateway.class);
when(customers.find("1")).thenReturn(new FleetCustomerDirectory.Customer("1", "C1", "测试货主", null, null));
when(warehouses.requireActiveSharedWarehouse(10L)).thenReturn(new WarehouseResourceGateway.WarehouseReference(10L, "USLAX01", "洛杉矶仓", "ACTIVE"));
LastlegOrder first = order(), second = order(), frozen = order();
frozen.setCustomerName("原货主快照"); frozen.setOverseasWarehouseCode("OLD"); frozen.setOverseasWarehouseName("原仓库快照");
new ReturnOrderReferenceResolver(customers, warehouses).populate(List.of(first, second, frozen));
assertEquals("测试货主", first.getCustomerName());
assertEquals("USLAX01", second.getOverseasWarehouseCode());
assertEquals("洛杉矶仓", second.getOverseasWarehouseName());
assertEquals("原货主快照", frozen.getCustomerName());
assertEquals("原仓库快照", frozen.getOverseasWarehouseName());
verify(customers, times(1)).find("1");
verify(warehouses, times(1)).requireActiveSharedWarehouse(10L);
}
private LastlegOrder order() {
var order = new LastlegOrder(); order.setOrderType("RETURN_RELABEL");
order.setCustomerId("1"); order.setOverseasWarehouseId("10"); return order;
}
}