> ## Documentation Index
> Fetch the complete documentation index at: https://docs.filexpress.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Compras y gastos (v2)

> Registrar compras en moneda extranjera con impuestos por id y consultar su referencia de moneda

**Permiso requerido:** `expenses`

La compra v2 es la misma de [v1](/docs/api/facturacion/compras.md) (mismos campos, centros de costo y tipos de documento) más **moneda** (`currency_code`, `exchange_rate`) y **`tax_ids` obligatorio por línea**. Sus errores llevan `code`; ver [API v2: qué cambia](/docs/api/v2/introduccion.md#errores-y-códigos).

> [!WARNING]
>
> A diferencia de las ventas, el guardado de compras **no recalcula los impuestos**: FileXpress guarda lo que envía tu sistema (convertido a moneda base si trae moneda) y tú eres quien los calcula. Solo se valida que cada id de `tax_ids` exista. Un IVA o un total a pagar que no cuadre se guarda tal cual, por lo que debes enviar los totales consistentes.

> [!NOTE]
>
> Los campos que no se describen aquí (`observation`, `is_credit`, `credit_days`, `number_control`, `generation_code`, `stamp`, centros de costo, etc.) son los de [Registrar compra en v1](/docs/api/facturacion/compras.md#registrar-compra). Los tipos de línea siguen siendo `goods` (bien) y `services` (servicio).

## Registrar compra

```http theme={null}
POST /expenses
```

Ejemplo: compra (CCF) de un negocio con base USD, **en quetzales (GTQ)**, con IVA, COTRANS, FOVIAL, retención de IVA y renta. La tasa vigente de GTQ es `0.128`.

```json theme={null}
{
  "branch_id": "9d1c5a3e-4b7f-4e2a-9c1d-2f6b8a0e7c11",
  "provider_id": "5e6f7a8b-3c4d-4e1f-9a2b-1c0d9e8f7a6b",
  "document_type": "ccf",
  "date": "2026-10-21T10:00:00",
  "is_credit": true,
  "credit_days": 30,
  "number_control": "DTE-03-M001P001-000000000000123",
  "generation_code": "A1B2C3D4-5E6F-4A7B-8C9D-0E1F2A3B4C5D",
  "currency_code": "GTQ",
  "exchange_rate": 0.128,
  "products": [
    {
      "sku": "COM-001",
      "name": "Diésel",
      "type": "goods",
      "quantity": 5,
      "price_before_taxes": 100.00,
      "expense_taxed": 500.00,
      "expense_exent": 0,
      "expense_not_suject": 0,
      "subtotal": 500.00,
      "tax_ids": ["<id-del-IVA>", "<id-del-COTRANS>"]
    },
    {
      "sku": "COM-002",
      "name": "Gasolina",
      "type": "goods",
      "quantity": 5,
      "price_before_taxes": 50.00,
      "expense_taxed": 250.00,
      "expense_exent": 0,
      "expense_not_suject": 0,
      "subtotal": 250.00,
      "tax_ids": ["<id-del-IVA>", "<id-del-FOVIAL>"]
    },
    {
      "sku": "SRV-010",
      "name": "Servicio exento",
      "type": "services",
      "quantity": 1,
      "price_before_taxes": 125.00,
      "expense_taxed": 0,
      "expense_exent": 125.00,
      "expense_not_suject": 0,
      "subtotal": 125.00,
      "tax_ids": []
    },
    {
      "sku": "SRV-011",
      "name": "Servicio no sujeto",
      "type": "services",
      "quantity": 1,
      "price_before_taxes": 62.50,
      "expense_taxed": 0,
      "expense_exent": 0,
      "expense_not_suject": 62.50,
      "subtotal": 62.50,
      "tax_ids": []
    }
  ],
  "totals": {
    "netTotals": 937.50,
    "charge_1": 15.63,
    "charge_2": 7.81,
    "expenseTotalTax": 750.00,
    "expenseTotalExent": 125.00,
    "expenseTotalNotSuject": 62.50,
    "totalTax": 97.50,
    "totalRetein": 7.50,
    "totalRent": 10.00,
    "expenseTotalToPayment": 1040.94
  }
}
```

> [!WARNING]
>
> **Todos los montos van en la moneda de `currency_code`.** El servidor convierte cada monto a la moneda base con la tasa del documento. En el ejemplo, 750.00 GTQ gravados son 96.00 USD; el IVA de 97.50 GTQ, 12.48 USD; COTRANS (`charge_1`), 2.00 USD; FOVIAL (`charge_2`), 1.00 USD; la retención de 7.50 GTQ, 0.96 USD; y la renta de 10.00 GTQ, 1.28 USD. El total a pagar de 1,040.94 GTQ se guarda como 133.24 USD.

El total a pagar es `netTotals + totalTax + charge_1 + charge_2 − totalRetein − totalRent`: 937.50 + 97.50 + 15.63 + 7.81 − 7.50 − 10.00 = 1,040.94.

### Campos nuevos o distintos de v1

| Campo | Tipo | Requerido | Descripción |
| - | - | - | - |
| `currency_code` | string | No | Código de 3 letras de la moneda del documento. Debe estar activa para el negocio; ver [Monedas y tasas](/docs/api/v2/monedas-y-tasas.md). Si se omite o es la moneda base, la compra es en moneda base. |
| `exchange_rate` | number | No | Unidades de moneda **base** por 1 unidad de `currency_code`. Mayor que cero. Si se omite, se usa la tasa vigente a la `date` de la compra. |
| `products[].tax_ids` | array de uuid | **Sí** | IDs de los impuestos de la línea (deben existir). La clave debe estar presente en cada línea; puede ser `[]`. Los IDs se obtienen con [`GET /taxes/branch/{branch_id}`](/docs/api/facturacion/catalogos) (v1). |
| `totals.expenseTotalExent` | number | No | Total exento del documento. Si se omite se guarda como 0. |
| `totals.expenseTotalNotSuject` | number | No | Total no sujeto del documento. Si se omite se guarda como 0. |

`totals.expenseTotalTax` es la base gravada, `totalTax` el IVA y `charge_1` / `charge_2` los cargos especiales (en El Salvador, COTRANS y FOVIAL). Si la línea lleva esos impuestos fijos en `tax_ids`, los montos quedan registrados por impuesto. Ver [totals en v1](/docs/api/facturacion/compras.md#totals-todos-requeridos).

### Retención y renta

`totalRetein` (IVA retenido) y `totalRent` (renta retenida) **restan** del total a pagar. Ejemplo en USD con IVA 26.00, retención 2.00 y renta 5.00 sobre 200.00: `expenseTotalToPayment` = 200.00 + 26.00 − 2.00 − 5.00 = 219.00.

```json theme={null}
{
  "totals": {
    "netTotals": 200.00,
    "charge_1": 0,
    "charge_2": 0,
    "expenseTotalTax": 200.00,
    "totalTax": 26.00,
    "totalRetein": 2.00,
    "totalRent": 5.00,
    "expenseTotalToPayment": 219.00
  }
}
```

### Respuesta `200`

```json theme={null}
{
  "status": true,
  "message": "Expense created successfully",
  "data": {
    "expense_id": "4a9d1e7c-8b2f-4c3a-9e6d-5f0b1a2c3d4e",
    "generation_code": "A1B2C3D4-5E6F-4A7B-8C9D-0E1F2A3B4C5D",
    "control_number": "DTE-03-M001P001-000000000000123",
    "status": "processed",
    "issue_time": "2026-10-21 10:00:00",
    "total": 1040.94,
    "currency_code": "GTQ",
    "exchange_rate": "0.12800000",
    "foreign_total": "1040.94",
    "base_currency_code": "USD"
  }
}
```

| Campo | Descripción |
| - | - |
| `total` | El `expenseTotalToPayment` que enviaste, tal cual (en la moneda del documento). |
| `currency_code` | Moneda del documento (la moneda base si no enviaste moneda). |
| `exchange_rate` | Tasa aplicada, texto con 8 decimales. `"1.00000000"` en moneda base. |
| `foreign_total` | Total a pagar en la moneda del documento, texto con 2 decimales. `null` si la compra es en moneda base. |
| `base_currency_code` | Moneda base del negocio. |

Para una compra en moneda base: `currency_code` es la moneda base, `exchange_rate` `"1.00000000"` y `foreign_total` `null`.

### Errores

Se validan antes de guardar; ningún error crea la compra.

| `code` | HTTP | Causa |
| - | - | - |
| `currency_not_enabled` | `422` | Multi-moneda no habilitada, moneda inexistente o no activa para el negocio. |
| `exchange_rate_not_found` | `422` | Sin `exchange_rate` y sin tasa vigente a la fecha ni anterior. |
| `exchange_rate_invalid` | `422` | `exchange_rate` no numérica o menor o igual que cero. |
| `base_currency_not_legal` | `422` | Documento electrónico (`fse`) en moneda extranjera y la moneda base del negocio no es la moneda legal del país. |
| `validation_error` | `422` | Falta `products.*.tax_ids`, un id de `tax_ids` no existe (`products.0.tax_ids.0`) u otro campo inválido. |

```json theme={null}
{
  "status": false,
  "code": "validation_error",
  "message": "Validation errors",
  "errors": { "products.0.tax_ids": ["The products.0.tax_ids field must be present."] }
}
```

## Consultar compra

```http theme={null}
GET /expenses/{expense_id}
```

Devuelve lo mismo que [v1](/docs/api/facturacion/compras.md#consultar-compra) más la referencia de moneda. Los montos de `totals` y `products` están **en moneda base**; el total en la moneda del documento está en `foreign_total`.

```json theme={null}
{
  "status": true,
  "data": {
    "expense_id": "4a9d1e7c-8b2f-4c3a-9e6d-5f0b1a2c3d4e",
    "generation_code": "A1B2C3D4-5E6F-4A7B-8C9D-0E1F2A3B4C5D",
    "control_number": "DTE-03-M001P001-000000000000123",
    "document_type": "ccf",
    "status": "processed",
    "issue_time": "2026-10-21 10:00:00",
    "payment_status": "pending",
    "is_credit": true,
    "provider": { "id": "5e6f7a8b-3c4d-4e1f-9a2b-1c0d9e8f7a6b", "legal_name": "Distribuidora XYZ S.A. de C.V.", "provider_name": "Distribuidora XYZ" },
    "totals": { "expense_total": 120.00, "expense_to_payment": 133.24 },
    "products": [
      { "sku": "COM-001", "name": "Diésel", "quantity": 5, "individual_price": 12.80, "subtotal": 64.00 }
    ],
    "currency_code": "GTQ",
    "exchange_rate": "0.12800000",
    "foreign_total": "1040.94",
    "base_currency_code": "USD"
  }
}
```

Una compra de otro negocio o inexistente responde `404` con `{ "status": false, "message": "..." }`.

> [!TIP]
>
> `GET /payables` (v1) también expone la referencia de moneda de cada documento por pagar. Ver [Cuentas por pagar](/docs/api/cuentas-por-pagar.md#documentos-pendientes).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.