提交
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
package com.baiyue.finance.service;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
/** 导出业务枚举沿用财务页面中文口径,不改动筛选值、币种及业务编号。 */
|
||||
final class FinanceExportLabels {
|
||||
/** 中文导出映射仅提供静态转换。 */
|
||||
private FinanceExportLabels() { }
|
||||
private static final Map<String, String> STATUSES = Map.ofEntries(
|
||||
Map.entry("ACTIVE", "正常"),
|
||||
Map.entry("DISABLED", "停用"),
|
||||
Map.entry("INACTIVE", "停用"),
|
||||
Map.entry("DRAFT", "草稿"),
|
||||
Map.entry("PENDING", "待审核"),
|
||||
Map.entry("PENDING_REVIEW", "待审核"),
|
||||
Map.entry("APPROVED", "已审核"),
|
||||
Map.entry("ISSUED", "已出账"),
|
||||
Map.entry("REJECTED", "已驳回"),
|
||||
Map.entry("UNPAID", "未支付"),
|
||||
Map.entry("PARTIALLY_PAID", "部分结清"),
|
||||
Map.entry("PAID", "已结清"),
|
||||
Map.entry("VOIDED", "已作废"),
|
||||
Map.entry("AVAILABLE", "待结算"),
|
||||
Map.entry("LOCKED", "结算锁定"),
|
||||
Map.entry("BILLED", "已入账"),
|
||||
Map.entry("SETTLED", "已结算"),
|
||||
Map.entry("RECORDED", "已登记"),
|
||||
Map.entry("CONFIRMED", "已确认"),
|
||||
Map.entry("PENDING_CONFIRMATION", "待客户确认"),
|
||||
Map.entry("NOT_REQUIRED", "无需确认"));
|
||||
private static final Map<String, String> SOURCES = Map.ofEntries(
|
||||
Map.entry("INBOUND_FEE", "入库费用"),
|
||||
Map.entry("ORDER_FEE", "订单费用"),
|
||||
Map.entry("CONTAINER_FEE", "装柜费用"),
|
||||
Map.entry("CONTAINER_COST", "整柜成本"),
|
||||
Map.entry("MANUAL_COST", "人工成本"),
|
||||
Map.entry("STORAGE_FEE", "仓储费用"),
|
||||
Map.entry("OVERSEAS_INBOUND_FEE", "海外入仓费用"),
|
||||
Map.entry("OVERSEAS_PROCUREMENT_FEE", "海外采购费用"),
|
||||
Map.entry("OVERSEAS_TRUCK_FEE", "本土卡车费用"),
|
||||
Map.entry("OVERSEAS_LOOSEBOARD_FEE", "本土散板费用"),
|
||||
Map.entry("OVERSEAS_WAREHOUSE_FEE", "海外仓服务费"),
|
||||
Map.entry("OVERSEAS_CONTAINER_FEE", "海外柜子费用"),
|
||||
Map.entry("OVERSEAS_ORDER_FEE", "尾程订单费用"),
|
||||
Map.entry("OVERSEAS_DELIVERY_FEE", "尾程派送费用"),
|
||||
Map.entry("OVERSEAS_RELABEL_FEE", "换标费用"));
|
||||
private static final Map<String, String> BUSINESSES = Map.ofEntries(
|
||||
Map.entry("RECHARGE", "充值"),
|
||||
Map.entry("DEDUCTION", "扣款"),
|
||||
Map.entry("CREDIT_LIMIT", "授信额度调整"),
|
||||
Map.entry("FREEZE", "冻结"),
|
||||
Map.entry("UNFREEZE", "解冻"),
|
||||
Map.entry("CONSUMPTION", "消费扣款"),
|
||||
Map.entry("REFUND", "退款"),
|
||||
Map.entry("RECONCILIATION_CREATED", "生成对账单"),
|
||||
Map.entry("SETTLEMENT_ISSUED", "结算单生效"),
|
||||
Map.entry("RECEIPT", "收款"),
|
||||
Map.entry("PAYMENT", "付款"),
|
||||
Map.entry("SUPPLIER_DIRECT_PAID", "供应商线下直付"),
|
||||
Map.entry("SUPPLIER_ON_ACCOUNT_PAYMENT", "供应商账期付款"),
|
||||
Map.entry("ADJUSTMENT", "账务调整"),
|
||||
Map.entry("VOID", "作废"),
|
||||
Map.entry("BILL", "对账单"),
|
||||
Map.entry("COST", "成本"),
|
||||
Map.entry("SETTLEMENT", "结算"),
|
||||
Map.entry("CREATE", "创建"),
|
||||
Map.entry("APPROVE", "审核通过"),
|
||||
Map.entry("REJECT", "审核驳回"),
|
||||
Map.entry("UPDATE", "更新"),
|
||||
Map.entry("FEE_SOURCE_EDIT", "编辑费用"),
|
||||
Map.entry("FEE_SOURCE_SUBMIT", "提交审核"),
|
||||
Map.entry("FEE_SOURCE_REVIEW_APPROVE", "审核通过"),
|
||||
Map.entry("FEE_SOURCE_REVIEW_REJECT", "审核驳回"),
|
||||
Map.entry("FEE_SOURCE_ADJUST", "调整费用"),
|
||||
Map.entry("FEE_SOURCE_VOID", "作废费用"),
|
||||
Map.entry("FEE_SOURCE_PUBLISHED", "费用同步"),
|
||||
Map.entry("FEE_SOURCE_VERSION_ADJUST", "费用调整同步"),
|
||||
Map.entry("RECONCILIATION_CREATE", "生成对账单"),
|
||||
Map.entry("RECONCILIATION_ADJUST", "调整对账单"),
|
||||
Map.entry("BILL_CREATE", "创建账单"),
|
||||
Map.entry("BILL_ISSUE", "出账"),
|
||||
Map.entry("BILL_ADJUST", "调整账单"),
|
||||
Map.entry("BILL_VOID", "作废账单"),
|
||||
Map.entry("SETTLEMENT_AR_CREATE", "生成应收结算单"),
|
||||
Map.entry("SETTLEMENT_AP_CREATE", "生成应付结算单"),
|
||||
Map.entry("SETTLEMENT_PAYMENT_CREATE", "登记收付款"),
|
||||
Map.entry("RECEIPT_CREATE", "登记收款"),
|
||||
Map.entry("COST_CREATE", "登记成本"),
|
||||
Map.entry("REOPEN", "重开"));
|
||||
|
||||
/** 转换账户、审核及结算状态。 */
|
||||
static String status(String value) { return label(value, STATUSES, "未知状态"); }
|
||||
/** 转换费用来源类型。 */
|
||||
static String source(String value) { return label(value, SOURCES, "未知来源类型"); }
|
||||
/** 转换账务及资金流水业务类型。 */
|
||||
static String business(String value) { return label(value, BUSINESSES, "未知业务类型"); }
|
||||
/** 转换应收应付方向。 */
|
||||
static String direction(String value) { return label(value, Map.of("RECEIVABLE", "应收", "PAYABLE", "应付"), "未知方向"); }
|
||||
|
||||
/** 空值保持空白,已是中文的历史快照保持原文;新增英文枚举不直接泄露到业务报表。 */
|
||||
private static String label(String value, Map<String, String> labels, String fallback) {
|
||||
if (value == null || value.isBlank()) return value;
|
||||
String code = value.trim().toUpperCase(Locale.ROOT);
|
||||
return labels.getOrDefault(code, value.matches(".*[一-鿿].*") ? value : fallback);
|
||||
}
|
||||
}
|
||||
+11
-18
@@ -339,35 +339,39 @@ public class FinanceExportProviderService {
|
||||
} catch (GeneralSecurityException exception) { throw new IllegalStateException("HmacSHA256 is unavailable", exception); }
|
||||
}
|
||||
|
||||
/** 导出账户摘要,状态使用中文展示。 */
|
||||
private Map<String, Object> accountRow(AccountSummary value) {
|
||||
return row("customerCode", value.customerCode(), "customerName", value.customerName(), "contactName", value.contactName(),
|
||||
"contactPhone", value.contactPhone(), "outstandingReceivable", value.outstandingReceivable(), "pendingBilling", value.pendingBilling(),
|
||||
"receivedInPeriod", value.receivedInPeriod(), "totalCost", value.totalCost(), "paidCost", value.paidCost(),
|
||||
"outstandingPayable", value.outstandingPayable(), "status", value.status(), "lastChangedAt", value.lastChangedAt());
|
||||
"outstandingPayable", value.outstandingPayable(), "status", FinanceExportLabels.status(value.status()), "lastChangedAt", value.lastChangedAt());
|
||||
}
|
||||
/** 导出账户流水,转换业务类型并保留金额和编号。 */
|
||||
private Map<String, Object> ledgerRow(LedgerEntry value) {
|
||||
return row("ledgerId", String.valueOf(value.ledgerId()), "occurredAt", value.occurredAt(), "businessType", value.businessType(),
|
||||
return row("ledgerId", String.valueOf(value.ledgerId()), "occurredAt", value.occurredAt(), "businessType", FinanceExportLabels.business(value.businessType()),
|
||||
"documentNo", value.documentNo(), "receivableChange", value.receivableChange(), "payableChange", value.payableChange(),
|
||||
"outstandingReceivableAfter", value.outstandingReceivableAfter(), "operatorName", value.operatorName(), "remark", value.remark());
|
||||
}
|
||||
/** 费用方向、来源类型及审核和结算状态统一展示中文。 */
|
||||
private Map<String, Object> feeRow(FeeSourceSummary source) {
|
||||
return row("customerName", source.customerName(), "direction", source.direction(), "sourceType", source.sourceType(),
|
||||
return row("customerName", source.customerName(), "direction", FinanceExportLabels.direction(source.direction()), "sourceType", FinanceExportLabels.source(source.sourceType()),
|
||||
"sourceFeeId", String.valueOf(source.sourceFeeId()), "sourceVersion", source.sourceVersion(), "inboundCode", source.inboundCode(),
|
||||
"containerNo", source.containerNo(), "businessDate", source.businessDate(), "feeItemName", source.feeItemName(),
|
||||
"quantity", source.quantity(), "unitPrice", source.unitPrice(), "amount", source.amount(),
|
||||
"reviewStatus", source.reviewStatus(), "sourceStatus", source.sourceStatus(), "statementGenerated", source.statementGenerated());
|
||||
"reviewStatus", FinanceExportLabels.status(source.reviewStatus()), "sourceStatus", FinanceExportLabels.status(source.sourceStatus()), "statementGenerated", source.statementGenerated());
|
||||
}
|
||||
/** 对账单导出沿用页面状态口径,是否结清仍传递布尔值。 */
|
||||
private Map<String, Object> statementRow(ReconciliationDetail value) {
|
||||
return row("statementNo", value.statementNo(), "customerName", value.customerName(), "periodStart", value.periodStart(),
|
||||
"periodEnd", value.periodEnd(), "creditDays", value.creditDays(), "currency", value.currency(), "totalReceivable", value.totalReceivable(),
|
||||
"receivedAmount", value.receivedAmount(), "outstandingAmount", value.outstandingAmount(), "settled", value.settled(), "status", value.status(),
|
||||
"receivedAmount", value.receivedAmount(), "outstandingAmount", value.outstandingAmount(), "settled", value.settled(), "status", FinanceExportLabels.status(value.status()),
|
||||
"remark", value.remark(), "createdByName", value.createdByName(), "createdAt", value.createdAt(), "updatedByName", value.updatedByName(), "updatedAt", value.updatedAt());
|
||||
}
|
||||
/** 结算单列表导出使用中文状态,便于财务人员直接阅读。 */
|
||||
private Map<String, Object> settlementRow(SettlementSummary value) {
|
||||
return row("settlementNo", value.settlementNo(), "customerName", value.customerName(), "supplierName", value.supplierName(),
|
||||
"currency", value.currency(), "totalAmount", value.totalAmount(), "paidAmount", value.paidAmount(), "outstandingAmount", value.outstandingAmount(),
|
||||
"businessPeriod", value.businessPeriod(), "generatedDate", value.generatedDate(), "dueDate", value.dueDate(), "status", settlementStatusLabel(value.status()));
|
||||
"businessPeriod", value.businessPeriod(), "generatedDate", value.generatedDate(), "dueDate", value.dueDate(), "status", FinanceExportLabels.status(value.status()));
|
||||
}
|
||||
/** 明细导出与列表保持相同的中文结算状态。 */
|
||||
private Map<String, Object> settlementDetailRow(SettlementSummary summary, SettlementLine line) {
|
||||
@@ -375,18 +379,7 @@ public class FinanceExportProviderService {
|
||||
"feeItemName", line == null ? null : line.feeItemName(), "inboundCode", line == null ? null : line.inboundCode(),
|
||||
"containerNo", line == null ? null : line.containerNo(), "businessDate", line == null ? null : line.businessDate(),
|
||||
"lineAmount", line == null ? null : line.amount(), "totalAmount", summary.totalAmount(), "paidAmount", summary.paidAmount(),
|
||||
"outstandingAmount", summary.outstandingAmount(), "status", settlementStatusLabel(summary.status()));
|
||||
}
|
||||
/** 转换正式结算状态,未知值保留原值以便排查。 */
|
||||
private String settlementStatusLabel(String status) {
|
||||
if (status == null) return null;
|
||||
return switch (status) {
|
||||
case "ISSUED" -> "已出账";
|
||||
case "PARTIALLY_PAID" -> "部分结清";
|
||||
case "PAID" -> "已结清";
|
||||
case "VOIDED" -> "已作废";
|
||||
default -> status;
|
||||
};
|
||||
"outstandingAmount", summary.outstandingAmount(), "status", FinanceExportLabels.status(summary.status()));
|
||||
}
|
||||
private Map<String, Object> receivableStatisticsRow(YearMonth month, ReceivableStatisticsRow value) {
|
||||
return row("statisticsMonth", month.toString(), "customerName", value.customerName(),
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.baiyue.finance.service;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import java.util.List;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class FinanceExportLabelsTest {
|
||||
@Test void translatesStatusSourceAndLedgerFamiliesWithoutLeakingCodes() {
|
||||
assertThat(FinanceExportLabels.status("PARTIALLY_PAID")).isEqualTo("部分结清");
|
||||
assertThat(FinanceExportLabels.status("SETTLED")).isEqualTo("已结算");
|
||||
for (String code : List.of("ACTIVE", "DISABLED", "DRAFT", "PENDING", "PENDING_REVIEW", "APPROVED", "REJECTED", "ISSUED", "PARTIALLY_PAID", "PAID", "VOIDED", "AVAILABLE", "LOCKED", "BILLED")) {
|
||||
assertThat(FinanceExportLabels.status(code)).matches(".*[一-鿿].*").doesNotContain("未知");
|
||||
}
|
||||
for (String code : List.of("INBOUND_FEE", "ORDER_FEE", "CONTAINER_FEE", "CONTAINER_COST", "MANUAL_COST", "STORAGE_FEE", "OVERSEAS_INBOUND_FEE", "OVERSEAS_PROCUREMENT_FEE", "OVERSEAS_TRUCK_FEE", "OVERSEAS_LOOSEBOARD_FEE", "OVERSEAS_WAREHOUSE_FEE", "OVERSEAS_CONTAINER_FEE", "OVERSEAS_ORDER_FEE", "OVERSEAS_DELIVERY_FEE", "OVERSEAS_RELABEL_FEE")) {
|
||||
assertThat(FinanceExportLabels.source(code)).matches(".*[一-鿿].*").doesNotContain("未知");
|
||||
}
|
||||
for (String code : List.of("FEE_SOURCE_EDIT", "FEE_SOURCE_SUBMIT", "FEE_SOURCE_REVIEW_APPROVE", "FEE_SOURCE_REVIEW_REJECT", "FEE_SOURCE_ADJUST", "FEE_SOURCE_VOID", "RECONCILIATION_CREATED", "SETTLEMENT_ISSUED", "RECEIPT", "PAYMENT", "SUPPLIER_DIRECT_PAID", "SUPPLIER_ON_ACCOUNT_PAYMENT", "ADJUSTMENT", "VOID")) {
|
||||
assertThat(FinanceExportLabels.business(code)).matches(".*[一-鿿].*").doesNotContain("未知");
|
||||
}
|
||||
assertThat(FinanceExportLabels.direction("RECEIVABLE")).isEqualTo("应收");
|
||||
for (String code : List.of("RECHARGE", "DEDUCTION", "CREDIT_LIMIT", "FREEZE", "UNFREEZE", "CONSUMPTION", "REFUND")) {
|
||||
assertThat(FinanceExportLabels.business(code)).matches(".*[一-鿿].*").doesNotContain("未知");
|
||||
}
|
||||
assertThat(FinanceExportLabels.direction("PAYABLE")).isEqualTo("应付");
|
||||
}
|
||||
|
||||
@Test void handlesEmptyChineseSnapshotsAndUnknownValues() {
|
||||
assertThat(FinanceExportLabels.status(null)).isNull();
|
||||
assertThat(FinanceExportLabels.status("")).isEmpty();
|
||||
assertThat(FinanceExportLabels.status("已结清")).isEqualTo("已结清");
|
||||
assertThat(FinanceExportLabels.status("NEW_INTERNAL_STATUS")).isEqualTo("未知状态");
|
||||
assertThat(FinanceExportLabels.status(" partially_paid ")).isEqualTo("部分结清");
|
||||
}
|
||||
}
|
||||
+15
-2
@@ -45,6 +45,19 @@ class FinanceExportProviderServiceTest {
|
||||
service.initializeSigningKey();
|
||||
}
|
||||
|
||||
@Test
|
||||
void exportsAccountAndLedgerEnumsAsChineseWithoutChangingFiltersOrReferences() throws Exception {
|
||||
var mapper = new ObjectMapper().findAndRegisterModules();
|
||||
var account = mapper.convertValue(Map.of("customerId", 10001L, "customerCode", "C-10001", "status", "ACTIVE"), com.baiyue.finance.dto.FinancePhase1Dtos.AccountSummary.class);
|
||||
var ledger = mapper.convertValue(Map.of("ledgerId", 1L, "businessType", "SUPPLIER_ON_ACCOUNT_PAYMENT", "documentNo", "AP-1"), com.baiyue.finance.dto.FinancePhase1Dtos.LedgerEntry.class);
|
||||
when(accountService.list(1, 100, null, "ACTIVE", 10001L)).thenReturn(PageEnvelope.success(List.of(account), 1, 100, 1));
|
||||
when(operationService.listDerivedCustomerAccountLedger(10001L, 1, 100, "SUPPLIER_ON_ACCOUNT_PAYMENT", null, null)).thenReturn(PageEnvelope.success(List.of(ledger), 1, 100, 1));
|
||||
var accounts = service.exportBatch(FinanceExportProviderService.CUSTOMER_ACCOUNT_LIST, request(Map.of("customerId", "10001", "status", "ACTIVE"), null, null));
|
||||
var ledgers = service.exportBatch(FinanceExportProviderService.CUSTOMER_ACCOUNT_LEDGER, request(Map.of("customerId", "10001", "businessType", "SUPPLIER_ON_ACCOUNT_PAYMENT"), null, null));
|
||||
assertThat(accounts.rows().get(0)).containsEntry("status", "正常").containsEntry("customerCode", "C-10001");
|
||||
assertThat(ledgers.rows().get(0)).containsEntry("businessType", "供应商账期付款").containsEntry("documentNo", "AP-1");
|
||||
}
|
||||
|
||||
@Test
|
||||
/** 应付结算列表和详情导出均使用中文状态,包括无明细的单据。 */
|
||||
void exportsSettlementStatusesInChinese() {
|
||||
@@ -98,7 +111,7 @@ class FinanceExportProviderServiceTest {
|
||||
|
||||
assertThat(first.columns()).isNotEmpty();
|
||||
assertThat(first.rows()).hasSize(1);
|
||||
assertThat(first.rows().get(0)).containsEntry("statementNo", "REC-501").containsEntry("customerName", "客户A");
|
||||
assertThat(first.rows().get(0)).containsEntry("statementNo", "REC-501").containsEntry("customerName", "客户A").containsEntry("status", "已出账");
|
||||
assertThat(first.totalRows()).isEqualTo(1L);
|
||||
assertThat(continued.columns()).isEmpty();
|
||||
assertThat(continued.rows()).isEmpty();
|
||||
@@ -119,7 +132,7 @@ class FinanceExportProviderServiceTest {
|
||||
var result = service.exportBatch(FinanceExportProviderService.FEE_SOURCE_LIST,
|
||||
request(Map.of("direction", "PAYABLE", "status", "PAID"), null, null));
|
||||
assertThat(result.rows()).singleElement().satisfies(row -> {
|
||||
assertThat(row).containsEntry("sourceStatus", "PAID").containsEntry("sourceFeeId", "9001");
|
||||
assertThat(row).containsEntry("sourceStatus", "已结清").containsEntry("reviewStatus", "已审核").containsEntry("direction", "应付").containsEntry("sourceType", "装柜费用").containsEntry("sourceFeeId", "9001");
|
||||
assertThat(row.get("amount")).isEqualTo(new BigDecimal("300"));
|
||||
});
|
||||
org.mockito.Mockito.verifyNoInteractions(queryService);
|
||||
|
||||
Reference in New Issue
Block a user