Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/juadariasmar/inventory_project/llms.txt

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

Purchase orders close the inventory loop by providing a structured, auditable way to bring stock in from suppliers. An order moves from a mutable BORRADOR draft through to RECIBIDA receipt, at which point the system automatically posts stock-entry movements for every line item and locks the order against further changes.

OrdenCompra model

FieldTypeDescription
idIntAuto-incremented primary key
proveedorIdIntFK to the Proveedor who will fulfill the order
estadoEstadoOrdenCompraBORRADOR, RECIBIDA, or CANCELADA
totalFloatSum of all line subtotals (computed at creation)
notasString?Optional free-text notes
itemsOrdenCompraItem[]Line items
creadoEnDateTimeUTC timestamp of creation
recibidaEnDateTime?Timestamp set when the order is received
empresaIdStringTenant key

EstadoOrdenCompra enum and state transitions

BORRADOR ──────────────────► RECIBIDA
    │       POST /api/ordenes-compra/[id]/recibir

    └──────────────────────► CANCELADA
            POST /api/ordenes-compra/[id]/cancelar

BORRADOR

The order has been created and can still be edited. Items can be added or removed. No stock has been affected.

RECIBIDA

Merchandise has arrived. Stock-entry movements have been created automatically. The order record is now immutable.

CANCELADA

The order was cancelled before receipt. No stock was ever affected. Only orders in BORRADOR can be cancelled.

OrdenCompraItem structure

Each line item specifies what is being ordered and at what cost:
FieldTypeDescription
idIntAuto-incremented primary key
ordenCompraIdIntFK to the parent OrdenCompra
productoIdIntFK to the Producto being ordered
cantidadIntUnits ordered (positive integer)
costoUnitarioFloatPurchase cost per unit
subtotalFloatcantidad × costoUnitario

Receiving an order

POST /api/ordenes-compra/[id]/recibir
OrdenesCompraService.recibir executes the following steps inside a single database transaction:
1

Lock product rows

Acquires a pessimistic SELECT … FOR UPDATE lock on all products referenced in the order items (sorted by ID) to prevent concurrent quantity conflicts.
2

Create stock-entry movements

For each OrdenCompraItem, creates a Movimiento of type entrada with ordenCompraId set and a note of "Orden de compra #<id>".
3

Increment product quantities

Updates Producto.cantidad with { increment: cantidad } for each item.
4

Mark order as received

Sets estado = RECIBIDA and recibidaEn = now() on the OrdenCompra record.
5

Write audit record

Creates an Auditoria entry recording the receive action inside the same transaction.

Cancelling an order

POST /api/ordenes-compra/[id]/cancelar
Only orders in BORRADOR state can be cancelled. Attempting to cancel a RECIBIDA or already CANCELADA order returns a 409 Conflict error. Cancellation does not affect stock because no movements were ever created for a draft order.

PDF generation

Each purchase order can be exported as a formatted PDF document:
GET /api/pdf/orden-compra/[id]
The response has Content-Type: application/pdf and includes supplier contact details, all line items with costs, and the order total. PDFs are generated server-side using the pdf-lib library (v1.17.1). This is useful for sending to suppliers or archiving with physical delivery notes.

Purchase suggestions

The suggestion engine scans the product catalog for items whose current quantity has dropped at or below their stockMinimo threshold (plus the alert margin) and recommends how many units to purchase:
GET /api/compras/sugerencias
The suggestion quantity (sugerenciaCompra) is calculated by calcularSugerenciaCompraInteligente in src/lib/inventario.ts. When a product has sales history, the algorithm aims to cover 14 days of average daily consumption plus the stockMinimo as a safety buffer. For products without sales history it falls back to the simpler formula: max(stockMinimo × 2, stockMinimo + alertMargin + 1) − cantidadActual.

Proveedor model

Suppliers are stored as Proveedor records scoped to the company:
FieldTypeDescription
idIntAuto-incremented primary key
nombreStringSupplier display name (required)
nitString?Tax identification number
contactoString?Contact person name
telefonoString?Phone number
emailString?Email address
direccionString?Physical address
activoBooleanWhether the supplier is currently active (default true)
empresaIdStringTenant key
A supplier with one or more associated purchase orders cannot be deleted. The API returns a 409 Conflict error. Deactivate the supplier instead by setting activo = false.

Code example — create a purchase order

POST /api/ordenes-compra
Content-Type: application/json
{
  "proveedorId": 7,
  "notas": "Reposición mensual — prioridad alta",
  "items": [
    {
      "productoId": 42,
      "cantidad": 200,
      "costoUnitario": 900
    },
    {
      "productoId": 17,
      "cantidad": 50,
      "costoUnitario": 4500
    }
  ]
}
The response is the full OrdenCompra object including the nested items array and proveedor summary. The order is created in BORRADOR state with total computed automatically as 200 × 900 + 50 × 4500 = 405 000.

Build docs developers (and LLMs) love