backfill button and usage
Build and Push Docker Image / build (push) Successful in 16s

This commit is contained in:
2026-05-19 09:29:41 -05:00
parent 6ddc09aa71
commit 2d4920bd15
7 changed files with 171 additions and 5 deletions
+52
View File
@@ -287,6 +287,58 @@ function recomputeSnapshotsAfter(employeeId, incidentDate) {
return changes;
}
// Helper: rebuild prior_active_points for every violation belonging to an
// employee, regardless of negate state. Used by the manual backfill button
// to repair snapshots after a back-dated insert that happened under older
// code (before recomputeSnapshotsAfter existed) or any case where the
// per-row snapshot has drifted from current data.
function recomputeAllSnapshotsForEmployee(employeeId) {
const rows = db.prepare(`
SELECT id, incident_date, prior_active_points
FROM violations
WHERE employee_id = ?
ORDER BY incident_date ASC
`).all(employeeId);
const updateStmt = db.prepare('UPDATE violations SET prior_active_points = ? WHERE id = ?');
const changes = [];
for (const v of rows) {
const newPrior = getPriorActivePoints(employeeId, v.incident_date);
if (newPrior !== v.prior_active_points) {
updateStmt.run(newPrior, v.id);
changes.push({ id: v.id, incident_date: v.incident_date, old: v.prior_active_points, new: newPrior });
}
}
return { scanned: rows.length, changes };
}
// POST /api/employees/:id/recompute-snapshots
// Manual backfill — rebuild prior_active_points for every violation on this
// employee. Use after a back-dated insert under older code left downstream
// PDFs showing stale "Prior Active Points".
app.post('/api/employees/:id/recompute-snapshots', (req, res) => {
const empId = parseInt(req.params.id);
const emp = db.prepare('SELECT id, name FROM employees WHERE id = ?').get(empId);
if (!emp) return res.status(404).json({ error: 'Employee not found' });
const result = db.transaction(() => recomputeAllSnapshotsForEmployee(empId))();
if (result.changes.length > 0) {
audit('violation_snapshots_recomputed', 'employee', empId, req.body?.performed_by, {
reason: 'manual_backfill',
scanned: result.scanned,
affected: result.changes,
});
}
res.json({
success: true,
scanned: result.scanned,
updated: result.changes.length,
changes: result.changes,
});
});
// POST new violation
app.post('/api/violations', (req, res) => {
const {