odoo/addons/account_tax_cash_basis/models/account_partial_reconcile.py

144 lines
7.2 KiB
Python

# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import api, models, _
from odoo.exceptions import UserError
from odoo.tools import float_is_zero
class AccountPartialReconcileCashBasis(models.Model):
_inherit = 'account.partial.reconcile'
def _get_tax_cash_basis_base_account(self, line, tax):
''' Get the account of lines that will contain the base amount of taxes.
:param line: An account.move.line record
:param tax: An account.tax record
:return: An account record
'''
return line.account_id
def _get_tax_cash_basis_lines(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 = []
move_date = self.debit_move_id.date
for move in (self.debit_move_id.move_id, self.credit_move_id.move_id):
if move_date < move.date:
move_date = move.date
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.
# amount to write is the current cash_basis amount minus the one before the reconciliation
currency_id = line.currency_id or line.company_id.currency_id
matched_percentage = value_before_reconciliation[move.id]
amount = currency_id.round((line.credit_cash_basis - line.debit_cash_basis) - (line.credit - line.debit) * matched_percentage)
if not float_is_zero(amount, precision_rounding=currency_id.rounding) and not line.tax_exigible:
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
if not acc:
raise UserError(_('Please configure a Tax Received Account for tax %s') % line.tax_line_id.name)
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
if any([tax.use_cash_basis for tax in line.tax_ids]):
for tax in line.tax_ids:
account_id = self._get_tax_cash_basis_base_account(line, tax)
line_to_create.append((0, 0, {
'name': '/',
'debit': currency_id.round(line.debit_cash_basis - line.debit * matched_percentage),
'credit': currency_id.round(line.credit_cash_basis - line.credit * matched_percentage),
'account_id': account_id.id,
'tax_ids': [(6, 0, [tax.id])],
'tax_exigible': True,
}))
line_to_create.append((0, 0, {
'name': '/',
'credit': currency_id.round(line.debit_cash_basis - line.debit * matched_percentage),
'debit': currency_id.round(line.credit_cash_basis - line.credit * matched_percentage),
'account_id': account_id.id,
'tax_exigible': True,
}))
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,
'tax_exigible': True,
}))
# Create counterpart vals
for key, v in total_by_cash_basis_account.items():
k, tax_id = key
# Only entries with cash flow must be created
if not self.company_id.currency_id.is_zero(v):
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,
}))
return line_to_create, move_date
def create_tax_cash_basis_entry(self, value_before_reconciliation):
line_to_create, move_date = self._get_tax_cash_basis_lines(value_before_reconciliation)
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_vals = {
'journal_id': self.company_id.tax_cash_basis_journal_id.id,
'line_ids': line_to_create,
'tax_cash_basis_rec_id': self.id
}
# The move date should be the maximum date between payment and invoice (in case
# of payment in advance). However, we should make sure the move date is not
# recorded after the period lock date as the tax statement for this period is
# probably already sent to the estate.
if move_date > self.company_id.period_lock_date:
move_vals['date'] = move_date
move = self.env['account.move'].create(move_vals)
# post move
move.post()
@api.model
def create(self, vals):
aml = []
if vals.get('debit_move_id', False):
aml.append(vals['debit_move_id'])
if vals.get('credit_move_id', False):
aml.append(vals['credit_move_id'])
# Get value of matched percentage from both move before reconciliating
lines = self.env['account.move.line'].browse(aml)
value_before_reconciliation = {}
for line in lines:
if not value_before_reconciliation.get(line.move_id.id, False):
value_before_reconciliation[line.move_id.id] = line.move_id.matched_percentage
# Reconcile
res = super(AccountPartialReconcileCashBasis, self).create(vals)
# eventually create a tax cash basis entry
res.create_tax_cash_basis_entry(value_before_reconciliation)
return res
@api.multi
def unlink(self):
move = self.env['account.move'].search([('tax_cash_basis_rec_id', 'in', self._ids)])
move.reverse_moves()
super(AccountPartialReconcileCashBasis, self).unlink()