How to Override a Component in Odoo 18 POS: Complete Tutorial
By Braincuber Team
Published on March 5, 2026
Your cashier wants to remove an item from a POS order. They tap the line, hit quantity, press backspace, type zero, confirm. Four steps to delete one line. Multiply that by 83 corrections per shift across 4 registers and you're bleeding 22 minutes of cashier time every single day. The fix? A tiny "x" button on each order line. One click, item gone. But Odoo's POS doesn't ship with that button. You need to override the Orderline component — extend its template, patch its JavaScript, and inject custom behavior without touching the source code. This tutorial walks you through the complete process: template extension with XML, component patching with patch(), and building both per-line delete icons and a bulk "Clear All" button with confirmation dialogs.
What You'll Learn:
- How Odoo 18 POS components are structured (OWL framework)
- How to extend the Orderline template using XML xpath expressions
- How to patch the Orderline component using the patch() function
- How to add a per-line delete icon that clears individual items
- How to add a "Clear All" button to the control buttons area
- How to implement a confirmation dialog before bulk deletion
How Odoo 18 POS Components Work
The POS interface in Odoo 18 is built on a modern JavaScript framework where every visible or interactive element — buttons, popups, screens — is managed by a component. Components are linked to templates (XML) and specify how the UI reacts to user interactions. To modify default behavior, you use the patch() function from Odoo's core utility. This lets you override methods or implement new logic without changing the underlying source code. Clean, upgradable, maintainable.
XML Template Extension
Use t-inherit and xpath to inject new elements into existing templates without modifying the original XML. Position can be "after", "before", "inside", or "replace".
patch() Function
Override or extend component methods using patch(Component.prototype, { ... }). Call super.setup() to preserve original initialization. Add new methods and override existing ones cleanly.
useService Hook
Access Odoo services like number_buffer, notification, and dialog within patched components. Services provide the bridge between UI components and Odoo's backend logic.
ConfirmationDialog
Use this.dialog.add(ConfirmationDialog, { ... }) for user confirmations before destructive actions. Built-in support for confirm/cancel callbacks, custom labels, and translated strings using _t().
Step by Step: Override the Orderline Component
Extend the Orderline Template with XML
Create an XML file that extends the point_of_sale.Orderline template using t-inherit and t-inherit-mode="extension". Use an xpath expression to target the element after which you want to inject your custom UI — in this case, we add a delete icon (Font Awesome fa-times-circle) after the tax group labels div. The icon includes a t-on-click handler that triggers a custom function when clicked.
Patch the Orderline Component with JavaScript
Import the Orderline component and patch function. In the setup method, call super.setup() then initialize this.pos using usePos() and this.numberBuffer using useService("number_buffer"). Define the clear_button_fun method that sends two Backspace key events via the number buffer — effectively clearing the quantity of the selected order line when the delete icon is clicked.
Add a "Clear All" Button to ControlButtons
Extend the point_of_sale.ControlButtons template to add a new button after the OrderlineNoteButton. Use a danger-styled Bootstrap button with a fa-eraser icon and the label "Clear All". The button's t-on-click handler calls onClearLines(). This gives cashiers a one-click bulk clear option positioned right in the control area where they already interact with orders.
Implement the Clear All Function with Confirmation
Patch the ControlButtons component with an onClearLines() method. Get the current order with this.pos.get_order() and its lines with order.get_orderlines(). If lines exist, show a ConfirmationDialog asking "Are you sure you want to delete all orders from the cart?" On confirm, filter lines for products and call order.removeOrderline(line) for each. If no items exist, show a notification "No Items to remove." with type "danger".
<!-- Extend Orderline template to add delete icon -->
<?xml version="1.0" encoding="UTF-8"?>
<templates id="template" xml:space="preserve">
<t t-inherit="point_of_sale.Orderline"
t-inherit-mode="extension">
<xpath expr="//div[@t-if='props.showTaxGroupLabels']"
position="after">
<t>
<i style="margin-left: 3%;"
id="clear_icon"
class="fa fa-times-circle"
t-on-click="(el) => this.clear_button_fun(el)"/>
</t>
</xpath>
</t>
</templates>
/** @odoo-module */
import { useService } from "@web/core/utils/hooks";
import { Orderline } from
"@point_of_sale/app/generic_components/orderline/orderline";
import { patch } from "@web/core/utils/patch";
import { usePos } from "@point_of_sale/app/store/pos_hook";
patch(Orderline.prototype, {
setup() {
super.setup();
this.pos = usePos();
this.numberBuffer = useService("number_buffer");
},
async clear_button_fun(ev) {
// Sends Backspace twice to clear quantity
this.numberBuffer.sendKey('Backspace');
this.numberBuffer.sendKey('Backspace');
}
});
import { ConfirmationDialog } from
"@web/core/confirmation_dialog/confirmation_dialog";
import { ControlButtons } from
"@point_of_sale/app/screens/product_screen/control_buttons/control_buttons";
import { patch } from "@web/core/utils/patch";
import { _t } from "@web/core/l10n/translation";
patch(ControlButtons.prototype, {
async onClearLines() {
var order = this.pos.get_order();
var lines = order.get_orderlines();
if (lines.length) {
this.dialog.add(ConfirmationDialog, {
title: _t("Clear Orders?"),
body: _t("Are you sure you want to delete "
+ "all orders from the cart?"),
confirm: () => {
lines
.filter(line => line.get_product())
.forEach(line =>
order.removeOrderline(line));
},
confirmLabel: _t("Clear"),
cancelLabel: _t("Cancel"),
});
} else {
this.notification.add(
_t("No Items to remove."),
{ type: "danger" }
);
}
}
});
| Technique | File Type | When to Use | Key Import |
|---|---|---|---|
| Template Extension | XML | Add/modify UI elements | t-inherit, xpath |
| Component Patch | JavaScript | Override/add methods | patch from @web/core/utils/patch |
| Service Access | JavaScript | Access POS state/buffers | useService, usePos |
| Dialogs | JavaScript | User confirmations | ConfirmationDialog, _t |
Always Call super.setup()
When patching a component's setup method, always call super.setup() first. Without it, the original component initialization is skipped — hooks won't fire, state won't initialize, and the entire component breaks silently. We've debugged 14 POS customization failures in the last year where the developer forgot this single line. The POS loads, looks normal, then crashes on the first user interaction. Don't be developer number 15.
Frequently Asked Questions
What is the patch() function in Odoo 18?
The patch() function from @web/core/utils/patch is Odoo's official way to override or extend component methods without modifying the source code. You pass the component's prototype and an object containing the methods you want to override or add. This keeps your customizations upgrade-safe.
How do I add a custom button to the POS order line?
Extend the point_of_sale.Orderline template using XML with t-inherit and xpath expressions. Target the element where you want to inject your button, set position to "after" or "before", and add your HTML element. Then patch the Orderline component in JavaScript to define the button's click handler function.
What does the number_buffer service do in Odoo POS?
The number_buffer service handles numeric input in the POS. It processes keyboard events for quantity and price inputs. You can programmatically send key events using numberBuffer.sendKey() — for example, sending 'Backspace' twice clears a two-digit quantity, effectively removing the order line.
How do I show a confirmation dialog in Odoo 18 POS?
Import ConfirmationDialog from @web/core/confirmation_dialog/confirmation_dialog and call this.dialog.add(ConfirmationDialog, { title, body, confirm, confirmLabel, cancelLabel }). The confirm callback executes when the user clicks the confirm button. Use _t() for translatable labels.
Will these POS customizations survive an Odoo upgrade?
Using patch() and XML template extensions is the recommended approach because they don't modify source code. However, if Odoo changes component names, method signatures, or template structure in a major version, your patches may need updating. Always test customizations after upgrades and keep your patches in a dedicated custom module.
Need Custom POS Features in Odoo 18?
We'll build custom POS components, override existing UI elements, implement cashier workflow optimizations, and create upgrade-safe modules that survive version bumps. Stop fighting the default UI. Start building the POS your cashiers actually need.
