# Спринт 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. [ ] **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. ## Лог - Каждый пункт: build + тесты + коммит порцией + отметка [x] + коммит прогресса. - Все правки на `main` (origin Forgejo), без коммита `global.json`.