87 lines
3.7 KiB
Python
87 lines
3.7 KiB
Python
|
|
"""产品模型"""
|
|||
|
|
import uuid
|
|||
|
|
from datetime import datetime
|
|||
|
|
from sqlalchemy import String, DateTime, ForeignKey
|
|||
|
|
from sqlalchemy.dialects.postgresql import UUID
|
|||
|
|
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
|||
|
|
|
|||
|
|
from app.models.base import Base
|
|||
|
|
from app.core.time_utils import get_beijing_time
|
|||
|
|
from app.core.lifecycle import LIFECYCLE_PRODUCTION
|
|||
|
|
|
|||
|
|
|
|||
|
|
class Product(Base):
|
|||
|
|
__tablename__ = "products"
|
|||
|
|
|
|||
|
|
id: Mapped[uuid.UUID] = mapped_column(
|
|||
|
|
UUID(as_uuid=True), primary_key=True, default=uuid.uuid4,
|
|||
|
|
)
|
|||
|
|
serial_number: Mapped[str] = mapped_column(
|
|||
|
|
String(16), unique=True, index=True, nullable=False, comment="产品序列号(16位)",
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
# ---- 物理外键(关联本库 production_orders)— 可选 ----
|
|||
|
|
order_id: Mapped[uuid.UUID | None] = mapped_column(
|
|||
|
|
UUID(as_uuid=True), ForeignKey("production_orders.id"), nullable=True, comment="所属订单ID(可选)",
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
# ---- 外部序列号(用户自定义,可选) ----
|
|||
|
|
external_serial: Mapped[str | None] = mapped_column(
|
|||
|
|
String(64), nullable=True, comment="用户自定义产品序列号(可选)",
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
# ---- 逻辑外键(关联老系统物料表,仅存储 ID,无物理约束) ----
|
|||
|
|
material_id: Mapped[str | None] = mapped_column(
|
|||
|
|
String(64), nullable=True, comment="物料ID(逻辑外键→老系统)",
|
|||
|
|
)
|
|||
|
|
# ---- MOM 物料快照字段(创产品时写入,防止老系统数据变动影响标签) ----
|
|||
|
|
material_name: Mapped[str | None] = mapped_column(
|
|||
|
|
String(255), nullable=True, comment="物料名称快照",
|
|||
|
|
)
|
|||
|
|
spec_model: Mapped[str | None] = mapped_column(
|
|||
|
|
String(255), nullable=True, comment="规格型号快照",
|
|||
|
|
)
|
|||
|
|
category: Mapped[str | None] = mapped_column(
|
|||
|
|
String(255), nullable=True, comment="物料分类快照",
|
|||
|
|
)
|
|||
|
|
material_type: Mapped[str | None] = mapped_column(
|
|||
|
|
String(100), nullable=True, comment="物料类型快照",
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
# ---- 物理外键(自引用:父产品) ----
|
|||
|
|
parent_product_id: Mapped[uuid.UUID | None] = mapped_column(
|
|||
|
|
UUID(as_uuid=True), ForeignKey("products.id"), nullable=True, comment="父产品ID",
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
# ---- 当前位置追踪(逻辑外键→用户ID 或 'virtual_warehouse') ----
|
|||
|
|
current_location_id: Mapped[str | None] = mapped_column(
|
|||
|
|
String(64), nullable=True, comment="当前持有者ID 或 'virtual_warehouse'(仓库)",
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
status: Mapped[str] = mapped_column(
|
|||
|
|
String(50), nullable=False, default="pending", comment="产品状态",
|
|||
|
|
)
|
|||
|
|
# 宏观流转状态 — 首次扫码时强制设定:备货/生产/测试/维修/待仓库收货/已入库/已出库
|
|||
|
|
overall_status: Mapped[str | None] = mapped_column(
|
|||
|
|
String(20), nullable=True, comment="宏观状态: 备货/生产/测试/维修/待仓库收货/已入库/已出库",
|
|||
|
|
)
|
|||
|
|
# 生命周期阶段 — 生产制造 vs 出库后返厂售后。
|
|||
|
|
# 设备出库后再次被派发任务 = 回流返厂,此处切为 AFTER_SALES 且不再回退。
|
|||
|
|
lifecycle_phase: Mapped[str] = mapped_column(
|
|||
|
|
String(20), nullable=False, default=LIFECYCLE_PRODUCTION,
|
|||
|
|
server_default=LIFECYCLE_PRODUCTION, index=True,
|
|||
|
|
comment="生命周期阶段: PRODUCTION(生产制造) | AFTER_SALES(出库后返厂售后)",
|
|||
|
|
)
|
|||
|
|
created_at: Mapped[datetime] = mapped_column(
|
|||
|
|
DateTime(timezone=True), default=get_beijing_time, comment="创建时间",
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
# ---- 关系 ----
|
|||
|
|
order: Mapped["ProductionOrder"] = relationship("ProductionOrder", lazy="selectin")
|
|||
|
|
parent_product: Mapped["Product | None"] = relationship(
|
|||
|
|
"Product", remote_side="Product.id", lazy="selectin",
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
def __repr__(self) -> str:
|
|||
|
|
return f"<Product {self.serial_number}>"
|