I wizard sono uno strumento molto utile per fornire agli utenti un interfaccia per effettuare operazioni complesse sui dati. Per esempio chiedendogli dati aggiuntivi rispetto alle operazioni che stanno facendo oppure semplicemente per avere uno strumento di validazione delle operazioni da effettuare.
I wizard di solito risiedono nella cartella wizards/ all’interno dei moduli. Cominciamo a creare il nosto wizard nella cartella del modulo task_plus creando la seguente struttura
todo_plus/
models/
[...]
security/
[...]
views/
[...]
# Aggiungiamo questo
wizards/
__init__.py
todo_wizard.py
todo_wizard.xml
__init__.py
__manifest__.py
All’interno del file task_plus/__init.py__ aggiungiamo
from . import wizards
All’interno del file task_plus/wizards/__init.py__ aggiungiamo
from . import todo_wizard
All’interno del file task_plus/__manifest.py__ aggiungiamo
{
'name': 'TODO Plus',
'description': "Maggiori funzionalita per l'app TODO",
'author': 'Metadonors',
'depends': ['todo_user'],
'data': [
'security/todo_project_acl.xml',
'security/todo_tag_acl.xml',
'views/todo_task.xml',
'views/todo_project.xml',
'wizards/todo_wizard.xml' # <--- Aggiungiamo questo
]
}
Un wizard visualizza una Form View all’utente, solitamente all’interno di una finestra modale. Questa vista è implementata con la stessa architattura che abbiamo visto finora con l’unica differenza che il modello del wizard erediterà dalla classe models.TransientModel anzichè dalla classe models.Models. Questo tipo di modelli vengono salvati in database come i modelli classici con l’unica differenza che vengono automaticamente cancellati ogni giorno.
Quindi apriamo il file task_plus/wizards/task_wizard.py e scriviamo
from odoo import models, fields, api
class TodoWizard(models.TransientModel):
_name = 'todo.wizard'
_desctiption = 'Todo Wizard per assegnazioni massive'
todo_ids = fields.Many2many('todo.task', string='Tasks')
new_deadline_date = fields.Date(string='Nuova deadline')
new_user_id = fields.Many2one('res.users', string='Nuovo Responsabile')
Le viste dei wizard sono uguali alle viste classiche, con due differenze:
Quindi apriamo il file task_plus/wizards/task_wizard.xml e scriviamo
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<record model="ir.ui.view" id="todo_task_wizard_view">
<field name="name">todo.wizard.form</field>
<field name="model">todo.wizard</field>
<field name="arch" type="xml">
<form>
<div class="oe_right">
<button type="object" name="do_count_todos" string="Conta"/>
<button type="object" name="do_select_all" string="Seleziona tutti"/>
</div>
<field name="todo_ids">
<tree>
<field name="name"/>
<field name="user_id"/>
<field name="deadline_date"/>
</tree>
</field>
<group>
<group> <field name="new_user_id"/></group>
<group> <field name="new_deadline_date"/></group>
</group>
<footer>
<button type="object" name="do_mass_update" string="Riassegna" class="oe_highlight"
attrs="{'invisible':[('new_user_id', '=', False)]}"/>
<button special="cancel" string="Annulla"/>
</footer>
</form>
</field>
</record>
<act_window
id="todo_app.action_todo_wizard"
name="Todo Wizard"
src_model="todo.task"
res_model="todo.wizard"
view_mode="form"
target="new"
multi="True"/>
</odoo>
$ docker compose run odoo upgrade todo_user
ricaricare la pagine nel nostro browser e dalla lista dei todo selezioniamo qualche check box. Apparirà il menu Action al cui interno ci sarà il sottomenu Todo Wizard. Premendo si dovrebbe aprire il nostro wizard
A questo punto non ci rimane che implementare la logica dietro il nostro wizard. Escluso il tasto cancel, abbiamo tre pulsanti che devono essere programmati. Cominciamo con il tasto principale Riassegna
Il metodo chiamato dal pulsante è do_mass_update e, come gli altri, dovrà essere definito nel modello
Quindi apriamo il file task_plus/wizards/task_wizard.py e aggiungiamo in cima al file
from odoo import exceptions
import logging
log = logging.getLogger(__name__)
nel corpo della classe invece aggiungiamo
@api.multi
def do_mass_update(self):
self.ensure_one()
if not self.new_user_id:
raise exceptions.ValidationError("E' necessario specificare un utente")
log.debug("Mass update on todos %s" % self.todo_ids.ids)
vals = {}
if self.new_deadline_date:
vals['deadline_date'] = self.new_deadline_date
vals['user_id'] = self.new_user_id.id
self.todo_ids.write(vals)
Questo metodo solleva un errore se l’utente cerca di inviare il form senza specificare un responsabile da assegnare ai todo selezionati.
Ora vediamo come implementare il conteggio dei task aperti, aggiungiamo sempre nel corpo della modello del wizard:
@api.multi
def do_count_todos(self):
count = self.env['todo.task'].search_count([('is_done', '=', False)])
raise exceptions.Warning("Ci sono %d todo ancora aperti" % count)
per l’ultimo metodo (Seleziona tutti) invece c’è un piccolo problema: ogni volta che premiamo un bottone in un wizard e la chiamata non ritorna un eccezione, Oddo chiude automaticamente la finestra popup. La soluzione a questo è qquella di restituire un’azione che dice ad odoo di riaprire il wizard, vediamo come:
@api.multi
def _reopen_wizard(self):
self.ensure_one()
# restituiamo un dizionario rappresentate l'action che riaprira' questo wizard
return {
'type': 'ir.actions.act_window',
'res_model': self._name, # questo modello
'res_id': self.id, # l'id del wizard corrente
'view_type': 'form',
'view_mode': 'form',
'target': 'new'
}
@api.multi
def do_select_all(self):
self.ensure_one()
open_todos = self.env['todo.task'].search([
('is_done', '=', False)
])
self.todo_ids = open_todos
return self._reopen_wizard()