Skip to main content

Descripción General

El endpoint de Compras permite gestionar las compras realizadas a proveedores, incluyendo el registro de productos adquiridos, control automático de inventario, gestión de documentos (facturas, boletas), y seguimiento de pagos al contado o a crédito. Cada compra registrada actualiza automáticamente el stock de los productos y genera movimientos de inventario para trazabilidad completa.

Permisos Requeridos

Las operaciones en este endpoint requieren los siguientes permisos:
  • compras.view - Ver listado y detalle de compras
  • compras.create - Crear nuevas compras
  • compras.edit - Actualizar compras existentes
  • compras.delete - Eliminar y anular compras
Los usuarios con rol de Administrador (rol_id=1) tienen acceso automático a todas las operaciones.

Listar Compras

Obtiene el listado completo de compras de la empresa del usuario autenticado, ordenadas por ID descendente (más recientes primero).

Headers

Authorization
string
required
Token de autenticación Bearer
Bearer {token}
Accept
string
default:"application/json"
Tipo de contenido aceptado

Permisos

Requiere permiso: compras.view

Respuesta Exitosa

success
boolean
Indica si la operación fue exitosa
data
array
Array de compras

Ejemplo de Solicitud

curl --request GET \
  --url https://tu-dominio.com/api/compras \
  --header 'Authorization: Bearer tu_token_aqui' \
  --header 'Accept: application/json'

Ejemplo de Respuesta

{
  "success": true,
  "data": [
    {
      "id_compra": 42,
      "documento": "F001-00000042",
      "serie": "F001",
      "numero": "42",
      "fecha_emision": "2026-03-04",
      "fecha_vencimiento": "2026-04-04",
      "proveedor": {
        "proveedor_id": 15,
        "ruc": "20123456789",
        "razon_social": "DISTRIBUIDORA COMERCIAL SAC"
      },
      "tipo_pago": "Crédito",
      "id_tipo_pago": 2,
      "moneda": "PEN",
      "total": "5420.00",
      "estado": "1",
      "estado_nombre": "Activo",
      "usuario": "Juan Pérez",
      "created_at": "2026-03-04 14:30:22"
    }
  ]
}

Crear Compra

Registra una nueva compra a proveedor. Automáticamente actualiza el inventario de productos, genera movimientos de stock, y crea cuotas de pago si es a crédito.

Headers

Authorization
string
required
Token de autenticación Bearer
Content-Type
string
default:"application/json"
Tipo de contenido de la solicitud

Permisos

Requiere permiso: compras.create

Parámetros del Body

id_proveedor
integer
required
ID del proveedor (debe existir en la tabla proveedores)
tipo_doc
integer
required
ID del tipo de documento según tabla documentos_sunatEjemplos comunes:
  • 1 = Factura
  • 3 = Boleta de Venta
serie
string
required
Serie del documento (1-4 caracteres alfanuméricos en mayúsculas)Ejemplos: F001, B001, NC01
numero
string
required
Número correlativo del documento (solo dígitos, máximo 8)
fecha_emision
string
required
Fecha de emisión del documento (formato: Y-m-d)
fecha_vencimiento
string
Fecha de vencimiento para compras a crédito (formato: Y-m-d)
id_tipo_pago
integer
required
Tipo de pago:
  • 1 = Contado
  • 2 = Crédito
moneda
string
required
Código de moneda:
  • PEN = Soles Peruanos
  • USD = Dólares Americanos
productos
array
required
Array de productos comprados (mínimo 1 producto)
empresas_ids
array
Array de IDs de empresas asociadas a la compra (para compras compartidas)
cuotas
array
Array de cuotas de pago (requerido si id_tipo_pago = 2)
direccion
string
Dirección asociada a la compra
observaciones
string
Observaciones o notas adicionales

Validaciones Importantes

  • La combinación de proveedor + tipo_doc + serie + número debe ser única
  • La serie debe ser alfanumérica en mayúsculas (1-4 caracteres)
  • El número debe contener solo dígitos (máximo 8)
  • Debe incluir al menos 1 producto
  • Si es pago a crédito (id_tipo_pago=2), debe incluir cuotas

Respuesta Exitosa

success
boolean
Siempre true en caso de éxito
message
string
Mensaje de confirmación
data
object
Datos de la compra creada

Ejemplo de Solicitud

curl --request POST \
  --url https://tu-dominio.com/api/compras \
  --header 'Authorization: Bearer tu_token_aqui' \
  --header 'Content-Type: application/json' \
  --data '{
    "id_proveedor": 15,
    "tipo_doc": 1,
    "serie": "F001",
    "numero": "00000123",
    "fecha_emision": "2026-03-04",
    "fecha_vencimiento": "2026-04-04",
    "id_tipo_pago": 2,
    "moneda": "PEN",
    "direccion": "Av. Los Industriales 456",
    "observaciones": "Compra de mercadería para almacén central",
    "productos": [
      {
        "id_producto": 10,
        "cantidad": 50,
        "costo": 25.50
      },
      {
        "id_producto": 12,
        "cantidad": 100,
        "costo": 12.80
      }
    ],
    "cuotas": [
      {
        "monto": 1000.00,
        "fecha": "2026-03-19"
      },
      {
        "monto": 1555.00,
        "fecha": "2026-04-04"
      }
    ],
    "empresas_ids": [1, 3]
  }'

Ejemplo de Respuesta

{
  "success": true,
  "message": "Compra registrada exitosamente",
  "data": {
    "id_compra": 43,
    "documento": "F001-00000123"
  }
}

Errores Comunes

{
  "success": false,
  "message": "Ya existe una compra registrada con este documento del proveedor"
}
{
  "success": false,
  "message": "Serie inválida. Use 1-4 caracteres alfanuméricos"
}
{
  "success": false,
  "message": "Número inválido. Use solo dígitos (máximo 8)"
}

Obtener Detalle de Compra

Obtiene el detalle completo de una compra específica, incluyendo productos, proveedor, cuotas de pago y empresas asociadas.

Headers

Authorization
string
required
Token de autenticación Bearer

Permisos

Requiere permiso: compras.view

Parámetros de Ruta

id
integer
required
ID de la compra a consultar

Respuesta Exitosa

success
boolean
Indica si la operación fue exitosa
data
object
Objeto con el detalle completo de la compra

Ejemplo de Solicitud

curl --request GET \
  --url https://tu-dominio.com/api/compras/43 \
  --header 'Authorization: Bearer tu_token_aqui' \
  --header 'Accept: application/json'

Ejemplo de Respuesta

{
  "success": true,
  "data": {
    "id_compra": 43,
    "serie": "F001",
    "numero": "00000123",
    "fecha_emision": "2026-03-04",
    "fecha_vencimiento": "2026-04-04",
    "id_tipo_pago": 2,
    "moneda": "PEN",
    "subtotal": 2555.00,
    "igv": 0.00,
    "total": 2555.00,
    "direccion": "Av. Los Industriales 456",
    "observaciones": "Compra de mercadería para almacén central",
    "estado": "1",
    "proveedor": {
      "proveedor_id": 15,
      "ruc": "20123456789",
      "razon_social": "DISTRIBUIDORA COMERCIAL SAC",
      "direccion": "Jr. Las Flores 123, Lima"
    },
    "detalles": [
      {
        "id_producto": 10,
        "codigo": "PROD-001",
        "nombre": "Producto A",
        "cantidad": 50,
        "costo": 25.50,
        "subtotal": 1275.00
      },
      {
        "id_producto": 12,
        "codigo": "PROD-003",
        "nombre": "Producto C",
        "cantidad": 100,
        "costo": 12.80,
        "subtotal": 1280.00
      }
    ],
    "cuotas": [
      {
        "monto": 1000.00,
        "fecha": "2026-03-19",
        "estado": "1"
      },
      {
        "monto": 1555.00,
        "fecha": "2026-04-04",
        "estado": "1"
      }
    ],
    "empresas": [
      {
        "id_empresa": 1,
        "comercial": "Empresa Principal SAC",
        "ruc": "20612706702"
      },
      {
        "id_empresa": 3,
        "comercial": "Sucursal Norte EIRL",
        "ruc": "20987654321"
      }
    ]
  }
}

Error - Compra No Encontrada

{
  "success": false,
  "message": "Error al obtener compra: No query results for model..."
}

Actualizar Compra

Este endpoint está definido en las rutas pero no está implementado en el controlador. Actualmente, la actualización de compras no está disponible para mantener la integridad del inventario y trazabilidad de movimientos de stock.

Permisos

Requiere permiso: compras.edit
Si necesitas modificar una compra, se recomienda anularla y crear una nueva con los datos correctos para mantener el historial de auditoría.

Eliminar Compra

Este endpoint está definido en las rutas pero no está implementado en el controlador. Para eliminar lógicamente una compra, utiliza el endpoint de anulación.

Permisos

Requiere permiso: compras.delete

Anular Compra

Anula una compra existente. Esta operación revierte automáticamente el stock de todos los productos incluidos en la compra y genera movimientos de stock de salida para mantener la trazabilidad.
La anulación es irreversible. Los productos volverán a su stock anterior y se generarán movimientos de inventario de tipo “anulacion_compra”.

Headers

Authorization
string
required
Token de autenticación Bearer

Permisos

Requiere permiso: compras.delete

Parámetros de Ruta

id
integer
required
ID de la compra a anular

Proceso de Anulación

Al anular una compra, el sistema ejecuta las siguientes operaciones en una transacción:
  1. Valida que la compra no esté ya anulada
  2. Cambia el estado de la compra a “0” (Anulado)
  3. Para cada producto en la compra:
    • Reduce el stock actual por la cantidad comprada
    • Genera un movimiento de stock de tipo “salida” con motivo “Anulación de compra”
  4. Mantiene las cuotas y relaciones para historial

Respuesta Exitosa

success
boolean
Siempre true en caso de éxito
message
string
Mensaje de confirmación: “Compra anulada exitosamente”

Ejemplo de Solicitud

curl --request POST \
  --url https://tu-dominio.com/api/compras/43/anular \
  --header 'Authorization: Bearer tu_token_aqui' \
  --header 'Accept: application/json'

Ejemplo de Respuesta

{
  "success": true,
  "message": "Compra anulada exitosamente"
}

Errores Comunes

{
  "success": false,
  "message": "La compra ya está anulada"
}
{
  "success": false,
  "message": "Error al anular compra: No query results for model..."
}

Códigos de Estado HTTP

CódigoDescripción
200Operación exitosa
400Error de validación o lógica de negocio
401No autenticado (token inválido o faltante)
403No autorizado (falta permiso requerido)
404Compra no encontrada
500Error interno del servidor

Notas Importantes

Control de Inventario AutomáticoTodas las operaciones de compra afectan automáticamente el inventario:
  • Al crear: Incrementa stock y costo de productos
  • Al anular: Revierte el stock al estado anterior
  • Todos los movimientos quedan registrados en movimientos_stock
Ámbito Multi-EmpresaTodas las consultas están automáticamente filtradas por la empresa del usuario autenticado (id_empresa). No es posible acceder a compras de otras empresas.
Unicidad de DocumentosLa combinación de proveedor + tipo_doc + serie + número debe ser única dentro de cada empresa. Esto evita registrar duplicados del mismo documento.
Compras a CréditoLas compras con id_tipo_pago = 2 requieren:
  • Fecha de vencimiento
  • Array de cuotas con montos y fechas
  • Las cuotas se crean con estado “1” (Pendiente)

Modelos Relacionados

Compra

{
  "id_compra": integer,
  "id_tido": integer,          // ID tipo de documento
  "serie": string,             // Máx. 4 caracteres
  "numero": string,            // Máx. 8 dígitos
  "id_proveedor": integer,
  "proveedor_id": integer,     // Redundante para relación
  "fecha_emision": date,
  "fecha_vencimiento": date|null,
  "id_tipo_pago": integer,     // 1=Contado, 2=Crédito
  "moneda": string,            // PEN o USD
  "subtotal": decimal(10,2),
  "igv": decimal(10,2),
  "total": decimal(10,2),
  "direccion": string,
  "observaciones": text,
  "id_empresa": integer,
  "id_usuario": integer,
  "sucursal": integer,
  "estado": string,            // 1=Activo, 0=Anulado
  "created_at": timestamp,
  "updated_at": timestamp
}

ProductoCompra (Detalle)

{
  "id_compra": integer,
  "id_producto": integer,
  "cantidad": decimal(10,2),
  "precio": decimal(10,2),     // Precio unitario
  "costo": decimal(10,2),      // Costo unitario
  "subtotal": decimal(10,2)    // cantidad × costo
}

DiaCompra (Cuota)

{
  "id_compra": integer,
  "monto": decimal(10,2),
  "fecha": date,
  "estado": string             // 1=Pendiente, 0=Pagada
}

MovimientoStock

{
  "id_producto": integer,
  "tipo_movimiento": string,   // "entrada" o "salida"
  "cantidad": decimal(10,2),
  "stock_anterior": decimal(10,2),
  "stock_nuevo": decimal(10,2),
  "tipo_documento": string,    // "compra" o "anulacion_compra"
  "id_documento": integer,     // id_compra
  "documento_referencia": string,  // "F001-00000123"
  "motivo": string,
  "id_almacen": integer,
  "id_empresa": integer,
  "id_usuario": integer,
  "fecha_movimiento": timestamp
}

Ejemplos de Integración

JavaScript/React

// Listar compras
const fetchCompras = async () => {
  const response = await fetch('https://tu-dominio.com/api/compras', {
    method: 'GET',
    headers: {
      'Authorization': `Bearer ${localStorage.getItem('auth_token')}`,
      'Accept': 'application/json'
    }
  });
  
  const data = await response.json();
  return data.data;
};

// Crear compra
const crearCompra = async (compraData) => {
  const response = await fetch('https://tu-dominio.com/api/compras', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${localStorage.getItem('auth_token')}`,
      'Content-Type': 'application/json',
      'Accept': 'application/json'
    },
    body: JSON.stringify(compraData)
  });
  
  const result = await response.json();
  
  if (!result.success) {
    throw new Error(result.message);
  }
  
  return result.data;
};

// Anular compra
const anularCompra = async (idCompra) => {
  const response = await fetch(
    `https://tu-dominio.com/api/compras/${idCompra}/anular`,
    {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${localStorage.getItem('auth_token')}`,
        'Accept': 'application/json'
      }
    }
  );
  
  const result = await response.json();
  
  if (!result.success) {
    throw new Error(result.message);
  }
  
  return result;
};

PHP

<?php

$baseUrl = 'https://tu-dominio.com/api';
$token = 'tu_token_aqui';

// Crear compra
$compraData = [
    'id_proveedor' => 15,
    'tipo_doc' => 1,
    'serie' => 'F001',
    'numero' => '00000124',
    'fecha_emision' => '2026-03-04',
    'id_tipo_pago' => 1,
    'moneda' => 'PEN',
    'productos' => [
        [
            'id_producto' => 10,
            'cantidad' => 50,
            'costo' => 25.50
        ]
    ]
];

$ch = curl_init("{$baseUrl}/compras");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($compraData));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    "Authorization: Bearer {$token}",
    'Content-Type: application/json',
    'Accept: application/json'
]);

$response = curl_exec($ch);
$result = json_decode($response, true);

if ($result['success']) {
    echo "Compra creada: {$result['data']['documento']}\n";
} else {
    echo "Error: {$result['message']}\n";
}

curl_close($ch);

Python

import requests
import json

BASE_URL = 'https://tu-dominio.com/api'
TOKEN = 'tu_token_aqui'

headers = {
    'Authorization': f'Bearer {TOKEN}',
    'Accept': 'application/json',
    'Content-Type': 'application/json'
}

# Listar compras
def listar_compras():
    response = requests.get(f'{BASE_URL}/compras', headers=headers)
    response.raise_for_status()
    return response.json()['data']

# Crear compra
def crear_compra(compra_data):
    response = requests.post(
        f'{BASE_URL}/compras',
        headers=headers,
        json=compra_data
    )
    response.raise_for_status()
    return response.json()

# Ver detalle
def ver_compra(id_compra):
    response = requests.get(
        f'{BASE_URL}/compras/{id_compra}',
        headers=headers
    )
    response.raise_for_status()
    return response.json()['data']

# Anular
def anular_compra(id_compra):
    response = requests.post(
        f'{BASE_URL}/compras/{id_compra}/anular',
        headers=headers
    )
    response.raise_for_status()
    return response.json()

# Ejemplo de uso
if __name__ == '__main__':
    # Crear nueva compra
    nueva_compra = {
        'id_proveedor': 15,
        'tipo_doc': 1,
        'serie': 'F001',
        'numero': '00000125',
        'fecha_emision': '2026-03-04',
        'id_tipo_pago': 1,
        'moneda': 'PEN',
        'productos': [
            {
                'id_producto': 10,
                'cantidad': 50,
                'costo': 25.50
            }
        ]
    }
    
    resultado = crear_compra(nueva_compra)
    print(f"Compra creada: {resultado['data']['documento']}")

Preguntas Frecuentes

No, actualmente no está implementada la funcionalidad de actualización de compras. Esto es por diseño para mantener la integridad del inventario y la trazabilidad de movimientos de stock. Si necesitas corregir una compra, debes anularla y crear una nueva.
Al anular una compra, el sistema automáticamente:
  1. Reduce el stock de cada producto por la cantidad comprada
  2. Genera movimientos de stock de tipo “salida” con motivo “Anulación de compra”
  3. El stock nunca será menor a 0 (se usa max(0, stock - cantidad))
Para compras a crédito (id_tipo_pago = 2):
  1. Debes proporcionar fecha_vencimiento
  2. Debes incluir un array de cuotas con montos y fechas
  3. Las cuotas se crean con estado “1” (Pendiente)
  4. La suma de cuotas debería coincidir con el total (no se valida automáticamente)
Sí, puedes incluir el campo empresas_ids con un array de IDs de empresas. Esto es útil para compras compartidas o distribución de costos entre sucursales.
No, en las compras el IGV normalmente es 0 ya que se registra el costo neto. El total se calcula como subtotal + igv. Si necesitas registrar IGV, debes incluirlo en el costo unitario de los productos.
El sistema valida automáticamente que no exista otra compra con la misma combinación de:
  • Proveedor
  • Tipo de documento
  • Serie
  • Número
Si intentas crear un duplicado, recibirás un error 400.

Build docs developers (and LLMs) love