diff --git a/addons/account_tax_exigible/__init__.py b/addons/account_tax_exigible/__init__.py new file mode 100644 index 00000000000..18647072982 --- /dev/null +++ b/addons/account_tax_exigible/__init__.py @@ -0,0 +1,46 @@ +import models + +from openerp import SUPERUSER_ID + +def _migration_script(cr, registry): + #1. set the tax_exigible at True on all account.move lines not related to a tax in cash basis + cr.execute('''UPDATE account_move_line SET tax_exigible='t' ''') + + cr.execute('''SELECT id FROM account_tax WHERE use_cash_basis = 't' ''') + tax_ids = tuple([x[0] for x in cr.fetchall()]) + if tax_ids: + cr.execute('''UPDATE account_move_line SET tax_exigible='f' WHERE id IN (SELECT id FROM account_move_line WHERE tax_line_id IN %s OR id IN (SELECT account_move_line_id FROM account_move_line_account_tax_rel WHERE account_tax_id IN %s))''', (tax_ids, tax_ids)) + + #2. Fix the moves already created by the tax case based feature + # create account.move.lines for tax_line_id and tax_ids that weren't correctly set/created at the time of reconciliation + existing_move = registry['account.move'].search(cr, SUPERUSER_ID, [('tax_cash_basis_rec_id', '!=', False)]) + for m in registry['account.move'].browse(cr, SUPERUSER_ID, existing_move): + partial_rec = m.tax_cash_basis_rec_id + for move in (partial_rec.debit_move_id.move_id, partial_rec.credit_move_id.move_id): + matched_percentage = partial_rec.amount and (partial_rec.amount / move.amount) or 0.0 + for l in move.line_ids: + if l.tax_exigible: + continue + + move_to_fix = registry['account.move'].search(cr, SUPERUSER_ID, [('tax_cash_basis_rec_id', '=', partial_rec.id)])[0] + amount = (l.debit - l.credit) * matched_percentage + + registry['account.move.line'].create(cr, SUPERUSER_ID, { + 'name': '/', + 'debit': amount > 0 and amount or 0.0, + 'credit': amount < 0 and -amount or 0.0, + 'account_id': l.account_id.id, + 'tax_line_id': l.tax_line_id and l.tax_line_id.id or False, + 'tax_ids': l.tax_ids and [(6, 0, l.tax_ids.ids)] or [], + 'tax_exigible': True, + 'move_id': move_to_fix, + }, apply_taxes=False, context={'check_move_validity': False}) + registry['account.move.line'].create(cr, SUPERUSER_ID, { + 'name': '/', + 'debit': -amount > 0 and -amount or 0.0, + 'credit': -amount < 0 and amount or 0.0, + 'account_id': l.account_id.id, + 'move_id': move_to_fix, + 'tax_exigible': True, + }, context={'check_move_validity': False}) + return diff --git a/addons/account_tax_exigible/__openerp__.py b/addons/account_tax_exigible/__openerp__.py new file mode 100644 index 00000000000..11244bd9aa7 --- /dev/null +++ b/addons/account_tax_exigible/__openerp__.py @@ -0,0 +1,17 @@ +# -*- encoding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. +{ + 'name': 'Tax exigible', + 'version': '1.0', + 'category': 'Accounting', + 'description': """ + This is a bugfix-module backporting 39d68ac and adding the concept of tax exigibility on journal items. + """, + 'depends': ['account_tax_cash_basis'], + 'data': [], + 'test': [], + 'demo': [], + 'auto_install': True, + 'installable': True, + 'post_init_hook': '_migration_script', +} diff --git a/addons/account_tax_exigible/models/__init__.py b/addons/account_tax_exigible/models/__init__.py new file mode 100644 index 00000000000..00c67c4c228 --- /dev/null +++ b/addons/account_tax_exigible/models/__init__.py @@ -0,0 +1 @@ +import account_move \ No newline at end of file diff --git a/addons/account_tax_exigible/models/account_move.py b/addons/account_tax_exigible/models/account_move.py new file mode 100644 index 00000000000..d0954d38ff1 --- /dev/null +++ b/addons/account_tax_exigible/models/account_move.py @@ -0,0 +1,103 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from openerp import fields, models, api, _ +from openerp.exceptions import UserError + + +class AccountMoveLine(models.Model): + _inherit = "account.move.line" + tax_exigible = fields.Boolean(string='Appears in VAT report', default=True, + help="Technical field used to mark a tax line as exigible in the vat report or not (only exigible journal items are displayed). By default all new journal items are directly exigible, but with the module account_tax_cash_basis, some will become exigible only when the payment is recorded.") + + @api.model + def create(self, vals, apply_taxes=True): + taxes = False + if vals.get('tax_line_id'): + taxes = [{'use_cash_basis': self.env['account.tax'].browse(vals['tax_line_id']).use_cash_basis}] + if vals.get('tax_ids'): + taxes = self.env['account.move.line'].resolve_2many_commands('tax_ids', vals['tax_ids']) + if taxes and any([tax['use_cash_basis'] for tax in taxes]) and not vals.get('tax_exigible'): + vals['tax_exigible'] = False + return super(AccountMoveLine, self).create(vals, apply_taxes=apply_taxes) + +class AccountPartialReconcileCashBasis(models.Model): + _inherit = 'account.partial.reconcile' + + def create_tax_cash_basis_entry(self, value_before_reconciliation): + # Search in account_move if we have any taxes account move lines + tax_group = {} + total_by_cash_basis_account = {} + line_to_create = [] + for move in (self.debit_move_id.move_id, self.credit_move_id.move_id): + for line in move.line_ids: + #TOCHECK: normal and cash basis taxes shoudn't be mixed together (on the same invoice line for example) as it will + #create reporting issues. Not sure of the behavior to implement in that case, though. + if not line.tax_exigible: + # amount to write is the current cash_basis amount minus the one before the reconciliation + matched_percentage = value_before_reconciliation[move.id] + amount = (line.credit_cash_basis - line.debit_cash_basis) - (line.credit - line.debit) * matched_percentage + if line.tax_line_id and line.tax_line_id.use_cash_basis: + # group by line account + acc = line.account_id.id + if tax_group.get(acc, False): + tax_group[acc] += amount + else: + tax_group[acc] = amount + # Group by cash basis account and tax + acc = line.tax_line_id.cash_basis_account.id + key = (acc, line.tax_line_id.id) + if key in total_by_cash_basis_account: + total_by_cash_basis_account[key] += amount + else: + total_by_cash_basis_account[key] = amount + for tax in line.tax_ids: + if tax.use_cash_basis: + line_to_create.append((0, 0, { + 'name': '/', + 'debit': line.debit_cash_basis - line.debit * matched_percentage, + 'credit': line.credit_cash_basis - line.credit * matched_percentage, + 'account_id': line.account_id.id, + 'tax_ids': [(6, 0, [tax.id])], + 'tax_exigible': True, + })) + line_to_create.append((0, 0, { + 'name': '/', + 'credit': line.debit_cash_basis - line.debit * matched_percentage, + 'debit': line.credit_cash_basis - line.credit * matched_percentage, + 'account_id': line.account_id.id, + })) + + for k, v in tax_group.items(): + line_to_create.append((0, 0, { + 'name': '/', + 'debit': v if v > 0 else 0.0, + 'credit': abs(v) if v < 0 else 0.0, + 'account_id': k, + })) + + # Create counterpart vals + for key, v in total_by_cash_basis_account.items(): + k, tax_id = key + line_to_create.append((0, 0, { + 'name': '/', + 'debit': abs(v) if v < 0 else 0.0, + 'credit': v if v > 0 else 0.0, + 'account_id': k, + 'tax_line_id': tax_id, + 'tax_exigible': True, + })) + + # Create move + if len(line_to_create) > 0: + # Check if company_journal for cash basis is set if not, raise exception + if not self.company_id.tax_cash_basis_journal_id: + raise UserError(_('There is no tax cash basis journal defined ' \ + 'for this company: "%s" \nConfigure it in Accounting/Configuration/Settings') % \ + (self.company_id.name)) + move = self.env['account.move'].with_context(dont_create_taxes=True).create({ + 'journal_id': self.company_id.tax_cash_basis_journal_id.id, + 'line_ids': line_to_create, + 'tax_cash_basis_rec_id': self.id}) + # post move + move.post()