Odoo Developer Training: From Basis To First Module: Release 2020.10
Odoo Developer Training: From Basis To First Module: Release 2020.10
to First Module
Release 2020.10
3 Agenda 7
5 Docker environment 11
5.1 Docker environment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
5.2 Before start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
5.3 Setup Odoo Environment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
5.4 Running Odoo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
5.5 Adding OCA projects / modules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
i
ii
Odoo Developer Training: From Basis to First Module, Release 2020.10
CONTENTS 1
Odoo Developer Training: From Basis to First Module, Release 2020.10
2 CONTENTS
3
Odoo Developer Training: From Basis to First Module, Release 2020.10
CHAPTER
ONE
TWO
Note:
• Fell free to contact me after the presentation at OCA discord channel. I will be glad to clarify any remaining
doubts.
5
Odoo Developer Training: From Basis to First Module, Release 2020.10
THREE
AGENDA
7
Odoo Developer Training: From Basis to First Module, Release 2020.10
8 Chapter 3. Agenda
CHAPTER
FOUR
Note:
• TIP: A good developer must seek functional understanding. If you know a module that has functionality similar
to the one you want to develop, you can get good ideas by reading it.
Warning:
• OCA contributors use Twitter a lot! Find who they are here: https://github.com/orgs/OCA/people and follow
them.
9
Odoo Developer Training: From Basis to First Module, Release 2020.10
FIVE
DOCKER ENVIRONMENT
5.1.2 Akretion - AK
The toolbelt for odoo: https://github.com/akretion/ak
It does a lot of things, but in this training we will use it for only two:
1. Download the addons
2. Generate addons path
#Install with a normal user
python3 -m pip install git+https://github.com/akretion/ak --user
11
Odoo Developer Training: From Basis to First Module, Release 2020.10
Check https://odoo.localhost
Note: You can use Odoo SA shop to search modules too, but take a special attention in the author of the module.
Editing spec.yml:
odoo:
modules: []
src: https://github.com/odoo/odoo 13.0
web:
modules: []
src: https://github.com/OCA/web 13.0
When ak build has finished running it displays new addons path that you can must put in your docker-compose file
ak build
[...]
(INFO) [07:21:45] git_aggregator.repo src End aggregation of /home/mileo/Projects/oca-days-2020/oc
Addons path for your config file: /odoo/links,/odoo/local-src,/odoo/src/odoo/addons,/odoo/src/addons
TIP: This will add all the modules of the project OCA/web, if you just want the web_responsive you can use the
following syntax:
odoo:
modules: []
src: https://github.com/odoo/odoo 13.0
web:
modules: ['web_responsive']
src: https://github.com/OCA/web 13.0
[...]
- DB_PASS=$PGPASSWORD
- PGHOST=$PGHOST
- PGDATABASE=$PGDATABASE
- PGUSER=$PGUSER
- PGPASSWORD=$PGPASSWORD
- ADDONS_PATH=/odoo/links,/odoo/local-src,/odoo/src/odoo/addons,/odoo/src/addons,/odoo/external
hostname: ${ENV}-${COMPOSE_PROJECT_NAME}
labels:
docky.main.service: true
docky.user: odoo
volumes:
- ./odoo:/odoo
- ./data/addons:/data/odoo/addons
[...]
SIX
15
Odoo Developer Training: From Basis to First Module, Release 2020.10
- web_responsive
- version: 13.0.0.0.2
addons:
- customer_service
Note:
To learn more:
• You can see a complete Odoo manifest here: https://github.com/OCA/maintainer-tools/blob/master/
template/module/__manifest__.py
• The official documentation: https://www.odoo.com/documentation/14.0/reference/module.html
Note:
To learn more:
• Check the Odoo documentation: https://www.odoo.com/documentation/14.0/reference/orm.html
• Please read the Odoo code:
– odoo/src/odoo/models.py
– odoo/src/odoo/fields.py
With the help o bob.odoo we will create our first Odoo model with accompanying form, tree, action, menu, demo data
and ACL
cd customer_service # Go inside de module folder that you want to create the model
mrbob bobtemplates.odoo:model
Welcome to mr.bob interactive mode. Before we generate directory structure, some questions
need to be answered. Answer with a question mark to display help. Values in square
brackets at the end of the questions show the default value if there is no answer.
--> Odoo version (8|9|10|11|12) [12]: 13
--> Model name (dotted notation): customer.service.ticket
--> Inherit [y]: n
--> Form view [y]: y
--> Search view [y]: y
--> Tree view [y]: y
--> Action and menu entry [y]: y
--> ACL [y]: y
--> Demo data [y]: y
--> Copyright holder name: KMEE
--> Copyright year: 2020
Generated file structure at PROJECT_ROOT/odoo/local-src/customer_service
Note:
To learn more:
• You can see here a complete Odoo module: https://github.com/OCA/maintainer-tools/blob/master/
template/module/
Note:
To learn more:
• Read more about the views at the official documentation: https://www.odoo.com/documentation/14.0/
reference/views.html
Ensure that you put it before the customer_service_ticket, the order matters.
Let’s relate the service ticket menu with our new main menu, open the file: odoo/local-
src/customer_service/views/customer_service_ticket.xml and edit the last section:
[...]
<record model="ir.ui.menu" id="customer_service_ticket_menu">
<field name="name">Customer Service Ticket</field>
<field name="parent_id" ref="customer_service_main_menu"/> <!-- The id of the main menu -->
<field name="action" ref="customer_service_ticket_act_window"/>
<field name="sequence" eval="16"/>
</record>
Note:
To learn more:
• You can see here a complete Odoo module: https://github.com/OCA/maintainer-tools/blob/master/
template/module/
class CustomerServiceTicket(models.Model):
_name = 'customer.service.ticket'
_description = 'Customer Service Ticket' # TODO
name = fields.Char()
description = fields.Html(sanitize_style=True)
user_id = fields.Many2one('res.users')
partner_id = fields.Many2one('res.partner')
partner_email = fields.Char(related='partner_id.email')
Note:
• Check more about the Odoo fields: https://www.odoo.com/documentation/14.0/reference/orm.html#fields
• odoo/src/odoo/fields.py <——- There is nothing better than reading the source code!!!!
We need to add this new fields to the view, check your odoo/local-
src/customer_service/views/customer_service_ticket.xml, you will see that are five blocks of code:
1. Form view;
2. Search / Filter View (Add Filter, Group and Favorites search);
3. Tree / List view;
4. Action (Speaking in a very simple way, links the menu with the views);
5. Menu;
Add the all the new fields to the form view:
<record model="ir.ui.view" id="customer_service_ticket_form_view">
<field name="name">customer.service.ticket.form (in customer_service)</field>
<field name="model">customer.service.ticket</field>
<field name="arch" type="xml">
<form>
<header>
</header>
<sheet>
<group>
<field name="name"/>
<field name="description"/>
<field name="user_id"/>
<field name="partner_id"/>
<field name="partner_email"/>
</group>
</sheet>
<div class="oe_chatter"></div>
</form>
</field>
</record>
web:
modules: []
src: https://github.com/OCA/web 13.0
kmee-server-tools:
modules: ['base_kanban_stage']
src: https://github.com/kmee/server-tools 13.0-mig-base_kanban_stage
_name = 'customer.service.ticket'
_inherit = 'base.kanban.abstract'
By doing that we add all the functionalists of the model ‘base.kanban.abstract’ in our model.
Note:
To learn more:
• Odoo provides two inheritance mechanisms to extend an existing model in a modular way. https://www.
odoo.com/documentation/14.0/howtos/backend.html?highlight=inherit#inheritance
Add a new view record at customer_service_ticket.xml and the kanban to the view_mode
<record id="customer_service_ticket_kanban_view" model="ir.ui.view">
<field name="name">customer.service.ticket.kanban (in customer_service)</field>
<field name="model">customer.service.ticket</field>
<field name="mode">primary</field>
<field name="inherit_id" ref="base_kanban_stage.base_kanban_abstract_view_kanban"/>
We need to change the form view too, to add the field stage_id to the header:
<record model="ir.ui.view" id="customer_service_ticket_form_view">
<field name="name">customer.service.ticket.form (in customer_service)</field>
<field name="model">customer.service.ticket</field>
<field name="arch" type="xml">
<form>
<header>
<field name="stage_id" widget="statusbar"/>
</header>
<sheet>
<group>
<field name="name"/>
<field name="description"/>
<field name="user_id"/>
<field name="partner_id"/>
<field name="partner_email"/>
</group>
</sheet>
<div class="oe_chatter"></div>
</form>
Note:
To learn more:
• Odoo implements some useful classes and mixins that make it easy for you to add often-used behaviours
on your objects. This guide will details most of them, with examples and use cases. https://www.odoo.
com/documentation/14.0/reference/mixins.html?highlight=inherit
1. Add dependence:
{
'name': 'Customer Service',
'description': """
ACME Customer Service""",
'version': '13.0.1.0.0',
'license': 'AGPL-3',
'author': 'KMEE',
'website': 'www.kmee.com.br',
'depends': [
'base_kanban_stage',
'mail,
],
'data': [
'security/customer_service_ticket.xml',
'views/customer_service_menu.xml',
'views/customer_service_ticket.xml',
],
'demo': [
'demo/customer_service_ticket.xml',
],
}
_name = 'customer.service.ticket'
_inherit = ['base.kanban.abstract', 'mail.thread']
sms.send_message({
'from': 'Vonage APIs',
'to': '5535988763663',
'text': 'Hello from Vonage SMS API',
})
Odoo SA has a module sms which has two methods that we need to override, here is the Odoo SA code:
class SmsApi(models.AbstractModel):
_name = 'sms.api'
_description = 'SMS API'
@api.model
def _contact_iap(self, local_endpoint, params):
account = self.env['iap.account'].get('sms')
params['account_token'] = account.account_token
endpoint = self.env['ir.config_parameter'].sudo().get_param('sms.endpoint', DEFAULT_ENDPOINT)
return iap.jsonrpc(endpoint + local_endpoint, params=params)
@api.model
def _send_sms(self, numbers, message):
params = {'numbers': numbers, 'message': message}
return self._contact_iap('/iap/message_send', params)
@api.model
def _send_sms_batch(self, messages):
params = {'messages': messages}
return self._contact_iap('/iap/sms/1/send', params)
web:
modules: []
src: https://github.com/OCA/web 13.0
kmee-server-tools:
modules: ['base_kanban_stage']
src: https://github.com/kmee/server-tools 13.0-mig-base_kanban_stage
connector-telephony:
modules: []
src: https://github.com/YOUR_REMOTE_HERE/connector-telephony 13.0
'demo': [
],
}
Adding the two fields to the model is very easy, edit the file: PROJECT_ROOT/odoo/external-src/connector-
telephony/sms_nexmo/models/iap_account.py
from odoo import api, fields, models, _
class IapAccount(models.Model):
_inherit = 'iap.account'
key = fields.Char()
secret = fields.Char()
To add them to form view, the easy way is to find the view and at developer mode click on View Form View to discover
the external id of the main view.
Only with the external id in hand can we edit the inherited view.
<record model="ir.ui.view" id="iap_account_form_view">
<field name="name">iap.account.form (in sms_nexmo)</field>
<field name="model">iap.account</field>
<field name="inherit_id" ref="iap.iap_account_view_form"/>
<field name="arch" type="xml">
<field name="account_token" position="after">
<field name="key"/>
<field name="secret"/>
</field>
</field>
</record>
Note:
• TIP: Always remember to use the complete name: <MODULE><DOT><RECORD_ID>
• Check the documentation about the view inheritance: https://www.odoo.com/documentation/14.0/reference/
views.html?highlight=inherit#inheritance
class SmsApi(models.AbstractModel):
_inherit = 'sms.api'
@api.model
def _send_sms(self, numbers, message):
account = self.env['iap.account'].get('nexmo.sms')
if not account:
return super(SmsApi, self)._send_sms(numbers, message)
@api.model
def _send_sms_batch(self, messages):
account = self.env['iap.account'].get('nexmo.sms')
if not account:
return super(SmsApi, self)._send_sms_batch(messages)
_name = 'customer.service.ticket'
_inherit = ['base.kanban.abstract', 'mail.thread']
2. When installing the Odoo SA sms module we added new features to the mail.thread model, making Odoo
records able to communicate via sms as well.
3. When we create a module sms_nexmo we change de default behavior of sending sms via Odoo SA IAP to send
it via Nexmo.
</data>
</odoo>
class CustomerServiceTicket(models.Model):
_name = 'customer.service.ticket'
_inherit = ['base.kanban.abstract', 'mail.thread']
code = fields.Char(
readonly=True,
default=lambda self: self.env['ir.sequence'].next_by_code('customer.service'))
)
name = fields.Char()
description = fields.Html(sanitize_style=True)
user_id = fields.Many2one('res.users')
partner_id = fields.Many2one('res.partner')
partner_email = fields.Char(related='partner_id.email')
</sheet>
<div class="oe_chatter">
<field name="message_follower_ids" widget="mail_followers" groups="base.group_user"/>
<field name="message_ids" widget="mail_thread"/>
</div>
class CustomerServiceTicket(models.Model):
_name = 'customer.service.ticket'
_inherit = ['base.kanban.abstract', 'mail.thread']
code = fields.Char(
readonly=True,
default=lambda self: self.env['ir.sequence'].next_by_code('customer.service'))
)
name = fields.Char()
description = fields.Html(sanitize_style=True)
user_id = fields.Many2one('res.users')
partner_id = fields.Many2one('res.partner')
partner_email = fields.Char(related='partner_id.email')
def send_number_by_sms(self):
for record in self:
record._message_sms(
'OCA days: Your ticket number is {}'.format(record.code),
partner_ids=record.partner_id.ids
)
Note:
To learn more:
• Check the OCA guidelines: https://github.com/OCA/odoo-community.org/blob/master/website/
Contribution/CONTRIBUTING.rst
6.9 Review
1. Setup your project from a project template;
2. Use OCA modules;
3. Create your own module;
4. Create an OCA module;
SEVEN
33