feat(business): 回款管理接口(批次号/手动指定批次/已核算禁改/合计回写)
- 批次 HK 生成;shipment_id 同业务强校验(统一失败面防探测) - 已核算回款禁改禁删(M3 防重锚点);payment_days 派生(发货日首日=第 1 天) Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
1 parent
4aea2be103
commit
a02b6c3ec8
4 files changed
+669
No files matched your search
@@ -0,0 +1,127 @@
|
||||
"""回款数据访问:回款行与关联引用(业务号/发货单号/发货日)。
|
||||
|
||||
只读写数据并 flush;commit/rollback 与业务异常一律在 service 层。
|
||||
|
||||
- 列表与详情统一 join 出 ``business_no / shipment_no / ship_date``,
|
||||
ship_date 供 service 按 ``formula.payment_days`` 派生回款天数 —— 关联
|
||||
引用经本层查询一次供给,service 不再对发货单二次查询
|
||||
- shipment_id 是手动指定(可空),发货单关联一律 outerjoin
|
||||
"""
|
||||
|
||||
from datetime import date
|
||||
|
||||
from sqlalchemy import Select, func, select
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.models import Business, Receipt, Shipment
|
||||
|
||||
|
||||
def get_by_id(session: Session, receipt_id: int) -> Receipt | None:
|
||||
"""按主键取回款行(供摘要预取与状态判定;不锁行,写保护锚在业务行)。"""
|
||||
return session.get(Receipt, receipt_id)
|
||||
|
||||
|
||||
def get_with_refs(
|
||||
session: Session, receipt_id: int
|
||||
) -> tuple[Receipt, str, str | None, date | None] | None:
|
||||
"""回款行 + 业务号 + 发货单号 + 发货日(DTO 与 payment_days 派生共用)。
|
||||
|
||||
business 主表为 inner join(FK NOT NULL,业务与回款同存亡);发货单
|
||||
可未指定,outerjoin 后 shipment_no / ship_date 可能为 None。
|
||||
"""
|
||||
row = session.execute(
|
||||
select(Receipt, Business.business_no, Shipment.shipment_no, Shipment.ship_date)
|
||||
.join(Business, Business.id == Receipt.business_id)
|
||||
.outerjoin(Shipment, Shipment.id == Receipt.shipment_id)
|
||||
.where(Receipt.id == receipt_id)
|
||||
).first()
|
||||
return row
|
||||
|
||||
|
||||
def get_shipment(session: Session, shipment_id: int) -> Shipment | None:
|
||||
"""按主键取发货单(同业务强校验的只读预查;FK RESTRICT 为最终防线)。"""
|
||||
return session.get(Shipment, shipment_id)
|
||||
|
||||
|
||||
def _apply_filters(
|
||||
stmt: Select,
|
||||
*,
|
||||
business_id: int | None,
|
||||
date_from: date | None,
|
||||
date_to: date | None,
|
||||
is_barter: int | None,
|
||||
settlement_status: int | None,
|
||||
) -> Select:
|
||||
"""回款列表的筛选条件(全部落在 Receipt 自身列上)。"""
|
||||
if business_id is not None:
|
||||
stmt = stmt.where(Receipt.business_id == business_id)
|
||||
if date_from is not None:
|
||||
stmt = stmt.where(Receipt.receipt_date >= date_from)
|
||||
if date_to is not None:
|
||||
stmt = stmt.where(Receipt.receipt_date <= date_to)
|
||||
if is_barter is not None:
|
||||
stmt = stmt.where(Receipt.is_barter == is_barter)
|
||||
if settlement_status is not None:
|
||||
stmt = stmt.where(Receipt.settlement_status == settlement_status)
|
||||
return stmt
|
||||
|
||||
|
||||
def list_receipts(
|
||||
session: Session,
|
||||
*,
|
||||
offset: int,
|
||||
limit: int,
|
||||
business_id: int | None,
|
||||
date_from: date | None,
|
||||
date_to: date | None,
|
||||
is_barter: int | None,
|
||||
settlement_status: int | None,
|
||||
) -> list[tuple[Receipt, str, str | None, date | None]]:
|
||||
"""分页查询回款(附业务号/发货单号/发货日),按 id 倒序。"""
|
||||
stmt = _apply_filters(
|
||||
select(Receipt, Business.business_no, Shipment.shipment_no, Shipment.ship_date)
|
||||
.join(Business, Business.id == Receipt.business_id)
|
||||
.outerjoin(Shipment, Shipment.id == Receipt.shipment_id),
|
||||
business_id=business_id,
|
||||
date_from=date_from,
|
||||
date_to=date_to,
|
||||
is_barter=is_barter,
|
||||
settlement_status=settlement_status,
|
||||
)
|
||||
return list(
|
||||
session.execute(stmt.order_by(Receipt.id.desc()).offset(offset).limit(limit)).all()
|
||||
)
|
||||
|
||||
|
||||
def count_receipts(
|
||||
session: Session,
|
||||
*,
|
||||
business_id: int | None,
|
||||
date_from: date | None,
|
||||
date_to: date | None,
|
||||
is_barter: int | None,
|
||||
settlement_status: int | None,
|
||||
) -> int:
|
||||
"""回款总数(筛选条件与列表共用同一 apply_filters,防两侧口径漂移)。"""
|
||||
stmt = _apply_filters(
|
||||
select(func.count()).select_from(Receipt),
|
||||
business_id=business_id,
|
||||
date_from=date_from,
|
||||
date_to=date_to,
|
||||
is_barter=is_barter,
|
||||
settlement_status=settlement_status,
|
||||
)
|
||||
return int(session.execute(stmt).scalar_one())
|
||||
|
||||
|
||||
def create(session: Session, receipt: Receipt) -> Receipt:
|
||||
"""新增回款行。只 flush 以取得自增 id,编号唯一键冲突由 service 兜底。"""
|
||||
session.add(receipt)
|
||||
session.flush()
|
||||
return receipt
|
||||
|
||||
|
||||
def delete(session: Session, receipt: Receipt) -> None:
|
||||
"""物理删除回款行(已核算与否的准入判定在 service 层)。"""
|
||||
session.delete(receipt)
|
||||
session.flush()
|
||||
Reference in new issue
Block a user