from app.extensions import db, beijing_time from datetime import datetime from sqlalchemy import func class TransBorrow(db.Model): __tablename__ = 'trans_borrow' id = db.Column(db.Integer, primary_key=True) borrow_no = db.Column(db.String(100)) sku = db.Column(db.String(100)) source_table = db.Column(db.String(50)) stock_id = db.Column(db.Integer) barcode = db.Column(db.String(100)) quantity = db.Column(db.Numeric(19, 4)) returned_quantity = db.Column(db.Numeric(19, 4), default=0) borrower_name = db.Column(db.String(100)) borrow_time = db.Column(db.DateTime, default=beijing_time) borrow_signature = db.Column(db.Text) expected_return_time = db.Column(db.DateTime) is_returned = db.Column(db.Boolean, default=False) return_time = db.Column(db.DateTime) return_operator = db.Column(db.String(100)) return_signature = db.Column(db.Text) return_location = db.Column(db.String(100)) status = db.Column(db.String(20), default='borrowed') remark = db.Column(db.Text) def to_dict(self): returned_qty = float(self.returned_quantity) if self.returned_quantity is not None else 0 total_qty = float(self.quantity) if self.quantity is not None else 0 pending_qty = total_qty - returned_qty return { 'id': self.id, 'borrow_no': self.borrow_no, 'sku': self.sku, 'source_table': self.source_table, 'stock_id': self.stock_id, 'barcode': self.barcode, 'quantity': total_qty, 'returned_quantity': returned_qty, 'pending_quantity': pending_qty, 'borrower_name': self.borrower_name, 'borrow_time': self.borrow_time.strftime('%Y-%m-%d %H:%M') if self.borrow_time else None, 'borrow_signature': self.borrow_signature, 'expected_return_time': self.expected_return_time.strftime('%Y-%m-%d %H:%M') if self.expected_return_time else None, 'is_returned': self.is_returned, 'return_time': self.return_time.strftime('%Y-%m-%d %H:%M') if self.return_time else None, 'return_operator': self.return_operator, 'return_signature': self.return_signature, 'return_location': self.return_location, 'status': self.status, 'remark': self.remark, } @classmethod def get_borrowed_quantity(cls, source_table, stock_id): """ 获取指定库存记录(source_table 和 stock_id)的借出未还数量总和。 返回浮点数,若无借出记录则返回 0.0。 """ result = db.session.query(func.sum(cls.quantity)).filter( cls.source_table == source_table, cls.stock_id == stock_id, cls.is_returned == False ).scalar() return float(result) if result is not None else 0.0 class TransRepair(db.Model): __tablename__ = 'trans_repair' id = db.Column(db.Integer, primary_key=True, autoincrement=True) # 维修单号 (新增) repair_no = db.Column(db.String(50), nullable=True, unique=True) # 关联基础信息 (新增) base_id = db.Column(db.Integer, db.ForeignKey('material_base.id'), nullable=True) # SKU 保留 sku = db.Column(db.String(100)) # 物料名称 (独立录入时使用,非关联base_id) material_name = db.Column(db.String(200)) # 序列号SN (新增,用于单台追溯) serial_number = db.Column(db.String(100), nullable=True) # 来源追溯 (兼容旧数据) source_table = db.Column(db.String(50)) stock_id = db.Column(db.Integer) is_self_made = db.Column(db.Boolean, default=False) related_product_id = db.Column(db.Integer) # 入库/接收时间 arrival_date = db.Column(db.Date) # 维修状态 (新增) repair_status = db.Column(db.String(50), default='待检测') # 客户反馈 fault_description = db.Column(db.Text) # 预计修复时间 expected_repair_time = db.Column(db.String(100)) # 维修日志/结果 repair_result = db.Column(db.Text) # 维修人 repair_manager = db.Column(db.String(100)) # 出库交付时间 shipping_date = db.Column(db.Date) # 客户名/来源 related_contract_id = db.Column(db.String(100)) # 客户名称 (新增) customer_name = db.Column(db.String(100)) # 客户所在地 (新增) customer_location = db.Column(db.String(255)) # 成本与售价 cost_price = db.Column(db.Numeric(19, 4)) sale_price = db.Column(db.Numeric(19, 4)) # 数据隔离 (新增) company_id = db.Column(db.Integer, nullable=True) # 关联关系 base = db.relationship('MaterialBase', backref='repairs') def to_dict(self): return { 'id': self.id, 'repair_no': self.repair_no, 'base_id': self.base_id, 'sku': self.sku, 'material_name': self.material_name, 'serial_number': self.serial_number, 'source_table': self.source_table, 'stock_id': self.stock_id, 'arrival_date': self.arrival_date.strftime('%Y-%m-%d') if self.arrival_date else None, 'repair_status': self.repair_status, 'expected_repair_time': self.expected_repair_time, 'shipping_date': self.shipping_date.strftime('%Y-%m-%d') if self.shipping_date else None, 'is_self_made': self.is_self_made, 'related_product_id': self.related_product_id, 'related_contract_id': self.related_contract_id, 'customer_name': self.customer_name, 'customer_location': self.customer_location, 'repair_manager': self.repair_manager, 'fault_description': self.fault_description, 'repair_result': self.repair_result, 'cost_price': float(self.cost_price) if self.cost_price is not None else None, 'sale_price': float(self.sale_price) if self.sale_price is not None else None, 'company_id': self.company_id, } class TransScrap(db.Model): __tablename__ = 'trans_scrap' id = db.Column(db.Integer, primary_key=True) sku = db.Column(db.String(100)) source_table = db.Column(db.String(50)) stock_id = db.Column(db.Integer) quantity = db.Column(db.Numeric(19, 4)) reason = db.Column(db.Text) operator_name = db.Column(db.String(100)) operation_time = db.Column(db.DateTime, default=beijing_time) approver_name = db.Column(db.String(100)) approval_status = db.Column(db.String(20), default='pending') cost_at_scrap = db.Column(db.Numeric(19, 4)) total_loss = db.Column(db.Numeric(19, 4)) def to_dict(self): return { 'id': self.id, 'sku': self.sku, 'source_table': self.source_table, 'stock_id': self.stock_id, 'quantity': float(self.quantity) if self.quantity is not None else None, 'reason': self.reason, 'operator_name': self.operator_name, 'operation_time': self.operation_time.strftime('%Y-%m-%d %H:%M:%S') if self.operation_time else None, 'approver_name': self.approver_name, 'approval_status': self.approval_status, 'cost_at_scrap': float(self.cost_at_scrap) if self.cost_at_scrap is not None else None, 'total_loss': float(self.total_loss) if self.total_loss is not None else None, }