d582f15681
- Implemented tests for import polling functionality, ensuring correct behavior for active and terminal import statuses. - Created tests for the login page, validating user interactions and error handling. - Added unit tests for money utility functions, covering parsing, formatting, and summation of monetary values. - Developed tests for query keys, ensuring proper scoping and invalidation roots. chore: setup testing environment and configuration - Added setup file for testing with Vitest and Testing Library. - Configured TypeScript and Vite for the frontend project. - Created Docker and Nginx configurations for local development and production environments. - Established a PostgreSQL initialization script for setting up application roles. docs: add README and environment configuration for infra - Included README for the infra directory, detailing local development setup and service descriptions. - Created an example environment configuration file for local development with placeholders for sensitive information.
189 lines
7.6 KiB
Python
189 lines
7.6 KiB
Python
import uuid
|
|
from datetime import date, datetime
|
|
from typing import Any
|
|
|
|
from sqlalchemy import (
|
|
JSON,
|
|
BigInteger,
|
|
CheckConstraint,
|
|
Date,
|
|
ForeignKey,
|
|
ForeignKeyConstraint,
|
|
Integer,
|
|
String,
|
|
UniqueConstraint,
|
|
Uuid,
|
|
)
|
|
from sqlalchemy.orm import Mapped, mapped_column
|
|
|
|
from app.db.base import Base, PkMixin, TimestampMixin, UTCDateTime
|
|
|
|
PERIOD_STATUSES = ("open", "closed")
|
|
FUNDING_KINDS = ("fund", "unfund")
|
|
|
|
|
|
class BudgetPeriod(Base, PkMixin, TimestampMixin):
|
|
"""Calendar month budget period in the space timezone (month granularity)."""
|
|
|
|
__tablename__ = "budget_periods"
|
|
__table_args__ = (
|
|
CheckConstraint("status IN ('open', 'closed')", name="status_valid"),
|
|
CheckConstraint("month BETWEEN 1 AND 12", name="month_valid"),
|
|
UniqueConstraint("space_id", "year", "month", name="uq_budget_periods_space_ym"),
|
|
UniqueConstraint("space_id", "id", name="uq_budget_periods_space_id"),
|
|
)
|
|
|
|
space_id: Mapped[uuid.UUID] = mapped_column(
|
|
Uuid, ForeignKey("financial_spaces.id", ondelete="CASCADE"), nullable=False, index=True
|
|
)
|
|
year: Mapped[int] = mapped_column(nullable=False)
|
|
month: Mapped[int] = mapped_column(nullable=False)
|
|
status: Mapped[str] = mapped_column(String(8), nullable=False, default="open")
|
|
version: Mapped[int] = mapped_column(Integer, nullable=False, default=1)
|
|
|
|
__mapper_args__ = {"version_id_col": version}
|
|
|
|
|
|
class PlanAssignment(Base, PkMixin, TimestampMixin):
|
|
__tablename__ = "plan_assignments"
|
|
__table_args__ = (
|
|
CheckConstraint("planned_kopecks >= 0", name="planned_nonnegative"),
|
|
ForeignKeyConstraint(
|
|
["space_id", "period_id"],
|
|
["budget_periods.space_id", "budget_periods.id"],
|
|
ondelete="CASCADE",
|
|
name="fk_plan_assignments_period_tenant",
|
|
),
|
|
ForeignKeyConstraint(
|
|
["space_id", "category_id"],
|
|
["categories.space_id", "categories.id"],
|
|
name="fk_plan_assignments_category_tenant",
|
|
),
|
|
UniqueConstraint("period_id", "category_id", name="uq_plan_assignments_period_category"),
|
|
UniqueConstraint("space_id", "id", name="uq_plan_assignments_space_id"),
|
|
)
|
|
|
|
space_id: Mapped[uuid.UUID] = mapped_column(
|
|
Uuid, ForeignKey("financial_spaces.id", ondelete="CASCADE"), nullable=False, index=True
|
|
)
|
|
period_id: Mapped[uuid.UUID] = mapped_column(Uuid, nullable=False, index=True)
|
|
category_id: Mapped[uuid.UUID] = mapped_column(Uuid, nullable=False)
|
|
planned_kopecks: Mapped[int] = mapped_column(BigInteger, nullable=False, default=0)
|
|
version: Mapped[int] = mapped_column(Integer, nullable=False, default=1)
|
|
|
|
__mapper_args__ = {"version_id_col": version}
|
|
|
|
|
|
class ExpectedIncome(Base, PkMixin, TimestampMixin):
|
|
__tablename__ = "expected_incomes"
|
|
__table_args__ = (
|
|
CheckConstraint("amount_kopecks > 0", name="amount_positive"),
|
|
ForeignKeyConstraint(
|
|
["space_id", "period_id"],
|
|
["budget_periods.space_id", "budget_periods.id"],
|
|
ondelete="CASCADE",
|
|
name="fk_expected_incomes_period_tenant",
|
|
),
|
|
UniqueConstraint("space_id", "id", name="uq_expected_incomes_space_id"),
|
|
)
|
|
|
|
space_id: Mapped[uuid.UUID] = mapped_column(
|
|
Uuid, ForeignKey("financial_spaces.id", ondelete="CASCADE"), nullable=False, index=True
|
|
)
|
|
period_id: Mapped[uuid.UUID] = mapped_column(Uuid, nullable=False, index=True)
|
|
expected_on: Mapped[date] = mapped_column(Date, nullable=False)
|
|
amount_kopecks: Mapped[int] = mapped_column(BigInteger, nullable=False)
|
|
source_name: Mapped[str | None] = mapped_column(String(200), nullable=True)
|
|
note: Mapped[str | None] = mapped_column(String(500), nullable=True)
|
|
version: Mapped[int] = mapped_column(Integer, nullable=False, default=1)
|
|
|
|
__mapper_args__ = {"version_id_col": version}
|
|
|
|
|
|
class ExpectedIncomeMatch(Base, PkMixin, TimestampMixin):
|
|
"""Links one actual income operation to one expected income (1:1 per operation)."""
|
|
|
|
__tablename__ = "expected_income_matches"
|
|
__table_args__ = (
|
|
CheckConstraint("amount_kopecks > 0", name="amount_positive"),
|
|
ForeignKeyConstraint(
|
|
["space_id", "expected_income_id"],
|
|
["expected_incomes.space_id", "expected_incomes.id"],
|
|
ondelete="CASCADE",
|
|
name="fk_income_matches_expected_tenant",
|
|
),
|
|
ForeignKeyConstraint(
|
|
["space_id", "operation_id"],
|
|
["operations.space_id", "operations.id"],
|
|
name="fk_income_matches_operation_tenant",
|
|
),
|
|
UniqueConstraint("operation_id", name="uq_income_matches_operation"),
|
|
UniqueConstraint("space_id", "id", name="uq_income_matches_space_id"),
|
|
)
|
|
|
|
space_id: Mapped[uuid.UUID] = mapped_column(
|
|
Uuid, ForeignKey("financial_spaces.id", ondelete="CASCADE"), nullable=False, index=True
|
|
)
|
|
expected_income_id: Mapped[uuid.UUID] = mapped_column(Uuid, nullable=False, index=True)
|
|
operation_id: Mapped[uuid.UUID] = mapped_column(Uuid, nullable=False)
|
|
amount_kopecks: Mapped[int] = mapped_column(BigInteger, nullable=False)
|
|
|
|
|
|
class FundingMovement(Base, PkMixin, TimestampMixin):
|
|
"""Manual money movement between the general pool and a category/reserve."""
|
|
|
|
__tablename__ = "funding_movements"
|
|
__table_args__ = (
|
|
CheckConstraint("kind IN ('fund', 'unfund')", name="kind_valid"),
|
|
CheckConstraint("amount_kopecks > 0", name="amount_positive"),
|
|
ForeignKeyConstraint(
|
|
["space_id", "period_id"],
|
|
["budget_periods.space_id", "budget_periods.id"],
|
|
ondelete="CASCADE",
|
|
name="fk_funding_movements_period_tenant",
|
|
),
|
|
ForeignKeyConstraint(
|
|
["space_id", "category_id"],
|
|
["categories.space_id", "categories.id"],
|
|
name="fk_funding_movements_category_tenant",
|
|
),
|
|
UniqueConstraint("space_id", "id", name="uq_funding_movements_space_id"),
|
|
)
|
|
|
|
space_id: Mapped[uuid.UUID] = mapped_column(
|
|
Uuid, ForeignKey("financial_spaces.id", ondelete="CASCADE"), nullable=False, index=True
|
|
)
|
|
period_id: Mapped[uuid.UUID] = mapped_column(Uuid, nullable=False, index=True)
|
|
category_id: Mapped[uuid.UUID] = mapped_column(Uuid, nullable=False)
|
|
kind: Mapped[str] = mapped_column(String(8), nullable=False)
|
|
amount_kopecks: Mapped[int] = mapped_column(BigInteger, nullable=False)
|
|
note: Mapped[str | None] = mapped_column(String(500), nullable=True)
|
|
created_by_user_id: Mapped[uuid.UUID | None] = mapped_column(
|
|
Uuid, ForeignKey("users.id", ondelete="SET NULL"), nullable=True
|
|
)
|
|
|
|
|
|
class PeriodClosure(Base, PkMixin, TimestampMixin):
|
|
"""Immutable-at-rest snapshot written when a period closes."""
|
|
|
|
__tablename__ = "period_closures"
|
|
__table_args__ = (
|
|
ForeignKeyConstraint(
|
|
["space_id", "period_id"],
|
|
["budget_periods.space_id", "budget_periods.id"],
|
|
ondelete="CASCADE",
|
|
name="fk_period_closures_period_tenant",
|
|
),
|
|
UniqueConstraint("space_id", "id", name="uq_period_closures_space_id"),
|
|
)
|
|
|
|
space_id: Mapped[uuid.UUID] = mapped_column(
|
|
Uuid, ForeignKey("financial_spaces.id", ondelete="CASCADE"), nullable=False, index=True
|
|
)
|
|
period_id: Mapped[uuid.UUID] = mapped_column(Uuid, nullable=False)
|
|
snapshot: Mapped[dict[str, Any]] = mapped_column(JSON, nullable=False)
|
|
closed_at: Mapped[datetime] = mapped_column(UTCDateTime(), nullable=False)
|
|
closed_by_user_id: Mapped[uuid.UUID | None] = mapped_column(
|
|
Uuid, ForeignKey("users.id", ondelete="SET NULL"), nullable=True
|
|
)
|