README.md

# sat_catalogos

Catálogos oficiales del SAT para CFDI 4.0, generados automáticamente desde `catCFDI.xsd` y `catCFDI.xlsx`.

> **Estos archivos son auto-generados por [`sat_catalogos_codegen`](../catalogos_codegen). NO EDITAR a mano.**

## Catálogos disponibles

| SimpleType SAT       | Módulo Elixir                        | Variante        | Átomos | Extras |
|----------------------|--------------------------------------|-----------------|--------|--------|
| `c_FormaPago`        | `Sat.Catalogos.FormaPago`           | `:with_atoms`   | Sí     |        |
| `c_MetodoPago`       | `Sat.Catalogos.MetodoPago`          | `:with_atoms`   | Sí     |        |
| `c_TipoDeComprobante`| `Sat.Catalogos.TipoComprobante`     | `:with_atoms`   | Sí     |        |
| `c_Impuesto`         | `Sat.Catalogos.Impuesto`            | `:with_atoms`   | Sí     |        |
| `c_UsoCFDI`          | `Sat.Catalogos.UsoCFDI`             | `:with_atoms`   | Sí     |        |
| `c_Exportacion`      | `Sat.Catalogos.Exportacion`         | `:with_atoms`   | Sí     |        |
| `c_Moneda`           | `Sat.Catalogos.Moneda`              | `:with_atoms`   | Sí     |        |
| `c_RegimenFiscal`    | `Sat.Catalogos.RegimenFiscal`       | `:regimen_fiscal`| No    | `persona_fisica`, `persona_moral`, `inicio_vigencia`, `fin_vigencia` |
| `c_Periodicidad`     | `Sat.Catalogos.Periodicidad`        | `:strings_only` | No     |        |
| `c_Meses`            | `Sat.Catalogos.Meses`               | `:strings_only` | No     |        |
| `c_TipoRelacion`     | `Sat.Catalogos.TipoRelacion`        | `:strings_only` | No     |        |
| `c_ObjetoImp`        | `Sat.Catalogos.ObjetoImp`           | `:strings_only` | No     |        |
| `c_TipoFactor`       | `Sat.Catalogos.TipoFactor`          | `:strings_only` | No     |        |
| `c_Pais`             | `Sat.Catalogos.Pais`                | `:strings_only` | No     |        |
| `c_Estado`           | `Sat.Catalogos.Estado`              | `:strings_only` | No     |        |

## API

### Catálogos con átomos (`FormaPago`, `MetodoPago`, `TipoComprobante`, `Impuesto`, `UsoCFDI`, `Exportacion`, `Moneda`)

```elixir
# Todos los registros
list/0  :: [%{value: atom(), code: String.t(), label: String.t(), deprecated: boolean()}]

# ¿Existe este código SAT?
valid?/1 :: (String.t()) -> boolean()   # solo acepta string; atom/integer → false

# Átomo → código SAT string
value/1  :: (atom()) -> String.t() | nil

# Código SAT string → mapa completo
from_code/1 :: (String.t()) -> {:ok, %{value: atom(), code: String.t(), label: String.t(), deprecated: boolean()}} | :error
```

### Catálogos string-only (`RegimenFiscal`, `Periodicidad`, `Meses`, `TipoRelacion`, `ObjetoImp`, `TipoFactor`, `Pais`, `Estado`)

```elixir
# Todos los registros
list/0  :: [%{value: String.t(), label: String.t(), deprecated: boolean(), ...extras}]

# ¿Existe este código SAT?
valid?/1 :: (String.t()) -> boolean()   # no hay value/1 en estos módulos

# Código SAT string → mapa completo
from_code/1 :: (String.t()) -> {:ok, %{value: String.t(), label: String.t(), deprecated: boolean(), ...extras}} | :error
```

> Nota: los catálogos string-only **no exponen `value/1`**. El campo `:value` de cada entrada es directamente el código SAT como string.

## Ejemplos

```elixir
alias Sat.Catalogos.{FormaPago, MetodoPago, RegimenFiscal, Pais, TipoFactor}

# Átomo → código SAT
FormaPago.value(:efectivo)
# => "01"

# Código SAT → mapa completo
FormaPago.from_code("01")
# => {:ok, %{value: :efectivo, code: "01", label: "Efectivo", deprecated: false}}

# Validación por código string
FormaPago.valid?("01")   # => true
FormaPago.valid?("ZZ")   # => false
FormaPago.valid?(:efectivo)  # => false (solo acepta string)

# MetodoPago (atom-bearing)
MetodoPago.from_code("PUE")
# => {:ok, %{value: :pago_en_una_exhibicion, code: "PUE", label: "Pago en una sola exhibición", deprecated: false}}

# RegimenFiscal (string-only con extras)
RegimenFiscal.from_code("601")
# => {:ok, %{value: "601", label: "General de Ley Personas Morales",
#            persona_fisica: false, persona_moral: true,
#            inicio_vigencia: ~D[2022-01-01], fin_vigencia: nil, deprecated: false}}

RegimenFiscal.valid?("626")  # RESICO => true
RegimenFiscal.valid?(601)    # integer => false

# TipoFactor (string-only; el código ES el valor)
TipoFactor.valid?("Tasa")   # => true
TipoFactor.from_code("Tasa")
# => {:ok, %{value: "Tasa", label: "Tasa", deprecated: false}}

# Pais
Pais.valid?("MEX")  # => true
Pais.from_code("MEX")
# => {:ok, %{value: "MEX", label: "México", deprecated: false}}
```

## Campos en entradas deprecadas

Las entradas deprecadas siguen siendo accesibles y retornan `deprecated: true`. Esto permite validar CFDIs históricos. Para filtrar solo activos:

```elixir
FormaPago.list() |> Enum.reject(& &1.deprecated)
```