diff --git a/backend/src/routes/cashup.js b/backend/src/routes/cashup.js
index 06f357d..5e89756 100644
--- a/backend/src/routes/cashup.js
+++ b/backend/src/routes/cashup.js
@@ -117,6 +117,19 @@ export async function cashupRoutes(app) {
return { message: 'Deleted' }
})
+ // POST /api/cashup/:id/unfinalise (admin only)
+ app.post('/api/cashup/:id/unfinalise', async (req, reply) => {
+ if (!req.user.is_admin) return reply.status(403).send({ error: 'Admin only' })
+ const { rows } = await pool.query('SELECT status FROM cash_ups WHERE id = $1', [req.params.id])
+ if (!rows.length) return reply.status(404).send({ error: 'Not found' })
+ if (rows[0].status !== 'final') return reply.status(409).send({ error: 'Not finalised' })
+ await pool.query(
+ 'UPDATE cash_ups SET status=$1, submitted_at=NULL, submitted_by=NULL WHERE id=$2',
+ ['draft', req.params.id]
+ )
+ return { message: 'Cash up reverted to draft.' }
+ })
+
// POST /api/cashup/bulk-finalize (requires cashup:finalise)
app.post('/api/cashup/bulk-finalize', async (req, reply) => {
if (!hasCap(req, 'finalise')) return reply.status(403).send({ error: 'Missing capability: finalise' })
diff --git a/frontend/src/pages/History.tsx b/frontend/src/pages/History.tsx
index caf21e0..9ca3dec 100644
--- a/frontend/src/pages/History.tsx
+++ b/frontend/src/pages/History.tsx
@@ -56,6 +56,13 @@ export function History({ user }: { user: User }) {
load()
}
+ async function unfinalise(id: number) {
+ if (!confirm('Revert this cash up to draft so it can be edited?')) return
+ await api.post(`/cashup/${id}/unfinalise`, {})
+ flash('Reverted to draft.')
+ load()
+ }
+
async function bulkFinalize() {
if (!selected.size) return
if (!confirm(`Finalise ${selected.size} draft(s)?`)) return
@@ -155,6 +162,9 @@ export function History({ user }: { user: User }) {
{canFinalise && row.status === 'draft' && (
deleteDraft(row.id)}>Delete
)}
+ {user.is_admin && row.status === 'final' && (
+ unfinalise(row.id)}>Unfinalise
+ )}
))}