diff --git a/lemarche/tenders/admin.py b/lemarche/tenders/admin.py index 53ab56dab..531a93496 100644 --- a/lemarche/tenders/admin.py +++ b/lemarche/tenders/admin.py @@ -780,11 +780,25 @@ class TenderSiaeSourceFilter(MultiChoice): BUTTON_LABEL = "Appliquer" +class TenderSiaeStatusFilter(admin.SimpleListFilter): + title = "Status" + parameter_name = "status" + + def lookups(self, request, model_admin): + return tender_constants.TENDER_SIAE_STATUS_CHOICES + + def queryset(self, request, queryset): + value = self.value() + return queryset.filter(status_annotated=value) + + @admin.register(TenderSiae, site=admin_site) class TenderSiaeAdmin(admin.ModelAdmin): - list_display = ["created_at", "siae_with_app_link", "tender_with_link", "source"] + list_display = ["created_at", "siae_with_app_link", "tender_with_link", "source", "status"] list_filter = [ ("source", TenderSiaeSourceFilter), + TenderSiaeStatusFilter, + "survey_transactioned_answer", ] readonly_fields = [field for field in TenderSiae.READONLY_FIELDS] + [ @@ -800,12 +814,18 @@ class TenderSiaeAdmin(admin.ModelAdmin): None, {"fields": ("siae", "siae_with_app_link", "tender_with_link", "source", "found_with_ai")}, ), - ("Mise en relation", {"fields": TenderSiae.FIELDS_RELATION}), + ("Mise en relation", {"fields": (*TenderSiae.FIELDS_RELATION, "status")}), ("Transaction ?", {"fields": TenderSiae.FIELDS_SURVEY_TRANSACTIONED}), ("Stats", {"fields": ("logs_display",)}), ("Dates", {"fields": ("created_at", "updated_at")}), ) + def get_queryset(self, request): + qs = super().get_queryset(request) + qs = qs.with_prefetch_related() + qs = qs.with_status() + return qs + def has_add_permission(self, request): return False @@ -826,6 +846,12 @@ def tender_with_link(self, tendersiae): tender_with_link.short_description = "Besoin d'achat (lien vers l'admin)" tender_with_link.admin_order_field = "tender" + def status(self, tendersiae): + return tendersiae.status + + status.short_description = "Status" + status.admin_order_field = "status" + def logs_display(self, tender=None): if tender: return pretty_print_readonly_jsonfield(tender.logs) diff --git a/lemarche/tenders/constants.py b/lemarche/tenders/constants.py index c9e43f79b..c41537dff 100644 --- a/lemarche/tenders/constants.py +++ b/lemarche/tenders/constants.py @@ -167,3 +167,16 @@ TENDER_SIAE_SOURCES_EXCEPT_IA = [ source[0] for source in TENDER_SIAE_SOURCE_CHOICES if source[0] != TENDER_SIAE_SOURCE_AI ] + +TENDER_SIAE_STATUS_EMAIL_SEND_DATE = "EMAIL_SEND_DATE" +TENDER_SIAE_STATUS_EMAIL_LINK_CLICK_DATE = "EMAIL_LINK_CLICK_DATE" +TENDER_SIAE_STATUS_DETAIL_DISPLAY_DATE = "DETAIL_DISPLAY_DATE" +TENDER_SIAE_STATUS_DETAIL_CONTACT_CLICK_DATE = "DETAIL_CONTACT_CLICK_DATE" +TENDER_SIAE_STATUS_DETAIL_NOT_INTERESTED_CLICK_DATE = "DETAIL_NOT_INTERESTED_CLICK_DATE" +TENDER_SIAE_STATUS_CHOICES = ( + (TENDER_SIAE_STATUS_EMAIL_SEND_DATE, "Contactée"), + (TENDER_SIAE_STATUS_EMAIL_LINK_CLICK_DATE, "Cliquée"), + (TENDER_SIAE_STATUS_DETAIL_DISPLAY_DATE, "Vue"), + (TENDER_SIAE_STATUS_DETAIL_CONTACT_CLICK_DATE, "Intéressée"), + (TENDER_SIAE_STATUS_DETAIL_NOT_INTERESTED_CLICK_DATE, "Pas intéressée"), +) diff --git a/lemarche/tenders/models.py b/lemarche/tenders/models.py index 6a4af88c4..dbc8771f7 100644 --- a/lemarche/tenders/models.py +++ b/lemarche/tenders/models.py @@ -893,6 +893,24 @@ def set_sent(self): self.save() +class TenderQuestion(models.Model): + text = models.TextField(verbose_name="Intitulé de la question", blank=False) + + tender = models.ForeignKey( + "tenders.Tender", verbose_name="Besoin d'achat", related_name="questions", on_delete=models.CASCADE + ) + + created_at = models.DateTimeField(verbose_name="Date de création", default=timezone.now) + updated_at = models.DateTimeField(verbose_name="Date de modification", auto_now=True) + + class Meta: + verbose_name = "Question de l'acheteur" + verbose_name_plural = "Questions de l'acheteur" + + def __str__(self): + return self.text + + class TenderSiaeQuerySet(models.QuerySet): def email_click_reminder(self, gte_days_ago, lt_days_ago): return ( @@ -908,6 +926,33 @@ def detail_contact_click_post_reminder(self, gte_days_ago, lt_days_ago): detail_contact_click_date__lt=lt_days_ago ) + def with_prefetch_related(self): + return self.prefetch_related("tender", "siae") + + def with_status(self): + return self.annotate( + status_annotated=Case( + When( + detail_not_interested_click_date__isnull=False, + then=Value(tender_constants.TENDER_SIAE_STATUS_DETAIL_NOT_INTERESTED_CLICK_DATE), + ), + When( + detail_contact_click_date__isnull=False, + then=Value(tender_constants.TENDER_SIAE_STATUS_DETAIL_CONTACT_CLICK_DATE), + ), + When( + detail_display_date__isnull=False, + then=Value(tender_constants.TENDER_SIAE_STATUS_DETAIL_DISPLAY_DATE), + ), + When( + email_link_click_date__isnull=False, + then=Value(tender_constants.TENDER_SIAE_STATUS_EMAIL_LINK_CLICK_DATE), + ), + When(email_send_date__isnull=False, then=Value(tender_constants.TENDER_SIAE_STATUS_EMAIL_SEND_DATE)), + default=None, + ) + ) + def unread_stats(self, user): limit_date = datetime.today() aggregates = { @@ -925,24 +970,6 @@ def unread_stats(self, user): ) -class TenderQuestion(models.Model): - text = models.TextField(verbose_name="Intitulé de la question", blank=False) - - tender = models.ForeignKey( - "tenders.Tender", verbose_name="Besoin d'achat", related_name="questions", on_delete=models.CASCADE - ) - - created_at = models.DateTimeField(verbose_name="Date de création", default=timezone.now) - updated_at = models.DateTimeField(verbose_name="Date de modification", auto_now=True) - - class Meta: - verbose_name = "Question de l'acheteur" - verbose_name_plural = "Questions de l'acheteur" - - def __str__(self): - return self.text - - class TenderSiae(models.Model): FIELDS_RELATION = [ "email_send_date", @@ -1018,6 +1045,19 @@ class Meta: verbose_name_plural = "Structures correspondantes au besoin" ordering = ["-created_at"] + @property + def status(self): + if self.detail_not_interested_click_date: + return "Pas intéressée" + if self.contact_click_date: + return "Intéressée" + if self.detail_display_date: + return "Vue" + if self.email_link_click_date: + return "Cliquée" + if self.email_send_date: + return "Contactée" + class PartnerShareTenderQuerySet(models.QuerySet): def is_active(self):