# logo nullInvoice **nullInvoice** es un microservicio Spring Boot para la **generación y gestión automatizada de facturas** con **plantillas HTML totalmente personalizables**, diseñado para integrarse con tiendas en línea y plataformas SaaS. ## Resumen Las empresas usan nullInvoice para la generación de facturas después de finalizar las ventas. Los proveedores se configuran una sola vez desde la interfaz web, luego su aplicación llama a la API REST para generar facturas conformes bajo demanda. **Cómo funciona:** 1. **Configuración**: Configure proveedores en la UI con datos de la empresa, locale, moneda, tasas de impuestos, branding personalizado y plantillas de facturas 2. **Autenticación**: Genere claves API desde el Panel de administrador para un acceso seguro a la API REST 3. **Integración**: Su tienda en línea/SaaS realiza llamadas autenticadas a `/api/v1/invoices/generate` usando el ID del proveedor 4. **Generación**: Las facturas se crean a partir de plantillas HTML totalmente personalizables y se devuelven como JSON o PDF con metadatos en los encabezados 5. **Entrega**: Su aplicación recibe la factura y puede reenviarla al cliente o almacenarla para sus registros ### Flujo de integración típico ``` ┌─────────────────┐ │ Cliente │ │ finaliza │ │ la compra │ └────────┬────────┘ │ ▼ ┌─────────────────────────────────────────┐ │ Su plataforma de tienda/SaaS │ │ ───────────────────────────────────── │ │ 1. Procesar el pago │ │ 2. Emitir comprobante digital │ │ (requerido) │ │ 3. ¿Cliente solicita factura? ──────┐ │ └──────────────────────────────────────┼──┘ │ │ Llamada API ▼ ┌──────────────────────────────┐ │ Servicio nullInvoice │ │ ────────────────────────── │ │ POST /api/v1/invoices/ │ │ generate │ │ │ │ - Valida ID del proveedor │ │ - Aplica plantilla │ │ - Guarda snapshot HTML │ │ - Genera PDF │ │ - Devuelve la factura │ └──────────────┬───────────────┘ │ │ Respuesta (JSON o PDF) ▼ ┌──────────────────────────────────────────┐ │ Su plataforma de tienda/SaaS │ │ ────────────────────────────────────── │ │ - Recibe metadatos JSON O archivo PDF │ │ - Guarda el número de factura │ │ para registro │ │ - Envía PDF al cliente por correo │ └──────────────────────────────────────────┘ ``` ### Funciones clave **Plantillas totalmente personalizables** - Las facturas se basan en plantillas HTML definidas por el usuario con CSS en línea - Las plantillas admiten más de 30 marcadores de posición para proveedor, cliente y datos financieros - Las plantillas predeterminadas por proveedor permiten branding distinto por unidad de negocio **Inmutabilidad de documentos** - Cada factura almacena un **snapshot del HTML procesado** al momento de la generación - Los cambios en la plantilla no afectan a las facturas ya generadas - Las facturas pueden regenerarse de forma consistente desde el snapshot guardado - Los permisos de la base de datos impiden eliminar registros de facturas (cumplimiento financiero) **Preparado para multi-tenant** - Configure múltiples proveedores con ajustes independientes (locale, moneda, numeración, branding) - Los consumidores de la API especifican el ID del proveedor para generar facturas en distintas entidades de negocio **Entrega flexible** - Devuelve metadatos de la factura como JSON para archivo (`response_type: number`) - Devuelve PDF directamente con metadatos en los encabezados para entrega inmediata (`response_type: pdf`) - Recupere PDFs más tarde en `/api/v1/invoices/{invoiceNumber}/pdf` **Cola asíncrona de generación** - Ruta alternativa para integraciones que no quieren esperar la generación de forma síncrona - La solicitud se persiste en una cola, un worker en segundo plano genera la factura y los clientes consultan el estado mediante polling - Mismas garantías de numeración que la ruta síncrona (bloqueo compartido del proveedor) - El worker se puede deshabilitar con `QUEUE_ENABLED=false` (predeterminado `true`) **Documentación OpenAPI** - Documentación API interactiva en `/swagger` - Especificación OpenAPI JSON en `/openapi` - Ejemplos completos de solicitudes/respuestas y funcionalidad "try-it-out" ## Avisos importantes **⚠️ Aviso de seguridad** nullInvoice incluye **autenticación integrada** (inicio de sesión de UI basado en sesión + autenticación con clave API para endpoints REST). Si bien esto ofrece seguridad por defecto, la aplicación está pensada para despliegues en redes internas/privadas. **Despliegue recomendado:** - Detrás de un firewall o VPN - Dentro de una red privada accesible solo por sus aplicaciones de confianza - Con HTTPS/TLS habilitado para todas las conexiones - Detrás de un reverse proxy con limitación de tasa configurada **Para despliegues en producción se requieren medidas de seguridad adicionales:** - Habilitar HTTPS/TLS - Configurar limitación de tasa a nivel de reverse proxy - Usar contraseñas de administrador seguras - Rotar claves API regularmente - Mantener las claves API en un gestor seguro de secretos (HashiCorp Vault, AWS Secrets Manager, etc.) Consulte la sección [Seguridad y mejores prácticas](#seguridad-y-mejores-practicas) para una lista completa de producción. **⚠️ Esta aplicación NO es un instrumento contable.** nullInvoice es una **tubería de generación de facturas** diseñada para crear, almacenar y entregar documentos de facturas. No: - Hace seguimiento de pagos o estado de pago más allá de las banderas básicas "unpaid/issued" - Gestiona cuentas por cobrar o por pagar - Genera reportes financieros o balances - Se integra con sistemas contables (libros mayores, diarios, etc.) - Maneja contabilidad, conciliación o declaraciones fiscales Para una gestión financiera completa, integre nullInvoice con un sistema contable dedicado. Use este servicio para generar documentos de facturas y luego impórtelos en su software contable para seguimiento y cumplimiento. ## Stack - Java 21, Spring Boot 3.5.3 - MariaDB + JPA - Thymeleaf (UI) - OpenHTMLToPDF (PDFBox) - OpenAPI en `/openapi`, Swagger UI en `/swagger` ## Requisitos previos **Para despliegue con Docker (recomendado):** - Docker - Docker Compose **Para desarrollo local:** - Java 21 (JDK - Eclipse Temurin u OpenJDK) - Maven 3.9+ - MariaDB 10.5+ (o MySQL 8.0+) - Binario standalone de Tailwind CSS (para construir CSS - desde [GitHub releases](https://github.com/tailwindlabs/tailwindcss/releases)) ## Configuración de la base de datos **Crear la base de datos:** ```sql CREATE DATABASE nullinvoice CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; ``` **Crear un usuario de aplicación dedicado con permisos restringidos:** ```sql CREATE USER 'nullinvoice'@'localhost' IDENTIFIED BY 'your_secure_password'; -- Otorgar permisos para operaciones normales y migraciones de esquema GRANT SELECT, INSERT, UPDATE ON nullinvoice.* TO 'nullinvoice'@'localhost'; GRANT CREATE, ALTER, INDEX, REFERENCES ON nullinvoice.* TO 'nullinvoice'@'localhost'; FLUSH PRIVILEGES; ``` **Para acceso remoto, ajuste el host:** ```sql CREATE USER 'nullinvoice'@'%' IDENTIFIED BY 'your_secure_password'; GRANT SELECT, INSERT, UPDATE ON nullinvoice.* TO 'nullinvoice'@'%'; GRANT CREATE, ALTER, INDEX, REFERENCES ON nullinvoice.* TO 'nullinvoice'@'%'; FLUSH PRIVILEGES; ``` **Importante: ¿Por qué estos permisos restringidos?** Las facturas son **documentos financieros inmutables** que nunca deben eliminarse una vez creadas. El usuario de la aplicación está intencionalmente restringido y no puede: - `DELETE` - no puede eliminar registros - `DROP` - no puede eliminar tablas ni la base de datos - `TRUNCATE` - no puede vaciar tablas - `GRANT` - no puede otorgar permisos a otros Esto garantiza la integridad de los datos y el cumplimiento de los requisitos de conservación de registros financieros. El permiso `UPDATE` se concede para cambios de estado (p. ej., marcar facturas como pagadas) y soft delete en registros de partes. ### Gestión del esquema - El esquema de base de datos se gestiona con migraciones **Flyway** - Esquema inicial: `nullInvoice/src/main/resources/db/migration/V1__initial_schema.sql` - El esquema se crea/actualiza automáticamente al iniciar la aplicación - El modo DDL de Hibernate está configurado en `none` (Flyway gestiona todos los cambios de esquema) ## Configuración Se proporciona un archivo de configuración de ejemplo en `.env.example`. Cópielo a `.env` y ajuste los valores para su entorno. Variables de entorno (valores predeterminados entre paréntesis): - `TZ` - **REQUERIDO** zona horaria del sistema (Europe/Sofia) - `APP_PORT` (8080) - `DB_HOST` (localhost) - `DB_PORT` (3306) - `DB_USER` (nullinvoice) - `DB_PASSWORD` (vacío) - `DB_NAME` (nullinvoice) - `DB_PARAMS` - **REQUERIDO** parámetros JDBC agregados a la URL de conexión ### **IMPORTANTE: Configuración de zona horaria** **DEBE configurar la zona horaria en DOS lugares:** 1. **`TZ`** variable de entorno - establece la zona horaria del sistema/aplicación 2. **`serverTimezone`** parámetro en `DB_PARAMS` - establece la zona horaria de la conexión a la base de datos **Ambos valores DEBEN coincidir con la zona horaria del servidor de base de datos** para que las fechas/horas se interpreten y almacenen correctamente. Configuración de ejemplo: ```bash TZ=Europe/Sofia DB_PARAMS=?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Europe/Sofia ``` Zonas horarias comunes: `UTC`, `Europe/London`, `America/New_York`, `Asia/Tokyo`. Consulte la [lista completa](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones). **Las zonas horarias desalineadas causarán fechas y marcas de tiempo incorrectas en las facturas.** ### Configuración inicial del administrador En el primer inicio, la aplicación redirige a `/setup` para crear la cuenta de administrador inicial. Esto es obligatorio antes de acceder a cualquier otra funcionalidad. **Para restablecer y crear una nueva cuenta de administrador (recuperación de emergencia):** 1. Detenga la aplicación 2. Trunque las tablas `users` y `api_keys` en la base de datos 3. Reinicie la aplicación 4. Complete nuevamente el flujo de `/setup` **Recomendaciones de seguridad:** - Use una contraseña fuerte para la cuenta de administrador - Establezca una pista de contraseña (opcional pero recomendado) - Genere claves API separadas para distintos entornos (dev, staging, prod) - Revoque las claves API no utilizadas ## Seguridad y mejores prácticas **Arquitectura de autenticación:** - **Acceso a UI:** Autenticación basada en sesión con form login - **Acceso a API:** Autenticación Bearer token sin estado (claves API) - **Contraseñas:** BCrypt con 10 rondas - **Claves API:** Formato UUID, hasheadas con BCrypt, se muestran solo una vez al generarlas **Duración de la sesión:** Por defecto, las sesiones de UI expiran después de **30 minutos de inactividad** (predeterminado de Spring Boot/Tomcat). Los usuarios serán automáticamente desconectados y redirigidos a la página de inicio de sesión. Para personalizar la duración de la sesión, agregue lo siguiente a `nullInvoice/src/main/resources/application.yml`: ```yaml server: servlet: session: timeout: 60m # Opciones: 15m, 30m, 1h, 2h, etc. ``` Valores de timeout comunes: - `15m` - 15 minutos (seguridad más estricta) - `30m` - 30 minutos (predeterminado) - `1h` - 1 hora (comodidad para usuarios activos) - `8h` - 8 horas (extendido para sesiones largas) **Lista de verificación para producción:** - Habilitar HTTPS/TLS para todas las conexiones - Usar contraseñas de administrador fuertes - Generar claves API separadas por aplicación/entorno - Implementar detrás de firewall o VPN - Configurar limitación de tasa en el reverse proxy - Configurar logging y monitoreo - Revisar regularmente el uso de claves API (timestamp de último uso) - Revocar inmediatamente claves API no usadas o comprometidas - Mantener claves API en variables de entorno, nunca en el código - Usar un gestor seguro de secretos (HashiCorp Vault, AWS Secrets Manager, etc.) **Protección CSRF:** - Habilitada para todos los formularios de UI - Deshabilitada para endpoints API (auth Bearer sin estado) **Seguridad en el primer inicio:** - La aplicación no se puede usar hasta crear una cuenta de administrador vía `/setup` - La página de configuración solo es accesible cuando no existe un administrador - Tras la configuración, se requiere inicio de sesión para toda la funcionalidad ## Docker Compose **Imágenes oficiales de Docker:** Hay imágenes preconstruidas disponibles en [Docker Hub](https://hub.docker.com). Busque la imagen oficial de nullInvoice para omitir el paso de build. Construir sin caché: ```bash docker compose build --no-cache ``` Levantar el stack: ```bash docker compose up -d ``` Detener el stack: ```bash docker compose down ``` ## Desarrollo local (sin Docker) ### Configuración 1. Asegúrese de tener una instancia de MariaDB en ejecución y cree la base de datos. Ver [Configuración de la base de datos](#configuracion-de-la-base-de-datos) 2. Construir Tailwind CSS (requerido antes del primer inicio): ```bash ./build-tailwind.sh ``` 3. Construir el proyecto con Maven: ```bash cd nullInvoice mvn clean package ``` 4. Iniciar la aplicación: ```bash java -jar target/nullinvoice-0.0.1-SNAPSHOT.jar ``` ### Uso de NetBeans IDE El proyecto fue construido con NetBeans y puede abrirse como proyecto Maven con el plugin de Spring Boot. **Configurar variables de entorno en NetBeans:** Opción 1 - Vía IDE: 1. Clic derecho en el proyecto >> Properties 2. Ir a Actions >> Run 3. Establecer variables de entorno: `APP_PORT`, `DB_NAME`, `DB_USER`, `DB_PASSWORD` Opción 2 - Editar directamente: - Modifique `nbactions.xml` en la raíz del proyecto ## Primeros pasos ### Configuración del primer inicio En el primer acceso, será redirigido a `/setup` para crear la cuenta de administrador inicial: 1. Navegue a `http://localhost:8080` (o su URL configurada) 2. Será redirigido automáticamente a `/setup` 3. Cree una cuenta de administrador con nombre de usuario, contraseña y pista opcional 4. Después del setup, será redirigido a `/login` **Iniciar sesión:** - Acceda a la página de inicio de sesión en `/login` - Use las credenciales de administrador que creó - La pista de contraseña está disponible mediante el icono de información (si está configurada) **Panel de administrador:** Después de iniciar sesión, acceda al panel de administrador desde el menú de usuario: - Cambiar contraseña de administrador - Generar claves API para acceso a la API REST - Revocar claves API - Ver uso de claves API (timestamp de último uso) ### Configurar su primer proveedor **Requisitos previos:** - Configuración inicial completada (cuenta de administrador creada) - Inicio de sesión con credenciales de administrador Antes de poder generar facturas vía API, debe configurar al menos un proveedor en la interfaz web. Los proveedores definen datos de la empresa, locale, moneda, tasas de impuestos, branding personalizado (a través de plantillas) y la numeración de facturas para la generación. 1. Inicie sesión en la interfaz web en `http://localhost:8080` (o su puerto configurado) 2. Vaya a Proveedores 3. Cree un nuevo proveedor con los datos de la empresa 4. Configure locale, moneda y ajustes de impuestos 5. Configure preferencias de numeración de facturas (prefijo, relleno) 6. Anote el ID del proveedor para la integración API 7. Genere una clave API desde Administrador > Claves API para acceso a la API REST **Para ejemplos de configuración de proveedores, vea `example-images` - `en-us` para un ejemplo de EE. UU. o no UE; y `eu-de` para un ejemplo de la UE.** Una vez configurado, puede establecer una plantilla XHTML y usar el ID del proveedor (mostrado en el menú Editar proveedor) para realizar llamadas a `/api/v1/invoices/generate`. ## Capacidades - Administrar proveedores y clientes con soft delete y comprobaciones de unicidad. - Crear y administrar plantillas de facturas con branding personalizado, con un predeterminado global y predeterminados por proveedor. - Generar facturas con snapshots HTML guardados en el registro de la factura. - Renderizar facturas a PDF bajo demanda. - Buscar y ordenar facturas por número, fecha, cliente, proveedor y estado. - Gestionar el estado de la factura como `unpaid` o `issued` (pagada/final). ## Ciclo de vida y estado de las facturas - Los valores de estado son `unpaid` y `issued`. `issued` se considera pagada y final. - La creación de facturas vía API siempre resulta en estado `issued`, y la API no acepta override de estado. - La creación de facturas vía UI puede marcar una factura como `unpaid` solo cuando se establece una fecha de vencimiento. - Las facturas impagas pueden marcarse como `issued` desde la página de detalles de la factura. - Las facturas emitidas no pueden revertirse a `unpaid`. ## Comportamiento de la UI - `/invoices/new` crea facturas usando el proveedor seleccionado desde la cookie (si existe). - El interruptor de impagada está deshabilitado hasta que se proporciona una fecha de vencimiento. - `/invoices` admite filtrado por proveedor (dropdown) y búsqueda por número, fecha o cliente. La búsqueda por fecha acepta ISO (`YYYY-MM-DD`) o `dd.MM.yyyy`. - La lista de facturas permite ordenar por estado; ordenar por estado alterna el orden de `unpaid` y `issued`. - `/invoices/{invoiceNumber}` muestra estado, totales, vista previa HTML guardada y ofrece una acción de una sola vía "Marcar como pagada" para facturas impagas. ## Flujo de trabajo de la UI 1) Proveedores: configure primero los datos del proveedor. El perfil del proveedor define locale, moneda, numeración de facturas y tasa de impuesto predeterminada. 2) Plantillas: cree una plantilla de branding y establezca un predeterminado. Use un predeterminado global o uno por proveedor para sobrescribir el global. 3) Clientes (opcional): puede agregar clientes manualmente, pero la generación de facturas también crea/actualiza clientes a partir de los datos ingresados. 4) Seleccionar proveedor activo: elija un proveedor predeterminado en la UI, lo que establece una cookie usada en la creación de facturas. 5) ID de proveedor para la API: abra un proveedor en modo edición y use el ID mostrado en la esquina superior izquierda. 6) Facturas: liste, busque y filtre facturas; abra una factura para revisar detalles y marcar facturas impagas como emitidas/pagadas. 7) Generar factura: ingrese datos del cliente o busque un cliente existente, agregue partidas y establezca impuestos por partida. Si una partida omite el impuesto, se aplica la tasa predeterminada del proveedor. 8) Descuentos y notas: ingrese un descuento fijo o use la calculadora de % de descuento; agregue notas y genere la factura para ver la página de resumen. La generación de facturas usa un bloqueo de escritura pesimista en el registro del proveedor para evitar condiciones de carrera al calcular el siguiente número de factura. Esto bloquea solicitudes concurrentes para el mismo proveedor hasta asignar el número. ## Autenticación **Autenticación requerida:** Todos los endpoints de la API requieren: - Bearer token en el encabezado `Authorization` (recomendado para integraciones) - Sesión activa (si inició sesión en la interfaz web) ### Autenticación con clave API (recomendado para integraciones externas) Genere una clave API en el panel de administrador y agréguela en el encabezado `Authorization`: ```bash curl -H "Authorization: Bearer YOUR_API_KEY_HERE" \ http://localhost:8080/api/v1/invoices ``` ### Autenticación por sesión (para solicitudes iniciadas desde la UI) Si inició sesión en la interfaz web, su sesión se usa automáticamente para las solicitudes de la API. **Generar una clave API:** 1. Inicie sesión en la interfaz web 2. Navegue a Administrador (menú de usuario) 3. Desplácese a la sección "Claves API" 4. Ingrese una descripción opcional y haga clic en "Generar clave" 5. **Copie la clave de inmediato** - no se mostrará nuevamente 6. La clave se muestra en formato `Authorization: Bearer {key}` **Notas de seguridad:** - Las claves API se almacenan hasheadas en la base de datos (BCrypt) - Las claves se pueden revocar en cualquier momento desde el panel de administrador - Se registra el timestamp de último uso para cada clave - Genere claves separadas para distintas aplicaciones/entornos ## REST API (Base: `/api/v1`) ### Generación de facturas `POST /api/v1/invoices/generate` **Autenticación requerida** - incluya el Bearer token en el encabezado `Authorization`. - Requiere `supplier_id` y `client`. - `response_type` admite `number` (predeterminado) o `pdf`. - `number`: devuelve JSON solo con metadatos de la factura - `pdf`: devuelve el PDF directamente con metadatos en los encabezados de respuesta - El estado siempre es `issued` para facturas generadas por la API. Ejemplo de solicitud (cliente existente por id): ```bash curl -X POST http://localhost:8080/api/v1/invoices/generate \ -H "Authorization: Bearer YOUR_API_KEY_HERE" \ -H "Content-Type: application/json" \ -d '{ "response_type": "number", "supplier_id": 1, "client": { "id": 42 }, "items": [ { "description": "Consulting", "quantity": 1, "unit_price": 1000, "tax_rate": 0.2 } ], "issue_date": "2026-01-16", "due_date": "2026-01-30", "currency_code": "EUR", "notes": "Thank you" }' ``` Ejemplo de solicitud (nuevos datos de cliente): ```bash curl -X POST http://localhost:8080/api/v1/invoices/generate \ -H "Authorization: Bearer YOUR_API_KEY_HERE" \ -H "Content-Type: application/json" \ -d '{ "response_type": "number", "supplier_id": 1, "client": { "name": "Client Co", "addressLine1": "2 Side St", "city": "Burgas", "country": "BG", "taxId": "123", "vatId": "BG123" }, "items": [ { "description": "Consulting", "quantity": 1, "unit_price": 1000, "tax_rate": 0.2 } ] }' ``` Ejemplo de respuesta (response_type: number): ```json { "status": "issued", "message": "invoice generated", "invoiceNumber": "INV-000001", "issueDate": "2026-01-16" } ``` Ejemplo de solicitud (response_type: pdf para descarga directa de PDF): ```bash curl -X POST http://localhost:8080/api/v1/invoices/generate \ -H "Authorization: Bearer YOUR_API_KEY_HERE" \ -H "Content-Type: application/json" \ -d '{ "response_type": "pdf", "supplier_id": 1, "client": { "name": "Client Co", "addressLine1": "2 Side St", "city": "Plovdiv", "country": "BG", "taxId": "123", "vatId": "BG123" }, "items": [ { "description": "Consulting", "quantity": 1, "unit_price": 1000, "tax_rate": 0.2 } ] }' \ -o invoice.pdf -i ``` Ejemplo de respuesta (response_type: pdf): ``` HTTP/1.1 200 OK Content-Type: application/pdf Content-Disposition: attachment; filename="INV-000001.pdf" X-Invoice-Number: INV-000001 X-Invoice-Status: issued X-Invoice-Issue-Date: 2026-01-16 [PDF binary data] ``` La respuesta PDF incluye metadatos de la factura en encabezados personalizados (`X-Invoice-Number`, `X-Invoice-Status`, `X-Invoice-Issue-Date`), lo que permite a su aplicación guardar los detalles de la factura mientras recibe el PDF directamente. ### Listado y filtrado de facturas `GET /api/v1/invoices` **Autenticación requerida** - incluya el Bearer token en el encabezado `Authorization`. - Filtro opcional: `status=unpaid` o `status=issued` Ejemplo de solicitud: ```bash curl -H "Authorization: Bearer YOUR_API_KEY_HERE" \ http://localhost:8080/api/v1/invoices?status=unpaid ``` Ejemplo de respuesta (filtrado): ```json [ { "invoiceNumber": "INV-000002", "status": "unpaid" } ] ``` ### Obtención de factura **Autenticación requerida** - incluya el Bearer token en el encabezado `Authorization`. Obtener metadatos de la factura: ```bash curl -H "Authorization: Bearer YOUR_API_KEY_HERE" \ http://localhost:8080/api/v1/invoices/INV-000001 ``` Descargar PDF de la factura: ```bash curl -H "Authorization: Bearer YOUR_API_KEY_HERE" \ http://localhost:8080/api/v1/invoices/INV-000001/pdf \ -o invoice.pdf ``` ### Cola asíncrona de generación de facturas Ruta alternativa a `POST /api/v1/invoices/generate` para clientes que no quieren bloquearse esperando la generación síncrona. La solicitud se persiste en una cola, un worker en segundo plano genera la factura y los clientes consultan el estado mediante polling. Ambas rutas pasan por el mismo bloqueo de la fila del proveedor, por lo que los números de factura permanecen secuenciales entre la ruta síncrona y la asíncrona. #### Enviar una solicitud de generación `POST /api/v1/invoice-requests` **Autenticación requerida** - incluya el Bearer token en el encabezado `Authorization`. El cuerpo de la solicitud es idéntico al de `POST /api/v1/invoices/generate`. El campo `response_type` se ignora - las solicitudes asíncronas solo persisten la fila de la cola; una vez completada, recupere la factura usando los endpoints estándar con el `invoiceNumber` devuelto en la respuesta de estado. ```bash curl -X POST http://localhost:8080/api/v1/invoice-requests \ -H "Authorization: Bearer YOUR_API_KEY_HERE" \ -H "Content-Type: application/json" \ -d '{ "supplier_id": 1, "client": { "id": 42 }, "items": [ { "description": "Consulting", "quantity": 1, "unit_price": 1000, "tax_rate": 0.2 } ] }' ``` Respuesta (201 Created): ```json { "requestId": 42, "status": "PENDING", "message": "invoice generation request queued", "createdAt": "2026-05-17T10:00:00" } ``` #### Consultar el estado de la solicitud `GET /api/v1/invoice-requests/{requestId}` ```bash curl -H "Authorization: Bearer YOUR_API_KEY_HERE" \ http://localhost:8080/api/v1/invoice-requests/42 ``` Respuesta cuando está `COMPLETED`: ```json { "requestId": 42, "status": "COMPLETED", "invoiceId": 107, "invoiceNumber": "INV-000042", "attempts": 1, "createdAt": "2026-05-17T10:00:00", "completedAt": "2026-05-17T10:00:02" } ``` #### Ciclo de vida de los estados ``` nueva solicitud -> PENDING worker exitoso -> COMPLETED worker lanza un error -> PENDING (reintentará) attempts >= max_attempts -> FAILED (terminal) ``` `PENDING` es el único estado reintenable. Las caídas durante el procesamiento se revierten por completo y no cuentan contra el presupuesto de reintentos. Después de `COMPLETED`, recupere la factura con `GET /api/v1/invoices/{invoiceNumber}` (JSON) o `/api/v1/invoices/{invoiceNumber}/pdf` (PDF). El worker se puede deshabilitar mediante la variable de entorno `QUEUE_ENABLED=false`. ### Partes **Autenticación requerida** - incluya el Bearer token en el encabezado `Authorization`. - `GET /api/v1/parties/client?taxId=...&vatId=...` (requiere uno de taxId/vatId) - `GET /api/v1/parties/clients/search?q=...` (mínimo 2 caracteres) - `GET /api/v1/parties/suppliers` Ejemplo: ```bash curl -H "Authorization: Bearer YOUR_API_KEY_HERE" \ http://localhost:8080/api/v1/parties/suppliers ``` ### Salud - `GET /api/v1/health` (sin autenticación) ## Plantillas y PDFs - Las plantillas viven en `invoice_templates` y deben incluir contenido HTML. - La generación de facturas requiere una plantilla predeterminada efectiva (por proveedor o global). - Los proveedores pueden reemplazar el predeterminado global con un predeterminado específico. - Las facturas generadas almacenan un snapshot HTML para re-renderizado consistente. - Los PDFs se renderizan desde el snapshot HTML guardado cuando está disponible. ### Requisitos de formato de la plantilla Las plantillas deben usar formato **XHTML** para un renderizado correcto del PDF: - Incluir declaración XML: `` - Usar namespace XHTML: `` - Todo el CSS debe ser **en línea** dentro de un `