fix(business): 行金额乘积显式两位固化,计提比例对负天数钳制

- 明细行金额(数量2位×单价4位可产 6 位小数)直写 DECIMAL(14,2) 被
  MySQL 静默舍入且回显漂移(创建响应读内存值、复查读库值不一致);
  显式 quantize HALF_UP 固化,舍入方向与 MySQL 一致(业务/发货同改)
- 先款后货回款(payment_days 为负)线性外插会算出高于起始值的计提
  比例(多算绩效);钳制到 0 天按起始比例计提,payment_days 出参仍
  照实输出负值(口径:2026-10-14 用户裁决)
- 新增 formula 负天数钳制单测与 quantize 回显/库值一致性集成用例

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
liuwenxinandClaude Code committed 2026-10-14 18:58:30 +08:00
1 parent 91fc6f6293
commit e6c2c71d1b
5 files changed
+56 -4

No files matched your search

+9 -2
View File
@@ -16,7 +16,7 @@
import logging
from datetime import date
from decimal import Decimal
from decimal import ROUND_HALF_UP, Decimal
from fractions import Fraction
from sqlalchemy import Row, func, select
@@ -136,12 +136,19 @@ def _build_item_rows(business_id: int, items: list[BusinessItemIn]) -> list[Busi
不信任前端传值(schema 亦无 sale_amount 入参)——单价可空时销售额记 0,
由用户在编辑中补价后重算(毛利润视图中销售额 0 的行全部为 0,防除零)。
乘积显式 quantize 两位固化:数量(2位)×单价(4位) 可产 6 位小数,直写
DECIMAL(14,2) 会被 MySQL 静默舍入且回显漂移(创建响应读内存值、复查
读库值前后不一致);HALF_UP 与 MySQL 的 DECIMAL 舍入方向一致。
"""
rows: list[BusinessItem] = []
for item in items:
quantity = Decimal(item.quantity)
sale_price = Decimal(item.sale_price) if item.sale_price is not None else None
sale_amount = (quantity * sale_price) if sale_price is not None else Decimal("0")
sale_amount = (
(quantity * sale_price).quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)
if sale_price is not None
else Decimal("0.00")
)
rows.append(
BusinessItem(
business_id=business_id,