Авто-подтянутые строки создаются как ActualQty=BookQty (Diff=0) вместо
ActualQty=0 (Diff=-stock). Проведение не трогает непересчитанные позиции (в т.ч.
вне выбранной группы); истинный ноль вводится явным ActualQty=0.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>
Тот же баг что в 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>
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>