Files
contract/agreement_rebate/wizards/settlement_create.py

322 lines
13 KiB
Python

# Copyright 2020 Tecnativa - Carlos Dauden
# Copyright 2020 Tecnativa - Sergio Teruel
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from collections import defaultdict
from odoo import api, fields, models
from odoo.osv import expression
from odoo.tools import safe_eval
class AgreementSettlementCreateWiz(models.TransientModel):
_name = "agreement.settlement.create.wiz"
_description = "Agreement settlement create wizard"
date = fields.Date(default=fields.Date.today)
date_from = fields.Date(string="From")
date_to = fields.Date(string="To", required=True)
domain = fields.Selection("_domain_selection", string="Domain", default="sale")
journal_ids = fields.Many2many(comodel_name="account.journal", string="Journals",)
agreement_type_ids = fields.Many2many(
comodel_name="agreement.type", string="Agreement types",
)
agreement_ids = fields.Many2many(comodel_name="agreement", string="Agreements",)
discard_settled_agreement = fields.Boolean(
string="Discard settled agreements",
default="True",
help="If checked, the agreements with settlements in selected period "
"will be discard",
)
@api.model
def _domain_selection(self):
return self.env["agreement"]._domain_selection()
def _prepare_agreement_domain(self):
domain = [
("rebate_type", "!=", False),
("agreement_type_id.is_rebate", "=", True),
]
settlement_domain = []
if self.date_from:
domain.extend(
["|", ("end_date", "=", False), ("end_date", ">=", self.date_from)]
)
settlement_domain.extend([("date_to", ">=", self.date_from)])
if self.date_to:
domain.extend([("start_date", "<=", self.date_to)])
settlement_domain.extend([("date_to", "<=", self.date_to)])
if self.agreement_ids:
domain.extend([("id", "in", self.agreement_ids.ids)])
settlement_domain.extend(
[("line_ids.agreement_id", "in", self.agreement_ids.ids)]
)
elif self.agreement_type_ids:
domain.extend([("agreement_type_id", "in", self.agreement_type_ids.ids)])
settlement_domain.extend(
[
(
"line_ids.agreement_id.agreement_type_id",
"in",
self.agreement_type_ids.ids,
)
]
)
else:
domain.extend([("agreement_type_id.domain", "=", self.domain)])
if self.discard_settled_agreement:
settlements = self._get_existing_settlement(settlement_domain)
if settlements:
domain.extend(
[("id", "not in", settlements.mapped("line_ids.agreement_id").ids)]
)
return domain
def _get_existing_settlement(self, domain):
return self.env["agreement.rebate.settlement"].search(domain)
def _get_target_model(self):
return self.env["account.invoice.report"]
def _prepare_target_domain(self):
domain = [
("state", "not in", ["draft", "cancel"]),
]
if self.journal_ids:
domain.extend([("journal_id", "in", self.journal_ids.ids)])
else:
domain.extend([("journal_id.type", "=", self.domain)])
if self.date_from:
domain.extend(
[("invoice_date", ">=", fields.Date.to_string(self.date_from),)]
)
if self.date_to:
domain.extend(
[("invoice_date", "<=", fields.Date.to_string(self.date_to),)]
)
return domain
def _target_line_domain(self, agreement_domain, agreement, line=False):
domain = agreement_domain.copy()
if agreement.start_date:
domain.append(
("invoice_date", ">=", fields.Date.to_string(agreement.start_date),)
)
if agreement.end_date:
domain.append(
("invoice_date", "<=", fields.Date.to_string(agreement.end_date),)
)
if line:
domain += safe_eval(line.rebate_domain)
elif agreement.rebate_line_ids:
domain = expression.AND(
[
domain,
expression.OR(
[safe_eval(x.rebate_domain) for x in agreement.rebate_line_ids]
),
]
)
return domain
def get_agregate_fields(self):
return [
"price_subtotal",
]
def _get_amount_field(self):
return "price_subtotal"
def _prepare_settlement_line(
self, domain, group, agreement, line=False, section=False
):
amount = (
group[self._get_amount_field()] or 0.0 + agreement.additional_consumption
)
amount_section = 0.0
vals = {
"agreement_id": agreement.id,
"partner_id": group["partner_id"][0]
if "partner_id" in group
else agreement.partner_id.id,
}
if agreement.rebate_type == "line":
rebate = amount * line.rebate_discount / 100
vals.update({"rebate_line_id": line.id, "percent": line.rebate_discount})
elif agreement.rebate_type == "section_prorated":
if amount >= section.amount_to:
amount_section = section.amount_to
elif amount >= section.amount_from:
amount_section = amount - section.amount_from
rebate = amount_section * section.rebate_discount / 100
vals.update(
{
"rebate_section_id": section.id,
"amount_from": section.amount_from,
"amount_to": section.amount_to,
"percent": section.rebate_discount,
}
)
elif agreement.rebate_type == "global":
rebate = amount * agreement.rebate_discount / 100
vals.update({"percent": agreement.rebate_discount})
elif agreement.rebate_type == "section_total":
section = agreement.rebate_section_ids.filtered(
lambda s: s.amount_from <= amount <= s.amount_to
)
rebate = amount * section.rebate_discount / 100
vals.update(
{
"percent": section.rebate_discount,
"amount_from": section.amount_from,
"amount_to": section.amount_to,
}
)
vals.update(
{
"target_domain": domain,
"amount_invoiced": agreement.company_id.currency_id.round(
amount_section or amount
),
"amount_rebate": agreement.company_id.currency_id.round(rebate),
}
)
return vals
def _get_rebate_discount(self, agreement, amount):
if agreement.rebate_type == "global":
return agreement.rebate_discount
if agreement.rebate_type == "section_total":
section = agreement.rebate_section_ids.filtered(
lambda s: s.amount_from <= amount <= s.amount_to
)
return section.rebate_discount
def _partner_domain(self, agreement):
return [
("partner_id", "child_of", agreement.partner_id.ids),
]
def get_settlement_key(self, agreement):
return agreement
def action_create_settlement(self):
self.ensure_one()
Agreement = self.env["agreement"]
target_model = self._get_target_model()
orig_domain = self._prepare_target_domain()
settlement_dic = defaultdict(lambda: {"lines": []})
agreements = Agreement.search(self._prepare_agreement_domain())
for agreement in agreements:
key = self.get_settlement_key(agreement)
if key not in settlement_dic:
settlement_dic[key]["amount_rebate"] = 0.0
settlement_dic[key]["amount_invoiced"] = 0.0
settlement_dic[key]["partner_id"] = agreement.partner_id.id
agreement_domain = orig_domain + self._partner_domain(agreement)
if agreement.rebate_type == "line":
if not agreement.rebate_line_ids:
continue
for line in agreement.rebate_line_ids:
domain = self._target_line_domain(
agreement_domain, agreement, line=line
)
groups = target_model.read_group(
domain,
self.get_agregate_fields(),
self._settlement_line_break_fields(),
lazy=False,
)
if (
not groups
or not groups[0]["__count"]
and not agreement.additional_consumption
):
continue
for group in groups:
vals = self._prepare_settlement_line(
domain, group, agreement, line=line
)
settlement_dic[key]["amount_rebate"] += vals["amount_rebate"]
settlement_dic[key]["amount_invoiced"] += vals[
"amount_invoiced"
]
settlement_dic[key]["lines"].append((0, 0, vals))
elif agreement.rebate_type == "section_prorated":
domain = self._target_line_domain(agreement_domain, agreement)
groups = target_model.read_group(
domain,
self.get_agregate_fields(),
self._settlement_line_break_fields(),
lazy=False,
)
if (
not groups
or not groups[0]["__count"]
and not agreement.additional_consumption
):
continue
amount = groups and groups[0][self._get_amount_field()] or 0.0
for section in agreement.rebate_section_ids:
if amount < section.amount_to and amount < section.amount_from:
break
for group in groups:
vals = self._prepare_settlement_line(
domain, group, agreement, section=section
)
settlement_dic[key]["amount_rebate"] += vals["amount_rebate"]
settlement_dic[key]["lines"].append((0, 0, vals))
settlement_dic[key]["amount_invoiced"] += amount
else:
domain = self._target_line_domain(agreement_domain, agreement)
groups = target_model.read_group(
domain,
self.get_agregate_fields(),
self._settlement_line_break_fields(),
lazy=False,
)
if (
not groups
or not groups[0]["__count"]
and not agreement.additional_consumption
):
continue
for group in groups:
vals = self._prepare_settlement_line(domain, group, agreement)
settlement_dic[key]["lines"].append((0, 0, vals))
settlement_dic[key]["amount_rebate"] += vals["amount_rebate"]
settlement_dic[key]["amount_invoiced"] += vals["amount_invoiced"]
settlements = self._create_settlement(settlement_dic)
return settlements.action_show_settlement()
def _settlement_line_break_fields(self):
return ["partner_id"]
def _filter_settlement_lines(self, settlement_lines):
return [
line
for line in filter(lambda l: l[2]["amount_rebate"] != 0.0, settlement_lines)
]
def _prepare_settlement(self, settlement_lines):
lines = self._filter_settlement_lines(settlement_lines["lines"])
if not lines:
return {}
return {
"date": self.date,
"date_from": self.date_from,
"date_to": self.date_to,
"line_ids": lines,
"partner_id": settlement_lines["partner_id"],
"amount_rebate": settlement_lines["amount_rebate"],
"amount_invoiced": settlement_lines["amount_invoiced"],
}
def _create_settlement(self, settlements):
vals_list = []
for settlement_lines in settlements.values():
vals = self._prepare_settlement(settlement_lines)
if vals:
vals_list.append(vals)
return self.env["agreement.rebate.settlement"].create(vals_list)