From 42a3d2aa5078c1f8abc4b9fd51f92be111194387 Mon Sep 17 00:00:00 2001 From: nns <278048682+nurdotnet@users.noreply.github.com> Date: Fri, 24 Apr 2026 16:35:40 +0500 Subject: [PATCH] =?UTF-8?q?feat(barcode):=20=D0=B0=D0=B2=D1=82=D0=BE-?= =?UTF-8?q?=D0=B3=D0=B5=D0=BD=D0=B5=D1=80=D0=B0=D1=86=D0=B8=D1=8F=20EAN-13?= =?UTF-8?q?=20=D0=BF=D1=80=D0=B8=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B8=20=D1=88=D1=82=D1=80=D0=B8=D1=85=D0=BA?= =?UTF-8?q?=D0=BE=D0=B4=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Новый штрихкод в товаре сразу получает валидный EAN-13 с префиксом "2" (зарезервирован под внутренние штрихкоды магазина, не конфликтует с GTIN производителей). Пользователь может заменить на реальный считанный — поле остаётся редактируемым. Утилита lib/barcode.ts::generateEan13InternalPrefix2() генерирует 11 случайных цифр после "2" и дописывает контрольную сумму EAN-13. Уникальность штрихкода в организации уже обеспечивает IX_product_barcodes_OrganizationId_Code. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/food-market.web/src/lib/barcode.ts | 21 +++++++++++++++++++ .../src/pages/ProductEditPage.tsx | 3 ++- 2 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 src/food-market.web/src/lib/barcode.ts diff --git a/src/food-market.web/src/lib/barcode.ts b/src/food-market.web/src/lib/barcode.ts new file mode 100644 index 0000000..37767ad --- /dev/null +++ b/src/food-market.web/src/lib/barcode.ts @@ -0,0 +1,21 @@ +// EAN-13 утилиты. +// +// Внутренние штрихкоды магазина начинаются с "2" — это зарезервированный +// префикс для in-store use, он не пересекается с GTIN реальных товаров +// от производителей. + +function ean13Checksum(first12: string): number { + let sum = 0 + for (let i = 0; i < 12; i++) { + const d = first12.charCodeAt(i) - 48 + sum += i % 2 === 0 ? d : d * 3 + } + return (10 - (sum % 10)) % 10 +} + +/** Сгенерировать внутренний EAN-13 с префиксом "2" и случайной серединой. */ +export function generateEan13InternalPrefix2(): string { + let body = '2' + for (let i = 0; i < 11; i++) body += Math.floor(Math.random() * 10).toString() + return body + ean13Checksum(body).toString() +} diff --git a/src/food-market.web/src/pages/ProductEditPage.tsx b/src/food-market.web/src/pages/ProductEditPage.tsx index 7695df9..7e072e8 100644 --- a/src/food-market.web/src/pages/ProductEditPage.tsx +++ b/src/food-market.web/src/pages/ProductEditPage.tsx @@ -11,6 +11,7 @@ import { import { useOrgSettings } from '@/lib/useOrgSettings' import { BarcodeType, Packaging, type Product } from '@/lib/types' import { ProductImageGallery } from '@/components/ProductImageGallery' +import { generateEan13InternalPrefix2 } from '@/lib/barcode' interface PriceRow { id?: string; priceTypeId: string; amount: number; currencyId: string } interface BarcodeRow { id?: string; code: string; type: BarcodeType; isPrimary: boolean } @@ -167,7 +168,7 @@ export function ProductEditPage() { setForm({ ...form, prices: form.prices.map((p, ix) => ix === i ? { ...p, ...patch } : p) }) const addBarcode = () => setForm({ ...form, barcodes: [...form.barcodes, { - code: '', type: BarcodeType.Ean13, isPrimary: form.barcodes.length === 0, + code: generateEan13InternalPrefix2(), type: BarcodeType.Ean13, isPrimary: form.barcodes.length === 0, }] }) const removeBarcode = (i: number) => setForm({ ...form, barcodes: form.barcodes.filter((_, ix) => ix !== i) })