"""Modèles C4 : contrats photocopieurs et stock de consommables partagé.""" from datetime import datetime, timezone from ...extensions import db class PhotocopierContract(db.Model): __tablename__ = "photocopier_contracts" id = db.Column(db.Integer, primary_key=True) supplier_id = db.Column(db.Integer, db.ForeignKey("companies.id"), nullable=False, index=True) name = db.Column(db.String(200), nullable=False) reference = db.Column(db.String(120), nullable=True) start_date = db.Column(db.Date, nullable=False) end_date = db.Column(db.Date, nullable=False) quota_start_date = db.Column(db.Date, nullable=True) anniversary_month = db.Column(db.Integer, nullable=False) anniversary_day = db.Column(db.Integer, nullable=False) status = db.Column(db.String(20), nullable=False, default="ACTIVE", index=True) notes = db.Column(db.Text, nullable=True) created_at = db.Column(db.DateTime, nullable=False, default=lambda: datetime.now(timezone.utc)) closed_at = db.Column(db.Date, nullable=True) supplier = db.relationship("Company") periods = db.relationship("PhotocopierContractPeriod", back_populates="contract", cascade="all, delete-orphan", order_by="PhotocopierContractPeriod.start_date") assignments = db.relationship("ContractEquipmentAssignment", back_populates="contract", cascade="all, delete-orphan") class PhotocopierContractPeriod(db.Model): __tablename__ = "photocopier_contract_periods" __table_args__ = ( db.UniqueConstraint("contract_id", "start_date", name="uq_c4_contract_period_start"), db.CheckConstraint("end_date >= start_date", name="ck_c4_period_dates"), db.CheckConstraint("(quota_bw IS NULL OR quota_bw >= 0) AND (quota_color IS NULL OR quota_color >= 0)", name="ck_c4_period_quotas"), ) id = db.Column(db.Integer, primary_key=True) contract_id = db.Column(db.Integer, db.ForeignKey("photocopier_contracts.id", ondelete="CASCADE"), nullable=False, index=True) start_date = db.Column(db.Date, nullable=False, index=True) end_date = db.Column(db.Date, nullable=False, index=True) quota_bw = db.Column(db.Integer, nullable=True) quota_color = db.Column(db.Integer, nullable=True) bw_included_unit_price = db.Column(db.Numeric(12, 6), nullable=True) bw_overage_unit_price = db.Column(db.Numeric(12, 6), nullable=True) color_included_unit_price = db.Column(db.Numeric(12, 6), nullable=True) color_overage_unit_price = db.Column(db.Numeric(12, 6), nullable=True) status = db.Column(db.String(20), nullable=False, default="OPEN") contract = db.relationship("PhotocopierContract", back_populates="periods") class ContractEquipmentAssignment(db.Model): __tablename__ = "contract_equipment_assignments" __table_args__ = ( db.UniqueConstraint("contract_id", "equipment_id", "entry_date", name="uq_c4_contract_equipment_entry"), db.CheckConstraint("exit_date IS NULL OR exit_date >= entry_date", name="ck_c4_assignment_dates"), ) id = db.Column(db.Integer, primary_key=True) contract_id = db.Column(db.Integer, db.ForeignKey("photocopier_contracts.id", ondelete="CASCADE"), nullable=False, index=True) equipment_id = db.Column(db.Integer, db.ForeignKey("equipments.id", ondelete="CASCADE"), nullable=False, index=True) entry_date = db.Column(db.Date, nullable=False) exit_date = db.Column(db.Date, nullable=True) replacement_reason = db.Column(db.String(40), nullable=True) replacement_comment = db.Column(db.Text, nullable=True) contract = db.relationship("PhotocopierContract", back_populates="assignments") equipment = db.relationship("Equipment") class ConsumableOrder(db.Model): __tablename__ = "consumable_orders" __table_args__ = ( db.CheckConstraint("quantity_ordered > 0", name="ck_c4_order_quantity"), db.CheckConstraint("quantity_received >= 0 AND quantity_received <= quantity_ordered", name="ck_c4_order_received"), ) id = db.Column(db.Integer, primary_key=True) consumable_id = db.Column(db.Integer, db.ForeignKey("consumables.id", ondelete="RESTRICT"), nullable=False, index=True) supplier_id = db.Column(db.Integer, db.ForeignKey("companies.id"), nullable=True, index=True) quantity_ordered = db.Column(db.Integer, nullable=False) quantity_received = db.Column(db.Integer, nullable=False, default=0) status = db.Column(db.String(30), nullable=False, default="TO_ORDER", index=True) ordered_at = db.Column(db.DateTime, nullable=True) received_at = db.Column(db.DateTime, nullable=True) comment = db.Column(db.Text, nullable=True) created_by_id = db.Column(db.Integer, db.ForeignKey("users.id", ondelete="SET NULL"), nullable=True) cancelled_at = db.Column(db.DateTime, nullable=True) cancelled_by_id = db.Column(db.Integer, db.ForeignKey("users.id", ondelete="SET NULL"), nullable=True) cancellation_reason = db.Column(db.Text, nullable=True) consumable = db.relationship("Consumable") supplier = db.relationship("Company") created_by = db.relationship("User", foreign_keys=[created_by_id]) cancelled_by = db.relationship("User", foreign_keys=[cancelled_by_id]) receipts = db.relationship("ConsumableReceipt", back_populates="order", cascade="all, delete-orphan") @property def remaining_quantity(self): return max((self.quantity_ordered or 0) - (self.quantity_received or 0), 0) class ConsumableReceipt(db.Model): __tablename__ = "consumable_receipts" id = db.Column(db.Integer, primary_key=True) order_id = db.Column(db.Integer, db.ForeignKey("consumable_orders.id", ondelete="CASCADE"), nullable=False, index=True) quantity = db.Column(db.Integer, nullable=False) received_at = db.Column(db.DateTime, nullable=False, default=lambda: datetime.now(timezone.utc)) received_by_id = db.Column(db.Integer, db.ForeignKey("users.id", ondelete="SET NULL"), nullable=True) comment = db.Column(db.Text, nullable=True) order = db.relationship("ConsumableOrder", back_populates="receipts") received_by = db.relationship("User")