From 848e74737935d374a439f6aac1172a5b9c5922db Mon Sep 17 00:00:00 2001 From: nns Date: Fri, 9 Oct 2026 12:45:17 +0500 Subject: [PATCH] =?UTF-8?q?fix:=20=D0=B2=D0=BD=D1=83=D1=82=D1=80=D0=B5?= =?UTF-8?q?=D0=BD=D0=BD=D0=B8=D0=B9=20=D0=A8=D0=9A=202x=20(=D0=BD=D0=B5=20?= =?UTF-8?q?02/=D0=B2=D0=B5=D0=B4=D1=83=D1=89=D0=B8=D0=B9-0)=20+=20=D1=80?= =?UTF-8?q?=D0=B0=D0=B7=D0=BE=D0=B2=D1=8B=D0=B9=20=D0=BF=D1=80=D0=BE=D0=B3?= =?UTF-8?q?=D0=BE=D0=BD=20=C2=AB=D0=9F=D1=80=D0=BE=D0=B2=D0=B5=D1=80=D0=BA?= =?UTF-8?q?=D0=B8=20=D0=B4=D0=B0=D0=BD=D0=BD=D1=8B=D1=85=C2=BB=20=D0=BF?= =?UTF-8?q?=D1=80=D0=B8=20=D1=81=D1=82=D0=B0=D1=80=D1=82=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Найдено 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 --- .../Background/HangfireJobsConfigurator.cs | 5 +++++ src/food-market.shared/Barcodes/InternalBarcode.cs | 11 +++++++---- tests/food-market.UnitTests/InternalBarcodeTests.cs | 10 +++++++--- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/food-market.api/Background/HangfireJobsConfigurator.cs b/src/food-market.api/Background/HangfireJobsConfigurator.cs index bd68867..725c49b 100644 --- a/src/food-market.api/Background/HangfireJobsConfigurator.cs +++ b/src/food-market.api/Background/HangfireJobsConfigurator.cs @@ -169,6 +169,11 @@ public Task StartAsync(CancellationToken ct) cronExpression: cronDataQuality, options: new RecurringJobOptions { TimeZone = TimeZoneInfo.Utc }); + // Разовый прогон при старте: чтобы после деплоя/пересева Демо (и любой новой + // организации) счётчики «Проверки данных» были посчитаны сразу, не дожидаясь + // ночного cron (иначе панель/бейдж показывают 0 и lastCheckedAt=null). + BackgroundJob.Enqueue(j => j.RunAsync(CancellationToken.None)); + return Task.CompletedTask; } diff --git a/src/food-market.shared/Barcodes/InternalBarcode.cs b/src/food-market.shared/Barcodes/InternalBarcode.cs index 4aeb5a8..429e24a 100644 --- a/src/food-market.shared/Barcodes/InternalBarcode.cs +++ b/src/food-market.shared/Barcodes/InternalBarcode.cs @@ -23,8 +23,12 @@ public static int Ean13CheckDigit(string body12) return (10 - sum % 10) % 10; } - /// Подобрать 2-значный префикс, не входящий в весовые/ценовые префиксы орг. - /// Приоритет: 02 → свободный из 20–29 → свободный из 90–99 → 02 (как крайний). + /// Подобрать 2-значный префикс внутреннего кода. GS1 restricted circulation + /// (внутримагазинные) — это 02 и 20–29. НЕ используем «02»: с ведущим нулём 13-значный + /// код выглядит как UPC-A (зона США) и может совпасть с реальным товаром. Берём 2x + /// (20–29) — настоящие коды такие префиксы не используют — не пересекаясь с весовыми/ + /// ценовыми префиксами организации (если разбор этикеток включён). Приоритет: + /// свободный из 20–29 → свободный из 90–99 → 02 (крайний случай). public static int PickPrefix(WeightedBarcodeSettings settings) { var forbidden = new HashSet(); @@ -33,10 +37,9 @@ public static int PickPrefix(WeightedBarcodeSettings settings) foreach (var p in settings.WeightPrefixes) forbidden.Add(p); foreach (var p in settings.PricePrefixes) forbidden.Add(p); } - if (!forbidden.Contains(2)) return 2; for (int p = 20; p <= 29; p++) if (!forbidden.Contains(p)) return p; for (int p = 90; p <= 99; p++) if (!forbidden.Contains(p)) return p; - return 2; + return 2; // всё занято (весьма редкий случай) — крайний резерв } /// Сгенерировать внутренний EAN-13. — источник diff --git a/tests/food-market.UnitTests/InternalBarcodeTests.cs b/tests/food-market.UnitTests/InternalBarcodeTests.cs index dfac35e..a1f7b1a 100644 --- a/tests/food-market.UnitTests/InternalBarcodeTests.cs +++ b/tests/food-market.UnitTests/InternalBarcodeTests.cs @@ -20,11 +20,15 @@ public void Generated_is_valid_ean13_with_correct_check_digit() } [Fact] - public void Default_prefix_is_02_and_not_parsed_as_weighted() + public void Default_prefix_is_2x_not_leading_zero() { var code = InternalBarcode.Generate(WeightedBarcodeSettings.Default); - code.Should().StartWith("02"); - // Внутренний код НЕ должен распознаваться как весовая/ценовая этикетка. + // Внутренний код — зона restricted circulation 20–29 (не «02»: с ведущим нулём + // 13-значный код выглядит как UPC-A и может совпасть с реальным товаром). + code.Should().StartWith("2"); + code[1].Should().BeInRange('0', '9'); + code[0].Should().NotBe('0'); + // По умолчанию (разбор этикеток выключен) как весовая не распознаётся. WeightedBarcode.Parse(code, WeightedBarcodeSettings.Default).IsWeighted.Should().BeFalse(); }