# Спринт 6 — технический долг + 2FA Автономная работа. После каждого пункта: `dotnet build` (SDK 8.0.126), unit + integration тесты, коммит порцией, отметка `[x]`, коммит прогресса. Не ломать auth. НЕ трогать global.json/POS/nginx/ОФД. ## Чек-лист 1. [x] **TD-6 Concurrency-токены на документах** — `RowVersion` (PostgreSQL `xmin` через `IsRowVersion`) на Supply/Demand/RetailSale/Transfer/Inventory. Миграция. EF concurrency check. 409 при конфликте. Тест: два параллельных PUT → один 200, другой 409. ✅ Postgres `xmin` (system column, без миграции) через `UseXminAsConcurrencyToken` для 5 документов + `IVersionedEntity`-marker. SuppliesController PUT принимает `Xmin`, сверяет, 409 при mismatch. Bonus: Supply.Update перешёл на ExecuteDelete+AddRange (тот же fix что в RetailSale). 2 интеграционных теста. 2. [x] **TD-2 FluentValidation** — `FluentValidation.AspNetCore`, validator'ы для SupplyInputDto/RetailSaleInputDto/ProductInputDto/CounterpartyInputDto/ EmployeeInputDto. Auto-register. Тесты на каждый. ✅ Не используем deprecated `FluentValidation.AspNetCore` — текущая рекомендация: `AddValidatorsFromAssemblyContaining()` + кастомный `ValidationFilter` (IAsyncActionFilter). На неуспех → 400 ValidationProblemDetails. 5 validator'ов, 16 unit-тестов, 70 integration зелёных без регрессий. 3. [ ] **TD-4 Структурные log-fields в Serilog** — `LogContext.PushProperty` в middleware: OrgId, UserId, CorrelationId. Бизнес-логи (Supply.Post, Sale.Post) — структурно. `docs/logging.md`. 4. [ ] **TD-1 CQRS partial (MediatR)** — `CreateSupplyCommand`, `PostRetailSaleCommand`, `GetSalesReportQuery`. Показать паттерн, не полный рефакторинг. Тесты на handlers. 5. [ ] **P2-4 2FA для админов (TOTP)** — `AuthenticatorTokenProvider`, endpoints `/api/me/2fa/enroll`, `/api/me/2fa/verify`, `/api/me/2fa/disable`. Опционально для Admin+SuperAdmin. При логине с включённым 2FA — два шага. ## Лог - Каждый пункт: build + тесты + коммит порцией + отметка [x] + коммит прогресса. - Все правки на `main` (origin Forgejo), без коммита `global.json`.