§9.4–9.5 и гайд фронта: повтор обработки и уточнение шапки ОСВ (AURORA-1442) #157
@ -458,3 +458,12 @@ JetStream** (3 subject `files.*` — как в старом АРМ, решени
|
|||||||
`expires_at` → `status ready|timeout|error` + `error_code`, без ack `processing`, без `Nats-Msg-Id`, без queue group
|
`expires_at` → `status ready|timeout|error` + `error_code`, без ack `processing`, без `Nats-Msg-Id`, без queue group
|
||||||
(один экземпляр), дедуп только в памяти процесса — доставка at-most-once. Это параллельный сервис старого АРМ, а не
|
(один экземпляр), дедуп только в памяти процесса — доставка at-most-once. Это параллельный сервис старого АРМ, а не
|
||||||
замена scan-gateway; из него берём только инфраструктурные решения (как и из `reference-old-arm`).
|
замена scan-gateway; из него берём только инфраструктурные решения (как и из `reference-old-arm`).
|
||||||
|
|
||||||
|
### Ручной повтор и уточнение ОСВ (AURORA-1442)
|
||||||
|
|
||||||
|
Повтор `tabular` после `TIMEOUT`/`NO_ACK` создаёт новое `recognition_job` (`attempt + 1`) и публикует его через
|
||||||
|
существующий outbox; прежнее задание и исходник в S3 сохраняются, аудит — `FILE_REPROCESS` (v5.21). Пакет версии
|
||||||
|
рассматривает только файлы в `ERROR` консолидации версии с ЮЛ из периметра. Реализация — `services/file_retry.py`.
|
||||||
|
`services/osv_header.py` уточняет период и счёт ОСВ на месте с `document.original_values` (v5.20), снимает
|
||||||
|
подтверждение и привязки к версиям «в работе», пишет `RECOGNIZED_DATA_EDIT`. Правила — `05-domain-rules.md` §9.4–9.5
|
||||||
|
п. 12–13, контракт — `dev/frontend-api-guide.md` §5.5. Миграции — 0008 (v5.20) и 0009 (v5.21) отдельно.
|
||||||
|
|||||||
@ -849,7 +849,10 @@ INITIATED; в Срезе 2c консолидаций ещё нет, поэтом
|
|||||||
`info`) хранятся в `document_issue` документа ОСВ и отдаются в `DocumentOut.issues`.
|
`info`) хранятся в `document_issue` документа ОСВ и отдаются в `DocumentOut.issues`.
|
||||||
|
|
||||||
### 9.4 Задание (`recognition_status`): queued → published → processing → done | needs_check | error | timeout → dlq;
|
### 9.4 Задание (`recognition_status`): queued → published → processing → done | needs_check | error | timeout → dlq;
|
||||||
replay: dlq → queued (`attempt + 1`); по кнопке пользователя — повторная обработка файла (§9.5 п. 13). `max_attempts`: OCR 1, парсер 3; `reply_deadline` OCR 20 мин (решение владельца 09.10.2026; note DBML «5 мин» — OQ-159), парсер 5 мин.
|
автоповтор публикации без ack (`OUTBOX_MAX_REPUBLISH`) — то же задание, `attempt` не меняется, счётчик —
|
||||||
|
`metrics.republish_count`, после исчерпания — `dlq` (`NO_ACK`); ручной повтор по кнопке пользователя — **новое** задание
|
||||||
|
с `attempt + 1`, прежнее остаётся в истории (§9.5 п. 13). Note DBML у `recognition_job` («replay = сброс в queued с
|
||||||
|
attempt+1») устарела — исправить в следующей версии DBML. `max_attempts`: OCR 1, парсер 3; `reply_deadline` OCR 20 мин (решение владельца 09.10.2026; note DBML «5 мин» — OQ-159), парсер 5 мин.
|
||||||
|
|
||||||
### 9.5 Первичные документы: загрузка, маппинг, замена, удаление, подтверждение (Срез 2c)
|
### 9.5 Первичные документы: загрузка, маппинг, замена, удаление, подтверждение (Срез 2c)
|
||||||
|
|
||||||
@ -1081,8 +1084,9 @@ replay: dlq → queued (`attempt + 1`); по кнопке пользовател
|
|||||||
Пересчитывается то, что выводится из периода и счёта: подходящесть версиям, чек-лист, колонка «ОСВ» (чистые функции,
|
Пересчитывается то, что выводится из периода и счёта: подходящесть версиям, чек-лист, колонка «ОСВ» (чистые функции,
|
||||||
отдельно не хранятся); `period_check` и `counterparty_check` не меняются (§9.3). Автозамена при правке не запускается
|
отдельно не хранятся); `period_check` и `counterparty_check` не меняются (§9.3). Автозамена при правке не запускается
|
||||||
(только при маппинге, п. 3): документ с ключом, как у другого файла ЮЛ, тот файл не заменяет; при привязке документ с
|
(только при маппинге, п. 3): документ с ключом, как у другого файла ЮЛ, тот файл не заменяет; при привязке документ с
|
||||||
тем же ключом в версии заменяется (§3 п. 1). Перезагрузка файла создаёт документы заново из результата — уточнения
|
тем же ключом в версии заменяется (§3 п. 1). Ключ документа после правки — исправленные период и счёт: при маппинге
|
||||||
прежних документов не переносятся.
|
следующего файла ЮЛ автозамена (п. 3) сравнивает уже их, а не распознанные значения. Перезагрузка файла создаёт
|
||||||
|
документы заново из результата — уточнения прежних документов не переносятся.
|
||||||
Аудит — одно `audit_event(RECOGNIZED_DATA_EDIT)` (`object_type = document`): в `before/after` изменённые поля, при
|
Аудит — одно `audit_event(RECOGNIZED_DATA_EDIT)` (`object_type = document`): в `before/after` изменённые поля, при
|
||||||
сброшенном подтверждении — `status`, `confirmed_by`, `confirmed_at`, в `after_value.unbound_version_ids` — версии, из
|
сброшенном подтверждении — `status`, `confirmed_by`, `confirmed_at`, в `after_value.unbound_version_ids` — версии, из
|
||||||
которых снята привязка. Запрос без изменений — 200 без аудита. Порядок блокировок — п. 7 (файл → документ → версии по
|
которых снята привязка. Запрос без изменений — 200 без аудита. Порядок блокировок — п. 7 (файл → документ → версии по
|
||||||
@ -1095,8 +1099,17 @@ replay: dlq → queued (`attempt + 1`); по кнопке пользовател
|
|||||||
задания нет ни документов, ни подтверждений, ни правок — переносить и сбрасывать нечего. Ядро создаёт новое
|
задания нет ни документов, ни подтверждений, ни правок — переносить и сбрасывать нечего. Ядро создаёт новое
|
||||||
`recognition_job` с `attempt + 1` (уникальность `(file_id, attempt)`), делает его текущим и публикует через outbox;
|
`recognition_job` с `attempt + 1` (уникальность `(file_id, attempt)`), делает его текущим и публикует через outbox;
|
||||||
файл снова уходит в обработку (§9.2). Прежнее задание остаётся в истории. Предел — `max_attempts` задания (у парсера
|
файл снова уходит в обработку (§9.2). Прежнее задание остаётся в истории. Предел — `max_attempts` задания (у парсера
|
||||||
3): на нём — отказ. В остальных случаях — 409. Права — `DOCUMENT_EDIT` и policy по ЮЛ. **Пакет версии** — то же
|
3): на нём — 409 `MAX_ATTEMPTS_REACHED`; не подходит под условие — 409 `FILE_NOT_RETRYABLE`. Права — `DOCUMENT_EDIT`
|
||||||
одним запросом для всех файлов версии, подходящих под условие; версия после `VERIFIED` read-only — запрет.
|
и policy по ЮЛ. Ответ — 202 и карточка файла с новым заданием. Файл, загруженный в консолидацию
|
||||||
|
(`stored_file.consolidation_id`), повторяется, только если у этой консолидации есть версия «в работе» с ЮЛ файла в
|
||||||
|
периметре, иначе 409 `VERSION_NOT_EDITABLE`; файл без контекста — без этого условия.
|
||||||
|
**Пакет версии** (`POST /consolidations/versions/{id}/files/retry`) — то же одним запросом. Версия должна быть «в работе»
|
||||||
|
(после `VERIFIED` и `CANCELLED` — 409 `VERSION_NOT_EDITABLE`); блокировка версии (`consolidation_lock`) не нужна.
|
||||||
|
Рассматриваются файлы в `ERROR`, загруженные в консолидацию этой версии (`stored_file.consolidation_id`), ЮЛ которых
|
||||||
|
в периметре версии; файлы без контекста и файлы других статусов в пакет не входят и не блокируются. Ответ — 202
|
||||||
|
`{accepted[] {file_id, job_id, attempt}, skipped[] {file_id, code}}`: в `skipped` — рассмотренные файлы, которые
|
||||||
|
повторить нельзя, с кодом `FILE_NOT_RETRYABLE` или `MAX_ATTEMPTS_REACHED`; пустой пакет — 202 с пустыми списками.
|
||||||
|
Статус версии и периметр перепроверяются под блокировкой.
|
||||||
Аудит — `audit_event(FILE_REPROCESS)` на каждый файл, отправленный на повтор (раздел `PRIMARY_DOCUMENTS`,
|
Аудит — `audit_event(FILE_REPROCESS)` на каждый файл, отправленный на повтор (раздел `PRIMARY_DOCUMENTS`,
|
||||||
`object_type = stored_file`, `object_id` — файл, `user_id` — инициатор): `before_value` — прежнее задание (`job_id`,
|
`object_type = stored_file`, `object_id` — файл, `user_id` — инициатор): `before_value` — прежнее задание (`job_id`,
|
||||||
`attempt`, `status`, `error_code`), `after_value` — новое (`job_id`, `attempt`); при обработке пакета — ещё
|
`attempt`, `status`, `error_code`), `after_value` — новое (`job_id`, `attempt`); при обработке пакета — ещё
|
||||||
|
|||||||
@ -333,8 +333,11 @@ UI прячет элементы по `GET /me → permissions` (объедин
|
|||||||
| `GET /consolidations/{consolidation_id}/legal-entities/{legal_entity_id}/files` | VIEW | `limit`, `offset`, `include_superseded` | `Page[FileOut]` — **рабочий стол консоли** для ЮЛ: файлы с этим `consolidation_id` плюс файлы, чьи документы привязаны к любой версии консолидации; новые первыми; `SUPERSEDED` — только при `include_superseded=true` (скрыт по умолчанию) | 404 |
|
| `GET /consolidations/{consolidation_id}/legal-entities/{legal_entity_id}/files` | VIEW | `limit`, `offset`, `include_superseded` | `Page[FileOut]` — **рабочий стол консоли** для ЮЛ: файлы с этим `consolidation_id` плюс файлы, чьи документы привязаны к любой версии консолидации; новые первыми; `SUPERSEDED` — только при `include_superseded=true` (скрыт по умолчанию) | 404 |
|
||||||
| `GET /files/{file_id}` | VIEW | — | `FileOut` | — |
|
| `GET /files/{file_id}` | VIEW | — | `FileOut` | — |
|
||||||
| `DELETE /files/{file_id}` | EDIT | — | 204 (удаляет документы, строки ОСВ, формы ББ/ОФР, комментарии, задания) | 409 `FILE_HAS_CONFIRMED_DOCUMENTS`, 409 `FILE_SUPERSEDES_OTHERS` |
|
| `DELETE /files/{file_id}` | EDIT | — | 204 (удаляет документы, строки ОСВ, формы ББ/ОФР, комментарии, задания) | 409 `FILE_HAS_CONFIRMED_DOCUMENTS`, 409 `FILE_SUPERSEDES_OTHERS` |
|
||||||
|
| `POST /files/{file_id}/retry` | EDIT | — | 202 `FileOut` — новое задание (`job.attempt + 1`), файл снова `UPLOADED` | 409 `FILE_NOT_RETRYABLE`, `MAX_ATTEMPTS_REACHED`; для файла консолидации — 409 `VERSION_NOT_EDITABLE` (нет версии «в работе» с его ЮЛ) |
|
||||||
|
| `POST /consolidations/versions/{version_id}/files/retry` | EDIT | — | 202 `RetryBatchOut {accepted[] {file_id, job_id, attempt}, skipped[] {file_id, code}}` | 409 `VERSION_NOT_EDITABLE` (версия не «в работе») |
|
||||||
| `GET /files/{file_id}/content` | VIEW | `disposition` = `attachment` (по умолчанию) / `inline` | поток байт; `Content-Type` по типу файла | 404 `FILE_CONTENT_NOT_FOUND` (в БД есть, в хранилище нет), 503 `STORAGE_UNAVAILABLE` |
|
| `GET /files/{file_id}/content` | VIEW | `disposition` = `attachment` (по умолчанию) / `inline` | поток байт; `Content-Type` по типу файла | 404 `FILE_CONTENT_NOT_FOUND` (в БД есть, в хранилище нет), 503 `STORAGE_UNAVAILABLE` |
|
||||||
| `GET /documents/{document_id}` | VIEW | — | `DocumentOut` | — |
|
| `GET /documents/{document_id}` | VIEW | — | `DocumentOut` | — |
|
||||||
|
| `PATCH /documents/{document_id}/osv-header` | EDIT | любые из `{period_year, period_quarter, is_cumulative, osv_account_code}`; отсутствующее поле не меняется | `DocumentOut` | по порядку: 409 `DOCUMENT_NOT_OSV`, `FILE_SUPERSEDED`; 422 `INVALID_PERIOD`, `INVALID_ACCOUNT_CODE`; 409 `DUPLICATE_ACCOUNT` (`document_id`), `VERSION_NOT_EDITABLE` (`version_ids`); 422 массивом — неверный тип поля |
|
||||||
| `GET /documents/{document_id}/entries` | VIEW | `limit` (≤ 2000, 500), `offset` | `Page[EntryOut]` по `row_number`; дерево — по `parent_entry_id` | — |
|
| `GET /documents/{document_id}/entries` | VIEW | `limit` (≤ 2000, 500), `offset` | `Page[EntryOut]` по `row_number`; дерево — по `parent_entry_id` | — |
|
||||||
| `POST /documents/{document_id}/confirm` | EDIT | — | `DocumentOut` | 409 `FILE_SUPERSEDED`, `ALREADY_CONFIRMED`, `DOCUMENT_NOT_CONFIRMABLE`; для ББ/ОФР — `PERIOD_REQUIRED`, `CHECKS_NOT_PASSED` (`failures[]`) — именно в этом порядке, если совпало несколько |
|
| `POST /documents/{document_id}/confirm` | EDIT | — | `DocumentOut` | 409 `FILE_SUPERSEDED`, `ALREADY_CONFIRMED`, `DOCUMENT_NOT_CONFIRMABLE`; для ББ/ОФР — `PERIOD_REQUIRED`, `CHECKS_NOT_PASSED` (`failures[]`) — именно в этом порядке, если совпало несколько |
|
||||||
| `GET /documents/{document_id}/comments` | VIEW | — | `CommentOut[] {id, text, author {id, full_name}, created_at}` по возрастанию | — |
|
| `GET /documents/{document_id}/comments` | VIEW | — | `CommentOut[] {id, text, author {id, full_name}, created_at}` по возрастанию | — |
|
||||||
@ -379,9 +382,36 @@ UI прячет элементы по `GET /me → permissions` (объедин
|
|||||||
`period_quarter = null` — «период не определён» (например, ОСВ со скользящим периодом) — не подставлять квартал самим.
|
`period_quarter = null` — «период не определён» (например, ОСВ со скользящим периодом) — не подставлять квартал самим.
|
||||||
- Проверки: `counterparty_check` — сверка шапки с ЮЛ-владельцем; `period_check` **всегда `UNKNOWN`** — показывать «—»
|
- Проверки: `counterparty_check` — сверка шапки с ЮЛ-владельцем; `period_check` **всегда `UNKNOWN`** — показывать «—»
|
||||||
(OQ-120). Подходит ли документ версии — только по чек-листу версии.
|
(OQ-120). Подходит ли документ версии — только по чек-листу версии.
|
||||||
- Подтверждённый документ необратим (отмены нет); исправление — загрузкой нового файла (автозамена).
|
- Подтверждённый документ отменить нельзя; исправление — загрузкой нового файла (автозамена). Исключение — уточнение
|
||||||
|
периода и счёта ОСВ (ниже): оно снимает подтверждение.
|
||||||
- Строки ОСВ (`entries`) — только чтение; экрана правки ОСВ в макете нет.
|
- Строки ОСВ (`entries`) — только чтение; экрана правки ОСВ в макете нет.
|
||||||
|
|
||||||
|
Повторная обработка (`…/retry`, AURORA-1442, правила — `05-domain-rules.md` §9.5 п. 13):
|
||||||
|
|
||||||
|
- Доступна только для файла в `ERROR`, у которого текущее задание табличного парсера (`job.pipeline = tabular`) упало на
|
||||||
|
обмене: `job.status = timeout` (`error_code = TIMEOUT`) или `dlq` (`NO_ACK`). PDF/сканы (OCR) — нет. Кнопку
|
||||||
|
«Повторно обработать» показывать только при этом условии; остальное — 409 `FILE_NOT_RETRYABLE`.
|
||||||
|
- Попыток у парсера 3 (`job.max_attempts`); на последней — 409 `MAX_ATTEMPTS_REACHED`, кнопку прятать по
|
||||||
|
`job.attempt >= job.max_attempts`.
|
||||||
|
- Ответ 202: файл снова `UPLOADED` с новым заданием — опрашивать как после загрузки. Прежнее задание остаётся в истории.
|
||||||
|
- Файл, загруженный в консолидацию (`consolidation_id` не `null`), повторяется, только пока есть версия «в работе» с его ЮЛ
|
||||||
|
в периметре (иначе 409 `VERSION_NOT_EDITABLE`); файл без контекста — без этого условия.
|
||||||
|
- Пакет версии: рассматриваются файлы в `ERROR`, загруженные в консолидацию этой версии, ЮЛ которых в её периметре.
|
||||||
|
Подошедшие — в `accepted`, остальные файлы в `ERROR` — в `skipped` с кодом `FILE_NOT_RETRYABLE` или
|
||||||
|
`MAX_ATTEMPTS_REACHED`. Пустой пакет — 202 с двумя пустыми списками. Версия не «в работе» — 409 `VERSION_NOT_EDITABLE`.
|
||||||
|
Блокировка версии не нужна.
|
||||||
|
- В журнал пишется `FILE_REPROCESS` на каждый принятый файл.
|
||||||
|
|
||||||
|
Уточнение периода и счёта ОСВ (`PATCH …/osv-header`, правила — §9.5 п. 12):
|
||||||
|
|
||||||
|
- Только документ `OSV`. Если передано хоть одно поле периода, после правки все три должны быть заполнены, у `Q1` —
|
||||||
|
`is_cumulative = true` (иначе 422 `INVALID_PERIOD`). Счёт нормализуется как при разборе (`60.1` → `60.01`).
|
||||||
|
- Новые поля `DocumentOut`: `is_modified` (текущие значения отличаются от распознанных), `original_values` (распознанные
|
||||||
|
`period_year`, `period_quarter`, `is_cumulative`, `osv_account_code` — заполняется при первой правке и дальше не
|
||||||
|
меняется), `modified_by`, `modified_at`. Возврат к исходным значениям снимает `is_modified`.
|
||||||
|
- Подтверждённый документ после правки — `NEEDS_CHECK`, привязки к версиям «в работе» сняты; подтверждать заново вручную.
|
||||||
|
- Запрос без изменений — 200 без записи в журнал. Строки ОСВ не меняются.
|
||||||
|
|
||||||
### 5.6 Формы ББ/ОФР
|
### 5.6 Формы ББ/ОФР
|
||||||
|
|
||||||
| Метод и путь | Право | Запрос | Ответ | Ошибки |
|
| Метод и путь | Право | Запрос | Ответ | Ошибки |
|
||||||
@ -751,7 +781,11 @@ async function download(path: string, fallbackName: string): Promise<void> {
|
|||||||
| `ENTITY_NOT_IN_PERIMETER` | 422 | загрузка в консолидацию | — | «ЮЛ не входит в периметр ни одной версии консолидации «в работе»» (`05-domain-rules.md` §9.5 п. 8) |
|
| `ENTITY_NOT_IN_PERIMETER` | 422 | загрузка в консолидацию | — | «ЮЛ не входит в периметр ни одной версии консолидации «в работе»» (`05-domain-rules.md` §9.5 п. 8) |
|
||||||
| `QUERY_TOO_SHORT` | 422 | кандидаты `scope=all` | — | «Введите не менее 3 символов» |
|
| `QUERY_TOO_SHORT` | 422 | кандидаты `scope=all` | — | «Введите не менее 3 символов» |
|
||||||
| `COMMENT_REQUIRED` | 422 | отмена версии | — | ошибка поля «Комментарий» |
|
| `COMMENT_REQUIRED` | 422 | отмена версии | — | ошибка поля «Комментарий» |
|
||||||
| `VERSION_NOT_EDITABLE` | 409 | правки версии, взятие `CANCELLED` | — | перевести экран в просмотр |
|
| `VERSION_NOT_EDITABLE` | 409 | правки версии, взятие `CANCELLED`; повтор обработки; уточнение шапки ОСВ | у шапки ОСВ — `version_ids` | перевести экран в просмотр |
|
||||||
|
| `FILE_NOT_RETRYABLE`, `MAX_ATTEMPTS_REACHED` | 409 / в `skipped[]` | повторная обработка | — | спрятать кнопку повтора, показать причину |
|
||||||
|
| `DOCUMENT_NOT_OSV` | 409 | уточнение шапки ОСВ | — | правка только для ОСВ |
|
||||||
|
| `INVALID_PERIOD`, `INVALID_ACCOUNT_CODE` | 422 | уточнение шапки ОСВ | — | ошибка поля |
|
||||||
|
| `DUPLICATE_ACCOUNT` | 409 | уточнение шапки ОСВ: счёт уже есть у другой ОСВ файла | `document_id` | ссылка на документ с этим счётом |
|
||||||
| `CONSOLIDATION_NOT_EDITABLE` | 409 | загрузка в консолидацию, у которой нет версии «в работе» (`POST /consolidations/{cid}/legal-entities/{le}/files`) | — | заблокировать кнопку загрузки |
|
| `CONSOLIDATION_NOT_EDITABLE` | 409 | загрузка в консолидацию, у которой нет версии «в работе» (`POST /consolidations/{cid}/legal-entities/{le}/files`) | — | заблокировать кнопку загрузки |
|
||||||
| `LOCK_REQUIRED` | 409 | правки версии | — | «Возьмите версию в работу» |
|
| `LOCK_REQUIRED` | 409 | правки версии | — | «Возьмите версию в работу» |
|
||||||
| `LOCKED_BY_OTHER` | 409 | взятие и правки версии | `locked_by`, `locked_at`, из `POST …/lock` ещё `locked_by_name` | «Версию держит <имя>», предложить перехват |
|
| `LOCKED_BY_OTHER` | 409 | взятие и правки версии | `locked_by`, `locked_at`, из `POST …/lock` ещё `locked_by_name` | «Версию держит <имя>», предложить перехват |
|
||||||
@ -789,6 +823,7 @@ async function download(path: string, fallbackName: string): Promise<void> {
|
|||||||
|---|---|---|
|
|---|---|---|
|
||||||
| OQ-54 — SSO РАИСА | Меняется только получение токена (вместо dev-формы) | держать слой получения токена изолированным (`frontend-auth-guide.md` §9) |
|
| OQ-54 — SSO РАИСА | Меняется только получение токена (вместо dev-формы) | держать слой получения токена изолированным (`frontend-auth-guide.md` §9) |
|
||||||
| arm-ka-docs#116 — единая нормализация ББ/ОФР | Выполнено в `arm-core` `main` (#129, #132) — см. §1 и §5.6.1; фронт (план 6) — в `arm-core/web`; выкладка сервисов на контракт 2.0 — `qa-statements-v2.md` | типы из §5.6.1 |
|
| arm-ka-docs#116 — единая нормализация ББ/ОФР | Выполнено в `arm-core` `main` (#129, #132) — см. §1 и §5.6.1; фронт (план 6) — в `arm-core/web`; выкладка сервисов на контракт 2.0 — `qa-statements-v2.md` | типы из §5.6.1 |
|
||||||
|
| arm-core#161 — повторная обработка файла и уточнение шапки ОСВ (AURORA-1442) | Маршруты и коды в §5.5 и §9 уже описаны; на `main` появятся после слияния PR | кнопки — AURORA-1679 (повтор), правка шапки ОСВ — отдельной задачей |
|
||||||
|
|
||||||
Состояние этих задач — в Gitea (`RSHB/arm-core`, `RSHB/arm-ka-docs`); после слияния каждой этот раздел переезжает в §1.
|
Состояние этих задач — в Gitea (`RSHB/arm-core`, `RSHB/arm-ka-docs`); после слияния каждой этот раздел переезжает в §1.
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user