migrazione verso gitea

This commit is contained in:
2026-03-31 19:15:33 +02:00
parent 8806d598eb
commit f6a5b1b29f
118 changed files with 17197 additions and 459 deletions

27
docs/flows/README.md Normal file
View File

@@ -0,0 +1,27 @@
# Flow Diagrams
Questa cartella contiene schemi di flusso e schemi di chiamata dei moduli
principali avviati da `main.py`.
I diagrammi sono scritti in Mermaid, quindi possono essere:
- letti direttamente nei file Markdown;
- renderizzati da molti editor Git/Markdown;
- inclusi in una futura documentazione Sphinx o MkDocs.
## Indice
- [main](./main_flow.md)
- [layout_window](./layout_window_flow.md)
- [reset_corsie](./reset_corsie_flow.md)
- [view_celle_multiple](./view_celle_multiple_flow.md)
- [search_pallets](./search_pallets_flow.md)
- [gestione_pickinglist](./gestione_pickinglist_flow.md)
- [infrastruttura async/db](./async_db_flow.md)
## Convenzioni
- I diagrammi descrivono il flusso applicativo ad alto livello.
- Non rappresentano ogni singola riga di codice.
- I nodi `AsyncRunner` e `query_json` evidenziano i passaggi asincroni più
importanti tra interfaccia e database.

View File

@@ -0,0 +1,39 @@
# Infrastruttura Async / DB
## Scopo
Questo diagramma descrive il flusso comune usato da tutti i moduli GUI quando
eseguono una query sul database.
## Flusso trasversale
```{mermaid}
flowchart TD
A["Evento UI (click / selezione / ricerca)"] --> B["Metodo finestra"]
B --> C["AsyncRunner.run(awaitable)"]
C --> D["Coroutines sul loop globale"]
D --> E["AsyncMSSQLClient.query_json() / exec()"]
E --> F["SQL Server"]
F --> G["Risultato query"]
G --> H["Future completata"]
H --> I["Callback _ok / _err su thread Tk"]
I --> J["Aggiornamento widget"]
```
## Relazioni principali
```{mermaid}
flowchart LR
Main["main.py"] --> Loop["get_global_loop()"]
Main --> DB["AsyncMSSQLClient"]
Windows["Moduli GUI"] --> Runner["AsyncRunner"]
Runner --> Loop
Runner --> DB
DB --> SQL["SQL Server Mediseawall"]
```
## Note
- Il loop asincrono è condiviso tra tutte le finestre.
- Il client DB è condiviso e creato una sola volta nel launcher.
- I callback che aggiornano la UI rientrano sempre sul thread Tk.

View File

@@ -0,0 +1,39 @@
# `async_loop_singleton.py`
## Scopo
Questo modulo mantiene un loop asyncio globale e condiviso, eseguito su un
thread dedicato.
## Flusso
```{mermaid}
flowchart TD
A["Chiamata a get_global_loop()"] --> B{"Loop gia presente?"}
B -- Si --> C["Ritorna loop esistente"]
B -- No --> D["Crea Event ready"]
D --> E["Avvia thread daemon"]
E --> F["_run()"]
F --> G["new_event_loop()"]
G --> H["set_event_loop(loop)"]
H --> I["ready.set()"]
I --> J["loop.run_forever()"]
J --> K["Ritorna loop al chiamante"]
```
## Chiusura
```{mermaid}
flowchart TD
A["stop_global_loop()"] --> B{"Loop attivo?"}
B -- No --> C["Nessuna azione"]
B -- Si --> D["call_soon_threadsafe(loop.stop)"]
D --> E["join del thread"]
E --> F["Azzera riferimenti globali"]
```
## Note
- E un helper minimale usato da `main.py`.
- Il modulo esiste separato da `gestione_aree_frame_async.py`, ma concettualmente
svolge lo stesso ruolo di gestione del loop condiviso.

View File

@@ -0,0 +1,41 @@
# `async_msssql_query.py`
## Scopo
Questo modulo centralizza la costruzione del DSN SQL Server e l'accesso
asincrono al database tramite `AsyncMSSQLClient`.
## Flusso di utilizzo
```{mermaid}
flowchart TD
A["main.py o modulo chiamante"] --> B["make_mssql_dsn(...)"]
B --> C["Crea stringa mssql+aioodbc"]
C --> D["AsyncMSSQLClient(dsn)"]
D --> E["query_json(...) o exec(...)"]
E --> F["_ensure_engine()"]
F --> G{"Engine gia creato?"}
G -- No --> H["create_async_engine(..., NullPool, loop corrente)"]
G -- Si --> I["Riusa engine esistente"]
H --> J["execute(text(sql), params)"]
I --> J
J --> K["Normalizza rows/columns"]
K --> L["Ritorna payload JSON-friendly"]
```
## Schema di chiamata
```{mermaid}
flowchart LR
DSN["make_mssql_dsn"] --> Client["AsyncMSSQLClient.__init__"]
Client --> Ensure["_ensure_engine"]
Ensure --> Query["query_json"]
Ensure --> Exec["exec"]
Client --> Dispose["dispose"]
```
## Note
- `NullPool` evita problemi di riuso connessioni tra loop diversi.
- L'engine viene creato solo al primo utilizzo reale.
- `query_json()` restituisce un formato gia pronto per le callback GUI.

View File

@@ -0,0 +1,45 @@
# `gestione_aree_frame_async.py`
## Scopo
Questo modulo fornisce l'infrastruttura async usata dalle finestre GUI:
- loop asincrono globale;
- overlay di attesa;
- runner che collega coroutine e callback Tk.
## Flusso infrastrutturale
```{mermaid}
flowchart TD
A["Metodo finestra GUI"] --> B["AsyncRunner.run(awaitable)"]
B --> C{"busy overlay richiesto?"}
C -- Si --> D["BusyOverlay.show()"]
C -- No --> E["Salta overlay"]
D --> F["run_coroutine_threadsafe(awaitable, loop globale)"]
E --> F
F --> G["Polling del Future"]
G --> H{"Future completato?"}
H -- No --> G
H -- Si --> I{"Successo o errore?"}
I -- Successo --> J["widget.after(..., on_success)"]
I -- Errore --> K["widget.after(..., on_error)"]
J --> L["BusyOverlay.hide()"]
K --> L
```
## Schema di componenti
```{mermaid}
flowchart LR
Holder["_LoopHolder"] --> Loop["get_global_loop"]
Loop --> Runner["AsyncRunner"]
Overlay["BusyOverlay"] --> Runner
Runner --> GUI["Moduli GUI"]
```
## Note
- Il modulo fa da ponte tra thread Tk e thread del loop asincrono.
- `BusyOverlay` e riusato da piu finestre, quindi e un componente condiviso.
- `AsyncRunner` evita che i moduli GUI gestiscano direttamente i `Future`.

View File

@@ -0,0 +1,69 @@
# `gestione_pickinglist.py`
## Scopo
Questo modulo gestisce la vista master/detail delle picking list e permette di:
- caricare l'elenco dei documenti;
- vedere il dettaglio UDC della riga selezionata;
- prenotare e s-prenotare una picking list;
- mantenere una UI fluida con spinner e refresh differiti.
## Flusso di apertura
```{mermaid}
flowchart TD
A["open_pickinglist_window() da main.py"] --> B["create_pickinglist_frame()"]
B --> C["GestionePickingListFrame.__init__()"]
C --> D["_build_layout()"]
D --> E["after_idle(_first_show)"]
E --> F["reload_from_db(first=True)"]
F --> G["query_json SQL_PL"]
G --> H["_refresh_mid_rows()"]
H --> I["Render tabella master"]
```
## Flusso master/detail
```{mermaid}
flowchart TD
A["Utente seleziona checkbox riga"] --> B["on_row_checked()"]
B --> C["Deseleziona altre righe"]
C --> D["Salva detail_doc"]
D --> E["query_json SQL_PL_DETAILS"]
E --> F["_refresh_details()"]
F --> G["Render tabella dettaglio"]
```
## Prenotazione / s-prenotazione
```{mermaid}
flowchart TD
A["Click Prenota o S-prenota"] --> B["Verifica riga selezionata"]
B --> C["Determina documento e stato atteso"]
C --> D["Chiama sp_xExePackingListPallet_async()"]
D --> E["Aggiorna Celle e LogPackingList sul DB"]
E --> F["SPResult"]
F --> G{"rc == 0?"}
G -- Si --> H["_recolor_row_by_documento()"]
G -- No --> I["Messaggio di errore"]
```
## Schema di chiamata
```{mermaid}
flowchart LR
Init["__init__"] --> Build["_build_layout"]
Init --> First["_first_show"]
First --> Reload["reload_from_db"]
Reload --> Mid["_refresh_mid_rows"]
Check["on_row_checked"] --> Details["_refresh_details"]
Pren["on_prenota"] --> SP["sp_xExePackingListPallet_async"]
Spren["on_sprenota"] --> SP
```
## Note
- Il modulo usa `AsyncRunner`, `BusyOverlay` e `ToolbarSpinner`.
- Il caricamento iniziale è differito con `after_idle` per ridurre lo sfarfallio.
- La riga selezionata viene tenuta separata dal dettaglio tramite `detail_doc`.

20
docs/flows/index.rst Normal file
View File

@@ -0,0 +1,20 @@
Flow Diagrams
=============
Questa sezione raccoglie i diagrammi Mermaid dei moduli applicativi e
infrastrutturali.
.. toctree::
:maxdepth: 1
README.md
main_flow.md
layout_window_flow.md
reset_corsie_flow.md
view_celle_multiple_flow.md
search_pallets_flow.md
gestione_pickinglist_flow.md
async_db_flow.md
async_msssql_query_flow.md
gestione_aree_frame_async_flow.md
async_loop_singleton_flow.md

View File

@@ -0,0 +1,61 @@
# `layout_window.py`
## Scopo
Questo modulo visualizza il layout delle corsie come matrice di celle, mostra
lo stato di occupazione, consente di cercare una UDC e permette l'export della
matrice.
## Flusso operativo
```{mermaid}
flowchart TD
A["open_layout_window()"] --> B["Crea o riporta in primo piano LayoutWindow"]
B --> C["LayoutWindow.__init__()"]
C --> D["Costruisce toolbar, host matrice, statistiche"]
D --> E["_load_corsie()"]
E --> F["AsyncRunner.run(query_json SQL corsie)"]
F --> G["_on_select() sulla corsia iniziale"]
G --> H["_load_matrix(corsia)"]
H --> I["AsyncRunner.run(query_json SQL matrice)"]
I --> J["_rebuild_matrix()"]
J --> K["_refresh_stats()"]
```
## Ricerca UDC
```{mermaid}
flowchart TD
A["Utente inserisce barcode"] --> B["_search_udc()"]
B --> C["query_json ricerca pallet -> corsia/colonna/fila"]
C --> D{"UDC trovata?"}
D -- No --> E["Messaggio informativo"]
D -- Si --> F["Seleziona corsia in listbox"]
F --> G["_load_matrix(corsia)"]
G --> H["_rebuild_matrix()"]
H --> I["_highlight_cell_by_labels()"]
```
## Schema di chiamata essenziale
```{mermaid}
flowchart LR
Init["__init__"] --> Top["_build_top"]
Init --> Host["_build_matrix_host"]
Init --> Stats["_build_stats"]
Init --> LoadCorsie["_load_corsie"]
LoadCorsie --> Select["_on_select"]
Select --> LoadMatrix["_load_matrix"]
LoadMatrix --> Rebuild["_rebuild_matrix"]
Rebuild --> RefreshStats["_refresh_stats"]
Search["_search_udc"] --> LoadMatrix
Export["_export_xlsx"] --> MatrixState["matrix_state / fila_txt / col_txt / udc1"]
```
## Note
- Il modulo usa un token `_req_counter` per evitare che risposte async vecchie
aggiornino la UI fuori ordine.
- La statistica globale viene ricalcolata da query SQL, mentre quella della
corsia corrente usa la matrice già caricata in memoria.
- `destroy()` marca la finestra come non più attiva per evitare callback tardive.

45
docs/flows/main_flow.md Normal file
View File

@@ -0,0 +1,45 @@
# `main.py`
## Scopo
`main.py` è il punto di ingresso dell'applicazione desktop. Inizializza il loop
asincrono condiviso, crea il client database condiviso e costruisce il launcher
con i pulsanti che aprono i moduli operativi.
## Flusso principale
```{mermaid}
flowchart TD
A["Avvio di main.py"] --> B["Configura policy asyncio su Windows"]
B --> C["Ottiene loop globale con get_global_loop()"]
C --> D["Imposta il loop come default"]
D --> E["Costruisce DSN SQL Server"]
E --> F["Crea AsyncMSSQLClient condiviso"]
F --> G["Istanzia Launcher"]
G --> H["Mostra finestra principale"]
H --> I{"Click su un pulsante"}
I --> J["open_reset_corsie_window()"]
I --> K["open_layout_window()"]
I --> L["open_celle_multiple_window()"]
I --> M["open_search_window()"]
I --> N["open_pickinglist_window()"]
```
## Schema di chiamata
```{mermaid}
flowchart LR
Launcher["Launcher.__init__"] --> Reset["open_reset_corsie_window"]
Launcher --> Layout["open_layout_window"]
Launcher --> Ghost["open_celle_multiple_window"]
Launcher --> Search["open_search_window"]
Launcher --> Pick["open_pickinglist_window"]
Pick --> PickFactory["create_pickinglist_frame"]
```
## Note
- `db_app` viene creato una sola volta e poi passato a tutte le finestre.
- Alla chiusura del launcher viene chiamato `db_app.dispose()` sul loop globale.
- `open_pickinglist_window()` costruisce la finestra in modo nascosto e la rende
visibile solo a layout pronto, per ridurre lo sfarfallio iniziale.

View File

@@ -0,0 +1,45 @@
# `reset_corsie.py`
## Scopo
Questo modulo mostra il riepilogo di una corsia e permette, dopo doppia
conferma, di cancellare i record di `MagazziniPallet` collegati a quella corsia.
## Flusso operativo
```{mermaid}
flowchart TD
A["open_reset_corsie_window()"] --> B["ResetCorsieWindow.__init__()"]
B --> C["_build_ui()"]
C --> D["_load_corsie()"]
D --> E["query_json SQL_CORSIE"]
E --> F["Seleziona corsia iniziale"]
F --> G["refresh()"]
G --> H["query_json SQL_RIEPILOGO"]
G --> I["query_json SQL_DETTAGLIO"]
H --> J["Aggiorna contatori"]
I --> K["Aggiorna tree celle occupate"]
```
## Flusso distruttivo
```{mermaid}
flowchart TD
A["Click su Svuota corsia"] --> B["_ask_reset()"]
B --> C["query_json SQL_COUNT_DELETE"]
C --> D{"Record da cancellare > 0?"}
D -- No --> E["Messaggio: niente da rimuovere"]
D -- Si --> F["Richiesta conferma testuale"]
F --> G{"Testo corretto?"}
G -- No --> H["Annulla operazione"]
G -- Si --> I["_do_reset(corsia)"]
I --> J["query_json SQL_DELETE"]
J --> K["Messaggio completato"]
K --> L["refresh()"]
```
## Note
- È il modulo più delicato lato operazioni, perché esegue `DELETE`.
- La finestra separa chiaramente fase di ispezione e fase distruttiva.
- Tutte le query passano dal client condiviso tramite `AsyncRunner`.

View File

@@ -0,0 +1,43 @@
# `search_pallets.py`
## Scopo
Questo modulo consente di cercare pallet/UDC, lotti e codici prodotto su tutto
il magazzino e di esportare i risultati.
## Flusso operativo
```{mermaid}
flowchart TD
A["open_search_window()"] --> B["SearchWindow.__init__()"]
B --> C["_build_ui()"]
C --> D["Utente compila filtri"]
D --> E["_do_search()"]
E --> F{"Filtri vuoti?"}
F -- Si --> G["Richiesta conferma ricerca globale"]
F -- No --> H["Prepara parametri SQL"]
G --> H
H --> I["AsyncRunner.run(query_json SQL_SEARCH)"]
I --> J["_ok()"]
J --> K["Popola Treeview"]
K --> L["Eventuale reset campi"]
```
## Ordinamento ed export
```{mermaid}
flowchart TD
A["Doppio click su header"] --> B["_on_heading_double_click()"]
B --> C["_sort_by_column()"]
C --> D["Riordina righe del Treeview"]
E["Click Export XLSX"] --> F["_export_xlsx()"]
F --> G["Legge righe visibili"]
G --> H["Scrive workbook Excel"]
```
## Note
- Il modulo usa `Treeview` come backend principale.
- Le ricerche possono essere molto ampie: per questo, senza filtri, viene chiesta conferma.
- `IDCella = 9999` viene evidenziata con uno stile dedicato.

View File

@@ -0,0 +1,58 @@
# `view_celle_multiple.py`
## Scopo
Questo modulo esplora le celle che contengono più pallet del previsto,
organizzando i risultati in un albero:
- corsia
- cella duplicata
- pallet contenuti nella cella
## Flusso operativo
```{mermaid}
flowchart TD
A["open_celle_multiple_window()"] --> B["CelleMultipleWindow.__init__()"]
B --> C["_build_layout()"]
C --> D["_bind_events()"]
D --> E["refresh_all()"]
E --> F["_load_corsie()"]
E --> G["_load_riepilogo()"]
F --> H["query_json SQL_CORSIE"]
G --> I["query_json SQL_RIEPILOGO_PERCENTUALI"]
H --> J["_fill_corsie()"]
I --> K["_fill_riepilogo()"]
```
## Lazy loading dell'albero
```{mermaid}
flowchart TD
A["Espansione nodo tree"] --> B["_on_open_node()"]
B --> C{"Nodo corsia o nodo cella?"}
C -- Corsia --> D["_load_celle_for_corsia()"]
D --> E["query_json SQL_CELLE_DUP_PER_CORSIA"]
E --> F["_fill_celle()"]
C -- Cella --> G["_load_pallet_for_cella()"]
G --> H["query_json SQL_PALLET_IN_CELLA"]
H --> I["_fill_pallet()"]
```
## Schema di chiamata
```{mermaid}
flowchart LR
Refresh["refresh_all"] --> Corsie["_load_corsie"]
Refresh --> Riep["_load_riepilogo"]
Open["_on_open_node"] --> LoadCelle["_load_celle_for_corsia"]
Open --> LoadPallet["_load_pallet_for_cella"]
Export["export_to_xlsx"] --> Tree["tree dati dettaglio"]
Export --> Sum["sum_tbl riepilogo"]
```
## Note
- L'albero è caricato a richiesta, non tutto in una sola query.
- Questo riduce il costo iniziale e rende il modulo più scalabile.
- L'export legge sia il dettaglio dell'albero sia la tabella di riepilogo.