Files
Autoparts-DB/docs/API-POS.md
2026-04-02 03:38:10 +00:00

25 KiB

API Reference -- Nexus Autoparts POS

Base URL: http://[IP]:5001

Todas las rutas empiezan con /pos/api/. Salvo indicacion contraria, todos los endpoints requieren autenticacion via header Authorization: Bearer <token>.


Autenticacion

Todos los endpoints (excepto login y employees list) requieren un JWT token obtenido via /pos/api/auth/login. El token se envia en el header:

Authorization: Bearer eyJhbGciOi...

El token contiene: tenant_id, employee_id, name, role, branch_id, permissions, device_id.

Duracion por defecto: 8 horas.


1. Auth (3 endpoints)

Prefix: /pos/api/auth

POST /login

Login con PIN. No requiere autenticacion.

Request body:

{
  "tenant_id": 1,
  "pin": "1234",
  "device_id": "tablet-01",
  "branch_id": 1
}

Response 200:

{
  "token": "eyJhbGciOi...",
  "employee": {
    "id": 1,
    "name": "Admin",
    "role": "owner",
    "branch_id": 1,
    "max_discount_pct": 100.0
  },
  "permissions": ["pos.sell", "pos.discount", "..."]
}

Errores: 400 (faltan campos), 401 (PIN incorrecto), 404 (tenant no encontrado), 429 (rate limit)

GET /employees/:tenant_id

Lista empleados para la pantalla de login. No requiere autenticacion.

Response 200:

{
  "data": [
    {"id": 1, "name": "Admin", "initials": "AD", "role": "owner", "role_label": "Dueno"}
  ]
}

GET /me

Info del empleado actual desde el token.

Response 200:

{
  "employee_id": 1,
  "name": "Admin",
  "role": "owner",
  "tenant_id": 1,
  "branch_id": 1,
  "permissions": ["pos.sell", "..."]
}

2. Config (5 endpoints)

Prefix: /pos/api/config

GET /branches

Lista sucursales del tenant.

Permiso: cualquier empleado autenticado

Response 200:

{
  "data": [
    {"id": 1, "name": "Sucursal Centro", "address": "Av. Juarez 100", "phone": "555-0001", "is_active": true}
  ]
}

POST /branches

Crear sucursal.

Permiso: config.edit

Request body:

{
  "name": "Sucursal Norte",
  "address": "Blvd. Independencia 200",
  "phone": "555-0002"
}

Response 201:

{"id": 2, "message": "Branch created"}

GET /employees

Lista empleados con detalle.

Permiso: config.view

Response 200:

{
  "data": [
    {
      "id": 1, "name": "Admin", "email": null, "phone": null,
      "role": "owner", "branch_id": 1, "branch_name": "Sucursal Centro",
      "max_discount_pct": 100.0, "is_active": true
    }
  ]
}

POST /employees

Crear empleado.

Permiso: config.edit

Request body:

{
  "name": "Juan Lopez",
  "role": "cashier",
  "pin": "5678",
  "email": "juan@correo.com",
  "phone": "555-1234",
  "branch_id": 1,
  "max_discount_pct": 10
}

Roles validos: admin, cashier, warehouse, accountant

Response 201:

{"id": 2, "message": "Employee created"}

GET /theme

Obtener tema actual del tenant.

Permiso: cualquier empleado autenticado

Response 200:

{
  "theme": "default",
  "variables": {
    "--color-primary": "#1a73e8",
    "--color-secondary": "#5f6368",
    "--color-accent": "#ff6b35",
    "--color-bg": "#ffffff",
    "--color-surface": "#f8f9fa",
    "--color-text": "#202124",
    "--color-border": "#dadce0",
    "--font-display": "'Sora', sans-serif",
    "--font-body": "'Plus Jakarta Sans', sans-serif",
    "--font-mono": "'JetBrains Mono', monospace",
    "--radius": "8px"
  }
}

3. Inventory (22 endpoints)

Prefix: /pos/api/inventory

GET /items

Lista productos con stock. Soporta busqueda y paginacion.

Permiso: inventory.view

Query params:

  • q (string) -- buscar por nombre, part_number o barcode
  • page (int, default 1)
  • per_page (int, default 50, max 200)
  • branch_id (int) -- filtrar por sucursal
  • category_id (int) -- filtrar por categoria
  • low_stock (bool) -- solo productos bajo minimo

Response 200:

{
  "data": [
    {
      "id": 1, "part_number": "04465-02220", "barcode": "7501234567890",
      "name": "Pastillas de freno delanteras", "description": "...",
      "branch_id": 1, "branch_name": "Sucursal Centro",
      "category_id": 5, "brand": "Toyota", "unit": "PZA",
      "cost": 150.00, "price_1": 350.00, "price_2": 300.00, "price_3": 280.00,
      "tax_rate": 0.16, "min_stock": 5, "max_stock": 50, "location": "A-3-2",
      "image_url": null, "catalog_part_id": 12345, "stock": 23
    }
  ],
  "pagination": {"page": 1, "per_page": 50, "total": 1234, "total_pages": 25}
}

GET /items/:item_id

Detalle de un producto con stock e historial de movimientos.

Permiso: inventory.view

POST /items

Crear producto. Genera codigo de barras automaticamente si no se proporciona.

Permiso: inventory.create

Request body:

{
  "part_number": "04465-02220",
  "name": "Pastillas de freno delanteras",
  "description": "Pastillas ceramicas originales Toyota",
  "branch_id": 1,
  "category_id": 5,
  "brand": "Toyota",
  "unit": "PZA",
  "cost": 150.00,
  "price_1": 350.00,
  "price_2": 300.00,
  "price_3": 280.00,
  "tax_rate": 0.16,
  "min_stock": 5,
  "max_stock": 50,
  "location": "A-3-2",
  "initial_stock": 10,
  "catalog_part_id": 12345
}

Response 201:

{"id": 1, "barcode": "7501234567890", "message": "Item created"}

PUT /items/:item_id

Actualizar datos de un producto.

Permiso: inventory.edit

Request body: Mismos campos que POST (parcial, solo campos a actualizar).

POST /purchase

Registrar compra a proveedor. Incrementa stock.

Permiso: inventory.create

Request body:

{
  "supplier": "Distribuidora Central",
  "invoice_number": "FAC-001",
  "items": [
    {"inventory_id": 1, "quantity": 20, "unit_cost": 145.00}
  ],
  "notes": "Compra semanal"
}

POST /adjustment

Ajuste manual de inventario.

Permiso: inventory.adjust

Request body:

{
  "inventory_id": 1,
  "quantity": -2,
  "reason": "Merma por dano en almacen"
}

POST /transfer

Transferir stock entre sucursales.

Permiso: inventory.transfer

Request body:

{
  "from_branch_id": 1,
  "to_branch_id": 2,
  "items": [
    {"inventory_id": 1, "quantity": 5}
  ],
  "notes": "Reabasto sucursal norte"
}

POST /return

Registrar devolucion de producto. Incrementa stock.

Permiso: inventory.create

Request body:

{
  "sale_id": 123,
  "items": [
    {"inventory_id": 1, "quantity": 1}
  ],
  "reason": "Parte equivocada"
}

POST /physical-count/start

Iniciar toma fisica de inventario.

Permiso: inventory.adjust

Request body:

{
  "branch_id": 1,
  "items": [
    {"inventory_id": 1, "counted_quantity": 22}
  ]
}

Response 201:

{"count_id": 1, "items_counted": 1, "message": "Physical count started"}

POST /physical-count/approve

Aprobar toma fisica y aplicar ajustes.

Permiso: inventory.adjust

Request body:

{
  "count_id": 1
}

GET /alerts

Obtener alertas de inventario (stock cero, bajo minimo, sobre maximo).

Permiso: inventory.view

Response 200:

{
  "zero_stock": [...],
  "low_stock": [...],
  "over_stock": [...]
}

GET /items/:item_id/history

Historial de movimientos de un producto.

Permiso: inventory.view

Query params:

  • limit (int, default 50)

GET /reports/valuation

Reporte de valorizacion del inventario.

Permiso: inventory.view

Query params:

  • branch_id (int, opcional)

Response 200:

{
  "total_cost_value": 125000.00,
  "total_sale_value": 350000.00,
  "item_count": 450,
  "items": [...]
}

GET /reports/abc

Clasificacion ABC de productos por volumen de venta.

Permiso: inventory.view

Query params:

  • branch_id (int, opcional)
  • days (int, default 90)

GET /reports/no-movement

Productos sin movimiento en un periodo.

Permiso: inventory.view

Query params:

  • branch_id (int, opcional)
  • days (int, default 60)

GET /reports/low-stock

Productos por debajo del stock minimo.

Permiso: inventory.view

Query params:

  • branch_id (int, opcional)

GET /reports/branch-comparison

Comparativo de stock entre sucursales.

Permiso: inventory.view

Query params:

  • inventory_id (int, opcional) -- para un producto especifico

GET /categories

Lista categorias de inventario.

Permiso: inventory.view

GET /brands

Lista marcas de productos en inventario.

Permiso: inventory.view

POST /generate-barcode

Generar codigo de barras para un producto.

Permiso: inventory.create

Response 200:

{"barcode": "7501234567891"}

4. Catalog (9 endpoints)

Prefix: /pos/api/catalog

Estos endpoints consultan la base maestra TecDoc y enriquecen con stock local del tenant.

GET /brands

Lista marcas de vehiculos con partes.

Permiso: catalog.view

Response 200:

{
  "data": [
    {"id": 1, "name": "TOYOTA", "part_count": 245000}
  ]
}

GET /models

Modelos por marca.

Permiso: catalog.view

Query params:

  • brand_id (int, requerido)

GET /years

Anos disponibles por modelo.

Permiso: catalog.view

Query params:

  • model_id (int, requerido)

GET /engines

Motorizaciones por modelo + ano.

Permiso: catalog.view

Query params:

  • model_id (int, requerido)
  • year_id (int, requerido)

GET /categories

Categorias de partes para un vehiculo.

Permiso: catalog.view

Query params:

  • mye_id (int, requerido) -- ID de model_year_engine

GET /groups

Subcategorias (grupos) de partes.

Permiso: catalog.view

Query params:

  • mye_id (int, requerido)
  • category_id (int, requerido)

GET /parts

Partes con enriquecimiento de stock local.

Permiso: catalog.view

Query params:

  • mye_id (int, requerido)
  • group_id (int, requerido)
  • page (int, default 1)
  • per_page (int, default 30)
  • branch_id (int, opcional)

Response 200:

{
  "data": [
    {
      "id": 12345, "part_number": "04465-02220", "name": "Pastillas de freno",
      "manufacturer": "TOYOTA", "local_stock": 5, "local_price": 350.00,
      "alternatives_count": 3
    }
  ],
  "pagination": {"page": 1, "per_page": 30, "total": 15, "total_pages": 1}
}

GET /part/:part_id

Detalle completo: stock local, stock en bodegas, alternativas aftermarket, cross-references.

Permiso: catalog.view

Query params:

  • branch_id (int, opcional)

Busqueda inteligente por numero de parte o texto.

Permiso: catalog.view

Query params:

  • q (string, requerido, minimo 2 caracteres)
  • limit (int, default 50)
  • branch_id (int, opcional)

5. POS / Sales (16 endpoints)

Prefix: /pos/api

POST /sales

Crear venta.

Permiso: pos.sell

Request body:

{
  "items": [
    {"inventory_id": 1, "quantity": 2, "unit_price": 350.00, "discount_pct": 0, "tax_rate": 0.16}
  ],
  "customer_id": null,
  "payment_method": "efectivo",
  "sale_type": "cash",
  "register_id": 1,
  "amount_paid": 812.00,
  "payment_details": [],
  "notes": ""
}

sale_type: cash (contado), credit (credito), mixed

payment_method: efectivo, transferencia, tarjeta, mixto

Para pagos mixtos, payment_details es un array:

[
  {"method": "efectivo", "amount": 500.00},
  {"method": "tarjeta", "amount": 312.00, "reference": "AUTH-12345"}
]

Response 201:

{
  "sale_id": 1,
  "folio": "V-0001",
  "total": 812.00,
  "change": 0.00,
  "items_count": 1
}

GET /sales

Lista ventas con filtros.

Permiso: pos.view

Query params:

  • date_from (YYYY-MM-DD)
  • date_to (YYYY-MM-DD)
  • employee_id (int)
  • customer_id (int)
  • status (string: completed, cancelled, returned)
  • register_id (int)
  • page (int, default 1)
  • per_page (int, default 50, max 200)

GET /sales/:sale_id

Detalle de una venta con items, pagos y datos del cliente.

Permiso: pos.view

PUT /sales/:sale_id/cancel

Cancelar una venta. Restaura inventario.

Permiso: pos.cancel

Request body:

{
  "reason": "Cliente cambio de opinion"
}

GET /sales/last

Obtener la ultima venta (para reimprimir ticket).

Permiso: pos.sell

POST /quotations

Crear cotizacion (venta sin cobrar).

Permiso: pos.sell

Request body:

{
  "items": [
    {"inventory_id": 1, "quantity": 2, "unit_price": 350.00, "discount_pct": 0, "tax_rate": 0.16}
  ],
  "customer_id": 5,
  "notes": "Cotizacion para taller Perez",
  "valid_days": 15
}

GET /quotations

Lista cotizaciones.

Permiso: pos.view

Query params:

  • status (string: active, converted, expired, cancelled)
  • customer_id (int)
  • page (int, default 1)
  • per_page (int, default 50)

GET /quotations/:quot_id

Detalle de una cotizacion.

Permiso: pos.view

POST /quotations/:quot_id/convert

Convertir cotizacion a venta. Aplica los mismos campos que POST /sales.

Permiso: pos.sell

Request body:

{
  "payment_method": "efectivo",
  "register_id": 1,
  "amount_paid": 812.00
}

PUT /quotations/:quot_id/cancel

Cancelar cotizacion.

Permiso: pos.cancel

POST /layaways

Crear apartado con pago parcial.

Permiso: pos.sell

Request body:

{
  "items": [
    {"inventory_id": 1, "quantity": 1, "unit_price": 5000.00, "discount_pct": 0, "tax_rate": 0.16}
  ],
  "customer_id": 5,
  "down_payment": 2000.00,
  "payment_method": "efectivo",
  "register_id": 1,
  "notes": "Apartado motor de arranque",
  "due_date": "2026-04-30"
}

GET /layaways

Lista apartados.

Permiso: pos.view

Query params:

  • status (string: active, completed, cancelled)
  • customer_id (int)
  • page, per_page

GET /layaways/:layaway_id

Detalle de un apartado con items y pagos.

Permiso: pos.view

POST /layaways/:layaway_id/payment

Registrar abono a un apartado.

Permiso: pos.sell

Request body:

{
  "amount": 1500.00,
  "payment_method": "efectivo",
  "register_id": 1
}

POST /layaways/:layaway_id/complete

Completar apartado (cuando el saldo llega a cero). Descuenta inventario.

Permiso: pos.sell

PUT /layaways/:layaway_id/cancel

Cancelar apartado.

Permiso: pos.cancel

Request body:

{
  "reason": "Cliente no completo pagos"
}

6. Customers (7 endpoints)

Prefix: /pos/api/customers

GET /

Lista clientes con busqueda.

Permiso: customers.view

Query params:

  • q (string) -- busca por nombre, RFC, telefono, razon social
  • page (int, default 1)
  • per_page (int, default 50, max 200)
  • branch_id (int)

GET /:customer_id

Detalle de un cliente.

Permiso: customers.view

POST /

Crear cliente.

Permiso: customers.create

Request body:

{
  "name": "Taller Perez",
  "rfc": "XAXX010101000",
  "razon_social": "Taller Mecanico Perez SA de CV",
  "regimen_fiscal": "601",
  "codigo_postal": "06600",
  "uso_cfdi": "G03",
  "email": "taller@correo.com",
  "phone": "555-9876",
  "address": "Calle Reforma 50",
  "price_tier": "taller",
  "credit_limit": 50000.00,
  "branch_id": 1
}

price_tier: mostrador (default), taller, mayoreo

PUT /:customer_id

Actualizar cliente.

Permiso: customers.edit

GET /:customer_id/statement

Estado de cuenta del cliente.

Permiso: customers.view

Query params:

  • date_from (YYYY-MM-DD)
  • date_to (YYYY-MM-DD)

Response 200:

{
  "customer": {"id": 5, "name": "Taller Perez", "credit_limit": 50000.00, "balance": 12500.00},
  "transactions": [
    {"date": "2026-03-28", "type": "sale", "reference": "V-0045", "amount": 5000.00, "balance": 12500.00},
    {"date": "2026-03-25", "type": "payment", "reference": "PAG-012", "amount": -3000.00, "balance": 7500.00}
  ]
}

GET /:customer_id/vehicles

Vehiculos asociados al cliente.

Permiso: customers.view

POST /:customer_id/payment

Registrar pago/abono del cliente.

Permiso: customers.edit

Request body:

{
  "amount": 5000.00,
  "payment_method": "transferencia",
  "reference": "SPEI-20260401-001",
  "notes": "Abono parcial"
}

7. Cash Register (7 endpoints)

Prefix: /pos/api/register

POST /open

Abrir caja registradora.

Permiso: pos.sell

Request body:

{
  "register_number": 1,
  "opening_amount": 2000.00
}

Response 201:

{
  "register_id": 1,
  "register_number": 1,
  "opened_at": "2026-04-01T08:00:00",
  "opening_amount": 2000.00
}

Errores: 400 (ya tiene caja abierta)

GET /current

Obtener la caja abierta del empleado actual.

Permiso: pos.sell

Response 200:

{
  "register_id": 1,
  "register_number": 1,
  "opened_at": "2026-04-01T08:00:00",
  "opening_amount": 2000.00,
  "current_cash": 5430.00,
  "sales_count": 12,
  "sales_total": 8750.00
}

POST /movement

Registrar entrada o salida de efectivo.

Permiso: pos.sell

Request body:

{
  "register_id": 1,
  "type": "out",
  "amount": 500.00,
  "reason": "Compra de papeleria"
}

type: in (entrada) o out (salida)

GET /cut-x

Corte X (consulta sin cerrar caja).

Permiso: pos.sell

Response 200:

{
  "register_id": 1,
  "opening_amount": 2000.00,
  "sales_cash": 3500.00,
  "sales_card": 2100.00,
  "sales_transfer": 1150.00,
  "movements_in": 0.00,
  "movements_out": 500.00,
  "expected_cash": 5000.00,
  "sales_count": 12,
  "total_sales": 6750.00
}

POST /cut-z

Corte Z (cierre de caja).

Permiso: pos.sell

Request body:

{
  "register_id": 1,
  "counted_amount": 4980.00
}

Response 200:

{
  "register_id": 1,
  "expected_cash": 5000.00,
  "counted_amount": 4980.00,
  "difference": -20.00,
  "closed_at": "2026-04-01T22:00:00",
  "summary": {"..."}
}

GET /history

Historial de cortes.

Permiso: pos.sell

Query params:

  • date_from (YYYY-MM-DD)
  • date_to (YYYY-MM-DD)
  • page, per_page

GET /daily-summary

Resumen consolidado del dia (todas las cajas).

Permiso: pos.sell

Query params:

  • date (YYYY-MM-DD, default hoy)

8. Invoicing (6 endpoints)

Prefix: /pos/api/invoicing

POST /invoice

Generar factura CFDI 4.0 desde una venta.

Permiso: invoicing.create

Request body:

{
  "sale_id": 45,
  "tipo_comprobante": "I",
  "uso_cfdi": "G03",
  "forma_pago": "01",
  "metodo_pago": "PUE"
}

tipo_comprobante: I (Ingreso), E (Egreso), P (Pago)

metodo_pago: PUE (Pago en Una sola Exhibicion) o PPD (Pago en Parcialidades o Diferido)

Response 201:

{
  "cfdi_id": 1,
  "uuid": null,
  "status": "pending",
  "xml_preview": "<?xml ..."
}

GET /queue

Lista cola de timbrado.

Permiso: invoicing.view

Query params:

  • status (string: pending, stamped, error, cancelled)
  • page, per_page

GET /queue/:cfdi_id

Detalle de un CFDI en cola.

Permiso: invoicing.view

POST /queue/process

Procesar cola de timbrado (enviar pendientes al PAC).

Permiso: invoicing.create

POST /cancel/:cfdi_id

Cancelar factura ante el SAT.

Permiso: invoicing.cancel

Request body:

{
  "motivo": "02",
  "folio_sustitucion": null
}

Motivos SAT:

  • 01: Comprobante emitido con errores con relacion (requiere folio_sustitucion)
  • 02: Comprobante emitido con errores sin relacion
  • 03: No se llevo a cabo la operacion
  • 04: Operacion nominativa relacionada en una factura global

GET /:sale_id/pdf

Descargar PDF de representacion impresa de la factura.

Permiso: invoicing.view

Response: PDF file (application/pdf)


9. Accounting (11 endpoints)

Prefix: /pos/api/accounting

GET /accounts

Catalogo de cuentas contables (lista plana, el frontend construye el arbol).

Permiso: accounting.view

Response 200:

{
  "data": [
    {
      "id": 1, "code": "100", "sat_code": "100",
      "name": "Activo", "type": "debit",
      "parent_id": null, "level": 1, "is_active": true
    }
  ]
}

POST /accounts

Crear cuenta contable.

Permiso: accounting.create

Request body:

{
  "code": "100.01",
  "sat_code": "100.01",
  "name": "Caja",
  "type": "debit",
  "parent_id": 1,
  "description": "Efectivo en caja"
}

GET /entries

Lista polizas contables.

Permiso: accounting.view

Query params:

  • date_from (YYYY-MM-DD)
  • date_to (YYYY-MM-DD)
  • type (string: sale, purchase, adjustment, manual)
  • page, per_page

GET /entries/:entry_id

Detalle de una poliza con lineas de cargo y abono.

Permiso: accounting.view

Response 200:

{
  "id": 1,
  "date": "2026-04-01",
  "type": "sale",
  "reference": "V-0045",
  "description": "Venta al contado",
  "lines": [
    {"account_id": 5, "account_code": "100.01", "account_name": "Caja", "debit": 812.00, "credit": 0.00},
    {"account_id": 10, "account_code": "400.01", "account_name": "Ventas", "debit": 0.00, "credit": 700.00},
    {"account_id": 15, "account_code": "210.01", "account_name": "IVA Trasladado", "debit": 0.00, "credit": 112.00}
  ]
}

POST /entries

Crear poliza manual.

Permiso: accounting.create

Request body:

{
  "date": "2026-04-01",
  "description": "Pago de renta del local",
  "lines": [
    {"account_id": 20, "debit": 15000.00, "credit": 0.00},
    {"account_id": 5, "debit": 0.00, "credit": 15000.00}
  ]
}

La suma de cargos debe ser igual a la suma de abonos.

GET /trial-balance

Balanza de comprobacion.

Permiso: accounting.view

Query params:

  • date_from (YYYY-MM-DD)
  • date_to (YYYY-MM-DD)

Response 200:

{
  "period": {"from": "2026-04-01", "to": "2026-04-30"},
  "accounts": [
    {
      "code": "100.01", "name": "Caja",
      "opening_debit": 50000.00, "opening_credit": 0.00,
      "period_debit": 25000.00, "period_credit": 18000.00,
      "closing_debit": 57000.00, "closing_credit": 0.00
    }
  ],
  "totals": {
    "opening_debit": 100000.00, "opening_credit": 100000.00,
    "period_debit": 45000.00, "period_credit": 45000.00,
    "closing_debit": 145000.00, "closing_credit": 145000.00
  }
}

GET /income-statement

Estado de resultados.

Permiso: accounting.view

Query params:

  • date_from (YYYY-MM-DD)
  • date_to (YYYY-MM-DD)

Response 200:

{
  "period": {"from": "2026-04-01", "to": "2026-04-30"},
  "income": [{"code": "400.01", "name": "Ventas", "amount": 150000.00}],
  "cost_of_sales": [{"code": "500.01", "name": "Costo de Ventas", "amount": 90000.00}],
  "expenses": [{"code": "600.01", "name": "Gastos de Operacion", "amount": 25000.00}],
  "gross_profit": 60000.00,
  "operating_profit": 35000.00,
  "net_income": 35000.00
}

GET /balance-sheet

Balance general.

Permiso: accounting.view

Query params:

  • date (YYYY-MM-DD, default hoy)

Response 200:

{
  "date": "2026-04-30",
  "assets": [{"code": "100", "name": "Activo Circulante", "amount": 250000.00, "children": [...]}],
  "liabilities": [{"code": "200", "name": "Pasivo", "amount": 80000.00, "children": [...]}],
  "equity": [{"code": "300", "name": "Capital", "amount": 170000.00, "children": [...]}],
  "total_assets": 250000.00,
  "total_liabilities_equity": 250000.00
}

GET /aging

Antiguedad de saldos (cuentas por cobrar).

Permiso: accounting.view

Query params:

  • date (YYYY-MM-DD, default hoy)

Response 200:

{
  "date": "2026-04-30",
  "customers": [
    {
      "customer_id": 5, "name": "Taller Perez",
      "current": 5000.00,
      "days_1_30": 3000.00,
      "days_31_60": 2000.00,
      "days_61_90": 0.00,
      "days_over_90": 0.00,
      "total": 10000.00
    }
  ],
  "totals": {
    "current": 15000.00, "days_1_30": 8000.00,
    "days_31_60": 3000.00, "days_61_90": 1000.00,
    "days_over_90": 500.00, "total": 27500.00
  }
}

GET /periods

Lista periodos fiscales.

Permiso: accounting.view

Response 200:

{
  "data": [
    {"year": 2026, "month": 4, "status": "open", "closed_at": null, "closed_by": null},
    {"year": 2026, "month": 3, "status": "closed", "closed_at": "2026-04-05T10:00:00", "closed_by": "Admin"}
  ]
}

POST /periods/close

Cerrar periodo fiscal. Irreversible.

Permiso: accounting.create

Request body:

{
  "year": 2026,
  "month": 3
}

10. Health Check (1 endpoint)

GET /pos/health

No requiere autenticacion.

Response 200:

{"status": "ok"}

Resumen de endpoints

Modulo Prefix Endpoints
Auth /pos/api/auth 3
Config /pos/api/config 5
Inventory /pos/api/inventory 22
Catalog /pos/api/catalog 9
POS/Sales /pos/api 16
Customers /pos/api/customers 7
Cash Register /pos/api/register 7
Invoicing /pos/api/invoicing 6
Accounting /pos/api/accounting 11
Health /pos 1
Total 87

Codigos de error comunes

Codigo Significado
400 Request invalido (faltan campos requeridos, datos incorrectos)
401 No autenticado (token faltante, expirado o invalido)
403 Sin permiso (el rol no tiene el permiso requerido)
404 Recurso no encontrado
429 Rate limit excedido (demasiados intentos de PIN)
500 Error interno del servidor

Todos los errores retornan:

{"error": "Descripcion del error"}