import { AttendanceOutcomeValue } from "../domain/enums/attendance-outcome.enum.mjs"; import { CheckInMethodValue } from "../domain/enums/check-in-method.enum.mjs"; import { SessionStatusValue } from "../domain/enums/session-status.enum.mjs"; import { GeoPoint } from "../domain/value-objects/geo-point.vo.mjs"; import { DeviceInfo } from "../domain/value-objects/device-info.vo.mjs"; import { Document, Schema, Types } from "mongoose"; //#region src/models/attendance-session.model.d.ts /** * One person who entered alongside the subject. Subject-polymorphic like the * session itself — an identified guest carries `ref` + `model`, a walk-in * carries only `name`, an anonymous head-count carries neither. */ interface SessionCompanion { /** Opaque host id, when the companion is a known entity. */ ref?: string; /** Discriminator for `ref` — 'Customer', 'GymMember', 'Guardian', … */ model?: string; /** Display name for an unregistered guest. */ name?: string; /** How they relate to the subject — 'guest', 'parent', 'carer', 'crew'. */ relation?: string; } interface AttendanceSessionDocument extends Document { _id: Types.ObjectId; /** Human-readable public id via customIdPlugin: ATT-2026-0001 */ sessionNumber: string; organizationId: Types.ObjectId | string; /** Opaque string id the host stores. Never an ObjectId field. */ subjectRef: string; /** Discriminator the host owns: 'Employee', 'Patient', 'Student', ... */ subjectModel: string; /** Host-defined "where" — branch id, classroom, gym id, doctor office. */ scope?: string; /** * Host-defined modality discriminator — 'corporate' | 'class' | 'gym' | * 'appointment' | 'visitor' | …. The FSM is identical across kinds; this * field lets policy hooks (outcomePolicy, allowed methods, validation * rules) diverge without spinning up separate engines. Defaults to * `'attendance'` when the caller omits it. */ sessionKind: string; scheduledStart?: Date; scheduledEnd?: Date; actualStart: Date; actualEnd?: Date; /** * The attendance day this session belongs to — a `CivilDate` string * (`'2026-07-15'`) derived from `actualStart` in the engine's `timezone` * (`civilDateOf` from `@classytic/primitives/timezone`). THE roster key: * a night-shift check-in at 23:30 local belongs to THAT local day, not * whatever UTC (or the deploy server's clock) says. Lexicographic order * is chronological, so "muster roll for scope X on day D" is a plain * string match and date ranges are string ranges. Present on every * session written by muster >= 0.3.0. */ businessDate?: string; /** * People who entered WITH the subject on this visit — a member's gym * guests, a pupil's collecting parent, a patient's visitor, a contractor's * crew. Drives real occupancy: a member with two guests puts THREE bodies * in the room, which is what `capacity` must count. * * Always present (default 0) so occupancy aggregation never needs a * `$ifNull`. Whether a subject MAY bring companions, and how many, is an * `AdmissionPolicy` decision (`@classytic/muster/admission`); the standing * right to bring guests at all — and any metered allowance like "3 guest * visits per month" — belongs to `@classytic/access`. */ companionCount: number; /** * Optional identities of those companions (liability, contact tracing, * "who signed in with whom"). WRITE-ONCE at check-in and bounded by the * arriving party — never appended to over the session's life, so this is * not the unbounded-array pattern break intervals correctly avoid. */ companions?: SessionCompanion[]; status: SessionStatusValue; outcome?: AttendanceOutcomeValue; checkInMethod: CheckInMethodValue; checkOutMethod?: CheckInMethodValue; checkInGeo?: GeoPoint; checkOutGeo?: GeoPoint; checkInDevice?: DeviceInfo; checkOutDevice?: DeviceInfo; /** Total elapsed ms between actualStart and actualEnd (set on close). */ durationMs?: number; /** Total break ms accumulated during the session (set on close). */ breakMs?: number; /** Expected duration from the schedule window — diagnostic for late/early. */ plannedDurationMs?: number; /** True while a break is in progress (between break_start and break_end). */ onBreak: boolean; /** Count of approved corrections applied to the session. */ correctionsAppliedCount: number; /** Note attached on cancel / close. */ note?: string; metadata?: Record; deletedAt?: Date | null; createdAt: Date; updatedAt: Date; version: number; } //#endregion export { AttendanceSessionDocument, SessionCompanion };