Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,15 @@ fragment AdditionalSalaryRequestDetailsFields on AdditionalSalaryRequest {
currentSalaryCap
combinedCap
staffAccountBalance
pendingAsrAmount
ytdAsrAmount
grossAnnualSalary
outstandingSalaryRequest
}
spouseCalculations {
currentSalaryCap
pendingAsrAmount
ytdAsrAmount
staffAccountBalance
grossAnnualSalary
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@ const mockRequest = {
calculations: {
currentSalaryCap: 50000,
staffAccountBalance: 10000,
pendingAsrAmount: 0,
ytdAsrAmount: 0,
grossAnnualSalary: 40000,
outstandingSalaryRequest: false,
},
spouseCalculations: null,
user: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,13 +86,7 @@ describe('EditForm', () => {
});

it('should not render cap-related components when user does not exceed cap', () => {
const { queryByText } = renderComponent({
contextOverrides: {
user: {
currentSalary: { grossSalaryAmount: 1000 },
},
},
});
const { queryByText } = renderComponent();

expect(queryByText('Total Salary Requested')).not.toBeInTheDocument();
expect(queryByText('Approval Process')).not.toBeInTheDocument();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,13 +136,7 @@ describe('NewForm', () => {
});

it('does not render exceeded cap components when not exceeded', () => {
const { queryByText } = renderComponent({
contextOverrides: {
user: {
currentSalary: { grossSalaryAmount: 1000 },
},
},
});
const { queryByText } = renderComponent();

expect(queryByText('Total Salary Requested')).not.toBeInTheDocument();
expect(queryByText('Approval Process')).not.toBeInTheDocument();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,13 +90,7 @@ describe('ViewForm', () => {
});

it('should not render total salary requested or approval process when under cap', () => {
const { queryByText } = renderComponent({
contextOverrides: {
user: {
currentSalary: { grossSalaryAmount: 1000 },
},
},
});
const { queryByText } = renderComponent();

expect(queryByText('Total Salary Requested')).not.toBeInTheDocument();
expect(queryByText('Approval Process')).not.toBeInTheDocument();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,6 @@ export const mockUser = {
personNumber: '00123456',
emailAddress: '[email protected]',
},
currentSalary: {
grossSalaryAmount: 40000,
},
};

export const defaultMockContextValue = {
Expand All @@ -34,14 +31,16 @@ export const defaultMockContextValue = {
calculations: {
currentSalaryCap: 100000,
staffAccountBalance: 40000,
pendingAsrAmount: 5000,
ytdAsrAmount: 5000,
grossAnnualSalary: 40000,
},
},
},
calculations: {
currentSalaryCap: 100000,
staffAccountBalance: 40000,
pendingAsrAmount: 5000,
ytdAsrAmount: 5000,
grossAnnualSalary: 40000,
},
user: mockUser,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,19 +36,20 @@ const setupContext = (opts: {
mockUseAdditionalSalaryRequest.mockReturnValue({
traditional403bPercentage: 0.12,
roth403bPercentage: 0.1,
user: { currentSalary: { grossSalaryAmount: 50000 } },
spouse:
spouseCap !== null
? { currentSalary: { grossSalaryAmount: spouseGross } }
: undefined,
spouse: spouseCap !== null ? {} : undefined,
requestData: {
latestAdditionalSalaryRequest: {
calculations: { currentSalaryCap: 60000, pendingAsrAmount: 0 },
calculations: {
grossAnnualSalary: 50000,
currentSalaryCap: 60000,
ytdAsrAmount: 0,
},
spouseCalculations:
spouseCap !== null
? {
grossAnnualSalary: spouseGross,
currentSalaryCap: spouseCap,
pendingAsrAmount: spousePending,
ytdAsrAmount: spousePending,
}
: null,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -497,7 +497,6 @@ describe('RequestPage', () => {
currentStep: AdditionalSalaryRequestSectionEnum.CompleteForm,
pageType: PageEnum.New,
spouse: {
currentSalary: { grossSalaryAmount: 40000 },
staffInfo: {
preferredName: 'Jane',
lastName: 'Doe',
Expand All @@ -506,11 +505,13 @@ describe('RequestPage', () => {
requestData: {
latestAdditionalSalaryRequest: {
calculations: {
grossAnnualSalary: 40000,
currentSalaryCap: 500,
},
spouseCalculations: {
grossAnnualSalary: 40000,
currentSalaryCap: 500,
pendingAsrAmount: 600,
ytdAsrAmount: 600,
},
progressiveApprovalTier: { id: 'tier-1' },
progressiveApprovalTierReason:
Expand Down Expand Up @@ -722,7 +723,7 @@ describe('RequestPage', () => {
currentSalaryCap: 10000,
combinedCap: null,
staffAccountBalance: 999999,
pendingAsrAmount: 0,
ytdAsrAmount: 0,
},
progressiveApprovalTier: { id: 'tier-1' },
progressiveApprovalTierReason:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,9 @@ export type AdditionalSalaryRequestType = {
| 'currentSalaryCap'
| 'combinedCap'
| 'staffAccountBalance'
| 'pendingAsrAmount'
| 'ytdAsrAmount'
| 'grossAnnualSalary'
| 'outstandingSalaryRequest'
>;
user: HcmQuery['hcm'][0] | undefined;
spouse: HcmQuery['hcm'][1] | undefined;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ const defaultMockContextValue = {
calculations: {
currentSalaryCap: 100000,
staffAccountBalance: 50000,
pendingAsrAmount: 0,
ytdAsrAmount: 0,
grossAnnualSalary: 40000,
},
},
},
Expand All @@ -93,9 +94,6 @@ const defaultMockContextValue = {
personNumber: '00123456',
emailAddress: '[email protected]',
},
currentSalary: {
grossSalaryAmount: 40000,
},
},
],
},
Expand Down Expand Up @@ -161,9 +159,12 @@ const defaultGqlMocks: DeepPartial<MocksType> = {
AdditionalSalaryRequest: {
latestAdditionalSalaryRequest: {
id: 'test-request-id',
progressiveApprovalTierReason:
ProgressiveApprovalTierReasonEnum.OverUserCap,
calculations: {
currentSalaryCap: 100000,
pendingAsrAmount: 0,
ytdAsrAmount: 0,
grossAnnualSalary: 40000,
},
},
},
Expand Down Expand Up @@ -287,7 +288,7 @@ describe('useAdditionalSalaryRequestForm', () => {
calculations: {
currentSalaryCap: 50000,
staffAccountBalance: 20000,
pendingAsrAmount: 0,
ytdAsrAmount: 0,
},
},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,170 +44,171 @@
const individualCap =
requestData?.latestAdditionalSalaryRequest?.calculations.currentSalaryCap ??
0;
const pendingAsrAmount =
requestData?.latestAdditionalSalaryRequest?.calculations.pendingAsrAmount ??
0;
const grossAnnualSalary = user?.currentSalary?.grossSalaryAmount ?? 0;
const ytdAsrAmount =
requestData?.latestAdditionalSalaryRequest?.calculations.ytdAsrAmount ?? 0;
const grossAnnualSalary =
requestData?.latestAdditionalSalaryRequest?.calculations
.grossAnnualSalary ?? 0;

const [updateAdditionalSalaryRequest] =
useUpdateAdditionalSalaryRequestMutation();

const [submitAdditionalSalaryRequest] =
useSubmitAdditionalSalaryRequestMutation();

const lastValidTotalRef = useRef<number>(0);
const lastValidNonBackpayTotalRef = useRef<number>(0);

const createCurrencyValidation = useCallback(
(fieldName: string, max?: number) => {
let schema = amount(fieldName, t).nullable();
if (max !== null && max !== undefined) {
schema = schema.max(
max,
t('Exceeds {{amount}} limit', {
amount: currencyFormat(max, 'USD', locale, {
showTrailingZeros: true,
}),
}),
);
}
return schema;
},
[t, locale],
);

const defaultInitialValues: CompleteFormValues = {
...Object.fromEntries(fieldConfig.map(({ key }) => [key, '0'])),
totalAdditionalSalaryRequested: '0',
additionalInfo: '',
electionType403b: ElectionType403bEnum.None,
phoneNumber: user?.staffInfo?.primaryPhoneNumber || '',
emailAddress: user?.staffInfo?.emailAddress || '',
} as CompleteFormValues;

const initialValuesRef = useRef<CompleteFormValues | null>(null);

if (!initialValuesRef.current) {
if (providedInitialValues) {
initialValuesRef.current = providedInitialValues;
} else {
const request = requestData?.latestAdditionalSalaryRequest;
if (request && request.status !== AsrStatusEnum.ApprovedAndPaid) {
initialValuesRef.current = {
...Object.fromEntries(
fieldConfig.map(({ key }) => [
key,
String((request[key as keyof typeof request] as number) ?? ''),
]),
),
phoneNumber:
request.phoneNumber || user?.staffInfo?.primaryPhoneNumber || '',
emailAddress:
request.emailAddress || user?.staffInfo?.emailAddress || '',
totalAdditionalSalaryRequested:
request.totalAdditionalSalaryRequested || '',
additionalInfo: request.additionalInfo || '',
electionType403b:
request.electionType403b ?? ElectionType403bEnum.None,
} as CompleteFormValues;
}
}
}

const initialValues = initialValuesRef.current ?? defaultInitialValues;

const getMaxForField = useCallback(
(field: (typeof fieldConfig)[number]): number | undefined => {
if (!field.salaryInfoIntKey || !field.salaryInfoUssKey || !salaryInfo) {
return undefined;
}
const key = isInternational
? field.salaryInfoIntKey
: field.salaryInfoUssKey;
return salaryInfo[key] as number | undefined;
},
[salaryInfo, isInternational],
);

const validationSchema = useMemo(
() =>
yup.object({
...Object.fromEntries(
fieldConfig.map((field) => [
field.key,
createCurrencyValidation(field.label, getMaxForField(field)),
]),
),
phoneNumber: phoneNumber(i18n.t).required(
i18n.t('Phone Number is required.'),
),
emailAddress: yup
.string()
.required(t('Email address is required'))
.email(t('Please enter a valid email address')),
totalAdditionalSalaryRequested: yup
.number()
.test(
'total-within-remaining-allowable-salary',
t('Exceeds account balance.'),
function () {
const total = getTotal(this.parent as CompleteFormValues);

if (total >= 0) {
lastValidTotalRef.current = total;
}

return lastValidTotalRef.current <= primaryAccountBalance;
},
),
additionalInfo: yup
.string()
.test(
'required-when-exceeds-cap',
t('Additional info is required for requests exceeding your cap.'),
function (value) {
if (
requestData?.latestAdditionalSalaryRequest
?.progressiveApprovalTierReason ===
ProgressiveApprovalTierReasonEnum.BoardCapException
) {
return true;
}
const nonBackpayTotal = getNonBackpayTotal(
this.parent as CompleteFormValues,
);
if (nonBackpayTotal > 0) {
lastValidNonBackpayTotalRef.current = nonBackpayTotal;
}
const stableNonBackpayTotal =
nonBackpayTotal > 0
? nonBackpayTotal
: lastValidNonBackpayTotalRef.current;

const amountRequestedYtd =
grossAnnualSalary + pendingAsrAmount + stableNonBackpayTotal;
grossAnnualSalary + ytdAsrAmount + stableNonBackpayTotal;
const exceedsCap = amountRequestedYtd > individualCap;

if (exceedsCap) {
return !!value && value.trim().length > 0;
}
return true;
},
),
electionType403b: yup
.string()
.required(
t('Please select how you would like to contribute to your 403(b).'),
),
}),
[
createCurrencyValidation,
fieldConfig,
getMaxForField,
t,
primaryAccountBalance,
individualCap,
pendingAsrAmount,
ytdAsrAmount,

Check warning on line 211 in src/components/HrTools/AdditionalSalaryRequest/Shared/useAdditionalSalaryRequestForm.ts

View check run for this annotation

CodeScene Delta Analysis / CodeScene Code Health Review (main)

❌ Getting worse: Complex Method

useAdditionalSalaryRequestForm already has high cyclomatic complexity, and now it increases in Lines of Code from 224 to 225 This function has many conditional statements (e.g. if, for, while), leading to lower code health. Avoid adding more conditionals and code to it without refactoring.
grossAnnualSalary,
locale,
requestData,
Expand Down
Loading
Loading