Beispiel #1
0
    def test_accept_after_cancel(self, *args, **kwargs):
        # A canceled job application is not linked to an approval
        # unless the job seeker has an accepted job application.
        create_test_cities(["54", "57"], num_per_department=2)
        city = City.objects.first()
        job_seeker = JobSeekerWithAddressFactory(city=city.name)
        job_application = JobApplicationSentByJobSeekerFactory(
            state=JobApplicationWorkflow.STATE_CANCELLED, job_seeker=job_seeker
        )
        siae_user = job_application.to_siae.members.first()
        self.client.login(username=siae_user.email, password=DEFAULT_PASSWORD)

        url_accept = reverse("apply:accept", kwargs={"job_application_id": job_application.pk})
        hiring_start_at = timezone.localdate()
        hiring_end_at = Approval.get_default_end_date(hiring_start_at)
        post_data = {
            "hiring_start_at": hiring_start_at.strftime(DuetDatePickerWidget.INPUT_DATE_FORMAT),
            "hiring_end_at": hiring_end_at.strftime(DuetDatePickerWidget.INPUT_DATE_FORMAT),
            "pole_emploi_id": job_application.job_seeker.pole_emploi_id,
            "answer": "",
            "address_line_1": job_seeker.address_line_1,
            "post_code": job_seeker.post_code,
            "city": city.name,
            "city_slug": city.slug,
        }
        response = self.client.post(url_accept, data=post_data)

        next_url = reverse("apply:details_for_siae", kwargs={"job_application_id": job_application.pk})
        self.assertRedirects(response, next_url)

        job_application.refresh_from_db()
        self.assertEqual(job_seeker.approvals.count(), 1)
        approval = job_seeker.approvals.first()
        self.assertEqual(approval.start_at, job_application.hiring_start_at)
        self.assertTrue(job_application.state.is_accepted)
Beispiel #2
0
    def test_get_unique_fk_objects(self):
        # Create 3 job applications for 2 candidates to check
        # that `get_unique_fk_objects` returns 2 candidates.
        JobApplicationSentByJobSeekerFactory()
        job_seeker = JobSeekerFactory()
        JobApplicationSentByJobSeekerFactory.create_batch(2, job_seeker=job_seeker)

        unique_job_seekers = JobApplication.objects.get_unique_fk_objects("job_seeker")

        self.assertEqual(JobApplication.objects.count(), 3)
        self.assertEqual(len(unique_job_seekers), 2)
        self.assertEqual(type(unique_job_seekers[0]), get_user_model())
Beispiel #3
0
    def test_eligibility_state_for_job_application(self, *args, **kwargs):
        """The eligibility diagnosis page must only be accessible
        in JobApplicationWorkflow.CAN_BE_ACCEPTED_STATES states."""
        siae = SiaeWithMembershipFactory()
        siae_user = siae.members.first()
        job_application = JobApplicationSentByJobSeekerFactory(to_siae=siae)

        # Right states
        for state in JobApplicationWorkflow.CAN_BE_ACCEPTED_STATES:
            job_application.state = state
            job_application.save()
            self.client.login(username=siae_user.email, password=DEFAULT_PASSWORD)
            url = reverse("apply:eligibility", kwargs={"job_application_id": job_application.pk})
            response = self.client.get(url)
            self.assertEqual(response.status_code, 200)
            self.client.logout()

        # Wrong state
        job_application.state = JobApplicationWorkflow.STATE_ACCEPTED
        job_application.save()
        self.client.login(username=siae_user.email, password=DEFAULT_PASSWORD)
        url = reverse("apply:eligibility", kwargs={"job_application_id": job_application.pk})
        response = self.client.get(url)
        self.assertEqual(response.status_code, 404)
        self.client.logout()
Beispiel #4
0
    def test_created_in_past_hours(self):

        now = timezone.now()
        hours_ago_10 = now - timezone.timedelta(hours=10)
        hours_ago_20 = now - timezone.timedelta(hours=20)
        hours_ago_30 = now - timezone.timedelta(hours=30)

        JobApplicationSentByJobSeekerFactory(created_at=hours_ago_10)
        JobApplicationSentByJobSeekerFactory(created_at=hours_ago_20)
        JobApplicationSentByJobSeekerFactory(created_at=hours_ago_30)

        self.assertEqual(JobApplication.objects.created_in_past_hours(5).count(), 0)
        self.assertEqual(JobApplication.objects.created_in_past_hours(15).count(), 1)
        self.assertEqual(JobApplication.objects.created_in_past_hours(25).count(), 2)
        self.assertEqual(JobApplication.objects.created_in_past_hours(35).count(), 3)
Beispiel #5
0
    def test_refuse(self):

        # When sent by authorized prescriber.
        job_application = JobApplicationSentByAuthorizedPrescriberOrganizationFactory(
            refusal_reason=JobApplication.REFUSAL_REASON_DID_NOT_COME
        )
        email = job_application.email_refuse
        # To.
        self.assertIn(job_application.job_seeker.email, email.to)
        self.assertIn(job_application.sender.email, email.to)
        self.assertEqual(len(email.to), 2)
        # Body.
        self.assertIn(job_application.sender.first_name, email.body)
        self.assertIn(job_application.sender.last_name, email.body)
        self.assertIn(job_application.job_seeker.first_name, email.body)
        self.assertIn(job_application.job_seeker.last_name, email.body)
        self.assertIn(job_application.to_siae.display_name, email.body)
        self.assertIn(job_application.answer, email.body)

        # When sent by jobseeker.
        job_application = JobApplicationSentByJobSeekerFactory(
            refusal_reason=JobApplication.REFUSAL_REASON_DID_NOT_COME
        )
        email = job_application.email_refuse
        # To.
        self.assertEqual(job_application.job_seeker.email, job_application.sender.email)
        self.assertIn(job_application.job_seeker.email, email.to)
        self.assertEqual(len(email.to), 1)
        # Body.
        self.assertIn(job_application.to_siae.display_name, email.body)
        self.assertIn(job_application.answer, email.body)
Beispiel #6
0
    def test_accept(self):

        # When sent by authorized prescriber.
        job_application = JobApplicationSentByAuthorizedPrescriberOrganizationFactory()
        email = job_application.email_accept
        # To.
        self.assertIn(job_application.job_seeker.email, email.to)
        self.assertIn(job_application.sender.email, email.to)
        self.assertEqual(len(email.to), 2)
        # Body.
        self.assertIn(job_application.sender.first_name, email.body)
        self.assertIn(job_application.sender.last_name, email.body)
        self.assertIn(job_application.job_seeker.first_name, email.body)
        self.assertIn(job_application.job_seeker.last_name, email.body)
        self.assertIn(job_application.to_siae.display_name, email.body)
        self.assertIn(job_application.answer, email.body)

        # When sent by jobseeker.
        job_application = JobApplicationSentByJobSeekerFactory()
        email = job_application.email_accept
        # To.
        self.assertEqual(job_application.job_seeker.email, job_application.sender.email)
        self.assertIn(job_application.job_seeker.email, email.to)
        self.assertEqual(len(email.to), 1)
        # Body.
        self.assertIn(job_application.to_siae.display_name, email.body)
        self.assertIn(job_application.answer, email.body)
Beispiel #7
0
 def test_accept_job_application_sent_by_job_seeker_with_forgotten_pole_emploi_id(
         self):
     job_seeker = JobSeekerFactory(
         pole_emploi_id="",
         lack_of_pole_emploi_id_reason=JobSeekerFactory._meta.model.
         REASON_FORGOTTEN)
     job_application = JobApplicationSentByJobSeekerFactory(
         job_seeker=job_seeker,
         state=JobApplicationWorkflow.STATE_PROCESSING)
     job_application.accept(user=job_application.to_siae.members.first())
     self.assertIsNone(job_application.approval)
     # This will be set only after the effective approval delivery.
     self.assertEqual(job_application.approval_delivery_mode, "")
     # Check sent email.
     self.assertEqual(len(mail.outbox), 2)
     self.assertIn("Candidature acceptée", mail.outbox[0].subject)
     self.assertIn("PASS IAE requis sur Itou", mail.outbox[1].subject)
Beispiel #8
0
 def test_accept_job_application_sent_by_job_seeker_with_valid_approval(
         self):
     job_seeker = JobSeekerFactory()
     pe_approval = PoleEmploiApprovalFactory(
         pole_emploi_id=job_seeker.pole_emploi_id,
         birthdate=job_seeker.birthdate)
     job_application = JobApplicationSentByJobSeekerFactory(
         job_seeker=job_seeker,
         state=JobApplicationWorkflow.STATE_PROCESSING)
     job_application.accept(user=job_application.to_siae.members.first())
     self.assertIsNotNone(job_application.approval)
     self.assertEqual(job_application.approval.number, pe_approval.number)
     self.assertTrue(job_application.approval_number_sent_by_email)
     self.assertEqual(job_application.approval_delivery_mode,
                      job_application.APPROVAL_DELIVERY_MODE_AUTOMATIC)
     # Check sent email.
     self.assertEqual(len(mail.outbox), 2)
     self.assertIn("Candidature acceptée", mail.outbox[0].subject)
     self.assertIn("Délivrance d'un PASS IAE pour", mail.outbox[1].subject)
Beispiel #9
0
    def test_accept_with_manual_approval_delivery(self, *args, **kwargs):
        """
        Test the "manual approval delivery mode" path of the view.
        """
        create_test_cities(["57"], num_per_department=1)
        city = City.objects.first()

        job_application = JobApplicationSentByJobSeekerFactory(
            state=JobApplicationWorkflow.STATE_PROCESSING,
            # The state of the 2 `pole_emploi_*` fields will trigger a manual delivery.
            job_seeker__pole_emploi_id="",
            job_seeker__lack_of_pole_emploi_id_reason=User.REASON_FORGOTTEN,
        )

        siae_user = job_application.to_siae.members.first()
        self.client.login(username=siae_user.email, password=DEFAULT_PASSWORD)

        url = reverse("apply:accept", kwargs={"job_application_id": job_application.pk})
        response = self.client.get(url)
        self.assertEqual(response.status_code, 200)

        post_data = {
            # Data for `JobSeekerPoleEmploiStatusForm`.
            "pole_emploi_id": job_application.job_seeker.pole_emploi_id,
            "lack_of_pole_emploi_id_reason": job_application.job_seeker.lack_of_pole_emploi_id_reason,
            # Data for `UserAddressForm`.
            "address_line_1": "11 rue des Lilas",
            "post_code": "57000",
            "city": city.name,
            "city_slug": city.slug,
            # Data for `AcceptForm`.
            "hiring_start_at": timezone.localdate().strftime(DuetDatePickerWidget.INPUT_DATE_FORMAT),
            "hiring_end_at": (timezone.localdate() + relativedelta(days=360)).strftime(
                DuetDatePickerWidget.INPUT_DATE_FORMAT
            ),
            "answer": "",
        }
        response = self.client.post(url, data=post_data)
        next_url = reverse("apply:details_for_siae", kwargs={"job_application_id": job_application.pk})
        self.assertRedirects(response, next_url)

        job_application.refresh_from_db()
        self.assertEqual(job_application.approval_delivery_mode, job_application.APPROVAL_DELIVERY_MODE_MANUAL)
Beispiel #10
0
    def test_details_for_siae(self, *args, **kwargs):
        """Display the details of a job application."""

        job_application = JobApplicationSentByAuthorizedPrescriberOrganizationFactory()
        siae = job_application.to_siae
        siae_user = siae.members.first()
        self.client.login(username=siae_user.email, password=DEFAULT_PASSWORD)

        url = reverse("apply:details_for_siae", kwargs={"job_application_id": job_application.pk})
        response = self.client.get(url)
        self.assertFalse(job_application.has_editable_job_seeker)
        self.assertContains(response, "Ce candidat a pris le contrôle de son compte utilisateur.")

        job_application.job_seeker.created_by = siae_user
        job_application.job_seeker.save()

        url = reverse("apply:details_for_siae", kwargs={"job_application_id": job_application.pk})
        response = self.client.get(url)
        self.assertTrue(job_application.has_editable_job_seeker)
        self.assertContains(response, "Modifier les informations")

        # Test resume presence:
        # 1/ Job seeker has a personal resume (technical debt).
        resume_link = "https://server.com/rockie-balboa.pdf"
        job_application = JobApplicationSentByJobSeekerFactory(
            job_seeker__resume_link=resume_link, resume_link="", to_siae=siae
        )
        url = reverse("apply:details_for_siae", kwargs={"job_application_id": job_application.pk})
        response = self.client.get(url)
        self.assertContains(response, resume_link)

        # 2/ Job application was sent with an attached resume
        resume_link = "https://server.com/rockie-balboa.pdf"
        job_application = JobApplicationSentByJobSeekerFactory(to_siae=siae)
        url = reverse("apply:details_for_siae", kwargs={"job_application_id": job_application.pk})
        response = self.client.get(url)
        self.assertContains(response, resume_link)
Beispiel #11
0
    def test_new_for_job_seeker(self):
        job_application = JobApplicationSentByJobSeekerFactory(
            selected_jobs=Appellation.objects.all())
        email = job_application.email_new_for_job_seeker(
            base_url="http://testserver")
        # To.
        self.assertIn(job_application.sender.email, email.to)
        self.assertEqual(len(email.to), 1)
        self.assertEqual(job_application.sender_kind,
                         JobApplication.SENDER_KIND_JOB_SEEKER)

        # Subject
        self.assertIn(job_application.to_siae.display_name, email.subject)

        # Body.
        self.assertIn(job_application.job_seeker.first_name, email.body)
        self.assertIn(job_application.job_seeker.last_name, email.body)
        self.assertIn(
            job_application.job_seeker.birthdate.strftime("%d/%m/%Y"),
            email.body)
        self.assertIn(job_application.job_seeker.email, email.body)
        self.assertIn(
            format_filters.format_phone(job_application.job_seeker.phone),
            email.body)
        self.assertIn(job_application.message, email.body)
        for job in job_application.selected_jobs.all():
            self.assertIn(job.display_name, email.body)
        self.assertIn(job_application.sender.first_name, email.body)
        self.assertIn(job_application.sender.last_name, email.body)
        self.assertIn(job_application.sender.email, email.body)
        self.assertIn(
            format_filters.format_phone(job_application.sender.phone),
            email.body)
        self.assertIn(job_application.to_siae.display_name, email.body)
        self.assertIn(reverse("account_login"), email.body)
        self.assertIn(reverse("account_reset_password"), email.body)
Beispiel #12
0
    def test_is_sent_by_authorized_prescriber(self):
        job_application = JobApplicationSentByJobSeekerFactory()
        self.assertFalse(job_application.is_sent_by_authorized_prescriber)

        job_application = JobApplicationSentByPrescriberFactory()
        self.assertFalse(job_application.is_sent_by_authorized_prescriber)

        job_application = JobApplicationSentByPrescriberOrganizationFactory()
        self.assertFalse(job_application.is_sent_by_authorized_prescriber)

        job_application = JobApplicationSentBySiaeFactory()
        self.assertFalse(job_application.is_sent_by_authorized_prescriber)

        job_application = JobApplicationSentByAuthorizedPrescriberOrganizationFactory()
        self.assertTrue(job_application.is_sent_by_authorized_prescriber)
Beispiel #13
0
 def test_eligibility_wrong_state_for_job_application(self):
     """The eligibility diagnosis page must only be accessible in `STATE_PROCESSING`."""
     for state in [
             JobApplicationWorkflow.STATE_POSTPONED,
             JobApplicationWorkflow.STATE_ACCEPTED,
             JobApplicationWorkflow.STATE_REFUSED,
             JobApplicationWorkflow.STATE_OBSOLETE,
     ]:
         job_application = JobApplicationSentByJobSeekerFactory(state=state)
         siae_user = job_application.to_siae.members.first()
         self.client.login(username=siae_user.email,
                           password=DEFAULT_PASSWORD)
         url = reverse("apply:eligibility",
                       kwargs={"job_application_id": job_application.pk})
         response = self.client.get(url)
         self.assertEqual(response.status_code, 404)
         self.client.logout()
Beispiel #14
0
    def test_cancel(self):
        job_application = JobApplicationSentByAuthorizedPrescriberOrganizationFactory(
            state=JobApplicationWorkflow.STATE_ACCEPTED)

        cancellation_user = job_application.to_siae.active_members.first()
        email = job_application.email_cancel(cancelled_by=cancellation_user)
        # To.
        self.assertIn(cancellation_user.email, email.to)
        self.assertIn(job_application.sender.email, email.bcc)
        self.assertEqual(len(email.to), 1)
        self.assertEqual(len(email.bcc), 1)
        # Body.
        self.assertIn("annulée", email.body)
        self.assertIn(job_application.sender.first_name, email.body)
        self.assertIn(job_application.sender.last_name, email.body)
        self.assertIn(job_application.job_seeker.first_name, email.body)
        self.assertIn(job_application.job_seeker.last_name, email.body)

        # When sent by jobseeker.
        job_application = JobApplicationSentByJobSeekerFactory(
            state=JobApplicationWorkflow.STATE_ACCEPTED)
        email = job_application.email_cancel(cancelled_by=cancellation_user)
        # To.
        self.assertFalse(email.bcc)
Beispiel #15
0
    def test_deduplicate_job_seekers_without_empty_sender_field(self):
        """
        Easy case: among all the duplicates, only one has a PASS IAE.
        Ensure that the `sender` field is never left empty.
        """

        # Attributes shared by all users.
        # Deduplication is based on these values.
        kwargs = {
            "job_seeker__pole_emploi_id": "6666666B",
            "job_seeker__birthdate": datetime.date(2002, 12, 12),
        }

        # Create `user1` through a job application sent by him.
        job_app1 = JobApplicationSentByJobSeekerFactory(job_seeker__nir=None,
                                                        **kwargs)
        user1 = job_app1.job_seeker

        self.assertEqual(1, user1.job_applications.count())
        self.assertEqual(job_app1.sender, user1)

        # Create `user2` through a job application sent by him.
        job_app2 = JobApplicationSentByJobSeekerFactory(job_seeker__nir=None,
                                                        **kwargs)
        user2 = job_app2.job_seeker

        self.assertEqual(1, user2.job_applications.count())
        self.assertEqual(job_app2.sender, user2)

        # Create `user3` through a job application sent by a prescriber.
        job_app3 = JobApplicationWithEligibilityDiagnosis(job_seeker__nir=None,
                                                          **kwargs)
        user3 = job_app3.job_seeker
        self.assertNotEqual(job_app3.sender, user3)
        job_app3_sender = job_app3.sender  # The sender is a prescriber.

        # Ensure that `user1` will always be the target into which duplicates will be merged
        # by attaching a PASS IAE to him.
        self.assertEqual(0, user1.approvals.count())
        self.assertEqual(0, user2.approvals.count())
        self.assertEqual(0, user3.approvals.count())
        ApprovalFactory(user=user1)

        # Merge all users into `user1`.
        call_command("deduplicate_job_seekers", verbosity=0, no_csv=True)

        self.assertEqual(3, user1.job_applications.count())

        job_app1.refresh_from_db()
        job_app2.refresh_from_db()
        job_app3.refresh_from_db()

        self.assertEqual(job_app1.sender, user1)
        self.assertEqual(job_app2.sender,
                         user1)  # The sender must now be user1.
        self.assertEqual(
            job_app3.sender,
            job_app3_sender)  # The sender must still be a prescriber.

        self.assertEqual(0, User.objects.filter(email=user2.email).count())
        self.assertEqual(0, User.objects.filter(email=user3.email).count())

        self.assertEqual(
            0,
            JobApplication.objects.filter(job_seeker=user2).count())
        self.assertEqual(
            0,
            JobApplication.objects.filter(job_seeker=user3).count())

        self.assertEqual(
            0,
            EligibilityDiagnosis.objects.filter(job_seeker=user2).count())
        self.assertEqual(
            0,
            EligibilityDiagnosis.objects.filter(job_seeker=user3).count())
Beispiel #16
0
    def test_accept_with_active_suspension(self, *args, **kwargs):
        """Test the `accept` transition with active suspension for active user"""
        create_test_cities(["54", "57"], num_per_department=2)
        city = City.objects.first()
        today = timezone.localdate()
        # the old job of job seeker
        job_seeker_user = JobSeekerWithAddressFactory()
        old_job_application = JobApplicationWithApprovalFactory(
            state=JobApplicationWorkflow.STATE_ACCEPTED,
            job_seeker=job_seeker_user,
            # Ensure that the old_job_application cannot be canceled.
            hiring_start_at=today - relativedelta(days=100),
        )
        # create suspension for the job seeker
        approval_job_seeker = old_job_application.approval
        siae_user = old_job_application.to_siae.members.first()
        susension_start_at = today
        suspension_end_at = today + relativedelta(days=50)

        SuspensionFactory(
            approval=approval_job_seeker,
            start_at=susension_start_at,
            end_at=suspension_end_at,
            created_by=siae_user,
            reason=Suspension.Reason.BROKEN_CONTRACT.value,
        )

        # Now, another Siae wants to hire the job seeker
        other_siae = SiaeWithMembershipFactory()
        job_application = JobApplicationSentByJobSeekerFactory(
            approval=approval_job_seeker,
            state=JobApplicationWorkflow.STATE_PROCESSING,
            job_seeker=job_seeker_user,
            to_siae=other_siae,
        )
        other_siae_user = job_application.to_siae.members.first()

        # login with other siae
        self.client.login(username=other_siae_user.email, password=DEFAULT_PASSWORD)
        url = reverse("apply:accept", kwargs={"job_application_id": job_application.pk})

        hiring_start_at = today + relativedelta(days=20)
        hiring_end_at = Approval.get_default_end_date(hiring_start_at)

        post_data = {
            # Data for `JobSeekerPoleEmploiStatusForm`.
            "pole_emploi_id": job_application.job_seeker.pole_emploi_id,
            # Data for `AcceptForm`.
            "hiring_start_at": hiring_start_at.strftime(DuetDatePickerWidget.INPUT_DATE_FORMAT),
            "hiring_end_at": hiring_end_at.strftime(DuetDatePickerWidget.INPUT_DATE_FORMAT),
            "answer": "",
            "address_line_1": job_seeker_user.address_line_1,
            "post_code": job_seeker_user.post_code,
            "city": city.name,
            "city_slug": city.slug,
        }
        response = self.client.post(url, data=post_data)
        self.assertEqual(response.status_code, 302)
        get_job_application = JobApplication.objects.get(pk=job_application.pk)
        g_suspension = get_job_application.approval.suspension_set.in_progress().last()

        # The end date of suspension is set to d-1 of hiring start day
        self.assertEqual(g_suspension.end_at, get_job_application.hiring_start_at - relativedelta(days=1))
        # Check if the duration of approval was updated correctly
        self.assertEqual(
            get_job_application.approval.end_at,
            approval_job_seeker.end_at + relativedelta(days=(g_suspension.end_at - g_suspension.start_at).days),
        )
Beispiel #17
0
    def test_accept_and_update_hiring_start_date_of_two_job_applications(self, *args, **kwargs):
        create_test_cities(["54", "57"], num_per_department=2)
        city = City.objects.first()
        job_seeker = JobSeekerWithAddressFactory()
        base_for_post_data = {
            "address_line_1": job_seeker.address_line_1,
            "post_code": job_seeker.post_code,
            "city": city.name,
            "city_slug": city.slug,
            "pole_emploi_id": job_seeker.pole_emploi_id,
            "answer": "",
        }
        hiring_start_at = timezone.localdate() + relativedelta(months=2)
        hiring_end_at = hiring_start_at + relativedelta(months=2)
        approval_default_ending = Approval.get_default_end_date(start_at=hiring_start_at)

        # Send 3 job applications to 3 different structures
        job_application = JobApplicationSentByJobSeekerFactory(
            job_seeker=job_seeker, state=JobApplicationWorkflow.STATE_PROCESSING
        )
        job_app_starting_earlier = JobApplicationSentByJobSeekerFactory(
            job_seeker=job_seeker, state=JobApplicationWorkflow.STATE_PROCESSING
        )
        job_app_starting_later = JobApplicationSentByJobSeekerFactory(
            job_seeker=job_seeker, state=JobApplicationWorkflow.STATE_PROCESSING
        )

        # SIAE 1 logs in and accepts the first job application.
        # The delivered approval should start at the same time as the contract.
        user = job_application.to_siae.members.first()
        self.client.login(username=user.email, password=DEFAULT_PASSWORD)
        url_accept = reverse("apply:accept", kwargs={"job_application_id": job_application.pk})
        post_data = {
            "hiring_start_at": hiring_start_at.strftime(DuetDatePickerWidget.INPUT_DATE_FORMAT),
            "hiring_end_at": hiring_end_at.strftime(DuetDatePickerWidget.INPUT_DATE_FORMAT),
            **base_for_post_data,
        }
        response = self.client.post(url_accept, data=post_data)
        self.assertEqual(response.status_code, 302)

        # First job application has been accepted.
        # All other job applications are obsolete.
        job_application.refresh_from_db()
        self.assertTrue(job_application.state.is_accepted)
        self.assertEqual(job_application.approval.start_at, job_application.hiring_start_at)
        self.assertEqual(job_application.approval.end_at, approval_default_ending)
        self.client.logout()

        # SIAE 2 accepts the second job application
        # but its contract starts earlier than the approval delivered the first time.
        # Approval's starting date should be brought forward.
        user = job_app_starting_earlier.to_siae.members.first()
        hiring_start_at = hiring_start_at - relativedelta(months=1)
        hiring_end_at = hiring_start_at + relativedelta(months=2)
        approval_default_ending = Approval.get_default_end_date(start_at=hiring_start_at)
        job_app_starting_earlier.refresh_from_db()
        self.assertTrue(job_app_starting_earlier.state.is_obsolete)

        self.client.login(username=user.email, password=DEFAULT_PASSWORD)
        url_accept = reverse("apply:accept", kwargs={"job_application_id": job_app_starting_earlier.pk})
        post_data = {
            "hiring_start_at": hiring_start_at.strftime(DuetDatePickerWidget.INPUT_DATE_FORMAT),
            "hiring_end_at": hiring_end_at.strftime(DuetDatePickerWidget.INPUT_DATE_FORMAT),
            **base_for_post_data,
        }
        response = self.client.post(url_accept, data=post_data)
        job_app_starting_earlier.refresh_from_db()

        # Second job application has been accepted.
        # The job seeker has now two part-time jobs at the same time.
        self.assertEqual(response.status_code, 302)
        self.assertTrue(job_app_starting_earlier.state.is_accepted)
        self.assertEqual(job_app_starting_earlier.approval.start_at, job_app_starting_earlier.hiring_start_at)
        self.assertEqual(job_app_starting_earlier.approval.end_at, approval_default_ending)
        self.client.logout()

        # SIAE 3 accepts the third job application.
        # Its contract starts later than the corresponding approval.
        # Approval's starting date should not be updated.
        user = job_app_starting_later.to_siae.members.first()
        hiring_start_at = hiring_start_at + relativedelta(months=6)
        hiring_end_at = hiring_start_at + relativedelta(months=2)
        job_app_starting_later.refresh_from_db()
        self.assertTrue(job_app_starting_later.state.is_obsolete)

        self.client.login(username=user.email, password=DEFAULT_PASSWORD)
        url_accept = reverse("apply:accept", kwargs={"job_application_id": job_app_starting_later.pk})
        post_data = {
            "hiring_start_at": hiring_start_at.strftime(DuetDatePickerWidget.INPUT_DATE_FORMAT),
            "hiring_end_at": hiring_end_at.strftime(DuetDatePickerWidget.INPUT_DATE_FORMAT),
            **base_for_post_data,
        }
        response = self.client.post(url_accept, data=post_data)
        job_app_starting_later.refresh_from_db()

        # Third job application has been accepted.
        # The job seeker has now three part-time jobs at the same time.
        self.assertEqual(response.status_code, 302)
        self.assertTrue(job_app_starting_later.state.is_accepted)
        self.assertEqual(job_app_starting_later.approval.start_at, job_app_starting_earlier.hiring_start_at)
Beispiel #18
0
    def test_accept(self, *args, **kwargs):
        """Test the `accept` transition."""
        create_test_cities(["54", "57"], num_per_department=2)
        city = City.objects.first()
        today = timezone.localdate()

        job_seeker = JobSeekerWithAddressFactory(city=city.name)
        address = {
            "address_line_1": job_seeker.address_line_1,
            "post_code": job_seeker.post_code,
            "city": city.name,
            "city_slug": city.slug,
        }
        siae = SiaeWithMembershipFactory()
        siae_user = siae.members.first()

        for state in JobApplicationWorkflow.CAN_BE_ACCEPTED_STATES:
            job_application = JobApplicationSentByJobSeekerFactory(state=state, job_seeker=job_seeker, to_siae=siae)
            self.client.login(username=siae_user.email, password=DEFAULT_PASSWORD)

            url = reverse("apply:accept", kwargs={"job_application_id": job_application.pk})
            response = self.client.get(url)
            self.assertEqual(response.status_code, 200)

            # Good duration.
            hiring_start_at = today
            hiring_end_at = Approval.get_default_end_date(hiring_start_at)
            post_data = {
                # Data for `JobSeekerPoleEmploiStatusForm`.
                "pole_emploi_id": job_application.job_seeker.pole_emploi_id,
                # Data for `AcceptForm`.
                "hiring_start_at": hiring_start_at.strftime(DuetDatePickerWidget.INPUT_DATE_FORMAT),
                "hiring_end_at": hiring_end_at.strftime(DuetDatePickerWidget.INPUT_DATE_FORMAT),
                "answer": "",
                **address,
            }
            response = self.client.post(url, data=post_data)
            next_url = reverse("apply:details_for_siae", kwargs={"job_application_id": job_application.pk})
            self.assertRedirects(response, next_url)

            job_application = JobApplication.objects.get(pk=job_application.pk)
            self.assertEqual(job_application.hiring_start_at, hiring_start_at)
            self.assertEqual(job_application.hiring_end_at, hiring_end_at)
            self.assertTrue(job_application.state.is_accepted)

        ##############
        # Exceptions #
        ##############
        job_application = JobApplicationSentByJobSeekerFactory(state=state, job_seeker=job_seeker, to_siae=siae)
        url = reverse("apply:accept", kwargs={"job_application_id": job_application.pk})

        # Wrong dates.
        hiring_start_at = today
        hiring_end_at = Approval.get_default_end_date(hiring_start_at)
        # Force `hiring_start_at` in past.
        hiring_start_at = hiring_start_at - relativedelta(days=1)
        post_data = {
            "hiring_start_at": hiring_start_at.strftime(DuetDatePickerWidget.INPUT_DATE_FORMAT),
            "hiring_end_at": hiring_end_at.strftime(DuetDatePickerWidget.INPUT_DATE_FORMAT),
            "answer": "",
            **address,
        }
        response = self.client.post(url, data=post_data)
        self.assertFormError(response, "form_accept", "hiring_start_at", JobApplication.ERROR_START_IN_PAST)

        # Wrong dates: end < start.
        hiring_start_at = today
        hiring_end_at = hiring_start_at - relativedelta(days=1)
        post_data = {
            "hiring_start_at": hiring_start_at.strftime(DuetDatePickerWidget.INPUT_DATE_FORMAT),
            "hiring_end_at": hiring_end_at.strftime(DuetDatePickerWidget.INPUT_DATE_FORMAT),
            "answer": "",
            **address,
        }
        response = self.client.post(url, data=post_data)
        self.assertFormError(response, "form_accept", None, JobApplication.ERROR_END_IS_BEFORE_START)

        # No address provided.
        job_application = JobApplicationSentByJobSeekerFactory(
            state=JobApplicationWorkflow.STATE_PROCESSING, to_siae=siae
        )
        url = reverse("apply:accept", kwargs={"job_application_id": job_application.pk})

        hiring_start_at = today
        hiring_end_at = Approval.get_default_end_date(hiring_start_at)
        post_data = {
            # Data for `JobSeekerPoleEmploiStatusForm`.
            "pole_emploi_id": job_application.job_seeker.pole_emploi_id,
            # Data for `AcceptForm`.
            "hiring_start_at": hiring_start_at.strftime(DuetDatePickerWidget.INPUT_DATE_FORMAT),
            "hiring_end_at": hiring_end_at.strftime(DuetDatePickerWidget.INPUT_DATE_FORMAT),
            "answer": "",
        }
        response = self.client.post(url, data=post_data)
        self.assertFormError(response, "form_user_address", "address_line_1", "Ce champ est obligatoire.")
        self.assertFormError(response, "form_user_address", "city", "Ce champ est obligatoire.")
        self.assertFormError(response, "form_user_address", "post_code", "Ce champ est obligatoire.")
Beispiel #19
0
    def test_manually_add_approval(self):
        user = UserFactory()
        self.client.login(username=user.email, password=DEFAULT_PASSWORD)

        # When a Pôle emploi ID has been forgotten, an approval must be delivered
        # with a manual verification.
        job_seeker = JobSeekerFactory(
            pole_emploi_id="", lack_of_pole_emploi_id_reason=JobSeekerFactory._meta.model.REASON_FORGOTTEN
        )
        job_application = JobApplicationSentByJobSeekerFactory(
            job_seeker=job_seeker,
            state=JobApplicationWorkflow.STATE_PROCESSING,
            approval=None,
            approval_number_sent_by_email=False,
        )
        job_application.accept(user=job_application.to_siae.members.first())

        # Delete emails sent by previous transition.
        mail.outbox = []

        url = reverse("admin:approvals_approval_manually_add_approval", args=[job_application.pk])

        # Not enough perms.
        response = self.client.get(url)
        self.assertEqual(response.status_code, 302)

        user.is_staff = True
        user.save()
        content_type = ContentType.objects.get_for_model(Approval)
        permission = Permission.objects.get(content_type=content_type, codename="add_approval")
        user.user_permissions.add(permission)

        # With good perms.
        response = self.client.get(url)
        self.assertEqual(response.status_code, 200)

        # Create an approval.
        post_data = {
            "start_at": job_application.hiring_start_at.strftime("%d/%m/%Y"),
            "end_at": job_application.hiring_end_at.strftime("%d/%m/%Y"),
            "number": "400121910144",
            "user": job_application.job_seeker.pk,
            "created_by": user.pk,
        }
        response = self.client.post(url, data=post_data)
        self.assertEqual(response.status_code, 302)

        # An approval should have been created, attached to the job
        # application, and sent by email.
        job_application = JobApplication.objects.get(pk=job_application.pk)
        self.assertTrue(job_application.approval_number_sent_by_email)
        self.assertIsNotNone(job_application.approval_number_sent_at)
        self.assertEqual(job_application.approval_number_delivered_by, user)
        self.assertEqual(job_application.approval_delivery_mode, job_application.APPROVAL_DELIVERY_MODE_MANUAL)

        approval = job_application.approval
        self.assertEqual(approval.created_by, user)
        self.assertEqual(approval.number, post_data["number"])
        self.assertEqual(approval.user, job_application.job_seeker)

        self.assertEqual(len(mail.outbox), 1)
        email = mail.outbox[0]
        self.assertIn(approval.number_with_spaces, email.body)
Beispiel #20
0
 def test_job_application_sent_by_job_seeker_factory(self):
     job_application = JobApplicationSentByJobSeekerFactory()
     self.assertEqual(
         job_application.sender_kind, JobApplication.SENDER_KIND_JOB_SEEKER
     )
     self.assertEqual(job_application.job_seeker, job_application.sender)