diff --git a/apply_plist_reservation_patch.sql b/apply_plist_reservation_patch.sql new file mode 100644 index 0000000..51727b2 --- /dev/null +++ b/apply_plist_reservation_patch.sql @@ -0,0 +1,408 @@ +SET ANSI_NULLS ON +GO +SET QUOTED_IDENTIFIER ON +GO + +/* +Patch deploy per i test sul campo: +- salva le definizioni correnti degli oggetti legacy coinvolti +- applica la nuova logica di prenotazione plist a livello documento + +Oggetti salvati: +- dbo.sp_xExePackingListPallet +- dbo.XMag_ViewPackingList +- dbo.sp_xExePackingListPalletPrenota +- dbo.sp_ControllaPrenotazionePackingListPalletNew + +Oggetti creati/usati dalla patch: +- dbo.WarehouseObjectBackup +- dbo.PickingListReservation + +Nota: +- la tabella dbo.PickingListReservation può restare anche dopo il rollback + perché il vecchio codice non la usa. +*/ + +DECLARE @BackupTag varchar(64) = 'plist_reservation_fix_alpha2'; + +IF OBJECT_ID(N'dbo.WarehouseObjectBackup', N'U') IS NULL +BEGIN + CREATE TABLE dbo.WarehouseObjectBackup ( + BackupTag varchar(64) NOT NULL, + ObjectName sysname NOT NULL, + ObjectType varchar(16) NOT NULL, + Definition nvarchar(max) NOT NULL, + SavedAt datetime NOT NULL CONSTRAINT DF_WarehouseObjectBackup_SavedAt DEFAULT(GETDATE()), + CONSTRAINT PK_WarehouseObjectBackup PRIMARY KEY CLUSTERED (BackupTag, ObjectName) + ); +END; +GO + +DECLARE @BackupTag varchar(64) = 'plist_reservation_fix_alpha2'; + +;WITH Targets AS ( + SELECT N'dbo.sp_xExePackingListPallet' AS ObjectName, 'PROCEDURE' AS ObjectType + UNION ALL SELECT N'dbo.XMag_ViewPackingList', 'VIEW' + UNION ALL SELECT N'dbo.sp_xExePackingListPalletPrenota', 'PROCEDURE' + UNION ALL SELECT N'dbo.sp_ControllaPrenotazionePackingListPalletNew', 'PROCEDURE' +) +INSERT INTO dbo.WarehouseObjectBackup (BackupTag, ObjectName, ObjectType, Definition) +SELECT + @BackupTag, + t.ObjectName, + t.ObjectType, + sm.[definition] +FROM Targets t +INNER JOIN sys.sql_modules sm + ON sm.object_id = OBJECT_ID(t.ObjectName) +WHERE NOT EXISTS ( + SELECT 1 + FROM dbo.WarehouseObjectBackup b + WHERE b.BackupTag = @BackupTag + AND b.ObjectName = t.ObjectName +); +GO + +SET ANSI_NULLS ON +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), + @Azione char(1) = 'P', + @RC int OUTPUT +AS +BEGIN + SET NOCOUNT ON; + SET @RC = 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; + + 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 + ); + + INSERT INTO @TargetCelle (IDCella) + SELECT DISTINCT Cella + FROM dbo.XMag_ViewPackingList + WHERE CAST(Documento AS varchar(8)) = @Documento + AND Cella IS NOT NULL; + + IF @Azione = 'P' + BEGIN + IF @DocumentoAttivo = @Documento + RETURN; + + UPDATE c + SET c.IDStato = 0, + c.ModUtente = @Nominativo, + c.ModDataOra = GETDATE() + FROM dbo.Celle c + WHERE ISNULL(c.IDStato, 0) <> 0; + + 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; + + 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 CAST(Documento AS varchar(8)) = @Documento; + + EXEC dbo.sp_LogPackingList + @ID = @ID, + @Code = @Documento, + @Description = @Description, + @Message = @Message OUTPUT, + @IDResult = @IDResult OUTPUT; + + RETURN; + END; + + IF @Azione = 'S' + BEGIN + IF ISNULL(@DocumentoAttivo, '') <> @Documento + RETURN; + + 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; + + 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 diff --git a/login_window.py b/login_window.py index 4b8f87a..60e1195 100644 --- a/login_window.py +++ b/login_window.py @@ -7,7 +7,6 @@ from tkinter import messagebox, ttk from typing import Any from audit_log import log_session_event -from busy_overlay import InlineBusyOverlay from gestione_aree import AsyncRunner from locale_text import load_locale_catalog, text as loc_text from ui_theme import theme_section, theme_value @@ -62,10 +61,11 @@ class LoginWindow(tk.Toplevel): self._login_button: ttk.Button | None = None self._cancel_button: ttk.Button | None = None self._status_var = tk.StringVar(value="") - self._busy = InlineBusyOverlay(self, self._theme) + self._show_ready_after_id: str | None = None + self._clear_topmost_after_id: str | None = None 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", "235x185+0+0"))) + self.geometry("170x145+0+0" if self.compact else str(theme_value(self._theme, "window_geometry", "165x155+0+0"))) self.resizable(False, False) try: if parent is not None and parent.winfo_viewable(): @@ -83,33 +83,24 @@ class LoginWindow(tk.Toplevel): self.deiconify() self.lift() self.attributes("-topmost", True) - self.after(50, self._show_ready) + self._show_ready_after_id = self.after(50, self._show_ready) def _build_ui(self) -> None: """Build the compact operator login form.""" - body = ttk.Frame(self, padding=8 if self.compact else 10) + body = ttk.Frame(self, padding=8 if self.compact else 8) body.pack(fill="both", expand=True) - body.columnconfigure(1, weight=1) + body.columnconfigure(1, weight=0) row_offset = 0 - if not self.compact: - ttk.Label( - body, - text=loc_text("login.heading", catalog=self._locale_catalog, default="Autenticazione operatore"), - 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( - row=row_offset, column=0, sticky="w", padx=(0, 6), pady=4 if self.compact else 6 - ) + ttk.Label(body, text="User:").grid(row=row_offset, column=0, sticky="w", padx=(0, 4), pady=4) self.login_entry = ttk.Entry(body, textvariable=self.login_var, width=10, font=("Segoe UI", 10 if self.compact else 9)) - self.login_entry.grid(row=row_offset, column=1, sticky="ew", pady=4 if self.compact else 6) + self.login_entry.grid(row=row_offset, column=1, sticky="w", pady=4) - ttk.Label(body, text="Pwd:").grid(row=row_offset + 1, column=0, sticky="w", padx=(0, 6), pady=4 if self.compact else 6) + ttk.Label(body, text="Pwd:").grid(row=row_offset + 1, column=0, sticky="w", padx=(0, 4), pady=4) self.password_entry = ttk.Entry(body, textvariable=self.password_var, width=10, show="*", font=("Segoe UI", 10 if self.compact else 9)) - self.password_entry.grid(row=row_offset + 1, column=1, sticky="ew", pady=4 if self.compact else 6) + self.password_entry.grid(row=row_offset + 1, column=1, sticky="w", pady=4) if self.compact: actions = ttk.Frame(body) @@ -128,10 +119,10 @@ class LoginWindow(tk.Toplevel): self._login_button.grid(row=0, column=0, sticky="ew") else: self.status_label = ttk.Label(body, textvariable=self._status_var, foreground="#555555") - self.status_label.grid(row=3, column=0, columnspan=2, sticky="w", pady=(2, 2)) + self.status_label.grid(row=2, column=0, columnspan=2, sticky="w", pady=(2, 2)) actions = ttk.Frame(body) - actions.grid(row=4, column=0, columnspan=2, sticky="w", pady=(6, 0)) + actions.grid(row=3, 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"), @@ -168,10 +159,6 @@ class LoginWindow(tk.Toplevel): self._cancel_button.configure(state=state) self.configure(cursor="watch" if busy else "") self._status_var.set(message) - if busy: - self._busy.show(message or loc_text("login.status.checking", catalog=self._locale_catalog, default="Verifico credenziali...")) - else: - self._busy.hide() self.update_idletasks() except Exception: pass @@ -195,7 +182,7 @@ class LoginWindow(tk.Toplevel): self._focus_login() finally: try: - self.after(250, lambda: self.attributes("-topmost", False)) + self._clear_topmost_after_id = self.after(250, lambda: self.attributes("-topmost", False)) except Exception: pass @@ -283,6 +270,18 @@ class LoginWindow(tk.Toplevel): def _close(self) -> None: """Release the modal grab and destroy the login window.""" + if self._show_ready_after_id is not None: + try: + self.after_cancel(self._show_ready_after_id) + except Exception: + pass + self._show_ready_after_id = None + if self._clear_topmost_after_id is not None: + try: + self.after_cancel(self._clear_topmost_after_id) + except Exception: + pass + self._clear_topmost_after_id = None try: self.grab_release() except Exception: diff --git a/main.py b/main.py index 3c236bd..ae4b16e 100644 --- a/main.py +++ b/main.py @@ -14,7 +14,7 @@ import time import customtkinter as ctk from tkinter import messagebox -from async_loop_singleton import get_global_loop +from async_loop_singleton import get_global_loop, stop_global_loop from async_msssql_query import AsyncMSSQLClient from audit_log import log_session_event from db_config import build_dsn_from_config, ensure_db_config @@ -45,12 +45,6 @@ BYPASS_LOGIN_USER = { "codice_unita": "U1", } -if sys.platform.startswith("win"): - try: - asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) - except Exception: - pass - # Create one global loop and make it the default everywhere. _loop = get_global_loop() asyncio.set_event_loop(_loop) @@ -71,6 +65,51 @@ _APP_MUTEX = None _APP_MUTEX_NAME = "Local\\WarehousePythonAppSingleton" +def _dispose_db_client() -> None: + """Best-effort disposal of the shared DB client.""" + + global db_app + if db_app is None: + return + try: + fut = asyncio.run_coroutine_threadsafe(db_app.dispose(), _loop) + try: + fut.result(timeout=2) + except Exception: + pass + finally: + db_app = None + + +def _destroy_tk_root(root: tk.Misc | None) -> None: + """Destroy a hidden bootstrap root without leaking a Tk interpreter.""" + + if root is None: + return + try: + root.quit() + except Exception: + pass + try: + root.destroy() + except Exception: + pass + + +def _shutdown_runtime(*, bootstrap: tk.Misc | None = None, dispose_db: bool = True) -> None: + """Release temporary Tk resources, DB client and background loop.""" + + try: + _destroy_tk_root(bootstrap) + finally: + if dispose_db: + _dispose_db_client() + try: + stop_global_loop() + except Exception: + pass + + def _acquire_single_instance_mutex() -> bool: """Return ``True`` only for the first running instance of the application.""" @@ -435,14 +474,19 @@ if __name__ == "__main__": ), parent=root, ) + _destroy_tk_root(root) try: - root.destroy() + stop_global_loop() except Exception: pass raise SystemExit(0) db_cfg = ensure_db_config(_loop) if db_cfg is None: + try: + stop_global_loop() + except Exception: + pass raise SystemExit(0) db_app = AsyncMSSQLClient(build_dsn_from_config(db_cfg)) @@ -466,23 +510,12 @@ if __name__ == "__main__": session = prompt_login(bootstrap, db_app) if session is None: - if db_app is not None: - fut = asyncio.run_coroutine_threadsafe(db_app.dispose(), _loop) - try: - fut.result(timeout=2) - except Exception: - pass - try: - if bootstrap is not None: - bootstrap.destroy() - except Exception: - pass + _shutdown_runtime(bootstrap=bootstrap, dispose_db=True) raise SystemExit(0) - try: - if bootstrap is not None: - bootstrap.destroy() - except Exception: - pass + _destroy_tk_root(bootstrap) - Launcher(session, db_app).mainloop() + try: + Launcher(session, db_app).mainloop() + finally: + _shutdown_runtime(bootstrap=None, dispose_db=True) diff --git a/rollback_plist_reservation_patch.sql b/rollback_plist_reservation_patch.sql new file mode 100644 index 0000000..53e22ef --- /dev/null +++ b/rollback_plist_reservation_patch.sql @@ -0,0 +1,69 @@ +SET ANSI_NULLS ON +GO +SET QUOTED_IDENTIFIER ON +GO + +/* +Rollback della patch prenotazione plist. + +Ripristina dal backup salvato da: +- apply_plist_reservation_patch.sql + +Oggetti ripristinati: +- dbo.XMag_ViewPackingList +- dbo.sp_xExePackingListPallet +- dbo.sp_xExePackingListPalletPrenota +- dbo.sp_ControllaPrenotazionePackingListPalletNew + +La tabella dbo.PickingListReservation NON viene rimossa: +- può restare nel DB +- il codice legacy non la usa +*/ + +DECLARE @BackupTag varchar(64) = 'plist_reservation_fix_alpha2'; +DECLARE @Sql nvarchar(max); + +IF OBJECT_ID(N'dbo.WarehouseObjectBackup', N'U') IS NULL +BEGIN + RAISERROR('dbo.WarehouseObjectBackup non esiste. Impossibile fare rollback.', 16, 1); + RETURN; +END; + +IF NOT EXISTS ( + SELECT 1 + FROM dbo.WarehouseObjectBackup + WHERE BackupTag = @BackupTag +) +BEGIN + RAISERROR('Backup tag %s non trovato. Impossibile fare rollback.', 16, 1, @BackupTag); + RETURN; +END; + +SELECT @Sql = Definition +FROM dbo.WarehouseObjectBackup +WHERE BackupTag = @BackupTag + AND ObjectName = N'dbo.XMag_ViewPackingList'; + +IF @Sql IS NOT NULL EXEC(@Sql); + +SELECT @Sql = Definition +FROM dbo.WarehouseObjectBackup +WHERE BackupTag = @BackupTag + AND ObjectName = N'dbo.sp_xExePackingListPallet'; + +IF @Sql IS NOT NULL EXEC(@Sql); + +SELECT @Sql = Definition +FROM dbo.WarehouseObjectBackup +WHERE BackupTag = @BackupTag + AND ObjectName = N'dbo.sp_xExePackingListPalletPrenota'; + +IF @Sql IS NOT NULL EXEC(@Sql); + +SELECT @Sql = Definition +FROM dbo.WarehouseObjectBackup +WHERE BackupTag = @BackupTag + AND ObjectName = N'dbo.sp_ControllaPrenotazionePackingListPalletNew'; + +IF @Sql IS NOT NULL EXEC(@Sql); + diff --git a/ui_theme.json b/ui_theme.json index 77be64a..40ecc8e 100644 --- a/ui_theme.json +++ b/ui_theme.json @@ -163,7 +163,7 @@ } }, "login_window": { - "window_geometry": "420x250", + "window_geometry": "165x155+0+0", "overlay_cover_fg_color": ["#d9d9d9", "#4a4a4a"], "overlay_panel_fg_color": ["#f2f2f2", "#353535"], "overlay_panel_corner_radius": 10,