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/GlobalControllersin InfocadServer,GlobalWebin InfocadWeb). Il dominio Global è in larga parte infrastruttura trasversale (tenancy, generazione codici, identità/login, spooler, metamodello griglie, audit/debug): molte tabelleGLOBAL_*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 tabelleGLOBAL_*si rimanda al dominio proprietario (vedi Cross-reference).Sistema: Infocad (Descor) — backend Oracle
INFOCAD_TEST38, ASP.NET Web Forms (VB.NET) come UI (schedaMObjectDetails.ascx, shell CASSANDRA), servizio Windows InfocadServer raggiunto via .NET Remoting (proxy well-knownDAO_Objects.rem); nessun ORM (accesso via stored procedure ODP.NETExecute*StoredProcedure). Nel dominio Global il Controller inoltra direttamente al DAO remoto, senza un layer<Domain>BLintermedio.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). Esclusibin/ obj/ packages/. - Evidenza DB: letta in sola lettura da
USER_SOURCE(sorgente procedure),USER_TRIGGERS(corpo trigger),USER_OBJECTS(tipo/validità) e dal catalogodocs/_generated/db-detail.json(colonne/PK/unique/FK, generato daUSER_*) suINFOCAD_TEST38, limitatamente agli oggetti toccati dalle regole (RPS_ADDMOBJECT,RPS_SETMOBJECTTENANTS,RPS_UPDATEMOBJECTSSTATUS,RPS_GETMOBJECTFATHER,COD_GENERATECODE, trigger diGLOBAL_OBJECTS, tabelleGLOBAL_OBJECTS/GLOBAL_TENANCY_OBJECTS). Nessuna lettura di dati applicativi, nessuna DML/DDL. Le righe delle procedure sono indicate comeNOME_PROC:NNN(numerazioneUSER_SOURCE). - I sorgenti di
RPS_ADDMOBJECT/RPS_SETMOBJECTTENANTS/RPS_UPDATEMOBJECTSSTATUSe i corpi dei tre triggerINSERTdiGLOBAL_OBJECTSsono 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 proceduraPRP_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 = 0⇒ctrl.Insert(...)(nuovo oggetto,RPS_ADDMOBJECT);ID_DTO > 0⇒ctrl.Update(...)(RPS_UPDATEMOBJECT), con eventuale cambio stato seWorkingStatusè 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):
Insert→RPS_ADDMOBJECT(DAO_MObject.vb:115);Update→RPS_UPDATEMOBJECT(DAO_MObject.vb:244);SetMObjectStatus→RPS_UPDATEMOBJECTSSTATUS(DAO_MObject.vb:305).
BR-GLB-002 — Categoria obbligatoria (guardia DAO)
- Regola: nessun MObject può essere creato senza categoria valida: se
ID_Category ≤ 0il DAO sollevaDAOException("Object Category can't be null")prima di aprire ilDataLayer. - 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_C00309651ID_CATEGORY → GLOBAL_CATEGORIES.ID_C(catalogoGLOBAL_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 o0, la procedura lo forza a1prima 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 colonnaWORKING_STATUSèNOT NULL(catalogoGLOBAL_OBJECTS).
BR-GLB-004 — Risoluzione del padre dalla categoria
- Regola: se il padre gerarchico non è indicato (
PIDFATHERNULL o0), la procedura invocaRPS_GETMOBJECTFATHER(PIDCATEGORY, TMPIDFATHER)per ricavare il nodo-padre di categoria; il valore risolto è usato comeID_FATHERdell'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 contmpIDFather—RPS_ADDMOBJECT:70-73. - Nota:
ID_FATHERèNOT NULLinGLOBAL_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
≤ 0viene normalizzato a NULL (ID_POS_C/E/P/Vrestano nullabili); solo i valori> 0sono 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; defaulttmpIDxxx := NULL—RPS_ADDMOBJECT:20-23.
BR-GLB-006 — Identità ID_OBJ da sequence e immutabilità
- Regola: alla INSERT, se
ID_OBJè NULL il triggerCNT_GLOBAL_OBJECTSlo valorizza daSEQ_GLOBAL_OBJECTS.NEXTVAL; in UPDATE un tentativo di cambiareID_OBJsollevaORA-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 ')— triggerCNT_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 triggerADD_GLOBAL_OBJECT_DATA(ridondanza, vedi BR-GLB-008). PatternSEQ_*+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 triggerGLOBAL_OBJECTS_INSERTgenera un codice nella formaTRIM(SUBSTR(PREFIX,0,3)) || '-' || (NEWCODE+1) || '-' || (IDENTIFIER+1), dovePREFIX=GLOBAL_CATEGORIES.ID_LINK(fallbackGLOBAL_CATEGORIES.NAMEse il link è vuoto),NEWCODE=COUNT(ID_OBJ)+1degli 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): perGLOBAL_OBJECTSla 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)eMAX(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+1conNEWCODE = COUNT+1, cioèCOUNT+2(doppio incremento).
- Meccanismo distinto da quello generico
BR-GLB-008 — Riga gemella in GLOBAL_OBJECTS_DATA
- Regola: ad ogni INSERT su
GLOBAL_OBJECTSil trigger AFTER INSERTADD_GLOBAL_OBJECT_DATAcrea la riga gemellaINSERT 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_OBJin UPDATE conraise_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) aggiornaGLOBAL_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 inGLOBAL_OM_WS_HISTORYconSTARTDATE=SYSDATE,ENDDATE=NULL,NOTE,USERNAMEe categoria (OBJ_CATEGORY) dal joinGLOBAL_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 apertaUPDATE GLOBAL_OM_WS_HISTORY ws1 SET ENDDATE = TMPSYSDATE WHERE ws1.ENDDATE IS NULL AND ... AND ws1.WORKING_STATUS <> PSTATUS(:15-22); insert nuova rigaINSERT 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 creazioneRPS_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ù righeENDDATE IS NULLper lo stesso oggetto (SUPPORTED come rischio, dedotto dal sorgente).
- Magic literal
BR-GLB-010 — Archiviazione ODL su stato di dismissione
- Regola: al termine dell'aggiornamento stato,
RPS_UPDATEMOBJECTSSTATUSinvocaMNT_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_OIWSnon 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_STATUSattivino 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 valorizzaID_Tenancy = WS.CurrentTenant.ID_DTOinInstantiateDto, senza la guardiaIsTenantEnabledusata canonicamente altrove: l'effettiva enforcement ("binding solo con tenant valido") è quindi la guardia> 0del 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 disabilitataWS.CurrentTenant.ID_DTOè0e la guardia DAO> 0non scrive alcun binding. Da uniformare (rischio latente seCurrentTenantnon fosse0a tenancy disattiva).
BR-GLB-012 — Propagazione della tenancy agli antenati (GLOBAL_TENANCY_OBJECTS)
- Regola:
RPS_SETMOBJECTTENANTSnaviga 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)inGLOBAL_TENANCY_OBJECTSsolo se non già presente (COUNT(*) = 0), conCODICE = NAMEdell'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_OBJECTSha PK composita(ID_OBJ, ID_TENANCY)(catalogo) ⇒ la guardiaCOUNT = 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)conPERMISSION=2alla creazione. L'MObject fa eccezione: usa la tabella dedicataGLOBAL_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 conPERMISSION=2) e BR-PROP-029 (DELETE su cancellazione). TabellaGLOBAL_TENANCY_BINDINGSa catalogo (INFOCAD_TEST38). - Flag: magic number
PERMISSION=2(mappatura sull'enumTenancyPermissionda confermare, vedi BR-PROP-009). Due meccanismi paralleli di binding tenant (GLOBAL_TENANCY_BINDINGSper le entità spaziali/anagrafiche,GLOBAL_TENANCY_OBJECTSper 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_SETTINGSin join con lo schema ASPNET (ORA_ASPNET_USERS/ORA_ASPNET_APPLICATIONS), filtrando su username e suAPPLICATIONNAME = 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 usaUSER(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_GENERATECODEeGetCodeFieldByEntityTenant(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 inGLOBAL_CODE_CONFIGURATION. - Tipo: calculation · Enforcement: DB · Confidence: VERIFIED (wrapper VERIFIED qui; interno di
COD_GENERATECODEVERIFIED in BR-PROP-005) - Evidenza (DAO):
dl.ExecuteNonQueryStoredProcedure("COD_GENERATECODE")conV_TABLE_NAME/V_IDTENANT/PRETVALUE—InfocadServer/GlobalManager/DAO/DAO_GlobalCodeConfiguration.vb:32-51;COD_GETCODEFIELD—:60-83. InterfacciaIDAO_GlobalCode, controllerGlobalCodeController(remotingService_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_OBJECTSnon 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 grant1320oWS.SessionProfile.SuperAdmin(sia in modifica sia in nuovo); Elimina e Ripristina richiedono grant1330(Elimina ancheCanDeleteMObject). - 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) e1330(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
MObjectControllerimplementaIDAO_MObjectinoltrando 1:1 ogni metodo al DAO remoto (CType(Me.DAO, IDAO_MObject).Insert(...)), senza alcunHasUserGrant; il DAO server-side (DAO_MObject.Insert) non ri-verifica alcun grant. Inoltre il bulk-loader istanziaDAO_MObjectin-process e chiamaInsert. 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 perUpdate: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 warningMObjectAlreadyExistMessagee non scrive. Questo controllo è solo lato UI:GLOBAL_OBJECTSnon ha alcun vincolo unique suNAME(solo PK suID_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 lookupRPS_GETMOBJECTBYCODEANDTNT—DAO_MObject.vb:2272-2283. - Evidenza (DB):
GLOBAL_OBJECTS—uniques: [],pk: [ID_OBJ](catalogodb-detail.json);NAMEèNOT NULLma 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 ereditaCtrl_EntityBase/Ctrl_Basee, in costruzione, attiva un proxy .NET Remoting verso il DAO server-side;DAOFactory.CreateDAOrisolve il tipo di servizio e chiamaActivator.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-knownRegisterWellKnownServiceType(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 daDescor.Common(config di deploy).
BR-GLB-020 — Scrittura programmatica in-process con username "System"
- Regola: l'overload
Insert(dto)(path programmatico/import) delega aInsert("System", dto): le scritture non interattive sono attribuite allo username literal"System"(che finisce inGLOBAL_OM_WS_HISTORY.USERNAMEviaRPS_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-loaderdaoOM.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 conCOMMITintermedio. - 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). TabellaGLOBAL_DEBUG_LOGa catalogo (INFOCAD_TEST38). - Flag: logging senza feature-flag, con
COMMITintermedio (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), viaRPS_DELETEOBJECT(LogicalDelete)/LogicalDeleteMObjects; il ripristino è esposto comeRestore. In UI l'azione è gated daCanDeleteMObject(category, id)e dal grant1330; 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); controllerRestore(MObjectController.vb:514-516),CanDeleteMObject(:522-524); gate UI:399-401.GLOBAL_OBJECTShaISCANCELLED NUMBER NOT NULLe i triggerGLOBAL_OBJECTS_DELETE/DOCS_GLOBAL_OBJECTS_UPDsu 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/1330sono 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 gemellaGLOBAL_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 incrementoCOUNT+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-20000per il cambio diID_OBJ(BR-GLB-006/008); prefisso codice a 3 caratteriSUBSTR(PREFIX,0,3)(BR-GLB-007); grant1320/1330come stringhe UI (BR-GLB-016);PERMISSION = 2nel binding generico (BR-GLB-013); username"System"per l'import (BR-GLB-020).
Ridondanze / doppi meccanismi
- Guardia di immutabilità di
ID_OBJduplicata nei triggerCNT_GLOBAL_OBJECTSeADD_GLOBAL_OBJECT_DATA(BR-GLB-006/008). - Due meccanismi di generazione codice: trigger dedicato per
GLOBAL_OBJECTS(BR-GLB-007) vsCOD_GENERATECODE/GLOBAL_CODE_CONFIGURATIONgenerico (BR-GLB-015). - Due tabelle di binding tenant:
GLOBAL_TENANCY_OBJECTSper gli oggetti (BR-GLB-012) vsGLOBAL_TENANCY_BINDINGSper le altre entità (BR-GLB-013).
Feature flag / logging nascosto
- BR-GLB-021 —
GLOBAL_DEBUG_LOGscritto ad ogni upsert conCOMMITintermedio, 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 massivo → BR-BLK (BulkLoader).GLOBAL_SPOOLER— spooler di export (procedureRPS_*SPOOLER*inInfocadServer/ExportService) → BR-EXP (ExportService).GLOBAL_TAB_COLUMNS— metamodello griglie/colonne IEM (DAO_TabColumns) → BR-IEM.GLOBAL_TENANCY_BINDINGS,GLOBAL_CODE_CONFIGURATION,GLOBAL_LOGIN_SETTINGS,GLOBAL_DEBUG_LOG— scritte dall'upsert genericoPRP_MANAGEENTITYnel 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/_idfatherper la creazione (set via ControlState,MObjectDetails.ascx.vb:114-115) — vedi FLOW-GLB-001. - Quali
WORKING_STATUSsono considerati "di dismissione" e attivanoMNT_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=2sull'enumTenancyPermission(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).