feat(backend): add core work data APIs - #8
Merged
Conversation
Checkpoint of in-progress Phase 3A work data backend before pausing. Incomplete: models/schemas/routers/migration not yet added. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EwR3pvvEE1hy9NNrmMjmCQ
Build out Phase 3A on the existing scaffolding (geo + owned-record mixin): full CRUD for workplaces, work_schedules, and attendance_records. - Models + Alembic 0003_work_data (FKs, unique(user_id, client_id), check constraints, indexes). Times as "HH:mm" strings, work_date as DATE, money as integer won. Schema mirrors the mobile local models. - Ownership scoping (cross-user/missing -> 404), client_id idempotency (active replay -> 200, deleted -> 409), soft delete that preserves history (deleting a workplace keeps its schedules/attendance; new refs to a deleted workplace -> 422). - GPS distance/proximity recomputed server-side from workplace coords via core/geo (client-sent distance ignored); minimal fix so radius check uses the raw distance (mobile parity). - Pagination + workplace_id/date_from/date_to filters, deterministic ordering, internal fields hidden in responses. - 45 new tests (100 passed, 2 skipped); Phase 2 auth suite unchanged. Not yet wired to the mobile app; not deployed to Neon/Render. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019AhusKsaukQ5TyJYCUgJvm
dohyeon16
marked this pull request as ready for review
August 5, 2026 05:59
Preview verified (Neon Preview DB 0003_work_data, live verification 55/55 PASS); Production migration/deploy still pending. Docs only; no code/migration/test changes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PKqTTjZMNgaK1k6Udw8ATX
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.
Summary
Phase 3A on top of the existing WIP scaffolding (
92f8891:geo.py+ owned-record mixin). Completes full CRUD for the three core work-data resources: workplaces, work_schedules, attendance_records.Builds on Phase 2 auth (base
main=770416e) — every endpoint is authenticated and scoped to the token's user. Schema is derived from the mobile app's local storage models, not guessed.What's in it
Models & migration — Alembic
0003_work_data(←0002_auth_tables): 3 tables with FKs,unique(user_id, client_id), check constraints (non-negative wage/break, coordinate range + pairing), and per-resource indexes. Times stored as"HH:mm"strings,work_dateasDATE, money as integer won (no float).Ownership & security — all queries scoped to the current user; cross-user or missing IDs return 404 (no existence leak); invalid UUIDs → 422. Two-account isolation covered by tests.
client_id idempotency —
unique(user_id, client_id). Re-sending the sameclient_idreturns the existing record (200) instead of duplicating; re-creating a deletedclient_id→ 409 (prevents offline-sync resurrection).Deletion policy — soft delete everywhere. Deleting a workplace does not cascade-delete its schedules/attendance (history preserved). New schedule/attendance referencing a deleted (or others') workplace → 422.
Dates & times —
work_dateis a realDATE(no UTC conversion);created_at/updated_atare timezone-aware UTC. Midnight-crossing shifts supported (end < start ⇒ next day), no separate end-date column, matching the mobile calc rule.GPS —
clock_in_proximity/clock_out_proximityin responses are recomputed server-side from workplace coords viacore/geo(client-sent distance is never accepted). Minimal fix togeo.proximityso the radius check uses the raw (un-rounded) distance — matches mobileevaluateProximity.Lists —
limit/offsetpagination (default 50, max 200),workplace_id/date_from/date_tofilters, deterministic ordering, internal fields (user_id,deleted_at) hidden.Tests
0003_work_data, offline PG SQL for all 3 tables, and a real SQLite upgrade→downgrade→upgrade round-trip proving auth tables survive the downgrade.Preview live verification (2026-08-05)
Verified against the live Preview deployment of
2820922(serviceworkproof-backend-preview, connected branchmain, Neon Preview DBshy-thunder-48215343). Production was not touched.0002_auth_tables → 0003_work_data; revision confirmed on the DB. Applied once (not re-run).Coverage exercised end-to-end over HTTP:
/health,/api/v1/health,/api/v1/health/db(database: connected) all 200.users/me/ refresh rotation + refresh-reuse detection (old token → 401); unauthenticated request → 401.client_id→ 409.date_from/date_to,workplace_id,limit/offset(disjoint pages), deterministic ordering, inverted range → 422.verified: true; 201 m & 250 m →verified: false; missing workplace or record coords →proximity: null. Display distances match half-up rounding (150/199/201/250 m).Temp accounts and all verification work-data were cleaned up through the API (both accounts deleted; work-data soft-deleted) — 0 active temp rows remain in the Preview DB. This PR stays Draft; not merged, not wired to mobile.
Scope / not included
Notes
92f8891(WIP scaffolding) is preserved; this PR adds2820922on top.🤖 Generated with Claude Code