From 4eb5e594d5efc3f42c81c95444ebf31bf0b03bb8 Mon Sep 17 00:00:00 2001 From: eLBati Date: Thu, 28 Apr 2016 11:55:22 +0200 Subject: [PATCH 01/15] [ADD] account_tax_balance: Compute tax balances based on date range --- account_tax_balance/README.rst | 59 ++++++++++++ account_tax_balance/__init__.py | 6 ++ account_tax_balance/__openerp__.py | 25 +++++ account_tax_balance/models/__init__.py | 5 + account_tax_balance/models/account_tax.py | 86 ++++++++++++++++++ .../static/description/icon.png | Bin 0 -> 9455 bytes .../static/description/tax_balance.png | Bin 0 -> 25711 bytes .../views/account_tax_view.xml | 47 ++++++++++ account_tax_balance/wizard/__init__.py | 5 + .../wizard/open_tax_balances.py | 40 ++++++++ .../wizard/open_tax_balances_view.xml | 40 ++++++++ 11 files changed, 313 insertions(+) create mode 100644 account_tax_balance/README.rst create mode 100644 account_tax_balance/__init__.py create mode 100644 account_tax_balance/__openerp__.py create mode 100644 account_tax_balance/models/__init__.py create mode 100644 account_tax_balance/models/account_tax.py create mode 100644 account_tax_balance/static/description/icon.png create mode 100644 account_tax_balance/static/description/tax_balance.png create mode 100644 account_tax_balance/views/account_tax_view.xml create mode 100644 account_tax_balance/wizard/__init__.py create mode 100644 account_tax_balance/wizard/open_tax_balances.py create mode 100644 account_tax_balance/wizard/open_tax_balances_view.xml diff --git a/account_tax_balance/README.rst b/account_tax_balance/README.rst new file mode 100644 index 00000000..8b9b2905 --- /dev/null +++ b/account_tax_balance/README.rst @@ -0,0 +1,59 @@ +.. 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 + +============ +Tax Balances +============ + +This module allows to compute tax balances within a certain date range. +It depends on date_range module and exposes 'compute' methods that can be called by other modules (like localization ones) + +Usage +===== + +Accounting --> Reporting --> Open Tax Balances + +Select the company, the date range, the target moves and 'open taxes' + +.. figure:: /account_tax_balance/static/description/tax_balance.png + +.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas + :alt: Try me on Runbot + :target: https://runbot.odoo-community.org/runbot/91/9.0 + +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. + +Credits +======= + +Images +------ + +* Odoo Community Association: `Icon `_. + +Contributors +------------ + +* Lorenzo Battistini + +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 https://odoo-community.org. diff --git a/account_tax_balance/__init__.py b/account_tax_balance/__init__.py new file mode 100644 index 00000000..7ef5132c --- /dev/null +++ b/account_tax_balance/__init__.py @@ -0,0 +1,6 @@ +# -*- coding: utf-8 -*- +# © 2016 Lorenzo Battistini - Agile Business Group +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import models +from . import wizard diff --git a/account_tax_balance/__openerp__.py b/account_tax_balance/__openerp__.py new file mode 100644 index 00000000..14f7df88 --- /dev/null +++ b/account_tax_balance/__openerp__.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +# © 2016 Lorenzo Battistini - Agile Business Group +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +{ + "name": "Tax Balance", + "summary": "Compute tax balances based on date range", + "version": "9.0.1.0.0", + "category": "Accounting & Finance", + "website": "https://www.agilebg.com/", + "author": "Agile Business Group, Odoo Community Association (OCA)", + "license": "AGPL-3", + "application": False, + "installable": True, + "depends": [ + "account", + "date_range", + ], + "data": [ + "wizard/open_tax_balances_view.xml", + "views/account_tax_view.xml", + ], + "images": [ + 'images/tax_balance.png', + ] +} diff --git a/account_tax_balance/models/__init__.py b/account_tax_balance/models/__init__.py new file mode 100644 index 00000000..97aedc09 --- /dev/null +++ b/account_tax_balance/models/__init__.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- +# © 2016 Lorenzo Battistini - Agile Business Group +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import account_tax diff --git a/account_tax_balance/models/account_tax.py b/account_tax_balance/models/account_tax.py new file mode 100644 index 00000000..fbcfbce2 --- /dev/null +++ b/account_tax_balance/models/account_tax.py @@ -0,0 +1,86 @@ +# -*- coding: utf-8 -*- +# © 2016 Lorenzo Battistini - Agile Business Group +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from openerp import models, fields + + +class AccountTax(models.Model): + _inherit = 'account.tax' + + balance = fields.Float(string="Balance", compute="_compute_balance") + base_balance = fields.Float( + string="Base Balance", compute="_compute_balance") + + def get_context_values(self): + if not self.env.context.get('from_date'): + from_date = fields.Date.context_today(self) + else: + from_date = self.env.context['from_date'] + if not self.env.context.get('to_date'): + to_date = fields.Date.context_today(self) + else: + to_date = self.env.context['to_date'] + if not self.env.context.get('target_move'): + target_move = 'posted' + else: + target_move = self.env.context['target_move'] + if not self.env.context.get('company_id'): + company_id = self.env.user.company_id.id + else: + company_id = self.env.context['company_id'] + return from_date, to_date, company_id, target_move + + def _compute_balance(self): + from_date, to_date, company_id, target_move = self.get_context_values() + for tax in self: + tax.balance = tax.compute_balance( + from_date, to_date, company_id, target_move) + tax.base_balance = tax.compute_base_balance( + from_date, to_date, company_id, target_move) + + def get_target_state_list(self, target_move="posted"): + if target_move == 'posted': + state = ['posted'] + elif target_move == 'all': + state = ['posted', 'draft'] + else: + state = [] + return state + + def get_move_line_domain(self, from_date, to_date, company_id): + return [ + ('date', '<=', to_date), + ('date', '>=', from_date), + ('company_id', '=', company_id), + ] + + def compute_balance( + self, from_date, to_date, company_id, target_move="posted" + ): + self.ensure_one() + move_line_model = self.env['account.move.line'] + state_list = self.get_target_state_list(target_move) + domain = self.get_move_line_domain(from_date, to_date, company_id) + domain.extend([ + ('move_id.state', 'in', state_list), + ('tax_line_id', '=', self.id), + ]) + move_lines = move_line_model.search(domain) + total = sum([l.balance for l in move_lines]) + return total + + def compute_base_balance( + self, from_date, to_date, company_id, target_move="posted" + ): + self.ensure_one() + move_line_model = self.env['account.move.line'] + state_list = self.get_target_state_list(target_move) + domain = self.get_move_line_domain(from_date, to_date, company_id) + domain.extend([ + ('move_id.state', 'in', state_list), + ('tax_ids', 'in', self.id), + ]) + move_lines = move_line_model.search(domain) + total = sum([l.balance for l in move_lines]) + return total diff --git a/account_tax_balance/static/description/icon.png b/account_tax_balance/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_tax_balance/static/description/tax_balance.png b/account_tax_balance/static/description/tax_balance.png new file mode 100644 index 0000000000000000000000000000000000000000..b5063a440bdc6d7b055918a22d58f1a9f9dde87b GIT binary patch literal 25711 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 T*bOuPZdglQ|55qFC$Iho=(0ol literal 0 HcmV?d00001 diff --git a/account_tax_balance/views/account_tax_view.xml b/account_tax_balance/views/account_tax_view.xml new file mode 100644 index 00000000..a204056d --- /dev/null +++ b/account_tax_balance/views/account_tax_view.xml @@ -0,0 +1,47 @@ + + + + + + account.tax.tree.balance + account.tax + + + + + + + + + + + + + account.tax.search.balance + account.tax + + + + + + + + + + + + + + + + + + Tax Balances + account.tax + form + tree + + + + + \ No newline at end of file diff --git a/account_tax_balance/wizard/__init__.py b/account_tax_balance/wizard/__init__.py new file mode 100644 index 00000000..c4942697 --- /dev/null +++ b/account_tax_balance/wizard/__init__.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- +# © 2016 Lorenzo Battistini - Agile Business Group +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import open_tax_balances diff --git a/account_tax_balance/wizard/open_tax_balances.py b/account_tax_balance/wizard/open_tax_balances.py new file mode 100644 index 00000000..ca390f47 --- /dev/null +++ b/account_tax_balance/wizard/open_tax_balances.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +# © 2016 Lorenzo Battistini - Agile Business Group +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from openerp import models, fields, api + + +class OpenTaxBalances(models.TransientModel): + _name = 'wizard.open.tax.balances' + company_id = fields.Many2one( + 'res.company', 'Company', required=True, + default=lambda self: self.env.user.company_id) + from_date = fields.Date('From date', required=True) + to_date = fields.Date('To date', required=True) + date_range_id = fields.Many2one('date.range', 'Date range') + target_move = fields.Selection([ + ('posted', 'All Posted Entries'), + ('all', 'All Entries'), + ], 'Target Moves', required=True, default='posted') + + @api.onchange('date_range_id') + def onchange_date_range_id(self): + if self.date_range_id: + self.from_date = self.date_range_id.date_start + self.to_date = self.date_range_id.date_end + else: + self.from_date = self.to_date = None + + @api.multi + def open_taxes(self): + self.ensure_one() + action = self.env.ref('account_tax_balance.action_tax_balances_tree') + vals = action.read()[0] + vals['context'] = { + 'from_date': self.from_date, + 'to_date': self.to_date, + 'target_move': self.target_move, + 'company_id': self.company_id.id, + } + return vals diff --git a/account_tax_balance/wizard/open_tax_balances_view.xml b/account_tax_balance/wizard/open_tax_balances_view.xml new file mode 100644 index 00000000..fed17d33 --- /dev/null +++ b/account_tax_balance/wizard/open_tax_balances_view.xml @@ -0,0 +1,40 @@ + + + + + wizard_open_tax_balances + wizard.open.tax.balances + +
+ + + + + + + +
+
+
+
+
+ + + Open Tax Balances + wizard.open.tax.balances + form + form + + new + + + +
+
\ No newline at end of file From c5b1aaa6fe1810d14bbfb5221e51fbb832017480 Mon Sep 17 00:00:00 2001 From: gfcapalbo Date: Fri, 29 Apr 2016 10:42:30 +0200 Subject: [PATCH 02/15] tests --- account_tax_balance/tests/__init__.py | 3 + .../tests/test_account_tax_balance.py | 72 +++++++++++++++++++ 2 files changed, 75 insertions(+) create mode 100644 account_tax_balance/tests/__init__.py create mode 100644 account_tax_balance/tests/test_account_tax_balance.py diff --git a/account_tax_balance/tests/__init__.py b/account_tax_balance/tests/__init__.py new file mode 100644 index 00000000..7094e61d --- /dev/null +++ b/account_tax_balance/tests/__init__.py @@ -0,0 +1,3 @@ +#Accounting tests extending AccountingTestCase + +from . import test_account_tax_balance diff --git a/account_tax_balance/tests/test_account_tax_balance.py b/account_tax_balance/tests/test_account_tax_balance.py new file mode 100644 index 00000000..9c7cd74a --- /dev/null +++ b/account_tax_balance/tests/test_account_tax_balance.py @@ -0,0 +1,72 @@ +#from openerp.addons.account.tests.account_test_users import AccountTestUsers +from openerp.tests.common import TransactionCase +from openerp.tools import float_compare + + +class TestAccountTaxBalance(TransactionCase): + + def setUp(self): + super(TestAccountTaxBalance, self).setUp() + self.fixed_tax = self.tax_model.create({ + 'name': "Fixed tax", + 'amount_type': 'fixed', + 'amount': 10.0, + 'sequence': 1, + }) + self.fixed_tax_bis = self.tax_model.create({ + 'name': "Fixed tax bis", + 'amount_type': 'fixed', + 'amount': 15, + 'sequence': 2, + }) + self.percent_tax = self.tax_model.create({ + 'name': "Percent tax", + 'amount_type': 'percent', + 'amount': 10.0, + 'sequence': 3, + }) + self.bank_journal = self.env['account.journal'].search([('type', '=', 'bank'), ('company_id', '=', self.account_manager.company_id.id)])[0] + self.bank_account = self.bank_journal.default_debit_account_id + self.expense_account = self.env['account.account'].search([('user_type_id.type', '=', 'payable')], limit=1) #Should be done by onchange later + + + def test_tax_balance(self): + company_id = self.env['res.users'].browse(self.env.uid).company_id.id + tax = self.env['account.tax'].create({ + 'name': 'Tax 10.0', + 'amount': 10.0, + 'amount_type': 'fixed', + }) + analytic_account = self.env['account.analytic.account'].create({ + 'name': 'test account', + }) + invoice_account = self.env['account.account'].search([('user_type_id', '=', self.env.ref('account.data_account_type_receivable').id)], limit=1).id + invoice_line_account = self.env['account.account'].search([('user_type_id', '=', self.env.ref('account.data_account_type_expenses').id)], limit=1).id + invoice = self.env['account.invoice'].create({ + 'partner_id': self.env.ref('base.res_partner_2').id, + 'account_id': invoice_account, + 'type': 'in_invoice', + }) + + self.env['account.invoice.line'].create({ + 'product_id': self.env.ref('product.product_product_4').id, + 'quantity': 1.0, + 'price_unit': 100.0, + 'invoice_id': invoice.id, + 'name': 'product that cost 100', + 'account_id': invoice_line_account, + 'invoice_line_tax_ids': [(6, 0, [tax.id])], + 'account_analytic_id': analytic_account.id, + }) + + # : check that Initially supplier bill state is "Draft" + self.assertTrue((invoice.state == 'draft'), "Initially vendor bill state is Draft") + + #change the state of invoice to open by clicking Validate button + invoice.signal_workflow('invoice_open') + + self.assertEquals(tax.base_balance, 100) + self.assertEquals(tax.balance, 10) + + + From e1d494b6cbfaebba6beeca7235fc33ec992233dd Mon Sep 17 00:00:00 2001 From: eLBati Date: Fri, 29 Apr 2016 12:23:48 +0200 Subject: [PATCH 03/15] [FIX+IMP] account_tax_balance: * Tests * PEP8 * Use invoice._convert_to_write(invoice._cache). This way, the onchange will be inheritable and will add here also the added values * better get_context_values * unify method compute_balance * open move lines linked to balance --- account_tax_balance/README.rst | 3 +- account_tax_balance/__openerp__.py | 3 +- account_tax_balance/models/account_tax.py | 105 ++++++++------- account_tax_balance/tests/__init__.py | 7 +- .../tests/test_account_tax_balance.py | 126 ++++++++++++------ .../views/account_tax_view.xml | 6 +- .../wizard/open_tax_balances_view.xml | 6 +- 7 files changed, 157 insertions(+), 99 deletions(-) diff --git a/account_tax_balance/README.rst b/account_tax_balance/README.rst index 8b9b2905..ba3acddb 100644 --- a/account_tax_balance/README.rst +++ b/account_tax_balance/README.rst @@ -12,7 +12,7 @@ It depends on date_range module and exposes 'compute' methods that can be called Usage ===== -Accounting --> Reporting --> Open Tax Balances +Accounting --> Reporting --> Taxes Balance Select the company, the date range, the target moves and 'open taxes' @@ -42,6 +42,7 @@ Contributors ------------ * Lorenzo Battistini +* Giovanni Capalbo Maintainer ---------- diff --git a/account_tax_balance/__openerp__.py b/account_tax_balance/__openerp__.py index 14f7df88..c1077042 100644 --- a/account_tax_balance/__openerp__.py +++ b/account_tax_balance/__openerp__.py @@ -7,7 +7,8 @@ "version": "9.0.1.0.0", "category": "Accounting & Finance", "website": "https://www.agilebg.com/", - "author": "Agile Business Group, Odoo Community Association (OCA)", + "author": "Agile Business Group, Therp BV, " + "Odoo Community Association (OCA)", "license": "AGPL-3", "application": False, "installable": True, diff --git a/account_tax_balance/models/account_tax.py b/account_tax_balance/models/account_tax.py index fbcfbce2..2a0c149e 100644 --- a/account_tax_balance/models/account_tax.py +++ b/account_tax_balance/models/account_tax.py @@ -2,7 +2,7 @@ # © 2016 Lorenzo Battistini - Agile Business Group # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from openerp import models, fields +from openerp import models, fields, api class AccountTax(models.Model): @@ -13,31 +13,18 @@ class AccountTax(models.Model): string="Base Balance", compute="_compute_balance") def get_context_values(self): - if not self.env.context.get('from_date'): - from_date = fields.Date.context_today(self) - else: - from_date = self.env.context['from_date'] - if not self.env.context.get('to_date'): - to_date = fields.Date.context_today(self) - else: - to_date = self.env.context['to_date'] - if not self.env.context.get('target_move'): - target_move = 'posted' - else: - target_move = self.env.context['target_move'] - if not self.env.context.get('company_id'): - company_id = self.env.user.company_id.id - else: - company_id = self.env.context['company_id'] - return from_date, to_date, company_id, target_move + context = self.env.context + return ( + context.get('from_date', fields.Date.context_today(self)), + context.get('to_date', fields.Date.context_today(self)), + context.get('company_id', self.env.user.company_id.id), + context.get('target_move', 'posted') + ) def _compute_balance(self): - from_date, to_date, company_id, target_move = self.get_context_values() for tax in self: - tax.balance = tax.compute_balance( - from_date, to_date, company_id, target_move) - tax.base_balance = tax.compute_base_balance( - from_date, to_date, company_id, target_move) + tax.balance = tax.compute_balance(tax_or_base='tax') + tax.base_balance = tax.compute_balance(tax_or_base='base') def get_target_state_list(self, target_move="posted"): if target_move == 'posted': @@ -48,39 +35,63 @@ class AccountTax(models.Model): state = [] return state - def get_move_line_domain(self, from_date, to_date, company_id): + def get_move_line_partial_domain(self, from_date, to_date, company_id): return [ ('date', '<=', to_date), ('date', '>=', from_date), ('company_id', '=', company_id), ] - def compute_balance( - self, from_date, to_date, company_id, target_move="posted" - ): + def compute_balance(self, tax_or_base='tax'): self.ensure_one() - move_line_model = self.env['account.move.line'] - state_list = self.get_target_state_list(target_move) - domain = self.get_move_line_domain(from_date, to_date, company_id) - domain.extend([ + move_lines = self.get_move_lines_domain(tax_or_base=tax_or_base) + # balance is debit - credit whereas on tax return you want to see what + # vat has to be paid so: + # VAT on sales (credit) - VAT on purchases (debit). + total = -sum([l.balance for l in move_lines]) + return total + + def get_balance_domain(self, state_list): + return [ ('move_id.state', 'in', state_list), ('tax_line_id', '=', self.id), - ]) - move_lines = move_line_model.search(domain) - total = sum([l.balance for l in move_lines]) - return total + ] - def compute_base_balance( - self, from_date, to_date, company_id, target_move="posted" - ): - self.ensure_one() - move_line_model = self.env['account.move.line'] - state_list = self.get_target_state_list(target_move) - domain = self.get_move_line_domain(from_date, to_date, company_id) - domain.extend([ + def get_base_balance_domain(self, state_list): + return [ ('move_id.state', 'in', state_list), ('tax_ids', 'in', self.id), - ]) - move_lines = move_line_model.search(domain) - total = sum([l.balance for l in move_lines]) - return total + ] + + def get_move_lines_domain(self, tax_or_base='tax'): + move_line_model = self.env['account.move.line'] + from_date, to_date, company_id, target_move = self.get_context_values() + state_list = self.get_target_state_list(target_move) + domain = self.get_move_line_partial_domain( + from_date, to_date, company_id) + balance_domain = [] + if tax_or_base == 'tax': + balance_domain = self.get_balance_domain(state_list) + elif tax_or_base == 'base': + balance_domain = self.get_base_balance_domain(state_list) + domain.extend(balance_domain) + return move_line_model.search(domain) + + def get_lines_action(self, tax_or_base='tax'): + move_lines = self.get_move_lines_domain(tax_or_base=tax_or_base) + move_line_ids = [l.id for l in move_lines] + action = self.env.ref('account.action_account_moves_all_tree') + vals = action.read()[0] + vals['context'] = {} + vals['domain'] = [('id', 'in', move_line_ids)] + return vals + + @api.multi + def view_tax_lines(self): + self.ensure_one() + return self.get_lines_action(tax_or_base='tax') + + @api.multi + def view_base_lines(self): + self.ensure_one() + return self.get_lines_action(tax_or_base='base') diff --git a/account_tax_balance/tests/__init__.py b/account_tax_balance/tests/__init__.py index 7094e61d..b1bcc511 100644 --- a/account_tax_balance/tests/__init__.py +++ b/account_tax_balance/tests/__init__.py @@ -1,3 +1,6 @@ -#Accounting tests extending AccountingTestCase +# -*- coding: utf-8 -*- +# © 2016 Lorenzo Battistini - Agile Business Group +# © 2016 Giovanni Capalbo +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from . import test_account_tax_balance +from . import test_account_tax_balance diff --git a/account_tax_balance/tests/test_account_tax_balance.py b/account_tax_balance/tests/test_account_tax_balance.py index 9c7cd74a..15d0382d 100644 --- a/account_tax_balance/tests/test_account_tax_balance.py +++ b/account_tax_balance/tests/test_account_tax_balance.py @@ -1,51 +1,54 @@ -#from openerp.addons.account.tests.account_test_users import AccountTestUsers +# -*- coding: utf-8 -*- +# © 2016 Lorenzo Battistini - Agile Business Group +# © 2016 Giovanni Capalbo +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + from openerp.tests.common import TransactionCase -from openerp.tools import float_compare - +from datetime import datetime +from dateutil.rrule import MONTHLY + class TestAccountTaxBalance(TransactionCase): - - def setUp(self): - super(TestAccountTaxBalance, self).setUp() - self.fixed_tax = self.tax_model.create({ - 'name': "Fixed tax", - 'amount_type': 'fixed', - 'amount': 10.0, - 'sequence': 1, - }) - self.fixed_tax_bis = self.tax_model.create({ - 'name': "Fixed tax bis", - 'amount_type': 'fixed', - 'amount': 15, - 'sequence': 2, - }) - self.percent_tax = self.tax_model.create({ - 'name': "Percent tax", - 'amount_type': 'percent', - 'amount': 10.0, - 'sequence': 3, - }) - self.bank_journal = self.env['account.journal'].search([('type', '=', 'bank'), ('company_id', '=', self.account_manager.company_id.id)])[0] - self.bank_account = self.bank_journal.default_debit_account_id - self.expense_account = self.env['account.account'].search([('user_type_id.type', '=', 'payable')], limit=1) #Should be done by onchange later + def setUp(self): + super(TestAccountTaxBalance, self).setUp() + self.range_type = self.env['date.range.type'].create( + {'name': 'Fiscal year', + 'company_id': False, + 'allow_overlap': False}) + self.range_generator = self.env['date.range.generator'] + self.current_year = datetime.now().year + self.current_month = datetime.now().month + range_generator = self.range_generator.create({ + 'date_start': '%s-01-01' % self.current_year, + 'name_prefix': '%s-' % self.current_year, + 'type_id': self.range_type.id, + 'duration_count': 1, + 'unit_of_time': MONTHLY, + 'count': 12}) + range_generator.action_apply() + self.range = self.env['date.range'] def test_tax_balance(self): - company_id = self.env['res.users'].browse(self.env.uid).company_id.id + tax_account_id = self.env['account.account'].search( + [('name', '=', 'Tax Paid')], limit=1).id tax = self.env['account.tax'].create({ 'name': 'Tax 10.0', 'amount': 10.0, 'amount_type': 'fixed', + 'account_id': tax_account_id, }) - analytic_account = self.env['account.analytic.account'].create({ - 'name': 'test account', - }) - invoice_account = self.env['account.account'].search([('user_type_id', '=', self.env.ref('account.data_account_type_receivable').id)], limit=1).id - invoice_line_account = self.env['account.account'].search([('user_type_id', '=', self.env.ref('account.data_account_type_expenses').id)], limit=1).id + invoice_account_id = self.env['account.account'].search( + [('user_type_id', '=', self.env.ref( + 'account.data_account_type_receivable' + ).id)], limit=1).id + invoice_line_account_id = self.env['account.account'].search( + [('user_type_id', '=', self.env.ref( + 'account.data_account_type_expenses').id)], limit=1).id invoice = self.env['account.invoice'].create({ 'partner_id': self.env.ref('base.res_partner_2').id, - 'account_id': invoice_account, - 'type': 'in_invoice', + 'account_id': invoice_account_id, + 'type': 'out_invoice', }) self.env['account.invoice.line'].create({ @@ -54,19 +57,56 @@ class TestAccountTaxBalance(TransactionCase): 'price_unit': 100.0, 'invoice_id': invoice.id, 'name': 'product that cost 100', - 'account_id': invoice_line_account, + 'account_id': invoice_line_account_id, 'invoice_line_tax_ids': [(6, 0, [tax.id])], - 'account_analytic_id': analytic_account.id, }) + invoice._onchange_invoice_line_ids() + invoice._convert_to_write(invoice._cache) + self.assertEqual(invoice.state, 'draft') - # : check that Initially supplier bill state is "Draft" - self.assertTrue((invoice.state == 'draft'), "Initially vendor bill state is Draft") - - #change the state of invoice to open by clicking Validate button + # change the state of invoice to open by clicking Validate button invoice.signal_workflow('invoice_open') - + self.assertEquals(tax.base_balance, 100) self.assertEquals(tax.balance, 10) - + # testing wizard + current_range = self.range.search([ + ('date_start', '=', '%s-%s-01' % ( + self.current_year, self.current_month)) + ]) + wizard = self.env['wizard.open.tax.balances'].new({}) + self.assertFalse(wizard.from_date) + self.assertFalse(wizard.to_date) + wizard = self.env['wizard.open.tax.balances'].new({ + 'date_range_id': current_range[0].id, + }) + wizard.onchange_date_range_id() + wizard._convert_to_write(wizard._cache) + action = wizard.open_taxes() + self.assertEqual( + action['context']['from_date'], current_range[0].date_start) + self.assertEqual( + action['context']['to_date'], current_range[0].date_end) + self.assertEqual( + action['xml_id'], 'account_tax_balance.action_tax_balances_tree') + + # testing buttons + tax_action = tax.view_tax_lines() + base_action = tax.view_base_lines() + self.assertTrue( + tax_action['domain'][0][2][0] in + [l.id for l in invoice.move_id.line_ids]) + self.assertEqual( + tax_action['xml_id'], 'account.action_account_moves_all_tree') + self.assertTrue( + base_action['domain'][0][2][0] in + [l.id for l in invoice.move_id.line_ids]) + self.assertEqual( + base_action['xml_id'], 'account.action_account_moves_all_tree') + # test specific method + state_list = tax.get_target_state_list(target_move='all') + self.assertEqual(state_list, ['posted', 'draft']) + state_list = tax.get_target_state_list(target_move='whatever') + self.assertEqual(state_list, []) diff --git a/account_tax_balance/views/account_tax_view.xml b/account_tax_balance/views/account_tax_view.xml index a204056d..e3f097cf 100644 --- a/account_tax_balance/views/account_tax_view.xml +++ b/account_tax_balance/views/account_tax_view.xml @@ -13,6 +13,8 @@ + + @@ -36,7 +38,7 @@ - Tax Balances + Taxes Balance account.tax form tree @@ -44,4 +46,4 @@ - \ No newline at end of file + diff --git a/account_tax_balance/wizard/open_tax_balances_view.xml b/account_tax_balance/wizard/open_tax_balances_view.xml index fed17d33..39446320 100644 --- a/account_tax_balance/wizard/open_tax_balances_view.xml +++ b/account_tax_balance/wizard/open_tax_balances_view.xml @@ -5,7 +5,7 @@ wizard_open_tax_balances wizard.open.tax.balances -
+ @@ -23,7 +23,7 @@ - Open Tax Balances + Taxes Balance wizard.open.tax.balances form form @@ -37,4 +37,4 @@ parent="account.menu_finance_reports" groups="account.group_account_user,account.group_account_manager"> - \ No newline at end of file + From 4a8c33b71405c0f9e1780f90467bd0a758d80514 Mon Sep 17 00:00:00 2001 From: Antonio Espinosa Date: Fri, 21 Oct 2016 15:08:38 +0200 Subject: [PATCH 04/15] Consider normal and refund operations separately Allow to explore all move lines Spanish translation Bugfixes Show negative lines too Show move type in account.move views Tests include new fields --- account_tax_balance/__openerp__.py | 4 +- account_tax_balance/i18n/es.po | 261 ++++++++++++++++++ account_tax_balance/models/__init__.py | 1 + account_tax_balance/models/account_move.py | 42 +++ account_tax_balance/models/account_tax.py | 128 ++++++++- .../tests/test_account_tax_balance.py | 41 ++- .../views/account_move_view.xml | 40 +++ .../views/account_tax_view.xml | 116 ++++---- .../wizard/open_tax_balances_view.xml | 76 ++--- 9 files changed, 605 insertions(+), 104 deletions(-) create mode 100644 account_tax_balance/i18n/es.po create mode 100644 account_tax_balance/models/account_move.py create mode 100644 account_tax_balance/views/account_move_view.xml diff --git a/account_tax_balance/__openerp__.py b/account_tax_balance/__openerp__.py index c1077042..10e3e685 100644 --- a/account_tax_balance/__openerp__.py +++ b/account_tax_balance/__openerp__.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- # © 2016 Lorenzo Battistini - Agile Business Group +# © 2016 Antonio Espinosa # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). { "name": "Tax Balance", @@ -7,7 +8,7 @@ "version": "9.0.1.0.0", "category": "Accounting & Finance", "website": "https://www.agilebg.com/", - "author": "Agile Business Group, Therp BV, " + "author": "Agile Business Group, Therp BV, Tecnativa, " "Odoo Community Association (OCA)", "license": "AGPL-3", "application": False, @@ -18,6 +19,7 @@ ], "data": [ "wizard/open_tax_balances_view.xml", + "views/account_move_view.xml", "views/account_tax_view.xml", ], "images": [ diff --git a/account_tax_balance/i18n/es.po b/account_tax_balance/i18n/es.po new file mode 100644 index 00000000..41cd1957 --- /dev/null +++ b/account_tax_balance/i18n/es.po @@ -0,0 +1,261 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * account_tax_balance +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 9.0c\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2016-10-21 15:30+0000\n" +"PO-Revision-Date: 2016-10-21 15:30+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_tax_balance +#: model:ir.ui.view,arch_db:account_tax_balance.view_tax_search_balance +msgid "Account" +msgstr "Cuenta" + +#. module: account_tax_balance +#: model:ir.model,name:account_tax_balance.model_account_move +msgid "Account Entry" +msgstr "Asiento contable" + +#. module: account_tax_balance +#: model:ir.ui.view,arch_db:account_tax_balance.view_tax_search_balance +#: model:ir.ui.view,arch_db:account_tax_balance.view_tax_tree_balance +msgid "Account Tax" +msgstr "Cuenta de impuesto" + +#. module: account_tax_balance +#: selection:wizard.open.tax.balances,target_move:0 +msgid "All Entries" +msgstr "Todos los asientos" + +#. module: account_tax_balance +#: selection:wizard.open.tax.balances,target_move:0 +msgid "All Posted Entries" +msgstr "Todos los asientos asentados" + +#. module: account_tax_balance +#: model:ir.model.fields,field_description:account_tax_balance.field_account_tax_balance_regular +msgid "Balance" +msgstr "Cuota" + +#. module: account_tax_balance +#: model:ir.model.fields,field_description:account_tax_balance.field_account_tax_balance_refund +msgid "Balance Refund" +msgstr "Cuota devoluciones" + +#. module: account_tax_balance +#: model:ir.model.fields,field_description:account_tax_balance.field_account_tax_base_balance_regular +msgid "Base Balance" +msgstr "Base imponible" + +#. module: account_tax_balance +#: model:ir.model.fields,field_description:account_tax_balance.field_account_tax_base_balance_refund +msgid "Base Balance Refund" +msgstr "Base devoluciones" + +#. module: account_tax_balance +#: model:ir.ui.view,arch_db:account_tax_balance.view_tax_tree_balance +msgid "Base Total" +msgstr "Base total" + +#. module: account_tax_balance +#: model:ir.ui.view,arch_db:account_tax_balance.wizard_open_tax_balances +msgid "Cancel" +msgstr "Cancelar" + +#. module: account_tax_balance +#: model:ir.model.fields,field_description:account_tax_balance.field_wizard_open_tax_balances_company_id +msgid "Company" +msgstr "Compañía" + +#. module: account_tax_balance +#: model:ir.model.fields,field_description:account_tax_balance.field_wizard_open_tax_balances_create_uid +msgid "Created by" +msgstr "Creado por" + +#. module: account_tax_balance +#: model:ir.model.fields,field_description:account_tax_balance.field_wizard_open_tax_balances_create_date +msgid "Created on" +msgstr "Creado en" + +#. module: account_tax_balance +#: model:ir.model.fields,field_description:account_tax_balance.field_wizard_open_tax_balances_date_range_id +msgid "Date range" +msgstr "Periodo" + +#. module: account_tax_balance +#: model:ir.model.fields,field_description:account_tax_balance.field_wizard_open_tax_balances_display_name +msgid "Display Name" +msgstr "Nombre a mostrar" + +#. module: account_tax_balance +#: model:ir.model.fields,field_description:account_tax_balance.field_wizard_open_tax_balances_from_date +msgid "From date" +msgstr "Desde" + +#. module: account_tax_balance +#: model:ir.ui.view,arch_db:account_tax_balance.view_tax_search_balance +msgid "Group By" +msgstr "Agrupar por" + +#. module: account_tax_balance +#: model:ir.model.fields,field_description:account_tax_balance.field_wizard_open_tax_balances_id +msgid "ID" +msgstr "ID" + +#. module: account_tax_balance +#: model:ir.model.fields,field_description:account_tax_balance.field_wizard_open_tax_balances___last_update +msgid "Last Modified on" +msgstr "Última modificación en" + +#. module: account_tax_balance +#: model:ir.model.fields,field_description:account_tax_balance.field_wizard_open_tax_balances_write_uid +msgid "Last Updated by" +msgstr "Última modificación por" + +#. module: account_tax_balance +#: model:ir.model.fields,field_description:account_tax_balance.field_wizard_open_tax_balances_write_date +msgid "Last Updated on" +msgstr "Última actualización en" + +#. module: account_tax_balance +#: selection:account.move,move_type:0 +msgid "Liquidity" +msgstr "Liquidez" + +#. module: account_tax_balance +#: model:ir.model.fields,field_description:account_tax_balance.field_account_move_move_type +msgid "Move type" +msgstr "Tipo de operación" + +#. module: account_tax_balance +#: model:ir.ui.view,arch_db:account_tax_balance.wizard_open_tax_balances +msgid "Open Taxes" +msgstr "Ver impuestos" + +#. module: account_tax_balance +#: selection:account.move,move_type:0 +msgid "Other" +msgstr "Otro" + +#. module: account_tax_balance +#: selection:account.move,move_type:0 +msgid "Payable" +msgstr "A pagar" + +#. module: account_tax_balance +#: selection:account.move,move_type:0 +msgid "Payable refund" +msgstr "Devoluciones a cobrar" + +#. module: account_tax_balance +#: selection:account.move,move_type:0 +msgid "Receivable" +msgstr "A cobrar" + +#. module: account_tax_balance +#: selection:account.move,move_type:0 +msgid "Receivable refund" +msgstr "Devoluciones a pagar" + +#. module: account_tax_balance +#: model:ir.ui.view,arch_db:account_tax_balance.view_tax_search_balance +#: model:ir.ui.view,arch_db:account_tax_balance.view_tax_tree_balance +msgid "Short Name" +msgstr "Nombre corto" + +#. module: account_tax_balance +#: model:ir.model.fields,field_description:account_tax_balance.field_wizard_open_tax_balances_target_move +msgid "Target Moves" +msgstr "Movimientos destino" + +#. module: account_tax_balance +#: model:ir.model,name:account_tax_balance.model_account_tax +msgid "Tax" +msgstr "Impuesto" + +#. module: account_tax_balance +#: model:ir.ui.view,arch_db:account_tax_balance.view_tax_search_balance +msgid "Tax Group" +msgstr "Grupo del impuesto" + +#. module: account_tax_balance +#: model:ir.ui.view,arch_db:account_tax_balance.view_tax_search_balance +msgid "Tax Scope" +msgstr "Uso del impuesto" + +#. module: account_tax_balance +#: model:ir.actions.act_window,name:account_tax_balance.action_open_tax_balances +#: model:ir.actions.act_window,name:account_tax_balance.action_tax_balances_tree +#: model:ir.ui.menu,name:account_tax_balance.menu_action_open_tax_balances +#: model:ir.ui.view,arch_db:account_tax_balance.wizard_open_tax_balances +msgid "Taxes Balance" +msgstr "Tabla de impuestos" + +#. module: account_tax_balance +#: model:ir.model.fields,field_description:account_tax_balance.field_wizard_open_tax_balances_to_date +msgid "To date" +msgstr "Hasta" + +#. module: account_tax_balance +#: model:ir.ui.view,arch_db:account_tax_balance.view_tax_tree_balance +msgid "Total" +msgstr "Total" + +#. module: account_tax_balance +#: model:ir.model.fields,field_description:account_tax_balance.field_account_tax_balance +msgid "Total Balance" +msgstr "Total cuota" + +#. module: account_tax_balance +#: model:ir.model.fields,field_description:account_tax_balance.field_account_tax_base_balance +msgid "Total Base Balance" +msgstr "Total base imponible" + +#. module: account_tax_balance +#: model:ir.ui.view,arch_db:account_tax_balance.view_tax_tree_balance +msgid "View base lines" +msgstr "Ver líneas de base imponible" + +#. module: account_tax_balance +#: model:ir.ui.view,arch_db:account_tax_balance.view_tax_tree_balance +msgid "View base refund lines" +msgstr "Ver líneas de base imponible de devoluciones" + +#. module: account_tax_balance +#: model:ir.ui.view,arch_db:account_tax_balance.view_tax_tree_balance +msgid "View base regular lines" +msgstr "Ver líneas de base imponible de operaciones corrientes" + +#. module: account_tax_balance +#: model:ir.ui.view,arch_db:account_tax_balance.view_tax_tree_balance +msgid "View tax lines" +msgstr "Ver líneas de cuota" + +#. module: account_tax_balance +#: model:ir.ui.view,arch_db:account_tax_balance.view_tax_tree_balance +msgid "View tax refund lines" +msgstr "Ver líneas de cuota de devoluciones" + +#. module: account_tax_balance +#: model:ir.ui.view,arch_db:account_tax_balance.view_tax_tree_balance +msgid "View tax regular lines" +msgstr "Ver líneas de cuota de operaciones corrientes" + +#. module: account_tax_balance +#: model:ir.ui.view,arch_db:account_tax_balance.wizard_open_tax_balances +msgid "or" +msgstr "o" + +#. module: account_tax_balance +#: model:ir.model,name:account_tax_balance.model_wizard_open_tax_balances +msgid "wizard.open.tax.balances" +msgstr "wizard.open.tax.balances" diff --git a/account_tax_balance/models/__init__.py b/account_tax_balance/models/__init__.py index 97aedc09..b78e51cd 100644 --- a/account_tax_balance/models/__init__.py +++ b/account_tax_balance/models/__init__.py @@ -2,4 +2,5 @@ # © 2016 Lorenzo Battistini - Agile Business Group # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from . import account_move from . import account_tax diff --git a/account_tax_balance/models/account_move.py b/account_tax_balance/models/account_move.py new file mode 100644 index 00000000..a7092c95 --- /dev/null +++ b/account_tax_balance/models/account_move.py @@ -0,0 +1,42 @@ +# -*- coding: utf-8 -*- +# © 2016 Antonio Espinosa +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from openerp import models, fields, api + + +class AccountMove(models.Model): + _inherit = 'account.move' + + move_type = fields.Selection( + string="Move type", selection=[ + ('other', 'Other'), + ('liquidity', 'Liquidity'), + ('receivable', 'Receivable'), + ('receivable_refund', 'Receivable refund'), + ('payable', 'Payable'), + ('payable_refund', 'Payable refund'), + ], compute='_compute_move_type', store=True, readonly=True) + + @api.multi + @api.depends('line_ids.account_id.internal_type', 'line_ids.balance') + def _compute_move_type(self): + def _balance_get(line_ids, internal_type): + return sum(line_ids.filtered( + lambda x: x.account_id.internal_type == internal_type).mapped( + 'balance')) + + for move in self: + internal_types = move.line_ids.mapped('account_id.internal_type') + if 'liquidity' in internal_types: + move.move_type = 'liquidity' + elif 'payable' in internal_types: + balance = _balance_get(move.line_ids, 'payable') + move.move_type = ( + 'payable' if balance < 0 else 'payable_refund') + elif 'receivable' in internal_types: + balance = _balance_get(move.line_ids, 'receivable') + move.move_type = ( + 'receivable' if balance > 0 else 'receivable_refund') + else: + move.move_type = 'other' diff --git a/account_tax_balance/models/account_tax.py b/account_tax_balance/models/account_tax.py index 2a0c149e..a6596899 100644 --- a/account_tax_balance/models/account_tax.py +++ b/account_tax_balance/models/account_tax.py @@ -1,16 +1,68 @@ # -*- coding: utf-8 -*- # © 2016 Lorenzo Battistini - Agile Business Group +# © 2016 Antonio Espinosa # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from openerp import models, fields, api +from openerp.tools.safe_eval import safe_eval class AccountTax(models.Model): _inherit = 'account.tax' - balance = fields.Float(string="Balance", compute="_compute_balance") + balance = fields.Float( + string="Total Balance", compute="_compute_balance", + search='_search_balance') base_balance = fields.Float( - string="Base Balance", compute="_compute_balance") + string="Total Base Balance", compute="_compute_balance", + search='_search_base_balance') + balance_regular = fields.Float( + string="Balance", compute="_compute_balance", + search='_search_balance_regular') + base_balance_regular = fields.Float( + string="Base Balance", compute="_compute_balance", + search='_search_base_balance_regular') + balance_refund = fields.Float( + string="Balance Refund", compute="_compute_balance", + search='_search_balance_refund') + base_balance_refund = fields.Float( + string="Base Balance Refund", compute="_compute_balance", + search='_search_base_balance_refund') + + def _search_balance_field(self, field, operator, value): + operators = {'>', '<', '>=', '<=', '!=', '=', '<>'} + fields = { + 'balance', 'base_balance', 'balance_regular', + 'base_balance_regular', 'balance_refund', 'base_balance_refund', + } + domain = [] + if operator in operators and field in fields: + value = float(value) if value else 0 + taxes = self.search([]).filtered( + lambda x: safe_eval( + '%.2f %s %.2f' % (x[field], operator, value))) + domain.append(('id', 'in', taxes.ids)) + return domain + + def _search_balance(self, operator, value): + return self._search_balance_field('balance', operator, value) + + def _search_base_balance(self, operator, value): + return self._search_balance_field('base_balance', operator, value) + + def _search_balance_regular(self, operator, value): + return self._search_balance_field('balance_regular', operator, value) + + def _search_base_balance_regular(self, operator, value): + return self._search_balance_field( + 'base_balance_regular', operator, value) + + def _search_balance_refund(self, operator, value): + return self._search_balance_field('balance_refund', operator, value) + + def _search_base_balance_refund(self, operator, value): + return self._search_balance_field( + 'base_balance_refund', operator, value) def get_context_values(self): context = self.env.context @@ -23,8 +75,24 @@ class AccountTax(models.Model): def _compute_balance(self): for tax in self: - tax.balance = tax.compute_balance(tax_or_base='tax') - tax.base_balance = tax.compute_balance(tax_or_base='base') + tax.balance_regular = tax.compute_balance( + tax_or_base='tax', move_type='regular') + tax.base_balance_regular = tax.compute_balance( + tax_or_base='base', move_type='regular') + tax.balance_refund = tax.compute_balance( + tax_or_base='tax', move_type='refund') + tax.base_balance_refund = tax.compute_balance( + tax_or_base='base', move_type='refund') + tax.balance = tax.balance_regular + tax.balance_refund + tax.base_balance = ( + tax.base_balance_regular + tax.base_balance_refund) + + def get_target_type_list(self, move_type=None): + if move_type == 'refund': + return ['receivable_refund', 'payable_refund'] + elif move_type == 'regular': + return ['receivable', 'payable'] + return [] def get_target_state_list(self, target_move="posted"): if target_move == 'posted': @@ -42,43 +110,53 @@ class AccountTax(models.Model): ('company_id', '=', company_id), ] - def compute_balance(self, tax_or_base='tax'): + def compute_balance(self, tax_or_base='tax', move_type=None): self.ensure_one() - move_lines = self.get_move_lines_domain(tax_or_base=tax_or_base) + move_lines = self.get_move_lines_domain( + tax_or_base=tax_or_base, move_type=move_type) # balance is debit - credit whereas on tax return you want to see what # vat has to be paid so: # VAT on sales (credit) - VAT on purchases (debit). total = -sum([l.balance for l in move_lines]) return total - def get_balance_domain(self, state_list): - return [ + def get_balance_domain(self, state_list, type_list): + domain = [ ('move_id.state', 'in', state_list), ('tax_line_id', '=', self.id), ] + if type_list: + domain.append(('move_id.move_type', 'in', type_list)) + return domain - def get_base_balance_domain(self, state_list): - return [ + def get_base_balance_domain(self, state_list, type_list): + domain = [ ('move_id.state', 'in', state_list), ('tax_ids', 'in', self.id), ] + if type_list: + domain.append(('move_id.move_type', 'in', type_list)) + return domain - def get_move_lines_domain(self, tax_or_base='tax'): + def get_move_lines_domain(self, tax_or_base='tax', move_type=None): move_line_model = self.env['account.move.line'] from_date, to_date, company_id, target_move = self.get_context_values() state_list = self.get_target_state_list(target_move) + type_list = self.get_target_type_list(move_type) domain = self.get_move_line_partial_domain( from_date, to_date, company_id) balance_domain = [] if tax_or_base == 'tax': - balance_domain = self.get_balance_domain(state_list) + balance_domain = self.get_balance_domain(state_list, type_list) elif tax_or_base == 'base': - balance_domain = self.get_base_balance_domain(state_list) + balance_domain = self.get_base_balance_domain( + state_list, type_list) domain.extend(balance_domain) return move_line_model.search(domain) - def get_lines_action(self, tax_or_base='tax'): - move_lines = self.get_move_lines_domain(tax_or_base=tax_or_base) + def get_lines_action(self, tax_or_base='tax', move_type=None): + move_lines = self.get_move_lines_domain( + tax_or_base=tax_or_base, move_type=move_type) move_line_ids = [l.id for l in move_lines] action = self.env.ref('account.action_account_moves_all_tree') vals = action.read()[0] @@ -95,3 +173,23 @@ class AccountTax(models.Model): def view_base_lines(self): self.ensure_one() return self.get_lines_action(tax_or_base='base') + + @api.multi + def view_tax_regular_lines(self): + self.ensure_one() + return self.get_lines_action(tax_or_base='tax', move_type='regular') + + @api.multi + def view_base_regular_lines(self): + self.ensure_one() + return self.get_lines_action(tax_or_base='base', move_type='regular') + + @api.multi + def view_tax_refund_lines(self): + self.ensure_one() + return self.get_lines_action(tax_or_base='tax', move_type='refund') + + @api.multi + def view_base_refund_lines(self): + self.ensure_one() + return self.get_lines_action(tax_or_base='base', move_type='refund') diff --git a/account_tax_balance/tests/test_account_tax_balance.py b/account_tax_balance/tests/test_account_tax_balance.py index 15d0382d..e66eca5c 100644 --- a/account_tax_balance/tests/test_account_tax_balance.py +++ b/account_tax_balance/tests/test_account_tax_balance.py @@ -33,9 +33,9 @@ class TestAccountTaxBalance(TransactionCase): tax_account_id = self.env['account.account'].search( [('name', '=', 'Tax Paid')], limit=1).id tax = self.env['account.tax'].create({ - 'name': 'Tax 10.0', + 'name': 'Tax 10.0%', 'amount': 10.0, - 'amount_type': 'fixed', + 'amount_type': 'percent', 'account_id': tax_account_id, }) invoice_account_id = self.env['account.account'].search( @@ -67,8 +67,12 @@ class TestAccountTaxBalance(TransactionCase): # change the state of invoice to open by clicking Validate button invoice.signal_workflow('invoice_open') - self.assertEquals(tax.base_balance, 100) - self.assertEquals(tax.balance, 10) + self.assertEquals(tax.base_balance, 100.) + self.assertEquals(tax.balance, 10.) + self.assertEquals(tax.base_balance_regular, 100.) + self.assertEquals(tax.balance_regular, 10.) + self.assertEquals(tax.base_balance_refund, 0.) + self.assertEquals(tax.balance_refund, 0.) # testing wizard current_range = self.range.search([ @@ -110,3 +114,32 @@ class TestAccountTaxBalance(TransactionCase): self.assertEqual(state_list, ['posted', 'draft']) state_list = tax.get_target_state_list(target_move='whatever') self.assertEqual(state_list, []) + + refund = self.env['account.invoice'].create({ + 'partner_id': self.env.ref('base.res_partner_2').id, + 'account_id': invoice_account_id, + 'type': 'out_refund', + }) + + self.env['account.invoice.line'].create({ + 'product_id': self.env.ref('product.product_product_2').id, + 'quantity': 1.0, + 'price_unit': 25.0, + 'invoice_id': refund.id, + 'name': 'returned product that cost 25', + 'account_id': invoice_line_account_id, + 'invoice_line_tax_ids': [(6, 0, [tax.id])], + }) + refund._onchange_invoice_line_ids() + refund._convert_to_write(invoice._cache) + self.assertEqual(refund.state, 'draft') + + # change the state of refund to open by clicking Validate button + refund.signal_workflow('invoice_open') + + self.assertEquals(tax.base_balance, 75.) + self.assertEquals(tax.balance, 7.5) + self.assertEquals(tax.base_balance_regular, 100.) + self.assertEquals(tax.balance_regular, 10.) + self.assertEquals(tax.base_balance_refund, -25.) + self.assertEquals(tax.balance_refund, -2.5) diff --git a/account_tax_balance/views/account_move_view.xml b/account_tax_balance/views/account_move_view.xml new file mode 100644 index 00000000..5211c056 --- /dev/null +++ b/account_tax_balance/views/account_move_view.xml @@ -0,0 +1,40 @@ + + + + + + + Add move type column + account.move + + + + + + + + + + Add move type field + account.move + + + + + + + + + + Add move type group by + account.move + + + + + + + + + diff --git a/account_tax_balance/views/account_tax_view.xml b/account_tax_balance/views/account_tax_view.xml index e3f097cf..fa1895c8 100644 --- a/account_tax_balance/views/account_tax_view.xml +++ b/account_tax_balance/views/account_tax_view.xml @@ -1,49 +1,71 @@ - - + + - - account.tax.tree.balance - account.tax - - - - - - - - - - - - - - - account.tax.search.balance - account.tax - - - - - - - - - - - - - - - - - - Taxes Balance - account.tax - form - tree - - - - - + + account.tax.tree.balance + account.tax + + + + + + + +