# Спринт 3 — отчёты и аналитика (P1) Автономная работа. После каждого пункта: `dotnet build` (SDK 8.0.126), unit + integration тесты этого пункта, коммит порцией, отметка `[x]` здесь, коммит прогресса. Multi-tenant: все запросы фильтруются по `OrganizationId` через query filter `AppDbContext`. Каждый отчёт — отдельный e2e/integration на изоляцию orgA vs orgB. ## Чек-лист 1. [x] **P1-8 Отчёт «Продажи»** — `/api/reports/sales` с группировкой по период (день/неделя/месяц), товар, кассир, касса, способ оплаты; фильтры (от/до, магазин, группа товаров). Web `/reports/sales`: фильтр периода, табы по группировкам, экспорт CSV+XLSX. ✅ Реализация: проекция в плоский ряд на сервере + агрегация в C# (EF8 не переводит «distinct count» в group-проекции с nullable-ключами). `CsvHelper` + `ClosedXML`. Bonus: исправлен баг `RetailSalesController.Update` (DbUpdateConcurrency на свеже-созданном возврате). 5 интеграционных тестов. 2. [x] **P1-9 Отчёт «Остатки на дату»** — `/api/reports/stock` восстанавливает остатки на произвольную дату через журнал `StockMovement` (Σ движений до даты по продукту). Web `/reports/stock`: выбор даты, фильтр магазин/группа, экспорт. Edge: дата в будущем, дата раньше первой операции. ✅ Реконструкция через Σ `StockMovement.Quantity` где `OccurredAt ≤ date`. Стоимость — последний `UnitCost` движения до даты + fallback на `Product.Cost`. Edge'ы покрыты тестами: 5 интеграционных (today=current, before-first→empty, future=current, future-supply исключается на «сегодня», tenant-изоляция). 3. [x] **P1-10 Отчёт «Прибыль»** — `/api/reports/profit` = выручка − себестоимость по периодам/группам/товарам. Cost-snapshot уже есть в `RetailSaleLine` (через `UnitCost` movement'а). Защита от деления на ноль при нулевой выручке. ✅ Cost-snapshot — `Product.Cost` (скользящее среднее; точный FIFO потребует партий и вынесен из scope). Margin = profit/revenue·100, при revenue=0 возвращаем 0. Возврат вычитает и выручку, и COGS симметрично. 3 интеграционных. 4. [x] **P1-11 Отчёт «ABC-анализ»** — топ товаров по выручке за период, классы A/B/C по Парето (A=80%, B=15%, C=5% накопительной выручки). Параметр метрики (выручка/прибыль/маржа). Web с визуализацией. ✅ `GET /api/reports/abc?metric=revenue|profit|margin`. Граница A — `cumulativeShare ≤ 80`, B — `≤ 95`, C — остальное. Товары с неположительной метрикой исключаются. Web: цветные плашки класса + полоса накопительной доли. 4 интеграционных теста. 5. [ ] **P1-19 OpenAPI / Swagger** — `Swashbuckle.AspNetCore`, `/swagger/v1/swagger.json` в Development. Сгенерировать TS-клиент для food-market.web (`openapi-typescript`/`nswag`) и подключить для пары контроллеров как образец. ## Лог - Каждый пункт: build + тесты + коммит порцией + отметка [x] + коммит прогресса. - Все правки на `main` (origin Forgejo), без коммита `global.json`.