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.
212 lines
9.2 KiB
Python
212 lines
9.2 KiB
Python
import uuid
|
|
from datetime import date
|
|
from typing import Any
|
|
|
|
from sqlalchemy import (
|
|
JSON,
|
|
BigInteger,
|
|
Boolean,
|
|
CheckConstraint,
|
|
Date,
|
|
ForeignKey,
|
|
ForeignKeyConstraint,
|
|
Integer,
|
|
LargeBinary,
|
|
String,
|
|
UniqueConstraint,
|
|
Uuid,
|
|
)
|
|
from sqlalchemy.orm import Mapped, mapped_column
|
|
|
|
from app.db.base import Base, PkMixin, TimestampMixin
|
|
|
|
DOCUMENT_STATUSES = (
|
|
"uploaded",
|
|
"queued",
|
|
"processing",
|
|
"ready",
|
|
"review_required",
|
|
"confirmed",
|
|
"failed",
|
|
"deleted",
|
|
)
|
|
RUN_STATUSES = ("pending", "running", "succeeded", "failed")
|
|
CANDIDATE_KINDS = ("duplicate", "transfer")
|
|
CANDIDATE_CONFIDENCES = ("unique", "possible")
|
|
CANDIDATE_RESOLUTIONS = ("pending", "is_duplicate", "is_new")
|
|
|
|
|
|
class StatementDocument(Base, PkMixin, TimestampMixin):
|
|
__tablename__ = "statement_documents"
|
|
__table_args__ = (
|
|
ForeignKeyConstraint(
|
|
["space_id", "account_id"],
|
|
["accounts.space_id", "accounts.id"],
|
|
name="fk_documents_account_tenant",
|
|
),
|
|
UniqueConstraint("space_id", "content_sha256", name="uq_documents_space_sha"),
|
|
UniqueConstraint("space_id", "id", name="uq_documents_space_id"),
|
|
)
|
|
|
|
space_id: Mapped[uuid.UUID] = mapped_column(
|
|
Uuid, ForeignKey("financial_spaces.id", ondelete="CASCADE"), nullable=False, index=True
|
|
)
|
|
account_id: Mapped[uuid.UUID | None] = mapped_column(Uuid, nullable=True, index=True)
|
|
original_filename: Mapped[str] = mapped_column(String(300), nullable=False)
|
|
size_bytes: Mapped[int] = mapped_column(BigInteger, nullable=False)
|
|
content_sha256: Mapped[str] = mapped_column(String(64), nullable=False, index=True)
|
|
object_key: Mapped[str] = mapped_column(String(64), nullable=False, unique=True)
|
|
dek_wrapped: Mapped[bytes | None] = mapped_column(LargeBinary, nullable=True)
|
|
dek_wrap_nonce: Mapped[bytes | None] = mapped_column(LargeBinary, nullable=True)
|
|
content_nonce: Mapped[bytes | None] = mapped_column(LargeBinary, nullable=True)
|
|
adapter_id: Mapped[str | None] = mapped_column(String(48), nullable=True)
|
|
account_fingerprint: Mapped[str | None] = mapped_column(String(128), nullable=True)
|
|
status: Mapped[str] = mapped_column(String(20), nullable=False, default="uploaded", index=True)
|
|
error_code: Mapped[str | None] = mapped_column(String(64), nullable=True)
|
|
reconciliation: Mapped[dict[str, Any] | None] = mapped_column(JSON, nullable=True)
|
|
needs_account_mapping: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
|
|
active_parse_run_id: Mapped[uuid.UUID | None] = mapped_column(Uuid, nullable=True)
|
|
uploaded_by_user_id: Mapped[uuid.UUID | None] = mapped_column(
|
|
Uuid, ForeignKey("users.id", ondelete="SET NULL"), nullable=True
|
|
)
|
|
|
|
|
|
class ParseRun(Base, PkMixin, TimestampMixin):
|
|
__tablename__ = "parse_runs"
|
|
__table_args__ = (
|
|
ForeignKeyConstraint(
|
|
["space_id", "document_id"],
|
|
["statement_documents.space_id", "statement_documents.id"],
|
|
ondelete="CASCADE",
|
|
name="fk_parse_runs_document_tenant",
|
|
),
|
|
UniqueConstraint("space_id", "id", name="uq_parse_runs_space_id"),
|
|
)
|
|
|
|
space_id: Mapped[uuid.UUID] = mapped_column(
|
|
Uuid, ForeignKey("financial_spaces.id", ondelete="CASCADE"), nullable=False, index=True
|
|
)
|
|
document_id: Mapped[uuid.UUID] = mapped_column(Uuid, nullable=False, index=True)
|
|
adapter_id: Mapped[str | None] = mapped_column(String(48), nullable=True)
|
|
parser_version: Mapped[str] = mapped_column(String(16), nullable=False, default="1")
|
|
status: Mapped[str] = mapped_column(String(16), nullable=False, default="pending")
|
|
error_code: Mapped[str | None] = mapped_column(String(64), nullable=True)
|
|
draft: Mapped[dict[str, Any] | None] = mapped_column(JSON, nullable=True)
|
|
|
|
|
|
class SourceRecord(Base, PkMixin, TimestampMixin):
|
|
"""Immutable extracted bank fact with document origin coordinates."""
|
|
|
|
__tablename__ = "source_records"
|
|
__table_args__ = (
|
|
ForeignKeyConstraint(
|
|
["space_id", "document_id"],
|
|
["statement_documents.space_id", "statement_documents.id"],
|
|
name="fk_source_records_document_tenant",
|
|
),
|
|
ForeignKeyConstraint(
|
|
["space_id", "parse_run_id"],
|
|
["parse_runs.space_id", "parse_runs.id"],
|
|
ondelete="CASCADE",
|
|
name="fk_source_records_run_tenant",
|
|
),
|
|
ForeignKeyConstraint(
|
|
["space_id", "duplicate_operation_id"],
|
|
["operations.space_id", "operations.id"],
|
|
name="fk_source_records_duplicate_tenant",
|
|
),
|
|
UniqueConstraint("space_id", "id", name="uq_source_records_space_id"),
|
|
)
|
|
|
|
space_id: Mapped[uuid.UUID] = mapped_column(
|
|
Uuid, ForeignKey("financial_spaces.id", ondelete="CASCADE"), nullable=False, index=True
|
|
)
|
|
document_id: Mapped[uuid.UUID | None] = mapped_column(Uuid, nullable=True, index=True)
|
|
parse_run_id: Mapped[uuid.UUID] = mapped_column(Uuid, nullable=False, index=True)
|
|
external_id: Mapped[str | None] = mapped_column(String(120), nullable=True)
|
|
occurred_on: Mapped[date | None] = mapped_column(Date, nullable=True)
|
|
posted_on: Mapped[date | None] = mapped_column(Date, nullable=True)
|
|
amount_kopecks: Mapped[int] = mapped_column(BigInteger, nullable=False)
|
|
direction: Mapped[str] = mapped_column(String(8), nullable=False)
|
|
description: Mapped[str | None] = mapped_column(String(500), nullable=True)
|
|
mcc: Mapped[int | None] = mapped_column(Integer, nullable=True)
|
|
bank_category: Mapped[str | None] = mapped_column(String(100), nullable=True)
|
|
location: Mapped[str | None] = mapped_column(String(200), nullable=True)
|
|
row_ref: Mapped[str] = mapped_column(String(120), nullable=False)
|
|
field_warnings: Mapped[dict[str, Any] | None] = mapped_column(JSON, nullable=True)
|
|
critical: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
|
|
record_fp: Mapped[str] = mapped_column(String(64), nullable=False, index=True)
|
|
review_decision: Mapped[str] = mapped_column(String(16), nullable=False, default="create")
|
|
duplicate_operation_id: Mapped[uuid.UUID | None] = mapped_column(Uuid, nullable=True)
|
|
|
|
|
|
class SourceRecordLink(Base, PkMixin, TimestampMixin):
|
|
__tablename__ = "source_record_links"
|
|
__table_args__ = (
|
|
ForeignKeyConstraint(
|
|
["space_id", "source_record_id"],
|
|
["source_records.space_id", "source_records.id"],
|
|
ondelete="CASCADE",
|
|
name="fk_links_source_record_tenant",
|
|
),
|
|
ForeignKeyConstraint(
|
|
["space_id", "operation_id"],
|
|
["operations.space_id", "operations.id"],
|
|
name="fk_links_operation_tenant",
|
|
),
|
|
UniqueConstraint("source_record_id", name="uq_links_source_record"),
|
|
UniqueConstraint("space_id", "id", name="uq_links_space_id"),
|
|
)
|
|
|
|
space_id: Mapped[uuid.UUID] = mapped_column(
|
|
Uuid, ForeignKey("financial_spaces.id", ondelete="CASCADE"), nullable=False, index=True
|
|
)
|
|
source_record_id: Mapped[uuid.UUID] = mapped_column(Uuid, nullable=False)
|
|
operation_id: Mapped[uuid.UUID] = mapped_column(Uuid, nullable=False, index=True)
|
|
kind: Mapped[str] = mapped_column(String(16), nullable=False)
|
|
|
|
|
|
class DedupCandidate(Base, PkMixin, TimestampMixin):
|
|
__tablename__ = "dedup_candidates"
|
|
__table_args__ = (
|
|
CheckConstraint("candidate_kind IN ('duplicate', 'transfer')", name="kind_valid"),
|
|
CheckConstraint(
|
|
"resolution IN ('pending', 'is_duplicate', 'is_new', 'accepted_transfer')",
|
|
name="resolution_valid",
|
|
),
|
|
ForeignKeyConstraint(
|
|
["space_id", "source_record_id"],
|
|
["source_records.space_id", "source_records.id"],
|
|
ondelete="CASCADE",
|
|
name="fk_candidates_source_tenant",
|
|
),
|
|
ForeignKeyConstraint(
|
|
["space_id", "existing_operation_id"],
|
|
["operations.space_id", "operations.id"],
|
|
name="fk_candidates_operation_tenant",
|
|
),
|
|
ForeignKeyConstraint(
|
|
["space_id", "existing_source_record_id"],
|
|
["source_records.space_id", "source_records.id"],
|
|
ondelete="CASCADE",
|
|
name="fk_candidates_existing_source_tenant",
|
|
),
|
|
UniqueConstraint("space_id", "id", name="uq_candidates_space_id"),
|
|
)
|
|
|
|
space_id: Mapped[uuid.UUID] = mapped_column(
|
|
Uuid, ForeignKey("financial_spaces.id", ondelete="CASCADE"), nullable=False, index=True
|
|
)
|
|
source_record_id: Mapped[uuid.UUID] = mapped_column(Uuid, nullable=False, index=True)
|
|
candidate_kind: Mapped[str] = mapped_column(String(16), nullable=False, default="duplicate")
|
|
existing_operation_id: Mapped[uuid.UUID | None] = mapped_column(Uuid, nullable=True, index=True)
|
|
existing_source_record_id: Mapped[uuid.UUID | None] = mapped_column(
|
|
Uuid, nullable=True, index=True
|
|
)
|
|
confidence: Mapped[str] = mapped_column(String(16), nullable=False, default="possible")
|
|
resolution: Mapped[str] = mapped_column(String(16), nullable=False, default="pending")
|
|
replacement_snapshot: Mapped[dict | None] = mapped_column(JSON, nullable=True)
|
|
decided_by_user_id: Mapped[uuid.UUID | None] = mapped_column(
|
|
Uuid, ForeignKey("users.id", ondelete="SET NULL"), nullable=True
|
|
)
|