diff --git a/lemarche/tenders/constants.py b/lemarche/tenders/constants.py index f405334ab..c9e43f79b 100644 --- a/lemarche/tenders/constants.py +++ b/lemarche/tenders/constants.py @@ -4,6 +4,7 @@ KIND_QUOTE_DISPLAY = "Demande de devis" KIND_PROJECT = "PROJ" KIND_PROJECT_DISPLAY = "Sourcing" +KIND_PROJECT_SIAE_DISPLAY = "Projet d'achat" KIND_BOAMP = "BOAMP" KIND_CHOICES = ( (KIND_TENDER, KIND_TENDER_DISPLAY), diff --git a/lemarche/tenders/models.py b/lemarche/tenders/models.py index fe79af028..6a4af88c4 100644 --- a/lemarche/tenders/models.py +++ b/lemarche/tenders/models.py @@ -908,6 +908,22 @@ def detail_contact_click_post_reminder(self, gte_days_ago, lt_days_ago): detail_contact_click_date__lt=lt_days_ago ) + def unread_stats(self, user): + limit_date = datetime.today() + aggregates = { + f"unread_count_{kind}_annotated": Count( + Case(When(tender__kind=kind, then=1), output_field=IntegerField()), distinct=True + ) + for kind, _ in tender_constants.KIND_CHOICES + } + return ( + self.filter( + siae__in=user.siaes.all(), tender__validated_at__isnull=False, tender__deadline_date__gt=limit_date + ) + .filter(detail_display_date__isnull=True) + .aggregate(**aggregates) + ) + class TenderQuestion(models.Model): text = models.TextField(verbose_name="Intitulé de la question", blank=False) diff --git a/lemarche/tenders/tests.py b/lemarche/tenders/tests.py index 5453ae288..7905d4d2c 100644 --- a/lemarche/tenders/tests.py +++ b/lemarche/tenders/tests.py @@ -786,7 +786,9 @@ def setUpTestData(cls): siae_with_tender_5 = SiaeFactory() cls.siae_without_tender = SiaeFactory() cls.tender_with_siae_1 = TenderFactory( - siaes=[cls.siae_with_tender_1, siae_with_tender_2], deadline_date=date_tomorrow + siaes=[cls.siae_with_tender_1, siae_with_tender_2], + deadline_date=date_tomorrow, + kind=tender_constants.KIND_TENDER, ) TenderSiae.objects.create( tender=cls.tender_with_siae_1, siae=siae_with_tender_3, email_send_date=date_last_week @@ -830,6 +832,12 @@ def test_detail_contact_click_post_reminder(self): 1, ) + def test_unread_stats(self): + stats = TenderSiae.objects.unread_stats(user=self.user_siae) + self.assertEqual(stats[f"unread_count_{tender_constants.KIND_TENDER}_annotated"], 1) + self.assertEqual(stats[f"unread_count_{tender_constants.KIND_QUOTE}_annotated"], 1) + self.assertEqual(stats[f"unread_count_{tender_constants.KIND_PROJECT}_annotated"], 0) + class TenderAdminTest(TestCase): def setUp(cls): diff --git a/lemarche/www/tenders/forms.py b/lemarche/www/tenders/forms.py index 2e9227291..4f4ef6424 100644 --- a/lemarche/www/tenders/forms.py +++ b/lemarche/www/tenders/forms.py @@ -347,7 +347,7 @@ class TenderFilterForm(forms.Form): ("", "Toutes les opportunités"), (tender_constants.KIND_QUOTE, tender_constants.KIND_QUOTE_DISPLAY), (tender_constants.KIND_TENDER, tender_constants.KIND_TENDER_DISPLAY), - (tender_constants.KIND_PROJECT, "Projets d'achats"), + (tender_constants.KIND_PROJECT, tender_constants.KIND_PROJECT_SIAE_DISPLAY), ) kind = forms.ChoiceField( @@ -361,3 +361,17 @@ class TenderFilterForm(forms.Form): ), required=False, ) + + def __init__(self, user, *args, **kwargs): + super().__init__(*args, **kwargs) + + stats = TenderSiae.objects.unread_stats(user=user) + new_choices = [] + for kind_key, kind_label in self.FORM_KIND_CHOICES: + count_key = f"unread_count_{kind_key}_annotated" + if count_key in stats and stats[count_key] > 0: + new_choices.append((kind_key, f"{kind_label} ({stats[count_key]})")) + else: + new_choices.append((kind_key, kind_label)) + + self.fields["kind"].choices = new_choices diff --git a/lemarche/www/tenders/tests.py b/lemarche/www/tenders/tests.py index 3d4bfef09..d240acc64 100644 --- a/lemarche/www/tenders/tests.py +++ b/lemarche/www/tenders/tests.py @@ -585,12 +585,29 @@ def test_siae_user_should_only_see_filtered_kind(self): response = self.client.get(url) self.assertEqual(response.status_code, 200) self.assertEqual(len(response.context["tenders"]), 2) + self.assertContains( + response, + f'', + 1, + html=True, + ) + self.assertContains( + response, + f'', + 1, + html=True, + ) url = reverse("tenders:list") response = self.client.get(f"{url}?kind={tender_constants.KIND_TENDER}") self.assertEqual(response.status_code, 200) self.assertEqual(len(response.context["tenders"]), 1) self.assertEqual(response.context["tenders"][0], self.tender_4) + expected_option = ( + f'" + ) + self.assertContains(response, expected_option, 1, html=True) class TenderDetailViewTest(TestCase): diff --git a/lemarche/www/tenders/views.py b/lemarche/www/tenders/views.py index e1f9bfec5..5d7a0953a 100644 --- a/lemarche/www/tenders/views.py +++ b/lemarche/www/tenders/views.py @@ -46,7 +46,6 @@ TITLE_DETAIL_PAGE_SIAE = "Trouver de nouvelles opportunités" TITLE_DETAIL_PAGE_OTHERS = "Mes besoins" -TITLE_KIND_SOURCING_SIAE = "Consultation en vue d'un achat" class TenderCreateMultiStepView(SessionWizardView): @@ -298,7 +297,7 @@ def get_queryset(self): if self.status: qs = qs.filter(status=self.status) - self.filter_form = TenderFilterForm(data=self.request.GET) + self.filter_form = TenderFilterForm(data=self.request.GET, user=self.request.user) if self.filter_form.is_valid(): kind = self.filter_form.cleaned_data.get("kind") if kind: @@ -323,7 +322,11 @@ def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) user_kind = self.request.user.kind if self.request.user.is_authenticated else "anonymous" context["page_title"] = TITLE_DETAIL_PAGE_SIAE if user_kind == User.KIND_SIAE else TITLE_DETAIL_PAGE_OTHERS - context["title_kind_sourcing_siae"] = TITLE_KIND_SOURCING_SIAE + context["title_kind_sourcing_siae"] = ( + tender_constants.KIND_PROJECT_SIAE_DISPLAY + if user_kind == User.KIND_SIAE + else tender_constants.KIND_PROJECT_DISPLAY + ) context["tender_constants"] = tender_constants context["filter_form"] = self.filter_form return context @@ -373,7 +376,7 @@ def get_context_data(self, **kwargs): context["is_admin"] = self.request.user.is_authenticated and self.request.user.is_admin context["parent_title"] = TITLE_DETAIL_PAGE_SIAE if user_kind == User.KIND_SIAE else TITLE_DETAIL_PAGE_OTHERS context["tender_kind_display"] = ( - TITLE_KIND_SOURCING_SIAE + tender_constants.KIND_PROJECT_SIAE_DISPLAY if user_kind == User.KIND_SIAE and self.object.kind == tender_constants.KIND_PROJECT else self.object.get_kind_display() )