From b0fb87991412fc14c3b28f2cc53e75d3eb2f4e60 Mon Sep 17 00:00:00 2001 From: pcosson Date: Fri, 24 Jul 2026 10:10:33 +0200 Subject: [PATCH] feat: restore compact ticket notification format --- README.md | 16 +++++++-------- TRAVAIL.md | 8 ++++++++ WAPT/control | 2 +- setup.py | 43 +++++++++++++++++++++++++++++++++++------ tests/test_ticketing.py | 37 +++++++++++++++++++++++++++++------ ticketing.ini.example | 1 + 6 files changed, 86 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 43fb673..0989b1a 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,7 @@ timeout = 30 [notification] max_tickets = 50 +ticket_url_template = {odoo_url}/web#id={id}&model={model}&view_type=form ``` Au deploiement, `install()` copie ce modele vers `WAPT.private_dir/ticketing.ini` uniquement si le fichier n'existe pas deja. Il faut ensuite renseigner le fichier prive sur la machine cible. Les mises a jour du paquet n'ecrasent donc pas une configuration deja remplie. @@ -92,7 +93,7 @@ La cle API est utilisee comme mot de passe d'API Odoo pour authentifier `usernam `odoo.fields` controle les champs recuperes depuis Odoo pour enrichir la notification. Les champs `id` et `write_date` sont toujours ajoutes s'ils sont absents, car ils sont necessaires a la comparaison d'etat. -`notification.max_tickets` limite le nombre de lignes detaillees envoyees dans Rocket.Chat. Le message indique toujours le nombre total de tickets modifies. +`notification.max_tickets` limite le nombre de tickets affiches dans Rocket.Chat. `notification.ticket_url_template` controle le lien d'ouverture du ticket ; les variables disponibles sont `{odoo_url}`, `{id}` et `{model}`. ## Protocole Odoo @@ -132,17 +133,16 @@ Lecture des tickets : Quand des tickets changent, Rocket.Chat recoit un message avec : -- le nombre total de tickets modifies ; -- une ligne par ticket affiche, limitee par `notification.max_tickets` ; -- l'ID, le titre, le type de changement, le statut, le client, l'assigne, la nouvelle date de modification et l'ancienne date quand elle existe. +- une ligne compacte par ticket affiche, limitee par `notification.max_tickets` ; +- l'ID, le sujet, la personne assignee et un lien d'ouverture direct dans Odoo ; +- une ligne de resume si tous les tickets modifies ne sont pas affiches. Exemple : ```text -Tickets Odoo mis a jour depuis la derniere verification : 2 - -- #187 | Probleme imprimante | type: modifie | statut: En cours | client: Comitari | assigne: Support | date: 2026-07-24 10:00:00 | ancienne date: 2026-07-24 09:00:00 -- #251 | Nouveau ticket | type: nouveau | statut: - | client: - | assigne: - | date: 2026-07-24 11:00:00 +Les tickets suivants ont ete mis a jour depuis la derniere verification : +187 : Sujet : Probleme imprimante gere par Support : [cliquer ici pour ouvrir le ticket](https://odoo.example.test/web#id=187&model=helpdesk.ticket&view_type=form) +251 : Sujet : Nouveau ticket gere par Personne : [cliquer ici pour ouvrir le ticket](https://odoo.example.test/web#id=251&model=helpdesk.ticket&view_type=form) ``` ## Tests diff --git a/TRAVAIL.md b/TRAVAIL.md index 4ffe0ca..f7f5fcf 100644 --- a/TRAVAIL.md +++ b/TRAVAIL.md @@ -120,3 +120,11 @@ Ce fichier consigne les interventions Codex sur le projet afin de garder une tra - Ajout de l'option `notification.max_tickets` pour limiter le nombre de lignes detaillees dans Rocket.Chat. - Remplacement du message brut contenant seulement une liste d'IDs par un message avec nombre total, titre, statut, client, assigne, date courante et ancienne date quand disponible. - Incrementation de la version WAPT locale de `0.1-11` a `0.1-12`. + +### Alignement format historique Rocket.Chat + +- Remplacement du format detaille par un format compact proche de l'ancien rendu : + `ID : Sujet : titre gere par personne : cliquer ici pour ouvrir le ticket`. +- Ajout d'un lien Markdown vers Odoo pour chaque ticket. +- Ajout de l'option `notification.ticket_url_template` avec les variables `{odoo_url}`, `{id}` et `{model}`. +- Incrementation de la version WAPT de `0.1-12` a `0.1-13`. diff --git a/WAPT/control b/WAPT/control index a663cb1..64c5598 100644 --- a/WAPT/control +++ b/WAPT/control @@ -1,5 +1,5 @@ package : comi-odoo-ticketing -version : 0.1-12 +version : 0.1-13 architecture : all section : base priority : optional diff --git a/setup.py b/setup.py index b1474ad..9feb943 100644 --- a/setup.py +++ b/setup.py @@ -14,6 +14,7 @@ STATE_FILE = "tickets_state.json" DEFAULT_TIMEOUT = 30 DEFAULT_TICKET_FIELDS = ["id", "name", "stage_id", "partner_id", "user_id", "write_date"] DEFAULT_MAX_NOTIFIED_TICKETS = 50 +DEFAULT_TICKET_URL_TEMPLATE = "{odoo_url}/web#id={id}&model={model}&view_type=form" def get_config_path(): @@ -40,6 +41,9 @@ def audit(): tickets_updated, previous_tickets_state, get_max_notified_tickets(conf_wapt), + get_ticket_url_template(conf_wapt), + conf_wapt.get("odoo", "url").rstrip("/"), + conf_wapt.get("odoo", "model", fallback="helpdesk.ticket"), ) send_to_rocket(message, conf_wapt=conf_wapt) @@ -110,6 +114,10 @@ def get_max_notified_tickets(conf_wapt): return conf_wapt.getint("notification", "max_tickets", fallback=DEFAULT_MAX_NOTIFIED_TICKETS) +def get_ticket_url_template(conf_wapt): + return conf_wapt.get("notification", "ticket_url_template", fallback=DEFAULT_TICKET_URL_TEMPLATE) + + def build_odoo_payload(service, method, args): return { "jsonrpc": "2.0", @@ -235,7 +243,28 @@ def display_odoo_value(value): return str(value) -def format_ticket_line(ticket, previous_tickets_state): +def build_ticket_url(ticket, ticket_url_template, odoo_url, model): + return ticket_url_template.format( + id=ticket["id"], + odoo_url=odoo_url, + model=model, + ) + + +def format_ticket_line(ticket, ticket_url_template, odoo_url, model): + assignee = display_odoo_value(ticket.get("user_id")) + if assignee == "-": + assignee = "Personne" + + return "%s : Sujet : %s gere par %s : [cliquer ici pour ouvrir le ticket](%s)" % ( + ticket["id"], + display_odoo_value(ticket.get("name", "Sans titre")), + assignee, + build_ticket_url(ticket, ticket_url_template, odoo_url, model), + ) + + +def format_detailed_ticket_line(ticket, previous_tickets_state): ticket_id = str(ticket["id"]) previous_write_date = previous_tickets_state.get(ticket_id) change_type = "nouveau" if previous_write_date is None else "modifie" @@ -253,25 +282,27 @@ def format_ticket_line(ticket, previous_tickets_state): return "- " + " | ".join(parts) -def build_notification_message(tickets_updated, previous_tickets_state, max_tickets): +def build_notification_message(tickets_updated, previous_tickets_state, max_tickets, ticket_url_template, odoo_url, model): if not tickets_updated: return "Aucun ticket n'a ete mis a jour depuis la derniere verification." displayed_tickets = tickets_updated[:max_tickets] hidden_count = len(tickets_updated) - len(displayed_tickets) lines = [ - "Tickets Odoo mis a jour depuis la derniere verification : %s" % len(tickets_updated), - "", + "Les tickets suivants ont ete mis a jour depuis la derniere verification :", ] lines.extend([ - format_ticket_line(ticket, previous_tickets_state) + format_ticket_line(ticket, ticket_url_template, odoo_url, model) for ticket in displayed_tickets ]) if hidden_count > 0: lines.extend([ "", - "... %s ticket(s) supplementaire(s) non affiche(s)." % hidden_count, + "... %s ticket(s) supplementaire(s) non affiche(s) sur %s au total." % ( + hidden_count, + len(tickets_updated), + ), ]) return "\n".join(lines) diff --git a/tests/test_ticketing.py b/tests/test_ticketing.py index 934c591..16ac50a 100644 --- a/tests/test_ticketing.py +++ b/tests/test_ticketing.py @@ -92,15 +92,40 @@ class TicketingStateTest(unittest.TestCase): }, ] - message = ticketing.build_notification_message(tickets, previous_state, 1) + message = ticketing.build_notification_message( + tickets, + previous_state, + 1, + "{odoo_url}/web#id={id}&model={model}&view_type=form", + "https://odoo.example.test", + "helpdesk.ticket", + ) - self.assertIn("Tickets Odoo mis a jour depuis la derniere verification : 2", message) - self.assertIn("#1 | Probleme imprimante | type: modifie", message) - self.assertIn("statut: En cours", message) - self.assertIn("ancienne date: 2026-07-24 09:00:00", message) - self.assertIn("1 ticket(s) supplementaire(s) non affiche(s)", message) + self.assertIn("Les tickets suivants ont ete mis a jour depuis la derniere verification :", message) + self.assertIn( + "1 : Sujet : Probleme imprimante gere par Support : " + "[cliquer ici pour ouvrir le ticket]" + "(https://odoo.example.test/web#id=1&model=helpdesk.ticket&view_type=form)", + message, + ) + self.assertIn("1 ticket(s) supplementaire(s) non affiche(s) sur 2 au total", message) self.assertNotIn("Nouveau ticket", message) + def test_format_ticket_line_uses_personne_when_no_assignee(self): + line = ticketing.format_ticket_line( + {"id": 2, "name": "Nouveau ticket", "user_id": False}, + "{odoo_url}/web#id={id}&model={model}&view_type=form", + "https://odoo.example.test", + "helpdesk.ticket", + ) + + self.assertEqual( + line, + "2 : Sujet : Nouveau ticket gere par Personne : " + "[cliquer ici pour ouvrir le ticket]" + "(https://odoo.example.test/web#id=2&model=helpdesk.ticket&view_type=form)", + ) + def test_load_tickets_state_returns_empty_dict_when_file_is_missing(self): missing_path = os.path.join(tempfile.gettempdir(), "missing-ticket-state.json") diff --git a/ticketing.ini.example b/ticketing.ini.example index 2c2251a..a07c51c 100644 --- a/ticketing.ini.example +++ b/ticketing.ini.example @@ -14,3 +14,4 @@ timeout = 30 [notification] max_tickets = 50 +ticket_url_template = {odoo_url}/web#id={id}&model={model}&view_type=form