Conversation
Bundle sizes [mpdx-react]Compared against df619f6
|
142873a to
911af15
Compare
| const incomeMonthly = new Array(monthCount).fill(0); | ||
| const expenseMonthly = new Array(monthCount).fill(0); | ||
| const reader = splitsPerPerson(category, household) | ||
| ? household[0].personNumber |
There was a problem hiding this comment.
reader = household[0] is whoever Hcm was queried for (the login in own view, the drawer row's personNumber in supervisor view), not the staff account's primary; unattributed payroll is credited to and the table led by a different person depending on who views.
Failure scenario: Alex (primary) and Jordan (spouse) share one staffAccountId; schema documents hcm as 'Fetch HCM data for a person and their spouse, defaulting to yourself'. Alex logs in: unattributed payroll -> 'Salary (Alex)', listed first. Jordan logs in (or a supervisor opens Jordan's drawer row): same data, unattributed payroll -> 'Salary (Jordan)', listed first. Same account, different per-person totals. Pre-existing shared assumption with filterTransactions.tsx [reader] = household, but this PR extends it; at minimum the HouseholdMember docstring should say 'the person requested', not 'the staff member reading the report'.
| // Only a missing one falls back to your own account. | ||
| // Only a missing one falls back to your own account and HCM record. | ||
| const staffAccountId = getQueryParam(query, 'staffAccountId') || undefined; | ||
| const personNumber = getQueryParam(query, 'personNumber') || undefined; |
There was a problem hiding this comment.
personNumber is read from the URL independently of staffAccountId; a supervisor URL carrying only staffAccountId (bookmark, hand-built link, any producer other than ViewReportLink) skips Hcm and silently renders one combined Salary row for a couple, and a personNumber of a different supervised employee is never cross-checked against the account.
Failure scenario: Supervisor bookmarked ?staffAccountId=1000000001 before this PR -> skip: isSupervisorView && !personNumber -> combined 'Salary' row while the staff member's own view shows two rows; nothing hints the split exists. With ?staffAccountId=AAA&personNumber=BBB where BBB is another supervised employee, Hcm returns BBB's household, so AAA's real people render as 'Salary (Spouse)' and unattributed payroll is credited to BBB. Same pattern already exists on the staffExpense page, so this is a shared design gap rather than a regression.
| () => | ||
| hcmData?.hcm.map(({ staffInfo }) => ({ | ||
| personNumber: staffInfo.personNumber, | ||
| name: staffInfo.preferredName ?? staffInfo.lastName, |
There was a problem hiding this comment.
preferredName ?? lastName only falls back on null/undefined; an empty-string preferredName produces name: '', which fails the person ? ... : categoryLabel truthiness check downstream and leaves that spouse's split Salary row unlabeled.
Failure scenario: HCM returns { preferredName: '', lastName: 'Smith', personNumber: '000000111' } for the reader (preferredName is a nullable String with no client-side normalization). filterFunds: household.find(...)?.name ?? unknownName keeps '' (not null, so no 'Spouse' fallback), description = person ? ... : categoryLabel yields plain 'Salary' beside 'Salary (Jordan)'; BreakdownModal likewise shows 'Salary Breakdown'. preferredName || lastName closes it (same latent edge in StaffExpenseReport.tsx:108).
| // Person numbers tell the reader's payroll from their spouse's. HCM lists the reader first, then | ||
| // their spouse. Both requests leave together; `loading` below covers them both so salary is not | ||
| // rendered as one household total and then split. | ||
| const { data: hcmData, loading: hcmLoading } = useHcmQuery({ |
There was a problem hiding this comment.
Cleanup: the useHcmQuery call (with skip: isSupervisorView && !personNumber) and the household useMemo mapping to { personNumber, name: preferredName ?? lastName } are copied line-for-line from StaffExpenseReport.tsx:100-111, and loading = reportLoading || hcmLoading from :154; the PR created Reports/Shared/Helpers/household.ts for the type but left the behavior duplicated.
Failure scenario: Two reports now own identical 15-line blocks that must agree on the skip rule, name fallback, error handling and loading gate; fixing any of the findings above (suppressErrors, || fallback, reader selection) has to be done twice and will drift. Extract useHousehold({ personNumber, isSupervisorView }) returning { household, loading } next to HouseholdMember and call it from both reports.
| // Naming a person is only worth the noise once the report holds someone besides the reader. | ||
| const namePeople = people.size > 1; | ||
| // A person number HCM does not list belongs to neither spouse, so the household cannot name it. | ||
| const unknownName = t('Spouse'); |
There was a problem hiding this comment.
Cleanup: the per-person rules are now written in three places -- the naming block (t('Spouse'), household.find(...)?.name ?? unknownName, t('{{bucket}} ({{person}})')) is near-verbatim from filterTransactions.tsx:214-229, splitsPerPerson's === Salary duplicates aggregationPolicy.ts's perPerson: true, and BreakdownModal.tsx:46 re-checks category === Salary even though person is only ever set on the Salary path.
Failure scenario: Renaming the 'Spouse' fallback, changing the label format, or flagging another category perPerson desyncs the two reports the PR says should match, and the modal silently drops the name for any future split category (the new test 'ignores a person on any category other than salary' pins an unreachable state). Move householdMemberLabel(bucketLabel, personNumber, household, t) and a single splitsPerPerson(category) into Reports/Shared/Helpers/household.ts; in BreakdownModal trust person ? ... : ... (or pass the row's description in BreakdownTarget) and drop the enum import and that test.
There was a problem hiding this comment.
The Salary check in BreakdownModal is deliberate hardening: the modal should only ever name a person for the one category that splits per person, even if a caller passes a person for something else. The shared-helper extraction across both reports is agreed as a follow-up rather than part of this PR.
| t, | ||
| incomeData, | ||
| expenseData, | ||
| household, |
There was a problem hiding this comment.
(Refers to the addRowPerSubcategory branch at line 50, just above this hunk.)
Only the checked-category branch (addCombinedSubcategoryRow) receives household; unchecking Salary in Report Settings routes to addRowPerSubcategory, which collapses both spouses' payroll into one household 'Salary - Regular Pay' row.
Failure scenario: Couple's report shows 'Salary (Alex)' / 'Salary (Jordan)'. User unchecks Salary to itemize -> rows become a single 'Salary - Regular Pay' with both people's monthly totals summed and no person; re-checking splits them again. MPGA's itemized mode is the only place a couple's payroll is summed into a shared row (Staff Expense's itemized mode is one row per transaction, so inherently per person).
There was a problem hiding this comment.
Intentional. Unchecking Salary in Report Settings itemizes it, the same way unchecking a category itemizes in the Staff Expense report, and itemized rows are never split by person there either. Only the combined Salary row splits.
911af15 to
f3db576
Compare
The Staff Expense report already gives each spouse their own salary rows, but the MPGA report still rolled a couple's payroll into a single "Salary" line. Ask the API for each transaction's person number, load the household from HCM the way the Staff Expense report does, and split the combined Salary row into one row per person, reader first, labeled "Salary (Name)". Unattributed payroll stays with the reader and a person HCM does not list reads "Spouse". A single staff member's row is unchanged. Each person's monthly amount is summed from their own transactions, as the Staff Expense report does, so a rounding gap between the API's month total and its transactions never becomes a phantom row for a person the month did not pay. Every split row keeps the Salary category, so the pie chart, totals, CSV export, and breakdown modal work as before. The supervisor drawer's MPGA link now carries the person number so a supervisor sees the same split. The HCM and report requests leave together, and the report counts as loading until both land, so salary is never shown combined and then split. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
With salary split one row per person, both rows opened a breakdown titled plain "Salary Breakdown" with different totals, so the two could not be told apart. Carry the person's name on the row and into the breakdown target, and compose the modal title and footer from the category plus person using the same string the row label uses. Rows that are not split are unchanged. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
f3db576 to
29ccaff
Compare
Summary
The Staff Expense report already gives each spouse their own salary rows, but the MPGA report still rolled a couple's payroll into a single "Salary" line. This makes the MPGA report match.
personNumberin the MPGA transactions querypersonNumberquery param and the supervisor drawer's MPGA tab passes it in the link, matching the Staff Expense tabHouseholdMembermoves toReports/Shared/Helpers/household.tsso both reports import it from one placePer-subcategory rows shown when Salary is unchecked in Report Settings stay unsplit, matching the Staff Expense report where unchecking itemizes.
Jira
MPDX-9992
Testing
useFilteredFundstests: couple split with reader first, per-row breakdowns, unattributed payroll, blank person numbers, a month total that disagrees with its transactions, no phantom row for a person the month did not pay, single member, unknown person labeled "Spouse", no household, non-salary categories staying whole. Watched five fail before the implementation.ExportCsvButtontest clicked a menu item before data arrived. The added HCM round-trip exposed that, so it now waits for the item to enable like its siblings.🤖 Generated with Claude Code