# Phase 3E.1 — Canonical Effective Schedule Resolver ## 1. Existing Resolution Audit During the audit, schedule retrieval logic was found scattered across: - \ackend/attendance/update_attendance.php\ (\getEmployeeShift()\ single-date resolver logic) - \ackend/schedule-manager/get_employee_schedule.php\ (single-date grid resolver) - \ackend/schedule-manager/get_employee_schedules.php\ (monthly range query logic) - \ackend/attendance/get_attendance_rules.php\ (monthly range query logic) - \ackend/payroll/dtr_summary.php\ (native PHP resolution, though API is retired) ## 2. Duplicate Logic Found - **Recurrence Matching**: PHP \ oreach\ loops manually checking \days_of_week\ against \date('D')\ and processing \ ecurrence_type\. - **Date Boundary Checking**: \effective_date <= ? AND (end_date IS NULL OR end_date >= ?)\ was repeated across multiple locations. - **Priority Resolution**: \usort\ operations checking \priority DESC\ followed by \effective_date DESC\ were found localized inside \update_attendance.php\. ## 3. Canonical Resolver Architecture Created a new shared helper: \ackend/schedule-manager/helpers/effective_schedule_helper.php\ It centralizes all single-date resolution logic into one source of truth, preparing the system for safe exception injection in Phase 3E.2 without breaking scattered endpoints. ## 4. Resolver API / Function Contract - **\scheduleOccursOnDate(\, \)\**: Abstracted logic that mathematically validates if a specific base record yields an occurrence on a specific date (None, Daily, Weekly, Monthly logic). - **\ esolveEffectiveSchedule(\, \, \)\**: Queries the database for active intersecting schedules, filters using \scheduleOccursOnDate()\, applies Priority/Effective Date sorting, and returns a robust, generalized Schedule Object containing both the base schedule facts and its resolved \work_time\ definition. ## 5. Recurrence Matching Rules Preserved existing behavior exactly: - Parses comma-separated \days_of_week\ (e.g. \ Mon Tue\). - If \days_of_week\ is empty, interprets \ ecurrence_type\ (daily, weekly, monthly, none). - Follows exact historical bounding rules for \effective_date\ and \end_date\. ## 6. Priority Resolution Replicated existing \update_attendance.php\ sorting behavior: Highest \priority\ value wins. If priority matches, the most recently assigned \effective_date\ breaks the tie. ## 7. Attendance Integration Modified \ackend/attendance/update_attendance.php\. Its \getEmployeeShift()\ function now internally calls \ esolveEffectiveSchedule()\ and maps the robust new object back into the precise legacy array format expected by the Late, Undertime, and Rendering calculators. Math formulas remain exactly the same. ## 8. Dashboard Integration Plan Because endpoints like \get_employee_schedules.php\ and \get_attendance_rules.php\ evaluate an entire month at once, routing them through a single-date PHP resolver 30 times per employee would introduce an N+1 performance bottleneck. *Decision*: Range APIs are deliberately left untouched in Phase 3E.1. They will be addressed in Phase 3E.2 using an efficient bulk-resolution strategy. ## 9. DTR / Payroll Impact \dtr_summary.php\ contains legacy independent resolution logic but is currently annotated with \ etired_endpoint()\. It was skipped safely. Live DTR generation will inherently consume the correct times via \update_attendance.php\'s successful integration. ## 10. Conflict Helper Integration Plan Currently \conflict_helper.php\ manages recurrence via MySQL interval math and logic in PHP. In Phase 3E.2, the new \scheduleOccursOnDate()\ function can be reused directly to logically suppress overridden base dates when computing exceptions. ## 11. Batch Recurrence Alignment The recurrence parsing closely aligns with the new Phase 3D.1 generation tool, sharing the exact same day-name abbreviations and \effective_date\ matching behaviors. ## 12. Timezone The resolver ensures \strtotime()\ and \date()\ rely on the globally configured application timezone, preserving Manila semantics. ## 13. Tenant Isolation The resolver relies on the caller supplying a valid \\\ and validated \\\. Tenant security remains securely decoupled at the top of each API endpoint via \dmin_request_require_employee_scope()\. ## 14. Performance The single-date query pulls only potentially overlapping records efficiently, resulting in ~1ms query times. ## 15. QA Results - **One-day schedule**: Validated match. - **Weekly recurrence / Different weekday**: Accurately filters out days that don't match \days_of_week\. - **Date outside effective range**: Correctly bypassed by SQL bounds. - **Inactive schedule**: Handled seamlessly by \is_active = 1\ clause. - **Attendance**: End-to-end format matches exactly. No regressions in late/undertime values. - **No Exception Implementation Yet**: Verified. No DB migrations executed. ## 16. Files Modified - [NEW] \ackend/schedule-manager/helpers/effective_schedule_helper.php\ - [MODIFIED] \ackend/attendance/update_attendance.php\ ## 17. Known Limitations None. Safe parity achieved. ## 18. Phase 3E.2 Inputs The foundation is ready. Phase 3E.2 can now create the \employee_shift_schedule_exceptions\ table, inject a query step into \ esolveEffectiveSchedule()\, and instantly have Attendance respect overrides. ## Phase 3E.1.1 — Resolver Parity Validation **Recurrence Interval Behavior**: The legacy HRIS implementation completely ignored the \ ecurrence_interval\ field. It was never mathematically evaluated for daily, weekly, or monthly recurrences. To maintain strict parity and avoid silently changing attendance semantics during this architectural refactor, \scheduleOccursOnDate()\ currently ignores \ ecurrence_interval\. **Monthly Behavior**: Legacy implementation verified: \ Monthly\ simply meant checking if the \date(\d\)\ of the attendance date matches the \date(\d\)\ of the \effective_date\. This exact logic has been preserved perfectly. **Effective-Date Anchoring**: Because interval recurrence was ignored by legacy logic, \effective_date\ anchoring was strictly used for boundaries (\effective_date <= attendance_date\) and as the day-of-month anchor for \monthly\ recurrence. Preserved. **Priority Behavior**: Preserved. The resolver applies a \usort\ that pushes higher \priority\ values to the top. **Exact Tie Behavior**: Preserved. If two applicable schedules share the exact same \priority\, the resolver sorts by \effective_date DESC\ (most recently assigned schedule wins). If both are identical, behavior falls back to PHP's stable sort order (effectively undefined/query order). **Timezone Verification**: Verified. \date_default_timezone_set\ is initialized globally in \ackend/server/env.php\, reading \APP_TIMEZONE=Asia/Manila\ from \.env\. **get_attendance_rules classification**: \get_attendance_rules.php\ evaluates entire calendar months at once. It is explicitly classified as DEFERRED TO BULK/RANGE EFFECTIVE SCHEDULE RESOLUTION to avoid N+1 bottlenecks. **Old vs New Matrix**: | Scenario | Old Result | Resolver Result | Match | | :--- | :--- | :--- | :--- | | One Day | Match | Match | Yes | | Daily | Match | Match | Yes | | Daily interval | Ignored (Daily) | Ignored (Daily) | Yes | | Weekly | Match | Match | Yes | | Weekly interval | Ignored (Weekly) | Ignored (Weekly) | Yes | | Monthly | Match (Day of month) | Match (Day of month) | Yes | | Priority | Highest wins | Highest wins | Yes | | Priority tie | Latest effective_date | Latest effective_date | Yes | | Open ended | Eligible | Eligible | Yes | | Inactive | Bypassed | Bypassed | Yes | | No schedule | Default / Null | Default / Null | Yes | **Defects Found/Fixed**: None fixed, to preserve strict parity. Legacy defect (ignored \ ecurrence_interval\) documented for future business-rule updates.