Alpha6 barcode non scaffalate e bypass login
This commit is contained in:
154
docs/review/module_gestione_pickinglist.md
Normal file
154
docs/review/module_gestione_pickinglist.md
Normal file
@@ -0,0 +1,154 @@
|
||||
# Modulo `gestione_pickinglist.py`
|
||||
|
||||
## Scopo
|
||||
|
||||
Il modulo implementa la finestra operativa "Gestione Picking List". La finestra serve al backoffice/magazzino per:
|
||||
|
||||
- visualizzare le picking list ancora lavorabili;
|
||||
- selezionare una sola lista alla volta;
|
||||
- visualizzare le UDC residue della lista selezionata;
|
||||
- prenotare una lista per darle priorita' alta sul barcode;
|
||||
- s-prenotare la lista correntemente prenotata;
|
||||
- mantenere UI reattiva tramite query asincrone, overlay e spinner.
|
||||
|
||||
La finestra lavora sulla vista residuale `dbo.py_ViewPackingListRestante`, quindi mostra cio' che resta da scaricare, non necessariamente il documento ERP completo. Il documento completo e' invece esposto dalla form "Storico Picking List".
|
||||
|
||||
## Processo logico
|
||||
|
||||
### Apertura finestra
|
||||
|
||||
1. `open_pickinglist_window` crea o riporta in primo piano una singola finestra.
|
||||
2. La finestra viene inizialmente nascosta con `withdraw` e alpha `0.0`.
|
||||
3. Viene creato `GestionePickingListFrame`.
|
||||
4. Dopo la stabilizzazione layout, `_first_show` avvia il primo caricamento.
|
||||
5. La finestra viene mostrata solo quando il layout e' pronto, riducendo flicker.
|
||||
|
||||
### Caricamento lista alta
|
||||
|
||||
1. `reload_from_db` esegue `SQL_PL`.
|
||||
2. `SQL_PL` legge da `dbo.py_ViewPackingListRestante`.
|
||||
3. Il risultato viene normalizzato con `_rows_to_dicts`.
|
||||
4. `_refresh_mid_rows` ricrea la tabella alta.
|
||||
5. Ogni riga e' rappresentata da un `PLRow`, che incapsula dizionario dati e checkbox.
|
||||
|
||||
### Selezione lista
|
||||
|
||||
1. Il click sul checkbox chiama `on_row_checked`.
|
||||
2. La selezione e' esclusiva: eventuali altre righe selezionate vengono deselezionate.
|
||||
3. Il documento selezionato viene salvato in `detail_doc`.
|
||||
4. Viene eseguita `SQL_PL_DETAILS`.
|
||||
5. Il dettaglio viene messo in cache in `_detail_cache`.
|
||||
6. `_refresh_details_incremental` ordina e renderizza il dettaglio con `tksheet`.
|
||||
|
||||
### Ricarica
|
||||
|
||||
`reload_from_db` preserva il documento selezionato quando possibile. Questo e' importante per il caso operativo in cui il barcode sta scaricando UDC mentre l'operatore aggiorna la finestra.
|
||||
|
||||
Flusso:
|
||||
|
||||
1. `_selected_documento_for_reload` determina il documento da mantenere.
|
||||
2. La griglia alta viene ricaricata.
|
||||
3. `_reselect_documento_after_reload` riseleziona la stessa lista se ancora presente.
|
||||
4. Il dettaglio viene ricaricato dalla vista residuale.
|
||||
|
||||
### Prenotazione
|
||||
|
||||
1. `on_prenota` verifica il permesso `pickinglist.prenota`.
|
||||
2. Richiede una riga selezionata.
|
||||
3. Se `IDStato == 1`, l'operazione e' no-op.
|
||||
4. Verifica sessione operatore valida.
|
||||
5. Chiama `sp_xExePackingListPallet_async(..., Azione="P")`.
|
||||
6. Se `rc == 0`, logga audit e ricarica la griglia mantenendo il documento.
|
||||
|
||||
### S-prenotazione
|
||||
|
||||
1. `on_sprenota` verifica il permesso `pickinglist.sprenota`.
|
||||
2. Richiede una riga selezionata.
|
||||
3. Se `IDStato == 0`, l'operazione e' no-op.
|
||||
4. Chiama `sp_xExePackingListPallet_async(..., Azione="S")`.
|
||||
5. Se `rc == 0`, logga audit e ricarica.
|
||||
|
||||
## Semantica operativa
|
||||
|
||||
La UI non implementa toggle implicito. I pulsanti hanno semantica esplicita:
|
||||
|
||||
| Pulsante | Effetto |
|
||||
| --- | --- |
|
||||
| `Prenota` | porta la lista selezionata a priorita' alta se non gia' prenotata |
|
||||
| `S-prenota` | libera la lista selezionata se e' la lista attiva |
|
||||
|
||||
Premere piu' volte `Prenota` sulla stessa lista gia' prenotata non la libera. Premere piu' volte `S-prenota` su lista non prenotata non la prenota.
|
||||
|
||||
## Strutture dati principali
|
||||
|
||||
| Nome | Tipo | Ruolo |
|
||||
| --- | --- | --- |
|
||||
| `rows_models` | `list[PLRow]` | modelli riga della griglia alta |
|
||||
| `_detail_cache` | `dict[Any, list]` | cache righe dettaglio per documento |
|
||||
| `detail_doc` | `Any` | documento correntemente selezionato |
|
||||
| `_detail_sort_key` | `str | None` | colonna dettaglio ordinata |
|
||||
| `_detail_sort_reverse` | `bool` | verso ordinamento dettaglio |
|
||||
| `pl_table` | `ScrollTable` | tabella custom griglia alta |
|
||||
| `detail_sheet` | `tksheet.Sheet` | griglia dettaglio ad alto volume |
|
||||
| `spinner` | `ToolbarSpinner` | feedback leggero toolbar |
|
||||
| `busy` | `InlineBusyOverlay` | overlay query/operazioni lente |
|
||||
|
||||
## Funzioni e classi principali
|
||||
|
||||
| Oggetto | Firma | Ruolo |
|
||||
| --- | --- | --- |
|
||||
| `_rows_to_dicts` | `_rows_to_dicts(res: Dict[str, Any]) -> List[Dict[str, Any]]` | normalizza payload DB |
|
||||
| `_s` | `_s(v) -> str` | converte `None` in stringa vuota |
|
||||
| `_first` | `_first(d: Dict[str, Any], keys: List[str], default: str = "")` | estrae il primo campo valorizzato |
|
||||
| `ColSpec` | `dataclass(title, key, width, anchor)` | descrive una colonna tabellare |
|
||||
| `ToolbarSpinner` | classe | animazione leggera toolbar |
|
||||
| `ScrollTable` | classe | tabella custom per griglia alta |
|
||||
| `PLRow` | classe | modello riga PL + checkbox |
|
||||
| `GestionePickingListFrame` | classe | frame principale |
|
||||
| `_build_detail_sheet` | `_build_detail_sheet(self)` | crea tksheet dettaglio |
|
||||
| `_detail_rows_to_sheet_data` | `_detail_rows_to_sheet_data(self, rows)` | converte righe DB in righe tksheet |
|
||||
| `_load_detail_sheet_data` | `_load_detail_sheet_data(self, data)` | applica headers, dati e zebra |
|
||||
| `_refresh_mid_rows` | `_refresh_mid_rows(self, rows)` | ricrea griglia alta |
|
||||
| `_get_selected_model` | `_get_selected_model(self) -> Optional[PLRow]` | ritorna riga selezionata |
|
||||
| `on_row_checked` | `on_row_checked(self, model: PLRow, is_checked: bool)` | carica dettaglio della lista |
|
||||
| `reload_from_db` | `reload_from_db(self, first=False, reselect_documento=None)` | aggiorna griglia alta |
|
||||
| `_refresh_details` | `_refresh_details(self)` | ridisegna dettaglio da cache |
|
||||
| `_refresh_details_incremental` | `_refresh_details_incremental(self, batch_size=25)` | render dettaglio con overlay |
|
||||
| `on_prenota` | `on_prenota(self)` | prenota lista selezionata |
|
||||
| `on_sprenota` | `on_sprenota(self)` | s-prenota lista selezionata |
|
||||
| `create_frame` | `create_frame(parent, *, db_client=None, conn_str=None, session=None)` | factory frame |
|
||||
| `open_pickinglist_window` | `open_pickinglist_window(parent, db_client, session=None) -> tk.Misc` | entry point finestra |
|
||||
|
||||
## Query runtime
|
||||
|
||||
### `SQL_PL`
|
||||
|
||||
Aggrega `dbo.py_ViewPackingListRestante` per documento. Conta UDC con:
|
||||
|
||||
```sql
|
||||
COUNT(DISTINCT NULLIF(LTRIM(RTRIM(CAST(Pallet AS varchar(32)))), '')) AS Pallet
|
||||
```
|
||||
|
||||
Questo evita il doppio conteggio di UDC multi-lotto.
|
||||
|
||||
### `SQL_PL_DETAILS`
|
||||
|
||||
Ritorna tutte le righe residuali della lista selezionata, ordinate per `Ordinamento`.
|
||||
|
||||
## Effetti sul database
|
||||
|
||||
Il modulo UI non aggiorna direttamente tabelle. Tutte le modifiche passano da `sp_xExePackingListPallet_async`, che esegue `dbo.py_sp_xExePackingListPallet`.
|
||||
|
||||
Effetti indiretti:
|
||||
|
||||
- aggiorna `dbo.PyPickingListReservation`;
|
||||
- aggiorna `dbo.Celle.IDStato`;
|
||||
- scrive audit applicativo con `log_user_action`;
|
||||
- la stored puo' scrivere log legacy tramite `sp_LogPackingList`.
|
||||
|
||||
## Rischi e note review
|
||||
|
||||
- La vista residuale cambia mentre il barcode scarica UDC: per questo `Ricarica` conserva la selezione e ricarica il dettaglio.
|
||||
- La tabella alta e il dettaglio possono non avere lo stesso numero di righe dello storico, perche' qui si vedono solo residui.
|
||||
- La convivenza C#/Python dipende dalla presenza degli oggetti `py_*`.
|
||||
- La selezione e' esclusiva lato UI, ma il secondo livello F2 barcode dipende dalla logica barcode/stato DB, non da una seconda prenotazione backoffice.
|
||||
Reference in New Issue
Block a user