diff --git a/addons/point_of_sale/models/pos_config.py b/addons/point_of_sale/models/pos_config.py index f7d9d8e2e10..e2e42915b37 100644 --- a/addons/point_of_sale/models/pos_config.py +++ b/addons/point_of_sale/models/pos_config.py @@ -392,7 +392,7 @@ class PosConfig(models.Model): return pos_configs def _reset_default_on_vals(self, vals): - if 'tip_product_id' in vals and vals['iface_tipproduct'] and not vals['tip_product_id']: + if 'tip_product_id' in vals and not vals['tip_product_id'] and 'iface_tipproduct' in vals and vals['iface_tipproduct']: default_product = self.env.ref('point_of_sale.product_product_tip', False) if default_product: vals['tip_product_id'] = default_product.id diff --git a/addons/point_of_sale/models/pos_order.py b/addons/point_of_sale/models/pos_order.py index 00ef174980b..15e6aa1cbc4 100644 --- a/addons/point_of_sale/models/pos_order.py +++ b/addons/point_of_sale/models/pos_order.py @@ -59,7 +59,7 @@ class PosOrder(models.Model): 'tip_amount': ui_order.get('tip_amount', 0), 'access_token': ui_order.get('access_token', ''), 'ticket_code': ui_order.get('ticket_code', ''), - 'last_order_preparation_change': ui_order.get('last_order_preparation_change', False), + 'last_order_preparation_change': ui_order.get('last_order_preparation_change', '{}'), } @api.model @@ -113,9 +113,10 @@ class PosOrder(models.Model): return new_session - def _get_tracking_ref(self): - self.ensure_one() - return str(self.session_id.id)[-1] + str(self.sequence_number)[-2:] + @api.depends('sequence_number', 'session_id') + def _compute_tracking_number(self): + for record in self: + record.tracking_number = str(record.session_id.id)[-1] + str(record.sequence_number)[-2:] @api.model def _process_order(self, order, draft, existing_order): @@ -327,6 +328,7 @@ class PosOrder(models.Model): has_refundable_lines = fields.Boolean('Has Refundable Lines', compute='_compute_has_refundable_lines') refunded_orders_count = fields.Integer(compute='_compute_refund_related_fields') ticket_code = fields.Char(help='5 digits alphanumeric code to be used by portal user to request an invoice') + tracking_number = fields.Char(string="Tracking Number", compute='_compute_tracking_number') @api.depends('lines.refund_orderline_ids', 'lines.refunded_orderline_id') def _compute_refund_related_fields(self): @@ -1132,6 +1134,7 @@ class PosOrder(models.Model): 'access_token': order.access_token, 'ticket_code': order.ticket_code, 'last_order_preparation_change': order.last_order_preparation_change, + 'tracking_number':order.tracking_number, } @api.model diff --git a/addons/point_of_sale/models/pos_session.py b/addons/point_of_sale/models/pos_session.py index 4351088e28b..596d8fd599a 100644 --- a/addons/point_of_sale/models/pos_session.py +++ b/addons/point_of_sale/models/pos_session.py @@ -229,6 +229,18 @@ class PosSession(models.Model): sessions = super().create(vals_list) sessions.action_pos_session_open() + date_string = fields.Date.today().isoformat() + ir_sequence = self.env['ir.sequence'].sudo().search([('code', '=', f'pos.order_{date_string}')]) + if not ir_sequence: + self.env['ir.sequence'].sudo().create({ + 'name': _("PoS Order"), + 'padding': 0, + 'code': f'pos.order_{date_string}', + 'number_next': 1, + 'number_increment': 1, + 'company_id': self.env.company.id, + }) + return sessions def unlink(self): diff --git a/addons/point_of_sale/static/src/app/store/models.js b/addons/point_of_sale/static/src/app/store/models.js index 6e6c3466ee2..b58004dd6cd 100644 --- a/addons/point_of_sale/static/src/app/store/models.js +++ b/addons/point_of_sale/static/src/app/store/models.js @@ -1,6 +1,6 @@ /** @odoo-module */ -import { uuidv4 } from "@point_of_sale/utils"; +import { uuidv4, constructFullProductName } from "@point_of_sale/utils"; // FIXME POSREF - unify use of native parseFloat and web's parseFloat. We probably don't need the native version. import { parseFloat as oParseFloat } from "@web/views/fields/parsers"; import { @@ -567,27 +567,11 @@ export class Orderline extends PosModel { this.price_extra = parseFloat(price_extra) || 0.0; } set_full_product_name() { - let attributeString = ""; - - if (this.attribute_value_ids && this.attribute_value_ids.length > 0) { - for (const valId of this.attribute_value_ids) { - const value = this.pos.db.attribute_value_by_id[valId]; - if (value.is_custom) { - const customValue = this.custom_attribute_value_ids.find( - (cus) => cus.custom_product_template_attribute_value_id == parseInt(valId) - ); - attributeString += customValue - ? `${value.name}: ${customValue.custom_value}, ` - : `${value.name}, `; - } else { - attributeString += `${value.name}, `; - } - } - attributeString = attributeString.slice(0, -2); - attributeString = `(${attributeString})`; - } - - this.full_product_name = `${this.product.display_name} ${attributeString}`; + this.full_product_name = constructFullProductName( + this, + this.pos.db.attribute_value_by_id, + this.product.display_name + ); } get_price_extra() { return this.price_extra; diff --git a/addons/point_of_sale/static/src/utils.js b/addons/point_of_sale/static/src/utils.js index ff854138b7f..20ab81ce39d 100644 --- a/addons/point_of_sale/static/src/utils.js +++ b/addons/point_of_sale/static/src/utils.js @@ -29,3 +29,27 @@ export function deduceUrl(url) { } return url; } + +export function constructFullProductName(line, attribute_value_by_id, display_name) { + let attributeString = ""; + + if (line.attribute_value_ids && line.attribute_value_ids.length > 0) { + for (const valId of line.attribute_value_ids) { + const value = attribute_value_by_id[valId]; + if (value.is_custom) { + const customValue = line.custom_attribute_value_ids.find( + (cus) => cus.custom_product_template_attribute_value_id == parseInt(valId) + ); + attributeString += customValue + ? `${value.name}: ${customValue.custom_value}, ` + : `${value.name}, `; + } else { + attributeString += `${value.name}, `; + } + } + attributeString = attributeString.slice(0, -2); + attributeString = `(${attributeString})`; + } + + return `${display_name} ${attributeString}`; +} diff --git a/addons/pos_adyen/controllers/main.py b/addons/pos_adyen/controllers/main.py index a9a69a93a62..2fb43e8c245 100644 --- a/addons/pos_adyen/controllers/main.py +++ b/addons/pos_adyen/controllers/main.py @@ -20,7 +20,7 @@ class PosAdyenController(http.Controller): _logger.info('notification received from adyen:\n%s', pprint.pformat(data)) terminal_identifier = data['SaleToPOIResponse']['MessageHeader']['POIID'] payment_method_sudo = request.env['pos.payment.method'].sudo().search([('adyen_terminal_identifier', '=', terminal_identifier)], limit=1) - pos_session_id = int(data["SaleToPOIResponse"]["PaymentResponse"]["SaleData"]["SaleTransactionID"]["TransactionID"].split("--")[1]) + pos_session_id = int(data["SaleToPOIResponse"]["PaymentResponse"]["SaleData"]["SaleTransactionID"]["TransactionID"].split("-")[1]) pos_session_sudo = request.env["pos.session"].sudo().browse(pos_session_id) if payment_method_sudo: diff --git a/addons/pos_online_payment_self_order/models/pos_config.py b/addons/pos_online_payment_self_order/models/pos_config.py index 40d4edcab10..40b79f37d0e 100644 --- a/addons/pos_online_payment_self_order/models/pos_config.py +++ b/addons/pos_online_payment_self_order/models/pos_config.py @@ -8,25 +8,13 @@ from odoo.exceptions import ValidationError class PosConfig(models.Model): _inherit = 'pos.config' - self_order_online_payment_method_id = fields.Many2one('pos.payment.method', string='Self Online Payment', help="The online payment method to use when a customer pays a self-order online.", domain=[('is_online_payment', '=', True)], compute='_compute_self_order_online_payment_method_id', store=True, readonly=False) + self_order_online_payment_method_id = fields.Many2one('pos.payment.method', string='Self Online Payment', help="The online payment method to use when a customer pays a self-order online.", domain=[('is_online_payment', '=', True)], store=True, readonly=False) @api.constrains('self_order_online_payment_method_id') def _check_self_order_online_payment_method_id(self): - if any(config.self_order_online_payment_method_id and not config.self_order_online_payment_method_id._get_online_payment_providers(config.id, error_if_invalid=True) for config in self): - raise ValidationError(_("The online payment method used for self-order in a POS config must have at least one published payment provider supporting the currency of that POS config.")) - - @api.constrains('self_ordering_mode', 'self_ordering_pay_after', 'self_order_online_payment_method_id') - def _check_self_order_pay_after_each(self): - if any(config.self_ordering_mode == 'mobile' and config.self_ordering_pay_after == 'each' and not config.self_order_online_payment_method_id for config in self): - raise ValidationError(_("The POS self-order mode with payment after each order requires an online payment method to be configured.")) - - @api.depends('company_id', 'self_ordering_mode', 'self_ordering_pay_after', 'self_order_online_payment_method_id') - def _compute_self_order_online_payment_method_id(self): for config in self: - if not config.self_ordering_mode == 'mobile': - config.self_order_online_payment_method_id = False - elif config.self_ordering_pay_after == 'each' and (not config.self_order_online_payment_method_id or not config.self_order_online_payment_method_id.is_online_payment): - config.self_order_online_payment_method_id = self.env['pos.payment.method'].sudo()._get_or_create_online_payment_method(config.company_id.id, config.id) + if config.self_ordering_mode == 'mobile' and config.self_ordering_service_mode == 'each' and config.self_order_online_payment_method_id and not config.self_order_online_payment_method_id._get_online_payment_providers(config.id, error_if_invalid=True): + raise ValidationError(_("The online payment method used for self-order in a POS config must have at least one published payment provider supporting the currency of that POS config.")) def _get_self_ordering_data(self): res = super()._get_self_ordering_data() diff --git a/addons/pos_restaurant/models/pos_session.py b/addons/pos_restaurant/models/pos_session.py index 98967a6e073..ba31451cc18 100644 --- a/addons/pos_restaurant/models/pos_session.py +++ b/addons/pos_restaurant/models/pos_session.py @@ -95,5 +95,6 @@ class PosSession(models.Model): "note": "", "product_id": orderline.product_id.id, "quantity": orderline.qty, + "attribute_value_ids": orderline.attribute_value_ids.ids, } order.write({'last_order_preparation_change': json.dumps(last_order_preparation_change)}) diff --git a/addons/pos_self_order/__manifest__.py b/addons/pos_self_order/__manifest__.py index a729cb20285..0bb4c48177a 100644 --- a/addons/pos_self_order/__manifest__.py +++ b/addons/pos_self_order/__manifest__.py @@ -63,6 +63,7 @@ 'web/static/lib/bootstrap/js/dist/scrollspy.js', "pos_self_order/static/src/app/**/*", "point_of_sale/static/src/app/store/models/product_custom_attribute.js", + "point_of_sale/static/src/utils.js", 'web_editor/static/src/js/editor/odoo-editor/src/base_style.scss', 'web_editor/static/src/scss/web_editor.common.scss', ], diff --git a/addons/pos_self_order/controllers/orders.py b/addons/pos_self_order/controllers/orders.py index 32cf239bcf7..76386f5dddf 100644 --- a/addons/pos_self_order/controllers/orders.py +++ b/addons/pos_self_order/controllers/orders.py @@ -13,14 +13,10 @@ class PosSelfOrderController(http.Controller): is_take_away = order.get('take_away') pos_config, table = self._verify_authorization(access_token, table_identifier, is_take_away) pos_session = pos_config.current_session_id - - date_string = fields.Date.today().isoformat() ir_sequence_session = pos_config.env['ir.sequence'].with_context(company_id=pos_config.company_id.id).next_by_code(f'pos.order_{pos_session.id}') - ir_sequence_tracking = pos_config.env['ir.sequence'].with_context(company_id=pos_config.company_id.id).next_by_code(f'pos.order_{date_string}') sequence_number = re.findall(r'\d+', ir_sequence_session)[0] order_reference = self._generate_unique_id(pos_session.id, pos_config.id, sequence_number, device_type) - tracking_number = f"{'A' if device_type == 'kiosk' else 'B'}{ir_sequence_tracking}" fiscal_position = ( pos_config.self_ordering_alternative_fp_id @@ -49,7 +45,7 @@ class PosSelfOrderController(http.Controller): 'amount_total': 0, 'amount_paid': 0, 'amount_return': 0, - 'tracking_number': tracking_number, + 'table_stand_number': order.get('table_stand_number'), }, 'to_invoice': False, 'session_id': pos_session.id, @@ -143,6 +139,7 @@ class PosSelfOrderController(http.Controller): 'amount_tax': amount_total - amount_untaxed, 'amount_total': amount_total, 'table_id': table if table else False, + 'table_stand_number': order.get('table_stand_number'), }) pos_order.send_table_count_notification(pos_order.table_id) diff --git a/addons/pos_self_order/models/pos_config.py b/addons/pos_self_order/models/pos_config.py index 732c1608f93..18601965a67 100644 --- a/addons/pos_self_order/models/pos_config.py +++ b/addons/pos_self_order/models/pos_config.py @@ -147,7 +147,7 @@ class PosConfig(models.Model): def write(self, vals): for record in self: - if vals.get('self_ordering_mode') == 'kiosk' or record.self_ordering_mode == 'kiosk': + if vals.get('self_ordering_mode') == 'kiosk' or (vals.get('pos_self_ordering_mode') == 'mobile' and vals.get('pos_self_ordering_service_mode') == 'counter'): vals['self_ordering_pay_after'] = 'each' if (not vals.get('module_pos_restaurant') and not record.module_pos_restaurant) and vals.get('self_ordering_mode') == 'mobile': @@ -170,6 +170,11 @@ class PosConfig(models.Model): vals['self_ordering_image_home_ids'] = unlink_commands + vals['self_ordering_image_home_ids'] + if vals.get('self_ordering_mode') == 'mobile' and vals.get('self_ordering_pay_after') == 'meal': + vals['self_ordering_service_mode'] = 'table' + + if (vals.get('self_ordering_service_mode') == 'counter' or record.self_ordering_service_mode == 'counter') and vals.get('self_ordering_mode') == 'mobile': + vals['self_ordering_pay_after'] = 'each' return super().write(vals) @api.depends("module_pos_restaurant") diff --git a/addons/pos_self_order/models/pos_order.py b/addons/pos_self_order/models/pos_order.py index f7265eed186..0199f9b02bc 100644 --- a/addons/pos_self_order/models/pos_order.py +++ b/addons/pos_self_order/models/pos_order.py @@ -42,7 +42,7 @@ class PosOrderLine(models.Model): class PosOrder(models.Model): _inherit = "pos.order" - tracking_number = fields.Char(string="Tracking Number") + table_stand_number = fields.Char(string="Table Stand Number") take_away = fields.Boolean(string="Take Away", default=False) @api.model @@ -66,8 +66,8 @@ class PosOrder(models.Model): def _order_fields(self, ui_order): fields = super()._order_fields(ui_order) fields.update({ - 'tracking_number': ui_order.get('tracking_number'), 'take_away': ui_order.get('take_away'), + 'table_stand_number': ui_order.get('table_stand_number'), }) return fields @@ -92,7 +92,7 @@ class PosOrder(models.Model): "take_away": self.take_away, "pos_reference": self.pos_reference, "access_token": self.access_token, - "tracking_number": self.tracking_number, + "table_stand_number": self.table_stand_number, "state": self.state, "date_order": str(self.date_order), "amount_total": self.amount_total, @@ -113,3 +113,7 @@ class PosOrder(models.Model): for line in self.lines ], } + + def _send_order(self): + #This function is made to be overriden by pos_self_order_preparation_display + pass diff --git a/addons/pos_self_order/models/pos_session.py b/addons/pos_self_order/models/pos_session.py index 6c61fa0a373..6dd398752ab 100644 --- a/addons/pos_self_order/models/pos_session.py +++ b/addons/pos_self_order/models/pos_session.py @@ -15,8 +15,6 @@ class PosSession(models.Model): @api.model def _create_pos_self_sessions_sequence(self, sessions): - date_string = fields.Date.today().isoformat() - ir_sequence = self.env['ir.sequence'].sudo().search([('code', '=', f'pos.order_{date_string}')]) company_id = self.env.company.id for session in sessions: @@ -29,16 +27,6 @@ class PosSession(models.Model): 'company_id': company_id, }) - if not ir_sequence: - self.env['ir.sequence'].sudo().create({ - 'name': _("PoS Order"), - 'padding': 0, - 'code': f'pos.order_{date_string}', - 'number_next': 1, - 'number_increment': 1, - 'company_id': company_id, - }) - return sessions def _loader_params_product_product(self): diff --git a/addons/pos_self_order/static/src/app/components/product_card/product_card.js b/addons/pos_self_order/static/src/app/components/product_card/product_card.js index 11dba3acbd6..ca1d94143ff 100644 --- a/addons/pos_self_order/static/src/app/components/product_card/product_card.js +++ b/addons/pos_self_order/static/src/app/components/product_card/product_card.js @@ -5,6 +5,7 @@ import { useSelfOrder } from "@pos_self_order/app/self_order_service"; import { useService, useForwardRefToParent } from "@web/core/utils/hooks"; import { Line } from "@pos_self_order/app/models/line"; import { ProductInfoPopup } from "@pos_self_order/app/components/product_info_popup/product_info_popup"; +import { constructFullProductName } from "@point_of_sale/utils"; export class ProductCard extends Component { static template = "pos_self_order.ProductCard"; @@ -100,16 +101,18 @@ export class ProductCard extends Component { isProductInCart.qty++; } else { const lines = this.selfOrder.currentOrder.lines; - - lines.push( - new Line({ - id: null, - uuid: null, - qty: 1, - product_id: product.id, - full_product_name: product.name, - }) + const line = new Line({ + id: null, + uuid: null, + qty: 1, + product_id: product.id, + }); + line.full_product_name = constructFullProductName( + line, + this.selfOrder.attributeValueById, + product.name ); + lines.push(line); } await this.selfOrder.getPricesFromServer(); } diff --git a/addons/pos_self_order/static/src/app/models/line.js b/addons/pos_self_order/static/src/app/models/line.js index 00a293b4454..163463caaf9 100644 --- a/addons/pos_self_order/static/src/app/models/line.js +++ b/addons/pos_self_order/static/src/app/models/line.js @@ -28,7 +28,7 @@ export class Line extends Reactive { // server data this.id = line.id || null; this.uuid = line.uuid || uuidv4(); - this.full_product_name = line.full_product_name; + this.full_product_name = line.full_product_name || ""; this.product_id = line.product_id; this.qty = line.qty ? line.qty : 0; this.customer_note = line.customer_note; diff --git a/addons/pos_self_order/static/src/app/models/order.js b/addons/pos_self_order/static/src/app/models/order.js index 2cd9d878d84..cb930eceb68 100644 --- a/addons/pos_self_order/static/src/app/models/order.js +++ b/addons/pos_self_order/static/src/app/models/order.js @@ -13,7 +13,6 @@ export class Order extends Reactive { date, amount_total, amount_tax, - tracking_number, lastChangesSent, take_away, }) { @@ -32,7 +31,6 @@ export class Order extends Reactive { this.amount_total = order.amount_total || 0; this.amount_tax = order.amount_tax || 0; this.lines = order.lines || []; - this.tracking_number = order.tracking_number || null; this.take_away = typeof order.take_away === "boolean" ? order.take_away : null; // data @@ -53,6 +51,18 @@ export class Order extends Reactive { return this.isAlreadySent && this.hasNotAllLinesSent().length === 0; } + get trackingNumber() { + if (this.pos_reference) { + const reference = this.pos_reference; + const arrRef = reference.split(" ")[1].split("-"); + const sessionID = parseInt(arrRef[0]).toString(); + const sequence = parseInt(arrRef[2]).toString(); + const trackingNumber = sessionID + sequence; + return trackingNumber; + } + return null; + } + initLines() { this.lines = this.lines.map((line) => new Line(line)); } diff --git a/addons/pos_self_order/static/src/app/pages/cart_page/cart_page.js b/addons/pos_self_order/static/src/app/pages/cart_page/cart_page.js index ab43eee4337..18ad53fa73f 100644 --- a/addons/pos_self_order/static/src/app/pages/cart_page/cart_page.js +++ b/addons/pos_self_order/static/src/app/pages/cart_page/cart_page.js @@ -63,6 +63,11 @@ export class CartPage extends Component { return; } + if (type === "mobile" && orderingMode === "table" && !takeAway && !this.selfOrder.table) { + this.state.selectTable = true; + return; + } + if (mode === "meal" && !order.isSavedOnServer) { this.sendInProgress = true; await this.selfOrder.sendDraftOrderToServer(); @@ -72,13 +77,9 @@ export class CartPage extends Component { return; } } - if (orderingMode === "table" && !takeAway) { if (type === "kiosk") { this.router.navigate("stand_number"); - } else if (type === "mobile" && !this.selfOrder.table) { - this.state.selectTable = true; - return; } else { this.router.navigate("payment"); } diff --git a/addons/pos_self_order/static/src/app/pages/cart_page/cart_page.xml b/addons/pos_self_order/static/src/app/pages/cart_page/cart_page.xml index 343bbbe571f..2b0ddd43321 100644 --- a/addons/pos_self_order/static/src/app/pages/cart_page/cart_page.xml +++ b/addons/pos_self_order/static/src/app/pages/cart_page/cart_page.xml @@ -19,7 +19,7 @@
x - +
- - () + - ()
- : diff --git a/addons/pos_self_order/static/src/app/pages/combo_page/combo_page.js b/addons/pos_self_order/static/src/app/pages/combo_page/combo_page.js index 72909bd0342..98b74043b55 100644 --- a/addons/pos_self_order/static/src/app/pages/combo_page/combo_page.js +++ b/addons/pos_self_order/static/src/app/pages/combo_page/combo_page.js @@ -6,6 +6,7 @@ import { ComboSelection } from "@pos_self_order/app/components/combo_selection/c import { useService } from "@web/core/utils/hooks"; import { Line } from "@pos_self_order/app/models/line"; import { attributeFlatter, attributeFormatter } from "@pos_self_order/app/utils"; +import { constructFullProductName } from "@point_of_sale/utils"; export class ComboPage extends Component { static template = "pos_self_order.ComboPage"; @@ -151,12 +152,16 @@ export class ComboPage extends Component { uuid: null, qty: this.state.qty, product_id: combo.product.id, - full_product_name: combo.product.name, attribute_value_ids: attributeFlatter(combo.product.variants), custom_attribute_value_ids: Object.values(combo.product.customValues), combo_parent_uuid: parent_line.uuid, combo_id: combo.id, }); + child_line.full_product_name = constructFullProductName( + child_line, + this.selfOrder.attributeValueById, + combo.product.name + ); lines.push(child_line); parent_line.child_lines.push(child_line); } diff --git a/addons/pos_self_order/static/src/app/pages/landing_page/landing_page.js b/addons/pos_self_order/static/src/app/pages/landing_page/landing_page.js index 785e71505bf..e16cb338198 100644 --- a/addons/pos_self_order/static/src/app/pages/landing_page/landing_page.js +++ b/addons/pos_self_order/static/src/app/pages/landing_page/landing_page.js @@ -22,8 +22,6 @@ export class LandingPage extends Component { this.selfOrder.orders = []; this.selfOrder.editedOrder = null; } - - this.tablePadNumber = null; }); onMounted(() => { diff --git a/addons/pos_self_order/static/src/app/pages/order_history_page/order_history_page.xml b/addons/pos_self_order/static/src/app/pages/order_history_page/order_history_page.xml index 8087537c9ce..4a8d973af56 100644 --- a/addons/pos_self_order/static/src/app/pages/order_history_page/order_history_page.xml +++ b/addons/pos_self_order/static/src/app/pages/order_history_page/order_history_page.xml @@ -55,7 +55,7 @@
- +