From 39d6e3d8c0c6f8c6bedb7dfad511e91e32a8421b Mon Sep 17 00:00:00 2001 From: "Nicolas (vin)" Date: Thu, 24 Sep 2020 12:53:11 +0000 Subject: [PATCH] [IMP] account_reports: allow to carry over lines between periods In belgian accounting, some tax lines in the tax report must be carried over to the next period if they are negative. If the balance for the next period is positive, it will be deduced by the maximum possible amount that is carried over. If it is negative, the negative balance will simply be added to the carried over balance. This process repeats itself from one period to the other endlessly, until the carried over balance come back to 0. This is done by putting the carried over balance in dedicated analytic accounts. task id #2271978 --- addons/account/models/__init__.py | 1 + .../models/account_tax_carryover_line.py | 27 +++++++++ addons/account/models/account_tax_report.py | 59 +++++++++++++++++-- addons/account/security/account_security.xml | 5 ++ addons/account/security/ir.model.access.csv | 4 +- .../views/account_tax_report_views.xml | 32 +++++++++- .../l10n_be/data/account_tax_report_data.xml | 6 ++ 7 files changed, 127 insertions(+), 7 deletions(-) create mode 100644 addons/account/models/account_tax_carryover_line.py diff --git a/addons/account/models/__init__.py b/addons/account/models/__init__.py index d9e2695b020..0322c41fd1d 100644 --- a/addons/account/models/__init__.py +++ b/addons/account/models/__init__.py @@ -7,6 +7,7 @@ from . import account_account_tag from . import account_account from . import account_journal from . import account_tax +from . import account_tax_carryover_line from . import account_tax_report from . import account_reconcile_model from . import account_payment_term diff --git a/addons/account/models/account_tax_carryover_line.py b/addons/account/models/account_tax_carryover_line.py new file mode 100644 index 00000000000..ee9a7065288 --- /dev/null +++ b/addons/account/models/account_tax_carryover_line.py @@ -0,0 +1,27 @@ +from odoo import api, fields, models, _ + +from odoo.exceptions import ValidationError + +class AccountTaxCarryoverLine(models.Model): + _name = 'account.tax.carryover.line' + _description = 'Tax carryover line' + + name = fields.Char(required=True) + amount = fields.Float(required=True, default=0.0) + date = fields.Date(required=True, default=fields.Date.context_today, readonly=True) + tax_report_line_id = fields.Many2one(comodel_name='account.tax.report.line', string="Tax report line") + tax_report_country_id = fields.Many2one(related='tax_report_line_id.report_id.country_id') + company_id = fields.Many2one(comodel_name='res.company', string='Company', + required=True, + default=lambda self: self.env.company) + foreign_vat_fiscal_position_id = fields.Many2one(comodel_name='account.fiscal.position', + string='Fiscal position', + help="The foreign fiscal position for which this carryover is made.", + domain=[('company_id', '=', company_id), + ('country_id', '=', tax_report_country_id), + ('foreign_vat', '!=', False)]) + + @api.constrains('foreign_vat_fiscal_position_id') + def _check_fiscal_position(self): + if self.foreign_vat_fiscal_position_id and not self.foreign_vat_fiscal_position_id.country_id == self.tax_report_country_id: + raise ValidationError(_("The country of the fiscal position must be this report line's report country.")) diff --git a/addons/account/models/account_tax_report.py b/addons/account/models/account_tax_report.py index 2533002972a..e7b34becc05 100644 --- a/addons/account/models/account_tax_report.py +++ b/addons/account/models/account_tax_report.py @@ -75,11 +75,12 @@ class AccountTaxReport(models.Model): lines_to_treat = list(to_yield.children_line_ids.sorted(lambda x: x.sequence)) + lines_to_treat[1:] yield to_yield - def get_checks_to_perform(self, d): + def get_checks_to_perform(self, amounts, carried_over): """ To override in localizations If value is a float, it will be formatted with format_value The line is not displayed if it is falsy (0, 0.0, False, ...) - :param d: the mapping dictionay between codes and values + :param amounts: the mapping dictionary between codes and values + :param carried_over: the mapping dictionary between codes and whether they are carried over :return: iterable of tuple (name, value) """ self.ensure_one() @@ -107,13 +108,34 @@ class AccountTaxReportLine(models.Model): parent_path = fields.Char(index=True) report_id = fields.Many2one(string="Tax Report", required=True, comodel_name='account.tax.report', ondelete='cascade', help="The parent tax report of this line") - #helper to create tags (positive and negative) on report line creation + # helper to create tags (positive and negative) on report line creation tag_name = fields.Char(string="Tag Name", help="Short name for the tax grid corresponding to this report line. Leave empty if this report line should not correspond to any such grid.") - #fields used in specific localization reports, where a report line isn't simply the given by the sum of account.move.line with selected tags + # fields used in specific localization reports, where a report line isn't simply the given by the sum of account.move.line with selected tags code = fields.Char(string="Code", help="Optional unique code to refer to this line in total formulas") formula = fields.Char(string="Formula", help="Python expression used to compute the value of a total line. This field is mutually exclusive with tag_name, setting it turns the line to a total line. Tax report line codes can be used as variables in this expression to refer to the balance of the corresponding lines in the report. A formula cannot refer to another line using a formula.") + # fields used to carry over amounts between periods + + # The selection should be filled in localizations using the system + carry_over_condition_method = fields.Selection( + selection=[('no_negative_amount_carry_over_condition', 'No negative amount')], + string="Carry over method", + help="The method used to determine if this line should be carried over." + ) + carry_over_destination_line_id = fields.Many2one( + string="Carry over to", + comodel_name="account.tax.report.line", + domain=[('tag_name', '!=', False)], + help="The line to which the value of this line will be carried over to if needed." + " If left empty the line will carry over to itself." + ) + carryover_line_ids = fields.One2many( + string="Carryover lines", + comodel_name='account.tax.carryover.line', + inverse_name='tax_report_line_id', + ) + @api.model def create(self, vals): # Manage tags @@ -264,3 +286,32 @@ class AccountTaxReportLine(models.Model): if neg_tags.name != '-'+record.tag_name or pos_tags.name != '+'+record.tag_name: raise ValidationError(_("The tags linked to a tax report line should always match its tag name.")) + + def _get_carryover_bounds(self, options, line_amount, carried_over_amount): + """ + Check if the line will be carried over, by checking the condition method set on the line. + Do not override this method, but instead set your condition methods on each lines. + :param options: The options of the reports + :param line_amount: The amount on the line + :param carried_over_amount: The amount carried over for this line + :return: A tuple containing the lower and upper bounds from which the line will be carried over. + E.g. (0, 42) : Lines which value is below 0 or above 42 will be carried over. + E.g. (0, None) : Only lines which value is below 0 will be carried over. + E.g. None : This line will never be carried over. + """ + self.ensure_one() + # Carry over is disabled by default, but if there is a carry over condition method on the line we are + # calling it first. That way we can have a default carryover condition for the whole report (carryover_bounds) + # and specialized condition for specific lines if needed + if self.carry_over_condition_method: + condition_method = getattr(self, self.carry_over_condition_method, False) + if condition_method: + return condition_method(options, line_amount, carried_over_amount) + + return None + + def no_negative_amount_carry_over_condition(self, options, line_amount, carried_over_amount): + # The bounds are (0, None). + # Lines below 0 will be set to 0 and reduce the balance of the carryover. + # Lines above 0 will never be carried over + return (0, None) diff --git a/addons/account/security/account_security.xml b/addons/account/security/account_security.xml index 6fc42d68ea8..5fe852d5d07 100644 --- a/addons/account/security/account_security.xml +++ b/addons/account/security/account_security.xml @@ -275,5 +275,10 @@ ['|',('company_id','=',False),('company_id', 'in', company_ids)] + + Carry over line multi-company + + [('company_id', 'in', company_ids)] + diff --git a/addons/account/security/ir.model.access.csv b/addons/account/security/ir.model.access.csv index cddbb5372f2..b6a47288b84 100644 --- a/addons/account/security/ir.model.access.csv +++ b/addons/account/security/ir.model.access.csv @@ -91,6 +91,8 @@ access_account_tax_repartition_line_invoice,account.tax repartition.line.invoice access_account_tax_repartition_line_manager,account.tax repartition.line.manager,model_account_tax_repartition_line,account.group_account_manager,1,1,1,1 access_account_tax_report_line_readonly,account.tax.report.line.invoice,model_account_tax_report_line,account.group_account_readonly,1,0,0,0 access_account_tax_report_line_ac_user,account.tax.report.line.ac.user,model_account_tax_report_line,account.group_account_manager,1,1,1,1 +access_account_tax_carryover_line_readonly,account.tax.carryover.line.invoice,model_account_tax_carryover_line,account.group_account_readonly,1,0,0,0 +access_account_tax_carryover_line_ac_user,account.tax.carryover.line.ac.user,model_account_tax_carryover_line,account.group_account_manager,1,1,1,1 access_account_tax_report_invoice,account.tax.report.invoice,model_account_tax_report,account.group_account_readonly,1,0,0,0 access_account_tax_report_ac_user,account.tax.report.ac.user,model_account_tax_report,account.group_account_manager,1,1,1,1 access_account_tax_group_internal_user,account.tax.group internal user,model_account_tax_group,base.group_user,1,0,0,0 @@ -143,4 +145,4 @@ access_account_analytic_default,account.analytic.default,model_account_analytic_ access_account_analytic_default_analytic,account.analytic.default analytic,model_account_analytic_default,analytic.group_analytic_accounting,1,0,0,0 access_account_analytic_default_invoice,account.analytic.default invoice,model_account_analytic_default,account.group_account_invoice,1,1,1,1 access_account_tour_upload_bill,account.tour.upload.bill,model_account_tour_upload_bill,account.group_account_manager,1,1,1,0 -access_account_tour_upload_bill_email_confirm,account.tour.upload.bill.email.confirm,model_account_tour_upload_bill_email_confirm,account.group_account_manager,1,1,1,0 \ No newline at end of file +access_account_tour_upload_bill_email_confirm,account.tour.upload.bill.email.confirm,model_account_tour_upload_bill_email_confirm,account.group_account_manager,1,1,1,0 diff --git a/addons/account/views/account_tax_report_views.xml b/addons/account/views/account_tax_report_views.xml index 26737629c9a..9e31fb76e81 100644 --- a/addons/account/views/account_tax_report_views.xml +++ b/addons/account/views/account_tax_report_views.xml @@ -49,6 +49,21 @@ + + account.tax.carryover.line.tree + account.tax.carryover.line + + + + + + + + + + + + account.tax.report.line.form account.tax.report.line @@ -70,9 +85,22 @@ - - + + + + + + + + + + + + + + + diff --git a/addons/l10n_be/data/account_tax_report_data.xml b/addons/l10n_be/data/account_tax_report_data.xml index 6b21b5c1124..d2664138fbb 100644 --- a/addons/l10n_be/data/account_tax_report_data.xml +++ b/addons/l10n_be/data/account_tax_report_data.xml @@ -160,6 +160,7 @@ 1 + no_negative_amount_carry_over_condition @@ -169,6 +170,7 @@ 2 + no_negative_amount_carry_over_condition @@ -178,6 +180,7 @@ 3 + no_negative_amount_carry_over_condition @@ -205,6 +208,7 @@ 6 + no_negative_amount_carry_over_condition @@ -214,6 +218,7 @@ 7 + no_negative_amount_carry_over_condition @@ -223,6 +228,7 @@ 8 + no_negative_amount_carry_over_condition