feat(nature): create timeline planning events and display due date on asset interventions

- Create InterventionPlanificationTimeLine events (expected_begin and expected_end)
  during Fulcrum tree interventions import and maintenance dates synchronization
- Display planned/completion date instead of creation_time in asset_interventions.html
- Add unit test assertions for planning timeline events
This commit is contained in:
kdeterme 2026-10-10 14:09:15 +02:00
parent 74ef7d3a34
commit af411b2105
3 changed files with 80 additions and 1 deletions

View file

@ -972,8 +972,73 @@ class Command(BaseCommand):
InterventionAsset.objects.bulk_create(chunk, batch_size=BATCH_SIZE)
self.log(f" Insertion liens : {min(i + BATCH_SIZE, len(assets_to_create))} / {len(assets_to_create)}")
# Synchronisation des événements de planification timeline
self.sync_intervention_planning_events()
self.log(f"Importation des interventions terminée. Total en base : {Intervention.objects.count()}")
def sync_intervention_planning_events(self):
"""
Crée ou met à jour les événements de la timeline de planification (InterventionPlanificationTimeLine) :
- 'expected_end' : échéance prévisionnelle calculée depuis in_deadline
- 'expected_begin' : date de début / diagnostic initial depuis dia_date
"""
self.log("--- Synchronisation des événements de planification (timeline) ---")
from django.db import connection
with connection.cursor() as cur:
# 1. Échéances prévisionnelles (expected_end)
cur.execute("""
INSERT INTO interventions_interventionplanificationtimeline (
intervention_id, event_time, event_type, event_description
)
SELECT i.id, i.expected_end_time, 'expected_end', 'Échéance prévisionnelle'
FROM interventions_intervention i
WHERE i.expected_end_time IS NOT NULL
AND NOT EXISTS (
SELECT 1 FROM interventions_interventionplanificationtimeline p
WHERE p.intervention_id = i.id AND p.event_type = 'expected_end'
);
""")
created_end = cur.rowcount
cur.execute("""
UPDATE interventions_interventionplanificationtimeline p
SET event_time = i.expected_end_time
FROM interventions_intervention i
WHERE p.intervention_id = i.id
AND p.event_type = 'expected_end'
AND p.event_time != i.expected_end_time;
""")
updated_end = cur.rowcount
self.log(f" Échéance prévisionnelle (expected_end) : {created_end} créés, {updated_end} mis à jour")
# 2. Diagnostics initiaux (expected_begin)
cur.execute("""
INSERT INTO interventions_interventionplanificationtimeline (
intervention_id, event_time, event_type, event_description
)
SELECT i.id, i.expected_begin_time, 'expected_begin', 'Diagnostic initial'
FROM interventions_intervention i
WHERE i.expected_begin_time IS NOT NULL
AND NOT EXISTS (
SELECT 1 FROM interventions_interventionplanificationtimeline p
WHERE p.intervention_id = i.id AND p.event_type = 'expected_begin'
);
""")
created_begin = cur.rowcount
cur.execute("""
UPDATE interventions_interventionplanificationtimeline p
SET event_time = i.expected_begin_time
FROM interventions_intervention i
WHERE p.intervention_id = i.id
AND p.event_type = 'expected_begin'
AND p.event_time != i.expected_begin_time;
""")
updated_begin = cur.rowcount
self.log(f" Diagnostic initial (expected_begin) : {created_begin} créés, {updated_begin} mis à jour")
def sync_tree_maintenance_dates(self, gestion_file=None):
"""
Synchronise les dates et périodicités de maintenance sur les arbres :
@ -981,10 +1046,13 @@ class Command(BaseCommand):
- pruning_frequency_years : périodicité extraite du délai/titre (ex: Dans 1 an -> 1)
- last_phytosanitary_date : max(expected_begin_time) des interventions de diagnostic
- last_pruning_date : date d'exécution de la dernière taille (depuis gestion.csv ou interventions passées)
- timeline de planification : synchronisation des événements expected_begin / expected_end
"""
self.log("--- Synchronisation des dates de maintenance sur les arbres ---")
from django.db import connection
self.sync_intervention_planning_events()
content_type_tree = ContentType.objects.get_for_model(NatureTree)
with connection.cursor() as cur:

View file

@ -85,7 +85,9 @@
{% endif %}
</td>
<td class="pe-3">
<small class="text-muted">{{ intervention.creation_time|date:"d/m/Y" }}</small>
<small class="text-muted" title="{% if intervention.end_time %}{% translate 'Date de réalisation' %}{% elif intervention.expected_end_time %}{% translate 'Échéance prévisionnelle' %}{% elif intervention.expected_begin_time %}{% translate 'Date de début prévisionnelle' %}{% else %}{% translate 'Date de création' %}{% endif %}">
{{ intervention.end_time|default:intervention.expected_end_time|default:intervention.expected_begin_time|default:intervention.creation_time|date:"d/m/Y" }}
</small>
</td>
</tr>
{% endfor %}

View file

@ -158,6 +158,15 @@ class ImportFulcrumTreesCommandTest(TestCase):
self.assertEqual(tree.next_inspection_date, date(2025, 5, 10))
self.assertEqual(tree.pruning_frequency_years, 1)
# Vérifications événements de planification (timeline)
self.assertEqual(itv.planned_events.count(), 2)
begin_event = itv.planned_events.filter(event_type='expected_begin').first()
self.assertIsNotNone(begin_event)
self.assertEqual(begin_event.event_time.date(), date(2024, 5, 10))
end_event = itv.planned_events.filter(event_type='expected_end').first()
self.assertIsNotNone(end_event)
self.assertEqual(end_event.event_time.date(), date(2025, 5, 10))
# Vérification liaison InterventionAsset
links = InterventionAsset.objects.filter(intervention=itv)
self.assertEqual(links.count(), 1)