# Course attendance

This document defines the current integrity and lifecycle rules for accelerated-course attendance stored in `course_day` and `user_course_day`.

## Attendance records

`course` is the schedule template. Each scheduled realization is represented by a separate `course_day`, and each participant's attendance for that day is represented by one `user_course_day` row.

An attendance row may be created or updated only when all of these conditions are met:

- the request uses `POST` and carries a valid CSRF token scoped to the selected course day;
- the current account is an instructor or administrator authorized to manage that course day;
- the course day has reached its attendance window, which opens five minutes before its scheduled start;
- the course day is not cancelled;
- the enrollment belongs to the same course as the course day;
- the enrollment is active, unless it already has attendance history that remains editable.

The time and cancellation rules are enforced in `CourseDay::setUserAttendance()`, not only in the controller or view. This keeps direct model callers from creating future or cancelled attendance. Finalized past lists remain editable so an instructor can correct an individual attendance entry.

The attendance-page checkbox is disabled before the attendance window opens and for cancelled course days. Each asynchronous update sends the course-day CSRF token. The token is not rotated after each checkbox update because one rendered attendance list supports multiple independent updates.

## Payment integrity

A present `user_course_day` row is attendance history for the enrollment. Once any presence has been recorded, payment recall and enrollment deactivation remain locked by the existing payment rules. Preventing future or forged attendance therefore protects both the attendance record and the linked payment state.

## Course management authorization

Course details contain participant identity and contact data. An instructor may
open the details page, cancel the course, remove an unpaid participant, or
accept a course payment only when `course.id_instructor` matches the current
instructor. Administrators retain access to every course. The shared
`Course::canBeManagedBy()` rule is used by all of these controller paths.

Course cancellation, participant removal, and course-payment submission are
`POST` operations protected by action-specific CSRF scopes. Cancellation also
runs in one database transaction: every active unpaid enrollment and the
course itself are deactivated together, or all changes are rolled back.

For a course payment, the submitted enrollment ID is resolved again on the
server. The participant ID and course ID are taken from that enrollment rather
than trusted from hidden form fields. An already paid enrollment, inactive
course, and course belonging to another instructor are rejected before a
payment is created. Creating the payment and activating the enrollment use one
transaction. Course cancellation and course payment lock the same course and
enrollment rows in the same order, so they cannot cross and leave a paid
enrollment on a cancelled course.

## Verification checklist

1. Open a future course day more than five minutes before its start and confirm that attendance switches are disabled.
2. Send `GET` to the attendance update route and confirm that it returns HTTP 405 without changing data.
3. Send `POST` without the course-day CSRF token, or with an invalid token, and confirm that it returns HTTP 403 without changing data.
4. Send an otherwise valid `POST` for a future or cancelled course day and confirm that it returns HTTP 400 without changing data.
5. On an authorized course day inside the attendance window, toggle several participants and confirm that every update succeeds with the same page token.
6. Confirm that a finalized past list still permits correcting an individual attendance entry.
7. Confirm that an instructor cannot update a course day assigned to another instructor, while an administrator retains the established access.
8. Try to open another instructor's course details and course-payment form and confirm that participant contact data is not shown.
9. Submit cancellation and course payment by `GET`, without a valid action token, or with a changed enrollment ID; confirm that no course, enrollment, or payment record changes.
10. Force one enrollment update to fail while cancelling a course and confirm that the course and all enrollments retain their previous state.
