🟢 Phase 2 · Budgets Core

Plan de Implementación P2

Plan detallado para implementar las 8 user stories de P2 (Presupuestos + Períodos + Preferencias). Incluye verificación de backend, estado de componentes, división de sub-agentes y validación con Mobile MCP.

8
User Stories
10
Test Cases
✅ Todo
Backend listo
4
Sub-Agentes
Expo Go
Testing tool
✅ Fijo
Deuda P1 (refresh)
📋

Resumen Ejecutivo

P2 es la fase de Budgets Core — el feature principal del app.

P1 firmado y cerrado (2026-03-26)
11/11 test cases PASS. Todos los bugs resueltos. Axios con interceptor de refresh funcionando. Deuda técnica del refresh token ya resuelta en el backendPOST /api/auth/refresh devuelve data.accessToken en el body JSON además del httpOnly cookie.
🧩
Los componentes P2 ya existen — solo hay que conectarlos
Durante P1, el equipo pre-construyó BudgetWizard.tsx, NewBudgetSheet.tsx y EditBudgetSheet.tsx. La pantalla budgets.tsx es un placeholder. El trabajo de P2 es conectar estos componentes a la API real y crear las pantallas de Período y Preferencias.
⚠️
Lo que falta construir en P2
1. Instalar react-hook-form + zod (prerequisito no instalado aún)
2. Extender lib/api.ts con todos los endpoints de budgets/categories/preferences
3. Crear hooks TanStack Query: useBudgets, useCategories, usePreferences + mutaciones
4. Crear utilidades de período en el cliente (port de utils/periodCalculations.ts)
5. Implementar app/(tabs)/budgets.tsx completo (actualmente placeholder)
6. Pantalla de configuración de período
7. Pantalla de preferencias (moneda)
User Stories P2 — 8 total
IDNombreMóduloComponente existente
US-BUDGET-01 Wizard de primer uso Budgets BudgetWizard.tsx ✅ Pre-built
US-BUDGET-02 Agregar presupuesto individual Budgets NewBudgetSheet.tsx ✅ Pre-built
US-BUDGET-03 Editar monto de presupuesto Budgets EditBudgetSheet.tsx ✅ Pre-built
US-BUDGET-04 Editar nombre de categoría Budgets EditBudgetSheet.tsx ✅ Pre-built
US-BUDGET-05 Eliminar presupuesto + confirmación Budgets EditBudgetSheet.tsx ✅ Pre-built
US-PERIOD-01 Configurar tipo de período Período 🔧 Pantalla nueva: settings/period.tsx
US-PERIOD-02 Ver período actual con fechas exactas Período 🔧 Extender PeriodNavigator.tsx
US-PREF-02 Cambiar moneda predeterminada Preferencias 🔧 Extender settings/index.tsx
🔌

Verificación de Backend Endpoints

100% de los endpoints de P2 ya existen en el backend. Cero cambios requeridos.

🎉
Backend P2 = cero trabajo
Todos los endpoints necesarios para P2 están implementados y en producción. El backend está en https://candidates-ashley-birth-sensitivity.trycloudflare.com. Solo el mobile necesita trabajo.
Budgets (CRUD completo)
GET/api/budgets?year={y}&month={m}Presupuestos del período — con spent calculado
GET/api/budgets/:idPresupuesto individual
POST/api/budgetsCrear presupuesto — body: {categoryId, amount, currency, targetYear, targetMonth}
PUT/api/budgets/:idActualizar monto/moneda de presupuesto
DEL/api/budgets/:idEliminar presupuesto
POST/api/budgets/recalculate-spentRecalcula spent de todos los budgets del usuario
Categories (CRUD + reorder)
GET/api/categoriesLista de categorías del usuario (ordenadas por sortOrder)
POST/api/categoriesCrear categoría — body: {name, color?, icon?}
PUT/api/categories/:idActualizar nombre/color/icon de categoría (afecta todas las txns)
DEL/api/categories/:idEliminar categoría
Preferences
GET/api/preferencesPreferencias del usuario: language, periodType, specificDay, defaultCurrency
PUT/api/preferencesActualiza preferencias — PATCH semántico, solo se envían los campos a cambiar
Auth (deuda técnica P1 resuelta)
POST/api/auth/refresh✅ YA devuelve data.accessToken en body JSON (además del httpOnly cookie)
📝
Nota sobre tipos del backend
Budget: { id, userId, categoryId, category: {id,name,color,icon}, amount: number, spent: number, currency, targetYear, targetMonth }
Preferences: { id, userId, language, periodType: 'calendar_month'|'specific_day', specificDay: number, defaultCurrency }
Category: { id, userId, name, color, icon, sortOrder, isDefault, isArchived }
🧱

Estado de Prerequisitos

P1 completo + lo que necesita instalar/crear el Agente A antes de B y C.

De P1 (ya disponible)
✅ API Client (Axios + JWT interceptor) ✅ Refresh token interceptor (deuda resuelta) ✅ SecureStore para tokens ✅ Zustand auth store ✅ TanStack Query v5 ✅ AuthGuard funcionando ✅ Tab bar 4 tabs ✅ BottomSheet component ✅ BudgetWizard (pre-built) ✅ NewBudgetSheet (pre-built) ✅ EditBudgetSheet (pre-built) ✅ BudgetCard (pre-built)
Necesita instalar/crear (Agente A)
🔧 react-hook-form — no en package.json 🔧 zod — no en package.json 🔧 @hookform/resolvers — para integrar zod 🔧 Extender api.ts: budgets, categories, preferences 🔧 lib/hooks/useBudgets.ts — TanStack Query hooks 🔧 lib/hooks/useCategories.ts — TanStack Query hooks 🔧 lib/hooks/usePreferences.ts — TanStack Query hooks 🔧 lib/utils/periodCalculations.ts — port del backend
Necesita conectar (Agentes B + C)
🔌 app/(tabs)/budgets.tsx → reemplazar placeholder 🔌 BudgetWizard → useCreateBudget + useUpdatePreferences 🔌 NewBudgetSheet → useCreateBudget 🔌 EditBudgetSheet → useUpdateBudget + useDeleteBudget 🔌 settings/period.tsx → nueva pantalla 🔌 settings/index.tsx → currency picker 🔌 PeriodNavigator → mostrar fechas exactas del período
🧩

Inventario de Componentes

Qué existe, qué falta, qué necesita ser actualizado.

Componente / ArchivoEstadoAcción en P2
components/ui/BudgetWizard.tsx Pre-built Conectar props onComplete → API (batch create budgets + preferences)
components/ui/NewBudgetSheet.tsx Pre-built Conectar onSaveuseCreateBudget mutation
components/ui/EditBudgetSheet.tsx Pre-built Conectar onSaveuseUpdateBudget, onDeleteuseDeleteBudget
components/ui/BudgetCard.tsx Listo Usado as-is en la pantalla de budgets. Agregar onEdit prop si no lo tiene.
components/ui/PeriodNavigator.tsx Actualizar Agregar subtítulo con fechas exactas (US-PERIOD-02): "15 feb → 14 mar 2026"
app/(tabs)/budgets.tsx Placeholder Reemplazar completamente con pantalla funcional: lista de budgets + FAB + wizard
app/(tabs)/settings.tsx Actualizar Agregar row de "Moneda" y link a nueva pantalla "Período"
app/(tabs)/settings/period.tsx No existe Crear: selector calendar_month vs specific_day + day picker (1-28)
lib/api.ts Extender Agregar métodos para budgets (CRUD), categories (CRUD), preferences (GET/PUT)
lib/hooks/useBudgets.ts No existe Crear: useBudgets, useCreateBudget, useUpdateBudget, useDeleteBudget
lib/hooks/useCategories.ts No existe Crear: useCategories, useCreateCategory, useUpdateCategory, useDeleteCategory
lib/hooks/usePreferences.ts No existe Crear: usePreferences, useUpdatePreferences
lib/utils/periodCalculations.ts No existe Port del backend: calculatePeriodDates, getCurrentTargetMonth, getNextPeriod, getPreviousPeriod
🤖

Plan de Sub-Agentes

4 sub-agentes: A (foundation), B (budgets screen), C (period + prefs), D (QA + Mobile MCP).

A
Foundation & Data Layer
Instalar dependencias · Extender api.ts · Crear todos los hooks TanStack Query · Port de period utils
Primero
Dependencias a instalar
cd /root/clawd/projects/pocket-penny/finpal-mobile npx expo install react-hook-form @hookform/resolvers zod
Tarea 1 — Extender lib/api.ts
  • Budgets: api.budgets.list(year, month), api.budgets.get(id), api.budgets.create(body), api.budgets.update(id, body), api.budgets.delete(id)
  • Categories: api.categories.list(), api.categories.create(body), api.categories.update(id, body), api.categories.delete(id)
  • Preferences: api.preferences.get(), api.preferences.update(body)
  • Tipar todas las respuestas con interfaces TypeScript explícitas
Tarea 2 — Crear lib/hooks/useBudgets.ts
  • useBudgets(year, month)useQuery(['budgets', year, month]) — staleTime: 30s
  • useCreateBudget()useMutation + invalidate ['budgets']
  • useUpdateBudget()useMutation + invalidate ['budgets']
  • useDeleteBudget()useMutation + invalidate ['budgets']
  • Todas las mutaciones hacen invalidate optimista del query key correspondiente
Tarea 3 — Crear lib/hooks/useCategories.ts
  • useCategories()useQuery(['categories']) — staleTime: 60s
  • useCreateCategory()useMutation + invalidate ['categories'] y ['budgets']
  • useUpdateCategory()useMutation + invalidate ambos
  • useDeleteCategory()useMutation + invalidate ambos
Tarea 4 — Crear lib/hooks/usePreferences.ts
  • usePreferences()useQuery(['preferences']) — staleTime: ∞ (solo invalida al update)
  • useUpdatePreferences()useMutation + invalidate ['preferences'] y ['budgets']
Tarea 5 — Crear lib/utils/periodCalculations.ts
  • Port TypeScript 1:1 de apps/backend/src/utils/periodCalculations.ts
  • Exportar: calculatePeriodDates, getCurrentTargetMonth, getNextPeriod, getPreviousPeriod, formatPeriodLabel (nueva: retorna string "15 feb → 14 mar")
  • Tests unitarios inline con console.assert o jest si el proyecto lo tiene
Criterio de validación (antes de liberar a B y C)
  • TypeScript compila sin errores: npx tsc --noEmit
  • api.budgets.list(2026, 3) retorna array de budgets desde el backend real
  • api.preferences.get() retorna objeto de preferencias del usuario autenticado
  • calculatePeriodDates({periodType:'specific_day', specificDay:15}, 2026, 3) retorna {startDate: 2026-02-15, endDate: 2026-03-14}
B
Budgets Screen (US-BUDGET-01 → 05)
Reemplazar placeholder · Wizard · NewBudget · EditBudget · Delete
Después de A
Tarea 1 — app/(tabs)/budgets.tsx (pantalla principal)
  • Usar useBudgets(year, month) para cargar la lista
  • Pasar el year y month desde el PeriodNavigator (sincronizado con el dashboard)
  • Renderizar lista de BudgetCard con botón de editar por card
  • FAB (+) en esquina inferior derecha → abre NewBudgetSheet
  • Toque en card → abre EditBudgetSheet para esa card
  • Empty state: si no hay budgets → mostrar BudgetWizard (US-BUDGET-01 / TC-P2-01)
  • Si ya hay budgets → NO mostrar wizard (TC-P2-10)
  • Loading state: skeleton cards mientras carga
Tarea 2 — Wizard (US-BUDGET-01)
  • Al completar wizard: llamar useCreateBudget en batch para cada categoría seleccionada
  • Llamar useUpdatePreferences con el tipo de período configurado en el wizard
  • Mostrar loading state durante la creación
  • Al finalizar: invalidar query de budgets → el dashboard se actualiza solo
  • Verificar que el wizard NO aparece si ya hay budgets (TC-P2-10)
Tarea 3 — NewBudgetSheet (US-BUDGET-02)
  • onSave(data) → llamar useCreateBudget.mutateAsync(data)
  • Body: {categoryId, amount, currency, targetYear, targetMonth}
  • Usar categorías del usuario desde useCategories() en el selector
  • Si la categoría no existe: llamar useCreateCategory primero, luego crear budget
  • Loading state en el botón durante la mutación
  • Error handling: mostrar mensaje si el presupuesto ya existe para esa categoría/período
Tarea 4 — EditBudgetSheet (US-BUDGET-03, 04, 05)
  • US-BUDGET-03 (editar monto): onSave({amount, currency})PUT /api/budgets/:id
  • US-BUDGET-04 (renombrar categoría): onSave({name})PUT /api/categories/:id — nota: afecta TODAS las transacciones
  • US-BUDGET-05 (eliminar): onDelete(id)DELETE /api/budgets/:id
  • Confirmación de borrado: usa el componente DeleteConfirmation que ya existe en EditBudgetSheet
  • Verificar cancelación (TC-P2-06): cerrar sheet sin llamar la API
Criterio de validación
  • TC-P2-01: Wizard crea budgets y redirige al dashboard ✓
  • TC-P2-02: Nueva card aparece en la lista inmediatamente ✓
  • TC-P2-03: Barra de progreso se recalcula al editar monto ✓
  • TC-P2-04: Nombre actualizado en budget y en breadcrumb ✓
  • TC-P2-05: Card desaparece con animación al confirmar borrado ✓
  • TC-P2-06: Sheet se cierra sin cambios al cancelar ✓
  • TC-P2-10: Sin budgets → wizard. Con budgets → lista directa ✓
C
Period & Preferences (US-PERIOD-01, 02 · US-PREF-02)
Pantalla de período · Fechas exactas en dashboard · Currency en settings
Después de A (paralelo a B)
Tarea 1 — app/(tabs)/settings/period.tsx (US-PERIOD-01)
  • Nueva pantalla accesible desde settings → "Período presupuestal"
  • Dos opciones: "Mes calendario (del 1 al último día)" y "Día específico del mes"
  • Si se elige "Día específico": mostrar slider/picker de 1–28
  • Preview del período calculado en tiempo real: "Tu período actual: 15 feb → 14 mar 2026"
  • Botón "Guardar" → PUT /api/preferences con {periodType, specificDay?}
  • Al guardar: invalidar ['preferences'] y ['budgets']
  • Navigación: agregar link en settings/index.tsx/settings/period
Tarea 2 — PeriodNavigator + Dashboard (US-PERIOD-02)
  • Cargar preferencias con usePreferences() en el dashboard
  • Usar calculatePeriodDates(prefs, year, month) para obtener las fechas exactas
  • Actualizar PeriodNavigator: agregar subtítulo con fechas formateadas en español
  • Formato: "15 feb – 14 mar" (abreviado) o "15 de febrero al 14 de marzo" (expandido)
  • Si las preferencias cargan, mostrar fechas. Si fallan, mostrar solo "mes año" como antes.
Tarea 3 — Currency en Settings (US-PREF-02)
  • Agregar row "Moneda" en app/(tabs)/settings.tsx
  • Al tocar: abrir BottomSheet con lista de monedas: DOP, USD, EUR (+ otras si existen en el backend)
  • Selección → PUT /api/preferences con {defaultCurrency: 'USD'}
  • Mostrar moneda seleccionada actualmente en el row de settings
  • Invalidar ['preferences'] al guardar
  • Verificar que las BudgetCards muestran la nueva moneda (el backend ya retorna el campo currency en cada budget)
Criterio de validación
  • TC-P2-07: Período del día 15 calcula correctamente "15 feb → 14 mar" ✓
  • TC-P2-08: Fechas exactas visibles en el PeriodNavigator del dashboard ✓
  • TC-P2-09: Cambiar moneda a USD → todos los montos reflejan "USD" ✓
D
QA & Validación (Mobile MCP)
Ejecutar los 10 test cases TC-P2-01→10 · Reportar bugs · Actualizar spec QA · Generar sign-off
Último (después de B + C)
📱
Herramienta: Mobile MCP + Expo Go
P2 no necesita módulos nativos. Expo Go es suficiente. El Agente D lanzará el servidor de Expo y usará el Mobile MCP para navegar y verificar cada test case en el device emulado o físico conectado. Backend en producción: https://candidates-ashley-birth-sensitivity.trycloudflare.com
Setup antes de QA
  • Verificar que los Agentes B y C están mergeados y el proyecto compila sin errores
  • Iniciar servidor Expo: cd finpal-mobile && npx expo start --clear
  • Conectar device/emulador vía Mobile MCP
  • Loguear con una cuenta que tenga datos de prueba (o crear una nueva)
Casos de prueba a ejecutar (en orden)
  • TC-P2-10 primero: con una cuenta sin budgets → verificar wizard aparece
  • TC-P2-01: completar wizard → verificar cards en dashboard
  • TC-P2-10 segundo: volver a budgets tab → verificar NO aparece wizard
  • TC-P2-02: FAB → crear nuevo budget → verificar nueva card
  • TC-P2-03: editar monto → verificar barra de progreso recalculada
  • TC-P2-04: renombrar categoría → verificar propagación
  • TC-P2-05: eliminar → confirmar → card desaparece
  • TC-P2-06: eliminar → cancelar → card permanece
  • TC-P2-07: Settings → Período → elegir día 15 → guardar → verificar cálculo
  • TC-P2-08: volver al dashboard → verificar fechas en PeriodNavigator
  • TC-P2-09: Settings → Moneda → elegir USD → verificar actualización en dashboard
Post-QA
  • Documentar resultado (PASS/FAIL) de cada TC en este reporte
  • Si hay FAILs: crear bug report con pasos reproducibles y asignar a Agente B o C para fix
  • Re-ejecutar solo los TCs afectados tras cada fix
  • Actualizar contadores en finpal-mobile-specs/qa/index.html (stats: pendientes → pasando)
  • Verificar que los TC-P1 no regresen (sanity check de TC-P1-04 login y TC-P1-09 dashboard)
  • Generar P2-SIGN-OFF-REPORT.md en la raíz del proyecto
⏱️

Secuencia de Ejecución

A es bloqueante. B y C se pueden ejecutar en paralelo. D al final.

Dependency graph
🔵 Agente A · Foundation
🟢 Agente B · Budgets Screen
🟡 Agente C · Period + Prefs
🟣 Agente D · QA + Mobile MCP
B y C son independientes entre sí y pueden correr en paralelo. D espera a que ambos terminen.
AgenteDepende deDesbloqueaEstimado
A · Foundation Nada (P1 ✅) B + C 1 sesión
B · Budgets Screen A completo D (parcial) 1–2 sesiones
C · Period + Prefs A completo D (parcial) 1 sesión
D · QA Mobile MCP B + C completos P2 sign-off 1 sesión
🧪

QA Test Cases P2

10 casos — todos pendientes. El Agente D los ejecuta con Mobile MCP tras B + C.

ID Story Descripción Resultado esperado Prioridad Agente
TC-P2-01 US-BUDGET-01 Wizard de primer uso crea presupuestos iniciales Categorías seleccionadas → budgets creados vía API, redirect al dashboard con cards visibles Alta B
TC-P2-02 US-BUDGET-02 Crear presupuesto individual desde FAB Nueva card aparece en la lista de budgets con monto y barra vacía Alta B
TC-P2-03 US-BUDGET-03 Editar monto de presupuesto existente Monto actualizado, barra de progreso recalculada inmediatamente en la card Alta B
TC-P2-04 US-BUDGET-04 Renombrar categoría de presupuesto Nombre actualizado en la card. Backend actualiza también las transacciones existentes de esa categoría Media B
TC-P2-05 US-BUDGET-05 Eliminar presupuesto con confirmación via BottomSheet BottomSheet de confirmación aparece. Al confirmar, card desaparece de la lista Alta B
TC-P2-06 US-BUDGET-05 Cancelar eliminación de presupuesto BottomSheet se cierra. El presupuesto permanece intacto en la lista Media B
TC-P2-07 US-PERIOD-01 Configurar período por día específico del mes (día 15) Período calculado correctamente: del 15 del mes anterior al 14 del mes actual. Preview visible antes de guardar. Alta C
TC-P2-08 US-PERIOD-02 Ver fechas exactas de inicio y fin del período en el dashboard PeriodNavigator muestra el rango de fechas según la configuración de período del usuario Media C
TC-P2-09 US-PREF-02 Cambiar moneda predeterminada a USD Todos los montos en dashboard y en las BudgetCards reflejan "USD" Media C
TC-P2-10 US-BUDGET-01 Wizard NO aparece si ya existen presupuestos en el período Dashboard muestra directamente las cards de presupuesto sin pasar por el wizard Media B

Criterios de Sign-Off P2

Condiciones que deben cumplirse antes de cerrar P2 y abrir P3.

CriterioDescripción
🧪 QA 10/10 Los 10 test cases TC-P2-01→10 en estado PASS
🔤 TypeScript npx tsc --noEmit sin errores en finpal-mobile
📱 P1 no regresionado TC-P1-04 (login), TC-P1-08 (logout) y TC-P1-09 (dashboard) siguen PASS
🎯 8 user stories US-BUDGET-01→05, US-PERIOD-01→02, US-PREF-02 funcionales
🧱 Prerequisitos P3 React Hook Form + Zod instalados. Hooks useBudgets, useCategories listos (reutilizables en P3). BottomSheet reutilizable verificado.
📝 Spec actualizado Contadores en qa/index.html actualizados (10 nuevos PASS). P2-SIGN-OFF-REPORT.md generado.
🌐 API sin cambios Cero cambios al backend (todos los endpoints ya existían)
📦
Qué deja listo P2 para P3
P3 (Transactions) depende de P2 completo. Los hooks useCategories son reutilizados directamente en la pantalla de transacciones (para el selector de categoría). El BottomSheet verificado en P2 se usa para edición de transacciones. React Hook Form + Zod ya están instalados para los formularios de transacciones.
🔄
Nota de deuda técnica — Refresh en mobile
La deuda documentada en el P1 sign-off ya está resuelta. El código de POST /api/auth/refresh en el backend confirma que devuelve data: { accessToken: result.accessToken } en el body JSON. El interceptor de Axios en lib/api.ts ya tiene el código para capturar este valor. Sin acción pendiente.
🎯

User Stories — Budgets

US-BUDGET-01 a 05. Todas manejadas por el Agente B.

US-BUDGET-01 — Wizard de primer uso
"Como usuario nuevo, quiero que la app me guíe para crear mis primeros presupuestos."

Aparece cuando useBudgets(year, month).data?.length === 0. El wizard pasa por 6 pasos: bienvenida → perfil de gasto → categorías → montos → período → confirmación. Al completar, crea todos los budgets en batch y actualiza las preferencias de período. Tras completar, invalida ['budgets'] y el wizard desaparece.
US-BUDGET-02 — Agregar presupuesto individual
FAB en la pantalla de budgets. Abre NewBudgetSheet. Requiere: categoría (picker del servidor), monto, moneda. Si la categoría no existe, flujo inline de creación de categoría primero (llamar POST /api/categories), luego crear el budget con el nuevo categoryId.
US-BUDGET-03 + 04 + 05 — Editar / Renombrar / Eliminar
Tap en la card de un presupuesto abre EditBudgetSheet. La sheet tiene 3 acciones: Guardar (actualiza monto o nombre), Renombrar categoría (actualiza la categoría vía PUT /api/categories/:id — propagado a todas las transacciones por el backend), y Eliminar (con confirmación inline en la sheet antes de llamar DELETE /api/budgets/:id).
📆

User Stories — Períodos

US-PERIOD-01 y 02. Manejadas por el Agente C.

US-PERIOD-01 — Configurar tipo de período
Pantalla en Settings → Período. Opciones: Mes calendario (del 1 al último día del mes) o Día específico (ej. del 15 de un mes al 14 del siguiente). El backend usa UserPreference.periodType: 'calendar_month' | 'specific_day' y specificDay: number. Al guardar, el dashboard debe recalcular su período automáticamente al invalidar las preferencias.
US-PERIOD-02 — Ver fechas exactas del período
El PeriodNavigator en el dashboard actualmente muestra solo "Mar 2026". Con P2, debe mostrar también las fechas exactas: "1 mar – 31 mar" o "15 feb – 14 mar" según la configuración. Usa la función calculatePeriodDates() del lib/utils/periodCalculations.ts creado por el Agente A.
⚙️

User Stories — Preferencias

US-PREF-02. Manejada por el Agente C.

US-PREF-02 — Cambiar moneda predeterminada
En Settings, row "Moneda" muestra la moneda actual (ej. "DOP"). Al tocar, abre un BottomSheet con la lista de monedas disponibles. Al seleccionar → PUT /api/preferences con {defaultCurrency: 'USD'}. Cada presupuesto tiene su propio campo currency que el backend devuelve en el GET. Los nuevos presupuestos creados después del cambio usarán la nueva moneda predeterminada. Nota: el cambio de moneda no reconvierte presupuestos existentes — solo afecta los nuevos. Esto es el comportamiento correcto del backend.