To create pagination in portal user documents in Odoo 16, you can leverage the powerful features of the Odoo website module. Pagination is a technique that divides content into manageable pages, allowing users to navigate through a large collection of documents more efficiently.
With Odoo 16, you can implement pagination for portal user documents by customizing the website templates and views. Integrating pagination into your Odoo website can enhance the user experience and simplify document navigation for portal users.
With the help of pagination, users can easily browse through a list of documents, view a specific number of documents per page, and quickly navigate between pages. This ensures a smoother and more organized document management experience within the Odoo portal, enabling users to locate and access the desired documents easily.
Let's discuss how to create pagination in Odoo 16,
For that, we need a controller and the templates to which we want to add the pagination.
Controllers:
When a user wishes to render certain information into a site, they can use a class called a controller. 'Controllers' in Odoo are used to organize the frontend modules. The backend modules are integrated with these frontend components. For instance, a user cannot provide the specifics of a business request to the site using the functionality of "Models" in Odoo. However, clients can surely obtain the backend business request details by using the Controllers in Odoo.
Let’s define Python for this, name the file as ‘module_name.py’
module_name.py
class PortalAttendance(portal.CustomerPortal):
@http.route(['/attendance/list','/attendance/list/page/<int:page>'],
type='http', website=True)
def attendance_pagination_view(self, page=1, **kwargs):
uid = request.session.uid
user_id = request.env['res.users'].browse(uid)
employee_id = request.env['hr.employee'].search(
[('user_id', '=', user_id.id)])
attendance_obj = request.env['hr.attendance'].search(
[('is_portal', '=', True), ('employee_id', '=', employee_id.id)])
total_attendance = attendance_obj.search_count(
[('is_portal', '=', True),
('employee_id', '=',
employee_id.id)]
In the above code, I am fetching the attendance of the users to view on the portal.
Let's now pass the parameters for constructing pagination.
page_detail = pager(url='/attendance/list',
total=total_attendance,
page=page,
step=10,
)
attendance_domain = [('is_portal', '=', True),
('employee_id', '=', employee_id.id)]
attendance = attendance_obj.search(
attendance_domain,
limit=10,
offset=page_detail['offset'])
vals = {
'attendance': attendance,
'page_name': 'attendance',
'pager': page_detail,
}
return request.render(
"web_portal_attendance.portal_list_attendance_order", vals)
Let’s explain one by one,
@http.route(['/attendance/list',
'/attendance/list/page/<int:page>'], type='http', website=True)
This is a decorator for defining a route for the portal view.
'/attendance/list' is the main route for the attendance list view.
'/attendance/list/page/<int:page>' is a route pattern that includes a
variable <int:page>, which captures the page number from the URL.
def attendance_search_sort_view(self, page=1, **kwargs):
This is the main method that will be executed when the route is accessed.
self refers to the instance of the class.
page=1 is a default parameter for the page number. If no page number is provided in the URL, it defaults to page 1.
uid = request. session.uid This retrieves the current user's ID from the session.
user_id = request.env['res.users'].browse(uid) This fetches the user's record using the res.users model and the retrieved user ID.
employee_id = request.env['hr.employee'].search([('user_id', '=', user_id.id)])
This searches for an hr. employee record where the user_id field matches the current user's ID.
attendance_obj = request.env['hr.attendance'].search([('is_portal',
'=', True), ('employee_id', '=', employee_id.id)])
This searches for attendance records using the hr.attendance model, filtered by is_portal being True and matching the current user's employee ID.
total_attendance = attendance_obj.search_count([('is_portal', '=',
True), ('employee_id', '=', employee_id.id)])
This calculates the total count of attendance records that meet the same filters as attendance_obj.
page_detail = pager(url='/attendance/list', total=total_attendance,
page=page, step=10)
This generates pagination details using the pager function.
url specifies the base URL for pagination links.
total is the total number of records.
page is the current page number.
step is the number of records displayed per page.
attendance = attendance_obj.search(attendance_domain, limit=10,
offset=page_detail['offset'])
This retrieves attendance records based on the attendance_domain filter.
limit=10 limits the number of records per page to 10.
offset=page_detail['offset'] specifies the starting record based on the current page.
vals = {
'attendance': attendance,
'page_name': 'attendance',
'pager': page_detail,
}
This dictionary holds various values that will be passed to the template.
return
request.render("web_portal_attendance.portal_list_attendance_order",
vals)
his renders a specific template, passing the vals dictionary to it.
Overall, this code defines a route for viewing attendance records with pagination support. It calculates the appropriate records for the current page and generates pagination links. The template portal_list_attendance_order is then rendered with the necessary data.
The next step is creating a menu and list view for the attendance.
We need to define an XML file for that. That file contains the following code.
<template id="portal_my_home_menu_attendance"
name="Portal layout : Attendance menu entries"
inherit_id="portal.portal_breadcrumbs" priority="30">
<xpath expr="//ol[hasclass('o_portal_submenu')]" position="inside">
<li t-if="page_name == 'attendance'"
t-attf-class="breadcrumb-item #{'active ' if not attendance else ''}">
<a t-if="attendance"
t-attf-href="/attendance/list?{{ keep_query() }}">
Attendance
</a>
<t t-else="">Attendance</t>
</li>
</xpath>
</template>
It will create a new menu, as we can see in the image below.
To get the count, we need to define another function and templates.
class PortalAttendance(portal.CustomerPortal):
"""To get the values of portal attendance"""
def _prepare_home_portal_values(self, counters):
"""To get the count of the attendance in portal"""
values = super(PortalAttendance, self)._prepare_home_portal_values(
counters)
uid = request.session.uid
user_id = request.env['res.users'].browse(uid)
employee_id = request.env['hr.employee'].search(
[('user_id', '=', user_id.id)])
attendance_count = request.env['hr.attendance'].search_count(
[('is_portal', '=', True), ('employee_id', '=', employee_id.id)])
values.update({
'attendance_count': attendance_count
})
return values
Then pass the attendance_count to the template.
<template id="portal_view" name="Attendance" customize_show="True"
inherit_id="portal.portal_my_home" priority="90">
<xpath expr="//div[hasclass('o_portal_docs')]" position="inside">
<a href="/attendance/list"
class="list-group-item list-group-item-action d-flex align-items-center justify-content-between"
title="Attendance">
Attendance
<span class="badge text-bg-secondary rounded-pill"
data-oe-model="ir.ui.view" data-oe-id="405"
data-oe-field="arch"
data-oe-xpath="/t[1]/a[1]/t[3]/span[1]"
data-placeholder_count="attendance_count">
</span>
</a>
</xpath>
</template>
Then we need to design the list view. For that, we need another template.
<template id="portal_list_attendance_order" name="Attendance View">
<t t-call="portal.portal_layout">
<t t-set="breadcrumbs_searchbar" t-value="True"/>
<t t-call="portal.portal_searchbar">
<t t-set="title">Attendance
</t>
</t>
<t t-if="not attendance">
<p>There are currently no Attendance information for your
account.
</p>
</t>
<t t-if="attendance" t-call="portal.portal_table">
<thead>
<tr class="active">
<th>
<span class='d-none d-md-inline'>Employee
</span>
</th>
<th>
<span class='d-none d-md-inline'>Check In</span>
</th>
<th>
<span class='d-none d-md-inline'>Check Out</span>
</th>
<th>
<span class='d-none d-md-inline'>Work Hours</span>
</th>
</tr>
</thead>
<t t-foreach="attendance" t-as="pa">
<tr>
<td>
<t t-esc="pa.employee_id.name"/>
</td>
<td>
<span t-field="pa.check_in"
t-options="{'widget': 'date'}"/>&nbsp;
<span class='d-none d-md-inline'
t-field="pa.check_in"
t-options="{'time_only': True}"/>
</td>
<td>
<span t-field="pa.check_out"
t-options="{'widget': 'date'}"/>&nbsp;
<span class='d-none d-md-inline'
t-field="pa.check_out"
t-options="{'time_only': True}"/>
</td>
<td>
<t t-set="worked_hours" t-value="pa.worked_hours"/>
<t t-set="hours" t-value="int(worked_hours)"/>
<t t-set="minutes"
t-value="int(divmod(worked_hours, 1)[1] * 60)"/>
<span t-esc="'{:02d}:{:02d}'.format(hours, minutes)"/>
</td>
</tr>
</t>
</t>
</t>
</template>
Based on the code, we will get the view as below.
Also, here we can see the pagination of the list view, with the count of the page and the Previous and Next buttons. As per our code, we define the step=10 and the limit=10 so that the first page will show the ten records and the remaining records will be on another page.
In conclusion, integrating pagination in the user documents section of the Odoo 16 portal improves user experience and makes document navigation simpler. Users can efficiently browse through a big collection of papers by segmenting content into digestible pages.