Commit graph

7 commits

Author SHA1 Message Date
nns ab416d720c feat(inventory): отмена результатов не удаляет проведённые документы
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
Было: unpost при проведённых связанных Оприходовании/Списании молча снимал их с
проведения и УДАЛЯЛ (204, номера исчезали). Хуже МойСклада, теряется история.

- unpost при ПРОВЕДЁННЫХ связанных → 409 с номерами О-/С- и текстом «Сначала
  отмените результаты». Если связанные только черновики — удаляем черновики
  (не влияли на остатки) и возвращаем инвентаризацию в черновик.
- Новое POST /{id}/cancel-results (InventoryEdit + EnterEdit/LossEdit): снимает
  связанные Оприходование/Списание с проведения, НЕ удаляя (остаются черновиками,
  видны в «Связанных документах», истории, аудите); остатки возвращаются; для
  Оприходования — гард на уход остатка в минус. Аудит InventoryCancelResults.
- Веб: кнопка «Отменить результаты» (при проведённых связанных) + диалог со
  списком документов и влиянием на остатки; «Вернуть в черновик» переформулирован.
- docs/mobile-api.md: unpost больше не удаляет проведённые (409), добавлен cancel-results.
- Тесты: integration (409 + cancel-results оставляет черновики + повторное
  проведение) + full-chain шаг 10.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 00:51:00 +05:00
nns 0d44cdabaf feat(inventory): модель МойСклада — инвентаризация не двигает остатки
Замечание владельца: инвентаризация стояла «Проведено» без оприходования/списания.
Переделано по МойСкладу (docs/inventory-moysklad-reference.md):

- InventoryStatus Draft→Completed (было Posted; значение 1 то же). Инвентаризация
  остатки НЕ двигает: endpoint complete замораживает строки без движений.
- create-enter (излишки diff>0) / create-loss (недостача diff<0) — создают черновики
  Оприходования/Списания со строками разницы + ссылка InventoryDocId. Повторно нельзя (409).
- Кнопки «Применить результаты» НЕТ (поправка Mac-Claude — не одобрено владельцем).
- unpost («вернуть в черновик») отменяет проведение и удаляет связанные документы.
- post/unpost — временная совместимость для мобильного «FM Склад» (post = завершить +
  создать и провести оба документа); помечено в docs/mobile-api.md.
- Enter/Loss: поле InventoryDocId (+ миграция Phase38) + в DTO «на основании инвентаризации №…».
- InventoryResult (application) — чистая разбивка diff→излишки/недостача (unit-тесты:
  разница/сумма/только посчитанные строки/округление).
- InventoryMigrationController (dry-run/apply): старые Posted+InventoryAdjustment →
  связанные проведённые документы переметкой движений (не задваивает), с проверкой
  неизменности остатков. Скрипт deploy/migrate-inventory-to-docs.sh (backup + запуск).
- Демо-сидеры: инвентаризации теперь Completed без прямых движений.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 20:34:05 +05:00
nns 839fcaf9fc fix(inventory): пустая инвентаризация больше не обнуляет непосчитанное
Авто-подтянутые строки создаются как ActualQty=BookQty (Diff=0) вместо
ActualQty=0 (Diff=-stock). Проведение не трогает непересчитанные позиции (в т.ч.
вне выбранной группы); истинный ноль вводится явным ActualQty=0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 02:10:11 +05:00
nns 6b0fc68f77 feat(invoices+inventory): порт улучшений 05.10.2026 из sklad
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>
2026-10-06 00:02:13 +05:00
nns f42329281c feat(s31): мобильная инвентаризация — offline-каталог, drift-check, расхождения (4+5+6/8)
Backend дополнения (InventoriesController):
- GET /mobile-catalog?groupId= — компактные тройки [barcode, productId,
  name] для оффлайн-работы. Фильтр по группе = частичная инвентаризация.
- POST /{id}/add-line — scan-mode: +1 к существующей строке или новая,
  bookQty подтягивается из текущего Stock (пункт 6: без ручного
  «Пересчитать»).
- GET /{id}/book-drift — список позиций, где текущий Stock != сохранённого
  BookQty. UI показывает перед Post, чтобы было видно что учётный поплыл
  (продажи/приёмки с момента создания). Пункт 6 целиком.

Существующий Create уже заполняет bookQty из Stock (строки not passed →
подтягивает весь склад; строки passed → подтягивает book по каждой).
Update тоже auto-fill'ит для новых строк.

Web (lib/offlineCatalog.ts): IndexedDB wrapper (DB=fm-inventory,
STORE=catalog, META=meta). saveCatalog/findByBarcode/catalogMeta/
countRows. Порт из sklad'овского db.ts.

Web (InventoryMobilePage.tsx) — три stage'а в одном файле:
- setup: склад + опц. группа + 'Загрузить каталог на телефон'.
- scan: Камера <CameraScanner /> или аппаратный сканер; скан → +1 к
  существующей строке; неизвестные → в отдельный список unknown с
  предупреждением; офлайн-плашка; localStorage сохраняет rows+docId.
- review: карточки с book/факт/дельта/дельта*себестоимость + фильтр
  'только расхождения'; drift-плашка если учётный поплыл;
  'Провести' → POST /post.

Routes: /inventory/mobile. tsc зелёный.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-10-04 21:40:58 +05:00
nns 4e15359378 fix(docs): EF8 nav-collection bug в Enters/Losses/Transfers/SupplierReturns/Inventories.Update
Some checks are pending
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
Тот же баг что в TD-6 чинили на Supplies/Demands/RetailSales и в pt 2
на Products: добавление/замена line'ов через nav-collection даёт
DbUpdateConcurrencyException «0 rows affected» при следующем UPDATE
родителя. На документах без xmin это становится 500, на InventoryDoc
(с xmin от TD-6) — 409.

Переводим Enters/Losses/Transfers/SupplierReturns.Update на
ExecuteDelete + DbSet.Add (как Supplies). InventoriesController
дополнительно: добавление новых строк через _db.InventoryLines.Add
вместо doc.Lines.Add (RemoveRange/Clear там не было — merge-in-place
по ProductId).

Воспроизведение (на Enters):
1. POST /api/inventory/enters {lines:[A]}
2. PUT … {lines:[A,B]} (одна оставлена, одна новая) → было 500
   DbUpdateConcurrencyException ; стало 204.

stage-enter (10 шагов): CRUD + Post + Unpost + edge + multi-tenant +
concurrent PUT — все зелёные.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-29 16:57:48 +05:00
nns 4285bdee91 feat(inventories): инвентаризация с CSV-импортом факта (P1-4)
Domain InventoryDoc+InventoryLine (productId, bookQty, actualQty, diff).
EF, миграция Phase6d_Inventories. Контроллер api/inventory/inventories:
Create без строк автоматически подгружает все товары склада с текущим
Stock в bookQty (actual=0); Update пишет actualQty по строкам, пересчитывая
diff. Post создаёт корректирующие движения InventoryAdjustment на diff
(положительный — приход излишка, отрицательный — списание недостачи).
Unpost атомарно откатывает; проверка «излишек уже расходован» → 409.

Web: /inventory/inventories (list с разделением излишек/недостача) +
edit с импортом CSV (productId|article;actualQty). Сайдбар «Инвентаризации».

Тесты: 3 интеграционных (create-подгрузка bookQty + apply diff;
post 400 если diff=0; tenant-изоляция).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-28 09:39:32 +05:00