Ciclo mensual de facturación
Cuándo usar este documento
El primer y el cuarto día de cada mes, para facturar el mes que acaba de cerrar.
Es el proceso manual más sensible del sistema: produce documentos fiscales y no tiene procedimiento de reversión automatizado.
Audiencia
Operación y soporte. Requiere acceso de consola a la instancia del participante y, para contratos de costo fijo, también a cfe-rates-microservice.
Estado documental
- Estado: Pendiente de revisión SME
- Última actualización: 2026-08-02
- Owner: Pendiente
- SME requerido: operación (quien ejecuta el ciclo)
- Fuente resumida: secuencia declarada por operación, contrastada contra los comandos del repositorio.
Qué está confirmado y qué no
La secuencia de comandos de esta página proviene de operación: es lo que se ejecuta hoy. Los comandos de sy-energy están verificados contra el código.
Lo que sigue abierto son los porqués — por qué ciertos rangos empiezan dos días antes del mes, por qué solo algunos centros de carga reciben ciertos cálculos — y los tiempos de cada etapa. Ver Preguntas abiertas.
Por qué son dos fechas
Los contratos se facturan según su tipo de producto, y cada tipo necesita información distinta:
| Día | Contratos | Tipo de producto | Por qué esa fecha |
|---|---|---|---|
| 1 | Costo fijo por centro de carga | fixed, fixed-with-energy-loss | El precio está pactado. Basta con la demanda del mes cerrado. |
| 4 | Pass-through por centro de carga | pass-through | El costo depende de datos del mercado que CENACE publica con desfase. |
El día 4 está codificado en la configuración
No es una convención arbitraria. Cada participante tiene un ajuste de fecha de corte pass-through, cuyo valor por omisión es 3 días. El cálculo desplaza por ese número de días la ventana con la que busca los ECD según su fecha de emisión.
Correr el cálculo el día 4 significa correrlo después del corte.
Verificar el corte de cada participante
El valor por omisión es 3, pero es configurable. Si un participante tiene otro valor, su ciclo no corre el día 4 sino el que corresponda a su configuración.
Convenciones de esta página
Los ejemplos usan el cierre de julio de 2026, ejecutado a principios de agosto:
| Marcador | Significado | En el ejemplo |
|---|---|---|
<RMU> | Identificador del centro de carga | — |
<AAAA> / <MM> | Año y mes del periodo que se cierra | 2026 / 7 |
| Primer día del mes | 2026-07-01 | |
| Último día del mes | 2026-07-31 | |
| Inicio extendido | Dos días antes del mes | 2026-06-29 |
Confirmar la instancia antes de cualquier comando
Hay una instancia por participante. Un comando correcto en la instancia equivocada produce datos incorrectos sin reportar error.
Antes del día 1 — Tarifas CFE
Solo aplica a los contratos de costo fijo. Se ejecuta en cfe-rates-microservice, no en sy-energy.
1. Registrar las tarifas del mes
Hoy es un paso manual, cargando el archivo de tarifas publicado por CFE:
php artisan app:import-rates-from-csv --filename=cfe-rates-<AAAA>-<MM>.csv2. Calcular la tarifa del centro de carga
php artisan app:calculate-rate-command --rmu=<RMU> --period=<AAAA>-<MM> --owner=<participante>Requiere el consumo completo del mes
El cálculo se hace sobre la demanda ("Consumo") del centro de carga. Si falta demanda del mes, la tarifa sale mal — y con ella el contrato que el cliente registrará en sy-energy.
Verificar la completitud de la demanda antes de calcular, no después.
Con la tarifa calculada, el cliente puede registrar su tarifa fija en sy-energy. Ese contrato es el insumo del cierre del día 1.
Día 1 — Contratos de costo fijo
Paso 1. Verificar que los contratos están registrados
php artisan contracts:active --date=2026-07-01Exporta a CSV los contratos vigentes en la fecha. Sirve para dos cosas: confirmar que las tarifas quedaron registradas, y dejar la evidencia del punto de partida del cierre.
Un solo contrato activo por centro de carga
El sistema exige exactamente uno. Con cero o con más de uno, ese centro de carga no genera estado de cuenta y se notifica al administrador. Es la causa más frecuente de un centro que queda sin facturar; este listado es donde se detecta.
Paso 2. Generar la demanda mensual
php artisan demand:monthly 2026-07-01 <RMU>La demanda debe estar completa antes de este paso
demand:monthly consolida lo que hay. Si faltan horas, consolida un mes incompleto y el importe sale por debajo del real, sin ningún error.
Si hay huecos, reconstruir primero la demanda horaria:
php artisan demand:fix-hourly 2026-07-01 2026-07-31 --rmu=<RMU>Opciones: --force=true la crea aunque ya exista, --retry=true la reprocesa.
Paso 3. Generar el estado de cuenta
php artisan file:statement-account --year=2026 --month=7 --rmu=<RMU>Se ejecuta por centro de carga. El comando elige el constructor según el tipo de producto del contrato activo, así que los contratos de costo fijo se resuelven con la información ya disponible.
Paso 4. Revisar los importes
php artisan validate:rates --date=2026-07-01Imprime, para cada estado de cuenta generado del mes, el desglose por concepto:
file_id · rmu · periodo · cargo de transmisión · cargo administrativo · potencia MTR · potencia MDA · pérdidas · capacidad · congestión · total
Es la revisión final antes de facturar: permite comparar centros de carga entre sí y detectar totales en cero, importes anómalos o conceptos faltantes.
Qué buscar en la salida
Un total en cero, un concepto vacío que otros centros sí tienen, o un importe muy distinto al del mes anterior. Cualquiera de los tres significa que falta un insumo aguas arriba.
Paso 5. Facturar
Con los importes validados, la emisión sigue el flujo de CFDI. En la operación normal, la cadena diaria se encarga de crear las facturas pendientes, subirlas a S3 y cargarlas al SIM.
Para verificar antes de emitir, sin timbrar nada:
php artisan ecd:create_invoice --csvDía 4 — Contratos pass-through
El orden sí importa: cada paso depende de que el anterior haya terminado.
Paso 1. Despachar los cálculos por ECD y FUF
php artisan dispatch:calculations --operation_date=2026-06-29 --ending_at=2026-07-31Despacha los cálculos por FUF y por ECD del participante, aplicando sus exclusiones — hay slugs que no se despachan para ciertos participantes, de forma intencional.
El rango empieza dos días antes del mes
No es un error: el rango arranca el 29 de junio para cerrar julio. Lo mismo ocurre con distribución, MDA y MTR más abajo.
La razón exacta está pendiente de confirmar; ver Preguntas abiertas. Lo importante operativamente es respetarlo: recortar el rango al mes calendario produciría un cierre incompleto.
Paso 2. Generar la demanda mensual
php artisan demand:monthly 2026-07-01 <RMU>Mismo requisito que en el día 1: la demanda horaria debe estar completa antes.
Paso 3. Verificar la potencia MDA
php artisan verify:mda-deals --year=2026 --month=7Compara la potencia MDA contratada contra la del estado de cuenta. Con --fix=true corrige las diferencias que encuentre.
Paso 4. Calcular distribución, MDA y MTR
Aplican a todos los centros de carga, con el rango extendido:
php artisan dispatch:by-operation-date --slug=distribution --operation_date=2026-06-29 --ending_at=2026-07-31
php artisan dispatch:by-charge-center --slug=mda --operation_date=2026-06-29 --ending_at=2026-07-31
php artisan dispatch:by-charge-center --slug=mtr --operation_date=2026-06-29 --ending_at=2026-07-31Volumen
Sin --rmu, estos comandos despachan todos los centros de carga por cada día del rango: son cientos o miles de trabajos encolados.
Los comandos terminan de inmediato — encolar no es calcular. Antes de continuar, confirmar que los workers vaciaron la cola y que queue:failed no creció.
Paso 5. Calcular ancho de banda y congestión
A diferencia del paso anterior, estos solo aplican a ciertos centros de carga, y usan el rango del mes calendario, no el extendido:
php artisan dispatch:by-charge-center --slug=bandwidth --operation_date=2026-07-01 --ending_at=2026-07-31 --rmu=<RMU>
php artisan dispatch:by-charge-center --slug=congestion --operation_date=2026-07-01 --ending_at=2026-07-31 --rmu=<RMU>Se repite el comando una vez por cada centro de carga aplicable.
La lista de centros aplicables no está en el código
Qué centros de carga reciben ancho de banda y cuáles reciben congestión es conocimiento de operación: no se deduce de la aplicación.
Hoy esa lista vive en el procedimiento de quien ejecuta el cierre. Es el punto más frágil del ciclo: si se omite un centro de carga, su estado de cuenta sale sin ese concepto y nadie lo detecta salvo revisando el desglose del paso 8.
Paso 6. Cálculo estimado del mes
php artisan passthrough:estimated --year=2026 --month=7 --rmu=<RMU>Paso 7. Generar el estado de cuenta
php artisan file:statement-account --year=2026 --month=7 --rmu=<RMU>Paso 8. Revisar los importes
php artisan validate:rates --date=2026-07-01Aquí es donde se detecta un centro de carga al que le faltó ancho de banda o congestión: aparecerá con ese concepto vacío mientras otros lo tienen.
Resumen de la secuencia
| # | Día 1 — costo fijo | Día 4 — pass-through |
|---|---|---|
| 0 | Tarifas CFE registradas y calculadas | — |
| 1 | contracts:active | dispatch:calculations (rango extendido) |
| 2 | demand:monthly | demand:monthly |
| 3 | file:statement-account | verify:mda-deals |
| 4 | validate:rates | dispatch:by-operation-date --slug=distribution |
| 5 | dispatch:by-charge-center --slug=mda | |
| 6 | dispatch:by-charge-center --slug=mtr | |
| 7 | dispatch:by-charge-center --slug=bandwidth (centros específicos) | |
| 8 | dispatch:by-charge-center --slug=congestion (centros específicos) | |
| 9 | passthrough:estimated | |
| 10 | file:statement-account | |
| 11 | validate:rates |
Validación posterior
| Qué revisar | Cómo |
|---|---|
| Todos los centros tienen estado de cuenta | Comparar la salida de validate:rates contra el CSV de contracts:active. |
| Ningún total en cero ni concepto faltante | Revisar el desglose de validate:rates. |
| No quedaron trabajos fallidos | php artisan queue:failed |
| No hay centros sin contrato o con contrato duplicado | Notificaciones de error al administrador. |
| Las facturas llegaron al SIM | php artisan ecd:invoices_status --from=… --to=… |
Si algo falla
| Síntoma | Causa probable | Qué hacer |
|---|---|---|
| Un centro no generó estado de cuenta | Cero contratos activos, o más de uno, en el periodo. | Corregir la vigencia y reejecutar file:statement-account solo para ese RMU. |
| Total en cero en un contrato pass-through | Los cálculos se corrieron antes de que CENACE publicara, o los workers no procesaron. | passthrough:check-cenace-energy, recalcular y regenerar. |
| Falta un concepto en un solo centro | No se despachó ancho de banda o congestión para ese RMU. | Reejecutar ese dispatch:by-charge-center con su --rmu y regenerar el estado de cuenta. |
| Importes por debajo de lo esperado | Demanda incompleta al momento de demand:monthly. | demand:fix-hourly, reejecutar demand:monthly --retry=true y regenerar. |
| Los comandos de despacho terminaron sin efecto | Workers caídos. | Revisar Supervisor, levantar y queue:retry all. |
| La tarifa fija del cliente sale mal | El consumo del mes estaba incompleto al calcular la tarifa CFE. | Completar la demanda y recalcular la tarifa antes de rehacer el contrato. |
| Se facturó de más | — | No hay reversión automatizada. Escalar a desarrollo. |
Preguntas abiertas
Pendientes de confirmar. Hasta resolverlas, el documento sigue en estado Pendiente de revisión SME.
- El rango extendido. ¿Por qué
dispatch:calculations, distribución, MDA y MTR arrancan dos días antes del inicio del mes, mientras ancho de banda y congestión usan el mes calendario? ¿El desfase es siempre de dos días o depende del mes? - Los centros de carga con ancho de banda y congestión. ¿Qué criterio define la lista? ¿Está en el contrato, en el tipo de centro, o es una lista mantenida a mano? Es el punto más frágil del ciclo.
file:statement-accountpor RMU. ¿Se recorren todos los centros de carga uno por uno, o existe una lista acotada? ¿Se ha consideradorun:statement-account, que los procesa todos encolando un trabajo por cada uno?- Los comandos de tarifas CFE.
app:calculate-rate-commandeapp:import-rates-from-csvno existen en la rama principal decfe-rates-microservice. Ver la nota de discrepancia abajo. - Espera entre pasos. ¿Cuánto hay que esperar entre despachar los cálculos y generar el estado de cuenta? ¿Cómo se confirma hoy que las colas terminaron?
- Duración total. ¿Cuánto tarda cada día del ciclo? Sirve para saber cuándo preocuparse.
- Reliquidaciones. Cuando CENACE republica un mes ya facturado, ¿cuál es el procedimiento?
- Quién ejecuta y quién valida. ¿Alguien revisa la salida de
validate:ratesantes de que se emitan las facturas?
Discrepancia con el repositorio de tarifas CFE
Los comandos app:calculate-rate-command e app:import-rates-from-csv que se ejecutan en producción no aparecen en la rama principal de cfe-rates-microservice. Ese repositorio solo declara app:ask-bot-cfe-rates, y el cálculo de tarifas existe ahí como acción de API, no como comando de consola.
Puede tratarse de trabajo sin integrar a la rama principal, o de un despliegue que difiere del repositorio. Hasta aclararlo, esos dos comandos quedan documentados según la operación, no verificados contra código.
Riesgos y gaps
- La lista de centros de carga con ancho de banda y congestión no está en el sistema: vive en el procedimiento de quien ejecuta el cierre.
- No existe reversión de facturas emitidas por error.
- No está confirmado qué comandos son idempotentes al repetirse sobre un periodo ya procesado.
- No hay registro histórico de qué se ejecutó en cada cierre.
- No hay verificación automática de que la demanda esté completa antes de
demand:monthly, que es la precondición de todo lo demás. - El registro de tarifas CFE es manual y depende de un archivo externo.
Próxima revisión
Al resolver las preguntas abiertas.