Enable Dark Mode!
how-to-add-search-in-customer-portal-in-odoo-19.jpg
By: Henna Mehjabin

How to Add Search in Customer Portal in Odoo 19

Technical Odoo 19 Odoo Enterprises Odoo Community

Delivering a streamlined customer experience is a key driver in Odoo 19, and the customer portal remains a central touchpoint for users accessing their records online. While Odoo provides a clean default portal, many business workflows require searchable listings so customers can quickly locate the information they need. In this article, we walk through how to extend the Odoo 19 portal with a fully server-side search feature. Using a simple controller, domain filters, and a QWeb template, you can enable customers to search and filter their records directly from the portal interface, keeping the solution lightweight, secure, and easy to maintain.

To operationalize employee visibility within the portal, we extend the CustomerPortal class and introduce a dedicated endpoint /my/employees. The controller orchestrates search parameters, builds the dynamic domain, and retrieves employee records via a secure sudo() envelope. It also provisions the search bar metadata consumed by the front-end for category-based filtering. This design ensures a modular, low-overhead extension that adheres to the platform’s routing conventions and UI extensibility guidelines.

We extend the existing portal breadcrumb stack to maintain UI continuity across the end-to-end navigation flow. By appending a contextual “Employees” trail marker when the active page is rendered, we reinforce discoverability and ensure users maintain orientation throughout the customer journey. This aligns with standard UX governance and avoids regressions in navigation patterns.

# -*- coding: utf-8 -*-
from odoo import http
from odoo.http import request
from odoo.addons.portal.controllers.portal import CustomerPortal
class PortalEmployees(CustomerPortal):

  @http.route(['/my/employees'], type='http', auth='user', website=True)
  def portal_my_employees(self, **kwargs):
      """Display the Employees portal page with optional search filters. """
      search = kwargs.get('search', '').strip()
      search_in = kwargs.get('search_in', 'all')
      searchbar_inputs = {
          'all': {
              'input': 'all',
              'label': 'All',
          },
          'name': {
              'input': 'name',
              'label': 'Name',
          },
          'job_title': {
              'input': 'job_title',
              'label': 'Job Title',
          },
      }
      domain = []
      if search:
          if search_in == 'name':
              domain = [('name', 'ilike', search)]
          elif search_in == 'job_title':
              domain = [('job_title', 'ilike', search)]
          elif search_in == 'all':
              domain = ['|', ('name', 'ilike', search), ('job_title', 'ilike', search)]

      employees = request.env['hr.employee'].sudo().search(domain)

      values = self._prepare_portal_layout_values()
      values.update({
          'employees': employees,
          'page_name': 'employees',
          'search': search,
          'search_in': search_in,
          'searchbar_inputs': searchbar_inputs,
      })
      return request.render("portal_search_blog.portal_my_employees_page", values)

The primary portal page integrates seamlessly with Odoo’s native portal_layout and portal_searchbar assets. We pass in search parameters sourced from the controller and render a responsive directory table showcasing key employee attributes such as name, work email, phone, and job title. The template includes exception-handling for empty datasets to uphold UI polish even when search results yield no matches. This unlocks frictionless data consumption for portal users while preserving Odoo’s design language.

<?xml version="1.0" encoding="UTF-8"?>
<odoo>
   <template id="portal_my_home_custom_customers"
             inherit_id="portal.portal_my_home"
             priority="5000">
       <xpath expr="//div[@id='portal_client_category']" position="inside">
           <div class="o_portal_index_card col-md-6 order-2">
               <a href="/my/employees" title="Employees"
                  class="d-flex gap-2 gap-md-3 py-3 pe-2 px-md-3 h-100 rounded text-decoration-none bg-100">
                   <div class="o_portal_icon d-block align-self-start">
                       <img src="/base/static/src/img/user-solid.svg"/>
                   </div>
                   <div>
                       <div class="mt-0 mb-1 fs-5 fw-normal lh-1 d-flex gap-2">
                           <span>Employees</span>
                       </div>
                       <div class="opacity-75">
                           View your employee directory
                       </div>
                   </div>
               </a>
           </div>
       </xpath>
   </template>
</odoo>

This template extends the portal UI to introduce a full employee directory page. It injects an “Employees” breadcrumb for contextual navigation, activates the standard portal search bar, and renders a responsive table listing employee details such as name, email, phone, and job title. When no records match the search criteria, it displays a clean fallback message to maintain a polished user experience.

<?xml version="1.0" encoding="UTF-8"?>
<odoo>
   <!-- Breadcrumb -->
   <template id="portal_employees_breadcrumb" name="Employees Breadcrumb"
             inherit_id="portal.portal_breadcrumbs" priority="30">
       <xpath expr="//ol[hasclass('o_portal_submenu')]" position="inside">
           <li t-if="page_name == 'employees'"
               class="breadcrumb-item active">
               <span>Employees</span>
           </li>
       </xpath>
   </template>
   <!-- Main Page -->
   <template id="portal_my_employees_page" name="Employees Portal Page">
       <t t-call="portal.portal_layout">
           <t t-set="breadcrumbs_searchbar" t-value="True"/>
           <t t-call="portal.portal_searchbar">
               <t t-set="title">Employees</t>
               <t t-set="searchbar_inputs" t-value="searchbar_inputs"/>
               <t t-set="search_in" t-value="search_in"/>
               <t t-set="search" t-value="search"/>
           </t>
           <div class="o_portal_wrap">
               <div class="row">
                   <div class="col-12">
                       <table class="table table-hover o_portal_my_doc_table">
                           <thead>
                               <tr class="active">
                                   <th>Name</th>
                                   <th>Work Email</th>
                                   <th>Work Phone</th>
                                   <th>Job Title</th>
                               </tr>
                           </thead>
                           <tbody>
                               <t t-foreach="employees" t-as="emp">
                                   <tr>
                                       <td><t t-esc="emp.name"/></td>
                                       <td><t t-esc="emp.work_email or '-'"/></td>
                                       <td><t t-esc="emp.work_phone or '-'"/></td>
                                       <td><t t-esc="emp.job_title or '-'"/></td>
                                   </tr>
                               </t>
                               <t t-if="not employees">
                                   <tr>
                                       <td colspan="4" class="text-center text-muted">
                                           No employees found.
                                       </td>
                                   </tr>
                               </t>
                           </tbody>
                       </table>
                   </div>
               </div>
           </div>
       </t>
   </template>
</odoo>

By integrating a lightweight server-side search layer and extending the portal with targeted UI enhancements, Odoo 19 enables a more intuitive and self-service-driven customer experience. The approach demonstrated here—anchored in a custom controller, dynamic domain filtering, and modular QWeb templates—delivers a scalable pattern for any searchable portal dataset, not just employee records. Organizations can now surface relevant information faster, reduce navigation friction, and maintain a clean, maintainable codebase that stays aligned with Odoo’s native framework. This foundation can be further expanded to support additional models, filters, and business-specific workflows, ensuring long-term extensibility with minimal operational overhead.

To read more about How to Add Search in Customer Portal in Odoo 18, refer to our blog How to Add Search in Customer Portal in Odoo 18.


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



0
Comments



Leave a comment



whatsapp_icon
location

Calicut

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

location

Kochi

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

location

Bangalore

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

Send Us A Message