From 5a6524ffa5643dd6e906fc0a574f9f4f58315893 Mon Sep 17 00:00:00 2001 From: ping <1017253325@qq.com> Date: Wed, 3 Jun 2026 10:12:51 +0800 Subject: [PATCH] update bill report --- b/bill/finance_order_report.dspy | 45 +++++++++++++++++++---- b/bill/finance_order_report_overview.dspy | 45 +++++++++++++++++++---- 2 files changed, 74 insertions(+), 16 deletions(-) diff --git a/b/bill/finance_order_report.dspy b/b/bill/finance_order_report.dspy index 426f023..79a964d 100644 --- a/b/bill/finance_order_report.dspy +++ b/b/bill/finance_order_report.dspy @@ -115,6 +115,15 @@ def _round_money(v): return round(float(v), 2) +def _is_reverse_op(business_op): + """退订/红冲类操作(BUY_REVERSE 等 *_REVERSE)。 + + 数据中退订账单的 bill_detail 方向与正常单一致(均为贷),仅靠 business_op 区分。 + 退订口径:销售额、利润取反(减少);上游结算/应付供应商仍正向累加(增加)。 + """ + return str(business_op or '').upper().endswith('_REVERSE') + + def _salemode_label(code): if code is None: return None @@ -326,11 +335,13 @@ async def _protocol_snapshot(sor, accounting_orgid, customerid, providerid, prod } -def _estimate_finance(catalog_amount, protocol): +def _estimate_finance(catalog_amount, protocol, is_reverse=False): """未记账:估算本级利润与本级应付(本级成本)。 与已记账口径一致,本级应付取「本级作为采购方(bid)的协议」即 own_*, 对业主机构与分销商统一(own_discount 即上级给本级的折扣/底价)。 + + 退订(is_reverse):利润取反(减少);上游结算仍正向(增加)。 """ catalog_amount = float(catalog_amount or 0) qty = protocol.get('quantity') or 1 @@ -356,6 +367,9 @@ def _estimate_finance(catalog_amount, protocol): if own_price is not None: settle_upstream = float(own_price) * qty + if is_reverse and profit is not None: + profit = -profit + return { 'profit_amount': _round_money(profit), 'settle_upstream_amount': _round_money(settle_upstream), @@ -376,12 +390,14 @@ async def _bill_detail_rows(sor, billid): )) -async def _finance_from_bill_detail(sor, billid, accounting_orgid): +async def _finance_from_bill_detail(sor, billid, accounting_orgid, is_reverse=False): """已记账:从 bill_detail 取本级利润与本级应付。 统一口径(业主机构与分销商一致): - profit_amount = 本级账本「折扣收入/底价收入」贷方合计 - settle_upstream = 本级账本「待结转*销售收入」贷方合计(本级成本/本级应付上级) + + 退订(is_reverse):利润取反(减少);上游结算仍正向累加(增加)。 """ rows = await _bill_detail_rows(sor, billid) profit = 0.0 @@ -408,6 +424,9 @@ async def _finance_from_bill_detail(sor, billid, accounting_orgid): elif subj.startswith(SUPPLIER_SETTLE_PREFIX): settle_upstream += amt + if is_reverse: + profit = -profit + return { 'profit_amount': _round_money(profit), 'settle_upstream_amount': _round_money(settle_upstream) if settle_upstream else _round_money(0), @@ -426,6 +445,10 @@ async def _build_report_row(sor, row, accounting_orgid, is_owner): catalog_amount = _round_money(_row_get(row, 'catalog_amount')) customer_pay = _round_money(_row_get(row, 'customer_pay_amount')) customer_parentid = _row_get(row, 'customer_parentid') + is_reverse = _is_reverse_op(_row_get(row, 'business_op')) + if is_reverse and customer_pay is not None: + # 退订:客户实付(销售额)取负,使汇总相应减少 + customer_pay = _round_money(-customer_pay) protocol = await _protocol_snapshot( sor, accounting_orgid, customerid, providerid, productid, bill_date, quantity, @@ -434,9 +457,9 @@ async def _build_report_row(sor, row, accounting_orgid, is_owner): bill_state = _row_get(row, 'bill_state') if str(bill_state) == '1': - amounts = await _finance_from_bill_detail(sor, bill_id, accounting_orgid) + amounts = await _finance_from_bill_detail(sor, bill_id, accounting_orgid, is_reverse) else: - amounts = _estimate_finance(catalog_amount, protocol) + amounts = _estimate_finance(catalog_amount, protocol, is_reverse) amounts['bill_detail_legs'] = [] product_name = _row_get(row, 'product_name') @@ -638,14 +661,20 @@ def _customer_segment(customer_parentid, accounting_orgid, reseller_id_set): async def _bill_finance_amounts(sor, row, accounting_orgid): - """单笔:销售额、本级利润、本级应付上级/供应商。""" + """单笔:销售额、本级利润、本级应付上级/供应商。 + + 退订(BUY_REVERSE 等):销售额、利润取负(减少);应付上级仍正向(增加)。 + """ sales = float(_row_get(row, 'customer_pay_amount') or 0) bill_id = _row_get(row, 'bill_id') bill_state = _row_get(row, 'bill_state') quantity = int(_row_get(row, 'quantity') or 1) + is_reverse = _is_reverse_op(_row_get(row, 'business_op')) + if is_reverse: + sales = -sales if str(bill_state) == '1': - fin = await _finance_from_bill_detail(sor, bill_id, accounting_orgid) + fin = await _finance_from_bill_detail(sor, bill_id, accounting_orgid, is_reverse) else: catalog = float(_row_get(row, 'catalog_amount') or 0) protocol = await _protocol_snapshot( @@ -657,7 +686,7 @@ async def _bill_finance_amounts(sor, row, accounting_orgid): _row_get(row, 'bill_date'), quantity, ) - fin = _estimate_finance(catalog, protocol) + fin = _estimate_finance(catalog, protocol, is_reverse) profit = float(fin.get('profit_amount') or 0) settle = float(fin.get('settle_upstream_amount') or 0) @@ -769,7 +798,7 @@ async def _fetch_bill_row_via_R(sor, bill_id): 'customer_pay_amount': b.get('amount'), 'quantity': b.get('quantity'), 'order_date': o.get('order_date'), - 'business_op': o.get('business_op'), + 'business_op': b.get('business_op') or o.get('business_op'), 'servicename': o.get('servicename'), 'customer_name': cust.get('orgname'), 'customer_parentid': cust.get('parentid'), diff --git a/b/bill/finance_order_report_overview.dspy b/b/bill/finance_order_report_overview.dspy index 2049911..0202ca3 100644 --- a/b/bill/finance_order_report_overview.dspy +++ b/b/bill/finance_order_report_overview.dspy @@ -115,6 +115,15 @@ def _round_money(v): return round(float(v), 2) +def _is_reverse_op(business_op): + """退订/红冲类操作(BUY_REVERSE 等 *_REVERSE)。 + + 数据中退订账单的 bill_detail 方向与正常单一致(均为贷),仅靠 business_op 区分。 + 退订口径:销售额、利润取反(减少);上游结算/应付供应商仍正向累加(增加)。 + """ + return str(business_op or '').upper().endswith('_REVERSE') + + def _salemode_label(code): if code is None: return None @@ -326,11 +335,13 @@ async def _protocol_snapshot(sor, accounting_orgid, customerid, providerid, prod } -def _estimate_finance(catalog_amount, protocol): +def _estimate_finance(catalog_amount, protocol, is_reverse=False): """未记账:估算本级利润与本级应付(本级成本)。 与已记账口径一致,本级应付取「本级作为采购方(bid)的协议」即 own_*, 对业主机构与分销商统一(own_discount 即上级给本级的折扣/底价)。 + + 退订(is_reverse):利润取反(减少);上游结算仍正向(增加)。 """ catalog_amount = float(catalog_amount or 0) qty = protocol.get('quantity') or 1 @@ -356,6 +367,9 @@ def _estimate_finance(catalog_amount, protocol): if own_price is not None: settle_upstream = float(own_price) * qty + if is_reverse and profit is not None: + profit = -profit + return { 'profit_amount': _round_money(profit), 'settle_upstream_amount': _round_money(settle_upstream), @@ -376,12 +390,14 @@ async def _bill_detail_rows(sor, billid): )) -async def _finance_from_bill_detail(sor, billid, accounting_orgid): +async def _finance_from_bill_detail(sor, billid, accounting_orgid, is_reverse=False): """已记账:从 bill_detail 取本级利润与本级应付。 统一口径(业主机构与分销商一致): - profit_amount = 本级账本「折扣收入/底价收入」贷方合计 - settle_upstream = 本级账本「待结转*销售收入」贷方合计(本级成本/本级应付上级) + + 退订(is_reverse):利润取反(减少);上游结算仍正向累加(增加)。 """ rows = await _bill_detail_rows(sor, billid) profit = 0.0 @@ -408,6 +424,9 @@ async def _finance_from_bill_detail(sor, billid, accounting_orgid): elif subj.startswith(SUPPLIER_SETTLE_PREFIX): settle_upstream += amt + if is_reverse: + profit = -profit + return { 'profit_amount': _round_money(profit), 'settle_upstream_amount': _round_money(settle_upstream) if settle_upstream else _round_money(0), @@ -426,6 +445,10 @@ async def _build_report_row(sor, row, accounting_orgid, is_owner): catalog_amount = _round_money(_row_get(row, 'catalog_amount')) customer_pay = _round_money(_row_get(row, 'customer_pay_amount')) customer_parentid = _row_get(row, 'customer_parentid') + is_reverse = _is_reverse_op(_row_get(row, 'business_op')) + if is_reverse and customer_pay is not None: + # 退订:客户实付(销售额)取负,使汇总相应减少 + customer_pay = _round_money(-customer_pay) protocol = await _protocol_snapshot( sor, accounting_orgid, customerid, providerid, productid, bill_date, quantity, @@ -434,9 +457,9 @@ async def _build_report_row(sor, row, accounting_orgid, is_owner): bill_state = _row_get(row, 'bill_state') if str(bill_state) == '1': - amounts = await _finance_from_bill_detail(sor, bill_id, accounting_orgid) + amounts = await _finance_from_bill_detail(sor, bill_id, accounting_orgid, is_reverse) else: - amounts = _estimate_finance(catalog_amount, protocol) + amounts = _estimate_finance(catalog_amount, protocol, is_reverse) amounts['bill_detail_legs'] = [] product_name = _row_get(row, 'product_name') @@ -638,14 +661,20 @@ def _customer_segment(customer_parentid, accounting_orgid, reseller_id_set): async def _bill_finance_amounts(sor, row, accounting_orgid): - """单笔:销售额、本级利润、本级应付上级/供应商。""" + """单笔:销售额、本级利润、本级应付上级/供应商。 + + 退订(BUY_REVERSE 等):销售额、利润取负(减少);应付上级仍正向(增加)。 + """ sales = float(_row_get(row, 'customer_pay_amount') or 0) bill_id = _row_get(row, 'bill_id') bill_state = _row_get(row, 'bill_state') quantity = int(_row_get(row, 'quantity') or 1) + is_reverse = _is_reverse_op(_row_get(row, 'business_op')) + if is_reverse: + sales = -sales if str(bill_state) == '1': - fin = await _finance_from_bill_detail(sor, bill_id, accounting_orgid) + fin = await _finance_from_bill_detail(sor, bill_id, accounting_orgid, is_reverse) else: catalog = float(_row_get(row, 'catalog_amount') or 0) protocol = await _protocol_snapshot( @@ -657,7 +686,7 @@ async def _bill_finance_amounts(sor, row, accounting_orgid): _row_get(row, 'bill_date'), quantity, ) - fin = _estimate_finance(catalog, protocol) + fin = _estimate_finance(catalog, protocol, is_reverse) profit = float(fin.get('profit_amount') or 0) settle = float(fin.get('settle_upstream_amount') or 0) @@ -769,7 +798,7 @@ async def _fetch_bill_row_via_R(sor, bill_id): 'customer_pay_amount': b.get('amount'), 'quantity': b.get('quantity'), 'order_date': o.get('order_date'), - 'business_op': o.get('business_op'), + 'business_op': b.get('business_op') or o.get('business_op'), 'servicename': o.get('servicename'), 'customer_name': cust.get('orgname'), 'customer_parentid': cust.get('parentid'),