diff --git a/src/frontend/flask_app/static/js/vision_execute.js b/src/frontend/flask_app/static/js/vision_execute.js
index 4670f49..176a2d8 100644
--- a/src/frontend/flask_app/static/js/vision_execute.js
+++ b/src/frontend/flask_app/static/js/vision_execute.js
@@ -266,6 +266,8 @@ function visionExecute() {
value: m.value,
unit: st.unit || 'mm',
status: m.pass_fail,
+ // #117: nominale e tolleranze della quota (come la schermata dei calibri); null se mancano
+ nominal: st.nominal, ltl: st.ltl, lwl: st.lwl, uwl: st.uwl, utl: st.utl,
};
});
},
@@ -379,6 +381,9 @@ function visionExecute() {
},
zPanEnd() { this._zPan = null; this.zPanning = false; },
+ // #117: tolleranza a 3 decimali, trattino se manca (come x-text="...?.toFixed(3) || '—'" dei calibri, ma 0 resta 0.000)
+ fmtTol(v) { return v == null ? '\u2014' : Number(v).toFixed(3); },
+
statusClass(s) {
// Same palette as the measurement markers of the manual flow.
if (s === 'pass') return 'bg-measure-pass/20 text-measure-pass border border-measure-pass/40';
diff --git a/src/frontend/flask_app/templates/measure/task_execute.html b/src/frontend/flask_app/templates/measure/task_execute.html
index ab5631e..7d77fa5 100644
--- a/src/frontend/flask_app/templates/measure/task_execute.html
+++ b/src/frontend/flask_app/templates/measure/task_execute.html
@@ -609,12 +609,28 @@
+ {# #117: due righe per quota (esito, poi nominale e tolleranze come nella schermata dei calibri); un solo tbody per quota perche' x-for vuole un'unica radice #}
-
- |
- |
- |
-
+
+
+ |
+ |
+ |
+
+
+ |
+ {{ _('Nom') }}
+ LTL
+ UTL
+
+
+ LWL
+ UWL
+
+
+ |
+
+
diff --git a/src/frontend/flask_app/tests/js/test_vision_rows.js b/src/frontend/flask_app/tests/js/test_vision_rows.js
index ee40899..358c6d7 100644
--- a/src/frontend/flask_app/tests/js/test_vision_rows.js
+++ b/src/frontend/flask_app/tests/js/test_vision_rows.js
@@ -46,4 +46,29 @@ check('#114 riga 99: subtask sconosciuta', rows[3].label === '#99', rows[3].labe
check('#114 unit riga 17 = mm', rows[2].unit === 'mm', rows[2].unit);
check('#114 unit riga 99 = mm', rows[3].unit === 'mm', rows[3].unit);
+// #117: nominale e tolleranze portati dalla quota; fmtTol
+win.__visionCfg = {
+ subtasks: [
+ { id: 20, description: 'Largh', unit: 'mm', nominal: 12.5, ltl: 12.4, utl: 12.6, lwl: 12.45, uwl: 12.55 },
+ { id: 21, description: 'Diam', unit: 'mm', nominal: 0, ltl: null, utl: 0.05 },
+ { id: 22, description: 'Nuda', unit: 'mm' },
+ ],
+};
+const c2 = ctx.visionExecute();
+c2.result = { measurements: [
+ { subtask_id: 20, value: 12.5, pass_fail: 'pass' },
+ { subtask_id: 21, value: 0, pass_fail: 'pass' },
+ { subtask_id: 22, value: 1, pass_fail: 'pass' },
+] };
+const r2 = c2.rows;
+check('#117 riga 20: nominale e tolleranze',
+ r2[0].nominal === 12.5 && r2[0].ltl === 12.4 && r2[0].utl === 12.6 && r2[0].lwl === 12.45 && r2[0].uwl === 12.55);
+check('#117 riga 21: nominale 0 resta 0, ltl null', r2[1].nominal === 0 && r2[1].ltl == null && r2[1].utl === 0.05);
+check('#117 riga 22: senza tolleranze sono undefined/null', r2[2].nominal == null && r2[2].utl == null && r2[2].lwl == null);
+check('#117 label invariato', r2[0].label === 'Largh', r2[0].label);
+check('#117 fmtTol(null) = trattino', c2.fmtTol(null) === '\u2014', c2.fmtTol(null));
+check('#117 fmtTol(undefined) = trattino', c2.fmtTol(undefined) === '\u2014');
+check('#117 fmtTol(0.05) = 0.050', c2.fmtTol(0.05) === '0.050', c2.fmtTol(0.05));
+check('#117 fmtTol(0) = 0.000', c2.fmtTol(0) === '0.000', c2.fmtTol(0));
+
process.exit(failures ? 1 : 0);
diff --git a/src/frontend/flask_app/tests/test_vision_panel_fit.py b/src/frontend/flask_app/tests/test_vision_panel_fit.py
index 19a688e..f02645e 100644
--- a/src/frontend/flask_app/tests/test_vision_panel_fit.py
+++ b/src/frontend/flask_app/tests/test_vision_panel_fit.py
@@ -63,3 +63,11 @@ def test_image_box_and_outcome_column_can_shrink():
assert "flex-1" in box and "min-h-0" in box
outcome = _tag_with(panel, 'x-show="result">')
assert "overflow-y-auto" in outcome and "min-h-0" in outcome
+
+
+def test_quote_table_shows_nominal_and_tolerances():
+ # #117: la tabella quote del pannello mostra nominale e tolleranze come la schermata dei calibri
+ panel = _panel()
+ for marker in ("fmtTol(r.nominal)", "fmtTol(r.ltl)", "fmtTol(r.utl)", "fmtTol(r.lwl)", "fmtTol(r.uwl)"):
+ assert marker in panel, marker
+ assert "text-measure-fail" in panel and "text-measure-warning" in panel