food-market/docs/sklad-port-20261005-progress.md
nns ef29acb0d1
Some checks are pending
Auto-tag / Create date-tag (push) Waiting to run
docs(port): stage retest + menu-walk 3/3 зелёный
Smoke stage:
- POST /purchases/invoices/upload-photos → 400 (не 404) без файлов
- mobile-catalog: 31 269 ШК, JSON 3.44 МБ → gzip 1.75 МБ, ETag работает,
  повтор с If-None-Match → 304
- stage-ui-menu-walk.spec.ts: 3/3 passed, 0 ошибок у admin/keeper/cashier

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-10-06 00:19:57 +05:00

88 lines
7.1 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.

# Порт улучшений накладных/инвентаризации из sklad (05.10.2026)
Источник: `~/ref/sklad` (read-only), handoff: `~/ref/sklad/docs/handoff-2026-10-05-nakladnye-inventarizaciya.md`,
диапазон коммитов `f455fa9..b7eb637` + `3ced706` (readme).
Правило: ничего из МойСклад — только наша БД (`Product`, `ProductBarcode`), наши документы
`Supply` (как InvoiceUpload-черновик→Supply) и `Inventory`, наши права
(`SuppliesEdit`, `InventoryEdit`, роли Кладовщик/Администратор).
## Задачи
- [x] 1. Серверное автосохранение правок накладной + индикатор + lines до черновика + refetchOnWindowFocus:false
- [x] 2. Общее поле скана с панелью «в какую строку?» (цветные сообщения, linesRef)
- [x] 3. CameraScanner без перезапуска + глобальный hardware keydown на страницах накл/приёмки/инв
- [x] 4. Фото-приёмка (PhotoSession + shots.ts + upload-photos + Claude photos + EAN + миниатюры)
- [x] 5. Хранение фото: удаление на draft/delete, 24h для брошенных; без KeepFilesDays
- [x] 6. «Файлы» и «камера» в обоих способах приёмки
- [x] 7. Excel/CSV через TableReader + шаблон xlsx в docs/
- [x] 8. Инвентаризация: камера + видимое поле + импорт Excel/CSV
- [x] 9. Каталог без простоев: индекс штрихкода ✓ (unique `IX_product_barcodes_OrganizationId_Code`),
gzip через `UseResponseCompression`, ETag/If-None-Match на `mobile-catalog`.
Синтетический замер 31 000 позиций: JSON 3.60 МБ → gzip 0.98 МБ (ratio 27%).
- [x] 10. Тесты: unit — EanTests (6), TableReaderTests (5), ClaudeCliInvoicePhotosParseTests (4),
InvoicePhotoRetentionTests (3). Build integration/e2e — зелёный.
- [x] 11. deploy-stage, ретест, docs/supply-inventory.md
## Решения владельца
- **Хранение фото:** НЕ копить. Удалять при создании Supply-черновика, при удалении InvoiceUpload,
и через 24 часа для брошенных «на проверке». Никакого `Invoices:KeepFilesDays`.
- **Модель Claude:** `claude-sonnet-5-5` (Haiku сочиняет данные).
- **Тайм-аут фото-пачки:** `Invoices:ClaudeCli:PhotoTimeoutSeconds` = 600 с.
- **Каталог:** наша БД, права `SuppliesEdit`/`InventoryEdit`.
- **gateway:** лимит запроса не трогал самостоятельно; если фото-пачка не пройдёт
на стенде по размеру — поднять `client_max_body_size` до 64M (сейчас, вероятно, 10M-32M).
## Что лежит где
### Backend
| Что | Файл |
|---|---|
| Контрольная цифра EAN | `src/food-market.api/Infrastructure/Invoices/Ean.cs` |
| Табличная читалка .xlsx/.csv + парсер шаблона | `src/food-market.api/Infrastructure/Invoices/TableReader.cs` |
| InvoiceLine +Photo/BarcodeSource/Note, PhotoHint, IInvoiceExtractor.ExtractPhotosAsync | `.../InvoiceModels.cs` |
| InvoiceMatcher.FindByBarcode | `.../InvoiceMatcher.cs` |
| InvoiceProcessor: ветки photos/excel/vision, MatchFromPhoto, SweepAbandonedPhotos, DeleteStored | `.../InvoiceProcessor.cs` |
| ClaudeCli ExtractPhotosAsync + BuildPhotosPrompt + FromPhotosJson; дефолт `claude-sonnet-5-5` | `.../ClaudeCliInvoiceExtractor.cs` |
| `POST /invoices/upload-photos` + `GET /invoices/{id}/photo/{n}`; apply/delete убирают папку | `Controllers/Purchases/InvoicesController.cs` |
| `POST /inventory/inventories/import` (xlsx/csv → {found, unknown}) | `Controllers/Inventory/InventoriesController.cs` |
| gzip response-compression + ETag на `mobile-catalog` | `Program.cs`, `InventoriesController.cs` |
### Frontend
| Что | Файл |
|---|---|
| Общий модуль ZXing TRY_HARDER + compress 2400/0.85 + fileToCanvas | `src/food-market.web/src/lib/shots.ts` |
| Полноэкранная пачка снимков (камера + готовые фото) | `.../components/PhotoSession.tsx` |
| CameraScanner: `onCodeRef` + эффект `[]` (нет перезапуска камеры) | `.../components/CameraScanner.tsx` |
| InvoicesPage: `Снять камерой / Загрузить файл / Сфотографировать листы / Фото товаров+накладная` | `.../pages/InvoicesPage.tsx` |
| InvoiceReviewPage: автосохранение 700 мс + индикатор + row-picker + linesRef + глобальный keydown + миниатюры | `.../pages/InvoiceReviewPage.tsx` |
| SupplyScanPage: глобальный keydown (аппаратный сканер вне фокуса) | `.../pages/SupplyScanPage.tsx` |
| InventoryMobilePage: сканер-поле на виду + импорт Excel/CSV + глобальный keydown | `.../pages/InventoryMobilePage.tsx` |
| Шаблон для поставщиков (xlsx, 9-я строка — заголовки) | `docs/supplier-invoice-template.xlsx` (`scripts/make_supplier_template.py`) |
### Тесты
| Что | Файл |
|---|---|
| EAN корректность + 1-digit error ловится | `tests/food-market.UnitTests/EanTests.cs` |
| xlsx/csv UTF-8/1251 + шаблон + .xls friendly error | `tests/food-market.UnitTests/TableReaderTests.cs` |
| JSON ответа фото-пачки + Note из comment + unmatched → Warnings | `tests/food-market.UnitTests/ClaudeCliInvoicePhotosParseTests.cs` |
| DeleteStored: файл, папка, идемпотентность | `tests/food-market.UnitTests/InvoicePhotoRetentionTests.cs` |
Пре-существующий отказ `ApiReferenceDocsJobTests.Scans_endpoints_with_doubly_nested_generic_return`
не связан с портом (Sprint 28, падал до изменений).
## Deploy и retest
- Commit `6b0fc68` → `~/deploy-stage.sh` → stage зелёный на https://test.admin.food-market.kz
- Smoke по новым endpoint'ам:
- `POST /api/purchases/invoices/upload-photos` без файлов → 400 «Нет снимков.» (ранее 404)
- `GET /api/inventory/inventories/mobile-catalog` под gzip:
JSON 3.44 МБ (31 269 ШК на Demo) → gzip 1.75 МБ, Content-Encoding: gzip, Vary: Accept-Encoding
ETag `"inv-cat-f7abeb59d181533a-all"`, Cache-Control `private, max-age=60`
- Повтор с `If-None-Match` → `HTTP/2 304`
- `tests/e2e/scenarios/stage-ui-menu-walk.spec.ts` на стенде — **3/3 passed, 3.7 min**,
0 4xx/5xx/toast.error/console.error у demo-admin (28 пунктов), demo-keeper (17), demo-cashier (7).
## Журнал
- 05.10.2026: порт выполнен в один проход; API build зелёный, web tsc+vite build зелёный,
18 новых unit-тестов зелёные. deploy-stage зелёный, menu-walk 3/3 зелёный.