From 3845e23d360c43601a43b2cfc59b489a76eec910 Mon Sep 17 00:00:00 2001 From: Sergio Teruel Date: Thu, 19 Mar 2020 09:13:15 +0100 Subject: [PATCH] [IMP] account_netting: black, isort --- account_netting/__manifest__.py | 25 +- account_netting/tests/test_account_netting.py | 403 +++++++++++------- .../wizards/account_move_make_netting.py | 125 +++--- .../account_move_make_netting_view.xml | 41 +- 4 files changed, 338 insertions(+), 256 deletions(-) diff --git a/account_netting/__manifest__.py b/account_netting/__manifest__.py index b28c2ebb..a1902e1b 100644 --- a/account_netting/__manifest__.py +++ b/account_netting/__manifest__.py @@ -3,19 +3,14 @@ # License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html { - 'name': 'Account netting', - 'version': '12.0.1.0.0', - 'summary': 'Compensate AR/AP accounts from the same partner', - 'category': 'Accounting & Finance', - 'author': 'Tecnativa, ' - 'Odoo Community Association (OCA)', - 'license': 'AGPL-3', - 'website': 'https://github.com/OCA/account-financial-tools/', - 'depends': [ - 'account', - ], - 'data': [ - 'wizards/account_move_make_netting_view.xml', - ], - 'installable': True, + "name": "Account netting", + "version": "12.0.1.0.0", + "summary": "Compensate AR/AP accounts from the same partner", + "category": "Accounting & Finance", + "author": "Tecnativa, " "Odoo Community Association (OCA)", + "license": "AGPL-3", + "website": "https://github.com/OCA/account-financial-tools/", + "depends": ["account",], + "data": ["wizards/account_move_make_netting_view.xml",], + "installable": True, } diff --git a/account_netting/tests/test_account_netting.py b/account_netting/tests/test_account_netting.py index f0094b46..9024949b 100644 --- a/account_netting/tests/test_account_netting.py +++ b/account_netting/tests/test_account_netting.py @@ -9,209 +9,288 @@ class TestAccountNetting(common.SavepointCase): @classmethod def setUpClass(cls): super(TestAccountNetting, cls).setUpClass() - cls.partner = cls.env['res.partner'].create({ - 'supplier': True, - 'customer': True, - 'name': "Supplier/Customer", - }) - cls.partner1 = cls.env['res.partner'].create({ - 'supplier': True, - 'customer': True, - 'name': "Supplier/Customer 1", - }) - res_users_account_manager = cls.env.ref( - 'account.group_account_manager') - partner_manager = cls.env.ref('base.group_partner_manager') - cls.env.user.write({ - 'groups_id': [ - (6, 0, [res_users_account_manager.id, partner_manager.id]) - ], - }) + cls.partner = cls.env["res.partner"].create( + {"supplier": True, "customer": True, "name": "Supplier/Customer",} + ) + cls.partner1 = cls.env["res.partner"].create( + {"supplier": True, "customer": True, "name": "Supplier/Customer 1",} + ) + res_users_account_manager = cls.env.ref("account.group_account_manager") + partner_manager = cls.env.ref("base.group_partner_manager") + cls.env.user.write( + {"groups_id": [(6, 0, [res_users_account_manager.id, partner_manager.id])],} + ) # only adviser can create an account - cls.account_receivable = cls.env['account.account'].create({ - 'code': 'cust_acc', - 'name': 'customer account', - 'user_type_id': cls.env.ref( - 'account.data_account_type_receivable').id, - 'reconcile': True, - }) - cls.account_payable = cls.env['account.account'].create({ - 'code': 'supp_acc', - 'name': 'supplier account', - 'user_type_id': cls.env.ref( - 'account.data_account_type_payable').id, - 'reconcile': True, - }) - cls.account_revenue = cls.env['account.account'].search([ - ('user_type_id', '=', cls.env.ref( - 'account.data_account_type_revenue').id) - ], limit=1) - cls.account_expense = cls.env['account.account'].search([ - ('user_type_id', '=', cls.env.ref( - 'account.data_account_type_expenses').id) - ], limit=1) - cls.journal = cls.env['account.journal'].create({ - 'name': 'Test sale journal', - 'type': 'sale', - 'code': 'TEST', - }) - cls.expenses_journal = cls.env['account.journal'].create({ - 'name': 'Test expense journal', - 'type': 'purchase', - 'code': 'EXP', - }) - cls.miscellaneous_journal = cls.env['account.journal'].create({ - 'name': 'Miscellaneus journal', - 'type': 'general', - 'code': 'OTHER', - }) - cls.customer_invoice = cls.env['account.invoice'].create({ - 'journal_id': cls.journal.id, - 'type': 'out_invoice', - 'partner_id': cls.partner.id, - 'account_id': cls.account_receivable.id, - 'invoice_line_ids': [(0, 0, { - 'name': 'Test', - 'price_unit': 100.0, - 'account_id': cls.account_revenue.id, - })], - }) + cls.account_receivable = cls.env["account.account"].create( + { + "code": "cust_acc", + "name": "customer account", + "user_type_id": cls.env.ref("account.data_account_type_receivable").id, + "reconcile": True, + } + ) + cls.account_payable = cls.env["account.account"].create( + { + "code": "supp_acc", + "name": "supplier account", + "user_type_id": cls.env.ref("account.data_account_type_payable").id, + "reconcile": True, + } + ) + cls.account_revenue = cls.env["account.account"].search( + [ + ( + "user_type_id", + "=", + cls.env.ref("account.data_account_type_revenue").id, + ) + ], + limit=1, + ) + cls.account_expense = cls.env["account.account"].search( + [ + ( + "user_type_id", + "=", + cls.env.ref("account.data_account_type_expenses").id, + ) + ], + limit=1, + ) + cls.journal = cls.env["account.journal"].create( + {"name": "Test sale journal", "type": "sale", "code": "TEST",} + ) + cls.expenses_journal = cls.env["account.journal"].create( + {"name": "Test expense journal", "type": "purchase", "code": "EXP",} + ) + cls.miscellaneous_journal = cls.env["account.journal"].create( + {"name": "Miscellaneus journal", "type": "general", "code": "OTHER",} + ) + cls.customer_invoice = cls.env["account.invoice"].create( + { + "journal_id": cls.journal.id, + "type": "out_invoice", + "partner_id": cls.partner.id, + "account_id": cls.account_receivable.id, + "invoice_line_ids": [ + ( + 0, + 0, + { + "name": "Test", + "price_unit": 100.0, + "account_id": cls.account_revenue.id, + }, + ) + ], + } + ) cls.customer_invoice.action_invoice_open() customer_move = cls.customer_invoice.move_id cls.move_line_1 = customer_move.line_ids.filtered( - lambda x: x.account_id == cls.account_receivable) - cls.supplier_invoice = cls.env['account.invoice'].create({ - 'journal_id': cls.expenses_journal.id, - 'type': 'in_invoice', - 'partner_id': cls.partner.id, - 'account_id': cls.account_payable.id, - 'invoice_line_ids': [(0, 0, { - 'name': 'Test', - 'price_unit': 1200.0, - 'account_id': cls.account_expense.id, - })], - }) + lambda x: x.account_id == cls.account_receivable + ) + cls.supplier_invoice = cls.env["account.invoice"].create( + { + "journal_id": cls.expenses_journal.id, + "type": "in_invoice", + "partner_id": cls.partner.id, + "account_id": cls.account_payable.id, + "invoice_line_ids": [ + ( + 0, + 0, + { + "name": "Test", + "price_unit": 1200.0, + "account_id": cls.account_expense.id, + }, + ) + ], + } + ) cls.supplier_invoice.action_invoice_open() supplier_move = cls.supplier_invoice.move_id cls.move_line_2 = supplier_move.line_ids.filtered( - lambda x: x.account_id == cls.account_payable) + lambda x: x.account_id == cls.account_payable + ) cls.move_line_3 = supplier_move.line_ids.filtered( - lambda x: x.account_id == cls.account_expense) - cls.supplier_invoice = cls.env['account.invoice'].create({ - 'journal_id': cls.expenses_journal.id, - 'type': 'in_invoice', - 'partner_id': cls.partner1.id, - 'account_id': cls.account_payable.id, - 'invoice_line_ids': [(0, 0, { - 'name': 'Test', - 'price_unit': 200.0, - 'account_id': cls.account_expense.id, - })], - }) + lambda x: x.account_id == cls.account_expense + ) + cls.supplier_invoice = cls.env["account.invoice"].create( + { + "journal_id": cls.expenses_journal.id, + "type": "in_invoice", + "partner_id": cls.partner1.id, + "account_id": cls.account_payable.id, + "invoice_line_ids": [ + ( + 0, + 0, + { + "name": "Test", + "price_unit": 200.0, + "account_id": cls.account_expense.id, + }, + ) + ], + } + ) cls.supplier_invoice.action_invoice_open() supplier_move = cls.supplier_invoice.move_id cls.move_line_4 = supplier_move.line_ids.filtered( - lambda x: x.account_id == cls.account_payable) - cls.supplier_invoice = cls.env['account.invoice'].create({ - 'journal_id': cls.expenses_journal.id, - 'type': 'in_refund', - 'partner_id': cls.partner1.id, - 'account_id': cls.account_payable.id, - 'invoice_line_ids': [(0, 0, { - 'name': 'Test', - 'price_unit': 200.0, - 'account_id': cls.account_expense.id, - })], - }) + lambda x: x.account_id == cls.account_payable + ) + cls.supplier_invoice = cls.env["account.invoice"].create( + { + "journal_id": cls.expenses_journal.id, + "type": "in_refund", + "partner_id": cls.partner1.id, + "account_id": cls.account_payable.id, + "invoice_line_ids": [ + ( + 0, + 0, + { + "name": "Test", + "price_unit": 200.0, + "account_id": cls.account_expense.id, + }, + ) + ], + } + ) cls.supplier_invoice.action_invoice_open() supplier_move = cls.supplier_invoice.move_id cls.move_line_5 = supplier_move.line_ids.filtered( - lambda x: x.account_id == cls.account_payable) - cls.supplier_invoice = cls.env['account.invoice'].create({ - 'journal_id': cls.expenses_journal.id, - 'type': 'in_refund', - 'partner_id': cls.partner1.id, - 'account_id': cls.account_payable.id, - 'invoice_line_ids': [(0, 0, { - 'name': 'Test', - 'price_unit': 200.0, - 'account_id': cls.account_expense.id, - })], - }) + lambda x: x.account_id == cls.account_payable + ) + cls.supplier_invoice = cls.env["account.invoice"].create( + { + "journal_id": cls.expenses_journal.id, + "type": "in_refund", + "partner_id": cls.partner1.id, + "account_id": cls.account_payable.id, + "invoice_line_ids": [ + ( + 0, + 0, + { + "name": "Test", + "price_unit": 200.0, + "account_id": cls.account_expense.id, + }, + ) + ], + } + ) cls.supplier_invoice.action_invoice_open() supplier_move = cls.supplier_invoice.move_id cls.move_line_6 = supplier_move.line_ids.filtered( - lambda x: x.account_id == cls.account_payable) + lambda x: x.account_id == cls.account_payable + ) def test_compensation(self): # Test exception line 33 from account_move_make_netting - obj = self.env['account.move.make.netting'].with_context( - active_ids=[self.move_line_1.id]) + obj = self.env["account.move.make.netting"].with_context( + active_ids=[self.move_line_1.id] + ) with self.assertRaises(Exception): wizard = obj.create( - {'move_line_ids': [(6, 0, [self.move_line_1.id])], - 'journal_id': self.miscellaneous_journal.id}) + { + "move_line_ids": [(6, 0, [self.move_line_1.id])], + "journal_id": self.miscellaneous_journal.id, + } + ) # Test exception line 39 from account_move_make_netting - obj = self.env['account.move.make.netting'].with_context( - active_ids=[self.move_line_1.id, self.move_line_3.id]) + obj = self.env["account.move.make.netting"].with_context( + active_ids=[self.move_line_1.id, self.move_line_3.id] + ) with self.assertRaises(Exception): wizard = obj.create( - {'move_line_ids': [(6, 0, [self.move_line_1.id, - self.move_line_3.id])], - 'journal_id': self.miscellaneous_journal.id}) + { + "move_line_ids": [ + (6, 0, [self.move_line_1.id, self.move_line_3.id]) + ], + "journal_id": self.miscellaneous_journal.id, + } + ) # Test exception line 45 from account_move_make_netting - obj = self.env['account.move.make.netting'].with_context( - active_ids=[self.move_line_4.id, self.move_line_5.id]) + obj = self.env["account.move.make.netting"].with_context( + active_ids=[self.move_line_4.id, self.move_line_5.id] + ) with self.assertRaises(Exception): wizard = obj.create( - {'move_line_ids': [(6, 0, [self.move_line_4.id, - self.move_line_5.id])], - 'journal_id': self.miscellaneous_journal.id}) + { + "move_line_ids": [ + (6, 0, [self.move_line_4.id, self.move_line_5.id]) + ], + "journal_id": self.miscellaneous_journal.id, + } + ) # Test exception line 42 from account_move_make_netting - moves = self.env['account.move.line'].browse([self.move_line_4.id, - self.move_line_5.id]) + moves = self.env["account.move.line"].browse( + [self.move_line_4.id, self.move_line_5.id] + ) moves.reconcile() - obj = self.env['account.move.make.netting'].with_context( - active_ids=[self.move_line_4.id, self.move_line_5.id]) + obj = self.env["account.move.make.netting"].with_context( + active_ids=[self.move_line_4.id, self.move_line_5.id] + ) with self.assertRaises(Exception): wizard = obj.create( - {'move_line_ids': [(6, 0, [self.move_line_4.id, - self.move_line_5.id])], - 'journal_id': self.miscellaneous_journal.id}) + { + "move_line_ids": [ + (6, 0, [self.move_line_4.id, self.move_line_5.id]) + ], + "journal_id": self.miscellaneous_journal.id, + } + ) # Test exception line 52 from account_move_make_netting - obj = self.env['account.move.make.netting'].with_context( - active_ids=[self.move_line_1.id, self.move_line_6.id]) + obj = self.env["account.move.make.netting"].with_context( + active_ids=[self.move_line_1.id, self.move_line_6.id] + ) with self.assertRaises(Exception): wizard = obj.create( - {'move_line_ids': [(6, 0, [self.move_line_1.id, - self.move_line_6.id])], - 'journal_id': self.miscellaneous_journal.id}) - obj = self.env['account.move.make.netting'].with_context( - active_ids=[self.move_line_1.id, self.move_line_2.id]) + { + "move_line_ids": [ + (6, 0, [self.move_line_1.id, self.move_line_6.id]) + ], + "journal_id": self.miscellaneous_journal.id, + } + ) + 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_line_ids': [(6, 0, [self.move_line_1.id, - self.move_line_2.id])], - 'journal_id': self.miscellaneous_journal.id}) + { + "move_line_ids": [(6, 0, [self.move_line_1.id, self.move_line_2.id])], + "journal_id": self.miscellaneous_journal.id, + } + ) res = wizard.button_compensate() - move = self.env['account.move'].browse(res['res_id']) + move = self.env["account.move"].browse(res["res_id"]) self.assertEqual( - len(move.line_ids), 2, - 'AR/AP netting move has an incorrect line number') + len(move.line_ids), 2, "AR/AP netting move has an incorrect line number" + ) move_line_receivable = move.line_ids.filtered( - lambda x: x.account_id == self.account_receivable) + lambda x: x.account_id == self.account_receivable + ) self.assertEqual( - move_line_receivable.credit, 100, - 'Incorrect credit amount for receivable move line') + move_line_receivable.credit, + 100, + "Incorrect credit amount for receivable move line", + ) self.assertTrue( - move_line_receivable.reconciled and - move_line_receivable.full_reconcile_id, - 'Receivable move line should be totally reconciled') + move_line_receivable.reconciled and move_line_receivable.full_reconcile_id, + "Receivable move line should be totally reconciled", + ) move_line_payable = move.line_ids.filtered( - lambda x: x.account_id == self.account_payable) + lambda x: x.account_id == self.account_payable + ) self.assertEqual( - move_line_payable.debit, 100, - 'Incorrect debit amount for payable move line') + move_line_payable.debit, 100, "Incorrect debit amount for payable move line" + ) self.assertTrue( - move_line_payable.reconciled and not - move_line_payable.full_reconcile_id, - 'Receivable move line should be partially reconciled') + move_line_payable.reconciled and not move_line_payable.full_reconcile_id, + "Receivable move line should be partially reconciled", + ) diff --git a/account_netting/wizards/account_move_make_netting.py b/account_netting/wizards/account_move_make_netting.py index f45cd66f..0fea5444 100644 --- a/account_netting/wizards/account_move_make_netting.py +++ b/account_netting/wizards/account_move_make_netting.py @@ -7,83 +7,85 @@ from odoo import _, api, exceptions, fields, models class AccountMoveMakeNetting(models.TransientModel): _name = "account.move.make.netting" - _description = 'Wizard to generate account moves for netting' + _description = "Wizard to generate account moves for netting" journal_id = fields.Many2one( comodel_name="account.journal", required=True, domain="[('type', '=', 'general')]", ) - move_line_ids = fields.Many2many( - comodel_name="account.move.line", - ) - balance = fields.Float( - readonly=True, - ) + move_line_ids = 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, + selection=[("pay", "To pay"), ("receive", "To receive"),], readonly=True, ) @api.model def default_get(self, fields_list): - if len(self.env.context.get('active_ids', [])) < 2: + 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.user_type_id.type'))): + _("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.user_type_id.type") + ): raise exceptions.ValidationError( - _("All entries must have a receivable or payable account")) - if any(move_lines.mapped('reconciled')): + _("All entries must have a receivable or payable account") + ) + if any(move_lines.mapped("reconciled")): + raise exceptions.ValidationError(_("All entries mustn't been reconciled")) + if len(move_lines.mapped("account_id")) == 1: raise exceptions.ValidationError( - _("All entries mustn't been reconciled")) - if len(move_lines.mapped('account_id')) == 1: + _( + "The 'Compensate' function is intended to balance " + "operations on different accounts for the same partner.\n" + "In this case all selected entries belong to the same " + "account.\n Please use the 'Reconcile' function." + ) + ) + if len(move_lines.mapped("partner_id")) != 1: raise exceptions.ValidationError( - _("The 'Compensate' function is intended to balance " - "operations on different accounts for the same partner.\n" - "In this case all selected entries belong to the same " - "account.\n Please use the 'Reconcile' function.")) - if len(move_lines.mapped('partner_id')) != 1: - raise exceptions.ValidationError( - _("All entries should have a partner and the partner must " - "be the same for all.")) + _( + "All entries should have a partner and the partner must " + "be the same for all." + ) + ) res = super(AccountMoveMakeNetting, self).default_get(fields_list) - res['move_line_ids'] = [(6, 0, move_lines.ids)] - debit_move_lines_debit = move_lines.filtered('debit') - credit_move_lines_debit = move_lines.filtered('credit') - balance = (abs(sum(debit_move_lines_debit.mapped('amount_residual'))) - - abs(sum(credit_move_lines_debit.mapped('amount_residual')))) - res['balance'] = abs(balance) - res['balance_type'] = 'pay' if balance < 0 else 'receive' + res["move_line_ids"] = [(6, 0, move_lines.ids)] + debit_move_lines_debit = move_lines.filtered("debit") + credit_move_lines_debit = move_lines.filtered("credit") + balance = abs(sum(debit_move_lines_debit.mapped("amount_residual"))) - abs( + sum(credit_move_lines_debit.mapped("amount_residual")) + ) + res["balance"] = abs(balance) + res["balance_type"] = "pay" if balance < 0 else "receive" return res 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.id, - }) + move = self.env["account.move"].create( + {"ref": _("AR/AP netting"), "journal_id": self.journal_id.id,} + ) # Group amounts by account account_groups = self.move_line_ids.read_group( - [('id', 'in', self.move_line_ids.ids)], - ['account_id', 'amount_residual'], - ['account_id'], + [("id", "in", self.move_line_ids.ids)], + ["account_id", "amount_residual"], + ["account_id"], ) debtors = [] creditors = [] total_debtors = 0 total_creditors = 0 for account_group in account_groups: - balance = account_group['amount_residual'] + balance = account_group["amount_residual"] group_vals = { - 'account_id': account_group['account_id'][0], - 'balance': abs(balance), + "account_id": account_group["account_id"][0], + "balance": abs(balance), } if balance > 0: debtors.append(group_vals) @@ -93,36 +95,37 @@ class AccountMoveMakeNetting(models.TransientModel): total_creditors += abs(balance) # Create move lines netting_amount = min(total_creditors, total_debtors) - field_map = {1: 'debit', 0: 'credit'} + field_map = {1: "debit", 0: "credit"} move_lines = [] for i, group in enumerate([debtors, creditors]): available_amount = netting_amount for account_group in group: - if account_group['balance'] > available_amount: + if account_group["balance"] > available_amount: amount = available_amount else: - amount = account_group['balance'] + amount = account_group["balance"] move_line_vals = { field_map[i]: amount, - 'partner_id': self.move_line_ids[0].partner_id.id, - 'name': move.ref, - 'account_id': account_group['account_id'], + "partner_id": self.move_line_ids[0].partner_id.id, + "name": move.ref, + "account_id": account_group["account_id"], } move_lines.append((0, 0, move_line_vals)) - available_amount -= account_group['balance'] + available_amount -= account_group["balance"] if available_amount <= 0: break if move_lines: - move.write({'line_ids': move_lines}) + move.write({"line_ids": move_lines}) # Make reconciliation for move_line in move.line_ids: to_reconcile = move_line + self.move_line_ids.filtered( - lambda x: x.account_id == move_line.account_id) + lambda x: x.account_id == move_line.account_id + ) to_reconcile.reconcile() # 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 + 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/wizards/account_move_make_netting_view.xml b/account_netting/wizards/account_move_make_netting_view.xml index b0266068..2f1ac9fa 100644 --- a/account_netting/wizards/account_move_make_netting_view.xml +++ b/account_netting/wizards/account_move_make_netting_view.xml @@ -1,33 +1,38 @@ - + - - - + Compensate entries account.move.make.netting
-

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.

+

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.

- - - + + +
- -