# Phase 4 Execution Plan: Modular Attendance and Scanner Extraction


# Islamic Sunday School Alignment Update

Phase 4 attendance now targets **Islamic Sunday School** as the extension layer.

`SchoolCore\Attendance` owns scanner behavior, attendance records, sessions, status, idempotency, audit, and cross-school isolation.

`IslamicSundaySchool\Attendance` may customize:

```text
Sunday program sessions
Qur'an class sessions
Arabic class sessions
Islamic studies sessions
halaqa/group attendance
Jumu'ah or special-program attendance, if used by the school
volunteer attendance
late arrival windows
masjid calendar rules
```

The core must use neutral terms such as session, group, program, student, staff, volunteer, and attendance status. Islamic-specific vocabulary belongs in extension resolvers and policies.

---
## 1. Purpose

Phase 4 extracts attendance and scanner behavior into a reusable `SchoolCore` module that can support any school system while allowing Islamic Sunday School-specific attendance behavior through policies and extension services.

The attendance module must own the invariant parts of attendance:

- attendance sessions/days
- student attendance
- staff attendance
- check-in/check-out events
- scan logs
- badge lookup
- late/absent/present status calculation
- idempotency
- audit logging
- cross-school isolation
- authorization boundaries

Islamic Sunday School may customize:

- Islamic Sunday School program session rules
- program/session attendance grouping
- program volunteer attendance
- late arrival windows
- class/session mapping
- masjid/family reporting labels
- event-specific attendance rules

Islamic Sunday School must not own the core attendance mutation rules. That way the system can serve schools, academies, training centers, camps, daycare programs, Islamic Sunday Schools, and future human organizational inventions that will somehow still need attendance.

---

## 2. Required Dependency Direction

Attendance must follow the modular platform boundary:

```text
App\Domain\IslamicIslamicSundaySchool\Attendance
        ↓ depends on
App\Domain\SchoolCore\Attendance
        ↓ depends on
App\Domain\SchoolCore\Shared
```

Forbidden dependencies:

```text
SchoolCore\Attendance -> IslamicSundaySchool\*
SchoolCore\Attendance -> Http\Request
SchoolCore\Attendance -> auth()
SchoolCore\Attendance -> controller classes
SchoolCore\Attendance -> Islamic Sunday School vocabulary
SchoolCore\Attendance -> direct unscoped school queries
```

Allowed dependencies:

```text
IslamicSundaySchool\Attendance -> SchoolCore\Attendance contracts
Http\Controllers -> attendance contracts
Jobs -> attendance contracts
Attendance services -> SchoolContext
Attendance services -> shared audit/transaction contracts
Attendance services -> academic calendar provider contract
```

---

## 3. Target Namespace Structure

Create the attendance module under `SchoolCore`:

```text
app/
  Domain/
    SchoolCore/
      Attendance/
        Contracts/
          AttendanceServiceContract.php
          ScannerServiceContract.php
          BadgeLookupServiceContract.php
          AttendancePolicyContract.php
          AttendanceSessionResolverContract.php
          AttendanceStatusPolicyContract.php
          AttendanceAuditLoggerContract.php
          AttendanceIdempotencyServiceContract.php
          StaffAttendanceServiceContract.php
          StudentAttendanceServiceContract.php
        DTO/
          RecordStudentAttendanceData.php
          RecordStaffAttendanceData.php
          ProcessScanData.php
          BadgeLookupData.php
          AttendanceSessionData.php
          AttendanceStatusData.php
          AttendanceActorData.php
          AttendanceAuditData.php
          AttendanceIdempotencyData.php
        Enums/
          AttendanceSubjectType.php
          AttendanceStatus.php
          ScanAction.php
          ScanSource.php
          AttendanceAuditAction.php
          AttendanceSessionStatus.php
        Exceptions/
          AttendanceException.php
          AttendanceSessionNotFoundException.php
          BadgeNotFoundException.php
          DuplicateScanException.php
          UnauthorizedAttendanceAccessException.php
          InvalidAttendanceStatusException.php
          AttendanceConcurrencyException.php
        Services/
          AttendanceService.php
          ScannerService.php
          BadgeLookupService.php
          StudentAttendanceService.php
          StaffAttendanceService.php
          AttendanceSessionResolver.php
          AttendanceStatusResolver.php
          AttendanceAuditLogger.php
          AttendanceIdempotencyService.php
          LateArrivalEvaluator.php
        Policies/
          DefaultAttendancePolicy.php
          DefaultAttendanceStatusPolicy.php
          DefaultAttendanceSessionPolicy.php
        Repositories/
          AttendanceRepository.php
          AttendanceReadRepository.php
          BadgeRepository.php
          AttendanceSessionRepository.php
        Support/
          AttendanceTransactionRunner.php
          AttendanceIdempotencyKey.php
          ScanFingerprint.php
          AttendanceClock.php
```

Islamic Sunday School-specific attendance goes here:

```text
app/
  Domain/
    IslamicSundaySchool/
      Attendance/
        Policies/
          IslamicSundaySchoolAttendancePolicy.php
          IslamicSundaySchoolAttendanceStatusPolicy.php
          IslamicSundaySchoolSessionPolicy.php
        Services/
          IslamicSundaySchoolSessionResolver.php
          IslamicSundaySchoolLateArrivalEvaluator.php
        Reports/
          IslamicSundaySchoolAttendanceReportBuilder.php
        Providers/
          IslamicSundaySchoolAttendanceServiceProvider.php
```

---

## 4. Core Design Rules

### 4.1 Attendance services must require `SchoolContext`

Every public attendance service method must receive `SchoolContext`.

Example:

```php
public function processScan(
    SchoolContext $context,
    ProcessScanData $data
): ScanResult;
```

A scan without context is invalid. A scan with the wrong context is worse.

### 4.2 Controllers must not decide attendance state

Controllers may:

- validate request
- authorize route
- build DTO
- call service contract
- return resource

Controllers may not:

- decide present/late/absent
- create attendance records directly
- create scan logs directly
- lookup badges directly
- calculate session dates
- mutate attendance day/session state
- decide duplicate scan handling

### 4.3 Attendance writes must be transactional

Required transactional operations:

- scanner check-in/check-out
- student attendance manual edit
- staff attendance manual edit
- late slip/log creation
- attendance day/session creation
- bulk attendance import
- absence marking
- status recalculation

### 4.4 Scanner flow must be idempotent

Duplicate scans must not double-count attendance.

Minimum duplicate identity:

```text
school_id
subject_type
subject_id
session_id or attendance_date
station_id, nullable
scan_action
scan_source
scan_time_window
```

Example behavior:

```text
- same student scans same station twice within configured duplicate window
  => return existing result or duplicate warning, do not create second attendance mutation
```

### 4.5 Attendance must separate subject from role

Attendance subjects are not always students.

Core supported subject types:

```text
student
staff
volunteer
visitor
```

Islamic Sunday School may map program volunteers to `volunteer` or `staff`, but core must not hardcode ministry language.

---

## 5. Core Contracts

### 5.1 `ScannerServiceContract`

```php
namespace App\Domain\SchoolCore\Attendance\Contracts;

use App\Domain\SchoolCore\Shared\Context\SchoolContext;
use App\Domain\SchoolCore\Attendance\DTO\ProcessScanData;

interface ScannerServiceContract
{
    public function processScan(SchoolContext $context, ProcessScanData $data): ScanResult;
}
```

### 5.2 `AttendanceServiceContract`

```php
namespace App\Domain\SchoolCore\Attendance\Contracts;

use App\Domain\SchoolCore\Shared\Context\SchoolContext;

interface AttendanceServiceContract
{
    public function getSessionAttendance(SchoolContext $context, int $sessionId): AttendanceSessionView;

    public function summarizeAttendance(SchoolContext $context, AttendanceSummaryQuery $query): AttendanceSummaryResult;
}
```

### 5.3 `StudentAttendanceServiceContract`

```php
namespace App\Domain\SchoolCore\Attendance\Contracts;

use App\Domain\SchoolCore\Shared\Context\SchoolContext;
use App\Domain\SchoolCore\Attendance\DTO\RecordStudentAttendanceData;

interface StudentAttendanceServiceContract
{
    public function recordStudentAttendance(
        SchoolContext $context,
        RecordStudentAttendanceData $data
    ): AttendanceRecordResult;
}
```

### 5.4 `StaffAttendanceServiceContract`

```php
namespace App\Domain\SchoolCore\Attendance\Contracts;

use App\Domain\SchoolCore\Shared\Context\SchoolContext;
use App\Domain\SchoolCore\Attendance\DTO\RecordStaffAttendanceData;

interface StaffAttendanceServiceContract
{
    public function recordStaffAttendance(
        SchoolContext $context,
        RecordStaffAttendanceData $data
    ): AttendanceRecordResult;
}
```

### 5.5 `BadgeLookupServiceContract`

```php
namespace App\Domain\SchoolCore\Attendance\Contracts;

use App\Domain\SchoolCore\Shared\Context\SchoolContext;
use App\Domain\SchoolCore\Attendance\DTO\BadgeLookupData;

interface BadgeLookupServiceContract
{
    public function lookup(SchoolContext $context, BadgeLookupData $data): BadgeLookupResult;
}
```

### 5.6 `AttendanceSessionResolverContract`

```php
namespace App\Domain\SchoolCore\Attendance\Contracts;

use App\Domain\SchoolCore\Shared\Context\SchoolContext;

interface AttendanceSessionResolverContract
{
    public function resolveForScan(
        SchoolContext $context,
        ScanSubjectView $subject,
        ScanTimestamp $timestamp
    ): AttendanceSessionView;
}
```

Islamic Sunday School may bind this contract to a Islamic Sunday School-specific resolver.

### 5.7 `AttendancePolicyContract`

```php
namespace App\Domain\SchoolCore\Attendance\Contracts;

use App\Domain\SchoolCore\Shared\Context\SchoolContext;

interface AttendancePolicyContract
{
    public function canRecordAttendance(
        SchoolContext $context,
        AttendanceSubjectView $subject,
        AttendanceSessionView $session
    ): bool;

    public function duplicateScanWindowSeconds(SchoolContext $context): int;

    public function allowCheckout(SchoolContext $context): bool;

    public function allowManualOverride(SchoolContext $context): bool;
}
```

### 5.8 `AttendanceStatusPolicyContract`

```php
namespace App\Domain\SchoolCore\Attendance\Contracts;

use App\Domain\SchoolCore\Shared\Context\SchoolContext;

interface AttendanceStatusPolicyContract
{
    public function statusForScan(
        SchoolContext $context,
        AttendanceSessionView $session,
        ScanTimestamp $timestamp
    ): AttendanceStatusResult;
}
```

---

## 6. DTO Requirements

Do not pass raw request arrays into attendance services.

### 6.1 `ProcessScanData`

Fields:

```text
badge_value
station_id, nullable
scan_action
scan_source
scanned_at
device_id, nullable
client_request_id, nullable
metadata, nullable
idempotency_key, nullable
```

### 6.2 `RecordStudentAttendanceData`

Fields:

```text
student_id
session_id, nullable
attendance_date
status
source
recorded_at
reason, nullable
notes, nullable
override_reason, nullable
idempotency_key, nullable
metadata, nullable
```

### 6.3 `RecordStaffAttendanceData`

Fields:

```text
staff_id
session_id, nullable
attendance_date
status
source
recorded_at
reason, nullable
notes, nullable
override_reason, nullable
idempotency_key, nullable
metadata, nullable
```

### 6.4 `BadgeLookupData`

Fields:

```text
badge_value
badge_type, nullable
subject_type_hint, nullable
station_id, nullable
```

---

## 7. Scanner Flow Target Design

Current risk: scanner logic can become a giant controller/service blob that mixes lookup, validation, attendance mutation, late logic, logging, and response formatting.

Target flow:

```text
ScannerController
  -> ProcessScanRequest
  -> SchoolContext
  -> ProcessScanData
  -> ScannerServiceContract::processScan()
  -> ScanResource
```

Inside `ScannerService`:

```text
1. Validate SchoolContext.
2. Normalize scan timestamp.
3. Build idempotency fingerprint.
4. Check duplicate scan.
5. Lookup badge.
6. Resolve attendance subject.
7. Resolve session/day.
8. Authorize attendance recording.
9. Determine scan status through policy.
10. Start transaction.
11. Lock subject/session attendance row.
12. Create or update attendance record.
13. Create scan log.
14. Create late/event log if needed.
15. Write audit log.
16. Store idempotency result.
17. Commit.
18. Return stable scan result.
```

Controllers should not know any of that. They have one job: receive chaos from the internet and pass it to adults.

---

## 8. Attendance Session Model

Core must support multiple attendance models:

```text
daily attendance
period attendance
session attendance
event attendance
check-in/check-out attendance
```

Suggested neutral terms:

```text
attendance_session
attendance_record
attendance_event
scan_log
attendance_subject
```

Avoid core terms like:

```text
Sunday program
liturgy
Islamic Sunday School class
volunteer/program group
homeroom-only
semester-only
```

Those can exist in extensions.

### 8.1 Session fields

Suggested fields:

```text
id
school_id
academic_year_id
term_id, nullable
name
session_type
starts_at
ends_at
timezone
status
metadata
created_at
updated_at
```

### 8.2 Record fields

Suggested fields:

```text
id
school_id
attendance_session_id, nullable
attendance_date
subject_type
subject_id
status
source
first_seen_at, nullable
last_seen_at, nullable
recorded_by_user_id, nullable
override_reason, nullable
metadata
created_at
updated_at
```

### 8.3 Event/scan log fields

Suggested fields:

```text
id
school_id
attendance_record_id, nullable
subject_type
subject_id
badge_id, nullable
station_id, nullable
scan_action
scan_source
scanned_at
status_result
duplicate_of_id, nullable
raw_payload_json, nullable
created_at
```

---

## 9. Attendance Status Rules

Core statuses:

```text
present
late
absent
excused
unexcused
checked_in
checked_out
unknown
```

Optional extended statuses:

```text
left_early
medical
field_trip
remote
tardy_excused
tardy_unexcused
```

Rules:

```text
- Status calculation belongs to AttendanceStatusPolicyContract.
- Manual override requires reason.
- Override must create audit log.
- Status transitions must be explicit.
- Reports must use canonical status codes, not display labels.
```

Islamic Sunday School can map statuses to friendly labels, but the core should use neutral stable codes.

---

## 10. Idempotency and Duplicate Scan Rules

### 10.1 Idempotency key

Preferred:

```text
client_request_id or idempotency_key from scanner device/client
```

Fallback fingerprint:

```text
school_id
badge_value_hash
station_id
scan_action
scan_source
rounded_scanned_at_window
```

### 10.2 Duplicate scan behavior

Within duplicate window:

```text
- do not mutate attendance again
- return original scan result, or explicit duplicate result
- create optional duplicate scan log pointing to original scan
```

Outside duplicate window:

```text
- allow check-in/check-out flow if policy allows
- otherwise return current attendance state without double-counting
```

### 10.3 Required tests

```text
- duplicate scan within window does not create second record
- duplicate scan within window returns stable result
- duplicate scan from another station follows policy
- same badge next session creates new record
- same badge different school cannot collide
```

---

## 11. Transaction and Locking Strategy

### 11.1 Scanner check-in

Required transaction:

```text
BEGIN
  lock attendance session
  lock or create attendance record for subject/session/date
  write attendance event/scan log
  update first_seen_at/last_seen_at/status
  write late/event log if needed
  write audit log
  store idempotency result
COMMIT
```

### 11.2 Manual attendance edit

Required transaction:

```text
BEGIN
  lock attendance record
  capture before state
  validate actor authorization
  apply status update
  write override reason
  write audit log before/after
COMMIT
```

### 11.3 Bulk attendance marking

Required transaction strategy:

```text
- process in chunks
- each chunk runs in transaction
- every row scoped to school_id
- failures return row-level error report
- audit each changed row or write batch audit plus row details
```

---

## 12. Audit Logging Requirements

Every attendance mutation must create an audit event.

Required audit fields:

```text
school_id
academic_year_id
term_id, nullable
actor_user_id, nullable
actor_type
action
subject_type
subject_id
attendance_session_id, nullable
attendance_record_id, nullable
before_json, nullable
after_json, nullable
reason, nullable
source
station_id, nullable
request_id, nullable
ip_address, nullable
user_agent, nullable
idempotency_key, nullable
created_at
```

Required actions:

```text
attendance.scan.processed
attendance.scan.duplicate
attendance.student.recorded
attendance.student.edited
attendance.staff.recorded
attendance.staff.edited
attendance.status.overridden
attendance.session.created
attendance.session.closed
attendance.bulk.updated
```

Audit logs are append-only.

---

## 13. Authorization Requirements

Minimum actions:

```text
attendance.scan.process
attendance.student.view
attendance.student.record
attendance.student.override
attendance.staff.view
attendance.staff.record
attendance.staff.override
attendance.session.view
attendance.session.manage
attendance.report.view
```

Every attendance endpoint must have tests for:

```text
unauthenticated
wrong school
wrong role
wrong owner/family
authorized admin
authorized staff
authorized teacher
scanner/device token, if applicable
```

Authorization must check:

```text
actor role/capability
school boundary
resource relationship
session state
device/station permission, if scanner devices are used
```

---

## 14. Islamic Sunday School Extension Model

Islamic Sunday School attendance extends core through policies.

### 14.1 Islamic Sunday School allowed customizations

```text
- Islamic Sunday School program session resolver
- masjid/community calendar mapping
- class/session grouping
- program volunteer attendance
- program session attendance labels
- late window policy
- family attendance summaries
- curriculum attendance reports
```

### 14.2 Islamic Sunday School forbidden customizations

```text
- bypassing attendance transactions
- bypassing audit logs
- creating scan logs without core scanner service
- hardcoding Islamic Sunday School terminology into SchoolCore contracts
- bypassing SchoolContext
- reading request/auth directly in domain services
```

### 14.3 Binding example

```php
$this->app->bind(
    AttendanceSessionResolverContract::class,
    IslamicSundaySchoolSessionResolver::class
);

$this->app->bind(
    AttendanceStatusPolicyContract::class,
    IslamicSundaySchoolAttendanceStatusPolicy::class
);

$this->app->bind(
    AttendancePolicyContract::class,
    IslamicSundaySchoolAttendancePolicy::class
);
```

---

## 15. Migration Strategy

### 15.1 Do not rewrite all attendance at once

Use vertical slices.

Recommended order:

```text
1. Scanner request/controller delegation
2. Badge lookup service
3. Scan idempotency service
4. Student attendance recording
5. Staff attendance recording
6. Attendance status policy
7. Attendance session resolver
8. Manual override flows
9. Reports/read models
```

Why this order:

```text
- Scanner is the current high-risk blob.
- Badge lookup creates a stable seam.
- Idempotency prevents duplicate reality.
- Student/staff recording are core reusable mutations.
- Status/session policies allow Islamic Sunday School customization.
- Reports should consume stable attendance data last.
```

### 15.2 Compatibility layer

Existing controllers may remain temporarily, but must delegate to new contracts.

Example:

```text
ScannerController
  -> ProcessScanRequest
  -> SchoolContext
  -> ProcessScanData
  -> ScannerServiceContract::processScan()
  -> ScanResource
```

Legacy private methods should move into services or be marked deprecated.

### 15.3 Deprecation rule

Every legacy attendance/scanner method must be labeled as one of:

```text
canonical
adapter
deprecated
remove
```

No unlabeled duplicate attendance logic.

---

## 16. First Implementation Slice: Scanner Controller Delegation

### Objective

Make the scanner controller thin without changing behavior.

### Tasks

```text
ATT-001. Create attendance module scaffold.
ATT-002. Create ScannerServiceContract.
ATT-003. Create ProcessScanData.
ATT-004. Create ProcessScanRequest.
ATT-005. Create ScannerService shell using existing logic internally.
ATT-006. Update ScannerController to delegate.
ATT-007. Add response resource/snapshot tests.
```

### Required tests

```text
- valid student scan still works
- valid staff scan still works
- invalid badge still fails correctly
- existing response shape remains compatible
- unauthenticated scan behavior remains as intended
- school context is resolved
```

### Done when

```text
- ScannerController has no private domain methods.
- Existing scanner API remains compatible.
- ScannerService is the only scanner orchestration entry point.
```

---

## 17. Second Implementation Slice: Badge Lookup Service

### Objective

Extract badge lookup into reusable service.

### Tasks

```text
ATT-008. Create BadgeLookupServiceContract.
ATT-009. Create BadgeLookupData.
ATT-010. Create BadgeRepository.
ATT-011. Scope badge lookup by school_id.
ATT-012. Return neutral subject result.
ATT-013. Add tests for student/staff/unknown badge.
```

### Required tests

```text
- finds student badge in same school
- finds staff badge in same school
- does not find badge from another school
- handles inactive badge
- handles duplicate badge conflict explicitly
- unknown badge returns controlled error
```

### Done when

```text
- ScannerService no longer performs direct badge queries.
- Badge result does not contain Islamic Sunday School-specific assumptions.
```

---

## 18. Third Implementation Slice: Scan Idempotency

### Objective

Prevent duplicate scanner mutations.

### Tasks

```text
ATT-014. Create AttendanceIdempotencyServiceContract.
ATT-015. Create ScanFingerprint.
ATT-016. Add idempotency key/fingerprint table if needed.
ATT-017. Store scan result for repeated calls.
ATT-018. Add duplicate scan log behavior.
```

### Required tests

```text
- repeated scan with same key returns same result
- same key with different payload fails
- repeated scan within duplicate window does not double-count
- repeated scan after duplicate window follows policy
- duplicate detection is school-scoped
```

### Done when

```text
- Duplicate scans cannot create duplicate attendance records.
- Idempotency behavior is deterministic and tested.
```

---

## 19. Fourth Implementation Slice: Student Attendance Recording

### Objective

Move student attendance mutation into modular service.

### Tasks

```text
ATT-019. Create StudentAttendanceServiceContract.
ATT-020. Create RecordStudentAttendanceData.
ATT-021. Implement StudentAttendanceService.
ATT-022. Add transaction runner.
ATT-023. Lock attendance record/session during mutation.
ATT-024. Add attendance.student.recorded audit event.
ATT-025. Make ScannerService call StudentAttendanceService.
```

### Required tests

```text
- records present status
- records late status
- rejects invalid status
- rejects cross-school student
- creates audit log
- duplicate scan does not double mutate
- concurrent scan does not duplicate record
```

### Done when

```text
- Student attendance mutation is in one service.
- Scanner and manual endpoints share the same mutation path where practical.
```

---

## 20. Fifth Implementation Slice: Staff Attendance Recording

### Objective

Move staff attendance mutation into modular service.

### Tasks

```text
ATT-026. Create StaffAttendanceServiceContract.
ATT-027. Create RecordStaffAttendanceData.
ATT-028. Implement StaffAttendanceService.
ATT-029. Add staff attendance audit events.
ATT-030. Make ScannerService call StaffAttendanceService.
```

### Required tests

```text
- records staff present status
- records staff late status
- rejects cross-school staff
- rejects inactive staff if policy says so
- creates audit log
- duplicate scan does not double mutate
```

### Done when

```text
- Staff attendance uses modular service.
- Staff and student attendance share common audit/idempotency infrastructure.
```

---

## 21. Sixth Implementation Slice: Attendance Session Resolver

### Objective

Create neutral session resolution that Islamic Sunday School can override.

### Tasks

```text
ATT-031. Create AttendanceSessionResolverContract.
ATT-032. Implement DefaultAttendanceSessionResolver.
ATT-033. Implement IslamicSundaySchoolSessionResolver.
ATT-034. Bind resolver through service provider.
ATT-035. Add tests for standard school and Islamic Sunday School sessions.
```

### Required tests

```text
- resolves daily session for standard school
- resolves Islamic Sunday School program session for Islamic Sunday School profile
- rejects scan outside valid session if policy requires
- handles timezone correctly
- does not resolve another school's session
```

### Done when

```text
- Core scanner does not hardcode Sunday/session logic.
- Islamic Sunday School session behavior is policy/resolver-driven.
```

---

## 22. Seventh Implementation Slice: Attendance Status Policy

### Objective

Move late/present/absent decision-making behind policy.

### Tasks

```text
ATT-036. Create AttendanceStatusPolicyContract.
ATT-037. Implement DefaultAttendanceStatusPolicy.
ATT-038. Implement IslamicSundaySchoolAttendanceStatusPolicy if needed.
ATT-039. Move late window logic out of controller/legacy methods.
ATT-040. Add status transition tests.
```

### Required tests

```text
- scan before start is handled by policy
- scan during on-time window returns present
- scan after late threshold returns late
- excused/manual status requires override path
- Islamic Sunday School late window differs without changing core
```

### Done when

```text
- Late/present logic is not hardcoded in scanner orchestration.
- Different school profiles can define status rules.
```

---

## 23. Eighth Implementation Slice: Manual Overrides

### Objective

Standardize manual attendance edits.

### Tasks

```text
ATT-041. Create manual override FormRequests.
ATT-042. Require override reason.
ATT-043. Use StudentAttendanceService/StaffAttendanceService.
ATT-044. Capture before/after audit logs.
ATT-045. Add authorization policy checks.
```

### Required tests

```text
- manual override requires reason
- unauthorized teacher cannot override outside scope
- admin can override
- cross-school override fails
- before/after audit is written
- invalid transition fails
```

### Done when

```text
- Manual edits and scanner writes use consistent rules.
- Override history is auditable.
```

---

## 24. Attendance Reports

Reports must consume attendance read models, not invent attendance truth.

### Tasks

```text
ATT-046. Create AttendanceReadRepository.
ATT-047. Create attendance summary read model.
ATT-048. Create student attendance history read model.
ATT-049. Create staff attendance summary read model.
ATT-050. Update attendance reports to use read models.
ATT-051. Add school context to all report queries.
```

### Required tests

```text
- report excludes other schools
- report status counts match AttendanceService
- duplicate scans do not inflate report
- Islamic Sunday School report labels do not leak into core
- export output matches approved snapshot
```

---

## 25. Database and Schema Requirements

### 25.1 Required indexes

Add or verify:

```text
attendance_sessions.school_id indexed
attendance_sessions.academic_year_id indexed
attendance_sessions.term_id indexed, nullable
attendance_sessions.starts_at indexed
attendance_sessions.status indexed

attendance_records.school_id indexed
attendance_records.attendance_session_id indexed, nullable
attendance_records.attendance_date indexed
attendance_records.subject_type indexed
attendance_records.subject_id indexed
attendance_records.status indexed

attendance_scan_logs.school_id indexed
attendance_scan_logs.subject_type/subject_id indexed
attendance_scan_logs.badge_id indexed, nullable
attendance_scan_logs.station_id indexed, nullable
attendance_scan_logs.scanned_at indexed
attendance_scan_logs.duplicate_of_id indexed, nullable

attendance_audit_logs.school_id indexed
attendance_audit_logs.actor_user_id indexed, nullable
attendance_audit_logs.subject_type/subject_id indexed
attendance_audit_logs.created_at indexed
```

### 25.2 Recommended unique constraints

```text
unique(school_id, attendance_session_id, subject_type, subject_id)
unique(school_id, attendance_date, subject_type, subject_id) for daily-only records, if used
unique(school_id, idempotency_key, operation) where idempotency_key is not null
```

### 25.3 Migration caution

Do not aggressively delete or rename existing attendance tables in Phase 4. Add adapters/read repositories first. Attendance history is institutional memory, and apparently institutions become cranky when memory gets truncated by “cleanup.”

---

## 26. FormRequest Requirements

Create or update:

```text
ProcessScanRequest
RecordStudentAttendanceRequest
RecordStaffAttendanceRequest
ManualStudentAttendanceOverrideRequest
ManualStaffAttendanceOverrideRequest
AttendanceSummaryRequest
AttendanceSessionRequest
```

Each FormRequest must define:

```text
authorize()
rules()
messages(), if needed
toDTO(), optional but recommended
```

Controllers should use:

```php
$data = $request->toDTO();
```

Not raw request array merging.

---

## 27. Service Provider Bindings

Create:

```php
namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use App\Domain\SchoolCore\Attendance\Contracts\ScannerServiceContract;
use App\Domain\SchoolCore\Attendance\Services\ScannerService;

final class SchoolCoreAttendanceServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        $this->app->bind(ScannerServiceContract::class, ScannerService::class);
        $this->app->bind(BadgeLookupServiceContract::class, BadgeLookupService::class);
        $this->app->bind(StudentAttendanceServiceContract::class, StudentAttendanceService::class);
        $this->app->bind(StaffAttendanceServiceContract::class, StaffAttendanceService::class);
        $this->app->bind(AttendanceSessionResolverContract::class, DefaultAttendanceSessionResolver::class);
        $this->app->bind(AttendanceStatusPolicyContract::class, DefaultAttendanceStatusPolicy::class);
        $this->app->bind(AttendancePolicyContract::class, DefaultAttendancePolicy::class);
        $this->app->bind(AttendanceAuditLoggerContract::class, AttendanceAuditLogger::class);
        $this->app->bind(AttendanceIdempotencyServiceContract::class, AttendanceIdempotencyService::class);
    }
}
```

Islamic Sunday School may override policy/resolver bindings in its own provider.

---

## 28. Architecture Tests

Add tests that fail if attendance boundaries are violated.

Required assertions:

```text
SchoolCore\Attendance must not depend on Domain\IslamicIslamicSundaySchool
SchoolCore\Attendance must not depend on Illuminate\Http\Request
SchoolCore\Attendance services must not call auth()
SchoolCore\Attendance services must not call request()
Controllers must not instantiate attendance concrete services directly
ScannerController must not contain domain private methods
Attendance mutations must use transaction runner or DB transaction
Scanner writes must go through ScannerServiceContract
```

Optional static checks:

```text
No Sunday/masjid/community/ministry vocabulary inside SchoolCore\Attendance contracts
No direct attendance DB writes inside controllers
No duplicate scan mutation without idempotency check
No manual override without reason
```

---

## 29. Rollout Plan

### Step 1: Add attendance module scaffolding

```text
- create namespaces
- create contracts
- create DTOs
- create provider
- bind default implementations
```

### Step 2: Add regression tests around current scanner behavior

```text
- student scan
- staff scan
- invalid badge
- duplicate scan
- late scan
- cross-school badge
```

Some tests may fail because current behavior is unsafe or ambiguous. Good. Ambiguity is a bug wearing a cardigan.

### Step 3: Delegate ScannerController

```text
- move orchestration behind ScannerServiceContract
- preserve response shape initially
```

### Step 4: Extract badge lookup

```text
- centralize badge resolution
- school-scope lookup
```

### Step 5: Add idempotency

```text
- prevent duplicate scan mutation
- add scan fingerprinting
```

### Step 6: Extract student/staff attendance services

```text
- shared mutation path
- transactions
- audit logging
```

### Step 7: Add session/status policies

```text
- enable Islamic Sunday School extension
- remove hardcoded late/session logic
```

### Step 8: Manual override standardization

```text
- require reasons
- before/after audit
- policy authorization
```

### Step 9: Reports/read models

```text
- consume stable attendance truth
- school context enforced
```

---

## 30. Implementation Tickets

## ATT-001: Create attendance module scaffold

### Description

Create the `SchoolCore\Attendance` namespace and initial contracts, DTO folders, service folders, enum folders, exception folders, and provider.

### Acceptance Criteria

```text
- Namespace exists.
- Service provider is registered.
- Empty contracts compile.
- No controller behavior changes yet.
```

---

## ATT-002: Delegate ScannerController to ScannerService

### Description

Move scanner orchestration behind `ScannerServiceContract` while preserving current response behavior.

### Acceptance Criteria

```text
- ScannerController validates and delegates only.
- Existing scanner responses remain compatible.
- ScannerService is the only orchestration entry point.
- Tests cover student/staff/invalid badge scans.
```

---

## ATT-003: Extract BadgeLookupService

### Description

Centralize badge lookup and scope it by school.

### Acceptance Criteria

```text
- Student badge lookup works.
- Staff badge lookup works.
- Unknown badge returns controlled error.
- Cross-school badge lookup fails.
- Inactive badges are handled explicitly.
```

---

## ATT-004: Add scanner idempotency

### Description

Prevent duplicate scans from creating duplicate attendance mutations.

### Acceptance Criteria

```text
- Same idempotency key returns same result.
- Same key with different payload fails.
- Duplicate scan within window does not double-count.
- Duplicate scan is logged.
- Idempotency is school-scoped.
```

---

## ATT-005: Extract StudentAttendanceService

### Description

Move student attendance mutation into modular service.

### Acceptance Criteria

```text
- Uses SchoolContext.
- Uses transaction.
- Locks relevant record/session.
- Creates audit log.
- Rejects cross-school student.
- Handles duplicate scan safely.
```

---

## ATT-006: Extract StaffAttendanceService

### Description

Move staff attendance mutation into modular service.

### Acceptance Criteria

```text
- Uses SchoolContext.
- Uses transaction.
- Creates audit log.
- Rejects cross-school staff.
- Shares idempotency/audit infrastructure.
```

---

## ATT-007: Add AttendanceSessionResolver

### Description

Create neutral session resolution with Islamic Sunday School override support.

### Acceptance Criteria

```text
- Default resolver works for generic school.
- Islamic Sunday School resolver works through binding.
- Timezone is respected.
- Cross-school sessions fail.
- Core resolver contains no Islamic Sunday School vocabulary.
```

---

## ATT-008: Add AttendanceStatusPolicy

### Description

Move status and late logic behind policy.

### Acceptance Criteria

```text
- Present/late logic is policy-driven.
- Islamic Sunday School can use different late window.
- Manual-only statuses cannot be set through scan.
- Tests cover status boundaries.
```

---

## ATT-009: Standardize manual overrides

### Description

Move manual edit flows through attendance services with audit trail.

### Acceptance Criteria

```text
- Override requires reason.
- Authorization is enforced.
- Before/after audit is recorded.
- Cross-school override fails.
```

---

## ATT-010: Add attendance architecture tests

### Description

Enforce attendance module boundaries in CI.

### Acceptance Criteria

```text
- SchoolCore\Attendance cannot import IslamicSundaySchool.
- Attendance services cannot call auth() or request().
- Controllers cannot write attendance rows directly.
- ScannerController has no domain private methods.
- CI fails on boundary violation.
```

---

## 31. Definition of Done for Phase 4

Phase 4 is done when:

```text
- Attendance core contracts exist.
- Scanner controller delegates to ScannerService.
- Badge lookup is modular and school-scoped.
- Scanner idempotency prevents duplicate mutations.
- Student attendance mutation is modular.
- Staff attendance mutation is modular.
- Attendance sessions are resolved through a contract.
- Attendance status is policy-driven.
- Manual overrides require reason and audit trail.
- Attendance services require SchoolContext.
- Islamic Sunday School attendance behavior is implemented through policies/resolvers.
- Cross-school attendance access is blocked by tests.
- Architecture tests prevent SchoolCore from depending on IslamicSundaySchool.
- Legacy attendance/scanner methods are labeled canonical, adapter, deprecated, or remove.
```

---

## 32. Non-Goals

Do not include in Phase 4:

```text
- full attendance UI redesign
- total attendance schema rewrite
- biometric device integration
- facial recognition
- external hardware vendor SDK integration
- parent-facing attendance portal redesign
- full reporting redesign
```

Those may happen later. Phase 4 is about making attendance reliable, reusable, scoped, and extensible.

---

## 33. Main Risks

### Risk: duplicate attendance records

Mitigation:

```text
- idempotency key
- scan fingerprint
- unique constraints
- row locks
- duplicate tests
```

### Risk: cross-school leakage

Mitigation:

```text
- mandatory SchoolContext
- school-scoped repositories
- authorization tests
```

### Risk: scanner behavior break

Mitigation:

```text
- snapshot current responses
- delegate first, refactor internals second
- preserve compatibility adapter
```

### Risk: Islamic Sunday School leakage into core

Mitigation:

```text
- contract vocabulary review
- architecture tests
- Islamic Sunday School resolver/policies only
```

### Risk: status logic drift

Mitigation:

```text
- single AttendanceStatusPolicyContract
- tests for status boundary times
- report counts based on canonical statuses
```

---

## 34. Immediate Next Work Queue

Start here:

```text
1. ATT-001 Create attendance module scaffold
2. ATT-002 Delegate ScannerController to ScannerService
3. ATT-003 Extract BadgeLookupService
4. ATT-004 Add scanner idempotency
5. ATT-005 Extract StudentAttendanceService
6. ATT-006 Extract StaffAttendanceService
7. ATT-007 Add AttendanceSessionResolver
8. ATT-008 Add AttendanceStatusPolicy
9. ATT-010 Add attendance architecture tests
```

Do not start with reports. Reports are mirrors. Fix the face first.
