BR-STK — Regole di business del dominio Stock / Magazzino
Regole di business estratte dal flusso pilota FLOW-STK-001 (registrazione di un movimento di magazzino: ordine / carico / scarico / inventario) e dai sorgenti del modulo Stock. Sistema Infocad (Descor): backend Oracle (nessun ORM, stored procedure via DAL
Execute*StoredProcedure), frontend ASP.NET Web Forms (VB.NET) sotto la shell CASSANDRA (StockCenterWeb), controller/BL/DAL in-process (Descor.Stock.StockControllers/StockBL/StockDAL). Prosa in italiano; identificatori (classi/metodi/proc/colonne/costanti) non tradotti.
- Dominio: Stock / Magazzino (registrazione movimenti, giacenza, ordinato, storicizzazione)
- Flusso di riferimento:
docs/07-business-flows/FLOW-STK-001.md - Confidenza complessiva: VERIFIED (catena UI → code-behind → controller → BL → DAL → stored procedure,
confermata su codice e su sorgente DB
USER_SOURCE/USER_TRIGGERS/USER_CONSTRAINTS/USER_CONS_COLUMNS/USER_TAB_COLUMNS, letti in sola lettura) - Schema DB pilota:
INFOCAD_TEST38(osservato solo ambiente TEST38)
Legenda enforcement
- UI = validato solo nel code-behind/controlli ASP.NET (rischio: bypassabile chiamando BL/DAL direttamente)
- BL = logica nel Business Layer VB.NET (
StockBL, DTOStockItemHistory) - DB = vincolo / sequence / trigger / procedura Oracle
- mixed = combinazione dei precedenti
Registro delle regole
| id | regola (sintesi) | tipo | enforcement | conf. |
|---|---|---|---|---|
| BR-STK-001 | Accesso alla sezione movimenti solo con grant 6410 o SuperAdmin |
permission | UI | VERIFIED |
| BR-STK-002 | Comando Save visibile solo con grant 6420 o SuperAdmin |
permission | UI | VERIFIED |
| BR-STK-003 | Comando Delete / aggiunta movimento visibile solo con grant 6430 o SuperAdmin |
permission | UI | VERIFIED |
| BR-STK-004 | Selezione fornitore/costo reale abilitata solo con grant 6450 (e 6440 per costo reale) |
permission | UI | VERIFIED |
| BR-STK-005 | Quattro tipi di movimento hard-coded: Order=1, Load=2, Unload=3, StockTaking=4 | config-flag | mixed (BL+DB) | VERIFIED |
| BR-STK-006 | Ricalcolo giacenza/ordinato dipende dalla direzione del movimento | calculation | DB | VERIFIED |
| BR-STK-007 | Lo scarico (dir. 3) non decrementa la giacenza se l'articolo è price-list | state-transition | DB | VERIFIED |
| BR-STK-008 | L'inventario (dir. 4) imposta la giacenza al valore assoluto rilevato | calculation | DB | VERIFIED |
| BR-STK-009 | MOVE_DATE sempre = SYSDATE lato DB; DateMove calcolato in UI non è passato alla proc |
date-logic | DB | VERIFIED |
| BR-STK-010 | Quantità obbligatoria e strettamente > 0 | validation | UI | VERIFIED |
| BR-STK-011 | Scarico non oltre la giacenza disponibile per articoli non price-list | validation | UI | VERIFIED |
| BR-STK-012 | Tipo operazione obbligatorio prima del salvataggio | validation | UI | VERIFIED |
| BR-STK-013 | Validazione DTO minima: IdStockItem, QuantityMoved, DateMove, Direction non nulli | validation | BL | VERIFIED |
| BR-STK-014 | Save disabilitato su Scarico se giacenza ≤ 0 e articolo non price-list | permission/validation | UI | VERIFIED |
| BR-STK-015 | Carico (dir. 2) limitato in UI alla quantità ordinata | validation | UI | VERIFIED |
| BR-STK-016 | ID_ITEM_HISTORY generato da trigger BEFORE INSERT via SEQ_STOCK_ITEM_HISTORY (con gap-recovery) |
calculation | DB | VERIFIED |
| BR-STK-017 | Nuovo id restituito via SEQ...CURRVAL nel parametro OUT PNEWINDEX |
calculation | mixed (BL+DB) | VERIFIED |
| BR-STK-018 | Riga di collegamento in STOCK_ITEM_LINK creata solo se PIDLINK IS NOT NULL |
state-transition | DB | VERIFIED |
| BR-STK-019 | LinkApplicationID valorizzato in UI con il GUID strong-name "RequestCenter" |
config-flag | mixed (UI+BL) | VERIFIED |
| BR-STK-020 | Vincoli NOT NULL su STOCK_ITEM_HISTORY (ID_ITEM, QMOVE, MOVE_DATE, DIRECTION, ID_CONTACT, costi) |
validation | DB | VERIFIED |
| BR-STK-021 | Integrità referenziale movimento → articolo (FK_STOCK_ITEM_HIST) |
validation | DB | VERIFIED |
| BR-STK-022 | Multi-tenant: ID_TENANCY propagato solo se la tenancy è abilitata |
config-flag | mixed (UI+BL) | VERIFIED |
| BR-STK-023 | Cancellazione logica (ISCANCELLED=1) storna la giacenza in senso inverso con clamp a 0; l'inventario NON è stornato |
state-transition | DB | VERIFIED |
| BR-STK-024 | Su Ordine con richieste: chiusura/decurtazione progressiva delle richieste collegate | state-transition | BL | VERIFIED |
| BR-STK-025 | La proc STK_ADDITEMMOVE non esegue check di business (no under-zero guard) |
validation (gap) | DB | VERIFIED |
| BR-STK-026 | SELECT ... INTO TMP_ISPRICELIST presuppone esattamente una riga stock per l'articolo |
validation (gap) | DB | VERIFIED |
| BR-STK-027 | Update movimento fa upsert del collegamento in STOCK_ITEM_LINK |
state-transition | DB | VERIFIED |
| BR-STK-028 | Secondo chiamante DAL di STK_ADDITEMMOVE con 5 parametri, incompatibile con la firma |
config-flag | BL | INFERRED |
Dettaglio regole
BR-STK-001 — Gate di accesso alla sezione movimenti
Statement: L'accesso alla scheda movimento è consentito solo se l'utente possiede il grant funzionale
6410, oppure è SuperAdmin; altrimenti viene sollevata ThrowSectionAccessException().
- Tipo: permission · Enforcement: UI · Confidence: VERIFIED · customer_specific: false
- Evidenza:
MovementDetailControl.ascx.vb:170-174(CheckPermissions():If Not Me.HasUserGrant("6410") And Not WS.SessionProfile.SuperAdmin Then ThrowSectionAccessException()). - Flag: UI-only (nessun controllo di grant a livello BL/DAL); hard-coded id grant
6410.
BR-STK-002 — Grant di salvataggio (Save)
Statement: La visibilità del comando Save della toolbar richiede il grant 6420 (o SuperAdmin).
- Tipo: permission · Enforcement: UI · Confidence: VERIFIED · customer_specific: false
- Evidenza:
MovementDetailControl.ascx.vb:188(FindItemByValue("Save").Visible = Me.HasUserGrant("6420") Or WS.SessionProfile.SuperAdmin), ripetuto per ogni tipo movimento (:574,591,609,623). - Flag: UI-only; hard-coded id grant
6420. Nota: in ramo di nuovo record (:210) il Save è talvolta legato a6430— vedi Open questions.
BR-STK-003 — Grant di cancellazione / aggiunta movimento
Statement: La visibilità del comando Delete (e l'abilitazione all'aggiunta di un movimento) richiede il grant 6430 (o SuperAdmin).
- Tipo: permission · Enforcement: UI · Confidence: VERIFIED · customer_specific: false
- Evidenza:
MovementDetailControl.ascx.vb:189(FindItemByValue("Delete").Visible = Me.HasUserGrant("6430") Or WS.SessionProfile.SuperAdmin),:210,575,592,610,624. - Flag: UI-only; hard-coded id grant
6430.
BR-STK-004 — Grant selezione fornitore e costo reale
Statement: La combo fornitore (rcbCompany) e i campi del costo reale del movimento sono abilitati/visibili
solo con grant 6450 (o SuperAdmin); la visibilità del costo reale totale è concessa anche col grant 6440.
- Tipo: permission · Enforcement: UI · Confidence: VERIFIED · customer_specific: false
- Evidenza:
MovementDetailControl.ascx.vb:213(If rblMovementType.SelectedValue = "3" AndAlso (HasUserGrant("6450") Or ...)),:285,561; costo reale:579-580(... = Me.HasUserGrant("6450") Or Me.HasUserGrant("6440") Or WS.SessionProfile.SuperAdmin). - Flag: UI-only; hard-coded id grant
6450,6440.
BR-STK-005 — Quattro tipi di movimento (enum + magic numbers DB)
Statement: Il movimento ammette quattro direzioni codificate come interi: Order=1 (ordine),
Load=2 (carico), Unload=3 (scarico), StockTaking=4 (inventario). Gli stessi valori numerici
sono usati come rblMovementType in UI, come enum StockMoveDirection nel DTO e come PDIRECTION nella
procedura Oracle.
- Tipo: config-flag (magic numbers) · Enforcement: mixed (BL+DB) · Confidence: VERIFIED · customer_specific: false
- Evidenza: enum
StockSharedObjects/Objects/StockItemHistory.vb:322-329(Order=1, Load=2, Unload=3, StockTaking=4); UIMovementDetailControl.ascx.vb:195(rblMovementType ... "3"), ramorblMovementType_SelectedIndexChanged(:582 "2",:601 "1",:615 "4"); DTOInstantiateDto:692(itemHis.Direction = DirectCast(CInt(rblMovementType.SelectedValue), StockMoveDirection)); DBSTK_ADDITEMMOVE src:35,42,49,57(IF PDIRECTION = 1..4). - Flag: magic numbers replicati su 3 livelli (UI/BL/DB) senza costante condivisa lato DB.
BR-STK-006 — Semantica della direzione sul ricalcolo giacenza/ordinato
Statement: La procedura STK_ADDITEMMOVE, dopo l'INSERT del movimento, aggiorna STOCK_ITEM in base a PDIRECTION:
- Order (1):
QORDERED = QORDERED + PQMOVE(incrementa l'ordinato); - Load (2):
QSTOCK = QSTOCK + PQMOVE, QORDERED = QORDERED - PQMOVE(carica a magazzino e scarica l'ordinato); - Unload (3):
QSTOCK = QSTOCK - PQMOVE(solo se non price-list, vedi BR-STK-007); - StockTaking (4):
QSTOCK = PQMOVE(rettifica assoluta, vedi BR-STK-008). - Tipo: calculation / state-transition · Enforcement: DB · Confidence: VERIFIED · customer_specific: false
- Evidenza (sorgente proc
USER_SOURCE, letto read-only):STK_ADDITEMMOVE src:35-40(dir 1),src:42-47(dir 2),src:49-55(dir 3),src:57-62(dir 4). - Flag: DB-only (semantica di segno interamente nel PL/SQL; la UI non ricalcola la giacenza, si limita a leggerla).
BR-STK-007 — Scarico neutro su articolo price-list
Statement: Il ramo di scarico (PDIRECTION=3) decrementa QSTOCK solo se l'articolo non è price-list
(TMP_ISPRICELIST = 0); per gli articoli price-list il movimento di scarico è storicizzato ma non intacca la giacenza.
- Tipo: state-transition / config-flag · Enforcement: DB · Confidence: VERIFIED · customer_specific: false
- Evidenza:
STK_ADDITEMMOVE src:23-28(SELECT STOCK_STOCK.ISPRICELIST INTO TMP_ISPRICELIST ... JOIN STOCK_ITEM ON ID_STOCK ... AND ID_ITEM = PIDITEM),src:49-50(IF PDIRECTION = 3 AND TMP_ISPRICELIST = 0 THEN). Coerente con la guardia UI:207e:754. - Flag: hidden flag
STOCK_STOCK.ISPRICELIST(proprietà dello stock, non del movimento).
BR-STK-008 — Inventario come rettifica assoluta
Statement: Il movimento di inventario (PDIRECTION=4) imposta la giacenza al valore rilevato:
QSTOCK = PQMOVE (non è un delta ma un valore assoluto). La differenza rispetto alla giacenza precedente
è mostrata solo informativamente in UI (StockTakingDifference = QuantityMoved - OldQuantity).
- Tipo: calculation · Enforcement: DB · Confidence: VERIFIED · customer_specific: false
- Evidenza:
STK_ADDITEMMOVE src:57-62(IF PDIRECTION = 4 THEN UPDATE STOCK_ITEM SET QSTOCK = PQMOVE); differenza informativa UIMovementDetailControl.ascx.vb:738-740.
BR-STK-009 — Data movimento sempre = SYSDATE (data server)
Statement: La data del movimento (MOVE_DATE) è impostata a SYSDATE direttamente nell'INSERT lato DB;
il valore DateMove = Date.Now calcolato in UI e propagato fino al DAL come parametro moveDate non è
passato come OracleParameter alla procedura, quindi è di fatto ignorato dalla persistenza. La colonna ha
inoltre default DB SYSDATE ed è NOT NULL.
- Tipo: date-logic · Enforcement: DB · Confidence: VERIFIED · customer_specific: false
- Evidenza:
STK_ADDITEMMOVE src:30-33(INSERT ... MOVE_DATE ... VALUES (..., SYSDATE, ...)); default/NOT NULL daUSER_TAB_COLUMNS(MOVE_DATE NULLABLE=N DEFAULT=SYSDATE); UIMovementDetailControl.ascx.vb:691(itemHis.DateMove = Date.Now); DAL ricevemoveDate(StockItemHistory_DAL.vb:128) ma non lo aggiunge ai parametri (:144-183). - Flag: date-sensitive — la data movimento non è modificabile dall'operatore (sempre data del server DB).
BR-STK-010 — Quantità obbligatoria e > 0
Statement: In UI la quantità è obbligatoria e deve essere strettamente maggiore di zero: campo vuoto →
errore QuantityRequiredString; valore = 0 → errore QuantityGreaterString.
- Tipo: validation · Enforcement: UI · Confidence: VERIFIED · customer_specific: false
- Evidenza:
MovementDetailControl.ascx.vb:757-761(SetErrorTooltips:If rnbQuantity.Text = String.Empty ... ElseIf rnbQuantity.Value = 0 ...). - Flag: UI-only — il DTO valida solo
QuantityMoved <> Nothing(≠ 0/null), la proc non ha check di segno (vedi BR-STK-013, BR-STK-025).
BR-STK-011 — Scarico entro la giacenza (articoli non price-list)
Statement: Per lo scarico (dir. 3) di un articolo non price-list, se la quantità richiesta supera la
giacenza (rnbQuantity.Value > Me.StockQuantity) viene mostrato l'errore NoItemsInStockString e il
salvataggio è bloccato; il campo quantità ha inoltre MaxValue = StockQuantity.
- Tipo: validation · Enforcement: UI · Confidence: VERIFIED · customer_specific: false
- Evidenza:
MovementDetailControl.ascx.vb:754-756(If rblMovementType.SelectedValue = "3" AndAlso rnbQuantity.Value > Me.StockQuantity AndAlso Not Me.StockIsPriceList),:567(If Not Me.StockIsPriceList Then rnbQuantity.MaxValue = Me.StockQuantity). - Flag: UI-only — l'articolo price-list è escluso dal controllo (coerente con BR-STK-007).
BR-STK-012 — Tipo operazione obbligatorio
Statement: Se nessun tipo movimento è selezionato (rblMovementType.SelectedValue = String.Empty),
viene mostrato l'errore OperationRequiredString e il salvataggio è bloccato.
- Tipo: validation · Enforcement: UI · Confidence: VERIFIED · customer_specific: false
- Evidenza:
MovementDetailControl.ascx.vb:751-753. - Flag: UI-only.
BR-STK-013 — Validazione DTO minima
Statement: Prima dell'insert il DTO StockItemHistory.Validate() verifica la valorizzazione di
IdStockItem, QuantityMoved, DateMove e Direction; se uno è Nothing la validazione fallisce con
messaggio. È l'unico controllo ripetuto fuori dalla UI.
- Tipo: validation · Enforcement: BL · Confidence: VERIFIED · customer_specific: false
- Evidenza:
StockSharedObjects/Objects/StockItemHistory.vb:284-312(Validate: checkIdStockItem/QuantityMoved/DateMove/Direction = Nothing); invocato daDetailBase.Save. - Nota:
= Nothingper unDecimalequivale a= 0, quindi copre indirettamente quantità nulla ma non quantità negativa.
BR-STK-014 — Save inibito su scarico con giacenza esaurita
Statement: In fase di nuovo record, se il tipo è Scarico (dir. 3) e la giacenza è ≤ 0 su articolo non price-list, il pulsante Save è reso invisibile (impossibile registrare lo scarico).
- Tipo: permission / validation · Enforcement: UI · Confidence: VERIFIED · customer_specific: false
- Evidenza:
MovementDetailControl.ascx.vb:207-211(If rblMovementType.Items.FindByValue("3").Selected AndAlso (Me.StockQuantity <= 0 AndAlso Not Me.StockIsPriceList) Then ...Save.Visible = False),:569-577. - Flag: UI-only.
BR-STK-015 — Carico limitato alla quantità ordinata
Statement: Per il carico (dir. 2) la quantità movimentabile è limitata in UI al valore ordinato
(rnbQuantity.MaxValue = Me.OrderedQuantity); se l'ordinato è ≤ 0 il comando Save è nascosto.
- Tipo: validation · Enforcement: UI · Confidence: VERIFIED · customer_specific: false
- Evidenza:
MovementDetailControl.ascx.vb:585-594(Case "2" ... rnbQuantity.MaxValue = Me.OrderedQuantity ... If Me.OrderedQuantity <= 0 Then Save.Visible = False). - Flag: UI-only (la proc non verifica il rapporto carico/ordinato; per Ordine (dir 1) e Inventario (dir 4) il
MaxValueèDecimal.MaxValue,:608,621).
BR-STK-016 — Storicizzazione: id da trigger + sequence (con gap-recovery)
Statement: La chiave ID_ITEM_HISTORY è generata dal trigger BEFORE INSERT su STOCK_ITEM_HISTORY:
se :NEW.ID_ITEM_HISTORY IS NULL viene assegnato SEQ_STOCK_ITEM_HISTORY.NEXTVAL; se l'id è fornito
esplicitamente e supera il valore corrente della sequence, la sequence viene fatta avanzare in loop fino a
superarlo (recupero dei gap / import controllato).
- Tipo: calculation · Enforcement: DB · Confidence: VERIFIED · customer_specific: false
- Evidenza (trigger
USER_TRIGGERS, letto read-only): trigger INSERTBEFORE EACH ROWsuSTOCK_ITEM_HISTORY, corpo:IF (:NEW."ID_ITEM_HISTORY" IS NULL) THEN SELECT "SEQ_STOCK_ITEM_HISTORY".NEXTVAL INTO :NEW."ID_ITEM_HISTORY" FROM DUAL; ELSE ... WHILE (last_InsertID > last_Sequence) LOOP SELECT SEQ...NEXTVAL .... Trigger analogo esiste suSTOCK_ITEM(SEQ_STOCK_ITEM). - Flag: DB-only — logica di identity applicativa (non
IDENTITY/auto-increment nativo).
BR-STK-017 — Ritorno del nuovo id via parametro OUT
Statement: Il nuovo id del movimento è restituito leggendo SEQ_STOCK_ITEM_HISTORY.CURRVAL nel parametro
OUT PNEWINDEX subito dopo gli UPDATE di giacenza; il DAL lo converte da OracleDecimal e lo propaga come ID_DTO.
- Tipo: calculation · Enforcement: mixed (BL+DB) · Confidence: VERIFIED · customer_specific: false
- Evidenza:
STK_ADDITEMMOVE src:64-66(SELECT SEQ_STOCK_ITEM_HISTORY.CURRVAL INTO PNEWINDEX FROM DUAL); DALStockItemHistory_DAL.vb:165(param OUTPNEWINDEX),:187(Return DirectCast(indexparam.Value, Types.OracleDecimal).ToInt32). - Nota: dipendenza dalla sessione/transazione —
CURRVALè affidabile perché laNEXTVAL(nel trigger) e laCURRVALavvengono nella stessa chiamata.
BR-STK-018 — Collegamento a ticket/richiesta condizionale
Statement: Se PIDLINK IS NOT NULL la procedura inserisce una riga in STOCK_ITEM_LINK
(ID_LINK, ID_ITEM_HISTORY, APPLICATION_ID, LINKDESCRIPTION) che lega il movimento a un'entità di un'altra
applicazione (tipicamente una richiesta/ticket di RequestCenter). Se PIDLINK è nullo il collegamento non è creato.
- Tipo: state-transition · Enforcement: DB · Confidence: VERIFIED · customer_specific: false
- Evidenza:
STK_ADDITEMMOVE src:68-74(IF PIDLINK IS NOT NULL THEN INSERT INTO STOCK_ITEM_LINK ...).
BR-STK-019 — Application ID del collegamento hard-coded a "RequestCenter"
Statement: In fase di costruzione del DTO, LinkApplicationID è sempre impostato al GUID strong-name
dell'applicazione RequestCenter; il collegamento (BR-STK-018) punta quindi per costruzione al modulo Service Desk.
- Tipo: config-flag · Enforcement: mixed (UI+BL) · Confidence: VERIFIED · customer_specific: false
- Evidenza:
MovementDetailControl.ascx.vb:703(itemHis.LinkApplicationID = Cassandra.WebSharedObject.Applications.GetKeyForStrongName("RequestCenter")); mappato al paramPLINKAPPLICATIONID(RAW 16) inStockItemHistory_DAL.vb:152-158. - Flag: hard-coded id (strong-name "RequestCenter" risolto a GUID); collegamento cross-modulo.
BR-STK-020 — Vincoli NOT NULL sulla riga di movimento
Statement: La tabella STOCK_ITEM_HISTORY impone NOT NULL su ID_ITEM, QMOVE, MOVE_DATE, DIRECTION,
ID_CONTACT, MOVE_TOTAL_COST, MOVE_REAL_COST. Le colonne MOVE_TOTAL_COST/MOVE_REAL_COST hanno default
0, MOVE_DATE default SYSDATE, ISCANCELLED default 0. CAUSE, ID_ITEM_COMPANY, OLD_QUANTITY,
PRICELIST_TYPE, ID_TENANCY, PROGRESSIVE sono nullabili.
- Tipo: validation (integrità dati) · Enforcement: DB · Confidence: VERIFIED · customer_specific: false
- Evidenza (
USER_CONSTRAINTS/USER_TAB_COLUMNS, letti read-only): check NOT NULLSYS_C001714474 "ID_ITEM",SYS_C001714475 "QMOVE",SYS_C001714476 "MOVE_DATE",SYS_C001714477 "DIRECTION",SYS_C001714478 "MOVE_TOTAL_COST",SYS_C001714479 "ID_CONTACT",SYS_C001714480 "MOVE_REAL_COST"; PKPK_STOCK_ITEM_HISTORY. - Flag: DB-only —
ID_CONTACTNOT NULL implica che l'operatore/contatto sia sempre valorizzato (dal DTOContactOperator.ID_DTO,StockItemHistory_BL.vb:20).
BR-STK-021 — Integrità referenziale movimento → articolo
Statement: Ogni riga di STOCK_ITEM_HISTORY.ID_ITEM deve riferire un articolo esistente in STOCK_ITEM
(FK FK_STOCK_ITEM_HIST → PK_STOCK_ITEM); non è possibile storicizzare un movimento su un articolo inesistente.
- Tipo: validation (integrità referenziale) · Enforcement: DB · Confidence: VERIFIED · customer_specific: false
- Evidenza (
USER_CONSTRAINTS/USER_CONS_COLUMNS):FK_STOCK_ITEM_HIST (ID_ITEM) → PK_STOCK_ITEM (STOCK_ITEM).STOCK_ITEMha a sua volta FK verso stock/categoria/unità di misura (FK_STOCK_ITEM_STK_STK,FK_STOCK_ITEM_STK_CAT,FK_STOCK_ITEM_STOCK_MEAS) e NOT NULL suQSTOCK/QORDERED/QALERT/QMINIMUM. - Flag: DB-only.
BR-STK-022 — Propagazione tenancy condizionata
Statement: ID_TENANCY è valorizzato sul DTO (e propagato fino a PIDTENANCY) solo se la tenancy è
abilitata nella sessione web; altrimenti resta nullo (colonna nullabile).
- Tipo: config-flag · Enforcement: mixed (UI+BL) · Confidence: VERIFIED · customer_specific: false
- Evidenza:
MovementDetailControl.ascx.vb:715-717(If WS.IsTenantEnabled Then itemHis.ID_Tenancy = WS.CurrentTenant.ID_DTO); mappatura BLStockItemHistory_BL.vb:28; INSERTSTK_ADDITEMMOVE src:31,33(ID_TENANCY ... PIDTENANCY). - Flag: hidden flag multi-tenant.
BR-STK-023 — Cancellazione logica con storno inverso della giacenza
Statement: La cancellazione di un movimento è logica: STK_DELETEITEMHISTORY_LOGICAL imposta
ISCANCELLED = 1 e storna la giacenza in senso inverso rispetto alla direzione originale —
Order(1): QORDERED -= QMOVE (con clamp a 0 se negativo); Load(2): QSTOCK -= QMOVE e QORDERED += QMOVE
(con clamp QSTOCK a 0); Unload(3): QSTOCK += QMOVE. L'inventario (dir. 4) NON è stornato (nessun ramo ELSIF).
- Tipo: state-transition · Enforcement: DB · Confidence: VERIFIED · customer_specific: false
- Evidenza (
USER_SOURCE, read-only):STK_DELETEITEMHISTORY_LOGICAL src:9-11(UPDATE ... SET ISCANCELLED = 1),src:22-36(dir 1 + clamp),src:37-56(dir 2 + clamp),src:59-62(dir 3); nessunELSIF NDIRECTION = 4. DALStockItemHistory_DAL.vb:14-23; controllerStockItemHistoryController.vb(LogicalDelete). - Flag: magic number
ISCANCELLED = 1; gap: l'annullamento di un inventario non ripristina la giacenza precedente (vedi Open questions).
BR-STK-024 — Chiusura/decurtazione richieste su Ordine
Statement: Dopo il salvataggio di un movimento di tipo Ordine (dir. 1) con richieste collegate, la UI
decurta progressivamente le QEscape delle richieste: se QRequest ≤ residuo la richiesta è chiusa
(CloseRequest), altrimenti la quota residua è aggiornata (UpdateEscape). Sulla voce "(Tutti)" lo storno è
progressivo dall'ultima richiesta.
- Tipo: state-transition · Enforcement: BL (via controller) · Confidence: VERIFIED · customer_specific: false
- Evidenza:
MovementDetailControl.ascx.vb:326-354(If rblMovementType.SelectedValue = "1" AndAlso _arrRequestData IsNot Nothing ... CloseRequest / UpdateEscape); proc backendSTK_UPDATEREQUEST/STK_UPDATEESCAPE(viaStockItemRequestController). - Flag: side-effect cross-modulo verso RequestCenter.
BR-STK-025 — La procedura non applica guardie di business
Statement: STK_ADDITEMMOVE non contiene alcun controllo di business (nessuna verifica che la giacenza
non scenda sotto zero, che la quantità sia positiva, che l'ordinato sia sufficiente per il carico). La coerenza
è demandata interamente al livello UI (BR-STK-010/011/014/015). Una chiamata diretta al DAL/proc può portare
la giacenza in negativo.
- Tipo: validation (gap) · Enforcement: DB · Confidence: VERIFIED · customer_specific: false
- Evidenza:
STK_ADDITEMMOVE src:1-76(assenza diIF/RAISE/check oltre allo split per direzione); nessun CHECK constraint di segno suQSTOCK/QORDEREDinUSER_CONSTRAINTS(solo NOT NULL). - Flag: DB-only gap — le regole di quantità/segno sono UI-only e quindi bypassabili.
BR-STK-026 — Presupposto di unicità dello stock dell'articolo
Statement: La SELECT STOCK_STOCK.ISPRICELIST INTO TMP_ISPRICELIST (join STOCK_ITEM su ID_ITEM = PIDITEM)
presuppone che esista esattamente una riga stock per l'articolo: assenza di righe solleverebbe
NO_DATA_FOUND, righe multiple TOO_MANY_ROWS — nessuna delle due eccezioni è gestita esplicitamente.
- Tipo: validation (gap) · Enforcement: DB · Confidence: VERIFIED · customer_specific: false
- Evidenza:
STK_ADDITEMMOVE src:23-28; assenza di bloccoEXCEPTIONnella procedura (src:21-76). - Flag: DB-only gap — comportamento non definito per articolo senza stock associato.
BR-STK-027 — Update movimento: upsert del collegamento
Statement: L'aggiornamento di un movimento (STK_UPDATEITEMMOVE) riscrive i campi della riga
STOCK_ITEM_HISTORY e gestisce il collegamento con logica di upsert: se esiste già una riga in
STOCK_ITEM_LINK per il movimento la aggiorna, altrimenti la inserisce. Nota: l'update NON ricalcola la
giacenza/ordinato (nessun UPDATE su STOCK_ITEM).
- Tipo: state-transition · Enforcement: DB · Confidence: VERIFIED · customer_specific: false
- Evidenza (
USER_SOURCE, read-only):STK_UPDATEITEMMOVE src:19-21(UPDATE riga history),src:23-32(IF PLINKCOUNT > 0 THEN UPDATE ... ELSE INSERT INTO STOCK_ITEM_LINK); DALStockItemHistory_DAL.vb:254. - Flag: gap: modificare quantità/direzione di un movimento esistente non riallinea la giacenza (a differenza di insert/cancellazione logica).
BR-STK-028 — Secondo chiamante legacy di STK_ADDITEMMOVE
Statement: Oltre al path canonico (StockItemHistory_DAL.Insert, 16 parametri coerenti con la firma DB),
esiste un secondo chiamante in StockItem_DAL che invoca STK_ADDITEMMOVE con soli 5 parametri di input +
OUT, incompatibile con la firma attuale a 16 parametri: probabile codice legacy/morto o riferito a un overload
non più presente.
- Tipo: config-flag · Enforcement: BL · Confidence: INFERRED · customer_specific: false
- Evidenza:
StockDAL/OracleODP/StockItem_DAL.vb:301eStockDAL/Oracle/StockItem_DAL.vb:328(5 param IN); firma DB a 16 parametriSTK_ADDITEMMOVE src:1-16. - Flag: possibile dead code / disallineamento firma — da verificare (vedi Open questions del flusso).
Sintesi flag di rischio
- Regole UI-only (bypassabili via BL/DAL diretto): BR-STK-001, BR-STK-002, BR-STK-003, BR-STK-004, BR-STK-010, BR-STK-011, BR-STK-012, BR-STK-014, BR-STK-015. Le regole di quantità/segno/giacenza (BR-STK-010/011/014/015) non sono ripetute a livello DB (vedi BR-STK-025): la procedura può portare la giacenza in negativo se chiamata direttamente.
- Regole DB-only: BR-STK-006, BR-STK-007, BR-STK-008 (semantica direzione), BR-STK-016 (trigger+sequence), BR-STK-018, BR-STK-020, BR-STK-021, BR-STK-023, BR-STK-026, BR-STK-027. La UI non replica il ricalcolo della giacenza.
- Magic numbers / hard-coded ids: grant
6410/6420/6430/6440/6450(BR-STK-001..004); direzioni1/2/3/4replicate su UI/BL/DB senza costante condivisa lato DB (BR-STK-005);ISCANCELLED = 1(BR-STK-023); strong-name "RequestCenter" (BR-STK-019). - Hidden flags:
STOCK_STOCK.ISPRICELIST(BR-STK-007), tenancyIsTenantEnabled(BR-STK-022). - Date-sensitive: BR-STK-009 (
MOVE_DATE = SYSDATE, data movimento non modificabile). - Gap funzionali: BR-STK-023 (annullamento inventario non stornato), BR-STK-025 (nessuna guardia under-zero),
BR-STK-026 (
NO_DATA_FOUNDsu articolo senza stock), BR-STK-027 (update non riallinea la giacenza). - Customer-specific: nessuna regola risulta variante per cliente in questo dominio (il modulo Stock non usa
il motore parametrico
DynamicParametricosservato in RequestCenter).
Open questions
- BR-STK-002: in ramo di nuovo record (
MovementDetailControl.ascx.vb:210) la visibilità del Save è talvolta legata al grant6430anziché6420: verificare se intenzionale o refuso. - BR-STK-023: l'annullamento logico di un movimento di inventario (dir. 4) non ripristina la giacenza precedente — confermare se è comportamento voluto (l'inventario è una rettifica assoluta) o una lacuna.
- BR-STK-027: la modifica di quantità/direzione di un movimento esistente non ricalcola
QSTOCK/QORDERED— verificare se l'update è ammesso solo per campi non impattanti (note/costo/collegamento). - BR-STK-028: confermare se il chiamante a 5 parametri di
StockItem_DALè codice morto. - Comportamento in configurazione PROD (osservato solo ambiente TEST38).