2026-04-17 11:04:06 -06:00
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
import enum
|
|
|
|
|
from datetime import datetime
|
|
|
|
|
from decimal import Decimal
|
|
|
|
|
|
|
|
|
|
from sqlalchemy import (
|
|
|
|
|
CheckConstraint,
|
|
|
|
|
DateTime,
|
|
|
|
|
Enum,
|
|
|
|
|
ForeignKey,
|
|
|
|
|
Numeric,
|
|
|
|
|
String,
|
2026-04-17 11:39:50 -06:00
|
|
|
UniqueConstraint,
|
2026-04-17 11:04:06 -06:00
|
|
|
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")
|
2026-04-17 11:39:50 -06:00
|
|
|
|
|
|
|
|
|
|
|
|
|
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")
|