From 6c5b159eafbfe9c03e82b33d3efc9a27be94a11f Mon Sep 17 00:00:00 2001 From: wjames111 Date: Wed, 13 May 2026 15:37:01 -0400 Subject: [PATCH 01/10] Remove +1 geographic multiplier --- .../HrTools/PdsGoalCalculator/calculations/salaryCalculation.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/HrTools/PdsGoalCalculator/calculations/salaryCalculation.ts b/src/components/HrTools/PdsGoalCalculator/calculations/salaryCalculation.ts index d47af4622d..9be7972240 100644 --- a/src/components/HrTools/PdsGoalCalculator/calculations/salaryCalculation.ts +++ b/src/components/HrTools/PdsGoalCalculator/calculations/salaryCalculation.ts @@ -31,7 +31,7 @@ export const calculateSalaryTotals = ( calculation.salaryOrHourly === DesignationSupportSalaryType.Salaried; const monthlyBase = isSalaried ? payRate / 12 : (payRate * hours * 52) / 12; - const grossMonthlyPay = monthlyBase * (1 + geographicMultiplier); + const grossMonthlyPay = monthlyBase * geographicMultiplier; const employerFica = grossMonthlyPay * employerFicaRate; const subtotal = grossMonthlyPay + employerFica; From a7cc3b4e0319ad521324e3313e3e73d066960d4f Mon Sep 17 00:00:00 2001 From: wjames111 Date: Wed, 13 May 2026 15:48:58 -0400 Subject: [PATCH 02/10] Fix goal total bug --- .../GoalCard/PdsGoalCard.test.tsx | 2 +- .../GoalCard/PdsGoalCard.tsx | 24 +--- .../buildPdsGoalConstants.test.ts | 2 +- .../calculatePdsGoalTotal.test.ts | 134 ------------------ ...atePdsGoalTotal.ts => pdsGoalConstants.ts} | 43 +----- .../calculations/usePdsSummaryData.test.ts | 33 ----- .../calculations/usePdsSummaryData.ts | 2 +- 7 files changed, 10 insertions(+), 230 deletions(-) delete mode 100644 src/components/HrTools/PdsGoalCalculator/calculations/calculatePdsGoalTotal.test.ts rename src/components/HrTools/PdsGoalCalculator/calculations/{calculatePdsGoalTotal.ts => pdsGoalConstants.ts} (70%) diff --git a/src/components/HrTools/PdsGoalCalculator/GoalCard/PdsGoalCard.test.tsx b/src/components/HrTools/PdsGoalCalculator/GoalCard/PdsGoalCard.test.tsx index 1f508b8339..ea14e11621 100644 --- a/src/components/HrTools/PdsGoalCalculator/GoalCard/PdsGoalCard.test.tsx +++ b/src/components/HrTools/PdsGoalCalculator/GoalCard/PdsGoalCard.test.tsx @@ -17,7 +17,7 @@ describe('PdsGoalCard', () => { , ); - expect(await findByText('$849.44')).toBeInTheDocument(); + expect(await findByText('$2,265.18')).toBeInTheDocument(); }); it('builds the View link with the PDS goal calculator path', async () => { diff --git a/src/components/HrTools/PdsGoalCalculator/GoalCard/PdsGoalCard.tsx b/src/components/HrTools/PdsGoalCalculator/GoalCard/PdsGoalCard.tsx index 70c2b32d80..14f74e1b03 100644 --- a/src/components/HrTools/PdsGoalCalculator/GoalCard/PdsGoalCard.tsx +++ b/src/components/HrTools/PdsGoalCalculator/GoalCard/PdsGoalCard.tsx @@ -1,4 +1,4 @@ -import React, { useMemo } from 'react'; +import React from 'react'; import { Chip } from '@mui/material'; import { useTranslation } from 'react-i18next'; import { GoalCard } from 'src/components/Reports/Shared/GoalCard/GoalCard'; @@ -10,10 +10,7 @@ import { useDeletePdsGoalCalculationMutation, } from '../GoalsList/PdsGoalCalculations.generated'; import { useHcmUserQuery } from '../Shared/HCM.generated'; -import { - buildPdsGoalConstants, - calculatePdsGoalTotal, -} from '../calculations/calculatePdsGoalTotal'; +import { usePdsSummaryData } from '../calculations/usePdsSummaryData'; export interface PdsGoalCardProps { goal: PdsGoalCalculationFieldsFragment; @@ -24,23 +21,12 @@ export const PdsGoalCard: React.FC = ({ goal }) => { const accountListId = useAccountListId() ?? ''; const [deletePdsGoalCalculation] = useDeletePdsGoalCalculationMutation(); - const { - goalMiscConstants, - goalGeographicConstantMap, - loading: constantsLoading, - } = useGoalCalculatorConstants(); + const { loading: constantsLoading } = useGoalCalculatorConstants(); const { data: hcmData, loading: hcmLoading } = useHcmUserQuery(); const hcmUser = hcmData?.hcm[0]; - const goalTotal = useMemo(() => { - const constants = buildPdsGoalConstants( - goalMiscConstants, - goalGeographicConstantMap, - goal.geographicLocation, - hcmUser?.fourOThreeB, - ); - return constants ? calculatePdsGoalTotal(goal, constants) : 0; - }, [goal, goalMiscConstants, goalGeographicConstantMap, hcmUser]); + const summaryData = usePdsSummaryData(goal, hcmUser); + const goalTotal = summaryData?.overallTotal ?? 0; const formType = goal.formType ?? DesignationSupportFormType.Detailed; const formTypeBadge = diff --git a/src/components/HrTools/PdsGoalCalculator/calculations/buildPdsGoalConstants.test.ts b/src/components/HrTools/PdsGoalCalculator/calculations/buildPdsGoalConstants.test.ts index 1a398f7b1e..a9b8b71729 100644 --- a/src/components/HrTools/PdsGoalCalculator/calculations/buildPdsGoalConstants.test.ts +++ b/src/components/HrTools/PdsGoalCalculator/calculations/buildPdsGoalConstants.test.ts @@ -2,7 +2,7 @@ import { GoalGeographicConstantMap, GoalMiscConstants, } from 'src/hooks/useGoalCalculatorConstants'; -import { buildPdsGoalConstants } from './calculatePdsGoalTotal'; +import { buildPdsGoalConstants } from './pdsGoalConstants'; const makeConstant = (fee: number) => ({ fee, diff --git a/src/components/HrTools/PdsGoalCalculator/calculations/calculatePdsGoalTotal.test.ts b/src/components/HrTools/PdsGoalCalculator/calculations/calculatePdsGoalTotal.test.ts deleted file mode 100644 index d5c613222a..0000000000 --- a/src/components/HrTools/PdsGoalCalculator/calculations/calculatePdsGoalTotal.test.ts +++ /dev/null @@ -1,134 +0,0 @@ -import { - DesignationSupportFormType, - DesignationSupportSalaryType, - DesignationSupportStatus, -} from 'src/graphql/types.generated'; -import { - PdsGoalTotalConstants, - PdsGoalTotalFields, - calculatePdsGoalTotal, -} from './calculatePdsGoalTotal'; - -const defaultConstants: PdsGoalTotalConstants = { - employerFicaRate: 0.08, - workCompPercentage: 0.17, - attritionRate: 0.06, - creditCardFeeRate: 0.06, - adminRate: 0.12, - fourOThreeBPercentage: 0.1, - geographicMultiplier: 0, -}; - -const makeGoal = ( - overrides: Partial = {}, -): PdsGoalTotalFields => ({ - hoursWorkedPerWeek: null, - salaryOrHourly: DesignationSupportSalaryType.Salaried, - status: DesignationSupportStatus.FullTime, - payRate: 60000, - benefits: 1500, - geographicLocation: null, - ministryCellPhone: 50, - ministryInternet: 50, - mpdNewsletter: 50, - mpdMiscellaneous: 50, - accountTransfers: 50, - otherMonthlyReimbursements: 50, - conferenceRetreatCosts: 0, - ministryTravelMeals: 0, - otherAnnualReimbursements: 0, - ...overrides, -}); - -describe('calculatePdsGoalTotal', () => { - it('computes the final assessment for a full-time salaried employee', () => { - const goal = makeGoal(); - const result = calculatePdsGoalTotal(goal, defaultConstants); - // assessment ≈ 1038.21 - expect(result).toBeCloseTo(1038.21, 1); - }); - - it('computes the final assessment for a part-time hourly employee', () => { - const goal = makeGoal({ - status: DesignationSupportStatus.PartTime, - salaryOrHourly: DesignationSupportSalaryType.Hourly, - payRate: 25, - hoursWorkedPerWeek: 20, - benefits: null, - }); - const result = calculatePdsGoalTotal(goal, defaultConstants); - expect(result).toBeCloseTo(434.83, 0); - }); - - it('returns a positive value when payRate is null', () => { - const goal = makeGoal({ payRate: null }); - const result = calculatePdsGoalTotal(goal, defaultConstants); - expect(result).toBeGreaterThan(0); - }); - - it('applies geographic multiplier', () => { - const goal = makeGoal(); - const withGeo = calculatePdsGoalTotal(goal, { - ...defaultConstants, - geographicMultiplier: 0.06, - }); - const withoutGeo = calculatePdsGoalTotal(goal, defaultConstants); - expect(withGeo).toBeGreaterThan(withoutGeo); - }); - - it('excludes reimbursable expenses and 403b when formType is Simple', () => { - const baseline = calculatePdsGoalTotal( - makeGoal({ formType: DesignationSupportFormType.Simple }), - { ...defaultConstants, fourOThreeBPercentage: 0.1 }, - ); - - const withDifferentReimbursables = calculatePdsGoalTotal( - makeGoal({ - formType: DesignationSupportFormType.Simple, - ministryCellPhone: 9999, - otherAnnualReimbursements: 9999, - }), - { ...defaultConstants, fourOThreeBPercentage: 0.1 }, - ); - expect(withDifferentReimbursables).toBeCloseTo(baseline); - - const withDifferent403b = calculatePdsGoalTotal( - makeGoal({ formType: DesignationSupportFormType.Simple }), - { ...defaultConstants, fourOThreeBPercentage: 0.5 }, - ); - expect(withDifferent403b).toBeCloseTo(baseline); - }); - - it('includes reimbursable expenses and 403b when formType is Detailed', () => { - const baseline = calculatePdsGoalTotal( - makeGoal({ formType: DesignationSupportFormType.Detailed }), - { ...defaultConstants, fourOThreeBPercentage: 0.1 }, - ); - - const withDifferentReimbursables = calculatePdsGoalTotal( - makeGoal({ - formType: DesignationSupportFormType.Detailed, - ministryCellPhone: 9999, - otherAnnualReimbursements: 9999, - }), - { ...defaultConstants, fourOThreeBPercentage: 0.1 }, - ); - expect(withDifferentReimbursables).toBeGreaterThan(baseline); - - const withDifferent403b = calculatePdsGoalTotal( - makeGoal({ formType: DesignationSupportFormType.Detailed }), - { ...defaultConstants, fourOThreeBPercentage: 0.5 }, - ); - expect(withDifferent403b).toBeGreaterThan(baseline); - }); - - it('treats null formType the same as Detailed (legacy goals)', () => { - const legacy = makeGoal({ formType: null }); - const detailed = makeGoal({ - formType: DesignationSupportFormType.Detailed, - }); - expect(calculatePdsGoalTotal(legacy, defaultConstants)).toBeCloseTo( - calculatePdsGoalTotal(detailed, defaultConstants), - ); - }); -}); diff --git a/src/components/HrTools/PdsGoalCalculator/calculations/calculatePdsGoalTotal.ts b/src/components/HrTools/PdsGoalCalculator/calculations/pdsGoalConstants.ts similarity index 70% rename from src/components/HrTools/PdsGoalCalculator/calculations/calculatePdsGoalTotal.ts rename to src/components/HrTools/PdsGoalCalculator/calculations/pdsGoalConstants.ts index 36e9aad328..83632dea10 100644 --- a/src/components/HrTools/PdsGoalCalculator/calculations/calculatePdsGoalTotal.ts +++ b/src/components/HrTools/PdsGoalCalculator/calculations/pdsGoalConstants.ts @@ -4,24 +4,8 @@ import { GoalMiscConstants, } from 'src/hooks/useGoalCalculatorConstants'; import { HcmUserQuery } from '../Shared/HCM.generated'; -import { - OtherExpensesConstants, - OtherExpensesFields, - calculateOtherExpenses, -} from './OtherExpenses'; -import { - ReimbursableCalculationFields, - calculateReimbursableTotals, -} from './reimbursableExpenses'; -import { - SalaryCalculationFields, - SalaryTotals, - calculateSalaryTotals, -} from './salaryCalculation'; - -export type PdsGoalTotalFields = SalaryCalculationFields & - ReimbursableCalculationFields & - OtherExpensesFields; +import { OtherExpensesConstants } from './OtherExpenses'; +import { SalaryTotals } from './salaryCalculation'; export interface PdsGoalTotalConstants { employerFicaRate: number; @@ -97,26 +81,3 @@ export const buildOtherExpensesConstants = ( }; }; -export const calculatePdsGoalTotal = ( - calculation: PdsGoalTotalFields, - constants: PdsGoalTotalConstants, -): number => { - const salaryTotals = calculateSalaryTotals(calculation, { - geographicMultiplier: constants.geographicMultiplier, - employerFicaRate: constants.employerFicaRate, - }); - - const reimbursableTotal = calculateReimbursableTotals(calculation).total; - - const otherExpenses = calculateOtherExpenses( - calculation, - buildOtherExpensesConstants( - calculation.formType ?? DesignationSupportFormType.Detailed, - constants, - salaryTotals, - reimbursableTotal, - ), - ); - - return otherExpenses.assessment; -}; diff --git a/src/components/HrTools/PdsGoalCalculator/calculations/usePdsSummaryData.test.ts b/src/components/HrTools/PdsGoalCalculator/calculations/usePdsSummaryData.test.ts index 9e58409a7c..2f93b9a08c 100644 --- a/src/components/HrTools/PdsGoalCalculator/calculations/usePdsSummaryData.test.ts +++ b/src/components/HrTools/PdsGoalCalculator/calculations/usePdsSummaryData.test.ts @@ -17,10 +17,6 @@ import { PdsGoalCalculationFieldsFragmentDoc, } from '../GoalsList/PdsGoalCalculations.generated'; import { HcmUserDocument, HcmUserQuery } from '../Shared/HCM.generated'; -import { - PdsGoalTotalConstants, - calculatePdsGoalTotal, -} from './calculatePdsGoalTotal'; import { usePdsSummaryData } from './usePdsSummaryData'; jest.mock('src/hooks/useGoalCalculatorConstants'); @@ -389,33 +385,4 @@ describe('usePdsSummaryData', () => { }); }); - describe('consistency with calculatePdsGoalTotal', () => { - // Mirrors what buildPdsGoalConstants would derive from the mocked - // useGoalCalculatorConstants + defaultHcmUser, so we can call - // calculatePdsGoalTotal directly without the hook. - const directConstants: PdsGoalTotalConstants = { - employerFicaRate: EMPLOYER_FICA_RATE, - workCompPercentage: WORK_COMP_PERCENTAGE, - attritionRate: ATTRITION_RATE, - creditCardFeeRate: CREDIT_CARD_FEE_RATE, - adminRate: ADMIN_RATE, - fourOThreeBPercentage: 0.08, - geographicMultiplier: 0, - }; - - it.each([ - DesignationSupportFormType.Detailed, - DesignationSupportFormType.Simple, - ])( - 'calculatePdsGoalTotal matches usePdsSummaryData.otherTotals.assessment when formType is %s', - (formType) => { - const calc = { ...defaultCalculation, formType }; - const { result } = renderHook(() => - usePdsSummaryData(calc, defaultHcmUser), - ); - const direct = calculatePdsGoalTotal(calc, directConstants); - expect(direct).toBeCloseTo(result.current!.otherTotals.assessment, 5); - }, - ); - }); }); diff --git a/src/components/HrTools/PdsGoalCalculator/calculations/usePdsSummaryData.ts b/src/components/HrTools/PdsGoalCalculator/calculations/usePdsSummaryData.ts index 0dc642c228..9e4ff50f8b 100644 --- a/src/components/HrTools/PdsGoalCalculator/calculations/usePdsSummaryData.ts +++ b/src/components/HrTools/PdsGoalCalculator/calculations/usePdsSummaryData.ts @@ -11,7 +11,7 @@ import { import { buildOtherExpensesConstants, buildPdsGoalConstants, -} from './calculatePdsGoalTotal'; +} from './pdsGoalConstants'; import { ReimbursableTotals, calculateReimbursableTotals, From 43c10594d22e9ac269fb586e83916262800705f4 Mon Sep 17 00:00:00 2001 From: wjames111 Date: Wed, 13 May 2026 16:34:38 -0400 Subject: [PATCH 03/10] Maybe fix attrition rate, unclear --- .../GoalCard/PdsGoalCard.test.tsx | 2 +- .../SupportItem/otherBreakdown.tsx | 12 +++++++++--- .../calculations/OtherExpenses.test.ts | 14 +++++++------- .../calculations/OtherExpenses.ts | 6 ++++-- .../calculations/pdsGoalConstants.ts | 1 - .../calculations/usePdsSummaryData.test.ts | 1 - 6 files changed, 21 insertions(+), 15 deletions(-) diff --git a/src/components/HrTools/PdsGoalCalculator/GoalCard/PdsGoalCard.test.tsx b/src/components/HrTools/PdsGoalCalculator/GoalCard/PdsGoalCard.test.tsx index ea14e11621..5385552eb0 100644 --- a/src/components/HrTools/PdsGoalCalculator/GoalCard/PdsGoalCard.test.tsx +++ b/src/components/HrTools/PdsGoalCalculator/GoalCard/PdsGoalCard.test.tsx @@ -17,7 +17,7 @@ describe('PdsGoalCard', () => { , ); - expect(await findByText('$2,265.18')).toBeInTheDocument(); + expect(await findByText('$2,298.27')).toBeInTheDocument(); }); it('builds the View link with the PDS goal calculator path', async () => { diff --git a/src/components/HrTools/PdsGoalCalculator/SupportItem/otherBreakdown.tsx b/src/components/HrTools/PdsGoalCalculator/SupportItem/otherBreakdown.tsx index 7937838ce1..fd6208f74c 100644 --- a/src/components/HrTools/PdsGoalCalculator/SupportItem/otherBreakdown.tsx +++ b/src/components/HrTools/PdsGoalCalculator/SupportItem/otherBreakdown.tsx @@ -126,9 +126,15 @@ export const buildOtherBreakdownRows = ( { id: 'assessment', category: t('Assessment'), - formula: t('(Subtotal + Credit Card Fees + Attrition) × {{rate}}', { - rate: percentageFormat(constants.adminRate, locale), - }), + formula: t( + '(Subtotal + Attrition + Credit Card Fees) ÷ {{divisor}} − (Subtotal + Attrition + Credit Card Fees)', + { + divisor: new Intl.NumberFormat(locale, { + minimumFractionDigits: 2, + maximumFractionDigits: 4, + }).format(1 - constants.adminRate), + }, + ), amount: totals.assessment, testId: 'other-assessment', bold: true, diff --git a/src/components/HrTools/PdsGoalCalculator/calculations/OtherExpenses.test.ts b/src/components/HrTools/PdsGoalCalculator/calculations/OtherExpenses.test.ts index bc6f01250f..33d738154d 100644 --- a/src/components/HrTools/PdsGoalCalculator/calculations/OtherExpenses.test.ts +++ b/src/components/HrTools/PdsGoalCalculator/calculations/OtherExpenses.test.ts @@ -145,11 +145,10 @@ describe('calculateOtherExpenses', () => { }); describe('assessment', () => { - it('is (subtotal + creditCardFees + attrition) × adminRate', () => { + it('grosses up (subtotal + creditCardFees + attrition) so that admin is `adminRate` of the post-admin total', () => { const result = calculateOtherExpenses(fullTime(), defaultConstants); - // subtotal=7400, attrition=444, creditCardFees=470.64 - // (7400 + 470.64 + 444) * 0.12 ≈ 997.76 - expect(result.assessment).toBeCloseTo(997.76, 1); + // adminBase=7400+470.64+444=8314.64; assessment = adminBase/0.88 - adminBase ≈ 1133.81 + expect(result.assessment).toBeCloseTo(1133.81, 1); }); it('returns 0 when adminRate is 0', () => { @@ -171,7 +170,7 @@ describe('calculateOtherExpenses', () => { expect(result.subtotal).toBeCloseTo(7400); expect(result.attrition).toBeCloseTo(444); expect(result.creditCardFees).toBeCloseTo(470.64); - expect(result.assessment).toBeCloseTo(997.76, 1); + expect(result.assessment).toBeCloseTo(1133.81, 1); }); it('produces correct totals for a part-time employee', () => { @@ -180,11 +179,12 @@ describe('calculateOtherExpenses', () => { // subtotal=5000+500+400+680+0=6580 // attrition=6580*0.06=394.80 // creditCardFees=(6580+394.80)*0.06=418.49 - // assessment=(6580+418.49+394.80)*0.12≈887.19 + // adminBase=6580+418.49+394.80=7393.29 + // assessment = adminBase/0.88 - adminBase ≈ 1008.18 expect(result.subtotal).toBeCloseTo(6580); expect(result.attrition).toBeCloseTo(394.8); expect(result.creditCardFees).toBeCloseTo(418.49, 1); - expect(result.assessment).toBeCloseTo(887.19, 1); + expect(result.assessment).toBeCloseTo(1008.18, 1); }); }); }); diff --git a/src/components/HrTools/PdsGoalCalculator/calculations/OtherExpenses.ts b/src/components/HrTools/PdsGoalCalculator/calculations/OtherExpenses.ts index c9240ae34b..89c817f6ab 100644 --- a/src/components/HrTools/PdsGoalCalculator/calculations/OtherExpenses.ts +++ b/src/components/HrTools/PdsGoalCalculator/calculations/OtherExpenses.ts @@ -55,8 +55,10 @@ export const calculateOtherExpenses = ( const attrition = subtotal * constants.attritionRate; const creditCardFees = (subtotal + attrition) * constants.creditCardFeeRate; - const adminRate = constants.adminRate; - const assessment = (subtotal + creditCardFees + attrition) * adminRate; + const adminBase = subtotal + creditCardFees + attrition; + // Admin assessment is `adminRate` of the post-admin total, not a markup on + // `adminBase`, so gross up: assessment / (adminBase + assessment) = adminRate. + const assessment = adminBase / (1 - constants.adminRate) - adminBase; return { reimbursableExpenses, diff --git a/src/components/HrTools/PdsGoalCalculator/calculations/pdsGoalConstants.ts b/src/components/HrTools/PdsGoalCalculator/calculations/pdsGoalConstants.ts index 83632dea10..17977c8e2b 100644 --- a/src/components/HrTools/PdsGoalCalculator/calculations/pdsGoalConstants.ts +++ b/src/components/HrTools/PdsGoalCalculator/calculations/pdsGoalConstants.ts @@ -80,4 +80,3 @@ export const buildOtherExpensesConstants = ( adminRate: constants.adminRate, }; }; - diff --git a/src/components/HrTools/PdsGoalCalculator/calculations/usePdsSummaryData.test.ts b/src/components/HrTools/PdsGoalCalculator/calculations/usePdsSummaryData.test.ts index 2f93b9a08c..b8384a9fac 100644 --- a/src/components/HrTools/PdsGoalCalculator/calculations/usePdsSummaryData.test.ts +++ b/src/components/HrTools/PdsGoalCalculator/calculations/usePdsSummaryData.test.ts @@ -384,5 +384,4 @@ describe('usePdsSummaryData', () => { ); }); }); - }); From 2057ac41a846ac8f72ab6b510d2d0ea65beb0d4e Mon Sep 17 00:00:00 2001 From: wjames111 Date: Wed, 13 May 2026 17:09:56 -0400 Subject: [PATCH 04/10] Fix tests --- .../GoalCard/PdsGoalCard.test.tsx | 2 +- .../PdsGoalCalculatorTestWrapper.tsx | 6 ++--- .../SupportItem/salaryBreakdown.test.tsx | 8 +++---- .../SupportItem/salaryBreakdown.tsx | 4 ++-- .../buildPdsGoalConstants.test.ts | 12 +++++----- .../calculations/pdsGoalConstants.ts | 4 +++- .../calculations/salaryCalculation.test.ts | 16 ++++++------- .../calculations/usePdsSummaryData.test.ts | 23 ++++++++++--------- 8 files changed, 39 insertions(+), 36 deletions(-) diff --git a/src/components/HrTools/PdsGoalCalculator/GoalCard/PdsGoalCard.test.tsx b/src/components/HrTools/PdsGoalCalculator/GoalCard/PdsGoalCard.test.tsx index 5385552eb0..9e6fd98306 100644 --- a/src/components/HrTools/PdsGoalCalculator/GoalCard/PdsGoalCard.test.tsx +++ b/src/components/HrTools/PdsGoalCalculator/GoalCard/PdsGoalCard.test.tsx @@ -17,7 +17,7 @@ describe('PdsGoalCard', () => { , ); - expect(await findByText('$2,298.27')).toBeInTheDocument(); + expect(await findByText('$8,043.95')).toBeInTheDocument(); }); it('builds the View link with the PDS goal calculator path', async () => { diff --git a/src/components/HrTools/PdsGoalCalculator/PdsGoalCalculatorTestWrapper.tsx b/src/components/HrTools/PdsGoalCalculator/PdsGoalCalculatorTestWrapper.tsx index ed9f228020..dfa4f67b90 100644 --- a/src/components/HrTools/PdsGoalCalculator/PdsGoalCalculatorTestWrapper.tsx +++ b/src/components/HrTools/PdsGoalCalculator/PdsGoalCalculatorTestWrapper.tsx @@ -194,15 +194,15 @@ export const PdsGoalCalculatorTestWrapper: React.FC< mpdGoalGeographicConstants: [ { location: 'None', - percentageMultiplier: 0, + percentageMultiplier: 1, }, { location: 'Orlando, FL', - percentageMultiplier: 0.06, + percentageMultiplier: 1.06, }, { location: 'New York, NY', - percentageMultiplier: 0.12, + percentageMultiplier: 1.12, }, ], mpdGoalMiscConstants: [ diff --git a/src/components/HrTools/PdsGoalCalculator/SupportItem/salaryBreakdown.test.tsx b/src/components/HrTools/PdsGoalCalculator/SupportItem/salaryBreakdown.test.tsx index 41081d08eb..12fa8981e2 100644 --- a/src/components/HrTools/PdsGoalCalculator/SupportItem/salaryBreakdown.test.tsx +++ b/src/components/HrTools/PdsGoalCalculator/SupportItem/salaryBreakdown.test.tsx @@ -9,7 +9,7 @@ import { buildSalaryBreakdownRows, } from './salaryBreakdown'; -const constants = { geographicMultiplier: 0, employerFicaRate: 0.08 }; +const constants = { geographicMultiplier: 1, employerFicaRate: 0.08 }; const salariedCalculation: SalaryCalculationFields = { salaryOrHourly: DesignationSupportSalaryType.Salaried, @@ -70,8 +70,8 @@ describe('buildSalaryBreakdownRows', () => { // monthlyBase = 60000 / 12 = 5000 expect(byId['monthly-base']).toBe(5000); // geographicMultiplier passed through from constants - expect(byId['geographic-multiplier']).toBe(0); - // grossMonthlyPay = 5000 * (1 + 0) = 5000 + expect(byId['geographic-multiplier']).toBe(1); + // grossMonthlyPay = 5000 * 1 = 5000 expect(byId['gross-monthly-pay']).toBe(5000); // employerFica = 5000 * 0.08 = 400 expect(byId['employer-fica']).toBe(400); @@ -94,7 +94,7 @@ describe('buildSalaryBreakdownRows', () => { expect(byId['hours-per-week']).toBe(40); // monthlyBase = (25 * 40 * 52) / 12 ≈ 4333.33 expect(byId['monthly-base']).toBeCloseTo(4333.33, 2); - // grossMonthlyPay = 4333.33 * (1 + 0) ≈ 4333.33 + // grossMonthlyPay = 4333.33 * 1 ≈ 4333.33 expect(byId['gross-monthly-pay']).toBeCloseTo(4333.33, 2); // employerFica = 4333.33 * 0.08 ≈ 346.67 expect(byId['employer-fica']).toBeCloseTo(346.67, 2); diff --git a/src/components/HrTools/PdsGoalCalculator/SupportItem/salaryBreakdown.tsx b/src/components/HrTools/PdsGoalCalculator/SupportItem/salaryBreakdown.tsx index cd8138c7bc..f76be25ee9 100644 --- a/src/components/HrTools/PdsGoalCalculator/SupportItem/salaryBreakdown.tsx +++ b/src/components/HrTools/PdsGoalCalculator/SupportItem/salaryBreakdown.tsx @@ -77,12 +77,12 @@ export const buildSalaryBreakdownRows = ( id: 'geographic-multiplier', category: t('Geographic Multiplier'), amount: geographicMultiplier, - format: 'percentage', + format: 'number', }, { id: 'gross-monthly-pay', category: t('Gross Monthly Pay'), - formula: t('Monthly Base × (1 + Geographic Multiplier)'), + formula: t('Monthly Base × Geographic Multiplier'), amount: grossMonthlyPay, format: 'currency', testId: 'gross-monthly-pay', diff --git a/src/components/HrTools/PdsGoalCalculator/calculations/buildPdsGoalConstants.test.ts b/src/components/HrTools/PdsGoalCalculator/calculations/buildPdsGoalConstants.test.ts index a9b8b71729..2870431791 100644 --- a/src/components/HrTools/PdsGoalCalculator/calculations/buildPdsGoalConstants.test.ts +++ b/src/components/HrTools/PdsGoalCalculator/calculations/buildPdsGoalConstants.test.ts @@ -108,7 +108,7 @@ describe('buildPdsGoalConstants', () => { expect(result).toBeNull(); }); - it('returns geographicMultiplier of 0 for unknown location', () => { + it('defaults geographicMultiplier to 1 (no adjustment) for unknown location', () => { const result = buildPdsGoalConstants( buildMiscConstants(), defaultGeoMap, @@ -116,10 +116,10 @@ describe('buildPdsGoalConstants', () => { null, ); - expect(result?.geographicMultiplier).toBe(0); + expect(result?.geographicMultiplier).toBe(1); }); - it('returns geographicMultiplier of 0 when location is null', () => { + it('defaults geographicMultiplier to 1 (no adjustment) when location is null', () => { const result = buildPdsGoalConstants( buildMiscConstants(), defaultGeoMap, @@ -127,10 +127,10 @@ describe('buildPdsGoalConstants', () => { null, ); - expect(result?.geographicMultiplier).toBe(0); + expect(result?.geographicMultiplier).toBe(1); }); - it('returns geographicMultiplier of 0 when location is undefined', () => { + it('defaults geographicMultiplier to 1 (no adjustment) when location is undefined', () => { const result = buildPdsGoalConstants( buildMiscConstants(), defaultGeoMap, @@ -138,7 +138,7 @@ describe('buildPdsGoalConstants', () => { null, ); - expect(result?.geographicMultiplier).toBe(0); + expect(result?.geographicMultiplier).toBe(1); }); it('looks up correct geographic multiplier', () => { diff --git a/src/components/HrTools/PdsGoalCalculator/calculations/pdsGoalConstants.ts b/src/components/HrTools/PdsGoalCalculator/calculations/pdsGoalConstants.ts index 17977c8e2b..d1a1ba702e 100644 --- a/src/components/HrTools/PdsGoalCalculator/calculations/pdsGoalConstants.ts +++ b/src/components/HrTools/PdsGoalCalculator/calculations/pdsGoalConstants.ts @@ -44,8 +44,10 @@ export const buildPdsGoalConstants = ( return null; } + // Multiplier is the *full* factor applied to monthlyBase (e.g. 1.06 for a + // 6% high-cost location), so the no-adjustment default must be 1, not 0. const geographicMultiplier = - goalGeographicConstantMap.get(geographicLocation ?? '') ?? 0; + goalGeographicConstantMap.get(geographicLocation ?? '') ?? 1; const taxDeferredPct = (fourOThreeB?.currentTaxDeferredContributionPercentage ?? 0) / 100; diff --git a/src/components/HrTools/PdsGoalCalculator/calculations/salaryCalculation.test.ts b/src/components/HrTools/PdsGoalCalculator/calculations/salaryCalculation.test.ts index c78088deed..d8904fca35 100644 --- a/src/components/HrTools/PdsGoalCalculator/calculations/salaryCalculation.test.ts +++ b/src/components/HrTools/PdsGoalCalculator/calculations/salaryCalculation.test.ts @@ -5,7 +5,7 @@ import { } from './salaryCalculation'; const FICA_RATE = 0.08; -const GEO_MULTIPLIER = 0.06; +const GEO_MULTIPLIER = 1.06; const salaried = ( overrides: Partial = {}, @@ -32,14 +32,14 @@ describe('calculateSalaryTotals', () => { describe('salaried', () => { it('divides yearly payRate by 12 when there is no geographic multiplier', () => { const result = calculateSalaryTotals(salaried(), { - geographicMultiplier: 0, + geographicMultiplier: 1, employerFicaRate: FICA_RATE, }); // 60000 / 12 expect(result.grossMonthlyPay).toBe(5000); }); - it('applies geographic multiplier additively', () => { + it('applies geographic multiplier as a full factor', () => { const result = calculateSalaryTotals(salaried(), { geographicMultiplier: GEO_MULTIPLIER, employerFicaRate: FICA_RATE, @@ -51,7 +51,7 @@ describe('calculateSalaryTotals', () => { it('ignores hoursWorkedPerWeek', () => { const result = calculateSalaryTotals( salaried({ hoursWorkedPerWeek: 40 }), - { geographicMultiplier: 0, employerFicaRate: FICA_RATE }, + { geographicMultiplier: 1, employerFicaRate: FICA_RATE }, ); expect(result.grossMonthlyPay).toBe(5000); }); @@ -60,7 +60,7 @@ describe('calculateSalaryTotals', () => { describe('hourly', () => { it('converts hourly rate to monthly when there is no geographic multiplier', () => { const result = calculateSalaryTotals(hourly(), { - geographicMultiplier: 0, + geographicMultiplier: 1, employerFicaRate: FICA_RATE, }); // 25 * 40 * 52 / 12 @@ -91,7 +91,7 @@ describe('calculateSalaryTotals', () => { it('treats null hoursWorkedPerWeek as 0 when hourly', () => { const result = calculateSalaryTotals( hourly({ hoursWorkedPerWeek: null }), - { geographicMultiplier: 0, employerFicaRate: FICA_RATE }, + { geographicMultiplier: 1, employerFicaRate: FICA_RATE }, ); expect(result.grossMonthlyPay).toBe(0); }); @@ -100,7 +100,7 @@ describe('calculateSalaryTotals', () => { describe('employer FICA and subtotal', () => { it('multiplies grossMonthlyPay by the provided FICA rate', () => { const result = calculateSalaryTotals(salaried(), { - geographicMultiplier: 0, + geographicMultiplier: 1, employerFicaRate: FICA_RATE, }); expect(result.employerFica).toBeCloseTo(400); @@ -108,7 +108,7 @@ describe('calculateSalaryTotals', () => { it('uses the passed-in FICA rate verbatim (no fallback applied)', () => { const result = calculateSalaryTotals(salaried(), { - geographicMultiplier: 0, + geographicMultiplier: 1, employerFicaRate: 0.0765, }); expect(result.employerFica).toBeCloseTo(382.5); diff --git a/src/components/HrTools/PdsGoalCalculator/calculations/usePdsSummaryData.test.ts b/src/components/HrTools/PdsGoalCalculator/calculations/usePdsSummaryData.test.ts index b8384a9fac..af7a2b960b 100644 --- a/src/components/HrTools/PdsGoalCalculator/calculations/usePdsSummaryData.test.ts +++ b/src/components/HrTools/PdsGoalCalculator/calculations/usePdsSummaryData.test.ts @@ -35,7 +35,7 @@ const WORK_COMP_PERCENTAGE = 0.17; const ATTRITION_RATE = 0.06; const CREDIT_CARD_FEE_RATE = 0.06; const ADMIN_RATE = 0.12; -const GEO_MULTIPLIER = 0.06; +const GEO_MULTIPLIER = 1.06; const constantsMock = gqlMock( GoalCalculatorConstantsDocument, @@ -71,7 +71,7 @@ const constantsMock = gqlMock( ], mpdGoalGeographicConstants: [ { location: 'Orlando, FL', percentageMultiplier: GEO_MULTIPLIER }, - { location: 'None', percentageMultiplier: 0 }, + { location: 'None', percentageMultiplier: 1 }, ], mpdGoalBenefitsConstants: [], }, @@ -212,14 +212,14 @@ describe('usePdsSummaryData', () => { expect(result.current?.geographicMultiplier).toBe(GEO_MULTIPLIER); }); - it('defaults to 0 when geographicLocation is null', () => { + it('defaults to 1 (no adjustment) when geographicLocation is null', () => { const { result } = renderHook(() => usePdsSummaryData(defaultCalculation, defaultHcmUser), ); - expect(result.current?.geographicMultiplier).toBe(0); + expect(result.current?.geographicMultiplier).toBe(1); }); - it('defaults to 0 when geographicLocation is not in the map', () => { + it('defaults to 1 (no adjustment) when geographicLocation is not in the map', () => { const calc = { ...defaultCalculation, geographicLocation: 'Unknown City', @@ -227,7 +227,7 @@ describe('usePdsSummaryData', () => { const { result } = renderHook(() => usePdsSummaryData(calc, defaultHcmUser), ); - expect(result.current?.geographicMultiplier).toBe(0); + expect(result.current?.geographicMultiplier).toBe(1); }); }); @@ -296,8 +296,8 @@ describe('usePdsSummaryData', () => { }); it('computes correct overallTotal for a full-time salaried employee', () => { - // No geographic multiplier, payRate = 60000 - // grossMonthlyPay = 60000 / 12 = 5000 + // Geographic multiplier defaults to 1 (no adjustment), payRate = 60000 + // grossMonthlyPay = 60000 / 12 * 1 = 5000 // employerFica = 5000 * 0.08 = 400 // salarySubtotal = 5400 // @@ -310,12 +310,13 @@ describe('usePdsSummaryData', () => { // otherSubtotal = 5400 + 500 + 400 + 0 + 1500 = 7800 // attrition = 7800 * 0.06 = 468 // creditCardFees = (7800 + 468) * 0.06 = 496.08 - // assessment = (7800 + 468 + 496.08) * 0.12 = 1051.69 - // overallTotal = 7800 + 468 + 496.08 + 1051.69 = 9815.77 + // adminBase = 7800 + 468 + 496.08 = 8764.08 + // assessment = adminBase / 0.88 - adminBase ≈ 1195.10 + // overallTotal = 7800 + 468 + 496.08 + 1195.10 ≈ 9959.18 const { result } = renderHook(() => usePdsSummaryData(defaultCalculation, defaultHcmUser), ); - expect(result.current?.overallTotal).toBeCloseTo(9815.77, 0); + expect(result.current?.overallTotal).toBeCloseTo(9959.18, 0); }); }); From 6ab40c7023272b2b1345e17a318cae334fc31030 Mon Sep 17 00:00:00 2001 From: wjames111 Date: Thu, 14 May 2026 14:22:16 -0400 Subject: [PATCH 05/10] Fix calculations --- .../HrTools/PdsGoalCalculator/calculations/OtherExpenses.ts | 4 +++- .../PdsGoalCalculator/calculations/pdsGoalConstants.ts | 1 - .../PdsGoalCalculator/calculations/salaryCalculation.ts | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/HrTools/PdsGoalCalculator/calculations/OtherExpenses.ts b/src/components/HrTools/PdsGoalCalculator/calculations/OtherExpenses.ts index 89c817f6ab..15068d8a77 100644 --- a/src/components/HrTools/PdsGoalCalculator/calculations/OtherExpenses.ts +++ b/src/components/HrTools/PdsGoalCalculator/calculations/OtherExpenses.ts @@ -54,7 +54,9 @@ export const calculateOtherExpenses = ( benefits; const attrition = subtotal * constants.attritionRate; - const creditCardFees = (subtotal + attrition) * constants.creditCardFeeRate; + const creditCardFees = + (subtotal + attrition) / (1 - constants.creditCardFeeRate) - + (subtotal + attrition); const adminBase = subtotal + creditCardFees + attrition; // Admin assessment is `adminRate` of the post-admin total, not a markup on // `adminBase`, so gross up: assessment / (adminBase + assessment) = adminRate. diff --git a/src/components/HrTools/PdsGoalCalculator/calculations/pdsGoalConstants.ts b/src/components/HrTools/PdsGoalCalculator/calculations/pdsGoalConstants.ts index d1a1ba702e..ef8379a914 100644 --- a/src/components/HrTools/PdsGoalCalculator/calculations/pdsGoalConstants.ts +++ b/src/components/HrTools/PdsGoalCalculator/calculations/pdsGoalConstants.ts @@ -43,7 +43,6 @@ export const buildPdsGoalConstants = ( ) { return null; } - // Multiplier is the *full* factor applied to monthlyBase (e.g. 1.06 for a // 6% high-cost location), so the no-adjustment default must be 1, not 0. const geographicMultiplier = diff --git a/src/components/HrTools/PdsGoalCalculator/calculations/salaryCalculation.ts b/src/components/HrTools/PdsGoalCalculator/calculations/salaryCalculation.ts index 9be7972240..d47af4622d 100644 --- a/src/components/HrTools/PdsGoalCalculator/calculations/salaryCalculation.ts +++ b/src/components/HrTools/PdsGoalCalculator/calculations/salaryCalculation.ts @@ -31,7 +31,7 @@ export const calculateSalaryTotals = ( calculation.salaryOrHourly === DesignationSupportSalaryType.Salaried; const monthlyBase = isSalaried ? payRate / 12 : (payRate * hours * 52) / 12; - const grossMonthlyPay = monthlyBase * geographicMultiplier; + const grossMonthlyPay = monthlyBase * (1 + geographicMultiplier); const employerFica = grossMonthlyPay * employerFicaRate; const subtotal = grossMonthlyPay + employerFica; From 4136ea8029ba1bde4f595fc102abcb74f28075a6 Mon Sep 17 00:00:00 2001 From: wjames111 Date: Thu, 14 May 2026 16:23:49 -0400 Subject: [PATCH 06/10] fix tests --- .../GoalCard/PdsGoalCard.test.tsx | 2 +- .../SupportItem/salaryBreakdown.test.tsx | 10 +++---- .../SupportItem/salaryBreakdown.tsx | 10 +++---- .../calculations/OtherExpenses.test.ts | 24 ++++++++--------- .../buildPdsGoalConstants.test.ts | 12 ++++----- .../calculations/pdsGoalConstants.ts | 4 +-- .../calculations/salaryCalculation.test.ts | 20 +++++++------- .../calculations/usePdsSummaryData.test.ts | 26 +++++++++---------- 8 files changed, 49 insertions(+), 59 deletions(-) diff --git a/src/components/HrTools/PdsGoalCalculator/GoalCard/PdsGoalCard.test.tsx b/src/components/HrTools/PdsGoalCalculator/GoalCard/PdsGoalCard.test.tsx index 9e6fd98306..8355678e28 100644 --- a/src/components/HrTools/PdsGoalCalculator/GoalCard/PdsGoalCard.test.tsx +++ b/src/components/HrTools/PdsGoalCalculator/GoalCard/PdsGoalCard.test.tsx @@ -17,7 +17,7 @@ describe('PdsGoalCard', () => { , ); - expect(await findByText('$8,043.95')).toBeInTheDocument(); + expect(await findByText('$8,073.02')).toBeInTheDocument(); }); it('builds the View link with the PDS goal calculator path', async () => { diff --git a/src/components/HrTools/PdsGoalCalculator/SupportItem/salaryBreakdown.test.tsx b/src/components/HrTools/PdsGoalCalculator/SupportItem/salaryBreakdown.test.tsx index 12fa8981e2..c12a0a874e 100644 --- a/src/components/HrTools/PdsGoalCalculator/SupportItem/salaryBreakdown.test.tsx +++ b/src/components/HrTools/PdsGoalCalculator/SupportItem/salaryBreakdown.test.tsx @@ -9,7 +9,7 @@ import { buildSalaryBreakdownRows, } from './salaryBreakdown'; -const constants = { geographicMultiplier: 1, employerFicaRate: 0.08 }; +const constants = { geographicMultiplier: 0, employerFicaRate: 0.08 }; const salariedCalculation: SalaryCalculationFields = { salaryOrHourly: DesignationSupportSalaryType.Salaried, @@ -49,7 +49,6 @@ describe('buildSalaryBreakdownRows', () => { expect(rows.map((row) => row.id)).toEqual([ 'pay-rate', 'monthly-base', - 'geographic-multiplier', 'gross-monthly-pay', 'employer-fica', 'total', @@ -69,9 +68,7 @@ describe('buildSalaryBreakdownRows', () => { expect(byId['pay-rate']).toBe(60000); // monthlyBase = 60000 / 12 = 5000 expect(byId['monthly-base']).toBe(5000); - // geographicMultiplier passed through from constants - expect(byId['geographic-multiplier']).toBe(1); - // grossMonthlyPay = 5000 * 1 = 5000 + // grossMonthlyPay = 5000 * (1 + 0) = 5000 expect(byId['gross-monthly-pay']).toBe(5000); // employerFica = 5000 * 0.08 = 400 expect(byId['employer-fica']).toBe(400); @@ -94,7 +91,7 @@ describe('buildSalaryBreakdownRows', () => { expect(byId['hours-per-week']).toBe(40); // monthlyBase = (25 * 40 * 52) / 12 ≈ 4333.33 expect(byId['monthly-base']).toBeCloseTo(4333.33, 2); - // grossMonthlyPay = 4333.33 * 1 ≈ 4333.33 + // grossMonthlyPay = 4333.33 * (1 + 0) ≈ 4333.33 expect(byId['gross-monthly-pay']).toBeCloseTo(4333.33, 2); // employerFica = 4333.33 * 0.08 ≈ 346.67 expect(byId['employer-fica']).toBeCloseTo(346.67, 2); @@ -113,7 +110,6 @@ describe('buildSalaryBreakdownRows', () => { 'pay-rate', 'hours-per-week', 'monthly-base', - 'geographic-multiplier', 'gross-monthly-pay', 'employer-fica', 'total', diff --git a/src/components/HrTools/PdsGoalCalculator/SupportItem/salaryBreakdown.tsx b/src/components/HrTools/PdsGoalCalculator/SupportItem/salaryBreakdown.tsx index f76be25ee9..f8b4e60bda 100644 --- a/src/components/HrTools/PdsGoalCalculator/SupportItem/salaryBreakdown.tsx +++ b/src/components/HrTools/PdsGoalCalculator/SupportItem/salaryBreakdown.tsx @@ -73,16 +73,12 @@ export const buildSalaryBreakdownRows = ( amount: monthlyBase, format: 'currency', }, - { - id: 'geographic-multiplier', - category: t('Geographic Multiplier'), - amount: geographicMultiplier, - format: 'number', - }, { id: 'gross-monthly-pay', category: t('Gross Monthly Pay'), - formula: t('Monthly Base × Geographic Multiplier'), + formula: t('Monthly Base × {{rate}}', { + rate: percentageFormat(geographicMultiplier, locale), + }), amount: grossMonthlyPay, format: 'currency', testId: 'gross-monthly-pay', diff --git a/src/components/HrTools/PdsGoalCalculator/calculations/OtherExpenses.test.ts b/src/components/HrTools/PdsGoalCalculator/calculations/OtherExpenses.test.ts index 33d738154d..485fdf43b5 100644 --- a/src/components/HrTools/PdsGoalCalculator/calculations/OtherExpenses.test.ts +++ b/src/components/HrTools/PdsGoalCalculator/calculations/OtherExpenses.test.ts @@ -137,18 +137,18 @@ describe('calculateOtherExpenses', () => { }); describe('credit card fees', () => { - it('is 6% of (subtotal + attrition)', () => { + it('grosses up (subtotal + attrition) so that fees are `creditCardFeeRate` of the post-fees total', () => { const result = calculateOtherExpenses(fullTime(), defaultConstants); - // (7400 + 444) * 0.06 - expect(result.creditCardFees).toBeCloseTo(470.64); + // (7400 + 444) / (1 - 0.06) - (7400 + 444) ≈ 500.68 + expect(result.creditCardFees).toBeCloseTo(500.68); }); }); describe('assessment', () => { it('grosses up (subtotal + creditCardFees + attrition) so that admin is `adminRate` of the post-admin total', () => { const result = calculateOtherExpenses(fullTime(), defaultConstants); - // adminBase=7400+470.64+444=8314.64; assessment = adminBase/0.88 - adminBase ≈ 1133.81 - expect(result.assessment).toBeCloseTo(1133.81, 1); + // adminBase=7400+500.68+444=8344.68; assessment = adminBase/0.88 - adminBase ≈ 1137.91 + expect(result.assessment).toBeCloseTo(1137.91, 1); }); it('returns 0 when adminRate is 0', () => { @@ -169,8 +169,8 @@ describe('calculateOtherExpenses', () => { expect(result.benefits).toBe(1500); expect(result.subtotal).toBeCloseTo(7400); expect(result.attrition).toBeCloseTo(444); - expect(result.creditCardFees).toBeCloseTo(470.64); - expect(result.assessment).toBeCloseTo(1133.81, 1); + expect(result.creditCardFees).toBeCloseTo(500.68); + expect(result.assessment).toBeCloseTo(1137.91, 1); }); it('produces correct totals for a part-time employee', () => { @@ -178,13 +178,13 @@ describe('calculateOtherExpenses', () => { // reimbursable=500, 403b=400, workComp=4000*0.17=680, benefits=0 // subtotal=5000+500+400+680+0=6580 // attrition=6580*0.06=394.80 - // creditCardFees=(6580+394.80)*0.06=418.49 - // adminBase=6580+418.49+394.80=7393.29 - // assessment = adminBase/0.88 - adminBase ≈ 1008.18 + // creditCardFees=(6580+394.80)/(1-0.06)-(6580+394.80)≈445.20 + // adminBase=6580+445.20+394.80=7420 + // assessment = adminBase/0.88 - adminBase ≈ 1011.82 expect(result.subtotal).toBeCloseTo(6580); expect(result.attrition).toBeCloseTo(394.8); - expect(result.creditCardFees).toBeCloseTo(418.49, 1); - expect(result.assessment).toBeCloseTo(1008.18, 1); + expect(result.creditCardFees).toBeCloseTo(445.2, 1); + expect(result.assessment).toBeCloseTo(1011.82, 1); }); }); }); diff --git a/src/components/HrTools/PdsGoalCalculator/calculations/buildPdsGoalConstants.test.ts b/src/components/HrTools/PdsGoalCalculator/calculations/buildPdsGoalConstants.test.ts index 2870431791..939def989b 100644 --- a/src/components/HrTools/PdsGoalCalculator/calculations/buildPdsGoalConstants.test.ts +++ b/src/components/HrTools/PdsGoalCalculator/calculations/buildPdsGoalConstants.test.ts @@ -108,7 +108,7 @@ describe('buildPdsGoalConstants', () => { expect(result).toBeNull(); }); - it('defaults geographicMultiplier to 1 (no adjustment) for unknown location', () => { + it('defaults geographicMultiplier to 0 (no adjustment) for unknown location', () => { const result = buildPdsGoalConstants( buildMiscConstants(), defaultGeoMap, @@ -116,10 +116,10 @@ describe('buildPdsGoalConstants', () => { null, ); - expect(result?.geographicMultiplier).toBe(1); + expect(result?.geographicMultiplier).toBe(0); }); - it('defaults geographicMultiplier to 1 (no adjustment) when location is null', () => { + it('defaults geographicMultiplier to 0 (no adjustment) when location is null', () => { const result = buildPdsGoalConstants( buildMiscConstants(), defaultGeoMap, @@ -127,10 +127,10 @@ describe('buildPdsGoalConstants', () => { null, ); - expect(result?.geographicMultiplier).toBe(1); + expect(result?.geographicMultiplier).toBe(0); }); - it('defaults geographicMultiplier to 1 (no adjustment) when location is undefined', () => { + it('defaults geographicMultiplier to 0 (no adjustment) when location is undefined', () => { const result = buildPdsGoalConstants( buildMiscConstants(), defaultGeoMap, @@ -138,7 +138,7 @@ describe('buildPdsGoalConstants', () => { null, ); - expect(result?.geographicMultiplier).toBe(1); + expect(result?.geographicMultiplier).toBe(0); }); it('looks up correct geographic multiplier', () => { diff --git a/src/components/HrTools/PdsGoalCalculator/calculations/pdsGoalConstants.ts b/src/components/HrTools/PdsGoalCalculator/calculations/pdsGoalConstants.ts index ef8379a914..360d557f91 100644 --- a/src/components/HrTools/PdsGoalCalculator/calculations/pdsGoalConstants.ts +++ b/src/components/HrTools/PdsGoalCalculator/calculations/pdsGoalConstants.ts @@ -43,10 +43,8 @@ export const buildPdsGoalConstants = ( ) { return null; } - // Multiplier is the *full* factor applied to monthlyBase (e.g. 1.06 for a - // 6% high-cost location), so the no-adjustment default must be 1, not 0. const geographicMultiplier = - goalGeographicConstantMap.get(geographicLocation ?? '') ?? 1; + goalGeographicConstantMap.get(geographicLocation ?? '') ?? 0; const taxDeferredPct = (fourOThreeB?.currentTaxDeferredContributionPercentage ?? 0) / 100; diff --git a/src/components/HrTools/PdsGoalCalculator/calculations/salaryCalculation.test.ts b/src/components/HrTools/PdsGoalCalculator/calculations/salaryCalculation.test.ts index d8904fca35..4f3d2cabd6 100644 --- a/src/components/HrTools/PdsGoalCalculator/calculations/salaryCalculation.test.ts +++ b/src/components/HrTools/PdsGoalCalculator/calculations/salaryCalculation.test.ts @@ -5,7 +5,7 @@ import { } from './salaryCalculation'; const FICA_RATE = 0.08; -const GEO_MULTIPLIER = 1.06; +const GEO_MULTIPLIER = 0.06; const salaried = ( overrides: Partial = {}, @@ -32,26 +32,26 @@ describe('calculateSalaryTotals', () => { describe('salaried', () => { it('divides yearly payRate by 12 when there is no geographic multiplier', () => { const result = calculateSalaryTotals(salaried(), { - geographicMultiplier: 1, + geographicMultiplier: 0, employerFicaRate: FICA_RATE, }); // 60000 / 12 expect(result.grossMonthlyPay).toBe(5000); }); - it('applies geographic multiplier as a full factor', () => { + it('applies geographic multiplier as a delta to the monthly base', () => { const result = calculateSalaryTotals(salaried(), { geographicMultiplier: GEO_MULTIPLIER, employerFicaRate: FICA_RATE, }); - // (60000 / 12) * 1.06 + // (60000 / 12) * (1 + 0.06) expect(result.grossMonthlyPay).toBeCloseTo(5300); }); it('ignores hoursWorkedPerWeek', () => { const result = calculateSalaryTotals( salaried({ hoursWorkedPerWeek: 40 }), - { geographicMultiplier: 1, employerFicaRate: FICA_RATE }, + { geographicMultiplier: 0, employerFicaRate: FICA_RATE }, ); expect(result.grossMonthlyPay).toBe(5000); }); @@ -60,7 +60,7 @@ describe('calculateSalaryTotals', () => { describe('hourly', () => { it('converts hourly rate to monthly when there is no geographic multiplier', () => { const result = calculateSalaryTotals(hourly(), { - geographicMultiplier: 1, + geographicMultiplier: 0, employerFicaRate: FICA_RATE, }); // 25 * 40 * 52 / 12 @@ -72,7 +72,7 @@ describe('calculateSalaryTotals', () => { geographicMultiplier: GEO_MULTIPLIER, employerFicaRate: FICA_RATE, }); - // (25 * 40 * 52 / 12) * 1.06 + // (25 * 40 * 52 / 12) * (1 + 0.06) expect(result.grossMonthlyPay).toBeCloseTo(4593.333, 2); }); }); @@ -91,7 +91,7 @@ describe('calculateSalaryTotals', () => { it('treats null hoursWorkedPerWeek as 0 when hourly', () => { const result = calculateSalaryTotals( hourly({ hoursWorkedPerWeek: null }), - { geographicMultiplier: 1, employerFicaRate: FICA_RATE }, + { geographicMultiplier: 0, employerFicaRate: FICA_RATE }, ); expect(result.grossMonthlyPay).toBe(0); }); @@ -100,7 +100,7 @@ describe('calculateSalaryTotals', () => { describe('employer FICA and subtotal', () => { it('multiplies grossMonthlyPay by the provided FICA rate', () => { const result = calculateSalaryTotals(salaried(), { - geographicMultiplier: 1, + geographicMultiplier: 0, employerFicaRate: FICA_RATE, }); expect(result.employerFica).toBeCloseTo(400); @@ -108,7 +108,7 @@ describe('calculateSalaryTotals', () => { it('uses the passed-in FICA rate verbatim (no fallback applied)', () => { const result = calculateSalaryTotals(salaried(), { - geographicMultiplier: 1, + geographicMultiplier: 0, employerFicaRate: 0.0765, }); expect(result.employerFica).toBeCloseTo(382.5); diff --git a/src/components/HrTools/PdsGoalCalculator/calculations/usePdsSummaryData.test.ts b/src/components/HrTools/PdsGoalCalculator/calculations/usePdsSummaryData.test.ts index af7a2b960b..d5bfc1e250 100644 --- a/src/components/HrTools/PdsGoalCalculator/calculations/usePdsSummaryData.test.ts +++ b/src/components/HrTools/PdsGoalCalculator/calculations/usePdsSummaryData.test.ts @@ -35,7 +35,7 @@ const WORK_COMP_PERCENTAGE = 0.17; const ATTRITION_RATE = 0.06; const CREDIT_CARD_FEE_RATE = 0.06; const ADMIN_RATE = 0.12; -const GEO_MULTIPLIER = 1.06; +const GEO_MULTIPLIER = 0.06; const constantsMock = gqlMock( GoalCalculatorConstantsDocument, @@ -71,7 +71,7 @@ const constantsMock = gqlMock( ], mpdGoalGeographicConstants: [ { location: 'Orlando, FL', percentageMultiplier: GEO_MULTIPLIER }, - { location: 'None', percentageMultiplier: 1 }, + { location: 'None', percentageMultiplier: 0 }, ], mpdGoalBenefitsConstants: [], }, @@ -212,14 +212,14 @@ describe('usePdsSummaryData', () => { expect(result.current?.geographicMultiplier).toBe(GEO_MULTIPLIER); }); - it('defaults to 1 (no adjustment) when geographicLocation is null', () => { + it('defaults to 0 (no adjustment) when geographicLocation is null', () => { const { result } = renderHook(() => usePdsSummaryData(defaultCalculation, defaultHcmUser), ); - expect(result.current?.geographicMultiplier).toBe(1); + expect(result.current?.geographicMultiplier).toBe(0); }); - it('defaults to 1 (no adjustment) when geographicLocation is not in the map', () => { + it('defaults to 0 (no adjustment) when geographicLocation is not in the map', () => { const calc = { ...defaultCalculation, geographicLocation: 'Unknown City', @@ -227,7 +227,7 @@ describe('usePdsSummaryData', () => { const { result } = renderHook(() => usePdsSummaryData(calc, defaultHcmUser), ); - expect(result.current?.geographicMultiplier).toBe(1); + expect(result.current?.geographicMultiplier).toBe(0); }); }); @@ -296,8 +296,8 @@ describe('usePdsSummaryData', () => { }); it('computes correct overallTotal for a full-time salaried employee', () => { - // Geographic multiplier defaults to 1 (no adjustment), payRate = 60000 - // grossMonthlyPay = 60000 / 12 * 1 = 5000 + // Geographic multiplier defaults to 0 (no adjustment), payRate = 60000 + // grossMonthlyPay = 60000 / 12 * (1 + 0) = 5000 // employerFica = 5000 * 0.08 = 400 // salarySubtotal = 5400 // @@ -309,14 +309,14 @@ describe('usePdsSummaryData', () => { // workComp = 0 (full-time) // otherSubtotal = 5400 + 500 + 400 + 0 + 1500 = 7800 // attrition = 7800 * 0.06 = 468 - // creditCardFees = (7800 + 468) * 0.06 = 496.08 - // adminBase = 7800 + 468 + 496.08 = 8764.08 - // assessment = adminBase / 0.88 - adminBase ≈ 1195.10 - // overallTotal = 7800 + 468 + 496.08 + 1195.10 ≈ 9959.18 + // creditCardFees = (7800 + 468) / (1 - 0.06) - (7800 + 468) ≈ 527.74 + // adminBase = 7800 + 468 + 527.74 ≈ 8795.74 + // assessment = adminBase / 0.88 - adminBase ≈ 1199.42 + // overallTotal = 7800 + 468 + 527.74 + 1199.42 ≈ 9995.16 const { result } = renderHook(() => usePdsSummaryData(defaultCalculation, defaultHcmUser), ); - expect(result.current?.overallTotal).toBeCloseTo(9959.18, 0); + expect(result.current?.overallTotal).toBeCloseTo(9995.16, 0); }); }); From ead7b68246e11f2ddbb404b24e729dff213ae025 Mon Sep 17 00:00:00 2001 From: wjames111 Date: Thu, 14 May 2026 16:33:52 -0400 Subject: [PATCH 07/10] Fix The Gross Monthly Pay display formula --- .../HrTools/PdsGoalCalculator/SupportItem/salaryBreakdown.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/HrTools/PdsGoalCalculator/SupportItem/salaryBreakdown.tsx b/src/components/HrTools/PdsGoalCalculator/SupportItem/salaryBreakdown.tsx index f8b4e60bda..80c3330382 100644 --- a/src/components/HrTools/PdsGoalCalculator/SupportItem/salaryBreakdown.tsx +++ b/src/components/HrTools/PdsGoalCalculator/SupportItem/salaryBreakdown.tsx @@ -77,7 +77,7 @@ export const buildSalaryBreakdownRows = ( id: 'gross-monthly-pay', category: t('Gross Monthly Pay'), formula: t('Monthly Base × {{rate}}', { - rate: percentageFormat(geographicMultiplier, locale), + rate: percentageFormat(1 + geographicMultiplier, locale), }), amount: grossMonthlyPay, format: 'currency', From 7d8e9b4fed098ed1c56e226d7caed2b3699f471c Mon Sep 17 00:00:00 2001 From: Daniel Bisgrove Date: Fri, 15 May 2026 12:08:14 -0400 Subject: [PATCH 08/10] MPDX-9562 --- .../DirectionButtons/DirectionButtons.test.tsx | 4 ++-- .../CalculationReports/DirectionButtons/DirectionButtons.tsx | 4 +--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/components/HrTools/Shared/CalculationReports/DirectionButtons/DirectionButtons.test.tsx b/src/components/HrTools/Shared/CalculationReports/DirectionButtons/DirectionButtons.test.tsx index 540cf55729..c442284174 100644 --- a/src/components/HrTools/Shared/CalculationReports/DirectionButtons/DirectionButtons.test.tsx +++ b/src/components/HrTools/Shared/CalculationReports/DirectionButtons/DirectionButtons.test.tsx @@ -138,7 +138,7 @@ describe('DirectionButtons', () => { userEvent.hover(continueButton.parentElement!); expect( - await findByText('Complete all required fields to continue'), + await findByText('Complete all fields to continue'), ).toBeInTheDocument(); }); @@ -152,7 +152,7 @@ describe('DirectionButtons', () => { await waitFor(() => { expect( - queryByText('Complete all required fields to continue'), + queryByText('Complete all fields to continue'), ).not.toBeInTheDocument(); }); }); diff --git a/src/components/HrTools/Shared/CalculationReports/DirectionButtons/DirectionButtons.tsx b/src/components/HrTools/Shared/CalculationReports/DirectionButtons/DirectionButtons.tsx index b4266f250c..d93eaf700b 100644 --- a/src/components/HrTools/Shared/CalculationReports/DirectionButtons/DirectionButtons.tsx +++ b/src/components/HrTools/Shared/CalculationReports/DirectionButtons/DirectionButtons.tsx @@ -142,9 +142,7 @@ export const DirectionButtons: React.FC = ({ ) : (