diff --git a/addons/account/models/account_tax_report.py b/addons/account/models/account_tax_report.py index 15b78f8c299..99e7775e936 100644 --- a/addons/account/models/account_tax_report.py +++ b/addons/account/models/account_tax_report.py @@ -31,12 +31,11 @@ class AccountTaxReport(models.Model): new_tags = tags_cache[cache_key] if new_tags: - tags_to_unlink = line.tag_ids.filtered(lambda x: record == x.mapped('tax_report_line_ids.report_id')) - # == instead of in, as we only want tags_to_unlink to contain the tags that are not linked to any other report than the one we're considering + line._remove_tags_used_only_by_self() line.write({'tag_ids': [(6, 0, new_tags.ids)]}) - self.env['account.tax.report.line']._delete_tags_from_taxes(tags_to_unlink.ids) elif line.mapped('tag_ids.tax_report_line_ids.report_id').filtered(lambda x: x not in self): + line._remove_tags_used_only_by_self() line.write({'tag_ids': [(5, 0, 0)] + line._get_tags_create_vals(line.tag_name, vals['country_id'])}) tags_cache[cache_key] = line.tag_ids @@ -225,7 +224,7 @@ class AccountTaxReportLine(models.Model): # All the lines sharing their tags must always be synchronized, tags_to_remove += records_to_link.mapped('tag_ids') records_to_link = tags_to_remove.mapped('tax_report_line_ids') - self._delete_tags_from_taxes(tags_to_remove.ids) + tags_to_remove.mapped('tax_report_line_ids')._remove_tags_used_only_by_self() records_to_link.write({'tag_name': tag_name_postponed, 'tag_ids': [(2, tag.id) for tag in tags_to_remove] + [(6, 0, existing_tags.ids)]}) else: @@ -242,17 +241,27 @@ class AccountTaxReportLine(models.Model): return rslt def unlink(self): - self._delete_tags_from_taxes(self.mapped('tag_ids.id')) + self._remove_tags_used_only_by_self() children = self.mapped('children_line_ids') if children: children.unlink() return super(AccountTaxReportLine, self).unlink() + def _remove_tags_used_only_by_self(self): + """ Deletes and removes from taxes and move lines all the + tags from the provided tax report lines that are not linked + to any other tax report lines. + """ + all_tags = self.mapped('tag_ids') + tags_to_unlink = all_tags.filtered(lambda x: not (x.tax_report_line_ids - self)) + self.write({'tag_ids': [(2, tag.id, 0) for tag in tags_to_unlink]}) + self._delete_tags_from_taxes(tags_to_unlink.ids) + @api.model def _delete_tags_from_taxes(self, tag_ids_to_delete): """ Based on a list of tag ids, removes them first from the repartition lines they are linked to, then deletes them - from the account move lines. + from the account move lines, and finally unlink them. """ if not tag_ids_to_delete: # Nothing to do, then! @@ -269,6 +278,8 @@ class AccountTaxReportLine(models.Model): self.env['account.move.line'].invalidate_cache(fnames=['tax_tag_ids']) self.env['account.tax.repartition.line'].invalidate_cache(fnames=['tag_ids']) + self.env['account.account.tag'].browse(tag_ids_to_delete).unlink() + @api.constrains('formula', 'tag_name') def _validate_formula(self): for record in self: diff --git a/addons/account/tests/test_tax_report.py b/addons/account/tests/test_tax_report.py index 7b261a059bb..97fd9d50e9d 100644 --- a/addons/account/tests/test_tax_report.py +++ b/addons/account/tests/test_tax_report.py @@ -64,6 +64,13 @@ class TaxReportTest(AccountTestInvoicingCommon): 'sequence': 7, }) + cls.tax_report_line_1_6 = cls.env['account.tax.report.line'].create({ + 'name': "[100] Line 100", + 'tag_name': '100', + 'report_id': cls.tax_report_1.id, + 'sequence': 8, + }) + cls.tax_report_2 = cls.env['account.tax.report'].create({ 'name': "Tax report 2", 'country_id': cls.test_country_1.id, @@ -89,6 +96,13 @@ class TaxReportTest(AccountTestInvoicingCommon): 'sequence': 3, }) + cls.tax_report_line_2_6 = cls.env['account.tax.report.line'].create({ + 'name': "[100] Line 100", + 'tag_name': '100', + 'report_id': cls.tax_report_2.id, + 'sequence': 4, + }) + def _get_tax_tags(self, tag_name=None): domain = [('country_id', '=', self.test_country_1.id), ('applicability', '=', 'taxes')] if tag_name: @@ -255,3 +269,17 @@ class TaxReportTest(AccountTestInvoicingCommon): self.assertEqual(line.tag_ids.ids, original_report_2_tags[line.id], "The tax report lines not sharing their tags with any other report should keep the same tags when the country of their report is changed") elif line.tag_ids or original_report_2_tags[line.id]: self.assertNotEqual(line.tag_ids.ids, original_report_2_tags[line.id], "The tax report lines sharing their tags with other report should receive new tags when the country of their report is changed") + + def test_unlink_report_line_tags(self): + """ Under certain circumstances, unlinking a tax report line should also unlink + the tags that are linked to it. We test those cases here. + """ + def check_tags_unlink(tag_name, report_lines, unlinked, error_message): + report_lines.unlink() + surviving_tags = self._get_tax_tags(tag_name) + required_len = 0 if unlinked else 2 # 2 for + and - tag + self.assertEqual(len(surviving_tags), required_len, error_message) + + check_tags_unlink('42', self.tax_report_line_2_42, True, "Unlinking one line not sharing its tags should also unlink them") + check_tags_unlink('01', self.tax_report_line_1_1, False, "Unlinking one line sharing its tags with others should keep the tags") + check_tags_unlink('100', self.tax_report_line_1_6 + self.tax_report_line_2_6, True, "Unlinkink all the lines sharing the same tags should also unlink them")