feat(measure): l'operatore entra nella sequenza, non in un elenco

Punto 10. Sceglere una ricetta apriva la lista completa dei task e chiedeva
all'operatore di decidere da dove cominciare prima ancora di aver fatto
qualcosa. Ora AVVIA IN SEQUENZA porta dentro il primo task; la lista resta,
un livello sotto, per quando serve vederla tutta o tornare indietro.

- nuova rotta /measure/start/<ricetta>: memorizza lotto e seriale (prima lo
  faceva la lista, che ora si salta), verifica la tracciabilità obbligatoria
  e apre il primo task
- GET /api/measurements/task-progress: quante quote ha già preso ogni task di
  misura, contate per quota e non per tentativo, delimitate dalla produzione
  aperta o, fuori produzione, dall'operatore
- la lista distingue «Incompiuto 1/3» da «Completato 3/3»: un task lasciato a
  metà non somigliava più a uno mai aperto
- «Visualizza singolo TASK» non è più riservato al Maker — dire «incompiuto»
  senza dare la strada per tornarci sarebbe una lamentela, non una funzione
- nel task «Riepilogo» diventa «Completato» e si apre solo quando il ciclo è
  chiuso: altrimenti sarebbe la scorciatoia che rende facoltative le quote
- «Fine ciclo misura» si vede da subito, spento, e dice quante quote mancano;
  prima compariva a task già finito, quando non serviva più saperlo

Il pulsante verde del footer diventa «Task successivo»: due bottoni con la
stessa parola addosso sono uno di troppo.

Test: 334 (erano 309). Il side effect dei mock del client Flask ora risponde
per endpoint invece che per turno — una lista posizionale si rompe appena una
pagina fa una domanda in più al server, che è come crescono tutte.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Adriano Dal Pastro
2026-07-28 21:31:42 +00:00
parent 919ee7ac79
commit 98b1c5ae85
15 changed files with 1656 additions and 659 deletions
+19
View File
@@ -24,6 +24,7 @@ from src.backend.models.api.measurement import (
MeasurementCreate, MeasurementCreate,
MeasurementListResponse, MeasurementListResponse,
MeasurementResponse, MeasurementResponse,
TaskProgressListResponse,
) )
from src.backend.services import auth_service, measurement_service from src.backend.services import auth_service, measurement_service
from src.backend.services.measurement_service import save_measurement from src.backend.services.measurement_service import save_measurement
@@ -77,6 +78,24 @@ async def get_pending_authorisation(
return MeasurementResponse.model_validate(blocking) if blocking else None return MeasurementResponse.model_validate(blocking) if blocking else None
@router.get("/task-progress", response_model=TaskProgressListResponse)
async def get_task_progress(
version_id: int = Query(..., gt=0),
production_run_id: int | None = Query(None, gt=0),
user: User = Depends(require_measurement_tec),
db: AsyncSession = Depends(get_db),
):
"""How many quotes of each measurement task are already taken.
The task list is where an operator picks up work they left behind, and until
now it showed a task begun and a task never touched identically.
"""
tasks = await measurement_service.task_progress(
db, version_id, user.id, production_run_id,
)
return TaskProgressListResponse(tasks=tasks)
@router.post("/{measurement_id}/authorise", response_model=MeasurementResponse) @router.post("/{measurement_id}/authorise", response_model=MeasurementResponse)
async def authorise_measurement( async def authorise_measurement(
measurement_id: int, measurement_id: int,
+16
View File
@@ -63,6 +63,22 @@ class MeasurementAuthorisation(BaseModel):
supervisor_password: str = Field(..., min_length=1) supervisor_password: str = Field(..., min_length=1)
class TaskProgressResponse(BaseModel):
"""How far one measurement task has got: quotes taken out of quotes expected."""
task_id: int
quotes: int
measured: int
# empty (declared as measurement, no quotes yet) | none | partial | complete
state: str
class TaskProgressListResponse(BaseModel):
"""Progress of every measurement task of a version."""
tasks: list[TaskProgressResponse]
class MeasurementQuery(BaseModel): class MeasurementQuery(BaseModel):
"""Schema for measurement query filters.""" """Schema for measurement query filters."""
recipe_id: Optional[int] = None recipe_id: Optional[int] = None
+68 -1
View File
@@ -8,7 +8,9 @@ from sqlalchemy.ext.asyncio import AsyncSession
from src.backend.models.orm.measurement import Measurement from src.backend.models.orm.measurement import Measurement
from src.backend.models.orm.recipe import Recipe, RecipeVersion from src.backend.models.orm.recipe import Recipe, RecipeVersion
from src.backend.models.orm.task import RecipeSubtask from src.backend.models.orm.task import (
MEASURING_TASK_TYPES, RecipeSubtask, RecipeTask,
)
from src.backend.models.orm.user import User from src.backend.models.orm.user import User
@@ -135,6 +137,71 @@ async def pending_authorisation(
return result.scalar_one_or_none() return result.scalar_one_or_none()
async def task_progress(
db: AsyncSession,
version_id: int,
measured_by: int,
production_run_id: int | None = None,
) -> list[dict]:
"""How far each measurement task of a version has got.
A task left half done is the thing the operator cannot see today: they walk
away from it, come back to the list, and it looks exactly like one never
started. Counting quotes against measurements is what tells them apart.
Documental tasks are not in here. A note cannot be half read, and marking one
incomplete for ever would be noise on the only screen that has to stay legible.
Which tasks count is read from the declared type, not from whether quotes happen
to have been entered yet - the same rule point 2 settled everywhere else.
"""
rows = (await db.execute(
select(RecipeTask.id, RecipeSubtask.id)
.join(RecipeSubtask, RecipeSubtask.task_id == RecipeTask.id, isouter=True)
.where(
RecipeTask.version_id == version_id,
RecipeTask.task_type.in_(MEASURING_TASK_TYPES),
)
)).all()
if not rows:
return []
by_task: dict[int, set[int]] = {}
for task_id, subtask_id in rows:
quotes = by_task.setdefault(task_id, set())
if subtask_id is not None:
quotes.add(subtask_id)
measured = set((await db.execute(
_scope(
select(Measurement.subtask_id).distinct(),
version_id, measured_by, production_run_id,
)
)).scalars().all())
progress = []
for task_id, subtask_ids in by_task.items():
total = len(subtask_ids)
done = len(subtask_ids & measured)
if total == 0:
# Declared as a measurement but with no quotes on it yet: nothing to
# take, so nothing to report as missing either.
state = "empty"
elif done == total:
state = "complete"
elif done:
state = "partial"
else:
state = "none"
progress.append({
"task_id": task_id,
"quotes": total,
"measured": done,
"state": state,
})
progress.sort(key=lambda row: row["task_id"])
return progress
async def authorise_measurement( async def authorise_measurement(
db: AsyncSession, measurement: Measurement, supervisor: User, db: AsyncSession, measurement: Measurement, supervisor: User,
) -> Measurement: ) -> Measurement:
+246
View File
@@ -0,0 +1,246 @@
"""Point 10: a task left half done has to look different from one never opened.
The task list showed both the same way, so an operator coming back to a recipe
had to remember where they had got to - or measure something twice. These tests
are about the count the list now puts on each card.
"""
from httpx import AsyncClient
from sqlalchemy import select
from src.backend.models.orm.recipe import RecipeVersion
from src.backend.models.orm.station import Station
from src.backend.models.orm.task import RecipeSubtask, RecipeTask
from src.backend.tests.conftest import auth_headers, create_test_recipe
async def _version_and_task(db_session, recipe_id: int):
version = (await db_session.execute(
select(RecipeVersion).where(
RecipeVersion.recipe_id == recipe_id,
RecipeVersion.is_current == True, # noqa: E712
)
)).scalar_one()
task = (await db_session.execute(
select(RecipeTask).where(RecipeTask.version_id == version.id)
)).scalars().first()
return version, task
async def _add_quote(db_session, task_id: int, marker: int):
quote = RecipeSubtask(
task_id=task_id, marker_number=marker, description=f"Quota {marker}",
nominal=10.0, utl=10.5, uwl=10.3, lwl=9.7, ltl=9.5, unit="mm",
)
db_session.add(quote)
await db_session.commit()
await db_session.refresh(quote)
return quote
async def _measure(client, user, subtask_id, version_id, value=10.0):
return await client.post(
"/api/measurements/", headers=auth_headers(user),
json={"subtask_id": subtask_id, "version_id": version_id, "value": value},
)
async def _progress(client, user, version_id):
resp = await client.get(
"/api/measurements/task-progress",
headers=auth_headers(user),
params={"version_id": version_id},
)
assert resp.status_code == 200
return {row["task_id"]: row for row in resp.json()["tasks"]}
async def test_untouched_task_reads_as_none(
client: AsyncClient, db_session, measurement_tec_user,
):
recipe = await create_test_recipe(db_session, measurement_tec_user.id)
version, task = await _version_and_task(db_session, recipe.id)
rows = await _progress(client, measurement_tec_user, version.id)
assert rows[task.id]["state"] == "none"
assert rows[task.id]["measured"] == 0
async def test_half_measured_task_reads_as_partial(
client: AsyncClient, db_session, measurement_tec_user,
):
recipe = await create_test_recipe(db_session, measurement_tec_user.id)
version, task = await _version_and_task(db_session, recipe.id)
first = (await db_session.execute(
select(RecipeSubtask).where(RecipeSubtask.task_id == task.id)
)).scalars().first()
await _add_quote(db_session, task.id, 2)
await _measure(client, measurement_tec_user, first.id, version.id)
rows = await _progress(client, measurement_tec_user, version.id)
assert rows[task.id] == {
"task_id": task.id, "quotes": 2, "measured": 1, "state": "partial",
}
async def test_every_quote_taken_reads_as_complete(
client: AsyncClient, db_session, measurement_tec_user,
):
recipe = await create_test_recipe(db_session, measurement_tec_user.id)
version, task = await _version_and_task(db_session, recipe.id)
first = (await db_session.execute(
select(RecipeSubtask).where(RecipeSubtask.task_id == task.id)
)).scalars().first()
second = await _add_quote(db_session, task.id, 2)
await _measure(client, measurement_tec_user, first.id, version.id)
await _measure(client, measurement_tec_user, second.id, version.id)
rows = await _progress(client, measurement_tec_user, version.id)
assert rows[task.id]["state"] == "complete"
assert rows[task.id]["measured"] == 2
async def test_measuring_the_same_quote_twice_does_not_inflate_the_count(
client: AsyncClient, db_session, measurement_tec_user,
):
"""Two readings of one quote are one quote done, not two.
Re-measuring is allowed - it is how an operator recovers from a caliper that
slipped - and counting attempts would show a task of two quotes as finished
after one of them had been measured twice.
"""
recipe = await create_test_recipe(db_session, measurement_tec_user.id)
version, task = await _version_and_task(db_session, recipe.id)
first = (await db_session.execute(
select(RecipeSubtask).where(RecipeSubtask.task_id == task.id)
)).scalars().first()
await _add_quote(db_session, task.id, 2)
await _measure(client, measurement_tec_user, first.id, version.id, 10.0)
await _measure(client, measurement_tec_user, first.id, version.id, 10.1)
rows = await _progress(client, measurement_tec_user, version.id)
assert rows[task.id]["measured"] == 1
assert rows[task.id]["state"] == "partial"
async def test_documental_tasks_are_left_out(
client: AsyncClient, db_session, measurement_tec_user,
):
"""A note cannot be half read; carrying it as unfinished for ever is noise."""
recipe = await create_test_recipe(db_session, measurement_tec_user.id)
version, task = await _version_and_task(db_session, recipe.id)
note = RecipeTask(
version_id=version.id, order_index=5, title="Leggere prima",
task_type="note",
)
db_session.add(note)
await db_session.commit()
await db_session.refresh(note)
rows = await _progress(client, measurement_tec_user, version.id)
assert note.id not in rows
assert task.id in rows
async def test_a_measurement_task_without_quotes_is_not_reported_as_missing(
client: AsyncClient, db_session, measurement_tec_user,
):
"""Declared as a measurement, no quotes entered yet: nothing to take."""
recipe = await create_test_recipe(db_session, measurement_tec_user.id)
version, _task = await _version_and_task(db_session, recipe.id)
empty = RecipeTask(
version_id=version.id, order_index=6, title="Da compilare",
task_type="measure",
)
db_session.add(empty)
await db_session.commit()
await db_session.refresh(empty)
rows = await _progress(client, measurement_tec_user, version.id)
assert rows[empty.id]["state"] == "empty"
assert rows[empty.id]["quotes"] == 0
async def test_another_operators_work_is_not_counted_as_yours(
client: AsyncClient, db_session, measurement_tec_user, admin_user,
):
"""Outside a production the boundary is the operator, as it is everywhere else."""
recipe = await create_test_recipe(db_session, measurement_tec_user.id)
version, task = await _version_and_task(db_session, recipe.id)
first = (await db_session.execute(
select(RecipeSubtask).where(RecipeSubtask.task_id == task.id)
)).scalars().first()
await _measure(client, admin_user, first.id, version.id)
rows = await _progress(client, measurement_tec_user, version.id)
assert rows[task.id]["state"] == "none"
async def test_inside_a_production_the_run_is_the_boundary(
client: AsyncClient, db_session, measurement_tec_user, admin_user,
):
"""A quote measured before the production opened is not part of it.
The list is read during the run, and carrying yesterday's readings into it
would show a task as finished that this production has not touched.
"""
recipe = await create_test_recipe(db_session, measurement_tec_user.id)
version, task = await _version_and_task(db_session, recipe.id)
first = (await db_session.execute(
select(RecipeSubtask).where(RecipeSubtask.task_id == task.id)
)).scalars().first()
# Measured outside any production.
await _measure(client, measurement_tec_user, first.id, version.id)
station = Station(
code="ST-PROG", name="Stazione", active=True, created_by=admin_user.id,
)
db_session.add(station)
await db_session.commit()
run = (await client.post(
"/api/production-runs",
headers=auth_headers(measurement_tec_user),
json={"station_code": station.code, "recipe_id": recipe.id},
)).json()
resp = await client.get(
"/api/measurements/task-progress",
headers=auth_headers(measurement_tec_user),
params={"version_id": version.id, "production_run_id": run["id"]},
)
assert resp.status_code == 200
rows = {row["task_id"]: row for row in resp.json()["tasks"]}
assert rows[task.id]["state"] == "none"
await client.post(
"/api/measurements/", headers=auth_headers(measurement_tec_user),
json={
"subtask_id": first.id, "version_id": version.id, "value": 10.0,
"production_run_id": run["id"],
},
)
resp = await client.get(
"/api/measurements/task-progress",
headers=auth_headers(measurement_tec_user),
params={"version_id": version.id, "production_run_id": run["id"]},
)
rows = {row["task_id"]: row for row in resp.json()["tasks"]}
assert rows[task.id]["state"] == "complete"
async def test_progress_needs_a_measurement_role(
client: AsyncClient, db_session, maker_user, measurement_tec_user,
):
recipe = await create_test_recipe(db_session, measurement_tec_user.id)
version, _task = await _version_and_task(db_session, recipe.id)
resp = await client.get(
"/api/measurements/task-progress",
headers=auth_headers(maker_user),
params={"version_id": version.id},
)
assert resp.status_code == 403
+114 -21
View File
@@ -119,6 +119,112 @@ def select_recipe():
) )
def _remember_traceability() -> tuple[str, str]:
"""Carry lot and serial from the query string into the session."""
lot_number = request.args.get("lot_number", session.get("lot_number", ""))
serial_number = request.args.get(
"serial_number", session.get("serial_number", ""),
)
if lot_number:
session["lot_number"] = lot_number
if serial_number:
session["serial_number"] = serial_number
return lot_number, serial_number
def _missing_traceability(recipe: dict, lot: str, serial: str) -> list[str]:
"""The compulsory fields this recipe is still owed."""
missing = []
if recipe.get("requires_lot") and not (lot or "").strip():
missing.append(_("lotto"))
if recipe.get("requires_serial") and not (serial or "").strip():
missing.append(_("seriale"))
return missing
def _ordered_tasks(recipe_id: int) -> list[dict] | None:
"""Tasks of a recipe's current version in execution order, or None on error."""
resp = api_client.get(f"/api/recipes/{recipe_id}/tasks")
if isinstance(resp, dict) and resp.get("error"):
return None
tasks = resp if isinstance(resp, list) else resp.get("items", [])
return sorted(tasks, key=lambda t: (t.get("order_index", 0), t.get("id", 0)))
def _task_progress(version_id, run_id) -> dict[int, dict]:
"""Quotes already taken per measurement task, keyed by task id.
Never fatal: a list that cannot say which task was left half done is still a
usable list, and losing the whole page over a badge would be the worse trade.
"""
if not version_id:
return {}
params = {"version_id": version_id}
if run_id:
params["production_run_id"] = run_id
resp = api_client.get("/api/measurements/task-progress", params=params)
if not isinstance(resp, dict) or resp.get("error"):
return {}
return {row["task_id"]: row for row in resp.get("tasks", [])}
def _current_run_id() -> int | None:
"""The production open at this station, if there is one."""
station_code, _overridden = _current_station()
if not station_code:
return None
# The endpoint answers with the run itself, or null when nothing is running.
resp = api_client.get(
"/api/production-runs/current", params={"station_code": station_code},
)
if not isinstance(resp, dict) or resp.get("error"):
return None
return resp.get("id")
# ---------------------------------------------------------------------------
# Route: start the guided sequence
# ---------------------------------------------------------------------------
@measure_bp.route("/start/<int:recipe_id>")
@login_required
@role_required("MeasurementTec")
def start_sequence(recipe_id: int):
"""Open the first task of a recipe and let the operator walk the sequence.
Choosing a recipe used to land on the full task list, which asked the operator
to pick a starting point before they had done anything. The sequence is the
normal way through; the list is still there, one level down, for when they need
to see the whole of it.
"""
lot_number, serial_number = _remember_traceability()
recipe_resp = api_client.get(f"/api/recipes/{recipe_id}")
if recipe_resp.get("error"):
flash(
_("Ricetta non trovata: %(detail)s",
detail=recipe_resp.get("detail", "")),
"error",
)
return redirect(url_for("measure.select_recipe"))
missing = _missing_traceability(recipe_resp, lot_number, serial_number)
if missing:
flash(
_("Compila prima: %(fields)s", fields=", ".join(missing)),
"error",
)
return redirect(url_for(
"measure.select_recipe", recipe=recipe_resp.get("code", ""),
))
tasks = _ordered_tasks(recipe_id)
if not tasks:
flash(_("Questa ricetta non ha ancora task definiti."), "error")
return redirect(url_for("measure.task_list", recipe_id=recipe_id))
return redirect(url_for("measure.task_execute", task_id=tasks[0]["id"]))
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Route: Task list for a recipe # Route: Task list for a recipe
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
@@ -127,17 +233,7 @@ def select_recipe():
@role_required("MeasurementTec") @role_required("MeasurementTec")
def task_list(recipe_id: int): def task_list(recipe_id: int):
"""Task list for selected recipe.""" """Task list for selected recipe."""
# Persist lot/serial from query params into session lot_number, serial_number = _remember_traceability()
lot_number = request.args.get(
"lot_number", session.get("lot_number", ""),
)
serial_number = request.args.get(
"serial_number", session.get("serial_number", ""),
)
if lot_number:
session["lot_number"] = lot_number
if serial_number:
session["serial_number"] = serial_number
# Load recipe details # Load recipe details
recipe_resp = api_client.get(f"/api/recipes/{recipe_id}") recipe_resp = api_client.get(f"/api/recipes/{recipe_id}")
@@ -149,22 +245,19 @@ def task_list(recipe_id: int):
) )
return redirect(url_for("measure.select_recipe")) return redirect(url_for("measure.select_recipe"))
# Load tasks for this recipe tasks = _ordered_tasks(recipe_id)
tasks_resp = api_client.get(f"/api/recipes/{recipe_id}/tasks") if tasks is None:
if isinstance(tasks_resp, dict) and tasks_resp.get("error"): flash(_("Errore nel caricamento dei task."), "error")
flash(
_("Errore nel caricamento dei task: %(detail)s",
detail=tasks_resp.get("detail", "")),
"error",
)
tasks = [] tasks = []
else:
tasks = tasks_resp if isinstance(tasks_resp, list) else tasks_resp.get("items", []) version = recipe_resp.get("current_version") or {}
progress = _task_progress(version.get("id"), _current_run_id())
return render_template( return render_template(
"measure/task_list.html", "measure/task_list.html",
recipe=recipe_resp, recipe=recipe_resp,
tasks=tasks, tasks=tasks,
progress=progress,
lot_number=lot_number, lot_number=lot_number,
serial_number=serial_number, serial_number=serial_number,
) )
@@ -31,8 +31,11 @@
if (recipe.requires_serial && !this.serial_number.trim()) missing.push('{{ _('seriale') }}'); if (recipe.requires_serial && !this.serial_number.trim()) missing.push('{{ _('seriale') }}');
return missing; return missing;
}, },
/* Straight into the first task. Choosing a recipe used to open the full
task list, which asked the operator to decide where to begin before they
had done anything; the list is still one tap away from inside the task. */
buildTaskUrl(recipeId) { buildTaskUrl(recipeId) {
let url = '/measure/tasks/' + recipeId + '?'; let url = '/measure/start/' + recipeId + '?';
const params = []; const params = [];
if (this.lot_number) params.push('lot_number=' + encodeURIComponent(this.lot_number)); if (this.lot_number) params.push('lot_number=' + encodeURIComponent(this.lot_number));
if (this.serial_number) params.push('serial_number=' + encodeURIComponent(this.serial_number)); if (this.serial_number) params.push('serial_number=' + encodeURIComponent(this.serial_number));
@@ -285,13 +288,18 @@
: 'group-hover:shadow-md'" : 'group-hover:shadow-md'"
class="btn btn-primary w-full justify-center text-sm font-semibold class="btn btn-primary w-full justify-center text-sm font-semibold
transition-shadow duration-200"> transition-shadow duration-200">
<svg class="w-4 h-4" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"> <svg class="w-4 h-4" fill="none" stroke="currentColor" stroke-width="2.5" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"/> <path stroke-linecap="round" stroke-linejoin="round" d="M5 3l14 9-14 9V3z"/>
</svg>
{{ _('Seleziona') }}
<svg class="w-4 h-4 transition-transform group-hover:translate-x-0.5" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" d="M9 5l7 7-7 7"/>
</svg> </svg>
{{ _('AVVIA IN SEQUENZA') }}
</a>
{# The whole list, for when the operator wants to see it or go back to a
task left unfinished. Deliberately the quiet one of the two. #}
<a :href="'/measure/tasks/' + recipe.id"
class="mt-2 block text-center text-xs text-[var(--text-secondary)]
hover:text-primary transition-colors">
{{ _('Vedi tutti i task') }}
</a> </a>
</div> </div>
</div> </div>
@@ -96,7 +96,16 @@
</h1> </h1>
</div> </div>
{# Lista task + Riepilogo buttons #} {#
Lista task + Completato.
The list is the second level now: the operator arrives inside the sequence
and comes here when they want the whole of it. What used to sit beside it
was «Riepilogo», which jumped out of the sequence to a recap - the wrong
thing to offer someone halfway through a task. The way forward is
«Completato», and on a measurement task it stays shut until the cycle is
closed, which is the same rule the footer enforces.
#}
<div class="shrink-0 flex items-center gap-1.5"> <div class="shrink-0 flex items-center gap-1.5">
<a href="{{ url_for('measure.task_list', recipe_id=task.recipe_id or 0) }}" <a href="{{ url_for('measure.task_list', recipe_id=task.recipe_id or 0) }}"
class="btn btn-secondary text-xs py-1 px-2.5 gap-1"> class="btn btn-secondary text-xs py-1 px-2.5 gap-1">
@@ -105,12 +114,15 @@
</svg> </svg>
{{ _('Lista task') }} {{ _('Lista task') }}
</a> </a>
<button @click="goToSummary()" <button @click="goToNextTask()"
:disabled="!canLeaveTask"
:title="canLeaveTask ? '' : blockedReason"
:class="canLeaveTask ? '' : 'opacity-40 cursor-not-allowed'"
class="btn btn-secondary text-xs py-1 px-2.5 gap-1"> class="btn btn-secondary text-xs py-1 px-2.5 gap-1">
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"> <svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" d="M9 17v-2m3 2v-4m3 4v-6m2 10H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/> <path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7"/>
</svg> </svg>
{{ _('Riepilogo') }} {{ _('Completato') }}
</button> </button>
</div> </div>
@@ -630,18 +642,30 @@
x-text="Math.round(progressPercent) + '%'"></span> x-text="Math.round(progressPercent) + '%'"></span>
</div> </div>
{# Right: Fine ciclo misura (measurement tasks) / Completato (non-measurement or after cycle) #} {# Right: Fine ciclo misura (measurement tasks) / Task successivo (after cycle) #}
<template x-if="isMeasureTask && subtasks.length > 0"> <template x-if="isMeasureTask && subtasks.length > 0">
<div class="flex items-center gap-1.5"> <div class="flex items-center gap-1.5">
<button x-show="isComplete && !cycleConfirmed" {#
Shown from the start, and greyed until every quote has a value. It used
to appear only once the task was finished, which left the operator with
no sign that the button existed or what would bring it out - a control
that is missing teaches nothing, one that is disabled says what is left.
#}
<button x-show="!cycleConfirmed"
x-transition x-transition
@click="confirmCycle()" @click="confirmCycle()"
class="btn text-xs shrink-0 gap-1 py-1.5 px-3 shadow-md :disabled="!isComplete"
bg-primary text-white hover:bg-primary-700"> :title="isComplete ? '' : missingQuotesLabel"
:class="isComplete
? 'bg-primary text-white hover:bg-primary-700 shadow-md'
: 'bg-steel-200 dark:bg-steel-700 text-steel-500 dark:text-steel-400 cursor-not-allowed'"
class="btn text-xs shrink-0 gap-1 py-1.5 px-3">
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"> <svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/> <path stroke-linecap="round" stroke-linejoin="round" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/>
</svg> </svg>
{{ _('Fine ciclo misura') }} <span x-text="isComplete
? {{ _('Fine ciclo misura')|tojson_attr }}
: missingQuotesLabel"></span>
</button> </button>
{# Girare il pezzo e rimisurare, senza chiudere il ciclo: a second reading {# Girare il pezzo e rimisurare, senza chiudere il ciclo: a second reading
of the same part is not a new one, and must not restart the interval. #} of the same part is not a new one, and must not restart the interval. #}
@@ -654,6 +678,9 @@
</svg> </svg>
{{ _('Rimisura') }} {{ _('Rimisura') }}
</button> </button>
{# The cycle is closed; this only walks on. «Completato» is the header's
word for finishing a task, and two buttons wearing it would be one too
many. #}
<button x-show="cycleConfirmed" <button x-show="cycleConfirmed"
x-transition x-transition
@click="goToNextTask()" @click="goToNextTask()"
@@ -662,7 +689,7 @@
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"> <svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7"/> <path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7"/>
</svg> </svg>
{{ _('Completato') }} {{ _('Task successivo') }}
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"> <svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" d="M14 5l7 7m0 0l-7 7m7-7H3"/> <path stroke-linecap="round" stroke-linejoin="round" d="M14 5l7 7m0 0l-7 7m7-7H3"/>
</svg> </svg>
@@ -988,6 +1015,40 @@ function taskExecute() {
: 0; : 0;
}, },
/* How many quotes are still owed, worded rather than counted down in silence.
This is what the greyed «Fine ciclo misura» says instead of nothing. */
get missingQuotes() {
return Math.max(0, this.totalSubtasks - this.completedCount);
},
get missingQuotesLabel() {
const n = this.missingQuotes;
return n === 1
? {{ _('Manca 1 quota')|tojson }}
: {{ _('Mancano')|tojson }} + ' ' + n + ' ' + {{ _('quote')|tojson }};
},
/* Whether «Completato» can take the operator on.
A documental task is done when they say it is. A measurement task is done
when its cycle is closed - otherwise the button would be a way round «Fine
ciclo misura», and the quotes it exists to collect would be optional. */
get canLeaveTask() {
if (this.blockedByTolerance) return false;
if (this.isMeasureTask && this.subtasks.length > 0) {
return this.cycleConfirmed;
}
return true;
},
get blockedReason() {
if (this.blockedByTolerance) {
return {{ _("Serve l'autorizzazione del capoturno")|tojson }};
}
if (!this.isComplete) return this.missingQuotesLabel;
return {{ _('Chiudi prima il ciclo di misura')|tojson }};
},
// ---- Pass/fail logic ---- // ---- Pass/fail logic ----
get passFailStatus() { get passFailStatus() {
if (this.currentValue === null || !this.currentSubtask) return null; if (this.currentValue === null || !this.currentSubtask) return null;
@@ -97,14 +97,14 @@
{{ _('Compila prima:') }} {{ missing_trace|join(', ') }} {{ _('Compila prima:') }} {{ missing_trace|join(', ') }}
</a> </a>
{% else %} {% else %}
<a href="{{ url_for('measure.task_execute', task_id=tasks[0].id) }}" <a href="{{ url_for('measure.start_sequence', recipe_id=recipe.id) }}"
class="btn gap-3 w-full sm:w-auto justify-center text-lg font-bold class="btn gap-3 w-full sm:w-auto justify-center text-lg font-bold
bg-red-600 hover:bg-red-700 text-white shadow-lg hover:shadow-xl bg-red-600 hover:bg-red-700 text-white shadow-lg hover:shadow-xl
px-8 py-3 rounded-xl transition-all duration-200"> px-8 py-3 rounded-xl transition-all duration-200">
<svg class="w-6 h-6" fill="none" stroke="currentColor" stroke-width="2.5" viewBox="0 0 24 24"> <svg class="w-6 h-6" fill="none" stroke="currentColor" stroke-width="2.5" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" d="M5 3l14 9-14 9V3z"/> <path stroke-linecap="round" stroke-linejoin="round" d="M5 3l14 9-14 9V3z"/>
</svg> </svg>
{{ _('AVVIA') }} {{ _('AVVIA IN SEQUENZA') }}
</a> </a>
{% endif %} {% endif %}
{% endif %} {% endif %}
@@ -162,7 +162,12 @@
{% if tasks %} {% if tasks %}
<div class="space-y-2.5"> <div class="space-y-2.5">
{% for task in tasks %} {% for task in tasks %}
<div class="tmf-card hover:border-primary/30 transition-all duration-200 group"> {# How far this task got. A task walked away from halfway through used to look
exactly like one never opened; this is what tells them apart. #}
{% set prog = (progress|default({})).get(task.id) %}
<div class="tmf-card transition-all duration-200 group
{% if prog and prog.state == 'partial' %}border-amber-300 dark:border-amber-700
{% else %}hover:border-primary/30{% endif %}">
<div class="p-3 sm:p-4"> <div class="p-3 sm:p-4">
<div class="flex flex-row items-center gap-3"> <div class="flex flex-row items-center gap-3">
@@ -208,7 +213,27 @@
</span> </span>
{% endif %} {% endif %}
{% if task.subtask_count is defined or task.subtasks %} {% if prog and prog.state in ('partial', 'complete') %}
<span class="inline-flex items-center gap-1 px-1.5 py-0.5 rounded text-[11px] font-semibold
{% if prog.state == 'complete' %}
bg-measure-pass/10 text-measure-pass
{% else %}
bg-amber-100 dark:bg-amber-900/30 text-amber-700 dark:text-amber-300
{% endif %}">
{% if prog.state == 'complete' %}
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" stroke-width="2.5" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7"/>
</svg>
{{ _('Completato') }}
{% else %}
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" stroke-width="2.5" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 8v4l2.5 2.5M12 21a9 9 0 110-18 9 9 0 010 18z"/>
</svg>
{{ _('Incompiuto') }}
{% endif %}
<span class="font-mono">{{ prog.measured }}/{{ prog.quotes }}</span>
</span>
{% elif task.subtask_count is defined or task.subtasks %}
<span class="inline-flex items-center gap-1 text-xs text-[var(--text-secondary)]" <span class="inline-flex items-center gap-1 text-xs text-[var(--text-secondary)]"
title="{{ _('misurazioni') }}"> title="{{ _('misurazioni') }}">
<svg class="w-4 h-4" fill="none" stroke="currentColor" stroke-width="1.75" viewBox="0 0 24 24"> <svg class="w-4 h-4" fill="none" stroke="currentColor" stroke-width="1.75" viewBox="0 0 24 24">
@@ -233,23 +258,23 @@
{% endif %} {% endif %}
</div> </div>
<!-- Action Button (Maker only — operators start the guided flow with AVVIA) --> {#
{% if current_user and 'Maker' in current_user.get('roles', []) %} Opening one task on its own. The sequence is the normal way through the
recipe - this is the way back to a task left unfinished, which is why the
operator has it now and not only the Maker: a list that says "incompiuto"
and gives you no way there would be a complaint, not a feature.
#}
<div class="shrink-0 sm:ml-4"> <div class="shrink-0 sm:ml-4">
<a href="{{ url_for('measure.task_execute', task_id=task.id) }}" <a href="{{ url_for('measure.task_execute', task_id=task.id) }}"
class="btn btn-primary gap-2 w-full sm:w-auto justify-center class="btn btn-secondary gap-2 w-full sm:w-auto justify-center
group-hover:shadow-md transition-shadow duration-200"> group-hover:shadow-md transition-shadow duration-200">
<svg class="w-4 h-4" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"> <svg class="w-4 h-4" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" d="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z"/> <path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/>
<path stroke-linecap="round" stroke-linejoin="round" d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/> <path stroke-linecap="round" stroke-linejoin="round" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/>
</svg>
{{ _('Visualizza Task') }}
<svg class="w-4 h-4 transition-transform group-hover:translate-x-0.5" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" d="M9 5l7 7-7 7"/>
</svg> </svg>
{{ _('Visualizza singolo TASK') }}
</a> </a>
</div> </div>
{% endif %}
</div> </div>
</div> </div>
</div> </div>
+21
View File
@@ -73,6 +73,27 @@ def logged_in_client(flask_app):
yield test_client yield test_client
def api_get_router(routes: dict, default=None):
"""A ``get`` side effect that answers by endpoint instead of by turn.
A list of responses in call order breaks the moment a page asks the server one
more question - which is how every page grows. Matching on the endpoint says
what a test actually means: this is what /api/recipes/3 replies.
Keys are matched as substrings of the endpoint, longest first, so
``/api/recipes/3/tasks`` wins over ``/api/recipes/3``.
"""
ordered = sorted(routes.items(), key=lambda kv: -len(kv[0]))
def _get(endpoint, *args, **kwargs):
for prefix, response in ordered:
if prefix in endpoint:
return response() if callable(response) else response
return {} if default is None else default
return _get
@pytest.fixture @pytest.fixture
def mock_api_client(): def mock_api_client():
"""Patch the api_client singleton used in blueprints. """Patch the api_client singleton used in blueprints.
+116 -7
View File
@@ -4,6 +4,8 @@ Covers recipe selection, task list, login requirement, and measurement submissio
""" """
import pytest import pytest
from tests.conftest import api_get_router
class TestSelectRecipe: class TestSelectRecipe:
"""GET /measure/select tests.""" """GET /measure/select tests."""
@@ -36,22 +38,129 @@ class TestTaskList:
def test_task_list_renders(self, logged_in_client, mock_api_client): def test_task_list_renders(self, logged_in_client, mock_api_client):
"""Task list page renders with recipe and task data.""" """Task list page renders with recipe and task data."""
# First call: recipe details (dict), second call: tasks list. mock_api_client.get.side_effect = api_get_router({
# The route calls tasks_resp.get("error") so the mock must return "/api/recipes/1/tasks": {
# a dict (not a bare list) to avoid AttributeError.
mock_api_client.get.side_effect = [
{"id": 1, "code": "REC-001", "name": "Test Recipe"},
{
"items": [ "items": [
{"id": 1, "title": "Task 1", "order_index": 0}, {"id": 1, "title": "Task 1", "order_index": 0},
{"id": 2, "title": "Task 2", "order_index": 1}, {"id": 2, "title": "Task 2", "order_index": 1},
], ],
}, },
] "/api/recipes/1": {"id": 1, "code": "REC-001", "name": "Test Recipe"},
"/api/production-runs/current": None,
})
resp = logged_in_client.get("/measure/tasks/1") resp = logged_in_client.get("/measure/tasks/1")
assert resp.status_code == 200 assert resp.status_code == 200
def test_task_list_marks_the_task_left_half_done(
self, logged_in_client, mock_api_client,
):
"""A task with some of its quotes taken says so, and says how many."""
with logged_in_client.session_transaction() as sess:
sess["language"] = "it"
mock_api_client.get.side_effect = api_get_router({
"/api/recipes/1/tasks": [
{"id": 7, "title": "Misura", "order_index": 0, "task_type": "measure"},
{"id": 8, "title": "Nota", "order_index": 1, "task_type": "note"},
],
"/api/recipes/1": {
"id": 1, "code": "REC-001", "name": "Test Recipe",
"current_version": {"id": 42, "version_number": 1},
},
"/api/production-runs/current": None,
"/api/measurements/task-progress": {
"tasks": [
{"task_id": 7, "quotes": 3, "measured": 1, "state": "partial"},
],
},
})
resp = logged_in_client.get("/measure/tasks/1")
assert resp.status_code == 200
html = resp.get_data(as_text=True)
assert "Incompiuto" in html
assert "1/3" in html
def test_task_list_survives_a_progress_lookup_that_fails(
self, logged_in_client, mock_api_client,
):
"""Losing the badges must not lose the page."""
mock_api_client.get.side_effect = api_get_router({
"/api/recipes/1/tasks": [
{"id": 7, "title": "Misura", "order_index": 0, "task_type": "measure"},
],
"/api/recipes/1": {
"id": 1, "code": "REC-001", "name": "Test Recipe",
"current_version": {"id": 42},
},
"/api/production-runs/current": None,
"/api/measurements/task-progress": {
"error": True, "status_code": 500, "detail": "boom",
},
})
resp = logged_in_client.get("/measure/tasks/1")
assert resp.status_code == 200
assert "Misura" in resp.get_data(as_text=True)
class TestStartSequence:
"""GET /measure/start/<recipe_id> — the operator's way in."""
def test_start_goes_to_the_first_task(self, logged_in_client, mock_api_client):
mock_api_client.get.side_effect = api_get_router({
"/api/recipes/1/tasks": [
{"id": 31, "title": "Nota", "order_index": 1},
{"id": 30, "title": "Prima", "order_index": 0},
],
"/api/recipes/1": {"id": 1, "code": "REC-001", "name": "Test Recipe"},
})
resp = logged_in_client.get("/measure/start/1?lot_number=L1")
assert resp.status_code == 302
assert resp.headers["Location"].endswith("/measure/execute/30")
def test_start_remembers_the_traceability(self, logged_in_client, mock_api_client):
"""The list used to be where lot and serial were stored; the sequence
skips it, so the storing has to happen on the way in."""
mock_api_client.get.side_effect = api_get_router({
"/api/recipes/1/tasks": [{"id": 30, "title": "Prima", "order_index": 0}],
"/api/recipes/1": {"id": 1, "code": "REC-001", "name": "Test Recipe"},
})
logged_in_client.get("/measure/start/1?lot_number=L9&serial_number=S9")
with logged_in_client.session_transaction() as sess:
assert sess["lot_number"] == "L9"
assert sess["serial_number"] == "S9"
def test_start_refuses_without_the_compulsory_lot(
self, logged_in_client, mock_api_client,
):
"""Point 8 holds on this door too: it is a way into the measurement."""
mock_api_client.get.side_effect = api_get_router({
"/api/recipes/1/tasks": [{"id": 30, "title": "Prima", "order_index": 0}],
"/api/recipes/1": {
"id": 1, "code": "REC-001", "name": "Test Recipe",
"requires_lot": True,
},
})
resp = logged_in_client.get("/measure/start/1")
assert resp.status_code == 302
assert "/measure/select" in resp.headers["Location"]
def test_start_without_tasks_falls_back_to_the_list(
self, logged_in_client, mock_api_client,
):
mock_api_client.get.side_effect = api_get_router({
"/api/recipes/1/tasks": [],
"/api/recipes/1": {"id": 1, "code": "REC-001", "name": "Test Recipe"},
})
resp = logged_in_client.get("/measure/start/1")
assert resp.status_code == 302
assert resp.headers["Location"].endswith("/measure/tasks/1")
class TestTaskComplete: class TestTaskComplete:
"""GET /measure/complete/<recipe_id> tests.""" """GET /measure/complete/<recipe_id> tests."""
@@ -0,0 +1,192 @@
"""Point 10: the operator's way through a recipe.
The sequence is the normal path and the list is a place you go on purpose. What
these tests hold on to is the part that is easy to undo by accident: the way in
lands on a task, the list says which task was left unfinished and lets the
operator back into it, «Fine ciclo misura» is visible before it is usable, and
nothing in any of it opens a door to the statistics (point 15).
"""
from tests.conftest import api_get_router
TASK = {
"id": 11,
"recipe_id": 3,
"version_id": 5,
"task_type": "measure",
"title": "Quota",
"directive": None,
"description": None,
"file_path": None,
"file_type": None,
"annotations_json": None,
"subtasks": [{
"id": 21, "marker_number": 1, "description": "Altezza", "order_index": 0,
"nominal": 10.0, "utl": 10.5, "uwl": 10.2, "lwl": 9.8, "ltl": 9.5,
"unit": "mm",
}],
}
RECIPE = {
"id": 3, "code": "REC-1", "name": "Ricetta",
"measurement_interval_minutes": 30, "allow_manual_input": True,
"current_version": {"id": 5, "version_number": 1},
}
def _italian(client):
with client.session_transaction() as sess:
sess["language"] = "it"
def _operator_only(client):
"""Strip the Maker role: this is what the shop floor account looks like."""
with client.session_transaction() as sess:
user = dict(sess["user"])
user["roles"] = ["MeasurementTec"]
user["is_admin"] = False
sess["user"] = user
def _execute_page(client, mock_api_client):
_italian(client)
mock_api_client.get.side_effect = api_get_router({
"/api/tasks/11": TASK,
"/api/recipes/3/tasks": [{"id": 11, "order_index": 0}],
"/api/recipes/3": RECIPE,
})
resp = client.get("/measure/execute/11")
assert resp.status_code == 200
return resp.get_data(as_text=True)
def _list_page(client, mock_api_client, progress=None):
_italian(client)
mock_api_client.get.side_effect = api_get_router({
"/api/recipes/3/tasks": [
{"id": 11, "title": "Misura", "order_index": 0, "task_type": "measure"},
],
"/api/recipes/3": RECIPE,
"/api/production-runs/current": None,
"/api/measurements/task-progress": {"tasks": progress or []},
})
resp = client.get("/measure/tasks/3")
assert resp.status_code == 200
return resp.get_data(as_text=True)
# ---------------------------------------------------------------------------
# The way in: a recipe opens on a task, not on a list
# ---------------------------------------------------------------------------
def test_the_selection_screen_starts_the_sequence(
logged_in_client, mock_api_client, monkeypatch,
):
_italian(logged_in_client)
# The page refuses to render without a station, which is the right answer on a
# real terminal and only an obstacle here.
monkeypatch.setattr("blueprints.measure.Config.STATION_CODE", "ST-TEST")
mock_api_client.get_station_recipes.return_value = []
resp = logged_in_client.get("/measure/select")
assert resp.status_code == 200
html = resp.get_data(as_text=True)
assert "'/measure/start/'" in html
# The full list is still reachable, deliberately as the quiet option.
assert "'/measure/tasks/'" in html
def test_avvia_on_the_list_starts_the_sequence(logged_in_client, mock_api_client):
html = _list_page(logged_in_client, mock_api_client)
assert "/measure/start/3" in html
assert "AVVIA IN SEQUENZA" in html
# ---------------------------------------------------------------------------
# The list: second level, and it says what was left unfinished
# ---------------------------------------------------------------------------
def test_the_operator_can_open_a_single_task(logged_in_client, mock_api_client):
"""Without this, «incompiuto» would be a label with no way to act on it."""
_operator_only(logged_in_client)
html = _list_page(logged_in_client, mock_api_client)
assert "Visualizza singolo TASK" in html
assert "/measure/execute/11" in html
def test_a_half_measured_task_is_marked(logged_in_client, mock_api_client):
html = _list_page(
logged_in_client, mock_api_client,
progress=[{"task_id": 11, "quotes": 3, "measured": 2, "state": "partial"}],
)
assert "Incompiuto" in html
assert "2/3" in html
def test_a_finished_task_is_marked(logged_in_client, mock_api_client):
html = _list_page(
logged_in_client, mock_api_client,
progress=[{"task_id": 11, "quotes": 3, "measured": 3, "state": "complete"}],
)
assert "Completato" in html
assert "3/3" in html
def test_an_untouched_task_says_nothing(logged_in_client, mock_api_client):
"""A list that shouts at every row is a list nobody reads."""
html = _list_page(
logged_in_client, mock_api_client,
progress=[{"task_id": 11, "quotes": 3, "measured": 0, "state": "none"}],
)
assert "Incompiuto" not in html
# ---------------------------------------------------------------------------
# Inside the task: «Completato» forward, «Fine ciclo misura» visible but shut
# ---------------------------------------------------------------------------
def test_the_header_offers_completato_not_the_summary(
logged_in_client, mock_api_client,
):
html = _execute_page(logged_in_client, mock_api_client)
assert 'x-data="taskExecute()"' in html
# The header control moves the operator on and is gated by the same rule as
# the footer, rather than jumping out of the sequence to a recap.
assert 'canLeaveTask' in html
assert '@click="goToNextTask()"' in html
def test_fine_ciclo_is_shown_before_it_can_be_used(
logged_in_client, mock_api_client,
):
"""It used to appear only once the task was already finished."""
html = _execute_page(logged_in_client, mock_api_client)
assert 'x-show="!cycleConfirmed"' in html
assert ':disabled="!isComplete"' in html
assert "Fine ciclo misura" in html
def test_the_missing_quotes_are_counted_out_loud(
logged_in_client, mock_api_client,
):
html = _execute_page(logged_in_client, mock_api_client)
assert "missingQuotesLabel" in html
assert "Mancano" in html
# ---------------------------------------------------------------------------
# Point 15: none of this may open a way to the statistics
# ---------------------------------------------------------------------------
def test_the_operator_flow_never_links_to_the_statistics(
logged_in_client, mock_api_client,
):
_operator_only(logged_in_client)
pages = [
_list_page(logged_in_client, mock_api_client),
_execute_page(logged_in_client, mock_api_client),
]
for html in pages:
assert "/statistics" not in html
@@ -24,6 +24,8 @@ from unittest.mock import MagicMock, patch
import pytest import pytest
from tests.conftest import api_get_router
# Match every inline <script>…</script> (i.e. without `src=`) so we don't try # Match every inline <script>…</script> (i.e. without `src=`) so we don't try
# to syntax-check Alpine.js / Plotly bundles served from a CDN. # to syntax-check Alpine.js / Plotly bundles served from a CDN.
@@ -307,13 +309,20 @@ def test_task_list_inline_js_is_valid(logged_in_client, mock_measure_api):
with it, and the operator would simply never be called back. with it, and the operator would simply never be called back.
""" """
_force_italian(logged_in_client) _force_italian(logged_in_client)
mock_measure_api.get.side_effect = [ mock_measure_api.get.side_effect = api_get_router({
{"id": 3, "code": "REC-3", "name": "Ricetta", "description": None}, "/api/recipes/3/tasks": [{
[{
"id": 11, "order_index": 0, "title": "Quota d'ingresso", "id": 11, "order_index": 0, "title": "Quota d'ingresso",
"task_type": "measure", "subtasks": [], "file_path": None, "task_type": "measure", "subtasks": [], "file_path": None,
}], }],
] "/api/recipes/3": {
"id": 3, "code": "REC-3", "name": "Ricetta", "description": None,
"current_version": {"id": 5},
},
"/api/production-runs/current": None,
"/api/measurements/task-progress": {
"tasks": [{"task_id": 11, "quotes": 2, "measured": 1, "state": "partial"}],
},
})
resp = logged_in_client.get("/measure/tasks/3") resp = logged_in_client.get("/measure/tasks/3")
assert resp.status_code == 200 assert resp.status_code == 200
@@ -3,7 +3,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: TieMeasureFlow 1.0\n" "Project-Id-Version: TieMeasureFlow 1.0\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2026-07-28 20:48+0000\n" "POT-Creation-Date: 2026-07-28 21:28+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: en\n" "Language: en\n"
@@ -129,43 +129,64 @@ msgstr "Current station: %(code)s"
msgid "Errore nel caricamento delle ricette: %(detail)s" msgid "Errore nel caricamento delle ricette: %(detail)s"
msgstr "Error loading recipes: %(detail)s" msgstr "Error loading recipes: %(detail)s"
#: blueprints/measure.py:146 blueprints/measure.py:237 # Measure - Task List
#: blueprints/measure.py:139 templates/measure/select_recipe.html:30
#: templates/measure/task_list.html:86
msgid "lotto"
msgstr "lot"
#: blueprints/measure.py:141 templates/measure/select_recipe.html:31
#: templates/measure/task_list.html:87
msgid "seriale"
msgstr "serial"
#: blueprints/measure.py:204 blueprints/measure.py:242
#: blueprints/measure.py:330
#, python-format #, python-format
msgid "Ricetta non trovata: %(detail)s" msgid "Ricetta non trovata: %(detail)s"
msgstr "Recipe not found: %(detail)s" msgstr "Recipe not found: %(detail)s"
#: blueprints/measure.py:156 #: blueprints/measure.py:213
#, python-format #, python-format
msgid "Errore nel caricamento dei task: %(detail)s" msgid "Compila prima: %(fields)s"
msgstr "Error loading tasks: %(detail)s" msgstr "Fill in first: %(fields)s"
#: blueprints/measure.py:185 #: blueprints/measure.py:222 templates/measure/task_list.html:297
msgid "Questa ricetta non ha ancora task definiti."
msgstr "This recipe has no tasks defined yet."
#: blueprints/measure.py:250
msgid "Errore nel caricamento dei task."
msgstr "Error loading tasks."
#: blueprints/measure.py:278
#, python-format #, python-format
msgid "Task non trovato: %(detail)s" msgid "Task non trovato: %(detail)s"
msgstr "Task not found: %(detail)s" msgstr "Task not found: %(detail)s"
#: blueprints/measure.py:306 #: blueprints/measure.py:399
msgid "Codice non fornito" msgid "Codice non fornito"
msgstr "Code not provided" msgstr "Code not provided"
#: blueprints/measure.py:312 templates/measure/select_recipe.html:57 #: blueprints/measure.py:405 templates/measure/select_recipe.html:60
msgid "Ricetta non trovata" msgid "Ricetta non trovata"
msgstr "Recipe not found" msgstr "Recipe not found"
#: blueprints/measure.py:368 #: blueprints/measure.py:461
msgid "Dati mancanti: subtask_id, version_id e value sono obbligatori" msgid "Dati mancanti: subtask_id, version_id e value sono obbligatori"
msgstr "Missing data: subtask_id, version_id and value are required" msgstr "Missing data: subtask_id, version_id and value are required"
#: blueprints/measure.py:393 templates/admin/stations.html:549 #: blueprints/measure.py:486 templates/admin/stations.html:549
#: templates/maker/recipe_editor.html:598 #: templates/maker/recipe_editor.html:598
msgid "Errore nel salvataggio" msgid "Errore nel salvataggio"
msgstr "Error saving" msgstr "Error saving"
#: blueprints/measure.py:437 blueprints/measure.py:542 #: blueprints/measure.py:530 blueprints/measure.py:635
msgid "Username e password richiesti" msgid "Username e password richiesti"
msgstr "Username and password required" msgstr "Username and password required"
#: blueprints/measure.py:463 blueprints/measure.py:480 #: blueprints/measure.py:556 blueprints/measure.py:573
#: templates/errors/station_not_configured.html:2 #: templates/errors/station_not_configured.html:2
#: templates/errors/station_not_configured.html:16 #: templates/errors/station_not_configured.html:16
msgid "Stazione non configurata" msgid "Stazione non configurata"
@@ -371,8 +392,8 @@ msgstr "Optional notes"
#: templates/maker/task_editor.html:312 templates/maker/task_editor.html:421 #: templates/maker/task_editor.html:312 templates/maker/task_editor.html:421
#: templates/maker/task_editor.html:782 templates/maker/task_editor.html:901 #: templates/maker/task_editor.html:782 templates/maker/task_editor.html:901
#: templates/maker/task_editor.html:966 templates/maker/task_editor.html:1071 #: templates/maker/task_editor.html:966 templates/maker/task_editor.html:1071
#: templates/measure/select_recipe.html:405 #: templates/measure/select_recipe.html:413
#: templates/measure/task_execute.html:860 #: templates/measure/task_execute.html:887
msgid "Annulla" msgid "Annulla"
msgstr "Cancel" msgstr "Cancel"
@@ -388,7 +409,7 @@ msgstr "Create Station"
#: templates/admin/stations.html:241 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:107
#: templates/maker/recipe_editor.html:490 templates/maker/task_drawing.html:61 #: templates/maker/recipe_editor.html:490 templates/maker/task_drawing.html:61
#: templates/measure/task_execute.html:412 #: templates/measure/task_execute.html:424
msgid "Salvataggio..." msgid "Salvataggio..."
msgstr "Saving..." msgstr "Saving..."
@@ -517,7 +538,7 @@ msgstr "New User"
#: templates/admin/users.html:48 templates/admin/users.html:173 #: templates/admin/users.html:48 templates/admin/users.html:173
#: templates/admin/users.html:179 templates/auth/login.html:35 #: templates/admin/users.html:179 templates/auth/login.html:35
#: templates/auth/login.html:49 templates/auth/profile.html:36 #: templates/auth/login.html:49 templates/auth/profile.html:36
#: templates/measure/task_execute.html:842 #: templates/measure/task_execute.html:869
msgid "Username" msgid "Username"
msgstr "Username" msgstr "Username"
@@ -569,7 +590,7 @@ msgstr "Username cannot be changed"
#: templates/admin/users.html:206 templates/admin/users.html:214 #: templates/admin/users.html:206 templates/admin/users.html:214
#: templates/auth/login.html:57 templates/auth/login.html:71 #: templates/auth/login.html:57 templates/auth/login.html:71
#: templates/measure/task_execute.html:847 #: templates/measure/task_execute.html:874
msgid "Password" msgid "Password"
msgstr "Password" msgstr "Password"
@@ -724,8 +745,8 @@ msgstr "Logout"
#: templates/components/barcode_scanner.html:21 #: templates/components/barcode_scanner.html:21
#: templates/components/barcode_scanner.html:54 #: templates/components/barcode_scanner.html:54
#: templates/measure/select_recipe.html:128 #: templates/measure/select_recipe.html:131
#: templates/measure/select_recipe.html:357 #: templates/measure/select_recipe.html:365
msgid "Scansiona Barcode" msgid "Scansiona Barcode"
msgstr "Scan Barcode" msgstr "Scan Barcode"
@@ -824,14 +845,14 @@ msgid "Misura solo da calibro: questa ricetta non ammette valori digitati"
msgstr "Caliper only: this recipe does not accept typed values" msgstr "Caliper only: this recipe does not accept typed values"
#: templates/components/production_clock.html:42 #: templates/components/production_clock.html:42
#: templates/measure/task_execute.html:460 #: templates/measure/task_execute.html:472
msgid "Prossima misurazione tra" msgid "Prossima misurazione tra"
msgstr "Next measurement in" msgstr "Next measurement in"
#: templates/components/production_clock.html:49 #: templates/components/production_clock.html:49
#: templates/components/production_clock.html:62 #: templates/components/production_clock.html:62
#: templates/measure/task_execute.html:463 #: templates/measure/task_execute.html:475
#: templates/measure/task_execute.html:542 #: templates/measure/task_execute.html:554
msgid "Misurazione in ritardo di" msgid "Misurazione in ritardo di"
msgstr "Measurement overdue by" msgstr "Measurement overdue by"
@@ -840,27 +861,27 @@ msgid "Linea ferma — conto alla rovescia congelato a"
msgstr "Line stopped — countdown frozen at" msgstr "Line stopped — countdown frozen at"
#: templates/components/production_clock.html:62 #: templates/components/production_clock.html:62
#: templates/measure/task_execute.html:536 #: templates/measure/task_execute.html:548
msgid "Linea ferma" msgid "Linea ferma"
msgstr "Line stopped" msgstr "Line stopped"
#: templates/components/production_clock.html:74 #: templates/components/production_clock.html:74
#: templates/measure/task_execute.html:472 #: templates/measure/task_execute.html:484
msgid "Ciclo" msgid "Ciclo"
msgstr "Cycle" msgstr "Cycle"
#: templates/components/production_clock.html:92 #: templates/components/production_clock.html:92
#: templates/measure/task_execute.html:704 #: templates/measure/task_execute.html:731
msgid "È ora di misurare" msgid "È ora di misurare"
msgstr "Time to measure" msgstr "Time to measure"
#: templates/components/production_clock.html:95 #: templates/components/production_clock.html:95
#: templates/measure/task_execute.html:706 #: templates/measure/task_execute.html:733
msgid "Ritorno alla misurazione tra" msgid "Ritorno alla misurazione tra"
msgstr "Returning to the measurement in" msgstr "Returning to the measurement in"
#: templates/components/production_clock.html:101 #: templates/components/production_clock.html:101
#: templates/measure/task_execute.html:711 #: templates/measure/task_execute.html:738
msgid "Vai alla misura" msgid "Vai alla misura"
msgstr "Go to the measurement" msgstr "Go to the measurement"
@@ -913,8 +934,8 @@ msgstr "Preview"
#: templates/maker/recipe_editor.html:128 templates/maker/recipe_list.html:246 #: templates/maker/recipe_editor.html:128 templates/maker/recipe_list.html:246
#: templates/maker/task_drawing.html:106 templates/maker/task_editor.html:130 #: templates/maker/task_drawing.html:106 templates/maker/task_editor.html:130
#: templates/measure/task_complete.html:171 #: templates/measure/task_complete.html:171
#: templates/measure/task_execute.html:615 templates/measure/task_list.html:2 #: templates/measure/task_execute.html:627 templates/measure/task_list.html:2
#: templates/measure/task_list.html:180 #: templates/measure/task_list.html:185
msgid "Task" msgid "Task"
msgstr "Task" msgstr "Task"
@@ -1089,9 +1110,9 @@ msgid "Errore durante eliminazione"
msgstr "Error during deletion" msgstr "Error during deletion"
# Recipe Selection Additional # Recipe Selection Additional
#: templates/maker/recipe_list.html:55 templates/measure/select_recipe.html:72 #: templates/maker/recipe_list.html:55 templates/measure/select_recipe.html:75
#: templates/measure/task_execute.html:1362 #: templates/measure/task_execute.html:1423
#: templates/measure/task_execute.html:1672 #: templates/measure/task_execute.html:1733
msgid "Errore di connessione" msgid "Errore di connessione"
msgstr "Connection Error" msgstr "Connection Error"
@@ -1124,7 +1145,7 @@ msgid "ricetta trovata"
msgstr "recipe found" msgstr "recipe found"
#: templates/maker/recipe_list.html:144 #: templates/maker/recipe_list.html:144
#: templates/measure/select_recipe.html:196 #: templates/measure/select_recipe.html:199
msgid "ricette trovate" msgid "ricette trovate"
msgstr "recipes found" msgstr "recipes found"
@@ -1142,7 +1163,7 @@ msgid "Versioni"
msgstr "Versions" msgstr "Versions"
#: templates/maker/recipe_list.html:297 #: templates/maker/recipe_list.html:297
#: templates/measure/select_recipe.html:311 #: templates/measure/select_recipe.html:319
msgid "Nessuna ricetta trovata" msgid "Nessuna ricetta trovata"
msgstr "No recipe found" msgstr "No recipe found"
@@ -1221,7 +1242,7 @@ msgid "Apri PDF"
msgstr "Open PDF" msgstr "Open PDF"
#: templates/maker/recipe_preview.html:254 #: templates/maker/recipe_preview.html:254
#: templates/measure/task_execute.html:287 #: templates/measure/task_execute.html:299
msgid "Nessuna immagine allegata" msgid "Nessuna immagine allegata"
msgstr "No image attached" msgstr "No image attached"
@@ -1232,7 +1253,7 @@ msgstr "Measurement Points"
#: templates/maker/recipe_preview.html:299 templates/maker/task_editor.html:579 #: templates/maker/recipe_preview.html:299 templates/maker/task_editor.html:579
#: templates/maker/task_editor.html:721 #: templates/maker/task_editor.html:721
#: templates/measure/task_complete.html:173 #: templates/measure/task_complete.html:173
#: templates/measure/task_execute.html:321 #: templates/measure/task_execute.html:333
msgid "Nominale" msgid "Nominale"
msgstr "Nominal" msgstr "Nominal"
@@ -1286,7 +1307,6 @@ msgid "Torna ai Task"
msgstr "Back to Tasks" msgstr "Back to Tasks"
#: templates/maker/task_drawing.html:205 templates/maker/task_drawing.html:209 #: templates/maker/task_drawing.html:205 templates/maker/task_drawing.html:209
#: templates/measure/select_recipe.html:291
msgid "Seleziona" msgid "Seleziona"
msgstr "Select" msgstr "Select"
@@ -1402,18 +1422,18 @@ msgid "Tipo"
msgstr "Type" msgstr "Type"
#: templates/maker/task_editor.html:277 templates/maker/task_editor.html:543 #: templates/maker/task_editor.html:277 templates/maker/task_editor.html:543
#: templates/measure/task_list.html:198 #: templates/measure/task_list.html:203
msgid "Nota" msgid "Nota"
msgstr "Note" msgstr "Note"
#: templates/maker/task_editor.html:278 templates/maker/task_editor.html:544 #: templates/maker/task_editor.html:278 templates/maker/task_editor.html:544
#: templates/measure/task_execute.html:309 templates/measure/task_list.html:196 #: templates/measure/task_execute.html:321 templates/measure/task_list.html:201
msgid "Misura" msgid "Misura"
msgstr "Measure" msgstr "Measure"
# Maker - Task Editor # Maker - Task Editor
#: templates/maker/task_editor.html:279 templates/maker/task_editor.html:545 #: templates/maker/task_editor.html:279 templates/maker/task_editor.html:545
#: templates/measure/task_list.html:197 #: templates/measure/task_list.html:202
msgid "Disegno" msgid "Disegno"
msgstr "Drawing" msgstr "Drawing"
@@ -1444,7 +1464,7 @@ msgstr "Drag to reorder"
#: templates/maker/task_editor.html:390 #: templates/maker/task_editor.html:390
#: templates/maker/version_history.html:188 #: templates/maker/version_history.html:188
#: templates/measure/task_list.html:213 #: templates/measure/task_list.html:238
msgid "misurazioni" msgid "misurazioni"
msgstr "measurements" msgstr "measurements"
@@ -1710,7 +1730,7 @@ msgid "Misurazione aggiunta"
msgstr "Measurement added" msgstr "Measurement added"
#: templates/maker/task_editor.html:1685 #: templates/maker/task_editor.html:1685
#: templates/measure/task_execute.html:1170 #: templates/measure/task_execute.html:1231
msgid "Errore nel salvataggio della misurazione" msgid "Errore nel salvataggio della misurazione"
msgstr "Error saving measurement" msgstr "Error saving measurement"
@@ -1773,122 +1793,123 @@ msgstr ""
# Measure - Recipe Selection # Measure - Recipe Selection
#: templates/measure/select_recipe.html:2 #: templates/measure/select_recipe.html:2
#: templates/measure/select_recipe.html:94 #: templates/measure/select_recipe.html:97
msgid "Seleziona Ricetta" msgid "Seleziona Ricetta"
msgstr "Select Recipe" msgstr "Select Recipe"
# Measure - Task List #: templates/measure/select_recipe.html:67
#: templates/measure/select_recipe.html:30 templates/measure/task_list.html:86 #: templates/measure/select_recipe.html:280 templates/measure/task_list.html:97
msgid "lotto"
msgstr "lot"
#: templates/measure/select_recipe.html:31 templates/measure/task_list.html:87
msgid "seriale"
msgstr "serial"
#: templates/measure/select_recipe.html:64
#: templates/measure/select_recipe.html:277 templates/measure/task_list.html:97
msgid "Compila prima:" msgid "Compila prima:"
msgstr "Fill in first:" msgstr "Fill in first:"
#: templates/measure/select_recipe.html:97 #: templates/measure/select_recipe.html:100
msgid "Scegli la ricetta di misura da eseguire" msgid "Scegli la ricetta di misura da eseguire"
msgstr "Choose the measurement recipe to execute" msgstr "Choose the measurement recipe to execute"
#: templates/measure/select_recipe.html:100 #: templates/measure/select_recipe.html:103
msgid "Stazione" msgid "Stazione"
msgstr "Station" msgstr "Station"
#: templates/measure/select_recipe.html:110 #: templates/measure/select_recipe.html:113
msgid "Stazione forzata per collaudo" msgid "Stazione forzata per collaudo"
msgstr "Station overridden for commissioning" msgstr "Station overridden for commissioning"
#: templates/measure/select_recipe.html:110 #: templates/measure/select_recipe.html:113
msgid "configurata" msgid "configurata"
msgstr "configured" msgstr "configured"
#: templates/measure/select_recipe.html:115 #: templates/measure/select_recipe.html:118
msgid "Ripristina" msgid "Ripristina"
msgstr "Restore" msgstr "Restore"
#: templates/measure/select_recipe.html:140 #: templates/measure/select_recipe.html:143
msgid "Cerca ricetta" msgid "Cerca ricetta"
msgstr "Search recipe" msgstr "Search recipe"
#: templates/measure/select_recipe.html:144 #: templates/measure/select_recipe.html:147
msgid "Nome, codice o descrizione..." msgid "Nome, codice o descrizione..."
msgstr "Name, code or description..." msgstr "Name, code or description..."
#: templates/measure/select_recipe.html:157 #: templates/measure/select_recipe.html:160
msgid "Tracciabilità" msgid "Tracciabilità"
msgstr "Traceability" msgstr "Traceability"
#: templates/measure/select_recipe.html:158 #: templates/measure/select_recipe.html:161
msgid "Dati del pezzo da misurare — compila prima di selezionare la ricetta" 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" msgstr "Data of the part to measure — fill in before selecting the recipe"
#: templates/measure/select_recipe.html:168 #: templates/measure/select_recipe.html:171
#: templates/measure/task_complete.html:284 #: templates/measure/task_complete.html:284
msgid "Numero Lotto" msgid "Numero Lotto"
msgstr "Lot Number" msgstr "Lot Number"
#: templates/measure/select_recipe.html:172 #: templates/measure/select_recipe.html:175
msgid "Es. LOT-2026-001" msgid "Es. LOT-2026-001"
msgstr "E.g. LOT-2026-001" msgstr "E.g. LOT-2026-001"
#: templates/measure/select_recipe.html:182 #: templates/measure/select_recipe.html:185
#: templates/measure/task_complete.html:285 #: templates/measure/task_complete.html:285
msgid "Numero Seriale" msgid "Numero Seriale"
msgstr "Serial Number" msgstr "Serial Number"
#: templates/measure/select_recipe.html:186 #: templates/measure/select_recipe.html:189
msgid "Es. SN-000123" msgid "Es. SN-000123"
msgstr "E.g. SN-000123" msgstr "E.g. SN-000123"
#: templates/measure/select_recipe.html:239 #: templates/measure/select_recipe.html:242
msgid "Nessuna descrizione disponibile" msgid "Nessuna descrizione disponibile"
msgstr "No description available" msgstr "No description available"
#: templates/measure/select_recipe.html:315 #: templates/measure/select_recipe.html:294
#: templates/measure/task_list.html:107
msgid "AVVIA IN SEQUENZA"
msgstr "START THE SEQUENCE"
# Maker - Task Editor
#: templates/measure/select_recipe.html:302
msgid "Vedi tutti i task"
msgstr "See all tasks"
#: templates/measure/select_recipe.html:323
msgid "Nessun risultato per" msgid "Nessun risultato per"
msgstr "No results for" msgstr "No results for"
#: templates/measure/select_recipe.html:316 #: templates/measure/select_recipe.html:324
msgid "Prova con un termine diverso." msgid "Prova con un termine diverso."
msgstr "Try a different search term." msgstr "Try a different search term."
#: templates/measure/select_recipe.html:319 #: templates/measure/select_recipe.html:327
msgid "Non ci sono ricette disponibili al momento." msgid "Non ci sono ricette disponibili al momento."
msgstr "No recipes available at the moment." msgstr "No recipes available at the moment."
#: templates/measure/select_recipe.html:372 #: templates/measure/select_recipe.html:380
msgid "" msgid ""
"Inserisci o scansiona il codice della ricetta per selezionarla " "Inserisci o scansiona il codice della ricetta per selezionarla "
"automaticamente." "automaticamente."
msgstr "Enter or scan the recipe code to select it automatically." msgstr "Enter or scan the recipe code to select it automatically."
#: templates/measure/select_recipe.html:377 #: templates/measure/select_recipe.html:385
msgid "Codice Ricetta" msgid "Codice Ricetta"
msgstr "Recipe Code" msgstr "Recipe Code"
#: templates/measure/select_recipe.html:383 #: templates/measure/select_recipe.html:391
msgid "Es. REC-001" msgid "Es. REC-001"
msgstr "E.g. REC-001" msgstr "E.g. REC-001"
#: templates/measure/select_recipe.html:419 #: templates/measure/select_recipe.html:427
msgid "Cerca" msgid "Cerca"
msgstr "Search" msgstr "Search"
#: templates/measure/task_complete.html:3 #: templates/measure/task_complete.html:3
#: templates/measure/task_complete.html:39 #: templates/measure/task_complete.html:39
#: templates/measure/task_execute.html:113 #: templates/measure/task_execute.html:792
#: templates/measure/task_execute.html:765
#: templates/statistics/dashboard.html:139 #: templates/statistics/dashboard.html:139
msgid "Riepilogo" msgid "Riepilogo"
msgstr "Summary" msgstr "Summary"
#: templates/measure/task_complete.html:47 #: templates/measure/task_complete.html:47
#: templates/measure/task_execute.html:742 #: templates/measure/task_execute.html:769
msgid "Misurazioni Complete" msgid "Misurazioni Complete"
msgstr "Measurements Complete" msgstr "Measurements Complete"
@@ -1910,12 +1931,12 @@ msgid "Totale"
msgstr "Total" msgstr "Total"
#: templates/measure/task_complete.html:106 #: templates/measure/task_complete.html:106
#: templates/measure/task_execute.html:750 #: templates/measure/task_execute.html:777
msgid "Conformi" msgid "Conformi"
msgstr "Pass" msgstr "Pass"
#: templates/measure/task_complete.html:123 #: templates/measure/task_complete.html:123
#: templates/measure/task_execute.html:754 #: templates/measure/task_execute.html:781
msgid "Attenzione" msgid "Attenzione"
msgstr "Warning" msgstr "Warning"
@@ -2079,175 +2100,197 @@ msgstr "MEASUREMENT DETAILS"
msgid "Task di misurazione" msgid "Task di misurazione"
msgstr "Measurement task" msgstr "Measurement task"
#: templates/measure/task_execute.html:106 #: templates/measure/task_execute.html:115
msgid "Lista task" msgid "Lista task"
msgstr "Task list" msgstr "Task list"
#: templates/measure/task_execute.html:128 #: templates/measure/task_execute.html:125
msgid "Fermo linea" #: templates/measure/task_execute.html:706 templates/measure/task_list.html:227
msgstr "Line stop"
#: templates/measure/task_execute.html:136
msgid "Riprendi"
msgstr "Resume"
#: templates/measure/task_execute.html:144
msgid "Fine Produzione"
msgstr "End Production"
#: templates/measure/task_execute.html:259
msgid "Immagine dettaglio misura"
msgstr "Measurement detail image"
#: templates/measure/task_execute.html:307
msgid "Misurazione"
msgstr "Measurement"
#: templates/measure/task_execute.html:385
msgid "Registrata"
msgstr "Recorded"
#: templates/measure/task_execute.html:491
msgid "Task di misura senza quote configurate: la ricetta è incompleta"
msgstr "Measurement task with no quotes configured: the recipe is incomplete"
#: templates/measure/task_execute.html:510
msgid "Quota fuori tolleranza"
msgstr "Measurement out of tolerance"
#: templates/measure/task_execute.html:513
msgid "serve il capoturno, oppure una nuova misura della stessa quota"
msgstr "the supervisor must authorise it, or the same quote must be measured again"
#: templates/measure/task_execute.html:517
#: templates/measure/task_execute.html:870
msgid "Autorizza"
msgstr "Authorize"
#: templates/measure/task_execute.html:541
msgid "Il conto alla rovescia è congelato a"
msgstr "The countdown is frozen at"
#: templates/measure/task_execute.html:544
msgid "serve il capoturno per riprendere"
msgstr "the supervisor must authorise the restart"
#: templates/measure/task_execute.html:563
msgid "Produzione non registrata sul server"
msgstr "Production not recorded on the server"
#: templates/measure/task_execute.html:584
#: templates/measure/task_execute.html:793
msgid "Avvio Produzione"
msgstr "Production Start"
#: templates/measure/task_execute.html:588
msgid "Invia segnale al gestionale per avviare il timer della linea"
msgstr "Send a signal to the ERP system to start the line timer"
#: templates/measure/task_execute.html:599
msgid "Produzione avviata"
msgstr "Production started"
#: templates/measure/task_execute.html:644
msgid "Fine ciclo misura"
msgstr "Measurement cycle complete"
#: templates/measure/task_execute.html:655
msgid "Rimisura"
msgstr "Measure again"
#: templates/measure/task_execute.html:665
#: templates/measure/task_execute.html:679
msgid "Completato" msgid "Completato"
msgstr "Completed" msgstr "Completed"
#: templates/measure/task_execute.html:744 #: templates/measure/task_execute.html:140
msgid "Tutte le" msgid "Fermo linea"
msgstr "All" msgstr "Line stop"
#: templates/measure/task_execute.html:744 #: templates/measure/task_execute.html:148
msgid "misurazioni sono state registrate." msgid "Riprendi"
msgstr "measurements have been recorded." msgstr "Resume"
#: templates/measure/task_execute.html:758 #: templates/measure/task_execute.html:156
msgid "Non Conf." msgid "Fine Produzione"
msgstr "Fail" msgstr "End Production"
#: templates/measure/task_execute.html:773 #: templates/measure/task_execute.html:271
msgid "Conferma ciclo" msgid "Immagine dettaglio misura"
msgstr "Confirm cycle" msgstr "Measurement detail image"
#: templates/measure/task_execute.html:783 #: templates/measure/task_execute.html:319
msgid "Misurazione"
msgstr "Measurement"
#: templates/measure/task_execute.html:397
msgid "Registrata"
msgstr "Recorded"
#: templates/measure/task_execute.html:503
msgid "Task di misura senza quote configurate: la ricetta è incompleta"
msgstr "Measurement task with no quotes configured: the recipe is incomplete"
#: templates/measure/task_execute.html:522
msgid "Quota fuori tolleranza"
msgstr "Measurement out of tolerance"
#: templates/measure/task_execute.html:525
msgid "serve il capoturno, oppure una nuova misura della stessa quota"
msgstr "the supervisor must authorise it, or the same quote must be measured again"
#: templates/measure/task_execute.html:529
#: templates/measure/task_execute.html:897
msgid "Autorizza"
msgstr "Authorize"
#: templates/measure/task_execute.html:553
msgid "Il conto alla rovescia è congelato a"
msgstr "The countdown is frozen at"
#: templates/measure/task_execute.html:556
msgid "serve il capoturno per riprendere"
msgstr "the supervisor must authorise the restart"
#: templates/measure/task_execute.html:575
msgid "Produzione non registrata sul server"
msgstr "Production not recorded on the server"
#: templates/measure/task_execute.html:596
#: templates/measure/task_execute.html:820
msgid "Avvio Produzione"
msgstr "Production Start"
#: templates/measure/task_execute.html:600
msgid "Invia segnale al gestionale per avviare il timer della linea"
msgstr "Send a signal to the ERP system to start the line timer"
#: templates/measure/task_execute.html:611
msgid "Produzione avviata"
msgstr "Production started"
#: templates/measure/task_execute.html:667
msgid "Fine ciclo misura"
msgstr "Measurement cycle complete"
#: templates/measure/task_execute.html:679
msgid "Rimisura"
msgstr "Measure again"
#: templates/measure/task_execute.html:692
#: templates/measure/task_execute.html:810
msgid "Task successivo" msgid "Task successivo"
msgstr "Next task" msgstr "Next task"
#: templates/measure/task_execute.html:809 #: templates/measure/task_execute.html:771
msgid "Tutte le"
msgstr "All"
#: templates/measure/task_execute.html:771
msgid "misurazioni sono state registrate."
msgstr "measurements have been recorded."
#: templates/measure/task_execute.html:785
msgid "Non Conf."
msgstr "Fail"
#: templates/measure/task_execute.html:800
msgid "Conferma ciclo"
msgstr "Confirm cycle"
#: templates/measure/task_execute.html:836
msgid "Girare il pezzo e rimisurare" msgid "Girare il pezzo e rimisurare"
msgstr "Turn the piece over and measure again" msgstr "Turn the piece over and measure again"
#: templates/measure/task_execute.html:835 #: templates/measure/task_execute.html:862
msgid "Autorizzazione capoturno" msgid "Autorizzazione capoturno"
msgstr "Shift supervisor authorization" msgstr "Shift supervisor authorization"
#: templates/measure/task_execute.html:844 #: templates/measure/task_execute.html:871
msgid "Username capoturno" msgid "Username capoturno"
msgstr "Supervisor username" msgstr "Supervisor username"
#: templates/measure/task_execute.html:861 #: templates/measure/task_execute.html:888
msgid "Rimisura la quota" msgid "Rimisura la quota"
msgstr "Measure this quote again" msgstr "Measure this quote again"
#: templates/measure/task_execute.html:1125 #: templates/measure/task_execute.html:1027
msgid "Manca 1 quota"
msgstr "1 quote missing"
#: templates/measure/task_execute.html:1028
msgid "Mancano"
msgstr "Missing"
#: templates/measure/task_execute.html:1028
msgid "quote"
msgstr "quotes"
#: templates/measure/task_execute.html:1046
msgid "Serve l'autorizzazione del capoturno"
msgstr "The shift supervisor must authorise it"
#: templates/measure/task_execute.html:1049
msgid "Chiudi prima il ciclo di misura"
msgstr "Close the measurement cycle first"
#: templates/measure/task_execute.html:1186
msgid "Questa ricetta non ammette valori digitati: usare il calibro" msgid "Questa ricetta non ammette valori digitati: usare il calibro"
msgstr "This recipe does not accept typed values: use the caliper" msgstr "This recipe does not accept typed values: use the caliper"
#: templates/measure/task_execute.html:1222 #: templates/measure/task_execute.html:1283
msgid "Errore di rete. Riprovare." msgid "Errore di rete. Riprovare."
msgstr "Network error. Please retry." msgstr "Network error. Please retry."
#: templates/measure/task_execute.html:1260 #: templates/measure/task_execute.html:1321
msgid "" msgid ""
"Quota fuori tolleranza: serve l'autorizzazione del capoturno, oppure " "Quota fuori tolleranza: serve l'autorizzazione del capoturno, oppure "
"misurare di nuovo la quota" "misurare di nuovo la quota"
msgstr "Out of tolerance: the supervisor must authorise it, or the quote must be measured again" msgstr ""
"Out of tolerance: the supervisor must authorise it, or the quote must be "
"measured again"
#: templates/measure/task_execute.html:1357 #: templates/measure/task_execute.html:1418
msgid "Errore di comunicazione con il server" msgid "Errore di comunicazione con il server"
msgstr "Error communicating with the server" msgstr "Error communicating with the server"
#: templates/measure/task_execute.html:1609 #: templates/measure/task_execute.html:1670
msgid "Misurazione fuori tolleranza" msgid "Misurazione fuori tolleranza"
msgstr "Measurement out of tolerance" msgstr "Measurement out of tolerance"
#: templates/measure/task_execute.html:1610 #: templates/measure/task_execute.html:1671
msgid "Fermo linea richiesto" msgid "Fermo linea richiesto"
msgstr "Line stop requested" msgstr "Line stop requested"
#: templates/measure/task_execute.html:1611 #: templates/measure/task_execute.html:1672
msgid "Ripresa della produzione" msgid "Ripresa della produzione"
msgstr "Resuming production" msgstr "Resuming production"
#: templates/measure/task_execute.html:1612 #: templates/measure/task_execute.html:1673
msgid "Fine produzione richiesta" msgid "Fine produzione richiesta"
msgstr "End of production requested" msgstr "End of production requested"
#: templates/measure/task_execute.html:1656 #: templates/measure/task_execute.html:1717
msgid "Credenziali non valide o utente non autorizzato" msgid "Credenziali non valide o utente non autorizzato"
msgstr "Invalid credentials or unauthorized user" msgstr "Invalid credentials or unauthorized user"
#: templates/measure/task_execute.html:1683 #: templates/measure/task_execute.html:1744
msgid "Nessuna produzione aperta su questa stazione" msgid "Nessuna produzione aperta su questa stazione"
msgstr "No production open at this station" msgstr "No production open at this station"
#: templates/measure/task_execute.html:1700 #: templates/measure/task_execute.html:1761
msgid "Azione non riuscita" msgid "Azione non riuscita"
msgstr "Action failed" msgstr "Action failed"
#: templates/measure/task_list.html:107
msgid "AVVIA"
msgstr "START"
#: templates/measure/task_list.html:145 #: templates/measure/task_list.html:145
msgid "Task da eseguire" msgid "Task da eseguire"
msgstr "Tasks to execute" msgstr "Tasks to execute"
@@ -2256,30 +2299,32 @@ msgstr "Tasks to execute"
msgid "misurazioni totali" msgid "misurazioni totali"
msgstr "total measurements" msgstr "total measurements"
#: templates/measure/task_list.html:199 #: templates/measure/task_list.html:204
msgid "Confronto profilo" msgid "Confronto profilo"
msgstr "Profile comparison" msgstr "Profile comparison"
#: templates/measure/task_list.html:200 #: templates/measure/task_list.html:205
msgid "Misura camera" msgid "Misura camera"
msgstr "Camera measurement" msgstr "Camera measurement"
#: templates/measure/task_list.html:222 #: templates/measure/task_list.html:232
msgid "Incompiuto"
msgstr "Unfinished"
#: templates/measure/task_list.html:247
msgid "Allegato" msgid "Allegato"
msgstr "Attachment" msgstr "Attachment"
#: templates/measure/task_list.html:246 #: templates/measure/task_list.html:275
msgid "Visualizza Task"
msgstr "View Tasks"
#: templates/measure/task_list.html:269 msgid "Visualizza singolo TASK"
msgstr "View single TASK"
#: templates/measure/task_list.html:294
msgid "Nessun task disponibile" msgid "Nessun task disponibile"
msgstr "No tasks available" msgstr "No tasks available"
#: templates/measure/task_list.html:272
msgid "Questa ricetta non ha ancora task definiti."
msgstr "This recipe has no tasks defined yet."
# SPC Statistics Dashboard # SPC Statistics Dashboard
#: templates/statistics/dashboard.html:3 templates/statistics/dashboard.html:17 #: templates/statistics/dashboard.html:3 templates/statistics/dashboard.html:17
msgid "Statistiche SPC" msgid "Statistiche SPC"
@@ -2700,3 +2745,6 @@ msgstr "Error generating report"
#~ msgid "Utente non autorizzato (richiesto capoturno)" #~ msgid "Utente non autorizzato (richiesto capoturno)"
#~ msgstr "User not authorized (shift supervisor required)" #~ msgstr "User not authorized (shift supervisor required)"
#~ msgid "Errore nel caricamento dei task: %(detail)s"
#~ msgstr "Error loading tasks: %(detail)s"
@@ -3,7 +3,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: TieMeasureFlow 1.0\n" "Project-Id-Version: TieMeasureFlow 1.0\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2026-07-28 20:48+0000\n" "POT-Creation-Date: 2026-07-28 21:28+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: it\n" "Language: it\n"
@@ -129,43 +129,64 @@ msgstr "Stazione corrente: %(code)s"
msgid "Errore nel caricamento delle ricette: %(detail)s" msgid "Errore nel caricamento delle ricette: %(detail)s"
msgstr "Errore nel caricamento delle ricette: %(detail)s" msgstr "Errore nel caricamento delle ricette: %(detail)s"
#: blueprints/measure.py:146 blueprints/measure.py:237 # Measure - Task List
#: blueprints/measure.py:139 templates/measure/select_recipe.html:30
#: templates/measure/task_list.html:86
msgid "lotto"
msgstr "lotto"
#: blueprints/measure.py:141 templates/measure/select_recipe.html:31
#: templates/measure/task_list.html:87
msgid "seriale"
msgstr "seriale"
#: blueprints/measure.py:204 blueprints/measure.py:242
#: blueprints/measure.py:330
#, python-format #, python-format
msgid "Ricetta non trovata: %(detail)s" msgid "Ricetta non trovata: %(detail)s"
msgstr "Ricetta non trovata: %(detail)s" msgstr "Ricetta non trovata: %(detail)s"
#: blueprints/measure.py:156 #: blueprints/measure.py:213
#, python-format #, python-format
msgid "Errore nel caricamento dei task: %(detail)s" msgid "Compila prima: %(fields)s"
msgstr "Errore nel caricamento dei task: %(detail)s" msgstr "Compila prima: %(fields)s"
#: blueprints/measure.py:185 #: blueprints/measure.py:222 templates/measure/task_list.html:297
msgid "Questa ricetta non ha ancora task definiti."
msgstr "Questa ricetta non ha ancora task definiti."
#: blueprints/measure.py:250
msgid "Errore nel caricamento dei task."
msgstr "Errore nel caricamento dei task."
#: blueprints/measure.py:278
#, python-format #, python-format
msgid "Task non trovato: %(detail)s" msgid "Task non trovato: %(detail)s"
msgstr "Task non trovato: %(detail)s" msgstr "Task non trovato: %(detail)s"
#: blueprints/measure.py:306 #: blueprints/measure.py:399
msgid "Codice non fornito" msgid "Codice non fornito"
msgstr "Codice non fornito" msgstr "Codice non fornito"
#: blueprints/measure.py:312 templates/measure/select_recipe.html:57 #: blueprints/measure.py:405 templates/measure/select_recipe.html:60
msgid "Ricetta non trovata" msgid "Ricetta non trovata"
msgstr "Ricetta non trovata" msgstr "Ricetta non trovata"
#: blueprints/measure.py:368 #: blueprints/measure.py:461
msgid "Dati mancanti: subtask_id, version_id e value sono obbligatori" msgid "Dati mancanti: subtask_id, version_id e value sono obbligatori"
msgstr "Dati mancanti: subtask_id, version_id e value sono obbligatori" msgstr "Dati mancanti: subtask_id, version_id e value sono obbligatori"
#: blueprints/measure.py:393 templates/admin/stations.html:549 #: blueprints/measure.py:486 templates/admin/stations.html:549
#: templates/maker/recipe_editor.html:598 #: templates/maker/recipe_editor.html:598
msgid "Errore nel salvataggio" msgid "Errore nel salvataggio"
msgstr "Errore nel salvataggio" msgstr "Errore nel salvataggio"
#: blueprints/measure.py:437 blueprints/measure.py:542 #: blueprints/measure.py:530 blueprints/measure.py:635
msgid "Username e password richiesti" msgid "Username e password richiesti"
msgstr "Username e password richiesti" msgstr "Username e password richiesti"
#: blueprints/measure.py:463 blueprints/measure.py:480 #: blueprints/measure.py:556 blueprints/measure.py:573
#: templates/errors/station_not_configured.html:2 #: templates/errors/station_not_configured.html:2
#: templates/errors/station_not_configured.html:16 #: templates/errors/station_not_configured.html:16
msgid "Stazione non configurata" msgid "Stazione non configurata"
@@ -373,8 +394,8 @@ msgstr "Note opzionali"
#: templates/maker/task_editor.html:312 templates/maker/task_editor.html:421 #: templates/maker/task_editor.html:312 templates/maker/task_editor.html:421
#: templates/maker/task_editor.html:782 templates/maker/task_editor.html:901 #: templates/maker/task_editor.html:782 templates/maker/task_editor.html:901
#: templates/maker/task_editor.html:966 templates/maker/task_editor.html:1071 #: templates/maker/task_editor.html:966 templates/maker/task_editor.html:1071
#: templates/measure/select_recipe.html:405 #: templates/measure/select_recipe.html:413
#: templates/measure/task_execute.html:860 #: templates/measure/task_execute.html:887
msgid "Annulla" msgid "Annulla"
msgstr "Annulla" msgstr "Annulla"
@@ -390,7 +411,7 @@ msgstr "Crea Stazione"
#: templates/admin/stations.html:241 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:107
#: templates/maker/recipe_editor.html:490 templates/maker/task_drawing.html:61 #: templates/maker/recipe_editor.html:490 templates/maker/task_drawing.html:61
#: templates/measure/task_execute.html:412 #: templates/measure/task_execute.html:424
msgid "Salvataggio..." msgid "Salvataggio..."
msgstr "Salvataggio..." msgstr "Salvataggio..."
@@ -519,7 +540,7 @@ msgstr "Nuovo Utente"
#: templates/admin/users.html:48 templates/admin/users.html:173 #: templates/admin/users.html:48 templates/admin/users.html:173
#: templates/admin/users.html:179 templates/auth/login.html:35 #: templates/admin/users.html:179 templates/auth/login.html:35
#: templates/auth/login.html:49 templates/auth/profile.html:36 #: templates/auth/login.html:49 templates/auth/profile.html:36
#: templates/measure/task_execute.html:842 #: templates/measure/task_execute.html:869
msgid "Username" msgid "Username"
msgstr "Username" msgstr "Username"
@@ -571,7 +592,7 @@ msgstr "Il nome utente non può essere modificato"
#: templates/admin/users.html:206 templates/admin/users.html:214 #: templates/admin/users.html:206 templates/admin/users.html:214
#: templates/auth/login.html:57 templates/auth/login.html:71 #: templates/auth/login.html:57 templates/auth/login.html:71
#: templates/measure/task_execute.html:847 #: templates/measure/task_execute.html:874
msgid "Password" msgid "Password"
msgstr "Password" msgstr "Password"
@@ -726,8 +747,8 @@ msgstr "Logout"
#: templates/components/barcode_scanner.html:21 #: templates/components/barcode_scanner.html:21
#: templates/components/barcode_scanner.html:54 #: templates/components/barcode_scanner.html:54
#: templates/measure/select_recipe.html:128 #: templates/measure/select_recipe.html:131
#: templates/measure/select_recipe.html:357 #: templates/measure/select_recipe.html:365
msgid "Scansiona Barcode" msgid "Scansiona Barcode"
msgstr "Scansiona Barcode" msgstr "Scansiona Barcode"
@@ -826,14 +847,14 @@ msgid "Misura solo da calibro: questa ricetta non ammette valori digitati"
msgstr "Misura solo da calibro: questa ricetta non ammette valori digitati" msgstr "Misura solo da calibro: questa ricetta non ammette valori digitati"
#: templates/components/production_clock.html:42 #: templates/components/production_clock.html:42
#: templates/measure/task_execute.html:460 #: templates/measure/task_execute.html:472
msgid "Prossima misurazione tra" msgid "Prossima misurazione tra"
msgstr "Prossima misurazione tra" msgstr "Prossima misurazione tra"
#: templates/components/production_clock.html:49 #: templates/components/production_clock.html:49
#: templates/components/production_clock.html:62 #: templates/components/production_clock.html:62
#: templates/measure/task_execute.html:463 #: templates/measure/task_execute.html:475
#: templates/measure/task_execute.html:542 #: templates/measure/task_execute.html:554
msgid "Misurazione in ritardo di" msgid "Misurazione in ritardo di"
msgstr "Misurazione in ritardo di" msgstr "Misurazione in ritardo di"
@@ -842,27 +863,27 @@ msgid "Linea ferma — conto alla rovescia congelato a"
msgstr "Linea ferma — conto alla rovescia congelato a" msgstr "Linea ferma — conto alla rovescia congelato a"
#: templates/components/production_clock.html:62 #: templates/components/production_clock.html:62
#: templates/measure/task_execute.html:536 #: templates/measure/task_execute.html:548
msgid "Linea ferma" msgid "Linea ferma"
msgstr "Linea ferma" msgstr "Linea ferma"
#: templates/components/production_clock.html:74 #: templates/components/production_clock.html:74
#: templates/measure/task_execute.html:472 #: templates/measure/task_execute.html:484
msgid "Ciclo" msgid "Ciclo"
msgstr "Ciclo" msgstr "Ciclo"
#: templates/components/production_clock.html:92 #: templates/components/production_clock.html:92
#: templates/measure/task_execute.html:704 #: templates/measure/task_execute.html:731
msgid "È ora di misurare" msgid "È ora di misurare"
msgstr "È ora di misurare" msgstr "È ora di misurare"
#: templates/components/production_clock.html:95 #: templates/components/production_clock.html:95
#: templates/measure/task_execute.html:706 #: templates/measure/task_execute.html:733
msgid "Ritorno alla misurazione tra" msgid "Ritorno alla misurazione tra"
msgstr "Ritorno alla misurazione tra" msgstr "Ritorno alla misurazione tra"
#: templates/components/production_clock.html:101 #: templates/components/production_clock.html:101
#: templates/measure/task_execute.html:711 #: templates/measure/task_execute.html:738
msgid "Vai alla misura" msgid "Vai alla misura"
msgstr "Vai alla misura" msgstr "Vai alla misura"
@@ -915,8 +936,8 @@ msgstr "Anteprima"
#: templates/maker/recipe_editor.html:128 templates/maker/recipe_list.html:246 #: templates/maker/recipe_editor.html:128 templates/maker/recipe_list.html:246
#: templates/maker/task_drawing.html:106 templates/maker/task_editor.html:130 #: templates/maker/task_drawing.html:106 templates/maker/task_editor.html:130
#: templates/measure/task_complete.html:171 #: templates/measure/task_complete.html:171
#: templates/measure/task_execute.html:615 templates/measure/task_list.html:2 #: templates/measure/task_execute.html:627 templates/measure/task_list.html:2
#: templates/measure/task_list.html:180 #: templates/measure/task_list.html:185
msgid "Task" msgid "Task"
msgstr "Task" msgstr "Task"
@@ -1091,9 +1112,9 @@ msgid "Errore durante eliminazione"
msgstr "Errore durante eliminazione" msgstr "Errore durante eliminazione"
# Recipe Selection Additional # Recipe Selection Additional
#: templates/maker/recipe_list.html:55 templates/measure/select_recipe.html:72 #: templates/maker/recipe_list.html:55 templates/measure/select_recipe.html:75
#: templates/measure/task_execute.html:1362 #: templates/measure/task_execute.html:1423
#: templates/measure/task_execute.html:1672 #: templates/measure/task_execute.html:1733
msgid "Errore di connessione" msgid "Errore di connessione"
msgstr "Errore di connessione" msgstr "Errore di connessione"
@@ -1126,7 +1147,7 @@ msgid "ricetta trovata"
msgstr "ricetta trovata" msgstr "ricetta trovata"
#: templates/maker/recipe_list.html:144 #: templates/maker/recipe_list.html:144
#: templates/measure/select_recipe.html:196 #: templates/measure/select_recipe.html:199
msgid "ricette trovate" msgid "ricette trovate"
msgstr "ricette trovate" msgstr "ricette trovate"
@@ -1144,7 +1165,7 @@ msgid "Versioni"
msgstr "Versioni" msgstr "Versioni"
#: templates/maker/recipe_list.html:297 #: templates/maker/recipe_list.html:297
#: templates/measure/select_recipe.html:311 #: templates/measure/select_recipe.html:319
msgid "Nessuna ricetta trovata" msgid "Nessuna ricetta trovata"
msgstr "Nessuna ricetta trovata" msgstr "Nessuna ricetta trovata"
@@ -1223,7 +1244,7 @@ msgid "Apri PDF"
msgstr "Apri PDF" msgstr "Apri PDF"
#: templates/maker/recipe_preview.html:254 #: templates/maker/recipe_preview.html:254
#: templates/measure/task_execute.html:287 #: templates/measure/task_execute.html:299
msgid "Nessuna immagine allegata" msgid "Nessuna immagine allegata"
msgstr "Nessuna immagine allegata" msgstr "Nessuna immagine allegata"
@@ -1234,7 +1255,7 @@ msgstr "Punti di Misura"
#: templates/maker/recipe_preview.html:299 templates/maker/task_editor.html:579 #: templates/maker/recipe_preview.html:299 templates/maker/task_editor.html:579
#: templates/maker/task_editor.html:721 #: templates/maker/task_editor.html:721
#: templates/measure/task_complete.html:173 #: templates/measure/task_complete.html:173
#: templates/measure/task_execute.html:321 #: templates/measure/task_execute.html:333
msgid "Nominale" msgid "Nominale"
msgstr "Nominale" msgstr "Nominale"
@@ -1288,7 +1309,6 @@ msgid "Torna ai Task"
msgstr "Torna ai Task" msgstr "Torna ai Task"
#: templates/maker/task_drawing.html:205 templates/maker/task_drawing.html:209 #: templates/maker/task_drawing.html:205 templates/maker/task_drawing.html:209
#: templates/measure/select_recipe.html:291
msgid "Seleziona" msgid "Seleziona"
msgstr "Seleziona" msgstr "Seleziona"
@@ -1404,18 +1424,18 @@ msgid "Tipo"
msgstr "Tipo" msgstr "Tipo"
#: templates/maker/task_editor.html:277 templates/maker/task_editor.html:543 #: templates/maker/task_editor.html:277 templates/maker/task_editor.html:543
#: templates/measure/task_list.html:198 #: templates/measure/task_list.html:203
msgid "Nota" msgid "Nota"
msgstr "Nota" msgstr "Nota"
#: templates/maker/task_editor.html:278 templates/maker/task_editor.html:544 #: templates/maker/task_editor.html:278 templates/maker/task_editor.html:544
#: templates/measure/task_execute.html:309 templates/measure/task_list.html:196 #: templates/measure/task_execute.html:321 templates/measure/task_list.html:201
msgid "Misura" msgid "Misura"
msgstr "Misura" msgstr "Misura"
# Maker - Task Editor # Maker - Task Editor
#: templates/maker/task_editor.html:279 templates/maker/task_editor.html:545 #: templates/maker/task_editor.html:279 templates/maker/task_editor.html:545
#: templates/measure/task_list.html:197 #: templates/measure/task_list.html:202
msgid "Disegno" msgid "Disegno"
msgstr "Disegno" msgstr "Disegno"
@@ -1446,7 +1466,7 @@ msgstr "Trascina per riordinare"
#: templates/maker/task_editor.html:390 #: templates/maker/task_editor.html:390
#: templates/maker/version_history.html:188 #: templates/maker/version_history.html:188
#: templates/measure/task_list.html:213 #: templates/measure/task_list.html:238
msgid "misurazioni" msgid "misurazioni"
msgstr "misurazioni" msgstr "misurazioni"
@@ -1712,7 +1732,7 @@ msgid "Misurazione aggiunta"
msgstr "Misurazione aggiunta" msgstr "Misurazione aggiunta"
#: templates/maker/task_editor.html:1685 #: templates/maker/task_editor.html:1685
#: templates/measure/task_execute.html:1170 #: templates/measure/task_execute.html:1231
msgid "Errore nel salvataggio della misurazione" msgid "Errore nel salvataggio della misurazione"
msgstr "Errore nel salvataggio della misurazione" msgstr "Errore nel salvataggio della misurazione"
@@ -1775,95 +1795,97 @@ msgstr ""
# Measure - Recipe Selection # Measure - Recipe Selection
#: templates/measure/select_recipe.html:2 #: templates/measure/select_recipe.html:2
#: templates/measure/select_recipe.html:94 #: templates/measure/select_recipe.html:97
msgid "Seleziona Ricetta" msgid "Seleziona Ricetta"
msgstr "Seleziona Ricetta" msgstr "Seleziona Ricetta"
# Measure - Task List #: templates/measure/select_recipe.html:67
#: templates/measure/select_recipe.html:30 templates/measure/task_list.html:86 #: templates/measure/select_recipe.html:280 templates/measure/task_list.html:97
msgid "lotto"
msgstr "lotto"
#: templates/measure/select_recipe.html:31 templates/measure/task_list.html:87
msgid "seriale"
msgstr "seriale"
#: templates/measure/select_recipe.html:64
#: templates/measure/select_recipe.html:277 templates/measure/task_list.html:97
msgid "Compila prima:" msgid "Compila prima:"
msgstr "Compila prima:" msgstr "Compila prima:"
#: templates/measure/select_recipe.html:97 #: templates/measure/select_recipe.html:100
msgid "Scegli la ricetta di misura da eseguire" msgid "Scegli la ricetta di misura da eseguire"
msgstr "Scegli la ricetta di misura da eseguire" msgstr "Scegli la ricetta di misura da eseguire"
#: templates/measure/select_recipe.html:100 #: templates/measure/select_recipe.html:103
msgid "Stazione" msgid "Stazione"
msgstr "Stazione" msgstr "Stazione"
#: templates/measure/select_recipe.html:110 #: templates/measure/select_recipe.html:113
msgid "Stazione forzata per collaudo" msgid "Stazione forzata per collaudo"
msgstr "Stazione forzata per collaudo" msgstr "Stazione forzata per collaudo"
#: templates/measure/select_recipe.html:110 #: templates/measure/select_recipe.html:113
msgid "configurata" msgid "configurata"
msgstr "configurata" msgstr "configurata"
#: templates/measure/select_recipe.html:115 #: templates/measure/select_recipe.html:118
msgid "Ripristina" msgid "Ripristina"
msgstr "Ripristina" msgstr "Ripristina"
#: templates/measure/select_recipe.html:140 #: templates/measure/select_recipe.html:143
msgid "Cerca ricetta" msgid "Cerca ricetta"
msgstr "Cerca ricetta" msgstr "Cerca ricetta"
#: templates/measure/select_recipe.html:144 #: templates/measure/select_recipe.html:147
msgid "Nome, codice o descrizione..." msgid "Nome, codice o descrizione..."
msgstr "Nome, codice o descrizione..." msgstr "Nome, codice o descrizione..."
#: templates/measure/select_recipe.html:157 #: templates/measure/select_recipe.html:160
msgid "Tracciabilità" msgid "Tracciabilità"
msgstr "Tracciabilità" msgstr "Tracciabilità"
#: templates/measure/select_recipe.html:158 #: templates/measure/select_recipe.html:161
msgid "Dati del pezzo da misurare — compila prima di selezionare la ricetta" msgid "Dati del pezzo da misurare — compila prima di selezionare la ricetta"
msgstr "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:168 #: templates/measure/select_recipe.html:171
#: templates/measure/task_complete.html:284 #: templates/measure/task_complete.html:284
msgid "Numero Lotto" msgid "Numero Lotto"
msgstr "Numero Lotto" msgstr "Numero Lotto"
#: templates/measure/select_recipe.html:172 #: templates/measure/select_recipe.html:175
msgid "Es. LOT-2026-001" msgid "Es. LOT-2026-001"
msgstr "Es. LOT-2026-001" msgstr "Es. LOT-2026-001"
#: templates/measure/select_recipe.html:182 #: templates/measure/select_recipe.html:185
#: templates/measure/task_complete.html:285 #: templates/measure/task_complete.html:285
msgid "Numero Seriale" msgid "Numero Seriale"
msgstr "Numero Seriale" msgstr "Numero Seriale"
#: templates/measure/select_recipe.html:186 #: templates/measure/select_recipe.html:189
msgid "Es. SN-000123" msgid "Es. SN-000123"
msgstr "Es. SN-000123" msgstr "Es. SN-000123"
#: templates/measure/select_recipe.html:239 #: templates/measure/select_recipe.html:242
msgid "Nessuna descrizione disponibile" msgid "Nessuna descrizione disponibile"
msgstr "Nessuna descrizione disponibile" msgstr "Nessuna descrizione disponibile"
#: templates/measure/select_recipe.html:315 #: templates/measure/select_recipe.html:294
#: templates/measure/task_list.html:107
msgid "AVVIA IN SEQUENZA"
msgstr "AVVIA IN SEQUENZA"
# Maker - Task Editor
#: templates/measure/select_recipe.html:302
msgid "Vedi tutti i task"
msgstr "Vedi tutti i task"
#: templates/measure/select_recipe.html:323
msgid "Nessun risultato per" msgid "Nessun risultato per"
msgstr "Nessun risultato per" msgstr "Nessun risultato per"
#: templates/measure/select_recipe.html:316 #: templates/measure/select_recipe.html:324
msgid "Prova con un termine diverso." msgid "Prova con un termine diverso."
msgstr "Prova con un termine diverso." msgstr "Prova con un termine diverso."
#: templates/measure/select_recipe.html:319 #: templates/measure/select_recipe.html:327
msgid "Non ci sono ricette disponibili al momento." msgid "Non ci sono ricette disponibili al momento."
msgstr "Non ci sono ricette disponibili al momento." msgstr "Non ci sono ricette disponibili al momento."
#: templates/measure/select_recipe.html:372 #: templates/measure/select_recipe.html:380
msgid "" msgid ""
"Inserisci o scansiona il codice della ricetta per selezionarla " "Inserisci o scansiona il codice della ricetta per selezionarla "
"automaticamente." "automaticamente."
@@ -1871,28 +1893,27 @@ msgstr ""
"Inserisci o scansiona il codice della ricetta per selezionarla " "Inserisci o scansiona il codice della ricetta per selezionarla "
"automaticamente." "automaticamente."
#: templates/measure/select_recipe.html:377 #: templates/measure/select_recipe.html:385
msgid "Codice Ricetta" msgid "Codice Ricetta"
msgstr "Codice Ricetta" msgstr "Codice Ricetta"
#: templates/measure/select_recipe.html:383 #: templates/measure/select_recipe.html:391
msgid "Es. REC-001" msgid "Es. REC-001"
msgstr "Es. REC-001" msgstr "Es. REC-001"
#: templates/measure/select_recipe.html:419 #: templates/measure/select_recipe.html:427
msgid "Cerca" msgid "Cerca"
msgstr "Cerca" msgstr "Cerca"
#: templates/measure/task_complete.html:3 #: templates/measure/task_complete.html:3
#: templates/measure/task_complete.html:39 #: templates/measure/task_complete.html:39
#: templates/measure/task_execute.html:113 #: templates/measure/task_execute.html:792
#: templates/measure/task_execute.html:765
#: templates/statistics/dashboard.html:139 #: templates/statistics/dashboard.html:139
msgid "Riepilogo" msgid "Riepilogo"
msgstr "Riepilogo" msgstr "Riepilogo"
#: templates/measure/task_complete.html:47 #: templates/measure/task_complete.html:47
#: templates/measure/task_execute.html:742 #: templates/measure/task_execute.html:769
msgid "Misurazioni Complete" msgid "Misurazioni Complete"
msgstr "Misurazioni Complete" msgstr "Misurazioni Complete"
@@ -1914,12 +1935,12 @@ msgid "Totale"
msgstr "Totale" msgstr "Totale"
#: templates/measure/task_complete.html:106 #: templates/measure/task_complete.html:106
#: templates/measure/task_execute.html:750 #: templates/measure/task_execute.html:777
msgid "Conformi" msgid "Conformi"
msgstr "Conformi" msgstr "Conformi"
#: templates/measure/task_complete.html:123 #: templates/measure/task_complete.html:123
#: templates/measure/task_execute.html:754 #: templates/measure/task_execute.html:781
msgid "Attenzione" msgid "Attenzione"
msgstr "Attenzione" msgstr "Attenzione"
@@ -2083,175 +2104,197 @@ msgstr "DETTAGLIO MISURE"
msgid "Task di misurazione" msgid "Task di misurazione"
msgstr "Task di misurazione" msgstr "Task di misurazione"
#: templates/measure/task_execute.html:106 #: templates/measure/task_execute.html:115
msgid "Lista task" msgid "Lista task"
msgstr "Lista task" msgstr "Lista task"
#: templates/measure/task_execute.html:128 #: templates/measure/task_execute.html:125
msgid "Fermo linea" #: templates/measure/task_execute.html:706 templates/measure/task_list.html:227
msgstr "Fermo linea"
#: templates/measure/task_execute.html:136
msgid "Riprendi"
msgstr "Riprendi"
#: templates/measure/task_execute.html:144
msgid "Fine Produzione"
msgstr "Fine Produzione"
#: templates/measure/task_execute.html:259
msgid "Immagine dettaglio misura"
msgstr "Immagine dettaglio misura"
#: templates/measure/task_execute.html:307
msgid "Misurazione"
msgstr "Misurazione"
#: templates/measure/task_execute.html:385
msgid "Registrata"
msgstr "Registrata"
#: templates/measure/task_execute.html:491
msgid "Task di misura senza quote configurate: la ricetta è incompleta"
msgstr "Task di misura senza quote configurate: la ricetta è incompleta"
#: templates/measure/task_execute.html:510
msgid "Quota fuori tolleranza"
msgstr "Quota fuori tolleranza"
#: templates/measure/task_execute.html:513
msgid "serve il capoturno, oppure una nuova misura della stessa quota"
msgstr "serve il capoturno, oppure una nuova misura della stessa quota"
#: templates/measure/task_execute.html:517
#: templates/measure/task_execute.html:870
msgid "Autorizza"
msgstr "Autorizza"
#: templates/measure/task_execute.html:541
msgid "Il conto alla rovescia è congelato a"
msgstr "Il conto alla rovescia è congelato a"
#: templates/measure/task_execute.html:544
msgid "serve il capoturno per riprendere"
msgstr "serve il capoturno per riprendere"
#: templates/measure/task_execute.html:563
msgid "Produzione non registrata sul server"
msgstr "Produzione non registrata sul server"
#: templates/measure/task_execute.html:584
#: templates/measure/task_execute.html:793
msgid "Avvio Produzione"
msgstr "Avvio Produzione"
#: templates/measure/task_execute.html:588
msgid "Invia segnale al gestionale per avviare il timer della linea"
msgstr "Invia segnale al gestionale per avviare il timer della linea"
#: templates/measure/task_execute.html:599
msgid "Produzione avviata"
msgstr "Produzione avviata"
#: templates/measure/task_execute.html:644
msgid "Fine ciclo misura"
msgstr "Fine ciclo misura"
#: templates/measure/task_execute.html:655
msgid "Rimisura"
msgstr "Rimisura"
#: templates/measure/task_execute.html:665
#: templates/measure/task_execute.html:679
msgid "Completato" msgid "Completato"
msgstr "Completato" msgstr "Completato"
#: templates/measure/task_execute.html:744 #: templates/measure/task_execute.html:140
msgid "Tutte le" msgid "Fermo linea"
msgstr "Tutte le" msgstr "Fermo linea"
#: templates/measure/task_execute.html:744 #: templates/measure/task_execute.html:148
msgid "misurazioni sono state registrate." msgid "Riprendi"
msgstr "misurazioni sono state registrate." msgstr "Riprendi"
#: templates/measure/task_execute.html:758 #: templates/measure/task_execute.html:156
msgid "Non Conf." msgid "Fine Produzione"
msgstr "Non Conf." msgstr "Fine Produzione"
#: templates/measure/task_execute.html:773 #: templates/measure/task_execute.html:271
msgid "Conferma ciclo" msgid "Immagine dettaglio misura"
msgstr "Conferma ciclo" msgstr "Immagine dettaglio misura"
#: templates/measure/task_execute.html:783 #: templates/measure/task_execute.html:319
msgid "Misurazione"
msgstr "Misurazione"
#: templates/measure/task_execute.html:397
msgid "Registrata"
msgstr "Registrata"
#: templates/measure/task_execute.html:503
msgid "Task di misura senza quote configurate: la ricetta è incompleta"
msgstr "Task di misura senza quote configurate: la ricetta è incompleta"
#: templates/measure/task_execute.html:522
msgid "Quota fuori tolleranza"
msgstr "Quota fuori tolleranza"
#: templates/measure/task_execute.html:525
msgid "serve il capoturno, oppure una nuova misura della stessa quota"
msgstr "serve il capoturno, oppure una nuova misura della stessa quota"
#: templates/measure/task_execute.html:529
#: templates/measure/task_execute.html:897
msgid "Autorizza"
msgstr "Autorizza"
#: templates/measure/task_execute.html:553
msgid "Il conto alla rovescia è congelato a"
msgstr "Il conto alla rovescia è congelato a"
#: templates/measure/task_execute.html:556
msgid "serve il capoturno per riprendere"
msgstr "serve il capoturno per riprendere"
#: templates/measure/task_execute.html:575
msgid "Produzione non registrata sul server"
msgstr "Produzione non registrata sul server"
#: templates/measure/task_execute.html:596
#: templates/measure/task_execute.html:820
msgid "Avvio Produzione"
msgstr "Avvio Produzione"
#: templates/measure/task_execute.html:600
msgid "Invia segnale al gestionale per avviare il timer della linea"
msgstr "Invia segnale al gestionale per avviare il timer della linea"
#: templates/measure/task_execute.html:611
msgid "Produzione avviata"
msgstr "Produzione avviata"
#: templates/measure/task_execute.html:667
msgid "Fine ciclo misura"
msgstr "Fine ciclo misura"
#: templates/measure/task_execute.html:679
msgid "Rimisura"
msgstr "Rimisura"
#: templates/measure/task_execute.html:692
#: templates/measure/task_execute.html:810
msgid "Task successivo" msgid "Task successivo"
msgstr "Task successivo" msgstr "Task successivo"
#: templates/measure/task_execute.html:809 #: templates/measure/task_execute.html:771
msgid "Tutte le"
msgstr "Tutte le"
#: templates/measure/task_execute.html:771
msgid "misurazioni sono state registrate."
msgstr "misurazioni sono state registrate."
#: templates/measure/task_execute.html:785
msgid "Non Conf."
msgstr "Non Conf."
#: templates/measure/task_execute.html:800
msgid "Conferma ciclo"
msgstr "Conferma ciclo"
#: templates/measure/task_execute.html:836
msgid "Girare il pezzo e rimisurare" msgid "Girare il pezzo e rimisurare"
msgstr "Girare il pezzo e rimisurare" msgstr "Girare il pezzo e rimisurare"
#: templates/measure/task_execute.html:835 #: templates/measure/task_execute.html:862
msgid "Autorizzazione capoturno" msgid "Autorizzazione capoturno"
msgstr "Autorizzazione capoturno" msgstr "Autorizzazione capoturno"
#: templates/measure/task_execute.html:844 #: templates/measure/task_execute.html:871
msgid "Username capoturno" msgid "Username capoturno"
msgstr "Username capoturno" msgstr "Username capoturno"
#: templates/measure/task_execute.html:861 #: templates/measure/task_execute.html:888
msgid "Rimisura la quota" msgid "Rimisura la quota"
msgstr "Rimisura la quota" msgstr "Rimisura la quota"
#: templates/measure/task_execute.html:1125 #: templates/measure/task_execute.html:1027
msgid "Manca 1 quota"
msgstr "Manca 1 quota"
#: templates/measure/task_execute.html:1028
msgid "Mancano"
msgstr "Mancano"
#: templates/measure/task_execute.html:1028
msgid "quote"
msgstr "quote"
#: templates/measure/task_execute.html:1046
msgid "Serve l'autorizzazione del capoturno"
msgstr "Serve l'autorizzazione del capoturno"
#: templates/measure/task_execute.html:1049
msgid "Chiudi prima il ciclo di misura"
msgstr "Chiudi prima il ciclo di misura"
#: templates/measure/task_execute.html:1186
msgid "Questa ricetta non ammette valori digitati: usare il calibro" msgid "Questa ricetta non ammette valori digitati: usare il calibro"
msgstr "Questa ricetta non ammette valori digitati: usare il calibro" msgstr "Questa ricetta non ammette valori digitati: usare il calibro"
#: templates/measure/task_execute.html:1222 #: templates/measure/task_execute.html:1283
msgid "Errore di rete. Riprovare." msgid "Errore di rete. Riprovare."
msgstr "Errore di rete. Riprovare." msgstr "Errore di rete. Riprovare."
#: templates/measure/task_execute.html:1260 #: templates/measure/task_execute.html:1321
msgid "" msgid ""
"Quota fuori tolleranza: serve l'autorizzazione del capoturno, oppure " "Quota fuori tolleranza: serve l'autorizzazione del capoturno, oppure "
"misurare di nuovo la quota" "misurare di nuovo la quota"
msgstr "Quota fuori tolleranza: serve l'autorizzazione del capoturno, oppure misurare di nuovo la quota" msgstr ""
"Quota fuori tolleranza: serve l'autorizzazione del capoturno, oppure "
"misurare di nuovo la quota"
#: templates/measure/task_execute.html:1357 #: templates/measure/task_execute.html:1418
msgid "Errore di comunicazione con il server" msgid "Errore di comunicazione con il server"
msgstr "Errore di comunicazione con il server" msgstr "Errore di comunicazione con il server"
#: templates/measure/task_execute.html:1609 #: templates/measure/task_execute.html:1670
msgid "Misurazione fuori tolleranza" msgid "Misurazione fuori tolleranza"
msgstr "Misurazione fuori tolleranza" msgstr "Misurazione fuori tolleranza"
#: templates/measure/task_execute.html:1610 #: templates/measure/task_execute.html:1671
msgid "Fermo linea richiesto" msgid "Fermo linea richiesto"
msgstr "Fermo linea richiesto" msgstr "Fermo linea richiesto"
#: templates/measure/task_execute.html:1611 #: templates/measure/task_execute.html:1672
msgid "Ripresa della produzione" msgid "Ripresa della produzione"
msgstr "Ripresa della produzione" msgstr "Ripresa della produzione"
#: templates/measure/task_execute.html:1612 #: templates/measure/task_execute.html:1673
msgid "Fine produzione richiesta" msgid "Fine produzione richiesta"
msgstr "Fine produzione richiesta" msgstr "Fine produzione richiesta"
#: templates/measure/task_execute.html:1656 #: templates/measure/task_execute.html:1717
msgid "Credenziali non valide o utente non autorizzato" msgid "Credenziali non valide o utente non autorizzato"
msgstr "Credenziali non valide o utente non autorizzato" msgstr "Credenziali non valide o utente non autorizzato"
#: templates/measure/task_execute.html:1683 #: templates/measure/task_execute.html:1744
msgid "Nessuna produzione aperta su questa stazione" msgid "Nessuna produzione aperta su questa stazione"
msgstr "Nessuna produzione aperta su questa stazione" msgstr "Nessuna produzione aperta su questa stazione"
#: templates/measure/task_execute.html:1700 #: templates/measure/task_execute.html:1761
msgid "Azione non riuscita" msgid "Azione non riuscita"
msgstr "Azione non riuscita" msgstr "Azione non riuscita"
#: templates/measure/task_list.html:107
msgid "AVVIA"
msgstr "AVVIA"
#: templates/measure/task_list.html:145 #: templates/measure/task_list.html:145
msgid "Task da eseguire" msgid "Task da eseguire"
msgstr "Task da eseguire" msgstr "Task da eseguire"
@@ -2260,30 +2303,32 @@ msgstr "Task da eseguire"
msgid "misurazioni totali" msgid "misurazioni totali"
msgstr "misurazioni totali" msgstr "misurazioni totali"
#: templates/measure/task_list.html:199 #: templates/measure/task_list.html:204
msgid "Confronto profilo" msgid "Confronto profilo"
msgstr "Confronto profilo" msgstr "Confronto profilo"
#: templates/measure/task_list.html:200 #: templates/measure/task_list.html:205
msgid "Misura camera" msgid "Misura camera"
msgstr "Misura camera" msgstr "Misura camera"
#: templates/measure/task_list.html:222 #: templates/measure/task_list.html:232
msgid "Incompiuto"
msgstr "Incompiuto"
#: templates/measure/task_list.html:247
msgid "Allegato" msgid "Allegato"
msgstr "Allegato" msgstr "Allegato"
#: templates/measure/task_list.html:246 #: templates/measure/task_list.html:275
msgid "Visualizza Task"
msgstr "Visualizza Task"
#: templates/measure/task_list.html:269 msgid "Visualizza singolo TASK"
msgstr "Visualizza singolo TASK"
#: templates/measure/task_list.html:294
msgid "Nessun task disponibile" msgid "Nessun task disponibile"
msgstr "Nessun task disponibile" msgstr "Nessun task disponibile"
#: templates/measure/task_list.html:272
msgid "Questa ricetta non ha ancora task definiti."
msgstr "Questa ricetta non ha ancora task definiti."
# SPC Statistics Dashboard # SPC Statistics Dashboard
#: templates/statistics/dashboard.html:3 templates/statistics/dashboard.html:17 #: templates/statistics/dashboard.html:3 templates/statistics/dashboard.html:17
msgid "Statistiche SPC" msgid "Statistiche SPC"
@@ -2688,3 +2733,6 @@ msgstr "Errore nella generazione del report"
#~ msgid "Utente non autorizzato (richiesto capoturno)" #~ msgid "Utente non autorizzato (richiesto capoturno)"
#~ msgstr "Utente non autorizzato (richiesto capoturno)" #~ msgstr "Utente non autorizzato (richiesto capoturno)"
#~ msgid "Errore nel caricamento dei task: %(detail)s"
#~ msgstr "Errore nel caricamento dei task: %(detail)s"
+228 -193
View File
@@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PROJECT VERSION\n" "Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2026-07-28 20:48+0000\n" "POT-Creation-Date: 2026-07-28 21:28+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -126,43 +126,62 @@ msgstr ""
msgid "Errore nel caricamento delle ricette: %(detail)s" msgid "Errore nel caricamento delle ricette: %(detail)s"
msgstr "" msgstr ""
#: blueprints/measure.py:146 blueprints/measure.py:237 #: blueprints/measure.py:139 templates/measure/select_recipe.html:30
#: templates/measure/task_list.html:86
msgid "lotto"
msgstr ""
#: blueprints/measure.py:141 templates/measure/select_recipe.html:31
#: templates/measure/task_list.html:87
msgid "seriale"
msgstr ""
#: blueprints/measure.py:204 blueprints/measure.py:242
#: blueprints/measure.py:330
#, python-format #, python-format
msgid "Ricetta non trovata: %(detail)s" msgid "Ricetta non trovata: %(detail)s"
msgstr "" msgstr ""
#: blueprints/measure.py:156 #: blueprints/measure.py:213
#, python-format #, python-format
msgid "Errore nel caricamento dei task: %(detail)s" msgid "Compila prima: %(fields)s"
msgstr "" msgstr ""
#: blueprints/measure.py:185 #: blueprints/measure.py:222 templates/measure/task_list.html:297
msgid "Questa ricetta non ha ancora task definiti."
msgstr ""
#: blueprints/measure.py:250
msgid "Errore nel caricamento dei task."
msgstr ""
#: blueprints/measure.py:278
#, python-format #, python-format
msgid "Task non trovato: %(detail)s" msgid "Task non trovato: %(detail)s"
msgstr "" msgstr ""
#: blueprints/measure.py:306 #: blueprints/measure.py:399
msgid "Codice non fornito" msgid "Codice non fornito"
msgstr "" msgstr ""
#: blueprints/measure.py:312 templates/measure/select_recipe.html:57 #: blueprints/measure.py:405 templates/measure/select_recipe.html:60
msgid "Ricetta non trovata" msgid "Ricetta non trovata"
msgstr "" msgstr ""
#: blueprints/measure.py:368 #: blueprints/measure.py:461
msgid "Dati mancanti: subtask_id, version_id e value sono obbligatori" msgid "Dati mancanti: subtask_id, version_id e value sono obbligatori"
msgstr "" msgstr ""
#: blueprints/measure.py:393 templates/admin/stations.html:549 #: blueprints/measure.py:486 templates/admin/stations.html:549
#: templates/maker/recipe_editor.html:598 #: templates/maker/recipe_editor.html:598
msgid "Errore nel salvataggio" msgid "Errore nel salvataggio"
msgstr "" msgstr ""
#: blueprints/measure.py:437 blueprints/measure.py:542 #: blueprints/measure.py:530 blueprints/measure.py:635
msgid "Username e password richiesti" msgid "Username e password richiesti"
msgstr "" msgstr ""
#: blueprints/measure.py:463 blueprints/measure.py:480 #: blueprints/measure.py:556 blueprints/measure.py:573
#: templates/errors/station_not_configured.html:2 #: templates/errors/station_not_configured.html:2
#: templates/errors/station_not_configured.html:16 #: templates/errors/station_not_configured.html:16
msgid "Stazione non configurata" msgid "Stazione non configurata"
@@ -364,8 +383,8 @@ msgstr ""
#: templates/maker/task_editor.html:312 templates/maker/task_editor.html:421 #: templates/maker/task_editor.html:312 templates/maker/task_editor.html:421
#: templates/maker/task_editor.html:782 templates/maker/task_editor.html:901 #: templates/maker/task_editor.html:782 templates/maker/task_editor.html:901
#: templates/maker/task_editor.html:966 templates/maker/task_editor.html:1071 #: templates/maker/task_editor.html:966 templates/maker/task_editor.html:1071
#: templates/measure/select_recipe.html:405 #: templates/measure/select_recipe.html:413
#: templates/measure/task_execute.html:860 #: templates/measure/task_execute.html:887
msgid "Annulla" msgid "Annulla"
msgstr "" msgstr ""
@@ -381,7 +400,7 @@ msgstr ""
#: templates/admin/stations.html:241 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:107
#: templates/maker/recipe_editor.html:490 templates/maker/task_drawing.html:61 #: templates/maker/recipe_editor.html:490 templates/maker/task_drawing.html:61
#: templates/measure/task_execute.html:412 #: templates/measure/task_execute.html:424
msgid "Salvataggio..." msgid "Salvataggio..."
msgstr "" msgstr ""
@@ -509,7 +528,7 @@ msgstr ""
#: templates/admin/users.html:48 templates/admin/users.html:173 #: templates/admin/users.html:48 templates/admin/users.html:173
#: templates/admin/users.html:179 templates/auth/login.html:35 #: templates/admin/users.html:179 templates/auth/login.html:35
#: templates/auth/login.html:49 templates/auth/profile.html:36 #: templates/auth/login.html:49 templates/auth/profile.html:36
#: templates/measure/task_execute.html:842 #: templates/measure/task_execute.html:869
msgid "Username" msgid "Username"
msgstr "" msgstr ""
@@ -561,7 +580,7 @@ msgstr ""
#: templates/admin/users.html:206 templates/admin/users.html:214 #: templates/admin/users.html:206 templates/admin/users.html:214
#: templates/auth/login.html:57 templates/auth/login.html:71 #: templates/auth/login.html:57 templates/auth/login.html:71
#: templates/measure/task_execute.html:847 #: templates/measure/task_execute.html:874
msgid "Password" msgid "Password"
msgstr "" msgstr ""
@@ -712,8 +731,8 @@ msgstr ""
#: templates/components/barcode_scanner.html:21 #: templates/components/barcode_scanner.html:21
#: templates/components/barcode_scanner.html:54 #: templates/components/barcode_scanner.html:54
#: templates/measure/select_recipe.html:128 #: templates/measure/select_recipe.html:131
#: templates/measure/select_recipe.html:357 #: templates/measure/select_recipe.html:365
msgid "Scansiona Barcode" msgid "Scansiona Barcode"
msgstr "" msgstr ""
@@ -807,14 +826,14 @@ msgid "Misura solo da calibro: questa ricetta non ammette valori digitati"
msgstr "" msgstr ""
#: templates/components/production_clock.html:42 #: templates/components/production_clock.html:42
#: templates/measure/task_execute.html:460 #: templates/measure/task_execute.html:472
msgid "Prossima misurazione tra" msgid "Prossima misurazione tra"
msgstr "" msgstr ""
#: templates/components/production_clock.html:49 #: templates/components/production_clock.html:49
#: templates/components/production_clock.html:62 #: templates/components/production_clock.html:62
#: templates/measure/task_execute.html:463 #: templates/measure/task_execute.html:475
#: templates/measure/task_execute.html:542 #: templates/measure/task_execute.html:554
msgid "Misurazione in ritardo di" msgid "Misurazione in ritardo di"
msgstr "" msgstr ""
@@ -823,27 +842,27 @@ msgid "Linea ferma — conto alla rovescia congelato a"
msgstr "" msgstr ""
#: templates/components/production_clock.html:62 #: templates/components/production_clock.html:62
#: templates/measure/task_execute.html:536 #: templates/measure/task_execute.html:548
msgid "Linea ferma" msgid "Linea ferma"
msgstr "" msgstr ""
#: templates/components/production_clock.html:74 #: templates/components/production_clock.html:74
#: templates/measure/task_execute.html:472 #: templates/measure/task_execute.html:484
msgid "Ciclo" msgid "Ciclo"
msgstr "" msgstr ""
#: templates/components/production_clock.html:92 #: templates/components/production_clock.html:92
#: templates/measure/task_execute.html:704 #: templates/measure/task_execute.html:731
msgid "È ora di misurare" msgid "È ora di misurare"
msgstr "" msgstr ""
#: templates/components/production_clock.html:95 #: templates/components/production_clock.html:95
#: templates/measure/task_execute.html:706 #: templates/measure/task_execute.html:733
msgid "Ritorno alla misurazione tra" msgid "Ritorno alla misurazione tra"
msgstr "" msgstr ""
#: templates/components/production_clock.html:101 #: templates/components/production_clock.html:101
#: templates/measure/task_execute.html:711 #: templates/measure/task_execute.html:738
msgid "Vai alla misura" msgid "Vai alla misura"
msgstr "" msgstr ""
@@ -892,8 +911,8 @@ msgstr ""
#: templates/maker/recipe_editor.html:128 templates/maker/recipe_list.html:246 #: templates/maker/recipe_editor.html:128 templates/maker/recipe_list.html:246
#: templates/maker/task_drawing.html:106 templates/maker/task_editor.html:130 #: templates/maker/task_drawing.html:106 templates/maker/task_editor.html:130
#: templates/measure/task_complete.html:171 #: templates/measure/task_complete.html:171
#: templates/measure/task_execute.html:615 templates/measure/task_list.html:2 #: templates/measure/task_execute.html:627 templates/measure/task_list.html:2
#: templates/measure/task_list.html:180 #: templates/measure/task_list.html:185
msgid "Task" msgid "Task"
msgstr "" msgstr ""
@@ -1066,9 +1085,9 @@ msgstr ""
msgid "Errore durante eliminazione" msgid "Errore durante eliminazione"
msgstr "" msgstr ""
#: templates/maker/recipe_list.html:55 templates/measure/select_recipe.html:72 #: templates/maker/recipe_list.html:55 templates/measure/select_recipe.html:75
#: templates/measure/task_execute.html:1362 #: templates/measure/task_execute.html:1423
#: templates/measure/task_execute.html:1672 #: templates/measure/task_execute.html:1733
msgid "Errore di connessione" msgid "Errore di connessione"
msgstr "" msgstr ""
@@ -1101,7 +1120,7 @@ msgid "ricetta trovata"
msgstr "" msgstr ""
#: templates/maker/recipe_list.html:144 #: templates/maker/recipe_list.html:144
#: templates/measure/select_recipe.html:196 #: templates/measure/select_recipe.html:199
msgid "ricette trovate" msgid "ricette trovate"
msgstr "" msgstr ""
@@ -1119,7 +1138,7 @@ msgid "Versioni"
msgstr "" msgstr ""
#: templates/maker/recipe_list.html:297 #: templates/maker/recipe_list.html:297
#: templates/measure/select_recipe.html:311 #: templates/measure/select_recipe.html:319
msgid "Nessuna ricetta trovata" msgid "Nessuna ricetta trovata"
msgstr "" msgstr ""
@@ -1195,7 +1214,7 @@ msgid "Apri PDF"
msgstr "" msgstr ""
#: templates/maker/recipe_preview.html:254 #: templates/maker/recipe_preview.html:254
#: templates/measure/task_execute.html:287 #: templates/measure/task_execute.html:299
msgid "Nessuna immagine allegata" msgid "Nessuna immagine allegata"
msgstr "" msgstr ""
@@ -1206,7 +1225,7 @@ msgstr ""
#: templates/maker/recipe_preview.html:299 templates/maker/task_editor.html:579 #: templates/maker/recipe_preview.html:299 templates/maker/task_editor.html:579
#: templates/maker/task_editor.html:721 #: templates/maker/task_editor.html:721
#: templates/measure/task_complete.html:173 #: templates/measure/task_complete.html:173
#: templates/measure/task_execute.html:321 #: templates/measure/task_execute.html:333
msgid "Nominale" msgid "Nominale"
msgstr "" msgstr ""
@@ -1258,7 +1277,6 @@ msgid "Torna ai Task"
msgstr "" msgstr ""
#: templates/maker/task_drawing.html:205 templates/maker/task_drawing.html:209 #: templates/maker/task_drawing.html:205 templates/maker/task_drawing.html:209
#: templates/measure/select_recipe.html:291
msgid "Seleziona" msgid "Seleziona"
msgstr "" msgstr ""
@@ -1372,17 +1390,17 @@ msgid "Tipo"
msgstr "" msgstr ""
#: templates/maker/task_editor.html:277 templates/maker/task_editor.html:543 #: templates/maker/task_editor.html:277 templates/maker/task_editor.html:543
#: templates/measure/task_list.html:198 #: templates/measure/task_list.html:203
msgid "Nota" msgid "Nota"
msgstr "" msgstr ""
#: templates/maker/task_editor.html:278 templates/maker/task_editor.html:544 #: templates/maker/task_editor.html:278 templates/maker/task_editor.html:544
#: templates/measure/task_execute.html:309 templates/measure/task_list.html:196 #: templates/measure/task_execute.html:321 templates/measure/task_list.html:201
msgid "Misura" msgid "Misura"
msgstr "" msgstr ""
#: templates/maker/task_editor.html:279 templates/maker/task_editor.html:545 #: templates/maker/task_editor.html:279 templates/maker/task_editor.html:545
#: templates/measure/task_list.html:197 #: templates/measure/task_list.html:202
msgid "Disegno" msgid "Disegno"
msgstr "" msgstr ""
@@ -1413,7 +1431,7 @@ msgstr ""
#: templates/maker/task_editor.html:390 #: templates/maker/task_editor.html:390
#: templates/maker/version_history.html:188 #: templates/maker/version_history.html:188
#: templates/measure/task_list.html:213 #: templates/measure/task_list.html:238
msgid "misurazioni" msgid "misurazioni"
msgstr "" msgstr ""
@@ -1679,7 +1697,7 @@ msgid "Misurazione aggiunta"
msgstr "" msgstr ""
#: templates/maker/task_editor.html:1685 #: templates/maker/task_editor.html:1685
#: templates/measure/task_execute.html:1170 #: templates/measure/task_execute.html:1231
msgid "Errore nel salvataggio della misurazione" msgid "Errore nel salvataggio della misurazione"
msgstr "" msgstr ""
@@ -1738,121 +1756,121 @@ msgid ""
msgstr "" msgstr ""
#: templates/measure/select_recipe.html:2 #: templates/measure/select_recipe.html:2
#: templates/measure/select_recipe.html:94 #: templates/measure/select_recipe.html:97
msgid "Seleziona Ricetta" msgid "Seleziona Ricetta"
msgstr "" msgstr ""
#: templates/measure/select_recipe.html:30 templates/measure/task_list.html:86 #: templates/measure/select_recipe.html:67
msgid "lotto" #: templates/measure/select_recipe.html:280 templates/measure/task_list.html:97
msgstr ""
#: templates/measure/select_recipe.html:31 templates/measure/task_list.html:87
msgid "seriale"
msgstr ""
#: templates/measure/select_recipe.html:64
#: templates/measure/select_recipe.html:277 templates/measure/task_list.html:97
msgid "Compila prima:" msgid "Compila prima:"
msgstr "" msgstr ""
#: templates/measure/select_recipe.html:97 #: templates/measure/select_recipe.html:100
msgid "Scegli la ricetta di misura da eseguire" msgid "Scegli la ricetta di misura da eseguire"
msgstr "" msgstr ""
#: templates/measure/select_recipe.html:100 #: templates/measure/select_recipe.html:103
msgid "Stazione" msgid "Stazione"
msgstr "" msgstr ""
#: templates/measure/select_recipe.html:110 #: templates/measure/select_recipe.html:113
msgid "Stazione forzata per collaudo" msgid "Stazione forzata per collaudo"
msgstr "" msgstr ""
#: templates/measure/select_recipe.html:110 #: templates/measure/select_recipe.html:113
msgid "configurata" msgid "configurata"
msgstr "" msgstr ""
#: templates/measure/select_recipe.html:115 #: templates/measure/select_recipe.html:118
msgid "Ripristina" msgid "Ripristina"
msgstr "" msgstr ""
#: templates/measure/select_recipe.html:140 #: templates/measure/select_recipe.html:143
msgid "Cerca ricetta" msgid "Cerca ricetta"
msgstr "" msgstr ""
#: templates/measure/select_recipe.html:144 #: templates/measure/select_recipe.html:147
msgid "Nome, codice o descrizione..." msgid "Nome, codice o descrizione..."
msgstr "" msgstr ""
#: templates/measure/select_recipe.html:157 #: templates/measure/select_recipe.html:160
msgid "Tracciabilità" msgid "Tracciabilità"
msgstr "" msgstr ""
#: templates/measure/select_recipe.html:158 #: templates/measure/select_recipe.html:161
msgid "Dati del pezzo da misurare — compila prima di selezionare la ricetta" msgid "Dati del pezzo da misurare — compila prima di selezionare la ricetta"
msgstr "" msgstr ""
#: templates/measure/select_recipe.html:168 #: templates/measure/select_recipe.html:171
#: templates/measure/task_complete.html:284 #: templates/measure/task_complete.html:284
msgid "Numero Lotto" msgid "Numero Lotto"
msgstr "" msgstr ""
#: templates/measure/select_recipe.html:172 #: templates/measure/select_recipe.html:175
msgid "Es. LOT-2026-001" msgid "Es. LOT-2026-001"
msgstr "" msgstr ""
#: templates/measure/select_recipe.html:182 #: templates/measure/select_recipe.html:185
#: templates/measure/task_complete.html:285 #: templates/measure/task_complete.html:285
msgid "Numero Seriale" msgid "Numero Seriale"
msgstr "" msgstr ""
#: templates/measure/select_recipe.html:186 #: templates/measure/select_recipe.html:189
msgid "Es. SN-000123" msgid "Es. SN-000123"
msgstr "" msgstr ""
#: templates/measure/select_recipe.html:239 #: templates/measure/select_recipe.html:242
msgid "Nessuna descrizione disponibile" msgid "Nessuna descrizione disponibile"
msgstr "" msgstr ""
#: templates/measure/select_recipe.html:315 #: templates/measure/select_recipe.html:294
#: templates/measure/task_list.html:107
msgid "AVVIA IN SEQUENZA"
msgstr ""
#: templates/measure/select_recipe.html:302
msgid "Vedi tutti i task"
msgstr ""
#: templates/measure/select_recipe.html:323
msgid "Nessun risultato per" msgid "Nessun risultato per"
msgstr "" msgstr ""
#: templates/measure/select_recipe.html:316 #: templates/measure/select_recipe.html:324
msgid "Prova con un termine diverso." msgid "Prova con un termine diverso."
msgstr "" msgstr ""
#: templates/measure/select_recipe.html:319 #: templates/measure/select_recipe.html:327
msgid "Non ci sono ricette disponibili al momento." msgid "Non ci sono ricette disponibili al momento."
msgstr "" msgstr ""
#: templates/measure/select_recipe.html:372 #: templates/measure/select_recipe.html:380
msgid "" msgid ""
"Inserisci o scansiona il codice della ricetta per selezionarla " "Inserisci o scansiona il codice della ricetta per selezionarla "
"automaticamente." "automaticamente."
msgstr "" msgstr ""
#: templates/measure/select_recipe.html:377 #: templates/measure/select_recipe.html:385
msgid "Codice Ricetta" msgid "Codice Ricetta"
msgstr "" msgstr ""
#: templates/measure/select_recipe.html:383 #: templates/measure/select_recipe.html:391
msgid "Es. REC-001" msgid "Es. REC-001"
msgstr "" msgstr ""
#: templates/measure/select_recipe.html:419 #: templates/measure/select_recipe.html:427
msgid "Cerca" msgid "Cerca"
msgstr "" msgstr ""
#: templates/measure/task_complete.html:3 #: templates/measure/task_complete.html:3
#: templates/measure/task_complete.html:39 #: templates/measure/task_complete.html:39
#: templates/measure/task_execute.html:113 #: templates/measure/task_execute.html:792
#: templates/measure/task_execute.html:765
#: templates/statistics/dashboard.html:139 #: templates/statistics/dashboard.html:139
msgid "Riepilogo" msgid "Riepilogo"
msgstr "" msgstr ""
#: templates/measure/task_complete.html:47 #: templates/measure/task_complete.html:47
#: templates/measure/task_execute.html:742 #: templates/measure/task_execute.html:769
msgid "Misurazioni Complete" msgid "Misurazioni Complete"
msgstr "" msgstr ""
@@ -1873,12 +1891,12 @@ msgid "Totale"
msgstr "" msgstr ""
#: templates/measure/task_complete.html:106 #: templates/measure/task_complete.html:106
#: templates/measure/task_execute.html:750 #: templates/measure/task_execute.html:777
msgid "Conformi" msgid "Conformi"
msgstr "" msgstr ""
#: templates/measure/task_complete.html:123 #: templates/measure/task_complete.html:123
#: templates/measure/task_execute.html:754 #: templates/measure/task_execute.html:781
msgid "Attenzione" msgid "Attenzione"
msgstr "" msgstr ""
@@ -2040,175 +2058,192 @@ msgstr ""
msgid "Task di misurazione" msgid "Task di misurazione"
msgstr "" msgstr ""
#: templates/measure/task_execute.html:106 #: templates/measure/task_execute.html:115
msgid "Lista task" msgid "Lista task"
msgstr "" msgstr ""
#: templates/measure/task_execute.html:128 #: templates/measure/task_execute.html:125
msgid "Fermo linea" #: templates/measure/task_execute.html:706 templates/measure/task_list.html:227
msgstr ""
#: templates/measure/task_execute.html:136
msgid "Riprendi"
msgstr ""
#: templates/measure/task_execute.html:144
msgid "Fine Produzione"
msgstr ""
#: templates/measure/task_execute.html:259
msgid "Immagine dettaglio misura"
msgstr ""
#: templates/measure/task_execute.html:307
msgid "Misurazione"
msgstr ""
#: templates/measure/task_execute.html:385
msgid "Registrata"
msgstr ""
#: templates/measure/task_execute.html:491
msgid "Task di misura senza quote configurate: la ricetta è incompleta"
msgstr ""
#: templates/measure/task_execute.html:510
msgid "Quota fuori tolleranza"
msgstr ""
#: templates/measure/task_execute.html:513
msgid "serve il capoturno, oppure una nuova misura della stessa quota"
msgstr ""
#: templates/measure/task_execute.html:517
#: templates/measure/task_execute.html:870
msgid "Autorizza"
msgstr ""
#: templates/measure/task_execute.html:541
msgid "Il conto alla rovescia è congelato a"
msgstr ""
#: templates/measure/task_execute.html:544
msgid "serve il capoturno per riprendere"
msgstr ""
#: templates/measure/task_execute.html:563
msgid "Produzione non registrata sul server"
msgstr ""
#: templates/measure/task_execute.html:584
#: templates/measure/task_execute.html:793
msgid "Avvio Produzione"
msgstr ""
#: templates/measure/task_execute.html:588
msgid "Invia segnale al gestionale per avviare il timer della linea"
msgstr ""
#: templates/measure/task_execute.html:599
msgid "Produzione avviata"
msgstr ""
#: templates/measure/task_execute.html:644
msgid "Fine ciclo misura"
msgstr ""
#: templates/measure/task_execute.html:655
msgid "Rimisura"
msgstr ""
#: templates/measure/task_execute.html:665
#: templates/measure/task_execute.html:679
msgid "Completato" msgid "Completato"
msgstr "" msgstr ""
#: templates/measure/task_execute.html:744 #: templates/measure/task_execute.html:140
msgid "Tutte le" msgid "Fermo linea"
msgstr "" msgstr ""
#: templates/measure/task_execute.html:744 #: templates/measure/task_execute.html:148
msgid "misurazioni sono state registrate." msgid "Riprendi"
msgstr "" msgstr ""
#: templates/measure/task_execute.html:758 #: templates/measure/task_execute.html:156
msgid "Non Conf." msgid "Fine Produzione"
msgstr "" msgstr ""
#: templates/measure/task_execute.html:773 #: templates/measure/task_execute.html:271
msgid "Conferma ciclo" msgid "Immagine dettaglio misura"
msgstr "" msgstr ""
#: templates/measure/task_execute.html:783 #: templates/measure/task_execute.html:319
msgid "Misurazione"
msgstr ""
#: templates/measure/task_execute.html:397
msgid "Registrata"
msgstr ""
#: templates/measure/task_execute.html:503
msgid "Task di misura senza quote configurate: la ricetta è incompleta"
msgstr ""
#: templates/measure/task_execute.html:522
msgid "Quota fuori tolleranza"
msgstr ""
#: templates/measure/task_execute.html:525
msgid "serve il capoturno, oppure una nuova misura della stessa quota"
msgstr ""
#: templates/measure/task_execute.html:529
#: templates/measure/task_execute.html:897
msgid "Autorizza"
msgstr ""
#: templates/measure/task_execute.html:553
msgid "Il conto alla rovescia è congelato a"
msgstr ""
#: templates/measure/task_execute.html:556
msgid "serve il capoturno per riprendere"
msgstr ""
#: templates/measure/task_execute.html:575
msgid "Produzione non registrata sul server"
msgstr ""
#: templates/measure/task_execute.html:596
#: templates/measure/task_execute.html:820
msgid "Avvio Produzione"
msgstr ""
#: templates/measure/task_execute.html:600
msgid "Invia segnale al gestionale per avviare il timer della linea"
msgstr ""
#: templates/measure/task_execute.html:611
msgid "Produzione avviata"
msgstr ""
#: templates/measure/task_execute.html:667
msgid "Fine ciclo misura"
msgstr ""
#: templates/measure/task_execute.html:679
msgid "Rimisura"
msgstr ""
#: templates/measure/task_execute.html:692
#: templates/measure/task_execute.html:810
msgid "Task successivo" msgid "Task successivo"
msgstr "" msgstr ""
#: templates/measure/task_execute.html:809 #: templates/measure/task_execute.html:771
msgid "Tutte le"
msgstr ""
#: templates/measure/task_execute.html:771
msgid "misurazioni sono state registrate."
msgstr ""
#: templates/measure/task_execute.html:785
msgid "Non Conf."
msgstr ""
#: templates/measure/task_execute.html:800
msgid "Conferma ciclo"
msgstr ""
#: templates/measure/task_execute.html:836
msgid "Girare il pezzo e rimisurare" msgid "Girare il pezzo e rimisurare"
msgstr "" msgstr ""
#: templates/measure/task_execute.html:835 #: templates/measure/task_execute.html:862
msgid "Autorizzazione capoturno" msgid "Autorizzazione capoturno"
msgstr "" msgstr ""
#: templates/measure/task_execute.html:844 #: templates/measure/task_execute.html:871
msgid "Username capoturno" msgid "Username capoturno"
msgstr "" msgstr ""
#: templates/measure/task_execute.html:861 #: templates/measure/task_execute.html:888
msgid "Rimisura la quota" msgid "Rimisura la quota"
msgstr "" msgstr ""
#: templates/measure/task_execute.html:1125 #: templates/measure/task_execute.html:1027
msgid "Manca 1 quota"
msgstr ""
#: templates/measure/task_execute.html:1028
msgid "Mancano"
msgstr ""
#: templates/measure/task_execute.html:1028
msgid "quote"
msgstr ""
#: templates/measure/task_execute.html:1046
msgid "Serve l'autorizzazione del capoturno"
msgstr ""
#: templates/measure/task_execute.html:1049
msgid "Chiudi prima il ciclo di misura"
msgstr ""
#: templates/measure/task_execute.html:1186
msgid "Questa ricetta non ammette valori digitati: usare il calibro" msgid "Questa ricetta non ammette valori digitati: usare il calibro"
msgstr "" msgstr ""
#: templates/measure/task_execute.html:1222 #: templates/measure/task_execute.html:1283
msgid "Errore di rete. Riprovare." msgid "Errore di rete. Riprovare."
msgstr "" msgstr ""
#: templates/measure/task_execute.html:1260 #: templates/measure/task_execute.html:1321
msgid "" msgid ""
"Quota fuori tolleranza: serve l'autorizzazione del capoturno, oppure " "Quota fuori tolleranza: serve l'autorizzazione del capoturno, oppure "
"misurare di nuovo la quota" "misurare di nuovo la quota"
msgstr "" msgstr ""
#: templates/measure/task_execute.html:1357 #: templates/measure/task_execute.html:1418
msgid "Errore di comunicazione con il server" msgid "Errore di comunicazione con il server"
msgstr "" msgstr ""
#: templates/measure/task_execute.html:1609 #: templates/measure/task_execute.html:1670
msgid "Misurazione fuori tolleranza" msgid "Misurazione fuori tolleranza"
msgstr "" msgstr ""
#: templates/measure/task_execute.html:1610 #: templates/measure/task_execute.html:1671
msgid "Fermo linea richiesto" msgid "Fermo linea richiesto"
msgstr "" msgstr ""
#: templates/measure/task_execute.html:1611 #: templates/measure/task_execute.html:1672
msgid "Ripresa della produzione" msgid "Ripresa della produzione"
msgstr "" msgstr ""
#: templates/measure/task_execute.html:1612 #: templates/measure/task_execute.html:1673
msgid "Fine produzione richiesta" msgid "Fine produzione richiesta"
msgstr "" msgstr ""
#: templates/measure/task_execute.html:1656 #: templates/measure/task_execute.html:1717
msgid "Credenziali non valide o utente non autorizzato" msgid "Credenziali non valide o utente non autorizzato"
msgstr "" msgstr ""
#: templates/measure/task_execute.html:1683 #: templates/measure/task_execute.html:1744
msgid "Nessuna produzione aperta su questa stazione" msgid "Nessuna produzione aperta su questa stazione"
msgstr "" msgstr ""
#: templates/measure/task_execute.html:1700 #: templates/measure/task_execute.html:1761
msgid "Azione non riuscita" msgid "Azione non riuscita"
msgstr "" msgstr ""
#: templates/measure/task_list.html:107
msgid "AVVIA"
msgstr ""
#: templates/measure/task_list.html:145 #: templates/measure/task_list.html:145
msgid "Task da eseguire" msgid "Task da eseguire"
msgstr "" msgstr ""
@@ -2217,30 +2252,30 @@ msgstr ""
msgid "misurazioni totali" msgid "misurazioni totali"
msgstr "" msgstr ""
#: templates/measure/task_list.html:199 #: templates/measure/task_list.html:204
msgid "Confronto profilo" msgid "Confronto profilo"
msgstr "" msgstr ""
#: templates/measure/task_list.html:200 #: templates/measure/task_list.html:205
msgid "Misura camera" msgid "Misura camera"
msgstr "" msgstr ""
#: templates/measure/task_list.html:222 #: templates/measure/task_list.html:232
msgid "Incompiuto"
msgstr ""
#: templates/measure/task_list.html:247
msgid "Allegato" msgid "Allegato"
msgstr "" msgstr ""
#: templates/measure/task_list.html:246 #: templates/measure/task_list.html:275
msgid "Visualizza Task" msgid "Visualizza singolo TASK"
msgstr "" msgstr ""
#: templates/measure/task_list.html:269 #: templates/measure/task_list.html:294
msgid "Nessun task disponibile" msgid "Nessun task disponibile"
msgstr "" msgstr ""
#: templates/measure/task_list.html:272
msgid "Questa ricetta non ha ancora task definiti."
msgstr ""
#: templates/statistics/dashboard.html:3 templates/statistics/dashboard.html:17 #: templates/statistics/dashboard.html:3 templates/statistics/dashboard.html:17
msgid "Statistiche SPC" msgid "Statistiche SPC"
msgstr "" msgstr ""