Add Receipt model, photo upload, and update CouponUsage schema #8

Closed
opened 2026-03-16 18:38:10 +00:00 by forgejo_admin · 0 comments
Contributor

Lineage

plan-mcd-tracker → Phase 5 → Phase 5a: Receipt Model + Photo Upload

Repo

forgejo_admin/mcd-tracker-api (primary)
forgejo_admin/pal-e-deployments (PVC for photo storage)

User Story

  • scan-receipt: I want to snap a photo of my receipt and have the app store it
  • save-bogo: I want my BOGO code linked to the receipt it came from

Architecture

  • arch-domain-mcd-tracker — adds Receipt entity, updates CouponUsage (receipt_id FK, code→bogo_code, used_at→earned_at)
  • arch-dataflow-mcd-tracker#flow-1-scan-receipt — POST /receipts endpoint

Context

Phase 5 shipped 8 API endpoints with the original schema (Location + CouponUsage). The UX pivot to receipt-first workflow requires a Receipt entity to track photos, survey codes, and survey completion. CouponUsage needs updating to link back to receipts and use clearer field names (bogo_code not code, earned_at not used_at).

The app already has photo upload infrastructure proven in basketball-api (PVC mount pattern).

File Targets

Files to create:

  • src/mcd_tracker_api/models/receipt.py (or add to existing models.py) — Receipt SQLAlchemy model
  • src/mcd_tracker_api/routes/receipts.py — POST /receipts (multipart), GET /receipts, GET /receipts/{id}/photo
  • alembic/versions/002_add_receipt_and_update_coupon_usage.py — migration: create receipt table, add receipt_id to coupon_usage, rename code→bogo_code, rename used_at→earned_at
  • tests/test_receipts.py — receipt upload, photo serving, receipt-code linking

Files to modify:

  • src/mcd_tracker_api/models.py — add Receipt model, update CouponUsage (receipt_id, bogo_code, earned_at)
  • src/mcd_tracker_api/schemas.py — add ReceiptCreate/ReceiptResponse, update CodeCreate/CodeResponse for bogo_code + receipt_id
  • src/mcd_tracker_api/routes/locations.py — update POST /codes to accept optional receipt_id, update field names
  • src/mcd_tracker_api/routes/dashboard.py — update field references
  • src/mcd_tracker_api/routes/admin.py — update field references
  • src/mcd_tracker_api/main.py — register receipts router
  • pyproject.toml — add python-multipart dependency
  • tests/test_locations.py — update field names in existing tests (code→bogo_code, used_at→earned_at)

Files NOT to touch:

  • src/mcd_tracker_api/auth.py — no changes
  • .woodpecker.yaml — no changes

Acceptance Criteria

  • Receipt model: id, keycloak_sub (indexed), location_id (FK), photo_path, survey_code, survey_completed, captured_at
  • CouponUsage updated: receipt_id (FK, nullable), code→bogo_code, used_at→earned_at
  • POST /receipts accepts multipart form (photo file + location_id). Saves photo to /data/uploads/receipts/{uuid}.jpg. Returns receipt_id.
  • GET /receipts returns user's receipts (auth required)
  • GET /receipts/{id}/photo serves the photo file (auth required, user can only see own)
  • POST /codes accepts optional receipt_id to link BOGO code to receipt
  • When receipt_id provided on POST /codes, receipt.survey_completed set to true
  • Alembic migration is idempotent and handles existing data (nullable receipt_id, column renames)
  • All existing tests updated and passing with new field names
  • New receipt tests passing
  • Photo uploads stored at /data/uploads/receipts/ (will use PVC in production)

Test Expectations

  • test_receipts.py: upload photo, list receipts, serve photo, user isolation
  • test_locations.py: updated for bogo_code/earned_at field names
  • test_dashboard.py: updated field names
  • Run: pytest tests/ -v

Constraints

  • Use python-multipart for file upload handling (FastAPI requirement for multipart)
  • Photo path format: /data/uploads/receipts/{uuid}.jpg (uuid generated server-side)
  • Column renames in Alembic: use op.alter_column with new_column_name parameter
  • receipt_id on CouponUsage MUST be nullable (existing rows have no receipt)
  • Do NOT implement OCR yet — survey_code is entered manually or set to empty string. OCR is Phase 5b.
  • PVC creation in pal-e-deployments is a separate PR (or include in same PR if touching that repo)

Checklist

  • PR opened with Closes #7
  • Migration tested against existing data
  • All tests pass (existing + new)
  • Ruff clean
  • No unrelated changes
  • phase-mcd-tracker-5a-receipt-model — phase note
  • arch-domain-mcd-tracker — updated entity model
  • plan-mcd-tracker — parent plan
### Lineage `plan-mcd-tracker` → Phase 5 → Phase 5a: Receipt Model + Photo Upload ### Repo `forgejo_admin/mcd-tracker-api` (primary) `forgejo_admin/pal-e-deployments` (PVC for photo storage) ### User Story - `scan-receipt`: I want to snap a photo of my receipt and have the app store it - `save-bogo`: I want my BOGO code linked to the receipt it came from ### Architecture - `arch-domain-mcd-tracker` — adds Receipt entity, updates CouponUsage (receipt_id FK, code→bogo_code, used_at→earned_at) - `arch-dataflow-mcd-tracker#flow-1-scan-receipt` — POST /receipts endpoint ### Context Phase 5 shipped 8 API endpoints with the original schema (Location + CouponUsage). The UX pivot to receipt-first workflow requires a Receipt entity to track photos, survey codes, and survey completion. CouponUsage needs updating to link back to receipts and use clearer field names (bogo_code not code, earned_at not used_at). The app already has photo upload infrastructure proven in basketball-api (PVC mount pattern). ### File Targets Files to create: - `src/mcd_tracker_api/models/receipt.py` (or add to existing models.py) — Receipt SQLAlchemy model - `src/mcd_tracker_api/routes/receipts.py` — POST /receipts (multipart), GET /receipts, GET /receipts/{id}/photo - `alembic/versions/002_add_receipt_and_update_coupon_usage.py` — migration: create receipt table, add receipt_id to coupon_usage, rename code→bogo_code, rename used_at→earned_at - `tests/test_receipts.py` — receipt upload, photo serving, receipt-code linking Files to modify: - `src/mcd_tracker_api/models.py` — add Receipt model, update CouponUsage (receipt_id, bogo_code, earned_at) - `src/mcd_tracker_api/schemas.py` — add ReceiptCreate/ReceiptResponse, update CodeCreate/CodeResponse for bogo_code + receipt_id - `src/mcd_tracker_api/routes/locations.py` — update POST /codes to accept optional receipt_id, update field names - `src/mcd_tracker_api/routes/dashboard.py` — update field references - `src/mcd_tracker_api/routes/admin.py` — update field references - `src/mcd_tracker_api/main.py` — register receipts router - `pyproject.toml` — add python-multipart dependency - `tests/test_locations.py` — update field names in existing tests (code→bogo_code, used_at→earned_at) Files NOT to touch: - `src/mcd_tracker_api/auth.py` — no changes - `.woodpecker.yaml` — no changes ### Acceptance Criteria - [ ] Receipt model: id, keycloak_sub (indexed), location_id (FK), photo_path, survey_code, survey_completed, captured_at - [ ] CouponUsage updated: receipt_id (FK, nullable), code→bogo_code, used_at→earned_at - [ ] POST /receipts accepts multipart form (photo file + location_id). Saves photo to /data/uploads/receipts/{uuid}.jpg. Returns receipt_id. - [ ] GET /receipts returns user's receipts (auth required) - [ ] GET /receipts/{id}/photo serves the photo file (auth required, user can only see own) - [ ] POST /codes accepts optional receipt_id to link BOGO code to receipt - [ ] When receipt_id provided on POST /codes, receipt.survey_completed set to true - [ ] Alembic migration is idempotent and handles existing data (nullable receipt_id, column renames) - [ ] All existing tests updated and passing with new field names - [ ] New receipt tests passing - [ ] Photo uploads stored at /data/uploads/receipts/ (will use PVC in production) ### Test Expectations - [ ] test_receipts.py: upload photo, list receipts, serve photo, user isolation - [ ] test_locations.py: updated for bogo_code/earned_at field names - [ ] test_dashboard.py: updated field names - Run: `pytest tests/ -v` ### Constraints - Use `python-multipart` for file upload handling (FastAPI requirement for multipart) - Photo path format: `/data/uploads/receipts/{uuid}.jpg` (uuid generated server-side) - Column renames in Alembic: use `op.alter_column` with `new_column_name` parameter - receipt_id on CouponUsage MUST be nullable (existing rows have no receipt) - Do NOT implement OCR yet — survey_code is entered manually or set to empty string. OCR is Phase 5b. - PVC creation in pal-e-deployments is a separate PR (or include in same PR if touching that repo) ### Checklist - [ ] PR opened with `Closes #7` - [ ] Migration tested against existing data - [ ] All tests pass (existing + new) - [ ] Ruff clean - [ ] No unrelated changes ### Related - `phase-mcd-tracker-5a-receipt-model` — phase note - `arch-domain-mcd-tracker` — updated entity model - `plan-mcd-tracker` — parent plan
Commenting is not possible because the repository is archived.
No labels
No milestone
No project
No assignees
1 participant
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
ldraney/mcd-tracker-api#8
No description provided.