Skip to content

Commit

Permalink
apply: Mask sender email when he left his organization
Browse files Browse the repository at this point in the history
  • Loading branch information
tonial committed Dec 3, 2024
1 parent b62830d commit 10c1366
Show file tree
Hide file tree
Showing 4 changed files with 160 additions and 2 deletions.
17 changes: 17 additions & 0 deletions itou/templates/apply/includes/job_application_sender_info.html
Original file line number Diff line number Diff line change
@@ -1,6 +1,21 @@
{% load format_filters %}
{% load matomo %}

{% if job_application_sender_left_org %}
<div class="alert alert-warning alert-dismissible fade show" role="status">
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Fermer"></button>
<div class="row">
<div class="col-auto pe-0">
<i class="ri-information-line ri-xl text-warning" aria-hidden="true"></i>
</div>
<div class="col">
<p class="mb-0">L’émetteur de cette candidature ne fait plus partie de l’organisation émettrice</p>
</div>
</div>
</div>


{% endif %}
<ul class="list-data list-data__two-column-md mb-3">
<li>
<small>Émetteur</small>
Expand All @@ -14,6 +29,8 @@
<small>Adresse e-mail</small>
{% if request.user.is_job_seeker and job_application.sender_kind != SenderKind.JOB_SEEKER %}
<strong>Non communiquée</strong>
{% elif job_application_sender_left_org %}
<div class="text-warning fst-italic">Les réponses seront transmises aux administrateurs de l’organisation</div>
{% else %}
<strong>{{ job_application.sender.email }}</strong>
{% matomo_event "candidature" "clic" "copied_sender_email" as matomo_event_attrs %}
Expand Down
11 changes: 11 additions & 0 deletions itou/www/apply/views/process_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,14 @@ def _get_geiq_eligibility_diagnosis(job_application, only_prescriber):
).first()


def job_application_sender_left_org(job_app):
if org_id := job_app.sender_prescriber_organization_id:
return not job_app.sender.prescribermembership_set.active().filter(organization_id=org_id).exists()
if company_id := job_app.sender_company_id:
return not job_app.sender.companymembership_set.active().filter(company_id=company_id).exists()
return False


@login_required
def details_for_jobseeker(request, job_application_id, template_name="apply/process_details.html"):
"""
Expand Down Expand Up @@ -154,6 +162,7 @@ def details_for_jobseeker(request, job_application_id, template_name="apply/proc
"transition_logs": transition_logs,
"back_url": back_url,
"matomo_custom_title": "Candidature",
"job_application_sender_left_org": job_application_sender_left_org(job_application),
}

return render(request, template_name, context)
Expand Down Expand Up @@ -267,6 +276,7 @@ def details_for_company(request, job_application_id, template_name="apply/proces
PriorActionForm(action_only=True) if job_application.can_change_prior_actions else None
),
"matomo_custom_title": "Candidature",
"job_application_sender_left_org": job_application_sender_left_org(job_application),
}

return render(request, template_name, context)
Expand Down Expand Up @@ -353,6 +363,7 @@ def details_for_prescriber(request, job_application_id, template_name="apply/pro
"refused_by": refused_by,
"refusal_contact_email": refusal_contact_email,
"with_job_seeker_detail_url": True,
"job_application_sender_left_org": job_application_sender_left_org(job_application),
}

return render(request, template_name, context)
Expand Down
36 changes: 34 additions & 2 deletions tests/www/apply/__snapshots__/test_process.ambr
Original file line number Diff line number Diff line change
Expand Up @@ -957,7 +957,7 @@
# ---
# name: TestProcessViews.test_details_for_company_from_approval[job application detail for company]
dict({
'num_queries': 21,
'num_queries': 22,
'queries': list([
dict({
'origin': list([
Expand Down Expand Up @@ -1755,6 +1755,22 @@
LIMIT 1
''',
}),
dict({
'origin': list([
'job_application_sender_left_org[www/apply/views/process_views.py]',
'details_for_company[www/apply/views/process_views.py]',
]),
'sql': '''
SELECT %s AS "a"
FROM "prescribers_prescribermembership"
INNER JOIN "users_user" ON ("prescribers_prescribermembership"."user_id" = "users_user"."id")
WHERE ("prescribers_prescribermembership"."user_id" = %s
AND "users_user"."is_active"
AND "prescribers_prescribermembership"."is_active"
AND "prescribers_prescribermembership"."organization_id" = %s)
LIMIT 1
''',
}),
dict({
'origin': list([
'Company.has_admin[common_apps/organizations/models.py]',
Expand Down Expand Up @@ -2062,7 +2078,7 @@
# ---
# name: TestProcessViews.test_details_for_company_from_list[job application detail for company]
dict({
'num_queries': 21,
'num_queries': 22,
'queries': list([
dict({
'origin': list([
Expand Down Expand Up @@ -2860,6 +2876,22 @@
LIMIT 1
''',
}),
dict({
'origin': list([
'job_application_sender_left_org[www/apply/views/process_views.py]',
'details_for_company[www/apply/views/process_views.py]',
]),
'sql': '''
SELECT %s AS "a"
FROM "prescribers_prescribermembership"
INNER JOIN "users_user" ON ("prescribers_prescribermembership"."user_id" = "users_user"."id")
WHERE ("prescribers_prescribermembership"."user_id" = %s
AND "users_user"."is_active"
AND "prescribers_prescribermembership"."is_active"
AND "prescribers_prescribermembership"."organization_id" = %s)
LIMIT 1
''',
}),
dict({
'origin': list([
'Company.has_admin[common_apps/organizations/models.py]',
Expand Down
98 changes: 98 additions & 0 deletions tests/www/apply/test_process.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
from itou.utils.urls import add_url_params
from itou.utils.widgets import DuetDatePickerWidget
from itou.www.apply.forms import AcceptForm
from itou.www.apply.views.process_views import job_application_sender_left_org
from tests.approvals.factories import (
ApprovalFactory,
SuspensionFactory,
Expand All @@ -61,6 +62,7 @@
PriorActionFactory,
)
from tests.jobs.factories import create_test_romes_and_appellations
from tests.prescribers.factories import PrescriberMembershipFactory
from tests.siae_evaluations.factories import EvaluatedSiaeFactory
from tests.users.factories import EmployerFactory, JobSeekerFactory, LaborInspectorFactory, PrescriberFactory
from tests.utils.htmx.test import assertSoupEqual, update_page_with_htmx
Expand All @@ -76,6 +78,8 @@
"Si les détails apportés dans le message de réponse ne vous ont pas permis d’en savoir plus,"
" vous pouvez contacter l’employeur."
)
SENDER_LEFT_ORG = "Les réponses seront transmises aux administrateurs de l’organisation"
SENDER_LEFT_ORG_ALERT = "L’émetteur de cette candidature ne fait plus partie de l’organisation émettrice"

IAE_CANCELLATION_CONFIRMATION = (
"En validant, <strong>vous renoncez aux aides au poste</strong> liées à cette candidature "
Expand Down Expand Up @@ -287,6 +291,20 @@ def test_details_for_company_with_expired_approval(self, client, subtests):
# Check if approval is displayed
assertion(response, "Numéro de PASS IAE")

def test_details_when_sender_left_org(self, client):
job_application = JobApplicationFactory(sent_by_authorized_prescriber_organisation=True)
company = job_application.to_company
employer = company.members.first()
sender = job_application.sender
sender.prescribermembership_set.update(is_active=False)
client.force_login(employer)

url = reverse("apply:details_for_company", kwargs={"job_application_id": job_application.pk})
response = client.get(url)
assertNotContains(response, sender.email)
assertContains(response, SENDER_LEFT_ORG)
assertContains(response, SENDER_LEFT_ORG_ALERT)

def test_details_archived(self, client):
UNARCHIVE = "Désarchiver"
job_application = JobApplicationFactory(
Expand Down Expand Up @@ -434,6 +452,19 @@ def test_details_for_prescriber(self, client):
assertContains(response, f"<strong>{job_application.to_company.display_name}</strong>")
assertContains(response, reverse("companies_views:card", kwargs={"siae_id": job_application.to_company.pk}))

def test_details_for_prescriber_when_sender_left_org(self, client):
job_application = JobApplicationFactory(sent_by_authorized_prescriber_organisation=True)
prescriber = PrescriberMembershipFactory(organization=job_application.sender_prescriber_organization).user
sender = job_application.sender
sender.prescribermembership_set.update(is_active=False)
client.force_login(prescriber)

url = reverse("apply:details_for_prescriber", kwargs={"job_application_id": job_application.pk})
response = client.get(url)
assertNotContains(response, sender.email)
assertContains(response, SENDER_LEFT_ORG)
assertContains(response, SENDER_LEFT_ORG_ALERT)

def test_details_for_prescriber_as_company_when_i_am_not_the_sender(self, client):
job_application = JobApplicationFactory(sent_by_authorized_prescriber_organisation=True)
employer = job_application.to_company.members.first()
Expand Down Expand Up @@ -515,6 +546,19 @@ def test_details_for_job_seeker(self, client, snapshot):
assertContains(response, f"<strong>{job_application.to_company.display_name}</strong>")
assertContains(response, reverse("companies_views:card", kwargs={"siae_id": job_application.to_company.pk}))

def test_details_for_job_seeker_when_sender_left_org(self, client):
job_application = JobApplicationFactory(sent_by_authorized_prescriber_organisation=True)
sender = job_application.sender
sender.prescribermembership_set.update(is_active=False)

client.force_login(job_application.job_seeker)

url = reverse("apply:details_for_jobseeker", kwargs={"job_application_id": job_application.pk})
response = client.get(url)
assertNotContains(response, sender.email)
assertNotContains(response, SENDER_LEFT_ORG) # A job seeker never sees the email of the sender
assertContains(response, SENDER_LEFT_ORG_ALERT)

def test_details_for_job_seeker_as_other_user(self, client, subtests):
job_application = JobApplicationFactory()
url = reverse("apply:details_for_jobseeker", kwargs={"job_application_id": job_application.pk})
Expand Down Expand Up @@ -3824,3 +3868,57 @@ def test_htmx_reload_contract_type_and_options(client):
response = client.post(accept_url, data=data)
reloaded_form_soup = parse_response_to_soup(response, selector="#acceptForm")
assertSoupEqual(form_soup, reloaded_form_soup)


class TestJobApplicationSenderLeftOrg:
def test_sender_left_org_prescriber(self):
prescriber_membership = PrescriberMembershipFactory()
job_app = JobApplicationFactory(
sender=prescriber_membership.user, sender_prescriber_organization=prescriber_membership.organization
)
assert job_application_sender_left_org(job_app) is False

# membership is inactive
prescriber_membership.is_active = False
prescriber_membership.save(update_fields=["is_active"])
assert job_application_sender_left_org(job_app) is True

# prescriber is inactive
prescriber_membership.is_active = True
prescriber_membership.save(update_fields=["is_active"])
prescriber_membership.user.is_active = False
prescriber_membership.user.save(update_fields=["is_active"])
assert job_application_sender_left_org(job_app) is True

# membership was removed
prescriber_membership.user.is_active = True
prescriber_membership.user.save(update_fields=["is_active"])
prescriber_membership.delete()
assert job_application_sender_left_org(job_app) is True

def test_sender_left_org_employer(self):
company_membership = CompanyMembershipFactory()
job_app = JobApplicationFactory(sender=company_membership.user, sender_company=company_membership.company)
assert job_application_sender_left_org(job_app) is False

# membership is inactive
company_membership.is_active = False
company_membership.save(update_fields=["is_active"])
assert job_application_sender_left_org(job_app) is True

# prescriber is inactive
company_membership.is_active = True
company_membership.save(update_fields=["is_active"])
company_membership.user.is_active = False
company_membership.user.save(update_fields=["is_active"])
assert job_application_sender_left_org(job_app) is True

# membership was removed
company_membership.user.is_active = True
company_membership.user.save(update_fields=["is_active"])
company_membership.delete()
assert job_application_sender_left_org(job_app) is True

def test_sender_left_org_job_seeker(self):
job_app = JobApplicationSentByJobSeekerFactory()
assert job_application_sender_left_org(job_app) is False

0 comments on commit 10c1366

Please sign in to comment.