from datetime import date, datetime from decimal import Decimal from sqlalchemy import Date, DateTime, ForeignKey, Integer, Numeric, String, Text, func from sqlalchemy.orm import Mapped, mapped_column, relationship from app.db.database import Base class RepairEstimate(Base): __tablename__ = "repair_estimates" id: Mapped[int] = mapped_column(primary_key=True) repair_id: Mapped[int] = mapped_column(ForeignKey("repairs.id", ondelete="CASCADE"), index=True) estimate_number: Mapped[str] = mapped_column(String(20), unique=True, index=True) status: Mapped[str] = mapped_column(String(40), default="draft", server_default="draft", index=True) title: Mapped[str] = mapped_column(String(255)) customer_message: Mapped[str] = mapped_column(Text, default="", server_default="") internal_note: Mapped[str | None] = mapped_column(Text, nullable=True) subtotal_cents: Mapped[int] = mapped_column(Integer, default=0, server_default="0") tax_rate_percent: Mapped[Decimal] = mapped_column(Numeric(5, 2), default=Decimal("19.00"), server_default="19.00") tax_cents: Mapped[int] = mapped_column(Integer, default=0, server_default="0") total_cents: Mapped[int] = mapped_column(Integer, default=0, server_default="0") currency: Mapped[str] = mapped_column(String(3), default="EUR", server_default="EUR") valid_until: Mapped[date | None] = mapped_column(Date, nullable=True) sent_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True) approved_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True) declined_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True) customer_response_message: Mapped[str | None] = mapped_column(Text, nullable=True) lexware_invoice_id: Mapped[str | None] = mapped_column(String(80), nullable=True) lexware_invoice_number: Mapped[str | None] = mapped_column(String(80), nullable=True) lexware_invoice_status: Mapped[str | None] = mapped_column(String(80), nullable=True) lexware_synced_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True) created_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True, index=True) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now()) updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now()) repair = relationship("Repair", lazy="joined") created_by = relationship("User", lazy="joined") items: Mapped[list["RepairEstimateItem"]] = relationship( back_populates="estimate", cascade="all, delete-orphan", order_by="RepairEstimateItem.position", lazy="selectin", ) events: Mapped[list["RepairEstimateEvent"]] = relationship( back_populates="estimate", cascade="all, delete-orphan", order_by="RepairEstimateEvent.created_at", lazy="selectin", ) class RepairEstimateItem(Base): __tablename__ = "repair_estimate_items" id: Mapped[int] = mapped_column(primary_key=True) estimate_id: Mapped[int] = mapped_column(ForeignKey("repair_estimates.id", ondelete="CASCADE"), index=True) inventory_item_id: Mapped[int | None] = mapped_column(ForeignKey("inventory_items.id", ondelete="SET NULL"), nullable=True, index=True) inventory_snapshot_name: Mapped[str] = mapped_column(String(255), default="", server_default="") inventory_snapshot_sku: Mapped[str] = mapped_column(String(40), default="", server_default="") inventory_snapshot_manufacturer: Mapped[str | None] = mapped_column(String(180), nullable=True) inventory_snapshot_part_number: Mapped[str | None] = mapped_column(String(180), nullable=True) position: Mapped[int] = mapped_column(Integer) item_type: Mapped[str] = mapped_column(String(40), index=True) title: Mapped[str] = mapped_column(String(255)) description: Mapped[str | None] = mapped_column(Text, nullable=True) quantity: Mapped[Decimal] = mapped_column(Numeric(10, 2), default=Decimal("1.00"), server_default="1.00") unit: Mapped[str] = mapped_column(String(40), default="Stk.", server_default="Stk.") unit_price_cents: Mapped[int] = mapped_column(Integer, default=0, server_default="0") total_cents: Mapped[int] = mapped_column(Integer, default=0, server_default="0") created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now()) updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now()) estimate: Mapped[RepairEstimate] = relationship(back_populates="items") inventory_item = relationship("InventoryItem", lazy="joined") class RepairEstimateEvent(Base): __tablename__ = "repair_estimate_events" id: Mapped[int] = mapped_column(primary_key=True) estimate_id: Mapped[int] = mapped_column(ForeignKey("repair_estimates.id", ondelete="CASCADE"), index=True) event_type: Mapped[str] = mapped_column(String(40), index=True) actor_type: Mapped[str] = mapped_column(String(40), index=True) actor_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True, index=True) note: Mapped[str | None] = mapped_column(Text, nullable=True) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now()) estimate: Mapped[RepairEstimate] = relationship(back_populates="events") actor = relationship("User", lazy="joined")