Browse Source

pos_payment_terminal: receive transaction refs

Provide a mechanism to send the order UID to the payment terminal,
which can then pass the transaction reference generated by the
payment provider back to Odoo, and which is then added to the
order payment lines. This allows their subsequent reconciliation.
The order is also automatically validated when the payment
finishes.
pull/389/head
andreparames 7 years ago
committed by Denis Roussel
parent
commit
45bdc705a0
  1. 1
      pos_payment_terminal/models/__init__.py
  2. 60
      pos_payment_terminal/models/pos_order.py
  3. 38
      pos_payment_terminal/static/src/js/pos_payment_terminal.js
  4. 1
      pos_payment_terminal/tests/__init__.py
  5. 49
      pos_payment_terminal/tests/test_transactions.py

1
pos_payment_terminal/models/__init__.py

@ -2,3 +2,4 @@
from . import pos_config
from . import account_journal
from . import pos_order

60
pos_payment_terminal/models/pos_order.py

@ -0,0 +1,60 @@
# -*- coding: utf-8 -*-
# © 2018 ACSONE SA/NV
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from collections import defaultdict
import logging
from odoo import models, api
_logger = logging.getLogger(__name__)
class PosOrder(models.Model):
_inherit = 'pos.order'
@api.model
def _match_transactions_to_payments(self, pos_order):
payments = pos_order['statement_ids']
transactions = pos_order['transactions']
card_journals = self.env['account.journal'].search([
('id', 'in', [p[2]['journal_id'] for p in payments]),
('payment_mode', '!=', False),
])
card_payments = [record[2] for record in payments
if record[2]['journal_id'] in card_journals.ids]
def amount_cents(obj):
if 'amount_cents' in obj:
return obj['amount_cents']
else:
return int(round(obj['amount'] * 100))
try:
for payment, transaction in match(card_payments, transactions,
key=amount_cents):
payment['note'] = transaction['reference']
except ValueError as e:
_logger.error("Error matching transactions to payments: %s",
e.args[0])
def _process_order(self, pos_order):
if pos_order.get('transactions'):
self._match_transactions_to_payments(pos_order)
return super(PosOrder, self)._process_order(pos_order)
def group_by(lists, key):
count = range(len(lists))
d = defaultdict(lambda: tuple([[] for _ in count]))
for i, objects in enumerate(lists):
for obj in objects:
d[key(obj)][i].append(obj)
return d
def match(al, bl, key):
for key, groups in group_by((al, bl), key).items():
if groups[0] and len(groups[0]) != len(groups[1]):
raise ValueError("Missing value for {!r}".format(key))
for val in zip(*groups):
yield val

38
pos_payment_terminal/static/src/js/pos_payment_terminal.js

@ -20,9 +20,33 @@ odoo.define('pos_payment_terminal.pos_payment_terminal', function (require) {
models.load_fields('account.journal', ['payment_mode']);
devices.ProxyDevice.include({
init: function(parents, options) {
var self = this;
self._super(parents, options);
self.on('change:status', this, function(eh, status) {
var drivers = status.newValue.drivers;
var order = self.pos.get_order();
Object.keys(drivers).forEach(function(driver_name) {
var transactions = drivers[driver_name].latest_transactions;
if(!!transactions && transactions.hasOwnProperty(order.uid)) {
order.transactions = transactions[order.uid];
var order_total = Math.round(order.get_total_with_tax() * 100.0);
var paid_total = order.transactions.map(function(t) {
return t.amount_cents;
}).reduce(function add(a, b) {
return a + b;
}, 0);
if(order_total === paid_total) {
self.pos.chrome.screens.payment.validate_order();
}
}
});
});
},
payment_terminal_transaction_start: function(line_cid, currency_iso, currency_decimals){
var line;
var lines = this.pos.get_order().get_paymentlines();
var order = this.pos.get_order();
var lines = order.get_paymentlines();
for ( var i = 0; i < lines.length; i++ ) {
if (lines[i].cid === line_cid) {
line = lines[i];
@ -32,7 +56,8 @@ odoo.define('pos_payment_terminal.pos_payment_terminal', function (require) {
var data = {'amount' : line.get_amount(),
'currency_iso' : currency_iso,
'currency_decimals' : currency_decimals,
'payment_mode' : line.cashregister.journal.payment_mode};
'payment_mode' : line.cashregister.journal.payment_mode,
'order_id': order.uid};
//console.log(JSON.stringify(data));
this.message('payment_terminal_transaction_start', {'payment_info' : JSON.stringify(data)});
},
@ -50,4 +75,13 @@ odoo.define('pos_payment_terminal.pos_payment_terminal', function (require) {
});
},
});
var _orderproto = models.Order.prototype;
models.Order = models.Order.extend({
export_as_JSON: function() {
var vals = _orderproto.export_as_JSON.apply(this, arguments);
vals['transactions'] = this.transactions || {};
return vals;
}
})
});

1
pos_payment_terminal/tests/__init__.py

@ -0,0 +1 @@
from . import test_transactions

49
pos_payment_terminal/tests/test_transactions.py

@ -0,0 +1,49 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2018-TODAY ACSONE SA/NV (<https://www.acsone.eu>).
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
import odoo
class TestTransactions(odoo.tests.TransactionCase):
def test_matching(self):
card_journal_id = self.env['account.journal'].create({
'name': 'Card Journal',
'code': 'CARD',
'type': 'bank',
'payment_mode': 'card',
}).id
cash_journal_id = 0
pos_order = {
'statement_ids': [
(0, 0, {
'name': 'Payment1',
'amount': 45.2,
'journal_id': card_journal_id,
}),
(0, 0, {
'name': 'Payment2',
'amount': 10.5,
'journal_id': card_journal_id,
}),
(0, 0, {
'name': 'Payment3',
'amount': 22.0,
'journal_id': cash_journal_id,
}),
],
'transactions': [
{
'reference': 'ABCDE',
'amount_cents': 1050,
},
{
'reference': 'XPTO',
'amount_cents': 4520,
},
]
}
self.env['pos.order']._match_transactions_to_payments(pos_order)
self.assertEquals(pos_order['statement_ids'][0][2]['note'], 'XPTO')
self.assertEquals(pos_order['statement_ids'][1][2]['note'], 'ABCDE')
Loading…
Cancel
Save