Files
ACORE/apps/it_manager/equipment_labels.py
T

96 lines
2.9 KiB
Python

"""QR + EAN-13 для инвентарных этикеток оборудования."""
from __future__ import annotations
import base64
import io
from typing import Any
import qrcode
from barcode import get_barcode_class
from barcode.writer import ImageWriter
from PIL import Image
# Внутренний префикс EAN (2 = локальное использование) + 00 → 200…
_EAN_PREFIX = '200'
def equipment_ean13_body(pk: int) -> str:
"""12 цифр без контрольной: 200 + pk (9 знаков)."""
if pk < 1 or pk > 999_999_999:
raise ValueError(f'pk вне диапазона для EAN-13: {pk}')
return f'{_EAN_PREFIX}{pk:09d}'
def equipment_ean13(pk: int) -> str:
"""Полный EAN-13 (13 цифр) для единицы оборудования."""
EAN13 = get_barcode_class('ean13')
# python-barcode сам считает check digit из 12 цифр
code = EAN13(equipment_ean13_body(pk), writer=ImageWriter())
return code.get_fullcode()
def equipment_qr_payload(unit) -> str:
"""Что кодируем в QR: инв. №, иначе EQ{pk}."""
inv = (getattr(unit, 'inventory_number', None) or '').strip()
if inv:
return inv
return f'EQ{unit.pk}'
def _png_b64(img: Image.Image) -> str:
buf = io.BytesIO()
img.save(buf, format='PNG')
return base64.b64encode(buf.getvalue()).decode('ascii')
def qr_png_base64(payload: str, *, box_size: int = 4, border: int = 1) -> str:
qr = qrcode.QRCode(version=None, box_size=box_size, border=border)
qr.add_data(payload)
qr.make(fit=True)
img = qr.make_image(fill_color='black', back_color='white')
if not isinstance(img, Image.Image):
img = img.get_image()
return _png_b64(img.convert('RGB'))
def ean13_png_base64(pk: int) -> tuple[str, str]:
"""(png_base64, human_digits)."""
EAN13 = get_barcode_class('ean13')
code = EAN13(
equipment_ean13_body(pk),
writer=ImageWriter(),
)
buf = io.BytesIO()
code.write(
buf,
options={
'module_width': 0.25,
'module_height': 10.0,
'quiet_zone': 2.0,
'font_size': 8,
'text_distance': 3.0,
'write_text': True,
},
)
buf.seek(0)
img = Image.open(buf).convert('RGB')
return _png_b64(img), code.get_fullcode()
def label_payload_for_unit(unit) -> dict[str, Any]:
qr_text = equipment_qr_payload(unit)
ean_b64, ean_digits = ean13_png_base64(unit.pk)
model = unit.equipment_model
return {
'id': unit.pk,
'model': str(model) if model else '—',
'type_name': model.equipment_type.name if model and model.equipment_type_id else '',
'serial': unit.serial_number or '',
'inventory': (unit.inventory_number or '').strip(),
'qr_payload': qr_text,
'qr_png': qr_png_base64(qr_text),
'ean13': ean_digits,
'ean_png': ean_b64,
}