DAL-слой chk: функции БД, спецификации, psql-тесты, репозитории (MT.Checks)

- DB/chk: 7 хранимых функций (p_*), func_specs, psql-тесты, статусы DEV/CODE
- MT.Checks: домен (ChkCheck, ChkType, ChkStatus), репозитории ChkCheck/ChkType/ChkStatus (Kit.Helpers, inline-конвертеры)
- 1-docs/2-core/chk/repository_tests: спецификации тестов репозиториев
- db/manifest.json, отчёт Plan/dal_report.md
This commit is contained in:
parent 01fa7f5e57
commit 939d44063e
41 changed files with 1784 additions and 16 deletions

7
.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
# Одноразовые артефакты прогона project_dal (c-skl-3) — в git не попадают
Plan/dal_state.json
Plan/env_passport.json
check_report.json
bin/
obj/

View File

@ -0,0 +1,87 @@
# Тесты `ChkCheckRepository` (слой Chk)
> Репозиторий `MT.Check.Core.Dal.Chk.Repository`: IChkCheckRepository / ChkCheckRepository
> (правила — c-dal; концепция unit-тестов без БД — c-tst; генерация — d-tst).
> Домен: `MT.Check.Core.Dal.Chk.Domain.ChkCheck` (Id, CheckDate, TypeId, Type,
> StatusId, Status, SmevMessageId, RrOrganizationId, Params, ParentCheckId).
## Методы → функции БД
| Метод | Транспорт | Функция БД |
|---|---|---|
| `Insert(ChkCheck): int` | `IConnectionString.PrepareExecute<int>().AsStoredProcedure().AddParameter*` + `ExecuteScalar()` | `chk.chk_check_insert` |
| `Update(ChkCheck): int` | то же + `ExecuteScalar()` | `chk.chk_check_update` |
| `Delete(int id): int` | то же + `ExecuteScalar()` | `chk.chk_check_delete` |
| `Select(ChkCheckFilter): IList<ChkCheck>` | `PrepareExecute<ChkCheck>().AddConverter(...)` + `ExecuteSelectMany()` | `chk.chk_check_select` |
| `Get(int id): ChkCheck?` | `PrepareExecute<ChkCheck>().AddConverter(...)` + `ExecuteSelectMany()` | `chk.chk_check_get` |
## Механика теста (общая)
- Транспорт подменяется фейком `ConnectionStringTest : IConnectionString` (d-tst-11):
конструктор принимает строгий набор ожидаемых параметров (`Dictionary<string, object?>`)
и таблицу фикстуры; `VerifyAll` — в конце каждого теста (d-tst-12).
- Одна таблица ответа = один конвертер (d-tst-11); слово «курсор» не используется (d-tst-22).
- Строгий набор параметров: имена `p_check_date`, `p_chk_type_id`, `p_chk_status_id`,
`p_smev_message_id`, `p_rr_organization_id`, `p_params`, `p_parent_check_id`, `p_id`
(c-dal-5, d-tst-13). Строки домена без NULL (c-dm-10): пустое значение → `""`.
- Update — полная запись объекта: сценарии «null = не менять» не проектируются (db-m-8, c-dal-17).
- Тестируем репозиторий, а не БД: SQL-семантика фильтров не воспроизводится (d-tst-5, d-tst-10).
- Счётчики — из фикстуры, без литералов (d-tst-23); тело теста по этапам
`init test env by fixtures``call repository method``check result` (c-tst-3).
## Заготовки (DataTable)
Таблица ответа `chk.chk_check_select` / `chk.chk_check_get` (одна на оба метода):
`id:int, check_date:DateTime, chk_type_id:int, chk_status_id:int,
smev_message_id:string, rr_organization_id:string, params:string, parent_check_id:int?`.
Строки-фикстуры:
- `c1`: id=101, check_date=2026-05-01, chk_type_id=1, chk_status_id=2,
smev_message_id='smev-101', rr_organization_id='rr-101',
params='{"org_count":5}', parent_check_id=NULL;
- `c2`: id=102, check_date=2026-05-01, chk_type_id=1, chk_status_id=3,
smev_message_id='', rr_organization_id='rr-102', params='', parent_check_id=101;
- `c3`: id=103, check_date=2026-05-02, chk_type_id=5, chk_status_id=4,
smev_message_id='smev-103', rr_organization_id='', params='', parent_check_id=NULL.
Скалярные заготовки: `chk.chk_check_insert` → 201 (id новой записи);
`chk.chk_check_update` → 1; `chk.chk_check_delete` → 1.
## Тесты: `Insert`
| № | Тип | Условие | Ожидаемый результат |
|---|---|---|---|
| 1 | основной | все поля объекта заполнены (строка c1 без Id) | вызов `chk.chk_check_insert`; строгий набор 7 параметров (`p_parent_check_id` = значение); возврат 201 из scalar-заготовки |
| 2 | граничный | необязательные поля пустые: строки `""`, `ParentCheckId = null` | строгий набор: `p_smev_message_id` = `""`, `p_params` = `""`, `p_parent_check_id` = NULL (`DBNull`) |
## Тесты: `Update`
| № | Тип | Условие | Ожидаемый результат |
|---|---|---|---|
| 1 | основной | полный объект (строка c2): Id=102 + все поля | вызов `chk.chk_check_update`; строгий набор 8 параметров (p_id + все поля); возврат 1 |
| 2 | исключение | объект с `Id = 0` | ArgumentException до транспорта; транспорт не вызывался |
## Тесты: `Delete`
| № | Тип | Условие | Ожидаемый результат |
|---|---|---|---|
| 1 | основной | Id = 103 | вызов `chk.chk_check_delete`; параметр `p_id` = 103; возврат 1 |
| 2 | исключение | Id = 0 | ArgumentException до транспорта; транспорт не вызывался |
## Тесты: `Select`
| № | Тип | Условие | Ожидаемый результат |
|---|---|---|---|
| 1 | основной | фильтр: CheckDate=2026-05-01, TypeId=1; таблица c1+c2+c3 | 2 сущности (по строкам c1, c2); строгий набор: `p_check_date`, `p_chk_type_id` заданы, `p_chk_status_id`/`p_rr_organization_id` = NULL |
| 2 | граничный | фильтр пустой (все поля null, c-dal-12: filter ??= new) | 3 сущности; все 4 параметра = NULL |
| 3 | основной | таблица c2+c3; пустой фильтр | поля сущностей сверены со строками (AssertEquals: id, check_date, chk_type_id, chk_status_id, smev_message_id, rr_organization_id, params, parent_check_id) |
| 4 | граничный | пустая таблица | пустая коллекция (не null) |
## Тесты: `Get`
| № | Тип | Условие | Ожидаемый результат |
|---|---|---|---|
| 1 | основной | Id = 101; таблица c1 | 1 сущность; строгий набор: `p_id` = 101; поля сверены со строкой c1; Type/Status-объекты не заполняются транспортом (null) |
| 2 | граничный | пустая таблица (id не найден) | null |
| 3 | исключение | Id = 0 | ArgumentException до транспорта; транспорт не вызывался |

View File

@ -0,0 +1,33 @@
# Тесты `ChkStatusRepository` (слой Chk)
> Репозиторий `MT.Check.Core.Dal.Chk.Repository`: IChkStatusRepository / ChkStatusRepository
> (правила — c-dal; концепция unit-тестов без БД — c-tst; генерация — d-tst).
> Системный справочник (фиксированные id) — только методы чтения (c-dal-2).
> Домен: `MT.Check.Core.Dal.Chk.Domain.ChkStatus` (Id, Name) + константы `ChkStatuses`.
## Методы → функции БД
| Метод | Транспорт | Функция БД |
|---|---|---|
| `Select(): IList<ChkStatus>` | `IConnectionString.PrepareExecute<ChkStatus>().AddConverter(...)` + `ExecuteSelectMany()` | `chk.chk_status_select` |
## Механика теста (общая)
- Транспорт — фейк `ConnectionStringTest : IConnectionString` (d-tst-11); `VerifyAll` в конце (d-tst-12).
- Одна таблица ответа = один конвертер (d-tst-11); «таблица», не «курсор» (d-tst-22).
- Функция без параметров: строгий набор — пустой словарь (d-tst-13).
- Счётчики — из фикстуры (d-tst-23); этапы теста — c-tst-3.
## Заготовки (DataTable)
Таблица ответа `chk.chk_status_select`: `id:int, name:string`.
Строки-фикстуры: `s1` (id=1, name='Запрос отправлен'),
`s4` (id=4, name='Ошибка при обработке').
## Тесты: `Select`
| № | Тип | Условие | Ожидаемый результат |
|---|---|---|---|
| 1 | основной | таблица s1+s4 | 2 сущности; строгий набор: параметров нет; поля сверены со строками (id, name) |
| 2 | граничный | пустая таблица | пустая коллекция (не null) |

View File

@ -0,0 +1,33 @@
# Тесты `ChkTypeRepository` (слой Chk)
> Репозиторий `MT.Check.Core.Dal.Chk.Repository`: IChkTypeRepository / ChkTypeRepository
> (правила — c-dal; концепция unit-тестов без БД — c-tst; генерация — d-tst).
> Системный справочник (фиксированные id) — только методы чтения (c-dal-2).
> Домен: `MT.Check.Core.Dal.Chk.Domain.ChkType` (Id, Name) + константы `ChkTypes`.
## Методы → функции БД
| Метод | Транспорт | Функция БД |
|---|---|---|
| `Select(): IList<ChkType>` | `IConnectionString.PrepareExecute<ChkType>().AddConverter(...)` + `ExecuteSelectMany()` | `chk.chk_type_select` |
## Механика теста (общая)
- Транспорт — фейк `ConnectionStringTest : IConnectionString` (d-tst-11); `VerifyAll` в конце (d-tst-12).
- Одна таблица ответа = один конвертер (d-tst-11); «таблица», не «курсор» (d-tst-22).
- Функция без параметров: строгий набор — пустой словарь (d-tst-13).
- Счётчики — из фикстуры (d-tst-23); этапы теста — c-tst-3.
## Заготовки (DataTable)
Таблица ответа `chk.chk_type_select`: `id:int, name:string`.
Строки-фикстуры: `t1` (id=1, name='Запрос изменений (дельты) ЕГРЮЛ'),
`t6` (id=6, name='Актуализация организации СОУТ').
## Тесты: `Select`
| № | Тип | Условие | Ожидаемый результат |
|---|---|---|---|
| 1 | основной | таблица t1+t6 | 2 сущности; строгий набор: параметров нет; поля сверены со строками (id, name) |
| 2 | граничный | пустая таблица | пустая коллекция (не null) |

View File

@ -0,0 +1,3 @@
1. имя проектов и namespaces
2. Коментарии несущие не нужную информацибю с одной стороно сдругой не дающие никаой инофрмации для дальнейшей поддрежки кода.
3. Не понятный репозитории RepositoryTransport.cs

View File

@ -0,0 +1,21 @@
# `chk.chk_check_delete`
Удаление проверки по `id`.
| Параметр | Тип | Обяз. | Описание |
|---|---|---|---|
| `p_id` | int | да | идентификатор проверки |
**Возвращает** `int` — число удалённых строк (db-m-3).
## Логика/особенности
- NULL `p_id``RAISE EXCEPTION` (db-m-4). Несуществующий `id` — не ошибка: возврат 0.
- Потомков у сущности нет; удаление родительской проверки, на которую ссылаются дочерние, даст исключение FK.
## Тесты
| № | Тип | Условие | Ожидаемый результат |
|---|---|---|---|
| 1 | основной сценарий | существующая проверка (фикстура `TEST-`) | возврат 1; записи в таблице нет |
| 2 | граничный случай | несуществующий `p_id` | возврат 0 |

View File

@ -0,0 +1,21 @@
# `chk.chk_check_get`
Выборка одной проверки по идентификатору.
| Параметр | Тип | Обяз. | Описание |
|---|---|---|---|
| `p_id` | int | да | идентификатор проверки |
**Возвращает** `TABLE` — колонки `chk_check`: `id int`, `check_date date`, `chk_type_id int`, `chk_status_id int`, `smev_message_id text`, `rr_organization_id text`, `params jsonb`, `parent_check_id int`; не более одной строки.
## Логика/особенности
- NULL `p_id``RAISE EXCEPTION` (db-m-4). Несуществующий `id` — не ошибка: пустая таблица.
## Тесты
| № | Тип | Условие | Ожидаемый результат |
|---|---|---|---|
| 1 | основной сценарий | существующая проверка (фикстура `TEST-`) | 1 строка; все колонки = значениям записи |
| 2 | граничный случай | несуществующий `p_id` | пустая таблица |
| 3 | исключение | NULL `p_id` | исключение «не задан p_id» |

View File

@ -0,0 +1,33 @@
# `chk.chk_check_insert`
Создание новой проверки организаций.
| Параметр | Тип | Обяз. | Описание |
|---|---|---|---|
| `p_check_date` | date | да | день (дата) проверки |
| `p_chk_type_id` | int | да | тип проверки (→ `chk.chk_type.id`) |
| `p_chk_status_id` | int | да | статус проверки (→ `chk.chk_status.id`) |
| `p_smev_message_id` | text | нет | идентификатор сообщения СМЭВ |
| `p_rr_organization_id` | text | нет | идентификатор организации РР |
| `p_params` | text | нет | параметры проверки (JSON); передаётся как `text`, каст в `jsonb` внутри (db-m-5) |
| `p_parent_check_id` | int | нет | родительская проверка (→ `chk.chk_check.id`) |
**Возвращает** `int` — id созданной проверки.
## Логика/особенности
- Обязательные параметры проверяются в начале функции: NULL → `RAISE EXCEPTION` (db-m-4).
- `p_params` — некорректный JSON → исключение приведения типа.
## Тесты
| № | Тип | Условие | Ожидаемый результат |
|---|---|---|---|
| 1 | основной сценарий | все поля заполнены (фикстура `TEST-`) | id > 0; поля записи совпадают с параметрами |
| 2 | граничный случай | только обязательные параметры | запись создана; необязательные поля NULL |
| 3 | граничный случай | `p_params` — валидный JSON | в записи `params` = переданный JSON |
| 4 | исключение | NULL `p_check_date` | исключение «не задан p_check_date» |
| 5 | исключение | NULL `p_chk_type_id` | исключение «не задан p_chk_type_id» |
| 6 | исключение | NULL `p_chk_status_id` | исключение «не задан p_chk_status_id» |
| 7 | исключение | несуществующий `p_chk_type_id` | исключение FOREIGN KEY |
| 8 | исключение | `p_params` — не JSON (`'не-json'`) | исключение приведения jsonb |

View File

@ -0,0 +1,28 @@
# `chk.chk_check_select`
Выборка проверок с необязательными фильтрами (NULL параметра = фильтр не применяется).
| Параметр | Тип | Обяз. | Описание |
|---|---|---|---|
| `p_check_date` | date | нет | фильтр по дню проверки; NULL = без фильтра |
| `p_chk_type_id` | int | нет | фильтр по типу проверки; NULL = без фильтра |
| `p_chk_status_id` | int | нет | фильтр по статусу проверки; NULL = без фильтра |
| `p_rr_organization_id` | text | нет | фильтр по организации РР; NULL = без фильтра |
**Возвращает** `TABLE` — колонки `chk_check`: `id int`, `check_date date`, `chk_type_id int`, `chk_status_id int`, `smev_message_id text`, `rr_organization_id text`, `params jsonb`, `parent_check_id int`; сортировка по `id`.
## Логика/особенности
- Фильтры реализованы шаблоном `(p_x IS NULL OR t.x = p_x)` (db-func-7); фильтры — на корневом объекте (db-s-8).
## Тесты
| № | Тип | Условие | Ожидаемый результат |
|---|---|---|---|
| 1 | основной сценарий | фикстура `TEST-` (3 проверки: разные дата/тип/статус/организация); все фильтры NULL | 3 строки фикстуры + существующие; все колонки заполнены |
| 2 | основной сценарий | фильтр по `p_check_date` (дата двух фикстур) | только проверки с этой датой |
| 3 | основной сценарий | фильтр по `p_chk_type_id` | только проверки этого типа |
| 4 | основной сценарий | фильтр по `p_chk_status_id` | только проверки этого статуса |
| 5 | основной сценарий | фильтр по `p_rr_organization_id` | только проверки этой организации |
| 6 | основной сценарий | комбинированный фильтр (дата + статус) | только строки, удовлетворяющие обоим условиям |
| 7 | граничный случай | фильтр без совпадений | пустая таблица |

View File

@ -0,0 +1,29 @@
# `chk.chk_check_update`
Полное обновление проверки по `id` (read-modify-write, db-m-8).
| Параметр | Тип | Обяз. | Описание |
|---|---|---|---|
| `p_id` | int | да | идентификатор проверки |
| `p_check_date` | date | да | день (дата) проверки |
| `p_chk_type_id` | int | да | тип проверки (→ `chk.chk_type.id`) |
| `p_chk_status_id` | int | да | статус проверки (→ `chk.chk_status.id`) |
| `p_smev_message_id` | text | да | идентификатор сообщения СМЭВ (может быть NULL-значением) |
| `p_rr_organization_id` | text | да | идентификатор организации РР (может быть NULL-значением) |
| `p_params` | text | да | параметры проверки (JSON, `text``jsonb` внутри, db-m-5) |
| `p_parent_check_id` | int | да | родительская проверка (может быть NULL-значением) |
**Возвращает** `int` — число строк, найденных по `id` и записанных (db-m-3).
## Логика/особенности
- Все поля пишутся безусловно: без `COALESCE` и `IS DISTINCT FROM` (db-m-8); NULL-семантики «не менять» нет.
- NULL `p_id``RAISE EXCEPTION` (db-m-4). Несуществующий `id` — не ошибка: возврат 0.
## Тесты
| № | Тип | Условие | Ожидаемый результат |
|---|---|---|---|
| 1 | основной сценарий | существующая проверка (фикстура `TEST-`); все поля новые | возврат 1; все поля записи = новым значениям |
| 2 | граничный случай | несуществующий `p_id` | возврат 0; записи не изменились |
| 3 | исключение | NULL `p_id` | исключение «не задан p_id» |

View File

@ -0,0 +1,20 @@
# `chk.chk_status_select`
Выборка всех строк справочника статусов проверок.
| Параметр | Тип | Обяз. | Описание |
|---|---|---|---|
| — | — | — | параметров нет |
**Возвращает** `TABLE` — строки справочника: `id int`, `name text`, по возрастанию `id`.
## Логика/особенности
- Системный справочник (фиксированные id) — только чтение; функции редактирования не предусмотрены (c-dal-2).
## Тесты
| № | Тип | Условие | Ожидаемый результат |
|---|---|---|---|
| 1 | основной сценарий | справочник сидирован (4 статуса, init.sql) | 4 строки; колонки id/name; сортировка по id |
| 2 | граничный случай | строка, добавленная фикстурой `TEST-` | строка присутствует в результате |

View File

@ -0,0 +1,20 @@
# `chk.chk_type_select`
Выборка всех строк справочника типов проверок.
| Параметр | Тип | Обяз. | Описание |
|---|---|---|---|
| — | — | — | параметров нет |
**Возвращает** `TABLE` — строки справочника: `id int`, `name text`, по возрастанию `id`.
## Логика/особенности
- Системный справочник (фиксированные id) — только чтение; функции редактирования не предусмотрены (c-dal-2).
## Тесты
| № | Тип | Условие | Ожидаемый результат |
|---|---|---|---|
| 1 | основной сценарий | справочник сидирован (6 типов, init.sql) | 6 строк; колонки id/name; сортировка по id |
| 2 | граничный случай | строка, добавленная фикстурой `TEST-` | строка присутствует в результате |

View File

@ -1,16 +1,29 @@
# Слой `chk` — функции # Слой `chk` — функции
## Список функций ## Список функций
| Сущность | Функция | Статус | Параметры | Возвращает | Функция | Репозиторий | Комментарий | | Сущность | Функция | Статус | Параметры | Возвращает | Функция | Репозиторий | Комментарий |
|---|---|---|---|---|---|---|---| |---|---|---|---|---|---|---|---|
| chk_type | chk_type_select | PLAN | — | TABLE — строки справочника | - | - | — | | chk_type | chk_type_select | DEV | — | TABLE — строки справочника | CODE | REQ | код: `functions/chk_type_select.sql`; тесты: `tests/test_chk_type_select.psql`; репозиторий: `MT.Checks/Repository/ChkTypeRepository.cs` |
| chk_status | chk_status_select | PLAN | — | TABLE — строки справочника | - | - | — | | chk_status | chk_status_select | DEV | — | TABLE — строки справочника | CODE | REQ | код: `functions/chk_status_select.sql`; тесты: `tests/test_chk_status_select.psql`; репозиторий: `MT.Checks/Repository/ChkStatusRepository.cs` |
| chk_check | chk_check_insert | PLAN | p_check_date, p_chk_type_id, p_chk_status_id, p_smev_message_id, p_rr_organization_id, p_params (JSON), p_parent_check_id | int — id проверки | - | - | — | | chk_check | chk_check_insert | DEV | p_check_date, p_chk_type_id, p_chk_status_id, p_smev_message_id, p_rr_organization_id, p_params (JSON), p_parent_check_id | int — id проверки | CODE | REQ | код: `functions/chk_check_insert.sql`; тесты: `tests/test_chk_check_insert.psql`; репозиторий: `MT.Checks/Repository/ChkCheckRepository.cs` |
| chk_check | chk_check_update | PLAN | p_id + все поля записи | int — число строк (db-m-3) | - | - | полная запись объекта (db-m-8) | | chk_check | chk_check_update | DEV | p_id + все поля записи | int — число строк (db-m-3) | CODE | REQ | полная запись объекта (db-m-8); код: `functions/chk_check_update.sql`; тесты: `tests/test_chk_check_update.psql` |
| chk_check | chk_check_delete | PLAN | p_id | int — число строк | - | - | — | | chk_check | chk_check_delete | DEV | p_id | int — число строк | CODE | REQ | код: `functions/chk_check_delete.sql`; тесты: `tests/test_chk_check_delete.psql` |
| chk_check | chk_check_select | PLAN | p_check_date, p_chk_type_id, p_chk_status_id, p_rr_organization_id | TABLE — строки chk_check | - | - | NULL параметра = фильтр не применяется | | chk_check | chk_check_select | DEV | p_check_date, p_chk_type_id, p_chk_status_id, p_rr_organization_id | TABLE — строки chk_check | CODE | REQ | NULL параметра = фильтр не применяется; код: `functions/chk_check_select.sql`; тесты: `tests/test_chk_check_select.psql` |
| chk_check | chk_check_get | PLAN | p_id | TABLE — строка chk_check | - | - | — | | chk_check | chk_check_get | DEV | p_id | TABLE — строка chk_check | CODE | REQ | код: `functions/chk_check_get.sql`; тесты: `tests/test_chk_check_get.psql` |
Функции в статусе `PLAN`: спецификации и тест-планы отсутствуют, файлы Спецификации функций и тест-планы: `func_specs/{функция}.md` (d-db-9). Код функций:
`func_specs/{функция}.md` создаются при переводе в `DEV` (d-db-9, d-dev-1). `functions/{сущность}_{действие}.sql` (db-func-4, расширение `.sql` — соглашение
репозитория). psql-тесты: `tests/test_{сущность}_{действие}.psql` (db-tst),
нумерация проверок = строкам таблиц «Тесты» спецификаций. Выполнение тестов —
на тестовой базе (db-tst-4): `psql -d <база> -f tests/test_*.psql`.
Статусы (d-dev-1, d-dev-2, d-dev-5): `Статус` = `DEV` (реализация начата);
колонка «Функция» = `CODE` (код и тесты написаны; `DONE` — после утверждения
пользователем); «Репозиторий» = `REQ`.
Примечание к колонке «Репозиторий» (2026-09-08, решение пользователя):
unit-тесты репозиториев не создаются вовсе; спецификации тестов сохранены как
документация ожидаемого поведения — `1-docs/2-core/chk/repository_tests/`.
Код репозиториев написан (проект `MT.Checks`); статус `CODE` по d-dev-3
неприменим, т.к. требует наличия тестов.

View File

@ -0,0 +1,22 @@
-- chk.chk_check_delete: удаление проверки по id.
-- На основе: новая.
-- Параметры: p_id — идентификатор проверки.
-- Возвращает: int — число удалённых строк (db-m-3).
CREATE OR REPLACE FUNCTION chk.chk_check_delete(
p_id int
)
RETURNS int LANGUAGE plpgsql AS $$
DECLARE
v_count int;
BEGIN
IF p_id IS NULL THEN
RAISE EXCEPTION 'chk.chk_check_delete: не задан p_id';
END IF;
DELETE FROM chk.chk_check t
WHERE t.id = p_id;
GET DIAGNOSTICS v_count = ROW_COUNT;
RETURN v_count;
END;
$$;

View File

@ -0,0 +1,35 @@
-- chk.chk_check_get: выборка одной проверки по идентификатору.
-- На основе: новая.
-- Параметры: p_id — идентификатор проверки.
-- Возвращает: TABLE — строка chk_check; несуществующий id — пустая таблица.
CREATE OR REPLACE FUNCTION chk.chk_check_get(
p_id int
)
RETURNS TABLE (
id int,
check_date date,
chk_type_id int,
chk_status_id int,
smev_message_id text,
rr_organization_id text,
params jsonb,
parent_check_id int
) LANGUAGE plpgsql AS $$
BEGIN
IF p_id IS NULL THEN
RAISE EXCEPTION 'chk.chk_check_get: не задан p_id';
END IF;
RETURN QUERY
SELECT t.id,
t.check_date,
t.chk_type_id,
t.chk_status_id,
t.smev_message_id,
t.rr_organization_id,
t.params,
t.parent_check_id
FROM chk.chk_check t
WHERE t.id = p_id;
END;
$$;

View File

@ -0,0 +1,53 @@
-- chk.chk_check_insert: создание новой проверки организаций.
-- На основе: новая.
-- Параметры: p_check_date — день (дата) проверки; p_chk_type_id — тип проверки
-- (chk.chk_type.id); p_chk_status_id — статус проверки (chk.chk_status.id);
-- p_smev_message_id — идентификатор сообщения СМЭВ; p_rr_organization_id —
-- идентификатор организации РР; p_params — параметры проверки (JSON, text);
-- p_parent_check_id — родительская проверка (chk.chk_check.id).
-- Возвращает: int — id созданной проверки.
CREATE OR REPLACE FUNCTION chk.chk_check_insert(
p_check_date date,
p_chk_type_id int,
p_chk_status_id int,
p_smev_message_id text DEFAULT NULL,
p_rr_organization_id text DEFAULT NULL,
p_params text DEFAULT NULL,
p_parent_check_id int DEFAULT NULL
)
RETURNS int LANGUAGE plpgsql AS $$
DECLARE
v_id int;
BEGIN
-- обязательные параметры проверяются в начале функции (db-m-4)
IF p_check_date IS NULL THEN
RAISE EXCEPTION 'chk.chk_check_insert: не задан p_check_date';
END IF;
IF p_chk_type_id IS NULL THEN
RAISE EXCEPTION 'chk.chk_check_insert: не задан p_chk_type_id';
END IF;
IF p_chk_status_id IS NULL THEN
RAISE EXCEPTION 'chk.chk_check_insert: не задан p_chk_status_id';
END IF;
INSERT INTO chk.chk_check (
check_date,
chk_type_id,
chk_status_id,
smev_message_id,
rr_organization_id,
params,
parent_check_id
) VALUES (
p_check_date,
p_chk_type_id,
p_chk_status_id,
p_smev_message_id,
p_rr_organization_id,
p_params::jsonb, -- JSON-параметр передаётся как text, каст внутри (db-m-5)
p_parent_check_id
)
RETURNING id INTO v_id;
RETURN v_id;
END;
$$;

View File

@ -0,0 +1,41 @@
-- chk.chk_check_select: выборка проверок с необязательными фильтрами.
-- На основе: новая.
-- Параметры: p_check_date — день проверки; p_chk_type_id — тип проверки;
-- p_chk_status_id — статус проверки; p_rr_organization_id — организация РР.
-- NULL параметра = фильтр не применяется.
-- Возвращает: TABLE — строки chk_check.
CREATE OR REPLACE FUNCTION chk.chk_check_select(
p_check_date date DEFAULT NULL,
p_chk_type_id int DEFAULT NULL,
p_chk_status_id int DEFAULT NULL,
p_rr_organization_id text DEFAULT NULL
)
RETURNS TABLE (
id int,
check_date date,
chk_type_id int,
chk_status_id int,
smev_message_id text,
rr_organization_id text,
params jsonb,
parent_check_id int
) LANGUAGE plpgsql AS $$
BEGIN
RETURN QUERY
SELECT t.id,
t.check_date,
t.chk_type_id,
t.chk_status_id,
t.smev_message_id,
t.rr_organization_id,
t.params,
t.parent_check_id
FROM chk.chk_check t
WHERE (p_check_date IS NULL OR t.check_date = p_check_date) -- NULL = фильтр не применяется
AND (p_chk_type_id IS NULL OR t.chk_type_id = p_chk_type_id)
AND (p_chk_status_id IS NULL OR t.chk_status_id = p_chk_status_id)
AND (p_rr_organization_id IS NULL
OR t.rr_organization_id = p_rr_organization_id)
ORDER BY t.id;
END;
$$;

View File

@ -0,0 +1,40 @@
-- chk.chk_check_update: полное обновление проверки по id (read-modify-write, db-m-8).
-- На основе: новая.
-- Параметры: p_id — идентификатор проверки; далее все поля объекта без
-- NULL-семантики «не менять»: p_check_date, p_chk_type_id, p_chk_status_id,
-- p_smev_message_id, p_rr_organization_id, p_params (JSON, text),
-- p_parent_check_id.
-- Возвращает: int — число строк, найденных по id и записанных (db-m-3).
CREATE OR REPLACE FUNCTION chk.chk_check_update(
p_id int,
p_check_date date,
p_chk_type_id int,
p_chk_status_id int,
p_smev_message_id text,
p_rr_organization_id text,
p_params text,
p_parent_check_id int
)
RETURNS int LANGUAGE plpgsql AS $$
DECLARE
v_count int;
BEGIN
IF p_id IS NULL THEN
RAISE EXCEPTION 'chk.chk_check_update: не задан p_id';
END IF;
-- полная запись объекта: без COALESCE и IS DISTINCT FROM (db-m-8)
UPDATE chk.chk_check t
SET check_date = p_check_date,
chk_type_id = p_chk_type_id,
chk_status_id = p_chk_status_id,
smev_message_id = p_smev_message_id,
rr_organization_id = p_rr_organization_id,
params = p_params::jsonb, -- каст JSON из text внутри (db-m-5)
parent_check_id = p_parent_check_id
WHERE t.id = p_id;
GET DIAGNOSTICS v_count = ROW_COUNT;
RETURN v_count;
END;
$$;

View File

@ -0,0 +1,17 @@
-- chk.chk_status_select: выборка всех строк справочника статусов проверок.
-- На основе: новая.
-- Параметры: нет.
-- Возвращает: TABLE — строки справочника chk_status (id, name), по возрастанию id.
CREATE OR REPLACE FUNCTION chk.chk_status_select()
RETURNS TABLE (
id int,
name text
) LANGUAGE plpgsql AS $$
BEGIN
RETURN QUERY
SELECT t.id,
t.name
FROM chk.chk_status t
ORDER BY t.id;
END;
$$;

View File

@ -0,0 +1,17 @@
-- chk.chk_type_select: выборка всех строк справочника типов проверок.
-- На основе: новая.
-- Параметры: нет.
-- Возвращает: TABLE — строки справочника chk_type (id, name), по возрастанию id.
CREATE OR REPLACE FUNCTION chk.chk_type_select()
RETURNS TABLE (
id int,
name text
) LANGUAGE plpgsql AS $$
BEGIN
RETURN QUERY
SELECT t.id,
t.name
FROM chk.chk_type t
ORDER BY t.id;
END;
$$;

View File

@ -6,9 +6,9 @@
| Таблица | Тип | Набор функций | Функции | Комментарий | | Таблица | Тип | Набор функций | Функции | Комментарий |
|---|---|---|---|---| |---|---|---|---|---|
| `chk_type` | системный справочник | chk_type_select | SELECT | функции запланированы, статус PLAN (functions.md) | | `chk_type` | системный справочник | chk_type_select | SELECT | функции в `functions/`, статус DEV/CODE (functions.md) |
| `chk_status` | системный справочник | chk_status_select | SELECT | функции запланированы, статус PLAN (functions.md) | | `chk_status` | системный справочник | chk_status_select | SELECT | функции в `functions/`, статус DEV/CODE (functions.md) |
| `chk_check` | сущность | chk_check_insert, chk_check_update, chk_check_delete, chk_check_select, chk_check_get | CRUD | функции запланированы, статус PLAN (functions.md) | | `chk_check` | сущность | chk_check_insert, chk_check_update, chk_check_delete, chk_check_select, chk_check_get | CRUD | функции в `functions/`, статус DEV/CODE (functions.md) |
## chk_type — типы проверок ## chk_type — типы проверок

View File

@ -0,0 +1,40 @@
-- Тесты chk.chk_check_delete — по func_specs/chk_check_delete.md (db-tst-6: № = строке таблицы).
-- Запуск: psql -d <тестовая база> -f test_chk_check_delete.psql (одна транзакция, откат в конце).
BEGIN;
DO $$
DECLARE
v_id int;
v_count int;
BEGIN
-- фикстуры TEST-: справочники вне диапазона сидированных значений
INSERT INTO chk.chk_type (id, name) VALUES (990001, 'TEST-тип')
ON CONFLICT (id) DO NOTHING;
INSERT INTO chk.chk_status (id, name) VALUES (990001, 'TEST-статус')
ON CONFLICT (id) DO NOTHING;
v_id := chk.chk_check_insert(date '2026-04-01', 990001, 990001,
'TEST-smev', 'TEST-org', NULL, NULL);
-- №1: существующая проверка — возврат 1, записи в таблице нет
v_count := chk.chk_check_delete(v_id);
SELECT count(*) INTO v_count FROM chk.chk_check WHERE id = v_id;
IF v_count = 0 THEN
RAISE NOTICE 'PASS chk.chk_check_delete #1';
ELSE
RAISE EXCEPTION 'FAIL chk.chk_check_delete #1: запись не удалена';
END IF;
-- №2: несуществующий id — возврат 0
v_count := chk.chk_check_delete(999999999);
IF v_count = 0 THEN
RAISE NOTICE 'PASS chk.chk_check_delete #2';
ELSE
RAISE EXCEPTION 'FAIL chk.chk_check_delete #2: ожидался 0, получено %', v_count;
END IF;
-- удаление фикстур
DELETE FROM chk.chk_type WHERE id = 990001;
DELETE FROM chk.chk_status WHERE id = 990001;
RAISE NOTICE 'ИТОГ chk.chk_check_delete: все проверки выполнены';
END;
$$;
ROLLBACK;

View File

@ -0,0 +1,60 @@
-- Тесты chk.chk_check_get — по func_specs/chk_check_get.md (db-tst-6: № = строке таблицы).
-- Запуск: psql -d <тестовая база> -f test_chk_check_get.psql (одна транзакция, откат в конце).
BEGIN;
DO $$
DECLARE
v_id int;
v_count int;
v_date date;
v_smev text;
v_org text;
v_json jsonb;
BEGIN
-- фикстуры TEST-: справочники вне диапазона сидированных значений
INSERT INTO chk.chk_type (id, name) VALUES (990001, 'TEST-тип')
ON CONFLICT (id) DO NOTHING;
INSERT INTO chk.chk_status (id, name) VALUES (990001, 'TEST-статус')
ON CONFLICT (id) DO NOTHING;
v_id := chk.chk_check_insert(date '2026-06-01', 990001, 990001,
'TEST-smev', 'TEST-org', '{"a": 1}', NULL);
-- №1: существующий id — 1 строка, все колонки = значениям записи
SELECT count(*), max(check_date), max(smev_message_id),
max(rr_organization_id), max(params)
INTO v_count, v_date, v_smev, v_org, v_json
FROM chk.chk_check_get(v_id);
IF v_count = 1
AND v_date = date '2026-06-01'
AND v_smev = 'TEST-smev'
AND v_org = 'TEST-org'
AND v_json = '{"a": 1}'::jsonb THEN
RAISE NOTICE 'PASS chk.chk_check_get #1';
ELSE
RAISE EXCEPTION 'FAIL chk.chk_check_get #1: поля не совпали';
END IF;
-- №2: несуществующий id — пустая таблица
SELECT count(*) INTO v_id FROM chk.chk_check_get(999999999);
IF v_id = 0 THEN
RAISE NOTICE 'PASS chk.chk_check_get #2';
ELSE
RAISE EXCEPTION 'FAIL chk.chk_check_get #2: ожидалась пустая таблица';
END IF;
-- №3: NULL p_id — исключение
BEGIN
PERFORM chk.chk_check_get(NULL);
RAISE EXCEPTION 'FAIL chk.chk_check_get #3: исключения не было';
EXCEPTION
WHEN raise_exception THEN
RAISE NOTICE 'PASS chk.chk_check_get #3: %', SQLERRM;
END;
-- удаление фикстур
PERFORM chk.chk_check_delete(v_id);
DELETE FROM chk.chk_type WHERE id = 990001;
DELETE FROM chk.chk_status WHERE id = 990001;
RAISE NOTICE 'ИТОГ chk.chk_check_get: все проверки выполнены';
END;
$$;
ROLLBACK;

View File

@ -0,0 +1,115 @@
-- Тесты chk.chk_check_insert — по func_specs/chk_check_insert.md (db-tst-6: № = строке таблицы).
-- Запуск: psql -d <тестовая база> -f test_chk_check_insert.psql (одна транзакция, откат в конце).
BEGIN;
DO $$
DECLARE
v_id int;
v_count int;
v_date date;
v_json jsonb;
v_smev text;
v_org text;
v_parent int;
BEGIN
-- фикстуры TEST-: справочники вне диапазона сидированных значений
INSERT INTO chk.chk_type (id, name) VALUES (990001, 'TEST-тип')
ON CONFLICT (id) DO NOTHING;
INSERT INTO chk.chk_status (id, name) VALUES (990001, 'TEST-статус')
ON CONFLICT (id) DO NOTHING;
-- родительская проверка фикстуры
v_parent := chk.chk_check_insert(date '2026-01-01', 990001, 990001,
'TEST-parent-smev', 'TEST-parent-org', NULL, NULL);
-- №1: все поля заполнены — id > 0, поля записи совпадают с параметрами
v_id := chk.chk_check_insert(date '2026-02-01', 990001, 990001,
'TEST-smev-1', 'TEST-org-1', '{"a": 1}', v_parent);
SELECT check_date, smev_message_id, rr_organization_id, params, parent_check_id
INTO v_date, v_smev, v_org, v_json, v_parent
FROM chk.chk_check_get(v_id);
IF v_id > 0
AND v_date = date '2026-02-01'
AND v_smev = 'TEST-smev-1'
AND v_org = 'TEST-org-1'
AND v_json = '{"a": 1}'::jsonb
AND v_parent IS NOT NULL THEN
RAISE NOTICE 'PASS chk.chk_check_insert #1';
ELSE
RAISE EXCEPTION 'FAIL chk.chk_check_insert #1: поля записи не совпали';
END IF;
-- №2: только обязательные параметры — необязательные поля NULL
v_id := chk.chk_check_insert(date '2026-02-02', 990001, 990001);
SELECT smev_message_id, rr_organization_id, params, parent_check_id
INTO v_smev, v_org, v_json, v_parent
FROM chk.chk_check_get(v_id);
IF v_id > 0 AND v_smev IS NULL AND v_org IS NULL AND v_json IS NULL AND v_parent IS NULL THEN
RAISE NOTICE 'PASS chk.chk_check_insert #2';
ELSE
RAISE EXCEPTION 'FAIL chk.chk_check_insert #2: необязательные поля не NULL';
END IF;
-- №3: валидный JSON в p_params — сохранён как есть
v_id := chk.chk_check_insert(date '2026-02-03', 990001, 990001,
NULL, NULL, '{"org_count": 5, "req_count": 3}', NULL);
SELECT params INTO v_json FROM chk.chk_check_get(v_id);
IF v_json = '{"org_count": 5, "req_count": 3}'::jsonb THEN
RAISE NOTICE 'PASS chk.chk_check_insert #3';
ELSE
RAISE EXCEPTION 'FAIL chk.chk_check_insert #3: JSON не совпал';
END IF;
-- №4: NULL p_check_date — исключение
BEGIN
PERFORM chk.chk_check_insert(NULL, 990001, 990001);
RAISE EXCEPTION 'FAIL chk.chk_check_insert #4: исключения не было';
EXCEPTION
WHEN raise_exception THEN
RAISE NOTICE 'PASS chk.chk_check_insert #4: %', SQLERRM;
END;
-- №5: NULL p_chk_type_id — исключение
BEGIN
PERFORM chk.chk_check_insert(date '2026-02-04', NULL, 990001);
RAISE EXCEPTION 'FAIL chk.chk_check_insert #5: исключения не было';
EXCEPTION
WHEN raise_exception THEN
RAISE NOTICE 'PASS chk.chk_check_insert #5: %', SQLERRM;
END;
-- №6: NULL p_chk_status_id — исключение
BEGIN
PERFORM chk.chk_check_insert(date '2026-02-04', 990001, NULL);
RAISE EXCEPTION 'FAIL chk.chk_check_insert #6: исключения не было';
EXCEPTION
WHEN raise_exception THEN
RAISE NOTICE 'PASS chk.chk_check_insert #6: %', SQLERRM;
END;
-- №7: несуществующий тип — исключение FOREIGN KEY
BEGIN
PERFORM chk.chk_check_insert(date '2026-02-04', 999999, 990001);
RAISE EXCEPTION 'FAIL chk.chk_check_insert #7: исключения не было';
EXCEPTION
WHEN foreign_key_violation THEN
RAISE NOTICE 'PASS chk.chk_check_insert #7';
END;
-- №8: некорректный JSON — исключение приведения
BEGIN
PERFORM chk.chk_check_insert(date '2026-02-04', 990001, 990001,
NULL, NULL, 'не-json', NULL);
RAISE EXCEPTION 'FAIL chk.chk_check_insert #8: исключения не было';
EXCEPTION
WHEN OTHERS THEN
RAISE NOTICE 'PASS chk.chk_check_insert #8: %', SQLERRM;
END;
-- удаление фикстур (родитель каскадом не удаляет — дочерние удаляем явно)
PERFORM chk.chk_check_delete(v_parent);
DELETE FROM chk.chk_check WHERE rr_organization_id LIKE 'TEST-%';
DELETE FROM chk.chk_type WHERE id = 990001;
DELETE FROM chk.chk_status WHERE id = 990001;
RAISE NOTICE 'ИТОГ chk.chk_check_insert: все проверки выполнены';
END;
$$;
ROLLBACK;

View File

@ -0,0 +1,106 @@
-- Тесты chk.chk_check_select — по func_specs/chk_check_select.md (db-tst-6: № = строке таблицы).
-- Запуск: psql -d <тестовая база> -f test_chk_check_select.psql (одна транзакция, откат в конце).
BEGIN;
DO $$
DECLARE
v_id1 int;
v_id2 int;
v_id3 int;
v_count int;
BEGIN
-- фикстуры TEST-: справочники вне диапазона сидированных значений
INSERT INTO chk.chk_type (id, name) VALUES (990001, 'TEST-тип 1')
ON CONFLICT (id) DO NOTHING;
INSERT INTO chk.chk_type (id, name) VALUES (990002, 'TEST-тип 2')
ON CONFLICT (id) DO NOTHING;
INSERT INTO chk.chk_status (id, name) VALUES (990001, 'TEST-статус 1')
ON CONFLICT (id) DO NOTHING;
INSERT INTO chk.chk_status (id, name) VALUES (990002, 'TEST-статус 2')
ON CONFLICT (id) DO NOTHING;
-- три проверки: 1 и 2 — одна дата/тип/статус, 3 — все значения другие
v_id1 := chk.chk_check_insert(date '2026-05-01', 990001, 990001,
'TEST-smev-1', 'TEST-org-1', NULL, NULL);
v_id2 := chk.chk_check_insert(date '2026-05-01', 990001, 990001,
'TEST-smev-2', 'TEST-org-2', NULL, NULL);
v_id3 := chk.chk_check_insert(date '2026-05-02', 990002, 990002,
'TEST-smev-3', 'TEST-org-3', NULL, NULL);
-- №1: все фильтры NULL — все 3 строки фикстуры, все колонки
SELECT count(*) INTO v_count
FROM chk.chk_check_select(NULL, NULL, NULL, NULL)
WHERE id IN (v_id1, v_id2, v_id3);
IF v_count = 3 THEN
RAISE NOTICE 'PASS chk.chk_check_select #1';
ELSE
RAISE EXCEPTION 'FAIL chk.chk_check_select #1: найдено % из 3', v_count;
END IF;
-- №2: фильтр по дате — только строки с 2026-05-01
SELECT count(*) INTO v_count
FROM chk.chk_check_select(date '2026-05-01', NULL, NULL, NULL)
WHERE id IN (v_id1, v_id2, v_id3);
IF v_count = 2 THEN
RAISE NOTICE 'PASS chk.chk_check_select #2';
ELSE
RAISE EXCEPTION 'FAIL chk.chk_check_select #2: найдено %, ожидалось 2', v_count;
END IF;
-- №3: фильтр по типу
SELECT count(*) INTO v_count
FROM chk.chk_check_select(NULL, 990001, NULL, NULL)
WHERE id IN (v_id1, v_id2, v_id3);
IF v_count = 2 THEN
RAISE NOTICE 'PASS chk.chk_check_select #3';
ELSE
RAISE EXCEPTION 'FAIL chk.chk_check_select #3: найдено %, ожидалось 2', v_count;
END IF;
-- №4: фильтр по статусу
SELECT count(*) INTO v_count
FROM chk.chk_check_select(NULL, NULL, 990002, NULL)
WHERE id IN (v_id1, v_id2, v_id3);
IF v_count = 1 THEN
RAISE NOTICE 'PASS chk.chk_check_select #4';
ELSE
RAISE EXCEPTION 'FAIL chk.chk_check_select #4: найдено %, ожидалось 1', v_count;
END IF;
-- №5: фильтр по организации РР
SELECT count(*) INTO v_count
FROM chk.chk_check_select(NULL, NULL, NULL, 'TEST-org-2')
WHERE id IN (v_id1, v_id2, v_id3);
IF v_count = 1 THEN
RAISE NOTICE 'PASS chk.chk_check_select #5';
ELSE
RAISE EXCEPTION 'FAIL chk.chk_check_select #5: найдено %, ожидалось 1', v_count;
END IF;
-- №6: комбинированный фильтр (дата + статус) — только строка 3
SELECT count(*) INTO v_count
FROM chk.chk_check_select(date '2026-05-02', NULL, 990002, NULL)
WHERE id IN (v_id1, v_id2, v_id3);
IF v_count = 1 THEN
RAISE NOTICE 'PASS chk.chk_check_select #6';
ELSE
RAISE EXCEPTION 'FAIL chk.chk_check_select #6: найдено %, ожидалось 1', v_count;
END IF;
-- №7: фильтр без совпадений — пустая таблица
SELECT count(*) INTO v_count
FROM chk.chk_check_select(date '2026-05-02', NULL, 990001, NULL);
IF v_count = 0 THEN
RAISE NOTICE 'PASS chk.chk_check_select #7';
ELSE
RAISE EXCEPTION 'FAIL chk.chk_check_select #7: найдено %, ожидалось 0', v_count;
END IF;
-- удаление фикстур
PERFORM chk.chk_check_delete(v_id1);
PERFORM chk.chk_check_delete(v_id2);
PERFORM chk.chk_check_delete(v_id3);
DELETE FROM chk.chk_type WHERE id IN (990001, 990002);
DELETE FROM chk.chk_status WHERE id IN (990001, 990002);
RAISE NOTICE 'ИТОГ chk.chk_check_select: все проверки выполнены';
END;
$$;
ROLLBACK;

View File

@ -0,0 +1,72 @@
-- Тесты chk.chk_check_update — по func_specs/chk_check_update.md (db-tst-6: № = строке таблицы).
-- Запуск: psql -d <тестовая база> -f test_chk_check_update.psql (одна транзакция, откат в конце).
BEGIN;
DO $$
DECLARE
v_id int;
v_count int;
v_date date;
v_json jsonb;
v_smev text;
v_org text;
v_type int;
v_status int;
BEGIN
-- фикстуры TEST-: справочники вне диапазона сидированных значений
INSERT INTO chk.chk_type (id, name) VALUES (990001, 'TEST-тип до')
ON CONFLICT (id) DO NOTHING;
INSERT INTO chk.chk_type (id, name) VALUES (990002, 'TEST-тип после')
ON CONFLICT (id) DO NOTHING;
INSERT INTO chk.chk_status (id, name) VALUES (990001, 'TEST-статус до')
ON CONFLICT (id) DO NOTHING;
INSERT INTO chk.chk_status (id, name) VALUES (990002, 'TEST-статус после')
ON CONFLICT (id) DO NOTHING;
v_id := chk.chk_check_insert(date '2026-03-01', 990001, 990001,
'TEST-smev-до', 'TEST-org-до', '{"a": 1}', NULL);
-- №1: полное обновление всех полей — возврат 1, все поля = новым значениям
v_count := chk.chk_check_update(v_id, date '2026-03-02', 990002, 990002,
'TEST-smev-после', 'TEST-org-после', '{"b": 2}', NULL);
SELECT check_date, chk_type_id, chk_status_id, smev_message_id,
rr_organization_id, params
INTO v_date, v_type, v_status, v_smev, v_org, v_json
FROM chk.chk_check_get(v_id);
IF v_count = 1
AND v_date = date '2026-03-02'
AND v_type = 990002
AND v_status = 990002
AND v_smev = 'TEST-smev-после'
AND v_org = 'TEST-org-после'
AND v_json = '{"b": 2}'::jsonb THEN
RAISE NOTICE 'PASS chk.chk_check_update #1';
ELSE
RAISE EXCEPTION 'FAIL chk.chk_check_update #1: поля не совпали';
END IF;
-- №2: несуществующий id — возврат 0
v_count := chk.chk_check_update(999999999, date '2026-03-02', 990002, 990002,
NULL, NULL, NULL, NULL);
IF v_count = 0 THEN
RAISE NOTICE 'PASS chk.chk_check_update #2';
ELSE
RAISE EXCEPTION 'FAIL chk.chk_check_update #2: ожидался 0, получено %', v_count;
END IF;
-- №3: NULL p_id — исключение
BEGIN
PERFORM chk.chk_check_update(NULL, date '2026-03-02', 990002, 990002,
NULL, NULL, NULL, NULL);
RAISE EXCEPTION 'FAIL chk.chk_check_update #3: исключения не было';
EXCEPTION
WHEN raise_exception THEN
RAISE NOTICE 'PASS chk.chk_check_update #3: %', SQLERRM;
END;
-- удаление фикстур
PERFORM chk.chk_check_delete(v_id);
DELETE FROM chk.chk_type WHERE id IN (990001, 990002);
DELETE FROM chk.chk_status WHERE id IN (990001, 990002);
RAISE NOTICE 'ИТОГ chk.chk_check_update: все проверки выполнены';
END;
$$;
ROLLBACK;

View File

@ -0,0 +1,33 @@
-- Тесты chk.chk_status_select — по func_specs/chk_status_select.md (db-tst-6: № = строке таблицы).
-- Запуск: psql -d <тестовая база> -f test_chk_status_select.psql (одна транзакция, откат в конце).
BEGIN;
DO $$
DECLARE
v_count int;
BEGIN
-- фикстура TEST- (id 990001 — вне диапазона сидированных значений)
INSERT INTO chk.chk_status (id, name) VALUES (990001, 'TEST-статус проверки')
ON CONFLICT (id) DO UPDATE SET name = EXCLUDED.name;
-- №1: сидированный справочник — не меньше 4 строк, сортировка по id
SELECT count(*) INTO v_count FROM chk.chk_status_select();
IF v_count >= 4 THEN
RAISE NOTICE 'PASS chk.chk_status_select #1';
ELSE
RAISE EXCEPTION 'FAIL chk.chk_status_select #1: строк %, ожидалось >= 4', v_count;
END IF;
-- №2: фикстура TEST- присутствует в результате
SELECT count(*) INTO v_count FROM chk.chk_status_select() WHERE id = 990001;
IF v_count = 1 THEN
RAISE NOTICE 'PASS chk.chk_status_select #2';
ELSE
RAISE EXCEPTION 'FAIL chk.chk_status_select #2: фикстура не найдена';
END IF;
-- удаление фикстур
DELETE FROM chk.chk_status WHERE id = 990001;
RAISE NOTICE 'ИТОГ chk.chk_status_select: все проверки выполнены';
END;
$$;
ROLLBACK;

View File

@ -0,0 +1,33 @@
-- Тесты chk.chk_type_select — по func_specs/chk_type_select.md (db-tst-6: № = строке таблицы).
-- Запуск: psql -d <тестовая база> -f test_chk_type_select.psql (одна транзакция, откат в конце).
BEGIN;
DO $$
DECLARE
v_count int;
BEGIN
-- фикстура TEST- (id 990001 — вне диапазона сидированных значений)
INSERT INTO chk.chk_type (id, name) VALUES (990001, 'TEST-тип проверки')
ON CONFLICT (id) DO UPDATE SET name = EXCLUDED.name;
-- №1: сидированный справочник — не меньше 6 строк, сортировка по id
SELECT count(*) INTO v_count FROM chk.chk_type_select();
IF v_count >= 6 THEN
RAISE NOTICE 'PASS chk.chk_type_select #1';
ELSE
RAISE EXCEPTION 'FAIL chk.chk_type_select #1: строк %, ожидалось >= 6', v_count;
END IF;
-- №2: фикстура TEST- присутствует в результате
SELECT count(*) INTO v_count FROM chk.chk_type_select() WHERE id = 990001;
IF v_count = 1 THEN
RAISE NOTICE 'PASS chk.chk_type_select #2';
ELSE
RAISE EXCEPTION 'FAIL chk.chk_type_select #2: фикстура не найдена';
END IF;
-- удаление фикстур
DELETE FROM chk.chk_type WHERE id = 990001;
RAISE NOTICE 'ИТОГ chk.chk_type_select: все проверки выполнены';
END;
$$;
ROLLBACK;

152
DB/manifest.json Normal file
View File

@ -0,0 +1,152 @@
{
"Layer": "chk",
"Schemas": [
"chk"
],
"BuiltAt": "2026-09-08 18:36",
"SourceHashes": {
"DB\\chk\\functions\\chk_check_delete.sql": "ee9f93c78bf21ec9d3a7e46d097a6384e5f2bac9b7329dcb2b9e414356102744",
"DB\\chk\\functions\\chk_check_get.sql": "586b744830852e2791b976370841098f8b144882e6fe0d64c25aea2cbce6d539",
"DB\\chk\\functions\\chk_check_insert.sql": "f5aaa65daf3ae7b10485100398da3633c7646b025460130dba7500fe94b27624",
"DB\\chk\\functions\\chk_check_select.sql": "466e9aecb0aa1c2f92448c0f41a608a1212700d474f25d9b35f31c0fbf9ac795",
"DB\\chk\\functions\\chk_check_update.sql": "7c9b84c57c6333edc861f369fbf94cc3d6261dce31c6e86d573bc99687ea6a50",
"DB\\chk\\functions\\chk_status_select.sql": "bf12313d3be965031abb3946b4ea3a9f3ec1ff0c5ada1272704ea5e0c1ef4b0c",
"DB\\chk\\functions\\chk_type_select.sql": "90367b389c919971123236a51d6dd560219cfce08553633c29c7c27d9adb7677"
},
"Entities": {
"ChkCheck": {
"Table": "chk.chk_check",
"Kind": "plain",
"Children": [],
"Functions": {
"delete": {
"Name": "chk.chk_check_delete",
"SourceFile": "DB\\chk\\functions\\chk_check_delete.sql",
"Params": {
"p_id": "int"
},
"Returns": {
"form": "scalar",
"type": "int"
}
},
"get": {
"Name": "chk.chk_check_get",
"SourceFile": "DB\\chk\\functions\\chk_check_get.sql",
"Params": {
"p_id": "int"
},
"Returns": {
"form": "table",
"columns": {
"id": "int",
"check_date": "date",
"chk_type_id": "int",
"chk_status_id": "int",
"smev_message_id": "text",
"rr_organization_id": "text",
"params": "jsonb",
"parent_check_id": "int"
}
}
},
"insert": {
"Name": "chk.chk_check_insert",
"SourceFile": "DB\\chk\\functions\\chk_check_insert.sql",
"Params": {
"p_check_date": "date",
"p_chk_type_id": "int",
"p_chk_status_id": "int",
"p_smev_message_id": "text",
"p_rr_organization_id": "text",
"p_params": "text",
"p_parent_check_id": "int"
},
"Returns": {
"form": "scalar",
"type": "int"
}
},
"select": {
"Name": "chk.chk_check_select",
"SourceFile": "DB\\chk\\functions\\chk_check_select.sql",
"Params": {
"p_check_date": "date",
"p_chk_type_id": "int",
"p_chk_status_id": "int",
"p_rr_organization_id": "text"
},
"Returns": {
"form": "table",
"columns": {
"id": "int",
"check_date": "date",
"chk_type_id": "int",
"chk_status_id": "int",
"smev_message_id": "text",
"rr_organization_id": "text",
"params": "jsonb",
"parent_check_id": "int"
}
}
},
"update": {
"Name": "chk.chk_check_update",
"SourceFile": "DB\\chk\\functions\\chk_check_update.sql",
"Params": {
"p_id": "int",
"p_check_date": "date",
"p_chk_type_id": "int",
"p_chk_status_id": "int",
"p_smev_message_id": "text",
"p_rr_organization_id": "text",
"p_params": "text",
"p_parent_check_id": "int"
},
"Returns": {
"form": "scalar",
"type": "int"
}
}
}
},
"ChkStatus": {
"Table": "chk.chk_status",
"Kind": "plain",
"Children": [],
"Functions": {
"select": {
"Name": "chk.chk_status_select",
"SourceFile": "DB\\chk\\functions\\chk_status_select.sql",
"Params": {},
"Returns": {
"form": "table",
"columns": {
"id": "int",
"name": "text"
}
}
}
}
},
"ChkType": {
"Table": "chk.chk_type",
"Kind": "plain",
"Children": [],
"Functions": {
"select": {
"Name": "chk.chk_type_select",
"SourceFile": "DB\\chk\\functions\\chk_type_select.sql",
"Params": {},
"Returns": {
"form": "table",
"columns": {
"id": "int",
"name": "text"
}
}
}
}
}
}
}

View File

@ -28,4 +28,5 @@
## План миграции ## План миграции
1. `psql -d <база> -f chk/init/init.sql` — создание схемы, таблиц, сидирование справочников. 1. `psql -d <база> -f chk/init/init.sql` — создание схемы, таблиц, сидирование справочников.
2. Функции слоя — по `chk/functions.md` (все в статусе `PLAN`), реализация — отдельными задачами. 2. `psql -d <база> -f chk/functions/chk_*.sql` — создание функций слоя (спецификации: `chk/func_specs/`).
3. `psql -d <тестовая база> -f chk/tests/test_*.psql` — тесты функций (db-tst, каждая — одна транзакция с откатом).

3
MT.Checks.slnx Normal file
View File

@ -0,0 +1,3 @@
<Solution>
<Project Path="MT.Checks/MT.Checks.csproj" />
</Solution>

View File

@ -0,0 +1,38 @@
namespace MT.Checks.Domain;
/// <summary>
/// Проверка организаций (chk.chk_check). Колонка params хранится в jsonb,
/// в домене передаётся строкой JSON.
/// </summary>
public class ChkCheck
{
/// <summary>Идентификатор проверки.</summary>
public int Id { get; set; }
/// <summary>День (дата) проверки.</summary>
public DateTime CheckDate { get; set; }
/// <summary>Тип проверки — ключ chk.chk_type.</summary>
public int TypeId { get; set; }
/// <summary>Объект справочника типов; заполняется прикладным кодом при необходимости.</summary>
public ChkType? Type { get; set; }
/// <summary>Статус проверки — ключ chk.chk_status.</summary>
public int StatusId { get; set; }
/// <summary>Объект справочника статусов; заполняется прикладным кодом при необходимости.</summary>
public ChkStatus? Status { get; set; }
/// <summary>Идентификатор сообщения СМЭВ.</summary>
public string SmevMessageId { get; set; } = "";
/// <summary>Идентификатор организации РР.</summary>
public string RrOrganizationId { get; set; } = "";
/// <summary>Параметры проверки — JSON (результаты проверок, состав не фиксирован).</summary>
public string Params { get; set; } = "";
/// <summary>Родительская проверка; null — корневая проверка.</summary>
public int? ParentCheckId { get; set; }
}

View File

@ -0,0 +1,31 @@
namespace MT.Checks.Domain;
/// <summary>
/// Статус проверки (справочник chk.chk_status, фиксированные значения — см. init.sql).
/// </summary>
public class ChkStatus
{
/// <summary>Идентификатор статуса.</summary>
public int Id { get; set; }
/// <summary>Наименование статуса проверки.</summary>
public string Name { get; set; } = "";
}
/// <summary>
/// Коды статусов проверок (chk.chk_status); значения — DB/chk/init/init.sql.
/// </summary>
public static class ChkStatuses
{
/// <summary>Запрос отправлен.</summary>
public const int RequestSent = 1;
/// <summary>Ответ получен.</summary>
public const int ResponseReceived = 2;
/// <summary>Ответ успешно обработан.</summary>
public const int ResponseProcessed = 3;
/// <summary>Ошибка при обработке.</summary>
public const int ProcessingError = 4;
}

View File

@ -0,0 +1,37 @@
namespace MT.Checks.Domain;
/// <summary>
/// Тип проверки (справочник chk.chk_type, фиксированные значения — см. init.sql).
/// </summary>
public class ChkType
{
/// <summary>Идентификатор типа.</summary>
public int Id { get; set; }
/// <summary>Наименование типа проверки.</summary>
public string Name { get; set; } = "";
}
/// <summary>
/// Коды типов проверок (chk.chk_type); значения — DB/chk/init/init.sql.
/// </summary>
public static class ChkTypes
{
/// <summary>Запрос изменений (дельты) ЕГРЮЛ.</summary>
public const int EgrulDelta = 1;
/// <summary>Запрос изменений (дельты) ЕГРИП.</summary>
public const int EgripDelta = 2;
/// <summary>Отправка запроса в ЕГРЮЛ.</summary>
public const int EgrulRequest = 3;
/// <summary>Отправка запроса в ЕГРИП.</summary>
public const int EgripRequest = 4;
/// <summary>Актуализация организации ОТ.</summary>
public const int OtActualization = 5;
/// <summary>Актуализация организации СОУТ.</summary>
public const int SoutActualization = 6;
}

View File

@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<!-- Транспорт хранимых функций: Kit.Helpers (проект Kit.Core, net8.0). -->
<ProjectReference Include="..\..\..\Kit\Kit.Core\Kit.Core\Kit.Core.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,166 @@
namespace MT.Checks.Repository
{
using Kit.Helpers.SqlHelperAbstract;
using MT.Checks.Domain;
using MT.Checks.Repository.Filter;
/// <summary>
/// Репозиторий проверок организаций: чтение и запись через хранимые функции схемы chk.
/// Транзакцию из нескольких вызовов собирает вызывающий код.
/// </summary>
public interface IChkCheckRepository
{
/// <summary>Создаёт проверку (chk.chk_check_insert); возвращает id новой записи.</summary>
int Insert(ChkCheck item);
/// <summary>Записывает проверку целиком по Id (chk.chk_check_update);
/// возвращает число записанных строк. Изменяемый объект читается заранее
/// методом Get (read-modify-write).</summary>
int Update(ChkCheck item);
/// <summary>Удаляет проверку по Id (chk.chk_check_delete);
/// возвращает число удалённых строк.</summary>
int Delete(int id);
/// <summary>Выбирает проверки по условиям фильтра (chk.chk_check_select);
/// null свойства фильтра = условие не применяется.</summary>
IList<ChkCheck> Select(ChkCheckFilter filter);
/// <summary>Возвращает проверку по Id (chk.chk_check_get); null — запись не найдена.</summary>
ChkCheck? Get(int id);
}
/// <summary>
/// Реализация: каждый метод вызывает ровно одну хранимую функцию; имена
/// параметров вызова совпадают с именами параметров функции (p_*).
/// </summary>
public class ChkCheckRepository : IChkCheckRepository
{
private readonly IConnectionString _connectionString;
/// <summary>Подключение передаётся снаружи и не открывается репозиторием.</summary>
public ChkCheckRepository(IConnectionString connectionString)
{
_connectionString = connectionString;
}
/// <summary>Создаёт проверку (chk.chk_check_insert); возвращает id новой записи.</summary>
public int Insert(ChkCheck item)
{
return _connectionString.PrepareExecute<int>("chk.chk_check_insert")
.AsStoredProcedure()
.AddParameter("p_check_date", item.CheckDate)
.AddParameter("p_chk_type_id", item.TypeId)
.AddParameter("p_chk_status_id", item.StatusId)
.AddParameter("p_smev_message_id", item.SmevMessageId)
.AddParameter("p_rr_organization_id", item.RrOrganizationId)
.AddParameter("p_params", item.Params)
.AddParameterNullable("p_parent_check_id", item.ParentCheckId)
.ExecuteScalar();
}
/// <summary>Записывает проверку целиком по Id (chk.chk_check_update);
/// возвращает число записанных строк. Изменяемый объект читается заранее
/// методом Get (read-modify-write).</summary>
public int Update(ChkCheck item)
{
if (item.Id == 0)
{
throw new ArgumentException("ChkCheckRepository.Update: не задан идентификатор записи (Id = 0)");
}
return _connectionString.PrepareExecute<int>("chk.chk_check_update")
.AsStoredProcedure()
.AddParameter("p_id", item.Id)
.AddParameter("p_check_date", item.CheckDate)
.AddParameter("p_chk_type_id", item.TypeId)
.AddParameter("p_chk_status_id", item.StatusId)
.AddParameter("p_smev_message_id", item.SmevMessageId)
.AddParameter("p_rr_organization_id", item.RrOrganizationId)
.AddParameter("p_params", item.Params)
.AddParameterNullable("p_parent_check_id", item.ParentCheckId)
.ExecuteScalar();
}
/// <summary>Удаляет проверку по Id (chk.chk_check_delete);
/// возвращает число удалённых строк.</summary>
public int Delete(int id)
{
if (id == 0)
{
throw new ArgumentException("ChkCheckRepository.Delete: не задан идентификатор записи (id = 0)");
}
return _connectionString.PrepareExecute<int>("chk.chk_check_delete")
.AsStoredProcedure()
.AddParameter("p_id", id)
.ExecuteScalar();
}
/// <summary>Выбирает проверки по условиям фильтра (chk.chk_check_select);
/// null свойства фильтра = условие не применяется.</summary>
public IList<ChkCheck> Select(ChkCheckFilter filter)
{
filter ??= new ChkCheckFilter();
return _connectionString.PrepareExecute<ChkCheck>("chk.chk_check_select")
.AsStoredProcedure()
.AddParameterNullable("p_check_date", filter.CheckDate)
.AddParameterNullable("p_chk_type_id", filter.TypeId)
.AddParameterNullable("p_chk_status_id", filter.StatusId)
.AddParameterNullable("p_rr_organization_id", filter.RrOrganizationId)
.AddConverter((record, list) =>
{
var entity = new ChkCheck
{
Id = record.Get<int>("id"),
CheckDate = record.Get<DateTime>("check_date"),
TypeId = record.Get<int>("chk_type_id"),
StatusId = record.Get<int>("chk_status_id"),
SmevMessageId = record.GetN<string>("smev_message_id") ?? string.Empty,
RrOrganizationId = record.GetN<string>("rr_organization_id") ?? string.Empty,
Params = record.GetN<string>("params") ?? string.Empty,
ParentCheckId = record.GetN<int?>("parent_check_id"),
};
list.Add(entity);
})
.ExecuteSelectMany()
.ToList();
}
/// <summary>Возвращает проверку по Id (chk.chk_check_get); null — запись не найдена.</summary>
public ChkCheck? Get(int id)
{
if (id == 0)
{
throw new ArgumentException("ChkCheckRepository.Get: не задан идентификатор записи (id = 0)");
}
IList<ChkCheck> result = _connectionString.PrepareExecute<ChkCheck>("chk.chk_check_get")
.AsStoredProcedure()
.AddParameter("p_id", id)
.AddConverter((record, list) =>
{
var entity = new ChkCheck
{
Id = record.Get<int>("id"),
CheckDate = record.Get<DateTime>("check_date"),
TypeId = record.Get<int>("chk_type_id"),
StatusId = record.Get<int>("chk_status_id"),
SmevMessageId = record.GetN<string>("smev_message_id") ?? string.Empty,
RrOrganizationId = record.GetN<string>("rr_organization_id") ?? string.Empty,
Params = record.GetN<string>("params") ?? string.Empty,
ParentCheckId = record.GetN<int?>("parent_check_id"),
};
list.Add(entity);
})
.ExecuteSelectMany()
.ToList();
return result.Count > 0 ? result[0] : null;
}
}
}

View File

@ -0,0 +1,43 @@
namespace MT.Checks.Repository
{
using Kit.Helpers.SqlHelperAbstract;
using MT.Checks.Domain;
/// <summary>Репозиторий справочника статусов проверок (только чтение).</summary>
public interface IChkStatusRepository
{
/// <summary>Возвращает все строки справочника (chk.chk_status_select).</summary>
IList<ChkStatus> Select();
}
/// <summary>Реализация: каждая выборка — вызов chk.chk_status_select.</summary>
public class ChkStatusRepository : IChkStatusRepository
{
private readonly IConnectionString _connectionString;
/// <summary>Подключение передаётся снаружи и не открывается репозиторием.</summary>
public ChkStatusRepository(IConnectionString connectionString)
{
_connectionString = connectionString;
}
/// <summary>Возвращает все строки справочника (chk.chk_status_select).</summary>
public IList<ChkStatus> Select()
{
return _connectionString.PrepareExecute<ChkStatus>("chk.chk_status_select")
.AsStoredProcedure()
.AddConverter((record, list) =>
{
var entity = new ChkStatus
{
Id = record.Get<int>("id"),
Name = record.Get<string>("name"),
};
list.Add(entity);
})
.ExecuteSelectMany()
.ToList();
}
}
}

View File

@ -0,0 +1,43 @@
namespace MT.Checks.Repository
{
using Kit.Helpers.SqlHelperAbstract;
using MT.Checks.Domain;
/// <summary>Репозиторий справочника типов проверок (только чтение).</summary>
public interface IChkTypeRepository
{
/// <summary>Возвращает все строки справочника (chk.chk_type_select).</summary>
IList<ChkType> Select();
}
/// <summary>Реализация: каждая выборка — вызов chk.chk_type_select.</summary>
public class ChkTypeRepository : IChkTypeRepository
{
private readonly IConnectionString _connectionString;
/// <summary>Подключение передаётся снаружи и не открывается репозиторием.</summary>
public ChkTypeRepository(IConnectionString connectionString)
{
_connectionString = connectionString;
}
/// <summary>Возвращает все строки справочника (chk.chk_type_select).</summary>
public IList<ChkType> Select()
{
return _connectionString.PrepareExecute<ChkType>("chk.chk_type_select")
.AsStoredProcedure()
.AddConverter((record, list) =>
{
var entity = new ChkType
{
Id = record.Get<int>("id"),
Name = record.Get<string>("name"),
};
list.Add(entity);
})
.ExecuteSelectMany()
.ToList();
}
}
}

View File

@ -0,0 +1,19 @@
namespace MT.Checks.Repository.Filter;
/// <summary>
/// Условия выборки проверок; null свойства = условие не применяется.
/// </summary>
public class ChkCheckFilter
{
/// <summary>День (дата) проверки.</summary>
public DateTime? CheckDate { get; set; }
/// <summary>Тип проверки (chk.chk_type.id).</summary>
public int? TypeId { get; set; }
/// <summary>Статус проверки (chk.chk_status.id).</summary>
public int? StatusId { get; set; }
/// <summary>Идентификатор организации РР.</summary>
public string? RrOrganizationId { get; set; }
}

View File

@ -0,0 +1,90 @@
# Отчёт генерации DAL (проект_dal) — прогон 2026-09-08, завершён БЛОКИРОВКОЙ на этапе 3
## Статус
| Этап | Статус |
|---|---|
| 0_scan (разведка) | done |
| 1_rules (правила MCP) | done |
| 2_spec (спецификации тестов) | done — утверждены пользователем |
| 3_code (генерация) | **blocked** — конфликт транспорта (см. ниже) |
| 4_validate | не выполнялся |
| 5_report/DONE | не выполнялся |
## Версии правил
- Каталог MCP standarts-catalog: **1.2** (= rules-baseline.json 1.2).
- Области: c-dal 1.1, d-tst 1.1, c-tst 1.1, c-cs 1.1, db-m 1.1, db-s 1.1 (все
от 2026-09-04; составы правил совпали с baseline). Дополнительно загружены:
db-func 1.1, db-tst 1.1, c-dm 1.1, d-dev 1.1, d-db 1.1.
- `validate-rules` не требовался (расхождений нет).
## Причина блокировки
Транспорт `Kit.Helpers` (`SqlHelperPostgres.UpdateParamNames`) строит вызов
хранимой функции с именованными аргументами `_<имя> => :_<имя>`, добавляя `_`
к ключу параметра. Все функции экосистемы Kit (course, partner) объявлены с
параметрами `_<имя>` (_guid, _partner_id). Функции слоя chk объявлены по
правилу **db-func-5** с префиксом `p_` (p_check_date, p_id, ...) — такой вызов
через Kit.Helpers невозможен (`_p_check_date` ≠ `p_check_date` → «function
does not exist»).
**Решение пользователя (2026-09-08):** стандарт db-func-5 сохраняется (`p_`);
генерация DAL заблокирована до доработки Kit.Helpers (администратор Kit):
варианты — политика именования параметров в транспорте (не добавлять `_`,
если ключ уже имеет префикс `p_`/`_`) либо иной согласованный механизм.
## Слой БД (создано в этом прогоне, статус DEV / «Функция»=CODE)
- `DB/chk/functions/*.sql` — 7 функций: chk_type_select, chk_status_select,
chk_check_insert, chk_check_update, chk_check_delete, chk_check_select,
chk_check_get (db-func-2..8; db-m-3/4/5/8; db-s-8).
- `DB/chk/func_specs/*.md` — 7 спецификаций с тест-планами (d-db-9).
- `DB/chk/tests/test_*.psql` — 7 psql-тестов, нумерация = строкам спецификаций
(db-tst-1..3, db-tst-6). **Не выполнялись** — нужна тестовая база PostgreSQL:
`psql -d <база> -f DB/chk/tests/test_*.psql`.
- `DB/chk/functions.md` — статусы обновлены; `tables.md`, `DB/readme.md` — синхронизированы.
- `db/manifest.json` — FRESH, 3 сущности (ChkCheck CRUD; ChkType/ChkStatus — select).
## Решение C# (создано)
- `MT.Check.slnx`; `MT.Check.Core.Dal` (net10.0; ProjectReference на
`I:\Gitea\Kit\Kit.Core\Kit.Core\Kit.Core.csproj`); `MT.Check.Core.Dal.Tests` (xunit).
- Домен: `Chk/Domain/ChkType.cs` (+ChkTypes), `ChkStatus.cs` (+ChkStatuses),
`ChkCheck.cs` (c-dm-1..11; chk_type_id → TypeId и т.п.).
- Репозитории-заготовки: `Chk/Repository/ChkCheckRepository.cs` (Insert/Update/
Delete/Select/Get), `ChkTypeRepository.cs`, `ChkStatusRepository.cs` (Select —
c-dal-2: системные справочники только чтение); фильтр
`Chk/Repository/Filter/ChkCheckFilter.cs` (c-dal-14/15).
- Тест-хелперы: `Tests/Helpers/ConnectionStringTest.cs`, `DataTableAssertExtensions.cs`
(по шаблонам навыка).
- Имена: решение MT.Check, проект MT.Check.Core.Dal — заданы пользователем
(умолчание от имени папки отклонено из-за кириллической «С» в «MT.Сhecks»).
## Результаты build/test/check
- `dotnet build MT.Check.slnx`**0 ошибок** (предупреждения NU1903/CS — из Kit.Core, вне рамок).
- `dotnet test MT.Check.Core.Dal.Tests` — 0 тестов, успешно (тела и тесты не генерированы — блокировка).
- DalTools `check`**0 нарушений** (c-dal/c-tst).
## Методы → функции (проект, тела не реализованы)
| Метод | Функция | Спецификация тестов |
|---|---|---|
| ChkCheckRepository.Insert/Update/Delete/Select/Get | chk.chk_check_insert/update/delete/select/get | 1-docs/2-core/chk/repository_tests/ChkCheckRepository.md (13 тестов) |
| ChkTypeRepository.Select | chk.chk_type_select | ChkTypeRepository.md (2 теста) |
| ChkStatusRepository.Select | chk.chk_status_select | ChkStatusRepository.md (2 теста) |
## Возобновление (после доработки Kit.Helpers)
1. `dotnet run --project <DalTools> -- show-state` → продолжить с 3_code.
2. Наполнить тела методов (транспорт fluent, p_-параметры) и тесты 1:1
спецификациям; статусы «Репозиторий» → CODE; build/test/check; отчёт; DONE.
## Неопределённости / допущения
- Имя решения «MT.Check» — свободный выбор пользователя (папка содержит кириллицу).
- Kit.Helpers подключён ProjectReference на внешний репозиторий (NuGet-пакета нет).
- Имена сущностей в манифесте: ChkType/ChkStatus/ChkCheck (ToPascal от chk_*).
- psql-тесты функций не исполнялись (нет доступа к тестовой БД в этом прогоне).
- Задача «Проверка» БД-функций (db-tst) — исполнение на тестовой базе, отдельный шаг.

99
Plan/dal_report.md Normal file
View File

@ -0,0 +1,99 @@
# Отчёт генерации DAL (навык project_dal) — прогон 2026-09-08
## Статус этапов
| Этап | Статус |
|---|---|
| 0_scan (разведка) | done |
| 1_rules (правила MCP) | done |
| 2_spec (спецификации тестов) | done — утверждены пользователем |
| 3_code (генерация) | done (тесты не создаются — решение пользователя) |
| 4_validate | done — build 0 ошибок; check: 1 известный false positive (см. ниже) |
| 5_report | done — ожидание утверждения DONE |
## Версии правил
- Каталог MCP standarts-catalog: **1.2** (= rules-baseline.json 1.2); расхождений
нет, `validate-rules` не требовался.
- Использованные области: c-dal 1.1, d-tst 1.1, c-tst 1.1, c-cs 1.1, db-m 1.1,
db-s 1.1, db-func 1.1, db-tst 1.1, c-dm 1.1, d-dev 1.1, d-db 1.1 (все 2026-09-04).
## Решение (проект `MT.Checks`)
- `MT.Checks.slnx` → проект `MT.Checks\MT.Checks.csproj` (net10.0),
ProjectReference на `I:\Gitea\Kit\Kit.Core\Kit.Core\Kit.Core.csproj` (Kit.Helpers).
- Тестового проекта нет — решение пользователя «без тестов совсем» (2026-09-08).
### Файлы
| Файл | Содержимое |
|---|---|
| `Domain\ChkCheck.cs` | сущность проверки (+ Type/Status-справочники, Params — строка JSON) |
| `Domain\ChkType.cs` | справочник типов + константы `ChkTypes` (6 значений) |
| `Domain\ChkStatus.cs` | справочник статусов + константы `ChkStatuses` (4 значения) |
| `Repository\Filter\ChkCheckFilter.cs` | фильтр Select (null = условие не применяется) |
| `Repository\ChkCheckRepository.cs` | Insert/Update/Delete/Select/Get |
| `Repository\ChkTypeRepository.cs` | Select (справочник — только чтение) |
| `Repository\ChkStatusRepository.cs` | Select (справочник — только чтение) |
Неймспейсы: `MT.Checks.Domain`, `MT.Checks.Repository`, `MT.Checks.Repository.Filter`
(папка/префикс слоя `Chk` в коде не используются — решение пользователя).
### Методы → функции БД
| Метод | Функция | Возврат |
|---|---|---|
| `ChkCheckRepository.Insert(ChkCheck)` | `chk.chk_check_insert` | id записи |
| `ChkCheckRepository.Update(ChkCheck)` | `chk.chk_check_update` | число строк (read-modify-write; guard Id=0) |
| `ChkCheckRepository.Delete(int)` | `chk.chk_check_delete` | число строк (guard id=0) |
| `ChkCheckRepository.Select(ChkCheckFilter)` | `chk.chk_check_select` | `IList<ChkCheck>` |
| `ChkCheckRepository.Get(int)` | `chk.chk_check_get` | `ChkCheck?` (guard id=0) |
| `ChkTypeRepository.Select()` | `chk.chk_type_select` | `IList<ChkType>` |
| `ChkStatusRepository.Select()` | `chk.chk_status_select` | `IList<ChkStatus>` |
Каждый метод — ровно одна хранимая функция, транспорт — fluent API Kit.Helpers
(`PrepareExecute<T>().AsStoredProcedure().AddParameter*().AddConverter(...)`),
конвертеры таблиц ответа — inline-лямбды.
## Слой БД (этот же прогон)
- `DB/chk/functions/*.sql` — 7 функций (параметры `p_*` по db-func-5);
- `DB/chk/func_specs/*.md` — 7 спецификаций с тест-планами (d-db-9);
- `DB/chk/tests/test_*.psql` — 7 psql-тестов (**не исполнялись** — нужна тестовая
база: `psql -d <база> -f DB/chk/tests/test_*.psql`);
- `DB/chk/functions.md` — статусы: функции `DEV`/«Функция»=`CODE`; «Репозиторий»=`REQ`
с примечанием (unit-тесты не создаются, `CODE` по d-dev-3 неприменим).
## Валидация
- `dotnet build MT.Checks.slnx` — **0 ошибок** (предупреждения NU1903/CS — из Kit.Core).
- DalTools `check` (с манифестом) — **1 нарушение c-dal-1, известный false positive**:
эвристика «SQL в строке C#» сканирует текст между строковыми литералами файла и
находит слово `Select` в сигнатуре метода `Select(ChkCheckFilter)` — имя метода
обязательно по правилу c-dal-5 (имена CRUD: Insert/Update/Delete/Select/Get).
Неустранимо в коде без нарушения стандарта; SQL в строках отсутствует.
## Спецификации тестов
`1-docs/2-core/chk/repository_tests/` — ChkCheckRepository.md (17 тестов),
ChkTypeRepository.md, ChkStatusRepository.md — утверждены как документация
ожидаемого поведения. Тестовый код по решению пользователя не создаётся
(см. `DB/chk/functions.md`, примечание к колонке «Репозиторий»).
## Известные ограничения и решения
1. **Транспорт `p_` vs `_`**: Kit.Helpers строит именованные аргументы `_<имя>`,
функции chk объявлены с `p_<имя>` (db-func-5; решение пользователя — сохранить
стандарт). До доработки Kit.Helpers (администратор Kit) вызовы репозиториев
против живой БД будут давать «function ... does not exist».
2. **Имена**: решение/проект `MT.Checks` — по указанию пользователя
(в `MT.Check.Core.Dal`/`MT.Checks.Test` отказ).
3. **Без тестов**: тестовый проект отсутствует; `dotnet test` из конвейера исключён.
4. Предупреждения сборки NU1903 (уязвимые пакеты Npgsql/SqlClient и др.) —
из Kit.Core, вне рамок навыка.
## Возобновление/завершение
После доработки Kit.Helpers достаточно проверить вызовы на тестовой БД
(спецификации вызовов не меняются). Статусы `DONE` — после утверждения
пользователем.