- 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>
115 lines
4.0 KiB
Python
115 lines
4.0 KiB
Python
"""绩效核算台账仓储:只读写数据并 flush;commit/rollback 在服务层。"""
|
||
|
||
from sqlalchemy import func, select
|
||
from sqlalchemy.orm import Session
|
||
|
||
from app.models import PerformanceLedger
|
||
from app.schemas.calc import LedgerQuery
|
||
|
||
|
||
def get_by_id(session: Session, ledger_id: int) -> PerformanceLedger | None:
|
||
"""按主键取台账行。"""
|
||
return session.get(PerformanceLedger, ledger_id)
|
||
|
||
|
||
def get_active_by_receipt(
|
||
session: Session, receipt_id: int, *, calc_type: int = 1
|
||
) -> PerformanceLedger | None:
|
||
"""取某回款的当前生效核算行(is_active=1;无则 None)。"""
|
||
return session.execute(
|
||
select(PerformanceLedger).where(
|
||
PerformanceLedger.receipt_id == receipt_id,
|
||
PerformanceLedger.calc_type == calc_type,
|
||
PerformanceLedger.is_active == 1,
|
||
)
|
||
).scalar_one_or_none()
|
||
|
||
|
||
def has_active_for_business(session: Session, business_id: int, *, calc_type: int = 1) -> bool:
|
||
"""该业务是否已有生效核算行(核算粒度 #25:业务级一次性计提的判定)。"""
|
||
return (
|
||
session.execute(
|
||
select(func.count())
|
||
.select_from(PerformanceLedger)
|
||
.where(
|
||
PerformanceLedger.business_id == business_id,
|
||
PerformanceLedger.calc_type == calc_type,
|
||
PerformanceLedger.is_active == 1,
|
||
)
|
||
).scalar_one()
|
||
> 0
|
||
)
|
||
|
||
|
||
def get_active_by_receipt_of_business(
|
||
session: Session, business_id: int, *, calc_type: int = 1
|
||
) -> PerformanceLedger:
|
||
"""取该业务当前生效的核算行(重复回款登记进度时回显既有台账)。"""
|
||
return (
|
||
session.execute(
|
||
select(PerformanceLedger)
|
||
.where(
|
||
PerformanceLedger.business_id == business_id,
|
||
PerformanceLedger.calc_type == calc_type,
|
||
PerformanceLedger.is_active == 1,
|
||
)
|
||
.order_by(PerformanceLedger.id.desc())
|
||
)
|
||
.scalars()
|
||
.first()
|
||
)
|
||
|
||
|
||
def add(session: Session, row: PerformanceLedger) -> PerformanceLedger:
|
||
"""落台账行(flush 取 id;commit 在服务层)。"""
|
||
session.add(row)
|
||
session.flush()
|
||
return row
|
||
|
||
|
||
def mark_inactive(session: Session, row: PerformanceLedger) -> None:
|
||
"""重算时把旧版本行翻为已替代(全表唯一的受控 UPDATE 例外;不 commit)。"""
|
||
row.is_active = 0
|
||
session.flush()
|
||
|
||
|
||
def next_version(session: Session, receipt_id: int) -> int:
|
||
"""某回款的下一个核算版本号(max+1;无行则 1)。"""
|
||
current = session.execute(
|
||
select(func.max(PerformanceLedger.calc_version)).where(
|
||
PerformanceLedger.receipt_id == receipt_id
|
||
)
|
||
).scalar_one()
|
||
return int(current) + 1
|
||
|
||
|
||
def list_ledger(session: Session, query: LedgerQuery) -> list[PerformanceLedger]:
|
||
"""分页查询台账(筛选:月/人员/业务/生效/类型)。"""
|
||
stmt = select(PerformanceLedger).where(*_filters(query))
|
||
stmt = stmt.order_by(PerformanceLedger.id.desc()).offset(query.offset).limit(query.page_size)
|
||
return list(session.execute(stmt).scalars())
|
||
|
||
|
||
def count_ledger(session: Session, query: LedgerQuery) -> int:
|
||
"""台账计数(与 list_ledger 同筛选)。"""
|
||
return int(
|
||
session.execute(
|
||
select(func.count()).select_from(PerformanceLedger).where(*_filters(query))
|
||
).scalar_one()
|
||
)
|
||
|
||
|
||
def _filters(query: LedgerQuery) -> tuple:
|
||
filters = []
|
||
if query.settlement_month:
|
||
filters.append(PerformanceLedger.settlement_month == query.settlement_month)
|
||
if query.personnel_id is not None:
|
||
filters.append(PerformanceLedger.personnel_id == query.personnel_id)
|
||
if query.business_id is not None:
|
||
filters.append(PerformanceLedger.business_id == query.business_id)
|
||
if query.is_active is not None:
|
||
filters.append(PerformanceLedger.is_active == query.is_active)
|
||
if query.calc_type is not None:
|
||
filters.append(PerformanceLedger.calc_type == query.calc_type)
|
||
return tuple(filters)
|