Create a Custom Model and Its View in Odoo
In this snippet, we will discuss how to create a custom model and its views in Odoo.
Models are used to represent the business data in Odoo. The models are defined in Python classes. Each model represents a table in the database. In order to create a model, we need to inherit our class from models.Model.
Views are used to define the user interface of the model. Views are defined in XML files. Each view represents a window or a page in the user interface.
Let’s take an example to understand how to create a custom model and its view in Odoo.

Model
from odoo import models, fields, api, _ class Payroll(models.Model): _name = 'custom.payroll' _description = 'Payroll' name = fields.Char(string="Number", readonly=True, copy=False, default='New') state = fields.Selection([ ('draft', 'Draft'), ('verify', 'Waiting'), ('done', 'Done'), ('cancel', 'Rejected'), ], string='Status', index=True, readonly=True, copy=False, default='draft') @api.model def create(self, vals): if vals.get('name', 'New') == 'New': vals['name'] = self.env['ir.sequence'].next_by_code('salary.number') or 'New' result = super(Payroll, self).create(vals) return result
Views (views.xml)
<odoo> <data> <record id="custom_payroll_tree" model="ir.ui.view"> <field name="name">custom.payroll.tree</field> <field name="model">custom.payroll</field> <field name="arch" type="xml"> <tree string="Payrolls"> <field name="name"/> </tree> </field> </record> <record id="custom_payroll_form_view" model="ir.ui.view"> <field name="name">custom.payroll.form</field> <field name="model">custom.payroll</field> <field name="arch" type="xml"> <form string="Payroll"> <sheet> <group> <field name="name"/> </group> </sheet> </form> </field> </record> <!-- Salaray Sequence --> <record id="sequence_salary_number" model="ir.sequence"> <field name="name">Salary Number</field> <field name="code">salary.number</field> <field name="active">TRUE</field> <field name="prefix">SLIP/%(year)s/</field> <field name="padding">5</field> <field name="number_next">1</field> <field name="number_increment">1</field> </record> <!-- we will add the Payroll module to Invoice menu items instead of creatign its own section --> <record id="action_payrolls" model="ir.actions.act_window"> <field name="name">Payrolls</field> <field name="res_model">custom.payroll</field> <field name="view_mode">tree,form</field> <field name="view_type">form</field> <field name="target">inline</field> <field name="view_id" ref="custom_payroll_tree"/> </record> <menuitem id="menu_payrolls_parent" name="Paryolls" parent="account.menu_finance" sequence="100"/> <menuitem id="menu_payrolls" name="Paryolls" action="action_payrolls" parent="menu_payrolls_parent" sequence="0"/> </data> </odoo>
Security (ir.model.access.csv)
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink custom_payroll_manager,custom.payroll.manager,model_custom_payroll,base.group_user,1,1,1,1
Menu
Manifest
# -*- coding: utf-8 -*- { 'name': "Custom Payroll", 'summary': """ Payroll """, 'description': """ Payroll """, 'author': "TLe Apps", 'website': "https://tleapps.com", 'category': 'Uncategorized', 'version': '1.0.0', # any module necessary for this one to work correctly 'depends': ['base','account'], # always loaded 'data': [ 'security/ir.model.access.csv', 'views/views.xml', ], 'images': ['static/description/icon.png'], }