Modelo de saldos — cómo se mueve la plata
Análisis Funcional: Módulo Caja — parte 2 de 7. ← Resumen del alcance · Índice · Siguiente: Flujos de Caja →
Este es el documento central del paquete. Explica de qué bolsillo sale cada peso cuando se paga una transacción, y por qué los saldos que la app muestra no siempre bajan juntos.
El modelo que rige es el de tres capas —Holding → País → Caja—, definido con el cliente el 2026-08-05 e implementado en E4b (mergeada el 2026-08-06). El modelo anterior, de dos saldos, ya no existe: se menciona una sola vez, más abajo, para quien venga de conocerlo.
1.1 El principio que no cambia: los saldos no se guardan, se calculan
Ni el prefondeo ni la caja tienen una columna "saldo actual" en ningún lado. Cada saldo es la suma de sus movimientos, calculada en el momento en que alguien la pide.
saldo = Σ movimientos que suman − Σ movimientos que restanEs el patrón que el proyecto ya usaba para el prefondeo desde M2 y que ADR-005 extendió a la caja: un solo lugar de verdad, imposible de desincronizar. La contracara es que no existe "corregir el saldo" — solo existe agregar un movimiento más. Volvemos sobre esto en §1.8, porque cambia cómo se opera.
Las tres funciones que derivan —PrefundingService::balance() / globalBalance(), CountryPrefundingService::balance() y CashBoxService::balance()— suman su ledger en cada lectura; ninguna lee una columna cacheada.
1.2 Qué cambió respecto del modelo de dos saldos
Hasta E4b había dos: un único pool en USD rotulado "prefondeo del país", que descontaba amount + fee, y la caja. Si venís de conocer ese modelo, todo lo que cambió es esto: ese pool pasó a llamarse Prefondeo Holding —que es lo que siempre fue, porque descontar la comisión es su comportamiento, no el del país— y apareció una capa nueva entre él y la caja. El porqué está en §1.3.
1.3 Las tres capas
Por qué apareció una capa nueva
En la revisión del 2026-08-05, Teresa Ortiz explicó algo que ninguno de los ADRs anteriores podía haber previsto: por el contrato nuevo, Soterex ya no le transfiere la plata al país. Le transfiere a una holding, que se queda con la comisión y le baja al país únicamente el principal que hay que pagar.
Mirado contra el código, el modelo de dos saldos quedaba mal repartido:
- El único pool en USD que existe descuenta
amount + fee. Eso es, exactamente, el comportamiento de la Holding — se llamaba "prefondeo del país" pero nunca lo fue. - El pool que el país necesita, el que descuenta solo principales y es comparable contra el extracto bancario del país, no existía.
De ahí sale la decisión: no se cambia el pool existente, se lo renombra a lo que siempre fue y se agrega abajo el que faltaba.
Ver ADR-007 §1.
Qué descuenta cada capa
| Capa | Alcance | Descuenta de cada pago | ¿Puede quedar negativa? | ¿Ve la comisión? |
|---|---|---|---|---|
| Prefondeo Holding | Global — suma de todos los países | amount + fee | Sí, y nada lo impide ni lo avisa ⑴ | Sí, es la única que la ve |
| Prefondeo País | Por país | amount | Sí | No |
| Caja operativa | Por caja de un PDV | amount, en la moneda de pago del país | No | No |
⑴ La Holding puede quedar negativa y nadie se entera. Al sacar la validación de prefondeo del pago, ninguna capa chequea su saldo, y tampoco existe un aviso al repartir a los países de más. Es una asimetría real del sistema —abajo, fondear una caja por encima de lo disponible del país sí avisa— y está documentada en ADR-010 (Proposed), que propone el aviso que falta. Medido el 2026-09-13 sobre la base de desarrollo: los países tenían repartidos 148.100 contra 138.100 que la Holding había recibido, o sea 11.888,90 de más, en silencio.
La comisión se liquida a fin de mes entre la Holding y Soterex. Por eso el país nunca la ve: no la negocia, no la paga y verla en su saldo lo volvería incomparable contra el extracto del banco, que es justamente contra lo que Diego concilia.
1.4 El ejemplo canónico, paso a paso
Es el ejemplo que Diego Sánchez planteó en la reunión y que Carlos validó línea por línea. Sirve como test de comprensión: si los tres números finales te cierran, entendiste el modelo.
Punto de partida
Prefondeo Holding ....... 200 USD
└── Prefondeo Pais Guatemala .... 100 USDPaso 1 — tesorería le manda plata a las cajas. Llegan 20 en blindado a la Caja 1 y 20 a la Caja 2, y el Supervisor los carga como FONDEO con motivo Fondeo inicial.
| Capa | Antes | Después | Por qué |
|---|---|---|---|
| Holding | 200 | 200 | No se pagó nada todavía |
| País | 100 | 100 | ⚠️ No baja — la plata se movió de lugar, no se gastó |
| Caja 1 | 0 | 20 | Entró el efectivo |
| Caja 2 | 0 | 20 | Entró el efectivo |
En la pantalla de Prefondeo País, después de este paso, se ven tres números y no uno — porque "saldo del país" solo no alcanza para saber cuánto queda para repartir:
Saldo del pais ................ 100
Distribuido en cajas ........ 40 (20 + 20)
Disponible para distribuir ... 60 (100 - 40)Ese tercer número es contra el que avisa el sistema: si un fondeo lo deja en negativo, la caja se fondea igual y aparece el aviso (§1.3 ⑴ explica por qué arriba, entre la Holding y el país, ese aviso todavía no existe).
Este es el punto que más costó cerrar en la reunión, y vale detenerse. Diego preguntó textualmente: "ingresan los 20, ingresan los 20 también en la caja 2. No he pagado nada todavía… ahí la pregunta es, esto se mantiene en 100, ¿verdad?". La respuesta es sí.
El prefondeo del país no es "plata disponible sin repartir": es toda la plata del país. Mandarla a un cajón es moverla del banco al cajón, y en los dos lados sigue siendo del país. Recién deja de ser del país cuando se le entrega a un beneficiario. Dicho al revés: el prefondeo del país solo baja cuando se paga una transacción, nunca por distribuir.
Si el fondeo descontara el país, la plata se contaría dos veces hacia abajo — bajaría al mandarla al PDV y volvería a bajar al pagarla.
Paso 2 — el cajero paga una transacción desde la Caja 1. El principal es 5 USD y la comisión que Soterex informó para esa transacción es 2 USD.
| Capa | Antes | Después | Qué descontó |
|---|---|---|---|
| Caja 1 | 20 | 15 | Solo el principal — 5 |
| Caja 2 | 20 | 20 | Nada: el pago sale de la caja que abrió ese operador |
| Prefondeo País | 100 | 95 | Solo el principal — 5 |
| Prefondeo Holding | 200 | 193 | Principal + comisión — 5 + 2 |
Diego lo cerró así: "mi caja actual va a tener ahora 15 USD… la caja operativa se me redujo los 5 que ya pagué, y en el fondeo de holding van a estar los 5, que serían 195 − 2, 193". Carlos: "esa es exactamente [la lógica], eso pasa".
Nota sobre los 195. El plan de E4b muestra la fila del Holding como "antes 195, después 193". Ese 195 es el paso intermedio del cálculo hablado de Diego — restar primero el principal y después la comisión. El punto de partida del Holding es 200 y el final es 193, como está en ADR-007 §1. Es la misma cuenta, contada en dos tiempos.
Resultado final
Holding ......... 193 -- bajo 7: principal 5 + comision 2
Pais Guatemala .. 95 -- bajo 5: solo el principal
Caja 1 ......... 15 -- bajo 5: solo el principal
Caja 2 ......... 20 -- intactaLos tres bajan en el mismo pago, pero por importes distintos. No es un descuadre: cada capa mide una cosa distinta.
1.5 Por qué el saldo del país puede quedar negativo
Ver ADR-007 §2.
El escenario que planteó tesorería es concreto: la transferencia al país está en trámite, así que el prefondeo del país figura en cero. Pero en el cajón del PDV hay plata física, porque el blindado ya llegó. Llega un beneficiario a cobrar.
Con el modelo viejo ese pago se rechazaba con INSUFFICIENT_PREFUNDING: la app negaba una operación que físicamente se podía hacer, por un problema administrativo que no tenía nada que ver con el efectivo del cajón. Diego lo resolvió en una línea: "que me consulte en la caja operativa y listo". Teresa lo confirmó: "si hay, paga; si no hay, no paga — que no le consulte al prefondeo".
Si el pago se hace igual y el país estaba en cero, el país queda en rojo. Y eso es correcto: el número negativo es exactamente la deuda que el país tiene con la Holding por la plata que ya entregó y todavía no le bajaron. Carlos lo planteó así en la reunión: "para que a mí me cierre toda la contabilidad del sistema, esto tiene que permitir negativo por lo que acabás de pagar".
Un saldo negativo del país es información contable válida, no un estado de error. Se muestra —en rojo, con leyenda— y no bloquea nada.
Ojo con no generalizarlo: la caja operativa no puede quedar negativa. En un cajón físico no existe "menos cero", así que un ajuste que dejaría la caja bajo cero se rechaza (ADJUSTMENT_WOULD_GO_NEGATIVE). El negativo es una propiedad del pool contable del país, no del efectivo.
1.6 Qué valida el pago
La regla de fondo: el pago consulta la plata que está en el cajón, no la que está en los pools contables. Los tres saldos bajan con cada pago, pero solo uno puede frenarlo.
En un país con módulo Caja activo (Guatemala)
El pago corta si falla cualquiera de estas, en este orden:
- Faltan los documentos obligatorios —carta e identificación, anverso y reverso— →
MISSING_REQUIRED_DOCUMENTS(ADR-008). Es el bloqueo más frecuente en la operación real, y no tiene nada que ver con saldos. - El operador no tiene ninguna caja abierta a su nombre →
NO_CASH_BOX_OPEN_FOR_USER. - La caja que tiene abierta es de otro país que el de la transacción →
CASH_BOX_COUNTRY_MISMATCH. - El país paga en moneda local y no hay tipo de cambio vigente →
NO_EXCHANGE_RATE. Con la bandera apagada —el caso de Guatemala— esta validación no corre (§1.7). - La caja no cubre el monto a entregar →
INSUFFICIENT_CASH_BALANCE.
Ninguna de las dos capas de prefondeo bloquea nada. El Prefondeo País baja y puede quedar negativo; el Prefondeo Holding baja y también puede quedar negativo, sin aviso (§1.3 ⑴). La Holding pasó a ser una capa puramente contable y de reporte hacia Soterex.
En un país sin módulo Caja (Ecuador, con su EPOS)
Ahí no hay cajón físico que consultar, así que se mantiene una validación de saldo, contra el Prefondeo País y por el principal solamente: INSUFFICIENT_PREFUNDING.
Merece una aclaración honesta: es una decisión conservadora del equipo, no algo que el cliente haya pedido. Toda la conversación del 2026-08-05 fue sobre Guatemala con caja física; sacar la validación también donde no hay caja dejaría a Ecuador —en producción— sin ninguna barrera de saldo, y eso nadie lo discutió. ADR-007 §2 la deja escrita como asunción explícita. Si el cliente confirma que quiere sacarla en todos lados, es borrar una rama. Anotada en Preguntas Abiertas #8.
1.7 La moneda de la caja
Cada país tiene una bandera, countries.pago_en_moneda_local, y el default es pagar en dólares.
- Apagada (Guatemala). Teresa lo explicó en la reunión: "en este momento en Guatemala no podemos hacer cambio porque no tenemos la autorización… la transacción es pagada en dólares". El pago descuenta el monto nominal en USD, no se guarda tipo de cambio (
exchange_ratequeda ennull) y no se exige TC vigente — bajo esta configuración,NO_EXCHANGE_RATEsería un bloqueo falso. - Encendida. El pago convierte:
amount × TC vigente, y el TC usado queda congelado en el movimiento, así que una reimpresión futura muestra el TC del día del pago y no el de hoy.
El ledger guarda la moneda en cada movimiento (cash_movements.currency), no la infiere del país. Como la bandera es editable, sin eso un país que cambiara de moneda dejaría movimientos viejos y nuevos en el mismo ledger en monedas distintas, y el saldo sumaría peras con manzanas. Los movimientos en la moneda anterior quedan visibles aparte, como saldos residuales.
El módulo de tipo de cambio no se tira: queda listo para cuando llegue la autorización, o para el esquema de casa de cambio tercerizada que Teresa mencionó como segunda instancia.
1.8 Ledger inmutable: qué implica para quien opera
Los movimientos de caja no se editan y no se borran. Nunca. Ni por un Admin.
Esto no es una limitación técnica que se pueda levantar más adelante: es lo que hace que el saldo sea confiable. Si un movimiento se pudiera editar, el saldo de ayer dejaría de ser reproducible y la auditoría perdería sentido.
En la práctica, para el usuario:
| Si pasó esto… | …no se hace esto | …se hace esto |
|---|---|---|
| Cargué un fondeo de 1000 y eran 100 | Editar el movimiento | Un AJUSTE de −900, con motivo |
| Conté el cajón y falta plata | Corregir el saldo inicial | Un AJUSTE negativo con motivo Faltante de arqueo, que tesorería revisa |
| Sobra plata en el cajón | Retocar el saldo | Un AJUSTE positivo con motivo Sobrante de arqueo |
| Llegó un blindado | Editar el saldo de apertura | Un FONDEO con motivo Blindado |
El historial queda con las dos líneas: el error y su corrección. Es más ruidoso de leer que un número prolijo, y es exactamente el punto — el ruido es la trazabilidad. Teresa lo dijo así sobre los faltantes: "debería estar como un ajuste, y ahí el ajuste ya debería estarlo revisando tesorería".
Lo mismo aplica a las tres capas: los asientos de prefondeo (Holding y País) tampoco se editan, y el historial de tipo de cambio se corrige insertando un valor nuevo, nunca sobreescribiendo el anterior.
El corolario del saldo inicial de apertura
De acá sale, directamente, la regla R9 del Resumen: el saldo inicial de una apertura se hereda del cierre anterior y no es editable. Si se pudiera tipear, el operador tendría una vía para cambiar el saldo sin dejar rastro de por qué cambió — que es justo lo que el ledger inmutable evita en todos los demás caminos. Las dos razones legítimas para que el número sea distinto quedan tipificadas y auditadas por separado:
- Falta o sobra plata física →
AJUSTEcon motivo de arqueo. - Entró plata nueva →
FONDEO.
1.9 Resumen en una tabla
| Prefondeo Holding | Prefondeo País | Caja operativa | |
|---|---|---|---|
| Alcance | Global | Por país | Por caja de PDV |
| Moneda | USD | USD | Moneda de pago del país |
| Se carga | Manual, tesorería | Manual, tesorería | FONDEO por Supervisor |
| Baja con | Pagos: principal + comisión | Pagos: solo principal | Pagos: solo principal |
| No baja con | Distribuir al país | Fondear una caja | — |
| Puede ser negativo | Sí — sin aviso ni validación | Sí — se muestra en rojo | No |
| Avisa si se reparte de más | ❌ No existe el aviso (ADR-010, pendiente) | ✅ Avisa al fondear una caja por encima de lo disponible | — |
| Bloquea el pago | No | Solo en países sin Caja | Sí, es la única validación de saldo |
| Ledger | prefunding_entries | country_prefunding_entries | cash_movements |
Análisis Funcional: Módulo Caja — parte 2 de 7. ← Resumen del alcance · Índice · Siguiente: Flujos de Caja →

