Odoo 18 POS: Saving Custom Order Data to Backend
By Braincuber Team
Published on February 11, 2026
Standard Odoo POS orders capture the essentials: products, payments, and customer details. But real-world retail often requires more context. What if you need to capture a "Gift Message," "Delivery Instruction," or "referral Source" right at the register?
In this tutorial, we will extend the Odoo 18 Point of Sale to capture a custom "Gift Wrap Note" for LuxeAura, a high-end boutique. We'll cover the full data journey: creating the backend field, building the POS UI button, and patching the Javascript model to ensure the data persists to the server.
The Data Flow Challenge
- POS runs in the browser (Javascript).
- Orders are stored in the database (Python).
- To bridge them, you must add the field to
export_as_JSONin Owl to send it, and the Python model to receive it.
Step 1: The Backend Storage
First, we create the destination for our data in the pos.order model.
from odoo import fields, models
class PosOrder(models.Model):
_inherit = 'pos.order'
gift_wrap_note = fields.Char(
string='Gift Wrap Instructions',
help='Special packing instructions from the customer.'
)
Don't forget to add this field to the XML Form view so back-office users can see it!
Step 2: Patching the Frontend Model
This is the critical step often missed. We must tell the POS Javascript model to handle this new field. We patch PosOrder to include gift_wrap_note in the JSON payload sent to the server.
/** @odoo-module */
import { patch } from "@web/core/utils/patch";
import { PosOrder } from "@point_of_sale/app/models/pos_order";
patch(PosOrder.prototype, {
setup(vals) {
super.setup(vals);
// Initialize the field
this.gift_wrap_note = vals.gift_wrap_note || "";
},
export_as_JSON() {
const json = super.export_as_JSON(...arguments);
// Add our custom field to the JSON payload
json.gift_wrap_note = this.gift_wrap_note;
return json;
},
init_from_JSON(json) {
super.init_from_JSON(...arguments);
// Recover data if POS reloads
this.gift_wrap_note = json.gift_wrap_note;
}
});
Step 3: The UI Button
Finally, we add a button to the Payment Screen. When clicked, it opens a text input popup.
And the corresponding logic in the Payment Screen component:
/** @odoo-module */
import { PaymentScreen } from "@point_of_sale/app/screens/payment_screen/payment_screen";
import { patch } from "@web/core/utils/patch";
import { TextInputPopup } from "@point_of_sale/app/utils/input_popups/text_input_popup";
import { _t } from "@web/core/l10n/translation";
patch(PaymentScreen.prototype, {
async onGiftWrapClick() {
const order = this.pos.get_order();
const { confirmed, payload: note } = await this.popup.add(TextInputPopup, {
title: _t("Gift Wrap Instructions"),
startingValue: order.gift_wrap_note,
rows: 3,
});
if (confirmed) {
order.gift_wrap_note = note;
}
}
});
Conclusion
You now have a complete pipeline for capturing custom checkout data. This pattern can be reused for Referral Codes, Delivery Slots, or any other metadata your business needs at the point of sale.
Custom Odoo POS Workflows?
Need to integrate complex third-party APIs or custom hardware into your POS? Our Odoo experts build seamless retail experiences.
