From a159eb2b8ba92258242b7a62f5c646affdfdaade Mon Sep 17 00:00:00 2001 From: Adriano Dal Pastro Date: Tue, 28 Jul 2026 15:42:17 +0000 Subject: [PATCH] feat(stations): ricette in lista, reset per riga e cambio stazione al volo Punto 7 del documento modifiche del 28/07, terza richiesta di Menoncin. GET /api/stations ora usa StationWithRecipesResponse, che era gia' definita nello schema senza che nessun endpoint la usasse. Non costa query in piu': Station.assignments e' gia' selectin-loaded. In lista compaiono le sole ricette attive, coerente con cio' che l'operatore vede davvero a quella stazione. DELETE /api/stations/{id}/recipes azzera le assegnazioni di una stazione senza eliminarla, cosi' si riassegna da capo. E' idempotente: azzerare una stazione gia' vuota risponde removed=0, non 404. In tabella e' un pulsante per riga, con conferma perche' l'azione e' distruttiva. Il cambio stazione al volo (?station=CODE, override in sessione) serve al collaudo: senza, provare N stazioni richiede N PC. E' dietro il flag STATION_SWITCH_ENABLED, default 0. Non e' legato al ruolo admin perche' /measure/select richiede MeasurementTec, e gatearlo su is_admin avrebbe escluso proprio chi fa il collaudo. In fabbrica l'identita' della stazione viene dall'installazione locale, e misurare contro le ricette di un'altra stazione romperebbe in silenzio la tracciabilita': per questo il codice richiesto e' validato prima di essere memorizzato - un refuso lascia l'operatore sulla stazione configurata con un messaggio, invece di incastrarlo su una stazione inesistente - e quando l'override e' attivo la pagina lo dichiara, con il codice configurato accanto e un link per ripristinare. Le traduzioni: pybabel update aveva indovinato dieci voci da stringhe simili marcandole fuzzy, in entrambi i cataloghi. "Azzera Stazione" era diventato "Crea Stazione", l'opposto, su un dialogo distruttivo. Non si vedeva perche' la compilazione salta le fuzzy e ricade sul sorgente italiano, ma sarebbe emerso al primo build con --use-fuzzy. Tradotte per esteso le 17 stringhe nuove in IT ed EN e tolti i flag. Co-Authored-By: Claude Opus 5 (1M context) --- .env.example | 5 + src/backend/api/routers/stations.py | 45 ++- src/backend/models/api/station.py | 7 + src/backend/services/station_service.py | 20 ++ src/backend/tests/test_stations_api.py | 173 ++++++++++ src/frontend/flask_app/blueprints/admin.py | 11 + src/frontend/flask_app/blueprints/measure.py | 68 +++- src/frontend/flask_app/config.py | 8 + .../flask_app/templates/admin/stations.html | 121 ++++++- .../templates/measure/select_recipe.html | 17 + .../flask_app/tests/test_admin_stations.py | 44 +++ .../tests/test_measure_station_filter.py | 120 ++++++- .../translations/en/LC_MESSAGES/messages.po | 302 ++++++++++------- .../translations/it/LC_MESSAGES/messages.po | 302 ++++++++++------- .../flask_app/translations/messages.pot | 308 +++++++++++------- 15 files changed, 1188 insertions(+), 363 deletions(-) diff --git a/.env.example b/.env.example index e4f24b2..aba9d14 100644 --- a/.env.example +++ b/.env.example @@ -24,6 +24,11 @@ API_SERVER_URL=http://localhost:8000 # Each physical tablet/PC deployment must set this unique per-station value. # Leave empty only for a single-station all-in-one demo using ST-DEFAULT. STATION_CODE=ST-DEFAULT +# Allow switching station from the URL (?station=CODE) during commissioning, so a +# single PC can exercise several stations. Leave at 0 in production: on the shop +# floor the station identity comes from the local install, and measuring against +# another station's recipes would silently break traceability. +STATION_SWITCH_ENABLED=0 # --- File Storage --- # Resolved against the project root in src/backend/config.py. diff --git a/src/backend/api/routers/stations.py b/src/backend/api/routers/stations.py index 1dabf91..3935a16 100644 --- a/src/backend/api/routers/stations.py +++ b/src/backend/api/routers/stations.py @@ -11,6 +11,8 @@ from src.backend.models.api.station import ( StationResponse, StationRecipeAssignmentCreate, StationRecipeAssignmentResponse, + StationResetResponse, + StationWithRecipesResponse, RecipeSummary, ) from src.backend.services import station_service @@ -18,15 +20,36 @@ from src.backend.services import station_service router = APIRouter(prefix="/api/stations", tags=["stations"]) -@router.get("", response_model=list[StationResponse]) +@router.get("", response_model=list[StationWithRecipesResponse]) async def list_stations( active_only: bool = False, admin: User = Depends(require_admin_user), db: AsyncSession = Depends(get_db), ): - """List all stations (admin only).""" + """List all stations with the recipes assigned to each (admin only). + + The admin station list shows which products a station handles, so the + assignments travel with the station instead of needing a call per row. + Station.assignments is selectin-loaded, so this costs no extra query. + + Only active recipes are listed, matching what an operator actually sees + at that station via /by-code/{code}/recipes. + """ stations = await station_service.list_stations(db, active_only=active_only) - return [StationResponse.model_validate(s) for s in stations] + return [ + StationWithRecipesResponse( + **StationResponse.model_validate(s).model_dump(), + recipes=sorted( + ( + RecipeSummary.model_validate(a.recipe) + for a in s.assignments + if a.recipe is not None and a.recipe.active + ), + key=lambda r: r.code, + ), + ) + for s in stations + ] @router.post("", response_model=StationResponse, status_code=status.HTTP_201_CREATED) @@ -128,6 +151,22 @@ async def assign_recipe_to_station( return StationRecipeAssignmentResponse.model_validate(assignment) +@router.delete("/{station_id}/recipes", response_model=StationResetResponse) +async def reset_station_recipes( + station_id: int, + admin: User = Depends(require_admin_user), + db: AsyncSession = Depends(get_db), +): + """Clear every recipe assignment of a station (admin only). + + Backs the per-row reset in the admin list: the station is left with no + recipes and can be reassigned from scratch, without deleting the station + itself. Idempotent - resetting an already empty station reports removed=0. + """ + removed = await station_service.unassign_all_recipes(db, station_id) + return StationResetResponse(station_id=station_id, removed=removed) + + @router.delete( "/{station_id}/recipes/{recipe_id}", status_code=status.HTTP_204_NO_CONTENT, diff --git a/src/backend/models/api/station.py b/src/backend/models/api/station.py index 80c25fd..24e8fd1 100644 --- a/src/backend/models/api/station.py +++ b/src/backend/models/api/station.py @@ -57,3 +57,10 @@ class RecipeSummary(BaseModel): class StationWithRecipesResponse(StationResponse): recipes: list[RecipeSummary] = Field(default_factory=list) + + +class StationResetResponse(BaseModel): + """Outcome of clearing every recipe assignment of a station.""" + + station_id: int + removed: int diff --git a/src/backend/services/station_service.py b/src/backend/services/station_service.py index fc41832..18f5f19 100644 --- a/src/backend/services/station_service.py +++ b/src/backend/services/station_service.py @@ -138,6 +138,26 @@ async def unassign_recipe( await db.flush() +async def unassign_all_recipes(db: AsyncSession, station_id: int) -> int: + """Drop every recipe assignment of a station and return how many were removed. + + Used by the per-row reset in the admin station list: the station goes back to + having no recipes so it can be reassigned from scratch. Unlike unassign_recipe + this is idempotent - resetting an already empty station returns 0 rather than 404. + """ + await get_station(db, station_id) + result = await db.execute( + select(StationRecipeAssignment).where( + StationRecipeAssignment.station_id == station_id + ) + ) + assignments = list(result.scalars().all()) + for assignment in assignments: + await db.delete(assignment) + await db.flush() + return len(assignments) + + async def list_station_recipes( db: AsyncSession, station_id: int, ) -> list[Recipe]: diff --git a/src/backend/tests/test_stations_api.py b/src/backend/tests/test_stations_api.py index 9b4cb2b..432794b 100644 --- a/src/backend/tests/test_stations_api.py +++ b/src/backend/tests/test_stations_api.py @@ -201,3 +201,176 @@ async def test_duplicate_assignment_returns_409( json={"recipe_id": recipe.id}, ) assert second.status_code == 409 + + +# --------------------------------------------------------------------------- +# Station list carries assigned recipes (point 7) +# --------------------------------------------------------------------------- + + +async def test_list_stations_includes_assigned_recipes( + client: AsyncClient, admin_user, db_session, +): + """The admin list shows which recipes a station handles, without a call per row.""" + recipe = await create_test_recipe(db_session, user_id=admin_user.id, code="REC-LIST") + await db_session.commit() + created = await client.post( + "/api/stations", + headers=auth_headers(admin_user), + json={"code": "ST-WITHREC", "name": "Con ricette"}, + ) + sid = created.json()["id"] + await client.post( + f"/api/stations/{sid}/recipes", + headers=auth_headers(admin_user), + json={"recipe_id": recipe.id}, + ) + + resp = await client.get("/api/stations", headers=auth_headers(admin_user)) + assert resp.status_code == 200 + station = next(s for s in resp.json() if s["id"] == sid) + assert [r["code"] for r in station["recipes"]] == ["REC-LIST"] + + +async def test_list_stations_empty_recipes_when_unassigned( + client: AsyncClient, admin_user, +): + created = await client.post( + "/api/stations", + headers=auth_headers(admin_user), + json={"code": "ST-NOREC", "name": "Senza ricette"}, + ) + sid = created.json()["id"] + resp = await client.get("/api/stations", headers=auth_headers(admin_user)) + station = next(s for s in resp.json() if s["id"] == sid) + assert station["recipes"] == [] + + +async def test_list_stations_hides_inactive_recipes( + client: AsyncClient, admin_user, db_session, +): + """An assigned but deactivated recipe must not show: the operator won't see it.""" + recipe = await create_test_recipe(db_session, user_id=admin_user.id, code="REC-OFF") + await db_session.commit() + created = await client.post( + "/api/stations", + headers=auth_headers(admin_user), + json={"code": "ST-INACT", "name": "Con ricetta spenta"}, + ) + sid = created.json()["id"] + await client.post( + f"/api/stations/{sid}/recipes", + headers=auth_headers(admin_user), + json={"recipe_id": recipe.id}, + ) + + recipe.active = False + await db_session.commit() + + resp = await client.get("/api/stations", headers=auth_headers(admin_user)) + station = next(s for s in resp.json() if s["id"] == sid) + assert station["recipes"] == [] + + +# --------------------------------------------------------------------------- +# Per-row station reset (point 7) +# --------------------------------------------------------------------------- + + +async def test_reset_station_removes_all_assignments( + client: AsyncClient, admin_user, db_session, +): + first = await create_test_recipe(db_session, user_id=admin_user.id, code="REC-R1") + second = await create_test_recipe(db_session, user_id=admin_user.id, code="REC-R2") + await db_session.commit() + created = await client.post( + "/api/stations", + headers=auth_headers(admin_user), + json={"code": "ST-RESET", "name": "Da azzerare"}, + ) + sid = created.json()["id"] + for recipe in (first, second): + await client.post( + f"/api/stations/{sid}/recipes", + headers=auth_headers(admin_user), + json={"recipe_id": recipe.id}, + ) + + resp = await client.delete( + f"/api/stations/{sid}/recipes", headers=auth_headers(admin_user), + ) + assert resp.status_code == 200, resp.text + assert resp.json() == {"station_id": sid, "removed": 2} + + left = await client.get( + f"/api/stations/{sid}/recipes", headers=auth_headers(admin_user), + ) + assert left.json() == [] + + +async def test_reset_station_keeps_the_station( + client: AsyncClient, admin_user, db_session, +): + """Reset clears assignments only - the station stays and can be reassigned.""" + recipe = await create_test_recipe(db_session, user_id=admin_user.id, code="REC-KEEP") + await db_session.commit() + created = await client.post( + "/api/stations", + headers=auth_headers(admin_user), + json={"code": "ST-KEEP", "name": "Resta"}, + ) + sid = created.json()["id"] + await client.post( + f"/api/stations/{sid}/recipes", + headers=auth_headers(admin_user), + json={"recipe_id": recipe.id}, + ) + await client.delete(f"/api/stations/{sid}/recipes", headers=auth_headers(admin_user)) + + still_there = await client.get( + f"/api/stations/{sid}", headers=auth_headers(admin_user), + ) + assert still_there.status_code == 200 + + reassigned = await client.post( + f"/api/stations/{sid}/recipes", + headers=auth_headers(admin_user), + json={"recipe_id": recipe.id}, + ) + assert reassigned.status_code == 201 + + +async def test_reset_empty_station_is_idempotent(client: AsyncClient, admin_user): + created = await client.post( + "/api/stations", + headers=auth_headers(admin_user), + json={"code": "ST-IDEM", "name": "Gia vuota"}, + ) + sid = created.json()["id"] + resp = await client.delete( + f"/api/stations/{sid}/recipes", headers=auth_headers(admin_user), + ) + assert resp.status_code == 200 + assert resp.json()["removed"] == 0 + + +async def test_reset_station_non_admin_is_403( + client: AsyncClient, admin_user, maker_user, +): + created = await client.post( + "/api/stations", + headers=auth_headers(admin_user), + json={"code": "ST-RBAC", "name": "Protetta"}, + ) + sid = created.json()["id"] + resp = await client.delete( + f"/api/stations/{sid}/recipes", headers=auth_headers(maker_user), + ) + assert resp.status_code == 403 + + +async def test_reset_unknown_station_is_404(client: AsyncClient, admin_user): + resp = await client.delete( + "/api/stations/999999/recipes", headers=auth_headers(admin_user), + ) + assert resp.status_code == 404 diff --git a/src/frontend/flask_app/blueprints/admin.py b/src/frontend/flask_app/blueprints/admin.py index 5adc0bc..940a57f 100644 --- a/src/frontend/flask_app/blueprints/admin.py +++ b/src/frontend/flask_app/blueprints/admin.py @@ -220,3 +220,14 @@ def api_unassign_recipe(station_id: int, recipe_id: int): if isinstance(resp, dict) and resp.get("error"): return jsonify(resp), resp.get("status_code", 500) return jsonify({"deleted": True}), 200 + + +@admin_bp.route("/api/stations//recipes", methods=["DELETE"]) +@login_required +@admin_required +def api_reset_station_recipes(station_id: int): + """Proxy: Clear every recipe assignment of a station.""" + resp = api_client.delete(f"/api/stations/{station_id}/recipes") + if isinstance(resp, dict) and resp.get("error"): + return jsonify(resp), resp.get("status_code", 500) + return jsonify(resp), 200 diff --git a/src/frontend/flask_app/blueprints/measure.py b/src/frontend/flask_app/blueprints/measure.py index 026483c..6d6d669 100644 --- a/src/frontend/flask_app/blueprints/measure.py +++ b/src/frontend/flask_app/blueprints/measure.py @@ -12,6 +12,61 @@ from services.file_proxy import proxy_file measure_bp = Blueprint("measure", __name__) +# Session key holding a station chosen at runtime, overriding Config.STATION_CODE. +STATION_OVERRIDE_KEY = "station_override" + + +def _current_station() -> tuple[str | None, bool]: + """Return the station this client acts as, and whether it is an override.""" + override = session.get(STATION_OVERRIDE_KEY) + if override: + return override, True + return Config.STATION_CODE, False + + +def _apply_station_switch() -> None: + """Honour ?station=CODE, used to exercise several stations from one PC. + + Only active when STATION_SWITCH_ENABLED is set: in production the station + identity comes from the local install, and silently measuring against another + station's recipes would break traceability. An empty value clears the override + and falls back to the configured station. + + The requested code is validated against the server before being stored, so a + typo leaves the operator on the current station with an explanation rather + than stranded on a station that does not exist. + """ + if "station" not in request.args: + return + + requested = (request.args.get("station") or "").strip().upper() + + if not Config.STATION_SWITCH_ENABLED: + flash(_("Il cambio stazione da URL non è abilitato su questa postazione."), "error") + return + + if not requested: + if session.pop(STATION_OVERRIDE_KEY, None): + flash(_("Stazione riportata a quella configurata."), "info") + return + + try: + probe = api_client.get_station_recipes(requested) + except Exception: + flash(_("Impossibile contattare il server per cambiare stazione."), "error") + return + + if isinstance(probe, dict) and probe.get("error"): + flash( + _("Stazione '%(code)s' inesistente o disattivata: stazione invariata.", + code=requested), + "error", + ) + return + + session[STATION_OVERRIDE_KEY] = requested + flash(_("Stazione corrente: %(code)s", code=requested), "info") + # --------------------------------------------------------------------------- # Route: Recipe selection @@ -21,13 +76,16 @@ measure_bp = Blueprint("measure", __name__) @role_required("MeasurementTec") def select_recipe(): """Recipe selection page with search and barcode support.""" - # Fail-fast if STATION_CODE is not configured - if not Config.STATION_CODE: + _apply_station_switch() + station_code, station_overridden = _current_station() + + # Fail-fast if no station is configured and none was chosen + if not station_code: return render_template("errors/station_not_configured.html"), 503 # Load recipes filtered by station try: - resp = api_client.get_station_recipes(Config.STATION_CODE) + resp = api_client.get_station_recipes(station_code) except Exception as e: return render_template( "errors/station_not_configured.html", error=str(e), @@ -52,7 +110,9 @@ def select_recipe(): return render_template( "measure/select_recipe.html", recipes=recipes, - station_code=Config.STATION_CODE, + station_code=station_code, + station_overridden=station_overridden, + station_configured=Config.STATION_CODE, auto_recipe_code=auto_recipe_code, auto_lot=auto_lot, auto_serial=auto_serial, diff --git a/src/frontend/flask_app/config.py b/src/frontend/flask_app/config.py index 3a3ee54..0e09b8d 100644 --- a/src/frontend/flask_app/config.py +++ b/src/frontend/flask_app/config.py @@ -26,6 +26,14 @@ class Config: # code it belongs to. Empty/None means "not configured". STATION_CODE: str | None = os.getenv("STATION_CODE") or None + # Allow switching the current station from the URL (?station=CODE), keeping the + # override in the session. Meant for commissioning: it lets one PC exercise + # several stations instead of needing one machine per station. + # Off by default - on the shop floor the station identity comes from the local + # install, and measuring against the wrong station's recipes would silently + # break traceability. + STATION_SWITCH_ENABLED: bool = os.getenv("STATION_SWITCH_ENABLED", "0") == "1" + # Babel i18n BABEL_DEFAULT_LOCALE = "it" BABEL_DEFAULT_TIMEZONE = "Europe/Rome" diff --git a/src/frontend/flask_app/templates/admin/stations.html b/src/frontend/flask_app/templates/admin/stations.html index 4247922..4d22975 100644 --- a/src/frontend/flask_app/templates/admin/stations.html +++ b/src/frontend/flask_app/templates/admin/stations.html @@ -49,6 +49,7 @@ {{ _('Codice') }} {{ _('Nome') }} {{ _('Postazione') }} + {{ _('Ricette collegate') }} {{ _('Stato') }} {{ _('Azioni') }} @@ -62,6 +63,24 @@ + + + + + + + + + +
@@ -375,11 +437,13 @@ function stationManagement(initialStations, initialRecipes) { showModal: false, showAssignments: false, showDeleteConfirm: false, + showResetConfirm: false, isEditing: false, editingId: null, saving: false, errorMsg: '', deleteTarget: null, + resetTarget: null, assignmentStation: null, assignedRecipes: [], recipeSearch: '', @@ -486,7 +550,9 @@ function stationManagement(initialStations, initialRecipes) { return; } const idx = this.stations.findIndex(s => s.id === this.editingId); - if (idx >= 0) this.stations[idx] = result; + // PUT returns the station without its assignments; keep the ones the + // row already knows about or the "ricette collegate" column blanks out. + if (idx >= 0) this.stations[idx] = { ...result, recipes: this.stations[idx].recipes || [] }; } else { if (!this.form.code || !this.form.name) { this.errorMsg = '{{ _("Codice e nome sono obbligatori") }}'; @@ -509,7 +575,7 @@ function stationManagement(initialStations, initialRecipes) { this.errorMsg = result.detail || '{{ _("Errore nella creazione") }}'; return; } - this.stations.push(result); + this.stations.push({ ...result, recipes: [] }); } this.closeModal(); } catch (e) { @@ -524,6 +590,40 @@ function stationManagement(initialStations, initialRecipes) { this.showDeleteConfirm = true; }, + confirmReset(station) { + if (!station.recipes || station.recipes.length === 0) return; + this.resetTarget = station; + this.showResetConfirm = true; + }, + + async performReset() { + if (!this.resetTarget) return; + this.saving = true; + try { + const resp = await fetch(`/admin/api/stations/${this.resetTarget.id}/recipes`, { + method: 'DELETE', + headers: { 'X-CSRFToken': this.csrfToken }, + }); + if (!resp.ok) { + const result = await resp.json().catch(() => ({})); + alert(result.detail || '{{ _("Errore nell azzeramento") }}'); + return; + } + // resetTarget is the same object the row renders, so clearing it here + // updates the chips column without reloading the page. + this.resetTarget.recipes = []; + if (this.assignmentStation && this.assignmentStation.id === this.resetTarget.id) { + this.assignedRecipes = []; + } + this.showResetConfirm = false; + this.resetTarget = null; + } catch (e) { + alert('{{ _("Errore di connessione al server") }}'); + } finally { + this.saving = false; + } + }, + async performDelete() { if (!this.deleteTarget) return; this.saving = true; @@ -591,7 +691,11 @@ function stationManagement(initialStations, initialRecipes) { return; } const recipe = this.allRecipes.find(r => r.id === id); - if (recipe) this.assignedRecipes.push({ id: recipe.id, code: recipe.code, name: recipe.name, active: recipe.active }); + if (recipe) { + const summary = { id: recipe.id, code: recipe.code, name: recipe.name, active: recipe.active }; + this.assignedRecipes.push(summary); + this._syncStationRecipes(); + } } catch (e) { this.errorMsg = '{{ _("Errore di connessione al server") }}'; } finally { @@ -614,12 +718,23 @@ function stationManagement(initialStations, initialRecipes) { return; } this.assignedRecipes = this.assignedRecipes.filter(r => r.id !== recipeId); + this._syncStationRecipes(); } catch (e) { this.errorMsg = '{{ _("Errore di connessione al server") }}'; } finally { this.saving = false; } }, + + /* Mirror the modal's assignment list onto the table row, so the chips in + "ricette collegate" stay true after assigning or unassigning. Sorted by + code to match the order the server returns. */ + _syncStationRecipes() { + if (!this.assignmentStation) return; + this.assignmentStation.recipes = [...this.assignedRecipes] + .filter(r => r.active !== false) + .sort((a, b) => (a.code || '').localeCompare(b.code || '')); + }, }; } diff --git a/src/frontend/flask_app/templates/measure/select_recipe.html b/src/frontend/flask_app/templates/measure/select_recipe.html index 9a1b4b6..aa75020 100644 --- a/src/frontend/flask_app/templates/measure/select_recipe.html +++ b/src/frontend/flask_app/templates/measure/select_recipe.html @@ -78,6 +78,23 @@

{{ _('Stazione') }}: {{ station_code }}

+ {% if station_overridden %} +

+ + + + + {{ _('Stazione forzata per collaudo') }}{% if station_configured %} — {{ _('configurata') }}: + {{ station_configured }}{% endif %} + + + {{ _('Ripristina') }} + +

+ {% endif %}
diff --git a/src/frontend/flask_app/tests/test_admin_stations.py b/src/frontend/flask_app/tests/test_admin_stations.py index e3d93fa..362c11d 100644 --- a/src/frontend/flask_app/tests/test_admin_stations.py +++ b/src/frontend/flask_app/tests/test_admin_stations.py @@ -132,3 +132,47 @@ def test_non_admin_cannot_access(client, mock_admin_api): sess["user_id"] = 2 resp = client.get("/admin/stations", follow_redirects=False) assert resp.status_code in (301, 302) + + +# --------------------------------------------------------------------------- +# Per-row station reset (point 7) +# --------------------------------------------------------------------------- + + +def test_reset_station_proxy(logged_in_client, mock_admin_api): + mock_admin_api.delete.return_value = {"station_id": 3, "removed": 2} + resp = logged_in_client.delete("/admin/api/stations/3/recipes") + assert resp.status_code == 200 + assert resp.get_json() == {"station_id": 3, "removed": 2} + mock_admin_api.delete.assert_called_once_with("/api/stations/3/recipes") + + +def test_reset_station_propagates_error(logged_in_client, mock_admin_api): + mock_admin_api.delete.return_value = { + "error": True, "status_code": 404, "detail": "Station not found", + } + resp = logged_in_client.delete("/admin/api/stations/999/recipes") + assert resp.status_code == 404 + + +def test_reset_station_distinct_from_single_unassign(logged_in_client, mock_admin_api): + """The collection route must not swallow the per-recipe delete.""" + mock_admin_api.delete.return_value = {} + logged_in_client.delete("/admin/api/stations/3/recipes/7") + mock_admin_api.delete.assert_called_once_with("/api/stations/3/recipes/7") + + +def test_station_list_page_renders_assigned_recipes(logged_in_client, mock_admin_api): + """Recipes assigned to a station reach the page so the row can show them.""" + mock_admin_api.get.side_effect = [ + [{ + "id": 1, "code": "ST-001", "name": "Linea A", "location": "Reparto A", + "notes": None, "active": True, "created_by": 1, + "created_at": "2026-04-25T10:00:00", + "recipes": [{"id": 9, "code": "REC-ABC", "name": "Perno", "active": True}], + }], + [{"id": 9, "code": "REC-ABC", "name": "Perno", "active": True}], + ] + resp = logged_in_client.get("/admin/stations") + assert resp.status_code == 200 + assert b"REC-ABC" in resp.data diff --git a/src/frontend/flask_app/tests/test_measure_station_filter.py b/src/frontend/flask_app/tests/test_measure_station_filter.py index 59efe10..74f5213 100644 --- a/src/frontend/flask_app/tests/test_measure_station_filter.py +++ b/src/frontend/flask_app/tests/test_measure_station_filter.py @@ -2,13 +2,32 @@ import importlib from unittest.mock import patch, MagicMock +import pytest -def _reload_measure(monkeypatch, station_code=None): - """Reload config and measure module under the given STATION_CODE env.""" + +@pytest.fixture(autouse=True) +def _restore_measure_module(): + """Reload config/measure after each test so env tweaks don't leak. + + Reloading runs the module body in its existing namespace, so the view + functions already registered on the app see the refreshed globals. Without + this teardown a station code set here would stay visible to later tests. + Autouse fixtures tear down after monkeypatch, so the env is pristine by then. + """ + yield + import config + importlib.reload(config) + import blueprints.measure + importlib.reload(blueprints.measure) + + +def _reload_measure(monkeypatch, station_code=None, switch_enabled=False): + """Reload config and measure module under the given station env.""" if station_code is None: monkeypatch.delenv("STATION_CODE", raising=False) else: monkeypatch.setenv("STATION_CODE", station_code) + monkeypatch.setenv("STATION_SWITCH_ENABLED", "1" if switch_enabled else "0") import config importlib.reload(config) import blueprints.measure @@ -35,3 +54,100 @@ def test_select_recipe_without_station_code_shows_error(logged_in_client, monkey assert resp.status_code == 503 body = resp.data.lower() assert b"station_code" in body or b"stazione" in body + + +# --------------------------------------------------------------------------- +# Switching the current station from the URL (point 7) +# --------------------------------------------------------------------------- + + +def _station_calls(mock_api): + """Station codes passed to get_station_recipes, in order.""" + return [c.args[0] for c in mock_api.get_station_recipes.call_args_list] + + +def test_station_switch_ignored_when_disabled(logged_in_client, monkeypatch): + """Off by default: on the shop floor the station comes from the install.""" + _reload_measure(monkeypatch, station_code="ST-CONF", switch_enabled=False) + from blueprints import measure as measure_mod + with patch.object(measure_mod, "api_client") as mock_api: + mock_api.get_station_recipes.return_value = [] + resp = logged_in_client.get("/measure/select?station=ST-OTHER") + assert resp.status_code == 200 + # The requested station is never even probed. + assert _station_calls(mock_api) == ["ST-CONF"] + + +def test_station_switch_applies_when_enabled(logged_in_client, monkeypatch): + _reload_measure(monkeypatch, station_code="ST-CONF", switch_enabled=True) + from blueprints import measure as measure_mod + with patch.object(measure_mod, "api_client") as mock_api: + mock_api.get_station_recipes.return_value = [] + resp = logged_in_client.get("/measure/select?station=ST-OTHER") + assert resp.status_code == 200 + # Probed first to validate, then used to load the recipes. + assert _station_calls(mock_api) == ["ST-OTHER", "ST-OTHER"] + assert b"ST-OTHER" in resp.data + + +def test_station_switch_survives_next_request(logged_in_client, monkeypatch): + """The override lives in the session, so it holds without repeating the param.""" + _reload_measure(monkeypatch, station_code="ST-CONF", switch_enabled=True) + from blueprints import measure as measure_mod + with patch.object(measure_mod, "api_client") as mock_api: + mock_api.get_station_recipes.return_value = [] + logged_in_client.get("/measure/select?station=ST-OTHER") + mock_api.get_station_recipes.reset_mock() + resp = logged_in_client.get("/measure/select") + assert resp.status_code == 200 + assert _station_calls(mock_api) == ["ST-OTHER"] + + +def test_station_switch_rejects_unknown_code(logged_in_client, monkeypatch): + """A typo must not strand the operator on a station that does not exist.""" + _reload_measure(monkeypatch, station_code="ST-CONF", switch_enabled=True) + from blueprints import measure as measure_mod + with patch.object(measure_mod, "api_client") as mock_api: + mock_api.get_station_recipes.side_effect = [ + {"error": True, "status_code": 404, "detail": "Station not found"}, + [], + ] + resp = logged_in_client.get("/measure/select?station=ST-NOPE") + assert resp.status_code == 200 + # Probed, refused, then fell back to the configured station. + assert _station_calls(mock_api) == ["ST-NOPE", "ST-CONF"] + assert b"ST-CONF" in resp.data + + +def test_station_switch_can_be_cleared(logged_in_client, monkeypatch): + _reload_measure(monkeypatch, station_code="ST-CONF", switch_enabled=True) + from blueprints import measure as measure_mod + with patch.object(measure_mod, "api_client") as mock_api: + mock_api.get_station_recipes.return_value = [] + logged_in_client.get("/measure/select?station=ST-OTHER") + mock_api.get_station_recipes.reset_mock() + resp = logged_in_client.get("/measure/select?station=") + assert resp.status_code == 200 + assert _station_calls(mock_api) == ["ST-CONF"] + + +def test_station_switch_is_signalled_in_the_page(logged_in_client, monkeypatch): + """An override must never be silent: wrong-station measurements are untraceable.""" + _reload_measure(monkeypatch, station_code="ST-CONF", switch_enabled=True) + from blueprints import measure as measure_mod + with patch.object(measure_mod, "api_client") as mock_api: + mock_api.get_station_recipes.return_value = [] + resp = logged_in_client.get("/measure/select?station=ST-OTHER") + body = resp.data.decode() + assert "ST-OTHER" in body + # The configured station is named so the deviation is obvious. + assert "ST-CONF" in body + + +def test_station_switch_normalises_case(logged_in_client, monkeypatch): + _reload_measure(monkeypatch, station_code="ST-CONF", switch_enabled=True) + from blueprints import measure as measure_mod + with patch.object(measure_mod, "api_client") as mock_api: + mock_api.get_station_recipes.return_value = [] + logged_in_client.get("/measure/select?station=st-other") + assert _station_calls(mock_api) == ["ST-OTHER", "ST-OTHER"] diff --git a/src/frontend/flask_app/translations/en/LC_MESSAGES/messages.po b/src/frontend/flask_app/translations/en/LC_MESSAGES/messages.po index 52fbd02..0c252d2 100644 --- a/src/frontend/flask_app/translations/en/LC_MESSAGES/messages.po +++ b/src/frontend/flask_app/translations/en/LC_MESSAGES/messages.po @@ -3,7 +3,7 @@ msgid "" msgstr "" "Project-Id-Version: TieMeasureFlow 1.0\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2026-06-11 15:33+0000\n" +"POT-Creation-Date: 2026-07-28 15:24+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language: en\n" @@ -37,7 +37,7 @@ msgstr "Please log in to continue" msgid "Inserisci username e password" msgstr "Enter username and password" -#: blueprints/auth.py:81 blueprints/measure.py:346 +#: blueprints/auth.py:81 blueprints/measure.py:407 msgid "Credenziali non valide" msgstr "Invalid credentials" @@ -101,65 +101,87 @@ msgstr "No file uploaded" msgid "Nome file vuoto" msgstr "Empty file name" +#: blueprints/measure.py:45 +msgid "Il cambio stazione da URL non è abilitato su questa postazione." +msgstr "Switching station from the URL is not enabled on this workstation." + +#: blueprints/measure.py:50 +msgid "Stazione riportata a quella configurata." +msgstr "Station restored to the configured one." + +#: blueprints/measure.py:56 +msgid "Impossibile contattare il server per cambiare stazione." +msgstr "Could not reach the server to switch station." + +#: blueprints/measure.py:61 +#, python-format +msgid "Stazione '%(code)s' inesistente o disattivata: stazione invariata." +msgstr "Station '%(code)s' does not exist or is disabled: station unchanged." + +#: blueprints/measure.py:68 +#, python-format +msgid "Stazione corrente: %(code)s" +msgstr "Current station: %(code)s" + # Measure - API Errors -#: blueprints/measure.py:38 +#: blueprints/measure.py:96 #, python-format msgid "Errore nel caricamento delle ricette: %(detail)s" msgstr "Error loading recipes: %(detail)s" -#: blueprints/measure.py:86 blueprints/measure.py:172 +#: blueprints/measure.py:146 blueprints/measure.py:232 #, python-format msgid "Ricetta non trovata: %(detail)s" msgstr "Recipe not found: %(detail)s" -#: blueprints/measure.py:96 +#: blueprints/measure.py:156 #, python-format msgid "Errore nel caricamento dei task: %(detail)s" msgstr "Error loading tasks: %(detail)s" -#: blueprints/measure.py:125 +#: blueprints/measure.py:185 #, python-format msgid "Task non trovato: %(detail)s" msgstr "Task not found: %(detail)s" -#: blueprints/measure.py:241 +#: blueprints/measure.py:301 msgid "Codice non fornito" msgstr "Code not provided" -#: blueprints/measure.py:247 templates/measure/select_recipe.html:48 +#: blueprints/measure.py:307 templates/measure/select_recipe.html:48 msgid "Ricetta non trovata" msgstr "Recipe not found" -#: blueprints/measure.py:303 +#: blueprints/measure.py:363 msgid "Dati mancanti: subtask_id, version_id e value sono obbligatori" msgstr "Missing data: subtask_id, version_id and value are required" -#: blueprints/measure.py:322 templates/admin/stations.html:485 +#: blueprints/measure.py:383 templates/admin/stations.html:549 #: templates/maker/recipe_editor.html:543 msgid "Errore nel salvataggio" msgstr "Error saving" -#: blueprints/measure.py:341 +#: blueprints/measure.py:402 msgid "Username e password richiesti" msgstr "Username and password required" -#: blueprints/measure.py:351 +#: blueprints/measure.py:412 msgid "Utente non autorizzato (richiesto capoturno)" msgstr "User not authorized (shift supervisor required)" -#: templates/base.html:175 +#: templates/base.html:173 msgid "Sessione in scadenza" msgstr "Session expiring" -#: templates/base.html:178 +#: templates/base.html:176 msgid "Sarai disconnesso tra" msgstr "You will be logged out in" -#: templates/base.html:178 +#: templates/base.html:176 msgid "secondi per inattività." msgstr "seconds due to inactivity." -#: templates/base.html:181 +#: templates/base.html:179 msgid "Continua a lavorare" msgstr "Keep working" @@ -203,16 +225,16 @@ msgstr "Saved" msgid "Salva impostazioni" msgstr "Save settings" -#: templates/admin/settings.html:107 templates/admin/stations.html:516 -#: templates/admin/stations.html:544 templates/admin/stations.html:565 -#: templates/admin/stations.html:596 templates/admin/stations.html:618 -#: templates/admin/users.html:497 templates/admin/users.html:533 -#: templates/maker/recipe_editor.html:571 templates/maker/task_drawing.html:65 -#: templates/maker/task_editor.html:1243 templates/maker/task_editor.html:1321 -#: templates/maker/task_editor.html:1382 templates/maker/task_editor.html:1415 -#: templates/maker/task_editor.html:1510 templates/maker/task_editor.html:1586 -#: templates/maker/task_editor.html:1660 templates/maker/task_editor.html:1697 -#: templates/maker/task_editor.html:1776 +#: templates/admin/settings.html:107 templates/admin/stations.html:582 +#: templates/admin/stations.html:621 templates/admin/stations.html:644 +#: templates/admin/stations.html:665 templates/admin/stations.html:700 +#: templates/admin/stations.html:723 templates/admin/users.html:497 +#: templates/admin/users.html:533 templates/maker/recipe_editor.html:571 +#: templates/maker/task_drawing.html:65 templates/maker/task_editor.html:1243 +#: templates/maker/task_editor.html:1321 templates/maker/task_editor.html:1382 +#: templates/maker/task_editor.html:1415 templates/maker/task_editor.html:1510 +#: templates/maker/task_editor.html:1586 templates/maker/task_editor.html:1660 +#: templates/maker/task_editor.html:1697 templates/maker/task_editor.html:1776 #: templates/statistics/dashboard.html:372 #: templates/statistics/dashboard.html:457 msgid "Errore di connessione al server" @@ -232,209 +254,245 @@ msgstr "Create, edit and manage measurement stations and their assigned recipes" msgid "Cerca stazione..." msgstr "Search station..." -#: templates/admin/stations.html:39 templates/admin/stations.html:133 +#: templates/admin/stations.html:39 templates/admin/stations.html:163 msgid "Nuova Stazione" msgstr "New Station" # Task Complete Page -#: templates/admin/stations.html:49 templates/admin/stations.html:143 +#: templates/admin/stations.html:49 templates/admin/stations.html:173 #: templates/maker/recipe_editor.html:195 #: templates/measure/task_complete.html:49 msgid "Codice" msgstr "Code" -#: templates/admin/stations.html:50 templates/admin/stations.html:160 +#: templates/admin/stations.html:50 templates/admin/stations.html:190 #: templates/maker/recipe_editor.html:213 #: templates/measure/task_complete.html:53 msgid "Nome" msgstr "Name" -#: templates/admin/stations.html:51 templates/admin/stations.html:169 +#: templates/admin/stations.html:51 templates/admin/stations.html:199 msgid "Postazione" msgstr "Location" -#: templates/admin/stations.html:52 templates/admin/users.html:52 +#: templates/admin/stations.html:52 +msgid "Ricette collegate" +msgstr "Assigned recipes" + +#: templates/admin/stations.html:53 templates/admin/users.html:52 #: templates/measure/task_complete.html:292 msgid "Stato" msgstr "Status" -#: templates/admin/stations.html:53 templates/admin/users.html:53 +#: templates/admin/stations.html:54 templates/admin/users.html:53 #: templates/maker/task_editor.html:551 msgid "Azioni" msgstr "Actions" -#: templates/admin/stations.html:70 templates/admin/stations.html:189 +#: templates/admin/stations.html:68 +msgid "Nessuna ricetta" +msgstr "No recipes" + +#: templates/admin/stations.html:89 templates/admin/stations.html:219 #: templates/maker/recipe_list.html:186 msgid "Attiva" msgstr "Active" -#: templates/admin/stations.html:70 templates/maker/recipe_list.html:186 +#: templates/admin/stations.html:89 templates/maker/recipe_list.html:186 msgid "Disattivata" msgstr "Inactive" # Maker - Recipe List -#: templates/admin/stations.html:76 +#: templates/admin/stations.html:95 msgid "Gestisci ricette" msgstr "Manage recipes" -#: templates/admin/stations.html:83 templates/admin/users.html:100 +#: templates/admin/stations.html:105 +msgid "Azzera le ricette della stazione" +msgstr "Clear the station's recipes" + +#: templates/admin/stations.html:106 +msgid "Nessuna ricetta da azzerare" +msgstr "No recipes to clear" + +#: templates/admin/stations.html:113 templates/admin/users.html:100 #: templates/maker/recipe_list.html:237 templates/maker/task_editor.html:602 msgid "Modifica" msgstr "Edit" -#: templates/admin/stations.html:90 templates/admin/stations.html:359 +#: templates/admin/stations.html:120 templates/admin/stations.html:421 #: templates/maker/recipe_list.html:277 templates/maker/recipe_list.html:387 #: templates/maker/task_drawing.html:275 templates/maker/task_editor.html:610 msgid "Elimina" msgstr "Delete" -#: templates/admin/stations.html:108 +#: templates/admin/stations.html:138 msgid "Nessuna stazione trovata" msgstr "No stations found" -#: templates/admin/stations.html:114 +#: templates/admin/stations.html:144 msgid "stazioni" msgstr "stations" -#: templates/admin/stations.html:133 +#: templates/admin/stations.html:163 msgid "Modifica Stazione" msgstr "Edit Station" -#: templates/admin/stations.html:152 +#: templates/admin/stations.html:182 msgid "Il codice non può essere modificato" msgstr "Code cannot be modified" -#: templates/admin/stations.html:155 +#: templates/admin/stations.html:185 msgid "Identificativo univoco usato dal client tramite STATION_CODE" msgstr "Unique identifier used by client via STATION_CODE" -#: templates/admin/stations.html:164 +#: templates/admin/stations.html:194 msgid "Nome descrittivo della stazione" msgstr "Descriptive name of the station" -#: templates/admin/stations.html:173 +#: templates/admin/stations.html:203 msgid "Es. Reparto A - Linea 2" msgstr "E.g. Department A - Line 2" -#: templates/admin/stations.html:178 +#: templates/admin/stations.html:208 msgid "Note" msgstr "Notes" -#: templates/admin/stations.html:182 +#: templates/admin/stations.html:212 msgid "Note opzionali" msgstr "Optional notes" -#: templates/admin/stations.html:204 templates/admin/stations.html:354 -#: templates/admin/users.html:292 templates/admin/users.html:323 -#: templates/maker/recipe_editor.html:137 +#: templates/admin/stations.html:234 templates/admin/stations.html:389 +#: templates/admin/stations.html:416 templates/admin/users.html:292 +#: templates/admin/users.html:323 templates/maker/recipe_editor.html:137 #: templates/maker/recipe_editor.html:456 templates/maker/recipe_list.html:383 #: templates/maker/task_editor.html:294 templates/maker/task_editor.html:403 #: templates/maker/task_editor.html:747 templates/maker/task_editor.html:866 #: templates/maker/task_editor.html:931 templates/maker/task_editor.html:1036 -#: templates/measure/select_recipe.html:350 +#: templates/measure/select_recipe.html:367 #: templates/measure/task_execute.html:664 msgid "Annulla" msgstr "Cancel" -#: templates/admin/stations.html:210 templates/admin/users.html:298 +#: templates/admin/stations.html:240 templates/admin/users.html:298 #: templates/auth/profile.html:171 msgid "Salva Modifiche" msgstr "Save Changes" -#: templates/admin/stations.html:210 +#: templates/admin/stations.html:240 msgid "Crea Stazione" msgstr "Create Station" -#: templates/admin/stations.html:211 templates/admin/users.html:299 +#: templates/admin/stations.html:241 templates/admin/users.html:299 #: templates/maker/recipe_editor.html:107 #: templates/maker/recipe_editor.html:448 templates/maker/task_drawing.html:61 #: templates/measure/task_execute.html:380 msgid "Salvataggio..." msgstr "Saving..." -#: templates/admin/stations.html:228 +#: templates/admin/stations.html:258 msgid "Ricette Assegnate" msgstr "Assigned Recipes" -#: templates/admin/stations.html:246 +#: templates/admin/stations.html:276 msgid "Filtra per codice o nome ricetta..." msgstr "Filter by recipe code or name..." -#: templates/admin/stations.html:259 +#: templates/admin/stations.html:289 msgid "Ricette disponibili" msgstr "Available recipes" -#: templates/admin/stations.html:272 +#: templates/admin/stations.html:302 msgid "Assegna a questa stazione" msgstr "Assign to this station" -#: templates/admin/stations.html:276 +#: templates/admin/stations.html:306 msgid "Assegna" msgstr "Assign" -#: templates/admin/stations.html:293 +#: templates/admin/stations.html:323 msgid "Assegnate alla stazione" msgstr "Assigned to the station" -#: templates/admin/stations.html:305 +#: templates/admin/stations.html:335 msgid "Rimuovi assegnazione" msgstr "Remove assignment" -#: templates/admin/stations.html:332 +#: templates/admin/stations.html:362 #: templates/components/barcode_scanner.html:145 msgid "Chiudi" msgstr "Close" -#: templates/admin/stations.html:345 templates/maker/recipe_list.html:363 +#: templates/admin/stations.html:376 +msgid "Azzera Stazione" +msgstr "Reset Station" + +#: templates/admin/stations.html:378 +msgid "Rimuovere tutte le ricette assegnate a" +msgstr "Remove every recipe assigned to" + +#: templates/admin/stations.html:383 +msgid "assegnazioni verranno rimosse. La stazione resta, e si potrà riassegnare." +msgstr "assignments will be removed. The station stays and can be reassigned." + +#: templates/admin/stations.html:394 +msgid "Azzera" +msgstr "Reset" + +#: templates/admin/stations.html:407 templates/maker/recipe_list.html:363 msgid "Conferma Eliminazione" msgstr "Confirm Deletion" -#: templates/admin/stations.html:347 +#: templates/admin/stations.html:409 msgid "Sei sicuro di voler eliminare la stazione" msgstr "Are you sure you want to delete the station" -#: templates/admin/stations.html:349 +#: templates/admin/stations.html:411 msgid "Verranno rimosse anche tutte le assegnazioni di ricette." msgstr "All recipe assignments will also be removed." -#: templates/admin/stations.html:430 templates/admin/stations.html:436 +#: templates/admin/stations.html:494 templates/admin/stations.html:500 msgid "Nessun risultato per il filtro" msgstr "No results for filter" -#: templates/admin/stations.html:431 +#: templates/admin/stations.html:495 msgid "Nessuna ricetta nel sistema" msgstr "No recipes in system" -#: templates/admin/stations.html:432 +#: templates/admin/stations.html:496 msgid "Tutte le ricette sono già assegnate" msgstr "All recipes are already assigned" -#: templates/admin/stations.html:437 +#: templates/admin/stations.html:501 msgid "Nessuna ricetta assegnata" msgstr "No assigned recipes" -#: templates/admin/stations.html:492 +#: templates/admin/stations.html:558 msgid "Codice e nome sono obbligatori" msgstr "Code and name are required" -#: templates/admin/stations.html:509 +#: templates/admin/stations.html:575 msgid "Errore nella creazione" msgstr "Error during creation" -#: templates/admin/stations.html:537 +#: templates/admin/stations.html:609 +msgid "Errore nell azzeramento" +msgstr "Error clearing assignments" + +#: templates/admin/stations.html:637 msgid "Errore nella eliminazione" msgstr "Error during deletion" # Maker - API Errors -#: templates/admin/stations.html:562 +#: templates/admin/stations.html:662 msgid "Errore nel caricamento delle ricette" msgstr "Error loading recipes" -#: templates/admin/stations.html:590 +#: templates/admin/stations.html:690 msgid "Errore nella assegnazione" msgstr "Error during assignment" -#: templates/admin/stations.html:613 +#: templates/admin/stations.html:717 msgid "Errore nella rimozione" msgstr "Error during removal" @@ -455,8 +513,8 @@ msgid "Nuovo Utente" msgstr "New User" #: templates/admin/users.html:48 templates/admin/users.html:173 -#: templates/admin/users.html:179 templates/auth/login.html:33 -#: templates/auth/login.html:47 templates/auth/profile.html:36 +#: templates/admin/users.html:179 templates/auth/login.html:35 +#: templates/auth/login.html:49 templates/auth/profile.html:36 #: templates/measure/task_execute.html:649 msgid "Username" msgstr "Username" @@ -508,7 +566,7 @@ msgid "Il nome utente non può essere modificato" msgstr "Username cannot be changed" #: templates/admin/users.html:206 templates/admin/users.html:214 -#: templates/auth/login.html:55 templates/auth/login.html:69 +#: templates/auth/login.html:57 templates/auth/login.html:71 #: templates/measure/task_execute.html:654 msgid "Password" msgstr "Password" @@ -578,36 +636,36 @@ msgid "Username, nome visualizzato e password sono obbligatori" msgstr "Username, display name and password are required" # Login Page -#: templates/auth/login.html:23 +#: templates/auth/login.html:25 msgid "Accedi al sistema" msgstr "Sign in to your account" -#: templates/auth/login.html:73 +#: templates/auth/login.html:75 msgid "Nascondi password" msgstr "Hide password" -#: templates/auth/login.html:73 +#: templates/auth/login.html:75 msgid "Mostra password" msgstr "Show password" -#: templates/auth/login.html:94 +#: templates/auth/login.html:96 msgid "Accedi" msgstr "Sign In" -#: templates/auth/login.html:102 +#: templates/auth/login.html:104 msgid "Hai dimenticato la password?" msgstr "Forgot password?" -#: templates/auth/login.html:104 +#: templates/auth/login.html:106 msgid "Contatta l'amministratore" msgstr "Contact administrator" # Additional Login Page -#: templates/auth/login.html:111 +#: templates/auth/login.html:113 msgid "Sistema di misurazione industriale" msgstr "Industrial measurement system" -#: templates/auth/profile.html:2 templates/components/navbar.html:147 +#: templates/auth/profile.html:2 templates/components/navbar.html:149 msgid "Profilo" msgstr "Profile" @@ -657,15 +715,15 @@ msgstr "Logout from system" msgid "Termina la sessione corrente e torna alla schermata di login" msgstr "End current session and return to login screen" -#: templates/auth/profile.html:197 templates/components/navbar.html:158 -#: templates/components/navbar.html:167 +#: templates/auth/profile.html:197 templates/components/navbar.html:160 +#: templates/components/navbar.html:169 msgid "Logout" msgstr "Logout" #: templates/components/barcode_scanner.html:21 #: templates/components/barcode_scanner.html:54 -#: templates/measure/select_recipe.html:90 -#: templates/measure/select_recipe.html:302 +#: templates/measure/select_recipe.html:107 +#: templates/measure/select_recipe.html:319 msgid "Scansiona Barcode" msgstr "Scan Barcode" @@ -751,7 +809,7 @@ msgstr "Users" msgid "Stazioni" msgstr "Stations" -#: templates/components/navbar.html:154 +#: templates/components/navbar.html:156 msgid "Logout bloccato durante le misurazioni" msgstr "Logout blocked during measurements" @@ -954,7 +1012,7 @@ msgstr "Error during deletion" # Recipe Selection Additional #: templates/maker/recipe_list.html:55 templates/measure/select_recipe.html:55 -#: templates/measure/task_execute.html:1111 +#: templates/measure/task_execute.html:1121 msgid "Errore di connessione" msgstr "Connection Error" @@ -987,7 +1045,7 @@ msgid "ricetta trovata" msgstr "recipe found" #: templates/maker/recipe_list.html:144 -#: templates/measure/select_recipe.html:158 +#: templates/measure/select_recipe.html:175 msgid "ricette trovate" msgstr "recipes found" @@ -1005,7 +1063,7 @@ msgid "Versioni" msgstr "Versions" #: templates/maker/recipe_list.html:297 -#: templates/measure/select_recipe.html:256 +#: templates/measure/select_recipe.html:273 msgid "Nessuna ricetta trovata" msgstr "No recipe found" @@ -1149,7 +1207,7 @@ msgid "Torna ai Task" msgstr "Back to Tasks" #: templates/maker/task_drawing.html:205 templates/maker/task_drawing.html:209 -#: templates/measure/select_recipe.html:236 +#: templates/measure/select_recipe.html:253 msgid "Seleziona" msgstr "Select" @@ -1552,7 +1610,7 @@ msgid "Misurazione aggiunta" msgstr "Measurement added" #: templates/maker/task_editor.html:1645 -#: templates/measure/task_execute.html:862 +#: templates/measure/task_execute.html:870 msgid "Errore nel salvataggio della misurazione" msgstr "Error saving measurement" @@ -1627,71 +1685,83 @@ msgstr "Choose the measurement recipe to execute" msgid "Stazione" msgstr "Station" -#: templates/measure/select_recipe.html:102 +#: templates/measure/select_recipe.html:89 +msgid "Stazione forzata per collaudo" +msgstr "Station overridden for commissioning" + +#: templates/measure/select_recipe.html:89 +msgid "configurata" +msgstr "configured" + +#: templates/measure/select_recipe.html:94 +msgid "Ripristina" +msgstr "Restore" + +#: templates/measure/select_recipe.html:119 msgid "Cerca ricetta" msgstr "Search recipe" -#: templates/measure/select_recipe.html:106 +#: templates/measure/select_recipe.html:123 msgid "Nome, codice o descrizione..." msgstr "Name, code or description..." -#: templates/measure/select_recipe.html:119 +#: templates/measure/select_recipe.html:136 msgid "Tracciabilità" msgstr "Traceability" -#: templates/measure/select_recipe.html:120 +#: templates/measure/select_recipe.html:137 msgid "Dati del pezzo da misurare — compila prima di selezionare la ricetta" msgstr "Data of the part to measure — fill in before selecting the recipe" -#: templates/measure/select_recipe.html:130 +#: templates/measure/select_recipe.html:147 #: templates/measure/task_complete.html:281 msgid "Numero Lotto" msgstr "Lot Number" -#: templates/measure/select_recipe.html:134 +#: templates/measure/select_recipe.html:151 msgid "Es. LOT-2026-001 (opzionale)" msgstr "E.g. LOT-2026-001 (optional)" -#: templates/measure/select_recipe.html:144 +#: templates/measure/select_recipe.html:161 #: templates/measure/task_complete.html:282 msgid "Numero Seriale" msgstr "Serial Number" -#: templates/measure/select_recipe.html:148 +#: templates/measure/select_recipe.html:165 msgid "Es. SN-000123 (opzionale)" msgstr "E.g. SN-000123 (optional)" -#: templates/measure/select_recipe.html:201 +#: templates/measure/select_recipe.html:218 msgid "Nessuna descrizione disponibile" msgstr "No description available" -#: templates/measure/select_recipe.html:260 +#: templates/measure/select_recipe.html:277 msgid "Nessun risultato per" msgstr "No results for" -#: templates/measure/select_recipe.html:261 +#: templates/measure/select_recipe.html:278 msgid "Prova con un termine diverso." msgstr "Try a different search term." -#: templates/measure/select_recipe.html:264 +#: templates/measure/select_recipe.html:281 msgid "Non ci sono ricette disponibili al momento." msgstr "No recipes available at the moment." -#: templates/measure/select_recipe.html:317 +#: templates/measure/select_recipe.html:334 msgid "" "Inserisci o scansiona il codice della ricetta per selezionarla " "automaticamente." msgstr "Enter or scan the recipe code to select it automatically." -#: templates/measure/select_recipe.html:322 +#: templates/measure/select_recipe.html:339 msgid "Codice Ricetta" msgstr "Recipe Code" -#: templates/measure/select_recipe.html:328 +#: templates/measure/select_recipe.html:345 msgid "Es. REC-001" msgstr "E.g. REC-001" -#: templates/measure/select_recipe.html:364 +#: templates/measure/select_recipe.html:381 msgid "Cerca" msgstr "Search" @@ -1984,23 +2054,23 @@ msgstr "Supervisor username" msgid "Autorizza" msgstr "Authorize" -#: templates/measure/task_execute.html:906 +#: templates/measure/task_execute.html:914 msgid "Errore di rete. Riprovare." msgstr "Network error. Please retry." -#: templates/measure/task_execute.html:1068 +#: templates/measure/task_execute.html:1078 msgid "Misurazione fuori tolleranza" msgstr "Measurement out of tolerance" -#: templates/measure/task_execute.html:1069 +#: templates/measure/task_execute.html:1079 msgid "Fermo linea richiesto" msgstr "Line stop requested" -#: templates/measure/task_execute.html:1070 +#: templates/measure/task_execute.html:1080 msgid "Fine produzione richiesta" msgstr "End of production requested" -#: templates/measure/task_execute.html:1089 +#: templates/measure/task_execute.html:1099 msgid "Credenziali non valide o utente non autorizzato" msgstr "Invalid credentials or unauthorized user" diff --git a/src/frontend/flask_app/translations/it/LC_MESSAGES/messages.po b/src/frontend/flask_app/translations/it/LC_MESSAGES/messages.po index 46543e7..89df0b8 100644 --- a/src/frontend/flask_app/translations/it/LC_MESSAGES/messages.po +++ b/src/frontend/flask_app/translations/it/LC_MESSAGES/messages.po @@ -3,7 +3,7 @@ msgid "" msgstr "" "Project-Id-Version: TieMeasureFlow 1.0\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2026-06-11 15:33+0000\n" +"POT-Creation-Date: 2026-07-28 15:24+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language: it\n" @@ -37,7 +37,7 @@ msgstr "Effettua il login per continuare" msgid "Inserisci username e password" msgstr "Inserisci username e password" -#: blueprints/auth.py:81 blueprints/measure.py:346 +#: blueprints/auth.py:81 blueprints/measure.py:407 msgid "Credenziali non valide" msgstr "Credenziali non valide" @@ -101,65 +101,87 @@ msgstr "Nessun file caricato" msgid "Nome file vuoto" msgstr "Nome file vuoto" +#: blueprints/measure.py:45 +msgid "Il cambio stazione da URL non è abilitato su questa postazione." +msgstr "Il cambio stazione da URL non è abilitato su questa postazione." + +#: blueprints/measure.py:50 +msgid "Stazione riportata a quella configurata." +msgstr "Stazione riportata a quella configurata." + +#: blueprints/measure.py:56 +msgid "Impossibile contattare il server per cambiare stazione." +msgstr "Impossibile contattare il server per cambiare stazione." + +#: blueprints/measure.py:61 +#, python-format +msgid "Stazione '%(code)s' inesistente o disattivata: stazione invariata." +msgstr "Stazione '%(code)s' inesistente o disattivata: stazione invariata." + +#: blueprints/measure.py:68 +#, python-format +msgid "Stazione corrente: %(code)s" +msgstr "Stazione corrente: %(code)s" + # Measure - API Errors -#: blueprints/measure.py:38 +#: blueprints/measure.py:96 #, python-format msgid "Errore nel caricamento delle ricette: %(detail)s" msgstr "Errore nel caricamento delle ricette: %(detail)s" -#: blueprints/measure.py:86 blueprints/measure.py:172 +#: blueprints/measure.py:146 blueprints/measure.py:232 #, python-format msgid "Ricetta non trovata: %(detail)s" msgstr "Ricetta non trovata: %(detail)s" -#: blueprints/measure.py:96 +#: blueprints/measure.py:156 #, python-format msgid "Errore nel caricamento dei task: %(detail)s" msgstr "Errore nel caricamento dei task: %(detail)s" -#: blueprints/measure.py:125 +#: blueprints/measure.py:185 #, python-format msgid "Task non trovato: %(detail)s" msgstr "Task non trovato: %(detail)s" -#: blueprints/measure.py:241 +#: blueprints/measure.py:301 msgid "Codice non fornito" msgstr "Codice non fornito" -#: blueprints/measure.py:247 templates/measure/select_recipe.html:48 +#: blueprints/measure.py:307 templates/measure/select_recipe.html:48 msgid "Ricetta non trovata" msgstr "Ricetta non trovata" -#: blueprints/measure.py:303 +#: blueprints/measure.py:363 msgid "Dati mancanti: subtask_id, version_id e value sono obbligatori" msgstr "Dati mancanti: subtask_id, version_id e value sono obbligatori" -#: blueprints/measure.py:322 templates/admin/stations.html:485 +#: blueprints/measure.py:383 templates/admin/stations.html:549 #: templates/maker/recipe_editor.html:543 msgid "Errore nel salvataggio" msgstr "Errore nel salvataggio" -#: blueprints/measure.py:341 +#: blueprints/measure.py:402 msgid "Username e password richiesti" msgstr "Username e password richiesti" -#: blueprints/measure.py:351 +#: blueprints/measure.py:412 msgid "Utente non autorizzato (richiesto capoturno)" msgstr "Utente non autorizzato (richiesto capoturno)" -#: templates/base.html:175 +#: templates/base.html:173 msgid "Sessione in scadenza" msgstr "Sessione in scadenza" -#: templates/base.html:178 +#: templates/base.html:176 msgid "Sarai disconnesso tra" msgstr "Sarai disconnesso tra" -#: templates/base.html:178 +#: templates/base.html:176 msgid "secondi per inattività." msgstr "secondi per inattività." -#: templates/base.html:181 +#: templates/base.html:179 msgid "Continua a lavorare" msgstr "Continua a lavorare" @@ -203,16 +225,16 @@ msgstr "Salvato" msgid "Salva impostazioni" msgstr "Salva impostazioni" -#: templates/admin/settings.html:107 templates/admin/stations.html:516 -#: templates/admin/stations.html:544 templates/admin/stations.html:565 -#: templates/admin/stations.html:596 templates/admin/stations.html:618 -#: templates/admin/users.html:497 templates/admin/users.html:533 -#: templates/maker/recipe_editor.html:571 templates/maker/task_drawing.html:65 -#: templates/maker/task_editor.html:1243 templates/maker/task_editor.html:1321 -#: templates/maker/task_editor.html:1382 templates/maker/task_editor.html:1415 -#: templates/maker/task_editor.html:1510 templates/maker/task_editor.html:1586 -#: templates/maker/task_editor.html:1660 templates/maker/task_editor.html:1697 -#: templates/maker/task_editor.html:1776 +#: templates/admin/settings.html:107 templates/admin/stations.html:582 +#: templates/admin/stations.html:621 templates/admin/stations.html:644 +#: templates/admin/stations.html:665 templates/admin/stations.html:700 +#: templates/admin/stations.html:723 templates/admin/users.html:497 +#: templates/admin/users.html:533 templates/maker/recipe_editor.html:571 +#: templates/maker/task_drawing.html:65 templates/maker/task_editor.html:1243 +#: templates/maker/task_editor.html:1321 templates/maker/task_editor.html:1382 +#: templates/maker/task_editor.html:1415 templates/maker/task_editor.html:1510 +#: templates/maker/task_editor.html:1586 templates/maker/task_editor.html:1660 +#: templates/maker/task_editor.html:1697 templates/maker/task_editor.html:1776 #: templates/statistics/dashboard.html:372 #: templates/statistics/dashboard.html:457 msgid "Errore di connessione al server" @@ -234,209 +256,245 @@ msgstr "" msgid "Cerca stazione..." msgstr "Cerca stazione..." -#: templates/admin/stations.html:39 templates/admin/stations.html:133 +#: templates/admin/stations.html:39 templates/admin/stations.html:163 msgid "Nuova Stazione" msgstr "Nuova Stazione" # Task Complete Page -#: templates/admin/stations.html:49 templates/admin/stations.html:143 +#: templates/admin/stations.html:49 templates/admin/stations.html:173 #: templates/maker/recipe_editor.html:195 #: templates/measure/task_complete.html:49 msgid "Codice" msgstr "Codice" -#: templates/admin/stations.html:50 templates/admin/stations.html:160 +#: templates/admin/stations.html:50 templates/admin/stations.html:190 #: templates/maker/recipe_editor.html:213 #: templates/measure/task_complete.html:53 msgid "Nome" msgstr "Nome" -#: templates/admin/stations.html:51 templates/admin/stations.html:169 +#: templates/admin/stations.html:51 templates/admin/stations.html:199 msgid "Postazione" msgstr "Postazione" -#: templates/admin/stations.html:52 templates/admin/users.html:52 +#: templates/admin/stations.html:52 +msgid "Ricette collegate" +msgstr "Ricette collegate" + +#: templates/admin/stations.html:53 templates/admin/users.html:52 #: templates/measure/task_complete.html:292 msgid "Stato" msgstr "Stato" -#: templates/admin/stations.html:53 templates/admin/users.html:53 +#: templates/admin/stations.html:54 templates/admin/users.html:53 #: templates/maker/task_editor.html:551 msgid "Azioni" msgstr "Azioni" -#: templates/admin/stations.html:70 templates/admin/stations.html:189 +#: templates/admin/stations.html:68 +msgid "Nessuna ricetta" +msgstr "Nessuna ricetta" + +#: templates/admin/stations.html:89 templates/admin/stations.html:219 #: templates/maker/recipe_list.html:186 msgid "Attiva" msgstr "Attiva" -#: templates/admin/stations.html:70 templates/maker/recipe_list.html:186 +#: templates/admin/stations.html:89 templates/maker/recipe_list.html:186 msgid "Disattivata" msgstr "Disattivata" # Maker - Recipe List -#: templates/admin/stations.html:76 +#: templates/admin/stations.html:95 msgid "Gestisci ricette" msgstr "Gestisci ricette" -#: templates/admin/stations.html:83 templates/admin/users.html:100 +#: templates/admin/stations.html:105 +msgid "Azzera le ricette della stazione" +msgstr "Azzera le ricette della stazione" + +#: templates/admin/stations.html:106 +msgid "Nessuna ricetta da azzerare" +msgstr "Nessuna ricetta da azzerare" + +#: templates/admin/stations.html:113 templates/admin/users.html:100 #: templates/maker/recipe_list.html:237 templates/maker/task_editor.html:602 msgid "Modifica" msgstr "Modifica" -#: templates/admin/stations.html:90 templates/admin/stations.html:359 +#: templates/admin/stations.html:120 templates/admin/stations.html:421 #: templates/maker/recipe_list.html:277 templates/maker/recipe_list.html:387 #: templates/maker/task_drawing.html:275 templates/maker/task_editor.html:610 msgid "Elimina" msgstr "Elimina" -#: templates/admin/stations.html:108 +#: templates/admin/stations.html:138 msgid "Nessuna stazione trovata" msgstr "Nessuna stazione trovata" -#: templates/admin/stations.html:114 +#: templates/admin/stations.html:144 msgid "stazioni" msgstr "stazioni" -#: templates/admin/stations.html:133 +#: templates/admin/stations.html:163 msgid "Modifica Stazione" msgstr "Modifica Stazione" -#: templates/admin/stations.html:152 +#: templates/admin/stations.html:182 msgid "Il codice non può essere modificato" msgstr "Il codice non può essere modificato" -#: templates/admin/stations.html:155 +#: templates/admin/stations.html:185 msgid "Identificativo univoco usato dal client tramite STATION_CODE" msgstr "Identificativo univoco usato dal client tramite STATION_CODE" -#: templates/admin/stations.html:164 +#: templates/admin/stations.html:194 msgid "Nome descrittivo della stazione" msgstr "Nome descrittivo della stazione" -#: templates/admin/stations.html:173 +#: templates/admin/stations.html:203 msgid "Es. Reparto A - Linea 2" msgstr "Es. Reparto A - Linea 2" -#: templates/admin/stations.html:178 +#: templates/admin/stations.html:208 msgid "Note" msgstr "Note" -#: templates/admin/stations.html:182 +#: templates/admin/stations.html:212 msgid "Note opzionali" msgstr "Note opzionali" -#: templates/admin/stations.html:204 templates/admin/stations.html:354 -#: templates/admin/users.html:292 templates/admin/users.html:323 -#: templates/maker/recipe_editor.html:137 +#: templates/admin/stations.html:234 templates/admin/stations.html:389 +#: templates/admin/stations.html:416 templates/admin/users.html:292 +#: templates/admin/users.html:323 templates/maker/recipe_editor.html:137 #: templates/maker/recipe_editor.html:456 templates/maker/recipe_list.html:383 #: templates/maker/task_editor.html:294 templates/maker/task_editor.html:403 #: templates/maker/task_editor.html:747 templates/maker/task_editor.html:866 #: templates/maker/task_editor.html:931 templates/maker/task_editor.html:1036 -#: templates/measure/select_recipe.html:350 +#: templates/measure/select_recipe.html:367 #: templates/measure/task_execute.html:664 msgid "Annulla" msgstr "Annulla" -#: templates/admin/stations.html:210 templates/admin/users.html:298 +#: templates/admin/stations.html:240 templates/admin/users.html:298 #: templates/auth/profile.html:171 msgid "Salva Modifiche" msgstr "Salva Modifiche" -#: templates/admin/stations.html:210 +#: templates/admin/stations.html:240 msgid "Crea Stazione" msgstr "Crea Stazione" -#: templates/admin/stations.html:211 templates/admin/users.html:299 +#: templates/admin/stations.html:241 templates/admin/users.html:299 #: templates/maker/recipe_editor.html:107 #: templates/maker/recipe_editor.html:448 templates/maker/task_drawing.html:61 #: templates/measure/task_execute.html:380 msgid "Salvataggio..." msgstr "Salvataggio..." -#: templates/admin/stations.html:228 +#: templates/admin/stations.html:258 msgid "Ricette Assegnate" msgstr "Ricette Assegnate" -#: templates/admin/stations.html:246 +#: templates/admin/stations.html:276 msgid "Filtra per codice o nome ricetta..." msgstr "Filtra per codice o nome ricetta..." -#: templates/admin/stations.html:259 +#: templates/admin/stations.html:289 msgid "Ricette disponibili" msgstr "Ricette disponibili" -#: templates/admin/stations.html:272 +#: templates/admin/stations.html:302 msgid "Assegna a questa stazione" msgstr "Assegna a questa stazione" -#: templates/admin/stations.html:276 +#: templates/admin/stations.html:306 msgid "Assegna" msgstr "Assegna" -#: templates/admin/stations.html:293 +#: templates/admin/stations.html:323 msgid "Assegnate alla stazione" msgstr "Assegnate alla stazione" -#: templates/admin/stations.html:305 +#: templates/admin/stations.html:335 msgid "Rimuovi assegnazione" msgstr "Rimuovi assegnazione" -#: templates/admin/stations.html:332 +#: templates/admin/stations.html:362 #: templates/components/barcode_scanner.html:145 msgid "Chiudi" msgstr "Chiudi" -#: templates/admin/stations.html:345 templates/maker/recipe_list.html:363 +#: templates/admin/stations.html:376 +msgid "Azzera Stazione" +msgstr "Azzera Stazione" + +#: templates/admin/stations.html:378 +msgid "Rimuovere tutte le ricette assegnate a" +msgstr "Rimuovere tutte le ricette assegnate a" + +#: templates/admin/stations.html:383 +msgid "assegnazioni verranno rimosse. La stazione resta, e si potrà riassegnare." +msgstr "assegnazioni verranno rimosse. La stazione resta, e si potrà riassegnare." + +#: templates/admin/stations.html:394 +msgid "Azzera" +msgstr "Azzera" + +#: templates/admin/stations.html:407 templates/maker/recipe_list.html:363 msgid "Conferma Eliminazione" msgstr "Conferma Eliminazione" -#: templates/admin/stations.html:347 +#: templates/admin/stations.html:409 msgid "Sei sicuro di voler eliminare la stazione" msgstr "Sei sicuro di voler eliminare la stazione" -#: templates/admin/stations.html:349 +#: templates/admin/stations.html:411 msgid "Verranno rimosse anche tutte le assegnazioni di ricette." msgstr "Verranno rimosse anche tutte le assegnazioni di ricette." -#: templates/admin/stations.html:430 templates/admin/stations.html:436 +#: templates/admin/stations.html:494 templates/admin/stations.html:500 msgid "Nessun risultato per il filtro" msgstr "Nessun risultato per il filtro" -#: templates/admin/stations.html:431 +#: templates/admin/stations.html:495 msgid "Nessuna ricetta nel sistema" msgstr "Nessuna ricetta nel sistema" -#: templates/admin/stations.html:432 +#: templates/admin/stations.html:496 msgid "Tutte le ricette sono già assegnate" msgstr "Tutte le ricette sono già assegnate" -#: templates/admin/stations.html:437 +#: templates/admin/stations.html:501 msgid "Nessuna ricetta assegnata" msgstr "Nessuna ricetta assegnata" -#: templates/admin/stations.html:492 +#: templates/admin/stations.html:558 msgid "Codice e nome sono obbligatori" msgstr "Codice e nome sono obbligatori" -#: templates/admin/stations.html:509 +#: templates/admin/stations.html:575 msgid "Errore nella creazione" msgstr "Errore nella creazione" -#: templates/admin/stations.html:537 +#: templates/admin/stations.html:609 +msgid "Errore nell azzeramento" +msgstr "Errore nell azzeramento" + +#: templates/admin/stations.html:637 msgid "Errore nella eliminazione" msgstr "Errore nella eliminazione" # Maker - API Errors -#: templates/admin/stations.html:562 +#: templates/admin/stations.html:662 msgid "Errore nel caricamento delle ricette" msgstr "Errore nel caricamento delle ricette" -#: templates/admin/stations.html:590 +#: templates/admin/stations.html:690 msgid "Errore nella assegnazione" msgstr "Errore nella assegnazione" -#: templates/admin/stations.html:613 +#: templates/admin/stations.html:717 msgid "Errore nella rimozione" msgstr "Errore nella rimozione" @@ -457,8 +515,8 @@ msgid "Nuovo Utente" msgstr "Nuovo Utente" #: templates/admin/users.html:48 templates/admin/users.html:173 -#: templates/admin/users.html:179 templates/auth/login.html:33 -#: templates/auth/login.html:47 templates/auth/profile.html:36 +#: templates/admin/users.html:179 templates/auth/login.html:35 +#: templates/auth/login.html:49 templates/auth/profile.html:36 #: templates/measure/task_execute.html:649 msgid "Username" msgstr "Username" @@ -510,7 +568,7 @@ msgid "Il nome utente non può essere modificato" msgstr "Il nome utente non può essere modificato" #: templates/admin/users.html:206 templates/admin/users.html:214 -#: templates/auth/login.html:55 templates/auth/login.html:69 +#: templates/auth/login.html:57 templates/auth/login.html:71 #: templates/measure/task_execute.html:654 msgid "Password" msgstr "Password" @@ -580,36 +638,36 @@ msgid "Username, nome visualizzato e password sono obbligatori" msgstr "Username, nome visualizzato e password sono obbligatori" # Login Page -#: templates/auth/login.html:23 +#: templates/auth/login.html:25 msgid "Accedi al sistema" msgstr "Accedi al sistema" -#: templates/auth/login.html:73 +#: templates/auth/login.html:75 msgid "Nascondi password" msgstr "Nascondi password" -#: templates/auth/login.html:73 +#: templates/auth/login.html:75 msgid "Mostra password" msgstr "Mostra password" -#: templates/auth/login.html:94 +#: templates/auth/login.html:96 msgid "Accedi" msgstr "Accedi" -#: templates/auth/login.html:102 +#: templates/auth/login.html:104 msgid "Hai dimenticato la password?" msgstr "Hai dimenticato la password?" -#: templates/auth/login.html:104 +#: templates/auth/login.html:106 msgid "Contatta l'amministratore" msgstr "Contatta l'amministratore" # Additional Login Page -#: templates/auth/login.html:111 +#: templates/auth/login.html:113 msgid "Sistema di misurazione industriale" msgstr "Sistema di misurazione industriale" -#: templates/auth/profile.html:2 templates/components/navbar.html:147 +#: templates/auth/profile.html:2 templates/components/navbar.html:149 msgid "Profilo" msgstr "Profilo" @@ -659,15 +717,15 @@ msgstr "Esci dal sistema" msgid "Termina la sessione corrente e torna alla schermata di login" msgstr "Termina la sessione corrente e torna alla schermata di login" -#: templates/auth/profile.html:197 templates/components/navbar.html:158 -#: templates/components/navbar.html:167 +#: templates/auth/profile.html:197 templates/components/navbar.html:160 +#: templates/components/navbar.html:169 msgid "Logout" msgstr "Logout" #: templates/components/barcode_scanner.html:21 #: templates/components/barcode_scanner.html:54 -#: templates/measure/select_recipe.html:90 -#: templates/measure/select_recipe.html:302 +#: templates/measure/select_recipe.html:107 +#: templates/measure/select_recipe.html:319 msgid "Scansiona Barcode" msgstr "Scansiona Barcode" @@ -753,7 +811,7 @@ msgstr "Utenti" msgid "Stazioni" msgstr "Stazioni" -#: templates/components/navbar.html:154 +#: templates/components/navbar.html:156 msgid "Logout bloccato durante le misurazioni" msgstr "Logout bloccato durante le misurazioni" @@ -956,7 +1014,7 @@ msgstr "Errore durante eliminazione" # Recipe Selection Additional #: templates/maker/recipe_list.html:55 templates/measure/select_recipe.html:55 -#: templates/measure/task_execute.html:1111 +#: templates/measure/task_execute.html:1121 msgid "Errore di connessione" msgstr "Errore di connessione" @@ -989,7 +1047,7 @@ msgid "ricetta trovata" msgstr "ricetta trovata" #: templates/maker/recipe_list.html:144 -#: templates/measure/select_recipe.html:158 +#: templates/measure/select_recipe.html:175 msgid "ricette trovate" msgstr "ricette trovate" @@ -1007,7 +1065,7 @@ msgid "Versioni" msgstr "Versioni" #: templates/maker/recipe_list.html:297 -#: templates/measure/select_recipe.html:256 +#: templates/measure/select_recipe.html:273 msgid "Nessuna ricetta trovata" msgstr "Nessuna ricetta trovata" @@ -1151,7 +1209,7 @@ msgid "Torna ai Task" msgstr "Torna ai Task" #: templates/maker/task_drawing.html:205 templates/maker/task_drawing.html:209 -#: templates/measure/select_recipe.html:236 +#: templates/measure/select_recipe.html:253 msgid "Seleziona" msgstr "Seleziona" @@ -1554,7 +1612,7 @@ msgid "Misurazione aggiunta" msgstr "Misurazione aggiunta" #: templates/maker/task_editor.html:1645 -#: templates/measure/task_execute.html:862 +#: templates/measure/task_execute.html:870 msgid "Errore nel salvataggio della misurazione" msgstr "Errore nel salvataggio della misurazione" @@ -1629,57 +1687,69 @@ msgstr "Scegli la ricetta di misura da eseguire" msgid "Stazione" msgstr "Stazione" -#: templates/measure/select_recipe.html:102 +#: templates/measure/select_recipe.html:89 +msgid "Stazione forzata per collaudo" +msgstr "Stazione forzata per collaudo" + +#: templates/measure/select_recipe.html:89 +msgid "configurata" +msgstr "configurata" + +#: templates/measure/select_recipe.html:94 +msgid "Ripristina" +msgstr "Ripristina" + +#: templates/measure/select_recipe.html:119 msgid "Cerca ricetta" msgstr "Cerca ricetta" -#: templates/measure/select_recipe.html:106 +#: templates/measure/select_recipe.html:123 msgid "Nome, codice o descrizione..." msgstr "Nome, codice o descrizione..." -#: templates/measure/select_recipe.html:119 +#: templates/measure/select_recipe.html:136 msgid "Tracciabilità" msgstr "Tracciabilità" -#: templates/measure/select_recipe.html:120 +#: templates/measure/select_recipe.html:137 msgid "Dati del pezzo da misurare — compila prima di selezionare la ricetta" msgstr "Dati del pezzo da misurare — compila prima di selezionare la ricetta" -#: templates/measure/select_recipe.html:130 +#: templates/measure/select_recipe.html:147 #: templates/measure/task_complete.html:281 msgid "Numero Lotto" msgstr "Numero Lotto" -#: templates/measure/select_recipe.html:134 +#: templates/measure/select_recipe.html:151 msgid "Es. LOT-2026-001 (opzionale)" msgstr "Es. LOT-2026-001 (opzionale)" -#: templates/measure/select_recipe.html:144 +#: templates/measure/select_recipe.html:161 #: templates/measure/task_complete.html:282 msgid "Numero Seriale" msgstr "Numero Seriale" -#: templates/measure/select_recipe.html:148 +#: templates/measure/select_recipe.html:165 msgid "Es. SN-000123 (opzionale)" msgstr "Es. SN-000123 (opzionale)" -#: templates/measure/select_recipe.html:201 +#: templates/measure/select_recipe.html:218 msgid "Nessuna descrizione disponibile" msgstr "Nessuna descrizione disponibile" -#: templates/measure/select_recipe.html:260 +#: templates/measure/select_recipe.html:277 msgid "Nessun risultato per" msgstr "Nessun risultato per" -#: templates/measure/select_recipe.html:261 +#: templates/measure/select_recipe.html:278 msgid "Prova con un termine diverso." msgstr "Prova con un termine diverso." -#: templates/measure/select_recipe.html:264 +#: templates/measure/select_recipe.html:281 msgid "Non ci sono ricette disponibili al momento." msgstr "Non ci sono ricette disponibili al momento." -#: templates/measure/select_recipe.html:317 +#: templates/measure/select_recipe.html:334 msgid "" "Inserisci o scansiona il codice della ricetta per selezionarla " "automaticamente." @@ -1687,15 +1757,15 @@ msgstr "" "Inserisci o scansiona il codice della ricetta per selezionarla " "automaticamente." -#: templates/measure/select_recipe.html:322 +#: templates/measure/select_recipe.html:339 msgid "Codice Ricetta" msgstr "Codice Ricetta" -#: templates/measure/select_recipe.html:328 +#: templates/measure/select_recipe.html:345 msgid "Es. REC-001" msgstr "Es. REC-001" -#: templates/measure/select_recipe.html:364 +#: templates/measure/select_recipe.html:381 msgid "Cerca" msgstr "Cerca" @@ -1988,23 +2058,23 @@ msgstr "Username capoturno" msgid "Autorizza" msgstr "Autorizza" -#: templates/measure/task_execute.html:906 +#: templates/measure/task_execute.html:914 msgid "Errore di rete. Riprovare." msgstr "Errore di rete. Riprovare." -#: templates/measure/task_execute.html:1068 +#: templates/measure/task_execute.html:1078 msgid "Misurazione fuori tolleranza" msgstr "Misurazione fuori tolleranza" -#: templates/measure/task_execute.html:1069 +#: templates/measure/task_execute.html:1079 msgid "Fermo linea richiesto" msgstr "Fermo linea richiesto" -#: templates/measure/task_execute.html:1070 +#: templates/measure/task_execute.html:1080 msgid "Fine produzione richiesta" msgstr "Fine produzione richiesta" -#: templates/measure/task_execute.html:1089 +#: templates/measure/task_execute.html:1099 msgid "Credenziali non valide o utente non autorizzato" msgstr "Credenziali non valide o utente non autorizzato" diff --git a/src/frontend/flask_app/translations/messages.pot b/src/frontend/flask_app/translations/messages.pot index 51abd43..e4099a8 100644 --- a/src/frontend/flask_app/translations/messages.pot +++ b/src/frontend/flask_app/translations/messages.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2026-06-11 15:33+0000\n" +"POT-Creation-Date: 2026-07-28 15:24+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -39,7 +39,7 @@ msgstr "" msgid "Inserisci username e password" msgstr "" -#: blueprints/auth.py:81 blueprints/measure.py:346 +#: blueprints/auth.py:81 blueprints/measure.py:407 msgid "Credenziali non valide" msgstr "" @@ -99,64 +99,86 @@ msgstr "" msgid "Nome file vuoto" msgstr "" -#: blueprints/measure.py:38 -#, python-format -msgid "Errore nel caricamento delle ricette: %(detail)s" +#: blueprints/measure.py:45 +msgid "Il cambio stazione da URL non è abilitato su questa postazione." msgstr "" -#: blueprints/measure.py:86 blueprints/measure.py:172 +#: blueprints/measure.py:50 +msgid "Stazione riportata a quella configurata." +msgstr "" + +#: blueprints/measure.py:56 +msgid "Impossibile contattare il server per cambiare stazione." +msgstr "" + +#: blueprints/measure.py:61 #, python-format -msgid "Ricetta non trovata: %(detail)s" +msgid "Stazione '%(code)s' inesistente o disattivata: stazione invariata." +msgstr "" + +#: blueprints/measure.py:68 +#, python-format +msgid "Stazione corrente: %(code)s" msgstr "" #: blueprints/measure.py:96 #, python-format +msgid "Errore nel caricamento delle ricette: %(detail)s" +msgstr "" + +#: blueprints/measure.py:146 blueprints/measure.py:232 +#, python-format +msgid "Ricetta non trovata: %(detail)s" +msgstr "" + +#: blueprints/measure.py:156 +#, python-format msgid "Errore nel caricamento dei task: %(detail)s" msgstr "" -#: blueprints/measure.py:125 +#: blueprints/measure.py:185 #, python-format msgid "Task non trovato: %(detail)s" msgstr "" -#: blueprints/measure.py:241 +#: blueprints/measure.py:301 msgid "Codice non fornito" msgstr "" -#: blueprints/measure.py:247 templates/measure/select_recipe.html:48 +#: blueprints/measure.py:307 templates/measure/select_recipe.html:48 msgid "Ricetta non trovata" msgstr "" -#: blueprints/measure.py:303 +#: blueprints/measure.py:363 msgid "Dati mancanti: subtask_id, version_id e value sono obbligatori" msgstr "" -#: blueprints/measure.py:322 templates/admin/stations.html:485 +#: blueprints/measure.py:383 templates/admin/stations.html:549 #: templates/maker/recipe_editor.html:543 msgid "Errore nel salvataggio" msgstr "" -#: blueprints/measure.py:341 +#: blueprints/measure.py:402 msgid "Username e password richiesti" msgstr "" -#: blueprints/measure.py:351 +#: blueprints/measure.py:412 msgid "Utente non autorizzato (richiesto capoturno)" msgstr "" -#: templates/base.html:175 +#: templates/base.html:173 msgid "Sessione in scadenza" msgstr "" -#: templates/base.html:178 +#: templates/base.html:176 msgid "Sarai disconnesso tra" msgstr "" -#: templates/base.html:178 +#: templates/base.html:176 msgid "secondi per inattività." msgstr "" -#: templates/base.html:181 +#: templates/base.html:179 msgid "Continua a lavorare" msgstr "" @@ -198,16 +220,16 @@ msgstr "" msgid "Salva impostazioni" msgstr "" -#: templates/admin/settings.html:107 templates/admin/stations.html:516 -#: templates/admin/stations.html:544 templates/admin/stations.html:565 -#: templates/admin/stations.html:596 templates/admin/stations.html:618 -#: templates/admin/users.html:497 templates/admin/users.html:533 -#: templates/maker/recipe_editor.html:571 templates/maker/task_drawing.html:65 -#: templates/maker/task_editor.html:1243 templates/maker/task_editor.html:1321 -#: templates/maker/task_editor.html:1382 templates/maker/task_editor.html:1415 -#: templates/maker/task_editor.html:1510 templates/maker/task_editor.html:1586 -#: templates/maker/task_editor.html:1660 templates/maker/task_editor.html:1697 -#: templates/maker/task_editor.html:1776 +#: templates/admin/settings.html:107 templates/admin/stations.html:582 +#: templates/admin/stations.html:621 templates/admin/stations.html:644 +#: templates/admin/stations.html:665 templates/admin/stations.html:700 +#: templates/admin/stations.html:723 templates/admin/users.html:497 +#: templates/admin/users.html:533 templates/maker/recipe_editor.html:571 +#: templates/maker/task_drawing.html:65 templates/maker/task_editor.html:1243 +#: templates/maker/task_editor.html:1321 templates/maker/task_editor.html:1382 +#: templates/maker/task_editor.html:1415 templates/maker/task_editor.html:1510 +#: templates/maker/task_editor.html:1586 templates/maker/task_editor.html:1660 +#: templates/maker/task_editor.html:1697 templates/maker/task_editor.html:1776 #: templates/statistics/dashboard.html:372 #: templates/statistics/dashboard.html:457 msgid "Errore di connessione al server" @@ -227,206 +249,242 @@ msgstr "" msgid "Cerca stazione..." msgstr "" -#: templates/admin/stations.html:39 templates/admin/stations.html:133 +#: templates/admin/stations.html:39 templates/admin/stations.html:163 msgid "Nuova Stazione" msgstr "" -#: templates/admin/stations.html:49 templates/admin/stations.html:143 +#: templates/admin/stations.html:49 templates/admin/stations.html:173 #: templates/maker/recipe_editor.html:195 #: templates/measure/task_complete.html:49 msgid "Codice" msgstr "" -#: templates/admin/stations.html:50 templates/admin/stations.html:160 +#: templates/admin/stations.html:50 templates/admin/stations.html:190 #: templates/maker/recipe_editor.html:213 #: templates/measure/task_complete.html:53 msgid "Nome" msgstr "" -#: templates/admin/stations.html:51 templates/admin/stations.html:169 +#: templates/admin/stations.html:51 templates/admin/stations.html:199 msgid "Postazione" msgstr "" -#: templates/admin/stations.html:52 templates/admin/users.html:52 +#: templates/admin/stations.html:52 +msgid "Ricette collegate" +msgstr "" + +#: templates/admin/stations.html:53 templates/admin/users.html:52 #: templates/measure/task_complete.html:292 msgid "Stato" msgstr "" -#: templates/admin/stations.html:53 templates/admin/users.html:53 +#: templates/admin/stations.html:54 templates/admin/users.html:53 #: templates/maker/task_editor.html:551 msgid "Azioni" msgstr "" -#: templates/admin/stations.html:70 templates/admin/stations.html:189 +#: templates/admin/stations.html:68 +msgid "Nessuna ricetta" +msgstr "" + +#: templates/admin/stations.html:89 templates/admin/stations.html:219 #: templates/maker/recipe_list.html:186 msgid "Attiva" msgstr "" -#: templates/admin/stations.html:70 templates/maker/recipe_list.html:186 +#: templates/admin/stations.html:89 templates/maker/recipe_list.html:186 msgid "Disattivata" msgstr "" -#: templates/admin/stations.html:76 +#: templates/admin/stations.html:95 msgid "Gestisci ricette" msgstr "" -#: templates/admin/stations.html:83 templates/admin/users.html:100 +#: templates/admin/stations.html:105 +msgid "Azzera le ricette della stazione" +msgstr "" + +#: templates/admin/stations.html:106 +msgid "Nessuna ricetta da azzerare" +msgstr "" + +#: templates/admin/stations.html:113 templates/admin/users.html:100 #: templates/maker/recipe_list.html:237 templates/maker/task_editor.html:602 msgid "Modifica" msgstr "" -#: templates/admin/stations.html:90 templates/admin/stations.html:359 +#: templates/admin/stations.html:120 templates/admin/stations.html:421 #: templates/maker/recipe_list.html:277 templates/maker/recipe_list.html:387 #: templates/maker/task_drawing.html:275 templates/maker/task_editor.html:610 msgid "Elimina" msgstr "" -#: templates/admin/stations.html:108 +#: templates/admin/stations.html:138 msgid "Nessuna stazione trovata" msgstr "" -#: templates/admin/stations.html:114 +#: templates/admin/stations.html:144 msgid "stazioni" msgstr "" -#: templates/admin/stations.html:133 +#: templates/admin/stations.html:163 msgid "Modifica Stazione" msgstr "" -#: templates/admin/stations.html:152 +#: templates/admin/stations.html:182 msgid "Il codice non può essere modificato" msgstr "" -#: templates/admin/stations.html:155 +#: templates/admin/stations.html:185 msgid "Identificativo univoco usato dal client tramite STATION_CODE" msgstr "" -#: templates/admin/stations.html:164 +#: templates/admin/stations.html:194 msgid "Nome descrittivo della stazione" msgstr "" -#: templates/admin/stations.html:173 +#: templates/admin/stations.html:203 msgid "Es. Reparto A - Linea 2" msgstr "" -#: templates/admin/stations.html:178 +#: templates/admin/stations.html:208 msgid "Note" msgstr "" -#: templates/admin/stations.html:182 +#: templates/admin/stations.html:212 msgid "Note opzionali" msgstr "" -#: templates/admin/stations.html:204 templates/admin/stations.html:354 -#: templates/admin/users.html:292 templates/admin/users.html:323 -#: templates/maker/recipe_editor.html:137 +#: templates/admin/stations.html:234 templates/admin/stations.html:389 +#: templates/admin/stations.html:416 templates/admin/users.html:292 +#: templates/admin/users.html:323 templates/maker/recipe_editor.html:137 #: templates/maker/recipe_editor.html:456 templates/maker/recipe_list.html:383 #: templates/maker/task_editor.html:294 templates/maker/task_editor.html:403 #: templates/maker/task_editor.html:747 templates/maker/task_editor.html:866 #: templates/maker/task_editor.html:931 templates/maker/task_editor.html:1036 -#: templates/measure/select_recipe.html:350 +#: templates/measure/select_recipe.html:367 #: templates/measure/task_execute.html:664 msgid "Annulla" msgstr "" -#: templates/admin/stations.html:210 templates/admin/users.html:298 +#: templates/admin/stations.html:240 templates/admin/users.html:298 #: templates/auth/profile.html:171 msgid "Salva Modifiche" msgstr "" -#: templates/admin/stations.html:210 +#: templates/admin/stations.html:240 msgid "Crea Stazione" msgstr "" -#: templates/admin/stations.html:211 templates/admin/users.html:299 +#: templates/admin/stations.html:241 templates/admin/users.html:299 #: templates/maker/recipe_editor.html:107 #: templates/maker/recipe_editor.html:448 templates/maker/task_drawing.html:61 #: templates/measure/task_execute.html:380 msgid "Salvataggio..." msgstr "" -#: templates/admin/stations.html:228 +#: templates/admin/stations.html:258 msgid "Ricette Assegnate" msgstr "" -#: templates/admin/stations.html:246 +#: templates/admin/stations.html:276 msgid "Filtra per codice o nome ricetta..." msgstr "" -#: templates/admin/stations.html:259 +#: templates/admin/stations.html:289 msgid "Ricette disponibili" msgstr "" -#: templates/admin/stations.html:272 +#: templates/admin/stations.html:302 msgid "Assegna a questa stazione" msgstr "" -#: templates/admin/stations.html:276 +#: templates/admin/stations.html:306 msgid "Assegna" msgstr "" -#: templates/admin/stations.html:293 +#: templates/admin/stations.html:323 msgid "Assegnate alla stazione" msgstr "" -#: templates/admin/stations.html:305 +#: templates/admin/stations.html:335 msgid "Rimuovi assegnazione" msgstr "" -#: templates/admin/stations.html:332 +#: templates/admin/stations.html:362 #: templates/components/barcode_scanner.html:145 msgid "Chiudi" msgstr "" -#: templates/admin/stations.html:345 templates/maker/recipe_list.html:363 +#: templates/admin/stations.html:376 +msgid "Azzera Stazione" +msgstr "" + +#: templates/admin/stations.html:378 +msgid "Rimuovere tutte le ricette assegnate a" +msgstr "" + +#: templates/admin/stations.html:383 +msgid "assegnazioni verranno rimosse. La stazione resta, e si potrà riassegnare." +msgstr "" + +#: templates/admin/stations.html:394 +msgid "Azzera" +msgstr "" + +#: templates/admin/stations.html:407 templates/maker/recipe_list.html:363 msgid "Conferma Eliminazione" msgstr "" -#: templates/admin/stations.html:347 +#: templates/admin/stations.html:409 msgid "Sei sicuro di voler eliminare la stazione" msgstr "" -#: templates/admin/stations.html:349 +#: templates/admin/stations.html:411 msgid "Verranno rimosse anche tutte le assegnazioni di ricette." msgstr "" -#: templates/admin/stations.html:430 templates/admin/stations.html:436 +#: templates/admin/stations.html:494 templates/admin/stations.html:500 msgid "Nessun risultato per il filtro" msgstr "" -#: templates/admin/stations.html:431 +#: templates/admin/stations.html:495 msgid "Nessuna ricetta nel sistema" msgstr "" -#: templates/admin/stations.html:432 +#: templates/admin/stations.html:496 msgid "Tutte le ricette sono già assegnate" msgstr "" -#: templates/admin/stations.html:437 +#: templates/admin/stations.html:501 msgid "Nessuna ricetta assegnata" msgstr "" -#: templates/admin/stations.html:492 +#: templates/admin/stations.html:558 msgid "Codice e nome sono obbligatori" msgstr "" -#: templates/admin/stations.html:509 +#: templates/admin/stations.html:575 msgid "Errore nella creazione" msgstr "" -#: templates/admin/stations.html:537 +#: templates/admin/stations.html:609 +msgid "Errore nell azzeramento" +msgstr "" + +#: templates/admin/stations.html:637 msgid "Errore nella eliminazione" msgstr "" -#: templates/admin/stations.html:562 +#: templates/admin/stations.html:662 msgid "Errore nel caricamento delle ricette" msgstr "" -#: templates/admin/stations.html:590 +#: templates/admin/stations.html:690 msgid "Errore nella assegnazione" msgstr "" -#: templates/admin/stations.html:613 +#: templates/admin/stations.html:717 msgid "Errore nella rimozione" msgstr "" @@ -447,8 +505,8 @@ msgid "Nuovo Utente" msgstr "" #: templates/admin/users.html:48 templates/admin/users.html:173 -#: templates/admin/users.html:179 templates/auth/login.html:33 -#: templates/auth/login.html:47 templates/auth/profile.html:36 +#: templates/admin/users.html:179 templates/auth/login.html:35 +#: templates/auth/login.html:49 templates/auth/profile.html:36 #: templates/measure/task_execute.html:649 msgid "Username" msgstr "" @@ -500,7 +558,7 @@ msgid "Il nome utente non può essere modificato" msgstr "" #: templates/admin/users.html:206 templates/admin/users.html:214 -#: templates/auth/login.html:55 templates/auth/login.html:69 +#: templates/auth/login.html:57 templates/auth/login.html:71 #: templates/measure/task_execute.html:654 msgid "Password" msgstr "" @@ -569,35 +627,35 @@ msgstr "" msgid "Username, nome visualizzato e password sono obbligatori" msgstr "" -#: templates/auth/login.html:23 +#: templates/auth/login.html:25 msgid "Accedi al sistema" msgstr "" -#: templates/auth/login.html:73 +#: templates/auth/login.html:75 msgid "Nascondi password" msgstr "" -#: templates/auth/login.html:73 +#: templates/auth/login.html:75 msgid "Mostra password" msgstr "" -#: templates/auth/login.html:94 +#: templates/auth/login.html:96 msgid "Accedi" msgstr "" -#: templates/auth/login.html:102 +#: templates/auth/login.html:104 msgid "Hai dimenticato la password?" msgstr "" -#: templates/auth/login.html:104 +#: templates/auth/login.html:106 msgid "Contatta l'amministratore" msgstr "" -#: templates/auth/login.html:111 +#: templates/auth/login.html:113 msgid "Sistema di misurazione industriale" msgstr "" -#: templates/auth/profile.html:2 templates/components/navbar.html:147 +#: templates/auth/profile.html:2 templates/components/navbar.html:149 msgid "Profilo" msgstr "" @@ -645,15 +703,15 @@ msgstr "" msgid "Termina la sessione corrente e torna alla schermata di login" msgstr "" -#: templates/auth/profile.html:197 templates/components/navbar.html:158 -#: templates/components/navbar.html:167 +#: templates/auth/profile.html:197 templates/components/navbar.html:160 +#: templates/components/navbar.html:169 msgid "Logout" msgstr "" #: templates/components/barcode_scanner.html:21 #: templates/components/barcode_scanner.html:54 -#: templates/measure/select_recipe.html:90 -#: templates/measure/select_recipe.html:302 +#: templates/measure/select_recipe.html:107 +#: templates/measure/select_recipe.html:319 msgid "Scansiona Barcode" msgstr "" @@ -734,7 +792,7 @@ msgstr "" msgid "Stazioni" msgstr "" -#: templates/components/navbar.html:154 +#: templates/components/navbar.html:156 msgid "Logout bloccato durante le misurazioni" msgstr "" @@ -931,7 +989,7 @@ msgid "Errore durante eliminazione" msgstr "" #: templates/maker/recipe_list.html:55 templates/measure/select_recipe.html:55 -#: templates/measure/task_execute.html:1111 +#: templates/measure/task_execute.html:1121 msgid "Errore di connessione" msgstr "" @@ -964,7 +1022,7 @@ msgid "ricetta trovata" msgstr "" #: templates/maker/recipe_list.html:144 -#: templates/measure/select_recipe.html:158 +#: templates/measure/select_recipe.html:175 msgid "ricette trovate" msgstr "" @@ -982,7 +1040,7 @@ msgid "Versioni" msgstr "" #: templates/maker/recipe_list.html:297 -#: templates/measure/select_recipe.html:256 +#: templates/measure/select_recipe.html:273 msgid "Nessuna ricetta trovata" msgstr "" @@ -1121,7 +1179,7 @@ msgid "Torna ai Task" msgstr "" #: templates/maker/task_drawing.html:205 templates/maker/task_drawing.html:209 -#: templates/measure/select_recipe.html:236 +#: templates/measure/select_recipe.html:253 msgid "Seleziona" msgstr "" @@ -1522,7 +1580,7 @@ msgid "Misurazione aggiunta" msgstr "" #: templates/maker/task_editor.html:1645 -#: templates/measure/task_execute.html:862 +#: templates/measure/task_execute.html:870 msgid "Errore nel salvataggio della misurazione" msgstr "" @@ -1593,71 +1651,83 @@ msgstr "" msgid "Stazione" msgstr "" -#: templates/measure/select_recipe.html:102 -msgid "Cerca ricetta" +#: templates/measure/select_recipe.html:89 +msgid "Stazione forzata per collaudo" msgstr "" -#: templates/measure/select_recipe.html:106 -msgid "Nome, codice o descrizione..." +#: templates/measure/select_recipe.html:89 +msgid "configurata" +msgstr "" + +#: templates/measure/select_recipe.html:94 +msgid "Ripristina" msgstr "" #: templates/measure/select_recipe.html:119 +msgid "Cerca ricetta" +msgstr "" + +#: templates/measure/select_recipe.html:123 +msgid "Nome, codice o descrizione..." +msgstr "" + +#: templates/measure/select_recipe.html:136 msgid "Tracciabilità" msgstr "" -#: templates/measure/select_recipe.html:120 +#: templates/measure/select_recipe.html:137 msgid "Dati del pezzo da misurare — compila prima di selezionare la ricetta" msgstr "" -#: templates/measure/select_recipe.html:130 +#: templates/measure/select_recipe.html:147 #: templates/measure/task_complete.html:281 msgid "Numero Lotto" msgstr "" -#: templates/measure/select_recipe.html:134 +#: templates/measure/select_recipe.html:151 msgid "Es. LOT-2026-001 (opzionale)" msgstr "" -#: templates/measure/select_recipe.html:144 +#: templates/measure/select_recipe.html:161 #: templates/measure/task_complete.html:282 msgid "Numero Seriale" msgstr "" -#: templates/measure/select_recipe.html:148 +#: templates/measure/select_recipe.html:165 msgid "Es. SN-000123 (opzionale)" msgstr "" -#: templates/measure/select_recipe.html:201 +#: templates/measure/select_recipe.html:218 msgid "Nessuna descrizione disponibile" msgstr "" -#: templates/measure/select_recipe.html:260 +#: templates/measure/select_recipe.html:277 msgid "Nessun risultato per" msgstr "" -#: templates/measure/select_recipe.html:261 +#: templates/measure/select_recipe.html:278 msgid "Prova con un termine diverso." msgstr "" -#: templates/measure/select_recipe.html:264 +#: templates/measure/select_recipe.html:281 msgid "Non ci sono ricette disponibili al momento." msgstr "" -#: templates/measure/select_recipe.html:317 +#: templates/measure/select_recipe.html:334 msgid "" "Inserisci o scansiona il codice della ricetta per selezionarla " "automaticamente." msgstr "" -#: templates/measure/select_recipe.html:322 +#: templates/measure/select_recipe.html:339 msgid "Codice Ricetta" msgstr "" -#: templates/measure/select_recipe.html:328 +#: templates/measure/select_recipe.html:345 msgid "Es. REC-001" msgstr "" -#: templates/measure/select_recipe.html:364 +#: templates/measure/select_recipe.html:381 msgid "Cerca" msgstr "" @@ -1947,23 +2017,23 @@ msgstr "" msgid "Autorizza" msgstr "" -#: templates/measure/task_execute.html:906 +#: templates/measure/task_execute.html:914 msgid "Errore di rete. Riprovare." msgstr "" -#: templates/measure/task_execute.html:1068 +#: templates/measure/task_execute.html:1078 msgid "Misurazione fuori tolleranza" msgstr "" -#: templates/measure/task_execute.html:1069 +#: templates/measure/task_execute.html:1079 msgid "Fermo linea richiesto" msgstr "" -#: templates/measure/task_execute.html:1070 +#: templates/measure/task_execute.html:1080 msgid "Fine produzione richiesta" msgstr "" -#: templates/measure/task_execute.html:1089 +#: templates/measure/task_execute.html:1099 msgid "Credenziali non valide o utente non autorizzato" msgstr ""