Skip to content

Commit

Permalink
feat(Dépôt de besoins): Permettre aux structures de supprimer les dép…
Browse files Browse the repository at this point in the history
…ôts de besoins expirés (#1110)
  • Loading branch information
madjid-asa authored Mar 13, 2024
1 parent 14c2e05 commit 4404359
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 18 deletions.
29 changes: 21 additions & 8 deletions lemarche/templates/tenders/_list_item_siae.html
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
{% load static humanize %}

<div class="card c-card c-card--marche c-card--marche-{{ tender.kind|lower }}{% if tender.deadline_date_is_outdated_annotated %} c-card--marche-outdated{% endif %} c-card--link siae-card" role="button" data-url="{% url 'tenders:detail' tender.slug %}">
<div id="card-{{tender.slug}}" class="card c-card c-card--marche-{{ tender.kind|lower }}{% if tender.deadline_date_is_outdated_annotated %} c-card--marche-outdated{% endif %} mb-2">
<div class="card-body">
<div class="row">
<div class="col-md-12">
<div class="col-md-9">
<p class="mb-1">
{% if tender.deadline_date_is_outdated_annotated %}
<span class="badge badge-sm badge-base badge-pill badge-nuance-08">Clôturé le {{ tender.deadline_date|default:"" }}</span>
Expand All @@ -16,15 +15,29 @@
{% endif %}
</span>
Disponible <strong>jusqu'au : {{ tender.deadline_date|default:"" }}</strong>
{% if not tender.tendersiae_set.first.detail_display_date %}
<span class="float-right badge badge-sm badge-pill badge-new">Nouveau</span>
{% endif %}

{% endif %}
</p>
</div>
<div class="col-md-3">
{% if tender.is_new_for_siaes %}
<span class="float-right badge badge-sm badge-pill badge-new">Nouveau</span>
{% elif tender.deadline_date_is_outdated_annotated %}
<form action="{% url 'tenders:hide-tender-siae' tender.slug %}"
method="post" hx-post="{% url 'tenders:hide-tender-siae' tender.slug %}"
hx-target="#card-{{tender.slug}}" hx-swap="outerHTML"
>
{% csrf_token %}
<button type="submit" class="btn btn-light btn-ico btn-sm float-right" aria-label="Supprimer">
<i class="ri-delete-bin-2-line"></i>
</button>
</form>
{% endif %}
</div>
</div>

<h2 class="py-2{% if tender.deadline_date_is_outdated_annotated %} text-nuance-03{% endif %}">{{ tender.title }}</h2>
<a href="{% url 'tenders:detail' tender.slug %}">
<h2 class="py-2{% if tender.deadline_date_is_outdated_annotated %} text-nuance-03{% endif %}">{{ tender.title }}</h2>
</a>

<hr />

Expand Down
17 changes: 17 additions & 0 deletions lemarche/tenders/migrations/0078_tendersiae_is_deleted_by_siae.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Generated by Django 4.2.9 on 2024-02-22 13:01

from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("tenders", "0077_alter_tender_siae_kind"),
]

operations = [
migrations.AddField(
model_name="tendersiae",
name="is_deleted_by_siae",
field=models.BooleanField(db_index=True, default=False, verbose_name="Supprimé par l'utilisateur ?"),
),
]
39 changes: 37 additions & 2 deletions lemarche/tenders/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,20 @@
from django.conf import settings
from django.contrib.contenttypes.fields import GenericRelation
from django.db import IntegrityError, models, transaction
from django.db.models import BooleanField, Case, Count, ExpressionWrapper, F, IntegerField, Q, Sum, Value, When
from django.db.models import (
BooleanField,
Case,
Count,
Exists,
ExpressionWrapper,
F,
IntegerField,
OuterRef,
Q,
Sum,
Value,
When,
)
from django.db.models.functions import Greatest
from django.urls import reverse
from django.utils import timezone
Expand Down Expand Up @@ -141,13 +154,34 @@ def in_sectors(self, sectors):
else:
return self

def with_is_new_for_siaes(self, siaes, limit_date=None):
# Set limit_date to today if not specified.
if limit_date is None:
limit_date = datetime.today()

# Subquery to find TenderSiae instances linked to the Tender, part of the specified Siae,
# and without a detailed display date.
tender_siae_subquery = TenderSiae.objects.filter(
tender=OuterRef("pk"),
siae__in=siaes,
detail_display_date__isnull=True,
)

# Annotates each Tender with a boolean 'is_new_for_siaes'. A Tender is considered new for the Siae
# if there's at least one associated TenderSiae (meeting subquery criteria) with a deadline_date
# beyond the limit_date. This implies the Tender was introduced after the limit_date, marking it as new.
return self.annotate(
is_new_for_siaes=Exists(tender_siae_subquery.filter(tender__deadline_date__gt=limit_date))
)

def filter_with_siaes(self, siaes):
"""
Return the list of tenders corresponding to the list of
- we return only sent tenders
- the tender-siae matching has already been done with filter_with_tender()
- with annotation to new if it's new for siaes
"""
return self.sent().filter(tendersiae__siae__in=siaes).distinct()
return self.sent().filter(tendersiae__is_deleted_by_siae=False, tendersiae__siae__in=siaes).distinct()

def with_deadline_date_is_outdated(self, limit_date=datetime.today()):
return self.annotate(
Expand Down Expand Up @@ -1046,6 +1080,7 @@ class TenderSiae(models.Model):
default=tender_constants.TENDER_SIAE_SOURCE_EMAIL,
)
found_with_ai = models.BooleanField("Trouvé par l'IA", default=False)
is_deleted_by_siae = models.BooleanField("Supprimé par l'utilisateur ?", default=False, db_index=True)

# stats: relation
email_send_date = models.DateTimeField("Date d'envoi de l'e-mail", blank=True, null=True)
Expand Down
6 changes: 6 additions & 0 deletions lemarche/www/tenders/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
TenderDetailSurveyTransactionedView,
TenderDetailView,
TenderListView,
TenderSiaeHideView,
TenderSiaeListView,
)

Expand Down Expand Up @@ -45,6 +46,11 @@
TenderDetailNotInterestedClickView.as_view(),
name="detail-not-interested-click",
),
path(
"<str:slug>/cacher-depot-de-besoin",
TenderSiaeHideView.as_view(),
name="hide-tender-siae",
),
path(
"<str:slug>/sondage-transaction",
TenderDetailSurveyTransactionedView.as_view(),
Expand Down
28 changes: 20 additions & 8 deletions lemarche/www/tenders/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,12 @@
from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin
from django.core.paginator import Paginator
from django.db.models import Prefetch
from django.http import HttpResponseForbidden, HttpResponseRedirect
from django.http import HttpResponse, HttpResponseForbidden, HttpResponseRedirect
from django.shortcuts import get_object_or_404, redirect
from django.urls import reverse_lazy
from django.utils import timezone
from django.utils.safestring import mark_safe
from django.views.generic import DetailView, ListView, UpdateView
from django.views.generic import DetailView, ListView, UpdateView, View
from django.views.generic.edit import FormMixin
from formtools.wizard.views import SessionWizardView

Expand Down Expand Up @@ -276,6 +275,7 @@ class TenderListView(LoginRequiredMixin, ListView):
paginate_by = 10
paginator_class = Paginator
status = None
siae: Siae = None

def get_queryset(self):
"""
Expand All @@ -287,11 +287,8 @@ def get_queryset(self):
if user.kind == User.KIND_SIAE and user.siaes:
siaes = user.siaes.all()
if siaes:
# filtered prefetch to get detail_display_date on tendersiae_set related to user's siaes
tendersiae_qs = TenderSiae.objects.filter(siae__in=siaes)
qs = Tender.objects.filter_with_siaes(siaes).prefetch_related(
Prefetch("tendersiae_set", queryset=tendersiae_qs)
)
# we get the first siae by default
qs = Tender.objects.filter_with_siaes(siaes).with_is_new_for_siaes(siaes)
else:
qs = Tender.objects.by_user(user).with_siae_stats()
if self.status:
Expand Down Expand Up @@ -738,3 +735,18 @@ def get_success_message(self, already_answered=False):
if already_answered:
return "Votre réponse a déjà été prise en compte."
return "Merci pour votre réponse !"


class TenderSiaeHideView(LoginRequiredMixin, View):
def post(self, request, *args, **kwargs):
tender = get_object_or_404(Tender, slug=kwargs["slug"])
user = self.request.user
if user.kind == User.KIND_SIAE:
tender.tendersiae_set.filter(siae__in=user.siaes.all()).update(is_deleted_by_siae=True)
if request.htmx:
# status code 204 doesn't work with htmx
return HttpResponse("", status=200)
return HttpResponseRedirect(reverse_lazy("home"))
else:
# if the user is not SIAE kind, the post is not allowed
return HttpResponse(status=401)

0 comments on commit 4404359

Please sign in to comment.