Enable Dark Mode!
By: Sreelakshmi PM

How to Create a Custom Report in Odoo 15 Portal View

Technical Odoo 15

A portal user is a user who typically belongs outside of the company, such as a customer or client. We can grant those users access to the Odoo portal so they can access information pertinent to them.

This blog explains how to create a report in portal view.

Now let's see how to create a detailed Maintenance report in the maintenance module. The report includes the details like a subject, team, schedule date, serial No, kanban state, request date, employee, technician, category, stage, and the company of the maintenance request on the given period. 

Before that, we can create a button in the portal view for redirects to the maintenance report.

For that, we can create a view file(maintenance.xml) for the button.

Also defined the path of the report like (href=”/maintenance/report”)

XML File: maintenance_button.xml


<template id="maintenance_request" name="Maintenance Request" inherit_id="portal.portal_my_home">

<xpath expr="//div[hasclass('o_portal_docs')]" position="before">

<a id="maintenance_report" role="button"

href="/maintenance/report" style="width: 539px;"

class="btn btn-primary btn-block">

<i class="fa fa-ticket"/>

Maintenance Report





After executing the code we can view the button in the portal view.


Now, we can create a model for the report. In the model, we have to define the fields that are included in the report.

Python File: maintenance_report.py

from odoo import models, fields, tools, api, _

class MaintenanceReport(models.Model):

_name = 'portal.maintenance.report'

_auto = False

id = fields.Integer("ID")

subject = fields.Char("Subject")

maintenance_id = fields.Char("Maintenance ID")

serial_no = fields.Char("Serial No:")

stage = fields.Char("Stage")

company = fields.Char("Company")

schedule_date = fields.Char("Schedule Date")

kanban_state = fields.Char("Kanban State")

request_date = fields.Char("Request Date")

created_by = fields.Char("Created By")

technician = fields.Char("Technician")

category = fields.Char("Category")

team = fields.Char("Team")

department = fields.Char("Department")

For the Maintenance report in the created model, we have to create a SQL query defined in the init function.

def init(self):

tools.drop_view_if_exists(self.env.cr, 'portal_maintenance_report')


CREATE OR REPLACE VIEW portal_maintenance_report AS (

select row_number() OVER() AS id,res_partner.name as technician,hr_department.name as department,

l.id as maintenance_id,l.name as subject,l.maintenance_team_id,l.x_studio_serial_no as serial_no,

l.stage_id,maintenance_stage.name as stage,l.company_id,res_company.name as company,

l.schedule_date as schedule_date,l.kanban_state as kanban_state,l.request_date as request_date,


maintenance_equipment_category.name as category,

hr_employee.name as created_by,maintenance_team.name as team from maintenance_request as l

left join hr_employee on l.employee_id = hr_employee.id

left join maintenance_team on l.maintenance_team_id = maintenance_team.id

left join maintenance_equipment_category on l.category_id = maintenance_equipment_category.id

left join maintenance_stage on l.stage_id = maintenance_stage.id

left join res_company on l.company_id = res_company.id left join res_users on l.user_id = res_users.id

left join res_partner on res_users.partner_id = res_partner.id

left join maintenance_equipment on l.equipment_id = maintenance_equipment.id

left join hr_department on maintenance_equipment.department_id = hr_department.id)''')

After creating the model we can create the view file of the Maintenance report. First of all, I have created a template (maintenance_report_page).

In the maintenance_report_page template, we can define the table header and call the fields that were created using the SQL query.

XML File: maintenance_report.xml

<template id="maintenance_report_page" name="Maintenance Report">

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

<t t-set="breadcrumbs_searchbar" t-value="True"/>


<div id="wrap_maintenance">



<h1 style="margin: 20px;">

<b>Maintenance Request Report</b>




<table class="table table-sm table-reports" style="border:1px solid black;">



<th colspan="6" class="text-left">Subject</th>

<th colspan="6" class="text-center">Team</th>

<th colspan="6" class="text-center">Scheduled Date</th>

<th colspan="6" class="text-left">Serial No:</th>

<th colspan="6" class="text-center">Kanban State</th>

<th colspan="6" class="text-center">Request Date</th>

<th colspan="6" class="text-center">Employee</th>

<th colspan="6" class="text-center">Technician</th>

<th colspan="6" class="text-center">Category</th>

<th colspan="6" class="text-center">Stage</th>

<th colspan="6" class="text-center">Company</th>



<tbody class="text-left">


<tr class="maintenance_table" t-foreach="datas" t-as="main">

<input id="main_id" type="hidden" t-att-value="main.maintenance_id"/>

<td colspan="6">

<span id="main_subject" t-esc="main['subject']"/>


<td colspan="6">

<span id="main_team" t-esc="main['team']"/>


<td colspan="6">

<span id="main_schedule_date" t-esc="main['schedule_date']"/>


<td colspan="6">

<span id="main_serial_no" t-esc="main['serial_no']"/>


<td colspan="6">

<span id="main_kanban_state" t-esc="main['kanban_state']"/>


<td colspan="6">

<span id="main_request_date" t-esc="main['request_date']"/>


<td colspan="6">

<span id="main_created_by" t-esc="main['created_by']"/>


<td colspan="6">

<span id="main_technician" t-esc="main['technician']"/>


<td colspan="6">

<span id="main_category" t-esc="main['category']"/>


<td colspan="6">

<span id="main_stage" t-esc="main['stage']"/>


<td colspan="6">

<span id="main_company" t-esc="main['company']"/>










In the maintenance_button XML file, the path ‘/maintenance/report’ is directed to controllers. So next, we have to create a python file for the controller.

Controller: main.py

from odoo import http
from odoo.http import request
class MaintenanceRequest(http.Controller):
@http.route(['/maintenance/report'], type="http", auth="public", website=True)
def maintenance_report(self, **kw):
report = request.env['portal.maintenance.report'].sudo().search([])
values = {
'datas': report,}
return request.render("portal_users.maintenance_report_page", values)

After that, when clicking the Maintenance button, the controller redirects the template “maintenance_report_page”

The output will look like the one below.


Additionally, we can add additional filters to this report and handle the associated data by altering the controller function

If you need any assistance in odoo, we are online, please chat with us.


Leave a comment




Cybrosys Technologies Pvt. Ltd.
Neospace, Kinfra Techno Park
Kakkancherry, Calicut
Kerala, India - 673635



Cybrosys Technologies Pvt. Ltd.
1st Floor, Thapasya Building,
Infopark, Kakkanad,
Kochi, India - 682030.



Cybrosys Techno Solutions
The Estate, 8th Floor,
Dickenson Road,
Bangalore, India - 560042

Send Us A Message