1248 lines
49 KiB
Python
1248 lines
49 KiB
Python
import json
|
|
import time
|
|
from datetime import timedelta
|
|
from unittest import mock
|
|
|
|
from django.test import TestCase
|
|
from django.contrib.auth import get_user_model
|
|
from django.contrib import messages
|
|
from django.contrib.messages import get_messages
|
|
from django.core.files.uploadedfile import SimpleUploadedFile
|
|
|
|
from common.models import UserConfig, Role, Thematic, UserThematics, UserContractAccess
|
|
from contracts.models import Company, CompanyTeam, CompanyMember, Contract, ContractPost, ContractOrder, ContractOrderQuote, ContractOrderQuoteItem
|
|
from assets.models import AssetCategory
|
|
from interventions.models import (
|
|
Intervention,
|
|
InterventionContractPost,
|
|
ContractOperationTemplatePost,
|
|
InterventionPlanificationTimeLine,
|
|
InterventionRealizationTimeLine,
|
|
Operation,
|
|
InterventionAsset,
|
|
OperationTemplate,
|
|
OperationTemplateItem,
|
|
OperationStatusSet,
|
|
OperationStatus,
|
|
STATUS_ORDERS,
|
|
InterventionDocument,
|
|
)
|
|
from django.contrib.contenttypes.models import ContentType
|
|
from interventions.views import _group_operations_by_asset_and_template, _create_equipment_operations_for_assets
|
|
from interventions.permissions import can_manage_occupations, can_delete_document
|
|
from django.urls import reverse
|
|
from django.utils import timezone
|
|
from decimal import Decimal
|
|
|
|
|
|
|
|
class OperationTemplateTests(TestCase):
|
|
"""Test the operation template functionality including template types."""
|
|
|
|
def setUp(self):
|
|
from interventions.models import OperationTemplate, OperationTemplateItem, OperationStatusSet
|
|
from common.models import Thematic
|
|
|
|
# Create a test thematic
|
|
self.thematic = Thematic.objects.create(
|
|
code='test',
|
|
name_fr='Test',
|
|
name_nl='Test'
|
|
)
|
|
|
|
# Create generic template
|
|
self.generic_template = OperationTemplate.objects.create(
|
|
name='Generic Inspection',
|
|
description='Generic inspection template',
|
|
thematic=self.thematic
|
|
)
|
|
|
|
# Create equipment template
|
|
self.equipment_template = OperationTemplate.objects.create(
|
|
name='Equipment Check',
|
|
description='Equipment check template',
|
|
thematic=self.thematic
|
|
)
|
|
|
|
# Create status set
|
|
self.status_set = OperationStatusSet.objects.create(name='Standard')
|
|
|
|
# Add items to templates
|
|
OperationTemplateItem.objects.create(
|
|
template=self.generic_template,
|
|
name_fr='Vérification générale',
|
|
name_nl='Algemene controle',
|
|
status_set=self.status_set,
|
|
order=1
|
|
)
|
|
|
|
OperationTemplateItem.objects.create(
|
|
template=self.equipment_template,
|
|
name_fr='Vérification équipement',
|
|
name_nl='Apparatuurcontrole',
|
|
status_set=self.status_set,
|
|
order=1
|
|
)
|
|
|
|
def test_template_items_creation(self):
|
|
"""Test that template items are created correctly."""
|
|
generic_items = self.generic_template.items.all()
|
|
equipment_items = self.equipment_template.items.all()
|
|
|
|
self.assertEqual(generic_items.count(), 1)
|
|
self.assertEqual(equipment_items.count(), 1)
|
|
|
|
self.assertEqual(generic_items.first().name_fr, 'Vérification générale')
|
|
self.assertEqual(equipment_items.first().name_fr, 'Vérification équipement')
|
|
|
|
def test_operation_with_template_group(self):
|
|
"""Test that operations can be created with template group."""
|
|
from interventions.models import Intervention, Operation
|
|
|
|
intervention = Intervention.objects.create(
|
|
title='Test Intervention',
|
|
status='in_preparation',
|
|
maintain_type='preventive'
|
|
)
|
|
|
|
# Create operation with template group
|
|
operation = Operation.objects.create(
|
|
intervention=intervention,
|
|
name_fr='Test operation',
|
|
name_nl='Test operatie',
|
|
template_group=self.generic_template.name,
|
|
order=1
|
|
)
|
|
|
|
self.assertEqual(operation.template_group, 'Generic Inspection')
|
|
self.assertEqual(intervention.operations.count(), 1)
|
|
|
|
# Test ordering by template_group
|
|
operation2 = Operation.objects.create(
|
|
intervention=intervention,
|
|
name_fr='Test operation 2',
|
|
name_nl='Test operatie 2',
|
|
template_group=self.equipment_template.name,
|
|
order=1
|
|
)
|
|
|
|
operations = intervention.operations.all()
|
|
self.assertEqual(operations.count(), 2)
|
|
# Operations should be ordered by template_group first
|
|
self.assertEqual(operations[0].template_group, 'Equipment Check')
|
|
self.assertEqual(operations[1].template_group, 'Generic Inspection')
|
|
|
|
def test_get_asset_model_filters(self):
|
|
"""Test that get_asset_model_filters returns the correct dictionary with asset model IDs."""
|
|
from interventions.models import OperationTemplateAssetModelFilter
|
|
from django.contrib.contenttypes.models import ContentType
|
|
from common.models import Thematic
|
|
|
|
ct = ContentType.objects.get_for_model(Thematic)
|
|
|
|
# Create a filter entry
|
|
filter_entry = OperationTemplateAssetModelFilter.objects.create(
|
|
template=self.equipment_template,
|
|
asset_model_content_type=ct,
|
|
asset_model_object_id=999
|
|
)
|
|
|
|
filters = self.equipment_template.get_asset_model_filters()
|
|
self.assertIsNotNone(filters)
|
|
self.assertIn('thematic', filters)
|
|
self.assertIn(999, filters['thematic'])
|
|
|
|
|
|
|
|
|
|
|
|
class OperationGroupingTests(TestCase):
|
|
def setUp(self):
|
|
self.intervention = Intervention.objects.create(
|
|
title='Grouped intervention',
|
|
status='in_preparation',
|
|
maintain_type='preventive'
|
|
)
|
|
self.company = Company.objects.create(name='Asset Company')
|
|
self.asset_ct = ContentType.objects.get_for_model(Company)
|
|
self.intervention_asset = InterventionAsset.objects.create(
|
|
intervention=self.intervention,
|
|
content_type=self.asset_ct,
|
|
object_id=self.company.id,
|
|
)
|
|
|
|
def test_operations_grouped_by_asset_and_template(self):
|
|
op_asset_a = Operation.objects.create(
|
|
intervention=self.intervention,
|
|
name_fr='Contrôle câble',
|
|
name_nl='Kabelcontrole',
|
|
template_group='Template A',
|
|
order=1,
|
|
asset_content_type=self.asset_ct,
|
|
asset_object_id=self.company.id,
|
|
)
|
|
op_asset_b = Operation.objects.create(
|
|
intervention=self.intervention,
|
|
name_fr='Contrôle boîtier',
|
|
name_nl='Kastcontrole',
|
|
template_group='Template B',
|
|
order=2,
|
|
asset_content_type=self.asset_ct,
|
|
asset_object_id=self.company.id,
|
|
)
|
|
op_generic = Operation.objects.create(
|
|
intervention=self.intervention,
|
|
name_fr='Inspection générale',
|
|
name_nl='Algemene inspectie',
|
|
template_group=None,
|
|
order=3,
|
|
)
|
|
|
|
linked_assets = [self.intervention_asset]
|
|
operations = [op_asset_a, op_asset_b, op_generic]
|
|
|
|
grouped = _group_operations_by_asset_and_template(operations, linked_assets)
|
|
|
|
self.assertEqual(len(grouped), 2)
|
|
self.assertFalse(grouped[0]['has_asset'])
|
|
|
|
asset_group = next(group for group in grouped if group['has_asset'])
|
|
self.assertEqual(asset_group['asset'], self.company)
|
|
self.assertEqual(len(asset_group['template_groups']), 2)
|
|
self.assertListEqual(
|
|
[tg['name'] for tg in asset_group['template_groups']],
|
|
['Template A', 'Template B']
|
|
)
|
|
self.assertEqual(asset_group['template_groups'][0]['operations'][0], op_asset_a)
|
|
|
|
generic_group = next(group for group in grouped if not group['has_asset'])
|
|
self.assertEqual(len(generic_group['template_groups']), 1)
|
|
self.assertIsNone(generic_group['template_groups'][0]['name'])
|
|
self.assertEqual(generic_group['template_groups'][0]['operations'][0], op_generic)
|
|
|
|
|
|
|
|
|
|
class PreventiveInterventionFormTests(TestCase):
|
|
def test_preventive_intervention_form_status_choices(self):
|
|
from interventions.views.forms import PreventiveInterventionForm
|
|
|
|
# Test default choices (type = 'care' or default)
|
|
form_default = PreventiveInterventionForm()
|
|
status_field_default = form_default.fields['status']
|
|
choices_default = [val for val, label in status_field_default.choices]
|
|
self.assertIn('in_preparation', choices_default)
|
|
self.assertIn('to_be_approved', choices_default)
|
|
self.assertIn('to_be_planned', choices_default)
|
|
self.assertNotIn('to_be_processed', choices_default)
|
|
self.assertNotIn('assigned', choices_default)
|
|
|
|
# Test choices when type = 'inspection' in initial data
|
|
form_inspection = PreventiveInterventionForm(initial={'type': 'inspection'})
|
|
status_field_inspection = form_inspection.fields['status']
|
|
choices_inspection = [val for val, label in status_field_inspection.choices]
|
|
self.assertIn('in_preparation', choices_inspection)
|
|
self.assertIn('to_be_approved', choices_inspection)
|
|
self.assertIn('to_be_planned', choices_inspection)
|
|
self.assertIn('to_be_processed', choices_inspection)
|
|
self.assertIn('assigned', choices_inspection)
|
|
|
|
# Test choices when type = 'inspection' in submitted data (POST)
|
|
form_post = PreventiveInterventionForm(data={'type': 'inspection'})
|
|
status_field_post = form_post.fields['status']
|
|
choices_post = [val for val, label in status_field_post.choices]
|
|
self.assertIn('in_preparation', choices_post)
|
|
self.assertIn('to_be_approved', choices_post)
|
|
self.assertIn('to_be_planned', choices_post)
|
|
self.assertIn('to_be_processed', choices_post)
|
|
self.assertIn('assigned', choices_post)
|
|
|
|
|
|
|
|
|
|
class PreventiveDraftFormDataTests(TestCase):
|
|
def test_multiple_operation_templates_are_preserved(self):
|
|
User = get_user_model()
|
|
user = User.objects.create_user(username='preventive_creator', password='pwd')
|
|
role_admin, _ = Role.objects.get_or_create(name='admin')
|
|
user_config = UserConfig.objects.create(user=user, is_intern=True)
|
|
user_config.roles.add(role_admin)
|
|
|
|
thematic = Thematic.objects.create(code='test', name_fr='Test', name_nl='Test')
|
|
UserThematics.objects.create(
|
|
user_config=user_config,
|
|
thematic=thematic,
|
|
can_view_assets=True,
|
|
can_edit_interventions=True,
|
|
)
|
|
|
|
self.client.login(username='preventive_creator', password='pwd')
|
|
url = reverse('interventions:intervention_preventive_draft', kwargs={'thematic_code': 'test'})
|
|
response = self.client.post(url, {
|
|
'title': 'Préventive',
|
|
'operation_templates': ['1', '2'],
|
|
})
|
|
|
|
self.assertEqual(response.status_code, 302)
|
|
|
|
session = self.client.session
|
|
drafts = session.get('intervention_drafts', {})
|
|
self.assertEqual(len(drafts), 1)
|
|
|
|
draft_data = next(iter(drafts.values()))
|
|
self.assertEqual(draft_data['form_data'].get('operation_templates'), ['1', '2'])
|
|
|
|
|
|
|
|
|
|
class EquipmentTemplateOperationsTests(TestCase):
|
|
def setUp(self):
|
|
self.thematic = Thematic.objects.create(code='thema', name_fr='Thema', name_nl='Thema')
|
|
self.status_set = OperationStatusSet.objects.create(name='Default')
|
|
self.template = OperationTemplate.objects.create(
|
|
name='Equipment Template',
|
|
thematic=self.thematic,
|
|
)
|
|
OperationTemplateItem.objects.create(
|
|
template=self.template,
|
|
name_fr='Inspection A',
|
|
name_nl='Inspectie A',
|
|
status_set=self.status_set,
|
|
order=1,
|
|
)
|
|
OperationTemplateItem.objects.create(
|
|
template=self.template,
|
|
name_fr='Inspection B',
|
|
name_nl='Inspectie B',
|
|
status_set=self.status_set,
|
|
order=2,
|
|
)
|
|
|
|
self.intervention = Intervention.objects.create(
|
|
title='Preventive',
|
|
status='in_preparation',
|
|
maintain_type='preventive',
|
|
)
|
|
self.company = Company.objects.create(name='Asset Company')
|
|
self.asset_ct = ContentType.objects.get_for_model(Company)
|
|
self.intervention.equipment_operation_templates.add(self.template)
|
|
|
|
def _call_helper(self):
|
|
return _create_equipment_operations_for_assets(
|
|
self.intervention,
|
|
[(self.asset_ct, self.company)],
|
|
)
|
|
|
|
def test_operations_created_for_new_asset(self):
|
|
created = self._call_helper()
|
|
self.assertEqual(created, 2)
|
|
|
|
operations = Operation.objects.filter(
|
|
intervention=self.intervention,
|
|
asset_content_type=self.asset_ct,
|
|
asset_object_id=self.company.id,
|
|
).order_by('order')
|
|
|
|
self.assertEqual(operations.count(), 2)
|
|
self.assertListEqual(
|
|
list(operations.values_list('name_fr', flat=True)),
|
|
['Inspection A', 'Inspection B'],
|
|
)
|
|
self.assertTrue(all(op.template_group == self.template.name for op in operations))
|
|
|
|
def test_operations_not_duplicated_for_same_asset(self):
|
|
first_run = self._call_helper()
|
|
self.assertEqual(first_run, 2)
|
|
|
|
second_run = self._call_helper()
|
|
self.assertEqual(second_run, 0)
|
|
|
|
operations = Operation.objects.filter(
|
|
intervention=self.intervention,
|
|
asset_content_type=self.asset_ct,
|
|
asset_object_id=self.company.id,
|
|
)
|
|
self.assertEqual(operations.count(), 2)
|
|
|
|
|
|
|
|
class AssetOperationCleanupTests(TestCase):
|
|
def setUp(self):
|
|
self.User = get_user_model()
|
|
self.user = self.User.objects.create_user(username='asset-admin', password='pwd')
|
|
self.role_admin = Role.objects.create(name='admin')
|
|
self.user_config = UserConfig.objects.create(user=self.user, is_intern=True)
|
|
self.user_config.roles.add(self.role_admin)
|
|
|
|
self.thematic = Thematic.objects.create(code='road', name_fr='Route', name_nl='Weg')
|
|
self.intervention = Intervention.objects.create(
|
|
title='Asset cleanup',
|
|
status='in_preparation',
|
|
maintain_type='preventive',
|
|
thematic=self.thematic,
|
|
)
|
|
self.company = Company.objects.create(name='Linked asset company')
|
|
self.asset_ct = ContentType.objects.get_for_model(Company)
|
|
InterventionAsset.objects.create(
|
|
intervention=self.intervention,
|
|
content_type=self.asset_ct,
|
|
object_id=self.company.id,
|
|
)
|
|
|
|
self.status_set = OperationStatusSet.objects.create(name='Ops')
|
|
self.status_ok = OperationStatus.objects.create(
|
|
status_set=self.status_set,
|
|
label_fr='OK',
|
|
label_nl='OK',
|
|
value='ok',
|
|
order=1,
|
|
)
|
|
|
|
self.operation_one = Operation.objects.create(
|
|
intervention=self.intervention,
|
|
name_fr='Op FR 1',
|
|
name_nl='Op NL 1',
|
|
order=1,
|
|
template_group='Template A',
|
|
asset_content_type=self.asset_ct,
|
|
asset_object_id=self.company.id,
|
|
)
|
|
self.operation_two = Operation.objects.create(
|
|
intervention=self.intervention,
|
|
name_fr='Op FR 2',
|
|
name_nl='Op NL 2',
|
|
order=2,
|
|
template_group='Template A',
|
|
asset_content_type=self.asset_ct,
|
|
asset_object_id=self.company.id,
|
|
)
|
|
|
|
self.url = reverse('interventions:intervention_link_assets', args=[self.intervention.id])
|
|
|
|
def _post_assets(self, assets):
|
|
payload = json.dumps(assets)
|
|
return self.client.post(self.url, {'assets': payload})
|
|
|
|
def test_operations_removed_when_asset_detached(self):
|
|
self.client.login(username='asset-admin', password='pwd')
|
|
|
|
response = self._post_assets([])
|
|
self.assertEqual(response.status_code, 302)
|
|
|
|
remaining_assets = InterventionAsset.objects.filter(intervention=self.intervention)
|
|
self.assertFalse(remaining_assets.exists())
|
|
self.assertFalse(Operation.objects.filter(intervention=self.intervention).exists())
|
|
|
|
success_messages = [m for m in get_messages(response.wsgi_request) if m.level == messages.SUCCESS]
|
|
self.assertTrue(success_messages)
|
|
|
|
def test_warning_emitted_when_operations_had_values(self):
|
|
self.client.login(username='asset-admin', password='pwd')
|
|
|
|
self.operation_one.status = self.status_ok
|
|
self.operation_one.save(update_fields=['status'])
|
|
|
|
response = self._post_assets([])
|
|
warning_messages = [m for m in get_messages(response.wsgi_request) if m.level == messages.WARNING]
|
|
self.assertTrue(warning_messages)
|
|
|
|
# Aucun nettoyage car au moins une opération contenait des valeurs
|
|
remaining_ops = Operation.objects.filter(intervention=self.intervention)
|
|
self.assertEqual(remaining_ops.count(), 2)
|
|
|
|
|
|
class ExternalManagerPreventivePermissionsTests(TestCase):
|
|
def setUp(self):
|
|
User = get_user_model()
|
|
self.role_ext_manager, _ = Role.objects.get_or_create(name='external_manager')
|
|
self.role_manager, _ = Role.objects.get_or_create(name='manager')
|
|
|
|
self.thematic = Thematic.objects.create(code='test_thema', name_fr='Test', name_nl='Test')
|
|
|
|
self.provider = Company.objects.create(name="Test Provider")
|
|
from datetime import date, timedelta
|
|
today = date.today()
|
|
self.contract_with_rights = Contract.objects.create(
|
|
contract_number="C-001",
|
|
company=self.provider,
|
|
is_active=True,
|
|
start_date=today - timedelta(days=30),
|
|
end_date=today + timedelta(days=365)
|
|
)
|
|
self.contract_with_rights.thematics.add(self.thematic)
|
|
|
|
self.contract_without_rights = Contract.objects.create(
|
|
contract_number="C-002",
|
|
company=self.provider,
|
|
is_active=True,
|
|
start_date=today - timedelta(days=30),
|
|
end_date=today + timedelta(days=365)
|
|
)
|
|
self.contract_without_rights.thematics.add(self.thematic)
|
|
|
|
# 1. User with creation rights
|
|
self.user_with_rights = User.objects.create_user(username='ext_with_rights', password='pwd')
|
|
self.uc_with_rights = UserConfig.objects.create(user=self.user_with_rights, is_intern=False)
|
|
self.uc_with_rights.roles.add(self.role_ext_manager)
|
|
UserThematics.objects.create(
|
|
user_config=self.uc_with_rights,
|
|
thematic=self.thematic,
|
|
can_view_assets=True,
|
|
can_edit_interventions=True,
|
|
)
|
|
UserContractAccess.objects.create(
|
|
user_config=self.uc_with_rights,
|
|
contract=self.contract_with_rights,
|
|
can_create_interventions=True
|
|
)
|
|
|
|
# 2. User without creation rights
|
|
self.user_without_rights = User.objects.create_user(username='ext_without_rights', password='pwd')
|
|
self.uc_without_rights = UserConfig.objects.create(user=self.user_without_rights, is_intern=False)
|
|
self.uc_without_rights.roles.add(self.role_ext_manager)
|
|
UserThematics.objects.create(
|
|
user_config=self.uc_without_rights,
|
|
thematic=self.thematic,
|
|
can_view_assets=True,
|
|
can_edit_interventions=True,
|
|
)
|
|
UserContractAccess.objects.create(
|
|
user_config=self.uc_without_rights,
|
|
contract=self.contract_without_rights,
|
|
can_create_interventions=False
|
|
)
|
|
|
|
# 3. An internal manager to view the detail page and verify the badge
|
|
self.internal_manager = User.objects.create_user(username='int_manager', password='pwd')
|
|
self.uc_int = UserConfig.objects.create(user=self.internal_manager, is_intern=True)
|
|
self.uc_int.roles.add(self.role_manager)
|
|
|
|
def test_access_to_add_preventive_view(self):
|
|
# User with rights should access
|
|
self.client.login(username='ext_with_rights', password='pwd')
|
|
url = reverse('interventions:intervention_add_preventive', kwargs={'thematic_code': 'test_thema'})
|
|
|
|
# We need to set up a mock draft in the session
|
|
session = self.client.session
|
|
session['intervention_drafts'] = {
|
|
'draft-1': {
|
|
'locations': [],
|
|
'assets': [],
|
|
'form_data': {}
|
|
}
|
|
}
|
|
session.save()
|
|
|
|
response = self.client.get(f"{url}?draft_id=draft-1")
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
# User without rights should be Forbidden (403)
|
|
self.client.login(username='ext_without_rights', password='pwd')
|
|
response = self.client.get(f"{url}?draft_id=draft-1")
|
|
self.assertEqual(response.status_code, 403)
|
|
|
|
def test_contracts_filtered_in_form(self):
|
|
self.client.login(username='ext_with_rights', password='pwd')
|
|
url = reverse('interventions:intervention_add_preventive', kwargs={'thematic_code': 'test_thema'})
|
|
|
|
session = self.client.session
|
|
session['intervention_drafts'] = {
|
|
'draft-1': {
|
|
'locations': [],
|
|
'assets': [],
|
|
'form_data': {}
|
|
}
|
|
}
|
|
session.save()
|
|
|
|
response = self.client.get(f"{url}?draft_id=draft-1")
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
# Verify that contract_with_rights is in form's queryset, but contract_without_rights is not
|
|
contracts_queryset = response.context['form'].fields['contract'].queryset
|
|
self.assertIn(self.contract_with_rights, contracts_queryset)
|
|
self.assertNotIn(self.contract_without_rights, contracts_queryset)
|
|
|
|
def test_preventive_intervention_created_by_provider(self):
|
|
self.client.login(username='ext_with_rights', password='pwd')
|
|
url = reverse('interventions:intervention_add_preventive', kwargs={'thematic_code': 'test_thema'})
|
|
|
|
session = self.client.session
|
|
session['intervention_drafts'] = {
|
|
'draft-1': {
|
|
'locations': [],
|
|
'assets': [],
|
|
'form_data': {}
|
|
}
|
|
}
|
|
session.save()
|
|
|
|
# Submit creation form
|
|
post_data = {
|
|
'title': 'Test Preventive Creation',
|
|
'status': 'in_preparation',
|
|
'type': 'care',
|
|
'contract': self.contract_with_rights.id,
|
|
'expected_begin_time': '2026-08-13T12:00',
|
|
'expected_end_time': '2026-08-13T13:00',
|
|
'recurrence_count': 0,
|
|
'type_of_selected_elements': 'locations',
|
|
'intervention_creation_mode': 'single_for_all',
|
|
'draft_id': 'draft-1',
|
|
}
|
|
response = self.client.post(url, post_data)
|
|
self.assertEqual(response.status_code, 302)
|
|
|
|
# Check created intervention
|
|
intervention = Intervention.objects.filter(title='Test Preventive Creation').first()
|
|
self.assertIsNotNone(intervention)
|
|
self.assertEqual(intervention.created_by, self.user_with_rights)
|
|
self.assertTrue(intervention.created_by_provider)
|
|
self.assertEqual(intervention.maintain_type, 'preventive')
|
|
|
|
# Check that the badge is shown for internal manager
|
|
from interventions.permissions import should_show_created_by_provider_badge
|
|
self.assertTrue(should_show_created_by_provider_badge(self.internal_manager, intervention))
|
|
|
|
|
|
class PreventiveTemplateTeamAssignmentTests(TestCase):
|
|
"""Test automatic team assignment from OperationTemplate in preventive interventions."""
|
|
|
|
def setUp(self):
|
|
User = get_user_model()
|
|
self.thematic, _ = Thematic.objects.get_or_create(code='trafficlights', defaults={'name_fr': 'Signalisation', 'name_nl': 'Signalisatie'})
|
|
self.company = Company.objects.create(name='Provider Corp')
|
|
self.team_a = CompanyTeam.objects.create(name='Equipe Alpha', company=self.company)
|
|
self.team_a.thematics.add(self.thematic)
|
|
self.team_b = CompanyTeam.objects.create(name='Equipe Beta', company=self.company)
|
|
self.team_b.thematics.add(self.thematic)
|
|
|
|
from datetime import date, timedelta
|
|
today = date.today()
|
|
self.contract = Contract.objects.create(
|
|
contract_number='C-TEST-001',
|
|
company=self.company,
|
|
is_active=True,
|
|
start_date=today - timedelta(days=30),
|
|
end_date=today + timedelta(days=365)
|
|
)
|
|
self.contract.thematics.add(self.thematic)
|
|
|
|
self.user = User.objects.create_user(username='manager_user', password='pwd')
|
|
self.role_manager = Role.objects.create(name='manager')
|
|
self.config = UserConfig.objects.create(user=self.user, is_intern=True)
|
|
self.config.roles.add(self.role_manager)
|
|
self.config.contracts.add(self.contract)
|
|
UserThematics.objects.create(user_config=self.config, thematic=self.thematic, can_edit_interventions=True)
|
|
|
|
self.status_set = OperationStatusSet.objects.create(name='Standard Status Set')
|
|
|
|
# Template with team A
|
|
self.template_with_team = OperationTemplate.objects.create(
|
|
name='Gamme Entretien A',
|
|
description='Gamme avec équipe A',
|
|
thematic=self.thematic,
|
|
team=self.team_a,
|
|
)
|
|
OperationTemplateItem.objects.create(
|
|
template=self.template_with_team,
|
|
name_fr='Vérification A',
|
|
name_nl='Controle A',
|
|
status_set=self.status_set,
|
|
is_generic=True,
|
|
order=1,
|
|
)
|
|
|
|
# Template with team B
|
|
self.template_with_team_b = OperationTemplate.objects.create(
|
|
name='Gamme Entretien B',
|
|
description='Gamme avec équipe B',
|
|
thematic=self.thematic,
|
|
team=self.team_b,
|
|
)
|
|
OperationTemplateItem.objects.create(
|
|
template=self.template_with_team_b,
|
|
name_fr='Vérification B',
|
|
name_nl='Controle B',
|
|
status_set=self.status_set,
|
|
is_generic=True,
|
|
order=1,
|
|
)
|
|
|
|
# Template without team
|
|
self.template_without_team = OperationTemplate.objects.create(
|
|
name='Gamme Sans Equipe',
|
|
description='Gamme sans équipe',
|
|
thematic=self.thematic,
|
|
team=None,
|
|
)
|
|
OperationTemplateItem.objects.create(
|
|
template=self.template_without_team,
|
|
name_fr='Vérification C',
|
|
name_nl='Controle C',
|
|
status_set=self.status_set,
|
|
is_generic=True,
|
|
order=1,
|
|
)
|
|
|
|
def test_assign_team_from_templates_helper(self):
|
|
"""Test _assign_team_from_templates helper function directly."""
|
|
from interventions.views.helpers import _assign_team_from_templates
|
|
|
|
itv = Intervention.objects.create(
|
|
title='Test ITV',
|
|
status='in_preparation',
|
|
maintain_type='preventive',
|
|
thematic=self.thematic,
|
|
)
|
|
|
|
# No templates -> no change
|
|
_assign_team_from_templates(itv, [])
|
|
self.assertIsNone(itv.assigned_team)
|
|
|
|
# Template without team -> no change
|
|
_assign_team_from_templates(itv, [self.template_without_team])
|
|
self.assertIsNone(itv.assigned_team)
|
|
|
|
# Template with team -> assigned
|
|
_assign_team_from_templates(itv, [self.template_with_team])
|
|
itv.refresh_from_db()
|
|
self.assertEqual(itv.assigned_team, self.team_a)
|
|
self.assertEqual(itv.assigned_provider, self.company)
|
|
|
|
def test_preventive_creation_auto_assigns_team_single_mode(self):
|
|
"""Test preventive intervention creation assigns team from template in single_for_all mode."""
|
|
self.client.login(username='manager_user', password='pwd')
|
|
url = reverse('interventions:intervention_add_preventive', kwargs={'thematic_code': 'trafficlights'})
|
|
|
|
session = self.client.session
|
|
session['intervention_drafts'] = {
|
|
'draft-team': {
|
|
'locations': [],
|
|
'assets': [],
|
|
'form_data': {},
|
|
'timestamp': time.time(),
|
|
}
|
|
}
|
|
session.save()
|
|
|
|
post_data = {
|
|
'title': 'Preventive with team',
|
|
'status': 'in_preparation',
|
|
'type': 'care',
|
|
'contract': self.contract.id,
|
|
'operation_templates': [str(self.template_with_team.id)],
|
|
'expected_begin_time': '2026-08-20T08:00',
|
|
'expected_end_time': '2026-08-20T17:00',
|
|
'recurrence_count': 0,
|
|
'type_of_selected_elements': 'locations',
|
|
'intervention_creation_mode': 'single_for_all',
|
|
'draft_id': 'draft-team',
|
|
}
|
|
response = self.client.post(url, post_data)
|
|
self.assertEqual(response.status_code, 302)
|
|
|
|
intervention = Intervention.objects.filter(title='Preventive with team').first()
|
|
self.assertIsNotNone(intervention)
|
|
self.assertEqual(intervention.assigned_team, self.team_a)
|
|
self.assertEqual(intervention.assigned_provider, self.company)
|
|
self.assertIn(self.template_with_team, intervention.equipment_operation_templates.all())
|
|
|
|
def test_preventive_creation_multiple_templates_first_team_assigned(self):
|
|
"""When multiple templates are selected, the first one with a team sets the assigned_team."""
|
|
self.client.login(username='manager_user', password='pwd')
|
|
url = reverse('interventions:intervention_add_preventive', kwargs={'thematic_code': 'trafficlights'})
|
|
|
|
session = self.client.session
|
|
session['intervention_drafts'] = {
|
|
'draft-multi': {
|
|
'locations': [],
|
|
'assets': [],
|
|
'form_data': {},
|
|
'timestamp': time.time(),
|
|
}
|
|
}
|
|
session.save()
|
|
|
|
# template_without_team first, then template_with_team_b
|
|
post_data = {
|
|
'title': 'Preventive multi templates',
|
|
'status': 'in_preparation',
|
|
'type': 'care',
|
|
'contract': self.contract.id,
|
|
'operation_templates': [str(self.template_without_team.id), str(self.template_with_team_b.id)],
|
|
'expected_begin_time': '2026-08-20T08:00',
|
|
'expected_end_time': '2026-08-20T17:00',
|
|
'recurrence_count': 0,
|
|
'type_of_selected_elements': 'locations',
|
|
'intervention_creation_mode': 'single_for_all',
|
|
'draft_id': 'draft-multi',
|
|
}
|
|
response = self.client.post(url, post_data)
|
|
self.assertEqual(response.status_code, 302)
|
|
|
|
intervention = Intervention.objects.filter(title='Preventive multi templates').first()
|
|
self.assertIsNotNone(intervention)
|
|
self.assertEqual(intervention.assigned_team, self.team_b)
|
|
|
|
def test_preventive_creation_without_team_template(self):
|
|
"""When template has no team, assigned_team remains None."""
|
|
self.client.login(username='manager_user', password='pwd')
|
|
url = reverse('interventions:intervention_add_preventive', kwargs={'thematic_code': 'trafficlights'})
|
|
|
|
session = self.client.session
|
|
session['intervention_drafts'] = {
|
|
'draft-noteam': {
|
|
'locations': [],
|
|
'assets': [],
|
|
'form_data': {},
|
|
'timestamp': time.time(),
|
|
}
|
|
}
|
|
session.save()
|
|
|
|
post_data = {
|
|
'title': 'Preventive without team',
|
|
'status': 'in_preparation',
|
|
'type': 'care',
|
|
'contract': self.contract.id,
|
|
'operation_templates': [str(self.template_without_team.id)],
|
|
'expected_begin_time': '2026-08-20T08:00',
|
|
'expected_end_time': '2026-08-20T17:00',
|
|
'recurrence_count': 0,
|
|
'type_of_selected_elements': 'locations',
|
|
'intervention_creation_mode': 'single_for_all',
|
|
'draft_id': 'draft-noteam',
|
|
}
|
|
response = self.client.post(url, post_data)
|
|
self.assertEqual(response.status_code, 302)
|
|
|
|
intervention = Intervention.objects.filter(title='Preventive without team').first()
|
|
self.assertIsNotNone(intervention)
|
|
self.assertIsNone(intervention.assigned_team)
|
|
|
|
def test_preventive_creation_auto_assigns_team_one_per_element_locations(self):
|
|
"""Test preventive creation in one_per_element mode auto-assigns team to each intervention."""
|
|
from assets.models import TrafficLightIntersection
|
|
loc = TrafficLightIntersection.objects.create(code='TL-001', name_fr='Carrefour 1')
|
|
|
|
self.client.login(username='manager_user', password='pwd')
|
|
url = reverse('interventions:intervention_add_preventive', kwargs={'thematic_code': 'trafficlights'})
|
|
|
|
session = self.client.session
|
|
session['intervention_drafts'] = {
|
|
'draft-loc': {
|
|
'locations': [loc.id],
|
|
'assets': [],
|
|
'form_data': {},
|
|
'timestamp': time.time(),
|
|
}
|
|
}
|
|
session.save()
|
|
|
|
post_data = {
|
|
'title': 'Preventive Location',
|
|
'status': 'in_preparation',
|
|
'type': 'care',
|
|
'contract': self.contract.id,
|
|
'operation_templates': [str(self.template_with_team.id)],
|
|
'expected_begin_time': '2026-08-20T08:00',
|
|
'expected_end_time': '2026-08-20T17:00',
|
|
'recurrence_count': 0,
|
|
'type_of_selected_elements': 'locations',
|
|
'intervention_creation_mode': 'one_per_element',
|
|
'draft_id': 'draft-loc',
|
|
}
|
|
response = self.client.post(url, post_data)
|
|
self.assertEqual(response.status_code, 302)
|
|
|
|
intervention = Intervention.objects.filter(location_code='TL-001').first()
|
|
self.assertIsNotNone(intervention)
|
|
self.assertEqual(intervention.assigned_team, self.team_a)
|
|
self.assertEqual(intervention.assigned_provider, self.company)
|
|
|
|
def test_ajax_get_teams_by_thematic(self):
|
|
"""Test the AJAX endpoint ajax_get_teams_by_thematic."""
|
|
self.client.login(username='manager_user', password='pwd')
|
|
url = reverse('interventions:ajax_get_teams_by_thematic')
|
|
|
|
# Filter by thematic
|
|
response = self.client.get(f"{url}?thematic_id={self.thematic.id}")
|
|
self.assertEqual(response.status_code, 200)
|
|
data = response.json()
|
|
self.assertIn('teams', data)
|
|
team_ids = [t['id'] for t in data['teams']]
|
|
self.assertIn(self.team_a.id, team_ids)
|
|
self.assertIn(self.team_b.id, team_ids)
|
|
|
|
# Without thematic -> all teams
|
|
response = self.client.get(url)
|
|
self.assertEqual(response.status_code, 200)
|
|
data = response.json()
|
|
self.assertIn('teams', data)
|
|
self.assertGreaterEqual(len(data['teams']), 2)
|
|
|
|
|
|
class ContractOperationTemplatePostTests(TestCase):
|
|
"""Test associating ContractPosts with default quantities to OperationTemplates and preventive interventions."""
|
|
|
|
def setUp(self):
|
|
User = get_user_model()
|
|
self.user = User.objects.create_user(username='test_user_posts', password='password123')
|
|
self.thematic, _ = Thematic.objects.get_or_create(
|
|
code='trafficlights',
|
|
defaults={'name_fr': 'Feux', 'name_nl': 'Verkeerslichten'}
|
|
)
|
|
self.role, _ = Role.objects.get_or_create(name='manager')
|
|
self.user_config = UserConfig.objects.create(user=self.user, is_intern=True)
|
|
self.user_config.roles.add(self.role)
|
|
|
|
self.company = Company.objects.create(name='Provider Co')
|
|
self.contract = Contract.objects.create(
|
|
contract_number='CTR-TEST-001',
|
|
company=self.company,
|
|
start_date=timezone.now().date(),
|
|
end_date=timezone.now().date() + timedelta(days=365),
|
|
is_active=True,
|
|
)
|
|
self.contract.thematics.add(self.thematic)
|
|
self.user_config.contracts.add(self.contract)
|
|
uca = UserContractAccess.objects.get(user_config=self.user_config, contract=self.contract)
|
|
uca.can_edit_interventions = True
|
|
uca.can_view_posts = True
|
|
uca.can_view_interventions = True
|
|
uca.save()
|
|
UserThematics.objects.create(user_config=self.user_config, thematic=self.thematic, can_edit_interventions=True)
|
|
|
|
self.contract_other = Contract.objects.create(
|
|
contract_number='CTR-OTHER-002',
|
|
company=self.company,
|
|
start_date=timezone.now().date(),
|
|
end_date=timezone.now().date() + timedelta(days=365),
|
|
is_active=True,
|
|
)
|
|
|
|
self.post_1 = ContractPost.objects.create(
|
|
contract=self.contract,
|
|
code='P01',
|
|
description_fr='Poste 1 FR',
|
|
description_nl='Post 1 NL',
|
|
order_number=1,
|
|
unit='pc',
|
|
unit_price=Decimal('10.00'),
|
|
)
|
|
self.post_2 = ContractPost.objects.create(
|
|
contract=self.contract,
|
|
code='P02',
|
|
description_fr='Poste 2 FR',
|
|
description_nl='Post 2 NL',
|
|
order_number=2,
|
|
unit='m',
|
|
unit_price=Decimal('25.50'),
|
|
)
|
|
self.post_3 = ContractPost.objects.create(
|
|
contract=self.contract,
|
|
code='P03',
|
|
description_fr='Poste 3 FR',
|
|
description_nl='Post 3 NL',
|
|
order_number=3,
|
|
unit='h',
|
|
unit_price=Decimal('50.00'),
|
|
)
|
|
self.post_other = ContractPost.objects.create(
|
|
contract=self.contract_other,
|
|
code='P_OTHER',
|
|
description_fr='Poste Autre Contrat',
|
|
description_nl='Post Andere',
|
|
order_number=1,
|
|
unit='pc',
|
|
unit_price=Decimal('100.00'),
|
|
)
|
|
|
|
self.template_a = OperationTemplate.objects.create(
|
|
name='Template A',
|
|
thematic=self.thematic
|
|
)
|
|
self.template_b = OperationTemplate.objects.create(
|
|
name='Template B',
|
|
thematic=self.thematic
|
|
)
|
|
|
|
def test_model_str_and_save_clean(self):
|
|
"""Test model validation, clean method, auto-contract and string representation."""
|
|
from django.core.exceptions import ValidationError
|
|
|
|
# Auto-populates contract if only contract_post is set
|
|
mapping = ContractOperationTemplatePost(
|
|
operation_template=self.template_a,
|
|
contract_post=self.post_1,
|
|
default_quantity=Decimal('2.00'),
|
|
comment='Commentaire test'
|
|
)
|
|
mapping.save()
|
|
self.assertEqual(mapping.contract, self.contract)
|
|
self.assertIn('CTR-TEST-001', str(mapping))
|
|
self.assertIn('Template A', str(mapping))
|
|
self.assertIn('P01', str(mapping))
|
|
|
|
# Invalid: contract_post belongs to another contract
|
|
invalid_mapping = ContractOperationTemplatePost(
|
|
contract=self.contract,
|
|
operation_template=self.template_a,
|
|
contract_post=self.post_other,
|
|
default_quantity=Decimal('1.00')
|
|
)
|
|
with self.assertRaises(ValidationError):
|
|
invalid_mapping.clean()
|
|
|
|
def test_assign_contract_posts_from_templates_single(self):
|
|
"""Test assigning default posts to an intervention from a single template."""
|
|
from interventions.views.helpers import _assign_contract_posts_from_templates
|
|
|
|
ContractOperationTemplatePost.objects.create(
|
|
contract=self.contract,
|
|
operation_template=self.template_a,
|
|
contract_post=self.post_1,
|
|
default_quantity=Decimal('3.00'),
|
|
comment='Nettoyage standard'
|
|
)
|
|
ContractOperationTemplatePost.objects.create(
|
|
contract=self.contract,
|
|
operation_template=self.template_a,
|
|
contract_post=self.post_2,
|
|
default_quantity=Decimal('1.50'),
|
|
)
|
|
|
|
intervention = Intervention.objects.create(
|
|
title='Intervention Test',
|
|
contract=self.contract,
|
|
thematic=self.thematic,
|
|
maintain_type='preventive',
|
|
created_by=self.user
|
|
)
|
|
|
|
_assign_contract_posts_from_templates(intervention, [self.template_a])
|
|
|
|
posts = list(intervention.interv_contract_posts.all().order_by('contract_post__order_number'))
|
|
self.assertEqual(len(posts), 2)
|
|
self.assertEqual(posts[0].contract_post, self.post_1)
|
|
self.assertEqual(posts[0].quantity, Decimal('3.00'))
|
|
self.assertEqual(posts[0].comment, 'Nettoyage standard')
|
|
self.assertEqual(posts[1].contract_post, self.post_2)
|
|
self.assertEqual(posts[1].quantity, Decimal('1.50'))
|
|
|
|
def test_assign_contract_posts_deduplication_first_found(self):
|
|
"""
|
|
When multiple templates are chosen and share a post,
|
|
the first one encountered must be kept, and subsequent duplicates ignored.
|
|
"""
|
|
from interventions.views.helpers import _assign_contract_posts_from_templates
|
|
|
|
# Template A defines Post 1 (qty 2) and Post 2 (qty 5)
|
|
ContractOperationTemplatePost.objects.create(
|
|
contract=self.contract,
|
|
operation_template=self.template_a,
|
|
contract_post=self.post_1,
|
|
default_quantity=Decimal('2.00'),
|
|
comment='From Template A'
|
|
)
|
|
ContractOperationTemplatePost.objects.create(
|
|
contract=self.contract,
|
|
operation_template=self.template_a,
|
|
contract_post=self.post_2,
|
|
default_quantity=Decimal('5.00'),
|
|
)
|
|
|
|
# Template B defines Post 1 (qty 10 with other comment) and Post 3 (qty 1)
|
|
ContractOperationTemplatePost.objects.create(
|
|
contract=self.contract,
|
|
operation_template=self.template_b,
|
|
contract_post=self.post_1,
|
|
default_quantity=Decimal('10.00'),
|
|
comment='From Template B'
|
|
)
|
|
ContractOperationTemplatePost.objects.create(
|
|
contract=self.contract,
|
|
operation_template=self.template_b,
|
|
contract_post=self.post_3,
|
|
default_quantity=Decimal('1.00'),
|
|
)
|
|
|
|
intervention = Intervention.objects.create(
|
|
title='Intervention Multi-Templates',
|
|
contract=self.contract,
|
|
thematic=self.thematic,
|
|
maintain_type='preventive',
|
|
created_by=self.user
|
|
)
|
|
|
|
# Pass templates in order [A, B]
|
|
_assign_contract_posts_from_templates(intervention, [self.template_a, self.template_b])
|
|
|
|
posts = list(intervention.interv_contract_posts.all().order_by('contract_post__order_number'))
|
|
self.assertEqual(len(posts), 3)
|
|
|
|
# Post 1 must come from Template A (qty 2, first found)
|
|
self.assertEqual(posts[0].contract_post, self.post_1)
|
|
self.assertEqual(posts[0].quantity, Decimal('2.00'))
|
|
self.assertEqual(posts[0].comment, 'From Template A')
|
|
|
|
# Post 2 from Template A
|
|
self.assertEqual(posts[1].contract_post, self.post_2)
|
|
self.assertEqual(posts[1].quantity, Decimal('5.00'))
|
|
|
|
# Post 3 from Template B
|
|
self.assertEqual(posts[2].contract_post, self.post_3)
|
|
self.assertEqual(posts[2].quantity, Decimal('1.00'))
|
|
|
|
def test_assign_contract_posts_empty_or_no_contract(self):
|
|
"""Test that helper handles empty templates or no contract gracefully."""
|
|
from interventions.views.helpers import _assign_contract_posts_from_templates
|
|
|
|
intervention_no_contract = Intervention.objects.create(
|
|
title='No Contract',
|
|
thematic=self.thematic,
|
|
created_by=self.user
|
|
)
|
|
_assign_contract_posts_from_templates(intervention_no_contract, [self.template_a])
|
|
self.assertEqual(intervention_no_contract.interv_contract_posts.count(), 0)
|
|
|
|
intervention = Intervention.objects.create(
|
|
title='With Contract',
|
|
contract=self.contract,
|
|
thematic=self.thematic,
|
|
created_by=self.user
|
|
)
|
|
_assign_contract_posts_from_templates(intervention, [])
|
|
self.assertEqual(intervention.interv_contract_posts.count(), 0)
|
|
|
|
def test_preventive_creation_flow_auto_creates_posts(self):
|
|
"""Test full preventive intervention creation flow automatically populates posts."""
|
|
from assets.models import TrafficLightIntersection
|
|
loc = TrafficLightIntersection.objects.create(code='TL-POST-01', name_fr='Carrefour Test')
|
|
|
|
ContractOperationTemplatePost.objects.create(
|
|
contract=self.contract,
|
|
operation_template=self.template_a,
|
|
contract_post=self.post_1,
|
|
default_quantity=Decimal('4.00'),
|
|
comment='Auto Post'
|
|
)
|
|
|
|
self.client.login(username='test_user_posts', password='password123')
|
|
url = reverse('interventions:intervention_add_preventive', kwargs={'thematic_code': 'trafficlights'})
|
|
|
|
session = self.client.session
|
|
session['intervention_drafts'] = {
|
|
'draft-posts': {
|
|
'locations': [loc.id],
|
|
'assets': [],
|
|
'form_data': {},
|
|
'timestamp': time.time(),
|
|
}
|
|
}
|
|
session.save()
|
|
|
|
post_data = {
|
|
'title': 'Preventive With Posts',
|
|
'status': 'in_preparation',
|
|
'type': 'care',
|
|
'contract': self.contract.id,
|
|
'operation_templates': [str(self.template_a.id)],
|
|
'expected_begin_time': '2026-08-20T08:00',
|
|
'expected_end_time': '2026-08-20T17:00',
|
|
'recurrence_count': 0,
|
|
'type_of_selected_elements': 'locations',
|
|
'intervention_creation_mode': 'one_per_element',
|
|
'draft_id': 'draft-posts',
|
|
}
|
|
response = self.client.post(url, post_data)
|
|
self.assertEqual(response.status_code, 302)
|
|
|
|
intervention = Intervention.objects.filter(location_code='TL-POST-01').first()
|
|
self.assertIsNotNone(intervention)
|
|
posts = list(intervention.interv_contract_posts.all())
|
|
self.assertEqual(len(posts), 1)
|
|
self.assertEqual(posts[0].contract_post, self.post_1)
|
|
self.assertEqual(posts[0].quantity, Decimal('4.00'))
|
|
self.assertEqual(posts[0].comment, 'Auto Post')
|
|
|
|
def test_detail_view_includes_associated_order_and_posts_modal(self):
|
|
"""Test that the detail view context includes orders and correctly preselects the associated order."""
|
|
admin_role, _ = Role.objects.get_or_create(name='admin')
|
|
self.user_config.roles.add(admin_role)
|
|
from contracts.models import ContractOrder
|
|
order = ContractOrder.objects.create(
|
|
contract=self.contract,
|
|
order_code='CMD-2026-001',
|
|
order_date=timezone.now().date(),
|
|
delivery_date=timezone.now().date() + timedelta(days=30),
|
|
order_status='pending',
|
|
total_amount=Decimal('500.00'),
|
|
)
|
|
intervention = Intervention.objects.create(
|
|
title='Intervention With Order',
|
|
contract=self.contract,
|
|
order=order,
|
|
thematic=self.thematic,
|
|
status='in_preparation',
|
|
created_by=self.user,
|
|
)
|
|
|
|
self.client.login(username='test_user_posts', password='password123')
|
|
detail_url = reverse('interventions:intervention_detail', kwargs={'intervention_id': intervention.id})
|
|
response = self.client.get(detail_url)
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
# Check context
|
|
self.assertIn('orders', response.context)
|
|
orders = response.context['orders']
|
|
self.assertIsNotNone(orders)
|
|
self.assertIn(order, orders)
|
|
self.assertEqual(response.context['contract_id'], self.contract.id)
|
|
|
|
# Check rendered HTML for modal selector
|
|
content = response.content.decode('utf-8')
|
|
self.assertIn(f'data-initial-order-id="{order.id}"', content)
|
|
self.assertIn(f'value="{order.id}"', content)
|
|
self.assertIn('selected', content)
|
|
|
|
def test_ajax_get_posts_by_contract(self):
|
|
"""Test the AJAX endpoint filtering contract posts by contract."""
|
|
self.client.login(username='test_user_posts', password='password123')
|
|
url = reverse('interventions:ajax_get_posts_by_contract')
|
|
|
|
# Call with no contract_id
|
|
res = self.client.get(url)
|
|
self.assertEqual(res.status_code, 200)
|
|
self.assertEqual(res.json().get('posts'), [])
|
|
|
|
# Call with self.contract
|
|
res = self.client.get(url, {'contract_id': self.contract.id})
|
|
self.assertEqual(res.status_code, 200)
|
|
posts_data = res.json().get('posts', [])
|
|
post_ids = [p['id'] for p in posts_data]
|
|
self.assertIn(self.post_1.id, post_ids)
|
|
self.assertIn(self.post_2.id, post_ids)
|
|
self.assertNotIn(self.post_other.id, post_ids)
|
|
|
|
def test_ajax_get_contracts_by_thematic(self):
|
|
"""Test the AJAX endpoint filtering contracts by thematic."""
|
|
self.client.login(username='test_user_posts', password='password123')
|
|
url = reverse('interventions:ajax_get_contracts_by_thematic')
|
|
|
|
res = self.client.get(url, {'thematic_id': self.thematic.id})
|
|
self.assertEqual(res.status_code, 200)
|
|
contracts_data = res.json().get('contracts', [])
|
|
contract_ids = [c['id'] for c in contracts_data]
|
|
self.assertIn(self.contract.id, contract_ids)
|
|
self.assertNotIn(self.contract_other.id, contract_ids)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|