# Attendance Review

- Module key: `AttendanceReview`
- Category: `HRIS`
- Path: `app/Modules/AttendanceReview`

## Ringkasan

Attendance Review berada di kategori HRIS. Modul ini memiliki 177 definisi route, 9 controller, 10 model, dan 15 view. Database module terdeteksi pada tabel: `attendance_logs`, `attendance_daily_reviews`, `attendance_daily_review_histories`, `attendance_export_jobs`, `attendance_warning_letters`.

## Flowchart

```mermaid
flowchart TD
    A[User / Operator] --> B[Menu Attendance Review]
    B --> C[Routes: 177]
    C --> D[Controllers: 9]
    D --> E[Views: 15]
    D --> F[(Tables: 5<br/>attendance_logs<br/>attendance_daily_reviews<br/>attendance_daily_review_histories<br/>attendance_export_jobs<br/>attendance_warning_letters)]
    D --> G[Validation / Business Rules]
    G --> F
    F --> H[Report / Export / Audit]
```


## Routes

- `Route::middleware(['auth', 'ip.block', 'menu.access'])->group(function () {`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::prefix('attendance-site-assignments')->name('attendance_site_assignments.')->group(function () {`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::get('/', [AttendanceSiteAssignmentController::class, 'index'])->name('index');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::get('data', [AttendanceSiteAssignmentController::class, 'data'])->name('data');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::post('/', [AttendanceSiteAssignmentController::class, 'store'])->name('store');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::put('{id}', [AttendanceSiteAssignmentController::class, 'update'])->name('update');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::delete('{id}', [AttendanceSiteAssignmentController::class, 'destroy'])->name('destroy');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::get('refs/karyawans', [AttendanceSiteAssignmentController::class, 'refKaryawans'])->name('refs.karyawans');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::get('refs/sites', [AttendanceSiteAssignmentController::class, 'refSites'])->name('refs.sites');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::get('template', [AttendanceSiteAssignmentController::class, 'template'])->name('template');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::get('export', [AttendanceSiteAssignmentController::class, 'export'])->name('export');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::post('import/preview', [AttendanceSiteAssignmentController::class, 'importPreview'])->name('import.preview');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::post('import/confirm', [AttendanceSiteAssignmentController::class, 'importConfirm'])->name('import.confirm');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::get('import/error-report/{token}', [AttendanceSiteAssignmentController::class, 'downloadErrorReport'])->name('import.error_report');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::prefix('attendance-mobile-notification-settings')->name('attendance_mobile_notification_settings.')->group(function () {`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::get('/', [AttendanceMobileNotificationSettingController::class, 'index'])->name('index');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::post('/', [AttendanceMobileNotificationSettingController::class, 'update'])->name('update');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::prefix('attendance-morning-checkin-report')->name('attendance_morning_checkin_report.')->group(function () {`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::get('/', [AttendanceMorningCheckinReportController::class, 'index'])->name('index');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::get('data', [AttendanceMorningCheckinReportController::class, 'data'])->name('data');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::prefix('attendance-mobile-sessions')->name('attendance_mobile_sessions.')->group(function () {`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::get('/', [AttendanceMobileSessionController::class, 'index'])->name('index');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::get('data', [AttendanceMobileSessionController::class, 'data'])->name('data');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::post('kick/{id}', [AttendanceMobileSessionController::class, 'kickSession'])->name('kick_session');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::post('kick-user/{karyawanId}', [AttendanceMobileSessionController::class, 'kickUser'])->name('kick_user');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::post('kick-all', [AttendanceMobileSessionController::class, 'kickAll'])->name('kick_all');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::post('toggle-user-lock/{karyawanId}', [AttendanceMobileSessionController::class, 'toggleUserLock'])->name('toggle_user_lock');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::post('toggle-global-lock', [AttendanceMobileSessionController::class, 'toggleGlobalLock'])->name('toggle_global_lock');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::prefix('attendance-review')->name('attendance_review.')->group(function () {`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::get('/', [AttendanceReviewController::class, 'index'])->name('index');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::get('summary', [AttendanceReviewController::class, 'summaryPage'])->name('summary');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::get('summary-data', [AttendanceReviewController::class, 'summaryData'])->name('summary.data');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::get('summary-export-excel', [AttendanceReviewController::class, 'exportSummaryExcel'])->name('summary.export_excel');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::get('summary-export-pdf', [AttendanceReviewController::class, 'exportSummaryPdf'])->name('summary.export_pdf');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::post('discipline/check-profile', [AttendanceReviewController::class, 'disciplineCheckProfile'])->name('discipline.check_profile');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::post('discipline/issue-warning-letter', [AttendanceReviewController::class, 'disciplineIssueWarningLetter'])->name('discipline.issue_warning_letter');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::post('discipline/warning-letter/{id}/revoke', [AttendanceReviewController::class, 'disciplineRevokeWarningLetter'])->name('discipline.warning_letter_revoke');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::get('discipline/warning-letter/{id}/pdf', [AttendanceReviewController::class, 'disciplineWarningLetterPdf'])->name('discipline.warning_letter_pdf');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::post('discipline/settings', [AttendanceReviewController::class, 'updateDisciplineSettings'])->name('discipline.settings.update');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::get('refs', [AttendanceReviewController::class, 'refs'])->name('refs');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::get('refs/profil-bulans', [AttendanceReviewController::class, 'profilBulanOptions'])->name('refs.profil_bulans');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::get('logs', [AttendanceReviewController::class, 'logData'])->name('logs.data');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::get('logs/unmapped', [AttendanceReviewController::class, 'unmappedLogData'])->name('logs.unmapped');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::post('logs', [AttendanceReviewController::class, 'storeLog'])->name('logs.store');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::post('logs/import-machine', [AttendanceReviewController::class, 'queueImportMachineLog'])->name('logs.import_machine');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::post('logs/test-public-api', [AttendanceReviewController::class, 'testPublicApiConnection'])->name('logs.test_public_api');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::post('logs/preview-public-api', [AttendanceReviewController::class, 'previewPublicApiLog'])->name('logs.preview_public_api');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::post('logs/sync-public-api', [AttendanceReviewController::class, 'queueSyncPublicApiLog'])->name('logs.sync_public_api');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::get('logs/jobs/{id}/status', [AttendanceReviewController::class, 'logJobStatus'])->name('logs.job.status');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::post('logs/repair-mapping', [AttendanceReviewController::class, 'repairLogMapping'])->name('logs.repair_mapping');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::delete('logs/{id}', [AttendanceReviewController::class, 'destroyLog'])->name('logs.destroy');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::post('detect', [AttendanceReviewController::class, 'runDetection'])->name('detect');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::post('rebuild-planning', [AttendanceReviewController::class, 'rebuildPlanning'])->name('rebuild_planning');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::get('rebuild/{id}/status', [AttendanceReviewController::class, 'rebuildStatus'])->name('rebuild.status');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::get('detect/{id}/status', [AttendanceReviewController::class, 'detectionStatus'])->name('detect.status');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::post('detect/{id}/pause', [AttendanceReviewController::class, 'pauseDetection'])->name('detect.pause');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::post('detect/{id}/resume', [AttendanceReviewController::class, 'resumeDetection'])->name('detect.resume');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::post('detect/{id}/cancel', [AttendanceReviewController::class, 'cancelDetection'])->name('detect.cancel');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::get('detect/suggest-shift', [AttendanceReviewController::class, 'suggestShiftProfile'])->name('detect.suggest_shift');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::post('detect/suggest-shift/apply', [AttendanceReviewController::class, 'applySuggestedShiftProfile'])->name('detect.suggest_shift.apply');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::get('queue/health', [AttendanceReviewController::class, 'queueHealth'])->name('queue.health');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::post('queue/process-once', [AttendanceReviewController::class, 'queueProcessOnce'])->name('queue.process_once');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::get('reviews', [AttendanceReviewController::class, 'reviewData'])->name('reviews.data');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::get('calculations', [AttendanceReviewController::class, 'calculationData'])->name('calculations.data');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::get('calculations/detail', [AttendanceReviewController::class, 'calculationDetailPage'])->name('calculations.detail');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::get('calculations/detail/export-pdf', [AttendanceReviewController::class, 'exportCalculationDetailPdf'])->name('calculations.detail.pdf');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::post('calculations/detail/send', [AttendanceReviewController::class, 'sendCalculationDetail'])->name('calculations.detail.send');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::post('calculations/detail/approve-all', [AttendanceReviewController::class, 'approveCalculationDetailAll'])->name('calculations.detail.approve_all');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::post('calculations/zip/request', [AttendanceReviewController::class, 'requestCalculationZip'])->name('calculations.zip.request');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::get('calculations/zip/{id}/status', [AttendanceReviewController::class, 'calculationZipStatus'])->name('calculations.zip.status');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::get('calculations/zip/{id}/download', [AttendanceReviewController::class, 'downloadCalculationZip'])->name('calculations.zip.download');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::get('reviews-export', [AttendanceReviewController::class, 'exportReviews'])->name('reviews.export');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::post('reviews-export/send', [AttendanceReviewController::class, 'sendReviewsExport'])->name('reviews.export.send');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::get('calculation-sheet', [AttendanceReviewController::class, 'exportCalculationSheet'])->name('calculations.sheet');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::post('reviews/{id}/approve', [AttendanceReviewController::class, 'approveReview'])->name('reviews.approve');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::get('reviews/{id}/log-options', [AttendanceReviewController::class, 'reviewLogOptions'])->name('reviews.log_options');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::post('reviews/{id}/manual-time', [AttendanceReviewController::class, 'manualUpdateReviewTime'])->name('reviews.manual_time');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::post('reviews/approve-by-date', [AttendanceReviewController::class, 'approveReviewsByDate'])->name('reviews.approve_by_date');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::post('reviews/unapprove-by-date', [AttendanceReviewController::class, 'unapproveReviewsByDate'])->name('reviews.unapprove_by_date');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::post('reviews/{id}/reject-hours', [AttendanceReviewController::class, 'rejectHolidayHours'])->name('reviews.reject_hours');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::post('reviews/{id}/offline-approve', [AttendanceReviewController::class, 'approveOfflineLogs'])->name('reviews.offline_approve');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::post('reviews/{id}/offline-reject', [AttendanceReviewController::class, 'rejectOfflineLogs'])->name('reviews.offline_reject');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::post('reviews/{id}/restore-hours', [AttendanceReviewController::class, 'restoreHolidayHours'])->name('reviews.restore_hours');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::get('reviews/{id}/histories', [AttendanceReviewController::class, 'reviewHistories'])->name('reviews.histories');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::prefix('login-reset-requests')->name('login_reset_requests.')->group(function () {`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::get('/', [AttendanceLoginResetRequestController::class, 'index'])->name('index');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::get('data', [AttendanceLoginResetRequestController::class, 'data'])->name('data');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::put('{id}/status', [AttendanceLoginResetRequestController::class, 'updateStatus'])->name('update_status');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::prefix('attendance-review-v2')->name('attendance_review_v2.')->group(function () {`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::get('/', [AttendanceReviewV2Controller::class, 'index'])->name('index');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::get('summary', [AttendanceReviewV2Controller::class, 'summaryPage'])->name('summary');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::get('summary-data', [AttendanceReviewV2Controller::class, 'summaryData'])->name('summary.data');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::get('summary-export-excel', [AttendanceReviewV2Controller::class, 'exportSummaryExcel'])->name('summary.export_excel');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::get('summary-export-pdf', [AttendanceReviewV2Controller::class, 'exportSummaryPdf'])->name('summary.export_pdf');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::post('discipline/check-profile', [AttendanceReviewV2Controller::class, 'disciplineCheckProfile'])->name('discipline.check_profile');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::post('discipline/issue-warning-letter', [AttendanceReviewV2Controller::class, 'disciplineIssueWarningLetter'])->name('discipline.issue_warning_letter');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::post('discipline/warning-letter/{id}/revoke', [AttendanceReviewV2Controller::class, 'disciplineRevokeWarningLetter'])->name('discipline.warning_letter_revoke');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::get('discipline/warning-letter/{id}/pdf', [AttendanceReviewV2Controller::class, 'disciplineWarningLetterPdf'])->name('discipline.warning_letter_pdf');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::post('discipline/settings', [AttendanceReviewV2Controller::class, 'updateDisciplineSettings'])->name('discipline.settings.update');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::post('v2/rules/simulate', [AttendanceReviewV2Controller::class, 'v2RuleSimulate'])->name('v2.rules.simulate');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::get('refs', [AttendanceReviewV2Controller::class, 'refs'])->name('refs');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::get('refs/profil-bulans', [AttendanceReviewV2Controller::class, 'profilBulanOptions'])->name('refs.profil_bulans');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::get('logs', [AttendanceReviewV2Controller::class, 'logData'])->name('logs.data');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::get('logs/unmapped', [AttendanceReviewV2Controller::class, 'unmappedLogData'])->name('logs.unmapped');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::post('logs', [AttendanceReviewV2Controller::class, 'storeLog'])->name('logs.store');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::post('logs/import-machine', [AttendanceReviewV2Controller::class, 'queueImportMachineLog'])->name('logs.import_machine');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::post('logs/test-public-api', [AttendanceReviewV2Controller::class, 'testPublicApiConnection'])->name('logs.test_public_api');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::post('logs/preview-public-api', [AttendanceReviewV2Controller::class, 'previewPublicApiLog'])->name('logs.preview_public_api');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::post('logs/sync-public-api', [AttendanceReviewV2Controller::class, 'queueSyncPublicApiLog'])->name('logs.sync_public_api');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::get('logs/jobs/{id}/status', [AttendanceReviewV2Controller::class, 'logJobStatus'])->name('logs.job.status');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::post('logs/repair-mapping', [AttendanceReviewV2Controller::class, 'repairLogMapping'])->name('logs.repair_mapping');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::delete('logs/{id}', [AttendanceReviewV2Controller::class, 'destroyLog'])->name('logs.destroy');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::post('detect', [AttendanceReviewV2Controller::class, 'runDetection'])->name('detect');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::post('rebuild-planning', [AttendanceReviewV2Controller::class, 'rebuildPlanning'])->name('rebuild_planning');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::get('rebuild/{id}/status', [AttendanceReviewV2Controller::class, 'rebuildStatus'])->name('rebuild.status');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::get('detect/{id}/status', [AttendanceReviewV2Controller::class, 'detectionStatus'])->name('detect.status');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::post('detect/{id}/pause', [AttendanceReviewV2Controller::class, 'pauseDetection'])->name('detect.pause');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::post('detect/{id}/resume', [AttendanceReviewV2Controller::class, 'resumeDetection'])->name('detect.resume');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::post('detect/{id}/cancel', [AttendanceReviewV2Controller::class, 'cancelDetection'])->name('detect.cancel');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::get('detect/suggest-shift', [AttendanceReviewV2Controller::class, 'suggestShiftProfile'])->name('detect.suggest_shift');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::post('detect/suggest-shift/apply', [AttendanceReviewV2Controller::class, 'applySuggestedShiftProfile'])->name('detect.suggest_shift.apply');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::get('queue/health', [AttendanceReviewV2Controller::class, 'queueHealth'])->name('queue.health');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::post('queue/process-once', [AttendanceReviewV2Controller::class, 'queueProcessOnce'])->name('queue.process_once');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::get('reviews', [AttendanceReviewV2Controller::class, 'reviewData'])->name('reviews.data');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::get('calculations', [AttendanceReviewV2Controller::class, 'calculationData'])->name('calculations.data');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::get('calculations/detail', [AttendanceReviewV2Controller::class, 'calculationDetailPage'])->name('calculations.detail');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::get('calculations/detail/export-pdf', [AttendanceReviewV2Controller::class, 'exportCalculationDetailPdf'])->name('calculations.detail.pdf');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::post('calculations/detail/send', [AttendanceReviewV2Controller::class, 'sendCalculationDetail'])->name('calculations.detail.send');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::post('calculations/detail/approve-all', [AttendanceReviewV2Controller::class, 'approveCalculationDetailAll'])->name('calculations.detail.approve_all');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::post('calculations/zip/request', [AttendanceReviewV2Controller::class, 'requestCalculationZip'])->name('calculations.zip.request');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::get('calculations/zip/{id}/status', [AttendanceReviewV2Controller::class, 'calculationZipStatus'])->name('calculations.zip.status');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::get('calculations/zip/{id}/download', [AttendanceReviewV2Controller::class, 'downloadCalculationZip'])->name('calculations.zip.download');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::get('reviews-export', [AttendanceReviewV2Controller::class, 'exportReviews'])->name('reviews.export');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::post('reviews-export/send', [AttendanceReviewV2Controller::class, 'sendReviewsExport'])->name('reviews.export.send');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::get('calculation-sheet', [AttendanceReviewV2Controller::class, 'exportCalculationSheet'])->name('calculations.sheet');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::post('reviews/{id}/approve', [AttendanceReviewV2Controller::class, 'approveReview'])->name('reviews.approve');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::get('reviews/{id}/log-options', [AttendanceReviewV2Controller::class, 'reviewLogOptions'])->name('reviews.log_options');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::post('reviews/{id}/manual-time', [AttendanceReviewV2Controller::class, 'manualUpdateReviewTime'])->name('reviews.manual_time');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::post('reviews/bulk-spl-shortcut', [AttendanceReviewV2Controller::class, 'bulkCreateAutomaticSpl'])->name('reviews.bulk_spl_shortcut');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::post('reviews/auto-ot-holiday-weekend-spl', [AttendanceReviewV2Controller::class, 'bulkCreateAutoOtHolidayWeekendSpl'])->name('reviews.auto_ot_holiday_weekend_spl');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::post('reviews/approve-by-date', [AttendanceReviewV2Controller::class, 'approveReviewsByDate'])->name('reviews.approve_by_date');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::post('reviews/unapprove-by-date', [AttendanceReviewV2Controller::class, 'unapproveReviewsByDate'])->name('reviews.unapprove_by_date');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::post('reviews/{id}/reject-hours', [AttendanceReviewV2Controller::class, 'rejectHolidayHours'])->name('reviews.reject_hours');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::post('reviews/{id}/offline-approve', [AttendanceReviewV2Controller::class, 'approveOfflineLogs'])->name('reviews.offline_approve');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::post('reviews/{id}/offline-reject', [AttendanceReviewV2Controller::class, 'rejectOfflineLogs'])->name('reviews.offline_reject');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::post('reviews/{id}/restore-hours', [AttendanceReviewV2Controller::class, 'restoreHolidayHours'])->name('reviews.restore_hours');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::get('reviews/{id}/histories', [AttendanceReviewV2Controller::class, 'reviewHistories'])->name('reviews.histories');`  
  File: `app/Modules/AttendanceReview/routes/web.php`
- `Route::prefix('api/v1/attendance')`  
  File: `app/Modules/AttendanceReview/routes/api.php`
- `Route::post('login', [AttendanceIngestApiController::class, 'login'])->name('login');`  
  File: `app/Modules/AttendanceReview/routes/api.php`
- `Route::post('employee-login', [AttendanceIngestApiController::class, 'employeeLogin'])->name('employee_login');`  
  File: `app/Modules/AttendanceReview/routes/api.php`
- `Route::post('reset-login-request', [AttendanceIngestApiController::class, 'resetLoginRequest'])->name('reset_login_request');`  
  File: `app/Modules/AttendanceReview/routes/api.php`
- `Route::post('logout', [AttendanceIngestApiController::class, 'logout'])->name('logout');`  
  File: `app/Modules/AttendanceReview/routes/api.php`
- `Route::get('request-data', [AttendanceIngestApiController::class, 'requestData'])->name('request_data');`  
  File: `app/Modules/AttendanceReview/routes/api.php`
- `Route::post('checkin', [AttendanceIngestApiController::class, 'checkin'])`  
  File: `app/Modules/AttendanceReview/routes/api.php`
- `Route::post('checkout', [AttendanceIngestApiController::class, 'checkout'])`  
  File: `app/Modules/AttendanceReview/routes/api.php`
- `Route::get('report', [AttendanceIngestApiController::class, 'report'])->name('report');`  
  File: `app/Modules/AttendanceReview/routes/api.php`
- `Route::get('report-pdf', [AttendanceIngestApiController::class, 'reportPdf'])->name('report_pdf');`  
  File: `app/Modules/AttendanceReview/routes/api.php`
- `Route::get('notifications', [AttendanceIngestApiController::class, 'notifications'])->name('notifications');`  
  File: `app/Modules/AttendanceReview/routes/api.php`
- `Route::get('leave-requests', [AttendanceEmployeeApiController::class, 'leaveList'])->name('leave_list');`  
  File: `app/Modules/AttendanceReview/routes/api.php`
- `Route::post('leave-requests', [AttendanceEmployeeApiController::class, 'leaveSubmit'])->name('leave_submit');`  
  File: `app/Modules/AttendanceReview/routes/api.php`
- `Route::post('leave-requests/{id}/submit', [AttendanceEmployeeApiController::class, 'leaveResubmit'])->name('leave_resubmit');`  
  File: `app/Modules/AttendanceReview/routes/api.php`
- `Route::get('spl-requests', [AttendanceEmployeeApiController::class, 'splList'])->name('spl_list');`  
  File: `app/Modules/AttendanceReview/routes/api.php`
- `Route::post('spl-requests', [AttendanceEmployeeApiController::class, 'splSubmit'])->name('spl_submit');`  
  File: `app/Modules/AttendanceReview/routes/api.php`
- `Route::post('spl-requests/{id}/submit', [AttendanceEmployeeApiController::class, 'splResubmit'])->name('spl_resubmit');`  
  File: `app/Modules/AttendanceReview/routes/api.php`
- `Route::get('warning-letters', [AttendanceEmployeeApiController::class, 'warningLetters'])->name('warning_letters');`  
  File: `app/Modules/AttendanceReview/routes/api.php`
- `Route::get('salary-history', [AttendanceEmployeeApiController::class, 'salaryHistory'])->name('salary_history');`  
  File: `app/Modules/AttendanceReview/routes/api.php`
- `Route::get('salary-slip', [AttendanceEmployeeApiController::class, 'salarySlip'])->name('salary_slip');`  
  File: `app/Modules/AttendanceReview/routes/api.php`
- `Route::get('profile-assignment', [AttendanceEmployeeApiController::class, 'profileAssignment'])->name('profile_assignment');`  
  File: `app/Modules/AttendanceReview/routes/api.php`
- `Route::get('site-assignment', [AttendanceEmployeeApiController::class, 'siteAssignment'])->name('site_assignment');`  
  File: `app/Modules/AttendanceReview/routes/api.php`
- `Route::get('profile-photo-status', [AttendanceEmployeeApiController::class, 'profilePhotoStatus'])->name('profile_photo_status');`  
  File: `app/Modules/AttendanceReview/routes/api.php`
- `Route::get('profile-photo-file', [AttendanceEmployeeApiController::class, 'profilePhotoFile'])->name('profile_photo_file');`  
  File: `app/Modules/AttendanceReview/routes/api.php`
- `Route::post('profile-photo', [AttendanceEmployeeApiController::class, 'updateProfilePhoto'])->name('profile_photo_update');`  
  File: `app/Modules/AttendanceReview/routes/api.php`
- `Route::get('template', [AttendanceIngestApiController::class, 'template'])->name('template');`  
  File: `app/Modules/AttendanceReview/routes/api.php`
- `Route::post('ingest', [AttendanceIngestApiController::class, 'ingest'])`  
  File: `app/Modules/AttendanceReview/routes/api.php`
- `Route::post('ingest-bulk', [AttendanceIngestApiController::class, 'ingestBulk'])`  
  File: `app/Modules/AttendanceReview/routes/api.php`
- `Route::post('ingest-csv', [AttendanceIngestApiController::class, 'ingestCsv'])`  
  File: `app/Modules/AttendanceReview/routes/api.php`
- `Route::post('ingest-x100c', [AttendanceIngestApiController::class, 'ingestX100c'])`  
  File: `app/Modules/AttendanceReview/routes/api.php`

## Controllers

- `app/Modules/AttendanceReview/Http/Controllers/AttendanceEmployeeApiController.php`
- `app/Modules/AttendanceReview/Http/Controllers/AttendanceIngestApiController.php`
- `app/Modules/AttendanceReview/Http/Controllers/AttendanceLoginResetRequestController.php`
- `app/Modules/AttendanceReview/Http/Controllers/AttendanceMobileNotificationSettingController.php`
- `app/Modules/AttendanceReview/Http/Controllers/AttendanceMobileSessionController.php`
- `app/Modules/AttendanceReview/Http/Controllers/AttendanceMorningCheckinReportController.php`
- `app/Modules/AttendanceReview/Http/Controllers/AttendanceReviewController.php`
- `app/Modules/AttendanceReview/Http/Controllers/AttendanceReviewV2Controller.php`
- `app/Modules/AttendanceReview/Http/Controllers/AttendanceSiteAssignmentController.php`

## Models

- `app/Modules/AttendanceReview/Models/AttendanceDailyReviewHistoryModel.php`
- `app/Modules/AttendanceReview/Models/AttendanceDailyReviewModel.php`
- `app/Modules/AttendanceReview/Models/AttendanceExportJobModel.php`
- `app/Modules/AttendanceReview/Models/AttendanceLogModel.php`
- `app/Modules/AttendanceReview/Models/AttendanceLoginResetRequestModel.php`
- `app/Modules/AttendanceReview/Models/AttendanceMobileSessionModel.php`
- `app/Modules/AttendanceReview/Models/AttendanceMobileUserControlModel.php`
- `app/Modules/AttendanceReview/Models/AttendanceSiteAssignmentModel.php`
- `app/Modules/AttendanceReview/Models/AttendanceSiteModel.php`
- `app/Modules/AttendanceReview/Models/AttendanceWarningLetterModel.php`

## Views

- `app/Modules/AttendanceReview/Resources/views/calculation_detail.blade.php`
- `app/Modules/AttendanceReview/Resources/views/exports/calculation_detail_pdf.blade.php`
- `app/Modules/AttendanceReview/Resources/views/exports/calculation_sheet.blade.php`
- `app/Modules/AttendanceReview/Resources/views/exports/reviews_pdf.blade.php`
- `app/Modules/AttendanceReview/Resources/views/exports/summary_pdf.blade.php`
- `app/Modules/AttendanceReview/Resources/views/exports/warning_letter_pdf.blade.php`
- `app/Modules/AttendanceReview/Resources/views/index.blade.php`
- `app/Modules/AttendanceReview/Resources/views/index_v2.blade.php`
- `app/Modules/AttendanceReview/Resources/views/login_reset_requests.blade.php`
- `app/Modules/AttendanceReview/Resources/views/mobile_notification_settings.blade.php`
- `app/Modules/AttendanceReview/Resources/views/mobile_sessions.blade.php`
- `app/Modules/AttendanceReview/Resources/views/morning_checkin_report.blade.php`
- `app/Modules/AttendanceReview/Resources/views/site_assignments.blade.php`
- `app/Modules/AttendanceReview/Resources/views/summary.blade.php`
- `app/Modules/AttendanceReview/Resources/views/summary_v2.blade.php`

## Database / Migration

### `attendance_logs`
- Actions: `create`, `table`
- Columns: 14
  - `karyawan_id:foreignId`
  - `employee_code:string`
  - `log_at:dateTime`
  - `log_type:enum`
  - `source:string`
  - `device_code:string`
  - `notes:text`
  - `is_active:boolean`
  - `karyawan_id:index`
  - `employee_code:index`
  - `finger_id:string`
  - `finger_id:index`
  - `att_logs_finger_logat_idx:dropIndex`
  - `finger_id:dropColumn`
- Migrations:
  - `app/Modules/AttendanceReview/Database/migrations/2026_02_24_170000_create_attendance_logs_table.php`
  - `app/Modules/AttendanceReview/Database/migrations/2026_02_26_120100_add_finger_id_to_attendance_logs_table.php`
### `attendance_daily_reviews`
- Actions: `create`
- Columns: 22
  - `profil_bulan_id:unsignedBigInteger`
  - `karyawan_id:unsignedBigInteger`
  - `work_date:date`
  - `planned_profil_jam_kerja_id:unsignedBigInteger`
  - `planned_shift_start:dateTime`
  - `planned_shift_end:dateTime`
  - `is_cross_day:boolean`
  - `checkin_at:dateTime`
  - `checkout_at:dateTime`
  - `suggested_checkin_at:dateTime`
  - `suggested_checkout_at:dateTime`
  - `status:enum`
  - `confidence_score:decimal`
  - `reason:text`
  - `is_approved:boolean`
  - `approved_by:unsignedBigInteger`
  - `approved_at:dateTime`
  - `profil_bulan_id:unique`
  - `profil_bulan_id:foreign`
  - `karyawan_id:foreign`
  - `planned_profil_jam_kerja_id:foreign`
  - `approved_by:foreign`
- Migrations:
  - `app/Modules/AttendanceReview/Database/migrations/2026_02_24_170100_create_attendance_daily_reviews_table.php`
### `attendance_daily_review_histories`
- Actions: `create`
- Columns: 11
  - `attendance_daily_review_id:unsignedBigInteger`
  - `action:string`
  - `before_data:json`
  - `after_data:json`
  - `notes:text`
  - `changed_by:unsignedBigInteger`
  - `changed_at:dateTime`
  - `attendance_daily_review_id:index`
  - `action:index`
  - `attendance_daily_review_id:foreign`
  - `changed_by:foreign`
- Migrations:
  - `app/Modules/AttendanceReview/Database/migrations/2026_02_24_170300_create_attendance_daily_review_histories_table.php`
### `attendance_export_jobs`
- Actions: `create`
- Columns: 14
  - `request_no:string`
  - `user_id:unsignedBigInteger`
  - `profil_bulan_id:unsignedBigInteger`
  - `filters:json`
  - `status:string`
  - `total_users:unsignedInteger`
  - `processed_users:unsignedInteger`
  - `zip_path:string`
  - `error_message:text`
  - `started_at:timestamp`
  - `finished_at:timestamp`
  - `status:index`
  - `profil_bulan_id:foreign`
  - `user_id:foreign`
- Migrations:
  - `app/Modules/AttendanceReview/Database/migrations/2026_02_26_230000_create_attendance_export_jobs_table.php`
### `attendance_warning_letters`
- Actions: `create`, `table`
- Columns: 29
  - `profil_bulan_id:unsignedBigInteger`
  - `karyawan_id:unsignedBigInteger`
  - `sp_level:unsignedTinyInteger`
  - `letter_no:string`
  - `period_start:date`
  - `period_end:date`
  - `alpha_count:unsignedInteger`
  - `telat_count:unsignedInteger`
  - `violation_score:unsignedInteger`
  - `payload:json`
  - `notes:text`
  - `status:string`
  - `issued_by:unsignedBigInteger`
  - `issued_at:timestamp`
  - `revoked_by:unsignedBigInteger`
  - `revoked_at:timestamp`
  - `revoke_reason:text`
  - `profil_bulan_id:unique`
  - `karyawan_id:index`
  - `status:index`
  - `letter_no:index`
  - `profil_bulan_id:foreign`
  - `karyawan_id:foreign`
  - `issued_by:foreign`
  - `revoked_by:foreign`
  - `valid_days:unsignedSmallInteger`
  - `valid_until:date`
  - `valid_until:dropColumn`
  - `valid_days:dropColumn`
- Migrations:
  - `app/Modules/AttendanceReview/Database/migrations/2026_03_10_090000_create_attendance_warning_letters_table.php`
  - `app/Modules/AttendanceReview/Database/migrations/2026_03_10_235500_add_validity_to_attendance_warning_letters_table.php`
