From 0a572de67c2bbf6551fe7d7d0efa0a5e8247e8a6 Mon Sep 17 00:00:00 2001 From: "Pedro M. Baeza" Date: Fri, 7 Aug 2015 18:05:03 +0200 Subject: [PATCH] [ADD] account_netting: AR/AP netting AR/AP netting ============= This module allows to compensate the balance of a receivable account with the balance of a payable account for the same partner, creating a journal item that reflects this operation. **WARNING**: This operation can be forbidden in your country by the accounting regulations, so you should check current laws before using it. For example, in Spain, this is not allowed at first instance, unless you document well the operation from both parties. Usage ===== From any account journal entries view: * Accounting/Journal Entries/Journal Items * Accounting/Periodic Processing/Reconciliation/Manual Reconciliation select all the lines that corresponds to both AR/AP operations from the same partner. Click on "More > Compensate". If the items don't correspond to the same partner or they aren't AR/AP accounts, you will get an error. On contrary, a dialog box will be presented with the result of the operation and a selection of the journal to register the operation. When you click on the "Compensate" button, a journal entry is created with the corresponding counterparts of the AR/AP operations. --- account_netting/README.rst | 76 ++++++++++ account_netting/__init__.py | 5 + account_netting/__openerp__.py | 20 +++ account_netting/i18n/es.po | 133 ++++++++++++++++++ account_netting/static/description/icon.png | Bin 0 -> 9455 bytes account_netting/tests/__init__.py | 5 + account_netting/tests/test_account_netting.py | 77 ++++++++++ account_netting/wizard/__init__.py | 5 + .../wizard/account_move_make_netting.py | 115 +++++++++++++++ .../wizard/account_move_make_netting_view.xml | 37 +++++ 10 files changed, 473 insertions(+) create mode 100644 account_netting/README.rst create mode 100644 account_netting/__init__.py create mode 100644 account_netting/__openerp__.py create mode 100644 account_netting/i18n/es.po create mode 100644 account_netting/static/description/icon.png create mode 100644 account_netting/tests/__init__.py create mode 100644 account_netting/tests/test_account_netting.py create mode 100644 account_netting/wizard/__init__.py create mode 100644 account_netting/wizard/account_move_make_netting.py create mode 100644 account_netting/wizard/account_move_make_netting_view.xml diff --git a/account_netting/README.rst b/account_netting/README.rst new file mode 100644 index 000000000..73cf5f783 --- /dev/null +++ b/account_netting/README.rst @@ -0,0 +1,76 @@ +.. 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 + +============= +AR/AP netting +============= + +This module allows to compensate the balance of a receivable account with the +balance of a payable account for the same partner, creating a journal item +that reflects this operation. + +**WARNING**: This operation can be forbidden in your country by the accounting +regulations, so you should check current laws before using it. For example, in +Spain, this is not allowed at first instance, unless you document well the +operation from both parties. + +Usage +===== + +From any account journal entries view: + +* Accounting/Journal Entries/Journal Items +* Accounting/Periodic Processing/Reconciliation/Manual Reconciliation + +select all the lines that corresponds to both AR/AP operations from the same +partner. Click on "More > Compensate". If the items don't correspond to the +same partner or they aren't AR/AP accounts, you will get an error. + +On contrary, a dialog box will be presented with the result of the operation +and a selection of the journal to register the operation. When you click on the +"Compensate" button, a journal entry is created with the corresponding +counterparts of the AR/AP operations. + + +.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas + :alt: Try me on Runbot + :target: https://runbot.odoo-community.org/runbot/92/8.0 + +Known issues / Roadmap +====================== + +* We can add the possibility to pay the netting result amount directly from + the wizard. + +Bug Tracker +=========== + +Bugs are tracked on `GitHub 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 +`here `_. + + +Credits +======= + +Contributors +------------ + +* Pedro M. Baeza + +Maintainer +---------- + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://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 http://odoo-community.org. diff --git a/account_netting/__init__.py b/account_netting/__init__.py new file mode 100644 index 000000000..9b0f517f5 --- /dev/null +++ b/account_netting/__init__.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- +# (c) 2015 Pedro M. Baeza +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html + +from . import wizard diff --git a/account_netting/__openerp__.py b/account_netting/__openerp__.py new file mode 100644 index 000000000..7f4bafe60 --- /dev/null +++ b/account_netting/__openerp__.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# (c) 2015 Pedro M. Baeza +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html + +{ + 'name': 'Account netting', + 'version': '8.0.1.0.0', + 'summary': "Compensate AR/AP accounts from the same partner", + 'category': 'Accounting & Finance', + 'author': 'Serv. Tecnol. Avanzados - Pedro M. Baeza, ' + 'Odoo Community Association (OCA)', + 'website': 'https://github.com/OCA/account-financial-tools', + 'depends': [ + 'account', + ], + 'data': [ + 'wizard/account_move_make_netting_view.xml', + ], + "installable": True, +} diff --git a/account_netting/i18n/es.po b/account_netting/i18n/es.po new file mode 100644 index 000000000..9c6e19e27 --- /dev/null +++ b/account_netting/i18n/es.po @@ -0,0 +1,133 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * account_netting +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 8.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2015-08-07 15:54+0000\n" +"PO-Revision-Date: 2015-08-07 15:54+0000\n" +"Last-Translator: <>\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: account_netting +#: code:addons/account_netting/wizard/account_move_make_netting.py:56 +#, python-format +msgid "AR/AP netting" +msgstr "Compensación a cobrar/a pagar" + +#. module: account_netting +#: code:addons/account_netting/wizard/account_move_make_netting.py:30 +#, python-format +msgid "All entries must have a receivable or payable account" +msgstr "Todos los apuntes deben tener una cuenta a pagar o a cobrar" + +#. module: account_netting +#: code:addons/account_netting/wizard/account_move_make_netting.py:33 +#, python-format +msgid "All entries mustn't been reconciled" +msgstr "Ningún apunte debe estar conciliado" + +#. module: account_netting +#: code:addons/account_netting/wizard/account_move_make_netting.py:39 +#, python-format +msgid "All entries should have a partner and the partner must be the same for all." +msgstr "Todos los apuntes deben tener una empresa y la empresa debe ser la misma para todos." + +#. module: account_netting +#: field:account.move.make.netting,balance:0 +msgid "Balance" +msgstr "Saldo" + +#. module: account_netting +#: field:account.move.make.netting,balance_type:0 +msgid "Balance type" +msgstr "Tipo de saldo" + +#. module: account_netting +#: view:account.move.make.netting:account_netting.view_account_move_make_netting_form +msgid "Cancel" +msgstr "Cancelar" + +#. module: account_netting +#: view:account.move.make.netting:account_netting.view_account_move_make_netting_form +#: model:ir.actions.act_window,name:account_netting.act_account_move_make_netting +msgid "Compensate" +msgstr "Compensar" + +#. module: account_netting +#: view:account.move.make.netting:account_netting.view_account_move_make_netting_form +msgid "Compensate entries" +msgstr "Compensar apuntes" + +#. module: account_netting +#: field:account.move.make.netting,create_uid:0 +msgid "Created by" +msgstr "Creado por" + +#. module: account_netting +#: field:account.move.make.netting,create_date:0 +msgid "Created on" +msgstr "Creado en" + +#. module: account_netting +#: field:account.move.make.netting,id:0 +msgid "ID" +msgstr "ID" + +#. module: account_netting +#: field:account.move.make.netting,journal:0 +msgid "Journal" +msgstr "Diario" + +#. module: account_netting +#: field:account.move.make.netting,write_uid:0 +msgid "Last Updated by" +msgstr "Última actualización por" + +#. module: account_netting +#: field:account.move.make.netting,write_date:0 +msgid "Last Updated on" +msgstr "Última actualización en" + +#. module: account_netting +#: field:account.move.make.netting,move_lines:0 +msgid "Move lines" +msgstr "Apuntes" + +#. module: account_netting +#: view:account.move.make.netting:account_netting.view_account_move_make_netting_form +msgid "Select the journal where storing the journal entries" +msgstr "Seleccione el diario en el que se guardarán los apuntes" + +#. module: account_netting +#: view:account.move.make.netting:account_netting.view_account_move_make_netting_form +msgid "This operation will generate account entries that are counterpart of the receivable/payable accounts selected, and reconcile each other, letting this balance in the partner:" +msgstr "Esta operación generará apuntes que serán la contrapartida de las cuentas a cobrar/a pagar seleccionadas, y las reconciliará entre ellas, dejando este saldo en la empresa:" + +#. module: account_netting +#: selection:account.move.make.netting,balance_type:0 +msgid "To pay" +msgstr "A pagar" + +#. module: account_netting +#: selection:account.move.make.netting,balance_type:0 +msgid "To receive" +msgstr "A cobrar" + +#. module: account_netting +#: code:addons/account_netting/wizard/account_move_make_netting.py:24 +#, python-format +msgid "You should compensate at least 2 journal entries." +msgstr "Debe compensar al menos 2 apuntes" + +#. module: account_netting +#: view:account.move.make.netting:account_netting.view_account_move_make_netting_form +msgid "or" +msgstr "o" + diff --git a/account_netting/static/description/icon.png b/account_netting/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..3a0328b516c4980e8e44cdb63fd945757ddd132d GIT binary patch literal 9455 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 z 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 Xviia!U7SGha1wx#SCgwmn*{w2TRX*I literal 0 HcmV?d00001 diff --git a/account_netting/tests/__init__.py b/account_netting/tests/__init__.py new file mode 100644 index 000000000..89ab9a71c --- /dev/null +++ b/account_netting/tests/__init__.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- +# (c) 2015 Pedro M. Baeza +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html + +from . import test_account_netting diff --git a/account_netting/tests/test_account_netting.py b/account_netting/tests/test_account_netting.py new file mode 100644 index 000000000..c5a6a5ce4 --- /dev/null +++ b/account_netting/tests/test_account_netting.py @@ -0,0 +1,77 @@ +# -*- coding: utf-8 -*- +# (c) 2015 Pedro M. Baeza +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html + +import openerp.tests.common as common +from openerp import workflow + + +class TestAccountNetting(common.TransactionCase): + + def setUp(self): + super(TestAccountNetting, self).setUp() + self.partner = self.env['res.partner'].create( + {'supplier': True, + 'customer': True, + 'name': "Supplier/Customer" + }) + self.invoice_model = self.env['account.invoice'] + self.account_receivable = self.env.ref('account.a_recv') + self.customer_invoice = self.invoice_model.create( + {'journal_id': self.env.ref('account.sales_journal').id, + 'type': 'out_invoice', + 'partner_id': self.partner.id, + 'account_id': self.account_receivable.id, + 'invoice_line': [(0, 0, {'name': 'Test', + 'price_unit': 100.0})], + }) + workflow.trg_validate( + self.uid, 'account.invoice', self.customer_invoice.id, + 'invoice_open', self.cr) + customer_move = self.customer_invoice.move_id + self.move_line_1 = customer_move.line_id.filtered( + lambda x: x.account_id == self.account_receivable) + self.account_payable = self.env.ref('account.a_pay') + self.supplier_invoice = self.invoice_model.create( + {'journal_id': self.env.ref('account.expenses_journal').id, + 'type': 'in_invoice', + 'partner_id': self.partner.id, + 'account_id': self.account_payable.id, + 'invoice_line': [(0, 0, {'name': 'Test', + 'price_unit': 1200.0})], + }) + workflow.trg_validate( + self.uid, 'account.invoice', self.supplier_invoice.id, + 'invoice_open', self.cr) + supplier_move = self.supplier_invoice.move_id + self.move_line_2 = supplier_move.line_id.filtered( + lambda x: x.account_id == self.account_payable) + + def test_compensation(self): + obj = self.env['account.move.make.netting'].with_context( + active_ids=[self.move_line_1.id, self.move_line_2.id]) + wizard = obj.create( + {'move_lines': [(6, 0, [self.move_line_1.id, + self.move_line_2.id])], + 'journal': self.env.ref('account.miscellaneous_journal').id}) + res = wizard.button_compensate() + move = self.env['account.move'].browse(res['res_id']) + self.assertEqual( + len(move.line_id), 2, + 'AR/AP netting move has an incorrect line number') + move_line_receivable = move.line_id.filtered( + lambda x: x.account_id == self.account_receivable) + self.assertEqual( + move_line_receivable.credit, 100, + 'Incorrect credit amount for receivable move line') + self.assertTrue( + move_line_receivable.reconcile_id, + 'Receivable move line should be totally reconciled') + move_line_payable = move.line_id.filtered( + lambda x: x.account_id == self.account_payable) + self.assertEqual( + move_line_payable.debit, 100, + 'Incorrect debit amount for payable move line') + self.assertTrue( + move_line_payable.reconcile_partial_id, + 'Receivable move line should be partially reconciled') diff --git a/account_netting/wizard/__init__.py b/account_netting/wizard/__init__.py new file mode 100644 index 000000000..45a789c27 --- /dev/null +++ b/account_netting/wizard/__init__.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- +# (c) 2015 Pedro M. Baeza +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html + +from . import account_move_make_netting diff --git a/account_netting/wizard/account_move_make_netting.py b/account_netting/wizard/account_move_make_netting.py new file mode 100644 index 000000000..fbdc7dc80 --- /dev/null +++ b/account_netting/wizard/account_move_make_netting.py @@ -0,0 +1,115 @@ +# -*- coding: utf-8 -*- +# (c) 2015 Pedro M. Baeza +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html + +from openerp import models, fields, api, exceptions, _ + + +class AccountMoveMakeNetting(models.TransientModel): + _name = "account.move.make.netting" + + journal = fields.Many2one( + comodel_name="account.journal", required=True, + domain="[('type', '=', 'general')]") + move_lines = fields.Many2many(comodel_name="account.move.line") + balance = fields.Float(readonly=True) + balance_type = fields.Selection( + selection=[('pay', 'To pay'), ('receive', 'To receive')], + readonly=True) + + @api.model + def default_get(self, fields): + if len(self.env.context.get('active_ids', [])) < 2: + raise exceptions.ValidationError( + _("You should compensate at least 2 journal entries.")) + move_lines = self.env['account.move.line'].browse( + self.env.context['active_ids']) + if (any(x not in ('payable', 'receivable') for + x in move_lines.mapped('account_id.type'))): + raise exceptions.ValidationError( + _("All entries must have a receivable or payable account")) + if any(move_lines.mapped('reconcile_id')): + raise exceptions.ValidationError( + _("All entries mustn't been reconciled")) + partner_id = None + for move in move_lines: + if (not move.partner_id or ( + move.partner_id != partner_id and partner_id is not None)): + raise exceptions.ValidationError( + _("All entries should have a partner and the partner must " + "be the same for all.")) + partner_id = move.partner_id + res = super(AccountMoveMakeNetting, self).default_get(fields) + res['move_lines'] = [(6, 0, move_lines.ids)] + balance = (sum(move_lines.mapped('debit')) - + sum(move_lines.mapped('credit'))) + res['balance'] = abs(balance) + res['balance_type'] = 'pay' if balance < 0 else 'receive' + return res + + @api.multi + def button_compensate(self): + self.ensure_one() + # Create account move + move = self.env['account.move'].create( + { + 'ref': _('AR/AP netting'), + 'journal_id': self.journal.id, + }) + # Group amounts by account + account_groups = self.move_lines.read_group( + [('id', 'in', self.move_lines.ids)], + ['account_id', 'debit', 'credit'], ['account_id']) + debtors = [] + creditors = [] + total_debtors = 0 + total_creditors = 0 + for account_group in account_groups: + balance = account_group['debit'] - account_group['credit'] + group_vals = { + 'account_id': account_group['account_id'][0], + 'balance': abs(balance), + } + if balance > 0: + debtors.append(group_vals) + total_debtors += balance + else: + creditors.append(group_vals) + total_creditors += abs(balance) + # Create move lines + move_line_model = self.env['account.move.line'] + netting_amount = min(total_creditors, total_debtors) + field_map = {1: 'debit', 0: 'credit'} + for i, group in enumerate([debtors, creditors]): + available_amount = netting_amount + for account_group in group: + if account_group['balance'] > available_amount: + amount = available_amount + else: + amount = account_group['balance'] + move_line_vals = { + field_map[i]: amount, + 'move_id': move.id, + 'partner_id': self.move_lines[0].partner_id.id, + 'date': move.date, + 'period_id': move.period_id.id, + 'journal_id': move.journal_id.id, + 'name': move.ref, + 'account_id': account_group['account_id'], + } + move_line_model.create(move_line_vals) + available_amount -= account_group['balance'] + if available_amount <= 0: + break + # Make reconciliation + for move_line in move.line_id: + to_reconcile = move_line + self.move_lines.filtered( + lambda x: x.account_id == move_line.account_id) + to_reconcile.reconcile_partial() + # Open created move + action = self.env.ref('account.action_move_journal_line').read()[0] + action['view_mode'] = 'form' + del action['views'] + del action['view_id'] + action['res_id'] = move.id + return action diff --git a/account_netting/wizard/account_move_make_netting_view.xml b/account_netting/wizard/account_move_make_netting_view.xml new file mode 100644 index 000000000..7a90badb1 --- /dev/null +++ b/account_netting/wizard/account_move_make_netting_view.xml @@ -0,0 +1,37 @@ + + + + + + + Compensate entries + account.move.make.netting + +
+
+
+ + +
+