top of page
  • Writer's pictureTek Siong, Hock

Odoo Website Portal Development - Part 1

This Odoo technical development blog is my way to contribute back to the Odoo Community, for all the selfless and great sharing by the community members.

Odoo technical development has tonnes of excellent material for standard custom module development (Form, Tree, Kanban, etc), but not so much information on the website portal development. Odoo portal is implemented with MVC (Model-View-Controller) concept.

There are many standard Odoo modules that are using portal, such as in sale, purchase, invoice, project, etc, which the customer/vendor can log on to view their sale quotation, order, purchase order, invoice, etc.

A Simple Portal Website Page to Submit Data

1. Create a new module

a) In the new module, add the dependency to the “website” module.

b) Create new folder for “controllers”, “views” and “data”.

2. Create a portal menu

a) Create a XML file ‘menu.xml’ in the data folder. Declare it in the manifest file.

b) Add the following to display the Menu and the URL to render the page.

<?xml version="1.0" encoding="utf-8"?> <odoo> <data noupdate="1"> <record id="portal_menu" model=""> <field name="name">Payroll</field> <field name="url">/payroll_generate</field> <field name="parent_id" ref="website.main_menu"/> <field name="sequence" type="int">70</field> </record> </data> </odoo>

c) Create a in the controllers folder, with class as follow. Don’t forget to add the file name to the

d) Create a new method and add @http.route that URL is corresponding the URL in the step b), which will be trigger when user click on “Portal” page. Then will display the template, based on the matched “template id”.

from odoo.addons.portal.controllers.portal import CustomerPortal, pager as portal_pager
import odoo.http as http

class PayrollPortal(CustomerPortal):

    #1 - will be trigger by the menu.xml url to render the page.
    @http.route("/payroll_generate", type="http", auth="public", website=True)
    def payroll_webform(self, **kw):

        return http.request.render(

    #2 - will be trigger when user submit the payroll data
    @http.route(['/payroll/create'], type='http', auth="public", website=True)
    def payroll(self, **post):
        #return http.request.render('payroll_web.payroll_data')
        print('>>>>>>>>>>>>>>>>>>>>> Payroll >>>>>>>>>>>>>>>')
        if post:
            salary = post["salary"]

3. Create a Portal Page

a) Create a new xml in the views folder. Declare it in the manifest file. See the bottom of the page.

b) template id must be same as step 2 d).

c) Create a <form> in the template, with action url and post method. The url must be corresponding to the @http.route in the method payroll.

d) Add all the input field, selection fields, etc.

f) Add a submit button at the bottom of the form. Type must be “submit”.

4. Create a Portal Page

a) in the, create a new method to process the data. In the method, add the @http.route, which has the same URL as declared in the step 3 c).

b) The form data is passed through the **post object.

<?xml version="1.0" encoding="utf-8"?>


<template id="payroll_generate" name="Payroll">

<t t-call="website.layout">

<div class="wrap">

<div class="oe_structure">

<div class="container">


<div class="container">

<div class="row">

<div class="col-md-12">

<h1 class="text-center">Payroll Calculator</h1>





<form action="/payroll/create" method="POST" class="form-horizontal mt32 container"


<div class="row o_portal_details">

<div class="col-lg-12">

<div class='row'>

<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/>

<table width="75%" border="0" cellpadding="0" cellspacing="0"

style="float: left" class="table table-noline">



<td bgcolor="#369F81" colspan="2">

<label class="control-label">

<strong style="font-size:15px;color:#fff">Salary and Bonus






<td colspan="2" class="form-group">

<button type="submit" class="btn btn-primary btn-lg">Submit</button>















917 views1 comment

Recent Posts

See All

1 comentario

Felipe Ferreira
Felipe Ferreira
31 mar 2023

can you share the code?

Me gusta
bottom of page