Panoramica e Impostazioni
Centro di Controllo
consente di definire regole di controllo periodiche su un modello (es. mrp.production
): ad ogni esecuzione la regola applica un dominio, calcola il totale reale dei record trovati, mostra fino a N esempi e invia un messaggio in un canale con un bottone che apre direttamente la lista filtrata.
1) Configurazione principale
Apri Impostazioni ▸ Funzioni tecniche▸ Point of Control ▸ Regole e crea una nuova regola.
- Nome: titolo chiaro della regola (es. ODP scadute e ferme).
- Azienda: azienda corrente (preimpostata).
- Modello target (obbligatorio): il modello da controllare (es.
mrp.production
). - Canale destinazione (obbligatorio): canale di messaggistica in cui inviare il messaggio.
- Attiva: se spuntato, la regola viene eseguita da cron.
-
Pianificazione:
-
Ogni N + Periodo (minuti/ore/giorni/settimane/mesi).
-
Prossima esecuzione / Ultima esecuzione: viene ricalcolata automaticamente
-
- Messaggio: testo che viene riportato nel messaggio sul canale, se vuoto viene usato il modello di default. Ogni messaggio è personalizzabile e riporta nell'intestazione il titolo della regola ed il bottone per accedere all'elenco.
- {count} : Segnaposto che riporta il num. totale di voci trovate
- {sample} : Segnaposto che riporta un max. di 10 esempi delle voci trovate
2) Configurazione azione
L’azione Python è il codice che viene eseguito quando si clicca il bottone all’interno del messaggio.
Questa configurazione è fondamentale per garantire il corretto funzionamento del modulo.
{
'type': 'ir.actions.act_window',
'res_model': model._name,
'name': "Ordini di produzione",
'domain': domain,
'context': dict(context or {}),
'target': "main",
}
- type → Non modificare mai. Deve rimanere invariato rispetto al valore riportato.
- res_model → Inserire sempre
model._name
in modo da compilare automaticamente il campo con il valore selezionato sul Modello target. - name → Nome che verrà mostrato nel navigatore.
- domain → Lasciare sempre
domain
. Questo verrà compilato secondo le regole già definite. - context → Si consiglia di utilizzare il valore di default oppure aggiungerne altri per attivare filtri o raggruppamenti specifici.
- target → Impostare il valore main oppure current, a seconda della necessità.
3) Configurazione viste
Le viste sono fondamentali per visualizzare l’interfaccia nel modo più efficace possibile e permettere una corretta analisi dei dati.
È consigliato sempre configurare almeno due tipologie di viste nel seguente ordine:
- Tree / Lista → per visualizzare l’elenco delle voci in formato tabellare.
- Form → per accedere al dettaglio di una singola voce.
Il sistema utilizza una sequenza per definire l’ordine di priorità delle viste.
Non è necessario applicare filtri nella selezione, poiché il sistema propone automaticamente solo le viste relative al modello e alla tipologia selezionati.
È altamente consigliato definire viste primarie, ossia non ereditate da altre viste.
4) Condizioni (AND/OR)
La tabella Condizioni (AND/OR) definisce tutti filtri/regole da applicare nella regola di controllo.
Ogni condizione creata può appartenere a un gruppo, che stabilisce la logica di valutazione.
Senza gruppo tutte le condizione vengono valutate come AND.
Campi di configurazione
- Campo: un campo memorizzato del modello.
- Operatore:
=
,!=
,<
,<=
,>
,>=
,ilike
,not ilike
,in
,not in
,is set
,is not set
. - Tipo valore:
char
,integer
,float
,date
,datetime
,boolean
,selection
,many2one
. - Sorgente valore:
- literal: inserisci il valore direttamente.
- today: data odierna (date).
- now: data e ora correnti (datetime).
- field: confronto campo vs campo (placeholder; non supportato nativamente dal dominio → completare in Filtro Python).
- python: calcoli il valore con
safe_eval
(scalare/lista coerente con l’operatore).
5) Gruppi (AND/OR)
La tabella Gruppi (AND/OR) consente di organizzare e raggruppare più condizioni in insiemi logici.
Ogni gruppo definisce come devono essere valutate le condizioni al suo interno e come deve avvenire la combinazione con altri gruppi.
Funzionamento
- AND → tutte le condizioni contenute nel gruppo devono risultare vere affinché il gruppo sia valido.
- OR → è sufficiente che almeno una delle condizioni contenute nel gruppo sia vera affinché il gruppo sia valido.
I gruppi possono essere utilizzati per creare logiche complesse, combinando condizioni multiple tra loro e migliorando la precisione dei controlli.
6) Filtro Python (opz.)
Il Filtro Python è un meccanismo avanzato e opzionale che consente di definire controlli non esprimibili con le sole Condizioni (domini Odoo). È utile per confronti campo–campo, aggregazioni su relazioni, logiche multi-passo, ecc.
Funzionamento
- Il codice è valutato con
safe_eval
in un ambiente sicuro. - Devi restituire uno dei seguenti valori:
- Dominio (lista Odoo) → viene fatto AND con il dominio costruito dalle Condizioni/Group.
- Recordset (sul modello della regola) → viene eseguita l’intersezione con i risultati del dominio base; l’apertura lista usa un dominio su
id in [...]
.
- Non sono supportati filtri che restituiscono un semplice booleano.
Modalità supportate
- Espressione singola (consigliata): una sola espressione che valuta in un dominio o un recordset.
- Script multi-linea: puoi usare assegnazioni; al termine devi valorizzare una variabile
result
(oppuredomain
/records
) con il valore da restituire.
Contesto disponibile
All’interno del filtro sono disponibili:
env, model, relativedelta, company_id, today, now
today
è la data odierna (tipo date).now
è il datetime corrente.relativedelta
permette l'aggiunta di tempo ad un datetime
Esempi di utilizzo
◆︎ Espressione singola → DOMINIO
Obiettivo: ODP collegati a una vendita confermata/chiusa e già scaduti (rispetto a now
).
[
('date_deadline', '<', now),
('procurement_group_id', 'in',
env['sale.order']
.search([('state','in',['sale','done']),
('procurement_group_id','!=',False)])
.mapped('procurement_group_id').ids
),
]
Variante: per “scaduti prima dell’inizio di oggi” usa today
al posto di now
.
◆︎ Espressione singola → RECORDSET
Obiettivo: Ordini non completati con ≤ 5 pezzi residui (product_qty - qty_produced
).
model.search([]).filtered(
lambda p: (p.product_qty - p.qty_produced) > 0
and (p.product_qty - p.qty_produced) <= 5
)
La regola convertirà il recordset in un dominio su id
per aprire la lista coerente.
◆︎ Script multi-linea → DOMINIO via result
Obiettivo: come l’esempio A, ma scritto a passaggi.
sale_groups = env['sale.order'].search([
('state','in',['sale','done']),
('procurement_group_id','!=',False),
]).mapped('procurement_group_id').ids
result = [
('date_deadline','<', now),
('procurement_group_id','in', sale_groups),
]
Note
- Preferisci restituire un dominio quando possibile (più efficiente); usa il recordset solo per logiche impossibili in SQL (lambda, confronti campo–campo, aggregazioni complesse).
- Le Condizioni standard restano attive: il filtro Python si somma (AND) al dominio base, oppure raffina i risultati via intersezione se restituisce un recordset.
- Anteprima, messaggio e azione mostrano esattamente gli stessi record (coerenza tra
len(rs)
, lista e bottone). - Evita calcoli costosi su tabelle molto grandi: se una logica è frequente, valuta un campo computed stored e poi filtra con un dominio classico.