diff --git a/finance/src/main/java/com/baiyue/finance/service/TripFinanceQueryService.java b/finance/src/main/java/com/baiyue/finance/service/TripFinanceQueryService.java index e889a9458..197d70a00 100644 --- a/finance/src/main/java/com/baiyue/finance/service/TripFinanceQueryService.java +++ b/finance/src/main/java/com/baiyue/finance/service/TripFinanceQueryService.java @@ -49,7 +49,7 @@ public class TripFinanceQueryService { SELECT b.id,b.tenant_id,b.bill_no,b.currency_code,l.source_id FROM fin_bill_line l JOIN fin_bill b ON b.id=l.bill_id AND b.tenant_id=l.tenant_id JOIN fin_fee_source f ON f.id=l.source_id AND f.customer_id=l.tenant_id AND f.source_fee_version=l.source_version - WHERE l.source_id IN (:ids) AND b.status='ISSUED' + WHERE l.source_id IN (:ids) AND b.status IN ('ISSUED','PARTIALLY_PAID','PAID') """,Map.of("ids",eligible.keySet()),(r,i)->new Object[]{ new Bill(r.getLong("id"),r.getLong("tenant_id"),r.getString("bill_no"),r.getString("currency_code")),r.getLong("source_id")}); var bills=new LinkedHashMap();var byFee=new HashMap>(); diff --git a/finance/src/main/resources/openapi/finance-trip-receivables-v1.yaml b/finance/src/main/resources/openapi/finance-trip-receivables-v1.yaml index 5b15dabc6..5ff65b8c9 100644 --- a/finance/src/main/resources/openapi/finance-trip-receivables-v1.yaml +++ b/finance/src/main/resources/openapi/finance-trip-receivables-v1.yaml @@ -3,7 +3,7 @@ "info": { "title": "车次Finance权威应收与实收只读来源", "version": "1.0.0", - "description": "Lastleg以不可变DEPART验证后的客户/派送业务号读取。仅本Finance库;最新已审核有效LASTLEG OVERSEAS_DELIVERY_FEE应收,按币种。实收只取fin_receipt_bill已确认分配;整张ISSUED账单全部来源均明确归属请求车次时才计一次。混合账单、无分配、无有效来源为待分配,不按账单总额或比例估算。最多200业务、2000费用,批量查询。现行应收结算单收款沿关联账单,fin_settlement_payment仅用于应付,不重复叠加。" + "description": "Lastleg以不可变DEPART验证后的客户/派送业务号读取。仅本Finance库;最新已审核有效LASTLEG OVERSEAS_DELIVERY_FEE应收,按币种。实收只取fin_receipt_bill已确认分配;整张已出账账单(ISSUED、PARTIALLY_PAID、PAID)全部来源均明确归属请求车次时才计一次。混合账单、无分配、无有效来源为待分配,不按账单总额或比例估算。最多200业务、2000费用,批量查询。现行应收结算单收款沿关联账单,fin_settlement_payment仅用于应付,不重复叠加。" }, "paths": { "/internal/finance/v1/trip-receivables/query": { diff --git a/finance/src/test/java/com/baiyue/finance/service/TripFinanceQueryServiceTest.java b/finance/src/test/java/com/baiyue/finance/service/TripFinanceQueryServiceTest.java index b7601d48d..47e4c6ea3 100644 --- a/finance/src/test/java/com/baiyue/finance/service/TripFinanceQueryServiceTest.java +++ b/finance/src/test/java/com/baiyue/finance/service/TripFinanceQueryServiceTest.java @@ -35,17 +35,46 @@ class TripFinanceQueryServiceTest { assertThat(result.totals().get(0).receivableAmount()).isEqualByComparingTo("20"); assertThat(result.documents().get(0).allocationStatus()).isEqualTo("ATTRIBUTED"); } + @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); + String amount="PAID".equals(status)?"20":"3.5";receipt(amount); + jdbc.update("UPDATE fin_bill SET status=?",status); + var result=service.query(request(true)); + assertThat(result.documents()).as(status).hasSize(1); + assertThat(result.documents().get(0).allocationStatus()).isEqualTo("ATTRIBUTED"); + assertThat(result.totals().get(0).receivedAmount()).as(status).isEqualByComparingTo(amount); + } + } + @Test void draftPendingApprovedAndVoidedBillsNeverContributeReceipts() { + for(String status:List.of("DRAFT","PENDING_REVIEW","APPROVED","VOIDED")) { + setup();fee(1,1,"DL-A",1,"CNY","20","AVAILABLE");bill(1,1);receipt("3.5"); + jdbc.update("UPDATE fin_bill SET status=?",status); + var result=service.query(request(true));assertThat(result.documents()).as(status).isEmpty(); + assertThat(result.totals().get(0).receivedAmount()).isNull(); + } + } + @Test void paidBillStillEnforcesCustomerVersionAndCurrencyAttribution() { + for(String change:List.of("source_version=2","tenant_id=2","currency_code='USD'")) { + setup();fee(1,1,"DL-A",1,"CNY","20","AVAILABLE");bill(1,1);receipt("20"); + jdbc.update("UPDATE fin_bill SET status='PAID'");jdbc.update("UPDATE fin_bill_line SET "+change); + assertThat(service.query(request(true)).totals().get(0).receivedAmount()).as(change).isNull(); + } + } @Test void mixedBillNeverProratesReceipt() { fee(1,1,"DL-A",1,"CNY","20","AVAILABLE");bill(1,1);receipt("30"); jdbc.update("INSERT INTO fin_bill_line VALUES(1,1,999,1,'CNY')"); + jdbc.update("UPDATE fin_bill SET status='PAID'"); var result=service.query(request(true));assertThat(result.totals().get(0).receivedAmount()).isNull(); + assertThat(result.documents()).hasSize(1); assertThat(result.documents().get(0).billNo()).isEqualTo("BILL-A"); assertThat(result.documents().get(0).allocationStatus()).isEqualTo("PENDING_ALLOCATION"); } @Test void unbilledOrNoConfirmedReceiptIsUnknownNotZero() { fee(1,1,"DL-A",1,"CNY","0","AVAILABLE"); assertThat(service.query(request(true)).totals().get(0).receivedAmount()).isNull(); - bill(1,1);assertThat(service.query(request(true)).totals().get(0).receivedAmount()).isNull(); + bill(1,1);var unpaid=service.query(request(true));assertThat(unpaid.totals().get(0).receivedAmount()).isNull(); + assertThat(unpaid.documents()).hasSize(1);assertThat(unpaid.documents().get(0).allocationStatus()).isEqualTo("PENDING_ALLOCATION"); assertThat(service.query(request(true)).totals().get(0).receivableAmount()).isEqualByComparingTo("0"); } @Test void latestVoidedDoesNotFallBackAndOtherCustomerExcluded() {