Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

08 — Form Specifications (Attendance Module)

Exact field-level specs for the three write surfaces (mark, batch, update) plus the report trigger. Every field maps to a DTO — no client-invented fields. Validation: server class-validator rules as written in the DTOs; client mirrors them for pre-submit feedback (server stays authoritative, 00-shared/06 §5).


1. Mark form (single) — POST /attendance

DTO: MarkAttendanceDto (mark-attendance.dto.ts:11-49).

FieldTypeRequiredServer ruleClient control
studentIdMongoId string@IsMongoId() (mark-attendance.dto.ts:13-14)hidden (from selected row)
classIdMongoId string@IsMongoId() (mark-attendance.dto.ts:16-17)hidden (from class context)
dateYYYY-MM-DD string@IsDateString() (mark-attendance.dto.ts:20-22)AppDatePicker; serialized yyyy-MM-dd in tenant-local date (see 14_QA_Checklist.md §timezone)
statusenum@IsEnum(AttendanceStatus) — one of present,absent,late,half_day,leave,holiday (mark-attendance.dto.ts:24-28; enum attendance.schema.ts:7-14)StatusChip/popover; invalid → client-blocked; server 400 VALIDATION_ERROR w/ details (e2e p1-operations.e2e-spec.ts:222-228)
checkInISO date-time stringoptional@IsOptional() + @IsDateString() (mark-attendance.dto.ts:30-33)AppTimePicker default now; sent as full ISO (new Date(...), attendance.service.ts:32)
checkOutISO date-time stringoptional@IsOptional() + @IsDateString() (mark-attendance.dto.ts:35-38)AppTimePicker
sourcestringoptional@IsOptional() @IsString() (mark-attendance.dto.ts:40-43) — client never sends (server default manual, attendance.schema.ts:46-51); invalid value → schema enum rejection risk (OQ)hidden
remarksstringoptional@IsOptional() @IsString() (mark-attendance.dto.ts:45-48)AppTextField in popover, ≤ 500 chars

Success: 201 → data = full attendance doc (_id, studentId, classId, date, status, source, checkIn/checkOut?, remarks?, markedBy?, version, timestamps — attendance.schema.ts:24-55 + base.schema.ts).

Errors: 400 details per field; 404 if referenced entities invalid (upstream modules); 409 DUPLICATE_RESOURCE only if upsert races a concurrent create on the unique index (attendance.schema.ts:59-62) — client: refresh grid; 429/5xx per shared mapping.

Idempotency: re-submit is safe by design (upsert findOrCreate, attendance.repository.ts:17-37); still send Idempotency-Key on offline flush to dedupe events.

2. Batch form — POST /attendance/bulk

DTO: BulkMarkAttendanceDto { records: MarkAttendanceDto[] } (mark-attendance.dto.ts:51-54).

  • Client build: selection rows → records array, each a full mark payload (studentId/classId/date/status; optional checkIn/checkOut/remarks only if set).
  • Validation (client-side, mirrors server per-record): every record must pass §1 rules; pre-validate all records before submit — the server loops sequentially (attendance.service.ts:48-54) and a single invalid record throws, leaving earlier records saved (partial success — OQ-2). Client blocks submit if any record invalid.
  • Size: no server cap in DTO; practical limit = one class day (≤ ~150 records). Offline flush batches ≤ 100 (see 15).
  • Success: 200/201 → data = array of docs (same order as records). Client reconciles grid per doc by studentId.
  • Failures: map per-record where possible; snackbar "9 of 34 failed" + retry of failed subset (safe — upsert).

3. Update form (correction) — PATCH /attendance/:id

DTO: UpdateAttendanceDto (update-attendance.dto.ts:4-22). Partial replace: only sent fields are $set (base.repository.ts:57-66).

FieldTypeServer ruleNotes
statusstring@IsString() only — no enum validation (update-attendance.dto.ts:8)OQ-6: server may persist invalid value via $set (no runValidators). Client MUST restrict to the 6-status enum; keep server-mirror validation on
checkInDate@IsOptional() (raw Date, no validator) (update-attendance.dto.ts:12)ISO string → Date
checkOutDate@IsOptional() (update-attendance.dto.ts:16)as above
remarksstring@IsOptional() @IsString() (update-attendance.dto.ts:21)clearing = send ""
  • Semantics: doc must exist → 404 "Attendance record not found." if not (attendance.service.ts:75-79,85-87). Success → data = updated doc (version +1, base.repository.ts:63).
  • Event: AttendanceUpdated {attendanceId, studentId, changes:[keys]}changes = Object.keys(dto) (attendance.service.ts:94-98).
  • UI: from S7 day detail / S4 read grid (teacher, attendance.mark+attendance.edit). Prefill current values from the fetched doc. Undo via re-PATCH (not supported server-side as an action — client re-sends previous values).

4. Report trigger — POST /reports/generate

DTO: GenerateReportDto (reports module; type from ReportType, report-job.schema.ts:8-9). For attendance: {type: 'attendance_summary', classId?, startDate?, endDate?}{jobId, status:'queued'} (reports.service.ts:28-44). Poll GET /reports/:jobId.

5. Biometric ingest (machine→API, not a user form)

POST /biometric/ingest {studentId (MongoId), deviceId (string), timestamp (date-string), mode? (string)} (create-biometric-log.dto.ts:4-21). Displayed read-only in S8; no client form (device pushes). deviceId is a free string today — no device-registry validation (device biometric-device.schema.ts:15-31 exists; association OQ-3).

6. Validation error UX mapping (server → fields)

Server 400 details array {field, message} (00-shared/07 §3). Client maps:

Server fieldClient target
studentId/classIdhidden fields — treat as 422-class failure (snackbar, refresh roster)
datedate picker error text "Enter a valid date"
statuschip popover error; re-open with current value
checkIn/checkOuttime field error "Enter a valid time"
remarksremarks field error

Envelope-level errors (409/422/429/5xx) render per 00-shared/06 §5 mapping, never raw server internals (00-shared/07 §11).