feat: add reprogrammation intervention type restricted to trafficlights thematic and update forms accordingly
This commit is contained in:
parent
f810041e98
commit
46f1d0d384
12 changed files with 171 additions and 5 deletions
Binary file not shown.
|
|
@ -149,6 +149,10 @@ msgstr "Construction"
|
||||||
msgid "Ramassage"
|
msgid "Ramassage"
|
||||||
msgstr "Collection"
|
msgstr "Collection"
|
||||||
|
|
||||||
|
#: interventions/models.py:105
|
||||||
|
msgid "Reprogrammation"
|
||||||
|
msgstr "Reprogramming"
|
||||||
|
|
||||||
#: interventions/models.py:108
|
#: interventions/models.py:108
|
||||||
msgid "Avarie"
|
msgid "Avarie"
|
||||||
msgstr "Damage"
|
msgstr "Damage"
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -149,6 +149,10 @@ msgstr "Construction"
|
||||||
msgid "Ramassage"
|
msgid "Ramassage"
|
||||||
msgstr "Ramassage"
|
msgstr "Ramassage"
|
||||||
|
|
||||||
|
#: interventions/models.py:105
|
||||||
|
msgid "Reprogrammation"
|
||||||
|
msgstr "Reprogrammation"
|
||||||
|
|
||||||
#: interventions/models.py:108
|
#: interventions/models.py:108
|
||||||
msgid "Avarie"
|
msgid "Avarie"
|
||||||
msgstr "Avarie"
|
msgstr "Avarie"
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -149,6 +149,10 @@ msgstr "Bouw"
|
||||||
msgid "Ramassage"
|
msgid "Ramassage"
|
||||||
msgstr "Ophaling"
|
msgstr "Ophaling"
|
||||||
|
|
||||||
|
#: interventions/models.py:105
|
||||||
|
msgid "Reprogrammation"
|
||||||
|
msgstr "Herprogrammering"
|
||||||
|
|
||||||
#: interventions/models.py:108
|
#: interventions/models.py:108
|
||||||
msgid "Avarie"
|
msgid "Avarie"
|
||||||
msgstr "Schade"
|
msgstr "Schade"
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 6.0.7 on 2026-08-19 08:31
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('interventions', '0003_operationtemplate_team'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='intervention',
|
||||||
|
name='type',
|
||||||
|
field=models.CharField(blank=True, choices=[('fixing', 'Dépannage'), ('repair', 'Réparation'), ('repair_post_preventive', 'Réparation post préventif'), ('replace', 'Remplacement'), ('care', 'Entretien'), ('control', 'Contrôle'), ('inspection', 'Inspection'), ('build', 'Construction'), ('collection', 'Ramassage'), ('reprogramming', 'Reprogrammation')], null=True),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -102,8 +102,26 @@ TYPE_CHOICES = [
|
||||||
('inspection', _('Inspection')),
|
('inspection', _('Inspection')),
|
||||||
('build', _('Construction')),
|
('build', _('Construction')),
|
||||||
('collection', _('Ramassage')),
|
('collection', _('Ramassage')),
|
||||||
|
('reprogramming', _('Reprogrammation')),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def get_type_choices(thematic=None):
|
||||||
|
"""
|
||||||
|
Renvoie la liste des choix de type d'intervention selon la thématique.
|
||||||
|
Le type 'reprogramming' (Reprogrammation) n'est disponible que pour la thématique 'trafficlights'.
|
||||||
|
"""
|
||||||
|
thematic_code = None
|
||||||
|
if thematic:
|
||||||
|
if isinstance(thematic, str):
|
||||||
|
thematic_code = thematic
|
||||||
|
elif hasattr(thematic, 'code'):
|
||||||
|
thematic_code = thematic.code
|
||||||
|
|
||||||
|
if thematic_code == 'trafficlights':
|
||||||
|
return list(TYPE_CHOICES)
|
||||||
|
return [c for c in TYPE_CHOICES if c[0] != 'reprogramming']
|
||||||
|
|
||||||
ORIGIN_TYPE_CHOICES = [
|
ORIGIN_TYPE_CHOICES = [
|
||||||
('damage', _('Avarie')),
|
('damage', _('Avarie')),
|
||||||
('defect', _('Défaut/Panne')),
|
('defect', _('Défaut/Panne')),
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ from interventions.models import (
|
||||||
OperationStatus,
|
OperationStatus,
|
||||||
STATUS_ORDERS,
|
STATUS_ORDERS,
|
||||||
InterventionDocument,
|
InterventionDocument,
|
||||||
|
get_type_choices,
|
||||||
)
|
)
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
|
@ -1665,6 +1666,114 @@ class InterventionGeojsonExternalFilterTests(TestCase):
|
||||||
self.assertIn(self.itv_external.id, ids_ext)
|
self.assertIn(self.itv_external.id, ids_ext)
|
||||||
|
|
||||||
|
|
||||||
|
class TrafficlightsReprogrammingTypeTests(TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
User = get_user_model()
|
||||||
|
self.user = User.objects.create_user(username='admin_tl', password='pwd')
|
||||||
|
self.role_admin = Role.objects.create(name='admin')
|
||||||
|
self.user_config = UserConfig.objects.create(user=self.user, is_intern=True)
|
||||||
|
self.user_config.roles.add(self.role_admin)
|
||||||
|
|
||||||
|
self.tl_thematic = Thematic.objects.create(code='trafficlights', name_fr='Feux tricolores', name_nl='Verkeerslichten')
|
||||||
|
self.struct_thematic = Thematic.objects.create(code='structures', name_fr='Ouvrages d\'art', name_nl='Kunstwerken')
|
||||||
|
|
||||||
|
UserThematics.objects.create(user_config=self.user_config, thematic=self.tl_thematic, can_edit_interventions=True)
|
||||||
|
UserThematics.objects.create(user_config=self.user_config, thematic=self.struct_thematic, can_edit_interventions=True)
|
||||||
|
|
||||||
|
self.itv_tl = Intervention.objects.create(
|
||||||
|
code='ITV-TL-001',
|
||||||
|
title='Intervention Feux',
|
||||||
|
status='in_preparation',
|
||||||
|
thematic=self.tl_thematic,
|
||||||
|
type='fixing',
|
||||||
|
created_by=self.user
|
||||||
|
)
|
||||||
|
self.itv_struct = Intervention.objects.create(
|
||||||
|
code='ITV-ST-001',
|
||||||
|
title='Intervention Ouvrage',
|
||||||
|
status='in_preparation',
|
||||||
|
thematic=self.struct_thematic,
|
||||||
|
type='fixing',
|
||||||
|
created_by=self.user
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_get_type_choices(self):
|
||||||
|
tl_choices = [c[0] for c in get_type_choices(self.tl_thematic)]
|
||||||
|
self.assertIn('reprogramming', tl_choices)
|
||||||
|
self.assertIn('fixing', tl_choices)
|
||||||
|
|
||||||
|
tl_str_choices = [c[0] for c in get_type_choices('trafficlights')]
|
||||||
|
self.assertIn('reprogramming', tl_str_choices)
|
||||||
|
|
||||||
|
struct_choices = [c[0] for c in get_type_choices(self.struct_thematic)]
|
||||||
|
self.assertNotIn('reprogramming', struct_choices)
|
||||||
|
self.assertIn('fixing', struct_choices)
|
||||||
|
|
||||||
|
struct_str_choices = [c[0] for c in get_type_choices('structures')]
|
||||||
|
self.assertNotIn('reprogramming', struct_str_choices)
|
||||||
|
|
||||||
|
none_choices = [c[0] for c in get_type_choices(None)]
|
||||||
|
self.assertNotIn('reprogramming', none_choices)
|
||||||
|
|
||||||
|
def test_intervention_form_choices(self):
|
||||||
|
from interventions.views.forms import InterventionForm
|
||||||
|
form_tl = InterventionForm(thematic=self.tl_thematic, user=self.user)
|
||||||
|
tl_choices = [c[0] for c in form_tl.fields['type'].choices]
|
||||||
|
self.assertIn('reprogramming', tl_choices)
|
||||||
|
|
||||||
|
form_struct = InterventionForm(thematic=self.struct_thematic, user=self.user)
|
||||||
|
struct_choices = [c[0] for c in form_struct.fields['type'].choices]
|
||||||
|
self.assertNotIn('reprogramming', struct_choices)
|
||||||
|
|
||||||
|
def test_preventive_intervention_form_choices(self):
|
||||||
|
from interventions.views.forms import PreventiveInterventionForm
|
||||||
|
form_tl = PreventiveInterventionForm(thematic=self.tl_thematic, user=self.user)
|
||||||
|
tl_choices = [c[0] for c in form_tl.fields['type'].choices]
|
||||||
|
self.assertIn('reprogramming', tl_choices)
|
||||||
|
|
||||||
|
form_struct = PreventiveInterventionForm(thematic=self.struct_thematic, user=self.user)
|
||||||
|
struct_choices = [c[0] for c in form_struct.fields['type'].choices]
|
||||||
|
self.assertNotIn('reprogramming', struct_choices)
|
||||||
|
|
||||||
|
def test_intervention_detail_context(self):
|
||||||
|
self.client.force_login(self.user)
|
||||||
|
|
||||||
|
resp_tl = self.client.get(reverse('interventions:intervention_detail', kwargs={'intervention_id': self.itv_tl.id}))
|
||||||
|
self.assertEqual(resp_tl.status_code, 200)
|
||||||
|
tl_type_choices = [c[0] for c in resp_tl.context['type_choices']]
|
||||||
|
self.assertIn('reprogramming', tl_type_choices)
|
||||||
|
|
||||||
|
resp_struct = self.client.get(reverse('interventions:intervention_detail', kwargs={'intervention_id': self.itv_struct.id}))
|
||||||
|
self.assertEqual(resp_struct.status_code, 200)
|
||||||
|
struct_type_choices = [c[0] for c in resp_struct.context['type_choices']]
|
||||||
|
self.assertNotIn('reprogramming', struct_type_choices)
|
||||||
|
|
||||||
|
def test_update_intervention_type_reprogramming(self):
|
||||||
|
self.client.force_login(self.user)
|
||||||
|
|
||||||
|
# Updating trafficlights intervention to reprogramming -> SUCCESS
|
||||||
|
update_url_tl = reverse('interventions:update_intervention', kwargs={'intervention_id': self.itv_tl.id})
|
||||||
|
resp = self.client.post(
|
||||||
|
update_url_tl,
|
||||||
|
data=json.dumps({'type': 'reprogramming'}),
|
||||||
|
content_type='application/json'
|
||||||
|
)
|
||||||
|
self.assertEqual(resp.status_code, 200)
|
||||||
|
self.itv_tl.refresh_from_db()
|
||||||
|
self.assertEqual(self.itv_tl.type, 'reprogramming')
|
||||||
|
|
||||||
|
# Updating structures intervention to reprogramming -> REJECTED
|
||||||
|
update_url_struct = reverse('interventions:update_intervention', kwargs={'intervention_id': self.itv_struct.id})
|
||||||
|
resp = self.client.post(
|
||||||
|
update_url_struct,
|
||||||
|
data=json.dumps({'type': 'reprogramming'}),
|
||||||
|
content_type='application/json'
|
||||||
|
)
|
||||||
|
self.itv_struct.refresh_from_db()
|
||||||
|
self.assertEqual(self.itv_struct.type, 'fixing')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,7 @@ from interventions.models import (InterventionSubscription, Intervention, Interv
|
||||||
Operation, OperationTemplate, OperationTemplateItem, OperationStatus, OperationStatusSet, MeasurementDefinition,
|
Operation, OperationTemplate, OperationTemplateItem, OperationStatus, OperationStatusSet, MeasurementDefinition,
|
||||||
GuidedOperationTemplate, GuidedOperationData, GuidedOperationMedia,
|
GuidedOperationTemplate, GuidedOperationData, GuidedOperationMedia,
|
||||||
InterventionPostPeriodQuantity, InterventionEmailLog, InterventionAssignment)
|
InterventionPostPeriodQuantity, InterventionEmailLog, InterventionAssignment)
|
||||||
from interventions.models import STATUS_CHOICES, STATUS_ORDERS, MAINTAIN_CHOICES, TYPE_CHOICES, ORIGIN_TYPE_CHOICES, PAUSE_REASON_CHOICES
|
from interventions.models import STATUS_CHOICES, STATUS_ORDERS, MAINTAIN_CHOICES, TYPE_CHOICES, ORIGIN_TYPE_CHOICES, PAUSE_REASON_CHOICES, get_type_choices
|
||||||
from interventions.views.forms import (InterventionForm, InterventionNoteForm, InterventionDocumentForm,
|
from interventions.views.forms import (InterventionForm, InterventionNoteForm, InterventionDocumentForm,
|
||||||
InterventionOccupationForm, PreventiveInterventionForm, AssetsLinkForm,
|
InterventionOccupationForm, PreventiveInterventionForm, AssetsLinkForm,
|
||||||
LinkExistingInterventionsForm)
|
LinkExistingInterventionsForm)
|
||||||
|
|
@ -637,7 +637,7 @@ def intervention_detail(request, intervention_id):
|
||||||
'providers': providers,
|
'providers': providers,
|
||||||
'contracts': contracts,
|
'contracts': contracts,
|
||||||
'maintain_choices': MAINTAIN_CHOICES,
|
'maintain_choices': MAINTAIN_CHOICES,
|
||||||
'type_choices': TYPE_CHOICES,
|
'type_choices': get_type_choices(intervention.thematic),
|
||||||
'origin_type_choices': ORIGIN_TYPE_CHOICES,
|
'origin_type_choices': ORIGIN_TYPE_CHOICES,
|
||||||
'priority_choices': Intervention._meta.get_field('priority').choices,
|
'priority_choices': Intervention._meta.get_field('priority').choices,
|
||||||
'symptoms': Symptom.objects.filter(is_active=True, thematic=intervention.thematic).order_by('name_fr'),
|
'symptoms': Symptom.objects.filter(is_active=True, thematic=intervention.thematic).order_by('name_fr'),
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import bleach
|
||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from interventions.models import Intervention, InterventionNote, InterventionDocument, Symptom, OperationTemplate, InterventionOccupation, SourceCategory, OperationStatusSet, MeasurementDefinition, TYPE_CHOICES
|
from interventions.models import Intervention, InterventionNote, InterventionDocument, Symptom, OperationTemplate, InterventionOccupation, SourceCategory, OperationStatusSet, MeasurementDefinition, TYPE_CHOICES, get_type_choices
|
||||||
from assets.models import AssetCategory
|
from assets.models import AssetCategory
|
||||||
from common.models import UserConfig, UserThematics
|
from common.models import UserConfig, UserThematics
|
||||||
from contracts.models import Contract, ContractOrder, CompanyMember
|
from contracts.models import Contract, ContractOrder, CompanyMember
|
||||||
|
|
@ -93,6 +93,11 @@ class InterventionForm(forms.ModelForm):
|
||||||
(val, lbl) for val, lbl in self.fields['status'].choices if val in allowed_statuses
|
(val, lbl) for val, lbl in self.fields['status'].choices if val in allowed_statuses
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# Filtrer les types selon la thématique (ex: reprogrammation uniquement pour trafficlights)
|
||||||
|
thematic_obj = thematic or (self.instance.thematic if self.instance and getattr(self.instance, 'pk', None) else None)
|
||||||
|
if 'type' in self.fields:
|
||||||
|
self.fields['type'].choices = get_type_choices(thematic_obj)
|
||||||
|
|
||||||
type_value = self.initial.get('type') or self.data.get('type')
|
type_value = self.initial.get('type') or self.data.get('type')
|
||||||
maintain_type_value = self.initial.get('maintain_type') or self.data.get('maintain_type')
|
maintain_type_value = self.initial.get('maintain_type') or self.data.get('maintain_type')
|
||||||
if type_value == 'inspection':
|
if type_value == 'inspection':
|
||||||
|
|
@ -515,6 +520,9 @@ class PreventiveInterventionForm(forms.Form):
|
||||||
def __init__(self, *args, user=None, thematic=None, **kwargs):
|
def __init__(self, *args, user=None, thematic=None, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
if 'type' in self.fields:
|
||||||
|
self.fields['type'].choices = get_type_choices(thematic)
|
||||||
|
|
||||||
type_value = self.initial.get('type') or (self.data.get('type') if self.data else None) or 'care'
|
type_value = self.initial.get('type') or (self.data.get('type') if self.data else None) or 'care'
|
||||||
if type_value == 'inspection':
|
if type_value == 'inspection':
|
||||||
self.fields['status'].choices = [
|
self.fields['status'].choices = [
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,7 @@ from interventions.models import (InterventionSubscription, Intervention, Interv
|
||||||
GuidedOperationTemplate, GuidedOperationData, GuidedOperationMedia,
|
GuidedOperationTemplate, GuidedOperationData, GuidedOperationMedia,
|
||||||
InterventionPostPeriodQuantity, InterventionEmailLog, InterventionAssignment,
|
InterventionPostPeriodQuantity, InterventionEmailLog, InterventionAssignment,
|
||||||
DirectContractRouting)
|
DirectContractRouting)
|
||||||
from interventions.models import STATUS_CHOICES, STATUS_ORDERS, MAINTAIN_CHOICES, TYPE_CHOICES, ORIGIN_TYPE_CHOICES, PAUSE_REASON_CHOICES
|
from interventions.models import STATUS_CHOICES, STATUS_ORDERS, MAINTAIN_CHOICES, TYPE_CHOICES, ORIGIN_TYPE_CHOICES, PAUSE_REASON_CHOICES, get_type_choices
|
||||||
from interventions.views.forms import (InterventionForm, InterventionNoteForm, InterventionDocumentForm,
|
from interventions.views.forms import (InterventionForm, InterventionNoteForm, InterventionDocumentForm,
|
||||||
InterventionOccupationForm, PreventiveInterventionForm, AssetsLinkForm,
|
InterventionOccupationForm, PreventiveInterventionForm, AssetsLinkForm,
|
||||||
LinkExistingInterventionsForm)
|
LinkExistingInterventionsForm)
|
||||||
|
|
@ -2505,7 +2505,8 @@ def _update_intervention_field(intervention, field, new_value, user_config=None)
|
||||||
return False, _('Maintain Type not found')
|
return False, _('Maintain Type not found')
|
||||||
|
|
||||||
elif field == "type":
|
elif field == "type":
|
||||||
if new_value in dict(TYPE_CHOICES).keys():
|
allowed_types = dict(get_type_choices(intervention.thematic)).keys()
|
||||||
|
if new_value in allowed_types:
|
||||||
intervention.type = new_value
|
intervention.type = new_value
|
||||||
return True, None
|
return True, None
|
||||||
return False, _('Type not found')
|
return False, _('Type not found')
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue