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(); }