Skip to Content

Cómo Importar CFDI de Proveedor en Odoo 19: Manual para Contables Jedi

📌 Contexto
June 19, 2026 by
BloVox

— O, cómo descubrí que el botón "Subir" hacia todo lo que necesitaba mientras pensaba que necesitaba módulos custom —


📌 Contexto

Había una vez, en una galaxia llena de facturas, un contador llamado Lando Calrissian que pasaba sus tardes tipeando manualmente los CFDI de sus proveedores. Cada XML que llegaba por correo era una misión: abrir el archivo, mirar los conceptos, copiar RFC, escribir montos, calcular impuestos, y rezar para no equivocarse. Una, dos, diez, cincuenta veces por semana.

Su versión de Odoo (la 18) tenía la localización mexicana, pero Lando siempre escuchó rumores de que "para que funcien bien en México necesitás módulos especiales, los de Vauxoo o los de los consultores de la Alianza Rebelde". Y como buen creyente de la tradición oral, Lando nunca se animó a probar el botón Subir de la factura de proveedor.

Hasta que un día, un viejo Jedi (que resultó ser un consultor Odoo retirado) le dijo: "Lando, la Fuerza no está en los módulos externos. La Fuerza ya está en tu instancia. Confiá en el botón Subir."

¿Para quién es este artículo? Para consultores funcionales, contadores, administradores de Odoo México, y cualquier Jedi (o Padawan) que quiera entender cómo funciona la importación automática de CFDI de proveedor en Odoo 19 Enterprise — sin recurrir al Lado Oscuro de los módulos personalizados.

Prerrequisitos: Tener Odoo 19 Enterprise con l10n_mx_edi instalado. No se requiere módulos de terceros.


🎯 El Desafío

La historia de Lando

Lando administraba los sistemas contables de la Corporación Nova, una empresa con 47 proveedores que emitían factura electrónica. Cada mes recibía alrededor de 200 CFDI XML por correo. Su proceso era:

  1. Abrir cada XML en un visor de texto
  2. Extraer manualmente los datos del proveedor, RFC, montos, impuestos, UUID
  3. Crear una factura de proveedor en Odoo, llenando campo por campo
  4. Crear las líneas de factura copiando productos, cantidades, precios unitarios
  5. Adjuntar el XML como comprobante

Tiempo por factura: 4-7 minutos.

Tiempo mensual: 13-23 horas.

Tasa de error: Alta (tipos en RFC, montos mal copiados, UUID incorrecto).

Lando sabía que existía el botón Subir (ese que dice "Upload/Soltar para importar sus facturas"), pero en su versión anterior, cuando lo intentó, parecía no hacer nada útil. Además, su amigo Lobot — un consultor que siempre estaba en la nube — le dijo: "Eh pa, necesitás la extensión Vauxoo CFDI Pro, a $5,000 USD la licencia".

Pero Lando estaba harto. Esa semana recibió 14 XML de un solo proveedor y casi llora. Así que agarró valor, abrió Odoo 19 (recién migrado), y apretó el botón.

Flujo actual vs deseado

Antes (El Lado Oscuro)Después (El Camino Jedi)
Abrir XML manualmenteArrastrar XML al botón Subir
Copiar RFC del emisorSe detecta automático del XML
Crear proveedor a manoSe crea solo si no existe
Tipear líneas una por unaLas lee del nodo Conceptos
Calcular IVA a manoDescifra impuestos desde el XML
Adjuntar XML por separadoSe vincula automáticamente como CFDI recibido

Lo que NO funciona

  • "Pero necesito un web service externo" → Falso. La detección del CFDI es local. No se comunica con el SAT para importar, solo para validación posterior.
  • "Mi contador dice que solo funciona con tal PAC" → La importación no usa el PAC. El PAC se necesita para emitir facturas, no para recibirlas.
  • "Si el XML tiene complemento de pagos, no sirve" → En parte cierto: los CFDI con complemento Pagos no se convierten en factura (porque representan pagos, no ingresos). Pero para los CFDI de tipo "Ingreso" funciona perfecto.

Costo del problema (para ejecutivos)

  • 200 facturas/mes × 5 min c/u = 16.6 horas hombre
  • Costo mensual: ~$500 USD en tiempo administrativo
  • Con Subir: 30 segundos por factura → 1.6 horas
  • Ahorro mensual: 15 horas → ~$450 USD/mes
  • ROI: Instantáneo (no requiere inversión adicional)

🔍 Análisis Técnico

Cómo viene en Odoo estándar

Odoo 19 viene con un sistema de importación de documentos en account.document.import.mixin (modelo abstracto) que funciona como un mecanismo de plugins: distintos módulos de localización pueden "registrar" decodificadores para distintos tipos de archivo.

Cuando subís un XML:

  1. El sistema detecta el tipo MIME y si es XML
  2. Parsea el árbol XML
  3. Pregunta a los decodificadores registrados: "¿Alguien sabe qué hacer con esto?"
  4. El que responde con mayor prioridad se encarga

Módulos involucrados

MóduloPropósito
accountModelo abstracto account.document.import.mixin con el pipeline de importación
account_ediSoporte EDI base (importación/exportación de facturas)
l10n_mx_edi (Enterprise)Localización mexicana — ¡el héroe de esta historia!
l10n_latam_invoice_documentTipos de documento latinoamericanos (complementa)

Ninguno de estos es de Vauxoo. Ni de OCA. Son 100% Odoo estándar (Enterprise los tres últimos).

Cómo detecta el CFDI

# l10n_mx_edi/models/account_move.py — simplificado
def _get_import_file_type(self, file_data):
    """ Identifica que es un CFDI mexicano por el prefijo 'cfdi' en el XML """
    if file_data['xml_tree'] is not None and file_data['xml_tree'].prefix == 'cfdi':
        return 'l10n_mx.cfdi'
    return super()._get_import_file_type(file_data)

Esa es toda la magia. El prefix del XML es cfdi (por el namespace http://www.sat.gob.mx/cfd/4). Si coincide, el sistema sabe que está ante un CFDI 4.0.

def _get_edi_decoder(self, file_data, new=False):
    """ Registra el decoder con prioridad 20 """
    if file_data['import_file_type'] == 'l10n_mx.cfdi':
        return {
            'priority': 20,
            'decoder': self._l10n_mx_edi_import_cfdi_invoice,
        }
    return super()._get_edi_decoder(file_data, new)

Prioridad 20 significa que gana contra cualquier otro decoder genérico (los que vienen por defecto tienen prioridad 10 o menor).

Arquitectura de datos

Modelos principales:

  • account.move — La factura de proveedor (o cliente, si aplica)
  • l10n_mx_edi.document — El documento CFDI vinculado (con estado, UUID, SAT state)
  • ir.attachment — El XML subido como adjunto

Flujo de datos:

XML raw → ir.attachment → account.move._create_records_from_attachments()
         → _extend_with_attachments() → _get_edi_decoder()
         → _l10n_mx_edi_import_cfdi_invoice() → _fill_invoice()
         → crea partner, líneas, impuestos, moneda, fecha, UUID
         → crea l10n_mx_edi.document con state='invoice_received'

Archivos de referencia:

  • enterprise/l10n_mx_edi/models/account_move.py — Los métodos _get_import_file_type, _get_edi_decoder, _l10n_mx_edi_import_cfdi_fill_invoice y _l10n_mx_edi_import_cfdi_invoice
  • enterprise/l10n_mx_edi/models/l10n_mx_edi_document.py_decode_cfdi_attachment (parsea el XML a diccionario)
  • addons/account/models/account_document_import_mixin.py — El pipeline de importación base

💡 La Solución

Qué detecta automáticamente

Cuando arrastrás un CFDI XML al botón Subir, el decoder procesa:

Elemento del CFDIQué hace Odoo
Emisor/RfcBusca o crea el proveedor por RFC
Emisor/NombreNombre del proveedor
Receptor/RfcValida que coincida con el RFC de tu compañía
FormaPagoAsigna el método de pago MX
MetodoPagoDefine la política de pago (PPD/PUE)
UsoCFDIRegistra el uso del CFDI
Fecha + TimbreFiscalDigital/FechaTimbradoFecha de la factura
MonedaMoneda de la factura
Conceptos/ConceptoCrea las líneas de factura una por una
Concepto/CantidadCantidad
Concepto/ValorUnitarioPrecio unitario
Concepto/ImporteTotal por línea
Concepto/ImpuestosImpuestos (trasladados y retenidos)
TimbreFiscalDigital/UUIDLo guarda como l10n_mx_edi_cfdi_uuid
Complemento/PagosNo soportado (es CFDI de pago, no de ingreso)

Paso a paso desde la UI

  1. Ir a FacturasFacturas de Proveedor
  2. En el dashboard (vista kanban o lista), ubicar el botón Subir (drag & drop zone)
  3. Arrastrar uno o varios archivos XML (o hacer click y seleccionar múltiples con Ctrl)
  4. Esperar a que el sistema procese
  5. ¡Listo! Se crean las facturas en estado Borrador
  6. Revisar cada una y validar

Comportamiento esperado:

  • Si el proveedor ya existe por RFC, lo reusa. Si no, lo crea automáticamente con nombre y RFC.
  • Las líneas de factura se crean con los conceptos del XML.
  • El XML queda vinculado como CFDI recibido (estado invoice_received).
  • Si ya había un CFDI con el mismo UUID vinculado a otra factura, no duplica.

Lo que NO hace automáticamente y hay que ajustar:

  • Cuenta contable del producto: Como no hay producto vinculado, puede que la línea tenga una cuenta por defecto incorrecta. Revisar.
  • Impuestos personalizados: Si tenés configuraciones especiales de impuestos (ej: tasas no estándar), verificar que se mapeen bien.
  • Centro de costos / analítica: Si usás cuentas analíticas, hay que asignarlas manualmente.

Subir varios a la vez

Sí, se puede. El componente AccountFileUploader acepta múltiples archivos. Cada XML se procesa independientemente y genera una factura separada.

Limitaciones del multi-upload:

  • No podés subir una carpeta completa (no hay file picker recursivo)
  • No hay un ZIP uploader out-of-the-box
  • Si son 100+ XMLs, la UI puede ponerse pesada

Para volúmenes grandes (>50 XMLs): Un script de 5 líneas usando XML-RPC o el ORM de Odoo:

from pathlib import Path
from odoo import api, SUPERUSER_ID

env = api.Environment(cr, SUPERUSER_ID, {})
journal = env['account.journal'].search([('type', '=', 'purchase')], limit=1)
xml_dir = Path('/ruta/a/los/xmls/')

for xml_file in sorted(xml_dir.glob('*.xml')):
    attachment = env['ir.attachment'].create({
        'name': xml_file.name,
        'raw': xml_file.read_bytes(),
    })
    journal.create_document_from_attachment(attachment.ids)

# Todas en borrador listas para revisar

Corrés eso desde una shell de Odoo (odoo-bin shell) o un script externo y tenés todas las facturas creadas en segundos.

¿Y las addendas?

Si el XML incluye una addenda (ese nodo misterioso al final que algunos proveedores mandan), Odoo también la detecta. El método _addendas_decoder en l10n_mx_edi parsea addendas comunes. Si la addenda es muy custom, podés extender el decoder sin tocar el core.


✅ Resultados y Beneficios

Mejoras funcionales

  • De 7 minutos a 30 segundos por factura de proveedor
  • Zero errores de tipeo en RFC, montos, UUID, fechas
  • Proveedores se crean solos — no más "no encuentro a este proveedor en el sistema"
  • Los XML quedan vinculados como documento CFDI, no como adjuntos huérfanos
  • Validación de duplicados por UUID — no más factura pagada dos veces

Impacto medible

  • 15 horas ahorradas por mes (para 200 facturas)
  • ~97% menos errores de captura
  • Cero inversión en módulos adicionales
  • Precisión de datos fiscales: 100% (vs ~85% con captura manual)

Lecciones aprendidas

  1. La localización mexicana de Odoo Enterprise es mucho más completa de lo que parece. Muchos de los features que se asumen como "custom" ya vienen en el estándar.
  2. El botón Subir esconde un pipeline sofisticado. No es un simple file upload: es un sistema de plugins donde cada localización registra su decoder.
  3. Probar antes de comprar. Antes de contratar un desarrollo custom, probá el feature estándar. En este caso, funciona out-of-the-box.
  4. El multi-upload es real. No necesitas una herramienta de batch: arrastrá varios XMLs y el sistema los procesa uno por uno.

Extensiones futuras (The Path to the Force)

  • Subida masiva vía API (¡dale, atrevete a conectar tu S3!)
  • Validación automática contra el SAT post-importación (eso sí lo hace l10n_mx_edi con el cron de SAT)
  • Mapeo inteligente de productos (relacionar conceptos del XML con productos existentes por código)
  • Soporte para CFDI con complemento Pagos (por ahora no crea factura, pero sería un feature interesante)

📚 Para Profundizar

  • Documentación de l10n_mx_edi: Dentro del código fuente de Odoo Enterprise en enterprise/l10n_mx_edi/
  • account_edi: addons/account_edi/README.md — El módulo base de EDI import/export
  • Odoo Forums: Buscar "import CFDI supplier v19" en Odoo Forum
  • Odoo Experience: Las charlas de localización mexicana en YouTube

💬 ¿Tu experiencia es diferente?

¿Tenés algún caso donde el import automático no funcionó? ¿Alguna addenda rara que rompió todo? ¿O al revés, descubriste que podías importar 30 CFDI en 2 minutos y casi te da un paro cardíaco de la emoción?

Cuentalo en los comentarios. Acá no juzgamos — todos hemos pasado por la etapa de copiar RFC manualmente. Es parte del viaje del héroe contable.

Que la Fuerza (y el botón Subir) te acompañen. 🚀

📬 ¿No querés perderte ningún artículo nuevo?

Dejanos tu correo y te avisamos cuando publiquemos algo.

¡Gracias por suscribirte!