Gig::QrCode
Purpose
A QrCode is a time-limited, scannable code that the employer generates for talent to clock in or clock out of a shift. Each QR encodes a unique UUID that the talent's mobile app scans to record attendance on their Gig::Assignment.
QR codes are the primary attendance mechanism in the Gig domain. They solve two problems:
- Verification: the talent must be physically present at the workplace to scan the QR (the employer has the screen)
- Simplicity: no GPS, no biometrics — the employer shows a code, the worker scans it. Simple enough for non-tech-savvy blue-collar workers and busy hiring managers.
QR codes are generated per shift (not per assignment). All workers on the same shift scan the same QR. This avoids the employer needing to generate individual codes for each worker.
Model Context
| Context | Details |
|---|---|
| Entity | Gig::QrCode (child of Shift) |
| Layer | Operations — Attendance |
| Upstream dependencies | Gig::Shift (which shift this QR is for) |
| Downstream dependents | Gig::Assignment (scanning updates actual_clock_in / actual_clock_out on the Assignment) |
State Machine
QrCode has no status column. A QR is either valid (current time is before expires_at) or expired (current time is at or after expires_at). This is a derived state, not stored.
Use Cases
| ID | Use Case | Trigger | Actor |
|---|---|---|---|
| UC-1 | Generate a clock-in QR code | Shift is active, employer ready for workers to arrive | Employer |
| UC-2 | Generate a clock-out QR code | Workers finishing, employer ready for clock-out | Employer |
| UC-3 | Talent scans a clock-in QR code | Talent arrives at the shift and scans the QR | Talent |
| UC-4 | Talent scans a clock-out QR code | Talent finishes work and scans the QR | Talent |
| UC-5 | QR code expires and employer regenerates | QR expired before all workers scanned | Employer |
UC-1: Generate a clock-in QR code
| Field | Details |
|---|---|
| Actor | Org::UserProfile (employer) |
| Trigger | Shift is active, employer ready for workers to arrive |
Preconditions:
Gig::Shiftstatus: active- At least one Assignment in
confirmedstatus (workers expected)
System Behavior:
- Employer requests a clock-in QR for the shift
- System checks: does a valid (non-expired) clock-in QR already exist for this shift?
- If yes: return the existing QR (reuse)
- If no: generate a new QR
- To generate: system creates a
Gig::QrCodewith:gig_shift_id: the shiftqr_type:clock_inqr_uuid: a unique UUID (e.g.,SecureRandom.uuid)expires_at:now + configured_duration(e.g., 15 minutes)generated_by_id: the employer
- System renders the UUID as a QR image and displays it to the employer
Business Rules:
- One valid clock-in QR per shift at a time — if a valid one exists, reuse it instead of creating a new one
- Expiry duration is configurable (system-wide or per-company, default: 15 minutes)
- The QR image is generated client-side from the UUID — the backend only stores and validates the UUID
- Multiple expired QRs can exist for the same shift (each regeneration creates a new record)
Postconditions:
Gig::QrCodeexists withqr_type: clock_in- Employer has the QR displayed for workers to scan
UC-2: Generate a clock-out QR code
| Field | Details |
|---|---|
| Actor | Org::UserProfile (employer) |
| Trigger | Workers finishing, employer ready for clock-out |
Preconditions:
Gig::Shiftstatus: active- At least one Assignment in
clocked_instatus
System Behavior:
- Employer requests a clock-out QR for the shift
- Same reuse logic as UC-1 (check for existing valid clock-out QR)
- If generating new: same as UC-1 but with
qr_type: clock_out - Alongside QR generation, employer is presented with the time confirmation form for each
clocked_inAssignment:billable_clock_in,billable_clock_out,billable_break_minutes- Pre-filled from actual clock-in times and shift end time
Business Rules:
- Same reuse and expiry rules as UC-1
- The time confirmation form is presented at clock-out QR generation time — this is the employer's first opportunity to set billable times
- The form is optional — if skipped, billable times default to actual times with 0 break
Postconditions:
Gig::QrCodeexists withqr_type: clock_out- Time confirmation form presented to employer
UC-3: Talent scans a clock-in QR code
| Field | Details |
|---|---|
| Actor | Talent::Profile (talent) |
| Trigger | Talent arrives at the shift and scans QR |
Preconditions:
- Talent has an Assignment for this shift in
confirmedstatus - QR code is valid (not expired)
System Behavior:
- Talent scans the QR code with their mobile app
- App sends the
qr_uuidto the backend - System looks up the QR by
qr_uuid - System validates:
- QR exists and
qr_type = clock_in - QR has not expired (
expires_at > now) - Talent has a
confirmedAssignment for this QR's shift - Talent has not already clocked in
- QR exists and
- System updates the Assignment (see Assignment UC-2):
actual_clock_in = now()status→clocked_in
Business Rules:
- The QR UUID is the only input from the talent — the system resolves the shift and assignment from it
- If the QR is expired: error "This QR code has expired. Ask your manager to generate a new one."
- If the talent has no Assignment for this shift: error "You are not assigned to this shift."
- If already clocked in: error "You have already clocked in for this shift."
Postconditions:
- Assignment updated (see Assignment UC-2 postconditions)
UC-4: Talent scans a clock-out QR code
| Field | Details |
|---|---|
| Actor | Talent::Profile (talent) |
| Trigger | Talent finishes work and scans QR |
Preconditions:
- Talent has an Assignment for this shift in
clocked_instatus - QR code is valid (not expired)
System Behavior:
- Talent scans the QR code
- App sends the
qr_uuidto the backend - System validates:
- QR exists and
qr_type = clock_out - QR has not expired
- Talent has a
clocked_inAssignment for this QR's shift - Talent has not already clocked out
- QR exists and
- System updates the Assignment (see Assignment UC-3):
actual_clock_out = now()billable_*fields set from the employer's time confirmation form (or defaulted from actuals)status→clocked_out
Business Rules:
- Same validation rules as UC-3 but for clock-out
- If the employer filled in the time confirmation form (during UC-2), those values are applied to this Assignment's
billable_*fields - If not,
billable_clock_indefaults toactual_clock_in,billable_clock_outdefaults toactual_clock_out,billable_break_minutesdefaults to 0
Postconditions:
- Assignment updated (see Assignment UC-3 postconditions)
UC-5: QR code expires and employer regenerates
| Field | Details |
|---|---|
| Actor | Org::UserProfile (employer) |
| Trigger | QR expired before all workers scanned |
Preconditions:
- Previous QR for this shift + type has expired
- Workers still need to clock in or out
System Behavior:
- Employer requests a new QR (same as UC-1 or UC-2)
- System sees no valid QR exists (previous one expired)
- System generates a new QR with a new UUID and new expiry
- The expired QR remains in the database for audit (not deleted)
Business Rules:
- Expired QRs are never deleted — they are audit records showing that a QR was generated at a specific time
- Each regeneration creates a new record with a new UUID
- Workers who scanned the old QR before it expired are already clocked in — they are not affected by regeneration
- Workers who missed the window must scan the new QR
Postconditions:
- New
Gig::QrCodeexists with fresh UUID and expiry - Previous QR remains in database (expired, for audit)
Invariants
qr_uuidis globally unique across all QR codes- At most one valid (non-expired) QR per
(shift, qr_type)at any time - QR codes are never updated or deleted — they are append-only records
- Expiry is the only mechanism for invalidation — there is no manual "revoke" action
- QR codes belong to a Shift, not an Assignment — all workers on the same shift scan the same QR
expires_atmust be in the future at creation time- A QR can only be scanned by talent who have an Assignment for the QR's shift in the correct status (
confirmedfor clock-in,clocked_infor clock-out)
Model Interactions
| Related Model | Relationship | Interaction |
|---|---|---|
Gig::Shift | QrCode belongs_to Shift | QR codes are generated for a specific shift. The shift must be active. |
Gig::Assignment | QR scan updates Assignment | When talent scans, the system finds their Assignment for the QR's shift and updates clock times. |
Org::UserProfile | Employer generates QR codes | The employer initiates QR generation from the shift management screen. |
Talent::Profile | Talent scans QR codes | The talent's app scans the QR and sends the UUID to the backend for validation. |
Schema Gaps
The gig_qr_codes table is new — it does not exist in the current DBML for jodapp. The schema is defined in jodapp-api/docs/db/gig.dbml.
Open Questions:
- Should QR codes support a "scan radius" (GPS check to ensure talent is at the outlet)? This would reduce fraud but adds complexity and may not work well in indoor environments.
- Should the system support manual clock-in (employer enters it without QR) as a fallback for situations where the talent's phone can't scan? The legacy system supports this for the "forgotten QR code" scenario.