diff --git a/lemarche/templates/tenders/create_step_survey.html b/lemarche/templates/tenders/create_step_survey.html index 18493200e..63d575f7a 100644 --- a/lemarche/templates/tenders/create_step_survey.html +++ b/lemarche/templates/tenders/create_step_survey.html @@ -29,7 +29,7 @@
- {% bootstrap_field form.le_marche_doesnt_exist_how_to_find_siae form_group_class="form-group" %} + {% bootstrap_field form.le_marche_doesnt_exist_how_to_find_siae form_group_class="form-group mb-lg-5" %}
diff --git a/lemarche/tenders/admin.py b/lemarche/tenders/admin.py index 31472276b..50effb560 100644 --- a/lemarche/tenders/admin.py +++ b/lemarche/tenders/admin.py @@ -476,7 +476,7 @@ class TenderAdmin(FieldsetsInlineMixin, admin.ModelAdmin): ), ( "Utilité du marché de l'inclusion", - {"fields": ("scale_marche_useless",)}, + {"fields": ("scale_marche_useless", "le_marche_doesnt_exist_how_to_find_siae")}, ), ( "Status", diff --git a/lemarche/tenders/constants.py b/lemarche/tenders/constants.py index a8f1c3f5a..84768e9b1 100644 --- a/lemarche/tenders/constants.py +++ b/lemarche/tenders/constants.py @@ -167,19 +167,6 @@ (TENDER_SIAE_STATUS_DETAIL_NOT_INTERESTED_CLICK_DATE, TENDER_SIAE_STATUS_DETAIL_NOT_INTERESTED_CLICK_DATE_DISPLAY), ) - -SURVEY_SCALE_QUESTION_0 = "0" -SURVEY_SCALE_QUESTION_1 = "1" -SURVEY_SCALE_QUESTION_2 = "2" -SURVEY_SCALE_QUESTION_3 = "3" - -SURVEY_SCALE_QUESTION_CHOICES = ( - (SURVEY_SCALE_QUESTION_0, "Non"), - (SURVEY_SCALE_QUESTION_1, "Peu probablement"), - (SURVEY_SCALE_QUESTION_2, "Très probablement"), - (SURVEY_SCALE_QUESTION_3, "Oui"), -) - # survey choices SURVEY_YES_NO_DONT_KNOW_CHOICES = ( (constants.NO, "Non"), diff --git a/lemarche/tenders/enums.py b/lemarche/tenders/enums.py new file mode 100644 index 000000000..477eedd57 --- /dev/null +++ b/lemarche/tenders/enums.py @@ -0,0 +1,30 @@ +""" +Enums fields used in Tender models. +""" + +from django.db import models + + +class SurveyScaleQuestionChoices(models.TextChoices): + NON = "0", "Non" + PEU_PROBABLE = "1", "Peu probablement" + TRES_PROBABLE = "2", "Très probablement" + OUI = "3", "Oui" + + +class SurveyDoesNotExistQuestionChoices(models.TextChoices): + DONT_KNOW = "DK", "Je ne sais pas" + INTERNET_SEARCH = "IS", "Recherche sur Internet (Google, page jaune, recherche sur le web)" + NETWORKS = "NW", "Réseaux professionnels et partenariats" + DIRECTORY = "DI", "Annuaire spécialisé (GESAT, UNEA, Handeco)" + RECOMMENDATIONS = ( + "RC", + "Recommandations et bouche-à-oreille (Réseaux sociaux, recommandations personnelles, collègues)", + ) + KNOWN_PROVIDERS = "KP", "Prestataires connus et habituels (Fournisseurs actuels)" + PUBLIC_TENDERS = "PT", "Appel d'offres et consultations publiques (BOAMP, JOUE, AWS, appels d'offres)" + FACILITATORS = "FA", "Facilitateurs de clauses sociales" + LOCAL_SOURCING = ( + "LS", + "Sourcing local et salons professionnels (Recherche locale, salons, événements professionnels)", + ) diff --git a/lemarche/tenders/migrations/0089_tender_le_marche_doesnt_exist_how_to_find_siae.py b/lemarche/tenders/migrations/0089_tender_le_marche_doesnt_exist_how_to_find_siae.py new file mode 100644 index 000000000..389cc7e42 --- /dev/null +++ b/lemarche/tenders/migrations/0089_tender_le_marche_doesnt_exist_how_to_find_siae.py @@ -0,0 +1,45 @@ +# Generated by Django 4.2.13 on 2024-06-26 15:11 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("tenders", "0088_alter_tender_contact_phone"), + ] + + operations = [ + migrations.AddField( + model_name="tender", + name="le_marche_doesnt_exist_how_to_find_siae", + field=models.CharField( + choices=[ + ("DK", "Je ne sais pas"), + ("IS", "Recherche sur Internet (Google, page jaune, recherche sur le web)"), + ("NW", "Réseaux professionnels et partenariats"), + ("DI", "Annuaire spécialisé (GESAT, UNEA, Handeco)"), + ( + "RC", + ( + "Recommandations et bouche-à-oreille (Réseaux sociaux, " + "recommandations personnelles, collègues)" + ), + ), + ("KP", "Prestataires connus et habituels (Fournisseurs actuels)"), + ("PT", "Appel d'offres et consultations publiques (BOAMP, JOUE, AWS, appels d'offres)"), + ("FA", "Facilitateurs de clauses sociales"), + ( + "LS", + ( + "Sourcing local et salons professionnels (Recherche locale, " + "salons, événements professionnels)" + ), + ), + ], + default="DK", + help_text="Comment auriez-vous fait pour consulter des prestataires inclusifs ?", + max_length=2, + verbose_name="Sans le marché de l'inclusion", + ), + ), + ] diff --git a/lemarche/tenders/models.py b/lemarche/tenders/models.py index 2965c112c..fae6bd95d 100644 --- a/lemarche/tenders/models.py +++ b/lemarche/tenders/models.py @@ -32,6 +32,7 @@ from lemarche.siaes import constants as siae_constants from lemarche.siaes.models import Siae from lemarche.tenders import constants as tender_constants +from lemarche.tenders.enums import SurveyDoesNotExistQuestionChoices, SurveyScaleQuestionChoices from lemarche.tenders.utils import find_amount_ranges from lemarche.users.models import User from lemarche.utils.apis import api_elasticsearch @@ -528,8 +529,15 @@ class Tender(models.Model): verbose_name="Utilité du marché de l'inclusion", help_text="Si le Marché de l'inclusion n'existait pas, auriez-vous consulté des prestataires inclusifs* pour ce besoin ?", # noqa max_length=2, - choices=tender_constants.SURVEY_SCALE_QUESTION_CHOICES, - default=tender_constants.SURVEY_SCALE_QUESTION_0, + choices=SurveyScaleQuestionChoices.choices, + default=SurveyScaleQuestionChoices.NON, + ) + le_marche_doesnt_exist_how_to_find_siae = models.CharField( + verbose_name="Sans le marché de l'inclusion", + help_text="Comment auriez-vous fait pour consulter des prestataires inclusifs ?", + max_length=2, + choices=SurveyDoesNotExistQuestionChoices.choices, + default=SurveyDoesNotExistQuestionChoices.DONT_KNOW, ) marche_benefits = ChoiceArrayField( diff --git a/lemarche/www/pages/views.py b/lemarche/www/pages/views.py index 9c4edb61b..29f01a3cb 100644 --- a/lemarche/www/pages/views.py +++ b/lemarche/www/pages/views.py @@ -295,7 +295,6 @@ def csrf_failure(request, reason=""): # noqa C901 ) tender_published_at = None if request.POST.get("is_draft") else timezone.now() tender_dict = dict( - extra_data={}, status=tender_status, published_at=tender_published_at, source=tender_constants.SOURCE_FORM_CSRF, @@ -308,17 +307,13 @@ def csrf_failure(request, reason=""): # noqa C901 if not key.startswith(("csrfmiddlewaretoken", "tender_create_multi_step_view")): value = formtools_session_step_data.get(step).get(key) key_cleaned = key.replace(f"{step}-", "") - if key_cleaned == "le_marche_doesnt_exist_how_to_find_siae": - tender_dict["extra_data"] |= {key_cleaned: value[0]} - elif key_cleaned == "location": + if key_cleaned == "location": tender_dict[key_cleaned] = Perimeter.objects.get(slug=value[0]) - elif key_cleaned in [ "is_country_area", "accept_share_amount", ]: tender_dict[key_cleaned] = value[0] == "on" - elif key_cleaned == "sectors": tender_dict[key_cleaned] = Sector.objects.filter(slug__in=value) elif key_cleaned == "questions_list" and value: diff --git a/lemarche/www/tenders/forms.py b/lemarche/www/tenders/forms.py index e0afd9ba3..2effa8ded 100644 --- a/lemarche/www/tenders/forms.py +++ b/lemarche/www/tenders/forms.py @@ -5,6 +5,7 @@ from lemarche.sectors.models import Sector from lemarche.tenders import constants as tender_constants +from lemarche.tenders.enums import SurveyDoesNotExistQuestionChoices, SurveyScaleQuestionChoices from lemarche.tenders.models import Tender, TenderSiae from lemarche.users.models import User from lemarche.utils import constants @@ -257,15 +258,15 @@ def clean(self): class TenderCreateStepSurveyForm(forms.ModelForm): scale_marche_useless = forms.ChoiceField( label=Tender._meta.get_field("scale_marche_useless").help_text, - choices=tender_constants.SURVEY_SCALE_QUESTION_CHOICES, + choices=SurveyScaleQuestionChoices.choices, widget=forms.RadioSelect, required=True, ) - le_marche_doesnt_exist_how_to_find_siae = forms.CharField( - label="Si le Marché de l'inclusion n'existait pas, comment auriez-vous fait pour trouver un prestataire inclusif ?", # noqa + le_marche_doesnt_exist_how_to_find_siae = forms.ChoiceField( + label=Tender._meta.get_field("le_marche_doesnt_exist_how_to_find_siae").help_text, + choices=SurveyDoesNotExistQuestionChoices.choices, required=False, - widget=forms.Textarea(attrs={"rows": 2, "cols": 15, "data-expandable": "true"}), ) class Meta: @@ -276,23 +277,9 @@ class Meta: def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - if self.instance.id: - self.initial["le_marche_doesnt_exist_how_to_find_siae"] = self.instance.extra_data.get( - "le_marche_doesnt_exist_how_to_find_siae" - ) - else: + if not self.instance.id: self.initial["scale_marche_useless"] = None - def clean(self) -> dict[str, any]: - if not self.errors: - super_cleaned_data = super().clean() - if super_cleaned_data: - cleaned_data = { - "scale_marche_useless": super_cleaned_data.pop("scale_marche_useless"), - "extra_data": super_cleaned_data, - } - return cleaned_data - class TenderCreateStepConfirmationForm(forms.Form): pass diff --git a/lemarche/www/tenders/tests.py b/lemarche/www/tenders/tests.py index 29152d7e0..4805d7dac 100644 --- a/lemarche/www/tenders/tests.py +++ b/lemarche/www/tenders/tests.py @@ -17,6 +17,7 @@ from lemarche.siaes.factories import SiaeFactory from lemarche.siaes.models import Siae from lemarche.tenders import constants as tender_constants +from lemarche.tenders.enums import SurveyDoesNotExistQuestionChoices, SurveyScaleQuestionChoices from lemarche.tenders.factories import TenderFactory, TenderQuestionFactory from lemarche.tenders.models import Tender, TenderSiae, TenderStepsData from lemarche.users.factories import UserFactory @@ -67,8 +68,8 @@ def _generate_fake_data_form( } | _step_3 step_4 = { "tender_create_multi_step_view-current_step": "survey", - "survey-scale_marche_useless": tender_constants.SURVEY_SCALE_QUESTION_0, - "survey-le_marche_doesnt_exist_how_to_find_siae": "TEST", + "survey-scale_marche_useless": SurveyScaleQuestionChoices.NON, + "survey-le_marche_doesnt_exist_how_to_find_siae": SurveyDoesNotExistQuestionChoices.INTERNET_SEARCH, } | _step_4 step_5 = { @@ -134,6 +135,11 @@ def test_tender_wizard_form_all_good_authenticated(self): self.assertEqual(tender.contact_last_name, self.user_buyer.last_name) self.assertEqual(tender.contact_email, self.user_buyer.email) self.assertEqual(tender.contact_phone_display, self.user_buyer.phone_display) + self.assertEqual(tender.scale_marche_useless, tenders_step_data[3].get("survey-scale_marche_useless")) + self.assertEqual( + tender.le_marche_doesnt_exist_how_to_find_siae, + tenders_step_data[3].get("survey-le_marche_doesnt_exist_how_to_find_siae"), + ) def test_tender_wizard_form_not_created(self): self.client.force_login(self.user_buyer) @@ -1516,7 +1522,7 @@ def test_user_can_notify_cocontracting_wish_with_authenticated_user(self): self.assertContains(response, self.cta_message_success) mock_send_mail_async.assert_called_once() email_body = mock_send_mail_async.call_args[1]["email_body"] - self.assertTrue(f"La structure {self.siae.name } souhaite répondre en co-traitance" in email_body) + self.assertTrue(f"La structure {self.siae.name} souhaite répondre en co-traitance" in email_body) self.assertIsNotNone(tendersiae.detail_cocontracting_click_date) response = self.client.get(self.tender_detail_url) self.assertNotContains(response, self.cta_message) diff --git a/lemarche/www/tenders/views.py b/lemarche/www/tenders/views.py index 9557418ce..fa0d5acc0 100644 --- a/lemarche/www/tenders/views.py +++ b/lemarche/www/tenders/views.py @@ -196,25 +196,21 @@ def save_instance_tender(self, tender_dict: dict, form_dict: dict, is_draft: boo sectors = None for step, model_form in form_dict.items(): if model_form.has_changed(): - if step != self.STEP_SURVEY: - for attribute in model_form.changed_data: - match attribute: - case "sectors": - sectors = tender_dict.get("sectors", None) - self.instance.sectors.set(sectors) - case "location": - location = tender_dict.get("location") - self.instance.location = location - self.instance.perimeters.set([location]) - case "questions_list": - update_or_create_questions_list( - tender=self.instance, questions_list=tender_dict.get("questions_list") - ) - case _: - setattr(self.instance, attribute, tender_dict.get(attribute)) - elif step == self.STEP_SURVEY: - setattr(self.instance, "scale_marche_useless", tender_dict.get("scale_marche_useless")) - self.instance.extra_data.update(tender_dict.get("extra_data")) + for attribute in model_form.changed_data: + match attribute: + case "sectors": + sectors = tender_dict.get("sectors", None) + self.instance.sectors.set(sectors) + case "location": + location = tender_dict.get("location") + self.instance.location = location + self.instance.perimeters.set([location]) + case "questions_list": + update_or_create_questions_list( + tender=self.instance, questions_list=tender_dict.get("questions_list") + ) + case _: + setattr(self.instance, attribute, tender_dict.get(attribute)) self.instance.save() else: tender_dict |= {"status": tender_status, "published_at": tender_published_at}