This commit is contained in:
@@ -158,6 +158,7 @@ Useful for showing the app to stakeholders without exposing live employee data.
|
||||
- PDF download for any historical violation record
|
||||
- **Notes & Flags** — free-text notes (e.g. "on PIP", "union member") with quick-add tag buttons; visible in the profile modal without affecting scoring
|
||||
- **Point Expiration Timeline** — shows when each active violation rolls off the 90-day window, with a progress bar, days-remaining countdown, and projected tier-drop indicators
|
||||
- **↻ Backfill Snapshots** button (next to the Active Violations header) — manually rebuilds the `prior_active_points` snapshot on every violation for this employee. Use after back-dating a violation under older code, or any time a regenerated PDF shows stale prior-point totals. Audit-logged as `violation_snapshots_recomputed` with reason `manual_backfill`. See [Backfilling Prior-Points Snapshots](#backfilling-prior-points-snapshots) below.
|
||||
- **Toast notifications** for all actions: negate, restore, delete, amend, PDF download, employee edit
|
||||
|
||||
### Audit Log
|
||||
@@ -208,6 +209,45 @@ Scores are computed over a **rolling 90-day window** (negated violations exclude
|
||||
- Filename: `CPAS_<EmployeeName>_<IncidentDate>.pdf`
|
||||
- PDF captures prior active points **at the time of the incident** (snapshot stored on insert)
|
||||
- **Acknowledgment rendering**: if the violation has an `acknowledged_by` value, the employee signature block on the PDF shows the recorded name and date with an "Acknowledged" badge; otherwise, blank signature lines are rendered for wet-ink signing
|
||||
- **Back-dated inserts** auto-refresh the snapshot on downstream violations whose 90-day prior window now includes the new earlier event (handled inside the insert transaction by `recomputeSnapshotsAfter()`). If a back-date happened under older code that lacked this auto-refresh, use the **↻ Backfill Snapshots** button in the Employee Profile Modal — see [Backfilling Prior-Points Snapshots](#backfilling-prior-points-snapshots).
|
||||
|
||||
---
|
||||
|
||||
## Backfilling Prior-Points Snapshots
|
||||
|
||||
Each violation stores a `prior_active_points` snapshot at insert time so its PDF always reflects the score *as it was on the incident date* (and stays stable through later negate/restore actions). Normally you never touch this column.
|
||||
|
||||
There is one situation where the snapshot can drift from current truth: a violation was back-dated *before* `recomputeSnapshotsAfter()` shipped (commit `e2c352d`), so the auto-refresh never ran on the violations that now sit inside its 90-day window. Symptom: re-downloading the PDF for the newer violation shows "Prior Active Points: 0" even though an earlier active violation clearly exists in the timeline.
|
||||
|
||||
**To fix:**
|
||||
|
||||
1. Open the affected employee's profile modal.
|
||||
2. Click **↻ Backfill Snapshots** next to the **Active Violations** header.
|
||||
3. Confirm the prompt. A toast reports `Updated X of Y snapshot(s)` or `Snapshots already up to date`.
|
||||
4. Re-download the PDFs — they now reflect the corrected prior totals.
|
||||
|
||||
**What it does, exactly:**
|
||||
|
||||
- Iterates every violation belonging to that employee (active *and* negated).
|
||||
- Recomputes each row's `prior_active_points` using the current set of non-negated violations in the 90 days before its `incident_date`.
|
||||
- Writes only the rows that actually changed and reports the diff.
|
||||
- Runs inside a single transaction.
|
||||
- Writes one `violation_snapshots_recomputed` entry to the audit log with `reason: "manual_backfill"` and the per-row before/after values.
|
||||
|
||||
**When *not* to use it:**
|
||||
|
||||
- After a negate, restore, amend, or hard delete in normal workflow. The auto-managed snapshot is correct in those cases by design (PDFs are intentionally stable through negate/restore).
|
||||
- As a routine maintenance step. It's a targeted repair tool, not a recurring task. If you find yourself reaching for it after normal back-dated inserts, file a bug — the auto-recompute should already be handling those.
|
||||
|
||||
**API endpoint:** `POST /api/employees/:id/recompute-snapshots`
|
||||
|
||||
Response shape:
|
||||
|
||||
```json
|
||||
{ "success": true, "scanned": 2, "updated": 1, "changes": [
|
||||
{ "id": 47, "incident_date": "2026-04-02", "old": 0, "new": 3 }
|
||||
]}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -222,6 +262,7 @@ Scores are computed over a **rolling 90-day window** (negated violations exclude
|
||||
| PATCH | `/api/employees/:id` | Edit name, department, supervisor, or notes |
|
||||
| PATCH | `/api/employees/:id/notes` | Save employee notes only (shorthand) |
|
||||
| POST | `/api/employees/:id/merge` | Merge duplicate employee; reassigns all violations |
|
||||
| POST | `/api/employees/:id/recompute-snapshots` | Manual backfill — rebuild `prior_active_points` on every violation for this employee. See [Backfilling Prior-Points Snapshots](#backfilling-prior-points-snapshots) |
|
||||
| GET | `/api/employees/:id/score` | Get active CPAS score for employee |
|
||||
| GET | `/api/employees/:id/expiration` | Active violation roll-off timeline with days remaining |
|
||||
| GET | `/api/employees/:id/violation-counts` | 90-day non-negated counts grouped by violation type |
|
||||
|
||||
Reference in New Issue
Block a user