feat(recipes): la ricetta decide cosa pretendere e come si misura
Tre cose che prima non erano di nessuno diventano regole della ricetta, decise da chi la scrive e fatte valere dal server. Punto 8 — tracciabilita' obbligatoria. Lotto e seriale si dichiarano obbligatori sulla ricetta. L'operatore li inserisce alla selezione, dove il pulsante non si attiva finche' mancano, e la stessa regola vale sulla lista task, raggiungibile anche per link diretto, e sul barcode: uno sbarramento che dura solo finche' qualcuno prende in mano il lettore non e' uno sbarramento. La produzione non si apre e la misura non si salva senza cio' che la ricetta pretende, perche' un valore senza il suo lotto non e' riconducibile a niente, e accorgersene dopo significa accorgersene tardi. Punto 9 — inserimento manuale. Una ricetta puo' vietare i valori digitati, ed e' il valore predefinito: il calibro e' lo strumento, digitare e' cio' che va concesso. Dove e' vietato il tastierino non viene disegnato (non nascosto con i CSS: il markup nascosto e' markup che si puo' rimostrare) e restano correzione e conferma, perche' una lettura sbagliata va cancellata. Il controllo vero e' sul server: una regola che vive solo nel frontend e' un consiglio. Migliorato al passaggio il riconoscimento del calibro. Contava solo la raffica di cifre, cosi' una lettura corta come "9.5" — tre battute — finiva registrata come digitata a mano; ora conta anche l'Invio che il wedge manda dentro la stessa raffica. Senza questa correzione il divieto avrebbe respinto misure legittime. Punto 11 — formattazione delle descrizioni. A capo e grassetto sopravvivono: chi scrive le ricette incolla dal PDF della scheda tecnica e il testo arrivava appiattito, da risistemare a mano ogni volta. Nessun HTML viene accettato o salvato — il testo viene escapato e gli unici tag nel risultato sono quelli prodotti dal renderer. La sanificazione e' questa: non c'e' niente da sanificare perche' non si accetta niente. Le stesse due regole in Jinja e in JS, cosi' una descrizione si legge uguale ovunque. E la descrizione ora si vede anche in esecuzione: era scritta per l'operatore e la vedeva solo chi la scriveva. Migrazione 009: tre colonne sulla ricetta. Le due di tracciabilita' partono false, che e' il comportamento di oggi; l'inserimento manuale parte *vero* sulle ricette gia' esistenti — il default della colonna e' falso, quindi le ricette nuove sono solo-calibro, ma spegnerlo d'ufficio su quelle in uso fermerebbe una linea alla misura successiva. Chi possiede la ricetta lo decide dall'editor. Test: +26 (291). Coprono il rifiuto sul server per lotto, seriale e valore digitato, il calibro sempre ammesso, le regole che sopravvivono alla nuova versione, il tastierino assente in pagina, l'Avvia sbarrato, e il renderer delle descrizioni compreso il caso in cui si prova a farci passare un tag. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
"""traceability and manual input become rules of the recipe
|
||||
|
||||
Three settings that were nowhere: whether the lot and the serial are compulsory,
|
||||
and whether a value may be typed instead of read from the caliper. Until now lot
|
||||
and serial were optional everywhere and the keypad was always available, so a
|
||||
value that happens to be in tolerance could simply be entered by hand.
|
||||
|
||||
Backfill: the two traceability flags start false, which is exactly today's
|
||||
behaviour. Manual input starts *true* on the recipes that already exist - the
|
||||
column default is false, so recipes written from now on are caliper-only, but
|
||||
flipping the ones already in use would stop a running line at the next
|
||||
measurement. Turning them off is a decision for whoever owns the recipe, taken in
|
||||
the editor, not a side effect of an upgrade.
|
||||
|
||||
Revision ID: 009_recipe_rules
|
||||
Revises: 008_loop_events
|
||||
Create Date: 2026-07-28
|
||||
|
||||
"""
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
revision: str = '009_recipe_rules'
|
||||
down_revision: Union[str, None] = '008_loop_events'
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
op.add_column(
|
||||
'recipes',
|
||||
sa.Column(
|
||||
'requires_lot', sa.Boolean(), nullable=False, server_default='0',
|
||||
),
|
||||
)
|
||||
op.add_column(
|
||||
'recipes',
|
||||
sa.Column(
|
||||
'requires_serial', sa.Boolean(), nullable=False, server_default='0',
|
||||
),
|
||||
)
|
||||
op.add_column(
|
||||
'recipes',
|
||||
sa.Column(
|
||||
'allow_manual_input', sa.Boolean(), nullable=False, server_default='0',
|
||||
),
|
||||
)
|
||||
# Recipes already in production keep the behaviour they were written under.
|
||||
op.execute("UPDATE recipes SET allow_manual_input = 1")
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
op.drop_column('recipes', 'allow_manual_input')
|
||||
op.drop_column('recipes', 'requires_serial')
|
||||
op.drop_column('recipes', 'requires_lot')
|
||||
Reference in New Issue
Block a user