From 78a73a1b0e5c8a685705fdabcb4d12b4a22769ce Mon Sep 17 00:00:00 2001 From: ouyangxq <775153912@qq.com> Date: Tue, 8 Sep 2026 17:07:06 +0800 Subject: [PATCH] feat(firstleg): expose loading item tax mode --- .../baiyue/firstleg/dto/LoadingListDtos.java | 2 + .../mapper/LoadingListQueryMapper.java | 11 ++++- .../repository/LoadingListRepository.java | 3 +- .../resources/openapi/loading-lists-v1.yaml | 2 + .../LoadingListControllerContractTest.java | 11 ++++- .../LoadingListQueryMapperContractTest.java | 5 +++ .../repository/LoadingListRepositoryTest.java | 44 +++++++++++++++++++ 7 files changed, 74 insertions(+), 4 deletions(-) diff --git a/firstleg/src/main/java/com/baiyue/firstleg/dto/LoadingListDtos.java b/firstleg/src/main/java/com/baiyue/firstleg/dto/LoadingListDtos.java index 58dbe8e4e..ee98f5bc5 100644 --- a/firstleg/src/main/java/com/baiyue/firstleg/dto/LoadingListDtos.java +++ b/firstleg/src/main/java/com/baiyue/firstleg/dto/LoadingListDtos.java @@ -177,6 +177,8 @@ public final class LoadingListDtos { Long itemId, Long accountId, String customerName, + String customsTaxMode, + Boolean taxRefundFlag, Long orderId, String orderNo, Long inboundId, diff --git a/firstleg/src/main/java/com/baiyue/firstleg/mapper/LoadingListQueryMapper.java b/firstleg/src/main/java/com/baiyue/firstleg/mapper/LoadingListQueryMapper.java index 97c77cfaa..db85de82e 100644 --- a/firstleg/src/main/java/com/baiyue/firstleg/mapper/LoadingListQueryMapper.java +++ b/firstleg/src/main/java/com/baiyue/firstleg/mapper/LoadingListQueryMapper.java @@ -168,7 +168,14 @@ public interface LoadingListQueryMapper { @Param("operatorName") String operatorName); @Select(""" - SELECT i.id AS item_id, i.account_id, i.customer_name, i.order_id, i.order_no, + SELECT i.id AS item_id, i.account_id, i.customer_name, + inbound.tax_mode AS customs_tax_mode, + CASE inbound.tax_mode + WHEN 'TAX_REFUND' THEN TRUE + WHEN 'BUY_DECLARATION' THEN FALSE + ELSE NULL + END AS tax_refund_flag, + i.order_id, i.order_no, i.inbound_id, i.inbound_detail_id, i.inbound_code, b.container_no, i.sku_code, i.destination_warehouse_name, i.product_name_zh, product_name_en, product_image_url, i.package_count, @@ -409,6 +416,8 @@ public interface LoadingListQueryMapper { private Long itemId; private Long accountId; private String customerName; + private String customsTaxMode; + private Boolean taxRefundFlag; private Long orderId; private String orderNo; private Long inboundId; diff --git a/firstleg/src/main/java/com/baiyue/firstleg/repository/LoadingListRepository.java b/firstleg/src/main/java/com/baiyue/firstleg/repository/LoadingListRepository.java index 33289ea30..16503bed5 100644 --- a/firstleg/src/main/java/com/baiyue/firstleg/repository/LoadingListRepository.java +++ b/firstleg/src/main/java/com/baiyue/firstleg/repository/LoadingListRepository.java @@ -184,7 +184,8 @@ public class LoadingListRepository { private List items(List rows) { return rows.stream().map(row -> new Item(row.getItemId(), row.getAccountId(), row.getCustomerName(), - row.getOrderId(), row.getOrderNo(), row.getInboundId(), row.getInboundDetailId(), + row.getCustomsTaxMode(), row.getTaxRefundFlag(), row.getOrderId(), row.getOrderNo(), + row.getInboundId(), row.getInboundDetailId(), row.getInboundCode(), row.getContainerNo(), row.getSkuCode(), row.getDestinationWarehouseName(), row.getProductNameZh(), row.getProductNameEn(), row.getProductImageUrl(), integer(row.getPackageCount()), integer(row.getPalletCount()), diff --git a/firstleg/src/main/resources/openapi/loading-lists-v1.yaml b/firstleg/src/main/resources/openapi/loading-lists-v1.yaml index 4a9021268..8dce29cbf 100644 --- a/firstleg/src/main/resources/openapi/loading-lists-v1.yaml +++ b/firstleg/src/main/resources/openapi/loading-lists-v1.yaml @@ -361,6 +361,8 @@ components: itemId: {$ref: '#/components/schemas/LongId'} accountId: {$ref: '#/components/schemas/LongId'} customerName: {type: string, nullable: true, description: 客户名称快照} + customsTaxMode: {type: string, enum: [TAX_REFUND, BUY_DECLARATION], nullable: true, description: 订单/入仓片段冻结的报关方式;不根据退税文件推断} + taxRefundFlag: {type: boolean, nullable: true, description: customsTaxMode为TAX_REFUND时为true、BUY_DECLARATION时为false;缺少权威事实时为null} orderId: {$ref: '#/components/schemas/LongId'} orderNo: {type: string, description: 订单号快照} inboundId: {$ref: '#/components/schemas/LongId'} diff --git a/firstleg/src/test/java/com/baiyue/firstleg/controller/LoadingListControllerContractTest.java b/firstleg/src/test/java/com/baiyue/firstleg/controller/LoadingListControllerContractTest.java index 21563a38f..aca53edea 100644 --- a/firstleg/src/test/java/com/baiyue/firstleg/controller/LoadingListControllerContractTest.java +++ b/firstleg/src/test/java/com/baiyue/firstleg/controller/LoadingListControllerContractTest.java @@ -16,6 +16,7 @@ import org.springframework.security.access.prepost.PreAuthorize; import java.lang.reflect.Method; import java.lang.reflect.RecordComponent; import java.nio.charset.StandardCharsets; +import java.util.List; import java.util.Map; import jakarta.validation.constraints.NotNull; @@ -145,9 +146,15 @@ class LoadingListControllerContractTest { .doesNotContainKey("internalContainerNo"); assertThat(properties(schemas, "ConfirmedSummary")) .containsKeys("planId", "containerBookingId", "loadingListNo"); - assertThat(properties(schemas, "LoadingItem")).containsKeys( - "itemId", "accountId", "inboundId", "inboundDetailId", "skuCode", + Map loadingItem = properties(schemas, "LoadingItem"); + assertThat(loadingItem).containsKeys( + "itemId", "accountId", "customsTaxMode", "taxRefundFlag", "inboundId", "inboundDetailId", "skuCode", "destinationWarehouse", "productQuantity", "frozenAt"); + assertThat(stringMap(loadingItem.get("customsTaxMode"))) + .containsEntry("nullable", true) + .containsEntry("enum", List.of("TAX_REFUND", "BUY_DECLARATION")); + assertThat(stringMap(loadingItem.get("taxRefundFlag"))) + .containsEntry("nullable", true); assertThat(properties(schemas, "OperationHistory")).containsKeys( "historyId", "itemId", "operationId", "actionType", "fromStatus", "toStatus", "beforeSnapshot", "afterSnapshot", "operatorId", "occurredAt"); diff --git a/firstleg/src/test/java/com/baiyue/firstleg/repository/LoadingListQueryMapperContractTest.java b/firstleg/src/test/java/com/baiyue/firstleg/repository/LoadingListQueryMapperContractTest.java index f24d554ff..659703cb6 100644 --- a/firstleg/src/test/java/com/baiyue/firstleg/repository/LoadingListQueryMapperContractTest.java +++ b/firstleg/src/test/java/com/baiyue/firstleg/repository/LoadingListQueryMapperContractTest.java @@ -59,6 +59,11 @@ class LoadingListQueryMapperContractTest { String items = sql("selectItems", Select.class); assertThat(items) .contains("i.order_id", "i.order_no", "b.container_no") + .contains("inbound.tax_mode AS customs_tax_mode") + .contains("WHEN 'TAX_REFUND' THEN TRUE") + .contains("WHEN 'BUY_DECLARATION' THEN FALSE") + .contains("ELSE NULL") + .doesNotContain("fl_customs_container_document", "fl_customs_container_document_link") .contains("b.record_status='ACTIVE'") .contains("NULLIF(i.pallet_count,0)") .contains("fl_inbound_weight_result weight") diff --git a/firstleg/src/test/java/com/baiyue/firstleg/repository/LoadingListRepositoryTest.java b/firstleg/src/test/java/com/baiyue/firstleg/repository/LoadingListRepositoryTest.java index f9996b3f3..63c6fffe0 100644 --- a/firstleg/src/test/java/com/baiyue/firstleg/repository/LoadingListRepositoryTest.java +++ b/firstleg/src/test/java/com/baiyue/firstleg/repository/LoadingListRepositoryTest.java @@ -5,6 +5,7 @@ import com.baiyue.firstleg.dto.LoadingListDtos.ConfirmedQuery; import com.baiyue.firstleg.dto.LoadingListDtos.PreloadQuery; import com.baiyue.firstleg.mapper.LoadingListQueryMapper; import com.baiyue.firstleg.mapper.LoadingListQueryMapper.AggregateRow; +import com.baiyue.firstleg.mapper.LoadingListQueryMapper.ItemRow; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -96,4 +97,47 @@ class LoadingListRepositoryTest { var confirmed = repository.preloadDetail(row).summary().readiness(); assertThat(confirmed.readyForPda()).isTrue(); } + + @Test + void mapsFrozenCustomsTaxFactsPerInboundAcrossCustomers() { + AggregateRow aggregate = new AggregateRow(); + aggregate.setPlanId(41L); + aggregate.setContainerBookingId(61L); + aggregate.setPlanNo("LP-41"); + aggregate.setPlanStatus("COMPLETED"); + aggregate.setVersion(3); + ItemRow refund = item(101L, 11L, 201L, "IN-REFUND", "TAX_REFUND", true); + ItemRow declaration = item(102L, 12L, 202L, "IN-BUY", "BUY_DECLARATION", false); + ItemRow unknown = item(103L, 12L, 203L, "IN-UNKNOWN", null, null); + when(mapper.selectItems(41L)).thenReturn(List.of(refund, declaration, unknown)); + when(mapper.selectHistories(41L)).thenReturn(List.of()); + + var items = repository.confirmedDetail(aggregate).items(); + + assertThat(items).extracting(value -> value.inboundCode(), value -> value.accountId(), + value -> value.customsTaxMode(), value -> value.taxRefundFlag()) + .containsExactly( + org.assertj.core.groups.Tuple.tuple("IN-REFUND", 11L, "TAX_REFUND", true), + org.assertj.core.groups.Tuple.tuple("IN-BUY", 12L, "BUY_DECLARATION", false), + org.assertj.core.groups.Tuple.tuple("IN-UNKNOWN", 12L, null, null)); + } + + private static ItemRow item(long itemId, long accountId, long inboundId, String inboundCode, + String customsTaxMode, Boolean taxRefundFlag) { + ItemRow row = new ItemRow(); + row.setItemId(itemId); + row.setAccountId(accountId); + row.setCustomerName("customer-" + accountId); + row.setCustomsTaxMode(customsTaxMode); + row.setTaxRefundFlag(taxRefundFlag); + row.setOrderId(300L + inboundId); + row.setOrderNo("ORDER-" + inboundId); + row.setInboundId(inboundId); + row.setInboundDetailId(400L + inboundId); + row.setInboundCode(inboundCode); + row.setPackageCount(1); + row.setLoadSequence(1); + row.setItemVersion(0); + return row; + } }