diff --git a/sale_commission_product_criteria/models/sale_commission_line_mixin.py b/sale_commission_product_criteria/models/sale_commission_line_mixin.py index 5f97b5859..cdea718f9 100644 --- a/sale_commission_product_criteria/models/sale_commission_line_mixin.py +++ b/sale_commission_product_criteria/models/sale_commission_line_mixin.py @@ -41,7 +41,7 @@ def _get_commission_items(self, commission, product): AND (item.commission_id = %s) AND (item.active = TRUE) ORDER BY - item.applied_on, categ.complete_name desc + item.applied_on, item.based_on, categ.complete_name desc """, ( product.product_tmpl_id.ids, diff --git a/sale_commission_product_criteria_discount/models/sale.py b/sale_commission_product_criteria_discount/models/sale.py index 45c9ae882..046a6d149 100644 --- a/sale_commission_product_criteria_discount/models/sale.py +++ b/sale_commission_product_criteria_discount/models/sale.py @@ -14,7 +14,7 @@ def _get_single_commission_amount(self, commission, subtotal, product, quantity) self.ensure_one() if product.commission_free or not commission: return 0.0 - if commission.commission_type != "product": + if commission.commission_type in ["percentage", "fixed"]: return self._get_commission_amount(commission, subtotal, product, quantity) item_ids = self._get_commission_items(commission, product) if not item_ids: diff --git a/sale_commission_product_criteria_domain/README.rst b/sale_commission_product_criteria_domain/README.rst new file mode 100644 index 000000000..e69de29bb diff --git a/sale_commission_product_criteria_domain/__init__.py b/sale_commission_product_criteria_domain/__init__.py new file mode 100644 index 000000000..0650744f6 --- /dev/null +++ b/sale_commission_product_criteria_domain/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/sale_commission_product_criteria_domain/__manifest__.py b/sale_commission_product_criteria_domain/__manifest__.py new file mode 100644 index 000000000..f3b0f3a87 --- /dev/null +++ b/sale_commission_product_criteria_domain/__manifest__.py @@ -0,0 +1,26 @@ +# © 2023 ooops404 +# License AGPL-3 - See https://www.gnu.org/licenses/agpl-3.0.html +{ + "name": "Sale Commission Product Criteria Domain", + "version": "14.0.1.0.0", + "author": "Ilyas," "Ooops404," "Odoo Community Association (OCA)", + "contributors": ["Ilyas"], + "maintainers": ["ilyasProgrammer"], + "website": "https://github.com/OCA/commission", + "category": "Sales Management", + "license": "AGPL-3", + "depends": [ + "sale_commission_product_criteria", + "web_domain_field", + ], + "demo": [ + "demo/demo_data.xml", + ], + "data": [ + "views/views.xml", + "security/ir.model.access.csv", + ], + "application": False, + "installable": True, + "auto_install": False, +} diff --git a/sale_commission_product_criteria_domain/demo/demo_data.xml b/sale_commission_product_criteria_domain/demo/demo_data.xml new file mode 100644 index 000000000..faa6da478 --- /dev/null +++ b/sale_commission_product_criteria_domain/demo/demo_data.xml @@ -0,0 +1,132 @@ + + + + + Based on Rules Restricted + product_restricted + + + + + Spain + + + + + Italy + + + + + + + + sol + 3_global + fixed + 10 + + + + + + + sol + 2_product_category + fixed + 20 + + + + + + + + sol + 1_product + percentage + 5 + + + + + + + + sol + 0_product_variant + percentage + 15 + + + + + + + Agent Rules Restricted Italy + True + True + + + + + Agent Rules Restricted Spain + True + True + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sale_commission_product_criteria_domain/models/__init__.py b/sale_commission_product_criteria_domain/models/__init__.py new file mode 100644 index 000000000..8aa51beed --- /dev/null +++ b/sale_commission_product_criteria_domain/models/__init__.py @@ -0,0 +1,6 @@ +from . import commission +from . import commission_group +from . import partner +from . import account_invoice_line_agent +from . import sale_order_line_agent +from . import sale_commission_line_mixin diff --git a/sale_commission_product_criteria_domain/models/account_invoice_line_agent.py b/sale_commission_product_criteria_domain/models/account_invoice_line_agent.py new file mode 100644 index 000000000..3d24b65c8 --- /dev/null +++ b/sale_commission_product_criteria_domain/models/account_invoice_line_agent.py @@ -0,0 +1,28 @@ +# © 2023 ooops404 +# License AGPL-3 - See https://www.gnu.org/licenses/agpl-3.0.html +from odoo import api, models + + +class AccountInvoiceLineAgent(models.Model): + _inherit = "account.invoice.line.agent" + + @api.depends( + "object_id.price_subtotal", + "object_id.product_id.commission_free", + "commission_id", + ) + def _compute_amount(self): + for line in self: + if ( + line.commission_id + and line.commission_id.commission_type == "product_restricted" + ): + inv_line = line.object_id + line.amount = line._get_single_commission_amount( + line.commission_id, + inv_line.price_subtotal, + inv_line.product_id, + inv_line.quantity, + ) + else: + super(AccountInvoiceLineAgent, line)._compute_amount() diff --git a/sale_commission_product_criteria_domain/models/commission.py b/sale_commission_product_criteria_domain/models/commission.py new file mode 100644 index 000000000..c280d9535 --- /dev/null +++ b/sale_commission_product_criteria_domain/models/commission.py @@ -0,0 +1,81 @@ +# © 2023 ooops404 +# License AGPL-3 - See https://www.gnu.org/licenses/agpl-3.0.html +from odoo import api, fields, models + + +class SaleCommission(models.Model): + _inherit = "sale.commission" + + commission_type = fields.Selection( + selection_add=[("product_restricted", "Product criteria (with restrictions)")], + ondelete={"product_restricted": "set default"}, + ) + + +class CommissionItem(models.Model): + _inherit = "commission.item" + + commission_id = fields.Many2one( + "sale.commission", + string="Commission Type", + domain=[("commission_type", "in", ["product", "product_restricted"])], + required=True, + ) + sale_commission_type = fields.Selection( + related="commission_id.commission_type", readonly=True + ) + group_id = fields.Many2one( + "commission.items.group", + ondelete="restrict", + ) + + def write(self, values): + res = super().write(values) + if self.group_id and not self.group_id.commission_ids.ids: + self.group_id.commission_ids = [(6, 0, self.commission_id.ids)] + if self.commission_id.commission_type != "product_restricted" and self.group_id: + self.group_id = False + return res + + +class CommissionItemAgent(models.Model): + _name = "commission.item.agent" + _description = "Commission Item Agent" + + _sql_constraints = [ + ( + "commission_item_unique_agent", + "UNIQUE(partner_id, agent_id)", + "You can only add one time each agent into Commission " + "Items Groups Restrictions table.", + ) + ] + + partner_agent_ids = fields.Many2many(related="partner_id.agent_ids") + agent_group_ids = fields.Many2many( + "commission.items.group", compute="_compute_agent_group_ids" + ) + agent_id = fields.Many2one( + "res.partner", domain='[("id", "in", partner_agent_ids)]', required=True + ) + partner_id = fields.Many2one( + "res.partner", domain=[("agent", "=", False)], required=True + ) + group_ids = fields.Many2many( + "commission.items.group", + domain="[('id', 'in', agent_group_ids)]", + string="Commission Items Groups Restrictions", + required=True, + ) + + @api.depends("agent_id") + def _compute_agent_group_ids(self): + for rec in self: + if rec.agent_id.allowed_commission_group_ids: + dom = ("group_id", "in", rec.agent_id.allowed_commission_group_ids.ids) + else: + dom = ("group_id", "!=", False) + items = self.env["commission.item"].search( + [("commission_id", "=", rec.agent_id.commission_id.id), dom] + ) + rec.agent_group_ids = [(6, 0, items.mapped("group_id").ids)] diff --git a/sale_commission_product_criteria_domain/models/commission_group.py b/sale_commission_product_criteria_domain/models/commission_group.py new file mode 100644 index 000000000..e0746fa0b --- /dev/null +++ b/sale_commission_product_criteria_domain/models/commission_group.py @@ -0,0 +1,70 @@ +# © 2023 ooops404 +# License AGPL-3 - See https://www.gnu.org/licenses/agpl-3.0.html +from odoo import _, api, exceptions, fields, models + + +class CommissionItemsGroup(models.Model): + _name = "commission.items.group" + _description = "Commission Items Group" + _sql_constraints = [ + ( + "unique_cig_name", + "UNIQUE(name)", + "Commission items group with such name already exists. " + "Name must be unique.", + ) + ] + + name = fields.Char(required=True) + commission_ids = fields.Many2many( + "sale.commission", + compute="_compute_commission_ids", + domain=[("commission_type", "=", "product_restricted")], + readonly=True, + store=True, + ) + item_ids = fields.One2many( + "commission.item", "group_id", string="Items", readonly=True + ) + agents_count = fields.Integer(compute="_compute_agents_count") + + @api.depends("item_ids") + def _compute_commission_ids(self): + for rec in self: + rec.commission_ids = [(6, 0, rec.item_ids.mapped("commission_id").ids)] + + def unlink(self): + if self.item_ids: + raise exceptions.ValidationError( + _( + "You can not delete this commission group since " + "there is related to it commission items." + ) + ) + return super().unlink() + + def _compute_agents_count(self): + res_partner_obj = self.env["res.partner"] + for rec in self: + self.agents_count = res_partner_obj.search_count( + [ + ("agent", "=", True), + ("allowed_commission_group_ids", "in", rec.ids), + ] + ) + + def action_open_related_agents(self): + agent_ids = self.env["res.partner"].search( + [ + ("agent", "=", True), + ("allowed_commission_group_ids", "in", self.ids), + ] + ) + return { + "name": _("Commission Group Agents"), + "type": "ir.actions.act_window", + "view_mode": "tree", + "res_model": "res.partner", + "context": self.env.context, + "domain": [("id", "in", agent_ids.ids)], + } diff --git a/sale_commission_product_criteria_domain/models/partner.py b/sale_commission_product_criteria_domain/models/partner.py new file mode 100644 index 000000000..0d6e0c9fe --- /dev/null +++ b/sale_commission_product_criteria_domain/models/partner.py @@ -0,0 +1,68 @@ +# © 2023 ooops404 +# License AGPL-3 - See https://www.gnu.org/licenses/agpl-3.0.html +import json + +from odoo import api, fields, models + + +class ResPartner(models.Model): + _inherit = "res.partner" + + apply_commission_restrictions = fields.Boolean("Apply Restrictions") + commission_item_agent_ids = fields.One2many( + "commission.item.agent", "partner_id", string="Commission Items Groups" + ) + allowed_commission_group_ids = fields.Many2many( + "commission.items.group", help="Related only to agents" + ) + allowed_commission_group_ids_domain = fields.Char( + compute="_compute_allowed_commission_group_ids_domain", + readonly=True, + store=False, + ) + commission_type = fields.Selection(related="commission_id.commission_type") + + @api.depends("commission_id") + def _compute_allowed_commission_group_ids_domain(self): + for rec in self: + if rec.agent: + allowed_group_ids = rec.commission_id.filtered( + lambda x: x.commission_type == "product_restricted" + ).item_ids.mapped("group_id") + rec.allowed_commission_group_ids_domain = json.dumps( + [("id", "in", allowed_group_ids.ids)] + ) + else: + rec.allowed_commission_group_ids_domain = False + + @api.onchange("agent_ids") + def _onchange_agent_ids(self): + for rec in self: + exiting_agents = rec.commission_item_agent_ids.mapped("agent_id") + to_create = [ + {"partner_id": rec._origin.id, "agent_id": x._origin.id} + for x in rec.agent_ids.filtered( + lambda x: x.commission_id.commission_type == "product_restricted" + ) + if x not in exiting_agents.ids + ] + to_delete = rec.commission_item_agent_ids.filtered( + lambda x: x.agent_id.id in (exiting_agents - rec.agent_ids).ids + ) + if to_delete: + rec.update( + {"commission_item_agent_ids": [(2, dl.id, 0) for dl in to_delete]} + ) + if to_create: + rec.update( + {"commission_item_agent_ids": [(0, 0, line) for line in to_create]} + ) + + def write(self, vals): + res = super().write(vals) + if ( + self.commission_id.commission_type != "product_restricted" + and self.allowed_commission_group_ids + ): + self.allowed_commission_group_ids = False + return res diff --git a/sale_commission_product_criteria_domain/models/sale_commission_line_mixin.py b/sale_commission_product_criteria_domain/models/sale_commission_line_mixin.py new file mode 100644 index 000000000..d44075e8f --- /dev/null +++ b/sale_commission_product_criteria_domain/models/sale_commission_line_mixin.py @@ -0,0 +1,65 @@ +# © 2023 ooops404 +# License AGPL-3 - See https://www.gnu.org/licenses/agpl-3.0.html +from odoo import models + + +class SaleCommissionLineMixin(models.AbstractModel): + _inherit = "sale.commission.line.mixin" + + def _get_commission_items(self, commission, product): + # Method replaced + categ_ids = {} + categ = product.categ_id + while categ: + categ_ids[categ.id] = True + categ = categ.parent_id + categ_ids = list(categ_ids) + + # Module specific mod: + if self.object_id._name == "sale.order.line": + partner = self.object_id.order_id.partner_id + elif self.object_id._name == "account.move.line": + partner = self.object_id.partner_id + else: + partner = False + if partner: + group_ids = ( + partner.commission_item_agent_ids.filtered( + lambda x: x.agent_id == self.agent_id + ) + .mapped("group_ids") + .ids + ) + else: + group_ids = [] + + # Select all suitable items. Order by best match + # (priority is: all/cat/subcat/product/variant). + self.env.cr.execute( + """ + SELECT + item.id + FROM + commission_item AS item + LEFT JOIN product_category AS categ ON item.categ_id = categ.id + LEFT JOIN commission_item_agent AS cia ON item.group_id = cia.id + WHERE + (item.product_tmpl_id IS NULL OR item.product_tmpl_id = any(%s)) + AND (item.product_id IS NULL OR item.product_id = any(%s)) + AND (item.categ_id IS NULL OR item.categ_id = any(%s)) + AND (item.commission_id = %s) + AND (item.active = TRUE) + AND (cia.id IS NULL OR cia.id = any(%s)) + ORDER BY + item.applied_on, item.based_on, categ.complete_name desc + """, + ( + product.product_tmpl_id.ids, + product.ids, + categ_ids, + commission._origin.id, + group_ids, + ), + ) + item_ids = [x[0] for x in self.env.cr.fetchall()] + return item_ids diff --git a/sale_commission_product_criteria_domain/models/sale_order_line_agent.py b/sale_commission_product_criteria_domain/models/sale_order_line_agent.py new file mode 100644 index 000000000..923fac456 --- /dev/null +++ b/sale_commission_product_criteria_domain/models/sale_order_line_agent.py @@ -0,0 +1,26 @@ +# © 2023 ooops404 +# License AGPL-3 - See https://www.gnu.org/licenses/agpl-3.0.html +from odoo import api, models + + +class SaleOrderLineAgent(models.Model): + _inherit = "sale.order.line.agent" + + @api.depends( + "object_id.price_subtotal", "object_id.product_id", "object_id.product_uom_qty" + ) + def _compute_amount(self): + for line in self: + if ( + line.commission_id + and line.commission_id.commission_type == "product_restricted" + ): + order_line = line.object_id + line.amount = line._get_single_commission_amount( + line.commission_id, + order_line.price_subtotal, + order_line.product_id, + order_line.product_uom_qty, + ) + else: + super(SaleOrderLineAgent, line)._compute_amount() diff --git a/sale_commission_product_criteria_domain/readme/CONTRIBUTORS.rst b/sale_commission_product_criteria_domain/readme/CONTRIBUTORS.rst new file mode 100644 index 000000000..c29a9c72e --- /dev/null +++ b/sale_commission_product_criteria_domain/readme/CONTRIBUTORS.rst @@ -0,0 +1,3 @@ +* `Ooops404 `__: + + * Ilyas diff --git a/sale_commission_product_criteria_domain/readme/DESCRIPTION.rst b/sale_commission_product_criteria_domain/readme/DESCRIPTION.rst new file mode 100644 index 000000000..4916f9805 --- /dev/null +++ b/sale_commission_product_criteria_domain/readme/DESCRIPTION.rst @@ -0,0 +1 @@ +This module allows to limit applied commission items for specific groups. diff --git a/sale_commission_product_criteria_domain/readme/USAGE.rst b/sale_commission_product_criteria_domain/readme/USAGE.rst new file mode 100644 index 000000000..72e9f3a9d --- /dev/null +++ b/sale_commission_product_criteria_domain/readme/USAGE.rst @@ -0,0 +1,22 @@ +Go to Sales > Commission management > Commission Type Items Groups + +Create one or more new Groups, eg. “Italy” and “Spain” + +Create new Commission type, select type “Product Criteria (with restrictions)”, eg: “Southern Europe” + +Add lines to Commission type; for each line one Group must be set, eg. + + Product: Conference Chair, value: $20, group: Italy + + Product: Conference Chair, value: $10, group: Spain + + +Go to Agent A, assign Commission type: “Southern Europe” > add “Allowed Commission Groups”: “Italy”, “Spain” + +In this way, we are allowing Commission type lines for both “Spain” and “Italy” to be applied to this agent. + +Go to customer X, set agent: “Agent A” > in table “Commission items group” set group “Spain” + +Go to customer Y, set agent: “Agent A” > in table “Commission items group” set group “Italy” + +On sales for customer X, only Commission type lines with group “Spain” will be applied to agent; on sales for customer Y, only Commission type lines with group “Italy” will be applied to agent. diff --git a/sale_commission_product_criteria_domain/security/ir.model.access.csv b/sale_commission_product_criteria_domain/security/ir.model.access.csv new file mode 100644 index 000000000..ada09650e --- /dev/null +++ b/sale_commission_product_criteria_domain/security/ir.model.access.csv @@ -0,0 +1,5 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +cia1,cia1,model_commission_item_agent,sales_team.group_sale_manager,1,1,1,1 +cia2,cia2,model_commission_item_agent,sales_team.group_sale_salesman,1,1,0,0 +cig1,cig1,model_commission_items_group,sales_team.group_sale_manager,1,1,1,1 +cig2,cig2,model_commission_items_group,sales_team.group_sale_salesman,1,1,0,0 diff --git a/sale_commission_product_criteria_domain/static/description/icon.png b/sale_commission_product_criteria_domain/static/description/icon.png new file mode 100644 index 000000000..3a0328b51 Binary files /dev/null and b/sale_commission_product_criteria_domain/static/description/icon.png differ diff --git a/sale_commission_product_criteria_domain/tests/__init__.py b/sale_commission_product_criteria_domain/tests/__init__.py new file mode 100644 index 000000000..110b0564c --- /dev/null +++ b/sale_commission_product_criteria_domain/tests/__init__.py @@ -0,0 +1 @@ +from . import test_sale_commission_product_criteria_domain diff --git a/sale_commission_product_criteria_domain/tests/test_sale_commission_product_criteria_domain.py b/sale_commission_product_criteria_domain/tests/test_sale_commission_product_criteria_domain.py new file mode 100644 index 000000000..96ef954cf --- /dev/null +++ b/sale_commission_product_criteria_domain/tests/test_sale_commission_product_criteria_domain.py @@ -0,0 +1,320 @@ +# Copyright 2023 - ooops404 +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +import odoo.exceptions +from odoo.tests.common import SavepointCase + + +class TestSaleCommissionDomain(SavepointCase): + @classmethod + def setUpClass(cls): + super(TestSaleCommissionDomain, cls).setUpClass() + cls.commission_model = cls.env["sale.commission"] + cls.company = cls.env.ref("base.main_company") + cls.res_partner_model = cls.env["res.partner"] + cls.azure = cls.env.ref("base.res_partner_12") # Azure + cls.deco = cls.env.ref("base.res_partner_2") # Deco + cls.partner2 = cls.env.ref("base.res_partner_10") # The Jackson Group + cls.sale_order_model = cls.env["sale.order"] + cls.advance_inv_model = cls.env["sale.advance.payment.inv"] + cls.settle_model = cls.env["sale.commission.settlement"] + cls.make_settle_model = cls.env["sale.commission.make.settle"] + cls.make_inv_model = cls.env["sale.commission.make.invoice"] + cls.product_1 = cls.env.ref("product.product_product_1") + cls.product_4 = cls.env.ref("product.product_product_4") + cls.product_5 = cls.env.ref("product.product_product_5") + cls.product_6 = cls.env.ref("product.product_product_6") + # Acoustic Bloc Screens + cls.product_product_25 = cls.env.ref("product.product_product_25") + cls.product_1.write({"invoice_policy": "order"}) + cls.product_4.write({"invoice_policy": "order"}) + cls.product_5.write({"invoice_policy": "order"}) + cls.product_product_25.write({"invoice_policy": "order"}) + cls.product_6.write({"commission_free": True, "invoice_policy": "order"}) + cls.product_template_4 = cls.env.ref( + "product.product_product_4_product_template" + ) # Customizable Desk (CONFIG) + cls.product_template_4.write({"invoice_policy": "order"}) + cls.pt_11 = cls.env.ref("product.product_product_11_product_template") + cls.journal = cls.env["account.journal"].search( + [("type", "=", "purchase")], limit=1 + ) + cls.rules_commission_id = cls.env.ref( + "sale_commission_product_criteria.demo_commission_rules" + ) + cls.com_item_1 = cls.env.ref( + "sale_commission_product_criteria.demo_commission_rules_item_1" + ) + cls.com_item_2 = cls.env.ref( + "sale_commission_product_criteria.demo_commission_rules_item_2" + ) + cls.com_item_3 = cls.env.ref( + "sale_commission_product_criteria.demo_commission_rules_item_3" + ) + cls.com_item_4 = cls.env.ref( + "sale_commission_product_criteria.demo_commission_rules_item_4" + ) + cls.demo_crr_item_1 = cls.env.ref( + "sale_commission_product_criteria_domain.demo_crr_item_1" + ) + cls.demo_cig_spain = cls.env.ref( + "sale_commission_product_criteria_domain.demo_cig_spain" + ) + cls.demo_cig_italy = cls.env.ref( + "sale_commission_product_criteria_domain.demo_cig_italy" + ) + cls.demo_agent_rules_restricted_italy = cls.env.ref( + "sale_commission_product_criteria_domain.demo_agent_rules_restricted_italy" + ) + cls.demo_agent_rules_restricted_spain = cls.env.ref( + "sale_commission_product_criteria_domain.demo_agent_rules_restricted_spain" + ) + cls.demo_commission_rules_restrict = cls.env.ref( + "sale_commission_product_criteria_domain.demo_commission_rules_restrict" + ) + cls.demo_commission_rules = cls.env.ref( + "sale_commission_product_criteria.demo_commission_rules" + ) + cls.demo_commission = cls.env.ref("sale_commission.demo_commission") + cls.conf_chair_config_id = cls.env.ref( + "product.product_product_11_product_template" + ) + cls.cia_azure = cls.env.ref("sale_commission_product_criteria_domain.cia_azure") + cls.res_partner_tiny_sale_agent = cls.env.ref( + "sale_commission.res_partner_tiny_sale_agent" + ) + + def test_commission_domain_demo_cases(self): + # Azure Spain Office furn. - Category + so = self._create_sale_order(self.product_5, self.azure) + so.recompute_lines_agents() + so.action_confirm() + invoice = self._invoice_sale_order(so) + invoice.recompute_lines_agents() + so.order_line.agent_ids._compute_amount() + invoice.line_ids.agent_ids._compute_amount() + self.assertEqual(so.order_line.agent_ids.fixed_amount, 20) + self.assertEqual(invoice.line_ids.agent_ids.amount, 20) + + # Azure Spain Customizable Desk (CONFIG) - Product Template + so = self._create_sale_order( + self.product_template_4.product_variant_id, self.azure + ) + so.recompute_lines_agents() + so.action_confirm() + invoice = self._invoice_sale_order(so) + invoice.recompute_lines_agents() + so.order_line.agent_ids._compute_amount() + invoice.line_ids.agent_ids._compute_amount() + self.assertEqual(so.order_line.agent_ids.percent_amount, 5) + self.assertEqual(invoice.line_ids.agent_ids.amount, 50) + + # Azure Spain Variant: Customizable Desk (CONFIG) (Steel, White) - Variant + so = self._create_sale_order(self.product_4, self.azure) + so.recompute_lines_agents() + so.action_confirm() + invoice = self._invoice_sale_order(so) + invoice.recompute_lines_agents() + so.order_line.agent_ids._compute_amount() + invoice.line_ids.agent_ids._compute_amount() + self.assertEqual(so.order_line.agent_ids.percent_amount, 15) + self.assertEqual(invoice.line_ids.agent_ids.amount, 150) + + # Deco Italy - All products + so = self._create_sale_order(self.product_product_25, self.deco) + so.recompute_lines_agents() + so.action_confirm() + invoice = self._invoice_sale_order(so) + invoice.recompute_lines_agents() + so.order_line.agent_ids._compute_amount() + invoice.line_ids.agent_ids._compute_amount() + self.assertEqual(so.order_line.agent_ids.fixed_amount, 10) + self.assertEqual(invoice.line_ids.agent_ids.amount, 10) + + def test_commission_domain(self): + # group must have commission of CI + self.demo_crr_item_1.group_id.commission_ids = False + self.demo_crr_item_1.write({"sequence": 2}) + self.assertTrue(self.demo_crr_item_1.group_id.commission_ids) + + # count related agents + self.demo_cig_italy._compute_agents_count() + # self.assertEqual(demo_cig_italy.agents_count, 1) + + # if commission is not type of restricted then CI must have no group + self.demo_crr_item_1.commission_id = self.demo_commission_rules + self.demo_crr_item_1.write({"sequence": 3}) + self.assertFalse(self.demo_crr_item_1.group_id) + + # commission.item.agent: check agent_group_ids computed properly + self.cia_azure._compute_agent_group_ids() + self.assertTrue(self.cia_azure.agent_group_ids) + + # commission.item.agent: check agent_group_ids is False when agent got no rules + self.cia_azure.agent_id = self.res_partner_tiny_sale_agent + self.cia_azure._compute_agent_group_ids() + self.assertFalse(self.cia_azure.agent_group_ids) + + # false, when commission is not product_restricted + self.res_partner_tiny_sale_agent.write( + { + "allowed_commission_group_ids": [(6, 0, self.demo_cig_spain.ids)], + } + ) + self.assertFalse(self.res_partner_tiny_sale_agent.allowed_commission_group_ids) + + # check some partners compute methods + self.azure._onchange_agent_ids() + self.azure._compute_allowed_commission_group_ids_domain() + self.assertFalse(self.azure.allowed_commission_group_ids_domain) + self.demo_agent_rules_restricted_italy._compute_allowed_commission_group_ids_domain() + self.assertTrue( + self.demo_agent_rules_restricted_italy.allowed_commission_group_ids_domain + ) + + # trigger window action + action = self.demo_cig_spain.action_open_related_agents() + self.assertEqual(type(action), dict) + + # you cant delete it having related CIs + with self.assertRaises(exception=odoo.exceptions.ValidationError): + self.demo_cig_spain.unlink() + + # + self.env["commission.items.group"].create({"name": "Delete Me"}).unlink() + + # computes was modified to consider new commission type: product_restricted + so = self._create_sale_order(self.product_4, self.azure) + so.recompute_lines_agents() + so.action_confirm() + invoice = self._invoice_sale_order(so) + invoice.recompute_lines_agents() + so.order_line.agent_ids._compute_amount() + invoice.line_ids.agent_ids._compute_amount() + self.assertEqual(so.order_line.agent_ids.amount, 150) + self.assertEqual(invoice.line_ids.agent_ids.amount, 150) + + # + tst_partner = so.partner_id.copy({}) + tst_partner.commission_item_agent_ids = [(6, 0, self.demo_cig_italy.ids)] + so.partner_id = tst_partner + so.order_line.agent_ids.agent_id = self.demo_agent_rules_restricted_italy + res = so.order_line.agent_ids._get_single_commission_amount( + self.demo_commission_rules_restrict, + 1, + self.conf_chair_config_id.product_variant_id, + 1, + ) + self.assertEqual(res, 20) + + # computes was modified to consider new commission type: product_restricted + self.demo_agent_rules_restricted_italy.commission_id = ( + self.demo_commission_rules_restrict + ) + so = self._create_sale_order(self.product_5, self.azure) + so.recompute_lines_agents() + so.action_confirm() + invoice = self._invoice_sale_order(so) + invoice.recompute_lines_agents() + so.order_line.agent_ids._compute_amount() + invoice.line_ids.agent_ids._compute_amount() + self.assertEqual(so.order_line.agent_ids.amount, 20) + self.assertEqual(invoice.line_ids.agent_ids.amount, 20) + + # computes was modified to consider new commission type: product_restricted + self.product_5.commission_free = True + so = self._create_sale_order(self.product_5, self.azure) + so.recompute_lines_agents() + so.action_confirm() + invoice = self._invoice_sale_order(so) + invoice.recompute_lines_agents() + so.order_line.agent_ids._compute_amount() + invoice.line_ids.agent_ids._compute_amount() + self.assertEqual(so.order_line.agent_ids.amount, 0) + self.assertEqual(invoice.line_ids.agent_ids.amount, 0) + + # computes was modified to consider new commission type: product_restricted + self.azure.agent_ids.commission_id = self.demo_commission_rules + so = self._create_sale_order(self.product_6, self.azure) + so.recompute_lines_agents() + so.action_confirm() + invoice = self._invoice_sale_order(so) + invoice.recompute_lines_agents() + so.order_line.agent_ids._compute_amount() + invoice.line_ids.agent_ids._compute_amount() + self.assertEqual(so.order_line.agent_ids.amount, 0) + self.assertEqual(invoice.line_ids.agent_ids.amount, 0) + + # computes was modified to consider new commission type: product_restricted + so = self._create_sale_order(self.product_1, self.azure) + so.recompute_lines_agents() + so.action_confirm() + invoice = self._invoice_sale_order(so) + invoice.recompute_lines_agents() + so.order_line.agent_ids._compute_amount() + invoice.line_ids.agent_ids._compute_amount() + self.assertEqual(so.order_line.agent_ids.amount, 15) + self.assertEqual(invoice.line_ids.agent_ids.amount, 15) + + # discount net_amount percentage + self.demo_agent_rules_restricted_spain.commission_id = ( + self.demo_commission_rules_restrict + ) + self.azure.commission_item_agent_ids.group_ids = [ + (6, 0, self.demo_cig_spain.ids) + ] + so = self._create_sale_order(self.pt_11.product_variant_id, self.azure) + so.order_line.discount = 20 + so.recompute_lines_agents() + so.action_confirm() + so.order_line.agent_ids.commission_id.item_ids.commission_type = "percentage" + so.order_line.agent_ids._compute_amount() + invoice.line_ids.agent_ids.commission_id = self.demo_commission_rules_restrict + invoice.line_ids.agent_ids.commission_id.amount_base_type = "net_amount" + invoice.line_ids.agent_ids.commission_id.item_ids.commission_type = "percentage" + invoice.line_ids.agent_ids._compute_amount() + self.assertEqual(so.order_line.agent_ids.amount, 0) + self.assertEqual(invoice.line_ids.agent_ids.amount, 0) + + # no commission items + self.demo_commission_rules_restrict.item_ids.unlink() + so = self._create_sale_order(self.pt_11.product_variant_id, self.azure) + so.recompute_lines_agents() + so.action_confirm() + so.order_line.agent_ids._compute_amount() + invoice.line_ids.agent_ids._compute_amount() + self.assertEqual(so.order_line.agent_ids.amount, 0) + self.assertEqual(invoice.line_ids.agent_ids.amount, 0) + + def _create_sale_order(self, product, partner): + return self.sale_order_model.create( + { + "partner_id": partner.id, + "order_line": [ + ( + 0, + 0, + { + "name": product.name, + "product_id": product.id, + "product_uom_qty": 1.0, + "product_uom": product.uom_id.id, + "price_unit": 1000, + }, + ) + ], + } + ) + + def _invoice_sale_order(self, sale_order, date=None): + old_invoices = sale_order.invoice_ids + wizard = self.advance_inv_model.create({"advance_payment_method": "delivered"}) + wizard.with_context( + { + "active_model": "sale.order", + "active_ids": [sale_order.id], + "active_id": sale_order.id, + } + ).create_invoices() + invoice = sale_order.invoice_ids - old_invoices + invoice.flush() + return invoice diff --git a/sale_commission_product_criteria_domain/views/views.xml b/sale_commission_product_criteria_domain/views/views.xml new file mode 100644 index 000000000..06355af79 --- /dev/null +++ b/sale_commission_product_criteria_domain/views/views.xml @@ -0,0 +1,219 @@ + + + + + res.partner.form.agent.inherit + res.partner + + + + + + + + + + + + + + + + + + + + + + + + commission.item.agent.form + commission.item.agent + + +
+ + + + + + + + + + + +
+
+
+ + + commission.item.agent.tree + commission.item.agent + + + + + + + + + + + + + + commission.item.tree.inherit + commission.item + + + + + + + + + + + commission.item.form.inherit + commission.item + + + + + + + + + + + commission.items.group.form + commission.items.group + +
+ +
+ +
+ + + + + + + +
+
+
+
+ + + commission.items.group.tree + commission.items.group + + + + + + + + + + Commission Type Items Groups + ir.actions.act_window + commission.items.group + tree,form + + + + + sale.commission.form.view.mod.inherit + sale.commission + + + + + {'invisible': [('commission_type', 'not in', ['product','product_restricted'])]} + + + + + + + + + + sale.commission.form.view.inherit + sale.commission + + + + + {'invisible': [('commission_type', 'in', ['product','product_restricted'])]} + + + + + + + +
diff --git a/setup/sale_commission_product_criteria_domain/odoo/addons/sale_commission_product_criteria_domain b/setup/sale_commission_product_criteria_domain/odoo/addons/sale_commission_product_criteria_domain new file mode 120000 index 000000000..1e9f2ff8a --- /dev/null +++ b/setup/sale_commission_product_criteria_domain/odoo/addons/sale_commission_product_criteria_domain @@ -0,0 +1 @@ +../../../../sale_commission_product_criteria_domain \ No newline at end of file diff --git a/setup/sale_commission_product_criteria_domain/setup.py b/setup/sale_commission_product_criteria_domain/setup.py new file mode 100644 index 000000000..28c57bb64 --- /dev/null +++ b/setup/sale_commission_product_criteria_domain/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +)