Skip to content

Caja

Caja

Prefijo: /api/caja
Requiere Auth: Sí
Permiso requerido: caja, *

Módulo de caja para gestión del dinero en efectivo y electrónico. El flujo típico es: apertura de caja → operaciones (ventas, gastos) → cierre con resumen.

GET /api/caja/

Descripción: Lista los registros de caja del tenant con paginación y filtros opcionales por rango de fechas y usuario/cajero. Ideal para el panel de administración que muestra el historial de cierres de caja con filtros por día y por cajero.
Requiere Auth: Sí
Permiso requerido: caja, leer

Parámetros query:

ParámetroTipoObligatorioDescripción
skipint❌Registros a saltar (default 0)
limitint❌Máximo de registros a retornar (default 100)
desdedate❌Fecha inicio del filtro inclusiva (YYYY-MM-DD). Filtra por Caja.fecha >= desde
hastadate❌Fecha fin del filtro inclusiva (YYYY-MM-DD). Filtra por Caja.fecha <= hasta
usuariostring❌Filtra por nombre exacto del usuario/cajero que registró el cierre

Comportamiento:

  • Sin filtros: retorna los 100 registros más recientes del tenant ordenados por fecha descendente.
  • Con desde y hasta: retorna solo los cierres dentro del rango (inclusive). Útil para reportes diarios/semanales.
  • Con usuario: retorna solo los cierres de ese cajero específico. Combinable con rango de fechas.
  • Los filtros se combinan con AND lógico.

Respuesta exitosa: 200 OK

Headers de respuesta:

HeaderTipoDescripción
X-Total-CountintegerTotal de cierres de caja que cumplen los filtros (desde, hasta, usuario). No considera skip/limit.

Response:

[
{
"id": 1,
"base": 500000,
"cierre": 850000,
"credito": 100000,
"efectivo": 700000,
"gastos": 50000,
"total": 750000,
"transferencia": 50000,
"fecha": "2025-01-15",
"hora": "18:00:00",
"usuario": "Juan Pérez",
"id_jornada": 1,
"tenant_id": "uuid-tenant"
}
]

cURL:

Terminal window
# Sin filtros
curl "http://localhost/api/caja/?skip=0&limit=10" -H "Authorization: Bearer <token>"
# Solo cierres del 15 al 20 de enero
curl "http://localhost/api/caja/?desde=2025-01-15&hasta=2025-01-20" -H "Authorization: Bearer <token>"
# Solo cierres de un cajero específico
curl "http://localhost/api/caja/?usuario=Juan%20Pérez&desde=2025-01-15" -H "Authorization: Bearer <token>"

Python:

import httpx
resp = httpx.get("http://localhost/api/caja/",
params={"desde": "2025-01-15", "hasta": "2025-01-20", "usuario": "Juan Pérez"},
headers={"Authorization": f"Bearer :token"})
caja = resp.json()
total = int(resp.headers.get("X-Total-Count", "0"))
total_pages = (total + 10 - 1) // 10 # ceil division

JavaScript:

const params = new URLSearchParams({desde: '2025-01-15', hasta: '2025-01-20', usuario: 'Juan Pérez'});
const resp = await fetch(`http://localhost/api/caja/?$:params`, {
headers: {'Authorization': `Bearer $:token`}
});
const caja = await resp.json();
const totalCount = parseInt(resp.headers.get('X-Total-Count') || '0', 10);
const totalPages = Math.ceil(totalCount / 10);

GET /api/caja/count [NUEVO]

Descripción: Cuenta el total de registros de caja del tenant con los mismos filtros opcionales que GET /api/caja/. Diseñado para paginación del frontend: primero se llama /count para saber cuántos registros hay, y luego se llama /api/caja/ con skip y limit para la página actual.
Requiere Auth: Sí
Permiso requerido: caja, leer

Parámetros query:

ParámetroTipoObligatorioDescripción
desdedate❌Fecha inicio del filtro inclusiva (YYYY-MM-DD)
hastadate❌Fecha fin del filtro inclusiva (YYYY-MM-DD)
usuariostring❌Filtra por nombre exacto del usuario/cajero

Caso de uso típico:

  1. El admin abre la vista “Cierres de Caja” en NeoxAdminPanel.
  2. Frontend llama GET /api/caja/count?desde=2025-01-15&hasta=2025-01-20 para saber que hay 45 cierres en ese rango.
  3. Frontend renderiza paginator con 5 páginas (10 por página).
  4. Para cada página, llama GET /api/caja/?desde=2025-01-15&hasta=2025-01-20&skip=0&limit=10.

Respuesta exitosa: 200 OK

Response:

{
"total": 45
}

cURL:

Terminal window
# Contar todos los cierres del tenant
curl "http://localhost/api/caja/count" -H "Authorization: Bearer <token>"
# Contar cierres de un rango específico
curl "http://localhost/api/caja/count?desde=2025-01-15&hasta=2025-01-20" -H "Authorization: Bearer <token>"
# Contar cierres de un cajero en un rango
curl "http://localhost/api/caja/count?usuario=Juan%20Pérez&desde=2025-01-15&hasta=2025-01-20" -H "Authorization: Bearer <token>"

Python:

import httpx
resp = httpx.get("http://localhost/api/caja/count",
params={"desde": "2025-01-15", "hasta": "2025-01-20"},
headers={"Authorization": f"Bearer :token"})
total = resp.json()["total"] # 45

JavaScript:

const resp = await fetch('http://localhost/api/caja/count?desde=2025-01-15&hasta=2025-01-20', {
headers: {'Authorization': `Bearer $:token`}
});
const { total } = await resp.json(); // 45

GET /api/caja/resumen-cierre

Descripción: Obtiene el resumen de cierre de jornada para un usuario
Requiere Auth: Sí
Permiso requerido: caja, leer

Parámetros query:

ParámetroTipoObligatorioDescripción
usuariostring✅Nombre del usuario

Respuesta exitosa: 200 OK

Response:

{
"ventas_canal": {
"local": 450000,
"electronico": 150000
},
"ventas_medio": {
"efectivo": 400000,
"transferencia": 100000,
"credito": 100000
},
"creditos_jornada": {
"monto_total": 200000,
"abono_inicial": 0
},
"abonos": {
"efectivo": 50000,
"digital": 30000,
"total": 80000
},
"gastos": 50000,
"ingresos": 0,
"retiros": 0,
"base_inicial": 500000
}

cURL:

Terminal window
curl "http://localhost/api/caja/resumen-cierre?usuario=Juan%20P%C3%A9rez" -H "Authorization: Bearer <token>"

GET /api/caja/base-inicial

Descripción: Obtiene la base inicial de la jornada activa del usuario
Requiere Auth: Sí
Permiso requerido: caja, leer

Parámetros query:

ParámetroTipoObligatorioDescripción
usuariostring✅Nombre del usuario

Respuesta exitosa: 200 OK

Response:

500000

cURL:

Terminal window
curl "http://localhost/api/caja/base-inicial?usuario=Juan%20P%C3%A9rez" -H "Authorization: Bearer <token>"

GET /api/caja/gastos

Descripción: Obtiene el total de gastos en un rango de fechas
Requiere Auth: Sí
Permiso requerido: caja, leer

Parámetros query:

ParámetroTipoObligatorioDescripción
desdedate✅Fecha inicial
hastadate✅Fecha final

Respuesta exitosa: 200 OK

Response:

{
"total": 150000,
"fecha": "2025-01-01"
}

cURL:

Terminal window
curl "http://localhost/api/caja/gastos?desde=2025-01-01&hasta=2025-01-15" -H "Authorization: Bearer <token>"

GET /api/caja/ultimo

Descripción: Obtiene el último ID de caja registrado
Requiere Auth: Sí
Permiso requerido: caja, leer

Respuesta exitosa: 200 OK

Response:

42

cURL:

Terminal window
curl http://localhost/api/caja/ultimo -H "Authorization: Bearer <token>"

GET /api/caja/estado-sedes [NUEVO]

Descripción: Devuelve el estado de ventas y jornada por sede en una fecha — alimenta las pestañas del CierreCaja (una pestaña por sede). Para cada sede activa indica: estado de su jornada (ABIERTA/CERRADA/SIN_JORNADA), total vendido, cantidad de ventas y desglose por medio (efectivo/transferencia/crédito, con pagos_venta como fuente de verdad).
Requiere Auth: Sí
Permiso requerido: caja, leer

Parámetros query:

ParámetroTipoObligatorioDescripción
fechadate (YYYY-MM-DD)❌Fecha a consultar (default: hoy, hora Colombia)
sede_idstring UUID❌Acota a una sola sede. En Asistente se ignora y se fuerza su sede; en Admin/Superadmin aplica el filtro. Omitir = todas las sedes

Comportamiento / Casos de uso:

  • Cierre multi-sede: el admin abre el CierreCaja y ve las pestañas por sede con lo vendido y si su jornada está abierta.
  • jornada_estado: ABIERTA gana sobre CERRADA; SIN_JORNADA si esa sede no abrió/operó en la fecha.
  • Ventas antiguas (sin sede_id por backfill) no suman en ninguna sede.

Respuesta exitosa: 200 OK — arreglo con una fila por sede activa.

Response:

[
{
"sede_id": "uuid-sede-1",
"sede_codigo": "default",
"sede_nombre": "Sede Principal",
"es_principal": true,
"jornada_estado": "ABIERTA",
"jornada_id": 15,
"total_vendido": 450000,
"cantidad_ventas": 12,
"efectivo": 300000,
"transferencia": 100000,
"credito": 50000,
"fecha": "2026-10-22"
},
{
"sede_id": "uuid-sede-2",
"sede_codigo": "NOR",
"sede_nombre": "Sede Norte",
"es_principal": false,
"jornada_estado": "SIN_JORNADA",
"jornada_id": null,
"total_vendido": 0,
"cantidad_ventas": 0,
"efectivo": 0,
"transferencia": 0,
"credito": 0,
"fecha": "2026-10-22"
}
]

Errores:

CódigoCausaSolución
401Token inválidoRe-login
403Sin permiso caja, leerSolicitar permiso
422fecha con formato inválidoUsar YYYY-MM-DD

cURL:

Terminal window
curl "http://localhost/api/caja/estado-sedes?fecha=2026-10-22" -H "Authorization: Bearer <token>"

Python:

import httpx
resp = httpx.get("http://localhost/api/caja/estado-sedes",
params={"fecha": "2026-10-22"},
headers={"Authorization": f"Bearer {token}"})
estado = resp.json() # [{sede_nombre, jornada_estado, total_vendido, ...}]

JavaScript:

const resp = await fetch('http://localhost/api/caja/estado-sedes?fecha=2026-10-22', {
headers: {'Authorization': `Bearer ${token}`}
});
const estado = await resp.json();

POST /api/caja/

Descripción: Registra el CIERRE de caja con todos los totales del día (base, cierre, efectivo, crédito, gastos, transferencia). Diferencia clave vs /apertura: este endpoint cierra la jornada contabilizando ventas, gastos y calculando el cuadratura. Usar al final del día cuando el cajero hace arqueo.
Requiere Auth: Sí
Permiso requerido: caja, crear

Request:

{
"base": 500000,
"cierre": 1200000,
"efectivo": 800000,
"credito": 100000,
"gastos": 50000,
"transferencia": 50000,
"total": 1150000,
"usuario": "Juan Pérez",
"id_jornada": 5
}

Campos:

CampoTipoObligatorioDescripción
baseint✅Base inicial (debe coincidir con apertura)
cierreint✅Total en caja al cerrar (efectivo + cheques)
efectivoint✅Efectivo contado en arqueo
creditoint✅Ventas a crédito del día
gastosint✅Egresos/gastos del día
transferenciaint✅Transferencias recibidas
totalint✅efectivo + credito + transferencia - gastos (debe cuadrar con cierre)
usuariostring✅Cajero que cierra
id_jornadaint✅Jornada a cerrar

Comportamiento / Casos de uso:

  • Cierre diario: cajero cuenta efectivo, ingresa totales, sistema valida cuadratura
  • Si total != cierre → advertencia pero permite guardar (para ajustes manuales)
  • Crea registro en caja + actualiza jornada_caja.estado = CERRADA

Respuesta exitosa: 201 Created

Response:

{
"id": 10,
"base": 500000,
"cierre": 1200000,
"credito": 100000,
"efectivo": 800000,
"gastos": 50000,
"total": 1150000,
"transferencia": 50000,
"fecha": "2025-01-15",
"hora": "18:00:00",
"usuario": "Juan Pérez",
"id_jornada": 5,
"tenant_id": "uuid-tenant"
}

Errores:

CódigoCausaSolución
400Jornada no activa o datos inválidosVerificar id_jornada y usuario
403Sin permiso caja, crearSolicitar a admin
409Jornada ya cerradaNo se puede cerrar dos veces

cURL:

Terminal window
curl -X POST http://localhost/api/caja/ \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <token>" \
-d '{"base":500000,"cierre":1200000,"efectivo":800000,"credito":100000,"gastos":50000,"transferencia":50000,"total":1150000,"usuario":"Juan Pérez","id_jornada":5}'

Python:

import httpx
resp = httpx.post("http://localhost/api/caja/",
json={"base": 500000, "cierre": 1200000, "efectivo": 800000, "credito": 100000, "gastos": 50000, "transferencia": 50000, "total": 1150000, "usuario": "Juan Pérez", "id_jornada": 5},
headers={"Authorization": f"Bearer :token"})
cierre = resp.json()

JavaScript:

const resp = await fetch('http://localhost/api/caja/', {
method: 'POST',
headers: {'Content-Type': 'application/json', 'Authorization': `Bearer $:token`},
body: JSON.stringify({base: 500000, cierre: 1200000, efectivo: 800000, credito: 100000, gastos: 50000, transferencia: 50000, total: 1150000, usuario: 'Juan Pérez', id_jornada: 5})
});
const cierre = await resp.json();

POST /api/caja/apertura

Descripción: Registra la apertura de caja para un usuario
Requiere Auth: Sí
Permiso requerido: caja, crear

Request:

{
"base": 500000,
"usuario": "Juan Pérez"
}

Respuesta exitosa: 201 Created

Response:

{
"id_caja": 10,
"id_jornada": 5,
"base": 500000,
"mensaje": "Apertura registrada exitosamente"
}

cURL:

Terminal window
curl -X POST http://localhost/api/caja/apertura \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <token>" \
-d '{"base":500000,"usuario":"Juan Pérez"}'

GET /api/caja/:caja_id

Descripción: Obtiene el detalle completo de un cierre de caja por ID. Incluye todos los totales (base, cierre, efectivo, crédito, gastos, transferencia) y metadatos (fecha, hora, usuario, jornada). Para auditoría, reimpresión de cierre o corrección de errores.
Requiere Auth: Sí
Permiso requerido: caja, leer

Parámetros path:

ParámetroTipoObligatorioDescripción
caja_idint✅ID del registro de caja (cierre)

Comportamiento / Casos de uso:

  • Admin revisa cierre específico por discrepancia en arqueo
  • Reimprimir comprobante de cierre
  • Verificar que un cierre tiene los datos correctos antes de anular

Respuesta exitosa: 200 OK

Response:

{
"id": 1,
"base": 500000,
"cierre": 850000,
"credito": 100000,
"efectivo": 700000,
"gastos": 50000,
"total": 750000,
"transferencia": 50000,
"fecha": "2025-01-15",
"hora": "18:00:00",
"usuario": "Juan Pérez",
"id_jornada": 1,
"tenant_id": "uuid-tenant"
}

Errores:

CódigoCausaSolución
404Cierre no encontradoVerificar caja_id
403Sin permiso caja, leerSolicitar a admin

cURL:

Terminal window
curl http://localhost/api/caja/1 -H "Authorization: Bearer <token>"

Python:

import httpx
resp = httpx.get("http://localhost/api/caja/1", headers={"Authorization": f"Bearer :token"})
caja = resp.json()

JavaScript:

const resp = await fetch('http://localhost/api/caja/1', {
headers: {'Authorization': `Bearer $:token`}
});
const caja = await resp.json();

PUT /api/caja/:caja_id

Descripción: Actualiza campos de un cierre de caja ya registrado. Solo admin para corregir errores de digitación en arqueo. No cambia el estado de la jornada (para reabrir usar jornadas).
Requiere Auth: Sí
Permiso requerido: caja, actualizar

Parámetros path:

ParámetroTipoObligatorioDescripción
caja_idint✅ID del registro de caja

Request (campos opcionales, solo los enviados):

{
"cierre": 900000,
"efectivo": 750000,
"total": 850000,
"gastos": 60000
}

Campos actualizables:

CampoTipoDescripción
cierreintTotal en caja al cerrar
efectivointEfectivo contado
creditointVentas a crédito
gastosintEgresos del día
transferenciaintTransferencias
totalintTotal calculado

Comportamiento / Casos de uso:

  • Corrección de error de digitación: cajero escribió mal el efectivo
  • Ajuste post-cierre: se detecta diferencia en conciliación bancaria
  • NO recalcula automáticamente — el admin debe asegurar que total = efectivo + credito + transferencia - gastos

Respuesta exitosa: 200 OK — retorna CajaResponse actualizado

Response:

{
"id": 1,
"base": 500000,
"cierre": 900000,
"credito": 100000,
"efectivo": 750000,
"gastos": 60000,
"total": 850000,
"transferencia": 50000,
"fecha": "2025-01-15",
"hora": "18:00:00",
"usuario": "Juan Pérez",
"id_jornada": 1,
"tenant_id": "uuid-tenant"
}

Errores:

CódigoCausaSolución
404Cierre no encontradoVerificar caja_id
403Sin permiso caja, actualizarSolo admin
400Datos inválidosValidar tipos numéricos

cURL:

Terminal window
curl -X PUT http://localhost/api/caja/1 \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <token>" \
-d '{"cierre":900000,"efectivo":750000,"total":850000,"gastos":60000}'

Python:

import httpx
resp = httpx.put("http://localhost/api/caja/1",
json={"cierre": 900000, "efectivo": 750000, "total": 850000, "gastos": 60000},
headers={"Authorization": f"Bearer :token"})
caja = resp.json()

JavaScript:

const resp = await fetch('http://localhost/api/caja/1', {
method: 'PUT',
headers: {'Content-Type': 'application/json', 'Authorization': `Bearer $:token`},
body: JSON.stringify({cierre: 900000, efectivo: 750000, total: 850000, gastos: 60000})
});
const caja = await resp.json();

DELETE /api/caja/:caja_id

Descripción: Elimina un registro de cierre de caja (hard delete). Solo para casos excepcionales: registro de prueba, cierre duplicado por error de sistema. NO usar para anular cierre real — eso requiere reabrir jornada y nuevo cierre.
Requiere Auth: Sí
Permiso requerido: caja, eliminar

Parámetros path:

ParámetroTipoObligatorioDescripción
caja_idint✅ID del registro de caja

Comportamiento / Casos de uso:

  • Cierre de prueba creado en desarrollo/testing
  • Doble cierre por bug de frontend (verificar que no haya movimientos posteriores)
  • ADVERTENCIA: Elimina el registro permanentemente. No hay soft delete en caja.

Respuesta exitosa: 204 No Content

Errores:

CódigoCausaSolución
404Cierre no encontradoVerificar caja_id
403Sin permiso caja, eliminarSolo superadmin
409Tiene dependencias (movimientos)No se puede eliminar

cURL:

Terminal window
curl -X DELETE http://localhost/api/caja/1 -H "Authorization: Bearer <token>"

Python:

import httpx
resp = httpx.delete("http://localhost/api/caja/1", headers={"Authorization": f"Bearer :token"})
assert resp.status_code == 204

JavaScript:

const resp = await fetch('http://localhost/api/caja/1', {
method: 'DELETE',
headers: {'Authorization': `Bearer $:token`}
});
// 204 = éxito sin body