Alpha6 barcode non scaffalate e bypass login
This commit is contained in:
44
docs/review/README.md
Normal file
44
docs/review/README.md
Normal file
@@ -0,0 +1,44 @@
|
||||
# Documentazione review Warehouse / FlyWMS bridge
|
||||
|
||||
Questa cartella contiene la documentazione tecnica destinata alla review del codice Python e SQL.
|
||||
|
||||
## Obiettivi
|
||||
|
||||
- Descrivere il processo logico implementato da ogni modulo.
|
||||
- Documentare firme, parametri, ritorni e responsabilita' di funzioni e classi.
|
||||
- Documentare SQL, viste, stored procedure, tabelle coinvolte ed effetti collaterali.
|
||||
- Aggiungere commenti inline nel codice solo dove il comportamento non e' auto-evidente.
|
||||
|
||||
## Criterio di documentazione
|
||||
|
||||
La documentazione e' divisa in tre livelli.
|
||||
|
||||
1. Processo logico: flussi operativi, eventi UI, query, effetti sul database.
|
||||
2. API interna: classi, funzioni, parametri, valori di ritorno, errori, dipendenze.
|
||||
3. Dettaglio implementativo: strutture dati, cicli, dedupliche, transazioni, side effect.
|
||||
|
||||
## Ambito escluso
|
||||
|
||||
Questa review non considera:
|
||||
|
||||
- `__pycache__`
|
||||
- log applicativi
|
||||
- zip di distribuzione
|
||||
- cartelle `_package_*`
|
||||
- cartella `trash`
|
||||
- build HTML generata in `docs/_build`
|
||||
|
||||
## Primo lotto documentato
|
||||
|
||||
- [Modulo storico picking list](module_storico_pickinglist.md)
|
||||
- [SQL storico picking list](sql_storico_pickinglist.md)
|
||||
- [Modulo gestione picking list](module_gestione_pickinglist.md)
|
||||
- [Modulo prenota/sprenota SQL](module_prenota_sprenota_sql.md)
|
||||
- [SQL prenotazione picking list](sql_pickinglist_reservation.md)
|
||||
|
||||
## Prossimi lotti consigliati
|
||||
|
||||
1. `gestione_scarico.py`
|
||||
2. `barcode_service.py` e `barcode_repository.py`
|
||||
3. `main.py`, `login_window.py`, `db_config.py`
|
||||
4. Moduli di visualizzazione: `gestione_layout.py`, `reset_corsie.py`, `search_pallets.py`, `view_celle_multi_udc.py`
|
||||
68
docs/review/barcode_error_handling.md
Normal file
68
docs/review/barcode_error_handling.md
Normal file
@@ -0,0 +1,68 @@
|
||||
# Barcode: logging errori e transazioni
|
||||
|
||||
## Problema rilevato
|
||||
|
||||
Durante uno scarico picking list dal lettore barcode, l'operazione DB e' arrivata a buon fine ma il client ha mostrato un'eccezione. I file standard output/error erano vuoti.
|
||||
|
||||
Con `pythonw` non e' sufficiente affidarsi a stdout/stderr: eccezioni Tk, future asincrone o callback possono non arrivare al wrapper principale.
|
||||
|
||||
## Strategia adottata
|
||||
|
||||
Il client barcode ora registra errori in modo esplicito su `warehouse_fatal.log` e su `barcode_wms_launch.log`.
|
||||
|
||||
Canali coperti:
|
||||
|
||||
- `sys.excepthook`
|
||||
- `threading.excepthook`
|
||||
- `Tk.report_callback_exception`
|
||||
- exception handler del loop `asyncio`
|
||||
- eccezioni delle operazioni asincrone barcode
|
||||
- errori del repository durante movimento DB
|
||||
- errori post-movimento durante ricostruzione stato UI
|
||||
|
||||
## Transazione movimento DB
|
||||
|
||||
Il movimento barcode passa da `BarcodeRepository.execute_legacy_move`.
|
||||
|
||||
La batch SQL usa:
|
||||
|
||||
```sql
|
||||
SET XACT_ABORT ON;
|
||||
```
|
||||
|
||||
ed e' eseguita con:
|
||||
|
||||
```python
|
||||
query_json(..., commit=True)
|
||||
```
|
||||
|
||||
Questo significa:
|
||||
|
||||
- successo: commit;
|
||||
- eccezione SQL/Python: rollback automatico della transazione SQLAlchemy;
|
||||
- il valore `@RC` restituito dalla stored legacy non viene interpretato come errore.
|
||||
|
||||
Nota importante: in `sp_xMagGestioneMagazziniPallet`, quando il movimento va a buon fine, `@RC` viene valorizzato con `@@IDENTITY`, cioe' con l'identificativo del movimento inserito. Quindi un valore diverso da zero e' compatibile con un movimento riuscito e non deve causare rollback.
|
||||
|
||||
## Distinzione tra movimento e refresh UI
|
||||
|
||||
Dopo un movimento riuscito, il service ricostruisce il messaggio UI interrogando picking list e tracciabilita'.
|
||||
|
||||
Se questa fase post-movimento fallisce:
|
||||
|
||||
- il movimento resta valido;
|
||||
- l'errore viene loggato;
|
||||
- l'utente vede `Movimento eseguito. Dettagli non aggiornati.`;
|
||||
- non viene mostrato il messaggio "transazione non completata".
|
||||
|
||||
Se invece fallisce la transazione vera:
|
||||
|
||||
- l'utente vede `Transazione non completata, ripeti l'operazione.`;
|
||||
- il dettaglio tecnico viene scritto nel log.
|
||||
|
||||
## File principali
|
||||
|
||||
- `runtime_support.py`
|
||||
- `barcode_client.py`
|
||||
- `barcode_repository.py`
|
||||
- `barcode_service.py`
|
||||
119
docs/review/documentation_plan.md
Normal file
119
docs/review/documentation_plan.md
Normal file
@@ -0,0 +1,119 @@
|
||||
# Piano operativo documentazione review
|
||||
|
||||
## Strategia
|
||||
|
||||
La documentazione viene prodotta per lotti piccoli e verificabili. Ogni lotto deve contenere:
|
||||
|
||||
- descrizione del processo logico;
|
||||
- tabella firme funzioni/classi;
|
||||
- documentazione SQL collegata;
|
||||
- eventuali commenti inline mirati;
|
||||
- compilazione Python dei moduli modificati.
|
||||
|
||||
## Lotto 1 - Storico picking list
|
||||
|
||||
Stato: completato.
|
||||
|
||||
File documentati:
|
||||
|
||||
- `storico_pickinglist.py`
|
||||
- `apply_python_pickinglist_history_views.sql`
|
||||
- `apply_online_history_forms_patch.sql`
|
||||
- query runtime `SQL_STORICO_PL`
|
||||
- query runtime `SQL_STORICO_PL_DETAILS`
|
||||
- movimento collegato `move_pallet_async`
|
||||
|
||||
Motivo priorita:
|
||||
|
||||
- contiene una nuova funzione con effetti sul database;
|
||||
- gestisce casi critici `Chiusa ERP con residui`;
|
||||
- usa convenzioni operative `1000 / Non scaff.` e `9999 / 7G.1.1`.
|
||||
|
||||
## Lotto 2 - Gestione picking list
|
||||
|
||||
Stato: completato.
|
||||
|
||||
File target:
|
||||
|
||||
- `gestione_pickinglist.py`
|
||||
- `prenota_sprenota_sql.py`
|
||||
- `apply_python_parallel_pickinglist_patch.sql`
|
||||
- `rollback_python_parallel_pickinglist_patch.sql`
|
||||
|
||||
Aspetti da documentare:
|
||||
|
||||
- prenotazione/sprenotazione Python-only;
|
||||
- differenza tra vista residua e vista storica;
|
||||
- aggiornamento griglia alta/bassa;
|
||||
- stato `IDStato`;
|
||||
- separazione da C# legacy;
|
||||
- limiti concorrenza.
|
||||
|
||||
## Lotto 3 - Movimento UDC e scarico
|
||||
|
||||
File target:
|
||||
|
||||
- `gestione_scarico.py`
|
||||
- `storico_udc.py`
|
||||
|
||||
Aspetti da documentare:
|
||||
|
||||
- batch `SQL_SCARICA_UDC`;
|
||||
- movimento `P` e `V`;
|
||||
- audit utenti/date;
|
||||
- cella source e target;
|
||||
- fallback SPED in storico UDC.
|
||||
|
||||
## Lotto 4 - Barcode
|
||||
|
||||
File target:
|
||||
|
||||
- `barcode_client.py`
|
||||
- `barcode_service.py`
|
||||
- `barcode_repository.py`
|
||||
|
||||
Aspetti da documentare:
|
||||
|
||||
- stati operativi del barcode;
|
||||
- F1/F2 e priorita picking list;
|
||||
- gestione `9000000`;
|
||||
- mappatura 1:1 con comportamento C#;
|
||||
- stabilita di connessione DB.
|
||||
|
||||
## Lotto 5 - Avvio, autenticazione e configurazione
|
||||
|
||||
File target:
|
||||
|
||||
- `main.py`
|
||||
- `login_window.py`
|
||||
- `db_config.py`
|
||||
- `runtime_support.py`
|
||||
- `user_session.py`
|
||||
|
||||
Aspetti da documentare:
|
||||
|
||||
- single instance;
|
||||
- login operatore;
|
||||
- creazione configurazione DB;
|
||||
- gestione errori in `pythonw`;
|
||||
- shutdown ordinato.
|
||||
|
||||
## Lotto 6 - Visualizzazioni operative
|
||||
|
||||
File target:
|
||||
|
||||
- `gestione_layout.py`
|
||||
- `reset_corsie.py`
|
||||
- `search_pallets.py`
|
||||
- `view_celle_multi_udc.py`
|
||||
- `ui_tables.py`
|
||||
- `ui_theme.py`
|
||||
|
||||
Aspetti da documentare:
|
||||
|
||||
- griglie e layout;
|
||||
- query diagnostiche;
|
||||
- overlay async;
|
||||
- colori semantici;
|
||||
- export XLSX;
|
||||
- bonifica UDC fantasma.
|
||||
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.
|
||||
97
docs/review/module_prenota_sprenota_sql.md
Normal file
97
docs/review/module_prenota_sprenota_sql.md
Normal file
@@ -0,0 +1,97 @@
|
||||
# Modulo `prenota_sprenota_sql.py`
|
||||
|
||||
## Scopo
|
||||
|
||||
Il modulo contiene il port asincrono della stored procedure di prenotazione picking list. E' il ponte tra la UI Python e la stored SQL Python-only `dbo.py_sp_xExePackingListPallet`.
|
||||
|
||||
La funzione pubblica principale e':
|
||||
|
||||
```python
|
||||
async def sp_xExePackingListPallet_async(db, IDOperatore: int, Documento: str, Azione: str = "P") -> SPResult
|
||||
```
|
||||
|
||||
## Motivazione
|
||||
|
||||
Durante la fase di test sul campo devono convivere:
|
||||
|
||||
- applicazione C# legacy;
|
||||
- applicazione Python.
|
||||
|
||||
Per evitare collisioni, il Python non chiama `dbo.sp_xExePackingListPallet` legacy ma `dbo.py_sp_xExePackingListPallet`.
|
||||
|
||||
## Semantica parametro `Azione`
|
||||
|
||||
| Azione | Significato |
|
||||
| --- | --- |
|
||||
| `P` | prenota il documento |
|
||||
| `S` | s-prenota il documento |
|
||||
|
||||
Ogni altra azione ritorna:
|
||||
|
||||
```python
|
||||
SPResult(rc=-10, message="Azione non valida: ...")
|
||||
```
|
||||
|
||||
## Classe `SPResult`
|
||||
|
||||
Firma:
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class SPResult:
|
||||
rc: int = 0
|
||||
message: Optional[str] = ""
|
||||
id_result: Optional[int] = None
|
||||
```
|
||||
|
||||
Campi:
|
||||
|
||||
| Campo | Ruolo |
|
||||
| --- | --- |
|
||||
| `rc` | codice ritorno della stored |
|
||||
| `message` | messaggio errore o vuoto |
|
||||
| `id_result` | predisposizione per ID risultato, non usato oggi |
|
||||
|
||||
## Funzioni helper
|
||||
|
||||
| Funzione | Firma | Ruolo |
|
||||
| --- | --- | --- |
|
||||
| `_query_one_value` | `_query_one_value(db, sql: str, params: Dict[str, Any]) -> Optional[Any]` | helper generico scalar, oggi non centrale nel flusso |
|
||||
| `_query_all` | `_query_all(db, sql: str, params: Dict[str, Any]) -> List[Dict[str, Any]]` | normalizza query multi-riga |
|
||||
| `_execute` | `_execute(db, sql: str, params: Dict[str, Any]) -> int` | helper generico DML |
|
||||
| `sp_xExePackingListPallet_async` | vedi sopra | esegue stored Python-only |
|
||||
|
||||
## Processo logico `sp_xExePackingListPallet_async`
|
||||
|
||||
1. Normalizza `Azione` a maiuscolo.
|
||||
2. Valida `Azione in ("P", "S")`.
|
||||
3. Costruisce una batch SQL con output parameter `@RC`.
|
||||
4. Esegue:
|
||||
|
||||
```sql
|
||||
EXEC dbo.py_sp_xExePackingListPallet
|
||||
@IDOperatore = :IDOperatore,
|
||||
@Documento = :Documento,
|
||||
@Azione = :Azione,
|
||||
@RC = @RC OUTPUT;
|
||||
```
|
||||
|
||||
5. Legge il valore `RC`.
|
||||
6. Ritorna `SPResult(rc=rc, message="", id_result=None)`.
|
||||
7. In caso di eccezione ritorna `SPResult(rc=-1, message=str(exc), id_result=None)`.
|
||||
|
||||
## Commit
|
||||
|
||||
La chiamata usa:
|
||||
|
||||
```python
|
||||
commit=True
|
||||
```
|
||||
|
||||
Motivo: la stored modifica `PyPickingListReservation` e `Celle.IDStato`.
|
||||
|
||||
## Rischi e note review
|
||||
|
||||
- Gli helper `_query_one_value`, `_query_all`, `_execute` sono generici e non tutti sono essenziali al percorso attuale.
|
||||
- La funzione cattura le eccezioni e ritorna `SPResult(rc=-1)`, quindi la UI deve controllare sempre `rc`.
|
||||
- La transazione reale dipende dal comportamento di `query_json(..., commit=True)` nel client DB.
|
||||
150
docs/review/module_storico_pickinglist.md
Normal file
150
docs/review/module_storico_pickinglist.md
Normal file
@@ -0,0 +1,150 @@
|
||||
# Modulo `storico_pickinglist.py`
|
||||
|
||||
## Scopo
|
||||
|
||||
Il modulo implementa la finestra "Storico Picking List". La finestra permette di:
|
||||
|
||||
- cercare picking list storiche per numero documento;
|
||||
- visualizzare lo stato operativo sintetico di ciascuna lista;
|
||||
- visualizzare il dettaglio UDC/lotto/articolo della lista selezionata;
|
||||
- evidenziare liste chiuse, liste attive e casi anomali;
|
||||
- versare in blocco in `7G.1.1` le UDC residue di una lista classificata come `Chiusa ERP con residui`.
|
||||
|
||||
Il modulo legge da viste SQL dedicate al ramo Python. Non usa le viste legacy C# per evitare collisioni durante il periodo in cui i due programmi convivono sullo stesso database.
|
||||
|
||||
## Dipendenze principali
|
||||
|
||||
- `customtkinter` e `tkinter.ttk`: UI e griglie.
|
||||
- `AsyncRunner`: esecuzione asincrona delle query senza bloccare la GUI.
|
||||
- `InlineBusyOverlay`: indicatore visivo durante query e movimenti.
|
||||
- `move_pallet_async`: funzione centrale per registrare un movimento UDC verso una cella.
|
||||
- `ui_tables`: stile comune delle griglie.
|
||||
- `version_info`: versione visibile nel titolo finestra.
|
||||
- `window_placement`: posizionamento della finestra rispetto al launcher.
|
||||
|
||||
## Processo logico
|
||||
|
||||
### Apertura finestra
|
||||
|
||||
1. `open_storico_pickinglist_window(parent, db_client, session)` crea `StoricoPickingListWindow`.
|
||||
2. La finestra viene posizionata sotto il launcher con `place_window_fullsize_below_parent_later`.
|
||||
3. Dopo 300 ms viene chiamato `_load_master`.
|
||||
|
||||
### Caricamento griglia alta
|
||||
|
||||
1. `_load_master` legge il filtro `Documento`.
|
||||
2. Viene eseguita `SQL_STORICO_PL`.
|
||||
3. `_rows_to_dicts` normalizza il payload del DB.
|
||||
4. `_fill_master` svuota griglia alta e dettaglio.
|
||||
5. Per ogni documento viene calcolato il tag visuale:
|
||||
- `warning` se lista aperta con UDC gia' spedite;
|
||||
- `done` se lista chiusa o esaurita;
|
||||
- `active` se lista prenotata;
|
||||
- zebra tag per alternanza righe.
|
||||
6. La riga viene inserita nella griglia alta.
|
||||
|
||||
### Selezione documento
|
||||
|
||||
1. `_on_master_select` legge la riga selezionata.
|
||||
2. Memorizza:
|
||||
- `_selected_documento`
|
||||
- `_selected_stato_operativo`
|
||||
- `_detail_rows`
|
||||
3. Abilita o disabilita il pulsante "Versa residui in 7G.1.1".
|
||||
4. Esegue `SQL_STORICO_PL_DETAILS`.
|
||||
5. `_fill_detail` carica la griglia bassa.
|
||||
|
||||
### Versamento residui in 7G.1.1
|
||||
|
||||
Il pulsante e' abilitato solo se `_selected_stato_operativo == "Chiusa ERP con residui"`.
|
||||
|
||||
Flusso:
|
||||
|
||||
1. `_ship_selected_residuals` verifica lo stato selezionato.
|
||||
2. Calcola una stima delle UDC residue gia' presenti nel dettaglio UI.
|
||||
3. Mostra una conferma all'operatore.
|
||||
4. Dentro il job asincrono rilegge il dettaglio dal DB, cosi' evita dati UI obsoleti.
|
||||
5. `_residual_pallets_from_rows` deduplica le UDC e scarta quelle gia' in `IDCella = 9999`.
|
||||
6. Per ogni UDC residua chiama `move_pallet_async` con:
|
||||
- `target_idcella = 9999`
|
||||
- `target_barcode_cella = "9000000"`
|
||||
- `utente = login sessione` oppure `warehouse_ui`
|
||||
7. Al termine mostra un riepilogo.
|
||||
8. Ricarica la griglia alta e tenta di ripristinare la selezione del documento.
|
||||
|
||||
## Stati operativi
|
||||
|
||||
La colonna `Stato` della griglia alta deriva da SQL:
|
||||
|
||||
| Stato operativo | Condizione |
|
||||
| --- | --- |
|
||||
| `Chiusa ERP con residui` | `StatoDocumento = 'D'` e almeno una UDC non e' in `9999` |
|
||||
| `Chiusa` | `StatoDocumento = 'D'` e nessuna UDC residua |
|
||||
| `Esaurita` | tutte le UDC risultano spedite |
|
||||
| `In corso` | almeno una UDC e' spedita e almeno una resta da lavorare |
|
||||
| `Da lavorare` | nessuna UDC ancora spedita |
|
||||
|
||||
## Strutture dati interne
|
||||
|
||||
| Nome | Tipo | Ruolo |
|
||||
| --- | --- | --- |
|
||||
| `var_documento` | `tk.StringVar` | filtro documento digitato dall'utente |
|
||||
| `_selected_documento` | `str | None` | documento correntemente selezionato |
|
||||
| `_selected_stato_operativo` | `str` | stato operativo della riga selezionata |
|
||||
| `_detail_rows` | `list[dict[str, Any]]` | ultimo dettaglio normalizzato caricato nella griglia bassa |
|
||||
| `master_tree` | `ttk.Treeview` | griglia alta documenti |
|
||||
| `detail_tree` | `ttk.Treeview` | griglia bassa righe UDC |
|
||||
| `btn_ship_residuals` | `ctk.CTkButton` | azione di versamento massivo residui |
|
||||
|
||||
## Funzioni e classi
|
||||
|
||||
| Oggetto | Firma | Ruolo |
|
||||
| --- | --- | --- |
|
||||
| `_rows_to_dicts` | `_rows_to_dicts(res: dict[str, Any] | None) -> list[dict[str, Any]]` | normalizza payload DB in lista di dizionari |
|
||||
| `_format_date` | `_format_date(value: Any) -> str` | converte date SQL/SAMA in `dd/mm/yyyy` |
|
||||
| `StoricoPickingListWindow.__init__` | `__init__(self, parent: tk.Widget, db_client, session=None)` | costruisce stato, tema, runner async e UI |
|
||||
| `_build_ui` | `_build_ui(self) -> None` | crea toolbar, griglia alta e griglia dettaglio |
|
||||
| `_make_tree` | `_make_tree(self, *, row: int, columns: tuple[str, ...], widths: dict[str, int]) -> ttk.Treeview` | factory griglia con stile comune |
|
||||
| `_load_master` | `_load_master(self) -> None` | lancia query master asincrona |
|
||||
| `_fill_master` | `_fill_master(self, rows: list[dict[str, Any]]) -> None` | popola griglia alta |
|
||||
| `_on_master_select` | `_on_master_select(self, _event=None) -> None` | gestisce selezione documento e carica dettaglio |
|
||||
| `_fill_detail` | `_fill_detail(self, rows: list[dict[str, Any]]) -> None` | popola griglia bassa |
|
||||
| `_update_residual_button` | `_update_residual_button(self) -> None` | abilita azione solo su `Chiusa ERP con residui` |
|
||||
| `_restore_master_selection` | `_restore_master_selection(self, documento: str) -> None` | riseleziona documento dopo reload |
|
||||
| `_residual_pallets_from_rows` | `_residual_pallets_from_rows(self, rows: list[dict[str, Any]]) -> list[str]` | deduplica UDC residue non gia' spedite |
|
||||
| `_operator_login` | `_operator_login(self) -> str` | determina utente per movimenti |
|
||||
| `_ship_selected_residuals` | `_ship_selected_residuals(self) -> None` | esegue versamento massivo verso `7G.1.1` |
|
||||
| `open_storico_pickinglist_window` | `open_storico_pickinglist_window(parent: tk.Misc, db_client, session=None) -> tk.Misc` | entry point chiamato dal launcher |
|
||||
|
||||
## Effetti sul database
|
||||
|
||||
Il modulo e' in sola lettura tranne `_ship_selected_residuals`.
|
||||
|
||||
`_ship_selected_residuals` non esegue SQL diretto di update. Delega ogni UDC a `move_pallet_async`, che:
|
||||
|
||||
- trova l'ultimo movimento `V` positivo della UDC;
|
||||
- aggiorna `ModUtente` e `ModDataOra` della riga sorgente;
|
||||
- inserisce un movimento `P` sulla cella sorgente;
|
||||
- libera `Celle.IDStato` della cella sorgente;
|
||||
- inserisce un movimento `V` sulla cella target `9999`;
|
||||
- esegue `dbo.py_sp_ControllaPrenotazionePackingListPalletNew`.
|
||||
|
||||
## Rischi e note review
|
||||
|
||||
- Il versamento massivo non e' atomico a livello di intera lista: ogni UDC viene movimentata con una chiamata separata. Se una UDC fallisce, quelle precedenti potrebbero essere gia' state registrate.
|
||||
- La rilettura del dettaglio riduce il rischio di dato UI obsoleto, ma non sostituisce un lock transazionale su documento.
|
||||
- Le UDC multi-lotto sono deduplicate per `Pallet`, per evitare doppio movimento fisico.
|
||||
- Le UDC gia' in `9999` vengono saltate.
|
||||
- Le UDC non scaffalate risultano `IDCella = 1000`; vengono considerate residue e quindi movimentate verso `9999`.
|
||||
|
||||
## Test consigliati
|
||||
|
||||
1. Selezionare una lista `Da lavorare`: il pulsante deve restare disabilitato.
|
||||
2. Selezionare una lista `Chiusa`: il pulsante deve restare disabilitato.
|
||||
3. Selezionare una lista `Chiusa ERP con residui`: il pulsante deve abilitarsi.
|
||||
4. Confermare il versamento su una lista con UDC non scaffalate.
|
||||
5. Verificare in `Storico movimenti UDC` che ogni UDC abbia:
|
||||
- ultimo passaggio sorgente;
|
||||
- movimento `P` sulla sorgente;
|
||||
- movimento `V` su `9999 / 7G.1.1`.
|
||||
6. Ricaricare lo storico picking list: la lista non deve piu' risultare con residui se tutte le UDC sono in `9999`.
|
||||
153
docs/review/sql_pickinglist_reservation.md
Normal file
153
docs/review/sql_pickinglist_reservation.md
Normal file
@@ -0,0 +1,153 @@
|
||||
# SQL prenotazione picking list Python-only
|
||||
|
||||
## Script
|
||||
|
||||
- `apply_python_parallel_pickinglist_patch.sql`
|
||||
- `rollback_python_parallel_pickinglist_patch.sql`
|
||||
|
||||
## Obiettivo
|
||||
|
||||
Permettere a Python e C# di lavorare sullo stesso database senza condividere le stesse stored procedure operative.
|
||||
|
||||
Il C# continua a usare gli oggetti legacy:
|
||||
|
||||
- `dbo.XMag_ViewPackingList`
|
||||
- `dbo.ViewPackingListRestante`
|
||||
- `dbo.sp_xExePackingListPallet`
|
||||
- `dbo.sp_xExePackingListPalletPrenota`
|
||||
- `dbo.sp_ControllaPrenotazionePackingListPalletNew`
|
||||
|
||||
Il Python usa oggetti separati:
|
||||
|
||||
- `dbo.py_XMag_ViewPackingList`
|
||||
- `dbo.py_ViewPackingListRestante`
|
||||
- `dbo.py_sp_xExePackingListPallet`
|
||||
- `dbo.py_sp_xExePackingListPalletPrenota`
|
||||
- `dbo.py_sp_ControllaPrenotazionePackingListPalletNew`
|
||||
- `dbo.PyPickingListReservation`
|
||||
|
||||
## Tabella `dbo.PyPickingListReservation`
|
||||
|
||||
### Scopo
|
||||
|
||||
Contiene la singola picking list prenotata dal programma Python.
|
||||
|
||||
### Schema
|
||||
|
||||
| Colonna | Tipo | Ruolo |
|
||||
| --- | --- | --- |
|
||||
| `ID` | `tinyint` | sempre `1`, enforced da check constraint |
|
||||
| `Documento` | `varchar(8)` | documento prenotato |
|
||||
| `IDOperatore` | `int` | operatore che ha fatto l'ultima azione |
|
||||
| `ModUtente` | `varchar(50)` | login operatore |
|
||||
| `ModDataOra` | `datetime` | timestamp modifica |
|
||||
|
||||
### Vincoli
|
||||
|
||||
- Primary key su `ID`.
|
||||
- Check `ID = 1`.
|
||||
|
||||
Questa scelta implementa un singleton DB: il Python puo' avere una sola lista prenotata dal backoffice.
|
||||
|
||||
## Vista `dbo.py_XMag_ViewPackingList`
|
||||
|
||||
### Scopo
|
||||
|
||||
Replica la vista legacy `dbo.XMag_ViewPackingList` ma calcola `IDStato` usando `PyPickingListReservation`, non `Celle.IDStato` legacy.
|
||||
|
||||
### Logica `IDStato`
|
||||
|
||||
```sql
|
||||
CASE
|
||||
WHEN pr.Documento IS NOT NULL
|
||||
AND pr.Documento = CAST(legacy.Documento AS varchar(8))
|
||||
THEN 1
|
||||
ELSE 0
|
||||
END AS IDStato
|
||||
```
|
||||
|
||||
## Vista `dbo.py_ViewPackingListRestante`
|
||||
|
||||
### Scopo
|
||||
|
||||
Espone solo le UDC non ancora spedite:
|
||||
|
||||
```sql
|
||||
WHERE Cella <> 9999
|
||||
```
|
||||
|
||||
La UI "Gestione Picking List" usa questa vista sia per la griglia alta sia per il dettaglio.
|
||||
|
||||
## Stored `dbo.py_sp_xExePackingListPallet`
|
||||
|
||||
### Parametri
|
||||
|
||||
| Parametro | Tipo | Ruolo |
|
||||
| --- | --- | --- |
|
||||
| `@IDOperatore` | `int` | operatore corrente |
|
||||
| `@Documento` | `varchar(8)` | documento da prenotare/sprenotare |
|
||||
| `@Azione` | `char(1)` | `P` prenota, `S` s-prenota |
|
||||
| `@RC` | `int OUTPUT` | codice ritorno |
|
||||
|
||||
### Validazioni
|
||||
|
||||
- `@Azione` deve essere `P` o `S`, altrimenti `@RC = -10`.
|
||||
- Il documento deve esistere in `py_XMag_ViewPackingList`, altrimenti `@RC = -20`.
|
||||
- Se manca la riga singleton in `PyPickingListReservation`, viene creata.
|
||||
|
||||
### Prenotazione `P`
|
||||
|
||||
1. Se il documento e' gia' attivo, ritorna senza modifiche.
|
||||
2. Azzera `IDStato` su tutte le celle con stato diverso da zero.
|
||||
3. Mette `IDStato = 1` sulle celle del documento target.
|
||||
4. Aggiorna `PyPickingListReservation` con documento, operatore e timestamp.
|
||||
5. Scrive log tramite `sp_LogPackingList`.
|
||||
|
||||
### S-prenotazione `S`
|
||||
|
||||
1. Se il documento attivo non e' quello richiesto, ritorna senza modifiche.
|
||||
2. Azzera `IDStato` sulle celle target.
|
||||
3. Pulisce `PyPickingListReservation.Documento`.
|
||||
|
||||
## Stored `dbo.py_sp_xExePackingListPalletPrenota`
|
||||
|
||||
### Scopo
|
||||
|
||||
Mette `IDStato = 1` sulle celle ancora residue del documento. Viene usata dal controllo automatico prenotazione.
|
||||
|
||||
## Stored `dbo.py_sp_ControllaPrenotazionePackingListPalletNew`
|
||||
|
||||
### Scopo
|
||||
|
||||
Mantiene coerente la prenotazione mentre la picking list viene consumata dal barcode.
|
||||
|
||||
### Logica
|
||||
|
||||
1. Legge documento attivo da `PyPickingListReservation`.
|
||||
2. Se non c'e' documento attivo, termina.
|
||||
3. Se non esistono piu' righe residue in `py_ViewPackingListRestante`:
|
||||
- azzera `Celle.IDStato`;
|
||||
- svuota `PyPickingListReservation.Documento`;
|
||||
- termina.
|
||||
4. Se esistono ancora residui:
|
||||
- azzera tutti gli `IDStato`;
|
||||
- richiama `py_sp_xExePackingListPalletPrenota` per prenotare le celle residue.
|
||||
|
||||
## Rollback
|
||||
|
||||
`rollback_python_parallel_pickinglist_patch.sql` elimina:
|
||||
|
||||
- `py_sp_ControllaPrenotazionePackingListPalletNew`
|
||||
- `py_sp_xExePackingListPalletPrenota`
|
||||
- `py_sp_xExePackingListPallet`
|
||||
- `py_ViewPackingListRestante`
|
||||
- `py_XMag_ViewPackingList`
|
||||
- `PyPickingListReservation`
|
||||
|
||||
Non tocca gli oggetti legacy C#.
|
||||
|
||||
## Note review
|
||||
|
||||
- La stored Python usa una prenotazione singleton, quindi non gestisce due liste prenotate backoffice.
|
||||
- L'azzeramento globale di `Celle.IDStato` e' coerente con la semantica attuale, ma in futuro dovra' essere rivalutato se piu' operatori o magazzini lavorano in parallelo.
|
||||
- Le UDC non scaffalate condividono il fallback dati del modello legacy; questo e' noto e andra' riprogettato nel nuovo schema FlyWMS.
|
||||
236
docs/review/sql_storico_pickinglist.md
Normal file
236
docs/review/sql_storico_pickinglist.md
Normal file
@@ -0,0 +1,236 @@
|
||||
# SQL storico picking list
|
||||
|
||||
## Oggetti SQL coinvolti
|
||||
|
||||
La form "Storico Picking List" usa un ramo SQL Python-only:
|
||||
|
||||
- `dbo.py_vPreparaPackingListSAMA1`
|
||||
- `dbo.py_vPreparaPackingList`
|
||||
- `dbo.py_XMag_ViewPackingListStorico`
|
||||
- `dbo.PyPickingListReservation`
|
||||
|
||||
Gli script principali sono:
|
||||
|
||||
- `apply_python_pickinglist_history_views.sql`
|
||||
- `apply_online_history_forms_patch.sql`
|
||||
- `rollback_python_pickinglist_history_views.sql`
|
||||
- `rollback_online_history_forms_patch.sql`
|
||||
|
||||
## Motivazione del ramo Python-only
|
||||
|
||||
Il progetto deve convivere temporaneamente con l'applicazione C# legacy. Per questo lo storico Python non modifica:
|
||||
|
||||
- `dbo.vPreparaPackingListSAMA1`
|
||||
- `dbo.vPreparaPackingList`
|
||||
- `dbo.XMag_ViewPackingList`
|
||||
- stored procedure legacy C#
|
||||
|
||||
Le viste prefissate `py_` permettono di evolvere il comportamento Python senza cambiare la semantica legacy.
|
||||
|
||||
## Vista `dbo.py_vPreparaPackingListSAMA1`
|
||||
|
||||
### Scopo
|
||||
|
||||
Legge dal database ERP `SAMA1` le informazioni documentali e di riga necessarie a ricostruire la picking list.
|
||||
|
||||
### Tabelle ERP coinvolte
|
||||
|
||||
- `SAMA1.dbo.LOTSER`
|
||||
- `SAMA1.dbo.ARTICO`
|
||||
- `SAMA1.dbo.FATRIG`
|
||||
- `SAMA1.dbo.LOTTIBF`
|
||||
- `SAMA1.dbo.BAMTES`
|
||||
- `SAMA1.dbo.NAZIONI`
|
||||
- `SAMA1.sam.EXTUC`
|
||||
- `SAMA1.dbo.MTRASP`
|
||||
|
||||
### Campi principali
|
||||
|
||||
| Campo | Significato |
|
||||
| --- | --- |
|
||||
| `NUMLOT` | lotto |
|
||||
| `CODICE` | codice articolo |
|
||||
| `DESCR` | descrizione riga/articolo |
|
||||
| `UDC` | prime 6 cifre di `LOTSER.NUMSER` |
|
||||
| `Qta` | somma quantita giacente ERP |
|
||||
| `NUMDOC` | numero documento/picking list |
|
||||
| `DATDOC` | data documento ERP |
|
||||
| `StatoDocumento` | stato ERP, filtrato su `P` o `D` |
|
||||
| `NAZIONE` | trasporto + descrizione nazione |
|
||||
|
||||
### Filtri
|
||||
|
||||
- anno documento >= anno corrente;
|
||||
- stato documento in `P`, `D`;
|
||||
- lotto diverso da `00000000000`.
|
||||
|
||||
## Vista `dbo.py_vPreparaPackingList`
|
||||
|
||||
### Scopo
|
||||
|
||||
E' una vista di pulizia sopra `py_vPreparaPackingListSAMA1`.
|
||||
|
||||
### Filtro
|
||||
|
||||
Esclude righe con `NUMLOT = ''`.
|
||||
|
||||
## Vista `dbo.py_XMag_ViewPackingListStorico`
|
||||
|
||||
### Scopo
|
||||
|
||||
Unisce il contenuto ERP della picking list con la posizione WMS corrente della UDC.
|
||||
|
||||
### Tabelle WMS coinvolte
|
||||
|
||||
- `dbo.XMag_GiacenzaPallet`
|
||||
- `dbo.Celle`
|
||||
- `dbo.Aree`
|
||||
- `dbo.PyPickingListReservation`
|
||||
|
||||
### Join principale
|
||||
|
||||
La vista usa una `RIGHT OUTER JOIN` tra posizione WMS e preparazione ERP:
|
||||
|
||||
```sql
|
||||
RIGHT OUTER JOIN dbo.py_vPreparaPackingList prep
|
||||
ON g.BarcodePallet COLLATE SQL_Latin1_General_CP1_CI_AS = prep.UDC
|
||||
```
|
||||
|
||||
Questo garantisce che una UDC presente nella picking list ERP compaia anche se non e' scaffalata nel WMS.
|
||||
|
||||
### Convenzioni celle
|
||||
|
||||
| IDCella | Ubicazione | Significato |
|
||||
| --- | --- | --- |
|
||||
| `9999` | `7G - 1 - 1` | spedizione / UDC scaricata |
|
||||
| `1000` | `Non scaff.` | fallback per UDC non trovata in `XMag_GiacenzaPallet` |
|
||||
|
||||
La vista usa:
|
||||
|
||||
```sql
|
||||
ISNULL(g.IDCella, 1000) AS Cella
|
||||
ISNULL(c.Corsia + ' - ' + c.Colonna + ' - ' + c.Fila, 'Non scaff.') AS Ubicazione
|
||||
```
|
||||
|
||||
Quindi le UDC ERP non presenti in giacenza WMS sono rappresentate come non scaffalate.
|
||||
|
||||
## Query Python `SQL_STORICO_PL`
|
||||
|
||||
### Scopo
|
||||
|
||||
Costruisce la griglia alta della form.
|
||||
|
||||
### CTE
|
||||
|
||||
| CTE | Ruolo |
|
||||
| --- | --- |
|
||||
| `base` | normalizza `Pallet` in `PalletKey` |
|
||||
| `pallets` | aggrega righe multi-lotto per singola UDC |
|
||||
| `meta` | calcola metadati documento |
|
||||
| `agg` | calcola totale UDC, UDC spedite e UDC residue |
|
||||
|
||||
### Stato operativo
|
||||
|
||||
La query produce `StatoOperativo` con questa logica:
|
||||
|
||||
```sql
|
||||
CASE
|
||||
WHEN m.StatoDocumento = 'D' AND COALESCE(a.RigheResidue, 0) > 0 THEN 'Chiusa ERP con residui'
|
||||
WHEN m.StatoDocumento = 'D' THEN 'Chiusa'
|
||||
WHEN COALESCE(a.RigheResidue, 0) = 0 THEN 'Esaurita'
|
||||
WHEN COALESCE(a.RigheSpedite, 0) > 0 THEN 'In corso'
|
||||
ELSE 'Da lavorare'
|
||||
END
|
||||
```
|
||||
|
||||
### Deduplica UDC
|
||||
|
||||
La deduplica avviene su `PalletKey`, non sulle righe. Questo evita che una UDC con piu' lotti venga contata come piu' pallet.
|
||||
|
||||
## Query Python `SQL_STORICO_PL_DETAILS`
|
||||
|
||||
### Scopo
|
||||
|
||||
Costruisce la griglia bassa della form.
|
||||
|
||||
### Output
|
||||
|
||||
| Campo | Ruolo |
|
||||
| --- | --- |
|
||||
| `Documento` | numero picking list |
|
||||
| `Pallet` | UDC fisica |
|
||||
| `Lotto` | lotto |
|
||||
| `Articolo` | codice articolo |
|
||||
| `Descrizione` | descrizione articolo/riga |
|
||||
| `Qta` | quantita |
|
||||
| `DataDocumento` | data ERP |
|
||||
| `StatoDocumento` | stato ERP |
|
||||
| `Cella` | cella WMS corrente/fallback |
|
||||
| `Ubicazione` | descrizione cella |
|
||||
| `Ordinamento` | ordine operativo |
|
||||
| `IDStato` | prenotazione Python |
|
||||
|
||||
## Effetti SQL del versamento residui
|
||||
|
||||
La form non contiene direttamente update/insert SQL per il versamento residui. Chiama `move_pallet_async` in `gestione_scarico.py`.
|
||||
|
||||
La batch SQL associata e' `SQL_SCARICA_UDC` e produce:
|
||||
|
||||
1. ricerca dell'ultimo movimento `V` positivo della UDC;
|
||||
2. update audit della riga sorgente;
|
||||
3. insert movimento `P` sulla cella sorgente;
|
||||
4. update `Celle.IDStato = 0` sulla cella sorgente;
|
||||
5. insert movimento `V` sulla cella target;
|
||||
6. esecuzione `dbo.py_sp_ControllaPrenotazionePackingListPalletNew`.
|
||||
|
||||
Per la funzione storico picking list il target e':
|
||||
|
||||
- `target_idcella = 9999`
|
||||
- `target_barcode_cella = '9000000'`
|
||||
|
||||
## Note di consistenza dati
|
||||
|
||||
- Una UDC non scaffalata viene vista come `Cella = 1000`, quindi e' residua.
|
||||
- Una UDC in `9999` non e' residua e viene esclusa dal movimento massivo.
|
||||
- Una UDC multi-lotto puo' comparire piu' volte nel dettaglio ma deve generare un solo movimento fisico.
|
||||
- Lo stato `Chiusa ERP con residui` evidenzia disallineamento tra documento ERP chiuso e WMS non completamente versato in spedizione.
|
||||
|
||||
## Query diagnostiche utili
|
||||
|
||||
Verificare righe documento:
|
||||
|
||||
```sql
|
||||
SELECT *
|
||||
FROM dbo.py_XMag_ViewPackingListStorico
|
||||
WHERE Documento = 155
|
||||
ORDER BY Ordinamento, Pallet;
|
||||
```
|
||||
|
||||
Verificare UDC residue:
|
||||
|
||||
```sql
|
||||
SELECT DISTINCT Pallet, Cella, Ubicazione
|
||||
FROM dbo.py_XMag_ViewPackingListStorico
|
||||
WHERE Documento = 155
|
||||
AND ISNULL(Cella, 0) <> 9999
|
||||
ORDER BY Pallet;
|
||||
```
|
||||
|
||||
Verificare movimenti UDC dopo versamento:
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
ID,
|
||||
Tipo,
|
||||
IDRiferimento,
|
||||
Attributo,
|
||||
IDCella,
|
||||
DataMagazzino,
|
||||
InsUtente,
|
||||
InsDataOra,
|
||||
ModUtente,
|
||||
ModDataOra
|
||||
FROM dbo.MagazziniPallet
|
||||
WHERE Attributo = '655560'
|
||||
ORDER BY ID;
|
||||
```
|
||||
Reference in New Issue
Block a user