Skip to content

Commit

Permalink
eligibility: Improve eligibility diagnosis data consistancy
Browse files Browse the repository at this point in the history
  • Loading branch information
tonial committed Jan 28, 2025
1 parent 4364957 commit 48a2c83
Show file tree
Hide file tree
Showing 11 changed files with 123 additions and 19 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Generated by Django 5.1.5 on 2025-01-28 09:51

from django.conf import settings
from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("companies", "0014_company_fields_history_and_more"),
("eligibility", "0009_fix_selectedadministrativecriteria_certification_period"),
("prescribers", "0007_prescriberorganization_automatic_geocoding_update"),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]

operations = [
migrations.AddConstraint(
model_name="eligibilitydiagnosis",
constraint=models.CheckConstraint(
condition=models.Q(
models.Q(
("author_kind", "employer"),
("author_prescriber_organization__isnull", True),
("author_siae__isnull", False),
),
models.Q(
("author_kind", "prescriber"),
("author_prescriber_organization__isnull", False),
("author_siae__isnull", True),
),
_connector="OR",
),
name="eligibility_iae_diagnosis_author_kind_coherence",
violation_error_message="La structure de l'auteur ne correspond pas à son type",
),
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Generated by Django 5.1.5 on 2025-01-28 09:52

from django.conf import settings
from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("companies", "0014_company_fields_history_and_more"),
("eligibility", "0010_add_eligibilitydiagnosis_author_kind_coherence"),
("prescribers", "0007_prescriberorganization_automatic_geocoding_update"),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]

operations = [
migrations.RemoveConstraint(
model_name="geiqeligibilitydiagnosis",
name="author_kind_coherence",
),
migrations.AddConstraint(
model_name="geiqeligibilitydiagnosis",
constraint=models.CheckConstraint(
condition=models.Q(
models.Q(
("author_geiq__isnull", False),
("author_kind", "geiq"),
("author_prescriber_organization__isnull", True),
),
models.Q(
("author_geiq__isnull", True),
("author_kind", "prescriber"),
("author_prescriber_organization__isnull", False),
),
_connector="OR",
),
name="author_kind_coherence",
violation_error_message="La structure de l'auteur ne correspond pas à son type",
),
),
]
2 changes: 1 addition & 1 deletion itou/eligibility/models/geiq.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ class Meta:
constraints = [
models.CheckConstraint(
name="author_kind_coherence",
violation_error_message="Le diagnostic d'éligibilité GEIQ ne peut avoir 2 structures pour auteur",
violation_error_message="La structure de l'auteur ne correspond pas à son type",
condition=models.Q(
author_kind=AuthorKind.GEIQ,
author_geiq__isnull=False,
Expand Down
16 changes: 16 additions & 0 deletions itou/eligibility/models/iae.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,22 @@ class Meta:
verbose_name = "diagnostic d'éligibilité IAE"
verbose_name_plural = "diagnostics d'éligibilité IAE"
ordering = ["-created_at"]
constraints = [
models.CheckConstraint(
name="eligibility_iae_diagnosis_author_kind_coherence",
violation_error_message="La structure de l'auteur ne correspond pas à son type",
condition=models.Q(
author_kind=AuthorKind.EMPLOYER,
author_siae__isnull=False,
author_prescriber_organization__isnull=True,
)
| models.Q(
author_kind=AuthorKind.PRESCRIBER,
author_prescriber_organization__isnull=False,
author_siae__isnull=True,
),
),
]

@property
def author_organization(self):
Expand Down
2 changes: 1 addition & 1 deletion tests/companies/test_import_siae_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ def test_with_eligibility_diagnosis(self):
assert could_siae_be_deleted(company)

# Approval with eligibility diagnosis authored by SIAE
ApprovalFactory(eligibility_diagnosis__author_siae=company)
ApprovalFactory(eligibility_diagnosis__author_siae=company, eligibility_diagnosis__from_employer=True)
assert not could_siae_be_deleted(company)

def test_with_job_app(self):
Expand Down
4 changes: 4 additions & 0 deletions tests/eligibility/factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,11 @@ class Params:
from_employer = factory.Trait(
author_kind=AuthorKind.EMPLOYER,
author_siae=factory.SubFactory(CompanyFactory, subject_to_eligibility=True, with_membership=True),
author_prescriber_organization=None,
author=factory.LazyAttribute(lambda obj: obj.author_siae.members.first()),
)
with_certifiable_criteria = factory.Trait(romes=factory.PostGeneration(_get_iae_certifiable_criteria))
with_not_certifiable_criteria = factory.Trait(romes=factory.PostGeneration(_get_iae_not_certifiable_criteria))

def __init__(self, *args, **kwargs):
return super().__init__(*args, **kwargs)
11 changes: 7 additions & 4 deletions tests/eligibility/test_admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -260,10 +260,13 @@ def test_add_eligibility_not_both_org_and_company(self, admin_client, kind, user

response = admin_client.post(self.get_add_url(kind), data=post_data)
assert response.status_code == 200
expected_errors = [["Vous ne pouvez pas saisir une entreprise et une organisation prescriptrice."]]
if kind == "geiq":
# Additional error thanks to the db constraint
expected_errors[0].append("Le diagnostic d'éligibilité GEIQ ne peut avoir 2 structures pour auteur")
expected_errors = [
[
"Vous ne pouvez pas saisir une entreprise et une organisation prescriptrice.",
"La structure de l'auteur ne correspond pas à son type",
]
]

assert response.context["errors"] == expected_errors
assert not self.get_diag_model(kind).objects.exists()

Expand Down
2 changes: 1 addition & 1 deletion tests/eligibility/test_geiq.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ def test_geiq_eligibility_diagnosis_validation():

with pytest.raises(
ValidationError,
match="Le diagnostic d'éligibilité GEIQ ne peut avoir 2 structures pour auteur",
match="La structure de l'auteur ne correspond pas à son type",
):
GEIQEligibilityDiagnosis(
author_geiq=geiq,
Expand Down
23 changes: 14 additions & 9 deletions tests/siae_evaluations/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
InstitutionWith2MembershipFactory,
)
from tests.job_applications.factories import JobApplicationFactory
from tests.prescribers.factories import PrescriberMembershipFactory
from tests.siae_evaluations.factories import (
EvaluatedAdministrativeCriteriaFactory,
EvaluatedJobApplicationFactory,
Expand All @@ -54,7 +55,7 @@ def create_batch_of_job_applications(company):
for _ in range(evaluation_enums.EvaluationJobApplicationsBoundariesNumber.MIN):
approval = ApprovalFactory(
start_at=start,
eligibility_diagnosis__author_kind=AuthorKind.EMPLOYER,
eligibility_diagnosis__from_employer=True,
eligibility_diagnosis__author_siae=company,
)
JobApplicationFactory.create(
Expand Down Expand Up @@ -244,6 +245,10 @@ def test_eligibility_diag_not_made_by_employer(self, campaign_eligible_job_app_o
evaluation_campaign = EvaluationCampaignFactory()
diag = campaign_eligible_job_app_objects["diag"]
diag.author_kind = AuthorKind.PRESCRIBER
membership = PrescriberMembershipFactory()
diag.author_prescriber_organization = membership.organization
diag.author_siae = None
diag.author = membership.user
diag.save()
assert [] == list(evaluation_campaign.eligible_job_applications())
assert _eligible_to_siae_evaluations(campaign_eligible_job_app_objects["job_app"]) == "non"
Expand Down Expand Up @@ -347,18 +352,18 @@ def test_eligible_siaes(self):
evaluation_campaign = EvaluationCampaignFactory()

# company_1 got 1 job application
company_1 = CompanyFactory(department="14")
company_1 = CompanyFactory(department="14", with_membership=True)
JobApplicationFactory(
with_approval=True,
to_company=company_1,
sender_company=company_1,
eligibility_diagnosis__author_kind=AuthorKind.EMPLOYER,
eligibility_diagnosis__from_employer=AuthorKind.EMPLOYER,
eligibility_diagnosis__author_siae=company_1,
hiring_start_at=timezone.localdate() - relativedelta(months=2),
)

# company_2 got 2 job applications
company_2 = CompanyFactory(department="14")
company_2 = CompanyFactory(department="14", with_membership=True)
create_batch_of_job_applications(company_2)

eligible_siaes_res = evaluation_campaign.eligible_siaes()
Expand Down Expand Up @@ -387,7 +392,7 @@ def test_eligible_siae_approval_from_past_year(self):
before_evaluated_period = datetime.date(2022, 4, 4)
approval1 = ApprovalFactory(
start_at=before_evaluated_period, # Before evaluated period.
eligibility_diagnosis__author_kind=AuthorKind.EMPLOYER,
eligibility_diagnosis__from_employer=True,
eligibility_diagnosis__author_siae=company,
)
job_app_approval1_args = {
Expand All @@ -406,7 +411,7 @@ def test_eligible_siae_approval_from_past_year(self):
within_evaluated_period = datetime.date(2023, 2, 1)
approval2 = ApprovalFactory(
start_at=within_evaluated_period,
eligibility_diagnosis__author_kind=AuthorKind.EMPLOYER,
eligibility_diagnosis__from_employer=True,
eligibility_diagnosis__author_siae=company,
)
JobApplicationFactory.create(
Expand All @@ -424,13 +429,13 @@ def test_number_of_siaes_to_select(self):
assert 0 == evaluation_campaign.number_of_siaes_to_select()

for _ in range(3):
company = CompanyFactory(department="14")
company = CompanyFactory(department="14", with_membership=True)
create_batch_of_job_applications(company)

assert 1 == evaluation_campaign.number_of_siaes_to_select()

for _ in range(3):
company = CompanyFactory(department="14")
company = CompanyFactory(department="14", with_membership=True)
create_batch_of_job_applications(company)

assert 2 == evaluation_campaign.number_of_siaes_to_select()
Expand All @@ -439,7 +444,7 @@ def test_eligible_siaes_under_ratio(self):
evaluation_campaign = EvaluationCampaignFactory()

for _ in range(6):
company = CompanyFactory(department="14")
company = CompanyFactory(department="14", with_membership=True)
create_batch_of_job_applications(company)

assert 2 == evaluation_campaign.eligible_siaes_under_ratio().count()
Expand Down
2 changes: 1 addition & 1 deletion tests/www/apply/test_process.py
Original file line number Diff line number Diff line change
Expand Up @@ -1840,7 +1840,7 @@ def create_job_application(self, *args, **kwargs):
kwargs = {
"eligibility_diagnosis__with_certifiable_criteria": True,
"eligibility_diagnosis__author_siae": self.company,
"eligibility_diagnosis__author_kind": AuthorKind.EMPLOYER,
"eligibility_diagnosis__from_employer": True,
} | kwargs
return JobApplicationSentByJobSeekerFactory(**kwargs)

Expand Down
4 changes: 2 additions & 2 deletions tests/www/employees_views/test_detail.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ def test_detail_view(self, client, snapshot):
)
assert job_application.is_sent_by_authorized_prescriber
IAEEligibilityDiagnosisFactory(
from_prescriber=True, job_seeker=approval.user, author_siae=job_application.to_company
from_employer=True, job_seeker=approval.user, author_siae=job_application.to_company
)

# Another job applcation on the same SIAE, by a non authorized prescriber
Expand Down Expand Up @@ -90,7 +90,7 @@ def test_detail_view_no_job_application(self, client):
employer = company.members.first()
# Make sure the job seeker infos can be edited by the siae member
approval = ApprovalFactory(user__created_by=employer)
IAEEligibilityDiagnosisFactory(from_prescriber=True, job_seeker=approval.user, author_siae=company)
IAEEligibilityDiagnosisFactory(from_prescriber=True, job_seeker=approval.user)

client.force_login(employer)

Expand Down

0 comments on commit 48a2c83

Please sign in to comment.