feat(firstleg): expose loading item tax mode

This commit is contained in:
2026-09-08 17:07:06 +08:00
parent 40fe0ba0e1
commit 78a73a1b0e
7 changed files with 74 additions and 4 deletions
@@ -177,6 +177,8 @@ public final class LoadingListDtos {
Long itemId,
Long accountId,
String customerName,
String customsTaxMode,
Boolean taxRefundFlag,
Long orderId,
String orderNo,
Long inboundId,
@@ -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;
@@ -184,7 +184,8 @@ public class LoadingListRepository {
private List<Item> items(List<ItemRow> 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()),
@@ -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'}
@@ -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<String, Object> 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");
@@ -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")
@@ -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;
}
}