From 836b5c8edbc612a59fde3ea4463f17063f360f67 Mon Sep 17 00:00:00 2001 From: Karl Determe Date: Tue, 15 Sep 2026 16:10:31 +0200 Subject: [PATCH] feat: add custom notification recipient fields and helpers to Symptom model --- loko/interventions/admin.py | 63 ++++- .../0008_symptom_notification_recipients.py | 55 ++++ loko/interventions/models.py | 19 ++ .../tests/test_symptom_notifications.py | 246 ++++++++++++++++++ loko/interventions/views/helpers.py | 97 ++++--- loko/interventions/views/workflow.py | 12 +- 6 files changed, 444 insertions(+), 48 deletions(-) create mode 100644 loko/interventions/migrations/0008_symptom_notification_recipients.py create mode 100644 loko/interventions/tests/test_symptom_notifications.py diff --git a/loko/interventions/admin.py b/loko/interventions/admin.py index e58b94f..e731d8d 100644 --- a/loko/interventions/admin.py +++ b/loko/interventions/admin.py @@ -1,6 +1,6 @@ from django.contrib.gis import admin from django.contrib.admin.widgets import FilteredSelectMultiple -from django.utils.translation import get_language +from django.utils.translation import get_language, gettext_lazy as _ from django.utils.html import format_html from django import forms from django.urls import path @@ -353,16 +353,65 @@ class SymptomAdminForm(forms.ModelForm): ) return cats + def _clean_emails(self, field_name): + emails_raw = self.cleaned_data.get(field_name, '') + if not emails_raw: + return '' + import re + from django.core.validators import validate_email + from django.core.exceptions import ValidationError + + cleaned_emails = [] + for email in re.split(r'[;,]', emails_raw): + email = email.strip() + if email: + try: + validate_email(email) + cleaned_emails.append(email) + except ValidationError: + raise forms.ValidationError( + _("L'adresse e-mail '%(email)s' n'est pas valide.") % {'email': email} + ) + return ', '.join(cleaned_emails) + + def clean_notification_emails(self): + return self._clean_emails('notification_emails') + + def clean_cc_notification_emails(self): + return self._clean_emails('cc_notification_emails') + @admin.register(Symptom) class SymptomAdmin(admin.ModelAdmin): form = SymptomAdminForm change_list_template = "admin/interventions/symptom_changelist.html" - list_display = ("id", "code", "thematic", "name_fr", "name_nl", "is_active", "auto_create_intervention", "auto_create_intervention_status", "require_equipment_selection", "asset_categories_list", "contract", "teams_list") - list_filter = ("thematic", "asset_categories", "priority", "is_active", "auto_create_intervention", "require_equipment_selection", "teams") + list_display = ("id", "code", "thematic", "name_fr", "name_nl", "is_active", "notify_contract_recipients", "auto_create_intervention", "auto_create_intervention_status", "require_equipment_selection", "asset_categories_list", "contract", "teams_list") + list_filter = ("thematic", "asset_categories", "priority", "is_active", "notify_contract_recipients", "auto_create_intervention", "require_equipment_selection", "teams") search_fields = ("name_fr", "name_nl", "code") filter_horizontal = ("teams",) inlines = [SymptomRecommendationInline, SymptomContractAutoCloseInline] + fieldsets = ( + (None, { + 'fields': ('code', 'name_fr', 'name_nl', 'order_number', 'is_active'), + }), + (_("Classification"), { + 'fields': ('thematic', 'asset_categories', 'priority', 'check_priority', 'origin_type', 'maintenance_type', 'require_equipment_selection'), + }), + (_("Attribution / Contrat"), { + 'fields': ('provider', 'contract', 'teams'), + }), + (_("Notifications email"), { + 'fields': ('notify_contract_recipients', 'notification_emails', 'cc_notification_emails'), + 'description': _( + "Configuration des destinataires pour ce symptôme. " + "Par défaut, les destinataires configurés sur le contrat associé sont prévenus. " + "Vous pouvez désactiver cette notification du contrat et/ou spécifier des adresses email supplémentaires (principales et en copie, séparées par des virgules ou points-virgules)." + ), + }), + (_("Workflow & Création automatique"), { + 'fields': ('recommended_next_status', 'auto_create_intervention', 'auto_create_intervention_status'), + }), + ) def get_urls(self): urls = super().get_urls() @@ -444,6 +493,9 @@ class SymptomAdmin(admin.ModelAdmin): "Actif", "Statut recommandé", "Équipes", + "Notifier contrat", + "Emails supplémentaires symptôme", + "Emails CC supplémentaires symptôme", "Emails notifications contrat", "Emails CC contrat", "Société (prestataire)", @@ -506,6 +558,9 @@ class SymptomAdmin(admin.ModelAdmin): "Oui" if symptom.is_active else "Non", recommended_status, teams, + "Oui" if symptom.notify_contract_recipients else "Non", + symptom.notification_emails or "—", + symptom.cc_notification_emails or "—", contract_emails, contract_cc_emails, provider_name, @@ -515,7 +570,7 @@ class SymptomAdmin(admin.ModelAdmin): ]) # Largeurs de colonnes - col_widths = [6, 14, 40, 40, 20, 30, 12, 16, 18, 25, 14, 8, 20, 40, 40, 40, 30, 20, 12, 50] + col_widths = [6, 14, 40, 40, 20, 30, 12, 16, 18, 25, 14, 8, 20, 40, 18, 40, 40, 40, 40, 30, 20, 12, 50] for i, width in enumerate(col_widths, start=1): ws.column_dimensions[ws.cell(row=1, column=i).column_letter].width = width diff --git a/loko/interventions/migrations/0008_symptom_notification_recipients.py b/loko/interventions/migrations/0008_symptom_notification_recipients.py new file mode 100644 index 0000000..668e0b0 --- /dev/null +++ b/loko/interventions/migrations/0008_symptom_notification_recipients.py @@ -0,0 +1,55 @@ +from django.db import migrations, models + + +def migrate_internal_emails_forward(apps, schema_editor): + Symptom = apps.get_model('interventions', 'Symptom') + for symptom in Symptom.objects.exclude(internal_notification_emails__isnull=True).exclude(internal_notification_emails=''): + if not symptom.notification_emails: + symptom.notification_emails = symptom.internal_notification_emails + symptom.save(update_fields=['notification_emails']) + + +def migrate_internal_emails_reverse(apps, schema_editor): + pass + + +class Migration(migrations.Migration): + + dependencies = [ + ('interventions', '0007_cleanup_blocked_status_interventions'), + ] + + operations = [ + migrations.AddField( + model_name='symptom', + name='notify_contract_recipients', + field=models.BooleanField( + default=True, + help_text="Indique si les destinataires définis sur le contrat associé doivent être prévenus (oui par défaut).", + verbose_name="Notifier les destinataires du contrat", + ), + ), + migrations.AddField( + model_name='symptom', + name='notification_emails', + field=models.CharField( + blank=True, + help_text="Adresses email supplémentaires à notifier pour ce symptôme (séparées par des virgules ou points-virgules).", + max_length=1000, + null=True, + verbose_name="Emails supplémentaires", + ), + ), + migrations.AddField( + model_name='symptom', + name='cc_notification_emails', + field=models.CharField( + blank=True, + help_text="Adresses email supplémentaires à ajouter en copie pour ce symptôme (séparées par des virgules ou points-virgules).", + max_length=1000, + null=True, + verbose_name="Emails en copie (CC) supplémentaires", + ), + ), + migrations.RunPython(migrate_internal_emails_forward, migrate_internal_emails_reverse), + ] diff --git a/loko/interventions/models.py b/loko/interventions/models.py index cadd6ad..c16b106 100644 --- a/loko/interventions/models.py +++ b/loko/interventions/models.py @@ -231,6 +231,25 @@ class Symptom(models.Model): help_text=_("Statut dans lequel l'intervention automatique doit être créée.") ) internal_notification_emails = models.CharField(blank=True, null=True, max_length=1000, help_text=_("Liste d'emails pour les notifications internes.")) + notify_contract_recipients = models.BooleanField( + default=True, + verbose_name=_("Notifier les destinataires du contrat"), + help_text=_("Indique si les destinataires définis sur le contrat associé doivent être prévenus (oui par défaut)."), + ) + notification_emails = models.CharField( + max_length=1000, + blank=True, + null=True, + verbose_name=_("Emails supplémentaires"), + help_text=_("Adresses email supplémentaires à notifier pour ce symptôme (séparées par des virgules ou points-virgules)."), + ) + cc_notification_emails = models.CharField( + max_length=1000, + blank=True, + null=True, + verbose_name=_("Emails en copie (CC) supplémentaires"), + help_text=_("Adresses email supplémentaires à ajouter en copie pour ce symptôme (séparées par des virgules ou points-virgules)."), + ) def __str__(self): lang = get_language() diff --git a/loko/interventions/tests/test_symptom_notifications.py b/loko/interventions/tests/test_symptom_notifications.py new file mode 100644 index 0000000..172818a --- /dev/null +++ b/loko/interventions/tests/test_symptom_notifications.py @@ -0,0 +1,246 @@ +from django.test import TestCase, override_settings +from django.utils import timezone +from django.core.exceptions import ValidationError + +from common.models import Thematic +from contracts.models import Company, Contract +from assets.models import AssetCategory +from interventions.models import Intervention, Symptom +from interventions.admin import SymptomAdminForm +from interventions.views.helpers import get_contract_recipients, get_contract_cc_recipients + + +@override_settings(EMAIL_OVERRIDE=None) +class SymptomNotificationRecipientsTests(TestCase): + def setUp(self): + self.thematic, _ = Thematic.objects.get_or_create(code='trafficlights', defaults={'name': 'Feux de signalisation'}) + self.category, _ = AssetCategory.objects.get_or_create(name_fr='Test Category', thematic=self.thematic) + + self.company = Company.objects.create(name='Prestataire Feux') + today = timezone.now().date() + + self.contract = Contract.objects.create( + contract_number='CTR-FEUX-001', + company=self.company, + start_date=today, + end_date=today + timezone.timedelta(days=365), + is_active=True, + is_blocked=False, + notification_emails='contract_to1@example.com; contract_to2@example.com', + cc_notification_emails='contract_cc1@example.com, contract_cc2@example.com', + send_email_notifications=True, + ) + self.contract.thematics.add(self.thematic) + + self.contract_blocked = Contract.objects.create( + contract_number='CTR-FEUX-BLOCKED', + company=self.company, + start_date=today, + end_date=today + timezone.timedelta(days=365), + is_active=True, + is_blocked=True, + blocked_reason='Gel budget', + notification_emails='blocked_contract@example.com', + cc_notification_emails='blocked_cc@example.com', + send_email_notifications=True, + ) + self.contract_blocked.thematics.add(self.thematic) + + def test_default_symptom_notifies_contract_recipients(self): + """Par défaut (notify_contract_recipients=True, sans emails supplémentaires), seuls les emails du contrat sont retournés.""" + symptom = Symptom.objects.create( + name_fr='Ampoule grillée', + thematic=self.thematic, + contract=self.contract, + notify_contract_recipients=True, + ) + intervention = Intervention.objects.create( + title='Remplacement ampoule', + thematic=self.thematic, + contract=self.contract, + symptom=symptom, + ) + + recipients = get_contract_recipients(self.contract, intervention) + self.assertIn('contract_to1@example.com', recipients) + self.assertIn('contract_to2@example.com', recipients) + self.assertEqual(len(recipients), 2) + + cc_recipients = get_contract_cc_recipients(self.contract, intervention) + self.assertIn('contract_cc1@example.com', cc_recipients) + self.assertIn('contract_cc2@example.com', cc_recipients) + self.assertIn('bm.perm.tech@sprb.brussels', cc_recipients) + + def test_symptom_with_additional_recipients_and_cc(self): + """Avec notify_contract_recipients=True et des emails supplémentaires, les emails du contrat et du symptôme sont cumulés.""" + symptom = Symptom.objects.create( + name_fr='Panne contrôleur critique', + thematic=self.thematic, + contract=self.contract, + notify_contract_recipients=True, + notification_emails='extra_expert@sprb.brussels; contract_to1@example.com', # Doublon volontaire avec contrat + cc_notification_emails='extra_supervision@sprb.brussels', + ) + intervention = Intervention.objects.create( + title='Contrôleur HS', + thematic=self.thematic, + contract=self.contract, + symptom=symptom, + ) + + recipients = get_contract_recipients(self.contract, intervention) + self.assertEqual( + set(recipients), + {'contract_to1@example.com', 'contract_to2@example.com', 'extra_expert@sprb.brussels'} + ) + + cc_recipients = get_contract_cc_recipients(self.contract, intervention) + self.assertEqual( + set(cc_recipients), + {'contract_cc1@example.com', 'contract_cc2@example.com', 'extra_supervision@sprb.brussels', 'bm.perm.tech@sprb.brussels'} + ) + + def test_symptom_disabling_contract_recipients(self): + """Avec notify_contract_recipients=False, les emails du contrat sont exclus, seuls les emails du symptôme sont prévenus.""" + symptom = Symptom.objects.create( + name_fr='Vandalisme interne', + thematic=self.thematic, + contract=self.contract, + notify_contract_recipients=False, + notification_emails='police_bruxelles@sprb.brussels, securite@sprb.brussels', + cc_notification_emails='responsable_securite@sprb.brussels', + ) + intervention = Intervention.objects.create( + title='Dégradation armoire', + thematic=self.thematic, + contract=self.contract, + symptom=symptom, + ) + + recipients = get_contract_recipients(self.contract, intervention) + self.assertEqual( + set(recipients), + {'police_bruxelles@sprb.brussels', 'securite@sprb.brussels'} + ) + self.assertNotIn('contract_to1@example.com', recipients) + self.assertNotIn('contract_to2@example.com', recipients) + + cc_recipients = get_contract_cc_recipients(self.contract, intervention) + self.assertIn('responsable_securite@sprb.brussels', cc_recipients) + self.assertIn('bm.perm.tech@sprb.brussels', cc_recipients) + self.assertNotIn('contract_cc1@example.com', cc_recipients) + self.assertNotIn('contract_cc2@example.com', cc_recipients) + + def test_symptom_disabling_contract_recipients_without_extra_emails(self): + """Avec notify_contract_recipients=False et sans emails supplémentaires, aucun destinataire principal n'est retourné.""" + symptom = Symptom.objects.create( + name_fr='Test sans notification', + thematic=self.thematic, + contract=self.contract, + notify_contract_recipients=False, + ) + intervention = Intervention.objects.create( + title='Intervention silencieuse', + thematic=self.thematic, + contract=self.contract, + symptom=symptom, + ) + + recipients = get_contract_recipients(self.contract, intervention) + self.assertEqual(recipients, []) + + def test_blocked_contract_returns_empty_even_with_symptom_emails(self): + """Un marché bloqué ne doit envoyer aucun email, même si le symptôme configure des destinataires.""" + symptom = Symptom.objects.create( + name_fr='Panne sur marché bloqué', + thematic=self.thematic, + contract=self.contract_blocked, + notify_contract_recipients=True, + notification_emails='extra@sprb.brussels', + cc_notification_emails='extra_cc@sprb.brussels', + ) + intervention = Intervention.objects.create( + title='Intervention bloquée', + thematic=self.thematic, + contract=self.contract_blocked, + symptom=symptom, + ) + + recipients = get_contract_recipients(self.contract_blocked, intervention) + self.assertEqual(recipients, []) + + cc_recipients = get_contract_cc_recipients(self.contract_blocked, intervention) + self.assertEqual(cc_recipients, []) + + def test_intervention_without_contract_uses_symptom_recipients(self): + """Une intervention sans contrat mais avec un symptôme configuré peut quand même notifier ses destinataires spécifiques.""" + symptom = Symptom.objects.create( + name_fr='Alerte voirie spéciale', + thematic=self.thematic, + contract=None, + notify_contract_recipients=True, + notification_emails='service_voirie@sprb.brussels', + cc_notification_emails='cc_voirie@sprb.brussels', + ) + intervention = Intervention.objects.create( + title='Intervention sans contrat', + thematic=self.thematic, + contract=None, + symptom=symptom, + ) + + recipients = get_contract_recipients(None, intervention) + self.assertEqual(recipients, ['service_voirie@sprb.brussels']) + + cc_recipients = get_contract_cc_recipients(None, intervention) + self.assertIn('cc_voirie@sprb.brussels', cc_recipients) + self.assertIn('bm.perm.tech@sprb.brussels', cc_recipients) + + def test_backward_compatibility_without_intervention(self): + """L'appel sans intervention (intervention=None) conserve le comportement historique du contrat.""" + recipients = get_contract_recipients(self.contract) + self.assertEqual( + set(recipients), + {'contract_to1@example.com', 'contract_to2@example.com'} + ) + + cc_recipients = get_contract_cc_recipients(self.contract) + self.assertIn('contract_cc1@example.com', cc_recipients) + self.assertIn('contract_cc2@example.com', cc_recipients) + self.assertIn('bm.perm.tech@sprb.brussels', cc_recipients) + + def test_symptom_admin_form_email_validation(self): + """Le formulaire d'administration valide et normalise les listes d'adresses email.""" + # Emails valides avec mélange de virgules et points-virgules + form = SymptomAdminForm(data={ + 'name_fr': 'Test Form', + 'name_nl': 'Test Form NL', + 'thematic': self.thematic.pk, + 'priority': '3', + 'is_active': True, + 'notify_contract_recipients': True, + 'notification_emails': 'alpha@example.com; beta@example.com, gamma@example.com', + 'cc_notification_emails': 'delta@example.com; epsilon@example.com', + }) + self.assertTrue(form.is_valid(), form.errors) + self.assertEqual( + form.cleaned_data['notification_emails'], + 'alpha@example.com, beta@example.com, gamma@example.com' + ) + self.assertEqual( + form.cleaned_data['cc_notification_emails'], + 'delta@example.com, epsilon@example.com' + ) + + # Email invalide + form_invalid = SymptomAdminForm(data={ + 'name_fr': 'Test Form Invalid', + 'name_nl': 'Test Form Invalid NL', + 'thematic': self.thematic.pk, + 'priority': '3', + 'is_active': True, + 'notify_contract_recipients': True, + 'notification_emails': 'adresse_pas_valide', + }) + self.assertFalse(form_invalid.is_valid()) + self.assertIn('notification_emails', form_invalid.errors) diff --git a/loko/interventions/views/helpers.py b/loko/interventions/views/helpers.py index 094072a..7c91f27 100644 --- a/loko/interventions/views/helpers.py +++ b/loko/interventions/views/helpers.py @@ -3681,41 +3681,52 @@ def is_user_same_as_provider(user, intervention) -> bool: def get_contract_recipients(contract, intervention=None) -> List[str]: - """Retourne une liste unique d'adresses email associées au contrat.""" + """Retourne une liste unique d'adresses email associées au contrat et/ou au symptôme.""" # En dev/test, redirige tous les emails vers l'adresse de test email_override = getattr(settings, 'EMAIL_OVERRIDE', None) if email_override: return [email_override] - if not contract: + if not contract and intervention: + contract = getattr(intervention, 'contract', None) + + if contract and getattr(contract, 'is_blocked', False): return [] - if getattr(contract, 'is_blocked', False): - return [] + symptom = getattr(intervention, 'symptom', None) if intervention else None + notify_contract = getattr(symptom, 'notify_contract_recipients', True) if symptom else True - if hasattr(contract, 'send_email_notifications') and not contract.send_email_notifications: - return [] - - if getattr(contract, 'email_only_if_urgent', False) and intervention and getattr(intervention, 'priority', None) != '1': - return [] - emails = set() - # 1) Champ texte sur le contrat (séparé par ; ou ,) - if hasattr(contract, 'notification_emails') and contract.notification_emails: - for e in re.split(r'[;,]', contract.notification_emails): - e = e.strip() - if e: - emails.add(e) + # 1) Destinataires du contrat (si notif contrat autorisée pour ce symptôme) + if notify_contract and contract: + contract_notifs_enabled = not (hasattr(contract, 'send_email_notifications') and not contract.send_email_notifications) + urgent_only_ok = not (getattr(contract, 'email_only_if_urgent', False) and intervention and getattr(intervention, 'priority', None) != '1') - # 2) Relations de contacts (ex: contract.contacts) - if hasattr(contract, 'contacts'): - qs = contract.contacts.all() - # Filtre facultatif si vous avez un flag pour recevoir les rapports - if hasattr(qs.model, 'receive_intervention_reports'): - qs = qs.filter(receive_intervention_reports=True) - emails.update([c.email for c in qs if getattr(c, 'email', None)]) + if contract_notifs_enabled and urgent_only_ok: + # Champ texte sur le contrat (séparé par ; ou ,) + if hasattr(contract, 'notification_emails') and contract.notification_emails: + for e in re.split(r'[;,]', contract.notification_emails): + e = e.strip() + if e: + emails.add(e) + + # Relations de contacts (ex: contract.contacts) + if hasattr(contract, 'contacts'): + qs = contract.contacts.all() + if hasattr(qs.model, 'receive_intervention_reports'): + qs = qs.filter(receive_intervention_reports=True) + emails.update([c.email for c in qs if getattr(c, 'email', None)]) + + # 2) Destinataires supplémentaires configurés sur le symptôme + if symptom: + for raw in [getattr(symptom, 'notification_emails', None), getattr(symptom, 'internal_notification_emails', None)]: + if raw: + for e in re.split(r'[;,]', raw): + e = e.strip() + if e: + emails.add(e) return [e for e in emails if e] @@ -3723,36 +3734,46 @@ def get_contract_recipients(contract, intervention=None) -> List[str]: def get_contract_cc_recipients(contract, intervention=None) -> List[str]: - """Retourne une liste unique d'adresses email en copie (CC) associées au contrat.""" + """Retourne une liste unique d'adresses email en copie (CC) associées au contrat et/ou au symptôme.""" # En dev/test, ne pas utiliser de CC pour éviter les envois externes email_override = getattr(settings, 'EMAIL_OVERRIDE', None) if email_override: return [] - if not contract: + if not contract and intervention: + contract = getattr(intervention, 'contract', None) + + if contract and getattr(contract, 'is_blocked', False): return [] - if getattr(contract, 'is_blocked', False): - return [] + symptom = getattr(intervention, 'symptom', None) if intervention else None + notify_contract = getattr(symptom, 'notify_contract_recipients', True) if symptom else True - if hasattr(contract, 'send_email_notifications') and not contract.send_email_notifications: - return [] - - if getattr(contract, 'email_only_if_urgent', False) and intervention and getattr(intervention, 'priority', None) != '1': - return [] - emails = set() - # Champ texte CC sur le contrat (séparé par ; ou ,) - if hasattr(contract, 'cc_notification_emails') and contract.cc_notification_emails: - for e in re.split(r'[;,]', contract.cc_notification_emails): + # 1) CC du contrat (si notif contrat autorisée pour ce symptôme) + if notify_contract and contract: + contract_notifs_enabled = not (hasattr(contract, 'send_email_notifications') and not contract.send_email_notifications) + urgent_only_ok = not (getattr(contract, 'email_only_if_urgent', False) and intervention and getattr(intervention, 'priority', None) != '1') + + if contract_notifs_enabled and urgent_only_ok: + if hasattr(contract, 'cc_notification_emails') and contract.cc_notification_emails: + for e in re.split(r'[;,]', contract.cc_notification_emails): + e = e.strip() + if e: + emails.add(e) + + # 2) CC supplémentaires configurés sur le symptôme + if symptom and hasattr(symptom, 'cc_notification_emails') and symptom.cc_notification_emails: + for e in re.split(r'[;,]', symptom.cc_notification_emails): e = e.strip() if e: emails.add(e) - # Toujours ajouter l'email par défaut en CC - emails.add(getattr(settings, 'EMAIL_TECH_SUPPORT', 'bm.perm.tech@sprb.brussels')) + # Toujours ajouter l'email par défaut en CC si des destinataires ou un contrat/symptôme existent + if contract or symptom: + emails.add(getattr(settings, 'EMAIL_TECH_SUPPORT', 'bm.perm.tech@sprb.brussels')) return [e for e in emails if e] diff --git a/loko/interventions/views/workflow.py b/loko/interventions/views/workflow.py index b555c31..29ebb1d 100644 --- a/loko/interventions/views/workflow.py +++ b/loko/interventions/views/workflow.py @@ -807,8 +807,8 @@ def attempt_status_update(request, intervention, user, new_status, pause_reason= reverse("interventions:intervention_detail", args=[intervention.id]) ) - recipients = get_contract_recipients(contract, intervention) if contract else [] - cc_recipients = get_contract_cc_recipients(contract, intervention) if contract else [getattr(settings, 'EMAIL_TECH_SUPPORT', 'bm.perm.tech@sprb.brussels')] + recipients = get_contract_recipients(contract, intervention) + cc_recipients = get_contract_cc_recipients(contract, intervention) # Si le contrat utilise l'envoi groupé journalier, ne pas envoyer l'email individuel if recipients and not (contract and getattr(contract, 'send_daily_summary', False)): @@ -1000,8 +1000,8 @@ def attempt_status_update(request, intervention, user, new_status, pause_reason= intervention.save(update_fields=['intervention_sheet']) contract = getattr(intervention, 'contract', None) - recipients = get_contract_recipients(contract, intervention) if contract else [] - cc_recipients = get_contract_cc_recipients(contract, intervention) if contract else [getattr(settings, 'EMAIL_TECH_SUPPORT', 'bm.perm.tech@sprb.brussels')] + recipients = get_contract_recipients(contract, intervention) + cc_recipients = get_contract_cc_recipients(contract, intervention) if recipients: detail_url = request.build_absolute_uri( @@ -1241,7 +1241,7 @@ def send_intervention_reminder(request, intervention_id): if not recipients: return JsonResponse({ 'success': False, - 'error': _("Aucun destinataire n'a été configuré pour le contrat associé."), + 'error': _("Aucun destinataire n'a été configuré pour le contrat ou le symptôme associé."), }, status=400) buffer = io.BytesIO() @@ -1381,7 +1381,7 @@ def send_intervention_modification(request, intervention_id): if not recipients: return JsonResponse({ 'success': False, - 'error': _("Aucun destinataire n'a été configuré pour le contrat associé."), + 'error': _("Aucun destinataire n'a été configuré pour le contrat ou le symptôme associé."), }, status=400) buffer = io.BytesIO()