Files
accounted/supabase/migrations/20260325130000_pending_operations.sql
T
Jakob WennbergandClaude Opus 4.6 f3ae3cd361 feat: event log, pending operations, and MCP staging (#135)
* feat: event log, pending operations, and MCP staging

- Event log system: persist bus events to event_log table for external
  automation platforms. Batch insert for transaction.synced. Daily
  cleanup cron at 02:00 UTC.
- Pending operations: MCP write tools (categorize, create customer,
  create invoice) now stage to pending_operations instead of executing
  directly. Users review and commit/reject from /pending in the web UI.
- Granskning page: card-based review UI with expandable previews,
  commit/reject dialogs. Only shown in nav when pending ops exist.
- Commit route re-executes using core lib functions (no extension
  imports). Guards against stale state (double-commit, deleted entities).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: stage new MCP write tools after main merge

Add staging for 4 new write tools from #133:
- mark_invoice_paid, send_invoice, mark_invoice_sent,
  match_transaction_invoice
- Expand pending_operations CHECK constraint
- Add commit executors with full execution logic
- Add UI labels and generic preview component
- Remove confirm parameter from categorize (single-call staging)
- Fix UUID in pending op title (fetch transaction description)
- Hide Granskning nav when no pending ops

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: address PR review feedback

- Fix TS build error: use `select('*, customer:customers(*)')` for
  match_transaction_invoice to avoid array type inference
- Add status guard to commitSendInvoice (prevents duplicate sends)
- Replace auth.admin.getUserById with user email from session auth
- Restore optimistic lock check in commitMatchTransactionInvoice
- Fix tool description typo: expense_software → expense_office

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 10:07:36 +01:00

49 lines
2.1 KiB
SQL

-- Migration: Pending Operations
-- Staging table for MCP agent write operations that require user review.
-- Agent proposes → user reviews in web UI → commits or rejects.
-- =============================================================================
-- 1. pending_operations table
-- =============================================================================
CREATE TABLE public.pending_operations (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
operation_type TEXT NOT NULL CHECK (operation_type IN (
'categorize_transaction', 'create_customer', 'create_invoice'
)),
status TEXT NOT NULL DEFAULT 'pending' CHECK (status IN (
'pending', 'committed', 'rejected'
)),
title TEXT NOT NULL,
params JSONB NOT NULL DEFAULT '{}',
preview_data JSONB NOT NULL DEFAULT '{}',
result_data JSONB,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
resolved_at TIMESTAMPTZ,
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
-- Primary query: list pending ops for a user
CREATE INDEX idx_pending_ops_user_status ON public.pending_operations (user_id, status);
-- =============================================================================
-- 2. RLS
-- =============================================================================
ALTER TABLE public.pending_operations ENABLE ROW LEVEL SECURITY;
CREATE POLICY "pending_ops_select_own" ON public.pending_operations
FOR SELECT USING (auth.uid() = user_id);
CREATE POLICY "pending_ops_update_own" ON public.pending_operations
FOR UPDATE USING (auth.uid() = user_id);
-- No INSERT policy: writes via service role client from MCP handler
-- No DELETE policy: status transitions only (pending → committed/rejected)
-- =============================================================================
-- 3. updated_at trigger
-- =============================================================================
CREATE TRIGGER pending_operations_updated_at
BEFORE UPDATE ON public.pending_operations
FOR EACH ROW EXECUTE FUNCTION public.update_updated_at_column();