Overview
The Facturas API provides comprehensive invoice management functionality including invoice creation, detail management, approval, and cancellation workflows. All endpoints require JWT authentication.
Invoices follow a state machine pattern: PEN (Pending) → APR (Approved) or ANU (Canceled). Only pending invoices can be modified.
Authentication
Authorization: Bearer < your_jwt_toke n >
The JWT token must contain usuario and password fields for database connection.
Invoice Lifecycle
Invoices in the system follow this workflow:
Create Invoice - Creates invoice in PEN (Pending) state with zero totals
Add Details - Add products to the invoice detail
Modify Details - Update quantities or remove products (only in PEN state)
Approve Invoice - Locks invoice, updates inventory, creates transactions (irreversible)
Cancel Invoice - Cancels approved invoice, restores inventory (only APR invoices)
Once an invoice is approved, its details cannot be modified. You can only cancel the entire invoice to restore inventory.
List All Facturas
Retrieves a paginated list of all invoices with customer information. Supports search by invoice code or customer name.
Query Parameters
Page number for pagination
Number of records per page
Search term for invoice code or customer name (case-insensitive partial match)
Example Request
curl -X GET "https://api.example.com/api/pos/factura?page=1&limit=10&search=juan" \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
Response
Array of invoice objects Show Invoice object properties
Invoice status (PEN, APR, ANU)
Total amount including IVA
Total number of records matching search criteria
{
"data" : [
{
"fac_codigo" : "FAC0001" ,
"fac_fecha" : "2024-03-04T10:30:00Z" ,
"fac_estado" : "PEN" ,
"fac_total" : 115.50 ,
"cli_codigo" : "CLI001" ,
"cli_nombre" : "Juan Pérez"
},
{
"fac_codigo" : "FAC0002" ,
"fac_fecha" : "2024-03-04T11:15:00Z" ,
"fac_estado" : "APR" ,
"fac_total" : 230.00 ,
"cli_codigo" : "CLI002" ,
"cli_nombre" : "María González"
}
],
"total" : 45 ,
"page" : 1 ,
"totalPages" : 5
}
{
"message" : "Error al obtener facturas"
}
Get Factura by Code
/api/pos/factura/:facCodigo
Retrieves complete invoice information including header and detail lines.
Path Parameters
Example Request
curl -X GET https://api.example.com/api/pos/factura/FAC0001 \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
Response
Invoice header information Invoice status (PEN, APR, ANU)
Total amount including tax
Array of invoice detail lines Show detalle item properties
Detail line code (used for updates/deletes)
Line subtotal (quantity × price)
{
"factura" : {
"fac_codigo" : "FAC0001" ,
"fac_fecha" : "2024-03-04T10:30:00Z" ,
"fac_estado" : "PEN" ,
"fac_subtotal" : 100.00 ,
"fac_iva" : 15.00 ,
"fac_total" : 115.00 ,
"fac_descripcion" : "Venta de productos" ,
"cli_codigo" : "CLI001" ,
"cli_nombre" : "Juan Pérez" ,
"cli_ruc_ced" : "1234567890001"
},
"detalle" : [
{
"pxfa_codigo" : "PXF0001" ,
"prd_codigo" : "PRD001" ,
"pxfa_cantidad" : 2 ,
"pxfa_subtotal" : 50.00 ,
"prd_precio_venta" : 25.00
},
{
"pxfa_codigo" : "PXF0002" ,
"prd_codigo" : "PRD002" ,
"pxfa_cantidad" : 1 ,
"pxfa_subtotal" : 50.00 ,
"prd_precio_venta" : 50.00
}
]
}
{
"message" : "Factura no encontrada"
}
{
"message" : "Error al obtener factura"
}
Create Factura
Creates a new invoice in PEN (Pending) status with initial totals set to zero. The invoice code is auto-generated using the next_fac_codigo() database function.
Request Body
Customer code (must exist in cliente table)
Invoice description or notes
Example Request
curl -X POST https://api.example.com/api/pos/factura \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"cli_codigo": "CLI001",
"fac_descripcion": "Venta de productos varios"
}'
Response
{
"fac_codigo" : "FAC0001"
}
{
"message" : "Error al crear factura"
}
Approve Factura
/api/pos/factura/:facCodigo/aprobar
Approves a pending invoice using the stored procedure aprobar_factura. This operation:
Validates invoice is in PEN status
Creates a transaction record with auto-generated reference
Creates kardex entries for each product
Decreases product stock quantities
Changes invoice status to APR
This operation is irreversible and cannot be undone. It will fail if any product has insufficient stock.
Path Parameters
Example Request
curl -X PUT https://api.example.com/api/pos/factura/FAC0001/aprobar \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
Response
{
"message" : "Factura aprobada correctamente" ,
"factura" : {
"fac_codigo" : "FAC0001" ,
"fac_fecha" : "2024-03-04T10:30:00Z" ,
"fac_estado" : "APR" ,
"fac_subtotal" : 100.00 ,
"fac_iva" : 15.00 ,
"fac_total" : 115.00 ,
"fac_descripcion" : "Venta de productos" ,
"cli_codigo" : "CLI001" ,
"cli_nombre" : "Juan Pérez" ,
"cli_ruc_ced" : "1234567890001"
},
"detalle" : [
{
"pxfa_codigo" : "PXF0001" ,
"prd_codigo" : "PRD001" ,
"pxfa_cantidad" : 2 ,
"pxfa_subtotal" : 50.00 ,
"prd_precio_venta" : 25.00
}
]
}
400 Bad Request - Not Pending
{
"message" : "Esta factura ya no está pendiente y no puede ser aprobada."
}
400 Bad Request - Insufficient Stock
{
"message" : "Stock insuficiente para producto PRD001"
}
400 Bad Request - Not Found
{
"message" : "La factura solicitada no existe."
}
Cancel Factura
/api/pos/factura/:facCodigo/anular
Cancels an approved invoice using the stored procedure anular_factura. This operation reverses the approval process by restoring inventory and changing status to ANU.
Only approved (APR) invoices can be canceled. Pending invoices should be deleted instead.
Path Parameters
Example Request
curl -X PUT https://api.example.com/api/pos/factura/FAC0001/anular \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
Response
{
"message" : "Factura anulada correctamente"
}
{
"message" : "Solo se pueden anular facturas aprobadas"
}
400 Bad Request - Not Found
{
"message" : "La factura solicitada no existe."
}
Delete Factura
/api/pos/factura/:facCodigo
Physically deletes an invoice from the database. Only pending (PEN) invoices can be deleted. Approved or canceled invoices cannot be deleted.
This is a physical delete operation and cannot be undone. All associated detail lines will be deleted due to foreign key cascade.
Path Parameters
Example Request
curl -X DELETE https://api.example.com/api/pos/factura/FAC0001 \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
Response
{
"message" : "Factura eliminada correctamente"
}
{
"message" : "No se pudo eliminar la factura (puede que no exista o no esté en estado PENDIENTE)"
}
{
"message" : "Error interno al eliminar factura"
}
Invoice Detail Operations
Add Product to Invoice
Adds a product to an invoice’s detail lines. The invoice must be in PEN status. After adding, totals are automatically recalculated.
Request Body
Product code (must exist in producto table)
Example Request
curl -X POST https://api.example.com/api/pos/factura/detalle \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"fac_codigo": "FAC0001",
"prd_codigo": "PRD001",
"pxfa_cantidad": 2
}'
Response
{
"message" : "Detalle agregado correctamente" ,
"factura" : {
"fac_codigo" : "FAC0001" ,
"fac_fecha" : "2024-03-04T10:30:00Z" ,
"fac_estado" : "PEN" ,
"fac_subtotal" : 50.00 ,
"fac_iva" : 7.50 ,
"fac_total" : 57.50 ,
"fac_descripcion" : "Venta de productos" ,
"cli_codigo" : "CLI001" ,
"cli_nombre" : "Juan Pérez" ,
"cli_ruc_ced" : "1234567890001"
},
"detalle" : [
{
"pxfa_codigo" : "PXF0001" ,
"prd_codigo" : "PRD001" ,
"pxfa_cantidad" : 2 ,
"pxfa_subtotal" : 50.00 ,
"prd_precio_venta" : 25.00
}
]
}
400 Bad Request - Duplicate
{
"message" : "Producto ya en lista"
}
{
"message" : "Error adding detail"
}
Update Invoice Detail
/api/pos/factura/detalle/:pxfaCodigo
Updates the quantity of an existing detail line. The invoice must be in PEN status. Totals are recalculated automatically within a transaction.
Path Parameters
Detail line code (pxfa_codigo)
Request Body
Example Request
curl -X PUT https://api.example.com/api/pos/factura/detalle/PXF0001 \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"pxfa_cantidad": 5
}'
Response
{
"message" : "Detalle actualizado correctamente" ,
"factura" : {
"fac_codigo" : "FAC0001" ,
"fac_fecha" : "2024-03-04T10:30:00Z" ,
"fac_estado" : "PEN" ,
"fac_subtotal" : 125.00 ,
"fac_iva" : 18.75 ,
"fac_total" : 143.75 ,
"fac_descripcion" : "Venta de productos" ,
"cli_codigo" : "CLI001" ,
"cli_nombre" : "Juan Pérez" ,
"cli_ruc_ced" : "1234567890001"
},
"detalle" : [
{
"pxfa_codigo" : "PXF0001" ,
"prd_codigo" : "PRD001" ,
"pxfa_cantidad" : 5 ,
"pxfa_subtotal" : 125.00 ,
"prd_precio_venta" : 25.00
}
]
}
{
"message" : "No se puede modificar el detalle (factura no PEN o detalle no existe)"
}
Delete Invoice Detail
/api/pos/factura/detalle/:pxfaCodigo
Removes a product line from an invoice. The invoice must be in PEN status. Totals are recalculated automatically within a transaction.
Path Parameters
Detail line code (pxfa_codigo)
Example Request
curl -X DELETE https://api.example.com/api/pos/factura/detalle/PXF0001 \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
Response
{
"message" : "Detalle eliminado correctamente" ,
"factura" : {
"fac_codigo" : "FAC0001" ,
"fac_fecha" : "2024-03-04T10:30:00Z" ,
"fac_estado" : "PEN" ,
"fac_subtotal" : 0.00 ,
"fac_iva" : 0.00 ,
"fac_total" : 0.00 ,
"fac_descripcion" : "Venta de productos" ,
"cli_codigo" : "CLI001" ,
"cli_nombre" : "Juan Pérez" ,
"cli_ruc_ced" : "1234567890001"
},
"detalle" : []
}
{
"message" : "No se puede eliminar el detalle (factura no PEN o no existe)"
}
{
"message" : "Error interno al eliminar detalle"
}
Invoice Status Values
PEN - Pending : Invoice can be edited, details can be added/modified/removed
APR - Approved : Invoice is locked, inventory has been updated, cannot be modified
ANU - Canceled : Invoice has been voided, inventory has been restored
Tax Calculation
The system applies a 15% IVA (Value Added Tax) to all invoices:
fac_subtotal = SUM ( pxfa_subtotal ) // Sum of all detail lines
fac_iva = fac_subtotal * 0.15 // 15% tax
fac_total = fac_subtotal + fac_iva // Final total
This calculation is performed automatically by the recalcTotalesFactura function whenever detail lines are added, updated, or removed.
Transaction Management
Approval Process
When an invoice is approved via the aprobar_factura stored procedure:
Transaction Record : Creates entry in transaccion table
trn_cod: Auto-generated using next_trn_cod()
trn_tipo: Set based on transaction type
trn_descripcion: “Factura ”
Kardex Entries : Creates inventory movement records
One entry per product in invoice detail
Links to transaction and invoice
Records quantity decrease
Stock Update : Decreases prd_stock for each product
Validates sufficient stock before update
Fails entire transaction if any product has insufficient stock
Status Change : Updates fac_estado from PEN to APR
Cancellation Process
The anular_factura stored procedure reverses the approval:
Validates invoice is in APR status
Restores product stock quantities
Creates reversal kardex entries
Changes status to ANU
Both approval and cancellation operations are atomic transactions. If any step fails, all changes are rolled back.
Error Handling
Common Error Scenarios
Error Status Description Duplicate product 400 Product already exists in invoice detail (code 23505) Not pending 400 Operation requires invoice in PEN status Insufficient stock 400 Product doesn’t have enough stock for approval Not found 404 Invoice or detail line doesn’t exist Invalid state 400 Operation not allowed in current invoice state
Error Response Examples
Duplicate Product (23505)
{
"message" : "Producto ya en lista"
}
{
"message" : "Stock insuficiente para producto PRD001"
}
{
"message" : "Esta factura ya no está pendiente y no puede ser aprobada."
}
Implementation Details
Source Code Reference
Routes : src/routes/pos.factura.routes.js
Controller : src/controllers/pos.factura.controller.js
Model : src/models/factura.model.js
Stored Procedures :
aprobar_factura(p_fac_codigo) - Handles approval logic
anular_factura(p_fac_codigo) - Handles cancellation logic
next_fac_codigo() - Generates invoice codes
next_pxfa_codigo() - Generates detail line codes
next_trn_cod() - Generates transaction codes
Database Connection
Uses specialized connection pool for factura operations:
const getCredsFromJWT = ( req ) => {
const usuario = req . user ?. usuario || req . user ?. user ;
const password = req . user ?. password ;
return { usuario , password };
};
pool = getFacturaConnectionWithCredentials ( usuario , password );
Transaction Safety
Detail modification operations (update/delete) use explicit transaction control:
client = await pool . connect ();
await client . query ( 'BEGIN' );
// ... operations ...
await client . query ( 'COMMIT' );
// ... or on error ...
await client . query ( 'ROLLBACK' );
Search Implementation
List endpoint supports case-insensitive search across:
Invoice code (fac_codigo)
Customer name (cli_nombre)
WHERE ( LOWER ( f . fac_codigo ) LIKE '%search%'
OR LOWER ( c . cli_nombre ) LIKE '%search%' )
Code Generation Functions
The system uses database functions for auto-incrementing codes:
next_fac_codigo(): Generates FAC#### format codes
next_pxfa_codigo(): Generates PXF#### format codes
next_trn_cod(): Generates TRN#### format codes
next_krd_prd_codigo(): Generates KRD#### format codes
All functions follow the pattern:
'PREFIX' || LPAD(( MAX (sequence_number) + 1 ):: text , 4 , '0' )