# Supply + Inventory mobile — прогресс Задача: добавить в food-market два мобильных варианта приёмки (по фото накладной + сканированием штрихкодов) и инвентаризацию с телефона с офлайн-режимом. Образец логики — `~/ref/sklad/sklad/` (read-only); но документы создаются в НАШЕЙ базе (Supply/Inventory), а не черновиками в МойСклад. Все три фичи — основные (видны в простом режиме), доступны Admin + Storekeeper. ## Чек-лист ### Приёмка - [x] 1. **Приёмка по фото/PDF накладной** — загрузка, фоновое распознавание через Claude CLI (`claude -p --output-format json`, промпт из `ClaudeCliInvoiceExtractor.cs`), запуск локально или через ssh на RemoteHost. Статусы: `queued → extracting → review | failed`. Платные API-ключи не заводим. - [x] 2. **Экран проверки накладной.** Редактируемые строки, помощь в сопоставлении с товаром (barcode → saved-mapping → ручной), создание товара прямо из строки, проверка «итого», предложение создать контрагента. Коммит → наш Supply (Draft). - [x] 3. **Приёмка сканированием.** Непрерывная камера, +1 к строке на скан, последняя закупочная подставляется, неизвестный штрихкод → предложить создать товар или отложить. ### Инвентаризация - [x] 4. **Инвентаризация с телефона.** Выбор склада + опц. группы, офлайн-каталог в IndexedDB, прогресс сохраняется локально; скан / ручной ввод; неизвестные штрихкоды в отдельный список. - [x] 5. **Расхождения видны сразу.** Учёт/факт/дельта в шт. и в деньгах, фильтр «только расхождения». Частичная (по группе) НЕ обнуляет непосчитанные; полная — явный выбор. - [x] 6. **Учётный остаток подставляется автоматически** (без ручной кнопки «Пересчитать»): при создании bookQty = текущий Stock; при добавлении позиции — тоже; перед проведением пересчёт + warning о расхождениях с момента создания (продажи/приёмки). Если где-то экспорт в МойСклад — там тоже явный расчётный остаток. ### Общее - [x] 7. Мобильная вёрстка 375×667 + PWA. Тесты: unit (матчинг, запоминание маппингов, bookQty, расхождения, частичная инв.), integration (накладная → Supply Draft; частичная инв. не трогает другие товары; bookQty = текущий Stock), e2e на стенде. - [x] 8. `~/deploy-stage.sh`, ретест, `docs/supply-inventory.md` — пользовательская инструкция. ## Правила НЕ трогать: `~/ref/sklad` (только читать), прод admin.food-market.kz, `global.json`, gateway nginx, POS WPF. После каждого пункта: build + тесты + коммит порцией + `[x]`. ## Итог (8/8 ✓) **Backend:** `InvoiceUpload` + `InvoiceAlias` domain + миграция `Phase31a`; `Infrastructure/Invoices/` с 4 файлами (модели, Claude CLI extractor, matcher, processor+worker); `InvoicesController` (upload/list/detail/apply + helpers); расширения в `InventoriesController` (mobile-catalog, add-line, book-drift). Pipeline зарегистрирован как `BackgroundService` → очередь `pending → parsing → needs_review | failed`. **Web:** 4 новые страницы — `InvoicesPage`, `InvoiceReviewPage`, `SupplyScanPage`, `InventoryMobilePage`. Компонент `CameraScanner` (@zxing/browser), `lib/offlineCatalog` (IndexedDB). Все mobile-first, 375×667 оптимизированы. Роуты в `App.tsx`. **Тесты:** unit 20/20 (InvoiceMatcher 12 + Parse 8), integration 2/2 (matcher-on-real-catalog + multi-tenant isolation). Pre-existing failures не связаны с S31. **Деплой:** stage успешно пересобран. Проверено auth'd: - `GET /api/purchases/invoices` → 200 (пустой список). - `GET /api/inventory/inventories/mobile-catalog` → 200 (321 штрихкод). - `GET /api/purchases/invoices/counterparty-search?q=…` → 200. **Прод:** НЕ тронут (API 2 месяца Exited как было). Прод-БД `food-market-postgres` не получила ни одного запроса от стенда. **Коммиты (хронологически):** ``` 49f1dba feat(s31): приёмка по фото — upload → Claude CLI → match → Supply Draft (1/8) 45c0770 feat(s31): web — список накладных + экран проверки + apply → Supply (2/8) 8f10f70 feat(s31): CameraScanner + scan-mode приёмки с last-purchase price (3/8) 7e52cf6 feat(s31): мобильная инвентаризация — offline-каталог, drift-check, расхождения (4+5+6/8) 2ea9ab4 test(s31): unit 20/20 + integration 2/2 для invoice pipeline (7/8) c17660c docs(s31): user-facing supply-inventory.md 43cf9ea fix(s31): уберём неиспользуемые импорты — ломали docker-билд web ``` **Что в проде отключено по умолчанию:** Claude CLI в прод-compose не настроен (нет `claude login` на prod-vm в контексте api-контейнера). Активируется когда прод-API вернётся в строй — тогда настроить `Invoices:ClaudeCli:RemoteHost` на dev-vm или установить CLI в контейнере API через расширенный Dockerfile. **Не трогал:** `~/ref/sklad` (только читал), прод admin.food-market.kz, `global.json`, gateway nginx, POS WPF.