update bill report

This commit is contained in:
ping 2026-06-03 10:12:51 +08:00
parent 38f849d346
commit 5a6524ffa5
2 changed files with 74 additions and 16 deletions

View File

@ -115,6 +115,15 @@ def _round_money(v):
return round(float(v), 2) 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): def _salemode_label(code):
if code is None: if code is None:
return 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_* 与已记账口径一致,本级应付取「本级作为采购方(bid)的协议」即 own_*
对业主机构与分销商统一own_discount 即上级给本级的折扣/底价)。 对业主机构与分销商统一own_discount 即上级给本级的折扣/底价)。
退订is_reverse利润取反减少上游结算仍正向增加
""" """
catalog_amount = float(catalog_amount or 0) catalog_amount = float(catalog_amount or 0)
qty = protocol.get('quantity') or 1 qty = protocol.get('quantity') or 1
@ -356,6 +367,9 @@ def _estimate_finance(catalog_amount, protocol):
if own_price is not None: if own_price is not None:
settle_upstream = float(own_price) * qty settle_upstream = float(own_price) * qty
if is_reverse and profit is not None:
profit = -profit
return { return {
'profit_amount': _round_money(profit), 'profit_amount': _round_money(profit),
'settle_upstream_amount': _round_money(settle_upstream), '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 取本级利润与本级应付。 """已记账:从 bill_detail 取本级利润与本级应付。
统一口径(业主机构与分销商一致): 统一口径(业主机构与分销商一致):
- profit_amount = 本级账本「折扣收入/底价收入」贷方合计 - profit_amount = 本级账本「折扣收入/底价收入」贷方合计
- settle_upstream = 本级账本「待结转*销售收入」贷方合计(本级成本/本级应付上级) - settle_upstream = 本级账本「待结转*销售收入」贷方合计(本级成本/本级应付上级)
退订is_reverse利润取反减少上游结算仍正向累加增加
""" """
rows = await _bill_detail_rows(sor, billid) rows = await _bill_detail_rows(sor, billid)
profit = 0.0 profit = 0.0
@ -408,6 +424,9 @@ async def _finance_from_bill_detail(sor, billid, accounting_orgid):
elif subj.startswith(SUPPLIER_SETTLE_PREFIX): elif subj.startswith(SUPPLIER_SETTLE_PREFIX):
settle_upstream += amt settle_upstream += amt
if is_reverse:
profit = -profit
return { return {
'profit_amount': _round_money(profit), 'profit_amount': _round_money(profit),
'settle_upstream_amount': _round_money(settle_upstream) if settle_upstream else _round_money(0), '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')) catalog_amount = _round_money(_row_get(row, 'catalog_amount'))
customer_pay = _round_money(_row_get(row, 'customer_pay_amount')) customer_pay = _round_money(_row_get(row, 'customer_pay_amount'))
customer_parentid = _row_get(row, 'customer_parentid') 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( protocol = await _protocol_snapshot(
sor, accounting_orgid, customerid, providerid, productid, bill_date, quantity, 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') bill_state = _row_get(row, 'bill_state')
if str(bill_state) == '1': 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: else:
amounts = _estimate_finance(catalog_amount, protocol) amounts = _estimate_finance(catalog_amount, protocol, is_reverse)
amounts['bill_detail_legs'] = [] amounts['bill_detail_legs'] = []
product_name = _row_get(row, 'product_name') 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): async def _bill_finance_amounts(sor, row, accounting_orgid):
"""单笔:销售额、本级利润、本级应付上级/供应商。""" """单笔:销售额、本级利润、本级应付上级/供应商。
退订BUY_REVERSE 等):销售额、利润取负(减少);应付上级仍正向(增加)。
"""
sales = float(_row_get(row, 'customer_pay_amount') or 0) sales = float(_row_get(row, 'customer_pay_amount') or 0)
bill_id = _row_get(row, 'bill_id') bill_id = _row_get(row, 'bill_id')
bill_state = _row_get(row, 'bill_state') bill_state = _row_get(row, 'bill_state')
quantity = int(_row_get(row, 'quantity') or 1) 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': 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: else:
catalog = float(_row_get(row, 'catalog_amount') or 0) catalog = float(_row_get(row, 'catalog_amount') or 0)
protocol = await _protocol_snapshot( protocol = await _protocol_snapshot(
@ -657,7 +686,7 @@ async def _bill_finance_amounts(sor, row, accounting_orgid):
_row_get(row, 'bill_date'), _row_get(row, 'bill_date'),
quantity, quantity,
) )
fin = _estimate_finance(catalog, protocol) fin = _estimate_finance(catalog, protocol, is_reverse)
profit = float(fin.get('profit_amount') or 0) profit = float(fin.get('profit_amount') or 0)
settle = float(fin.get('settle_upstream_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'), 'customer_pay_amount': b.get('amount'),
'quantity': b.get('quantity'), 'quantity': b.get('quantity'),
'order_date': o.get('order_date'), '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'), 'servicename': o.get('servicename'),
'customer_name': cust.get('orgname'), 'customer_name': cust.get('orgname'),
'customer_parentid': cust.get('parentid'), 'customer_parentid': cust.get('parentid'),

View File

@ -115,6 +115,15 @@ def _round_money(v):
return round(float(v), 2) 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): def _salemode_label(code):
if code is None: if code is None:
return 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_* 与已记账口径一致,本级应付取「本级作为采购方(bid)的协议」即 own_*
对业主机构与分销商统一own_discount 即上级给本级的折扣/底价)。 对业主机构与分销商统一own_discount 即上级给本级的折扣/底价)。
退订is_reverse利润取反减少上游结算仍正向增加
""" """
catalog_amount = float(catalog_amount or 0) catalog_amount = float(catalog_amount or 0)
qty = protocol.get('quantity') or 1 qty = protocol.get('quantity') or 1
@ -356,6 +367,9 @@ def _estimate_finance(catalog_amount, protocol):
if own_price is not None: if own_price is not None:
settle_upstream = float(own_price) * qty settle_upstream = float(own_price) * qty
if is_reverse and profit is not None:
profit = -profit
return { return {
'profit_amount': _round_money(profit), 'profit_amount': _round_money(profit),
'settle_upstream_amount': _round_money(settle_upstream), '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 取本级利润与本级应付。 """已记账:从 bill_detail 取本级利润与本级应付。
统一口径(业主机构与分销商一致): 统一口径(业主机构与分销商一致):
- profit_amount = 本级账本「折扣收入/底价收入」贷方合计 - profit_amount = 本级账本「折扣收入/底价收入」贷方合计
- settle_upstream = 本级账本「待结转*销售收入」贷方合计(本级成本/本级应付上级) - settle_upstream = 本级账本「待结转*销售收入」贷方合计(本级成本/本级应付上级)
退订is_reverse利润取反减少上游结算仍正向累加增加
""" """
rows = await _bill_detail_rows(sor, billid) rows = await _bill_detail_rows(sor, billid)
profit = 0.0 profit = 0.0
@ -408,6 +424,9 @@ async def _finance_from_bill_detail(sor, billid, accounting_orgid):
elif subj.startswith(SUPPLIER_SETTLE_PREFIX): elif subj.startswith(SUPPLIER_SETTLE_PREFIX):
settle_upstream += amt settle_upstream += amt
if is_reverse:
profit = -profit
return { return {
'profit_amount': _round_money(profit), 'profit_amount': _round_money(profit),
'settle_upstream_amount': _round_money(settle_upstream) if settle_upstream else _round_money(0), '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')) catalog_amount = _round_money(_row_get(row, 'catalog_amount'))
customer_pay = _round_money(_row_get(row, 'customer_pay_amount')) customer_pay = _round_money(_row_get(row, 'customer_pay_amount'))
customer_parentid = _row_get(row, 'customer_parentid') 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( protocol = await _protocol_snapshot(
sor, accounting_orgid, customerid, providerid, productid, bill_date, quantity, 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') bill_state = _row_get(row, 'bill_state')
if str(bill_state) == '1': 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: else:
amounts = _estimate_finance(catalog_amount, protocol) amounts = _estimate_finance(catalog_amount, protocol, is_reverse)
amounts['bill_detail_legs'] = [] amounts['bill_detail_legs'] = []
product_name = _row_get(row, 'product_name') 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): async def _bill_finance_amounts(sor, row, accounting_orgid):
"""单笔:销售额、本级利润、本级应付上级/供应商。""" """单笔:销售额、本级利润、本级应付上级/供应商。
退订BUY_REVERSE 等):销售额、利润取负(减少);应付上级仍正向(增加)。
"""
sales = float(_row_get(row, 'customer_pay_amount') or 0) sales = float(_row_get(row, 'customer_pay_amount') or 0)
bill_id = _row_get(row, 'bill_id') bill_id = _row_get(row, 'bill_id')
bill_state = _row_get(row, 'bill_state') bill_state = _row_get(row, 'bill_state')
quantity = int(_row_get(row, 'quantity') or 1) 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': 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: else:
catalog = float(_row_get(row, 'catalog_amount') or 0) catalog = float(_row_get(row, 'catalog_amount') or 0)
protocol = await _protocol_snapshot( protocol = await _protocol_snapshot(
@ -657,7 +686,7 @@ async def _bill_finance_amounts(sor, row, accounting_orgid):
_row_get(row, 'bill_date'), _row_get(row, 'bill_date'),
quantity, quantity,
) )
fin = _estimate_finance(catalog, protocol) fin = _estimate_finance(catalog, protocol, is_reverse)
profit = float(fin.get('profit_amount') or 0) profit = float(fin.get('profit_amount') or 0)
settle = float(fin.get('settle_upstream_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'), 'customer_pay_amount': b.get('amount'),
'quantity': b.get('quantity'), 'quantity': b.get('quantity'),
'order_date': o.get('order_date'), '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'), 'servicename': o.get('servicename'),
'customer_name': cust.get('orgname'), 'customer_name': cust.get('orgname'),
'customer_parentid': cust.get('parentid'), 'customer_parentid': cust.get('parentid'),