Skip to content

Commit

Permalink
DO NOT MERGE: sale_commission_partial_settlement: major refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
aleuffre committed Dec 20, 2024
1 parent 62bea39 commit 0b5783b
Show file tree
Hide file tree
Showing 15 changed files with 459 additions and 160 deletions.
5 changes: 4 additions & 1 deletion sale_commission_partial_settlement/__manifest__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Copyright 2023 Nextev
{
"name": "Sales commissions based on paid amount",
"version": "14.0.1.2.1",
"version": "14.0.2.0.0",
"author": "Nextev Srl," "Ooops," "Odoo Community Association (OCA)",
"maintainers": ["aleuffre", "renda-dev", "PicchiSeba"],
"category": "Sales Management",
Expand All @@ -10,7 +10,10 @@
"website": "https://github.com/OCA/commission",
"data": [
"security/ir.model.access.csv",
"views/account_invoice_line_agent_views.xml",
"views/account_invoice_line_agent_partial_views.xml",
"views/res_config_settings_view.xml",
"views/sale_commission_settlement_line_partial_views.xml",
"views/sale_commission_settlement_view.xml",
"views/sale_commission_view.xml",
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@ def recompute_partial_commission_settled(env):
"""
Recompute field "partial_commission_settled"
of model "account.partial.reconcile"
Removed in future versions of the module
"""
env["account.partial.reconcile"].search([])._compute_partial_commission_settled()
partial_reconcile = env["account.partial.reconcile"]
if getattr(partial_reconcile, "_compute_partial_commission_settled", False):
partial_reconcile.search([])._compute_partial_commission_settled()


@openupgrade.migrate()
Expand Down
1 change: 1 addition & 0 deletions sale_commission_partial_settlement/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@
from . import sale_commission
from . import sale_commission_settlement
from . import sale_commission_settlement_line
from . import sale_commission_settlement_line_partial
Original file line number Diff line number Diff line change
@@ -1,36 +1,38 @@
# Copyright 2023 Nextev
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from odoo import api, 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"

payment_amount_type = fields.Selection(related="commission_id.payment_amount_type")
partial_settled = fields.Monetary(
string="Partial Commission Amount Settled",
compute="_compute_partial_settled",
store=True,
)
is_fully_settled = fields.Boolean(compute="_compute_is_fully_settled", store=True)
invoice_line_agent_partial_ids = fields.One2many(
"account.invoice.line.agent.partial", "invoice_line_agent_id"
"account.invoice.line.agent.partial",
"invoice_line_agent_id",
compute="_compute_invoice_line_agent_partial_ids",
store=True,
)
commission_settlement_line_partial_ids = fields.One2many(
"sale.commission.settlement.line.partial",
compute="_compute_commission_settlement_line_partial_ids",
)

@api.depends(
"invoice_line_agent_partial_ids.amount",
"invoice_line_agent_partial_ids.agent_line.settlement_id.state",
"invoice_line_agent_partial_ids.settled_amount",
)
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 any(
settlement.state != "cancel"
for settlement in ailap.mapped("agent_line.settlement_id")
)
ailap.settled_amount for ailap in rec.invoice_line_agent_partial_ids
)

@api.depends(
Expand All @@ -50,62 +52,47 @@ def _compute_is_fully_settled(self):
== 0
)

def _partial_commissions(self, date_payment_to):
@api.depends(
"commission_id.payment_amount_type",
"object_id.move_id.move_type",
"object_id.move_id.line_ids.amount_residual",
)
def _compute_invoice_line_agent_partial_ids(self):
"""
This method iterates through agent invoice lines and calculates
partial commissions based on the payment amount.
If the partial payment amount is greater than the invoice line
amount, it fully settles the corresponding agent line.
Otherwise, it calculates the partial commission proportionally to
the amount paid, invoice amount and total commissions.
Create an account.invoice.line.agent.partial for each
payment term move line
"""
partial_lines_to_settle = []
partial_payment_remaining = {}
for line in self:
line_total_amount = line.amount
for (
partial,
amount,
counterpart_line,
) in line.invoice_id._get_reconciled_invoices_partials():
if partial.partial_commission_settled:
continue
elif date_payment_to and date_payment_to < counterpart_line.date:
break
if partial.id in partial_payment_remaining:
payment_amount = partial_payment_remaining[partial.id][
"remaining_amount"
]
else:
payment_amount = amount
partial_payment_remaining[partial.id] = {"remaining_amount": amount}
if line.object_id.price_total <= payment_amount:
partial_lines_to_settle.append(
{
"invoice_line_agent_id": line.id,
"currency_id": line.currency_id.id,
"amount": line_total_amount,
"account_partial_reconcile_id": partial.id,
}
for rec in self:
ailap_model = rec.invoice_line_agent_partial_ids.browse()
if rec.commission_id.payment_amount_type != "paid":
rec.invoice_line_agent_partial_ids = False
continue
pay_term_lines = rec.object_id.move_id.line_ids.filtered(
lambda line: line.account_internal_type in ("receivable", "payable")
)
forecast_lines = rec.invoice_line_agent_partial_ids.mapped("move_line_id")
for move_line in pay_term_lines:
if move_line not in forecast_lines:
ailap_model.create(
{"move_line_id": move_line.id, "invoice_line_agent_id": rec.id}
)
partial_payment_remaining[partial.id] = {
"remaining_amount": amount - line.object_id.price_total
}
break

paid_in_proportion = payment_amount / line.invoice_id.amount_total
partial_commission = (
line.invoice_id.commission_total * paid_in_proportion
)
partial_lines_to_settle.append(
{
"invoice_line_agent_id": line.id,
"currency_id": line.currency_id.id,
"amount": partial_commission,
"account_partial_reconcile_id": partial.id,
}
)
partial_agent_lines = self.env["account.invoice.line.agent.partial"].create(
partial_lines_to_settle
def _compute_commission_settlement_line_partial_ids(self):
for rec in self:
rec.commission_settlement_line_partial_ids = (
rec.invoice_line_agent_partial_ids.settlement_line_partial_ids
)

def action_see_partial_commissions(self):
view = self.env.ref(
"sale_commission_partial_settlement.account_invoice_line_agent_form_partial_only"
)
return partial_agent_lines
return {
"name": _("Partial Commissions"),
"type": "ir.actions.act_window",
"view_mode": "form",
"res_model": self._name,
"views": [(view.id, "form")],
"target": "new",
"res_id": self.id,
}
Original file line number Diff line number Diff line change
@@ -1,28 +1,115 @@
# 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


class AccountInvoiceLineAgentPartial(models.Model):
_name = "account.invoice.line.agent.partial"
_description = "Partial agent commissions"
_description = "Partial agent commissions. "
"Tracks the expected commissions."

move_line_id = fields.Many2one(
"account.move.line",
required=True, # TODO: migration? Probably cannot enforce
ondelete="cascade",
)
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,
settlement_line_partial_ids = fields.One2many(
"sale.commission.settlement.line.partial",
"invoice_agent_partial_id",
compute="_compute_settlement_line_partial_ids",
store=True,
)
account_partial_reconcile_id = fields.Many2one("account.partial.reconcile")
account_partial_reconcile_id = fields.Many2one(
"account.partial.reconcile"
) # TODO: Remove
amount = fields.Monetary(
compute="_compute_amount",
store=True,
string="Commission Amount",
)
currency_id = fields.Many2one(
related="invoice_line_agent_id.currency_id",
)
settled_amount = fields.Monetary(
compute="_compute_settled_amount",
store=True,
)
is_settled = fields.Boolean(
compute="_compute_settled_amount", store=True, string="Fully settled"
)

move_id = fields.Many2one(related="move_line_id.move_id", string="Invoice")
date_maturity = fields.Date(
related="move_line_id.date_maturity",
store=True,
)
invoice_line_id = fields.Many2one(
related="invoice_line_agent_id.object_id", string="Invoice Line"
)
agent_id = fields.Many2one(
related="invoice_line_agent_id.agent_id",
store=True,
)
invoice_date = fields.Date(
related="invoice_line_agent_id.invoice_date",
store=True,
)

@api.depends(
"settlement_line_partial_ids.amount",
"settlement_line_partial_ids.is_settled",
)
def _compute_settled_amount(self):
for rec in self:
# TODO: handle different currencies
rec.settled_amount = sum(
x.amount for x in rec.settlement_line_partial_ids if x.is_settled
)
rec.is_settled = rec.currency_id.is_zero(rec.settled_amount - rec.amount)

@api.depends(
"move_line_id.balance",
"move_line_id.move_id.amount_total",
"invoice_line_agent_id.amount",
)
def _compute_amount(self):
for rec in self:
rec.amount = (
rec.move_line_id.balance
* rec.invoice_line_agent_id.amount
/ rec.move_line_id.move_id.amount_total
)

@api.depends(
"move_line_id.matched_debit_ids",
"move_line_id.matched_credit_ids",
)
def _compute_settlement_line_partial_ids(self):
"""
Cf. method _get_reconciled_invoices_partials
in odoo.addons.account.models.account_move.AccountMove.
"""
for rec in self:
pay_term_line = rec.move_line_id
matched_partials = (
pay_term_line.matched_debit_ids + pay_term_line.matched_credit_ids
)
if not matched_partials:
continue
existing_partial_settlements = rec.settlement_line_partial_ids
existing_partials = existing_partial_settlements.mapped(
"partial_reconcile_id"
)

for partial in matched_partials:
if partial not in existing_partials:
existing_partial_settlements.create(
{
"partial_reconcile_id": partial.id,
"invoice_agent_partial_id": rec.id,
}
)
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
from odoo import api, fields, models
from odoo import fields, models


class AccountPartialReconcile(models.Model):
_inherit = "account.partial.reconcile"

# 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
)
) # TODO: Remove?
# partial_commission_settled = fields.Boolean(
# compute="_compute_partial_commission_settled", store=True
# )

@api.depends(
"account_invoice_line_agent_partial_ids",
"account_invoice_line_agent_partial_ids.agent_line.settlement_id.state",
)
def _compute_partial_commission_settled(self):
for rec in self:
rec.partial_commission_settled = any(
settlement.state != "cancel"
for settlement in rec.mapped(
"account_invoice_line_agent_partial_ids.agent_line.settlement_id"
)
)
# APR can't tell if every agent was settled!
# @api.depends(
# "account_invoice_line_agent_partial_ids",
# "account_invoice_line_agent_partial_ids.agent_line.settlement_id.state",
# )
# def _compute_partial_commission_settled(self):
# for rec in self:
# rec.partial_commission_settled = any(
# settlement.state != "cancel"
# for settlement in rec.mapped(
# "account_invoice_line_agent_partial_ids.agent_line.settlement_id"
# )
# )
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,6 @@ class SaleCommissionSettlement(models.Model):
help="The payment date used to create the settlement",
)

def unlink(self):
self.mapped("line_ids.agent_line_partial_ids").unlink()
return super().unlink()
# def unlink(self):
# self.mapped("line_ids.agent_line_partial_ids").unlink()
# return super().unlink()
Loading

0 comments on commit 0b5783b

Please sign in to comment.