f20b92bafba47e71045bfd9ad1433b0870afdbe3
5 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
e2aae88858 |
feat(measure): il fuori tolleranza si autorizza, non si chiude
Il punto 5 c'era a meta': la schermata apriva il modale del capoturno e bloccava l'avanzamento automatico, ma il modale si chiudeva con Annulla o con un click sullo sfondo, la misura era gia' salvata, e `pendingAdvance` veniva impostato e non letto da nessuno. Era una conferma, non uno sbarramento. Ora l'autorizzazione finisce sulla misura: due colonne, chi ha autorizzato e quando. Finche' una quota e' fuori tolleranza e nessuno l'ha autorizzata, il server rifiuta la misura della quota successiva e la chiusura del ciclo. Non e' la schermata a impedirlo: la schermata risparmia solo il viaggio. Rimisurare la stessa quota resta possibile — il calibro scivola, il pezzo si riposiziona — e una seconda lettura in tolleranza libera il blocco, perche' conta l'ultima lettura di ogni quota. Quante volte si possa riprovare e' il punto 4, e tutte le letture restano comunque a registro. Un warning non blocca: fuori dai limiti di attenzione ma dentro la tolleranza e' dentro la tolleranza. Ricaricare la pagina era il modo piu' semplice per scavalcare il vecchio gate. Non lo e' piu': la schermata chiede al server, all'apertura, se una quota sta aspettando, e si ritrova davanti lo stesso blocco. Sparisce /validate-supervisor, che verificava le credenziali e buttava via la risposta. Al suo posto un endpoint che le credenziali le usa per scrivere l'approvazione dove serve. Il controllo del capoturno si sposta in auth_service, accanto al resto delle credenziali: fermo linea, chiusura e fuori tolleranza fanno la stessa domanda, e solo una delle tre riguarda la produzione. Il file di statistica guadagna authorised_by e authorised_at: mostrava il fallimento e non la decisione, che e' la meta' che un auditor chiede. Migrazione 010: due colonne nullable sulle misure. Le righe esistenti restano nulle — retrodatare un'autorizzazione mai avvenuta sarebbe inventarsi un record di audit, e quelle produzioni sono chiuse da un pezzo. Test: +18 (309). Coprono il rifiuto della quota successiva, la rimisura ammessa, il rilascio del blocco con una lettura buona, il ciclo che non si chiude, il capoturno registrato sulla misura, le credenziali sbagliate e chi capoturno non e', il file di statistica, e il fatto che una produzione chiusa non blocchi la successiva. Aggiunto uploads/statistics/ al gitignore: i test che chiudono una produzione scrivevano nel repository. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
5aa3d595ad |
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> |
||
|
|
bde8fafd77 |
feat(production): la misura torna a cercare l'operatore
Il conto alla rovescia non si ferma piu' a zero. Oltre la scadenza continua nell'altro senso e dice da quanto la linea e' in ritardo, in rosso, su tutte le schermate del percorso di misura: lista task, riepilogo, scelta ricetta. Un ritardo va letto, non dedotto. Allo scadere dell'intervallo la misura si ripropone da sola: cicalino, un avviso di cinque secondi, e l'operatore torna al primo task di misura da qualunque schermata si trovi. Un ciclo copre tutti i task di misura della ricetta, quindi solo l'ultimo lo chiude e fa ripartire l'intervallo. Farlo ripartire al primo avrebbe fatto misurare una ricetta con tre task di misura tre volte piu' spesso di come e' configurata. E' il server a decidere quale task chiude il ciclo, perche' e' lui a conoscere la sequenza: la lista dei task di misura viaggia con la produzione (measurement_task_ids), che e' anche cio' che permette a una schermata qualsiasi di sapere dove riportare l'operatore. Aggiunta la rimisura: si gira il pezzo e si misura di nuovo dentro lo stesso ciclo, senza chiudere niente e senza guadagnare tempo sulla scadenza. Le due letture restano entrambe in statistica, che e' il motivo per cui si prendono. Chi sta gia' misurando quando l'intervallo scade e' in ritardo, non perso: la banda diventa rossa e lo si lascia lavorare. Portarlo altrove a meta' ciclo cancellerebbe quote che ha davanti agli occhi senza guadagnare nulla. Sistemato anche il pulsante "Conferma ciclo" dell'overlay: alzava una bandierina locale e basta, quindi il ciclo non veniva mai registrato sul server da quella strada. Ora passa da confirmCycle come il pulsante della barra. Migrazione 008: il registro eventi della produzione impara task_measured e remeasure, e la colonna task_id — un task_measured che non dice quale task non registra niente di utile. I valori nuovi entrano tutti insieme perche' allargare una enum MySQL riscrive la tabella, stesso ragionamento dei tipi di task in 007. Verificata su SQLite (batch mode) e in MySQL con --sql. La logica dell'orologio — come si legge un ritardo, quando suona, dove sta la misura — vive in un solo posto (production-clock.js) e la schermata di misura la usa invece di riscriverla. Test: +10 (265). Coprono il ciclo che non riparte a meta', la rimisura che non sposta la scadenza, il rifiuto a linea ferma, la sequenza dei task di misura esposta dalla produzione, i proxy Flask e la validita' JS della lista task. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
7bc3c1f938 |
feat(production): fermo linea e fine produzione fanno finalmente qualcosa
Punto 6 del documento modifiche del 28/07. I due pulsanti esistevano, chiedevano correttamente l'autorizzazione del capoturno, e poi ricadevano su un commento: "handled by GAIA integration (future)". E' la riga di apertura del documento. Ora agiscono sulla produzione aperta: fermo linea sospende e congela il conto alla rovescia, la ripresa lo fa ripartire restituendo il tempo del fermo, fine produzione chiude ed emette il file di statistica. Le credenziali del capoturno vanno direttamente all'endpoint, che le verifica e registra sulla traccia della produzione chi ha autorizzato cosa: validarle a parte non lascerebbe traccia, e sarebbe un giro in piu'. In interfaccia il pulsante di fermo diventa "Riprendi" quando la linea e' ferma, e una banda dichiara lo stato con il valore congelato a video: una linea ferma che sembra in marcia e' il modo in cui si salta un intervallo di misura senza accorgersene. Il file di statistica ha richiesto di legare le misure alla produzione (migrazione 006): Measurement conosceva solo versione, lotto e seriale, nessuno dei quali separa una produzione dalla successiva sulla stessa ricetta e sullo stesso lotto, quindi "le misure dell'intera produzione" non era una query. Il CSV rispetta i separatori configurati in system_settings e porta con se' i limiti di tolleranza: senza quelli un esito pass/fail non e' piu' ricalcolabile dal file a distanza di anni, che e' il senso di un documento per audit. Le misure esportate vengono marcate synced_to_csv. Se la produzione non ha misure non viene scritto nulla: un file vuoto sarebbe rumore nella cartella, non evidenza. L'invio a GAIA resta assente e non abbozzato, con il punto d'innesto dichiarato in close_run: una chiamata vuota che sembra collegata e' peggio di niente. Tutto il resto del punto 6 non dipende dal gestionale e funziona adesso. Anche la 006 e' stata eseguita su SQLite usa e getta prima di essere considerata buona, e anche qui la prova ha trovato un difetto: aggiungere una colonna con foreign key fa emettere ad alembic un ALTER di vincolo, che SQLite rifiuta. Ora usa batch_alter_table con il vincolo nominato, che su MySQL resta un ALTER normale e su SQLite ricostruisce la tabella - cosi' la chiave esterna non va persa per far contento il dialetto dei test. Corretto anche il proxy di salvataggio misura, che chiamava /api/measurements senza slash finale e pagava un redirect 307 a ogni singola misura. Traduzioni: pybabel aveva di nuovo indovinato sette voci marcandole fuzzy, e in italiano "Linea ferma" era diventato "Lingua Preferita" su una banda di sicurezza. Tradotte per esteso in IT ed EN e tolti i flag. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
55e5e0153f |
feat(production): dai una vita propria alla produzione, lato server
Punto 1 del documento modifiche del 28/07, il prerequisito su cui poggiano i punti 3, 4 e 6. Lo stato di una produzione viveva dentro una pagina del browser: timer, conteggio cicli e flag "produzione avviata" erano variabili Alpine di task_execute.html, e la navigazione fra task e' un ricaricamento completo, quindi cambiando task si perdeva tutto. Da qui il loop di misura che non reggeva, il fermo linea che non aveva nulla da fermare e l'assenza di storico. Nuove tabelle production_runs e production_events (migrazione 005), endpoint REST senza stato in memoria di processo - con un'app di stazione installata su ogni PC il database e' l'unico posto condiviso - e il frontend che legge lo stato all'apertura invece di tenerlo in memoria. Tre scelte di modello: - la scadenza e' un timestamp assoluto (next_measurement_at), non un contatore: il countdown si ricalcola da li' a ogni caricamento, e lasciarne andare la differenza sotto zero dara' gratis il contatore del ritardo del punto 3. Al client vanno i secondi gia' calcolati, non il timestamp: un datetime naive verrebbe letto nel fuso del browser e il conto sarebbe sfasato dell'offset UTC; - l'intervallo di misura e' copiato sulla produzione, non referenziato: modificare la ricetta a produzione avviata non deve spostare una scadenza in corso; - active_station_id rispecchia la stazione finche' la produzione e' aperta e va a NULL alla chiusura. Con un vincolo unico sopra, "una stazione = una produzione aperta" e' una garanzia del database e non un controllo soggetto a race; i NULL non collidono, quindi le produzioni chiuse si accumulano senza disturbo. Il fermo linea congela il conto alla rovescia e alla ripresa la scadenza viene traslata della durata del fermo, non ricalcolata: un fermo non regala ne' toglie tempo all'operatore. L'autorizzazione del capoturno passa da authenticate_user e non da un login, che rigenererebbe la sua API key buttando giu' la sessione che ha aperta altrove. La migrazione e' stata eseguita davvero, non solo scritta, su uno SQLite usa e getta: upgrade e downgrade girano e le colonne coincidono con i modelli. La prova ha trovato un difetto - create_unique_constraint dopo create_table e' un ALTER, che SQLite rifiuta - ora il vincolo e' dichiarato dentro create_table. Fuori da questo commit, per stare nei confini del punto 1: l'API espone gia' pause, resume e close, ma i pulsanti fermo linea e fine produzione restano da collegare (punto 6), e il rientro forzato sulla misura allo scadere e' il punto 3. Corretti due difetti trovati strada facendo: env.py non importava ne' Station ne' ProductionRun, quindi l'autogenerate di Alembic era gia' cieco sulle stazioni; e task_execute.html, lo schermo con piu' JavaScript dell'applicazione, non era coperto dal test di sintassi. Aggiungerlo ha richiesto di correggere l'helper, che validava le espressioni Alpine solo come espressione singola e bocciava @click="a = false; b = true", forma che Alpine accetta: ora prova entrambe le letture e fallisce solo se cadono tutte e due. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |