Skip to content

feat: surface payrollCalendarID on list-payroll-employees#202

Open
elliotchisholm wants to merge 13 commits into
XeroAPI:mainfrom
elliotchisholm:feat/au-v2/10-employee-payroll-calendar
Open

feat: surface payrollCalendarID on list-payroll-employees#202
elliotchisholm wants to merge 13 commits into
XeroAPI:mainfrom
elliotchisholm:feat/au-v2/10-employee-payroll-calendar

Conversation

@elliotchisholm

Copy link
Copy Markdown
Contributor

What

Adds payrollCalendarID to the list-payroll-employees output so callers can see which payroll calendar each employee is assigned to.

  • Adds payrollCalendarID to the EmployeeResult interface
  • Maps it from the AU employee payload in mapAuEmployee
  • Includes it in the tool's formatted output (with an explicit "No payroll calendar assigned" fallback) and in the tool description
  • Extends the handler test to assert the field is surfaced

Why

The payroll calendar assignment is needed to relate an employee to their pay run cadence. It was already present on the AU employee payload but dropped before reaching the tool response.

Testing

  • npm run build clean
  • Full suite passes (108 tests, 23 files)

Stack note

Part of the AU Payroll v2 series. This branch is stacked on feat/au-v2/9-tool-tests and depends on the earlier commits (it extends mapAuEmployee, introduced in the region-routing fix). Please merge after the preceding PRs (#166#171) land; the diff will narrow to just this change once the ancestors are merged into main.

Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com

elliotchisholm and others added 13 commits April 24, 2026 20:33
Centralize all deep-path xero-node imports (xero-node/dist/gen/model/...)
into two shim files: payroll-nz-types.ts and payroll-au-types.ts. This
isolates fragile import sites to 2 files, de-risking the upcoming SDK
upgrade from v13 to v15.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Rename label 'Hours Accrued Annually' → 'Units Accrued Annually'
- Add typeOfUnitsToAccrue field so consumers know what unit type the value represents
- Remove duplicate Leave Type ID output

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Upgrade xero-node from ^13.3.0 to ^15.0.1
- Add PayrollAuV2Api client to xero-client.ts with lazy init and token sync
- Add payroll-au-v2-types.ts shim for AU v2 Timesheet/TimesheetLine types
- Fix SDK breaking change: hoursAccruedAnnually renamed to unitsAccruedAnnually
- Add vitest for test infrastructure

The PayrollAuV2Api is a separate class not wired into XeroClient, so it
requires manual instantiation and access token management.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace all AU-specific workaround code with v2 API calls via
PayrollAuV2Api. This aligns AU with NZ/UK — all three regions now use
the same v2 endpoint patterns.

Changes per handler:
- approve: remove GET-then-PUT status workaround, call approveTimesheet
- revert: remove GET-then-PUT status workaround, call revertTimesheet
- delete: remove AU error block, call deleteTimesheet (previously unsupported)
- create: remove array wrapping, use single-object createTimesheet
- add-line: remove GET-append-PUT workaround, call createTimesheetLine
- update-line: remove earningsRateID matching, call updateTimesheetLine by ID
- get: call getTimesheet via v2 client
- list: remove AU-only params (where/order/ifModifiedSince), use v2 params

Breaking input changes for AU callers:
- numberOfUnits is now a single number (not array) with a date per line
- payrollCalendarID is now required on create
- timesheetLineID is now required on update-line

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New handler, tool, and tests for DELETE /timesheets/{id}/lines/{lineId}.
Previously this operation had no MCP tool. Supports AU (via
PayrollAuV2Api), NZ, and UK.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New tools:
- list-payroll-calendars: shows pay calendar names, types, and upcoming
  payment dates. AU uses payrollAUApi.getPayrollCalendars (v1), NZ/UK
  use getPayRunCalendars.
- list-earnings-rates: shows earnings rate IDs, names, and types needed
  for creating timesheet lines. AU uses payrollAUApi.getPayItems (v1)
  and extracts the earningsRates array, NZ/UK use getEarningsRates.

Both tools support AU, NZ, and UK with region auto-detection.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
For AU payroll, timesheets should be created empty then lines added
individually via add-timesheet-line. Passing lines at creation time
creates separate rows per day in the Xero UI instead of consolidating
them by earnings rate.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Route list-payroll-employees, list-payroll-employee-leave,
list-payroll-employee-leave-balances, list-payroll-employee-leave-types,
and list-payroll-leave-periods to the correct regional API using
xeroClient.getRegion(), following the same pattern as list-payroll-calendars
and list-earnings-rates.

AU orgs: list-payroll-employees now calls payrollAUApi.getEmployees (v1).
The 4 leave handlers short-circuit with a clear isError message pointing at
the unbuilt AU leave-applications gap, rather than calling the NZ v2
endpoints and receiving a 405 MethodNotAllowed from Xero.

UK/NZ orgs: calls the correct regional API (payrollUKApi vs payrollNZApi).

All 5 handlers now export normalized result interfaces (EmployeeResult,
EmployeeLeaveResult, etc.) so tool response shape is consistent across
regions. 18 new vitest cases added across the 5 handlers.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The tool layer surfaces typeOfUnitsToAccrue alongside unitsAccruedAnnually
so consumers know whether the accrual value is in hours, days, etc. NZ
exposes this field directly; UK only accrues in hours so we default to
"Hours" when normalising the result interface.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The xero-node SDK rejects failed API calls with a plain object (not an
Error or AxiosError) whose request.headers.authorization field contains
the caller's Bearer token. formatError's previous fallback path used
template string interpolation which serialised the entire object,
leaking the token into LLM context.

Fix: add an isXeroSdkError type guard that detects the SDK's
{response: {statusCode, body}} shape and extracts only
problem.title/detail and httpStatusCode. The unknown-error fallback now
returns a fixed string rather than stringifying the object.

The 405 MethodNotAllowed response against AU tenants (previously a 4KB
JSON blob with a Bearer token) now produces:
  "405 MethodNotAllowed: Method not allowed for the current customer jurisdiction."

14 new vitest cases including explicit assertions that no Bearer token or
cookie values appear in any error output path.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Asserts label strings, null-filter behaviour, and error-path rendering on
the tool that formats employee leave types. Handler-layer tests already
cover the data shape; this catches presentation bugs (wrong labels,
duplicate fields) that data-shape tests can't see.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds focused tool-layer tests for tools with complex conditional rendering:
- list-payroll-employees (fallback strings, optional fields)
- list-payroll-employee-leave (default labels, optional fields)
- list-payroll-employee-leave-balances (balance: 0 edge case, fallbacks)
- list-payroll-timesheets (totalHours zero vs N/A, empty result)
- get-payroll-timesheet (error / no-result / JSON paths)

Pattern matches the existing list-payroll-employee-leave-types tool test:
mock the handler import, invoke tool().handler({...}), assert on rendered
text. Where the handler's typed return value uses Date / enum types, an
asResult cast helper keeps fixtures readable without losing type safety
elsewhere.

22 new tests, 108/108 total passing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add payrollCalendarID to EmployeeResult, map it from the AU employee
payload, and include it in the tool output and description so callers
can see which payroll calendar each employee is assigned to.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant