Architettura Modulo Cassa (Flutter)
Panoramica
L'applicazione cassa (cassa_ui) e il cuore dell'interfaccia operatore del sistema POS. Costruita con Flutter, segue un'architettura modulare a plugin dove funzionalita verticali vengono caricate dinamicamente dal ModuleRegistry. L'app supporta Windows desktop e tablet Android con un design offline-first basato su Isar per lo storage locale.
Struttura dei Package
Il frontend e organizzato in package indipendenti:
- pos_core: libreria condivisa con contratti (
PosModule,IPosHostInterface), modelli dati, logica di stampa ESC/POS - cassa_ui: applicazione host che orchestra moduli, layout engine e workflow di cassa
- pos_mod_tables: gestione tavoli e sale
- pos_mod_bookings: prenotazioni
- pos_mod_takeaway: ordini asporto e delivery
- pos_mod_kitchen: display cucina (KDS)
- pos_mod_cash_management: prima nota e gestione contante
- pos_mod_warehouse: magazzino e ordini fornitori
- pos_mod_fidelity: fidelizzazione clienti
- editor_stampa_ui: editor template di stampa
Il Contratto PosModule
Ogni modulo implementa l'interfaccia astratta PosModule definita in pos_core:
abstract class PosModule {
String get id; // ID univoco (es. 'com.moley.tables')
String get name; // Nome per il menu
IconData get icon; // Icona nel menu
String get description; // Descrizione breve
List<CollectionSchema> get schemas; // Schemi Isar del modulo
Future<void> init(Isar isar, IPosHostInterface host, {Dio? dio});
Widget buildEntryPoint({String? initialRoute});
}
All'avvio, l'host raccoglie tutti gli schemi Isar dai moduli registrati, apre un'unica istanza del database e chiama init() su ciascun modulo passando l'istanza condivisa, l'interfaccia host e il client HTTP configurato.
ModuleRegistry
Il ModuleRegistry mantiene la lista dei moduli attivi. Attualmente la registrazione e statica:
class ModuleRegistry {
static final List<PosModule> _modules = [
PrinterEditorModule(), TablesModule(), BookingsModule(),
TakeawayModule(), KitchenModule(), FidelityModule(),
WarehouseModule(), ScratchCardsModule(), MealVoucherModule(),
CashManagementModule(), AutomaticCashModule(),
];
static List<PosModule> getActiveModules() => _modules;
}
In futuro la lista potra essere filtrata in base alla licenza del tenant.
IPosHostInterface
L'interfaccia IPosHostInterface consente ai moduli di comunicare con l'host senza dipendenze dirette:
processExternalOrder(orderId, origin)-- Carica un ordine esterno nel carrello per il pagamentoprintComanda(...)-- Stampa comanda cucina con routing automatico per stampanteprintFiscalReceipt(...)-- Emette scontrino fiscale per pagamenti splitnavigateToPos()-- Torna alla schermata principale della cassashowPrintQueue(context)-- Mostra la coda stampeisAutomaticCashAvailable()-- Verifica disponibilita cassa automatica
Questo pattern permette, ad esempio, al modulo Tavoli di richiedere il pagamento di un conto tavolo direttamente nel workflow di cassa.
Layout Engine
Il Layout Engine e il sistema di configurazione dinamica dell'interfaccia cassa, contenuto in cassa_ui/lib/layout_engine/. Include:
- ScreenComposer: composizione dinamica delle schermate a partire da configurazioni JSON
- ButtonEvaluator: valutazione dei pulsanti con binding a prodotti e azioni
- CartService / CartNotifier: gestione stato carrello con Riverpod
- WorkflowNotifier: macchina a stati del flusso cassa (idle, ordering, checkout, payment)
- PrintRouterService: instradamento stampe in base a regole configurabili
- EventOrchestrator: coordinamento eventi NATS, sync e stampa
State Management
L'app usa Riverpod come framework di state management. I provider principali sono definiti in layout_engine/providers.dart e includono il client Dio autenticato, i servizi di sync e i notifier del carrello.
Offline-First con Isar
L'architettura offline-first garantisce operativita anche senza connessione al backend:
- Sync iniziale: al primo avvio, i dati (prodotti, categorie, configurazioni) vengono scaricati dal backend e salvati in Isar
- Operativita locale: ordini e operazioni vengono creati localmente in Isar
- Delta sync: periodicamente (e alla riconnessione), il
SyncServicesincronizza i delta con il backend usando il parametrolastSync - Conflict resolution: il backend funge da source of truth; i conflitti vengono risolti con last-write-wins
Comunicazione Realtime
Il NatsOrchestrator gestisce il trasporto realtime con un'astrazione che supporta:
- Cloud: connessione NATS diretta al broker
- LAN Master: WebSocket server locale (per reti senza internet)
- LAN Client: WebSocket client verso il master
- Auto: failover automatico cloud/LAN
I messaggi NATS vengono usati per sincronizzare ordini, stati tavolo e notifiche tra dispositivi dello stesso tenant.
FAQ
D: Come aggiungo un nuovo modulo?
R: Crea un package Flutter, implementa PosModule, definisci gli schemi Isar e registra il modulo in ModuleRegistry. L'host aggreagera automaticamente gli schemi al prossimo avvio.
D: Posso testare un modulo in isolamento?
R: Si, ogni modulo e un package indipendente. Puoi creare un'app di test che inizializza solo quel modulo con un mock di IPosHostInterface.
D: Come funziona il binding dei pulsanti nel Layout Engine?
R: Il ButtonEvaluator interpreta la configurazione JSON dei pulsanti, collegandoli a prodotti specifici, azioni di sistema o navigazione. L'editor visuale (PosLayoutEditorScreen) permette di configurare i layout senza codice.
Vedi Anche
- API Ordini -- Backend per la gestione ordini
- API Prodotti e Categorie -- Backend catalogo
- Panoramica Architettura -- Visione d'insieme della piattaforma
- Installazione App Cassa -- Guida installazione