FLOW-PROP-001 — Creazione/aggiornamento di un immobile (Edificio) — Patrimonio
Flusso end-to-end di salvataggio di un edificio/immobile (create-or-update) del modulo Property / PropertyCenter. Path pilota: scheda
BuildingDetails.ascx(editing interattivo da parte di un operatore Patrimonio). Backend OracleINFOCAD_TEST38, accesso via stored procedure ODP.NET genericaPRP_MANAGEENTITY(nessun ORM). L'entità è persistita nella tabellaEDIFICI_.
- Flow ID: FLOW-PROP-001
- Repository: InfocadWeb (ASP.NET Web Forms, VB.NET, PropertyCenterWeb + CommonWeb/PropertyCenter) + InfocadServer (PropertyControllers/PropertyManager, VB.NET)
- Confidence complessiva: VERIFIED (catena UI → code-behind → controller → DAO → stored procedure, confermata su codice E su sorgente DB) · SUPPORTED (mappa
PRP_MANAGEENTITY→ tabelle secondarie)
Obiettivo di business
Consentire a un operatore del Patrimonio (o a un'integrazione programmatica) di creare un nuovo
edificio/immobile o aggiornarne i dati anagrafici (nome, descrizione, comprensorio di appartenenza,
campi custom della scheda) all'interno della gerarchia spaziale Regione → Città → Comprensorio →
Edificio → Piano → Vano. Al termine l'edificio esiste (o è aggiornato) nella tabella EDIFICI_,
con codice generato, binding di tenant assegnato e cache in-memory rinfrescata.
Attori
- Operatore Patrimonio (utente autenticato con grant
ADD_DELETE/EDITsull'entitàEDIFICI) — path interattivo schedaBuildingDetails.ascx. - Operatore su griglia multi-selezione — editing massivo via
BuildingData.ascx→EditManager.EditItems. - Sistemi esterni / integrazioni — WCF
EditingWCF.svc(AddItem/EditItem/EditItems) e i moduli di importInfocadIntegration(SAP BS/BDI/IREN/TrentinoSvil), che chiamano lo stessoBuildingController.SaveBuilding. - Oracle
INFOCAD_TEST38(hub anagrafico patrimonio; procedura genericaPRP_MANAGEENTITY), con letture cross-schema suASPNET_TEST38(risoluzione utente/tenant).
Trigger
Click sul pulsante Salva della toolbar della scheda edificio: evento
RadToolBar1.ButtonClick gestito da RadToolBar1_ButtonClick in BuildingDetails.ascx.vb:622,
che invoca l'override Save(...) (:682).
Frontend entry point
- Shell:
PropertyCenterWeb/Default.aspx(modello shell-page CASSANDRA con UserControl caricati dinamicamente). - Control scheda:
PropertyCenterWeb/UserControls/Building/BuildingDetails.ascx(+BuildingDataSheet.ascxper il rendering campi/sezioni). - Control griglia (path alternativo):
PropertyCenterWeb/UserControls/Navigation/BuildingData.ascx(editing multiplo →ExportAnalysisToolbar/EditManager.EditItems). - Gate di accesso: grant per entità verificato lato UI (tenancy) e, sul path WCF, da
HasUserGrant(ADD_DELETE|EDIT, EntityName)inEditingWCF.svc.vb.
Sequenza principale (main sequence)
- Operatore apre la scheda edificio (nuovo o esistente), compila i campi e preme Salva.
BuildingDetails.ascx.vb→RadToolBar1_ButtonClick(:622) →Save()(:682) impostaBuildingDataSheet1.ID_Parente chiamaSaveObj(Me.BuildingDataSheet1, changedDate)(:704).EntityBase.SaveObj(EntityBase.vb:760) raccoglie i campi editati della scheda (GetEditedFields,GetEditedCustomFields,GetEditedBinaries), determina l'entità (Building) e per ogni id chiama il controller:BuildingController.SaveBuilding(UserName, ide, allFieldValues)(EntityBase.vb:1067).ide = 0⇒ creazione;ide > 0⇒ aggiornamento.
BuildingController.SaveBuilding(BuildingController.vb:13) inoltra al DAO:DirectCast(DAO, IDAO_Building).SaveBuilding(...)(:15).DAO_Building.SaveBuilding(DAO_Building.vb:465) aggiunge la colonna di auditID_CHANGED_BY = usernamese assente (:469-479) e chiamaDAO_GenericEntity.ManageEntity(username, BuildingID, "ID_EDIFICIO", "EDIFICI", "", "", KeyValue)(:483).DAO_GenericEntity.ManageEntity(overload Integer:298→ overload String:304):- calcola
entityTableName = "EDIFICI" & "_"=EDIFICI_(:315); - serializza
KeyValuein XML (PXMLSERIALIZEDDATA) (:332); - prepara i parametri (
PID_INFOCAD=BuildingID,PCOL_IDINFOCAD="ID_EDIFICIO",PTAB_INFOCAD="EDIFICI_",PREQUIREREFRESH,PRETVALUEOUT CLOB) e invocaPRP_MANAGEENTITY(:346).
- calcola
- Oracle
PRP_MANAGEENTITY(INFOCAD_TEST38, procedura standalone):- risolve il tenant dell'utente da
GLOBAL_LOGIN_SETTINGS+ASPNET_TEST38.ORA_ASPNET_USERS/APPLICATIONS(src:48-56); - decide
ISINSERT= TRUE sePID_INFOCADè NULL o= 0(src:59-73); - verifica esistenza tabella/colonna via
USER_TABLES/USER_TAB_COLUMNS(src:77-95); - INSERT: genera il codice con
COD_GENERATECODE(PTAB_INFOCAD, TENANCYID, V_CODEVALUE)(src:573), costruisceINSERT INTO EDIFICI_ (...) VALUES (...) RETURNING ID_EDIFICIO INTO :1ed esegue viaEXECUTE IMMEDIATE(src:338,418,621), restituendo il nuovo id inPRETVALUE; - UPDATE: costruisce
UPDATE EDIFICI_ SET ... WHERE "ID_EDIFICIO" = ...ed esegue (src:169,835),PRETVALUE = '0'; - scrive/aggiorna il binding di tenant su
GLOBAL_TENANCY_BINDINGS(src:639-721insert /src:731-821update); - imposta e ripristina le NLS di sessione (data/decimali) attorno alla DML dinamica (
src:545,551,840,841).
- risolve il tenant dell'utente da
- Il DAO legge
PRETVALUE(OracleClob) come stringa di ritorno (DAO_GenericEntity.vb:348) e la propaga. EntityBase.SaveObj/EditManager.EditItemsinterpreta il ritorno: numerico ⇒ successo (id nuovo se>0, update se0); non numerico ⇒ messaggio d'errore DB.- Post-salvataggio: refresh scheda (
Reload), eventi observableRaiseObjectCreated/Updated, refresh cache (parametroPREQUIREREFRESH).
API / service
- Path interattivo: nessun servizio SOAP/WCF — postback ASP.NET Web Forms diretto sulla scheda.
- Path programmatico equivalente (stesso
EditManager→ stesso controller → stessa stored procedure): WCFEditingWCF.svc(CommonWeb/PropertyCenter/WebServices/EditingWCF.svc.vb, contrattoIEditingWCF):AddItem(loginkey, EntityID, EntityName, ParentID, AllFieldValues)(:272) — grantADD_DELETE;EditItem(...)(:298) /EditItems(...)(:324) — grantEDIT;- varianti
*Mobileper il MobileCenter.
Backend
- UI/Code-behind:
BuildingDetails.ascx.vb(scheda),BuildingData.ascx.vb(griglia). - Classe base editing:
Descor.CommonWeb.PropertyCenter.EntityBase(EntityBase.vb) —SaveObj,ManageEditing. - Orchestratore editing:
EditManager(CommonWeb/PropertyCenter/Classes/Editing/EditManager.vb) —AddItem/EditItem/EditItems. - Controller server:
Descor.Property.PropertyControllers.BuildingController(InfocadServer/PropertyControllers/BuildingController.vb), inoltra al DAO viaCtrl_EntityBase.DAO. - DAO:
PropertyManager.DAO.DAO_Building(InfocadServer/PropertyManager/DAO/DAO_Building.vb) ePropertyManager.DAO.DAO_GenericEntity(.../DAO_GenericEntity.vb). - Pattern DAL:
Using dl As New DataLayer(My.Settings.ConnectionName)→dl.Parameters.Add(OracleParameter...)→dl.ExecuteNonQueryStoredProcedure("PRP_MANAGEENTITY", False); identity/ritorno via parametro OUTPRETVALUE(CLOB). - Nota: nel web app i controller Property sono richiamati in-process (referenza a DLL
Descor.Property.PropertyControllers/PropertySharedObjects), non tramite WCF.
Query / stored procedure
| Passo | Stored procedure (Oracle) | Chiamata da (DAL) |
|---|---|---|
Upsert edificio (INSERT/UPDATE dinamico su EDIFICI_) |
INFOCAD_TEST38.PRP_MANAGEENTITY (OUT PRETVALUE CLOB) |
DAO_GenericEntity.vb:346 |
Generazione codice entità (in PRP_MANAGEENTITY) |
INFOCAD_TEST38.COD_GENERATECODE |
PRP_MANAGEENTITY src:319,393,573 |
| Verifica esistenza edificio per nome (Insert via DTO) | INFOCAD_TEST38.PRP_GETBUILDINGIDBYNAME |
DAO_Building.vb:947 |
| Load scheda edificio (READ-ONLY) | INFOCAD_TEST38.PRP_GETBUILDINGS / PRP_GETBUILDINGSNAV |
DAO_Building.vb:749,121 |
| Cancellazione logica (path alternativo) | INFOCAD_TEST38.PRP_LOGICALDELETEBUILDING |
DAO_Building.vb:358 |
Tabelle lette (read)
INFOCAD_TEST38:GLOBAL_LOGIN_SETTINGS,GLOBAL_CODE_CONFIGURATION(viaCOD_GENERATECODE), cataloghiUSER_TABLES/USER_TAB_COLUMNS/USER_VIEWS(verifica dinamica tabella/colonne).ASPNET_TEST38:ORA_ASPNET_USERS,ORA_ASPNET_APPLICATIONS(risoluzione tenant dell'utente).
(da USER_DEPENDENCIES di PRP_MANAGEENTITY e COD_GENERATECODE; la tabella target EDIFICI_ è
accessa tramite SQL dinamico e non compare nelle dipendenze statiche — verificata direttamente sul sorgente e sul catalogo colonne)
Tabelle modificate (write)
INFOCAD_TEST38:EDIFICI_(RW — tabella-radice dell'edificio: INSERT conRETURNING ID_EDIFICIO, oppure UPDATEWHERE ID_EDIFICIO = :id; VERIFIED su sorgente e suUSER_TAB_COLUMNS),GLOBAL_TENANCY_BINDINGS(RW — binding tenant dell'entità, INSERT su create / UPDATE su edit).- Effetto indiretto:
COD_GENERATECODEproduce il valore di codice iniettato nell'INSERT (uso diGLOBAL_CODE_CONFIGURATION+DBMS_RANDOM); eventuale aggiornamento contatore non ispezionato in dettaglio.
Validazione
- Lato UI (path griglia
EditManager.EditItems): controllo tenancyTenantController.GetPermissionByEntity(...)— se permessoNONE/VIEW→ blocco con alertNoGrant(EditManager.vb:278-288); poiValidateOnSave(AllFieldValues)(:292). - Lato UI (path scheda
SaveObj): lock ottimistico viaEntityBase.ManageEditing(IsLocked/LockedBy/Lock,EntityBase.vb:669+); su tenant non super-admin, check permessi entità. - Lato WCF:
ValidateLoginKey(loginkey)+HasUserGrant(ADD_DELETE|EDIT, EntityName)(EditingWCF.svc.vb:272,298,324). - Lato DB:
PRP_MANAGEENTITYverifica esistenza tabella e colonna id; in caso negativo NON esegue DML e restituisce stringa d'errore inPRETVALUE(es.LA TABELLA "..." NON ESISTE,LA COLONNA "..." NON ESISTE,src:172,343,427,528).
Side effects
- Assegnazione codice entità (
COD_GENERATECODE) e stamping auditID_CHANGED_BY(DAO) /ID_CREATED_BY = "WEB SERVICE"(EditManager.AddItem,EditManager.vb:200,208). - Creazione/aggiornamento binding di tenant su
GLOBAL_TENANCY_BINDINGS. - Refresh cache/strutture (
PREQUIREREFRESH) ed eventi observableRaiseObjectCreated/Updated(DAO_Building.vb:437,449). - Alterazione temporanea delle NLS di sessione (formato data/decimali) attorno alla DML dinamica,
con ripristino finale (
src:545,551,840,841).
Percorsi alternativi / di errore
- Griglia multi-edit:
BuildingData.ascx→EditManager.EditItems(IDArray, EntityName, AllFieldValues)(EditManager.vb:229) → dispatch aBuildingController.SaveBuilding(:309) → stessaPRP_MANAGEENTITY. - WCF programmatico:
EditingWCF.AddItem/EditItem/EditItems→EditManager.AddItem/EditItem/EditItems. - Integrazioni import (SAP/altri):
InfocadIntegration/.../EntityLayer*/*.cs|vbchiamano direttamentectrlBuilding.SaveBuilding(user, 0|id, entityFields)(create/update) — stessa stored procedure. - Insert via DTO tipizzato:
DAO_Building.Insert(:219) risolve/valida il nome (GetBuildingIdByName) e mappaID_COMPRENSORIO/EDIFICIO/DESCRIZIONEprima di chiamareSaveBuilding. - Errore validazione/DB → ritorno non numerico:
EditManager.EditItemslogga conInfocadTraceWrapper.CreateError, imposta_errMsg/DB_ERROR_MESSAGE(EditManager.vb:355-361), nessuna riga persistita (la DML dinamica non viene eseguita se i check falliscono). - Cancellazione logica (controprova non distruttiva sul dominio):
PRP_LOGICALDELETEBUILDING(DAO_Building.vb:358).
Diagramma di sequenza
sequenceDiagram
actor Op as Operatore Patrimonio
participant UI as BuildingDetails.ascx (Default.aspx shell)
participant CB as BuildingDetails.ascx.vb (Save)
participant EB as EntityBase.SaveObj
participant BC as BuildingController.SaveBuilding
participant DAOB as DAO_Building.SaveBuilding
participant DAOG as DAO_GenericEntity.ManageEntity
participant DB as Oracle INFOCAD_TEST38 (PRP_MANAGEENTITY)
Op->>UI: compila scheda + click Salva
UI->>CB: RadToolBar1_ButtonClick -> Save()
CB->>EB: SaveObj(BuildingDataSheet1)
EB->>EB: GetEditedFields / GetEditedCustomFields
EB->>BC: SaveBuilding(user, ide, allFieldValues)
BC->>DAOB: SaveBuilding(...)
DAOB->>DAOB: aggiunge ID_CHANGED_BY = user
DAOB->>DAOG: ManageEntity(user, id, "ID_EDIFICIO", "EDIFICI", KeyValue)
DAOG->>DAOG: entityTableName = "EDIFICI_"; XML-serialize KeyValue
DAOG->>DB: PRP_MANAGEENTITY (PID, PTAB="EDIFICI_", XML, OUT PRETVALUE)
alt PID = 0 / NULL (create)
DB->>DB: COD_GENERATECODE -> INSERT INTO EDIFICI_ RETURNING ID_EDIFICIO
DB->>DB: INSERT GLOBAL_TENANCY_BINDINGS
else PID > 0 (update)
DB->>DB: UPDATE EDIFICI_ SET ... WHERE ID_EDIFICIO = :id
DB->>DB: UPDATE GLOBAL_TENANCY_BINDINGS
end
DB-->>DAOG: PRETVALUE (id nuovo | '0' | errore)
DAOG-->>EB: retVal
EB-->>CB: Result.SUCCESS/FAILURE
CB-->>Op: Reload scheda (edificio salvato)
Matrice di tracciabilità
| Layer | Artefatto | Evidenza (file:riga) |
|---|---|---|
| UI | BuildingDetails.ascx (scheda edificio) |
PropertyCenterWeb/UserControls/Building/BuildingDetails.ascx:1 |
| Code-behind | RadToolBar1_ButtonClick → Save() → SaveObj |
BuildingDetails.ascx.vb:622,682,704 |
| Base editing | EntityBase.SaveObj → SaveBuilding |
EntityBase.vb:760,1067 |
| Orchestratore | EditManager.AddItem/EditItem/EditItems |
EditManager.vb:172,220,229,309 |
| WCF (programmatico) | EditingWCF.AddItem/EditItem/EditItems |
EditingWCF.svc.vb:272,298,324 |
| Controller | BuildingController.SaveBuilding → DAO |
BuildingController.vb:13,15 |
| DAO | DAO_Building.SaveBuilding → ManageEntity |
DAO_Building.vb:465,483 |
| DAO generico | ManageEntity (overload) → PRP_MANAGEENTITY |
DAO_GenericEntity.vb:298,304,315,332,346 |
| DB | PRP_MANAGEENTITY (upsert dinamico su EDIFICI_) |
USER_SOURCE PRP_MANAGEENTITY src:1-9,59-73,338,418,621,835 |
| DB | INSERT/UPDATE GLOBAL_TENANCY_BINDINGS |
src:639-721,731-821 |
| DB | COD_GENERATECODE (codice entità) |
src:319,393,573 ; deps → GLOBAL_CODE_CONFIGURATION |
| DB (schema) | EDIFICI_ colonne ID_EDIFICIO(NUMBER), EDIFICIO, DESCRIZIONE, ID_COMPRENSORIO, ID_CHANGED_BY |
USER_TAB_COLUMNS |
Reverse trace (dal DB/controller ai chiamanti)
BuildingController.SaveBuildingè condiviso da: scheda interattiva (EntityBase.SaveObj,EntityBase.vb:1067), editing su griglia (EditManager.EditItems,EditManager.vb:309) e dalle integrazioni di import (InfocadIntegration/.../EntityLayerBS|BDI|IREN|TrentinoSvil/*— es.BuildingsResponse.cs:541,547,RESPONSE_BU.cs:390,FuncLocationResponse.cs:280).PRP_MANAGEENTITYè l'upsert generico di TUTTE le entità spaziali/anagrafiche Property: raggiunto daDAO_GenericEntity.ManageEntity, a sua volta invocato daSaveBuilding(EDIFICI_),SaveRoom(VANI_),SaveFloor,SaveCity,SaveDistrict,SaveRegion,SaveTerrain,SaveUI,SaveAsset,SavePeople,SaveOC, e dai tipi di categoria asset (CategoryType.ascx.vb:233,325).- Forward e reverse concordano (la scheda edificio →
SaveBuilding→PRP_MANAGEENTITYconPTAB_INFOCAD='EDIFICI_',PCOL_IDINFOCAD='ID_EDIFICIO') → NO CONFLICTING.
Regole correlate (related rules)
- Convenzione nomi tabella: le entità spaziali usano il suffisso
_(es.EDIFICI→EDIFICI_); eccezioniBLOCKS_DATA/TIPO_(DAO_GenericEntity.vb:315). - Caso speciale
VANI/VANI_(locali): identity0viene forzata a-1(DAO_GenericEntity.vb:322-326). - Semantica create vs update pilotata da
PID_INFOCAD(0/NULL ⇒ INSERT;>0⇒ UPDATE) —PRP_MANAGEENTITY src:59-73. - Multi-tenant: il tenant è dedotto dall'utente (
GLOBAL_LOGIN_SETTINGS+ ASPNET) e propagato aGLOBAL_TENANCY_BINDINGS; grant per entità (ADD_DELETE/EDIT/VIEW) applicati prima della scrittura.
Evidenza (file:line)
Punti chiave (codice):
BuildingDetails.ascx.vb:622,682,704; EntityBase.vb:760,1067; EditManager.vb:172,212,220,229,309;
EditingWCF.svc.vb:272,278,298,304,324,330; BuildingController.vb:13,15;
DAO_Building.vb:465,469-479,483; DAO_GenericEntity.vb:298,300,304,315,332,346,348.
Punti chiave (DB, INFOCAD_TEST38, letti in sola lettura via USER_SOURCE/USER_DEPENDENCIES/USER_TAB_COLUMNS):
PRP_MANAGEENTITY firma src:1-9; decisione INSERT/UPDATE src:59-73; check tabella/colonna src:77-95;
UPDATE dinamico src:169,835; INSERT dinamico + RETURNING ID_EDIFICIO src:338,418,621;
COD_GENERATECODE src:319,393,573; GLOBAL_TENANCY_BINDINGS INSERT src:639-721 / UPDATE src:731-821;
risoluzione tenant src:48-56. Catalogo: PRP_MANAGEENTITY è PROCEDURE standalone di INFOCAD_TEST38
(database-catalog.json → user_procedures[6701]); EDIFICI_ è TABLE con colonna ID_EDIFICIO NUMBER.
Confidence
- VERIFIED: catena UI → code-behind → base editing → controller → DAO → nome stored procedure
(letta sui sorgenti) e sorgente della stored procedure
PRP_MANAGEENTITY(INSERT/UPDATE dinamico suEDIFICI_,RETURNING ID_EDIFICIO) letto daUSER_SOURCE; esistenza tabella/colonne da catalogo. - SUPPORTED: scritture su
GLOBAL_TENANCY_BINDINGSe generazione codice (COD_GENERATECODE→GLOBAL_CODE_CONFIGURATION) — confermate da sorgente/dipendenze ma senza esecuzione runtime. - INFERRED: eventuale aggiornamento di contatori interni in
COD_GENERATECODE(non ispezionato riga per riga).
Open questions
- Dettaglio delle colonne effettivamente scritte in
EDIFICI_a runtime (dipende dai campi della scheda serializzati inPXMLSERIALIZEDDATA, quindi variabile per configurazione/tenant). - Comportamento del ramo "interfaccia" di
PRP_MANAGEENTITY(parametriPID_INTERFACE/PCOL_INTERFACE, tabellaALL_CATEGORIES) non usato dal path edificio ma presente nella procedura (src:184-343,442-532). - Se
COD_GENERATECODEaggiorni (UPDATE)GLOBAL_CODE_CONFIGURATIONoltre a leggerla. - Mapping IIS della virtual directory
PropertyCenterWebsotto la shell CASSANDRA (runtime). - Comportamento in configurazioni PROD (osservato solo ambiente TEST38).