diff --git a/README.rst b/README.rst index f5b6d70af1870b42c35e07c385acb67fd2348f18..d0ccc88241c6992a1c284f64949ba287125cf09c 100644 --- a/README.rst +++ b/README.rst @@ -38,3 +38,4 @@ Maintainer :target: https://le-filament.com This module is maintained by Le Filament + diff --git a/models/mucs_commercial_event.py b/models/mucs_commercial_event.py index f204e1136ffae59b481767c8c8fd20fc55ce9484..5b3ef7d74887e300def217844db8cce59f1efc4e 100644 --- a/models/mucs_commercial_event.py +++ b/models/mucs_commercial_event.py @@ -10,7 +10,7 @@ class MucsCommercialEvent(models.Model): _order = "date desc" _rec_name = "partner_id" - date = fields.Date(required=True) + date = fields.Date("Date",default=fields.Date.context_today, required=True) partner_id = fields.Many2one( comodel_name="res.partner", string="Entreprise", @@ -18,7 +18,7 @@ class MucsCommercialEvent(models.Model): required=True, ) name = fields.Char("Sujet") - description = fields.Html() + description = fields.Text("Description", default="_trame_rdv") # TODO tags ? # categ_ids = fields.Many2one("X") @@ -53,3 +53,19 @@ class MucsCommercialEvent(models.Model): # ------------------------------------------------------ # Business methods # ------------------------------------------------------ + + def _trame_rdv(_): + return """RDV (D : distanciel, P : présentiel - à préciser -) en date du … + + En présence de : + … + + Ordre du jour : + … + + Contenu des échanges : + … + + Suites à donner : + … + """ diff --git a/models/mucs_support_event.py b/models/mucs_support_event.py index c345eab00e448798ffe28dccd3cf06544ab7340a..c7d56bc51ea708757091897269851b9a001a7f7b 100644 --- a/models/mucs_support_event.py +++ b/models/mucs_support_event.py @@ -17,10 +17,17 @@ class MucsSupportEvent(models.Model): domain="[('is_company', '=', True)]", required=True, ) - qs = fields.Selection(string="Qualité de service", selection=[("s", "Satisfait")]) - theme = fields.Char("Thématique") - topic = fields.Text("Sujet") - resolution = fields.Text("Résolution") + date = fields.Date("Date",default=fields.Date.context_today) + qs = fields.Selection(string="Qualité de service", + selection=[("s", "Satisfait"), ("none", "Sans avis"), + ("i", "Insatisfait"), ("m", "Mécontent")]) + theme = fields.Selection(string="Thématique", selection="_theme") + + topic = fields.Selection(string="Sujet", selection="_topic") + resolution = fields.Selection(string="Résolution", selection="_resolution") + + # relations + partner_id = fields.Many2one(comodel_name="res.partner") # ------------------------------------------------------ # Fields declaration @@ -53,3 +60,78 @@ class MucsSupportEvent(models.Model): # ------------------------------------------------------ # Business methods # ------------------------------------------------------ + + + def _theme(_): + return [("cotis", "COTISATIONS"), + ("tarif", "TARIFICATION"), + ("modif_contrat_entreprise", "MODIF CONTRAT ENTREPRISE"), + ("modif_contrat", "MODIF CONTRAT"), + ("adhesion_radiation", "ADHESION / RADIATION ADHERENT"), + ("modif_contrat_situ", "MODIF CONTRAT / SITU ADHERENT"), + ("pb_presta_adherent", "PB PRESTATION ADHERENT"), + ("info_offre", "INFO OFFRE CONTRAT"), + ("carte_tiers_payant", "CARTE TIERS PAYANT"), + ("connexion", "CONNEXION / ESPACE ADHERENT"), + ("prevoyance", "PREVOYANCE"), + ("mise_en_place", "MISE EN PLACE CONTRAT")] + + + def _topic(_): + return [("A", "Appel de cotisations non reçu"), + ("B", + "Adhérent pas encore present sur l'appel de cotisation (adhésion enregsitrée après édition de l'appel de cotisation)"), + ("C", + "Adhérent toujours present sur l'appel de cotisation (radiation enregsitrée après édition de l'appel de cotisation)"), + ("D", "Ecart de cotisations"), + ("E", "Demande Etat de compte de l'entreprise"), + ("F", "Modif adh/contrat/ RIB non enregistrée dans IZY"), + ("G", "Process adhésion salarié/bénéficiaire"), + ("H", "Process offre contrat entreprise / nouvelle entitée"), + ("I", "Process continuité de contrat d'un salarié sortant"), + ("J", "Process mise en place du contrat entreprise"), + ("K", "Info tarification des cotisations"), + ("L", "Process potabilité"), + ("M", "Process Naissance / Décès"), + ("N", "Process Cas de dispense"), + ("O", "Explication devis contrat entreprise reçu"), + ("P", "Demande devis entreprise"), + ("Q", "Relance devis enreprise"), + ( + "R", + "Relance ajout /Retrait de bénéficiaire non enregistrée dans IZY"), + ("S", "Relance adhésion/Radiation non enregistrée dans IZY"), + ("T", "Relance remboursement d'un salarié (prestation)"), + ("U", "Facture refusée, motif non compris"), + ("V", + "Probleme de remboursement (prestation adh) justif en attente / teletransmission"), + ("W", "Probleme de devis (prestation adh) reseau Kalixia"), + ("X", "Probleme espace adhérent 1ere connexion"), + ("Y", "Probleme espace adhérent modif mdp"), + ("Z", "Probleme facturation cotisations adhérent"), + ("0", "Erreur intégration adhérent sur IZY"), + ("1", "Fiche DSN"), + ("2", "IMPAYES / Contentieux"), + ("3", "Demande envoi bulletin admin / doc à jour"), + ("4", "PREVOYANCE"), + ("5", "Carte non dispo, erronée, demande renvoi"), + ("6", "Connexion impossible, pb d'affichage…"), + ("7", "En attente …"), + ("8", "Demande rappel du commercial"), + ("9", "DUE") + ] + + + def _resolution(_): + return [("A", "RENVOI SERVICE COMPTA / COMMERCIAL …"), + ("B", "DOC ENVOYE PAR MAIL LE JOUR J"), + ("C", "EXPLICATION / RESOLUTION EN DIRECT "), + ("D", "PB NOTIFIE ET RETOUR PAR MAIL DES SOLUTIONS TROUVE"), + ("E", "DELAI NON RESPECTE (D'ENREGISTREMENT DANS IZY)"), + ("F", "EN COURS DE TRAITEMENT (DELAI D'ENREGISTREMENT CORRECT)"), + ("G", "BULLETIN ADMINSITRATIF NON RECU"), + ("H", "BULLETIN ADMINSITRATIF RECU NON TRAITE"), + ("I", "DEMANDE RENVOI BULLETIN ADMIN"), + ("J", "RELANCE / RECTIFICATION DEMANDER A HARMONIE"), + ("K", "APPEL PERDU / RAPPEL"), + ] diff --git a/models/res_partner.py b/models/res_partner.py index aec5715f694ddfe21c5781819211c2f66449e464..2ae1c532feca74cde8778c94ac1d96686c5f09fd 100644 --- a/models/res_partner.py +++ b/models/res_partner.py @@ -13,8 +13,14 @@ class ResPartner(models.Model): is_cooperative = fields.Boolean("Est une coopérative") is_member_cgscop = fields.Boolean("Est adhérent à la CG SCOP") - company_size_id = fields.Many2one("mucs.company.size", string="Tranche d'effectif") + company_size_id = fields.Many2one("mucs.company.size", + string="Tranche d'effectif") company_size = fields.Integer(string="Effectif communiqué") + mucs_comment = fields.Text(string="Descriptif activité") + # TODO import these dates + date_coop = fields.Date(string="Date d'inscription en coopérative") + date_registration = fields.Date( + string="Date d'enregistrement de l'entreprise") # Note : peut-être utiliser l10n-france/l10n_fr_siret_lookup # en 18.0 qui gère la récupération des codes APE et forme juridique @@ -34,14 +40,16 @@ class ResPartner(models.Model): ccn_idcc = fields.Char("CCN Code", related="ccn_id.idcc") ccn_titre = fields.Char("CCN Label", related="ccn_id.titre") - is_member_sante = fields.Boolean( - "Adhérent Santé", compute="_compute_is_member_sante", store=True - ) - is_member_unmi = fields.Boolean("Adhérent UNMI") - is_member_mutex = fields.Boolean("Adhérent MUTEX") - type_rma_aid = fields.Selection( - string="Assistance RMA", selection=[("base", "Base")] - ) + is_member_sante = fields.Boolean(string="Adhérent Santé", + compute="_compute_is_member_sante", + store=True) + is_member_unmi = fields.Boolean(string="Adhérent UNMI") + is_member_mutex = fields.Boolean(string="Adhérent MUTEX") + type_rma_aid = fields.Selection(string="Assistance RMA", + selection=[("base", "Base"), + ("renforcee", "Renforcée"), + ("neant", "Néant"), + ("nsp", "NSP")]) first_join_date = fields.Date( string="Date de première adhésion", @@ -85,7 +93,6 @@ class ResPartner(models.Model): inverse_name="partner_id", string="Contrats", ) - # child_ids (odoo) Contacts meeting_ids = fields.One2many( string="Suivi commercial", comodel_name="mucs.commercial.event", @@ -97,6 +104,33 @@ class ResPartner(models.Model): inverse_name="partner_id", ) + # computed counters ----- + + lead_count = fields.Integer("Nombre d'opportunités", + compute="_compute_lead_count", store=True) + proposition_count = fields.Integer("Nombre de propositions", + compute="_compute_lead_count", + store=True) + amendment_count = fields.Integer("Nombre d'avenants", + compute="_compute_lead_count", store=True) + + meeting_count = fields.Integer("Nombre de rdv de suivi commercial", + compute="_compute_meeting_count", store=True) + support_count = fields.Integer("Nombre d'événements de gestion", + compute="_compute_support_count", + store=True) + contract_count = fields.Integer("Nombre de contrats", + compute="_compute_contract_count", + store=True) + + date_last_support = fields.Date( + "Date du dernier support", + compute="_compute_date_last_support", + store=True) + date_last_meeting = fields.Date("Date du dernier rendez-vous commercial", + compute="_compute_date_last_meeting", + store=True) + # ------------------------------------------------------ # SQL Constraints # ------------------------------------------------------ @@ -108,12 +142,53 @@ class ResPartner(models.Model): # ------------------------------------------------------ # Computed fields / Search Fields # ------------------------------------------------------ + @api.depends("siret") def _compute_siren(self): for partner in self: if partner.siret: partner.siren = partner.siret.replace(" ", "")[0:9] + @api.depends("lead_ids") + def _compute_lead_count(self): + for i in self: + i.lead_count = len(i.lead_ids) + i.proposition_count = i.lead_ids.filtered( + lambda x: x.mucs_type == 'proposition') + i.amendment_count = i.lead_ids.filtered( + lambda x: x.mucs_type == 'avenant') + + @api.depends("meeting_ids") + def _compute_meeting_count(self): + for i in self: + i.meeting_count = len(i.meeting_ids) + + @api.depends("support_ids") + def _compute_support_count(self): + for i in self: + i.support_count = len(i.support_ids) + + @api.depends("contract_ids") + def _compute_contract_count(self): + for i in self: + i.contract_count = len(i.contract_ids) + + @api.depends("support_ids") + def _compute_date_last_support(self): + for i in self: + if not i.support_ids: + continue + i.date_last_support = \ + i.support_ids.sorted("date", reverse=True)[0].date + + @api.depends("meeting_ids") + def _compute_date_last_meeting(self): + for i in self: + if not i.meeting_ids: + continue + i.date_last_meeting = i.meeting_ids.sorted("date", reverse=True)[ + 0].date + # TODO: valider la fonction @api.depends("contract_ids", "contract_ids.date_end") def _compute_is_member_sante(self): @@ -130,9 +205,10 @@ class ResPartner(models.Model): def _compute_first_join_date(self): for partner in self: if partner.contract_ids: - partner.first_join_date = partner.contract_ids.sort("date_start")[ - 0 - ].date_start + partner.first_join_date = \ + partner.contract_ids.sort("date_start")[ + 0 + ].date_start # ------------------------------------------------------ # Constraints diff --git a/views/res_partner.xml b/views/res_partner.xml index 9b126146b550abfdbda6ac845c93c47fa174b86f..e7a103e9c9e768a104a9575c45b9db43f8d78944 100644 --- a/views/res_partner.xml +++ b/views/res_partner.xml @@ -2,6 +2,8 @@ <!-- Copyright 2024- Le Filament (https://le-filament.com) License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). --> <odoo> + <!-- View --> + <!-- Form --> <record id="view_partner_form_inherit" model="ir.ui.view"> <field name="name">res.partner.form.inherit</field> @@ -47,6 +49,18 @@ invisible="not is_company" autofocus="autofocus" > + <!-- wip --> + <group invisible="True"> + <field name="lead_count"/> + <field name="proposition_count"/> + <field name="amendment_count"/> + <field name="meeting_count"/> + <field name="support_count"/> + <field name="contract_count"/> + <field name="date_last_support"/> + <field name="date_last_meeting"/> + </group> + <group> <group> <field @@ -63,9 +77,9 @@ <field name="company_size" /> <field name="company_form_id" /> <field name="naf_id" string="APE" /> - <!--<field name="ccn_id"/>--> - <field name="ccn_idcc" string="Numéro CCN" /> - <field name="ccn_titre" string="Intitulé" /> + <field name="ccn_id"/> +<!-- <field name="ccn_idcc" string="Numéro CCN" />--> +<!-- <field name="ccn_titre" string="Intitulé" />--> </group> <group> <field name="is_cooperative" /> @@ -93,6 +107,9 @@ <field name="social_object" nolabel="1" /> </group> </group> + <group string="Descriptif Activité"> + <field name="mucs_comment" nolabel="1"/> + </group> </page> <!-- environnement --> @@ -125,7 +142,7 @@ <separator /> <group string="Contrats"> <field name="contract_ids" nolabel="1"> - <list> + <list editable="top"> <field name="number" /> <field name="type" /> <field name="member_count" /> @@ -146,9 +163,9 @@ > <group string="Synthèse activité développement"> <field name="meeting_ids" nolabel="1"> - <list> - <field name="date" /> - <field name="name" /> + <list editable="top"> + <field name="date"/> + <field name="name"/> </list> </field> </group> @@ -161,7 +178,7 @@ > <group string="Synthèse activité gestion"> <field name="support_ids" nolabel="1"> - <list> + <list editable="top"> <field name="date" /> <field name="qs" /> <field name="theme" /> @@ -171,6 +188,16 @@ </field> </group> </page> + <!-- leads --> + <page + name="leads" + string="Opportunités" + invisible="not is_company" + > + <group string="Leads"> + <field name="lead_ids" nolabel="1"/> + </group> + </page> </xpath> </field> </record> @@ -187,6 +214,7 @@ <field name="city" optional="show" /> <field name="ur_id" optional="show" /> <field name="state_id" optional="show" /> + <field name="country_department_id" optional="hide"/> <field name="naf_id" optional="show" /> <field name="ccn_id" optional="show" /> <field name="is_cooperative" optional="show" /> @@ -333,12 +361,18 @@ icon="fa-check-square-o" enable_counters="1" /> + <field name="ccn_id"/> + <field name="state_id"/> </searchpanel> </search> </field> </record> <!-- Action --> + <record id="contacts.action_contacts" model="ir.actions.act_window"> + <field name="domain">[('is_cooperative', '!=', True)]</field> + </record> + <record id="coop_partner_action" model="ir.actions.act_window"> <field name="name">Coopératives</field> <field name="type">ir.actions.act_window</field>