Checkpoint before more window sizing work
This commit is contained in:
@@ -16,7 +16,12 @@ import customtkinter as ctk
|
||||
from openpyxl import Workbook
|
||||
from openpyxl.styles import Alignment, Font
|
||||
|
||||
from busy_overlay import InlineBusyOverlay
|
||||
from gestione_aree import AsyncRunner
|
||||
from gestione_scarico import move_pallet_async
|
||||
from tooltips import WidgetToolTip, load_tooltip_catalog, tooltip_text
|
||||
from ui_theme import theme_color, theme_font, theme_section, theme_value
|
||||
from window_placement import place_window_fullsize_below_parent_later
|
||||
|
||||
try:
|
||||
from loguru import logger
|
||||
@@ -288,6 +293,90 @@ GROUP BY b.BarcodePallet, ta.Descrizione, ta.Lotto, shipped.BarcodePallet, la.ID
|
||||
ORDER BY b.BarcodePallet;
|
||||
"""
|
||||
|
||||
SQL_PALLET_IN_CORSIA = BASE_CTE + f"""
|
||||
, dup_celle AS (
|
||||
SELECT b.IDCella
|
||||
FROM base b
|
||||
WHERE b.Corsia = RTRIM(:corsia)
|
||||
GROUP BY b.IDCella
|
||||
HAVING COUNT(DISTINCT b.BarcodePallet) > 1
|
||||
),
|
||||
corsia_pallets AS (
|
||||
SELECT DISTINCT b.IDCella, b.BarcodePallet
|
||||
FROM base b
|
||||
JOIN dup_celle dc ON dc.IDCella = b.IDCella
|
||||
),
|
||||
latest_any AS (
|
||||
SELECT
|
||||
ranked.BarcodePallet,
|
||||
ranked.IDCella
|
||||
FROM (
|
||||
SELECT
|
||||
mp.Attributo AS BarcodePallet,
|
||||
mp.IDCella,
|
||||
ROW_NUMBER() OVER (
|
||||
PARTITION BY mp.Attributo
|
||||
ORDER BY mp.ID DESC
|
||||
) AS rn
|
||||
FROM dbo.MagazziniPallet mp
|
||||
JOIN (SELECT DISTINCT BarcodePallet FROM corsia_pallets) cp
|
||||
ON cp.BarcodePallet COLLATE Latin1_General_CI_AS =
|
||||
mp.Attributo COLLATE Latin1_General_CI_AS
|
||||
WHERE mp.Tipo = 'V'
|
||||
AND mp.PesoUnitario > 0
|
||||
) ranked
|
||||
WHERE ranked.rn = 1
|
||||
),
|
||||
shipped AS (
|
||||
SELECT DISTINCT shipped.BarcodePallet
|
||||
FROM dbo.XMag_GiacenzaPalletPlistChiuse shipped
|
||||
JOIN (SELECT DISTINCT BarcodePallet FROM corsia_pallets) cp
|
||||
ON cp.BarcodePallet COLLATE Latin1_General_CI_AS =
|
||||
shipped.BarcodePallet COLLATE Latin1_General_CI_AS
|
||||
)
|
||||
SELECT
|
||||
cp.IDCella,
|
||||
{UBI_B} AS Ubicazione,
|
||||
cp.BarcodePallet AS Pallet,
|
||||
ta.Descrizione,
|
||||
ta.Lotto,
|
||||
CASE
|
||||
WHEN shipped.BarcodePallet IS NOT NULL THEN CAST(1 AS int)
|
||||
ELSE CAST(0 AS int)
|
||||
END AS IsShippedGhost,
|
||||
CASE
|
||||
WHEN la.IDCella IS NOT NULL
|
||||
AND la.IDCella <> cp.IDCella
|
||||
THEN CAST(1 AS int)
|
||||
ELSE CAST(0 AS int)
|
||||
END AS IsMovedGhost
|
||||
FROM corsia_pallets cp
|
||||
JOIN base b
|
||||
ON b.IDCella = cp.IDCella
|
||||
AND b.BarcodePallet = cp.BarcodePallet
|
||||
OUTER APPLY (
|
||||
SELECT TOP (1) t.Descrizione, t.Lotto
|
||||
FROM dbo.vXTracciaProdotti AS t
|
||||
WHERE t.Pallet = cp.BarcodePallet COLLATE Latin1_General_CI_AS
|
||||
ORDER BY t.Lotto
|
||||
) AS ta
|
||||
LEFT JOIN latest_any la
|
||||
ON la.BarcodePallet COLLATE Latin1_General_CI_AS =
|
||||
cp.BarcodePallet COLLATE Latin1_General_CI_AS
|
||||
LEFT JOIN shipped
|
||||
ON shipped.BarcodePallet COLLATE Latin1_General_CI_AS =
|
||||
cp.BarcodePallet COLLATE Latin1_General_CI_AS
|
||||
GROUP BY
|
||||
cp.IDCella,
|
||||
{UBI_B},
|
||||
cp.BarcodePallet,
|
||||
ta.Descrizione,
|
||||
ta.Lotto,
|
||||
shipped.BarcodePallet,
|
||||
la.IDCella
|
||||
ORDER BY cp.IDCella, cp.BarcodePallet;
|
||||
"""
|
||||
|
||||
|
||||
def _build_diagnostic_note(is_shipped: int | bool, is_moved: int | bool) -> str:
|
||||
"""Translate anomaly flags into the operator-facing ghost cause."""
|
||||
@@ -343,14 +432,25 @@ class CelleMultipleWindow(ctk.CTkToplevel):
|
||||
def __init__(self, root, db_client, runner: AsyncRunner | None = None, session=None):
|
||||
"""Bind the shared DB client and immediately load the tree summary."""
|
||||
super().__init__(root)
|
||||
self._theme = theme_section("multi_udc", {})
|
||||
self._tooltip_catalog = load_tooltip_catalog()
|
||||
self.title("Celle con piu' pallet")
|
||||
self.session = session
|
||||
self.geometry("1100x700")
|
||||
self.minsize(900, 550)
|
||||
self.geometry(str(theme_value(self._theme, "window_geometry", "1100x700")))
|
||||
minsize = theme_value(self._theme, "window_minsize", [900, 550])
|
||||
self.minsize(int(minsize[0]), int(minsize[1]))
|
||||
self.resizable(True, True)
|
||||
try:
|
||||
self.configure(fg_color=theme_color(self._theme, "window_fg_color", ("#efefef", "#2f2f2f")))
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
self.db = db_client
|
||||
self.runner = runner or AsyncRunner(self)
|
||||
self._busy = InlineBusyOverlay(self, self._theme)
|
||||
self.selected_corsia_id: str | None = None
|
||||
self.selected_udc_keys: set[str] = set()
|
||||
self.udc_meta_by_key: dict[str, dict[str, Any]] = {}
|
||||
|
||||
self._build_layout()
|
||||
self._bind_events()
|
||||
@@ -365,13 +465,35 @@ class CelleMultipleWindow(ctk.CTkToplevel):
|
||||
|
||||
toolbar = ctk.CTkFrame(self)
|
||||
toolbar.grid(row=0, column=0, sticky="nsew")
|
||||
ctk.CTkButton(toolbar, text="Aggiorna", command=self.refresh_all).pack(side="left", padx=6, pady=4)
|
||||
ctk.CTkButton(toolbar, text="Espandi tutto", command=self.expand_all).pack(side="left", padx=6, pady=4)
|
||||
ctk.CTkButton(toolbar, text="Comprimi tutto", command=self.collapse_all).pack(side="left", padx=6, pady=4)
|
||||
ctk.CTkButton(toolbar, text="Esporta in XLSX", command=self.export_to_xlsx).pack(side="left", padx=6, pady=4)
|
||||
try:
|
||||
toolbar.configure(fg_color=theme_color(self._theme, "toolbar_frame_fg_color", ("#d7d7d7", "#3b3b3b")))
|
||||
except Exception:
|
||||
pass
|
||||
btn_refresh = ctk.CTkButton(toolbar, text="Aggiorna", command=self.refresh_all, font=theme_font(self._theme, "toolbar_button_font", ("Segoe UI", 10, "bold")))
|
||||
btn_refresh.pack(side="left", padx=6, pady=4)
|
||||
btn_expand = ctk.CTkButton(toolbar, text="Espandi tutto", command=self.expand_all, font=theme_font(self._theme, "toolbar_button_font", ("Segoe UI", 10, "bold")))
|
||||
btn_expand.pack(side="left", padx=6, pady=4)
|
||||
btn_collapse = ctk.CTkButton(toolbar, text="Comprimi tutto", command=self.collapse_all, font=theme_font(self._theme, "toolbar_button_font", ("Segoe UI", 10, "bold")))
|
||||
btn_collapse.pack(side="left", padx=6, pady=4)
|
||||
btn_preselect = ctk.CTkButton(toolbar, text="Preselezione fantasmi corsia", command=self._preselect_selected_corsia, font=theme_font(self._theme, "toolbar_button_font", ("Segoe UI", 10, "bold")))
|
||||
btn_preselect.pack(side="left", padx=6, pady=4)
|
||||
btn_remove = ctk.CTkButton(toolbar, text="Rimuovi fantasmi corsia", command=self._remove_selected_ghosts_for_corsia, font=theme_font(self._theme, "toolbar_button_font", ("Segoe UI", 10, "bold")))
|
||||
btn_remove.pack(side="left", padx=6, pady=4)
|
||||
btn_export = ctk.CTkButton(toolbar, text="Esporta in XLSX", command=self.export_to_xlsx, font=theme_font(self._theme, "toolbar_button_font", ("Segoe UI", 10, "bold")))
|
||||
btn_export.pack(side="left", padx=6, pady=4)
|
||||
WidgetToolTip(btn_refresh, tooltip_text("multi_udc.refresh", catalog=self._tooltip_catalog))
|
||||
WidgetToolTip(btn_expand, tooltip_text("multi_udc.expand_all", catalog=self._tooltip_catalog))
|
||||
WidgetToolTip(btn_collapse, tooltip_text("multi_udc.collapse_all", catalog=self._tooltip_catalog))
|
||||
WidgetToolTip(btn_preselect, tooltip_text("multi_udc.preselect", catalog=self._tooltip_catalog))
|
||||
WidgetToolTip(btn_remove, tooltip_text("multi_udc.remove_ghosts", catalog=self._tooltip_catalog))
|
||||
WidgetToolTip(btn_export, tooltip_text("multi_udc.export_xlsx", catalog=self._tooltip_catalog))
|
||||
|
||||
frame = ctk.CTkFrame(self)
|
||||
frame.grid(row=1, column=0, sticky="nsew", padx=6, pady=(0, 6))
|
||||
try:
|
||||
frame.configure(fg_color=theme_color(self._theme, "content_frame_fg_color", ("#e5e5e5", "#383838")))
|
||||
except Exception:
|
||||
pass
|
||||
frame.grid_rowconfigure(0, weight=1)
|
||||
frame.grid_columnconfigure(0, weight=1)
|
||||
self.tree = ttk.Treeview(frame, columns=("col2", "col3", "col4"), show="tree headings", selectmode="browse")
|
||||
@@ -392,9 +514,21 @@ class CelleMultipleWindow(ctk.CTkToplevel):
|
||||
|
||||
sumf = ctk.CTkFrame(self)
|
||||
sumf.grid(row=2, column=0, sticky="nsew", padx=6, pady=(0, 6))
|
||||
ctk.CTkLabel(sumf, text="Riepilogo % celle multiple per corsia", font=("Segoe UI", 12, "bold")).pack(anchor="w", padx=8, pady=(8, 0))
|
||||
try:
|
||||
sumf.configure(fg_color=theme_color(self._theme, "summary_frame_fg_color", ("#dcdcdc", "#363636")))
|
||||
except Exception:
|
||||
pass
|
||||
ctk.CTkLabel(
|
||||
sumf,
|
||||
text="Riepilogo % celle multiple per corsia",
|
||||
font=theme_font(self._theme, "summary_title_font", ("Segoe UI", 12, "bold")),
|
||||
).pack(anchor="w", padx=8, pady=(8, 0))
|
||||
inner = ctk.CTkFrame(sumf)
|
||||
inner.pack(fill="both", expand=True, padx=6, pady=6)
|
||||
try:
|
||||
inner.configure(fg_color=theme_color(self._theme, "inner_summary_frame_fg_color", ("#d4d4d4", "#404040")))
|
||||
except Exception:
|
||||
pass
|
||||
inner.grid_rowconfigure(0, weight=1)
|
||||
inner.grid_columnconfigure(0, weight=1)
|
||||
self.sum_tbl = ttk.Treeview(inner, columns=("Corsia", "TotCelle", "CelleMultiple", "Percentuale"), show="headings")
|
||||
@@ -416,10 +550,85 @@ class CelleMultipleWindow(ctk.CTkToplevel):
|
||||
def _bind_events(self):
|
||||
"""Attach lazy-load behavior when nodes are expanded."""
|
||||
self.tree.bind("<<TreeviewOpen>>", self._on_open_node)
|
||||
self.tree.bind("<Button-1>", self._on_tree_click, add="+")
|
||||
|
||||
def _format_corsia_text(self, corsia: str) -> str:
|
||||
"""Render one aisle root with its exclusive selection marker."""
|
||||
|
||||
return f"[{'x' if self.selected_corsia_id == f'corsia:{corsia}' else ' '}] Corsia {corsia}"
|
||||
|
||||
def _format_pallet_text(self, pallet: str, selected: bool) -> str:
|
||||
"""Render one pallet leaf with its selection marker."""
|
||||
|
||||
return f"[{'x' if selected else ' '}] {pallet}"
|
||||
|
||||
def _selected_corsia_value(self) -> str | None:
|
||||
"""Return the code of the currently active aisle."""
|
||||
|
||||
if self.selected_corsia_id and self.selected_corsia_id.startswith("corsia:"):
|
||||
return self.selected_corsia_id.split(":", 1)[1]
|
||||
return None
|
||||
|
||||
def _set_selected_corsia(self, node_id: str | None):
|
||||
"""Keep exactly one selected aisle and refresh root labels."""
|
||||
|
||||
previous = self.selected_corsia_id
|
||||
self.selected_corsia_id = node_id
|
||||
for iid in self.tree.get_children(""):
|
||||
if not iid.startswith("corsia:"):
|
||||
continue
|
||||
self.tree.item(iid, text=self._format_corsia_text(iid.split(":", 1)[1]))
|
||||
if previous and previous != node_id and previous.startswith("corsia:"):
|
||||
self._clear_leaf_selection_for_corsia(previous.split(":", 1)[1])
|
||||
|
||||
def _set_leaf_selected(self, leaf_id: str, selected: bool):
|
||||
"""Toggle one selected pallet leaf and refresh its label if visible."""
|
||||
|
||||
meta = self.udc_meta_by_key.get(leaf_id)
|
||||
if selected:
|
||||
self.selected_udc_keys.add(leaf_id)
|
||||
else:
|
||||
self.selected_udc_keys.discard(leaf_id)
|
||||
if meta and self.tree.exists(leaf_id):
|
||||
self.tree.item(leaf_id, text=self._format_pallet_text(str(meta.get("pallet", "")), selected))
|
||||
|
||||
def _clear_leaf_selection_for_corsia(self, corsia: str):
|
||||
"""Clear all selected pallet leaves for one aisle."""
|
||||
|
||||
for leaf_id, meta in list(self.udc_meta_by_key.items()):
|
||||
if meta.get("corsia") == corsia:
|
||||
self._set_leaf_selected(leaf_id, False)
|
||||
|
||||
def _on_tree_click(self, event):
|
||||
"""Handle custom selection on aisle roots and pallet leaves."""
|
||||
|
||||
try:
|
||||
element = self.tree.identify_element(event.x, event.y)
|
||||
if "indicator" in str(element).lower():
|
||||
return
|
||||
item_id = self.tree.identify_row(event.y)
|
||||
except Exception:
|
||||
return
|
||||
if not item_id:
|
||||
return
|
||||
tags = self.tree.item(item_id, "tags") or ()
|
||||
if "corsia" in tags:
|
||||
self._set_selected_corsia(item_id)
|
||||
self.tree.selection_set(item_id)
|
||||
return "break"
|
||||
if "pallet" in tags:
|
||||
corsia = next((tag.split(":", 1)[1] for tag in tags if tag.startswith("corsia:")), "")
|
||||
self._set_selected_corsia(f"corsia:{corsia}")
|
||||
self._set_leaf_selected(item_id, item_id not in self.selected_udc_keys)
|
||||
self.tree.selection_set(item_id)
|
||||
return "break"
|
||||
|
||||
@_log_call()
|
||||
def refresh_all(self):
|
||||
"""Reload both the duplication tree and the summary percentage table."""
|
||||
self.selected_corsia_id = None
|
||||
self.selected_udc_keys.clear()
|
||||
self.udc_meta_by_key.clear()
|
||||
self._load_corsie()
|
||||
self._load_riepilogo()
|
||||
|
||||
@@ -448,8 +657,16 @@ class CelleMultipleWindow(ctk.CTkToplevel):
|
||||
if not corsia:
|
||||
continue
|
||||
node_id = f"corsia:{corsia}"
|
||||
self.tree.insert("", "end", iid=node_id, text=f"Corsia {corsia}", values=("", ""), open=False, tags=("corsia",))
|
||||
self.tree.insert(node_id, "end", iid=f"{node_id}::lazy", text="...", values=("", ""))
|
||||
self.tree.insert(
|
||||
"",
|
||||
"end",
|
||||
iid=node_id,
|
||||
text=self._format_corsia_text(corsia),
|
||||
values=("", "", ""),
|
||||
open=False,
|
||||
tags=("corsia",),
|
||||
)
|
||||
self.tree.insert(node_id, "end", iid=f"{node_id}::lazy", text="...", values=("", "", ""))
|
||||
|
||||
def _on_open_node(self, _evt):
|
||||
"""Lazy-load children when a tree node is expanded."""
|
||||
@@ -491,7 +708,7 @@ class CelleMultipleWindow(ctk.CTkToplevel):
|
||||
rows = _json_obj(res).get("rows", [])
|
||||
_log_dataset("multi_udc_celle_per_corsia", rows)
|
||||
if not rows:
|
||||
self.tree.insert(parent_iid, "end", text="(nessuna cella con >1 UDC)", values=("", ""))
|
||||
self.tree.insert(parent_iid, "end", text="(nessuna cella con >1 UDC)", values=("", "", ""))
|
||||
return
|
||||
for row in rows:
|
||||
idc = row["IDCella"]
|
||||
@@ -501,11 +718,19 @@ class CelleMultipleWindow(ctk.CTkToplevel):
|
||||
node_id = f"cella:{idc}"
|
||||
label = f"{ubi} [x{num}]"
|
||||
if self.tree.exists(node_id):
|
||||
self.tree.item(node_id, text=label, values=(f"IDCella {idc}", ""))
|
||||
self.tree.item(node_id, text=label, values=(f"IDCella {idc}", "", ""))
|
||||
else:
|
||||
self.tree.insert(parent_iid, "end", iid=node_id, text=label, values=(f"IDCella {idc}", ""), open=False, tags=("cella", f"corsia:{corsia}"))
|
||||
self.tree.insert(
|
||||
parent_iid,
|
||||
"end",
|
||||
iid=node_id,
|
||||
text=label,
|
||||
values=(f"IDCella {idc}", "", ""),
|
||||
open=False,
|
||||
tags=("cella", f"corsia:{corsia}"),
|
||||
)
|
||||
if not any(child.endswith("::lazy") for child in self.tree.get_children(node_id)):
|
||||
self.tree.insert(node_id, "end", iid=f"{node_id}::lazy", text="...", values=("", ""))
|
||||
self.tree.insert(node_id, "end", iid=f"{node_id}::lazy", text="...", values=("", "", ""))
|
||||
|
||||
@_log_call()
|
||||
def _load_pallet_for_cella(self, parent_iid, idcella: int):
|
||||
@@ -540,20 +765,169 @@ class CelleMultipleWindow(ctk.CTkToplevel):
|
||||
pallet = row.get("Pallet", "")
|
||||
desc = row.get("Descrizione", "")
|
||||
lotto = row.get("Lotto", "")
|
||||
causale = _build_diagnostic_note(row.get("IsShippedGhost", 0), row.get("IsMovedGhost", 0))
|
||||
is_shipped = int(row.get("IsShippedGhost", 0) or 0)
|
||||
is_moved = int(row.get("IsMovedGhost", 0) or 0)
|
||||
causale = _build_diagnostic_note(is_shipped, is_moved)
|
||||
leaf_id = f"pallet:{idcella_num}:{pallet}"
|
||||
self.udc_meta_by_key[leaf_id] = {
|
||||
"corsia": corsia_val,
|
||||
"ubicazione": cella_ubi,
|
||||
"idcella": idcella_num,
|
||||
"pallet": str(pallet),
|
||||
"descrizione": desc,
|
||||
"lotto": lotto,
|
||||
"causale": causale,
|
||||
"is_shipped": is_shipped,
|
||||
"is_moved": is_moved,
|
||||
}
|
||||
if self.tree.exists(leaf_id):
|
||||
self.tree.item(leaf_id, text=str(pallet), values=(desc, lotto, causale))
|
||||
self.tree.item(
|
||||
leaf_id,
|
||||
text=self._format_pallet_text(str(pallet), leaf_id in self.selected_udc_keys),
|
||||
values=(desc, lotto, causale),
|
||||
)
|
||||
continue
|
||||
self.tree.insert(
|
||||
parent_iid,
|
||||
"end",
|
||||
iid=leaf_id,
|
||||
text=str(pallet),
|
||||
text=self._format_pallet_text(str(pallet), leaf_id in self.selected_udc_keys),
|
||||
values=(desc, lotto, causale),
|
||||
tags=("pallet", f"corsia:{corsia_val}", f"ubicazione:{cella_ubi}", f"idcella:{idcella_num}"),
|
||||
)
|
||||
|
||||
@_log_call()
|
||||
def _preselect_selected_corsia(self):
|
||||
"""Expand one aisle and preselect only shipped/moved ghost pallets."""
|
||||
|
||||
corsia = self._selected_corsia_value()
|
||||
if not corsia:
|
||||
messagebox.showinfo("Preselezione", "Seleziona prima una corsia.", parent=self)
|
||||
return
|
||||
|
||||
corsia_node = f"corsia:{corsia}"
|
||||
self.tree.item(corsia_node, open=True)
|
||||
self._clear_leaf_selection_for_corsia(corsia)
|
||||
self._busy.show(f"Preselezione fantasmi corsia {corsia.strip()}...")
|
||||
|
||||
_log_sql("multi_udc_celle_per_corsia.preselect", SQL_CELLE_DUP_PER_CORSIA, {"corsia": corsia})
|
||||
_log_sql("multi_udc_pallet_in_corsia.preselect", SQL_PALLET_IN_CORSIA, {"corsia": corsia})
|
||||
|
||||
async def _q(db):
|
||||
celle_res = _json_obj(await db.query_json(SQL_CELLE_DUP_PER_CORSIA, params={"corsia": corsia}, as_dict_rows=True))
|
||||
celle_rows = celle_res.get("rows", [])
|
||||
pallet_res = _json_obj(await db.query_json(SQL_PALLET_IN_CORSIA, params={"corsia": corsia}, as_dict_rows=True))
|
||||
return {"cells": celle_rows, "pallets": pallet_res.get("rows", [])}
|
||||
|
||||
def _ok(res):
|
||||
self._busy.hide()
|
||||
payload = _json_obj(res)
|
||||
cell_rows = payload.get("cells", [])
|
||||
pallet_rows = payload.get("pallets", [])
|
||||
selected_count = 0
|
||||
grouped_pallets: dict[int, list[dict[str, Any]]] = {}
|
||||
for pallet_row in pallet_rows:
|
||||
grouped_pallets.setdefault(int(pallet_row["IDCella"]), []).append(pallet_row)
|
||||
|
||||
if self.tree.exists(f"{corsia_node}::lazy"):
|
||||
self.tree.delete(f"{corsia_node}::lazy")
|
||||
|
||||
for cell_row in cell_rows:
|
||||
idcella = int(cell_row["IDCella"])
|
||||
cell_node = f"cella:{idcella}"
|
||||
if not self.tree.exists(cell_node):
|
||||
self._fill_celle(corsia_node, {"rows": [cell_row]})
|
||||
self.tree.item(cell_node, open=True)
|
||||
for child in list(self.tree.get_children(cell_node)):
|
||||
self.tree.delete(child)
|
||||
self._fill_pallet(cell_node, {"rows": grouped_pallets.get(idcella, [])})
|
||||
for leaf_id, meta in list(self.udc_meta_by_key.items()):
|
||||
if meta.get("corsia") != corsia or meta.get("idcella") != idcella:
|
||||
continue
|
||||
if meta.get("is_shipped") or meta.get("is_moved"):
|
||||
self._set_leaf_selected(leaf_id, True)
|
||||
selected_count += 1
|
||||
|
||||
messagebox.showinfo(
|
||||
"Preselezione completata",
|
||||
f"Corsia {corsia}\nUDC candidate selezionate automaticamente: {selected_count}",
|
||||
parent=self,
|
||||
)
|
||||
|
||||
def _err(ex):
|
||||
self._busy.hide()
|
||||
_MODULE_LOGGER.exception(f"Errore preselezione fantasmi corsia={corsia}: {ex}")
|
||||
messagebox.showerror("Errore", str(ex), parent=self)
|
||||
|
||||
self.runner.run(_q(self.db), _ok, _err)
|
||||
|
||||
@_log_call()
|
||||
def _remove_selected_ghosts_for_corsia(self):
|
||||
"""Remove the selected ghost pallets only for the active aisle."""
|
||||
|
||||
corsia = self._selected_corsia_value()
|
||||
if not corsia:
|
||||
messagebox.showinfo("Bonifica fantasmi", "Seleziona prima una corsia.", parent=self)
|
||||
return
|
||||
|
||||
selected_meta = [
|
||||
meta
|
||||
for leaf_id, meta in self.udc_meta_by_key.items()
|
||||
if leaf_id in self.selected_udc_keys and meta.get("corsia") == corsia
|
||||
]
|
||||
if not selected_meta:
|
||||
messagebox.showinfo("Bonifica fantasmi", "Nessuna UDC selezionata nella corsia attiva.", parent=self)
|
||||
return
|
||||
|
||||
shipped_count = sum(1 for meta in selected_meta if meta.get("is_shipped"))
|
||||
moved_count = sum(1 for meta in selected_meta if meta.get("is_moved"))
|
||||
cell_count = len({meta.get("idcella") for meta in selected_meta})
|
||||
if not messagebox.askyesno(
|
||||
"Conferma bonifica corsia",
|
||||
(
|
||||
f"Corsia {corsia}\n"
|
||||
f"UDC selezionate: {len(selected_meta)}\n"
|
||||
f"Celle coinvolte: {cell_count}\n"
|
||||
f"Spedite: {shipped_count}\n"
|
||||
f"Spostate: {moved_count}\n\n"
|
||||
"Procedere con la rimozione delle sole UDC selezionate?"
|
||||
),
|
||||
parent=self,
|
||||
):
|
||||
return
|
||||
self._busy.show(f"Rimozione fantasmi corsia {corsia.strip()}...")
|
||||
|
||||
async def _q(_db):
|
||||
results: list[dict[str, Any]] = []
|
||||
for meta in selected_meta:
|
||||
result = await move_pallet_async(
|
||||
self.db,
|
||||
barcode_pallet=str(meta.get("pallet", "")).strip(),
|
||||
target_idcella=9999,
|
||||
target_barcode_cella="9000000",
|
||||
utente=str(getattr(self.session, "login", "") or "warehouse_ui").strip(),
|
||||
)
|
||||
results.append({"meta": meta, "result": result})
|
||||
return {"rows": results}
|
||||
|
||||
def _ok(res):
|
||||
self._busy.hide()
|
||||
rows = _json_obj(res).get("rows", [])
|
||||
removed = sum(1 for row in rows if row.get("result"))
|
||||
self.refresh_all()
|
||||
messagebox.showinfo(
|
||||
"Bonifica completata",
|
||||
f"Corsia {corsia}\nUDC rimosse: {removed}",
|
||||
parent=self,
|
||||
)
|
||||
|
||||
def _err(ex):
|
||||
self._busy.hide()
|
||||
_MODULE_LOGGER.exception(f"Errore bonifica fantasmi corsia={corsia}: {ex}")
|
||||
messagebox.showerror("Errore bonifica", str(ex), parent=self)
|
||||
|
||||
self.runner.run(_q(self.db), _ok, _err)
|
||||
|
||||
@_log_call()
|
||||
def _load_riepilogo(self):
|
||||
"""Load the percentage summary by aisle."""
|
||||
@@ -635,10 +1009,11 @@ class CelleMultipleWindow(ctk.CTkToplevel):
|
||||
tags = self.tree.item(pallet_node, "tags") or ()
|
||||
if "pallet" not in tags:
|
||||
continue
|
||||
corsia = next((tag.split(":", 1)[1] for tag in tags if tag.startswith("corsia:")), "")
|
||||
ubi = next((tag.split(":", 1)[1] for tag in tags if tag.startswith("ubicazione:")), "")
|
||||
idcella = next((tag.split(":", 1)[1] for tag in tags if tag.startswith("idcella:")), "")
|
||||
pallet = self.tree.item(pallet_node, "text")
|
||||
meta = self.udc_meta_by_key.get(pallet_node, {})
|
||||
corsia = meta.get("corsia") or next((tag.split(":", 1)[1] for tag in tags if tag.startswith("corsia:")), "")
|
||||
ubi = meta.get("ubicazione") or next((tag.split(":", 1)[1] for tag in tags if tag.startswith("ubicazione:")), "")
|
||||
idcella = meta.get("idcella") or next((tag.split(":", 1)[1] for tag in tags if tag.startswith("idcella:")), "")
|
||||
pallet = meta.get("pallet") or self.tree.item(pallet_node, "text")
|
||||
desc, lotto, causale = self.tree.item(pallet_node, "values")
|
||||
for j, value in enumerate([corsia, ubi, idcella, pallet, desc, lotto, causale], start=1):
|
||||
ws_det.cell(row=row_idx, column=j, value=value)
|
||||
@@ -690,6 +1065,7 @@ def open_celle_multiple_window(
|
||||
pass
|
||||
win = CelleMultipleWindow(root, db_client, runner=runner, session=session)
|
||||
setattr(root, key, win)
|
||||
place_window_fullsize_below_parent_later(root, win)
|
||||
try:
|
||||
win.lift()
|
||||
win.focus_force()
|
||||
|
||||
Reference in New Issue
Block a user