diff --git a/backend/benefit/applications/tests/test_applications_report.py b/backend/benefit/applications/tests/test_applications_report.py index 79bb329633..4e08792c3b 100644 --- a/backend/benefit/applications/tests/test_applications_report.py +++ b/backend/benefit/applications/tests/test_applications_report.py @@ -122,6 +122,7 @@ def _create_applications_for_export(): reason="This test fails in deploy pipeline -- DETAIL: Key (username)=(masonzachary_a45eb8) already exists." ) def test_applications_csv_export_new_applications(handler_api_client): + pytest ( application1, application2, diff --git a/frontend/benefit/applicant/src/components/applications/forms/application/step1/useApplicationFormStep1.ts b/frontend/benefit/applicant/src/components/applications/forms/application/step1/useApplicationFormStep1.ts index b75c49a9c5..a3446ab0d7 100644 --- a/frontend/benefit/applicant/src/components/applications/forms/application/step1/useApplicationFormStep1.ts +++ b/frontend/benefit/applicant/src/components/applications/forms/application/step1/useApplicationFormStep1.ts @@ -9,7 +9,7 @@ import { ORGANIZATION_TYPES, } from 'benefit-shared/constants'; import { Application, DeMinimisAid } from 'benefit-shared/types/application'; -import { FormikProps, useFormik } from 'formik'; +import { FormikErrors, FormikProps, useFormik } from 'formik'; import fromPairs from 'lodash/fromPairs'; import { TFunction } from 'next-i18next'; import React, { useState } from 'react'; @@ -39,6 +39,17 @@ type ExtendedComponentProps = { getDefaultSelectValue: (fieldName: keyof Application) => OptionType; }; +type DeMinimisFormikPromises = Promise< + [void | FormikErrors, void | FormikErrors] +>; + +const hasBusinessActivitiesOrIsCompany = ( + hasBusinessActivities: boolean, + organizationType: ORGANIZATION_TYPES +): boolean => + hasBusinessActivities === true || + organizationType === ORGANIZATION_TYPES.COMPANY; + const useApplicationFormStep1 = ( application: Partial, isUnfinishedDeminimisAid: boolean @@ -66,6 +77,31 @@ const useApplicationFormStep1 = ( const { values, touched, errors, setFieldValue } = formik; + const isDeMinimisAidRowUnfinished = (): boolean => { + if (isUnfinishedDeminimisAid) { + showErrorToast( + t(`${translationsBase}.notifications.deMinimisUnfinished.label`), + t(`${translationsBase}.notifications.deMinimisUnfinished.content`) + ); + return true; + } + + return false; + }; + + const handleDeMinimisRadioButtonChange = ( + formikFields: ExtendedComponentProps['fields'] + ): Promise | DeMinimisFormikPromises => { + if (deMinimisAids.length === 0) { + setDeMinimisAids([]); + return Promise.all([ + formik.setFieldValue(formikFields.deMinimisAidSet.name, []), + formik.setFieldValue(formikFields.deMinimisAid.name, false), + ]); + } + return Promise.resolve(); + }; + const fields: ExtendedComponentProps['fields'] = React.useMemo(() => { const fieldMasks: Partial> = { [APPLICATION_FIELDS_STEP1_KEYS.COMPANY_BANK_ACCOUNT_NUMBER]: { @@ -97,35 +133,47 @@ const useApplicationFormStep1 = ( const getErrorMessage = (fieldName: string): string | undefined => getErrorText(errors, touched, fieldName, t, isSubmitted); - const handleSubmit = (): void => { - setIsSubmitted(true); - void formik.validateForm().then((errs) => { - const errorFieldKey = Object.keys(errs)[0]; + const checkForFieldValidity = (errs: FormikErrors): boolean => { + const errorFieldKey = Object.keys(errs)[0]; - if (errorFieldKey) { - return focusAndScroll(errorFieldKey); - } + if (errorFieldKey) { + focusAndScroll(errorFieldKey); + return false; + } - if (isUnfinishedDeminimisAid) { - showErrorToast( - t(`${translationsBase}.deMinimisUnfinished.label`), - t(`${translationsBase}.deMinimisUnfinished.content`) - ); - return false; - } + if (isDeMinimisAidRowUnfinished()) { + focusAndScroll('deMinimisAid'); + return false; + } - // de minimis fields are empty, set radio to false - if (deMinimisAids.length === 0) { - void formik.setFieldValue(fields.deMinimisAidSet.name, []); - void formik.setFieldValue(fields.deMinimisAid.name, false); - } + void formik.validateForm(); + return true; + }; - return formik.submitForm(); - }); + const submitIfFormValid = (isFormValid: boolean): boolean => { + void handleDeMinimisRadioButtonChange(fields); + if (isFormValid) { + void formik.submitForm(); + return true; + } + return false; }; - const handleSave = (): void => { - void onSave(values); + const handleSubmit = (): void => { + setIsSubmitted(true); + void formik + .validateForm() + .then((errs) => checkForFieldValidity(errs)) + .then((isFormValid: boolean) => submitIfFormValid(isFormValid)); + }; + + const handleSave = (): void | boolean => { + if (isDeMinimisAidRowUnfinished()) { + return false; + } + return void handleDeMinimisRadioButtonChange(fields) + .then(() => onSave(values)) + .catch(() => false); }; const applicationId = values?.id; @@ -140,9 +188,10 @@ const useApplicationFormStep1 = ( void setFieldValue(fields.deMinimisAid.name, null); }, [fields.deMinimisAid.name, setDeMinimisAids, setFieldValue]); - const showDeminimisSection = - values.associationHasBusinessActivities === true || - organizationType === ORGANIZATION_TYPES.COMPANY; + const showDeminimisSection = hasBusinessActivitiesOrIsCompany( + values.associationHasBusinessActivities, + organizationType + ); const languageOptions = React.useMemo( (): OptionType[] => getLanguageOptions(t, 'languages'), diff --git a/frontend/benefit/applicant/src/hooks/useFormActions.tsx b/frontend/benefit/applicant/src/hooks/useFormActions.tsx index 0f3fea6e52..11d5c0016d 100644 --- a/frontend/benefit/applicant/src/hooks/useFormActions.tsx +++ b/frontend/benefit/applicant/src/hooks/useFormActions.tsx @@ -123,15 +123,13 @@ const useFormActions = (application: Partial): FormActions => { apprenticeshipProgram: currentValues.apprenticeshipProgram, }; - const deMinimisAidSet = - deMinimisAids.length > 0 - ? deMinimisAids - : currentValues.deMinimisAidSet ?? []; + const deMinimisAidSet = deMinimisAids; return { ...application, ...normalizedValues, deMinimisAidSet, + deMinimisAid: deMinimisAidSet.length > 0, }; };