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

# Presupuestos

> Presupuestos por centro de costo y reporte presupuesto vs real desde un sistema externo

| Permiso | Endpoints |
| - | - |
| `accounting.budgets` | `GET /accounting/budgets`, `GET /accounting/budgets/{id}`, `GET /accounting/reports/budget-vs-actual` |
| `accounting.budgets.write` | `POST /accounting/budgets`, `PUT /accounting/budgets/{id}`, `PUT /accounting/budgets/{id}/lines`, `POST /accounting/budgets/{id}/status`, `DELETE /accounting/budgets/{id}` |

<Note>
  Son permisos **explícitos**: deben marcarse uno por uno en **Integraciones externas**; las integraciones existentes no los reciben automáticamente.
</Note>

Un presupuesto tiene montos mensuales por [centro de costo](/docs/api/contabilidad/centros-de-costo) hoja y cuenta de resultado (cuentas con política de centro `optional` o `required`; consulta el [catálogo](/docs/api/contabilidad/catalogo)). Puede haber varios por año, pero solo uno `approved`. Ver [Presupuestos](/docs/contabilidad/presupuestos) en el manual.

## Objeto presupuesto

```json theme={null}
{
  "id": "b1f0...",
  "business_id": "uuid",
  "name": "Presupuesto 2026",
  "fiscal_year": 2026,
  "status": "draft",
  "notes": null,
  "lines_count": 24,
  "total_amount": 54000.00,
  "created_at": "2026-10-08T15:00:00.000000Z",
  "updated_at": "2026-10-08T15:00:00.000000Z",
  "lines": [
    {
      "id": "uuid",
      "cost_center_id": "c3a1...",
      "account_id": "a9d2...",
      "month": 1,
      "amount": 100.00,
      "cost_center": { "id": "c3a1...", "code": "0101", "name": "Ventas San Salvador" },
      "account": { "id": "a9d2...", "code": "6102", "name": "Papelería" }
    }
  ]
}
```

| Campo | Descripción |
| - | - |
| `status` | `draft` (borrador, editable), `approved` (aprobado: uno por año) o `closed` (cerrado). |
| `lines_count`, `total_amount` | Cantidad de líneas y suma de montos. |
| `lines` | Solo en la consulta individual y en las respuestas de escritura. Una línea por centro, cuenta y mes (`1` a `12`). Los meses en 0 no se guardan. |

## Listar presupuestos

```http theme={null}
GET /accounting/budgets?fiscal_year=2026&status=approved
```

**Permiso requerido:** `accounting.budgets`

| Parámetro | Requerido | Descripción |
| - | - | - |
| `fiscal_year` | No | Filtra por año. |
| `status` | No | `draft`, `approved` o `closed`. |

Devuelve en `data` la lista de presupuestos (sin `lines`), del año más reciente al más antiguo.

## Consultar presupuesto

```http theme={null}
GET /accounting/budgets/{id}
```

**Permiso requerido:** `accounting.budgets`

Responde `200` con el [presupuesto](#objeto-presupuesto) y sus `lines`, o `404` `Budget not found`.

## Crear presupuesto

```http theme={null}
POST /accounting/budgets
```

**Permiso requerido:** `accounting.budgets.write`

```json theme={null}
{
  "name": "Presupuesto 2026",
  "fiscal_year": 2026,
  "notes": "Versión inicial",
  "lines": [
    { "cost_center_id": "c3a1...", "account_id": "a9d2...", "month": 1, "amount": 100 },
    { "cost_center_id": "c3a1...", "account_id": "a9d2...", "month": 2, "amount": 100 }
  ]
}
```

| Campo | Requerido | Descripción |
| - | - | - |
| `name` | Sí | Nombre. |
| `fiscal_year` | Sí | Año fiscal. |
| `notes` | No | Notas. |
| `lines` | No | Líneas iniciales (ver [Líneas](#guardar-líneas)). |

El presupuesto nace en `draft`. **Respuesta `201`**: el [presupuesto](#objeto-presupuesto) con sus líneas.

## Editar presupuesto

```http theme={null}
PUT /accounting/budgets/{id}
```

**Permiso requerido:** `accounting.budgets.write`

```json theme={null}
{ "name": "Presupuesto 2026 v2", "notes": "Ajuste de gastos" }
```

Acepta `name`, `fiscal_year` y `notes` (opcionales). Solo en `draft`.

## Guardar líneas

```http theme={null}
PUT /accounting/budgets/{id}/lines
```

**Permiso requerido:** `accounting.budgets.write`

```json theme={null}
{
  "replace": false,
  "lines": [
    { "cost_center_id": "c3a1...", "account_id": "a9d2...", "month": 3, "amount": 150 },
    { "cost_center_id": "c3a1...", "account_id": "a9d2...", "month": 4, "amount": 0 }
  ]
}
```

| Campo | Requerido | Descripción |
| - | - | - |
| `lines[].cost_center_id` | Sí | Centro **hoja** y activo de la empresa. |
| `lines[].account_id` | Sí | Cuenta de la empresa con política de centro `optional` o `required` (cuenta de resultado). |
| `lines[].month` | Sí | Mes, de `1` a `12`. |
| `lines[].amount` | Sí | Monto mayor o igual a 0. |
| `replace` | No | `false` (por defecto): actualiza solo las celdas enviadas (por centro, cuenta y mes) y un `amount: 0` borra esa celda. `true`: reemplaza todas las líneas del presupuesto por las enviadas. |

Solo en `draft`. Responde `200` con el presupuesto y sus líneas.

## Cambiar estado

```http theme={null}
POST /accounting/budgets/{id}/status
```

**Permiso requerido:** `accounting.budgets.write`

```json theme={null}
{ "status": "approved" }
```

`status`: `draft`, `approved` o `closed`. Solo puede haber un presupuesto `approved` por año: si ya existe otro, responde `422` `Ya existe un presupuesto aprobado para 2026.`.

## Eliminar presupuesto

```http theme={null}
DELETE /accounting/budgets/{id}
```

**Permiso requerido:** `accounting.budgets.write`

Solo en `draft`. Responde `{"status": true, "data": {"deleted": true}}`.

## Reporte presupuesto vs real

```http theme={null}
GET /accounting/reports/budget-vs-actual?fiscal_year=2026&month_from=1&month_to=10
```

**Permiso requerido:** `accounting.budgets`

| Parámetro | Requerido | Descripción |
| - | - | - |
| `fiscal_year` | No | Año. Por defecto, el actual. |
| `budget_id` | No | Presupuesto a comparar. Sin él se usa el `approved` del año (`422` si no hay). |
| `month_from`, `month_to` | No | Rango de meses (`1` a `12`). |
| `cost_center_ids[]` | No | Centros a incluir. Sin él, todo el árbol. |
| `include_children` | No | Por defecto `true`: incluye los subcentros de los centros elegidos. |
| `depth` | No | Agrupa las cuentas en su cuenta padre de ese nivel del catálogo. Sin él, cuenta de detalle. |
| `branch_id` | No | Limita lo real a una sucursal. |
| `expense_threshold` | No | Umbral de ejecución para costos y gastos (por defecto `100`). |
| `income_threshold` | No | Umbral de ejecución para ingresos (por defecto `100`). |
| `format` | No | `json` (por defecto), `pdf` o `excel`: descarga `presupuesto-vs-real-2026.pdf` o `.xlsx`. |

```json theme={null}
{
  "status": true,
  "data": {
    "title": "Presupuesto vs real por centro de costo",
    "budget": { "id": "b1f0...", "name": "Presupuesto 2026", "fiscal_year": 2026, "status": "approved" },
    "month_from": 1,
    "month_to": 10,
    "expense_threshold": 100,
    "income_threshold": 100,
    "centers": [
      {
        "id": "c3a0...", "code": "01", "name": "Ventas", "level": 1, "parent_id": null, "is_leaf": false,
        "rows": [
          { "account_code": "6102", "account_name": "Papelería", "group": "expenses", "budget": 100, "actual": 120, "variance": 20, "execution_pct": 120, "status": "exceeded" }
        ],
        "totals": {
          "income": { "budget": 0, "actual": 0, "variance": 0, "execution_pct": null },
          "costs": { "budget": 0, "actual": 0, "variance": 0, "execution_pct": null },
          "expenses": { "budget": 100, "actual": 120, "variance": 20, "execution_pct": 120 }
        }
      }
    ],
    "totals": {
      "income": { "budget": 0, "actual": 0, "variance": 0, "execution_pct": null },
      "costs": { "budget": 0, "actual": 0, "variance": 0, "execution_pct": null },
      "expenses": { "budget": 100, "actual": 120, "variance": 20, "execution_pct": 120 }
    }
  }
}
```

| Campo | Descripción |
| - | - |
| `centers` | Centros seleccionados y sus descendientes, en orden de árbol. Los montos de un centro padre incluyen los de sus hijos. |
| `rows[].group` | `income`, `costs` o `expenses`. |
| `budget` | Suma de los meses del rango. |
| `actual` | Líneas de partidas **procesadas** del período con ese centro y cuenta. Ingresos: haber menos debe; costos y gastos: debe menos haber. |
| `variance` | `actual - budget`. |
| `execution_pct` | `actual / budget × 100`; `null` si el presupuesto es 0. |
| `status` | `ok`; `exceeded` (costo o gasto con `execution_pct` mayor que `expense_threshold`, o con real mayor que 0 sin presupuesto); `under` (ingreso con `execution_pct` menor que `income_threshold`). |
| `totals` | Suma de los centros raíz de la selección, sin contar dos veces a los subcentros. |

## Errores

| Código | Causa |
| - | - |
| `401` | API key ausente o inválida. |
| `403` | La integración no tiene el permiso requerido. |
| `404` | `Budget not found` (inexistente o de otra empresa). |
| `422` | `Validation errors`; por ejemplo `status`: `Solo se puede modificar un presupuesto en borrador.`, `Ya existe un presupuesto aprobado para 2026.`; `budget_id`: `No hay un presupuesto aprobado para 2026.`; o `lines.N.campo` con el error de la línea. |

```json theme={null}
{
  "status": false,
  "message": "Validation errors",
  "errors": { "status": ["Solo se puede modificar un presupuesto en borrador."] }
}
```

<Note>
  La importación y exportación en Excel del presupuesto está disponible en FileXpress (**Contabilidad → Presupuestos**), no en la API externa.
</Note>


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