Desenvolupament de la nostre primera aplicació de Odoo – Una visió general pràctica
Desenvolupar en Odoo la major part del temps significa crear els nostres propis mòduls. En aquest capítol, crearem la nostra primera aplicació Odoo i aprendrem els passos necessaris per a posar-la a disposició de Odoo i instal·lar-la.
Crearem una senzilla aplicació de coses per fer. Ha de permetre afegir noves tasques, marcar-les com completades i, finalment, esborrar la llista de tasques de totes les tasques ja completades.
Començarem aprenent els conceptes bàsics del flux de treball de desenvolupament: configurarem una nova instància per al seu treball, crearem i instal·larem un nou mòdul i el actualitzarem per aplicar els canvis que realitzem juntament amb les iteracions de desenvolupament.
Odoo segueix una arquitectura similar a MVC, i revisarem les capes durant la nostra implementació de l’aplicació “ToDo”:
- La capa de model, que defineix l’estructura de les dades de l’aplicació
- La capa de vista, que descriu la interfície d’usuari
- La capa de controlador, que admet la lògica de negocis de l’aplicació
A continuació, aprendrem com configurar la seguretat del control d’accés i, finalment, afegirem algunes descripcions i informació de personalització de marca al mòdul.
Amb aquest enfocament, seràs capaç d’aprendre gradualment sobre els blocs de construcció bàsics que componen una aplicació i experimentar el procés iteratiu de la construcció d’un mòdul Odoo des de zero.
Creació d’un nou mòdul de complements
Un mòdul addon és un directori que conté els fitxers que implementen algunes característiques de Odoo. Podeu afegir noves característiques o modificar les existents.
El directori addon ha de contenir un manifest, o arxiu descriptor, denominat __manifest__.py, a més dels altres arxius de mòdul.
Alguns complements de mòdul es presenten com aplicacions. Aquests representen aplicacions disponibles per Odoo i normalment afegeixen el seu propi element de menú de nivell superior. Proporcionen els elements principals per a una àrea funcional, com CRM o HR. A causa d’això, es resalten en el menú Aplicacions de Odoo. D’altra banda, s’espera que els complements de mòdul que no siguin aplicables agreguin característiques a aquestes aplicacions.
Si el mòdul afegeix una funcionalitat nova o principal a Odoo, probablement hauria de ser una aplicació. Si el mòdul simplement realitza canvis en la funcionalitat d’una aplicació existent, probablement hauria de ser un mòdul de complement normal. T’ho mostrarem en la següent secció.
Creació de l’esquelet bàsic
Odoo inclou una ordre scaffold per crear automàticament un nou directori de mòdul, amb una estructura bàsica. Podeu obtenir més informació amb la següent comanda:
odoo-bin scaffold –help
És possible que vulgueu tenir-ho en compte i utilitzar-ho quan comenceu a treballar en el vostre pròxim mòdul, però ara no ho utilitzarem, ja que ara mateix és millor alterar manualment l’estructura del nostre mòdul.
Un mòdul addon d’Odoo és un directori que conté un arxiu descriptor __manifest__.py. També ha de ser importable de Python, de manera que també ha de tenir un arxiu __init__.py.
El nom del directori és el seu nom tècnic. Farem servir perfer_app. El nom tècnic ha de ser un identificador de Python vàlid i per tant, ha de començar amb una lletra i només pot contenir lletres, números i el caràcter de subratllat.
Si utilitzes la línia d’ordres, podem inicialitzar el nostre directori de mòdul, amb un arxiu __init__.py buit, amb aquestes comandes:
$ mkdir -p ~/odoo-dev/custom-addons/perfer_app
$ touch ~/odoo-dev/custom-addons/perfer_app/__init__.py
A continuació, ens cal afegir el fitxer de manifest. Ha de contenir només un diccionari de Python, amb aproximadament una dotzena d’atributs possibles. D’ells, només es requereix l’atribut name, però description i author també són molt recomanables.
Ara, crea l’arxiu __manifest__.py, junt a l’arxiu __init__.py, amb el següent contingut:
{
'name': 'Aplicació de tasques pendent',
'description': 'Gestio tasques personals pendents',
'author': 'Xavier Sastre',
'depends': ['base'],
'application': True,
}
L’atribut depends pot tenir una llista d’altres mòduls que són necessaris. Odoo els instal·larà automàticament quan s’instal·li aquest mòdul. No és un atribut obligatori, però es recomana tenir-lo sempre. Si no es necessiten dependències concretes, hem de dependre del core base principal.
Heu de tenir cura d’assegurar-vos que totes les dependències s’estableixen explícitament aquí; o d’altra manera, el mòdul pot no instal·lar-se en una base de dades neta (a causa de les dependències que falten) o tenir errors de càrrega si per casualitat els altres mòduls requerits es carreguen després.
Per a la nostra aplicació, no necessitem cap dependència específica, de manera que depenem només de base.
Per ser concisos, només hem utilitzat alguns dels descriptors essencials:
- name és una cadena amb el títol del addon.
- description és un text llarg amb la descripció de les característiques, generalment en format RST.
- author és el nom de l’autor del mòdul. És un string, però pot contenir una llista de noms separats per comes.
- depends és una llista dels mòduls addon dels quals depèn. S’instal·laran automàticament abans que aquest mòdul.
- application és un indicador booleà, que declara si el mòdul addon ha d’aparèixer com una aplicació en la llista Aplicacions.
Des d’Odoo 8.0, en lloc de description podem utilitzar un fitxer README.rst o README.md en el directori superior del mòdul. Si tots dos existeixen, s’utilitza la descripció del manifest.
En un escenari real, es recomana que també utilitzeu les claus addicionals, ja que són rellevants per a la botiga d’aplicacions de Odoo:
- summary és una cadena que es mostra com un subtítol per al mòdul.
- version, per defecte, és 1.0. Ha de seguir les regles de control de versions (consulteu http://semver.org/per obtenir més informació). És una bona pràctica utilitzar la versió de Odoo abans de la nostra versió de mòdul, per exemple: 11.0.1.0.
- license, per defecte considerat com LGPL-3.
- website és un URL per trobar més informació sobre el mòdul. Això pot ajudar a trobar més documentació.
- category és una cadena amb la categoria funcional del mòdul, el valor predeterminat és Uncategorized. La llista de categories existents es pot trobar en el formulari Grups de seguretat (a Configuració | Usuari | Grups), a la llista desplegable Application.
Aquestes altres claus també estan disponibles:
- installable és per defecte True, però es pot establir en False per desactivar un mòdul.
- auto_install, si s’estableix en True, s’instal·larà automàticament tan aviat com totes les seves dependències ja estiguin instal·lades. S’utilitza per mòduls de “coa”, connectant dues característiques juntes tan aviat com les dues s’instal·len en la mateixa instància.
Addició d’una icona
Per fer això, només ens cal afegir un arxiu a static/description/icon.png amb la icona que s’utilitzarà.
Anem a reutilitzar la icona de l’aplicació del Notes existent, pel que hem de copiar el fitxer Odoo/addons/note/static/description/icon.png en el directori custom-addons/todo_app/static/description.
Ho podrem fer amb aquestes ordres:
$ cd ~/odoo-dev
$ mkdir -p ./custom-addons/todo_app/static/description
$ cp /opt/bitnami/apps/odoo/lib/odoo-11.0.post20190218-py3.7.egg/odoo/addons/note/static/description/icon.png ./custom-addons/perfer_app/static/description/.
Més endavant podrem veure la nova icona. Però encara ens queden coses per fer.
Sobre les llicències
Triar una llicència per al treball és molt important, i heu de considerar acuradament quina és la millor opció, i les seves implicacions. Les llicències més utilitzades per als mòduls de Odoo són la LGPL v3 (LGPLv3) i la Affero General Public License v3 (AGPLv3). La LGPL és més permissiva i permet modificacions comercials, sense necessitat de compartir el codi font corresponent. La AGPL és una llicència de codi obert més forta, i requereix treball derivat i allotjament de serveis per compartir el codi font. Més informació sobre les llicències GNU a https://www.gnu.org/licenses/.
Descobrir i instal·lar nous mòduls
Ara tenim un mòdul addon amb una estructura mínima, però es pot instal·lar a la nostra instància de Odoo. Abans que puguem fer això, el nostre servidor Odoo necessita saber sobre aquest nou directori que estem utilitzant per als nostres mòduls personalitzats.
Això és el que farem a continuació.
Addició de la ruta dels complements
Per tenir nous mòduls disponibles a Odoo, necessitem assegurar-nos que el directori que conté el mòdul està en la ruta dels complements, després actualitzar la llista de mòduls de Odoo.
Per això aturarem la instància d’Odoo i la reiniciarem.
Al nostre cas per aturar la instància d’Odoo hem de fer el següent:
bitnami@debian:~$ sudo /opt/bitnami/ctlscript.sh stop odoo_gevent
bitnami@debian:~$ sudo /opt/bitnami/ctlscript.sh stop odoo_background_worker
Ara afegirem el nostre custom-addons path al fitxer de configuració d’Odoo.
bitnami@debian:~$ vi /home/bitnami/apps/odoo/conf/odoo-server.conf
i modificarem la línia que comença per addons_path per a que quedi així:
addons_path = /home/bitname/odoo-dev/custom-addons,/opt/bitnami/apps/odoo/lib/od
oo-11.0.post20190218-py3.7.egg/odoo/addons
guardam el fitxer i reiniciam el servidor fent:
bitnami@debian:~$ sudo /opt/bitnami/ctlscript.sh start odoo_gevent
bitnami@debian:~$ sudo /opt/bitnami/ctlscript.sh start odoo_background_worker
Recordeu també incloure qualsevol altre directori de complements que feu servir. Per exemple, si també teniu un directori ./odoo-dev/extra que conté mòduls addicionals que es van a utilitzar, és possible que vulgueu incloure també, utilitzant –addons-path option:
--addons-path = "custom-addons,extra..."
Ara necessitem que la instància de Odoo reconegui el nou mòdul que acabem d’afegir.
Addicionalment ens convé modificar els permisos de qualcun directori d’Odoo
cd /opt/bitnami/apps/odoo/data/
sudo usermod -a -G daemon bitnami
sudo chmod -Rf g+w sessions
sudo chown -Rf daemon:bitnami sessions
Instal·lació del nou mòdul
Abans que es pugui instal·lar un nou mòdul addon, la llista de mòduls s’ha d’actualitzar.
Això es pot fer des del menú superior Aplicacions i l’opció Actualitzar llista d’aplicacions. Actualitza la llista de mòduls, afegint els mòduls que s’hagin afegit des de l’última actualització de la llista. Recordeu que necessitem el mode de desenvolupador habilitat perquè aquesta opció sigui visible.
L’opció de menú Aplicacions ens mostra la llista de mòduls disponibles. Per defecte, mostra només els mòduls d’aplicació. Ja que hem creat un mòdul d’aplicació, no cal treure aquest filtre per veure-ho.
Ara feu clic al botó Instal·lar del mòdul.
Actualització d’un mòdul
El desenvolupament d’un mòdul és un procés iteratiu, i voldrà que els canvis realitzats en els arxius d’origen s’apliquin i facin visibles en Odoo.
En molts casos, això es fa mitjançant l’actualització del mòdul: buscar el mòdul a la llista d’aplicacions i una vegada que ja està instal·lat, tindrà un botó d’actualització disponible.
No obstant això, quan els canvis només són al codi de Python, és possible que l’actualització no tingui un efecte. En lloc d’una actualització de mòdul, es necessita un reinici de servidor d’aplicacions. Atès que Odoo càrrega codi Python només una vegada, qualsevol canvi posterior en el codi requereix que s’apliqui un reinici de servidor.
En alguns casos, si els canvis del mòdul es van realitzar tant en arxius de dades com en codi de Python, és possible que necessiti dues operacions. Aquesta és confusió comú per als nous desenvolupadors de Odoo.
Quan necessiteu aplicar el treball realitzat en mòduls, la forma més segura és reiniciar la instància de Odoo. Degut a la particularitat de la instància d’Odoo que feu servir, haurem d’utilitzar unes ordres específiques per aturar en aquest moment l’instància:
bitnami@debian:~$ sudo /opt/bitnami/ctlscript.sh stop odoo_gevent
bitnami@debian:~$ sudo /opt/bitnami/ctlscript.sh stop odoo_background_worker
D’aquesta manera aturam Odoo i passarem a utilitzar la línia d’ordres per arrancar i aturar Odoo d’una altra manera
bitnami@debian:~$ odoo-bin -c /opt/bitnami/apps/odoo/conf/odoo-server.conf -d perfer -u perfer_app
Penseu que aquí fem servir les opcions -d i -u per actualitzar el mòdul. Habitualment no serà necessari fer-ho.
Per altra banda la opció -c
Quan vulguem aturar la instància, simplement pitjarem CTRL+C a la finestra de la línia d’ordres activa.
La capa model
Ara que Odoo coneix el nostre nou mòdul, comencem afegint un model simple.
Els models descriuen objectes de negoci, com una oportunitat, una comanda de vendes o un soci (client, proveïdor, etc.). Un model té una llista d’atributs i també podeu definir els vostres propis.
Els models s’implementen mitjançant una classe Python derivada d’una classe de plantilla de Odoo. Es tradueixen directament als objectes de base de dades, i Odoo s’encarrega automàticament d’això a l’instal·lar o actualitzar el mòdul. El component responsable d’això és l’assignació relacional d’objectes (ORM).
El nostre mòdul serà una aplicació molt senzilla per mantenir les tasques pendents. Aquestes tasques tindran un únic camp de text per a la descripció i una casella de verificació per a marcar-les com completes. Més endavant hem d’afegir un botó per eliminar les tasques antigues i completades de la llista de tasques pendents.
Creació del model de dades
Les directrius de desenvolupament de Odoo estableixen que els arxius Python per als models han de col·locar-se dins d’un subdirectori de models. Per tant, crearem un arxiu models/todo_task_model.py al directori principal del mòdul todo_app.
Les directrius oficials de codificació de Odoo es poden trobar a http://www.odoo.com/documentation/11.0/reference/guidelines.html. Un altre document de normes de codificació és el de les directrius de codificació de la Associació Comunitària de Odoo a odoo-community.org/CONTRIBUTING.rst at master · OCA/odoo-community.org.
Abans d’això, ens cal fer saber a Python que el directori models ha de ser utilitzat (importat). Per a això, editeu el fitxer __init__.py principal del mòdul de la següent manera:
from . import models
També necessitem afegir un arxiu models/__ init__.py, important l’arxiu de codi Python que s’utilitzarà:
from . import todo_task_model
Ara podem crear l’arxiu models/todo_task_model.py amb el següent contingut:
from odoo import api, fields, models
class PerferTasca(models.Model):
_name = 'perfer.tasca'
_description = 'Tasca Per Fer'
name = fields.Char('Descripcio', required=True)
is_done = fields.Boolean('Fet?')
active = fields.Boolean('Actiu?', default=True)
user_id = fields.Many2one(
'res.users',
string='Responsable',
default=lambda self: self.env.user)
team_ids = fields.Many2many('res.partner', string='Equip')
La primera línia és un marcador especial que indica a l’intèrpret de Python que aquest arxiu té UTF-8 perquè pugui esperar i controlar caràcters que no siguin ASCII. No farem servir cap, però és una bona pràctica tenir-lo de totes maneres.
La segona línia és una instrucció d’importació de codi Python, fent que els objectes model i camp del nucli de Odoo estiguin disponibles.
La tercera línia declara el nostre nou model. És una classe derivada de models.Model.
La següent línia l’atribut _name, defineix l’identificador que es farà servir en tot Odoo per fer referència a aquest model. Recordeu que el nom real de la classe Python, TodoTask en aquest cas, no té sentit per a altres mòduls de Odoo. El valor _name és el que es farà servir com a identificador.
Observa que aquesta i les següents línies tenen sangria. Si no està familiaritzat amb Python, ha de saber que això és important: la sagnia defineix un bloc de codi niat, de manera que aquestes quatre línies han de tenir la mateixa sagnia.
A continuació, tenim l’atribut de model _description. No és obligatori, però proporciona un nom fàcil d’utilitzar per als registres de model, que es pot utilitzar per a millors missatges d’usuari.
Les línies restants defineixen els camps del model. Val la pena assenyalar que el name i active són noms de camp especials. Per defecte, Odoo usarà el camp name com a títol de registre al fer referència a ell des d’altres models. El camp active s’utilitza per activar registres i, per defecte, només es mostraran els registres actius. El farem servir per esborrar les tasques completades sense eliminar-les de la base de dades.
També podem veure exemples de com agregar camps amb relacions a altres models. El camp user_id permet seleccionar el propietari de l’usuari de la tasca. Hem afegit un valor predeterminat perquè l’usuari actual s’estableixi automàticament com a responsable de la tasca.
Finalment, team_ids ens permet seleccionar una llista de Partners involucrats en la Tasca. És una relació de diversos a diversos, on cada tasca pot tenir molts membres de l’equip, i cada soci pot participar en moltes tasques.
Això és tot. Perquè el nostre codi Python s’actualitzi, el mòdul s’ha d’actualitzar, desencadenant la creació dels objectes corresponents a la base de dades.
No veurem cap opció de menú per accedir a aquest nou model ja que encara no les hem afegit. Tot i així, podem inspeccionar el model acabat de crear usant el menú Tècnic. Al menú superior Settings, aneu a General Settings Technical, Database Structure, Models, cerqueu el model perfer.task a la llista i feu clic per veure la seva definició
Si tot surt bé, podem veure el model i els camps s’han creat correctament. Si no ho podeu veure, intenteu reiniciar el servei com s’ha vist abans.
També podem veure alguns camps addicionals que no hem declarat. Aquests són camps reservats que Odoo afegeix automàticament a cada nou model. Són els següents:
- id és un identificador numèric únic per a cada registre d’el model.
- create_date i create_uid especifiquen quan es va crear el registre i qui el va crear, respectivament.
- write_date i write_uid confirma quan es va modificar per última vegada el registre i qui el va modificar, respectivament.
- __last_update és una aplicació auxiliar que no s’emmagatzema realment a la base de dades. S’utilitza per a comprovacions de simultaneïtat.
Ampliació dels models existents
Els nous models es defineixen a través de classes de Python. L’ampliació de models també es realitza a través de classes de Python, amb l’ajuda d’un mecanisme d’herència específic de Odoo, l’atribut de classe _inherit. Aquest atribut de classe _inherit identifica el model que es va a estendre. La nova classe hereta totes les característiques del model primari de Odoo i només necessitem declarar les modificacions que s’han d’introduir.
En el nostre model perfer.tasca, tenim un camp many to many per a l’equip de persones (Partners) que col·laborarà amb nosaltres en aquesta tasca. Ara afegirem al model de Partners la relació inversa, perquè puguem veure, per a cada soci, totes les Tasques en les que estan involucrats.
Les directrius d’estil de codificació recomanen tenir un arxiu Python per a cada model. Així que afegirem un arxiu models/res_partner_model.py per implementar les nostres extensions a Partners.
Editeu el fitxer models/__ init__. pi per importar també aquest nou arxiu de codi. El contingut hauria de tenir aquest aspecte:
from . import perfer_tasca_model
from . import res_partner_model
Ara crea el models/res_partner_model.py amb el següent codi:
from odoo import fields, models
class ResPartner(models.Model):
_inherit = 'res.partner'
perfer_ids= fields.Many2many(
'perfer.tasca',
string="Equips Per fer")
Fem servir l’atribut de classe _inherit per declarar el model que s’estendrà. Recordeu que no fem servir cap altre atribut de classe, ni tan sols el _name.Això no cal, llevat que vulguem fer canvis en qualsevol d’ells.
Podeu pensar en això com obtenir una referència a una definició de model que viu en un registre central i realitzar canvis in situ en ell. Poden ser agregar camps, modificar camps existents, modificar atributs de classe de model o fins i tot mètodes amb lògica de negocis.
En el nostre cas, afegim un nou camp, perfer_ids, amb la inversa de la relació entre Tasques i Partners, definida en el model Tasca. Hi ha alguns detalls aquí que estan sent manejats automàticament pel ORM, però no són rellevants per a nosaltres en aquest moment.
Estendre els camps existents segueix una lògica similar: només necessitem usar els atributs que es van a modificar. Tots els atributs omesos mantindran el valor original.
Per tenir els nous camps de model agregats a les taules de base de dades, ara hem de realitzar una actualització de mòdul. Si tot surt com s’esperava, el camp recent agregat ha de ser vist a l’inspeccionar el model res.partner, al menú tècnic que hem mirat abans.
La capa de vista
La capa de vista descriu la interfície d’usuari. Les vistes es defineixen mitjançant XML, que utilitza el marc de treball de el client web per generar vistes HTML amb reconeixement de dades.
Tenim elements de menú que poden activar accions que poden representar vistes. Per exemple, l’element de menú Usuaris processa una acció també anomenada Usuaris, que al seu torn representa una sèrie de vistes. Hi ha diversos tipus de vista disponibles, com la llista (de vegades anomenada arbre per motius històrics), els formulari, i el filtre opcions disponibles al quadre de cerca superior dret també es defineixen mitjançant un tipus determinat de vista, la vista de cerca.
Les directrius de desenvolupament de Odoo estableixen que els arxius XML que defineixen la interfície d’usuari han de col·locar-se dins d’un subdirectori views/.
Comencem a crear la interfície d’usuari per a la nostra aplicació.
En les següents seccions, realitzarem millores graduals i actualitzacions freqüents de mòduls perquè aquests canvis estiguin disponibles. És possible que també vulgueu provar l’opció –dev=all servidor, que ens estalvia de les actualitzacions de mòduls durant el desenvolupament. Amb ell, les definicions de vista es llegeixen directament des dels arxius XML perquè els canvis puguin estar immediatament disponibles per Odoo sense necessitat d’una actualització de mòdul.
Addició d’elements de menú
Ara que tenim els llocs per emmagatzemar les nostres dades, volem tenir-los disponibles en la interfície d’usuari. El primer que cal fer ha de ser afegir les opcions de menú corresponents.
Creu el fitxer views/perfer_menu.xml per definir un element de menú i l’acció realitzada per ell:
<?xml version="1.0"?>
<odoo>
<!-- Action to open To-do Task list -->
<act_window id="accio_perfer_tasca"
name="Tasca Per Fer"
res_model="perfer.tasca"
view_mode="tree,form"
/>
<!-- Menu item to open To-do Task list -->
<menuitem id="menu_perfer_tasca"
name="Tots"
action="accio_perfer_tasca"
/>
</odoo>
La interfície d’usuari, incloses les opcions de menú i les accions, s’emmagatzema en taules de base de dades. L’arxiu XML és un arxiu de dades que s’utilitza per carregar aquestes definicions a la base de dades quan s’instal·la o actualitza el mòdul addon. El codi anterior és un arxiu de dades de Odoo, que descriu dos registres per afegir a Odoo:
- L’element <act_window> defineix una acció de finestra de la banda client que obrirà el model perfer.tasca amb les vistes d’arbre i formulari habilitades, en aquest ordre.
- El <menuitem> defineix un element de menú superior que crida a l’acció accio_perfer_tasca, que hem definit abans.
Ambdós elements inclouen un atribut id. Aquest atribut id, denominat XML ID, és molt important; s’utilitza per a identificar de forma única cada element de dades dins del mòdul i és el mètode que altres elements poden usar per fer referència a ell. En aquest cas, l’element <menuitem> ha de fer referència a l’acció que es va a processar, pel que ha de fer servir l’identificador XML <act_window> per a això.
El nostre mòdul encara no sap sobre aquest nou arxiu de dades XML. Perquè això succeeixi, ha de declarar-se en l’arxiu __manifest__.py, utilitzant l’atribut data. És una llista dels arxius de dades que ha de carregar el mòdul després de la instal·lació o actualització.
Ara afegiu aquest atribut al diccionari del manifest:
'data': ['views/perfer_menu.xml'],
Necessitem actualitzar el mòdul de nou perquè aquests canvis tinguin efecte. Aneu a del menú superior de Tots i haurieu de veure la nostra nova opció de menú disponible. Al fer clic en ell es motra una vista de llista bàsica, i tindrem disponible una vista de formulari generada automàticament:
Encara que no hem definit la vista d’interfície d’usuari, les vistes de llista i de formulari generades automàticament són funcionals i ens permeten començar a editar dades de forma immediata.
Creació de la vista de formulari
Totes les vistes s’emmagatzemen a la base de dades, en el model ir.ui.view. Per afegir una vista a un mòdul, declaram un element <record> que descriu la vista en un arxiu XML, que es carregarà a la base de dades quan s’instal·li el mòdul.
Afegiu aquest nou arxiu views/perfer_vista.xml per definir la vista de formulari:
<?xml version="1.0"?>
<odoo>
<record id="vista_formulari_perfer_tasca" model="ir.ui.view">
<field name="name">Formulari Tasca Per Fer</field>
<field name="model">perfer.tasca</field>
<field name="arch" type="xml">
<form string="Tasca Per fer">
<group>
<field name="name"/>
<field name="is_done"/>
<field name="active" readonly="1"/>
</group>
</form>
</field>
</record>
</odoo>
El registre ir.ui.view té valors per tres camps: name, model, i arch. Un altre element important és l’identificador de registre id. Defineix un ID XML que es pot utilitzar perquè altres registres facin referència a ell.
La vista és pel model perfer.tasca i es denomina Formulari Tasca Per Fer. El nomb és només a títol informatiu; no té perquè ser únic, però ha de permetre que qualsevol identifiqui fàcilment a quin registre es refereix. De fet, el nom es pot ometre; en aquest cas, es generarà automàticament a partir del nom del model i del tipus de vista.
El camp més important es campo más importante és arch, ja que conté la definició de la vista, resaltada en el codi XML anterior. L’etiqueta <form> defineix el tipus de vista i, en aquest cas, conté tres camps. Afegim l’atribut define el tipo de vista y, en este caso, contiene tres campos. Agregamos el atributo readonly al camp actiu, perquè sigui de només lectura en l’interfície d’usuari.
Recordeu afegir aquest nou arxiu a l’arxiu __manifers__.py; en cas contrari, el nostre mòdul no carregarà la vista:
{
'name': 'Aplicació de tasques pendent',
'description': 'Gestio tasques personals pendents',
'author': 'Xavier Sastre',
'depends': ['base'],
'application': True,
'data': [
'views/perfer_menu.xml',
'views/perfer_vista.xml',
],
}
Recordeu que perquè els canvis es carreguin a la base de dades d’Odoo, es necessita una actualització del mòdul. Per veure els canvis en el navegador, el formulari s’ha de tornar carregar.
Vista de formulari
La secció anterior ens ha proporcionat una vista de formulari bàsica, però podem realitzar millores sobre ella. Pels models de documents, Odoo té un estil de presentació que imita una pàgina de paper. Aquest formulari conté dos elements: <header> per disposar-hi botons i <sheet> per disposar-hi els camps de dades.
Ara podem reemplaçar el <form> bàsic definit en la secció anterior amb aquest altre:
<form>
<header>
<!-- Buttons go here-->
</header>
<sheet>
<!-- Content goes here: -->
<group>
<field name="name"/>
<field name="is_done"/>
<field name="active" readonly="1"/>
</group>
</sheet>
</form>
Afegim botons
Els formularis poden tenir botons per realitzar accions. Aquests botons poden executar accions, com obrir un altre formulari o executar funcions de Python definides en el model.
Es poden col·locar en qualsevol lloc dins d’un formulari, però pels formularis que tractem ara, el lloc recomanat és la secció <header>.
Per la nostra aplicació, afegirem un botó per esborrar una tasca feta. Aquest botó executarà el mètode d’objecte esborrar_fet:
<header>
<!-- Buttons go here-->
<button name="esborrar_fet" type="object"
string="Esborrar fet" />
</header>
Els atributs bàsics d’un botó consten dels següents elements:
- string amb el text que es mostrarà en el botó
- type d’acció que realitza
- name és l’identificador per aquesta acció
- class és un atribut opcional per aplicar estils CSS, com a un HTML
Ús de grups per organitzar els formularis
L’etiqueta <group> permet organitzar el contingut del formulari. Col·locar elements <group> dins d’un element <group> crea un disseny de dues columnes dins del grup extern. Es recomana que els elements de grup tinguin un atribut name perquè sigui més fàcil ampliar-los per altres mòduls..
Utilitzarem això per organitzar millor el nostre contingut. Canviem el contingut de <sheet> amb el següent codi:
<sheet>
<group name="grup_extern">
<group name="grup_esquerra">
<field name="name" />
<field name="user_id" />
<field name="is_done" />
</group>
<group name="grup_dreta">
<field name="date_deadline" />
<field name="team_ids" widget="many2many_tags" />
<field name="active" readonly="1" />
</group>
</group>
</sheet>
La vista completa del formulari
En aquest punt, el nostre formulari perfer.tasca hauria de tenir aquest aspecte:
<form>
<header>
<!-- Buttons go here-->
<button name="Esborrar_fet" type="object"
string="Esborrar fet" />
</header>
<sheet>
<group name="grup_extern">
<group name="grup_esquerra">
<field name="name" />
<field name="user_id" />
<field name="is_done" />
</group>
<group name="grup_dreta">
<field name="date_deadline" />
<field name="team_ids" widget="many2many_tags" />
<field name="active" readonly="1" />
</group>
</group>
</sheet>
</form>
Els botons encara no funcionaran ja que falta afegir la seva lògica de negoci.
Afegint llistes i vistes de cerca
Quan visualitzem un model en mode de llista, s’utilitza una vista <tree>. Les vistes tree són capaces de mostrar línies organitzades en jerarquies, però la majoria de vegades s’utlitzen per mostrar llistes sense format o planes.
Podem afegir la següent definició de vista <tree> a perfer_vista.xml:
<record id="vista_arbre_perfer_tasca" model="ir.ui.view">
<field name="name">Formulari Tasca Per Fer</field>
<field name="model">perfer.tasca</field>
<field name="arch" type="xml">
<tree colors="decoration-muted:is_done==True">
<field name="name"/>
<field name="is_done"/>
</tree>
</field>
</record>
Això defineix una llista amb només dues columnes: name i is_done. També hem afegit una línia per les tasques realitzades (is_done==True) que es mostrarà com atenuada. Això es fa aplicant la classe muted Bootstrap. Consulteu http://getbootstrap.com/docs/3.3/css/#helper-classes-colors per obtenir més informació sobre Bootstrap i els seus colors contextuals.
Al cantó superior dret de la llista, Odoo mostra un quadre de cerca. Els camps en els que cerca i els filtres disponibles es defineixen mitjançant una vista <search>.
Com hem fet abans, afegirem això a Como antes, añadiremos esto a perfer_vista.xml:
<record id="vista_filtre" model="ir.ui.view">
<field name="name">Filtre Tasca Per Fer</field>
<field name="model">perfer.tasca</field>
<field name="arch" type="xml">
<search>
<field name="user_id"/>
<filter string="No està fet"
domain="[('is_done','=',False)]"/>
<filter string="Fet"
domain="[('is_done','!=',False)]"/>
</search>
</field>
</record>
Els elements <field> defineixen els camps que també es cerquen quan escrivim en el quadre de cerca. Hem afegit user_id per suggerir automàticamen la cerca en el camp Responsable.
Ampliació de vistes
Els formularis, les llistes i les vistes de cerca es defineixen mitjançant les estructures arch XML. Per ampliar les vistes, ens cal una forma de modificar aquest XML. Això vol dir localitzar elements XML i, a continuació, introduir modificacions en aquests punts.
El registre XML per l’herència de vistes és igual que el de les vistes normals, però també l’atribut inherit_id, amb una referència a la vista que es vol estendre.
Com a exemple d’això, ampliarem la vista Partner perquè el camp perfer_ids sigui visible, mostrant totes les tasques en què està involucrada la persona.
El primer que cal fer és trobar l’identificador XML de la vista que volem estendre. Ho podem trobar mirant a Settings, Technical | User Interface | Views. El ID XML pel formulari Partner és base.view_partner_form. Aquí mateix també hem de trobar l’element XML on volem inserir els nostres canvis. Anem a afegir la llista de col·laboradors a la fi de les columnes dretes, després del camp Idioma. Normalment identifiquem l’element a utilitzar pel seu atribut name. En aquest cas, tenim <field name=”lang” />.
Afegirem un arxiu XML per les extensions fetes a les vistes de Partner, views/res_partner_view.xml, amb aquest contingut:
<?xml version="1.0"?>
<odoo>
<record id="vista_heretada" model="ir.ui.view">
<field name="name">Afegeix Per Fers al formulari Partner</field>
<field name="model">res.partner</field>
<field name="inherit_id" ref="base.view_partner_form"/>
<field name="arch" type="xml">
<field name="lang" position="after">
<field name="todo_ids" />
</field>
</field>
</record>
</odoo>
El campo inherit_id identifica la vista que es vol ampliar fent referència al seu identificador extern mitjançant l’atribut especial ref.
Normalment utilitzam XPath per localitzar elements XML. Odoo també utilitza XPath per això, però també té disponible la forma més simple que acabam d’usar.
Els altres tipus de vista, com les vistes de llista i de cerca, també tenen un camp arch i es pot ampliar de la mateixa manera que les vistes de formulari.
La capa de lògica de negoci
Ara afegirem la lògica pel nostre botó. Això es fa amb codi Python, utilitzant els mètodes de la classe Python del model.
Afegint lògica de negoci
Hem d’editar l’arxiu perfer_tasca_model.py per afegir els mètodes cridats o invocats pel botó. En primer lloc, necessitem importar la nova API, així que afegiu-la a la instrucció import en la part superior de l’arxiu:
from odoo import api, fields, models
La lògica del botó Esborrar Fet és bastant simple. Simplement establir el flag “Actiu?” a falso. Això aprofita una característica integrada d’ORM: els registres amb una marca active posada a false, per defecte es filtres i no es mostraran a l’usuari.
A l’arxiu models/perfer_tasca_model.py afegiu el següent codi a la classe regue lo siguiente a la clase PerferTasca:
@api.multi
def esborrar_fet(self):
for task in self:
task.active = False
return True
Per la lògica dels registres, utilitzem el decorator @api.multi. Aquí, self representarà un conjunt de registres, i després farem un recorregut en bucle per cada registre. De fet, aquest és el valor predeterminat pels mètodes Model, per tant en aquest cas el decorator @api.multi es pot ometre amb seguretat. En qualsevol cas, millor deixar-lo per una major claretat.
El codi recorre tots els registres de tasques pendents i, per cada un, assigna el valor False al camp actiu.
El mètode no necessita retornar res, però hem de menester al menys un return True. El motiu per això és que no totes les implementacions de client del protocal XMLRPC admeten valors None/Null i poden generar errors quan un mètode torna aquest valor.
Ampliació dels mètodes de Python
Els mètodes de Python també es poden ampliar per un lògica de negoci adicional. Això es fa agafant el mecanisme que Python ja proporciona perquè els objectes heretats facin una extensió dels seu comportament de classe primària: super().
Com exemple, farem què la class PerferTasca ampliï el mètode write() de ORM per afegir-li una mica de lògica: quan escrigui en un registre no actiu, el reactivarà automàticament..
Afegiu aquest mètode addicional a l’arxiu models/perfer_tasca_model.py:
@api.multi
def write(self, values):
if 'active' not in values:
values['active'] = True
super().write(values)
El mètode write() espera un paràmetre addicional, amb un “diccionari de valors” per escriure en el registre. A no ser que s’estableixi un valor en el camp actiu, per defecte l’establim a True en el cas en que estem escrivint en un registre inactiu. A continuació, utilitzem super() per cridar al mètode pare write() utilitzant els valors modificats.
Resum
Hem creat un nou mòdul completament des de zero, utilitzant la instància d’Odoo proporcionada en exercicis anteriors. En aquesta aplicació hem cobert els elements més utilitzats en un mòdul: models, els tres tipus de vistes bàsics (formulari, lllista i cerca) i lògica de negoci en els mètodes del model.
Durant tot el procés hem vist com és el procés de desenvolupament del mòdul, que implica actualitzacions de mòduls i reinicis del servidor perquè els canvis graduals siguin efectius a Odoo.
Recordeu que sempre quan afegim canvis de model, es necessita una actualització del mòdul a Odoo. Quan canviem codi Python, inclòs el manifest, es necessita un reinici de la instància. En cas de dubte, reinicieu la instància i actualitzeu els mòduls.
Aquí teniu un arxiu amb tot el codi