Skip to main content

Visión General

El módulo de Caja permite gestionar el efectivo y otros métodos de pago en punto de venta, con flujos completos de apertura, movimientos, arqueo y cierre de caja.
El sistema soporta múltiples cajas por empresa con control granular de permisos por usuario.

Arquitectura del Módulo

Servicios Core

El módulo de caja utiliza tres servicios principales con interfaces:
CajaController.php:23
public function __construct(
    private readonly CajaServiceInterface       $cajaService,
    private readonly CajaSesionServiceInterface $sesionService,
    private readonly CajaArqueoServiceInterface $arqueoService,
) {}

CajaService

CRUD de cajas Validaciones Activación/Desactivación

CajaSesionService

Apertura de sesión Cierre de sesión Validación de estados

CajaArqueoService

Cálculo de arqueo Resumen por método de pago Diferencias teoría vs real

Modelo de Datos

Tabla cajas

Caja.php:15
protected $fillable = [
    'id_empresa',
    'nombre',
    'descripcion',
    'id_responsable',            // Usuario responsable de la caja
    'id_usuario',                // Usuario que abrió la sesión actual
    'id_usuario_cierre',         // Usuario que cerró
    'id_usuario_validacion',     // Usuario que validó el cierre
    'fecha_apertura',
    'fecha_cierre',
    'fecha_autorizacion_cierre',
    'saldo_inicial',             // Monto inicial al abrir
    'tipo_apertura',             // normal, especial, etc.
    'total_teorico',             // Calculado: inicial + ingresos - egresos
    'total_real',                // Conteo físico al cerrar
    'diferencia',                // total_real - total_teorico
    'tipo_cierre',
    'tipo_diferencia',           // sobrante, faltante, exacto
    'estado',                    // activa, inactiva, cerrada
    'observaciones',
    'observaciones_cierre',
];

Estados de Caja

CajaEstadoEnum.php
enum CajaEstadoEnum: string
{
    case Activa = 'activa';      // Caja abierta y operativa
    case Inactiva = 'inactiva';  // Caja cerrada temporalmente
    case Cerrada = 'cerrada';    // Sesión cerrada (requiere nueva apertura)
}

API Endpoints

Crear Caja

CajaController.php:42
POST /api/cajas
Request:
{
  "nombre": "Caja Principal",
  "descripcion": "Caja para ventas mostrador",
  "id_responsable": 5,
  "metodos_pago": [1, 2, 3, 7]  // IDs de métodos de pago habilitados
}

Apertura de Caja

CajaController.php:91
POST /api/cajas/{id}/abrir
Request:
{
  "saldo_inicial": 200.00,
  "tipo_apertura": "normal",
  "observaciones": "Apertura turno mañana",
  "billetes": [
    { "denominacion": 200, "cantidad": 1 },
    { "denominacion": 100, "cantidad": 0 },
    { "denominacion": 50, "cantidad": 0 },
    { "denominacion": 20, "cantidad": 0 },
    { "denominacion": 10, "cantidad": 0 }
  ]
}
No se puede abrir una caja que ya tiene una sesión activa. Debe cerrarse primero.

Registrar Movimiento

CajaController.php:128
POST /api/cajas/{id}/movimientos
Request:
{
  "tipo": "Ingreso",  // Ingreso | Egreso
  "monto": 50.00,
  "concepto": "Gastos varios",
  "descripcion": "Compra de útiles de oficina"
}
Validación:
CajaController.php:135
if ($caja->estado !== CajaEstadoEnum::Activa->value) {
    return $this->unprocessable('La caja no está activa.');
}

Obtener Resumen de Arqueo

CajaController.php:165
GET /api/cajas/{id}/resumen
Respuesta:
{
  "saldo_inicial": "200.00",
  "total_ingresos": "1850.00",
  "total_egresos": "120.00",
  "saldo_teorico": "1930.00",
  "saldo_real": "1925.00",
  "diferencia": "-5.00",
  "ventas_por_metodo": [
    {
      "metodo": "Efectivo",
      "cantidad": 15,
      "monto_total": "850.00"
    },
    {
      "metodo": "Yape",
      "cantidad": 8,
      "monto_total": "450.00"
    },
    {
      "metodo": "POS Visa",
      "cantidad": 12,
      "monto_total": "550.00"
    }
  ]
}

Obtener Caja Activa

CajaController.php:150
GET /api/cajas/activa
Retorna la caja activa de la empresa del usuario autenticado.

Cálculo de Saldo Teórico

El saldo teórico se calcula dinámicamente:
Caja.php:118
public function calcularSaldoTeorico(): float
{
    $ingresos = $this->movimientos()->where('tipo', 'Ingreso')->sum('monto');
    $egresos = $this->movimientos()->where('tipo', 'Egreso')->sum('monto');

    return (float) $this->saldo_inicial + $ingresos - $egresos;
}
Los ingresos por ventas se registran automáticamente al confirmar una venta en el sistema.

Relaciones de Base de Datos

Relación con Métodos de Pago

Caja.php:98
public function metodosPago()
{
    return $this->belongsToMany(
        MetodoPago::class,
        'caja_metodos_pago',
        'id_caja',
        'id_metodo_pago'
    )
    ->withPivot('activo')
    ->withTimestamps();
}
Permite definir qué métodos de pago acepta cada caja (Efectivo, Yape, POS, Transferencia, etc.).

Permisos Granulares

La tabla permisos_caja define permisos específicos por usuario:
CajaController.php:209
GET /api/cajas/permisos/{usuario_id}
Permisos disponibles:
  • puede_abrir_caja
  • puede_cerrar_caja
  • puede_autorizar_cierre
  • puede_rechazar_cierre
  • puede_registrar_movimientos
  • puede_ver_reportes
Estos permisos son independientes del sistema de roles general. Permiten control fino sobre quién puede hacer qué en las cajas.
Actualizar permisos:
CajaController.php:221
PUT /api/cajas/permisos/{usuario_id}
{
  "puede_abrir_caja": true,
  "puede_cerrar_caja": false,
  "puede_autorizar_cierre": false,
  "puede_rechazar_cierre": false,
  "puede_registrar_movimientos": true,
  "puede_ver_reportes": true
}

Auditoría

Todos los eventos importantes se registran en auditorias_caja:
CajaController.php:184
GET /api/cajas/{id}/auditoria
Eventos auditados:
  • Apertura de caja
  • Cierre de caja
  • Modificación de saldo inicial
  • Autorización/Rechazo de cierre
  • Cambios de estado

Integración con Frontend

Componentes ubicados en:
resources/js/components/Finanzas/Caja/
├── page.jsx              # Vista principal
├── AperturaCajaModal.jsx # Modal de apertura
├── CierreModal.jsx       # Modal de cierre
├── ArqueoModal.jsx       # Modal de arqueo
└── hooks/
    ├── useCajaActiva.js
    ├── useAbrirCaja.js
    └── useArqueo.js
Ejemplo de uso:
import { useCajaActiva, useAbrirCaja } from './hooks';

const { data: cajaActiva } = useCajaActiva();

const abrirMutation = useAbrirCaja();

const handleAbrir = (datos) => {
  abrirMutation.mutate({
    id: cajaId,
    saldo_inicial: datos.saldo_inicial,
    billetes: datos.billetes,
  });
};

Flujo de Trabajo Típico

1

Crear Caja

Administrador crea una nueva caja y asigna un responsable
2

Configurar Permisos

Definir qué usuarios pueden abrir, cerrar y operar la caja
3

Apertura Diaria

Usuario con permiso abre la caja, registra saldo inicial y billetes
4

Operaciones

Registro de ventas automático, movimientos manuales (ingresos/egresos)
5

Arqueo

Consultar resumen en tiempo real: saldo teórico vs ventas por método
6

Cierre

Contar efectivo real, registrar diferencias, solicitar autorización si aplica
Use GET /api/cajas/denominaciones para obtener la lista de billetes y monedas peruanas para el conteo de apertura/cierre.

Build docs developers (and LLMs) love