trading-platform-database-v2/ddl/schemas/financial/001_wallets.sql
rckrdmrd e520268348 Migración desde trading-platform/apps/database - Estándar multi-repo v2
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 08:32:52 -06:00

148 lines
4.7 KiB
PL/PgSQL

-- ============================================================================
-- SCHEMA: financial
-- TABLE: wallets
-- DESCRIPTION: Sistema de Wallet Virtual con creditos USD equivalentes
-- VERSION: 1.0.0
-- CREATED: 2026-01-10
-- ============================================================================
-- Crear schema si no existe
CREATE SCHEMA IF NOT EXISTS financial;
-- Enum para estado de wallet
DO $$ BEGIN
CREATE TYPE financial.wallet_status AS ENUM (
'active', -- Wallet activo y operativo
'frozen', -- Congelado temporalmente (investigacion)
'suspended', -- Suspendido por violacion de terminos
'closed' -- Cerrado permanentemente
);
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
-- Tabla principal de Wallets
CREATE TABLE IF NOT EXISTS financial.wallets (
-- Identificadores
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL,
user_id UUID NOT NULL,
-- Saldos en creditos (equivalente USD, no dinero real)
balance DECIMAL(15, 4) NOT NULL DEFAULT 0
CHECK (balance >= 0),
reserved DECIMAL(15, 4) NOT NULL DEFAULT 0
CHECK (reserved >= 0),
-- Totales acumulados (para auditoria)
total_credited DECIMAL(15, 4) NOT NULL DEFAULT 0,
total_debited DECIMAL(15, 4) NOT NULL DEFAULT 0,
-- Creditos promocionales (separados del balance principal)
promo_balance DECIMAL(15, 4) NOT NULL DEFAULT 0
CHECK (promo_balance >= 0),
promo_expiry TIMESTAMPTZ,
-- Configuracion
currency VARCHAR(3) NOT NULL DEFAULT 'USD',
status financial.wallet_status NOT NULL DEFAULT 'active',
-- Limites operacionales
daily_spend_limit DECIMAL(15, 4) DEFAULT 1000.0000,
monthly_spend_limit DECIMAL(15, 4) DEFAULT 10000.0000,
single_transaction_limit DECIMAL(15, 4) DEFAULT 500.0000,
-- Tracking de uso
daily_spent DECIMAL(15, 4) NOT NULL DEFAULT 0,
monthly_spent DECIMAL(15, 4) NOT NULL DEFAULT 0,
last_daily_reset DATE DEFAULT CURRENT_DATE,
last_monthly_reset DATE DEFAULT DATE_TRUNC('month', CURRENT_DATE)::DATE,
-- Timestamps
last_transaction_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
-- Constraints
CONSTRAINT wallets_total_balance_check CHECK (balance + reserved >= 0),
CONSTRAINT wallets_unique_user UNIQUE (tenant_id, user_id)
);
-- Columna calculada para balance total disponible
COMMENT ON TABLE financial.wallets IS
'Wallet virtual con creditos USD equivalentes. No es dinero real.';
COMMENT ON COLUMN financial.wallets.balance IS
'Saldo disponible en creditos (1 credito = 1 USD equivalente)';
COMMENT ON COLUMN financial.wallets.reserved IS
'Creditos reservados para operaciones pendientes (ej: fondeo de agentes)';
COMMENT ON COLUMN financial.wallets.promo_balance IS
'Creditos promocionales separados del balance principal';
-- Indices
CREATE INDEX IF NOT EXISTS idx_wallets_tenant_id
ON financial.wallets(tenant_id);
CREATE INDEX IF NOT EXISTS idx_wallets_user_id
ON financial.wallets(user_id);
CREATE INDEX IF NOT EXISTS idx_wallets_status
ON financial.wallets(status);
CREATE INDEX IF NOT EXISTS idx_wallets_created_at
ON financial.wallets(created_at DESC);
-- Trigger para updated_at
CREATE OR REPLACE FUNCTION financial.update_wallet_timestamp()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = NOW();
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
DROP TRIGGER IF EXISTS wallet_updated_at ON financial.wallets;
CREATE TRIGGER wallet_updated_at
BEFORE UPDATE ON financial.wallets
FOR EACH ROW
EXECUTE FUNCTION financial.update_wallet_timestamp();
-- Funcion para resetear limites diarios/mensuales
CREATE OR REPLACE FUNCTION financial.reset_wallet_limits()
RETURNS TRIGGER AS $$
BEGIN
-- Reset diario
IF NEW.last_daily_reset < CURRENT_DATE THEN
NEW.daily_spent := 0;
NEW.last_daily_reset := CURRENT_DATE;
END IF;
-- Reset mensual
IF NEW.last_monthly_reset < DATE_TRUNC('month', CURRENT_DATE)::DATE THEN
NEW.monthly_spent := 0;
NEW.last_monthly_reset := DATE_TRUNC('month', CURRENT_DATE)::DATE;
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
DROP TRIGGER IF EXISTS wallet_reset_limits ON financial.wallets;
CREATE TRIGGER wallet_reset_limits
BEFORE UPDATE ON financial.wallets
FOR EACH ROW
EXECUTE FUNCTION financial.reset_wallet_limits();
-- RLS Policy para multi-tenancy
ALTER TABLE financial.wallets ENABLE ROW LEVEL SECURITY;
CREATE POLICY wallets_tenant_isolation ON financial.wallets
FOR ALL
USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
-- Grants
GRANT SELECT, INSERT, UPDATE ON financial.wallets TO trading_app;
GRANT SELECT ON financial.wallets TO trading_readonly;