PayrollManagementSystem/backend/app/models/shipment.py
liuwenxin 1e53792ad6 feat(T+): 完成数据贯通与自动接入闭环
打通 T+ 快照、业务、发货、回款和核算准备链路,保留来源差异并提供待补状态。
新增业务自动接入任务、跨平台读取服务托管、权限复核、来源关联和前端核对页面。
补充迁移、隔离集成测试、浏览器模拟验收及角色授权边界修复。
2026-11-05 18:47:13 +08:00

115 lines
4.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""发货模型:发货单与发货产品明细。
- ship_date 是**回款天数计算基准日**(对照表 #1:发货日首日计第 1 天),
回款指定本发货单后由 formula.payment_days 派生回款天数,不在本表落库
- 运费仅记录展示,不计入毛利润口径(对照表 #2)
- total_amount 为明细行汇总冗余,服务层事务内维护
"""
from datetime import date
from decimal import Decimal
from sqlalchemy import Date, ForeignKey, Index, Numeric, String
from sqlalchemy.orm import Mapped, mapped_column
from app.models.base import (
BIGINT_UNSIGNED,
Base,
TimestampMixin,
TPlusSyncMixin,
mysql_table_args,
)
class Shipment(Base, TimestampMixin, TPlusSyncMixin):
"""发货单表(需求 2.2.2):每笔发货对应一个业务,支持分批发货。"""
__tablename__ = "shipments"
__table_args__ = mysql_table_args(
"发货单表(发货日为回款天数基准日,发货日首日计第 1 天)",
Index("idx_shipments_business_id", "business_id"),
Index("idx_shipments_ship_date", "ship_date"),
)
id: Mapped[int] = mapped_column(
BIGINT_UNSIGNED, primary_key=True, autoincrement=True, comment="主键ID"
)
shipment_no: Mapped[str] = mapped_column(
String(32),
nullable=False,
unique=True,
comment="发货单号,自动生成 SH{YYYYMMDD}-{4位当日序号}(内部追溯用)",
)
business_id: Mapped[int] = mapped_column(
BIGINT_UNSIGNED,
ForeignKey("businesses.id", ondelete="RESTRICT"),
nullable=False,
comment="业务ID(businesses.id;终态业务禁止新发货)",
)
ship_date: Mapped[date] = mapped_column(
Date, nullable=False, comment="发货日期(回款天数基准日,发货日首日=第 1 天)"
)
freight_amount: Mapped[Decimal | None] = mapped_column(
Numeric(14, 2),
nullable=True,
comment="运费金额(仅记录展示;不计入毛利润口径,对照表 #2)",
)
total_amount: Mapped[Decimal] = mapped_column(
Numeric(14, 2),
nullable=False,
server_default="0",
comment="发货总金额(明细行汇总冗余,服务层事务内维护,禁手工直改)",
)
remark: Mapped[str | None] = mapped_column(String(255), comment="备注")
class ShipmentItem(Base, TimestampMixin):
"""发货产品明细表(需求 2.2.2):记录发货的产品名称、规格、数量等。
价格口径两可(T+ 供价或人工录入,待 IT 确认):sale_price 可空人工补,
amount 由服务层按 数量×单价 计算固化。
"""
__tablename__ = "shipment_items"
__table_args__ = mysql_table_args(
"发货产品明细表",
Index("idx_shipment_items_shipment_id", "shipment_id"),
Index("idx_shipment_items_product_id", "product_id"),
)
id: Mapped[int] = mapped_column(
BIGINT_UNSIGNED, primary_key=True, autoincrement=True, comment="主键ID"
)
shipment_id: Mapped[int] = mapped_column(
BIGINT_UNSIGNED,
ForeignKey("shipments.id", ondelete="RESTRICT"),
nullable=False,
comment="发货单ID(shipments.id)",
)
product_id: Mapped[int | None] = mapped_column(
BIGINT_UNSIGNED,
ForeignKey("products.id", ondelete="RESTRICT"),
comment="产品ID(products.id;NULL=自由录入)",
)
product_name: Mapped[str] = mapped_column(
String(128), nullable=False, comment="产品名称(引用产品时冗余固化)"
)
spec_model: Mapped[str] = mapped_column(
String(128),
nullable=False,
server_default="''",
comment="规格型号(冗余固化;空串对齐 products 组合唯一键口径)",
)
unit: Mapped[str | None] = mapped_column(String(16), comment="计量单位")
quantity: Mapped[Decimal] = mapped_column(
Numeric(14, 2), nullable=False, comment="发货数量(>0)"
)
sale_price: Mapped[Decimal | None] = mapped_column(
Numeric(14, 4), comment="发货单价(元;T+ 同步或人工,口径待 IT 确认)"
)
amount: Mapped[Decimal] = mapped_column(
Numeric(14, 2),
nullable=False,
comment="行金额 = 数量×单价(服务层计算固化,不信任前端传值)",
)