-
Notifications
You must be signed in to change notification settings - Fork 24
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
PASS IAE: La date de début du contrat doit être avant la date de fin du PASS [GEN-2225] #5248
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -8,6 +8,7 @@ | |||||||||||||||||||||
|
||||||||||||||||||||||
from itou.approvals.models import Approval | ||||||||||||||||||||||
from itou.eligibility.enums import AdministrativeCriteriaLevel, AuthorKind | ||||||||||||||||||||||
from itou.utils.date import make_date_timezone_aware | ||||||||||||||||||||||
|
||||||||||||||||||||||
from .common import ( | ||||||||||||||||||||||
AbstractAdministrativeCriteria, | ||||||||||||||||||||||
|
@@ -39,10 +40,10 @@ def has_approval(self): | |||||||||||||||||||||
|
||||||||||||||||||||||
|
||||||||||||||||||||||
class EligibilityDiagnosisManager(models.Manager): | ||||||||||||||||||||||
def has_considered_valid(self, job_seeker, for_siae=None): | ||||||||||||||||||||||
def has_considered_valid(self, job_seeker, for_siae=None, on_date=None): | ||||||||||||||||||||||
""" | ||||||||||||||||||||||
Returns True if the given job seeker has a considered valid diagnosis, | ||||||||||||||||||||||
False otherwise. | ||||||||||||||||||||||
False otherwise. If on_date is given, the validity is considered for this date. | ||||||||||||||||||||||
|
||||||||||||||||||||||
We consider the eligibility as valid when a PASS IAE is valid but | ||||||||||||||||||||||
the eligibility diagnosis is missing. | ||||||||||||||||||||||
|
@@ -57,7 +58,11 @@ def has_considered_valid(self, job_seeker, for_siae=None): | |||||||||||||||||||||
Hence the Trello #2604 decision: if a PASS IAE is valid, we do not | ||||||||||||||||||||||
check the presence of an eligibility diagnosis. | ||||||||||||||||||||||
""" | ||||||||||||||||||||||
return job_seeker.has_valid_approval or bool(self.last_considered_valid(job_seeker, for_siae=for_siae)) | ||||||||||||||||||||||
on_date = on_date or timezone.now() | ||||||||||||||||||||||
last_valid_diagnosis = self.last_considered_valid(job_seeker, for_siae=for_siae) | ||||||||||||||||||||||
return job_seeker.has_valid_approval_for_date(on_date) or ( | ||||||||||||||||||||||
last_valid_diagnosis and last_valid_diagnosis.expires_at > make_date_timezone_aware(on_date) | ||||||||||||||||||||||
) | ||||||||||||||||||||||
Comment on lines
+62
to
+65
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. C'est dommage : avec ton changement on fait les requêtes de
Suggested change
|
||||||||||||||||||||||
|
||||||||||||||||||||||
def last_considered_valid(self, job_seeker, for_siae=None, prefetch=None): | ||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Par contre, c'est utilisé dans le modèle return EligibilityDiagnosis.objects.last_considered_valid(self.job_seeker, for_siae=self.to_company) Donc c'est possible que ce fonction devrait produire une éligibilité qui est valide pour la date de début d'embauche There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Pour moi, tu devrais ajouter un |
||||||||||||||||||||||
""" | ||||||||||||||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -229,8 +229,11 @@ def with_jobseeker_valid_eligibility_diagnosis(self): | |
""" | ||
sub_query = Subquery( | ||
( | ||
EligibilityDiagnosis.objects.valid() | ||
.for_job_seeker_and_siae(job_seeker=OuterRef("job_seeker"), siae=OuterRef("to_company")) | ||
EligibilityDiagnosis.objects.for_job_seeker_and_siae( | ||
job_seeker=OuterRef("job_seeker"), siae=OuterRef("to_company") | ||
) | ||
.filter(expires_at__gt=OuterRef("hiring_start_at")) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
.valid() | ||
.values("id")[:1] | ||
), | ||
output_field=models.IntegerField(), | ||
|
@@ -251,14 +254,14 @@ def with_jobseeker_eligibility_diagnosis(self): | |
def eligibility_validated(self): | ||
return self.filter( | ||
Exists( | ||
Approval.objects.filter( | ||
user=OuterRef("job_seeker"), | ||
).valid() | ||
Approval.objects.filter(user=OuterRef("job_seeker"), end_at__gte=OuterRef("hiring_start_at")).valid() | ||
) | ||
| Exists( | ||
EligibilityDiagnosis.objects.for_job_seeker_and_siae( | ||
OuterRef("job_seeker"), siae=OuterRef("to_company") | ||
).valid() | ||
) | ||
.filter(expires_at__gt=OuterRef("hiring_start_at")) | ||
.valid() | ||
) | ||
) | ||
|
||
|
@@ -729,13 +732,18 @@ def is_sent_by_authorized_prescriber(self): | |
def is_spontaneous(self): | ||
return not self.selected_jobs.exists() | ||
|
||
def job_seeker_has_valid_diagnosis(self): | ||
return self.job_seeker.has_valid_diagnosis(for_siae=self.to_company, on_date=self.hiring_start_at) | ||
|
||
def eligibility_diagnosis_by_siae_required(self): | ||
""" | ||
Returns True if an eligibility diagnosis must be made by an SIAE | ||
when processing an application, False otherwise. | ||
""" | ||
return self.to_company.is_subject_to_eligibility_rules and not self.job_seeker.has_valid_diagnosis( | ||
for_siae=self.to_company | ||
return ( | ||
not self.hiring_without_approval | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. bien vu pour cet ajout |
||
and self.to_company.is_subject_to_eligibility_rules | ||
and not self.job_seeker_has_valid_diagnosis() | ||
) | ||
|
||
@property | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -34,6 +34,7 @@ | |
from itou.common_apps.address.format import compute_hexa_address | ||
from itou.common_apps.address.models import AddressMixin | ||
from itou.companies.enums import CompanyKind | ||
from itou.utils.date import make_date_timezone_aware | ||
from itou.utils.db import or_queries | ||
from itou.utils.models import UniqueConstraintWithErrorCode | ||
from itou.utils.templatetags.str_filters import mask_unless | ||
|
@@ -511,6 +512,10 @@ def latest_common_approval(self): | |
|
||
return self.latest_approval or self.latest_pe_approval | ||
|
||
def has_valid_approval_for_date(self, on_date): | ||
on_date = make_date_timezone_aware(on_date) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. quel intérêt de rendre |
||
return self.has_valid_approval and make_date_timezone_aware(self.latest_approval.end_at) > on_date | ||
|
||
@property | ||
def has_valid_approval(self): | ||
return self.latest_approval and self.latest_approval.is_valid() | ||
|
@@ -533,11 +538,10 @@ def new_approval_blocked_by_waiting_period(self, siae, sender_prescriber_organiz | |
) | ||
|
||
# Only diagnoses made by authorized prescribers are taken into account. | ||
has_valid_diagnosis = self.has_valid_diagnosis() | ||
return ( | ||
self.has_latest_common_approval_in_waiting_period | ||
and siae.is_subject_to_eligibility_rules | ||
and not (is_sent_by_authorized_prescriber or has_valid_diagnosis) | ||
and not (is_sent_by_authorized_prescriber or self.has_valid_diagnosis()) | ||
) | ||
|
||
@property | ||
|
@@ -563,8 +567,9 @@ def has_external_data(self): | |
def has_jobseeker_profile(self): | ||
return self.is_job_seeker and hasattr(self, "jobseeker_profile") | ||
|
||
def has_valid_diagnosis(self, for_siae=None): | ||
return self.eligibility_diagnoses.has_considered_valid(job_seeker=self, for_siae=for_siae) | ||
def has_valid_diagnosis(self, for_siae=None, on_date=None): | ||
on_date = on_date or timezone.now() | ||
return self.eligibility_diagnoses.has_considered_valid(job_seeker=self, for_siae=for_siae, on_date=on_date) | ||
|
||
def joined_recently(self): | ||
time_since_date_joined = timezone.now() - self.date_joined | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,3 +6,15 @@ | |
def monday_of_the_week(value=None): | ||
the_day = timezone.localdate(value) | ||
return the_day - datetime.timedelta(days=the_day.weekday()) | ||
|
||
|
||
def make_date_timezone_aware(value, tz=None): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Je ne suis pas certain de saisir l'utilité de cette fonction, on ne devrait avoir que des datetime avec timezone dans notre base de code et notre base de données. |
||
""" | ||
Makes datetime.date or datetime.datetime timezone aware. | ||
Useful when providing a DateField to a query on the DateTimeField of another field/model. | ||
""" | ||
if not hasattr(value, "hour"): | ||
value = datetime.datetime.combine(value, datetime.datetime.min.time()) | ||
if timezone.is_aware(value): | ||
return value | ||
return timezone.make_aware(value, tz) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -536,19 +536,27 @@ def accept(request, job_application_id, template_name="apply/process_accept.html | |
Trigger the `accept` transition. | ||
""" | ||
queryset = JobApplication.objects.is_active_company_member(request.user).select_related( | ||
"job_seeker", "job_seeker__jobseeker_profile" | ||
"job_seeker", "job_seeker__jobseeker_profile", "to_company" | ||
) | ||
job_application = get_object_or_404(queryset, id=job_application_id) | ||
job_seeker = job_application.job_seeker | ||
check_waiting_period(job_application) | ||
next_url = reverse("apply:details_for_company", kwargs={"job_application_id": job_application.pk}) | ||
if not job_application.hiring_without_approval and job_application.eligibility_diagnosis_by_siae_required(): | ||
messages.error(request, "Cette candidature requiert un diagnostic d'éligibilité pour être acceptée.") | ||
|
||
# Verify PASS IAE approval. | ||
if job_application.eligibility_diagnosis_by_siae_required(): | ||
# Either the candidate has a valid diagnosis that will be out of date... | ||
if job_seeker.has_valid_diagnosis(for_siae=job_application.to_company, on_date=timezone.now()): | ||
messages.error(request, "Le contrat doit débuter sur la période couverte par le PASS IAE.") | ||
Comment on lines
+549
to
+550
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Et le lien de causalité entre |
||
# Or they have no valid diagnosis. | ||
else: | ||
messages.error(request, "Cette candidature requiert un diagnostic d'éligibilité pour être acceptée.") | ||
return HttpResponseRedirect(next_url) | ||
|
||
return common_views._accept( | ||
request, | ||
job_application.to_company, | ||
job_application.job_seeker, | ||
job_seeker, | ||
error_url=next_url, | ||
back_url=next_url, | ||
template_name=template_name, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -120,6 +120,22 @@ def test_expired_itou_diagnosis(self): | |
assert last_considered_valid is None | ||
assert last_expired is not None | ||
|
||
def test_diagnosis_expiring(self): | ||
expires_at = timezone.now() + datetime.timedelta(days=1) | ||
diagnosis = IAEEligibilityDiagnosisFactory( | ||
from_prescriber=True, job_seeker=self.job_seeker, expires_at=expires_at | ||
) | ||
has_considered_valid = EligibilityDiagnosis.objects.has_considered_valid(job_seeker=diagnosis.job_seeker) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. pourquoi ne pas mettre les assert sans variable intermédiaire ? |
||
last_considered_valid = EligibilityDiagnosis.objects.last_considered_valid(job_seeker=diagnosis.job_seeker) | ||
last_expired = EligibilityDiagnosis.objects.last_expired(job_seeker=diagnosis.job_seeker) | ||
assert last_considered_valid == diagnosis | ||
assert last_expired is None | ||
assert has_considered_valid | ||
|
||
assert not EligibilityDiagnosis.objects.has_considered_valid( | ||
job_seeker=diagnosis.job_seeker, on_date=(expires_at + datetime.timedelta(hours=1)) | ||
) | ||
|
||
def test_expired_itou_diagnosis_with_ongoing_approval(self): | ||
expired_diagnosis = IAEEligibilityDiagnosisFactory( | ||
from_prescriber=True, job_seeker=self.job_seeker, expired=True | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A confirmer avec le métier mais pour les diagnostiques il me semble qu'on veux qu'il soit valide au moment où on accepte la candidature et on s'en fiche qu'il le soit encore le jour de l'embauche car il aura un PASS IAE à ce moment là.