arm-core/api/src/models/accounting.py
Andrey Epifancev 4cc926c033 КПП юридического лица: legal_entity.kpp, миграция 0005, API, импорт (DBML v5.17, #45)
- модели LegalEntity.kpp / LegalEntityHistory.kpp (последней колонкой), миграция 0005_dbml_v5_17 (upgrade/downgrade);
- domain/kpp.py: 9 символов (4 цифры, 2 цифры/латинские A-Z, 3 цифры), у ИП запрещён, иначе 422 INVALID_KPP;
- схемы Create/Update/Out/Detail/HistoryOut, PATCH пишет снимок history и аудит before/after с kpp, поиск q по КПП;
- импорт справочника: необязательная колонка КПП (синоним kpp), код INVALID_KPP, update обновляет непустой КПП;
- dev-seed: необязательный ключ kpp (значений в данных нет, не выдумываем);
- сверка с DBML, Makefile, README, CLAUDE.md, openspec/config.yaml — на v5.17.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EAKRDNzXtRrwmWz9z6PZu3
2026-10-07 18:51:50 +04:00

65 lines
3.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Часть 03 DBML v5.17 (учёт): план счетов и правила элиминации ВГО (Прил. 3/4 ТЗ как данные)."""
import datetime
import uuid
from sqlalchemy import Boolean, Date, ForeignKey, Index, Integer, SmallInteger, String, Text, text
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm import Mapped, mapped_column
from .base import Base, UUIDPrimaryMixin
from .enums import AccountKind, AccountRowType, StatementType, pg_enum
class ChartOfAccount(UUIDPrimaryMixin, Base):
"""План счетов; `code` — нормализованный (60.01, 76.07.1)."""
__tablename__ = "chart_of_account"
code: Mapped[str] = mapped_column(String, nullable=False, unique=True)
parent_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("chart_of_account.id"))
name: Mapped[str] = mapped_column(String, nullable=False)
kind: Mapped[AccountKind | None] = mapped_column(pg_enum("account_kind"))
row_type: Mapped[AccountRowType] = mapped_column(pg_enum("account_row_type"), nullable=False)
is_off_balance: Mapped[bool] = mapped_column(Boolean, nullable=False, server_default=text("false"))
class EliminationRule(UUIDPrimaryMixin, Base):
"""Правило элиминации (Прил. 3 — ББ, Прил. 4 — ОФР), версионируется; состав счетов — `elimination_rule_account`."""
__tablename__ = "elimination_rule"
__table_args__ = (
Index(
"uq_elimination_rule_statement_type_rule_no_version_no",
"statement_type",
"rule_no",
"version_no",
unique=True,
),
)
statement_type: Mapped[StatementType] = mapped_column(pg_enum("statement_type"), nullable=False)
rule_no: Mapped[int] = mapped_column(SmallInteger, nullable=False)
article_1_line: Mapped[str] = mapped_column(String, nullable=False)
article_2_line: Mapped[str] = mapped_column(String, nullable=False)
side_1: Mapped[str] = mapped_column(String, nullable=False)
measure: Mapped[str] = mapped_column(String, nullable=False)
elimination_mode: Mapped[str] = mapped_column(String, nullable=False)
vat_handling: Mapped[str | None] = mapped_column(String)
excise_adjust: Mapped[bool] = mapped_column(Boolean, nullable=False, server_default=text("false"))
comment: Mapped[str | None] = mapped_column(Text)
version_no: Mapped[int] = mapped_column(Integer, nullable=False)
valid_from: Mapped[datetime.date] = mapped_column(Date, nullable=False)
valid_to: Mapped[datetime.date | None] = mapped_column(Date)
class EliminationRuleAccount(UUIDPrimaryMixin, Base):
"""Счета и субсчета сторон правила; `is_excluded` — исключение внутри маски («76 кроме 76.7.1»)."""
__tablename__ = "elimination_rule_account"
rule_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("elimination_rule.id"), nullable=False)
company_no: Mapped[int] = mapped_column(SmallInteger, nullable=False)
account_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("chart_of_account.id"), nullable=False)
is_excluded: Mapped[bool] = mapped_column(Boolean, nullable=False, server_default=text("false"))