feat(mileage): let caregivers log driving and agencies approve it - #190
Merged
Conversation
Home-care caregivers drive between clients all day and are commonly reimbursed for it, but there was nowhere in the platform to record that, so agencies were collecting it on paper or not at all. Caregivers log trips in the mobile app and watch them move through review. Agency staff approve or reject from a new Mileage page. The split is strict: a caregiver may only see, create, or withdraw their own entries, and only staff with staff.write may rule on one. A caregiver approving their own reimbursement would defeat the point of the workflow. Approved and awaiting-review totals are shown separately rather than blended, because agencies pay on approved trips and one combined number would overstate what is actually coming. Miles are stored as integer hundredths for the same reason money is stored in cents: a float odometer difference summed over a month drifts. Rounding happens once at the route boundary so the integer is the only representation the rest of the system sees. Review only matches rows still in 'submitted'. That makes the transition safe under a double click and stops a second coordinator silently overwriting the first decision, and it is why a caregiver cannot delete a trip the agency has already ruled on: the record of that decision is not theirs to erase. Both cases answer 404 without disclosing which one happened. The purpose field is caregiver-authored free text and is treated as potentially PHI, because somebody will eventually type a client's name into it. It stays inside agency-scoped responses and never reaches an audit payload or a notification body.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Home-care caregivers drive between clients all day and are commonly reimbursed for it. There was nowhere in the platform to record that, so agencies were collecting it on paper or not at all.
What
Caregivers log trips in the mobile app (Me → Mileage) and watch them move through review. Agency staff approve or reject from a new Mileage page under Agency.
Access split is the point
A caregiver may only see, create, or withdraw their own entries. Only staff with
staff.writemay rule on one. A caregiver approving their own reimbursement would defeat the entire workflow, so that boundary has its own test.Approved and pending stay separate
Both the mobile screen and the review page keep approved and awaiting-review totals apart rather than blending them. Agencies pay on approved trips; a single combined number would overstate what is actually coming.
Integer miles
Stored as hundredths (12.34 mi = 1234), same reason money is stored in cents: a float odometer difference summed over a month drifts. Rounding happens once at the route boundary, so the integer is the only representation the rest of the system ever sees.
Concurrency and immutability
review()only matches rows still insubmitted. That makes the transition safe under a double click, stops a second coordinator silently overwriting the first decision, and is the same clause that prevents a caregiver deleting a trip the agency already ruled on: the record of that decision is not theirs to erase. Both cases answer 404 without disclosing which happened, so rows can't be probed across tenants.PHI
purposeis caregiver-authored free text and is treated as potentially PHI, because somebody will eventually type a client's name into it. It stays inside agency-scoped responses and never reaches an audit payload or a notification body. This is stated in the migration, the repository, and the routes.Validation
Future-dated trips, zero/negative distances, and anything over 500 miles are rejected. The ceiling is a typo guard, not a policy: a caregiver does not drive 500 miles between clients in a day, and catching a slipped decimal beats explaining a four-figure reimbursement later. The client enforces the same bound so it never sends what the API will bounce.
Migration
2026-08-04-add-mileage-entries, one new table. Needs applying to prod. Until then the screens load and every call 500s.Tests
26 new: hundredths conversion and boundary rounding, future-date refusal, caregiver-cannot-approve, staff-vs-caregiver list scoping, already-reviewed 404, withdrawal limited to unreviewed trips, input parsing with actionable error messages, approved/pending split. All four suites pass, plus typecheck, lint,
sql:scan,security:scan.Not included
No reimbursement rate and no dollar figure. Mileage rates vary by agency and change annually with the IRS standard rate, and guessing one would put a wrong number in front of a caregiver. This tracks distance and approval; converting to dollars is a follow-up that needs an agency-level rate setting.