From d33922ad3568564c844509541e4cb21c5abb7f39 Mon Sep 17 00:00:00 2001 From: ouyangxq <775153912@qq.com> Date: Sat, 12 Sep 2026 13:55:48 +0800 Subject: [PATCH] fix overseas return inventory flow --- .../service/SecurityEventOutboxService.java | 2 +- .../SecurityEventOutboxServiceTest.java | 5 +- .../mapper/ReturnInventoryBalanceMapper.java | 7 + .../service/ReturnInventoryService.java | 8 +- .../service/ReturnInventoryServiceTest.java | 134 ++++++++++++++++++ 5 files changed, 150 insertions(+), 6 deletions(-) create mode 100644 lastleg-service/src/test/java/com/baiyue/lastleg/returnreceipt/service/ReturnInventoryServiceTest.java diff --git a/iam/src/main/java/com/baiyue/iam/service/SecurityEventOutboxService.java b/iam/src/main/java/com/baiyue/iam/service/SecurityEventOutboxService.java index 674748ef8..6d5266658 100644 --- a/iam/src/main/java/com/baiyue/iam/service/SecurityEventOutboxService.java +++ b/iam/src/main/java/com/baiyue/iam/service/SecurityEventOutboxService.java @@ -24,7 +24,7 @@ import java.util.stream.Collectors; public class SecurityEventOutboxService { private static final List OVERSEAS_AUTHORITY_PREFIXES = List.of( "platform:overseas-warehouse:", "platform:overseas-business:", - "platform:overseas-master-data:", "merchant:warehouse:", + "platform:overseas-master-data:", "merchant:warehouse:", "merchant:overseas-warehouse:", "merchant:cross-border:", "platform:finance:", "lastleg:"); @Resource private JdbcTemplate jdbc; @Resource private ObjectMapper objectMapper; diff --git a/iam/src/test/java/com/baiyue/iam/service/SecurityEventOutboxServiceTest.java b/iam/src/test/java/com/baiyue/iam/service/SecurityEventOutboxServiceTest.java index b66d458b7..b7e7f89c9 100644 --- a/iam/src/test/java/com/baiyue/iam/service/SecurityEventOutboxServiceTest.java +++ b/iam/src/test/java/com/baiyue/iam/service/SecurityEventOutboxServiceTest.java @@ -84,7 +84,7 @@ class SecurityEventOutboxServiceTest { .thenReturn(List.of("PLATFORM_ADMIN")); when(authorizationQueries.findPermissionsByIdentity(101L, 0L, "ADMIN")) .thenReturn(List.of("platform:overseas-warehouse:orders:view", "platform:iam:users:view", - "lastleg:orders:view")); + "merchant:overseas-warehouse:return-inventory:list", "lastleg:orders:view")); service.permissionChanged(101L, 0L, "ADMIN", "ROLE_CHANGED"); @@ -96,7 +96,8 @@ class SecurityEventOutboxServiceTest { envelope.payload(), IamSecurityEventsV1.PermissionVersionChanged.class); assertThat(payload.roles()).containsExactly("PLATFORM_ADMIN"); assertThat(payload.authorities()).containsExactlyInAnyOrder( - "platform:overseas-warehouse:orders:view", "lastleg:orders:view"); + "platform:overseas-warehouse:orders:view", + "merchant:overseas-warehouse:return-inventory:list", "lastleg:orders:view"); assertThat(payload.authorities()).doesNotContain("platform:iam:users:view"); } } diff --git a/lastleg-service/src/main/java/com/baiyue/lastleg/returnreceipt/mapper/ReturnInventoryBalanceMapper.java b/lastleg-service/src/main/java/com/baiyue/lastleg/returnreceipt/mapper/ReturnInventoryBalanceMapper.java index 18ef9bfa0..9aaeeb487 100644 --- a/lastleg-service/src/main/java/com/baiyue/lastleg/returnreceipt/mapper/ReturnInventoryBalanceMapper.java +++ b/lastleg-service/src/main/java/com/baiyue/lastleg/returnreceipt/mapper/ReturnInventoryBalanceMapper.java @@ -6,6 +6,7 @@ import org.apache.ibatis.annotations.Insert; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Select; +import org.apache.ibatis.annotations.Update; import java.util.List; @@ -20,6 +21,12 @@ public interface ReturnInventoryBalanceMapper extends BaseMapper=0") + int adjustCartonQuantity(@Param("customerId") String customerId, @Param("warehouseId") String warehouseId, + @Param("skuCode") String skuCode, @Param("cartons") long cartons); + @Select("") diff --git a/lastleg-service/src/main/java/com/baiyue/lastleg/returnreceipt/service/ReturnInventoryService.java b/lastleg-service/src/main/java/com/baiyue/lastleg/returnreceipt/service/ReturnInventoryService.java index b6ff1ff3e..03e3ba1e6 100644 --- a/lastleg-service/src/main/java/com/baiyue/lastleg/returnreceipt/service/ReturnInventoryService.java +++ b/lastleg-service/src/main/java/com/baiyue/lastleg/returnreceipt/service/ReturnInventoryService.java @@ -65,7 +65,8 @@ public class ReturnInventoryService { long total = lines.stream().mapToLong(ReceiptLine::quantity).sum(); ReturnInventoryCarton carton = new ReturnInventoryCarton(); carton.setId(cartonId); carton.setCustomerId(order.getCustomerId()); carton.setOverseasWarehouseId(order.getOverseasWarehouseId()); - carton.setSourceReceiptId(receipt.getId()); carton.setSourceBatchNo(receipt.getReceiptBatchNo()); carton.setCartonMark(cartonMark); + carton.setSourceReceiptId(receipt.getId()); carton.setSourceBatchNo(receipt.getReceiptBatchNo()); + carton.setSourceReturnNo(order.getReturnNo()); carton.setCartonMark(cartonMark); carton.setStatus(AVAILABLE); carton.setSkuTypes(lines.size()); carton.setTotalQuantity(total); carton.setCreatedAt(now); carton.setUpdatedAt(now); cartons.insert(carton); for (ReceiptLine line : lines) { @@ -264,8 +265,9 @@ public class ReturnInventoryService { // A SKU's carton count is the number of available physical cartons containing it: // remove fully emptied source cartons, then add the one merged target carton. long cartonDelta = 1L - emptiedBySku.getOrDefault(entry.getKey(), 0); - balances.adjust(IdWorker.getId(), customerId, target.getOverseasWarehouseId(), entry.getKey(), productNames.get(entry.getKey()), 0, - -entry.getValue(), cartonDelta); + if (balances.adjustCartonQuantity(customerId, target.getOverseasWarehouseId(), entry.getKey(), cartonDelta) != 1) { + throw new IllegalStateException("退货合箱库存箱数更新失败: " + entry.getKey()); + } } operation.setResultCartonId(targetId); operation.setUpdatedAt(now); operations.updateById(operation); return new ReturnInventoryResponses.MergeResult(target.getCartonMark(), requested.keySet().iterator().next(), total, label(target)); diff --git a/lastleg-service/src/test/java/com/baiyue/lastleg/returnreceipt/service/ReturnInventoryServiceTest.java b/lastleg-service/src/test/java/com/baiyue/lastleg/returnreceipt/service/ReturnInventoryServiceTest.java new file mode 100644 index 000000000..dc5f6338b --- /dev/null +++ b/lastleg-service/src/test/java/com/baiyue/lastleg/returnreceipt/service/ReturnInventoryServiceTest.java @@ -0,0 +1,134 @@ +package com.baiyue.lastleg.returnreceipt.service; + +import com.baiyue.lastleg.pda.security.PdaWarehouseIdentity; +import com.baiyue.lastleg.returnreceipt.dto.ReturnInventoryRequests; +import com.baiyue.lastleg.returnreceipt.entity.ReturnInventoryCarton; +import com.baiyue.lastleg.returnreceipt.entity.ReturnInventoryCartonItem; +import com.baiyue.lastleg.returnreceipt.entity.ReturnOrder; +import com.baiyue.lastleg.returnreceipt.entity.ReturnReceipt; +import com.baiyue.lastleg.returnreceipt.mapper.ReturnInventoryBalanceMapper; +import com.baiyue.lastleg.returnreceipt.mapper.ReturnInventoryCartonItemMapper; +import com.baiyue.lastleg.returnreceipt.mapper.ReturnInventoryCartonMapper; +import com.baiyue.lastleg.returnreceipt.mapper.ReturnInventoryMovementMapper; +import com.baiyue.lastleg.returnreceipt.mapper.ReturnInventoryOperationMapper; +import com.baiyue.lastleg.returnreceipt.mapper.ReturnInventoryQueryMapper; +import com.baiyue.lastleg.returnreceipt.mapper.ReturnOrderMapper; +import com.baiyue.lastleg.returnreceipt.mapper.ReturnReceiptMapper; +import com.baiyue.lastleg.service.LastlegOrderNoGenerator; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.test.util.ReflectionTestUtils; + +import java.math.BigDecimal; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class ReturnInventoryServiceTest { + @Mock ReturnInventoryCartonMapper cartons; + @Mock ReturnInventoryCartonItemMapper cartonItems; + @Mock ReturnInventoryBalanceMapper balances; + @Mock ReturnInventoryOperationMapper operations; + @Mock ReturnInventoryQueryMapper queries; + @Mock ReturnInventoryMovementMapper movements; + @Mock LastlegOrderNoGenerator numbers; + @Mock ReturnOrderMapper orders; + @Mock ReturnReceiptMapper receipts; + private ReturnInventoryService service; + + @BeforeEach + void setUp() { + service = new ReturnInventoryService(); + ReflectionTestUtils.setField(service, "cartons", cartons); + ReflectionTestUtils.setField(service, "cartonItems", cartonItems); + ReflectionTestUtils.setField(service, "balances", balances); + ReflectionTestUtils.setField(service, "operations", operations); + ReflectionTestUtils.setField(service, "queries", queries); + ReflectionTestUtils.setField(service, "movements", movements); + ReflectionTestUtils.setField(service, "numbers", numbers); + ReflectionTestUtils.setField(service, "orders", orders); + ReflectionTestUtils.setField(service, "receiptMapper", receipts); + } + + @Test + void receiptCartonKeepsTheSourceReturnNumber() { + ReturnOrder order = completedOrder(); + ReturnReceipt receipt = new ReturnReceipt(); + receipt.setId(21L); + receipt.setReceiptBatchNo("RB-1"); + when(numbers.next(anyString(), anyString(), anyLong())).thenReturn("RC-1"); + + service.createReceiptCarton(order, receipt, null, + List.of(new ReturnInventoryService.ReceiptLine("SKU-A", "商品A", 2))); + + ArgumentCaptor captor = ArgumentCaptor.forClass(ReturnInventoryCarton.class); + verify(cartons).insert(captor.capture()); + assertThat(captor.getValue().getSourceReturnNo()).isEqualTo("RT-1"); + } + + @Test + void mergeMovesItemsBetweenCartonsWithoutReducingAvailableQuantity() { + ReturnOrder order = completedOrder(); + ReturnReceipt receipt = new ReturnReceipt(); + receipt.setId(21L); + ReturnInventoryCarton source = new ReturnInventoryCarton(); + source.setId(31L); + source.setCustomerId("10001"); + source.setOverseasWarehouseId("42"); + source.setCartonMark("RC-SOURCE"); + source.setStatus(ReturnInventoryService.AVAILABLE); + source.setSkuTypes(2); + source.setTotalQuantity(4L); + ReturnInventoryCartonItem skuA = item(41L, "SKU-A", 2L); + ReturnInventoryCartonItem skuB = item(42L, "SKU-B", 2L); + + when(orders.findByNos("42", List.of("RT-1"))).thenReturn(List.of(order)); + when(receipts.listOrder(11L)).thenReturn(List.of(receipt)); + when(cartons.findReceipt(21L)).thenReturn(source); + when(cartons.lockMark("RC-SOURCE")).thenReturn(source); + when(cartonItems.listCarton(31L)).thenReturn(List.of(skuA, skuB)); + when(numbers.next(anyString(), anyString(), anyLong())).thenReturn("RC-MERGED"); + when(balances.adjustCartonQuantity(anyString(), anyString(), anyString(), anyLong())).thenReturn(1); + + service.merge(new PdaWarehouseIdentity("operator-1", "操作员", 42L), + new ReturnInventoryRequests.Merge(List.of("RT-1"), List.of( + new ReturnInventoryRequests.ValidItem("SKU-A", 1), + new ReturnInventoryRequests.ValidItem("SKU-B", 1)), + BigDecimal.ONE, BigDecimal.ONE, BigDecimal.ONE, BigDecimal.ONE, "merge-key")); + + verify(balances).adjustCartonQuantity("10001", "42", "SKU-A", 1L); + verify(balances).adjustCartonQuantity("10001", "42", "SKU-B", 1L); + assertThat(source.getTotalQuantity()).isEqualTo(2L); + assertThat(source.getStatus()).isEqualTo(ReturnInventoryService.AVAILABLE); + } + + private ReturnOrder completedOrder() { + ReturnOrder order = new ReturnOrder(); + order.setId(11L); + order.setCustomerId("10001"); + order.setReturnNo("RT-1"); + order.setOverseasWarehouseId("42"); + order.setStatus("COMPLETED"); + return order; + } + + private ReturnInventoryCartonItem item(long id, String sku, long quantity) { + ReturnInventoryCartonItem item = new ReturnInventoryCartonItem(); + item.setId(id); + item.setCartonId(31L); + item.setCustomerId("10001"); + item.setSkuCode(sku); + item.setProductNameZh(sku); + item.setQuantity(quantity); + return item; + } +}