Table of Contents

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 Oracle INFOCAD_TEST38, accesso via stored procedure ODP.NET generica PRP_MANAGEENTITY (nessun ORM). L'entità è persistita nella tabella EDIFICI_.

  • 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/EDIT sull'entità EDIFICI) — path interattivo scheda BuildingDetails.ascx.
  • Operatore su griglia multi-selezione — editing massivo via BuildingData.ascxEditManager.EditItems.
  • Sistemi esterni / integrazioni — WCF EditingWCF.svc (AddItem/EditItem/EditItems) e i moduli di import InfocadIntegration (SAP BS/BDI/IREN/TrentinoSvil), che chiamano lo stesso BuildingController.SaveBuilding.
  • Oracle INFOCAD_TEST38 (hub anagrafico patrimonio; procedura generica PRP_MANAGEENTITY), con letture cross-schema su ASPNET_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.ascx per 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) in EditingWCF.svc.vb.

Sequenza principale (main sequence)

  1. Operatore apre la scheda edificio (nuovo o esistente), compila i campi e preme Salva.
  2. BuildingDetails.ascx.vbRadToolBar1_ButtonClick (:622) → Save() (:682) imposta BuildingDataSheet1.ID_Parent e chiama SaveObj(Me.BuildingDataSheet1, changedDate) (:704).
  3. 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 = 0creazione; ide > 0aggiornamento.
  4. BuildingController.SaveBuilding (BuildingController.vb:13) inoltra al DAO: DirectCast(DAO, IDAO_Building).SaveBuilding(...) (:15).
  5. DAO_Building.SaveBuilding (DAO_Building.vb:465) aggiunge la colonna di audit ID_CHANGED_BY = username se assente (:469-479) e chiama DAO_GenericEntity.ManageEntity(username, BuildingID, "ID_EDIFICIO", "EDIFICI", "", "", KeyValue) (:483).
  6. DAO_GenericEntity.ManageEntity (overload Integer :298 → overload String :304):
    • calcola entityTableName = "EDIFICI" & "_" = EDIFICI_ (:315);
    • serializza KeyValue in XML (PXMLSERIALIZEDDATA) (:332);
    • prepara i parametri (PID_INFOCAD=BuildingID, PCOL_IDINFOCAD="ID_EDIFICIO", PTAB_INFOCAD="EDIFICI_", PREQUIREREFRESH, PRETVALUE OUT CLOB) e invoca PRP_MANAGEENTITY (:346).
  7. 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 se PID_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), costruisce INSERT INTO EDIFICI_ (...) VALUES (...) RETURNING ID_EDIFICIO INTO :1 ed esegue via EXECUTE IMMEDIATE (src:338,418,621), restituendo il nuovo id in PRETVALUE;
    • 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-721 insert / src:731-821 update);
    • imposta e ripristina le NLS di sessione (data/decimali) attorno alla DML dinamica (src:545,551,840,841).
  8. Il DAO legge PRETVALUE (OracleClob) come stringa di ritorno (DAO_GenericEntity.vb:348) e la propaga.
  9. EntityBase.SaveObj/EditManager.EditItems interpreta il ritorno: numerico ⇒ successo (id nuovo se >0, update se 0); non numerico ⇒ messaggio d'errore DB.
  10. Post-salvataggio: refresh scheda (Reload), eventi observable RaiseObjectCreated/Updated, refresh cache (parametro PREQUIREREFRESH).

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): WCF EditingWCF.svc (CommonWeb/PropertyCenter/WebServices/EditingWCF.svc.vb, contratto IEditingWCF):
    • AddItem(loginkey, EntityID, EntityName, ParentID, AllFieldValues) (:272) — grant ADD_DELETE;
    • EditItem(...) (:298) / EditItems(...) (:324) — grant EDIT;
    • varianti *Mobile per 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 via Ctrl_EntityBase.DAO.
  • DAO: PropertyManager.DAO.DAO_Building (InfocadServer/PropertyManager/DAO/DAO_Building.vb) e PropertyManager.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 OUT PRETVALUE (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 (via COD_GENERATECODE), cataloghi USER_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 con RETURNING ID_EDIFICIO, oppure UPDATE WHERE ID_EDIFICIO = :id; VERIFIED su sorgente e su USER_TAB_COLUMNS), GLOBAL_TENANCY_BINDINGS (RW — binding tenant dell'entità, INSERT su create / UPDATE su edit).
  • Effetto indiretto: COD_GENERATECODE produce il valore di codice iniettato nell'INSERT (uso di GLOBAL_CODE_CONFIGURATION + DBMS_RANDOM); eventuale aggiornamento contatore non ispezionato in dettaglio.

Validazione

  • Lato UI (path griglia EditManager.EditItems): controllo tenancy TenantController.GetPermissionByEntity(...) — se permesso NONE/VIEW → blocco con alert NoGrant (EditManager.vb:278-288); poi ValidateOnSave(AllFieldValues) (:292).
  • Lato UI (path scheda SaveObj): lock ottimistico via EntityBase.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_MANAGEENTITY verifica esistenza tabella e colonna id; in caso negativo NON esegue DML e restituisce stringa d'errore in PRETVALUE (es. LA TABELLA "..." NON ESISTE, LA COLONNA "..." NON ESISTE, src:172,343,427,528).

Side effects

  • Assegnazione codice entità (COD_GENERATECODE) e stamping audit ID_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 observable RaiseObjectCreated/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.ascxEditManager.EditItems(IDArray, EntityName, AllFieldValues) (EditManager.vb:229) → dispatch a BuildingController.SaveBuilding (:309) → stessa PRP_MANAGEENTITY.
  • WCF programmatico: EditingWCF.AddItem/EditItem/EditItemsEditManager.AddItem/EditItem/EditItems.
  • Integrazioni import (SAP/altri): InfocadIntegration/.../EntityLayer*/*.cs|vb chiamano direttamente ctrlBuilding.SaveBuilding(user, 0|id, entityFields) (create/update) — stessa stored procedure.
  • Insert via DTO tipizzato: DAO_Building.Insert (:219) risolve/valida il nome (GetBuildingIdByName) e mappa ID_COMPRENSORIO/EDIFICIO/DESCRIZIONE prima di chiamare SaveBuilding.
  • Errore validazione/DB → ritorno non numerico: EditManager.EditItems logga con InfocadTraceWrapper.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_ButtonClickSave()SaveObj BuildingDetails.ascx.vb:622,682,704
Base editing EntityBase.SaveObjSaveBuilding 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.SaveBuildingDAO BuildingController.vb:13,15
DAO DAO_Building.SaveBuildingManageEntity 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 da DAO_GenericEntity.ManageEntity, a sua volta invocato da SaveBuilding (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 → SaveBuildingPRP_MANAGEENTITY con PTAB_INFOCAD='EDIFICI_', PCOL_IDINFOCAD='ID_EDIFICIO') → NO CONFLICTING.
  • Convenzione nomi tabella: le entità spaziali usano il suffisso _ (es. EDIFICIEDIFICI_); eccezioni BLOCKS_DATA/TIPO_ (DAO_GenericEntity.vb:315).
  • Caso speciale VANI/VANI_ (locali): identity 0 viene 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 a GLOBAL_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.jsonuser_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 su EDIFICI_, RETURNING ID_EDIFICIO) letto da USER_SOURCE; esistenza tabella/colonne da catalogo.
  • SUPPORTED: scritture su GLOBAL_TENANCY_BINDINGS e generazione codice (COD_GENERATECODEGLOBAL_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 in PXMLSERIALIZEDDATA, quindi variabile per configurazione/tenant).
  • Comportamento del ramo "interfaccia" di PRP_MANAGEENTITY (parametri PID_INTERFACE/PCOL_INTERFACE, tabella ALL_CATEGORIES) non usato dal path edificio ma presente nella procedura (src:184-343,442-532).
  • Se COD_GENERATECODE aggiorni (UPDATE) GLOBAL_CODE_CONFIGURATION oltre a leggerla.
  • Mapping IIS della virtual directory PropertyCenterWeb sotto la shell CASSANDRA (runtime).
  • Comportamento in configurazioni PROD (osservato solo ambiente TEST38).