diff --git a/README.md b/README.md index aacbac966..600f3ca25 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,7 @@ addon | version | maintainers | summary [commission_formula](commission_formula/) | 16.0.1.0.0 | | Commissions computed by formulas [hr_commission](hr_commission/) | 16.0.1.0.0 | | HR commissions [sale_commission](sale_commission/) | 16.0.1.0.1 | [![pedrobaeza](https://github.com/pedrobaeza.png?size=30px)](https://github.com/pedrobaeza) | Sales commissions +[sale_commission_product_criteria](sale_commission_product_criteria/) | 16.0.1.0.0 | [![ilyasProgrammer](https://github.com/ilyasProgrammer.png?size=30px)](https://github.com/ilyasProgrammer) | Advanced commissions rules [sale_commission_salesman](sale_commission_salesman/) | 16.0.1.0.0 | | Sales commissions from salesman [//]: # (end addons) diff --git a/sale_commission_product_criteria/README.rst b/sale_commission_product_criteria/README.rst new file mode 100644 index 000000000..46954ac73 --- /dev/null +++ b/sale_commission_product_criteria/README.rst @@ -0,0 +1,116 @@ +================================ +Sale Commission Product Criteria +================================ + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:679118d6039b7893a5538b753c6809a13130884e390fb913b1a83cb29c0a0dd4 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fcommission-lightgray.png?logo=github + :target: https://github.com/OCA/commission/tree/16.0/sale_commission_product_criteria + :alt: OCA/commission +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/commission-16-0/commission-16-0-sale_commission_product_criteria + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png + :target: https://runboat.odoo-community.org/builds?repo=OCA/commission&target_branch=16.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module allows to set in the same Commission Type different commission rates according to the product on SO/invoice line. + +This is made possible since this module adds a new "Product criteria" type to Commission Type and applies commission rates with the same logic of sale pricelist items. + +For example, such a Commission Type can grant: + +10% on a specific Product A, +10$ on Product B, +4% on products in Category 1 and +5$ on all other products. + +In SO/invoice, system will apply different commissions based on variant/product/category or global, applied hierarchically. This means that for the example above, if product A is assigned to Category 1, commission assigned is 10%, as per variant/product/category/global rule application order. + +Furthermore, these commission type items can be accessed and created by a specific menu, to facilitate their management in environments with lots of records. + +The form for commission type item can be extended by future modules with further conditions to decide when to apply a specific item. + +**Table of contents** + +.. contents:: + :local: + +Usage +===== + +To use features of this module, you need to: + +#. Go to Commissions > Configuration > Commission Types. +#. Create a Commission Type with type = "Product criteria". +#. Create multiple rules based on variant/product/category or global +#. These rules will be sorted according to the same logic of sale pricelist. +#. Rest flow is according to OCA sale_commission module. + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* Ilyas +* Ooops404 + +Contributors +~~~~~~~~~~~~ + +* `Ooops404 `__: + + * Ilyas +* `Aion Tech `_: + + * Simone Rubino + +Maintainers +~~~~~~~~~~~ + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +.. |maintainer-ilyasProgrammer| image:: https://github.com/ilyasProgrammer.png?size=40px + :target: https://github.com/ilyasProgrammer + :alt: ilyasProgrammer + +Current `maintainer `__: + +|maintainer-ilyasProgrammer| + +This module is part of the `OCA/commission `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/sale_commission_product_criteria/__init__.py b/sale_commission_product_criteria/__init__.py new file mode 100644 index 000000000..0650744f6 --- /dev/null +++ b/sale_commission_product_criteria/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/sale_commission_product_criteria/__manifest__.py b/sale_commission_product_criteria/__manifest__.py new file mode 100644 index 000000000..d8cc78bf6 --- /dev/null +++ b/sale_commission_product_criteria/__manifest__.py @@ -0,0 +1,22 @@ +# © 2023 ooops404 +# Copyright 2023 Simone Rubino - Aion Tech +# License AGPL-3 - See https://www.gnu.org/licenses/agpl-3.0.html +{ + "name": "Sale Commission Product Criteria", + "summary": "Advanced commissions rules", + "version": "16.0.1.0.0", + "author": "Ilyas, Ooops404, Odoo Community Association (OCA)", + "maintainers": ["ilyasProgrammer"], + "website": "https://github.com/OCA/commission", + "category": "Sales Management", + "license": "AGPL-3", + "depends": ["sale_commission"], + "data": [ + "views/views.xml", + "security/ir.model.access.csv", + ], + "demo": ["demo/sale_agent_demo.xml"], + "application": False, + "installable": True, + "auto_install": False, +} diff --git a/sale_commission_product_criteria/demo/sale_agent_demo.xml b/sale_commission_product_criteria/demo/sale_agent_demo.xml new file mode 100644 index 000000000..caffc6e4a --- /dev/null +++ b/sale_commission_product_criteria/demo/sale_agent_demo.xml @@ -0,0 +1,69 @@ + + + + + + Based on Rules + product + + + + + + sol + 3_global + fixed + 10 + + + + + + sol + 2_product_category + fixed + 20 + + + + + + + sol + 1_product + percentage + 5 + + + + + + + sol + 0_product_variant + percentage + 15 + + + + + Agent Rules + True + True + + + + + + + + diff --git a/sale_commission_product_criteria/i18n/it.po b/sale_commission_product_criteria/i18n/it.po new file mode 100644 index 000000000..68496f24e --- /dev/null +++ b/sale_commission_product_criteria/i18n/it.po @@ -0,0 +1,427 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * sale_commission_product_criteria +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 14.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2023-05-24 12:10+0000\n" +"Last-Translator: Francesco Foresti \n" +"Language-Team: none\n" +"Language: it\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.17\n" + +#. module: sale_commission_product_criteria +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_commission_item__active +msgid "Active" +msgstr "Attivo" + +#. module: sale_commission_product_criteria +#: model:ir.model,name:sale_commission_product_criteria.model_account_invoice_line_agent +msgid "Agent detail of commission line in invoice lines" +msgstr "Dettaglio riga provvigione agente nelle righe fattura" + +#. module: sale_commission_product_criteria +#: model:ir.model,name:sale_commission_product_criteria.model_sale_order_line_agent +msgid "Agent detail of commission line in order lines" +msgstr "Dettaglio riga provvigione agente nelle righe ordine" + +#. module: sale_commission_product_criteria +#: code:addons/sale_commission_product_criteria/models/commission.py:0 +#: model:ir.model.fields.selection,name:sale_commission_product_criteria.selection__commission_item__applied_on__3_global +#, python-format +msgid "All Products" +msgstr "Tutti i prodotti" + +#. module: sale_commission_product_criteria +#: model_terms:ir.ui.view,arch_db:sale_commission_product_criteria.view_sale_order_line_tree_mod +msgid "Amount (%)" +msgstr "Importo (%)" + +#. module: sale_commission_product_criteria +#: model_terms:ir.ui.view,arch_db:sale_commission_product_criteria.view_sale_order_line_tree_mod +msgid "Amount (fixed)" +msgstr "Importo (fisso)" + +#. module: sale_commission_product_criteria +#: model:ir.model.fields.selection,name:sale_commission_product_criteria.selection__commission_item__based_on__sol +msgid "Any Sale Order Line" +msgstr "Qualsiasi riga ordine di vendita" + +#. module: sale_commission_product_criteria +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_account_invoice_line_agent__applied_commission_id +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_sale_commission_line_mixin__applied_commission_id +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_sale_order_line_agent__applied_commission_id +msgid "Applied Commission" +msgstr "Provvigione applicata" + +#. module: sale_commission_product_criteria +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_account_invoice_line_agent__applied_commission_item_id +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_sale_order_line_agent__applied_commission_item_id +msgid "Applied Commission Item" +msgstr "Riga provvigione applicata" + +#. module: sale_commission_product_criteria +#: model_terms:ir.ui.view,arch_db:sale_commission_product_criteria.view_sale_order_line_tree_mod +msgid "Applied Disc. (%)" +msgstr "Sc. applicato (%)" + +#. module: sale_commission_product_criteria +#: model_terms:ir.ui.view,arch_db:sale_commission_product_criteria.sale_commission_form_lines_mod +#: model_terms:ir.ui.view,arch_db:sale_commission_product_criteria.view_sale_order_line_tree_mod +msgid "Applied On" +msgstr "Applicato a" + +#. module: sale_commission_product_criteria +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_commission_item__applied_on +msgid "Apply On" +msgstr "Applica a" + +#. module: sale_commission_product_criteria +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_commission_item__based_on +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_sale_order_line_agent__based_on +#: model_terms:ir.ui.view,arch_db:sale_commission_product_criteria.view_sale_order_line_tree_mod +msgid "Based On" +msgstr "Basato su" + +#. module: sale_commission_product_criteria +#: code:addons/sale_commission_product_criteria/models/commission.py:0 +#, python-format +msgid "Category: %s" +msgstr "Categoria: %s" + +#. module: sale_commission_product_criteria +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_account_invoice_line_agent__commission_id +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_sale_commission_line_mixin__commission_id +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_sale_order_line_agent__commission_id +#: model_terms:ir.ui.view,arch_db:sale_commission_product_criteria.commission_item_search +msgid "Commission" +msgstr "Provvigione" + +#. module: sale_commission_product_criteria +#: model_terms:ir.ui.view,arch_db:sale_commission_product_criteria.commission_item_form_view +msgid "Commission Computation" +msgstr "Calcolo provvigione" + +#. module: sale_commission_product_criteria +#: model:ir.model,name:sale_commission_product_criteria.model_commission_item +#: model_terms:ir.ui.view,arch_db:sale_commission_product_criteria.commission_item_form_view +msgid "Commission Item" +msgstr "Riga provvigione" + +#. module: sale_commission_product_criteria +#: model:ir.model.fields,help:sale_commission_product_criteria.field_commission_item__applied_on +msgid "Commission Item applicable on selected option" +msgstr "Riga provvigione applicabile all'opzione selezionata" + +#. module: sale_commission_product_criteria +#: model:ir.actions.act_window,name:sale_commission_product_criteria.commission_item_action +#: model:ir.actions.act_window,name:sale_commission_product_criteria.commission_item_action_tree +#: model_terms:ir.ui.view,arch_db:sale_commission_product_criteria.commission_item_search +#: model_terms:ir.ui.view,arch_db:sale_commission_product_criteria.commission_item_tree_view +msgid "Commission Items" +msgstr "Righe provvigione" + +#. module: sale_commission_product_criteria +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_commission_item__commission_id +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_commission_item__commission_type +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_sale_order_line_agent__commission_type +msgid "Commission Type" +msgstr "Tipo di provvigione" + +#. module: sale_commission_product_criteria +#: model:ir.ui.menu,name:sale_commission_product_criteria.menu_sale_commissions_items +msgid "Commission Type Items" +msgstr "Righe tipi di provvigione" + +#. module: sale_commission_product_criteria +#: model:ir.model,name:sale_commission_product_criteria.model_sale_commission +msgid "Commission in sales" +msgstr "Provvigione nelle vendite" + +#. module: sale_commission_product_criteria +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_commission_item__company_id +msgid "Company" +msgstr "Azienda" + +#. module: sale_commission_product_criteria +#: model_terms:ir.ui.view,arch_db:sale_commission_product_criteria.commission_item_form_view +msgid "Compute Price" +msgstr "Calcola prezzo" + +#. module: sale_commission_product_criteria +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_commission_item__create_uid +msgid "Created by" +msgstr "Creato da" + +#. module: sale_commission_product_criteria +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_commission_item__create_date +msgid "Created on" +msgstr "Creato il" + +#. module: sale_commission_product_criteria +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_commission_item__currency_id +msgid "Currency" +msgstr "Valuta" + +#. module: sale_commission_product_criteria +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_sale_order_line_agent__discount +msgid "Discount (%)" +msgstr "Sconto (%)" + +#. module: sale_commission_product_criteria +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_account_invoice_line_agent__display_name +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_commission_item__display_name +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_sale_commission__display_name +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_sale_commission_line_mixin__display_name +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_sale_commission_settlement_line__display_name +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_sale_order_line__display_name +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_sale_order_line_agent__display_name +msgid "Display Name" +msgstr "Nome visualizzato" + +#. module: sale_commission_product_criteria +#: model:ir.model.fields,help:sale_commission_product_criteria.field_commission_item__name +#: model:ir.model.fields,help:sale_commission_product_criteria.field_sale_order_line_agent__applied_on_name +msgid "Explicit rule name for this commission line." +msgstr "Nome esplicito regola per questa riga provvigione." + +#. module: sale_commission_product_criteria +#: model_terms:ir.ui.view,arch_db:sale_commission_product_criteria.view_sale_order_line_tree_mod +msgid "Final Amount" +msgstr "Importo finale" + +#. module: sale_commission_product_criteria +#: model:ir.model.fields.selection,name:sale_commission_product_criteria.selection__commission_item__commission_type__fixed +msgid "Fixed" +msgstr "Fisso" + +#. module: sale_commission_product_criteria +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_commission_item__fixed_amount +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_sale_order_line_agent__fixed_amount +msgid "Fixed Amount" +msgstr "Importo fisso" + +#. module: sale_commission_product_criteria +#: model_terms:ir.ui.view,arch_db:sale_commission_product_criteria.commission_item_search +msgid "Group By" +msgstr "Raggruppa per" + +#. module: sale_commission_product_criteria +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_account_invoice_line_agent__id +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_commission_item__id +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_sale_commission__id +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_sale_commission_line_mixin__id +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_sale_commission_settlement_line__id +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_sale_order_line__id +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_sale_order_line_agent__id +msgid "ID" +msgstr "ID" + +#. module: sale_commission_product_criteria +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_sale_commission__item_ids +msgid "Item" +msgstr "Riga" + +#. module: sale_commission_product_criteria +#: model_terms:ir.ui.view,arch_db:sale_commission_product_criteria.sale_commission_form_lines_mod +msgid "Items" +msgstr "Righe" + +#. module: sale_commission_product_criteria +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_account_invoice_line_agent____last_update +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_commission_item____last_update +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_sale_commission____last_update +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_sale_commission_line_mixin____last_update +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_sale_commission_settlement_line____last_update +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_sale_order_line____last_update +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_sale_order_line_agent____last_update +msgid "Last Modified on" +msgstr "Ultima modifica il" + +#. module: sale_commission_product_criteria +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_commission_item__write_uid +msgid "Last Updated by" +msgstr "Ultimo aggiornamento di" + +#. module: sale_commission_product_criteria +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_commission_item__write_date +msgid "Last Updated on" +msgstr "Ultimo aggiornamento il" + +#. module: sale_commission_product_criteria +#: model:ir.model,name:sale_commission_product_criteria.model_sale_commission_settlement_line +msgid "Line of a commission settlement" +msgstr "Riga di regolazione provvigione" + +#. module: sale_commission_product_criteria +#: model:ir.model,name:sale_commission_product_criteria.model_sale_commission_line_mixin +msgid "" +"Mixin model for having commission agent lines in any object inheriting from " +"this one" +msgstr "" +"Modello mixin per avere righe di provvigione agente in qualsiasi oggetto che " +"erediti dall'attuale" + +#. module: sale_commission_product_criteria +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_commission_item__name +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_sale_order_line_agent__applied_on_name +msgid "Name" +msgstr "Nome" + +#. module: sale_commission_product_criteria +#: model:ir.model.fields.selection,name:sale_commission_product_criteria.selection__commission_item__commission_type__percentage +msgid "Percentage" +msgstr "Percentuale" + +#. module: sale_commission_product_criteria +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_commission_item__percent_amount +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_sale_order_line_agent__percent_amount +msgid "Percentage Amount" +msgstr "Importo percentuale" + +#. module: sale_commission_product_criteria +#: code:addons/sale_commission_product_criteria/models/commission.py:0 +#, python-format +msgid "Please specify the category for which this rule should be applied" +msgstr "" +"Specifica la categoria per la quale questa regola deve essere applicata" + +#. module: sale_commission_product_criteria +#: code:addons/sale_commission_product_criteria/models/commission.py:0 +#, python-format +msgid "Please specify the product for which this rule should be applied" +msgstr "Specifica il prodotto per la quale questa regola deve essere applicata" + +#. module: sale_commission_product_criteria +#: code:addons/sale_commission_product_criteria/models/commission.py:0 +#, python-format +msgid "" +"Please specify the product variant for which this rule should be applied" +msgstr "" +"Specifica la variante prodotto per la quale questa regola deve essere " +"applicata" + +#. module: sale_commission_product_criteria +#: model_terms:ir.ui.view,arch_db:sale_commission_product_criteria.view_sale_order_line_tree_mod +msgid "Price Type" +msgstr "Tipo prezzo" + +#. module: sale_commission_product_criteria +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_commission_item__product_tmpl_id +#: model:ir.model.fields.selection,name:sale_commission_product_criteria.selection__commission_item__applied_on__1_product +msgid "Product" +msgstr "Prodotto" + +#. module: sale_commission_product_criteria +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_commission_item__categ_id +#: model:ir.model.fields.selection,name:sale_commission_product_criteria.selection__commission_item__applied_on__2_product_category +msgid "Product Category" +msgstr "Categoria prodotto" + +#. module: sale_commission_product_criteria +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_commission_item__product_id +#: model:ir.model.fields.selection,name:sale_commission_product_criteria.selection__commission_item__applied_on__0_product_variant +msgid "Product Variant" +msgstr "Variante prodotto" + +#. module: sale_commission_product_criteria +#: model:ir.model.fields.selection,name:sale_commission_product_criteria.selection__sale_commission__commission_type__product +msgid "Product criteria" +msgstr "Criteri prodotto" + +#. module: sale_commission_product_criteria +#: code:addons/sale_commission_product_criteria/models/commission.py:0 +#, python-format +msgid "Product: %s" +msgstr "Prodotto: %s" + +#. module: sale_commission_product_criteria +#: model_terms:ir.ui.view,arch_db:sale_commission_product_criteria.sale_commission_form_lines_mod +msgid "Rules" +msgstr "Regole" + +#. module: sale_commission_product_criteria +#: model:ir.model,name:sale_commission_product_criteria.model_sale_order_line +msgid "Sales Order Line" +msgstr "Riga ordine di vendita" + +#. module: sale_commission_product_criteria +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_commission_item__sequence +msgid "Sequence" +msgstr "Sequenza" + +#. module: sale_commission_product_criteria +#: model:ir.model.fields,help:sale_commission_product_criteria.field_commission_item__categ_id +msgid "" +"Specify a product category if this rule only applies to products belonging " +"to this category or its children categories. Keep empty otherwise." +msgstr "" +"Specifica una categoria prodotto se questa regola si applica solo ai " +"prodotti appartenenti a questa categoria o alle sue categorie figlie, " +"altrimenti lascia vuoto." + +#. module: sale_commission_product_criteria +#: model:ir.model.fields,help:sale_commission_product_criteria.field_commission_item__product_id +msgid "" +"Specify a product if this rule only applies to one product. Keep empty " +"otherwise." +msgstr "" +"Specifica un prodotto se questa regola si applica solo ad un prodotto, " +"altrimenti lascia vuoto." + +#. module: sale_commission_product_criteria +#: model:ir.model.fields,help:sale_commission_product_criteria.field_commission_item__product_tmpl_id +msgid "" +"Specify a template if this rule only applies to one product template. Keep " +"empty otherwise." +msgstr "" +"Specifica un modello prodotto se questa regola si applica solo ad un modello " +"prodotto, altrimenti lascia vuoto." + +#. module: sale_commission_product_criteria +#: code:addons/sale_commission_product_criteria/models/commission.py:0 +#, python-format +msgid "" +"There is done Sale Orders with this commission. Commission type change is " +"not allowed." +msgstr "" +"Esistono ordini di vendita che usano questo tipo di provvigione. Non è " +"possibile modificare il tipo." + +#. module: sale_commission_product_criteria +#: code:addons/sale_commission_product_criteria/models/commission.py:0 +#, python-format +msgid "" +"There is posted Account Move Lines with this commission. Commission type " +"change is not allowed." +msgstr "" +"Esistono righe fattura che usano questo tipo di provvigione. Non è possibile " +"modificare il tipo." + +#. module: sale_commission_product_criteria +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_sale_commission__commission_type +msgid "Type" +msgstr "Tipo" + +#. module: sale_commission_product_criteria +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_commission_item__commission_value +msgid "Value" +msgstr "Valore" + +#. module: sale_commission_product_criteria +#: code:addons/sale_commission_product_criteria/models/commission.py:0 +#, python-format +msgid "Variant: %s" +msgstr "Variante: %s" + +#~ msgid "Pricelist" +#~ msgstr "Listino prezzi" + +#~ msgid "Use Pricelist" +#~ msgstr "Usa listino prezzi" diff --git a/sale_commission_product_criteria/i18n/sale_commission_product_criteria.pot b/sale_commission_product_criteria/i18n/sale_commission_product_criteria.pot new file mode 100644 index 000000000..b636d0ac7 --- /dev/null +++ b/sale_commission_product_criteria/i18n/sale_commission_product_criteria.pot @@ -0,0 +1,396 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * sale_commission_product_criteria +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: sale_commission_product_criteria +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_commission_item__active +msgid "Active" +msgstr "" + +#. module: sale_commission_product_criteria +#: model:ir.model,name:sale_commission_product_criteria.model_account_invoice_line_agent +msgid "Agent detail of commission line in invoice lines" +msgstr "" + +#. module: sale_commission_product_criteria +#: model:ir.model,name:sale_commission_product_criteria.model_sale_order_line_agent +msgid "Agent detail of commission line in order lines" +msgstr "" + +#. module: sale_commission_product_criteria +#. odoo-python +#: code:addons/sale_commission_product_criteria/models/commission.py:0 +#: model:ir.model.fields.selection,name:sale_commission_product_criteria.selection__commission_item__applied_on__3_global +#, python-format +msgid "All Products" +msgstr "" + +#. module: sale_commission_product_criteria +#: model_terms:ir.ui.view,arch_db:sale_commission_product_criteria.view_sale_order_line_tree_mod +msgid "Amount (%)" +msgstr "" + +#. module: sale_commission_product_criteria +#: model_terms:ir.ui.view,arch_db:sale_commission_product_criteria.view_sale_order_line_tree_mod +msgid "Amount (fixed)" +msgstr "" + +#. module: sale_commission_product_criteria +#: model:ir.model.fields.selection,name:sale_commission_product_criteria.selection__commission_item__based_on__sol +msgid "Any Sale Order Line" +msgstr "" + +#. module: sale_commission_product_criteria +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_account_invoice_line_agent__applied_commission_id +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_commission_line_mixin__applied_commission_id +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_sale_order_line_agent__applied_commission_id +msgid "Applied Commission" +msgstr "" + +#. module: sale_commission_product_criteria +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_account_invoice_line_agent__applied_commission_item_id +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_sale_order_line_agent__applied_commission_item_id +msgid "Applied Commission Item" +msgstr "" + +#. module: sale_commission_product_criteria +#: model_terms:ir.ui.view,arch_db:sale_commission_product_criteria.view_sale_order_line_tree_mod +msgid "Applied Disc. (%)" +msgstr "" + +#. module: sale_commission_product_criteria +#: model_terms:ir.ui.view,arch_db:sale_commission_product_criteria.sale_commission_form_lines_mod +#: model_terms:ir.ui.view,arch_db:sale_commission_product_criteria.view_sale_order_line_tree_mod +msgid "Applied On" +msgstr "" + +#. module: sale_commission_product_criteria +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_commission_item__applied_on +msgid "Apply On" +msgstr "" + +#. module: sale_commission_product_criteria +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_commission_item__based_on +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_sale_order_line_agent__based_on +#: model_terms:ir.ui.view,arch_db:sale_commission_product_criteria.view_sale_order_line_tree_mod +msgid "Based On" +msgstr "" + +#. module: sale_commission_product_criteria +#. odoo-python +#: code:addons/sale_commission_product_criteria/models/commission.py:0 +#, python-format +msgid "Category: %s" +msgstr "" + +#. module: sale_commission_product_criteria +#: model:ir.model,name:sale_commission_product_criteria.model_commission +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_account_invoice_line_agent__commission_id +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_commission_item__commission_id +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_commission_line_mixin__commission_id +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_sale_order_line_agent__commission_id +#: model_terms:ir.ui.view,arch_db:sale_commission_product_criteria.commission_item_search +msgid "Commission" +msgstr "" + +#. module: sale_commission_product_criteria +#: model_terms:ir.ui.view,arch_db:sale_commission_product_criteria.commission_item_form_view +msgid "Commission Computation" +msgstr "" + +#. module: sale_commission_product_criteria +#: model:ir.model,name:sale_commission_product_criteria.model_commission_item +#: model_terms:ir.ui.view,arch_db:sale_commission_product_criteria.commission_item_form_view +msgid "Commission Item" +msgstr "" + +#. module: sale_commission_product_criteria +#: model:ir.model.fields,help:sale_commission_product_criteria.field_commission_item__applied_on +msgid "Commission Item applicable on selected option" +msgstr "" + +#. module: sale_commission_product_criteria +#: model:ir.actions.act_window,name:sale_commission_product_criteria.commission_item_action +#: model:ir.actions.act_window,name:sale_commission_product_criteria.commission_item_action_tree +#: model_terms:ir.ui.view,arch_db:sale_commission_product_criteria.commission_item_search +msgid "Commission Items" +msgstr "" + +#. module: sale_commission_product_criteria +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_commission_item__commission_type +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_sale_order_line_agent__commission_type +msgid "Commission Type" +msgstr "" + +#. module: sale_commission_product_criteria +#: model:ir.ui.menu,name:sale_commission_product_criteria.menu_sale_commissions_items +msgid "Commission Type Items" +msgstr "" + +#. module: sale_commission_product_criteria +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_commission_item__company_id +msgid "Company" +msgstr "" + +#. module: sale_commission_product_criteria +#: model_terms:ir.ui.view,arch_db:sale_commission_product_criteria.commission_item_form_view +msgid "Compute Price" +msgstr "" + +#. module: sale_commission_product_criteria +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_commission_item__create_uid +msgid "Created by" +msgstr "" + +#. module: sale_commission_product_criteria +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_commission_item__create_date +msgid "Created on" +msgstr "" + +#. module: sale_commission_product_criteria +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_commission_item__currency_id +msgid "Currency" +msgstr "" + +#. module: sale_commission_product_criteria +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_sale_order_line_agent__discount +msgid "Discount (%)" +msgstr "" + +#. module: sale_commission_product_criteria +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_commission_item__display_name +msgid "Display Name" +msgstr "" + +#. module: sale_commission_product_criteria +#: model_terms:ir.ui.view,arch_db:sale_commission_product_criteria.view_order_agent_form_inherit_sale_commission_product_criteria_mod +msgid "Edit agents" +msgstr "" + +#. module: sale_commission_product_criteria +#: model:ir.model.fields,help:sale_commission_product_criteria.field_commission_item__name +#: model:ir.model.fields,help:sale_commission_product_criteria.field_sale_order_line_agent__applied_on_name +msgid "Explicit rule name for this commission line." +msgstr "" + +#. module: sale_commission_product_criteria +#: model_terms:ir.ui.view,arch_db:sale_commission_product_criteria.view_sale_order_line_tree_mod +msgid "Final Amount" +msgstr "" + +#. module: sale_commission_product_criteria +#: model:ir.model.fields.selection,name:sale_commission_product_criteria.selection__commission_item__commission_type__fixed +msgid "Fixed" +msgstr "" + +#. module: sale_commission_product_criteria +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_commission_item__fixed_amount +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_sale_order_line_agent__fixed_amount +msgid "Fixed Amount" +msgstr "" + +#. module: sale_commission_product_criteria +#: model_terms:ir.ui.view,arch_db:sale_commission_product_criteria.commission_item_search +msgid "Group By" +msgstr "" + +#. module: sale_commission_product_criteria +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_commission_item__id +msgid "ID" +msgstr "" + +#. module: sale_commission_product_criteria +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_commission__item_ids +msgid "Item" +msgstr "" + +#. module: sale_commission_product_criteria +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_commission_item____last_update +msgid "Last Modified on" +msgstr "" + +#. module: sale_commission_product_criteria +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_commission_item__write_uid +msgid "Last Updated by" +msgstr "" + +#. module: sale_commission_product_criteria +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_commission_item__write_date +msgid "Last Updated on" +msgstr "" + +#. module: sale_commission_product_criteria +#: model:ir.model,name:sale_commission_product_criteria.model_commission_settlement_line +msgid "Line of a commission settlement" +msgstr "" + +#. module: sale_commission_product_criteria +#: model:ir.model,name:sale_commission_product_criteria.model_commission_line_mixin +msgid "" +"Mixin model for having commission agent lines in any object inheriting from " +"this one" +msgstr "" + +#. module: sale_commission_product_criteria +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_commission_item__name +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_sale_order_line_agent__applied_on_name +msgid "Name" +msgstr "" + +#. module: sale_commission_product_criteria +#: model:ir.model.fields.selection,name:sale_commission_product_criteria.selection__commission_item__commission_type__percentage +msgid "Percentage" +msgstr "" + +#. module: sale_commission_product_criteria +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_commission_item__percent_amount +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_sale_order_line_agent__percent_amount +msgid "Percentage Amount" +msgstr "" + +#. module: sale_commission_product_criteria +#. odoo-python +#: code:addons/sale_commission_product_criteria/models/commission.py:0 +#, python-format +msgid "Please specify the category for which this rule should be applied" +msgstr "" + +#. module: sale_commission_product_criteria +#. odoo-python +#: code:addons/sale_commission_product_criteria/models/commission.py:0 +#, python-format +msgid "Please specify the product for which this rule should be applied" +msgstr "" + +#. module: sale_commission_product_criteria +#. odoo-python +#: code:addons/sale_commission_product_criteria/models/commission.py:0 +#, python-format +msgid "" +"Please specify the product variant for which this rule should be applied" +msgstr "" + +#. module: sale_commission_product_criteria +#: model_terms:ir.ui.view,arch_db:sale_commission_product_criteria.view_sale_order_line_tree_mod +msgid "Price Type" +msgstr "" + +#. module: sale_commission_product_criteria +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_commission_item__product_tmpl_id +#: model:ir.model.fields.selection,name:sale_commission_product_criteria.selection__commission_item__applied_on__1_product +msgid "Product" +msgstr "" + +#. module: sale_commission_product_criteria +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_commission_item__categ_id +#: model:ir.model.fields.selection,name:sale_commission_product_criteria.selection__commission_item__applied_on__2_product_category +msgid "Product Category" +msgstr "" + +#. module: sale_commission_product_criteria +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_commission_item__product_id +#: model:ir.model.fields.selection,name:sale_commission_product_criteria.selection__commission_item__applied_on__0_product_variant +msgid "Product Variant" +msgstr "" + +#. module: sale_commission_product_criteria +#: model:ir.model.fields.selection,name:sale_commission_product_criteria.selection__commission__commission_type__product +msgid "Product criteria" +msgstr "" + +#. module: sale_commission_product_criteria +#. odoo-python +#: code:addons/sale_commission_product_criteria/models/commission.py:0 +#, python-format +msgid "Product: %s" +msgstr "" + +#. module: sale_commission_product_criteria +#: model_terms:ir.ui.view,arch_db:sale_commission_product_criteria.sale_commission_form_lines_mod +msgid "Rules" +msgstr "" + +#. module: sale_commission_product_criteria +#: model:ir.model,name:sale_commission_product_criteria.model_sale_order_line +msgid "Sales Order Line" +msgstr "" + +#. module: sale_commission_product_criteria +#: model:ir.model.fields,help:sale_commission_product_criteria.field_commission__commission_type +msgid "" +"Select the percentage type of the commission:\n" +"* 'Fixed percentage': all commissions are computed with a fixed percentage. You can fill the percentage in the field \"Fixed percentage\".\n" +"* 'By sections': percentage varies depending amount intervals. You can fill intervals and percentages in the section \"Rate definition\"." +msgstr "" + +#. module: sale_commission_product_criteria +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_commission_item__sequence +msgid "Sequence" +msgstr "" + +#. module: sale_commission_product_criteria +#: model:ir.model.fields,help:sale_commission_product_criteria.field_commission_item__categ_id +msgid "" +"Specify a product category if this rule only applies to products belonging " +"to this category or its children categories. Keep empty otherwise." +msgstr "" + +#. module: sale_commission_product_criteria +#: model:ir.model.fields,help:sale_commission_product_criteria.field_commission_item__product_id +msgid "" +"Specify a product if this rule only applies to one product. Keep empty " +"otherwise." +msgstr "" + +#. module: sale_commission_product_criteria +#: model:ir.model.fields,help:sale_commission_product_criteria.field_commission_item__product_tmpl_id +msgid "" +"Specify a template if this rule only applies to one product template. Keep " +"empty otherwise." +msgstr "" + +#. module: sale_commission_product_criteria +#. odoo-python +#: code:addons/sale_commission_product_criteria/models/commission.py:0 +#, python-format +msgid "" +"There is done Sale Orders with this commission. Commission type change is " +"not allowed." +msgstr "" + +#. module: sale_commission_product_criteria +#. odoo-python +#: code:addons/sale_commission_product_criteria/models/commission.py:0 +#, python-format +msgid "" +"There is posted Account Move Lines with this commission. Commission type " +"change is not allowed." +msgstr "" + +#. module: sale_commission_product_criteria +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_commission__commission_type +msgid "Type" +msgstr "" + +#. module: sale_commission_product_criteria +#: model:ir.model.fields,field_description:sale_commission_product_criteria.field_commission_item__commission_value +msgid "Value" +msgstr "" + +#. module: sale_commission_product_criteria +#. odoo-python +#: code:addons/sale_commission_product_criteria/models/commission.py:0 +#, python-format +msgid "Variant: %s" +msgstr "" diff --git a/sale_commission_product_criteria/models/__init__.py b/sale_commission_product_criteria/models/__init__.py new file mode 100644 index 000000000..b14568ddb --- /dev/null +++ b/sale_commission_product_criteria/models/__init__.py @@ -0,0 +1,5 @@ +from . import sale_commission_line_mixin +from . import commission +from . import sale +from . import account_move +from . import settlement diff --git a/sale_commission_product_criteria/models/account_move.py b/sale_commission_product_criteria/models/account_move.py new file mode 100644 index 000000000..2b8cda490 --- /dev/null +++ b/sale_commission_product_criteria/models/account_move.py @@ -0,0 +1,30 @@ +# © 2023 ooops404 +# Copyright 2023 Simone Rubino - Aion Tech +# License AGPL-3 - See https://www.gnu.org/licenses/agpl-3.0.html +from odoo import api, fields, models + + +class AccountInvoiceLineAgent(models.Model): + _inherit = "account.invoice.line.agent" + + applied_commission_item_id = fields.Many2one("commission.item") + + @api.depends( + "object_id.price_subtotal", + "object_id.product_id.commission_free", + "commission_id", + ) + def _compute_amount(self): + res = None + for line in self: + if line.commission_id and line.commission_id.commission_type == "product": + 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: + res = super(AccountInvoiceLineAgent, line)._compute_amount() + return res diff --git a/sale_commission_product_criteria/models/commission.py b/sale_commission_product_criteria/models/commission.py new file mode 100644 index 000000000..18a970f45 --- /dev/null +++ b/sale_commission_product_criteria/models/commission.py @@ -0,0 +1,266 @@ +# © 2023 ooops404 +# Copyright 2023 Simone Rubino - Aion Tech +# License AGPL-3 - See https://www.gnu.org/licenses/agpl-3.0.html +from odoo import _, api, fields, models +from odoo.exceptions import ValidationError +from odoo.tools import float_repr + + +class SaleCommission(models.Model): + _inherit = "commission" + + commission_type = fields.Selection( + selection_add=[("product", "Product criteria")], + ondelete={"product": "set default"}, + ) + item_ids = fields.One2many("commission.item", "commission_id", copy=True) + + def action_unarchive(self): + res = super().action_unarchive() + items = ( + self.env["commission.item"] + .with_context(active_test=False) + .search([("commission_id", "=", self.id)]) + ) + if items: + items.write({"active": True}) + return res + + @api.onchange("commission_type") + def onchange_commission_type(self): + # Prevent commission_type change in certain cases + self.check_type_change_allowed_sale() + self.check_type_change_allowed_moves() + + def check_type_change_allowed_sale(self): + sola_ids = self.env["sale.order.line.agent"].search( + [("commission_id", "=", self._origin.id)] + ) + done_so_ids = sola_ids.filtered(lambda x: x.object_id.state in ["done", "sale"]) + if done_so_ids: + raise ValidationError( + _( + "There is done Sale Orders with this commission. " + "Commission type change is not allowed." + ) + ) + + def check_type_change_allowed_moves(self): + aila_ids = self.env["account.invoice.line.agent"].search( + [("commission_id", "=", self._origin.id)] + ) + done_move_ids = aila_ids.filtered( + lambda x: x.object_id.parent_state == "posted" + ) + if done_move_ids: + raise ValidationError( + _( + "There is posted Account Move Lines with this commission. " + "Commission type change is not allowed." + ) + ) + + +class CommissionItem(models.Model): + _name = "commission.item" + _description = "Commission Item" + _order = "applied_on, based_on, categ_id desc, id desc" + + sequence = fields.Integer(default=10) + active = fields.Boolean(default=True) + commission_id = fields.Many2one( + "commission", + string="Commission", + domain=[("commission_type", "=", "product")], + required=True, + ) + product_tmpl_id = fields.Many2one( + "product.template", + "Product", + ondelete="cascade", + check_company=True, + help="Specify a template if this rule only applies to one " + "product template. Keep empty otherwise.", + ) + product_id = fields.Many2one( + "product.product", + "Product Variant", + ondelete="cascade", + check_company=True, + help="Specify a product if this rule only applies " + "to one product. Keep empty otherwise.", + ) + categ_id = fields.Many2one( + "product.category", + "Product Category", + ondelete="cascade", + help="Specify a product category if this rule only applies to " + "products belonging to this category or its children categories. " + "Keep empty otherwise.", + ) + based_on = fields.Selection( + [("sol", "Any Sale Order Line")], + required=True, + default="sol", + ) + applied_on = fields.Selection( + [ + ("3_global", "All Products"), + ("2_product_category", "Product Category"), + ("1_product", "Product"), + ("0_product_variant", "Product Variant"), + ], + "Apply On", + default="3_global", + required=True, + help="Commission Item applicable on selected option", + ) + commission_type = fields.Selection( + [("fixed", "Fixed"), ("percentage", "Percentage")], + index=True, + default="fixed", + required=True, + ) + fixed_amount = fields.Float(digits="Product Price") + percent_amount = fields.Float("Percentage Amount") + company_id = fields.Many2one( + "res.company", + "Company", + default=lambda self: self.env.company, + readonly=True, + ) + currency_id = fields.Many2one( + "res.currency", + related="company_id.currency_id", + readonly=True, + ) + name = fields.Char( + compute="_compute_commission_item_name_value", + help="Explicit rule name for this commission line.", + ) + commission_value = fields.Char( + "Value", + compute="_compute_commission_item_name_value", + ) + + @api.depends( + "applied_on", + "categ_id", + "product_tmpl_id", + "product_id", + "commission_type", + "fixed_amount", + "percent_amount", + ) + def _compute_commission_item_name_value(self): + for item in self: + if item.categ_id and item.applied_on == "2_product_category": + item.name = _("Category: %s") % (item.categ_id.display_name) + elif item.product_tmpl_id and item.applied_on == "1_product": + item.name = _("Product: %s") % (item.product_tmpl_id.display_name) + elif item.product_id and item.applied_on == "0_product_variant": + item.name = _("Variant: %s") % ( + item.product_id.with_context( + display_default_code=False + ).display_name + ) + else: + item.name = _("All Products") + + if item.commission_type == "fixed": + decimal_places = self.env["decimal.precision"].precision_get( + "Product Price" + ) + if item.currency_id.position == "after": + item.commission_value = "%s %s" % ( + float_repr( + item.fixed_amount, + decimal_places, + ), + item.currency_id.symbol or "", + ) + else: + item.commission_value = "%s %s" % ( + item.currency_id.symbol or "", + float_repr( + item.fixed_amount, + decimal_places, + ), + ) + elif item.commission_type == "percentage": + item.commission_value = str(item.percent_amount) + " %" + + @api.constrains("product_id", "product_tmpl_id", "categ_id") + def _check_product_consistency(self): + for item in self: + if item.applied_on == "2_product_category" and not item.categ_id: + raise ValidationError( + _( + "Please specify the category for which this rule should " + "be applied" + ) + ) + elif item.applied_on == "1_product" and not item.product_tmpl_id: + raise ValidationError( + _( + "Please specify the product for which this rule should " + "be applied" + ) + ) + elif item.applied_on == "0_product_variant" and not item.product_id: + raise ValidationError( + _( + "Please specify the product variant for " + "which this rule should be applied" + ) + ) + + @api.onchange("product_id") + def _onchange_product_id(self): + has_product_id = self.filtered("product_id") + for item in has_product_id: + item.product_tmpl_id = item.product_id.product_tmpl_id + if self.env.context.get("default_applied_on", False) == "1_product": + # If a product variant is specified, apply on variants instead + # Reset if product variant is removed + has_product_id.update({"applied_on": "0_product_variant"}) + (self - has_product_id).update({"applied_on": "1_product"}) + + @api.onchange("product_tmpl_id") + def _onchange_product_tmpl_id(self): + has_tmpl_id = self.filtered("product_tmpl_id") + for item in has_tmpl_id: + if ( + item.product_id + and item.product_id.product_tmpl_id != item.product_tmpl_id + ): + item.product_id = None + + @api.model_create_multi + def create(self, values_list): + new_values_list = [] + for values in values_list: + values = self.validate_values(values) + new_values_list.append(values) + return super(CommissionItem, self).create(new_values_list) + + def write(self, values): + values = self.validate_values(values) + res = super(CommissionItem, self).write(values) + return res + + def validate_values(self, values): + if values.get("applied_on", False): + # Ensure item consistency for later searches. + applied_on = values["applied_on"] + if applied_on == "3_global": + values.update( + dict(product_id=None, product_tmpl_id=None, categ_id=None) + ) + elif applied_on == "2_product_category": + values.update(dict(product_id=None, product_tmpl_id=None)) + elif applied_on == "1_product": + values.update(dict(product_id=None, categ_id=None)) + elif applied_on == "0_product_variant": + values.update(dict(categ_id=None)) + return values diff --git a/sale_commission_product_criteria/models/sale.py b/sale_commission_product_criteria/models/sale.py new file mode 100644 index 000000000..b7018e573 --- /dev/null +++ b/sale_commission_product_criteria/models/sale.py @@ -0,0 +1,55 @@ +# © 2023 ooops404 +# Copyright 2023 Simone Rubino - Aion Tech +# License AGPL-3 - See https://www.gnu.org/licenses/agpl-3.0.html +from odoo import api, fields, models + + +class SaleOrderLineAgent(models.Model): + _inherit = "sale.order.line.agent" + + discount = fields.Float(related="object_id.discount") + applied_commission_item_id = fields.Many2one("commission.item") + based_on = fields.Selection(related="applied_commission_item_id.based_on") + applied_on_name = fields.Char(related="applied_commission_item_id.name") + commission_type = fields.Selection( + related="applied_commission_item_id.commission_type" + ) + fixed_amount = fields.Float(related="applied_commission_item_id.fixed_amount") + percent_amount = fields.Float(related="applied_commission_item_id.percent_amount") + + @api.depends( + "object_id.price_subtotal", "object_id.product_id", "object_id.product_uom_qty" + ) + def _compute_amount(self): + res = None + for line in self: + if line.commission_id and line.commission_id.commission_type == "product": + 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: + res = super(SaleOrderLineAgent, line)._compute_amount() + return res + + +class SaleOrderLine(models.Model): + _inherit = "sale.order.line" + + def _prepare_invoice_line(self, **optional_values): + vals = super()._prepare_invoice_line(**optional_values) + vals["agent_ids"] = [ + ( + 0, + 0, + { + "agent_id": x.agent_id.id, + "commission_id": x.commission_id.id, + }, + ) + for x in self.agent_ids + ] + return vals diff --git a/sale_commission_product_criteria/models/sale_commission_line_mixin.py b/sale_commission_product_criteria/models/sale_commission_line_mixin.py new file mode 100644 index 000000000..29bf476db --- /dev/null +++ b/sale_commission_product_criteria/models/sale_commission_line_mixin.py @@ -0,0 +1,78 @@ +# © 2023 ooops404 +# Copyright 2023 Simone Rubino - Aion Tech +# License AGPL-3 - See https://www.gnu.org/licenses/agpl-3.0.html +from odoo import fields, models + + +class SaleCommissionLineMixin(models.AbstractModel): + _inherit = "commission.line.mixin" + + applied_commission_id = fields.Many2one("commission", readonly=True) + commission_id = fields.Many2one( + comodel_name="commission", + ondelete="restrict", + required=False, + compute="_compute_commission_id", + store=True, + readonly=False, + copy=True, + ) + + 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) + # 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 + 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) + ORDER BY + item.applied_on, item.based_on, categ.complete_name desc + """, + ( + product.product_tmpl_id.ids, + product.ids, + categ_ids, + commission._origin.id, # Added this + ), + ) + item_ids = [x[0] for x in self.env.cr.fetchall()] + return item_ids + + def _get_single_commission_amount(self, commission, subtotal, product, quantity): + self.ensure_one() + item_ids = self._get_commission_items(commission, product) + if not item_ids: + return 0.0 + commission_item = self.env["commission.item"].browse(item_ids[0]) + if commission.amount_base_type == "net_amount": + # If subtotal (sale_price * quantity) is less than + # standard_price * quantity, it means that we are selling at + # lower price than we bought, so set amount_base to 0 + subtotal = max([0, subtotal - product.standard_price * quantity]) + self.applied_commission_item_id = commission_item + # if self.agent_id.use_multi_type_commissions: + self.applied_commission_id = commission_item.commission_id + if commission_item.commission_type == "fixed": + return commission_item.fixed_amount + elif commission_item.commission_type == "percentage": + return subtotal * (commission_item.percent_amount / 100.0) + + def _get_discount_value(self, commission_item): + # Will be overridden + return self.object_id.discount diff --git a/sale_commission_product_criteria/models/settlement.py b/sale_commission_product_criteria/models/settlement.py new file mode 100644 index 000000000..a7a9f032f --- /dev/null +++ b/sale_commission_product_criteria/models/settlement.py @@ -0,0 +1,7 @@ +# © 2023 ooops404 +# License AGPL-3 - See https://www.gnu.org/licenses/agpl-3.0.html +from odoo import models + + +class SettlementLine(models.Model): + _inherit = "commission.settlement.line" diff --git a/sale_commission_product_criteria/readme/CONTRIBUTORS.rst b/sale_commission_product_criteria/readme/CONTRIBUTORS.rst new file mode 100644 index 000000000..898ef9dd2 --- /dev/null +++ b/sale_commission_product_criteria/readme/CONTRIBUTORS.rst @@ -0,0 +1,6 @@ +* `Ooops404 `__: + + * Ilyas +* `Aion Tech `_: + + * Simone Rubino diff --git a/sale_commission_product_criteria/readme/DESCRIPTION.rst b/sale_commission_product_criteria/readme/DESCRIPTION.rst new file mode 100644 index 000000000..c4a10d9e4 --- /dev/null +++ b/sale_commission_product_criteria/readme/DESCRIPTION.rst @@ -0,0 +1,16 @@ +This module allows to set in the same Commission Type different commission rates according to the product on SO/invoice line. + +This is made possible since this module adds a new "Product criteria" type to Commission Type and applies commission rates with the same logic of sale pricelist items. + +For example, such a Commission Type can grant: + +10% on a specific Product A, +10$ on Product B, +4% on products in Category 1 and +5$ on all other products. + +In SO/invoice, system will apply different commissions based on variant/product/category or global, applied hierarchically. This means that for the example above, if product A is assigned to Category 1, commission assigned is 10%, as per variant/product/category/global rule application order. + +Furthermore, these commission type items can be accessed and created by a specific menu, to facilitate their management in environments with lots of records. + +The form for commission type item can be extended by future modules with further conditions to decide when to apply a specific item. diff --git a/sale_commission_product_criteria/readme/USAGE.rst b/sale_commission_product_criteria/readme/USAGE.rst new file mode 100644 index 000000000..bdd3fd9bd --- /dev/null +++ b/sale_commission_product_criteria/readme/USAGE.rst @@ -0,0 +1,7 @@ +To use features of this module, you need to: + +#. Go to Commissions > Configuration > Commission Types. +#. Create a Commission Type with type = "Product criteria". +#. Create multiple rules based on variant/product/category or global +#. These rules will be sorted according to the same logic of sale pricelist. +#. Rest flow is according to OCA sale_commission module. diff --git a/sale_commission_product_criteria/security/ir.model.access.csv b/sale_commission_product_criteria/security/ir.model.access.csv new file mode 100644 index 000000000..0958b9904 --- /dev/null +++ b/sale_commission_product_criteria/security/ir.model.access.csv @@ -0,0 +1,3 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_commission_item_manager,access_commission_item_manager,model_commission_item,sales_team.group_sale_manager,1,1,1,1 +access_commission_item_salesman,access_commission_item_salesman,model_commission_item,sales_team.group_sale_salesman,1,0,0,0 diff --git a/sale_commission_product_criteria/static/description/icon.png b/sale_commission_product_criteria/static/description/icon.png new file mode 100644 index 000000000..3a0328b51 Binary files /dev/null and b/sale_commission_product_criteria/static/description/icon.png differ diff --git a/sale_commission_product_criteria/static/description/index.html b/sale_commission_product_criteria/static/description/index.html new file mode 100644 index 000000000..9e075efce --- /dev/null +++ b/sale_commission_product_criteria/static/description/index.html @@ -0,0 +1,451 @@ + + + + + +Sale Commission Product Criteria + + + +
+

Sale Commission Product Criteria

+ + +

Beta License: AGPL-3 OCA/commission Translate me on Weblate Try me on Runboat

+

This module allows to set in the same Commission Type different commission rates according to the product on SO/invoice line.

+

This is made possible since this module adds a new “Product criteria” type to Commission Type and applies commission rates with the same logic of sale pricelist items.

+

For example, such a Commission Type can grant:

+

10% on a specific Product A, +10$ on Product B, +4% on products in Category 1 and +5$ on all other products.

+

In SO/invoice, system will apply different commissions based on variant/product/category or global, applied hierarchically. This means that for the example above, if product A is assigned to Category 1, commission assigned is 10%, as per variant/product/category/global rule application order.

+

Furthermore, these commission type items can be accessed and created by a specific menu, to facilitate their management in environments with lots of records.

+

The form for commission type item can be extended by future modules with further conditions to decide when to apply a specific item.

+

Table of contents

+ +
+

Usage

+

To use features of this module, you need to:

+
    +
  1. Go to Commissions > Configuration > Commission Types.
  2. +
  3. Create a Commission Type with type = “Product criteria”.
  4. +
  5. Create multiple rules based on variant/product/category or global
  6. +
  7. These rules will be sorted according to the same logic of sale pricelist.
  8. +
  9. Rest flow is according to OCA sale_commission module.
  10. +
+
+
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • Ilyas
  • +
  • Ooops404
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

This module is maintained by the OCA.

+Odoo Community Association +

OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use.

+

Current maintainer:

+

ilyasProgrammer

+

This module is part of the OCA/commission project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+ + diff --git a/sale_commission_product_criteria/tests/__init__.py b/sale_commission_product_criteria/tests/__init__.py new file mode 100644 index 000000000..9a20f675a --- /dev/null +++ b/sale_commission_product_criteria/tests/__init__.py @@ -0,0 +1 @@ +from . import test_sale_commission_product_criteria diff --git a/sale_commission_product_criteria/tests/test_sale_commission_product_criteria.py b/sale_commission_product_criteria/tests/test_sale_commission_product_criteria.py new file mode 100644 index 000000000..cf9bda14f --- /dev/null +++ b/sale_commission_product_criteria/tests/test_sale_commission_product_criteria.py @@ -0,0 +1,204 @@ +# © 2023 ooops404 +# Copyright 2023 Simone Rubino - Aion Tech +# License AGPL-3 - See https://www.gnu.org/licenses/agpl-3.0.html + + +from odoo.exceptions import ValidationError +from odoo.tests.common import TransactionCase + + +class TestSaleCommission(TransactionCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.commission_model = cls.env["commission"] + cls.company = cls.env.ref("base.main_company") + cls.res_partner_model = cls.env["res.partner"] + cls.partner = cls.env.ref("base.res_partner_12") + cls.partner2 = cls.env.ref("base.res_partner_10") + cls.sale_order_model = cls.env["sale.order"] + cls.advance_inv_model = cls.env["sale.advance.payment.inv"] + cls.settle_model = cls.env["commission.settlement"] + cls.make_settle_model = cls.env["commission.make.settle"] + cls.make_inv_model = cls.env["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") + cls.product_1.write({"invoice_policy": "order"}) + cls.product_4.write({"invoice_policy": "order"}) + cls.product_5.write({"invoice_policy": "order"}) + cls.product_6.write({"commission_free": True}) + cls.product_template_4 = cls.env.ref( + "product.product_product_4_product_template" + ) + cls.product_template_4.write({"invoice_policy": "order"}) + 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" + ) + + def test_sale_commission_product_criteria_items(self): + # items names + self.com_item_1._compute_commission_item_name_value() + self.com_item_1.currency_id.position = "after" + self.com_item_1._compute_commission_item_name_value() + self.assertEqual(self.com_item_1.name, "All Products") + self.com_item_1.write({"applied_on": "3_global"}) + self.com_item_2._compute_commission_item_name_value() + self.assertEqual( + self.com_item_2.name, "Category: All / Saleable / Office Furniture" + ) + self.com_item_2.write({"applied_on": "2_product_category"}) + self.com_item_3._compute_commission_item_name_value() + self.assertEqual(self.com_item_3.name, "Product: Customizable Desk") + self.com_item_3.write({"applied_on": "1_product"}) + self.com_item_4._compute_commission_item_name_value() + self.assertEqual( + self.com_item_4.name, "Variant: Customizable Desk (Steel, White)" + ) + self.com_item_4.write({"applied_on": "0_product_variant"}) + + # 3_global + so_1 = self._create_sale_order(self.product_1, self.partner) + so_1.recompute_lines_agents() + self.assertEqual(so_1.partner_agent_ids.name, "Agent Rules") + self.assertEqual(so_1.order_line.agent_ids.amount, 10) + so_1.action_confirm() + invoice = self._invoice_sale_order(so_1) + invoice.recompute_lines_agents() + invoice.action_post() + + # 2_product_category + so = self._create_sale_order(self.product_5, self.partner) + so.recompute_lines_agents() + self.assertEqual(so.partner_agent_ids.name, "Agent Rules") + self.assertEqual(so.order_line.agent_ids.amount, 20) + so.action_confirm() + invoice = self._invoice_sale_order(so) + invoice.recompute_lines_agents() + + # 1_product 5 % + pp4 = self.product_template_4.product_variant_id + so = self._create_sale_order(pp4, self.partner) + so.recompute_lines_agents() + self.assertEqual(so.partner_agent_ids.name, "Agent Rules") + self.assertEqual(so.order_line.agent_ids.amount, 50) + so.action_confirm() + invoice = self._invoice_sale_order(so) + invoice.recompute_lines_agents() + + # 0_product_variant 15 % + so = self._create_sale_order(self.product_4, self.partner) + so.recompute_lines_agents() + self.assertEqual(so.partner_agent_ids.name, "Agent Rules") + self.assertEqual(so.order_line.agent_ids.amount, 150) + so.action_confirm() + invoice = self._invoice_sale_order(so) + invoice.recompute_lines_agents() + + # Commission free product + so = self._create_sale_order(self.product_6, self.partner) + so.recompute_lines_agents() + + # Type != product + so = self._create_sale_order(self.product_4, self.partner2) + so.recompute_lines_agents() + + # net amount + self.rules_commission_id.amount_base_type = "net_amount" + so = self._create_sale_order(self.product_4, self.partner) + so.order_line.agent_ids._compute_amount() + + # archive + self.rules_commission_id.action_archive() + self.rules_commission_id.action_unarchive() + + # copy + new_rule = self.rules_commission_id.copy() + self.assertEqual(len(new_rule.item_ids), len(self.rules_commission_id.item_ids)) + + # change commission_type + self.rules_commission_id.commission_type = "fixed" + with self.assertRaises(ValidationError): + self.rules_commission_id.check_type_change_allowed_moves() + with self.assertRaises(ValidationError): + self.rules_commission_id.check_type_change_allowed_sale() + + # no rule found + self.env.ref( + "sale_commission_product_criteria.demo_commission_rules_item_1" + ).unlink() + so = self._create_sale_order(self.product_1, self.partner) + so.order_line.agent_ids._compute_amount() + + # _check_product_consistency + with self.assertRaises(ValidationError): + self.com_item_2.categ_id = False + with self.assertRaises(ValidationError): + self.com_item_3.product_tmpl_id = False + with self.assertRaises(ValidationError): + self.com_item_4.product_id = False + + # _onchange_product_id + self.com_item_4.product_id = self.product_1 + self.com_item_4._onchange_product_id() + self.com_item_4.with_context( + default_applied_on="1_product" + )._onchange_product_id() + self.com_item_4.product_tmpl_id = self.product_template_4 + self.com_item_4._onchange_product_id() + self.com_item_4.product_tmpl_id = self.product_template_4 + with self.assertRaises(ValidationError): + self.com_item_4._onchange_product_tmpl_id() + + 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.with_context( + **{ + "active_model": "sale.order", + "active_ids": [sale_order.id], + "active_id": sale_order.id, + } + ).create( + { + "advance_payment_method": "delivered", + } + ) + wizard.create_invoices() + invoice = sale_order.invoice_ids - old_invoices + return invoice diff --git a/sale_commission_product_criteria/views/views.xml b/sale_commission_product_criteria/views/views.xml new file mode 100644 index 000000000..dc555fd83 --- /dev/null +++ b/sale_commission_product_criteria/views/views.xml @@ -0,0 +1,239 @@ + + + + + + sale.agent.order.inherit.form.sale_commission_product_criteria.mod + sale.order + + + + + + + + + commission.form.view.inherit + commission + + + + {'invisible': [('commission_type', '=', 'product')]} + + + + + + + + + + + + + + + + + + sale.order.line.agent.mod + sale.order.line.agent + + + + + 1 + + + + + + + + + + + + + + + + + + + commission.item.form + commission.item + +
+ +

+ +

+ + + + + + + + + + + + + + + + + + + +
+
+
+
+ + + commission.item.tree + commission.item + + + + + + + + + + + + + commission.item.search.view + commission.item + + + + + + + + + + + + + + + Commission Items + ir.actions.act_window + commission.item + tree,form + + {"hide_commission_id":1, 'search_default_group_by_commission': 1,} + + + + + Commission Items + ir.actions.act_window + commission.item + tree,form + + {"hide_commission_id":0, 'search_default_group_by_commission': 1,} + + + + + +
diff --git a/setup/_metapackage/VERSION.txt b/setup/_metapackage/VERSION.txt index a5ff0d34c..420c6b873 100644 --- a/setup/_metapackage/VERSION.txt +++ b/setup/_metapackage/VERSION.txt @@ -1 +1 @@ -16.0.20231018.0 \ No newline at end of file +16.0.20240219.0 \ No newline at end of file diff --git a/setup/_metapackage/setup.py b/setup/_metapackage/setup.py index dc65fcb0d..b4cb1b73b 100644 --- a/setup/_metapackage/setup.py +++ b/setup/_metapackage/setup.py @@ -13,6 +13,7 @@ 'odoo-addon-commission_formula>=16.0dev,<16.1dev', 'odoo-addon-hr_commission>=16.0dev,<16.1dev', 'odoo-addon-sale_commission>=16.0dev,<16.1dev', + 'odoo-addon-sale_commission_product_criteria>=16.0dev,<16.1dev', 'odoo-addon-sale_commission_salesman>=16.0dev,<16.1dev', ], classifiers=[ diff --git a/setup/sale_commission_product_criteria/odoo/addons/sale_commission_product_criteria b/setup/sale_commission_product_criteria/odoo/addons/sale_commission_product_criteria new file mode 120000 index 000000000..29328d603 --- /dev/null +++ b/setup/sale_commission_product_criteria/odoo/addons/sale_commission_product_criteria @@ -0,0 +1 @@ +../../../../sale_commission_product_criteria \ No newline at end of file diff --git a/setup/sale_commission_product_criteria/setup.py b/setup/sale_commission_product_criteria/setup.py new file mode 100644 index 000000000..28c57bb64 --- /dev/null +++ b/setup/sale_commission_product_criteria/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +)