BR-ACC — Regole di business del dominio Accounting / Contabilità lavori (SAL)
Estrazione delle regole di business del modulo Contabilità lavori (
WorkAccounting), ancorata adocs/07-business-flows/FLOW-ACC-001.mde alla relativa evidenza. Il flusso pilota è l'emissione/aggiornamento di uno Stato Avanzamento Lavori (SAL): wizardSalPageWizard.aspx(5 step, ASP.NET Web Forms C#) →WorkAccountingController→ DAOWorkAccounting_DAL(via .NET Remoting) → procedura standalone OracleINFOCAD_TEST38.WRKACC_INSERTSAL(nessun ORM). Il ciclo di vita completo copre anche emissione/chiusura (WRKACC_CLOSESAL), cancellazione (WRKACC_DELETESAL) e le verifiche di eliminabilità/emissione (WRKACC_CANSALTOBEDEL/WRKACC_CANSALTOBEISSUED), invocate dalla pagina di listaSalPage.ascx.Le regole sotto coprono: validazioni di quadratura importi e di composizione dello SAL, calcolo del progressivo per anno fiscale, identity, scorporo IVA sulle allocazioni di costo, transazionalità dell'upsert, transizioni di stato (emissione/riapertura), vincoli di storno vs SAL normale, e le varianti customer-specific dell'integrazione SAP.
Legenda enforcement: UI = solo lato interfaccia (code-behind/markup) · BL = business layer (controller/DAL) · DB = vincolo/PL-SQL sul database · mixed = più punti.
Legenda confidence: VERIFIED = letto su codice e/o sorgente DB (USER_SOURCE/USER_ARGUMENTS/USER_TRIGGERS/USER_TAB_COLUMNS) · SUPPORTED = confermato da sorgente ma non testato a runtime · INFERRED = dedotto dalla struttura, non testato.
Registro regole
| ID | Regola | Tipo | Enforcement | Conf. |
|---|---|---|---|---|
| BR-ACC-001 | Create vs Update dello SAL pilotato da PIDSAL (>0 ⇒ UPDATE, altrimenti INSERT) |
state-transition | DB | VERIFIED |
| BR-ACC-002 | Progressivo SAL per anno fiscale: PROGRSAL = MAX(PROGRSAL WHERE IDFY)+1 (non globale) |
calculation | DB | VERIFIED |
| BR-ACC-003 | Identity IDSAL da trigger SEQ_WORK_ACC_SAL.NEXTVAL; IDSAL immutabile (ORA-20000) |
state-transition | DB | VERIFIED |
| BR-ACC-004 | Su UPDATE: DELETE totale di dettagli+allocazioni e reinserimento (full-replace idempotente) | state-transition | DB | VERIFIED |
| BR-ACC-005 | Allocazioni di costo registrate al netto IVA (AMOUNT/(1+RATE), ROUND 2); finanziamenti al lordo |
calculation | DB | VERIFIED |
| BR-ACC-006 | Importi passati come stringhe locale, normalizzati con REPLACE(',','.') + NLS_NUMERIC_CHARACTERS='.,' |
calculation | DB ⚠️ | VERIFIED |
| BR-ACC-007 | Upsert transazionale: COMMIT+POUT=1; WHEN OTHERS⇒ROLLBACK+POUT=0 (esito 1/0, non l'id) |
validation | DB ⚠️ | VERIFIED |
| BR-ACC-008 | Quadratura obbligatoria: somma pagamenti SAL = somma esborsi (totSal != totExpense blocca) |
validation | UI ⚠️ | VERIFIED |
| BR-ACC-009 | Almeno una voce di spesa (EXPENSECHART non vuoto) per proseguire |
validation | UI ⚠️ | VERIFIED |
| BR-ACC-010 | Almeno un pagamento SAL (SALPAYMENTS non vuoto) per proseguire |
validation | UI ⚠️ | VERIFIED |
| BR-ACC-011 | Ogni voce di spesa (IDBCD) deve avere un pagamento SAL corrispondente |
validation | UI ⚠️ | VERIFIED |
| BR-ACC-012 | Con EnabledServiceSAP: un SAL può riferirsi a un solo fornitore/contratto (GetNumContracts > 2) |
validation | UI ⚠️ | VERIFIED |
| BR-ACC-013 | Testata: contratto, descrizione e note obbligatori (ValidationGroup="headersave"); aliquota NON in tale gruppo |
validation | UI ⚠️ | SUPPORTED |
| BR-ACC-014 | Colonne FK di testata (IDCONTR/IDFY/IDSIGN/IDTR) tutte NULLABLE: nessun vincolo di presenza a DB |
validation | DB (assenza) ⚠️ | VERIFIED |
| BR-ACC-015 | SAL già emesso (ISCLOSED=1) non eliminabile |
state-transition | mixed | VERIFIED |
| BR-ACC-016 | SAL normale non eliminabile se le sue voci sono in un SAL di storno non ancora emesso | validation | DB | VERIFIED |
| BR-ACC-017 | SAL di storno non emettibile se le sue voci sono in un SAL normale non ancora emesso | validation | DB | VERIFIED |
| BR-ACC-018 | Emissione SAL: ISCLOSED=1, DATAISSUE=SYSDATE (WRKACC_CLOSESAL) |
state-transition | DB | VERIFIED |
| BR-ACC-019 | WRKACC_CLOSESAL ignora lo stato: CloseSal(key,0) (riapertura post-errore SAP) NON riapre lo SAL |
state-transition | mixed ⚠️ | SUPPORTED |
| BR-ACC-020 | Integrazione SAP (EnabledServiceSAP): all'emissione chiama IServiceSAP.CreateAPS; esito != "S" = errore |
customer-specific | UI/BL ⚠️ | VERIFIED |
| BR-ACC-021 | DeleteSal è cancellazione fisica (DELETE allocazioni+dettagli+testata), non soft-delete |
state-transition | DB ⚠️ | VERIFIED |
| BR-ACC-022 | Flag storno ISREVERSAL (0/1) da checkbox; default 0; discrimina le regole di del/emissione |
config-flag | mixed | VERIFIED |
| BR-ACC-023 | Reset ISCANCELLED=0 e stamping CREATED/MODIFIED=SYSDATE a ogni insert/update |
config-flag | DB | VERIFIED |
⚠️ = regola a rischio (solo-UI, solo-DB, assenza di vincolo, magic number, hidden flag, discrepanza o bypass).
Dettaglio regole
BR-ACC-001 — Create vs Update pilotato da PIDSAL
Tipo: state-transition · Enforcement: DB · Conf.: VERIFIED · customer_specific: no
La procedura decide il ramo su PIDSAL: se PIDSAL > 0 esegue UPDATE della testata (WORK_ACC_SAL) previa DELETE dei dettagli/allocazioni; altrimenti INSERT. La code-behind valorizza sal.ID = SalID da query string (salID>0 ⇒ modifica).
Evidenza: WRKACC_INSERTSAL src:29-59; SalPageWizard.aspx.cs:80,222; WorkAccounting_DAL.cs:1035.
BR-ACC-002 — Progressivo SAL per anno fiscale
Tipo: calculation · Enforcement: DB · Conf.: VERIFIED · customer_specific: no
Solo su INSERT: SELECT NVL(MAX(NVL(PROGRSAL,0)),0) INTO PROGRSAL FROM WORK_ACC_SAL WHERE IDFY = PIDFY e si inserisce PROGRSAL + 1. Il progressivo è per anno fiscale (IDFY), non globale. ⚠️ Nessun lock/sequenza dedicata: due emissioni concorrenti sullo stesso IDFY possono calcolare lo stesso MAX+1 (rischio di progressivi duplicati; non esiste unique constraint su (IDFY, PROGRSAL) — vedi vincoli tabella).
Evidenza: WRKACC_INSERTSAL src:48-56.
BR-ACC-003 — Identity IDSAL e immutabilità
Tipo: state-transition · Enforcement: DB · Conf.: VERIFIED · customer_specific: no
L'IDSAL NON è nella lista di INSERT: è assegnato da un trigger di INSERT (ENABLED) su WORK_ACC_SAL che fa SELECT SEQ_WORK_ACC_SAL.NEXTVAL quando :new.IDSAL IS NULL. La procedura rilegge PSALID := SEQ_WORK_ACC_SAL.CURRVAL. In UPDATE il trigger blocca il cambio di IDSAL con RAISE_APPLICATION_ERROR(-20000, 'Non posso cambiare il contatore per la tabella WORK_ACC_SAL'). ⚠️ Magic number -20000 hard-coded.
Evidenza: USER_TRIGGERS WORK_ACC_SAL (INSERT, ENABLED, corpo verificato); WRKACC_INSERTSAL src:57.
BR-ACC-004 — Full-replace idempotente dei dettagli in UPDATE
Tipo: state-transition · Enforcement: DB · Conf.: VERIFIED · customer_specific: no
Nel ramo UPDATE la procedura cancella integralmente i figli prima di reinserirli: DELETE WORK_ACC_SAL_DETAILS / WORK_ACC_FINANCING_DET_ALLOC / WORK_ACC_BDG_COSTS_DET_ALLOC WHERE IDSAL = PSALID, poi i cicli di INSERT ricreano dettagli e allocazioni. L'aggiornamento è quindi un rimpiazzo totale, non un merge incrementale.
Evidenza: WRKACC_INSERTSAL src:34-45,63-101.
BR-ACC-005 — Scorporo IVA sulle allocazioni di costo
Tipo: calculation · Enforcement: DB · Conf.: VERIFIED · customer_specific: no
Le allocazioni sui costi di budget sono registrate al netto dell'IVA: si legge l'aliquota RATE da WORK_ACC_TAXRATE (join su WORK_ACC_BUDGET_COSTS_DETAILS.IDTR) e si applica TEMPCOSTAMOUNTNORATE = ROUND(TEMPCOSTAMOUNT / (1 + RATE), 2) prima dell'INSERT in WORK_ACC_BDG_COSTS_DET_ALLOC. Le allocazioni di finanziamento (WORK_ACC_FINANCING_DET_ALLOC) sono invece registrate al lordo (FINAMOUNT così com'è). ⚠️ La formula e il ROUND a 2 decimali sono hard-coded; asimmetria netto/lordo tra costi e finanziamenti.
Evidenza: WRKACC_INSERTSAL src:84-97.
BR-ACC-006 — Normalizzazione locale degli importi
Tipo: calculation · Enforcement: DB · Conf.: VERIFIED · customer_specific: no
Gli importi arrivano dal DAL come stringhe (FINAMOUNT/COSTAMOUNT sono STRINGLIST) e vengono convertiti con TO_NUMBER(REPLACE(..., ',', '.')); la procedura forza inoltre EXECUTE IMMEDIATE 'ALTER SESSION SET NLS_NUMERIC_CHARACTERS = ".,"'. ⚠️ L'ALTER SESSION non viene ripristinato: sulla sessione riusata dal connection pool l'impostazione NLS resta alterata per le chiamate successive (possibile effetto collaterale su altre procedure che assumono le NLS di default).
Evidenza: WRKACC_INSERTSAL src:26,69,74,91,97; WorkAccounting_DAL.cs:1057,1066 (GetStringArray).
BR-ACC-007 — Transazionalità dell'upsert ed esito 1/0
Tipo: validation · Enforcement: DB · Conf.: VERIFIED · customer_specific: no
L'intero upsert (testata + cicli dettagli + cicli costi) è racchiuso tra BEGIN e COMMIT; POUT è impostato a 1 in caso di successo. In EXCEPTION WHEN OTHERS la procedura fa ROLLBACK e imposta POUT := 0 (nessuna riga persistita). ⚠️ POUT è un esito booleano 1/0, non il nuovo IDSAL; inoltre l'eccezione è inghiottita (nessun RAISE), quindi il DAL riceve 0 senza dettaglio dell'errore e la code-behind del wizard non usa ret (chiude comunque la finestra e ripulisce la Session anche in caso di fallimento).
Evidenza: WRKACC_INSERTSAL src:104-116; WorkAccounting_DAL.cs:1094; SalPageWizard.aspx.cs:253,257-262.
BR-ACC-008 — Quadratura pagamenti SAL = esborsi
Tipo: validation · Enforcement: UI · Conf.: VERIFIED · customer_specific: no
Allo step 3 la code-behind calcola totSal = Sum(AMOUNT) di SALPAYMENTS e totExpense = Sum(AMOUNT) di EXPENSECHART; se totSal != totExpense mostra StringSalPaymentsExpenseDifferent e torna allo step precedente. ⚠️ Regola solo-UI (nessun controllo di quadratura nella procedura); confronto con != tra double (rischio di falsi negativi per arrotondamenti in virgola mobile).
Evidenza: SalPageWizard.aspx.cs:160-169.
BR-ACC-009 — Almeno una voce di spesa
Tipo: validation · Enforcement: UI · Conf.: VERIFIED · customer_specific: no
Allo step 2, se EXPENSECHART.Rows.Count == 0 mostra StringSalExpensesNull e blocca l'avanzamento. ⚠️ Solo-UI.
Evidenza: SalPageWizard.aspx.cs:115-125.
BR-ACC-010 — Almeno un pagamento SAL
Tipo: validation · Enforcement: UI · Conf.: VERIFIED · customer_specific: no
Allo step 3, se SALPAYMENTS.Rows.Count == 0 mostra StringSalPaymentsNull e blocca. ⚠️ Solo-UI.
Evidenza: SalPageWizard.aspx.cs:135-143.
BR-ACC-011 — Corrispondenza voci di spesa ↔ pagamenti
Tipo: validation · Enforcement: UI · Conf.: VERIFIED · customer_specific: no
Per ogni riga di EXPENSECHART deve esistere almeno un pagamento in SALPAYMENTS con lo stesso IDBCD (filtro "IDBCD = " + expense["IDBCD"]); in caso contrario StringCheckSalPaymentsExpenseEquals e ritorno allo step precedente. ⚠️ Solo-UI.
Evidenza: SalPageWizard.aspx.cs:146-157.
BR-ACC-012 — Un solo fornitore/contratto per SAL (SAP)
Tipo: validation · Enforcement: UI · Conf.: VERIFIED · customer_specific: sì
Allo step 5, se EnabledServiceSAP è attivo e SalHeaderPage1.GetNumContracts > 2 (⚠️ magic number 2: la combo dei contratti carica anche un record vuoto, quindi >2 significa più di un contratto reale) mostra StringSalContractCheck e blocca il salvataggio. Serve a evitare incongruenza tra le voci di budget-fornitore e il contratto assegnato allo SAL. ⚠️ Solo-UI, hidden flag di configurazione EnabledServiceSAP, variante cliente.
Evidenza: SalPageWizard.aspx.cs:190-198.
BR-ACC-013 — Campi di testata obbligatori
Tipo: validation · Enforcement: UI · Conf.: SUPPORTED · customer_specific: no
Gli step 3/4 hanno ValidationGroup="headersave" con validazione client (Page_ClientValidate('headersave')). Sono RequiredFieldValidator nel gruppo headersave: contratto (rfvContract), descrizione (rfvDescription), note (rfvNote). ⚠️ Il validatore dell'aliquota IVA (rfvTaxRate) è nel gruppo "Add", non headersave: potrebbe non scattare sul salvataggio del wizard (potenziale gap di validazione lato UI). Solo-UI.
Evidenza: SalPageWizard.aspx:67,170,173; SalHeaderPage.ascx:116-117,126-127,137-138,161-162.
BR-ACC-014 — Nessun vincolo di presenza a DB sulla testata
Tipo: validation · Enforcement: DB (assenza) · Conf.: VERIFIED · customer_specific: no
Su WORK_ACC_SAL solo IDSAL è NOT NULL; IDCONTR, IDFY, IDSIGN, IDTR, DESCRIPTION, NOTE, DATASAL sono tutte NULLABLE. Esistono FK (FK_WORK_ACC_SAL_IDCONTR, FK_WORK_ACC_SAL_IDTR, FK_WORK_ACC_SAL_WORK_ACC_SIGN, FK_WORK_ACC_BUDGET_SAL_IDFY) ma nessun NOT NULL né CHECK. ⚠️ L'obbligatorietà dei campi di testata è delegata interamente alla UI (BR-ACC-013): un chiamante non-UI della procedura potrebbe persistere uno SAL con testata incompleta.
Evidenza: USER_TAB_COLUMNS WORK_ACC_SAL (NULLABLE); USER_CONSTRAINTS WORK_ACC_SAL (4×R, 1×P, nessun CHECK).
BR-ACC-015 — SAL emesso non eliminabile
Tipo: state-transition · Enforcement: mixed · Conf.: VERIFIED · customer_specific: no
Nella lista SAL la cancellazione è consentita solo se !isClosed (ISCLOSED=0) e CanSalToBeDeleted restituisce vero; altrimenti si mostra "Impossibile eliminare i sal già emessi...". Il controllo ISCLOSED è lato UI, CanSalToBeDeleted è la procedura DB WRKACC_CANSALTOBEDEL (vedi BR-ACC-016).
Evidenza: SalPage.ascx.cs:466-483.
BR-ACC-016 — Divieto di cancellazione per collisione con storno aperto
Tipo: validation · Enforcement: DB · Conf.: VERIFIED · customer_specific: no
WRKACC_CANSALTOBEDEL: per uno SAL normale (ISREVERSAL=0) conta le proprie voci (IDBCD) che compaiono anche in dettagli di SAL di storno (ISREVERSAL=1) non ancora emessi (ISCLOSED=0); se il conteggio >0 restituisce POUT=0 (non cancellabile), altrimenti 1. Su SAL di storno il controllo è saltato (sempre cancellabile). WHEN OTHERS ⇒ POUT=0.
Evidenza: WRKACC_CANSALTOBEDEL src:8-43; WorkAccounting_DAL.cs:1938.
BR-ACC-017 — Divieto di emissione di storno con voci in SAL normale aperto
Tipo: validation · Enforcement: DB · Conf.: VERIFIED · customer_specific: no
WRKACC_CANSALTOBEISSUED: per uno SAL di storno (ISREVERSAL=1) conta le proprie voci (IDBCD) presenti in SAL normali (ISREVERSAL=0) non ancora emessi (ISCLOSED=0); se >0 restituisce POUT=0 (non emettibile), altrimenti 1. Su SAL normale il controllo è saltato. La UI blocca con "Impossibile emettere sal di storno, le cui voci sono presenti in uno o più sal positivi non ancora emessi." WHEN OTHERS ⇒ POUT=0.
Evidenza: WRKACC_CANSALTOBEISSUED src:8-47; WorkAccounting_DAL.cs:1963; SalPage.ascx.cs:494-500,567.
BR-ACC-018 — Emissione/chiusura SAL
Tipo: state-transition · Enforcement: DB · Conf.: VERIFIED · customer_specific: no
WRKACC_CLOSESAL marca lo SAL come emesso: UPDATE WORK_ACC_SAL SET ISCLOSED = 1, DATAISSUE = SYSDATE WHERE IDSAL = PIDSAL, poi COMMIT e POUT=1 (WHEN OTHERS ⇒ POUT=0). L'emissione è invocata dalla lista solo se CanSalToBeIssued è vero.
Evidenza: WRKACC_CLOSESAL src:7-15; SalPage.ascx.cs:503.
BR-ACC-019 — WRKACC_CLOSESAL ignora lo stato passato (discrepanza)
Tipo: state-transition · Enforcement: mixed · Conf.: SUPPORTED · customer_specific: no
⚠️ Discrepanza codice↔DB. Il DAL CloseSal(int salID, int status) aggiunge un parametro PSTATUS (chiamato con 1 = emetti, 0 = riapri) ma la procedura WRKACC_CLOSESAL in DB ha solo PIDSAL e POUT (confermato su USER_ARGUMENTS, nessun overload) e imposta sempre ISCLOSED=1, DATAISSUE=SYSDATE. Di conseguenza la chiamata CloseSal(key, 0) — pensata per riaprire lo SAL dopo un errore di caricamento su SAP — non riapre nulla: lo SAL resta emesso. Il parametro PSTATUS non trova corrispondenza nella firma DB (rischio di errore di binding o di parametro ignorato a seconda della modalità di bind del DataLayer).
Evidenza: WorkAccounting_DAL.cs:1221,1227,1236 (aggiunge PSTATUS); USER_ARGUMENTS WRKACC_CLOSESAL = solo PIDSAL,POUT; WRKACC_CLOSESAL src:1-9; uso "riapertura" in SalPage.ascx.cs:556.
BR-ACC-020 — Integrazione SAP all'emissione (customer-specific)
Tipo: customer-specific · Enforcement: UI/BL · Conf.: VERIFIED · customer_specific: sì
Se EnabledServiceSAP è attivo, dopo la chiusura riuscita dello SAL la lista confeziona i parametri (RIF_SAL=PROGRSAL, RIF_DATA=DATAISSUE yyyy-MM-dd, EBELN=RDL, EBELP=posizione, TBTWR=somma importi) e chiama IServiceSAP.CreateAPS. ⚠️ Esito valutato sul campo TipoMessaggio: "S" = successo (magic string); qualunque altro valore è errore e innesca il tentativo di riapertura (CloseSal(key,0), inefficace per BR-ACC-019) più UpdateStatusSal(key, Messaggio). Hidden flag EnabledServiceSAP, dipendenza da servizio esterno, variante cliente.
Evidenza: SalPage.ascx.cs:508-561.
BR-ACC-021 — Cancellazione fisica dello SAL
Tipo: state-transition · Enforcement: DB · Conf.: VERIFIED · customer_specific: no
WRKACC_DELETESAL esegue DELETE fisico in cascata: WORK_ACC_FINANCING_DET_ALLOC, WORK_ACC_BDG_COSTS_DET_ALLOC, WORK_ACC_SAL_DETAILS, infine WORK_ACC_SAL WHERE IDSAL = PIDSAL, poi COMMIT. ⚠️ È una cancellazione hard, non uno soft-delete, nonostante l'esistenza della colonna ISCANCELLED (che resta inutilizzata per la cancellazione). WHEN OTHERS ⇒ POUT=0 (nessun ROLLBACK esplicito).
Evidenza: WRKACC_DELETESAL src:7-28; WorkAccounting_DAL.cs:979.
BR-ACC-022 — Flag di storno ISREVERSAL
Tipo: config-flag · Enforcement: mixed · Conf.: VERIFIED · customer_specific: no
Lo SAL può essere "di storno": il flag deriva dalla checkbox cbReversalSal (sal.IsReversal = Convert.ToInt32(cbReversalSal.Checked)), è persistito in WORK_ACC_SAL.ISREVERSAL (default 0) e discrimina la logica di BR-ACC-016/017. Nessun vincolo DB oltre al default; valori attesi 0/1.
Evidenza: SalPageWizard.aspx.cs:217,230; WRKACC_INSERTSAL src:44,56; USER_TAB_COLUMNS WORK_ACC_SAL.ISREVERSAL (default 0).
BR-ACC-023 — Stamping temporale e reset stato attivo
Tipo: config-flag · Enforcement: DB · Conf.: VERIFIED · customer_specific: no
A ogni INSERT si valorizza CREATED = SYSDATE, ISCANCELLED = 0; a ogni UPDATE MODIFIED = SYSDATE, ISCANCELLED = 0. Le righe di dettaglio ricevono CREATED = MODIFIED = SYSDATE, ISCANCELLED = 0. L'aggiornamento riporta quindi sempre lo SAL allo stato "attivo".
Evidenza: WRKACC_INSERTSAL src:44,54,56,67,69.
Note trasversali
- Solo-UI (⚠️): le validazioni funzionali di composizione dello SAL (BR-ACC-008…013) vivono
interamente nella code-behind del wizard; la procedura
WRKACC_INSERTSALnon ripete alcuna quadratura/obbligatorietà e le colonne di testata sono NULLABLE (BR-ACC-014). Un chiamante diverso dal wizard potrebbe persistere SAL non quadrati o incompleti. - Solo-DB (⚠️): le regole di storno (BR-ACC-016/017), l'identity/immutabilità (BR-ACC-003), il progressivo per anno fiscale (BR-ACC-002) e la cancellazione fisica (BR-ACC-021) sono esclusivamente nel PL/SQL.
- Discrepanza codice↔DB (⚠️): BR-ACC-019 (
WRKACC_CLOSESALsenzaPSTATUS) è la più rilevante: la riapertura post-errore SAP non ha effetto. - Magic numbers / hidden flags:
-20000(ORA trigger, BR-ACC-003),2(soglia contratti, BR-ACC-012),ROUND(...,2)(BR-ACC-005),EnabledServiceSAP(BR-ACC-012/020),"S"(esito SAP, BR-ACC-020),1/0(esito procedure, BR-ACC-007). - Race condition (⚠️): BR-ACC-002 calcola
MAX(PROGRSAL)+1senza lock né unique constraint su(IDFY, PROGRSAL)→ progressivi potenzialmente duplicati in concorrenza.