Volver a El motor

Backend · documento oficial

La API del motor

Contrato vigente de la API. Refleja el modelo de Las tablas del negocio y los roles de Roles y permisos.

El motor (Django + Django REST Framework) expone una API REST en JSON, versionada bajo /api/v1/. La intranet y la tienda pública son clientes ligeros: solo piden y presentan datos; todas las reglas —descontar stock, calcular el costo en soles, encolar la emisión a SUNAT— viven aquí.

Convenciones

Base Todo cuelga de /api/v1/.
Formato Entra y sale JSON (salvo subida de fotos, multipart). Fechas ISO 8601, montos como número.
Autenticación Token JWT (rest_framework_simplejwt) en Authorization: Bearer …, 8 h de vida. El catálogo y las fotos son de lectura pública sin token.
Listados Paginados (?page=, 25 por página), con ?buscar= (búsqueda) y filtros propios de cada recurso (?categoria=, ?estado=…).
Permisos Según el rol del usuario dentro del JWT. Detalle completo en Roles y permisos.
Errores 200/201 bien, 400 datos inválidos, 401/403 sin permiso, 404 no existe, 409 conflicto de negocio (sin stock, orden ya recibida…).

Autenticación

quién entra
/authAcceso del equipo a la intranet. El catálogo público no necesita login.
EndpointQué hace y reglas
POST /auth/login
{ email, password } → { access, refresh }
Valida credenciales y devuelve un JWT con el rol dentro. Además abre una sesión Django compartida (cookie Domain=.importacionleon.com) para que Documentation reconozca la sesión sin manejar el JWT.
POST /auth/registro
{ nombre, email, password, rol } → 201, sin token
Público, sin autenticación
Autoregistro del equipo. Crea la cuenta ya bloqueada (activo=false, aprobado=false); no admite pedir el rol admin. Queda visible en /equipo hasta que un admin la apruebe.
POST /auth/refresh
Renueva el token antes de que caduque (7 días de vida el refresh, rota en cada uso).
POST /auth/logout
Invalida (blacklist) el refresh token y cierra también la sesión Django compartida.
GET /auth/verificar
Solo mira la cookie de sesión (no el JWT); la usa nginx (auth_request) para proteger Documentation. 401 si no hay sesión o la cuenta ya no está activo.
GET /auth/yo
El usuario en sesión: email, nombre, rol, activo y tema.
GET/PATCH /auth/preferencias
Lee o cambia la preferencia de tema claro/oscuro del usuario en sesión. La fuente de verdad es esta; el navegador solo cachea el último valor.

Equipo

quién opera la intranet
/equipoLectura abierta a cualquier autenticado; alta, edición y aprobación solo admin. Sin DELETE: se desactiva, no se borra.
EndpointQué hace y reglas
GET /equipo
?activo=&aprobado=&rol=
Lista el equipo interno, incluidas las solicitudes de autoregistro (aprobado=false).
POST /equipo
Alta directa. Aprovisiona la credencial y devuelve password_temporal una única vez; no hay envío de correo con la contraseña.
PATCH /equipo/{id}
Edita nombre, rol o activo; sincroniza la credencial (staff/superusuario según el rol).
POST /equipo/{id}/aprobar
Aprueba una solicitud de autoregistro y avisa por correo (best-effort: si no hay SMTP, la aprobación se aplica igual).
POST /equipo/{id}/rechazar
Borra una solicitud de autoregistro aún no aprobada, junto con su credencial.

Catálogo

qué vendemos
/categorias · /productos · /variantesLectura pública (sin token) para la tienda; escritura abastecimiento / admin.
EndpointQué hace y reglas
GET /productos
?buscar=&categoria=&proveedor_principal=&activo=
Lista productos con sus variantes. ?buscar= usa pg_trgm sobre nombre/marca.
POST/PATCH/DELETE /productos/{id}
Crear/editar/borrar. Borrar da 409 si el producto tiene variantes con historial (ventas, compras o movimientos).
GET /variantes/{id}
El SKU real, con stock e imagenes calculados (cruza con leon_fotos por variante_id).
GET /variantes/{id}/stock
El stock calculado (nunca escrito): stock (físico), reservado y disponible (físico − reservado), más el físico por almacén.
GET /categorias
?buscar=
Árbol de categorías (con padre_id). Borrar da 409 si tiene productos.

Inventario

cuánto tenemos
/almacenes · /movimientosEl stock no se escribe: se calcula (ver Cómo se deriva el stock). abastecimiento / admin escriben.
EndpointQué hace y reglas
GET /almacenes
Las zonas físicas, con espacio_disponible calculado. Borrar da 409 si tiene movimientos.
GET /movimientos
?variante=&almacen=&tipo=
El historial de entradas/salidas.
POST /movimientos
Registra un ajuste o merma a mano — sin PATCH ni DELETE en este recurso. Las compras y ventas generan su movimiento automáticamente vía recibir/confirmar, no por aquí.

Compras / Importación

la parte de abastecimiento
/proveedores · /ordenes-compra · /embarques · /muestrasEl flujo desde China: proveedor → orden → embarque → recepción. abastecimiento / admin escriben.
EndpointQué hace y reglas
GET /proveedores
Fábricas y mayoristas, con sus productos anidados. Borrar da 409 si tiene órdenes o muestras.
POST /ordenes-compra
?proveedor=&estado=
Crea un pedido a un proveedor con sus líneas. Guarda tipo_cambio para el costo real en soles.
PATCH /ordenes-compra/{id}
Avanza el estado (pedido → producción → tránsito → recibido) o edita datos generales.
POST /ordenes-compra/{id}/recibir
{ almacen }
Genera las entradas de stock de todas las líneas, en una transacción. 409 si ya estaba recibida (idempotencia).
GET /embarques
?orden_compra=
Incoterm, contenedor, fechas, y sus costos/documentos anidados (flete, aduana, seguro).
GET /muestras
Control de calidad previo a importar. Sin interfaz propia en la intranet hoy — solo API.

Ventas

pedidos y clientes
/clientes · /pedidos · /enviosConfirmar un pedido descuenta stock. El precio se congela en cada línea. ventas / admin escriben.
EndpointQué hace y reglas
GET /clientes
?buscar=
Personas y empresas. El doc_tipo (DNI/RUC) decide boleta o factura.
POST /pedidos
{ …, es_reserva, almacen, fecha_expiracion, tipo_comprobante }
Crea una venta con sus líneas. Un pedido normal no verifica stock al crear. Un pedido a futuro (es_reserva) genera una reserva por línea en el almacen indicado — 409 si pide más que el disponible. tipo_comprobante se deriva del documento del cliente; Factura exige RUC (400 si no).
PATCH /pedidos/{id}
Edita líneas solo en pendiente (ajusta las reservas en la misma transacción). Cambios de estado permitidos: pendiente→enviado/anulado, pagado→enviado/anulado, enviado→entregado/anulado. Prohibido salir de entregado/anulado/expirado, poner pagado (usa confirmar) o expirado a mano (solo la tarea).
POST /pedidos/{id}/confirmar
{ almacen } — no hace falta en reservas
Pasa el pedido a pagado generando la venta real. En un pedido normal descuenta del almacén indicado (todo o nada; 409 con el detalle si no alcanza). En un pedido a futuro consume la reserva en su propio almacén. Transacción atómica.
GET /pedidos/{id}
Detalle con líneas; el total no es una columna, se calcula sumándolas.
POST /envios
?pedido=&estado=
Registra el despacho. courier/tracking son texto libre — sin API de courier real conectada.

Facturación y pagos

documentos legales
/comprobantes · /pagos · /notas-creditoEmitir a SUNAT es asíncrono — y hoy, simulado (ver Servicios externos). ventas / admin escriben.
EndpointQué hace y reglas
POST /comprobantes
?pedido=&estado_sunat=&tipo=
Registra el comprobante y encola emitir_comprobante_sunat (Celery); responde 202. La tarea simula el envío y marca aceptado — no hay integración SUNAT real.
GET /comprobantes/{id}
Estado SUNAT (siempre aceptado tras el simulacro). El XML/CDR vive en /documentos-sunat.
POST /notas-credito
Devolución o anulación que referencia al comprobante original. Sin interfaz propia en la intranet hoy — solo API.
POST /pagos
?pedido=&estado=&metodo=
Registra el cobro (Yape, Plin, tarjeta, transferencia, efectivo). Sin interfaz propia para crear pagos en la intranet — solo API.
POST /pagos/{id}/confirmar
Marca el pago confirmado y, si la suma cubre el total, pasa el pedido a pagado. Esta acción sí está en la intranet (Facturación).

Fotos de producto

base leon_fotos
/imagenes · /imagenes-renderLectura pública. Escritura abastecimiento / marketing / admin.
EndpointQué hace y reglas
GET /imagenes
?variante_id=&sku=
Lista imágenes de una variante. Público, sin token.
POST /imagenes
multipart: variante_id, sku, archivo, alt?, orden?
Sube la foto (guarda el render "original") y encola procesar_imagen (conversión real a WebP con Pillow) sin bloquear la respuesta (201).
GET /imagenes-render/{id}/archivo
Sirve el binario (modo autónomo, el usado hoy) o redirige a una URL de CDN si está configurada. Siempre público (AllowAny), para que un <img src> funcione sin JWT.

Documentos SUNAT

base leon_facturas, solo lectura
/documentos-sunatReadOnlyModelViewSet: el documento lo genera la tarea Celery, no se crea por API.
EndpointQué hace y reglas
GET /documentos-sunat
?comprobante_id=&estado_sunat=&tipo=&receptor_doc=
Lista los documentos generados (siempre simulados hoy).
GET /documentos-sunat/{id}
Detalle con sus eventos anidados (generado → enviado → aceptado).

Reportes

analítica del negocio
/reportesTodo se calcula en el backend con agregaciones del ORM (annotate/aggregate), nunca trayendo datos crudos. Mismo selector de periodo (?desde=&hasta=) en todos salvo stock-bajo.
EndpointQué hace y reglas
GET /reportes/ventas
?desde=&hasta=
Ventas por día/semana/mes (granularidad automática), variación vs. periodo anterior de igual duración, ticket medio, top productos.
GET /reportes/stock-bajo
Variantes bajo mínimo, con nivel de urgencia (agotado/crítico/bajo). Foto de "ahora", sin periodo.
GET /reportes/inventario
?desde=&hasta=
Valor de inventario (foto de hoy), entradas vs. salidas del periodo, SKU con stock parado.
GET /reportes/compras
?desde=&hasta=
Compras por proveedor en el periodo, pendientes de recibir (snapshot).
GET /reportes/rentabilidad
?desde=&hasta=
Margen bruto global y por producto, con el costo actual de la variante (no hay coste histórico por venta).

Actividad y salud

auditoría y monitoreo
/actividad · /salud/actividad exige admin / sistemas incluso para leer (SoloAdminOSistemas) — el único recurso sin lectura abierta a cualquier autenticado.
EndpointQué hace y reglas
GET /actividad
?usuario=&accion=&modelo_afectado=&buscar=&desde=&hasta=
Registro de auditoría, solo lectura, paginado de 50 en 50. Ver Registro de actividad.
GET /salud
Health check: responde si el motor y la base están vivos. Sin autenticación.
GET leer POST crear / acción PATCH editar DELETE borrar rol quién puede escribir

Tres reglas atraviesan toda la API: el stock se calcula (nunca se fija por endpoint — ver Cómo se deriva el stock), confirmar pedido y recibir orden son transacciones que no dejan estados a medias, y lo lento (SUNAT, fotos) es asíncrono vía Celery — aunque hoy SUNAT es un simulacro, no una integración real. Además, una tarea de Celery beat (apps.principal.tasks.expirar_reservas_vencidas, cada hora en punto) libera las reservas de los pedidos a futuro vencidos y los marca expirado; es idempotente y atómica por pedido (ver Tareas automáticas). El detalle de cada tabla está en Las tablas del negocio; el panorama de componentes, en El motor.