Skip to content

Commit

Permalink
shahzaib / KYC-375 / make TIN field mandatory in cfd personal details…
Browse files Browse the repository at this point in the history
… form (#10013)

* chore: make TIN field mandatory in cfd personal details form for MT5 account creation

* chore: added comment to for the new TIN validation rule in cfd

* chore: refactored cfd personal details form
  • Loading branch information
shahzaib-deriv committed Oct 4, 2023
1 parent 7c2abed commit f27c5d3
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 61 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ describe('<CFDPersonalDetailsForm />', () => {
value: 'ax',
},
],
value: {
initial_values: {
citizen: '',
tax_residence: '',
tax_identification_number: '',
Expand Down Expand Up @@ -137,7 +137,7 @@ describe('<CFDPersonalDetailsForm />', () => {
});

it('should disable Citizenship and Tax residence fields if they were submitted earlier & immutable from BE', async () => {
const values = {
const initial_values = {
citizen: 'Indonesia',
tax_residence: 'Indonesia',
tax_identification_number: '',
Expand All @@ -150,7 +150,7 @@ describe('<CFDPersonalDetailsForm />', () => {
<CFDPersonalDetailsForm
{...props}
is_fully_authenticated
value={values}
initial_values={initial_values}
changeable_fields={changeable_fields}
/>
);
Expand Down
76 changes: 30 additions & 46 deletions packages/cfd/src/Components/cfd-personal-details-form.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Field, FieldProps, Formik, FormikHelpers as FormikActions, FormikProps } from 'formik';
import React from 'react';
import { LandingCompany, ResidenceList } from '@deriv/api-types';
import { ResidenceList } from '@deriv/api-types';
import {
Autocomplete,
AutoHeightWrapper,
Expand All @@ -25,17 +25,15 @@ type TCFDPersonalDetailsFormProps = {
form_error?: string;
index: number;
is_loading: boolean;
landing_company: LandingCompany;
onSubmit: TOnSubmit;
residence_list: ResidenceList;
value: TFormValues;
initial_values: TFormValues;
};

type TValidatePersonalDetailsParams = {
values: TFormValues;
residence_list: ResidenceList;
account_opening_reason: TAccountOpeningReasonList;
is_tin_required: boolean;
};

type TFindDefaultValuesInResidenceList = (params: {
Expand Down Expand Up @@ -125,20 +123,18 @@ const validatePersonalDetails = ({
values,
residence_list,
account_opening_reason,
is_tin_required,
}: TValidatePersonalDetailsParams) => {
const [tax_residence_obj] = residence_list.filter(res => res.text === values.tax_residence && res.tin_format);

const tin_format = tax_residence_obj?.tin_format;
const tin_format = residence_list.find(res => res.text === values.tax_residence)?.tin_format;

const tin_regex = tin_format || ['^[A-Za-z0-9./s-]{0,25}$']; // fallback to API's default rule check

const validations: { [key: string]: ((v: string) => boolean | RegExpMatchArray | null)[] } = {
citizen: [(v: string) => !!v, (v: string) => residence_list.map(i => i.text).includes(v)],
tax_residence: [(v: string) => !!v, (v: string) => residence_list.map(i => i.text).includes(v)],
tax_identification_number: [
(v: string) => ((!values.tax_residence && is_tin_required) || tin_format ? !!v : true),
(v: string) => !!v,
(v: string) => (tin_regex ? tin_regex?.some(regex => v.match(regex)) : true),
() => !!values.tax_residence,
],
account_opening_reason: [
(v: string) => !!v,
Expand All @@ -157,6 +153,8 @@ const validatePersonalDetails = ({
const field_error_messages = (field_name: string): string[] => [
localize('{{field_name}} is required', { field_name }),
localize('{{field_name}} is not properly formatted.', { field_name }),
// this rule is specifically for tax_identification_number when tax_residence is empty
localize('Please fill in Tax residence.'),
];

const errors: { [key: string]: React.ReactNode } = {};
Expand Down Expand Up @@ -212,33 +210,31 @@ const submitForm: TSubmitForm = (values, actions, idx, onSubmit, is_dirty, resid
const CFDPersonalDetailsForm = ({
changeable_fields,
is_loading,
landing_company,
residence_list,
onSubmit,
value,
initial_values,
index,
form_error,
}: TCFDPersonalDetailsFormProps) => {
const account_opening_reason = getAccountOpeningReasonList();
const is_tin_required = !!(landing_company?.config?.tax_details_required ?? false);

const onSubmitForm = (values: TFormValues, actions: FormikActions<TFormValues>) =>
submitForm(values, actions, index, onSubmit, !isDeepEqual(value, values), residence_list);
submitForm(values, actions, index, onSubmit, !isDeepEqual(initial_values, values), residence_list);

const isFieldDisabled = (field: string) => !!initial_values[field] && !changeable_fields?.includes(field);

if (residence_list.length === 0) return <Loading is_fullscreen={false} />;
if (is_loading) return <Loading is_fullscreen={false} />;
if (is_loading || residence_list.length === 0) return <Loading is_fullscreen={false} />;

return (
<Formik
initialValues={{ ...value }}
initialValues={{ ...initial_values }}
validateOnChange
validateOnBlur
validate={values =>
validatePersonalDetails({
values,
residence_list,
account_opening_reason,
is_tin_required,
})
}
onSubmit={onSubmitForm}
Expand All @@ -258,23 +254,11 @@ const CFDPersonalDetailsForm = ({
const citizenship_error = touched.citizen && errors.citizen;
const place_of_birth_error = touched.place_of_birth && errors.place_of_birth;
const tax_residence_error = touched.tax_residence && errors.tax_residence;
const account_opening_reason_error = touched.account_opening_reason && errors.account_opening_reason;

const is_citizenship_disabled = !!value.citizen && !changeable_fields?.includes('citizen');
const is_place_of_birth_disabled =
!!value.place_of_birth && !changeable_fields?.includes('place_of_birth');
const is_tax_residence_disabled =
!!value.tax_residence && !changeable_fields?.includes('tax_residence');
const is_account_opening_reason_disabled =
!!value.account_opening_reason && !changeable_fields?.includes('account_opening_reason');
const handleItemSelection = (item: ResidenceList[0], _field: string) => {
const item_value = item.value ? item.text : '';
setFieldValue(_field, item_value, true);
};
const tin_field_label = residence_list.find(res => res.text === values.tax_residence && res.tin_format)
?.tin_format
? localize('Tax identification number*')
: localize('Tax identification number');

return (
<AutoHeightWrapper default_height={200} height_offset={isDesktop() ? 148 : null}>
Expand Down Expand Up @@ -318,7 +302,7 @@ const CFDPersonalDetailsForm = ({
type='text'
label={localize('Citizenship*')}
error={citizenship_error}
disabled={is_citizenship_disabled}
disabled={isFieldDisabled('citizen')}
list_items={residence_list}
onItemSelection={(item: ResidenceList[0]) =>
handleItemSelection(item, 'citizen')
Expand All @@ -336,7 +320,7 @@ const CFDPersonalDetailsForm = ({
value={values.citizen}
list_items={residence_list}
error={citizenship_error}
disabled={is_citizenship_disabled}
disabled={isFieldDisabled('citizen')}
use_text={true}
onChange={(e: React.ChangeEvent<HTMLSelectElement>) =>
setFieldValue('citizen', e.target.value, true)
Expand All @@ -358,7 +342,7 @@ const CFDPersonalDetailsForm = ({
type='text'
label={localize('Place of birth*')}
error={place_of_birth_error}
disabled={is_place_of_birth_disabled}
disabled={isFieldDisabled('place_of_birth')}
list_items={residence_list}
onItemSelection={(item: ResidenceList[0]) =>
handleItemSelection(item, 'place_of_birth')
Expand All @@ -376,7 +360,7 @@ const CFDPersonalDetailsForm = ({
value={values.place_of_birth}
list_items={residence_list}
error={place_of_birth_error}
disabled={is_place_of_birth_disabled}
disabled={isFieldDisabled('place_of_birth')}
use_text={true}
onChange={(e: React.ChangeEvent<HTMLSelectElement>) =>
setFieldValue('place_of_birth', e.target.value, true)
Expand All @@ -397,7 +381,7 @@ const CFDPersonalDetailsForm = ({
autoComplete='off'
label={localize('Tax residence*')}
error={tax_residence_error}
disabled={is_tax_residence_disabled}
disabled={isFieldDisabled('tax_residence')}
list_items={residence_list}
onItemSelection={(item: ResidenceList[0]) =>
handleItemSelection(item, 'tax_residence')
Expand All @@ -414,7 +398,7 @@ const CFDPersonalDetailsForm = ({
label={localize('Tax residence*')}
value={values.tax_residence}
error={tax_residence_error}
disabled={is_tax_residence_disabled}
disabled={isFieldDisabled('tax_residence')}
list_items={residence_list}
use_text={true}
onChange={(e: React.ChangeEvent<HTMLSelectElement>) =>
Expand All @@ -428,19 +412,19 @@ const CFDPersonalDetailsForm = ({
<InputField
id='real_mt5_tax_identification_number'
name='tax_identification_number'
label={tin_field_label}
placeholder={tin_field_label}
label={localize('Tax identification number*')}
placeholder={localize('Tax identification number*')}
value={values.tax_identification_number}
onBlur={handleBlur}
disabled={
!!value.tax_identification_number &&
!changeable_fields?.includes('tax_identification_number')
}
disabled={isFieldDisabled('tax_identification_number')}
optional
/>
</fieldset>
<Field name='account_opening_reason'>
{({ field }: FieldProps<string, TFormValues>) => (
{({
field,
meta: { error, touched },
}: FieldProps<string, TFormValues>) => (
<React.Fragment>
<DesktopWrapper>
<Dropdown
Expand All @@ -450,10 +434,10 @@ const CFDPersonalDetailsForm = ({
name={field.name}
list={account_opening_reason}
value={values.account_opening_reason}
disabled={is_account_opening_reason_disabled}
disabled={isFieldDisabled('account_opening_reason')}
onChange={handleChange}
handleBlur={handleBlur}
error={account_opening_reason_error}
error={touched && error}
list_portal_id='modal_root'
/>
</DesktopWrapper>
Expand All @@ -465,8 +449,8 @@ const CFDPersonalDetailsForm = ({
label={localize('Account opening reason*')}
list_items={account_opening_reason}
value={values.account_opening_reason}
disabled={is_account_opening_reason_disabled}
error={account_opening_reason_error}
disabled={isFieldDisabled('account_opening_reason')}
error={touched && error}
onChange={(e: React.ChangeEvent<HTMLSelectElement>) => {
handleChange(e);
setFieldValue(
Expand Down
16 changes: 4 additions & 12 deletions packages/cfd/src/Containers/cfd-personal-details-container.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react';
import { DesktopWrapper, Div100vhContainer, MobileWrapper, Text } from '@deriv/components';
import { Div100vhContainer, Text } from '@deriv/components';
import { localize } from '@deriv/translations';
import { TCFDPersonalDetailsContainerProps } from './props.types';
import CFDPersonalDetailsForm from '../Components/cfd-personal-details-form';
Expand All @@ -13,7 +13,7 @@ type TSetSubmitting = (isSubmitting: boolean) => void;
const CFDPersonalDetailsContainer = observer(({ onSubmit }: TCFDPersonalDetailsContainerProps) => {
const { client } = useStore();

const { account_settings, getChangeableFields, landing_company, residence_list, setAccountSettings } = client;
const { account_settings, getChangeableFields, residence_list, setAccountSettings } = client;

const [form_error, setFormError] = React.useState('');
const [is_loading, setIsLoading] = React.useState(false);
Expand Down Expand Up @@ -94,7 +94,7 @@ const CFDPersonalDetailsContainer = observer(({ onSubmit }: TCFDPersonalDetailsC
onSubmit(index, value);
};

const getPersonalDetailsForm = () => (
return (
<Div100vhContainer
className='cfd-personal-details-modal'
id='cfd-personal-details-modal'
Expand All @@ -111,22 +111,14 @@ const CFDPersonalDetailsContainer = observer(({ onSubmit }: TCFDPersonalDetailsC
form_error={form_error}
index={2}
is_loading={is_loading}
landing_company={landing_company}
onSubmit={updateValue}
residence_list={residence_list}
changeable_fields={getChangeableFields()}
value={form_values}
initial_values={form_values}
/>
</div>
</Div100vhContainer>
);

return (
<React.Fragment>
<DesktopWrapper>{getPersonalDetailsForm()}</DesktopWrapper>
<MobileWrapper>{getPersonalDetailsForm()}</MobileWrapper>
</React.Fragment>
);
});

export default CFDPersonalDetailsContainer;

0 comments on commit f27c5d3

Please sign in to comment.