Skip to content

Commit 19d5e54

Browse files
maq-adhocaugusto-weiss
authored andcommitted
[ADD] add new module account_payment_multi to create payment links for multiples invoices
closes #370 X-original-commit: 157c914 Signed-off-by: augusto-weiss <[email protected]>
1 parent 8c7c3c3 commit 19d5e54

12 files changed

+461
-0
lines changed

account_payment_multi/RADME.rst

+71
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
.. |company| replace:: ADHOC SA
2+
3+
.. |company_logo| image:: https://raw.githubusercontent.com/ingadhoc/maintainer-tools/master/resources/adhoc-logo.png
4+
:alt: ADHOC SA
5+
:target: https://www.adhoc.com.ar
6+
7+
.. |icon| image:: https://raw.githubusercontent.com/ingadhoc/maintainer-tools/master/resources/adhoc-icon.png
8+
9+
.. image:: https://img.shields.io/badge/license-AGPL--3-blue.png
10+
:target: https://www.gnu.org/licenses/agpl
11+
:alt: License: AGPL-3
12+
13+
==============
14+
account_payment_multi
15+
==============
16+
17+
This module extends the online invoice payment functionality by allowing you to generate a singlep ayment links to pay multiple invoices.
18+
It is available both from the backend and from the customer portal.
19+
20+
Installation
21+
============
22+
23+
To install this module, you need to:
24+
25+
#. Do this ...
26+
27+
Configuration
28+
=============
29+
30+
To configure this module, you need to:
31+
32+
#. Go to ...
33+
34+
Usage
35+
=====
36+
37+
To use this module, you need to:
38+
39+
#. Go to ...
40+
41+
.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas
42+
:alt: Try me on Runbot
43+
:target: http://runbot.adhoc.com.ar/
44+
45+
Bug Tracker
46+
===========
47+
48+
Bugs are tracked on `GitHub Issues
49+
<https://github.com/ingadhoc/account_payment/issues>`_. In case of trouble, please
50+
check there if your issue has already been reported. If you spotted it first,
51+
help us smashing it by providing a detailed and welcomed feedback.
52+
53+
Credits
54+
=======
55+
56+
Images
57+
------
58+
59+
* |company| |icon|
60+
61+
Contributors
62+
------------
63+
64+
Maintainer
65+
----------
66+
67+
|company_logo|
68+
69+
This module is maintained by the |company|.
70+
71+
To contribute to this module, please visit https://www.adhoc.com.ar.

account_payment_multi/__init__.py

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
from . import controllers
2+
from . import wizards

account_payment_multi/__manifest__.py

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{
2+
'name': "Account payment multi",
3+
'description': """
4+
Allows payment multiple invoices with a single payment link
5+
""",
6+
'author': 'ADHOC SA',
7+
'website': "https://www.adhoc.com.ar",
8+
'category': 'Technical',
9+
'version': "15.0.1.0.0",
10+
'depends': ['account_payment'],
11+
'license': 'LGPL-3',
12+
'images': [
13+
],
14+
'installable': True,
15+
'assets': {
16+
'web.assets_frontend': [
17+
'account_payment_multi/static/src/js/payment_form.js',
18+
'account_payment_multi/static/src/js/payment_multi.js',
19+
],
20+
},
21+
'data': [
22+
'views/payment_templates.xml',
23+
'views/account_portal_templates.xml',
24+
'wizards/payment_link_wizard_views.xml',
25+
],
26+
'demo': [
27+
],
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from . import portal
+101
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
##############################################################################
2+
# For copyright and license notices, see __manifest__.py file in module root
3+
# directory
4+
##############################################################################
5+
6+
from odoo import _, http
7+
from odoo.exceptions import ValidationError
8+
from odoo.fields import Command
9+
from odoo.http import request
10+
11+
from odoo.addons.portal.controllers import portal
12+
from odoo.tools import float_compare
13+
14+
15+
class PaymentPortal(portal.CustomerPortal):
16+
17+
@http.route('/payment/invoice_multi_link', type='json', auth='user')
18+
def invoice_multi_transaction(self, invoice_ids, amount, **kwargs):
19+
invoices_sudo = request.env['account.move'].sudo()
20+
for invoice in invoice_ids:
21+
# Check the invoice id
22+
invoices_sudo += self._document_check_access('account.move', invoice['id'], invoice.get('token'))
23+
payment_link = request.env['payment.link.wizard'].sudo().with_context(active_id=invoices_sudo[0].id, active_ids=invoices_sudo.ids, active_model='account.move').create({})
24+
25+
if float_compare(payment_link.amount_max, amount, precision_rounding=payment_link.currency_id.rounding or 0.01) == -1:
26+
raise ValidationError(_("Incorrect amount"))
27+
return payment_link.link
28+
29+
@http.route()
30+
def payment_pay(
31+
self, reference=None, amount=None, currency_id=None, partner_id=None, company_id=None,
32+
acquirer_id=None, access_token=None, invoice_id=None, **kwargs
33+
):
34+
35+
amount = self._cast_as_float(amount)
36+
if 'invoice_ids' in kwargs:
37+
invoice_ids = [int(x) for x in kwargs['invoice_ids'].split(',') if x.isdigit()]
38+
invoices_sudo = request.env['account.move'].sudo().browse(invoice_ids).exists()
39+
if not invoices_sudo:
40+
raise ValidationError(_("The provided parameters are invalid."))
41+
if len(invoices_sudo.mapped('commercial_partner_id')) > 1:
42+
raise ValidationError(_("Only pay invoices from the same customer."))
43+
if len(invoices_sudo.mapped('currency_id')) > 1:
44+
raise ValidationError(_("Only pay invoices from the same currency."))
45+
if len(invoices_sudo.mapped('company_id')) > 1:
46+
raise ValidationError(_("Only pay invoices from the same company."))
47+
first_invoice_sudo = invoices_sudo[0]
48+
# Check the access token against the invoice values. Done after fetching the invoice
49+
# as we need the invoice fields to check the access token.
50+
# if not payment_utils.check_access_token(
51+
# access_token, first_invoice_sudo.partner_id.id, amount, first_invoice_sudo.currency_id.id
52+
# ):
53+
# raise ValidationError(_("The provided parameters are invalid."))
54+
currency_id = first_invoice_sudo.currency_id.id
55+
partner_id = first_invoice_sudo.commercial_partner_id.id
56+
company_id = first_invoice_sudo.company_id.id
57+
58+
kwargs.update({
59+
'invoice_ids': invoice_ids,
60+
})
61+
return super().payment_pay(
62+
reference=reference, amount=amount, currency_id=currency_id, partner_id=partner_id, company_id=company_id,
63+
acquirer_id=acquirer_id, access_token=access_token, invoice_id=invoice_id, **kwargs
64+
65+
)
66+
67+
def _get_custom_rendering_context_values(self, invoice_ids=None, **kwargs):
68+
""" Override of `payment` to add the invoice id in the custom rendering context values.
69+
70+
:param int invoice_id: The invoice for which a payment id made, as an `account.move` id.
71+
:param dict kwargs: Optional data. This parameter is not used here.
72+
:return: The extended rendering context values.
73+
:rtype: dict
74+
"""
75+
rendering_context_values = super()._get_custom_rendering_context_values(**kwargs)
76+
if invoice_ids:
77+
rendering_context_values['invoice_ids'] = invoice_ids
78+
79+
# Interrupt the payment flow if the invoice has been canceled.
80+
# invoice_sudo = request.env['account.move'].sudo().browse(invoice_ids)
81+
#if invoice_sudo.state == 'cancel':
82+
# rendering_context_values['amount'] = 0.0
83+
return rendering_context_values
84+
85+
def _create_transaction(self, *args, invoice_ids=None, custom_create_values=None, **kwargs):
86+
""" Override of `payment` to add the invoice id in the custom create values.
87+
88+
:param int invoice_id: The invoice for which a payment id made, as an `account.move` id.
89+
:param dict custom_create_values: Additional create values overwriting the default ones.
90+
:param dict kwargs: Optional data. This parameter is not used here.
91+
:return: The result of the parent method.
92+
:rtype: recordset of `payment.transaction`
93+
"""
94+
##import pdb; pdb.set_trace()
95+
if invoice_ids:
96+
if custom_create_values is None:
97+
custom_create_values = {}
98+
custom_create_values['invoice_ids'] = [Command.set(invoice_ids)]
99+
return super()._create_transaction(
100+
*args, custom_create_values=custom_create_values, **kwargs
101+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
odoo.define('account_payment_multi.payment_form', require => {
2+
'use strict';
3+
4+
const checkoutForm = require('payment.checkout_form');
5+
const manageForm = require('payment.manage_form');
6+
7+
const PaymentMixin = {
8+
9+
//--------------------------------------------------------------------------
10+
// Private
11+
//--------------------------------------------------------------------------
12+
13+
/**
14+
* Add `invoice_id` to the transaction route params if it is provided.
15+
*
16+
* @override method from payment.payment_form_mixin
17+
* @private
18+
* @param {string} code - The provider code of the selected payment option.
19+
* @param {number} paymentOptionId - The id of the selected payment option.
20+
* @param {string} flow - The online payment flow of the selected payment option.
21+
* @return {object} The extended transaction route params.
22+
*/
23+
_prepareTransactionRouteParams: function (code, paymentOptionId, flow) {
24+
const transactionRouteParams = this._super(...arguments);
25+
return {
26+
...transactionRouteParams,
27+
'invoice_ids': this.txContext.invoiceIds ? this.txContext.invoiceIds : null,
28+
};
29+
},
30+
31+
};
32+
33+
checkoutForm.include(PaymentMixin);
34+
manageForm.include(PaymentMixin);
35+
36+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
odoo.define('account_payment.multi', function (require) {
2+
'use strict';
3+
4+
const core = require('web.core');
5+
const publicWidget = require('web.public.widget');
6+
const Dialog = require('web.Dialog');
7+
const _t = core._t;
8+
9+
publicWidget.registry.AccountPaymentWidget = publicWidget.Widget.extend({
10+
selector: '.payment_multi_table',
11+
events: {
12+
'change .checkbox_amount_residual': '_onChangeCheckboxAmountResidual',
13+
'click .oe_multi_pay_now': '_onPayNowBtnClick',
14+
},
15+
init: function () {
16+
this._super.apply(this, arguments);
17+
//this._computeAmount();
18+
},
19+
_onChangeCheckboxAmountResidual: function(event) {
20+
this._computeAmount()
21+
},
22+
_computeAmount: function(){
23+
var items = this.el.getElementsByClassName('checkbox_amount_residual');
24+
let total = 0;
25+
let currency = false;
26+
let old_group = false;
27+
let group = false;
28+
console.log(group);
29+
for (let i = 0; i < items.length; i++) {
30+
if (items[i].checked){
31+
old_group = group;
32+
group = items[i].dataset.invoiceGroup;
33+
currency = items[i].dataset.currencyName;
34+
if (old_group && group != old_group){
35+
items[i].checked = false;
36+
return new Dialog(null, {
37+
title: _t("Error in selection"),
38+
size: 'medium',
39+
$content: _t(`<p>selected invoices must be in the same currency, partner and company</p>`),
40+
buttons: [{text: _t("Ok"), close: true}]
41+
}).open();
42+
43+
}
44+
total = total + parseFloat(items[i].dataset.amountResidual);
45+
}
46+
}
47+
this.el.querySelectorAll('.oe_amount').forEach((selector) =>selector.innerHTML=currency + ' ' + total.toFixed(2));
48+
if (total){
49+
this.el.querySelectorAll('.multi_payment_selector').forEach((selector) =>selector.classList.remove('invisible'));
50+
} else {
51+
this.el.querySelectorAll('.multi_payment_selector').forEach((selector) =>selector.classList.add('invisible'));
52+
}
53+
},
54+
_onPayNowBtnClick: function(event){
55+
var items = this.el.getElementsByClassName('checkbox_amount_residual');
56+
let total = 0;
57+
let invoices = [];
58+
for (let i = 0; i < items.length; i++) {
59+
if (items[i].checked){
60+
total = total + parseFloat(items[i].dataset.amountResidual);
61+
invoices.push({id : parseInt(items[i].dataset.invoiceId), token: items[i].dataset.accessToken})
62+
}
63+
}
64+
let params = {invoice_ids: invoices, amount: total}
65+
return this._rpc({
66+
route: "/payment/invoice_multi_link",
67+
params: params,
68+
}).then(async data => {
69+
window.location = data;
70+
});
71+
72+
}
73+
});
74+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<odoo>
3+
4+
<template id="portal_my_invoices_payment" name="Payment on My Invoices" inherit_id="account.portal_my_invoices">
5+
<xpath expr="//t[@t-call='portal.portal_table']/thead/tr/th[1]" position="before">
6+
<th></th>
7+
</xpath>
8+
<xpath expr="//t[@t-foreach='invoices']/tr/td[1]" position="before">
9+
<td class="text-center">
10+
<input
11+
id="pay_amount_residual"
12+
t-att-data-invoice-group = "'%s-%s-%s' % (invoice.currency_id.id, invoice.commercial_partner_id.id , invoice.company_id.id)"
13+
t-att-data-invoice-id = "invoice.id"
14+
t-att-data-currency-name = "invoice.currency_id.name"
15+
t-att-data-access-token = "invoice.access_token"
16+
t-att-data-amount-residual = "invoice.amount_residual"
17+
type="checkbox"
18+
t-att-class="'checkbox_amount_residual'"
19+
t-if="invoice.state == 'posted' and invoice.payment_state in ('not_paid', 'partial') and invoice.amount_total and invoice.move_type == 'out_invoice'"/>
20+
</td>
21+
</xpath>
22+
<xpath expr="//t[@t-foreach='invoices']" position="before">
23+
<tr>
24+
<td class="text-center"><i class="fa fa-arrow-down"></i></td>
25+
<td><strong class="oe_zeroAmount">Select invoices to pay</strong></td>
26+
<td colspan="10">
27+
<span class="multi_payment_selector invisible">
28+
<a href="#" title="Pay Now" aria-label="Pay now" class="btn btn-sm btn-primary oe_multi_pay_now" role="button">
29+
<i class="fa fa-arrow-circle-right"/><span class='d-none d-md-inline'> Pay <span class="oe_amount"></span> Now</span>
30+
</a>
31+
</span>
32+
</td>
33+
</tr>
34+
</xpath>
35+
36+
<xpath expr="//t[@t-foreach='invoices']" position="after">
37+
<tr>
38+
<td class="text-center"></td>
39+
<td></td>
40+
<td colspan="10">
41+
<span class="multi_payment_selector invisible">
42+
<a href="#" title="Pay Now" aria-label="Pay now" class="btn btn-sm btn-primary oe_multi_pay_now" role="button">
43+
<i class="fa fa-arrow-circle-right"/><span class='d-none d-md-inline'> Pay <span class="oe_amount"></span> Now</span>
44+
</a>
45+
</span>
46+
</td>
47+
</tr>
48+
</xpath>
49+
<t t-call="portal.portal_table" position="before">
50+
<t t-set="classes" t-value="'payment_multi_table'"/>
51+
</t>
52+
53+
</template>
54+
55+
</odoo>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<odoo>
3+
<template id="payment_checkout_inherit" inherit_id="payment.checkout">
4+
<xpath expr="//form[@name='o_payment_checkout']" position="attributes">
5+
<attribute name="t-att-data-invoice-ids">invoice_ids</attribute>
6+
</xpath>
7+
</template>
8+
9+
<template id="payment_manage_inherit" inherit_id="payment.manage">
10+
<xpath expr="//form[@name='o_payment_manage']" position="attributes">
11+
<attribute name="t-att-data-invoice-ids">invoice_ids</attribute>
12+
</xpath>
13+
</template>
14+
</odoo>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from . import payment_link_wizard

0 commit comments

Comments
 (0)