feat: add intervention on pause widget and associated backend logic
This commit is contained in:
parent
441af33ddd
commit
b1e7f726a2
12 changed files with 211 additions and 1 deletions
Binary file not shown.
|
|
@ -69,6 +69,10 @@ msgid "Compteur interventions terminées"
|
||||||
msgstr "Incidents to be processed counter"
|
msgstr "Incidents to be processed counter"
|
||||||
|
|
||||||
#: streetup/home/models.py:20
|
#: streetup/home/models.py:20
|
||||||
|
msgid "Compteur interventions en pause"
|
||||||
|
msgstr "Paused interventions counter"
|
||||||
|
|
||||||
|
#: streetup/home/models.py:21
|
||||||
#: streetup/home/templates/home/widgets/stock_actions.html:9
|
#: streetup/home/templates/home/widgets/stock_actions.html:9
|
||||||
msgid "Actions stock"
|
msgid "Actions stock"
|
||||||
msgstr "Stock actions"
|
msgstr "Stock actions"
|
||||||
|
|
@ -543,6 +547,18 @@ msgstr "No incidents to process"
|
||||||
msgid "Voir toutes les interventions terminées"
|
msgid "Voir toutes les interventions terminées"
|
||||||
msgstr "See all my observations"
|
msgstr "See all my observations"
|
||||||
|
|
||||||
|
#: streetup/home/templates/home/widgets/intervention_on_pause.html:8
|
||||||
|
msgid "Interventions en pause"
|
||||||
|
msgstr "Paused interventions"
|
||||||
|
|
||||||
|
#: streetup/home/templates/home/widgets/intervention_on_pause.html:40
|
||||||
|
msgid "Aucune intervention en pause"
|
||||||
|
msgstr "No paused interventions"
|
||||||
|
|
||||||
|
#: streetup/home/templates/home/widgets/intervention_on_pause.html:48
|
||||||
|
msgid "Voir toutes les interventions en pause"
|
||||||
|
msgstr "View all paused interventions"
|
||||||
|
|
||||||
#: streetup/home/templates/home/widgets/intervention_stats.html:13
|
#: streetup/home/templates/home/widgets/intervention_stats.html:13
|
||||||
msgid "Interventions"
|
msgid "Interventions"
|
||||||
msgstr "Interventions"
|
msgstr "Interventions"
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -69,6 +69,10 @@ msgid "Compteur interventions terminées"
|
||||||
msgstr "Compteur interventions terminées"
|
msgstr "Compteur interventions terminées"
|
||||||
|
|
||||||
#: streetup/home/models.py:20
|
#: streetup/home/models.py:20
|
||||||
|
msgid "Compteur interventions en pause"
|
||||||
|
msgstr "Compteur interventions en pause"
|
||||||
|
|
||||||
|
#: streetup/home/models.py:21
|
||||||
#: streetup/home/templates/home/widgets/stock_actions.html:9
|
#: streetup/home/templates/home/widgets/stock_actions.html:9
|
||||||
msgid "Actions stock"
|
msgid "Actions stock"
|
||||||
msgstr "Actions stock"
|
msgstr "Actions stock"
|
||||||
|
|
@ -550,6 +554,18 @@ msgstr "Aucune intervention terminée"
|
||||||
msgid "Voir toutes les interventions terminées"
|
msgid "Voir toutes les interventions terminées"
|
||||||
msgstr "Voir toutes les interventions terminées"
|
msgstr "Voir toutes les interventions terminées"
|
||||||
|
|
||||||
|
#: streetup/home/templates/home/widgets/intervention_on_pause.html:8
|
||||||
|
msgid "Interventions en pause"
|
||||||
|
msgstr "Interventions en pause"
|
||||||
|
|
||||||
|
#: streetup/home/templates/home/widgets/intervention_on_pause.html:40
|
||||||
|
msgid "Aucune intervention en pause"
|
||||||
|
msgstr "Aucune intervention en pause"
|
||||||
|
|
||||||
|
#: streetup/home/templates/home/widgets/intervention_on_pause.html:48
|
||||||
|
msgid "Voir toutes les interventions en pause"
|
||||||
|
msgstr "Voir toutes les interventions en pause"
|
||||||
|
|
||||||
#: streetup/home/templates/home/widgets/intervention_stats.html:13
|
#: streetup/home/templates/home/widgets/intervention_stats.html:13
|
||||||
msgid "Interventions"
|
msgid "Interventions"
|
||||||
msgstr "Interventions"
|
msgstr "Interventions"
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -68,7 +68,11 @@ msgstr "Tegengeverifieerde interventies moeten worden goedgekeurd"
|
||||||
msgid "Compteur interventions terminées"
|
msgid "Compteur interventions terminées"
|
||||||
msgstr "Aantal te behandelen incidenten"
|
msgstr "Aantal te behandelen incidenten"
|
||||||
|
|
||||||
#: home/models.py:20 home/templates/home/widgets/stock_actions.html:9
|
#: home/models.py:20
|
||||||
|
msgid "Compteur interventions en pause"
|
||||||
|
msgstr "Teller gepauzeerde interventies"
|
||||||
|
|
||||||
|
#: home/models.py:21 home/templates/home/widgets/stock_actions.html:9
|
||||||
msgid "Actions stock"
|
msgid "Actions stock"
|
||||||
msgstr "Voorraadacties"
|
msgstr "Voorraadacties"
|
||||||
|
|
||||||
|
|
@ -544,6 +548,18 @@ msgstr "Geen incidenten te behandelen"
|
||||||
msgid "Voir toutes les interventions terminées"
|
msgid "Voir toutes les interventions terminées"
|
||||||
msgstr "Bekijk al mijn observaties"
|
msgstr "Bekijk al mijn observaties"
|
||||||
|
|
||||||
|
#: home/templates/home/widgets/intervention_on_pause.html:8
|
||||||
|
msgid "Interventions en pause"
|
||||||
|
msgstr "Gepauzeerde interventies"
|
||||||
|
|
||||||
|
#: home/templates/home/widgets/intervention_on_pause.html:40
|
||||||
|
msgid "Aucune intervention en pause"
|
||||||
|
msgstr "Geen gepauzeerde interventies"
|
||||||
|
|
||||||
|
#: home/templates/home/widgets/intervention_on_pause.html:48
|
||||||
|
msgid "Voir toutes les interventions en pause"
|
||||||
|
msgstr "Bekijk alle gepauzeerde interventies"
|
||||||
|
|
||||||
#: home/templates/home/widgets/intervention_stats.html:13
|
#: home/templates/home/widgets/intervention_stats.html:13
|
||||||
msgid "Interventions"
|
msgid "Interventions"
|
||||||
msgstr "Interventies"
|
msgstr "Interventies"
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 6.0.7 on 2026-08-14 20:24
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('home', '0002_delete_teambuildingvote'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='homewidget',
|
||||||
|
name='widget_code',
|
||||||
|
field=models.CharField(choices=[('intervention_stats', 'Statistiques interventions'), ('contracts_summary', 'Résumé contrats'), ('map_today', 'Carte du jour'), ('operator_shortcuts', 'Raccourcis opérateur'), ('assets_links', 'Liens assets'), ('stock_summary', 'Résumé stock'), ('observer_shortcuts', 'Raccourcis observateur'), ('incident_counter', 'Compteur incidents à traiter'), ('intervention_to_verify', 'Compteur interventions à vérifier'), ('intervention_to_plan', 'Compteur interventions à planifier'), ('intervention_to_approve', 'Compteur interventions à approuver'), ('intervention_checked_to_approve', 'Compteur interventions vérifiées à approuver'), ('intervention_finished', 'Compteur interventions terminées'), ('intervention_on_pause', 'Compteur interventions en pause'), ('stock_actions', 'Actions stock')], max_length=50, unique=True, verbose_name='Code du widget'),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -17,6 +17,7 @@ WIDGET_CODE_CHOICES = [
|
||||||
('intervention_to_approve', _('Compteur interventions à approuver')),
|
('intervention_to_approve', _('Compteur interventions à approuver')),
|
||||||
('intervention_checked_to_approve', _('Compteur interventions vérifiées à approuver')),
|
('intervention_checked_to_approve', _('Compteur interventions vérifiées à approuver')),
|
||||||
('intervention_finished', _('Compteur interventions terminées')),
|
('intervention_finished', _('Compteur interventions terminées')),
|
||||||
|
('intervention_on_pause', _('Compteur interventions en pause')),
|
||||||
('stock_actions', _('Actions stock')),
|
('stock_actions', _('Actions stock')),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{# ========== Widget compteur d'interventions en pause ========== #}
|
||||||
|
<div class="card shadow-sm h-100" style="border-color: #90a4ae !important;">
|
||||||
|
<div class="card-header d-flex align-items-center justify-content-between" style="background-color: #90a4ae !important;">
|
||||||
|
<a href="{% url 'interventions:interventions_index' %}?{% if thematic_filter_param %}{{ thematic_filter_param }}&{% endif %}status=on_pause"
|
||||||
|
class="text-decoration-none fw-bold text-uppercase" style="color: #000000 !important; letter-spacing:.05em;">
|
||||||
|
<i class="bi bi-pause-circle-fill me-2"></i>{% translate "Interventions en pause" %}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{# --- Compteur --- #}
|
||||||
|
<div class="text-center py-2 border-bottom">
|
||||||
|
<a href="{% url 'interventions:interventions_index' %}?{% if thematic_filter_param %}{{ thematic_filter_param }}&{% endif %}status=on_pause{% if is_manager_filter_applied %}&intervention_manager={{ request.user.id }}{% endif %}"
|
||||||
|
class="text-decoration-none">
|
||||||
|
<span class="display-5 fw-bold" style="color: #90a4ae !important;">{{ intervention_on_pause_count }}</span>
|
||||||
|
{% if is_manager_filter_applied %}
|
||||||
|
<span class="text-muted fs-6 fw-normal ms-1">{% translate "(gestionnaire)" %}</span>
|
||||||
|
{% endif %}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{# --- Liste des 5 premières interventions --- #}
|
||||||
|
<ul class="list-group list-group-flush">
|
||||||
|
{% for itv in intervention_on_pause_list %}
|
||||||
|
<li class="list-group-item px-2 py-1 small">
|
||||||
|
<a href="{% url 'interventions:intervention_detail' itv.id %}" class="text-decoration-none d-block">
|
||||||
|
<div class="d-flex justify-content-between align-items-start">
|
||||||
|
<span class="fw-semibold text-truncate me-2" style="max-width:70%; color: #90a4ae !important;">{{ itv.code }}</span>
|
||||||
|
<span class="text-muted text-nowrap" style="font-size:.75rem;">{{ itv.creation_time|date:"d/m H:i" }}</span>
|
||||||
|
</div>
|
||||||
|
<span class="text-muted" style="font-size:.75rem;">
|
||||||
|
{% if itv.title %}{{ itv.title|truncatechars:50 }}{% endif %}
|
||||||
|
{% if itv.thematic %} · {{ itv.thematic.code }}{% endif %}
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% empty %}
|
||||||
|
<li class="list-group-item text-center text-muted py-2 small">
|
||||||
|
<i class="bi bi-check-circle text-success me-1"></i>{% translate "Aucune intervention en pause" %}
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class="card-footer text-center text-muted small">
|
||||||
|
<a href="{% url 'interventions:interventions_index' %}?{% if thematic_filter_param %}{{ thematic_filter_param }}&{% endif %}status=on_pause"
|
||||||
|
class="text-muted text-decoration-none">
|
||||||
|
<i class="bi bi-arrow-right-circle me-1"></i>{% translate "Voir toutes les interventions en pause" %} ({{ total_on_pause_count }})
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
@ -231,4 +231,24 @@ class HomeIndexViewTests(TestCase):
|
||||||
self.assertContains(response, 'id="home-unread-notifications-alert"')
|
self.assertContains(response, 'id="home-unread-notifications-alert"')
|
||||||
self.assertContains(response, 'Vous avez <strong class="home-unread-notifications-count">1</strong> notification non lue.')
|
self.assertContains(response, 'Vous avez <strong class="home-unread-notifications-count">1</strong> notification non lue.')
|
||||||
|
|
||||||
|
def test_home_index_intervention_on_pause_widget(self):
|
||||||
|
from django.urls import reverse
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
from common.models import UserConfig
|
||||||
|
from home.models import HomeWidget
|
||||||
|
|
||||||
|
User = get_user_model()
|
||||||
|
user = User.objects.create_user('pause_widget_user', password='pass')
|
||||||
|
UserConfig.objects.create(user=user)
|
||||||
|
self.client.force_login(user)
|
||||||
|
|
||||||
|
# Create the widget
|
||||||
|
HomeWidget.objects.create(widget_code='intervention_on_pause')
|
||||||
|
|
||||||
|
response = self.client.get(reverse('home:index'))
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertContains(response, 'Interventions en pause')
|
||||||
|
self.assertContains(response, 'status=on_pause')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -631,6 +631,17 @@ def index(request):
|
||||||
.order_by('-code')[:5]
|
.order_by('-code')[:5]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
intervention_on_pause_qs = interventions.filter(status='on_pause').exclude(
|
||||||
|
Q(contract__uses_intervention_manager=True) &
|
||||||
|
~Q(intervention_manager=request.user)
|
||||||
|
)
|
||||||
|
intervention_on_pause_count = intervention_on_pause_qs.count()
|
||||||
|
intervention_on_pause_list = list(
|
||||||
|
intervention_on_pause_qs
|
||||||
|
.select_related('thematic')
|
||||||
|
.order_by('-code')[:5]
|
||||||
|
)
|
||||||
|
|
||||||
if home_contract_ids is not None:
|
if home_contract_ids is not None:
|
||||||
is_manager_filter_applied = user_contracts.filter(
|
is_manager_filter_applied = user_contracts.filter(
|
||||||
id__in=home_contract_ids, uses_intervention_manager=True
|
id__in=home_contract_ids, uses_intervention_manager=True
|
||||||
|
|
@ -641,6 +652,7 @@ def index(request):
|
||||||
).exists()
|
).exists()
|
||||||
|
|
||||||
total_finished_count = interventions.filter(status='finished').count()
|
total_finished_count = interventions.filter(status='finished').count()
|
||||||
|
total_on_pause_count = interventions.filter(status='on_pause').count()
|
||||||
|
|
||||||
is_inspector = 'inspector' in user_roles
|
is_inspector = 'inspector' in user_roles
|
||||||
inspection_configs = []
|
inspection_configs = []
|
||||||
|
|
@ -710,8 +722,11 @@ def index(request):
|
||||||
'intervention_to_plan_list': intervention_to_plan_list,
|
'intervention_to_plan_list': intervention_to_plan_list,
|
||||||
'intervention_finished_count': intervention_finished_count,
|
'intervention_finished_count': intervention_finished_count,
|
||||||
'intervention_finished_list': intervention_finished_list,
|
'intervention_finished_list': intervention_finished_list,
|
||||||
|
'intervention_on_pause_count': intervention_on_pause_count,
|
||||||
|
'intervention_on_pause_list': intervention_on_pause_list,
|
||||||
'is_manager_filter_applied': is_manager_filter_applied,
|
'is_manager_filter_applied': is_manager_filter_applied,
|
||||||
'total_finished_count': total_finished_count,
|
'total_finished_count': total_finished_count,
|
||||||
|
'total_on_pause_count': total_on_pause_count,
|
||||||
'intervention_to_approve_count': intervention_to_approve_count,
|
'intervention_to_approve_count': intervention_to_approve_count,
|
||||||
'intervention_to_approve_list': intervention_to_approve_list,
|
'intervention_to_approve_list': intervention_to_approve_list,
|
||||||
'intervention_checked_to_approve_count': intervention_checked_to_approve_count,
|
'intervention_checked_to_approve_count': intervention_checked_to_approve_count,
|
||||||
|
|
|
||||||
|
|
@ -628,6 +628,63 @@ class InterventionManagerTests(TestCase):
|
||||||
response2 = self.client.get(reverse('home:index'))
|
response2 = self.client.get(reverse('home:index'))
|
||||||
self.assertFalse(response2.context['is_manager_filter_applied'])
|
self.assertFalse(response2.context['is_manager_filter_applied'])
|
||||||
|
|
||||||
|
def test_homepage_on_pause_interventions_filtering(self):
|
||||||
|
# Create an intervention on_pause, with contract using manager, but manager is None (not this user)
|
||||||
|
itv_other_mgr = Intervention.objects.create(
|
||||||
|
title='Other Manager on pause',
|
||||||
|
status='on_pause',
|
||||||
|
contract=self.contract,
|
||||||
|
thematic=self.thematic,
|
||||||
|
intervention_manager=None,
|
||||||
|
symptom=self.symptom,
|
||||||
|
)
|
||||||
|
# Create an intervention on_pause, with contract using manager, managed by self.user
|
||||||
|
itv_self_mgr = Intervention.objects.create(
|
||||||
|
title='Self Manager on pause',
|
||||||
|
status='on_pause',
|
||||||
|
contract=self.contract,
|
||||||
|
thematic=self.thematic,
|
||||||
|
intervention_manager=self.user,
|
||||||
|
symptom=self.symptom,
|
||||||
|
)
|
||||||
|
# Create an intervention on_pause, but contract does not use manager
|
||||||
|
contract_no_mgr = Contract.objects.create(
|
||||||
|
company=self.company,
|
||||||
|
contract_number='C-NOMGR-PAUSE',
|
||||||
|
start_date=self.contract.start_date,
|
||||||
|
end_date=self.contract.end_date,
|
||||||
|
uses_intervention_manager=False
|
||||||
|
)
|
||||||
|
# Grant view permission for the no-manager contract
|
||||||
|
from common.models import UserContractAccess
|
||||||
|
UserContractAccess.objects.create(
|
||||||
|
user_config=self.user_config,
|
||||||
|
contract=contract_no_mgr,
|
||||||
|
can_view_interventions=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
itv_no_mgr = Intervention.objects.create(
|
||||||
|
title='No Manager on pause',
|
||||||
|
status='on_pause',
|
||||||
|
contract=contract_no_mgr,
|
||||||
|
thematic=self.thematic,
|
||||||
|
symptom=self.symptom,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Access the homepage
|
||||||
|
response = self.client.get(reverse('home:index'))
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
# On pause list/counter should only contain self-managed and no-manager interventions
|
||||||
|
on_pause_list = response.context['intervention_on_pause_list']
|
||||||
|
self.assertIn(itv_self_mgr, on_pause_list)
|
||||||
|
self.assertIn(itv_no_mgr, on_pause_list)
|
||||||
|
self.assertNotIn(itv_other_mgr, on_pause_list)
|
||||||
|
|
||||||
|
self.assertEqual(response.context['intervention_on_pause_count'], 2)
|
||||||
|
self.assertEqual(response.context['total_on_pause_count'], 3)
|
||||||
|
self.assertTrue(response.context['is_manager_filter_applied'])
|
||||||
|
|
||||||
def test_geojson_filtering_by_manager(self):
|
def test_geojson_filtering_by_manager(self):
|
||||||
# Create an intervention finished managed by self.user
|
# Create an intervention finished managed by self.user
|
||||||
itv_self_mgr = Intervention.objects.create(
|
itv_self_mgr = Intervention.objects.create(
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue