dataflow/database/queries/status.sql
Paul Trowbridge 7c63a2ac29 Status bar, stale tracking, Pivot stack selector, stack view fixes
- Add get_status() SQL and /api/status route; load stale state on login
- Replace polling with immediate client-side stale tracking via callbacks
- Amber status bar with per-item Generate buttons for sources and stacks
- Pivot: add stack selector to view any dfv.stack view via Perspective
- Stack views: DROP CASCADE, add id to source views, per-source balance columns
- net_balance = sum(all amounts) + total_offset guarantees chase+dcard=net per row
- CLAUDE.md: document correct dedup spec (within-batch duplicates always allowed)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 09:43:10 -04:00

86 lines
3.1 KiB
PL/PgSQL

--
-- Status tracking: view_generated_at on sources and stacks
-- Cleared by triggers when definitions change; set by API when views are generated.
--
SET search_path TO dataflow, public;
-- Add view_generated_at columns
ALTER TABLE dataflow.sources ADD COLUMN IF NOT EXISTS view_generated_at TIMESTAMPTZ;
ALTER TABLE dataflow.stacks ADD COLUMN IF NOT EXISTS view_generated_at TIMESTAMPTZ;
------------------------------------------------------
-- Trigger: clear source view_generated_at when rules change
------------------------------------------------------
CREATE OR REPLACE FUNCTION dataflow.rules_changed()
RETURNS TRIGGER AS $$
BEGIN
UPDATE dataflow.sources SET view_generated_at = NULL
WHERE name = COALESCE(NEW.source_name, OLD.source_name);
RETURN NULL;
END;
$$ LANGUAGE plpgsql;
DROP TRIGGER IF EXISTS trg_rules_changed ON dataflow.rules;
CREATE TRIGGER trg_rules_changed
AFTER INSERT OR UPDATE OR DELETE ON dataflow.rules
FOR EACH ROW EXECUTE FUNCTION dataflow.rules_changed();
------------------------------------------------------
-- Trigger: clear source view_generated_at when mappings change
------------------------------------------------------
CREATE OR REPLACE FUNCTION dataflow.mappings_changed()
RETURNS TRIGGER AS $$
BEGIN
UPDATE dataflow.sources SET view_generated_at = NULL
WHERE name = COALESCE(NEW.source_name, OLD.source_name);
RETURN NULL;
END;
$$ LANGUAGE plpgsql;
DROP TRIGGER IF EXISTS trg_mappings_changed ON dataflow.mappings;
CREATE TRIGGER trg_mappings_changed
AFTER INSERT OR UPDATE OR DELETE ON dataflow.mappings
FOR EACH ROW EXECUTE FUNCTION dataflow.mappings_changed();
------------------------------------------------------
-- Trigger: clear stack view_generated_at when sources change
------------------------------------------------------
CREATE OR REPLACE FUNCTION dataflow.stack_sources_changed()
RETURNS TRIGGER AS $$
BEGIN
UPDATE dataflow.stacks SET view_generated_at = NULL
WHERE name = COALESCE(NEW.stack_name, OLD.stack_name);
RETURN NULL;
END;
$$ LANGUAGE plpgsql;
DROP TRIGGER IF EXISTS trg_stack_sources_changed ON dataflow.stack_sources;
CREATE TRIGGER trg_stack_sources_changed
AFTER INSERT OR UPDATE OR DELETE ON dataflow.stack_sources
FOR EACH ROW EXECUTE FUNCTION dataflow.stack_sources_changed();
------------------------------------------------------
-- Function: get_status
-- Returns sources and stacks whose view is stale (null or never generated)
------------------------------------------------------
CREATE OR REPLACE FUNCTION get_status()
RETURNS JSON AS $$
DECLARE
v_sources JSON;
v_stacks JSON;
BEGIN
SELECT COALESCE(json_agg(json_build_object('name', name, 'view_generated_at', view_generated_at) ORDER BY name), '[]'::json)
INTO v_sources
FROM dataflow.sources
WHERE view_generated_at IS NULL;
SELECT COALESCE(json_agg(json_build_object('name', name, 'view_generated_at', view_generated_at) ORDER BY name), '[]'::json)
INTO v_stacks
FROM dataflow.stacks
WHERE view_generated_at IS NULL;
RETURN json_build_object('stale_sources', v_sources, 'stale_stacks', v_stacks);
END;
$$ LANGUAGE plpgsql STABLE;