Docs BookMind
07
SRD Backend — BookMind
Arquitectura FE multi-país, GL engine, integración bancaria y motor de tesorería
Versión
v1.0
Fecha
Mayo 2026
Audiencia
Founders · Dev
Estado
DRAFT
BOOKMIND
El cerebro y las manos de tu finanza.
Módulo #3 — Cheryx Suite

[CONSEJO — Doc 07] Contrarian: El path crítico del backend de BookMind no es el motor de tesorería — es la emisión de FE. Un bug en el motor de tesorería produce un forecast inexacto. Un bug en el emisor de FE produce una multa real al cliente. Diseñar la arquitectura FE con el mismo nivel de resiliencia que un sistema de pagos: retry automático, cola persistente, idempotencia estricta. Executor: La cola de FE debe sobrevivir un reinicio completo del servidor. Si la FE está en cola y el servidor cae, no puede perderse. Redis Streams con ACK explícito + respaldo en PostgreSQL es el patrón correcto, no fire-and-forget. Auditor Seg.: Las credenciales de acceso a Hacienda CR, SAT MX y DIAN CO son las llaves del reino fiscal del cliente. Si un atacante obtiene esas credenciales, puede emitir facturas a nombre del cliente o anularlas. Deben estar encriptadas en reposo con pgcrypto y nunca en variables de entorno en texto plano.


1. Arquitectura general

┌─────────────────────────────────────────────────────────────────┐
│                        FRONTEND (Next.js)                        │
└────────────────────────────┬────────────────────────────────────┘
                             │ HTTPS + JWT httpOnly cookie
┌────────────────────────────▼────────────────────────────────────┐
│                    FastAPI (API Gateway)                         │
│  /api/v1/bookmind/*                                             │
└──┬──────────────┬──────────────┬──────────────┬─────────────────┘
   │              │              │              │
   ▼              ▼              ▼              ▼
FEService    GLService    TreasuryService  BankService
(emisión FE) (asientos)   (forecast)       (Belvo/Prometeo)
   │              │              │              │
   └──────────────┴──────────────┴──────────────┘
                             │
                    ┌────────▼────────┐
                    │  PostgreSQL 16   │
                    │  (bk_* tables)  │
                    └─────────────────┘
                             │
                    ┌────────▼────────┐
                    │  Celery Workers  │
                    │  Redis Streams  │
                    └─────────────────┘
                             │
              ┌──────────────┼──────────────┐
              ▼              ▼              ▼
        Hacienda CR        SAT MX         DIAN CO
        (FE v4.4)        (CFDI 4.0)      (FE DIAN)

2. API endpoints principales

2.1 Facturación electrónica

POST   /api/v1/bookmind/fe/emitir
GET    /api/v1/bookmind/fe/{fe_id}
GET    /api/v1/bookmind/fe/lista?pais=CR&mes=2026-08&estado=emitida
POST   /api/v1/bookmind/fe/{fe_id}/anular          # requiere motivo
GET    /api/v1/bookmind/fe/contingencia             # FEs en cola con error
POST   /api/v1/bookmind/fe/{fe_id}/reenviar         # reintento manual

# Webhooks entrantes (Hacienda CR notifica confirmación o rechazo)
POST   /api/v1/bookmind/webhooks/hacienda-cr
POST   /api/v1/bookmind/webhooks/sat-mx
POST   /api/v1/bookmind/webhooks/dian-co

POST /api/v1/bookmind/fe/emitir — request:

{
  "tenant_id": "uuid",
  "pais": "CR",
  "tipo_documento": "01",
  "receptor": {
    "cedula": "3-101-XXXXXX",
    "nombre": "Distribuidora Ramírez S.A.",
    "email": "facturacion@ramirez.com"
  },
  "lineas": [
    {
      "detalle": "Servicio de consultoría",
      "cantidad": 1,
      "precio_unitario": 8500.00,
      "iva_porcentaje": 13
    }
  ],
  "tipo_pago": "01",
  "moneda": "CRC",
  "actividad_economica": "741001"
}

POST /api/v1/bookmind/fe/emitir — response (201):

{
  "fe_id": "bk-fe-uuid",
  "clave": "50601202608310101110001200100001010000000114842280",
  "numero_consecutivo": "00100001010000000001",
  "estado": "en_cola",
  "mensaje": "FE en cola de envío a Hacienda. Confirmación esperada en <30 segundos.",
  "webhook_url": "/api/v1/bookmind/webhooks/hacienda-cr"
}

2.2 General Ledger

GET    /api/v1/bookmind/gl/plan-cuentas
POST   /api/v1/bookmind/gl/asientos                # asiento manual
GET    /api/v1/bookmind/gl/balance-comprobacion?fecha=2026-08-31
GET    /api/v1/bookmind/gl/estado-resultados?desde=2026-01-01&hasta=2026-08-31
GET    /api/v1/bookmind/gl/balance-general?fecha=2026-08-31
GET    /api/v1/bookmind/gl/asientos?cuenta=1101&desde=2026-08-01

2.3 Cuentas por cobrar y pagar

GET    /api/v1/bookmind/ar/aging                   # aging report
GET    /api/v1/bookmind/ar/scoring                 # scoring cobranza por cliente
POST   /api/v1/bookmind/ar/facturas/{id}/pago-parcial
GET    /api/v1/bookmind/ap/calendario-vencimientos
POST   /api/v1/bookmind/ap/facturas                # registrar factura proveedor

2.4 Conciliación bancaria

GET    /api/v1/bookmind/bancos/cuentas
POST   /api/v1/bookmind/bancos/sync                # trigger sync Belvo/Prometeo
GET    /api/v1/bookmind/bancos/{cuenta_id}/movimientos?mes=2026-08
POST   /api/v1/bookmind/bancos/conciliacion/emparejar
POST   /api/v1/bookmind/bancos/conciliacion/cerrar  # cierre de conciliación

2.5 Tesorería predictiva

GET    /api/v1/bookmind/tesoreria/forecast          # P10/P50/P90 90 días
GET    /api/v1/bookmind/tesoreria/alertas           # alertas activas
POST   /api/v1/bookmind/tesoreria/escenario         # escenario manual ad-hoc
GET    /api/v1/bookmind/tesoreria/supuestos         # qué facturas/pagos alimentan el forecast

GET /api/v1/bookmind/tesoreria/forecast — response:

{
  "generado_en": "2026-08-12T08:00:00Z",
  "horizonte_dias": 90,
  "saldo_actual": 21650.00,
  "moneda_funcional": "USD",
  "series": [
    {
      "fecha": "2026-08-13",
      "p10": 18200.00,
      "p50": 20100.00,
      "p90": 23400.00,
      "confianza": "alta"
    }
  ],
  "alertas": [
    {
      "tipo": "gap_liquidez",
      "dia_estimado": 18,
      "severidad": "warning",
      "saldo_p50": 1200.00,
      "descripcion": "Saldo proyectado bajo escenario base cruza umbral de $2,000",
      "accion_sugerida": "Priorizar cobro Factura A-0234 ($8,500)"
    }
  ],
  "confianza_modelo": "alta",
  "dias_historial_usado": 365
}

3. Cola de FE — arquitectura de resiliencia

3.1 Flujo de emisión FE (CR)

1. Cliente llama POST /fe/emitir
2. FEService valida el payload (Pydantic) → rechaza si campos requeridos faltan
3. FEService genera XML firmado (v4.4 CR / CFDI 4.0 MX / FE DIAN CO)
4. FEService inserta en bk_fe_documents con estado='en_cola'
5. FEService publica mensaje en Redis Stream 'bk:fe:queue'
6. FEService retorna 201 inmediatamente al cliente (no espera Hacienda)
7. fe_worker consume el mensaje del Stream
8. fe_worker envía XML a Hacienda CR vía HTTPS con retry exponencial:
   - Intento 1: inmediato
   - Intento 2: +5 segundos
   - Intento 3: +30 segundos
   - Intento 4+: +5 minutos (hasta 24 horas max)
9. Hacienda CR responde con confirmación → fe_worker actualiza estado='emitida'
   O: Hacienda rechaza → estado='rechazada' + motivo_rechazo
   O: Timeout × 3 → estado='contingencia' + alerta al cliente
10. Webhook entrante (si Hacienda usa push) → /webhooks/hacienda-cr → actualizar estado

3.2 Idempotencia estricta

Cada emisión FE tiene idempotency_key = sha256(tenant_id + numero_consecutivo + clave_hacienda). Si el mismo XML se intenta emitir dos veces (por retry en el cliente), el segundo intento devuelve el estado del primero sin re-enviar a Hacienda.

async def emitir_fe(payload: FEPayload, tenant_id: UUID) -> FEDocument:
    idempotency_key = sha256(f"{tenant_id}:{payload.numero_consecutivo}".encode()).hexdigest()
    existing = await db.get(BkFEDocument, idempotency_key=idempotency_key)
    if existing:
        return existing  # idempotente: no re-envía
    # ... crear nuevo documento

3.3 Persistencia de la cola

Redis Streams con XACK obligatorio. Si el worker muere sin hacer XACK, el mensaje queda en PEL (Pending Entry List) y otro worker lo retoma automáticamente en el siguiente arranque.


4. Integración bancaria (Belvo / Prometeo)

class BankSyncService:
    async def sync_account(self, tenant_id: UUID, cuenta_id: UUID):
        # Prometeo para CR (Banco Nacional, BAC, Scotiabank CR)
        # Belvo para CO/MX (más cobertura)
        provider = self._get_provider(cuenta_id)
        movimientos = await provider.get_transactions(
            account_id=cuenta_id,
            from_date=last_sync_date,
            to_date=date.today()
        )
        await self._upsert_movimientos(tenant_id, movimientos)
        await self._trigger_auto_match(tenant_id, movimientos)

Auto-match heurístico (para conciliación automática): 1. Monto exacto + fecha ±3 días → match score 95% 2. Monto ±0.5% + descripción coincide → match score 80% 3. Monto ±2% → match score 40% (sugerir pero no auto-confirmar) 4. Sin match → presentar al contador para confirmación manual


5. Workers Celery

Worker Tarea Schedule
fe_worker Procesar cola de emisión FE Redis Stream consumer (continuo)
bank_sync_worker Sincronizar movimientos bancarios Cada 6 horas
treasury_worker Recalcular forecast P10/P50/P90 Diario 6:00 AM (antes del horario laboral)
scoring_worker Recalcular scoring cobranza clientes Diario 5:00 AM
gl_auto_post_worker Contabilizar automáticamente FEs emitidas/recibidas Trigger post-confirmación FE
report_worker Generar declaraciones IVA mensuales Primer día del mes

6. Contabilización automática de FE

Cada FE emitida confirmada genera asientos GL automáticos:

# FE de venta emitida → asientos automáticos
async def gl_auto_post_fe_venta(fe: BkFEDocument):
    asientos = [
        # Débito: Cuentas por Cobrar
        Asiento(cuenta='1101', monto=fe.total_con_iva, tipo='D', referencia=fe.clave),
        # Crédito: Ingresos por ventas (por línea de actividad)
        Asiento(cuenta='4101', monto=fe.subtotal, tipo='C', referencia=fe.clave),
        # Crédito: IVA por pagar
        Asiento(cuenta='2301', monto=fe.iva_total, tipo='C', referencia=fe.clave),
    ]
    await db.bulk_insert(BkAsiento, asientos, audit_origin='auto_fe')

7. Credenciales FE — almacenamiento seguro

Las credenciales de acceso a los portales tributarios se almacenan encriptadas:

-- En bk_integraciones
credenciales_enc BYTEA -- pgcrypto: pgp_sym_encrypt(json_credenciales, APP_SECRET)

-- Nunca en texto plano
-- Nunca en variables de entorno sin encriptar
-- Acceso solo desde FEService interno, nunca expuesto en API

8. Integración con StockMind y SalesMind

BookMind recibe datos automáticos de los otros módulos para el forecast de tesorería:

StockMind → BookMind:
  - Órdenes de compra aprobadas → AP futuro (pagos a proveedores esperados)
  - Recepciones de mercancía → devenga el pago del proveedor

SalesMind → BookMind:
  - Ventas del POS → AR inmediato (efectivo o por cobrar)
  - Pipeline CRM cerrado-ganado → AR futuro esperado (alimenta P50/P90)

BookMind → SalesMind:
  - Scoring de cobranza de clientes → enriquece perfil de cliente en CRM
  - Facturas vencidas → alerta de cobranza en SalesMind

Comunicación vía eventos internos en Redis Streams (no llamadas HTTP directas entre módulos).


9. Multi-tenancy y RLS

Idéntico al patrón MindStack Suite: - tenant_id UUID NOT NULL en todas las tablas bk_* - RLS en PostgreSQL habilitado por defecto - JWT con claim tid = tenant_id - FastAPI dependency get_current_tenant() extrae tid del JWT


10. Health checks y monitoreo

GET /health                  → {"status": "ok", "version": "1.0.0"}
GET /health/fe               → {"cr": "ok", "mx": "ok", "co": "degraded"}
GET /health/bancos           → {"belvo": "ok", "prometeo": "ok"}
GET /health/workers          → {"fe_worker": "ok", "treasury_worker": "ok"}

Alerta crítica automatizada (PagerDuty/Uptime Kuma → email + WhatsApp a Douglas): - Estado FE CR/MX/CO = degraded por más de 10 minutos → P0 - fe_worker queue length > 100 mensajes sin procesar → P1 - bank_sync_worker sin ejecutar en >8 horas → P2


11. Testing

Capa Cobertura objetivo
Emisión FE (generación XML + firma) ≥99% — incluye fixtures para cada versión (v4.4 CR, CFDI 4.0 MX, FE DIAN)
GL engine (asientos, balance) ≥95% — invariante: débitos = créditos en todos los asientos
Motor tesorería ≥90% — tests con historial sintético de 365 días
Conciliación bancaria (auto-match) ≥85%
API endpoints ≥80%

Tests de integración obligatorios con fixture de Hacienda CR (sandbox oficial) antes de cada deploy.


Ver también: Doc 06 (SRD Frontend — pantallas y UX) · Doc 08 (Modelo de Datos — esquema bk_) · Doc 09 (Motor Core — algoritmo tesorería predictiva) · Doc 10 (Seguridad — credenciales FE y pgcrypto)*