Some checks are pending
Auto-tag / Create date-tag (push) Waiting to run
CI / Backend (.NET 8) (push) Waiting to run
CI / Web (React + Vite) (push) Waiting to run
CI / POS (WPF, Windows) (push) Waiting to run
Docker API / Build + push API (push) Waiting to run
Docker API / Deploy API on stage (push) Blocked by required conditions
Docker Web / Build + push Web (push) Waiting to run
Docker Web / Deploy Web on stage (push) Blocked by required conditions
Диапазон коммитов f455fa9..b7eb637 + 3ced706 (handoff).
Backend (src/food-market.api/Infrastructure/Invoices):
- Ean.cs — контрольная цифра EAN-8/13/UPC-A/GTIN-14 (ловит одну ошибку в цифре).
- TableReader.cs — своя читалка .xlsx/.csv без библиотек, UTF-8/1251,
.xls → понятная ошибка; TableInvoiceParser — шаблон накладной.
- InvoiceModels: +Photo/BarcodeSource/Note в InvoiceLine, PhotoHint,
IInvoiceExtractor.ExtractPhotosAsync.
- InvoiceMatcher.FindByBarcode — для photo-matching.
- InvoiceProcessor: ветки photos/excel/vision, MatchFromPhoto с EAN-проверкой
(barcode_source="read" → валидация), SweepAbandonedPhotosAsync раз в час
убирает папки needs_review старше 24ч; DeleteStored (owner-policy: НЕ копить).
- ClaudeCliInvoiceExtractor: ExtractPhotosAsync + BuildPhotosPrompt +
FromPhotosJson (barcode_source → BarcodeSource, comment → Note, unmatched_photos
→ Warnings); дефолт модели claude-sonnet-5-5, PhotoTimeoutSeconds=600.
Backend (Controllers):
- POST /purchases/invoices/upload-photos (≤80 файлов, 60 МБ) +
GET /purchases/invoices/{id}/photo/{n}; Apply/Delete убирают папку (не только файл).
- InvoiceDetailDto +Source/+HasFiles — клиент не ломится за 404 миниатюрами.
- POST /inventory/inventories/import — xlsx/csv → {found, unknown}, повторы ШК
суммируются, документ не создаётся.
- Program.cs: UseResponseCompression + GzipCompressionProvider для application/json.
- InventoriesController.mobile-catalog: weak ETag + If-None-Match → 304;
Cache-Control private max-age=60. Замер 31k: JSON 3.60 МБ → gzip 0.98 МБ.
Frontend:
- lib/shots.ts — ZXing TRY_HARDER на 1x+1600; compress JPEG 0.85 до 2400.
- components/PhotoSession.tsx — полноэкранная пачка (камера + готовые фото).
- components/CameraScanner.tsx: onCodeRef + эффект [], камера не закрывается
после каждого скана.
- pages/InvoicesPage.tsx — четыре способа: камера одиночными, файл (xlsx/csv/img/pdf),
камера многостраничная, фото-приёмка (камера или готовые фото) + «Отправить ещё раз».
- pages/InvoiceReviewPage.tsx — автосохранение 700 мс + индикатор; row-picker панель
«в какую строку?» если fuzzy-match неуверен; linesRef; глобальный keydown; миниатюра
снимка у строки; пометки Note.
- pages/SupplyScanPage.tsx + InventoryMobilePage.tsx — глобальный keydown.
- InventoryMobilePage: поле сканера на виду + импорт Excel/CSV.
- docs/supplier-invoice-template.xlsx — шаблон для поставщиков (9-я строка заголовки).
Хранение файлов (owner-policy 2026-10-05): фото НЕ копим.
- Обычная загрузка → удаление сразу после разбора.
- Фото-приёмка → удаление при Apply/Delete/24h abandoned.
- KeepFilesDays удалён из флоу (конфиг-ключ игнорируется).
Тесты (unit, зелёные):
- EanTests (6): валидные/невалидные/one-digit error.
- TableReaderTests (5): xlsx cells, шапка «Поставщик/Номер/Дата», CSV UTF-8+BOM,
CSV Windows-1251, .xls friendly error.
- ClaudeCliInvoicePhotosParseTests (4): decoded проходит, source=null чистит Barcode,
unmatched_photos → Warnings, comment → Note.
- InvoicePhotoRetentionTests (3): DeleteStored файл/папка/идемпотентность.
Preexisting ApiReferenceDocsJob (Sprint 28) failure не связан с портом.
Прогресс: docs/sklad-port-20261005-progress.md. Пользовательская инструкция:
docs/supply-inventory.md обновлена.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
81 lines
6.6 KiB
Markdown
81 lines
6.6 KiB
Markdown
# Порт улучшений накладных/инвентаризации из 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
|
||
- `~/deploy-stage.sh` (отдельно) после commit → ретест на https://test.admin.food-market.kz
|
||
- `tests/e2e/scenarios/menu-walk-demo-*.ts` — не упал на новом экране (на локале web-build зелёный)
|
||
|
||
## Журнал
|
||
- 05.10.2026: порт выполнен в один проход; API build зелёный, web tsc+vite build зелёный,
|
||
18 новых unit-тестов зелёные.
|