提交
This commit is contained in:
+41
@@ -3,10 +3,51 @@ package com.baiyue.lastleg.discard;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.SQLException;
|
||||
import com.baiyue.lastleg.returnreceipt.mapper.ReturnInventoryCartonItemMapper;
|
||||
import org.apache.ibatis.annotations.Update;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
class CargoDiscardSchemaContractTest {
|
||||
@Test
|
||||
void returnCartonDeductionAllowsZeroButRejectsNegativeStock() throws Exception {
|
||||
String baseline;
|
||||
try (var input = getClass().getResourceAsStream("/db/lastleg.sql")) {
|
||||
baseline = new String(input.readAllBytes(), StandardCharsets.UTF_8);
|
||||
}
|
||||
var constraint = java.util.regex.Pattern.compile(
|
||||
"CONSTRAINT chk_ll_return_inventory_carton_item_quantity CHECK\\(([^)]+)\\)")
|
||||
.matcher(baseline);
|
||||
assertTrue(constraint.find());
|
||||
String deduction = ReturnInventoryCartonItemMapper.class.getMethod("deduct", long.class, long.class)
|
||||
.getAnnotation(Update.class).value()[0]
|
||||
.replace("#{quantity}", "?").replace("#{id}", "?");
|
||||
try (var connection = DriverManager.getConnection("jdbc:h2:mem:discard_zero_quantity;MODE=MySQL")) {
|
||||
try (var statement = connection.createStatement()) {
|
||||
statement.execute("CREATE TABLE ll_return_inventory_carton_item (id BIGINT PRIMARY KEY, "
|
||||
+ "quantity BIGINT NOT NULL, CONSTRAINT chk_ll_return_inventory_carton_item_quantity CHECK("
|
||||
+ constraint.group(1) + "))");
|
||||
statement.execute("INSERT INTO ll_return_inventory_carton_item VALUES (1, 3)");
|
||||
try (var update = connection.prepareStatement(deduction)) {
|
||||
update.setLong(1, 3); update.setLong(2, 1); update.setLong(3, 3);
|
||||
assertEquals(1, update.executeUpdate());
|
||||
update.setLong(1, 1); update.setLong(3, 1);
|
||||
assertEquals(0, update.executeUpdate());
|
||||
}
|
||||
try (var result = statement.executeQuery("SELECT quantity FROM ll_return_inventory_carton_item WHERE id=1")) {
|
||||
assertTrue(result.next());
|
||||
assertEquals(0, result.getLong(1));
|
||||
}
|
||||
assertThrows(SQLException.class, () -> statement.executeUpdate(
|
||||
"UPDATE ll_return_inventory_carton_item SET quantity=-1 WHERE id=1"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void baselineContainsDiscardApplicationReservationAndHistoryTables() throws Exception {
|
||||
String sql;
|
||||
|
||||
+16
@@ -15,6 +15,22 @@ import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
class MerchantCargoDiscardControllerTest {
|
||||
@Test
|
||||
void completionUsesAuthenticatedCustomerAndIndependentPermission() throws Exception {
|
||||
controller.confirmCompletion(user(100L, "MERCHANT"), "CD001");
|
||||
verify(service).confirmCompletion("100", "CD001", "1");
|
||||
assertThrows(ResponseStatusException.class,
|
||||
() -> controller.confirmCompletion(user(0L, "PLATFORM"), "CD001"));
|
||||
var guard = MerchantCargoDiscardController.class.getMethod("confirmCompletion", AuthenticatedUser.class, String.class)
|
||||
.getAnnotation(org.springframework.security.access.prepost.PreAuthorize.class);
|
||||
org.junit.jupiter.api.Assertions.assertEquals(
|
||||
"hasAuthority('merchant:overseas-warehouse:discard:confirm')", guard.value());
|
||||
org.junit.jupiter.api.Assertions.assertFalse(java.util.Arrays.stream(CargoDiscardController.class.getDeclaredMethods())
|
||||
.filter(method -> method.isAnnotationPresent(org.springframework.web.bind.annotation.PostMapping.class))
|
||||
.flatMap(method -> java.util.Arrays.stream(method.getAnnotation(org.springframework.web.bind.annotation.PostMapping.class).value()))
|
||||
.anyMatch(path -> path.endsWith("/complete") || path.endsWith("/confirm-completion")));
|
||||
}
|
||||
|
||||
private final CargoDiscardService service = mock(CargoDiscardService.class);
|
||||
private final MerchantCargoDiscardController controller = controller();
|
||||
private final CargoDiscardRequests.Query query = new CargoDiscardRequests.Query(
|
||||
|
||||
+9
@@ -43,6 +43,7 @@ class CargoDiscardOutboundServiceTest {
|
||||
application = new CargoDiscardApplication(); application.setId(1L); application.setCustomerId("C1");
|
||||
application.setDiscardNo("CD-TEST"); application.setOverseasWarehouseId("99");
|
||||
application.setTotalPackages(5); application.setStatus("APPROVED");
|
||||
application.setInboundCodes("RT-123,RK-456");
|
||||
first = line(11L, 21L, 3); second = line(12L, 22L, 2);
|
||||
when(items.listApplication("C1", 1L)).thenReturn(List.of(first, second));
|
||||
when(cartons.listApplication("C1", 1L)).thenReturn(List.of());
|
||||
@@ -63,6 +64,7 @@ class CargoDiscardOutboundServiceTest {
|
||||
@Test void approvalCreatesExactlyOneDocumentAndImmutablePlannedLines() {
|
||||
service.create(application, "OP"); service.create(application, "OP");
|
||||
assertEquals("DISCARD_OUTBOUND", document.getOutboundType()); assertEquals("WAIT_PICK", document.getStatus());
|
||||
assertNull(document.getLxCodes());
|
||||
assertEquals(5, document.getPackageCount()); assertEquals(2, document.getPalletCount());
|
||||
assertTrue(service.outboundNo(application).startsWith("OB-"));
|
||||
assertEquals(List.of(3, 2), snapshots.stream().map(OutboundPalletItem::getPackageCount).toList());
|
||||
@@ -82,6 +84,13 @@ class CargoDiscardOutboundServiceTest {
|
||||
service.create(application, "OP"); application.setStatus("CANCELLED"); service.sync(application);
|
||||
assertEquals("CANCELLED", document.getStatus()); assertTrue(snapshots.stream().allMatch(s -> "CANCELLED".equals(s.getStatus())));
|
||||
}
|
||||
@Test void syncClearsHistoricallyMislabelledInboundCodesWithoutChangingSourceSnapshots() {
|
||||
first.setInboundCode("RK-456");
|
||||
service.create(application, "OP"); document.setLxCodes("RT-123,RK-456");
|
||||
service.sync(application);
|
||||
assertNull(document.getLxCodes()); assertEquals("RK-456", snapshots.get(0).getInboundCode());
|
||||
assertEquals("RT-123,RK-456", application.getInboundCodes());
|
||||
}
|
||||
@Test void returnSourceKeepsRealCartonMarkWithoutInventingPalletNumber() {
|
||||
first.setReturnCartonId(31L); first.setSourceType("RETURN_CARTON");
|
||||
var carton = new ReturnInventoryCarton(); carton.setId(31L); carton.setCustomerId("C1");
|
||||
|
||||
+46
-1
@@ -47,6 +47,36 @@ import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class CargoDiscardServiceTest {
|
||||
@Test
|
||||
void customerConfirmationClosesPdaCompletedApplicationOnceWithoutAnotherDeduction() {
|
||||
CargoDiscardApplication application = new CargoDiscardApplication();
|
||||
application.setId(1001L); application.setCustomerId("100"); application.setDiscardNo("CD001");
|
||||
application.setTotalPackages(1); application.setOverseasWarehouseName("测试仓"); application.setStatus(CargoDiscardService.PDA_COMPLETED);
|
||||
storedApplication.set(application);
|
||||
when(applications.lockNo("100", "CD001")).thenReturn(application);
|
||||
|
||||
service.confirmCompletion("100", "CD001", "CUSTOMER");
|
||||
var confirmedAt = application.getCustomerConfirmedAt();
|
||||
org.junit.jupiter.api.Assertions.assertNotNull(confirmedAt);
|
||||
assertEquals(CargoDiscardService.COMPLETED, application.getStatus());
|
||||
assertEquals("客户确认废弃完成", storedHistories.get(0).getRemark());
|
||||
service.confirmCompletion("100", "CD001", "CUSTOMER");
|
||||
assertEquals(confirmedAt, application.getCustomerConfirmedAt());
|
||||
assertEquals(1, storedHistories.size());
|
||||
verify(applications, times(1)).updateById(application);
|
||||
org.mockito.Mockito.verifyNoInteractions(adjustments, inventory, returnCartonItems);
|
||||
}
|
||||
|
||||
@org.junit.jupiter.params.ParameterizedTest
|
||||
@org.junit.jupiter.params.provider.ValueSource(strings = {"PENDING_REVIEW", "APPROVED", "REJECTED", "CANCELLED"})
|
||||
void customerCannotConfirmBeforePdaCompletion(String status) {
|
||||
CargoDiscardApplication application = new CargoDiscardApplication(); application.setStatus(status);
|
||||
when(applications.lockNo("100", "CD001")).thenReturn(application);
|
||||
org.junit.jupiter.api.Assertions.assertThrows(org.springframework.web.server.ResponseStatusException.class,
|
||||
() -> service.confirmCompletion("100", "CD001", "CUSTOMER"));
|
||||
verify(applications, never()).updateById(any(CargoDiscardApplication.class));
|
||||
}
|
||||
|
||||
@Mock CargoDiscardApplicationMapper applications;
|
||||
@Mock CargoDiscardItemMapper items;
|
||||
@Mock CargoDiscardCartonMapper discardCartons;
|
||||
@@ -57,6 +87,7 @@ class CargoDiscardServiceTest {
|
||||
@Mock LastlegOrderStatusHistoryMapper orderHistories;
|
||||
@Mock ReturnInventoryCartonMapper returnCartons;
|
||||
@Mock ReturnInventoryCartonItemMapper returnCartonItems;
|
||||
@Mock com.baiyue.lastleg.client.DomesticWarehouseDirectoryClient warehouseDirectory;
|
||||
|
||||
private CargoDiscardService service;
|
||||
private final CargoDiscardOutboundService outboundService = org.mockito.Mockito.mock(CargoDiscardOutboundService.class);
|
||||
@@ -68,6 +99,9 @@ class CargoDiscardServiceTest {
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
service = new CargoDiscardService();
|
||||
ReflectionTestUtils.setField(service, "warehouseDirectory", warehouseDirectory);
|
||||
lenient().when(warehouseDirectory.receivingWarehouses(any())).thenReturn(com.baiyue.common.core.api.ApiResponse.success(List.of(
|
||||
new com.baiyue.lastleg.client.DomesticWarehouseDirectoryClient.ReceivingWarehouse(99L, "LAX", "洛杉矶仓", "US", "ACTIVE"))));
|
||||
ReflectionTestUtils.setField(service, "outboundService", outboundService);
|
||||
ReflectionTestUtils.setField(service, "stockService", org.mockito.Mockito.mock(CargoDiscardStockService.class));
|
||||
ReflectionTestUtils.setField(service, "photoService", org.mockito.Mockito.mock(CargoDiscardPhotoService.class));
|
||||
@@ -97,6 +131,7 @@ class CargoDiscardServiceTest {
|
||||
@Test
|
||||
void createReservesExactSkuCartonsWithoutDeductingInventory() {
|
||||
LastlegOrder order = order();
|
||||
order.setOverseasWarehouseId("99"); order.setOverseasWarehouseName(null);
|
||||
DropshipSkuInventory sku = skuInventory();
|
||||
when(orders.selectById(101L)).thenReturn(order);
|
||||
when(inventory.countAvailable("100", 101L, "SKU-1")).thenReturn(2L);
|
||||
@@ -109,6 +144,7 @@ class CargoDiscardServiceTest {
|
||||
List.of(new CargoDiscardRequests.Item(101L, null, "SKU-1", 2)), "discard-create-1", "damaged"));
|
||||
|
||||
assertEquals(CargoDiscardService.PENDING_REVIEW, detail.summary().status());
|
||||
assertEquals("洛杉矶仓", storedApplication.get().getOverseasWarehouseName());
|
||||
assertEquals(2, detail.summary().totalPackages());
|
||||
assertEquals(new BigDecimal("10.00"), detail.summary().totalFee());
|
||||
assertEquals(2, storedLinks.size());
|
||||
@@ -116,6 +152,14 @@ class CargoDiscardServiceTest {
|
||||
verify(inventory, never()).deductSkuInventory(any(), anyLong(), anyInt());
|
||||
}
|
||||
|
||||
@Test void historicalDiscardDetailResolvesMissingWarehouseNameByOperatingWarehouseId() {
|
||||
CargoDiscardApplication application = pendingApplication();
|
||||
application.setOverseasWarehouseId("99"); application.setOverseasWarehouseName(null);
|
||||
storedApplication.set(application);
|
||||
assertEquals("洛杉矶仓", service.detail("100", application.getDiscardNo()).summary().overseasWarehouseName());
|
||||
verify(applications, never()).updateById(any(CargoDiscardApplication.class));
|
||||
}
|
||||
|
||||
|
||||
@Test void selectedBoxesCreatePartialApplicationWithoutReservingUnselectedStock() {
|
||||
when(orders.selectById(101L)).thenReturn(order());
|
||||
@@ -159,7 +203,7 @@ class CargoDiscardServiceTest {
|
||||
ReturnInventoryCarton carton = new ReturnInventoryCarton();
|
||||
carton.setId(901L);
|
||||
carton.setCustomerId("100");
|
||||
carton.setOverseasWarehouseId("WH-1");
|
||||
carton.setOverseasWarehouseId("99");
|
||||
carton.setSourceReturnNo("RT-20260928-001");
|
||||
carton.setCartonMark("RC-LAX-001");
|
||||
carton.setStatus("AVAILABLE");
|
||||
@@ -176,6 +220,7 @@ class CargoDiscardServiceTest {
|
||||
|
||||
assertEquals(1, result.size());
|
||||
assertEquals("RETURN_CARTON", result.get(0).sourceType());
|
||||
assertEquals("洛杉矶仓", result.get(0).overseasWarehouseName());
|
||||
assertEquals(901L, result.get(0).returnCartonId());
|
||||
assertEquals("RC-LAX-001", result.get(0).cartonMark());
|
||||
assertEquals(4, result.get(0).availablePackages());
|
||||
|
||||
Reference in New Issue
Block a user