Skip to content
Extraits de code Groupes Projets
Sélectionner une révision Git
  • 186dc57a1d8d999fa5b932314a9e2acdffa5b2c6
  • 12.0-evo-202003 par défaut
  • 14-RV-20250324
  • 14-RV-20240830
  • 14-RV-20231222
  • 12-RV-Bug_ecrasement_date_radiation
  • 12-RV-revision-staff
  • 12-RV-copadev
  • 12-RV-Correctif-open-instagram
  • 12-RV-Tree-Coop-Ajout-effectif
  • 12.0-RV-Instagram
  • 12.0-RV-segment_visibility
  • 12.0 protégée
  • 12.0-RV-Abonnements
14 résultats

scop_questionnaire.py

Blame
  • Bifurcation depuis Le Filament / Confédération Générale des SCOP / cgscop_partner
    Le projet source a une visibilité limitée.
    res_partner.py 52,88 Kio
    # © 2019 Le Filament (<http://www.le-filament.com>)
    # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
    
    
    import logging
    import time
    from datetime import datetime, timedelta
    
    from odoo import _, api, fields, models
    from odoo.exceptions import ValidationError
    
    _logger = logging.getLogger(__name__)
    
    
    class ScopPartner(models.Model):
        _inherit = "res.partner"
    
        def _default_ur(self):
            return self.env["res.company"]._ur_default_get()
    
        def _default_country(self):
            return self.env.ref("base.fr", False)
    
        @api.model
        def _get_domain_incubator_id(self):
            try:
                incubator_id = self.env.ref("cgscop_partner.incubator").id
                return [
                    ("is_company", "=", True),
                    ("organization_subtype_id", "=", incubator_id),
                ]
            except Exception:
                return [("is_company", "=", True)]
    
        @api.model
        def _get_domain_revision_company_id(self):
            try:
                rev_id = self.env.ref("cgscop_partner.riga_14953").id
                com_id = self.env.ref("cgscop_partner.riga_16433").id
                return [
                    ("is_company", "=", True),
                    ("organization_subtype_id", "in", [com_id, rev_id]),
                ]
            except Exception:
                return [("is_company", "=", True)]
    
        # Infos générales - champs non affichés
        is_cooperative = fields.Boolean("Est une coopérative")
        current_user_ur_id = fields.Many2one(
            "union.regionale",
            string="Union Régionale de l'utilisateur",
            compute="_compute_current_user_ur_id",
            search="_search_current_user_ur_id",
        )
    
        # Informations organisme - champs non affichés
        id_ur = fields.Char("Identifiant UR")
        id_riga = fields.Char("Identifiant RIGA")
    
        # Informations Bandeau
        project_status = fields.Selection(
            [
                ("1_information", "Phase d'information"),
                ("2_pre-diagnostic", "Phase de pré-diagnostic"),
                ("3_accompagnement", "Phase d'accompagnement projet"),
                ("4_suivi", "Phase de suivi"),
                ("5_abandonne", "Projet abandonné"),
                ("6_decede", "Coop Décédée"),
            ],
            tracking=True,
            string="Statut projet",
            index=True,
        )
        name = fields.Char(index=True, tracking=True)
        cooperative_form_id = fields.Many2one(
            "res.partner.cooperative.form",
            string="Cooperative form",
            ondelete="restrict",
            tracking=True,
        )
        partner_company_type_id = fields.Many2one(
            comodel_name="res.partner.company.type",
            string="Statut Juridique",
            ondelete="restrict",
            tracking=True,
        )
    
        membership_status = fields.Selection(
            [
                ("not_member", "Non Adhérent"),
                ("adhesion", "Phase d'Adhésion"),
                ("soumis_cg", "Soumis CG"),
                ("member", "Adhérent"),
                ("out", "Radié"),
            ],
            string="Statut d'adhésion",
            default="not_member",
        )
        member_number = fields.Char(
            "No adhérent (texte)", compute="_compute_membership_number", store=True
        )
        member_number_int = fields.Integer(
            "No adhérent",
            group_operator="",
        )
        member_start_stat = fields.Date(
            "Date d'adhésion statistique",
            compute="_compute_membership",
            store=True,
        )
        member_start = fields.Date(
            "Date d'adhésion", compute="_compute_membership", store=True
        )
        cae = fields.Boolean("CAE", tracking=True)
    
        dissolution_date = fields.Date("Date de Décès", tracking=True)
        dissolution_reason_id = fields.Many2one(
            "res.partner.dissolution.reason",
            string="Motif Décès",
            ondelete="restrict",
            tracking=True,
        )
    
        # Infos générales / Contact
        sigle = fields.Char("Sigle")
        street = fields.Char("Rue", tracking=1)
        street2 = fields.Char("Rue 2", tracking=1)
        street3 = fields.Char("Rue 3", tracking=1)
        zip = fields.Char("CP", change_default=True, tracking=1)
        zip_departement = fields.Char(
            "Num Département", compute="_compute_num_departement", store=True
        )
        region = fields.Many2one(
            comodel_name="res.country.state",
            string="Région",
            compute="_compute_region",
            store=True,
        )
        city = fields.Char("Ville", tracking=1)
        cedex = fields.Char("Cedex", tracking=1)
        country_id = fields.Many2one(
            "res.country",
            string="Pays",
            ondelete="restrict",
            default=_default_country,
            tracking=True,
        )
        phone = fields.Char("Téléphone 1", tracking=True)
        mobile = fields.Char("Téléphone 2", tracking=True)
        email = fields.Char("eMail administratif", tracking=True)
        facebook = fields.Char("Facebook")
        linkedin = fields.Char("LinkedIn")
        twitter = fields.Char("Twitter")
        instagram = fields.Char("Instagram")
    
        # Infos générales / Suivi UR
        ur_id = fields.Many2one(
            "union.regionale",
            string="Union Régionale",
            index=True,
            ondelete="restrict",
            default=_default_ur,
            tracking=True,
        )
        creation_delegate_id = fields.Many2one(
            "res.users",
            string="Délégué de création",
            ondelete="restrict",
            tracking=True,
        )
        followup_delegate_id = fields.Many2one(
            "res.users",
            string="Délégué de suivi",
            domain=[("active", "=", True)],
            ondelete="restrict",
            tracking=True,
        )
        support_delegate_id = fields.Many2one(
            "res.users",
            string="Délégué en support",
            domain=[("active", "=", True)],
            ondelete="restrict",
            tracking=True,
        )
        segment_1_id = fields.Many2many(
            "res.partner.segment1",
            column1="partner_id",
            column2="segment_id",
            string="Segmentation 1",
        )
        segment_2_id = fields.Many2many(
            "res.partner.segment2",
            column1="partner_id",
            column2="segment_id",
            string="Segmentation 2",
        )
        segment_3_id = fields.Many2many(
            "res.partner.segment3",
            column1="partner_id",
            column2="segment_id",
            string="Segmentation 3",
        )
        segment_4_id = fields.Many2many(
            "res.partner.segment4",
            column1="partner_id",
            column2="segment_id",
            string="Segmentation 4",
        )
    
        segment_1_nb = fields.Integer(
            string="Nb de segments 1", compute="_compute_segment_nb"
        )
        segment_2_nb = fields.Integer(
            string="Nb de segments 2", compute="_compute_segment_nb"
        )
        segment_3_nb = fields.Integer(
            string="Nb de segments 3", compute="_compute_segment_nb"
        )
        segment_4_nb = fields.Integer(
            string="Nb de segments 4", compute="_compute_segment_nb"
        )
        filiere_ids = fields.Many2many(
            "res.partner.filiere",
            column1="partner_id",
            column2="filiere_id",
            string="Filière",
        )
        is_mucs = fields.Boolean(string="Est adhérent Mucs", tracking=True)
        is_mucs_update_date = fields.Date(string="Date MAJ Mucs", tracking=True)
        is_union_sociale = fields.Boolean(
            string="Est adhérent Union Sociale", tracking=True
        )
        is_union_sociale_update_date = fields.Date(
            string="Date MAJ Union Sociale", tracking=True
        )
    
        # Infos générales / Infos activité
        creation_origin_id = fields.Many2one(
            "res.partner.creation.origin",
            string="Origine création en coop",
            domain=[("parent_id", "=", False)],
            ondelete="restrict",
            tracking=True,
        )
        creation_suborigin_id = fields.Many2one(
            "res.partner.creation.origin",
            string="Sous-Origine création en coop",
            domain=[("child_ids", "=", False)],
            ondelete="restrict",
            tracking=True,
        )
        date_1st_sign = fields.Date("Date 1ère signature coop", tracking=True)
        registration_date = fields.Date(string="Date d'immatriculation RCS", tracking=True)
        social_object = fields.Text("Objet Social", tracking=True)
        naf_id = fields.Many2one(
            "res.partner.naf",
            string="Code NAF",
            ondelete="restrict",
            tracking=True,
        )
        secteur_id = fields.Many2one(
            "res.partner.secteur.activite",
            string="Secteur d'activité",
            related="naf_id.secteur_id",
            store=True,
        )
        activity_desc = fields.Text("Description de l'activité", tracking=True)
    
        certification_ids = fields.Many2many(
            comodel_name="res.partner.certification",
            string="Agréments",
            ondelete="restrict",
        )
        other_certification = fields.Char(string="Autre agrément", required=False)
        siret = fields.Char(string="SIRET", size=14, index=True, tracking=True, copy=False)
        formatted_siret = fields.Char(
            string="SIRET formaté", compute="_compute_formatted_siret"
        )
        siren = fields.Char(string="SIREN", size=11, compute="_compute_siren")
        pappers_url = fields.Char(string="URL Pappers", compute="_compute_siren")
        capital = fields.Integer("Capital (en €)")
        first_closeout = fields.Date("1er bilan en coop")
        closeout_month = fields.Selection(
            [
                ("1", "Janvier"),
                ("2", "Février"),
                ("3", "Mars"),
                ("4", "Avril"),
                ("5", "Mai"),
                ("6", "Juin"),
                ("7", "Juillet"),
                ("8", "Août"),
                ("9", "Septembre"),
                ("10", "Octobre"),
                ("11", "Novembre"),
                ("12", "Décembre"),
            ],
            string="Mois clôture exercices",
        )
        is_seed_scop = fields.Boolean("Est une SCOP d'amorçage")
        seed_end = fields.Date("Date de fin du dispositif d'amorçage")
        is_incubated = fields.Boolean("Est incubé")
        incubator_id = fields.Many2one(
            "res.partner",
            string="Incubateur",
            domain=_get_domain_incubator_id,
            ondelete="restrict",
        )
        is_ag_constitution = fields.Boolean("AG constitutive réalisée")
        is_federation_com = fields.Boolean(
            string="Fédération de la Communication",
            compute="_compute_federation",
            store=True,
            default=False,
        )
        is_federation_btp = fields.Boolean(
            string="Fédération du BTP",
            compute="_compute_federation",
            store=True,
            default=False,
        )
        is_federation_indus = fields.Boolean(
            string="Fédération de l'Industrie",
            compute="_compute_federation",
            store=True,
            default=False,
        )
        is_federation_cae = fields.Boolean(
            string="Fédération des CAE",
            compute="_compute_federation",
            store=True,
            default=False,
        )
        activity_federation_com_ids = fields.Many2many(
            comodel_name="scop.federation.com.activity",
            relation="res_partner_federation_com_activity_rel",
            column1="partner_id",
            column2="com_activity_id",
            string="Domaine d'activité Com",
            help="Fédération de la Com - Domaine d'activité",
        )
        activity_federation_indus_ids = fields.Many2many(
            comodel_name="scop.federation.indus.activity",
            relation="res_partner_federation_indus_activity_rel",
            column1="partner_id",
            column2="indus_activity_id",
            string="Domaine d'activité Industrie",
            help="Fédération de l'Industrie - Domaine d'activité",
        )
        copadev_member = fields.Boolean(string="Adhérent copadev", tracking=True)
    
        # Contacts
        director_ids = fields.One2many(
            "res.partner",
            "parent_id",
            string="Contacts Dirigeants",
            domain=[("mandate_id", "!=", False)],
        )
        other_child_ids = fields.One2many(
            "res.partner",
            "parent_id",
            string="Autres Contacts",
            domain=[("mandate_id", "=", False)],
        )
    
        # Révisions
        revision_contract = fields.Date("Date du contrat de révision", tracking=True)
        revision_company_id = fields.Many2one(
            "res.partner",
            string="Organisme de révision",
            domain=_get_domain_revision_company_id,
            ondelete="restrict",
            tracking=True,
        )
        revision_backup_company_id = fields.Many2one(
            "res.partner",
            string="Organisme de révision suppléant",
            domain=_get_domain_revision_company_id,
            ondelete="restrict",
        )
        revision_person_id = fields.Many2one(
            comodel_name="res.users",
            string="Réviseur",
            ondelete="restrict",
            tracking=True,
        )
        revision_certified_person_id = fields.Many2one(
            comodel_name="res.users", string="Réviseur agréé", ondelete="restrict"
        )
        revision_person_assign_date = fields.Date(
            string="Date de nomination du réviseur", tracking=True
        )
        revision_type = fields.Selection(
            [
                ("1y", "Annuelle"),
                ("5y", "Quinquennale"),
                ("5ys", "Quinquennale séquencée (annuel)"),
                ("5ys23", "Quinquennale séquencée (2 ans et 3 ans)"),
            ],
            string="Périodicité de la révision",
        )
        revision_next_date = fields.Date("Prochain exercice révisable (old)")
        revision_format_id = fields.Many2one(
            "scop.revision.format",
            string="Format de révision",
            ondelete="restrict",
        )
        revision_tarif = fields.Integer(
            "Tarif de vente", related="revision_format_id.tarif", store=False
        )
        revision_next_exercice = fields.Integer(
            "Prochain exercice révisable",
            compute="_compute_revision_next_exercice",
            store=True,
        )
        revision_next_year = fields.Integer(
            "Année prochaine révision",
            compute="_compute_revision_next_year",
            store=True,
        )
        revision_ids = fields.One2many(
            comodel_name="scop.revision",
            inverse_name="partner_id",
            string="Liste des Révisions",
        )
    
        # Action RSE
        action_rse_ids = fields.One2many(
            comodel_name="scop.action.rse",
            inverse_name="partner_id",
            string="Liste des Actions RSE",
        )
        is_rse = fields.Boolean(
            string="Engagement RSE", compute="_compute_is_rse", store=True, default=False
        )
    
        # Historique
        scop_period_ids = fields.One2many(
            comodel_name="scop.period",
            inverse_name="partner_id",
            string="Historique",
        )
    
        # Période d'adhésion
        membership_period_ids = fields.One2many(
            comodel_name="scop.membership.period",
            inverse_name="partner_id",
            string="Périodes d'adhésion",
        )
    
        # Effectifs
        staff_ids = fields.One2many(
            comodel_name="scop.partner.staff",
            inverse_name="partner_id",
            string="Effectifs",
        )
    
        # Liste Ministère
        staff_last = fields.Integer(
            string="Dernier effectif connu",
            compute="_compute_last_effective",
            store=True,
        )
        staff_shareholder_last = fields.Integer(
            string="Dernier effectif associés connu",
            compute="_compute_last_effective",
            store=True,
        )
    
        staff_last_date = fields.Date(
            string="Date dernier effectif connu",
            compute="_compute_last_effective",
            store=True,
        )
    
        # Champs pour personnes
        birthyear = fields.Integer("Année de naissance")
        subscription_ids = fields.One2many(
            comodel_name="res.partner.newsletter.subscription",
            inverse_name="partner_id",
            string="Abonnements",
        )
        contact_origin_id = fields.Many2one(
            "res.partner.rgpd.origin",
            string="Origine du contact",
            ondelete="restrict",
        )
        mandate_id = fields.Many2one(
            "res.partner.mandate", string="Mandat", ondelete="restrict"
        )
        function_lst_id = fields.Many2one(
            "res.partner.function_lst", string="Fonction", ondelete="restrict"
        )
        contact_legality = fields.Selection(
            [
                ("employee", "Salarié"),
                ("customer", "Client en contrat"),
                ("supplier", "Fournisseur en contrat"),
                ("consent", "Consentement"),
                ("legitimate", "Intérêt légitime"),
                ("none", "Aucune"),
            ],
            string="Licéité du contact",
            compute="_compute_contact_legality",
            store=True,
        )
        associate = fields.Selection(
            selection=[
                ("asso", "Associé extérieur"),
                ("coop", "Associé coopérateur"),
                ("none", "Non associé"),
            ],
            string="Associé",
        )
        employee = fields.Boolean(string="Salarié", default=True)
    
        # Champs pour partenaires
        organization_type_id = fields.Many2one(
            "res.partner.organization.type",
            string="Famille",
            ondelete="restrict",
            compute="_compute_org_type_id",
            tracking=True,
        )
        organization_subtype_id = fields.Many2one(
            "res.partner.organization.type",
            string="Type",
            domain=[("child_ids", "=", False)],
            ondelete="restrict",
            tracking=True,
        )
    
        # Droits utilisateurs
        is_administrative = fields.Boolean(compute="_compute_is_administrative")
    
        # Hack pour la création de contacts depuis la fiche organisme
        parent_id_onchange = fields.Many2one("res.partner")
    
        # ------------------------------------------------------
        # Constrains
        # ------------------------------------------------------
        @api.constrains("zip_id", "country_id", "city_id", "state_id")
        def _check_zip(self):
            return
    
        @api.constrains("siret")
        def _check_siret(self):
            if self.siret and not self.env.context.get("import_file"):
                siren = self.siret[:3] + " " + self.siret[3:6] + " " + self.siret[6:9]
                if not self.siret.isdigit():
                    raise ValidationError(_("Ce SIRET n'est pas valide"))
                if len(self.siret) != 14:
                    raise ValidationError(_("La longueur du SIRET doit être égale à 14"))
                if (
                    self.search_count(
                        [
                            ("siret", "=like", siren + "%"),
                            ("is_cooperative", "=", True),
                        ]
                    )
                    > 1
                ):
                    raise ValidationError(_("Ce SIREN existe déjà parmi les coopératives"))
                elif self.search_count([("siret", "=", self.siret)]) > 1:
                    raise ValidationError(_("Ce SIRET existe déjà"))
    
        # ------------------------------------------------------
        # Actions
        # ------------------------------------------------------
        def open_facebook(self):
            self.ensure_one()
            return {
                "type": "ir.actions.act_url",
                "url": self.facebook,
            }
    
        def open_linkedin(self):
            self.ensure_one()
            return {
                "type": "ir.actions.act_url",
                "url": self.linkedin,
            }
    
        def open_twitter(self):
            self.ensure_one()
            return {
                "type": "ir.actions.act_url",
                "url": self.twitter,
            }
    
        def open_instagram(self):
            self.ensure_one()
            return {
                "type": "ir.actions.act_url",
                "url": self.instagram,
            }
    
        def open_pappers(self):
            self.ensure_one()
            return {
                "type": "ir.actions.act_url",
                "url": self.pappers_url,
            }
    
        def remove_director(self):
            self.write({"mandate_id": False})
            return {"type": "ir.actions.act_window_close"}
    
        # ------------------------------------------------------
        # Onchange
        # ------------------------------------------------------
    
        # TODO: à vérifier
        # Hack pour la création de contacts depuis la fiche organisme
        @api.onchange("parent_id_onchange")
        def _onchange_parent_id_onchange(self):
            self.parent_id = self.parent_id_onchange
    
        # TODO: à vérifier
        @api.onchange("parent_id")
        def _onchange_parent_id(self):
            if self.parent_id.ur_id:
                self.ur_id = self.parent_id.ur_id
    
        @api.onchange("ur_id")
        def _onchange_ur_id(self):
            if self.child_ids:
                for child in self.child_ids:
                    child.ur_id = self.ur_id
    
        @api.onchange("creation_origin_id")
        def onchange_creation_origin_id(self):
            for coop in self:
                coop.creation_suborigin_id = False
    
        # TODO: à vérifier
        @api.onchange("is_seed_scop")
        def onchange_is_seed_scop(self):
            for coop in self:
                if coop.is_seed_scop:
                    if coop.date_1st_sign:
                        coop.seed_end = coop.date_1st_sign + timedelta(2556)
                    else:
                        coop.seed_end = datetime.today().date() + timedelta(2556)
                else:
                    coop.seed_end = False
    
        @api.onchange("name")
        def onchange_name(self):
            if self.search_count([("name", "=ilike", self.name)]) > 0:
                return {
                    "warning": {
                        "title": "Attention",
                        "message": "Ce nom / cette raison sociale existe déjà, "
                        + "merci de vérifier que vous n'êtes pas en "
                        + "train de créer un doublon",
                    }
                }
    
        @api.onchange("mobile")
        def onchange_mobile(self):
            if self.mobile and len(self.mobile) > 0 and len(self.mobile) < 10:
                raise ValidationError(
                    _("Le numéro de téléphone doit contenir au moins 10 caractères")
                )
    
        @api.onchange("phone")
        def onchange_phone(self):
            if self.phone and len(self.phone) > 0 and len(self.phone) < 10:
                raise ValidationError(
                    _("Le numéro de téléphone doit contenir au moins 10 caractères")
                )
    
        @api.onchange("siret")
        def onchange_siret(self):
            if self.siret:
                siren = self.siret[:3] + " " + self.siret[3:6] + " " + self.siret[6:9]
                if self.search_count([("siret", "=like", self.siret)]) > 0:
                    return {
                        "warning": {
                            "title": "Attention",
                            "message": "Ce SIRET existe déjà, merci de vérifier "
                            + "que vous n'êtes pas en train de créer un"
                            + " doublon",
                        }
                    }
                elif self.search_count([("siret", "=like", siren + "%")]) > 0:
                    return {
                        "warning": {
                            "title": "Attention",
                            "message": "Ce SIREN existe déjà, merci de vérifier "
                            + "que vous n'êtes pas en train de créer un"
                            + " doublon",
                        }
                    }
    
        @api.onchange("cooperative_form_id")
        def onchange_cooperative_form_id(self):
            if self.cooperative_form_id == self.env.ref(
                "cgscop_partner.form_noncooperative"
            ):
                self.creation_origin_id = None
                self.creation_suborigin_id = None
                self.is_ag_constitution = None
                self.date_1st_sign = None
                self.first_closeout = None
    
        # ------------------------------------------------------
        # Common functions
        # ------------------------------------------------------
        def _create_period(self, partner):
            new_period = self.env["scop.period"].create(
                {
                    "partner_id": partner.id,
                    "start": partner.registration_date or fields.Date.today(),
                    "name": partner.name,
                    "cooperative_form_id": partner.cooperative_form_id.id,
                    "partner_company_type_id": partner.partner_company_type_id.id,
                    "siret": partner.siret,
                    "street": partner.street,
                    "street2": partner.street2,
                    "street3": partner.street3,
                    "zip": partner.zip,
                    "zip_id": partner.zip_id.id,
                    "city": partner.city,
                    "cedex": partner.cedex,
                    "state_id": partner.state_id.id,
                    "country_id": partner.country_id.id,
                    "naf_id": partner.naf_id.id,
                    "ur_id": partner.ur_id.id,
                    "cae": partner.cae,
                }
            )
            partner.scop_period_ids = new_period
    
        # ------------------------------------------------------
        # Override ORM
        # ------------------------------------------------------
        @api.model
        def _name_search(
            self, name, args=None, operator="ilike", limit=100, name_get_uid=None
        ):
            args = args or []
            domain = [
                "|",
                ("name", operator, name),
                ("member_number", "ilike", name),
            ]
            return self._search(domain + args, limit=limit, access_rights_uid=name_get_uid)
    
        # Creation d'une periode lorsque le statut passe en Phase de Suivi
        # TODO: à revoir et demander si mise en majuscule du nom de la structure
        def write(self, vals):
            # Gestion casse des informations
            if vals.get("name"):
                vals["name"] = vals.get("name").title()
            if vals.get("lastname"):
                vals["lastname"] = vals.get("lastname").title()
            if vals.get("firstname"):
                vals["firstname"] = vals.get("firstname").title()
            if vals.get("city"):
                vals["city"] = vals.get("city").upper()
            if len(self) == 1 and self.parent_id:
                if self.type in ("contact", "invoice", "private"):
                    parent_ur_id = self.parent_id.ur_id.id
                    vals["ur_id"] = parent_ur_id
    
            result = super(ScopPartner, self).write(vals)
    
            # Hack pour notification lors de la modification du logo
            if "image_128" in vals:
                self.message_post(
                    body=_("Modification Logo"),
                    subtype="cgscop_base.cg_values_change",
                )
    
            for partner in self:
                # Contrainte de tel ou mail lors de la modification d'un contact
                if (
                    not partner.is_company
                    and partner.type == "contact"
                    and not partner.user_ids
                ):
                    if not partner.email and not partner.phone and not partner.mobile:
                        raise ValidationError(
                            _(
                                "Vous devez saisir au moins un e-mail ou un téléphone\
                            pour "
                                + partner.name
                            )
                        )
    
                # Création d'une période lors du changement de statut en Suivi
                # Todo: A voir si le statut reste à celui là
                if vals.get("project_status") == "4_suivi" and not self.env.context.get(
                    "import_file"
                ):
                    if not partner.scop_period_ids:
                        partner._create_period(partner)
    
                # Ajout des followers de la fiche
                partners_to_subscribe = []
                if partner.followup_delegate_id != partner.create_uid:
                    partners_to_subscribe.append(partner.followup_delegate_id.partner_id.id)
                if partner.creation_delegate_id != partner.create_uid:
                    partners_to_subscribe.append(partner.creation_delegate_id.partner_id.id)
    
                if partners_to_subscribe:
                    partner.message_subscribe(partner_ids=partners_to_subscribe)
    
            return result
    
        # TODO: à revoir
        # Création d'une période lors de la création d'une coopérative
        @api.model_create_multi
        def create(self, vals_list):
            # Gestion casse des informations
            for vals in vals_list:
                if vals.get("name"):
                    vals["name"] = vals.get("name").title()
                if vals.get("lastname"):
                    vals["lastname"] = vals.get("lastname").title()
                if vals.get("firstname"):
                    vals["firstname"] = vals.get("firstname").title()
                if vals.get("city"):
                    vals["city"] = vals.get("city").upper()
    
            partners = super(ScopPartner, self).create(vals_list)
    
            for vals in vals_list:
                # Création d'une période si la coop est en statut en Suivi
                # Todo: A voir si le statut est bien celui là
                if vals.get("is_cooperative") and vals.get("project_status") == "4_suivi":
                    for partner in partners:
                        if not partner.scop_period_ids:
                            partner._create_period(partner)
    
            return partners
    
        @api.model
        def _address_fields(self):
            address_fields = super(ScopPartner, self)._address_fields()
            address_fields.append("cedex")
            return address_fields
    
        # ------------------------------------------------------
        # Override parent
        # ------------------------------------------------------
        def _get_contact_name(self, partner, name):
            super(ScopPartner, self)._get_contact_name(partner, name)
            return "%s, %s" % (
                name,
                partner.commercial_company_name or partner.sudo().parent_id.name,
            )
    
        # ------------------------------------------------------
        # Computed Fields
        # ------------------------------------------------------
        @api.depends("siret")
        def _compute_siren(self):
            for partner in self:
                if partner.siret:
                    partner.siren = (
                        partner.siret[:3]
                        + " "
                        + partner.siret[3:6]
                        + " "
                        + partner.siret[6:9]
                    )
                    partner.pappers_url = (
                        "https://www.pappers.fr/entreprise/" + partner.siret[:9]
                    )
                else:
                    partner.siren = None
                    partner.pappers_url = None
    
        def _compute_formatted_siret(self):
            for partner in self:
                if partner.siret:
                    partner.formatted_siret = (
                        partner.siret[:3]
                        + " "
                        + partner.siret[3:6]
                        + " "
                        + partner.siret[6:9]
                        + " "
                        + partner.siret[9:]
                    )
                else:
                    partner.formatted_siret = None
    
        @api.depends("zip", "state_id")
        def _compute_num_departement(self):
            for company in self:
                if company.zip:
                    if company.state_id:
                        try:
                            domtom = self.env.ref("cgscop_partner.domtom")
                            if company.state_id == domtom:
                                company.zip_departement = company.zip[:3]
                            else:
                                company.zip_departement = company.zip[:2]
                        except Exception:
                            company.zip_departement = company.zip[:2]
                    else:
                        company.zip_departement = company.zip[:2]
    
        @api.depends("zip")
        def _compute_region(self):
            for partner in self:
                if partner.zip:
                    zip_id = self.env["res.city.zip"].search([("name", "=", partner.zip)])
                    if zip_id:
                        partner.region = zip_id[0].city_id[0].state_id
    
        @api.model
        def _compute_current_user_ur_id(self):
            for partner in self:
                partner.current_user_ur_id = self.env.company.ur_id
    
        def _search_current_user_ur_id(self, operator, value):
            return [("ur_id", "=", self.env.company.ur_id.id)]
    
        @api.depends(
            "contact_origin_id",
            "parent_id.cooperative_form_id",
            "parent_id.membership_status",
        )
        def _compute_contact_legality(self):
            for partner in self:
                partner.contact_legality = "none"
                if partner.contact_origin_id.name == "Fiche contact, site internet":
                    partner.contact_legality = "consent"
                if partner.contact_origin_id.name in (
                    "Prospect journée d'info coll",
                    "Prospect (salon, rdv, internet…)",
                    "Elus",
                ):
                    partner.contact_legality = "legitimate"
                if partner.contact_origin_id.name in (
                    "Salariés CG",
                    "Salariés UR",
                    "Salariés Fédération",
                ):
                    partner.contact_legality = "employee"
                if partner.contact_origin_id.name in (
                    "Elus",
                    "VIP, Officiels",
                    "Fournisseurs",
                ):
                    partner.contact_legality = "legitimate"
                if not partner.is_company and partner.parent_id:
                    parent = partner.parent_id
                    if (
                        partner.contact_origin_id.name
                        in (
                            "Dossiers d'adhésion",
                            "Dossiers annuels non LM (scic, scop47)",
                        )
                        and parent.cooperative_form_id
                        and parent.membership_status == "member"
                    ):
                        partner.contact_legality = "customer"
                    if (
                        partner.contact_origin_id.name
                        == ("Dossiers annuels non LM (scic, scop47)")
                        and parent.membership_status != "member"
                    ):
                        partner.contact_legality = "legitimate"
                    if (
                        partner.contact_origin_id.name == ("Dossiers Liste ministère")
                        and parent.cooperative_form_id.name == "SCIC"
                    ):
                        partner.contact_legality = "customer"
    
        @api.depends("action_rse_ids")
        def _compute_is_rse(self):
            """
            Est on une coop RSE
            """
            for partner in self:
                if len(partner.action_rse_ids) != 0:
                    partner.is_rse = True
    
        @api.depends(
            "membership_period_ids",
            "membership_period_ids.end_reason_id",
            "membership_period_ids.end",
        )
        # Todo: A revoir comment on assigne le statut member
        def _compute_membership(self):
            for partner in self:
                if partner.membership_period_ids:
                    type_cg = self.env.ref("cgscop_partner.membership_type_1").id
                    last_membership_period = self.env["scop.membership.period"].search(
                        [("partner_id", "=", partner.id), ("type_id", "=", type_cg)],
                        limit=1,
                        order="start desc,id desc",
                    )
                    if last_membership_period and not last_membership_period.end:
                        partner.membership_status = "member"
                        partner.member_start_stat = last_membership_period.start_stat
                        partner.member_start = last_membership_period.start
                    elif last_membership_period and last_membership_period.end_reason_id:
                        partner.membership_status = "out"
                else:
                    partner.membership_status = "not_member"
    
        @api.depends("member_number_int")
        def _compute_membership_number(self):
            for partner in self:
                if partner.member_number_int:
                    partner.member_number = str(partner.member_number_int)
                else:
                    partner.member_number = False
    
        @api.depends("membership_period_ids", "membership_period_ids.end")
        def _compute_federation(self):
            for partner in self:
                if partner.is_cooperative:
                    partner.is_federation_com = partner._get_federation(
                        "cgscop_partner.membership_type_2"
                    )
                    partner.is_federation_btp = partner._get_federation(
                        "cgscop_partner.membership_type_4"
                    )
                    partner.is_federation_indus = partner._get_federation(
                        "cgscop_partner.membership_type_3"
                    )
                    partner.is_federation_cae = partner._get_federation(
                        "cgscop_partner.membership_type_5"
                    )
    
        def _get_federation(self, external_id):
            member_type_id = self.env.ref(external_id).id
            partner_id = self.id
            membership_period = self.env["scop.membership.period"].search(
                [
                    ("partner_id", "=", partner_id),
                    ("type_id", "=", member_type_id),
                    ("end", "=", False),
                ]
            )
            if membership_period:
                return True
            else:
                return False
    
        @api.depends(
            "staff_ids",
            "staff_ids.staff_count",
            "staff_ids.staff_shareholder_count",
            "staff_ids.effective_date",
        )
        def _compute_last_effective(self):
            for partner in self:
                lm = partner.staff_ids.search(
                    [["partner_id", "=", partner.id], ["staff_count", ">", 0]],
                    limit=1,
                    order="effective_date desc",
                )
                if lm:
                    partner.staff_last = lm[0].staff_count
                    partner.staff_shareholder_last = lm[0].staff_shareholder_count
                    partner.staff_last_date = lm[0].effective_date
    
        def _compute_segment_nb(self):
            for partner in self:
                # Calcul nombre de segment 1
                seg1 = partner.env["res.partner.segment1"].search(
                    [("ur_id", "=", self.env.user.ur_id.id)]
                )
                partner.segment_1_nb = len(seg1)
                # Calcul nombre de segment 2
                seg2 = partner.env["res.partner.segment2"].search(
                    [("ur_id", "=", self.env.user.ur_id.id)]
                )
                partner.segment_2_nb = len(seg2)
                # Calcul nombre de segment 3
                seg3 = partner.env["res.partner.segment3"].search(
                    [("ur_id", "=", self.env.user.ur_id.id)]
                )
                partner.segment_3_nb = len(seg3)
                # Calcul nombre de segment 4
                seg4 = partner.env["res.partner.segment4"].search(
                    [("ur_id", "=", self.env.user.ur_id.id)]
                )
                partner.segment_4_nb = len(seg4)
    
        @api.depends("organization_subtype_id")
        def _compute_org_type_id(self):
            for partner in self:
                if partner.organization_subtype_id:
                    partner.organization_type_id = partner.organization_subtype_id.parent_id
                else:
                    partner.organization_type_id = False
    
        def _compute_is_administrative(self):
            for partner in self:
                if self.env.user.has_group("cgscop_partner.group_cg_administrative"):
                    partner.is_administrative = True
                else:
                    partner.is_administrative = False
    
        @api.depends("revision_next_exercice")
        def _compute_revision_next_year(self):
            for partner in self:
                partner.revision_next_year = partner.revision_next_exercice + 1
    
        @api.depends(
            "revision_type",
            "revision_ids",
            "revision_ids.revision_result_year",
            "first_closeout",
        )
        def _compute_revision_next_exercice(self):
            for partner in self:
                # Si aucune périodicité de défini, on n'insiste pas
                if not partner.revision_type or not partner.first_closeout:
                    partner.revision_next_exercice = False
                else:
                    # On commence par regarder si l'on a des révisions
                    last_rev = partner.revision_ids.sorted(
                        key=lambda r: r.revision_result_year, reverse=True
                    )
    
                    # On calcule l'année de référence du calcul
                    if len(last_rev) > 0:
                        # si On a déjà révisé un exercice il devient la base du calcul
                        base_rev = last_rev[0].revision_result_year
                    else:
                        base_rev = partner.first_closeout.year - 1
    
                    # On calcule le prochain exercice révisable
                    # Cas d'une révision annuelle
                    if partner.revision_type == "1y":
                        partner.revision_next_exercice = base_rev + 1
                    # Cas d'une révision quinquénnale
                    elif partner.revision_type == "5y":
                        partner.revision_next_exercice = base_rev + 5
                    # Cas d'une révision quinquénnale séquencée (annuelle)
                    elif partner.revision_type == "5ys":
                        partner.revision_next_exercice = base_rev + 1
                    # Cas d'une révision quinquénnale séquencée (2 et 3)
                    elif partner.revision_type == "5ys23":
                        # On doit regarder l'écart avec la révision précédente
                        if len(last_rev) > 1:
                            # On a une réunion précédente ...
                            # ... il faut regarder l'écart entre les deux
                            ex1 = last_rev[0].revision_result_year
                            ex2 = last_rev[1].revision_result_year
                            # ... prochain exercice = 5 - durée de la précédente révision
                            partner.revision_next_exercice = base_rev + (5 - (ex1 - ex2))
                        else:
                            # Pas de révision précédente
                            partner.revision_next_exercice = base_rev + 2
    
        # ------------------------------------------------------
        # Button & Action
        # ------------------------------------------------------
        def scop_send_to_cg(self):
            # Todo: A vérifier pour le statut suivi car déjà en statut suivi normalement
            self.write(
                {
                    "project_status": "4_suivi",
                    "membership_status": "soumis_cg",
                }
            )
            return True
    
        def partner_archive(self):
            self.active = False
    
        def scop_valid_cg_button(self):
            self.ensure_one()
            self.scop_valid_cg()
    
        def scop_valid_cg(self):
            # Todo: A confirmer qu'on passe bien au statut member?
            self.write(
                {
                    "membership_status": "member",
                }
            )
            return True
    
        def scop_prj_adhesion(self):
            self.write({"membership_status": "adhesion"})
            return True
    
        def scop_abandonne(self):
            # Todo: A vérifier pour le statut member?
            self.write(
                {
                    "project_status": "5_abandonne",
                }
            )
            return True
    
        def add_director(self):
            return {
                "type": "ir.actions.act_window",
                "views": [
                    [
                        self.env.ref("cgscop_partner.scop_partner_director_form_view").id,
                        "form",
                    ]
                ],
                "view_mode": "form",
                "res_model": "res.partner",
                "target": "new",
                "context": {
                    "default_parent_id_onchange": self.id,
                    "default_street": self.street,
                    "default_street2": self.street2,
                    "default_street3": self.street3,
                    "default_city": self.city,
                    "default_city_id": self.city_id.id,
                    "default_cedex": self.cedex,
                    "default_state_id": self.state_id.id,
                    "default_zip": self.zip,
                    "default_country_id": self.country_id.id,
                    "default_lang": self.lang,
                    "default_user_id": self.user_id.id,
                    "ur_id": self.ur_id.id,
                    "default_type": "contact",
                },
            }
    
        def edit_director(self):
            return {
                "type": "ir.actions.act_window",
                "views": [
                    [
                        self.env.ref("cgscop_partner.scop_partner_director_form_view").id,
                        "form",
                    ]
                ],
                "view_mode": "form",
                "res_model": "res.partner",
                "res_id": self.id,
                "target": "new",
            }
    
        def add_contact(self):
            return {
                "type": "ir.actions.act_window",
                "views": [
                    [
                        self.env.ref("cgscop_partner.scop_partner_contact_form_view").id,
                        "form",
                    ]
                ],
                "view_mode": "form",
                "res_model": "res.partner",
                "target": "new",
                "context": {
                    "default_parent_id_onchange": self.id,
                    "default_street": self.street,
                    "default_street2": self.street2,
                    "default_street3": self.street3,
                    "default_city": self.city,
                    "default_city_id": self.city_id.id,
                    "default_cedex": self.cedex,
                    "default_state_id": self.state_id.id,
                    "default_zip": self.zip,
                    "default_country_id": self.country_id.id,
                    "default_lang": self.lang,
                    "default_user_id": self.user_id.id,
                },
            }
    
        def edit_contact(self):
            return {
                "type": "ir.actions.act_window",
                "views": [
                    [
                        self.env.ref("cgscop_partner.scop_partner_contact_form_view").id,
                        "form",
                    ]
                ],
                "view_mode": "form",
                "res_model": "res.partner",
                "res_id": self.id,
                "target": "new",
            }
    
        def write_contact(self):
            return self
    
        def show_coop(self):
            """
            Affichage des coop avec filtre par défaut
            """
            ctx = {
                "default_is_company": True,
                "default_is_cooperative": True,
                "default_company_type": "company",
                "default_project_status": "4_suivi",
            }
    
            # Détermine le filtre par défaut pour l'affichage
            filtre = self.env.company.ur_id.partner_filter
            if filtre == "1":
                ctx.update({"search_default_is_adherent": True})
            elif filtre == "2":
                ctx.update({"search_default_my_ur_adherent": True})
            elif filtre == "3":
                ctx.update({"search_default_is_federation_com": True})
            elif filtre == "4":
                ctx.update({"search_default_is_federation_indus": True})
            elif filtre == "5":
                ctx.update({"search_default_my_is_federation_btp": True})
            elif filtre == "6":
                ctx.update({"search_default_my_is_federation_cae": True})
    
            return {
                "name": "Coopératives",
                "type": "ir.actions.act_window",
                "res_model": "res.partner",
                "search_view_id": (
                    self.env.ref("cgscop_partner.scop_partner_view_search").id,
                ),
                "view_mode": "tree,form,activity",
                "views": [
                    (
                        self.env.ref("cgscop_partner.view_partner_cooperative_tree").id,
                        "tree",
                    ),
                    (
                        self.env.ref("cgscop_partner.scop_contact_view_form").id,
                        "form",
                    ),
                ],
                "target": "current",
                "domain": [
                    ("is_cooperative", "=", True),
                    ("project_status", "=", "4_suivi"),
                ],
                "context": ctx,
            }
    
        def show_creation_project(self):
            """
            Affichage des projets en création avec filtre par défaut
            """
            ctx = {
                "default_is_company": True,
                "default_is_cooperative": True,
                "default_company_type": "company",
                "default_project_status": "1_information",
            }
    
            # Détermine le filtre par défaut pour l'affichage
            filtre = self.env.company.ur_id.partner_filter
            if filtre == "2":
                ctx.update({"search_default_my_ur": True})
    
            return {
                "name": "Projets de création",
                "type": "ir.actions.act_window",
                "res_model": "res.partner",
                "search_view_id": (
                    self.env.ref("cgscop_partner.scop_partner_view_search").id,
                ),
                "view_mode": "kanban,tree,form,activity",
                "views": [
                    (
                        self.env.ref("cgscop_partner.view_partner_cooperative_kanban").id,
                        "kanban",
                    ),
                    (
                        self.env.ref("cgscop_partner.view_partner_prospect_tree").id,
                        "tree",
                    ),
                    (
                        self.env.ref("cgscop_partner.scop_contact_view_form").id,
                        "form",
                    ),
                ],
                "target": "current",
                "domain": [
                    ("is_cooperative", "=", True),
                    (
                        "project_status",
                        "in",
                        (
                            "1_information",
                            "2_pre-diagnostic",
                            "3_accompagnement",
                            "5_abandonne",
                        ),
                    ),
                ],
                "context": ctx,
            }
    
        def show_organisme(self):
            """
            Affichage des organismes avec filtre par défaut
            """
            ctx = {
                "default_is_company": True,
                "default_is_cooperative": True,
                "default_company_type": "company",
                "default_project_status": "1_information",
            }
    
            # Détermine le filtre par défaut pour l'affichage
            filtre = self.env.company.ur_id.partner_filter
            if filtre == "2":
                ctx.update({"search_default_my_ur_adherent": True})
    
            return {
                "name": "Tous les organismes",
                "type": "ir.actions.act_window",
                "res_model": "res.partner",
                "search_view_id": (
                    self.env.ref("cgscop_partner.scop_partner_view_search").id,
                ),
                "view_mode": "tree,form,activity",
                "views": [
                    (
                        self.env.ref("cgscop_partner.view_partner_cooperative_tree").id,
                        "tree",
                    ),
                    (
                        self.env.ref("cgscop_partner.scop_contact_view_form").id,
                        "form",
                    ),
                ],
                "target": "current",
                "domain": [
                    ("is_cooperative", "=", True),
                    (
                        "project_status",
                        "in",
                        ("4_suivi",),
                    ),
                ],
                "context": ctx,
            }
    
        def show_processus_adhesion(self):
            """
            Affichage des coopératives dans leur processus d'adhésion par défaut
            """
            ctx = {
                "default_is_company": True,
                "default_is_cooperative": True,
                "default_company_type": "company",
                "default_project_status": "4_suivi",
            }
    
            # Détermine le filtre par défaut pour l'affichage
            filtre = self.env.company.ur_id.partner_filter
            if filtre == "2":
                ctx.update({"search_default_my_ur_adherent": True})
    
            return {
                "name": "Processus d'adhésion",
                "type": "ir.actions.act_window",
                "res_model": "res.partner",
                "search_view_id": (
                    self.env.ref("cgscop_partner.scop_partner_view_search").id,
                ),
                "view_mode": "kanban,tree,form,activity",
                "views": [
                    (
                        self.env.ref("cgscop_partner.view_partner_adhesion_kanban").id,
                        "kanban",
                    ),
                    (
                        self.env.ref("cgscop_partner.view_partner_prospect_tree").id,
                        "tree",
                    ),
                    (
                        self.env.ref("cgscop_partner.scop_contact_view_form").id,
                        "form",
                    ),
                ],
                "target": "current",
                "domain": [
                    ("is_cooperative", "=", True),
                    (
                        "membership_status",
                        "in",
                        ("adhesion", "soumis_cg"),
                    ),
                ],
                "context": ctx,
            }
    
        # ------------------------------------------------------
        # CRON function
        # ------------------------------------------------------
        def _cron_geoloc_data_gouv(self, days=1):
            # Récupération des valeurs de suivi sur zip/city/street de la veille
            yesterday = fields.Date.today() - timedelta(days=days)
            partner_model = self.env["ir.model"].search([("model", "=", "res.partner")])
            address_values = self.env["ir.model.fields"].search(
                [
                    ("model_id", "=", partner_model.id),
                    ("name", "in", ["street", "street2", "zip", "city"]),
                ]
            )
            mail_tracking_value_ids = self.env["mail.tracking.value"].search(
                [
                    ("field", "in", address_values.ids),
                    ("create_date", ">=", yesterday),
                ]
            )
    
            # Récupération des messages de notif sur
            # res.partner en lien avec les valeurs de suivi
            mail_mess_ids = self.env["mail.message"].search(
                [
                    ("model", "=", "res.partner"),
                    ("message_type", "=", "notification"),
                    ("tracking_value_ids", "in", mail_tracking_value_ids.ids),
                ]
            )
    
            partner_list = mail_mess_ids.mapped("res_id")
    
            # Récupération des partners pour calcul des données GPS
            partners = self.env["res.partner"].search(
                [
                    "|",
                    "|",
                    ("id", "in", partner_list),
                    ("partner_latitude", "=", 0.0),
                    ("partner_latitude", "=", False),
                    ("membership_status", "=", "member"),
                ]
            )
    
            i = 0
            for partner in partners:
                partner.geo_localize()
                i += 1
                time.sleep(1)
            _logger.info("Mise à jour de %d coordonnées", i)