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()
)