Files
ske087 bff6d70b17 Initial commit: aplicatie de cantarire si etichetare
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
2026-10-18 23:38:45 +03:00

132 lines
3.6 KiB
Python

from __future__ import annotations
import sys
from pathlib import Path
import pytest
ROOT = Path(__file__).resolve().parent.parent
if str(ROOT) not in sys.path:
sys.path.insert(0, str(ROOT))
from cantarire import create_app # noqa: E402
STABLE_READING = {
"ok": True,
"error": None,
"profile": "test-scale",
"port": "COM1",
"protocol": "mt_sics",
"open": True,
"value": 250.0,
"unit": "g",
"stable": True,
"stale": False,
"overload": False,
"age_ms": 120,
"measured_at": "2026-10-18T10:00:00.000+03:00",
"raw_hex": "53 20 53 20 20 20 20 32 35 30 2E 30",
"fields": {},
}
class FakeScale:
available = True
error = None
def __init__(self):
self.reading = dict(STABLE_READING)
self.calls = 0
self.probed = None
def profiles(self):
return [{"name": "test-scale", "label": "Cântar test", "port": "COM1", "protocol": "mt_sics"}]
def read(self, profile_name="", **_kwargs):
self.calls += 1
return dict(self.reading)
def probe(self, port, baud=9600, framing="8N1", seconds=5.0):
self.probed = {"port": port, "baud": baud, "framing": framing, "seconds": seconds}
return {
"ok": True,
"port": port,
"settings": f"{port} {baud} {framing}, listen only",
"baud": baud,
"framing": framing,
"seconds": seconds,
"error": None,
"bytes": 13,
"frames": [{"age_ms": 10, "hex": "02 2D 30 20 30 30 30 30 30 30 30 30 30 30 0D",
"text": "<STX>-0 000000000000<CR>", "size": 13}],
"guesses": [{"protocol": "toledo_cont", "name": "Toledo Continuous",
"kind": "continuous", "count": 2, "value": 12.345,
"unit": "kg", "stable": True, "specificity": 3}],
}
@pytest.fixture()
def app(tmp_path):
application = create_app({
"TESTING": True,
"INSTANCE_DIR": tmp_path,
"DATABASE": tmp_path / "test.db",
"BRIDGE_CONFIG": tmp_path / "bridge.json",
"SIMULATE": "",
"AUTO_PRINT": False,
"SECRET_KEY": "test",
})
application.extensions["scale"] = FakeScale()
return application
@pytest.fixture()
def client(app):
return app.test_client()
@pytest.fixture()
def scale(app):
return app.extensions["scale"]
def _create_component(client, code: str, name: str, unit: str = "g") -> int:
response = client.post("/componente/nou", data={
"code": code, "name": name, "unit": unit, "active": "on",
})
assert response.status_code == 302, response.get_data(as_text=True)
with client.application.app_context():
from cantarire.repositories import get_component_by_code
return get_component_by_code(code)["id"]
@pytest.fixture()
def fasole(client):
return _create_component(client, "FAS", "Fasole")
@pytest.fixture()
def carne(client):
return _create_component(client, "CAR", "Carne")
@pytest.fixture()
def recipe(client, fasole, carne):
response = client.post("/retete/nou", data={
"code": "PF-001",
"name": "Fasole cu carne",
"unit": "g",
"target_weight": "450",
"component_1_id": str(fasole),
"component_1_target": "250",
"component_2_id": str(carne),
"component_2_target": "200",
"active": "on",
})
assert response.status_code == 302, response.get_data(as_text=True)
with client.application.app_context():
from cantarire.repositories import list_recipes
return list_recipes()[0]