Freeze stato gestione picking e storico

This commit is contained in:
2026-06-15 20:56:35 +02:00
parent 8f9957a2db
commit 29900b8b09
4 changed files with 135 additions and 30 deletions

View File

@@ -0,0 +1,61 @@
# Installazione produzione - Warehouse/FlyWMS bridge
## Ordine consigliato
1. Fare backup del database `Mediseawall`.
2. Copiare il contenuto dello zip in una cartella locale, ad esempio `C:\flywms`.
3. Installare le dipendenze Python:
```bat
python -m pip install -r requirements.txt
```
4. In SSMS, sul database `Mediseawall`, lanciare:
```text
apply_python_parallel_pickinglist_patch.sql
apply_online_history_forms_patch.sql
```
## Cosa fanno gli script
- `apply_python_parallel_pickinglist_patch.sql` crea il ramo SQL Python per gestione picking list, senza modificare le stored procedure C# legacy.
- `apply_online_history_forms_patch.sql` crea le viste Python-only per "Storico Picking List".
- "Storico movimenti UDC" non richiede script dedicati: legge in sola lettura `MagazziniPallet`, `Celle` e `XMag_GiacenzaPalletPlistChiuse`.
## Rollback SQL
Se serve tornare indietro sugli oggetti Python, usare:
```text
rollback_online_history_forms_patch.sql
rollback_python_parallel_pickinglist_patch.sql
```
La tabella `dbo.PyPickingListReservation`, se creata, puo' rimanere anche in caso di rollback perche' il C# legacy non la usa.
## Avvio
Backoffice con console:
```bat
python main.py
```
Backoffice senza console:
```bat
pythonw warehouse.pyw
```
Barcode senza console:
```bat
pythonw barcode.pyw
```
Se si usa un collegamento Windows, impostare anche la cartella "Da" alla cartella dell'applicazione, ad esempio `C:\flywms`.
## File esclusi dal pacchetto
Il pacchetto non include `db_connection.json`, log, cache Python e vecchi zip locali. Alla prima apertura il programma chiedera' la configurazione DB se `db_connection.json` non esiste.

View File

@@ -203,7 +203,7 @@ if _MODULE_LOG_ENABLED:
# -------------------- SQL -------------------- # -------------------- SQL --------------------
SQL_PL = """ SQL_PL = """
SELECT SELECT
COUNT(DISTINCT Pallet) AS Pallet, COUNT(DISTINCT NULLIF(LTRIM(RTRIM(CAST(Pallet AS varchar(32)))), '')) AS Pallet,
COUNT(DISTINCT Lotto) AS Lotto, COUNT(DISTINCT Lotto) AS Lotto,
COUNT(DISTINCT Articolo) AS Articolo, COUNT(DISTINCT Articolo) AS Articolo,
COUNT(DISTINCT Descrizione) AS Descrizione, COUNT(DISTINCT Descrizione) AS Descrizione,
@@ -907,13 +907,25 @@ class GestionePickingListFrame(ctk.CTkFrame):
self.after_idle(_paint) self.after_idle(_paint)
break break
def _reselect_documento_after_reload(self, documento: str): def _reselect_documento_after_reload(self, documento: str) -> bool:
"""(Opzionale) Dopo un reload DB, riseleziona la PL con lo stesso Documento.""" """After a reload, reselect the same document and reload its details."""
for m in self.rows_models: for m in self.rows_models:
if _s(m.pl.get("Documento")) == _s(documento): if _s(m.pl.get("Documento")) == _s(documento):
self._detail_cache.pop(documento, None)
m.set_checked(True) m.set_checked(True)
self.on_row_checked(m, True) self.on_row_checked(m, True)
break return True
return False
def _selected_documento_for_reload(self) -> str | None:
"""Return the document that should survive a toolbar reload."""
selected = self._get_selected_model()
if selected is not None:
documento = _s(selected.pl.get("Documento"))
return documento or None
documento = _s(self.detail_doc)
return documento or None
# ----- eventi ----- # ----- eventi -----
@_log_call() @_log_call()
@@ -967,6 +979,8 @@ class GestionePickingListFrame(ctk.CTkFrame):
@_log_call() @_log_call()
def reload_from_db(self, first: bool = False, reselect_documento: str | None = None): def reload_from_db(self, first: bool = False, reselect_documento: str | None = None):
"""Load or reload the picking list summary table from the database.""" """Load or reload the picking list summary table from the database."""
if reselect_documento is None and not first:
reselect_documento = self._selected_documento_for_reload()
self.spinner.start(" Carico…") # spinner ON self.spinner.start(" Carico…") # spinner ON
async def _job(): async def _job():
_log_sql("SQL_PL", SQL_PL, {}) _log_sql("SQL_PL", SQL_PL, {})
@@ -976,7 +990,17 @@ class GestionePickingListFrame(ctk.CTkFrame):
_log_dataset("SQL_PL", rows) _log_dataset("SQL_PL", rows)
self._refresh_mid_rows(rows) self._refresh_mid_rows(rows)
if reselect_documento: if reselect_documento:
self.after_idle(lambda doc=reselect_documento: self._reselect_documento_after_reload(doc)) def _reselect_or_clear(doc=reselect_documento):
found = self._reselect_documento_after_reload(doc)
if not found:
self.detail_doc = None
self._draw_details_hint()
self.spinner.stop()
self.busy.hide()
self.after_idle(_reselect_or_clear)
else:
self.detail_doc = None
self._draw_details_hint()
self.spinner.stop() # spinner OFF self.spinner.stop() # spinner OFF
# se era il primo load, ripristina il cursore standard # se era il primo load, ripristina il cursore standard
if self._first_loading: if self._first_loading:

View File

@@ -21,46 +21,66 @@ __version__ = module_version(__name__)
SQL_STORICO_PL = """ SQL_STORICO_PL = """
WITH base AS ( WITH base AS (
SELECT * SELECT
*,
NULLIF(LTRIM(RTRIM(CAST(Pallet AS varchar(32)))), '') AS PalletKey
FROM dbo.py_XMag_ViewPackingListStorico FROM dbo.py_XMag_ViewPackingListStorico
), ),
agg AS ( pallets AS (
SELECT
Documento,
PalletKey,
MAX(CASE WHEN Cella <> 9999 OR Cella IS NULL THEN 1 ELSE 0 END) AS HasResiduo,
MAX(CASE WHEN Cella = 9999 THEN 1 ELSE 0 END) AS HasSpedito
FROM base
WHERE PalletKey IS NOT NULL
GROUP BY Documento, PalletKey
),
meta AS (
SELECT SELECT
Documento, Documento,
MAX(DataDocumento) AS DataDocumento, MAX(DataDocumento) AS DataDocumento,
MAX(StatoDocumento) AS StatoDocumento, MAX(StatoDocumento) AS StatoDocumento,
MAX(NAZIONE) AS NAZIONE, MAX(NAZIONE) AS NAZIONE,
MAX(CodNazione) AS CodNazione, MAX(CodNazione) AS CodNazione,
COUNT(DISTINCT Pallet) AS TotUDC,
SUM(CASE WHEN Cella = 9999 THEN 1 ELSE 0 END) AS RigheSpedite,
SUM(CASE WHEN Cella <> 9999 OR Cella IS NULL THEN 1 ELSE 0 END) AS RigheResidue,
COUNT(*) AS RigheTotali, COUNT(*) AS RigheTotali,
MIN(Ordinamento) AS PrimoOrdine, MIN(Ordinamento) AS PrimoOrdine,
MAX(IDStato) AS IDStato MAX(IDStato) AS IDStato
FROM base FROM base
GROUP BY Documento GROUP BY Documento
) ),
agg AS (
SELECT SELECT
Documento, Documento,
DataDocumento, COUNT(*) AS TotUDC,
StatoDocumento, SUM(CASE WHEN p.HasResiduo = 0 AND p.HasSpedito = 1 THEN 1 ELSE 0 END) AS RigheSpedite,
NAZIONE, SUM(CASE WHEN p.HasResiduo = 1 THEN 1 ELSE 0 END) AS RigheResidue
CodNazione, FROM pallets p
TotUDC, GROUP BY Documento
RigheResidue, )
RigheSpedite, SELECT
RigheTotali, m.Documento,
m.DataDocumento,
m.StatoDocumento,
m.NAZIONE,
m.CodNazione,
COALESCE(a.TotUDC, 0) AS TotUDC,
COALESCE(a.RigheResidue, 0) AS RigheResidue,
COALESCE(a.RigheSpedite, 0) AS RigheSpedite,
m.RigheTotali,
CASE CASE
WHEN StatoDocumento = 'D' THEN 'Chiusa' WHEN m.StatoDocumento = 'D' AND COALESCE(a.RigheResidue, 0) > 0 THEN 'Chiusa ERP con residui'
WHEN RigheResidue = 0 THEN 'Esaurita' WHEN m.StatoDocumento = 'D' THEN 'Chiusa'
WHEN RigheSpedite > 0 THEN 'In corso' WHEN COALESCE(a.RigheResidue, 0) = 0 THEN 'Esaurita'
WHEN COALESCE(a.RigheSpedite, 0) > 0 THEN 'In corso'
ELSE 'Da lavorare' ELSE 'Da lavorare'
END AS StatoOperativo, END AS StatoOperativo,
IDStato, m.IDStato,
PrimoOrdine m.PrimoOrdine
FROM agg FROM meta m
WHERE (:documento IS NULL OR CAST(Documento AS varchar(32)) LIKE CONCAT('%', :documento, '%')) LEFT JOIN agg a ON a.Documento = m.Documento
ORDER BY Documento DESC; WHERE (:documento IS NULL OR CAST(m.Documento AS varchar(32)) LIKE CONCAT('%', :documento, '%'))
ORDER BY m.Documento DESC;
""" """
SQL_STORICO_PL_DETAILS = """ SQL_STORICO_PL_DETAILS = """

View File

@@ -20,14 +20,14 @@ MODULE_VERSIONS: dict[str, str] = {
"db_config": "1.0.0", "db_config": "1.0.0",
"gestione_aree": "1.0.0", "gestione_aree": "1.0.0",
"gestione_layout": "1.0.0", "gestione_layout": "1.0.0",
"gestione_pickinglist": "1.0.0", "gestione_pickinglist": "1.0.2",
"gestione_scarico": "1.0.0", "gestione_scarico": "1.0.0",
"locale_text": "1.0.0", "locale_text": "1.0.0",
"login_window": "1.0.0", "login_window": "1.0.0",
"prenota_sprenota_sql": "1.0.0", "prenota_sprenota_sql": "1.0.0",
"reset_corsie": "1.0.0", "reset_corsie": "1.0.0",
"search_pallets": "1.0.0", "search_pallets": "1.0.0",
"storico_pickinglist": "1.0.0", "storico_pickinglist": "1.0.2",
"storico_udc": "1.0.0", "storico_udc": "1.0.0",
"tooltips": "1.0.0", "tooltips": "1.0.0",
"ui_theme": "1.0.0", "ui_theme": "1.0.0",