Comportamientos de Acciones y Recargas de CGRateS
Esta guía explica cómo funcionan las Acciones de CGRateS dentro de OmniCRM, centrándose específicamente en la gestión de saldos, comportamientos de recarga y cómo diferentes tipos de acción afectan a los productos adicionales.
Resumen
En el Sistema de Carga en Línea (CGRateS) de OmniCRM, Acciones son el mecanismo para agregar, modificar o eliminar saldos en las cuentas de los clientes.
Cuando provisionas un producto adicional o de recarga, en realidad estás ejecutando una Acción de CGRateS que manipula los saldos de la cuenta. (También puedes tomar otros enfoques, como agregar saldos manualmente a las cuentas desde Playbooks; este es solo un patrón común que usamos para mantener las cosas limpias).
Conceptos Clave
Acción - Un conjunto de operaciones a realizar en una cuenta (agregar saldo, deducir saldo, registrar CDR, etc.)
Saldo - Una cantidad de un recurso (datos, voz, SMS, monetario) con un tiempo de caducidad y peso.
ID de Saldo - Un identificador único para un tipo de saldo (por ejemplo, "Paquete de Datos", "Minutos de Voz").
Peso - Prioridad para el consumo de saldo (se consumen primero los de mayor peso).
Tiempo de Caducidad - Cuando el saldo expira (fecha absoluta o relativa como "+5 días").
Bloqueador - Una bandera especial de saldo que bloquea todo uso cuando el saldo llega a cero, incluso si existen otros saldos (ver Bloqueadores de Saldo).
Crítico: Las Acciones Deben Definirse Primero
Antes de poder ejecutar una Acción en un playbook usando ExecuteAction, esa Acción ya debe estar definida en CGRateS. Este es un requisito crítico que a menudo se pasa por alto.
Cuándo se Definen las Acciones
Las acciones se definen típicamente durante la configuración inicial del sistema o configuración del producto, NO durante el aprovisionamiento. Generalmente se crean a través de scripts de Python que configuran simultáneamente tanto el CRM como el OCS.
Cómo se Vinculan las Acciones a los Productos
Las acciones están vinculadas a productos a través de una convención de nomenclatura:
- Acción CGRateS:
ActionsId = "Action_50gb-data-pack" - Producto CRM:
product_slug = "50gb-data-pack" - En Playbook:
cgr_action_name = "Action_" + product_slug
Cuando se ejecuta el playbook, construye el nombre de la Acción a partir del product_slug y llama a ExecuteAction. Si esa Acción no existe en CGRateS, el aprovisionamiento falla.
Dónde Definir Acciones
Las acciones deben definirse en tus scripts de configuración de productos, típicamente:
- Durante la Configuración Inicial - Al configurar tu sistema por primera vez.
- Al Crear Nuevos Productos - Define la Acción antes de crear el producto.
- A través de Scripts de Configuración - Scripts de Python que configuran tanto OCS como CRM.
Ejemplo: Definiendo una Acción antes de crear el producto
import cgrateshttpapi
OCS_Obj = cgrateshttpapi.CGRateS("ocs.example.com", "2080")
tenant = "tu_tenant"
# Paso 1: Define la Acción en CGRateS PRIMERO
Action_50GB_Data_Pack = {
"method": "ApierV1.SetActions",
"params": [{
"ActionsId": "Action_50gb-data-pack",
"Tenant": tenant,
"Actions": [
{
"Identifier": "*topup",
"BalanceType": "*data",
"Units": 50 * 1024 * 1024 * 1024,
"ExpiryTime": "+720h",
"Weight": 10
}
]
}]
}
result = OCS_Obj.SendData(Action_50GB_Data_Pack)
assert result['error'] is None or result['error'] == "EXISTS"
# Paso 2: Ahora crea el producto en CRM
# (product_slug = "50gb-data-pack" se vinculará a Action_50gb-data-pack)
¿Qué Sucede Si la Acción No Existe?
# En playbook
- name: Ejecutar Acción
uri:
url: "http://{{ crm_config.ocs.cgrates }}/jsonrpc"
method: POST
body:
{
"method": "APIerSv1.ExecuteAction",
"params": [{
"ActionsId": "Action_50gb-data-pack"
}]
}
register: response
# Resultado si la Acción no está definida:
# response.json.error = "SERVER_ERROR: Acción no encontrada"
# El aprovisionamiento falla, el cliente no recibe saldo
Para obtener detalles completos sobre cómo definir Acciones y vincularlas a productos, consulta Definiendo Productos.
Independencia de Saldos
Por defecto, los productos adicionales crean saldos independientes que funcionan por separado entre sí. Esto significa:
- Puedes tener múltiples addons activos simultáneamente.
- Cada addon mantiene su propio saldo y caducidad.
- Los saldos se consumen según las reglas de peso y caducidad.
Ejemplo: Múltiples Addons Independientes
# El cliente tiene estos saldos activos:
Saldo 1:
ID: "Data_5GB_5days_uuid_abc123"
Tipo: *data
Valor: 5368709120 # 5GB en bytes
Caducidad: 2024-12-29
Peso: 10
Saldo 2:
ID: "Data_10GB_30days_uuid_def456"
Tipo: *data
Valor: 10737418240 # 10GB en bytes
Caducidad: 2025-01-24
Peso: 10
# Ambos saldos coexisten independientemente
# El sistema consume según el peso; si el peso es igual, el orden no está garantizado
Cuando los saldos tienen el mismo peso y coinciden con el mismo destino, CGRateS no garantiza el orden de consumo basado en la caducidad; el orden depende de cómo se almacenan y recuperan los saldos. Usa diferentes pesos para controlar el orden de consumo.
Tipos de Acción: *topup vs *topup_reset
El tipo de acción determina cómo los nuevos saldos interactúan con los saldos existentes del mismo ID.
*topup - Comportamiento Aditivo
La acción *topup agrega a los saldos existentes con el mismo ID de Saldo y extiende el tiempo de caducidad.
Comportamiento:
- Encuentra saldo existente con ID coincidente.
- Agrega nuevo valor al valor existente.
- Actualiza la caducidad al nuevo tiempo de caducidad.
- Preserva el saldo existente (rollover).
Ejemplo:
# Estado inicial:
Saldo:
ID: "Data_Package__5368709120"
Valor: 1073741824 # 1GB restante
Caducidad: 2024-12-24 (1 día restante)
# Ejecutar acción *topup con:
Acción:
Identificador: "*topup"
Saldo:
ID: "Data_Package__5368709120" # Mismo ID - activa el rollover
Valor: 5368709120 # 5GB
ExpiryTime: "+5d"
# Resultado después de *topup:
Saldo:
ID: "Data_Package__5368709120"
Valor: 6442450944 # 6GB (1GB + 5GB rollover)
Valor_original: 5368709120 # Aún muestra los 5GB originales
Valor_hr: "6 GB"
Valor_original_hr: "5 GB"
Restante_hr: "6 GB de 5 GB (1 GB rollover)"
Caducidad: 2024-12-29 (5 días a partir de ahora)
Casos de Uso:
- Recompensas de lealtad (agregar datos adicionales al paquete existente).
- Créditos de compensación (agregar al saldo del cliente).
- Paquetes de datos rollover.
- Extensiones de período de gracia.
*topup_reset - Comportamiento de Reinicio
La acción *topup_reset reemplaza los saldos existentes con el mismo ID de Saldo.
Comportamiento:
- Encuentra saldo existente con ID coincidente.
- Desecha el valor antiguo (sin rollover).
- Establece el saldo al nuevo valor únicamente.
- Actualiza la caducidad al nuevo tiempo de caducidad.
Ejemplo:
# Estado inicial:
Saldo:
ID: "Data_Package__5368709120"
Valor: 1073741824 # 1GB restante
Caducidad: 2024-12-24 (1 día restante)
# Ejecutar acción *topup_reset con:
Acción:
Identificador: "*topup_reset"
Saldo:
ID: "Data_Package__5368709120" # Mismo ID - activa el reinicio
Valor: 5368709120 # 5GB
ExpiryTime: "+5d"
# Resultado después de *topup_reset:
Saldo:
ID: "Data_Package__5368709120"
Valor: 5368709120 # 5GB (se desechó el antiguo 1GB)
Valor_original: 5368709120
Valor_hr: "5 GB"
Restante_hr: "5 GB de 5 GB"
PercentUsed: 0
Caducidad: 2024-12-29 (5 días a partir de ahora)
Casos de Uso:
- Paquetes mensuales recurrentes (reiniciar a la cantidad total cada mes).
- Recargas de tamaño fijo (siempre recibir la cantidad exacta).
- Cambios de plan (reemplazar el saldo del plan antiguo con el nuevo plan).
- Prevenir abusos (no se pueden apilar addons ilimitados).
Controlando el Comportamiento del Saldo con IDs de Saldo
El ID de Saldo es crucial para determinar si los saldos son independientes o interactúan entre sí.
Convención de Nomenclatura de ID de Saldo y Vistas Legibles por Humanos
OmniCRM utiliza una convención de nomenclatura específica para los IDs de Saldo que codifica tanto el nombre descriptivo como el tamaño original. Esto permite que la API genere automáticamente campos legibles por humanos para la interfaz web.
Patrón de ID de Saldo:
{NombreDescriptivo}__{TamañoOriginalEnUnidadesBase}
Ejemplos de IDs de Saldo:
# Saldo de datos: 100GB
"AU_Data_Domestic__107374182400"
# Se descompone en:
# - Parte descriptiva: "AU_Data_Domestic" (QUÉ es - tipo/destino)
# - Separador: "__" (doble guion bajo)
# - Tamaño original: "107374182400" (100GB en bytes)
# - La interfaz muestra: "AU Data Domestic - 100 GB"
# Saldo de voz: 3000 minutos
"AU_Voice_Domestic__180000000000000"
# - Parte descriptiva: "AU_Voice_Domestic" (NO "AU_Voice_Domestic_3000min")
# - Tamaño original: "180000000000000" (3000 minutos en nanosegundos)
# - La interfaz muestra: "AU Voice Domestic - 3000 min"
# Saldo de SMS: 3000 mensajes
"AU_SMS_Domestic__3000"
# - Parte descriptiva: "AU_SMS_Domestic"
# - Tamaño original: "3000" (conteo)
# - La interfaz muestra: "AU SMS Domestic - 3000 msgs"
Importante: No incluyas información de tamaño en la parte descriptiva, ya que es redundante, dado que el tamaño está codificado después de __ y la API lo convierte automáticamente a formato legible por humanos.
Cómo la API Crea Vistas Legibles por Humanos:
Cuando la API de OmniCRM recupera datos de saldo de CGRateS, automáticamente analiza el ID de Saldo y genera campos _hr (legibles por humanos):
{
"BalanceMap": {
"*data": [
{
"ID": "AU_Data_Domestic__107374182400",
"Value": 53687091200,
"ExpiryTime": "2025-01-25T23:59:59Z",
"Weight": 1200,
// Campos legibles por humanos generados automáticamente:
"ID_hr": "AU Data Domestic",
"OriginalValue": 107374182400,
"OriginalValue_hr": "100 GB",
"Value_hr": "50 GB",
"Remaining_hr": "50 GB de 100 GB",
"PercentUsed": 50,
"ExpiryTime_hr": "25 de enero de 2025 (22 días)"
}
]
}
}
Lógica de Procesamiento de la API:
-
Analizar ID de Saldo:
balance_id = "AU_Data_Domestic__107374182400"
parts = balance_id.split("__")
descriptive_name = parts[0] # "AU_Data_Domestic"
original_size = int(parts[1]) if len(parts) > 1 else None # 107374182400 -
Generar Nombre Descriptivo Legible por Humanos:
# Reemplazar guiones bajos por espacios
id_hr = descriptive_name.replace("_", " ") # "AU Data Domestic" -
Convertir Tamaño Original a Unidades Humanas:
# Para saldos de datos (bytes)
if balance_type == "*data":
original_value_hr = convert_bytes_to_gb(original_size) # "100 GB"
# Para saldos de voz (nanosegundos)
elif balance_type == "*voice":
original_value_hr = convert_ns_to_minutes(original_size) # "3000 min"
# Para saldos de SMS (conteo)
elif balance_type == "*sms":
original_value_hr = f"{original_size} msgs" # "3000 msgs" -
Calcular Porcentaje de Uso:
if original_size and original_size > 0:
percent_used = ((original_size - current_value) / original_size) * 100 -
Formatear Visualización Restante:
remaining_hr = f"{current_value_hr} de {original_value_hr}"
# "50 GB de 100 GB"
Visualización en la Interfaz Web:
El frontend utiliza estos campos _hr para mostrar información de saldo amigable para el usuario:
// En lugar de mostrar valores en bruto:
// ID: "AU_Data_Domestic__107374182400"
// Valor: 53687091200
// Mostrar legible por humanos:
<BalanceCard>
<Title>{balance.ID_hr}</Title> {/* "AU Data Domestic" */}
<Progress value={balance.PercentUsed}> {/* 50% */}
{balance.Remaining_hr} {/* "50 GB de 100 GB" */}
</Progress>
<Expiry>{balance.ExpiryTime_hr}</Expiry> {/* "25 de enero de 2025 (22 días)" */}
</BalanceCard>
Por qué Esto Importa:
-
Seguimiento del Tamaño Original - Incluso cuando los saldos se consumen parcialmente, la UI puede mostrar "50 GB de 100 GB" en lugar de solo "50 GB restantes".
-
Visualización del Progreso - Los cálculos de porcentaje permiten barras de progreso precisas.
-
Nomenclatura Consistente - Los nombres descriptivos extraídos de los IDs de Saldo aseguran consistencia entre backend y frontend.
-
Visualización de Rollover - Al usar
*topup(rollover), si un cliente tiene 70 GB restantes y recarga 100 GB:- El ID de Saldo permanece:
"AU_Data_Domestic__107374182400"(original 100 GB). - El valor actual se convierte en: 170 GB.
- La UI muestra: "170 GB (70 GB rollover + 100 GB nuevo)".
- El ID de Saldo permanece:
Mejor Práctica - Creación de IDs de Saldo:
Siempre incluye el tamaño original después de __ para una visualización adecuada en la UI. No dupliques la información de tamaño en el nombre descriptivo:
# Bueno - nombre descriptivo + tamaño en unidades base
Action_Data_100GB = {
"Actions": [
{
"BalanceId": f"AU_Data_Domestic__{100 * 1024 * 1024 * 1024}",
"Units": 100 * 1024 * 1024 * 1024
}
]
}
# Malo - tamaño redundante en el nombre descriptivo
Action_Data_100GB = {
"Actions": [
{
"BalanceId": f"AU_Data_Domestic_100GB__{100 * 1024 * 1024 * 1024}", # ¡Redundante!
"Units": 100 * 1024 * 1024 * 1024
}
]
}
# Malo - sin información de tamaño (la UI no puede calcular el porcentaje)
Action_Data_100GB = {
"Actions": [
{
"BalanceId": "AU_Data_Domestic", # Falta __tamaño
"Units": 100 * 1024 * 1024 * 1024
}
]
}
Caso Especial - Saldos Monetarios:
Los saldos monetarios generalmente no incluyen tamaño original, ya que pueden ser recargados a cualquier cantidad:
# Saldo monetario sin codificación de tamaño
{
"BalanceId": "PAYG_Monetary_Balance",
"BalanceType": "*monetary",
"Units": 5000 # $50.00
}
# La UI simplemente muestra el saldo actual sin porcentaje
# "Saldo: $50.00"
Estrategia 1: IDs Únicos (Saldos Independientes)
Usa IDs únicos (por ejemplo, con UUIDs) para crear saldos completamente independientes que nunca interactúan.
Ejemplo de Implementación:
- name: Generar identificador de saldo único
set_fact:
uuid: "{{ 99999999 | random | to_uuid }}"
balance_id: "Data_5days__5368709120_{{ uuid[0:8] }}"
- name: Agregar saldo independiente con *topup
uri:
url: "http://{{ crm_config.ocs.cgrates }}/jsonrpc"
method: POST
body_format: json
body:
{
"method": "ApierV1.AddBalance",
"params": [{
"Tenant": "{{ crm_config.ocs.ocsTenant }}",
"Account": "{{ service_uuid }}",
"BalanceType": "*data",
"Balance": {
"ID": "{{ balance_id }}",
"Value": 5368709120,
"ExpiryTime": "+5d",
"Weight": 10
}
}]
}
Resultado: Cada addon crea un nuevo saldo separado incluso si el cliente compra el mismo addon múltiples veces.
# El cliente compra "addon de datos de 5 días" tres veces:
Saldo 1:
ID: "Data_5days__5368709120_a1b2c3d4"
Valor: 5368709120
Valor_hr: "5 GB"
OriginalValue_hr: "5 GB"
Remaining_hr: "5 GB de 5 GB"
Caducidad: 2024-12-29
Saldo 2:
ID: "Data_5days__5368709120_e5f6g7h8"
Valor: 5368709120
Valor_hr: "5 GB"
Remaining_hr: "5 GB de 5 GB"
Caducidad: 2024-12-30
Saldo 3:
ID: "Data_5days__5368709120_i9j0k1l2"
Valor: 5368709120
Valor_hr: "5 GB"
Remaining_hr: "5 GB de 5 GB"
Caducidad: 2024-12-31
# Total disponible: 15GB en tres saldos separados
# Cada uno se muestra individualmente en la UI como "Datos 5 días - 5 GB de 5 GB"
Estrategia 2: IDs Compartidos con *topup (Rollover)
Usa el mismo ID de Saldo con la acción *topup para permitir el rollover de saldo y la extensión de caducidad.
Ejemplo de Implementación:
- name: Establecer ID de saldo fijo
set_fact:
balance_id: "Data_5days__5368709120"
- name: Agregar saldo con *topup (rollover)
uri:
url: "http://{{ crm_config.ocs.cgrates }}/jsonrpc"
method: POST
body_format: json
body:
{
"method": "ApierV1.AddBalance",
"params": [{
"Tenant": "{{ crm_config.ocs.ocsTenant }}",
"Account": "{{ service_uuid }}",
"BalanceType": "*data",
"Balance": {
"ID": "Data_5days__5368709120",
"Value": 5368709120,
"ExpiryTime": "+5d",
"Weight": 10
}
}]
}
Resultado: Compras posteriores se suman al saldo existente y extienden la caducidad.
# Día 1: El cliente compra "addon de datos de 5 días":
Saldo:
ID: "Data_5days__5368709120"
Valor: 5368709120
Valor_hr: "5 GB"
Remaining_hr: "5 GB de 5 GB"
PercentUsed: 0
Caducidad: 2024-12-29
# Día 3: El cliente usa 1GB, luego compra el mismo addon nuevamente:
Saldo:
ID: "Data_5days__5368709120"
Valor: 9663676416 # 4GB restantes + 5GB nuevos
Valor_hr: "9 GB"
OriginalValue_hr: "5 GB"
Remaining_hr: "9 GB (4 GB rollover + 5 GB nuevo)"
PercentUsed: -80 # Negativo indica rollover
Caducidad: 2024-12-27 (nuevos +5 días a partir de hoy)
Estrategia 3: IDs Compartidos con *topup_reset (Cantidad Fija)
Usa el mismo ID de Saldo con la acción *topup_reset para reiniciar siempre a una cantidad fija.
Ejemplo de Implementación:
- name: Establecer ID de saldo fijo
set_fact:
balance_id: "Monthly_Plan__32212254720"
- name: Agregar saldo con *topup_reset (sin rollover)
uri:
url: "http://{{ crm_config.ocs.cgrates }}/jsonrpc"
method: POST
body_format: json
body:
{
"method": "ApierV1.SetActions",
"params": [{
"ActionsId": "Action_Reset_Monthly_Plan",
"Actions": [{
"Identifier": "*topup_reset",
"BalanceType": "*data",
"Units": 32212254720, # 30GB
"ExpiryTime": "*monthly",
"DestinationIds": "*any",
"BalanceId": "Monthly_Plan__32212254720",
"Weight": 10
}]
}]
}
Resultado: Cada mes, el saldo se reinicia exactamente a 30GB sin importar cuánto se haya utilizado.
# Mes 1, Día 1:
Saldo:
ID: "Monthly_Plan__32212254720"
Valor: 32212254720
Valor_hr: "30 GB"
Remaining_hr: "30 GB de 30 GB"
PercentUsed: 0
Caducidad: 2024-12-31
# Mes 1, Día 25: El cliente usó 28GB
Saldo:
ID: "Monthly_Plan__32212254720"
Valor: 2147483648
Valor_hr: "2 GB"
Remaining_hr: "2 GB de 30 GB"
PercentUsed: 93
Caducidad: 2024-12-31
# Mes 2, Día 1: El ActionPlan ejecuta *topup_reset
Saldo:
ID: "Monthly_Plan__32212254720"
Valor: 32212254720 # Reiniciado a completo, los 2GB no utilizados se pierden
Valor_hr: "30 GB"
Remaining_hr: "30 GB de 30 GB"
PercentUsed: 0
Caducidad: 2025-01-31
Bloqueadores de Saldo
Los Bloqueadores de Saldo son una característica poderosa de CGRateS que te permite bloquear o limitar el uso incluso cuando existen saldos. Un saldo bloqueador detiene el consumo cuando llega a cero, impidiendo el uso adicional independientemente de otros saldos disponibles.
Cómo Funcionan los Bloqueadores
Cuando un saldo tiene Blocker: true:
- Mientras el bloqueador tenga valor → Se permite el uso hasta la cantidad del bloqueador.
- Cuando el bloqueador llega a cero → Todo el uso se detiene, incluso si existen otros saldos.
- Error devuelto →
INSUFFICIENT_CREDIT_BALANCE_BLOCKERpreviene la sesión.
Características Clave:
- Los saldos bloqueadores son verificados incluso cuando el valor es cero (a diferencia de los saldos normales que se omiten).
- Cuando se encuentra un bloqueador con uso restante solicitado, CGRateS detiene el procesamiento y devuelve un error.
- Los bloqueadores funcionan con todos los tipos de saldo:
*voice,*data,*sms,*monetary.
Casos de Uso para Bloqueadores
1. Suspensión de Cuenta
Bloquear todo uso cuando una cuenta está suspendida (por ejemplo, fallo de pago):
Action_Suspend_Account = {
"id": "0",
"method": "ApierV1.SetActions",
"params": [{
"ActionsId": "Action_suspend-account",
"Overwrite": True,
"Tenant": tenant,
"Actions": [
# Agregar bloqueador de valor cero para prevenir todo uso
{
"Identifier": "*topup",
"BalanceId": "Suspension_Blocker",
"BalanceType": "*monetary",
"DestinationIDs": "*any",
"Units": 0, # Valor cero
"BalanceWeight": 9999, # Mayor prioridad - verificado primero
"Blocker": True, # Bloquear todo uso
"Weight": 10
}
]
}]
}
result = OCS_Obj.SendData(Action_Suspend_Account)
Resultado: Todas las llamadas/datos/SMS bloqueados independientemente de otros saldos.
2. Límites de Gastos
Limitar el gasto máximo para prevenir sorpresas en la factura:
Action_Monthly_Plan_With_Cap = {
"id": "0",
"method": "ApierV1.SetActions",
"params": [{
"ActionsId": "Action_monthly-with-cap",
"Overwrite": True,
"Tenant": tenant,
"Actions": [
# 10GB de datos incluidos
{
"Identifier": "*topup_reset",
"BalanceId": f"Included_Data__{10 * 1024 * 1024 * 1024}",
"BalanceType": "*data",
"DestinationIDs": "Dest_PLMN_OnNet",
"Units": 10 * 1024 * 1024 * 1024,
"ExpiryTime": "*month",
"BalanceWeight": 1200, # Consumido primero
"Weight": 95
},
# Límite de $50 (bloqueador)
{
"Identifier": "*topup_reset",
"BalanceId": "Overage_Cap",
"BalanceType": "*monetary",
"DestinationIDs": "*any",
"Units": 5000, # $50.00 máximo de sobrecarga
"ExpiryTime": "*month",
"BalanceWeight": 1000, # Consumido después de incluido
"Blocker": True, # Detener cuando se gaste $50
"Weight": 90
}
]
}]
}
Flujo:
- El cliente usa 10GB incluidos → GRATIS (de Included_Data_10GB).
- El cliente usa 5GB adicionales → Cargado desde Overage_Cap (tarifas PAYG).
- Cuando Overage_Cap llega a $0 → Todo uso bloqueado (límite de gastos alcanzado).
3. Prueba Gratuita Limitada por Tiempo
Proporcionar uso gratuito limitado para cuentas de prueba:
Action_Trial_Account = {
"id": "0",
"method": "ApierV1.SetActions",
"params": [{
"ActionsId": "Action_trial-100-minutes",
"Overwrite": True,
"Tenant": tenant,
"Actions": [
# 100 minutos gratis (bloqueador - se detiene cuando se agota)
{
"Identifier": "*topup",
"BalanceId": f"Trial_Voice__{100 * 60 * 1000000000}",
"BalanceType": "*voice",
"DestinationIDs": "Dest_Domestic_All",
"Units": 100 * 60 * 1000000000, # 100 minutos
"ExpiryTime": "+720h", # 30 días
"BalanceWeight": 1200,
"Blocker": True, # Sin uso después de 100 minutos
"Weight": 10
}
]
}]
}
Resultado: El cliente obtiene exactamente 100 minutos gratis. Después de eso, todas las llamadas están bloqueadas (sin cargos automáticos).
4. Bloqueo Específico de Destinos
Bloquear destinos específicos mientras se permiten otros:
Action_Block_Premium_Numbers = {
"id": "0",
"method": "ApierV1.SetActions",
"params": [{
"ActionsId": "Action_block-premium",
"Overwrite": True,
"Tenant": tenant,
"Actions": [
# Uso regular permitido
{
"Identifier": "*topup_reset",
"BalanceId": "Regular_Usage",
"BalanceType": "*monetary",
"DestinationIDs": "Dest_Domestic_All",
"Units": 10000, # $100
"ExpiryTime": "*month",
"BalanceWeight": 1000,
"Weight": 20
},
# Bloquear números premium (0900, etc.)
{
"Identifier": "*topup",
"BalanceId": "Premium_Blocker",
"BalanceType": "*monetary",
"DestinationIDs": "Dest_Domestic_Premium",
"Units": 0, # Valor cero
"BalanceWeight": 2000, # Mayor peso - verificado primero para premium
"Blocker": True,
"Weight": 10
}
]
}]
}
Flujo:
- Llamadas nacionales → Usa el saldo Regular_Usage.
- Llamadas a números premium → Coincide con Premium_Blocker (peso 2000 > 1000) → BLOQUEADO.
Bloqueador vs Saldos Deshabilitados
No confundas Blocker con Disabled:
| Característica | Bloqueador | Deshabilitado |
|---|---|---|
| Propósito | Detener uso cuando el saldo se agota | Pausar temporalmente un saldo |
| Cuando el valor > 0 | El saldo se utiliza normalmente | El saldo se omite/ignora |
| Cuando el valor = 0 | Bloquea todo uso adicional | El saldo se omite (se intenta el siguiente saldo) |
| Caso de Uso | Límites de gastos, caps, límites de prueba | Suspender temporalmente un saldo específico |
# Bloqueador: Permite 10GB, luego bloquea todo
{
"BalanceId": f"Data_Cap__{10 * 1024 * 1024 * 1024}",
"Units": 10 * 1024 * 1024 * 1024,
"Blocker": True # Detiene el uso a los 10GB
}
# Deshabilitado: Ignora este saldo por completo (pausado temporalmente)
{
"BalanceId": f"Bonus_Data__{5 * 1024 * 1024 * 1024}",
"Units": 5 * 1024 * 1024 * 1024,
"Disabled": True # Este saldo no se utilizará en absoluto
}
Ejemplo Práctico: Plan Híbrido con Límite de Seguridad
Combina saldos unitarios, desbordamiento monetario y un límite de bloqueador:
Action_Safe_Hybrid_Plan = {
"id": "0",
"method": "ApierV1.SetActions",
"params": [{
"ActionsId": "Action_safe-hybrid-plan",
"Overwrite": True,
"Tenant": tenant,
"Actions": [
{
"Identifier": "*reset_account",
"Weight": 700
},
# 500 minutos nacionales incluidos
{
"Identifier": "*topup_reset",
"BalanceId": f"Domestic_Voice__{500 * 60 * 1000000000}",
"BalanceType": "*voice",
"DestinationIDs": "Dest_Domestic_All",
"Units": 500 * 60 * 1000000000,
"ExpiryTime": "*month",
"BalanceWeight": 1200,
"Weight": 95
},
# $20 de límite de sobrecarga
{
"Identifier": "*topup_reset",
"BalanceId": "Overage_Allowance",
"BalanceType": "*monetary",
"DestinationIDs": "*any",
"Units": 2000, # $20.00
"ExpiryTime": "*month",
"BalanceWeight": 1000,
"Weight": 90
},
# $50 LÍMITE DURO (bloqueador)
{
"Identifier": "*topup_reset",
"BalanceId": "Hard_Spending_Cap",
"BalanceType": "*monetary",
"DestinationIDs": "*any",
"Units": 5000, # $50.00 máximo absoluto
"ExpiryTime": "*month",
"BalanceWeight": 500, # Más bajo que el desbordamiento - usado al final
"Blocker": True, # DETENER cuando se alcance el límite
"Weight": 85
}
]
}]
}
Viaje del Cliente:
- 0-500 minutos: Usa Domestic_Voice_500min (GRATIS).
- 500-700 minutos: Usa Overage_Allowance a $0.10/min = $20 (200 min).
- 700-1200 minutos: Usa Hard_Spending_Cap a $0.10/min = $50 (500 min).
- A los 1200 minutos: Hard_Spending_Cap agotado → TODO USO BLOQUEADO.
El cliente obtiene un total de 1200 minutos, con un gasto limitado a un máximo de $50 en sobrecarga.
Mejores Prácticas con Bloqueadores
-
Usa Alto Peso para Bloqueadores
"BalanceWeight": 9999 # Asegúrate de que el bloqueador se verifique primero -
Bloqueadores de Valor Cero para Bloqueo Inmediato
"Units": 0, # Bloquear inmediatamente
"Blocker": True -
Notifica a los Clientes Antes de la Agotamiento del Bloqueador
- Usa ActionTriggers para enviar notificaciones al 80%, 90%, 100% del uso del bloqueador.
- Da a los clientes la opción de aumentar el límite antes de que ocurra el bloqueo.
-
Elimina Bloqueadores al Reanudar
# Usa *remove_balance para eliminar el bloqueador
{
"Identifier": "*remove_balance",
"BalanceId": "Suspension_Blocker"
} -
Prueba el Comportamiento del Bloqueador
- Verifica que el bloqueador devuelva el error
INSUFFICIENT_CREDIT_BALANCE_BLOCKER. - Confirma que los CDRs muestren costo = -1.0 cuando esté bloqueado.
- Prueba que otros saldos NO se utilicen después del agotamiento del bloqueador.
- Verifica que el bloqueador devuelva el error
Solución de Problemas de Bloqueadores
Problema: Uso no bloqueado a pesar de que el bloqueador esté en cero
Causas Posibles:
- Peso del bloqueador demasiado bajo (se verifican otros saldos primero).
- DestinationIDs no coinciden con el destino de uso.
- El campo Blocker no está configurado en
True.
Solución:
# Verifica la configuración del bloqueador
OCS_Obj.SendData({
'method': 'ApierV2.GetAccount',
'params': [{"Tenant": tenant, "Account": "service_uuid"}]
})
# Verifica:
# - Blocker: true
# - BalanceWeight es el más alto (por ejemplo, 9999)
# - DestinationIDs incluye el destino de uso
# - Valor = 0
Problema: Uso bloqueado inesperadamente
Causa Posible: Saldo bloqueador creado involuntariamente con valor bajo.
Solución: Verifica todos los saldos para Blocker: true y verifica que sus valores sean apropiados para tu caso de uso.
Reglas de Consumo de Saldos
Regla 1: Prioridad de Precisión de Destino
Los saldos con mayor precisión de destino (coincidencia de destino más específica) se consumen primero. Esto se determina por la longitud de coincidencia del prefijo.
# El cliente llama al +44-20-1234-5678 (Londres, Reino Unido)
Saldo 1:
DestinationIDs: "Dest_UK_London" # Prefijo: "4420" (precisión: 4)
Valor: 100 minutos
Peso: 10
Saldo 2:
DestinationIDs: "Dest_UK_All" # Prefijo: "44" (precisión: 2)
Valor: 200 minutos
Peso: 10
# Se consume primero el Saldo 1 (precisión 4 > precisión 2)
# La coincidencia de destino más específica gana.
Caso de Uso: Los saldos específicos de ciudad o región tienen prioridad sobre los saldos a nivel nacional.
Regla 2: Prioridad de Peso (Misma Precisión)
Cuando la precisión del destino es igual, se consumen primero los saldos con mayor peso.
Saldo 1:
ID: "Premium_Data"
Valor: 5GB
Peso: 20
Saldo 2:
ID: "Standard_Data"
Valor: 10GB
Peso: 10
# Se consume primero el Saldo 1 (peso 20 > peso 10)
# Aunque el Saldo 2 tiene más datos.
Caso de Uso: Los saldos prioritarios (datos adicionales consumidos antes que los datos regulares).
Regla 3: Primero el Más Antiguo
Cuando el peso coincide, se utiliza primero el saldo más antiguo.
Saldo 1:
ID: "Data_Package_A"
Valor: 5GB
Caducidad: 2024-12-25
Peso: 10
Saldo 2:
ID: "Data_Package_B"
Valor: 10GB
Caducidad: 2025-01-15
Peso: 10
Para controlar el orden de consumo, usa diferentes pesos:
# Enfoque correcto: Usa peso para priorizar saldos que están a punto de caducar.
Saldo 1:
ID: "Data_Package_A"
Valor: 5GB
Caducidad: 2024-12-25
Peso: 11 # Mayor peso = consumido primero
Saldo 2:
ID: "Data_Package_B"
Valor: 10GB
Caducidad: 2025-01-15
Peso: 10 # Peso más bajo = consumido segundo
# Se consume primero el Saldo 1 (peso 11 > peso 10).
Mejor Práctica: Si deseas asegurarte de que los saldos que están a punto de caducar se consuman primero, asígnales pesos más altos al crearlos.
Ejemplos Prácticos
Ejemplo 1: Addon de Datos Simple (Independiente)
Escenario: El cliente puede comprar el addon "5GB 5 días" múltiples veces, cada uno creando un saldo separado.
Implementación:
- name: Generar UUID para saldo único
set_fact:
uuid: "{{ 99999999 | random | to_uuid }}"
- name: Agregar saldo independiente de 5GB
uri:
url: "http://{{ crm_config.ocs.cgrates }}/jsonrpc"
method: POST
body_format: json
body:
{
"method": "ApierV1.AddBalance",
"params": [{
"Tenant": "{{ crm_config.ocs.ocsTenant }}",
"Account": "{{ service_uuid }}",
"BalanceType": "*data",
"Categories": "*any",
"Balance": {
"ID": "Data_5GB_{{ uuid[0:8] }}",
"Value": 5368709120,
"ExpiryTime": "+120h", # 5 días
"Weight": 10
}
}]
}
Experiencia del Cliente:
- Compra el addon el 24 de diciembre → Obtiene 5GB que expiran el 29 de diciembre.
- Compra el addon el 25 de diciembre → Obtiene 5GB que expiran el 30 de diciembre.
- Ambos saldos coexisten; el orden de consumo depende de los pesos de los saldos (ambos tienen peso 10, por lo que el orden no está garantizado).
Ejemplo 2: Paquete de Datos Rollover
Escenario: "Plan Mensual de 50GB" donde los datos no utilizados se transfieren cuando el cliente recarga antes.
Implementación:
- name: Agregar saldo de datos rollover
uri:
url: "http://{{ crm_config.ocs.cgrates }}/jsonrpc"
method: POST
body_format: json
body:
{
"method": "ApierV1.AddBalance",
"params": [{
"Tenant": "{{ crm_config.ocs.ocsTenant }}",
"Account": "{{ service_uuid }}",
"BalanceType": "*data",
"Balance": {
"ID": "Rollover_Monthly_50GB",
"Value": 53687091200, # 50GB
"ExpiryTime": "+720h", # 30 días
"Weight": 10
}
}]
}
Tipo de Acción: Usa el comportamiento predeterminado *topup (rollover habilitado).
Experiencia del Cliente:
- Día 1: Obtiene 50GB que expiran en 30 días.
- Día 20: Usó 30GB, tiene 20GB restantes.
- Día 20: Recarga nuevamente → Obtiene 70GB en total (20GB + 50GB), la caducidad se extiende a +30 días desde el Día 20.
Ejemplo 3: Plan Mensual Fijo (Sin Rollover)
Escenario: Plan "Mensual Ilimitado de 100GB" que se reinicia exactamente a 100GB cada mes, sin rollover.
Implementación:
- name: Crear acción de reinicio mensual
uri:
url: "http://{{ crm_config.ocs.cgrates }}/jsonrpc"
method: POST
body_format: json
body:
{
"method": "ApierV1.SetActions",
"params": [{
"ActionsId": "Action_Monthly_100GB_Reset",
"Overwrite": true,
"Actions": [{
"Identifier": "*topup_reset",
"BalanceType": "*data",
"Units": 107374182400, # 100GB
"ExpiryTime": "*monthly",
"BalanceId": "Monthly_Plan__107374182400",
"Weight": 10
}]
}]
}
- name: Crear ActionPlan mensual
uri:
url: "http://{{ crm_config.ocs.cgrates }}/jsonrpc"
method: POST
body_format: json
body:
{
"method": "ApierV1.SetActionPlan",
"params": [{
"Id": "ActionPlan_Monthly_100GB",
"ActionPlan": [{
"ActionsId": "Action_Monthly_100GB_Reset",
"Time": "*monthly",
"Weight": 10
}],
"Overwrite": true,
"ReloadScheduler": true
}]
}
- name: Asignar ActionPlan a la cuenta
uri:
url: "http://{{ crm_config.ocs.cgrates }}/jsonrpc"
method: POST
body_format: json
body:
{
"method": "ApierV2.SetAccount",
"params": [{
"Account": "{{ service_uuid }}",
"ActionPlanIds": ["ActionPlan_Monthly_100GB"],
"ReloadScheduler": true
}]
}
Experiencia del Cliente:
- Mes 1: Obtiene 100GB, usa 95GB, tiene 5GB restantes.
- Mes 2: El saldo se reinicia a 100GB (se pierden los 5GB no utilizados).
- Mes 2: Usa 20GB, tiene 80GB restantes.
- Mes 3: El saldo se reinicia a 100GB (se pierden los 80GB no utilizados).
Ejemplo 4: Balances de Múltiples Niveles con Prioridad de Peso
Escenario: El cliente tiene "Datos de Bonificación" (alta prioridad) y "Datos Regulares" (baja prioridad). Se consumen primero los datos de bonificación.
Implementación:
# Agregar datos de bonificación con alto peso
- name: Agregar saldo de datos de bonificación
uri:
url: "http://{{ crm_config.ocs.cgrates }}/jsonrpc"
method: POST
body_format: json
body:
{
"method": "ApierV1.AddBalance",
"params": [{
"Account": "{{ service_uuid }}",
"BalanceType": "*data",
"Balance": {
"ID": "Bonus_Data",
"Value": 5368709120, # 5GB
"ExpiryTime": "+240h", # 10 días
"Weight": 20
}
}]
}
# Agregar datos regulares con peso normal
- name: Agregar saldo de datos regulares
uri:
url: "http://{{ crm_config.ocs.cgrates }}/jsonrpc"
method: POST
body_format: json
body:
{
"method": "ApierV1.AddBalance",
"params": [{
"Account": "{{ service_uuid }}",
"BalanceType": "*data",
"Balance": {
"ID": "Regular_Data",
"Value": 53687091200, # 50GB
"ExpiryTime": "+720h", # 30 días
"Weight": 10
}
}]
}
Experiencia del Cliente:
- Tiene 5GB de datos de bonificación (peso 20) + 50GB de datos regulares (peso 10).
- Consume primero todos los 5GB de datos de bonificación.
- Luego consume del pool de 50GB de datos regulares.
- Los datos regulares se preservan por más tiempo.
Acciones Comunes
CGRateS admite múltiples identificadores de acción para diferentes operaciones:
Manipulación de Saldos
*topup - Agregar a saldo existente (rollover)
*topup_reset - Reiniciar saldo a nuevo valor (sin rollover)
*debit - Restar de saldo
*debit_reset - Establecer saldo a valor negativo
*reset_account - Eliminar todos los saldos
Diseño de Productos Adicionales
Al diseñar productos adicionales en OmniCRM, considera estas preguntas:
Pregunta 1: ¿Deben apilarse los saldos?
Sí (Independiente) → Usa IDs de Saldo únicos (con UUID)
No (Reemplazar) → Usa ID de Saldo fijo con *topup_reset
Sí (Rollover) → Usa ID de Saldo fijo con *topup
Pregunta 2: ¿Qué pasa con el saldo no utilizado?
Rollover → Usa acción *topup
Perdido → Usa acción *topup_reset
Pools separados → Usa IDs de Saldo únicos
Pregunta 3: ¿Cómo deben consumirse los saldos?
Primero el más antiguo → Usa diferentes pesos (asigna mayor peso a los saldos más antiguos/próximos a caducar)
Primero el premium → Diferentes pesos (peso más alto = mayor prioridad)
Orden específico → Usa pesos: 30 (premium), 20 (bono), 10 (regular)
Aleatorio/Sin preferencia → Usa el mismo peso (el orden de consumo no está garantizado)
Pregunta 4: ¿Cuál es la estrategia de caducidad?
Duración fija → Usa caducidad relativa (+720h para 30 días)
Fin de mes → Usa *monthly en ActionPlan
Nunca expira → Usa *unlimited o duración muy larga
Estructura de Acción de CGRateS en Playbooks
Aquí está la estructura completa para crear una Acción:
- name: Crear Acción CGRateS
uri:
url: "http://{{ crm_config.ocs.cgrates }}/jsonrpc"
method: POST
body_format: json
body:
{
"method": "ApierV1.SetActions",
"params": [{
"ActionsId": "Action_Name_Here",
"Overwrite": true, # Reemplazar si existe
"Actions": [
{
"Identifier": "*topup", # o *topup_reset
"BalanceType": "*data", # *data, *voice, *sms, *monetary
"Units": 5368709120, # Cantidad a agregar
"ExpiryTime": "+120h", # +Xh, *unlimited, *monthly
"DestinationIds": "*any", # Usualmente *any
"BalanceId": "Balance_Name", # Único o compartido
"Weight": 10, # Prioridad (mayor = consumido primero)
"Blocker": false, # Si es verdadero, previene saldo negativo
"Disabled": false, # Si es verdadero, el saldo está deshabilitado
"SharedGroups": "" # Grupo de saldo compartido (opcional)
},
{
"Identifier": "*cdrlog", # Registrar esta acción como CDR
"BalanceType": "*generic",
"ExtraParameters": "{\"Category\":\"^activation\",\"Destination\":\"Addon Name\"}"
}
]
}]
}
Descripciones de Campos
ActionsId - Identificador único para este conjunto de acciones.
Identifier - El tipo de operación (*topup, *topup_reset, *cdrlog, etc.).
BalanceType - Tipo de saldo:
*data- Saldos de datos (bytes)*voice- Saldos de voz (segundos)*sms- Saldos de SMS (conteo)*monetary- Saldos monetarios (unidades de moneda)*generic- Saldos genéricos
Units - Cantidad a agregar/deducir (en unidades base: bytes para datos, segundos para voz).
ExpiryTime - Cuando expira el saldo:
+Xh- Relativo (por ejemplo,+720h= 30 días)*unlimited- Nunca expira*monthly- Fin de mes2024-12-31T23:59:59Z- Marca de tiempo absoluta
BalanceId - Identificador para este saldo (ID compartido = interactúa, ID único = independiente).
Weight - Prioridad (número más alto = mayor prioridad, consumido primero).
Blocker - Si es verdadero, impide que la cuenta se vuelva negativa.
Disabled - Si es verdadero, el saldo existe pero no se puede usar.
Ejecutando Acciones
Una vez que se crea una Acción, ejecútala en una cuenta:
- name: Ejecutar Acción en Cuenta
uri:
url: "http://{{ crm_config.ocs.cgrates }}/jsonrpc"
method: POST
body_format: json
body:
{
"method": "APIerSv1.ExecuteAction",
"params": [{
"Tenant": "{{ crm_config.ocs.ocsTenant }}",
"Account": "{{ service_uuid }}",
"ActionsId": "Action_Name_Here"
}]
}
Esto ejecuta todas las operaciones definidas en la Acción en la cuenta especificada.
Enfoques de Gestión de Saldos
Hay tres enfoques principales para gestionar saldos en CGRateS, cada uno con diferentes compensaciones para la experiencia del cliente, la previsibilidad de facturación y la complejidad operativa.
Comparación: Unitario vs Monetario vs Híbrido
Esta tabla resume las compensaciones entre los tres enfoques de saldo:
| Característica | Unitario | Monetario (PAYG) | Híbrido |
|---|---|---|---|
| Previsibilidad del Cliente | ✅ Costo mensual fijo | ❌ Costos variables | ⚠️ Mayormente predecible |
| Flexibilidad de Destino | ❌ Limitado a destinos incluidos | ✅ Llamar/usar en cualquier lugar | ✅ Incluido + en cualquier lugar |
| Manejo de Sobrecargas | ❌ Corte duro | ✅ Uso automático | ✅ Desbordamiento automático |
| Riesgo de Sorpresas en la Factura | ✅ Bajo (caps duros) | ❌ Alto (facturación ilimitada) | ⚠️ Moderado (desbordamiento limitado) |
| Complejidad de Configuración | ⚠️ Moderada (muchos saldos) | ✅ Simple (un saldo) | ❌ Complejo (ambos) |
| Optimización de Ingresos | ⚠️ Menor ARPU | ✅ Mayor ARPU de usuarios intensivos | ✅ ARPU equilibrado |
| Satisfacción del Cliente | ✅ Alta (sin sorpresas) | ❌ Baja (sorpresas en la factura) | ✅ Alta (lo mejor de ambos) |
| Mejor Para | Usuarios predecibles | Usuarios ocasionales | La mayoría de los clientes |
Enfoque 1: Saldos Unitarios
Concepto: Proporcionar cantidades específicas para destinos específicos. Cada saldo tiene una cantidad fija (minutos, GB, conteo de SMS) vinculada a destinos específicos. Cuando se agota, el uso se bloquea a menos que haya una alternativa monetaria.
Ejemplo: Plan Nacional con Múltiples Saldos
Action_Domestic_Plan = {
"id": "0",
"method": "ApierV1.SetActions",
"params": [{
"ActionsId": "Action_domestic-plan",
"Overwrite": True,
"Tenant": tenant,
"Actions": [
{
"Identifier": "*reset_account",
"Weight": 700
},
# 500 minutos de llamadas nacionales
{
"Identifier": "*topup_reset",
"BalanceId": f"Domestic_Voice__{500 * 60 * 1000000000}",
"BalanceType": "*voice",
"DestinationIDs": "Dest_Domestic_All", # SOLO nacionales
"Units": 500 * 60 * 1000000000, # 500 minutos en nanosegundos
"ExpiryTime": "*month",
"BalanceWeight": 1200,
"Weight": 90
},
# 1000 SMS nacionales
{
"Identifier": "*topup_reset",
"BalanceId": "Domestic_SMS__1000",
"BalanceType": "*sms",
"DestinationIDs": "Dest_Domestic_All",
"Units": 1000,
"ExpiryTime": "*month",
"BalanceWeight": 1200,
"Weight": 89
},
# 10GB de datos nacionales
{
"Identifier": "*topup_reset",
"BalanceId": f"Domestic_Data__{10 * 1024 * 1024 * 1024}",
"BalanceType": "*data",
"DestinationIDs": "Dest_PLMN_OnNet", # Tu PLMN en red
"Units": 10 * 1024 * 1024 * 1024,
"ExpiryTime": "*month",
"BalanceWeight": 1200,
"Weight": 88
},
{
"Identifier": "*cdrlog",
"BalanceType": "*generic",
"ExtraParameters": "{\"Category\":\"^activation\",\"Destination\":\"Domestic Plan\"}",
"Weight": 80
}
]
}]
}
result = OCS_Obj.SendData(Action_Domestic_Plan)
assert result['error'] is None or result['error'] == "EXISTS"
Cómo Funciona Esto:
- Llamadas nacionales (1-555-1234) → Usa el saldo de 500 minutos.
- Llamadas internacionales (44-20-xxx) → NO hay saldo disponible, bloqueado O usa el saldo monetario si está disponible.
- SMS nacionales → Usa el saldo de 1000 SMS.
- Uso de datos en casa → Usa el saldo de 10GB.
- Datos en roaming → NO hay saldo disponible (necesitaría saldo de datos en roaming separado).
Pros:
- Costos predecibles para los clientes.
- Sin sorpresas en la factura.
- Límites claros.
Contras:
- Menos flexibilidad: no se puede usar el servicio fuera de los destinos incluidos.
- Requiere múltiples saldos para diferentes casos de uso.
- El cliente puede sentirse restringido.
Enfoque 2: Monetario (PAYG)
Concepto: Proporcionar crédito monetario cargado a tarifas específicas de destino. Un solo saldo monetario utilizado para TODOS los tipos de uso. CGRateS busca la tarifa para cada destino y deduce el costo del crédito.
Nota: PAYG requiere definir Perfiles de Tarifas para cada destino para establecer el monto en dólares por unidad. Consulta Perfiles de Tarifas para Saldos PAYG/Monetarios para la configuración completa.
Ejemplo: Crédito PAYG de $50
Action_PAYG_Credit = {
"id": "0",
"method": "ApierV1.SetActions",
"params": [{
"ActionsId": "Action_payg-50-credit",
"Overwrite": True,
"Tenant": tenant,
"Actions": [
# Saldo monetario de $50
{
"Identifier": "*topup",
"BalanceId": "PAYG_Monetary_Balance",
"BalanceType": "*monetary",
"DestinationIDs": "*any", # Funciona para CUALQUIER destino
"Units": 5000, # $50.00 (en centavos)
"ExpiryTime": "+2160h", # 90 días
"BalanceWeight": 1000, # Más bajo que los saldos unitarios
"Weight": 90
},
{
"Identifier": "*cdrlog",
"BalanceType": "*generic",
"ExtraParameters": "{\"Category\":\"^activation\",\"Destination\":\"$50 PAYG Credit\"}",
"Weight": 80
}
]
}]
}
result = OCS_Obj.SendData(Action_PAYG_Credit)
Cómo Funciona PAYG:
Escenario 1: Llamada nacional, 10 minutos
- Tarifa: $0.10/min
- Cargo: 10 × $0.10 = $1.00
- Restante: $49.00
Escenario 2: Llamada a UK, 5 minutos
- Tarifa: $0.25/min + $0.05 tarifa de conexión
- Cargo: (5 × $0.25) + $0.05 = $1.30
- Restante: $47.70
Escenario 3: Roaming en Verizon, 100MB de datos
- Tarifa: $2.00/MB
- Cargo: 100 × $2.00 = $200.00
- Resultado: Fondos insuficientes → Sesión bloqueada a ~$47 de valor (~23MB).
Pros:
- Un saldo para todo: muy flexible.
- El cliente puede usar el servicio en cualquier lugar donde se definan tarifas.
- Configuración simple.
Contras:
- Costos impredecibles para los clientes.
- Riesgo de sorpresas en la factura (especialmente en roaming).
- Puede ser costoso para usuarios intensivos.
Enfoque 3: Híbrido (Lo Mejor de Ambos)
Concepto: Combina saldos unitarios con una alternativa monetaria. Usa saldos unitarios de alto peso para el uso incluido, con un saldo monetario de bajo peso como desbordamiento. La mejor experiencia para el cliente con costos base predecibles y sobrecargas flexibles.
Ejemplo: Plan Híbrido Flexible
Action_Hybrid_Plan = {
"id": "0",
"method": "ApierV1.SetActions",
"params": [{
"ActionsId": "Action_hybrid-flex-plan",
"Overwrite": True,
"Tenant": tenant,
"Actions": [
{
"Identifier": "*reset_account",
"Weight": 700
},
# ============= Saldos UNITARIOS (Incluidos) =============
# 500 minutos de voz nacionales
{
"Identifier": "*topup_reset",
"BalanceId": f"Domestic_Voice__{500 * 60 * 1000000000}",
"BalanceType": "*voice",
"DestinationIDs": "Dest_Domestic_All",
"Units": 500 * 60 * 1000000000,
"ExpiryTime": "*month",
"BalanceWeight": 1200, # Consumido PRIMERO para llamadas nacionales
"Weight": 95
},
# 100 minutos de voz internacionales
{
"Identifier": "*topup_reset",
"BalanceId": f"International_Voice__{100 * 60 * 1000000000}",
"BalanceType": "*voice",
"DestinationIDs": "Dest_International_All",
"Units": 100 * 60 * 1000000000,
"ExpiryTime": "*month",
"BalanceWeight": 1150, # Consumido PRIMERO para internacionales
"Weight": 94
},
# 1000 SMS nacionales
{
"Identifier": "*topup_reset",
"BalanceId": "Domestic_SMS__1000",
"BalanceType": "*sms",
"DestinationIDs": "Dest_Domestic_All",
"Units": 1000,
"ExpiryTime": "*month",
"BalanceWeight": 1200,
"Weight": 93
},
# 15GB de datos nacionales
{
"Identifier": "*topup_reset",
"BalanceId": f"Domestic_Data__{15 * 1024 * 1024 * 1024}",
"BalanceType": "*data",
"DestinationIDs": "Dest_PLMN_OnNet",
"Units": 15 * 1024 * 1024 * 1024,
"ExpiryTime": "*month",
"BalanceWeight": 1200,
"Weight": 92
},
# 2GB de datos en roaming (Zona 1)
{
"Identifier": "*topup_reset",
"BalanceId": f"Roaming_Zone1_Data__{2 * 1024 * 1024 * 1024}",
"BalanceType": "*data",
"DestinationIDs": "Dest_PLMN_Zone_NorthAmerica",
"Units": 2 * 1024 * 1024 * 1024,
"ExpiryTime": "*month",
"BalanceWeight": 1100,
"Weight": 91
},
# ============= SALDO MONETARIO (Desbordamiento/PAYG) =============
# $20 para sobrecargas
{
"Identifier": "*topup",
"BalanceId": "PAYG_Overflow_Balance",
"BalanceType": "*monetary",
"DestinationIDs": "*any",
"Units": 2000, # $20.00
"ExpiryTime": "*month",
"BalanceWeight": 1000, # Consumido ÚLTIMO (desbordamiento)
"Weight": 90
},
{
"Identifier": "*cdrlog",
"BalanceType": "*generic",
"ExtraParameters": "{\"Category\":\"^activation\",\"Destination\":\"Hybrid Flex Plan\"}",
"Weight": 80
}
]
}]
}
result = OCS_Obj.SendData(Action_Hybrid_Plan)
Flujo de Consumo de Saldos:
Ejemplos de Escenarios:
Escenario 1: 600 minutos utilizados
- Primeros 500 minutos → Usa "Domestic_Voice_500min" (peso 1200) - GRATIS.
- Siguientes 100 minutos → "Domestic_Voice_500min" agotado, cae en "PAYG_Overflow_Balance" (peso 1000).
- Cargado: 100 min × $0.10 = $10.00 del saldo monetario.
- Restante: $10.00.
Escenario 2: 150 minutos internacionales (a UK)
- Primeros 100 minutos → Usa "International_Voice_100min" (peso 1150) - GRATIS.
- Siguientes 50 minutos → Cae en "PAYG_Overflow_Balance".
- Cargado: (50 × $0.25) + $0.05 conexión = $12.55.
- Restante: $7.45 monetarios (si comienza con $20).
Pros:
- Costo base predecible.
- Flexibilidad para sobrecargas ocasionales.
- Mejor experiencia para el cliente.
- Sin corte duro: el servicio continúa.
Contras:
- Más complejo de configurar.
- Más complejo de explicar a los clientes.
- Requiere gestión cuidadosa de los pesos de los saldos.
Escenarios de Uso del Mundo Real
Estos escenarios demuestran cómo los conceptos se unen en la práctica. Cada uno muestra el flujo completo desde el evento de uso hasta la deducción o bloqueo del saldo.
Escenario 1: Cliente en Casa Llama a UK
El cliente tiene: Plan Nacional (500min nacionales, 1000 SMS, 10GB de datos).
Acción: Llama a un número de UK (44-20-7946-0958) durante 10 minutos.
Verificación de Saldo:
- CGRateS recibe la llamada a
44207946.... - Coincide el destino:
Dest_International_UK. - Verifica saldo con
DestinationIDs: "Dest_International_UK". - NO se encuentra saldo coincidente.
- Verifica saldo monetario.
- NO se encuentra saldo monetario.
- Resultado: Llamada BLOQUEADA (saldo insuficiente).
Solución: El cliente necesita un Plan Internacional O crédito PAYG para realizar llamadas a UK.
Escenario 2: Cliente en Roaming en EE. UU. Usa Datos
El cliente tiene: Plan Nacional + addon de Roaming de 5GB en EE. UU.
Acción: Roaming en Verizon (PLMN mcc310.mnc004), usa 2GB de datos.