"""产品模型:产品主数据。 产品类别(product_category)是**折算阈值的行级锚点**:混合业务按产品逐条 计算(对照表 #14),阈值本身由 M1 参数 `product.threshold` 按 PRODUCT_CATEGORY 维度承担(#28「全部阈值统一在系统设置维护」),本表不建阈值列 —— 两处维护 必然漂移。类别值域与 M1 参数维度共用 PRODUCT_CATEGORIES 常量。 """ from decimal import Decimal from sqlalchemy import JSON, Numeric, String, UniqueConstraint from sqlalchemy.orm import Mapped, mapped_column from app.models.base import ( BIGINT_UNSIGNED, TINYINT_TYPE, Base, TimestampMixin, TPlusSyncMixin, mysql_table_args, ) class Product(Base, TimestampMixin, TPlusSyncMixin): """产品表(需求 2.1.2)。停用(status=0)为主,不物理删除。""" __tablename__ = "products" __table_args__ = mysql_table_args( "产品表(类别锚定折算阈值参数;T+同步不覆盖产品类别)", UniqueConstraint("product_name", "spec_model", name="uk_products_name_spec"), ) id: Mapped[int] = mapped_column( BIGINT_UNSIGNED, primary_key=True, autoincrement=True, comment="主键ID" ) product_name: Mapped[str] = mapped_column(String(128), nullable=False, comment="产品名称") spec_model: Mapped[str] = mapped_column( String(128), nullable=False, server_default="''", comment="规格型号(空串而非 NULL:参与组合唯一键,MySQL 组合索引对 NULL 不去重)", ) unit: Mapped[str | None] = mapped_column(String(16), comment="计量单位") stock_quantity: Mapped[Decimal | None] = mapped_column( Numeric(14, 2), comment="数量(T+ 同步原样存储,语义待 IT 确认)" ) sale_price: Mapped[Decimal | None] = mapped_column( Numeric(14, 4), comment="销售单价(元,T+ 同步)" ) product_category: Mapped[str | None] = mapped_column( String(32), nullable=True, comment="产品类别:municipal_water市政水利 rail_transit轨道交通(决定折算阈值 30%/20%;T+同步不覆盖)", ) status: Mapped[int] = mapped_column( TINYINT_TYPE, nullable=False, server_default="1", comment="状态:1启用 0停用" ) remark: Mapped[str | None] = mapped_column(String(255), comment="备注") source_trade_json: Mapped[dict | None] = mapped_column(JSON, comment="最近有效发货数量、含税成交价及源单据快照") quantity_price_manual: Mapped[int] = mapped_column(TINYINT_TYPE, nullable=False, server_default="0", comment="数量单价人工保护:1人工维护 0随源更新")