All checks were successful
Mostovik Backend CI/CD / Tests and lint (push) Successful in 9m37s
Mostovik Backend CI/CD / Build linux/amd64 release images (push) Successful in 4m18s
Mostovik Backend CI/CD / Deploy and verify internal main (push) Has been skipped
Mostovik Backend CI/CD / Deploy customer main (push) Has been skipped
Mostovik Backend CI/CD / Deploy dev (push) Successful in 1m48s
145 lines
13 KiB
Markdown
145 lines
13 KiB
Markdown
# Мостовик: интеграция frontend с backend, 13.09.2026
|
||
|
||
Передача Глебу. Проверен свежий frontend `origin/dev`
|
||
`c6f2ec787efef5617d6624ff83050589c3941f0d` после успешного SSH fetch через
|
||
штатный jump host. Рабочее дерево frontend не переключалось и не изменялось.
|
||
Backend-контракт: текущая поставка поверх
|
||
`c863563851642727045f58f16336aa96fb28308a`; основной контракт новых источников
|
||
описан в [registry-sources-ru.md](registry-sources-ru.md).
|
||
Документ не подтверждает публикацию backend/frontend на dev-стенде.
|
||
|
||
Все frontend-пути ниже начинаются с `src/pages/main/`.
|
||
|
||
**Уже подключено в свежем frontend dev:** оба новых view, таблицы и настройки
|
||
запуска, оба источника в фильтре истории и обе группы общей выгрузки. Это
|
||
подтверждено чтением `resolveSourceDetailView.ts`, `useScrapingCollectionCards.ts`,
|
||
`ReferenceDataSourcesExportCard.vue`, `updateHistoryLogs.ts` и исполнением resolver.
|
||
Старое заключение об отсутствии этих подключений к свежему dev не относится.
|
||
|
||
| Источник | Slug | Source | Source group | Record type |
|
||
| --- | --- | --- | --- | --- |
|
||
| Поддержка МСП | `sme-support-recipients-registry` | `fns_sme_support_recipients` | `government_support` | `sme_support_measure` |
|
||
| Бюджетный процесс | `budget-process-registry` | `budget_ubpandnubp` | `budget_process_registry` | `budget_registry_organization` |
|
||
|
||
**MV-1. Привести DTO и детали к фактическому MVP payload.**
|
||
Backend `src/apps/parsers/budget_registry.py:55` отдаёт нормализованные
|
||
`registry`, `classification`, `budget`, `address` и полный исходный `upstream`
|
||
в детали. Он не обещает нормализованные `legal`, `heads`, `contacts`, `activities`,
|
||
`authorities`, `successions`, `contracts`, `attachments`, `summary`,
|
||
`is_separate_division`. Однако frontend
|
||
`ui/SourceRecordDetail/BudgetProcessRegistryRecordDetail.vue:98` обращается к
|
||
`payload.legal.firm_name`, а
|
||
`model/source-record-detail/sourceRecordDetail.ts:400,414` — к
|
||
`payload.legal.legal_form` и `.heads.length`. На payload фактического normalizer
|
||
исполнение mapper завершилось TypeError `reading 'legal_form'`.
|
||
|
||
Backend `src/apps/parsers/sme_support.py:78` отдаёт `recipient_type` и
|
||
`sme_category` строковыми кодами, `support_form`/`support_kind` словарями,
|
||
`provider` с name/inn, `support_sizes` со значением и единицей. `violations` и
|
||
`regulatory_documents` сохраняют оригинальные XML-атрибуты. `provenance` и
|
||
нормализованного региона в payload нет. Frontend
|
||
`model/source-detail/smeSupportRecipients.ts:61,195` ожидает словарь категории
|
||
и теряет существующий код при выводе; detail mapper
|
||
`model/source-record-detail/sourceRecordDetail.ts:525` падает на
|
||
`payload.provenance.dataset_url` (TypeError воспроизведён).
|
||
|
||
Исправить перечисленные DTO, мапперы и detail renderer под этот контракт:
|
||
не читать несуществующие обязательные блоки; не заменять неизвестные признаки
|
||
значением «нет»; выводить подтверждённые код/значение/единицу, а не вымышленные
|
||
названия. Для бюджета полный исходный документ доступен в `payload.upstream`
|
||
детали/выгрузки. Для МСП читать реальные имена XML-атрибутов документов и
|
||
нарушений. Отдельно проверить экспорт полной карточки, использующий тот же mapper.
|
||
|
||
Список GET `/api/v2/organization-source-records/` возвращает `data` и
|
||
`meta.pagination`, detail GET `/{uid}/` — объект записи. Краткий payload
|
||
намеренно исключает `source_document_id`, `violations`, `regulatory_documents`,
|
||
`upstream`: за ними нужен detail-запрос. Отсутствие массива в списке не означает
|
||
отсутствие данных. Полный контракт: `docs/registry-sources-ru.md`.
|
||
|
||
**MV-2. Оставить только поддержанные сортировки и фильтры.**
|
||
Backend уже исправлен для составного `ordering` из существующего allowlist:
|
||
начальный запрос МСП `ordering=-record_date,-external_id` теперь проходит.
|
||
Каждый компонент отдельно валидируется; неизвестные поля остаются 400.
|
||
Дополнительные JSON-sort и специальные фильтры в MVP не добавлялись.
|
||
|
||
`model/source-detail/useSmeSupportRecipientsSourceTable.ts:76–135` посылает
|
||
неподдержанные sort по support_form, support_kind, support_until, provider,
|
||
region, sme_category, has_violation.
|
||
`model/source-detail/useBudgetProcessRegistrySourceTable.ts:93–129` — по
|
||
organization_type, establishment_kind, budget.level, address.region,
|
||
is_separate_division. Убрать sortable/orderingKey с этих заголовков до отдельного
|
||
согласования backend-расширения. Не подменять сортировку смыслово другим полем.
|
||
|
||
Допустимые простые поля для соответствующих колонок: `record_date`, `updated_at`,
|
||
`status`, `external_id`, `extension__organization__name` (также alias
|
||
`organization__name`), ИНН/ОГРН/ОКПО организации. Полный текущий allowlist находится
|
||
в `src/organizations/views.py:104`. Рекомендуемые defaults:
|
||
МСП `-record_date,-external_id`, бюджет `extension__organization__name`.
|
||
|
||
Бюджетная таблица на строках 252–259 отправляет `budget_level`,
|
||
`establishment_kind`, `has_procurement_permission`, `is_branch`,
|
||
`organization_type`, `region_code`: текущий backend эти шесть параметров
|
||
игнорирует. Скрыть/отключить соответствующие фильтры. Поддержаны общие
|
||
source/source_group/record_type, status, organization, search и период record_date.
|
||
Колонка бюджета «Обновлено» показывает `updated_at`, но её dateFilterKey —
|
||
`record_date` (строки 120–122): согласовать подпись/значение колонки с полем
|
||
фильтра. Это разные даты.
|
||
|
||
**MV-3. Завершать polling по статусу и считать всю группу задач.**
|
||
`model/sources/useSourceCardRefreshTracking.ts:128–153` по-прежнему считает
|
||
progress=100 завершением и успехом даже для status=running или status=error
|
||
с пустым error. Оба результата воспроизведены. Статус jobs API —
|
||
`running`, `success`, `error`; 100% не доказывает завершение. Terminal и success
|
||
определять по статусу; ошибка должна оставаться ошибкой.
|
||
|
||
Строки 180–203 делят сумму только на уже полученные числовые ответы. Если из
|
||
двух task_ids первая задача ответила 100, а вторая временно недоступна,
|
||
расчёт показывает 100; после ответа второй с 0 даёт 50. Свежий
|
||
`ui/SourceDetailPage/components/useSourceDetailRefresh.ts:107–128` уже удерживает
|
||
показанный максимум: пользователь теперь может видеть преждевременные 100,
|
||
а не снижение до 50. Исправить знаменатель на фиксированный полный task_ids,
|
||
сохранять последние значения при временной ошибке polling. После перезагрузки,
|
||
если полный набор неизвестен, использовать агрегированный backend progress:
|
||
текущий приоритет среднего только activeTasks на строках 73–102 исключает
|
||
завершённые части группы. Строки 87–88 и 130–133 показывают success/«Обновлено»
|
||
для любого неактивного состояния: отдельно отображать error и idle.
|
||
|
||
202 с task_ids уже поддерживается (`queued` вместо `accepted` не мешает).
|
||
При 409 `source_refresh_running` показать сообщение и перечитать карточку/
|
||
статусы; не включать фиктивный polling без IDs. Общий error envelope совместим,
|
||
отдельного восстановления карточки на 409 в hook пока нет.
|
||
|
||
**История и общая выгрузка уже согласованы по исходникам.**
|
||
`model/update-history/useUpdateHistoryTable.ts:55,67,148` теперь отправляет
|
||
`date_from/date_to` и в список, и в экспорт, включает период в query key и
|
||
сбрасывает страницу. `lib/update-history/updateHistoryLogs.ts:46` корректно
|
||
передаёт один день как одинаковые границы. Старый локальный date filter удалён.
|
||
Это регрессия для приёмки, не новая задача реализации. Проверить >100 строк,
|
||
совпадение периода/количества CSV и список, границу суток: backend фильтрует
|
||
updated_at в UTC, browser formatter использует локальную зону.
|
||
|
||
Обе новые группы уже есть в `ReferenceDataSourcesExportCard.vue:86–93`.
|
||
Общий ticket 201 / native POST download совместим. В
|
||
`model/source-detail/exportSourceDetailTable.ts:111` новые таблицы остаются
|
||
на локальном CSV fallback. Если кнопка обещает полный реестр, подключить
|
||
серверную выгрузку; если только текущую страницу — явно обозначить объём.
|
||
Карточки/списки относятся к ОПК, полная выгрузка — ко всем сопоставленным
|
||
организациям справочника; эти счётчики могут отличаться по контракту.
|
||
|
||
## Проверки и критерии готовности
|
||
|
||
Составной allowlisted ordering уже исправлен на backend: 29 API-тестов прошли,
|
||
Ruff и `git diff --check` прошли. Несуществующие payload-sort поля дают 400.
|
||
Node probes свежих frontend-функций воспроизвели ошибки деталей (`legal_form`,
|
||
`dataset_url`), потерю кода категории и ошибки определения terminal/progress.
|
||
Payload для деталей получен фактическими backend normalizers без сети и БД;
|
||
это не полный Vue/browser тест. Frontend build/Vitest/E2E здесь не запускались.
|
||
|
||
Готовность после MV-1–MV-3: оба реальных API списка и детали открываются без
|
||
TypeError; отсутствующие поля не подменяются утверждением «нет»; доступны только
|
||
работающие filters/sorts; running100 продолжает polling, error100 остаётся
|
||
ошибкой; две задачи с задержанным ответом не показывают преждевременные 100%;
|
||
после reload сохраняется прогресс всей группы. Проверить 409/refetch, экспорт
|
||
карточки, серверный период истории и настоящий ZIP общей выгрузки. Использовать
|
||
fixtures реального backend и стенд без frontend mock-режима.
|