bff6d70b17
Aplicatie Flask + SQLite pentru productie alimentara: cantarirea componentelor unui produs finit si tiparirea etichetei prin QZ Tray. - produse finite (retete) formate din componente cantarite - designer vizual de etichete (mm/px, zona sigura, campuri variabile, cod QR cu continut din mai multe bucati) - legare produs finit -> sablon de eticheta - navigare pe doua taburi: Cantarire / Setari - administrare: componente, produse finite, sabloane, campuri, istoric - backup, resetare baza de date, checklist de pregatire, /healthz - CLI de administrare si serviciu systemd pentru productie - vendor/syncpulse-scale-bridge (GPL-3.0-or-later) pentru citirea cantarului
191 lines
7.7 KiB
Python
191 lines
7.7 KiB
Python
"""Campuri variabile proprii (custom.*): CRUD, validare si randare pe eticheta."""
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
|
|
import pytest
|
|
|
|
from cantarire import designer
|
|
|
|
|
|
def _create_field(client, label="Contact firmă", template="Tel. {{company.phone}}",
|
|
active="on"):
|
|
return client.post("/campuri/nou", data={
|
|
"label": label, "template": template, "active": active,
|
|
}, follow_redirects=True)
|
|
|
|
|
|
def test_fields_page_and_nav(client):
|
|
page = client.get("/campuri")
|
|
assert page.status_code == 200
|
|
html = page.get_data(as_text=True)
|
|
assert "Câmpuri variabile" in html
|
|
assert 'href="/campuri/nou"' in html
|
|
# link in subnavigatia din Setari, nu in bara principala
|
|
flat = " ".join(html.split())
|
|
assert 'href="/campuri"' in flat
|
|
assert ">Câmpuri</a>" in flat
|
|
|
|
|
|
def test_create_field_generates_key_and_shows_preview(client):
|
|
_create_field(client)
|
|
html = client.get("/campuri").get_data(as_text=True)
|
|
assert "Contact firmă" in html
|
|
assert "custom.contact_firma" in html
|
|
assert "Tel. 0700 000 000" in html # rezultatul demo
|
|
|
|
with client.application.app_context():
|
|
from cantarire.repositories import get_label_field_by_key
|
|
|
|
row = get_label_field_by_key("custom.contact_firma")
|
|
assert row is not None and row["active"] == 1
|
|
|
|
|
|
def test_duplicate_labels_get_unique_keys(client):
|
|
_create_field(client, label="Alergeni", template="Poate conține gluten")
|
|
_create_field(client, label="Alergeni", template="Poate conține lactoză")
|
|
with client.application.app_context():
|
|
from cantarire.repositories import list_label_fields
|
|
|
|
keys = [row["key"] for row in list_label_fields()]
|
|
assert keys == ["custom.alergeni", "custom.alergeni_2"]
|
|
|
|
|
|
def test_unknown_placeholder_is_rejected(client):
|
|
response = _create_field(client, label="Greșit", template="X {{does.not.exist}}")
|
|
html = response.get_data(as_text=True)
|
|
assert "Campuri necunoscute" in html
|
|
with client.application.app_context():
|
|
from cantarire.repositories import list_label_fields
|
|
|
|
assert list_label_fields() == []
|
|
|
|
|
|
def test_empty_template_is_rejected(client):
|
|
response = _create_field(client, label="Gol", template=" ")
|
|
assert "Textul campului este obligatoriu." in response.get_data(as_text=True)
|
|
|
|
|
|
def test_update_keeps_key_and_can_deactivate(client):
|
|
_create_field(client)
|
|
with client.application.app_context():
|
|
from cantarire.repositories import get_label_field_by_key
|
|
|
|
field_id = get_label_field_by_key("custom.contact_firma")["id"]
|
|
|
|
client.post(f"/campuri/{field_id}/editare", data={
|
|
"label": "Contact (nou)", "template": "{{company.phone}}",
|
|
}, follow_redirects=True)
|
|
|
|
with client.application.app_context():
|
|
from cantarire.repositories import get_label_field, list_label_fields
|
|
|
|
row = get_label_field(field_id)
|
|
assert row["label"] == "Contact (nou)"
|
|
assert row["key"] == "custom.contact_firma" # cheia ramane stabila
|
|
assert row["active"] == 0 # fara bifa = inactiv
|
|
assert [f["key"] for f in list_label_fields()] == [] # ascuns din designer
|
|
|
|
|
|
def test_delete_field(client):
|
|
_create_field(client, label="Șters", template="text")
|
|
with client.application.app_context():
|
|
from cantarire.repositories import get_label_field_by_key
|
|
|
|
field_id = get_label_field_by_key("custom.sters")["id"]
|
|
response = client.post(f"/campuri/{field_id}/stergere", follow_redirects=True)
|
|
assert "a fost sters" in response.get_data(as_text=True)
|
|
with client.application.app_context():
|
|
from cantarire.repositories import get_label_field_by_key
|
|
|
|
assert get_label_field_by_key("custom.sters") is None
|
|
|
|
|
|
def test_preview_endpoint(client):
|
|
ok = client.get("/campuri/previzualizare",
|
|
query_string={"template": "{{recipe.name}} · {{package.lot}}"})
|
|
assert ok.get_json() == {"ok": True, "value": "Fasole cu carne · LOT-2610-A"}
|
|
|
|
bad = client.get("/campuri/previzualizare", query_string={"template": "{{nope.x}}"})
|
|
data = bad.get_json()
|
|
assert data["ok"] is False and "Campuri necunoscute" in data["error"]
|
|
|
|
|
|
def test_custom_field_is_offered_in_designer_picker(client):
|
|
_create_field(client, label="Contact firmă", template="Tel. {{company.phone}}")
|
|
response = client.post("/sabloane/designer/nou", data={"name": "Cu camp propriu"},
|
|
follow_redirects=True)
|
|
assert response.status_code == 200
|
|
html = response.get_data(as_text=True)
|
|
assert "custom.contact_firma" in html
|
|
assert "Tel. 0700 000 000" in html # sample pentru previzualizare
|
|
# denumirea trece prin tojson, deci diacriticele sunt escapate
|
|
assert "Contact firm\\u0103" in html
|
|
|
|
|
|
def test_custom_field_renders_on_label(client):
|
|
_create_field(client, label="Contact", template="Tel. {{company.phone}}")
|
|
design = designer.validate_design({
|
|
"width_mm": 100, "height_mm": 60,
|
|
"blocks": [
|
|
{"type": "field", "x": 3, "y": 3, "w": 70, "h": 6, "field": "custom.contact"},
|
|
],
|
|
}, 100, 60)
|
|
assert design["blocks"][0]["field"] == "custom.contact"
|
|
|
|
with client.application.app_context():
|
|
from cantarire.repositories import list_label_fields
|
|
|
|
fields = designer.apply_custom_fields(designer.demo_fields(), list_label_fields())
|
|
html = designer.render_design(design, fields, [])
|
|
assert "Tel. 0700 000 000" in html
|
|
|
|
|
|
def test_deleted_custom_field_renders_empty_not_wrong(client):
|
|
"""Un design care refera un camp sters nu trebuie sa cada sau sa afiseze altceva."""
|
|
design = designer.validate_design({
|
|
"width_mm": 100, "height_mm": 60,
|
|
"blocks": [{"type": "field", "x": 3, "y": 3, "w": 70, "h": 6, "field": "custom.sters"}],
|
|
}, 100, 60)
|
|
assert design["blocks"][0]["field"] == "custom.sters"
|
|
html = designer.render_design(design, designer.demo_fields(), [])
|
|
assert "Fasole cu carne" not in html # nu cade pe alt camp
|
|
assert "> <" in html or html.count("d-field") == 1
|
|
|
|
|
|
def test_custom_field_template_rejects_custom_reference():
|
|
"""Campurile proprii se pot referi doar la campuri fixe (fara bucle)."""
|
|
errors = designer.custom_template_errors("X {{custom.altceva}}")
|
|
assert errors and "Campuri necunoscute" in errors[0]
|
|
assert designer.custom_template_errors("{{company.phone}}") == []
|
|
|
|
|
|
@pytest.mark.parametrize("text,expected", [
|
|
("", ""),
|
|
("text simplu", "text simplu"),
|
|
("{{company.name}}", "Moto Adv SRL"),
|
|
("{{ package.lot }}/{{package.operator}}", "LOT-2610-A/Maria"),
|
|
])
|
|
def test_expand_custom(text, expected):
|
|
assert designer.expand_custom(text, designer.demo_fields()) == expected
|
|
|
|
|
|
def test_usage_counter_reports_templates(client):
|
|
_create_field(client, label="Folosit", template="X")
|
|
client.post("/sabloane/designer/nou", data={"name": "Cu folosit"})
|
|
with client.application.app_context():
|
|
from cantarire.repositories import (count_label_field_usage, get_default_template,
|
|
get_label_field_by_key, list_templates,
|
|
save_template_design)
|
|
|
|
field = get_label_field_by_key("custom.folosit")
|
|
target = [t for t in list_templates() if t["name"] == "Cu folosit"][0]
|
|
payload = {"version": 1, "width_mm": 100, "height_mm": 60, "blocks": [
|
|
{"id": "b1", "type": "field", "x": 1, "y": 1, "w": 50, "h": 6,
|
|
"field": "custom.folosit"},
|
|
]}
|
|
save_template_design(target["id"], json.dumps(payload))
|
|
assert count_label_field_usage(field["key"]) == 1
|
|
assert count_label_field_usage("custom.inexistent") == 0
|
|
assert get_default_template() is not None
|