← Volver al blog

Migración l10n_cl de Odoo 16 a 18: lo que nadie documenta

Por qué esta migración es diferente a otras

Migrar un Odoo "limpio" de 16 a 18 es relativamente predecible: corres el script de OpenUpgrade, arreglas tus módulos custom y listo. Pero cuando hay localización chilena con facturación electrónica, la historia cambia. No estás migrando solo datos: estás migrando documentos tributarios que ya fueron timbrados y aceptados por el SII, con folios consumidos y firmas digitales que no puedes regenerar.

El salto 16 → 18 atraviesa dos versiones de refactor importante en account.move y en la capa l10n_latam. Las guías genéricas se detienen en "migra el módulo de contabilidad"; la parte chilena —los CAF, el folio, el estado de aceptación del DTE— es justo donde se rompen las migraciones en producción. Esto es lo que aprendí haciéndolas.

Antes de empezar: nunca migres directo sobre la base de producción. Trabaja sobre una copia, valida el resultado contra el SII en ambiente de certificación, y recién entonces planifica la ventana real.

Cambios en el modelo account.move entre v16 y v18

Tres cambios concentran casi todos los problemas de una migración con l10n_cl:

1. El número del documento ya no vive donde crees

En LATAM, el name de la factura no es la fuente de verdad: el número fiscal real está en l10n_latam_document_number y el tipo en l10n_latam_document_type_id. Entre v16 y v18 cambió la forma en que la secuencia y el name se sincronizan al postear. Si tu módulo custom escribía name a mano, vas a ver duplicados o folios corridos. Verifica siempre el par correcto:

# Lo que importa para el SII no es move.name, es:
move.l10n_latam_document_number   # el folio
move.l10n_latam_document_type_id  # 33 factura, 34 exenta, 61 NC...

2. payment_state y posted_before

El campo payment_state es computed-stored y su algoritmo cambió. Tras migrar, conviene forzar el recálculo en lote en vez de confiar en el valor heredado. Y ojo con posted_before: marca si un asiento ya fue publicado alguna vez. Si se pierde en la migración, Odoo puede intentar re-asignar secuencia a documentos ya emitidos.

# post-migrate: recálculo controlado, por lotes
def migrate(cr, version):
    env = api.Environment(cr, SUPERUSER_ID, {})
    moves = env['account.move'].search([
        ('move_type', '!=', 'entry'),
        ('state', '=', 'posted'),
    ])
    moves._compute_payment_state()
    cr.commit()

3. Campos l10n_cl movidos o renombrados

Algunos campos de la localización cambiaron de módulo (de l10n_cl a l10n_cl_edi) o de nombre. No asumas que un account.move.l10n_cl_* de v16 existe igual en v18 — revisa el ir.model.fields antes de tocar nada:

-- ¿Qué campos l10n_cl sobreviven en la base migrada?
SELECT name, model FROM ir_model_fields
WHERE name LIKE 'l10n_cl%'
  AND model IN ('account.move', 'account.journal')
ORDER BY model, name;

El problema con l10n_cl.par_cfa y cómo resolverlo

Aquí es donde casi todos tropiezan. Los CAF (Código de Autorización de Folios) son los archivos que el SII te entrega para timbrar: definen el rango de folios autorizado por tipo de documento y traen la llave privada con la que se firma cada DTE. En Odoo viven en el modelo l10n_cl.dte.caf (lo que en configuraciones heredadas a veces verás referenciado como l10n_cl.par_cfa).

El problema: el contenido del CAF está almacenado como un campo binario/cifrado ligado al certificado digital de la compañía. Cuando migras, dos cosas pueden romperse:

  • El vínculo con el certificado (l10n_cl.certificate) se pierde si los IDs se reordenan.
  • El contador de folios usados queda desincronizado: Odoo cree que tienes folios libres que en realidad ya emitiste.

La regla de oro: no regeneres los CAF. Reutiliza los mismos archivos y reconcilia el contador contra los folios realmente consumidos en los documentos posteados:

# post-migrate: reconciliar el folio máximo usado por cada CAF
for caf in env['l10n_cl.dte.caf'].search([('status', '=', 'in_use')]):
    used = env['account.move'].search([
        ('l10n_latam_document_type_id', '=', caf.l10n_latam_document_type_id.id),
        ('l10n_latam_document_number', '!=', False),
    ])
    folios = [int(m.l10n_latam_document_number) for m in used
              if caf.start_nb <= int(m.l10n_latam_document_number) <= caf.final_nb]
    if folios:
        _logger.info("CAF %s: folio máx usado = %s", caf.id, max(folios))
        # Ajusta la secuencia del diario para que el próximo folio
        # sea max(folios) + 1, nunca uno ya emitido.
Si te equivocas aquí emites un DTE con un folio repetido, el SII lo rechaza, y tienes que anular con nota de crédito. Es recuperable, pero es exactamente el tipo de error que prefieres no explicarle al contador.

Validación de DTE después de la migración

Migrar la base no garantiza que la facturación electrónica siga funcionando. Antes de emitir el primer documento real en producción, valida en este orden:

  1. El certificado digital sigue cargado y vigente (revisa la fecha de expiración, no solo que exista).
  2. Los CAF en uso tienen folios disponibles y apuntan al diario correcto.
  3. La conexión al SII responde en certificación antes que en producción.
  4. El estado de los DTE históricos (l10n_cl_dte_status) se preservó: aceptado debe seguir siendo aceptado.
# ¿Quedó algún DTE histórico en estado inconsistente?
SELECT l10n_cl_dte_status, count(*)
FROM account_move
WHERE move_type IN ('out_invoice', 'out_refund')
  AND state = 'posted'
GROUP BY l10n_cl_dte_status;

Emite un primer documento de prueba (idealmente en certificación) y confirma el ciclo completo: generación del XML, firma, envío al SII y recepción del acuse. Si ese flujo cierra, la localización sobrevivió la migración.

Checklist final antes de producción

  • Migración corrida sobre copia, nunca sobre producción directa.
  • Módulos custom migrados y con sus tests pasando en 18.
  • payment_state y posted_before recalculados por lote.
  • Campos l10n_cl_* verificados contra ir.model.fields.
  • CAF reutilizados (no regenerados) y contador de folios reconciliado.
  • Certificado digital vigente y vinculado a la compañía.
  • DTE de prueba emitido y aceptado en certificación.
  • Respaldo completo + plan de rollback con ventana definida.

Hecho con método, una migración l10n_cl de 16 a 18 es perfectamente segura. El secreto no está en el script de OpenUpgrade —ese es el 70% fácil—, sino en tratar cada folio y cada DTE como lo que son: documentos tributarios que no admiten "lo arreglo después".

¿Necesitas ayuda con tu migración?

Análisis de gaps, migración de módulos custom y validación de l10n_cl, de punta a punta.

Hablemos de tu proyecto →