diff --git a/docs/migration-progress.md b/docs/migration-progress.md index c042ec1..173ab47 100644 --- a/docs/migration-progress.md +++ b/docs/migration-progress.md @@ -90,8 +90,9 @@ Part 1 (инвентаризация) и Part 3 (орг) — выполнены. дублем ШК (MergeByBarcode=false, dupeInInput не пропускает, артикул-дубль дизамбигуируется суффиксом msId). Перенос вернул **49 ранее слитых карточек** (каталог 30993 = МойСклад). Дубли ШК видны в «Проверке данных» → dup-barcode (чек уже есть). На кассе/в приёмке при скане - неоднозначного ШК фронт должен показывать выбор товара — серверные данные (несколько товаров с - одним ШК) и справочник dup-barcode для этого готовы. **Остатки пересчитаны**: см. раздел сверки — + неоднозначного ШК показывается ОКНО ВЫБОРА товара: сервер `by-barcode` отдаёт список совпадений + (цена/остаток), веб-приёмка (`SupplyLineQuickAdd`) и касса (POS `SaleView`/`FindAllByBarcode`) — + показывают выбор. **Остатки пересчитаны**: см. раздел сверки — `align-stock` выравнивает к снимку `report/stock/all` (движения `Initial` «МС-выравнивание») → расхождений 0 кроме живой дельты. 2. **Долги — на уровне КОНТРАГЕНТА** (SupplierDebts переписан): Σприёмок − Σвозвратов − Σисходящих diff --git a/src/food-market.pos.core/Data/PosStore.cs b/src/food-market.pos.core/Data/PosStore.cs index 2c855d1..11a84ef 100644 --- a/src/food-market.pos.core/Data/PosStore.cs +++ b/src/food-market.pos.core/Data/PosStore.cs @@ -173,6 +173,21 @@ public int ProductCount() return r.Read() ? Map(r) : null; } + /// Все товары с таким штрихкодом (ШК не уникален — «минимум как МойСклад»). + /// Если вернулось >1 — касса показывает окно выбора товара. + public IReadOnlyList FindAllByBarcode(string code) + { + var lookup = OnlyDigitsOrRaw(code); + using var cmd = _cn.CreateCommand(); + cmd.CommandText = @"SELECT p.* FROM products p JOIN barcodes b ON b.ProductId=p.Id + WHERE b.Code=$c AND p.IsArchived=0"; + Bind(cmd, "$c", lookup); + using var r = cmd.ExecuteReader(); + var list = new List(); + while (r.Read()) list.Add(Map(r)); + return list; + } + /// Избранные товары для плиток «быстрых товаров» на экране продажи. public IReadOnlyList GetFavorites(int limit = 12) { diff --git a/src/food-market.pos/ViewModels/ViewModels.cs b/src/food-market.pos/ViewModels/ViewModels.cs index cc61cc2..61039fa 100644 --- a/src/food-market.pos/ViewModels/ViewModels.cs +++ b/src/food-market.pos/ViewModels/ViewModels.cs @@ -531,6 +531,12 @@ public partial class SaleViewModel : ViewModelBase [ObservableProperty] private bool _favoritesOpen = true; public bool HasFavorites => Favorites.Count > 0; + // Выбор товара при сканировании ШК, который есть у НЕСКОЛЬКИХ карточек + // («минимум как МойСклад»: дубли ШК допустимы). Касса показывает окно выбора. + public ObservableCollection ScanCandidates { get; } = new(); + [ObservableProperty] private bool _isScanPickerOpen; + [ObservableProperty] private string _scanPickerCode = ""; + [ObservableProperty] private string _scanInput = ""; [ObservableProperty] private string _shiftNumber = "С-2026-000001"; [ObservableProperty] private string _message = "Отсканируйте товар или введите штрихкод"; @@ -677,8 +683,21 @@ private async Task Scan() if (raw.Length == 0) return; // Разбор этикеток по настройкам организации (по умолчанию ВЫКЛ → точный код). var parsed = BarcodeParser.Parse(raw, _svc?.WeightedSettings); + // ШК может быть у НЕСКОЛЬКИХ карточек (дубли допустимы). Если так — окно выбора. + var matches = _svc?.Store.FindAllByBarcode(raw.Trim()); + if (matches is null || matches.Count == 0) matches = _svc?.Store.FindAllByBarcode(parsed.Lookup); + if (matches is { Count: > 1 }) + { + ScanCandidates.Clear(); + foreach (var m in matches) ScanCandidates.Add(m); + ScanPickerCode = parsed.Lookup; + IsScanPickerOpen = true; + Message = $"Штрихкод {parsed.Lookup}: несколько товаров — выберите"; + return; + } // Сначала — точное совпадение штрихкода; этикеточный PLU как запасной путь. - var p = _svc?.Store.FindByBarcode(raw.Trim()) + var p = (matches is { Count: 1 } ? matches[0] : null) + ?? _svc?.Store.FindByBarcode(raw.Trim()) ?? _svc?.Store.FindByBarcode(parsed.Lookup); if (p is null) { @@ -708,6 +727,17 @@ private async Task Scan() [RelayCommand] private void QuickCreateCancel() { IsQuickCreateOpen = false; QuickCreateName = ""; QuickCreatePrice = null; } + /// Выбор товара из окна выбора (ШК у нескольких карточек). + [RelayCommand] private async Task PickScanCandidate(PosProduct? p) + { + IsScanPickerOpen = false; ScanCandidates.Clear(); + if (p is null) return; + if (p.IsWeighed) { await AddWeighedAsync(p); return; } + AddProduct(p, 1m); + } + + [RelayCommand] private void ScanPickerCancel() { IsScanPickerOpen = false; ScanCandidates.Clear(); } + /// Создать товар по неизвестному штрихкоду на сервере и сразу добавить в чек. [RelayCommand] private async Task QuickCreateConfirmAsync() diff --git a/src/food-market.pos/Views/SaleView.axaml b/src/food-market.pos/Views/SaleView.axaml index 19b42df..9060de8 100644 --- a/src/food-market.pos/Views/SaleView.axaml +++ b/src/food-market.pos/Views/SaleView.axaml @@ -238,6 +238,34 @@ + + + + + + + + + + + + + + + +