feat(calc): 单笔回款核算引擎与绩效台账

- calc_service.calc_receipt:锁回款行→settlement_status=0 校验→发货批次/
  归属前置→resolve_snapshot(回款日)→按 calc_template 分派(与 _preview_rate
  同构,‰÷1000 防线)→items 行级折算(行类别阈值,混合按行汇总)→
  barter/起付/R→台账 v1→CAS 置已核算,单 audit 事务
- 策略点集中:_personal_rate(#12 经理个人户单笔落 0 待答复)、_row_profit
  (#20 不含税原样);核算粒度 #25 一期口径=业务级一次性计提(重复回款
  repeated=True 仅登记进度)
- 台账仓储:生效行查询/业务级判定/版本号;schemas LedgerOut/Query
- api /api/v1/calc/*:台账分页/详情/核算触发(写限 admin+marketing_ops)
- 测试 8 例:金标准双类别复算到分(DB 锚定 4000.00/39.11/39.11)、
  一次性计提、已核算 400、未指定批次、成本缺失行保守 -3000、负绩效照算、
  准入、列表筛选;行级明细折算两位固化
- conftest 清理顺序修正:V7 台账/月结清理移到回款/人员删除之前
  (RESTRICT 外键不挡序)

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
liuwenxinandClaude Code committed 2026-10-16 20:52:02 +08:00
1 parent a5c86640e2
commit df1a3839a1
7 files changed
+1006 -6

No files matched your search

+62
View File
@@ -0,0 +1,62 @@
"""绩效核算的请求/响应模型(M3 ④)。"""
from datetime import date, datetime
from decimal import Decimal
from pydantic import BaseModel, ConfigDict, Field
from app.core.response import PageQuery
class LedgerQuery(PageQuery):
"""台账列表查询条件。"""
settlement_month: str | None = Field(default=None, max_length=7, description="归属月 YYYY-MM")
personnel_id: int | None = Field(default=None, ge=1, description="归属人员筛选")
business_id: int | None = Field(default=None, ge=1, description="业务筛选")
is_active: int | None = Field(default=None, ge=0, le=1, description="生效标记筛选")
calc_type: int | None = Field(default=None, ge=1, le=2, description="核算类型筛选")
class LedgerOut(BaseModel):
"""台账行响应(金额/比例一律 str)。"""
model_config = ConfigDict(from_attributes=True)
id: int
receipt_id: int | None = None
calc_version: int
is_active: int
calc_type: int
business_id: int
business_no: str
personnel_id: int
personnel_name: str
role_code: str
customer_type: str
product_category: str
ship_date: date | None = None
receipt_date: date
payment_days: int | None = None
rate_text: str
converted_profit: Decimal | None = None
performance_amount: Decimal | None = None
barter_applied: int
weight_r: int | None = None
payment_weight: Decimal | None = None
payable_amount: Decimal | None = None
settlement_month: str
is_probation: int
missing_cost: int
snapshot_json: dict | None = None
settlement_id: int | None = None
calc_reason: str | None = None
create_time: datetime
class CalcReceiptOut(BaseModel):
"""单笔核算结果:台账行 + 重复计提标记(核算粒度 #25 一期口径)。"""
ledger: LedgerOut
#: True=该业务此前已核算过,本笔回款仅登记进度、未重复计提(#25 一期口径)
repeated: bool = False