-- ===================================================== -- ORBIQUANT IA - FINANCIAL SCHEMA VIEWS -- ===================================================== -- Description: Useful views for common financial queries -- Schema: financial -- ===================================================== -- ===================================================== -- VIEW: Active user wallets summary -- ===================================================== CREATE OR REPLACE VIEW financial.v_user_wallets_summary AS SELECT w.user_id, w.wallet_type, w.currency, w.balance, w.available_balance, w.pending_balance, w.status, w.last_transaction_at, w.total_deposits, w.total_withdrawals, w.created_at, -- Transaction counts (SELECT COUNT(*) FROM financial.wallet_transactions wt WHERE wt.wallet_id = w.id AND wt.status = 'completed') as total_transactions, (SELECT COUNT(*) FROM financial.wallet_transactions wt WHERE wt.wallet_id = w.id AND wt.status = 'pending') as pending_transactions, -- Latest transaction (SELECT wt.created_at FROM financial.wallet_transactions wt WHERE wt.wallet_id = w.id ORDER BY wt.created_at DESC LIMIT 1) as last_tx_date FROM financial.wallets w WHERE w.status = 'active'; COMMENT ON VIEW financial.v_user_wallets_summary IS 'Active wallets with transaction statistics'; -- ===================================================== -- VIEW: User total balance across all wallets (USD) -- ===================================================== CREATE OR REPLACE VIEW financial.v_user_total_balance AS SELECT user_id, SUM(CASE WHEN currency = 'USD' THEN balance ELSE 0 END) as total_usd, SUM(CASE WHEN currency = 'MXN' THEN balance ELSE 0 END) as total_mxn, SUM(CASE WHEN currency = 'EUR' THEN balance ELSE 0 END) as total_eur, -- Totals by wallet type SUM(CASE WHEN wallet_type = 'trading' AND currency = 'USD' THEN balance ELSE 0 END) as trading_usd, SUM(CASE WHEN wallet_type = 'investment' AND currency = 'USD' THEN balance ELSE 0 END) as investment_usd, SUM(CASE WHEN wallet_type = 'earnings' AND currency = 'USD' THEN balance ELSE 0 END) as earnings_usd, SUM(CASE WHEN wallet_type = 'referral' AND currency = 'USD' THEN balance ELSE 0 END) as referral_usd, COUNT(*) as wallet_count, MAX(last_transaction_at) as last_activity FROM financial.wallets WHERE status = 'active' GROUP BY user_id; COMMENT ON VIEW financial.v_user_total_balance IS 'Aggregated balance per user across all wallets'; -- ===================================================== -- VIEW: Recent transactions (last 30 days) -- ===================================================== CREATE OR REPLACE VIEW financial.v_recent_transactions AS SELECT wt.id, wt.wallet_id, w.user_id, w.wallet_type, wt.transaction_type, wt.status, wt.amount, wt.fee, wt.net_amount, wt.currency, wt.description, wt.reference_id, wt.balance_before, wt.balance_after, wt.created_at, wt.completed_at, -- Days since transaction EXTRACT(DAY FROM NOW() - wt.created_at) as days_ago FROM financial.wallet_transactions wt JOIN financial.wallets w ON w.id = wt.wallet_id WHERE wt.created_at >= NOW() - INTERVAL '30 days' ORDER BY wt.created_at DESC; COMMENT ON VIEW financial.v_recent_transactions IS 'Wallet transactions from last 30 days'; -- ===================================================== -- VIEW: Active subscriptions with user details -- ===================================================== CREATE OR REPLACE VIEW financial.v_active_subscriptions AS SELECT s.id, s.user_id, s.plan, s.status, s.price, s.currency, s.billing_interval, s.current_period_start, s.current_period_end, s.next_payment_at, s.cancel_at_period_end, s.stripe_subscription_id, -- Days until renewal EXTRACT(DAY FROM s.current_period_end - NOW()) as days_until_renewal, -- Is in trial (s.status = 'trialing') as is_trial, -- Trial days remaining CASE WHEN s.trial_end IS NOT NULL THEN EXTRACT(DAY FROM s.trial_end - NOW()) ELSE NULL END as trial_days_remaining FROM financial.subscriptions s WHERE s.status IN ('active', 'trialing', 'past_due') ORDER BY s.current_period_end ASC; COMMENT ON VIEW financial.v_active_subscriptions IS 'Active subscriptions with renewal information'; -- ===================================================== -- VIEW: Pending payments -- ===================================================== CREATE OR REPLACE VIEW financial.v_pending_payments AS SELECT p.id, p.user_id, p.subscription_id, p.amount, p.currency, p.payment_method, p.status, p.description, p.stripe_payment_intent_id, p.created_at, -- Days pending EXTRACT(DAY FROM NOW() - p.created_at) as days_pending FROM financial.payments p WHERE p.status IN ('pending', 'processing') ORDER BY p.created_at ASC; COMMENT ON VIEW financial.v_pending_payments IS 'Payments awaiting completion'; -- ===================================================== -- VIEW: Unpaid invoices -- ===================================================== CREATE OR REPLACE VIEW financial.v_unpaid_invoices AS SELECT i.id, i.user_id, i.invoice_number, i.total, i.amount_due, i.currency, i.due_date, i.status, i.invoice_date, i.hosted_invoice_url, -- Days overdue CASE WHEN i.due_date IS NOT NULL AND i.due_date < NOW() THEN EXTRACT(DAY FROM NOW() - i.due_date) ELSE 0 END as days_overdue, -- Is overdue (i.due_date IS NOT NULL AND i.due_date < NOW()) as is_overdue FROM financial.invoices i WHERE i.status = 'open' AND i.paid = false ORDER BY i.due_date ASC NULLS LAST; COMMENT ON VIEW financial.v_unpaid_invoices IS 'Open invoices with overdue status'; -- ===================================================== -- VIEW: Daily transaction volume -- ===================================================== CREATE OR REPLACE VIEW financial.v_daily_transaction_volume AS SELECT DATE(wt.created_at) as transaction_date, wt.transaction_type, wt.currency, COUNT(*) as transaction_count, SUM(wt.amount) as total_amount, SUM(wt.fee) as total_fees, SUM(wt.net_amount) as total_net_amount, AVG(wt.amount) as avg_amount FROM financial.wallet_transactions wt WHERE wt.status = 'completed' AND wt.created_at >= NOW() - INTERVAL '90 days' GROUP BY DATE(wt.created_at), wt.transaction_type, wt.currency ORDER BY transaction_date DESC, transaction_type; COMMENT ON VIEW financial.v_daily_transaction_volume IS 'Daily aggregated transaction statistics'; -- ===================================================== -- VIEW: Wallet activity summary (last 7 days) -- ===================================================== CREATE OR REPLACE VIEW financial.v_wallet_activity_7d AS SELECT w.id as wallet_id, w.user_id, w.wallet_type, w.currency, w.balance, -- Transaction counts by type COUNT(CASE WHEN wt.transaction_type = 'deposit' THEN 1 END) as deposits_7d, COUNT(CASE WHEN wt.transaction_type = 'withdrawal' THEN 1 END) as withdrawals_7d, COUNT(CASE WHEN wt.transaction_type IN ('transfer_in', 'transfer_out') THEN 1 END) as transfers_7d, -- Amounts SUM(CASE WHEN wt.transaction_type = 'deposit' THEN wt.amount ELSE 0 END) as deposit_amount_7d, SUM(CASE WHEN wt.transaction_type = 'withdrawal' THEN wt.amount ELSE 0 END) as withdrawal_amount_7d, -- Total activity COUNT(wt.id) as total_transactions_7d FROM financial.wallets w LEFT JOIN financial.wallet_transactions wt ON wt.wallet_id = w.id AND wt.created_at >= NOW() - INTERVAL '7 days' AND wt.status = 'completed' WHERE w.status = 'active' GROUP BY w.id, w.user_id, w.wallet_type, w.currency, w.balance; COMMENT ON VIEW financial.v_wallet_activity_7d IS 'Wallet activity summary for last 7 days'; -- ===================================================== -- VIEW: Subscription revenue metrics -- ===================================================== CREATE OR REPLACE VIEW financial.v_subscription_revenue AS SELECT s.plan, s.billing_interval, s.currency, COUNT(*) as active_count, SUM(s.price) as total_monthly_value, AVG(s.price) as avg_price, -- MRR calculation (Monthly Recurring Revenue) SUM(CASE WHEN s.billing_interval = 'month' THEN s.price WHEN s.billing_interval = 'year' THEN s.price / 12 ELSE 0 END) as monthly_recurring_revenue FROM financial.subscriptions s WHERE s.status IN ('active', 'trialing') GROUP BY s.plan, s.billing_interval, s.currency ORDER BY s.plan, s.billing_interval; COMMENT ON VIEW financial.v_subscription_revenue IS 'Subscription metrics and MRR calculation';