Commit graph

418 commits

Author SHA1 Message Date
nns 7fbf94a202 fix(site): /status «Касса (Windows)» — «Скоро» (согласовано с «Скачать · скоро»)
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 Public / Build + push Public (test + latest) (push) Waiting to run
Docker Public / Deploy :test → test.food-market.kz (push) Blocked by required conditions
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 23:55:02 +05:00
nns b8e42e216c fix(site): критичные + честность текстов на test.food-market.kz (этап 1)
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 Public / Build + push Public (test + latest) (push) Waiting to run
Docker Public / Deploy :test → test.food-market.kz (push) Blocked by required conditions
1. /signup читает ?plan= и конфигурацию (общий модуль lib/pricing.ts): предвыбор тарифа,
   для «Бизнес» — конфигурация и цена (15 500 ₸ для 3/4/1); неизвестный план → «Старт».
2. Мобильная шапка: гамбургер + панель со всеми пунктами (aria-expanded, Esc/клик-вне, ≥44px).
3. og-картинки без ОФД/REST API/ABC — перегенерированы.
4. KB/блог: убраны несуществующие обещания (Kaspi Pay/Halyk/Visa, REST API, «Скачать установщик»,
   Kaspi Magazin Q3, маркировка, платные консультации, скидки 3/6/12, «Триал» как тариф).
   Противоречия сведены к оферте (удаление данных 30 дн, возврат по заявлению, Старт=2 сотр.).
5. /import: чужой HR-скриншот заменён на скриншот каталога продукта.
6. KB рендерился как сырой front-matter в code-блоке — развёрнут, дубль # убран.
7. Аналитика без реальных ID не подключается (нет data-id=REPLACE_ME).
8. robots.txt → рабочий /sitemap.xml.
9. Футер: дубль «Дорожная карта»/«Changelog» убран; 10. зоны нажатия футера + width/height у lazy-картинок.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 23:53:58 +05:00
nns a2183e9532 perf(stock-report): покрывающий индекс движений (Phase45) — отчёт остатков 2.4с→1.1с на реальном объёме
Some checks failed
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) Has been cancelled
Docker API / Deploy API on stage (push) Has been cancelled
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 21:29:27 +05:00
nns 6a7806ff08 perf(movements): считать total по базовой таблице без join (287k движений: count 1.1с→65мс)
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
На реальном объёме перенесённого магазина список движений отдавал >2.5с из-за COUNT по
3-table join. Count теперь по stock_movements с фильтрами (join не меняет число строк).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 21:27:37 +05:00
nns b04d3f0d0c feat(pos): окно выбора товара при скане ШК с несколькими карточками (дубли ШК)
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
- PosStore.FindAllByBarcode; SaleViewModel.ScanCandidates/IsScanPickerOpen + PickScanCandidate
- SaleView: модалка выбора; сервер by-barcode уже отдаёт items, веб-приёмка уже показывает выбор

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 21:04:20 +05:00
nns 1ee3c9b598 fix(migration): import — ChangeTracker.Clear при сбое товара (устранить каскад потери прогона)
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
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 20:48:18 +05:00
nns 99c2fc1e5a feat(migration-review): долги топ-20, UI «начальный остаток при переносе», дельта-таймер, прогресс
Some checks failed
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 Web / Build + push Web (push) Has been cancelled
Docker Web / Deploy Web on stage (push) Has been cancelled
- docs/migration-debts-top20.md: топ-20 долгов (контрагент-уровень) + находка (paymentout agent=орг)
- MoneyAccountsPage: строка «Начальный остаток при переносе из МойСклад на <дата>» для МС-счетов
- systemd food-market-migration-delta.timer (каждые 10 мин) — непрерывная дельта до перехода
- прогресс: раздел «Исправления по ревью» (все 4 пункта)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 20:43:44 +05:00
nns 150947c5fb fix(migration): импорт переносит дубль-ШК и дубль-артикул карточки (дизамбиг артикула, пары товар-ШК)
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
- create: артикул-коллизия → суффикс msId (перенести карточку, не падать на unique)
- create/update: ШК не уникален → добавляем код каждой карточке (пары товар|код против
  повторов), дубли ловит «Проверка данных»

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 20:21:03 +05:00
nns b1bdf5c228 fix(migration): не схлопывать разные карточки МойСклада по артикулу + актуализировать объяснение сверки
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
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 20:11:06 +05:00
nns 9d5c23d32c fix(migration): штрихкод не уникален (не сливать карточки), долг по контрагенту, выравнивание остатков
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
- Phase44: снята UNIQUE с (OrganizationId,Code) product_barcodes — «минимум как МойСклад»;
  импорт больше НЕ сливает/не пропускает карточки с дублем ШК (MergeByBarcode default=false,
  dupeInInput не пропускает). Дубли ловит «Проверка данных» (dup-barcode, уже есть).
- SupplierDebts: долг на уровне КОНТРАГЕНТА = Σприёмок − Σвозвратов − ΣисходящихРКО + Σвходящих
  + корректировки (учёт paymentout/cashout/paymentin/cashin по CounterpartyId).
- AlignStockToMoySkladAsync (type=align-stock): корректирующие Initial-движения на разницу
  (МС report/stock/all − наш остаток) → остаток точно = МойСклад, инвариант stock=Σдвижений.
- GetStockAllAsync в клиенте; MsStockRow DTO.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 20:00:34 +05:00
nns 77464d6c83 feat(prod): флаг SEED_DEMO_DATA — отключить демо-орг «Demo Market» и demo-admin на проде
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
DevDataSeeder по умолчанию (dev/stage) сидирует демо-орг; на проде SEED_DEMO_DATA=false
отключает демо-орг/demo-admin/привязку, сохраняя SuperAdmin+роли+глобальные ед.изм.+SystemSettings.
Нужно для чистого (пустого) старта реального магазина.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 19:19:10 +05:00
nns 574de0dafb feat(pos): CLI-диагностика оборудования кассы --probe-hw и тест-печать --test-print
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
- ProbeRunner: COM-порты (имя/VID/PID/описание через WMI), open→close (занят/свободен без
  удержания), опрос весов Масса-К+CAS 9600 (таймаут 1.5с), принтеры Windows (имя/порт/драйвер,
  БЕЗ печати), HID-клавиатуры (возможные сканеры). --port COMx — один порт; занятый не трогаем.
- --test-print "<принтер>": отдельный флаг, ESC/POS тест-чек «ТЕСТ ALL MARKET» через spooler
  (winspool RAW). Без флага ничего не печатается.
- WinExe → AttachConsole(parent) для видимого вывода; --out пишет чистый UTF-8 JSON.
- System.Management (WMI, только Windows, под guard'ами). Проверено на Win11 VM: выход <10с,
  COM1/принтер/HID определяются, test-print на несуществующий принтер — мягкая ошибка (код 4).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 18:45:57 +05:00
nns 81398fadad fix(migration): экранировать filter=updated>= целиком; дельта-драйвер — точный повторный опрос job + audit tolerant
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
- StreamDocumentsAsync: Uri.EscapeDataString всего выражения updated>=<дата> (>= → %3E%3D),
  не только даты — иначе часть форм 400/неоднозначна
- delta_sync: повторное чтение job после Succeeded (устранить гонку счётчиков), audit в try
- reconcile: розница демо/возвраты раздельно, URL-энкод фильтра чеков по месяцам

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 18:12:59 +05:00
nns 39d0243ba3 feat(migration): денежные документы (платежи/ордера/внесения/выплаты) + плуг балансов счетов
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
- ImportMoneyDocsAsync: paymentout/paymentin/cashin/cashout/retaildrawercashin/out
  → MoneyTransaction (история), agent→контрагент по externalId, суммы ÷100, МСК→UTC
- EnsureMoneyAccountsAsync: касса + расчётный счёт орг (идемпотентно)
- SetMoneyOpeningBalancesAsync: OpeningBalance = report/money/byaccount − Σпроводок,
  текущий баланс счёта точно совпадает с МойСкладом
- GetMoneyByAccountAsync в MoySkladClient; MsMoneyByAccountRow, paymentPurpose в DTO
- контроллер: type=money (все деньги+плуг), type=balances, одиночные денежные типы

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-09 16:32:22 +05:00
nns 5d11dd7434 feat(migration): возвраты розницы + инвентаризация + дельта-синхронизация (updated>=)
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
- ImportRetailReturnsAsync (retailsalesreturn): связь с чеком по externalId demand, +остаток
- ImportInventoriesAsync (inventory): Completed без движений, BookQty/ActualQty из calculated/quantity
- Все 7 импортёров принимают updatedFromMsk → filter=updated>= (МСК) для дельты
- Контроллер: ?updatedFrom=<МСК> прокинут во все типы

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-09 16:18:32 +05:00
nns 9506f9edc8 perf(migration): резюм документов по offset (не перечитывать уже перенесённое)
startOffset у всех документных импортов + эндпоинт ?offset=N: при продолжении
пропускаем уже перенесённые через offset API (а не перечитывая сотни страниц).
Идемпотентный skip по msId держит границу.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 15:52:28 +05:00
nns 0c9d20a469 feat(migration): мигратор оприходований/списаний/возвратов поставщику/розничных продаж
По проверенному шаблону приёмок (идемпотентно по msId, резюмируемо, МСК→UTC, тиыны,
дизамбигуация номеров, SaveChanges по документу, движения остатков):
- enter (+остаток), loss (−остаток, WriteOff), purchasereturn (−остаток, SupplierReturn),
  retaildemand (−остаток, RetailSale; оплата нал/безнал из cashSum/noCashSum, кассир=owner).
- Общие помощники LoadMaps/Aggregate. Эндпоинт POST /import-supplies?type=enter|loss|
  purchasereturn|retaildemand (фон, job с нужным kind).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 15:34:36 +05:00
nns 025d468492 fix(migration): дизамбигуация повторяющихся номеров приёмок (МойСклад дублирует по годам)
Номера МойСклада повторяются по годам, а у нас (OrganizationId, Number) уникален →
дубли роняли импорт. Сохраняем оригинальный номер где можно, при коллизии —
«<номер>-<год>[-N]». Оригинал всегда в MoySkladExternalId.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 15:08:15 +05:00
nns 955c3f5e77 fix(migration): очищать ChangeTracker при ошибке документа (стоп каскада сбоев)
Сбой SaveChanges одного документа оставлял сущности tracked → все последующие
падали (каскад), импорт застревал. Теперь в catch — ChangeTracker.Clear().

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 14:53:05 +05:00
nns 6fdc9e527a fix(migration): приёмки — сохранение по одному документу + агрегация позиций по товару
Батч из 50 приёмок с общим товаром давал дубль строки stocks (ApplyMovementAsync
создавал новую строку, не видя ещё не сохранённую). Теперь: SaveChanges после КАЖДОГО
документа (следующий видит закоммиченные остатки) + агрегация позиций по товару в
документе (средневзвешенная цена) + ChangeTracker.Clear (не копим граф на 7k докумов).
Идемпотентно по msId — повторный запуск продолжает с места.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 14:31:27 +05:00
nns 10cd9f989b fix(migration): суммы документов МойСклада как decimal (приходят «900000.0») + GET статуса job
МойСклад отдаёт sum/cashSum/price как число с точкой (900000.0) → long не парсился
(«JSON value could not be converted to System.Int64»). Поля DTO → decimal. Плюс
GET /api/admin/moysklad/jobs/{id} для наблюдения за прогрессом/ошибками переноса.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 14:19:52 +05:00
nns f2a1493ea0 fix(migration): лёгкий expand для документов (positions, не positions.assortment) + таймаут 130с
Тяжёлый expand=positions.assortment раскрывал весь товар в каждой позиции → страница
гигантская и зависала. assortment.meta.href в позиции есть и без раскрытия. Таймаут
HttpClient МойСклада поднят до 130с (страница иногда > 100с).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 14:07:54 +05:00
nns 2ed9f04cb2 feat(migration): документный мигратор МойСклад (приёмки) + externalId на документах
Part 2 (документы) — проверенный шаблон на приёмках:
- Phase41: MoySkladExternalId у counterparties/stores/units/retail_points/employees и
  документов (supplies/enters/losses/inventories/supplier_returns/retail_sales/
  cash_shifts/money_transactions) + индексы. Свойства на доменных сущностях.
- MoySkladClient.StreamDocumentsAsync: документы в хронологии (order=moment,asc), expand
  позиций/связей (limit=100), обработка 429/сбоев.
- MoySkladDocumentImportService.ImportSuppliesAsync: идемпотентно по msId, резюмируемо
  (skip перенесённых), moment МСК→UTC, суммы из тиынов, поставщик по externalId контрагента,
  товары по externalId, проведение → движения остатков (StockService). Журнал ошибок.
- Импорт контрагентов теперь пишет MoySkladExternalId (нужно для ссылок в документах).
- Эндпоинт POST /api/admin/moysklad/import-supplies (фон, job). Остальные типы — по шаблону.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 13:53:52 +05:00
nns 848e747379 fix: внутренний ШК 2x (не 02/ведущий-0) + разовый прогон «Проверки данных» при старте
Найдено Mac-Claude на стенде:
1. Генератор внутреннего EAN-13 давал «02…» (ведущий 0 → зона UPC-A, может совпасть
   с реальным товаром). InternalBarcode.PickPrefix теперь берёт 2x из restricted-
   circulation 20–29 (без ведущего нуля), не пересекаясь с весовыми префиксами орг;
   «02» — только крайний резерв. Юнит-тест обновлён.
2. «Проверка данных» на Демо показывала все 0 и lastCheckedAt=null — ночной cron ещё
   не отрабатывал после пересева. Добавлен разовый BackgroundJob.Enqueue(DataQualityJob)
   при старте: после деплоя/пересева счётчики считаются сразу по всем организациям.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 12:45:17 +05:00
nns b72b375381 style(pos): компактный бейдж весов (иконка ⚖ уже обозначает «весы»)
Чтобы статус весов не обрезался на узких экранах кассы (1280).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 11:30:42 +05:00
nns 195dfb1a2c feat(pos): весовой товар на кассе — вес с весов/вручную + автопоиск весов
Часть 3 + автопоиск (данные sysadmin: касса МойСклад спамит «Порт не выбран»):
- Весовой товар при сканировании/плитке → вес с весов (если на связи), иначе
  модалка ручного ввода веса. Сумма = цена/кг × вес; подпись «1,235 кг × 690 ₸/кг».
- ScaleMonitor (pos.core): опрос ТОЛЬКО по требованию (при весовом товаре), мягкая
  деградация на ручной ввод, запись о потере/восстановлении связи РОВНО ОДИН раз
  (не сотни «Таймаут»). Юнит-тесты (100 опросов → 1 запись).
- ScaleDiscovery (pos): автоскан всех COM-портов (вкл. USB-COM CH340/Prolific) ×
  протоколы Масса-К/CAS × скорости; кнопка «Найти весы» в верхней панели; найденный
  порт запоминается (kv) и восстанавливается при старте.
- UnitShort («шт»/«кг»/«л») протянут через sync→SQLite→PosProduct/CartLine; цена
  весового показывается «690 ₸/кг» вместо кода ОКЕИ «690 ₸/796».
- Разбор этикеток в кассе — по настройкам орг (по умолчанию ВЫКЛ, точный штрихкод).
- Юнит-тесты 321/321 (обновлены PosBarcodeTests под дефолт-off).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 11:17:35 +05:00
nns d0fee10e96 fix(barcode): весовой разбор выкл по умолчанию; поиск по ШК — точное совпадение
Реальный сценарий владельца: весов с печатью этикеток нет, штрихкоды весовых —
обычные коды «27xxxxx» в карточке. Исправлены два бага:
- поиск по ШК матчил весовой PLU по АРТИКУЛУ (вода арт.12345 → 1,5 кг) — убрано,
  поиск по штрихкоду больше нигде не матчит по артикулу;
- разбор весовых этикеток был дефолт-ON (WeightedBarcodeSettings.Enabled,
  Organization.WeightBarcodeEnabled) и ловил обычные коды — теперь дефолт OFF.

- ByBarcode: сначала точное совпадение штрихкода (вкл. короткие «27xxxxx»),
  разбор этикетки — только если включён в орг И точного ШК нет.
- Phase40: WeightBarcodeEnabled DEFAULT false + выключение у существующих орг;
  WeightedCodeFormat('short'|'ean13')/WeightedCodePrefix(27)/WeightedCodeDigits(5).
- Генератор ШК нового весового товара: короткий «<prefix>+N цифр» (27xxxxx, next-free)
  или EAN-13 по настройке; уникальность.
- Настройки орг отдают/принимают формат кода весовых.
- Sync отдаёт unitShort («шт»/«кг»/«л») рядом с unitCode (ОКЕИ) — shared UnitShort.
- Тесты: MobileApi (exact-only по умолчанию; не матчит артикул; PLU как ШК при вкл) 9/9;
  unit WeightedBarcode/UnitShort 16/16.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 11:02:43 +05:00
nns d380e186a8 feat(uat): дружелюбный заголовок валидации + mobile-UAT 390px + отчёт
- ValidationFilter (FluentValidation-путь): русский заголовок «Проверьте
  правильность заполнения» через ModelErrorTranslator вместо дефолтного англ.
  «One or more validation errors occurred» (сообщения полей уже были русскими).
- stage-ui-uat-mobile.spec.ts: P1-экраны на 390px без h-scroll/console/5xx +
  проверка «Остатки» (артикул/штрихкод, тумблер «показать нулевые»).
- docs/uat-report-2026-10-09.md: матрица покрытия (уже-зелёное vs проверено
  сейчас) по требованию владельца; галерея скринов reports/uat-mobile/.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-09 10:16:47 +05:00
nns f3a177a121 fix(sales): дата чека в UTC + защита от «будущих» времён
«Последние продажи»/Z-отчёт показывали времена вперёд, потому что дата чека
бралась от клиента (input.Date / POS OccurredAt) без нормализации: наивная
локальная дата или спешащие часы кассы давали будущий инстант. Добавил
BusinessTime.ToInstantUtc: Kind→UTC и clamp будущего (>5 мин) к now — чек не
может быть в будущем. Применено в рознице (create) и в приёме чеков с кассы
(движение берёт тот же инстант). Юнит-тесты на нормализацию/clamp.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-09 09:58:34 +05:00
nns 9dc8f6e044 fix(inventory): записывать автора движения (CauseUserId) для всех документов
CauseUserId в уведомлениях о минусах был null: StockService писал автора только
если вызывающий явно передал CreatedByUserId в черновике, а большинство
документов (касса/списание/перемещение/приёмка/…) его не передавали. Теперь
автор берётся из черновика, иначе — из текущего пользователя (ITenantContext.UserId)
в единой точке ApplyMovementAsync, поэтому пишется и в StockMovement, и в алерт
для всех типов документов. Тест: минус от списания → causeUserId = текущий юзер.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-09 09:52:33 +05:00
nns 4aceda3e41 fix(errors): дружелюбные русские ошибки ввода вместо технических JSON-текстов
Неверный тип/enum в теле запроса давал пользователю технический англ. текст с
именем класса и путём ($.reason: The JSON value could not be converted to
foodmarket.Api…LossInput…). Теперь InvalidModelStateResponseFactory прогоняет
ModelState через ModelErrorTranslator: технические сообщения → «Неверное значение
поля «Причина»» (имя поля по-русски, без классов/путей/англ.), осмысленные
FluentValidation-сообщения остаются как есть. Сырой текст пишется только в лог
бэкенда. Действует на web (toast) и mobile API. Тест: списание с неверным enum.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-09 09:52:33 +05:00
nns 193ba49ecb fix(stock): «показать нулевые» и фильтр по группе показывают товары без прихода
Остатки строились ОТ таблицы Stocks (JOIN Products), поэтому товары без строки
Stock (ни разу не приходованные) не попадали в список вообще, а includeZero лишь
снимал фильтр Quantity!=0 у существующих строк. То же в отчёте «Остатки на дату»,
в экспорте и в фильтре по группе (iOS получал total 0).

- GetStock/ExportStock: выборка ОТ Products (не-услуги, не-архивные) с остатком
  коррелированным подзапросом (товар без Stock = 0). Склад выбран → строка склада;
  без склада → агрегат «Все склады». Нулевые видны при includeZero; negativeOnly и
  пагинация/сортировка/поиск/экспорт работают и для нулевых. Остаток/ед./штрихкод
  дотягиваются batch-запросом по id страницы (EF8 не переводит подзапросы в SELECT+ORDER BY).
- Отчёт «Остатки на дату»: includeZero добавляет товары без движений (Qty 0);
  фильтр по группе теперь с вложенностью.
- groupId/productGroupId — синонимы с вложенными группами на всех списках остатков
  и в каталоге (List + Export). Раньше catalog?productGroupId молча игнорировался.
- Штрихкод рядом с артикулом в списке/экспорте — различать одноимённые товары
  (4× «BIOBLAS шампунь» с разными артикулами из МойСклада — не удалять).
- Demo (30 894 товара): без флага 12, includeZero → 30 894, <500мс/страница.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-09 09:44:50 +05:00
nns 592399f90e fix(data-quality): «без группы» — только служебный корень, не настоящие верхние категории
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
Раньше флагировались все товары в группах с ParentId==null (включая реальные
верхнеуровневые категории вроде «Молочные»). Теперь — только товары в служебной
группе «Все товары»/«Без группы». На seed-demo: было 50 (ложно), стало 3 (верно).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-09 08:27:15 +05:00
nns bcd44af3a7 feat(web): панель «Проверка данных» + бейдж на главной (п.2,3,4)
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 Web / Build + push Web (push) Waiting to run
Docker Web / Deploy Web on stage (push) Blocked by required conditions
Страница /catalog/data-quality: счётчики по проверкам, детальные списки, массовое
исправление (задать группу/цену, сгенерировать штрихкоды), «это разные товары» для
дублей, пересчёт, тумблеры отключения проверок. Ненавязчивый бейдж «N товаров требуют
внимания» на дашборде (исчезает при 0). Пункт меню «Проверка данных» в каталоге.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-09 08:15:28 +05:00
nns 261a720b52 feat(data-quality): фоновая «Проверка данных» — бэкенд (п.1,3,4)
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
Hangfire DataQualityJob (ночью 02:20 UTC) считает по орг. счётчики проблем: без группы
(корень/«Без группы»), без цены продажи, без штрихкода (кроме услуг), дубль штрихкода,
возможный дубль имя+единица; товары в минусе — ссылкой на центр (не дублируем). Результаты
в data_quality_results (Phase43), живые детальные списки — по запросу.
API /api/data-quality: сводка, {check}/products, recompute, dismiss («это разные товары»),
bulk set-group/set-price/generate-barcodes, PUT settings (отключение проверок). unknown-cost
отложен до FIFO; PLU убран (поправка владельца). Integration-тест: счётчики/список/bulk/dismiss.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-09 08:11:57 +05:00
nns 4181bfe8ad feat(pos): Z-отчёт по товарам на печать + вопрос «размен?» при автооткрытии (п.1,2)
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
П.2 (POS): закрытие смены печатает Z-отчёт с разбивкой по товарам (продано/возврат)
через ESC/POS; кнопка «Печать Z-отчёта (по товарам)». ShiftReportDto += byProduct/overLimit24h.
П.1 (POS): первая продажа без открытой смены (онлайн) — диалог «Открыть смену. Размен?
(можно пропустить)» → автооткрытие и переход к оплате; офлайн — как было.
П.10: full-chain обновлён — продажа без смены теперь автооткрытие (не 409) + проверка
byProduct в Z-отчёте; прогон на стенде 16 ✓ / 0 ✗.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-09 07:38:07 +05:00
nns 8c4a9c4e5e feat(pos): быстрое создание товара по неизвестному штрихкоду из кассы (п.4)
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
Скан неизвестного штрихкода (онлайн) → диалог «Новый товар» (наименование + опц.
цена) → POST /products/quick → товар сразу в чеке; попадёт в локальный каталог на
ближайшем синке. Офлайн — как было («не найден»). Smoke/uitest зелёные.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-09 07:22:14 +05:00
nns 210e8171d3 feat(money): автооткрытие смены на POS-синке + уведомление о сменах >24ч (п.1)
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
POS: чек с точки без открытой смены — сервер автооткрывает смену (Cash-счёт точки,
размен 0) и принимает чек (как в вебе). Фоновый ShiftWatchJob (раз в час) уведомляет
кассира и админов о сменах, открытых дольше 24ч (персистентное уведомление + SignalR,
дедуп раз в сутки). Тест POS-автооткрытия.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-09 07:11:29 +05:00
nns 3579e3583c feat(documents): единый блок «Связанные документы» на всех документах (п.5)
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
GET /api/documents/{type}/{id}/related — основание + связанные (возвраты/оплаты/
опр-спис/возвраты по чеку) для supply/supplier-return/retail-sale/inventory/enter/loss.
Компонент RelatedDocsBlock применён на edit-страницах приёмки, возврата поставщику,
оприходования, списания, чека (инвентаризация имела свой блок). Integration-тест:
приёмка↔возврат (related/basedOn).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-09 07:06:07 +05:00
nns 68bfb582e7 feat(web): Z-отчёт смены — разбивка по товарам + предупреждение о >24ч (п.1/2)
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 Web / Build + push Web (push) Waiting to run
Docker Web / Deploy Web on stage (push) Blocked by required conditions
Веб Z-отчёт показывает byProduct (товар/продано/выручка/возвраты) и баннер «смена
открыта дольше 24ч». Печать уже была (window.print()).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-09 06:48:00 +05:00
nns fd03700a58 feat(web): кнопка «Создать возврат» на проведённой приёмке (п.5 create-based-on)
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 Web / Build + push Web (push) Waiting to run
Docker Web / Deploy Web on stage (push) Blocked by required conditions
Проведённая приёмка → «Создать возврат» вызывает from-supply (преднабор строк) и
открывает черновик возврата поставщику. Вместе с секцией оплат приёмки (supply→
payment) и ссылками-основаниями в DTO — развивает «создать на основании».

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-09 06:41:03 +05:00
nns 524f8f41b1 feat(money): акт сверки с поставщиком — печать/PDF (п.7)
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
GET /api/money/reports/supplier-reconciliation?supplierId=&from=&to=: начальное
сальдо (до from), движения периода (приёмка +долг, возврат/оплата −долг) с бегущим
сальдо, конечное сальдо. Веб-страница /money/supplier-debts/:id/reconciliation —
печатная форма акта (window.print()/PDF) + кнопка «Акт сверки» в долгах поставщикам.
Integration-тест: приёмка как debit, конечное сальдо.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-09 06:39:08 +05:00
nns 18752497a8 feat(web): статусы DocStatusBadge на всех списках документов + кнопка «Сгенерировать» ШК
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 Web / Build + push Web (push) Waiting to run
Docker Web / Deploy Web on stage (push) Blocked by required conditions
П.9: DocStatusBadge (Черновик/Проведён с подсказкой) раскатан на Оприходования,
Списания, Перемещения, Возвраты поставщикам, Отгрузки (было: приёмки/продажи/инвент.).
П.4: в карточке товара рядом с полем штрихкода — кнопка «Сгенерировать» (внутренний
EAN-13). Группа по умолчанию — «Все товары», единица — «шт» (уже было).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-09 06:32:58 +05:00
nns ad2e3b3117 feat(purchases): создать возврат поставщику на основании приёмки (п.5 слайс)
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
POST /api/purchases/supplier-returns/from-supply/{supplyId}: черновик возврата с
преднабором строк из проведённой приёмки (товар/кол-во/цена), поставщик/склад/валюта
и ссылка ReferenceSupplyId. Integration-тест: преднабор + связь.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-09 06:16:33 +05:00
nns 0f1574df64 feat(web): привычные названия денег (ПКО/РКО) + прибыль на главной + статусы с подсказкой
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 Web / Build + push Web (push) Waiting to run
Docker Web / Deploy Web on stage (push) Blocked by required conditions
П.7: типы денег названы привычно — «Приход наличных (ПКО)», «Расход наличных (РКО)»,
«Входящий/Исходящий платёж», «Перевод между счетами».
П.8: блок «Прибыль за сегодня/месяц» на дашборде (во всех режимах, /api/dashboard/margin),
с пометкой «приблизительно; точно после FIFO».
П.9: общий DocStatusBadge (Черновик/Проведён/Завершена) с tooltip «что это значит»,
применён на списках Приёмок/Продаж/Инвентаризаций.
П.6: проверено — Оприходование/Списание в простом режиме не прячутся (отдельные пункты).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-09 06:04:18 +05:00
nns ff4391d19f feat(money): автооткрытие смены первой продажей + Z-отчёт по товарам + 24ч
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
П.1: продажа на точке без открытой смены — смена автооткрывается (подбор Cash-счёта
точки/орг., размен 0, можно внести позже), как МойСклад; ручное открытие остаётся.
Длительность смены и отметка overLimit24h в /current и Z-отчёте (мягкое правило:
предупреждение + отметка, не жёсткий лимит).
П.2: Z-отчёт /shifts/{id}/report теперь с разбивкой byProduct (продано кол-во/сумма,
возвращено кол-во/сумма).

Тест переписан: продажа без смены → автооткрытие + проводится; /current open=true;
Z-отчёт содержит byProduct с верными кол-вом/суммой.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-09 05:58:31 +05:00
nns c777b5b692 feat(sales): возврат — способ по умолчанию = оплата чека; иной способ по праву+причине
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
П.3 паритета: create-return уже ставит способ исходного чека. В ручном возврате
(IsReturn + referenceSaleId) если способ отличается от оплаты чека — нужно право
администратора и причина (RefundReason, пишется в Notes). Сообщения понятные.

Тест: тот же способ ок; иной без причины → 400; иной с причиной (админ) → 201.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-09 05:50:41 +05:00
nns 0ca0d6cc3b feat(catalog): карточка товара как МойСклад+ — обязательно только наименование
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
Правки владельца 08.10 (п.4 паритета):
- Сохранить можно при наличии Наименования. Единица не обязательна в запросе →
  дефолт «шт»; группа → корень. Штрихкод ОБЯЗАТЕЛЕН у любого товара: нет в запросе/
  импорте → автогенерация внутреннего EAN-13 (верная контр. цифра, префикс не
  пересекается с весовыми/ценовыми префиксами орг. — InternalBarcode).
- Цена продажи не обязательна для сохранения; но ПРОДАТЬ без неё нельзя — касса и
  розничная продажа дают «У товара «X» нет цены продажи» (можно ввести вручную).
- Быстрое создание из кассы/приёмки: POST /api/catalog/products/quick (имя + опц.
  штрихкод/цена/весовой).
- Снят FluentValidation NotEqual(Guid.Empty) на единицу (контроллер подставляет дефолт).

Тесты: unit InternalBarcode 3/3 (валидный EAN-13, не парсится как весовой, обходит
префиксы); integration 3/3 (создание по одному имени, продажа без цены → 400, quick).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-09 05:45:38 +05:00
nns db248d90f0 perf(pos): короткий таймаут (8с) на фоновую проверку версии
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
Некритичная проверка версии при старте — fire-and-forget, но на мёртвой сети висела
до 30с (дефолт HttpClient). Короткий CTS, чтобы фоновая задача не держалась зря.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-09 05:15:00 +05:00
nns e78f14bc6b test(pos): инструменты замера старта — bulk-каталог + bench
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
- POST /api/admin/seed-demo?bulk=N — массовое наполнение каталога (до 60k товаров)
  батчами для нагрузочных замеров (идемпотентно по маркеру BULK-).
- POS --bench <url> <user> <pass>: мерит первичную загрузку (сеть/запись отдельно),
  тёплый старт (открытие базы + поиск), дельта-синк. С gzip-декомпрессией.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-09 04:59:11 +05:00