Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/ierinconc/billar-pro-backend/llms.txt

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

Billar Pro supports an in-house product catalog — beverages, snacks, or any other item sold at the hall — and lets staff register sales directly against an occupied table during an active session. When the table is eventually closed, all open consumptions are automatically summed and folded into the session total, giving a single, unified bill that covers both play time and product sales.

The Producto Entity

Each item in the catalog is stored as a Producto record.
FieldTypeDescription
idLongAuto-generated primary key.
nombreStringDisplay name of the product (e.g., "Agua Postobón 600ml").
categoriaStringFree-text category label (e.g., "Bebidas", "Snacks").
precioDoubleUnit price charged per item.
disponiblebooleanAvailability flag. Only products where disponible = true can be added to a consumption.

Product Availability

Before saving a Consumo, the service checks producto.isDisponible(). If the flag is false, registration is blocked immediately:
if (!producto.isDisponible()) {
    throw new RuntimeException("Producto no disponible");
}
This makes it possible to temporarily hide seasonal items or products that are out of stock without disrupting historical consumption records that reference those products.

The Consumo Entity

A Consumo row represents a single line-item sale of a product to a table.
FieldTypeDescription
idLongAuto-generated primary key.
mesaMesaFK to the table where the product was ordered.
productoProductoFK to the catalog item that was sold.
cantidadIntegerNumber of units ordered.
subtotalDoubleComputed as producto.getPrecio() × cantidad. Stored on the record at the time of registration.
fechaLocalDateTimeExact timestamp when the consumption was registered (LocalDateTime.now()).
sesionSesionFK to the owning session. null while the table is still open. Set when the session closes.
The sesion field is the linking key that distinguishes open (in-flight) consumptions from closed (billed) ones. The query findByMesaAndSesionIsNull(mesa) is used both to list pending consumptions and to collect them during session closure.

The ConsumoRequest Body

Registering a consumption requires a JSON body with three fields:
FieldTypeDescription
mesaIdLongID of the occupied table.
productoIdLongID of the catalog product being ordered.
cantidadIntegerNumber of units.
{
  "mesaId": 3,
  "productoId": 5,
  "cantidad": 2
}

Registration Flow

1

Locate or create a product

List the existing catalog to find the product ID you need:
GET /api/productos
If the product does not exist yet, create it:
POST /api/productos
Content-Type: application/json

{
  "nombre": "Gatorade 500ml",
  "categoria": "Bebidas",
  "precio": 3500.0,
  "disponible": true
}
The response includes the new product’s id, which you will use in the next step.
2

Register the consumption against the table

While the target table’s estado is "OCUPADA", post the sale:
POST /api/consumos
Content-Type: application/json

{
  "mesaId": 3,
  "productoId": 5,
  "cantidad": 2
}
The service:
  1. Resolves the Mesa by mesaId — throws RuntimeException("Mesa no encontrada") if absent.
  2. Resolves the Producto by productoId — throws RuntimeException("Producto no encontrado") if absent.
  3. Checks producto.isDisponible() — throws RuntimeException("Producto no disponible") if false.
  4. Computes subtotal = producto.getPrecio() * cantidad.
  5. Saves the Consumo with sesion = null and fecha = LocalDateTime.now().
The saved Consumo is returned in the response body.
3

Query open consumptions for a table

To display a running tab for an occupied table, fetch all unlinked consumptions:
GET /api/consumos/mesa/{mesaId}
This calls consumoRepository.findByMesaAndSesionIsNull(mesa), returning only the line items that have not yet been assigned to a closed session — i.e., the active tab.Example response:
[
  {
    "id": 12,
    "mesa": { "id": 3, "numero": 3 },
    "producto": { "id": 5, "nombre": "Gatorade 500ml", "precio": 3500.0 },
    "cantidad": 2,
    "subtotal": 7000.0,
    "fecha": "2025-01-15T14:32:00",
    "sesion": null
  }
]
4

Close the table to link all consumptions to the session

When play ends, call:
PUT /api/mesas/{id}/cerrar
SesionService.guardarSesion fetches every Consumo where sesion IS NULL for this table, sums their subtotal values, persists the Sesion, and then updates each Consumo record to point at the new session. After this step the consumptions are permanently linked and no longer appear in the open tab query. See Tables & Sessions for the full closure flow.

Managing the Product Catalog

List all products

GET /api/productos
Returns every product in the catalog regardless of disponible status.

Get a single product

GET /api/productos/{id}
Throws RuntimeException("Producto no encontrado") if the ID does not exist.

Update a product

PUT /api/productos/{id}
Content-Type: application/json

{
  "nombre": "Agua 600ml",
  "categoria": "Bebidas",
  "precio": 2500.0,
  "disponible": true
}
Updates nombre, categoria, precio, and disponible in a single call.

Delete a product

DELETE /api/productos/{id}
Physically removes the record. See the tip below before deleting.
Prefer setting disponible = false over deleting a product. Hard-deleting a Producto that is referenced by historical Consumo rows will violate the foreign-key constraint and cause a database error. Marking it unavailable hides it from new sales while keeping all past consumption records intact — which is essential for accurate historical reports.

Build docs developers (and LLMs) love