quartermaster/src/quartermaster/models.py

158 lines
4.7 KiB
Python
Raw Normal View History

from __future__ import annotations
import enum
from datetime import datetime
from decimal import Decimal
from sqlalchemy import (
CheckConstraint,
DateTime,
Enum,
ForeignKey,
Numeric,
String,
UniqueConstraint,
func,
)
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship
class Section(str, enum.Enum):
income = "income"
fixed_bill = "fixed_bill"
debt_minimum = "debt_minimum"
food = "food"
subscription = "subscription"
other = "other"
SECTION_LABELS: dict[Section, str] = {
Section.income: "Incomes",
Section.fixed_bill: "Fixed Amount Bills",
Section.debt_minimum: "Debt Minimums",
Section.food: "Food and Essentials",
Section.subscription: "Subscriptions",
Section.other: "Other",
}
class Base(DeclarativeBase):
pass
class Entry(Base):
__tablename__ = "entry"
id: Mapped[int] = mapped_column(primary_key=True)
section: Mapped[Section] = mapped_column(
Enum(Section, native_enum=False, length=32), nullable=False, index=True
)
name: Mapped[str] = mapped_column(String(128), nullable=False)
amount: Mapped[Decimal] = mapped_column(Numeric(10, 2), nullable=False)
created_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), server_default=func.now(), nullable=False
)
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True),
server_default=func.now(),
onupdate=func.now(),
nullable=False,
)
class DebtTarget(Base):
__tablename__ = "debt_target"
__table_args__ = (CheckConstraint("id = 1", name="debt_target_singleton"),)
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=False)
debt_minimum_id: Mapped[int | None] = mapped_column(
ForeignKey("entry.id", ondelete="SET NULL"), nullable=True
)
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True),
server_default=func.now(),
onupdate=func.now(),
nullable=False,
)
entry: Mapped[Entry | None] = relationship(Entry, lazy="joined")
class Month(Base):
__tablename__ = "month"
id: Mapped[int] = mapped_column(primary_key=True)
year_month: Mapped[str] = mapped_column(String(7), nullable=False, unique=True)
created_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), server_default=func.now(), nullable=False
)
entries: Mapped[list["MonthEntry"]] = relationship(
back_populates="month", cascade="all, delete-orphan"
)
target: Mapped["MonthDebtTarget | None"] = relationship(
back_populates="month",
cascade="all, delete-orphan",
uselist=False,
lazy="joined",
)
class MonthEntry(Base):
__tablename__ = "month_entry"
__table_args__ = (UniqueConstraint("month_id", "id"),)
id: Mapped[int] = mapped_column(primary_key=True)
month_id: Mapped[int] = mapped_column(
ForeignKey("month.id", ondelete="CASCADE"), nullable=False, index=True
)
section: Mapped[Section] = mapped_column(
Enum(Section, native_enum=False, length=32), nullable=False, index=True
)
name: Mapped[str] = mapped_column(String(128), nullable=False)
planned: Mapped[Decimal] = mapped_column(Numeric(10, 2), nullable=False)
applied: Mapped[Decimal] = mapped_column(
Numeric(10, 2), nullable=False, default=Decimal("0.00"),
server_default="0.00",
)
origin_name: Mapped[str | None] = mapped_column(String(128), nullable=True)
origin_planned: Mapped[Decimal | None] = mapped_column(
Numeric(10, 2), nullable=True
)
source_entry_id: Mapped[int | None] = mapped_column(
ForeignKey("entry.id", ondelete="SET NULL"), nullable=True
)
created_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), server_default=func.now(), nullable=False
)
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True),
server_default=func.now(),
onupdate=func.now(),
nullable=False,
)
month: Mapped[Month] = relationship(back_populates="entries")
class MonthDebtTarget(Base):
__tablename__ = "month_debt_target"
month_id: Mapped[int] = mapped_column(
ForeignKey("month.id", ondelete="CASCADE"),
primary_key=True,
autoincrement=False,
)
month_entry_id: Mapped[int | None] = mapped_column(
ForeignKey("month_entry.id", ondelete="SET NULL"), nullable=True
)
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True),
server_default=func.now(),
onupdate=func.now(),
nullable=False,
)
month: Mapped[Month] = relationship(back_populates="target")
entry: Mapped[MonthEntry | None] = relationship(MonthEntry, lazy="joined")