git add loko/contracts/ loko/interventions/
git commit -m "feat(interventions): configure team maintain types for control auto-assignment and remove structures limitation"
This commit is contained in:
parent
41679d82e9
commit
927c049d02
12 changed files with 170 additions and 19 deletions
|
|
@ -314,23 +314,27 @@ class CompanyTeamAdminForm(forms.ModelForm):
|
||||||
class CompanyTeamAdmin(gis_admin.GISModelAdmin):
|
class CompanyTeamAdmin(gis_admin.GISModelAdmin):
|
||||||
form = CompanyTeamAdminForm
|
form = CompanyTeamAdminForm
|
||||||
list_display = (
|
list_display = (
|
||||||
'name', 'company', 'thematics_list', 'operation_types_list', 'has_geom',
|
'name', 'company', 'thematics_list', 'maintain_types_list', 'operation_types_list', 'has_geom',
|
||||||
'members_count', 'can_generate_daily_report', 'photos_required',
|
'members_count', 'can_generate_daily_report', 'photos_required',
|
||||||
'restrict_today_interventions_visibility', 'visibility_start_hour',
|
'restrict_today_interventions_visibility', 'visibility_start_hour',
|
||||||
'restrict_to_assigned_interventions'
|
'restrict_to_assigned_interventions'
|
||||||
)
|
)
|
||||||
search_fields = ('name', 'company__name')
|
search_fields = ('name', 'company__name')
|
||||||
list_filter = (
|
list_filter = (
|
||||||
'company', 'can_generate_daily_report', 'photos_required',
|
'company', 'maintain_types', 'can_generate_daily_report', 'photos_required',
|
||||||
'restrict_today_interventions_visibility', 'restrict_to_assigned_interventions'
|
'restrict_today_interventions_visibility', 'restrict_to_assigned_interventions'
|
||||||
)
|
)
|
||||||
filter_horizontal = ('thematics', 'asset_categories')
|
filter_horizontal = ('thematics', 'asset_categories', 'maintain_types')
|
||||||
inlines = [CompanyTeamMemberInline]
|
inlines = [CompanyTeamMemberInline]
|
||||||
|
|
||||||
def thematics_list(self, obj):
|
def thematics_list(self, obj):
|
||||||
return ", ".join([thematic.name for thematic in obj.thematics.all()])
|
return ", ".join([thematic.name for thematic in obj.thematics.all()])
|
||||||
thematics_list.short_description = 'Thématiques'
|
thematics_list.short_description = 'Thématiques'
|
||||||
|
|
||||||
|
def maintain_types_list(self, obj):
|
||||||
|
return obj.get_maintain_types_display()
|
||||||
|
maintain_types_list.short_description = _("Types de maintenance")
|
||||||
|
|
||||||
def operation_types_list(self, obj):
|
def operation_types_list(self, obj):
|
||||||
return obj.get_operation_types_display() or "—"
|
return obj.get_operation_types_display() or "—"
|
||||||
operation_types_list.short_description = _("Opérations gérées")
|
operation_types_list.short_description = _("Opérations gérées")
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -2375,3 +2375,11 @@ msgstr "If enabled, a control team is automatically assigned to corrective inter
|
||||||
msgid "Contrôleur assigné"
|
msgid "Contrôleur assigné"
|
||||||
msgstr "Assigned controller"
|
msgstr "Assigned controller"
|
||||||
|
|
||||||
|
#: contracts/models.py:450
|
||||||
|
msgid "Si activé, une équipe de contrôle est automatiquement assignée aux interventions de ce contrat."
|
||||||
|
msgstr "If enabled, a control team is automatically assigned to interventions for this contract."
|
||||||
|
|
||||||
|
#: contracts/models.py:93
|
||||||
|
msgid "Types de maintenance pris en charge par cette équipe (corrective, préventive, améliorative). Si aucun n'est sélectionné, tous les types sont pris en charge."
|
||||||
|
msgstr "Maintenance types handled by this team (corrective, preventive, ameliorative). If none is selected, all types are supported."
|
||||||
|
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -2425,3 +2425,11 @@ msgstr "Si activé, une équipe de contrôle est automatiquement assignée aux i
|
||||||
msgid "Contrôleur assigné"
|
msgid "Contrôleur assigné"
|
||||||
msgstr "Contrôleur assigné"
|
msgstr "Contrôleur assigné"
|
||||||
|
|
||||||
|
#: contracts/models.py:450
|
||||||
|
msgid "Si activé, une équipe de contrôle est automatiquement assignée aux interventions de ce contrat."
|
||||||
|
msgstr "Si activé, une équipe de contrôle est automatiquement assignée aux interventions de ce contrat."
|
||||||
|
|
||||||
|
#: contracts/models.py:93
|
||||||
|
msgid "Types de maintenance pris en charge par cette équipe (corrective, préventive, améliorative). Si aucun n'est sélectionné, tous les types sont pris en charge."
|
||||||
|
msgstr "Types de maintenance pris en charge par cette équipe (corrective, préventive, améliorative). Si aucun n'est sélectionné, tous les types sont pris en charge."
|
||||||
|
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -2381,3 +2381,11 @@ msgstr "Indien geactiveerd wordt automatisch een controleteam toegewezen aan de
|
||||||
msgid "Contrôleur assigné"
|
msgid "Contrôleur assigné"
|
||||||
msgstr "Toegewezen controleur"
|
msgstr "Toegewezen controleur"
|
||||||
|
|
||||||
|
#: contracts/models.py:450
|
||||||
|
msgid "Si activé, une équipe de contrôle est automatiquement assignée aux interventions de ce contrat."
|
||||||
|
msgstr "Indien geactiveerd wordt automatisch een controleteam toegewezen aan de interventies van dit contract."
|
||||||
|
|
||||||
|
#: contracts/models.py:93
|
||||||
|
msgid "Types de maintenance pris en charge par cette équipe (corrective, préventive, améliorative). Si aucun n'est sélectionné, tous les types sont pris en charge."
|
||||||
|
msgstr "Onderhoudstypes ondersteund door dit team (correctief, preventief, verbeterend). Indien niets is geselecteerd, worden alle types ondersteund."
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
# Generated by Django 6.0.7 on 2026-09-30 17:16
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
def seed_maintain_types(apps, schema_editor):
|
||||||
|
MaintainType = apps.get_model('contracts', 'MaintainType')
|
||||||
|
types_data = [
|
||||||
|
('corrective', 'Maintenance corrective', 'Correctief onderhoud'),
|
||||||
|
('preventive', 'Maintenance préventive', 'Preventief onderhoud'),
|
||||||
|
('ameliorative', 'Maintenance améliorative', 'Verbeterend onderhoud'),
|
||||||
|
]
|
||||||
|
for code, name_fr, name_nl in types_data:
|
||||||
|
MaintainType.objects.get_or_create(
|
||||||
|
code=code,
|
||||||
|
defaults={'name_fr': name_fr, 'name_nl': name_nl}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('contracts', '0012_reconcile_team_history_leave_dates'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='companyteam',
|
||||||
|
name='maintain_types',
|
||||||
|
field=models.ManyToManyField(blank=True, help_text="Types de maintenance pris en charge par cette équipe (corrective, préventive, améliorative). Si aucun n'est sélectionné, tous les types sont pris en charge.", related_name='company_teams', to='contracts.maintaintype', verbose_name='Types de maintenance'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='contract',
|
||||||
|
name='auto_assign_control_team',
|
||||||
|
field=models.BooleanField(default=False, help_text='Si activé, une équipe de contrôle est automatiquement assignée aux interventions de ce contrat.', verbose_name='Assigner automatiquement une équipe de contrôle'),
|
||||||
|
),
|
||||||
|
migrations.RunPython(seed_maintain_types, migrations.RunPython.noop),
|
||||||
|
]
|
||||||
|
|
@ -86,6 +86,13 @@ class CompanyTeam(models.Model):
|
||||||
verbose_name=_("Catégories d'assets gérées"),
|
verbose_name=_("Catégories d'assets gérées"),
|
||||||
help_text=_("Catégories d'assets dont cette équipe est responsable."),
|
help_text=_("Catégories d'assets dont cette équipe est responsable."),
|
||||||
)
|
)
|
||||||
|
maintain_types = models.ManyToManyField(
|
||||||
|
MaintainType,
|
||||||
|
blank=True,
|
||||||
|
related_name="company_teams",
|
||||||
|
verbose_name=_("Types de maintenance"),
|
||||||
|
help_text=_("Types de maintenance pris en charge par cette équipe (corrective, préventive, améliorative). Si aucun n'est sélectionné, tous les types sont pris en charge."),
|
||||||
|
)
|
||||||
geom = gis_models.MultiPolygonField(
|
geom = gis_models.MultiPolygonField(
|
||||||
srid=3812,
|
srid=3812,
|
||||||
null=True,
|
null=True,
|
||||||
|
|
@ -147,6 +154,23 @@ class CompanyTeam(models.Model):
|
||||||
mapping = dict(self.OPERATION_TYPES)
|
mapping = dict(self.OPERATION_TYPES)
|
||||||
return ", ".join(str(mapping.get(op, op)) for op in (self.operation_types or []))
|
return ", ".join(str(mapping.get(op, op)) for op in (self.operation_types or []))
|
||||||
|
|
||||||
|
def handles_maintain_type(self, maintain_type_code: str) -> bool:
|
||||||
|
"""Indique si l'équipe prend en charge le type de maintenance donné.
|
||||||
|
Si aucun type n'est configuré, l'équipe prend en charge tous les types."""
|
||||||
|
if not maintain_type_code:
|
||||||
|
return True
|
||||||
|
types = list(self.maintain_types.all())
|
||||||
|
if not types:
|
||||||
|
return True
|
||||||
|
return any(t.code == maintain_type_code for t in types)
|
||||||
|
|
||||||
|
def get_maintain_types_display(self) -> str:
|
||||||
|
"""Retourne la liste lisible des types de maintenance gérés."""
|
||||||
|
types = list(self.maintain_types.all())
|
||||||
|
if not types:
|
||||||
|
return str(_("Tous"))
|
||||||
|
return ", ".join(str(t) for t in types)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"{self.name} ({self.company.name})"
|
return f"{self.name} ({self.company.name})"
|
||||||
|
|
||||||
|
|
@ -447,7 +471,7 @@ class Contract(models.Model):
|
||||||
auto_assign_control_team = models.BooleanField(
|
auto_assign_control_team = models.BooleanField(
|
||||||
default=False,
|
default=False,
|
||||||
verbose_name=_("Assigner automatiquement une équipe de contrôle"),
|
verbose_name=_("Assigner automatiquement une équipe de contrôle"),
|
||||||
help_text=_("Si activé, une équipe de contrôle est automatiquement assignée aux interventions correctives de ce contrat."),
|
help_text=_("Si activé, une équipe de contrôle est automatiquement assignée aux interventions de ce contrat."),
|
||||||
)
|
)
|
||||||
is_blocked = models.BooleanField(
|
is_blocked = models.BooleanField(
|
||||||
default=False,
|
default=False,
|
||||||
|
|
|
||||||
|
|
@ -658,11 +658,9 @@ class Intervention(models.Model):
|
||||||
if user_config and not user_config.is_intern:
|
if user_config and not user_config.is_intern:
|
||||||
self.created_by_provider = True
|
self.created_by_provider = True
|
||||||
|
|
||||||
# Attribution automatique de l'équipe de contrôle pour les interventions correctives Structures
|
# Attribution automatique de l'équipe de contrôle
|
||||||
if (
|
if (
|
||||||
self.maintain_type == 'corrective'
|
self.contract and getattr(self.contract, 'auto_assign_control_team', False)
|
||||||
and self.thematic and getattr(self.thematic, 'code', None) == 'structures'
|
|
||||||
and self.contract and getattr(self.contract, 'auto_assign_control_team', False)
|
|
||||||
and not self.assigned_control_team_id
|
and not self.assigned_control_team_id
|
||||||
):
|
):
|
||||||
try:
|
try:
|
||||||
|
|
|
||||||
|
|
@ -10,16 +10,18 @@ def resolve_control_team_and_member(
|
||||||
point=None,
|
point=None,
|
||||||
scheduled_date=None,
|
scheduled_date=None,
|
||||||
thematic=None,
|
thematic=None,
|
||||||
|
maintain_type=None,
|
||||||
locations=None,
|
locations=None,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Détermine l'équipe de contrôle (CompanyTeam) et le contrôleur (CompanyMember) :
|
Détermine l'équipe de contrôle (CompanyTeam) et le contrôleur (CompanyMember) :
|
||||||
1. Équipes candidates gérant l'opération 'controls' (OPERATION_CONTROLS).
|
1. Équipes candidates gérant l'opération 'controls' (OPERATION_CONTROLS).
|
||||||
2. Filtrage thématique si l'équipe restreint ses thématiques.
|
2. Filtrage thématique si l'équipe restreint ses thématiques.
|
||||||
3. Résolution spatiale si une géométrie est définie (cible et équipes) :
|
3. Filtrage par type de maintenance (corrective, preventive, ameliorative) si l'équipe restreint ses types.
|
||||||
|
4. Résolution spatiale si une géométrie est définie (cible et équipes) :
|
||||||
- team.geom.intersects(target_geom) ou team.geom.contains(point).
|
- team.geom.intersects(target_geom) ou team.geom.contains(point).
|
||||||
- Fallback sur toutes les équipes candidates si aucune géométrie n'est définie.
|
- Fallback sur toutes les équipes candidates si aucune géométrie n'est définie.
|
||||||
4. Calendrier d'assignation à la date scheduled_date :
|
5. Calendrier d'assignation à la date scheduled_date :
|
||||||
- Évalue le nombre d'agents planifiés et non absents.
|
- Évalue le nombre d'agents planifiés et non absents.
|
||||||
- Si aucun agent n'est disponible ou aucune date : retourne (None, None).
|
- Si aucun agent n'est disponible ou aucune date : retourne (None, None).
|
||||||
- Sélectionne l'équipe ayant le plus d'agents disponibles.
|
- Sélectionne l'équipe ayant le plus d'agents disponibles.
|
||||||
|
|
@ -28,20 +30,31 @@ def resolve_control_team_and_member(
|
||||||
from contracts.models import CompanyTeam, CompanyMember
|
from contracts.models import CompanyTeam, CompanyMember
|
||||||
from contracts.services_teams import get_teams_composition_for_period, get_member_absences_for_period
|
from contracts.services_teams import get_teams_composition_for_period, get_member_absences_for_period
|
||||||
|
|
||||||
teams_qs = CompanyTeam.objects.all().select_related('company')
|
teams_qs = CompanyTeam.objects.all().select_related('company').prefetch_related('thematics', 'maintain_types')
|
||||||
candidate_teams = [t for t in teams_qs if t.handles_operation(CompanyTeam.OPERATION_CONTROLS)]
|
candidate_teams = [t for t in teams_qs if t.handles_operation(CompanyTeam.OPERATION_CONTROLS)]
|
||||||
|
|
||||||
if not candidate_teams:
|
if not candidate_teams:
|
||||||
return None, None
|
return None, None
|
||||||
|
|
||||||
# 1. Filtrage thématique
|
# 1. Filtrage thématique si l'équipe restreint ses thématiques
|
||||||
if thematic:
|
if thematic:
|
||||||
filtered_thematic = [
|
candidate_teams = [
|
||||||
t for t in candidate_teams
|
t for t in candidate_teams
|
||||||
if not t.thematics.exists() or t.thematics.filter(id=thematic.id).exists()
|
if not t.thematics.all() or any(thm.id == thematic.id for thm in t.thematics.all())
|
||||||
]
|
]
|
||||||
if filtered_thematic:
|
|
||||||
candidate_teams = filtered_thematic
|
if not candidate_teams:
|
||||||
|
return None, None
|
||||||
|
|
||||||
|
# 2. Filtrage par type de maintenance si l'équipe restreint ses types de maintenance
|
||||||
|
if maintain_type:
|
||||||
|
candidate_teams = [
|
||||||
|
t for t in candidate_teams
|
||||||
|
if t.handles_maintain_type(maintain_type)
|
||||||
|
]
|
||||||
|
|
||||||
|
if not candidate_teams:
|
||||||
|
return None, None
|
||||||
|
|
||||||
# 2. Résolution spatiale si géométrie définie
|
# 2. Résolution spatiale si géométrie définie
|
||||||
has_target_geom = bool(target_geom or point or locations)
|
has_target_geom = bool(target_geom or point or locations)
|
||||||
|
|
@ -150,5 +163,6 @@ def resolve_control_team_and_member_for_intervention(intervention):
|
||||||
point=point,
|
point=point,
|
||||||
scheduled_date=target_date,
|
scheduled_date=target_date,
|
||||||
thematic=intervention.thematic,
|
thematic=intervention.thematic,
|
||||||
|
maintain_type=intervention.maintain_type,
|
||||||
locations=locs,
|
locations=locs,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ from django.contrib.gis.geos import MultiPolygon, Polygon, Point
|
||||||
|
|
||||||
from common.models import Role, UserConfig, Thematic, UserContractAccess
|
from common.models import Role, UserConfig, Thematic, UserContractAccess
|
||||||
from contracts.models import (
|
from contracts.models import (
|
||||||
Company, CompanyTeam, CompanyMember, CompanyMemberHistory, CompanyMemberAbsence, Contract
|
Company, CompanyTeam, CompanyMember, CompanyMemberHistory, CompanyMemberAbsence, Contract, MaintainType
|
||||||
)
|
)
|
||||||
from interventions.models import Intervention, InterventionContractPost, ContractPost
|
from interventions.models import Intervention, InterventionContractPost, ContractPost
|
||||||
from interventions.permissions import can_view_intervention, can_control_intervention
|
from interventions.permissions import can_view_intervention, can_control_intervention
|
||||||
|
|
@ -30,6 +30,17 @@ class ControlAssignmentTests(TestCase):
|
||||||
code='roads', defaults={'name_fr': 'Voirie', 'name_nl': 'Wegen'}
|
code='roads', defaults={'name_fr': 'Voirie', 'name_nl': 'Wegen'}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Types de maintenance
|
||||||
|
self.mt_corrective, _ = MaintainType.objects.get_or_create(
|
||||||
|
code='corrective', defaults={'name_fr': 'Maintenance corrective', 'name_nl': 'Correctief onderhoud'}
|
||||||
|
)
|
||||||
|
self.mt_preventive, _ = MaintainType.objects.get_or_create(
|
||||||
|
code='preventive', defaults={'name_fr': 'Maintenance préventive', 'name_nl': 'Preventief onderhoud'}
|
||||||
|
)
|
||||||
|
self.mt_ameliorative, _ = MaintainType.objects.get_or_create(
|
||||||
|
code='ameliorative', defaults={'name_fr': 'Maintenance améliorative', 'name_nl': 'Verbeterend onderhoud'}
|
||||||
|
)
|
||||||
|
|
||||||
# Société & Prestataire
|
# Société & Prestataire
|
||||||
self.company = Company.objects.create(name='Prestataire Structures')
|
self.company = Company.objects.create(name='Prestataire Structures')
|
||||||
self.org_intern = Company.objects.create(name='Organisme Interne Contrôle')
|
self.org_intern = Company.objects.create(name='Organisme Interne Contrôle')
|
||||||
|
|
@ -51,6 +62,8 @@ class ControlAssignmentTests(TestCase):
|
||||||
operation_types=[CompanyTeam.OPERATION_CONTROLS],
|
operation_types=[CompanyTeam.OPERATION_CONTROLS],
|
||||||
geom=self.ctrl_geom
|
geom=self.ctrl_geom
|
||||||
)
|
)
|
||||||
|
self.team_ctrl.thematics.add(self.thematic_structures)
|
||||||
|
self.team_ctrl.maintain_types.add(self.mt_corrective)
|
||||||
|
|
||||||
# Membre contrôleur
|
# Membre contrôleur
|
||||||
self.user_ctrl = self.User.objects.create_user(username='ctrl_agent', password='pwd')
|
self.user_ctrl = self.User.objects.create_user(username='ctrl_agent', password='pwd')
|
||||||
|
|
@ -108,7 +121,7 @@ class ControlAssignmentTests(TestCase):
|
||||||
self.assertEqual(itv.assigned_controller, self.member_ctrl)
|
self.assertEqual(itv.assigned_controller, self.member_ctrl)
|
||||||
|
|
||||||
def test_no_auto_assignment_if_not_corrective(self):
|
def test_no_auto_assignment_if_not_corrective(self):
|
||||||
"""Une intervention préventive ne doit pas recevoir d'attribution automatique de contrôle."""
|
"""Une intervention préventive ne doit pas être assignée si l'équipe ne gère que le correctif."""
|
||||||
itv = Intervention.objects.create(
|
itv = Intervention.objects.create(
|
||||||
maintain_type='preventive',
|
maintain_type='preventive',
|
||||||
thematic=self.thematic_structures,
|
thematic=self.thematic_structures,
|
||||||
|
|
@ -118,8 +131,32 @@ class ControlAssignmentTests(TestCase):
|
||||||
self.assertIsNone(itv.assigned_control_team)
|
self.assertIsNone(itv.assigned_control_team)
|
||||||
self.assertIsNone(itv.assigned_controller)
|
self.assertIsNone(itv.assigned_controller)
|
||||||
|
|
||||||
|
def test_auto_assignment_preventive_when_team_handles_preventive(self):
|
||||||
|
"""Une intervention préventive est assignée dès lors que l'équipe prend en charge le préventif."""
|
||||||
|
self.team_ctrl.maintain_types.add(self.mt_preventive)
|
||||||
|
itv = Intervention.objects.create(
|
||||||
|
maintain_type='preventive',
|
||||||
|
thematic=self.thematic_structures,
|
||||||
|
contract=self.contract_auto,
|
||||||
|
planned_begin_time=timezone.now(),
|
||||||
|
)
|
||||||
|
self.assertEqual(itv.assigned_control_team, self.team_ctrl)
|
||||||
|
self.assertEqual(itv.assigned_controller, self.member_ctrl)
|
||||||
|
|
||||||
|
def test_auto_assignment_when_team_has_no_maintain_types_restriction(self):
|
||||||
|
"""Une équipe sans restriction de types de maintenance prend en charge tous les types (y compris amélioratif)."""
|
||||||
|
self.team_ctrl.maintain_types.clear()
|
||||||
|
itv = Intervention.objects.create(
|
||||||
|
maintain_type='ameliorative',
|
||||||
|
thematic=self.thematic_structures,
|
||||||
|
contract=self.contract_auto,
|
||||||
|
planned_begin_time=timezone.now(),
|
||||||
|
)
|
||||||
|
self.assertEqual(itv.assigned_control_team, self.team_ctrl)
|
||||||
|
self.assertEqual(itv.assigned_controller, self.member_ctrl)
|
||||||
|
|
||||||
def test_no_auto_assignment_if_not_structures(self):
|
def test_no_auto_assignment_if_not_structures(self):
|
||||||
"""Une intervention d'une autre thématique ne doit pas recevoir d'attribution automatique de contrôle."""
|
"""Une intervention d'une autre thématique ne doit pas être assignée à une équipe restreinte à structures."""
|
||||||
itv = Intervention.objects.create(
|
itv = Intervention.objects.create(
|
||||||
maintain_type='corrective',
|
maintain_type='corrective',
|
||||||
thematic=self.thematic_roads,
|
thematic=self.thematic_roads,
|
||||||
|
|
@ -129,6 +166,18 @@ class ControlAssignmentTests(TestCase):
|
||||||
self.assertIsNone(itv.assigned_control_team)
|
self.assertIsNone(itv.assigned_control_team)
|
||||||
self.assertIsNone(itv.assigned_controller)
|
self.assertIsNone(itv.assigned_controller)
|
||||||
|
|
||||||
|
def test_auto_assignment_other_thematic_when_team_matches(self):
|
||||||
|
"""Une intervention d'une autre thématique (ex: voirie) est assignée si une équipe de contrôle correspondante existe."""
|
||||||
|
self.team_ctrl.thematics.add(self.thematic_roads)
|
||||||
|
itv = Intervention.objects.create(
|
||||||
|
maintain_type='corrective',
|
||||||
|
thematic=self.thematic_roads,
|
||||||
|
contract=self.contract_auto,
|
||||||
|
planned_begin_time=timezone.now(),
|
||||||
|
)
|
||||||
|
self.assertEqual(itv.assigned_control_team, self.team_ctrl)
|
||||||
|
self.assertEqual(itv.assigned_controller, self.member_ctrl)
|
||||||
|
|
||||||
def test_no_auto_assignment_if_contract_not_enabled(self):
|
def test_no_auto_assignment_if_contract_not_enabled(self):
|
||||||
"""Un contrat sans auto_assign_control_team ne doit pas déclencher l'attribution."""
|
"""Un contrat sans auto_assign_control_team ne doit pas déclencher l'attribution."""
|
||||||
itv = Intervention.objects.create(
|
itv = Intervention.objects.create(
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue