diff --git a/lemarche/tenders/management/commands/send_validated_tenders.py b/lemarche/tenders/management/commands/send_validated_tenders.py index a15deddd5..2d6a34c7d 100644 --- a/lemarche/tenders/management/commands/send_validated_tenders.py +++ b/lemarche/tenders/management/commands/send_validated_tenders.py @@ -1,7 +1,7 @@ from django.core.management.base import BaseCommand from lemarche.tenders.models import Tender -from lemarche.www.tenders.tasks import send_validated_tender +from lemarche.www.tenders.tasks import send_tender_emails_to_siaes, send_validated_tender class Command(BaseCommand): @@ -18,9 +18,18 @@ class Command(BaseCommand): """ def handle(self, *args, **options): + # First send newly validated tenders validated_tenders_to_send = Tender.objects.validated_but_not_sent() if validated_tenders_to_send.count(): self.stdout.write(f"Found {validated_tenders_to_send.count()} validated tender(s) to send") for tender in validated_tenders_to_send: - self.stdout.write(f"Found {tender} ") send_validated_tender(tender) + + # Then look at already sent tenders (batch mode) + validated_sent_tenders_batch_to_send = Tender.objects.validated_sent_batch() + if validated_sent_tenders_batch_to_send.count(): + self.stdout.write( + f"Found {validated_sent_tenders_batch_to_send.count()} validated sent tender(s) to batch" + ) + for tender in validated_sent_tenders_batch_to_send: + send_tender_emails_to_siaes(tender) diff --git a/lemarche/tenders/migrations/0066_tender_limit_nb_siae_interested_and_more.py b/lemarche/tenders/migrations/0066_tender_limit_nb_siae_interested_and_more.py index dd44005ba..7dbe7f05f 100644 --- a/lemarche/tenders/migrations/0066_tender_limit_nb_siae_interested_and_more.py +++ b/lemarche/tenders/migrations/0066_tender_limit_nb_siae_interested_and_more.py @@ -43,7 +43,7 @@ class Migration(migrations.Migration): name="version", field=models.PositiveIntegerField(default=0, verbose_name="Version"), ), - # this is migration to manage the existants stock + # we set the value to 0 for existing Tenders, and 1 to future Tenders migrations.AlterField( model_name="tender", name="version", diff --git a/lemarche/tenders/models.py b/lemarche/tenders/models.py index fc8090db9..4196c482e 100644 --- a/lemarche/tenders/models.py +++ b/lemarche/tenders/models.py @@ -19,7 +19,7 @@ from lemarche.siaes.models import Siae from lemarche.tenders import constants as tender_constants from lemarche.users.models import User -from lemarche.utils.constants import MARCHE_BENEFIT_CHOICES, RECALCULATED_FIELD_HELP_TEXT +from lemarche.utils.constants import ADMIN_FIELD_HELP_TEXT, MARCHE_BENEFIT_CHOICES, RECALCULATED_FIELD_HELP_TEXT from lemarche.utils.fields import ChoiceArrayField @@ -44,23 +44,27 @@ def by_user(self, user): def validated(self): return self.filter(validated_at__isnull=False) + def sent(self): + return self.filter(first_sent_at__isnull=False) + def validated_but_not_sent(self): + return self.validated().filter(first_sent_at__isnull=True) + + def validated_sent_batch(self): yesterday = timezone.now() - timedelta(days=1) - return self.with_siae_stats().filter( - (Q(version=0) & Q(validated_at__isnull=False) & Q(first_sent_at__isnull=True)) - | ( + return ( + self.with_siae_stats() + .validated() + .sent() + .filter( Q(version=1) - & Q(validated_at__isnull=False) & Q(siae_detail_contact_click_count_annotated__lte=F("limit_nb_siae_interested")) & ~Q(siae_count_annotated=F("siae_email_send_count_annotated")) - & (Q(first_sent_at__isnull=True) | Q(last_sent_at__lt=yesterday)) + & Q(last_sent_at__lt=yesterday) ) ) - def sent(self): - return self.filter(first_sent_at__isnull=False) - def is_incremental(self): return self.filter( scale_marche_useless__in=[ @@ -443,29 +447,29 @@ class Tender(models.Model): notes = GenericRelation("notes.Note", related_query_name="tender") siae_transactioned = models.BooleanField( verbose_name="Abouti à une transaction avec une structure", - help_text="Champ renseigné par un ADMIN", + help_text=ADMIN_FIELD_HELP_TEXT, blank=True, null=True, ) amount_exact = models.PositiveIntegerField( - verbose_name="Montant exact du besoin", help_text="Champ renseigné par un ADMIN", blank=True, null=True + verbose_name="Montant exact du besoin", help_text=ADMIN_FIELD_HELP_TEXT, blank=True, null=True ) incremental_custom = models.PositiveSmallIntegerField( verbose_name="Modification de l'incrémental (%)", - help_text="Champ renseigné par un ADMIN", + help_text=ADMIN_FIELD_HELP_TEXT, blank=True, null=True, default=None, ) limit_send_to_siae_batch = models.PositiveSmallIntegerField( verbose_name="Nombre de SIAES par envoi", - help_text="Champ renseigné par un ADMIN", + help_text=ADMIN_FIELD_HELP_TEXT, default=10, ) limit_nb_siae_interested = models.PositiveSmallIntegerField( verbose_name="Limite des SIAES intéressées", - help_text="Champ renseigné par un ADMIN", + help_text=ADMIN_FIELD_HELP_TEXT, default=5, ) # stats diff --git a/lemarche/tenders/tests.py b/lemarche/tenders/tests.py index 6c207eef7..4d15ef455 100644 --- a/lemarche/tenders/tests.py +++ b/lemarche/tenders/tests.py @@ -202,6 +202,66 @@ def test_sent(self): TenderFactory(first_sent_at=None) self.assertEqual(Tender.objects.sent().count(), 1) + def test_validated_but_not_sent(self): + siae = SiaeFactory() + TenderFactory(siaes=[siae], status=tender_constants.STATUS_PUBLISHED, validated_at=None, first_sent_at=None) + TenderFactory( + siaes=[siae], status=tender_constants.STATUS_VALIDATED, validated_at=timezone.now(), first_sent_at=None + ) + TenderFactory( + siaes=[siae], + status=tender_constants.STATUS_SENT, + validated_at=timezone.now(), + first_sent_at=timezone.now(), + ) + self.assertEqual(Tender.objects.validated_but_not_sent().count(), 1) + + def test_validated_sent_batch(self): + one_hour_ago = timezone.now() - timedelta(hours=1) + two_days_ago = timezone.now() - timedelta(days=1) + siae = SiaeFactory() + TenderFactory( + siaes=[siae], + version=1, + status=tender_constants.STATUS_PUBLISHED, + validated_at=None, + first_sent_at=None, + last_sent_at=None, + ) + TenderFactory( + siaes=[siae], + version=1, + status=tender_constants.STATUS_VALIDATED, + validated_at=timezone.now(), + first_sent_at=None, + last_sent_at=None, + ) + TenderFactory( + siaes=[siae], + version=1, + status=tender_constants.STATUS_SENT, + validated_at=timezone.now(), + first_sent_at=timezone.now(), + last_sent_at=timezone.now(), + ) + TenderFactory( + siaes=[siae], + version=1, + status=tender_constants.STATUS_SENT, + validated_at=one_hour_ago, + first_sent_at=one_hour_ago, + last_sent_at=one_hour_ago, + ) + TenderFactory( + siaes=[siae], + version=1, + status=tender_constants.STATUS_SENT, + validated_at=two_days_ago, + first_sent_at=two_days_ago, + last_sent_at=two_days_ago, + ) + self.assertEqual(Tender.objects.validated_sent_batch().count(), 1) + def test_is_live(self): TenderFactory(deadline_date=timezone.now() + timedelta(days=1)) TenderFactory(deadline_date=timezone.now() - timedelta(days=1)) diff --git a/lemarche/utils/constants.py b/lemarche/utils/constants.py index f3c182d6a..697318793 100644 --- a/lemarche/utils/constants.py +++ b/lemarche/utils/constants.py @@ -1,5 +1,6 @@ EMPTY_CHOICE = (("", ""),) +ADMIN_FIELD_HELP_TEXT = "Champ renseigné par un ADMIN" RECALCULATED_FIELD_HELP_TEXT = "Champ recalculé à intervalles réguliers" MARCHE_BENEFIT_TIME = "TIME"