Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/Eleazarguitar18/kantuta_pos_back/llms.txt

Use this file to discover all available pages before exploring further.

In Kantuta POS, every financial transaction — whether a sale, a purchase paid from the drawer, or a cash movement — must be linked to an active cash register session (SesionCaja). A session represents a single operator shift: it opens with a counted starting float, accumulates all activity during the shift, and closes with a physical cash count that the backend reconciles against the theoretical balance. This lifecycle ensures full auditability and eliminates any end-of-day discrepancies going unexplained.

Caja Types

Each physical cash register (Caja) is created with a especialidad field that restricts the types of operations it can process:

SOLO_VENTAS

Dedicated to retail sales only. Sessions on this caja record product sales (POST /ventas) but not agent recharge transactions.

SOLO_AGENTES

Dedicated to agent operations exclusively. Sales cannot be posted against this caja’s sessions.

MIXTA

The default. Accepts both retail sales and agent transactions in the same session — ideal for small stores with a single counter.
Only users with the Administrador role can create new physical cajas via POST /cajas. Assigning the wrong especialidad at creation time will cause validation errors downstream when operators try to post mismatched transaction types.

Session Lifecycle

1

Check for an Existing Active Session

Before opening a new session, query whether the operator already has an open one. This prevents duplicates and lets your frontend inject the id_sesion_caja automatically.
GET /cajas/sesion-activa/:id_usuario
Example:
curl -X GET https://api.example.com/cajas/sesion-activa/2 \
  -H "Authorization: Bearer <token>"
Response — session exists (200 OK):
{
  "id": 12,
  "id_caja": 1,
  "monto_inicial": "100.00",
  "estado_sesion": "ABIERTA",
  "fecha_apertura": "2025-06-01T09:00:00.000Z",
  "id_usuario": 2
}
If no session is open the endpoint returns null or a 404. In that case, redirect the operator to the open-session screen.
2

Open a Session

Post to /cajas/abrir with the physical caja ID, the starting float amount, and the operator’s user ID.
POST /cajas/abrir
Request body:
{
  "id_caja": 1,
  "monto_inicial": 150.00,
  "id_usuario": 2,
  "id_user_create": 2
}
FieldTypeRequiredDescription
id_cajaintegerID of the physical cash register
monto_inicialnumberOptionalStarting float (min 0). Optional for Operadores
id_usuariointegerID of the operator opening the session
id_user_createintegerAudit field — usually the same as id_usuario
Example:
curl -X POST https://api.example.com/cajas/abrir \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "id_caja": 1,
    "monto_inicial": 150.00,
    "id_usuario": 2,
    "id_user_create": 2
  }'
Response (201 Created):
{
  "id": 12,
  "id_caja": 1,
  "id_usuario": 2,
  "monto_inicial": "150.00",
  "estado_sesion": "ABIERTA",
  "fecha_apertura": "2025-06-01T09:00:00.000Z"
}
Store the returned id as id_sesion_caja in your application context — every subsequent transaction on this shift must reference it.
A caja cannot be opened if the operator (id_usuario) already has a session with estado_sesion: ABIERTA. The backend will reject the request. Always call GET /cajas/sesion-activa/:id_usuario first.
3

Process Sales and Agent Transactions

With an open session, post sales and agent operations referencing the active id_sesion_caja. Stock and session balances are updated automatically.
{
  "metodo_pago": "EFECTIVO",
  "id_sesion_caja": 12,
  "detalles": [
    { "id_producto": 3, "cantidad": 2, "precio_unitario": 25.50 }
  ],
  "id_user_create": 2
}
See the Sales Workflow guide for full details on creating sales.
4

Record Cash Movements (Ingresos & Egresos)

Track any manual cash-in or cash-out that is not a direct sale — for example, petty cash withdrawals, change fund top-ups, or supplier payments made from the drawer.
POST /cajas/movimiento
Request body:
{
  "tipo": "EGRESO",
  "monto": 25.00,
  "motivo": "Compra de bolsas para empaque",
  "id_sesion_caja": 12,
  "id_user_create": 2
}
FieldTypeRequiredDescription
tipoINGRESO | EGRESODirection of cash flow
montonumberAmount (minimum 0.10)
motivostringReason for the movement
id_sesion_cajaintegerActive session to attach this movement to
id_user_createintegerAudit field
Example:
curl -X POST https://api.example.com/cajas/movimiento \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "tipo": "INGRESO",
    "monto": 200.00,
    "motivo": "Recarga de fondo de cambio",
    "id_sesion_caja": 12,
    "id_user_create": 2
  }'
5

Check the Live Balance

At any point during the shift, fetch the running balance for the session. This is the theoretical total before close.
GET /cajas/sesion/:id/balance
Example:
curl -X GET https://api.example.com/cajas/sesion/12/balance \
  -H "Authorization: Bearer <token>"
The response includes accumulated sales totals, agent totals, cash movements, and the theoretical final balance.
6

Close the Session (End-of-Shift Reconciliation)

At shift end, the operator physically counts the cash in the drawer and submits the real amount. The backend automatically calculates:
  • monto_final_teorico — starting float + all sales + ingresos − egresos
  • diferenciamonto_final_realmonto_final_teorico (negative = shortage, positive = surplus)
PATCH /cajas/sesion/:id/cerrar
Request body:
{
  "monto_final_real": 1450.80,
  "id_user_update": 2
}
FieldTypeRequiredDescription
monto_final_realnumberPhysical cash counted by operator (min 0)
id_user_updatenumberAudit field
Example:
curl -X PATCH https://api.example.com/cajas/sesion/12/cerrar \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "monto_final_real": 1450.80,
    "id_user_update": 2
  }'
Response (200 OK):
{
  "id": 12,
  "estado_sesion": "CERRADA",
  "monto_inicial": "150.00",
  "monto_final_teorico": "1451.30",
  "monto_final_real": "1450.80",
  "diferencia": "-0.50",
  "fecha_apertura": "2025-06-01T09:00:00.000Z",
  "fecha_cierre": "2025-06-01T18:00:00.000Z"
}
Once closed, the session transitions to estado_sesion: CERRADA and no further transactions can be posted against it.

Quick Reference: Session Endpoints

MethodEndpointDescription
GET/cajasList all physical cajas
POST/cajasCreate a physical caja (Administrador only)
GET/cajas/:idGet caja by ID
PATCH/cajas/:idUpdate a caja
DELETE/cajas/:id?id_user_update=Soft-delete a caja
POST/cajas/abrirOpen a new session
GET/cajas/sesion-activa/:id_usuarioGet operator’s active session
GET/cajas/sesion/:id/balanceGet running session balance
PATCH/cajas/sesion/:id/cerrarClose session with reconciliation
POST/cajas/movimientoRecord a cash in/out movement

Role Permissions

Administrador

  • Create physical cajas (POST /cajas)
  • Open sessions (POST /cajas/abrir)
  • Close sessions, view balances, and manage movements

Operador

  • Open sessions (POST /cajas/abrir)
  • Record movements and process transactions against their own active session
  • Cannot create physical cajas
For frontend integrations, store the active SesionCaja ID in a global React context (e.g., CajaContext). This lets you inject id_sesion_caja automatically into every sale or purchase form without requiring operators to type it manually. See the flow diagram in the backend’s guia_flujo_sesion_caja.md for a full state-machine reference.

Build docs developers (and LLMs) love