Resumen Ejecutivo
P2 es la fase de Budgets Core — el feature principal del app.
POST /api/auth/refresh devuelve data.accessToken en el body JSON además del httpOnly cookie.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.react-hook-form + zod (prerequisito no instalado aún)2. Extender
lib/api.ts con todos los endpoints de budgets/categories/preferences3. Crear hooks TanStack Query:
useBudgets, useCategories, usePreferences + mutaciones4. 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)
| ID | Nombre | Módulo | Componente 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.
https://candidates-ashley-birth-sensitivity.trycloudflare.com. Solo el mobile necesita trabajo.data.accessToken en body JSON (además del httpOnly cookie)✅{ 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.
Inventario de Componentes
Qué existe, qué falta, qué necesita ser actualizado.
| Componente / Archivo | Estado | Acción en P2 |
|---|---|---|
components/ui/BudgetWizard.tsx |
Pre-built | Conectar props onComplete → API (batch create budgets + preferences) |
components/ui/NewBudgetSheet.tsx |
Pre-built | Conectar onSave → useCreateBudget mutation |
components/ui/EditBudgetSheet.tsx |
Pre-built | Conectar onSave → useUpdateBudget, onDelete → useDeleteBudget |
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).
- 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
useBudgets(year, month)→useQuery(['budgets', year, month])— staleTime: 30suseCreateBudget()→useMutation+ invalidate['budgets']useUpdateBudget()→useMutation+ invalidate['budgets']useDeleteBudget()→useMutation+ invalidate['budgets']- Todas las mutaciones hacen invalidate optimista del query key correspondiente
useCategories()→useQuery(['categories'])— staleTime: 60suseCreateCategory()→useMutation+ invalidate['categories']y['budgets']useUpdateCategory()→useMutation+ invalidate ambosuseDeleteCategory()→useMutation+ invalidate ambos
usePreferences()→useQuery(['preferences'])— staleTime: ∞ (solo invalida al update)useUpdatePreferences()→useMutation+ invalidate['preferences']y['budgets']
- 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.asserto jest si el proyecto lo tiene
- TypeScript compila sin errores:
npx tsc --noEmit api.budgets.list(2026, 3)retorna array de budgets desde el backend realapi.preferences.get()retorna objeto de preferencias del usuario autenticadocalculatePeriodDates({periodType:'specific_day', specificDay:15}, 2026, 3)retorna {startDate: 2026-02-15, endDate: 2026-03-14}
- Usar
useBudgets(year, month)para cargar la lista - Pasar el
yearymonthdesde elPeriodNavigator(sincronizado con el dashboard) - Renderizar lista de
BudgetCardcon botón de editar por card - FAB (+) en esquina inferior derecha → abre
NewBudgetSheet - Toque en card → abre
EditBudgetSheetpara 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
- Al completar wizard: llamar
useCreateBudgeten batch para cada categoría seleccionada - Llamar
useUpdatePreferencescon 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)
onSave(data)→ llamaruseCreateBudget.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
useCreateCategoryprimero, 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
- 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
DeleteConfirmationque ya existe en EditBudgetSheet - Verificar cancelación (TC-P2-06): cerrar sheet sin llamar la API
- 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 ✓
- 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/preferencescon{periodType, specificDay?} - Al guardar: invalidar
['preferences']y['budgets'] - Navigación: agregar link en
settings/index.tsx→/settings/period
- 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.
- Agregar row "Moneda" en
app/(tabs)/settings.tsx - Al tocar: abrir
BottomSheetcon lista de monedas: DOP, USD, EUR (+ otras si existen en el backend) - Selección →
PUT /api/preferencescon{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
currencyen cada budget)
- 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" ✓
https://candidates-ashley-birth-sensitivity.trycloudflare.com- 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)
- 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
- 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.mden la raíz del proyecto
Secuencia de Ejecución
A es bloqueante. B y C se pueden ejecutar en paralelo. D al final.
| Agente | Depende de | Desbloquea | Estimado |
|---|---|---|---|
| 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.
| Criterio | Descripció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) |
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.
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.
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.
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.
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.
UserPreference.periodType: 'calendar_month' | 'specific_day' y specificDay: number. Al guardar, el dashboard debe recalcular su período automáticamente al invalidar las preferencias.
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.
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.