from datetime import datetime from sqlalchemy import BigInteger, CheckConstraint, Column, DateTime, ForeignKey, Integer, String, UniqueConstraint from config.database import Base from config.env import DataBaseConfig from utils.common_util import SqlalchemyUtil class BizInboundOrder(Base): """首次入库单。""" __tablename__ = 'biz_inbound_order' __table_args__ = ( CheckConstraint("status IN ('DRAFT', 'CONFIRMED')", name='ck_biz_inbound_order_status'), {'comment': '首次入库单'}, ) inbound_id = Column(BigInteger, primary_key=True, nullable=False, autoincrement=True, comment='入库单ID') inbound_no = Column(String(64), nullable=False, unique=True, comment='入库单号') warehouse_id = Column( BigInteger, ForeignKey('biz_warehouse.warehouse_id'), nullable=False, comment='仓库ID', ) status = Column(String(32), nullable=False, server_default='DRAFT', comment='单据状态') confirm_idempotency_key = Column(String(64), nullable=True, unique=True, comment='确认幂等键') confirm_request_hash = Column(String(64), nullable=True, comment='确认请求摘要') confirmed_by = Column(BigInteger, ForeignKey('sys_user.user_id'), nullable=True, comment='确认人ID') confirmed_at = Column(DateTime, nullable=True, comment='确认时间') create_by = Column(String(64), nullable=True, server_default='', comment='创建者') create_time = Column(DateTime, nullable=True, default=datetime.now, comment='创建时间') update_by = Column(String(64), nullable=True, server_default='', comment='更新者') update_time = Column(DateTime, nullable=True, default=datetime.now, comment='更新时间') remark = Column( String(500), nullable=True, server_default=SqlalchemyUtil.get_server_default_null(DataBaseConfig.db_type), comment='备注', ) class BizInboundItem(Base): """首次入库单托盘明细。""" __tablename__ = 'biz_inbound_item' __table_args__ = ( UniqueConstraint('inbound_id', 'pallet_id', name='uq_biz_inbound_item_order_pallet'), {'comment': '首次入库单托盘明细'}, ) inbound_item_id = Column( BigInteger, primary_key=True, nullable=False, autoincrement=True, comment='入库明细ID', ) inbound_id = Column( BigInteger, ForeignKey('biz_inbound_order.inbound_id'), nullable=False, comment='入库单ID', ) pallet_id = Column(BigInteger, ForeignKey('biz_pallet.pallet_id'), nullable=False, comment='托盘ID') create_by = Column(String(64), nullable=True, server_default='', comment='创建者') create_time = Column(DateTime, nullable=True, default=datetime.now, comment='创建时间') class BizFillingRecord(Base): """托盘装填记录。""" __tablename__ = 'biz_filling_record' __table_args__ = ( UniqueConstraint('pallet_id', 'active_slot', name='uq_biz_filling_record_pallet_active_slot'), CheckConstraint('initial_packages > 0', name='ck_biz_filling_record_initial_packages'), CheckConstraint( "active_slot IS NULL OR active_slot = 'ACTIVE'", name='ck_biz_filling_record_active_slot', ), {'comment': '托盘装填记录'}, ) filling_id = Column(BigInteger, primary_key=True, nullable=False, autoincrement=True, comment='装填记录ID') filling_no = Column(String(64), nullable=False, unique=True, comment='装填单号') pallet_id = Column(BigInteger, ForeignKey('biz_pallet.pallet_id'), nullable=False, comment='托盘ID') warehouse_id = Column( BigInteger, ForeignKey('biz_warehouse.warehouse_id'), nullable=False, comment='仓库ID', ) strain_id = Column(BigInteger, ForeignKey('biz_strain.strain_id'), nullable=False, comment='菌种ID') strain_code_snapshot = Column(String(64), nullable=False, comment='菌种编码快照') strain_name_snapshot = Column(String(100), nullable=False, comment='菌种名称快照') batch_no = Column(String(64), nullable=False, comment='生产批次') package_spec = Column(String(64), nullable=False, comment='包装规格') initial_packages = Column(Integer, nullable=False, comment='初始包数') active_slot = Column(String(16), nullable=True, comment='活动装填槽') closed_at = Column(DateTime, nullable=True, comment='装填周期关闭时间') idempotency_key = Column(String(64), nullable=False, unique=True, comment='幂等键') request_hash = Column(String(64), nullable=False, comment='请求摘要') operator_id = Column(BigInteger, ForeignKey('sys_user.user_id'), nullable=False, comment='操作人ID') operated_at = Column(DateTime, nullable=False, comment='操作时间') create_by = Column(String(64), nullable=True, server_default='', comment='创建者') create_time = Column(DateTime, nullable=True, default=datetime.now, comment='创建时间') update_by = Column(String(64), nullable=True, server_default='', comment='更新者') update_time = Column(DateTime, nullable=True, default=datetime.now, comment='更新时间') remark = Column( String(500), nullable=True, server_default=SqlalchemyUtil.get_server_default_null(DataBaseConfig.db_type), comment='备注', ) class BizShipmentOrder(Base): """整托发货单。""" __tablename__ = 'biz_shipment_order' __table_args__ = ( CheckConstraint("status IN ('DRAFT', 'CONFIRMED')", name='ck_biz_shipment_order_status'), {'comment': '整托发货单'}, ) shipment_id = Column(BigInteger, primary_key=True, nullable=False, autoincrement=True, comment='发货单ID') shipment_no = Column(String(64), nullable=False, unique=True, comment='发货单号') source_warehouse_id = Column( BigInteger, ForeignKey('biz_warehouse.warehouse_id'), nullable=False, comment='来源仓库ID', ) target_site_id = Column( BigInteger, ForeignKey('biz_customer_site.site_id'), nullable=False, comment='目标现场ID', ) status = Column(String(32), nullable=False, server_default='DRAFT', comment='单据状态') confirm_idempotency_key = Column(String(64), nullable=True, unique=True, comment='确认幂等键') confirm_request_hash = Column(String(64), nullable=True, comment='确认请求摘要') confirmed_by = Column(BigInteger, ForeignKey('sys_user.user_id'), nullable=True, comment='确认人ID') confirmed_at = Column(DateTime, nullable=True, comment='确认时间') create_by = Column(String(64), nullable=True, server_default='', comment='创建者') create_time = Column(DateTime, nullable=True, default=datetime.now, comment='创建时间') update_by = Column(String(64), nullable=True, server_default='', comment='更新者') update_time = Column(DateTime, nullable=True, default=datetime.now, comment='更新时间') remark = Column( String(500), nullable=True, server_default=SqlalchemyUtil.get_server_default_null(DataBaseConfig.db_type), comment='备注', ) class BizShipmentItem(Base): """整托发货明细。""" __tablename__ = 'biz_shipment_item' __table_args__ = ( UniqueConstraint('shipment_id', 'pallet_id', name='uq_biz_shipment_item_order_pallet'), CheckConstraint( 'package_qty IS NULL OR package_qty > 0', name='ck_biz_shipment_item_package_qty', ), {'comment': '整托发货明细'}, ) shipment_item_id = Column( BigInteger, primary_key=True, nullable=False, autoincrement=True, comment='发货明细ID', ) shipment_id = Column( BigInteger, ForeignKey('biz_shipment_order.shipment_id'), nullable=False, comment='发货单ID', ) pallet_id = Column(BigInteger, ForeignKey('biz_pallet.pallet_id'), nullable=False, comment='托盘ID') active_filling_id = Column( BigInteger, ForeignKey('biz_filling_record.filling_id'), nullable=True, comment='确认时活动装填记录ID', ) package_qty = Column(Integer, nullable=True, comment='确认时包数快照') create_by = Column(String(64), nullable=True, server_default='', comment='创建者') create_time = Column(DateTime, nullable=True, default=datetime.now, comment='创建时间') class BizUsageRecord(Base): """客户现场使用事实记录。""" __tablename__ = 'biz_usage_record' __table_args__ = ( CheckConstraint('used_packages > 0', name='ck_biz_usage_record_used_packages'), CheckConstraint( 'remaining_packages_after >= 0', name='ck_biz_usage_record_remaining_packages_after', ), {'comment': '客户现场使用事实记录'}, ) usage_id = Column(BigInteger, primary_key=True, nullable=False, autoincrement=True, comment='使用记录ID') usage_no = Column(String(64), nullable=False, unique=True, comment='使用记录号') pallet_id = Column(BigInteger, ForeignKey('biz_pallet.pallet_id'), nullable=False, comment='托盘ID') active_filling_id = Column( BigInteger, ForeignKey('biz_filling_record.filling_id'), nullable=False, comment='活动装填记录ID', ) site_id = Column(BigInteger, ForeignKey('biz_customer_site.site_id'), nullable=False, comment='现场ID') line_id = Column(BigInteger, ForeignKey('biz_production_line.line_id'), nullable=False, comment='产线ID') used_packages = Column(Integer, nullable=False, comment='本次使用包数') remaining_packages_after = Column(Integer, nullable=False, comment='使用后剩余包数') idempotency_key = Column(String(64), nullable=False, unique=True, comment='幂等键') request_hash = Column(String(64), nullable=False, comment='请求摘要') operator_id = Column(BigInteger, ForeignKey('sys_user.user_id'), nullable=False, comment='操作人ID') occurred_at = Column(DateTime, nullable=False, comment='发生时间') class BizReturnOrder(Base): """托盘退回单。""" __tablename__ = 'biz_return_order' __table_args__ = ( CheckConstraint("status IN ('REQUESTED', 'CONFIRMED')", name='ck_biz_return_order_status'), {'comment': '托盘退回单'}, ) return_id = Column(BigInteger, primary_key=True, nullable=False, autoincrement=True, comment='退回单ID') return_no = Column(String(64), nullable=False, unique=True, comment='退回单号') source_site_id = Column( BigInteger, ForeignKey('biz_customer_site.site_id'), nullable=False, comment='来源现场ID', ) target_warehouse_id = Column( BigInteger, ForeignKey('biz_warehouse.warehouse_id'), nullable=False, comment='目标仓库ID', ) status = Column(String(32), nullable=False, server_default='REQUESTED', comment='退回状态') request_idempotency_key = Column(String(64), nullable=False, unique=True, comment='申请幂等键') request_hash = Column(String(64), nullable=False, comment='申请请求摘要') confirm_idempotency_key = Column(String(64), nullable=True, unique=True, comment='确认幂等键') confirm_request_hash = Column(String(64), nullable=True, comment='确认请求摘要') requested_by = Column(BigInteger, ForeignKey('sys_user.user_id'), nullable=False, comment='申请人ID') requested_at = Column(DateTime, nullable=False, comment='申请时间') confirmed_by = Column(BigInteger, ForeignKey('sys_user.user_id'), nullable=True, comment='确认人ID') confirmed_at = Column(DateTime, nullable=True, comment='确认时间') create_by = Column(String(64), nullable=True, server_default='', comment='创建者') create_time = Column(DateTime, nullable=True, default=datetime.now, comment='创建时间') update_by = Column(String(64), nullable=True, server_default='', comment='更新者') update_time = Column(DateTime, nullable=True, default=datetime.now, comment='更新时间') remark = Column( String(500), nullable=True, server_default=SqlalchemyUtil.get_server_default_null(DataBaseConfig.db_type), comment='备注', ) class BizReturnItem(Base): """托盘退回明细。""" __tablename__ = 'biz_return_item' __table_args__ = ( UniqueConstraint('return_id', 'pallet_id', name='uq_biz_return_item_order_pallet'), CheckConstraint( 'remaining_packages_snapshot >= 0', name='ck_biz_return_item_remaining_packages_snapshot', ), {'comment': '托盘退回明细'}, ) return_item_id = Column( BigInteger, primary_key=True, nullable=False, autoincrement=True, comment='退回明细ID', ) return_id = Column( BigInteger, ForeignKey('biz_return_order.return_id'), nullable=False, comment='退回单ID', ) pallet_id = Column(BigInteger, ForeignKey('biz_pallet.pallet_id'), nullable=False, comment='托盘ID') active_filling_id_snapshot = Column( BigInteger, ForeignKey('biz_filling_record.filling_id'), nullable=True, comment='申请时活动装填记录快照', ) remaining_packages_snapshot = Column(Integer, nullable=False, comment='申请时剩余包数快照') create_by = Column(String(64), nullable=True, server_default='', comment='创建者') create_time = Column(DateTime, nullable=True, default=datetime.now, comment='创建时间') class BizPalletInventory(Base): """托盘当前库存、位置和流转状态。""" __tablename__ = 'biz_pallet_inventory' __table_args__ = ( CheckConstraint( "flow_state IN ('UNSTOCKED', 'EMPTY_IN_WAREHOUSE', 'FILLED_IN_WAREHOUSE', " "'AT_SITE_UNUSED', 'AT_SITE_PARTIAL', 'AT_SITE_EMPTY', 'RETURNING')", name='ck_biz_pallet_inventory_flow_state', ), CheckConstraint('remaining_packages >= 0', name='ck_biz_pallet_inventory_remaining_packages'), CheckConstraint('version >= 0', name='ck_biz_pallet_inventory_version'), CheckConstraint( 'NOT (warehouse_id IS NOT NULL AND site_id IS NOT NULL)', name='ck_biz_pallet_inventory_location', ), {'comment': '托盘当前库存、位置和流转状态'}, ) pallet_id = Column( BigInteger, ForeignKey('biz_pallet.pallet_id'), primary_key=True, nullable=False, autoincrement=False, comment='托盘ID', ) active_filling_id = Column( BigInteger, ForeignKey('biz_filling_record.filling_id'), nullable=True, comment='当前活动装填记录ID', ) warehouse_id = Column( BigInteger, ForeignKey('biz_warehouse.warehouse_id'), nullable=True, comment='当前仓库ID', ) site_id = Column( BigInteger, ForeignKey('biz_customer_site.site_id'), nullable=True, comment='当前现场ID', ) current_line_id = Column( BigInteger, ForeignKey('biz_production_line.line_id'), nullable=True, comment='当前驻场周期绑定产线ID', ) flow_state = Column(String(32), nullable=False, server_default='UNSTOCKED', comment='托盘流转状态') remaining_packages = Column(Integer, nullable=False, server_default='0', comment='当前剩余包数') last_event_id = Column( BigInteger, ForeignKey('biz_pallet_event.event_id'), nullable=True, comment='最后事件ID', ) version = Column(Integer, nullable=False, server_default='0', comment='并发版本号') updated_at = Column(DateTime, nullable=False, default=datetime.now, comment='更新时间') class BizPalletEvent(Base): """托盘只追加流转事件。""" __tablename__ = 'biz_pallet_event' __table_args__ = ( UniqueConstraint('pallet_id', 'event_seq', name='uq_biz_pallet_event_pallet_seq'), UniqueConstraint('pallet_id', 'idempotency_key', name='uq_biz_pallet_event_pallet_idempotency'), CheckConstraint( "event_type IN ('INBOUND_CONFIRMED', 'FILLING_CONFIRMED', 'SHIPMENT_CONFIRMED', " "'USAGE_RECORDED', 'RETURN_REQUESTED', 'RETURN_CONFIRMED')", name='ck_biz_pallet_event_event_type', ), {'comment': '托盘只追加流转事件'}, ) event_id = Column(BigInteger, primary_key=True, nullable=False, autoincrement=True, comment='事件ID') pallet_id = Column(BigInteger, ForeignKey('biz_pallet.pallet_id'), nullable=False, comment='托盘ID') event_seq = Column(Integer, nullable=False, comment='托盘内事件序号') event_type = Column(String(32), nullable=False, comment='事件类型') source_type = Column(String(32), nullable=False, comment='来源类型') source_id = Column(BigInteger, nullable=False, comment='来源记录ID') before_state = Column(String(32), nullable=False, comment='变更前状态') after_state = Column(String(32), nullable=False, comment='变更后状态') before_warehouse_id = Column( BigInteger, ForeignKey('biz_warehouse.warehouse_id'), nullable=True, comment='变更前仓库ID', ) before_site_id = Column( BigInteger, ForeignKey('biz_customer_site.site_id'), nullable=True, comment='变更前现场ID', ) before_line_id = Column( BigInteger, ForeignKey('biz_production_line.line_id'), nullable=True, comment='变更前产线ID', ) after_warehouse_id = Column( BigInteger, ForeignKey('biz_warehouse.warehouse_id'), nullable=True, comment='变更后仓库ID', ) after_site_id = Column( BigInteger, ForeignKey('biz_customer_site.site_id'), nullable=True, comment='变更后现场ID', ) after_line_id = Column( BigInteger, ForeignKey('biz_production_line.line_id'), nullable=True, comment='变更后产线ID', ) before_remaining_packages = Column(Integer, nullable=False, comment='变更前剩余包数') after_remaining_packages = Column(Integer, nullable=False, comment='变更后剩余包数') operator_id = Column(BigInteger, ForeignKey('sys_user.user_id'), nullable=False, comment='操作人ID') occurred_at = Column(DateTime, nullable=False, comment='发生时间') idempotency_key = Column(String(64), nullable=False, comment='幂等键') request_hash = Column(String(64), nullable=False, comment='请求摘要')