# Walk-Through Check-Ins Backend ## Purpose `walkthrough_checkins` stores structured classroom-observation ("walk-through") records for director-level staff. Each record captures the observed teacher, classroom, observing director, date/time, seven rated categories with optional per-category comments, and overall notes. The backend owns tenant scope, campus scope, creator ownership, validation, and role-gated access. ## Slice Files (by layer) - Route: `src/routes/walkthrough_checkins.ts` (thin wiring; `GET /`, `POST /`, `DELETE /:id`). - Controller: `src/api/controllers/walkthrough_checkins.controller.ts` (custom — not the CRUD factory; uses `paramStr` for the id param). - Service (BLL): `src/services/walkthrough_checkins.ts` (+ `walkthrough_checkins.types.ts` for `WalkthroughInput` / `WalkthroughFilter`). - Repository (DAL): queries run through `db.walkthrough_checkins` inside the service (no separate `db/api/walkthrough_checkins.ts`). - Model: `src/db/models/walkthrough_checkins.ts`. - Shared used: `services/shared/validate.ts` (`nullableString`); `db/with-transaction.ts` (`withTransaction`); `shared/constants/pagination.ts` (`resolvePagination`); `shared/constants/walkthrough.ts` (`WALKTHROUGH_MANAGER_ROLE_NAMES`, `WALKTHROUGH_TENANT_WIDE_ROLE_NAMES`); `services/shared/access.ts` (`getOrganizationIdOrGlobal`, `hasGlobalAccess`, `hasRoleAccess`, `requireUserId`); `shared/errors/*` (`ForbiddenError`, `ValidationError`). Note: the service defines a module-local `getCampusId` and `campusScope` (staff-profile campus only), not the shared access helpers. ## API All routes require JWT authentication. Base path mounted at `/api/walkthrough_checkins`. - `GET /api/walkthrough_checkins` -> `200` `{ rows, count }`. Optional query `teacher_name`, plus `limit` / `page` (paginated via `resolvePagination`). Returns check-ins visible to the current manager, ordered by `check_in_date` desc then `createdAt` desc. - `POST /api/walkthrough_checkins` -> `201`. Request body wrapped as `{ data: }`. Returns the created check-in DTO. - `DELETE /api/walkthrough_checkins/:id` -> `200` `{ deletedCount }`. Deletes one check-in visible to the current manager. ## Access Rules - All operations require a role in `WALKTHROUGH_MANAGER_ROLE_NAMES` (`Super Administrator`, `Administrator`, `Platform Owner`, `Tenant Director`, `Campus Manager`) or `globalAccess`, enforced by `assertCanManage` (which also requires an authenticated user); otherwise `ForbiddenError`. Users with `globalAccess` are always allowed. - Campus visibility: roles in `WALKTHROUGH_TENANT_WIDE_ROLE_NAMES` (`Super Administrator`, `Administrator`, `Platform Owner`, `Tenant Director`) or `globalAccess` see all org records; other managers (e.g. `Campus Manager`) are restricted to their own staff campus on `list` and `delete` via `campusScope`. ## Tenant Scope - Organization is resolved via `getOrganizationIdOrGlobal`: global access users bypass the org filter and see check-ins across all organizations; regular users are bound to their org. - `campusId` is resolved from the module-local `getCampusId` — the current staff profile's campus only (`currentUser.staff_user[0].campusId`), else `null`; it never falls back to the user's own `campusId`. - On create, `createdById` is required from the current user (`requireUserId`); `updatedById` from the current user. ## Data Contract - Required mutation fields: `teacher_name`, `classroom`, `director_name`, `check_in_date`, `check_in_time` (non-empty strings); and the seven rating fields `attitude_rating`, `classroom_management_rating`, `cleanliness_rating`, `vibes_rating`, `team_dynamics_rating`, `emergency_exit_rating`, `lesson_plan_rating` (finite numbers). Invalid input raises `ValidationError`. - Optional mutation fields: the matching per-category comments (`attitude_comment`, `classroom_management_comment`, `cleanliness_comment`, `vibes_comment`, `team_dynamics_comment`, `emergency_exit_comment`, `lesson_plan_comment`) and `overall_notes`. Comments are normalized via `nullableString` (trimmed, blank -> `null`). - DTO fields: `id`, the five required strings, the seven `*_rating` and matching `*_comment` fields, `overall_notes`, `organizationId`, `campusId`, `createdById`, `updatedById`, `createdAt`, `updatedAt`. - Model columns: the five required strings are TEXT not null except `check_in_date` (DATEONLY) and `check_in_time` (TIME); the seven `*_rating` are INTEGER not null; the seven `*_comment` and `overall_notes` are TEXT nullable; `importHash` (unique). Tenant/audit columns: `organizationId` (not null), `createdById` (not null), `campusId` (nullable), `updatedById` (nullable). The model is `paranoid` (soft delete via `deletedAt`) and uses `freezeTableName`. - Associations: `belongsTo` organizations (`organization`), campuses (`campus`), users (`createdBy`, `updatedBy`). Note: there is no `user` association on this model. ## Behavior / Notes - `create` runs inside `withTransaction`; required string fields are trimmed before persistence. - `list` is paginated with shared defaults (`resolvePagination`). - `remove` is a hard `destroy` scoped by `id` + organization + `campusScope`, returning the number of rows deleted (no transaction wrapper). ## Tests None yet (no `walkthrough_checkins` unit/e2e test in `src/`). ## Related - Frontend: `frontend/docs/walkthrough-integration.md`. - Related slices: `frame-entries.md` (similar editor-role-gated, campus-resolved director workflow), `safety-quiz-results.md`, `personality-quiz-results.md`.