Commit a747b1f9 by Thibault Grandjean

Merge branch 'dev_cooperatic' of https://gl.cooperatic.fr/cooperatic-foodcoops/odoo into docker

parents 3cd0ed8a 59273361
......@@ -27,6 +27,9 @@ class ShiftRegistration(models.Model):
to_add = 2 if absence_status == 'absent' else 1
for s in self.env['shift.registration']\
.search([('id', 'in', self.ids)]):
# Missing a makeup leads to have an additional makeup (the shift you initialy missed + the makeup you missed)
if s.is_makeup is True:
to_add += 1
new_makeups_to_do = s.partner_id.makeups_to_do + to_add
s.partner_id.update({'makeups_to_do': new_makeups_to_do})
return super(ShiftRegistration, self).write(vals)
\ No newline at end of file
......@@ -58,6 +58,9 @@ Products
.. figure:: https://raw.githubusercontent.com/legalsylvain/pos/12.0-ADD-pos_meal_voucher/pos_meal_voucher/static/description/pos_config_form.png
* configure if you want to allow or forbid to exceed the maximum allowed by ticket and for order (Give change on meal voucher)
Usage
=====
......@@ -86,6 +89,9 @@ It is a non blocking warning, because we don't want to prevent an order to be do
if products are not correctly set, or if a recent law changed the maximum amount that can
be used each day. (A recent case occured in France, during the Covid-19 pandemy)
If you want to make it impossible to finish an order if the amount of meal voucher is too big then uncheck the option "meal_voucher_change_accepted"
In this case if the cashier tries to validate the order the warning will redirect him to the paiement page.
Note
~~~~
......
......@@ -292,3 +292,64 @@ msgstr "En cliquant sur ce bouton, tous les produits de cette catégories auront
msgid "kg"
msgstr "kg"
#. module: pos_meal_voucher
#. openerp-web
#: code:addons/pos_meal_voucher/static/src/js/screens.js:159
#, python-format
msgid "Meal Voucher Amount incorrect"
msgstr "Titre restaurant incorrect"
#. module: pos_meal_voucher
#. openerp-web
#: code:addons/pos_meal_voucher/static/src/js/screens.js:160
#, python-format
msgid "Warning, the maximum amount of meal voucher accepted ( "
msgstr "Attention, le montant éligible au paiement par titre restaurant( "
#. module: pos_meal_voucher
#. openerp-web
#: code:addons/pos_meal_voucher/static/src/js/screens.js:160
#, python-format
msgid " ) is under the amount input ( "
msgstr " ) est inférieur à la valeur du/des ticket(s)( "
#. module: pos_meal_voucher
#. openerp-web
#: code:addons/pos_meal_voucher/static/src/js/screens.js:110
#, python-format
msgid "Warning, the input amount of meal voucher is above the maximum amount of "
msgstr "Le montant saisi est supérieur au montant maximum/au maximum éligible de "
#. module: pos_meal_voucher
#. openerp-web
#: code:addons/pos_meal_voucher/static/src/js/screens.js:
#, python-format
msgid "Meal Voucher already used"
msgstr "Ticket restaurant déjà scanné"
#. module: pos_meal_voucher
#. openerp-web
#: code:addons/pos_meal_voucher/static/src/js/screens.js:
#, python-format
msgid "The paper meal voucher "
msgstr "Le ticket restaurant "
#. module: pos_meal_voucher
#. openerp-web
#: code:addons/pos_meal_voucher/static/src/js/screens.js:
#, python-format
msgid " was already used"
msgstr " a déjà été scanné"
#: code:addons/pos_meal_voucher/static/src/js/screens.js:196
#, python-format
msgid "Meal Voucher ticket"
msgstr "Chèque restaurant"
#. module: pos_meal_voucher
#. openerp-web
#: code:addons/pos_meal_voucher/static/src/js/screens.js:197
#, python-format
msgid "To add a meal voucher ticket close this window and scan the ticket. If the ticket can't be read please enter the code"
msgstr "Pour ajouter un chèque restaurant merci de fermer cette fenêtre et scanner le chèque. Si le chèque est illisible veuillez rentrer le code à la main."
......@@ -16,3 +16,7 @@ class PosConfig(models.Model):
meal_voucher_display_product_screen = fields.Boolean(
string="Display icon before products on screen",
default=True)
meal_voucher_change_accepted = fields.Boolean(
string="Give change on meal voucher",
default=True)
......@@ -7,7 +7,7 @@ odoo.define("pos_meal_voucher.models", function (require) {
var models = require("point_of_sale.models");
var utils = require("web.utils");
var round_pr = utils.round_precision;
models.load_fields("product.product", ["meal_voucher_ok"]);
......@@ -16,6 +16,14 @@ odoo.define("pos_meal_voucher.models", function (require) {
var OrderSuper = models.Order.prototype;
var Order = models.Order.extend({
paper_meal_vouche_number_already_used: function(meal_voucher_number){
for(const paiementLine of this.get_paymentlines()){
if(paiementLine.statement_note == meal_voucher_number) return true
}
return false;
},
get_total_meal_voucher_eligible: function() {
return round_pr(this.orderlines.reduce((function(sum, orderLine) {
......@@ -35,6 +43,126 @@ odoo.define("pos_meal_voucher.models", function (require) {
}
}), 0), this.pos.currency.rounding);
},
/* point_of_sale/statis/src/js/models.js */
export_for_printing: function(){
var orderlines = [];
var self = this;
var paymentlines = [];
// Add fork&knife symbol on pos ticket for products with meal voucher allowed & when meal voucher is used
var meal_voucher_used = false;
this.paymentlines.each(function(paymentline){
var line = paymentline.export_for_printing();
paymentlines.push(line);
if (paymentline.is_meal_voucher()) {
meal_voucher_used = true;
}
});
this.orderlines.each(function(orderline){
var orderline_for_printing = orderline.export_for_printing()
if (
meal_voucher_used === true &&
orderline.product.meal_voucher_ok === true &&
orderline_for_printing.product_name.includes(String.fromCharCode(0xD83C, 0xDF74)) === false
) {
orderline_for_printing.product_name = String.fromCharCode(0xD83C, 0xDF74) + " " + orderline_for_printing.product_name;
} else if (meal_voucher_used === false && orderline_for_printing.product_name.includes(String.fromCharCode(0xD83C, 0xDF74)) === true) {
orderline_for_printing.product_name = orderline_for_printing.product_name.replace(String.fromCharCode(0xD83C, 0xDF74) + " ", "");
}
orderlines.push(orderline_for_printing);
});
var client = this.get('client');
var cashier = this.pos.cashier || this.pos.user;
var company = this.pos.company;
var shop = this.pos.shop;
var date = new Date();
function is_xml(subreceipt){
return subreceipt ? (subreceipt.split('\n')[0].indexOf('<!DOCTYPE QWEB') >= 0) : false;
}
function render_xml(subreceipt){
if (!is_xml(subreceipt)) {
return subreceipt;
} else {
subreceipt = subreceipt.split('\n').slice(1).join('\n');
var qweb = new QWeb2.Engine();
qweb.debug = core.debug;
qweb.default_dict = _.clone(QWeb.default_dict);
qweb.add_template('<templates><t t-name="subreceipt">'+subreceipt+'</t></templates>');
return qweb.render('subreceipt',{'pos':self.pos,'widget':self.pos.chrome,'order':self, 'receipt': receipt}) ;
}
}
var receipt = {
orderlines: orderlines,
paymentlines: paymentlines,
subtotal: this.get_subtotal(),
total_with_tax: this.get_total_with_tax(),
total_without_tax: this.get_total_without_tax(),
total_tax: this.get_total_tax(),
total_paid: this.get_total_paid(),
total_discount: this.get_total_discount(),
tax_details: this.get_tax_details(),
change: this.get_change(),
name : this.get_name(),
client: client ? client.name : null ,
invoice_id: null, //TODO
cashier: cashier ? cashier.name : null,
precision: {
price: 2,
money: 2,
quantity: 3,
},
date: {
year: date.getFullYear(),
month: date.getMonth(),
date: date.getDate(), // day of the month
day: date.getDay(), // day of the week
hour: date.getHours(),
minute: date.getMinutes() ,
isostring: date.toISOString(),
localestring: date.toLocaleString(),
},
company:{
email: company.email,
website: company.website,
company_registry: company.company_registry,
contact_address: company.partner_id[1],
vat: company.vat,
name: company.name,
phone: company.phone,
logo: this.pos.company_logo_base64,
},
shop:{
name: shop.name,
},
currency: this.pos.currency,
};
if (is_xml(this.pos.config.receipt_header)){
receipt.header = '';
receipt.header_xml = render_xml(this.pos.config.receipt_header);
} else {
receipt.header = this.pos.config.receipt_header || '';
}
if (is_xml(this.pos.config.receipt_footer)){
receipt.footer = '';
receipt.footer_xml = render_xml(this.pos.config.receipt_footer);
} else {
receipt.footer = this.pos.config.receipt_footer || '';
}
return receipt;
},
});
models.Order = Order;
......@@ -71,6 +199,14 @@ odoo.define("pos_meal_voucher.models", function (require) {
this.cashregister.journal.meal_voucher_type) !== -1
);
},
is_dematerialized_meal_voucher: function() {
return (
this.cashregister.journal.meal_voucher_type == "dematerialized") ;
},
is_paper_meal_voucher: function() {
return (
this.cashregister.journal.meal_voucher_type == "paper") ;
},
});
......
......@@ -7,38 +7,53 @@ odoo.define("pos_meal_voucher.screens", function (require) {
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;
screens.ScreenWidget.include({
barcode_meal_voucher_payment_action: function (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 order = this.pos.get_order();
var amount = code.value;
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(!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");
}
}
if (!cashregister){
return;
var paymentScreen = this.pos.gui.current_screen;
var order = this.pos.get_order();
var amount = code.value;
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("Meal Voucher Amount already used"),
'body': _t("The paper meal voucher ") +
code.code + _t(" was already used"),
});
}
// 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));
},
// Setup the callback action for the "meal_voucher_payment" barcodes.
......@@ -65,6 +80,129 @@ odoo.define("pos_meal_voucher.screens", function (require) {
screens.PaymentScreenWidget.include({
// 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();
}
};
},
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 (total_received > current_max){
this.gui.show_popup("alert", {
'title': _t("Meal Voucher Amount incorrect"),
'body': _t("Warning, the input amount of meal voucher is above the maximum amount of ") +
this.format_currency(current_max),
});
const max_curent_amount = current_max-total_received+order.selected_paymentline.get_amount() ;
order.selected_paymentline.set_amount(max_curent_amount);
this.order_changes();
this.render_paymentlines();
this.$('.paymentline.selected .edit').text(this.format_currency_no_symbol(max_curent_amount));
this.inputbuffer = "";
}
}
},
click_paymentmethods_meal_voucher_mixed: function(id) {
var cashregister = null;
for ( var i = 0; i < this.pos.cashregisters.length; i++ ) {
......@@ -88,11 +226,71 @@ odoo.define("pos_meal_voucher.screens", function (require) {
});
return methods;
},
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.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);
}
// 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
const max_curent_amount = current_max-total_received +order.selected_paymentline.get_amount();
order.selected_paymentline.set_amount(max_curent_amount);
paymentScreen.order_changes();
paymentScreen.render_paymentlines();
paymentScreen.$(".paymentline.selected .edit").text(paymentScreen.format_currency_no_symbol(max_curent_amount));
}
// 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()){
this.gui.show_popup("textinput", {
'title': _t("Meal Voucher ticket"),
'body': _t("To add a meal voucher ticket close this window and scan the ticket. If the ticket can't be read please enter the code"),
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) {
this.pos.get_order().remove_paymentline(order.selected_paymentline);
var paymentScreen = this.pos.gui.current_screen;
paymentScreen.reset_input();
paymentScreen.render_paymentlines();
},
});
}
},
render_paymentlines: function() {
var self = this;
this._super.apply(this, arguments);
var order = this.pos.get_order();
if (!order) {
......@@ -136,8 +334,8 @@ odoo.define("pos_meal_voucher.screens", function (require) {
current_max = Math.min(total_eligible, max_amount);
}
// if the change is too large, it's probably an input error, make the user confirm.
if (!force_validation && (total_received > current_max)) {
// 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 ") +
......@@ -153,8 +351,57 @@ odoo.define("pos_meal_voucher.screens", function (require) {
});
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();
var orderlines = order.get_orderlines();
var paymentlines = order.get_paymentlines();
// Add fork&knife symbol on pos ticket for product with meal voucher allowed & when meal voucher is used
var meal_voucher_used = false;
for (var i = 0; i < paymentlines.length; i++) {
if (paymentlines[i].is_meal_voucher()) {
meal_voucher_used = true;
break;
}
};
for (var i = 0; i < orderlines.length; i++) {
if (
meal_voucher_used === true &&
orderlines[i].product.meal_voucher_ok === true &&
orderlines[i].product.display_name.includes(String.fromCharCode(0xD83C, 0xDF74)) === false
) {
orderlines[i].product.display_name = String.fromCharCode(0xD83C, 0xDF74) + " " + orderlines[i].product.display_name;
} else if (meal_voucher_used === false && orderlines[i].product.display_name.includes(String.fromCharCode(0xD83C, 0xDF74)) === true) {
orderlines[i].product.display_name = orderlines[i].product.display_name.replace(String.fromCharCode(0xD83C, 0xDF74) + " ", "");
}
}
this.$('.pos-receipt-container').html(QWeb.render('PosTicket',{
widget:this,
order: order,
receipt: order.export_for_printing(),
orderlines: orderlines,
paymentlines: paymentlines
}));
}
});
});
......@@ -78,5 +78,10 @@
</t>
</t>
</t>
<t t-extend="TextInputPopupWidget">
<t t-jquery="p.title" t-operation="after">
<p class="body"><t t-esc=" widget.options.body || '' "/></p>
</t>
</t>
</templates>
......@@ -14,6 +14,7 @@ License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
<group string="Meal Vouchers">
<field name="max_meal_voucher_amount"/>
<field name="meal_voucher_display_product_screen"/>
<field name="meal_voucher_change_accepted"/>
</group>
</field>
</field>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment