diff --git a/docs/sprint22-progress.md b/docs/sprint22-progress.md index 50dad80..e73cf24 100644 --- a/docs/sprint22-progress.md +++ b/docs/sprint22-progress.md @@ -15,21 +15,110 @@ endpoint, итоговый ARCHITECTURE. ## Чек-лист -- [ ] **1. GDPR-export организации** — `POST /api/org/export` для админа, - Hangfire job, ZIP с JSON каждой сущности, signed URL 24h, email-notify. -- [ ] **2. CSV-импорт каталога из 1С** — `POST /api/catalog/import/1c-csv`, - preview → транзакция, multi-tenant. docs/imports.md. -- [ ] **3. Anonymized stage dump** — `deploy/anonymize-prod.sh`: - pg_dump + PII-обфускация (email/phone/passwords/IIN). -- [ ] **4. DB schema auto-docs** — Hangfire weekly: `docs/db-schema.md` - с mermaid ER-диаграммой. -- [ ] **5. Audit-log export API** — `POST /api/admin/audit/export` - csv/jsonl streaming, multi-tenant. -- [ ] **6. MoySklad sync-status** — `GET /api/moysklad/sync-status`, - stub если не настроено. -- [ ] **7. Final ARCHITECTURE** — итоговый `docs/ARCHITECTURE.md`. +- [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` нужен `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-провайдер.