account_payment_transfer_reconcile_batch (#226)

[ADD] account_payment_transfer_reconcile_batch

=================================================================
Batch reconciliation for transfer lines created in payment orders
=================================================================

This module allows to process with the connector technology the heavy task of
reconciliation of the receivable/payable journal entries of a payment order
against the created entries in transfer accounts.

This approach provides many advantages, similar to the ones we get
using that connector for e-commerce:

- Asynchronous: the operation is done in background, and users can
  continue to work.
- Dedicated workers: the queued jobs are performed by specific workers
  (processes). This is good for a long task, since the main workers are
  busy handling HTTP requests and can be killed if operations take
  too long, for example.
- Multiple transactions: this is an operation that doesn't need to be
  atomic, and if a line out of 100,000 fails, it is possible to catch
  it, see the error message, and fix the situation. Meanwhile, all
  other jobs can proceed.

Inspired on *account_move_batch_validate* module from Camptocamp and ACSONE.

Installation
============

This module requires the connector module, hosted on
`OCA/connector <https://github.com/OCA/connector>`_

Configuration
=============

This will only work for payment modes that have a transfer account set.

Usage
=====

When exporting the payment order, click on *Validate* to generate the transfer
move. One connector job will be created for each payment line for a deferred
conciliation of this line.
This commit is contained in:
Pedro M. Baeza
2016-08-28 14:11:57 +02:00
committed by Sergio Teruel Albert
parent 18d8cf674a
commit 319790b5a9
9 changed files with 265 additions and 0 deletions

View File

@@ -0,0 +1,80 @@
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License AGPL-3
=================================================================
Batch reconciliation for transfer lines created in payment orders
=================================================================
This module allows to process with the connector technology the heavy task of
reconciliation of the receivable/payable journal entries of a payment order
against the created entries in transfer accounts.
This approach provides many advantages, similar to the ones we get
using that connector for e-commerce:
- Asynchronous: the operation is done in background, and users can
continue to work.
- Dedicated workers: the queued jobs are performed by specific workers
(processes). This is good for a long task, since the main workers are
busy handling HTTP requests and can be killed if operations take
too long, for example.
- Multiple transactions: this is an operation that doesn't need to be
atomic, and if a line out of 100,000 fails, it is possible to catch
it, see the error message, and fix the situation. Meanwhile, all
other jobs can proceed.
Inspired on *account_move_batch_validate* module from Camptocamp and ACSONE.
Installation
============
This module requires the *connector* module, hosted on
`OCA/connector <https://github.com/OCA/connector>`_
Configuration
=============
This will only work for payment modes that have a transfer account set.
Usage
=====
When exporting the payment order, click on *Validate* to generate the transfer
move. One connector job will be created for each payment line for a deferred
conciliation of this line.
.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas
:alt: Try me on Runbot
:target: https://runbot.odoo-community.org/runbot/173/8.0
Bug Tracker
===========
Bugs are tracked on `GitHub Issues
<https://github.com/OCA/bank-payment/issues>`_. In case of trouble, please
check there if your issue has already been reported. If you spotted it first,
help us smashing it by providing a detailed and welcomed feedback.
Credits
=======
Contributors
------------
* Pedro M. Baeza <pedro.baeza@tecnativa.com>
Maintainer
----------
.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: http://odoo-community.org
This module is maintained by the OCA.
OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.
To contribute to this module, please visit https://odoo-community.org.

View File

@@ -0,0 +1,5 @@
# -*- coding: utf-8 -*-
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html
from . import models
from . import wizard

View File

@@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Copyright 2015-2016 Pedro M. Baeza <pedro.baeza@tecnativa.com>
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html
{
'name': "Batch Reconciliation for transfer moves",
'version': '8.0.1.0.0',
'author': "Tecnativa, "
"Odoo Community Association (OCA)",
'license': 'AGPL-3',
'category': 'Accounting & Finance',
'depends': [
'account_banking_payment_transfer',
'connector',
],
'website': 'https://www.tecnativa.com',
'data': [
],
'installable': True,
}

View File

@@ -0,0 +1,5 @@
# -*- coding: utf-8 -*-
# © 2015 Serv. Tecnol. Avanzados - Pedro M. Baeza
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html
from . import payment_order

View File

@@ -0,0 +1,46 @@
# -*- coding: utf-8 -*-
# © 2015-2016 Pedro M. Baeza <pedro.baeza@tecnativa.com>
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html
import logging
from openerp import models, api, _
from openerp.tools import config
_logger = logging.getLogger(__name__)
try:
from openerp.addons.connector.queue.job import job
from openerp.addons.connector.session import ConnectorSession
except ImportError:
_logger.debug('Can not `import connector`.')
import functools
def empty_decorator_factory(*argv, **kwargs):
return functools.partial
job = empty_decorator_factory
class PaymentOrder(models.Model):
_inherit = 'payment.order'
@api.multi
def _reconcile_payment_lines(self, bank_payment_lines):
test_condition = (config['test_enable'] and
not self.env.context.get('test_connector'))
if test_condition or self.env.context.get('no_connector'):
return super(PaymentOrder, self)._reconcile_payment_lines(
bank_payment_lines)
session = ConnectorSession.from_env(self.env)
for bline in bank_payment_lines:
reconcile_one_move.delay(session, bline._name, bline.id)
@job(default_channel='root.account_payment_transfer_reconcile_batch')
def reconcile_one_move(session, model_name, bank_payment_line_id):
bline_model = session.env[model_name]
bline = bline_model.browse(bank_payment_line_id)
if bline.exists():
obj = session.env['payment.order'].with_context(no_connector=True)
obj._reconcile_payment_lines(bline)
else:
return _(u'Nothing to do because the record has been deleted')

View File

@@ -0,0 +1,4 @@
# -*- coding: utf-8 -*-
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html
from . import test_account_payment_transfer_reconcile_batch

View File

@@ -0,0 +1,68 @@
# -*- coding: utf-8 -*-
# © 2016 Pedro M. Baeza <pedro.baeza@tecnativa.com>
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html
from openerp.tests import common
class TestAccountPaymentTransferReconcileBatch(common.TransactionCase):
def setUp(self):
super(TestAccountPaymentTransferReconcileBatch, self).setUp()
self.journal = self.env['account.journal'].create({
'name': 'Test journal',
'type': 'general',
'code': 'TEST',
})
self.bank_account = self.env['res.partner.bank'].create({
'state': 'bank',
'acc_number': 'TEST',
})
self.partner = self.env['res.partner'].create({
'name': 'Test partner',
'supplier': True,
})
self.mode = self.env['payment.mode'].create({
'name': 'Test payment mode',
'journal': self.journal.id,
'bank_id': self.bank_account.id,
'transfer_journal_id': self.journal.id,
'transfer_account_id': self.partner.property_account_payable.id,
'type': self.env.ref(
'account_banking_payment_export.manual_bank_tranfer').id,
})
self.product = self.env['product.product'].create({
'name': 'Test product',
})
self.invoice = self.env['account.invoice'].create({
'type': 'in_invoice',
'partner_id': self.partner.id,
'account_id': self.partner.property_account_payable.id,
'invoice_line': [
(0, 0, {
'product_id': self.product.id,
'name': self.product.name,
'price_unit': 20,
}),
]
})
self.invoice.signal_workflow('invoice_open')
self.payment_order = self.env['payment.order'].create({
'mode': self.mode.id,
})
line = self.invoice.move_id.line_id.filtered(
lambda x: x.account_id == self.invoice.account_id)
wizard = self.env['payment.order.create'].with_context(
active_model='payment.order', active_id=self.payment_order.id
).create({})
line_vals = wizard._prepare_payment_line(self.payment_order, line)
self.payment_line = self.env['payment.line'].create(line_vals)
def test_enqueue(self):
self.payment_order.signal_workflow('open')
self.payment_order.action_open()
self.payment_order.with_context(test_connector=True).action_sent()
func = "openerp.addons.account_payment_transfer_reconcile_batch." \
"models.payment_order.reconcile_one_move('bank.payment.line', "
job = self.env['queue.job'].sudo().search(
[('func_string', 'like', "%s%%" % func)])
self.assertTrue(job)

View File

@@ -0,0 +1,4 @@
# -*- coding: utf-8 -*-
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html
from . import payment_order_create

View File

@@ -0,0 +1,33 @@
# -*- coding: utf-8 -*-
# © 2016 Pedro M. Baeza <pedro.baeza@tecnativa.com>
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html
from openerp import api, models
class PaymentOrderCreate(models.TransientModel):
_inherit = 'payment.order.create'
@api.multi
def filter_lines(self, lines):
"""Filter move lines before proposing them for inclusion in the payment
order (inherited). This one removes the move lines that aren't still
being processed in the connector queue.
:param lines: recordset of move lines
:returns: list of move line ids
"""
filtered_line_ids = super(PaymentOrderCreate, self).filter_lines(lines)
func = "openerp.addons.account_payment_transfer_reconcile_batch." \
"models.payment_order.reconcile_one_move('bank.payment.line', "
jobs = self.env['queue.job'].sudo().search(
[('func_string', 'like', "%s%%" % func), ('state', '!=', 'done')])
if not jobs:
return filtered_line_ids
pline_ids = jobs.mapped(lambda x: int(x.func_string[len(func):-1]))
# With this, we remove non existing records
plines = self.env['bank.payment.line'].search(
[('id', 'in', pline_ids)])
to_exclude = plines.mapped('payment_line_ids.move_line_id')
return [line_id for line_id in filtered_line_ids if
line_id not in to_exclude.ids]