diff --git a/.gitignore b/.gitignore index e44f625..cd11982 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ .bounded dev/.env .venv/ +__pycache__/ diff --git a/Makefile b/Makefile index b280266..2edef54 100644 --- a/Makefile +++ b/Makefile @@ -4,8 +4,10 @@ VENV := .venv # Если окружение создано (`make venv`) — берём его интерпретатор, иначе системный python3 (в нём должен быть jsonschema) PY := $(if $(wildcard $(VENV)/bin/python),$(CURDIR)/$(VENV)/bin/python,python3) CHANGE ?= slice-0-skeleton +# Текущая версия модели данных — по ней рисуются диаграммы (make diagrams) +DBML ?= model/data-model-v5.17.dbml -.PHONY: venv validate-contracts validate-openspec check +.PHONY: venv validate-contracts validate-openspec check diagrams # Единственная зависимость скриптов проверки контракта — jsonschema venv: @@ -21,3 +23,7 @@ validate-openspec: openspec validate $(CHANGE) --strict check: validate-contracts validate-openspec + +# Диаграммы model/diagrams/part01…09.{svg,png} из $(DBML); нужен npx (Node.js), зависимостей Python нет +diagrams: + python3 scripts/gen_model_diagrams.py $(DBML) diff --git a/context/00-workspace-layout.md b/context/00-workspace-layout.md index d348a30..778b1ac 100644 --- a/context/00-workspace-layout.md +++ b/context/00-workspace-layout.md @@ -67,7 +67,7 @@ git remote add origin && git push -u origin main | Слой | Где | Статус | Правило для агента | |---|---|---|---| -| Модель данных | `docs/model/data-model-v5.14.dbml` | стабильный, версионируется | единственный источник имён и структур | +| Модель данных | `docs/model/data-model-v5.17.dbml` | стабильный, версионируется | единственный источник имён и структур | | Правила домена | `docs/context/05-domain-rules.md` | курируемый — пишет человек/агент по итогам ревью | **единственный вход алгоритмов в код**; из ТЗ напрямую не кодируем | | Открытые вопросы | `docs/context/07-open-questions.md` | живой реестр | каждое допущение в коде — `ASSUMPTION(OQ-nn)` | | Экспорт ТЗ | `docs/source/tz/` | снимок на дату из `docs/source/tz/_export.md` | читать для контекста; расхождение с §05 → вопрос, не код | diff --git a/context/04-data-model-guide.md b/context/04-data-model-guide.md index b6de7c3..8825a47 100644 --- a/context/04-data-model-guide.md +++ b/context/04-data-model-guide.md @@ -2,8 +2,9 @@ Файлы: `docs/model/data-model-v5.17.dbml` (источник истины), `docs/model/entities-v5.17.md` (смысл таблиц; предыдущие версии `data-model-v5.13…v5.16.dbml` и `entities-v5.13…v5.16.md` остаются как история), -`docs/model/diagrams/part01…09.svg` (диаграммы v5.12 — на одну версию старше: нет `permission`/`role_permission`, -нет `document`, есть `osv_group_schema`, нет `related_group_member.kind`; читать с поправкой на changelog v5.13…v5.17 в шапке DBML). +`docs/model/diagrams/part01…09.{svg,png}` (диаграммы v5.17 по частям из таблицы ниже; перерисовываются из DBML командой +`make diagrams` в корне репозитория docs — скрипт `scripts/gen_model_diagrams.py`, нужен npx; при новой версии DBML +обновить `DBML` в Makefile, а новую таблицу — добавить в раскладку частей скрипта и в эту таблицу). ## Девять частей модели diff --git a/context/05-domain-rules.md b/context/05-domain-rules.md index 37b0fef..93598df 100644 --- a/context/05-domain-rules.md +++ b/context/05-domain-rules.md @@ -1,4 +1,4 @@ -# 05. Правила предметной области (выжимка из ТЗ, имена — v5.15) +# 05. Правила предметной области (выжимка из ТЗ, имена — v5.17) Каждый раздел: источник в ТЗ → правило → где в модели. Где ТЗ молчит или противоречит — пометка **[открыто]** и ссылка на `07-open-questions.md`. diff --git a/context/06-test-data.md b/context/06-test-data.md index 4bddada..ee2eec3 100644 --- a/context/06-test-data.md +++ b/context/06-test-data.md @@ -194,7 +194,8 @@ arm-tabular-parser#11, OQ-131…OQ-135. ИНН организаций в шап - `Анализ Русский бисквит_заемщик 01102025.xlsx`, `Анализ Счастливочная_поручитель 01102025.xlsx` — макросные книги «Альбома инструментов и стандартов кредитования» (БлокТ32): листы «Отчетность», «Экспресс-анализ», «ЗАКЛ_лимит», «ЗАКЛ_сделка», «П2_ОД-2967», «Профсуждение» — контекст для будущей формы 276-ОД, не для первых срезов. -- `diagrams_v5.12.zip` → `docs/model/diagrams/` (part01…09 svg/png). +- `diagrams_v5.12.zip` → `docs/model/diagrams/` (part01…09 svg/png) — исходная поставка; с v5.17 диаграммы перерисовываются + из DBML (`make diagrams` в docs, см. `04-data-model-guide.md`). ## 6. Раскладка репозитория `arm-ka-fixtures` (workspace `fixtures/`) diff --git a/context/08-source-docs-index.md b/context/08-source-docs-index.md index 682de2e..4fa1206 100644 --- a/context/08-source-docs-index.md +++ b/context/08-source-docs-index.md @@ -48,10 +48,10 @@ | Файл | Что | |---|---| -| `data-model-v5.14.dbml` | источник истины; changelog v5.1…v5.14 в шапке; блоки «ИСТОЧНИК» над каждой таблицей (v5.13 остаётся рядом как история) | -| `entities-v5.14.md` | описание сущностей по частям 01–09, таблицы «Что изменилось в v5.14 / v5.13», сценарий «как читать модель», открытые вопросы (v5.13 остаётся как история) | -| `diagrams/part01…09.{svg,png}` | диаграммы v5.12 (на шаг старше) | -| `data-model-part1_4.dbml` | v4 часть 1 — историческое | +| `data-model-v5.17.dbml` | источник истины; changelog v5.1…v5.17 в шапке; блоки «ИСТОЧНИК» над каждой таблицей (v5.13…v5.16 остаются рядом как история) | +| `entities-v5.17.md` | описание сущностей по частям 01–09, таблицы «Что изменилось» по версиям, сценарий «как читать модель», открытые вопросы (v5.13…v5.16 остаются как история) | +| `diagrams/part01…09.{svg,png}` | диаграммы v5.17 по частям; перерисовываются `make diagrams` (`scripts/gen_model_diagrams.py`) | +| `source/tz/data-model-part1_4.dbml` (лежит не в `model/`, а в экспорте ТЗ) | v4 часть 1 — историческое | ## Макет UI diff --git a/context/09-delivery-plan.md b/context/09-delivery-plan.md index 9716562..bdb215a 100644 --- a/context/09-delivery-plan.md +++ b/context/09-delivery-plan.md @@ -8,7 +8,7 @@ `tasks.md`), сквозная часть — change в `docs/`; задачи — issues в Gitea; PR — по одному на репозиторий. Jira-эпики пользователя маппятся на срезы (пользователь ведёт соответствие сам). -## Срез 0 — Скелет (2–3 дня, три репозитория + docs) +## Срез 0 — Скелет (2–3 дня, три репозитория + docs) — **завершён 04.10.2026** - Три репозитория из нулевого коммита шаблона команды; соглашения шаблона — в `03-architecture.md` и в `CLAUDE.md` каждого репозитория. **Первая задача агента — прочитать шаблон и дополнить документы.** (Сделано: раздел «Соглашения @@ -75,7 +75,8 @@ UI справочника по макету) разделён: всё, кром ## Срез 2 — Загрузка файлов + табличный парсер (ОСВ, ББ, ОФР из Excel) — самый рискованный, делать рано Решение владельца 05.10.2026: срез делится на четыре подсреза со своими change'ами и PR; сначала **только парсер ОСВ**. -Порядок: 2a → 2b → 2c → 2d. DoD среза в целом (без правил под конкретный файл, без координат в коде парсера) — в конце раздела. +Порядок: 2a → 2b → 2c → 2d. Change'и закрытых срезов заархивированы: пути ниже вида `/openspec/changes/` теперь +`/openspec/changes/archive/<дата>-`. DoD среза в целом (без правил под конкретный файл, без координат в коде парсера) — в конце раздела. ### Срез 2a — Парсер ОСВ (`tabular-parser`) — закрыт 05.10.2026 (tabular-parser #6, архив #8; docs #10, архив #15; fixtures #5) @@ -162,6 +163,8 @@ Change'и: `arm-core/openspec/changes/slice-2c-osv-ingest` (issue arm-core#14, ### Срез 2d — UI «Первичные документы» +**Статус:** API готов (Срезы 2c, 3b, arm-core #42); UI — у отдельного исполнителя фронта по `dev/frontend-api-guide.md`. + - Панель «Первичные документы», окно загрузки, таблица ОСВ с деревом строк и подсветкой нераспознанных контрагентов. **DoD среза 2 целиком:** все реальные файлы и вся синтетика проходят **без единого правила под конкретный файл или @@ -189,9 +192,9 @@ without discussion» №1–№35). Реализация — по `05-domain-rul ### Срез 3b — маппинг форм, проверки, подтверждение и API до сводной ОСВ (arm-core) -**Статус: бэкенд и API реализованы в arm-core, октябрь 2026; UI экрана «Проверка распознавания» в срез не входит.** Объём по +**Статус: закрыт 07.10.2026** (arm-core #39, архив #41; docs #49; follow-up arm-core#40); UI экрана «Проверка распознавания» в срез не входит. Объём по решению владельца 07.10.2026: весь API пути пользователя до сводной ОСВ включительно. Issue arm-core#36, docs#48; спецификация — -`arm-core/openspec/changes/slice-3b-statements-api/` (design D-1…D-14, «Decisions made without discussion» №1–№22); документы +`arm-core/openspec/changes/archive/2026-10-07-slice-3b-statements-api/` (design D-1…D-14, «Decisions made without discussion» №1–№22); документы workspace — bounded-PR в docs (без сквозного change: DBML и контракт не менялись). **Сделано (по факту):** @@ -220,10 +223,10 @@ workspace — bounded-PR в docs (без сквозного change: DBML и ко ## Срез 4 — Консолидация: карточка, версии, периметр, периоды, сводная ОСВ -**Статус: реализован в arm-core (бэкенд + API + экспорт xlsx), октябрь 2026; UI — отдельный исполнитель.** Объём по решению +**Статус: закрыт 07.10.2026** (arm-core #19, архив #20; docs #20, архив #21; follow-up arm-core#21): бэкенд + API + экспорт xlsx; UI — отдельный исполнитель. Объём по решению владельца 06.10.2026: бэкенд, API, экспорт xlsx. Issue arm-core#18, docs#19; спецификация — -`arm-core/openspec/changes/slice-4-consolidation/` (design D-1…D-16, «Decisions made without discussion» №1–№85), -документный change — `docs/openspec/changes/slice-4-consolidation/`. +`arm-core/openspec/changes/archive/2026-10-06-slice-4-consolidation/` (design D-1…D-16, «Decisions made without discussion» №1–№85), +документный change — `docs/openspec/changes/archive/2026-10-06-slice-4-consolidation/`. **Сделано (по факту):** - DBML v5.16: `osv_entry.counterparty_match_kind` + enum `counterparty_match_kind` (миграция `0004_dbml_v5_16`, backfill). diff --git a/context/KICKOFF-PROMPT.md b/context/KICKOFF-PROMPT.md index 697dd5f..9fb29fa 100644 --- a/context/KICKOFF-PROMPT.md +++ b/context/KICKOFF-PROMPT.md @@ -9,8 +9,8 @@ 1. `CLAUDE.md` (workspace) и `docs/context/00-workspace-layout.md` — как устроена рабочая область, статус аналитики, макет. 2. `docs/context/01-project-overview.md`, `02-glossary.md` — предметная область. -3. `docs/context/03-architecture.md`, `04-data-model-guide.md`, затем **весь** `docs/model/data-model-v5.14.dbml` и - `docs/model/entities-v5.14.md`. +3. `docs/context/03-architecture.md`, `04-data-model-guide.md`, затем **весь** `docs/model/data-model-v5.17.dbml` и + `docs/model/entities-v5.17.md`. 4. `docs/context/05-domain-rules.md` — алгоритмы (особенно §1.0 и §12 — парсеры ищут структуру по содержимому); `06-test-data.md`; `07-open-questions.md`; `10-user-journey.md`; `09-delivery-plan.md`. 5. `arm-core/`, `tabular-parser/`, `scan-gateway/` — три репозитория, развёрнутые из нулевого коммита шаблона команды, @@ -37,7 +37,7 @@ NATS, MinIO, эмулятор ИС Сканирование), контракт ` `07-open-questions.md`; если нужен ответ для среза — делай допущение по правилам из этого файла. Ограничения на всё время работы: -- имена таблиц, колонок и enum'ов — ровно как в DBML v5.14; поля, которых нет в DBML, не изобретать; +- имена таблиц, колонок и enum'ов — ровно как в DBML v5.17; поля, которых нет в DBML, не изобретать; - алгоритмы — только из `05-domain-rules.md`; из страниц ТЗ напрямую не кодировать; - парсеры: никаких координат ячеек, букв колонок, имён листов и номеров строк в коде — структура по содержимому, синонимы в `rules/*.yaml`; фикстуры — регрессия, не спецификация; diff --git a/dev/README.md b/dev/README.md index 7f012d6..534de3f 100644 --- a/dev/README.md +++ b/dev/README.md @@ -417,7 +417,7 @@ postgres nats minio`, затем `make test-integration` (или `make check-ful - **Образы MinIO неофициальные.** Официальные `minio/minio` и `minio/mc` недоступны (Docker Hub — denied, на quay.io нет нужных тегов), поэтому в compose стоят `alpine/minio:RELEASE.2025-10-15T17-29-55Z` и `bitnamilegacy/minio-client:2025.7.21`, а сервис `minio` запущен с `user: "0:0"` (иначе образ не пишет в свежий том) — - только для dev. Решения: [`design.md`](../openspec/changes/slice-0-skeleton/design.md), раздел + только для dev. Решения: [`design.md`](../openspec/changes/archive/2026-10-04-slice-0-skeleton/design.md), раздел «Decisions made without discussion», решения №16–17. Замена на официальные образы — две строки `image:` в `docker-compose.yml` (и удаление `user: "0:0"`). - **Безопасность.** По умолчанию порты публикуются только на `127.0.0.1` (`BIND_ADDR`), снаружи машины они недоступны. diff --git a/dev/api-contract-slice-3b.md b/dev/api-contract-slice-3b.md index b887f2c..d8e9e2f 100644 --- a/dev/api-contract-slice-3b.md +++ b/dev/api-contract-slice-3b.md @@ -1,7 +1,7 @@ # Контракт API Среза 3b (формы ББ/ОФР и путь пользователя до сводной ОСВ) Источник — реальный код arm-core (`api/src/schemas/statements.py`, `schemas/comments.py`, `api/endpoints/files/statements.py`, -`comments.py`, `read.py`, `users.py`; Swagger — `/docs`) и спека `arm-core/openspec/changes/slice-3b-statements-api/` (design +`comments.py`, `read.py`, `users.py`; Swagger — `/docs`) и спека `arm-core/openspec/changes/archive/2026-10-07-slice-3b-statements-api/` (design D-1…D-14, «Decisions made without discussion» №1–№22). Правила предметной области — `context/05-domain-rules.md` §7, §9.3, §9.5 (п. 2, 4, 9, 10); экраны — `context/10-user-journey.md` (пути 1–2, шаг 3.1), номера в `[…]` — снимки макета (`mockup/INDEX.md`). Общие правила (префикс, токен, формат ошибок, права) — как в `dev/api-contract-slice-4.md` §1; здесь — то, что добавил Срез 3b. diff --git a/dev/api-contract-slice-4.md b/dev/api-contract-slice-4.md index b4bbac0..f92a5e4 100644 --- a/dev/api-contract-slice-4.md +++ b/dev/api-contract-slice-4.md @@ -1,7 +1,7 @@ # Контракт API Среза 4 (консолидация) для разработчика фронта Источник — реальная схема `GET /openapi.json` arm-core (ветка `feat/18-slice-4-consolidation`, Swagger — `/docs`); коды ошибок и -нюансы сверены с кодом и спекой `arm-core/openspec/changes/slice-4-consolidation/` (design D-1…D-16, решения №1–№81+). +нюансы сверены с кодом и спекой `arm-core/openspec/changes/archive/2026-10-06-slice-4-consolidation/` (design D-1…D-16, решения №1–№81+). Правила предметной области — `context/05-domain-rules.md` §2, §3, §9.1, §10; экраны — `context/10-user-journey.md` (пути 2 и 3), номера в `[…]` — снимки макета (`mockup/INDEX.md`). Здесь — только то, что нужно, чтобы писать клиент. diff --git a/dev/checks.md b/dev/checks.md index ad141cd..683682b 100644 --- a/dev/checks.md +++ b/dev/checks.md @@ -2,7 +2,7 @@ Конфигураций CI в репозиториях нет (в Gitea CI не используется, пайплайн платформы банка вне зоны проекта), поэтому проверки выполняются локально одной командой перед каждым PR. Требования — spec `quality-checks` -(`openspec/changes/slice-0-skeleton/specs/quality-checks/spec.md`), решение D-8. +(`openspec/specs/quality-checks/spec.md`), решение D-8 (`openspec/changes/archive/2026-10-04-slice-0-skeleton/design.md`). Все цели запускаются **из корня репозитория сервиса** (`arm-core`, `tabular-parser`, `scan-gateway`); рецепты сами переходят в `api/` и используют окружение `api/.venv`. Инфраструктура compose — `docs/dev/docker-compose.yml` @@ -57,7 +57,7 @@ docker compose -f ../docs/dev/docker-compose.yml --project-name arm-ka run --rm Для `arm-core` дополнительно: -- `dbml-check` — `python scripts/check_dbml_vs_orm.py ../../docs/model/data-model-v5.14.dbml` (путь от `api/`; +- `dbml-check` — `python scripts/check_dbml_vs_orm.py ../../docs/model/data-model-v5.17.dbml` (путь от `api/`, по умолчанию — переменная `DBML` Makefile, она указывает на текущую версию модели; другой — `make dbml-check DBML=<путь относительно api/>`): ORM обязана совпадать с DBML; расхождения печатаются построчно, код 1. - Цикл миграций «`alembic upgrade head` → `alembic downgrade base` → `alembic upgrade head`» на чистой БД, пустой diff --git a/dev/frontend-api-guide.md b/dev/frontend-api-guide.md index 7aa4a4d..716ebce 100644 --- a/dev/frontend-api-guide.md +++ b/dev/frontend-api-guide.md @@ -1,6 +1,7 @@ # Гайд фронтенд-разработчика: API arm-core -Состояние — `arm-core` на `main` от 07.10.2026 (коммит `f4a7806`: после PR #46 «API до сводной ОСВ» и #51 «Основной РФ объектом»). +Состояние — `arm-core` на `main` от 07.10.2026 (коммит `99669cd`: после PR #46 «API до сводной ОСВ», #51 «Основной РФ объектом», +#52 «КПП ЮЛ» и #53 «Схема БД настройкой»). Схема сверена с `GET /openapi.json` (62 операции на 49 путях), коды ошибок — с `api/src/services/*` и `arm-core/README.md` «Ошибки API». Это **точка входа**: здесь всё, что нужно, чтобы писать клиент, — подключение, общие правила, каждый маршрут, ошибки, сценарии @@ -28,6 +29,8 @@ | Когда, PR | Что изменилось в API | Что сделать в клиенте | |---|---|---| +| 07.10, arm-core #52 (#45) | **КПП ЮЛ** (DBML v5.17, `context/05-domain-rules.md` §13.5): поле `kpp: string \| null` в `LegalEntityCreate`, `LegalEntityUpdate`, `LegalEntityOut`/`Detail`, `LegalEntityHistoryOut`. Формат — 9 символов (4 цифры, 2 цифры или латинские A–Z, 3 цифры; пробелы убираются, буквы — в верхний регистр), иначе 422 `INVALID_KPP`; у ИП КПП запрещён (422), у ЮЛ необязателен; уникален только ИНН. Поиск `q` в списке ЮЛ ищет и по КПП; смена типа на ИП у ЮЛ с КПП без очистки КПП — 422 `INVALID_KPP`; `kpp: null` в `PATCH` очищает КПП; в импорте ЕЦП — необязательная колонка «КПП» и код строки `INVALID_KPP` | поле КПП в формах ЮЛ и в карточке; в форме ИП поле скрыть; ошибку `INVALID_KPP` — на поле «КПП» | +| 07.10, arm-core #53 (#38) | Схема БД задаётся настройкой `DB_SCHEMA` — **API не изменился** | — | | 07.10, arm-core #51 (#47) | «Основной РФ» приходит **объектом** `{id, name, is_head_office}` или `null`: `regional_office` в `LegalEntityOut`/`Detail`/`HistoryOut`, `base_regional_office` в `CompanyGroupOut`/`HistoryOut` (в т. ч. в ответах `POST`/`PATCH`/`move`/`withdraw`/`archive`). Поля `*_id` остались; неактивный филиал тоже отдаётся; входные схемы прежние (`*_id`) | брать имя филиала из объекта, а не склеивать с `GET /directories/regional-offices` | | 07.10, arm-core #50 (#43), #49 (#44) | Зависимости под контур (PyJWT 2.8) и настройки из OpenBao — **API не изменился** | — | | 07.10, arm-core #46 (#42) | `POST /consolidations` принимает необязательный `legal_entity_ids` (≤ 500): выбранный в модалке [42] периметр. Без поля или `null` — состав ГСК; `[]` — явный пустой периметр → 422 `PERIMETER_TOO_SMALL`; ЮЛ не из ГК → 422 `ENTITY_NOT_IN_GROUP` | модалка [42]: все ЮЛ ГК, отмечены члены ГСК, отправлять отмеченные (§6, строка [42]) | @@ -251,7 +254,7 @@ UI прячет элементы по `GET /me → permissions` (объедин | `GET /directories/company-groups/{group_id}/history` | — | `CompanyGroupHistoryOut[]` (прошлые снимки, новые первыми) | | `GET /directories/company-groups/{company_group_id}/related-groups` | — | `RelatedGroupOut[]` — ГСК и ГСЗ ГК (`kind`), по имени, с `members_count` | | `GET /directories/related-groups/{group_id}` | — | `RelatedGroupDetail` (+ `members[] {legal_entity_id, inn, name, valid_from}` — текущий состав) | -| `GET /directories/legal-entities` | `company_group_id`, `outside_group` (монозаёмщики), `inn` (точно), `q` (подстрока имени / ИНН), `closed`, `limit`, `offset` | `Page[LegalEntityOut]` | +| `GET /directories/legal-entities` | `company_group_id`, `outside_group` (монозаёмщики), `inn` (точно), `q` (подстрока имени / ИНН / КПП), `closed`, `limit`, `offset` | `Page[LegalEntityOut]` | | `GET /directories/legal-entities/{entity_id}` | — | `LegalEntityDetail` (+ `memberships[]` в ГСК/ГСЗ, `aliases[]`) | | `GET /directories/legal-entities/{entity_id}/history` | — | `LegalEntityHistoryOut[]` (снимки с `event_type` и `effective_date`, новые первыми) | @@ -273,8 +276,8 @@ UI прячет элементы по `GET /me → permissions` (объедин | `POST /directories/company-groups` | EDIT | `{name (1–500), crm_id?, base_regional_office_id?}` | 201 `CompanyGroupOut` (код `GK-NNNNNN` назначает сервер) | 422 `OFFICE_NOT_FOUND`, 422 `OFFICE_NOT_ALLOWED` (филиал не свой), 409 `CRM_ID_EXISTS` | | `PATCH /directories/company-groups/{group_id}` | EDIT | `{name?, crm_id?, base_regional_office_id?}` (частично) | `CompanyGroupOut` | те же | | `POST /directories/company-groups/{group_id}/archive` · `/unarchive` | EDIT | — | `CompanyGroupOut` (повтор — без изменений) | — | -| `POST /directories/legal-entities` | EDIT | `{inn, entity_type, name, company_group_id?, regional_office_id?, crm_id?}`; без `company_group_id` — монозаёмщик | 201 `LegalEntityOut` | 422 `INVALID_INN` (ЮЛ — 10 цифр, ИП — 12; пробелы внутри убираются), 404 (ГК не видна), 409 `INN_EXISTS`, 409 `CRM_ID_EXISTS`, 422 `OFFICE_NOT_FOUND` / `OFFICE_NOT_ALLOWED` — в этом порядке | -| `PATCH /directories/legal-entities/{entity_id}` | EDIT | `{name?, entity_type?, crm_id?, regional_office_id?}`; ИНН и ГК так не меняются | `LegalEntityOut` | 422 `INVALID_INN` (ИНН не подходит новому типу), 422 `OFFICE_*`, 409 `CRM_ID_EXISTS`; `name`/`entity_type: null` — 422 массивом | +| `POST /directories/legal-entities` | EDIT | `{inn, entity_type, name, kpp?, company_group_id?, regional_office_id?, crm_id?}`; без `company_group_id` — монозаёмщик | 201 `LegalEntityOut` | 422 `INVALID_INN` (ЮЛ — 10 цифр, ИП — 12; пробелы внутри убираются), 422 `INVALID_KPP` (формат или КПП у ИП), 404 (ГК не видна), 409 `INN_EXISTS`, 409 `CRM_ID_EXISTS`, 422 `OFFICE_NOT_FOUND` / `OFFICE_NOT_ALLOWED` — в этом порядке | +| `PATCH /directories/legal-entities/{entity_id}` | EDIT | `{name?, entity_type?, crm_id?, regional_office_id?, kpp?}`; `kpp: null` очищает КПП; ИНН и ГК так не меняются | `LegalEntityOut` | 422 `INVALID_INN` (ИНН не подходит новому типу), 422 `INVALID_KPP` (формат; КПП у ИП, в т. ч. смена типа на ИП без очистки КПП), 422 `OFFICE_*`, 409 `CRM_ID_EXISTS`; `name`/`entity_type: null` — 422 массивом | | `POST /directories/legal-entities/{entity_id}/move` | EDIT | `{company_group_id, effective_date}` (дата ≥ сегодня по Москве) | `LegalEntityOut`; текущие членства ГСК/ГСЗ закрываются | 422 `EFFECTIVE_DATE_IN_PAST`, 404 (ГК назначения не видна), 409 `LEGAL_ENTITY_CLOSED`, 409 `SAME_COMPANY_GROUP` | | `POST /directories/legal-entities/{entity_id}/withdraw` | EDIT | `{event: "CLOSE" \| "REMOVE_FROM_GROUP", effective_date, comment?}` | `LegalEntityOut` | 422 `EFFECTIVE_DATE_IN_PAST`, 409 `ALREADY_CLOSED`, 409 `NOT_IN_COMPANY_GROUP` | | `POST /directories/legal-entities/{entity_id}/aliases` | EDIT | `{alias (1–500)}` — как в ОСВ | 201 `AliasOut {id, alias, normalized_alias, created_at}` | 422 `EMPTY_ALIAS`, 409 `ALIAS_EXISTS`, 409 `ALIAS_TAKEN` (`legal_entity_id` владельца) | @@ -443,7 +446,7 @@ UI прячет элементы по `GET /me → permissions` (объедин Поля с `?` могут быть `null`, но ключ присутствует всегда. Полные схемы — `/openapi.json` и контракты по областям. - **`CompanyGroupOut`** `{id, code, crm_id?, name, base_regional_office_id?, base_regional_office? {id, name, is_head_office}, is_archived, valid_from, legal_entities_count, created_at?, updated_at?}`. -- **`LegalEntityOut`** `{id, inn, crm_id?, entity_type?, name, company_group_id?, regional_office_id?, regional_office? {id, name, is_head_office}, close_date?, valid_from, created_at?, updated_at?, documents_count, outside_group, closed}`; **`LegalEntityDetail`** — + `memberships[] {related_group_id, code, kind, name, valid_from}`, `aliases[] {id, alias, normalized_alias, created_at?}`. +- **`LegalEntityOut`** `{id, inn, kpp?, crm_id?, entity_type?, name, company_group_id?, regional_office_id?, regional_office? {id, name, is_head_office}, close_date?, valid_from, created_at?, updated_at?, documents_count, outside_group, closed}`; **`LegalEntityDetail`** — + `memberships[] {related_group_id, code, kind, name, valid_from}`, `aliases[] {id, alias, normalized_alias, created_at?}`. - **`RelatedGroupOut`** `{id, code, company_group_id, kind, name, created_date?, members_count}`; **`RelatedGroupDetail`** — + `members[] {legal_entity_id, inn, name, valid_from}`. - **`FileOut`** `{id, legal_entity_id, original_name?, file_type, status, version_no, size_bytes?, content_hash?, uploaded_at?, uploaded_by?, superseded_by_file_id?, job?, documents[]}`; **`JobBrief`** `{id, status, error_code?, error_message?, mapping_error?, warnings[], mapping_warnings[], finished_at?}`; **`DocumentBrief`** `{id, document_type, osv_account_code?, period_year?, period_quarter?, is_cumulative?, status, period_check?, counterparty_check?, locator?, comments_count}`. - **`DocumentOut`** = поля `DocumentBrief` + `{file_id, legal_entity_id, confirmed_by?, confirmed_at?, group_schema? [{type, name?, has_inn?}], warnings[]}`. @@ -558,6 +561,7 @@ async function download(path: string, fallbackName: string): Promise { | `OFFICE_NOT_FOUND` | 422 | ГК, ЮЛ, пользователь: филиала нет | — | ошибка поля «Филиал» | | `OFFICE_NOT_ALLOWED` | 422 | ГК/ЮЛ: пользователь филиала закрепляет за чужим филиалом | — | ошибка поля «Филиал» | | `INVALID_INN` | 422 | ЮЛ (10/12 цифр по типу), шапка формы (10 или 12) | — | ошибка поля «ИНН» | +| `INVALID_KPP` | 422 | ЮЛ: формат КПП, КПП у ИП (в т. ч. смена типа на ИП); в импорте — код строки отчёта | — | ошибка поля «КПП» | | `EFFECTIVE_DATE_IN_PAST` | 422 | перевод/вывод ЮЛ | — | ошибка поля «Дата» | | `EMPTY_ALIAS` | 422 | алиас из одних ОПФ и знаков | — | ошибка поля | | `DIFFERENT_COMPANY_GROUP` | 422 | член ГСК/ГСЗ из другой ГК | — | сообщение | @@ -627,8 +631,6 @@ async function download(path: string, fallbackName: string): Promise { | Задача | Что поменяется для фронта | Как подготовиться | |---|---|---| -| arm-core #45 — **КПП ЮЛ** (модель уже утверждена: DBML v5.17, docs #54 слит; правила — `context/05-domain-rules.md` §13.5) | Новое поле `kpp: string \| null` в `LegalEntityCreate`, `LegalEntityUpdate`, `LegalEntityOut`/`Detail`, `LegalEntityHistoryOut`. Формат — 9 символов (4 цифры, 2 цифры или латинские A–Z, 3 цифры), иначе 422 `INVALID_KPP`; у ИП КПП запрещён (422), у ЮЛ необязателен; уникален только ИНН. Поиск `q` в списке ЮЛ будет искать и по КПП; смена типа на ИП у ЮЛ с КПП без очистки КПП — 422 `INVALID_KPP`; очистить КПП у ЮЛ можно (`kpp: null`); в импорте ЕЦП — необязательная колонка «КПП» и код строки `INVALID_KPP` | заложить поле и код ошибки; в форме ИП поле КПП скрыть | -| arm-core #38 | Схема БД настройкой — **API не меняется** | — | | OQ-54 — SSO РАИСА | Меняется только получение токена (вместо dev-формы) | держать слой получения токена изолированным (`frontend-auth-guide.md` §9) | Состояние этих задач — в Gitea (`RSHB/arm-core`, `RSHB/arm-ka-docs`); после слияния каждой этот раздел переезжает в §1. diff --git a/model/diagrams/part01_groups_entities.png b/model/diagrams/part01_groups_entities.png index 056b304..4687549 100644 Binary files a/model/diagrams/part01_groups_entities.png and b/model/diagrams/part01_groups_entities.png differ diff --git a/model/diagrams/part01_groups_entities.svg b/model/diagrams/part01_groups_entities.svg index 193f4d1..c99f1c9 100644 --- a/model/diagrams/part01_groups_entities.svg +++ b/model/diagrams/part01_groups_entities.svg @@ -4,78 +4,78 @@ - - + + dbml entity_type - - - -       entity_type        - - -    ЮЛ     - - -    ИП     - + + + +       entity_type        + + +    ЮЛ     + + +    ИП     + related_group_kind - - - -       related_group_kind        - - -    gsk     - - -    gsz     - + + + +       related_group_kind        + + +    gsk     + + +    gsz     + le_history_event - - - -       le_history_event        - - -    CREATE     - - -    UPDATE     - - -    MOVE_TO_GROUP     - - -    CLOSE     - - -    REMOVE_FROM_GROUP     - + + + +       le_history_event        + + +    CREATE     + + +    UPDATE     + + +    MOVE_TO_GROUP     + + +    CLOSE     + + +    REMOVE_FROM_GROUP     + app_user - - - -       app_user        - - -id -     -uuid - + + + +       app_user        + + +id +     +uuid + @@ -83,34 +83,34 @@ regional_office - - - -       regional_office        - - -id -     -uuid - - -name     -varchar - -(!) - - -is_head_office     -boolean - -(!) - - -is_active     -boolean - -(!) - + + + +       regional_office        + + +id +     +uuid + + +name     +varchar + +(!) + + +is_head_office     +boolean + +(!) + + +is_active     +boolean + +(!) + @@ -118,60 +118,60 @@ company_group - - - -       company_group        - - -id -     -uuid - - -code     -varchar - -(!) - - -crm_id     -varchar - - -name     -varchar - -(!) - - -base_regional_office_id     -uuid - - -is_archived     -boolean - -(!) - - -valid_from     -timestamptz - -(!) - - -updated_by     -uuid - - -created_at     -timestamptz - - -updated_at     -timestamptz - + + + +       company_group        + + +id +     +uuid + + +code     +varchar + +(!) + + +crm_id     +varchar + + +name     +varchar + +(!) + + +base_regional_office_id     +uuid + + +is_archived     +boolean + +(!) + + +valid_from     +timestamptz + +(!) + + +updated_by     +uuid + + +created_at     +timestamptz + + +updated_at     +timestamptz + @@ -179,317 +179,325 @@ company_group:e->app_user:w - - -1 -* + + +1 +* company_group:e->regional_office:w - - -1 -* + + +1 +* company_group_history - - - -       company_group_history        - - -id -     -uuid - - -company_group_id     -uuid - -(!) - - -crm_id     -varchar - - -name     -varchar - - -base_regional_office_id     -uuid - - -valid_from     -timestamptz - - -valid_to     -timestamptz - - -changed_by     -uuid - + + + +       company_group_history        + + +id +     +uuid + + +company_group_id     +uuid + +(!) + + +crm_id     +varchar + + +name     +varchar + + +base_regional_office_id     +uuid + + +valid_from     +timestamptz + + +valid_to     +timestamptz + + +changed_by     +uuid + company_group_history:e->app_user:w - - -1 -* + + +1 +* company_group_history:e->company_group:w - - -1 -* + + +1 +* legal_entity - - - -       legal_entity        - - -id -     -uuid - - -inn     -varchar - -(!) - - -crm_id     -varchar - - -entity_type     -entity_type - - -name     -varchar - -(!) - - -company_group_id     -uuid - - -regional_office_id     -uuid - - -close_date     -date - - -valid_from     -timestamptz - -(!) - - -updated_by     -uuid - - -created_at     -timestamptz - - -updated_at     -timestamptz - + + + +       legal_entity        + + +id +     +uuid + + +inn     +varchar + +(!) + + +crm_id     +varchar + + +entity_type     +entity_type + + +name     +varchar + +(!) + + +company_group_id     +uuid + + +regional_office_id     +uuid + + +close_date     +date + + +valid_from     +timestamptz + +(!) + + +updated_by     +uuid + + +created_at     +timestamptz + + +updated_at     +timestamptz + + +kpp     +varchar + legal_entity:e->entity_type:w - + legal_entity:e->app_user:w - - -1 -* + + +1 +* legal_entity:e->regional_office:w - - -1 -* + + +1 +* legal_entity:e->company_group:w - - -1 -* + + +1 +* legal_entity_history - - - -       legal_entity_history        - - -id -     -uuid - - -legal_entity_id     -uuid - -(!) - - -inn     -varchar - - -crm_id     -varchar - - -entity_type     -entity_type - - -name     -varchar - - -company_group_id     -uuid - - -regional_office_id     -uuid - - -close_date     -date - - -event_type     -le_history_event - - -effective_date     -date - - -valid_from     -timestamptz - - -valid_to     -timestamptz - - -changed_by     -uuid - + + + +       legal_entity_history        + + +id +     +uuid + + +legal_entity_id     +uuid + +(!) + + +inn     +varchar + + +crm_id     +varchar + + +entity_type     +entity_type + + +name     +varchar + + +company_group_id     +uuid + + +regional_office_id     +uuid + + +close_date     +date + + +event_type     +le_history_event + + +effective_date     +date + + +valid_from     +timestamptz + + +valid_to     +timestamptz + + +changed_by     +uuid + + +kpp     +varchar + legal_entity_history:e->entity_type:w - + legal_entity_history:e->le_history_event:w - + legal_entity_history:e->app_user:w - - -1 -* + + +1 +* legal_entity_history:e->legal_entity:w - - -1 -* + + +1 +* legal_entity_alias - - - -       legal_entity_alias        - - -id -     -uuid - - -legal_entity_id     -uuid - -(!) - - -alias     -varchar - -(!) - - -normalized_alias     -varchar - -(!) - - -created_by     -uuid - - -created_at     -timestamptz - + + + +       legal_entity_alias        + + +id +     +uuid + + +legal_entity_id     +uuid + +(!) + + +alias     +varchar + +(!) + + +normalized_alias     +varchar + +(!) + + +created_by     +uuid + + +created_at     +timestamptz + @@ -497,154 +505,165 @@ legal_entity_alias:e->app_user:w - - -1 -* + + +1 +* legal_entity_alias:e->legal_entity:w - - -1 -* + + +1 +* related_group - - - -       related_group        - - -id -     -uuid - - -code     -varchar - -(!) - - -company_group_id     -uuid - -(!) - - -kind     -related_group_kind - -(!) - - -name     -varchar - -(!) - - -created_date     -date - + + + +       related_group        + + +id +     +uuid + + +code     +varchar + +(!) + + +company_group_id     +uuid + +(!) + + +kind     +related_group_kind + +(!) + + +name     +varchar + +(!) + + +created_date     +date + related_group:e->related_group_kind:w - + related_group:e->company_group:w - - -1 -* + + +1 +* related_group_member - - - - -       related_group_member        - - -id -     -uuid - - -related_group_id     -uuid - -(!) - - -legal_entity_id     -uuid - -(!) - - -valid_from     -timestamptz - -(!) - - -valid_to     -timestamptz - - -is_current     -boolean - -(!) - - -created_by     -uuid - + + + + +       related_group_member        + + +id +     +uuid + + +related_group_id     +uuid + +(!) + + +legal_entity_id     +uuid + +(!) + + +kind     +related_group_kind + +(!) + + +valid_from     +timestamptz + +(!) + + +valid_to     +timestamptz + + +is_current     +boolean + +(!) + + +created_by     +uuid + + + +related_group_member:e->related_group_kind:w + + related_group_member:e->app_user:w - - -1 -* + + +1 +* related_group_member:e->legal_entity:w - - -1 -* + + +1 +* related_group_member:e->related_group:w - - -1 -* + + +1 +* diff --git a/model/diagrams/part02_users_access.png b/model/diagrams/part02_users_access.png index e7fe4cc..aacb7cd 100644 Binary files a/model/diagrams/part02_users_access.png and b/model/diagrams/part02_users_access.png differ diff --git a/model/diagrams/part02_users_access.svg b/model/diagrams/part02_users_access.svg index ad73347..cd6cb95 100644 --- a/model/diagrams/part02_users_access.svg +++ b/model/diagrams/part02_users_access.svg @@ -4,187 +4,385 @@ - - + + dbml business_segment - - - -       business_segment        - - -    DKB     - - -    DMSB     - - -    DM     - + + + +       business_segment        + + +    DKB     + + +    DMSB     + + +    DM     + + + + +permission_object + + + +       permission_object        + + +    DIRECTORY     + + +    DOCUMENT     + + +    CONSOLIDATION     + + +    KFO     + + +    EX     + + +    EXPORT     + + +    USERS     + + +    AUDIT     + + + + +permission_action + + + +       permission_action        + + +    VIEW     + + +    EDIT     + + +    APPROVE     + + +    VERIFY     + + +    DELETE     + + +    EXPORT     + + +    ADMIN     + regional_office - - - -       regional_office        - - -id -     -uuid - + + + +       regional_office        + + +id +     +uuid + app_user - - - -       app_user        - - -id -     -uuid - - -external_id     -varchar - - -email     -varchar - - -full_name     -varchar - -(!) - - -employee_no     -varchar - - -regional_office_id     -uuid - -(!) - - -business_segment     -business_segment - - -is_active     -boolean - -(!) - - -created_at     -timestamptz - - -updated_at     -timestamptz - + + + +       app_user        + + +id +     +uuid + + +external_id     +varchar + + +email     +varchar + + +full_name     +varchar + +(!) + + +employee_no     +varchar + + +regional_office_id     +uuid + +(!) + + +business_segment     +business_segment + + +is_active     +boolean + +(!) + + +created_at     +timestamptz + + +updated_at     +timestamptz + - + app_user:e->business_segment:w - + app_user:e->regional_office:w - - -1 -* + + +1 +* role - - - -       role        - - -id -     -uuid - - -code     -varchar - -(!) - - -name     -varchar - -(!) - + + + + +       role        + + +id +     +uuid + + +code     +varchar + +(!) + + +name     +varchar + +(!) + + +description     +text + + +is_system     +boolean + +(!) + + +is_active     +boolean + +(!) + + + + + + +permission + + + + +       permission        + + +id +     +uuid + + +code     +varchar + +(!) + + +object_type     +permission_object + +(!) + + +action     +permission_action + +(!) + + +name     +varchar + +(!) + + +description     +text + + + + + + +permission:e->permission_object:w + + + + +permission:e->permission_action:w + + + + +role_permission + + + + +       role_permission        + + +role_id +     +uuid + +(!) + + +permission_id +     +uuid + +(!) + + +granted_by     +uuid + + +granted_at     +timestamptz + + + + + + + +role_permission:e->app_user:w + + +1 +* + + + + +role_permission:e->role:w + + +1 +* + + + + +role_permission:e->permission:w + + +1 +* user_role - - - -       user_role        - - -id -     -uuid - - -user_id     -uuid - -(!) - - -role_id     -uuid - -(!) - + + + +       user_role        + + +id +     +uuid + + +user_id     +uuid + +(!) + + +role_id     +uuid + +(!) + - + user_role:e->app_user:w - - -1 -* + + +1 +* - + user_role:e->role:w - - -1 -* + + +1 +* diff --git a/model/diagrams/part03_accounting_refs.png b/model/diagrams/part03_accounting_refs.png index 9d679ce..8214667 100644 Binary files a/model/diagrams/part03_accounting_refs.png and b/model/diagrams/part03_accounting_refs.png differ diff --git a/model/diagrams/part03_accounting_refs.svg b/model/diagrams/part03_accounting_refs.svg index c72360c..886db23 100644 --- a/model/diagrams/part03_accounting_refs.svg +++ b/model/diagrams/part03_accounting_refs.svg @@ -4,261 +4,261 @@ - + dbml account_kind - - - -       account_kind        - - -    A     - - -    P     - - -    AP     - + + + +       account_kind        + + +    A     + + +    P     + + +    AP     + account_row_type - - - -       account_row_type        - - -    account     - - -    subaccount     - - -    analytic     - + + + +       account_row_type        + + +    account     + + +    subaccount     + + +    analytic     + document_type - - - -       document_type        - - -    Бухгалтерский баланс     - - -    ОФР     - - -    ОСВ     - - -    Расшифровка     - - -    Прочий документ     - + + + +       document_type        + + +    BALANCE     + + +    INCOME     + + +    OSV     + + +    DISCLOSURE     + + +    OTHER     + statement_type - - - -       statement_type        - - -    balance     - - -    income     - + + + +       statement_type        + + +    balance     + + +    income     + consolidation_approach - - - -       consolidation_approach        - - -    BASIC     - - -    EXTENDED     - + + + +       consolidation_approach        + + +    BASIC     + + +    EXTENDED     + chart_of_account - - - -       chart_of_account        - - -id -     -uuid - - -code     -varchar - -(!) - - -parent_id     -uuid - - -name     -varchar - -(!) - - -kind     -account_kind - - -row_type     -account_row_type - -(!) - - -is_off_balance     -boolean - -(!) - + + + +       chart_of_account        + + +id +     +uuid + + +code     +varchar + +(!) + + +parent_id     +uuid + + +name     +varchar + +(!) + + +kind     +account_kind + + +row_type     +account_row_type + +(!) + + +is_off_balance     +boolean + +(!) + chart_of_account:e->account_kind:w - + chart_of_account:e->account_row_type:w - + chart_of_account:e->chart_of_account:w - - -1 -* + + +1 +* elimination_rule - - - -       elimination_rule        - - -id -     -uuid - - -statement_type     -statement_type - -(!) - - -rule_no     -smallint - -(!) - - -article_1_line     -varchar - -(!) - - -article_2_line     -varchar - -(!) - - -side_1     -varchar - -(!) - - -measure     -varchar - -(!) - - -elimination_mode     -varchar - -(!) - - -vat_handling     -varchar - - -excise_adjust     -boolean - -(!) - - -comment     -text - - -version_no     -int - -(!) - - -valid_from     -date - -(!) - - -valid_to     -date - + + + +       elimination_rule        + + +id +     +uuid + + +statement_type     +statement_type + +(!) + + +rule_no     +smallint + +(!) + + +article_1_line     +varchar + +(!) + + +article_2_line     +varchar + +(!) + + +side_1     +varchar + +(!) + + +measure     +varchar + +(!) + + +elimination_mode     +varchar + +(!) + + +vat_handling     +varchar + + +excise_adjust     +boolean + +(!) + + +comment     +text + + +version_no     +int + +(!) + + +valid_from     +date + +(!) + + +valid_to     +date + elimination_rule:e->statement_type:w - + @@ -305,24 +305,24 @@ elimination_rule_account:e->chart_of_account:w - - -1 -* + + +1 +* elimination_rule_account:e->elimination_rule:w - - -1 -* + + +1 +* required_document_rule - + @@ -361,21 +361,21 @@ required_document_rule:e->document_type:w - + required_document_rule:e->consolidation_approach:w - + required_document_rule:e->chart_of_account:w - - -1 -* + + +1 +* diff --git a/model/diagrams/part04_files_jobs.png b/model/diagrams/part04_files_jobs.png index 4e17cb8..5eb4559 100644 Binary files a/model/diagrams/part04_files_jobs.png and b/model/diagrams/part04_files_jobs.png differ diff --git a/model/diagrams/part04_files_jobs.svg b/model/diagrams/part04_files_jobs.svg index 2b6d3b9..3054f9d 100644 --- a/model/diagrams/part04_files_jobs.svg +++ b/model/diagrams/part04_files_jobs.svg @@ -4,213 +4,228 @@ - - + + dbml document_type - - - -       document_type        - - -    Бухгалтерский баланс     - - -    ОФР     - - -    ОСВ     - - -    Расшифровка     - - -    Прочий документ     - + + + +       document_type        + + +    BALANCE     + + +    INCOME     + + +    OSV     + + +    DISCLOSURE     + + +    OTHER     + file_type - - - -       file_type        - - -    XLS     - - -    XLSX     - - -    XLSB     - - -    P7     - - -    PDF     - - -    PNG     - - -    JPG     - + + + +       file_type        + + +    XLS     + + +    XLSX     + + +    XLSB     + + +    PDF     + + +    PNG     + + +    JPG     + recognition_pipeline - - - -       recognition_pipeline        - - -    tabular     - - -    image     - + + + +       recognition_pipeline        + + +    tabular     + + +    image     + period_quarter - - - -       period_quarter        - - -    Q1     - - -    Q2     - - -    Q3     - - -    Q4     - + + + +       period_quarter        + + +    Q1     + + +    Q2     + + +    Q3     + + +    Q4     + file_status - - - -       file_status        - - -    UPLOADED     - - -    PROCESSING     - - -    NEEDS_CHECK     - - -    CONFIRMED     - - -    ERROR     - - -    SUPERSEDED     - + + + +       file_status        + + +    UPLOADED     + + +    PROCESSING     + + +    PROCESSED     + + +    ERROR     + + +    SUPERSEDED     + + + + +document_status + + + +       document_status        + + +    RECOGNIZED     + + +    NEEDS_CHECK     + + +    CONFIRMED     + + +    ERROR     + s3_bucket_role - - - -       s3_bucket_role        - - -    uploads     - - -    results     - - -    exports     - - -    archive     - + + + +       s3_bucket_role        + + +    uploads     + + +    results     + + +    exports     + + +    archive     + recognition_status - - - -       recognition_status        - - -    queued     - - -    published     - - -    processing     - - -    done     - - -    needs_check     - - -    error     - - -    timeout     - - -    dlq     - + + + +       recognition_status        + + +    queued     + + +    published     + + +    processing     + + +    done     + + +    needs_check     + + +    error     + + +    timeout     + + +    dlq     + document_check_result - - - -       document_check_result        - - -    MATCH     - - -    MISMATCH     - - -    UNKNOWN     - + + + +       document_check_result        + + +    MATCH     + + +    MISMATCH     + + +    UNKNOWN     + app_user - - - -       app_user        - - -id -     -uuid - + + + +       app_user        + + +id +     +uuid + @@ -218,425 +233,350 @@ legal_entity - - - -       legal_entity        - - -id -     -uuid - + + + +       legal_entity        + + +id +     +uuid + s3_object - - - - -       s3_object        - - -id -     -uuid - - -bucket_role     -s3_bucket_role - -(!) - - -bucket     -varchar - -(!) - - -key     -varchar - -(!) - - -content_type     -varchar - - -size_bytes     -bigint - - -sha256     -varchar - - -version_id     -varchar - - -created_by     -varchar - - -created_at     -timestamptz - -(!) - - -expires_at     -timestamptz - - -archived_to     -varchar - + + + + +       s3_object        + + +id +     +uuid + + +bucket_role     +s3_bucket_role + +(!) + + +bucket     +varchar + +(!) + + +key     +varchar + +(!) + + +content_type     +varchar + + +size_bytes     +bigint + + +sha256     +varchar + + +version_id     +varchar + + +created_by     +varchar + + +created_at     +timestamptz + +(!) + + +expires_at     +timestamptz + + +archived_to     +varchar + - + s3_object:e->s3_bucket_role:w - + stored_file - - - - -       stored_file        - - -id -     -uuid - - -legal_entity_id     -uuid - -(!) - - -document_type     -document_type - -(!) - - -osv_account_code     -varchar - - -file_type     -file_type - -(!) - - -original_name     -varchar - - -s3_object_id     -uuid - -(!) - - -content_hash     -varchar - - -size_bytes     -bigint - - -period_year     -smallint - - -period_quarter     -period_quarter - - -is_cumulative     -boolean - - -reporting_date     -date - - -status     -file_status - -(!) - - -period_check     -document_check_result - - -counterparty_check     -document_check_result - - -version_no     -int - -(!) - - -superseded_by_file_id     -uuid - - -current_recognition_job_id     -uuid - - -uploaded_by     -uuid - - -uploaded_at     -timestamptz - - -confirmed_by     -uuid - - -confirmed_at     -timestamptz - - -fields_modified     -int - - -updated_at     -timestamptz - + + + + +       stored_file        + + +id +     +uuid + + +legal_entity_id     +uuid + +(!) + + +file_type     +file_type + +(!) + + +original_name     +varchar + + +s3_object_id     +uuid + +(!) + + +content_hash     +varchar + + +size_bytes     +bigint + + +status     +file_status + +(!) + + +version_no     +int + +(!) + + +superseded_by_file_id     +uuid + + +current_recognition_job_id     +uuid + + +uploaded_by     +uuid + + +uploaded_at     +timestamptz + + +updated_at     +timestamptz + - - -stored_file:e->document_type:w - - - -stored_file:e->file_type:w - - - -stored_file:e->period_quarter:w - +stored_file:e->file_type:w + stored_file:e->file_status:w - - - - -stored_file:e->document_check_result:w - - - - -stored_file:e->document_check_result:w - + stored_file:e->app_user:w - - -1 -* - - - - -stored_file:e->app_user:w - - -1 -* + + +1 +* stored_file:e->legal_entity:w - - -1 -* + + +1 +* stored_file:e->s3_object:w - - -1 -* + + +1 +* stored_file:e->stored_file:w - - -1 -* + + +1 +* recognition_job - - - - -       recognition_job        - - -id -     -uuid - - -file_id     -uuid - -(!) - - -pipeline     -recognition_pipeline - -(!) - - -request_subject     -varchar - -(!) - - -request_msg_id     -varchar - - -result_msg_id     -varchar - - -schema_version     -varchar - -(!) - - -service_name     -varchar - - -service_version     -varchar - - -status     -recognition_status - -(!) - - -attempt     -smallint - -(!) - - -max_attempts     -smallint - -(!) - - -requested_at     -timestamptz - - -published_at     -timestamptz - - -started_at     -timestamptz - - -finished_at     -timestamptz - - -reply_deadline     -timestamptz - - -external_task_id     -varchar - - -error_code     -varchar - - -error_message     -text - - -result_s3_object_id     -uuid - - -raw_result_s3_object_id     -uuid - - -result_schema_version     -varchar - - -metrics     -jsonb - - -fields_total     -int - - -fields_low_confidence     -int - - -fields_unrecognized     -int - + + + + +       recognition_job        + + +id +     +uuid + + +file_id     +uuid + +(!) + + +pipeline     +recognition_pipeline + +(!) + + +request_subject     +varchar + +(!) + + +request_msg_id     +varchar + + +result_msg_id     +varchar + + +schema_version     +varchar + +(!) + + +service_name     +varchar + + +service_version     +varchar + + +status     +recognition_status + +(!) + + +attempt     +smallint + +(!) + + +max_attempts     +smallint + +(!) + + +requested_at     +timestamptz + + +published_at     +timestamptz + + +started_at     +timestamptz + + +finished_at     +timestamptz + + +reply_deadline     +timestamptz + + +external_task_id     +varchar + + +error_code     +varchar + + +error_message     +text + + +result_s3_object_id     +uuid + + +result_schema_version     +varchar + + +group_schema     +jsonb + + +metrics     +jsonb + + +fields_total     +int + + +fields_low_confidence     +int + + +fields_unrecognized     +int + @@ -644,47 +584,179 @@ stored_file:e->recognition_job:w - - -1 -* + + +1 +* + + + +document + + + + +       document        + + +id +     +uuid + + +stored_file_id     +uuid + +(!) + + +legal_entity_id     +uuid + +(!) + + +document_type     +document_type + +(!) + + +osv_account_code     +varchar + + +period_year     +smallint + + +period_quarter     +period_quarter + + +is_cumulative     +boolean + + +reporting_date     +date + + +locator     +varchar + + +status     +document_status + +(!) + + +period_check     +document_check_result + + +counterparty_check     +document_check_result + + +confirmed_by     +uuid + + +confirmed_at     +timestamptz + + +created_at     +timestamptz + + +updated_at     +timestamptz + + + + + + +document:e->document_type:w + + + + +document:e->period_quarter:w + + + + +document:e->document_status:w + + + + +document:e->document_check_result:w + + + + +document:e->document_check_result:w + + + + + +document:e->app_user:w + + +1 +* + + + + +document:e->legal_entity:w + + +1 +* + + + + +document:e->stored_file:w + + +1 +* - + recognition_job:e->recognition_pipeline:w - + - + recognition_job:e->recognition_status:w - + - + recognition_job:e->s3_object:w - - -1 -* + + +1 +* - - + + -recognition_job:e->s3_object:w - - -1 -* - - - - recognition_job:e->stored_file:w - - -1 -* + + +1 +* diff --git a/model/diagrams/part05_osv_parsed.png b/model/diagrams/part05_osv_parsed.png index d05b90d..b5b6881 100644 Binary files a/model/diagrams/part05_osv_parsed.png and b/model/diagrams/part05_osv_parsed.png differ diff --git a/model/diagrams/part05_osv_parsed.svg b/model/diagrams/part05_osv_parsed.svg index 155cc81..7500e98 100644 --- a/model/diagrams/part05_osv_parsed.svg +++ b/model/diagrams/part05_osv_parsed.svg @@ -4,69 +4,96 @@ - - + + dbml osv_group_type - - - -       osv_group_type        - - -    ACCOUNT     - - -    DEPARTMENT     - - -    COUNTERPARTY     - - -    CONTRACT     - - -    OTHER     - + + + +       osv_group_type        + + +    ACCOUNT     + + +    DEPARTMENT     + + +    COUNTERPARTY     + + +    CONTRACT     + + +    OTHER     + + + + +counterparty_match_kind + + + +       counterparty_match_kind        + + +    INN     + + +    NAME     + + +    ALIAS     + + +    MANUAL     + + +    PROPAGATED     + osv_data_type - - - -       osv_data_type        - - -    BU     - - -    CURRENCY     - - -    QUANTITY     - - -    SUM     - + + + +       osv_data_type        + + +    BU     + + +    NU     + + +    CURRENCY     + + +    QUANTITY     + + +    SUM     + app_user - - - -       app_user        - - -id -     -uuid - + + + +       app_user        + + +id +     +uuid + @@ -74,16 +101,33 @@ chart_of_account - - - -       chart_of_account        - - -id -     -uuid - + + + +       chart_of_account        + + +id +     +uuid + + + + + + +document + + + + +       document        + + +id +     +uuid + @@ -91,16 +135,16 @@ legal_entity - - - -       legal_entity        - - -id -     -uuid - + + + +       legal_entity        + + +id +     +uuid + @@ -108,302 +152,233 @@ recognition_job - - - -       recognition_job        - - -id -     -uuid - + + + +       recognition_job        + + +id +     +uuid + - - -stored_file - - - - -       stored_file        - - -id -     -uuid - - - - - - -osv_group_schema - - - - -       osv_group_schema        - - -id -     -uuid - - -recognition_job_id     -uuid - -(!) - - -position     -smallint - -(!) - - -group_type     -osv_group_type - -(!) - - -group_name     -varchar - -(!) - - -has_inn     -boolean - -(!) - - - - - - -osv_group_schema:e->osv_group_type:w - - - - - -osv_group_schema:e->recognition_job:w - - -1 -* - osv_entry - - - - -       osv_entry        - - -id -     -uuid - - -recognition_job_id     -uuid - -(!) - - -stored_file_id     -uuid - -(!) - - -owner_legal_entity_id     -uuid - -(!) - - -parent_entry_id     -uuid - - -group_schema_id     -uuid - - -row_number     -int - - -level     -smallint - - -row_value     -varchar - - -data_type     -osv_data_type - - -account_code     -varchar - - -account_id     -uuid - - -account_root     -varchar - - -counterparty_name     -varchar - - -counterparty_inn     -varchar - - -counterparty_id     -uuid - - -opening_debit     -numeric(18,2) - - -opening_credit     -numeric(18,2) - - -turnover_debit     -numeric(18,2) - - -turnover_credit     -numeric(18,2) - - -closing_debit     -numeric(18,2) - - -closing_credit     -numeric(18,2) - - -is_modified     -boolean - -(!) - - -original_values     -jsonb - - -modified_by     -uuid - - -modified_at     -timestamptz - - -is_excisable     -boolean - + + + + +       osv_entry        + + +id +     +uuid + + +recognition_job_id     +uuid + +(!) + + +document_id     +uuid + +(!) + + +owner_legal_entity_id     +uuid + +(!) + + +parent_entry_id     +uuid + + +group_type     +osv_group_type + + +group_name     +varchar + + +row_number     +int + + +level     +smallint + + +row_value     +varchar + + +data_type     +osv_data_type + + +account_code     +varchar + + +account_id     +uuid + + +account_root     +varchar + + +counterparty_name     +varchar + + +counterparty_inn     +varchar + + +counterparty_id     +uuid + + +opening_debit     +numeric(18,2) + + +opening_credit     +numeric(18,2) + + +turnover_debit     +numeric(18,2) + + +turnover_credit     +numeric(18,2) + + +closing_debit     +numeric(18,2) + + +closing_credit     +numeric(18,2) + + +is_modified     +boolean + +(!) + + +original_values     +jsonb + + +modified_by     +uuid + + +modified_at     +timestamptz + + +is_excisable     +boolean + + +counterparty_match_kind     +counterparty_match_kind + + + +osv_entry:e->osv_group_type:w + + + + +osv_entry:e->counterparty_match_kind:w + + - -osv_entry:e->osv_data_type:w - - - - - -osv_entry:e->app_user:w - - -1 -* - - - - -osv_entry:e->chart_of_account:w - - -1 -* - - - - -osv_entry:e->legal_entity:w - - -1 -* - - - -osv_entry:e->legal_entity:w - - -1 -* +osv_entry:e->osv_data_type:w + - - - -osv_entry:e->recognition_job:w - - -1 -* + + + +osv_entry:e->app_user:w + + +1 +* - - - -osv_entry:e->stored_file:w - - -1 -* - - - - -osv_entry:e->osv_group_schema:w - - -1 -* - - - + + +osv_entry:e->chart_of_account:w + + +1 +* + + + + +osv_entry:e->document:w + + +1 +* + + + + +osv_entry:e->legal_entity:w + + +1 +* + + + + +osv_entry:e->legal_entity:w + + +1 +* + + + + +osv_entry:e->recognition_job:w + + +1 +* + + + + osv_entry:e->osv_entry:w - - -1 -* + + +1 +* diff --git a/model/diagrams/part06_statements.png b/model/diagrams/part06_statements.png index cdf53db..fcd98be 100644 Binary files a/model/diagrams/part06_statements.png and b/model/diagrams/part06_statements.png differ diff --git a/model/diagrams/part06_statements.svg b/model/diagrams/part06_statements.svg index 60ff040..3445498 100644 --- a/model/diagrams/part06_statements.svg +++ b/model/diagrams/part06_statements.svg @@ -4,39 +4,56 @@ - - + + dbml statement_type - - - -       statement_type        - - -    balance     - - -    income     - + + + +       statement_type        + + +    balance     + + +    income     + app_user - - - -       app_user        - - -id -     -uuid - + + + +       app_user        + + +id +     +uuid + + + + + + +document + + + + +       document        + + +id +     +uuid + @@ -44,33 +61,16 @@ recognition_job - - - -       recognition_job        - - -id -     -uuid - - - - - - -stored_file - - - - -       stored_file        - - -id -     -uuid - + + + +       recognition_job        + + +id +     +uuid + @@ -78,232 +78,232 @@ financial_statement_meta - - - -       financial_statement_meta        - - -id -     -uuid - - -stored_file_id     -uuid - -(!) - - -recognition_job_id     -uuid - -(!) - - -statement_type     -statement_type - -(!) - - -inn     -varchar - - -inn_original_value     -varchar - - -inn_confidence     -numeric(5,2) - - -inn_verified     -boolean - - -inn_modified     -boolean - - -organization_name     -varchar - - -report_date     -date - - -report_date_original_value     -varchar - - -report_date_verified     -boolean - - -report_date_modified     -boolean - - -units     -varchar - - -units_original_value     -varchar - - -units_verified     -boolean - - -units_modified     -boolean - - -currency     -varchar - - -okpo     -varchar - - -okopf     -varchar - - -okfs     -varchar - - -okud     -varchar - - -okei     -varchar - - -legal_form     -varchar - - -ownership_form     -varchar - - -address     -varchar - - -subject_to_audit     -boolean - - -reviewed_by     -uuid - - -reviewed_at     -timestamptz - - -checks_accepted_by     -uuid - - -checks_accepted_at     -timestamptz - - -checks_accepted_comment     -text - + + + +       financial_statement_meta        + + +id +     +uuid + + +document_id     +uuid + +(!) + + +recognition_job_id     +uuid + +(!) + + +statement_type     +statement_type + +(!) + + +inn     +varchar + + +inn_original_value     +varchar + + +inn_confidence     +numeric(5,2) + + +inn_verified     +boolean + + +inn_modified     +boolean + + +organization_name     +varchar + + +report_date     +date + + +report_date_original_value     +varchar + + +report_date_verified     +boolean + + +report_date_modified     +boolean + + +units     +varchar + + +units_original_value     +varchar + + +units_verified     +boolean + + +units_modified     +boolean + + +currency     +varchar + + +okpo     +varchar + + +okopf     +varchar + + +okfs     +varchar + + +okud     +varchar + + +okei     +varchar + + +legal_form     +varchar + + +ownership_form     +varchar + + +address     +varchar + + +subject_to_audit     +boolean + + +reviewed_by     +uuid + + +reviewed_at     +timestamptz + + +checks_accepted_by     +uuid + + +checks_accepted_at     +timestamptz + + +checks_accepted_comment     +text + financial_statement_meta:e->statement_type:w - + financial_statement_meta:e->app_user:w - - -1 -* + + +1 +* financial_statement_meta:e->app_user:w - - -1 -* + + +1 +* + + + + +financial_statement_meta:e->document:w + + +1 +* financial_statement_meta:e->recognition_job:w - - -1 -* - - - - -financial_statement_meta:e->stored_file:w - - -1 -* + + +1 +* financial_statement_column - - - -       financial_statement_column        - - -id -     -uuid - - -meta_id     -uuid - -(!) - - -column_index     -smallint - -(!) - - -label     -varchar - - -period_date     -date - - -is_current     -boolean - + + + +       financial_statement_column        + + +id +     +uuid + + +meta_id     +uuid + +(!) + + +column_index     +smallint + +(!) + + +label     +varchar + + +period_date     +date + + +is_current     +boolean + @@ -311,63 +311,63 @@ financial_statement_column:e->financial_statement_meta:w - - -1 -* + + +1 +* financial_statement_line - - - -       financial_statement_line        - - -id -     -uuid - - -meta_id     -uuid - -(!) - - -line_code     -varchar - - -line_code_original_value     -varchar - - -line_code_confidence     -numeric(5,2) - - -line_code_modified     -boolean - - -line_name     -varchar - - -line_name_original_value     -varchar - - -line_name_confidence     -numeric(5,2) - - -line_name_modified     -boolean - + + + +       financial_statement_line        + + +id +     +uuid + + +meta_id     +uuid + +(!) + + +line_code     +varchar + + +line_code_original_value     +varchar + + +line_code_confidence     +numeric(5,2) + + +line_code_modified     +boolean + + +line_name     +varchar + + +line_name_original_value     +varchar + + +line_name_confidence     +numeric(5,2) + + +line_name_modified     +boolean + @@ -375,103 +375,97 @@ financial_statement_line:e->financial_statement_meta:w - - -1 -* + + +1 +* financial_statement_value - - - -       financial_statement_value        - - -id -     -uuid - - -line_id     -uuid - -(!) - - -column_id     -uuid - -(!) - - -amount     -numeric(18,2) - - -string_value     -varchar - - -confidence     -numeric(5,2) - - -is_recognized     -boolean - - -needs_review     -boolean - -(!) - - -is_modified     -boolean - -(!) - - -original_amount     -numeric(18,2) - - -modified_by     -uuid - - -modified_at     -timestamptz - + + + +       financial_statement_value        + + +id +     +uuid + + +line_id     +uuid + +(!) + + +column_id     +uuid + +(!) + + +amount     +numeric(18,2) + + +string_value     +varchar + + +confidence     +numeric(5,2) + + +is_recognized     +boolean + + +is_modified     +boolean + +(!) + + +original_amount     +numeric(18,2) + + +modified_by     +uuid + + +modified_at     +timestamptz + financial_statement_value:e->app_user:w - - -1 -* + + +1 +* financial_statement_value:e->financial_statement_column:w - - -1 -* + + +1 +* financial_statement_value:e->financial_statement_line:w - - -1 -* + + +1 +* diff --git a/model/diagrams/part07_consolidation.png b/model/diagrams/part07_consolidation.png index 62b5231..3a31cd7 100644 Binary files a/model/diagrams/part07_consolidation.png and b/model/diagrams/part07_consolidation.png differ diff --git a/model/diagrams/part07_consolidation.svg b/model/diagrams/part07_consolidation.svg index 8bece72..2ad775c 100644 --- a/model/diagrams/part07_consolidation.svg +++ b/model/diagrams/part07_consolidation.svg @@ -138,6 +138,23 @@ + + +document + + + + +       document        + + +id +     +uuid + + + + legal_entity @@ -189,23 +206,6 @@ - - -stored_file - - - - -       stored_file        - - -id -     -uuid - - - - consolidation @@ -699,7 +699,7 @@ consolidation_document - + @@ -717,7 +717,7 @@ (!) -stored_file_id     +document_id     uuid (!) @@ -725,13 +725,13 @@ - - + + -consolidation_document:e->stored_file:w - - -1 +consolidation_document:e->document:w + + +1 * diff --git a/model/diagrams/part08_corrections_kfo.png b/model/diagrams/part08_corrections_kfo.png index 69ae65e..44b124c 100644 Binary files a/model/diagrams/part08_corrections_kfo.png and b/model/diagrams/part08_corrections_kfo.png differ diff --git a/model/diagrams/part09_audit_ex.png b/model/diagrams/part09_audit_ex.png index b477f89..92b2d8d 100644 Binary files a/model/diagrams/part09_audit_ex.png and b/model/diagrams/part09_audit_ex.png differ diff --git a/model/diagrams/part09_audit_ex.svg b/model/diagrams/part09_audit_ex.svg index b7540fa..32cdf6b 100644 --- a/model/diagrams/part09_audit_ex.svg +++ b/model/diagrams/part09_audit_ex.svg @@ -4,45 +4,45 @@ - - + + dbml period_quarter - - - -       period_quarter        - - -    Q1     - - -    Q2     - - -    Q3     - - -    Q4     - + + + +       period_quarter        + + +    Q1     + + +    Q2     + + +    Q3     + + +    Q4     + ex_snapshot_status - - - -       ex_snapshot_status        - - -    ACTUAL     - - -    ARCHIVE     - + + + +       ex_snapshot_status        + + +    ACTUAL     + + +    ARCHIVE     + @@ -83,104 +83,116 @@ audit_action - - - -       audit_action        - - -    LOGIN     - - -    LOGIN_FAILED     - - -    LOGOUT     - - -    CONSOLIDATION_CREATE     - - -    CONSOLIDATION_STATUS_CHANGE     - - -    CONSOLIDATION_APPROVE     - - -    CONSOLIDATION_VERIFY     - - -    GROUP_CHANGE     - - -    MEMBERSHIP_CHANGE     - - -    ASSIGNMENT_CHANGE     - - -    FILE_UPLOAD     - - -    FILE_DELETE     - - -    FILE_REPLACE     - - -    CONSOLIDATION_LOCK     - - -    CONSOLIDATION_LOCK_TAKEOVER     - - -    DISCLOSURE_EDIT     - - -    RECOGNIZED_DATA_EDIT     - - -    OSV_COUNTERPARTY_MAP     - - -    OSV_TYPE_OVERRIDE     - - -    CORRECTION_CREATE     - - -    CORRECTION_EDIT     - - -    CORRECTION_REVOKE     - - -    GOODWILL_EDIT     - - -    EXPORT     - - -    DIRECTORY_IMPORT     - - -    USER_ROLE_CHANGE     - + + + +       audit_action        + + +    LOGIN     + + +    LOGIN_FAILED     + + +    LOGOUT     + + +    CONSOLIDATION_CREATE     + + +    CONSOLIDATION_STATUS_CHANGE     + + +    CONSOLIDATION_APPROVE     + + +    CONSOLIDATION_VERIFY     + + +    GROUP_CHANGE     + + +    LEGAL_ENTITY_CHANGE     + + +    MEMBERSHIP_CHANGE     + + +    ASSIGNMENT_CHANGE     + + +    FILE_UPLOAD     + + +    FILE_DELETE     + + +    FILE_REPLACE     + + +    DOCUMENT_CONFIRM     + + +    CONSOLIDATION_LOCK     + + +    CONSOLIDATION_LOCK_TAKEOVER     + + +    DISCLOSURE_EDIT     + + +    RECOGNIZED_DATA_EDIT     + + +    OSV_COUNTERPARTY_MAP     + + +    OSV_TYPE_OVERRIDE     + + +    CORRECTION_CREATE     + + +    CORRECTION_EDIT     + + +    CORRECTION_REVOKE     + + +    GOODWILL_EDIT     + + +    EXPORT     + + +    DIRECTORY_IMPORT     + + +    USER_ROLE_CHANGE     + + +    USER_CHANGE     + + +    ROLE_PERMISSION_CHANGE     + app_user - - - -       app_user        - - -id -     -uuid - + + + +       app_user        + + +id +     +uuid + @@ -188,16 +200,16 @@ company_group - - - -       company_group        - - -id -     -uuid - + + + +       company_group        + + +id +     +uuid + @@ -218,20 +230,37 @@ + + +document + + + + +       document        + + +id +     +uuid + + + + legal_entity - - - -       legal_entity        - - -id -     -uuid - + + + +       legal_entity        + + +id +     +uuid + @@ -239,33 +268,16 @@ related_group - - - -       related_group        - - -id -     -uuid - - - - - - -stored_file - - - - -       stored_file        - - -id -     -uuid - + + + +       related_group        + + +id +     +uuid + @@ -273,46 +285,46 @@ comment - - - -       comment        - - -id -     -uuid - - -object_type     -varchar - -(!) - - -object_id     -uuid - -(!) - - -text     -text - -(!) - - -author_id     -uuid - -(!) - - -created_at     -timestamptz - -(!) - + + + +       comment        + + +id +     +uuid + + +object_type     +varchar + +(!) + + +object_id     +uuid + +(!) + + +text     +text + +(!) + + +author_id     +uuid + +(!) + + +created_at     +timestamptz + +(!) + @@ -320,304 +332,304 @@ comment:e->app_user:w - - -1 -* + + +1 +* audit_event - - - - -       audit_event        - - -id -     -uuid - - -occurred_at     -timestamptz - -(!) - - -user_id     -uuid - - -action     -audit_action - -(!) - - -section     -audit_section - -(!) - - -object_type     -varchar - -(!) - - -object_id     -uuid - - -consolidation_version_id     -uuid - - -description     -text - - -before_value     -jsonb - - -after_value     -jsonb - - -comment     -text - - -result     -varchar - - -changed_field     -varchar - - -ip_address     -varchar - - -user_agent     -varchar - + + + + +       audit_event        + + +id +     +uuid + + +occurred_at     +timestamptz + +(!) + + +user_id     +uuid + + +action     +audit_action + +(!) + + +section     +audit_section + +(!) + + +object_type     +varchar + +(!) + + +object_id     +uuid + + +consolidation_version_id     +uuid + + +description     +text + + +before_value     +jsonb + + +after_value     +jsonb + + +comment     +text + + +result     +varchar + + +changed_field     +varchar + + +ip_address     +varchar + + +user_agent     +varchar + audit_event:e->audit_section:w - + audit_event:e->audit_action:w - + audit_event:e->app_user:w - - -1 -* + + +1 +* audit_event:e->consolidation_version:w - + 1 -* +* ex_snapshot - - - -       ex_snapshot        - - -id -     -uuid - - -related_group_id     -uuid - -(!) - - -company_group_id     -uuid - -(!) - - -reporting_date     -date - -(!) - - -year     -smallint - -(!) - - -quarter     -period_quarter - -(!) - - -version_no     -int - -(!) - - -status     -ex_snapshot_status - -(!) - - -source_version_id     -uuid - -(!) - - -source_version_no     -int - -(!) - - -superseded_by_snapshot_id     -uuid - - -verified_at     -timestamptz - -(!) - - -written_at     -timestamptz - -(!) - - -written_by     -uuid - - -archived_at     -timestamptz - - -data_lake_ref     -varchar - + + + +       ex_snapshot        + + +id +     +uuid + + +related_group_id     +uuid + +(!) + + +company_group_id     +uuid + +(!) + + +reporting_date     +date + +(!) + + +year     +smallint + +(!) + + +quarter     +period_quarter + +(!) + + +version_no     +int + +(!) + + +status     +ex_snapshot_status + +(!) + + +source_version_id     +uuid + +(!) + + +source_version_no     +int + +(!) + + +superseded_by_snapshot_id     +uuid + + +verified_at     +timestamptz + +(!) + + +written_at     +timestamptz + +(!) + + +written_by     +uuid + + +archived_at     +timestamptz + + +data_lake_ref     +varchar + ex_snapshot:e->period_quarter:w - + ex_snapshot:e->ex_snapshot_status:w - + ex_snapshot:e->app_user:w - - -1 -* + + +1 +* ex_snapshot:e->company_group:w - - -1 -* + + +1 +* ex_snapshot:e->related_group:w - - -1 -* + + +1 +* ex_snapshot:e->ex_snapshot:w - - -1 -* + + +1 +* ex_snapshot_member - - - -       ex_snapshot_member        - - -ex_snapshot_id -     -uuid - -(!) - - -legal_entity_id -     -uuid - -(!) - - -inn     -varchar - -(!) - - -name     -varchar - -(!) - + + + +       ex_snapshot_member        + + +ex_snapshot_id +     +uuid + +(!) + + +legal_entity_id +     +uuid + +(!) + + +inn     +varchar + +(!) + + +name     +varchar + +(!) + @@ -625,91 +637,91 @@ ex_snapshot_member:e->legal_entity:w - - -1 -* + + +1 +* ex_snapshot_member:e->ex_snapshot:w - - -1 -* + + +1 +* ex_snapshot_item - - - - -       ex_snapshot_item        - - -id -     -uuid - - -ex_snapshot_id     -uuid - -(!) - - -item_type     -varchar - -(!) - - -legal_entity_id     -uuid - - -stored_file_id     -uuid - - -payload     -jsonb - - -payload_schema_version     -varchar - + + + + +       ex_snapshot_item        + + +id +     +uuid + + +ex_snapshot_id     +uuid + +(!) + + +item_type     +varchar + +(!) + + +legal_entity_id     +uuid + + +document_id     +uuid + + +payload     +jsonb + + +payload_schema_version     +varchar + + + + +ex_snapshot_item:e->document:w + + +1 +* + ex_snapshot_item:e->legal_entity:w - - -1 -* - - - - -ex_snapshot_item:e->stored_file:w - - -1 -* + + +1 +* ex_snapshot_item:e->ex_snapshot:w - - -1 -* + + +1 +* diff --git a/openspec/config.yaml b/openspec/config.yaml index e4280cf..092407e 100644 --- a/openspec/config.yaml +++ b/openspec/config.yaml @@ -1,12 +1,12 @@ schema: spec-driven context: | - Репозиторий arm-ka-docs — контекст, модель данных (DBML v5.14), правила домена, контракты между сервисами + Репозиторий arm-ka-docs — контекст, модель данных (DBML v5.17), правила домена, контракты между сервисами (docs/contracts/*.schema.json), макет UI и экспорт ТЗ системы АРМ КА (ДКБ, Россельхозбанк). Здесь оформляются СКВОЗНЫЕ изменения: контракт + окружение разработки (docs/dev) + проверки качества (make check) + изменения документов контекста. Код сервисов живёт в соседних репозиториях (../arm-core, ../tabular-parser, ../scan-gateway), у каждого свой openspec/ и свой change; сквозной change ссылается на них. - Источники истины по приоритету: docs/model/data-model-v5.14.dbml → docs/model/entities-v5.14.md → + Источники истины по приоритету: docs/model/data-model-v5.17.dbml → docs/model/entities-v5.17.md → docs/context/05-domain-rules.md → docs/source/tz (черновик, имена v4). Открытые вопросы — docs/context/07-open-questions.md; план срезов — docs/context/09-delivery-plan.md; соглашения шаблона команды — docs/context/03-architecture.md. Язык документов и коммитов — русский, идентификаторы — английские, как в DBML. docs/source/** не редактируется. diff --git a/openspec/specs/quality-checks/spec.md b/openspec/specs/quality-checks/spec.md index c22fa18..ec744ff 100644 --- a/openspec/specs/quality-checks/spec.md +++ b/openspec/specs/quality-checks/spec.md @@ -25,7 +25,7 @@ Docker, PostgreSQL или NATS. Каждый сервисный репозиторий SHALL предоставлять цель `make check-full`: `make check` плюс `pytest -m integration` против инфраструктуры `docs/dev/docker-compose.yml` (PostgreSQL 16, NATS, MinIO, для `scan-gateway` — эмулятор). Для `arm-core` цель SHALL дополнительно выполнять «`alembic upgrade head` → `alembic downgrade base` → `alembic upgrade head`» на чистой -БД и `python scripts/check_dbml_vs_orm.py` против `../docs/model/data-model-v5.13.dbml`. `make check-full` SHALL быть +БД и `python scripts/check_dbml_vs_orm.py` против текущей версии модели (`../docs/model/data-model-v<текущая>.dbml`, путь — переменная `DBML` Makefile). `make check-full` SHALL быть выполнен перед созданием PR, его итог вписывается в описание PR. #### Scenario: Расхождение ORM и DBML diff --git a/scripts/gen_model_diagrams.py b/scripts/gen_model_diagrams.py new file mode 100644 index 0000000..39116d8 --- /dev/null +++ b/scripts/gen_model_diagrams.py @@ -0,0 +1,145 @@ +"""Диаграммы модели данных по частям: DBML → 9 частичных DBML → SVG (dbml-renderer) → PNG (resvg). + +Запуск из корня репозитория: `make diagrams` (или `python3 scripts/gen_model_diagrams.py model/data-model-v5.17.dbml`). +Нужны Node.js и npx: пакеты рендера ставятся через `npx -y` с закреплёнными версиями (см. RENDERER, RASTERIZER). + +Каждая часть — таблицы своей области плюс заглушки STUB (только `id`) для связей с таблицами других частей; в частичный +DBML копируются шапка «Ключи», решения DECISION-* и используемые enum'ы. Раскладка частей — как в +`context/04-data-model-guide.md`, «Девять частей модели»; новая таблица DBML, не попавшая ни в одну часть, — ошибка. +Промежуточные частичные DBML пишутся во временный каталог (с `--keep-parts DIR` — в DIR). +""" + +import argparse +import pathlib +import re +import subprocess +import sys +import tempfile + +RENDERER = "@softwaretechnik/dbml-renderer@1.0.31" +RASTERIZER = "@resvg/resvg-js-cli@2.6.2-beta.1" +PNG_WIDTH = "2200" + +PARTS = { + "01_groups_entities": ( + "ГК, ГСК/ГСЗ, организации, филиалы", + ["regional_office", "company_group", "company_group_history", "legal_entity", "legal_entity_history", "legal_entity_alias", "related_group", "related_group_member"], + ), + "02_users_access": ("Пользователи и роли", ["app_user", "role", "permission", "role_permission", "user_role"]), + "03_accounting_refs": ( + "План счетов, правила элиминации, обязательные документы", + ["chart_of_account", "elimination_rule", "elimination_rule_account", "required_document_rule"], + ), + "04_files_jobs": ( + "Файлы, документы, S3, задания распознавания (NATS)", + ["s3_object", "stored_file", "document", "recognition_job"], + ), + "05_osv_parsed": ("Разобранные ОСВ", ["osv_entry"]), + "06_statements": ( + "Распознанные формы ББ/ОФР и логические проверки", + ["financial_statement_meta", "financial_statement_column", "financial_statement_line", "financial_statement_value"], + ), + "07_consolidation": ( + "Консолидация: версии, статусы, периметр, документы, блокировка", + ["consolidation", "consolidation_version", "consolidation_status_history", "consolidation_lock", "consolidation_member", "consolidation_document"], + ), + "08_corrections_kfo": ( + "Корректировки, гудвилл, результат КФО, подтверждение КФО клиента", + ["correction", "goodwill", "kfo_result", "kfo_result_line", "kfo_result_line_company", "client_kfo_line"], + ), + "09_audit_ex": ("Комментарии, аудит, ЕХФД", ["comment", "audit_event", "ex_snapshot", "ex_snapshot_member", "ex_snapshot_item"]), +} + +LEGEND = ( + "// Над каждой таблицей — блок «ИСТОЧНИК»: ТЗ АРМ КА BIQ-34 / ЕХ ТЗ BIQ-34649 / Методика; страница аналитики (md);\n" + "// экран макета dkb_24.09.26; версия модели, в которой таблица появилась (v4 = исходная DBML аналитики, v5+ = добавлено по ТЗ/макету).\n" + "// Правило v5.3+: в модели только сущности из ТЗ или с экрана макета, плюс инфраструктура обмена (s3_object, recognition_job)." +) + + +def parse_items(src: str) -> dict[str, tuple[str, str]]: + """Блоки `Enum`/`Table` с комментариями над ними: имя → (вид, текст блока).""" + items = {} + for m in re.finditer(r"^(Enum|Table)\s+(\w+)\s*\{", src, re.MULTILINE): + i, depth = m.end(), 1 + while depth: + c = src[i] + depth += (c == "{") - (c == "}") + i += 1 + before = src[: m.start()].rstrip("\n").split("\n") + comments = [] + while before and before[-1].startswith("//"): + comments.insert(0, before.pop()) + items[m.group(2)] = (m.group(1), ("\n".join(comments) + "\n" if comments else "") + src[m.start() : i]) + return items + + +def split_parts(src: str, src_name: str, version: str) -> dict[str, str]: + """Текст частичного DBML для каждой части.""" + items = parse_items(src) + enums = {k: v[1] for k, v in items.items() if v[0] == "Enum"} + tables = {k: v[1] for k, v in items.items() if v[0] == "Table"} + assigned = [t for _, ts in PARTS.values() for t in ts] + if set(tables) != set(assigned): + sys.exit(f"Раскладка частей не совпадает с таблицами DBML (поправить PARTS): {sorted(set(tables) ^ set(assigned))}") + owner = {t: p for p, (_, ts) in PARTS.items() for t in ts} + decisions = src[src.index("// DECISION-1") : src.index("// ============ ENUMS")].strip() + conventions = src[src.index("// Ключи:") : src.index("// DECISION-1")].strip() + index = "\n".join(f"// part{p}: {t}" for p, (t, _) in PARTS.items()) + out = {} + for pname, (title, ts) in PARTS.items(): + body = "\n\n".join(tables[t] for t in ts) + used_enums = [e for e in enums if re.search(r"\b" + e + r"\b", body)] + refs = set(re.findall(r"ref:\s*[<>-]\s*(\w+)\.", body)) + stubs = sorted(r for r in refs if r not in ts) + stub = "\n\n".join( + f"Table {r} {{\n id uuid [pk]\n Note: 'STUB — полное определение в part{owner[r]}'\n}}" for r in stubs + ) + out[pname] = ( + f"// ДКБ · Модель данных {version} · part{pname}: {title}\n" + f"// Часть общей модели {src_name} (единый источник истины; при правках менять обе).\n" + f"// Части:\n{index}\n" + "// Таблицы STUB ниже — заглушки (только id) для связей; их определения — в указанной части.\n" + + LEGEND + "\n//\n" + conventions + "\n//\n" + decisions + + "\n\n// ============ ENUMS ============\n" + "\n\n".join(enums[e] for e in used_enums) + + ("\n\n// ============ STUBS (таблицы других частей) ============\n" + stub if stubs else "") + + "\n\n// ============ TABLES ============\n" + body + "\n" + ) + print(f"part{pname}: таблиц {len(ts)}, заглушек {len(stubs)}") + return out + + +def render(part_dbml: pathlib.Path, svg: pathlib.Path, png: pathlib.Path) -> None: + subprocess.run(["npx", "-y", RENDERER, "-i", str(part_dbml), "-o", str(svg)], check=True) + subprocess.run( + ["npx", "-y", RASTERIZER, "--fit-width", PNG_WIDTH, "--background", "white", str(svg), str(png)], + check=True, + stdout=subprocess.DEVNULL, + ) + + +def main() -> None: + parser = argparse.ArgumentParser(description=__doc__.splitlines()[0]) + parser.add_argument("dbml", type=pathlib.Path, help="общая модель, например model/data-model-v5.17.dbml") + parser.add_argument("--out", type=pathlib.Path, default=pathlib.Path("model/diagrams"), help="каталог SVG/PNG") + parser.add_argument("--keep-parts", type=pathlib.Path, help="сохранить частичные DBML в этот каталог") + args = parser.parse_args() + + m = re.search(r"data-model-(v[\d.]+)\.dbml$", args.dbml.name) + if not m: + sys.exit(f"Имя файла модели должно быть data-model-v<версия>.dbml: {args.dbml.name}") + parts = split_parts(args.dbml.read_text(encoding="utf-8"), args.dbml.name, m.group(1)) + + args.out.mkdir(parents=True, exist_ok=True) + with tempfile.TemporaryDirectory() as tmp: + parts_dir = args.keep_parts or pathlib.Path(tmp) + parts_dir.mkdir(parents=True, exist_ok=True) + for pname, text in parts.items(): + part_dbml = parts_dir / f"data-model-{m.group(1)}-part{pname}.dbml" + part_dbml.write_text(text, encoding="utf-8") + render(part_dbml, args.out / f"part{pname}.svg", args.out / f"part{pname}.png") + print(f" → {args.out}/part{pname}.svg, .png") + + +if __name__ == "__main__": + main()