feat: ORM-модели частей 01–03 DBML v5.13 (справочники, доступ, учёт), naming_convention в MetaData
16 таблиц: directories, access, accounting; схема dkb, детерминированные имена ограничений. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
a272d6873a
commit
fb2b9ef9fc
@ -1,8 +1,12 @@
|
|||||||
"""ORM-модели. Источник истины — docs/model/data-model-v5.13.dbml; заполняется в Срезе 0."""
|
"""ORM-модели. Источник истины — docs/model/data-model-v5.13.dbml; заполняется в Срезе 0.
|
||||||
|
|
||||||
|
Импорт всех модулей моделей обязателен: он регистрирует таблицы в `Base.metadata` (create_all, alembic).
|
||||||
|
"""
|
||||||
|
|
||||||
|
from . import access, accounting, directories
|
||||||
from .base import Base, UUIDPrimaryMixin
|
from .base import Base, UUIDPrimaryMixin
|
||||||
|
|
||||||
#: Версия DBML, которой соответствуют модели (сверяется с шапкой DBML в check_dbml_vs_orm).
|
#: Версия DBML, которой соответствуют модели (сверяется с шапкой DBML в check_dbml_vs_orm).
|
||||||
DBML_VERSION = "5.13"
|
DBML_VERSION = "5.13"
|
||||||
|
|
||||||
__all__ = ["DBML_VERSION", "Base", "UUIDPrimaryMixin"]
|
__all__ = ["DBML_VERSION", "Base", "UUIDPrimaryMixin", "access", "accounting", "directories"]
|
||||||
|
|||||||
75
api/src/models/access.py
Normal file
75
api/src/models/access.py
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
"""Часть 02 DBML v5.13 «Пользователи и доступ»: пользователи, роли, права как данные (`effective_permissions`)."""
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
from sqlalchemy import Boolean, DateTime, ForeignKey, Index, String, Text, text
|
||||||
|
from sqlalchemy.dialects.postgresql import UUID
|
||||||
|
from sqlalchemy.orm import Mapped, mapped_column
|
||||||
|
|
||||||
|
from .base import Base, UUIDPrimaryMixin
|
||||||
|
from .enums import BusinessSegment, PermissionAction, PermissionObject, pg_enum
|
||||||
|
|
||||||
|
|
||||||
|
class AppUser(UUIDPrimaryMixin, Base):
|
||||||
|
"""Пользователь; аутентификация внешняя (JWT РАИСА, email → `app_user`)."""
|
||||||
|
|
||||||
|
__tablename__ = "app_user"
|
||||||
|
|
||||||
|
external_id: Mapped[str | None] = mapped_column(String, unique=True)
|
||||||
|
email: Mapped[str | None] = mapped_column(String, unique=True)
|
||||||
|
full_name: Mapped[str] = mapped_column(String, nullable=False)
|
||||||
|
employee_no: Mapped[str | None] = mapped_column(String)
|
||||||
|
regional_office_id: Mapped[uuid.UUID] = mapped_column(
|
||||||
|
UUID(as_uuid=True), ForeignKey("regional_office.id"), nullable=False
|
||||||
|
)
|
||||||
|
business_segment: Mapped[BusinessSegment | None] = mapped_column(pg_enum("business_segment"))
|
||||||
|
is_active: Mapped[bool] = mapped_column(Boolean, nullable=False, server_default=text("true"))
|
||||||
|
created_at: Mapped[datetime.datetime | None] = mapped_column(DateTime(timezone=True))
|
||||||
|
updated_at: Mapped[datetime.datetime | None] = mapped_column(DateTime(timezone=True))
|
||||||
|
|
||||||
|
|
||||||
|
class Role(UUIDPrimaryMixin, Base):
|
||||||
|
"""Роль; полномочия — через `role_permission`, код роли в бизнес-логике не проверяется."""
|
||||||
|
|
||||||
|
__tablename__ = "role"
|
||||||
|
|
||||||
|
code: Mapped[str] = mapped_column(String, nullable=False, unique=True)
|
||||||
|
name: Mapped[str] = mapped_column(String, nullable=False)
|
||||||
|
description: Mapped[str | None] = mapped_column(Text)
|
||||||
|
is_system: Mapped[bool] = mapped_column(Boolean, nullable=False, server_default=text("false"))
|
||||||
|
is_active: Mapped[bool] = mapped_column(Boolean, nullable=False, server_default=text("true"))
|
||||||
|
|
||||||
|
|
||||||
|
class Permission(UUIDPrimaryMixin, Base):
|
||||||
|
"""Справочник прав «объект × действие» (seed, привязан к коду)."""
|
||||||
|
|
||||||
|
__tablename__ = "permission"
|
||||||
|
__table_args__ = (Index("uq_permission_object_type_action", "object_type", "action", unique=True),)
|
||||||
|
|
||||||
|
code: Mapped[str] = mapped_column(String, nullable=False, unique=True)
|
||||||
|
object_type: Mapped[PermissionObject] = mapped_column(pg_enum("permission_object"), nullable=False)
|
||||||
|
action: Mapped[PermissionAction] = mapped_column(pg_enum("permission_action"), nullable=False)
|
||||||
|
name: Mapped[str] = mapped_column(String, nullable=False)
|
||||||
|
description: Mapped[str | None] = mapped_column(Text)
|
||||||
|
|
||||||
|
|
||||||
|
class RolePermission(Base):
|
||||||
|
"""Матрица доступов как данные; составной PK `(role_id, permission_id)`, суррогатного `id` нет."""
|
||||||
|
|
||||||
|
__tablename__ = "role_permission"
|
||||||
|
|
||||||
|
role_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("role.id"), primary_key=True)
|
||||||
|
permission_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("permission.id"), primary_key=True)
|
||||||
|
granted_by: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("app_user.id"))
|
||||||
|
granted_at: Mapped[datetime.datetime | None] = mapped_column(DateTime(timezone=True))
|
||||||
|
|
||||||
|
|
||||||
|
class UserRole(UUIDPrimaryMixin, Base):
|
||||||
|
"""Назначение ролей пользователю (M:N, current-state); история — `audit_event(USER_ROLE_CHANGE)`."""
|
||||||
|
|
||||||
|
__tablename__ = "user_role"
|
||||||
|
__table_args__ = (Index("uq_user_role_user_id_role_id", "user_id", "role_id", unique=True),)
|
||||||
|
|
||||||
|
user_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("app_user.id"), nullable=False)
|
||||||
|
role_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("role.id"), nullable=False)
|
||||||
64
api/src/models/accounting.py
Normal file
64
api/src/models/accounting.py
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
"""Часть 03 DBML v5.13 (учёт): план счетов и правила элиминации ВГО (Прил. 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"))
|
||||||
@ -1,14 +1,35 @@
|
|||||||
|
"""Базовый класс ORM: схема `dkb`, детерминированные имена ограничений и первичный ключ uuid."""
|
||||||
|
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from sqlalchemy import MetaData
|
from sqlalchemy import MetaData
|
||||||
from sqlalchemy.dialects.postgresql import UUID
|
from sqlalchemy.dialects.postgresql import UUID
|
||||||
from sqlalchemy.orm import DeclarativeBase, Mapped, declarative_mixin, mapped_column
|
from sqlalchemy.orm import DeclarativeBase, Mapped, declarative_mixin, mapped_column
|
||||||
|
|
||||||
|
#: Схема PostgreSQL, в которой живёт вся модель данных (DBML v5.13).
|
||||||
|
SCHEMA = "dkb"
|
||||||
|
|
||||||
|
#: Стандартная схема имён SQLAlchemy. Нужна, чтобы ORM, миграция 0001 и `alembic autogenerate` давали одни и те же
|
||||||
|
#: имена ограничений и `downgrade` был детерминирован. Для составных/partial-индексов, где конвенция даёт
|
||||||
|
#: неоднозначное или слишком длинное имя (лимит PostgreSQL — 63 символа), имена задаются явно в `__table_args__`.
|
||||||
|
NAMING_CONVENTION = {
|
||||||
|
"ix": "ix_%(column_0_label)s",
|
||||||
|
"uq": "uq_%(table_name)s_%(column_0_name)s",
|
||||||
|
"ck": "ck_%(table_name)s_%(constraint_name)s",
|
||||||
|
"fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s",
|
||||||
|
"pk": "pk_%(table_name)s",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class Base(DeclarativeBase):
|
class Base(DeclarativeBase):
|
||||||
metadata = MetaData(schema="dkb")
|
metadata = MetaData(schema=SCHEMA, naming_convention=NAMING_CONVENTION)
|
||||||
|
|
||||||
|
|
||||||
@declarative_mixin
|
@declarative_mixin
|
||||||
class UUIDPrimaryMixin:
|
class UUIDPrimaryMixin:
|
||||||
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
"""`id uuid [pk]` из DBML: значение генерирует приложение (uuid4), серверного default в БД нет.
|
||||||
|
|
||||||
|
`sort_order=-1` — `id` первой колонкой, как в DBML (иначе колонки миксина идут после колонок класса).
|
||||||
|
"""
|
||||||
|
|
||||||
|
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, sort_order=-1)
|
||||||
|
|||||||
169
api/src/models/directories.py
Normal file
169
api/src/models/directories.py
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
"""Часть 01 DBML v5.13 «Справочники»: филиалы, ГК и ЮЛ (SCD4), алиасы, ГСК/ГСЗ (SCD2)."""
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
from sqlalchemy import Boolean, Date, DateTime, ForeignKey, Index, String, text
|
||||||
|
from sqlalchemy.dialects.postgresql import UUID
|
||||||
|
from sqlalchemy.orm import Mapped, mapped_column
|
||||||
|
|
||||||
|
from .base import Base, UUIDPrimaryMixin
|
||||||
|
from .enums import EntityType, LeHistoryEvent, RelatedGroupKind, pg_enum
|
||||||
|
|
||||||
|
|
||||||
|
class RegionalOffice(UUIDPrimaryMixin, Base):
|
||||||
|
"""Филиал: ГО и региональные филиалы (DECISION-3: ГО — обычная запись с `is_head_office`)."""
|
||||||
|
|
||||||
|
__tablename__ = "regional_office"
|
||||||
|
|
||||||
|
name: Mapped[str] = mapped_column(String, nullable=False, unique=True)
|
||||||
|
is_head_office: Mapped[bool] = mapped_column(Boolean, nullable=False, server_default=text("false"))
|
||||||
|
is_active: Mapped[bool] = mapped_column(Boolean, nullable=False, server_default=text("true"))
|
||||||
|
|
||||||
|
|
||||||
|
class CompanyGroup(UUIDPrimaryMixin, Base):
|
||||||
|
"""ГК — текущее состояние (SCD4); история — `company_group_history`."""
|
||||||
|
|
||||||
|
__tablename__ = "company_group"
|
||||||
|
|
||||||
|
code: Mapped[str] = mapped_column(String, nullable=False, unique=True)
|
||||||
|
crm_id: Mapped[str | None] = mapped_column(String, unique=True)
|
||||||
|
name: Mapped[str] = mapped_column(String, nullable=False)
|
||||||
|
base_regional_office_id: Mapped[uuid.UUID | None] = mapped_column(
|
||||||
|
UUID(as_uuid=True), ForeignKey("regional_office.id")
|
||||||
|
)
|
||||||
|
is_archived: Mapped[bool] = mapped_column(Boolean, nullable=False, server_default=text("false"))
|
||||||
|
valid_from: Mapped[datetime.datetime] = mapped_column(DateTime(timezone=True), nullable=False)
|
||||||
|
updated_by: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("app_user.id"))
|
||||||
|
created_at: Mapped[datetime.datetime | None] = mapped_column(DateTime(timezone=True))
|
||||||
|
updated_at: Mapped[datetime.datetime | None] = mapped_column(DateTime(timezone=True))
|
||||||
|
|
||||||
|
|
||||||
|
class CompanyGroupHistory(UUIDPrimaryMixin, Base):
|
||||||
|
"""Прошлые снимки ГК (SCD4)."""
|
||||||
|
|
||||||
|
__tablename__ = "company_group_history"
|
||||||
|
__table_args__ = (
|
||||||
|
Index("uq_company_group_history_company_group_id_valid_from", "company_group_id", "valid_from", unique=True),
|
||||||
|
)
|
||||||
|
|
||||||
|
company_group_id: Mapped[uuid.UUID] = mapped_column(
|
||||||
|
UUID(as_uuid=True), ForeignKey("company_group.id"), nullable=False
|
||||||
|
)
|
||||||
|
crm_id: Mapped[str | None] = mapped_column(String)
|
||||||
|
name: Mapped[str | None] = mapped_column(String)
|
||||||
|
base_regional_office_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True))
|
||||||
|
valid_from: Mapped[datetime.datetime | None] = mapped_column(DateTime(timezone=True))
|
||||||
|
valid_to: Mapped[datetime.datetime | None] = mapped_column(DateTime(timezone=True))
|
||||||
|
changed_by: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("app_user.id"))
|
||||||
|
|
||||||
|
|
||||||
|
class LegalEntity(UUIDPrimaryMixin, Base):
|
||||||
|
"""Организация (ЮЛ/ИП) — текущее состояние (SCD4); `company_group_id IS NULL` = монозаёмщик."""
|
||||||
|
|
||||||
|
__tablename__ = "legal_entity"
|
||||||
|
|
||||||
|
inn: Mapped[str] = mapped_column(String, nullable=False, unique=True)
|
||||||
|
crm_id: Mapped[str | None] = mapped_column(String, unique=True)
|
||||||
|
entity_type: Mapped[EntityType | None] = mapped_column(pg_enum("entity_type"))
|
||||||
|
name: Mapped[str] = mapped_column(String, nullable=False)
|
||||||
|
company_group_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("company_group.id"))
|
||||||
|
regional_office_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("regional_office.id"))
|
||||||
|
close_date: Mapped[datetime.date | None] = mapped_column(Date)
|
||||||
|
valid_from: Mapped[datetime.datetime] = mapped_column(DateTime(timezone=True), nullable=False)
|
||||||
|
updated_by: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("app_user.id"))
|
||||||
|
created_at: Mapped[datetime.datetime | None] = mapped_column(DateTime(timezone=True))
|
||||||
|
updated_at: Mapped[datetime.datetime | None] = mapped_column(DateTime(timezone=True))
|
||||||
|
|
||||||
|
|
||||||
|
class LegalEntityHistory(UUIDPrimaryMixin, Base):
|
||||||
|
"""Прошлые снимки ЮЛ (SCD4) и события для архива ГК."""
|
||||||
|
|
||||||
|
__tablename__ = "legal_entity_history"
|
||||||
|
__table_args__ = (
|
||||||
|
Index("uq_legal_entity_history_legal_entity_id_valid_from", "legal_entity_id", "valid_from", unique=True),
|
||||||
|
)
|
||||||
|
|
||||||
|
legal_entity_id: Mapped[uuid.UUID] = mapped_column(
|
||||||
|
UUID(as_uuid=True), ForeignKey("legal_entity.id"), nullable=False
|
||||||
|
)
|
||||||
|
inn: Mapped[str | None] = mapped_column(String)
|
||||||
|
crm_id: Mapped[str | None] = mapped_column(String)
|
||||||
|
entity_type: Mapped[EntityType | None] = mapped_column(pg_enum("entity_type"))
|
||||||
|
name: Mapped[str | None] = mapped_column(String)
|
||||||
|
company_group_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True))
|
||||||
|
regional_office_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True))
|
||||||
|
close_date: Mapped[datetime.date | None] = mapped_column(Date)
|
||||||
|
event_type: Mapped[LeHistoryEvent | None] = mapped_column(pg_enum("le_history_event"))
|
||||||
|
effective_date: Mapped[datetime.date | None] = mapped_column(Date)
|
||||||
|
valid_from: Mapped[datetime.datetime | None] = mapped_column(DateTime(timezone=True))
|
||||||
|
valid_to: Mapped[datetime.datetime | None] = mapped_column(DateTime(timezone=True))
|
||||||
|
changed_by: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("app_user.id"))
|
||||||
|
|
||||||
|
|
||||||
|
class LegalEntityAlias(UUIDPrimaryMixin, Base):
|
||||||
|
"""Словарь соответствий наименований контрагентов (ТЗ 6.5)."""
|
||||||
|
|
||||||
|
__tablename__ = "legal_entity_alias"
|
||||||
|
__table_args__ = (
|
||||||
|
Index(
|
||||||
|
"uq_legal_entity_alias_legal_entity_id_normalized_alias", "legal_entity_id", "normalized_alias", unique=True
|
||||||
|
),
|
||||||
|
Index("ix_legal_entity_alias_normalized_alias", "normalized_alias"),
|
||||||
|
)
|
||||||
|
|
||||||
|
legal_entity_id: Mapped[uuid.UUID] = mapped_column(
|
||||||
|
UUID(as_uuid=True), ForeignKey("legal_entity.id"), nullable=False
|
||||||
|
)
|
||||||
|
alias: Mapped[str] = mapped_column(String, nullable=False)
|
||||||
|
normalized_alias: Mapped[str] = mapped_column(String, nullable=False)
|
||||||
|
created_by: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("app_user.id"))
|
||||||
|
created_at: Mapped[datetime.datetime | None] = mapped_column(DateTime(timezone=True))
|
||||||
|
|
||||||
|
|
||||||
|
class RelatedGroup(UUIDPrimaryMixin, Base):
|
||||||
|
"""ГСК/ГСЗ — текущее состояние; иерархия групп не поддерживается (ТЗ 6.1)."""
|
||||||
|
|
||||||
|
__tablename__ = "related_group"
|
||||||
|
|
||||||
|
code: Mapped[str] = mapped_column(String, nullable=False, unique=True)
|
||||||
|
company_group_id: Mapped[uuid.UUID] = mapped_column(
|
||||||
|
UUID(as_uuid=True), ForeignKey("company_group.id"), nullable=False
|
||||||
|
)
|
||||||
|
kind: Mapped[RelatedGroupKind] = mapped_column(pg_enum("related_group_kind"), nullable=False)
|
||||||
|
name: Mapped[str] = mapped_column(String, nullable=False)
|
||||||
|
created_date: Mapped[datetime.date | None] = mapped_column(Date)
|
||||||
|
|
||||||
|
|
||||||
|
class RelatedGroupMember(UUIDPrimaryMixin, Base):
|
||||||
|
"""Состав ГСК/ГСЗ — SCD2; исключение = закрытие версии (`is_current=false`)."""
|
||||||
|
|
||||||
|
__tablename__ = "related_group_member"
|
||||||
|
__table_args__ = (
|
||||||
|
# (related_group_id, legal_entity_id): partial UNIQUE WHERE is_current
|
||||||
|
Index(
|
||||||
|
"uq_related_group_member_group_entity_current",
|
||||||
|
"related_group_id",
|
||||||
|
"legal_entity_id",
|
||||||
|
unique=True,
|
||||||
|
postgresql_where=text("is_current"),
|
||||||
|
),
|
||||||
|
# legal_entity_id: partial UNIQUE WHERE is_current — ЮЛ только в одной ГСК или ГСЗ (ТЗ 6.1, ANL-030)
|
||||||
|
Index(
|
||||||
|
"uq_related_group_member_entity_current",
|
||||||
|
"legal_entity_id",
|
||||||
|
unique=True,
|
||||||
|
postgresql_where=text("is_current"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
related_group_id: Mapped[uuid.UUID] = mapped_column(
|
||||||
|
UUID(as_uuid=True), ForeignKey("related_group.id"), nullable=False
|
||||||
|
)
|
||||||
|
legal_entity_id: Mapped[uuid.UUID] = mapped_column(
|
||||||
|
UUID(as_uuid=True), ForeignKey("legal_entity.id"), nullable=False
|
||||||
|
)
|
||||||
|
valid_from: Mapped[datetime.datetime] = mapped_column(DateTime(timezone=True), nullable=False)
|
||||||
|
valid_to: Mapped[datetime.datetime | None] = mapped_column(DateTime(timezone=True))
|
||||||
|
is_current: Mapped[bool] = mapped_column(Boolean, nullable=False)
|
||||||
|
created_by: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("app_user.id"))
|
||||||
@ -10,7 +10,7 @@ import enum
|
|||||||
|
|
||||||
from sqlalchemy import Enum as SAEnum
|
from sqlalchemy import Enum as SAEnum
|
||||||
|
|
||||||
SCHEMA = "dkb"
|
from .base import SCHEMA
|
||||||
|
|
||||||
|
|
||||||
class EntityType(enum.StrEnum):
|
class EntityType(enum.StrEnum):
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user