// Copyright (C) 2020 - Today: GRAP (http://www.grap.coop) // @author: Sylvain LE GAL (https://twitter.com/legalsylvain) // License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). odoo.define("pos_meal_voucher.screens", function (require) { "use strict"; var screens = require("point_of_sale.screens"); var core = require('web.core'); var formats = require('web.formats'); var _t = core._t; var QWeb = core.qweb; var Model = require('web.DataModel'); var config_parameter = new Model('ir.config_parameter'); var utils = require("web.utils"); var round_pr = utils.round_precision; screens.ScreenWidget.include({ barcode_meal_voucher_payment_action: function (code) { var order = this.pos.get_order(); if(!order.paper_meal_vouche_number_already_used(code.code)){ // Display the payment screen, if it is not the current one. if (this.pos.gui.current_screen.template !== "PaymentScreenWidget"){ this.gui.show_screen("payment"); } var paymentScreen = this.pos.gui.current_screen; var amount = code.value; // Check if the total amount is OK regarding to max amounts var total_eligible = order.get_total_meal_voucher_eligible(); var max_amount = this.pos.config.max_meal_voucher_amount; var total_received = order.get_total_meal_voucher_received() + amount; let order_due = order.get_due(); // Display info popup if amount is too high // bug fix below : as order_due deduces previous paiement lines from initial due, // it must be compared to amount of current line only and not to total_received if (/*total_received*/amount > order_due) { this.gui.show_popup("alert", { 'title': _t("Impossible d'utiliser ce Titre Restaurant"), 'body': _t("Vous ne pouvez pas ajouter ce ticket restaurant car le montant total est supérieur au montant restant dû de la commande") }); return; } if (total_received > total_eligible) { this.gui.show_popup("alert", { 'title': _t("Impossible d'utiliser ce Titre Restaurant"), 'body': _t("Vous ne pouvez pas ajouter ce ticket restaurant car le montant total est supérieur au montant éligible en titres restaurant") }); return; } if (total_received > max_amount && max_amount > 0) { this.gui.show_popup("alert", { 'title': _t("Impossible d'utiliser ce Titre Restaurant"), 'body': _t("Vous ne pouvez pas ajouter ce ticket restaurant car le montant total est supérieur au montant maximum autorisé") }); return; } var cashregister = null; // find a meal voucher cash register, if exist for ( var i = 0; i < this.pos.cashregisters.length; i++ ) { if ( this.pos.cashregisters[i].journal.meal_voucher_type === "paper" ){ cashregister = this.pos.cashregisters[i]; break; } } if (!cashregister){ return; } // Add new payment line with the amount found in the barcode this.pos.get_order().add_paymentline(cashregister); paymentScreen.reset_input() order.selected_paymentline.set_amount(amount); order.selected_paymentline.statement_note = code.code; paymentScreen.order_changes(); paymentScreen.render_paymentlines(); paymentScreen.$(".paymentline.selected .edit").text(paymentScreen.format_currency_no_symbol(amount)); }else{ this.gui.show_popup("alert", { 'title': _t("Titre restaurant déjà scanné"), 'body': _t("Le ticket restaurant ") + code.code + _t(" a déjà été scanné") }); } }, // Setup the callback action for the "meal_voucher_payment" barcodes. show: function () { this._super(); this.pos.barcode_reader.set_action_callback( "meal_voucher_payment", _.bind(this.barcode_meal_voucher_payment_action, this)); }, }); screens.OrderWidget.include({ //check if POS if configured to accept payment in meal voucher meal_voucher_activated: function() { var is_meal_voucher_method = this.pos.cashregisters.map(function(cash_register) { if (cash_register.journal.meal_voucher_type === "paper" || cash_register.journal.meal_voucher_type === "dematerialized") { return true } }) return is_meal_voucher_method.includes(true); }, update_summary: function () { this._super.apply(this, arguments); var order = this.pos.get_order(); if (!this.meal_voucher_activated()) { const meal_voucher_summary = this.el.querySelector(".summary .meal-voucher"); if (meal_voucher_summary != null) { meal_voucher_summary.style.display = 'none'; } // this.el.querySelector(".summary .meal-voucher").style.display = 'none'; return; } if (!order.get_orderlines().length) { return; } this.el.querySelector(".summary .meal-voucher .value").textContent = this.format_currency(order.get_total_meal_voucher_eligible()); }, }); screens.PaymentScreenWidget.include({ //check if POS if configured to accept payment in meal voucher meal_voucher_activated: function() { var is_meal_voucher_method = this.pos.cashregisters.map(function(cash_register) { if (cash_register.journal.meal_voucher_type === "paper" || cash_register.journal.meal_voucher_type === "dematerialized") { return true } }) return is_meal_voucher_method.includes(true); }, // odoo/addons/point_of_sale/static/src/js/screens.js // We need to change the default behaviour of locking input with // popup bcause of the papar meal voucher init: function(parent, options) { var self = this; this._super(parent, options); // This is a keydown handler that prevents backspace from // doing a back navigation. It also makes sure that keys that // do not generate a keypress in Chrom{e,ium} (eg. delete, // backspace, ...) get passed to the keypress handler. this.keyboard_keydown_handler = function(event){ // We overight the expected behaviour if a popup is open if (self.gui.has_popup()) { } else if (event.keyCode === 8 || event.keyCode === 46) { // Backspace and Delete event.preventDefault(); console.log('keyboard_keydown_handler') // These do not generate keypress events in // Chrom{e,ium}. Even if they did, we just called // preventDefault which will cancel any keypress that // would normally follow. So we call keyboard_handler // explicitly with this keydown event. self.keyboard_handler(event); } }; // This keyboard handler listens for keypress events. It is // also called explicitly to handle some keydown events that // do not generate keypress events. this.keyboard_handler = function(event){ var key = ''; // We overight the expected behaviour if a popup is open if (self.gui.has_popup()) { } else{ if (event.type === "keypress") { if (event.keyCode === 13) { // Enter self.validate_order(); } else if ( event.keyCode === 190 || // Dot event.keyCode === 110 || // Decimal point (numpad) event.keyCode === 188 || // Comma event.keyCode === 46 ) { // Numpad dot key = self.decimal_point; } else if (event.keyCode >= 48 && event.keyCode <= 57) { // Numbers key = '' + (event.keyCode - 48); } else if (event.keyCode === 45) { // Minus key = '-'; } else if (event.keyCode === 43) { // Plus key = '+'; } } else { // keyup/keydown if (event.keyCode === 46) { // Delete key = 'CLEAR'; } else if (event.keyCode === 8) { // Backspace key = 'BACKSPACE'; } } self.payment_input(key); event.preventDefault(); } }; this.meal_voucher_issuers = []; // get meal voucher issuers from config config_parameter.call('get_param', ['pos_meal_voucher.meal_voucher_issuers']) .then( function(value){ try { self.meal_voucher_issuers = JSON.parse(value); } catch (error) { self.gui.show_popup("alert", { 'title': _t("Problème de configuration du POS"), 'body': _t("Le paramètre des émetteurs de CB déj est mal formatté dans la configuration d'Odoo. Veuillez demander à un.e salarié.e de régler ce problème (Configuration > Paramètres systèmes > pos_meal_voucher.meal_voucher_issuers). Vous pouvez continuer à utiliser la caisse.") }); } }); }, payment_input: function(input) { var newbuf = this.gui.numpad_input(this.inputbuffer, input, {'firstinput': this.firstinput}); this.firstinput = (newbuf.length === 0); if (newbuf !== this.inputbuffer) { this.inputbuffer = newbuf; var order = this.pos.get_order(); // We choose to unactivated the editing of the amount for the paper meal voucher if (order.selected_paymentline && !order.selected_paymentline.is_paper_meal_voucher()) { var amount = this.inputbuffer; if (this.inputbuffer !== "-") { amount = formats.parse_value(this.inputbuffer, {type: "float"}, 0.0); } order.selected_paymentline.set_amount(amount); this.order_changes(); this.render_paymentlines(); this.$('.paymentline.selected .edit').text(this.format_currency_no_symbol(amount)); } } var order = this.pos.get_order(); if(order.selected_paymentline && order.selected_paymentline.is_dematerialized_meal_voucher()){ var total_eligible = order.get_total_meal_voucher_eligible(); var total_received = order.get_total_meal_voucher_received(); var max_amount = this.pos.config.max_meal_voucher_amount; var current_max = total_eligible; if (max_amount) { current_max = Math.min(total_eligible, max_amount); } // If there is change -> the amount of last payment line was above the order remaining due amount let order_change = order.get_change(); if (order_change > 0) { let last_line_amount = order.selected_paymentline.get_amount(); let limit_amount = round_pr(last_line_amount - order_change, this.pos.currency.rounding) current_max = Math.min(current_max, limit_amount); } if (total_received > current_max){ this.gui.show_popup("alert", { 'title': _t("Meal Voucher Amount incorrect"), 'body': _t("Le montant saisi est supérieur au maximum éligible/au montant restant dû de ") + this.format_currency(current_max), }); const max_current_amount = current_max-total_received+order.selected_paymentline.get_amount() ; order.selected_paymentline.set_amount(max_current_amount); this.order_changes(); this.render_paymentlines(); this.$('.paymentline.selected .edit').text(this.format_currency_no_symbol(max_current_amount)); this.inputbuffer = ""; } } }, click_paymentmethods_meal_voucher_mixed: function(id) { var cashregister = null; for ( var i = 0; i < this.pos.cashregisters.length; i++ ) { if ( this.pos.cashregisters[i].journal_id[0] === id ){ cashregister = this.pos.cashregisters[i]; break; } } this.pos.get_order().add_paymentline( cashregister ); // manually set meal voucher this.pos.get_order().selected_paymentline.manual_meal_voucher = true; this.reset_input(); this.render_paymentlines(); }, render_paymentmethods: function() { var self = this; var methods = this._super.apply(this, arguments); methods.on('click','.paymentmethod-meal-voucher-mixed',function(){ self.click_paymentmethods_meal_voucher_mixed($(this).data('id')); }); return methods; }, remove_selected_paymentline: function(order) { this.pos.get_order().remove_paymentline(order.selected_paymentline); var paymentScreen = this.pos.gui.current_screen; paymentScreen.reset_input(); paymentScreen.render_paymentlines(); }, click_paymentmethods: function(id) { var self = this; var methods = this._super.apply(this, arguments); var paymentScreen = this.pos.gui.current_screen; var order = this.pos.get_order(); if ( order.selected_paymentline.is_meal_voucher() && order.get_total_meal_voucher_eligible() == 0 ) { this.gui.show_popup("alert", { 'title': _t("Impossible de payer en titre restaurant"), 'body': _t("Aucun article du panier n'est éligible en titres restaurants (Montant éligible : 0€)."), cancel: function() { self.remove_selected_paymentline(order); } }); return false; } // if user choose card meal voucher if(order.selected_paymentline.is_meal_voucher() && order.selected_paymentline.is_dematerialized_meal_voucher()){ // Set selected payment line amount to 0 before any further check order.selected_paymentline.set_amount(0); paymentScreen.order_changes(); paymentScreen.render_paymentlines(); paymentScreen.$(".paymentline.selected .edit").text(paymentScreen.format_currency_no_symbol(0)); // update selected (last) payment line & order with meal voucher data function update_order_meal_voucher_data(issuer = '') { var total_eligible = order.get_total_meal_voucher_eligible(); // Function defined in module "lacagette_mona", that may not be activated. // If said module is activated, consider this eligible amount. if (typeof order.get_total_meal_vouchers_eligible_including_mona === "function") { total_eligible = order.get_total_meal_vouchers_eligible_including_mona(); } var total_received = order.get_total_meal_voucher_received(); var max_amount = self.pos.config.max_meal_voucher_amount; var current_max = total_eligible; if (max_amount) { current_max = Math.min(total_eligible, max_amount); } // Check how much is still possible to pay with meal voucher // The selected line is "by default" set to the rest to pay of the order let max_current_amount = current_max-total_received +order.selected_paymentline.get_amount(); // Limit the amount paid to the remaining due amount const order_due_amount = order.get_due(); max_current_amount = Math.min(max_current_amount, order_due_amount); order.selected_paymentline.set_amount(max_current_amount); order.selected_paymentline.set_meal_voucher_issuer(issuer); paymentScreen.order_changes(); paymentScreen.render_paymentlines(); paymentScreen.$(".paymentline.selected .edit").text(paymentScreen.format_currency_no_symbol(max_current_amount)); } // If required by the config, ask for the meal voucher issuer if (this.pos.config.meal_voucher_ask_for_issuer) { let select_data = [{ 'val': "", 'text': "- Choississez un émetteur" }]; for (let issuer of this.meal_voucher_issuers) { select_data.push({ "val": issuer, "text": issuer }); } this.gui.show_popup("select-or-textinput", { 'title': _t("Émetteur du titre restaurant"), 'body': _t("Si l'émetteur n'est pas dans la liste ci-dessus, veuillez le noter dans le champs ci-dessous : "), 'selectText': _t("Veuillez sélectionner l'émetteur de CB déj dans la liste ci-dessous : "), 'selectData': select_data, confirm: function(value) { let issuer = value; update_order_meal_voucher_data(issuer) }, cancel: function(value) { self.remove_selected_paymentline(order); }, }); } else { update_order_meal_voucher_data(); } } // If user choose paper meal voucher // Open a popup asking for the number of the meal voucher if(order.selected_paymentline.is_meal_voucher() && order.selected_paymentline.is_paper_meal_voucher()){ // Maybe not needed but cleaner : Set selected payment line amount to 0 before any further check order.selected_paymentline.set_amount(0); paymentScreen.order_changes(); paymentScreen.render_paymentlines(); paymentScreen.$(".paymentline.selected .edit").text(paymentScreen.format_currency_no_symbol(0)); this.gui.show_popup("textinput", { 'title': _t("Chèque Restaurant"), 'body': _t("Pour ajouter un chèque restaurant, merci de scanner le code barre du chèque. Si le chèque est illisible, veuillez rentrer le code à la main."), confirm: function(value) { this.pos.get_order().remove_paymentline(order.selected_paymentline); var paymentScreen = this.pos.gui.current_screen; paymentScreen.reset_input(); paymentScreen.render_paymentlines(); var core = ''; odoo.define('coreservice', function(require){core=require('web.core');}) core.bus.trigger('barcode_scanned', value) }, cancel: function(vaue) { self.remove_selected_paymentline(order); }, }); } }, render_paymentlines: function() { var self = this; this._super.apply(this, arguments); var order = this.pos.get_order(); if (!order) { return; } // Update meal voucher summary var total_eligible = order.get_total_meal_voucher_eligible(); var max_amount = this.pos.config.max_meal_voucher_amount; this.el.querySelector("#meal-voucher-eligible-amount").textContent = this.format_currency(Math.min(total_eligible,max_amount)); var total_received = order.get_total_meal_voucher_received(); this.el.querySelector("#meal-voucher-received-amount").textContent = this.format_currency(total_received); // Display warnings if (total_received > total_eligible) { this.$("#meal-voucher-eligible-warning").removeClass("oe_hidden"); } else { this.$("#meal-voucher-eligible-warning").addClass("oe_hidden"); } if (total_received > max_amount) { this.$("#meal-voucher-max-warning").removeClass("oe_hidden"); } else { this.$("#meal-voucher-max-warning").addClass("oe_hidden"); } if (!this.meal_voucher_activated()) { this.$(".meal-voucher-summary").addClass("oe_hidden"); } }, order_is_valid: function(force_validation) { var self = this; var order = this.pos.get_order(); var total_eligible = order.get_total_meal_voucher_eligible(); var total_received = order.get_total_meal_voucher_received(); var max_amount = this.pos.config.max_meal_voucher_amount; var current_max = total_eligible; if (max_amount) { current_max = Math.min(total_eligible, max_amount); } // if the change is too large, it's probably an input error, if giving change is accepted make the user confirm. if (!force_validation && (total_received > current_max) && this.pos.config.meal_voucher_change_accepted) { this.gui.show_popup("confirm", { 'title': _t("Please Confirm Meal Voucher Amount"), 'body': _t("You are about to validate a meal voucher payment of ") + this.format_currency(total_received) + _t(", when the maximum amount is ") + this.format_currency(current_max) + _t(".\n\n Clicking 'Confirm' will validate the payment."), confirm: function() { // Note: Due to the bad design of the original function // the check "Large Amount" will be skipped in that case. self.validate_order("confirm"); }, }); return false; } //else force user to correct else if (!force_validation && (total_received > current_max) && !this.pos.config.meal_voucher_change_accepted) { this.gui.show_popup("alert", { 'title': _t("Meal Voucher Amount incorrect"), 'body': _t("Warning, the maximum amount of meal voucher accepted ( ") + this.format_currency(current_max) + _t(" ) is under the amount input ( ") + this.format_currency(total_received) + _t(")"), }); return false; } return this._super.apply(this, arguments); }, }); /* point_of_sale/statis/src/js/screens.js */ screens.ReceiptScreenWidget.include({ render_receipt: function() { var order = this.pos.get_order(); order.meal_voucher_used = order.is_meal_voucher_used(); this.$('.pos-receipt-container').html(QWeb.render('PosTicket',{ widget:this, order: order, receipt: order.export_for_printing(), orderlines: order.get_orderlines(), paymentlines: order.get_paymentlines() })); } }); });