0) Punt de partida
- L’entorn de dev està configurat segons la Pàg. 1 i l’Odoo s’executa a http://localhost:8070 amb la BD
dev_escola. - Carpeta per a mòduls:
~/odoo-dev/custom-addons. - Mode desenvolupador activat (Settings → Activate the developer mode).
1) Crear el mòdul “escola”
odoo scaffold escola ~/odoo-dev/custom-addons
Si no tens scaffold, crea manualment la carpeta i fitxers tal com mostrem més avall.
2) Estructura i manifest
Edita ~/odoo-dev/custom-addons/escola/__manifest__.py amb aquest contingut (ordre de fitxers clau: accions abans dels menús).
{
"name": "Escola",
"version": "17.0.1.0.0",
"category": "Education",
"summary": "Gestió d'alumnes, mòduls i professors",
"author": "Tu",
"depends": ["base"],
"data": [
"security/ir.model.access.csv",
"views/escola_actions.xml", # ACCIONS primer
"views/escola_menus.xml", # MENÚS després
"views/alumne_views.xml",
"views/modul_views.xml",
"views/professor_views.xml"
],
"installable": True,
"application": True
}
Amb això evitem l’error vist anteriorment:
External ID not found: escola.action_alumnes.3) Models
Crea els fitxers a ~/odoo-dev/custom-addons/escola/models.
3.1 __init__.py
from . import professor
from . import modul
from . import alumne
3.2 professor.py — (solució Many2one: nom complet)
from odoo import models, fields, api
class EscolaProfessor(models.Model):
_name = "escola.professor"
_description = "Professor"
_rec_name = "name" # camp que es mostra als Many2one
# nom i cognoms, i un 'name' calculat per mostrar-se arreu
first_name = fields.Char(string="Nom", required=True)
last_name = fields.Char(string="Cognoms", required=True)
name = fields.Char(string="Nom complet", compute="_compute_name", store=True)
email = fields.Char()
@api.depends('first_name', 'last_name')
def _compute_name(self):
for rec in self:
parts = [p for p in [rec.first_name, rec.last_name] if p]
rec.name = " ".join(parts) if parts else False
Fix Many2one: definim
_rec_name = "name" i calculem name amb nom+cognoms. Ara els Many2one mostraran el nom del professor, no “escola.professor,1”.3.3 modul.py
from odoo import models, fields
class EscolaModul(models.Model):
_name = "escola.modul"
_description = "Mòdul formatiu"
name = fields.Char(string="Nom del mòdul", required=True)
code = fields.Char(string="Codi")
professor_id = fields.Many2one(
"escola.professor",
string="Professor",
ondelete="set null",
)
alumne_ids = fields.Many2many(
"escola.alumne",
"escola_alumne_modul_rel",
"modul_id", "alumne_id",
string="Alumnes"
)
3.4 alumne.py
from odoo import models, fields
class EscolaAlumne(models.Model):
_name = "escola.alumne"
_description = "Alumne"
name = fields.Char(string="Nom i cognoms", required=True)
email = fields.Char()
modul_ids = fields.Many2many(
"escola.modul",
"escola_alumne_modul_rel",
"alumne_id", "modul_id",
string="Mòduls"
)
4) Vistes
Crea els fitxers a ~/odoo-dev/custom-addons/escola/views.
4.1 professor_views.xml
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="view_professor_tree" model="ir.ui.view">
<field name="name">escola.professor.tree</field>
<field name="model">escola.professor</field>
<field name="arch" type="xml">
<tree>
<field name="name"/>
<field name="first_name"/>
<field name="last_name"/>
<field name="email"/>
</tree>
</field>
</record>
<record id="view_professor_form" model="ir.ui.view">
<field name="name">escola.professor.form</field>
<field name="model">escola.professor</field>
<field name="arch" type="xml">
<form>
<sheet>
<group>
<field name="first_name"/>
<field name="last_name"/>
<field name="name" readonly="1"/>
<field name="email"/>
</group>
</sheet>
</form>
</field>
</record>
</odoo>
4.2 modul_views.xml
<odoo>
<record id="view_modul_tree" model="ir.ui.view">
<field name="name">escola.modul.tree</field>
<field name="model">escola.modul</field>
<field name="arch" type="xml">
<tree>
<field name="name"/>
<field name="code"/>
<field name="professor_id"/>
</tree>
</field>
</record>
<record id="view_modul_form" model="ir.ui.view">
<field name="name">escola.modul.form</field>
<field name="model">escola.modul</field>
<field name="arch" type="xml">
<form>
<sheet>
<group>
<field name="name"/>
<field name="code"/>
<field name="professor_id"/>
<field name="alumne_ids" widget="many2many_tags"/>
</group>
</sheet>
</form>
</field>
</record>
</odoo>
4.3 alumne_views.xml
<odoo>
<record id="view_alumne_tree" model="ir.ui.view">
<field name="name">escola.alumne.tree</field>
<field name="model">escola.alumne</field>
<field name="arch" type="xml">
<tree>
<field name="name"/>
<field name="email"/>
<field name="modul_ids" widget="many2many_tags"/>
</tree>
</field>
</record>
<record id="view_alumne_form" model="ir.ui.view">
<field name="name">escola.alumne.form</field>
<field name="model">escola.alumne</field>
<field name="arch" type="xml">
<form>
<sheet>
<group>
<field name="name"/>
<field name="email"/>
<field name="modul_ids"/>
</group>
</sheet>
</form>
</field>
</record>
</odoo>
5) Accions i menús
5.1 escola_actions.xml
<odoo>
<record id="action_alumnes" model="ir.actions.act_window">
<field name="name">Alumnes</field>
<field name="res_model">escola.alumne</field>
<field name="view_mode">tree,form</field>
</record>
<record id="action_moduls" model="ir.actions.act_window">
<field name="name">Mòduls</field>
<field name="res_model">escola.modul</field>
<field name="view_mode">tree,form</field>
</record>
<record id="action_professors" model="ir.actions.act_window">
<field name="name">Professors</field>
<field name="res_model">escola.professor</field>
<field name="view_mode">tree,form</field>
</record>
</odoo>
5.2 escola_menus.xml
<odoo>
<menuitem id="menu_escola_root" name="Escola"/>
<menuitem id="menu_escola_alumnes" name="Alumnes"
parent="menu_escola_root" action="action_alumnes"/>
<menuitem id="menu_escola_moduls" name="Mòduls"
parent="menu_escola_root" action="action_moduls"/>
<menuitem id="menu_escola_professors" name="Professors"
parent="menu_escola_root" action="action_professors"/>
</odoo>
Recorda: accions primer, menús després. Així evitem el External ID not found.
6) Permisos
Crea ~/odoo-dev/custom-addons/escola/security/ir.model.access.csv:
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_escola_alumne,access.escola.alumne,model_escola_alumne,base.group_user,1,1,1,1
access_escola_modul,access.escola.modul,model_escola_modul,base.group_user,1,1,1,1
access_escola_professor,access.escola.professor,model_escola_professor,base.group_user,1,1,1,1
7) Instal·lació i proves
- Reinicia/arrenca el servidor de dev:
odoo -c ~/.odoorc.dev -d dev_escola --http-port=8070 --dev=all - A Apps: Update Apps List, busca “Escola” i Install.
(o bé per consola:
odoo -c ~/.odoorc.dev -d dev_escola -u escola --stop-after-init) - Ves a Escola → Professors, crea’n un (Nom + Cognoms). Ves a Escola → Mòduls, crea un mòdul i assigna-hi el professor → ara el Many2one mostra el nom complet.
- Ves a Escola → Alumnes, crea un alumne i afegeix-li mòduls. A la llista d’alumnes veuràs els mòduls com a tags.
8) Errors típics i solucions (recap)
“escola.professor,1” a Many2one
Defineix un camp de nom (ex.
Defineix un camp de nom (ex.
name) i usa _rec_name = "name". En aquest mòdul calculem name = first_name + last_name.External ID not found: escola.action_alumnes
Carrega
Carrega
escola_actions.xml abans d’escola_menus.xml al manifest i comprova els IDs.No surt “Update Apps List”
Activa el Developer mode a Settings.
Activa el Developer mode a Settings.
No apareix el mòdul
Confirma
Confirma
addons_path i repeteix Update Apps List. El nostre ~/.odoorc.dev ja inclou ~/odoo-dev/custom-addons.9) Cicle de desenvolupament
# Actualitzar només el mòdul 'escola' i sortir:
odoo -c ~/.odoorc.dev -d dev_escola -u escola --stop-after-init
# Tornar a deixar-lo corrent al 8070:
odoo -c ~/.odoorc.dev -d dev_escola --http-port=8070 --dev=all
Què ve a la Pàg. 3?
- Ampliacions del mòdul escola: nous camps, constriccions, botons d’acció, seqüències, filtres, cerca avançada, informes…
- Exemples d’onchange, compute extra, i millores d’UX.