fix(order): preserve tax mode in remainder child orders
This commit is contained in:
@@ -142,6 +142,7 @@ public class OrderFulfillmentSplitService {
|
|||||||
child.setReceivingWarehouseName(root.getReceivingWarehouseName()); child.setChannelCode(root.getChannelCode());
|
child.setReceivingWarehouseName(root.getReceivingWarehouseName()); child.setChannelCode(root.getChannelCode());
|
||||||
child.setChannelName(root.getChannelName()); child.setDestinationType(root.getDestinationType());
|
child.setChannelName(root.getChannelName()); child.setDestinationType(root.getDestinationType());
|
||||||
child.setFulfillmentType(root.getFulfillmentType());
|
child.setFulfillmentType(root.getFulfillmentType());
|
||||||
|
child.setTaxMode(root.getTaxMode());
|
||||||
child.setInboundCode(remainderInboundCode(root, sequence));
|
child.setInboundCode(remainderInboundCode(root, sequence));
|
||||||
child.setCreatedBy(operatorId); child.setUpdatedBy(operatorId);
|
child.setCreatedBy(operatorId); child.setUpdatedBy(operatorId);
|
||||||
if (orders.insert(child) != 1) throw new BizException(ResultCode.CONFLICT, "余量子订单创建失败");
|
if (orders.insert(child) != 1) throw new BizException(ResultCode.CONFLICT, "余量子订单创建失败");
|
||||||
|
|||||||
+43
-3
@@ -24,11 +24,13 @@ import static org.assertj.core.api.Assertions.assertThat;
|
|||||||
/** Real mapper + pagination interceptor: tax filtering applies to COUNT and records before LIMIT. */
|
/** Real mapper + pagination interceptor: tax filtering applies to COUNT and records before LIMIT. */
|
||||||
class UnifiedOrderTaxModePaginationTest {
|
class UnifiedOrderTaxModePaginationTest {
|
||||||
private OrderRepository repository;
|
private OrderRepository repository;
|
||||||
|
private JdbcTemplate jdbc;
|
||||||
|
private OrderMapper mapper;
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setup() throws Exception {
|
void setup() throws Exception {
|
||||||
var dataSource = new JdbcDataSource();
|
var dataSource = new JdbcDataSource();
|
||||||
dataSource.setURL("jdbc:h2:mem:tax-page-" + UUID.randomUUID() + ";MODE=MySQL;DB_CLOSE_DELAY=-1");
|
dataSource.setURL("jdbc:h2:mem:tax-page-" + UUID.randomUUID() + ";MODE=MySQL;DB_CLOSE_DELAY=-1");
|
||||||
var jdbc = new JdbcTemplate(dataSource);
|
jdbc = new JdbcTemplate(dataSource);
|
||||||
var columns = new ArrayList<String>();
|
var columns = new ArrayList<String>();
|
||||||
for (Class<?> type = OrderEntity.class; type != Object.class; type = type.getSuperclass()) {
|
for (Class<?> type = OrderEntity.class; type != Object.class; type = type.getSuperclass()) {
|
||||||
for (var field : type.getDeclaredFields()) {
|
for (var field : type.getDeclaredFields()) {
|
||||||
@@ -41,7 +43,9 @@ class UnifiedOrderTaxModePaginationTest {
|
|||||||
: valueType == java.math.BigDecimal.class ? "DECIMAL(20,6)"
|
: valueType == java.math.BigDecimal.class ? "DECIMAL(20,6)"
|
||||||
: valueType == java.time.LocalDateTime.class ? "TIMESTAMP"
|
: valueType == java.time.LocalDateTime.class ? "TIMESTAMP"
|
||||||
: valueType == java.time.LocalDate.class ? "DATE" : "VARCHAR(2000)";
|
: valueType == java.time.LocalDate.class ? "DATE" : "VARCHAR(2000)";
|
||||||
columns.add(name + " " + sqlType + (name.equals("id") ? " PRIMARY KEY" : ""));
|
columns.add(name + " " + sqlType + (name.equals("id") ? " PRIMARY KEY"
|
||||||
|
: name.equals("deleted") || name.equals("version") ? " DEFAULT 0"
|
||||||
|
: name.equals("created_at") ? " DEFAULT CURRENT_TIMESTAMP" : ""));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
jdbc.execute("CREATE TABLE ord_order (" + String.join(",", columns) + ")");
|
jdbc.execute("CREATE TABLE ord_order (" + String.join(",", columns) + ")");
|
||||||
@@ -58,7 +62,8 @@ class UnifiedOrderTaxModePaginationTest {
|
|||||||
factory.setDataSource(dataSource); factory.setConfiguration(config); factory.setPlugins(pagination);
|
factory.setDataSource(dataSource); factory.setConfiguration(config); factory.setPlugins(pagination);
|
||||||
var session = new SqlSessionTemplate(factory.getObject());
|
var session = new SqlSessionTemplate(factory.getObject());
|
||||||
repository = new OrderRepository();
|
repository = new OrderRepository();
|
||||||
ReflectionTestUtils.setField(repository, "orderMapper", session.getMapper(OrderMapper.class));
|
mapper = session.getMapper(OrderMapper.class);
|
||||||
|
ReflectionTestUtils.setField(repository, "orderMapper", mapper);
|
||||||
}
|
}
|
||||||
@Test
|
@Test
|
||||||
void taxModeFiltersCountsAndPagesWithoutLeakingAnotherCustomerOrDeletedRows() {
|
void taxModeFiltersCountsAndPagesWithoutLeakingAnotherCustomerOrDeletedRows() {
|
||||||
@@ -69,6 +74,41 @@ class UnifiedOrderTaxModePaginationTest {
|
|||||||
assertPage(null, 1, 4, 6);
|
assertPage(null, 1, 4, 6);
|
||||||
assertPage(null, 2, 4, 4);
|
assertPage(null, 2, 4, 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@org.junit.jupiter.params.ParameterizedTest
|
||||||
|
@org.junit.jupiter.params.provider.ValueSource(strings = {"TAX_REFUND", "BUY_DECLARATION"})
|
||||||
|
void realSplitChildIsPersistedAndIncludedInTaxFilteredParentChildPages(String taxMode) {
|
||||||
|
jdbc.update("UPDATE ord_order SET tax_mode=?,order_role='MASTER',order_no='TAX-PARENT',inbound_code='TAX-INBOUND',version=0 WHERE id=6", taxMode);
|
||||||
|
var root = mapper.findOwned(11L, 6L);
|
||||||
|
var ledger = org.mockito.Mockito.mock(com.baiyue.order.mapper.OrderFulfillmentSplitMapper.class);
|
||||||
|
org.mockito.Mockito.when(ledger.findByIdempotency(11L, 6L, "tax-split")).thenReturn(List.of());
|
||||||
|
org.mockito.Mockito.when(ledger.lockOrder(11L, 6L)).thenReturn(root);
|
||||||
|
org.mockito.Mockito.when(ledger.insert(org.mockito.ArgumentMatchers.any(com.baiyue.order.entity.OrderFulfillmentSplitEntity.class))).thenReturn(1);
|
||||||
|
org.mockito.Mockito.when(ledger.incrementParentVersion(11L, 6L, 0, 9L)).thenReturn(1);
|
||||||
|
var splitter = new com.baiyue.order.service.OrderFulfillmentSplitService();
|
||||||
|
ReflectionTestUtils.setField(splitter, "splits", ledger);
|
||||||
|
ReflectionTestUtils.setField(splitter, "orders", mapper);
|
||||||
|
ReflectionTestUtils.setField(splitter, "json", new com.fasterxml.jackson.databind.ObjectMapper());
|
||||||
|
var result = splitter.split(new com.baiyue.order.dto.OrderSplitDtos.CreateRequest(
|
||||||
|
11L, 6L, 6L, 701L, 801L, 2, 1, 1, 0, "tax-split", "op-tax-split",
|
||||||
|
List.of(new com.baiyue.order.dto.OrderSplitDtos.QuantityLine(99L, 2)), "operator"), 9L);
|
||||||
|
long childId = result.remainderChild().orderId();
|
||||||
|
assertThat(mapper.findOwned(11L, childId).getTaxMode()).isEqualTo(taxMode);
|
||||||
|
var pageIds = new ArrayList<Long>();
|
||||||
|
for (long page = 1; page <= 2; page++) {
|
||||||
|
var query = new UnifiedOrderQuery(null, "TAX-PARENT", null, null, null, null, null, null, null,
|
||||||
|
null, null, null, null, null, null, null, null, null, null, null, page, 1, taxMode);
|
||||||
|
var filtered = repository.unifiedPage(11L, query, List.of(com.baiyue.order.entity.OrderStatus.APPROVED));
|
||||||
|
assertThat(filtered.total()).isEqualTo(2);
|
||||||
|
assertThat(filtered.records()).singleElement().satisfies(row -> {
|
||||||
|
pageIds.add(row.getId());
|
||||||
|
assertThat(row.getTaxMode()).isEqualTo(taxMode);
|
||||||
|
assertThat(row.getTenantId()).isEqualTo(11L);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
assertThat(pageIds).containsExactlyInAnyOrder(childId, 6L);
|
||||||
|
assertThat(mapper.findOwned(22L, childId)).isNull();
|
||||||
|
}
|
||||||
private void assertPage(String taxMode, long page, long total, long expectedId) {
|
private void assertPage(String taxMode, long page, long total, long expectedId) {
|
||||||
var query = new UnifiedOrderQuery(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, page, 1, taxMode);
|
var query = new UnifiedOrderQuery(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, page, 1, taxMode);
|
||||||
var result = repository.unifiedPage(11L, query, List.of(com.baiyue.order.entity.OrderStatus.APPROVED));
|
var result = repository.unifiedPage(11L, query, List.of(com.baiyue.order.entity.OrderStatus.APPROVED));
|
||||||
|
|||||||
@@ -68,6 +68,25 @@ class OrderFulfillmentSplitServiceTest {
|
|||||||
verify(splits).incrementParentVersion(10, 100, 3, 9);
|
verify(splits).incrementParentVersion(10, 100, 3, 9);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@org.junit.jupiter.params.ParameterizedTest
|
||||||
|
@org.junit.jupiter.params.provider.ValueSource(strings = {"TAX_REFUND", "BUY_DECLARATION"})
|
||||||
|
void remainderInheritsRootTaxModeThroughRealSplitCommand(String taxMode) {
|
||||||
|
OrderEntity parent = parent();
|
||||||
|
parent.setTaxMode(taxMode);
|
||||||
|
when(splits.findByIdempotency(10, 100, "tax-child")).thenReturn(List.of());
|
||||||
|
when(splits.lockOrder(10, 100)).thenReturn(parent);
|
||||||
|
when(orders.insert(any(OrderEntity.class))).thenReturn(1);
|
||||||
|
when(splits.insert(any(OrderFulfillmentSplitEntity.class))).thenReturn(1);
|
||||||
|
when(splits.incrementParentVersion(10, 100, 3, 9)).thenReturn(1);
|
||||||
|
var result = service.split(request(100L, 20, 8, 12, "tax-child"), 9);
|
||||||
|
ArgumentCaptor<OrderEntity> child = ArgumentCaptor.forClass(OrderEntity.class);
|
||||||
|
verify(orders).insert(child.capture());
|
||||||
|
assertThat(child.getValue().getId()).isEqualTo(result.remainderChild().orderId());
|
||||||
|
assertThat(child.getValue().getTaxMode()).isEqualTo(taxMode);
|
||||||
|
assertThat(child.getValue().getTenantId()).isEqualTo(parent.getTenantId());
|
||||||
|
assertThat(child.getValue().getRootOrderId()).isEqualTo(parent.getId());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void rejectsConcurrentParentVersionChangeWithoutPublishingNextVersion() {
|
void rejectsConcurrentParentVersionChangeWithoutPublishingNextVersion() {
|
||||||
OrderEntity parent = parent();
|
OrderEntity parent = parent();
|
||||||
|
|||||||
Reference in New Issue
Block a user