diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 2499a1e..07025d9 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -24,7 +24,8 @@ "mcp__plugin_supabase_supabase__execute_sql", "Bash(sed -n '88,100p' src/pages/Tasks.jsx)", "Bash(sed -n '570,600p' src/pages/Tasks.jsx)", - "Bash(git commit -q -m 'fix: hide counter on All tab in projects table *)" + "Bash(git commit -q -m 'fix: hide counter on All tab in projects table *)", + "Bash(git commit -q -m 'fix: guard task status updates on invoice lifecycle transitions *)" ] } } diff --git a/src/pages/team/TeamCreateInvoice.jsx b/src/pages/team/TeamCreateInvoice.jsx index 5222046..5c86a59 100644 --- a/src/pages/team/TeamCreateInvoice.jsx +++ b/src/pages/team/TeamCreateInvoice.jsx @@ -235,7 +235,7 @@ export default function CreateInvoice() { const taskIds = [...new Set(validItems.filter(i => i.task_id && !i.submission_id).map(i => i.task_id))]; if (taskIds.length > 0) { - const { error: taskError } = await supabase.from('tasks').update({ invoiced: true, status: 'invoiced' }).in('id', taskIds); + const { error: taskError } = await supabase.from('tasks').update({ invoiced: true, status: 'invoiced' }).in('id', taskIds).eq('status', 'client_approved'); if (taskError) throw taskError; } diff --git a/src/pages/team/TeamInvoiceDetail.jsx b/src/pages/team/TeamInvoiceDetail.jsx index ee348c2..b8eb15b 100644 --- a/src/pages/team/TeamInvoiceDetail.jsx +++ b/src/pages/team/TeamInvoiceDetail.jsx @@ -83,7 +83,10 @@ export function TeamInvoiceDetailPanel({ const taskIds = (freshItems || []).filter(i => i.task_id && !i.submission_id).map(i => i.task_id); if (taskIds.length > 0) { const newTaskStatus = status === 'paid' ? 'paid' : status === 'sent' ? 'invoiced' : 'client_approved'; - await supabase.from('tasks').update({ status: newTaskStatus }).in('id', taskIds); + // Guard: only update tasks still at the expected prior status so tasks + // that have moved on to a new revision cycle aren't overwritten. + const guardStatus = status === 'paid' ? 'invoiced' : status === 'sent' ? 'client_approved' : 'paid'; + await supabase.from('tasks').update({ status: newTaskStatus }).in('id', taskIds).eq('status', guardStatus); } if (status === 'paid') { try { diff --git a/src/pages/team/TeamInvoices.jsx b/src/pages/team/TeamInvoices.jsx index aeb79a3..c75c688 100644 --- a/src/pages/team/TeamInvoices.jsx +++ b/src/pages/team/TeamInvoices.jsx @@ -410,7 +410,7 @@ export default function Invoices() { const validItems = invItems.filter(i => i.description); if (validItems.length > 0) await supabase.from('invoice_items').insert(validItems.map(it => ({ invoice_id: invoice.id, task_id: it.task_id || null, submission_id: it.submission_id || null, description: it.description, quantity: Number(it.quantity) || 1, unit_price: Number(it.unit_price) || 0 }))); const taskIds = [...new Set(validItems.filter(i => i.task_id && !i.submission_id).map(i => i.task_id))]; - if (taskIds.length > 0) await supabase.from('tasks').update({ invoiced: true, status: 'invoiced' }).in('id', taskIds); + if (taskIds.length > 0) await supabase.from('tasks').update({ invoiced: true, status: 'invoiced' }).in('id', taskIds).eq('status', 'client_approved'); const subIds = [...new Set(validItems.filter(i => i.submission_id).map(i => i.submission_id))]; if (subIds.length > 0) await supabase.from('submissions').update({ invoiced: true }).in('id', subIds); if (status === 'sent') {