mirror of
https://github.com/OCA/contract.git
synced 2025-02-13 17:57:24 +02:00
322 lines
13 KiB
Python
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)
|