migrazione verso gitea
This commit is contained in:
105
reset_corsie.py
105
reset_corsie.py
@@ -1,12 +1,17 @@
|
||||
# reset_corsie.py
|
||||
"""Window used to inspect and empty an entire warehouse aisle.
|
||||
|
||||
The module exposes a destructive maintenance tool: it summarizes the occupancy
|
||||
state of a selected aisle and, after explicit confirmation, deletes matching
|
||||
rows from ``MagazziniPallet``.
|
||||
"""
|
||||
|
||||
import tkinter as tk
|
||||
from tkinter import ttk, messagebox, simpledialog
|
||||
from tkinter import messagebox, simpledialog, ttk
|
||||
|
||||
import customtkinter as ctk
|
||||
from datetime import datetime
|
||||
|
||||
from gestione_aree_frame_async import BusyOverlay, AsyncRunner
|
||||
from gestione_aree_frame_async import AsyncRunner, BusyOverlay
|
||||
|
||||
# ---------------- SQL ----------------
|
||||
SQL_CORSIE = """
|
||||
WITH C AS (
|
||||
SELECT DISTINCT LTRIM(RTRIM(Corsia)) AS Corsia
|
||||
@@ -85,17 +90,14 @@ JOIN dbo.Celle c ON c.ID = mp.IDCella
|
||||
WHERE c.ID <> 9999 AND LTRIM(RTRIM(c.Corsia)) = :corsia;
|
||||
"""
|
||||
|
||||
|
||||
class ResetCorsieWindow(ctk.CTkToplevel):
|
||||
"""
|
||||
Finestra per:
|
||||
- selezionare una corsia
|
||||
- vedere riepilogo occupazione / doppie / pallet
|
||||
- vedere l'elenco celle occupate
|
||||
- svuotare (DELETE MagazziniPallet) tutte le celle della corsia selezionata
|
||||
"""
|
||||
"""Toplevel used to inspect and clear the pallets assigned to an aisle."""
|
||||
|
||||
def __init__(self, parent, db_client):
|
||||
"""Create the window and immediately load the list of aisles."""
|
||||
super().__init__(parent)
|
||||
self.title("Reset Corsie — svuotamento celle per corsia")
|
||||
self.title("Reset Corsie - svuotamento celle per corsia")
|
||||
self.geometry("1000x680")
|
||||
self.minsize(880, 560)
|
||||
self.resizable(True, True)
|
||||
@@ -107,19 +109,22 @@ class ResetCorsieWindow(ctk.CTkToplevel):
|
||||
self._build_ui()
|
||||
self._load_corsie()
|
||||
|
||||
# ---------- UI ----------
|
||||
def _build_ui(self):
|
||||
top = ctk.CTkFrame(self); top.pack(fill="x", padx=8, pady=8)
|
||||
"""Create selectors, summary widgets and the occupied-cell grid."""
|
||||
top = ctk.CTkFrame(self)
|
||||
top.pack(fill="x", padx=8, pady=8)
|
||||
ctk.CTkLabel(top, text="Corsia:").pack(side="left")
|
||||
self.cmb = ctk.CTkComboBox(top, width=140, values=[])
|
||||
self.cmb.pack(side="left", padx=(6,10))
|
||||
self.cmb.pack(side="left", padx=(6, 10))
|
||||
ctk.CTkButton(top, text="Carica", command=self.refresh).pack(side="left")
|
||||
ctk.CTkButton(top, text="Svuota corsia…", command=self._ask_reset).pack(side="right")
|
||||
ctk.CTkButton(top, text="Svuota corsia...", command=self._ask_reset).pack(side="right")
|
||||
|
||||
mid = ctk.CTkFrame(self); mid.pack(fill="both", expand=True, padx=8, pady=(0,8))
|
||||
mid.grid_columnconfigure(0, weight=1); mid.grid_rowconfigure(0, weight=1)
|
||||
mid = ctk.CTkFrame(self)
|
||||
mid.pack(fill="both", expand=True, padx=8, pady=(0, 8))
|
||||
mid.grid_columnconfigure(0, weight=1)
|
||||
mid.grid_rowconfigure(0, weight=1)
|
||||
|
||||
self.tree = ttk.Treeview(mid, columns=("Ubicazione","NumUDC"), show="headings", selectmode="browse")
|
||||
self.tree = ttk.Treeview(mid, columns=("Ubicazione", "NumUDC"), show="headings", selectmode="browse")
|
||||
self.tree.heading("Ubicazione", text="Ubicazione")
|
||||
self.tree.heading("NumUDC", text="UDC in cella")
|
||||
self.tree.column("Ubicazione", width=240, anchor="w")
|
||||
@@ -133,8 +138,8 @@ class ResetCorsieWindow(ctk.CTkToplevel):
|
||||
sx.grid(row=1, column=0, sticky="ew")
|
||||
|
||||
bottom = ctk.CTkFrame(self)
|
||||
bottom.pack(fill="x", padx=8, pady=(0,8))
|
||||
ctk.CTkLabel(bottom, text="Riepilogo", font=("Segoe UI", 12, "bold")).pack(anchor="w", padx=8, pady=(8,0))
|
||||
bottom.pack(fill="x", padx=8, pady=(0, 8))
|
||||
ctk.CTkLabel(bottom, text="Riepilogo", font=("Segoe UI", 12, "bold")).pack(anchor="w", padx=8, pady=(8, 0))
|
||||
|
||||
g = ctk.CTkFrame(bottom)
|
||||
g.pack(fill="x", padx=8, pady=8)
|
||||
@@ -143,9 +148,10 @@ class ResetCorsieWindow(ctk.CTkToplevel):
|
||||
self.var_dbl = tk.StringVar(value="0")
|
||||
self.var_pallet = tk.StringVar(value="0")
|
||||
|
||||
def _kv(parent, label, var, col):
|
||||
ctk.CTkLabel(parent, text=label, font=("Segoe UI", 9, "bold")).grid(row=0, column=col*2, sticky="w", padx=(0,6))
|
||||
ctk.CTkLabel(parent, textvariable=var).grid(row=0, column=col*2+1, sticky="w", padx=(0,18))
|
||||
def _kv(parent_widget, label, var, col):
|
||||
"""Build a compact summary label/value pair."""
|
||||
ctk.CTkLabel(parent_widget, text=label, font=("Segoe UI", 9, "bold")).grid(row=0, column=col * 2, sticky="w", padx=(0, 6))
|
||||
ctk.CTkLabel(parent_widget, textvariable=var).grid(row=0, column=col * 2 + 1, sticky="w", padx=(0, 18))
|
||||
|
||||
g.grid_columnconfigure(7, weight=1)
|
||||
_kv(g, "Tot. celle:", self.var_tot_celle, 0)
|
||||
@@ -153,28 +159,30 @@ class ResetCorsieWindow(ctk.CTkToplevel):
|
||||
_kv(g, "Celle doppie:", self.var_dbl, 2)
|
||||
_kv(g, "Tot. pallet:", self.var_pallet, 3)
|
||||
|
||||
# ---------- Data ----------
|
||||
def _load_corsie(self):
|
||||
"""Load available aisles and preselect ``1A`` when present."""
|
||||
def _ok(res):
|
||||
rows = res.get("rows", []) if isinstance(res, dict) else []
|
||||
items = [r[0] for r in rows]
|
||||
self.cmb.configure(values=items)
|
||||
if items:
|
||||
# auto 1A se presente
|
||||
sel = "1A" if "1A" in items else items[0]
|
||||
self.cmb.set(sel)
|
||||
self.refresh()
|
||||
else:
|
||||
messagebox.showinfo("Info", "Nessuna corsia trovata.", parent=self)
|
||||
|
||||
def _err(ex):
|
||||
messagebox.showerror("Errore", f"Caricamento corsie fallito:\n{ex}", parent=self)
|
||||
self._async.run(self.db.query_json(SQL_CORSIE, {}), _ok, _err, busy=self._busy, message="Carico corsie…")
|
||||
|
||||
self._async.run(self.db.query_json(SQL_CORSIE, {}), _ok, _err, busy=self._busy, message="Carico corsie...")
|
||||
|
||||
def refresh(self):
|
||||
"""Refresh both the summary counters and the occupied-cell list."""
|
||||
corsia = self.cmb.get().strip()
|
||||
if not corsia:
|
||||
return
|
||||
# riepilogo
|
||||
|
||||
def _ok_sum(res):
|
||||
rows = res.get("rows", []) if isinstance(res, dict) else []
|
||||
if rows:
|
||||
@@ -184,39 +192,45 @@ class ResetCorsieWindow(ctk.CTkToplevel):
|
||||
self.var_dbl.set(str(dbl or 0))
|
||||
self.var_pallet.set(str(pallet or 0))
|
||||
else:
|
||||
self.var_tot_celle.set("0"); self.var_occ.set("0"); self.var_dbl.set("0"); self.var_pallet.set("0")
|
||||
self.var_tot_celle.set("0")
|
||||
self.var_occ.set("0")
|
||||
self.var_dbl.set("0")
|
||||
self.var_pallet.set("0")
|
||||
|
||||
def _err_sum(ex):
|
||||
messagebox.showerror("Errore", f"Riepilogo fallito:\n{ex}", parent=self)
|
||||
|
||||
self._async.run(self.db.query_json(SQL_RIEPILOGO, {"corsia": corsia}), _ok_sum, _err_sum, busy=self._busy, message=f"Riepilogo {corsia}…")
|
||||
self._async.run(self.db.query_json(SQL_RIEPILOGO, {"corsia": corsia}), _ok_sum, _err_sum, busy=self._busy, message=f"Riepilogo {corsia}...")
|
||||
|
||||
# dettaglio
|
||||
def _ok_det(res):
|
||||
rows = res.get("rows", []) if isinstance(res, dict) else []
|
||||
for i in self.tree.get_children(): self.tree.delete(i)
|
||||
for idc, ubi, n in rows:
|
||||
for item in self.tree.get_children():
|
||||
self.tree.delete(item)
|
||||
for _idc, ubi, n in rows:
|
||||
self.tree.insert("", "end", values=(ubi, n))
|
||||
|
||||
def _err_det(ex):
|
||||
messagebox.showerror("Errore", f"Dettaglio fallito:\n{ex}", parent=self)
|
||||
|
||||
self._async.run(self.db.query_json(SQL_DETTAGLIO, {"corsia": corsia}), _ok_det, _err_det, busy=None, message=None)
|
||||
|
||||
# ---------- Reset ----------
|
||||
def _ask_reset(self):
|
||||
"""Ask for confirmation and start the delete flow for the selected aisle."""
|
||||
corsia = self.cmb.get().strip()
|
||||
if not corsia:
|
||||
return
|
||||
# Primo: quante righe verrebbero cancellate?
|
||||
|
||||
def _ok_count(res):
|
||||
rows = res.get("rows", []) if isinstance(res, dict) else []
|
||||
n = int(rows[0][0]) if rows else 0
|
||||
if n <= 0:
|
||||
messagebox.showinfo("Svuota corsia", f"Nessun pallet da rimuovere per la corsia {corsia}.", parent=self)
|
||||
return
|
||||
# doppia conferma
|
||||
msg = (f"Verranno cancellati {n} record da MagazziniPallet per la corsia {corsia}.",
|
||||
"Questa operazione è irreversibile.",
|
||||
"Digitare il nome della corsia per confermare:")
|
||||
msg = (
|
||||
f"Verranno cancellati {n} record da MagazziniPallet per la corsia {corsia}.",
|
||||
"Questa operazione e' irreversibile.",
|
||||
"Digitare il nome della corsia per confermare:",
|
||||
)
|
||||
confirm = simpledialog.askstring("Conferma", "\n".join(msg), parent=self)
|
||||
if confirm is None:
|
||||
return
|
||||
@@ -224,22 +238,27 @@ class ResetCorsieWindow(ctk.CTkToplevel):
|
||||
messagebox.showwarning("Annullato", "Testo di conferma non corrispondente.", parent=self)
|
||||
return
|
||||
self._do_reset(corsia)
|
||||
|
||||
def _err_count(ex):
|
||||
messagebox.showerror("Errore", f"Conteggio righe da cancellare fallito:\n{ex}", parent=self)
|
||||
|
||||
self._async.run(self.db.query_json(SQL_COUNT_DELETE, {"corsia": corsia}), _ok_count, _err_count, busy=self._busy, message="Verifico…")
|
||||
self._async.run(self.db.query_json(SQL_COUNT_DELETE, {"corsia": corsia}), _ok_count, _err_count, busy=self._busy, message="Verifico...")
|
||||
|
||||
def _do_reset(self, corsia: str):
|
||||
"""Execute the actual delete and refresh the window afterwards."""
|
||||
def _ok_del(_):
|
||||
messagebox.showinfo("Completato", f"Corsia {corsia}: svuotamento completato.", parent=self)
|
||||
self.refresh()
|
||||
|
||||
def _err_del(ex):
|
||||
messagebox.showerror("Errore", f"Svuotamento fallito:\n{ex}", parent=self)
|
||||
|
||||
self._async.run(self.db.query_json(SQL_DELETE, {"corsia": corsia}), _ok_del, _err_del, busy=self._busy, message=f"Svuoto {corsia}…")
|
||||
self._async.run(self.db.query_json(SQL_DELETE, {"corsia": corsia}), _ok_del, _err_del, busy=self._busy, message=f"Svuoto {corsia}...")
|
||||
|
||||
|
||||
def open_reset_corsie_window(parent, db_app):
|
||||
"""Create, focus and return the aisle reset window."""
|
||||
win = ResetCorsieWindow(parent, db_app)
|
||||
win.lift(); win.focus_set()
|
||||
win.lift()
|
||||
win.focus_set()
|
||||
return win
|
||||
|
||||
Reference in New Issue
Block a user