Skip to main content

Gig::AssignmentAdjustment

Purpose

An AssignmentAdjustment is an immutable audit record created each time an employer or admin changes the billable times on a Gig::Assignment. It captures what changed, the old and new values, who made the change, and why.

AssignmentAdjustment is a domain concept, not just an infrastructure audit log. It exists because the business process has specific rules: employers can adjust billable times during the settlement window (from clock-out until 9am the next day), and each adjustment must be traceable for dispute resolution.

Common scenarios that produce adjustment records:

  • Employer corrects break time from 30 to 60 minutes after clock-out
  • Employer adjusts clock-in from 9:02am to 9:00am because the worker started before the QR was generated
  • Ops enters an adjustment on behalf of the employer based on a WhatsApp message

When a talent asks "Why am I paid less?" or an employer says "I sent you a WhatsApp to update the timings", the adjustment history provides the complete answer.

Model Context

ContextDetails
EntityGig::AssignmentAdjustment (child of Assignment, append-only)
LayerOperations — Audit
Upstream dependenciesGig::Assignment (the assignment whose billable times were changed)
Downstream dependentsNone — this is a terminal record. Read by the Assignment detail view for audit display.

State Machine

AssignmentAdjustment has no state machine. Records are immutable — once created, they are never updated or deleted. They are append-only event records.

Use Cases

IDUse CaseTriggerActor
UC-1Record an employer adjustmentEmployer changes billable times during settlementSystem (on behalf of Employer)
UC-2Record an admin adjustmentAdmin changes billable times on behalf of employerSystem (on behalf of Admin)
UC-3View adjustment history for an assignmentEmployer or admin reviewing changesEmployer or Admin

UC-1: Record an employer adjustment

FieldDetails
ActorSystem (triggered by employer action on Assignment UC-8)
TriggerEmployer modifies billable times during settlement window

Preconditions:

  • Gig::Assignment exists and status: clocked_out
  • billable_locked_at is NULL (settlement window open)

System Behavior:

  1. Employer changes one or more billable_* fields on the Assignment (see Assignment UC-8)
  2. System creates an AssignmentAdjustment record with:
    • gig_assignment_id: the assignment
    • changes: JSON capturing each changed field with old and new values
    • reason: employer-provided reason (mandatory)
    • adjusted_by_type: Org::UserProfile
    • adjusted_by_id: the employer's profile ID
    • created_at: current timestamp

Business Rules:

  • The AssignmentAdjustment is created in the same transaction as the Assignment update — they are atomic
  • The changes JSON uses a consistent format:
    {
    "field_name": {
    "was": "old_value",
    "now": "new_value"
    }
    }
  • Example — employer adjusts break time and clock-in:
    {
    "billable_break_minutes": {
    "was": 30,
    "now": 60
    },
    "billable_clock_in": {
    "was": "2026-04-08T09:02:00",
    "now": "2026-04-08T09:00:00"
    }
    }
  • If multiple fields change in one save, they are captured in a single adjustment record
  • Reason is mandatory

Postconditions:

  • Immutable AssignmentAdjustment record exists
  • Cannot be edited or deleted

UC-2: Record an admin adjustment

FieldDetails
ActorSystem (triggered by admin action on Assignment UC-9)
TriggerAdmin modifies billable times on behalf of employer

Preconditions:

  • Gig::Assignment exists
  • For non-admin: billable_locked_at is NULL. For admin: can override even after lock.

System Behavior:

  1. Admin changes billable_* fields (see Assignment UC-9)
  2. System creates an AssignmentAdjustment record with:
    • adjusted_by_type: Identities::Admin
    • adjusted_by_id: the admin's ID
    • reason: e.g., "Per employer WhatsApp request"
  3. All other fields same as UC-1

Business Rules:

  • Admin adjustments follow the same audit rules as employer adjustments
  • Admin CAN adjust after billable_locked_at is set — this is the only way to change verified times
  • When admin adjusts a verified Assignment, the Gig::Payment may need to be recalculated (open question — see Payment spec)

Postconditions:

  • Same as UC-1

UC-3: View adjustment history for an assignment

FieldDetails
ActorOrg::UserProfile or Identities::Admin
TriggerReviewing changes to billable times

Preconditions:

  • Assignment exists

System Behavior:

  1. System retrieves all AssignmentAdjustment records for the Assignment, ordered by created_at ascending
  2. Displays a chronological timeline:
    • Timestamp
    • Who made the change (employer name or admin name)
    • What changed (rendered from changes JSON)
    • Why (reason text)

Business Rules:

  • All adjustments are visible to both employers and admins
  • The timeline starts with the initial billable values (set at clock-out) and shows each subsequent change

Postconditions:

  • Read-only operation

Invariants

  1. AssignmentAdjustment records are immutable — once created, they cannot be updated or deleted
  2. Every change to billable_* fields on an Assignment MUST create an AssignmentAdjustment record — no silent changes
  3. changes JSON must contain at least one field with was and now values
  4. reason is mandatory — every adjustment must have an explanation
  5. adjusted_by_type and adjusted_by_id are mandatory — every adjustment must be attributable to a person
  6. Created in the same database transaction as the Assignment update

Model Interactions

Related ModelRelationshipInteraction
Gig::AssignmentAssignmentAdjustment belongs_to AssignmentEach adjustment references the assignment whose billable times were changed.
Org::UserProfilePolymorphic adjusted_byTracks which employer made the adjustment.
Identities::AdminPolymorphic adjusted_byTracks which admin made the adjustment (when ops acts on behalf of employer).

Schema Gaps

The gig_assignment_adjustments table is new — it does not exist in the current DBML. The schema is defined in jodapp-api/docs/db/gig.dbml.