diff --git a/loko/contracts/tests.py b/loko/contracts/tests.py index abbb7e1..53147c2 100644 --- a/loko/contracts/tests.py +++ b/loko/contracts/tests.py @@ -232,3 +232,111 @@ class ContractDisplayNameTests(TestCase): def test_display_name_long_description_truncated(self): self.assertTrue(self.contract_long_desc.display_name.startswith("CTR-003 (Travaux et maintenance préventive et correct")) self.assertTrue(self.contract_long_desc.display_name.endswith("…)")) + + +class GenerateClaimDeclarationTimelineTests(TestCase): + def setUp(self): + from decimal import Decimal + from datetime import date + from contracts.models import Contract, ContractOrder, ContractPost + from common.models import UserContractAccess + from interventions.models import Intervention, InterventionTimeLine, InterventionContractPost, InterventionPostPeriodQuantity + + self.client = Client() + self.user = User.objects.create_user(username="admin_user", password="password") + self.user_config = UserConfig.objects.create(user=self.user, is_intern=True) + self.admin_role, _ = Role.objects.get_or_create(name="admin") + self.user_config.roles.add(self.admin_role) + self.client.login(username="admin_user", password="password") + + self.company = Company.objects.create(name="Company A") + self.contract = Contract.objects.create( + company=self.company, + contract_number="CTR-TEST-001", + start_date=date(2025, 1, 1), + end_date=date(2026, 1, 1), + ) + UserContractAccess.objects.create( + user_config=self.user_config, + contract=self.contract, + can_view_interventions=True, + ) + self.order = ContractOrder.objects.create( + contract=self.contract, + order_code="ORD-TEST-01", + order_status="pending", + order_date=date(2025, 1, 1), + delivery_date=date(2025, 1, 15), + description="Commande test", + ) + self.post = ContractPost.objects.create( + contract=self.contract, + order_number=1, + code="P1", + description_fr="Poste 1", + description_nl="Post 1", + unit="u", + unit_price=Decimal("10.00"), + ) + self.intervention_validated = Intervention.objects.create( + title="Intervention Validated", + contract=self.contract, + order=self.order, + status="validated", + ) + InterventionContractPost.objects.create( + intervention=self.intervention_validated, + contract_post=self.post, + quantity=Decimal("2.00"), + control_status="validated", + ) + + self.intervention_period = Intervention.objects.create( + title="Intervention with Validated Period", + contract=self.contract, + order=self.order, + status="to_be_processed", + ) + InterventionPostPeriodQuantity.objects.create( + intervention=self.intervention_period, + contract_post=self.post, + period_number=1, + quantity=Decimal("3.00"), + validated_at=timezone.now(), + validated_by=self.user, + status="validated", + ) + + def test_generate_documents_creates_timeline_events(self): + from interventions.models import InterventionTimeLine + + url = reverse("contracts:generate_documents_for_order", args=[self.order.id]) + response = self.client.get(url) + self.assertEqual(response.status_code, 302) + + # 1. Check validated intervention status & timeline + self.intervention_validated.refresh_from_db() + self.assertEqual(self.intervention_validated.status, "invoiced") + + timeline_event_1 = InterventionTimeLine.objects.filter( + intervention=self.intervention_validated, + to_status="invoiced", + ).first() + self.assertIsNotNone(timeline_event_1) + self.assertEqual(timeline_event_1.from_status, "validated") + self.assertEqual(timeline_event_1.event_type, "status_change") + self.assertEqual(timeline_event_1.event_user, self.user) + + # 2. Check intervention with period status & timeline + self.intervention_period.refresh_from_db() + self.assertEqual(self.intervention_period.status, "invoiced") + + timeline_event_2 = InterventionTimeLine.objects.filter( + intervention=self.intervention_period, + to_status="invoiced", + ).first() + self.assertIsNotNone(timeline_event_2) + self.assertEqual(timeline_event_2.from_status, "to_be_processed") + self.assertEqual(timeline_event_2.event_type, "status_change") + self.assertEqual(timeline_event_2.event_user, self.user) + diff --git a/loko/contracts/views_pdf_reports.py b/loko/contracts/views_pdf_reports.py index ec81474..fb6d3d0 100644 --- a/loko/contracts/views_pdf_reports.py +++ b/loko/contracts/views_pdf_reports.py @@ -1372,7 +1372,7 @@ def generate_documents_for_order(request, order_id): messages.error(request, _("Vous n'avez pas la permission de générer des documents pour cette commande.")) return redirect("contracts:contracts_orders_detail", order_id=order.id) - from interventions.models import InterventionContractPost, InterventionPostPeriodQuantity + from interventions.models import InterventionContractPost, InterventionPostPeriodQuantity, InterventionTimeLine # Collecter les commandes directes validées AVANT génération des documents (pour les inclure # dans l'état de travaux/Excel et les réinitialiser seulement après) @@ -1454,20 +1454,42 @@ def generate_documents_for_order(request, order_id): ) # Mettre à jour le statut des interventions : + now = timezone.now() + event_user = request.user if request.user.is_authenticated else None + # 1. Toutes les interventions validées (même sans postes validés) validated_interventions = order.interventions.filter(status='validated') for intervention in validated_interventions: + old_status = intervention.status intervention.status = 'invoiced' intervention.save() + InterventionTimeLine.objects.create( + intervention=intervention, + event_user=event_user, + event_time=now, + event_type='status_change', + from_status=old_status, + to_status='invoiced', + ) # 2. Toutes les interventions avec périodes validées (quel que soit leur statut) intervention_ids_with_validated_periods = validated_periods.values_list('intervention_id', flat=True).distinct() - interventions_with_periods = order.interventions.filter(id__in=intervention_ids_with_validated_periods) + interventions_with_periods = order.interventions.filter( + id__in=intervention_ids_with_validated_periods + ).exclude(status='invoiced') for intervention in interventions_with_periods: - # Passer à 'invoiced' seulement si pas déjà dans cet état if intervention.status != 'invoiced': + old_status = intervention.status intervention.status = 'invoiced' intervention.save() + InterventionTimeLine.objects.create( + intervention=intervention, + event_user=event_user, + event_time=now, + event_type='status_change', + from_status=old_status, + to_status='invoiced', + ) # Marquer les périodes validées comme 'invoiced' validated_periods.update(status='invoiced')