food-market/docs/supply-inventory-mobile-progress.md
nns 11b43aefe5 feat(s31): приёмка по фото — upload → Claude CLI → match → Supply Draft (1/8)
Backend-pipeline по образцу ~/ref/sklad/sklad/Invoices/, но документы
создаются в НАШЕЙ базе (Supply), не в МойСкладе.

Домен (food-market.domain/Purchases/InvoiceUpload.cs):
- InvoiceUpload: файл + статус (pending/parsing/needs_review/applied/failed)
  + распознанная шапка + ParsedJson (jsonb) + ссылка на созданный Supply.
- InvoiceAlias: запомненное соответствие (SupplierKey, SourceCode|
  SourceNameNorm) → ProductId. Два частично-уникальных индекса (filter
  SourceCode/SourceNameNorm IS NOT NULL).

Миграция Phase31a (идемпотентная ADD/CREATE IF NOT EXISTS; [Migration]+
[DbContext]).

Infrastructure/Invoices/:
- IInvoiceExtractor + ParsedInvoice/InvoiceLine DTO.
- ClaudeCliInvoiceExtractor — порт из sklad: 'claude -p --output-format
  json', промпт один-в-один (копировать все строки, null если не читается,
  barcode != supplier_code, сверка с «Итого»). Поддерживает local и
  Invoices:ClaudeCli:RemoteHost (ssh+scp+cleanup).
- InvoiceMatcher — barcode → alias → none. По названию НЕ ищем
  (sklad-урок: 2-5 ошибок из 10, подмена хуже пустой строки).
- InvoiceProcessor + InvoiceWorker (BackgroundService):
  pending → parsing → needs_review | failed. SweepAsync раз в час
  удаляет файлы старше Invoices:KeepFilesDays (default 7); строки
  остаются. Multi-tenant через IgnoreQueryFilters + явный OrgId.

Controller api/purchases/invoices:
- POST /upload (SuppliesEdit, 60 MB max): файлы в uploads/invoices/{yyyy-
  MM}/, запись в pending.
- GET /, GET /{id} — список + детали со строками.
- GET /{id}/file — отдать файл.
- DELETE /{id} — удалить запись + файл.
- PUT /{id}/lines — правки шапки + строк + авто-upsert InvoiceAlias
  для каждой строки с MatchedBy=manual|alias (два ключа: по code и по
  name_norm).
- POST /{id}/apply — создать Supply (Draft) с данным поставщиком/
  складом/валютой, статус → applied, SupplyId ссылается, файл удаляется.
- GET /product-by-barcode, /counterparty-search — помощь UI.

Build зелёный. Web + тесты — следующими шагами.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-10-04 21:30:02 +05:00

58 lines
3.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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-ключи не заводим.
- [ ] 2. **Экран проверки накладной.** Редактируемые строки, помощь в
сопоставлении с товаром (barcode → saved-mapping → ручной), создание
товара прямо из строки, проверка «итого», предложение создать
контрагента. Коммит → наш Supply (Draft).
- [ ] 3. **Приёмка сканированием.** Непрерывная камера, +1 к строке на
скан, последняя закупочная подставляется, неизвестный штрихкод →
предложить создать товар или отложить.
### Инвентаризация
- [ ] 4. **Инвентаризация с телефона.** Выбор склада + опц. группы,
офлайн-каталог в IndexedDB, прогресс сохраняется локально; скан /
ручной ввод; неизвестные штрихкоды в отдельный список.
- [ ] 5. **Расхождения видны сразу.** Учёт/факт/дельта в шт. и в деньгах,
фильтр «только расхождения». Частичная (по группе) НЕ обнуляет
непосчитанные; полная — явный выбор.
- [ ] 6. **Учётный остаток подставляется автоматически** (без ручной
кнопки «Пересчитать»): при создании bookQty = текущий Stock; при
добавлении позиции — тоже; перед проведением пересчёт + warning о
расхождениях с момента создания (продажи/приёмки). Если где-то
экспорт в МойСклад — там тоже явный расчётный остаток.
### Общее
- [ ] 7. Мобильная вёрстка 375×667 + PWA. Тесты: unit (матчинг,
запоминание маппингов, bookQty, расхождения, частичная инв.),
integration (накладная → Supply Draft; частичная инв. не трогает
другие товары; bookQty = текущий Stock), e2e на стенде.
- [ ] 8. `~/deploy-stage.sh`, ретест, `docs/supply-inventory.md` —
пользовательская инструкция.
## Правила
НЕ трогать: `~/ref/sklad` (только читать), прод admin.food-market.kz,
`global.json`, gateway nginx, POS WPF.
После каждого пункта: build + тесты + коммит порцией + `[x]`.