BR-QC — Regole di business del dominio QualityCheck / Controllo qualità
Regole estratte dal dominio QualityCheck / Controllo qualità di Infocad, ancorate al flusso pilota
FLOW-QC-001(registrazione dell'esito di un controllo — esecuzione di un check: salvataggio delle risposte e chiusura con data/utente di esecuzione) e alla relativa evidenza. Sistema: Infocad (Descor), backend OracleINFOCAD_TEST38, front-end ASP.NET Web Forms (VB.NET) QualityCheckCenter + servizio WCFQualityCheckReaderWCF(client mobile) + server (Controller/DAL C#), nessun ORM (stored procedure via DALExecute*StoredProcedure).L'esecuzione di un controllo ha due entry-point con semantica equivalente ma implementazione divergente:
- Path mobile/WCF —
QualityCheckReaderWCF.DoCheck→QualityCheckController.DoCheck→QualityCheck_DAL.DoCheck→ SPQCK_DOCHECK(che chiamaQCK_SAVERESPONSES+UPDATEdi chiusura suQCHECK_CHECK).- Path web/scheda —
CheckSurveyControl.ascx(bottone Eseguito) →SaveResponses(QCK_SAVERESPONSES) +UpdateCheck(QCK_UPDATECHECK).Aspetto centrale del dominio: si tratta di WRITE/UPDATE in-place di un check preesistente (mai creazione di un check standalone in questo flusso); di guardie di stato (
Suspenddate/ExecutionDate/CancelDate); del gate WCFValidateLoginKey; del triggerQCHECK_CHECK_SCHEDULEche sincronizza il contatore controlli aperti dell'ODL (MAINT_ODL.COUNT_QCHECKS); e di un'importante divergenza mobile vs web (log, ricalcolo aggregati di gruppo, IGC, transazionalità).
Legenda enforcement: UI = validato solo lato interfaccia/code-behind · BL = logica applicativa (WCF/controller/DAL) · DB = logica in stored procedure/trigger o constraint · mixed = più livelli.
Legenda confidence: VERIFIED = catena di codice + controprova indipendente (sorgente DB su USER_SOURCE/USER_TRIGGERS) · SUPPORTED = evidenza coerente ma un anello non eseguito a runtime · INFERRED = dedotto dalla struttura, non testato.
Le stored procedure/trigger QCK_*/QCHECK_* citati sono stati confermati con lookup read-only su
USER_SOURCE/USER_TRIGGERS dello schema INFOCAD_TEST38 (driver oracledb thin, sola lettura del
testo — nessun accesso a dati di business, nessuna DML/DDL, nessuna credenziale stampata).
Registro regole
| ID | Regola | Tipo | Enforcement | Conf. |
|---|---|---|---|---|
| BR-QC-001 | Path WCF DoCheck: sessione valida (ValidateLoginKey), altrimenti AccessDenied |
permission | BL | VERIFIED |
| BR-QC-002 | Guardia stato: check sospeso (Suspenddate valorizzata) → esecuzione bloccata |
validation | BL | VERIFIED |
| BR-QC-003 | Guardia stato: check già eseguito (ExecutionDate valorizzata) → nessuna ri-esecuzione |
validation | BL | VERIFIED |
| BR-QC-004 | Guardia stato: check annullato (CancelDate valorizzata) → NoDataFound |
validation | BL | VERIFIED |
| BR-QC-005 | DoCheck opera in-place su un check preesistente (solo UPDATE), mai crea un check |
state-transition | DB/BL | VERIFIED |
| BR-QC-006 | Esecuzione = salva risposte + stamping EXECUTIONDATE=SYSDATE, EXECUTIONUSER=utente |
state-transition | DB | VERIFIED |
| BR-QC-007 | Le risposte sono isolate per check: UPDATE ... WHERE IDDETAILS=... AND CHECKID=PIDCHECK |
validation | DB | VERIFIED |
| BR-QC-008 | Successo = POUT/nrow ≠ 0 (righe risposte aggiornate); 0 → errore generico |
validation | BL/DB | VERIFIED |
| BR-QC-009 | Accoppiamento posizionale array PIDDETAILS(I)↔PRESPONSEVALUES(I); loop su FIRST..LAST |
calculation | DB | VERIFIED |
| BR-QC-010 | Trigger: chiusura check SCHEDULE (EXECUTIONDATE NULL→valorizzata) → MAINT_ODL.COUNT_QCHECKS -1 |
state-transition | DB | VERIFIED |
| BR-QC-011 | Trigger simmetrico: riapertura/annullo/insert/delete/riassegnazione riallineano COUNT_QCHECKS |
state-transition | DB | VERIFIED |
| BR-QC-012 | Path web: salva/esegui con grant 22540 o SuperAdmin; reset/annulla con grant 22550 |
permission | UI ⚠️ | VERIFIED |
| BR-QC-013 | Path web: check compilabile solo se Startdate valorizzata e Suspenddate non valorizzata |
validation | UI ⚠️ | VERIFIED |
| BR-QC-014 | Path web: ogni domanda deve avere risposta (o motivazione se N/A) prima di Eseguito | validation | UI ⚠️ | VERIFIED |
| BR-QC-015 | Path web: stamping ExecutionDate=DateTime.Now, utente di sessione, ActionCheck='ActionExecuted' |
state-transition | UI/BL | VERIFIED |
| BR-QC-016 | QCK_UPDATECHECK (solo web): logga l'azione in QCHECK_CHECK_LOG per ActionExecuted/ActionPlanned |
side-effect | DB | VERIFIED |
| BR-QC-017 | QCK_UPDATECHECK (solo web): ricalcola aggregati di gruppo; gruppo completato → IDSTATE=3 |
calculation | DB | VERIFIED |
| BR-QC-018 | IGC calcolato lato UI (web); il path mobile QCK_DOCHECK non aggiorna l'IGC |
calculation | UI ⚠️ | SUPPORTED |
| BR-QC-019 | Transazionalità: QCK_DOCHECK senza COMMIT; QCK_UPDATECHECK/SaveResponses con COMMIT esplicito |
config-flag | DB/BL ⚠️ | VERIFIED |
| BR-QC-020 | Annullamento web: salva risposte + CancelDate=DateTime.Now via UpdateCheck |
state-transition | UI/DB | VERIFIED |
| BR-QC-021 | Avvio check (WCF StartCheck): bloccato se Startdate già valorizzata |
validation | BL | VERIFIED |
⚠️ = regola a rischio (solo-UI, solo-DB, magic number/hard-coded id, date-logic app-side, divergenza tra path).
Dettaglio regole
BR-QC-001 — Path WCF: sessione valida
Tipo: permission · Enforcement: BL · Conf.: VERIFIED · customer_specific: no
L'operazione WCF DoCheck(loginkey, idCheck, idsDetail, responses) è gateata da
ValidateLoginKey(loginkey): se la sessione non è valida ritorna setAsAccessDenied() senza toccare
il DB. È il gate del path programmatico/mobile (equivalente ai grant di sezione UI del path web).
Non risulta un controllo grant azione (es. 22540) ripetuto lato WCF oltre alla validazione sessione
(vedi Open questions del flusso).
Evidenza: QualityCheckReaderWCF.svc.vb:257,278.
BR-QC-002 — Guardia di stato: check sospeso
Tipo: validation · Enforcement: BL · Conf.: VERIFIED · customer_specific: no
Prima di eseguire, il WCF ricarica lo stato con ctrl.GetCheckByID(idCheck): se Suspenddate è
valorizzata → setAsERROR con messaggio SuspendCheckLbl e nessuna scrittura. Un controllo
sospeso non è eseguibile finché non viene riattivato.
Evidenza: QualityCheckReaderWCF.svc.vb:259-261.
BR-QC-003 — Guardia di stato: check già eseguito (idempotenza)
Tipo: validation · Enforcement: BL · Conf.: VERIFIED · customer_specific: no
Se ExecutionDate è già valorizzata → setAsERROR (CheckStringStatusClosed). Impedisce la
ri-esecuzione di un controllo già chiuso: un check si esegue una sola volta. È la guardia che
rende idempotente lo stamping di esecuzione.
Evidenza: QualityCheckReaderWCF.svc.vb:262-263.
BR-QC-004 — Guardia di stato: check annullato
Tipo: validation · Enforcement: BL · Conf.: VERIFIED · customer_specific: no
Se CancelDate è valorizzata → setAsNoDataFound(): il controllo annullato è trattato come
inesistente ai fini dell'esecuzione (nessuna scrittura). L'ordine delle guardie è
Suspenddate → ExecutionDate → CancelDate (ElseIf), quindi la sospensione prevale sulle altre.
Evidenza: QualityCheckReaderWCF.svc.vb:264-265.
BR-QC-005 — Esecuzione in-place di un check preesistente
Tipo: state-transition · Enforcement: DB/BL · Conf.: VERIFIED · customer_specific: no
QCK_DOCHECK esegue esclusivamente UPDATE (delle risposte in QCHECK_CHECK_DETAILS via
QCK_SAVERESPONSES e dell'header QCHECK_CHECK): non crea un nuovo check né nuovi dettagli.
Il flusso presuppone quindi un check e i suoi dettagli già materializzati (via StartCheck/creazione
a monte). Se IDCHECK/IDDETAILS non esistono, gli UPDATE semplicemente non toccano righe
(POUT=0 → BR-QC-008).
Evidenza: QCK_DOCHECK USER_SOURCE:12,14-16; QCK_SAVERESPONSES USER_SOURCE:16-19.
BR-QC-006 — Stamping di esecuzione: data server + utente
Tipo: state-transition · Enforcement: DB · Conf.: VERIFIED · customer_specific: no
La chiusura del controllo esegue UPDATE QCHECK_CHECK SET EXECUTIONDATE = SYSDATE, EXECUTIONUSER = PUSERNAME WHERE IDCHECK = PIDCHECK. Nel path mobile la data di esecuzione è la
SYSDATE del DB (data server), mentre nel path web è DateTime.Now dell'app (BR-QC-015):
divergenza di sorgente-tempo tra i due path (date-logic).
Evidenza: QCK_DOCHECK USER_SOURCE:14-16.
BR-QC-007 — Isolamento delle risposte per check
Tipo: validation · Enforcement: DB · Conf.: VERIFIED · customer_specific: no
QCK_SAVERESPONSES aggiorna solo i dettagli il cui IDDETAILS è nell'array e il cui
CHECKID = PIDCHECK: il doppio predicato garantisce che le risposte non possano essere scritte su
dettagli appartenenti a un altro controllo, anche se un IDDETAILS errato venisse passato.
Evidenza: QCK_SAVERESPONSES USER_SOURCE:16-19.
BR-QC-008 — Semantica di successo: righe aggiornate ≠ 0
Tipo: validation · Enforcement: BL/DB · Conf.: VERIFIED · customer_specific: no
QCK_SAVERESPONSES accumula in POUT il numero di righe effettivamente aggiornate
(SQL%ROWCOUNT). Il WCF interpreta nrow ≠ 0 come OK e nrow = 0 come errore generico
(ErrorString). Conseguenza: il successo è misurato sulle risposte aggiornate, non sullo
stamping di QCHECK_CHECK (che non incrementa POUT). ⚠️ magic-number: nrow<>0. Edge/date-logic:
QCK_SAVERESPONSES itera PIDDETAILS.FIRST..LAST — con array di dettagli vuoto il FOR su
collezione vuota solleverebbe errore PL/SQL (non gestito con guardia sull'esistenza di elementi).
Evidenza: QCK_SAVERESPONSES USER_SOURCE:13,21,25; QualityCheckReaderWCF.svc.vb:267-272.
BR-QC-009 — Accoppiamento posizionale dettagli↔risposte
Tipo: calculation · Enforcement: DB · Conf.: VERIFIED · customer_specific: no
Le risposte sono associate per indice: la i-esima voce di PRESPONSEVALUES è scritta sul
dettaglio identificato dalla i-esima voce di PIDDETAILS. La DAL costruisce i due array PL/SQL
associativi dalle due liste (idsDetail, responses) mantenendo l'ordine; l'allineamento è
responsabilità del chiamante (nessun re-check di corrispondenza lato DB). ⚠️ rischio di
disallineamento se le due liste non hanno stessa lunghezza/ordine.
Evidenza: QualityCheck_DAL.cs:1410-1416; QCK_SAVERESPONSES USER_SOURCE:13-19.
BR-QC-010 — Trigger: decremento contatore ODL alla chiusura del check SCHEDULE
Tipo: state-transition · Enforcement: DB · Conf.: VERIFIED · customer_specific: no
Il trigger QCHECK_CHECK_SCHEDULE (INSERT OR UPDATE su QCHECK_CHECK) agisce solo se il check è
legato a un ODL di manutenzione: ENTITYTABLE='SCHEDULE' e ENTITYID>0 (valutato su OLD o NEW).
In UPDATING, quando oldExecDate IS NULL AND newExecDate IS NOT NULL (la chiusura prodotta da
QCK_DOCHECK/QCK_UPDATECHECK) esegue UPDATE MAINT_ODL SET COUNT_QCHECKS = COUNT_QCHECKS - 1 WHERE ID_ODL = newEntityId. È il legame automatico tra esecuzione del controllo e contatore
controlli aperti dell'ODL.
Evidenza: USER_TRIGGERS.QCHECK_CHECK_SCHEDULE (ramo UPDATING, oldExecDate IS NULL AND newExecDate IS NOT NULL); legame MAINT_ODL in FLOW-QC-001.
BR-QC-011 — Trigger: riallineamento simmetrico del contatore ODL
Tipo: state-transition · Enforcement: DB · Conf.: VERIFIED · customer_specific: no
Lo stesso trigger mantiene coerente MAINT_ODL.COUNT_QCHECKS in tutti gli scenari:
INSERT di un check con EXECUTIONDATE NULL → +1; UPDATE con EXECUTIONDATE da valorizzata a
NULL (riapertura/annullo che azzera l'esecuzione) → +1; passaggio da ENTITYTABLE='SCHEDULE' ad
altro → +1 sul vecchio ODL; cambio di ENTITYID tra due SCHEDULE → -1 sul vecchio e +1 sul
nuovo; DELETE di un check non eseguito → -1. Il contatore riflette quindi i controlli aperti
(non ancora eseguiti) associati all'ODL.
Evidenza: USER_TRIGGERS.QCHECK_CHECK_SCHEDULE (rami INSERTING/UPDATING/DELETING).
BR-QC-012 — Path web: grant di azione per esecuzione e reset
Tipo: permission · Enforcement: UI ⚠️ · Conf.: VERIFIED · customer_specific: no
Nella scheda CheckSurveyControl.ascx i bottoni Salva/Eseguito sono visibili solo con
HasUserGrant("22540") Or SuperAdmin (e IsCompile); il bottone Reset/annulla richiede
HasUserGrant("22550") Or SuperAdmin. ⚠️ solo-UI (visibilità del controllo) e magic-id
hard-coded (22540, 22550): il controller/DAL non ri-verifica i grant, quindi un chiamante
server-side non è soggetto a questo gate. Il path mobile usa invece ValidateLoginKey (BR-QC-001).
Evidenza: CheckSurveyControl.ascx.vb:244-246,279-281.
BR-QC-013 — Path web: compilabilità = avviato e non sospeso
Tipo: validation · Enforcement: UI ⚠️ · Conf.: VERIFIED · customer_specific: no
La scheda imposta IsCompile = True solo se Startdate è valorizzata e Suspenddate è
nulla; altrimenti i controlli di risposta sono disabilitati. Riflette lato UI il ciclo di vita
avviato→compilabile→sospeso. ⚠️ solo-UI: la regola non è ripetuta lato DB nel path web (il
path mobile la copre invece con le guardie BR-QC-002/003/004).
Evidenza: CheckSurveyControl.ascx.vb:348-352.
BR-QC-014 — Path web: tutte le domande devono avere risposta
Tipo: validation · Enforcement: UI ⚠️ · Conf.: VERIFIED · customer_specific: no
btnExecuted_Click esegue ExecutedSurvey() solo se CheckAllWithAnswer() è vero: ogni domanda del
survey deve avere una risposta (radio/numerico/check/testo) oppure, se marcata N/A, una motivazione
(MotivationNA). Alla prima domanda non soddisfatta la funzione ritorna False e ripopola il survey
con messaggio CheckAnswerSurvey. ⚠️ solo-UI: il completamento non è imposto dal DB (QCK_DOCHECK
scrive qualunque sottoinsieme di risposte fornito).
Evidenza: CheckSurveyControl.ascx.vb:999-1011,1073-1109.
BR-QC-015 — Path web: stamping di esecuzione lato applicazione
Tipo: state-transition · Enforcement: UI/BL · Conf.: VERIFIED · customer_specific: no
ExecutedSurvey() salva le risposte (SaveUserResponses → QCK_SAVERESPONSES) e poi imposta
sull'oggetto CurrentCheck: Igc = CDbl(txtIgc.Value), ExecutionDate = DateTime.Now,
ExecutionUser = WS.SessionLogin.UserName, ActionCheck = "ActionExecuted", salvando via
UpdateCheck (QCK_UPDATECHECK). A differenza del path mobile, data e IGC provengono
dall'applicazione, non dal DB (date-logic app-side).
Evidenza: CheckSurveyControl.ascx.vb:1201-1211,1283-1291.
BR-QC-016 — QCK_UPDATECHECK: log dell'azione (solo path web)
Tipo: side-effect · Enforcement: DB · Conf.: VERIFIED · customer_specific: no
QCK_UPDATECHECK inserisce una riga in QCHECK_CHECK_LOG (IDCHECK, utente, SYSDATE, azione)
quando PACTION='ActionExecuted' (utente = PEXECUTIONUSER) o PACTION='ActionPlanned'
(utente = PPLANNEDUSER). ⚠️ divergenza mobile vs web: il path mobile QCK_DOCHECK non
scrive alcun log di esecuzione — l'audit dell'esecuzione esiste solo per il path web.
Evidenza: QCK_UPDATECHECK USER_SOURCE:46-66.
BR-QC-017 — QCK_UPDATECHECK: ricalcolo aggregati e chiusura gruppo (solo path web)
Tipo: calculation · Enforcement: DB · Conf.: VERIFIED · customer_specific: no
Dopo l'update, QCK_UPDATECHECK ricalcola gli aggregati del gruppo del check (GROUPID):
QCHECK_GROUP.LQRAVG = AVG(IGC) dei check eseguiti e non annullati (EXECUTIONDATE IS NOT NULL AND ISCANCELLED = 0); NCHACKSCEXECUTE = numero eseguiti; NCHACKSPLANNED = numero pianificati; e se
NCHACKSCREATE = n_executecheck (tutti i check creati eseguiti) porta il gruppo a IDSTATE = 3
(gruppo chiuso/completato). ⚠️ divergenza mobile vs web: QCK_DOCHECK non ricalcola nulla a
livello di gruppo → eseguire un check dall'app mobile non aggiorna LQRAVG/contatori/stato gruppo.
Evidenza: QCK_UPDATECHECK USER_SOURCE:73-122 (magic-number stato IDSTATE=3 a riga 108).
BR-QC-018 — Calcolo IGC lato UI; assente nel path mobile
Tipo: calculation · Enforcement: UI ⚠️ · Conf.: SUPPORTED · customer_specific: no
L'IGC (indice di gravità/conformità) è accumulato lato UI nella proprietà Rilevato (in
ViewState, alimentata da NewCalculate sulle singole risposte) e salvato via txtIgc→Igc in
UpdateCheck. Nel path mobile QCK_DOCHECK non aggiorna la colonna IGC di QCHECK_CHECK:
un controllo eseguito da mobile resta con IGC non ricalcolato (hidden-flag/divergenza). Da confermare
dove/quando l'IGC venga eventualmente aggiornato per il path mobile (Open question del flusso).
Evidenza: CheckSurveyControl.ascx.vb:62-72,1206; assenza di update IGC in QCK_DOCHECK
USER_SOURCE:10-20.
BR-QC-019 — Transazionalità divergente tra i path
Tipo: config-flag · Enforcement: DB/BL ⚠️ · Conf.: VERIFIED · customer_specific: no
QCK_DOCHECK non contiene COMMIT: il commit del path mobile è demandato al DataLayer/gestione
connessione (la DAL DoCheck non apre neppure una transazione esplicita). Al contrario
QCK_UPDATECHECK esegue COMMIT espliciti (due volte) e le DAL SaveResponses/UpdateCheck usano
BeginTransaction/CommitTransaction. ⚠️ asimmetria di gestione transazionale tra i due path
(rischio su rollback parziale mobile). Vedi Open questions del flusso.
Evidenza: QCK_DOCHECK USER_SOURCE:1-20 (nessun COMMIT); QCK_UPDATECHECK USER_SOURCE:69,122;
QualityCheck_DAL.cs:1402-1442 (DoCheck, no BeginTransaction) vs :3016,3038 (SaveResponses) e
:1542,1602 (UpdateCheck).
BR-QC-020 — Annullamento controllo (path web)
Tipo: state-transition · Enforcement: UI/DB · Conf.: VERIFIED · customer_specific: no
Il bottone Reset (btnReset_Click, gateato dal grant 22550 — BR-QC-012) invoca CancelSurvey():
salva le risposte correnti e imposta CancelDate = DateTime.Now e ExecutionUser = utente di
sessione, salvando via UpdateCheck (QCK_UPDATECHECK). Coerente con la guardia BR-QC-004: un check
con CancelDate non è più eseguibile dal path mobile.
Evidenza: CheckSurveyControl.ascx.vb:1015-1017,1255-1265.
BR-QC-021 — Avvio check: non ri-avviabile
Tipo: validation · Enforcement: BL · Conf.: VERIFIED · customer_specific: no
L'operazione WCF correlata StartCheck(loginkey, idCheck, startDate) blocca l'avvio se Startdate
è già valorizzata (setAsERROR con CheckStringStatuStart), altrimenti registra la data di inizio.
È la transizione di stato che precede la compilabilità (BR-QC-013): un check si avvia una sola volta.
Evidenza: QualityCheckReaderWCF.svc.vb:289-298.
Note trasversali e flag
- Divergenza mobile vs web (BR-QC-006/015, BR-QC-016, BR-QC-017, BR-QC-018, BR-QC-019): i due
entry-point producono lo stesso stato "eseguito" ma con effetti collaterali diversi. Il path web
(
QCK_UPDATECHECK) è "ricco" (log, ricalcolo aggregati di gruppo, stato gruppo, IGC, commit espliciti); il path mobile (QCK_DOCHECK) è "minimale" (solo risposte + stamping data/utente). - Magic id / magic number: grant azione
22540(esegui/compila) e22550(reset) hard-coded (BR-QC-012); stato gruppoIDSTATE = 3(BR-QC-017); semanticanrow<>0(BR-QC-008). - Solo-UI: BR-QC-012 (grant), BR-QC-013 (compilabilità), BR-QC-014 (completezza risposte) non
sono replicate lato DB; un chiamante server-side/mobile non ne è soggetto (mitigato in parte dalle
guardie di stato BR-QC-002/003/004 e da
ValidateLoginKey). - Solo-DB: BR-QC-010/011 (contatore ODL via trigger), BR-QC-016/017 (log e aggregati di gruppo) vivono interamente in stored procedure/trigger e sono invisibili al codice applicativo.
- date-logic: sorgente-tempo divergente (
SYSDATEDB mobile vsDateTime.Nowapp web), BR-QC-006/015. - customer_specific: nessuna regola risulta specifica per un singolo cliente; comportamento di
prodotto standard sullo schema
INFOCAD_TEST38.