Skip to content

feat(payroll-scheduler): wire scheduler UI to a real backend cron pip… - #51

Open
Mrwicks00 wants to merge 1 commit into
LatterFixxx:mainfrom
Mrwicks00:feat/43-payroll-scheduler-backend-wiring
Open

feat(payroll-scheduler): wire scheduler UI to a real backend cron pip…#51
Mrwicks00 wants to merge 1 commit into
LatterFixxx:mainfrom
Mrwicks00:feat/43-payroll-scheduler-backend-wiring

Conversation

@Mrwicks00

Copy link
Copy Markdown
Contributor

Title:
feat(payroll-scheduler): wire scheduler UI to a real backend cron pipeline (#081)

Body:

Summary

  • Adds the missing /api/schedules backend from scratch: migration (payroll_schedules + payroll_schedule_runs), service (PayrollScheduleService), controller, and routes (POST /api/schedules, GET /api/schedules, DELETE /api/schedules/:id), all scoped by the JWT-derived organizationId (unlike a couple of sibling payroll routes in this codebase, this one actually wires authenticateJWT).
  • Adds sorobanContractService.ts — a server-side Soroban simulate/sign/submit utility (there was previously no backend code anywhere that invoked a Soroban contract; the existing payroll worker only signs classic Stellar Operation.payments).
  • Adds a BullMQ repeatable dispatcher (payrollScheduleQueueService.ts + payrollScheduleWorker.ts, ticking every 60s) that finds due schedules and fires bulk_payment.execute_batch on-chain, signed by a service keypair read from a new BULK_PAYMENT_SENDER_SECRET env var — mirroring the existing ORGUSD_DISTRIBUTION_SECRET pattern already in production use for classic payments (per your call on the signing-custody question).
  • Connects the previously-orphaned SchedulingWizard.tsx/CountdownTimer.tsx components (zero API calls, not rendered anywhere) and a brand-new src/pages/PayrollScheduler.tsx (no such page existed — only an unrelated, unused upstream_PayrollScheduler.tsx scaffold at the repo root) into a real flow: create → persists via POST /api/schedules → listed with a live CountdownTimer driven by the server's next_run_at → cancel calls DELETE /api/schedules/:id and removes it from the list immediately.
  • SchedulingWizard gained a required "Token Contract Address" field and a per-recipient wallet-address input (previously mock name/amount/currency only) since bulk_payment.execute_batch needs real addresses and a token contract to execute.

Acceptance criteria

  • Save Schedule submits to POST /api/schedules and persists config.
  • Active schedules listed with next-run timestamp from backend.
  • Cancellation calls DELETE /api/schedules/:id and updates UI immediately.
  • CountdownTimer driven by the server-returned next_run_at timestamp.
  • Backend job executes bulk_payment contract invocation at the scheduled time.

Known limitations (called out explicitly, not silently glossed over)

  • A schedule's recipient count is capped at 100 (bulk_payment's execute_batch batch limit) — schedules with more recipients fail loudly with a clear error rather than attempting fragile multi-call sequence chaining. Documented as a scope limit, not a bug.
  • Recipient amounts are converted to i128 stroops assuming 7-decimal precision, matching this codebase's existing DECIMAL(20, 7) convention for payroll amounts elsewhere.
  • Live end-to-end firing depends on infrastructure this sandbox doesn't have: a funded, authorized BULK_PAYMENT_SENDER_SECRET account, a reachable Redis instance for BullMQ, and the bulk_payment contract registered in the contract registry for the target network. None of that could be exercised here — correctness is covered by unit/controller tests instead (see below), plus a manual dev-server pass with the backend unavailable (graceful error states, no crashes).
  • No employee-directory API exists yet, so recipient wallet addresses/amounts are entered manually in the wizard rather than picked from a roster — out of scope for this issue.

Test plan

  • Backend (Jest): payrollScheduleService.test.ts (6 cases covering computeNextRunAt for weekly/biweekly/monthly, including rollover-when-already-passed and default-day edge cases) and payrollScheduleController.test.ts (7 cases: create/list/cancel happy paths, invalid frequency, empty recipients, cross-org 404, missing schedule 404) — 13/13 pass.
  • Frontend (vitest): paymentSchedule.test.ts (5 cases: create/list/cancel, auth header attachment, server-error propagation) and PayrollScheduler.test.tsx (4 cases: renders schedules with countdown, error state, cancel removes a row immediately, wizard create flow) — 9/9 pass; full suite 15/15.
  • npx tsc -b (frontend) and npx tsc --noEmit (backend) — clean; confirmed via git stash that the backend's pre-existing ~56 type errors are unrelated to this change (they exist on main too, in files this PR never touches).
  • npx eslint on all changed frontend files — clean (backend's npm run lint is broken pre-existing on main, ignoring every file including untouched ones — not something this PR introduced or fixes).
  • Manually loaded /payroll-scheduler in the dev server (headless Chrome + screenshots): nav link renders and highlights correctly, page shows a graceful "Failed to fetch"/empty state with no backend running, wizard opens with the new Token Contract Address field, no console page errors.

Closes #43

…eline

Adds the missing /api/schedules backend (migration, service, controller,
routes) plus a BullMQ-driven dispatcher that fires bulk_payment.execute_batch
on-chain via a new server-side Soroban invocation utility, signed by a
service keypair (mirroring the existing ORGUSD_DISTRIBUTION_SECRET pattern).
Connects the previously orphaned SchedulingWizard/CountdownTimer components
and a new PayrollScheduler page to this API, with live countdowns and
immediate cancellation.
@Mrwicks00
Mrwicks00 force-pushed the feat/43-payroll-scheduler-backend-wiring branch from 22b715b to 77d45c2 Compare July 29, 2026 22:50
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.

043: Payroll Scheduler Backend Wiring

1 participant