Skip to main content

Group session attendance

Outcome

Session supervisors take attendance and review supervision compliance before charges are committed, with the same logic as the post-claim scrubber so issues are caught in the room.

Prerequisites

ScopeAction
clinical.group-session.readRead list/detail/preview
clinical.attendance.writeMutate attendance

PLATFORM_ADMIN continues to bypass scope checks in code.

List → Detail flow

  1. Filter /group-sessions by date range (?from=YYYY-MM-DD&to=…), facility ID, or group type.

  2. Click a row to land on /group-sessions/:id.

  3. The header shows date/time/facility. The Supervisors card lists every distinct provider whose staff_assignment.role = 'SUPERVISING' is joined to a service event whose participant_id belongs to this session.

  4. The Roster table presents per-participant attendance buttons limited to legal next states for the current status:

    FromAllowed next
    SCHEDULEDATTENDED, ABSENT, LATE
    ATTENDEDABSENT, LATE
    LATEATTENDED, ABSENT
    ABSENTATTENDED, LATE

    Illegal transitions return HTTP 409 — no client-side regression ever lands.

Billing preview semantics

GET /api/v1/group-sessions/:id/billing-preview re-runs the same helpers GroupBillingStage consumes (countBillableParticipants, resolveGroupSizeBand) so the warnings the UI shows are the warnings the scrubber will fire later.

The preview resolves the primary active member.coverage_policy for any participant in the session and uses that payer + the session's facility state for supervision-rule + size-band lookups. When no coverage row matches (e.g. unenrolled roster), the preview reports billableCount and any GROUP_UNATTENDED_BILLED issue but skips payer-scoped checks.

Supervision-ratio breach

When the supervisor banner reads:

Session …: BCBA:RBT ratio 8:1 exceeds max 6

the same condition will fire GROUP_SUPERVISION_RATIO at scrub time. Resolve by either:

Post-billing lock

Once billing.claim_line.group_session_id references the session (typically once charges have been built and grouped), attendance mutations return HTTP 409:

Session is referenced by claim lines; pass force=true to override

Pass {"force": true} in the PATCH body only when:

  1. A scrubber error needs to be cleared by correcting an in-flight attendance record, and
  2. You will manually re-run claim-builder afterwards to refresh the affected claim lines.

The detail page surfaces a footer caveat ("Attendance changes after billing apply with a force flag — use carefully") whenever hasLinkedClaims=true.

Validation

CheckExpected
Roster transitionsMatch allowed-next table
Billing preview warningsMatch what the scrubber would later fire
Forced post-billing editFollowed by a manual claim-builder rebuild

Troubleshooting

SymptomCauseFix
409 on attendance changeIllegal transition, OR session referenced by claim linesEither pick a legal next state, or pass force=true if you're rebuilding lines after.
Supervision banner won't clear after assigning a new supervisorNew supervisor's staff_assignment.role not SUPERVISINGUpdate the assignment role; the Supervisors card pulls from this column.
Billing preview shows 0 billableCountRoster all ABSENT / LATE / unenrolledExpected behavior.

Latent schema note

Migration 108 dropped a legacy chk_participant_status constraint left behind by 068. Pre-fix, every insert against a real DB into service.service_participant would have failed silently through tests that mocked the table. Post-108, both API and direct SQL inserts of SCHEDULED attendance succeed. No data fix is needed — the column already holds canonical values from migration 068.

Cross-references

Next

6.3 — Institutional context UI