refactor: restrict contract blocked warning to specific intervention statuses and add test coverage
This commit is contained in:
parent
7f257a2ca3
commit
3a241e3d03
6 changed files with 77 additions and 10 deletions
|
|
@ -738,11 +738,21 @@ class Intervention(models.Model):
|
||||||
return self.assigned_team.company.name
|
return self.assigned_team.company.name
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
BLOCKED_CONTRACT_DISPLAY_STATUSES = ('in_preparation', 'to_be_approved', 'to_be_planned')
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_contract_blocked(self):
|
def is_contract_blocked(self):
|
||||||
"""Retourne True si l'intervention est marquée bloquée ou si son contrat est actuellement bloqué."""
|
"""Retourne True si l'intervention est marquée bloquée ou si son contrat est actuellement bloqué."""
|
||||||
return bool(self.is_blocked_due_to_contract or (self.contract and getattr(self.contract, 'is_blocked', False)))
|
return bool(self.is_blocked_due_to_contract or (self.contract and getattr(self.contract, 'is_blocked', False)))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def show_contract_blocked_warning(self):
|
||||||
|
"""
|
||||||
|
Indique si l'avertissement / badge de marché bloqué doit être affiché.
|
||||||
|
Uniquement affiché pour les interventions en préparation, à approuver ou à planifier.
|
||||||
|
"""
|
||||||
|
return bool(self.is_contract_blocked and self.status in self.BLOCKED_CONTRACT_DISPLAY_STATUSES)
|
||||||
|
|
||||||
def get_status_color(self):
|
def get_status_color(self):
|
||||||
return {
|
return {
|
||||||
"in_preparation": "secondary",
|
"in_preparation": "secondary",
|
||||||
|
|
|
||||||
|
|
@ -496,8 +496,9 @@ function statusFormatter(value, row) {
|
||||||
html += ` <span class="badge text-bg-${prepEntry.color}" title="${prepEntry.label}"><i class="bi ${prepEntry.icon}"></i></span>`;
|
html += ` <span class="badge text-bg-${prepEntry.color}" title="${prepEntry.label}"><i class="bi ${prepEntry.icon}"></i></span>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add blocked due to contract indicator if present
|
// Add blocked due to contract indicator if present (only for in_preparation, to_be_approved, to_be_planned)
|
||||||
if (row && (row.is_blocked_due_to_contract || (row.contract && row.contract.is_blocked))) {
|
const blockedStatuses = ['in_preparation', 'to_be_approved', 'to_be_planned'];
|
||||||
|
if (row && (row.is_blocked_due_to_contract || (row.contract && row.contract.is_blocked)) && blockedStatuses.includes(row.status || value)) {
|
||||||
html += ` <span class="badge text-bg-danger" title="${gettext('Marché temporairement bloqué')}"><i class="bi bi-slash-circle"></i></span>`;
|
html += ` <span class="badge text-bg-danger" title="${gettext('Marché temporairement bloqué')}"><i class="bi bi-slash-circle"></i></span>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,7 @@
|
||||||
<a href="{% url 'mobile:intervention_detail_mobile' intervention.id %}" class="me-2">{{ intervention.code }}</a> {{ intervention.title }}
|
<a href="{% url 'mobile:intervention_detail_mobile' intervention.id %}" class="me-2">{{ intervention.code }}</a> {{ intervention.title }}
|
||||||
</h6>
|
</h6>
|
||||||
<span class="badge bg-{{ intervention.get_status_color }} status-{{ intervention.status }}">{{ intervention.get_status_display }}</span>
|
<span class="badge bg-{{ intervention.get_status_color }} status-{{ intervention.status }}">{{ intervention.get_status_display }}</span>
|
||||||
{% if intervention.is_contract_blocked %}
|
{% if intervention.show_contract_blocked_warning %}
|
||||||
<span class="badge bg-danger" title="{% translate 'Marché temporairement bloqué' %}">
|
<span class="badge bg-danger" title="{% translate 'Marché temporairement bloqué' %}">
|
||||||
<i class="bi bi-slash-circle"></i> {% translate "Marché bloqué" %}
|
<i class="bi bi-slash-circle"></i> {% translate "Marché bloqué" %}
|
||||||
</span>
|
</span>
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<span class="field-value" style="color: var(--brand-color);">{{ intervention.title }}</span>
|
<span class="field-value" style="color: var(--brand-color);">{{ intervention.title }}</span>
|
||||||
{% if intervention.is_contract_blocked %}
|
{% if intervention.show_contract_blocked_warning %}
|
||||||
<span class="badge bg-danger text-white ms-2" title="{% translate 'Marché temporairement bloqué pour raisons budgétaires' %}">
|
<span class="badge bg-danger text-white ms-2" title="{% translate 'Marché temporairement bloqué pour raisons budgétaires' %}">
|
||||||
<i class="bi bi-slash-circle"></i> {% translate "Marché bloqué" %}
|
<i class="bi bi-slash-circle"></i> {% translate "Marché bloqué" %}
|
||||||
</span>
|
</span>
|
||||||
|
|
@ -84,7 +84,7 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% if intervention.is_contract_blocked %}
|
{% if intervention.show_contract_blocked_warning %}
|
||||||
<div class="alert alert-danger d-flex align-items-center mt-3 mb-2 py-2 px-3 shadow-sm border-danger" role="alert">
|
<div class="alert alert-danger d-flex align-items-center mt-3 mb-2 py-2 px-3 shadow-sm border-danger" role="alert">
|
||||||
<i class="bi bi-slash-circle-fill fs-4 me-2 text-danger"></i>
|
<i class="bi bi-slash-circle-fill fs-4 me-2 text-danger"></i>
|
||||||
<div>
|
<div>
|
||||||
|
|
@ -107,7 +107,7 @@
|
||||||
<h4>{% translate "Recommandations" %}</h4>
|
<h4>{% translate "Recommandations" %}</h4>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
{% if intervention.is_contract_blocked %}
|
{% if intervention.show_contract_blocked_warning %}
|
||||||
<div class="alert alert-warning d-flex align-items-center mb-3 py-2 px-3">
|
<div class="alert alert-warning d-flex align-items-center mb-3 py-2 px-3">
|
||||||
<i class="bi bi-telephone-x-fill fs-4 me-2 text-danger"></i>
|
<i class="bi bi-telephone-x-fill fs-4 me-2 text-danger"></i>
|
||||||
<div>
|
<div>
|
||||||
|
|
@ -121,7 +121,7 @@
|
||||||
<li class="list-group-item list-group-item-info d-flex justify-content-between align-items-center">
|
<li class="list-group-item list-group-item-info d-flex justify-content-between align-items-center">
|
||||||
<div>
|
<div>
|
||||||
<i class="bi {{recommendation.icon}} me-2"></i>
|
<i class="bi {{recommendation.icon}} me-2"></i>
|
||||||
{% if intervention.is_contract_blocked and recommendation.confirm_call %}
|
{% if intervention.show_contract_blocked_warning and recommendation.confirm_call %}
|
||||||
<span class="text-decoration-line-through text-muted">
|
<span class="text-decoration-line-through text-muted">
|
||||||
{% render_recommendation recommendation intervention.contract as rendered_text %}
|
{% render_recommendation recommendation intervention.contract as rendered_text %}
|
||||||
{{ rendered_text|safe }}
|
{{ rendered_text|safe }}
|
||||||
|
|
@ -132,7 +132,7 @@
|
||||||
{{ rendered_text|safe }}
|
{{ rendered_text|safe }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% if recommendation.confirm_call and not intervention.is_contract_blocked %}
|
{% if recommendation.confirm_call and not intervention.show_contract_blocked_warning %}
|
||||||
<button class="btn btn-outline-success btn-sm confirm-call"
|
<button class="btn btn-outline-success btn-sm confirm-call"
|
||||||
data-call-id="{{recommendation.order_number}}"
|
data-call-id="{{recommendation.order_number}}"
|
||||||
data-confirmed-at="{{ intervention.init_call_confirmed_time|date:'c' }}"
|
data-confirmed-at="{{ intervention.init_call_confirmed_time|date:'c' }}"
|
||||||
|
|
|
||||||
|
|
@ -472,7 +472,7 @@
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if intervention.is_contract_blocked %}
|
{% if intervention.show_contract_blocked_warning %}
|
||||||
<div class="alert alert-danger d-flex align-items-center mb-3 py-2 px-3 shadow-sm border-danger" role="alert">
|
<div class="alert alert-danger d-flex align-items-center mb-3 py-2 px-3 shadow-sm border-danger" role="alert">
|
||||||
<i class="bi bi-slash-circle-fill fs-4 me-2 text-danger"></i>
|
<i class="bi bi-slash-circle-fill fs-4 me-2 text-danger"></i>
|
||||||
<div>
|
<div>
|
||||||
|
|
@ -488,7 +488,7 @@
|
||||||
<div class="d-flex justify-content-between align-items-center mb-2 flex-wrap gap-2">
|
<div class="d-flex justify-content-between align-items-center mb-2 flex-wrap gap-2">
|
||||||
<div class="code d-flex align-items-center gap-2">
|
<div class="code d-flex align-items-center gap-2">
|
||||||
<span><i class="bi bi-tools"></i> {{ intervention.code }}</span>
|
<span><i class="bi bi-tools"></i> {{ intervention.code }}</span>
|
||||||
{% if intervention.is_contract_blocked %}
|
{% if intervention.show_contract_blocked_warning %}
|
||||||
<span class="badge bg-danger text-white ms-1" title="{% translate 'Marché bloqué' %}">
|
<span class="badge bg-danger text-white ms-1" title="{% translate 'Marché bloqué' %}">
|
||||||
<i class="bi bi-slash-circle"></i> {% translate "Marché bloqué" %}
|
<i class="bi bi-slash-circle"></i> {% translate "Marché bloqué" %}
|
||||||
</span>
|
</span>
|
||||||
|
|
|
||||||
|
|
@ -255,3 +255,59 @@ class BlockedContractsTests(TestCase):
|
||||||
itv.refresh_from_db()
|
itv.refresh_from_db()
|
||||||
self.assertEqual(itv.status, 'to_be_processed')
|
self.assertEqual(itv.status, 'to_be_processed')
|
||||||
|
|
||||||
|
def test_show_contract_blocked_warning_only_for_prep_approval_planning(self):
|
||||||
|
"""
|
||||||
|
Vérifie que show_contract_blocked_warning est True uniquement pour :
|
||||||
|
- in_preparation
|
||||||
|
- to_be_approved
|
||||||
|
- to_be_planned
|
||||||
|
Et False pour tous les autres statuts (ex: validated, finished, in_progress, etc.).
|
||||||
|
"""
|
||||||
|
allowed_warning_statuses = {'in_preparation', 'to_be_approved', 'to_be_planned'}
|
||||||
|
other_statuses = {
|
||||||
|
'to_be_processed', 'assigned', 'in_progress', 'on_pause', 'finished',
|
||||||
|
'processed', 'to_be_corrected', 'corrected', 'validated', 'invoiced',
|
||||||
|
'closed', 'canceled'
|
||||||
|
}
|
||||||
|
|
||||||
|
# Pour une intervention sur contrat bloqué
|
||||||
|
for status in allowed_warning_statuses:
|
||||||
|
itv = Intervention(
|
||||||
|
title=f'Test {status}',
|
||||||
|
contract=self.contract_blocked,
|
||||||
|
status=status,
|
||||||
|
thematic=self.thematic,
|
||||||
|
asset_category=self.category,
|
||||||
|
)
|
||||||
|
self.assertTrue(
|
||||||
|
itv.show_contract_blocked_warning,
|
||||||
|
f"show_contract_blocked_warning devrait être True pour le statut {status}"
|
||||||
|
)
|
||||||
|
|
||||||
|
for status in other_statuses:
|
||||||
|
itv = Intervention(
|
||||||
|
title=f'Test {status}',
|
||||||
|
contract=self.contract_blocked,
|
||||||
|
status=status,
|
||||||
|
thematic=self.thematic,
|
||||||
|
asset_category=self.category,
|
||||||
|
)
|
||||||
|
self.assertFalse(
|
||||||
|
itv.show_contract_blocked_warning,
|
||||||
|
f"show_contract_blocked_warning devrait être False pour le statut {status}"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Pour une intervention sur contrat actif (non bloqué)
|
||||||
|
for status in allowed_warning_statuses | other_statuses:
|
||||||
|
itv = Intervention(
|
||||||
|
title=f'Test {status} active',
|
||||||
|
contract=self.contract_active,
|
||||||
|
status=status,
|
||||||
|
thematic=self.thematic,
|
||||||
|
asset_category=self.category,
|
||||||
|
)
|
||||||
|
self.assertFalse(
|
||||||
|
itv.show_contract_blocked_warning,
|
||||||
|
f"show_contract_blocked_warning devrait être False sur contrat actif pour le statut {status}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue