Skip to content
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

Tech: Ajout d'une contrainte en base de donnée sur la structure de l'auteur d'un diagnostic d'éligibilité IAE #5489

Merged
merged 1 commit into from
Jan 28, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
1 change: 1 addition & 0 deletions tests/eligibility/factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ 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))
Expand Down
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=True,
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
Loading