diff --git a/pos_payment_terminal/models/__init__.py b/pos_payment_terminal/models/__init__.py index 3b0e3a1a..819b3c20 100644 --- a/pos_payment_terminal/models/__init__.py +++ b/pos_payment_terminal/models/__init__.py @@ -2,3 +2,4 @@ from . import pos_config from . import account_journal +from . import pos_order diff --git a/pos_payment_terminal/models/pos_order.py b/pos_payment_terminal/models/pos_order.py new file mode 100644 index 00000000..723a992b --- /dev/null +++ b/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 diff --git a/pos_payment_terminal/static/src/js/pos_payment_terminal.js b/pos_payment_terminal/static/src/js/pos_payment_terminal.js index ba99956a..adace081 100755 --- a/pos_payment_terminal/static/src/js/pos_payment_terminal.js +++ b/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; + } + }) }); diff --git a/pos_payment_terminal/tests/__init__.py b/pos_payment_terminal/tests/__init__.py new file mode 100644 index 00000000..64ce8822 --- /dev/null +++ b/pos_payment_terminal/tests/__init__.py @@ -0,0 +1 @@ +from . import test_transactions diff --git a/pos_payment_terminal/tests/test_transactions.py b/pos_payment_terminal/tests/test_transactions.py new file mode 100644 index 00000000..39597d12 --- /dev/null +++ b/pos_payment_terminal/tests/test_transactions.py @@ -0,0 +1,49 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2018-TODAY ACSONE SA/NV (). +# 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')