[IMP] account: allow mixing cash basis and non-cash basis taxes on the same line

The tax_exigible field of account.move.line made it impossible to mix 'on_payment' and 'on_invoice' taxes on the same invoice line, as the base line couldn't be be tax exigible and non-tax exigible at the same time. However, this use case is needed by some countries in order to implement withholding taxes.

To solve that, we totally remove the tax_exigible field from account.move.line and instead compute it on the fly with a domain (also passed to the query_get for SQL queries). An 'always_tax_exigible' stored computed field is also added on account.move, in order to still allow (like before) putting cash basis taxes on a miscellaneous operation without any payable/receivable line and still see it become exigible without needing any payment.

[IMP] account: improve cash basis traceability

A smart button is now available on invoices generating cash basis entries, so see them all at once. The move originally creating cash basis entries is also shown as a field on them.

Task: 2457374
Part-of: odoo/odoo#74138
This commit is contained in:
oco-odoo
2021-09-02 14:15:12 +00:00
parent 0f90e80f9e
commit d8c4332c8d
13 changed files with 208 additions and 205 deletions
+141 -48
View File
@@ -250,10 +250,22 @@ class AccountMove(models.Model):
string='Tax Cash Basis Entry of',
help="Technical field used to keep track of the tax cash basis reconciliation. "
"This is needed when cancelling the source: it will post the inverse journal entry to cancel that part too.")
tax_cash_basis_move_id = fields.Many2one(
tax_cash_basis_origin_move_id = fields.Many2one(
comodel_name='account.move',
string="Origin Tax Cash Basis Entry",
string="Cash Basis Origin",
readonly=1,
help="The journal entry from which this tax cash basis journal entry has been created.")
tax_cash_basis_created_move_ids = fields.One2many(
string="Cash Basis Entries",
comodel_name='account.move',
inverse_name='tax_cash_basis_origin_move_id',
help="The cash basis entries created from the taxes on this entry, when reconciling its lines."
)
always_tax_exigible = fields.Boolean(
compute='_compute_always_tax_exigible',
store=True,
help="Technical field used by cash basis taxes, telling the lines of the move are always exigible. "
"This happens if the move contains no payable or receivable line.")
# ==== Auto-post feature fields ====
auto_post = fields.Boolean(string='Post Automatically', default=False, copy=False,
@@ -666,6 +678,7 @@ class AccountMove(models.Model):
partner=base_line.partner_id,
is_refund=is_refund,
handle_price_include=handle_price_include,
include_caba_tags=move.always_tax_exigible,
)
taxes_map = {}
@@ -703,7 +716,6 @@ class AccountMove(models.Model):
if not recompute_tax_base_amount:
line.tax_tag_ids = compute_all_vals['base_tags'] or [(5, 0, 0)]
tax_exigible = True
for tax_vals in compute_all_vals['taxes']:
grouping_dict = self._get_tax_grouping_key_from_base_line(line, tax_vals)
grouping_key = _serialize_tax_grouping_key(grouping_dict)
@@ -711,9 +723,6 @@ class AccountMove(models.Model):
tax_repartition_line = self.env['account.tax.repartition.line'].browse(tax_vals['tax_repartition_line_id'])
tax = tax_repartition_line.invoice_tax_id or tax_repartition_line.refund_tax_id
if tax.tax_exigibility == 'on_payment':
tax_exigible = False
taxes_map_entry = taxes_map.setdefault(grouping_key, {
'tax_line': None,
'amount': 0.0,
@@ -723,8 +732,6 @@ class AccountMove(models.Model):
taxes_map_entry['amount'] += tax_vals['amount']
taxes_map_entry['tax_base_amount'] += self._get_base_amount_to_display(tax_vals['base'], tax_repartition_line, tax_vals['group'])
taxes_map_entry['grouping_dict'] = grouping_dict
if not recompute_tax_base_amount:
line.tax_exigible = tax_exigible
# ==== Process taxes_map ====
for taxes_map_entry in taxes_map.values():
@@ -782,7 +789,6 @@ class AccountMove(models.Model):
'company_currency_id': line.company_currency_id.id,
'tax_base_amount': tax_base_amount,
'exclude_from_invoice_tab': True,
'tax_exigible': tax.tax_exigibility == 'on_invoice',
**taxes_map_entry['grouping_dict'],
})
@@ -801,19 +807,6 @@ class AccountMove(models.Model):
return -base_amount
return base_amount
def update_lines_tax_exigibility(self):
if all(account.user_type_id.type not in {'payable', 'receivable'} for account in self.mapped('line_ids.account_id')):
self.line_ids.write({'tax_exigible': True})
else:
tax_lines_caba = self.line_ids.filtered(lambda x: x.tax_line_id.tax_exigibility == 'on_payment')
base_lines_caba = self.line_ids.filtered(lambda x: any(tax.tax_exigibility == 'on_payment'
or (tax.amount_type == 'group'
and 'on_payment' in tax.mapped('children_tax_ids.tax_exigibility'))
for tax in x.tax_ids))
caba_lines = tax_lines_caba + base_lines_caba
caba_lines.write({'tax_exigible': False})
(self.line_ids - caba_lines).write({'tax_exigible': True})
def _recompute_cash_rounding_lines(self):
''' Handle the cash rounding feature on invoices.
@@ -880,7 +873,6 @@ class AccountMove(models.Model):
'name': _('%s (rounding)', biggest_tax_line.name),
'account_id': biggest_tax_line.account_id.id,
'tax_repartition_line_id': biggest_tax_line.tax_repartition_line_id.id,
'tax_exigible': biggest_tax_line.tax_exigible,
'exclude_from_invoice_tab': True,
})
@@ -1696,6 +1688,16 @@ class AccountMove(models.Model):
else:
move.tax_lock_date_message = False
@api.depends('line_ids.account_id.internal_type')
def _compute_always_tax_exigible(self):
for record in self:
# We need to check is_invoice as well because always_tax_exigible is used to
# set the tags as well, during the encoding. So, if no receivable/payable
# line has been created yet, the invoice would be detected as always exigible,
# and set the tags on some lines ; which would be wrong.
record.always_tax_exigible = not record.is_invoice(True) \
and not record._collect_tax_cash_basis_values()
@api.depends('restrict_mode_hash_table', 'state')
def _compute_show_reset_to_draft_button(self):
for move in self:
@@ -2008,11 +2010,7 @@ class AccountMove(models.Model):
raise UserError(_('You cannot create a move already in the posted state. Please create a draft move and post it after.'))
vals_list = self._move_autocomplete_invoice_lines_create(vals_list)
rslt = super(AccountMove, self).create(vals_list)
for i, vals in enumerate(vals_list):
if 'line_ids' in vals:
rslt[i].update_lines_tax_exigibility()
return rslt
return super(AccountMove, self).create(vals_list)
def write(self, vals):
for move in self:
@@ -2060,10 +2058,8 @@ class AccountMove(models.Model):
res |= super(AccountMove, move).write(vals_hashing)
# Ensure the move is still well balanced.
if 'line_ids' in vals:
if self._context.get('check_move_validity', True):
self._check_balanced()
self.update_lines_tax_exigibility()
if 'line_ids' in vals and self._context.get('check_move_validity', True):
self._check_balanced()
self._synchronize_business_models(set(vals.keys()))
@@ -2142,7 +2138,14 @@ class AccountMove(models.Model):
- Compute the lines to be processed and the amounts needed to compute a percentage.
:return: A dictionary:
* move: The current account.move record passed as parameter.
* to_process_lines: An account.move.line recordset being not exigible on the tax report.
* to_process_lines: A tuple (caba_treatment, line) where:
- caba_treatment is either 'tax' or 'base', depending on what should
be considered on the line when generating the caba entry.
For example, a line with tax_ids=caba and tax_line_id=non_caba
will have a 'base' caba treatment, as we only want to treat its base
part in the caba entry (the tax part is already exigible on the invoice)
- line is an account.move.line record being not exigible on the tax report.
* currency: The currency on which the percentage has been computed.
* total_balance: sum(payment_term_lines.mapped('balance').
* total_residual: sum(payment_term_lines.mapped('amount_residual').
@@ -2154,7 +2157,7 @@ class AccountMove(models.Model):
values = {
'move': self,
'to_process_lines': self.env['account.move.line'],
'to_process_lines': [],
'total_balance': 0.0,
'total_residual': 0.0,
'total_amount_currency': 0.0,
@@ -2167,17 +2170,20 @@ class AccountMove(models.Model):
if line.account_internal_type in ('receivable', 'payable'):
sign = 1 if line.balance > 0.0 else -1
currencies.add(line.currency_id or line.company_currency_id)
currencies.add(line.currency_id)
has_term_lines = True
values['total_balance'] += sign * line.balance
values['total_residual'] += sign * line.amount_residual
values['total_amount_currency'] += sign * line.amount_currency
values['total_residual_currency'] += sign * line.amount_residual_currency
elif not line.tax_exigible:
elif line.tax_line_id.tax_exigibility == 'on_payment':
values['to_process_lines'].append(('tax', line))
currencies.add(line.currency_id)
values['to_process_lines'] += line
currencies.add(line.currency_id or line.company_currency_id)
elif 'on_payment' in line.tax_ids.mapped('tax_exigibility'):
values['to_process_lines'].append(('base', line))
currencies.add(line.currency_id)
if not values['to_process_lines'] or not has_term_lines:
return None
@@ -2189,7 +2195,7 @@ class AccountMove(models.Model):
# Don't support the case where there is multiple involved currencies.
return None
# Determine is the move is now fully paid.
# Determine whether the move is now fully paid.
values['is_fully_paid'] = self.company_id.currency_id.is_zero(values['total_residual']) \
or values['currency'].is_zero(values['total_residual_currency'])
@@ -2560,6 +2566,17 @@ class AccountMove(models.Model):
'views': [(False, 'form')],
}
def open_created_caba_entries(self):
self.ensure_one()
return {
'type': 'ir.actions.act_window',
'name': _("Cash Basis Entries"),
'res_model': 'account.move',
'view_mode': 'form',
'domain': [('id', 'in', self.tax_cash_basis_created_move_ids.ids)],
'views': [(self.env.ref('account.view_move_tree').id, 'tree'), (False, 'form')],
}
@api.model
def message_new(self, msg_dict, custom_values=None):
# OVERRIDE
@@ -2798,7 +2815,12 @@ class AccountMove(models.Model):
for move in self:
if move in move.line_ids.mapped('full_reconcile_id.exchange_move_id'):
raise UserError(_('You cannot reset to draft an exchange difference journal entry.'))
if move.tax_cash_basis_rec_id:
if move.tax_cash_basis_rec_id or move.tax_cash_basis_origin_move_id:
# If the reconciliation was undone, move.tax_cash_basis_rec_id will be empty;
# but we still don't want to allow setting the caba entry to draft
# (it'll have been reversed automatically, so no manual intervention is required),
# so we also check tax_cash_basis_origin_move_id, which stays unchanged
# (we need both, as tax_cash_basis_origin_move_id did not exist in older versions).
raise UserError(_('You cannot reset to draft a tax cash basis journal entry.'))
if move.restrict_mode_hash_table and move.state == 'posted' and move.id not in excluded_move_ids:
raise UserError(_('You cannot modify a posted entry of this journal because it is in strict mode.'))
@@ -3280,10 +3302,6 @@ class AccountMoveLine(models.Model):
help='technical field for widget tax-group-custom-field')
tax_base_amount = fields.Monetary(string="Base Amount", store=True, readonly=True,
currency_field='company_currency_id')
tax_exigible = fields.Boolean(string='Appears in VAT report', default=True, readonly=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 feature cash_basis"
" on taxes, some will become exigible only when the payment is recorded.")
tax_repartition_line_id = fields.Many2one(comodel_name='account.tax.repartition.line',
string="Originator Tax Distribution Line", ondelete='restrict', readonly=True,
check_company=True,
@@ -3761,6 +3779,40 @@ class AccountMoveLine(models.Model):
vals = {'price_unit': 0.0}
return vals
@api.model
def _get_tax_exigible_domain(self):
""" Returns a domain to be used to identify the move lines that are allowed
to be taken into account in the tax report.
"""
return [
# Lines on moves without any payable or receivable line are always exigible
'|', ('move_id.always_tax_exigible', '=', True),
# Lines with only tags are always exigible
'|', '&', ('tax_line_id', '=', False), ('tax_ids', '=', False),
# Lines from CABA entries are always exigible
'|', ('move_id.tax_cash_basis_rec_id', '!=', False),
# Lines from non-CABA taxes are always exigible
'|', ('tax_line_id.tax_exigibility', '!=', 'on_payment'),
('tax_ids.tax_exigibility', '!=', 'on_payment'), # So: exigible if at least one tax from tax_ids isn't on_payment
]
def belongs_to_refund(self):
""" Tells whether or not this move line corresponds to a refund operation.
"""
self.ensure_one()
if self.tax_repartition_line_id:
return self.tax_repartition_line_id.refund_tax_id
elif self.move_id.move_type == 'entry':
tax_type = self.tax_ids[0].type_tax_use if self.tax_ids else None
return (tax_type == 'sale' and self.debit) or (tax_type == 'purchase' and self.credit)
return self.move_id.move_type in ('in_refund', 'out_refund')
# -------------------------------------------------------------------------
# ONCHANGE METHODS
# -------------------------------------------------------------------------
@@ -4077,6 +4129,47 @@ class AccountMoveLine(models.Model):
"You can just change some non legal fields or you must unreconcile first.\n"
"Journal Entry (id): %s (%s)") % (line.move_id.name, line.move_id.id))
@api.constrains('tax_ids', 'tax_repartition_line_id')
def _check_caba_non_caba_shared_tags(self):
""" When mixing cash basis and non cash basis taxes, it is important
that those taxes don't share tags on the repartition creating
a single account.move.line.
Shared tags in this context cannot work, as the tags would need to
be present on both the invoice and cash basis move, leading to the same
base amount to be taken into account twice; which is wrong.This is
why we don't support that. A workaround may be provided by the use of
a group of taxes, whose children are type_tax_use=None, and only one
of them uses the common tag.
Note that taxes of the same exigibility are allowed to share tags.
"""
def get_base_repartition(base_aml, taxes):
if not taxes:
return self.env['account.tax.repartition.line']
is_refund = base_aml.belongs_to_refund()
repartition_field = is_refund and 'refund_repartition_line_ids' or 'invoice_repartition_line_ids'
return taxes.mapped(repartition_field)
for aml in self:
caba_taxes = aml.tax_ids.filtered(lambda x: x.tax_exigibility == 'on_payment')
non_caba_taxes = aml.tax_ids - caba_taxes
caba_base_tags = get_base_repartition(aml, caba_taxes).filtered(lambda x: x.repartition_type == 'base').tag_ids
non_caba_base_tags = get_base_repartition(aml, non_caba_taxes).filtered(lambda x: x.repartition_type == 'base').tag_ids
common_tags = caba_base_tags & non_caba_base_tags
if not common_tags:
# When a tax is affecting another one with different tax exigibility, tags cannot be shared either.
tax_tags = aml.tax_repartition_line_id.tag_ids
comparison_tags = non_caba_base_tags if aml.tax_repartition_line_id.tax_id.tax_exigibility == 'on_payment' else caba_base_tags
common_tags = tax_tags & comparison_tags
if common_tags:
raise ValidationError(_("Taxes exigible on payment and on invoice cannot be mixed on the same journal item if they share some tag."))
# -------------------------------------------------------------------------
# LOW-LEVEL METHODS
# -------------------------------------------------------------------------
@@ -4647,7 +4740,7 @@ class AccountMoveLine(models.Model):
# to compute the residual amount for each of them.
# ==========================================================================
for line in move_values['to_process_lines']:
for caba_treatment, line in move_values['to_process_lines']:
vals = {
'currency_id': line.currency_id.id,
@@ -4658,7 +4751,7 @@ class AccountMoveLine(models.Model):
'credit': line.credit,
}
if line.tax_repartition_line_id:
if caba_treatment == 'tax':
# Tax line.
grouping_key = self.env['account.partial.reconcile']._get_cash_basis_tax_line_grouping_key_from_record(line)
if grouping_key in account_vals_to_fix:
@@ -4678,7 +4771,7 @@ class AccountMoveLine(models.Model):
'tax_base_amount': line.tax_base_amount,
'tax_repartition_line_id': line.tax_repartition_line_id.id,
}
elif line.tax_ids:
elif caba_treatment == 'base':
# Base line.
account_to_fix = line.company_id.account_cash_basis_base_account_id
if not account_to_fix:
@@ -4702,7 +4795,7 @@ class AccountMoveLine(models.Model):
# in order to retrieve the residual balance of each involved transfer account.
# ==========================================================================
cash_basis_moves = self.env['account.move'].search([('tax_cash_basis_move_id', '=', move.id)])
cash_basis_moves = self.env['account.move'].search([('tax_cash_basis_origin_move_id', '=', move.id)])
for line in cash_basis_moves.line_ids:
grouping_key = None
if line.tax_repartition_line_id:
@@ -387,7 +387,11 @@ class AccountMoveLine(models.Model):
comp_curr.decimal_places AS comp_curr_prec,
curr.id AS currency_id,
curr.decimal_places AS curr_prec,
tax_line.tax_exigible,
(
tax.tax_exigibility != 'on_payment'
OR tax_move.tax_cash_basis_rec_id IS NOT NULL
OR tax_move.always_tax_exigible
) AS tax_exigible,
base_line.account_id AS base_account_id,
sub.base_amount,
@@ -423,6 +427,8 @@ class AccountMoveLine(models.Model):
FROM base_tax_matching_base_amounts sub
JOIN account_move_line tax_line ON
tax_line.id = sub.tax_line_id
JOIN account_move tax_move ON
tax_move.id = tax_line.move_id
JOIN account_move_line base_line ON
base_line.id = sub.base_line_id
JOIN account_tax tax ON
@@ -230,6 +230,9 @@ class AccountPartialReconcile(models.Model):
account.move.line.
'''
account = base_line.company_id.account_cash_basis_base_account_id or base_line.account_id
tax_ids = base_line.tax_ids.filtered(lambda x: x.tax_exigibility == 'on_payment')
is_refund = base_line.belongs_to_refund()
return {
'name': base_line.move_id.name,
'debit': balance if balance > 0.0 else 0.0,
@@ -238,9 +241,8 @@ class AccountPartialReconcile(models.Model):
'currency_id': base_line.currency_id.id,
'partner_id': base_line.partner_id.id,
'account_id': account.id,
'tax_ids': [(6, 0, base_line.tax_ids.ids)],
'tax_tag_ids': [(6, 0, base_line.tax_tag_ids.ids)],
'tax_exigible': True,
'tax_ids': [Command.set(tax_ids.ids)],
'tax_tag_ids': [Command.set(tax_ids.get_tax_tags(is_refund, 'base').ids)],
'tax_tag_invert': base_line.tax_tag_invert,
}
@@ -261,7 +263,6 @@ class AccountPartialReconcile(models.Model):
'amount_currency': -cb_base_line_vals['amount_currency'],
'currency_id': cb_base_line_vals['currency_id'],
'partner_id': cb_base_line_vals['partner_id'],
'tax_exigible': True,
}
@api.model
@@ -274,19 +275,22 @@ class AccountPartialReconcile(models.Model):
:return: A python dictionary that could be passed to the create method of
account.move.line.
'''
tax_ids = tax_line.tax_ids.filtered(lambda x: x.tax_exigibility == 'on_payment')
base_tags = tax_ids.get_tax_tags(tax_line.tax_repartition_line_id.refund_tax_id, 'base')
all_tags = base_tags + tax_line.tax_repartition_line_id.tag_ids
return {
'name': tax_line.name,
'debit': balance if balance > 0.0 else 0.0,
'credit': -balance if balance < 0.0 else 0.0,
'tax_base_amount': tax_line.tax_base_amount,
'tax_repartition_line_id': tax_line.tax_repartition_line_id.id,
'tax_ids': [(6, 0, tax_line.tax_ids.ids)],
'tax_tag_ids': [(6, 0, tax_line.tax_tag_ids.ids)],
'tax_ids': [Command.set(tax_ids.ids)],
'tax_tag_ids': [Command.set(all_tags.ids)],
'account_id': tax_line.tax_repartition_line_id.account_id.id or tax_line.account_id.id,
'amount_currency': amount_currency,
'currency_id': tax_line.currency_id.id,
'partner_id': tax_line.partner_id.id,
'tax_exigible': True,
# No need to set tax_tag_invert as on the base line; it will be computed from the repartition line
}
@@ -308,7 +312,6 @@ class AccountPartialReconcile(models.Model):
'amount_currency': -cb_tax_line_vals['amount_currency'],
'currency_id': cb_tax_line_vals['currency_id'],
'partner_id': cb_tax_line_vals['partner_id'],
'tax_exigible': True,
}
@api.model
@@ -317,12 +320,13 @@ class AccountPartialReconcile(models.Model):
:param base_line_vals: The values to create a new account.move.line record.
:return: The grouping key as a tuple.
'''
tax_ids = base_line_vals['tax_ids'][0][2] # Decode [(6, 0, [...])] command
base_taxes = self.env['account.tax'].browse(tax_ids)
return (
base_line_vals['currency_id'],
base_line_vals['partner_id'],
base_line_vals['account_id'],
tuple(base_line_vals['tax_ids'][0][2]), # Decode [(6, 0, [...])] command
tuple(base_line_vals['tax_tag_ids'][0][2]), # Decode [(6, 0, [...])] command
tuple(base_taxes.filtered(lambda x: x.tax_exigibility == 'on_payment').ids),
)
@api.model
@@ -336,8 +340,7 @@ class AccountPartialReconcile(models.Model):
base_line.currency_id.id,
base_line.partner_id.id,
(account or base_line.account_id).id,
tuple(base_line.tax_ids.ids),
tuple(base_line.tax_tag_ids.ids),
tuple(base_line.tax_ids.filtered(lambda x: x.tax_exigibility == 'on_payment').ids),
)
@api.model
@@ -346,12 +349,13 @@ class AccountPartialReconcile(models.Model):
:param tax_line_vals: The values to create a new account.move.line record.
:return: The grouping key as a tuple.
'''
tax_ids = tax_line_vals['tax_ids'][0][2] # Decode [(6, 0, [...])] command
base_taxes = self.env['account.tax'].browse(tax_ids)
return (
tax_line_vals['currency_id'],
tax_line_vals['partner_id'],
tax_line_vals['account_id'],
tuple(tax_line_vals['tax_ids'][0][2]), # Decode [(6, 0, [...])] command
tuple(tax_line_vals['tax_tag_ids'][0][2]), # Decode [(6, 0, [...])] command
tuple(base_taxes.filtered(lambda x: x.tax_exigibility == 'on_payment').ids),
tax_line_vals['tax_repartition_line_id'],
)
@@ -366,8 +370,7 @@ class AccountPartialReconcile(models.Model):
tax_line.currency_id.id,
tax_line.partner_id.id,
(account or tax_line.account_id).id,
tuple(tax_line.tax_ids.ids),
tuple(tax_line.tax_tag_ids.ids),
tuple(tax_line.tax_ids.filtered(lambda x: x.tax_exigibility == 'on_payment').ids),
tax_line.tax_repartition_line_id.id,
)
@@ -394,15 +397,15 @@ class AccountPartialReconcile(models.Model):
'journal_id': partial.company_id.tax_cash_basis_journal_id.id,
'line_ids': [],
'tax_cash_basis_rec_id': partial.id,
'tax_cash_basis_move_id': move.id,
'fiscal_position_id': move.fiscal_position_id,
'tax_cash_basis_origin_move_id': move.id,
'fiscal_position_id': move.fiscal_position_id.id,
}
# Tracking of lines grouped all together.
# Used to reduce the number of generated lines and to avoid rounding issues.
partial_lines_to_create = {}
for line in move_values['to_process_lines']:
for caba_treatment, line in move_values['to_process_lines']:
# ==========================================================================
# Compute the balance of the current line on the cash basis entry.
@@ -424,12 +427,12 @@ class AccountPartialReconcile(models.Model):
# and well reported in the Tax Report.
# ==========================================================================
if line.tax_repartition_line_id:
if caba_treatment == 'tax':
# Tax line.
cb_line_vals = self._prepare_cash_basis_tax_line_vals(line, balance, amount_currency)
grouping_key = self._get_cash_basis_tax_line_grouping_key_from_vals(cb_line_vals)
elif line.tax_ids:
elif caba_treatment == 'base':
# Base line.
cb_line_vals = self._prepare_cash_basis_base_line_vals(line, balance, amount_currency)
@@ -448,7 +451,7 @@ class AccountPartialReconcile(models.Model):
'amount_currency': aggregated_vals['amount_currency'] + cb_line_vals['amount_currency'],
})
if line.tax_repartition_line_id:
if caba_treatment == 'tax':
aggregated_vals.update({
'tax_base_amount': aggregated_vals['tax_base_amount'] + cb_line_vals['tax_base_amount'],
})
@@ -457,7 +460,7 @@ class AccountPartialReconcile(models.Model):
partial_lines_to_create[grouping_key] = {
'vals': cb_line_vals,
}
if line.tax_repartition_line_id:
if caba_treatment == 'tax':
partial_lines_to_create[grouping_key].update({
'tax_line': line,
})
@@ -338,7 +338,6 @@ class AccountReconcileModel(models.Model):
'credit': balance < 0 and -balance or 0,
'analytic_account_id': tax.analytic and base_line_dict['analytic_account_id'],
'analytic_tag_ids': tax.analytic and base_line_dict['analytic_tag_ids'],
'tax_exigible': tax_res['tax_exigibility'],
'tax_repartition_line_id': tax_res['tax_repartition_line_id'],
'tax_ids': [(6, 0, tax_res['tax_ids'])],
'tax_tag_ids': [(6, 0, tax_res['tag_ids'])],
+18 -3
View File
@@ -359,7 +359,7 @@ class AccountTax(models.Model):
rep_lines = self.mapped(is_refund and 'refund_repartition_line_ids' or 'invoice_repartition_line_ids')
return rep_lines.filtered(lambda x: x.repartition_type == repartition_type).mapped('tag_ids')
def compute_all(self, price_unit, currency=None, quantity=1.0, product=None, partner=None, is_refund=False, handle_price_include=True):
def compute_all(self, price_unit, currency=None, quantity=1.0, product=None, partner=None, is_refund=False, handle_price_include=True, include_caba_tags=False):
""" Returns all information required to apply taxes (in self + their children in case of a tax group).
We consider the sequence of the parent for group of taxes.
Eg. considering letters as taxes and alphabetic order as sequence :
@@ -569,6 +569,10 @@ class AccountTax(models.Model):
subsequent_tags = self.env['account.account.tag']
if tax.include_base_amount:
subsequent_taxes = taxes[i+1:].filtered('is_base_affected')
if not include_caba_tags:
subsequent_taxes = subsequent_taxes.filtered(lambda x: x.tax_exigibility != 'on_payment')
subsequent_tags = subsequent_taxes.get_tax_tags(is_refund, 'base')
# Compute the tax line amounts by multiplying each factor with the tax amount.
@@ -590,6 +594,11 @@ class AccountTax(models.Model):
line_amount += rounding_error
nber_rounding_steps -= 1
if not include_caba_tags and tax.tax_exigibility == 'on_payment':
repartition_line_tags = self.env['account.account.tag']
else:
repartition_line_tags = repartition_line.tag_ids
taxes_vals.append({
'id': tax.id,
'name': partner and tax.with_context(lang=partner.lang).name or tax.name,
@@ -602,7 +611,7 @@ class AccountTax(models.Model):
'tax_exigibility': tax.tax_exigibility,
'tax_repartition_line_id': repartition_line.id,
'group': groups_map.get(tax),
'tag_ids': (repartition_line.tag_ids + subsequent_tags).ids,
'tag_ids': (repartition_line_tags + subsequent_tags).ids,
'tax_ids': subsequent_taxes.ids,
})
@@ -618,8 +627,14 @@ class AccountTax(models.Model):
total_included += factorized_tax_amount
i += 1
base_taxes_for_tags = taxes
if not include_caba_tags:
base_taxes_for_tags = base_taxes_for_tags.filtered(lambda x: x.tax_exigibility != 'on_payment')
base_rep_lines = base_taxes_for_tags.mapped(is_refund and 'refund_repartition_line_ids' or 'invoice_repartition_line_ids').filtered(lambda x: x.repartition_type == 'base')
return {
'base_tags': taxes.mapped(is_refund and 'refund_repartition_line_ids' or 'invoice_repartition_line_ids').filtered(lambda x: x.repartition_type == 'base').mapped('tag_ids').ids,
'base_tags': base_rep_lines.tag_ids.ids,
'taxes': taxes_vals,
'total_excluded': sign * total_excluded,
'total_included': sign * currency.round(total_included),
+2 -1
View File
@@ -391,7 +391,8 @@ class AccountTestInvoicingCommon(TransactionCase):
line_form.price_unit = amount
if taxes:
line_form.tax_ids.clear()
line_form.tax_ids.add(taxes)
for tax in taxes:
line_form.tax_ids.add(tax)
rslt = move_form.save()
@@ -34,7 +34,6 @@ class TestAccountMoveInInvoiceOnchanges(AccountTestInvoicingCommon):
'debit': 800.0,
'credit': 0.0,
'date_maturity': False,
'tax_exigible': True,
}
cls.product_line_vals_2 = {
'name': cls.product_b.name,
@@ -54,7 +53,6 @@ class TestAccountMoveInInvoiceOnchanges(AccountTestInvoicingCommon):
'debit': 160.0,
'credit': 0.0,
'date_maturity': False,
'tax_exigible': True,
}
cls.tax_line_vals_1 = {
'name': cls.tax_purchase_a.name,
@@ -74,7 +72,6 @@ class TestAccountMoveInInvoiceOnchanges(AccountTestInvoicingCommon):
'debit': 144.0,
'credit': 0.0,
'date_maturity': False,
'tax_exigible': True,
}
cls.tax_line_vals_2 = {
'name': cls.tax_purchase_b.name,
@@ -94,7 +91,6 @@ class TestAccountMoveInInvoiceOnchanges(AccountTestInvoicingCommon):
'debit': 24.0,
'credit': 0.0,
'date_maturity': False,
'tax_exigible': True,
}
cls.term_line_vals_1 = {
'name': '',
@@ -114,7 +110,6 @@ class TestAccountMoveInInvoiceOnchanges(AccountTestInvoicingCommon):
'debit': 0.0,
'credit': 1128.0,
'date_maturity': fields.Date.from_string('2019-01-01'),
'tax_exigible': True,
}
cls.move_vals = {
'partner_id': cls.partner_a.id,
@@ -759,7 +754,6 @@ class TestAccountMoveInInvoiceOnchanges(AccountTestInvoicingCommon):
'price_subtotal': 800.0,
'price_total': 1176.0,
'tax_ids': (self.tax_purchase_a + self.tax_armageddon).ids,
'tax_exigible': False,
},
self.product_line_vals_2,
self.tax_line_vals_1,
@@ -782,7 +776,6 @@ class TestAccountMoveInInvoiceOnchanges(AccountTestInvoicingCommon):
'debit': 64.0,
'credit': 0.0,
'date_maturity': False,
'tax_exigible': False,
},
{
'name': child_tax_1.name,
@@ -802,7 +795,6 @@ class TestAccountMoveInInvoiceOnchanges(AccountTestInvoicingCommon):
'debit': 96.0,
'credit': 0.0,
'date_maturity': False,
'tax_exigible': False,
},
{
'name': child_tax_2.name,
@@ -822,7 +814,6 @@ class TestAccountMoveInInvoiceOnchanges(AccountTestInvoicingCommon):
'debit': 96.0,
'credit': 0.0,
'date_maturity': False,
'tax_exigible': False,
},
{
**self.term_line_vals_1,
@@ -878,7 +869,6 @@ class TestAccountMoveInInvoiceOnchanges(AccountTestInvoicingCommon):
'debit': 0.01,
'credit': 0.0,
'date_maturity': False,
'tax_exigible': True,
},
{
**self.product_line_vals_1,
@@ -929,7 +919,6 @@ class TestAccountMoveInInvoiceOnchanges(AccountTestInvoicingCommon):
'debit': 0.0,
'credit': 0.04,
'date_maturity': False,
'tax_exigible': True,
},
{
**self.term_line_vals_1,
@@ -33,7 +33,6 @@ class TestAccountMoveInRefundOnchanges(AccountTestInvoicingCommon):
'debit': 0.0,
'credit': 800.0,
'date_maturity': False,
'tax_exigible': True,
}
cls.product_line_vals_2 = {
'name': cls.product_b.name,
@@ -53,7 +52,6 @@ class TestAccountMoveInRefundOnchanges(AccountTestInvoicingCommon):
'debit': 0.0,
'credit': 160.0,
'date_maturity': False,
'tax_exigible': True,
}
cls.tax_line_vals_1 = {
'name': cls.tax_purchase_a.name,
@@ -73,7 +71,6 @@ class TestAccountMoveInRefundOnchanges(AccountTestInvoicingCommon):
'debit': 0.0,
'credit': 144.0,
'date_maturity': False,
'tax_exigible': True,
}
cls.tax_line_vals_2 = {
'name': cls.tax_purchase_b.name,
@@ -93,7 +90,6 @@ class TestAccountMoveInRefundOnchanges(AccountTestInvoicingCommon):
'debit': 0.0,
'credit': 24.0,
'date_maturity': False,
'tax_exigible': True,
}
cls.term_line_vals_1 = {
'name': '',
@@ -113,7 +109,6 @@ class TestAccountMoveInRefundOnchanges(AccountTestInvoicingCommon):
'debit': 1128.0,
'credit': 0.0,
'date_maturity': fields.Date.from_string('2019-01-01'),
'tax_exigible': True,
}
cls.move_vals = {
'partner_id': cls.partner_a.id,
@@ -459,7 +454,6 @@ class TestAccountMoveInRefundOnchanges(AccountTestInvoicingCommon):
'price_subtotal': 800.0,
'price_total': 1176.0,
'tax_ids': (self.tax_purchase_a + self.tax_armageddon).ids,
'tax_exigible': False,
},
self.product_line_vals_2,
self.tax_line_vals_1,
@@ -482,7 +476,6 @@ class TestAccountMoveInRefundOnchanges(AccountTestInvoicingCommon):
'debit': 0.0,
'credit': 96.0,
'date_maturity': False,
'tax_exigible': False,
},
{
'name': child_tax_1.name,
@@ -502,7 +495,6 @@ class TestAccountMoveInRefundOnchanges(AccountTestInvoicingCommon):
'debit': 0.0,
'credit': 64.0,
'date_maturity': False,
'tax_exigible': False,
},
{
'name': child_tax_2.name,
@@ -522,7 +514,6 @@ class TestAccountMoveInRefundOnchanges(AccountTestInvoicingCommon):
'debit': 0.0,
'credit': 96.0,
'date_maturity': False,
'tax_exigible': False,
},
{
**self.term_line_vals_1,
@@ -578,7 +569,6 @@ class TestAccountMoveInRefundOnchanges(AccountTestInvoicingCommon):
'debit': 0.0,
'credit': 0.01,
'date_maturity': False,
'tax_exigible': True,
},
{
**self.product_line_vals_1,
@@ -629,7 +619,6 @@ class TestAccountMoveInRefundOnchanges(AccountTestInvoicingCommon):
'debit': 0.04,
'credit': 0.0,
'date_maturity': False,
'tax_exigible': True,
},
{
**self.term_line_vals_1,
@@ -38,7 +38,6 @@ class TestAccountMoveOutInvoiceOnchanges(AccountTestInvoicingCommon):
'debit': 0.0,
'credit': 1000.0,
'date_maturity': False,
'tax_exigible': True,
}
cls.product_line_vals_2 = {
'name': cls.product_b.name,
@@ -58,7 +57,6 @@ class TestAccountMoveOutInvoiceOnchanges(AccountTestInvoicingCommon):
'debit': 0.0,
'credit': 200.0,
'date_maturity': False,
'tax_exigible': True,
}
cls.tax_line_vals_1 = {
'name': cls.tax_sale_a.name,
@@ -78,7 +76,6 @@ class TestAccountMoveOutInvoiceOnchanges(AccountTestInvoicingCommon):
'debit': 0.0,
'credit': 180.0,
'date_maturity': False,
'tax_exigible': True,
}
cls.tax_line_vals_2 = {
'name': cls.tax_sale_b.name,
@@ -98,7 +95,6 @@ class TestAccountMoveOutInvoiceOnchanges(AccountTestInvoicingCommon):
'debit': 0.0,
'credit': 30.0,
'date_maturity': False,
'tax_exigible': True,
}
cls.term_line_vals_1 = {
'name': '',
@@ -118,7 +114,6 @@ class TestAccountMoveOutInvoiceOnchanges(AccountTestInvoicingCommon):
'debit': 1410.0,
'credit': 0.0,
'date_maturity': fields.Date.from_string('2019-01-01'),
'tax_exigible': True,
}
cls.move_vals = {
'partner_id': cls.partner_a.id,
@@ -764,7 +759,6 @@ class TestAccountMoveOutInvoiceOnchanges(AccountTestInvoicingCommon):
'price_subtotal': 1000.0,
'price_total': 1470.0,
'tax_ids': (self.tax_sale_a + self.tax_armageddon).ids,
'tax_exigible': False,
},
self.product_line_vals_2,
self.tax_line_vals_1,
@@ -787,7 +781,6 @@ class TestAccountMoveOutInvoiceOnchanges(AccountTestInvoicingCommon):
'debit': 0.0,
'credit': 120.0,
'date_maturity': False,
'tax_exigible': False,
},
{
'name': child_tax_1.name,
@@ -807,7 +800,6 @@ class TestAccountMoveOutInvoiceOnchanges(AccountTestInvoicingCommon):
'debit': 0.0,
'credit': 80.0,
'date_maturity': False,
'tax_exigible': False,
},
{
'name': child_tax_2.name,
@@ -827,7 +819,6 @@ class TestAccountMoveOutInvoiceOnchanges(AccountTestInvoicingCommon):
'debit': 0.0,
'credit': 120.0,
'date_maturity': False,
'tax_exigible': False,
},
{
**self.term_line_vals_1,
@@ -1083,7 +1074,6 @@ class TestAccountMoveOutInvoiceOnchanges(AccountTestInvoicingCommon):
'debit': 0.0,
'credit': 119.91,
'date_maturity': False,
'tax_exigible': True,
},
{
**self.term_line_vals_1,
@@ -1143,7 +1133,6 @@ class TestAccountMoveOutInvoiceOnchanges(AccountTestInvoicingCommon):
'debit': 119.91,
'credit': 0.0,
'date_maturity': False,
'tax_exigible': True,
},
{
**self.term_line_vals_1,
@@ -1207,7 +1196,6 @@ class TestAccountMoveOutInvoiceOnchanges(AccountTestInvoicingCommon):
'debit': 0.0,
'credit': 59.95,
'date_maturity': False,
'tax_exigible': True,
},
{
**self.term_line_vals_1,
@@ -1271,7 +1259,6 @@ class TestAccountMoveOutInvoiceOnchanges(AccountTestInvoicingCommon):
'debit': 59.95,
'credit': 0.0,
'date_maturity': False,
'tax_exigible': True,
},
{
**self.term_line_vals_1,
@@ -1501,7 +1488,6 @@ class TestAccountMoveOutInvoiceOnchanges(AccountTestInvoicingCommon):
'debit': 0.0,
'credit': 0.01,
'date_maturity': False,
'tax_exigible': True,
},
{
**self.product_line_vals_1,
@@ -1552,7 +1538,6 @@ class TestAccountMoveOutInvoiceOnchanges(AccountTestInvoicingCommon):
'debit': 0.04,
'credit': 0.0,
'date_maturity': False,
'tax_exigible': True,
},
{
**self.term_line_vals_1,
@@ -33,7 +33,6 @@ class TestAccountMoveOutRefundOnchanges(AccountTestInvoicingCommon):
'debit': 1000.0,
'credit': 0.0,
'date_maturity': False,
'tax_exigible': True,
}
cls.product_line_vals_2 = {
'name': cls.product_b.name,
@@ -53,7 +52,6 @@ class TestAccountMoveOutRefundOnchanges(AccountTestInvoicingCommon):
'debit': 200.0,
'credit': 0.0,
'date_maturity': False,
'tax_exigible': True,
}
cls.tax_line_vals_1 = {
'name': cls.tax_sale_a.name,
@@ -73,7 +71,6 @@ class TestAccountMoveOutRefundOnchanges(AccountTestInvoicingCommon):
'debit': 180.0,
'credit': 0.0,
'date_maturity': False,
'tax_exigible': True,
}
cls.tax_line_vals_2 = {
'name': cls.tax_sale_b.name,
@@ -93,7 +90,6 @@ class TestAccountMoveOutRefundOnchanges(AccountTestInvoicingCommon):
'debit': 30.0,
'credit': 0.0,
'date_maturity': False,
'tax_exigible': True,
}
cls.term_line_vals_1 = {
'name': '',
@@ -113,7 +109,6 @@ class TestAccountMoveOutRefundOnchanges(AccountTestInvoicingCommon):
'debit': 0.0,
'credit': 1410.0,
'date_maturity': fields.Date.from_string('2019-01-01'),
'tax_exigible': True,
}
cls.move_vals = {
'partner_id': cls.partner_a.id,
@@ -459,7 +454,6 @@ class TestAccountMoveOutRefundOnchanges(AccountTestInvoicingCommon):
'price_subtotal': 1000.0,
'price_total': 1470.0,
'tax_ids': (self.tax_sale_a + self.tax_armageddon).ids,
'tax_exigible': False,
},
self.product_line_vals_2,
self.tax_line_vals_1,
@@ -482,7 +476,6 @@ class TestAccountMoveOutRefundOnchanges(AccountTestInvoicingCommon):
'debit': 80.0,
'credit': 0.0,
'date_maturity': False,
'tax_exigible': False,
},
{
'name': child_tax_1.name,
@@ -502,7 +495,6 @@ class TestAccountMoveOutRefundOnchanges(AccountTestInvoicingCommon):
'debit': 120.0,
'credit': 0.0,
'date_maturity': False,
'tax_exigible': False,
},
{
'name': child_tax_2.name,
@@ -522,7 +514,6 @@ class TestAccountMoveOutRefundOnchanges(AccountTestInvoicingCommon):
'debit': 120.0,
'credit': 0.0,
'date_maturity': False,
'tax_exigible': False,
},
{
**self.term_line_vals_1,
@@ -578,7 +569,6 @@ class TestAccountMoveOutRefundOnchanges(AccountTestInvoicingCommon):
'debit': 0.01,
'credit': 0.0,
'date_maturity': False,
'tax_exigible': True,
},
{
**self.product_line_vals_1,
@@ -629,7 +619,6 @@ class TestAccountMoveOutRefundOnchanges(AccountTestInvoicingCommon):
'debit': 0.0,
'credit': 0.04,
'date_maturity': False,
'tax_exigible': True,
},
{
**self.term_line_vals_1,
@@ -202,33 +202,6 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon):
"Amount currency of %s is incorrect" % account.name,
)
def assertTaxGridAmounts(self, amount_per_tag):
expected_values = {tag.id: (tag, balance) for tag, balance in amount_per_tag}
if not expected_values:
return
self.cr.execute('''
SELECT
rel.account_account_tag_id,
SUM(line.balance)
FROM account_account_tag_account_move_line_rel rel
JOIN account_move_line line ON line.id = rel.account_move_line_id
WHERE line.tax_exigible IS TRUE
AND line.company_id = ANY(%(company_ids)s)
GROUP BY rel.account_account_tag_id
''', {
'company_ids': self.env.companies.ids,
})
for tag_id, total_balance in self.cr.fetchall():
tag, expected_balance = expected_values[tag_id]
self.assertEqual(
total_balance,
expected_balance,
"Balance of %s is incorrect" % tag.name,
)
# -------------------------------------------------------------------------
# Test creation of account.partial.reconcile/account.full.reconcile
# during the reconciliation.
@@ -1216,7 +1189,6 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon):
'credit': 100.0,
'account_id': self.company_data['default_account_revenue'].id,
'tax_ids': [(6, 0, (self.cash_basis_tax_a_third_amount + self.cash_basis_tax_tiny_amount).ids)],
'tax_exigible': False,
}),
# Tax lines
@@ -1225,14 +1197,12 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon):
'credit': 33.33,
'account_id': self.cash_basis_transfer_account.id,
'tax_repartition_line_id': self.cash_basis_tax_a_third_amount.invoice_repartition_line_ids.filtered(lambda line: line.repartition_type == 'tax').id,
'tax_exigible': False,
}),
(0, 0, {
'debit': 0.0,
'credit': 0.01,
'account_id': self.cash_basis_transfer_account.id,
'tax_repartition_line_id': self.cash_basis_tax_tiny_amount.invoice_repartition_line_ids.filtered(lambda line: line.repartition_type == 'tax').id,
'tax_exigible': False,
}),
# Receivable lines
@@ -1428,7 +1398,6 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon):
'currency_id': currency_id,
'account_id': self.company_data['default_account_revenue'].id,
'tax_ids': [(6, 0, taxes.ids)],
'tax_exigible': False,
}),
# Tax lines
@@ -1439,7 +1408,6 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon):
'currency_id': currency_id,
'account_id': self.cash_basis_transfer_account.id,
'tax_repartition_line_id': self.cash_basis_tax_a_third_amount.invoice_repartition_line_ids.filtered(lambda line: line.repartition_type == 'tax').id,
'tax_exigible': False,
}),
(0, 0, {
'debit': 0.0,
@@ -1448,7 +1416,6 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon):
'currency_id': currency_id,
'account_id': self.cash_basis_transfer_account.id,
'tax_repartition_line_id': self.cash_basis_tax_tiny_amount.invoice_repartition_line_ids.filtered(lambda line: line.repartition_type == 'tax').id,
'tax_exigible': False,
}),
# Receivable lines
@@ -1639,7 +1606,7 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon):
def test_reconcile_cash_basis_exchange_difference_transfer_account_check_entries_1(self):
''' Test the generation of the exchange difference for a tax cash basis journal entry when the transfer
account is not a reconcile one.
account is not reconcilable.
'''
currency_id = self.currency_data['currency'].id
@@ -1656,7 +1623,6 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon):
'currency_id': currency_id,
'account_id': self.company_data['default_account_revenue'].id,
'tax_ids': [(6, 0, self.cash_basis_tax_a_third_amount.ids)],
'tax_exigible': False,
}),
# Tax line
@@ -1667,7 +1633,6 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon):
'currency_id': currency_id,
'account_id': self.cash_basis_transfer_account.id,
'tax_repartition_line_id': self.cash_basis_tax_a_third_amount.invoice_repartition_line_ids.filtered(lambda line: line.repartition_type == 'tax').id,
'tax_exigible': False,
}),
# Receivable lines
@@ -1794,7 +1759,6 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon):
'currency_id': currency_id,
'account_id': self.company_data['default_account_revenue'].id,
'tax_ids': [(6, 0, self.cash_basis_tax_a_third_amount.ids)],
'tax_exigible': False,
}),
# Tax line
@@ -1805,7 +1769,6 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon):
'currency_id': currency_id,
'account_id': self.cash_basis_transfer_account.id,
'tax_repartition_line_id': self.cash_basis_tax_a_third_amount.invoice_repartition_line_ids.filtered(lambda line: line.repartition_type == 'tax').id,
'tax_exigible': False,
}),
# Receivable lines
@@ -1882,7 +1845,6 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon):
'currency_id': currency_id,
'account_id': self.company_data['default_account_revenue'].id,
'tax_ids': [(6, 0, self.cash_basis_tax_a_third_amount.ids)],
'tax_exigible': False,
}),
# Tax line
@@ -1893,7 +1855,6 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon):
'currency_id': currency_id,
'account_id': self.cash_basis_transfer_account.id,
'tax_repartition_line_id': self.cash_basis_tax_a_third_amount.invoice_repartition_line_ids.filtered(lambda line: line.repartition_type == 'tax').id,
'tax_exigible': False,
}),
# Receivable lines
@@ -1957,7 +1918,6 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon):
'credit': 100.0,
'account_id': self.company_data['default_account_revenue'].id,
'tax_ids': [(6, 0, self.cash_basis_tax_a_third_amount.ids)],
'tax_exigible': False,
}),
# Tax line
@@ -1966,7 +1926,6 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon):
'credit': 33.33,
'account_id': self.cash_basis_transfer_account.id,
'tax_repartition_line_id': self.cash_basis_tax_a_third_amount.invoice_repartition_line_ids.filtered(lambda line: line.repartition_type == 'tax').id,
'tax_exigible': False,
}),
# Receivable line
@@ -2037,8 +1996,6 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon):
'credit': 100.0,
'account_id': self.company_data['default_account_revenue'].id,
'tax_ids': [(6, 0, self.cash_basis_tax_a_third_amount.ids)],
'tax_tag_ids': [(6, 0, self.tax_tags[0].ids)],
'tax_exigible': False,
}),
# Tax line
@@ -2047,8 +2004,6 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon):
'credit': 33.33,
'account_id': self.cash_basis_transfer_account.id,
'tax_repartition_line_id': self.cash_basis_tax_a_third_amount.invoice_repartition_line_ids.filtered(lambda line: line.repartition_type == 'tax').id,
'tax_tag_ids': [(6, 0, self.tax_tags[1].ids)],
'tax_exigible': False,
}),
# Receivable line
@@ -2072,8 +2027,6 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon):
'credit': 0.0,
'account_id': self.company_data['default_account_revenue'].id,
'tax_ids': [(6, 0, self.cash_basis_tax_a_third_amount.ids)],
'tax_tag_ids': [(6, 0, self.tax_tags[2].ids)],
'tax_exigible': False,
}),
# Tax line
@@ -2081,9 +2034,7 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon):
'debit': 33.33,
'credit': 0.0,
'account_id': self.cash_basis_transfer_account.id,
'tax_repartition_line_id': self.cash_basis_tax_a_third_amount.invoice_repartition_line_ids.filtered(lambda line: line.repartition_type == 'tax').id,
'tax_tag_ids': [(6, 0, self.tax_tags[3].ids)],
'tax_exigible': False,
'tax_repartition_line_id': self.cash_basis_tax_a_third_amount.refund_repartition_line_ids.filtered(lambda line: line.repartition_type == 'tax').id,
}),
# Receivable line
@@ -2104,7 +2055,7 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon):
self.assertFullReconcile(res['full_reconcile'], receivable_lines)
self.assertEqual(len(res.get('tax_cash_basis_moves', [])), 2)
tax_cash_basis_moves = res['tax_cash_basis_moves'].sorted(lambda move: move.tax_cash_basis_move_id.id)
tax_cash_basis_moves = res['tax_cash_basis_moves'].sorted(lambda move: move.tax_cash_basis_origin_move_id.id)
# Invoice:
cb_lines = tax_cash_basis_moves[0].line_ids.sorted(lambda line: (-abs(line.balance), -line.debit, line.account_id))
@@ -2128,14 +2079,6 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon):
{'debit': 0.0, 'credit': 33.33, 'tax_tag_ids': [], 'account_id': self.cash_basis_transfer_account.id},
])
self.assertTaxGridAmounts([
# Tag Balance
(self.tax_tags[0], -100.0),
(self.tax_tags[1], -33.33),
(self.tax_tags[2], 100.0),
(self.tax_tags[3], 33.33),
])
def test_reconcile_cash_basis_tax_grid_multi_taxes(self):
''' Test the tax grid when reconciling an invoice with multiple taxes/tax repartition. '''
base_taxes = self.cash_basis_tax_a_third_amount + self.cash_basis_tax_tiny_amount
@@ -2152,8 +2095,6 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon):
'credit': 100.0,
'account_id': self.company_data['default_account_revenue'].id,
'tax_ids': [(6, 0, base_taxes.ids)],
'tax_tag_ids': [(6, 0, base_tags.ids)],
'tax_exigible': False,
}),
# Tax lines
@@ -2162,16 +2103,12 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon):
'credit': 33.33,
'account_id': self.cash_basis_transfer_account.id,
'tax_repartition_line_id': self.cash_basis_tax_a_third_amount.invoice_repartition_line_ids.filtered(lambda line: line.repartition_type == 'tax').id,
'tax_tag_ids': [(6, 0, self.tax_tags[1].ids)],
'tax_exigible': False,
}),
(0, 0, {
'debit': 0.0,
'credit': 0.01,
'account_id': self.cash_basis_transfer_account.id,
'tax_repartition_line_id': self.cash_basis_tax_tiny_amount.invoice_repartition_line_ids.filtered(lambda line: line.repartition_type == 'tax').id,
'tax_tag_ids': [(6, 0, self.tax_tags[5].ids)],
'tax_exigible': False,
}),
# Receivable lines
@@ -2214,14 +2151,6 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon):
{'debit': 0.0, 'credit': 0.01, 'tax_ids': [], 'tax_tag_ids': self.tax_tags[5].ids, 'account_id': self.tax_account_2.id},
])
self.assertTaxGridAmounts([
# Tag Balance
(self.tax_tags[0], -100.0),
(self.tax_tags[1], -33.33),
(self.tax_tags[4], -100.0),
(self.tax_tags[5], -0.01),
])
def test_caba_mix_reconciliation(self):
""" Test the reconciliation of tax lines (when using a reconcilable tax account)
for cases mixing taxes exigible on payment and on invoices.
+9 -4
View File
@@ -42,7 +42,6 @@
<field name="tax_line_id" readonly="1" attrs="{'invisible': [('tax_line_id','=',False)]}"/>
<field name="tax_ids" widget="many2many_tags" readonly="1" attrs="{'invisible': [('tax_ids','=',[])]}"/>
<field name="tax_tag_invert" readonly="1" groups="base.group_no_one"/>
<field name="tax_exigible" readonly="1"/>
<field name="tax_audit"/>
</group>
<group string="Matching" attrs="{'invisible':[('matched_debit_ids', '=', []),('matched_credit_ids', '=', [])]}">
@@ -696,6 +695,13 @@
attrs="{'invisible': ['|', '|', ('move_type', '!=', 'entry'), ('id', '=', False), ('has_reconciled_entries', '=', False)]}"
string="Reconciled Entries">
</button>
<button name="open_created_caba_entries"
class="oe_stat_button"
icon="fa-usd"
type="object"
attrs="{'invisible': [('tax_cash_basis_created_move_ids', '=', [])]}"
string="Cash Basis Entries">
</button>
</div>
<!-- Payment status for invoices / receipts -->
@@ -737,6 +743,7 @@
<field name="statement_id" invisible="1"/>
<field name="payment_id" invisible="1"/>
<field name="tax_country_id" invisible="1"/>
<field name="tax_cash_basis_created_move_ids" invisible="1"/>
<div class="oe_title">
<!-- Invoice draft header -->
@@ -770,6 +777,7 @@
attrs="{'invisible':[('move_type', 'not in', ('in_invoice', 'in_receipt', 'in_refund'))]}" />
<field name="ref" nolabel="1" attrs="{'invisible':[('move_type', 'not in', ('in_invoice', 'in_receipt', 'in_refund'))]}" />
<field name="ref" attrs="{'invisible':[('move_type', 'in', ('in_invoice', 'in_receipt', 'in_refund', 'out_invoice', 'out_refund'))]}"/>
<field name="tax_cash_basis_origin_move_id" attrs="{'invisible': [('tax_cash_basis_origin_move_id', '=', False)]}"/>
<field name="payment_reference"
attrs="{'invisible': [('move_type', 'not in', ('out_invoice', 'out_refund', 'in_invoice', 'in_refund', 'out_receipt', 'in_receipt'))], 'readonly': [('state', '!=', 'draft')]}"/>
<field name="partner_bank_id"
@@ -904,7 +912,6 @@
<field name="tax_repartition_line_id" invisible="1"/>
<field name="tax_tag_ids" invisible="1"/>
<field name="tax_base_amount" invisible="1"/>
<field name="tax_exigible" invisible="1"/>
<field name="company_id" invisible="1"/>
<field name="company_currency_id" invisible="1"/>
<field name="recompute_tax_line" invisible="1" force_save="1"/>
@@ -981,7 +988,6 @@
<field name="tax_repartition_line_id" invisible="1"/>
<field name="tax_tag_ids" invisible="1"/>
<field name="tax_base_amount" invisible="1"/>
<field name="tax_exigible" invisible="1"/>
<field name="company_id" invisible="1"/>
<field name="company_currency_id" invisible="1"/>
<field name="recompute_tax_line" invisible="1" force_save="1"/>
@@ -1134,7 +1140,6 @@
<field name="tax_group_id" invisible="1"/>
<field name="tax_repartition_line_id" invisible="1" force_save="1"/>
<field name="tax_base_amount" invisible="1" force_save="1"/>
<field name="tax_exigible" invisible="1" force_save="1"/>
<field name="company_id" invisible="1"/>
<field name="company_currency_id" invisible="1"/>
<field name="recompute_tax_line" invisible="1" force_save="1"/>
@@ -39,7 +39,7 @@ class AccountTaxPython(models.Model):
return localdict['result']
return super(AccountTaxPython, self)._compute_amount(base_amount, price_unit, quantity, product, partner)
def compute_all(self, price_unit, currency=None, quantity=1.0, product=None, partner=None, is_refund=False, handle_price_include=True):
def compute_all(self, price_unit, currency=None, quantity=1.0, product=None, partner=None, is_refund=False, handle_price_include=True, include_caba_tags=False):
taxes = self.filtered(lambda r: r.amount_type != 'code')
company = self.env.company
if product and product._name == 'product.template':
@@ -50,7 +50,7 @@ class AccountTaxPython(models.Model):
safe_eval(tax.python_applicable, localdict, mode="exec", nocopy=True)
if localdict.get('result', False):
taxes += tax
return super(AccountTaxPython, taxes).compute_all(price_unit, currency, quantity, product, partner, is_refund=is_refund, handle_price_include=handle_price_include)
return super(AccountTaxPython, taxes).compute_all(price_unit, currency, quantity, product, partner, is_refund=is_refund, handle_price_include=handle_price_include, include_caba_tags=include_caba_tags)
class AccountTaxTemplatePython(models.Model):