# `CFDI`

Orquestador 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.

# `t`

```elixir
@type t() :: %CFDI{comprobante: Cfdi.Comprobante.t() | nil, config: map()}
```

# `cadena_original`

```elixir
@spec cadena_original(t()) :: String.t() | nil
```

Devuelve la cadena original guardada por `sellar/3`, o `nil` si todavía no
fue sellado.

Espejo del getter [`cadenaOriginal`](https://github.com/MisaelMa/node-cfdi/blob/main/packages/cfdi/xml/src/cfdi.ts#L196)
de Node.

# `certificar`

```elixir
@spec certificar(t(), Sat.Certificados.Credential.t()) ::
  {:ok, t()} | {:error, atom()}
```

Asocia certificado y número de certificado al comprobante.

# `generar_cadena_original`

```elixir
@spec generar_cadena_original(
  t(),
  keyword()
) :: {:ok, String.t()} | {:error, term()}
```

Genera la cadena original aplicando el XSLT al XML del CFDI.

Espejo de [`generarCadenaOriginal`](https://github.com/MisaelMa/node-cfdi/blob/main/packages/cfdi/xml/src/cfdi.ts#L115)
de Node. Toma el XSLT desde `config[:xslt]` (set vía `xslt/2`) o desde
`opts[:xslt]`.

# `generar_sello`

```elixir
@spec generar_sello(String.t(), Path.t(), String.t() | nil) ::
  {:ok, String.t()} | {:error, term()}
```

Firma la cadena original con la llave privada cargada desde archivo.

Espejo de [`generarSello`](https://github.com/MisaelMa/node-cfdi/blob/main/packages/cfdi/xml/src/cfdi.ts#L173)
de Node.

# `new`

```elixir
@spec new(Cfdi.Comprobante.t()) :: t()
```

# `sellar`

```elixir
@spec sellar(t()) :: {:ok, t()} | {:error, atom()}
```

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`.

# `sellar`

# `sellar`

```elixir
@spec sellar(t(), Path.t(), String.t() | nil) :: {:ok, t()} | {:error, term()}
```

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)`](https://github.com/MisaelMa/node-cfdi/blob/main/packages/cfdi/xml/src/cfdi.ts#L68)
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);
    }

# `to_json`

```elixir
@spec to_json(t()) :: String.t()
```

Serializa el CFDI a JSON.

Opciones:
  * `:ns` — `true` (default) incluye prefijos `cfdi:`; `false` los omite.
  * `:pretty` — `true` indenta el JSON; `false` (default) compacto.

# `to_json`

```elixir
@spec to_json(
  t(),
  keyword()
) :: String.t()
```

# `to_map`

```elixir
@spec to_map(t()) :: map()
```

Proyecta el CFDI a un mapa.

Opciones:
  * `:ns` — `true` (default) incluye el prefijo `cfdi:` en los nombres de
    elementos y mantiene los atributos como átomos (`:Rfc`, `:Nombre`)
    para distinguirlos de los elementos hijos; `false` los omite y
    uniforma TODAS las llaves al tipo elegido en `:keys`.

  * `:keys` — controla el tipo de las llaves cuando `ns: false`. Sin efecto
    con `ns: 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ía
        `String.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 cuando `ns: false`.
    Sin efecto con `ns: 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.

# `to_map`

```elixir
@spec to_map(
  t(),
  keyword()
) :: map()
```

# `to_xml`

```elixir
@spec to_xml(t()) :: String.t()
```

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` — `true` indenta el XML para lectura humana; `false` (default)
    produce XML compacto en una sola línea.

# `to_xml`

```elixir
@spec to_xml(
  t(),
  keyword()
) :: String.t()
```

# `xslt`

```elixir
@spec xslt(t(), String.t()) :: t()
```

Asocia la ruta del XSLT que generará la cadena original.

Espejo de `options.xslt` en el constructor de
[`CFDI`](https://github.com/MisaelMa/node-cfdi/blob/main/packages/cfdi/xml/src/cfdi.ts#L29) de Node.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
