From edc4c3801de8057658a834de23d10b02ee50f5d3 Mon Sep 17 00:00:00 2001 From: Yoshi Tashiro Date: Wed, 26 Oct 2022 16:35:48 +0000 Subject: [PATCH] wip - restructure menu items, move wizard_settle to commistion, update tests --- account_commission/__init__.py | 2 +- account_commission/__manifest__.py | 3 +- account_commission/models/__init__.py | 1 + account_commission/models/commission.py | 14 + account_commission/readme/CONFIGURE.rst | 4 +- account_commission/readme/CONTRIBUTORS.rst | 1 + account_commission/readme/USAGE.rst | 2 +- .../security/ir.model.access.csv | 1 - account_commission/tests/__init__.py | 2 +- ...mmission.py => test_account_commission.py} | 264 ++++++------------ account_commission/views/commission_views.xml | 13 + .../{wizard => wizards}/__init__.py | 0 account_commission/wizards/wizard_settle.py | 30 ++ commission/__init__.py | 2 +- commission/__manifest__.py | 16 +- commission/data/menuitem_data.xml | 19 ++ commission/demo/commission_and_agent_demo.xml | 6 +- commission/models/commission.py | 8 +- commission/models/settlement.py | 7 +- commission/readme/CONTRIBUTORS.rst | 1 + commission/security/ir.model.access.csv | 1 + commission/tests/test_commission.py | 176 +++++++++++- ...in_view.xml => commission_mixin_views.xml} | 0 ...nt.xml => commission_settlement_views.xml} | 4 +- ...mmission_view.xml => commission_views.xml} | 12 +- ...te_view.xml => product_template_views.xml} | 0 ...partner_view.xml => res_partner_views.xml} | 2 +- commission/{wizard => wizards}/__init__.py | 1 + .../{wizard => wizards}/wizard_invoice.py | 0 .../{wizard => wizards}/wizard_invoice.xml | 4 +- .../wizards}/wizard_settle.py | 49 ++-- .../wizards}/wizard_settle.xml | 12 +- 32 files changed, 403 insertions(+), 254 deletions(-) create mode 100644 account_commission/models/commission.py rename account_commission/tests/{test_invoice_commission.py => test_account_commission.py} (65%) create mode 100644 account_commission/views/commission_views.xml rename account_commission/{wizard => wizards}/__init__.py (100%) create mode 100644 account_commission/wizards/wizard_settle.py create mode 100644 commission/data/menuitem_data.xml rename commission/views/{commission_mixin_view.xml => commission_mixin_views.xml} (100%) rename commission/views/{commission_settlement.xml => commission_settlement_views.xml} (98%) rename commission/views/{commission_view.xml => commission_views.xml} (87%) rename commission/views/{product_template_view.xml => product_template_views.xml} (100%) rename commission/views/{res_partner_view.xml => res_partner_views.xml} (98%) rename commission/{wizard => wizards}/__init__.py (50%) rename commission/{wizard => wizards}/wizard_invoice.py (100%) rename commission/{wizard => wizards}/wizard_invoice.xml (94%) rename {account_commission/wizard => commission/wizards}/wizard_settle.py (81%) rename {account_commission/wizard => commission/wizards}/wizard_settle.xml (81%) diff --git a/account_commission/__init__.py b/account_commission/__init__.py index 50235cbb1..7c4c8a90a 100644 --- a/account_commission/__init__.py +++ b/account_commission/__init__.py @@ -1,3 +1,3 @@ from . import models -from . import wizard +from . import wizards from . import report diff --git a/account_commission/__manifest__.py b/account_commission/__manifest__.py index 40501bb8a..f461820f9 100644 --- a/account_commission/__manifest__.py +++ b/account_commission/__manifest__.py @@ -8,7 +8,6 @@ "license": "AGPL-3", "depends": [ "account", - "product", "commission", ], "website": "https://github.com/OCA/commission", @@ -18,9 +17,9 @@ "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", - "wizard/wizard_settle.xml", "report/commission_analysis_view.xml", ], "installable": True, diff --git a/account_commission/models/__init__.py b/account_commission/models/__init__.py index 449bae927..bffca9a74 100644 --- a/account_commission/models/__init__.py +++ b/account_commission/models/__init__.py @@ -1,3 +1,4 @@ from . import account_move +from . import commission from . import settlement from . import res_partner 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/readme/CONFIGURE.rst b/account_commission/readme/CONFIGURE.rst index 7c399782d..a10ea2f3e 100644 --- a/account_commission/readme/CONFIGURE.rst +++ b/account_commission/readme/CONFIGURE.rst @@ -24,7 +24,7 @@ For adding commissions: For adding new agents: -#. Go to *Invoicing > Vendors > Agents*. You can also access from +#. Go to *Invoicing > Vendors > Commissios Agents*. You can also access from *Contacts > Contacts* or *Invoicing > Customers > Customers*. #. Edit or create a new record. #. On "Sales & Purchases" page, mark "Agent" check. It should be checked if @@ -37,7 +37,7 @@ For adding new agents: for setting an "Employee" agent type. * The associated commission type. * The settlement period, where you can select: - * + * Monthly: the settlement will be done for the whole past month. * Bi-weekly: there will be 2 settlement per month, one covering the first 15 days, and the other for the rest of the month. diff --git a/account_commission/readme/CONTRIBUTORS.rst b/account_commission/readme/CONTRIBUTORS.rst index 9c0ce3b25..f56cd6749 100644 --- a/account_commission/readme/CONTRIBUTORS.rst +++ b/account_commission/readme/CONTRIBUTORS.rst @@ -7,6 +7,7 @@ * Oihane Crucelaegui * Nicola Malcontenti * Aitor Bouzas +* Aung Ko Ko Lin * `Tecnativa `__: diff --git a/account_commission/readme/USAGE.rst b/account_commission/readme/USAGE.rst index ffcaabbe1..ff850b044 100644 --- a/account_commission/readme/USAGE.rst +++ b/account_commission/readme/USAGE.rst @@ -24,7 +24,7 @@ For adding commissions on invoices: For settling the commissions to agents: -#. Go to *Invoicing > Configuration > Commissions Management > Settle commissions*. +#. Go to *Invoicing > Vendors > Commissions > Settle commissions*. #. On the window that appears, you should select the date up to which you want to create commissions. It should be at least one day after the last period date. For example, if you settlements are monthly, you have to put diff --git a/account_commission/security/ir.model.access.csv b/account_commission/security/ir.model.access.csv index 0c9f819b1..3ab48f625 100644 --- a/account_commission/security/ir.model.access.csv +++ b/account_commission/security/ir.model.access.csv @@ -1,5 +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_make_settle,access_invoice_commission_make_settle,model_invoice_commission_make_settle,account.group_account_manager,1,1,1,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/tests/__init__.py b/account_commission/tests/__init__.py index d447ef4e2..4833fb2de 100644 --- a/account_commission/tests/__init__.py +++ b/account_commission/tests/__init__.py @@ -1 +1 @@ -from . import test_invoice_commission +from . import test_account_commission diff --git a/account_commission/tests/test_invoice_commission.py b/account_commission/tests/test_account_commission.py similarity index 65% rename from account_commission/tests/test_invoice_commission.py rename to account_commission/tests/test_account_commission.py index a806425f2..115692c9f 100644 --- a/account_commission/tests/test_invoice_commission.py +++ b/account_commission/tests/test_account_commission.py @@ -1,26 +1,23 @@ -import dateutil.relativedelta +# 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 UserError, ValidationError +from odoo.exceptions import ValidationError from odoo.tests import tagged -from odoo.tests.common import Form, TransactionCase +from odoo.tests.common import Form + +from odoo.addons.commission.tests.test_commission import TestCommission @tagged("post_install", "-at_install") -class TestInvoiceCommission(TransactionCase): +class TestAccountCommission(TestCommission): @classmethod def setUpClass(cls): super().setUpClass() - cls.commission_model = cls.env["commission"] - cls.commission_net_paid = cls.commission_model.create( - { - "name": "20% fixed commission (Net amount) - Payment Based", - "fix_qty": 20.0, - "invoice_state": "paid", - "amount_base_type": "net_amount", - } - ) + cls.commission_net_paid.write({"invoice_state": "paid"}) cls.commission_net_invoice = cls.commission_model.create( { "name": "10% fixed commission (Net amount) - Invoice Based", @@ -28,61 +25,10 @@ def setUpClass(cls): "amount_base_type": "net_amount", } ) - cls.commission_section_paid = cls.commission_model.create( - { - "name": "Section commission - Payment Based", - "commission_type": "section", - "invoice_state": "paid", - "section_ids": [ - (0, 0, {"amount_from": 1.0, "amount_to": 100.0, "percent": 10.0}) - ], - "amount_base_type": "net_amount", - } - ) - cls.commission_section_invoice = cls.commission_model.create( - { - "name": "Section commission - Invoice Based", - "commission_type": "section", - "section_ids": [ - ( - 0, - 0, - { - "amount_from": 15000.0, - "amount_to": 16000.0, - "percent": 20.0, - }, - ) - ], - } - ) - cls.company = cls.env.ref("base.main_company") - cls.res_partner_model = cls.env["res.partner"] - cls.partner = cls.env.ref("base.res_partner_2") - cls.partner.write({"agent": False}) - cls.invoice_model = cls.env["account.move"] - cls.default_line_account = cls.env.ref("account.data_account_type_receivable") - - cls.settle_model = cls.env["commission.settlement"] - cls.make_settle_model = cls.env["invoice.commission.make.settle"] - cls.make_inv_model = cls.env["commission.make.invoice"] + 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.commission_product = cls.env["product.product"].create( - {"name": "Commission test product", "type": "service"} - ) - cls.journal = cls.env["account.journal"].search( - [("type", "=", "purchase")], limit=1 - ) - cls.agent_monthly = cls.res_partner_model.create( - { - "name": "Test Agent - Monthly", - "agent": True, - "settlement": "monthly", - "lang": "en_US", - "commission_id": cls.commission_net_invoice.id, - } - ) + 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", @@ -92,31 +38,6 @@ def setUpClass(cls): "commission_id": cls.commission_net_invoice.id, } ) - cls.agent_quaterly = cls.res_partner_model.create( - { - "name": "Test Agent - Quaterly", - "agent": True, - "settlement": "quaterly", - "lang": "en_US", - "commission_id": cls.commission_section_invoice.id, - } - ) - cls.agent_semi = cls.res_partner_model.create( - { - "name": "Test Agent - Semi-annual", - "agent": True, - "settlement": "semi", - "lang": "en_US", - } - ) - cls.agent_annual = cls.res_partner_model.create( - { - "name": "Test Agent - Annual", - "agent": True, - "settlement": "annual", - "lang": "en_US", - } - ) cls.income_account = cls.env["account.account"].search( [ ("company_id", "=", cls.company.id), @@ -126,7 +47,7 @@ def setUpClass(cls): ) def _create_invoice(self, agent, commission, date=None): - return self.invoice_model.create( + invoice = self.env["account.move"].create( { "partner_id": self.partner.id, "move_type": "out_invoice", @@ -155,30 +76,31 @@ def _create_invoice(self, agent, commission, date=None): ], } ) + if date: + invoice.invoice_date = date + invoice.date = date + return invoice - def _settle_agent(self, agent=None, period=None, date=None): - vals = { - "date_to": ( - fields.Datetime.from_string(fields.Datetime.now()) - + dateutil.relativedelta.relativedelta(months=period) - ) - if period - else date, - } - if agent: - vals["agent_ids"] = [(4, agent.id)] + 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 _create_invoice_and_settle(self, agent, commission, period): - invoices = self._create_invoice(agent, commission) - invoices.invoice_line_ids.agent_ids._compute_amount() - invoices.action_post() - self._settle_agent(agent, period) - return invoices + 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_full(self, agent, commission, period, initial_count): - invoice = self._create_invoice_and_settle(agent, commission, period) + 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( @@ -192,45 +114,43 @@ def _check_full(self, agent, commission, period, initial_count): ) 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(agent, period) + self._settle_agent_invoice(agent, period) settlements = self.settle_model.search([("state", "=", "settled")]) self.assertTrue(settlements) - inv_line = invoice.mapped("invoice_line_ids")[0] + inv_line = invoice.invoice_line_ids[0] self.assertTrue(inv_line.any_settled) with self.assertRaises(ValidationError): inv_line.agent_ids.amount = 5 - settlements.make_invoices(self.journal, self.commission_product) - for settlement in settlements: - self.assertEqual(settlement.state, "invoiced") - with self.assertRaises(UserError): - settlements.action_cancel() - with self.assertRaises(UserError): - settlements.unlink() - return settlements + return self._check_settlements(agent, commission, settlements) - def test_invoice_commission_gross_amount_payment(self): - settlements = self._check_full( + def test_account_commission_gross_amount_payment(self): + self._check_invoice_thru_settle( self.env.ref("commission.res_partner_pritesh_agent"), self.commission_section_paid, 1, 0, ) - # Check report print - It shouldn't fail - self.env.ref("commission.action_report_settlement")._render_qweb_html( - settlements[0].ids - ) - def test_invoice_commission_gross_amount_payment_annual(self): - self._check_full(self.agent_annual, self.commission_section_paid, 12, 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_invoice_commission_gross_amount_payment_semi(self): + def test_account_commission_gross_amount_payment_semi(self): self.product.list_price = 15100 # for testing specific commission section - self._check_full(self.agent_semi, self.commission_section_invoice, 6, 1) + self._check_invoice_thru_settle( + self.agent_semi, self.commission_section_invoice, 6, 1 + ) def test_account_commission_gross_amount_invoice(self): - self._create_invoice_and_settle( + self._process_invoice_and_settle( self.agent_quaterly, self.env.ref("commission.demo_commission"), 1, @@ -244,18 +164,6 @@ def test_account_commission_gross_amount_invoice(self): "Settlements need to be in Invoiced State.", ) - def test_wrong_section(self): - with self.assertRaises(ValidationError): - self.commission_model.create( - { - "name": "Section commission - Invoice Based", - "commission_type": "section", - "section_ids": [ - (0, 0, {"amount_from": 5, "amount_to": 1, "percent": 20.0}) - ], - } - ) - def test_commission_status(self): # Make sure user is in English self.env.user.lang = "en_US" @@ -311,11 +219,6 @@ def test_supplier_invoice(self): invoice = move_form.save() self.assertFalse(invoice.invoice_line_ids.agent_ids) - def _check_propagation(self, agent): - self.assertTrue(agent) - self.assertTrue(agent.commission_id, self.commission_net_invoice) - self.assertTrue(agent.agent_id, self.agent_monthly) - def test_commission_propagation(self): """Test propagation of agents from partner to invoice.""" self.partner.agent_ids = [(4, self.agent_monthly.id)] @@ -329,20 +232,21 @@ def test_commission_propagation(self): line_form.quantity = 1 invoice = move_form.save() agent = invoice.invoice_line_ids.agent_ids - self._check_propagation(agent) + 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() - self._check_propagation(invoice.invoice_line_ids.agent_ids) + 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._create_invoice_and_settle(agent, commission, 1) + 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") @@ -360,7 +264,7 @@ def test_negative_settlements(self): ) refund.invoice_line_ids.agent_ids._compute_amount() refund.action_post() - self._settle_agent(agent, 1) + 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) @@ -380,7 +284,7 @@ def test_negative_settlements(self): self.assertEqual(second_settlement.state, "except_invoice") commission_invoices.unlink() settlements.unlink() - self._settle_agent(False, 1) # agent=False for testing default + 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() @@ -402,7 +306,7 @@ def test_negative_settlements_join_invoice(self): self.product.write({"list_price": 1000}) agent = self.agent_monthly commission = self.commission_net_invoice - invoice = self._create_invoice_and_settle(agent, commission, 1) + 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") @@ -414,13 +318,12 @@ def test_negative_settlements_join_invoice(self): } ], ) - refund.flush() self.assertEqual( invoice.invoice_line_ids.agent_ids.agent_id, refund.invoice_line_ids.agent_ids.agent_id, ) refund.action_post() - self._settle_agent(agent, 1) + 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) @@ -435,21 +338,36 @@ def test_negative_settlements_join_invoice(self): self.assertEqual(commission_invoice.move_type, "in_invoice") self.assertAlmostEqual(commission_invoice.amount_total, 0, places=2) - def test_res_partner_agent_propagation(self): - partner = self.env["res.partner"].create( - { - "name": "Test partner", - "agent_ids": [(4, self.agent_monthly.id), (4, self.agent_quaterly.id)], - } - ) - # Create - child = self.env["res.partner"].create( - {"name": "Test child", "parent_id": partner.id} + 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(set(child.agent_ids.ids), set(partner.agent_ids.ids)) - # Write - partner.agent_ids = [(4, self.agent_monthly.id)] - self.assertEqual(set(child.agent_ids.ids), set(partner.agent_ids.ids)) + 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 @@ -466,7 +384,7 @@ def test_biweekly(self): invoice3.invoice_date = "2022-01-31" invoice3.date = "2022-01-31" invoice3.action_post() - self._settle_agent(agent=self.agent_biweekly, date="2022-02-01") + self._settle_agent_invoice(agent=self.agent_biweekly, date="2022-02-01") settlements = self.settle_model.search( [("agent_id", "=", self.agent_biweekly.id)] ) diff --git a/account_commission/views/commission_views.xml b/account_commission/views/commission_views.xml new file mode 100644 index 000000000..956ebe4c1 --- /dev/null +++ b/account_commission/views/commission_views.xml @@ -0,0 +1,13 @@ + + + + commissions form + commission + + + + + + + + diff --git a/account_commission/wizard/__init__.py b/account_commission/wizards/__init__.py similarity index 100% rename from account_commission/wizard/__init__.py rename to account_commission/wizards/__init__.py diff --git a/account_commission/wizards/wizard_settle.py b/account_commission/wizards/wizard_settle.py new file mode 100644 index 000000000..af4a68e7b --- /dev/null +++ b/account_commission/wizards/wizard_settle.py @@ -0,0 +1,30 @@ +# Copyright 2014-2020 Tecnativa - Pedro M. Baeza +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class CommissionMakeSettle(models.TransientModel): + _inherit = "commission.make.settle" + + settlement_type = fields.Selection( + selection_add=[("invoice", "Invoice")], + ondelete={"invoice": "cascade"}, + ) + + def _get_settlement_line_date(self, line): + if self.settlement_type == "invoice": + return line.invoice_date + return super()._get_settlement_line_date + + def _get_agent_lines(self, agent, date_to_agent): + if self.settlement_type == "invoice": + return self.env["account.invoice.line.agent"].search( + [ + ("invoice_date", "<", date_to_agent), + ("agent_id", "=", agent.id), + ("settled", "=", False), + ], + order="invoice_date", + ) + return super()._get_agent_lines diff --git a/commission/__init__.py b/commission/__init__.py index 9b4296142..aee8895e7 100644 --- a/commission/__init__.py +++ b/commission/__init__.py @@ -1,2 +1,2 @@ from . import models -from . import wizard +from . import wizards diff --git a/commission/__manifest__.py b/commission/__manifest__.py index 569799425..d3ca87e34 100644 --- a/commission/__manifest__.py +++ b/commission/__manifest__.py @@ -4,21 +4,23 @@ "author": "Tecnativa," "Odoo Community Association (OCA)", "category": "Invoicing", "license": "AGPL-3", - "depends": ["product", "account"], + "depends": ["account"], "website": "https://github.com/OCA/commission", "maintainers": ["pedrobaeza"], "data": [ "security/ir.model.access.csv", + "data/menuitem_data.xml", "views/account_move_view.xml", "security/commission_security.xml", - "views/commission_view.xml", - "views/commission_settlement.xml", - "views/commission_mixin_view.xml", - "views/product_template_view.xml", - "views/res_partner_view.xml", + "views/commission_views.xml", + "views/commission_settlement_views.xml", + "views/commission_mixin_views.xml", + "views/product_template_views.xml", + "views/res_partner_views.xml", "views/commission_settlement_report.xml", "views/report_settlement_templates.xml", - "wizard/wizard_invoice.xml", + "wizards/wizard_invoice.xml", + "wizards/wizard_settle.xml", ], "demo": ["demo/commission_and_agent_demo.xml"], "installable": True, diff --git a/commission/data/menuitem_data.xml b/commission/data/menuitem_data.xml new file mode 100644 index 000000000..316bec769 --- /dev/null +++ b/commission/data/menuitem_data.xml @@ -0,0 +1,19 @@ + + + + + + + diff --git a/commission/demo/commission_and_agent_demo.xml b/commission/demo/commission_and_agent_demo.xml index 1e63144d0..e377f87e3 100644 --- a/commission/demo/commission_and_agent_demo.xml +++ b/commission/demo/commission_and_agent_demo.xml @@ -11,7 +11,7 @@ paid - + Pritesh Sales Agent Ahmedabad 380007 @@ -20,7 +20,7 @@ - + Eiffel pvt ltd Ahmedabad 380007 @@ -29,7 +29,7 @@ - + Tiny Belgium Belgium 2457 diff --git a/commission/models/commission.py b/commission/models/commission.py index e06bee294..658ac3ecd 100644 --- a/commission/models/commission.py +++ b/commission/models/commission.py @@ -1,3 +1,5 @@ +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + from odoo import _, api, exceptions, fields, models @@ -19,12 +21,6 @@ class Commission(models.Model): inverse_name="commission_id", ) active = fields.Boolean(default=True) - invoice_state = fields.Selection( - [("open", "Invoice Based"), ("paid", "Payment Based")], - string="Invoice Status", - required=True, - default="open", - ) amount_base_type = fields.Selection( selection=[("gross_amount", "Gross Amount"), ("net_amount", "Net Amount")], string="Base", diff --git a/commission/models/settlement.py b/commission/models/settlement.py index 1acd5e24d..09390b18b 100644 --- a/commission/models/settlement.py +++ b/commission/models/settlement.py @@ -19,12 +19,11 @@ def _default_currency(self): agent_id = fields.Many2one( comodel_name="res.partner", domain="[('agent', '=', True)]" ) - settlement_type = fields.Selection( - selection=[("invoice", "Invoice")], - default="invoice", + agent_type = fields.Selection(related="agent_id.agent_type") + settlement_type = fields.Char( readonly=True, + help="e.g. 'invoice'. A technical field to control the view presentation.", ) - agent_type = fields.Selection(related="agent_id.agent_type") line_ids = fields.One2many( comodel_name="commission.settlement.line", inverse_name="settlement_id", diff --git a/commission/readme/CONTRIBUTORS.rst b/commission/readme/CONTRIBUTORS.rst index 9c0ce3b25..f56cd6749 100644 --- a/commission/readme/CONTRIBUTORS.rst +++ b/commission/readme/CONTRIBUTORS.rst @@ -7,6 +7,7 @@ * Oihane Crucelaegui * Nicola Malcontenti * Aitor Bouzas +* Aung Ko Ko Lin * `Tecnativa `__: diff --git a/commission/security/ir.model.access.csv b/commission/security/ir.model.access.csv index 479b94c79..e33099b90 100644 --- a/commission/security/ir.model.access.csv +++ b/commission/security/ir.model.access.csv @@ -4,6 +4,7 @@ access_commission,access_commission,model_commission,account.group_account_invoi access_commission_user,access_commission_user,model_commission,base.group_user,1,0,0,0 access_commission_section_manager,access_commission_section_manager,model_commission_section,account.group_account_manager,1,1,1,1 access_commission_section_user,access_commission_section_user,model_commission_section,base.group_user,1,0,0,0 +access_commission_make_settle,access_commission_make_settle,model_commission_make_settle,account.group_account_invoice,1,1,1,0 access_commission_settlement_manager,access_commission_settlement_manager,model_commission_settlement,account.group_account_manager,1,1,1,1 access_commission_settlement_user,access_commission_settlement_user,model_commission_settlement,account.group_account_invoice,1,0,0,0 access_commission_settlement_line_manager,access_commission_settlement_line_manager,model_commission_settlement_line,account.group_account_manager,1,1,1,1 diff --git a/commission/tests/test_commission.py b/commission/tests/test_commission.py index a756ac27e..d5f773cde 100644 --- a/commission/tests/test_commission.py +++ b/commission/tests/test_commission.py @@ -1,22 +1,67 @@ +# 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 UserError, ValidationError +from odoo.tests import tagged from odoo.tests.common import TransactionCase +@tagged("post_install", "-at_install") class TestCommission(TransactionCase): @classmethod def setUpClass(cls): super().setUpClass() cls.commission_model = cls.env["commission"] - cls.res_partner_model = cls.env["res.partner"] - cls.partner = cls.env.ref("base.res_partner_2") - cls.partner.write({"agent": False}) cls.commission_net_paid = cls.commission_model.create( { "name": "20% fixed commission (Net amount) - Payment Based", "fix_qty": 20.0, - "invoice_state": "paid", "amount_base_type": "net_amount", } ) + cls.commission_section_paid = cls.commission_model.create( + { + "name": "Section commission - Payment Based", + "commission_type": "section", + "section_ids": [ + (0, 0, {"amount_from": 1.0, "amount_to": 100.0, "percent": 10.0}) + ], + "amount_base_type": "net_amount", + } + ) + cls.commission_section_invoice = cls.commission_model.create( + { + "name": "Section commission - Invoice Based", + "commission_type": "section", + "section_ids": [ + ( + 0, + 0, + { + "amount_from": 15000.0, + "amount_to": 16000.0, + "percent": 20.0, + }, + ) + ], + } + ) + cls.company = cls.env.ref("base.main_company") + cls.res_partner_model = cls.env["res.partner"] + cls.partner = cls.env.ref("base.res_partner_2") + cls.partner.write({"agent": False}) + cls.settle_model = cls.env["commission.settlement"] + cls.make_settle_model = cls.env["commission.make.settle"] + cls.commission_product = cls.env["product.product"].create( + {"name": "Commission test product", "type": "service"} + ) + cls.journal = cls.env["account.journal"].search( + [("type", "=", "purchase")], limit=1 + ) cls.agent_monthly = cls.res_partner_model.create( { "name": "Test Agent - Monthly", @@ -26,10 +71,123 @@ def setUpClass(cls): "commission_id": cls.commission_net_paid.id, } ) + cls.agent_quaterly = cls.res_partner_model.create( + { + "name": "Test Agent - Quaterly", + "agent": True, + "settlement": "quaterly", + "lang": "en_US", + "commission_id": cls.commission_section_invoice.id, + } + ) + cls.agent_semi = cls.res_partner_model.create( + { + "name": "Test Agent - Semi-annual", + "agent": True, + "settlement": "semi", + "lang": "en_US", + } + ) + cls.agent_annual = cls.res_partner_model.create( + { + "name": "Test Agent - Annual", + "agent": True, + "settlement": "annual", + "lang": "en_US", + } + ) - def test_partner_agent_assign(self): - self.partner.write({"agent_ids": [(4, self.agent_monthly.id)]}) - self.assertEqual( - self.partner.agent_ids[0].id, - self.agent_monthly.id, + # Expected to be used in inheriting modules. + def _get_make_settle_vals(self, agent=None, period=None, date=None): + vals = { + "date_to": ( + fields.Datetime.from_string(fields.Datetime.now()) + + relativedelta(months=period) + ) + if period + else date, + } + if agent: + vals["agent_ids"] = [(4, agent.id)] + return vals + + # Expected to be used in inheriting modules. + def _check_propagation(self, agent, commission_type, agent_partner): + self.assertTrue(agent) + self.assertTrue(agent.commission_id, commission_type) + self.assertTrue(agent.agent_id, agent_partner) + + def _create_settlement(self, agent, commission): + sett_from = self.make_settle_model._get_period_start(agent, fields.Date.today()) + sett_to = self.make_settle_model._get_next_period_date(agent, sett_from) + return self.settle_model.create( + { + "agent_id": agent.id, + "date_from": sett_from, + "date_to": sett_to, + "company_id": self.company.id, + "line_ids": [ + ( + 0, + 0, + { + "date": fields.Date.today(), + "agent_id": agent.id, + "commission_id": commission.id, + "settled_amount": 100.0, + "currency_id": self.company.currency_id.id, + }, + ) + ], + } + ) + + def _check_settlements(self, agent, commission, settlements=None): + if not settlements: + settlements = self._create_settlement(agent, commission) + settlements.make_invoices(self.journal, self.commission_product) + for settlement in settlements: + self.assertEqual(settlement.state, "invoiced") + with self.assertRaises(UserError): + settlements.action_cancel() + with self.assertRaises(UserError): + settlements.unlink() + return settlements + + def test_commission_gross_amount(self): + settlements = self._check_settlements( + self.env.ref("commission.res_partner_pritesh_sale_agent"), + self.commission_section_paid, + ) + # Check report print - It shouldn't fail + self.env.ref("commission.action_report_settlement")._render_qweb_html( + settlements[0].ids + ) + + def test_wrong_section(self): + with self.assertRaises(ValidationError): + self.commission_model.create( + { + "name": "Section commission - Invoice Based", + "commission_type": "section", + "section_ids": [ + (0, 0, {"amount_from": 5, "amount_to": 1, "percent": 20.0}) + ], + } + ) + + def test_res_partner_agent_propagation(self): + partner = self.env["res.partner"].create( + { + "name": "Test partner", + "agent_ids": [(4, self.agent_monthly.id), (4, self.agent_quaterly.id)], + } + ) + # Create + child = self.env["res.partner"].create( + {"name": "Test child", "parent_id": partner.id} ) + self.assertEqual(set(child.agent_ids.ids), set(partner.agent_ids.ids)) + # Write + partner.agent_ids = [(4, self.agent_annual.id)] + self.assertEqual(set(child.agent_ids.ids), set(partner.agent_ids.ids)) diff --git a/commission/views/commission_mixin_view.xml b/commission/views/commission_mixin_views.xml similarity index 100% rename from commission/views/commission_mixin_view.xml rename to commission/views/commission_mixin_views.xml diff --git a/commission/views/commission_settlement.xml b/commission/views/commission_settlement_views.xml similarity index 98% rename from commission/views/commission_settlement.xml rename to commission/views/commission_settlement_views.xml index 19f924191..1ac43d282 100644 --- a/commission/views/commission_settlement.xml +++ b/commission/views/commission_settlement_views.xml @@ -74,7 +74,7 @@ - + diff --git a/commission/views/commission_view.xml b/commission/views/commission_views.xml similarity index 87% rename from commission/views/commission_view.xml rename to commission/views/commission_views.xml index bf8ae2b26..d34b70d19 100644 --- a/commission/views/commission_view.xml +++ b/commission/views/commission_views.xml @@ -21,10 +21,9 @@ - + - @@ -57,17 +56,10 @@ commission form,tree - diff --git a/commission/views/product_template_view.xml b/commission/views/product_template_views.xml similarity index 100% rename from commission/views/product_template_view.xml rename to commission/views/product_template_views.xml diff --git a/commission/views/res_partner_view.xml b/commission/views/res_partner_views.xml similarity index 98% rename from commission/views/res_partner_view.xml rename to commission/views/res_partner_views.xml index 0bf8841f6..e4352de24 100644 --- a/commission/views/res_partner_view.xml +++ b/commission/views/res_partner_views.xml @@ -74,7 +74,7 @@ diff --git a/commission/wizard/__init__.py b/commission/wizards/__init__.py similarity index 50% rename from commission/wizard/__init__.py rename to commission/wizards/__init__.py index d1941376c..c2151d762 100644 --- a/commission/wizard/__init__.py +++ b/commission/wizards/__init__.py @@ -1 +1,2 @@ from . import wizard_invoice +from . import wizard_settle diff --git a/commission/wizard/wizard_invoice.py b/commission/wizards/wizard_invoice.py similarity index 100% rename from commission/wizard/wizard_invoice.py rename to commission/wizards/wizard_invoice.py diff --git a/commission/wizard/wizard_invoice.xml b/commission/wizards/wizard_invoice.xml similarity index 94% rename from commission/wizard/wizard_invoice.xml rename to commission/wizards/wizard_invoice.xml index cfe217133..95b36ca9d 100644 --- a/commission/wizard/wizard_invoice.xml +++ b/commission/wizards/wizard_invoice.xml @@ -38,7 +38,7 @@ - Create commission invoices + Create Commission Invoices commission.make.invoice form new @@ -46,7 +46,7 @@ diff --git a/account_commission/wizard/wizard_settle.py b/commission/wizards/wizard_settle.py similarity index 81% rename from account_commission/wizard/wizard_settle.py rename to commission/wizards/wizard_settle.py index 4bebc4bf5..605e56667 100644 --- a/account_commission/wizard/wizard_settle.py +++ b/commission/wizards/wizard_settle.py @@ -7,14 +7,15 @@ from odoo import _, fields, models -class InvoiceCommissionMakeSettle(models.TransientModel): - _name = "invoice.commission.make.settle" - _description = "Wizard for settling commissions in invoices" +class CommissionMakeSettle(models.TransientModel): + _name = "commission.make.settle" + _description = "Wizard for settling commissions" date_to = fields.Date("Up to", required=True, default=fields.Date.today()) agent_ids = fields.Many2many( comodel_name="res.partner", domain="[('agent', '=', True)]" ) + settlement_type = fields.Selection(selection=[], required=True) def _get_period_start(self, agent, date_to): if agent.settlement == "monthly": @@ -61,7 +62,6 @@ def _get_settlement(self, agent, company, sett_from, sett_to): ("date_to", "=", sett_to), ("company_id", "=", company.id), ("state", "=", "settled"), - ("settlement_type", "=", "invoice"), ], limit=1, ) @@ -72,12 +72,30 @@ def _prepare_settlement_vals(self, agent, company, sett_from, sett_to): "date_from": sett_from, "date_to": sett_to, "company_id": company.id, - "settlement_type": "invoice", + "settlement_type": self.settlement_type, } + def _get_settlement_line_date(self, line): + """Need to be extended according to settlement_type.""" + raise NotImplementedError() + + def _prepare_settlement_line_vals(self, settlement, line): + return { + "settlement_id": settlement.id, + "agent_line": [(6, 0, [line.id])], + "date": self._get_settlement_line_date(line), + "agent_id": line.agent_id.id, + "commission_id": line.commission_id.id, + "settled_amount": line.amount, + "currency_id": line.currency_id.id, + } + + def _get_agent_lines(self, date_to_agent): + """Need to be extended according to settlement_type.""" + raise NotImplementedError() + def action_settle(self): self.ensure_one() - agent_line_obj = self.env["account.invoice.line.agent"] settlement_obj = self.env["commission.settlement"] settlement_line_obj = self.env["commission.settlement.line"] settlement_ids = [] @@ -90,14 +108,7 @@ def action_settle(self): for agent in agents: date_to_agent = self._get_period_start(agent, date_to) # Get non settled invoices - agent_lines = agent_line_obj.search( - [ - ("invoice_date", "<", date_to_agent), - ("agent_id", "=", agent.id), - ("settled", "=", False), - ], - order="invoice_date", - ) + agent_lines = self._get_agent_lines(agent, date_to_agent) for company in agent_lines.mapped("company_id"): agent_lines_company = agent_lines.filtered( lambda r: r.object_id.company_id == company @@ -124,15 +135,7 @@ def action_settle(self): ) settlement_ids.append(settlement.id) settlement_line_obj.create( - { - "settlement_id": settlement.id, - "agent_line": [(6, 0, [line.id])], - "date": line.invoice_date, - "agent_id": line.agent_id.id, - "commission_id": line.commission_id.id, - "settled_amount": line.amount, - "currency_id": line.currency_id.id, - } + self._prepare_settlement_line_vals(settlement, line) ) # go to results if len(settlement_ids): diff --git a/account_commission/wizard/wizard_settle.xml b/commission/wizards/wizard_settle.xml similarity index 81% rename from account_commission/wizard/wizard_settle.xml rename to commission/wizards/wizard_settle.xml index 125cc9bb8..dc0983f7f 100644 --- a/account_commission/wizard/wizard_settle.xml +++ b/commission/wizards/wizard_settle.xml @@ -2,7 +2,7 @@ Select period to settle - invoice.commission.make.settle + commission.make.settle
@@ -13,6 +13,7 @@ +

(keep empty for making the settlement of all agents) @@ -38,15 +39,16 @@ - Settle commissions - invoice.commission.make.settle + Settle Commissions + commission.make.settle form new - +