feat: add support for dynamic intervention subtype filtering and validation
This commit is contained in:
parent
396d26414c
commit
368a167b9c
9 changed files with 387 additions and 6 deletions
|
|
@ -471,7 +471,7 @@ def get_allowed_update_fields(user, intervention):
|
|||
|
||||
# Type d'intervention, Osiris, PMO, Assets
|
||||
if intervention.status_order < STATUS_ORDERS.get('validated') and any(role in user_roles for role in ['admin', 'manager', 'controller', 'external_manager', 'inspector']):
|
||||
allowed_fields.update(['type', 'osiris', 'pmo_number'])
|
||||
allowed_fields.update(['type', 'sub_type', 'osiris', 'pmo_number'])
|
||||
|
||||
if intervention.status_order < STATUS_ORDERS.get('validated'):
|
||||
allowed_fields.add('category')
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@
|
|||
let isEditMode = false;
|
||||
let originalValues = {};
|
||||
let originalContractOptions = null; // Store original contract options for cancel
|
||||
let originalSubtypeOptions = null; // Store original subtype options for cancel
|
||||
let originalSubtypeVisible = null; // Store original subtype visibility for cancel
|
||||
let isSavingInProgress = false;
|
||||
let isAutoSavingAndNavigating = false;
|
||||
let isSubmittingAutoSavedForm = false;
|
||||
|
|
@ -29,6 +31,67 @@
|
|||
|
||||
// Setup dynamic contract loading when provider changes
|
||||
setupProviderContractLink();
|
||||
// Setup dynamic subtype loading/visibility when type changes
|
||||
setupTypeSubtypeLink();
|
||||
|
||||
/**
|
||||
* Setup the dynamic link between type and subtype selects.
|
||||
* When the type changes, filter the subtype options and show/hide the subtype field.
|
||||
*/
|
||||
function setupTypeSubtypeLink() {
|
||||
const typeField = document.querySelector('.editable-field[data-field="type"]');
|
||||
const subtypeField = document.querySelector('.editable-field[data-field="sub_type"]');
|
||||
|
||||
if (!typeField || !subtypeField) return;
|
||||
|
||||
const typeSelect = typeField.querySelector('.field-edit select');
|
||||
const subtypeSelect = subtypeField.querySelector('.field-edit select');
|
||||
|
||||
if (!typeSelect || !subtypeSelect) return;
|
||||
|
||||
let subtypes = [];
|
||||
try {
|
||||
if (subtypeField.dataset.subtypes) {
|
||||
subtypes = JSON.parse(subtypeField.dataset.subtypes);
|
||||
} else if (typeof interventionSubtypesData !== 'undefined') {
|
||||
subtypes = interventionSubtypesData;
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Error parsing subtypes:', e);
|
||||
}
|
||||
|
||||
function updateSubtype(selectedType, keepValue) {
|
||||
const available = subtypes.filter(st => !st.types || st.types.length === 0 || st.types.includes(selectedType));
|
||||
const hasSubtypes = available.length > 0;
|
||||
|
||||
if (!hasSubtypes) {
|
||||
subtypeField.classList.add('d-none');
|
||||
subtypeSelect.value = '';
|
||||
} else {
|
||||
subtypeField.classList.remove('d-none');
|
||||
const currentVal = subtypeSelect.value;
|
||||
subtypeSelect.innerHTML = '<option value="">-</option>';
|
||||
let valueStillValid = false;
|
||||
available.forEach(st => {
|
||||
const opt = document.createElement('option');
|
||||
opt.value = String(st.id);
|
||||
opt.textContent = st.name;
|
||||
if (keepValue && String(st.id) === currentVal) {
|
||||
opt.selected = true;
|
||||
valueStillValid = true;
|
||||
}
|
||||
subtypeSelect.appendChild(opt);
|
||||
});
|
||||
if (keepValue && !valueStillValid) {
|
||||
subtypeSelect.value = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
typeSelect.addEventListener('change', function() {
|
||||
updateSubtype(this.value, false);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup the dynamic link between provider and contract selects.
|
||||
|
|
@ -130,6 +193,16 @@
|
|||
}
|
||||
}
|
||||
|
||||
// Store original subtype options and visibility
|
||||
const subtypeField = document.querySelector('.editable-field[data-field="sub_type"]');
|
||||
if (subtypeField) {
|
||||
const subtypeSelect = subtypeField.querySelector('.field-edit select');
|
||||
if (subtypeSelect) {
|
||||
originalSubtypeOptions = subtypeSelect.innerHTML;
|
||||
}
|
||||
originalSubtypeVisible = !subtypeField.classList.contains('d-none');
|
||||
}
|
||||
|
||||
// Store original values
|
||||
originalValues = {};
|
||||
editableFields.forEach(field => {
|
||||
|
|
@ -196,6 +269,22 @@
|
|||
}
|
||||
}
|
||||
|
||||
// Restore original subtype options and visibility
|
||||
if (originalSubtypeOptions !== null) {
|
||||
const subtypeField = document.querySelector('.editable-field[data-field="sub_type"]');
|
||||
if (subtypeField) {
|
||||
const subtypeSelect = subtypeField.querySelector('.field-edit select');
|
||||
if (subtypeSelect) {
|
||||
subtypeSelect.innerHTML = originalSubtypeOptions;
|
||||
}
|
||||
if (originalSubtypeVisible) {
|
||||
subtypeField.classList.remove('d-none');
|
||||
} else {
|
||||
subtypeField.classList.add('d-none');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Restore original values
|
||||
editableFields.forEach(field => {
|
||||
const canEdit = field.dataset.canEdit === 'true';
|
||||
|
|
@ -234,6 +323,8 @@
|
|||
|
||||
originalValues = {};
|
||||
originalContractOptions = null;
|
||||
originalSubtypeOptions = null;
|
||||
originalSubtypeVisible = null;
|
||||
}
|
||||
|
||||
async function saveChanges(options = {}) {
|
||||
|
|
|
|||
|
|
@ -335,6 +335,11 @@
|
|||
{{ form.type }}
|
||||
{{ form.type.errors }}
|
||||
</div>
|
||||
<div class="mb-3 d-none" id="sub_type_field_wrapper">
|
||||
<label for="id_sub_type" class="form-label">{% translate "Sous-type d'intervention" %}</label>
|
||||
{{ form.sub_type }}
|
||||
{{ form.sub_type.errors }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
const thematic = '{{ thematic.code }}';
|
||||
const symptomsData = {{ symptoms_data|safe }};
|
||||
const sourceCategoriesData = {{ source_categories_data|safe }};
|
||||
const subtypesData = {{ subtypes_data|default:"[]"|safe }};
|
||||
const autocomplete_street_url = "{% url 'common:autocomplete_street' %}";
|
||||
var autocomplete_locations_url = "{% url 'assets:locations_autocomplete' %}?thematic={{ thematic.code }}{% if inspection_config %}&prioritize={% if inspection_config.default_category %}{{ inspection_config.default_category.code|lower }}{% else %}giep{% endif %}{% endif %}";
|
||||
var regional_roads_url = '{% url "common:get_regional_roads_geojson" %}';
|
||||
|
|
@ -54,6 +55,50 @@
|
|||
{% endif %}
|
||||
</script>
|
||||
|
||||
<!-- Dynamic subtype filtering for intervention add form -->
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const typeSelect = document.getElementById('id_type');
|
||||
const subTypeSelect = document.getElementById('id_sub_type');
|
||||
const subTypeWrapper = document.getElementById('sub_type_field_wrapper');
|
||||
|
||||
if (!typeSelect || !subTypeSelect || !subTypeWrapper) return;
|
||||
|
||||
function updateSubTypeVisibility() {
|
||||
const selectedType = typeSelect.value;
|
||||
const available = (typeof subtypesData !== 'undefined' ? subtypesData : []).filter(st =>
|
||||
!st.types || st.types.length === 0 || st.types.includes(selectedType)
|
||||
);
|
||||
|
||||
if (available.length === 0) {
|
||||
subTypeWrapper.classList.add('d-none');
|
||||
subTypeSelect.value = '';
|
||||
} else {
|
||||
subTypeWrapper.classList.remove('d-none');
|
||||
const currentVal = subTypeSelect.value;
|
||||
subTypeSelect.innerHTML = '<option value="">---------</option>';
|
||||
let valueStillValid = false;
|
||||
available.forEach(st => {
|
||||
const opt = document.createElement('option');
|
||||
opt.value = String(st.id);
|
||||
opt.textContent = st.name;
|
||||
if (String(st.id) === currentVal) {
|
||||
opt.selected = true;
|
||||
valueStillValid = true;
|
||||
}
|
||||
subTypeSelect.appendChild(opt);
|
||||
});
|
||||
if (!valueStillValid && currentVal) {
|
||||
subTypeSelect.value = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
typeSelect.addEventListener('change', updateSubTypeVisibility);
|
||||
updateSubTypeVisibility();
|
||||
});
|
||||
</script>
|
||||
|
||||
<!-- intervention_add.js (split) -->
|
||||
<script src="{% static 'interventions/add/map.js' %}"></script>
|
||||
<script src="{% static 'interventions/add/source.js' %}"></script>
|
||||
|
|
|
|||
|
|
@ -498,6 +498,31 @@
|
|||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sous-type d'intervention -->
|
||||
<div class="d-flex align-items-center editable-field {% if not has_subtypes_for_current_type %}d-none{% endif %}"
|
||||
id="intervention-subtype-field"
|
||||
data-field="sub_type"
|
||||
data-subtypes="{{ subtypes_data_json|escape }}"
|
||||
data-can-edit="{% if 'sub_type' in allowed_update_fields %}true{% else %}false{% endif %}">
|
||||
<button class="btn btn-sm border-0" style="cursor: default;"><i class="bi bi-tag"></i></button>
|
||||
<span class="label mx-1">{% translate "Sous-type" %}:</span>
|
||||
<div class="field-display">
|
||||
<span class="field-value">{{ intervention.sub_type.get_name|default:"-" }}</span>
|
||||
</div>
|
||||
<div class="field-edit d-none" style="flex: 1;">
|
||||
<select class="form-select form-select-sm" id="intervention-subtype-select">
|
||||
<option value="">-</option>
|
||||
{% for st in sub_types %}
|
||||
{% if not st.intervention_types or intervention.type in st.intervention_types %}
|
||||
<option value="{{ st.id }}" {% if st.id == intervention.sub_type_id %}selected{% endif %}>
|
||||
{{ st.get_name }}
|
||||
</option>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Catégorie -->
|
||||
<div class="d-flex align-items-center editable-field" data-field="category" data-can-edit="{% if 'category' in allowed_update_fields %}true{% else %}false{% endif %}">
|
||||
<button class="btn btn-sm border-0" style="cursor: default;"><i class="bi bi-tags"></i></button>
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
const recommendedNextStatus = '{{ recommended_next_status }}';
|
||||
const interventionId = '{{ intervention.id }}';
|
||||
const interventionThematicId = {% if intervention.thematic %}{{ intervention.thematic.id }}{% else %}null{% endif %};
|
||||
const interventionSubtypesData = {{ subtypes_data_json|default:"[]"|safe }};
|
||||
const allowed_transitions = {{ allowed_transitions|safe }};
|
||||
const canViewPrices = {{ can_view_prices|yesno:"true,false" }};
|
||||
var contractId = {% if contract_id %} {{ contract_id }} {% else %} null {% endif %};
|
||||
|
|
|
|||
|
|
@ -645,6 +645,165 @@ class InterventionSubTypeTests(TestCase):
|
|||
display_univ = admin_instance.types_display(self.subtype_universal)
|
||||
self.assertEqual(str(display_univ), "Tous")
|
||||
|
||||
def test_update_intervention_field_subtype(self):
|
||||
from interventions.models import Intervention
|
||||
from interventions.views.helpers import _update_intervention_field
|
||||
|
||||
intervention = Intervention.objects.create(
|
||||
title='Test SubType Update',
|
||||
type='repair',
|
||||
thematic=self.thematic,
|
||||
status='in_preparation',
|
||||
)
|
||||
|
||||
# 1. Setting compatible subtype succeeds
|
||||
success, err = _update_intervention_field(intervention, 'sub_type', str(self.subtype_multi.id))
|
||||
self.assertTrue(success)
|
||||
self.assertIsNone(err)
|
||||
self.assertEqual(intervention.sub_type, self.subtype_multi)
|
||||
|
||||
# 2. Setting universal subtype succeeds
|
||||
success, err = _update_intervention_field(intervention, 'sub_type', str(self.subtype_universal.id))
|
||||
self.assertTrue(success)
|
||||
self.assertEqual(intervention.sub_type, self.subtype_universal)
|
||||
|
||||
# 3. Setting incompatible subtype fails
|
||||
success, err = _update_intervention_field(intervention, 'sub_type', str(self.subtype_build_only.id))
|
||||
self.assertFalse(success)
|
||||
self.assertIn("non compatible", err)
|
||||
# Old value unchanged
|
||||
self.assertEqual(intervention.sub_type, self.subtype_universal)
|
||||
|
||||
# 4. Setting empty string clears subtype
|
||||
success, err = _update_intervention_field(intervention, 'sub_type', "")
|
||||
self.assertTrue(success)
|
||||
self.assertIsNone(intervention.sub_type)
|
||||
|
||||
def test_type_change_resets_incompatible_subtype(self):
|
||||
from interventions.models import Intervention
|
||||
from interventions.views.helpers import _update_intervention_field
|
||||
|
||||
intervention = Intervention.objects.create(
|
||||
title='Test SubType Incompatible Reset',
|
||||
type='repair',
|
||||
sub_type=self.subtype_multi,
|
||||
thematic=self.thematic,
|
||||
status='in_preparation',
|
||||
)
|
||||
self.assertEqual(intervention.sub_type, self.subtype_multi)
|
||||
|
||||
# Change type to 'build' (incompatible with subtype_multi ['repair', 'fixing'])
|
||||
success, err = _update_intervention_field(intervention, 'type', 'build')
|
||||
self.assertTrue(success)
|
||||
self.assertEqual(intervention.type, 'build')
|
||||
self.assertIsNone(intervention.sub_type)
|
||||
|
||||
def test_permissions_includes_sub_type(self):
|
||||
from interventions.models import Intervention
|
||||
from interventions.permissions import get_allowed_update_fields
|
||||
from common.models import UserConfig, Role
|
||||
|
||||
admin_role, _ = Role.objects.get_or_create(name='admin')
|
||||
user_config, _ = UserConfig.objects.get_or_create(user=self.user)
|
||||
user_config.roles.add(admin_role)
|
||||
user_config.is_intern = True
|
||||
user_config.save()
|
||||
|
||||
intervention = Intervention.objects.create(
|
||||
title='Test Perms',
|
||||
type='repair',
|
||||
thematic=self.thematic,
|
||||
status='in_preparation',
|
||||
)
|
||||
allowed_fields = get_allowed_update_fields(self.user, intervention)
|
||||
self.assertIn('type', allowed_fields)
|
||||
self.assertIn('sub_type', allowed_fields)
|
||||
|
||||
def test_detail_view_subtypes_context(self):
|
||||
from interventions.models import Intervention
|
||||
from common.models import UserConfig, Role
|
||||
import json
|
||||
|
||||
admin_role, _ = Role.objects.get_or_create(name='admin')
|
||||
user_config, _ = UserConfig.objects.get_or_create(user=self.user)
|
||||
user_config.roles.add(admin_role)
|
||||
user_config.is_intern = True
|
||||
user_config.save()
|
||||
|
||||
# 1. Intervention with type 'repair' (has matching subtypes: sub_multi, sub_univ)
|
||||
interv_repair = Intervention.objects.create(
|
||||
title='Detail View Subtype Test',
|
||||
type='repair',
|
||||
thematic=self.thematic,
|
||||
status='in_preparation',
|
||||
created_by=self.user,
|
||||
)
|
||||
|
||||
self.client.force_login(self.user)
|
||||
response = self.client.get(reverse('interventions:intervention_detail', args=[interv_repair.id]))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTrue(response.context['has_subtypes_for_current_type'])
|
||||
subtypes_in_json = json.loads(response.context['subtypes_data_json'])
|
||||
self.assertTrue(any(s['id'] == self.subtype_multi.id for s in subtypes_in_json))
|
||||
|
||||
# 2. Deactivate universal subtype to test type without subtypes
|
||||
self.subtype_universal.is_active = False
|
||||
self.subtype_universal.save()
|
||||
|
||||
interv_cleaning = Intervention.objects.create(
|
||||
title='Detail View No Subtype Test',
|
||||
type='cleaning',
|
||||
thematic=self.thematic,
|
||||
status='in_preparation',
|
||||
created_by=self.user,
|
||||
)
|
||||
response_cleaning = self.client.get(reverse('interventions:intervention_detail', args=[interv_cleaning.id]))
|
||||
self.assertEqual(response_cleaning.status_code, 200)
|
||||
self.assertFalse(response_cleaning.context['has_subtypes_for_current_type'])
|
||||
|
||||
def test_update_intervention_endpoint_subtype(self):
|
||||
from interventions.models import Intervention
|
||||
from common.models import UserConfig, Role
|
||||
import json
|
||||
|
||||
admin_role, _ = Role.objects.get_or_create(name='admin')
|
||||
user_config, _ = UserConfig.objects.get_or_create(user=self.user)
|
||||
user_config.roles.add(admin_role)
|
||||
user_config.is_intern = True
|
||||
user_config.save()
|
||||
|
||||
intervention = Intervention.objects.create(
|
||||
title='API Update Subtype Test',
|
||||
type='repair',
|
||||
thematic=self.thematic,
|
||||
status='in_preparation',
|
||||
created_by=self.user,
|
||||
)
|
||||
|
||||
self.client.force_login(self.user)
|
||||
url = reverse('interventions:update_intervention', args=[intervention.id])
|
||||
|
||||
# Batch update sub_type
|
||||
response = self.client.post(
|
||||
url,
|
||||
data=json.dumps({'sub_type': str(self.subtype_multi.id)}),
|
||||
content_type='application/json',
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
intervention.refresh_from_db()
|
||||
self.assertEqual(intervention.sub_type, self.subtype_multi)
|
||||
|
||||
# Batch update type and sub_type together: switch type to 'build' and sub_type to sub_build
|
||||
response = self.client.post(
|
||||
url,
|
||||
data=json.dumps({'type': 'build', 'sub_type': str(self.subtype_build_only.id)}),
|
||||
content_type='application/json',
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
intervention.refresh_from_db()
|
||||
self.assertEqual(intervention.type, 'build')
|
||||
self.assertEqual(intervention.sub_type, self.subtype_build_only)
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ from common.private_files.helpers import build_private_url
|
|||
from common.utils import get_short_name
|
||||
|
||||
from interventions.models import (InterventionSubscription, Intervention, InterventionAsset, InterventionTimeLine, InterventionContractPost, InterventionDocument, InterventionNote, InterventionLocation,
|
||||
InterventionLink,
|
||||
InterventionLink, InterventionSubType,
|
||||
ContractPauseReasonAssignment,
|
||||
Symptom, SourceCategory, InterventionPlanificationTimeLine, InterventionRealizationTimeLine,
|
||||
InterventionOccupation,
|
||||
|
|
@ -632,6 +632,20 @@ def intervention_detail(request, intervention_id):
|
|||
|
||||
sequenced_data = get_sequenced_intervention_documents(intervention, notes_qs=notes)
|
||||
|
||||
sub_types_qs = InterventionSubType.objects.filter(is_active=True, thematic=intervention.thematic).order_by('order_number', 'name_fr')
|
||||
subtypes_data = [
|
||||
{
|
||||
'id': st.id,
|
||||
'name': st.get_name(),
|
||||
'types': st.intervention_types,
|
||||
}
|
||||
for st in sub_types_qs
|
||||
]
|
||||
has_subtypes_for_current_type = any(
|
||||
not st.intervention_types or intervention.type in st.intervention_types
|
||||
for st in sub_types_qs
|
||||
)
|
||||
|
||||
context = {
|
||||
"intervention": intervention,
|
||||
"status_choices" : [{"key": key, "label": label} for key, label in STATUS_CHOICES.items()],
|
||||
|
|
@ -663,6 +677,9 @@ def intervention_detail(request, intervention_id):
|
|||
'contracts': contracts,
|
||||
'maintain_choices': MAINTAIN_CHOICES,
|
||||
'type_choices': get_type_choices(intervention.thematic),
|
||||
'sub_types': sub_types_qs,
|
||||
'subtypes_data_json': _json_mod.dumps(subtypes_data),
|
||||
'has_subtypes_for_current_type': has_subtypes_for_current_type,
|
||||
'origin_type_choices': ORIGIN_TYPE_CHOICES,
|
||||
'priority_choices': Intervention._meta.get_field('priority').choices,
|
||||
'symptoms': Symptom.objects.filter(is_active=True, thematic=intervention.thematic).order_by('name_fr'),
|
||||
|
|
@ -1694,6 +1711,16 @@ def interventions_add(request, thematic_code):
|
|||
for sc in SourceCategory.objects.filter(is_active=True)
|
||||
]
|
||||
|
||||
subtypes_qs = InterventionSubType.objects.filter(thematic=thematic, is_active=True).order_by('order_number', 'name_fr')
|
||||
subtypes_data = [
|
||||
{
|
||||
"id": st.id,
|
||||
"name": st.get_name(),
|
||||
"types": st.intervention_types,
|
||||
}
|
||||
for st in subtypes_qs
|
||||
]
|
||||
|
||||
if request.method == 'POST':
|
||||
maintain_type = request.POST.get('maintain_type', 'corrective')
|
||||
form = InterventionForm(request.POST, request.FILES, thematic=thematic, user=request.user)
|
||||
|
|
@ -2017,7 +2044,7 @@ def interventions_add(request, thematic_code):
|
|||
for key in [
|
||||
'title', 'lat', 'lon', 'address', 'asset_category', 'status',
|
||||
'source_category', 'source_ref', 'source_detail', 'init_description',
|
||||
'type', 'origin_type', 'priority'
|
||||
'type', 'sub_type', 'origin_type', 'priority'
|
||||
]:
|
||||
val = request.GET.get(key)
|
||||
if val not in (None, ""):
|
||||
|
|
@ -2093,6 +2120,7 @@ def interventions_add(request, thematic_code):
|
|||
'thematic': thematic,
|
||||
'symptoms_data': json.dumps(symptoms_data),
|
||||
'source_categories_data': json.dumps(source_categories_data),
|
||||
'subtypes_data': json.dumps(subtypes_data),
|
||||
'maintain_type': maintain_type,
|
||||
'observation': observation,
|
||||
'observation_photos': observation_photos,
|
||||
|
|
@ -2654,14 +2682,18 @@ def update_intervention(request, intervention_id):
|
|||
# Handle notes separately (special case)
|
||||
notes_data = data.pop('notes', None)
|
||||
|
||||
# Sort fields to process assigned_provider before contract
|
||||
# Sort fields to process assigned_provider before contract, and type before sub_type
|
||||
def _field_priority(item):
|
||||
f_name = item[0]
|
||||
if f_name == 'assigned_provider':
|
||||
return 0
|
||||
if f_name == 'contract':
|
||||
return 1
|
||||
return 2
|
||||
if f_name == 'type':
|
||||
return 2
|
||||
if f_name == 'sub_type':
|
||||
return 3
|
||||
return 4
|
||||
|
||||
for field, new_value in sorted(data.items(), key=_field_priority):
|
||||
if field not in allowed_update_fields:
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ from common.private_files.helpers import build_private_url
|
|||
from common.utils import get_short_name
|
||||
|
||||
from interventions.models import (InterventionSubscription, Intervention, InterventionAsset, InterventionTimeLine, InterventionContractPost, InterventionDocument, InterventionNote, InterventionLocation,
|
||||
InterventionLink,
|
||||
InterventionLink, InterventionSubType,
|
||||
ContractPauseReasonAssignment,
|
||||
Symptom, SourceCategory, InterventionPlanificationTimeLine, InterventionRealizationTimeLine,
|
||||
InterventionOccupation,
|
||||
|
|
@ -2604,9 +2604,32 @@ def _update_intervention_field(intervention, field, new_value, user_config=None)
|
|||
allowed_types = dict(get_type_choices(intervention.thematic)).keys()
|
||||
if new_value in allowed_types:
|
||||
intervention.type = new_value
|
||||
if intervention.sub_type and intervention.sub_type.intervention_types:
|
||||
if new_value not in intervention.sub_type.intervention_types:
|
||||
intervention.sub_type = None
|
||||
return True, None
|
||||
return False, _('Type not found')
|
||||
|
||||
elif field == "sub_type":
|
||||
if new_value in ("", None, "null"):
|
||||
intervention.sub_type = None
|
||||
return True, None
|
||||
try:
|
||||
sub_type_id = int(new_value)
|
||||
sub_type = InterventionSubType.objects.filter(
|
||||
id=sub_type_id,
|
||||
is_active=True,
|
||||
thematic=intervention.thematic,
|
||||
).first()
|
||||
if not sub_type:
|
||||
return False, _('Sous-type introuvable')
|
||||
if sub_type.intervention_types and intervention.type not in sub_type.intervention_types:
|
||||
return False, _("Sous-type non compatible avec le type d'intervention")
|
||||
intervention.sub_type = sub_type
|
||||
return True, None
|
||||
except (ValueError, TypeError):
|
||||
return False, _('Sous-type invalide')
|
||||
|
||||
elif field == "origin_type":
|
||||
if new_value in dict(ORIGIN_TYPE_CHOICES).keys():
|
||||
intervention.origin_type = new_value
|
||||
|
|
|
|||
Loading…
Reference in a new issue