feat(barcode): авто-генерация EAN-13 при добавлении штрихкода
Some checks are pending
CI / POS (WPF, Windows) (push) Waiting to run
CI / Backend (.NET 8) (push) Successful in 26s
CI / Web (React + Vite) (push) Successful in 24s
Docker Images / API image (push) Successful in 32s
Docker Images / Web image (push) Successful in 26s
Docker Images / Deploy stage (push) Successful in 19s

Новый штрихкод в товаре сразу получает валидный 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) <noreply@anthropic.com>
This commit is contained in:
nns 2026-04-24 16:35:40 +05:00
parent 81c586342c
commit e60cd928d2
2 changed files with 23 additions and 1 deletions

View file

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

View file

@ -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) })