feat(client): filter select_recipe by STATION_CODE with error fallback
Replace generic /api/recipes call with api_client.get_station_recipes(STATION_CODE). Return 503 station_not_configured.html when STATION_CODE env var is unset. Add station indicator to recipe selection page header. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -22,8 +22,18 @@ measure_bp = Blueprint("measure", __name__)
|
||||
@role_required("MeasurementTec")
|
||||
def select_recipe():
|
||||
"""Recipe selection page with search and barcode support."""
|
||||
# Load recipes from API
|
||||
resp = api_client.get("/api/recipes", params={"per_page": 100})
|
||||
# Fail-fast if STATION_CODE is not configured
|
||||
if not Config.STATION_CODE:
|
||||
return render_template("errors/station_not_configured.html"), 503
|
||||
|
||||
# Load recipes filtered by station
|
||||
try:
|
||||
resp = api_client.get_station_recipes(Config.STATION_CODE)
|
||||
except Exception as e:
|
||||
return render_template(
|
||||
"errors/station_not_configured.html", error=str(e),
|
||||
), 502
|
||||
|
||||
if isinstance(resp, dict) and resp.get("error"):
|
||||
flash(
|
||||
_("Errore nel caricamento delle ricette: %(detail)s",
|
||||
@@ -43,6 +53,7 @@ def select_recipe():
|
||||
return render_template(
|
||||
"measure/select_recipe.html",
|
||||
recipes=recipes,
|
||||
station_code=Config.STATION_CODE,
|
||||
auto_recipe_code=auto_recipe_code,
|
||||
auto_lot=auto_lot,
|
||||
auto_serial=auto_serial,
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}{{ _('Stazione non configurata') }} — TieMeasureFlow{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container mx-auto px-4 py-8 max-w-2xl">
|
||||
<div class="mt-20 p-8 bg-[var(--bg-card)] rounded-xl shadow-lg text-center border border-red-200 dark:border-red-800">
|
||||
<div class="inline-flex items-center justify-center w-16 h-16 rounded-full
|
||||
bg-red-50 dark:bg-red-900/30 mb-6">
|
||||
<svg class="w-8 h-8 text-measure-fail" fill="none" stroke="currentColor" stroke-width="1.75" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126zM12 15.75h.007v.008H12v-.008z"/>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<h1 class="text-2xl font-bold text-measure-fail mb-4">
|
||||
{{ _('Stazione non configurata') }}
|
||||
</h1>
|
||||
|
||||
<p class="mb-4 text-[var(--text-primary)]">
|
||||
{{ _('Questo client non ha impostato la variabile di ambiente STATION_CODE.') }}
|
||||
</p>
|
||||
|
||||
<p class="mb-6 text-sm text-[var(--text-secondary)]">
|
||||
{{ _('Contattare il responsabile IT: il file .env del container deve contenere STATION_CODE con il codice della stazione assegnata.') }}
|
||||
</p>
|
||||
|
||||
{% if error %}
|
||||
<pre class="text-xs text-left text-[var(--text-secondary)] bg-[var(--bg-secondary)]
|
||||
p-3 rounded-lg border border-[var(--border-color)] overflow-auto">{{ error }}</pre>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -75,6 +75,9 @@
|
||||
<p class="mt-1 text-sm text-[var(--text-secondary)]">
|
||||
{{ _('Scegli la ricetta di misura da eseguire') }}
|
||||
</p>
|
||||
<p class="mt-1 text-sm text-steel-500 dark:text-steel-400">
|
||||
{{ _('Stazione') }}: <span class="font-mono font-bold">{{ station_code }}</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -8,15 +8,18 @@ import pytest
|
||||
class TestSelectRecipe:
|
||||
"""GET /measure/select tests."""
|
||||
|
||||
def test_select_recipe_renders(self, logged_in_client, mock_api_client):
|
||||
def test_select_recipe_renders(self, logged_in_client, mock_api_client, monkeypatch):
|
||||
"""Recipe selection page renders for MeasurementTec role."""
|
||||
mock_api_client.get.return_value = {
|
||||
"items": [
|
||||
{"id": 1, "code": "REC-001", "name": "Test Recipe"},
|
||||
],
|
||||
"total": 1,
|
||||
"pages": 1,
|
||||
}
|
||||
monkeypatch.setenv("STATION_CODE", "ST-TEST")
|
||||
import config
|
||||
import importlib
|
||||
importlib.reload(config)
|
||||
import blueprints.measure
|
||||
importlib.reload(blueprints.measure)
|
||||
|
||||
mock_api_client.get_station_recipes.return_value = [
|
||||
{"id": 1, "code": "REC-001", "name": "Test Recipe"},
|
||||
]
|
||||
|
||||
resp = logged_in_client.get("/measure/select")
|
||||
assert resp.status_code == 200
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
"""Verify that /measure/select reads STATION_CODE and filters recipes via the server."""
|
||||
import importlib
|
||||
from unittest.mock import patch, MagicMock
|
||||
|
||||
|
||||
def _reload_measure(monkeypatch, station_code=None):
|
||||
"""Reload config and measure module under the given STATION_CODE env."""
|
||||
if station_code is None:
|
||||
monkeypatch.delenv("STATION_CODE", raising=False)
|
||||
else:
|
||||
monkeypatch.setenv("STATION_CODE", station_code)
|
||||
import config
|
||||
importlib.reload(config)
|
||||
import blueprints.measure
|
||||
importlib.reload(blueprints.measure)
|
||||
|
||||
|
||||
def test_select_recipe_calls_station_endpoint(logged_in_client, monkeypatch):
|
||||
_reload_measure(monkeypatch, station_code="ST-TEST")
|
||||
from blueprints import measure as measure_bp_mod
|
||||
with patch.object(measure_bp_mod, "api_client") as mock_api:
|
||||
mock_api.get_station_recipes.return_value = [
|
||||
{"id": 1, "code": "R1", "name": "Recipe 1", "active": True},
|
||||
]
|
||||
resp = logged_in_client.get("/measure/select")
|
||||
assert resp.status_code == 200
|
||||
mock_api.get_station_recipes.assert_called_once()
|
||||
args, kwargs = mock_api.get_station_recipes.call_args
|
||||
assert args[0] == "ST-TEST" or kwargs.get("station_code") == "ST-TEST"
|
||||
|
||||
|
||||
def test_select_recipe_without_station_code_shows_error(logged_in_client, monkeypatch):
|
||||
_reload_measure(monkeypatch, station_code=None)
|
||||
resp = logged_in_client.get("/measure/select")
|
||||
assert resp.status_code == 503
|
||||
body = resp.data.lower()
|
||||
assert b"station_code" in body or b"stazione" in body
|
||||
Reference in New Issue
Block a user