Skip to main content
POST
/
api
/
ventas
/
{id}
/
anular
Anular Venta
curl --request POST \
  --url https://api.example.com/api/ventas/{id}/anular \
  --header 'Content-Type: application/json' \
  --data '
{
  "motivo_anulacion": "<string>"
}
'
{
  "success": true,
  "message": "<string>"
}

Endpoint

POST /api/ventas/{id}/anular

Autenticación

Requiere token de autenticación Bearer:
Authorization: Bearer {token}

Permisos Requeridos

  • ventas.delete

Descripción

Anula una venta existente que esté en estado activo (estado = '1'). Realiza las siguientes operaciones:
  1. Cambia el estado de la venta a anulada (estado = '2')
  2. Retorna el stock al almacén si afecta_stock = true
  3. Registra movimientos de entrada en movimientos_stock
  4. Registra la anulación en tabla ventas_anuladas con motivo y datos del usuario

Parámetros de URL

id
integer
required
ID de la venta a anular (id_venta)

Parámetros de Solicitud

motivo_anulacion
string
required
Motivo o razón de la anulación (máximo 500 caracteres).Ejemplos:
  • “Error en el monto facturado”
  • “Cliente solicitó anulación”
  • “Producto incorrecto registrado”
  • “Duplicado de factura”

Validaciones

Validaciones Automáticas

  1. Venta debe existir en la empresa del usuario autenticado
  2. Estado debe ser activo (estado = '1')
  3. Motivo es obligatorio y no puede exceder 500 caracteres

Restricciones de Negocio

Si la venta fue enviada a SUNAT (estado_sunat = '1'), la anulación local NO invalida el comprobante ante SUNAT.Para anular en SUNAT debe:
  • Facturas/Notas: Enviar Comunicación de Baja
  • Boletas: Enviar Resumen Diario de Baja
Ver: POST /api/comunicacion-baja y POST /api/resumen-diario/anular

Lógica de Retorno de Stock

Si afecta_stock = true:

Para cada producto en productosVentas:
  1. Incrementa productos.cantidad con la cantidad vendida
  2. Crea registro en movimientos_stock:
    • tipo_movimiento: “entrada”
    • tipo_documento: “anulacion_venta”
    • cantidad: cantidad retornada
    • stock_anterior: stock antes de retornar
    • stock_nuevo: stock después de retornar
    • motivo: “Anulación de venta”
    • documento_referencia: serie-número de la venta

Si afecta_stock = false:

No se modifica stock. Útil para:
  • Notas de venta (id_tido = 6)
  • Ventas de servicios
  • Productos libres

Registro en ventas_anuladas

Se inserta un registro con:
[
  'id_venta' => ID de la venta,
  'id_usuario' => ID del usuario que anula,
  'motivo_anulacion' => Motivo proporcionado,
  'fecha_anulacion' => now(),
  'tipo_documento' => Código SUNAT (01, 03, etc.),
  'serie' => Serie de la venta,
  'numero' => Número de la venta,
  'total_anulado' => Total de la venta,
  'estado_comunicacion_baja' => '0' (pendiente),
  'created_at' => now(),
  'updated_at' => now()
]

Respuesta Exitosa (200)

success
boolean
true si la anulación fue exitosa
message
string
Mensaje de confirmación. Indica si el stock fue retornado:
  • “Venta anulada exitosamente (stock retornado)”
  • “Venta anulada exitosamente”

Ejemplo de Respuesta Exitosa

Con retorno de stock:

{
  "success": true,
  "message": "Venta anulada exitosamente (stock retornado)"
}

Sin retorno de stock:

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

Errores

Error de Validación (422)

{
  "success": false,
  "message": "Error de validación",
  "errors": {
    "motivo_anulacion": ["El campo motivo anulacion es obligatorio."]
  }
}

Venta No Encontrada (404)

{
  "success": false,
  "message": "Error al anular la venta"
}
Ocurre cuando:
  • La venta no existe
  • La venta no pertenece a la empresa del usuario
  • La venta ya está anulada (estado != '1')

Error del Servidor (500)

{
  "success": false,
  "message": "Error al anular la venta"
}

Ejemplo de Solicitud

curl -X POST "https://facturacion.santodomingo.pe/api/ventas/126/anular" \
  -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGc..." \
  -H "Content-Type: application/json" \
  -d '{
    "motivo_anulacion": "Cliente solicitó anulación por error en descripción de producto"
  }'

Ejemplo de Solicitud con curl (Multiline)

curl -X POST \
  https://facturacion.santodomingo.pe/api/ventas/126/anular \
  -H 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGc...' \
  -H 'Content-Type: application/json' \
  -d '{
    "motivo_anulacion": "Duplicado de factura emitida por error"
  }'

Flujo Completo de Anulación ante SUNAT

Para Facturas (id_tido = 2):

  1. Anular localmente: POST /api/ventas/{id}/anular
  2. Enviar Comunicación de Baja: POST /api/comunicacion-baja
    {
      "documentos": [
        {
          "tipo_doc": "01",
          "serie": "F001",
          "correlativo": "349",
          "motivo": "ERROR EN EMISION"
        }
      ]
    }
    
  3. Consultar ticket: POST /api/comunicacion-baja/consultar

Para Boletas (id_tido = 1):

  1. Anular localmente: POST /api/ventas/{id}/anular
  2. Enviar Resumen Diario de Baja: POST /api/resumen-diario/anular
    {
      "fecha_resumen": "2024-03-15",
      "ventas_ids": [126, 127, 128]
    }
    
  3. Consultar ticket: POST /api/resumen-diario/consultar

Transaccionalidad

Toda la operación se ejecuta dentro de una transacción de base de datos (DB::transaction). Si cualquier paso falla, todos los cambios se revierten:
  • Cambio de estado de la venta
  • Retorno de stock
  • Registro de movimientos
  • Inserción en ventas_anuladas

Auditoría

La anulación registra:
  • Usuario: ID del usuario que ejecuta la anulación
  • Fecha/hora: Timestamp exacto de la anulación
  • Motivo: Razón proporcionada
  • Detalle: Serie, número, tipo de documento, monto total
Esta información es útil para:
  • Reportes de auditoría
  • Conciliación contable
  • Trazabilidad de operaciones

Estados de Venta

EstadoDescripciónPuede Anularse
1Activa✅ Sí
2Anulada❌ No
3Vendida (nota → venta)❌ No

Código de Referencia

Ver implementación en app/Http/Controllers/VentasController.php:410 método anular()

Endpoints Relacionados

Build docs developers (and LLMs) love