Overview
The Sales API manages sale transactions with automatic stock updates, IVA (tax) calculations, and support for both quick sales and registered client sales.
Venta Model (Sale)
Unique sale identifier (auto-generated)
Foreign key to admin user (owner)
Quick sale customer name (optional, max 100 characters)
Foreign key to registered Cliente (optional, takes precedence over cliente)
Payment method: Efectivo or Transferencia (default: Efectivo)
Bank name (required if forma_pago='Transferencia', max 50 characters)
Transaction reference number (optional, max 100 characters)
Sale timestamp (default: current time)
Foreign key to User who created the sale
Total amount including IVA (12 digits, 2 decimals, auto-calculated)
DetalleVenta Model (Sale Detail)
Foreign key to parent Venta
Foreign key to Producto (PROTECT constraint - cannot delete products in sales)
Quantity sold (positive integer, default: 1)
Base price before IVA (10 digits, 2 decimals)
IVA amount per unit (auto-calculated: 19% of precio_unitario)
Final unit price with IVA (auto-calculated: precio_unitario + monto_iva)
Line total (auto-calculated: cantidad × precio_unitario_final)
Product cost at time of sale (copied from producto.precio_compra)
Income type: domicilio, servicio, mano_obra, or otro
Description (max 200 characters, optional)
Amount (10 digits, 2 decimals)
Income timestamp (default: current time)
List Sales
curl -X GET http://localhost:8000/ventas/ \
-H "Cookie: sessionid=<your-session>"
Response
Returns rendered HTML with list of sales for the authenticated user.
Create Sale
Requires authentication. Both admin and vendedor can create sales.
curl -X POST http://localhost:8000/ventas/nueva/ \
-H "Cookie: sessionid=<session>; csrftoken=<token>" \
-H "X-CSRFToken: <token>" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "cliente=Juan Perez" \
-d "forma_pago=Efectivo"
Request Parameters
Customer name for quick sale (optional if cliente_fk provided)
Registered client ID (optional, takes precedence)
Payment method: Efectivo or Transferencia
Bank name (required if forma_pago=‘Transferencia’)
Adding Products to Sale
Products are typically added via the frontend interface or separate AJAX calls. The sale detail creation:
- Decrements product stock automatically
- Calculates IVA (19%)
- Calculates subtotals
- Updates total_venta on parent Venta
Response
Success: Redirects to sales list with flash message
Update Sale
curl -X POST http://localhost:8000/ventas/editar/<venta_id>/ \
-H "Cookie: sessionid=<session>; csrftoken=<token>" \
-H "X-CSRFToken: <token>" \
-d "cliente=Juan Perez (Updated)" \
-d "forma_pago=Transferencia" \
-d "banco=Banco de Bogota" \
-d "referencia=REF123456"
URL Parameters
Delete Sale
Deleting a sale restores product stock for all detail lines.
curl -X POST http://localhost:8000/ventas/eliminar/<venta_id>/ \
-H "Cookie: sessionid=<session>; csrftoken=<token>" \
-H "X-CSRFToken: <token>"
URL Parameters
Response
Success: Stock restored, sale deleted, redirects to sales list
AJAX/API Endpoints
Search Client
curl -X POST http://localhost:8000/ventas/api/buscar-cliente/ \
-H "Cookie: sessionid=<session>; csrftoken=<token>" \
-H "Content-Type: application/json" \
-H "X-CSRFToken: <token>" \
-d '{"nombre": "Juan"}'
Client name to search (partial match)
Response
{
"success": true,
"clientes": [
{
"id": 1,
"nombre": "Juan Perez",
"telefono": "3001234567",
"documento": "123456789"
}
]
}
Search Product by Barcode
curl -X POST http://localhost:8000/ventas/api/buscar-producto/ \
-H "Cookie: sessionid=<session>; csrftoken=<token>" \
-H "Content-Type: application/json" \
-H "X-CSRFToken: <token>" \
-d '{"codigo_barras": "1234567890"}'
Product barcode to search
Response
{
"success": true,
"producto": {
"id": 5,
"nombre": "Laptop Dell XPS 15",
"precio": "1500.00",
"stock": 10,
"codigo_barras": "1234567890"
}
}
Product Not Found:
{
"success": false,
"error": "Producto no encontrado"
}
Get Sale Detail (JSON)
curl -X GET http://localhost:8000/ventas/api/datos-json/<venta_id>/ \
-H "Cookie: sessionid=<session>"
Response
{
"id": 15,
"cliente": "Juan Perez",
"forma_pago": "Efectivo",
"total_venta": "3570.00",
"fecha_venta": "2024-03-15T10:30:00-05:00",
"detalles": [
{
"producto": {
"id": 5,
"nombre": "Laptop Dell XPS 15"
},
"cantidad": 2,
"precio_unitario": "1500.00",
"monto_iva": "285.00",
"precio_unitario_final": "1785.00",
"subtotal": "3570.00"
}
]
}
Search Sales
curl -X GET "http://localhost:8000/ventas/api/buscar-ventas/?q=Juan" \
-H "Cookie: sessionid=<session>"
Search query (searches cliente name)
Get Products by Sale
curl -X GET http://localhost:8000/ventas/<venta_id>/productos/ \
-H "Cookie: sessionid=<session>"
Response
Returns JSON array of products in the sale.
curl -X POST http://localhost:8000/ventas/api/ingreso-extra/ \
-H "Cookie: sessionid=<session>; csrftoken=<token>" \
-H "Content-Type: application/json" \
-H "X-CSRFToken: <token>" \
-d '{
"tipo": "domicilio",
"descripcion": "Delivery fee",
"monto": "5000.00"
}'
Income type: domicilio, servicio, mano_obra, or otro
Description (max 200 characters)
Response
{
"success": true,
"ingreso": {
"id": 10,
"tipo": "domicilio",
"descripcion": "Delivery fee",
"monto": "5000.00",
"fecha": "2024-03-15T14:20:00-05:00"
}
}
curl -X GET http://localhost:8000/ventas/api/ingreso-extra/<ingreso_id>/ \
-H "Cookie: sessionid=<session>"
curl -X POST http://localhost:8000/ventas/ingreso-extra/eliminar/<ingreso_id>/ \
-H "Cookie: sessionid=<session>; csrftoken=<token>" \
-H "X-CSRFToken: <token>"
Generate Sale Invoice (PDF)
curl -X GET http://localhost:8000/ventas/factura/<venta_id>/ \
-H "Cookie: sessionid=<session>" \
--output factura.pdf
Response
Content-Type: application/pdf
Downloads PDF invoice with sale details.
Business Logic
Automatic Stock Management
When DetalleVenta is saved:
# Stock decremented
producto.stock -= cantidad
producto.save()
When DetalleVenta is deleted:
# Stock restored
producto.stock += cantidad
producto.save()
When DetalleVenta quantity updated:
# Adjust stock by difference
diferencia = new_cantidad - old_cantidad
producto.stock -= diferencia
producto.save()
IVA Calculation (19%)
Constant defined: IVA_RATE = Decimal('0.19')
On save:
base = Decimal(self.precio_unitario)
self.monto_iva = (base * IVA_RATE).quantize(Decimal('0.01'))
self.precio_unitario_final = (base + self.monto_iva).quantize(Decimal('0.01'))
self.subtotal = self.cantidad * self.precio_unitario_final
Total Venta Update
After each detail save/delete:
venta.actualizar_total() # Recalculates sum of all detail subtotals
Cost Tracking
On first save, costo_compra copied from product:
if not self.pk or self.costo_compra == 0:
self.costo_compra = self.producto.precio_compra
This preserves historical cost for profit analysis.
Example Workflow
# 1. Search for product by barcode
curl -X POST http://localhost:8000/ventas/api/buscar-producto/ \
-H "Content-Type: application/json" \
-H "X-CSRFToken: <token>" \
-d '{"codigo_barras": "APP-IPH14-001"}'
# Response: {"success": true, "producto": {"id": 5, "precio": "999.00", "stock": 8}}
# 2. Create sale (typically done via web form)
curl -X POST http://localhost:8000/ventas/nueva/ \
-d "cliente=Maria Lopez" \
-d "forma_pago=Transferencia" \
-d "banco=Bancolombia" \
-d "referencia=TRX789"
# 3. Add product to sale (via detail creation)
# This happens in the UI - detail is created with:
# - producto_id=5
# - cantidad=2
# - precio_unitario=999.00
# Auto-calculates: monto_iva=189.81, precio_unitario_final=1188.81, subtotal=2377.62
# Stock updated: 8 - 2 = 6
# 4. Get sale detail as JSON
curl http://localhost:8000/ventas/api/datos-json/15/
# 5. Download invoice
curl http://localhost:8000/ventas/factura/15/ --output invoice.pdf
- Products API - Stock management
- Clients API - Registered customer sales
- See source:
applications/ventas/views.py and applications/ventas/models.py