Commit 61c405a9 by Yvon Kerdoncuff

Merge branch '4822-mona-payment' into 'dev_cooperatic'

create module for mona & set eligible amount to payment line at mona payment & security checks

See merge request !61
parents 293ed1f3 d9246966
============================
La Cagette - MonA
============================
This module extends the Point of Sale Odoo module, as well as the POS Meal Voucher module.
MonA is the payment method of a Common Food Fund experimentation.
The products elligible are the same as the ones elligible to Meal Vouchers.
Configuration
=============
Payment Method
~~~~~~~~
Go to the payment methods and add/update the MonA payment method (needs to be done each time the module is uninstalled/reinstalled).
"Type" should be "Bank".
In the "Point of sale" tab, check the "Pay with MonA" checkbox.
Point of sale
~~~~~~~~
Go to your point of sale and click on the "Activate MonA payment" checkbox.
Add the MonA payment method to your point of sale.
# -*- coding: utf-8 -*-
{
"name": "La Cagette - MonA",
"summary": "Handle the relation with MonA (Caisse Alimentaire Commune / Common Food Fund). "
"This module changes information handled by the Pos Meal Voucher module (eg meal voucher eligible amount).",
"version": "1.0.0",
"category": "Point of Sale",
"author": "Damien Moulard",
"website": "https://lacagette-coop.fr",
"depends": [
"base",
"point_of_sale",
"pos_meal_voucher"
],
"data": [
"views/templates.xml",
"views/view_pos_config.xml",
"views/view_account_journal.xml",
],
"qweb": [
"static/src/xml/lacagette_mona.xml",
],
"installable": True,
}
from . import pos_config
from . import account_journal
from openerp import fields, models
class AccountJournal(models.Model):
_inherit = 'account.journal'
mona_used = fields.Boolean(
string="Paiement en MonA",
default=False
)
from openerp import fields, models
class PosConfig(models.Model):
_inherit = 'pos.config'
enable_mona = fields.Boolean(
string="Activer le paiement MonA",
default=False)
.payment-screen div.monA-summary{
border-top: dashed 1px gainsboro;
}
.payment-screen div.monA-summary table{
width: 100%;
}
.payment-screen div.monA-summary tbody{
background: white;
}
.payment-screen div.monA-summary th{
font-size: 16px;
padding: 8px;
text-align: center;
}
.payment-screen div.monA-summary td{
font-size: 22px;
padding: 8px 12px;
}
\ No newline at end of file
odoo.define("lacagette_mona.models", function (require) {
"use strict";
var models = require("point_of_sale.models");
var utils = require("web.utils");
var round_pr = utils.round_precision;
models.load_fields("account.journal", ["mona_used"]);
var OrderSuper = models.Order.prototype;
var Order = models.Order.extend({
get_total_mona_received: function(ignore_selected_line=false){
return round_pr(this.paymentlines.reduce((function(sum, paymentLine) {
if (paymentLine.is_mona() && (!ignore_selected_line || ignore_selected_line && !paymentLine.selected)) {
return sum + paymentLine.get_amount();
} else {
return sum;
}
}), 0), this.pos.currency.rounding);
},
get_total_mona_eligible_including_meal_vouchers: function() {
let total_meal_voucher_received = this.get_total_meal_voucher_received();
let mona_eligible = this.get_total_meal_voucher_eligible(); // eligible amount is the same as meal vouchers
// Since the same products are eligible for mona & meal vouchers, deduce one's eligible amount with the other's received amount
return round_pr(mona_eligible - total_meal_voucher_received, this.pos.currency.rounding);
},
get_total_meal_vouchers_eligible_including_mona: function() {
let total_mona_received = this.get_total_mona_received();
let meal_voucher_eligible = this.get_total_meal_voucher_eligible();
let meal_voucher_max_amount = this.pos.config.max_meal_voucher_amount;
return round_pr(Math.min(meal_voucher_eligible - total_mona_received, meal_voucher_max_amount), this.pos.currency.rounding);
},
});
models.Order = Order;
var PaymentlineSuper = models.Paymentline.prototype;
var Paymentline = models.Paymentline.extend({
is_mona: function() {
return this.cashregister.journal.mona_used;
},
});
models.Paymentline = Paymentline;
});
odoo.define("lacagette_mona.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.PaymentScreenWidget.include({
click_paymentmethods: function(id) {
var self = this;
var methods = this._super.apply(this, arguments); // NOTE: applying _super() before mona checks might cause side effects (payment terminal issue?)
var paymentScreen = this.pos.gui.current_screen;
var order = this.pos.get_order();
if (!order.selected_paymentline.is_meal_voucher() && order.selected_paymentline.cashregister.journal.mona_used) {
let total_mona_eligible = order.get_total_mona_eligible_including_meal_vouchers();
let mona_payment_amount = total_mona_eligible;
let total_mona_received_without_last_line = order.get_total_mona_received(true);
// Mona limit already reached
if (total_mona_received_without_last_line === total_mona_eligible) {
this.gui.show_popup("alert", {
'title': _t("Impossible de rajouter un paiement MonA"),
'body': _t("Le montant éligible a déjà été payé."),
cancel: function() {
self.remove_selected_paymentline(order);
}
});
return;
}
// if mona payment already exists & was less than total eligible
if (total_mona_received_without_last_line > 0 && total_mona_received_without_last_line < total_mona_eligible) {
mona_payment_amount = total_mona_eligible - total_mona_received_without_last_line;
}
/* Limit payment to remaining order due amount */
// simulate paid amount: last payment line replaced with mona amount
let total_before_last_line = round_pr(order.get_total_paid() - order.selected_paymentline.get_amount(), this.pos.currency.rounding);
let amount_paid_with_mona = round_pr(total_before_last_line + mona_payment_amount, this.pos.currency.rounding);
let order_total = order.get_total_with_tax();
// equivalent to check if mona payment amount > due amount. If so limit eligible amount to due amount.
if (amount_paid_with_mona > order_total) {
mona_payment_amount = round_pr(order_total - total_before_last_line, this.pos.currency.rounding);
}
order.selected_paymentline.set_amount(mona_payment_amount);
paymentScreen.order_changes();
paymentScreen.render_paymentlines();
paymentScreen.$(".paymentline.selected .edit").text(paymentScreen.format_currency_no_symbol(mona_payment_amount));
} else if (order.selected_paymentline.is_meal_voucher() && order.get_total_meal_vouchers_eligible_including_mona() == 0) {
this.gui.show_popup("alert", {
'title': _t("Impossible de payer en titre restaurant"),
'body': _t("Le montant éligible a déjà été payé en MonA."),
cancel: function() {
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 MonA summary
if (this.pos.config.enable_mona) {
let total_mona_received = order.get_total_mona_received();
let total_mona_eligible = order.get_total_mona_eligible_including_meal_vouchers();
let total_meal_vouchers_eligible = order.get_total_meal_vouchers_eligible_including_mona();
this.el.querySelector("#monA-eligible-amount").textContent = this.format_currency(total_mona_eligible);
this.el.querySelector("#meal-voucher-eligible-amount").textContent = this.format_currency(total_meal_vouchers_eligible);
this.el.querySelector("#monA-received-amount").textContent = this.format_currency(total_mona_received);
}
},
payment_input: function(input) {
this._super.apply(this, arguments);
var order = this.pos.get_order();
if (order.selected_paymentline && order.selected_paymentline.cashregister.journal.mona_used) {
var total_eligible = order.get_total_mona_eligible_including_meal_vouchers();
var total_received = order.get_total_mona_received();
let current_max = total_eligible;
// 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("Montant MonA 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 = "";
}
}
},
});
});
<?xml version="1.0" encoding="UTF-8"?>
<templates id="template" xml:space="preserve">
<t t-extend="PaymentScreen-Paymentmethods">
<t t-jquery="div.paymentmethods" t-operation="after">
<div t-if="widget.pos.config.enable_mona" class="monA-summary">
<table class="monA-summary">
<thead>
<tr>
<th colspan="2">MonA</th>
</tr>
</thead>
<tbody>
<tr>
<td>Total Eligible</td>
<td>
<span id="monA-eligible-amount">0.00 €</span>
</td>
</tr>
<tr>
<td>Total Received</td>
<td>
<span id="monA-received-amount">0.00 €</span>
</td>
</tr>
</tbody>
</table>
</div>
</t>
</t>
</templates>
<?xml version="1.0" encoding="UTF-8"?>
<openerp>
<template id="assets" inherit_id="point_of_sale.assets">
<xpath expr="." position="inside">
<link rel="stylesheet" href="/lacagette_mona/static/src/css/lacagette_mona.css"/>
<script type="text/javascript" src="/lacagette_mona/static/src/js/models.js"></script>
<script type="text/javascript" src="/lacagette_mona/static/src/js/screens.js"></script>
</xpath>
</template>
</openerp>
<?xml version="1.0" encoding="UTF-8"?>
<openerp>
<record id="view_account_journal" model="ir.ui.view">
<field name="model">account.journal</field>
<field name="inherit_id" ref="point_of_sale.view_account_journal_pos_user_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='amount_authorized_diff']/.." position="after">
<group name="mona_section" string="Caisse Alimentaire Commune">
<field name="mona_used"/>
</group>
</xpath>
</field>
</record>
</openerp>
<?xml version="1.0" encoding="UTF-8"?>
<openerp>
<record id="view_pos_config_form" model="ir.ui.view">
<field name="model">pos.config</field>
<field name="inherit_id" ref="point_of_sale.view_pos_config_form"/>
<field name="arch" type="xml">
<field name="journal_ids" position="after">
<group string="Caisse Alimentaire Commune">
<field name="enable_mona"/>
</group>
</field>
</field>
</record>
</openerp>
# Copyright (C) 2020 - Today: GRAP (http://www.grap.coop) # Copyright (C) 2020 - Today: GRAP (http://www.grap.coop)
# @author: Sylvain LE GAL (https://twitter.com/legalsylvain) # @author: Sylvain LE GAL (https://twitter.com/legalsylvain)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
# TODO: Rendre l'affichage MonA paramétrable
{ {
"name": "Point Of Sale - Meal Voucher", "name": "Point Of Sale - Meal Voucher",
"summary": "Handle meal vouchers in Point of Sale" "summary": "Handle meal vouchers in Point of Sale"
......
...@@ -318,7 +318,7 @@ msgstr " ) est inférieur à la valeur du/des ticket(s)( " ...@@ -318,7 +318,7 @@ msgstr " ) est inférieur à la valeur du/des ticket(s)( "
#: code:addons/pos_meal_voucher/static/src/js/screens.js:110 #: code:addons/pos_meal_voucher/static/src/js/screens.js:110
#, python-format #, python-format
msgid "Warning, the input amount of meal voucher is above the maximum amount of " 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 " msgstr "Le montant saisi est supérieur au montant maximum/au maximum éligible/au montant restant dû de "
#. module: pos_meal_voucher #. module: pos_meal_voucher
#. openerp-web #. openerp-web
......
...@@ -37,7 +37,7 @@ ...@@ -37,7 +37,7 @@
background: rgba(255,255,255,0.4); background: rgba(255,255,255,0.4);
} }
.payment-screen div.meal-voucher-summary, .payment-screen div.monA-summary{ .payment-screen div.meal-voucher-summary {
border-top: dashed 1px gainsboro; border-top: dashed 1px gainsboro;
} }
...@@ -45,20 +45,20 @@ ...@@ -45,20 +45,20 @@
color: red; color: red;
} }
.payment-screen div.meal-voucher-summary table, .payment-screen div.monA-summary table{ .payment-screen div.meal-voucher-summary table{
width: 100%; width: 100%;
} }
.payment-screen div.meal-voucher-summary tbody, .payment-screen div.monA-summary tbody{ .payment-screen div.meal-voucher-summary tbody{
background: white; background: white;
} }
.payment-screen div.meal-voucher-summary th , .payment-screen div.monA-summary th{ .payment-screen div.meal-voucher-summary th {
font-size: 16px; font-size: 16px;
padding: 8px; padding: 8px;
text-align: center; text-align: center;
} }
.payment-screen div.meal-voucher-summary td, .payment-screen div.monA-summary td{ .payment-screen div.meal-voucher-summary td {
font-size: 22px; font-size: 22px;
padding: 8px 12px; padding: 8px 12px;
} }
......
...@@ -12,6 +12,8 @@ odoo.define("pos_meal_voucher.screens", function (require) { ...@@ -12,6 +12,8 @@ odoo.define("pos_meal_voucher.screens", function (require) {
var QWeb = core.qweb; var QWeb = core.qweb;
var Model = require('web.DataModel'); var Model = require('web.DataModel');
var config_parameter = new Model('ir.config_parameter'); var config_parameter = new Model('ir.config_parameter');
var utils = require("web.utils");
var round_pr = utils.round_precision;
screens.ScreenWidget.include({ screens.ScreenWidget.include({
barcode_meal_voucher_payment_action: function (code) { barcode_meal_voucher_payment_action: function (code) {
...@@ -28,8 +30,16 @@ odoo.define("pos_meal_voucher.screens", function (require) { ...@@ -28,8 +30,16 @@ odoo.define("pos_meal_voucher.screens", function (require) {
var total_eligible = order.get_total_meal_voucher_eligible(); var total_eligible = order.get_total_meal_voucher_eligible();
var max_amount = this.pos.config.max_meal_voucher_amount; var max_amount = this.pos.config.max_meal_voucher_amount;
var total_received = order.get_total_meal_voucher_received() + 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 // Display info popup if amount is too high
if (total_received > 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) { if (total_received > total_eligible) {
this.gui.show_popup("alert", { this.gui.show_popup("alert", {
'title': _t("Impossible d'utiliser ce Titre Restaurant"), 'title': _t("Impossible d'utiliser ce Titre Restaurant"),
...@@ -243,18 +253,26 @@ odoo.define("pos_meal_voucher.screens", function (require) { ...@@ -243,18 +253,26 @@ odoo.define("pos_meal_voucher.screens", function (require) {
if (max_amount) { if (max_amount) {
current_max = Math.min(total_eligible, 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){ if (total_received > current_max){
this.gui.show_popup("alert", { this.gui.show_popup("alert", {
'title': _t("Meal Voucher Amount incorrect"), 'title': _t("Meal Voucher Amount incorrect"),
'body': _t("Warning, the input amount of meal voucher is above the maximum amount of ") + 'body': _t("Le montant saisi est supérieur au maximum éligible/au montant restant dû de ") +
this.format_currency(current_max), this.format_currency(current_max),
}); });
const max_curent_amount = current_max-total_received+order.selected_paymentline.get_amount() ; const max_current_amount = current_max-total_received+order.selected_paymentline.get_amount() ;
order.selected_paymentline.set_amount(max_curent_amount); order.selected_paymentline.set_amount(max_current_amount);
this.order_changes(); this.order_changes();
this.render_paymentlines(); this.render_paymentlines();
this.$('.paymentline.selected .edit').text(this.format_currency_no_symbol(max_curent_amount)); this.$('.paymentline.selected .edit').text(this.format_currency_no_symbol(max_current_amount));
this.inputbuffer = ""; this.inputbuffer = "";
} }
...@@ -311,12 +329,25 @@ odoo.define("pos_meal_voucher.screens", function (require) { ...@@ -311,12 +329,25 @@ odoo.define("pos_meal_voucher.screens", function (require) {
return false; return false;
} }
// if user choose card meal voucher // if user choose card meal voucher
if(order.selected_paymentline.is_meal_voucher() && order.selected_paymentline.is_dematerialized_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 // update selected (last) payment line & order with meal voucher data
function update_order_meal_voucher_data(issuer = '') { function update_order_meal_voucher_data(issuer = '') {
var total_eligible = order.get_total_meal_voucher_eligible(); 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 total_received = order.get_total_meal_voucher_received();
var max_amount = self.pos.config.max_meal_voucher_amount; var max_amount = self.pos.config.max_meal_voucher_amount;
var current_max = total_eligible; var current_max = total_eligible;
...@@ -326,14 +357,18 @@ odoo.define("pos_meal_voucher.screens", function (require) { ...@@ -326,14 +357,18 @@ odoo.define("pos_meal_voucher.screens", function (require) {
// Check how much is still possible to pay with meal voucher // 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 // 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(); 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_curent_amount); order.selected_paymentline.set_amount(max_current_amount);
order.selected_paymentline.set_meal_voucher_issuer(issuer); order.selected_paymentline.set_meal_voucher_issuer(issuer);
paymentScreen.order_changes(); paymentScreen.order_changes();
paymentScreen.render_paymentlines(); paymentScreen.render_paymentlines();
paymentScreen.$(".paymentline.selected .edit").text(paymentScreen.format_currency_no_symbol(max_curent_amount)); 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 required by the config, ask for the meal voucher issuer
...@@ -423,10 +458,6 @@ odoo.define("pos_meal_voucher.screens", function (require) { ...@@ -423,10 +458,6 @@ odoo.define("pos_meal_voucher.screens", function (require) {
if (!this.meal_voucher_activated()) { if (!this.meal_voucher_activated()) {
this.$(".meal-voucher-summary").addClass("oe_hidden"); this.$(".meal-voucher-summary").addClass("oe_hidden");
} }
// Update MonA summary
this.el.querySelector("#monA-eligible-amount").textContent = this.format_currency(total_eligible)
}, },
order_is_valid: function(force_validation) { order_is_valid: function(force_validation) {
......
...@@ -56,23 +56,6 @@ ...@@ -56,23 +56,6 @@
</tbody> </tbody>
</table> </table>
</div> </div>
<div class="monA-summary">
<table class="monA-summary">
<thead>
<tr>
<th colspan="2">MonA</th>
</tr>
</thead>
<tbody>
<tr>
<td>Total Eligible</td>
<td>
<span id="monA-eligible-amount">0.00 €</span>
</td>
</tr>
</tbody>
</table>
</div>
</t> </t>
</t> </t>
......
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