diff --git a/server.js b/server.js index 667afec..f442834 100755 --- a/server.js +++ b/server.js @@ -70,7 +70,7 @@ app.get('/api/violations/employee/:id', (req, res) => { res.json(rows); }); -// NEW helper: compute prior_active_points at time of insert (excluding this violation) +// Helper: compute prior_active_points at time of insert function getPriorActivePoints(employeeId, incidentDate) { const row = db.prepare( `SELECT COALESCE(SUM(points),0) AS pts @@ -116,9 +116,63 @@ app.post('/api/violations', (req, res) => { res.status(201).json({ id: result.lastInsertRowid }); }); -// Negate / restore / delete endpoints unchanged ... +// ── Negate a violation ────────────────────────────────────────────────────── +app.patch('/api/violations/:id/negate', (req, res) => { + const { resolution_type, details, resolved_by } = req.body; + const id = req.params.id; -// PDF endpoint — use stored prior_active_points snapshot + const violation = db.prepare('SELECT * FROM violations WHERE id = ?').get(id); + if (!violation) return res.status(404).json({ error: 'Violation not found' }); + + // Mark negated + db.prepare('UPDATE violations SET negated = 1 WHERE id = ?').run(id); + + // Upsert resolution record + const existing = db.prepare('SELECT id FROM violation_resolutions WHERE violation_id = ?').get(id); + if (existing) { + db.prepare(` + UPDATE violation_resolutions + SET resolution_type = ?, details = ?, resolved_by = ?, created_at = datetime('now') + WHERE violation_id = ? + `).run(resolution_type || 'Resolved', details || null, resolved_by || null, id); + } else { + db.prepare(` + INSERT INTO violation_resolutions (violation_id, resolution_type, details, resolved_by) + VALUES (?, ?, ?, ?) + `).run(id, resolution_type || 'Resolved', details || null, resolved_by || null); + } + + res.json({ success: true }); +}); + +// ── Restore a negated violation ───────────────────────────────────────────── +app.patch('/api/violations/:id/restore', (req, res) => { + const id = req.params.id; + + const violation = db.prepare('SELECT * FROM violations WHERE id = ?').get(id); + if (!violation) return res.status(404).json({ error: 'Violation not found' }); + + db.prepare('UPDATE violations SET negated = 0 WHERE id = ?').run(id); + db.prepare('DELETE FROM violation_resolutions WHERE violation_id = ?').run(id); + + res.json({ success: true }); +}); + +// ── Hard delete a violation ───────────────────────────────────────────────── +app.delete('/api/violations/:id', (req, res) => { + const id = req.params.id; + + const violation = db.prepare('SELECT * FROM violations WHERE id = ?').get(id); + if (!violation) return res.status(404).json({ error: 'Violation not found' }); + + // Delete resolution first (FK safety) + db.prepare('DELETE FROM violation_resolutions WHERE violation_id = ?').run(id); + db.prepare('DELETE FROM violations WHERE id = ?').run(id); + + res.json({ success: true }); +}); + +// ── PDF endpoint ───────────────────────────────────────────────────────────── app.get('/api/violations/:id/pdf', async (req, res) => { try { const violation = db.prepare(` @@ -130,14 +184,12 @@ app.get('/api/violations/:id/pdf', async (req, res) => { if (!violation) return res.status(404).json({ error: 'Violation not found' }); - // For PDF, compute score row but pass stored prior_active_points so math is stable const active = db.prepare('SELECT * FROM active_cpas_scores WHERE employee_id = ?') .get(violation.employee_id) || { active_points: 0, violation_count: 0 }; const scoreForPdf = { - employee_id: violation.employee_id, - // snapshot at time of violation (if present); fall back to current - active_points: violation.prior_active_points != null ? violation.prior_active_points : active.active_points, + employee_id: violation.employee_id, + active_points: violation.prior_active_points != null ? violation.prior_active_points : active.active_points, violation_count: active.violation_count, };