food-market/src/food-market.web/src/lib/barcode.ts
nns 195ca2e2bb ui: barcode регенерация / h-10 поля / прыжок на страницу / min-max скрытие
- lib/barcode.ts: новая утилита generateBarcode(type) — валидные коды
  под все форматы (EAN-13 с префиксом 2, EAN-8 с checksum, UPC-A
  с checksum, UPC-E 8 цифр, Code128/Code39 12 буквенно-цифровых).
- ProductEditPage: при смене типа штрихкода в dropdown поле кода
  регенерируется под новый формат.
- Field.tsx: единая высота h-10 и leading-none для TextInput/Select
  чтобы Страна/Валюта/НДС в настройках были одного размера.
  TextArea оставлен с h-auto для multiline.
- Pagination.tsx: рядом с ← → добавлен input «Страница [N] из M»
  для прыжка на произвольную страницу (Enter / blur применяют).
- ProductEditPage: блок мин/макс остатков теперь показывается только
  при org.showMinMaxStock (сама настройка добавится следующим коммитом).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 19:02:39 +05:00

74 lines
2.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Штрихкод-утилиты: генерация валидных кодов под разные форматы.
//
// Внутренние EAN-13 магазина начинаются с "2" — зарезервированный префикс
// для in-store use, не пересекается с GTIN реальных товаров.
import { BarcodeType } from '@/lib/types'
function digitsChecksum(first: string, weightAtOdd: number): number {
// Общая EAN/UPC-подобная формула: сумма с чередующимися весами, остаток от 10.
// Нечётные позиции (с индекса 0) — weightAtOdd, чётные — 1.
let sum = 0
for (let i = 0; i < first.length; i++) {
const d = first.charCodeAt(i) - 48
sum += i % 2 === 0 ? d * weightAtOdd : d
}
return (10 - (sum % 10)) % 10
}
function randomDigits(n: number): string {
let s = ''
for (let i = 0; i < n; i++) s += Math.floor(Math.random() * 10).toString()
return s
}
function randomAlnum(n: number): string {
const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
let s = ''
for (let i = 0; i < n; i++) s += alphabet[Math.floor(Math.random() * alphabet.length)]
return s
}
function ean13(): string {
const body = '2' + randomDigits(11)
return body + digitsChecksum(body, 3).toString()
}
function ean8(): string {
// EAN-8: 7 цифр + checksum. Веса: нечётные×3, чётные×1 (с индекса 0).
const body = randomDigits(7)
return body + digitsChecksum(body, 3).toString()
}
function upca(): string {
// UPC-A: 11 цифр + checksum. Та же формула что у EAN-13.
const body = randomDigits(11)
return body + digitsChecksum(body, 3).toString()
}
function upce(): string {
// Упрощённая генерация: 8 случайных цифр (реальный UPC-E строится через
// сжатие UPC-A по спецправилам; для внутренних нужд достаточно числовой
// последовательности нужной длины).
return randomDigits(8)
}
/** Сгенерировать внутренний EAN-13 с префиксом "2" и случайной серединой. */
export function generateEan13InternalPrefix2(): string {
return ean13()
}
/** Сгенерировать штрихкод под указанный формат. */
export function generateBarcode(type: BarcodeType): string {
switch (type) {
case BarcodeType.Ean13: return ean13()
case BarcodeType.Ean8: return ean8()
case BarcodeType.Upca: return upca()
case BarcodeType.Upce: return upce()
case BarcodeType.Code128:
case BarcodeType.Code39: return randomAlnum(12)
case BarcodeType.Other:
default: return ean13()
}
}