Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Ajout d'un attribut pour filtrer l'envoi des besoins d'achat #1446

Merged
merged 15 commits into from
Oct 7, 2024
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions lemarche/static/itou_marche/admin.css
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,48 @@
div.custom-checkbox-select-multiple > div > label {
width: 100%; /* 160px default ; to avoid line breaks */
}

/* Modal style for tender change admin */
.tender-send-modal-container {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
z-index: 1000;
}

.tender-send-modal-content {
position: absolute;
top: 45%;
left: 50%;
transform: translate(-50%, -50%);
background-color: #212121;
margin: auto;
max-width: 500px;
width: 90%;
padding: 20px;
border-radius: 4px;
}

#submit-tender {
margin-bottom: 0;
}

#submit-button.submit-button {
background-color: #ba2121;
}

#submit-button.submit-button:hover {
background-color: #a41515;
}

#cancel-button {
margin-left: auto;
padding: 10px 15px;
}

#cancel-button:hover {
padding: 10px 15px;
}
55 changes: 55 additions & 0 deletions lemarche/static/js/admin_tender_confirmation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
document.addEventListener('DOMContentLoaded', function () {
const modal = document.getElementById('tender-send-confirmation-modal');
const confirmBtn = document.getElementById('submit-button');
const cancelBtn = document.getElementById('cancel-button');

function openModal(recipient, title) {
const messageElement = document.getElementById('tender-send-message');

// Set an attribute 'name' depending on the recipient
if (recipient === 'siaes') {
messageElement.textContent = "Le besoin « " + title + " » sera envoyé aux structures.";
confirmBtn.setAttribute('name', '_validate_send_to_siaes');
} else if (recipient === 'partners') {
messageElement.textContent = "Le besoin « " + title + " » sera envoyé aux partenaires.";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

J'aurais peut-être précisé ici: "Le besoin '[titre]' sera envoyé uniquement aux prestataires commerciaux.". Qu'en penses tu ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Merci pour ta remarque @SebastienReuiller, effectivement ce serait plus clair comme ça !

confirmBtn.setAttribute('name', '_validate_send_to_commercial_partners');
}
modal.style.display = 'block';
}

// data-recipent attribute determines the recipient of the tender
const buttons = document.querySelectorAll('input[data-recipient]');
buttons.forEach(function(button) {
button.addEventListener('click', function(e) {
e.preventDefault(); // Prevent instant form submission
const recipient = button.getAttribute('data-recipient');
const title = button.getAttribute('data-title');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const recipient = button.getAttribute('data-recipient');
const title = button.getAttribute('data-title');
const recipient = button.dataset.recipient;
const title = button.dataset.title;

Tu utilise généralement les getAttribute pour les attributs non-standards, mais pour les data-* tu peux utiliser dataset

openModal(recipient, title);
});
});

function closeModal() {
modal.style.display = 'none';
}

// Two different ways to close the modal:
// click on the cancel button
cancelBtn.addEventListener('click', function(e) {
e.preventDefault(); // Prevent page from scrolling up
closeModal();
});

// click outside the modal
window.addEventListener('click', function (e) {
if (e.target === modal) {
closeModal();
}
});

// Action confirmation
confirmBtn.addEventListener('click', function () {
if (formToSubmit) {
formToSubmit.submit();
}
});
});
15 changes: 14 additions & 1 deletion lemarche/templates/tenders/admin_change_form.html
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,21 @@
{% endif %}
{% else %}
<p><i>L'envoi des besoins 'validés' se fait toutes les 5 minutes, du Lundi au Vendredi, entre 9h et 17h</i></p>
<input type="submit" name="_validate_tender" value="Valider (sauvegarder) et envoyer aux structures 🚀" />
<input type="submit" value="Valider (sauvegarder) et envoyer aux structures 🚀" data-recipient="siaes" data-title="{{ original.title }}" style="margin-right: 5px"/>
<input type="submit" value="Valider (sauvegarder) et envoyer aux partenaires 🚀" data-recipient="partners" data-title="{{ original.title }}"/>
{% endif %}
</div>
{% endif %}

<!-- Modale de confirmation pour l'envoi d'un besoin d'achat -->
<div id="tender-send-confirmation-modal" class="tender-send-modal-container" style="display:none">
<div id="tender-send-modal-content" class="tender-send-modal-content">
<h1>Êtes-vous sûr ?</h1>
<p id="tender-send-message"></p>
<div id="submit-tender" class="submit-row">
<input type="submit" id="submit-button" class="submit-button" value="Confirmer"/>
<a id="cancel-button" class="button" href="#">Annuler</a>
</div>
</div>
</div>
{% endblock %}
16 changes: 15 additions & 1 deletion lemarche/tenders/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -526,6 +526,9 @@ class TenderAdmin(FieldsetsInlineMixin, admin.ModelAdmin):

change_form_template = "tenders/admin_change_form.html"

class Media:
js = ["/static/js/admin_tender_confirmation.js"]

def get_queryset(self, request):
qs = super().get_queryset(request)
qs = qs.select_related("author")
Expand Down Expand Up @@ -783,14 +786,25 @@ def response_change(self, request, obj: Tender):
obj.set_siae_found_list()
self.message_user(request, "Les structures concernées ont été mises à jour.")
return HttpResponseRedirect("./#structures") # redirect to structures sections
if request.POST.get("_validate_tender"):
if request.POST.get("_validate_send_to_siaes"):
obj.set_validated()
if obj.amount_int > settings.BREVO_TENDERS_MIN_AMOUNT_TO_SEND:
api_brevo.create_deal(tender=obj, owner_email=request.user.email)
# we link deal(tender) with author contact
api_brevo.link_deal_with_contact_list(tender=obj)
self.message_user(request, "Ce dépôt de besoin a été synchronisé avec Brevo")
self.message_user(request, "Ce dépôt de besoin a été validé. Il sera envoyé en temps voulu :)")
return HttpResponseRedirect(".")
if request.POST.get("_validate_send_to_commercial_partners"):
obj.set_validated()
obj.send_to_commercial_partners_only = True
if obj.amount_int > settings.BREVO_TENDERS_MIN_AMOUNT_TO_SEND:
api_brevo.create_deal(tender=obj, owner_email=request.user.email)
# we link deal(tender) with author contact
api_brevo.link_deal_with_contact_list(tender=obj)
self.message_user(request, "Ce dépôt de besoin a été synchronisé avec Brevo")
self.message_user(request, "Ce dépôt de besoin a été validé. Il sera envoyé en temps voulu :)")
obj.save()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pas besoin du .save() le set_validated le fait déjà, mais du coup tu peux remonter obj.send_to_commercial_partners_only = True pour le mettre avant le set_validated

return HttpResponseRedirect(".")
elif request.POST.get("_restart_tender"):
restart_send_tender_task(tender=obj)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Generated by Django 4.2.15 on 2024-09-27 12:50

from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("tenders", "0091_tender_is_followed_by_us_tender_is_reserved_tender_and_more"),
]

operations = [
migrations.AddField(
model_name="tender",
name="send_to_commercial_partners_only",
field=models.BooleanField(default=False, verbose_name="Envoyer uniquement aux partenaires externes"),
),
]
4 changes: 4 additions & 0 deletions lemarche/tenders/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ def validated_sent_batch(self):
& Q(siae_detail_contact_click_count_annotated__lte=F("limit_nb_siae_interested"))
& ~Q(siae_count_annotated=F("siae_email_send_count_annotated"))
& Q(last_sent_at__lt=yesterday)
& Q(send_to_commercial_partners_only=False)
)
)

Expand Down Expand Up @@ -632,6 +633,9 @@ class Tender(models.Model):
) # could become foreign key
# Admin specific for tenders
is_reserved_tender = models.BooleanField("Appel d'offre reservé", null=True)
send_to_commercial_partners_only = models.BooleanField(
"Envoyer uniquement aux partenaires externes", default=False
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"Envoyer uniquement aux partenaires externes", default=False
"Envoyer uniquement aux partenaires commerciaux", default=False

Préciser commerciaux pour ne pas confondre avec les partenaires externes (types facilitateurs ou autre)

)

# partner data
partner_approch_id = models.IntegerField("Partenaire APProch : ID", blank=True, null=True)
Expand Down
29 changes: 27 additions & 2 deletions lemarche/tenders/tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,7 @@ def test_validated_sent_batch(self):
validated_at=None,
first_sent_at=None,
last_sent_at=None,
send_to_commercial_partners_only=False,
)
TenderFactory(
siaes=[siae],
Expand All @@ -349,6 +350,7 @@ def test_validated_sent_batch(self):
validated_at=timezone.now(),
first_sent_at=None,
last_sent_at=None,
send_to_commercial_partners_only=False,
)
TenderFactory(
siaes=[siae],
Expand All @@ -357,6 +359,7 @@ def test_validated_sent_batch(self):
validated_at=timezone.now(),
first_sent_at=timezone.now(),
last_sent_at=timezone.now(),
send_to_commercial_partners_only=False,
)
TenderFactory(
siaes=[siae],
Expand All @@ -365,16 +368,19 @@ def test_validated_sent_batch(self):
validated_at=one_hour_ago,
first_sent_at=one_hour_ago,
last_sent_at=one_hour_ago,
send_to_commercial_partners_only=False,
)
# This tender would be sent if send_to_commercial_partners_only was False
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,
send_to_commercial_partners_only=True,
)
self.assertEqual(Tender.objects.validated_sent_batch().count(), 1)
self.assertEqual(Tender.objects.validated_sent_batch().count(), 0)

def test_is_not_outdated(self):
TenderFactory(deadline_date=None)
Expand Down Expand Up @@ -1085,17 +1091,36 @@ def test_edit_form_no_matching_on_validate_submission(self):
self.form_data
| {
"title": "New title",
"_validate_tender": "Valider (sauvegarder) et envoyer aux structures",
"_validate_send_to_siaes": "Valider (sauvegarder) et envoyer aux structures",
},
follow=True,
)
tender_response = response.context_data["adminform"].form.instance
self.assertEqual(tender_response.id, self.tender.id)
self.assertContains(response, "Validé le ")
self.assertFalse(tender_response.send_to_commercial_partners_only)
self.assertTrue(hasattr(tender_response, "siae_count_annotated"))
self.assertEqual(tender_response.siae_count_annotated, 1)
self.assertEqual(tender_response.siae_count_annotated, self.tender.tendersiae_set.count())

def test_edit_form_validate_submission_to_commercial_partners(self):
self.client.force_login(self.user)
tender_update_post_url = get_admin_change_view_url(self.tender)

response = self.client.post(
tender_update_post_url,
self.form_data
| {
"title": "New title",
"_validate_send_to_commercial_partners": "Valider (sauvegarder) et envoyer aux partenaires.",
},
follow=True,
)
tender_response = response.context_data["adminform"].form.instance
self.assertEqual(tender_response.id, self.tender.id)
self.assertContains(response, "Validé le ")
self.assertTrue(tender_response.send_to_commercial_partners_only)


class TenderUtilsTest(TestCase):
@classmethod
Expand Down
Loading