# Спринт 4 (частичный) — POS Sync API + observability

Автономная работа. WPF/POS-UI (`food-market.pos`, net8.0-windows) на Linux
не собирается — пропускаем. Здесь только API-сторона синхронизации
и метрики.

После каждого пункта: `dotnet build` (SDK 8.0.126), unit + integration
тесты, коммит порцией, отметка `[x]`, коммит прогресса.

## Чек-лист

1. [x] **P1-12a Контракты POS в food-market.shared** — DTO `ProductSyncDto`,
   `PriceSyncDto`, `StockSyncDto`, `CounterpartySyncDto`, `PosSaleBatchDto`
   (с idempotency-key). Версионирование через namespace v1.
   ✅ `src/food-market.shared/Pos/V1/SyncDtos.cs` — все DTO как record'ы
   с required-полями, конверты `PosSyncResponse` и `PosSaleBatchResponse`,
   двойная идемпотентность (batch IdempotencyKey + per-sale ClientSaleId).
   3 unit-теста на round-trip сериализации.
2. [x] **P1-12b POS Sync API** — `GET /api/pos/sync?since={iso8601}` возвращает
   изменения после ts (товары, цены, остатки, контрагенты). `POST /api/pos/sales`
   принимает батч продаж с idempotency-key (повторный запрос возвращает прежний
   результат без дублей). Multi-tenant: POS-токен ↔ один магазин.
   ✅ `/api/pos/v1/sync` и `/api/pos/v1/sales` (URL-versioned). Двойная
   идемпотентность: `PosBatchAck` (unique idx OrgId+Key, race ловится 23505)
   + ClientSaleId через маркер `pos:GUID32` в `RetailSale.Notes`. Pre-flight
   на остатки — недостающие позиции попадают в `Failed`, остальные проводятся.
   7 интеграционных тестов.
3. [x] **P1-17 Метрики Prometheus** — `prometheus-net.AspNetCore`, `/metrics`.
   `http_requests_total`, `http_request_duration_seconds`,
   `db_query_duration_seconds` (через EF interceptor), бизнес:
   `sales_posted_total`, `supplies_posted_total`, `documents_error_total`.
   `docs/observability.md` с образцом Grafana dashboard.
   ✅ `prometheus-net.AspNetCore@8.2.1` + `DbMetricsInterceptor` (singleton,
   EF Core `DbCommandInterceptor`). Бизнес-счётчики в `AppMetrics` —
   инкрементятся в успешных Post'ах Sales/Supplies/POS. Endpoint
   `/metrics` без auth. `docs/observability.md` с scrape-конфигом,
   образцом Grafana и alert-rules (HighErrorRate / DbSerializationContention /
   NoSalesIn30Min). 3 интеграционных теста.

## Итог

**Все 3 пункта выполнены.** Спринт 4 (POS Sync API + observability) завершён 2026-05-28.

Сводка:
- **P1-12a Контракты POS** — `food-market.shared/Pos/V1/SyncDtos.cs`, версионирование
  через namespace, двойная идемпотентность, 3 unit-теста.
- **P1-12b POS Sync API** — `/api/pos/v1/sync` + `/api/pos/v1/sales` с batch-
  идемпотентностью (`PosBatchAck`) + per-sale (`ClientSaleId` через `Notes`).
  7 интеграционных тестов.
- **P1-17 Prometheus** — `/metrics`, EF interceptor, бизнес-счётчики,
  `docs/observability.md` с Grafana/alerts. 3 интеграционных теста.

**Сборка:** зелёная.
**Тесты:** 27 unit + 59 integration (49 sprint3 + 7 POS + 3 Metrics) **зелёные**.
**Web:** не трогали в этом спринте.

### POS-side notes
WPF/POS UI (`food-market.pos`, net8.0-windows) собирается только на Windows
с Windows SDK — оставлен как отдельный шаг на Windows-машине. Сервер
готов принимать кассу через `/api/pos/v1/*`.

### Что не сделано в спринте (требует внешних участников)
- ОФД-интеграция (нужен оператор данных — `Транском`/`Касса24`);
- MoySklad webhook-токены (production-grade приёмка нотификаций);
- Прод-деплой stage→prod (нужен пользователь для проверки stage сначала);
- WPF/POS UI (Windows-машина с SDK).

## Лог

- Каждый пункт: build + тесты + коммит порцией + отметка [x] + коммит прогресса.
- Все правки на `main` (origin Forgejo), без коммита `global.json`.
