Freeze stato gestione picking e storico
This commit is contained in:
61
INSTALL_PRODUZIONE_20260610.md
Normal file
61
INSTALL_PRODUZIONE_20260610.md
Normal 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.
|
||||||
@@ -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:
|
||||||
|
|||||||
@@ -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
|
||||||
|
Documento,
|
||||||
|
COUNT(*) AS TotUDC,
|
||||||
|
SUM(CASE WHEN p.HasResiduo = 0 AND p.HasSpedito = 1 THEN 1 ELSE 0 END) AS RigheSpedite,
|
||||||
|
SUM(CASE WHEN p.HasResiduo = 1 THEN 1 ELSE 0 END) AS RigheResidue
|
||||||
|
FROM pallets p
|
||||||
|
GROUP BY Documento
|
||||||
)
|
)
|
||||||
SELECT
|
SELECT
|
||||||
Documento,
|
m.Documento,
|
||||||
DataDocumento,
|
m.DataDocumento,
|
||||||
StatoDocumento,
|
m.StatoDocumento,
|
||||||
NAZIONE,
|
m.NAZIONE,
|
||||||
CodNazione,
|
m.CodNazione,
|
||||||
TotUDC,
|
COALESCE(a.TotUDC, 0) AS TotUDC,
|
||||||
RigheResidue,
|
COALESCE(a.RigheResidue, 0) AS RigheResidue,
|
||||||
RigheSpedite,
|
COALESCE(a.RigheSpedite, 0) AS RigheSpedite,
|
||||||
RigheTotali,
|
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 = """
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
Reference in New Issue
Block a user