Skip to content
Snippets Groups Projects
contract.py 9.80 KiB
from datetime import datetime

from odoo import _, api, fields, models

from ..utils import (
    _CONTRACT_STATUS_VALUES,
    _SALE_ORDER_SERVICE_INVOICING_ACTION_VALUES,
    get_existing_open_contract,
    raise_existing_same_open_contract_error,
)

_CLOSING_ACTION_VALUES = _SALE_ORDER_SERVICE_INVOICING_ACTION_VALUES + [
    ("close", _("Close"))
]


class ContractContract(models.Model):
    _inherit = "contract.contract"
    _order = "id desc"

    community_company_id = fields.Many2one(
        "res.company",
        string="Related community",
        domain="[('hierarchy_level','=','community')]",
    )
    predecessor_contract_id = fields.Many2one(
        "contract.contract", string="Predecessor contract"
    )
    successor_contract_id = fields.Many2one(
        "contract.contract", string="Successor contract"
    )
    status = fields.Selection(
        selection=_CONTRACT_STATUS_VALUES,
        required=True,
        string="Status",
        default="in_progress",
    )
    discount = fields.Float(
        string="Discount (%)",
        digits="Discount",
        compute="_compute_discount",
        store=False,
    )
    last_date_invoiced = fields.Date(
        string="Last Date Invoiced", compute="_compute_last_date_invoiced", store=False
    )
    is_pack = fields.Boolean(related="contract_template_id.is_pack")
    is_free_pack = fields.Boolean(related="contract_template_id.is_free_pack")
    closing_action = fields.Selection(
        selection=_CLOSING_ACTION_VALUES,
        compute="_compute_closing_action",
        string="Closing reason",
        default="none",
        store=True,
    )
    closing_action_description = fields.Char(
        string="Closing reason description",
        compute="_compute_closing_action_description",
        store=True,
    )
    related_contract_product_ids = fields.One2many(
        "product.product",
        string="Related services",
        compute="_compute_related_contract_product_ids",
        store=False,
    )
    service_pack_id = fields.Many2one(
        "product.product",
        string="Service Pack",
        compute="_compute_service_pack_id",
        store=False,
    )
    sale_order_id = fields.Many2one(
        "sale.order",
        string="Sale Order (activation)",
    )
    received_invoices_count = fields.Integer(compute="_compute_received_invoices_count")
    # On energy_communities all contracts have skip_zero_qty marked by default
    skip_zero_qty = fields.Boolean(default=True)
    # On energy communities all contracts have company_id
    company_id = fields.Many2one(required=True)

    @api.depends("status", "successor_contract_id")
    def _compute_closing_action(self):
        for record in self:
            record.closing_action = "none"
            if record.status in ["closed", "closed_planned"]:
                if record.successor_contract_id:
                    record.closing_action = (
                        record.successor_contract_id.sale_order_id.service_invoicing_action
                    )
                else:
                    record.closing_action = "close"

    @api.depends("status", "successor_contract_id")
    def _compute_closing_action_description(self):
        for record in self:
            record.closing_action_description = ""
            if record.status in ["closed", "closed_planned"]:
                if record.successor_contract_id:
                    record.closing_action_description = (
                        record.successor_contract_id.sale_order_id.service_invoicing_action_description
                    )

    @api.constrains("partner_id", "community_company_id")
    def _constrain_unique_contract(self):
        for record in self:
            if record.community_company_id:
                existing_contract = record._get_existing_same_open_contract()
                if existing_contract:
                    raise_existing_same_open_contract_error(existing_contract)

    def _compute_received_invoices_count(self):
        for record in self:
            record.received_invoices_count = len(record._get_received_invoices_ids())

    @api.depends("contract_template_id")
    def _compute_related_contract_product_ids(self):
        for record in self:
            rel_products = [(5, 0, 0)]
            record.related_contract_product_ids = rel_products
            if record.contract_template_id:
                for line in record.contract_template_id.contract_line_ids:
                    rel_products.append((4, line.product_id.id))
                record.related_contract_product_ids = rel_products

    @api.depends("contract_line_ids")
    def _compute_discount(self):
        for record in self:
            record.discount = 0
            if record.contract_line_ids:
                record.discount = record.contract_line_ids[0].discount

    @api.depends("contract_line_ids")
    def _compute_last_date_invoiced(self):
        for record in self:
            record.last_date_invoiced = None
            if record.contract_line_ids:
                record.last_date_invoiced = record.contract_line_ids[
                    0
                ].last_date_invoiced

    @api.depends("contract_template_id")
    def _compute_service_pack_id(self):
        for record in self:
            record.service_pack_id = False
            if record.contract_template_id:
                rel_product = self.env["product.product"].search(
                    [
                        (
                            "property_contract_template_id",
                            "=",
                            record.contract_template_id.id,
                        )
                    ],
                    limit=1,
                )
                if rel_product:
                    record.service_pack_id = rel_product.id

    def _recurring_create_invoice(self, date_ref=False):
        moves = super()._recurring_create_invoice(date_ref)
        for move in moves:
            if not move.line_ids:
                move.unlink()
        return moves

    def action_activate_contract(self):
        return self._action_contract("activate")

    def action_close_contract(self):
        return self._action_contract("close")

    def action_modify_contract(self):
        return self._action_contract("modification")

    def action_reopen_contract(self):
        return self._action_contract(
            "reopen", self.service_pack_id, self.pricelist_id, self.payment_mode_id
        )

    def _action_contract(
        self, action, service_pack_id=False, pricelist_id=False, payment_mode_id=False
    ):
        self.ensure_one()
        create_dict = {
            "service_invoicing_id": self.id,
            "executed_action": action,
            "discount": self.discount,
        }
        if service_pack_id:
            create_dict["service_pack_id"] = service_pack_id.id
        if pricelist_id:
            create_dict["pricelist_id"] = pricelist_id.id
        if payment_mode_id:
            create_dict["payment_mode_id"] = payment_mode_id.id
        wizard = self.env["service.invoicing.action.wizard"].create(create_dict)
        return {
            "type": "ir.actions.act_window",
            "name": _("Executing: {}").format(action),
            "res_model": "service.invoicing.action.wizard",
            "view_type": "form",
            "view_mode": "form",
            "target": "new",
            "res_id": wizard.id,
        }

    def action_show_received_invoices(self):
        self.ensure_one()
        tree_view = self.env.ref("account.view_invoice_tree", raise_if_not_found=False)
        form_view = self.env.ref("account.view_move_form", raise_if_not_found=False)
        ctx = dict(self.env.context)
        ctx["default_move_type"] = "in_invoice"
        action = {
            "type": "ir.actions.act_window",
            "name": "Invoices",
            "res_model": "account.move",
            "view_mode": "tree,form",
            "domain": [("id", "in", self._get_received_invoices_ids())],
            "context": ctx,
        }
        if tree_view and form_view:
            action["views"] = [(tree_view.id, "tree"), (form_view.id, "form")]
        return action

    @api.model
    def cron_close_todays_closed_planned_contacts(self):
        impacted_contracts = self.env["contract.contract"].search(
            [("status", "=", "closed_planned")]
        )
        for contract in impacted_contracts:
            contract.set_close_status_type_by_date()
        return True

    # TODO: It would be very cool being able to use this methods on service_invoicing xml act_window definition
    @api.model
    def get_service_invoicing_views_domain(self):
        return [("community_company_id", "!=", False)]

    @api.model
    def get_service_invoicing_views_context(self):
        return {"search_default_not_finished": 1, "search_default_paused": 1}

    # TODO: Not working. Lack of access rules
    def _get_received_invoices_ids(self):
        received_invoices = []
        issued_invoices = self.sudo()._get_related_invoices().ids
        # related_partner = self.env["res.partner"].sudo.
        all_received_invoices = self.env["account.move"].search(
            [
                ("partner_id", "=", self.sudo().company_id.partner_id.id),
                ("move_type", "=", "in_invoice"),
            ]
        )
        for invoice in all_received_invoices:
            if invoice.sudo().ref_invoice_id:
                if invoice.sudo().ref_invoice_id.id in issued_invoices:
                    received_invoices.append(invoice.id)
        return received_invoices

    def _get_existing_same_open_contract(self):
        return get_existing_open_contract(
            self.env, self.partner_id, self.community_company_id, self
        )

    def set_close_status_type_by_date(self):
        if self.date_end.strftime("%Y-%m-%d") <= datetime.now().strftime("%Y-%m-%d"):
            self.write({"status": "closed"})
        else:
            self.write({"status": "closed_planned"})