完善财务费用来源与柜级分摊
This commit is contained in:
@@ -94,7 +94,7 @@ public class BusinessFeeSourceEventService {
|
||||
number(source.containerBookingId()), trim(source.containerNo()), source.feeItemCode().trim(),
|
||||
source.feeItemName().trim(), source.totalAmount(), source.currency(), trim(source.settlementMode()),
|
||||
source.paidAt(), trim(source.paymentMethod()), trim(source.paymentReferenceNo()),
|
||||
json(source.paymentVoucherFileIds()), source.sourceHash(), json(source));
|
||||
voucherJson(source.paymentVoucherFileIds()), source.sourceHash(), json(source));
|
||||
long id = jdbc.queryForObject("SELECT id FROM fin_fee_source WHERE source_system=? AND source_type=? AND source_fee_id=? AND source_fee_version=?",
|
||||
Long.class, system, source.sourceType(), feeId, source.sourceVersion());
|
||||
if ("PAYABLE".equals(source.direction()) && "DIRECT_PAID".equals(source.settlementMode()))
|
||||
@@ -131,14 +131,14 @@ public class BusinessFeeSourceEventService {
|
||||
|| (value.paymentVoucherFileIds() != null && !value.paymentVoucherFileIds().isEmpty())) {
|
||||
throw error("FIN-EVT-012", "应收来源不得携带供应商或付款事实");
|
||||
}
|
||||
BusinessFeeSourceChangedV1 unsigned = new BusinessFeeSourceChangedV1(value.action(), value.sourceSystem(),
|
||||
BusinessFeeSourceChangedV1 unsigned = new BusinessFeeSourceChangedV1(null, value.sourceSystem(),
|
||||
value.sourceType(), value.sourceFeeId(), value.sourceVersion(), value.customerId(), value.customerName(),
|
||||
value.sourceNo(), value.businessType(), value.businessId(), value.businessNo(), value.orderId(),
|
||||
value.inboundId(), value.inboundCode(), value.containerId(), value.containerBookingId(), value.containerNo(),
|
||||
value.warehouseId(), value.feeItemCode(), value.feeItemName(), value.direction(), value.supplierId(),
|
||||
value.supplierName(), value.supplierCategory(), value.settlementMode(), value.paidAt(), value.paymentMethod(),
|
||||
value.paymentReferenceNo(), value.paymentVoucherFileIds(), value.businessDate(), value.currency(),
|
||||
value.totalAmount(), value.reviewStatus(), value.publicationStatus(), value.sourceStatus(), null);
|
||||
value.totalAmount(), value.reviewStatus(), "PUBLISHED", "ACTIVE", null);
|
||||
String expected = EventPayloadHash.sha256(EventPayloadCanonicalizer.canonicalize(objectMapper.valueToTree(unsigned)));
|
||||
if (!expected.equals(value.sourceHash())) throw error("FIN-EVT-004", "费用来源快照哈希不一致");
|
||||
}
|
||||
@@ -166,10 +166,10 @@ public class BusinessFeeSourceEventService {
|
||||
}
|
||||
|
||||
private void materializeDirectPaidCost(String system, long feeId, BusinessFeeSourceChangedV1 value) {
|
||||
jdbc.update("INSERT INTO fin_cost(tenant_id,cost_no,cost_type,cost_origin,source_system,source_type,source_fee_id,source_fee_version,source_hash,order_id,inbound_id,container_id,supplier_id,supplier_name_snapshot,fee_item_code,fee_item_name,currency_code,cost_amount,voucher_file_ids,paid_at,payment_method,payment_reference_no,status,confirmed_at,created_by,created_by_name,updated_by) VALUES(?,?,'SOURCE_DIRECT_PAID','BUSINESS_DIRECT_PAID',?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,'CONFIRMED',CURRENT_TIMESTAMP(3),0,'系统',0)",
|
||||
jdbc.update("INSERT INTO fin_cost(tenant_id,cost_no,cost_type,cost_origin,source_system,source_type,source_fee_id,source_fee_version,source_hash,order_id,inbound_id,container_id,warehouse_id,supplier_id,supplier_name_snapshot,fee_item_code,fee_item_name,currency_code,cost_amount,voucher_file_ids,paid_at,payment_method,payment_reference_no,status,confirmed_at,created_by,created_by_name,updated_by) VALUES(?,?,'SOURCE_DIRECT_PAID','BUSINESS_DIRECT_PAID',?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,'CONFIRMED',CURRENT_TIMESTAMP(3),0,'系统',0)",
|
||||
positive(value.customerId(), "customerId"), "DIRECT-" + system + '-' + value.sourceType() + '-' + feeId + "-V" + value.sourceVersion(),
|
||||
system, value.sourceType(), feeId, value.sourceVersion(), value.sourceHash(), number(value.orderId()),
|
||||
number(value.inboundId()), number(value.containerId()), positive(value.supplierId(), "supplierId"),
|
||||
number(value.inboundId()), number(value.containerId()), number(value.warehouseId()), positive(value.supplierId(), "supplierId"),
|
||||
value.supplierName(), value.feeItemCode(), value.feeItemName(), value.currency(), value.totalAmount(),
|
||||
json(value.paymentVoucherFileIds()), value.paidAt(), value.paymentMethod(), value.paymentReferenceNo());
|
||||
}
|
||||
@@ -187,6 +187,9 @@ public class BusinessFeeSourceEventService {
|
||||
try { return objectMapper.writeValueAsString(value == null ? List.of() : value); }
|
||||
catch (JsonProcessingException exception) { throw error("FIN-EVT-001", "费用事件序列化失败"); }
|
||||
}
|
||||
private String voucherJson(List<String> values) {
|
||||
return values == null || values.isEmpty() ? null : json(values);
|
||||
}
|
||||
private static Long number(String value) { return blank(value) ? null : positive(value, "business identifier"); }
|
||||
private static long positive(String value, String name) {
|
||||
try { long parsed = Long.parseLong(value); if (parsed > 0) return parsed; }
|
||||
|
||||
@@ -68,9 +68,9 @@ public class FinanceCustomerAccountService {
|
||||
.forEach(row -> metric(result, row, "pendingBilling", "amount"));
|
||||
jdbc.queryForList("SELECT tenant_id customer_id,SUM(received_amount) amount FROM fin_receipt WHERE tenant_id IN (" + placeholders + ") AND status='CONFIRMED' GROUP BY tenant_id", customerIds.toArray())
|
||||
.forEach(row -> metric(result, row, "receivedInPeriod", "amount"));
|
||||
jdbc.queryForList("SELECT customer_id,SUM(amount) amount FROM (SELECT customer_id,amount FROM fin_fee_source WHERE customer_id IN (" + placeholders + ") AND direction='PAYABLE' AND settlement_mode='ON_ACCOUNT' AND review_status='APPROVED' AND currency_code='CNY' AND amount>0 AND status NOT IN ('VOIDED','SUPERSEDED') UNION ALL SELECT tenant_id customer_id,cost_amount amount FROM fin_cost WHERE tenant_id IN (" + placeholders + ") AND cost_origin='FIRSTLEG_DIRECT_PAID' AND status='CONFIRMED') costs GROUP BY customer_id", concat(customerIds, 2))
|
||||
jdbc.queryForList("SELECT customer_id,SUM(amount) amount FROM (SELECT customer_id,amount FROM fin_fee_source WHERE customer_id IN (" + placeholders + ") AND direction='PAYABLE' AND settlement_mode='ON_ACCOUNT' AND review_status='APPROVED' AND currency_code='CNY' AND amount>0 AND status NOT IN ('VOIDED','SUPERSEDED') UNION ALL SELECT tenant_id customer_id,cost_amount amount FROM fin_cost WHERE tenant_id IN (" + placeholders + ") AND cost_origin IN ('FIRSTLEG_DIRECT_PAID','BUSINESS_DIRECT_PAID') AND status='CONFIRMED') costs GROUP BY customer_id", concat(customerIds, 2))
|
||||
.forEach(row -> metric(result, row, "totalCost", "amount"));
|
||||
jdbc.queryForList("SELECT customer_id,SUM(amount) amount FROM (SELECT tenant_id customer_id,cost_amount amount FROM fin_cost WHERE tenant_id IN (" + placeholders + ") AND cost_origin='FIRSTLEG_DIRECT_PAID' AND status='CONFIRMED' UNION ALL SELECT tenant_id customer_id,paid_amount amount FROM fin_settlement WHERE tenant_id IN (" + placeholders + ") AND direction='PAYABLE' AND status<>'VOIDED') paid_costs GROUP BY customer_id", concat(customerIds, 2))
|
||||
jdbc.queryForList("SELECT customer_id,SUM(amount) amount FROM (SELECT tenant_id customer_id,cost_amount amount FROM fin_cost WHERE tenant_id IN (" + placeholders + ") AND cost_origin IN ('FIRSTLEG_DIRECT_PAID','BUSINESS_DIRECT_PAID') AND status='CONFIRMED' UNION ALL SELECT tenant_id customer_id,paid_amount amount FROM fin_settlement WHERE tenant_id IN (" + placeholders + ") AND direction='PAYABLE' AND status<>'VOIDED') paid_costs GROUP BY customer_id", concat(customerIds, 2))
|
||||
.forEach(row -> metric(result, row, "paidCost", "amount"));
|
||||
jdbc.queryForList("SELECT customer_id,SUM(amount) amount FROM (SELECT tenant_id customer_id,outstanding_amount amount FROM fin_settlement WHERE tenant_id IN (" + placeholders + ") AND direction='PAYABLE' AND status<>'VOIDED' UNION ALL SELECT f.customer_id,f.amount FROM fin_fee_source f WHERE f.customer_id IN (" + placeholders + ") AND f.direction='PAYABLE' AND f.settlement_mode='ON_ACCOUNT' AND f.review_status='APPROVED' AND f.currency_code='CNY' AND f.amount>0 AND f.status NOT IN ('VOIDED','SUPERSEDED') AND NOT EXISTS (SELECT 1 FROM fin_settlement_line sl JOIN fin_settlement s ON s.id=sl.settlement_id AND s.tenant_id=sl.tenant_id WHERE sl.tenant_id=f.customer_id AND sl.fee_source_id=f.id AND s.direction='PAYABLE' AND s.status<>'VOIDED')) payable_obligations GROUP BY customer_id", concat(customerIds, 2))
|
||||
.forEach(row -> metric(result, row, "outstandingPayable", "amount"));
|
||||
|
||||
@@ -34,6 +34,7 @@ import java.util.Comparator;
|
||||
import java.util.HexFormat;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@Service
|
||||
public class FinancePhase1OperationService {
|
||||
@@ -69,8 +70,8 @@ public class FinancePhase1OperationService {
|
||||
"(SELECT COALESCE(SUM(outstanding_amount),0) FROM fin_settlement ar WHERE ar.tenant_id=c.customer_id AND ar.direction='RECEIVABLE' AND ar.status<>'VOIDED') outstanding_receivable," +
|
||||
"(SELECT COALESCE(SUM(amount),0) FROM fin_fee_source f WHERE f.customer_id=c.customer_id AND f.direction='RECEIVABLE' AND f.review_status='APPROVED' AND f.currency_code='CNY' AND f.amount>0 AND f.status='AVAILABLE' AND NOT EXISTS(SELECT 1 FROM fin_bill_line bl JOIN fin_bill b ON b.id=bl.bill_id AND b.tenant_id=bl.tenant_id WHERE bl.tenant_id=f.customer_id AND bl.source_id=f.id AND bl.source_version=f.source_fee_version AND b.status<>'VOIDED')) pending_billing," +
|
||||
"(SELECT COALESCE(SUM(received_amount),0) FROM fin_receipt r WHERE r.tenant_id=c.customer_id AND r.status='CONFIRMED') received_period," +
|
||||
"((SELECT COALESCE(SUM(amount),0) FROM fin_fee_source f WHERE f.customer_id=c.customer_id AND f.direction='PAYABLE' AND f.settlement_mode='ON_ACCOUNT' AND f.review_status='APPROVED' AND f.currency_code='CNY' AND f.amount>0 AND f.status NOT IN ('VOIDED','SUPERSEDED'))+(SELECT COALESCE(SUM(cost_amount),0) FROM fin_cost x WHERE x.tenant_id=c.customer_id AND x.cost_origin='FIRSTLEG_DIRECT_PAID' AND x.status='CONFIRMED')) total_cost," +
|
||||
"((SELECT COALESCE(SUM(cost_amount),0) FROM fin_cost x WHERE x.tenant_id=c.customer_id AND x.cost_origin='FIRSTLEG_DIRECT_PAID' AND x.status='CONFIRMED')+(SELECT COALESCE(SUM(paid_amount),0) FROM fin_settlement x WHERE x.tenant_id=c.customer_id AND x.direction='PAYABLE' AND x.status<>'VOIDED')) paid_cost," +
|
||||
"((SELECT COALESCE(SUM(amount),0) FROM fin_fee_source f WHERE f.customer_id=c.customer_id AND f.direction='PAYABLE' AND f.settlement_mode='ON_ACCOUNT' AND f.review_status='APPROVED' AND f.currency_code='CNY' AND f.amount>0 AND f.status NOT IN ('VOIDED','SUPERSEDED'))+(SELECT COALESCE(SUM(cost_amount),0) FROM fin_cost x WHERE x.tenant_id=c.customer_id AND x.cost_origin IN ('FIRSTLEG_DIRECT_PAID','BUSINESS_DIRECT_PAID') AND x.status='CONFIRMED')) total_cost," +
|
||||
"((SELECT COALESCE(SUM(cost_amount),0) FROM fin_cost x WHERE x.tenant_id=c.customer_id AND x.cost_origin IN ('FIRSTLEG_DIRECT_PAID','BUSINESS_DIRECT_PAID') AND x.status='CONFIRMED')+(SELECT COALESCE(SUM(paid_amount),0) FROM fin_settlement x WHERE x.tenant_id=c.customer_id AND x.direction='PAYABLE' AND x.status<>'VOIDED')) paid_cost," +
|
||||
"((SELECT COALESCE(SUM(outstanding_amount),0) FROM fin_settlement x WHERE x.tenant_id=c.customer_id AND x.direction='PAYABLE' AND x.status<>'VOIDED')+(SELECT COALESCE(SUM(f.amount),0) FROM fin_fee_source f WHERE f.customer_id=c.customer_id AND f.direction='PAYABLE' AND f.settlement_mode='ON_ACCOUNT' AND f.review_status='APPROVED' AND f.currency_code='CNY' AND f.amount>0 AND f.status NOT IN ('VOIDED','SUPERSEDED') AND NOT EXISTS(SELECT 1 FROM fin_settlement_line sl JOIN fin_settlement s ON s.id=sl.settlement_id AND s.tenant_id=sl.tenant_id WHERE sl.tenant_id=f.customer_id AND sl.fee_source_id=f.id AND s.direction='PAYABLE' AND s.status<>'VOIDED'))) outstanding_payable," +
|
||||
"GREATEST(COALESCE((SELECT MAX(updated_at) FROM fin_bill b WHERE b.tenant_id=c.customer_id),'1970-01-01'),COALESCE((SELECT MAX(updated_at) FROM fin_receipt r WHERE r.tenant_id=c.customer_id),'1970-01-01'),COALESCE((SELECT MAX(updated_at) FROM fin_cost x WHERE x.tenant_id=c.customer_id),'1970-01-01'),COALESCE((SELECT MAX(updated_at) FROM fin_fee_source f WHERE f.customer_id=c.customer_id),'1970-01-01'),COALESCE((SELECT MAX(updated_at) FROM fin_settlement x WHERE x.tenant_id=c.customer_id),'1970-01-01')) last_changed_at" + base + " ORDER BY last_changed_at DESC,c.customer_id DESC LIMIT ? OFFSET ?";
|
||||
Object[] args = java.util.Arrays.copyOf(filterArgs, filterArgs.length + 2);
|
||||
@@ -89,7 +90,7 @@ public class FinancePhase1OperationService {
|
||||
List<RawLedger> rows = new ArrayList<>();
|
||||
rows.addAll(jdbc.query("SELECT id,bill_no,created_at,total_amount,created_by,created_by_name,remark FROM fin_bill WHERE tenant_id=? AND status<>'VOIDED'", (rs, n) -> raw(rs, "SETTLEMENT_ISSUED", "BILL", "bill_no", "total_amount", null), customerId));
|
||||
rows.addAll(jdbc.query("SELECT id,receipt_no,received_at received_time,received_amount,created_by,created_by_name,remark FROM fin_receipt WHERE tenant_id=? AND status='CONFIRMED'", (rs, n) -> raw(rs, "RECEIPT", "RECEIPT", "receipt_no", null, "received_amount"), customerId));
|
||||
rows.addAll(jdbc.query("SELECT id,cost_no,created_at,cost_amount,created_by,created_by_name,remark FROM fin_cost WHERE tenant_id=? AND cost_origin='FIRSTLEG_DIRECT_PAID' AND status='CONFIRMED'", (rs, n) -> raw(rs, "SUPPLIER_DIRECT_PAID", "DIRECT_PAID_COST", "cost_no", null, null), customerId));
|
||||
rows.addAll(jdbc.query("SELECT id,cost_no,created_at,cost_amount,created_by,created_by_name,remark FROM fin_cost WHERE tenant_id=? AND cost_origin IN ('FIRSTLEG_DIRECT_PAID','BUSINESS_DIRECT_PAID') AND status='CONFIRMED'", (rs, n) -> raw(rs, "SUPPLIER_DIRECT_PAID", "DIRECT_PAID_COST", "cost_no", null, null), customerId));
|
||||
rows.addAll(jdbc.query("SELECT p.id,CONCAT(s.settlement_no,'-',p.id) payment_no,p.created_at,p.amount cost_amount,p.created_by,p.created_by_name,p.remark FROM fin_settlement_payment p JOIN fin_settlement s ON s.id=p.settlement_id AND s.tenant_id=p.tenant_id WHERE p.tenant_id=? AND p.transaction_type='PAYMENT' AND s.direction='PAYABLE' AND s.status<>'VOIDED'", (rs, n) -> raw(rs, "SUPPLIER_ON_ACCOUNT_PAYMENT", "AP_PAYMENT", "payment_no", null, null), customerId));
|
||||
rows.sort(Comparator.comparing(RawLedger::occurredAt).thenComparing(RawLedger::id));
|
||||
BigDecimal outstanding = BigDecimal.ZERO;
|
||||
@@ -185,8 +186,8 @@ public class FinancePhase1OperationService {
|
||||
throw error("FIN-REQ-400", adjustment ? "费用调整请求无效" : "费用编辑请求无效");
|
||||
}
|
||||
FeeSourceDetail before = getFinanceFeeSource(feeSourceId);
|
||||
if ("FIRSTLEG".equals(before.sourceSystem())) {
|
||||
throw error("FIN-FEE-409", "Firstleg费用必须在入库或柜次费用明细中调整,Finance不可直接修改业务金额");
|
||||
if (Set.of("FIRSTLEG", "LASTLEG").contains(before.sourceSystem())) {
|
||||
throw error("FIN-FEE-409", "业务费用必须回到对应业务费用明细调整,Finance不可直接修改来源金额");
|
||||
}
|
||||
String operation = adjustment ? "FEE_SOURCE_ADJUST" : "FEE_SOURCE_EDIT";
|
||||
String path = "/api/platform/finance/v1/fee-sources/" + feeSourceId + (adjustment ? "/adjust" : "");
|
||||
@@ -222,6 +223,9 @@ public class FinancePhase1OperationService {
|
||||
throw error("FIN-REQ-400", "费用操作请求无效");
|
||||
}
|
||||
FeeSourceDetail before = getFinanceFeeSource(feeSourceId);
|
||||
if ("LASTLEG".equals(before.sourceSystem())) {
|
||||
throw error("FIN-FEE-409", "海外业务费用必须在海外费用来源中作废,Finance只消费作废事件");
|
||||
}
|
||||
if (!"FIRSTLEG".equals(before.sourceSystem())) {
|
||||
return transitionFeeSource(feeSourceId, operatorId, key, request, "FEE_SOURCE_VOID",
|
||||
"VOIDED", List.of("DRAFT", "PENDING", "REJECTED", "APPROVED"), true);
|
||||
|
||||
@@ -74,8 +74,8 @@ public class FinanceQueryService {
|
||||
"AND NOT EXISTS (SELECT 1 FROM fin_bill_line bl JOIN fin_bill b ON b.id=bl.bill_id AND b.tenant_id=bl.tenant_id " +
|
||||
"WHERE bl.tenant_id=f.customer_id AND bl.source_id=f.id AND b.status<>'VOIDED')) pending_billing," +
|
||||
"((SELECT COALESCE(SUM(f.amount),0) FROM fin_fee_source f WHERE f.customer_id=? AND f.direction='PAYABLE' AND f.settlement_mode='ON_ACCOUNT' AND f.review_status='APPROVED' AND f.currency_code='CNY' AND f.amount>0 AND f.status NOT IN ('VOIDED','SUPERSEDED'))+" +
|
||||
"(SELECT COALESCE(SUM(c.cost_amount),0) FROM fin_cost c WHERE c.tenant_id=? AND c.cost_origin='FIRSTLEG_DIRECT_PAID' AND c.status='CONFIRMED')) total_cost," +
|
||||
"((SELECT COALESCE(SUM(c.cost_amount),0) FROM fin_cost c WHERE c.tenant_id=? AND c.cost_origin='FIRSTLEG_DIRECT_PAID' AND c.status='CONFIRMED')+" +
|
||||
"(SELECT COALESCE(SUM(c.cost_amount),0) FROM fin_cost c WHERE c.tenant_id=? AND c.cost_origin IN ('FIRSTLEG_DIRECT_PAID','BUSINESS_DIRECT_PAID') AND c.status='CONFIRMED')) total_cost," +
|
||||
"((SELECT COALESCE(SUM(c.cost_amount),0) FROM fin_cost c WHERE c.tenant_id=? AND c.cost_origin IN ('FIRSTLEG_DIRECT_PAID','BUSINESS_DIRECT_PAID') AND c.status='CONFIRMED')+" +
|
||||
"(SELECT COALESCE(SUM(ps.paid_amount),0) FROM fin_settlement ps WHERE ps.tenant_id=? AND ps.direction='PAYABLE' AND ps.status<>'VOIDED')) paid_cost," +
|
||||
"((SELECT COALESCE(SUM(os.outstanding_amount),0) FROM fin_settlement os WHERE os.tenant_id=? AND os.direction='PAYABLE' AND os.status<>'VOIDED')+" +
|
||||
"(SELECT COALESCE(SUM(f.amount),0) FROM fin_fee_source f WHERE f.customer_id=? AND f.direction='PAYABLE' AND f.settlement_mode='ON_ACCOUNT' AND f.review_status='APPROVED' AND f.currency_code='CNY' AND f.amount>0 AND f.status NOT IN ('VOIDED','SUPERSEDED') " +
|
||||
|
||||
Reference in New Issue
Block a user