Aggiungi versamento residui 7G da storico pickinglist
This commit is contained in:
@@ -11,6 +11,7 @@ import customtkinter as ctk
|
||||
|
||||
from busy_overlay import InlineBusyOverlay
|
||||
from gestione_aree import AsyncRunner
|
||||
from gestione_scarico import move_pallet_async
|
||||
from locale_text import load_locale_catalog, text as loc_text
|
||||
from ui_theme import theme_color, theme_font, theme_section, theme_value
|
||||
from ui_tables import merge_tags, style_treeview, zebra_tag
|
||||
@@ -159,6 +160,9 @@ class StoricoPickingListWindow(ctk.CTkToplevel):
|
||||
self._async = AsyncRunner(self)
|
||||
self._busy = InlineBusyOverlay(self, self._theme)
|
||||
self.var_documento = tk.StringVar()
|
||||
self._selected_documento: str | None = None
|
||||
self._selected_stato_operativo: str = ""
|
||||
self._detail_rows: list[dict[str, Any]] = []
|
||||
|
||||
self.title(versioned_title(loc_text("history.picking.title", catalog=self._locale_catalog, default="Storico Picking List"), __name__))
|
||||
self.geometry(str(theme_value(self._theme, "window_geometry", "1200x720")))
|
||||
@@ -182,7 +186,7 @@ class StoricoPickingListWindow(ctk.CTkToplevel):
|
||||
fg_color=theme_color(self._theme, "toolbar_frame_fg_color", ("#d7d7d7", "#3b3b3b")),
|
||||
)
|
||||
top.grid(row=0, column=0, sticky="ew", padx=8, pady=8)
|
||||
top.grid_columnconfigure(3, weight=1)
|
||||
top.grid_columnconfigure(4, weight=1)
|
||||
|
||||
label_font = theme_font(self._theme, "toolbar_label_font", ("Segoe UI", 10))
|
||||
entry_font = theme_font(self._theme, "entry_font", ("Segoe UI", 10))
|
||||
@@ -200,6 +204,18 @@ class StoricoPickingListWindow(ctk.CTkToplevel):
|
||||
).grid(
|
||||
row=0, column=2, sticky="w"
|
||||
)
|
||||
self.btn_ship_residuals = ctk.CTkButton(
|
||||
top,
|
||||
text=loc_text(
|
||||
"history.picking.button.ship_residuals",
|
||||
catalog=self._locale_catalog,
|
||||
default="Versa residui in 7G.1.1",
|
||||
),
|
||||
command=self._ship_selected_residuals,
|
||||
state="disabled",
|
||||
font=button_font,
|
||||
)
|
||||
self.btn_ship_residuals.grid(row=0, column=3, sticky="w", padx=(12, 0))
|
||||
|
||||
self.master_tree = self._make_tree(
|
||||
row=1,
|
||||
@@ -266,12 +282,15 @@ class StoricoPickingListWindow(ctk.CTkToplevel):
|
||||
|
||||
def _load_master(self) -> None:
|
||||
params = {"documento": str(self.var_documento.get() or "").strip() or None}
|
||||
previous_documento = self._selected_documento
|
||||
|
||||
async def _job():
|
||||
return await self.db_client.query_json(SQL_STORICO_PL, params)
|
||||
|
||||
def _ok(res):
|
||||
self._fill_master(_rows_to_dicts(res))
|
||||
if previous_documento:
|
||||
self._restore_master_selection(previous_documento)
|
||||
|
||||
def _err(ex):
|
||||
messagebox.showerror(
|
||||
@@ -299,6 +318,10 @@ class StoricoPickingListWindow(ctk.CTkToplevel):
|
||||
def _fill_master(self, rows: list[dict[str, Any]]) -> None:
|
||||
self.master_tree.delete(*self.master_tree.get_children(""))
|
||||
self.detail_tree.delete(*self.detail_tree.get_children(""))
|
||||
self._selected_documento = None
|
||||
self._selected_stato_operativo = ""
|
||||
self._detail_rows = []
|
||||
self._update_residual_button()
|
||||
for index, row in enumerate(rows):
|
||||
stato = str(row.get("StatoOperativo") or "")
|
||||
is_open_with_shipped = (
|
||||
@@ -335,17 +358,26 @@ class StoricoPickingListWindow(ctk.CTkToplevel):
|
||||
def _on_master_select(self, _event=None) -> None:
|
||||
selected = self.master_tree.selection()
|
||||
if not selected:
|
||||
self._selected_documento = None
|
||||
self._selected_stato_operativo = ""
|
||||
self._detail_rows = []
|
||||
self._update_residual_button()
|
||||
return
|
||||
values = self.master_tree.item(selected[0], "values")
|
||||
if not values:
|
||||
return
|
||||
documento = values[0]
|
||||
self._selected_documento = str(documento)
|
||||
self._selected_stato_operativo = str(values[7] if len(values) > 7 else "")
|
||||
self._detail_rows = []
|
||||
self._update_residual_button()
|
||||
|
||||
async def _job():
|
||||
return await self.db_client.query_json(SQL_STORICO_PL_DETAILS, {"documento": documento})
|
||||
|
||||
def _ok(res):
|
||||
self._fill_detail(_rows_to_dicts(res))
|
||||
self._update_residual_button()
|
||||
|
||||
def _err(ex):
|
||||
messagebox.showerror(
|
||||
@@ -372,6 +404,7 @@ class StoricoPickingListWindow(ctk.CTkToplevel):
|
||||
|
||||
def _fill_detail(self, rows: list[dict[str, Any]]) -> None:
|
||||
self.detail_tree.delete(*self.detail_tree.get_children(""))
|
||||
self._detail_rows = rows
|
||||
for index, row in enumerate(rows):
|
||||
is_open_shipped = str(row.get("StatoDocumento") or "") == "P" and int(row.get("Cella") or 0) == 9999
|
||||
done = str(row.get("StatoDocumento") or "") == "D" or int(row.get("Cella") or 0) == 9999
|
||||
@@ -394,6 +427,117 @@ class StoricoPickingListWindow(ctk.CTkToplevel):
|
||||
tags=merge_tags(zebra_tag(index), tag),
|
||||
)
|
||||
|
||||
def _update_residual_button(self) -> None:
|
||||
"""Enable the bulk shipment button only for closed picking lists with residual UDCs."""
|
||||
|
||||
enabled = self._selected_stato_operativo == "Chiusa ERP con residui"
|
||||
try:
|
||||
self.btn_ship_residuals.configure(state="normal" if enabled else "disabled")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def _restore_master_selection(self, documento: str) -> None:
|
||||
"""Re-select a document after a reload, when it is still visible."""
|
||||
|
||||
for iid in self.master_tree.get_children(""):
|
||||
values = self.master_tree.item(iid, "values")
|
||||
if values and str(values[0]) == str(documento):
|
||||
self.master_tree.selection_set(iid)
|
||||
self.master_tree.focus(iid)
|
||||
self.master_tree.see(iid)
|
||||
self._on_master_select()
|
||||
return
|
||||
|
||||
def _residual_pallets_from_rows(self, rows: list[dict[str, Any]]) -> list[str]:
|
||||
"""Return distinct residual UDCs that are not already in 7G.1.1."""
|
||||
|
||||
pallets: list[str] = []
|
||||
seen: set[str] = set()
|
||||
for row in rows:
|
||||
pallet = str(row.get("Pallet") or "").strip()
|
||||
if not pallet or pallet in seen:
|
||||
continue
|
||||
try:
|
||||
cella = int(row.get("Cella") or 0)
|
||||
except Exception:
|
||||
cella = 0
|
||||
if cella == 9999:
|
||||
continue
|
||||
seen.add(pallet)
|
||||
pallets.append(pallet)
|
||||
return pallets
|
||||
|
||||
def _operator_login(self) -> str:
|
||||
"""Return the user recorded on generated warehouse movements."""
|
||||
|
||||
login = str(getattr(self.session, "login", "") or "").strip()
|
||||
return login or "warehouse_ui"
|
||||
|
||||
def _ship_selected_residuals(self) -> None:
|
||||
"""Move all residual UDCs of the selected closed PL to the shipment cell 7G.1.1."""
|
||||
|
||||
if self._selected_stato_operativo != "Chiusa ERP con residui" or not self._selected_documento:
|
||||
return
|
||||
estimated = self._residual_pallets_from_rows(self._detail_rows)
|
||||
count_text = str(len(estimated)) if estimated else "le"
|
||||
if not messagebox.askyesno(
|
||||
loc_text("history.picking.msg.title", catalog=self._locale_catalog, default="Storico Picking List"),
|
||||
(
|
||||
f"Documento {self._selected_documento}\n\n"
|
||||
f"Verranno versate in 7G.1.1 {count_text} UDC residue della picking list chiusa.\n"
|
||||
"L'operazione registra i movimenti nello storico UDC.\n\n"
|
||||
"Procedere?"
|
||||
),
|
||||
parent=self,
|
||||
):
|
||||
return
|
||||
|
||||
documento = self._selected_documento
|
||||
utente = self._operator_login()
|
||||
|
||||
async def _job():
|
||||
detail_res = await self.db_client.query_json(SQL_STORICO_PL_DETAILS, {"documento": documento})
|
||||
detail_rows = _rows_to_dicts(detail_res)
|
||||
pallets = self._residual_pallets_from_rows(detail_rows)
|
||||
results: list[dict[str, Any]] = []
|
||||
for pallet in pallets:
|
||||
result = await move_pallet_async(
|
||||
self.db_client,
|
||||
barcode_pallet=pallet,
|
||||
target_idcella=9999,
|
||||
target_barcode_cella="9000000",
|
||||
utente=utente,
|
||||
)
|
||||
results.append(result)
|
||||
return {"pallets": pallets, "results": results}
|
||||
|
||||
def _ok(res):
|
||||
pallets = res.get("pallets", []) if isinstance(res, dict) else []
|
||||
results = res.get("results", []) if isinstance(res, dict) else []
|
||||
moved = sum(1 for row in results if int(row.get("ok") or 0) == 1)
|
||||
messagebox.showinfo(
|
||||
loc_text("history.picking.msg.title", catalog=self._locale_catalog, default="Storico Picking List"),
|
||||
f"Documento {documento}\nUDC residue trovate: {len(pallets)}\nUDC versate in 7G.1.1: {moved}",
|
||||
parent=self,
|
||||
)
|
||||
self._selected_documento = str(documento)
|
||||
self._load_master()
|
||||
|
||||
def _err(ex):
|
||||
messagebox.showerror(
|
||||
loc_text("history.picking.msg.title", catalog=self._locale_catalog, default="Storico Picking List"),
|
||||
f"Versamento residui fallito:\n{ex}",
|
||||
parent=self,
|
||||
)
|
||||
|
||||
self._async.run(
|
||||
_job(),
|
||||
_ok,
|
||||
_err,
|
||||
busy=self._busy,
|
||||
message=f"Verso residui PL {documento} in 7G.1.1...",
|
||||
)
|
||||
|
||||
|
||||
def open_storico_pickinglist_window(parent: tk.Misc, db_client, session=None) -> tk.Misc:
|
||||
"""Open the picking-list history window."""
|
||||
|
||||
@@ -27,7 +27,7 @@ MODULE_VERSIONS: dict[str, str] = {
|
||||
"prenota_sprenota_sql": "1.0.0",
|
||||
"reset_corsie": "1.0.0",
|
||||
"search_pallets": "1.0.0",
|
||||
"storico_pickinglist": "1.0.2",
|
||||
"storico_pickinglist": "1.0.3",
|
||||
"storico_udc": "1.0.0",
|
||||
"tooltips": "1.0.0",
|
||||
"ui_theme": "1.0.0",
|
||||
|
||||
Reference in New Issue
Block a user