diff --git a/docs/mobile-api.md b/docs/mobile-api.md index 9ef7fba..a76415e 100644 --- a/docs/mobile-api.md +++ b/docs/mobile-api.md @@ -57,30 +57,35 @@ advanced-only скрыты; обычная роль — её флаги (в пр `PromotionsManage`, `PriceTypesManage` (зеркалит серверный `[RequiresAdvancedMode]`). Имена флагов — из `RolePermissions`. Веб использует тот же источник (`useCan()`). -## 4. Весовой признак в офлайн-каталоге +## 4. Весовой признак и единица в офлайн-каталоге `GET /api/pos/v1/sync` (каталог для сканирования/офлайна) в `ProductSyncDto` отдаёт: - `packaging` (1=штучный, 2=весовой, 3=разливной) и **`isWeighed`** (явный bool `packaging==2`), -- `unitCode` — единица измерения (шт/кг/л…). +- `unitCode` — код ОКЕИ единицы измерения («796»=шт, «166»=кг, «112»=л…), +- **`unitShort`** — короткое имя для отображения («шт»/«кг»/«л»), выведено из `unitCode`/названия, + чтобы касса/моб. не держали свою таблицу ОКЕИ. То же поле есть в строках возврата + (`PosReturnLineDto.unitShort`). Маппинг — `foodmarket.Shared.Units.UnitShort`. -## 5. Весовые этикетки (внутримагазинные EAN-13) -Как в МойСкладе: внутримагазинные EAN-13 с префиксом из диапазона 20–29, где зашиты -«код товара» (PLU, N цифр) и величина (вес в граммах → кг, либо цена). +## 5. Поиск по штрихкоду и весовые этикетки +**Реальный сценарий (09.10): весов с печатью этикеток нет.** Штрихкоды весовых товаров — +обычные коды в карточке (у владельца короткие «27xxxxx»). Поэтому: + +**Поиск** — `GET /api/catalog/products/by-barcode/{value}`: +- **Сначала точное совпадение штрихкода** карточки (включая короткие «27xxxxx»): + 1 → объект товара, несколько → `{ items:[…] }`. +- **Поиск по штрихкоду НИКОГДА не матчит по артикулу** (раньше весовой PLU матчился + по артикулу — товар с артикулом «12345» ошибочно находился; исправлено). +- Разбор весовой этикетки — **только если включён в организации И точного штрихкода нет**. + Тогда встроенный PLU ищется как ШТРИХКОД (не артикул) → `{ product:{…}, weighted:true, + valueKind:"WeightKg"|"Price", quantity:1.5, embeddedPrice:null }`. +- Нет совпадений → `404`. **Настройка организации** — `GET/PUT /api/organization/settings` (PUT — право Admin): -- `weightBarcodeEnabled` (bool, по умолчанию true), -- `weightBarcodeWeightPrefixes` (CSV префиксов веса, null → `20,21,22,23,24`), -- `weightBarcodePricePrefixes` (CSV префиксов цены, null → `25,26,27,28,29`), -- `weightBarcodeCodeDigits` (длина PLU, по умолчанию 5). -Блок применяется, только если в PUT передан `weightBarcodeEnabled` (иначе настройки не трогаются). - -**Разбор при поиске** — `GET /api/catalog/products/by-barcode/{value}`: -- Обычный штрихкод → товар (или `{ items:[…] }` при нескольких совпадениях), `404` если нет. -- **Весовая этикетка** (префикс весовой/ценовой) → `{ product:{…}, weighted:true, - valueKind:"WeightKg"|"Price", quantity:1.5, embeddedPrice:null }`. Товар ищется по - встроенному PLU (как короткий штрихкод товара либо как артикул); `quantity` — вес (кг), - `embeddedPrice` — зашитая цена (для ценовых этикеток). - -Пример: этикетка `2112345015007` при дефолтных настройках → PLU `12345`, вес `1.500` кг. +- `weightBarcodeEnabled` (bool, **по умолчанию FALSE**) — разбор этикеток; +- `weightBarcodeWeightPrefixes` / `weightBarcodePricePrefixes` (CSV, null → `20..24`/`25..29`); +- `weightBarcodeCodeDigits` (длина PLU, по умолчанию 5); +- **`weightedCodeFormat`** («short»|«ean13», default «short») — формат кода, который + генерируется для нового ВЕСОВОГО товара; **`weightedCodePrefix`** (default 27), + **`weightedCodeDigits`** (default 5) → код вида «27xxxxx», следующий свободный номер. Разбор единый для сервера и кассы — `foodmarket.Shared.Barcodes.WeightedBarcode`. diff --git a/docs/weighted-progress.md b/docs/weighted-progress.md index 68d438b..0134f4e 100644 --- a/docs/weighted-progress.md +++ b/docs/weighted-progress.md @@ -17,25 +17,26 @@ → внутренние/обычные коды принимались за весовые. ## План / статус -1. [ ] Разбор весовых этикеток ВЫКЛ по умолчанию (server/POS/web/mobile). Поиск по ШК — - только точное совпадение (включая короткие 7-значные «27xxxxx»); убрать матч по артикулу. -2. [ ] Настройка орг «Весы с печатью этикеток» (как префикс МойСклада): выкл по умолчанию; - при вкл — префикс(ы) + формат ПП ККККК ВВВВВ К; **сначала точный ШК, потом разбор**. -3. [ ] Касса: весовой товар → вес с весов (COM/USB, Масса-К/CAS) или ручной ввод; сумма = - цена/кг × вес; «1,235 кг × 690 ₸/кг». -4. [ ] Генератор ШК: штучный — EAN-13 (как сейчас); весовой — EAN-13 или короткий «27 + 5» - (настройка «формат кода весовых», по умолчанию 27 + следующий свободный 5-значный); - проверка уникальности. -5. [ ] `/api/pos/v1/sync`: unitCode «796» → отдавать unitCode (ОКЕИ) + unitShort («шт»/«кг»/«л»); - обновить docs/mobile-api.md. -6. [ ] Тесты (unit/integration) + full-chain без регрессий + деплой. +1. [x] Разбор весовых этикеток ВЫКЛ по умолчанию (`WeightedBarcodeSettings.Enabled=false`, + `Organization.WeightBarcodeEnabled=false` + Phase40 выключает у существующих). Поиск по ШК + (`ByBarcode`) — сначала точное совпадение (вкл. «27xxxxx»); матч по артикулу убран везде. +2. [x] Настройка орг «Весы с печатью этикеток»: выкл по умолчанию; при вкл — префиксы + + формат; **сначала точный ШК, потом разбор**. `OrganizationSettingsController`. +3. [x] Касса: весовой товар → вес с весов (Масса-К/CAS) или ручной ввод (модалка); сумма = + цена/кг × вес; подпись «1,235 кг × 690 ₸/кг». `ScaleMonitor` + Scan/AddWeighed flow. +4. [x] Генератор ШК: штучный — EAN-13; весовой — короткий «27+N» (next-free) или EAN-13 по + `weightedCodeFormat`; уникальность. `GenerateUniqueWeightedShortCodeAsync`. +5. [x] `/api/pos/v1/sync`: + `unitShort` («шт»/«кг»/«л») рядом с `unitCode` (ОКЕИ); в строках + возврата тоже; shared `UnitShort`; docs/mobile-api.md обновлён. +6. [~] Тесты: unit 321/321 ✓ (WeightedBarcode/UnitShort/ScaleMonitor/PosBarcode); integration + MobileApi 9/9 ✓. full-chain + деплой — ниже. ### Автопоиск весов (данные sysadmin: МойСклад-касса спамит «Порт не выбран»/«Таймаут») -- [ ] Касса сама находит весы: автоскан всех COM (вкл. USB-COM CH340/Prolific) с Масса-К/CAS, - кнопка «Найти весы» в настройках; найденный порт запоминается. -- [ ] Весы не отвечают → значок «Весы не подключены» + мгновенный ручной ввод, без спама, - опрос только при весовом товаре и не чаще нужного. -- [ ] В логе — одна запись о потере связи, не сотни. +- [x] Автоскан всех COM (вкл. USB-COM CH340/Prolific) × Масса-К/CAS × скорости; кнопка + «Найти весы» в верхней панели; найденный порт запоминается (kv) и восстанавливается. + `ScaleDiscovery` + `AppServices.DiscoverScaleAsync/RestoreScaleFromSettings`. +- [x] Весы не отвечают → «Весы не подключены» + ручной ввод; опрос только при весовом товаре. +- [x] В логе — одна запись о потере/восстановлении связи (тест: 100 опросов → 1 запись). ## Что НЕ трогаем Прод, shared-postgres, global.json, gateway nginx, sklad.