Olympus/backend/hermes/app/models/repair_estimate.py

108 lines
6.2 KiB
Python

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)
accounting_export_status: Mapped[str | None] = mapped_column(String(40), nullable=True)
accounting_note: Mapped[str | None] = mapped_column(Text, nullable=True)
accounting_transferred_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
accounting_transferred_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), 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",
foreign_keys="RepairEstimate.created_by_user_id",
lazy="joined",
)
accounting_transferred_by = relationship(
"User",
foreign_keys="RepairEstimate.accounting_transferred_by_user_id",
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",
foreign_keys="RepairEstimateEvent.actor_user_id",
lazy="joined",
)