Fix math logic for timeline
This commit is contained in:
@@ -260,6 +260,33 @@ function getPriorActivePoints(employeeId, incidentDate) {
|
||||
return row ? row.pts : 0;
|
||||
}
|
||||
|
||||
// Helper: after a back-dated insert, refresh snapshots on any existing
|
||||
// violations whose 90-day prior-window now includes the new (earlier)
|
||||
// incident_date. Without this, their PDFs would still show the pre-backdate
|
||||
// "Prior Active Points" and miss the inserted earlier violation.
|
||||
// Snapshots are still immutable w.r.t. negate/restore — only timeline-
|
||||
// rewriting events (back-dated inserts) trigger a refresh.
|
||||
function recomputeSnapshotsAfter(employeeId, incidentDate) {
|
||||
const affected = db.prepare(`
|
||||
SELECT id, incident_date, prior_active_points
|
||||
FROM violations
|
||||
WHERE employee_id = ?
|
||||
AND incident_date > ?
|
||||
AND incident_date <= DATE(?, '+90 days')
|
||||
`).all(employeeId, incidentDate, incidentDate);
|
||||
|
||||
const updateStmt = db.prepare('UPDATE violations SET prior_active_points = ? WHERE id = ?');
|
||||
const changes = [];
|
||||
for (const v of affected) {
|
||||
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 changes;
|
||||
}
|
||||
|
||||
// POST new violation
|
||||
app.post('/api/violations', (req, res) => {
|
||||
const {
|
||||
@@ -275,32 +302,51 @@ app.post('/api/violations', (req, res) => {
|
||||
}
|
||||
|
||||
const ptsInt = parseInt(points);
|
||||
const priorPts = getPriorActivePoints(employee_id, incident_date);
|
||||
|
||||
const result = db.prepare(`
|
||||
INSERT INTO violations (
|
||||
employee_id, violation_type, violation_name, category,
|
||||
points, incident_date, incident_time, location,
|
||||
details, submitted_by, witness_name,
|
||||
prior_active_points,
|
||||
acknowledged_by, acknowledged_date,
|
||||
amount
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`).run(
|
||||
employee_id, violation_type, violation_name || violation_type,
|
||||
category || 'General', ptsInt, incident_date,
|
||||
incident_time || null, location || null,
|
||||
details || null, submitted_by || null, witness_name || null,
|
||||
priorPts,
|
||||
acknowledged_by || null, acknowledged_date || null,
|
||||
amount || null
|
||||
);
|
||||
// Insert + downstream snapshot refresh run in a single transaction so a
|
||||
// failed recompute can't leave the system with a new violation and stale
|
||||
// sibling snapshots.
|
||||
const insertTxn = db.transaction(() => {
|
||||
const priorPts = getPriorActivePoints(employee_id, incident_date);
|
||||
const result = db.prepare(`
|
||||
INSERT INTO violations (
|
||||
employee_id, violation_type, violation_name, category,
|
||||
points, incident_date, incident_time, location,
|
||||
details, submitted_by, witness_name,
|
||||
prior_active_points,
|
||||
acknowledged_by, acknowledged_date,
|
||||
amount
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`).run(
|
||||
employee_id, violation_type, violation_name || violation_type,
|
||||
category || 'General', ptsInt, incident_date,
|
||||
incident_time || null, location || null,
|
||||
details || null, submitted_by || null, witness_name || null,
|
||||
priorPts,
|
||||
acknowledged_by || null, acknowledged_date || null,
|
||||
amount || null
|
||||
);
|
||||
const refreshed = recomputeSnapshotsAfter(employee_id, incident_date);
|
||||
return { id: result.lastInsertRowid, refreshed };
|
||||
});
|
||||
|
||||
audit('violation_created', 'violation', result.lastInsertRowid, submitted_by, {
|
||||
const { id: newId, refreshed } = insertTxn();
|
||||
|
||||
audit('violation_created', 'violation', newId, submitted_by, {
|
||||
employee_id, violation_type, points: ptsInt, incident_date,
|
||||
});
|
||||
|
||||
res.status(201).json({ id: result.lastInsertRowid });
|
||||
// Back-dated insert: log the snapshot refresh so the audit trail explains
|
||||
// why downstream violations' PDFs now show different "Prior Active Points".
|
||||
if (refreshed.length > 0) {
|
||||
audit('violation_snapshots_recomputed', 'violation', newId, submitted_by, {
|
||||
reason: 'backdated_insert',
|
||||
trigger_incident_date: incident_date,
|
||||
affected: refreshed,
|
||||
});
|
||||
}
|
||||
|
||||
res.status(201).json({ id: newId });
|
||||
});
|
||||
|
||||
// ── Violation Amendment (edit) ───────────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user