From 9578fb4abef418dbe444b590843211c33dfa9a9e Mon Sep 17 00:00:00 2001 From: Alejandro MG Date: Thu, 19 Dec 2024 17:57:00 +0100 Subject: [PATCH] Allows automatic approval for declarations with multiple "SUBMIT" snapshots --- config/tasks.py | 15 +++++---- config/tests/test_automatic_approval.py | 44 +++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 7 deletions(-) diff --git a/config/tasks.py b/config/tasks.py index 711d6edc..ac6358e6 100644 --- a/config/tasks.py +++ b/config/tasks.py @@ -3,17 +3,17 @@ from django.conf import settings from django.db import transaction -from django.db.models import Count, Max +from django.db.models import Count, F, Max, Q from django.utils import timezone from dateutil.relativedelta import relativedelta from viewflow import fsm from config import email +from data.etl.transformer_loader import ETL_OPEN_DATA_DECLARATIONS from data.models import Declaration, Snapshot from .celery import app -from data.etl.transformer_loader import ETL_OPEN_DATA_DECLARATIONS logger = logging.getLogger(__name__) Status = Declaration.DeclarationStatus @@ -145,11 +145,12 @@ def approve_declarations(): article__in=[Declaration.Article.ARTICLE_15, Declaration.Article.ARTICLE_15_HIGH_RISK_POPULATION], ) # Plus précisement, seulement les déclarations qui n'ont pas été traitées ni touchées par l'instruction, - # et donc ont seulement un snapshot : celui créé par la soumission DRAFT => AWAITING_INSTRUCTION - .annotate(snapshot_count=Count("snapshots")) - .filter(snapshot_count=1) - # On vérifie que le snapshot en question soit bien du type "SUBMIT" - .filter(snapshots__action=Snapshot.SnapshotActions.SUBMIT) + # et donc ont seulement des snapshots type "SUBMIT" (créés par le passage DRAFT => AWAITING_INSTRUCTION) + .annotate( + total_snapshot_count=Count("snapshots"), + submit_snapshots_count=Count("snapshots", filter=Q(snapshots__action=Snapshot.SnapshotActions.SUBMIT)), + ) + .filter(total_snapshot_count=F("submit_snapshots_count")) # Et finalement on ne prend que celles soumises au moins il y a deux mois .annotate(submission_date=Max("snapshots__creation_date")) .filter(submission_date__lt=cutoff_delta) diff --git a/config/tests/test_automatic_approval.py b/config/tests/test_automatic_approval.py index 311d0866..fea2e91f 100644 --- a/config/tests/test_automatic_approval.py +++ b/config/tests/test_automatic_approval.py @@ -69,6 +69,50 @@ def test_awaiting_declaration_approved_art_15(self, _): latest_snapshot_hrp = declaration_high_risk_population.snapshots.latest("creation_date") self.assertEqual(latest_snapshot_hrp.action, Snapshot.SnapshotActions.AUTOMATICALLY_AUTHORIZE) + def test_double_submission_declaration_approved_art_15(self, _): + """ + Dans certains cas, un·e admin peut remettre la déclaration à l'état brouillon depuis + l'admin afin que le pro puisse corriger une erreur repéré tardivement. Dans ces cas, + deux snapshots type SUBMIT sont créés. Le bot doit quand même approuver ces déclarations. + Plus d'infos : https://github.com/betagouv/complements-alimentaires/issues/1395 + """ + declaration_15 = AwaitingInstructionDeclarationFactory(overriden_article=Declaration.Article.ARTICLE_15) + + # Double soumission + TestAutomaticApproval._create_submission_snapshot(declaration_15) + TestAutomaticApproval._create_submission_snapshot(declaration_15) + + approve_declarations() + declaration_15.refresh_from_db() + + self.assertEqual(declaration_15.status, Declaration.DeclarationStatus.AUTHORIZED) + latest_snapshot_15 = declaration_15.snapshots.latest("creation_date") + self.assertEqual(latest_snapshot_15.action, Snapshot.SnapshotActions.AUTOMATICALLY_AUTHORIZE) + + def test_non_submission_snapshots_not_approved_art_15(self, _): + """ + Si au moins un snapshot est présent avec un type différent de "SUBMIT" la déclaration + ne devra pas être autorisée + """ + declaration_15 = AwaitingInstructionDeclarationFactory(overriden_article=Declaration.Article.ARTICLE_15) + + # Double soumission + TestAutomaticApproval._create_submission_snapshot(declaration_15) + TestAutomaticApproval._create_submission_snapshot(declaration_15) + + SnapshotFactory( + action=Snapshot.SnapshotActions.TAKE_FOR_INSTRUCTION, + status=Declaration.DeclarationStatus.AWAITING_INSTRUCTION, + declaration=declaration_15, + ) + + approve_declarations() + declaration_15.refresh_from_db() + + self.assertEqual(declaration_15.status, Declaration.DeclarationStatus.AWAITING_INSTRUCTION) + latest_snapshot_15 = declaration_15.snapshots.latest("creation_date") + self.assertEqual(latest_snapshot_15.action, Snapshot.SnapshotActions.TAKE_FOR_INSTRUCTION) + def test_email_sent_declaration_approved(self, mocked_brevo): """ L'email d'approbation doit être envoyé lors d'une approbation automatique