Skip to content

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íaContratosTipo de productoPor qué esa fecha
1Costo fijo por centro de cargafixed, fixed-with-energy-lossEl precio está pactado. Basta con la demanda del mes cerrado.
4Pass-through por centro de cargapass-throughEl 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:

MarcadorSignificadoEn el ejemplo
<RMU>Identificador del centro de carga
<AAAA> / <MM>Año y mes del periodo que se cierra2026 / 7
Primer día del mes2026-07-01
Último día del mes2026-07-31
Inicio extendidoDos días antes del mes2026-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:

bash
php artisan app:import-rates-from-csv --filename=cfe-rates-<AAAA>-<MM>.csv

2. Calcular la tarifa del centro de carga

bash
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

bash
php artisan contracts:active --date=2026-07-01

Exporta 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

bash
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:

bash
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

bash
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

bash
php artisan validate:rates --date=2026-07-01

Imprime, 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:

bash
php artisan ecd:create_invoice --csv

Dí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

bash
php artisan dispatch:calculations --operation_date=2026-06-29 --ending_at=2026-07-31

Despacha 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

bash
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

bash
php artisan verify:mda-deals --year=2026 --month=7

Compara 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:

bash
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-31

Volumen

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:

bash
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

bash
php artisan passthrough:estimated --year=2026 --month=7 --rmu=<RMU>

Paso 7. Generar el estado de cuenta

bash
php artisan file:statement-account --year=2026 --month=7 --rmu=<RMU>

Paso 8. Revisar los importes

bash
php artisan validate:rates --date=2026-07-01

Aquí 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 fijoDía 4 — pass-through
0Tarifas CFE registradas y calculadas
1contracts:activedispatch:calculations (rango extendido)
2demand:monthlydemand:monthly
3file:statement-accountverify:mda-deals
4validate:ratesdispatch:by-operation-date --slug=distribution
5dispatch:by-charge-center --slug=mda
6dispatch:by-charge-center --slug=mtr
7dispatch:by-charge-center --slug=bandwidth (centros específicos)
8dispatch:by-charge-center --slug=congestion (centros específicos)
9passthrough:estimated
10file:statement-account
11validate:rates

Validación posterior

Qué revisarCómo
Todos los centros tienen estado de cuentaComparar la salida de validate:rates contra el CSV de contracts:active.
Ningún total en cero ni concepto faltanteRevisar el desglose de validate:rates.
No quedaron trabajos fallidosphp artisan queue:failed
No hay centros sin contrato o con contrato duplicadoNotificaciones de error al administrador.
Las facturas llegaron al SIMphp artisan ecd:invoices_status --from=… --to=…

Si algo falla

SíntomaCausa probableQué hacer
Un centro no generó estado de cuentaCero 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-throughLos 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 centroNo 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 esperadoDemanda incompleta al momento de demand:monthly.demand:fix-hourly, reejecutar demand:monthly --retry=true y regenerar.
Los comandos de despacho terminaron sin efectoWorkers caídos.Revisar Supervisor, levantar y queue:retry all.
La tarifa fija del cliente sale malEl 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ásNo hay reversión automatizada. Escalar a desarrollo.

Preguntas abiertas

Pendientes de confirmar. Hasta resolverlas, el documento sigue en estado Pendiente de revisión SME.

  1. 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?
  2. 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.
  3. file:statement-account por RMU. ¿Se recorren todos los centros de carga uno por uno, o existe una lista acotada? ¿Se ha considerado run:statement-account, que los procesa todos encolando un trabajo por cada uno?
  4. Los comandos de tarifas CFE. app:calculate-rate-command e app:import-rates-from-csv no existen en la rama principal de cfe-rates-microservice. Ver la nota de discrepancia abajo.
  5. 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?
  6. Duración total. ¿Cuánto tarda cada día del ciclo? Sirve para saber cuándo preocuparse.
  7. Reliquidaciones. Cuando CENACE republica un mes ya facturado, ¿cuál es el procedimiento?
  8. Quién ejecuta y quién valida. ¿Alguien revisa la salida de validate:rates antes 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.