fix(finance): include billed sources in trip financial attribution
This commit is contained in:
@@ -28,7 +28,7 @@ public class TripFinanceQueryService {
|
||||
SELECT f.id,f.customer_id,f.business_no,f.source_no,f.fee_item_name,f.source_fee_version,f.currency_code,f.amount,f.status
|
||||
FROM fin_fee_source f WHERE f.source_system='LASTLEG' AND f.business_type='DELIVERY_ORDER'
|
||||
AND f.source_type='OVERSEAS_DELIVERY_FEE' AND f.direction='RECEIVABLE'
|
||||
AND f.review_status='APPROVED' AND f.status IN ('AVAILABLE','SETTLED')
|
||||
AND f.review_status='APPROVED' AND f.status IN ('AVAILABLE','BILLED','SETTLED')
|
||||
AND NOT EXISTS (SELECT 1 FROM fin_fee_source newer WHERE newer.source_system=f.source_system
|
||||
AND newer.source_type=f.source_type AND newer.source_fee_id=f.source_fee_id
|
||||
AND newer.source_fee_version>f.source_fee_version)
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"info": {
|
||||
"title": "车次Finance权威应收与实收只读来源",
|
||||
"version": "1.0.0",
|
||||
"description": "Lastleg以不可变DEPART验证后的客户/派送业务号读取。仅本Finance库;最新已审核有效LASTLEG OVERSEAS_DELIVERY_FEE应收,按币种。实收只取fin_receipt_bill已确认分配;整张已出账账单(ISSUED、PARTIALLY_PAID、PAID)全部来源均明确归属请求车次时才计一次。混合账单、无分配、无有效来源为待分配,不按账单总额或比例估算。最多200业务、2000费用,批量查询。现行应收结算单收款沿关联账单,fin_settlement_payment仅用于应付,不重复叠加。"
|
||||
"description": "Lastleg以不可变DEPART验证后的客户/派送业务号读取。仅本Finance库;最新已审核有效LASTLEG OVERSEAS_DELIVERY_FEE应收(AVAILABLE、正式出账后的BILLED、SETTLED;LOCKED、VOIDED、SUPERSEDED排除),按币种。实收只取fin_receipt_bill已确认分配;整张已出账账单(ISSUED、PARTIALLY_PAID、PAID)全部来源均明确归属请求车次时才计一次。混合账单、无分配、无有效来源为待分配,不按账单总额或比例估算。最多200业务、2000费用,批量查询。现行应收结算单收款沿关联账单,fin_settlement_payment仅用于应付,不重复叠加。"
|
||||
},
|
||||
"paths": {
|
||||
"/internal/finance/v1/trip-receivables/query": {
|
||||
|
||||
@@ -24,6 +24,8 @@ class TripFinanceQueryServiceTest {
|
||||
void bill(long source,int version) {
|
||||
jdbc.update("INSERT INTO fin_bill VALUES(1,1,'BILL-A','CNY','ISSUED')");
|
||||
jdbc.update("INSERT INTO fin_bill_line VALUES(1,1,?,?,'CNY')",source,version);
|
||||
// Fixture models the real ISSUE source state; this does not call the write service.
|
||||
jdbc.update("UPDATE fin_fee_source SET status='BILLED' WHERE id=? AND status='AVAILABLE'",source);
|
||||
}
|
||||
void receipt(String amount) {
|
||||
jdbc.update("INSERT INTO fin_receipt VALUES(1,1,'CONFIRMED','CNY')");
|
||||
@@ -35,6 +37,33 @@ class TripFinanceQueryServiceTest {
|
||||
assertThat(result.totals().get(0).receivableAmount()).isEqualByComparingTo("20");
|
||||
assertThat(result.documents().get(0).allocationStatus()).isEqualTo("ATTRIBUTED");
|
||||
}
|
||||
@Test void legalSourceAndBillStatesKeepReceivableAndRecordedReceiptAcrossLifecycle() {
|
||||
for(String sourceStatus:List.of("AVAILABLE","BILLED","SETTLED"))for(String billStatus:List.of("ISSUED","PARTIALLY_PAID","PAID")) {
|
||||
setup();fee(1,1,"DL-A",1,"CNY","20",sourceStatus);bill(1,1);
|
||||
jdbc.update("UPDATE fin_fee_source SET status=?",sourceStatus);
|
||||
jdbc.update("UPDATE fin_bill SET status=?",billStatus);
|
||||
if(!"ISSUED".equals(billStatus))receipt("PAID".equals(billStatus)?"20":"3.5");
|
||||
var result=service.query(request(true));
|
||||
assertThat(result.sources()).as(sourceStatus+"/"+billStatus).hasSize(1);
|
||||
assertThat(result.sources().get(0).status()).isEqualTo(sourceStatus);
|
||||
assertThat(result.totals().get(0).receivableAmount()).isEqualByComparingTo("20");
|
||||
assertThat(result.documents()).hasSize(1);
|
||||
if("ISSUED".equals(billStatus)) {
|
||||
assertThat(result.totals().get(0).receivedAmount()).isNull();
|
||||
assertThat(result.documents().get(0).allocationStatus()).isEqualTo("PENDING_ALLOCATION");
|
||||
} else {
|
||||
assertThat(result.totals().get(0).receivedAmount()).isEqualByComparingTo("PAID".equals(billStatus)?"20":"3.5");
|
||||
assertThat(result.documents().get(0).allocationStatus()).isEqualTo("ATTRIBUTED");
|
||||
}
|
||||
}
|
||||
}
|
||||
@Test void lockedVoidedAndSupersededSourcesRemainExcluded() {
|
||||
for(String status:List.of("LOCKED","VOIDED","SUPERSEDED")) {
|
||||
setup();fee(1,1,"DL-A",1,"CNY","20",status);bill(1,1);receipt("20");
|
||||
var result=service.query(request(true));assertThat(result.sources()).as(status).isEmpty();
|
||||
assertThat(result.totals()).isEmpty();assertThat(result.documents()).isEmpty();
|
||||
}
|
||||
}
|
||||
@Test void issuedPartialAndPaidBillsKeepTheirConfirmedAllocation() {
|
||||
for(String status:List.of("ISSUED","PARTIALLY_PAID","PAID")) {
|
||||
setup();fee(1,1,"DL-A",1,"CNY","20","AVAILABLE");bill(1,1);
|
||||
@@ -78,8 +107,8 @@ class TripFinanceQueryServiceTest {
|
||||
assertThat(service.query(request(true)).totals().get(0).receivableAmount()).isEqualByComparingTo("0");
|
||||
}
|
||||
@Test void latestVoidedDoesNotFallBackAndOtherCustomerExcluded() {
|
||||
fee(1,1,"DL-A",1,"CNY","20","AVAILABLE");fee(2,1,"DL-A",2,"CNY","20","VOIDED");
|
||||
fee(3,2,"DL-A",1,"CNY","999","AVAILABLE");
|
||||
fee(1,1,"DL-A",1,"CNY","20","BILLED");fee(2,1,"DL-A",2,"CNY","20","VOIDED");
|
||||
fee(3,2,"DL-A",1,"CNY","999","BILLED");
|
||||
var result=service.query(request(true));assertThat(result.sources()).isEmpty();assertThat(result.status()).isEqualTo("PENDING_ALLOCATION");
|
||||
}
|
||||
@Test void currenciesSeparateAndNoReceiptPermissionMasksDocuments() {
|
||||
|
||||
Reference in New Issue
Block a user