diff --git a/README.rst b/README.rst index 4634ba4d613b72ca3212605f233d2d8b11af582a..54cfa5d653dfc52844aea6d43330a68adff307e9 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/meeting_event.py b/models/meeting_event.py index 6ea798b4284df998b16c14640e56d439c300f4f7..e2f00420e97a5a078520f2adff48f95289fa6a5f 100644 --- a/models/meeting_event.py +++ b/models/meeting_event.py @@ -1,17 +1,16 @@ # Copyright 2024- Le Filament (https://le-filament.com) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) -from odoo import models, fields, api +from odoo import models, fields class MeetingEvent(models.Model): _name = "meeting.event" + _order = "date desc" - date = fields.Date("Date") + date = fields.Date("Date",default=fields.Date.context_today) name = fields.Char("Sujet") - description = fields.Html("Description") - # TODO tags ? - # categ_ids = fields.Many2one("X") + description = fields.Text("Description", default="_trame_rdv") # relations partner_id = fields.Many2one(comodel_name="res.partner") @@ -47,3 +46,19 @@ class MeetingEvent(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/mgmt_event.py b/models/mgmt_event.py index 482445a8997398fa3b10392b2ff0be62f74c509f..73d6e07f0b243db3d8fa584a796d895afe3ffb51 100644 --- a/models/mgmt_event.py +++ b/models/mgmt_event.py @@ -1,17 +1,21 @@ # Copyright 2024- Le Filament (https://le-filament.com) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) -from odoo import models, fields, api +from odoo import models, fields class MgmtEvent(models.Model): _name = "mgmt.event" + _order = "date desc" - date = fields.Date("Date") - qs = fields.Selection(string="Qualité de service", selection=[("s", "Satisfait")]) - theme = fields.Char("Thématique") - topic = fields.Html("Sujet") - resolution = fields.Char("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") @@ -47,3 +51,78 @@ class MgmtEvent(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 8942e6437a32e1ba13e78b8bde8167d0c995adf5..b9827d4f1330ebf4a180b1db6f39bf4a5faed88a 100644 --- a/models/res_partner.py +++ b/models/res_partner.py @@ -2,7 +2,6 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) from odoo import models, fields, api -from odoo.exceptions import ValidationError class ResPartner(models.Model): @@ -17,6 +16,11 @@ class ResPartner(models.Model): company_size_id = fields.Many2one("company.size", string="Tranche d'effectif") company_size = fields.Integer(string="Effectif approximatif") + 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 @@ -33,12 +37,19 @@ 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") - is_member_unmi = fields.Boolean("Adhérent UNMI") - is_member_mutex = fields.Boolean("Adhérent MUTEX") + is_member_sante = fields.Selection(selection="_ternary", + string="Adhérent Santé", + # compute="_compute_is_member_sante" + ) + is_member_unmi = fields.Selection(selection="_ternary", + string="Adhérent UNMI") + is_member_mutex = fields.Selection(selection="_ternary", + string="Adhérent MUTEX") type_rma_aid = fields.Selection(string="Assistance RMA", - selection=[("base", "Base")]) + selection=[("base", "Base"), + ("renforcee", "Renforcée"), + ("neant", "Néant"), + ("nsp", "NSP")]) first_join_date = fields.Date("Date de première adhésion") membership = fields.Boolean("Adhésion") @@ -66,7 +77,7 @@ class ResPartner(models.Model): federation_ids = fields.Many2many(comodel_name="res.partner.federation", string="Fédérations") - contract_id = fields.One2many( + contract_ids = fields.One2many( string="Contrats", comodel_name="mucs.contract", inverse_name="partner_id", @@ -88,6 +99,32 @@ 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) + mgmt_event_count = fields.Integer("Nombre d'événements de gestion", + compute="_compute_mgmt_event_count", + store=True) + contract_count = fields.Integer("Nombre de contrats", + compute="_compute_contract_count", + store=True) + + date_last_mgmt_event = fields.Date("Date du dernier événement de gestion", + compute="_compute_date_last_mgmt_event", + store=True) + date_last_meeting = fields.Date("Date du dernier rendez-vous commercial", + compute="_compute_date_last_meeting", + store=True) + # ------------------------------------------------------ # SQL Constraints # ------------------------------------------------------ @@ -106,12 +143,54 @@ class ResPartner(models.Model): if partner.siret: partner.siren = partner.siret.replace(" ", "")[0:9] - # TODO compute this - @api.depends("contract_id") - def _compute_is_member_sante(self): - for partner in self: - partner.is_member_sante = True - pass + # # TODO compute this + # @api.depends("contract_id") + # def _compute_is_member_sante(self): + # for partner in self: + # partner.is_member_sante = "nsp" + # si on fait la moindre faute de frappe sur la valeur de retour d'une string + # selection, ça passe sans problème jusqu'à ce qu'on tombe sur la vue et là + # on a une erreur owl obscure totalement absurde et non liée + + @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("mgmt_event_ids") + def _compute_mgmt_event_count(self): + for i in self: + i.mgmt_event_count = len(i.mgmt_event_ids) + + @api.depends("contract_ids") + def _compute_contract_count(self): + for i in self: + i.contract_count = len(i.contract_ids) + + @api.depends("mgmt_event_ids") + def _compute_date_last_mgmt_event(self): + for i in self: + if not i.mgmt_event_ids: + continue + i.date_last_mgmt_event = \ + i.mgmt_event_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 # ------------------------------------------------------ # Onchange / Constraints @@ -122,7 +201,7 @@ class ResPartner(models.Model): for record in self: if record.is_cooperative and not record.is_company: # raise ValidationError("An individual can not be a cooperative") - pass # gênant pour les contacts créés à partir d'une coopérative + pass # gênant pour les contacts créés à partir d'une coopérative # ------------------------------------------------------ # CRUD methods (ORM overrides) @@ -135,3 +214,6 @@ class ResPartner(models.Model): # ------------------------------------------------------ # Business methods # ------------------------------------------------------ + + def _ternary(_): + return [("oui", "Oui"), ("non", "Non"), ("nsp", "NSP"), ] diff --git a/views/res_partner.xml b/views/res_partner.xml index 8566acdf1049705b969e6f72fa3bc78b7776b9bc..f8898a3c21829c52ac10e53e4e1c484dbf191494 100644 --- a/views/res_partner.xml +++ b/views/res_partner.xml @@ -33,9 +33,9 @@ <xpath expr="//page[@name='sales_purchases']" position="attributes"> <attribute name="invisible">True</attribute> </xpath> - <xpath expr="//page[@name='internal_notes']" position="attributes"> - <attribute name="invisible">True</attribute> - </xpath> + <!-- <xpath expr="//page[@name='internal_notes']" position="attributes">--> + <!-- <attribute name="invisible">True</attribute>--> + <!-- </xpath>--> <!-- focus on company details instead --> <xpath expr="//page[@name='contact_addresses']" position="attributes"> @@ -50,6 +50,18 @@ invisible="not is_company" autofocus="autofocus" > + <!-- wip --> + <group invisible="False"> + <field name="lead_count"/> + <field name="proposition_count"/> + <field name="amendment_count"/> + <field name="meeting_count"/> + <field name="mgmt_event_count"/> + <field name="contract_count"/> + <field name="date_last_mgmt_event"/> + <field name="date_last_meeting"/> + </group> + <group> <group> <!--<field name="siret"/>--> @@ -77,7 +89,7 @@ </group> </group> <group string="Descriptif Activité"> - <field name="comment" nolabel="1"/> + <field name="mucs_comment" nolabel="1"/> </group> </page> @@ -110,8 +122,8 @@ </group> <separator/> <group string="Contrats"> - <field name="contract_id" nolabel="1"> - <list> + <field name="contract_ids" nolabel="1"> + <list editable="top"> <field name="number"/> <field name="type"/> <field name="member_count"/> @@ -122,7 +134,7 @@ </page> </xpath> - <!-- suivi + opportunités + leads --> + <!-- suivi + opportunités --> <xpath expr="//page[@name='contact_addresses']" position="after"> <!-- suivi commercial--> <page @@ -132,7 +144,7 @@ > <group string="Synthèse activité développement"> <field name="meeting_ids" nolabel="1"> - <list> + <list editable="top"> <field name="date"/> <field name="name"/> </list> @@ -147,7 +159,7 @@ > <group string="Synthèse activité gestion"> <field name="mgmt_event_ids" nolabel="1"> - <list> + <list editable="top"> <field name="date"/> <field name="qs"/> <field name="theme"/> @@ -157,16 +169,6 @@ </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> @@ -179,7 +181,6 @@ <field name="arch" type="xml"> <xpath expr="//field[@name='complete_name']" position="after"> <field name="ur_id" optional="hide"/> - <field name="state_id" optional="hide"/> <field name="country_department_id" optional="hide"/> </xpath> </field> @@ -196,23 +197,51 @@ <field name="ur_id"/> </xpath> <!-- Filters --> - <!-- <xpath expr="//filter[@name='inactive']" position="before">--> - <!-- <filter--> - <!-- string="Adhérents"--> - <!-- name="member_coop"--> - <!-- domain="[('membership_status', 'not in', ['not_member', 'out'])]"--> - <!-- />--> - <!-- <filter--> - <!-- string="Non Adhérents"--> - <!-- name="not_member_coop"--> - <!-- domain="[('membership_status', 'in', ['not_member', 'out'])]"--> - <!-- />--> - <!-- </xpath>--> + <xpath expr="//filter[@name='inactive']" + position="before"> + <filter + string="Adhérents" + name="member_coop" + domain="[('membership', '=', True)]" + /> + <filter + string="Non Adhérents" + name="not_member_coop" + domain="[('membership', '=', False)]" + /> + <filter + string="Adhérents santé" + name="is_member_sante" + domain="[('is_member_sante', '=', 'oui')]" + /> + <filter + string="Adhérents UNMI" + name="is_member_unmi" + domain="[('is_member_unmi', '=', 'oui')]" + /> + <filter + string="Adhérents MUTEX" + name="is_member_mutex" + domain="[('is_member_mutex', '=', 'oui')]" + /> + </xpath> <!-- Group --> <xpath expr="//filter[@name='salesperson']" position="before"> <filter string="Région" name="group_state_id" context="{'group_by': 'state_id'}"/> </xpath> + <xpath expr="//group[@name='group_by']" position="after"> + <searchpanel + view_types="list,kanban,pivot,graph" + class="mucs-partner-panel" + > + <field name="ur_id"/> + <field name="ccn_id"/> + <field name="state_id"/> + <field name="company_size_id"/> + </searchpanel> + </xpath> + </field> </record> @@ -226,7 +255,7 @@ <field name="name">Coopératives</field> <field name="type">ir.actions.act_window</field> <field name="res_model">res.partner</field> - <field name="view_mode">list,form</field> + <field name="view_mode">list,form,pivot,graph</field> <field name="search_view_id" ref="search_partner"/> <field name="domain">[('is_cooperative', '=', True)]</field> <field name="path">coop</field> @@ -236,9 +265,5 @@ 'default_is_company': True, } </field> - <field - name="view_ids" - eval="[(5, 0, 0), (0, 0, {'view_mode': 'list', 'view_id': ref('view_partner_tree')}),]" - /> </record> </odoo>