diff --git a/analisi_bug_prenotazione_pickinglist.md b/analisi_bug_prenotazione_pickinglist.md index defa8bc..33cf7ff 100644 --- a/analisi_bug_prenotazione_pickinglist.md +++ b/analisi_bug_prenotazione_pickinglist.md @@ -586,3 +586,236 @@ Dopo approvazione di questo documento: 2. testare il risultato con almeno 3 picking list attive 3. verificare il comportamento sul backoffice 4. verificare il comportamento sul barcode (`F1` / `F2`) + +--- + +## Revisione dopo test reale su documenti 133 / 135 / 137 + +Dopo avere applicato la prima correzione proposta solo sulla stored, il bug **resta presente**: + +- prenotando il documento `133` +- la picking list `135` continua a risultare prenotata + +Questo comportamento è coerente con le evidenze raccolte: + +- `133` e `135` condividono la cella `8057` +- `135` e `137` condividono la cella `1000` + +### Conclusione aggiornata + +La correzione che agisce **solo** su `Celle.IDStato` non è sufficiente. + +Il motivo è strutturale: + +1. la stored può anche rendere idempotente `Prenota` / `S-prenota` +2. ma se la UI e il barcode continuano a dedurre lo stato della prenotazione da `Celle.IDStato` +3. una cella condivisa continuerà a far risultare prenotati anche documenti diversi + +Quindi il modello corretto deve spostare la **sorgente di verità** della prenotazione: + +- **da:** stato della cella +- **a:** documento prenotato attivo + +### Nuova architettura proposta + +La soluzione corretta è questa: + +1. introdurre una piccola tabella di stato, ad esempio `dbo.PickingListReservation` +2. salvare lì il **documento attualmente prenotato** +3. fare in modo che `XMag_ViewPackingList.IDStato` non legga più la prenotazione da `Celle.IDStato` +4. calcolare invece `IDStato` così: + - `1` se `Documento = documento prenotato attivo` + - `0` negli altri casi + +In questo modo: + +- il backoffice mostra prenotata una sola picking list +- il barcode continua a usare `F1` = `IDStato 1` e `F2` = `IDStato 0` +- le celle condivise `1000` / `8057` non contaminano più altri documenti + +### Ruolo residuo di `Celle.IDStato` + +`Celle.IDStato` può restare utile come supporto: + +- per visualizzazioni legacy +- per evidenziare le celle del documento attivo +- per non rompere altre parti del sistema che si aspettano quel flag + +Ma non deve più essere la **fonte primaria** della prenotazione documento. + +### Stored definitiva da realizzare + +La stored `sp_xExePackingListPallet` deve quindi: + +1. mantenere il parametro `@Azione = 'P' | 'S'` +2. aggiornare la tabella `PickingListReservation` +3. opzionalmente riallineare anche `Celle.IDStato` +4. mantenere il log in `LogPackingList` + +Semantica definitiva: + +- `Prenota` + - se il documento è già quello attivo: nessun effetto + - altrimenti: + - il documento diventa l’unico prenotato attivo + - tutte le altre picking list risultano non prenotate + +- `S-prenota` + - se il documento non è quello attivo: nessun effetto + - se il documento è quello attivo: + - la prenotazione viene rimossa + - nessun altro documento viene acceso automaticamente + +### Nota importante + +Questa revisione **supera** la proposta precedente basata soltanto sul reset/set di `Celle.IDStato`. + +La patch SQL definitiva deve quindi includere: + +1. creazione della tabella `PickingListReservation` +2. `CREATE OR ALTER` della stored `sp_xExePackingListPallet` +3. `CREATE OR ALTER` della vista `XMag_ViewPackingList` + +Questo è il primo assetto realmente coerente con il requisito di business: + +> una sola picking list prenotata alla volta, anche in presenza di celle condivise. + +--- + +## Evoluzione proposta: usare `ViewPackingListRestante` anche per la lista alta + +### Situazione attuale + +Nel legacy C# e nel Python attuale la distinzione è questa: + +- la **griglia alta** legge la testata aggregata da `XMag_ViewPackingList` +- la **griglia bassa** legge il dettaglio da `ViewPackingListRestante` + +La vista `ViewPackingListRestante` è definita come: + +```sql +SELECT ... +FROM XMag_ViewPackingList +WHERE Cella <> 9999 +``` + +Quindi: + +- `XMag_ViewPackingList` contiene anche le UDC già finite nella locazione convenzionale `9999 = 7G.1.1` +- `ViewPackingListRestante` mostra invece solo le UDC ancora residue + +### Conseguenza pratica + +All’istante `t = 0` della prenotazione: + +- se la picking list non è ancora stata lavorata, `XMag_ViewPackingList` e `ViewPackingListRestante` mostrano di fatto lo stesso insieme di UDC +- le UDC non scaffalate **restano presenti** in entrambe, perché stanno nella cella convenzionale `1000 = 5E1.1` + +Dopo che il magazziniere ha iniziato i prelievi: + +- le UDC già spedite/prelevate finiscono in `9999 = 7G.1.1` +- quindi spariscono da `ViewPackingListRestante` +- ma restano ancora visibili in `XMag_ViewPackingList` + +### Limite del comportamento attuale + +Con la summary alta basata su `XMag_ViewPackingList`: + +- il comando `Ricarica` può continuare a mostrare picking list già lavorate in parte +- una picking list completamente esaurita può restare visibile nella lista alta +- il contenuto della lista alta non coincide più con il “residuo operativo reale” + +In altre parole: + +- la griglia bassa mostra il residuo +- la griglia alta mostra ancora lo storico completo + +### Modifica proposta + +La proposta è: + +- usare `ViewPackingListRestante` anche come sorgente della **lista alta aggregata** + +quindi sostituire, nella query summary di [gestione_pickinglist.py](C:/devel/python/ware_house/gestione_pickinglist.py): + +- `FROM dbo.XMag_ViewPackingList` + +con: + +- `FROM dbo.ViewPackingListRestante` + +mantenendo invariati: + +- `GROUP BY Documento, CodNazione, NAZIONE, Stato` +- `COUNT(DISTINCT Pallet)` +- `COUNT(DISTINCT Lotto)` +- `COUNT(DISTINCT Articolo)` +- `SUM(Qta)` +- `MIN(Ordinamento)` +- `MAX(IDStato)` + +### Effetti attesi + +Con questa modifica: + +1. all’inizio una picking list nuova continua a comparire normalmente +2. durante il lavoro il pulsante `Ricarica` mostra solo ciò che resta davvero da prelevare +3. le UDC già finite in `7G.1.1` non inquinano più i conteggi della testata +4. una picking list completamente esaurita esce naturalmente dalla lista visibile +5. le UDC non scaffalate continuano a comparire correttamente, perché stanno in `1000 = 5E1.1`, non in `9999` + +### Chiusura automatica della prenotazione + +Per mantenere coerenti: + +- griglia alta +- stato della prenotazione +- comportamento del barcode `F1` + +la prenotazione attiva non deve soltanto sparire dalla griglia: deve essere anche **azzerata logicamente** quando il residuo arriva a zero. + +La patch SQL definitiva quindi deve fare anche questo: + +- dopo ogni movimento, controllare se il documento prenotato ha ancora righe in `ViewPackingListRestante` +- se non ne ha più: + - la prenotazione attiva va portata a `NULL` / `0` + - le eventuali celle residue marcate `IDStato = 1` vanno riportate a `0` + +Effetto operativo: + +- plist piena all’inizio +- plist via via ridotta durante i prelievi +- plist che, all’ultima UDC, sparisce dalla griglia alta +- e nello stesso momento cessa anche di essere la coda `F1` + +### Impatto funzionale + +Questa modifica: + +- **non è** una replica pedissequa del legacy C# +- ma è un miglioramento operativo coerente con il comportamento atteso dal magazzino + +In particolare rende la lista alta: + +- più coerente con il significato di “picking list ancora da esaurire” +- allineata al contenuto della griglia bassa +- più utile nei refresh successivi durante il lavoro reale + +### Impatto tecnico + +Il cambiamento richiesto è piccolo lato Python: + +- aggiornare la query summary in [gestione_pickinglist.py](C:/devel/python/ware_house/gestione_pickinglist.py) + +Non richiede, in prima battuta: + +- modifiche alla stored di prenotazione +- modifiche al barcode +- modifiche a `ViewPackingListRestante` + +### Decisione progettuale + +Questa modifica va considerata come una scelta esplicita di evoluzione: + +- **se vogliamo restare 1:1 col C#**, la lista alta resta su `XMag_ViewPackingList` +- **se vogliamo rendere il modulo più aderente al residuo operativo reale**, conviene passare a `ViewPackingListRestante` diff --git a/gestione_pickinglist.py b/gestione_pickinglist.py index a546d4c..1ddbe97 100644 --- a/gestione_pickinglist.py +++ b/gestione_pickinglist.py @@ -215,7 +215,7 @@ SELECT MAX(Cella) AS Cella, MIN(Ordinamento) AS Ordinamento, MAX(IDStato) AS IDStato -FROM dbo.XMag_ViewPackingList +FROM dbo.ViewPackingListRestante GROUP BY Documento, CodNazione, NAZIONE, Stato ORDER BY MIN(Ordinamento), Documento, NAZIONE, Stato; """ diff --git a/login_window.py b/login_window.py index 1f0c946..4b8f87a 100644 --- a/login_window.py +++ b/login_window.py @@ -65,7 +65,7 @@ class LoginWindow(tk.Toplevel): self._busy = InlineBusyOverlay(self, self._theme) self.title(loc_text("login.msg.title", catalog=self._locale_catalog, default="Login")) - self.geometry("170x145+0+0" if self.compact else str(theme_value(self._theme, "window_geometry", "420x250"))) + self.geometry("170x145+0+0" if self.compact else str(theme_value(self._theme, "window_geometry", "235x185+0+0"))) self.resizable(False, False) try: if parent is not None and parent.winfo_viewable(): @@ -88,7 +88,7 @@ class LoginWindow(tk.Toplevel): def _build_ui(self) -> None: """Build the compact operator login form.""" - body = ttk.Frame(self, padding=8 if self.compact else 12) + body = ttk.Frame(self, padding=8 if self.compact else 10) body.pack(fill="both", expand=True) body.columnconfigure(1, weight=1) @@ -97,8 +97,8 @@ class LoginWindow(tk.Toplevel): ttk.Label( body, text=loc_text("login.heading", catalog=self._locale_catalog, default="Autenticazione operatore"), - font=("Segoe UI", 11, "bold"), - ).grid(row=0, column=0, columnspan=2, sticky="w", pady=(4, 14)) + font=("Segoe UI", 9, "bold"), + ).grid(row=0, column=0, columnspan=2, sticky="w", pady=(0, 8)) row_offset = 1 ttk.Label(body, text="User:").grid( @@ -127,36 +127,23 @@ class LoginWindow(tk.Toplevel): ) self._login_button.grid(row=0, column=0, sticky="ew") else: - self.info_label = ttk.Label( - body, - text=loc_text( - "login.info", - catalog=self._locale_catalog, - default="Per ora tutti gli operatori autenticati possono usare tutte le funzioni.", - ), - justify="left", - wraplength=320, - ) - self.info_label.grid(row=3, column=0, columnspan=2, sticky="ew", pady=(10, 8)) - self.status_label = ttk.Label(body, textvariable=self._status_var, foreground="#555555") - self.status_label.grid(row=4, column=0, columnspan=2, sticky="w", pady=(2, 2)) + self.status_label.grid(row=3, column=0, columnspan=2, sticky="w", pady=(2, 2)) actions = ttk.Frame(body) - actions.grid(row=5, column=0, columnspan=2, sticky="ew", pady=(6, 0)) - actions.columnconfigure(0, weight=1) + actions.grid(row=4, column=0, columnspan=2, sticky="w", pady=(6, 0)) self._cancel_button = ttk.Button( actions, text=loc_text("login.button.cancel", catalog=self._locale_catalog, default="Annulla"), command=self._on_cancel, ) - self._cancel_button.grid(row=0, column=1, padx=(0, 8), pady=8) + self._cancel_button.grid(row=1, column=0, sticky="ew", pady=(4, 0)) self._login_button = ttk.Button( actions, - text=loc_text("login.button.submit", catalog=self._locale_catalog, default="Accedi"), + text=loc_text("login.button.submit", catalog=self._locale_catalog, default="OK"), command=self._on_login, ) - self._login_button.grid(row=0, column=2, pady=8) + self._login_button.grid(row=0, column=0, sticky="ew") self.bind("", lambda _e: self._on_login()) self.bind("", lambda _e: self._on_cancel()) diff --git a/patch_sp_xExePackingListPallet.sql b/patch_sp_xExePackingListPallet.sql index 80c12f9..93d6e20 100644 --- a/patch_sp_xExePackingListPallet.sql +++ b/patch_sp_xExePackingListPallet.sql @@ -3,6 +3,40 @@ GO SET QUOTED_IDENTIFIER ON GO +/* ============================================================ + Patch prenotazione Picking List a livello documento + + Obiettivo: + - rendere esclusiva la prenotazione di una sola picking list + - mantenere la stored con lo stesso nome: + dbo.sp_xExePackingListPallet + - introdurre una sorgente di verità per documento prenotato: + dbo.PickingListReservation + - far leggere XMag_ViewPackingList.IDStato da tale stato + e non più soltanto da Celle.IDStato + ============================================================ */ + +IF OBJECT_ID(N'dbo.PickingListReservation', N'U') IS NULL +BEGIN + CREATE TABLE [dbo].[PickingListReservation]( + [ID] [tinyint] NOT NULL, + [Documento] [varchar](8) NULL, + [IDOperatore] [int] NULL, + [ModUtente] [varchar](50) NULL, + [ModDataOra] [datetime] NULL, + CONSTRAINT [PK_PickingListReservation] PRIMARY KEY CLUSTERED ([ID] ASC), + CONSTRAINT [CK_PickingListReservation_Singleton] CHECK ([ID] = 1) + ) ON [PRIMARY]; +END +GO + +IF NOT EXISTS (SELECT 1 FROM dbo.PickingListReservation WHERE ID = 1) +BEGIN + INSERT INTO dbo.PickingListReservation (ID, Documento, IDOperatore, ModUtente, ModDataOra) + VALUES (1, NULL, NULL, NULL, GETDATE()); +END +GO + CREATE OR ALTER PROCEDURE [dbo].[sp_xExePackingListPallet] @IDOperatore int, @Documento varchar(8), @@ -13,24 +47,46 @@ BEGIN SET NOCOUNT ON; SET @RC = 0; - DECLARE @Nominativo varchar(50); - DECLARE @DocumentoPrenotato bit = 0; + DECLARE @Nominativo varchar(50) = ''; + DECLARE @DocumentoAttivo varchar(8) = NULL; DECLARE @Description varchar(255) = ''; DECLARE @Message varchar(255) = ''; DECLARE @IDResult int = 0; DECLARE @ID int = 0; - DECLARE @Code varchar(64) = @Documento; SELECT @Nominativo = [Login] FROM dbo.Operatori WHERE ID = @IDOperatore; + IF @Nominativo IS NULL + SET @Nominativo = 'SYSTEM'; + IF @Azione NOT IN ('P', 'S') BEGIN SET @RC = -10; RETURN; END; + IF NOT EXISTS ( + SELECT 1 + FROM dbo.XMag_ViewPackingList + WHERE CAST(Documento AS varchar(8)) = @Documento + ) + BEGIN + SET @RC = -20; + RETURN; + END; + + IF NOT EXISTS (SELECT 1 FROM dbo.PickingListReservation WHERE ID = 1) + BEGIN + INSERT INTO dbo.PickingListReservation (ID, Documento, IDOperatore, ModUtente, ModDataOra) + VALUES (1, NULL, NULL, NULL, GETDATE()); + END; + + SELECT @DocumentoAttivo = NULLIF(LTRIM(RTRIM(Documento)), '') + FROM dbo.PickingListReservation + WHERE ID = 1; + DECLARE @TargetCelle TABLE ( IDCella int PRIMARY KEY ); @@ -38,46 +94,21 @@ BEGIN INSERT INTO @TargetCelle (IDCella) SELECT DISTINCT Cella FROM dbo.XMag_ViewPackingList - WHERE Documento = @Documento + WHERE CAST(Documento AS varchar(8)) = @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: nessuna variazione - IF @DocumentoPrenotato = 1 + IF @DocumentoAttivo = @Documento RETURN; - -- Azzera la prenotazione di tutti gli 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 - ); + WHERE ISNULL(c.IDStato, 0) <> 0; - -- Prenota soltanto le celle del documento target UPDATE c SET c.IDStato = 1, c.ModUtente = @Nominativo, @@ -85,27 +116,32 @@ BEGIN FROM dbo.Celle c INNER JOIN @TargetCelle t ON t.IDCella = c.ID; + UPDATE dbo.PickingListReservation + SET Documento = @Documento, + IDOperatore = @IDOperatore, + ModUtente = @Nominativo, + ModDataOra = GETDATE() + WHERE ID = 1; + SELECT TOP 1 @Description = NAZIONE FROM dbo.XMag_ViewPackingList - WHERE Documento = @Documento; + WHERE CAST(Documento AS varchar(8)) = @Documento; - EXECUTE @RC = [dbo].[sp_LogPackingList] - @ID - ,@Code - ,@Description - ,@Message OUTPUT - ,@IDResult OUTPUT; + EXEC dbo.sp_LogPackingList + @ID = @ID, + @Code = @Documento, + @Description = @Description, + @Message = @Message OUTPUT, + @IDResult = @IDResult OUTPUT; RETURN; END; IF @Azione = 'S' BEGIN - -- Gia' non prenotata: nessuna variazione - IF @DocumentoPrenotato = 0 + IF ISNULL(@DocumentoAttivo, '') <> @Documento RETURN; - -- Togli la prenotazione soltanto al documento target UPDATE c SET c.IDStato = 0, c.ModUtente = @Nominativo, @@ -113,7 +149,195 @@ BEGIN FROM dbo.Celle c INNER JOIN @TargetCelle t ON t.IDCella = c.ID; + UPDATE dbo.PickingListReservation + SET Documento = NULL, + IDOperatore = @IDOperatore, + ModUtente = @Nominativo, + ModDataOra = GETDATE() + WHERE ID = 1; + RETURN; END; END; GO + +CREATE OR ALTER VIEW [dbo].[XMag_ViewPackingList] +AS +WITH Base AS ( + SELECT + dbo.vPreparaPackingList.UDC AS Pallet, + dbo.vPreparaPackingList.NUMLOT AS Lotto, + dbo.vPreparaPackingList.CODICE AS Articolo, + dbo.vPreparaPackingList.DESCR AS Descrizione, + dbo.vPreparaPackingList.Qta, + dbo.vPreparaPackingList.NUMDOC AS Documento, + dbo.vPreparaPackingList.Expr1 AS CodNazione, + dbo.vPreparaPackingList.NAZIONE, + CASE + WHEN Expr1 = 'DE' THEN 10 + WHEN Expr1 = 'TH' THEN CASE WHEN SUBSTRING(dbo.vPreparaPackingList.DESCRDEST, 1, 2) = 'NA' THEN 11 ELSE 13 END + WHEN Expr1 = 'MEX' THEN 12 + ELSE 4 + END AS Stato, + ISNULL(dbo.XMag_GiacenzaPallet.NumeroPallet, 0) AS PalletCella, + ISNULL(dbo.XMag_GiacenzaPallet.IDMagazzino, 1) AS Magazzino, + ISNULL(dbo.XMag_GiacenzaPallet.IDArea, 5) AS Area, + ISNULL(dbo.XMag_GiacenzaPallet.IDCella, 1000) AS Cella, + ISNULL(dbo.Celle.Ordinamento, 99999) AS Ordinamento, + ISNULL(dbo.Celle.Corsia + ' - ' + dbo.Celle.Colonna + ' - ' + dbo.Celle.Fila, 'Non scaff.') AS Ubicazione, + SUBSTRING(dbo.vPreparaPackingList.DESCRDEST, 1, 2) AS DEST + FROM dbo.Celle + INNER JOIN dbo.XMag_GiacenzaPallet + ON dbo.Celle.ID = dbo.XMag_GiacenzaPallet.IDCella + RIGHT OUTER JOIN dbo.vPreparaPackingList + ON dbo.XMag_GiacenzaPallet.BarcodePallet COLLATE SQL_Latin1_General_CP1_CI_AS = dbo.vPreparaPackingList.UDC + GROUP BY + dbo.vPreparaPackingList.Expr1, + dbo.vPreparaPackingList.NAZIONE, + dbo.vPreparaPackingList.UDC, + dbo.vPreparaPackingList.NUMDOC, + dbo.vPreparaPackingList.NUMLOT, + dbo.vPreparaPackingList.CODICE, + dbo.vPreparaPackingList.DESCR, + dbo.vPreparaPackingList.Qta, + dbo.XMag_GiacenzaPallet.NumeroPallet, + dbo.XMag_GiacenzaPallet.IDMagazzino, + dbo.XMag_GiacenzaPallet.IDArea, + dbo.XMag_GiacenzaPallet.IDCella, + dbo.Celle.Ordinamento, + dbo.Celle.Corsia, + dbo.Celle.Colonna, + dbo.Celle.Fila, + dbo.vPreparaPackingList.DESCRDEST +) +SELECT TOP 10000 + Base.Pallet, + Base.Lotto, + Base.Articolo, + Base.Descrizione, + Base.Qta, + Base.Documento, + Base.CodNazione, + Base.NAZIONE, + Base.Stato, + Base.PalletCella, + Base.Magazzino, + Base.Area, + Base.Cella, + Base.Ordinamento, + Base.Ubicazione, + Base.DEST, + CASE + WHEN pr.Documento IS NOT NULL + AND pr.Documento = CAST(Base.Documento AS varchar(8)) + THEN 1 + ELSE 0 + END AS IDStato +FROM Base +LEFT JOIN dbo.PickingListReservation pr + ON pr.ID = 1 + AND NULLIF(LTRIM(RTRIM(pr.Documento)), '') IS NOT NULL +ORDER BY + CASE + WHEN pr.Documento IS NOT NULL + AND pr.Documento = CAST(Base.Documento AS varchar(8)) + THEN 1 + ELSE 0 + END DESC, + Base.Documento, + Base.Ordinamento; +GO + +CREATE OR ALTER PROCEDURE [dbo].[sp_xExePackingListPalletPrenota] + @IDOperatore int, + @Documento varchar(8), + @RC int OUTPUT +AS +BEGIN + SET NOCOUNT ON; + SET @RC = 0; + + DECLARE @Nominativo varchar(50) = ''; + + SELECT @Nominativo = LOGIN + FROM dbo.Operatori + WHERE ID = @IDOperatore; + + IF @Nominativo IS NULL + SET @Nominativo = 'SYSTEM'; + + UPDATE c + SET c.IDStato = 1, + c.ModUtente = @Nominativo, + c.ModDataOra = GETDATE() + FROM dbo.Celle c + WHERE c.ID IN ( + SELECT DISTINCT Cella + FROM dbo.ViewPackingListRestante + WHERE CAST(Documento AS varchar(8)) = @Documento + AND Cella IS NOT NULL + ); +END; +GO + +CREATE OR ALTER PROCEDURE [dbo].[sp_ControllaPrenotazionePackingListPalletNew] +AS +BEGIN + SET NOCOUNT ON; + + DECLARE @Documento varchar(8) = NULL; + DECLARE @IDOperatore int = 0; + DECLARE @Nominativo varchar(50) = 'SYSTEM'; + DECLARE @RC int = 0; + + SELECT + @Documento = NULLIF(LTRIM(RTRIM(Documento)), ''), + @IDOperatore = ISNULL(IDOperatore, 0), + @Nominativo = ISNULL(NULLIF(LTRIM(RTRIM(ModUtente)), ''), 'SYSTEM') + FROM dbo.PickingListReservation + WHERE ID = 1; + + IF ISNULL(@Documento, '') = '' + RETURN; + + IF NOT EXISTS ( + SELECT 1 + FROM dbo.ViewPackingListRestante + WHERE CAST(Documento AS varchar(8)) = @Documento + ) + BEGIN + UPDATE dbo.Celle + SET IDStato = 0, + ModUtente = @Nominativo, + ModDataOra = GETDATE() + WHERE ISNULL(IDStato, 0) <> 0; + + UPDATE dbo.PickingListReservation + SET Documento = NULL, + IDOperatore = @IDOperatore, + ModUtente = @Nominativo, + ModDataOra = GETDATE() + WHERE ID = 1; + + RETURN; + END; + + UPDATE dbo.Celle + SET IDStato = 0, + ModUtente = @Nominativo, + ModDataOra = GETDATE() + WHERE ISNULL(IDStato, 0) <> 0; + + IF @IDOperatore <= 0 + BEGIN + SELECT TOP 1 @IDOperatore = ID + FROM dbo.Operatori + WHERE LOGIN = @Nominativo; + END; + + EXEC dbo.sp_xExePackingListPalletPrenota + @IDOperatore = @IDOperatore, + @Documento = @Documento, + @RC = @RC OUTPUT; +END; +GO