From 251d2702982246ddfa6d72bee2eba5a5ea201e19 Mon Sep 17 00:00:00 2001 From: Alessandro Uffreduzzi Date: Tue, 12 Nov 2024 12:54:30 +0100 Subject: [PATCH] [FIX] sale_commission_partial_settlement: handle settlement deletion This commit fixes an issue where, if a settlement was deleted, any partial commission would not be recreated because it was still considered paid. Includes a small refactor of the files and deletion of some unused fields. --- .../__manifest__.py | 2 +- .../migrations/14.0.1.1.0/post-migrate.py | 47 ++++++++++++ .../models/__init__.py | 8 +- ..._move.py => account_invoice_line_agent.py} | 75 +++++++++---------- .../account_invoice_line_agent_partial.py | 28 +++++++ .../models/account_partial_reconcile.py | 17 ++++- .../models/sale_commission_settlement.py | 9 +++ ....py => sale_commission_settlement_line.py} | 4 + .../wizard/wizard_settle.py | 19 +++-- 9 files changed, 154 insertions(+), 55 deletions(-) create mode 100644 sale_commission_partial_settlement/migrations/14.0.1.1.0/post-migrate.py rename sale_commission_partial_settlement/models/{account_move.py => account_invoice_line_agent.py} (65%) create mode 100644 sale_commission_partial_settlement/models/account_invoice_line_agent_partial.py create mode 100644 sale_commission_partial_settlement/models/sale_commission_settlement.py rename sale_commission_partial_settlement/models/{settlement.py => sale_commission_settlement_line.py} (88%) diff --git a/sale_commission_partial_settlement/__manifest__.py b/sale_commission_partial_settlement/__manifest__.py index 1b98f3e68..eb5e8a989 100644 --- a/sale_commission_partial_settlement/__manifest__.py +++ b/sale_commission_partial_settlement/__manifest__.py @@ -1,7 +1,7 @@ # Copyright 2023 Nextev { "name": "Sales commissions based on paid amount", - "version": "14.0.1.0.1", + "version": "14.0.1.1.0", "author": "Nextev Srl," "Ooops," "Odoo Community Association (OCA)", "maintainers": ["aleuffre", "renda-dev", "PicchiSeba"], "category": "Sales Management", diff --git a/sale_commission_partial_settlement/migrations/14.0.1.1.0/post-migrate.py b/sale_commission_partial_settlement/migrations/14.0.1.1.0/post-migrate.py new file mode 100644 index 000000000..3e85b5ca4 --- /dev/null +++ b/sale_commission_partial_settlement/migrations/14.0.1.1.0/post-migrate.py @@ -0,0 +1,47 @@ +from openupgradelib import openupgrade + + +def unlink_orphan_agent_partials(env): + """ + Delete agent partials that are not linked + to a settlement line + e.g. because the settlement was deleted + but the ailap was left behind + """ + openupgrade.logged_query( + env.cr, + """ + SELECT id + FROM account_invoice_line_agent_partial + WHERE id not in + (SELECT agent_line_partial_id + FROM settlement_agent_line_partial_rel) + """, + ) + ids = [row[0] for row in env.cr.fetchall()] + env["account.invoice.line.agent.partial"].browse(ids).unlink() + + +def recompute_partial_settled(env): + env["account.invoice.line.agent"].search( + [("settled", "=", False), ("partial_settled", "!=", 0)] + )._compute_partial_settled() + + +def recompute_settled(env): + """ + Sometimes AILA were not marked as "settled" properly + for commissions of type "paid" + """ + env["account.invoice.line.agent"].search( + [ + ("commission_id.payment_amount_type", "=", "paid"), + ] + )._compute_settled() + + +@openupgrade.migrate() +def migrate(env, version): + unlink_orphan_agent_partials(env) + recompute_partial_settled(env) + recompute_settled(env) diff --git a/sale_commission_partial_settlement/models/__init__.py b/sale_commission_partial_settlement/models/__init__.py index 4e17db438..61873e96a 100644 --- a/sale_commission_partial_settlement/models/__init__.py +++ b/sale_commission_partial_settlement/models/__init__.py @@ -1,4 +1,6 @@ -from . import sale_commission -from . import account_move +from . import account_invoice_line_agent +from . import account_invoice_line_agent_partial from . import account_partial_reconcile -from . import settlement +from . import sale_commission +from . import sale_commission_settlement +from . import sale_commission_settlement_line diff --git a/sale_commission_partial_settlement/models/account_move.py b/sale_commission_partial_settlement/models/account_invoice_line_agent.py similarity index 65% rename from sale_commission_partial_settlement/models/account_move.py rename to sale_commission_partial_settlement/models/account_invoice_line_agent.py index a402c5ed3..bd460ed16 100644 --- a/sale_commission_partial_settlement/models/account_move.py +++ b/sale_commission_partial_settlement/models/account_invoice_line_agent.py @@ -1,24 +1,50 @@ # Copyright 2023 Nextev # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -from odoo import fields, models +from odoo import api, fields, models +from odoo.tools.float_utils import float_compare class AccountInvoiceLineAgent(models.Model): _inherit = "account.invoice.line.agent" - partial_settled = fields.Monetary(string="Partial Commission Amount Settled") + partial_settled = fields.Monetary( + string="Partial Commission Amount Settled", + compute="_compute_partial_settled", + store=True, + ) + invoice_line_agent_partial_ids = fields.One2many( + "account.invoice.line.agent.partial", "invoice_line_agent_id" + ) + @api.depends("commission_id.payment_amount_type", "amount", "partial_settled") def _compute_settled(self): filtered_lines = self.filtered( lambda x: x.commission_id.payment_amount_type != "paid" ) for line in self - filtered_lines: - if not line.mapped("agent_line.settlement_id"): - line.settled = False - + line.settled = line.amount > 0 and ( + float_compare( + line.partial_settled, + line.amount, + precision_rounding=line.currency_id.rounding, + ) + >= 0 # partial_settled >= line.amount + ) super(AccountInvoiceLineAgent, filtered_lines)._compute_settled() + @api.depends( + "invoice_line_agent_partial_ids.amount", + "invoice_line_agent_partial_ids.agent_line.settlement_id.state", + ) + def _compute_partial_settled(self): + for rec in self: + rec.partial_settled = sum( + ailap.amount + for ailap in rec.invoice_line_agent_partial_ids + if ailap.mapped("agent_line.settlement_id")[:1].state != "cancel" + ) + def _partial_commissions(self, date_payment_to): """ This method iterates through agent invoice lines and calculates @@ -30,7 +56,6 @@ def _partial_commissions(self, date_payment_to): """ partial_lines_to_settle = [] partial_payment_remaining = {} - lines_to_update = {} for line in self: line_total_amount = line.amount for ( @@ -55,12 +80,9 @@ def _partial_commissions(self, date_payment_to): "invoice_line_agent_id": line.id, "currency_id": line.currency_id.id, "amount": line_total_amount, + "account_partial_reconcile_id": partial.id, } ) - lines_to_update[line.id] = { - "partial_settled": line_total_amount, - "settled": True, - } partial_payment_remaining[partial.id] = { "remaining_amount": amount - line.object_id.price_total } @@ -75,39 +97,10 @@ def _partial_commissions(self, date_payment_to): "invoice_line_agent_id": line.id, "currency_id": line.currency_id.id, "amount": partial_commission, + "account_partial_reconcile_id": partial.id, } ) - if line.id in lines_to_update: - lines_to_update[line.id]["partial_settled"] += partial_commission - else: - lines_to_update[line.id] = {"partial_settled": partial_commission} - - if lines_to_update[line.id]["partial_settled"] >= line_total_amount: - lines_to_update[line.id].update({"settled": True}) - break - partial.partial_commission_settled = True partial_agent_lines = self.env["account.invoice.line.agent.partial"].create( partial_lines_to_settle ) - return partial_agent_lines, lines_to_update - - -class AccountInvoiceLineAgentPartial(models.Model): - _name = "account.invoice.line.agent.partial" - _description = "Partial agent commissions" - - invoice_line_agent_id = fields.Many2one("account.invoice.line.agent", required=True) - agent_line = fields.Many2many( - comodel_name="sale.commission.settlement.line", - relation="settlement_agent_line_partial_rel", - column1="agent_line_partial_id", - column2="settlement_id", - copy=False, - ) - amount = fields.Monetary( - string="Commission Amount", - ) - currency_id = fields.Many2one( - related="invoice_line_agent_id.currency_id", - ) - settled = fields.Boolean() + return partial_agent_lines diff --git a/sale_commission_partial_settlement/models/account_invoice_line_agent_partial.py b/sale_commission_partial_settlement/models/account_invoice_line_agent_partial.py new file mode 100644 index 000000000..181a7f8c9 --- /dev/null +++ b/sale_commission_partial_settlement/models/account_invoice_line_agent_partial.py @@ -0,0 +1,28 @@ +# Copyright 2023 Nextev +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class AccountInvoiceLineAgentPartial(models.Model): + _name = "account.invoice.line.agent.partial" + _description = "Partial agent commissions" + + invoice_line_agent_id = fields.Many2one( + "account.invoice.line.agent", required=True, ondelete="cascade" + ) + # logically a One2one + agent_line = fields.Many2many( + comodel_name="sale.commission.settlement.line", + relation="settlement_agent_line_partial_rel", + column1="agent_line_partial_id", + column2="settlement_id", + copy=False, + ) + account_partial_reconcile_id = fields.Many2one("account.partial.reconcile") + amount = fields.Monetary( + string="Commission Amount", + ) + currency_id = fields.Many2one( + related="invoice_line_agent_id.currency_id", + ) diff --git a/sale_commission_partial_settlement/models/account_partial_reconcile.py b/sale_commission_partial_settlement/models/account_partial_reconcile.py index 7c1714d4d..175b1af6a 100644 --- a/sale_commission_partial_settlement/models/account_partial_reconcile.py +++ b/sale_commission_partial_settlement/models/account_partial_reconcile.py @@ -1,7 +1,20 @@ -from odoo import fields, models +from odoo import api, fields, models class AccountPartialReconcile(models.Model): _inherit = "account.partial.reconcile" - partial_commission_settled = fields.Boolean() + # Logically a One2one + account_invoice_line_agent_partial_ids = fields.One2many( + "account.invoice.line.agent.partial", "account_partial_reconcile_id" + ) + partial_commission_settled = fields.Boolean( + compute="_compute_partial_commission_settled", store=True + ) + + @api.depends("account_invoice_line_agent_partial_ids") + def _compute_partial_commission_settled(self): + for rec in self: + rec.partial_commission_settled = bool( + rec.account_invoice_line_agent_partial_ids + ) diff --git a/sale_commission_partial_settlement/models/sale_commission_settlement.py b/sale_commission_partial_settlement/models/sale_commission_settlement.py new file mode 100644 index 000000000..1aa58e699 --- /dev/null +++ b/sale_commission_partial_settlement/models/sale_commission_settlement.py @@ -0,0 +1,9 @@ +from odoo import models + + +class Settlement(models.Model): + _inherit = "sale.commission.settlement" + + def unlink(self): + self.mapped("line_ids.agent_line_partial_ids").unlink() + return super().unlink() diff --git a/sale_commission_partial_settlement/models/settlement.py b/sale_commission_partial_settlement/models/sale_commission_settlement_line.py similarity index 88% rename from sale_commission_partial_settlement/models/settlement.py rename to sale_commission_partial_settlement/models/sale_commission_settlement_line.py index f25ba11e0..a5ebda029 100644 --- a/sale_commission_partial_settlement/models/settlement.py +++ b/sale_commission_partial_settlement/models/sale_commission_settlement_line.py @@ -24,3 +24,7 @@ def _compute_settled_amount(self): rec.settled_amount = rec.agent_line_partial_ids[:1].amount else: rec.settled_amount = rec.agent_line[:1].amount + + def unlink(self): + self.mapped("agent_line_partial_ids").unlink() + return super().unlink() diff --git a/sale_commission_partial_settlement/wizard/wizard_settle.py b/sale_commission_partial_settlement/wizard/wizard_settle.py index 5c271e5e1..44e4ddf06 100644 --- a/sale_commission_partial_settlement/wizard/wizard_settle.py +++ b/sale_commission_partial_settlement/wizard/wizard_settle.py @@ -32,14 +32,17 @@ def action_settle_partial(self): for agent in agents: date_to_agent = self._get_period_start(agent, date_to) main_agent_line = self.get_partial_agent_lines(agent, date_to_agent) - ( - partial_agent_lines, - agent_lines_to_update, - ) = main_agent_line._partial_commissions(self.date_payment_to) - for line_id in agent_lines_to_update: - self.env["account.invoice.line.agent"].browse(line_id).update( - agent_lines_to_update[line_id] - ) + # ( + # partial_agent_lines, + # agent_lines_to_update, + # ) = main_agent_line._partial_commissions(self.date_payment_to) + partial_agent_lines = main_agent_line._partial_commissions( + self.date_payment_to + ) + # for line_id in agent_lines_to_update: + # self.env["account.invoice.line.agent"].browse(line_id).update( + # agent_lines_to_update[line_id] + # ) for company in partial_agent_lines.mapped( "invoice_line_agent_id.company_id" ):