# app/models/inbound/semi.py from app.extensions import db from datetime import datetime class StockSemi(db.Model): """ 半成品入库库存表 对应数据库表: stock_semi """ __tablename__ = 'stock_semi' # ========================================================= # 1. 基础字段 (Strictly matching SQL Schema) # ========================================================= # 主键 id = db.Column(db.Integer, primary_key=True) # 外键关联 material_base 表 base_id = db.Column(db.Integer, db.ForeignKey('material_base.id'), nullable=False) # 身份标识 sku = db.Column(db.String(100)) # SQL字段名为 production_date, 对应前端的 "入库日期/生产日期" production_date = db.Column(db.Date) barcode = db.Column(db.String(100)) # 条码 serial_number = db.Column(db.String(100)) # 序列号 # 注意:提供的 SQL 中 stock_semi 没有 batch_number 字段,这里不定义,以免报错。 # 如果后续数据库加上了该字段,请取消下方注释: # batch_number = db.Column(db.String(100)) # --- 数量 --- in_quantity = db.Column(db.Numeric(19, 4), default=0) stock_quantity = db.Column(db.Numeric(19, 4), default=0) available_quantity = db.Column(db.Numeric(19, 4), default=0) # --- 状态与位置 --- status = db.Column(db.String(50)) # 在库/出库/损耗 warehouse_location = db.Column(db.String(100)) # 仓库位 # ========================================================= # 2. 半成品特有字段 (SQL 字段映射) # ========================================================= # BOM 相关 # 数据库列名: bom_id, Python属性: bom_code (为了适配前端习惯) bom_code = db.Column('bom_id', db.String(100)) bom_version = db.Column(db.String(50)) # 工单 相关 # 数据库列名: work_order_id, Python属性: work_order_code work_order_code = db.Column('work_order_id', db.String(100)) # 成本 相关 raw_material_cost = db.Column(db.Numeric(19, 4), default=0) # 原材料成本 manual_cost = db.Column(db.Numeric(19, 4), default=0) # 手动/人工成本 # 生产信息 # 数据库列名: producer_name, Python属性: production_manager production_manager = db.Column('producer_name', db.String(100)) # 生产起止时间 (SQL定义为 VARCHAR(255)) production_time_range = db.Column(db.String(255)) # 质量与链接 quality_status = db.Column(db.String(50)) # 质量状态 quality_report_link = db.Column(db.Text) # 质量报告链接 detail_link = db.Column(db.Text) # 详细信息链接 # ========================================================= # 3. 关系定义 # ========================================================= # 建立与 MaterialBase 的关系 # 注意:确保 MaterialBase 模型中定义了 back_populates='stock_semis' material = db.relationship('MaterialBase', back_populates='stock_semis') def to_dict(self): """ 序列化:将模型转换为字典,供API返回JSON使用 在这里处理字段名称转换,确保前端能正确显示数据 """ # 计算单件总成本 (原料 + 人工) raw_val = float(self.raw_material_cost or 0) man_val = float(self.manual_cost or 0) unit_total = raw_val + man_val return { 'id': self.id, 'base_id': self.base_id, # --- 级联基础信息 (防止 None 报错) --- 'material_name': self.material.name if self.material else '', 'spec_model': self.material.spec_model if self.material else '', 'category': self.material.category if self.material else '', 'unit': self.material.unit if self.material else '', 'material_type': self.material.material_type if self.material else '', # --- 实体信息 --- 'sku': self.sku, # 将 production_date 映射回前端通用的 inbound_date 'inbound_date': self.production_date.strftime('%Y-%m-%d') if self.production_date else '', 'barcode': self.barcode, 'serial_number': self.serial_number, # 'batch_number': self.batch_number, # SQL无此字段,暂不返回 'warehouse_loc': self.warehouse_location, 'status': self.status, # --- 数量 (转为float防止json序列化报错) --- 'in_quantity': float(self.in_quantity or 0), 'qty_inbound': float(self.in_quantity or 0), # 兼容字段 'stock_quantity': float(self.stock_quantity or 0), 'qty_stock': float(self.stock_quantity or 0), # 兼容字段 'available_quantity': float(self.available_quantity or 0), 'qty_available': float(self.available_quantity or 0), # 兼容字段 # --- 半成品特有数据 --- 'bom_code': self.bom_code, 'bom_version': self.bom_version, 'work_order_code': self.work_order_code, 'raw_material_cost': raw_val, 'manual_cost': man_val, 'unit_total_cost': unit_total, # 前端展示总成本用 'production_manager': self.production_manager, # 时间范围 (SQL存的是字符串,直接返回即可,或者根据需要拆分) # 如果 service 层存的是 "Start ~ End",这里直接返回 'production_time_range': self.production_time_range, # 为了兼容前端分开的时间字段(如果有): 'production_start_time': self.production_time_range.split(' ~ ')[ 0] if self.production_time_range and ' ~ ' in self.production_time_range else '', 'production_end_time': self.production_time_range.split(' ~ ')[ 1] if self.production_time_range and ' ~ ' in self.production_time_range else '', 'quality_status': self.quality_status, 'quality_report_link': self.quality_report_link, 'detail_link': self.detail_link }