From 2750f9268a3627b8019daee60e21d495d47d5aa0 Mon Sep 17 00:00:00 2001 From: xtremxpert Date: Fri, 11 Jul 2025 10:42:05 -0400 Subject: [PATCH] devben --- bemade_fsm/__manifest__.py | 1 + bemade_fsm/models/__init__.py | 1 + bemade_fsm/models/sale_order.py | 62 ++++ bemade_fsm/models/sale_order_template.py | 78 +++++ bemade_fsm/security/ir.model.access.csv | 1 + .../views/sale_order_template_views.xml | 34 ++ mail_manual_routing_ai/__init__.py | 3 + mail_manual_routing_ai/__manifest__.py | 36 ++ mail_manual_routing_ai/doc/ai-enhanced.md | 258 ++++++++++++++ mail_manual_routing_ai/models/__init__.py | 6 + mail_manual_routing_ai/models/mail_message.py | 103 ++++++ .../models/mail_message_ai_analyzer.py | 236 +++++++++++++ .../models/mail_message_ai_openwebui.py | 321 ++++++++++++++++++ .../models/mail_message_ai_suggestion.py | 76 +++++ .../models/res_config_settings.py | 70 ++++ .../security/ir.model.access.csv | 2 + .../views/mail_message_ai_suggestion_view.xml | 108 ++++++ .../views/mail_message_view.xml | 70 ++++ mail_manual_routing_ai/views/menu.xml | 10 + .../views/res_config_settings_view.xml | 108 ++++++ 20 files changed, 1584 insertions(+) create mode 100644 bemade_fsm/models/sale_order_template.py create mode 100644 bemade_fsm/views/sale_order_template_views.xml create mode 100644 mail_manual_routing_ai/__init__.py create mode 100644 mail_manual_routing_ai/__manifest__.py create mode 100644 mail_manual_routing_ai/doc/ai-enhanced.md create mode 100644 mail_manual_routing_ai/models/__init__.py create mode 100644 mail_manual_routing_ai/models/mail_message.py create mode 100644 mail_manual_routing_ai/models/mail_message_ai_analyzer.py create mode 100644 mail_manual_routing_ai/models/mail_message_ai_openwebui.py create mode 100644 mail_manual_routing_ai/models/mail_message_ai_suggestion.py create mode 100644 mail_manual_routing_ai/models/res_config_settings.py create mode 100644 mail_manual_routing_ai/security/ir.model.access.csv create mode 100644 mail_manual_routing_ai/views/mail_message_ai_suggestion_view.xml create mode 100644 mail_manual_routing_ai/views/mail_message_view.xml create mode 100644 mail_manual_routing_ai/views/menu.xml create mode 100644 mail_manual_routing_ai/views/res_config_settings_view.xml diff --git a/bemade_fsm/__manifest__.py b/bemade_fsm/__manifest__.py index e981190..e402f0f 100644 --- a/bemade_fsm/__manifest__.py +++ b/bemade_fsm/__manifest__.py @@ -49,6 +49,7 @@ "views/menus.xml", "views/task_views.xml", "views/sale_order_views.xml", + "views/sale_order_template_views.xml", "reports/worksheet_custom_report_templates.xml", "reports/worksheet_custom_reports.xml", "wizard/new_task_from_template.xml", diff --git a/bemade_fsm/models/__init__.py b/bemade_fsm/models/__init__.py index 5d52c07..27e3f32 100644 --- a/bemade_fsm/models/__init__.py +++ b/bemade_fsm/models/__init__.py @@ -2,6 +2,7 @@ from . import task_template from . import product_template from . import sale_order_line from . import sale_order +from . import sale_order_template from . import task from . import res_partner from . import fsm_visit diff --git a/bemade_fsm/models/sale_order.py b/bemade_fsm/models/sale_order.py index 65556cc..9f63bdc 100644 --- a/bemade_fsm/models/sale_order.py +++ b/bemade_fsm/models/sale_order.py @@ -161,3 +161,65 @@ class SaleOrder(models.Model): for rec in self: rec.tasks_ids.write({"partner_id": rec.partner_shipping_id.id}) return res + + @api.onchange('sale_order_template_id') + def _onchange_fsm_sale_order_template_id(self): + """Ajoute le support des champs FSM lors de l'utilisation d'un modèle de devis. + Cette méthode est appelée après le traitement standard du modèle de devis. + """ + if not self.sale_order_template_id: + return + + # Vérifier si le modèle a les attributs FSM (pour éviter les erreurs si module non installé) + if hasattr(self.sale_order_template_id, 'is_fsm') and self.sale_order_template_id.is_fsm: + # Copier les contacts du modèle de devis + if hasattr(self.sale_order_template_id, 'site_contacts'): + self.site_contacts = self.sale_order_template_id.site_contacts + + if hasattr(self.sale_order_template_id, 'work_order_contacts'): + self.work_order_contacts = self.sale_order_template_id.work_order_contacts + + # Copier les équipements par défaut du modèle + if hasattr(self.sale_order_template_id, 'default_equipment_ids') and self.sale_order_template_id.default_equipment_ids: + self.default_equipment_ids = self.sale_order_template_id.default_equipment_ids + + # Traiter les templates de visite après que toutes les lignes ont été créées + self._process_fsm_visit_templates() + + def _process_fsm_visit_templates(self): + """Créer des visites à partir des templates de visite du modèle de devis.""" + if not self.sale_order_template_id or not hasattr(self.sale_order_template_id, 'visit_template_ids'): + return + + for visit_template in self.sale_order_template_id.visit_template_ids: + # Créer une nouvelle visite pour chaque template + visit = self.env["bemade_fsm.visit"].create({ + "label": visit_template.name, + "sale_order_id": self.id, + }) + + # Dans le contexte d'un modèle de commande, nous devons associer les lignes aux visites + # d'une façon différente, puisque le lien direct entre les lignes de commande + # et les lignes de modèle n'est pas disponible + + # Si nous avons une section définie dans le template, nous associons la visite + # à la première section correspondante de la commande + if visit_template.so_section_template_id: + # Trouver une ligne de section avec un nom similaire + for line in self.order_line.filtered(lambda l: l.display_type == 'line_section'): + if line.name == visit_template.so_section_template_id.name: + visit.so_section_id = line + break + + # Pour les produits, associer au mieux les équipements par correspondance + # avec le nom ou la description des produits + for line in self.order_line.filtered(lambda l: not l.display_type): + # Associer les lignes à la visite si c'est pertinent + # La logique exacte dépendra de l'implémentation de votre système + if visit.so_section_id and line.sequence > visit.so_section_id.sequence: + # Si la ligne suit la section de la visite, l'associer à cette visite + line.visit_id = visit.id + + # Appliquer les équipements du modèle de devis par défaut + if self.default_equipment_ids: + line.equipment_ids = self.default_equipment_ids diff --git a/bemade_fsm/models/sale_order_template.py b/bemade_fsm/models/sale_order_template.py new file mode 100644 index 0000000..1bac4a1 --- /dev/null +++ b/bemade_fsm/models/sale_order_template.py @@ -0,0 +1,78 @@ +# -*- coding: utf-8 -*- +from odoo import fields, models, api + + +class SaleOrderTemplate(models.Model): + _inherit = "sale.order.template" + + # Champs pour les équipements + default_equipment_ids = fields.Many2many( + comodel_name="fsm.equipment", + string="Default Equipment to Service", + help="The default equipment to service for template lines.", + ) + + # Contacts du site et destinataires des ordres de travail + site_contacts = fields.Many2many( + comodel_name="res.partner", + relation="sale_order_template_site_contacts_rel", + string="Site Contacts", + ) + + work_order_contacts = fields.Many2many( + comodel_name="res.partner", + relation="sale_order_template_work_order_contacts_rel", + string="Work Order Recipients", + ) + + # Visites + visit_template_ids = fields.One2many( + comodel_name="bemade_fsm.visit.template", + inverse_name="sale_order_template_id", + string="Visit Templates", + copy=True, + ) + + is_fsm = fields.Boolean( + string="Is FSM", + compute="_compute_is_fsm", + store=True, + ) + + @api.depends("sale_order_template_line_ids.product_id.is_field_service") + def _compute_is_fsm(self): + for rec in self: + rec.is_fsm = any([line.product_id.is_field_service for line in rec.sale_order_template_line_ids if not line.display_type]) + + +class SaleOrderTemplateLine(models.Model): + _inherit = "sale.order.template.line" + + equipment_ids = fields.Many2many( + string="Equipment to Service", + comodel_name="fsm.equipment", + relation="bemade_fsm_equipment_sale_template_line_rel", + column1="sale_template_line_id", + column2="equipment_id", + ) + + visit_template_id = fields.Many2one( + comodel_name="bemade_fsm.visit.template", + string="Visit Template", + ) + +class BemadeFsmVisitTemplate(models.Model): + _name = "bemade_fsm.visit.template" + _description = "Template for FSM Visits" + + name = fields.Char(string="Label", required=True) + sale_order_template_id = fields.Many2one( + comodel_name="sale.order.template", + string="Quotation Template", + ondelete="cascade", + ) + + so_section_template_id = fields.Many2one( + comodel_name="sale.order.template.line", + string="Section Line", + ) diff --git a/bemade_fsm/security/ir.model.access.csv b/bemade_fsm/security/ir.model.access.csv index 12cb538..523c7e7 100644 --- a/bemade_fsm/security/ir.model.access.csv +++ b/bemade_fsm/security/ir.model.access.csv @@ -2,4 +2,5 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink access_bemade_fsm_task_template_manager,bemade_fsm_task_template,model_project_task_template,project.group_project_manager,1,1,1,1 access_bemade_fsm_task_template_user,bemade_fsm_task_template,model_project_task_template,project.group_project_user,1,1,1,1 access_bemade_fsm_visit,access_bemade_fsm_visit,model_bemade_fsm_visit,base.group_user,1,1,1,1 +access_bemade_fsm_visit_template,access_bemade_fsm_visit_template,model_bemade_fsm_visit_template,base.group_user,1,1,1,1 access_bemade_fsm_task_wizard,access_bemade_fsm_task_wizard,model_project_task_from_template_wizard,project.group_project_user,1,1,1,1 diff --git a/bemade_fsm/views/sale_order_template_views.xml b/bemade_fsm/views/sale_order_template_views.xml new file mode 100644 index 0000000..8e23c2f --- /dev/null +++ b/bemade_fsm/views/sale_order_template_views.xml @@ -0,0 +1,34 @@ + + + + sale.order.template.form.fsm + sale.order.template + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mail_manual_routing_ai/__init__.py b/mail_manual_routing_ai/__init__.py new file mode 100644 index 0000000..cde864b --- /dev/null +++ b/mail_manual_routing_ai/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- + +from . import models diff --git a/mail_manual_routing_ai/__manifest__.py b/mail_manual_routing_ai/__manifest__.py new file mode 100644 index 0000000..dd8aa67 --- /dev/null +++ b/mail_manual_routing_ai/__manifest__.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- +{ + "name": "IA pour messages perdus", + "version": "17.0.1.0.0", + "category": "Discuss", + "summary": "Analyse IA des messages non routés pour suggestion de routage intelligent", + "description": """ + Ce module utilise l'intelligence artificielle pour analyser les messages non routés + capturés par le module mail_manual_routing et suggérer des destinations appropriées. + + Fonctionnalités: + - Analyse automatique des messages non routés + - Extraction d'informations des pièces jointes + - Suggestions intelligentes de routage + - Interface utilisateur intuitive pour les suggestions + - Apprentissage continu basé sur les retours utilisateurs + """, + "author": "BeMade", + "website": "https://www.bemade.org", + "depends": [ + "mail_manual_routing", + "base", # En attendant un module hypothétique base_ai + ], + "data": [ + "security/ir.model.access.csv", + "views/mail_message_view.xml", + "views/res_config_settings_view.xml", + "views/mail_message_ai_suggestion_view.xml", + "views/menu.xml", + ], + "demo": [], + "installable": True, + "application": False, + "auto_install": False, + "license": "LGPL-3", +} diff --git a/mail_manual_routing_ai/doc/ai-enhanced.md b/mail_manual_routing_ai/doc/ai-enhanced.md new file mode 100644 index 0000000..aa41d3f --- /dev/null +++ b/mail_manual_routing_ai/doc/ai-enhanced.md @@ -0,0 +1,258 @@ +# Analyse d'un module IA pour améliorer `mail_manual_routing` + +## Introduction + +Ce document présente une analyse conceptuelle d'un module complémentaire nommé `mail_manual_routing_ai` qui utiliserait l'intelligence artificielle pour améliorer la gestion des messages non routés capturés par le module `mail_manual_routing`. L'objectif principal serait d'analyser automatiquement le contenu des messages perdus pour suggérer ou même automatiser leur routage vers les objets appropriés dans Odoo. + +## Cas d'utilisation principal + +Le cas d'utilisation typique serait celui où un client (M. X) envoie un courriel à un employé (M. Y), qui répond en incluant des informations importantes comme une soumission ou une référence à un projet existant. Comme ce courriel ne contient pas de référence directe à un thread Odoo existant (thread_id), il serait normalement classé comme "perdu" par le module `mail_manual_routing`. Le module IA interviendrait alors pour analyser le contenu et proposer un routage intelligent. + +## Architecture proposée + +### 1. Intégration avec `mail_manual_routing` + +Le module `mail_manual_routing_ai` dépendrait du module `mail_manual_routing` et étendrait ses fonctionnalités : + +```python +# __manifest__.py +{ + "name": "IA pour messages perdus", + "version": "17.0.1.0.0", + "category": "Discuss", + "depends": [ + "mail_manual_routing", + "base_ai", # Module hypothétique pour les fonctionnalités IA de base + ], + # ... +} +``` + +### 2. Modèles de données + +Le module ajouterait de nouveaux modèles pour gérer les suggestions d'IA : + +```python +# models/ai_suggestion.py +class MailMessageAISuggestion(models.Model): + _name = "mail.message.ai.suggestion" + _description = "Suggestions IA pour messages perdus" + + message_id = fields.Many2one('mail.message', string="Message", required=True) + suggested_model = fields.Char(string="Modèle suggéré") + suggested_record_id = fields.Integer(string="ID d'enregistrement suggéré") + confidence_score = fields.Float(string="Score de confiance", help="Score de confiance de l'IA (0-1)") + suggestion_reason = fields.Text(string="Explication", help="Explication de la suggestion par l'IA") + is_applied = fields.Boolean(string="Appliqué", default=False) + user_feedback = fields.Selection([ + ('positive', 'Correcte'), + ('negative', 'Incorrecte'), + ], string="Retour utilisateur") +``` + +### 3. Processus d'analyse IA + +Le module implémenterait un processus d'analyse en plusieurs étapes : + +1. **Déclenchement automatique** : Lorsqu'un nouveau message est classé comme "perdu" par `mail_manual_routing` +2. **Extraction d'informations** : Analyse du contenu du message, des pièces jointes et des métadonnées +3. **Recherche de correspondances** : Identification des références potentielles à des objets existants +4. **Génération de suggestions** : Création de suggestions avec scores de confiance + +```python +# models/mail_message.py +class MailMessage(models.Model): + _inherit = "mail.message" + + ai_suggestion_ids = fields.One2many('mail.message.ai.suggestion', 'message_id', string="Suggestions IA") + ai_suggestion_count = fields.Integer(compute='_compute_ai_suggestion_count') + ai_analyzed = fields.Boolean(string="Analysé par IA", default=False) + ai_top_suggestion_id = fields.Many2one('mail.message.ai.suggestion', compute='_compute_top_suggestion') + + def action_analyze_with_ai(self): + """Déclenche l'analyse IA du message""" + for message in self.filtered(lambda m: m.is_unattached): + self.env['mail.message.ai.analyzer'].analyze_message(message) + return True + + @api.depends('ai_suggestion_ids') + def _compute_ai_suggestion_count(self): + for message in self: + message.ai_suggestion_count = len(message.ai_suggestion_ids) + + @api.depends('ai_suggestion_ids.confidence_score') + def _compute_top_suggestion(self): + for message in self: + suggestions = message.ai_suggestion_ids.sorted(key=lambda s: s.confidence_score, reverse=True) + message.ai_top_suggestion_id = suggestions[0] if suggestions else False +``` + +### 4. Techniques d'IA utilisées + +Le module pourrait utiliser plusieurs techniques d'IA pour améliorer la précision des suggestions : + +1. **Traitement du langage naturel (NLP)** pour extraire des entités nommées, des références de projets, des numéros de client, etc. +2. **Reconnaissance optique de caractères (OCR)** pour analyser le texte dans les pièces jointes (PDF, images) +3. **Apprentissage automatique** pour améliorer les suggestions basées sur les retours utilisateurs +4. **Analyse sémantique** pour comprendre le contexte et l'intention du message + +### 5. Interface utilisateur + +L'interface utilisateur serait enrichie pour présenter les suggestions de l'IA de manière intuitive : + +```xml + + + mail.message + + + +