Skip to content
Extraits de code Groupes Projets
Sélectionner une révision Git
  • 4f8db418d18fd2ec48fc094ef0d1a0fdda620890
  • 18.0 par défaut
  • 18.0-hugo
  • 16.0 protégée
4 résultats

financial_contract_guarantee.py

Blame
  • financial_contract_guarantee.py 16,27 Kio
    # © 2024 Le Filament (<http://www.le-filament.com>)
    # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
    
    from odoo import Command, _, api, fields, models
    from odoo.exceptions import ValidationError
    
    
    class FinancialContractGuarantee(models.Model):
        _name = "financial.contract.guarantee"
        _inherit = [
            "financial.contract",
            "mail.thread",
            "mail.activity.mixin",
        ]
        _description = "Contrat de garantie"
        _check_company_auto = True
        _rec_names_search = [
            "name",
            "number",
            "partner_id.name",
            "partner_id.member_number",
        ]
        _order = "create_date desc"
    
        # chaque contrat de garantie est associée à un identifiant titulaire
        # plusieurs contrats de garantie peuvent être associés au même identifiant
        titular_number = fields.Char("Identifiant Titulaire")
        person_number = fields.Char(
            "Identifiant Personne", related="partner_id.person_number"
        )
    
        product_id = fields.Many2one(
            comodel_name="financial.product.template.guarantee",
            string="Gamme",
            check_company=True,
            required=True,
        )
        suspensive_condition_ids = fields.One2many(
            comodel_name="financial.contract.guarantee.suspensive.condition",
            inverse_name="contract_id",
            compute="_compute_suspensive_condition_ids",
            store=True,
            readonly=False,
        )
        # TODO: voir pour la gestion de ce champ avec le module contract external
        external_loan_id = fields.Many2one(
            comodel_name="financial.contract.external",
            string="Prêt lié",
            domain="[('partner_id', '=', partner_id), ('tool', '=', 'loan')]",
        )
        loan_contract_number = fields.Char(
            string="Numéro de contrat de prêt",
            related="external_loan_id.number",
            store=True,
        )
        loan_bank = fields.Many2one(
            comodel_name="financial.contract.external.partner",
            related="external_loan_id.external_partner_id",
        )
        # 123.45 % → 1.2345 → 5 digits, 4 after decimal
        guarantee_rate = fields.Float(
            "Quotité garantie", tracking=1, aggregator="avg", digits=(5, 4)
        )
        initial_guarantee_amount = fields.Monetary(
            string="Montant initial garanti",
            compute="_compute_initial_guarantee_amount",
            store=True,
        )
        guarantee_amount = fields.Monetary(
            compute="_compute_guarantee_amount",
            string="En cours de garantie",
            store=True,
            tracking=1,
            help="Calculé comme le produit du CRDU par la quotité garantie.",
        )
        is_counter_guarantee = fields.Boolean("Contre Garantie", default=False)
        counter_guarantee_rate = fields.Float("Quotité contre garantie")
        counter_guarantee_partner_id = fields.Many2one(
            comodel_name="financial.contract.guarantee.partner", string="Partenaire"
        )
        final_risk_guarantee = fields.Monetary(
            string="Garantie risque final",
            compute="_compute_final_risk_guarantee",
            store=True,
            tracking=1,
            help="Le risque final est l'encours de garantie moins les FMG "
            "et les parts sociales. Dans le cas d'une co-garantie, "
            "on retire également son montant.",
        )
    
        # --- FMG ---
        fmg_amount = fields.Monetary(
            string="FMG calculé",
            compute="_compute_fmg_amount",
            store=True,
            help="Calculé à partir du montant garanti initial et du taux de FMG.",
        )
        fmg_paid = fields.Monetary(
            string="FMG payé",
            compute="_compute_fmg_paid",
            store=True,
            help="Calculé sur la somme des lignes de FMG.",
        )
        fmg_ids = fields.One2many(
            comodel_name="mutual.guarantee.fund.line",
            inverse_name="guarantee_id",
            string="FMG",
        )
    
        # --- Social share ---
        social_share_number = fields.Integer(
            string="Nbre parts sociales",
            compute="_compute_social_share_number",
            store=True,
            help="Nombre de parts sociales calculé à partir "
            "du montant total et du prix unitaire.",
        )
        social_share_amount = fields.Monetary(
            string="Montant parts sociales décidé",
        )
        social_share_amount_computed = fields.Monetary(
            string="Montant parts sociales calculé",
            compute="_compute_social_share",
            store=True,
            help="Montant des parts sociales calculé à partir du taux "
            "et de la quantité maximale de parts sociales.",
        )
        social_share_ids = fields.One2many(
            comodel_name="company.share.line",
            inverse_name="guarantee_id",
            string="Parts sociales",
        )
        social_share_paid = fields.Monetary(
            string="Montant parts sociales payé",
            compute="_compute_social_share_paid",
            store=True,
            help="Montant calculé sur la somme des lignes de mouvements de parts sociales.",
        )
    
        # --- Commission ---
        commission_ids = fields.One2many(
            comodel_name="financial.guarantee.commission.line",
            inverse_name="guarantee_id",
            string="Commissions",
        )
    
        is_old_associate = fields.Boolean(
            string="Nouveau sociétaire",
            compute="_compute_is_new_associate",
            store=True,
            default=False,
        )
        line_ids = fields.One2many(
            comodel_name="financial.contract.guarantee.line",
            inverse_name="guarantee_id",
            string="Lignes de solde",
        )
        date_folder_received = fields.Date("Date de réception dossier")
        date_approval_president = fields.Date("Date Validation Président")
    
        # --- Loan Data ---
        payment_date = fields.Date("Date de versement")
        payment_date_computed = fields.Date(
            string="Date de paiement mise à jour",
            help="Calculée sur la dernière valeur connue dans les lignes CRESERFI",
            store=True,
            compute="_compute_guarantee_data",
        )
        amount_initial = fields.Monetary(
            string="Montant initial",
            compute="_compute_amount_initial",
            store=True,
            readonly=False,
            help="Montant du prêt."
        )
        amount_received = fields.Monetary("Montant reçu")
        expiration_date_computed = fields.Date(
            string="Date d'expiration mise à jour",
            help="Calculée sur la dernière valeur connue dans les lignes CRESERFI",
            store=True,
            compute="_compute_guarantee_data",
        )
        remaining_capital = fields.Monetary(
            "Capital restant dû",
            tracking=1,
            store=True,
            compute="_compute_guarantee_data",
        )
        loan_duration = fields.Integer(
            string="Durée du crédit (mois)",
            compute="_compute_loan_duration",
            store=True,
            readonly=False,
        )
        loan_duration_computed = fields.Integer(
            string="Durée du crédit (mois) mise à jour",
            help="Calculée sur la dernière valeur connue dans les lignes CRESERFI",
            store=True,
            compute="_compute_guarantee_data",
        )
    
        # --- Company Data ---
        segment_code = fields.Char("Code Segment")
        bdf_scoring = fields.Char(
            string="Cotation BDF",
            related="partner_id.bdf_scoring",
        )
        bdf_date = fields.Date(
            string="Date Cotation BDF",
            related="partner_id.bdf_date",
        )
        mcdo_scoring = fields.Char(
            string="Cotation MacDonough",
            related="partner_id.mcdo_scoring",
        )
        mcdo_date = fields.Date(
            string="Date Cotation MacDonough",
            related="partner_id.mcdo_date",
        )
        branch_code = fields.Char("Code branche")
    
        # Invoice Data
        commission_amount = fields.Monetary(
            string="Montant de commission",
            compute="_compute_commission_amount",
            store=True,
        )
    
        # --- counters ----
        # TODO is there a less verbose way to do this?
        commission_ids_count = fields.Integer(
            compute="_compute_commission_ids_count", store=True
        )
    
        @api.depends("commission_ids")
        def _compute_commission_ids_count(self):
            for g in self:
                g.commission_ids_count = len(g.commission_ids)
    
        line_ids_count = fields.Integer(compute="_compute_line_ids_count", store=True)
    
        @api.depends("line_ids")
        def _compute_line_ids_count(self):
            for g in self:
                g.line_ids_count = len(g.line_ids)
    
        fmg_ids_count = fields.Integer(compute="_compute_fmg_ids_count", store=True)
    
        @api.depends("fmg_ids")
        def _compute_fmg_ids_count(self):
            for g in self:
                g.fmg_ids_count = len(g.fmg_ids)
    
        social_share_ids_count = fields.Integer(
            compute="_compute_social_share_ids_count", store=True
        )
    
        @api.depends("social_share_ids")
        def _compute_social_share_ids_count(self):
            for g in self:
                g.social_share_ids_count = len(g.social_share_ids)
    
        # ------------------------------------------------------
        # Constrains functions
        # ------------------------------------------------------
    
        # ------------------------------------------------------
        # Computed fields / Search Fields
        # ------------------------------------------------------
        @api.depends("external_loan_id", "external_loan_id.amount")
        def _compute_amount_initial(self):
            for guarantee in self:
                if guarantee.external_loan_id:
                    guarantee.amount_initial = guarantee.external_loan_id.amount
    
        @api.depends("external_loan_id", "external_loan_id.duration")
        def _compute_loan_duration(self):
            for guarantee in self:
                if guarantee.external_loan_id:
                    guarantee.loan_duration = guarantee.external_loan_id.duration
    
        @api.depends(
            "line_ids",
            "line_ids.end_date",
            "line_ids.payment_date",
            "line_ids.loan_duration",
            "line_ids.remaining_capital",
            "line_ids.amount_received",
        )
        def _compute_guarantee_data(self):
            for guarantee in self:
                with_date = guarantee.line_ids.filtered(lambda x: not not x.line_date)
                ordered = with_date.sorted("line_date", reverse=True)
                if ordered:
                    last_line = ordered[0]
                    guarantee.update(
                        {
                            "expiration_date_computed": last_line.end_date,
                            "payment_date_computed": last_line.payment_date,
                            "loan_duration_computed": last_line.loan_duration,
                            "remaining_capital": last_line.remaining_capital,
                            "amount_received": last_line.amount_received,
                        }
                    )
                    if last_line.remaining_capital == 0:
                        guarantee.state = "done"
                        guarantee.external_loan_id.state = "done"
    
        @api.depends("line_ids", "line_ids.commission_amount")
        def _compute_commission_amount(self):
            for guarantee in self:
                guarantee.commission_amount = sum(
                    guarantee.line_ids.mapped("commission_amount")
                )
    
        @api.depends("guarantee_amount", "is_counter_guarantee", "counter_guarantee_rate")
        def _compute_final_risk_guarantee(self):
            """encours garanti - FMG - PS
            si contre garantie, on retire également son montant
            """
            for guarantee in self:
                # FIXME doit-on vraiment retrancher le montant des parts sociales ?
                frg = (
                    guarantee.guarantee_amount
                    - guarantee.fmg_amount
                    - guarantee.social_share_amount
                )
                # la contre garantie diminue d'autant le risque final
                if guarantee.is_counter_guarantee:
                    frg *= 1 - guarantee.counter_guarantee_rate
                guarantee.final_risk_guarantee = frg
    
        @api.depends("remaining_capital", "guarantee_rate")
        def _compute_guarantee_amount(self):
            "CRDU × quotité"
            for guarantee in self:
                guarantee.guarantee_amount = (
                    guarantee.remaining_capital * guarantee.guarantee_rate
                )
    
        @api.depends("amount_initial", "guarantee_rate")
        def _compute_initial_guarantee_amount(self):
            for guarantee in self:
                guarantee.initial_guarantee_amount = (
                    guarantee.amount_initial * guarantee.guarantee_rate
                )
    
        @api.depends("initial_guarantee_amount")
        def _compute_fmg_amount(self):
            fmg_rate = self.company_id.fmg_rate
            if not fmg_rate or fmg_rate == 0.0:
                raise ValidationError(_("Le taux de FMG n'est pas configuré."))
            for guarantee in self:
                guarantee.fmg_amount = fmg_rate * guarantee.initial_guarantee_amount
    
        @api.depends("fmg_ids", "fmg_ids.amount")
        def _compute_fmg_paid(self):
            for guarantee in self:
                guarantee.fmg_paid = sum(guarantee.fmg_ids.mapped("amount"))
    
        @api.depends("initial_guarantee_amount", "partner_id")
        def _compute_social_share(self):
            """
            Calcule le montant des parts sociales en fonction :
            - du taux défini sur la société
            - du montant max défini sur la société
            - du prix unitaire de la part défini sur la société
            - des prises de parts existantes sur la société
            :return: affecte les valeurs de montant et nombre de parts
    
            Sert au moment de la décision pour automatiser le calcul
            """
            social_share_rate = self.company_id.social_share_rate
            social_share_amount_max = self.company_id.social_share_amount_max
            for guarantee in self:
                max_amount = (
                    social_share_amount_max - guarantee.partner_id.company_share_total
                )
                rate_amount = social_share_rate * guarantee.initial_guarantee_amount
                final_amount = rate_amount if rate_amount <= max_amount else max_amount
                guarantee.social_share_amount_computed = final_amount
    
        @api.depends("social_share_amount")
        def _compute_social_share_number(self):
            share_unit_price = self.company_id.share_unit_price
            for guarantee in self:
                guarantee.social_share_number = (
                    guarantee.social_share_amount // share_unit_price
                )
    
        @api.depends("social_share_ids", "social_share_ids.share_total_amount")
        def _compute_social_share_paid(self):
            for guarantee in self:
                guarantee.social_share_paid = sum(
                    guarantee.social_share_ids.mapped("share_total_amount")
                )
    
        @api.depends("product_id")
        def _compute_suspensive_condition_ids(self):
            for contract in self:
                condition_to_create = []
                if contract.product_id:
                    for condition in self.product_id.suspensive_condition_ids:
                        condition_to_create.append(
                            Command.create(
                                {
                                    "condition_id": condition.id,
                                    "condition_comment": condition.condition_comment,
                                },
                            )
                        )
                    if condition_to_create:
                        contract.suspensive_condition_ids.unlink()
                        contract.suspensive_condition_ids = condition_to_create
    
        @api.depends("partner_id", "partner_id.company_share_line_ids")
        def _compute_is_new_associate(self):
            for guarantee in self:
                if guarantee.partner_id and guarantee.partner_id.company_share_line_ids:
                    guarantee.is_old_associate = True
    
        # ------------------------------------------------------
        # Onchange
        # ------------------------------------------------------
        @api.onchange("product_id")
        def _onchange_product_id(self):
            self._set_product_values()
    
        @api.onchange("is_counter_guarantee")
        def _onchange_is_counter_guarantee(self):
            if not self.is_counter_guarantee:
                self.counter_guarantee_rate = 0.0
                self.counter_guarantee_partner_id = False
    
        # ------------------------------------------------------
        # Actions
        # ------------------------------------------------------
    
        def action_set_social_share_amount(self):
            self.write({"social_share_amount": self.social_share_amount_computed})
    
        # ------------------------------------------------------
        # CRUD (Override ORM)
        # ------------------------------------------------------
    
        # ------------------------------------------------------
        # Business functions
        # ------------------------------------------------------
        def _set_product_values(self):
            if self.product_id:
                self.guarantee_rate = self.product_id.guarantee_rate