const express = require('express'); const { fcTable } = require('../lib/utils'); module.exports = function(pool) { const router = express.Router(); // list log entries for a version, newest first, with row counts and value/units totals router.get('/versions/:id/log', async (req, res) => { const versionId = parseInt(req.params.id); try { const verResult = await pool.query( `SELECT v.*, s.tname, s.id AS source_id FROM pf.version v JOIN pf.source s ON s.id = v.source_id WHERE v.id = $1`, [versionId] ); if (!verResult.rows.length) return res.status(404).json({ error: 'Version not found' }); const { tname, source_id } = verResult.rows[0]; const table = fcTable(tname, versionId); const colMeta = await pool.query( `SELECT cname, role FROM pf.col_meta WHERE source_id = $1 AND role IN ('value', 'units')`, [source_id] ); const valueCol = colMeta.rows.find(c => c.role === 'value')?.cname; const unitsCol = colMeta.rows.find(c => c.role === 'units')?.cname; const aggCols = [ `count(f.pf_id)::int AS row_count`, valueCol ? `sum(f."${valueCol}")::float8 AS value_total` : `NULL::float8 AS value_total`, unitsCol ? `sum(f."${unitsCol}")::float8 AS units_total` : `NULL::float8 AS units_total` ].join(', '); const result = await pool.query(` SELECT l.*, ${aggCols}, $2::text AS value_col, $3::text AS units_col FROM pf.log l LEFT JOIN ${table} f ON f.pf_logid = l.id WHERE l.version_id = $1 GROUP BY l.id ORDER BY l.id DESC `, [versionId, valueCol || null, unitsCol || null]); res.json(result.rows); } catch (err) { console.error(err); res.status(err.status || 500).json({ error: err.message }); } }); // undo a log entry — delete all fc rows with this logid, then delete the log entry router.delete('/log/:logid', async (req, res) => { const logId = parseInt(req.params.logid); try { const logResult = await pool.query(` SELECT l.*, v.status, s.tname, v.id AS version_id FROM pf.log l JOIN pf.version v ON v.id = l.version_id JOIN pf.source s ON s.id = v.source_id WHERE l.id = $1 `, [logId]); if (!logResult.rows.length) return res.status(404).json({ error: 'Log entry not found' }); const log = logResult.rows[0]; if (log.status === 'closed') return res.status(403).json({ error: 'Version is closed' }); const table = fcTable(log.tname, log.version_id); const client = await pool.connect(); try { await client.query('BEGIN'); const deleted = await client.query( `DELETE FROM ${table} WHERE pf_logid = $1 RETURNING pf_id`, [logId] ); await client.query('DELETE FROM pf.log WHERE id = $1', [logId]); await client.query('COMMIT'); res.json({ rows_deleted: deleted.rowCount, pf_ids: deleted.rows.map(r => r.pf_id) }); } catch (err) { await client.query('ROLLBACK'); throw err; } finally { client.release(); } } catch (err) { console.error(err); res.status(err.status || 500).json({ error: err.message }); } }); // update the note on a log entry router.patch('/log/:logid', async (req, res) => { const logId = parseInt(req.params.logid); const { note } = req.body; try { const result = await pool.query( `UPDATE pf.log SET note = $1 WHERE id = $2 RETURNING *`, [note ?? null, logId] ); if (!result.rows.length) return res.status(404).json({ error: 'Log entry not found' }); res.json(result.rows[0]); } catch (err) { console.error(err); res.status(err.status || 500).json({ error: err.message }); } }); return router; };