Skip to main content

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_token>
The JWT token must contain usuario and password fields for database connection.

Invoice Lifecycle

Invoices in the system follow this workflow:
  1. Create Invoice - Creates invoice in PEN (Pending) state with zero totals
  2. Add Details - Add products to the invoice detail
  3. Modify Details - Update quantities or remove products (only in PEN state)
  4. Approve Invoice - Locks invoice, updates inventory, creates transactions (irreversible)
  5. 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.

Invoice Header Operations

List All Facturas

GET
endpoint
/api/pos/factura
Retrieves a paginated list of all invoices with customer information. Supports search by invoice code or customer name.

Query Parameters

page
integer
default:"1"
Page number for pagination
limit
integer
default:"10"
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

data
array
Array of invoice objects
total
integer
Total number of records matching search criteria
page
integer
Current page number
totalPages
integer
Total number of pages
200 Success
{
  "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
}
500 Server Error
{
  "message": "Error al obtener facturas"
}

Get Factura by Code

GET
endpoint
/api/pos/factura/:facCodigo
Retrieves complete invoice information including header and detail lines.

Path Parameters

facCodigo
string
required
Invoice code

Example Request

curl -X GET https://api.example.com/api/pos/factura/FAC0001 \
  -H "Authorization: Bearer YOUR_JWT_TOKEN"

Response

factura
object
Invoice header information
detalle
array
Array of invoice detail lines
200 Success
{
  "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
    }
  ]
}
404 Not Found
{
  "message": "Factura no encontrada"
}
500 Server Error
{
  "message": "Error al obtener factura"
}

Create Factura

POST
endpoint
/api/pos/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

cli_codigo
string
required
Customer code (must exist in cliente table)
fac_descripcion
string
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

201 Success
{
  "fac_codigo": "FAC0001"
}
500 Server Error
{
  "message": "Error al crear factura"
}

Approve Factura

PUT
endpoint
/api/pos/factura/:facCodigo/aprobar
Approves a pending invoice using the stored procedure aprobar_factura. This operation:
  1. Validates invoice is in PEN status
  2. Creates a transaction record with auto-generated reference
  3. Creates kardex entries for each product
  4. Decreases product stock quantities
  5. Changes invoice status to APR
This operation is irreversible and cannot be undone. It will fail if any product has insufficient stock.

Path Parameters

facCodigo
string
required
Invoice code to approve

Example Request

curl -X PUT https://api.example.com/api/pos/factura/FAC0001/aprobar \
  -H "Authorization: Bearer YOUR_JWT_TOKEN"

Response

200 Success
{
  "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

PUT
endpoint
/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

facCodigo
string
required
Invoice code to cancel

Example Request

curl -X PUT https://api.example.com/api/pos/factura/FAC0001/anular \
  -H "Authorization: Bearer YOUR_JWT_TOKEN"

Response

200 Success
{
  "message": "Factura anulada correctamente"
}
400 Bad Request
{
  "message": "Solo se pueden anular facturas aprobadas"
}
400 Bad Request - Not Found
{
  "message": "La factura solicitada no existe."
}

Delete Factura

DELETE
endpoint
/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

facCodigo
string
required
Invoice code to delete

Example Request

curl -X DELETE https://api.example.com/api/pos/factura/FAC0001 \
  -H "Authorization: Bearer YOUR_JWT_TOKEN"

Response

200 Success
{
  "message": "Factura eliminada correctamente"
}
400 Bad Request
{
  "message": "No se pudo eliminar la factura (puede que no exista o no esté en estado PENDIENTE)"
}
500 Server Error
{
  "message": "Error interno al eliminar factura"
}

Invoice Detail Operations

Add Product to Invoice

POST
endpoint
/api/pos/factura/detalle
Adds a product to an invoice’s detail lines. The invoice must be in PEN status. After adding, totals are automatically recalculated.

Request Body

fac_codigo
string
required
Invoice code
prd_codigo
string
required
Product code (must exist in producto table)
pxfa_cantidad
integer
required
Quantity to add

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

200 Success
{
  "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"
}
400 Bad Request
{
  "message": "Error adding detail"
}

Update Invoice Detail

PUT
endpoint
/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

pxfaCodigo
string
required
Detail line code (pxfa_codigo)

Request Body

pxfa_cantidad
integer
required
New quantity

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

200 Success
{
  "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
    }
  ]
}
400 Bad Request
{
  "message": "No se puede modificar el detalle (factura no PEN o detalle no existe)"
}

Delete Invoice Detail

DELETE
endpoint
/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

pxfaCodigo
string
required
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

200 Success
{
  "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": []
}
400 Bad Request
{
  "message": "No se puede eliminar el detalle (factura no PEN o no existe)"
}
500 Server Error
{
  "message": "Error interno al eliminar detalle"
}

Invoice Status Values

fac_estado
enum

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:
  1. 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
  2. Kardex Entries: Creates inventory movement records
    • One entry per product in invoice detail
    • Links to transaction and invoice
    • Records quantity decrease
  3. Stock Update: Decreases prd_stock for each product
    • Validates sufficient stock before update
    • Fails entire transaction if any product has insufficient stock
  4. Status Change: Updates fac_estado from PEN to APR

Cancellation Process

The anular_factura stored procedure reverses the approval:
  1. Validates invoice is in APR status
  2. Restores product stock quantities
  3. Creates reversal kardex entries
  4. 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

ErrorStatusDescription
Duplicate product400Product already exists in invoice detail (code 23505)
Not pending400Operation requires invoice in PEN status
Insufficient stock400Product doesn’t have enough stock for approval
Not found404Invoice or detail line doesn’t exist
Invalid state400Operation not allowed in current invoice state

Error Response Examples

Duplicate Product (23505)
{
  "message": "Producto ya en lista"
}
Stock Validation Error
{
  "message": "Stock insuficiente para producto PRD001"
}
State Validation Error
{
  "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')

Build docs developers (and LLMs) love