Manual técnico de Mi Kuchubal

Identificación del documento

Campo Valor
Producto Mi Kuchubal
Tipo de documento Manual técnico del proyecto
Estado Borrador para revisión
Línea base funcional specs/001-grupos-ahorro-vsla/
Iteración activa de aceptación y release specs/002-cierre-release-mi-kuchubal/
Fecha de corte técnico 11/08/2028

Este manual describe la arquitectura, la preparación del entorno, el desarrollo y la operación técnica de Mi Kuchubal. No contiene contraseñas, llaves privadas, tokens, archivos de servicio, datos personales ni credenciales de firma.

Propósito

El documento busca que una persona con acceso autorizado al repositorio pueda:

Audiencia

El manual está dirigido a:

Se presupone conocimiento básico de Git, TypeScript, npm, Angular, APIs HTTP y Firebase. Las personas que trabajen con Android también necesitan familiaridad con Android Studio, Gradle y ADB.

Fuente de verdad y gobierno documental

Mi Kuchubal es un proyecto Spec-Driven. La autoridad se aplica en este orden:

  1. .specify/memory/constitution.md gobierna los principios y las puertas de desarrollo.
  2. specs/001-grupos-ahorro-vsla/ contiene el contrato funcional y de dominio vigente.
  3. Los archivos de specs/001-grupos-ahorro-vsla/contracts/ gobiernan payloads, rutas de datos, eventos, permisos e integraciones.
  4. specs/002-cierre-release-mi-kuchubal/ gobierna la aceptación, la evidencia y el release; no redefine el producto.
  5. CONVENTIONS.md gobierna nombres, capas técnicas y selección de componentes de interfaz.
  6. El código implementa esos contratos. Si existe una contradicción, se corrige primero la especificación o el contrato mediante una decisión explícita y luego la implementación.

Seguridad del documento

Los ejemplos de este manual usan nombres de variables o marcadores, nunca valores sensibles. Los siguientes elementos deben obtenerse únicamente mediante los canales aprobados por la organización:

La ausencia de uno de estos accesos debe registrarse como requisito pendiente o bloqueo de tipo secret, environment o human, según corresponda. Nunca debe reemplazarse con un valor inventado ni marcarse como evidencia superada.

Índice

  1. Introducción a Mi Kuchubal
  2. Metodología VSLA y decisiones de producto
  3. Arquitectura general
  4. Tecnologías y servicios
  5. Requerimientos técnicos
  6. Preparación del entorno
  7. Obtención y configuración del proyecto
  8. Estructura y convenciones
  9. Desarrollo local
  10. Arquitectura del frontend
  11. Arquitectura del backend
  12. Modelo de datos y persistencia
  13. Seguridad, privacidad y consentimiento
  14. Flujos funcionales
  15. Notificaciones
  16. Estrategia de pruebas y evidencia
  17. Compilación
  18. Ambientes, despliegue y CI/CD
  19. Release y aceptación
  20. Operación, monitoreo y auditoría
  21. Mantenimiento y evolución
  22. Solución de problemas
  23. Apéndices

1. Introducción a Mi Kuchubal

1.1 Descripción general

Mi Kuchubal es una solución para grupos comunitarios de ahorro que aplican la metodología de asociaciones comunitarias de ahorro y préstamo (Village Savings and Loan Associations, VSLA). Permite administrar miembros, reuniones, ahorros, fondo social, préstamos, pagos, multas de asistencia, correcciones, salidas y cierres de ciclo con trazabilidad digital.

El producto es una única aplicación Angular con capacidades condicionadas por rol y contexto:

Capacitor empaqueta el mismo bundle Angular para Android. No existe una segunda aplicación móvil ni un repositorio independiente para administración.

1.2 Objetivos

Los objetivos del sistema son:

1.3 Alcance funcional resumido

La línea base incluye:

1.4 Perfiles de usuario

Rol Alcance principal
miembro Consulta su información, la proyección comunal permitida y presenta solicitudes personales.
presidencia Mantiene su condición de miembro y comparte las capacidades digitales operativas de Junta Directiva.
secretaria Mantiene su condición de miembro y comparte las capacidades digitales operativas de Junta Directiva.
tesoreria Mantiene su condición de miembro y comparte las capacidades digitales operativas de Junta Directiva.
contabilidad Mantiene su condición de miembro y comparte las capacidades digitales operativas de Junta Directiva; no participa en las tres llaves.
admin Supervisa grupos, auditoría y reportes agregados sin acceso indiscriminado al detalle sensible individual.

Fuera del flujo independiente de tres llaves, los roles activos de Junta Directiva comparten las capacidades digitales de operación. El nombre del cargo describe su responsabilidad organizativa, pero no debe producir permisos digitales contradictorios.

1.5 Plataformas y límites

Plataformas soportadas:

Quedan fuera del alcance actual:

2. Metodología VSLA y decisiones de producto

2.1 Fundamentos del grupo de ahorro

La metodología de origen organiza a una comunidad que se reúne periódicamente para ahorrar cantidades pequeñas y flexibles, otorgar préstamos con los recursos disponibles y mantener fondos grupales o sociales. Cada grupo define colectivamente su frecuencia, el aporte mínimo, la duración del ciclo y las reglas de reparto.

El ahorro se registra por persona y por sesión. La metodología permite aportes diferentes entre miembros y propone múltiplos del monto mínimo acordado. Los registros individuales y grupales permiten verificar lo recibido, prestado, pagado y disponible.

2.2 Junta Directiva y control colectivo

En este manual, gobierno colectivo describe las reglas, el quórum y las decisiones adoptadas por el grupo; Junta Directiva describe los cargos con capacidades operativas y de revisión. No son sinónimos.

La práctica física contempla presidencia, tesorería, secretaría y personas responsables del conteo de efectivo. La caja usa tres candados cuyas llaves están en manos diferentes y se abre frente al grupo cuando existe participación suficiente.

Mi Kuchubal conserva el principio de separación de responsabilidades mediante una certificación digital de tres llaves:

2.3 Reuniones como frontera operativa

Las mutaciones financieras y las solicitudes que requieren aprobación colectiva se originan en una reunión en estado operaciones con quórum confirmado por el backend. El quórum mínimo es de dos tercios de los miembros activos.

Una solicitud personal puede registrar intención antes de la reunión, pero su revisión, certificación, desembolso y efecto financiero ocurren dentro de la reunión correspondiente. Si no hay quórum, no se habilitan operaciones y la reunión puede reprogramarse.

Los tipos contractuales son:

2.4 Ahorros, caja y fondo social

El saldo grupal conceptual sigue la relación:

saldo anterior + ahorros + multas cobradas + pagos de principal e intereses + ingresos del fondo social - desembolsos - gastos = saldo actual

En la aplicación, todo impacto de saldo debe dejar un evento inmutable. Una multa por tardanza o ausencia se crea primero como cobro pendiente; solo aumenta la caja cuando la Junta Directiva registra su pago.

El fondo social se mantiene separado de los beneficios por intereses. Un ingreso puede registrarse como operación de reunión. Un egreso entra a tres llaves y solo produce el movimiento gasto al completar la certificación y volver a comprobar el saldo disponible.

2.5 Préstamos

La metodología establece que los préstamos son opcionales, los decide el grupo y se financian con el dinero disponible. Mi Kuchubal aplica, entre otras, estas reglas de producto:

La mención de tres meses en el material metodológico no se usa como límite único de la aplicación. El contrato vigente aplica el mínimo regulatorio de 61 días y limita el máximo por configuración y ciclo.

2.6 Cierre y reparto

La reunión de reparto debe resolver los compromisos pendientes antes de cerrar el ciclo. El cierre utiliza la configuración vigente del ciclo y conserva una instantánea auditable de los resultados.

Los ahorros acumulados se devuelven íntegramente a cada miembro activo elegible. Sobre las demás bolsas se aplican reglas independientes:

Las reglas de intereses y neto comunal son independientes, por lo que pueden combinarse como equitativo/equitativo, equitativo/proporcional, proporcional/equitativo o proporcional/proporcional.

Cada bolsa se redondea a centavos y cualquier residuo se asigna de forma determinista para que el total distribuido coincida exactamente con el total de la bolsa. Un ciclo cerrado no vuelve a pagar sus resultados si un miembro sale posteriormente.

2.7 Decisiones digitales que prevalecen

Tema metodológico o legado Decisión vigente de Mi Kuchubal
Dos aplicaciones Un proyecto Angular con rutas por rol y un solo bundle.
Tres candados físicos Tres llaves digitales de personas distintas que certifican la decisión colectiva.
Dos contadores como cargos fijos Un rol contabilidad asignable a 0, 1 o 2 personas.
Aprobación social separada Revisión de Junta Directiva y tres llaves; no existe un estado social adicional.
PostgreSQL y SQLite Firestore para operación y BigQuery para eventos, auditoría y reportes.
Escritura sin conexión Consulta limitada mediante caché oficial; ninguna mutación se autoriza o encola offline.
Acciones desde una notificación La notificación únicamente abre una ruta; la acción se ejecuta dentro de la aplicación.
Corrección por edición o borrado Evento compensatorio y conservación del registro original.

3. Arquitectura general

3.1 Vista de contexto

Mi Kuchubal utiliza una arquitectura web/móvil serverless. El diagrama muestra las fronteras verificadas entre cliente, servicios Firebase, backend, BigQuery, entrega y validación local:

[Diagram]

La explicación corta es: la aplicación lee de Firestore, pero las operaciones de dominio pasan por la API. Los triggers y jobs llevan los cambios a BigQuery y envían recordatorios; la API consulta BigQuery para la bandeja y la administración. El cliente no accede directamente a BigQuery.

La aplicación cliente no es una segunda fuente persistente. Los Signals proyectan estado de lectura; Firestore conserva el estado operativo y el backend es la autoridad de mutación.

3.2 Capas lógicas

Aplicación cliente

apps/monederito/ contiene un proyecto Angular standalone con:

Contratos compartidos

shared/ publica @monederito/shared. Es la fuente común de tipos de dominio, DTOs, estados, rutas, validadores, errores, eventos y cálculos financieros consumidos por frontend y backend.

Backend

functions/ contiene una aplicación Express expuesta como una Cloud Function HTTP y funciones adicionales para tareas programadas y espejos de eventos. Sus responsabilidades incluyen:

3.3 Persistencia

Firestore es la base operacional para estado actual, documentos de grupo, miembros, reuniones, solicitudes, créditos, movimientos, configuración y eventos. El ciclo actual está embebido en el documento de grupo; no existe una colección de ciclos independiente.

BigQuery conserva filas de eventos e instantáneas históricas para reportes y auditoría. No debe utilizarse como autoridad transaccional inmediata. La bandeja de notificaciones en línea consulta envíos FCM auditados en BigQuery y combina su estado leído/no leído con recibos mínimos en Firestore.

3.4 Lecturas en tiempo real y modo offline

Los dominios colaborativos, como la reunión activa y el avance de aprobaciones, usan snapshots de Firestore y un store readonly por dominio. Las listas históricas paginadas usan lecturas puntuales para evitar listeners ilimitados.

La caché de Firestore permite consultar información capturada previamente, pero:

3.5 Límites de confianza

3.6 Despliegue

La configuración actual define:

El identificador heredado del proyecto Firebase sigue siendo coejuv-cedro. Los recursos nuevos controlados por el proyecto deben usar mi-kuchubal o, si no se admiten guiones, kuchubal.

4. Tecnologías y servicios

4.1 Versiones confirmadas

Las versiones siguientes provienen de los manifiestos actuales, no del documento de referencia:

Área Tecnología confirmada
Runtime Node.js 22
Lenguaje TypeScript 6.0
Frontend Angular 22, RxJS 7.8, Zone.js 0.15
UI híbrida Ionic 8
Android híbrido Capacitor 8
Cliente cloud Firebase JavaScript SDK 12
Backend Firebase Functions 7, Firebase Admin 13, Express 5
Datos analíticos Google Cloud BigQuery
Documentos generados PDFKit y ExcelJS
Pruebas Jasmine/Karma, Vitest, Mocha, Firestore Rules y Playwright
Android Gradle 8.13, Android Gradle Plugin 8.13, minSdk 24, compile/target SDK 36

4.2 Servicios Firebase y Google Cloud

4.3 Capacidades Android

Capacitor integra red, sistema de archivos, compartir, navegador, ciclo de la aplicación y push notifications. Los archivos generados se guardan temporalmente en caché y se entregan mediante el selector nativo de compartir. La compilación release no debe habilitar tráfico HTTP en claro; esa excepción existe únicamente en el manifiesto de depuración para pruebas contra emuladores locales.

5. Requerimientos técnicos

5.1 Hardware

El repositorio no fija mínimos de RAM, CPU o espacio en disco para desarrollo. Como requisito operativo, el equipo debe soportar simultáneamente Node.js, Angular, Firebase Emulator Suite y, cuando aplique, Android Studio o un dispositivo Android.

Para pruebas Android se necesita al menos una de estas opciones:

Los recursos exactos del equipo deben definirse en la política interna de desarrollo; no se consideran confirmados por el repositorio.

5.2 Software

Requerimientos:

El repositorio fija Gradle, Android Gradle Plugin y compatibilidad de compilación Java 21 mediante sourceCompatibility/targetCompatibility, pero no fija la distribución ni la versión del JDK en CI. Debe utilizarse un JDK compatible con Java 21 y documentar la distribución y versión seleccionadas en el entorno de CI o desarrollo.

5.3 Accesos y credenciales

Acceso Uso Estado esperado
Repositorio Git Código, specs y configuración Obligatorio
Firebase/Google Cloud Despliegues, lectura autorizada y servicios reales Asignado por la organización
Application Default Credentials Sembrado local desde documentos controlados o FCM físico aislado Solo cuando se ejecute ese flujo
GitHub Actions CI/CD y artefactos Según responsabilidad
google-services.json Integración Android con Firebase Fuera del control de versiones
Keystore y contraseñas Firma APK/AAB Pendiente hasta el gate de release; deben permanecer fuera del repositorio y del manual
Dispositivo/cuentas de prueba QA funcional y push Aprobados y sin PII real innecesaria

El desarrollo con fixtures completamente locales puede usar Emulator Suite sin credenciales productivas. El comando interactivo npm run dev:api incluye un sembrado controlado desde el proyecto real y, por ello, sí necesita credenciales autorizadas de lectura. El .gitignore raíz excluye activamente archivos .jks y .keystore; las reglas comentadas del .gitignore específico de Android no sustituyen esa protección.

5.4 Conocimientos recomendados

6. Preparación del entorno

6.1 Verificar el runtime

Desde la raíz del repositorio:

node --version
npx firebase --version

La versión mayor de Node debe ser 22. Si el equipo usa un administrador de versiones, la selección debe realizarse antes de instalar dependencias.

6.2 Instalar dependencias

El repositorio usa npm workspaces para apps/*, functions y shared:

npm ci

npm ci instala exactamente el árbol registrado en package-lock.json. No es necesario instalar Angular CLI, Firebase CLI o TypeScript de forma global para usar los scripts del proyecto.

6.3 Preparar Firebase local

La configuración principal inicia Authentication, Firestore, Functions y Pub/Sub. Pub/Sub es necesario para cargar las funciones programadas. Antes del modo local interactivo, la persona debe:

El script local configura MI_KUCHUBAL_E2E_ISOLATED=true, redirige FCM y BigQuery a archivos JSONL temporales y habilita consultas locales a ese outbox.

6.4 Preparar Android

Para compilar la aplicación híbrida:

  1. Instalar Android Studio, ADB y SDK Android 36.
  2. Confirmar que el JDK es compatible con Gradle 8.13 y Android Gradle Plugin 8.13.
  3. Mantener local.properties, google-services.json, keystores y contraseñas fuera del control de versiones.
  4. Conectar un dispositivo autorizado o crear un emulador con API 24 o superior.
  5. Recordar que iOS no forma parte del producto.

6.5 Validación inicial

Después de instalar dependencias, ejecutar:

npm run build:shared
npm run build:api
npm run build:app
npm run lint

Estos comandos validan compilación y tipos. Un resultado exitoso no equivale por sí solo a aceptación funcional, E2E, offline o Android.

7. Obtención y configuración del proyecto

7.1 Obtener el repositorio

La URL y el método de clonación deben suministrarse mediante el canal autorizado de la organización. Una vez obtenido, trabajar desde la raíz Git que contiene package.json, firebase.json, .specify/ y specs/.

No debe crearse una segunda carpeta de especificaciones fuera de esa raíz.

7.2 Configuración de workspaces

El package.json raíz declara:

Los scripts raíz coordinan esos workspaces. Se debe compilar shared antes de consumidores cuando se modifican sus exportaciones.

7.3 Configuración Firebase

La configuración versionada confirma:

Los archivos de entorno Angular ya separan producción, desarrollo, local y E2E. No se deben copiar al manual sus identificadores de cliente ni convertirlos en secretos nuevos. Cualquier cambio de proyecto, endpoint, claim, colección o recurso debe pasar primero por la especificación y los contratos.

7.4 Perfiles de frontend

Configuración Uso
production Bundle optimizado y servicios desplegados.
development Desarrollo Angular; su combinación con servicios reales/locales debe revisarse antes de usar datos.
local API y emuladores en los puertos principales del repositorio.
e2e Stack aislado en puertos alternos, sin servicios externos por defecto.

No debe mezclarse Auth de un stack con Functions o Firestore de otro: una prueba válida usa un entorno coherente.

7.5 Configuración Capacitor

La configuración actual define:

Después de cambiar el bundle web o plugins nativos, el equipo debe usar el flujo Capacitor/Android aprobado por el gate de release. Este capítulo no presupone que una firma de producción esté disponible.

8. Estructura y convenciones

8.1 Estructura principal

app/
├── apps/monederito/     Aplicación Angular, Ionic y Capacitor
├── functions/           API, cron, triggers, reportes y notificaciones
├── shared/              Dominio, DTOs, validadores, eventos y cálculos comunes
├── scripts/             Despliegue, setup, auditoría y pruebas E2E
├── specs/001-.../       Contrato funcional
├── specs/002-.../       Aceptación y release activos
├── .specify/            Constitución y plantillas Spec-Driven
├── firestore.rules      Reglas de seguridad
├── firestore.indexes.json
├── firebase.json        Configuración local y de despliegue
└── firebase.e2e.json    Stack aislado de pruebas

8.2 Frontend

Dentro de apps/monederito/src/app/:

Las rutas se cargan de forma diferida. Los claims controlan el estado de primer acceso y la separación de roles; el backend sigue siendo la autoridad final.

8.3 Backend

Dentro de functions/src/:

No deben añadirse rutas o payloads que no estén descritos por 001/contracts/.

8.4 Paquete compartido

shared/src/ separa:

La regla de simplicidad es eliminar duplicación real entre frontend y backend, no crear abstracciones especulativas.

8.5 Convenciones de nombres

Se deben expandir abreviaturas ambiguas: isJuntaDirectiva es preferible a isJD.

8.6 Convenciones Angular y UI

Las llamadas HTTP imperativas en el frontend usan los métodos Promise del BackendService, que normalizan errores en BackendError. No se añaden wrappers .catch(...) repetidos por funcionalidad.

8.7 Cambios contractuales

Antes de modificar comportamiento, permisos, roles, estados, datos, eventos, offline, consentimiento, finanzas o notificaciones:

  1. localizar la sección de 001 y el contrato aplicable;
  2. registrar la decisión o conflicto en research.md si corresponde;
  3. actualizar spec y contrato;
  4. actualizar shared/;
  5. implementar frontend/backend/reglas;
  6. ejecutar las pruebas proporcionales al riesgo;
  7. registrar la evidencia de release únicamente en 002.

9. Desarrollo local

9.1 Modo local interactivo

Antes de iniciar, comprueba que el runtime cumpla la compatibilidad indicada en la sección 5.2. Con Angular CLI 22, Node 24.10.0 falla incluso antes de levantar ng serve; usa Node 22.22.3+ o Node 24.15.0+.

Terminal 1, desde la raíz:

npm run dev:api

Este script compila Functions, inicia Auth, Firestore, Functions y Pub/Sub, activa adaptadores aislados y ejecuta un sembrado mínimo controlado. Requiere credenciales autorizadas porque lee documentos específicos del proyecto real; no copia usuarios de Authentication ni colecciones completas.

Terminal 2, desde la raíz:

npm run start -w monederito -- --configuration=local

El frontend local se conecta al mismo stack. No debe sustituirse por la configuración development sin comprobar sus destinos, porque ese perfil no conecta Auth y Firestore a los emuladores.

9.2 Puertos locales principales

Servicio Puerto
Emulator UI 4000
Functions 5001
Firestore 8080
Pub/Sub 8085
Authentication 9099

El script comprueba también el puerto 4400 usado por el hub de emuladores. Si un puerto está ocupado, se debe detener el proceso anterior en lugar de iniciar stacks superpuestos.

9.3 Perfil E2E aislado

firebase.e2e.json usa puertos alternos:

Servicio Puerto
Functions 5101
Firestore 8181
Pub/Sub 8185
Authentication 9199

La prueba financiera reproducible crea un directorio nuevo, activa outboxes y ejecuta en orden:

export ARTIFACT_DIR="/tmp/mi-kuchubal-e2e/current-$(date +%Y%m%d-%H%M%S)"
export MI_KUCHUBAL_E2E_ISOLATED=true
export MI_KUCHUBAL_E2E_NOTIFICATION_OUTBOX="$ARTIFACT_DIR/notifications.jsonl"
export MI_KUCHUBAL_E2E_BQ_OUTBOX="$ARTIFACT_DIR/bigquery.jsonl"
npx --no-install firebase emulators:exec --config firebase.e2e.json --only auth,firestore,functions \
  "node scripts/e2e/bootstrap-group.mjs '$ARTIFACT_DIR' && node scripts/e2e/prepare-members.mjs '$ARTIFACT_DIR' && node scripts/e2e/run-financial-flow.mjs '$ARTIFACT_DIR'"

Cada ejecución usa fixtures limpios y un directorio nuevo. Reutilizar datos o artefactos antiguos invalida la evidencia del candidato actual.

Este flujo financiero no invoca tareas onSchedule, por eso inicia solo Auth, Firestore y Functions. Si se necesita levantar el stack completo para una prueba que incluya Pub/Sub, usa:

npx --no-install firebase emulators:exec --config firebase.e2e.json --only auth,firestore,functions,pubsub \
  "node scripts/e2e/run-financial-flow.mjs '$ARTIFACT_DIR'"

Iniciar Pub/Sub no dispara por sí solo un cron; para validar una tarea onSchedule hace falta un runner o una invocación controlada específica para ese job. El runner financiero anterior no pretende demostrar esa ejecución.

9.4 Pruebas y validaciones frecuentes

npm run build:shared
npm run build:api
npm run build:app
npm run lint
npm --workspace monederito test -- --watch=false

npm run test -w monederito inicia Karma en modo watch y no es una validación de una sola ejecución. Al corte técnico de este checkout, npm run lint no termina correctamente: reporta que cycleId puede ser undefined en shared/src/finance/__tests__/effective-movements.test.ts y que cycleStartDate no existe en RegistrationGroupConfig dentro de shared/src/validators/__tests__/validators.test.ts. Estos errores deben resolverse antes de tratar lint como evidencia verde.

Para la línea base de release, 002 también confirma tipos directamente:

npm --workspace shared run build
./node_modules/.bin/tsc -p apps/monederito/tsconfig.app.json --noEmit
./node_modules/.bin/tsc -p apps/monederito/tsconfig.spec.json --noEmit
./node_modules/.bin/tsc -p functions/tsconfig.json --noEmit

Las suites de Functions, reglas y E2E se seleccionan según el cambio. Una modificación financiera exige pruebas de eventos, transacciones, permisos y conciliación; una modificación de interfaz no demuestra por sí sola que el backend cumpla el contrato.

9.5 Validación offline

La secuencia contractual es:

  1. cargar información autorizada en línea;
  2. retirar la conexión;
  3. reiniciar completamente la aplicación manteniendo el mismo origen/perfil;
  4. comprobar datos permitidos, indicador de obsolescencia y cero escrituras encoladas;
  5. reconectar;
  6. comprobar que el estado fresco reemplaza al cacheado.

La consulta offline no incluye la bandeja de notificaciones ni autoriza solicitudes, aprobaciones, reuniones o movimientos.

9.6 Android contra emuladores

El perfil E2E puede exponerse a un dispositivo conectado mediante ADB. Primero genera e instala el APK del perfil E2E:

npm --workspace monederito run build -- --configuration=e2e
cd apps/monederito
npx --no-install cap sync android
cd android
./gradlew assembleDebug
adb -s "$ANDROID_SERIAL" install -r app/build/outputs/apk/debug/app-debug.apk

Después redirige los puertos al dispositivo:

adb -s "$ANDROID_SERIAL" reverse tcp:5101 tcp:5101
adb -s "$ANDROID_SERIAL" reverse tcp:9199 tcp:9199
adb -s "$ANDROID_SERIAL" reverse tcp:8181 tcp:8181

La variable ANDROID_SERIAL debe identificar un dispositivo autorizado. El envío FCM real en un entorno aislado requiere credenciales específicas y una habilitación explícita; no forma parte del desarrollo local ordinario.

9.7 Diagnóstico básico

9.8 Límites de la validación local

Una compilación verde o una ejecución local satisfactoria no significa que el release esté aceptado. La iteración 002 exige un mismo candidato identificado, siete gates aprobados, H1-H10 con evidencia, cero defectos críticos abiertos y APK/AAB firmado con smoke test Android. Las credenciales, revisores o dispositivos ausentes permanecen como bloqueos explícitos.

10. Arquitectura del frontend

Mi Kuchubal utiliza un único proyecto Angular para las superficies de miembros, Junta Directiva y administración. La aplicación web y la aplicación Android comparten el mismo código funcional: Capacitor empaqueta el bundle Angular para Android y aporta solamente las integraciones nativas necesarias. No existen aplicaciones separadas por rol ni una implementación móvil paralela.

La arquitectura del frontend distingue tres responsabilidades: presentar información, consultar proyecciones de lectura y solicitar mutaciones al backend. Esta separación es especialmente importante en las operaciones financieras: el cliente puede mostrar y preparar una operación, pero no decide por sí mismo si la operación es válida ni escribe directamente el resultado financiero.

10.1 Superficies y navegación

Las rutas se cargan de forma diferida y están protegidas por guards de autenticación y claims. Las superficies principales son:

El frontend diferencia la pertenencia financiera de las capacidades operativas. Todo registro activo en groups/{groupId}/members/{uid} conserva su alcance personal de ahorro, deuda, préstamos, multas y estado de cuenta. Los roles presidencia, secretaria, tesoreria y contabilidad agregan capacidades de Junta Directiva; no reemplazan el alcance de miembro.

10.2 Organización por capas

Componentes y páginas

Las páginas Angular standalone se organizan por funcionalidad. Cada página administra su estado de presentación, formularios y navegación, pero delega el acceso remoto a servicios. Los componentes compartidos concentran patrones visuales repetidos —encabezados, tarjetas, listas resumen, botones, formularios, hojas y banners— para mantener una experiencia consistente.

La capa visual dominante es HTML y SCSS con componentes standalone. Ionic se reserva para shell, navegación híbrida, modales o sheets, teclado, botón Atrás y puentes nativos. Angular Material se utiliza en superficies administrativas densas, como tablas, paginación, ordenamiento, diálogos y selectores complejos. Una pantalla mantiene una capa visual dominante; no se abstraen Ionic, Material y HTML detrás de un único wrapper genérico.

Servicios de acceso a datos

BackendService centraliza las llamadas HTTP. Sus métodos de promesa convierten errores HTTP en un error común que preserva mensaje, estado y payload del backend. El interceptor de autenticación adjunta el token Firebase y el interceptor de tiempo limita las solicitudes para que una falla de transporte no deje el formulario bloqueado indefinidamente.

GroupsService reúne la integración de la superficie de grupo: comandos HTTP, consultas Firestore autorizadas y observadores en tiempo real. Los componentes no deben duplicar URLs, listeners ni reglas de interpretación de datos.

Estado reactivo compartido

El frontend usa Signals y recursos RxJS para publicar proyecciones de solo lectura. Los stores raíz más relevantes son:

Cada store cancela o reemplaza sus observadores cuando cambia la autenticación o el groupId. De esta forma no se conserva estado del grupo anterior ni se multiplican listeners por cada componente consumidor.

10.3 Frontera entre comandos y proyecciones

El frontend aplica una separación deliberada:

Después de un comando exitoso, la UI no debe construir localmente un estado financiero “optimista” como si fuera definitivo. Espera que Firestore publique el documento confirmado por el servidor. Esta regla permite que dos dispositivos abiertos en la misma reunión converjan sin recargar la página y evita que una respuesta parcial sustituya el documento completo.

10.4 Sincronización de reuniones y aprobaciones

La reunión activa se observa con un único listener Firestore compartido. El snapshot informa si proviene de caché. Una pantalla puede mostrar información almacenada, pero no habilita acciones privilegiadas mientras el estado esté cargando, provenga solamente de caché o exista un error de sincronización.

Las vistas que mutan una reunión evalúan una política común con estos datos:

Si un snapshot confirmado demuestra que la reunión de la ruta ya no es la activa o ya no admite la acción, la vista vuelve a /grupo/reunion con una explicación. La validación vuelve a ejecutarse inmediatamente antes del comando, y el backend revalida todo dentro de su frontera transaccional.

Las aprobaciones no usan una bandeja ni una colección adicional. ApprovalRealtimeStore consulta las colecciones de solicitudes existentes filtradas por sourceMeetingId. De allí deriva el número de validaciones, las firmas pendientes y el progreso de Secretaria, Tesoreria y Presidencia.

10.5 Autenticación y resolución de rutas

Al iniciar o refrescar sesión, el frontend obtiene el contexto Firebase y aplica el orden obligatorio:

  1. Si mustChangePassword=true, solo permite la ruta de cambio de contraseña.
  2. Si consentAccepted=false, dirige al consentimiento.
  3. Si blocked=true o el usuario está inactivo, deniega el acceso y cierra la sesión.
  4. Cuando la cuenta está lista, resuelve la superficie por role y groupId.

Los guards mejoran la experiencia y evitan mostrar controles indebidos, pero no son una frontera de seguridad suficiente. El backend y las reglas Firestore vuelven a verificar identidad, preparación, rol y grupo en cada acceso.

10.6 Manejo de archivos generados

Una única frontera, GeneratedFileService, recibe un Blob y el nombre de archivo:

Los componentes no crean enlaces de descarga, archivos de Capacitor ni intents de compartir por su cuenta. PDF y XLSX siguen el mismo flujo de entrega sin regenerar el contenido en el cliente.

10.7 Manejo de fallos

La interfaz distingue estados de carga, vacío, error, datos desde caché y datos confirmados por servidor. Un documento ausente no se interpreta como saldo cero. Una falla de red no autoriza reintentos automáticos de escrituras financieras ni convierte la pantalla en un modo editable sin conexión.

Las acciones fallidas conservan el contexto necesario para corregir o reintentar. Los mensajes muestran lenguaje humano y evitan exponer excepciones del navegador, rutas internas, hashes, identificadores técnicos o enumeraciones crudas como explicación principal.

11. Arquitectura del backend

El backend se implementa con TypeScript, Express y Cloud Functions. Una función HTTP expone la API y monta módulos de rutas para autenticación, administración, grupos, miembros, reuniones, préstamos, créditos, ajustes, gastos de fondo social, salidas, reportes, configuración y ciclos. Las tareas programadas y los triggers Firestore se despliegan como funciones independientes.

11.1 Responsabilidades del backend

Para las operaciones de grupo, financieras y gobernadas, el backend es la autoridad exclusiva para:

Los clientes Firestore no crean ni modifican reuniones, movimientos, créditos, solicitudes, configuración operacional ni estados financieros. Las variables globales y algunas acciones administrativas siguen la política específica de Rules y backend.

11.2 Middlewares comunes

La cadena HTTP verifica el token Firebase y construye un contexto autenticado con uid, role, groupId, mustChangePassword, consentAccepted y blocked. Además consulta el estado persistido del usuario para rechazar cuentas inactivas o bloqueadas.

Los middlewares reutilizables aplican tres límites:

Los handlers devuelven respuestas estructuradas para errores esperados, con estado HTTP y cuerpo. Las excepciones inesperadas se delegan a la cadena Express mediante next(err); no existe en este checkout un serializador JSON de errores inesperados propio del backend.

11.3 Mutaciones transaccionales

Las operaciones financieras y las decisiones gobernadas se ejecutan con transacciones o lotes atómicos de Firestore. En las rutas críticas se vuelven a leer, dentro de la misma frontera, las condiciones relevantes: ciclo activo, versión de configuración, reunión, quórum, miembro, fondos, obligaciones, estado de la solicitud e idempotencia.

Las escrituras financieras del dominio incluyen su evento requerido en la misma transacción. Si el evento no puede crearse, tampoco se confirma la mutación. Las operaciones concurrentes que podrían competir por caja o por el cierre utilizan el documento del grupo como cerca de serialización; por ejemplo, un desembolso y un cierre de ciclo no pueden finalizar ambos sobre el mismo saldo previo.

11.4 Motor reutilizable de tres llaves

Un motor común gobierna préstamos, correcciones, anulaciones, gastos de fondo social, configuración del siguiente ciclo y cambios de la fecha de cierre activa. Cada recurso conserva su propio documento y efecto final; el motor comparte las validaciones de identidad, grupo, rol, reunión, quórum, actor distinto, hash e idempotencia.

El contrato 001 y el runtime actual implementan firmas de orden libre, sujetas al conflicto constitucional identificado al inicio del manual. La finalización requiere exactamente una llave de cada rol canónico —secretaria, tesoreria y presidencia— aportada por tres usuarios distintos. El rol proviene del token, no del body. Cada solicitud certifica un approvalPayload inmutable mediante hash y versión; un documento incompleto o cambiado falla cerrado.

La tercera llave y el efecto de finalización se confirman atómicamente. Según el recurso, ese efecto puede dejar un préstamo listo para desembolso, ejecutar una corrección, publicar un gasto, programar configuración futura o cambiar la fecha de cierre activa.

11.5 Eventos y sincronización con BigQuery

Los eventos obligatorios se escriben en subcolecciones events junto con la mutación de origen. Cada evento lleva alcance explícito: cycleId, configVersion y meetingId, usando null solo cuando realmente no pertenece a un ciclo o reunión.

Triggers Firestore reflejan los eventos en kuchubal.events y marcan bqSyncedAt después de insertar. La entrega es asíncrona y usa event_id/insertId y bqSyncedAt para deduplicación y reintento; la deduplicación de BigQuery es best-effort y el código tolera un duplicado excepcional si la marca falla después de insertar. Los triggers se despliegan con reintentos. Una reconciliación programada busca eventos antiguos aún no sincronizados; un backfill permite recuperar registros históricos. El éxito de la mutación de dominio depende de que exista su evento Firestore, no de que BigQuery responda en ese instante.

Otros triggers conservan snapshots de grupos, miembros, configuración y usuarios en las tablas históricas. Las vistas *_latest seleccionan la versión más reciente para las listas administrativas.

11.6 Procesos programados

Las tareas recurrentes usan onSchedule del SDK oficial. El checkout actual exporta recordatorios de reunión de un día y una hora, recordatorio de cuota y reconciliación de eventos pendientes con BigQuery. El contrato también exige arrearsCalculator y cycleCutoffCheck, pero todavía no están exportados por functions/src/index.ts; deben tratarse como trabajo pendiente, no como procesos operativos. Los horarios de negocio se interpretan en America/Guatemala; los timestamps de auditoría permanecen como instantes.

En el entorno local aislado, FCM y BigQuery se sustituyen por outboxes de prueba. Esto permite validar payloads y eventos sin contactar servicios externos.

11.7 Idempotencia y consistencia

Los eventos conservan un identificador después de escribirse; algunos se derivan determinísticamente de la operación y otros se generan con UUID. Las inserciones en BigQuery reutilizan ese identificador como insertId, con la garantía best-effort descrita arriba. Las operaciones que pueden repetirse —firmas, inicio de ciclo, traspaso de fondo social o desembolso mensual— conservan marcadores deterministas para no duplicar efectos.

La consistencia entre Firestore y BigQuery es eventual y no convierte a BigQuery en autoridad transaccional. Firestore confirma la operación actual; BigQuery recibe la evidencia y los snapshots para auditoría, historia y consultas consolidadas.

12. Modelo de datos y persistencia

12.1 División de almacenamiento

Responsabilidad Firestore BigQuery
Estado operacional actual Fuente primaria No autoriza operaciones
Reuniones y aprobaciones en tiempo real Fuente de lectura persistida No se usa como fuente en vivo
Mutaciones Las rutas oficiales usan backend y aplican transacciones o lotes atómicos cuando la operación lo requiere No recibe comandos del cliente
Auditoría e historia Eventos append-only y documentos actuales Eventos y snapshots históricos consultables
Listas administrativas consolidadas Origen de las mutaciones Vistas *_latest
Notificaciones Token, preferencias y recibos de lectura; no conserva la bandeja ni su contenido Entregas FCM exitosas retenidas

Esta división evita usar un almacén analítico como base transaccional y, al mismo tiempo, conserva evidencia suficiente para reconstruir estados de cuenta, caja, créditos, cambios de configuración e historial de usuarios.

Existe una excepción de autorización pendiente de cerrar: las reglas actuales permiten a un usuario administrativo listo crear o actualizar directamente globalVariables. La interfaz oficial usa el endpoint backend, pero la garantía «toda mutación pasa por backend y deja auditoría» no queda impuesta por Firestore mientras ese permiso directo continúe habilitado.

12.2 Identidad de grupo, ciclo y fechas

El alias del grupo en mayúsculas es el groupId y también el identificador del documento groups/{groupId}. Un ciclo se identifica exclusivamente como <groupId>-cycle-<sequence>. La secuencia positiva y la versión aplicable forman parte explícita de los documentos operacionales sujetos al ciclo, como movimientos, créditos, reuniones y solicitudes de préstamo, ajuste o gasto social. configChangeRequests y cycleDateChangeRequests guardan sourceCycleId; su versión se valida contra el ciclo y queda preservada en los eventos derivados, pero no existe como campo propio de esos dos documentos de solicitud.

No existe una colección cycles. El ciclo actual está embebido en el grupo y el historial se reconstruye mediante eventos y snapshots. Un identificador mal formado, de otro grupo o con secuencia inconsistente es un error de integridad; nunca se corrige infiriéndolo por fecha ni sustituyéndolo por el ciclo actual.

Las fechas de negocio se reciben como YYYY-MM-DD y se guardan a medianoche de Guatemala. Los horarios de reuniones usan un HH:mm separado y el backend construye el instante scheduledAt. createdAt, updatedAt, firmas y demás auditoría son timestamps de servidor.

cycleStartDate no es una configuración editable. El backend lo deriva al registrar el grupo o iniciar el siguiente ciclo. durationMonths puede ayudar a estimar una fecha en la UI, pero no viaja en payloads ni se persiste.

12.3 Documentos raíz

Variables globales

globalVariables/{key} concentra el catálogo canónico de razones, límites/defaults del sistema y banderas conocidas. El endpoint administrativo oficial escribe la variable y su evento en una transacción. Las reglas vigentes también permiten escritura directa a un administrador listo; esa vía no garantiza el evento y debe cerrarse si se quiere imponer auditoría completa. Los límites globales actúan como techo o cota para la configuración de cada grupo; no sustituyen las decisiones financieras del grupo.

Credenciales de alias

authCredentials/{userId} almacena material de credencial solamente para usuarios de grupo. Es una colección exclusiva del servidor: las reglas niegan toda lectura y escritura de cliente. No guarda la contraseña en texto claro.

Usuarios

users/{userId} conserva alias, rol principal, grupo, origen de creación, año de nacimiento, estado de credencial, flags reflejados, consentimiento y preferencias de notificación. Los usuarios administrativos pueden tener correo; los usuarios de grupo usan alias y mantienen email=null.

users/{userId}/events conserva el ciclo de vida del usuario. users/{userId}/notificationReads almacena únicamente recibos de lectura de notificaciones retenidas, nunca el contenido de la notificación.

12.4 Agregado de grupo y configuración

groups/{groupId} conserva identidad, ubicación, estado y currentCycle. El ciclo embebido registra secuencia, configuración aplicable, fechas, estado, resultado de distribución y decisiones de continuación/fondo social.

groups/{groupId}/config/current conserva la configuración activa y, cuando ya fue certificada, pendingNextCycle. La configuración pendiente es visible a usuarios listos del grupo, pero no modifica el ciclo que se está cerrando. Solo se promueve al iniciar el siguiente ciclo.

Las solicitudes configChangeRequests y cycleDateChangeRequests conservan el voto, el meeting de origen, el hash y las llaves. La primera programa reglas para el siguiente ciclo; la segunda cambia únicamente la fecha de cierre del ciclo activo.

12.5 Miembros, reuniones y obligaciones

members enlaza usuario, rol y estado de participación. La salida cambia el estado a inactivo; no elimina documentos.

meetings conserva ciclo, versión de configuración, tipo, estado, horario, quórum y el corte contable de apertura. attendance registra una fila por cada registro recibido. El backend comprueba que cada identificador enviado sea único y corresponda a un miembro activo, pero actualmente no exige que la solicitud incluya a todos los miembros activos. El denominador del quórum sí usa el total de miembros activos. Una tardanza o ausencia enviada puede crear un fineCharge pendiente; la multa no cambia la caja hasta ser cobrada.

memberExitRequests conserva la intención y su resolución. Mientras esté pendiente no cambia membresía ni saldo.

12.6 Libro financiero

movements es el libro operacional de asientos no eliminables. Sus valores económicos y referencias originales se conservan; una corrección o anulación puede cambiar el status del asiento original y agregar un nuevo movimiento compensatorio o de reversión. Cada fila conserva tipo, estado, ciclo, versión, miembro opcional, reunión, monto, moneda, balanceImpact, descripción y referencias de trazabilidad.

Las reglas principales son:

status=vigente significa que el asiento sigue siendo efectivo en el libro. No significa que una deuda siga abierta. Por eso los movimientos vigentes de ciclos anteriores permanecen en el historial.

12.7 Préstamos y cuotas

loanRequests conserva la solicitud, su validación, subjectMemberId, reunión de origen, estado, payload certificado, llaves y marcadores de desembolso. Solo puede existir una solicitud no terminal por miembro.

credits conserva términos fijos aprobados, saldo de capital, interés único, total contractual, número de cuotas, ronda de inicio, siguiente cuota, fecha de vencimiento contractual y resumen de mora. Cada installment conserva su número, ronda de vencimiento, componentes, total, monto pagado y estado.

El modelo vigente es fixed_upfront_v2: el interés se calcula una vez; pagos parciales o anticipados no recalculan tasa, calendario ni fecha contractual.

12.8 Solicitudes gobernadas

adjustmentRequests certifica correcciones o anulaciones sin eliminar ni modificar los valores económicos del asiento original; al ejecutarse sí actualiza su status y crea el movimiento compensatorio o de reversión. socialFundExpenseRequests conserva el gasto propuesto y crea el movimiento gasto solamente al completarse la tercera llave.

Las solicitudes sujetas al motor compartido de tres llaves —préstamo, ajuste, gasto de fondo social, configuración del siguiente ciclo y cambio de fecha de cierre— usan el mismo esquema conceptual: estado, reunión de origen, sujeto personal o decisión grupal, payload canónico, hash, versión y llaves. Otras solicitudes controladas, como memberExitRequests, siguen su propio contrato. El contexto humano de aprobación se construye como una proyección de lectura; no se persiste otro documento duplicado.

12.9 BigQuery

La tabla events contiene el rastro unificado de entidades, actores, estados, razones, alcance de ciclo/reunión e impactos financieros. Se particiona por fecha del evento y se agrupa por grupo y tipo de entidad.

Las tablas groups_history, members_history, group_configs_history y users_history guardan snapshots en cada escritura. Las vistas groups_latest, members_latest, group_configs_latest y users_latest seleccionan el estado más reciente para administración y exportaciones.

notifications contiene los envíos FCM exitosos cuya inserción de auditoría también tuvo éxito. La bandeja consulta únicamente filas de los últimos 365 días y los recibos de lectura vencen con esa misma ventana. login_events tiene una política contractual de 365 días. Sin embargo, el DDL actual no declara expiración de particiones ni una tarea de limpieza para ninguna de esas dos tablas; por ello, la eliminación física a los 365 días no queda garantizada por el repositorio y debe verificarse en la configuración del servicio.

Los eventos financieros, consentimientos, snapshots operativos y auditoría administrativa tienen retención permanente según el contrato actual y el DDL no les configura expiración. La configuración efectiva de retención en BigQuery debe comprobarse en el proyecto desplegado antes de tratarla como evidencia operacional.

13. Seguridad, privacidad y consentimiento

13.1 Modos de autenticación

Mi Kuchubal admite dos formas de inicio de sesión:

La sesión usa la persistencia y renovación del SDK Firebase. No existe un temporizador propio que cierre sesiones después de un número fijo de minutos. La sesión termina por cierre voluntario, revocación, inactividad lógica, cambio de credencial o bloqueo.

13.2 Claims y cuenta lista

Los claims canónicos son role, groupId, mustChangePassword, consentAccepted y blocked. Los tres controles de preparación se aplican tanto en el frontend como en el backend:

Después de cambiar claims, el cliente fuerza su actualización para no continuar con un token obsoleto. El documento users/{uid} mantiene un espejo operativo para supervisión y para denegar también cuentas inactivas.

13.3 Modelo de autorización

Actor Datos personales Datos comunales Operaciones Administración
Miembro activo Solo los propios Totales y feed saneado Solicitudes personales en línea No
Junta Directiva Datos propios más contexto operacional del grupo Detalle necesario para reunión y cierre Sí, dentro de reunión y reglas No
Admin No recibe detalle sensible individual en vistas consolidadas Agregados y auditoría autorizada No opera reuniones como JD Usuarios, grupos, variables, bitácora y exportes

Fuera del flujo independiente de tres llaves, los cuatro roles de Junta Directiva tienen las mismas capacidades digitales de operación. En el flujo de tres llaves solo firman Secretaria, Tesoreria y Presidencia.

Las autorizaciones de cliente son aditivas: un miembro que ocupa un cargo de Junta Directiva conserva sus datos propios. El rol no habilita acceso a otro grupo.

13.4 Fronteras de lectura

Las reglas Firestore verifican sesión lista, grupo y rol. Los miembros pueden consultar sus documentos propios; Junta Directiva obtiene lecturas operacionales del grupo; administración accede únicamente donde el contrato lo permite. Las escrituras de documentos críticos permanecen denegadas a clientes.

Para la lista comunal de movimientos no basta con confiar en reglas por documento, porque Firestore no aplica filtrado de campos. El backend construye una respuesta saneada que excluye identidad, descripción, metadata, referencias internas y recibos de otros miembros. El acceso administrativo consolidado aplica el mismo principio: muestra agregados, no detalle financiero personal.

13.5 Minimización de datos

La identidad de un miembro se limita a alias, año de nacimiento y credenciales. Están fuera del modelo actual DPI, nombre legal completo, dirección, teléfono, contactos del dispositivo, GPS y otros datos intrusivos.

Los payloads push de aprobación y salida evitan alias, montos, ahorros, propósito, razón, votos y valores de configuración. La notificación solo informa que existe una tarea y dirige a una pantalla autenticada, donde se vuelve a comprobar el acceso.

13.6 Consentimiento

El consentimiento de uso y privacidad se captura antes de habilitar la aplicación. El backend deriva la edad exclusivamente de birthYear; si falta o es inválido, falla cerrado.

Para una persona menor de 18 años se exige además una declaración separada de aprobación parental o de tutor. No se recopilan credenciales del tutor. La aceptación genera eventos consent_accepted y, cuando corresponde, minor_consent_accepted, además de actualizar el espejo del usuario.

La versión y las rutas de documentos legales provienen de la configuración global pública limitada. La ruta /politicas puede mostrar valores de respaldo incluidos en la aplicación si esa consulta no está disponible; no crea otra fuente de configuración.

13.7 Contraseñas y credenciales temporales

Al crear un participante, el backend genera una contraseña temporal y la devuelve una sola vez. El texto claro no se almacena en Firestore ni BigQuery. El usuario elige su contraseña definitiva al primer ingreso.

Junta Directiva puede requerir cambio de contraseña únicamente a otro miembro activo de su grupo; administración puede hacerlo dentro de su alcance. La operación actualiza la credencial correspondiente, activa el claim obligatorio, revoca sesiones y registra password_change_required.

13.8 Conservación e inmutabilidad

Un miembro que sale pasa a estado inactivo y pierde acceso operacional, pero su historia financiera, eventos, consentimiento y snapshots se conservan. Las correcciones nunca borran el registro original. Esta retención permite auditoría y reconstrucción, pero obliga a limitar las vistas según propósito y rol.

14. Flujos funcionales

14.1 Registro y preparación del grupo

El registro público crea, como una sola operación funcional, el grupo, su configuración inicial, el ciclo 1 y la primera persona con rol presidencia. El alias del grupo se normaliza en mayúsculas. La fecha de inicio del ciclo la deriva el backend con el calendario de Guatemala; la fecha de cierre debe ser posterior.

Si se proporciona una primera reunión, fecha y hora son obligatorias y se crea en estado programada. El registro captura el consentimiento de privacidad de Presidencia y, si corresponde por edad, la declaración adicional para menor.

Después, cualquier rol activo de Junta Directiva puede incorporar participantes. El orden de preparación exige Tesoreria y Secretaria; luego puede agregarse hasta dos personas de Contabilidad y miembros hasta el límite global. Cada participante recibe alias completo, contraseña temporal y los claims de su grupo.

14.2 Ciclo de una reunión

Los estados canónicos son:

programadaen_cursooperacionesfinalizada

Si no se alcanza quórum, el recorrido es:

en_cursosin_quorumreprogramadaprogramada

cancelada es final y de consulta. Los nombres heredados abierta y cerrada son inválidos.

Una reunión puede abrirse únicamente en su fecha de Guatemala. Al abrirla, el backend registra openedAt y openingBalance en la misma transacción; este es el corte contable de la reunión. La hora programada informa, pero no impide abrir antes durante la fecha correcta.

La asistencia acepta solo miembros activos. presente y tardanza cuentan para quórum; la proporción debe ser al menos dos tercios. La solicitud completa se rechaza si contiene un miembro inexistente o inactivo. Con quórum, la reunión pasa a operaciones; sin él pasa a sin_quorum y no admite movimientos.

Una reunión ordinaria que alcanza operaciones incrementa una sola vez la ronda de pagos del ciclo. Reuniones extraordinarias, de reparto, canceladas o sin quórum no la incrementan.

14.3 Reprogramación e historial

Junta Directiva puede reprogramar una reunión programada o sin_quorum. Se actualiza la fecha/hora, se reinician asistencia, quórum y corte de apertura, pero no se eliminan multas pendientes generadas por un intento anterior.

Cuando no hay reunión activa, el formulario de programación permite elegir ordinaria, extraordinaria o cierre de ciclo (reparto). Puede sugerir una fecha según la frecuencia y la última reunión ordinaria finalizada, pero la sugerencia es editable y nunca crea automáticamente la reunión.

Mientras una reunión programada está visible, Junta Directiva puede consultar hasta las cinco reuniones finalizadas más recientes y navegar al historial paginado. La consulta histórica es puntual, no un listener ilimitado.

14.4 Ahorros, asistencia y fondo social

Durante operaciones, Junta Directiva registra aportes de ahorro de miembros activos. El monto debe ser positivo y múltiplo del mínimo configurado. Registrar cero acciones significa “sin aporte” y no llama al backend ni crea un movimiento.

Al registrar asistencia, una tardanza crea un cargo pendiente con lateFeeAmount y una ausencia con absenceFeeAmount. Un valor cero no crea cargo. El cargo pendiente no cambia la caja; al cobrarlo durante una reunión con quórum se crea el movimiento multa y el cargo pasa a pagada.

Un ingreso de fondo social crea un movimiento fondo_social. Un egreso no crea inmediatamente gasto: primero crea una solicitud grupal pendiente_3_llaves. La tercera llave vuelve a comprobar miembro asociado y saldo disponible, y solo entonces publica el gasto de forma atómica.

14.5 Solicitud y aprobación de préstamo

Un miembro activo, incluso si también pertenece a Junta Directiva, solicita su propio préstamo en línea. El backend determina memberId desde la sesión y registra amountRequested, installmentCount y propósito. No existe prestatario externo ni selección de otra persona beneficiaria.

La solicitud se rechaza si hay un crédito activo o en_mora, otra solicitud no terminal, un desembolso en el mismo mes de Guatemala, monto superior al múltiplo de ahorro o tope nominal, fondos insuficientes, plazo inválido, madurez menor a 61 días o fecha dentro de los dos meses finales del ciclo.

Una solicitud válida inicia en pendiente_revision_jd. Durante una reunión en operaciones con quórum, Junta Directiva puede rechazarla con razón, etapa, actor y fecha, o enviarla a pendiente_3_llaves vinculándola de forma inmutable mediante sourceMeetingId.

Según el contrato 001 y el runtime actual, las tres personas canónicas firman en cualquier orden; aplica el conflicto constitucional indicado en la sección de gobierno docu