Hello Odoo Community,
I'm developing a custom module for the Odoo Point of Sale (POS) and have encountered a persistent issue with data loading for a custom Many2Many field, preventing me from displaying the correct information in a custom popup.
My Goal: I'm implementing a new payment method type for "Tickets Restaurant" (or similar vouchers). Each such payment method needs to be associated with specific pos.ticket.type records, which define properties like a name and discount_rate. When a user selects one of these ticket payment methods in the POS, I intend to open a custom popup (ManageTicketRestaurantPopup). This popup should then display a list of the actual names and discount rates of the pos.ticket.type records that are linked to the selected payment method.
Any guidance or common pitfalls I might be overlooking would be greatly appreciated! Thank you!
My Current Setup (Odoo 18):
Here's a simplified view of my relevant models and JavaScript:
-
pos.ticket.type Model (Python):
This model defines the structure for my ticket types.
Python
# your_module_name/models/pos_ticket_type.py from odoo import models, fields class PosTicketType(models.Model): _name = 'pos.ticket.type' _description = 'Type de Ticket' name = fields.Char(required=True) discount_rate = fields.Float(string="Taux de retenue (%)", required=True)
-
pos.payment.method Model (Python - Inherited):
I've added a boolean flag and a Many2Many field to link to the ticket types. I've also extended _load_pos_data_fields to ensure these fields are loaded for the payment method itself.
Python
# your_module_name/models/pos_payment_method.py from odoo import models, fields, api class PosPaymentMethod(models.Model): _inherit = 'pos.payment.method' is_ticket_payment = fields.Boolean(string="Mode de paiement par ticket ?", default=False) allowed_ticket_type_ids = fields.Many2many('pos.ticket.type', string="Types de tickets autorisés") @api.model def _load_pos_data_fields(self, config_id): res = super()._load_pos_data_fields(config_id) res += ['is_ticket_payment', 'allowed_ticket_type_ids'] return res
-
pos.config Model (Python - Loading related data):
To make the full pos.ticket.type records available in the frontend, I've extended _get_pos_ui_res_config.
Python
# your_module_name/models/pos_config.py from odoo import models, fields, api class PosConfig(models.Model): _inherit = 'pos.config' @api.model def _get_pos_ui_res_config(self, config): result = super()._get_pos_ui_res_config(config) ticket_types_data = self.env['pos.ticket.type'].search_read([], ['id', 'name', 'discount_rate']) result['pos_ticket_types'] = ticket_types_data return result
- payment_screen_patch.js (JavaScript Frontend):
This is where I try to access the data and pass it to my custom popup.
JavaScript
// your_module_name/static/src/js/payment_screen_patch.js /** @odoo-module **/ import { _t } from "@web/core/l10n/translation"; import { patch } from "@web/core/utils/patch"; import { PaymentScreen } from "@point_of_sale/app/screens/payment_screen/payment_screen"; import { ManageTicketRestaurantPopup } from "@pos_ticket_voucher/js/ticket_restaurant_popup"; // Ensure this path is correct import { useService } from "@web/core/utils/hooks"; patch(PaymentScreen.prototype, { setup() { super.setup(...arguments); this.dialog = useService("dialog"); this.pos = useService("pos"); // Access the pos service }, async addNewPaymentLine(payment_method) { await super.addNewPaymentLine(payment_method); if (payment_method.is_ticket_payment) { // This line is where the problem occurs for me. // I expect full voucher types, but I get just IDs or an empty array when trying to map. const allowedTicketTypeIds = payment_method.allowed_ticket_type_ids || []; // Access the loaded pos_ticket_types data from this.pos const allPosTicketTypes = this.pos.pos_ticket_types; // This seems to contain full records // Attempt to map IDs to full objects const ticketTypes = allowedTicketTypeIds.map(id => allPosTicketTypes.find(ticketType => ticketType.id === id) ).filter(Boolean); console.log("Allowed Ticket Type IDs (from payment_method):", allowedTicketTypeIds); console.log("All loaded POS Ticket Types (from this.pos):", allPosTicketTypes); console.log("Filtered Ticket Types for Popup (EXPECTING FULL OBJECTS):", ticketTypes); const { payload } = await this.dialog.add(ManageTicketRestaurantPopup, { title: _t("Tickets Restaurant"), ticketTypes: ticketTypes, // This `ticketTypes` array is empty or contains wrong data getPayload: (tickets) => { const total = tickets.reduce((sum, t) => sum + parseFloat(t.discounted), 0); const paymentline = this.currentOrder.selected_paymentline; if (paymentline) { paymentline.set_amount(total); paymentline.ticket_data = tickets; // for later use } }, close: () => {}, }); // (Optional code to remove payment line if popup is cancelled) // if (!payload || !payload.length) { // this.currentOrder.remove_paymentline(this.currentOrder.selected_paymentline); // } } } });