In Odoo 19, implementing a custom POS component does not always require creating a brand-new OWL component from scratch. In many real-world scenarios, Odoo encourages developers to extend existing POS components using the patch utility. This method allows new behavior to be introduced while preserving the original structure, layout, and stability of the POS interface.
The POS control buttons area is itself managed by an OWL component called ControlButtons. By extending this component and adding new actions to it, we are effectively implementing a custom POS component that integrates seamlessly with the existing POS architecture. This is the same approach followed internally by Odoo for features such as discounts and pricing actions.
In this blog, we demonstrate how to implement a custom POS component in Odoo 19 by extending the existing ControlButtons component and attaching a new popup action.
Module Structure for the Custom POS Button
To begin, create a custom module to hold the POS front-end logic. Since this customization is purely client-side, the module only needs JavaScript and XML files.
A minimal and clean structure looks like this:
pos_custom_popup/
¦-- __manifest__.py
¦-- static/
¦ +-- src/
¦ +-- js/
¦ ¦ +-- control_button.js
¦ +-- xml/
¦ +-- control_button.xml
Implementing a Custom POS Component via Control Buttons
In Odoo 19, most POS actions on the product screen are handled through the ControlButtons component. Instead of creating a brand-new component from scratch, we can safely extend existing behavior using Odoo’s patch utility. This approach follows the same pattern used internally by Odoo itself.
JavaScript: Handling the Button Action
Create a JavaScript file at static/src/js/control_button.js.
/** @odoo-module **/
import { patch } from "@web/core/utils/patch";
import { ControlButtons } from
"@point_of_sale/app/screens/product_screen/control_buttons/control_buttons";
import { AlertDialog } from
"@web/core/confirmation_dialog/confirmation_dialog";
import { _t } from "@web/core/l10n/translation";
patch(ControlButtons.prototype, {
clickCustomAction() {
this.dialog.add(AlertDialog, {
title: _t("Custom Action"),
body: _t("This popup is triggered from a custom Control Button."),
});
},
});
Here’s what this code does:
- patch(ControlButtons.prototype, ...) extends the existing POS control buttons logic.
- clickCustomAction() defines a new method that will be called when our button is clicked.
- AlertDialog is a built-in Odoo dialog component, making it ideal for simple confirmations or informational popups.
- The pop-up is opened using this.dialog.add, which is the recommended way to display dialogs in Odoo 19.
Defining the Button UI Using XML
Next, we define how the button appears in the POS interface. This is done by extending the existing ControlButtons template and inserting our button into the expected container.
Create an XML file at static/src/xml/control_button.xml.
<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
<t t-inherit="point_of_sale.ControlButtons" t-inherit-mode="extension">
<xpath expr="//div[hasclass('control-buttons')]" position="inside">
<button class="button-action btn btn-secondary" t-on-click="clickCustomAction">
<i class="fa fa-cog"/> Custom Action
</button>
</xpath>
</t>
</templates>
This template:
- Inherits the existing POS ControlButtons layout.
- Adds a new button to the control-buttons container.
- Binds the click of the button directly to the JavaScript clickCustomAction method.
The button seamlessly integrates into the interface without the need for an extra CSS file since we reuse pre-existing POS button classes.
Loading the Assets in the Manifest
Finally, the JavaScript and XML files must be loaded into the POS asset bundle. This is done through the module manifest.
Update __manifest__.py as shown below:
{
"name": "POS Action Popup Button",
"version": "1.0",
"category": "Point of Sale",
"depends": ["point_of_sale"],
"assets": {
"point_of_sale._assets_pos": [
"pos_custom_popup/static/src/js/control_button.js",
"pos_custom_popup/static/src/xml/control_button.xml",
],
},
"installable": True,
"application": False,
"license": "LGPL-3",
}The new Custom Action button will show up in the POS control buttons section after the module has been installed or upgraded and the browser cache has been cleared. When you click it, an alert dialogue box appears, verifying that the integration functions as intended.
Once you understand how OWL and the registry system interact, custom POS components in Odoo 19 become surprisingly accessible. A pop-up is just a component that is activated by the pop-up service, and a button is just another component. Both the code and the flow remain tidy. You can customize the POS interface to fit any business need—extra actions, alerts, confirmations, or completely new screens—by using the above example.
Like any craft, the possibilities begin to expand once you understand the fundamentals. Despite their modest appearance, this tiny button and pop-up are the first steps towards more sophisticated POS features.
To read more about How to Create/Modify Component Control Buttons in POS Odoo 18, refer to our blog How to Create/Modify Component Control Buttons in POS Odoo 18.