food-market/docs/sprint22-progress.md
nns b6f3c55d81
Some checks are pending
Auto-tag / Create date-tag (push) Waiting to run
docs(s22): итог — 7/7 ✓ + 10/10 endpoint smoke + ARCHITECTURE финал
Последний автономный спринт. После этого watchdog молчит — все
оставшиеся задачи требуют user-decisions / credentials / Windows-машины.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-07 23:14:42 +05:00

125 lines
8.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Sprint 22 — data tooling: export/import, schema docs, anonymized dump
Цель: финальный спринт автономной работы. GDPR-ready export, 1С-import,
схема-документация, audit-export, anonymized stage dump, MoySklad status
endpoint, итоговый ARCHITECTURE.
Старт: 2026-06-07 (после Sprint 21). Исполнитель: Claude Opus 4.7.
**Последний автономный спринт — после очередь пустая, watchdog молчит.**
## Принципы
- Все export/import — multi-tenant строго (нельзя выгрузить чужое).
- Долгие операции — Hangfire job + status polling (не блокировать HTTP).
- НЕ трогать: `global.json`, prod admin.food-market.kz, POS WPF.
## Чек-лист
- [x] **1. GDPR-export организации** — domain `OrgExport` (Phase22a:
jsonb-like fields, unique download token). `POST /api/org/export`
202 + id; Hangfire `OrgExportJob` собирает ZIP с JSON каждой сущности
(organizations, stores, retail-points, employees, product-groups,
products, prices, barcodes, counterparties, stocks, stock-movements,
supplies, supply-lines, retail-sales, org-audit-log + README.md +
metadata.json) через `IObjectStorage`, генерирует 64-hex token,
expires 24h, email-notify. `GET /api/org/export[/{id}]` + `GET
/api/org/export/download/{token}` (anonymous, token-protected).
- [x] **2. CSV-импорт из 1С**`POST /api/catalog/products/import/1c-csv`:
Content-Type charset auto-detect (UTF-8 BOM / Windows-1251 / другие),
разделитель `;` или `,` (по большинству в header'е), русские
заголовки (Артикул/Наименование/Единица/Цена/Группа/Штрихкод) или
английские. Unit-коды нормализуются (шт/кг/г/л/мл/упак/etc). Делегирует
на универсальный `ImportCsv` (одна транзакция, multi-tenant query-filter).
`docs/imports.md` с примером + curl.
- [x] **3. Anonymized prod dump**`deploy/anonymize-prod.sh`:
- pg_dump прода через ssh+docker exec в кастом-формат
- pg_restore во временную локальную БД `food_market_anon_$$`
- UPDATE AspNetUsers (email→`user{N}@example.kz`, phone→`+7700111{N:04}`,
PasswordHash→тестовый, SecurityStamp/ConcurrencyStamp→random)
- UPDATE employees (имена→`Тестов{N}`, контакты)
- UPDATE counterparties (BIN/IIN→синтетические 12-цифр, контакты)
- UPDATE organizations (MoySkladToken=NULL, OwnerTelegramChatId=NULL)
- REVOKE OpenIddictTokens
- TRUNCATE audit-логи
- pg_dump → gz файл
- DROP temp-БД через trap
- [x] **4. DB schema auto-docs**`DbSchemaDocsJob` weekly воскр 05:00 UTC:
читает `information_schema` (НЕ EF-модель — источник правды = БД),
генерит `db-schema-generated.md` с таблицами/колонками/FK + mermaid
ER-диаграмма (топ-20 таблиц по числу FK). Скрывает AspNet*/OpenIddict*.
- [x] **5. Audit-log export API**`POST /api/admin/audit-log/export?format=csv|jsonl`:
стримит через `AsAsyncEnumerable().WithCancellation(ct)` без полной
материализации. UTF-8 BOM для CSV (Excel-RU). Фильтры:
from/to/entityType/userId. Multi-tenant через query-filter.
- [x] **6. MoySklad sync-status**`GET /api/moysklad/sync-status`:
`{ configured: bool, lastSuccessAt, errorCountLast7Days, pendingCount,
byKind: { products: KindStatus, counterparties: KindStatus } }`.
Stub-режим (`configured=false`) если `Organization.MoySkladToken` пуст.
- [x] **7. Final ARCHITECTURE.md** — обновил с разделами:
- Sprint 13-22 changes (быстрая сводка) — добавил строки 16-22
- **Реализовано полностью** — 9 пунктов про backend / catalog /
reports / background / observability / a11y / tests / web / POS / DevOps
- **Scaffolding (готово к подключению, но не активно)** — таблица
с 9 пунктами: SSO Google/Microsoft, 3 ОФД-провайдера, MoySklad,
Telegram-alerts, Yandex.Metrika/GA4, SMTP, MinIO. Каждый row
указывает «что нужно от user'а».
- **Не реализовано** — 6 пунктов (прод-деплой, SSO callback flow,
KZ-перевод, POS Windows-тест, down-migrations, public-site SEO).
- Актуальная файловая структура.
## Журнал
### 2026-06-07 старт
Sprint 21 закрыт (7/7 ✓). Поехали по data tooling — финальный sprint.
### 2026-06-07 итог
Все 7 пунктов ✓. Stage deploy + retest:
**Hotfixes в процессе:**
1. `OrgExportJob.WriteCollection<T>` нужен `where T : class` (CS0452 на `.AsNoTracking()`).
2. Phase22a миграция забыла `UpdatedAt` (унаследовано от Entity); первый POST упал 500 «column "UpdatedAt" does not exist». Добавил `ADD COLUMN IF NOT EXISTS` в миграции + сделал ALTER на stage вручную.
3. 1C-CSV import возвращал «не найдена колонка Наименование» — `DetectEncoding` падал на Windows-1251 для UTF-8 без BOM. Теперь сначала смотрит `Content-Type charset=…`, потом BOM, потом win-1251.
**Sprint 22 endpoint smoke** (10 проверок, все ✓):
| Endpoint | Статус | Результат |
|---|---|---|
| POST /api/org/export | 202 | Pending (id выдан) |
| GET /api/org/export/{id} (polling) | 200 | Status=Ready после 1.7с |
| GET /api/org/export/download/{token} | 200 | 5254 bytes ZIP, application/zip |
| GET /api/org/export (list) | 200 | count=1 |
| POST /api/catalog/products/import/1c-csv | 200 | created=2 (русский заголовок ✓) |
| POST /api/admin/audit-log/export?format=csv | 200 | 2943 bytes streaming |
| POST /api/admin/audit-log/export?format=jsonl | 200 | 4280 bytes streaming |
| GET /api/moysklad/sync-status | 200 | `configured=false` (stub-режим) |
| stage-smoke scenarios | — | 5/5 ✓ |
| stage-audit-log scenarios | — | 7/7 ✓ |
**Hangfire-job registered**: `recurring-job:db-schema-docs`
`DbSchemaDocsJob.GenerateAsync` cron `0 5 * * 0` (вс 05:00 UTC).
## Итог
Все 7 пунктов ✓. Локальные цифры:
- **GDPR-export**: 5KB ZIP за 1.7с для пустой org'и; для реальной org'и
с 1000 товаров + 10k движений — оценка <30с.
- **1C-CSV import**: 2 строки за <100ms, отдельный endpoint без
дублирования транзакционной логики.
- **Anonymize**: bash-скрипт, тестировал bash -n; реальный прогон
невозможен на dev-vm без доступа на прод.
- **DB schema docs**: cron зарегистрирован, первый прогон в вс 05:00 UTC.
Файл `/app/db-schema-generated.md` пока не создан (cron ещё не сработал).
- **Audit export**: streaming через AsAsyncEnumerable; на 5KB ничего
тяжёлого не видно, но архитектурно готов к миллионам строк.
- **MoySklad status**: 8ms ответ, stub-режим корректно отдаёт `configured=false`.
- **ARCHITECTURE.md**: 470 строк, секции «Реализовано» / «Scaffolding»
/ «Не реализовано» это **финальный документ** для передачи системы.
## Заключение (после 22 спринтов)
**Все автономные задачи закрыты.** Дальнейшее автономное добавление
work-for-the-sake-of-work без явного запроса юзера. Touch'аем
`~/.fm-watchdog/DONE` и ждём пока user не вернётся с конкретным
запросом, требующим внешних решений: prod-deploy, OAuth-keys, ОФД-keys,
Windows-тестирование POS, kz-перевод, реальный SMTP-провайдер.