"""Randarea etichetelor de produs finit si contractul de printare. Eticheta este generata din designul vizual (blocuri in milimetri) si contine: - denumirea produsului finit si greutatea totala - defalcarea pe componente (ex. 250 g Fasole, 200 g Carne) - lot, data, operator """ from __future__ import annotations from datetime import datetime from typing import Any, Optional from . import designer from .repositories import ValidationError, list_label_fields from .units import format_weight as _format_weight def _format_datetime(value: Any, fmt: str = "%d.%m.%Y %H:%M") -> str: if not value: return "" try: return datetime.fromisoformat(str(value)).strftime(fmt) except (TypeError, ValueError): return str(value) class LabelTemplateError(ValidationError): pass def sample_package() -> dict[str, Any]: now = datetime.now().astimezone().isoformat(timespec="seconds") return { "id": 0, "created_at": now, "completed_at": now, "status": "done", "recipe_code": "PF-DEMO", "recipe_name": "Fasole cu carne", "unit": "g", "target_weight": 450, "total_weight": 450, "lot": "LOT-DEMO", "operator": "Operator", "notes": "", "print_count": 0, "items": [ {"position": 1, "component_name": "Fasole", "weight": 250, "unit": "g"}, {"position": 2, "component_name": "Carne", "weight": 200, "unit": "g"}, ], } def sample_recipe() -> dict[str, Any]: return { "id": 0, "code": "PF-DEMO", "name": "Fasole cu carne", "unit": "g", "target_weight": 450, "note": "", "components": [ {"position": 1, "name": "Fasole", "code": "FAS", "target_weight": 250}, {"position": 2, "name": "Carne", "code": "CAR", "target_weight": 200}, ], } def build_context( package: dict[str, Any], items: list[dict[str, Any]], recipe: Optional[dict[str, Any]], settings: dict[str, str], template: Optional[dict[str, Any]] = None, ) -> dict[str, Any]: """Contextul vechi Jinja, pastrat pentru compatibilitate cu integrarile existente.""" return { "package": package, "items": items, "recipe": recipe or { "id": None, "code": package.get("recipe_code", ""), "name": package.get("recipe_name", ""), "unit": package.get("unit", ""), "components": [], }, "company": { "name": settings.get("company_name", ""), "address": settings.get("company_address", ""), "phone": settings.get("company_phone", ""), }, "label": { "width_mm": float((template or {}).get("width_mm", 100)), "height_mm": float((template or {}).get("height_mm", 60)), }, "settings": settings, } def design_for(template: dict[str, Any]) -> dict[str, Any]: """Designul sablonului; daca lipseste (sablon vechi), se foloseste cel implicit.""" width = float(template.get("width_mm") or 100) height = float(template.get("height_mm") or 60) design = designer.load_design(template.get("design_json") or "", width, height) if design is None: return designer.default_design(width, height) return design def render_body( template: dict[str, Any], package: dict[str, Any], items: Optional[list[dict[str, Any]]] = None, recipe: Optional[dict[str, Any]] = None, settings: Optional[dict[str, str]] = None, ) -> str: item_list = items if items is not None else package.get("items") or [] design = design_for(template) fields = designer.resolve_fields(package, item_list, recipe, settings or {}) fields = designer.apply_custom_fields(fields, list_label_fields()) return designer.render_design(design, fields, item_list) def template_css(template: dict[str, Any]) -> str: css = template.get("css") or "" return f"{designer.DESIGN_CSS}\n{css}".strip() def validate_template(template: dict[str, Any], settings: Optional[dict[str, str]] = None) -> None: """Revalideaza designul si il randeaza cu date demo, ca sa prindem erorile.""" design = designer.validate_design( design_for(template), float(template.get("width_mm") or 100), float(template.get("height_mm") or 60), ) demo = designer.apply_custom_fields(designer.demo_fields(), list_label_fields()) designer.render_design(design, demo, designer.demo_items()) def print_payload( package: dict[str, Any], template: dict[str, Any], settings: dict[str, str], recipe: Optional[dict[str, Any]] = None, ) -> dict[str, Any]: items = package.get("items") or [] html = render_body(template, package, items, recipe, settings) breakdown = ", ".join( f"{_format_weight(item['weight'])} {item['unit']} {item['component_name']}" for item in items ) return { "job_id": f"p{package['id']}-{int(package.get('print_count') or 0) + 1}", "package_id": package["id"], "template": { "id": template["id"], "name": template["name"], "width_mm": float(template["width_mm"]), "height_mm": float(template["height_mm"]), }, "html": html, "css": template_css(template), "fields": { "recipe_code": package.get("recipe_code", ""), "recipe_name": package.get("recipe_name", ""), "lot": package.get("lot", ""), "operator": package.get("operator", ""), "total_weight": _format_weight(package.get("total_weight")), "unit": package.get("unit", ""), "completed_at": package.get("completed_at", ""), "breakdown": breakdown, }, "print_url": f"/labels/{package['id']}/print", "ack_url": f"/api/packages/{package['id']}/printed", }