Varianti, Modificatori e Extra
Il sistema di varianti permette di personalizzare i prodotti al momento dell'ordine: scelta della cottura, aggiunta di ingredienti, selezione della taglia. Le varianti sono gestite tramite Gruppi Varianti (variant-groups), entita indipendenti che possono essere associate a uno o piu prodotti.
Concetti Fondamentali
Gruppo Varianti
Un gruppo varianti definisce un set di opzioni tra cui l'operatore (o il cliente) puo scegliere. Ogni gruppo ha:
| Campo | Descrizione | Esempio |
|---|---|---|
groupId | Identificativo univoco | grp_cottura_carne |
name | Nome visualizzato | Cottura |
type | Tipo di selezione | SINGLE o MULTIPLE |
isRequired | Obbligatorio per procedere | true |
minSelection | Selezioni minime | 1 |
maxSelection | Selezioni massime | 1 (per SINGLE), 5 (per MULTIPLE) |
errorMessage | Messaggio d'errore personalizzato (validazione) | Devi selezionare la dimensione del panino |
hasNoneOption | Abilita l'opzione "Nessuno" (vedi sotto) | false |
noneLabel | Etichetta della voce "Nessuno" | Nessuno |
nonePriceDelta | Sovrapprezzo/sconto della scelta "Nessuno" | 0 |
noneIsDefault | "Nessuno" preselezionato di default | false |
nonePrintOnComanda | Stampa la scelta "Nessuno" in comanda | false |
noneComandaLabel | Testo stampato in comanda per "Nessuno" | senza contorno |
accordion | Mostra il gruppo chiuso ("a fisarmonica", vedi sotto) | false |
options | Array di opzioni disponibili | vedi sotto |
Tipo SINGLE vs MULTIPLE
- SINGLE: l'operatore deve scegliere esattamente una opzione (es. cottura: al sangue / media / ben cotta)
- MULTIPLE: l'operatore puo scegliere piu opzioni simultaneamente (es. topping pizza: olive, funghi, prosciutto)
Visualizzazione a fisarmonica (accordion)
Un gruppo con molte opzioni (es. Contorno con quindici voci) occupa da solo tutto lo schermo
della cassa, rendendo scomodo raggiungere gli altri gruppi. Attivando Visualizzazione →
"Mostra a fisarmonica (chiuso)" nella scheda del gruppo, il gruppo compare chiuso: si vede
solo il titolo e, fra parentesi, il numero di opzioni gia scelte (es. CONTORNO (2)). Al tocco
sulla testata il gruppo si apre e mostra tutte le opzioni come di consueto.
- L'apertura e la chiusura sono manuali e indipendenti per gruppo: piu gruppi possono restare aperti contemporaneamente.
- Cambiando prodotto si riparte con tutti i gruppi a fisarmonica chiusi.
- Vale anche per i gruppi obbligatori: se si conferma senza aver completato la scelta, il gruppo incriminato si apre da solo insieme al messaggio d'errore.
- L'impostazione e attiva su cassa, tavoli e asporto. Cameriere, chiosco e webapp mostrano il gruppo sempre aperto.
Opzione
Ogni opzione all'interno di un gruppo contiene:
| Campo | Descrizione | Esempio |
|---|---|---|
optionId | Identificativo univoco | opt_media |
name | Nome visualizzato | Media Cottura |
priceDelta | Sovrapprezzo o sconto | 0 (nessun extra) o 1.50 |
action | Modifica alla ricetta | NONE, ADD, REMOVE, SUBSTITUTE |
isDefault | Selezionata per default | false |
displayOrder | Ordine di visualizzazione | 1 |
Scelta di Default
Il campo isDefault su un'opzione la rende preselezionata all'apertura del pannello varianti: la voce appare gia scelta, come se l'operatore l'avesse selezionata, e puo comunque essere cambiata. Serve a velocizzare l'inserimento quando esiste una scelta "piu comune" (es. Dimensione panino default = Media).
- La scelta di default e facoltativa: se nessuna opzione ha
isDefault: true, il gruppo si apre senza preselezioni (l'operatore deve ragionare sulla scelta anche se il gruppo e obbligatorio). - Per i gruppi
SINGLE, se per errore piu opzioni sono marcate default, viene preselezionata solo la prima. - Nella console admin il default si imposta con la spunta "Preselezionata di default" sull'opzione.
Il default viene applicato in tutti i punti di inserimento: banco cassa (layout default, gerarchico e "varianti visibili"), comanda tavoli, waiter, kiosk e webapp tavolo/asporto.
Opzione "Nessuno"
Per i gruppi obbligatori e possibile abilitare una voce speciale "Nessuno" (hasNoneOption: true) che permette di "chiudere" l'obbligo senza effettuare una scelta reale. Esempio: un panino con contorno obbligatorio, dove il cliente puo scegliere di non prendere il contorno.
| Campo | Descrizione |
|---|---|
hasNoneOption | Abilita la voce "Nessuno" come opzione selezionabile |
noneLabel | Etichetta mostrata nel pannello (default Nessuno) |
nonePriceDelta | Eventuale sovrapprezzo/sconto della scelta (di norma 0) |
noneIsDefault | Se true, "Nessuno" e preselezionato |
nonePrintOnComanda | Se true, la scelta "Nessuno" viene stampata in comanda |
noneComandaLabel | Testo stampato in comanda quando la stampa e attiva (es. senza contorno) |
Comportamento:
- "Nessuno" appare come una normale opzione selezionabile e soddisfa l'obbligatorieta del gruppo (esclude le altre opzioni, come una scelta singola).
- Le varianti reali vengono sempre stampate in comanda. Per la sola scelta "Nessuno" si decide con
nonePrintOnComandase stamparla e con quale testo (noneComandaLabel, es. "senza contorno"). - Se
nonePrintOnComandaefalse, la scelta "Nessuno" chiude comunque l'obbligo ma non compare in comanda.
Creazione di un Gruppo Varianti
Dalla Console Admin
- Accedere a Catalogo > Gruppi Varianti > Nuovo Gruppo
- Compilare:
- Nome: es.
Cottura Carne - Tipo:
SINGLE(scelta singola) - Obbligatorio:
Si - Min selezioni:
1, Max selezioni:1
- Nome: es.
- Aggiungere le opzioni:
Al Sangue-- priceDelta: 0Media-- priceDelta: 0Ben Cotta-- priceDelta: 0
- Salvare
Esempio: Extra con Sovrapprezzo
Per un gruppo "Topping Pizza" con extra a pagamento:
- Creare gruppo con tipo
MULTIPLE,isRequired: false,maxSelection: 5 - Aggiungere opzioni:
Mozzarella di Bufala-- priceDelta:2.00, action:ADDProsciutto Crudo-- priceDelta:1.50, action:ADDSenza Olive-- priceDelta:0, action:REMOVE
Associazione ai Prodotti
I gruppi varianti si collegano ai prodotti tramite il campo variantGroups, un array di groupId:
{
"sku": "BISTECCA-FIORENTINA",
"name": "Bistecca alla Fiorentina",
"variantGroups": ["grp_cottura_carne", "grp_contorno"]
}
Quando l'operatore aggiunge il prodotto al carrello, il sistema presenta automaticamente i gruppi varianti associati. Se almeno un gruppo e isRequired: true, l'operatore non puo procedere senza completare la selezione.
Varianti Ereditate dalla Categoria
Le categorie supportano il campo defaultVariantGroups: un array di groupId applicati automaticamente a tutti i prodotti della categoria. Questo evita di configurare le stesse varianti su ogni singolo prodotto.
Ad esempio, assegnando grp_cottura_carne alla categoria Carni alla Griglia, tutti i prodotti di quella categoria erediteranno il gruppo cottura.
Azioni sugli Ingredienti (BOM)
Le opzioni con campo action diverso da NONE modificano la distinta base del prodotto:
| Azione | Effetto | Campi necessari |
|---|---|---|
ADD | Aggiunge ingrediente | ingredientSku, quantity, uom |
REMOVE | Rimuove ingrediente | ingredientSku, quantity |
SUBSTITUTE | Sostituisce un ingrediente della ricetta | variantId (la variante che entra), ingredientSkuToRemove (l'ingrediente di ricetta che esce) |
Queste modifiche si propagano al magazzino quando il modulo warehouse e attivo, decrementando le giacenze in base alla ricetta modificata.
La sostituzione
La sostituzione non mette a confronto due varianti: toglie un ingrediente dalla ricetta del prodotto e ci mette una variante. Esempio: sulla pizza margherita, la cui ricetta contiene mozzarella fiordilatte, si sceglie la variante Mozzarella di bufala e come ingrediente da sostituire il fiordilatte.
Per configurarla servono due cose:
- la variante che entra, scelta dall'anagrafica varianti come per
ADDeREMOVE: porta con se nome, sovrapprezzo, canali di vendita e SKU di magazzino; - l'ingrediente di ricetta che esce, scelto dall'anagrafica ingredienti.
Un prodotto la cui ricetta non contiene quell'ingrediente non mostra l'opzione — e se il gruppo resta senza alternative, non mostra nemmeno il gruppo. E il motivo per cui lo stesso gruppo "Tipo di mozzarella" si puo assegnare a tutte le pizze senza preoccuparsi: appare solo su quelle che il fiordilatte lo hanno davvero. Un prodotto senza ricetta non mostra mai una sostituzione.
Quantita: entra la stessa quantita della riga di ricetta sostituita — 100 g di fiordilatte tolti, 100 g di bufala aggiunti — senza dover configurare nulla. Se serve una quantita diversa si valorizza Delta Quantita sulla variante (o l'override per gruppo), che vince.
Prezzo: la sostituzione costa quanto la variante che entra (deltaPriceAdd), oppure
l'override di prezzo dell'opzione se valorizzato.
In comanda la riga si legge per esteso: Mozzarella di bufala al posto di Mozzarella fiordilatte.
Le sostituzioni configurate con la vecchia forma (una variante al posto di un'altra) continuano
a funzionare. Lo script scripts/migrate-substitute-to-recipe-ingredient.js le converte alla
forma nuova; quelle la cui variante "da rimuovere" non era collegata a nessuno SKU vanno
riconfigurate a mano, e il pannello le segnala.
Collegare la variante a un ingrediente o a un prodotto
Nell'anagrafica variante (Varianti > editor) il campo Collega a sceglie in quale anagrafica cercare lo SKU da scaricare:
| Collega a | Quando usarlo | Cosa viene scaricato |
|---|---|---|
| Ingrediente (default) | La variante aggiunge/toglie una materia prima o un semilavorato (es. mozzarella, prosciutto) | Lo SKU dell'ingrediente |
| Prodotto | La variante corrisponde a un articolo a catalogo (es. gruppo "Bibita" con opzioni acqua / aranciata / birra su una torta) | Lo SKU del prodotto; se il prodotto ha una ricetta, questa viene esplosa nei suoi ingredienti |
La ricerca e server-side su nome o SKU in entrambi i casi. Il tipo scelto viene salvato sulla variante
(skuKind) e serve a ri-aprire l'editor sull'anagrafica giusta: lo scarico a magazzino e comunque
guidato dal solo sku.
Le varianti collegate a magazzino vengono scaricate anche quando il prodotto base non ha una ricetta (es. una torta venduta a pezzo con la bibita in abbinamento).
Non basta collegare lo SKU: serve valorizzare Delta Quantita nella sezione AGGIUNTA (o SOTTRATTA). Con delta a 0 la variante modifica solo il prezzo. L'editor mostra un avviso quando lo SKU e collegato ma entrambi i delta quantita sono a 0.
Override per gruppo
Una variante vive una volta sola in anagrafica, ma dentro un gruppo puo' comportarsi diversamente. Nell'editor del gruppo (Gruppi Varianti > modifica > riga dell'opzione) ci sono tre override, tutti con la stessa regola: campo vuoto = usa l'anagrafica.
| Override | A cosa serve | Esempio |
|---|---|---|
| Override € | Prezzo diverso in questo gruppo | "+ Mozzarella" costa 1,50 sulle pizze e 1,00 sulle piadine |
| Override qta aggiunta / rimossa | Quantita di scarico diversa in questo gruppo | "+ Mozzarella" pesa 50 g sulle pizze e 30 g sulle piadine |
| Override canali di vendita | Visibilita' diversa in questo gruppo | "Bicchiere di carta" solo in asporto nel gruppo A, ovunque nel gruppo B |
Senza questi override si dovrebbe duplicare la voce in anagrafica per ogni variazione.
Lasciare il campo vuoto significa "eredita dall'anagrafica". Scrivere 0 significa invece "in questo gruppo la variante non movimenta il magazzino": e' una scelta esplicita, utile quando la stessa variante altrove scarica.
Come si compongono i canali di vendita
I canali si configurano su tre livelli, e si sommano in AND:
- Anagrafica variante — dove quella variante e' disponibile in generale.
- Gruppo — dove compare l'intero gruppo (es. "Bibite" nascosto in asporto).
- Override sull'opzione dentro il gruppo — sostituisce il livello 1, solo in quel gruppo.
Ovunque, elenco vuoto = disponibile su tutti i canali. Per rendere una variante disponibile ovunque solo in un certo gruppo non basta svuotare l'override (vorrebbe dire "eredita"): vanno selezionati esplicitamente tutti i canali.
L'override agisce dentro il gruppo, non sopra di esso. Se il gruppo esclude un canale, una
variante con override su quel canale non compare comunque. Esempio: gruppo limitato a
Banco, opzione con override Asporto → quell'opzione non si vede ne' al banco (l'override
non include banco) ne' in asporto (il gruppo e' escluso). Se l'obiettivo e' mostrarla in
asporto, va prima ampliato il canale del gruppo.
Il filtro e' applicato sul dispositivo per cassa, tavoli e palmare, perche' la stessa cassa passa da banco ad asporto a tavolo durante la giornata. Per il kiosk, che ha un canale fisso, il filtro e' applicato dal server: le varianti escluse non vengono proprio inviate al totem.
Calcolo Prezzo Finale
Quando l'operatore seleziona le varianti, il sistema calcola il prezzo finale sommando tutti i priceDelta:
Prezzo base: Margherita 8.00 EUR
+ Bufala: priceDelta +2.00 EUR
+ Crudo: priceDelta +1.50 EUR
---
Prezzo finale: 11.50 EUR
L'azione variant-groups.applyVariantsToProduct esegue questo calcolo lato server, restituendo originalPrice, finalPrice, priceDelta e le modifiche BOM.
Validazione Selezioni
Il sistema valida automaticamente le selezioni dell'operatore:
- Se
isRequired: trueeminSelection: 1, almeno un'opzione deve essere selezionata - Se
maxSelection: 3, non si possono selezionare piu di 3 opzioni - Per tipo
SINGLE,maxSelectione automaticamente1 - L'opzione "Nessuno" (se abilitata) conta come selezione valida e soddisfa l'obbligatorieta
In caso di violazione, il sistema mostra un errore e impedisce l'aggiunta al carrello.
Messaggio d'Errore Personalizzato
Il campo errorMessage del gruppo permette di definire il testo mostrato quando la validazione fallisce (es. Devi selezionare la dimensione del panino). Se vuoto, viene usato un messaggio generato automaticamente dal nome del gruppo. Il messaggio compare come avviso nei pannelli varianti con pulsante di conferma e negli avvisi dei controlli configurabili (vedi sotto).
Momenti di Verifica (layout "varianti visibili")
I pannelli varianti con pulsante di conferma (dialog banco, comanda tavoli, waiter, kiosk, webapp) validano sempre la scelta alla conferma. Il layout banco "varianti visibili" (Fast Variants) applica invece le varianti direttamente sulla riga senza un passo di conferma: per questo la verifica delle varianti obbligatorie e configurabile a livello di locale dalla console admin, in Configurazione Ristorante > Impostazioni Station > Verifica Varianti Obbligatorie (policy del ristorante, sincronizzata a tutte le casse). I tre interruttori sono selezionabili in modo indipendente:
- Verifica all'invio in cucina: blocca l'invio comanda se una variante obbligatoria non e stata scelta.
- Verifica al pagamento: blocca il pagamento in presenza di righe con varianti obbligatorie mancanti.
- Verifica al cambio prodotto: quando si aggiunge un altro prodotto, verifica la riga precedente (pensato proprio per il layout "varianti visibili").
In tutti i casi viene mostrato il messaggio d'errore configurato sul gruppo.
Pressione Lunga in Cassa
In cassa, la pressione lunga su un tasto prodotto (CART_ADD) forza l'apertura del pannello varianti anche se il prodotto non ha gruppi obbligatori. Questo permette all'operatore di aggiungere modifiche occasionali senza rendere le varianti obbligatorie per tutti gli ordini.
FAQ
Posso usare lo stesso gruppo varianti per piu prodotti?
Si. I gruppi varianti sono entita indipendenti. Lo stesso gruppo Cottura Carne puo essere associato a bistecca, hamburger e qualsiasi altro prodotto.
Come configuro taglie diverse con prezzi diversi?
Creare un gruppo SINGLE con isRequired: true. Ogni opzione rappresenta una taglia con il suo priceDelta. Ad esempio: Piccola (0), Media (+1.00), Grande (+2.00).
Come gestisco "senza" un ingrediente (es. senza cipolle)?
Creare un'opzione con action: REMOVE, ingredientSku corrispondente e priceDelta: 0. L'opzione apparira nella lista dei modificatori e, se il magazzino e attivo, non verranno scalate le cipolle dalla giacenza.
Le varianti appaiono sullo scontrino?
Si. Le varianti selezionate vengono stampate sotto il nome del prodotto nella comanda cucina e nel preconto. La formattazione dipende dal template di stampa configurato. Le varianti reali sono sempre stampate; per la sola opzione "Nessuno" la stampa e controllata da nonePrintOnComanda / noneComandaLabel (vedi Opzione "Nessuno").
Come preseleziono una scelta senza renderla fissa?
Impostare isDefault: true sull'opzione desiderata (spunta "Preselezionata di default" nella console). La voce appare gia scelta ma resta modificabile dall'operatore. Vedi Scelta di Default.
Come permetto di "chiudere" un gruppo obbligatorio senza una scelta reale (es. senza contorno)?
Abilitare l'opzione "Nessuno" sul gruppo (hasNoneOption: true), impostando eventualmente etichetta, prezzo, testo comanda e se stamparla. Vedi Opzione "Nessuno".
Vedi Anche
- Catalogo Prodotti -- associazione variantGroups ai prodotti
- Categorie Gerarchiche -- defaultVariantGroups per categoria
- Template Stampe -- formattazione varianti sulle stampe
- Layout Cassa -- comportamento tasti con varianti