Dash
Cargando…

⚠️ Algo falló al cargar la app

Toma captura y envíame este mensaje para resolverlo rápido.
) 3) Run. Verás "Success. No rows returned" 4) Vuelve a la app y pon DEMO_MODE: false en window.DASH_CONFIG ────────────────────────────────────────────────────────────────────────────── -- CLIENTES (tenants) CREATE TABLE IF NOT EXISTS public.clientes ( id text PRIMARY KEY, email text UNIQUE NOT NULL, nombre text NOT NULL, pin_hash text NOT NULL, pin_length int DEFAULT 4, codigo_maestro text NOT NULL, token_macrodroid text UNIQUE NOT NULL, moneda_principal text DEFAULT 'COP', monedas_activas text[] DEFAULT ARRAY['COP','USD'], licencia_activa boolean DEFAULT true, licencia_vence timestamptz, plan text DEFAULT 'trial', modo_macrodroid text DEFAULT 'B', fuentes_macrodroid jsonb DEFAULT '{"push":true,"sms":true,"whatsapp":false,"email":false}'::jsonb, permitir_soporte boolean DEFAULT false, soporte_hasta timestamptz, prefs jsonb DEFAULT '{}'::jsonb, created_at timestamptz DEFAULT now() ); -- CUENTAS CREATE TABLE IF NOT EXISTS public.cuentas ( id text PRIMARY KEY, cliente_id text REFERENCES public.clientes(id) ON DELETE CASCADE, nombre text NOT NULL, tipo text DEFAULT 'debito', banco text, moneda text DEFAULT 'COP', saldo_inicial numeric DEFAULT 0, color text, activa boolean DEFAULT true, created_at timestamptz DEFAULT now() ); -- TRANSACCIONES CREATE TABLE IF NOT EXISTS public.transacciones ( id text PRIMARY KEY, cliente_id text REFERENCES public.clientes(id) ON DELETE CASCADE, fecha date NOT NULL, hora time, tipo text NOT NULL, -- ingreso | egreso | transferencia concepto text, categoria text, persona text, cuenta_origen text, monto_origen numeric, moneda_origen text, cuenta_destino text, monto_destino numeric, moneda_destino text, tasa numeric, comision numeric DEFAULT 0, referencia text, notas text, origen text DEFAULT 'manual', -- manual | macrodroid | importado texto_original text, created_at timestamptz DEFAULT now() ); CREATE INDEX IF NOT EXISTS idx_trans_cli_fecha ON public.transacciones(cliente_id, fecha DESC); -- CXC (por cobrar) CREATE TABLE IF NOT EXISTS public.cxc ( id text PRIMARY KEY, cliente_id text REFERENCES public.clientes(id) ON DELETE CASCADE, deudor text NOT NULL, concepto text, monto numeric NOT NULL, moneda text DEFAULT 'COP', fecha_registro date, vence date, estado text DEFAULT 'pendiente', -- pendiente | parcial | pagado notas text, created_at timestamptz DEFAULT now() ); -- CXP (por pagar) CREATE TABLE IF NOT EXISTS public.cxp ( id text PRIMARY KEY, cliente_id text REFERENCES public.clientes(id) ON DELETE CASCADE, acreedor text NOT NULL, concepto text, monto numeric NOT NULL, moneda text DEFAULT 'COP', fecha_registro date, vence date, estado text DEFAULT 'pendiente', notas text, created_at timestamptz DEFAULT now() ); -- VENCIMIENTOS RECURRENTES CREATE TABLE IF NOT EXISTS public.vencimientos ( id text PRIMARY KEY, cliente_id text REFERENCES public.clientes(id) ON DELETE CASCADE, concepto text NOT NULL, categoria text, dia_mes int NOT NULL, monto numeric NOT NULL, moneda text DEFAULT 'COP', tipo text DEFAULT 'servicio', activo boolean DEFAULT true, created_at timestamptz DEFAULT now() ); -- PERSONAS CREATE TABLE IF NOT EXISTS public.personas ( id text PRIMARY KEY, cliente_id text REFERENCES public.clientes(id) ON DELETE CASCADE, nombre text NOT NULL, relacion text, empresa text, whatsapp text, email text, color text, activo boolean DEFAULT true, created_at timestamptz DEFAULT now() ); -- CATEGORIAS CREATE TABLE IF NOT EXISTS public.categorias ( id text PRIMARY KEY, cliente_id text REFERENCES public.clientes(id) ON DELETE CASCADE, nombre text NOT NULL, tipo text NOT NULL, -- ingreso | egreso color text, descripcion text, created_at timestamptz DEFAULT now() ); -- POR CLASIFICAR (cola MacroDroid) CREATE TABLE IF NOT EXISTS public.por_clasificar ( id text PRIMARY KEY, cliente_id text REFERENCES public.clientes(id) ON DELETE CASCADE, fecha date NOT NULL, monto numeric, moneda text, tipo text, banco text, texto_original text, origen text DEFAULT 'macrodroid', created_at timestamptz DEFAULT now() ); -- LOGS DE SOPORTE (solo admin lo lee) CREATE TABLE IF NOT EXISTS public.logs_soporte ( id text PRIMARY KEY, cliente_id text REFERENCES public.clientes(id) ON DELETE CASCADE, accion text NOT NULL, contexto jsonb, created_at timestamptz DEFAULT now() ); CREATE INDEX IF NOT EXISTS idx_logs_cli ON public.logs_soporte(cliente_id, created_at DESC); -- ═══════════════════════════════════════════════════════════════════════════ -- ROW LEVEL SECURITY -- ═══════════════════════════════════════════════════════════════════════════ -- Habilitar RLS en todas las tablas ALTER TABLE public.clientes ENABLE ROW LEVEL SECURITY; ALTER TABLE public.cuentas ENABLE ROW LEVEL SECURITY; ALTER TABLE public.transacciones ENABLE ROW LEVEL SECURITY; ALTER TABLE public.cxc ENABLE ROW LEVEL SECURITY; ALTER TABLE public.cxp ENABLE ROW LEVEL SECURITY; ALTER TABLE public.vencimientos ENABLE ROW LEVEL SECURITY; ALTER TABLE public.personas ENABLE ROW LEVEL SECURITY; ALTER TABLE public.categorias ENABLE ROW LEVEL SECURITY; ALTER TABLE public.por_clasificar ENABLE ROW LEVEL SECURITY; ALTER TABLE public.logs_soporte ENABLE ROW LEVEL SECURITY; -- Para empezar simple, política PERMISIVA con anon key -- (la app ya filtra por cliente_id en cada query) -- Cuando quieras endurecer, cámbiala a usar JWT con auth.uid() CREATE POLICY "anon_all_clientes" ON public.clientes FOR ALL USING (true) WITH CHECK (true); CREATE POLICY "anon_all_cuentas" ON public.cuentas FOR ALL USING (true) WITH CHECK (true); CREATE POLICY "anon_all_trans" ON public.transacciones FOR ALL USING (true) WITH CHECK (true); CREATE POLICY "anon_all_cxc" ON public.cxc FOR ALL USING (true) WITH CHECK (true); CREATE POLICY "anon_all_cxp" ON public.cxp FOR ALL USING (true) WITH CHECK (true); CREATE POLICY "anon_all_venc" ON public.vencimientos FOR ALL USING (true) WITH CHECK (true); CREATE POLICY "anon_all_personas" ON public.personas FOR ALL USING (true) WITH CHECK (true); CREATE POLICY "anon_all_cats" ON public.categorias FOR ALL USING (true) WITH CHECK (true); CREATE POLICY "anon_all_clas" ON public.por_clasificar FOR ALL USING (true) WITH CHECK (true); CREATE POLICY "anon_all_logs" ON public.logs_soporte FOR ALL USING (true) WITH CHECK (true); -- ═══════════════════════════════════════════════════════════════════════════ -- RPC: macrodroid_capture -- Llamada desde MacroDroid: valida el token y mete el movimiento -- ═══════════════════════════════════════════════════════════════════════════ CREATE OR REPLACE FUNCTION public.macrodroid_capture( token text, banco text, monto numeric, moneda text DEFAULT 'COP', tipo text DEFAULT 'egreso', texto_original text DEFAULT NULL ) RETURNS jsonb LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_cliente_id text; v_modo text; v_id text; BEGIN -- Validar token SELECT id, modo_macrodroid INTO v_cliente_id, v_modo FROM public.clientes WHERE token_macrodroid = token AND licencia_activa = true; IF v_cliente_id IS NULL THEN RETURN jsonb_build_object('ok', false, 'error', 'token_invalido_o_licencia_inactiva'); END IF; v_id := 'mac_' || extract(epoch from now())::bigint || '_' || substr(md5(random()::text),1,6); -- Modo A: todo a por_clasificar -- Modo B: si el monto y banco están claros, va directo; si no, a por_clasificar -- Modo C: todo a por_clasificar (confirmación se hace en MacroDroid) IF v_modo = 'B' AND monto > 0 AND banco IS NOT NULL THEN INSERT INTO public.transacciones (id, cliente_id, fecha, tipo, concepto, monto_origen, moneda_origen, origen, texto_original) VALUES (v_id, v_cliente_id, CURRENT_DATE, tipo, banco || ' · auto', monto, moneda, 'macrodroid', texto_original); RETURN jsonb_build_object('ok', true, 'destino', 'transacciones', 'id', v_id); ELSE INSERT INTO public.por_clasificar (id, cliente_id, fecha, monto, moneda, tipo, banco, texto_original) VALUES (v_id, v_cliente_id, CURRENT_DATE, monto, moneda, tipo, banco, texto_original); RETURN jsonb_build_object('ok', true, 'destino', 'por_clasificar', 'id', v_id); END IF; END; $$; -- Permitir llamar la RPC con anon key GRANT EXECUTE ON FUNCTION public.macrodroid_capture(text,text,numeric,text,text,text) TO anon; -- ═══════════════════════════════════════════════════════════════════════════ -- LISTO. Vuelve a la app, configura DASH_CONFIG.SUPABASE_URL y SUPABASE_ANON_KEY, -- pon DEMO_MODE: false y refresca. El primer cliente debe crearlo el admin desde /?admin -- ═══════════════════════════════════════════════════════════════════════════ -->