# Offline Payroll Verification

This guide explains how `PayrollVerification.js` queues verification actions when the browser is offline.

`LocalSyncManager` stores `payslips` in IndexedDB and pushes edits to the API once connectivity returns. When verifying a payslip, the module attempts to call `/api/salary/verify/{id}`. If the request fails, the payslip record is updated locally with `status`, `verified_at` and `verification_notes` and marked with `pendingSync`. The next successful sync performs a `PUT /api/salary/{id}` which applies the verification server side.

## Manual Test Instructions
1. Open `views/hr/payroll_verification.php` in your browser.
2. Disable network access using your browser's dev tools.
3. Verify a pending payslip using **View/Edit** or **Verify Selected**.
4. A toast "Verification queued for sync" should appear.
5. Inspect IndexedDB and confirm the payslip has `pendingSync: true` and `status` set to `verified`.
6. Re-enable the network and run `syncManager.syncNow()` in the console or wait for background sync.
7. Check the server record to ensure the verification fields were updated and the local `pendingSync` flag cleared.

Once all payslips for a location are verified, submit the results using `POST ../api/payroll/locationSubmit/{periodId}`. The request body should contain an `employees` array with hours and overtime totals. Submitting marks the related time clocks as `locationApproved` for that payroll period.

## Limitations
- Audit logs recorded by `SalaryController::verify` are not generated when the action is queued offline.

## Real‑Time Updates
`LocalSyncManager.startWebSocketListener()` listens for server messages. The payload can include an `updates` object mapping table names to arrays of `{action, record}` entries.

```json
{
  "updates": {
    "payslips": [
      { "action": "update", "record": { "id": 5, "status": "verified" } },
      { "action": "delete", "record": { "id": 6 } }
    ]
  }
}
```

`create` and `update` actions write the record to IndexedDB while `delete` removes it. After processing a table, the manager dispatches a `tableUpdated` event whose detail includes `{ tableName, action, record }` so modules can update the affected row without performing extra lookups.
