Skip to main content

Overview

Sistema Financiero is designed to be highly customizable. This guide covers common customizations without modifying core architecture.

Customizing Categories

Adding New Categories

Categories are hardcoded in 4 different files. You must update all locations to maintain consistency.
1

Location 1: AI Chat System Prompt

File: app/api/chat/stream/route.ts:23-31
content: `Eres un asistente financiero personal. Registras gastos e ingresos de forma conversacional.

📋 CATEGORÍAS VÁLIDAS:
**Gastos:** Alimentación, Transporte, Vivienda, Salud, Entretenimiento, Educación, Suscripciones, Otros Gastos
**Ingresos:** Salario, Ventas, Servicios, Inversiones, Freelance, Otros Ingresos

💳 MÉTODOS: Efectivo, Tarjeta, Transferencia, PayPal

Sé amigable y confirma con resumen detallado.`
2

Location 2: AI Function Schema (Expenses)

File: app/api/chat/stream/route.ts:66-90
{
  type: 'function',
  function: {
    name: 'registrar_gasto',
    description: 'Registra un gasto',
    parameters: {
      type: 'object',
      properties: {
        monto: { type: 'number' },
        categoria: {
          type: 'string',
          enum: [
            'Alimentación',
            'Transporte',
            'Vivienda',
            'Salud',
            'Entretenimiento',
            'Educación',
            'Suscripciones', // ← New category
            'Otros Gastos'
          ]
        },
        // ... rest of parameters
      }
    }
  }
}
3

Location 3: AI Function Schema (Income)

File: app/api/chat/stream/route.ts:92-119
{
  type: 'function',
  function: {
    name: 'registrar_ingreso',
    description: 'Registra un ingreso',
    parameters: {
      type: 'object',
      properties: {
        monto: { type: 'number' },
        categoria: {
          type: 'string',
          enum: [
            'Salario',
            'Ventas',
            'Servicios',
            'Inversiones',
            'Freelance', // ← New category
            'Otros Ingresos'
          ]
        },
        // ... rest of parameters
      }
    }
  }
}
4

Location 4: OCR Analysis Prompt

File: app/api/upload-image/route.ts:63-66
**CATEGORÍAS VÁLIDAS DEL SISTEMA:**
- Gastos: Alimentación, Transporte, Vivienda, Salud, Entretenimiento, Educación, Suscripciones, Otros Gastos
- Ingresos: Salario, Ventas, Servicios, Inversiones, Freelance, Otros Ingresos

Removing Categories

Simply remove from all 4 locations above. Users won’t be able to select them anymore.
Existing transactions with old categories will still display correctly. The database doesn’t enforce category validation.

Adding Payment Methods

1

Update AI System Prompt

File: app/api/chat/stream/route.ts:29
💳 MÉTODOS: Efectivo, Tarjeta, Transferencia, PayPal, Criptomonedas
2

Update Function Schema

File: app/api/chat/stream/route.ts:78-82 and 102-106
metodo_pago: {
  type: 'string',
  enum: ['Efectivo', 'Tarjeta', 'Transferencia', 'PayPal', 'Criptomonedas'],
  default: 'Efectivo'
}
3

Update Database Constraint

Optional: Add CHECK constraint in Supabase:
ALTER TABLE transacciones
DROP CONSTRAINT IF EXISTS transacciones_metodo_pago_check;

ALTER TABLE transacciones
ADD CONSTRAINT transacciones_metodo_pago_check
CHECK (metodo_pago IN ('Efectivo', 'Tarjeta', 'Transferencia', 'PayPal', 'Criptomonedas'));

Customizing AI Behavior

Changing AI Model

File: app/api/chat/stream/route.ts:54
const response = await fetch('https://openrouter.ai/api/v1/chat/completions', {
  body: JSON.stringify({
    model: 'anthropic/claude-3.5-sonnet', // ← Change here
    messages: openRouterMessages,
    max_tokens: 2000,
    temperature: 0.7,
    stream: true,
  })
})
Recommended Models:
ModelSpeedCostBest For
google/gemini-2.5-flash⚡ Fastest💰 CheapestDefault (current)
anthropic/claude-3.5-sonnet🐢 Slow💸 ExpensiveBest accuracy
openai/gpt-4o⚡ Fast💵 MediumBalanced
meta-llama/llama-3.1-70b-instruct⚡ Fast💰 CheapOpen source

Adjusting AI Temperature

File: app/api/chat/stream/route.ts:57
temperature: 0.7, // ← Change between 0.0 (precise) and 1.0 (creative)
  • 0.0-0.3: Very precise, deterministic responses
  • 0.4-0.7: Balanced (default)
  • 0.8-1.0: More creative, varied responses

Customizing System Prompt

File: app/api/chat/stream/route.ts:23-31
content: `Eres un asistente financiero EXTREMADAMENTE detallista.

Siempre pides confirmación antes de registrar cualquier transacción.

Cuando el usuario diga "gasté X en Y":
1. Extrae el monto y categoría
2. Pregunta el método de pago
3. Pregunta si quiere agregar descripción
4. SOLO entonces registra

📋 CATEGORÍAS VÁLIDAS:
**Gastos:** Alimentación, Transporte, Vivienda, Salud, Entretenimiento, Educación, Otros Gastos
**Ingresos:** Salario, Ventas, Servicios, Inversiones, Otros Ingresos

💳 MÉTODOS: Efectivo, Tarjeta, Transferencia

Sé profesional y detallado.`

Enabling Thinking Mode

File: app/api/chat/stream/route.ts:60-62
thinking_config: {
  max_thinking_tokens: 500, // Increase for deeper reasoning
}
Only supported by google/gemini-2.5-flash and newer models. Costs extra tokens.

Customizing UI/Styling

Changing Theme Colors

File: tailwind.config.js
module.exports = {
  theme: {
    extend: {
      colors: {
        primary: '#10b981', // emerald-500 → Change to your brand color
        secondary: '#06b6d4', // cyan-500
        danger: '#ef4444', // red-500
        success: '#22c55e', // green-500
      }
    }
  }
}
Then replace Tailwind classes:
// Before
className="bg-emerald-500 text-white"

// After
className="bg-primary text-white"

Changing Date Format

File: components/DataViews.tsx:456-461
{new Date(tx.fecha).toLocaleString('es-MX', {
  day: '2-digit',
  month: 'short', // ← Change to 'long' or 'numeric'
  year: 'numeric',
  hour: '2-digit',
  minute: '2-digit',
})}
Format Options:
  • 'es-MX' → Spanish Mexico
  • 'en-US' → English US
  • 'pt-BR' → Portuguese Brazil

Customizing Currency Display

File: components/DataViews.tsx:489
${tx.monto.toLocaleString('es-MX', {
  minimumFractionDigits: 2,
  style: 'currency', // ← Add this
  currency: 'MXN' // ← Add this (MXN, USD, EUR, etc.)
})}
Result: $450.50MXN 450.50 or $450.50 MXN

Changing Items Per Page

File: components/DataViews.tsx:40
const [itemsPorPagina, setItemsPorPagina] = useState(50) // ← Change default

Customizing Dashboard KPIs

Adding New KPI Card

File: app/page.tsx (add after existing KPI cards)
// Calculate average transaction amount
const avgTransaccion = transacciones.length > 0
  ? kpis.totalTransacciones > 0
    ? (kpis.totalIngresos + kpis.totalGastos) / kpis.totalTransacciones
    : 0
  : 0

// Add to JSX
<div className="bg-gradient-to-br from-purple-50 to-pink-50 dark:from-purple-900/20 dark:to-pink-900/20 p-6 rounded-xl border-2 border-purple-200 dark:border-purple-800">
  <div className="text-sm text-purple-700 dark:text-purple-300 mb-1">
    Promedio por Transacción
  </div>
  <div className="text-2xl font-bold text-purple-900 dark:text-purple-100">
    ${avgTransaccion.toLocaleString('es-MX', { minimumFractionDigits: 2 })}
  </div>
</div>

Customizing Date Range Filters

File: app/api/transacciones/route.ts:28-43
if (vista === 'diaria') {
  // Change from 7 days to 30 days
  const thirtyDaysAgo = new Date(now)
  thirtyDaysAgo.setDate(now.getDate() - 30) // ← Changed
  query = query.gte('fecha', thirtyDaysAgo.toISOString())
} else if (vista === 'semanal') {
  // Change from 4 weeks to 8 weeks
  const eightWeeksAgo = new Date(now)
  eightWeeksAgo.setDate(now.getDate() - 56) // ← Changed
  query = query.gte('fecha', eightWeeksAgo.toISOString())
} else {
  // Change from 12 months to 24 months
  const twentyFourMonthsAgo = new Date(now)
  twentyFourMonthsAgo.setMonth(now.getMonth() - 24) // ← Changed
  query = query.gte('fecha', twentyFourMonthsAgo.toISOString())
}

Customizing OCR Behavior

Adjusting OCR Prompt

File: app/api/upload-image/route.ts:56-114
text: `Analiza esta imagen y determina si es un ticket/factura válido.

**PASO 1: VALIDAR SI ES UN TICKET**
- ¿La imagen muestra un ticket, factura, recibo o comprobante de compra?
- ¿Tiene información de comercio, monto, items comprados?
- Si es screenshot de chat, foto aleatoria, o documento que NO sea ticket → marca "es_ticket": false

**INSTRUCCIONES ADICIONALES:**
- Si el ticket está en inglés, traduce las categorías al español
- Si no puedes leer el monto claramente, marca "monto": null
- Si el ticket tiene múltiples totales, usa el "Total Final" o "Total a Pagar"

// ... rest of prompt
`

Changing OCR Model

File: app/api/upload-image/route.ts:49
model: 'anthropic/claude-3.5-sonnet', // ← Better for complex receipts
// Or:
model: 'openai/gpt-4o', // ← Good balance
gemini-2.5-flash (default) is fastest and cheapest but may struggle with handwritten receipts.

Customizing Chat Interface

Changing Thinking Indicator Text

File: app/agente-mejorado/page.tsx (search for isThinking)
{msg.isThinking && (
  <div className="flex items-center gap-2 text-emerald-600 dark:text-emerald-400">
    <div className="animate-spin rounded-full h-4 w-4 border-b-2 border-emerald-600" />
    <span>Analizando tu solicitud...</span> {/* ← Change text */}
  </div>
)}

Adding Chat Message Timestamps

File: app/agente-mejorado/page.tsx (add to message rendering)
<div className="flex justify-between items-start">
  <div className="flex-1">
    {msg.content}
  </div>
  <span className="text-xs text-gray-400 ml-2">
    {new Date().toLocaleTimeString('es-MX', {
      hour: '2-digit',
      minute: '2-digit'
    })}
  </span>
</div>

Environment Variables

Adding Custom Configuration

File: .env.local
# Existing variables
NEXT_PUBLIC_SUPABASE_URL=https://xxx.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1...
OPENROUTER_API_KEY=sk-or-v1-...
NEXT_PUBLIC_SITE_URL=http://localhost:3000

# Custom variables
NEXT_PUBLIC_DEFAULT_CURRENCY=MXN
NEXT_PUBLIC_DATE_FORMAT=es-MX
NEXT_PUBLIC_MAX_TRANSACTIONS_PER_PAGE=50
NEXT_PUBLIC_ENABLE_BUDGET_ALERTS=true

Using Custom Variables

// File: components/DataViews.tsx
const defaultItemsPerPage = Number(
  process.env.NEXT_PUBLIC_MAX_TRANSACTIONS_PER_PAGE || 20
)

const [itemsPorPagina, setItemsPorPagina] = useState(defaultItemsPerPage)

Advanced Customizations

Adding Custom Validation Rules

File: app/api/chat/stream/route.ts:208-217
const { error } = await supabase.from('transacciones').insert({
  tipo,
  monto: functionArgs.monto,
  categoria: functionArgs.categoria,
  // ... other fields
})

// Add custom validation
if (!error) {
  // Send notification if amount > 1000
  if (functionArgs.monto > 1000) {
    await fetch('/api/notifications/send', {
      method: 'POST',
      body: JSON.stringify({
        message: `Gran transacción registrada: $${functionArgs.monto}`,
        tipo: 'alerta'
      })
    })
  }
}

Adding Category Icons

Create a mapping object:
// File: lib/categoryIcons.ts
export const CATEGORY_ICONS: Record<string, string> = {
  'Alimentación': '🍔',
  'Transporte': '🚗',
  'Vivienda': '🏠',
  'Salud': '💊',
  'Entretenimiento': '🎮',
  'Educación': '📚',
  'Otros Gastos': '📦',
  'Salario': '💼',
  'Ventas': '💰',
  'Servicios': '🔧',
  'Inversiones': '📈',
  'Otros Ingresos': '💵',
}

// Use in component
import { CATEGORY_ICONS } from '@/lib/categoryIcons'

<span>{CATEGORY_ICONS[tx.categoria]} {tx.categoria}</span>

Testing Customizations

1

Clear Cache

rm -rf .next
npm run dev
2

Test in Incognito

Open browser in incognito mode to avoid cached state
3

Check Console

Open DevTools → Console for errors
4

Test Database

Verify changes in Supabase Dashboard → Table Editor

Next Steps

Database Schema

Understand the database structure

Troubleshooting

Fix common customization issues

Build docs developers (and LLMs) love