Skip to content

Commit

Permalink
final commit
Browse files Browse the repository at this point in the history
  • Loading branch information
chloend committed Jan 16, 2025
1 parent 302ba09 commit 9a3db2b
Show file tree
Hide file tree
Showing 16 changed files with 108 additions and 254 deletions.
21 changes: 0 additions & 21 deletions lemarche/api/tenders/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,26 +197,6 @@ def test_create_contact_call_has_user_buyer_attributes(self, mock_create_contact
if sectors.exists():
attributes["TYPE_VERTICALE_ACHETEUR"] = sectors.first().name

def test_add_log_entry_method(self):
"""Test 'add_log_entry' method to check tender logs"""
extra_data = {"source": "TALLY"}
_, tender, _ = self.setup_mock_user_and_tender_creation(
title="Test tally", user=self.user_buyer, extra_data=extra_data
)
tender.add_log_entry("PUBLISHED")
tender.save()

self.assertEqual(tender.status, tender_constants.STATUS_PUBLISHED)
self.assertEqual(len(tender.logs), 1)

tender.status = tender_constants.STATUS_DRAFT
tender.save()
tender.status = tender_constants.STATUS_PUBLISHED
tender.add_log_entry("PUBLISHED")
tender.save()

self.assertEqual(len(tender.logs), 2)

def test_reset_modification_request(self):
"""Test 'reset_modification_request' method to check tender fields updates"""
extra_data = {"source": "TALLY"}
Expand All @@ -228,7 +208,6 @@ def test_reset_modification_request(self):

self.assertEqual(tender.status, tender_constants.STATUS_PUBLISHED)
self.assertEqual(tender.email_sent_for_modification, False)
self.assertEqual(tender.changes_information, "")

def test_create_tender_with_different_contact_data(self):
tender_data = TENDER_JSON.copy()
Expand Down
1 change: 0 additions & 1 deletion lemarche/api/tenders/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,6 @@ def perform_create(self, serializer: TenderSerializer):
)
# Check before adding logs or resetting modification request
if tender.status == tender_constants.STATUS_PUBLISHED:
tender.add_log_entry("PUBLISHED")
tender.reset_modification_request()

add_to_contact_list(user=user, type="signup", source=user_source, tender=tender)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ def create_template(apps, schema_editor):

def delete_template(apps, schema_editor):
TemplateTransactional = apps.get_model("conversations", "TemplateTransactional")
TemplateTransactional.objects.get(code="TENDERS_AUTHOR_MODIFICATION_REQUEST").delete()
TemplateTransactional.objects.get(code="TENDERS_AUTHOR_REJECT_MESSAGE").delete()
TemplateTransactional.objects.filter(code="TENDERS_AUTHOR_MODIFICATION_REQUEST").delete()
TemplateTransactional.objects.filter(code="TENDERS_AUTHOR_REJECT_MESSAGE").delete()


class Migration(migrations.Migration):
Expand Down
5 changes: 3 additions & 2 deletions lemarche/templates/tenders/admin_change_form.html
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,10 @@
{% block after_related_objects %}
{{ block.super }}
{% if original %}
{% if not original.validated_at %}
{% if not original.validated_at and original.status == "PUBLISHED"%}
<div class="submit-row">
<input type="submit" class="button" name="_calculate_tender" value="Sauvegarder et chercher les structures correspondantes" />
<input type="submit" name="_send_modification_request" value="Envoyer une demande de modification"/>
</div>
{% endif %}
<div class="submit-row" style="display:block">
Expand All @@ -54,7 +55,7 @@
<p><i>Date idéale de début des prestations dépassée ({{ original.start_working_date }})</i></p>
{% endif %}
{% endif %}
{% else %}
{% elif original.status == "PUBLISHED" %}
<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" 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 }}"/>
Expand Down
66 changes: 22 additions & 44 deletions lemarche/tenders/admin.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import logging

from ckeditor.widgets import CKEditorWidget
from django import forms
from django.contrib import admin
Expand Down Expand Up @@ -31,6 +33,9 @@
)


logger = logging.getLogger(__name__)


class KindFilter(MultiChoice):
FILTER_LABEL = Tender._meta.get_field("kind").verbose_name
BUTTON_LABEL = "Appliquer"
Expand Down Expand Up @@ -282,8 +287,6 @@ def clean(self):
"""
cleaned_data = super().clean()
distance_location = cleaned_data.get("distance_location")
email_sent_for_modification = cleaned_data.get("email_sent_for_modification")
changes_information = cleaned_data.get("changes_information")

if distance_location:
location = cleaned_data.get("location")
Expand All @@ -296,11 +299,6 @@ def clean(self):
{"location": "Le champ 'Distance en km' est spécifié, ce champ doit être une ville"}
)

if not email_sent_for_modification and changes_information:
raise forms.ValidationError(
{"changes_information": "Vous devez cocher la case 'Modifications requises' pour remplir ce champ"}
)


@admin.register(Tender, site=admin_site)
class TenderAdmin(FieldsetsInlineMixin, admin.ModelAdmin):
Expand All @@ -314,7 +312,7 @@ class TenderAdmin(FieldsetsInlineMixin, admin.ModelAdmin):
"start_working_date_in_list",
"siae_count_annotated_with_link_in_list",
"siae_detail_contact_click_count_annotated_with_link_in_list",
"email_sent_for_modification",
"status",
"is_validated_or_sent",
"is_followed_by_us",
]
Expand Down Expand Up @@ -525,8 +523,6 @@ class TenderAdmin(FieldsetsInlineMixin, admin.ModelAdmin):
{
"fields": (
"admins",
"email_sent_for_modification",
"changes_information",
"is_followed_by_us",
"proj_resulted_in_reserved_tender",
"is_reserved_tender",
Expand Down Expand Up @@ -560,28 +556,22 @@ class Media:

def handle_email_sent_for_modification(self, request, obj):
"""
Send an email to the author of the tender to request a modification, if it hasn't already been done.
Update the status of the tender to STATUS_DRAFT and the email_sent_for_modification field.
Redirect to the page for modifying the tender.
Send an email to the author and set some fields with 'set_modification_request'
Display an error message if the email can't be sent
"""
if obj.email_sent_for_modification:
try:
send_tender_author_modification_request(tender=obj)
self.message_user(request, "Une demande de modification a été envoyée à l'auteur du besoin")
obj.email_sent_for_modification = True
obj.status = tender_constants.STATUS_DRAFT
obj.add_log_entry("SEND_TENDER_AUTHOR_MODIFICATION_REQUEST")
obj.save(update_fields=["email_sent_for_modification", "status", "logs"])
except Exception as e:
# Force the email_sent_for_modification field to False
obj.email_sent_for_modification = False
obj.save(update_fields=["email_sent_for_modification"])
self.message_user(
request,
f"Erreur lors de l'envoi de la demande de modification : {str(e)}",
level="error",
)
return HttpResponseRedirect(".")
try:
send_tender_author_modification_request(tender=obj)
obj.set_modification_request()
self.message_user(request, "Une demande de modification a été envoyée à l'auteur du besoin")
except Exception as e:
self.message_user(
request,
"Erreur lors de l'envoi de la demande de modification : veuillez contacter le support.",
level="error",
)
logger.error(f"Exception when sending mail {e}")
finally:
return HttpResponseRedirect(".")

def handle_rejected_status(self, request, obj):
"""
Expand Down Expand Up @@ -624,27 +614,15 @@ def get_readonly_fields(self, request, obj=None):
# slug cannot be changed once the tender is validated
if obj.status == tender_constants.STATUS_VALIDATED:
readonly_fields.append("slug")
# cannot edit 'email_sent_for_modification' while tender is not published
if obj and obj.email_sent_for_modification and obj.status != Tender.STATUS_PUBLISHED:
readonly_fields.append("email_sent_for_modification")
return readonly_fields

def save_model(self, request, obj: Tender, form, change):
"""
Set Tender author on create
Set 'email_sent_for_modification' and 'changes_information' on update
"""
if not obj.id and not obj.author_id:
obj.author = request.user

if change and obj.email_sent_for_modification:
fields_to_update = ["email_sent_for_modification"]
if obj.changes_information:
fields_to_update.append("changes_information")
obj.save(update_fields=fields_to_update)

obj.save()

def save_formset(self, request, form, formset, change):
"""
Set Note author on create
Expand Down Expand Up @@ -862,7 +840,7 @@ def response_change(self, request, obj: Tender):
# we don't need to send it in the crm, parteners manage them
self.message_user(request, "Ce dépôt de besoin a été validé. Il sera envoyé aux partenaires :)")
return HttpResponseRedirect(".")
if obj.email_sent_for_modification and obj.status == tender_constants.STATUS_PUBLISHED:
if request.POST.get("_send_modification_request"):
return self.handle_email_sent_for_modification(request, obj)
if obj.status == tender_constants.STATUS_REJECTED:
return self.handle_rejected_status(request, obj)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@ def handle(self, *args, **options):
for tender in tenders_draft:
email_sent_at = None
for log_entry in tender.logs:
if log_entry.get("action") == "send_tender_author_modification_request":
email_sent_at = log_entry.get("email_sent_at")
if log_entry.get("action") == "send tender author modification request":
email_sent_at = log_entry.get("date")
break

if email_sent_at:
email_sent_at_date = timezone.now().strptime(email_sent_at, "%Y-%m-%dT%H:%M:%S.%f%z")
email_sent_at_date = timezone.datetime.fromisoformat(email_sent_at)
if email_sent_at_date <= threshold_date:
tenders_to_update.append(tender)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,14 @@
# Generated by Django 4.2.15 on 2024-12-26 18:02
# Generated by Django 4.2.15 on 2025-01-16 04:01

from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("tenders", "0094_add_templatetransactional_tender_author_commercial_partners"),
("tenders", "0095_remove_tender_accept_cocontracting_and_more"),
]

operations = [
migrations.AddField(
model_name="tender",
name="changes_information",
field=models.TextField(
blank=True, verbose_name="Informations complémentaires sur les modifications requises"
),
),
migrations.AddField(
model_name="tender",
name="email_sent_for_modification",
Expand Down
35 changes: 14 additions & 21 deletions lemarche/tenders/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -616,7 +616,6 @@ class Tender(models.Model):
email_sent_for_modification = models.BooleanField(
"Modifications requises", help_text="Envoyer un e-mail pour demander des modifications", default=False
)
changes_information = models.TextField("Informations complémentaires sur les modifications requises", blank=True)
# Admin specific for proj
proj_resulted_in_reserved_tender = models.BooleanField(
"Abouti à un appel d’offre (uniquement sourcing)", null=True
Expand Down Expand Up @@ -712,35 +711,29 @@ def __init__(self, *args, **kwargs):
for field_name in self.TRACK_UPDATE_FIELDS:
setattr(self, f"__previous_{field_name}", getattr(self, field_name))

def add_log_entry(self, action, details=None):
"""
Add an entry to the log list.
Args:
action (str): The action that was performed (e.g. status change, email sent, etc.)
details (dict, optional): Additional details about the action
"""
log_entry = {
"action": action,
"timestamp": timezone.now().isoformat(),
}
if details:
log_entry["details"] = details

self.logs.append(log_entry)

def reset_modification_request(self):
"""
Reset modification request when republishing a tender.
This method can only be called on Tender updates if status is changed to published
"""
if self.status == self.STATUS_PUBLISHED and self.email_sent_for_modification:
if self.changes_information:
self.changes_information = ""
self.save(update_fields=["changes_information"])
self.email_sent_for_modification = False
self.save(update_fields=["email_sent_for_modification"])

def set_modification_request(self):
"""
Set modification request when republishing a tender.
This method can only be called on Tender updates if status is changed to published
"""
self.email_sent_for_modification = True
self.status = tender_constants.STATUS_DRAFT
log_item = {
"action": "send tender author modification request",
"date": timezone.now().isoformat(),
}
self.logs.append(log_item)
self.save(update_fields=["email_sent_for_modification", "status", "logs"])

def set_slug(self, with_uuid=False):
"""
The slug field should be unique.
Expand Down
4 changes: 2 additions & 2 deletions lemarche/tenders/tests/test_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,14 +197,14 @@ def test_update_tender_status_to_rejected(self):
tender_recent = TenderFactory(
status=tender_constants.STATUS_DRAFT,
logs=[
{"action": "send_tender_author_modification_request", "email_sent_at": recent_date.isoformat()},
{"action": "send tender author modification request", "date": recent_date.isoformat()},
],
)

tender_expired = TenderFactory(
status=tender_constants.STATUS_DRAFT,
logs=[
{"action": "send_tender_author_modification_request", "email_sent_at": threshold_date.isoformat()},
{"action": "send tender author modification request", "date": threshold_date.isoformat()},
],
)

Expand Down
Loading

0 comments on commit 9a3db2b

Please sign in to comment.