Skip to content

Commit

Permalink
eligibility: Fix admin add form
Browse files Browse the repository at this point in the history
  • Loading branch information
tonial committed Jan 21, 2025
1 parent 38ce6d6 commit de1cff4
Show file tree
Hide file tree
Showing 4 changed files with 458 additions and 16 deletions.
34 changes: 22 additions & 12 deletions itou/eligibility/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

from itou.approvals import models as approvals_models
from itou.eligibility import models
from itou.eligibility.admin_form import GEIQEligibilityDiagnosisAdminForm, IAEEligibilityDiagnosisAdminForm
from itou.eligibility.models.common import AbstractSelectedAdministrativeCriteria
from itou.job_applications import models as job_applications_models
from itou.utils.admin import ItouModelAdmin, ItouTabularInline, PkSupportRemarkInline, get_admin_view_link
Expand All @@ -14,7 +15,7 @@
class AbstractSelectedAdministrativeCriteriaInlineFormSet(BaseInlineFormSet):
def clean(self):
super().clean()
for line in self.cleaned_data:
for line in getattr(self, "cleaned_data", []):
if line["DELETE"] and line["id"].certified:
raise ValidationError("Impossible de supprimer un critère certifié")

Expand All @@ -32,14 +33,18 @@ class AbstractSelectedAdministrativeCriteriaInline(ItouTabularInline):
"created_at",
)
readonly_fields = (
"administrative_criteria",
"certified_display",
"certified_at",
"certification_period",
"data_returned_by_api",
"created_at",
)

def get_readonly_fields(self, request, obj=None):
if obj:
return ("administrative_criteria",) + self.readonly_fields
return self.readonly_fields

@admin.display(description=AbstractSelectedAdministrativeCriteria._meta.get_field("certified").verbose_name)
def certified_display(self, obj):
if not obj.administrative_criteria.is_certifiable:
Expand Down Expand Up @@ -161,6 +166,7 @@ class AbstractEligibilityDiagnosisAdmin(ItouModelAdmin):
"created_at",
"updated_at",
"expires_at",
"is_valid",
)
search_fields = ("pk", "job_seeker__email", "author__email")
list_filter = (
Expand All @@ -170,32 +176,38 @@ class AbstractEligibilityDiagnosisAdmin(ItouModelAdmin):

@admin.display(boolean=True, description="en cours de validité")
def is_valid(self, obj):
return obj.is_valid
if obj.pk:
return obj.is_valid
return None

def save_model(self, request, obj, form, change):
obj.expires_at = self.model._expiration_date(obj.author)
return super().save_model(request, obj, form, change)


@admin.register(models.EligibilityDiagnosis)
class EligibilityDiagnosisAdmin(AbstractEligibilityDiagnosisAdmin):
list_display = AbstractEligibilityDiagnosisAdmin.list_display + ("has_approval",)
list_filter = AbstractEligibilityDiagnosisAdmin.list_filter + (HasApprovalFilter,)
raw_id_fields = AbstractEligibilityDiagnosisAdmin.raw_id_fields + ("author_siae",)
readonly_fields = AbstractEligibilityDiagnosisAdmin.readonly_fields + (
"is_valid",
"is_considered_valid",
)
readonly_fields = AbstractEligibilityDiagnosisAdmin.readonly_fields + ("is_considered_valid",)
inlines = (
SelectedAdministrativeCriteriaInline,
JobApplicationInline,
ApprovalInline,
PkSupportRemarkInline,
)
form = IAEEligibilityDiagnosisAdminForm

@admin.display(boolean=True, description="valide ou PASS IAE en cours")
def is_considered_valid(self, obj):
"""
This uses a property of the model and is intended to be used on the
detail view to avoid too many SQL queries on a list view.
"""
return obj.is_considered_valid
if obj.pk:
return obj.is_considered_valid
return None

@admin.display(boolean=True, description="PASS IAE en cours")
def has_approval(self, obj):
Expand All @@ -214,15 +226,13 @@ def get_queryset(self, request):
@admin.register(models.GEIQEligibilityDiagnosis)
class GEIQEligibilityDiagnosisAdmin(AbstractEligibilityDiagnosisAdmin):
raw_id_fields = AbstractEligibilityDiagnosisAdmin.raw_id_fields + ("author_geiq",)
readonly_fields = AbstractEligibilityDiagnosisAdmin.readonly_fields + (
"is_valid",
"allowance_amount",
)
readonly_fields = AbstractEligibilityDiagnosisAdmin.readonly_fields + ("allowance_amount",)
inlines = (
SelectedGEIQAdministrativeCriteriaInline,
JobApplicationInline,
PkSupportRemarkInline,
)
form = GEIQEligibilityDiagnosisAdminForm

@admin.display(description="montant de l'aide")
def allowance_amount(self, obj):
Expand Down
107 changes: 107 additions & 0 deletions itou/eligibility/admin_form.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
from django import forms

from itou.companies.enums import CompanyKind
from itou.eligibility.enums import AuthorKind
from itou.eligibility.models.geiq import GEIQEligibilityDiagnosis
from itou.eligibility.models.iae import EligibilityDiagnosis
from itou.users.enums import UserKind


class GEIQEligibilityDiagnosisAdminForm(forms.ModelForm):
class Meta:
model = GEIQEligibilityDiagnosis
fields = ["job_seeker", "author", "author_kind", "author_prescriber_organization", "author_geiq"]

def clean_author_kind(self):
value = self.cleaned_data["author_kind"]
if value not in [AuthorKind.GEIQ, AuthorKind.PRESCRIBER]:
raise forms.ValidationError("Un diagnostic GEIQ ne peut pas avoir ce type d'auteur.")
return value

def clean_job_seeker(self):
value = self.cleaned_data["job_seeker"]
if value.kind != UserKind.JOB_SEEKER:
raise forms.ValidationError("L'utilisateur doit être un candidat")
return value

def clean(self):
super().clean()

author = self.cleaned_data.get("author")
author_kind = self.cleaned_data.get("author_kind")
author_prescriber_organization = self.cleaned_data.get("author_prescriber_organization")
author_geiq = self.cleaned_data.get("author_geiq")

if author and author_kind:
if author.kind == UserKind.PRESCRIBER:
if not author_kind == AuthorKind.PRESCRIBER:
self.add_error("author_kind", "Le type ne correspond pas à l'utilisateur.")
if not author_prescriber_organization or not author_prescriber_organization.is_authorized:
self.add_error(
"author_prescriber_organization",
"Une organisation prescriptrice est obligatoire pour ce type d'auteur.",
)
if (
author_prescriber_organization
and not author_prescriber_organization.memberships.filter(user=author, is_active=True).exists()
):
self.add_error("author_prescriber_organization", "L'auteur n'appartient pas à cette organisation.")
elif author.kind == UserKind.EMPLOYER:
if not author_kind == AuthorKind.GEIQ:
self.add_error("author_kind", "Le type ne correspond pas à l'utilisateur.")
if not author_geiq:
self.add_error("author_geiq", "Une entreprise GEIQ est obligatoire pour ce type d'auteur")
elif not author_geiq.memberships.filter(user=author, is_active=True).exists():
self.add_error("author_geiq", "L'auteur n'appartient pas à cette structure.")
else: # Any other kind
self.add_error("author", "Seul un prescripteur ou employeur peut être auteur d'un diagnostic.")


class IAEEligibilityDiagnosisAdminForm(forms.ModelForm):
class Meta:
model = EligibilityDiagnosis
fields = ["job_seeker", "author", "author_kind", "author_prescriber_organization", "author_siae"]

def clean_author_kind(self):
value = self.cleaned_data["author_kind"]
if value not in [AuthorKind.EMPLOYER, AuthorKind.PRESCRIBER]:
raise forms.ValidationError("Un diagnostic IAE ne peut pas avoir ce type d'auteur.")
return value

def clean_job_seeker(self):
value = self.cleaned_data["job_seeker"]
if value.kind != UserKind.JOB_SEEKER:
raise forms.ValidationError("L'utilisateur doit être un candidat")
return value

def clean(self):
super().clean()

author = self.cleaned_data.get("author")
author_kind = self.cleaned_data.get("author_kind")
author_prescriber_organization = self.cleaned_data.get("author_prescriber_organization")
author_siae = self.cleaned_data.get("author_siae")

if author and author_kind:
if author.kind == UserKind.PRESCRIBER:
if not author_kind == AuthorKind.PRESCRIBER:
self.add_error("author_kind", "Le type ne correspond pas à l'utilisateur.")
if not author_prescriber_organization or not author_prescriber_organization.is_authorized:
self.add_error(
"author_prescriber_organization",
"Une organisation prescriptrice est obligatoire pour ce type d'auteur.",
)
if (
author_prescriber_organization
and not author_prescriber_organization.memberships.filter(user=author, is_active=True).exists()
):
self.add_error("author_prescriber_organization", "L'auteur n'appartient pas à cette organisation.")
elif author.kind == UserKind.EMPLOYER:
if not author_kind == AuthorKind.EMPLOYER:
self.add_error("author_kind", "Le type ne correspond pas à l'utilisateur.")
if not author_siae:
self.add_error("author_siae", "Une SIAE est obligatoire pour ce type d'auteur.")
elif not author_siae.memberships.filter(user=author, is_active=True).exists():
self.add_error("author_siae", "L'auteur n'appartient pas à cette structure.")
else: # Any other kind
self.add_error("author", "Seul un prescripteur ou employeur peut être auteur d'un diagnostic.")
16 changes: 13 additions & 3 deletions itou/eligibility/models/geiq.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,11 @@ class Meta:
objects = GEIQEligibilityDiagnosisQuerySet.as_manager()

def clean(self):
if self.author_geiq and self.author_geiq.kind != CompanyKind.GEIQ:
# we dont want to raise something else than a ValidationError here
author_geiq = getattr(self, "author_geiq", None)
job_seeker = getattr(self, "job_seeker", None)

if author_geiq and self.author_geiq.kind != CompanyKind.GEIQ:
raise ValidationError("L'auteur du diagnostic n'est pas un GEIQ")

if self.pk:
Expand All @@ -118,7 +122,8 @@ def clean(self):
# The following would have been nice in a unique constraint,
# but infortunately functions.Now() is not immutable
if (
self.job_seeker
job_seeker
and author_geiq
and GEIQEligibilityDiagnosis.objects.valid_diagnoses_for(self.job_seeker, self.author_geiq).exists()
):
raise ValidationError(f"Il existe déjà un diagnostic GEIQ valide pour cet utilisateur : {self.job_seeker}")
Expand Down Expand Up @@ -167,6 +172,11 @@ def get_criteria_display_qs(self, hiring_start_at=None):
.exclude(administrative_criteria__annex=AdministrativeCriteriaAnnex.NO_ANNEX)
)

@classmethod
def _expiration_date(cls, author=None):
now = timezone.localdate()
return now + relativedelta(months=cls.EXPIRATION_DELAY_MONTHS)

@classmethod
@transaction.atomic()
def create_eligibility_diagnosis(
Expand Down Expand Up @@ -198,7 +208,7 @@ def create_eligibility_diagnosis(
author_kind=author_kind,
author_prescriber_organization=author_org,
author_geiq=author_geiq,
expires_at=timezone.localdate() + relativedelta(months=cls.EXPIRATION_DELAY_MONTHS),
expires_at=cls._expiration_date(),
)

if administrative_criteria:
Expand Down
Loading

0 comments on commit de1cff4

Please sign in to comment.