Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FIX acc_fisc_classif: find or create a classif if none #435

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 45 additions & 13 deletions account_product_fiscal_classification/models/product_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).

import json
import logging

from lxml import etree

from odoo import _, api, fields, models
from odoo.exceptions import ValidationError
from odoo import api, fields, models

_logger = logging.getLogger(__name__)


class ProductTemplate(models.Model):
Expand All @@ -32,7 +34,7 @@ class ProductTemplate(models.Model):
@api.model_create_multi
def create(self, vals_list):
for vals in vals_list:
self._update_vals_fiscal_classification(vals)
self._update_vals_fiscal_classification(vals, create_mode=True)
return super().create(vals_list)

def write(self, vals):
Expand Down Expand Up @@ -60,9 +62,9 @@ def get_view(self, view_id=None, view_type="form", **options):
return result

# Custom Section
def _update_vals_fiscal_classification(self, vals):
def _update_vals_fiscal_classification(self, vals, create_mode=False):
FiscalClassification = self.env["account.product.fiscal.classification"]
if vals.get("fiscal_classification_id", False):
if vals.get("fiscal_classification_id"):
# We use sudo to have access to all the taxes, even taxes that belong
# to companies that the user can't access in the current context
classification = FiscalClassification.sudo().browse(
Expand All @@ -74,16 +76,46 @@ def _update_vals_fiscal_classification(self, vals):
"taxes_id": [(6, 0, classification.sale_tax_ids.ids)],
}
)
elif vals.get("supplier_taxes_id") or vals.get("taxes_id"):
raise ValidationError(
_(
"You can not create or write products with"
" 'Customer Taxes' or 'Supplier Taxes'\n."
"Please, use instead the 'Fiscal Classification' field."
)
)
elif create_mode or {"supplier_taxes_id", "taxes_id"} & vals.keys():
self._find_or_create_classification(vals)
bealdav marked this conversation as resolved.
Show resolved Hide resolved
return vals

@api.constrains("categ_id", "fiscal_classification_id")
def _check_rules_fiscal_classification(self):
self.env["account.product.fiscal.rule"].check_product_templates_integrity(self)

def _find_or_create_classification(self, vals):
"""Find the correct Fiscal classification,
depending of the taxes, or create a new one, if no one are found."""
# search for matching classication
purchase_tax_ids = vals.get("supplier_taxes_id")
bealdav marked this conversation as resolved.
Show resolved Hide resolved
sale_tax_ids = vals.get("taxes_id")
bealdav marked this conversation as resolved.
Show resolved Hide resolved
domain = [("sale_tax_ids", "=", False)]
if sale_tax_ids:
domain = [("sale_tax_ids", "=", sale_tax_ids)]
if purchase_tax_ids:
domain.append(("purchase_tax_ids", "=", purchase_tax_ids))
else:
domain.append(("purchase_tax_ids", "=", False))
for elm in ("supplier_taxes_id", "taxes_id"):
if elm in vals:
del vals[elm]
classification = self.env["account.product.fiscal.classification"].search(
domain, limit=1
)
if not classification:
# Create a dedicate classification for these taxes combination
classif_vals = self.env[
"account.product.fiscal.classification"
]._prepare_vals_from_taxes(
self.env["account.tax"].browse(purchase_tax_ids),
self.env["account.tax"].browse(sale_tax_ids),
)
classification = self.env["account.product.fiscal.classification"].create(
classif_vals
)
_logger.info(
f"Creating new Fiscal Classification '{classif_vals['name']}'"
f" for {self.display_name}"
)
vals["fiscal_classification_id"] = classification.id
125 changes: 88 additions & 37 deletions account_product_fiscal_classification/tests/test_module.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
# Copyright (C) 2014-Today GRAP (http://www.grap.coop)
# @author: Sylvain LE GAL (https://twitter.com/legalsylvain)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).

from odoo.exceptions import ValidationError
from odoo.tests.common import TransactionCase

Expand All @@ -20,13 +19,13 @@ def setUp(self):
self.company_2 = self.env.ref("account_product_fiscal_classification.company_2")
self.user_demo = self.env.ref("base.user_demo")

self.fiscal_classification_A_company_1 = self.env.ref(
self.classification_A_company_1 = self.env.ref(
"account_product_fiscal_classification.fiscal_classification_A_company_1"
)
self.fiscal_classification_B_company_1 = self.env.ref(
self.classification_B_company_1 = self.env.ref(
"account_product_fiscal_classification.fiscal_classification_B_company_1"
)
self.fiscal_classification_D_global = self.env.ref(
self.classification_D_global = self.env.ref(
"account_product_fiscal_classification.fiscal_classification_D_global"
)
self.product_template_A_company_1 = self.env.ref(
Expand All @@ -35,6 +34,9 @@ def setUp(self):
self.account_tax_purchase_20_company_1 = self.env.ref(
"account_product_fiscal_classification.account_tax_purchase_20_company_1"
)
self.account_tax_sale_5_company_1 = self.env.ref(
"account_product_fiscal_classification.account_tax_sale_5_company_1"
)
self.account_tax_sale_20_company_1 = self.env.ref(
"account_product_fiscal_classification.account_tax_sale_20_company_1"
)
Expand All @@ -44,58 +46,60 @@ def setUp(self):
self.chart_template = self.env.ref(
"account_product_fiscal_classification.chart_template"
)
# self.sale_tax_2 = self.env.ref(
# "account_product_fiscal_classification.account_tax_sale_5_company_1"
# )

self.category_all = self.env.ref("product.product_category_all")
self.category_wine = self.env.ref(
"account_product_fiscal_classification.category_wine"
)

# # Group to create product
# self.product_group = self.env.ref("account.group_account_manager")
# self.restricted_group = self.env.ref("base.group_system")
self.initial_classif_count = self.FiscalClassification.search_count([])

def _create_product(self, extra_vals, user=False):
if not user:
user = self.env.user
vals = {
"name": "Test Product",
"company_id": self.main_company.id,
"categ_id": self.category_all.id,
}
vals.update(extra_vals)
return self.ProductTemplate.with_user(user).create(vals)

# Test Section
def test_01_change_classification(self):
"""Test the behaviour when we change Fiscal Classification for
products."""
wizard = self.WizardChange.create(
{
"old_fiscal_classification_id": self.fiscal_classification_A_company_1.id,
"new_fiscal_classification_id": self.fiscal_classification_B_company_1.id,
"old_fiscal_classification_id": self.classification_A_company_1.id,
"new_fiscal_classification_id": self.classification_B_company_1.id,
}
)
wizard.button_change_fiscal_classification()
self.assertEqual(
self.product_template_A_company_1.fiscal_classification_id,
self.fiscal_classification_B_company_1,
self.classification_B_company_1,
"Fiscal Classification change has failed for products via Wizard.",
)

def test_02_create_product(self):
"""Test if creating a product with fiscal classification set correct taxes"""
vals = {
"name": "Product Product Name",
"company_id": self.main_company.id,
"fiscal_classification_id": self.fiscal_classification_D_global.id,
}
newTemplate = self.ProductTemplate.create(vals)
newTemplate = self._create_product(
{"fiscal_classification_id": self.classification_D_global.id}
)
# Test that all taxes are set (in sudo mode)
self.assertEqual(
set(newTemplate.sudo().taxes_id.ids),
set(self.fiscal_classification_D_global.sudo().sale_tax_ids.ids),
set(self.classification_D_global.sudo().sale_tax_ids.ids),
)
self.assertEqual(
set(newTemplate.sudo().supplier_taxes_id.ids),
set(self.fiscal_classification_D_global.sudo().purchase_tax_ids.ids),
set(self.classification_D_global.sudo().purchase_tax_ids.ids),
)

def test_03_update_fiscal_classification(self):
"""Test if changing a Configuration of a Fiscal Classification changes
the product."""
self.fiscal_classification_A_company_1.write(
self.classification_A_company_1.write(
{"sale_tax_ids": [(6, 0, [self.account_tax_sale_20_company_1.id])]}
)
self.assertEqual(
Expand All @@ -107,7 +111,7 @@ def test_03_update_fiscal_classification(self):
def test_05_unlink_fiscal_classification(self):
"""Test if unlinking a Fiscal Classification with products fails."""
with self.assertRaises(ValidationError):
self.fiscal_classification_A_company_1.unlink()
self.classification_A_company_1.unlink()

def test_10_chart_template(self):
"""Test if installing new CoA creates correct classification"""
Expand Down Expand Up @@ -148,29 +152,76 @@ def test_30_rules(self):

# Create a product without rules should success
self._create_product(
self.env.user, self.category_all, self.fiscal_classification_B_company_1
{"fiscal_classification_id": self.classification_B_company_1.id}
)
self._create_product(
self.user_demo, self.category_all, self.fiscal_classification_B_company_1
{"fiscal_classification_id": self.classification_B_company_1.id},
user=self.user_demo,
)

# create a product not respecting rules should succeed with accountant perfil
self._create_product(
self.env.user, self.category_wine, self.fiscal_classification_B_company_1
{
"categ_id": self.category_wine.id,
"fiscal_classification_id": self.classification_B_company_1.id,
}
)

# create a product not respecting rules should fail without accountant perfil
with self.assertRaises(ValidationError):
self._create_product(
self.user_demo,
self.category_wine,
self.fiscal_classification_B_company_1,
{
"categ_id": self.category_wine.id,
"fiscal_classification_id": self.classification_B_company_1.id,
},
user=self.user_demo,
)

def _create_product(self, user, category, classification):
vals = {
"name": "Test Product",
"categ_id": category.id,
"fiscal_classification_id": classification.id,
}
self.ProductTemplate.with_user(user).create(vals)
def test_no_classification_and_find_one(self):
product = self._create_product(
{
"taxes_id": self.classification_B_company_1.sale_tax_ids.ids,
"supplier_taxes_id": self.classification_A_company_1.purchase_tax_ids.ids,
}
)
# no other classification is created
classif_count_after = self.FiscalClassification.search_count([])
self.assertEqual(classif_count_after, self.initial_classif_count)
# product is linked to created classification
self.assertEqual(
product.fiscal_classification_id, self.classification_A_company_1
)

def test_no_classification_one_more_tax_and_create_one(self):
"""Create a product with fiscal settings that looks like
classification_B_company_1 but with an additional supplier tax.
"""
product = self._create_product(
{
"taxes_id": self.classification_B_company_1.sale_tax_ids.ids,
"supplier_taxes_id": self.account_tax_purchase_20_company_1.ids,
}
)
self.assertNotEqual(product.fiscal_classification_id, False)
classif_count_after = self.FiscalClassification.search_count([])
self.assertEqual(classif_count_after, self.initial_classif_count + 1)

def test_no_classification_one_less_tax_and_create_one(self):
"""Create a product with fiscal settings that looks like
classification_B_company_1 but with one less tax
"""
product = self._create_product(
{
"taxes_id": self.account_tax_sale_5_company_1.ids,
"supplier_taxes_id": [],
}
)
self.assertNotEqual(product.fiscal_classification_id, False)
classif_count_after = self.FiscalClassification.search_count([])
self.assertEqual(classif_count_after, self.initial_classif_count + 1)

def test_no_tax_nor_classification_and_create_one(self):
product = self._create_product({"taxes_id": [], "supplier_taxes_id": []})
classif = product.fiscal_classification_id
self.assertEqual(classif.purchase_tax_ids.ids, [])
self.assertEqual(classif.sale_tax_ids.ids, [])
Loading