Skip to content

Commit

Permalink
Page validation habilitation : modale back
Browse files Browse the repository at this point in the history
  • Loading branch information
christophehenry committed Oct 30, 2024
1 parent 5df4e11 commit da37e0d
Show file tree
Hide file tree
Showing 17 changed files with 406 additions and 53 deletions.
5 changes: 5 additions & 0 deletions aidants_connect/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@
path("", include("aidants_connect_web.urls")),
path("habilitation/", include("aidants_connect_habilitation.urls")),
path("", include("aidants_connect_pico_cms.urls")),
# APIs
path(
"api/habilitation/",
include("aidants_connect_habilitation.api.urls"),
),
]

if "test" in sys.argv:
Expand Down
47 changes: 43 additions & 4 deletions aidants_connect_common/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,14 @@
from django.db.models import Choices, IntegerChoices, TextChoices
from django.db.models.enums import ChoicesMeta as DjangoChoicesMeta
from django.utils.functional import Promise, classproperty
from django.utils.safestring import mark_safe
from django.utils.timezone import now
from django.utils.version import PY311

if PY311:
from enum import property as enum_property
else:
from types import DynamicClassAttribute as enum_property

__all__ = [
"DictChoices",
Expand Down Expand Up @@ -253,13 +260,45 @@ class RequestOriginConstants(IntegerChoices):

class RequestStatusConstants(TextChoicesEnum):
NEW = "Brouillon"
AC_VALIDATION_PROCESSING = "En attente de validation par Aidants Connect"
VALIDATED = "Validée"
REFUSED = "Refusée"
AC_VALIDATION_PROCESSING = mark_safe(
"En attente de validation d’éligibilité avant inscription en "
"formation des aidants"
)
VALIDATED = "Éligibilité validée"
REFUSED = "Éligibilité Refusée"
CLOSED = "Clôturée"
CHANGES_REQUIRED = "Modifications demandées"
CHANGES_REQUIRED = "Demande de modifications par l’équipe Aidants Connect"
CHANGES_PROPOSED = "Modifications proposées par Aidants Connect"

@enum_property
def description(self):
match self:
case self.AC_VALIDATION_PROCESSING:
return mark_safe(
"<p>Votre demande d’habilitation est en cours d’instruction "
"par nos équipes. Vous serez prochainement notifié de la "
"décision de nos équipes concernant votre dossier."
)
case self.VALIDATED:
return mark_safe(
"<p>Félicitations, votre demande d’habilitation a été acceptée par "
"Aidants Connect !</p>"
"<p>Vous pouvez désormais inscrire le référent sur un webinaire "
"d’information dédié aux référents et inscrire les aidants en "
"formation.</p>"
)
case self.CHANGES_REQUIRED:
return mark_safe(
"<p>L'équipe Aidants Connect a étudié votre demande d’habilitation "
"et souhaite que vous y apportiez des modifications. N’oubliez pas "
"de valider à nouveau votre demande d’habilitation en cliquant sur "
"le bouton « Soumettre la demande » pour que l'équipe Aidants "
"Connect prenne en compte vos modifications et valide votre "
"demande</p>"
)
case _:
return ""

@classproperty
def aidant_registrable(cls):
"""Statuses that allow to add new aidants to an habilitation request"""
Expand Down
2 changes: 2 additions & 0 deletions aidants_connect_common/tests/testcases.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from django.test import override_settings, tag
from django.urls import reverse

from faker.proxy import Faker
from selenium.common.exceptions import (
NoSuchElementException,
StaleElementReferenceException,
Expand All @@ -34,6 +35,7 @@
@tag("functional")
class FunctionalTestCase(StaticLiveServerTestCase):
js = True
faker = Faker()

@classmethod
def setUpClass(cls):
Expand Down
Empty file.
11 changes: 11 additions & 0 deletions aidants_connect_habilitation/api/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from django.urls import path

from aidants_connect_habilitation.api.views import PersonnelRequestEditView

urlpatterns = [
path(
"demandeur/<str:issuer_id>/organisation/<str:uuid>/aidant/<int:aidant_id>/edit/", # noqa: E501
PersonnelRequestEditView.as_view(),
name="api_habilitation_aidant_edit",
),
]
77 changes: 77 additions & 0 deletions aidants_connect_habilitation/api/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
from django.http import HttpResponse
from django.shortcuts import get_object_or_404
from django.urls import reverse
from django.views.generic import FormView

from aidants_connect_habilitation.constants import HabilitationFormStep
from aidants_connect_habilitation.forms import AidantRequestForm
from aidants_connect_habilitation.models import AidantRequest
from aidants_connect_habilitation.views import (
OnlyNewRequestsView,
ProfileCardAidantRequestPresenter,
)


class PersonnelRequestEditView(OnlyNewRequestsView, FormView):
form_class = AidantRequestForm
template_name = "forms/form.html"

@property
def step(self) -> HabilitationFormStep:
return HabilitationFormStep.SUMMARY

def setup(self, request, *args, **kwargs):
self.success = False
super().setup(request, *args, **kwargs)
self.aidant_request = get_object_or_404(
AidantRequest,
organisation=self.organisation,
pk=self.kwargs.get("aidant_id"),
)

def get_form_kwargs(self):
return {
"organisation": self.organisation,
"instance": self.aidant_request,
**super().get_form_kwargs(),
}

def get_context_data(self, **kwargs):
if "habilitation_request" in kwargs:
kwargs["habilitation_request"] = ProfileCardAidantRequestPresenter(
kwargs["habilitation_request"]
)

kwargs.update(
{
"action": reverse(
"api_habilitation_aidant_edit",
kwargs={
"issuer_id": self.organisation.issuer.issuer_id,
"uuid": self.organisation.uuid,
"aidant_id": self.aidant_request.pk,
},
)
}
)
return super().get_context_data(**kwargs)

def get_template_names(self):
if self.success:
return "aidants_connect_habilitation/common/_habilitation-request-profile-card.html" # noqa: E501

return super().get_template_names()

def form_invalid(self, form):
return self.render_to_response(self.get_context_data(form=form), status=422)

def form_valid(self, form):
habilitation_request = form.save()
self.success = True
return self.render_to_response(
self.get_context_data(habilitation_request=habilitation_request)
)

def delete(self, request, *args, **kwargs):
self.aidant_request.delete()
return HttpResponse(status=202)
61 changes: 58 additions & 3 deletions aidants_connect_habilitation/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -452,7 +452,45 @@ def __init__(self, email):
)


class AidantRequestForm(ConseillerNumerique, PatchedModelForm, CleanEmailMixin):
# TODO: Remove and replace by AidantRequestForm when PersonnelRequestFormView is ported to DSFR # noqa: E501
class AidantRequestFormLegacy(ConseillerNumerique, PatchedModelForm, CleanEmailMixin):
def __init__(self, organisation: OrganisationRequest, *args, **kwargs):
self.organisation = organisation
super().__init__(*args, **kwargs)

def clean_email(self):
email = super().clean_email()

query = Q(organisation=self.organisation) & Q(email__iexact=email)
if getattr(self.instance, "pk"):
# This user already exists, and we need to verify that
# we are not trying to modify its email with the email
# of antoher aidant in the organisation
query = query & ~Q(pk=self.instance.pk)
if AidantRequest.objects.filter(query).exists():
raise EmailOrganisationValidationError(email)

if (
self.organisation.manager
and self.organisation.manager.is_aidant
and self.organisation.manager.email == email
):
raise ManagerEmailOrganisationValidationError(email)

return email

def save(self, commit=True):
self.instance.organisation = self.organisation
return super().save(commit)

class Meta:
model = AidantRequest
exclude = ["organisation", "habilitation_request"]


class AidantRequestForm(ModelForm, ConseillerNumerique, CleanEmailMixin, DsfrBaseForm):
template_name = "aidants_connect_habilitation/forms/aidant.html"

def __init__(self, organisation: OrganisationRequest, *args, **kwargs):
self.organisation = organisation
super().__init__(*args, **kwargs)
Expand Down Expand Up @@ -550,10 +588,13 @@ def __management_form_widget_attrs(self, widget_name: str, attrs: dict):


AidantRequestFormSet = modelformset_factory(
AidantRequestForm.Meta.model, AidantRequestForm, formset=BaseAidantRequestFormSet
AidantRequestFormLegacy.Meta.model,
AidantRequestFormLegacy,
formset=BaseAidantRequestFormSet,
)


# TODO: Replace implementation with BaseMultiForm when PersonnelRequestFormView is ported to DSFR # noqa: E501
class PersonnelForm:
MANAGER_FORM_PREFIX = "manager"
AIDANTS_FORMSET_PREFIX = "aidants"
Expand Down Expand Up @@ -707,7 +748,8 @@ def save(self, commit=True) -> Tuple[Manager, List[AidantRequest]]:
save.alters_data = True


class ValidationForm(PatchedForm):
class ValidationForm(DsfrBaseForm):
template_name = "aidants_connect_habilitation/forms/validation.html" # noqa: E501
cgu = BooleanField(
required=True,
label='J’ai pris connaissance des <a href="{url}" class="fr-link">'
Expand Down Expand Up @@ -759,6 +801,19 @@ def save(
save.alters_data = True


class RequestViewForm(PatchedForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

def save(
self, organisation: OrganisationRequest, commit=True
) -> OrganisationRequest:
organisation.prepare_request_for_ac_validation(self.cleaned_data)
return organisation

save.alters_data = True


class RequestMessageForm(PatchedModelForm):
content = CharField(label="Votre message", widget=Textarea(attrs={"rows": 2}))

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 4.2.15 on 2024-09-26 13:42

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('aidants_connect_habilitation', '0032_organisationrequest_not_free_and_more'),
]

operations = [
migrations.AlterField(
model_name='organisationrequest',
name='status',
field=models.CharField(choices=[('NEW', 'Brouillon'), ('AC_VALIDATION_PROCESSING', 'En attente de validation d’éligibilité avant inscription en formation des aidants'), ('VALIDATED', 'Éligibilité validée'), ('REFUSED', 'Éligibilité Refusée'), ('CLOSED', 'Clôturée'), ('CHANGES_REQUIRED', 'Demande de modifications par l’équipe Aidants Connect'), ('CHANGES_PROPOSED', 'Modifications proposées par Aidants Connect')], default='NEW', max_length=150, verbose_name='État'),
),
]
31 changes: 16 additions & 15 deletions aidants_connect_habilitation/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -349,8 +349,8 @@ def is_draft(self):
return RequestStatusConstants[self.status] == RequestStatusConstants.NEW

@property
def status_label(self):
return RequestStatusConstants[self.status].label
def status_enum(self):
return RequestStatusConstants[self.status]

def __str__(self):
return self.name
Expand Down Expand Up @@ -481,19 +481,20 @@ def accept_request_and_create_organisation(self):
self.create_aidants(organisation)

if self.manager.is_aidant:
self.manager.habilitation_request, _ = (
HabilitationRequest.objects.get_or_create(
email=self.manager.email,
organisation=organisation,
defaults=dict(
origin=HabilitationRequest.ORIGIN_HABILITATION,
first_name=self.manager.first_name,
last_name=self.manager.last_name,
profession=self.manager.profession,
conseiller_numerique=self.manager.conseiller_numerique,
status=ReferentRequestStatuses.STATUS_PROCESSING,
),
)
(
self.manager.habilitation_request,
_,
) = HabilitationRequest.objects.get_or_create(
email=self.manager.email,
organisation=organisation,
defaults=dict(
origin=HabilitationRequest.ORIGIN_HABILITATION,
first_name=self.manager.first_name,
last_name=self.manager.last_name,
profession=self.manager.profession,
conseiller_numerique=self.manager.conseiller_numerique,
status=ReferentRequestStatuses.STATUS_PROCESSING,
),
)
self.manager.save(update_fields=("habilitation_request",))

Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
{% extends "admin/base_site.html" %}
{% load static admin_extras ac_extras i18n %}
{% load ac_extras admin_extras i18n static %}

{% block extrastyle %}
{{ block.super }}
<link rel="stylesheet" type="text/css" href="{% static "admin/css/changelists.css" %}">
<link rel="stylesheet" type="text/css" href="{% static "admin/css/forms.css" %}">
{{ media.css }}
{% endblock %}
{% endblock extrastyle %}

{% block extrahead %}
{{ block.super }}
{{ media.js }}
{% endblock %}
{% endblock extrahead %}

{% block content %}
<h1>Remettre en attente la demande d'habilitation n° {{ object.data_pass_id }}</h1>
<h2>La demande est celle de l'organisation {{ object.name }}</h2>
<h2> La demande est actuellement en statut : {{ object.status_label }}</h2>
<h2> La demande est actuellement en statut : {{ object.get_status_display }}</h2>
<form action="{% qurl 'otpadmin:aidants_connect_habilitation_organisationrequest_waiting' object_id=object_id %}"
method="POST">

Expand All @@ -31,4 +31,4 @@ <h2> La demande est actuellement en statut : {{ object.status_label }}</h2>
</form>


{% endblock %}
{% endblock content %}
8 changes: 4 additions & 4 deletions aidants_connect_habilitation/templates/issuer_space.html
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
{% extends 'layouts/main-habilitation.html' %}
{% load static form_extras %}
{% load form_extras static %}

{% block title %}
Aidants Connect - Mon espace habilitation
{% endblock %}
{% endblock title %}

{% block content %}
<div class="fr-container">
Expand Down Expand Up @@ -47,7 +47,7 @@ <h2>Vos demandes d'habilitation</h2>
{% for organisation in issuer.organisation_requests.all %}
<div class="fr-col-12 fr-col-md-4">
<div class="shadowed with-button-box">
<h3 class="h2">{{ organisation.name }} - <i>{{ organisation.status_label }}</i></h3>
<h3 class="h2">{{ organisation.name }} - <i>{{ organisation.get_status_display }}</i></h3>
<p>
{{ organisation.address|linebreaksbr }}<br>{{ organisation.zipcode }} {{ organisation.city }}
{% if organisation.status == "NEW" %}
Expand Down Expand Up @@ -98,4 +98,4 @@ <h3 class="h2">{{ organisation.name }} - <i>{{ organisation.status_label }}</i><
{% include "_more-info.html" %}
</div>

{% endblock %}
{% endblock content %}
Loading

0 comments on commit da37e0d

Please sign in to comment.