CFDI (cfdi v4.0.5)
Copy MarkdownOrquestador principal del CFDI 4.0.
Recibe un %Cfdi.Comprobante{} previamente armado con sus elementos
(Emisor, Receptor, Conceptos, …), lo certifica, sella y serializa
a XML, mapa o JSON.
Summary
Functions
Devuelve la cadena original guardada por sellar/3, o nil si todavía no
fue sellado.
Asocia certificado y número de certificado al comprobante.
Invalida el sellado del documento: borra el Sello y el Timbre Fiscal
Digital, dejándolo listo para volver a sellar.
Igual que from_xml/2 pero leyendo desde disco.
Igual que from_file/2 pero devuelve el %CFDI{} directo o levanta.
Reconstruye un %CFDI{} completo a partir de un XML.
Igual que from_xml/2 pero devuelve el %CFDI{} directo o levanta.
Genera la cadena original aplicando el XSLT al XML del CFDI.
Firma la cadena original con la llave privada cargada desde archivo.
¿El comprobante está sellado (tiene Sello)?
Firma la cadena original y escribe el atributo Sello usando una credencial
ya cargada en config[:credential] y la cadena en config[:cadena].
Genera la cadena original, la firma con la llave privada del archivo dado,
guarda la cadena en config[:cadena_original] y escribe el atributo Sello.
¿El comprobante trae Timbre Fiscal Digital?
Serializa el CFDI a JSON.
Proyecta el CFDI a un mapa de datos.
Serializa el CFDI a XML respetando el orden de elementos que exige el Anexo 20 del SAT (indispensable para que los XSLT de cadena original procesen el documento correctamente).
Folio fiscal (UUID) del timbre, o nil si el comprobante no está timbrado.
Asocia la ruta del XSLT que generará la cadena original.
Types
@type t() :: %CFDI{comprobante: Cfdi.Comprobante.t() | nil, config: map()}
Functions
Devuelve la cadena original guardada por sellar/3, o nil si todavía no
fue sellado.
Espejo del getter cadenaOriginal
de Node.
@spec certificar(t(), Sat.Certificados.Credential.t()) :: {:ok, t()} | {:error, atom()}
Asocia certificado y número de certificado al comprobante.
Invalida el sellado del documento: borra el Sello y el Timbre Fiscal
Digital, dejándolo listo para volver a sellar.
Atajo de Cfdi.Comprobante.desellar/1 al nivel del %CFDI{}. Ver ahí
cuándo hace falta llamarlo a mano.
@spec from_file( Path.t(), keyword() ) :: {:ok, t()} | {:error, Cfdi.Decoder.reason() | {:file_error, File.posix()}}
Igual que from_xml/2 pero leyendo desde disco.
Agrega {:error, {:file_error, posix}} a los errores posibles.
Igual que from_file/2 pero devuelve el %CFDI{} directo o levanta.
@spec from_xml( String.t(), keyword() ) :: {:ok, t()} | {:error, Cfdi.Decoder.reason()}
Reconstruye un %CFDI{} completo a partir de un XML.
Camino inverso de to_xml/2: devuelve el comprobante con todo lo que traía
el documento —emisor, receptor, información global, conceptos con sus
impuestos y complementos de concepto, impuestos globales, CFDI relacionados
y complementos— como structs tipados, listo para inspeccionar o modificar.
{:ok, cfdi} = CFDI.from_xml(xml)
cfdi.comprobante."Total"
cfdi.comprobante |> Map.get(:"cfdi:Emisor") |> Map.get(:Rfc)Los complementos se resuelven por la URI de su namespace contra
Cfdi.Complementos.Registry. Uno desconocido no se descarta: cae al struct
genérico Cfdi.Complementos.Complemento, preservando su carga útil.
Cuidado: un CFDI timbrado es inmutable
from_xml/2 preserva Sello, Certificado, NoCertificado y el
TimbreFiscalDigital tal como venían — to_xml/2 los devuelve intactos.
Pero modificar un comprobante timbrado invalida su sello: la cadena
original deja de coincidir. Fiscalmente eso no es "editar" una factura, es
emitir otra: hay que volver a sellar (sellar/3) y re-timbrar, lo que
produce un folio fiscal nuevo. Usá timbrado?/1 antes de tocar nada.
Errores
{:error, {:malformed_xml, reason}}— el XML no parsea{:error, {:unexpected_root, name}}— la raíz no escfdi:Comprobante
Igual que from_xml/2 pero devuelve el %CFDI{} directo o levanta.
Genera la cadena original aplicando el XSLT al XML del CFDI.
Espejo de generarCadenaOriginal
de Node. Toma el XSLT desde config[:xslt] (set vía xslt/2) o desde
opts[:xslt].
Firma la cadena original con la llave privada cargada desde archivo.
Espejo de generarSello
de Node.
@spec new(Cfdi.Comprobante.t()) :: t()
¿El comprobante está sellado (tiene Sello)?
Sellado y timbrado son etapas distintas: primero el emisor sella con su CSD,
después el PAC timbra. Un CFDI recién sellado está sellado? pero todavía no
timbrado?.
Firma la cadena original y escribe el atributo Sello usando una credencial
ya cargada en config[:credential] y la cadena en config[:cadena].
Para el flujo de alto nivel desde archivos, usar sellar/3.
Genera la cadena original, la firma con la llave privada del archivo dado,
guarda la cadena en config[:cadena_original] y escribe el atributo Sello.
Espejo de sellar(keyfile, password)
de Node:
public async sellar(keyfile: string, password: string): Promise<void> {
const cadena = await this.generarCadenaOriginal();
const sello = await this.generarSello(cadena, keyfile, password);
this._cadenaOriginal = cadena;
this.setSello(sello);
}
¿El comprobante trae Timbre Fiscal Digital?
Un CFDI timbrado ya fue certificado por un PAC: su Sello y su UUID sólo
son válidos para el contenido exacto que se timbró. Modificar el contenido
lo invalida — los setters de contenido borran el Sello y el timbre. Ver
from_xml/2 y desellar/1.
Serializa el CFDI a JSON.
Opciones:
:ns—true(default) incluye prefijoscfdi:;falselos omite.:pretty—trueindenta el JSON;false(default) compacto.
Proyecta el CFDI a un mapa de datos.
Las declaraciones de namespace (xmlns:*) y el xsi:schemaLocation NO
aparecen: son plomería para reconstruir el XML, no datos del comprobante, y
viven sólo en el camino de to_xml/2. El mapa trae emisor, receptor,
conceptos, impuestos, sello, complementos… sin ruido de XML. Lo mismo aplica
al JSON de to_json/2, que se arma sobre este mapa.
Opciones:
:ns—true(default) incluye el prefijocfdi:en los nombres de elementos y mantiene los atributos como átomos (:Rfc,:Nombre) para distinguirlos de los elementos hijos;falselos omite y uniforma TODAS las llaves al tipo elegido en:keys.:keys— controla el tipo de las llaves cuandons: false. Sin efecto conns: true(la convención manda). Valores::string(default) — todas las llaves son strings. Siempre seguro.:existing— llaves se convierten a átomo si ya existe en la tabla global de átomos del VM; si no, quedan como string. Seguro ante XML/llaves arbitrarias.:atom— todas las llaves se convierten a átomos víaString.to_atom/1. Peligroso con XML externo: la tabla de átomos no tiene GC y puede agotarse (atom_table_full). Usar solo cuando se controla la fuente del XML.
:case— controla la capitalización de las llaves cuandons: false. Sin efecto conns: true. Valores::as_is(default) — preserva la capitalización original (PascalCase como en el XSD oficial:NoCertificado,RegimenFiscal).:camel— pasa la primera letra a minúscula para producir camelCase idiomático (noCertificado,regimenFiscal). El resto del nombre queda intacto (UsoCFDI→usoCFDI, preservando el acrónimo final). Útil al exportar a JSON o sistemas JS que esperan camelCase.
Convenciones de llaves cuando ns: true:
- strings (
"cfdi:Emisor") son elementos XML. - átomos (
:Rfc,:Nombre) son atributos XML.
Cuando ns: false, las llaves se uniforman para una vista plana — útil
para inspección, serialización a JSON o consumo desde sistemas que no
distinguen entre atributos y elementos.
Serializa el CFDI a XML respetando el orden de elementos que exige el Anexo 20 del SAT (indispensable para que los XSLT de cadena original procesen el documento correctamente).
Opciones:
:pretty—trueindenta el XML para lectura humana;false(default) produce XML compacto en una sola línea.
Folio fiscal (UUID) del timbre, o nil si el comprobante no está timbrado.
Asocia la ruta del XSLT que generará la cadena original.
Espejo de options.xslt en el constructor de
CFDI de Node.