diff --git a/account_commission/README.rst b/account_commission/README.rst new file mode 100644 index 000000000..56765f8ee --- /dev/null +++ b/account_commission/README.rst @@ -0,0 +1,132 @@ +=================== +Account commissions +=================== + +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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/15.0/account_commission + :alt: OCA/commission +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/commission-15-0/commission-15-0-account_commission + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png + :target: https://runbot.odoo-community.org/runbot/165/15.0 + :alt: Try me on Runbot + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module adds the function to calculate commissions in invoices (account moves). + +This module depends on the commission module. + +**Table of contents** + +.. contents:: + :local: + +Configuration +============= + +For selecting invoice status in commissions: + +#. Edit or create a new record to select the invoice status for settling the commissions. + + * **Invoice Based**: Commissions are settled when the invoice is issued. + * **Payment Based**: Commissions are settled when the invoice is paid. + +Usage +===== + +For adding commissions on invoices: + +#. Go to *Invoicing > Customers > Invoices*. +#. Edit or create a new record. +#. When you have selected a partner, each new invoice line you add will have + the agents and commissions set at customer level. +#. You can add, modify or delete these agents discretely clicking on the + icon with several persons represented, next to the "Commission" field in the + list. This icon will be available only if the line hasn't been invoiced yet. +#. If you have configured your system for editing lines in a popup window, + agents will appear also in this window. +#. The agents icon will be in this ocassion visible when the line hasn't been + settled. +#. You have a button "Regenerate agents" on the bottom of the page + "Invoice Lines" for forcing a recompute of all agents from the partner setup. + This is needed for example when you have changed the partner on the + invoice having already inserted lines. + +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 smashing it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* Tecnativa + +Contributors +~~~~~~~~~~~~ + +* Pexego. +* Davide Corio +* Joao Alfredo Gama Batista +* Sandy Carter +* Giorgio Borelli +* Daniel Campos +* Oihane Crucelaegui +* Nicola Malcontenti +* Aitor Bouzas + +* `Tecnativa `__: + + * Pedro M. Baeza + * Manuel Calero + +* `Quartile `__: + + * Aung Ko Ko Lin + * Yoshi Tashiro + +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-pedrobaeza| image:: https://github.com/pedrobaeza.png?size=40px + :target: https://github.com/pedrobaeza + :alt: pedrobaeza + +Current `maintainer `__: + +|maintainer-pedrobaeza| + +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/account_commission/__init__.py b/account_commission/__init__.py new file mode 100644 index 000000000..7c4c8a90a --- /dev/null +++ b/account_commission/__init__.py @@ -0,0 +1,3 @@ +from . import models +from . import wizards +from . import report diff --git a/account_commission/__manifest__.py b/account_commission/__manifest__.py new file mode 100644 index 000000000..f461820f9 --- /dev/null +++ b/account_commission/__manifest__.py @@ -0,0 +1,26 @@ +# Copyright 2014-2020 Tecnativa - Pedro M. Baeza +# Copyright 2020 Tecnativa - Manuel Calero +{ + "name": "Account commissions", + "version": "15.0.1.0.0", + "author": "Tecnativa," "Odoo Community Association (OCA)", + "category": "Sales Management", + "license": "AGPL-3", + "depends": [ + "account", + "commission", + ], + "website": "https://github.com/OCA/commission", + "maintainers": ["pedrobaeza"], + "data": [ + "security/ir.model.access.csv", + "security/account_commission_security.xml", + "views/account_move_views.xml", + "views/account_commission_settlement_view.xml", + "views/commission_views.xml", + "views/report_settlement_templates.xml", + "views/res_partner.xml", + "report/commission_analysis_view.xml", + ], + "installable": True, +} diff --git a/account_commission/models/__init__.py b/account_commission/models/__init__.py new file mode 100644 index 000000000..bffca9a74 --- /dev/null +++ b/account_commission/models/__init__.py @@ -0,0 +1,4 @@ +from . import account_move +from . import commission +from . import settlement +from . import res_partner diff --git a/sale_commission/models/account_move.py b/account_commission/models/account_move.py similarity index 92% rename from sale_commission/models/account_move.py rename to account_commission/models/account_move.py index cfc6185c9..6654e08f7 100644 --- a/sale_commission/models/account_move.py +++ b/account_commission/models/account_move.py @@ -93,14 +93,14 @@ def fields_view_get( class AccountMoveLine(models.Model): _inherit = [ "account.move.line", - "sale.commission.mixin", + "commission.mixin", ] _name = "account.move.line" agent_ids = fields.One2many(comodel_name="account.invoice.line.agent") any_settled = fields.Boolean(compute="_compute_any_settled") settlement_id = fields.Many2one( - comodel_name="sale.commission.settlement", + comodel_name="commission.settlement", help="Settlement that generates this invoice line", copy=False, ) @@ -112,6 +112,16 @@ def _compute_any_settled(self): @api.depends("move_id.partner_id") def _compute_agent_ids(self): + for res in self: + settlement_lines = self.env["commission.settlement.line"].search( + [("invoice_line_id", "=", res.id)] + ) + for line in settlement_lines: + line.date = False + line.agent_id = False + line.settled_amount = 0.00 + line.currency_id = False + line.commission_id = False self.agent_ids = False # for resetting previous agents for record in self.filtered( lambda x: x.move_id.partner_id and x.move_id.move_type[:3] == "out" @@ -131,10 +141,11 @@ def _copy_data_extend_business_fields(self, values): super()._copy_data_extend_business_fields(values) if self.settlement_id and self.env.context.get("include_settlement", False): values["settlement_id"] = self.settlement_id.id + return class AccountInvoiceLineAgent(models.Model): - _inherit = "sale.commission.line.mixin" + _inherit = "commission.line.mixin" _name = "account.invoice.line.agent" _description = "Agent detail of commission line in invoice lines" @@ -152,7 +163,7 @@ class AccountInvoiceLineAgent(models.Model): readonly=True, ) agent_line = fields.Many2many( - comodel_name="sale.commission.settlement.line", + comodel_name="commission.settlement.line", relation="settlement_agent_line_rel", column1="agent_line_id", column2="settlement_id", diff --git a/account_commission/models/commission.py b/account_commission/models/commission.py new file mode 100644 index 000000000..056117b32 --- /dev/null +++ b/account_commission/models/commission.py @@ -0,0 +1,14 @@ +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class Commission(models.Model): + _inherit = "commission" + + invoice_state = fields.Selection( + [("open", "Invoice Based"), ("paid", "Payment Based")], + string="Invoice Status", + required=True, + default="open", + ) diff --git a/account_commission/models/res_partner.py b/account_commission/models/res_partner.py new file mode 100644 index 000000000..877cb56d5 --- /dev/null +++ b/account_commission/models/res_partner.py @@ -0,0 +1,17 @@ +# Copyright 2016-2019 Tecnativa - Pedro M. Baeza +# Copyright 2018 Tecnativa - Ernesto Tejeda +# License AGPL-3 - See https://www.gnu.org/licenses/agpl-3.0.html + +from odoo import fields, models + + +class ResPartner(models.Model): + """Add some fields related to commissions""" + + _inherit = "res.partner" + + settlement_ids = fields.One2many( + comodel_name="commission.settlement", + inverse_name="agent_id", + readonly=True, + ) diff --git a/account_commission/models/settlement.py b/account_commission/models/settlement.py new file mode 100644 index 000000000..3b2905619 --- /dev/null +++ b/account_commission/models/settlement.py @@ -0,0 +1,31 @@ +# Copyright 2014-2020 Tecnativa - Pedro M. Baeza +# Copyright 2020 Tecnativa - Manuel Calero +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import _, api, fields, models +from odoo.exceptions import UserError + + +class SettlementLine(models.Model): + _inherit = "commission.settlement.line" + + agent_line = fields.Many2many( + comodel_name="account.invoice.line.agent", + relation="settlement_agent_line_rel", + column1="settlement_id", + column2="agent_line_id", + required=True, + ) + invoice_line_id = fields.Many2one( + comodel_name="account.move.line", + store=True, + related="agent_line.object_id", + string="Source invoice line", + ) + + @api.constrains("settlement_id", "agent_line") + def _check_company(self): + for record in self: + for line in record.agent_line: + if line.company_id != record.company_id: + raise UserError(_("Company must be the same")) diff --git a/account_commission/readme/CONFIGURE.rst b/account_commission/readme/CONFIGURE.rst new file mode 100644 index 000000000..78d97059d --- /dev/null +++ b/account_commission/readme/CONFIGURE.rst @@ -0,0 +1,6 @@ +For selecting invoice status in commissions: + +#. Edit or create a new record to select the invoice status for settling the commissions. + + * **Invoice Based**: Commissions are settled when the invoice is issued. + * **Payment Based**: Commissions are settled when the invoice is paid. diff --git a/account_commission/readme/CONTRIBUTORS.rst b/account_commission/readme/CONTRIBUTORS.rst new file mode 100644 index 000000000..77b751822 --- /dev/null +++ b/account_commission/readme/CONTRIBUTORS.rst @@ -0,0 +1,19 @@ +* Pexego. +* Davide Corio +* Joao Alfredo Gama Batista +* Sandy Carter +* Giorgio Borelli +* Daniel Campos +* Oihane Crucelaegui +* Nicola Malcontenti +* Aitor Bouzas + +* `Tecnativa `__: + + * Pedro M. Baeza + * Manuel Calero + +* `Quartile `__: + + * Aung Ko Ko Lin + * Yoshi Tashiro diff --git a/account_commission/readme/DESCRIPTION.rst b/account_commission/readme/DESCRIPTION.rst new file mode 100644 index 000000000..92ffbc61c --- /dev/null +++ b/account_commission/readme/DESCRIPTION.rst @@ -0,0 +1,3 @@ +This module adds the function to calculate commissions in invoices (account moves). + +This module depends on the commission module. diff --git a/account_commission/readme/USAGE.rst b/account_commission/readme/USAGE.rst new file mode 100644 index 000000000..6ae855144 --- /dev/null +++ b/account_commission/readme/USAGE.rst @@ -0,0 +1,17 @@ +For adding commissions on invoices: + +#. Go to *Invoicing > Customers > Invoices*. +#. Edit or create a new record. +#. When you have selected a partner, each new invoice line you add will have + the agents and commissions set at customer level. +#. You can add, modify or delete these agents discretely clicking on the + icon with several persons represented, next to the "Commission" field in the + list. This icon will be available only if the line hasn't been invoiced yet. +#. If you have configured your system for editing lines in a popup window, + agents will appear also in this window. +#. The agents icon will be in this ocassion visible when the line hasn't been + settled. +#. You have a button "Regenerate agents" on the bottom of the page + "Invoice Lines" for forcing a recompute of all agents from the partner setup. + This is needed for example when you have changed the partner on the + invoice having already inserted lines. diff --git a/account_commission/report/__init__.py b/account_commission/report/__init__.py new file mode 100644 index 000000000..f93e938fb --- /dev/null +++ b/account_commission/report/__init__.py @@ -0,0 +1 @@ +from . import commission_analysis diff --git a/sale_commission/report/sale_commission_analysis_report.py b/account_commission/report/commission_analysis.py similarity index 80% rename from sale_commission/report/sale_commission_analysis_report.py rename to account_commission/report/commission_analysis.py index 4b54a01b1..12e833503 100644 --- a/sale_commission/report/sale_commission_analysis_report.py +++ b/account_commission/report/commission_analysis.py @@ -1,14 +1,15 @@ # Copyright 2014-2018 Tecnativa - Pedro M. Baeza # Copyright 2020 Tecnativa - Manuel Calero # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + from psycopg2.extensions import AsIs from odoo import api, fields, models, tools -class SaleCommissionAnalysisReport(models.Model): - _name = "sale.commission.analysis.report" - _description = "Sale Commission Analysis Report" +class InvoiceCommissionAnalysisReport(models.Model): + _name = "invoice.commission.analysis.report" + _description = "Invoice Commission Analysis Report" _auto = False _rec_name = "commission_id" @@ -21,7 +22,7 @@ def _get_selection_invoice_state(self): invoice_state = fields.Selection( selection="_get_selection_invoice_state", string="Invoice Status", readonly=True ) - date_invoice = fields.Date("Date Invoice", readonly=True) + date_invoice = fields.Date("Invoice Date", readonly=True) company_id = fields.Many2one("res.company", "Company", readonly=True) partner_id = fields.Many2one("res.partner", "Partner", readonly=True) agent_id = fields.Many2one("res.partner", "Agent", readonly=True) @@ -29,19 +30,16 @@ def _get_selection_invoice_state(self): product_id = fields.Many2one("product.product", "Product", readonly=True) uom_id = fields.Many2one("uom.uom", "Unit of Measure", readonly=True) quantity = fields.Float("# of Qty", readonly=True) - price_unit = fields.Float("Price unit", readonly=True) - price_subtotal = fields.Float("Price subtotal", readonly=True) + price_unit = fields.Float("Unit Price", readonly=True) + price_subtotal = fields.Float("Subtotal", readonly=True) balance = fields.Float( - string="Balance", readonly=True, ) percentage = fields.Integer("Percentage of commission", readonly=True) - amount = fields.Float("Amount", readonly=True) - invoice_line_id = fields.Many2one( - "account.move.line", "Invoice line", readonly=True - ) - settled = fields.Boolean("Settled", readonly=True) - commission_id = fields.Many2one("sale.commission", "Sale commission", readonly=True) + amount = fields.Float(readonly=True) + invoice_line_id = fields.Many2one("account.move.line", readonly=True) + settled = fields.Boolean(readonly=True) + commission_id = fields.Many2one("commission", "Commission", readonly=True) def _select(self): select_str = """ @@ -58,7 +56,7 @@ def _select(self): AVG(ail.price_unit) AS price_unit, SUM(ail.price_subtotal) AS price_subtotal, SUM(ail.balance) AS balance, - AVG(sc.fix_qty) AS percentage, + AVG(c.fix_qty) AS percentage, SUM(aila.amount) AS amount, ail.id AS invoice_line_id, aila.settled AS settled, @@ -71,7 +69,7 @@ def _from(self): account_invoice_line_agent aila LEFT JOIN account_move_line ail ON ail.id = aila.object_id INNER JOIN account_move ai ON ai.id = ail.move_id - LEFT JOIN sale_commission sc ON sc.id = aila.commission_id + LEFT JOIN commission c ON c.id = aila.commission_id LEFT JOIN product_product pp ON pp.id = ail.product_id INNER JOIN product_template pt ON pp.product_tmpl_id = pt.id LEFT JOIN res_partner rp ON aila.agent_id = rp.id diff --git a/sale_commission/report/sale_commission_analysis_report_view.xml b/account_commission/report/commission_analysis_view.xml similarity index 74% rename from sale_commission/report/sale_commission_analysis_report_view.xml rename to account_commission/report/commission_analysis_view.xml index 3e4a6b2bf..8a72ee2ad 100644 --- a/sale_commission/report/sale_commission_analysis_report_view.xml +++ b/account_commission/report/commission_analysis_view.xml @@ -1,31 +1,31 @@ - - sale.commission.analysis.pivot - sale.commission.analysis.report + + invoice.commission.analysis.pivot + invoice.commission.analysis.report - + - - sale.commission.analysis.graph - sale.commission.analysis.report + + invoice.commission.analysis.graph + invoice.commission.analysis.report - + - - sale.commission.analysis.search - sale.commission.analysis.report + + invoice.commission.analysis.search + invoice.commission.analysis.report - + - + Commission Analysis - sale.commission.analysis.report - form,pivot,graph - - + invoice.commission.analysis.report + pivot,graph + + This report performs analysis on your commissions added in invoice lines. You can check the amount and lines that will be settled by date, product, product category, aget, etc. Use this report to perform analysis on invoice lines agents not beeing settled yet. diff --git a/sale_commission/security/sale_commission_security.xml b/account_commission/security/account_commission_security.xml similarity index 62% rename from sale_commission/security/sale_commission_security.xml rename to account_commission/security/account_commission_security.xml index c2229e594..f3fe99133 100644 --- a/sale_commission/security/sale_commission_security.xml +++ b/account_commission/security/account_commission_security.xml @@ -10,11 +10,4 @@ name="domain_force" >['|', ('company_id', '=', False), ('company_id', 'in', company_ids)] - - Commission settlement multi company rule - - ['|', ('company_id', '=', False), ('company_id', 'in', company_ids)] - diff --git a/account_commission/security/ir.model.access.csv b/account_commission/security/ir.model.access.csv new file mode 100644 index 000000000..3ab48f625 --- /dev/null +++ b/account_commission/security/ir.model.access.csv @@ -0,0 +1,4 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_account_invoice_line_agent,access_account_invoice_line_agent,model_account_invoice_line_agent,account.group_account_invoice,1,1,1,1 +access_account_invoice_line_agent_user,access_account_invoice_line_agent_user,model_account_invoice_line_agent,base.group_user,1,0,0,0 +access_invoice_commission_analysis_report,access_invoice_commission_analysis_report,model_invoice_commission_analysis_report,base.group_user,1,0,0,0 diff --git a/account_commission/static/description/icon.png b/account_commission/static/description/icon.png new file mode 100644 index 000000000..053992789 Binary files /dev/null and b/account_commission/static/description/icon.png differ diff --git a/account_commission/static/description/icon.svg b/account_commission/static/description/icon.svg new file mode 100644 index 000000000..3e224be4e --- /dev/null +++ b/account_commission/static/description/icon.svg @@ -0,0 +1,2804 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + Openclipart + + + Pile of Golden Coins + 2010-04-09T03:27:45 + A pile of hypothetical golden coins, drawn in Inkscape. + https://openclipart.org/detail/43969/pile-of-golden-coins-by-j_alves + + + J_Alves + + + + + coin + currency + gold + money + thaler + + + + + + + + + + + diff --git a/account_commission/static/description/index.html b/account_commission/static/description/index.html new file mode 100644 index 000000000..b920424dc --- /dev/null +++ b/account_commission/static/description/index.html @@ -0,0 +1,474 @@ + + + + + + +Account commissions + + + +
+

Account commissions

+ + +

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

+

This module adds the function to calculate commissions in invoices (account moves).

+

This module depends on the commission module.

+

Table of contents

+ +
+

Configuration

+

For selecting invoice status in commissions:

+
    +
  1. Edit or create a new record to select the invoice status for settling the commissions.
      +
    • Invoice Based: Commissions are settled when the invoice is issued.
    • +
    • Payment Based: Commissions are settled when the invoice is paid.
    • +
    +
  2. +
+
+
+

Usage

+

For adding commissions on invoices:

+
    +
  1. Go to Invoicing > Customers > Invoices.
  2. +
  3. Edit or create a new record.
  4. +
  5. When you have selected a partner, each new invoice line you add will have +the agents and commissions set at customer level.
  6. +
  7. You can add, modify or delete these agents discretely clicking on the +icon with several persons represented, next to the “Commission” field in the +list. This icon will be available only if the line hasn’t been invoiced yet.
  8. +
  9. If you have configured your system for editing lines in a popup window, +agents will appear also in this window.
  10. +
  11. The agents icon will be in this ocassion visible when the line hasn’t been +settled.
  12. +
  13. You have a button “Regenerate agents” on the bottom of the page +“Invoice Lines” for forcing a recompute of all agents from the partner setup. +This is needed for example when you have changed the partner on the +invoice having already inserted lines.
  14. +
+
+
+

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 smashing it by providing a detailed and welcomed +feedback.

+

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

+
+
+

Credits

+
+

Authors

+
    +
  • Tecnativa
  • +
+
+
+

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:

+

pedrobaeza

+

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/account_commission/tests/__init__.py b/account_commission/tests/__init__.py new file mode 100644 index 000000000..4833fb2de --- /dev/null +++ b/account_commission/tests/__init__.py @@ -0,0 +1 @@ +from . import test_account_commission diff --git a/account_commission/tests/test_account_commission.py b/account_commission/tests/test_account_commission.py new file mode 100644 index 000000000..04fe516ea --- /dev/null +++ b/account_commission/tests/test_account_commission.py @@ -0,0 +1,395 @@ +# Copyright 2016-2019 Tecnativa - Pedro M. Baeza +# Copyright 2020 Tecnativa - Manuel Calero +# License AGPL-3 - See https://www.gnu.org/licenses/agpl-3.0.html + +from dateutil.relativedelta import relativedelta + +from odoo import fields +from odoo.exceptions import ValidationError +from odoo.tests import tagged +from odoo.tests.common import Form + +from odoo.addons.commission.tests.test_commission import TestCommission + + +@tagged("post_install", "-at_install") +class TestAccountCommission(TestCommission): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.commission_net_paid.write({"invoice_state": "paid"}) + cls.commission_net_invoice = cls.commission_model.create( + { + "name": "10% fixed commission (Net amount) - Invoice Based", + "fix_qty": 10.0, + "amount_base_type": "net_amount", + } + ) + cls.commission_section_paid.write({"invoice_state": "paid"}) + cls.product = cls.env.ref("product.product_product_5") + cls.product.list_price = 5 # for testing specific commission section + cls.default_line_account = cls.env.ref("account.data_account_type_receivable") + cls.agent_biweekly = cls.res_partner_model.create( + { + "name": "Test Agent - Bi-weekly", + "agent": True, + "settlement": "biweekly", + "lang": "en_US", + "commission_id": cls.commission_net_invoice.id, + } + ) + cls.income_account = cls.env["account.account"].search( + [ + ("company_id", "=", cls.company.id), + ("user_type_id.name", "=", "Income"), + ], + limit=1, + ) + + def _create_invoice(self, agent, commission, date=None): + invoice = self.env["account.move"].create( + { + "partner_id": self.partner.id, + "move_type": "out_invoice", + "invoice_line_ids": [ + ( + 0, + 0, + { + "name": self.product.name, + "product_id": self.product.id, + "quantity": 1.0, + "account_id": self.default_line_account.id, + "price_unit": self.product.lst_price, + "agent_ids": [ + ( + 0, + 0, + { + "agent_id": agent.id, + "commission_id": commission.id, + }, + ) + ], + }, + ) + ], + } + ) + if date: + invoice.invoice_date = date + invoice.date = date + return invoice + + def _settle_agent_invoice(self, agent=None, period=None, date=None): + vals = self._get_make_settle_vals(agent, period, date) + vals["settlement_type"] = "invoice" + wizard = self.make_settle_model.create(vals) + wizard.action_settle() + + def _process_invoice_and_settle(self, agent, commission, period, order=None): + if not order: + invoice = self._create_invoice(agent, commission) + else: + invoice = order.invoice_ids + invoice.invoice_line_ids.agent_ids._compute_amount() + invoice.action_post() + self._settle_agent_invoice(agent, period) + return invoice + + def _check_invoice_thru_settle( + self, agent, commission, period, initial_count, order=None + ): + invoice = self._process_invoice_and_settle(agent, commission, period, order) + settlements = self.settle_model.search([("state", "=", "settled")]) + self.assertEqual(len(settlements), initial_count) + journal = self.env["account.journal"].search( + [("type", "=", "cash"), ("company_id", "=", invoice.company_id.id)], + limit=1, + ) + register_payments = ( + self.env["account.payment.register"] + .with_context(active_ids=invoice.id, active_model="account.move") + .create({"journal_id": journal.id}) + ) + register_payments.action_create_payments() + self.assertEqual(invoice.partner_agent_ids.ids, agent.ids) + self.assertEqual( + self.env["account.move"] + .search([("partner_agent_ids", "=", agent.name)]) + .ids, + invoice.ids, + ) + self.assertIn(invoice.payment_state, ["in_payment", "paid"]) + self._settle_agent_invoice(agent, period) + settlements = self.settle_model.search([("state", "=", "settled")]) + self.assertTrue(settlements) + inv_line = invoice.invoice_line_ids[0] + self.assertTrue(inv_line.any_settled) + with self.assertRaises(ValidationError): + inv_line.agent_ids.amount = 5 + return self._check_settlements(agent, commission, settlements) + + def test_account_commission_gross_amount_payment(self): + self._check_invoice_thru_settle( + self.env.ref("commission.res_partner_pritesh_sale_agent"), + self.commission_section_paid, + 1, + 0, + ) + + def test_account_commission_gross_amount_payment_annual(self): + self._check_invoice_thru_settle( + self.agent_annual, self.commission_section_paid, 12, 0 + ) + + def test_account_commission_gross_amount_payment_semi(self): + self.product.list_price = 15100 # for testing specific commission section + self._check_invoice_thru_settle( + self.agent_semi, self.commission_section_invoice, 6, 1 + ) + + def test_account_commission_gross_amount_invoice(self): + self._process_invoice_and_settle( + self.agent_quaterly, + self.env.ref("commission.demo_commission"), + 1, + ) + settlements = self.settle_model.search([("state", "=", "invoiced")]) + settlements.make_invoices(self.journal, self.commission_product) + for settlement in settlements: + self.assertNotEqual( + len(settlement.invoice_id), + 0, + "Settlements need to be in Invoiced State.", + ) + + def test_commission_status(self): + # Make sure user is in English + self.env.user.lang = "en_US" + invoice = self._create_invoice( + self.env.ref("commission.res_partner_pritesh_sale_agent"), + self.commission_section_invoice, + ) + self.assertIn("1", invoice.invoice_line_ids[0].commission_status) + self.assertNotIn("agents", invoice.invoice_line_ids[0].commission_status) + invoice.mapped("invoice_line_ids.agent_ids").unlink() + self.assertIn("No", invoice.invoice_line_ids[0].commission_status) + invoice.invoice_line_ids[0].agent_ids = [ + ( + 0, + 0, + { + "agent_id": self.env.ref( + "commission.res_partner_pritesh_sale_agent" + ).id, + "commission_id": self.env.ref("commission.demo_commission").id, + }, + ), + ( + 0, + 0, + { + "agent_id": self.env.ref( + "commission.res_partner_eiffel_sale_agent" + ).id, + "commission_id": self.env.ref("commission.demo_commission").id, + }, + ), + ] + self.assertIn("2", invoice.invoice_line_ids[0].commission_status) + self.assertIn("agents", invoice.invoice_line_ids[0].commission_status) + invoice.action_post() + # Free + invoice.invoice_line_ids.commission_free = True + self.assertIn("free", invoice.invoice_line_ids.commission_status) + self.assertAlmostEqual(invoice.invoice_line_ids.agent_ids.amount, 0) + # test show agents buton + action = invoice.invoice_line_ids.button_edit_agents() + self.assertEqual(action["res_id"], invoice.invoice_line_ids.id) + + def test_supplier_invoice(self): + """No agents should be populated on supplier invoices.""" + self.partner.agent_ids = self.agent_semi + move_form = Form( + self.env["account.move"].with_context(default_move_type="in_invoice") + ) + move_form.partner_id = self.partner + move_form.ref = "sale_comission_TEST" + with move_form.invoice_line_ids.new() as line_form: + line_form.product_id = self.product + line_form.quantity = 1 + line_form.currency_id = self.company.currency_id + invoice = move_form.save() + self.assertFalse(invoice.invoice_line_ids.agent_ids) + + def test_commission_propagation(self): + """Test propagation of agents from partner to invoice.""" + self.partner.agent_ids = [(4, self.agent_monthly.id)] + move_form = Form( + self.env["account.move"].with_context(default_move_type="out_invoice") + ) + move_form.partner_id = self.partner + with move_form.invoice_line_ids.new() as line_form: + line_form.currency_id = self.company.currency_id + line_form.product_id = self.product + line_form.quantity = 1 + invoice = move_form.save() + agent = invoice.invoice_line_ids.agent_ids + self._check_propagation(agent, self.commission_net_invoice, self.agent_monthly) + # Check agent change + agent.agent_id = self.agent_quaterly + self.assertTrue(agent.commission_id, self.commission_section_invoice) + # Check recomputation + agent.unlink() + invoice.recompute_lines_agents() + agent = invoice.invoice_line_ids.agent_ids + self._check_propagation(agent, self.commission_net_invoice, self.agent_monthly) + + def test_negative_settlements(self): + self.product.write({"list_price": 1000}) + agent = self.agent_monthly + commission = self.commission_net_invoice + invoice = self._process_invoice_and_settle(agent, commission, 1) + settlement = self.settle_model.search([("agent_id", "=", agent.id)]) + self.assertEqual(1, len(settlement)) + self.assertEqual(settlement.state, "settled") + commission_invoice = settlement.make_invoices( + product=self.commission_product, journal=self.journal + ) + self.assertEqual(settlement.state, "invoiced") + self.assertEqual(commission_invoice.move_type, "in_invoice") + refund = invoice._reverse_moves( + default_values_list=[{"invoice_date": invoice.invoice_date}], + ) + self.assertEqual( + invoice.invoice_line_ids.agent_ids.agent_id, + refund.invoice_line_ids.agent_ids.agent_id, + ) + refund.invoice_line_ids.agent_ids._compute_amount() + refund.action_post() + self._settle_agent_invoice(agent, 1) + settlements = self.settle_model.search([("agent_id", "=", agent.id)]) + self.assertEqual(2, len(settlements)) + second_settlement = settlements.filtered(lambda r: r.total < 0) + self.assertEqual(second_settlement.state, "settled") + # Use invoice wizard for testing also this part + wizard = self.env["commission.make.invoice"].create( + {"product_id": self.commission_product.id} + ) + action = wizard.button_create() + commission_refund = self.env["account.move"].browse(action["domain"][0][2]) + self.assertEqual(second_settlement.state, "invoiced") + self.assertEqual(commission_refund.move_type, "in_refund") + # Undo invoices + make invoice again to get a unified invoice + commission_invoices = commission_invoice + commission_refund + commission_invoices.button_cancel() + self.assertEqual(settlement.state, "except_invoice") + self.assertEqual(second_settlement.state, "except_invoice") + commission_invoices.unlink() + settlements.unlink() + self._settle_agent_invoice(False, 1) # agent=False for testing default + settlement = self.settle_model.search([("agent_id", "=", agent.id)]) + # Check make invoice wizard + action = settlement.action_invoice() + self.assertEqual(action["context"]["settlement_ids"], settlement.ids) + # Use invoice wizard for testing also this part + wizard = self.env["commission.make.invoice"].create( + { + "product_id": self.commission_product.id, + "journal_id": self.journal.id, + "settlement_ids": [(4, settlement.id)], + } + ) + action = wizard.button_create() + invoice = self.env["account.move"].browse(action["domain"][0][2]) + self.assertEqual(invoice.move_type, "in_invoice") + self.assertAlmostEqual(invoice.amount_total, 0) + + def test_negative_settlements_join_invoice(self): + self.product.write({"list_price": 1000}) + agent = self.agent_monthly + commission = self.commission_net_invoice + invoice = self._process_invoice_and_settle(agent, commission, 1) + settlement = self.settle_model.search([("agent_id", "=", agent.id)]) + self.assertEqual(1, len(settlement)) + self.assertEqual(settlement.state, "settled") + refund = invoice._reverse_moves( + default_values_list=[ + { + "invoice_date": invoice.invoice_date + relativedelta(months=-1), + "date": invoice.date + relativedelta(months=-1), + } + ], + ) + self.assertEqual( + invoice.invoice_line_ids.agent_ids.agent_id, + refund.invoice_line_ids.agent_ids.agent_id, + ) + refund.action_post() + self._settle_agent_invoice(agent, 1) + settlements = self.settle_model.search([("agent_id", "=", agent.id)]) + self.assertEqual(2, len(settlements)) + second_settlement = settlements.filtered(lambda r: r.total < 0) + self.assertEqual(second_settlement.state, "settled") + # Use invoice wizard for testing also this part + wizard = self.env["commission.make.invoice"].create( + {"product_id": self.commission_product.id, "grouped": True} + ) + action = wizard.button_create() + commission_invoice = self.env["account.move"].browse(action["domain"][0][2]) + self.assertEqual(1, len(commission_invoice)) + self.assertEqual(commission_invoice.move_type, "in_invoice") + self.assertAlmostEqual(commission_invoice.amount_total, 0, places=2) + + def _create_multi_settlements(self): + agent = self.agent_monthly + commission = self.commission_section_invoice + today = fields.Date.today() + last_month = today + relativedelta(months=-1) + invoice_1 = self._create_invoice(agent, commission, today) + invoice_1.action_post() + invoice_2 = self._create_invoice(agent, commission, last_month) + invoice_2.action_post() + self._settle_agent_invoice(agent, 1) + settlements = self.settle_model.search( + [ + ("agent_id", "=", agent.id), + ("state", "=", "settled"), + ] + ) + self.assertEqual(2, len(settlements)) + return settlements + + def test_commission_single_invoice(self): + settlements = self._create_multi_settlements() + settlements.make_invoices(self.journal, self.commission_product, grouped=True) + invoices = settlements.mapped("invoice_id") + self.assertEqual(1, len(invoices)) + + def test_commission_multiple_invoice(self): + settlements = self._create_multi_settlements() + settlements.make_invoices(self.journal, self.commission_product) + invoices = settlements.mapped("invoice_id") + self.assertEqual(2, len(invoices)) + + def test_biweekly(self): + agent = self.agent_biweekly + commission = self.commission_net_invoice + invoice = self._create_invoice(agent, commission) + invoice.invoice_date = "2022-01-01" + invoice.date = "2022-01-01" + invoice.action_post() + invoice2 = self._create_invoice(agent, commission, date="2022-01-16") + invoice2.invoice_date = "2022-01-16" + invoice2.date = "2022-01-16" + invoice2.action_post() + invoice3 = self._create_invoice(agent, commission, date="2022-01-31") + invoice3.invoice_date = "2022-01-31" + invoice3.date = "2022-01-31" + invoice3.action_post() + self._settle_agent_invoice(agent=self.agent_biweekly, date="2022-02-01") + settlements = self.settle_model.search( + [("agent_id", "=", self.agent_biweekly.id)] + ) + self.assertEqual(len(settlements), 2) diff --git a/account_commission/views/account_commission_settlement_view.xml b/account_commission/views/account_commission_settlement_view.xml new file mode 100644 index 000000000..eb789f6ae --- /dev/null +++ b/account_commission/views/account_commission_settlement_view.xml @@ -0,0 +1,32 @@ + + + + Settlements + commission.settlement + + + + + + + + + Settlement lines + commission.settlement.line + + + + + + + + diff --git a/sale_commission/views/account_move_views.xml b/account_commission/views/account_move_views.xml similarity index 93% rename from sale_commission/views/account_move_views.xml rename to account_commission/views/account_move_views.xml index 445b2baae..5b6ceeccb 100644 --- a/sale_commission/views/account_move_views.xml +++ b/account_commission/views/account_move_views.xml @@ -4,7 +4,7 @@ account.invoice.line.agent - + @@ -28,7 +28,6 @@ attrs="{'readonly': [('commission_free', '=', True)]}" force_save="1" /> - @@ -63,7 +62,6 @@ ('parent.move_type', 'not in', ['out_invoice', 'out_refund']) ]}" /> -