feat: add location management, asset creation modal, and intervention proximity tracking to details view
This commit is contained in:
parent
1bdb771f3b
commit
ca6d09c6b1
6 changed files with 88 additions and 9 deletions
|
|
@ -45,13 +45,15 @@
|
|||
const thematicCode = form.dataset.thematic || form.querySelector('[name="thematic_code"]')?.value || '';
|
||||
const locationId = form.querySelector('[name="location_id"]')?.value || '';
|
||||
const structureId = form.querySelector('[name="structure_id"]')?.value || '';
|
||||
const interventionId = form.dataset.interventionId || form.querySelector('[name="intervention_id"]')?.value || '';
|
||||
|
||||
if (!thematicCode) return;
|
||||
|
||||
const params = new URLSearchParams({
|
||||
thematic_code: thematicCode,
|
||||
location_id: locationId,
|
||||
structure_id: structureId
|
||||
structure_id: structureId,
|
||||
intervention_id: interventionId
|
||||
});
|
||||
|
||||
fetch(`/assets/api/suggested-code/?${params.toString()}`)
|
||||
|
|
|
|||
|
|
@ -477,6 +477,33 @@ class StructuresLocationDetailAddAssetModalTest(TestCase):
|
|||
self.assertEqual(page_res.context["suggested_code"], "WTB30-000025")
|
||||
self.assertContains(page_res, 'value="WTB30-000025"')
|
||||
|
||||
# Test intervention code suggestion (mobile / desktop intervention)
|
||||
from interventions.models import Intervention, InterventionLocation
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
|
||||
itv = Intervention.objects.create(
|
||||
code="ITV_TEST_99",
|
||||
title="Intervention Test Structures",
|
||||
thematic=self.thematic
|
||||
)
|
||||
loc_ct = ContentType.objects.get_for_model(self.location)
|
||||
InterventionLocation.objects.create(
|
||||
intervention=itv,
|
||||
content_type=loc_ct,
|
||||
object_id=self.location.id
|
||||
)
|
||||
|
||||
itv_suggested = get_suggested_asset_code("structures", intervention_id=itv.id)
|
||||
self.assertEqual(itv_suggested, "WTB30-000025")
|
||||
|
||||
itv_api_res = self.client.get(reverse("assets:get_suggested_asset_code_api"), {
|
||||
"thematic_code": "structures",
|
||||
"intervention_id": itv.id
|
||||
})
|
||||
self.assertEqual(itv_api_res.status_code, 200)
|
||||
self.assertEqual(itv_api_res.json().get("suggested_code"), "WTB30-000025")
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -7,16 +7,54 @@ def get_next_structure_asset_code(
|
|||
structure=None,
|
||||
location=None,
|
||||
structure_id: Optional[int] = None,
|
||||
location_id: Optional[int] = None
|
||||
location_id: Optional[int] = None,
|
||||
intervention=None,
|
||||
intervention_id: Optional[int] = None
|
||||
) -> str:
|
||||
"""
|
||||
Computes the next incremental asset code for a structure / tunnel location.
|
||||
Computes the next incremental asset code for a structure / tunnel location / intervention.
|
||||
Looks up existing asset codes (e.g. WTB30-000023, WTB30-000412), determines
|
||||
the dominant prefix and maximum integer number, and returns the next code
|
||||
(e.g. WTB30-000413), ensuring it does not collide with existing codes.
|
||||
"""
|
||||
from assets.models.structures import Structure, StructureLocation, StructureGeoAsset
|
||||
|
||||
if intervention_id and not intervention:
|
||||
try:
|
||||
from interventions.models import Intervention
|
||||
intervention = Intervention.objects.get(pk=intervention_id)
|
||||
except Exception:
|
||||
intervention = None
|
||||
|
||||
if intervention and not location and not structure:
|
||||
# 1. Look for linked structure locations
|
||||
for loc_link in intervention.locations.all():
|
||||
loc_obj = loc_link.location
|
||||
if isinstance(loc_obj, StructureLocation) or hasattr(loc_obj, 'structure'):
|
||||
location = loc_obj
|
||||
structure = getattr(loc_obj, 'structure', None)
|
||||
break
|
||||
|
||||
# 2. Look for linked structure assets
|
||||
if not structure and not location:
|
||||
for a_link in intervention.interventionasset_set.all():
|
||||
asset_obj = a_link.asset
|
||||
if asset_obj and hasattr(asset_obj, 'structure') and asset_obj.structure:
|
||||
structure = asset_obj.structure
|
||||
location = getattr(asset_obj, 'location', None)
|
||||
break
|
||||
|
||||
# 3. Fallback: match by location_code / address if structure exists
|
||||
if not structure and not location and intervention.location_code:
|
||||
loc_match = StructureLocation.objects.filter(code__iexact=intervention.location_code).first()
|
||||
if loc_match:
|
||||
location = loc_match
|
||||
structure = loc_match.structure
|
||||
else:
|
||||
struct_match = Structure.objects.filter(code__iexact=intervention.location_code).first()
|
||||
if struct_match:
|
||||
structure = struct_match
|
||||
|
||||
if location_id and not location:
|
||||
try:
|
||||
location = StructureLocation.objects.select_related('structure').get(pk=location_id)
|
||||
|
|
@ -79,17 +117,21 @@ def get_suggested_asset_code(
|
|||
thematic_code: str,
|
||||
location_id: Optional[int] = None,
|
||||
structure_id: Optional[int] = None,
|
||||
intervention_id: Optional[int] = None,
|
||||
location=None,
|
||||
structure=None
|
||||
structure=None,
|
||||
intervention=None
|
||||
) -> str:
|
||||
"""
|
||||
Returns a suggested asset code based on thematic and context.
|
||||
Returns a suggested asset code based on thematic and context (location, structure, or intervention).
|
||||
"""
|
||||
if thematic_code == 'structures':
|
||||
return get_next_structure_asset_code(
|
||||
structure=structure,
|
||||
location=location,
|
||||
structure_id=structure_id,
|
||||
location_id=location_id
|
||||
location_id=location_id,
|
||||
intervention=intervention,
|
||||
intervention_id=intervention_id
|
||||
)
|
||||
return ""
|
||||
|
|
|
|||
|
|
@ -1286,12 +1286,14 @@ def get_suggested_asset_code_api(request):
|
|||
- thematic_code (str)
|
||||
- location_id (int, optional)
|
||||
- structure_id (int, optional)
|
||||
- intervention_id (int, optional)
|
||||
"""
|
||||
from assets.utils.asset_code_generator import get_suggested_asset_code
|
||||
|
||||
thematic_code = request.GET.get('thematic_code') or ''
|
||||
location_id = request.GET.get('location_id')
|
||||
structure_id = request.GET.get('structure_id')
|
||||
intervention_id = request.GET.get('intervention_id')
|
||||
|
||||
try:
|
||||
loc_id = int(location_id) if location_id else None
|
||||
|
|
@ -1303,10 +1305,16 @@ def get_suggested_asset_code_api(request):
|
|||
except (ValueError, TypeError):
|
||||
struct_id = None
|
||||
|
||||
try:
|
||||
itv_id = int(intervention_id) if intervention_id else None
|
||||
except (ValueError, TypeError):
|
||||
itv_id = None
|
||||
|
||||
suggested_code = get_suggested_asset_code(
|
||||
thematic_code=thematic_code,
|
||||
location_id=loc_id,
|
||||
structure_id=struct_id
|
||||
structure_id=struct_id,
|
||||
intervention_id=itv_id
|
||||
)
|
||||
|
||||
return JsonResponse({
|
||||
|
|
|
|||
|
|
@ -169,7 +169,7 @@
|
|||
<label for="addAssetDesktopModalCode" class="form-label">
|
||||
<i class="bi bi-hash me-1"></i>{% translate "Code" %} <span class="text-danger">*</span>
|
||||
</label>
|
||||
<input type="text" class="form-control" id="addAssetDesktopModalCode" name="code" required
|
||||
<input type="text" class="form-control asset-code-input" id="addAssetDesktopModalCode" name="code" required
|
||||
placeholder="{% translate 'Ex: STR-001' %}" maxlength="50">
|
||||
<div class="form-text text-muted">
|
||||
{% translate "Code unique pour identifier l'équipement." %}
|
||||
|
|
|
|||
|
|
@ -1517,7 +1517,7 @@
|
|||
<label for="addAssetMobileModalCode" class="form-label">
|
||||
{% translate "Code" %} <span class="text-danger">*</span>
|
||||
</label>
|
||||
<input type="text" class="form-control" id="addAssetMobileModalCode" name="code" required
|
||||
<input type="text" class="form-control asset-code-input" id="addAssetMobileModalCode" name="code" required
|
||||
placeholder="{% translate 'Ex: STR-001' %}" maxlength="50">
|
||||
</div>
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue