Table of Contents

BR-BLK — Regole di business del dominio BulkLoader / Caricamento massivo

Regole di business estratte dal flusso pilota FLOW-BLK-001 (import massivo di anagrafiche PERSONE da file Excel) e dai sorgenti del dominio BulkLoader. Il dominio gestisce il caricamento massivo (import batch) di entità — Persone, Aziende, Contatti, Contratti, Conduzioni, Fornitori, Centri di costo, Asset, Spaziali, Categorie, Energy, Documenti — a partire da un foglio Excel caricato dall'operatore. Tutti i loader condividono lo stesso motore di processo (classe server BulkLoader) che traccia il ciclo di vita su GLOBAL_UPLOAD_PROCESS/GLOBAL_UPLOAD_ALERT tramite le procedure RPS_*, e dispatcha a un loader specializzato (BulkLoaderBase-derivato) per tipo-entità. La persistenza delle entità anagrafiche passa dalla stessa procedura generica di upsert PRP_MANAGEENTITY usata dal salvataggio interattivo (vedi BR-PROP).

Sistema: Infocad (Descor) — backend Oracle INFOCAD_TEST38, ASP.NET Web Forms (VB.NET) come UI (UploadProcessControl.ascx), servizio Windows InfocadServer raggiunto via .NET Remoting (BulkLoader.rem, contratto IBulkLoad); nessun ORM (accesso via stored procedure ODP.NET).

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/BulkLoaderManager/PeopleBulkLoader.vb:333).
  • Evidenza DB: le procedure di processo RPS_STARTUPLOADPROCESS / RPS_WRITEUPLOADALERT / RPS_COMPLETEUPLOADPROCESS sono state lette in sola lettura da USER_SOURCE durante la tracciatura di FLOW-BLK-001 (INSERT/UPDATE su GLOBAL_UPLOAD_PROCESS/GLOBAL_UPLOAD_ALERT confermati); esistenza tabelle/colonne da catalogo USER_OBJECTS/USER_TAB_COLUMNS. Nessuna lettura di dati applicativi, nessuna DML/DDL.
  • Il comportamento interno di PRP_MANAGEENTITY (upsert dinamico PERSONE_, binding tenant) è documentato VERIFIED in BR-PROP/FLOW-PROP-001 (stessa procedura): qui è ereditato come SUPPORTED via riuso + verifica colonna ID_N.
  • Il path pilota è l'entità Persone (IMP_PERSONE/UPD_PERSONE). Le regole del motore di processo (BR-BLK-001..007, 023) valgono per tutti i loader del dominio; le regole di dominio Persone (BR-BLK-008..022, 024..028) sono verificate sul PeopleBulkLoader e possono variare per gli altri loader specializzati.

Registro delle regole (register)

id regola tipo enforcement conf
BR-BLK-001 Ogni caricamento massivo apre un record processo su GLOBAL_UPLOAD_PROCESS con STATUS='RUNNING' e restituisce ID_PROCESS (RPS_STARTUPLOADPROCESS) state-transition mixed VERIFIED
BR-BLK-002 Il file caricato è persistito integralmente: come BLOB DOCUMENT_FILE nel processo e come copia temporanea su disco (bulkloadtmp, naming dedotto anti-collisione) side-effect mixed VERIFIED
BR-BLK-003 Chiusura processo con stato calcolato Done/DoneWithWarnings/NotDone, END_DATE=SYSDATE e contatori TOTALOBJS/VALIDOBJS/CREATEDOBJS (RPS_COMPLETEUPLOADPROCESS) state-transition mixed VERIFIED
BR-BLK-004 Ogni riga elaborata genera un alert su GLOBAL_UPLOAD_ALERT con esito ActionResults 0-4 (RPS_WRITEUPLOADALERT) state-transition mixed VERIFIED
BR-BLK-005 Il messaggio d'alert è troncato a 3999 caratteri prima dell'INSERT validation BL VERIFIED
BR-BLK-006 Retry: RetryFileUploadProcess rilegge il processo (RPS_RETRYUPLOADPROCESS) e rilancia Import solo se il file esiste ancora su disco state-transition mixed VERIFIED
BR-BLK-007 Motore unico con dispatch per tipo-entità verso un loader specializzato; tipo non riconosciuto ⇒ alert ...UNKNOWN/ENTITY UNKNOW (status NoAction) e processo comunque chiuso config-flag BL VERIFIED
BR-BLK-008 Insert vs Update Persone pilotato dal ramo UI (IMP_PERSONE⇒insert / UPD_PERSONE⇒update) e propagato a SavePeople (0⇒INSERT, ID_N>0⇒UPDATE) state-transition mixed VERIFIED
BR-BLK-009 Import Persone richiede le chiavi di deduplica (strPeopleKey non vuoto; in update accettata anche colonna ID_N): assenti ⇒ file rifiutato validation mixed VERIFIED
BR-BLK-010 Il file deve contenere colonna VANO/CODICE_VANO e le colonne di servizio LOG_RES e LOG_DET: assenti ⇒ file rifiutato validation BL VERIFIED
BR-BLK-011 Le colonne DB di PERSONE_ devono essere individuabili (getColumnsByTable); la congruenza colonne-file↔colonne-DB è però disabilitata (codice commentato) validation mixed VERIFIED
BR-BLK-012 In insert ogni riga deve indicare il vano (VANO/CODICE_VANO): assente ⇒ riga scartata validation BL VERIFIED
BR-BLK-013 La collocazione spaziale deve risolvere a esattamente 1 vano appartenente al tenant (0 o >1 ⇒ scarto con ActionError) validation mixed VERIFIED
BR-BLK-014 Deduplica in insert: se la persona esiste già per la commessa o per altra commessa (con la chiave) l'insert è bloccato con alert validation BL VERIFIED
BR-BLK-015 La chiave persona è risolta da strPeopleKey: se vuoto usa ID_N di riga, altrimenti compone un filtro tipizzato e recupera ID_N solo se individua esattamente 1 persona calculation BL VERIFIED
BR-BLK-016 Vengono scritti solo i campi del file mappabili sulla EntityStructure di PERSONE_ (filtrata per ruoli); esclusi campi spaziali, LOG_RES/LOG_DET, BLOB e campi che iniziano con ID_ validation BL VERIFIED
BR-BLK-017 L'ID_VANO risolto è iniettato esplicitamente nei campi entità in insert calculation BL VERIFIED
BR-BLK-018 Le date dei campi del file sono normalizzate a formato dd/MM/yyyy con cultura it-IT prima del salvataggio date-logic BL VERIFIED
BR-BLK-019 Il DAO aggiunge ID_CHANGED_BY = <username> ai campi da salvare se assente validation BL VERIFIED
BR-BLK-020 La persistenza della persona usa la procedura generica PRP_MANAGEENTITY (PERSONE/ID_N), la stessa del salvataggio interattivo state-transition mixed VERIFIED/SUPPORTED
BR-BLK-021 Interpretazione del ritorno di SavePeople: numerico ⇒ successo; non numerico ⇒ errore DB con messaggio in alert ActionError state-transition BL VERIFIED
BR-BLK-022 Contatori di completamento (TotalObjects/ValidObjects/CreatedObjects, warns) — CreatedObjects sovrastima perché InsertObject ritorna True anche su riga scartata (dedup/errore DB) state-transition BL VERIFIED (difetto INFERRED)
BR-BLK-023 Al termine il foglio Excel è aggiornato con LOG_RES/LOG_DET per riga (UpdateDocumentFile), il file ripersistito e la copia su disco cancellata; LOG_DET troncato a 250 char, errori ORA- al solo codice (9 char) side-effect mixed VERIFIED
BR-BLK-024 Multi-tenant: collocazione e deduplica sono sempre filtrate per il tenant corrente (GetCurrentTenancyID(Username)) permission BL VERIFIED
BR-BLK-025 L'abilitazione delle opzioni di upload dipende dai grant utente: IMP_PERSONE richiede ADD_DELETE, UPD_PERSONE richiede EDIT — entrambi valutati sull'entità Asset — ma solo lato UI (nessun ricontrollo server-side) permission UI VERIFIED
BR-BLK-026 File obbligatorio: senza file caricato (tranne rami DWG/OMI) il caricamento è interrotto con messaggio validation UI VERIFIED
BR-BLK-027 Update Persone: flag nascosti evaluateLocation/evaluateReleaseAsset da checkbox; con cambio vano gli asset collegati sono trasferiti (ed eventualmente rilasciati) via UpdatePeopleChangeROOM state-transition mixed VERIFIED
BR-BLK-028 Update dedup: existEntityByKeyAndTenant deve ritornare code 1 (persona univoca per la commessa); code 0 (assente per la commessa) o -1 (non identificabile) ⇒ update saltato validation BL VERIFIED
BR-BLK-029 Il file Excel è letto via OleDb (provider ACE/JET, connection string xls da config): selezione automatica del primo foglio con nome contenente $ e diverso da Foglio1$; provider scelto per estensione (Excel 8.0/12.0) config-flag BL SUPPORTED

Dettaglio delle regole

BR-BLK-001 — Apertura del processo di caricamento (STATUS='RUNNING')

  • Regola: ogni StartFileUploadProcess chiama StartProcess, che esegue RPS_STARTUPLOADPROCESS: INSERT INTO GLOBAL_UPLOAD_PROCESS (FILENAME, START_DATE, END_DATE, USERNAME, DOCUMENT_FILE, STATUS, ENTITY_TYPE) VALUES (..., SYSDATE, NULL, ..., 'RUNNING', ...) RETURNING ID_PROCESS INTO PNEWINDEX. Il processid restituito identifica il caricamento.
  • Tipo: state-transition · Enforcement: mixed (BL orchestra, DB inserisce) · Confidence: VERIFIED
  • Evidenza (BL): StartProcess(...) con OUT PNEWINDEX e dl.ExecuteNonQueryStoredProcedure("RPS_STARTUPLOADPROCESS")InfocadServer/BulkLoaderManager/BulkLoader.vb:791-812; invocazione sul ramo People :199-207.
  • Evidenza (DB): RPS_STARTUPLOADPROCESS (USER_SOURCE src:9-12, letto in FLOW-BLK-001).
  • Nota: FILENAME salvato è solo il nome base del file (filename.Substring(filename.LastIndexOf("\") + 1)), non il path — rilevante per BR-BLK-006.

BR-BLK-002 — Doppia persistenza del file sorgente (BLOB + disco)

  • Regola: il file caricato è salvato (a) come BLOB DOCUMENT_FILE nel record di processo e (b) come copia temporanea su disco sotto <AppDir>\bulkloadtmp, con naming anti-collisione (_1, _2, … se il nome esiste già).
  • Tipo: side-effect · Enforcement: mixed · Confidence: VERIFIED
  • Evidenza: SaveUploadedFile(exceldoc, filename) crea bulkloadtmp e itera il suffisso _cntBulkLoader.vb:1013-1030; BLOB via PDOCUMENTFILE in StartProcess :794-795.
  • Flag: il BLOB abilita GetFileUploadSource/retry senza ri-upload; la copia su disco è invece cancellata a fine import (vedi BR-BLK-023) — divergenza rilevante per il retry (BR-BLK-006).

BR-BLK-003 — Chiusura processo con stato e contatori

  • Regola: al termine di StartImport, CompleteFileUploadProcess esegue RPS_COMPLETEUPLOADPROCESS: UPDATE GLOBAL_UPLOAD_PROCESS SET STATUS=PCOMPLETION, END_DATE=SYSDATE, TOTALOBJS=..., VALIDOBJS=..., CREATEDOBJS=... WHERE ID_PROCESS=.... Lo stato è uno tra Done (nessun warning), DoneWithWarnings (almeno un warning), NotDone (file non valido).
  • Tipo: state-transition · Enforcement: mixed · Confidence: VERIFIED
  • Evidenza (BL): calcolo stato If warns > 0 ... DoneWithWarnings Else Done e Return CompletionResults.NotDone su file invalido — PeopleBulkLoader.vb:122-135; chiamata CompleteFileUploadProcess(...)BulkLoader.vb:687-689,989-1011; enum CompletionResults {Running, Done, DoneWithWarnings, NotDone}BulkLoaderBase.vb:1350-1355.
  • Evidenza (DB): RPS_COMPLETEUPLOADPROCESS (USER_SOURCE src:9-11).
  • Flag: il parametro PCOMPLETION è bindato come Varchar2 a partire dal valore enum (BulkLoader.vb:993): la rappresentazione effettivamente scritta in STATUS (nome "Done" vs ordinale) non è determinabile dal solo codice — vedi Open questions.

BR-BLK-004 — Alert per riga su GLOBAL_UPLOAD_ALERT

  • Regola: per ogni riga (e per gli errori a livello file) viene scritto un alert INSERT INTO GLOBAL_UPLOAD_ALERT (PROCESSID, ROWNUMBER, OBJECTCODE, MESSAGE, STATUS) ... RETURNING alertid. Lo STATUS è il nome dell'enum ActionResults: NoAction=0, ActionWaitingForValidation=1, ActionSuccess=2, ActionError=3, ActionSuccessWithWarning=4.
  • Tipo: state-transition · Enforcement: mixed · Confidence: VERIFIED
  • Evidenza (BL): WriteAlert(drObj, code, logs, status)bulkCtrl.WriteUploadAlert(ProcessID, rowindex, code, logs, status.ToString)BulkLoaderBase.vb:558-566; WriteUploadAlertRPS_WRITEUPLOADALERTBulkLoader.vb:954-987; enum — BulkLoaderBase.vb:1357-1363.
  • Evidenza (DB): RPS_WRITEUPLOADALERT (USER_SOURCE src:10-13).
  • Flag: magic numbers (valori enum 0-4) e stringhe di stato scritte in chiaro; ROWNUMBER = indice riga+1 (0 per alert a livello file).

BR-BLK-005 — Troncamento messaggio alert a 3999 caratteri

  • Regola: se il messaggio dell'alert è ≥ 4000 caratteri viene troncato a 3999 prima dell'INSERT.
  • Tipo: validation · Enforcement: BL · Confidence: VERIFIED
  • Evidenza: If message.Length >= 4000 Then message = message.Substring(0, 3999)BulkLoader.vb:956-958.
  • Flag: magic number 4000/3999 (verosimilmente il limite VARCHAR2 della colonna MESSAGE).

BR-BLK-006 — Retry condizionato alla presenza del file su disco

  • Regola: RetryFileUploadProcess(processID, username) rilegge il record processo (RPS_RETRYUPLOADPROCESS), ne estrae FILENAME e ENTITY_TYPE, ricostruisce il tipo entità e rilancia Import solo se System.IO.File.Exists(filename); altrimenti ritorna 0 senza fare nulla.
  • Tipo: state-transition · Enforcement: mixed · Confidence: VERIFIED
  • Evidenza: RetryFileUploadProcess(...) e guardia If filename IsNot DBNull.Value AndAlso System.IO.File.Exists(filename)BulkLoader.vb:267-285; RetryProcessRPS_RETRYUPLOADPROCESS :814-837.
  • Flag: gotcha nascosto — il retry dipende dalla copia su disco, non dal BLOB persistito; ma FILENAME è memorizzato come nome base (BR-BLK-001) e la copia su disco viene cancellata a fine import (BR-BLK-023). Di conseguenza il retry di un processo già completato rischia di non trovare il file e ritornare 0. Impatto esatto da verificare con Descor (INFERRED).

BR-BLK-007 — Motore unico e dispatch per tipo-entità

  • Regola: esiste un unico motore server (BulkLoader) che, in base al tipo dinamico dell'entità (SPATIAL/MAINT/CATEGORY/GLOBAL/DOCUMENT/TYPOLOGY/NRG/ENERGY/BOOK), istanzia il loader specializzato corrispondente (PeopleBulkLoader, CompaniesBulkLoader, AssetBulkLoader, …). Un tipo non gestito produce un alert "<AREA> UNKNOWN"/"ENTITY UNKNOW" con status NoAction e il processo viene comunque chiuso.
  • Tipo: config-flag (routing) · Enforcement: BL · Confidence: VERIFIED
  • Evidenza: overload Import(...) con Select Case entityType.GetType()/getEnum() e rami Case Else WriteUploadAlert(..., "... UNKNOWN", ..., "NoAction")BulkLoader.vb:431-582 (generale), :646-671 (Costcentre/Companies), :673-692 (People). Ramo People: Case BulkEntity.GLOBAL.ENTITY.PERSONE ⇒ New PeopleBulkLoader(...) :677-678.
  • Flag: refuso hard-coded "ENTITY UNKNOW" (manca la N finale) :659,680.

BR-BLK-008 — Insert vs Update Persone pilotato dall'UI

  • Regola: il ramo UI determina la modalità: IMP_PERSONE chiama il servizio con isInsert:=True, evaluateLocation:=0, evaluateAction:=0; UPD_PERSONE con isInsert:=False e i flag da checkbox. Il flag isInsert seleziona InsertPeople vs updatePeople, e SavePeople(username, peopleID, ...) decide INSERT (peopleID=0) o UPDATE (peopleID=ID_N>0) a livello PRP_MANAGEENTITY.
  • Tipo: state-transition · Enforcement: mixed · Confidence: VERIFIED
  • Evidenza: UI Case "IMP_PERSONE" ...StartFileUploadProcess(..., strPeopleKey, True, 0, 0)UploadProcessControl.ascx.vb:453-460; Case "UPD_PERSONE" ..., strPeopleKey, False, ... :461-468; dispatch InsertObject/InsertPeople/updatePeoplePeopleBulkLoader.vb:36-67; SavePeople(Username, 0, entityFields) (insert) :406 vs SavePeople(Username, CInt(peopleValueArray(1)), ...) (update) :544.
  • Nota: coerente con BR-PROP-001 (semantica id 0/>0 risolta in PRP_MANAGEENTITY).

BR-BLK-009 — Chiavi di deduplica obbligatorie

  • Regola: in insert il file è rifiutato se strPeopleKey (chiavi selezionate in UI) è vuoto; in update è accettata in alternativa la presenza della colonna ID_N nel file.
  • Tipo: validation · Enforcement: mixed (UI compone, BL valida) · Confidence: VERIFIED
  • Evidenza (UI): composizione strPeopleKey dalle cmbPeopleKey.CheckedItems (;-separated) — UploadProcessControl.ascx.vb:454-459,462-467.
  • Evidenza (BL): insert If String.IsNullOrEmpty(_tableKey) Then valid=False ... "Specificare le chiavi..."PeopleBulkLoader.vb:196-199; update If String.IsNullOrEmpty(_tableKey) AndAlso Not dt.Columns.Contains("ID_N") :245-248.

BR-BLK-010 — Colonne di file obbligatorie

  • Regola: il file deve contenere la colonna VANO o CODICE_VANO (per il posizionamento) e le due colonne di servizio LOG_RES e LOG_DET (per la scrittura dell'esito); l'assenza di una qualsiasi rende il file non valido (ValidateFile ⇒ alert NoAction, StartImport ritorna NotDone).
  • Tipo: validation · Enforcement: BL · Confidence: VERIFIED
  • Evidenza: ValidateInsertingFilePeopleBulkLoader.vb:191-194 (VANO/CODICE_VANO), :224-232 (LOG_DET/LOG_RES); ValidateFileWriteAlert(Nothing, "FILE", logs, NoAction) :146-149.

BR-BLK-011 — Colonne DB di PERSONE_ individuabili; congruenza file↔DB disabilitata

  • Regola: se getColumnsByTable("PERSONE_") non restituisce colonne il file è rifiutato ("Colonne della tabella PERSONE non individuate"). Il controllo che verificava che ogni colonna del file esistesse anche nel DB è invece commentato/disattivato.
  • Tipo: validation · Enforcement: mixed (BL + lettura catalogo DB) · Confidence: VERIFIED
  • Evidenza: Dim gs As List(Of String) = daoGS.getColumnsByTable(ENTITY) e Else valid=False ... non individuatePeopleBulkLoader.vb:187-189,201,220-223; ciclo di congruenza colonne commentato :214-219 (insert) e :262-267 (update).
  • Flag: validazione disabilitata (dead code) — colonne del file non presenti nel DB non generano più errore; vengono semplicemente ignorate dalla mappatura (BR-BLK-016).

BR-BLK-012 — Vano obbligatorio per riga (solo insert)

  • Regola: in insert ogni riga deve indicare VANO o CODICE_VANO; se entrambi mancano/nulli la riga è scartata con alert NoAction. In update il controllo non si applica.
  • Tipo: validation · Enforcement: BL · Confidence: VERIFIED
  • Evidenza: If valid And _isInsert Then If (Not ...CODICE_VANO ...) AndAlso (Not ...VANO ...) Then valid=False ... "specificare il vano..."PeopleBulkLoader.vb:159-168.

BR-BLK-013 — Collocazione univoca e nel tenant

  • Regola: ValidateSpaceLocation costruisce i parametri spaziali presenti nella riga (REGIONE, CITTA, COMPRENSORIO, EDIFICIO, PIANO, VANO e le varianti CODICE_*) e interroga GetAssetLocationByParamAndTnt(param, _currenttenantID). La collocazione è valida solo se restituisce esattamente 1 riga (ID_VANO); >1 ⇒ "non univoca", 0/nessuna tabella ⇒ "non appartenente al tenant e/o non individuabile"; in entrambi i casi alert ActionError e riga scartata.
  • Tipo: validation · Enforcement: mixed (BL + lettura DB) · Confidence: VERIFIED (logica BL) / INFERRED (SQL interno di GetAssetLocationByParamAndTnt)
  • Evidenza: ValidateSpaceLocationPeopleBulkLoader.vb:285-330; Rows.Count = 1ID_VANO :306-309; > 1 :310-313; 0/else :314-323.

BR-BLK-014 — Deduplica in insert

  • Regola: prima dell'inserimento, se la chiave individua una persona già esistente per il tenant, l'insert è bloccato: existEntityByKeyAndTenant("ID_N", "PERSONE_", "ID_N", peopleKeyValue, tenant) ritorna una stringa code,id; code=0 ⇒ "esiste già ... per un'altra commessa" (da far legare dal responsabile), altrimenti ⇒ "esiste già ... associata alla commessa". In entrambi i casi la persona non viene creata.
  • Tipo: validation · Enforcement: BL · Confidence: VERIFIED (chiamante) / INFERRED (SQL interno PRP_EXISTENTITYBYKEYANDTENANT)
  • Evidenza: peopleValue = New DAO_GenericEntity().existEntityByKeyAndTenant(...) e branch sul primo elemento dello split ","PeopleBulkLoader.vb:370-377; DAO → PRP_EXISTENTITYBYKEYANDTENANT (OUT CLOB) — DAO_GenericEntity.vb:731-763.
  • Flag: magic codes nel valore di ritorno (0 = altra commessa, ≠0 = commessa corrente).

BR-BLK-015 — Risoluzione della chiave persona

  • Regola: se strPeopleKey è vuoto la chiave è ID_N letto direttamente dalla riga; altrimenti si compone una WHERE a partire dai valori delle colonne-chiave (tipizzata: numerico senza apici, stringa con apici ed escaping ''') e si recupera ID_N via GetPeopleByFilter, solo se il filtro individua esattamente 1 persona.
  • Tipo: calculation · Enforcement: BL · Confidence: VERIFIED
  • Evidenza: composizione whereclause e ctrlPeople.GetPeopleByFilter(whereclause) con Rows.Count = 1PeopleBulkLoader.vb:342-367.
  • Flag: SQL costruito per concatenazione di stringhe (superficie di injection mitigata dall'escaping Replace("'", "''") e dalla tipizzazione delle colonne).

BR-BLK-016 — Mappatura campi per struttura entità e ruoli

  • Regola: solo le colonne del file mappabili sui campi della EntityStructure di PERSONE_ (ottenuta per i ruoli dell'utente) vengono scritte. Sono esclusi: i campi spaziali (_spaceFields), le colonne di servizio LOG_RES/LOG_DET, i campi di tipo BLOB e i campi il cui nome inizia con ID_.
  • Tipo: validation (permission-driven) · Enforcement: BL · Confidence: VERIFIED
  • Evidenza: struct = New DAO_Metadata().GetEntityStructure(ENTITY, roles) e struct.Fields.Find(Function(f) (f.Label = col.ColumnName Or f.Field = col.ColumnName.ToUpper) AndAlso (f.DataType <> "BLOB" And Not f.Field.StartsWith("ID_")))PeopleBulkLoader.vb:383-405; _spaceFields:20; ruoli via InfocadLoginSettingsProvider().GetUserRoles :337.
  • Flag: i campi ID_* sono esclusi dall'import per riga (l'unica eccezione è ID_VANO, aggiunto esplicitamente — BR-BLK-017).

BR-BLK-017 — Iniezione esplicita di ID_VANO in insert

  • Regola: il vano risolto (idRoom) è aggiunto ai campi entità come ID_VANO prima della mappatura degli altri campi.
  • Tipo: calculation · Enforcement: BL · Confidence: VERIFIED
  • Evidenza: entityFields(...) = New String(1) {"ID_VANO", CStr(idRoom)}PeopleBulkLoader.vb:379-381.

BR-BLK-018 — Normalizzazione date dd/MM/yyyy (it-IT)

  • Regola: i valori di tipo DateTime del file vengono riformattati a dd/MM/yyyy usando la cultura it-IT prima di essere passati al salvataggio.
  • Tipo: date-logic · Enforcement: BL · Confidence: VERIFIED
  • Evidenza: Dim culture As CultureInfo = New CultureInfo("it-IT") ... dateValue = giorno + "/" + mese + "/" + annoPeopleBulkLoader.vb:391-399 (insert) e :499-507 (update).
  • Flag: cultura e formato hard-coded (it-IT, dd/MM/yyyy); si veda anche BR-PROP-024 (le NLS di sessione forzate lato DB a DD/MM/YYYY HH24:MI:SS in PRP_MANAGEENTITY). La lettura dell'Excel applica a sua volta un'euristica di conversione date (GetCellValue) dipendente dalla CurrentUICulture (BulkLoaderBase.vb:702-750).

BR-BLK-019 — Stamping ID_CHANGED_BY

  • Regola: DAO_People.SavePeople aggiunge ID_CHANGED_BY = <username> all'insieme dei campi se non già presente.
  • Tipo: validation (audit) · Enforcement: BL · Confidence: VERIFIED
  • Evidenza: If Not bFound Then Array.Resize(KeyValue, ...) : KeyValue(...) = New String(1) {"ID_CHANGED_BY", username}DAO_People.vb:233-244.
  • Nota: condivisa con BR-PROP-016 (stesso pattern di audit sulle entità).

BR-BLK-020 — Persistenza via PRP_MANAGEENTITY

  • Regola: SavePeople(username, peopleID, KeyValue) invoca DAO_GenericEntity.ManageEntity(username, peopleID, "ID_N", "PERSONE", "", "", KeyValue), che risolve la tabella fisica PERSONE_ e chiama la procedura generica PRP_MANAGEENTITY (INSERT con RETURNING ID_N in creazione, UPDATE su id in aggiornamento; binding tenant su GLOBAL_TENANCY_BINDINGS).
  • Tipo: state-transition · Enforcement: mixed · Confidence: VERIFIED (catena) / SUPPORTED (interno procedura, ereditato da BR-PROP)
  • Evidenza: Return genCtrl.ManageEntity(username, peopleID, "ID_N", "PERSONE", String.Empty, String.Empty, KeyValue)DAO_People.vb:247-248. Comportamento interno di PRP_MANAGEENTITY: vedi BR-PROP (BR-PROP-001..010, 024, 025).

BR-BLK-021 — Interpretazione del ritorno di SavePeople

  • Regola: idPeople numerico ⇒ successo (alert ActionSuccess, oppure ActionSuccessWithWarning se il log contiene già messaggi); idPeople non numerico ma non vuoto ⇒ errore DB (il messaggio è accodato al log e scritto in alert ActionError).
  • Tipo: state-transition · Enforcement: BL · Confidence: VERIFIED
  • Evidenza: If IsNumeric(idPeople) Then ... ActionSuccess/ActionSuccessWithWarning ElseIf idPeople <> "" Then log.Append(idPeople) ... ActionErrorPeopleBulkLoader.vb:414-426 (insert), :558-573 (update).
  • Nota: coerente con BR-PROP-027 (numerico = successo, non numerico = errore DB).

BR-BLK-022 — Contatori di completamento (e sovrastima di CreatedObjects)

  • Regola: durante StartImport si contano TotalObjects (righe non vuote), ValidObjects (righe che superano ValidateObject), CreatedObjects (righe per cui InsertObject ritorna True); warns è incrementato quando l'insert fallisce o quando il log non contiene la stringa "senza errori".
  • Tipo: state-transition · Enforcement: BL · Confidence: VERIFIED (conteggio) / INFERRED (difetto)
  • Evidenza: ciclo di conteggio — PeopleBulkLoader.vb:101-120; marcatura warning If Not log.ToString().Contains("senza errori") :108-113.
  • Flag: difettoInsertPeople/updatePeople ritornano True (:427, :575) anche quando la riga è scartata (deduplica bloccante, errore DB, chiave non risolta): in questi casi CreatedObjects viene comunque incrementato, pur avendo scritto un alert ActionError. Il contatore CREATEDOBJS persistito può quindi sovrastimare le persone effettivamente create. Da verificare con Descor.
  • Flag: magic string "senza errori" come discriminante di successo pieno.

BR-BLK-023 — Scrittura dell'esito sul foglio e ripersistenza

  • Regola: al termine, UploadLogOnUploadedFile aggiorna il foglio Excel scrivendo LOG_RES (SUCCESS/ERROR) e LOG_DET (dettaglio) per ogni riga (via UPDATE [foglio] ... WHERE <chiavi>), ripersiste il file aggiornato nel processo (UpdateDocumentFileRPS_UPDATEDOCUMENTFILE) e cancella la copia temporanea su disco. LOG_DET è troncato a 250 caratteri; gli errori ORA-* sono ridotti al solo codice (primi 9 caratteri).
  • Tipo: side-effect · Enforcement: mixed · Confidence: VERIFIED
  • Evidenza: UploadLogOnUploadedFile(...)BulkLoaderBase.vb:1132-1232; troncamenti errLog.Substring(0, 9) e Substring(0, 250) :1180-1186; UpdateDocumentFile + File.Delete(filename) :1216-1220; chiamata da StartImport con keyVal (chiavi o ID_N) — PeopleBulkLoader.vb:125-129.
  • Flag: dipende dal provider OleDb (vedi BR-BLK-029); la cancellazione del file su disco impatta il retry (BR-BLK-006).

BR-BLK-024 — Filtro multi-tenant

  • Regola: il tenant corrente è risolto una volta a inizio import (GetCurrentTenancyID(Username)) e usato per la risoluzione della collocazione (BR-BLK-013) e per la deduplica (BR-BLK-014).
  • Tipo: permission · Enforcement: BL · Confidence: VERIFIED
  • Evidenza: _currenttenantID = tntCtrl.GetCurrentTenancyID(Username)PeopleBulkLoader.vb:76-77; uso in GetAssetLocationByParamAndTnt(param, _currenttenantID) :303 e existEntityByKeyAndTenant(..., _currenttenantID) :371,472.
  • Nota: il binding tenant vero e proprio è propagato da PRP_MANAGEENTITY (BR-PROP-009/010).

BR-BLK-025 — Grant di upload (solo UI, valutato sull'entità Asset)

  • Regola: l'opzione IMP_PERSONE è abilitata solo se l'utente ha grant ADD_DELETE, UPD_PERSONE solo se ha grant EDIT; entrambi i grant sono valutati sull'entità Asset (SystemFields.AssetEntity). Se il grant manca, l'opzione della dropdown è disabilitata.
  • Tipo: permission · Enforcement: UI · Confidence: VERIFIED
  • Evidenza: If Not HasUserGrant(IVPermission.ADD_DELETE, SystemFields.AssetEntity) Then LoadOptions.Items.FindByValue("IMP_PERSONE").Enabled = FalseUploadProcessControl.ascx.vb:897-899; EDITUPD_PERSONE :893-895.
  • Flag: controllo UI-only — il servizio remoting BulkLoader/PeopleBulkLoader non ricontrolla alcun grant (nessun HasUserGrant lato server): la barriera è puramente cosmetica e bypassabile da un chiamante che invochi direttamente il servizio. Inoltre il permesso per l'import Persone è legato all'entità Asset (accoppiamento non ovvio) — da chiarire con Descor.

BR-BLK-026 — File obbligatorio

  • Regola: se non è stato caricato alcun file (per tutti i rami tranne DWG e OMI-anagrafica) viene mostrato un messaggio e il caricamento è interrotto.
  • Tipo: validation · Enforcement: UI · Confidence: VERIFIED
  • Evidenza: If upload1.UploadedFiles.Count <= 0 Then ShowMessage(...DocUploadNoFileSelectedMsg...) : Exit SubUploadProcessControl.ascx.vb:391-402.

BR-BLK-027 — Update Persone: flag di collocazione/rilascio asset

  • Regola: in UPD_PERSONE, evaluateLocation vale 0 se è spuntato "aggiorna solo attributi" (chkUpdateOnlyPeopleAttributes), 1 altrimenti; evaluateReleaseAsset vale 1 se spuntato "sgancia asset collegati" (chkDropLinkedAsset). Con cambio vano (idRoom ≠ SourceRoomID) gli asset collegati alla persona sono trasferiti nel nuovo vano (UpdatePeopleChangeROOM), ed eventualmente marcati ToRelease se evaluateReleaseAsset=1.
  • Tipo: state-transition · Enforcement: mixed · Confidence: VERIFIED
  • Evidenza (UI): ..., strPeopleKey, False, If(chkUpdateOnlyPeopleAttributes.Checked, 0, 1), If(chkDropLinkedAsset.Checked, 1, 0)UploadProcessControl.ascx.vb:468.
  • Evidenza (BL): dispatch collocazione InsertObject (_evaluateLocation) — PeopleBulkLoader.vb:36-66; trasferimento asset e UpdatePeopleChangeROOM(data):515-543.
  • Flag: flag nascosti mappati da checkbox; stringhe magiche di ReturnCode ("OK", "KO", "-99", "The source and destination room are the same.") :529-539.

BR-BLK-028 — Update dedup (codici 1/0/-1)

  • Regola: in update la persona è aggiornata solo se existEntityByKeyAndTenant ritorna code 1 (individuata univocamente per la commessa). Code 0 ⇒ "non esiste in archivio per questa commessa", -1 ⇒ "non identificabile univocamente", altro ⇒ "non identificabile univocamente per la commessa corrente"; in tutti questi casi l'update è saltato (viene comunque scritto un alert).
  • Tipo: validation · Enforcement: BL · Confidence: VERIFIED
  • Evidenza: Select sui valori CInt(peopleValueArray(0)) = 0 / = -1 / = 1 / ElsePeopleBulkLoader.vb:471-548; l'aggiornamento effettivo SavePeople(Username, CInt(peopleValueArray(1)), entityFields) avviene solo nel ramo = 1 :544.
  • Flag: magic codes 1/0/-1.

BR-BLK-029 — Lettura Excel via OleDb e selezione del foglio

  • Regola: il file è letto con il provider OleDb (connection string di configurazione xls, con Extended Properties Excel 8.0 per .xls / Excel 12.0 per .xlsx). Viene selezionato automaticamente il primo foglio il cui nome tabella contiene $ ed è diverso da Foglio1$.
  • Tipo: config-flag · Enforcement: BL · Confidence: SUPPORTED (dipende dall'ambiente/provider installato)
  • Evidenza: LoadUploadedFileOleDb con euristica sul foglio ... .Contains("$") AndAlso Not ....Contains("Foglio1$") ...BulkLoaderBase.vb:610-654; GetExcelConnString (provider per estensione) :1318-1339; LoadUploadedFileLoadUploadedFileOleDb(filename, False) :568-570.
  • Flag: dipendenza d'ambiente (provider ACE/JET OleDb sul server) e valore hard-coded italiano "Foglio1$"; esiste anche un percorso alternativo OpenXml non usato nel path pilota (LoadUploadedFileOpenXml).

Sintesi dei rischi e flag

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

  • BR-BLK-025 — il grant di upload (ADD_DELETE/EDIT su entità Asset) è verificato solo lato UI; il servizio remoting BulkLoader non ricontrolla alcun permesso. Un chiamante diretto del servizio bypassa il controllo.
  • BR-BLK-026 — il vincolo "file obbligatorio" è solo UI.

Difetti / gotcha

  • BR-BLK-022CreatedObjects/CREATEDOBJS sovrastima: InsertObject ritorna True anche su riga scartata (dedup, errore DB, chiave non risolta).
  • BR-BLK-006 — il retry dipende dalla copia su disco del file, che a fine import viene cancellata (BR-BLK-023), e da un FILENAME memorizzato come solo nome-base (BR-BLK-001): il retry di un processo già completato rischia di ritornare 0 senza operare.
  • BR-BLK-011 — la validazione di congruenza colonne-file↔colonne-DB è disattivata (codice commentato): colonne inattese nel file vengono silenziosamente ignorate.

Magic number / valori e stringhe hard-coded

  • Limite messaggio alert 4000/3999 (BR-BLK-005); troncamenti LOG_DET 250 e ORA- 9 (BR-BLK-023).
  • Enum ActionResults 0-4 scritti come STATUS in GLOBAL_UPLOAD_ALERT (BR-BLK-004); codici ritorno dedup 1/0/-1 (BR-BLK-014, BR-BLK-028).
  • Stringa "senza errori" come discriminante di successo (BR-BLK-022); ReturnCode "OK"/"KO"/"-99"/"The source and destination room are the same." (BR-BLK-027).
  • Refuso "ENTITY UNKNOW" (BR-BLK-007); foglio "Foglio1$" escluso (BR-BLK-029).

Logica date-sensitive / cultura

  • BR-BLK-018 — riformattazione dd/MM/yyyy con cultura it-IT hard-coded; euristica di conversione date in lettura Excel dipendente dalla CurrentUICulture (GetCellValue).

Flag nascosti

  • BR-BLK-027evaluateLocation (solo-attributi) ed evaluateReleaseAsset (sgancio asset) pilotati da checkbox UI e propagati fino al server come interi.

Dipendenze d'ambiente / cliente-specifiche

  • BR-BLK-029 — provider OleDb (ACE/JET) installato sul server InfocadServer; BR-BLK-020 eredita le varianti cliente-specifiche di PRP_MANAGEENTITY (generazione codice, trigger custom) documentate in BR-PROP.

Open questions

  • Rappresentazione effettivamente scritta in GLOBAL_UPLOAD_PROCESS.STATUS dal parametro PCOMPLETION (enum CompletionResults bindato come Varchar2): nome ("Done") o ordinale? (BR-BLK-003).
  • Impatto reale del retry (BR-BLK-006): RPS_RETRYUPLOADPROCESS restituisce il path completo o il solo nome-base? Il file su disco è ancora presente dopo il completamento? Da verificare con Descor.
  • Conferma della sovrastima di CREATEDOBJS (BR-BLK-022) su dati reali e sua eventuale rilevanza per la reportistica dei caricamenti.
  • SQL interno di GetAssetLocationByParamAndTnt e PRP_EXISTENTITYBYKEYANDTENANT (BR-BLK-013, BR-BLK-014) — non ispezionati riga per riga.
  • Motivazione dell'accoppiamento del permesso di import Persone all'entità Asset (BR-BLK-025).
  • Se il processo BulkLoader giri in modo sincrono nel thread della richiesta remoting o su thread dedicato del servizio (non determinabile dal solo codice).