From 5af6c895d01356d786e73e7234d3ce339a5256b4 Mon Sep 17 00:00:00 2001 From: Alexis de Lattre Date: Sat, 30 Nov 2024 01:19:31 +0100 Subject: [PATCH] Add modules commission_simple, commission_simple_agent, commission_simple_agent_purchase Add demo data in sale_agent --- commission_simple/__init__.py | 2 + commission_simple/__manifest__.py | 48 ++ commission_simple/data/decimal_precision.xml | 11 + commission_simple/i18n/fr.po | 640 ++++++++++++++++++ commission_simple/models/__init__.py | 5 + commission_simple/models/account_move_line.py | 90 +++ .../models/commission_profile.py | 125 ++++ commission_simple/models/commission_result.py | 77 +++ commission_simple/models/commission_rule.py | 52 ++ commission_simple/models/res_company.py | 13 + commission_simple/models/res_users.py | 20 + .../security/ir.model.access.csv | 11 + commission_simple/security/rule.xml | 29 + commission_simple/views/commission.xml | 206 ++++++ .../views/commission_profile.xml | 73 ++ commission_simple/views/commission_result.xml | 113 ++++ commission_simple/views/commission_rule.xml | 80 +++ .../views/res_config_settings.xml | 37 + commission_simple/views/res_users.xml | 36 + commission_simple/wizards/__init__.py | 2 + .../wizards/commission_compute.py | 77 +++ .../wizards/commission_compute_view.xml | 41 ++ .../wizards/res_config_settings.py | 12 + commission_simple_agent/__init__.py | 1 + commission_simple_agent/__manifest__.py | 22 + commission_simple_agent/i18n/fr.po | 44 ++ commission_simple_agent/models/__init__.py | 1 + .../models/commission_profile_assignment.py | 51 ++ .../views/commission_profile.xml | 21 + .../views/commission_result.xml | 32 + commission_simple_agent_purchase/__init__.py | 2 + .../__manifest__.py | 22 + commission_simple_agent_purchase/i18n/fr.po | 79 +++ .../models/__init__.py | 2 + .../models/commission_result.py | 78 +++ .../models/res_company.py | 14 + .../views/commission_result.xml | 21 + .../wizards/__init__.py | 1 + .../wizards/res_config_settings.py | 12 + .../wizards/res_config_settings.xml | 26 + sale_agent/__manifest__.py | 1 + sale_agent/demo/demo.xml | 32 + 42 files changed, 2262 insertions(+) create mode 100644 commission_simple/__init__.py create mode 100644 commission_simple/__manifest__.py create mode 100644 commission_simple/data/decimal_precision.xml create mode 100644 commission_simple/i18n/fr.po create mode 100644 commission_simple/models/__init__.py create mode 100644 commission_simple/models/account_move_line.py create mode 100644 commission_simple/models/commission_profile.py create mode 100644 commission_simple/models/commission_result.py create mode 100644 commission_simple/models/commission_rule.py create mode 100644 commission_simple/models/res_company.py create mode 100644 commission_simple/models/res_users.py create mode 100644 commission_simple/security/ir.model.access.csv create mode 100644 commission_simple/security/rule.xml create mode 100644 commission_simple/views/commission.xml create mode 100644 commission_simple/views/commission_profile.xml create mode 100644 commission_simple/views/commission_result.xml create mode 100644 commission_simple/views/commission_rule.xml create mode 100644 commission_simple/views/res_config_settings.xml create mode 100644 commission_simple/views/res_users.xml create mode 100644 commission_simple/wizards/__init__.py create mode 100644 commission_simple/wizards/commission_compute.py create mode 100644 commission_simple/wizards/commission_compute_view.xml create mode 100644 commission_simple/wizards/res_config_settings.py create mode 100644 commission_simple_agent/__init__.py create mode 100644 commission_simple_agent/__manifest__.py create mode 100644 commission_simple_agent/i18n/fr.po create mode 100644 commission_simple_agent/models/__init__.py create mode 100644 commission_simple_agent/models/commission_profile_assignment.py create mode 100644 commission_simple_agent/views/commission_profile.xml create mode 100644 commission_simple_agent/views/commission_result.xml create mode 100644 commission_simple_agent_purchase/__init__.py create mode 100644 commission_simple_agent_purchase/__manifest__.py create mode 100644 commission_simple_agent_purchase/i18n/fr.po create mode 100644 commission_simple_agent_purchase/models/__init__.py create mode 100644 commission_simple_agent_purchase/models/commission_result.py create mode 100644 commission_simple_agent_purchase/models/res_company.py create mode 100644 commission_simple_agent_purchase/views/commission_result.xml create mode 100644 commission_simple_agent_purchase/wizards/__init__.py create mode 100644 commission_simple_agent_purchase/wizards/res_config_settings.py create mode 100644 commission_simple_agent_purchase/wizards/res_config_settings.xml create mode 100644 sale_agent/demo/demo.xml diff --git a/commission_simple/__init__.py b/commission_simple/__init__.py new file mode 100644 index 0000000..aee8895 --- /dev/null +++ b/commission_simple/__init__.py @@ -0,0 +1,2 @@ +from . import models +from . import wizards diff --git a/commission_simple/__manifest__.py b/commission_simple/__manifest__.py new file mode 100644 index 0000000..4322ba0 --- /dev/null +++ b/commission_simple/__manifest__.py @@ -0,0 +1,48 @@ +# Copyright 2019-2024 Akretion France (http://www.akretion.com) +# @author Alexis de Lattre +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + 'name': 'Commission Simple', + 'version': '16.0.1.0.0', + 'category': 'Sales', + 'license': 'AGPL-3', + 'summary': 'Compute commissions for salesman', + 'description': """ +Commission Simple +================= + +This module is a **simple** module to compute commission for salesman. From my experience, companies often use very specific methods to compute commissions and it's impossible to develop a module that can support all of them. So the goal of this module is just to have a simple base to build the company-specific commissionning system by inheriting this simple module. + +Here is a short description of this module: + +* create commission profiles using rules (per product category, per product, per product and customer, etc.), +* the commission rules can have a start and end date (optional), +* commissionning can happen on invoicing or on payment, +* each invoice line can only be commissionned to one salesman, +* commission reports are stored in Odoo. + +This module has been written by Alexis de Lattre from Akretion +. + """, + 'author': 'Akretion', + 'website': 'https://github.com/akretion/odoo-usability', + 'depends': [ + 'account', + 'date_range', + # this uses some related fields on account.move.line +# 'account_usability_akretion', + ], + 'data': [ + 'security/ir.model.access.csv', + 'security/rule.xml', + 'data/decimal_precision.xml', + 'views/commission_profile.xml', + 'views/commission_rule.xml', + 'views/commission_result.xml', +# 'views/res_users.xml', + 'views/res_config_settings.xml', + 'wizards/commission_compute_view.xml', + ], + 'installable': True, +} diff --git a/commission_simple/data/decimal_precision.xml b/commission_simple/data/decimal_precision.xml new file mode 100644 index 0000000..5878b9d --- /dev/null +++ b/commission_simple/data/decimal_precision.xml @@ -0,0 +1,11 @@ + + + + + + Commission Rate + 2 + + + + diff --git a/commission_simple/i18n/fr.po b/commission_simple/i18n/fr.po new file mode 100644 index 0000000..007a490 --- /dev/null +++ b/commission_simple/i18n/fr.po @@ -0,0 +1,640 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * commission_simple +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-11-29 23:38+0000\n" +"PO-Revision-Date: 2024-11-29 23:38+0000\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: commission_simple +#: model:ir.model.constraint,message:commission_simple.constraint_commission_result_salesman_period_company_unique +msgid "" +"A commission result already exists for this salesman/agent for the same " +"period." +msgstr "" +"Un état des commissions existe déjà pour ce vendeur/agent pour la même " +"période." + +#. module: commission_simple +#. odoo-python +#: code:addons/commission_simple/models/commission_profile.py:0 +#, python-format +msgid "A salesman must be selected when the assignment type is 'Salesman'." +msgstr "" +"Un vendeur doit être sélectionné lorsque le type d'affectation est " +"\"Vendeur\"." + +#. module: commission_simple +#: model:ir.model.fields,field_description:commission_simple.field_commission_result__message_needaction +msgid "Action Needed" +msgstr "Action requise" + +#. module: commission_simple +#: model:ir.model.fields,field_description:commission_simple.field_commission_profile__active +#: model:ir.model.fields,field_description:commission_simple.field_commission_rule__active +msgid "Active" +msgstr "Actif" + +#. module: commission_simple +#: model:ir.model.fields,field_description:commission_simple.field_commission_result__activity_ids +msgid "Activities" +msgstr "Activités" + +#. module: commission_simple +#: model:ir.model.fields,field_description:commission_simple.field_commission_result__activity_exception_decoration +msgid "Activity Exception Decoration" +msgstr "Style d'affichage de l'activité-alerte" + +#. module: commission_simple +#: model:ir.model.fields,field_description:commission_simple.field_commission_result__activity_state +msgid "Activity State" +msgstr "État de l'activité" + +#. module: commission_simple +#: model:ir.model.fields,field_description:commission_simple.field_commission_result__activity_type_icon +msgid "Activity Type Icon" +msgstr "Îcone du type d'activité" + +#. module: commission_simple +#: model:ir.model.fields,field_description:commission_simple.field_commission_rule__applied_on +msgid "Apply On" +msgstr "Conditions" + +#. module: commission_simple +#: model_terms:ir.ui.view,arch_db:commission_simple.commission_profile_form +#: model_terms:ir.ui.view,arch_db:commission_simple.commission_rule_search +msgid "Archived" +msgstr "Archivé" + +#. module: commission_simple +#: model_terms:ir.ui.view,arch_db:commission_simple.commission_result_form +msgid "Are you sure you want to go back to draft?" +msgstr "Êtes-vous sûr de vouloir revenir à l'état brouillon ?" + +#. module: commission_simple +#: model:ir.model.fields,field_description:commission_simple.field_commission_result__assign_type +#: model_terms:ir.ui.view,arch_db:commission_simple.commission_result_search +msgid "Assign Type" +msgstr "Type d'affectation" + +#. module: commission_simple +#: model:ir.model.fields,field_description:commission_simple.field_commission_profile__assign_ids +#: model_terms:ir.ui.view,arch_db:commission_simple.commission_profile_form +msgid "Assignments" +msgstr "Assignations" + +#. module: commission_simple +#: model:ir.model.fields,field_description:commission_simple.field_commission_result__message_attachment_count +msgid "Attachment Count" +msgstr "Nombre de pièces jointes" + +#. module: commission_simple +#: model_terms:ir.ui.view,arch_db:commission_simple.commission_result_form +msgid "Back to Draft" +msgstr "Remettre en brouillon" + +#. module: commission_simple +#: model_terms:ir.ui.view,arch_db:commission_simple.commission_compute_form +msgid "Cancel" +msgstr "Annuler" + +#. module: commission_simple +#: model:ir.model.fields,field_description:commission_simple.field_account_move_line__commission_amount +msgid "Commission Amount" +msgstr "Montant de la commission" + +#. module: commission_simple +#: model:ir.model.fields,field_description:commission_simple.field_account_move_line__commission_base +#: model:ir.model.fields,field_description:commission_simple.field_commission_rule__base +msgid "Commission Base" +msgstr "Base de la commission" + +#. module: commission_simple +#: model:ir.model.fields,field_description:commission_simple.field_commission_result__line_ids +#: model_terms:ir.ui.view,arch_db:commission_simple.commission_result_form +msgid "Commission Lines" +msgstr "Lignes commission" + +#. module: commission_simple +#: model:ir.model.fields,field_description:commission_simple.field_commission_compute__date_range_type_id +#: model:ir.model.fields,field_description:commission_simple.field_res_company__commission_date_range_type_id +#: model:ir.model.fields,field_description:commission_simple.field_res_config_settings__commission_date_range_type_id +msgid "Commission Periodicity" +msgstr "Périodicité de commission" + +#. module: commission_simple +#: model:ir.model,name:commission_simple.model_commission_profile +#: model:ir.model.fields,field_description:commission_simple.field_commission_result__profile_id +msgid "Commission Profile" +msgstr "Profil de commission" + +#. module: commission_simple +#: model:ir.model,name:commission_simple.model_commission_profile_assignment +msgid "Commission Profile Assignment" +msgstr "Affectation du profil de commission" + +#. module: commission_simple +#: model:ir.actions.act_window,name:commission_simple.commission_profile_action +#: model:ir.ui.menu,name:commission_simple.commission_profile_menu +msgid "Commission Profiles" +msgstr "Profils de commission" + +#. module: commission_simple +#: model:ir.model.fields,field_description:commission_simple.field_account_move_line__commission_rate +#: model:ir.model.fields,field_description:commission_simple.field_commission_rule__rate +msgid "Commission Rate" +msgstr "Taux de commission" + +#. module: commission_simple +#: model:ir.model,name:commission_simple.model_commission_result +#: model:ir.model.fields,field_description:commission_simple.field_account_move_line__commission_result_id +msgid "Commission Result" +msgstr "État des commissions" + +#. module: commission_simple +#: model:ir.model,name:commission_simple.model_commission_rule +msgid "Commission Rule" +msgstr "Règle de commission" + +#. module: commission_simple +#: model:ir.actions.act_window,name:commission_simple.commission_rule_action +#: model:ir.model.fields,field_description:commission_simple.field_commission_profile__rule_ids +#: model:ir.ui.menu,name:commission_simple.commission_rule_menu +msgid "Commission Rules" +msgstr "Règles de commission" + +#. module: commission_simple +#: model:ir.model.fields,field_description:commission_simple.field_commission_result__amount_total +msgid "Commission Total" +msgstr "Total des commissions" + +#. module: commission_simple +#: model:ir.actions.act_window,name:commission_simple.commission_result_action +#: model:ir.ui.menu,name:commission_simple.commission_config_root +#: model:ir.ui.menu,name:commission_simple.commission_result_menu +#: model:ir.ui.menu,name:commission_simple.commission_root +#: model_terms:ir.ui.view,arch_db:commission_simple.res_config_settings_view_form +msgid "Commissions" +msgstr "" + +#. module: commission_simple +#. odoo-python +#: code:addons/commission_simple/wizards/commission_compute.py:0 +#, python-format +msgid "Commissions already exist for %(period)s in company %(company)s." +msgstr "" +"Des commissions existent déjà pour %(period)s dans la société %(company)s." + +#. module: commission_simple +#: model:ir.model,name:commission_simple.model_res_company +msgid "Companies" +msgstr "Sociétés" + +#. module: commission_simple +#: model:ir.model.fields,field_description:commission_simple.field_commission_compute__company_id +#: model:ir.model.fields,field_description:commission_simple.field_commission_profile__company_id +#: model:ir.model.fields,field_description:commission_simple.field_commission_profile_assignment__company_id +#: model:ir.model.fields,field_description:commission_simple.field_commission_result__company_id +#: model:ir.model.fields,field_description:commission_simple.field_commission_rule__company_id +msgid "Company" +msgstr "Société" + +#. module: commission_simple +#: model:ir.model.fields,field_description:commission_simple.field_commission_result__company_currency_id +msgid "Company Currency" +msgstr "Devise de la société" + +#. module: commission_simple +#: model_terms:ir.ui.view,arch_db:commission_simple.commission_compute_form +#: model_terms:ir.ui.view,arch_db:commission_simple.commission_rule_form +msgid "Compute" +msgstr "Calculer" + +#. module: commission_simple +#: model:ir.actions.act_window,name:commission_simple.commission_compute_action +#: model:ir.model,name:commission_simple.model_commission_compute +#: model:ir.ui.menu,name:commission_simple.commission_compute_menu +msgid "Compute Commissions" +msgstr "Calculer les commissions" + +#. module: commission_simple +#: model:ir.model,name:commission_simple.model_res_config_settings +msgid "Config Settings" +msgstr "Configuration" + +#. module: commission_simple +#: model_terms:ir.ui.view,arch_db:commission_simple.commission_result_form +msgid "Confirm" +msgstr "Confirmer" + +#. module: commission_simple +#: model:ir.model.fields,field_description:commission_simple.field_commission_compute__create_uid +#: model:ir.model.fields,field_description:commission_simple.field_commission_profile__create_uid +#: model:ir.model.fields,field_description:commission_simple.field_commission_profile_assignment__create_uid +#: model:ir.model.fields,field_description:commission_simple.field_commission_result__create_uid +#: model:ir.model.fields,field_description:commission_simple.field_commission_rule__create_uid +msgid "Created by" +msgstr "Créé par" + +#. module: commission_simple +#: model:ir.model.fields,field_description:commission_simple.field_commission_compute__create_date +#: model:ir.model.fields,field_description:commission_simple.field_commission_profile__create_date +#: model:ir.model.fields,field_description:commission_simple.field_commission_profile_assignment__create_date +#: model:ir.model.fields,field_description:commission_simple.field_commission_result__create_date +#: model:ir.model.fields,field_description:commission_simple.field_commission_rule__create_date +msgid "Created on" +msgstr "Créé le" + +#. module: commission_simple +#: model:ir.model.fields,field_description:commission_simple.field_commission_rule__partner_ids +msgid "Customers" +msgstr "Clients" + +#. module: commission_simple +#: model:ir.model.fields,field_description:commission_simple.field_commission_compute__display_name +#: model:ir.model.fields,field_description:commission_simple.field_commission_profile__display_name +#: model:ir.model.fields,field_description:commission_simple.field_commission_profile_assignment__display_name +#: model:ir.model.fields,field_description:commission_simple.field_commission_result__display_name +#: model:ir.model.fields,field_description:commission_simple.field_commission_rule__display_name +msgid "Display Name" +msgstr "Nom affiché" + +#. module: commission_simple +#: model:ir.model.fields.selection,name:commission_simple.selection__commission_result__state__done +#: model_terms:ir.ui.view,arch_db:commission_simple.commission_result_search +msgid "Done" +msgstr "Validé" + +#. module: commission_simple +#: model:ir.model.fields.selection,name:commission_simple.selection__commission_result__state__draft +#: model_terms:ir.ui.view,arch_db:commission_simple.commission_result_search +msgid "Draft" +msgstr "Brouillon" + +#. module: commission_simple +#: model:ir.model.fields,field_description:commission_simple.field_commission_rule__date_end +msgid "End Date" +msgstr "Date de fin" + +#. module: commission_simple +#: model:ir.model.fields,field_description:commission_simple.field_commission_compute__date_end +#: model:ir.model.fields,field_description:commission_simple.field_commission_result__date_end +msgid "End date" +msgstr "Date de fin" + +#. module: commission_simple +#: model:ir.model.fields,field_description:commission_simple.field_commission_result__message_follower_ids +msgid "Followers" +msgstr "Abonnés" + +#. module: commission_simple +#: model:ir.model.fields,field_description:commission_simple.field_commission_result__message_partner_ids +msgid "Followers (Partners)" +msgstr "Abonnés (partenaires)" + +#. module: commission_simple +#: model:ir.model.fields,help:commission_simple.field_commission_result__activity_type_icon +msgid "Font awesome icon e.g. fa-tasks" +msgstr "Îcone font-awesome, par exemple fa-task" + +#. module: commission_simple +#: model:ir.model.fields.selection,name:commission_simple.selection__commission_rule__applied_on__4_global +msgid "Global" +msgstr "" + +#. module: commission_simple +#: model:ir.model.fields,field_description:commission_simple.field_commission_result__has_message +msgid "Has Message" +msgstr "A un message" + +#. module: commission_simple +#: model:ir.model.fields,field_description:commission_simple.field_commission_compute__id +#: model:ir.model.fields,field_description:commission_simple.field_commission_profile__id +#: model:ir.model.fields,field_description:commission_simple.field_commission_profile_assignment__id +#: model:ir.model.fields,field_description:commission_simple.field_commission_result__id +#: model:ir.model.fields,field_description:commission_simple.field_commission_rule__id +msgid "ID" +msgstr "" + +#. module: commission_simple +#: model:ir.model.fields,field_description:commission_simple.field_commission_result__activity_exception_icon +msgid "Icon" +msgstr "Îcone" + +#. module: commission_simple +#: model:ir.model.fields,help:commission_simple.field_commission_result__activity_exception_icon +msgid "Icon to indicate an exception activity." +msgstr "Îcone pour indiquer une activité-alerte" + +#. module: commission_simple +#: model:ir.model.fields,help:commission_simple.field_commission_result__message_needaction +msgid "If checked, new messages require your attention." +msgstr "Si activé, de nouveaux messages nécessitent votre attention." + +#. module: commission_simple +#: model:ir.model.fields,help:commission_simple.field_commission_result__message_has_error +msgid "If checked, some messages have a delivery error." +msgstr "Si activé, des messages ont une erreur d'envoi." + +#. module: commission_simple +#: model:ir.model.fields.selection,name:commission_simple.selection__commission_profile__trigger_type__in_payment +msgid "In Payment and Paid" +msgstr "En paiement et payé" + +#. module: commission_simple +#: model:ir.model.fields.selection,name:commission_simple.selection__commission_profile__trigger_type__invoice +msgid "Invoiced" +msgstr "Facturé" + +#. module: commission_simple +#: model:ir.model.fields.selection,name:commission_simple.selection__commission_rule__base__invoiced +#: model_terms:ir.ui.view,arch_db:commission_simple.commission_result_form +msgid "Invoiced Amount" +msgstr "Montant facturé" + +#. module: commission_simple +#: model:ir.model.fields,field_description:commission_simple.field_commission_result__message_is_follower +msgid "Is Follower" +msgstr "Est abonné" + +#. module: commission_simple +#: model:ir.model,name:commission_simple.model_account_move_line +msgid "Journal Item" +msgstr "Écriture comptable" + +#. module: commission_simple +#: model:ir.model.fields,field_description:commission_simple.field_commission_compute____last_update +#: model:ir.model.fields,field_description:commission_simple.field_commission_profile____last_update +#: model:ir.model.fields,field_description:commission_simple.field_commission_profile_assignment____last_update +#: model:ir.model.fields,field_description:commission_simple.field_commission_result____last_update +#: model:ir.model.fields,field_description:commission_simple.field_commission_rule____last_update +msgid "Last Modified on" +msgstr "Dernière modification le" + +#. module: commission_simple +#: model:ir.model.fields,field_description:commission_simple.field_commission_compute__write_uid +#: model:ir.model.fields,field_description:commission_simple.field_commission_profile__write_uid +#: model:ir.model.fields,field_description:commission_simple.field_commission_profile_assignment__write_uid +#: model:ir.model.fields,field_description:commission_simple.field_commission_result__write_uid +#: model:ir.model.fields,field_description:commission_simple.field_commission_rule__write_uid +msgid "Last Updated by" +msgstr "Dernière mise à jour par" + +#. module: commission_simple +#: model:ir.model.fields,field_description:commission_simple.field_commission_compute__write_date +#: model:ir.model.fields,field_description:commission_simple.field_commission_profile__write_date +#: model:ir.model.fields,field_description:commission_simple.field_commission_profile_assignment__write_date +#: model:ir.model.fields,field_description:commission_simple.field_commission_result__write_date +#: model:ir.model.fields,field_description:commission_simple.field_commission_rule__write_date +msgid "Last Updated on" +msgstr "Dernière mise à jour le" + +#. module: commission_simple +#: model:ir.model.fields,field_description:commission_simple.field_commission_result__message_main_attachment_id +msgid "Main Attachment" +msgstr "Pièce jointe principale" + +#. module: commission_simple +#: model:ir.model.fields.selection,name:commission_simple.selection__commission_rule__base__margin +msgid "Margin" +msgstr "Marge" + +#. module: commission_simple +#: model_terms:ir.ui.view,arch_db:commission_simple.commission_rule_form +msgid "Match" +msgstr "" + +#. module: commission_simple +#: model:ir.model.fields,field_description:commission_simple.field_account_move_line__commission_rule_id +msgid "Matched Commission Rule" +msgstr "Règle de commission associée" + +#. module: commission_simple +#: model:ir.model.fields,field_description:commission_simple.field_commission_result__message_has_error +msgid "Message Delivery error" +msgstr "Erreur d'envoi du message" + +#. module: commission_simple +#: model:ir.model.fields,field_description:commission_simple.field_commission_result__message_ids +msgid "Messages" +msgstr "" + +#. module: commission_simple +#: model:ir.model.fields,field_description:commission_simple.field_commission_result__my_activity_date_deadline +msgid "My Activity Deadline" +msgstr "Date butoir de l'activité" + +#. module: commission_simple +#: model:ir.model.fields,field_description:commission_simple.field_commission_profile__name +msgid "Name of the Profile" +msgstr "Nom du profil" + +#. module: commission_simple +#: model:ir.model.fields,field_description:commission_simple.field_commission_result__activity_calendar_event_id +msgid "Next Activity Calendar Event" +msgstr "Prochaine activité du calendrier" + +#. module: commission_simple +#: model:ir.model.fields,field_description:commission_simple.field_commission_result__activity_date_deadline +msgid "Next Activity Deadline" +msgstr "Date butoir de l'activité suivante" + +#. module: commission_simple +#: model:ir.model.fields,field_description:commission_simple.field_commission_result__activity_summary +msgid "Next Activity Summary" +msgstr "Résumé de l'activité suivante" + +#. module: commission_simple +#: model:ir.model.fields,field_description:commission_simple.field_commission_result__activity_type_id +msgid "Next Activity Type" +msgstr "Type de l'activité suivante" + +#. module: commission_simple +#. odoo-python +#: code:addons/commission_simple/wizards/commission_compute.py:0 +#, python-format +msgid "No commissions generated." +msgstr "Aucune commission n'a été générée." + +#. module: commission_simple +#: model:ir.model.fields,field_description:commission_simple.field_commission_result__message_needaction_counter +msgid "Number of Actions" +msgstr "Nombre d'actions" + +#. module: commission_simple +#: model:ir.model.fields,field_description:commission_simple.field_commission_result__message_has_error_counter +msgid "Number of errors" +msgstr "Nombre d'erreurs" + +#. module: commission_simple +#: model:ir.model.fields,help:commission_simple.field_commission_result__message_needaction_counter +msgid "Number of messages requiring action" +msgstr "Nombre de messages nécessitant une action" + +#. module: commission_simple +#: model:ir.model.fields,help:commission_simple.field_commission_result__message_has_error_counter +msgid "Number of messages with delivery error" +msgstr "Nombre de messages en échec d'envoi" + +#. module: commission_simple +#: model:ir.model.fields.selection,name:commission_simple.selection__commission_profile__trigger_type__paid +msgid "Paid" +msgstr "Payé" + +#. module: commission_simple +#: model:ir.model.fields,field_description:commission_simple.field_commission_compute__date_range_id +#: model:ir.model.fields,field_description:commission_simple.field_commission_result__date_range_id +#: model_terms:ir.ui.view,arch_db:commission_simple.commission_result_search +msgid "Period" +msgstr "Période" + +#. module: commission_simple +#: model:ir.model.fields,field_description:commission_simple.field_commission_rule__product_categ_ids +#: model:ir.model.fields.selection,name:commission_simple.selection__commission_rule__applied_on__3_product_category +msgid "Product Categories" +msgstr "Catégories de produits" + +#. module: commission_simple +#: model:ir.model.fields.selection,name:commission_simple.selection__commission_rule__applied_on__1_customer_product_category +msgid "Product Categories and Customers" +msgstr "Catégories de produits et clients" + +#. module: commission_simple +#: model:ir.model.fields,field_description:commission_simple.field_account_move_line__product_categ_id +msgid "Product Category" +msgstr "Catégorie de produit" + +#. module: commission_simple +#: model:ir.model.fields,field_description:commission_simple.field_commission_rule__product_ids +#: model:ir.model.fields.selection,name:commission_simple.selection__commission_rule__applied_on__2_product +msgid "Products" +msgstr "Produits" + +#. module: commission_simple +#: model:ir.model.fields.selection,name:commission_simple.selection__commission_rule__applied_on__0_customer_product +msgid "Products and Customers" +msgstr "Produits et clients" + +#. module: commission_simple +#: model:ir.model.fields,field_description:commission_simple.field_commission_profile_assignment__profile_id +#: model:ir.model.fields,field_description:commission_simple.field_commission_rule__profile_id +#: model_terms:ir.ui.view,arch_db:commission_simple.commission_rule_search +msgid "Profile" +msgstr "Profil" + +#. module: commission_simple +#: model_terms:ir.ui.view,arch_db:commission_simple.commission_result_form +#: model_terms:ir.ui.view,arch_db:commission_simple.commission_rule_tree +msgid "Rate (%)" +msgstr "Taux (%)" + +#. module: commission_simple +#: model:ir.model.constraint,message:commission_simple.constraint_commission_rule_rate_positive +msgid "Rate must be positive !" +msgstr "Le taux doit être positif !" + +#. module: commission_simple +#: model:ir.model.fields,field_description:commission_simple.field_commission_result__activity_user_id +msgid "Responsible User" +msgstr "Utilisateur responsable" + +#. module: commission_simple +#: model_terms:ir.ui.view,arch_db:commission_simple.commission_profile_form +msgid "Rules" +msgstr "Règles" + +#. module: commission_simple +#. odoo-python +#: code:addons/commission_simple/models/commission_profile.py:0 +#: model:ir.model.fields,field_description:commission_simple.field_commission_profile_assignment__user_id +#: model_terms:ir.ui.view,arch_db:commission_simple.commission_result_search +#, python-format +msgid "Salesman" +msgstr "Vendeur" + +#. module: commission_simple +#: model:ir.model.fields,field_description:commission_simple.field_commission_result__partner_id +msgid "Salesman/Agent" +msgstr "Vendeur/Agent" + +#. module: commission_simple +#: model:ir.model.fields,field_description:commission_simple.field_commission_profile__sequence +msgid "Sequence" +msgstr "Séquence" + +#. module: commission_simple +#: model:ir.model.fields,field_description:commission_simple.field_commission_rule__date_start +msgid "Start Date" +msgstr "Date de début" + +#. module: commission_simple +#: model:ir.model.fields,field_description:commission_simple.field_commission_compute__date_start +#: model:ir.model.fields,field_description:commission_simple.field_commission_result__date_start +msgid "Start date" +msgstr "Date de début" + +#. module: commission_simple +#: model:ir.model.fields,field_description:commission_simple.field_commission_result__state +msgid "State" +msgstr "État" + +#. module: commission_simple +#: model:ir.model.fields,help:commission_simple.field_commission_result__activity_state +msgid "" +"Status based on activities\n" +"Overdue: Due date is already passed\n" +"Today: Activity date is today\n" +"Planned: Future activities." +msgstr "" +"Statut basé sur les activités\n" +"En retard : La date d'échéance est déjà dépassée\n" +"Aujourd'hui : La date de l'activité est aujourd'hui\n" +"Planifié : Activités futures." + +#. module: commission_simple +#: model:ir.model.constraint,message:commission_simple.constraint_commission_profile_assignment_company_user_uniq +msgid "This salesman already has an assignment in this company." +msgstr "Ce vendeur a déjà une assignation dans cette société." + +#. module: commission_simple +#: model:ir.model.fields,field_description:commission_simple.field_commission_profile__trigger_type +msgid "Trigger" +msgstr "Déclencheur" + +#. module: commission_simple +#: model:ir.model.fields,field_description:commission_simple.field_commission_profile_assignment__assign_type +msgid "Type" +msgstr "" + +#. module: commission_simple +#: model:ir.model.fields,help:commission_simple.field_commission_result__activity_exception_decoration +msgid "Type of the exception activity on record." +msgstr "Type de l'activité-alerte sur l'enregistrement." + +#. module: commission_simple +#: model:ir.model.fields,field_description:commission_simple.field_commission_result__website_message_ids +msgid "Website Messages" +msgstr "Messages du site Web" + +#. module: commission_simple +#: model:ir.model.fields,help:commission_simple.field_commission_result__website_message_ids +msgid "Website communication history" +msgstr "Historique des échanges sur le site Web" + +#. module: commission_simple +#. odoo-python +#: code:addons/commission_simple/models/commission_result.py:0 +#, python-format +msgid "You cannot delete commission result %s because it is in done state." +msgstr "" +"Vous ne pouvez pas supprimer l'état de commission %s parce qu'il est à " +"l'état \"validé\"." diff --git a/commission_simple/models/__init__.py b/commission_simple/models/__init__.py new file mode 100644 index 0000000..5c812d8 --- /dev/null +++ b/commission_simple/models/__init__.py @@ -0,0 +1,5 @@ +from . import commission_profile +from . import commission_rule +from . import commission_result +from . import res_company +from . import account_move_line diff --git a/commission_simple/models/account_move_line.py b/commission_simple/models/account_move_line.py new file mode 100644 index 0000000..7bf84dd --- /dev/null +++ b/commission_simple/models/account_move_line.py @@ -0,0 +1,90 @@ +# Copyright 2019-2024 Akretion France (http://www.akretion.com/) +# @author Alexis de Lattre +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + + +from odoo import api, fields, models +from odoo.tools import float_is_zero + + +class AccountMoveLine(models.Model): + _inherit = 'account.move.line' + + commission_result_id = fields.Many2one( + 'commission.result', string='Commission Result', check_company=True) + commission_rule_id = fields.Many2one( + 'commission.rule', 'Matched Commission Rule', ondelete='restrict', check_company=True) + commission_base = fields.Monetary('Commission Base', currency_field='company_currency_id') + commission_rate = fields.Float('Commission Rate', digits='Commission Rate') + commission_amount = fields.Monetary( + string='Commission Amount', currency_field='company_currency_id', + readonly=True, compute='_compute_commission_amount', store=True) + # to display on commission line + product_categ_id = fields.Many2one( + related='product_id.product_tmpl_id.categ_id') + + @api.depends('commission_rate', 'commission_base') + def _compute_commission_amount(self): + for line in self: + commission_amount = False + if line.display_type == 'product': + commission_amount = line.company_currency_id.round( + line.commission_rate * line.commission_base / 100.0) + line.commission_amount = commission_amount + + def _match_commission_rule(self, rules): + # commission rules are already in the right order + self.ensure_one() + for rule in rules: + if rule['date_start'] and rule['date_start'] > self.date: + continue + if rule['date_end'] and rule['date_end'] < self.date: + continue + if rule['applied_on'] == '0_customer_product': + if ( + self.partner_id.id in + rule['partner_ids'] and + self.product_id.id in rule['product_ids']): + return rule + elif rule['applied_on'] == '1_customer_product_category': + if ( + self.partner_id.id in + rule['partner_ids'] and + self.product_categ_id.id in rule['product_categ_ids']): + return rule + elif rule['applied_on'] == '2_product': + if self.product_id.id in rule['product_ids']: + return rule + elif rule['applied_on'] == '3_product_category': + if self.product_categ_id.id in rule['product_categ_ids']: + return rule + elif rule['applied_on'] == '4_global': + return rule + return False + + def _prepare_commission_data(self, rule): + self.ensure_one() + rate_prec = self.env['decimal.precision'].precision_get('Commission Rate') + lvals = { + 'commission_rule_id': rule['id'], + # company currency + # inherit this method to change the value below if you want to base on margin + # or something else + 'commission_rate': rule['rate'], + } + if rule['base'] == 'margin': + # What do we do if it's negative ? For the moment, it adds a negative commission line + cost = 0 + if self.product_id and self.product_uom_id: + # if the module account_invoice_margin from akretion/odoo-usability is installed + if hasattr(self, 'margin_company_currency'): + cost = self.margin_company_currency + else: + sign = self.move_id.move_type == 'out_refund' and -1 or 1 + cost = self.product_id.standard_price * self.product_uom_id._compute_quantity(self.quantity, self.product_id.uom_id) * sign + lvals['commission_base'] = self.balance * -1 - cost + else: + lvals['commission_base'] = self.balance * -1 + if float_is_zero(lvals['commission_rate'], precision_digits=rate_prec) or self.company_currency_id.is_zero(lvals['commission_base']): + return False + return lvals diff --git a/commission_simple/models/commission_profile.py b/commission_simple/models/commission_profile.py new file mode 100644 index 0000000..e1f43e7 --- /dev/null +++ b/commission_simple/models/commission_profile.py @@ -0,0 +1,125 @@ +# Copyright Akretion France (http://www.akretion.com/) +# @author Alexis de Lattre +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + + +from odoo import fields, models, api, _ +from odoo.exceptions import ValidationError + + +class CommissionProfile(models.Model): + _name = 'commission.profile' + _description = 'Commission Profile' + _order = 'sequence, id' + + name = fields.Char(string='Name of the Profile', required=True) + active = fields.Boolean(string='Active', default=True) + sequence = fields.Integer() + company_id = fields.Many2one( + 'res.company', string='Company', ondelete='cascade', + required=False, default=lambda self: self.env.company) + assign_ids = fields.One2many( + 'commission.profile.assignment', 'profile_id', string="Assignments") + rule_ids = fields.One2many( + 'commission.rule', 'profile_id', string='Commission Rules') + trigger_type = fields.Selection([ + ('invoice', 'Invoiced'), + ('paid', 'Paid'), + ('in_payment', 'In Payment and Paid'), + ], default='paid', string='Trigger', required=True) + + +class CommissionProfileAssignment(models.Model): + _name = "commission.profile.assignment" + _description = "Commission Profile Assignment" + + profile_id = fields.Many2one('commission.profile', ondelete='cascade') + company_id = fields.Many2one( + 'res.company', string='Company', ondelete='cascade', + required=True, default=lambda self: self.env.company) + assign_type = fields.Selection( + '_assign_type_selection', default='user', required=True, string="Type") + user_id = fields.Many2one( + 'res.users', compute="_compute_user_id", store=True, precompute=True, readonly=False, + ondelete="restrict", string="Salesman", + ) + + _sql_constraints = [ + ( + 'company_user_uniq', + 'unique(user_id, company_id)', + 'This salesman already has an assignment in this company.')] + + @api.model + def _assign_type_selection(self): + return [('user', _('Salesman'))] + + @api.constrains('assign_type', 'user_id') + def _check_user(self): + for assignment in self: + if assignment.assign_type == 'user' and not assignment.user_id: + raise ValidationError(_("A salesman must be selected when the assignment type is 'Salesman'.")) + + @api.depends('assign_type') + def _compute_user_id(self): + for assign in self: + if assign.assign_type != 'user': + assign.user_id = False + + def _get_partner(self): + self.ensure_one() + if self.assign_type == 'user': + return self.user_id.partner_id + return False + + def _prepare_move_line_domain(self, date_range): + self.ensure_one() + domain = [ + ('display_type', '=', 'product'), + ('move_id.move_type', 'in', ('out_invoice', 'out_refund')), + ('date', '<=', date_range.date_end), + ('company_id', '=', self.company_id.id), + ('commission_result_id', '=', False), + ('parent_state', '=', 'posted'), + ] + if self.assign_type == 'user': + domain.append(('move_id.invoice_user_id', '=', self.user_id.id)) + # TODO : for trigger 'paid' and 'in_payment', we would need to filter + # out the invoices paid after the end date of the commission period + if self.profile_id.trigger_type == 'paid': + domain.append(('move_id.payment_state', 'in', ('paid', 'reversed'))) + elif self.profile_id.trigger_type == 'in_payment': + domain.append(('move_id.payment_state', 'in', ('in_payment', 'paid', 'reversed'))) + elif self.profile_id.trigger_type == 'invoice': + domain.append(('date', '>=', date_range.date_start)) + return domain + + def _prepare_commission_result(self, date_range): + vals = { + 'partner_id': self._get_partner().id, + 'profile_id': self.profile_id.id, + 'date_range_id': date_range.id, + 'assign_type': self.assign_type, + 'company_id': self.company_id.id, + } + return vals + + def _generate_commission_result(self, date_range, rules): + self.ensure_one() + ilines = self.env['account.move.line'].search( + self._prepare_move_line_domain(date_range), order='date, move_id, sequence, id') + profile = self.profile_id + ilines2write = {} + for iline in ilines: + rule = iline._match_commission_rule(rules[profile.id]) + if rule: + lvals = iline._prepare_commission_data(rule) + if lvals: + ilines2write[iline] = lvals + if ilines2write: + com_result = self.env['commission.result'].create(self._prepare_commission_result(date_range)) + for iline, vals in ilines2write.items(): + iline.write(dict(vals, commission_result_id=com_result.id)) + return com_result + else: + return False diff --git a/commission_simple/models/commission_result.py b/commission_simple/models/commission_result.py new file mode 100644 index 0000000..3e0dbc1 --- /dev/null +++ b/commission_simple/models/commission_result.py @@ -0,0 +1,77 @@ +# Copyright Akretion France (http://www.akretion.com/) +# @author Alexis de Lattre +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models, api, _ +from odoo.exceptions import UserError + + +class CommissionResult(models.Model): + _name = 'commission.result' + _description = "Commission Result" + _order = 'date_start desc' + _inherit = ['mail.thread', 'mail.activity.mixin'] + + partner_id = fields.Many2one( + 'res.partner', string='Salesman/Agent', required=True, ondelete='restrict', + readonly=True, tracking=True) + profile_id = fields.Many2one( + 'commission.profile', string='Commission Profile', readonly=True, tracking=True) + assign_type = fields.Selection('_assign_type_selection', readonly=True, tracking=True) + company_id = fields.Many2one( + 'res.company', string='Company', ondelete='cascade', + required=True, readonly=True, default=lambda self: self.env.company, tracking=True) + company_currency_id = fields.Many2one( + related='company_id.currency_id', string='Company Currency', store=True) + date_range_id = fields.Many2one( + 'date.range', required=True, string='Period', readonly=True, tracking=True) + date_start = fields.Date(related='date_range_id.date_start', store=True) + date_end = fields.Date(related='date_range_id.date_end', store=True) + line_ids = fields.One2many( + 'account.move.line', 'commission_result_id', string='Commission Lines', + states={'done': [('readonly', True)]}) + amount_total = fields.Monetary( + string='Commission Total', currency_field='company_currency_id', + compute='_compute_amount_total', store=True, tracking=True) + state = fields.Selection([ + ('draft', 'Draft'), + ('done', 'Done'), + ], default='draft', tracking=True) + # TODO copy amount to another field + # help='This is the total amount at the date of the computation of the commission') + + @api.model + def _assign_type_selection(self): + return self.env['commission.profile.assignment']._assign_type_selection() + + @api.depends('line_ids.commission_amount') + def _compute_amount_total(self): + rg_res = self.env['account.move.line'].read_group([('commission_result_id', 'in', self.ids)], ['commission_result_id', 'commission_amount:sum'], ['commission_result_id']) + mapped_data = dict([(x['commission_result_id'][0], x['commission_amount']) for x in rg_res]) + for rec in self: + rec.amount_total = mapped_data.get(rec.id, 0) + + def unlink(self): + for result in self: + if result.state == 'done': + raise UserError(_( + "You cannot delete commission result %s because it is in done state.") % result.display_name) + return super().unlink() + + def draft2done(self): + self.write({'state': 'done'}) + + def backtodraft(self): + self.write({'state': 'draft'}) + + def name_get(self): + res = [] + for result in self: + name = '%s (%s)' % (result.partner_id.name, result.date_range_id.name) + res.append((result.id, name)) + return res + + _sql_constraints = [( + 'salesman_period_company_unique', + 'unique(company_id, partner_id, date_range_id)', + 'A commission result already exists for this salesman/agent for the same period.')] diff --git a/commission_simple/models/commission_rule.py b/commission_simple/models/commission_rule.py new file mode 100644 index 0000000..590ba5b --- /dev/null +++ b/commission_simple/models/commission_rule.py @@ -0,0 +1,52 @@ +# Copyright Akretion France (http://www.akretion.com/) +# @author Alexis de Lattre +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + + +from odoo import fields, models, api + + +class CommissionRule(models.Model): + _name = 'commission.rule' + _description = 'Commission Rule' + _order = 'profile_id, applied_on' + + partner_ids = fields.Many2many( + 'res.partner', string='Customers', domain=[('parent_id', '=', False)]) + product_categ_ids = fields.Many2many( + 'product.category', string="Product Categories") + product_ids = fields.Many2many('product.product', string='Products') + date_start = fields.Date('Start Date') + date_end = fields.Date('End Date') + profile_id = fields.Many2one( + 'commission.profile', string='Profile', ondelete='cascade') + company_id = fields.Many2one(related='profile_id.company_id', store=True) + rate = fields.Float('Commission Rate', digits="Commission Rate", copy=False) + base = fields.Selection([ + ('invoiced', 'Invoiced Amount'), + ('margin', 'Margin'), + ], default='invoiced', required=True, string="Commission Base") + applied_on = fields.Selection([ + ('0_customer_product', 'Products and Customers'), + ('1_customer_product_category', "Product Categories and Customers"), + ('2_product', "Products"), + ('3_product_category', "Product Categories"), + ('4_global', 'Global')], + string='Apply On', default='4_global', required=True) + active = fields.Boolean(string='Active', default=True) + + @api.model + def load_all_rules(self): + rules = self.search_read([('profile_id', '!=', False)]) + res = {} # key = profile, value = [rule1 recordset, rule2] + for rule in rules: + if rule['profile_id'][0] not in res: + res[rule['profile_id'][0]] = [rule] + else: + res[rule['profile_id'][0]].append(rule) + return res + + _sql_constraints = [( + 'rate_positive', + 'CHECK(rate >= 0)', + 'Rate must be positive !')] diff --git a/commission_simple/models/res_company.py b/commission_simple/models/res_company.py new file mode 100644 index 0000000..e2b6373 --- /dev/null +++ b/commission_simple/models/res_company.py @@ -0,0 +1,13 @@ +# Copyright 2019-2024 Akretion France (https://www.akretion.com/) +# @author Alexis de Lattre +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + + +from odoo import fields, models + + +class ResCompany(models.Model): + _inherit = 'res.company' + + commission_date_range_type_id = fields.Many2one( + 'date.range.type', string='Commission Periodicity', ondelete='restrict') diff --git a/commission_simple/models/res_users.py b/commission_simple/models/res_users.py new file mode 100644 index 0000000..c39bb65 --- /dev/null +++ b/commission_simple/models/res_users.py @@ -0,0 +1,20 @@ +# Copyright 2019-2024 Akretion France (https://www.akretion.com/) +# @author Alexis de Lattre +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + + +from odoo import fields, models + + +class ResUsers(models.Model): + _inherit = 'res.users' + + # TODO mon idée : déplacer ça dans une table dédiée + # company_id oblig + # partner_id (filtré... sur lien vers user ou agent petit difficulté) + # profile_id + # type agent ou user => ça donne le champ de recherche + + commission_profile_id = fields.Many2one( + 'commission.profile', string='Commission Profile', + company_dependent=True) diff --git a/commission_simple/security/ir.model.access.csv b/commission_simple/security/ir.model.access.csv new file mode 100644 index 0000000..f92a076 --- /dev/null +++ b/commission_simple/security/ir.model.access.csv @@ -0,0 +1,11 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_commission_profile_read,Read access on commission.profile for employees,model_commission_profile,base.group_user,1,0,0,0 +access_commission_profile_full,Full access on commission.profile for financial manager,model_commission_profile,account.group_account_manager,1,1,1,1 +access_commission_profile_assignment_full,Full access on commission.profile.assignment for financial manager,model_commission_profile_assignment,account.group_account_manager,1,1,1,1 +access_commission_rule_full,Full access on commission.rule for financial manager,model_commission_rule,account.group_account_manager,1,1,1,1 +access_commission_rule_read,Read access on commission.rule for invoicing group,model_commission_rule,account.group_account_invoice,1,0,0,0 +access_commission_rule_audit,Read access on commission.rule for viewer group,model_commission_rule,account.group_account_readonly,1,0,0,0 +access_commission_result_full,Full access on commission.result to accountant,model_commission_result,account.group_account_user,1,1,1,1 +access_commission_result_read,Read access on commission.result to invoicing grp,model_commission_result,account.group_account_invoice,1,0,0,0 +access_commission_result_audit,Read access on commission.result to viewer grp,model_commission_result,account.group_account_readonly,1,0,0,0 +access_commission_compute_full,Full access to wizard commission.compute,model_commission_compute,account.group_account_manager,1,1,1,1 diff --git a/commission_simple/security/rule.xml b/commission_simple/security/rule.xml new file mode 100644 index 0000000..ef0245d --- /dev/null +++ b/commission_simple/security/rule.xml @@ -0,0 +1,29 @@ + + + + + + + + Commission Profile multi-company + + [('company_id', 'in', company_ids + [False])] + + + + Commission Rule multi-company + + [('company_id', 'in', company_ids + [False])] + + + + Commission Result multi-company + + [('company_id', 'in', company_ids)] + + + diff --git a/commission_simple/views/commission.xml b/commission_simple/views/commission.xml new file mode 100644 index 0000000..28eecad --- /dev/null +++ b/commission_simple/views/commission.xml @@ -0,0 +1,206 @@ + + + + + + + + + + + commission.profile.form + commission.profile + +
+ + + + + + + + + + + + + +
+
+
+ + + commission.profile.tree + commission.profile + + + + + + + + + + + + Commission Profiles + commission.profile + tree,form + + + + + + + + commission.rule.form + commission.rule + +
+ + + + + + + + + + + + + + + + + +
+
+
+ + + commission.rule.tree + commission.rule + + + + + + + + + + + + + commission.rule.search + commission.rule + + + + + + + + + + + + + Commission Rules + commission.rule + tree,form + {'commission_rule_main_view': True} + + + + + + + + commission.result.form + commission.result + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + + commission.result.tree + commission.result + + + + + + + + + + + + + + + + commission.result.search + commission.result + + + + + + + + + + + + + + Commissions + commission.result + tree,form + + + + + +
diff --git a/commission_simple/views/commission_profile.xml b/commission_simple/views/commission_profile.xml new file mode 100644 index 0000000..fc62649 --- /dev/null +++ b/commission_simple/views/commission_profile.xml @@ -0,0 +1,73 @@ + + + + + + + + + + commission.profile.form + commission.profile + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + + commission.profile.tree + commission.profile + + + + + + + + + + + + Commission Profiles + commission.profile + tree,form + + + + + +
diff --git a/commission_simple/views/commission_result.xml b/commission_simple/views/commission_result.xml new file mode 100644 index 0000000..9658356 --- /dev/null +++ b/commission_simple/views/commission_result.xml @@ -0,0 +1,113 @@ + + + + + + + commission.result.form + commission.result + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+
+
+
+ + + commission.result.tree + commission.result + + + + + + + + + + + + + + + + + + commission.result.search + commission.result + + + + + + + + + + + + + + + + + + + + Commissions + commission.result + tree,form + + + + + +
diff --git a/commission_simple/views/commission_rule.xml b/commission_simple/views/commission_rule.xml new file mode 100644 index 0000000..4b5ff8d --- /dev/null +++ b/commission_simple/views/commission_rule.xml @@ -0,0 +1,80 @@ + + + + + + + commission.rule.form + commission.rule + +
+ + + + + + + + + + + + + + + + +
+
+
+ + + commission.rule.tree + commission.rule + + + + + + + + + + + + + + commission.rule.search + commission.rule + + + + + + + + + + + + + Commission Rules + commission.rule + tree,form + {'commission_rule_main_view': True} + + + + + + +
diff --git a/commission_simple/views/res_config_settings.xml b/commission_simple/views/res_config_settings.xml new file mode 100644 index 0000000..0ccc859 --- /dev/null +++ b/commission_simple/views/res_config_settings.xml @@ -0,0 +1,37 @@ + + + + + + + + commission.res.config.settings.form + res.config.settings + + + +

Commissions

+
+
+
+
+
+
+
+
+
+ + + + + + diff --git a/commission_simple/views/res_users.xml b/commission_simple/views/res_users.xml new file mode 100644 index 0000000..7206313 --- /dev/null +++ b/commission_simple/views/res_users.xml @@ -0,0 +1,36 @@ + + + + + + + + commission.res.users.form + res.users + + + + + + + + + + + + commission.res.users.tree + res.users + + + + + + + + + + diff --git a/commission_simple/wizards/__init__.py b/commission_simple/wizards/__init__.py new file mode 100644 index 0000000..6eaf3db --- /dev/null +++ b/commission_simple/wizards/__init__.py @@ -0,0 +1,2 @@ +from . import commission_compute +from . import res_config_settings diff --git a/commission_simple/wizards/commission_compute.py b/commission_simple/wizards/commission_compute.py new file mode 100644 index 0000000..f6f7971 --- /dev/null +++ b/commission_simple/wizards/commission_compute.py @@ -0,0 +1,77 @@ +# Copyright 2019-2024 Akretion France (http://www.akretion.com) +# @author Alexis de Lattre +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models, _ +from dateutil.relativedelta import relativedelta +from odoo.exceptions import UserError +import logging +logger = logging.getLogger(__name__) + + +class CommissionCompute(models.TransientModel): + _name = 'commission.compute' + _description = 'Compute Commissions' + + company_id = fields.Many2one('res.company', required=True, default=lambda self: self.env.company) + date_range_type_id = fields.Many2one(related='company_id.commission_date_range_type_id') + date_range_id = fields.Many2one( + 'date.range', required=True, string='Period', + compute='_compute_date_range_id', store=True, precompute=True, readonly=False, + domain="[('type_id', '=', date_range_type_id)]") + date_start = fields.Date(related='date_range_id.date_start') + date_end = fields.Date(related='date_range_id.date_end') + + @api.depends('company_id') + def _compute_date_range_id(self): + for wiz in self: + date_range_id = False + company = wiz.company_id + if company and company.commission_date_range_type_id: + type_id = company.commission_date_range_type_id.id + last_commission_result = self.env['commission.result'].search([ + ('company_id', '=', company.id), + ], order='date_end desc', limit=1) + limit_date = last_commission_result and last_commission_result.date_end or (fields.Date.context_today(self) + relativedelta(months=-2, day=31)) + date_range = self.env['date.range'].search([ + ('company_id', 'in', (company.id, False)), + ('type_id', '=', type_id), + ('date_start', '>', limit_date) + ], order='date_start', limit=1) + date_range_id = date_range and date_range.id or False + wiz.date_range_id = date_range_id + + def run(self): + self.ensure_one() + creso = self.env['commission.result'] + date_range = self.date_range_id + existing_commissions = creso.search([ + ('date_range_id', '=', date_range.id), + ('company_id', '=', self.company_id.id), + ]) + if existing_commissions: + raise UserError(_( + 'Commissions already exist for %(period)s in company %(company)s.', + period=date_range.display_name, company=self.company_id.display_name)) + com_result_ids = self._core_compute() + if not com_result_ids: + raise UserError(_('No commissions generated.')) + action = self.env['ir.actions.actions']._for_xml_id( + 'commission_simple.commission_result_action') + action.update({ + 'views': False, + 'domain': f"[('id', 'in', {com_result_ids})]", + }) + return action + + def _core_compute(self): + rules = self.env['commission.rule'].load_all_rules() + com_result_ids = [] + assignments = self.env['commission.profile.assignment'].search([('company_id', '=', self.company_id.id)]) + for assignment in assignments: + com_result = assignment._generate_commission_result(self.date_range_id, rules) + if com_result: + com_result_ids.append(com_result.id) + else: + logger.info("No commission for %s", assignment._get_partner().display_name) + return com_result_ids diff --git a/commission_simple/wizards/commission_compute_view.xml b/commission_simple/wizards/commission_compute_view.xml new file mode 100644 index 0000000..4caa5e8 --- /dev/null +++ b/commission_simple/wizards/commission_compute_view.xml @@ -0,0 +1,41 @@ + + + + + + + commission.compute.form + commission.compute + +
+ + + + + + + + +
+
+
+
+
+ + + Compute Commissions + commission.compute + form + new + + + + +
diff --git a/commission_simple/wizards/res_config_settings.py b/commission_simple/wizards/res_config_settings.py new file mode 100644 index 0000000..c790ddd --- /dev/null +++ b/commission_simple/wizards/res_config_settings.py @@ -0,0 +1,12 @@ +# Copyright 2019-2024 Akretion France (https://www.akretion.com/) +# @author: Alexis de Lattre +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class ResConfigSettings(models.TransientModel): + _inherit = 'res.config.settings' + + commission_date_range_type_id = fields.Many2one( + related='company_id.commission_date_range_type_id', readonly=False) diff --git a/commission_simple_agent/__init__.py b/commission_simple_agent/__init__.py new file mode 100644 index 0000000..0650744 --- /dev/null +++ b/commission_simple_agent/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/commission_simple_agent/__manifest__.py b/commission_simple_agent/__manifest__.py new file mode 100644 index 0000000..ce16dc2 --- /dev/null +++ b/commission_simple_agent/__manifest__.py @@ -0,0 +1,22 @@ +# Copyright 2019-2024 Akretion France (http://www.akretion.com) +# @author Alexis de Lattre +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + 'name': 'Commission Simple Agent', + 'version': '16.0.1.0.0', + 'category': 'Sales', + 'license': 'AGPL-3', + 'summary': 'Glue module between commission_simple and sale_agent', + 'author': 'Akretion', + 'website': 'https://github.com/akretion/odoo-usability', + 'depends': [ + 'commission_simple', + 'sale_agent', + ], + 'data': [ + 'views/commission_profile.xml', + 'views/commission_result.xml', + ], + 'installable': True, +} diff --git a/commission_simple_agent/i18n/fr.po b/commission_simple_agent/i18n/fr.po new file mode 100644 index 0000000..8e9d929 --- /dev/null +++ b/commission_simple_agent/i18n/fr.po @@ -0,0 +1,44 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * commission_simple_agent +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-11-29 23:32+0000\n" +"PO-Revision-Date: 2024-11-29 23:32+0000\n" +"Last-Translator: Alexis de Lattre \n" +"Language-Team: \n" +"Language: fr\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: commission_simple_agent +#. odoo-python +#: code:addons/commission_simple_agent/models/commission_profile_assignment.py:0 +#: model:ir.model.fields,field_description:commission_simple_agent.field_commission_profile_assignment__agent_id +#: model_terms:ir.ui.view,arch_db:commission_simple_agent.commission_result_search +#, python-format +msgid "Agent" +msgstr "Agent" + +#. module: commission_simple_agent +#. odoo-python +#: code:addons/commission_simple_agent/models/commission_profile_assignment.py:0 +#, python-format +msgid "An agent must be selected when the assignment type is 'Agent'." +msgstr "" +"Un agent doit être sélectionné lorsque le type d'affectation est \"Agent\"." + +#. module: commission_simple_agent +#: model:ir.model,name:commission_simple_agent.model_commission_profile_assignment +msgid "Commission Profile Assignment" +msgstr "Affectation du profil de commission" + +#. module: commission_simple_agent +#: model:ir.model.constraint,message:commission_simple_agent.constraint_commission_profile_assignment_company_agent_uniq +msgid "This agent already has an assignment in this company." +msgstr "Cet agent a déjà une affectation dans cette société." diff --git a/commission_simple_agent/models/__init__.py b/commission_simple_agent/models/__init__.py new file mode 100644 index 0000000..ca82562 --- /dev/null +++ b/commission_simple_agent/models/__init__.py @@ -0,0 +1 @@ +from . import commission_profile_assignment diff --git a/commission_simple_agent/models/commission_profile_assignment.py b/commission_simple_agent/models/commission_profile_assignment.py new file mode 100644 index 0000000..065279f --- /dev/null +++ b/commission_simple_agent/models/commission_profile_assignment.py @@ -0,0 +1,51 @@ +# Copyright Akretion France (http://www.akretion.com/) +# @author Alexis de Lattre +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models, _ +from odoo.exceptions import ValidationError + + +class CommissionProfileAssignment(models.Model): + _inherit = "commission.profile.assignment" + + agent_id = fields.Many2one( + 'res.partner', ondelete='restrict', + compute="_compute_agent_id", store=True, precompute=True, readonly=False, + domain=[('agent', '=', True)]) + + _sql_constraints = [ + ( + 'company_agent_uniq', + 'unique(agent_id, company_id)', + 'This agent already has an assignment in this company.')] + + @api.model + def _assign_type_selection(self): + sel = super()._assign_type_selection() + sel.append(('agent', _('Agent'))) + return sel + + @api.constrains('assign_type', 'agent_id') + def _check_agent(self): + for assignment in self: + if assignment.assign_type == 'agent' and not assignment.agent_id: + raise ValidationError(_("An agent must be selected when the assignment type is 'Agent'.")) + + @api.depends('assign_type') + def _compute_agent_id(self): + for assign in self: + if assign.assign_type != 'agent': + assign.agent_id = False + + def _prepare_move_line_domain(self, date_range): + domain = super()._prepare_move_line_domain(date_range) + if self.assign_type == 'agent': + domain.append(('move_id.invoice_agent_id', '=', self.agent_id.id)) + return domain + + def _get_partner(self): + self.ensure_one() + if self.assign_type == 'agent': + return self.agent_id + return super()._get_partner() diff --git a/commission_simple_agent/views/commission_profile.xml b/commission_simple_agent/views/commission_profile.xml new file mode 100644 index 0000000..4bd82e1 --- /dev/null +++ b/commission_simple_agent/views/commission_profile.xml @@ -0,0 +1,21 @@ + + + + + + + commission.profile + + + + + + + + + + diff --git a/commission_simple_agent/views/commission_result.xml b/commission_simple_agent/views/commission_result.xml new file mode 100644 index 0000000..906b273 --- /dev/null +++ b/commission_simple_agent/views/commission_result.xml @@ -0,0 +1,32 @@ + + + + + + + commission.result + + + + assign_type == 'agent' + + + + + + + + commission.result + + + + + + + + + diff --git a/commission_simple_agent_purchase/__init__.py b/commission_simple_agent_purchase/__init__.py new file mode 100644 index 0000000..aee8895 --- /dev/null +++ b/commission_simple_agent_purchase/__init__.py @@ -0,0 +1,2 @@ +from . import models +from . import wizards diff --git a/commission_simple_agent_purchase/__manifest__.py b/commission_simple_agent_purchase/__manifest__.py new file mode 100644 index 0000000..ac7f333 --- /dev/null +++ b/commission_simple_agent_purchase/__manifest__.py @@ -0,0 +1,22 @@ +# Copyright 2019-2024 Akretion France (http://www.akretion.com) +# @author Alexis de Lattre +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + 'name': 'Commission Simple Agent Purchase', + 'version': '16.0.1.0.0', + 'category': 'Sales', + 'license': 'AGPL-3', + 'summary': 'Glue module between commission_simple_agent and purchase', + 'author': 'Akretion', + 'website': 'https://github.com/akretion/odoo-usability', + 'depends': [ + 'commission_simple_agent', + 'purchase', + ], + 'data': [ + 'views/commission_result.xml', + 'wizards/res_config_settings.xml', + ], + 'installable': True, +} diff --git a/commission_simple_agent_purchase/i18n/fr.po b/commission_simple_agent_purchase/i18n/fr.po new file mode 100644 index 0000000..8bb85f5 --- /dev/null +++ b/commission_simple_agent_purchase/i18n/fr.po @@ -0,0 +1,79 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * commission_simple_agent_purchase +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-11-29 23:33+0000\n" +"PO-Revision-Date: 2024-11-29 23:34+0000\n" +"Last-Translator: Alexis de Lattre \n" +"Language-Team: \n" +"Language: fr\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: commission_simple_agent_purchase +#. odoo-python +#: code:addons/commission_simple_agent_purchase/models/commission_result.py:0 +#, python-format +msgid "" +"Cannot delete commission %(commission)s because it is linked to purchase " +"order %(po)s. You must delete the purchase order first." +msgstr "" +"Impossible de supprimer la commission %(commission)s car elle est liée à la " +"commande fournisseur %(po)s. Vous devez d'abord supprimer la commande " +"fournisseur." + +#. module: commission_simple_agent_purchase +#. odoo-python +#: code:addons/commission_simple_agent_purchase/models/commission_result.py:0 +#, python-format +msgid "Commission %s" +msgstr "Commission %s" + +#. module: commission_simple_agent_purchase +#: model:ir.model.fields,field_description:commission_simple_agent_purchase.field_res_company__commission_product_id +#: model:ir.model.fields,field_description:commission_simple_agent_purchase.field_res_config_settings__commission_product_id +msgid "Commission Product" +msgstr "Produit de commission" + +#. module: commission_simple_agent_purchase +#: model:ir.model,name:commission_simple_agent_purchase.model_commission_result +msgid "Commission Result" +msgstr "État des commissions" + +#. module: commission_simple_agent_purchase +#. odoo-python +#: code:addons/commission_simple_agent_purchase/models/commission_result.py:0 +#, python-format +msgid "Commission product is not set on company %s." +msgstr "Le produit de commission n'est pas défini sur la société %s." + +#. module: commission_simple_agent_purchase +#: model:ir.model,name:commission_simple_agent_purchase.model_res_company +msgid "Companies" +msgstr "Sociétés" + +#. module: commission_simple_agent_purchase +#: model:ir.model,name:commission_simple_agent_purchase.model_res_config_settings +msgid "Config Settings" +msgstr "Configuration" + +#. module: commission_simple_agent_purchase +#: model:ir.model.fields,field_description:commission_simple_agent_purchase.field_commission_result__purchase_id +msgid "Purchase Order" +msgstr "Commande fournisseur" + +#. module: commission_simple_agent_purchase +#. odoo-python +#: code:addons/commission_simple_agent_purchase/models/commission_result.py:0 +#, python-format +msgid "" +"Purchase Order %s has already been confirmed. You should cancel it first." +msgstr "" +"La commande fournisseur %s a déjà été confirmée. Vous devez d'abord " +"l'annuler." diff --git a/commission_simple_agent_purchase/models/__init__.py b/commission_simple_agent_purchase/models/__init__.py new file mode 100644 index 0000000..8b8e848 --- /dev/null +++ b/commission_simple_agent_purchase/models/__init__.py @@ -0,0 +1,2 @@ +from . import commission_result +from . import res_company diff --git a/commission_simple_agent_purchase/models/commission_result.py b/commission_simple_agent_purchase/models/commission_result.py new file mode 100644 index 0000000..9c3778c --- /dev/null +++ b/commission_simple_agent_purchase/models/commission_result.py @@ -0,0 +1,78 @@ +# Copyright Akretion France (https://www.akretion.com/) +# @author Alexis de Lattre +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models, _ +from odoo.exceptions import UserError +from odoo.tools.misc import format_amount, formatLang + + +class CommissionResult(models.Model): + _inherit = 'commission.result' + + purchase_id = fields.Many2one('purchase.order', string="Purchase Order", tracking=True, readonly=True) + + def draft2done(self): + for result in self: + if result.assign_type == 'agent': + if not result.purchase_id: + vals = result._prepare_purchase_order() + po = self.env['purchase.order'].create(vals) + result.write({'purchase_id': po.id}) + else: + po = self.purchase_id + if po.state in ('draft', 'sent', 'cancel'): + po.order_line.unlink() + else: + raise UserError(_("Purchase Order %s has already been confirmed. You should cancel it first.") % po.display_name) + if po.state == 'cancel': + po.button_draft() + assert not po.order_line + # create lines + if not result.company_id.commission_product_id: + raise UserError(_("Commission product is not set on company %s.") % result.company_id.display_name) + line_vals = [] + for move_line in result.line_ids: + line_vals.append(result._prepare_purchase_order_line(move_line, po)) + po_lines = self.env['purchase.order.line'].create(line_vals) + po_lines._compute_tax_id() + return super().draft2done() + + def _prepare_purchase_order(self): + self.ensure_one() + fp = self.env['account.fiscal.position']._get_fiscal_position(self.partner_id) + vals = { + 'partner_id': self.partner_id.id, + 'origin': _('Commission %s') % self.date_range_id.display_name, + 'company_id': self.company_id.id, + 'currency_id': self.company_id.currency_id.id, + 'fiscal_position_id': fp and fp.id or False, + 'payment_term_id': self.partner_id.property_supplier_payment_term_id.id, + } + return vals + + def _prepare_purchase_order_line(self, move_line, order): + self.ensure_one() + move = move_line.move_id + company_currency = move_line.company_id.currency_id + lang = self.partner_id.lang or self.env.lang + env = self.with_context(lang=lang).env + product = self.company_id.commission_product_id + vals = { + 'order_id': order.id, + 'product_id': product.id, + 'name': f"""{move.name} {move.commercial_partner_id.name}: {move_line.product_id.display_name} x {formatLang(env, move_line.quantity, dp='Product Unit of Measure')} {move_line.product_uom_id.display_name}\n{_('Base:')} {format_amount(env, move_line.commission_base, company_currency)} - {_('Rate:')} {formatLang(env, move_line.commission_rate, dp='Commission Rate')} %""", + 'product_qty': 1, + 'product_uom': product.uom_id.id, + 'price_unit': move_line.commission_amount, + } + return vals + + def unlink(self): + for result in self: + if result.purchase_id: + raise UserError(_( + "Cannot delete commission %(commission)s because it is linked to " + "purchase order %(po)s. You must delete the purchase order first.", + commission=result.display_name, po=result.purchase_id.display_name)) + return super().unlink() diff --git a/commission_simple_agent_purchase/models/res_company.py b/commission_simple_agent_purchase/models/res_company.py new file mode 100644 index 0000000..253223a --- /dev/null +++ b/commission_simple_agent_purchase/models/res_company.py @@ -0,0 +1,14 @@ +# Copyright 2024 Akretion France (https://www.akretion.com/) +# @author Alexis de Lattre +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + + +from odoo import fields, models + + +class ResCompany(models.Model): + _inherit = 'res.company' + + commission_product_id = fields.Many2one( + 'product.product', string='Commission Product', ondelete='restrict', check_company=True, + domain=[('type', '=', 'service')]) diff --git a/commission_simple_agent_purchase/views/commission_result.xml b/commission_simple_agent_purchase/views/commission_result.xml new file mode 100644 index 0000000..320a428 --- /dev/null +++ b/commission_simple_agent_purchase/views/commission_result.xml @@ -0,0 +1,21 @@ + + + + + + + commission.result + + + + + + + + + + diff --git a/commission_simple_agent_purchase/wizards/__init__.py b/commission_simple_agent_purchase/wizards/__init__.py new file mode 100644 index 0000000..0deb68c --- /dev/null +++ b/commission_simple_agent_purchase/wizards/__init__.py @@ -0,0 +1 @@ +from . import res_config_settings diff --git a/commission_simple_agent_purchase/wizards/res_config_settings.py b/commission_simple_agent_purchase/wizards/res_config_settings.py new file mode 100644 index 0000000..0a99838 --- /dev/null +++ b/commission_simple_agent_purchase/wizards/res_config_settings.py @@ -0,0 +1,12 @@ +# Copyright 2019-2024 Akretion France (https://www.akretion.com/) +# @author: Alexis de Lattre +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class ResConfigSettings(models.TransientModel): + _inherit = 'res.config.settings' + + commission_product_id = fields.Many2one( + related='company_id.commission_product_id', readonly=False) diff --git a/commission_simple_agent_purchase/wizards/res_config_settings.xml b/commission_simple_agent_purchase/wizards/res_config_settings.xml new file mode 100644 index 0000000..585ce6f --- /dev/null +++ b/commission_simple_agent_purchase/wizards/res_config_settings.xml @@ -0,0 +1,26 @@ + + + + + + + + commission.res.config.settings.form + res.config.settings + + + +
+
+
+
+
+ + +
diff --git a/sale_agent/__manifest__.py b/sale_agent/__manifest__.py index cb695c9..378aac4 100644 --- a/sale_agent/__manifest__.py +++ b/sale_agent/__manifest__.py @@ -18,5 +18,6 @@ "views/account_move.xml", "views/account_invoice_report.xml", ], + 'demo': ['demo/demo.xml'], 'installable': True, } diff --git a/sale_agent/demo/demo.xml b/sale_agent/demo/demo.xml new file mode 100644 index 0000000..a25eb02 --- /dev/null +++ b/sale_agent/demo/demo.xml @@ -0,0 +1,32 @@ + + + + + + + Agent Bienrenseigné + + + + + + Agent Entregent + + + + + + Client Introduit + + + 12 rue du bas de loyasse + 69009 + Lyon + + + +