> ## Documentation Index
> Fetch the complete documentation index at: https://docs.lerian.studio/llms.txt
> Use this file to discover all available pages before exploring further.

# Cálculos del Fees Engine

> Entiende cómo Fees Engine calcula, asigna y exime tarifas con reglas trazables y conformes, ideales para flujos contables complejos y precios por cliente.

## Valores numéricos (string)

***

Expresa todos los valores financieros en **Fees Engine** como `string` con el tipo `numeric`. Esto ofrece manejo de decimales de alta precisión para activos como BRL o BTC. También previene errores de redondeo durante cálculos, divisiones o exenciones.

<Danger>
  **Importante**

  * **Requerido**: Midaz v3.x.x (usa `numeric`).
  * **Incompatible**: Midaz v2.x.x (formato obsoleto `amount` + `scale`).

  Los clientes que usen Midaz v2.x.x deben actualizar a v3.x.x para garantizar la integración y funcionalidad adecuadas con el Fees Engine.
</Danger>

**Ejemplo:**

```
"value": "12.50"
```

## Períodos de facturación

***

Al activar un cálculo de facturación, especificas la ventana de tiempo a través del campo `period`. Fees Engine soporta tres formatos:

| Formato | Ejemplo      | Ventana                                                           |
| ------- | ------------ | ----------------------------------------------------------------- |
| Diario  | `2026-03-15` | Inicio de ese día → inicio del día siguiente (UTC)                |
| Semanal | `2026-W13`   | Lunes 00:00 UTC de la semana ISO → siguiente lunes 00:00 UTC      |
| Mensual | `2026-03`    | Primer instante del mes → primer instante del mes siguiente (UTC) |

El motor usa el período para contar transacciones que califican (para paquetes de volumen) o cuentas activas (para paquetes de mantenimiento) dentro de esa ventana exacta.

<Tip>
  Los períodos semanales siguen [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601#Week_dates). La numeración de semanas va de `W01` a `W52` (o `W53` en años que tienen 53 semanas ISO). La semana siempre comienza el lunes.
</Tip>

Elige la granularidad que coincida con tu ciclo de facturación. Un producto de tarjeta prepago facturado diariamente usaría `2026-03-15`. Una plataforma SaaS facturada mensualmente usaría `2026-03`. Un marketplace que liquida semanalmente usaría `2026-W13`.

## Reglas de cálculo de tarifas

***

Cada tarifa usa una `applicationRule` para definir cómo se calcula. Puedes elegir entre tres tipos de reglas:

* [maxBetweenTypes](#maxbetweentypes)
* [flatFee](#flatfee)
* [percentual](#percentual)

Puedes combinar diferentes reglas en un solo paquete para ajustarse a tu caso de uso.

Otros campos clave:

* `isDeductibleFrom`: define si la tarifa se deduce del remitente o del receptor.
* `referenceAmount`: ya sea `originalAmount` o `afterFeesAmount`.
* `priority`: define el orden de aplicación. La prioridad 1 siempre debe usar `originalAmount`.

### maxBetweenTypes

Aplica el mayor valor entre: una tarifa fija o una basada en porcentaje.

**Ejemplo**

* Valor de tarifa fija: R\$5.
* Tarifa porcentual: 2%.
* Monto de referencia: R\$1,000.

```
tasa = 1000 * 0.02 = R$ 20.00
```

Dado que **R\$ 20 > R\$ 5**, el motor aplica la tarifa basada en porcentaje.

### flatFee

Aplica un monto de tarifa fijo. El comportamiento depende de `isDeductibleFrom`.

**Ejemplo**

* Tarifa fija: R\$15.
* Monto de referencia: R\$115.

| `isDeductibleFrom` | Fórmula                 | Tarifa Total |
| :----------------- | :---------------------- | :----------- |
| `false`            | `referenceAmount + fee` | R\$ 130.00   |
| `true`             | `referenceAmount - fee` | R\$ 100.00   |

### percentual

Aplica una tarifa como porcentaje del monto de referencia.

**Ejemplo**

* Valor: 30%.
* Monto de referencia: R\$ 389.50.

| `isDeductibleFrom` | Fórmula                                       | Tarifa Total |
| :----------------- | --------------------------------------------- | :----------- |
| `false`            | `referenceAmount * value`                     | R\$ 116.85   |
| `true`             | `referenceAmount - (referenceAmount * value)` | R\$ 272.65   |

## División de tarifas

***

Cuando una transacción tiene múltiples cuentas de origen, el Fees Engine divide las tarifas proporcionalmente.

### Ejemplo

* Monto total: R\$4,000.00
* Tarifa fija: R\$15.00
* Impuesto: 4%
* `isDeductibleFrom: false`

### Porcentaje de participación

> **Fórmula**: (Monto de Cuenta ÷ Monto Total) × 100

| Cuenta    | Participación | Monto     |
| :-------- | :------------ | :-------- |
| @account1 | 25%           | R\$ 1,000 |
| @account2 | 25%           | R\$ 1,000 |
| @account3 | 40%           | R\$ 1,600 |
| @account4 | 10%           | R\$ 400   |

### Distribución de tarifa fija

> **Fórmula**: `Tarifa fija × participación %`

| Cuenta    | Porción de Tarifa | Total        |
| :-------- | :---------------- | :----------- |
| @account1 | R\$ 3.75          | R\$ 1,003.75 |
| @account2 | R\$ 3.75          | R\$ 1,003.75 |
| @account3 | R\$ 6.00          | R\$ 1,606.00 |
| @account4 | R\$ 1.50          | R\$ 401.50   |

### Impuesto proporcional

> **Fórmula**: `monto de cuenta × impuesto %`

| Cuenta    | Impuesto  | Total c/ Impuesto |
| :-------- | :-------- | :---------------- |
| @account1 | R\$ 40.00 | R\$ 1,040.00      |
| @account2 | R\$ 40.00 | R\$ 1,040.00      |
| @account3 | R\$ 64.00 | R\$ 1,664.00      |
| @account4 | R\$ 16.00 | R\$ 416.00        |

### Monto final por cuenta

> **Fórmula**: `principal + tarifa + impuesto`

| Cuenta    | Tarifa   | Impuesto  | Total Final  |
| :-------- | :------- | :-------- | :----------- |
| @account1 | R\$ 3.75 | R\$ 40.00 | R\$ 1,043.75 |
| @account2 | R\$ 3.75 | R\$ 40.00 | R\$ 1,043.75 |
| @account3 | R\$ 6.00 | R\$ 64.00 | R\$ 1,670.00 |
| @account4 | R\$ 1.50 | R\$ 16.00 | R\$ 417.50   |

#### Validaciones

* Total de participaciones = 100%
* División de tarifa coincide con tarifa fija
* División de impuesto = 4%
* Total enviado = R\$ 4,175.00

## Exenciones de tarifas: reglas y jerarquía

***

### Por monto de transacción

Usa `minimumAmount` y `maximumAmount` para definir cuándo **deben aplicarse las tarifas**.

> Por ejemplo: Si el rango es R$ 0–300, una transacción de R$ 301 no activará tarifas.

### Por cuenta

El sistema verifica `waivedAccounts`. Una fuente en esa lista está exenta de tarifas.

<Tip>
  **Jerarquía:** Verificación de rango de valor > luego exención de cuenta
</Tip>

## Ejemplo mixto: exenciones de tarifas y división proporcional de tarifas

***

Veamos un ejemplo de un paquete que incluye cuentas con exenciones de tarifas y requiere dividir las tarifas proporcionalmente.

### Escenario

Estamos procesando una transacción de **R\$ 4,000**, que incluye:

* Una **tarifa fija** de **R\$ 16**.
* Solo algunas cuentas están sujetas a la tarifa fija
* Un **impuesto IOF** del **6%** a deducir.

#### División en el lado de origen

| Cuenta de Origen | %   | Valor Proporcional |
| :--------------- | --- | :----------------- |
| @account1        | 15% | R\$ 600            |
| @account2        | 35% | R\$ 1,400          |
| @account3        | 40% | R\$ 1,600          |
| @account4        | 10% | R\$ 400            |

El motor aplica la tarifa fija solo a `@account3` y `@account4`.

#### Resultado después de Tarifa Administrativa (proporcional)

| Cuenta    | Tarifa Admin | Total        |
| :-------- | :----------- | :----------- |
| @account1 | Exento       | R\$ 600      |
| @account2 | Exento       | R\$ 1,400    |
| @account3 | R\$ 12.80    | R\$ 1,612.80 |
| @account4 | R\$ 3.20     | R\$ 403.20   |

El valor total de envío aumenta a **R\$4,016**.

#### Deducción de IOF (destinatario)

| Destinatario | %   | Bruto     | IOF (6%) | Neto    |
| :----------- | --- | :-------- | :------- | :------ |
| @donation1   | 25% | R\$ 1,000 | R\$ 60   | R\$ 940 |
| @donation2   | 25% | R\$ 1,000 | R\$ 60   | R\$ 940 |
| @donation3   | 25% | R\$ 1,000 | R\$ 60   | R\$ 940 |
| @donation4   | 25% | R\$ 1,000 | R\$ 60   | R\$ 940 |

<Danger>
  El motor acredita las tarifas en las cuentas definidas en el `creditAccount` de cada tarifa.
</Danger>

## Decimales repetidos

***

Cuando una división de tarifa produce un decimal periódico (por ejemplo, 0.3333...), el Fees Engine mantiene cada línea de tarifa con precisión completa. Reconcilia el pequeño resto en la tarifa de la cuenta con el mayor valor. Esto mantiene el total exacto, evita la deriva por redondeo y mantiene tu ledger consistente.

## Cálculos de facturación

***

Los billing packages usan un modelo de cálculo diferente al de los fee packages. En lugar de evaluar transacciones individuales, agregan datos durante un período de facturación y devuelven payloads de cargo para que tu orquestador los ejecute.

La facturación soporta tres formatos de período: mensual (`YYYY-MM`), semanal (`YYYY-Www`, ej., `2026-W13`) y diario (`YYYY-MM-DD`).

### Cálculo de facturación por volumen

La facturación por volumen cuenta transacciones que coinciden con un `eventFilter` (ruta de transacción + estado) dentro del período de facturación, luego aplica precios según el modelo configurado.

El cálculo sigue este orden:

1. **Contar** transacciones que califican en el período.
2. **Restar** el `freeQuota` del conteo total para obtener el conteo facturable.
3. **Aplicar precios** según el `pricingModel` (`tiered` o `fixed`).
4. **Aplicar un descuento** de `discountTiers`, evaluado contra el conteo **total** (antes de restar la cuota gratuita).

Los montos se mantienen con precisión decimal completa en todo el proceso — el motor no aplica redondeo a la escala del activo.

#### Precios escalonados

Los precios escalonados son **precios por volumen, no precios graduados**. El motor encuentra el único nivel cuyo rango de cantidad contiene el conteo facturable y luego cobra **cada** unidad facturable al precio unitario de ese nivel. No aplica el precio de cada rango a las unidades que caen dentro de él.

El rango de un nivel es inclusivo en ambos extremos. Si omites el límite superior, el nivel queda sin tope.

El motor busca un nivel solo cuando el conteo facturable es positivo. Si el conteo facturable es positivo y ningún nivel lo cubre, el cálculo falla para ese paquete. Un conteo facturable de cero omite la búsqueda de nivel y produce un monto cero, así que tus niveles no necesitan cubrir el cero.

**Ejemplo**: Un billing package para emisión de boletos con tres niveles y una cuota gratuita de 50:

| Rango     | Precio unitario |
| --------- | --------------- |
| 1–500     | R\$ 1.20        |
| 501–2,000 | R\$ 0.80        |
| 2,001+    | R\$ 0.45        |

Para un cliente que emitió 1,800 boletos en el mes:

* 50 exentos (cuota gratuita) → 1,750 facturables
* 1,750 cae en el nivel 501–2,000, así que **todas** las 1,750 unidades se cobran a R\$ 0.80: R\$ 1,400.00 bruto
* Se aplica el nivel de descuento sobre el conteo total de 1,800 (≥ 1,000 → 5%): −R\$ 70.00
* **Total neto: R\$ 1,330.00**

#### Precio fijo

Un solo precio unitario se aplica a todas las transacciones facturables independientemente del volumen. El motor aún resta la cuota gratuita antes del cálculo. El precio fijo toma su precio unitario del **primer nivel** del paquete, así que un paquete fijo debe declarar al menos un nivel — de lo contrario el cálculo falla.

**Ejemplo**: R\$ 0.10 por Pix enviado, sin cuota gratuita:

* 5,000 transacciones Pix × R\$ 0.10 = R\$ 500.00

#### Niveles de descuento

Se aplica como máximo **un** nivel de descuento: el que tiene el `minQuantity` más alto que el conteo total alcanza o supera. Su porcentaje se aplica al monto bruto. Los descuentos se evalúan contra el conteo total de transacciones, no contra el conteo facturable.

#### Alcance del conteo

El cálculo por volumen cuenta transacciones **por ruta de transacción en todo el ledger**. La cuota gratuita, los niveles y los niveles de descuento se aplican a ese total por ruta. Para contar dos flujos por separado, crea un paquete por ruta de transacción.

### Cálculo de facturación de mantenimiento

La facturación de mantenimiento cobra un monto fijo por cuenta activa en el período de facturación. El motor resuelve las cuentas objetivo, conserva solo las activas y genera un único payload de transacción.

El resultado es una transacción N:1:

* Cada cuenta activa aparece como una entrada de débito (`source.from`) por el `feeAmount` configurado.
* La `maintenanceCreditAccount` recibe el total completo como una única entrada de crédito (`distribute.to`).

Solo se incluyen las cuentas cuyo código de estado es `active`; cualquier otro estado queda excluido. Si no se resuelve ninguna cuenta, el paquete devuelve un payload vacío (`{}`) en lugar de una transacción.

**Ejemplo**: Mantenimiento mensual de R\$ 9.90 para un segmento con 12,000 cuentas PF activas:

* 12,000 entradas en `source.from`, cada una debitada R\$ 9.90
* 1 entrada en `distribute.to` acreditada R\$ 118,800.00

### Resultados con monto cero

Cuando el monto neto de un paquete resulta cero — por ejemplo, si la cuota gratuita cubrió todas las transacciones — el motor aún devuelve un resultado para ese paquete, pero con un payload de transacción vacío (`{}`). Interprétalo como "procesado, nada que enviar".

El uso totalmente exento llega a este resultado sin una búsqueda de nivel. La cuota gratuita lleva el conteo facturable a cero, el motor omite la coincidencia de niveles y el monto es cero. Un paquete cuyos niveles empiezan en 1 es correcto para este caso.

### Política de fallo todo-o-nada

Si algún billing package falla durante una llamada a `/billing/calculate`, toda la operación falla. El motor no devuelve resultados parciales. La respuesta incluye qué paquete y recurso causó el fallo, para que puedas corregir y re-ejecutar.

### Metadatos de auditoría

Cada resultado de cálculo de facturación incluye metadatos estructurados para trazabilidad. Los resultados de volumen incluyen el tipo de facturación, el id y la etiqueta del paquete, el período, los conteos de eventos totales y facturables, la cuota gratuita utilizada, el modelo de precios, los montos bruto y neto, y el detalle del descuento (porcentaje, monto y `minQuantity`) cuando se aplicó alguno. Los resultados de mantenimiento incluyen el tipo de facturación, el id y la etiqueta del paquete, el período, el conteo total de cuentas y el monto de la tarifa por cuenta.
