Table of Contents

BR-GLB — Regole di business del dominio Global / Infrastruttura trasversale

Regole di business estratte dal flusso pilota FLOW-GLB-001 (creazione di un MObject — oggetto censito/manutentivo, riga di GLOBAL_OBJECTS) e dai sorgenti del dominio Global (GlobalManager / GlobalControllers in InfocadServer, GlobalWeb in InfocadWeb). Il dominio Global è in larga parte infrastruttura trasversale (tenancy, generazione codici, identità/login, spooler, metamodello griglie, audit/debug): molte tabelle GLOBAL_* sono scritte da altri domini tramite manager condivisi e non hanno un entry-point UI proprio. L'unica entità di dominio con un path di scrittura autonomo e completo è l'MObject (GLOBAL_OBJECTS), documentato qui come operazione rappresentativa; per il resto delle tabelle GLOBAL_* si rimanda al dominio proprietario (vedi Cross-reference).

Sistema: Infocad (Descor) — backend Oracle INFOCAD_TEST38, ASP.NET Web Forms (VB.NET) come UI (scheda MObjectDetails.ascx, shell CASSANDRA), servizio Windows InfocadServer raggiunto via .NET Remoting (proxy well-known DAO_Objects.rem); nessun ORM (accesso via stored procedure ODP.NET Execute*StoredProcedure). Nel dominio Global il Controller inoltra direttamente al DAO remoto, senza un layer <Domain>BL intermedio.

Le regole sono classificate per tipo, enforcement point (UI / BL / DB / mixed) e confidence (VERIFIED / SUPPORTED / INFERRED). Gli identificatori tecnici NON sono tradotti.

Note di metodo e provenienza dell'evidenza

  • Evidenza di codice: percorsi relativi alla radice del repository Documentation/ (es. InfocadServer/GlobalManager/DAO/DAO_MObject.vb:115). Esclusi bin/ obj/ packages/.
  • Evidenza DB: letta in sola lettura da USER_SOURCE (sorgente procedure), USER_TRIGGERS (corpo trigger), USER_OBJECTS (tipo/validità) e dal catalogo docs/_generated/db-detail.json (colonne/PK/unique/FK, generato da USER_*) su INFOCAD_TEST38, limitatamente agli oggetti toccati dalle regole (RPS_ADDMOBJECT, RPS_SETMOBJECTTENANTS, RPS_UPDATEMOBJECTSSTATUS, RPS_GETMOBJECTFATHER, COD_GENERATECODE, trigger di GLOBAL_OBJECTS, tabelle GLOBAL_OBJECTS / GLOBAL_TENANCY_OBJECTS). Nessuna lettura di dati applicativi, nessuna DML/DDL. Le righe delle procedure sono indicate come NOME_PROC:NNN (numerazione USER_SOURCE).
  • I sorgenti di RPS_ADDMOBJECT / RPS_SETMOBJECTTENANTS / RPS_UPDATEMOBJECTSSTATUS e i corpi dei tre trigger INSERT di GLOBAL_OBJECTS sono stati letti riga per riga durante questa estrazione: le relative regole DB sono quindi VERIFIED (non solo dedotte dal flusso).
  • Il dominio riusa meccanismi trasversali documentati altrove: la generazione codice (COD_GENERATECODE / GLOBAL_CODE_CONFIGURATION), il binding tenant generico (GLOBAL_TENANCY_BINDINGS, PERMISSION=2), la risoluzione tenant-utente (GLOBAL_LOGIN_SETTINGS) e il log di debug (GLOBAL_DEBUG_LOG) sono VERIFIED in BR-PROP (stessa procedura PRP_MANAGEENTITY); qui sono richiamati come regole del dominio proprietario del meccanismo, con cross-reference.

Registro delle regole (register)

id regola tipo enforcement conf
BR-GLB-001 Semantica create/update dell'MObject pilotata da ID_DTO: 0 ⇒ INSERT (RPS_ADDMOBJECT), > 0 ⇒ UPDATE (RPS_UPDATEMOBJECT) state-transition mixed VERIFIED
BR-GLB-002 ID_Category obbligatoria (> 0): il DAO solleva DAOException("Object Category can't be null") prima di ogni scrittura validation BL VERIFIED
BR-GLB-003 WORKING_STATUS default = 1 se in input è NULL o 0 (normalizzazione nella procedura) calculation DB VERIFIED
BR-GLB-004 Se il padre (PIDFATHER) è NULL/0 viene risolto il padre della categoria via RPS_GETMOBJECTFATHER calculation DB VERIFIED
BR-GLB-005 Posizioni spaziali (ID_POS_C/E/P/V): ogni id ≤ 0 o NULL è normalizzato a NULL prima dell'INSERT validation DB VERIFIED
BR-GLB-006 Identità ID_OBJ assegnata da SEQ_GLOBAL_OBJECTS (trigger CNT_GLOBAL_OBJECTS) se NULL, e immutabile in UPDATE (ORA-20000) calculation DB VERIFIED
BR-GLB-007 Se NAME è vuoto il trigger GLOBAL_OBJECTS_INSERT genera un codice PREFIX(3)-progressivo-id dalla categoria (GLOBAL_CATEGORIES.ID_LINK/NAME) calculation DB VERIFIED
BR-GLB-008 Ogni INSERT crea la riga gemella in GLOBAL_OBJECTS_DATA (ID_O, ID_F) (trigger AFTER INSERT ADD_GLOBAL_OBJECT_DATA) side-effect DB VERIFIED
BR-GLB-009 Storia stato obbligatoria: RPS_UPDATEMOBJECTSSTATUS chiude la riga aperta (ENDDATE=SYSDATE, solo se lo stato cambia) e inserisce sempre una nuova riga in GLOBAL_OM_WS_HISTORY state-transition DB VERIFIED
BR-GLB-010 Se lo stato applicato è di dismissione, RPS_UPDATEMOBJECTSSTATUS archivia gli ODL collegati (MNT_ARCHIVEWORKORDER_BY_OIWS) side-effect DB SUPPORTED
BR-GLB-011 Il binding tenant dell'MObject è creato solo se ID_Tenancy > 0 (guardia DAO); la UI valorizza ID_Tenancy = WS.CurrentTenant.ID_DTO senza guardia IsTenantEnabled permission mixed VERIFIED
BR-GLB-012 Propagazione tenancy agli antenati: RPS_SETMOBJECTTENANTS naviga l'albero (CONNECT BY), fa DELETE dei binding del solo oggetto corrente e INSERT in GLOBAL_TENANCY_OBJECTS (CODICE = NAME) per oggetto e antenati non ancora assegnati state-transition DB VERIFIED
BR-GLB-013 Il binding tenant generico delle entità usa GLOBAL_TENANCY_BINDINGS con PERMISSION=2 (meccanismo trasversale); l'MObject usa invece la tabella dedicata GLOBAL_TENANCY_OBJECTS permission DB VERIFIED
BR-GLB-014 Il tenant dell'operazione è dedotto dall'utente via GLOBAL_LOGIN_SETTINGS × schema ASPNET (cross-schema); infrastruttura di identità Global permission DB VERIFIED
BR-GLB-015 Generazione codice standard entità: COD_GENERATECODE per (entità, tenant), solo se una colonna-codice è configurata in GLOBAL_CODE_CONFIGURATION calculation DB VERIFIED
BR-GLB-016 Gate UI scheda oggetto: Salva/crea visibile solo con grant 1320 o SuperAdmin; Elimina/Ripristina solo con grant 1330 permission UI VERIFIED
BR-GLB-017 TD-008: i grant sono verificati solo in UI; il Controller inoltra 1:1 al DAO remoto senza ri-verifica, e il bulk-loader chiama il DAO in-process ⇒ scrittura MObject eseguibile senza grant da chiamanti server-side permission UI VERIFIED
BR-GLB-018 Unicità del nome per tenant verificata solo in UI (RPS_GETMOBJECTBYCODEANDTNT + warning); nessun unique DB su GLOBAL_OBJECTS.NAME ⇒ bypassabile validation UI VERIFIED
BR-GLB-019 Nessuna BL dedicata: Controller (Ctrl_EntityBase) → DAOFactory → proxy .NET Remoting well-known SingleCall (*.rem); host registrato in Business/Start.vb config-flag mixed VERIFIED
BR-GLB-020 La scrittura programmatica in-process (import massivo) usa lo username literal "System" (overload Insert(dto)) validation BL VERIFIED
BR-GLB-021 GLOBAL_DEBUG_LOG è il sink di debug trasversale, scritto incondizionatamente con COMMIT intermedio dall'upsert generico config-flag DB VERIFIED
BR-GLB-022 Cancellazione logica vs fisica dell'MObject: RPS_DELETEOBJECT (logica) / Restore, con gate CanDeleteMObject + grant 1330; ISCANCELLED come flag state-transition mixed SUPPORTED

Dettaglio delle regole

BR-GLB-001 — Create vs Update dell'MObject pilotato da ID_DTO

  • Regola: la scheda decide create/update dall'id del DTO: ID_DTO = 0ctrl.Insert(...) (nuovo oggetto, RPS_ADDMOBJECT); ID_DTO > 0ctrl.Update(...) (RPS_UPDATEMOBJECT), con eventuale cambio stato se WorkingStatus è variato.
  • Tipo: state-transition · Enforcement: mixed (UI decide il ramo, DB esegue) · Confidence: VERIFIED
  • Evidenza (UI): If ID_DTO = 0 Then ID_DTO = ctrl.Insert(WS.SessionLogin.UserName, oi) ElseIf ID_DTO > 0 Then ctrl.Update(...)InfocadWeb/WebMachine/CASSANDRA/GlobalWeb/UserControls/MObject/MObjectDetails.ascx.vb:160-171.
  • Evidenza (DAO): InsertRPS_ADDMOBJECT (DAO_MObject.vb:115); UpdateRPS_UPDATEMOBJECT (DAO_MObject.vb:244); SetMObjectStatusRPS_UPDATEMOBJECTSSTATUS (DAO_MObject.vb:305).

BR-GLB-002 — Categoria obbligatoria (guardia DAO)

  • Regola: nessun MObject può essere creato senza categoria valida: se ID_Category ≤ 0 il DAO solleva DAOException("Object Category can't be null") prima di aprire il DataLayer.
  • Tipo: validation · Enforcement: BL (DAO server-side) · Confidence: VERIFIED
  • Evidenza: If om.ID_Category <= 0 Then Throw New DAOException("Object Category can't be null")InfocadServer/GlobalManager/DAO/DAO_MObject.vb:77.
  • Nota: a valle il DB ha comunque la FK SYS_C00309651 ID_CATEGORY → GLOBAL_CATEGORIES.ID_C (catalogo GLOBAL_OBJECTS), che garantisce l'esistenza della categoria; la guardia DAO intercetta prima il caso ≤ 0.

BR-GLB-003 — Default WORKING_STATUS = 1

  • Regola: se lo stato di lavoro in input (PWORKINGSTATUS) è NULL o 0, la procedura lo forza a 1 prima dell'INSERT e prima di scrivere la storia stato.
  • Tipo: calculation (default) · Enforcement: DB · Confidence: VERIFIED
  • Evidenza: IF tmpWS IS NULL OR tmpWS = 0 THEN tmpWS := 1; END IF;RPS_ADDMOBJECT:27-31 (USER_SOURCE).
  • Flag: magic number 1 (stato iniziale hard-coded, non parametrico). La colonna WORKING_STATUS è NOT NULL (catalogo GLOBAL_OBJECTS).

BR-GLB-004 — Risoluzione del padre dalla categoria

  • Regola: se il padre gerarchico non è indicato (PIDFATHER NULL o 0), la procedura invoca RPS_GETMOBJECTFATHER(PIDCATEGORY, TMPIDFATHER) per ricavare il nodo-padre di categoria; il valore risolto è usato come ID_FATHER dell'INSERT.
  • Tipo: calculation · Enforcement: DB · Confidence: VERIFIED
  • Evidenza: IF tmpIDFather IS NULL OR tmpIDFather = 0 THEN RPS_GETMOBJECTFATHER(PIDCATEGORY, TMPIDFATHER); END IF;RPS_ADDMOBJECT:58-63; INSERT con tmpIDFatherRPS_ADDMOBJECT:70-73.
  • Nota: ID_FATHER è NOT NULL in GLOBAL_OBJECTS; la risoluzione garantisce un valore non nullo per gli oggetti radice di categoria.

BR-GLB-005 — Normalizzazione delle posizioni spaziali

  • Regola: per ciascuna delle quattro coordinate spaziali (comprensorio/edificio/piano/vano), se il valore in input è NULL o ≤ 0 viene normalizzato a NULL (ID_POS_C/E/P/V restano nullabili); solo i valori > 0 sono persistiti.
  • Tipo: validation (normalizzazione) · Enforcement: DB · Confidence: VERIFIED
  • Evidenza: quattro blocchi IF PIDDISTRICT IS NOT NULL AND PIDDISTRICT > 0 THEN tmpIDDistrict := PIDDISTRICT; END IF; (analoghi per building/floor/room) — RPS_ADDMOBJECT:33-55; default tmpIDxxx := NULLRPS_ADDMOBJECT:20-23.

BR-GLB-006 — Identità ID_OBJ da sequence e immutabilità

  • Regola: alla INSERT, se ID_OBJ è NULL il trigger CNT_GLOBAL_OBJECTS lo valorizza da SEQ_GLOBAL_OBJECTS.NEXTVAL; in UPDATE un tentativo di cambiare ID_OBJ solleva ORA-20000.
  • Tipo: calculation + validation · Enforcement: DB (trigger BEFORE ROW) · Confidence: VERIFIED
  • Evidenza: IF INSERTING THEN IF (:new.ID_OBJ IS NULL) THEN SELECT SEQ_GLOBAL_OBJECTS.NEXTVAL INTO :new.ID_OBJ FROM dual; ... IF UPDATING THEN IF NOT (:new.ID_OBJ = :old.ID_OBJ) THEN RAISE non_posso_cambiare; ... raise_application_error(-20000, 'Non posso cambiare il contatore per la tabella GLOBAL_OBJECTS ') — trigger CNT_GLOBAL_OBJECTS (USER_TRIGGERS, BEFORE EACH ROW INSERT, ENABLED). SEQ_GLOBAL_OBJECTS è SEQUENCE VALID (USER_OBJECTS); ID_OBJ è PK ma non identity column (catalogo).
  • Flag: regola enforced solo in SQL con codice hard-coded -20000. La stessa guardia di immutabilità è ripetuta nel trigger ADD_GLOBAL_OBJECT_DATA (ridondanza, vedi BR-GLB-008). Pattern SEQ_*+trigger comune alle tabelle Infocad (cfr. CNT_EDIFICI/SEQ_GLOBAL_GEOM, BR-PROP-006).

BR-GLB-007 — Auto-generazione del codice quando NAME è vuoto

  • Regola: se NAME è NULL o stringa vuota, il trigger GLOBAL_OBJECTS_INSERT genera un codice nella forma TRIM(SUBSTR(PREFIX,0,3)) || '-' || (NEWCODE+1) || '-' || (IDENTIFIER+1), dove PREFIX = GLOBAL_CATEGORIES.ID_LINK (fallback GLOBAL_CATEGORIES.NAME se il link è vuoto), NEWCODE = COUNT(ID_OBJ)+1 degli oggetti della categoria, IDENTIFIER = MAX(ID_OBJ) della tabella; il codice è scritto in :NEW.NAME.
  • Tipo: calculation · Enforcement: DB (trigger BEFORE INSERT) · Confidence: VERIFIED
  • Evidenza: corpo del trigger GLOBAL_OBJECTS_INSERT (USER_TRIGGERS, BEFORE EACH ROW INSERT, ENABLED): IF :NEW.NAME IS NULL OR :NEW.NAME = '' THEN SELECT COUNT(ID_OBJ)+1 INTO NEWCODE ... SELECT ID_LINK INTO PREFIX FROM GLOBAL_CATEGORIES ... CODE := TRIM(SUBSTR(PREFIX,0,3)) || '-' || CAST((NEWCODE+1) ...) || '-' || CAST((IDENTIFIER+1) ...); :NEW."NAME" := CODE;.
  • Flag:
    • Meccanismo distinto da quello generico COD_GENERATECODE/GLOBAL_CODE_CONFIGURATION (BR-GLB-015): per GLOBAL_OBJECTS la generazione codice è hard-coded nel trigger, non configurabile per tenant.
    • Magic number SUBSTR(PREFIX, 0, 3) (prefisso a 3 caratteri).
    • Race condition / non concorrenza-safe: COUNT(ID_OBJ) e MAX(ID_OBJ) non sono serializzabili sotto insert concorrenti ⇒ codici potenzialmente duplicati o non progressivi in carichi paralleli (es. bulk-loader). Probabile difetto (SUPPORTED come rischio).
    • Quirk di calcolo: il progressivo di categoria è NEWCODE+1 con NEWCODE = COUNT+1, cioè COUNT+2 (doppio incremento).

BR-GLB-008 — Riga gemella in GLOBAL_OBJECTS_DATA

  • Regola: ad ogni INSERT su GLOBAL_OBJECTS il trigger AFTER INSERT ADD_GLOBAL_OBJECT_DATA crea la riga gemella INSERT INTO GLOBAL_OBJECTS_DATA (ID_O, ID_F) VALUES (:new.ID_OBJ, :new.ID_FATHER).
  • Tipo: side-effect · Enforcement: DB (trigger AFTER ROW) · Confidence: VERIFIED
  • Evidenza: corpo trigger ADD_GLOBAL_OBJECT_DATA (USER_TRIGGERS, AFTER EACH ROW INSERT, ENABLED): IF INSERTING THEN INSERT INTO GLOBAL_OBJECTS_DATA (ID_O, ID_F) VALUES (:new.ID_OBJ, :new.ID_FATHER); END IF;.
  • Flag: il trigger ripete inoltre la guardia di immutabilità di ID_OBJ in UPDATE con raise_application_error(-20000) (duplica BR-GLB-006).

BR-GLB-009 — Storia stato obbligatoria e transizione datata

  • Regola: ogni creazione o cambio stato passa da RPS_UPDATEMOBJECTSSTATUS, che (1) aggiorna GLOBAL_OBJECTS.WORKING_STATUS, (2) chiude la riga di storia aperta (ENDDATE=SYSDATE) — ma solo se esiste ed è di stato diverso da quello nuovo — e (3) inserisce una nuova riga in GLOBAL_OM_WS_HISTORY con STARTDATE=SYSDATE, ENDDATE=NULL, NOTE, USERNAME e categoria (OBJ_CATEGORY) dal join GLOBAL_CATEGORIES. In creazione la nota è il literal '-----'.
  • Tipo: state-transition · Enforcement: DB · Confidence: VERIFIED
  • Evidenza: UPDATE GLOBAL_OBJECTS SET WORKING_STATUS = PSTATUS ... (RPS_UPDATEMOBJECTSSTATUS:10-13); chiusura riga aperta UPDATE GLOBAL_OM_WS_HISTORY ws1 SET ENDDATE = TMPSYSDATE WHERE ws1.ENDDATE IS NULL AND ... AND ws1.WORKING_STATUS <> PSTATUS (:15-22); insert nuova riga INSERT INTO GLOBAL_OM_WS_HISTORY (...) SELECT ... TMPSYSDATE STARTDATE, NULL ENDDATE, PNOTE NOTE, PUSERNAME USERNAME FROM GLOBAL_OBJECTS GO JOIN GLOBAL_CATEGORIES GC ... (:24-40); chiamata in creazione RPS_UPDATEMOBJECTSSTATUS(PUSERNAME, TMPOBJLIST, tmpWS, '-----') (RPS_ADDMOBJECT:76-77).
  • Flag:
    • Magic literal '-----' come nota di creazione.
    • Probabile difetto (data quality): l'INSERT della nuova riga è incondizionato, mentre la chiusura della precedente è condizionata a WORKING_STATUS <> PSTATUS. Impostando lo stesso stato una seconda volta si inserisce una nuova riga aperta senza chiudere la precedente ⇒ possibili più righe ENDDATE IS NULL per lo stesso oggetto (SUPPORTED come rischio, dedotto dal sorgente).

BR-GLB-010 — Archiviazione ODL su stato di dismissione

  • Regola: al termine dell'aggiornamento stato, RPS_UPDATEMOBJECTSSTATUS invoca MNT_ARCHIVEWORKORDER_BY_OIWS(PUSERNAME, POBJLIST, PSTATUS, SYSDATE) per archiviare gli ODL (ordini di lavoro) collegati quando lo stato applicato è di dismissione.
  • Tipo: side-effect · Enforcement: DB · Confidence: SUPPORTED (chiamata VERIFIED; logica interna di MNT_ARCHIVEWORKORDER_BY_OIWS non ispezionata riga per riga)
  • Evidenza: -- Cerco eventuali ODL da archiviare se lo "status" applicato è di dismissione + MNT_ARCHIVEWORKORDER_BY_OIWS(PUSERNAME, POBJLIST, PSTATUS, TMPSYSDATE);RPS_UPDATEMOBJECTSSTATUS:43-44.
  • Cross-reference: la semantica ODL/archiviazione è del dominio Maintenance (vedi BR-MNT); quali WORKING_STATUS attivino l'archiviazione resta open question (vedi FLOW-GLB-001).

BR-GLB-011 — Binding tenant dell'MObject solo se ID_Tenancy > 0

  • Regola: dopo l'INSERT dell'oggetto, il DAO crea il binding di tenancy solo se om.ID_Tenancy > 0. La scheda valorizza ID_Tenancy = WS.CurrentTenant.ID_DTO in InstantiateDto, senza la guardia IsTenantEnabled usata canonicamente altrove: l'effettiva enforcement ("binding solo con tenant valido") è quindi la guardia > 0 del DAO.
  • Tipo: permission · Enforcement: mixed (UI valorizza, DAO decide) · Confidence: VERIFIED
  • Evidenza (DAO): If om.ID_Tenancy > 0 Then SetMObjectTenancies(idMo, New List(Of Integer)({om.ID_Tenancy}))DAO_MObject.vb:118-119.
  • Evidenza (UI): Obj.ID_Tenancy = WS.CurrentTenant.ID_DTO (incondizionato) — MObjectDetails.ascx.vb:212.
  • Flag / incoerenza: il pattern canonico è If(WS.IsTenantEnabled = True, WS.CurrentTenant.ID_DTO, Nothing) — es. InfocadWeb/WebMachine/CASSANDRA/GlobalWeb/UserControls/Contract/StockContractBinder.ascx.vb:205. La scheda MObject omette la guardia; il comportamento è comunque corretto perché con tenancy disabilitata WS.CurrentTenant.ID_DTO è 0 e la guardia DAO > 0 non scrive alcun binding. Da uniformare (rischio latente se CurrentTenant non fosse 0 a tenancy disattiva).

BR-GLB-012 — Propagazione della tenancy agli antenati (GLOBAL_TENANCY_OBJECTS)

  • Regola: RPS_SETMOBJECTTENANTS naviga l'oggetto e i suoi antenati nell'albero (CONNECT BY NOCYCLE PRIOR ID_FATHER = ID_OBJ START WITH ID_OBJ = PIDOBJ); per il solo oggetto corrente cancella tutti i binding esistenti (reset), poi — per l'oggetto e per ogni antenato — inserisce il binding (ID_OBJ, ID_TENANCY, CODICE) in GLOBAL_TENANCY_OBJECTS solo se non già presente (COUNT(*) = 0), con CODICE = NAME dell'oggetto.
  • Tipo: state-transition · Enforcement: DB · Confidence: VERIFIED
  • Evidenza: FOR XROW IN (SELECT ID_OBJ, ID_FATHER, "NAME" FROM GLOBAL_OBJECTS CONNECT BY NOCYCLE PRIOR ID_FATHER = ID_OBJ START WITH ID_OBJ = PIDOBJ) LOOP (RPS_SETMOBJECTTENANTS:7-13); IF PIDOBJ = XROW.ID_OBJ THEN DELETE FROM GLOBAL_TENANCY_OBJECTS gto WHERE gto.ID_OBJ = XROW.ID_OBJ; END IF; (:15-20); IF tmpCount = 0 THEN INSERT INTO GLOBAL_TENANCY_OBJECTS (ID_OBJ, ID_TENANCY, CODICE) VALUES (XROW.ID_OBJ, PTENANTIDS(IX), XROW."NAME"); END IF; (:34-40).
  • Nota (DAO): con lista tenant vuota il DAO passa un array PL/SQL NULL (NullInsert) ⇒ per l'oggetto corrente il binding viene solo cancellato (nessun reinserimento) — DAO_MObject.vb:2256-2264.
  • Flag: GLOBAL_TENANCY_OBJECTS ha PK composita (ID_OBJ, ID_TENANCY) (catalogo) ⇒ la guardia COUNT = 0 è ridondante ma evita la violazione di PK sugli antenati già assegnati. La propagazione è verso l'alto (antenati), non verso i figli.

BR-GLB-013 — Binding tenant generico (GLOBAL_TENANCY_BINDINGS, PERMISSION=2)

  • Regola: il binding di tenancy generico delle entità (non-MObject) è tenuto in GLOBAL_TENANCY_BINDINGS (TAB_INFOCAD, ID_INFOCAD, ID_TENANCY, PERMISSION, CODICE) con PERMISSION=2 alla creazione. L'MObject fa eccezione: usa la tabella dedicata GLOBAL_TENANCY_OBJECTS (BR-GLB-012).
  • Tipo: permission · Enforcement: DB · Confidence: VERIFIED (meccanismo VERIFIED in BR-PROP; qui richiamato come regola trasversale del dominio Global)
  • Evidenza: creato/pulito da PRP_MANAGEENTITY — vedi BR-PROP-009 (INSERT con PERMISSION=2) e BR-PROP-029 (DELETE su cancellazione). Tabella GLOBAL_TENANCY_BINDINGS a catalogo (INFOCAD_TEST38).
  • Flag: magic number PERMISSION=2 (mappatura sull'enum TenancyPermission da confermare, vedi BR-PROP-009). Due meccanismi paralleli di binding tenant (GLOBAL_TENANCY_BINDINGS per le entità spaziali/anagrafiche, GLOBAL_TENANCY_OBJECTS per gli oggetti censiti) da tenere allineati.

BR-GLB-014 — Risoluzione del tenant dell'utente (GLOBAL_LOGIN_SETTINGS, cross-schema)

  • Regola: il tenant dell'operazione è dedotto dall'utente autenticato tramite GLOBAL_LOGIN_SETTINGS in join con lo schema ASPNET (ORA_ASPNET_USERS / ORA_ASPNET_APPLICATIONS), filtrando su username e su APPLICATIONNAME = USER; utente non risolto ⇒ fallimento dell'operazione.
  • Tipo: permission · Enforcement: DB · Confidence: VERIFIED (meccanismo VERIFIED in BR-PROP; GLOBAL_LOGIN_SETTINGS è infrastruttura di identità del dominio Global)
  • Evidenza: SELECT ID_TENANCY INTO TENANCYID FROM GLOBAL_LOGIN_SETTINGS GLS JOIN ...ORA_ASPNET_USERS... WHERE OAU.LOWEREDUSERNAME = LOWER(PUSERNAME) AND OAA.APPLICATIONNAME = USER — vedi BR-PROP-010 (PRP_MANAGEENTITY:48-56). Provider lato server: InfocadProfileProvider/ProfileProviderController (GlobalManager/Identity/InfocadProfileProvider.vb, GlobalControllers/ProfileProviderController.vb).
  • Flag: dipendenza cross-schema (ASPNET_TEST38); il legame applicazione↔tenant usa USER (schema Oracle corrente).

BR-GLB-015 — Generazione codice standard entità (COD_GENERATECODE)

  • Regola: il servizio Global di generazione codice espone GenerateStandardCode(id_tenant, entity)COD_GENERATECODE e GetCodeFieldByEntityTenant(id_tenant, entity)COD_GETCODEFIELD, entrambi parametrizzati su (V_TABLE_NAME, V_IDTENANT); il codice è generato solo se per la coppia (entità, tenant) esiste una colonna-codice configurata in GLOBAL_CODE_CONFIGURATION.
  • Tipo: calculation · Enforcement: DB · Confidence: VERIFIED (wrapper VERIFIED qui; interno di COD_GENERATECODE VERIFIED in BR-PROP-005)
  • Evidenza (DAO): dl.ExecuteNonQueryStoredProcedure("COD_GENERATECODE") con V_TABLE_NAME/V_IDTENANT/PRETVALUEInfocadServer/GlobalManager/DAO/DAO_GlobalCodeConfiguration.vb:32-51; COD_GETCODEFIELD:60-83. Interfaccia IDAO_GlobalCode, controller GlobalCodeController (remoting Service_GlobalCode, DAOFactory.vb:151-152).
  • Cross-reference: uso concreto e internals lato DB (lookup GLOBAL_CODE_CONFIGURATION, DBMS_RANDOM) documentati in BR-PROP-005. Nota: GLOBAL_OBJECTS non usa questo meccanismo ma la generazione hard-coded del trigger (BR-GLB-007).
  • Flag: comportamento tenant/cliente-dipendente (dipende dalla configurazione in GLOBAL_CODE_CONFIGURATION).

BR-GLB-016 — Gate UI della scheda oggetto (grant 1320 / 1330)

  • Regola: sulla scheda MObjectDetails, il pulsante Salva è visibile solo con grant 1320 o WS.SessionProfile.SuperAdmin (sia in modifica sia in nuovo); Elimina e Ripristina richiedono grant 1330 (Elimina anche CanDeleteMObject).
  • Tipo: permission · Enforcement: UI · Confidence: VERIFIED
  • Evidenza: Me.btnUpdate.Visible = (Me.HasUserGrant("1320") Or WS.SessionProfile.SuperAdmin) And obj.IsCancelled = False (MObjectDetails.ascx.vb:396; ramo nuovo :403); Me.btnDelete.Visible = (moCtrl.CanDeleteMObject(...) And (Me.HasUserGrant("1330") Or WS.SessionProfile.SuperAdmin) ...) (:399); Me.btnRestore.Visible = (Me.HasUserGrant("1330") Or ...) And obj.IsCancelled = True (:401).
  • Flag: magic number dei codici grant 1320 (edit/create) e 1330 (delete/restore), hard-coded come stringhe nella UI. Vedi BR-GLB-017 per il limite di questo gate.

BR-GLB-017 — TD-008: grant verificati solo in UI, non oltre il confine remoting

  • Regola: l'autorizzazione alla scrittura MObject è verificata solo nell'UI (BR-GLB-016). Il MObjectController implementa IDAO_MObject inoltrando 1:1 ogni metodo al DAO remoto (CType(Me.DAO, IDAO_MObject).Insert(...)), senza alcun HasUserGrant; il DAO server-side (DAO_MObject.Insert) non ri-verifica alcun grant. Inoltre il bulk-loader istanzia DAO_MObject in-process e chiama Insert. Un chiamante che raggiunge il controller/remoting o il tier server può quindi creare/modificare MObject senza grant.
  • Tipo: permission · Enforcement: UI-only (assente in BL/DB) · Confidence: VERIFIED
  • Evidenza: Public Function Insert(...) ... Return CType(Me.DAO, IDAO_MObject).Insert(username, om)InfocadServer/GlobalControllers/MObjectController.vb:21-23 (identico per Update:31-33, SetMObjectStatus:409-411, SetMObjectTenancies:616-618, LogicalDeleteMObjects:640-642; nessun controllo grant nell'intera classe, 648 righe). DAO server-side privo di grant-check — DAO_MObject.vb:73-126. Bulk-loader in-process: Dim daoOM As New DAO_MObject (InfocadServer/BulkLoaderManager/MObjectBulkLoader.vb:201) → daoOM.Insert(OM) (:375).
  • Flag: regola enforced solo in UI — bypassabile via .NET Remoting / integrazioni / bulk-loader. Istanza dominio-Global del difetto sistemico TD-008 (docs/14-technical-debt/reperti-prioritari.md:12), già rilevato in 8 domini (BR-CEN-001, BR-QC-012, BR-BLK-025, BR-CHK-002, BR-RPT-003, BR-DOC-029, BR-EXP-002, BR-PRJ-024). Da confermare con Descor.

BR-GLB-018 — Unicità nome per tenant: solo UI, nessun vincolo DB

  • Regola: prima del salvataggio la scheda verifica che non esista già un oggetto diverso con lo stesso nome nel tenant corrente (GetMObjectByCodeAndTnt(nome, tenant)); se esiste mostra il warning MObjectAlreadyExistMessage e non scrive. Questo controllo è solo lato UI: GLOBAL_OBJECTS non ha alcun vincolo unique su NAME (solo PK su ID_OBJ).
  • Tipo: validation · Enforcement: UI · Confidence: VERIFIED
  • Evidenza (UI): Dim OBJ_Ds As DataSet = ctrl.GetMObjectByCodeAndTnt(TxtName.Text, WS.CurrentTenant.ID_DTO) + If ((idAsset = 0) OrElse (idAsset > 0 AndAlso idAsset = Me.ID_DTO)) Then ... Else NotifyRadMessage(... "MObjectAlreadyExistMessage" ...)MObjectDetails.ascx.vb:141-147,177-179. Proc di lookup RPS_GETMOBJECTBYCODEANDTNTDAO_MObject.vb:2272-2283.
  • Evidenza (DB): GLOBAL_OBJECTSuniques: [], pk: [ID_OBJ] (catalogo db-detail.json); NAME è NOT NULL ma non unico.
  • Flag: regola enforced solo in UI ⇒ nomi duplicati nello stesso tenant inseribili via remoting/bulk-loader. Coerente con TD-008 (BR-GLB-017).

BR-GLB-019 — Nessuna BL dedicata: Controller → DAO remoto (.NET Remoting)

  • Regola: nel dominio Global non esiste un layer <Domain>BL: il Controller eredita Ctrl_EntityBase/Ctrl_Base e, in costruzione, attiva un proxy .NET Remoting verso il DAO server-side; DAOFactory.CreateDAO risolve il tipo di servizio e chiama Activator.GetObject(GetType(IDAO_*), CHANNEL://SERVERADDRESS/<Service>.rem). Il servizio è well-known SingleCall, registrato all'avvio del servizio Windows.
  • Tipo: config-flag (pattern architetturale) · Enforcement: mixed · Confidence: VERIFIED
  • Evidenza: Public Sub New() ... Me.DaoActivate(enControllers.Ctrl_MObject) (MObjectController.vb:16-19, Inherits Ctrl_EntityBase, :10); Protected Sub DaoActivate(...) _dao = DAOFactory.CreateDAO(EntityType) (serv/Ctrl_Base.vb:16-18); Case enControllers.Ctrl_MObject Return DirectCast(Activator.GetObject(GetType(IDAO_MObject), ... My.Resources.ServicesDictionary.Service_Objects), IDaoBase) (serv/DAOFactory.vb:34-35); host well-known RegisterWellKnownServiceType(DAO_MObject, "DAO_Objects.rem", SingleCall)InfocadServer/Business/Start.vb:342 (vedi FLOW-GLB-001).
  • Nota: implica BR-GLB-017 (nessun punto BL dove inserire una ri-verifica dei grant). Canale/indirizzo (SettingsManager.CHANNEL/SERVERADDRESS) vengono da Descor.Common (config di deploy).

BR-GLB-020 — Scrittura programmatica in-process con username "System"

  • Regola: l'overload Insert(dto) (path programmatico/import) delega a Insert("System", dto): le scritture non interattive sono attribuite allo username literal "System" (che finisce in GLOBAL_OM_WS_HISTORY.USERNAME via RPS_UPDATEMOBJECTSSTATUS).
  • Tipo: validation (audit) · Enforcement: BL · Confidence: VERIFIED
  • Evidenza: Public Function Insert(dto As DTOBase) As Integer ... Return Insert("System", DirectCast(dto, MObject))DAO_MObject.vb:69-71; usato dal bulk-loader daoOM.Insert(OM) (MObjectBulkLoader.vb:375).
  • Flag: valore hard-coded "System" — l'operatore reale dell'import non è tracciato nell'audit di stato.

BR-GLB-021 — GLOBAL_DEBUG_LOG: log trasversale incondizionato

  • Regola: GLOBAL_DEBUG_LOG è la tabella di log/diagnostica trasversale del prodotto; l'upsert generico delle entità vi scrive ogni statement SQL costruito, in modo non condizionato e con COMMIT intermedio.
  • Tipo: config-flag (side-effect) · Enforcement: DB · Confidence: VERIFIED (meccanismo VERIFIED in BR-PROP-025; tabella GLOBAL_* di dominio Global)
  • Evidenza: EXECUTE IMMEDIATE 'insert into global_debug_log values(:1,:2,:3,:4)' USING 'INFOCAD','MANAGEENTITY',TMPSQL,SYSDATE; COMMIT; — vedi BR-PROP-025 (PRP_MANAGEENTITY:158-159). Tabella GLOBAL_DEBUG_LOG a catalogo (INFOCAD_TEST38).
  • Flag: logging senza feature-flag, con COMMIT intermedio (implicazioni transazionali e di crescita tabella in PROD).

BR-GLB-022 — Cancellazione logica vs fisica dell'MObject

  • Regola: la cancellazione dell'MObject è tipicamente logica (flag ISCANCELLED), via RPS_DELETEOBJECT (LogicalDelete)/LogicalDeleteMObjects; il ripristino è esposto come Restore. In UI l'azione è gated da CanDeleteMObject(category, id) e dal grant 1330; l'oggetto cancellato mostra Ripristina invece di Salva.
  • Tipo: state-transition · Enforcement: mixed (UI gate + proc DB) · Confidence: SUPPORTED (path delete/restore non tracciato end-to-end come la creazione)
  • Evidenza: LogicalDelete(id_dto)RPS_DELETEOBJECT (DAO_MObject.vb:313-318); LogicalDeleteMObjects(username, ids) (:326); controller Restore (MObjectController.vb:514-516), CanDeleteMObject (:522-524); gate UI :399-401. GLOBAL_OBJECTS ha ISCANCELLED NUMBER NOT NULL e i trigger GLOBAL_OBJECTS_DELETE/DOCS_GLOBAL_OBJECTS_UPD su UPDATE (catalogo/USER_TRIGGERS).
  • Flag: la delete "fisica" reale non è sul path UI (cancellazione logica); il ruolo del trigger GLOBAL_OBJECTS_DELETE (BEFORE UPDATE) nel ciclo di vita resta open question (FLOW-GLB-001).

Sintesi dei rischi e flag

Regole enforced SOLO nell'UI (rischio: bypassabili da altri path)

  • BR-GLB-016/017 — i grant 1320/1330 sono verificati solo nella scheda; il Controller inoltra 1:1 al DAO remoto senza ri-verifica e il bulk-loader scrive in-process ⇒ istanza Global del difetto sistemico TD-008.
  • BR-GLB-018 — l'unicità del nome per tenant è un controllo UI (RPS_GETMOBJECTBYCODEANDTNT), non un vincolo DB.

Regole enforced SOLO in SQL (invisibili al codice applicativo)

  • BR-GLB-003 (default stato 1), BR-GLB-004 (padre di categoria), BR-GLB-005 (normalizzazione posizioni), BR-GLB-006 (identity da sequence + immutabilità -20000), BR-GLB-007 (auto-codice da trigger), BR-GLB-008 (riga gemella GLOBAL_OBJECTS_DATA), BR-GLB-009 (storia stato), BR-GLB-012 (propagazione tenancy antenati), BR-GLB-021 (debug log).

Probabili difetti (VERIFIED sul sorgente, impatto SUPPORTED)

  • BR-GLB-007 — codice auto-generato con COUNT/MAX(ID_OBJ) non concorrenza-safe (duplicati in carichi paralleli) e doppio incremento COUNT+2.
  • BR-GLB-009 — INSERT storia stato incondizionato vs chiusura condizionata (<> PSTATUS) ⇒ possibili più righe aperte (ENDDATE IS NULL) sullo stesso oggetto ri-applicando lo stesso stato.
  • BR-GLB-011 — la scheda MObject omette la guardia IsTenantEnabled (presente nel pattern canonico); non produce difetto grazie alla guardia DAO > 0, ma è un'incoerenza da uniformare.

Magic number / valori e id hard-coded

  • Stato iniziale WORKING_STATUS = 1 (BR-GLB-003); nota storia '-----' (BR-GLB-009); ORA-20000 per il cambio di ID_OBJ (BR-GLB-006/008); prefisso codice a 3 caratteri SUBSTR(PREFIX,0,3) (BR-GLB-007); grant 1320/1330 come stringhe UI (BR-GLB-016); PERMISSION = 2 nel binding generico (BR-GLB-013); username "System" per l'import (BR-GLB-020).

Ridondanze / doppi meccanismi

  • Guardia di immutabilità di ID_OBJ duplicata nei trigger CNT_GLOBAL_OBJECTS e ADD_GLOBAL_OBJECT_DATA (BR-GLB-006/008).
  • Due meccanismi di generazione codice: trigger dedicato per GLOBAL_OBJECTS (BR-GLB-007) vs COD_GENERATECODE/GLOBAL_CODE_CONFIGURATION generico (BR-GLB-015).
  • Due tabelle di binding tenant: GLOBAL_TENANCY_OBJECTS per gli oggetti (BR-GLB-012) vs GLOBAL_TENANCY_BINDINGS per le altre entità (BR-GLB-013).

Feature flag / logging nascosto

  • BR-GLB-021GLOBAL_DEBUG_LOG scritto ad ogni upsert con COMMIT intermedio, senza flag di disattivazione.

Cross-reference (tabelle GLOBAL_* di proprietà di altri domini)

Il dominio Global non ri-documenta le regole delle tabelle GLOBAL_* scritte da domini specifici; per queste si rimanda alla pagina di dominio:

  • GLOBAL_UPLOAD_PROCESS / GLOBAL_UPLOAD_ALERT — motore di import massivoBR-BLK (BulkLoader).
  • GLOBAL_SPOOLER — spooler di export (procedure RPS_*SPOOLER* in InfocadServer/ExportService) → BR-EXP (ExportService).
  • GLOBAL_TAB_COLUMNSmetamodello griglie/colonne IEM (DAO_TabColumns) → BR-IEM.
  • GLOBAL_TENANCY_BINDINGS, GLOBAL_CODE_CONFIGURATION, GLOBAL_LOGIN_SETTINGS, GLOBAL_DEBUG_LOG — scritte dall'upsert generico PRP_MANAGEENTITY nel salvataggio delle entità → BR-PROP (qui richiamate come BR-GLB-013/014/015/021 in quanto meccanismi trasversali).
  • Entità anagrafiche condivise (GLOBAL_COMPANIES, GLOBAL_CONTACTS, GLOBAL_CONTRACTS, GLOBAL_MANAGEMENTS, GLOBAL_COSTCENTRE, …) — gestite dai rispettivi Controller/DAO Global ma con regole di dominio nelle pagine funzionali corrispondenti (Accounting/Property/ServiceDesk).

Open questions

  • Da quale evento dell'albero (Objects_Tree) vengono valorizzati _idCat/_idfather per la creazione (set via ControlState, MObjectDetails.ascx.vb:114-115) — vedi FLOW-GLB-001.
  • Quali WORKING_STATUS sono considerati "di dismissione" e attivano MNT_ARCHIVEWORKORDER_BY_OIWS (BR-GLB-010).
  • Conferma del difetto storia-stato (BR-GLB-009): comportamento runtime ri-applicando lo stesso stato (più righe ENDDATE IS NULL?).
  • Conferma del rischio codice concorrente (BR-GLB-007) su import massivi paralleli.
  • Mappatura esatta di PERMISSION=2 sull'enum TenancyPermission (BR-GLB-013, cfr. BR-PROP-009).
  • Ruolo del trigger GLOBAL_OBJECTS_DELETE (BEFORE UPDATE) nel ciclo di vita (BR-GLB-022).
  • Comportamento in configurazioni PROD (osservato solo su ambiente TEST38).