feat: restore compact ticket notification format
This commit is contained in:
@@ -57,6 +57,7 @@ timeout = 30
|
|||||||
|
|
||||||
[notification]
|
[notification]
|
||||||
max_tickets = 50
|
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.
|
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.
|
`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
|
## Protocole Odoo
|
||||||
|
|
||||||
@@ -132,17 +133,16 @@ Lecture des tickets :
|
|||||||
|
|
||||||
Quand des tickets changent, Rocket.Chat recoit un message avec :
|
Quand des tickets changent, Rocket.Chat recoit un message avec :
|
||||||
|
|
||||||
- le nombre total de tickets modifies ;
|
- une ligne compacte par ticket affiche, limitee par `notification.max_tickets` ;
|
||||||
- une ligne par ticket affiche, limitee par `notification.max_tickets` ;
|
- l'ID, le sujet, la personne assignee et un lien d'ouverture direct dans Odoo ;
|
||||||
- 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 de resume si tous les tickets modifies ne sont pas affiches.
|
||||||
|
|
||||||
Exemple :
|
Exemple :
|
||||||
|
|
||||||
```text
|
```text
|
||||||
Tickets Odoo mis a jour depuis la derniere verification : 2
|
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)
|
||||||
- #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 : 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)
|
||||||
- #251 | Nouveau ticket | type: nouveau | statut: - | client: - | assigne: - | date: 2026-07-24 11:00:00
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Tests
|
## Tests
|
||||||
|
|||||||
@@ -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.
|
- 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.
|
- 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`.
|
- 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`.
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
package : comi-odoo-ticketing
|
package : comi-odoo-ticketing
|
||||||
version : 0.1-12
|
version : 0.1-13
|
||||||
architecture : all
|
architecture : all
|
||||||
section : base
|
section : base
|
||||||
priority : optional
|
priority : optional
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ STATE_FILE = "tickets_state.json"
|
|||||||
DEFAULT_TIMEOUT = 30
|
DEFAULT_TIMEOUT = 30
|
||||||
DEFAULT_TICKET_FIELDS = ["id", "name", "stage_id", "partner_id", "user_id", "write_date"]
|
DEFAULT_TICKET_FIELDS = ["id", "name", "stage_id", "partner_id", "user_id", "write_date"]
|
||||||
DEFAULT_MAX_NOTIFIED_TICKETS = 50
|
DEFAULT_MAX_NOTIFIED_TICKETS = 50
|
||||||
|
DEFAULT_TICKET_URL_TEMPLATE = "{odoo_url}/web#id={id}&model={model}&view_type=form"
|
||||||
|
|
||||||
|
|
||||||
def get_config_path():
|
def get_config_path():
|
||||||
@@ -40,6 +41,9 @@ def audit():
|
|||||||
tickets_updated,
|
tickets_updated,
|
||||||
previous_tickets_state,
|
previous_tickets_state,
|
||||||
get_max_notified_tickets(conf_wapt),
|
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)
|
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)
|
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):
|
def build_odoo_payload(service, method, args):
|
||||||
return {
|
return {
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
@@ -235,7 +243,28 @@ def display_odoo_value(value):
|
|||||||
return str(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"])
|
ticket_id = str(ticket["id"])
|
||||||
previous_write_date = previous_tickets_state.get(ticket_id)
|
previous_write_date = previous_tickets_state.get(ticket_id)
|
||||||
change_type = "nouveau" if previous_write_date is None else "modifie"
|
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)
|
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:
|
if not tickets_updated:
|
||||||
return "Aucun ticket n'a ete mis a jour depuis la derniere verification."
|
return "Aucun ticket n'a ete mis a jour depuis la derniere verification."
|
||||||
|
|
||||||
displayed_tickets = tickets_updated[:max_tickets]
|
displayed_tickets = tickets_updated[:max_tickets]
|
||||||
hidden_count = len(tickets_updated) - len(displayed_tickets)
|
hidden_count = len(tickets_updated) - len(displayed_tickets)
|
||||||
lines = [
|
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([
|
lines.extend([
|
||||||
format_ticket_line(ticket, previous_tickets_state)
|
format_ticket_line(ticket, ticket_url_template, odoo_url, model)
|
||||||
for ticket in displayed_tickets
|
for ticket in displayed_tickets
|
||||||
])
|
])
|
||||||
|
|
||||||
if hidden_count > 0:
|
if hidden_count > 0:
|
||||||
lines.extend([
|
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)
|
return "\n".join(lines)
|
||||||
|
|||||||
+31
-6
@@ -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("Les tickets suivants ont ete mis a jour depuis la derniere verification :", message)
|
||||||
self.assertIn("#1 | Probleme imprimante | type: modifie", message)
|
self.assertIn(
|
||||||
self.assertIn("statut: En cours", message)
|
"1 : Sujet : Probleme imprimante gere par Support : "
|
||||||
self.assertIn("ancienne date: 2026-07-24 09:00:00", message)
|
"[cliquer ici pour ouvrir le ticket]"
|
||||||
self.assertIn("1 ticket(s) supplementaire(s) non affiche(s)", message)
|
"(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)
|
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):
|
def test_load_tickets_state_returns_empty_dict_when_file_is_missing(self):
|
||||||
missing_path = os.path.join(tempfile.gettempdir(), "missing-ticket-state.json")
|
missing_path = os.path.join(tempfile.gettempdir(), "missing-ticket-state.json")
|
||||||
|
|
||||||
|
|||||||
@@ -14,3 +14,4 @@ timeout = 30
|
|||||||
|
|
||||||
[notification]
|
[notification]
|
||||||
max_tickets = 50
|
max_tickets = 50
|
||||||
|
ticket_url_template = {odoo_url}/web#id={id}&model={model}&view_type=form
|
||||||
|
|||||||
Reference in New Issue
Block a user