Table of Contents

BR-ENE — Regole di business del dominio Energy / Energia

Regole di business estratte dal dominio Energy / Energia di Infocad (Descor), ancorate al flusso pilota FLOW-ENE-001 (inserimento manuale di una lettura contatore, EnergyCenterWebNRG_INSERTDATACOLLECTOR) e verificate sul sorgente C# + sul PL/SQL Oracle (USER_SOURCE / USER_TRIGGERS / USER_CONSTRAINTS, schema INFOCAD_TEST38, lookup read-only bounded).

Backend Oracle, ASP.NET Web Forms (C#, EnergyCenterWeb) + Controller/BL/DAL su Windows Service via .NET Remoting, nessun ORM (stored procedure ODP.NET).

Legenda

  • Tipo: validation / state-transition / permission / calculation / date-logic / config-flag / customer-specific.
  • Enforcement: UI (code-behind Web Forms), BL (Business Logic / Controller / DAL C#), DB (procedura/trigger/constraint Oracle), mixed.
  • Confidence: VERIFIED (letto direttamente su codice + DB), SUPPORTED (dedotto da evidenza forte), INFERRED (ipotesi ragionata).

⚠️ Nota trasversale di rischio (BR-ENE-022): sulla tabella radice NRG_DATACOLLECTOR non esiste alcun CHECK constraint (solo la PK). Tutte le regole di validità della lettura (ordine di data, valore non decrescente, valore ammesso di COLLECTSOURCE) sono applicate solo nell'applicazione (UI o procedura), mai dal vincolo di schema. Vedi BR-ENE-022.


Registro delle regole

ID Regola Tipo Enforcement Conf
BR-ENE-001 Inserimento lettura richiede grant 15020; modifica richiede 15010; SuperAdmin bypassa permission UI VERIFIED
BR-ENE-002 La lettura creata dalla UI/WCF è sempre forzata a COLLECTSOURCE = 'M' (manuale) config-flag mixed (UI+BL) VERIFIED
BR-ENE-003 Insert bloccato se data ≤ ultima lettura manuale oppure valore < ultimo valore manuale validation UI (solo UI) VERIFIED
BR-ENE-004 Il riferimento "ultima lettura" considera solo le letture con COLLECTSOURCE = 'M' date-logic DB VERIFIED
BR-ENE-005 Consumo MEASUREAMOUNT = MEASUREREAD − ultima MEASUREREAD; se amount/read = 0 → 0 calculation DB VERIFIED
BR-ENE-006 Spalmatura: se esistono letture precedenti e giorni intercorsi ≥ 1, il consumo è ripartito per giorno (righe intermedie source 'm', riga finale 'M') calculation DB VERIFIED
BR-ENE-007 Il calcolo consumo/ultima lettura considera solo contatori ISACTIVE = 1 AND ISCANCELLED = 0 validation DB VERIFIED
BR-ENE-008 L'immagine è scritta in NRG_READINGS_IMG solo se COLLECTSOURCE = 'M' e id > 0 state-transition DB VERIFIED
BR-ENE-009 PK ID_DATACOLLECTOR assegnata dal trigger BEFORE INSERT da SEQ_NRG_DATACOLLECTOR; se id esplicito, la sequence viene riallineata state-transition DB VERIFIED
BR-ENE-010 AREA/CUBATURE/PERSON denormalizzati sulla lettura da EDIFICI/COMPRENSORI_, con nomi colonna letti da NRG_DICTIONARY (ID_KEY='FIELDUNIT'); default 1 se nessun edificio calculation DB VERIFIED
BR-ENE-011 Lettura multi-canale: MEASUREREAD = A1 + A2 + A3 calculation mixed (UI+BL) VERIFIED
BR-ENE-012 Immagine di lettura ridimensionata a max 1024×768 @72dpi e salvata come JPEG calculation UI VERIFIED
BR-ENE-013 La modifica di una lettura cancella i record esistenti del contatore (anche gli spalmati 'm') e li reinserisce; non è incrementale state-transition DB VERIFIED
BR-ENE-014 In modifica è editabile solo l'ultima lettura: il pulsante Salva è nascosto se la data è antecedente alla penultima permission UI VERIFIED
BR-ENE-015 La validazione data in update varia per control: ReadingDetail blocca solo se data < penultima; ReadingCounterImgsDetail è incondizionata (check deprecato 24/10/2014) validation UI VERIFIED
BR-ENE-016 La guardia data in insert è incoerente tra i control: ReadingDetail usa > (stessa data ammessa), ReadingCounter* usa >= (stessa data bloccata) validation UI (rischio) VERIFIED
BR-ENE-017 WCF mobile: se readTime = data ultima lettura → Update, altrimenti Insert date-logic BL VERIFIED
BR-ENE-018 WCF richiede loginkey decifrabile + sessione client validata, altrimenti ritorna −1/null permission BL VERIFIED
BR-ENE-019 Il selettore contatori di ReadingDetail è forzato a IdMeterType = 2 (Misuratore) config-flag UI VERIFIED
BR-ENE-020 Il parametro PACCURACY (colonna ACCURACY) è popolato con IdImportSource, non con l'accuratezza calculation BL/DAL (anomalia) SUPPORTED
BR-ENE-021 MEASUREAVG, MEASUREAMOUNT, A1..A3 salvati con ROUND(NVL(x,0),4); consumo spalmato ROUND(...,8) calculation DB VERIFIED
BR-ENE-022 Nessun CHECK constraint su NRG_DATACOLLECTOR: validità lettura enforced solo app-side validation DB (assenza) VERIFIED
BR-ENE-023 Alla creazione la data lettura è preimpostata a oggi (DateTime.Today) date-logic UI VERIFIED

Dettaglio delle regole

BR-ENE-001 — Grant di inserimento/modifica lettura

  • Statement: Il pulsante Salva della finestra di dettaglio lettura è visibile solo se l'utente ha il grant 15020 (creazione) o 15010 (modifica), oppure è SuperAdmin. In sola visualizzazione il pulsante è nascosto.
  • Tipo: permission · Enforcement: UI · Confidence: VERIFIED · customer_specific: false
  • Evidenza:
    • EnergyCenterWeb/UserControls/Details/ReadingDetail.ascx.cs:124btnSave.Visible = HasUserGrant("15020") | WS.SessionProfile.SuperAdmin; (Crea)
    • ReadingDetail.ascx.cs:115HasUserGrant("15010") (Modifica); :109 Salva nascosto in Visualizza
    • Stessi grant in ReadingCounterDetail.ascx.cs:148,158 e ReadingCounterImgsDetail.ascx.cs:215,230
  • Rischio: controllo di autorizzazione solo lato UI (visibilità pulsante). Non risulta un enforcement server-side sul metodo InsertDataCollect del Controller/BL/DAL: un client che invochi direttamente il Controller/WCF non passa per questo gate (vedi BR-ENE-018 per il gate WCF, che è però solo di sessione, non di grant). 15020/15010 sono id di grant hard-coded come stringhe.

BR-ENE-002 — Origine lettura forzata a 'M'

  • Statement: Ogni lettura creata dai control interattivi e dalla WCF mobile è marcata COLLECTSOURCE = 'M' (manuale). Questo attiva a valle il salvataggio immagine (BR-ENE-008) e definisce l'insieme di riferimento dell'"ultima lettura" (BR-ENE-004).
  • Tipo: config-flag · Enforcement: mixed (UI+BL) · Confidence: VERIFIED · customer_specific: false
  • Evidenza:
    • ReadingDetail.ascx.cs:293this.CurrentReading.CollectSourceString = "M";
    • ReadingCounterDetail.ascx.cs:354, ReadingCounterImgsDetail.ascx.cs:657
    • WCF: EnergyReaderWCF.svc.cs:126,197 — costruttore DataCollector(... CollectSourceEnum.Manuale ...)
  • Nota: il codice 'm' minuscolo è riservato dal DB alle righe intermedie di spalmatura (BR-ENE-006); gli import massivi usano altri codici (path NRG_ADDMEASUREMENTDATA, fuori da questo flow).

BR-ENE-003 — Guardia data/valore in inserimento (SOLO UI)

  • Statement: L'inserimento è bloccato con messaggio invalidDateOrMeasureMsg (nessuna scrittura) se la data scelta è ≤ alla data dell'ultima lettura manuale, oppure se il valore letto è < all'ultimo valore letto manuale.
  • Tipo: validation · Enforcement: UI (solo UI) · Confidence: VERIFIED · customer_specific: false
  • Evidenza:
    • ReadingDetail.ascx.cs:256if (Convert.ToDateTime(hdnMaxDate.Value) > this.CurrentReading.CollectDate || Convert.ToDouble(hdnMeasureRead.Value) > this.CurrentReading.MeasureRead)
    • valori di confronto popolati da checkCollectDate() (:160-199) via getLastMeasureRead (BR-ENE-004)
  • Rischio elevato: questa è una regola UI-only. Il DB non ha alcun vincolo che impedisca una data antecedente o un valore decrescente (vedi BR-ENE-022). La procedura NRG_INSERTDATACOLLECTOR accetta e calcola comunque il consumo (che potrebbe risultare negativo). Un accesso via WCF non applica questa guardia (BR-ENE-017 gestisce solo insert-vs-update sulla base della data, non l'ordinamento).

BR-ENE-004 — "Ultima lettura" = solo COLLECTSOURCE='M'

  • Statement: La lettura di riferimento per data e valore precedenti considera esclusivamente i record con COLLECTSOURCE = 'M', ordinati per data decrescente (ultima e penultima).
  • Tipo: date-logic · Enforcement: DB · Confidence: VERIFIED · customer_specific: false
  • Evidenza: procedura NRG_GETLASTMEASUREREAD (USER_SOURCE, INFOCAD_TEST38): ... FROM NRG_DATACOLLECTOR ND WHERE ND.ID_METER = PIDMETER AND ND.COLLECTSOURCE = 'M' ORDER BY COLLECTDATE DESC. Consumata da EnergyDataCollector_DAL.getLastMeasureRead (:472-514).
  • Nota: le righe intermedie di spalmatura ('m') e le letture di import sono quindi escluse dal confronto UI, ma incluse nel calcolo MAX(MEASUREREAD) della procedura di insert (BR-ENE-005, che filtra solo su meter attivo/non cancellato, non su source). Discrepanza di perimetro tra UI e DB.

BR-ENE-005 — Calcolo del consumo

  • Statement: Se PMEASUREAMOUNT è nullo o 0 e PMEASUREREAD > 0, il consumo PAMOUNT = PMEASUREREAD − PLASTMEASUREREAD dove PLASTMEASUREREAD = NVL(MAX(MEASUREREAD),0) sulle letture precedenti del contatore (attivo, non cancellato, COLLECTDATE < PCOLLECTDATE). Se amount/read = 0 → consumo 0; se amount già valorizzato → si usa quello.
  • Tipo: calculation · Enforcement: DB · Confidence: VERIFIED · customer_specific: false
  • Evidenza: NRG_INSERTDATACOLLECTOR (USER_SOURCE): blocco IF (PMEASUREAMOUNT IS NULL OR PMEASUREAMOUNT = 0) AND PMEASUREREAD > 0 THEN SELECT NVL(MAX(MEASUREREAD),0) INTO PLASTMEASUREREAD ... ; PAMOUNT := PMEASUREREAD - PLASTMEASUREREAD;
  • Nota: nessun clamp a 0 del consumo → possibile valore negativo se BR-ENE-003 non è stata applicata (es. via WCF).

BR-ENE-006 — Spalmatura del consumo sui giorni intercorsi

  • Statement: Se esistono letture precedenti (PEXIST > 0) e i giorni intercorsi dall'ultima lettura sono ≥ 1, il consumo è ripartito: per ogni giorno intercorso viene inserita una riga con COLLECTSOURCE = 'm' e valore progressivo PLASTMEASUREREAD + PAMOUNT*I, seguita da una riga finale con la lettura reale e COLLECTSOURCE = 'M'. Altrimenti (0 giorni o nessuna data precedente) viene inserita una singola riga.
  • Tipo: calculation · Enforcement: DB · Confidence: VERIFIED · customer_specific: false
  • Evidenza: NRG_INSERTDATACOLLECTOR:
    • PNUMGIORNI := NVL(TRUNC(PCOLLECTDATE - MAX(COLLECTDATE)),0) - 1
    • PREADAMOUNT := ROUND(PMEASUREREAD/(PNUMGIORNI+1),8); PAMOUNT := ROUND(PAMOUNT/(PNUMGIORNI+1),8);
    • FOR I IN 1 .. PNUMGIORNI + 1 LOOP NRG_ADDDATACOLLECTOR(..., 'm', ...); END LOOP; poi NRG_ADDDATACOLLECTOR(..., PAMOUNT*(PNUMGIORNI+1), ..., 'M', ...)
  • Magic numbers: arrotondamento a 8 decimali; branch su PNUMGIORNI < 1. Effetto collaterale: una singola lettura manuale genera N+2 righe in NRG_DATACOLLECTOR.

BR-ENE-007 — Solo contatori attivi e non cancellati

  • Statement: Tutte le SELECT di MAX(MEASUREREAD) e COUNT(*) per il calcolo/spalmatura filtrano MT.ISACTIVE = 1 AND MT.ISCANCELLED = 0.
  • Tipo: validation · Enforcement: DB · Confidence: VERIFIED · customer_specific: false
  • Evidenza: NRG_INSERTDATACOLLECTOR (join NRG_METERS MT ... AND MT.ISACTIVE = 1 AND MT.ISCANCELLED = 0); idem NRG_UPDATEDATACOLLECTOR.
  • Magic flags: ISACTIVE = 1, ISCANCELLED = 0 (colonne nullable a livello di schema — vedi tabella NRG_METERS).

BR-ENE-008 — Salvataggio immagine solo per letture manuali

  • Statement: L'INSERT su NRG_READINGS_IMG avviene solo se PCOLLECTSOURCE = 'M' e l'id generato è > 0. Le righe intermedie di spalmatura ('m') e gli import non generano immagini.
  • Tipo: state-transition · Enforcement: DB · Confidence: VERIFIED · customer_specific: false
  • Evidenza: NRG_ADDDATACOLLECTOR (USER_SOURCE): IF PCOLLECTSOURCE = 'M' AND PIDENTITY > 0 THEN NRG_ADDREADINGIMAGES(PIDENTITY, PIMAGEREAD, PIMAGEREADA1, PIMAGEREADA2, PIMAGEREADA3); END IF;. NRG_ADDREADINGIMAGES: INSERT INTO NRG_READINGS_IMG (ID_DATACOLLECTOR, IMAGEREADA1, IMAGEREADA2, IMAGEREADA3, IMAGEREAD) VALUES (...).
  • Vincolo DB: NRG_READINGS_IMG ha FK FK_NRG_READINGS_IMG → PK_NRG_DATACOLLECTOR e ID_DATACOLLECTOR NOT NULL (USER_CONSTRAINTS). Le colonne immagine sono tutte nullable → è ammesso un record immagine "vuoto".

BR-ENE-009 — Assegnazione PK via sequence (trigger) e riallineamento

  • Statement: Il trigger NRG_DATACOLLECTOR BEFORE INSERT FOR EACH ROW valorizza ID_DATACOLLECTOR da SEQ_NRG_DATACOLLECTOR.NEXTVAL quando l'id è NULL; se invece l'id è fornito esplicitamente, avanza la sequence finché supera l'id inserito (riallineamento).
  • Tipo: state-transition · Enforcement: DB · Confidence: VERIFIED · customer_specific: false
  • Evidenza: USER_TRIGGERS NRG_DATACOLLECTOR (BEFORE EACH ROW, INSERT, ENABLED): IF (:NEW."ID_DATACOLLECTOR" IS NULL) THEN SELECT SEQ_NRG_DATACOLLECTOR.NEXTVAL INTO :NEW ... ELSE ... WHILE (last_InsertID > last_Sequence) LOOP SELECT SEQ_NRG_DATACOLLECTOR.NEXTVAL ... END LOOP; END IF;

BR-ENE-010 — Denormalizzazione Mq/Mc/Persone da dizionario configurabile

  • Statement: All'insert, i valori AREA, CUBATURE, PERSON sono letti dall'edificio del contatore (EDIFICI join COMPRENSORI_ su BUILDINGCODE = NRG_METERS.BUILDINGCODE). I nomi delle colonne da leggere sono presi da NRG_DICTIONARY con ID_KEY = 'FIELDUNIT' e ID_ITEM in (AREA, CUBATURE, PERSON), tramite SQL dinamico (EXECUTE IMMEDIATE). Se nessun edificio corrisponde, i valori restano al default 1.
  • Tipo: calculation · Enforcement: DB · Confidence: VERIFIED · customer_specific: true
  • Evidenza: NRG_ADDDATACOLLECTOR: SELECT ND.ITEM_VALUE INTO PFIELDAREA FROM NRG_DICTIONARY ND WHERE ND.ID_KEY = 'FIELDUNIT' AND ID_ITEM = 'AREA' (idem CUBATURE/PERSON); tmpSql := 'SELECT ED.'||PFIELDAREA||', ED.'||PFIELDCUBATURE||', C.'||PFIELDPERSON||...'; PVALUEAREA/PVALUECUBATURE/PVALUEPERSON := 1 di default.
  • Customer-specific / hard-coded: la chiave 'FIELDUNIT' e gli item AREA/CUBATURE/PERSON sono hard-coded nella procedura, ma quali colonne di EDIFICI/COMPRENSORI_ vengono lette dipende dai valori configurati in NRG_DICTIONARY (mappatura per installazione). SQL dinamico costruito per concatenazione → dipende dall'integrità del dizionario (potenziale rischio se il valore contiene testo arbitrario).

BR-ENE-011 — Lettura multi-canale: somma A1+A2+A3

  • Statement: Per la lettura multi-canale (ReadingCounterDetail, ReadingCounterImgsDetail, WCF SetMeterMultiRead), il valore complessivo MeasureRead è la somma dei tre canali A1 + A2 + A3.
  • Tipo: calculation · Enforcement: mixed (UI+BL) · Confidence: VERIFIED · customer_specific: false
  • Evidenza:
    • ReadingCounterDetail.ascx.cs:346, ReadingCounterImgsDetail.ascx.cs:595MeasureRead = MeasureReadA1 + MeasureReadA2 + MeasureReadA3;
    • WCF EnergyReaderWCF.svc.cs:197... last1 + last2 + last3 ...

BR-ENE-012 — Ridimensionamento immagine

  • Statement: L'immagine caricata viene ridimensionata a un massimo di 1024×768 px e forzata a 72 dpi se superiore, quindi salvata in formato JPEG prima di essere passata come BLOB.
  • Tipo: calculation · Enforcement: UI · Confidence: VERIFIED · customer_specific: false
  • Evidenza: ReadingCounterImgsDetail.ascx.csImgRedim (:410-460): int MaxH = 768; int MaxW = 1024; ... if (f.Width > MaxW || f.Height > MaxH || bmp.HorizontalResolution > 72) ...; salvataggio redimImage.Save(msRedim, ImageFormat.Jpeg) (:580,609,627,645).
  • Magic numbers: 1024, 768, 72.

BR-ENE-013 — Modifica lettura = delete + reinsert (non incrementale)

  • Statement: L'update di una lettura non aggiorna in-place: la procedura cancella i record del contatore a partire dalla data pertinente (inclusi i record spalmati 'm') e li reinserisce ricalcolando consumo e spalmatura. La logica di individuazione della data di taglio è stata modificata a luglio 2018.
  • Tipo: state-transition · Enforcement: DB · Confidence: VERIFIED · customer_specific: false
  • Evidenza: NRG_UPDATEDATACOLLECTOR (USER_SOURCE): commento --**CANCELLO TUTTI I DATI DELLA LETTURA CHE STO MODIFICANDO, ANCHE I RECORD SPALMATI; DELETE FROM NRG_DATACOLLECTOR ND WHERE ND.ID_METER = PIDMETER AND ND.COLLECTSOURCE = 'M' (ramo PLASTMANINSERTDATE IS NULL) o ... COLLECTDATE > PLASTMANINSERTDATE / >= PCOLLECTDATE; commenti datati --M.A. 07/2018.
  • Date-sensitive logic: presenza di blocchi commentati datati (07/2018) e comportamento dipendente dalla data della penultima lettura manuale. PLASTMANINSERTDATE filtra COLLECTSOURCE='M' e COLLECTDATE <= PCOLLECTDATE AND COLLECTDATE < PDATE.

BR-ENE-014 — Solo l'ultima lettura è modificabile

  • Statement: In modalità Modifica, se la lettura selezionata ha data antecedente alla penultima lettura manuale (hdnMaxDate_0), il pulsante Salva viene nascosto: non è possibile modificare letture diverse dall'ultima.
  • Tipo: permission · Enforcement: UI · Confidence: VERIFIED · customer_specific: false
  • Evidenza: ReadingDetail.ascx.cs:156-157if (this.CurrentReading.CollectDate < DateTime.ParseExact(hdnMaxDate_0.Value, "dd/MM/yyyy", ...)) btnSave.Visible = false;
  • Rischio: enforcement solo UI (nasconde il pulsante), non a livello di procedura.

BR-ENE-015 — Validazione update dipendente dal control

  • Statement: La validazione dell'update non è uniforme: ReadingDetail.UpdateDataCollector blocca l'update solo se hdnMaxDate_0 > CollectDate (data antecedente alla penultima); ReadingCounterImgsDetail.UpdateDataCollector non applica alcuna validazione (la modifica è incondizionata, con il check originale marcato "Deprecato 24/10/2014").
  • Tipo: validation · Enforcement: UI · Confidence: VERIFIED · customer_specific: false
  • Evidenza:
    • ReadingDetail.ascx.cs:223-227 — guardia su hdnMaxDate_0
    • ReadingCounterImgsDetail.ascx.cs:490-521 — blocco /* Deprecato 24/10/2014 la modifica di una lettura è incondizionata ... */ e update diretto senza controllo
  • Date-sensitive / variante: comportamento divergente tra control con annotazione datata.

BR-ENE-016 — Incoerenza operatore guardia insert (> vs >=)

  • Statement: La guardia sulla data in inserimento usa operatori diversi tra i control: ReadingDetail usa > (una lettura con la stessa data dell'ultima è ammessa), mentre ReadingCounterDetail/ReadingCounterImgsDetail usano >= (stessa data bloccata).
  • Tipo: validation · Enforcement: UI (rischio incoerenza) · Confidence: VERIFIED · customer_specific: false
  • Evidenza:
    • ReadingDetail.ascx.cs:256hdnMaxDate.Value > this.CurrentReading.CollectDate
    • ReadingCounterDetail.ascx.cs:306 e ReadingCounterImgsDetail.ascx.cs:536hdnMaxDate.Value >= this.CurrentReading.CollectDate

BR-ENE-017 — WCF: insert vs update in base alla data

  • Statement: Nelle operazioni WCF SetMeterMonoRead/SetMeterMultiRead, se la data di lettura (readTime) coincide con la data dell'ultima lettura esistente si chiama UpdateDataCollect, altrimenti InsertDataCollect. Se non esistono letture precedenti si esegue comunque Insert.
  • Tipo: date-logic / state-transition · Enforcement: BL · Confidence: VERIFIED · customer_specific: false
  • Evidenza: EnergyReaderWCF.svc.cs:153-156if (readTime.CompareTo(collectDate) == 0) return dcc.UpdateDataCollect(dataC); else return dcc.InsertDataCollect(dataC);; fallback :164 return dcc.InsertDataCollect(dataC);. Idem SetMeterMultiRead (:223-233).

BR-ENE-018 — Autenticazione WCF (sessione, non grant)

  • Statement: Ogni operazione WCF decifra loginkey da header SOAP r1; se il GUID è vuoto o la sessione client non è validata, ritorna −1 (setter) o null/lista vuota (getter), senza toccare il DB.
  • Tipo: permission · Enforcement: BL · Confidence: VERIFIED · customer_specific: false
  • Evidenza: EnergyReaderWCF.svc.cs:23-38 (DecryptLoginId, ValidateClientSession), guardie :118-122,166,168 (mono), :190-194,235,237 (multi).
  • Nota: il gate WCF è di sessione, non di grant 15020/15010: la separazione insert/modifica di BR-ENE-001 non è replicata sul canale WCF.

BR-ENE-019 — Selettore contatori forzato a Misuratore

  • Statement: Nel dettaglio lettura interattivo il filtro contatori è forzato a IdMeterType = 2 (Misuratore).
  • Tipo: config-flag · Enforcement: UI · Confidence: VERIFIED · customer_specific: false
  • Evidenza: ReadingDetail.ascx.cs:92_NRGFS.IdMeterType = 2; //Misuratore
  • Magic number: 2.

BR-ENE-020 — ACCURACY popolato con IdImportSource (anomalia)

  • Statement: Nel DAL, il parametro PACCURACY passato alle procedure è valorizzato con data.IdImportSource, non con un valore di accuratezza; la colonna ACCURACY di NRG_DATACOLLECTOR riceve quindi l'id sorgente di import.
  • Tipo: calculation (anomalia di mappatura) · Enforcement: BL/DAL · Confidence: SUPPORTED · customer_specific: false
  • Evidenza: EnergyDataCollector_DAL.cs:406 (InsertDataCollect), :172 (AddDataCollect), :259 (UpdateDataCollect) — new OracleParameter("PACCURACY", OracleDbType.Int32, data.IdImportSource, ParameterDirection.Input). La procedura scrive PACCURACY in colonna ACCURACY senza trasformazioni.
  • Nota: probabile refuso storico; segnalato come rischio dati, non come regola voluta. Da confermare con il team.

BR-ENE-021 — Arrotondamenti e NVL sui valori scritti

  • Statement: In NRG_ADDDATACOLLECTOR i valori numerici sono normalizzati: MEASUREREAD = NVL(x,0), MEASUREAVG/MEASUREAMOUNT/A1..A3/PREV1..3 = ROUND(NVL(x,0),4), INSERTDATE = SYSDATE. In spalmatura il consumo è arrotondato a 8 decimali (BR-ENE-006).
  • Tipo: calculation · Enforcement: DB · Confidence: VERIFIED · customer_specific: false
  • Evidenza: NRG_ADDDATACOLLECTOR VALUES (..., NVL(PMEASUREREAD,0), ROUND(NVL(PMEASUREAVG,0),4), ROUND(NVL(PMEASUREAMOUNT,0),4), ... SYSDATE, ...).

BR-ENE-022 — Assenza di CHECK constraint sulla lettura (rischio)

  • Statement: La tabella NRG_DATACOLLECTOR ha come unico vincolo la PK; nessun CHECK vincola i valori ammessi di COLLECTSOURCE, la non-negatività dei consumi/letture o l'ordinamento delle date. Tutte queste regole vivono solo nel codice applicativo.
  • Tipo: validation · Enforcement: DB (assenza di enforcement) · Confidence: VERIFIED · customer_specific: false
  • Evidenza: USER_CONSTRAINTS per NRG_DATACOLLECTOR → solo PK_NRG_DATACOLLECTOR (tipo P), nessun constraint C. Quasi tutte le colonne (incl. COLLECTSOURCE, COLLECTDATE, MEASUREREAD) sono NULLABLE = Y.
  • Rischio: combinato con BR-ENE-003 (UI-only) e BR-ENE-018 (WCF senza guardia di ordinamento), consente in linea di principio letture con data/valore incoerenti o COLLECTSOURCE arbitrario tramite chiamate dirette al BL. Presente anche colonna MEASUREAMOUNT_M non popolata da questo flusso (possibile campo residuo).

BR-ENE-023 — Data lettura preimpostata a oggi

  • Statement: All'apertura in creazione, il selettore data lettura è inizializzato a DateTime.Today.
  • Tipo: date-logic · Enforcement: UI · Confidence: VERIFIED · customer_specific: false
  • Evidenza: ReadingDetail.ascx.cs:126this.rdpCollectDate.SelectedDate = DateTime.Today;

Regole enforced solo in UI (rischio)

  • BR-ENE-003 (guardia data/valore insert), BR-ENE-014 (solo ultima lettura modificabile), BR-ENE-015/BR-ENE-016 (validazioni update/insert divergenti), BR-ENE-019 (filtro Misuratore), BR-ENE-023 (default data), BR-ENE-001 (grant come visibilità pulsante). Nessuna di queste è replicata a livello di procedura/constraint.

Regole enforced solo in SQL (procedura/trigger/constraint)

  • BR-ENE-004 (perimetro 'M'), BR-ENE-005/BR-ENE-006 (consumo e spalmatura), BR-ENE-007 (meter attivo), BR-ENE-008 (immagine solo 'M'), BR-ENE-009 (PK/sequence via trigger), BR-ENE-010 (denormalizzazione da dizionario), BR-ENE-013 (delete+reinsert in update), BR-ENE-021 (arrotondamenti). Vincolo di integrità: FK NRG_READINGS_IMG → NRG_DATACOLLECTOR.

Magic number / id hard-coded

  • Grant 15020 (insert), 15010 (modifica) — BR-ENE-001.
  • IdMeterType = 2 (Misuratore) — BR-ENE-019.
  • Chiave dizionario 'FIELDUNIT' + item AREA/CUBATURE/PERSON — BR-ENE-010.
  • Codici COLLECTSOURCE 'M'/'m' — BR-ENE-002/006/008.
  • Flag ISACTIVE=1, ISCANCELLED=0 — BR-ENE-007.
  • Arrotondamenti a 4 / 8 decimali — BR-ENE-021/006.
  • Immagine 1024×768 @72dpi — BR-ENE-012.

Logica sensibile alla data / annotazioni datate

  • NRG_UPDATEDATACOLLECTOR: rework --M.A. 07/2018 (BR-ENE-013).
  • ReadingCounterImgsDetail: check update "Deprecato 24/10/2014" (BR-ENE-015).

Varianti customer-specific / configurabili

  • BR-ENE-010: mappatura colonne EDIFICI/COMPRENSORI_ via NRG_DICTIONARY (FIELDUNIT) — configurabile per installazione (customer_specific).

Open questions

  • Esiste un enforcement server-side dei grant 15020/15010 oltre alla visibilità del pulsante Salva? (allo stato risulta solo UI)
  • MEASUREAMOUNT_M in NRG_DATACOLLECTOR: da quale flusso è popolata (non da NRG_ADDDATACOLLECTOR)?
  • BR-ENE-020: la mappatura PACCURACY = IdImportSource è voluta o un refuso?
  • Comportamento in PROD (analisi condotta su INFOCAD_TEST38); eventuali CHECK aggiuntivi presenti in altri ambienti?
  • NRG_READINGS_IMG: politica su record immagine "vuoti" (tutte le colonne BLOB nullable) e su letture multi-immagine.