Skip to content

Commit

Permalink
Merge pull request #54280 from callstack-internal/feat/step-4-logic
Browse files Browse the repository at this point in the history
[NO QA] feat: Step 4 logic
  • Loading branch information
madmax330 authored Feb 7, 2025
2 parents 33a03bf + 40ff042 commit 6842b46
Show file tree
Hide file tree
Showing 36 changed files with 689 additions and 310 deletions.
12 changes: 9 additions & 3 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -688,8 +688,7 @@ const CONST = {
IS_ANYONE_ELSE_BENEFICIAL_OWNER: 2,
BENEFICIAL_OWNER_DETAILS_FORM: 3,
ARE_THERE_MORE_BENEFICIAL_OWNERS: 4,
OWNERSHIP_CHART: 5,
BENEFICIAL_OWNERS_LIST: 6,
BENEFICIAL_OWNERS_LIST: 5,
},
BENEFICIAL_OWNER_DATA: {
BENEFICIAL_OWNER_KEYS: 'beneficialOwnerKeys',
Expand All @@ -703,7 +702,13 @@ const CONST = {
CITY: 'city',
STATE: 'state',
ZIP_CODE: 'zipCode',
COUNTRY: 'country',
COUNTRY: 'nationality',
PROOF_OF_OWNERSHIP: 'proofOfBeneficialOwner',
COPY_OF_ID: 'copyOfIDForBeneficialOwner',
ADDRESS_PROOF: 'addressProofForBeneficialOwner',
CODICE_FISCALE: 'codiceFisclaleTaxID',
FULL_NAME: 'fullName',
RESIDENTIAL_ADDRESS: 'residentialAddress',
},
CURRENT_USER_KEY: 'currentUser',
},
Expand Down Expand Up @@ -758,6 +763,7 @@ const CONST = {
AU: 'AU',
CA: 'CA',
GB: 'GB',
IT: 'IT',
},
DESKTOP_DEEPLINK_APP_STATE: {
CHECKING: 'checking',
Expand Down
3 changes: 3 additions & 0 deletions src/components/SubStepForms/YesNoStep.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React, {useMemo, useState} from 'react';
import type {StyleProp, ViewStyle} from 'react-native';
import {useOnyx} from 'react-native-onyx';
import FormProvider from '@components/Form/FormProvider';
import type {Choice} from '@components/RadioButtons';
import RadioButtons from '@components/RadioButtons';
Expand Down Expand Up @@ -28,6 +29,7 @@ type YesNoStepProps = {
function YesNoStep({title, description, defaultValue, onSelectedValue, submitButtonStyles}: YesNoStepProps) {
const {translate} = useLocalize();
const styles = useThemeStyles();
const [reimbursementAccount] = useOnyx(ONYXKEYS.REIMBURSEMENT_ACCOUNT);
const [value, setValue] = useState(defaultValue);

const handleSubmit = () => {
Expand Down Expand Up @@ -55,6 +57,7 @@ function YesNoStep({title, description, defaultValue, onSelectedValue, submitBut
onSubmit={handleSubmit}
style={[styles.mh5, styles.flexGrow1]}
submitButtonStyles={submitButtonStyles}
isLoading={reimbursementAccount?.isSavingCorpayOnboardingBeneficialOwnersFields}
>
<Text style={[styles.textHeadlineLineHeightXXL]}>{title}</Text>
<Text style={[styles.pv3, styles.textSupporting]}>{description}</Text>
Expand Down
4 changes: 2 additions & 2 deletions src/hooks/useSubStep/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@ export default function useSubStep<TProps extends SubStepProps>({bodyContent, on
[screenIndex, lastScreenIndex, skipSteps, onFinished, onNextSubStep],
);

const moveTo = useCallback((step: number) => {
isEditing.current = true;
const moveTo = useCallback((step: number, turnOnEditMode?: boolean) => {
isEditing.current = !(turnOnEditMode !== undefined && !turnOnEditMode);
setScreenIndex(step);
}, []);

Expand Down
2 changes: 1 addition & 1 deletion src/hooks/useSubStep/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ type SubStepProps = {
onNext: (data?: unknown) => void;

/** moves user to passed sub step */
onMove: (step: number) => void;
onMove: (step: number, turnOnEditMode?: boolean) => void;

/** index of currently displayed sub step */
screenIndex?: number;
Expand Down
14 changes: 14 additions & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2368,6 +2368,20 @@ const translations = {
selectCountry: 'Select country',
findCountry: 'Find country',
address: 'Address',
chooseFile: 'Choose file',
uploadDocuments: 'Upload additional documentation',
pleaseUpload: 'Please upload additional documentation below to help us verify your identity as a direct or indirect owner of 25% or more of the business entity.',
acceptedFiles: 'Accepted file formats: PDF, PNG, JPEG. Total file size for each section cannot exceed 5 MB.',
proofOfBeneficialOwner: 'Proof of beneficial owner',
proofOfBeneficialOwnerDescription:
"Please provide a signed attestation and org chart from a public accountant, notary, or lawyer verifying ownership of 25% or more of the business. It must be dated within the last three months and include the signer's license number.",
copyOfID: 'Copy of ID for beneficial owner',
copyOfIDDescription: "Examples: Passport, driver's license, etc.",
proofOfAddress: 'Address proof for beneficial owner',
proofOfAddressDescription: 'Examples: Utility bill, rental agreement, etc.',
codiceFiscale: 'Codice fiscale/Tax ID',
codiceFiscaleDescription:
'Please upload a video of a site visit or a recorded call with the signing officer. The officer must provide: full name, date of birth, company name, registered number, fiscal code number, registered address, nature of business and purpose of account.',
},
validationStep: {
headerTitle: 'Validate bank account',
Expand Down
15 changes: 15 additions & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2392,6 +2392,21 @@ const translations = {
selectCountry: 'Seleccionar país',
findCountry: 'Buscar país',
address: 'Dirección',
chooseFile: 'Elige archivo',
uploadDocuments: 'Sube documentación adicional',
pleaseUpload:
'Por favor, sube la documentación adicional a continuación para ayudarnos a verificar tu identidad como propietario directo o indirecto del 25% o más de la entidad empresarial.',
acceptedFiles: 'Formatos de archivo aceptados: PDF, PNG, JPEG. El tamaño total del archivo para cada sección no puede superar los 5 MB.',
proofOfBeneficialOwner: 'Prueba del propietario beneficiario',
proofOfBeneficialOwnerDescription:
'Por favor, proporciona una declaración firmada y un organigrama de un contador público, notario o abogado que verifique la propiedad del 25% o más del negocio. Debe estar fechado dentro de los últimos tres meses e incluir el número de licencia del firmante.',
copyOfID: 'Copia de la identificación del propietario beneficiario',
copyOfIDDescription: 'Ejemplos: Pasaporte, licencia de conducir, etc.',
proofOfAddress: 'Prueba de la dirección del propietario beneficiario',
proofOfAddressDescription: 'Ejemplos: Factura de servicios, contrato de alquiler, etc.',
codiceFiscale: 'Codice fiscale/ID fiscal',
codiceFiscaleDescription:
'Por favor, sube un video de una visita al sitio o una llamada grabada con el oficial firmante. El oficial debe proporcionar: nombre completo, fecha de nacimiento, nombre de la empresa, número de registro, número de código fiscal, dirección registrada, naturaleza del negocio y propósito de la cuenta.',
},
validationStep: {
headerTitle: 'Validar cuenta bancaria',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import type {FileObject} from '@components/AttachmentModal';
import type {BeneficialOwnerDataKey} from '@src/types/form/ReimbursementAccountForm';

type SaveCorpayOnboardingBeneficialOwnerParams = {
inputs: string;
beneficialOwnerIDs?: string;
bankAccountID: number;
[key: BeneficialOwnerDataKey]: FileObject;
};

export default SaveCorpayOnboardingBeneficialOwnerParams;
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type {BeneficialOwnersStepProps} from '@src/types/form/ReimbursementAccountForm';

type UpdateBeneficialOwnersForBankAccountParams = Partial<BeneficialOwnersStepProps> & {bankAccountID: number; policyID: string};
type UpdateBeneficialOwnersForBankAccountParams = Partial<BeneficialOwnersStepProps> & {bankAccountID: number; policyID: string | undefined};

export default UpdateBeneficialOwnersForBankAccountParams;
1 change: 1 addition & 0 deletions src/libs/API/parameters/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -376,3 +376,4 @@ export type {default as QueueExpensifyCardForBillingParams} from './QueueExpensi
export type {default as GetCorpayOnboardingFieldsParams} from './GetCorpayOnboardingFieldsParams';
export type {SaveCorpayOnboardingCompanyDetailsParams} from './SaveCorpayOnboardingCompanyDetailsParams';
export type {default as AcceptSpotnanaTermsParams} from './AcceptSpotnanaTermsParams';
export type {default as SaveCorpayOnboardingBeneficialOwnerParams} from './SaveCorpayOnboardingBeneficialOwnerParams';
2 changes: 2 additions & 0 deletions src/libs/API/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,7 @@ const WRITE_COMMANDS = {
DISMISS_PRODUCT_TRAINING: 'DismissProductTraining',
RESET_SMS_DELIVERY_FAILURE_STATUS: 'ResetSMSDeliveryFailureStatus',
SAVE_CORPAY_ONBOARDING_COMPANY_DETAILS: 'SaveCorpayOnboardingCompanyDetails',
SAVE_CORPAY_ONBOARDING_BENEFICIAL_OWNER: 'SaveCorpayOnboardingBeneficialOwner',
} as const;

type WriteCommand = ValueOf<typeof WRITE_COMMANDS>;
Expand Down Expand Up @@ -790,6 +791,7 @@ type WriteCommandParameters = {
[WRITE_COMMANDS.UPDATE_WORKSPACE_CUSTOM_UNIT]: Parameters.UpdateWorkspaceCustomUnitParams;
[WRITE_COMMANDS.RESET_SMS_DELIVERY_FAILURE_STATUS]: Parameters.ResetSMSDeliveryFailureStatusParams;
[WRITE_COMMANDS.SAVE_CORPAY_ONBOARDING_COMPANY_DETAILS]: Parameters.SaveCorpayOnboardingCompanyDetailsParams;
[WRITE_COMMANDS.SAVE_CORPAY_ONBOARDING_BENEFICIAL_OWNER]: Parameters.SaveCorpayOnboardingBeneficialOwnerParams;

[WRITE_COMMANDS.DELETE_MONEY_REQUEST_ON_SEARCH]: Parameters.DeleteMoneyRequestOnSearchParams;
[WRITE_COMMANDS.HOLD_MONEY_REQUEST_ON_SEARCH]: Parameters.HoldMoneyRequestOnSearchParams;
Expand Down
55 changes: 53 additions & 2 deletions src/libs/actions/BankAccounts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import type {
ConnectBankAccountParams,
DeletePaymentBankAccountParams,
OpenReimbursementAccountPageParams,
SaveCorpayOnboardingBeneficialOwnerParams,
ValidateBankAccountWithTransactionsParams,
VerifyIdentityForBankAccountParams,
} from '@libs/API/parameters';
Expand Down Expand Up @@ -407,12 +408,13 @@ function getCorpayBankAccountFields(country: string, currency: string) {
return API.read(READ_COMMANDS.GET_CORPAY_BANK_ACCOUNT_FIELDS, parameters, onyxData);
}

function createCorpayBankAccount(fields: ReimbursementAccountForm) {
function createCorpayBankAccount(fields: ReimbursementAccountForm, policyID: string | undefined) {
const parameters = {
type: 1,
isSavings: false,
isWithdrawal: true,
inputs: JSON.stringify(fields),
policyID,
};

const onyxData: OnyxData = {
Expand Down Expand Up @@ -501,10 +503,52 @@ function saveCorpayOnboardingCompanyDetails(parameters: SaveCorpayOnboardingComp
return API.write(WRITE_COMMANDS.SAVE_CORPAY_ONBOARDING_COMPANY_DETAILS, formattedParams, onyxData);
}

function saveCorpayOnboardingBeneficialOwners(parameters: SaveCorpayOnboardingBeneficialOwnerParams) {
const onyxData: OnyxData = {
optimisticData: [
{
onyxMethod: Onyx.METHOD.MERGE,
key: ONYXKEYS.REIMBURSEMENT_ACCOUNT,
value: {
isSavingCorpayOnboardingBeneficialOwnersFields: true,
errors: null,
},
},
],
successData: [
{
onyxMethod: Onyx.METHOD.MERGE,
key: ONYXKEYS.REIMBURSEMENT_ACCOUNT,
value: {
isSavingCorpayOnboardingBeneficialOwnersFields: false,
isSuccess: true,
},
},
],
failureData: [
{
onyxMethod: Onyx.METHOD.MERGE,
key: ONYXKEYS.REIMBURSEMENT_ACCOUNT,
value: {
isSavingCorpayOnboardingBeneficialOwnersFields: false,
isSuccess: false,
errors: getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage'),
},
},
],
};

return API.write(WRITE_COMMANDS.SAVE_CORPAY_ONBOARDING_BENEFICIAL_OWNER, parameters, onyxData);
}

function clearReimbursementAccount() {
Onyx.set(ONYXKEYS.REIMBURSEMENT_ACCOUNT, null);
}

function clearCorpayBankAccountFields() {
Onyx.set(ONYXKEYS.CORPAY_FIELDS, null);
}

function clearReimbursementAccountBankCreation() {
Onyx.merge(ONYXKEYS.REIMBURSEMENT_ACCOUNT, {isCreateCorpayBankAccount: null, isSuccess: null, isLoading: null});
}
Expand All @@ -513,6 +557,10 @@ function clearReimbursementAccountSaveCorpayOnboardingCompanyDetails() {
Onyx.merge(ONYXKEYS.REIMBURSEMENT_ACCOUNT, {isSuccess: null, isSavingCorpayOnboardingCompanyFields: null});
}

function clearReimbursementAccountSaveCorpayOnboardingBeneficialOwners() {
Onyx.merge(ONYXKEYS.REIMBURSEMENT_ACCOUNT, {isSuccess: null, isSavingCorpayOnboardingBeneficialOwnersFields: null});
}

/**
* Function to display and fetch data for Reimbursement Account step
* @param stepToOpen - current step to open
Expand Down Expand Up @@ -584,7 +632,7 @@ function updateCompanyInformationForBankAccount(bankAccountID: number, params: P
* Add beneficial owners for the bank account and verify the accuracy of the information provided
* @param params - Beneficial Owners step form params
*/
function updateBeneficialOwnersForBankAccount(bankAccountID: number, params: Partial<BeneficialOwnersStepProps>, policyID: string) {
function updateBeneficialOwnersForBankAccount(bankAccountID: number, params: Partial<BeneficialOwnersStepProps>, policyID: string | undefined) {
API.write(
WRITE_COMMANDS.UPDATE_BENEFICIAL_OWNERS_FOR_BANK_ACCOUNT,
{
Expand Down Expand Up @@ -796,6 +844,9 @@ export {
getCorpayOnboardingFields,
saveCorpayOnboardingCompanyDetails,
clearReimbursementAccountSaveCorpayOnboardingCompanyDetails,
saveCorpayOnboardingBeneficialOwners,
clearReimbursementAccountSaveCorpayOnboardingBeneficialOwners,
clearCorpayBankAccountFields,
};

export type {BusinessAddress, PersonalAddress};
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@ function AddressUBO({onNext, onMove, isEditing, beneficialOwnerBeingModifiedID}:
} as const;

const defaultValues = {
street: reimbursementAccountDraft?.[inputKeys.street] ?? '',
city: reimbursementAccountDraft?.[inputKeys.city] ?? '',
state: reimbursementAccountDraft?.[inputKeys.state] ?? '',
zipCode: reimbursementAccountDraft?.[inputKeys.zipCode] ?? '',
street: String(reimbursementAccountDraft?.[inputKeys.street] ?? ''),
city: String(reimbursementAccountDraft?.[inputKeys.city] ?? ''),
state: String(reimbursementAccountDraft?.[inputKeys.state] ?? ''),
zipCode: String(reimbursementAccountDraft?.[inputKeys.zipCode] ?? ''),
};

const stepFields = [inputKeys.street, inputKeys.city, inputKeys.state, inputKeys.zipCode];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ function DateOfBirthUBO({onNext, onMove, isEditing, beneficialOwnerBeingModified
const [reimbursementAccountDraft] = useOnyx(ONYXKEYS.FORMS.REIMBURSEMENT_ACCOUNT_FORM_DRAFT);

const dobInputID = `${BENEFICIAL_OWNER_PREFIX}_${beneficialOwnerBeingModifiedID}_${DOB}` as const;
const dobDefaultValue = reimbursementAccountDraft?.[dobInputID] ?? '';
const dobDefaultValue = String(reimbursementAccountDraft?.[dobInputID] ?? '');

const handleSubmit = useReimbursementAccountStepFormSubmit({
fieldIds: [dobInputID],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ function LegalNameUBO({onNext, onMove, isEditing, beneficialOwnerBeingModifiedID
const lastNameInputID = `${BENEFICIAL_OWNER_PREFIX}_${beneficialOwnerBeingModifiedID}_${LAST_NAME}` as keyof FormOnyxValues;
const stepFields = [firstNameInputID, lastNameInputID];
const defaultValues = {
firstName: reimbursementAccountDraft?.[firstNameInputID] ?? '',
lastName: reimbursementAccountDraft?.[lastNameInputID] ?? '',
firstName: String(reimbursementAccountDraft?.[firstNameInputID] ?? ''),
lastName: String(reimbursementAccountDraft?.[lastNameInputID] ?? ''),
};

const handleSubmit = useReimbursementAccountStepFormSubmit({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import SingleFieldStep from '@components/SubStepForms/SingleFieldStep';
import useLocalize from '@hooks/useLocalize';
import useReimbursementAccountStepFormSubmit from '@hooks/useReimbursementAccountStepFormSubmit';
import type {SubStepProps} from '@hooks/useSubStep/types';
import * as ValidationUtils from '@libs/ValidationUtils';
import {getFieldRequiredErrors, isValidSSNLastFour} from '@libs/ValidationUtils';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';

Expand All @@ -20,12 +20,12 @@ function SocialSecurityNumberUBO({onNext, onMove, isEditing, beneficialOwnerBein
const [reimbursementAccountDraft] = useOnyx(ONYXKEYS.FORMS.REIMBURSEMENT_ACCOUNT_FORM_DRAFT);

const ssnLast4InputID = `${BENEFICIAL_OWNER_PREFIX}_${beneficialOwnerBeingModifiedID}_${SSN_LAST_4}` as const;
const defaultSsnLast4 = reimbursementAccountDraft?.[ssnLast4InputID] ?? '';
const defaultSsnLast4 = String(reimbursementAccountDraft?.[ssnLast4InputID] ?? '');
const stepFields = [ssnLast4InputID];

const validate = (values: FormOnyxValues<typeof ONYXKEYS.FORMS.REIMBURSEMENT_ACCOUNT_FORM>): FormInputErrors<typeof ONYXKEYS.FORMS.REIMBURSEMENT_ACCOUNT_FORM> => {
const errors = ValidationUtils.getFieldRequiredErrors(values, stepFields);
if (values[ssnLast4InputID] && !ValidationUtils.isValidSSNLastFour(values[ssnLast4InputID])) {
const errors = getFieldRequiredErrors(values, stepFields);
if (values[ssnLast4InputID] && !isValidSSNLastFour(String(values[ssnLast4InputID]))) {
errors[ssnLast4InputID] = translate('bankAccount.error.ssnLast4');
}
return errors;
Expand Down
Loading

0 comments on commit 6842b46

Please sign in to comment.