Files
ware_house/analisi_bug_prenotazione_pickinglist.md
2026-05-22 14:25:09 +02:00

15 KiB

Analisi bug prenotazione Picking List

Obiettivo

Documentare in modo chiaro:

  • il comportamento attuale della prenotazione picking list
  • il bug osservato
  • la causa tecnica del bug
  • la correzione proposta lato stored procedure

Il documento serve come base di riferimento prima di modificare la logica SQL legacy.


Contesto operativo

L'operatore, dal backoffice:

  1. seleziona una picking list
  2. preme Prenota
  3. si aspetta che una sola picking list risulti prenotata

Il comportamento atteso del sistema è:

  • una sola picking list prenotata alla volta
  • quella picking list diventa la coda F1 sul barcode
  • le altre picking list restano non prenotate e alimentano la coda F2

Comportamento attuale

Il comportamento effettivamente osservato è questo:

  1. l'operatore seleziona una sola picking list
  2. preme Prenota
  3. risultano prenotate due picking list, non una sola

Questo comportamento è stato osservato:

  • nell'applicazione Python
  • anche nell'applicazione C#

Quindi il bug non nasce nella UI Python.


Verifica di aderenza Python / C#

Lato Python

La finestra gestione_pickinglist.py chiama:

che esegue direttamente la stored procedure SQL legacy:

  • dbo.sp_xExePackingListPallet

Lato C#

La finestra legacy FRMagViewLayout.cs chiama anch'essa:

  • dbo.sp_xExePackingListPallet

Conclusione

Python e C# usano la stessa logica DB per la prenotazione.

Se il bug compare in entrambi, il problema è nella stored procedure o nel modello dati su cui essa si basa.


Stato attuale della griglia picking list

La query summary della picking list in gestione_pickinglist.py aggrega così:

  • GROUP BY Documento, CodNazione, NAZIONE, Stato
  • MAX(IDStato) AS IDStato

Questo significa:

  • se anche una sola riga del documento ha IDStato = 1
  • l'intera picking list appare come prenotata

Questa scelta è coerente con l'idea UI di “documento prenotato”, ma diventa problematica se la stored non lavora in modo esclusivo per documento.


Stored procedure attuale

Stored coinvolta:

Logica attuale

La stored:

  1. legge da XMag_ViewPackingList
  2. estrae le Cella associate al Documento
  3. per ogni cella:
    • se IDStato = 0, la mette a 1
    • altrimenti la rimette a 0

Quindi la stored non prenota davvero una picking list come entità esclusiva.

Prenota invece:

  • le celle toccate dal documento selezionato

Conseguenza tecnica

Se due documenti condividono almeno una stessa cella:

  • la prenotazione della prima list mette IDStato = 1 sulla cella condivisa
  • anche la seconda list, leggendo quella stessa cella, eredita IDStato = 1
  • la griglia alta, usando MAX(IDStato), la mostra come prenotata

Evidenze raccolte

Query 1 - celle del sottoinsieme di documenti osservati

Scopo:

  • verificare quali celle sono coinvolte dai documenti sospetti
  • controllare rapidamente se esistono sovrapposizioni evidenti

Query:

SELECT
    Documento,
    Cella,
    COUNT(*) AS Righe
FROM dbo.XMag_ViewPackingList
WHERE Documento IN (<doc1>, <doc2>)
GROUP BY Documento, Cella
ORDER BY Cella, Documento;

Uso nel caso analizzato:

  • ha permesso di vedere, per esempio, che documenti diversi insistono sulla stessa cella 8057

Query di controllo celle condivise

Risultato:

1000    2
8057    2

Interpretazione:

  • la cella 1000 è condivisa da 2 documenti
  • la cella 8057 è condivisa da 2 documenti

Query di dettaglio documenti / celle condivise

Risultato:

135    1000    16    16
137    1000    2     1
133    8057    1     1
135    8057    1     1

Interpretazione:

  • documento 135 e documento 137 condividono la cella 1000
  • documento 133 e documento 135 condividono la cella 8057

Questa è una prova concreta del fatto che la prenotazione a livello cella può propagarsi a più documenti.

Query 2 - ricerca globale delle celle condivise

Scopo:

  • trovare quali celle sono condivise da più documenti
  • identificare i punti strutturalmente più critici del dataset

Query:

SELECT
    Cella,
    COUNT(DISTINCT Documento) AS NumDocumenti
FROM dbo.XMag_ViewPackingList
GROUP BY Cella
HAVING COUNT(DISTINCT Documento) > 1
ORDER BY NumDocumenti DESC, Cella;

Risultato osservato:

1000    2
8057    2

Interpretazione:

  • la cella 1000 è condivisa da 2 documenti
  • la cella 8057 è condivisa da 2 documenti

Query 3 - dettaglio completo delle collisioni sulle celle condivise

Scopo:

  • capire esattamente quali documenti condividono le celle critiche
  • vedere quante righe e quanti pallet distinti insistono su quelle celle

Query:

SELECT
    Documento,
    Cella,
    COUNT(*) AS Righe,
    COUNT(DISTINCT Pallet) AS Pallet
FROM dbo.XMag_ViewPackingList
WHERE Cella IN (1000, 8057)
GROUP BY Documento, Cella
ORDER BY Cella, Documento;

Risultato osservato:

135    1000    16    16
137    1000    2     1
133    8057    1     1
135    8057    1     1

Interpretazione:

  • documento 135 e documento 137 condividono la cella 1000
  • documento 133 e documento 135 condividono la cella 8057

Queste tre query, lette insieme, sono quelle che hanno permesso di evidenziare in modo oggettivo il bug.


Ruolo delle celle convenzionali

Dalle verifiche fatte con il magazziniere:

  • 1000 = 5E1.1
  • 9999 = 7G.1.1

La cella 1000 è una locazione convenzionale delle UDC non scaffalate.

Questo rende il problema ancora più frequente, perché:

  • più documenti possono convivere sulla stessa locazione convenzionale 1000
  • quindi la prenotazione per cella tende naturalmente a contaminare più picking list

Diagnosi finale del bug

Il bug è dovuto alla combinazione di due scelte:

  1. la stored sp_xExePackingListPallet lavora a livello cella
  2. la UI mostra la prenotazione a livello documento

Quando esistono celle condivise tra documenti:

  • la prenotazione di un documento produce IDStato = 1 anche su righe lette da altri documenti
  • quindi più picking list risultano prenotate

Forma sintetica del bug

Il sistema non sta prenotando una singola picking list in modo esclusivo.
Sta prenotando le celle associate a quel documento, e la UI deduce da lì lo stato della picking list.


Correzione proposta

Obiettivo della correzione

Allineare il comportamento del sistema alla regola di business desiderata:

una sola picking list prenotata per volta

Questo implica che il comando Prenota deve produrre uno stato esclusivo a livello documento.


Principio della nuova logica

La stored non deve più comportarsi come un semplice toggle delle celle del documento selezionato.

Deve invece:

  1. rimuovere la prenotazione da tutte le altre picking list
  2. applicare la prenotazione solo al documento selezionato

In altre parole:

  • prima si resetta lo stato prenotato degli altri documenti
  • poi si prenota il documento scelto

Comportamento desiderato dopo la correzione

Caso Prenota

Quando l'operatore prenota il documento D:

  1. se il documento D è già prenotato, non succede nulla
  2. se il documento D non è prenotato:
    • tutte le celle prenotate appartenenti ad altri documenti vengono riportate a IDStato = 0
    • tutte le celle del documento D vengono portate a IDStato = 1
    • il log della picking list resta aggiornato come oggi

Effetto atteso:

  • solo il documento D risulta prenotato nella griglia
  • solo il documento D alimenta la coda F1
  • tutte le altre picking list alimentano F2
  • premere Prenota più volte sulla stessa picking list già prenotata non deve produrre toggle né effetti collaterali

Caso S-prenota

Quando l'operatore s-prenota il documento D:

  1. se il documento D non è prenotato, non succede nulla
  2. se il documento D è prenotato:
    • le celle del documento D vengono riportate a IDStato = 0
    • nessun altro documento viene automaticamente prenotato

Effetto atteso:

  • nessuna picking list resta prenotata, salvo una nuova prenotazione esplicita
  • premere S-prenota più volte sulla stessa picking list già non prenotata non deve produrre toggle né effetti collaterali

Strategia tecnica consigliata

Approccio minimo e prudente

La correzione più sicura, restando vicini al legacy, è mantenere una stored unica ma con una semantica esplicita guidata da un parametro azione.

Proposta:

  • stored unica con parametro @Azione
    • P = Prenota
    • S = S-prenota

Logica:

  1. determinare se il documento selezionato è già prenotato o no
  2. se @Azione = 'P'
    • se il documento è già prenotato: non fare nulla
    • altrimenti:
      • azzerare IDStato = 1 sulle celle coinvolte da altri documenti prenotati
      • impostare IDStato = 1 sulle celle del documento selezionato
  3. se @Azione = 'S'
    • se il documento non è prenotato: non fare nulla
    • altrimenti:
      • riportare a 0 solo le celle del documento selezionato

Questa logica mantiene:

  • la semantica esistente a livello cella
  • ma aggiunge l'esclusività a livello documento
  • e mantiene pulita la semantica distinta dei pulsanti Prenota e S-prenota

Vantaggi

  • modifica contenuta
  • comportamento coerente con l'operatività reale
  • nessuna necessità immediata di cambiare UI Python o C#
  • comportamento idempotente dei pulsanti

Rischi da considerare

1. Celle condivise tra documenti

La presenza di celle condivise resta un'anomalia logica del dominio.

Anche con la correzione proposta:

  • se una cella appartiene contemporaneamente a più documenti nella vista
  • la semantica “quale documento possiede davvero la prenotazione della cella” resta concettualmente debole

Tuttavia la correzione proposta risolve il bug visibile di doppia picking list prenotata.

2. Effetti sul flusso barcode

Il barcode usa IDStato = 1 per la coda F1.

Quindi la nuova esclusività deve essere verificata attentamente su:

  • proposta UDC in F1
  • proposta UDC in F2
  • avanzamento dopo prelievo
  • ri-prenotazione automatica residua via sp_ControllaPrenotazionePackingListPalletNew

3. Interazione con LogPackingList

La stored attuale aggiorna il log del documento.

Questa parte va mantenuta, perché il barcode e la logica di ri-prenotazione residua si appoggiano su quel log.


Criteri di accettazione della correzione

La correzione sarà considerata valida se, dopo Prenota:

  1. una sola picking list risulta prenotata in griglia
  2. F1 propone solo UDC del documento prenotato
  3. F2 propone UDC delle altre picking list
  4. il problema di doppia prenotazione non si ripresenta più nemmeno in presenza di celle condivise come 1000 o 8057

E dopo S-prenota:

  1. il documento selezionato torna non prenotato
  2. F1 non propone più quella picking list

Decisione consigliata

La correzione lato stored procedure è consigliata e necessaria.

Motivo:

  • il bug è reale
  • il bug è condiviso da Python e C#
  • il bug nasce dalla logica DB attuale
  • la prenotazione esclusiva è coerente con il modello operativo del magazzino

Stored procedure proposta

Di seguito una proposta documentale di stored unica con parametro P / S.

CREATE OR ALTER PROCEDURE [dbo].[sp_xExePackingListPallet]
    @IDOperatore int,
    @Documento varchar(8),
    @Azione char(1),       -- 'P' = Prenota, 'S' = S-prenota
    @RC int OUTPUT
AS
BEGIN
    SET NOCOUNT ON;
    SET @RC = 0;

    DECLARE @Nominativo varchar(50);
    DECLARE @DocumentoPrenotato bit = 0;
    DECLARE @Description varchar(255) = '';
    DECLARE @Message varchar(255) = '';
    DECLARE @IDResult int = 0;

    SELECT @Nominativo = [Login]
    FROM dbo.Operatori
    WHERE ID = @IDOperatore;

    IF @Azione NOT IN ('P', 'S')
    BEGIN
        SET @RC = -10;
        RETURN;
    END;

    IF OBJECT_ID('tempdb..#TargetCelle') IS NOT NULL DROP TABLE #TargetCelle;
    CREATE TABLE #TargetCelle (
        IDCella int PRIMARY KEY
    );

    INSERT INTO #TargetCelle (IDCella)
    SELECT DISTINCT Cella
    FROM dbo.XMag_ViewPackingList
    WHERE Documento = @Documento
      AND Cella IS NOT NULL;

    IF NOT EXISTS (SELECT 1 FROM #TargetCelle)
    BEGIN
        SET @RC = -20;
        RETURN;
    END;

    IF EXISTS (
        SELECT 1
        FROM dbo.XMag_ViewPackingList
        WHERE Documento = @Documento
          AND ISNULL(IDStato, 0) = 1
    )
    BEGIN
        SET @DocumentoPrenotato = 1;
    END;

    IF @Azione = 'P'
    BEGIN
        -- Gia' prenotata: no-op
        IF @DocumentoPrenotato = 1
            RETURN;

        -- Azzera prenotazioni di altri documenti
        UPDATE c
        SET c.IDStato = 0,
            c.ModUtente = @Nominativo,
            c.ModDataOra = GETDATE()
        FROM dbo.Celle c
        WHERE c.ID IN (
            SELECT DISTINCT Cella
            FROM dbo.XMag_ViewPackingList
            WHERE ISNULL(IDStato, 0) = 1
              AND Documento <> @Documento
              AND Cella IS NOT NULL
        );

        -- Prenota solo il documento target
        UPDATE c
        SET c.IDStato = 1,
            c.ModUtente = @Nominativo,
            c.ModDataOra = GETDATE()
        FROM dbo.Celle c
        INNER JOIN #TargetCelle t ON t.IDCella = c.ID;

        SELECT TOP 1 @Description = NAZIONE
        FROM dbo.XMag_ViewPackingList
        WHERE Documento = @Documento;

        EXEC dbo.sp_LogPackingList
             @ID = 0,
             @Code = @Documento,
             @Description = @Description,
             @Message = @Message OUTPUT,
             @IDResult = @IDResult OUTPUT;

        RETURN;
    END;

    IF @Azione = 'S'
    BEGIN
        -- Gia' non prenotata: no-op
        IF @DocumentoPrenotato = 0
            RETURN;

        -- S-prenota solo il documento target
        UPDATE c
        SET c.IDStato = 0,
            c.ModUtente = @Nominativo,
            c.ModDataOra = GETDATE()
        FROM dbo.Celle c
        INNER JOIN #TargetCelle t ON t.IDCella = c.ID;

        RETURN;
    END;
END;

Note sulla stored proposta

  • Non esiste più il toggle implicito.
  • Prenota è idempotente:
    • se la list è già prenotata, non cambia nulla.
  • S-prenota è idempotente:
    • se la list è già non prenotata, non cambia nulla.
  • L'esclusività viene garantita solo nel ramo P.
  • La logica continua a usare IDStato sulle celle, così il barcode legacy può continuare a usare F1 / F2 con la semantica attuale.

Prossimo passo

Dopo approvazione di questo documento:

  1. modificare sp_xExePackingListPallet o introdurre una nuova versione compatibile
  2. testare il risultato con almeno 3 picking list attive
  3. verificare il comportamento sul backoffice
  4. verificare il comportamento sul barcode (F1 / F2)