# logo nullInvoice **nullInvoice** è un microservizio Spring Boot per la **generazione e gestione automatizzata delle fatture** con **modelli HTML completamente personalizzabili**, progettato per l'integrazione con webstore e piattaforme SaaS. ## Panoramica Le aziende usano nullInvoice per gestire la generazione delle fatture dopo che le vendite sono state completate. I fornitori vengono configurati una sola volta tramite la UI web, poi la sua applicazione chiama la REST API per generare fatture conformi on-demand. **Come funziona:** 1. **Configurazione**: Configuri i fornitori nella UI con dati aziendali, locale, valuta, aliquote fiscali, branding personalizzato e modelli di fattura 2. **Autenticazione**: Generi chiavi API dalla Dashboard amministratore per un accesso sicuro alla REST API 3. **Integrazione**: Il suo webstore/SaaS effettua chiamate API autenticate a `/api/v1/invoices/generate` usando l'ID del fornitore 4. **Generazione**: Le fatture vengono create da modelli HTML completamente personalizzabili e restituite come JSON o PDF con metadati negli header 5. **Consegna**: La sua applicazione riceve la fattura e può inoltrarla ai clienti o conservarla per archivio ### Flusso di integrazione tipico ``` ┌─────────────────┐ │ Cliente │ │ completa │ │ l'acquisto │ └────────┬────────┘ │ ▼ ┌─────────────────────────────────────────┐ │ La sua piattaforma Webstore/SaaS │ │ ───────────────────────────────────── │ │ 1. Elabora il pagamento │ │ 2. Emette ricevuta digitale │ │ (obbligatoria) │ │ 3. Il cliente richiede fattura? ────┐ │ └──────────────────────────────────────┼──┘ │ │ Chiamata API ▼ ┌──────────────────────────────┐ │ Servizio nullInvoice │ │ ────────────────────────── │ │ POST /api/v1/invoices/ │ │ generate │ │ │ │ - Valida ID fornitore │ │ - Applica modello │ │ - Salva snapshot HTML │ │ - Genera PDF │ │ - Restituisce la fattura │ └──────────────┬───────────────┘ │ │ Risposta (JSON o PDF) ▼ ┌──────────────────────────────────────────┐ │ La sua piattaforma Webstore/SaaS │ │ ────────────────────────────────────── │ │ - Riceve metadati JSON O file PDF │ │ - Salva il numero fattura per archivio │ │ - Invia il PDF al cliente via email │ └──────────────────────────────────────────┘ ``` ### Funzionalità principali **Modelli completamente personalizzabili** - Le fatture si basano su modelli HTML definiti dall'utente con CSS inline - I modelli supportano oltre 30 placeholder per dati di fornitore, cliente e finanziari - I modelli predefiniti per fornitore consentono branding diverso per unità di business **Immutabilità dei documenti** - Ogni fattura memorizza uno **snapshot dell'HTML parsato** al momento della generazione - Le modifiche ai modelli non influenzano le fatture già generate - Le fatture possono essere rigenerate in modo coerente dallo snapshot salvato - Le autorizzazioni del database impediscono l'eliminazione dei record delle fatture (compliance finanziaria) **Pronto per multi-tenant** - Configuri più fornitori con impostazioni indipendenti (locale, valuta, numerazione, branding) - I chiamanti API specificano l'ID del fornitore per generare fatture per diverse entità aziendali **Consegna flessibile** - Restituisce i metadati della fattura come JSON per archiviazione (`response_type: number`) - Restituisce il PDF direttamente con metadati negli header per consegna immediata (`response_type: pdf`) - Recupero PDF successivo tramite `/api/v1/invoices/{invoiceNumber}/pdf` **Coda asincrona di generazione** - Percorso alternativo per integrazioni che non vogliono attendere la generazione in modo sincrono - La richiesta viene persistita in una coda, un worker in background genera la fattura e i client interrogano lo stato tramite polling - Stesse garanzie di numerazione del percorso sincrono (blocco condiviso del fornitore) - Il worker può essere disabilitato tramite `QUEUE_ENABLED=false` (predefinito `true`) **Documentazione OpenAPI** - Documentazione API interattiva disponibile su `/swagger` - Specifica OpenAPI JSON su `/openapi` - Esempi completi di request/response e funzionalità try-it-out ## Avvisi importanti **⚠️ Avviso di sicurezza** nullInvoice include **autenticazione integrata** (login UI basato su sessione + autenticazione con chiave API per gli endpoint REST). Anche se offre sicurezza di default, l'applicazione è comunque destinata a deployment in reti interne/private. **Deploy consigliato:** - Dietro un firewall o VPN - All'interno di una rete privata accessibile solo dalle sue applicazioni fidate - Con HTTPS/TLS abilitato per tutte le connessioni - Dietro un reverse proxy con rate limiting configurato **Per i deploy in produzione sono necessarie misure di sicurezza aggiuntive:** - Abilitare HTTPS/TLS - Configurare il rate limiting a livello di reverse proxy - Usare password amministratore robuste - Ruotare regolarmente le chiavi API - Conservare le chiavi API in un sistema sicuro di gestione segreti (HashiCorp Vault, AWS Secrets Manager, ecc.) Veda la sezione [Sicurezza e best practice](#sicurezza-e-best-practice) per una checklist completa di produzione. **⚠️ Questa applicazione NON è uno strumento contabile.** nullInvoice è una **pipeline di generazione fatture** progettata per creare, archiviare e consegnare documenti di fattura. Non: - Traccia pagamenti o stati di pagamento oltre i flag base "unpaid/issued" - Gestisce crediti o debiti - Genera report finanziari o bilanci - Si integra con sistemi contabili (registri, giornali, ecc.) - Gestisce contabilità, riconciliazioni o dichiarazioni fiscali Per una gestione finanziaria completa, integri nullInvoice con un sistema contabile dedicato. Usi questo servizio per generare i documenti di fattura, poi li importi nel suo software contabile per tracciamento e compliance. ## Stack - Java 21, Spring Boot 3.5.3 - MariaDB + JPA - Thymeleaf (UI) - OpenHTMLToPDF (PDFBox) - OpenAPI su `/openapi`, Swagger UI su `/swagger` ## Prerequisiti **Per deployment Docker (consigliato):** - Docker - Docker Compose **Per sviluppo locale:** - Java 21 (JDK - Eclipse Temurin o OpenJDK) - Maven 3.9+ - MariaDB 10.5+ (o MySQL 8.0+) - Binario standalone di Tailwind CSS (per build CSS - da [GitHub releases](https://github.com/tailwindlabs/tailwindcss/releases)) ## Configurazione database **Creare il database:** ```sql CREATE DATABASE nullinvoice CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; ``` **Creare un utente applicazione dedicato con permessi limitati:** ```sql CREATE USER 'nullinvoice'@'localhost' IDENTIFIED BY 'your_secure_password'; -- Conceda i permessi per operazioni normali e migrazioni dello schema GRANT SELECT, INSERT, UPDATE ON nullinvoice.* TO 'nullinvoice'@'localhost'; GRANT CREATE, ALTER, INDEX, REFERENCES ON nullinvoice.* TO 'nullinvoice'@'localhost'; FLUSH PRIVILEGES; ``` **Per accesso remoto, regoli l'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: perché questi permessi limitati?** Le fatture sono **documenti finanziari immutabili** che non devono mai essere eliminate dopo la creazione. L'utente dell'applicazione è intenzionalmente limitato e non può: - `DELETE` - non può eliminare record - `DROP` - non può eliminare tabelle o database - `TRUNCATE` - non può svuotare tabelle - `GRANT` - non può concedere permessi ad altri Questo garantisce l'integrità dei dati e la conformità ai requisiti di conservazione dei record finanziari. Il permesso `UPDATE` è concesso per i cambi di stato (es. segnare fatture come pagate) e soft delete sui record delle parti. ### Gestione dello schema - Lo schema del database è gestito da migrazioni **Flyway** - Schema iniziale: `nullInvoice/src/main/resources/db/migration/V1__initial_schema.sql` - Lo schema viene creato/aggiornato automaticamente all'avvio dell'applicazione - La modalità DDL di Hibernate è impostata su `none` (Flyway gestisce tutti i cambi di schema) ## Configurazione Un file di configurazione di esempio è fornito in `.env.example`. Lo copi in `.env` e adatti i valori per il suo ambiente. Variabili d'ambiente (valori predefiniti tra parentesi): - `TZ` - **RICHIESTO** fuso orario di sistema (Europe/Sofia) - `APP_PORT` (8080) - `DB_HOST` (localhost) - `DB_PORT` (3306) - `DB_USER` (nullinvoice) - `DB_PASSWORD` (vuoto) - `DB_NAME` (nullinvoice) - `DB_PARAMS` - **RICHIESTO** parametri JDBC aggiunti all'URL di connessione ### **IMPORTANTE: Configurazione fuso orario** **DEVE impostare il fuso orario in DUE punti:** 1. **`TZ`** variabile d'ambiente - imposta il fuso orario di sistema/applicazione 2. **`serverTimezone`** parametro in `DB_PARAMS` - imposta il fuso orario della connessione al database **Entrambi i valori DEVONO corrispondere al fuso orario del server database** per interpretare e salvare correttamente date e orari. Esempio di configurazione: ```bash TZ=Europe/Sofia DB_PARAMS=?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Europe/Sofia ``` Fusi orari comuni: `UTC`, `Europe/London`, `America/New_York`, `Asia/Tokyo`. Veda [la lista completa](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones). **Fusi orari non allineati causeranno date e timestamp errati nelle fatture.** ### Configurazione iniziale amministratore Al primo avvio, l'applicazione reindirizza a `/setup` per creare l'account amministratore iniziale. Questo è obbligatorio prima di accedere a qualsiasi altra funzionalità. **Per reimpostare e creare un nuovo account amministratore (recupero di emergenza):** 1. Arresti l'applicazione 2. Trunchi le tabelle `users` e `api_keys` nel database 3. Riavvii l'applicazione 4. Completi nuovamente il flusso `/setup` **Raccomandazioni di sicurezza:** - Usi una password forte per l'account amministratore - Imposti un suggerimento password (opzionale ma consigliato) - Generi chiavi API separate per diversi ambienti (dev, staging, prod) - Revochi le chiavi API inutilizzate ## Sicurezza e best practice **Architettura di autenticazione:** - **Accesso UI:** Autenticazione basata su sessione con form login - **Accesso API:** Autenticazione Bearer token stateless (chiavi API) - **Password:** BCrypt con 10 round - **Chiavi API:** Formato UUID, hashate con BCrypt, mostrate una sola volta alla generazione **Durata della sessione:** Di default, le sessioni UI scadono dopo **30 minuti di inattività** (default di Spring Boot/Tomcat). Gli utenti verranno automaticamente disconnessi e reindirizzati alla pagina di login. Per personalizzare la durata della sessione, aggiunga il seguente codice a `nullInvoice/src/main/resources/application.yml`: ```yaml server: servlet: session: timeout: 60m # Opzioni: 15m, 30m, 1h, 2h, ecc. ``` Valori di timeout comuni: - `15m` - 15 minuti (sicurezza più rigorosa) - `30m` - 30 minuti (predefinito) - `1h` - 1 ora (comodità per utenti attivi) - `8h` - 8 ore (esteso per sessioni lunghe) **Checklist per la produzione:** - Abilitare HTTPS/TLS per tutte le connessioni - Usare password amministratore robuste - Generare chiavi API separate per ogni applicazione/ambiente - Deploy dietro firewall o VPN - Configurare il rate limiting a livello di reverse proxy - Configurare logging e monitoraggio - Rivedere regolarmente l'uso delle chiavi API (timestamp dell'ultimo utilizzo) - Revocare immediatamente chiavi API inutilizzate o compromesse - Conservare le chiavi API nelle variabili d'ambiente, mai nel codice - Usare un sistema sicuro di gestione segreti (HashiCorp Vault, AWS Secrets Manager, ecc.) **Protezione CSRF:** - Abilitata per tutti i form UI - Disabilitata per gli endpoint API (auth Bearer stateless) **Sicurezza al primo avvio:** - L'applicazione è inutilizzabile finché non viene creato un account amministratore tramite `/setup` - La pagina di setup è accessibile solo quando non esiste un amministratore - Dopo il setup è richiesto il login per tutte le funzionalità ## Docker Compose **Immagini Docker ufficiali:** Sono disponibili immagini precompilate su [Docker Hub](https://hub.docker.com). Cerchi l'immagine ufficiale di nullInvoice per saltare il build. Build senza cache: ```bash docker compose build --no-cache ``` Avvii lo stack: ```bash docker compose up -d ``` Arresti lo stack: ```bash docker compose down ``` ## Sviluppo locale (senza Docker) ### Configurazione 1. Si assicuri di avere un'istanza MariaDB in esecuzione e crei il database. Veda [Configurazione database](#configurazione-database) 2. Build di Tailwind CSS (necessario prima del primo avvio): ```bash ./build-tailwind.sh ``` 3. Build del progetto con Maven: ```bash cd nullInvoice mvn clean package ``` 4. Avvii l'applicazione: ```bash java -jar target/nullinvoice-0.0.1-SNAPSHOT.jar ``` ### Uso di NetBeans IDE Il progetto è stato costruito con NetBeans e può essere aperto come progetto Maven con il plugin Spring Boot. **Impostare le variabili d'ambiente in NetBeans:** Opzione 1 - Tramite IDE: 1. Clic destro sul progetto >> Properties 2. Navigare in Actions >> Run 3. Impostare le variabili d'ambiente: `APP_PORT`, `DB_NAME`, `DB_USER`, `DB_PASSWORD` Opzione 2 - Modifica diretta: - Modifichi `nbactions.xml` nella root del progetto ## Per iniziare ### Configurazione al primo avvio Al primo accesso verrà reindirizzato a `/setup` per creare l'account amministratore iniziale: 1. Apra `http://localhost:8080` (o l'URL configurato) 2. Verrà reindirizzato automaticamente a `/setup` 3. Crei un account amministratore con nome utente, password e suggerimento opzionale 4. Dopo il setup verrà reindirizzato a `/login` **Accedi:** - Acceda alla pagina di login su `/login` - Usi le credenziali amministratore che ha creato - Il suggerimento password è disponibile tramite l'icona info (se configurato) **Dashboard amministratore:** Dopo il login, acceda alla dashboard amministratore dal menu utente: - Cambiare la password amministratore - Generare chiavi API per l'accesso alla REST API - Revocare chiavi API - Visualizzare l'uso delle chiavi API (timestamp dell'ultimo utilizzo) ### Configurare il primo fornitore **Prerequisiti:** - Configurazione iniziale completata (account amministratore creato) - Login con credenziali amministratore Prima di poter generare fatture via API, deve configurare almeno un fornitore tramite la UI web. I fornitori definiscono dati aziendali, locale, valuta, aliquote fiscali, branding personalizzato (tramite modelli) e numerazione fatture per la generazione. 1. Effettui il login nella UI web su `http://localhost:8080` (o la porta configurata) 2. Vada a Fornitori 3. Crei un nuovo fornitore con i dati aziendali 4. Configuri locale, valuta e impostazioni fiscali 5. Imposti le preferenze di numerazione fatture (prefisso, padding) 6. Annoti l'ID fornitore per l'integrazione API 7. Generi una chiave API da Amministratore > Chiavi API per l'accesso alla REST API **Per esempi di configurazione del fornitore, veda `example-images` - `en-us` per un esempio USA o non UE; e `eu-de` per un esempio UE.** Una volta configurato, può impostare un modello XHTML e usare l'ID fornitore (mostrato nel menu Modifica fornitore) per effettuare chiamate a `/api/v1/invoices/generate`. ## Capacità - Gestire fornitori e clienti con soft delete e controlli di unicità. - Creare e gestire modelli di fattura con branding personalizzato, con un predefinito globale e predefiniti per fornitore. - Generare fatture con snapshot HTML salvati nel record della fattura. - Renderizzare fatture in PDF su richiesta. - Cercare e ordinare fatture per numero, data, cliente, fornitore e stato. - Gestire lo stato della fattura come `unpaid` o `issued` (pagata/finale). ## Ciclo di vita e stato delle fatture - I valori di stato sono `unpaid` e `issued`. `issued` è considerato pagata e finale. - La creazione di una fattura via API produce sempre lo stato `issued` e l'API non accetta override dello stato. - La creazione via UI può contrassegnare una fattura come `unpaid` solo quando è impostata una data di scadenza. - Le fatture non pagate possono essere contrassegnate come `issued` dalla pagina dettagli fattura. - Le fatture emesse non possono essere riportate a `unpaid`. ## Comportamento UI - `/invoices/new` crea fatture usando il fornitore selezionato dal cookie (se presente). - Il toggle per `unpaid` è disabilitato finché non viene fornita una data di scadenza. - `/invoices` supporta il filtro per fornitore (dropdown) e la ricerca per numero, data o cliente. La ricerca per data accetta ISO (`YYYY-MM-DD`) o `dd.MM.yyyy`. - L'elenco fatture include l'ordinamento per stato; l'ordinamento per stato alterna `unpaid` e `issued`. - `/invoices/{invoiceNumber}` mostra stato, totali, anteprima HTML salvata e offre un'azione unidirezionale "Segna come pagata" per le fatture non pagate. ## Flusso di lavoro UI 1) Fornitori: configuri prima i dati del fornitore. Il profilo fornitore guida locale, valuta, numerazione fatture e aliquota fiscale predefinita. 2) Modelli: crei un modello per il branding personalizzato e imposti un predefinito. Usi un predefinito globale o uno specifico per fornitore per sovrascrivere la scelta globale. 3) Clienti (opzionale): può aggiungere clienti manualmente, ma la generazione fatture crea/aggiorna anche i clienti dai dati inseriti. 4) Selezioni il fornitore attivo: scelga un fornitore predefinito nella UI, che imposta un cookie usato durante la creazione delle fatture. 5) ID fornitore per API: apra un fornitore in modalità modifica e usi l'ID mostrato nell'angolo in alto a sinistra della pagina. 6) Fatture: elenchi, cerchi e filtri le fatture; apra una fattura per rivedere i dettagli e contrassegnare le fatture non pagate come emesse/pagate. 7) Generare fattura: inserisca i dati del cliente o cerchi un cliente esistente, aggiunga voci e imposti l'imposta per voce. Se una voce omette l'imposta, si applica l'aliquota predefinita del fornitore. 8) Sconti e note: inserisca uno sconto fisso o usi il calcolatore % di sconto; aggiunga note e generi la fattura per vedere la pagina di riepilogo. La generazione delle fatture usa un lock di scrittura pessimista sul record del fornitore per evitare race condition nel calcolo del prossimo numero fattura. Questo blocca richieste concorrenti per lo stesso fornitore fino all'assegnazione del numero. ## Autenticazione **Autenticazione richiesta:** Tutti gli endpoint API richiedono: - Bearer token nell'header `Authorization` (consigliato per le integrazioni) - Sessione attiva (se è loggato tramite UI web) ### Autenticazione con chiave API (consigliata per integrazioni esterne) Generi una chiave API dalla dashboard amministratore e la includa nell'header `Authorization`: ```bash curl -H "Authorization: Bearer YOUR_API_KEY_HERE" \ http://localhost:8080/api/v1/invoices ``` ### Autenticazione di sessione (per richieste avviate dalla UI) Se è loggato tramite la UI web, la sua sessione viene usata automaticamente per le richieste API. **Generare una chiave API:** 1. Effettui il login nella UI web 2. Navighi a Amministratore (menu utente) 3. Scorra alla sezione "Chiavi API" 4. Inserisca una descrizione opzionale e clicchi "Genera chiave" 5. **Copi immediatamente la chiave** - non verrà mostrata di nuovo 6. La chiave viene mostrata nel formato `Authorization: Bearer {key}` **Note sulla sicurezza:** - Le chiavi API sono hashate nel database (BCrypt) - Le chiavi possono essere revocate in qualsiasi momento dalla dashboard amministratore - Per ogni chiave è tracciato il timestamp dell'ultimo utilizzo - Generi chiavi separate per diverse applicazioni/ambienti ## REST API (Base: `/api/v1`) ### Generazione fatture `POST /api/v1/invoices/generate` **Autenticazione richiesta** - includa il Bearer token nell'header `Authorization`. - Richiede `supplier_id` e `client`. - `response_type` supporta `number` (predefinito) o `pdf`. - `number`: restituisce JSON solo con metadati della fattura - `pdf`: restituisce il PDF direttamente con metadati negli header di risposta - Lo stato è sempre `issued` per le fatture generate via API. Esempio di richiesta (cliente esistente per 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" }' ``` Esempio di richiesta (nuovi dati 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 } ] }' ``` Esempio di risposta (response_type: number): ```json { "status": "issued", "message": "invoice generated", "invoiceNumber": "INV-000001", "issueDate": "2026-01-16" } ``` Esempio di richiesta (response_type: pdf per download diretto del 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 ``` Esempio di risposta (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 risposta PDF include i metadati della fattura negli header di risposta personalizzati (`X-Invoice-Number`, `X-Invoice-Status`, `X-Invoice-Issue-Date`), consentendo alla sua applicazione di salvare i dettagli della fattura mentre riceve il PDF direttamente. ### Elenco e filtro fatture `GET /api/v1/invoices` **Autenticazione richiesta** - includa il Bearer token nell'header `Authorization`. - Filtro opzionale: `status=unpaid` o `status=issued` Esempio di richiesta: ```bash curl -H "Authorization: Bearer YOUR_API_KEY_HERE" \ http://localhost:8080/api/v1/invoices?status=unpaid ``` Esempio di risposta (filtrato): ```json [ { "invoiceNumber": "INV-000002", "status": "unpaid" } ] ``` ### Recupero fattura **Autenticazione richiesta** - includa il Bearer token nell'header `Authorization`. Recupero metadati fattura: ```bash curl -H "Authorization: Bearer YOUR_API_KEY_HERE" \ http://localhost:8080/api/v1/invoices/INV-000001 ``` Download PDF fattura: ```bash curl -H "Authorization: Bearer YOUR_API_KEY_HERE" \ http://localhost:8080/api/v1/invoices/INV-000001/pdf \ -o invoice.pdf ``` ### Coda asincrona di generazione fatture Percorso alternativo a `POST /api/v1/invoices/generate` per chiamanti che non vogliono bloccarsi in attesa della generazione sincrona. La richiesta viene persistita in una coda, un worker in background genera la fattura e i client interrogano lo stato tramite polling. Entrambi i percorsi passano attraverso lo stesso blocco sulla riga del fornitore, quindi i numeri di fattura rimangono sequenziali tra percorso sincrono e asincrono. #### Invio di una richiesta di generazione `POST /api/v1/invoice-requests` **Autenticazione richiesta** - includa il Bearer token nell'header `Authorization`. Il corpo della richiesta è identico a `POST /api/v1/invoices/generate`. Il campo `response_type` viene ignorato - le richieste asincrone persistono solo la riga della coda; una volta completata, recuperi la fattura usando gli endpoint standard con il valore `invoiceNumber` restituito nella risposta di stato. ```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 } ] }' ``` Risposta (201 Created): ```json { "requestId": 42, "status": "PENDING", "message": "invoice generation request queued", "createdAt": "2026-05-17T10:00:00" } ``` #### Verifica dello stato della richiesta `GET /api/v1/invoice-requests/{requestId}` ```bash curl -H "Authorization: Bearer YOUR_API_KEY_HERE" \ http://localhost:8080/api/v1/invoice-requests/42 ``` Risposta quando è `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 di vita degli stati ``` nuova richiesta -> PENDING worker con successo -> COMPLETED worker solleva un errore -> PENDING (riproverà) attempts >= max_attempts -> FAILED (terminale) ``` `PENDING` è l'unico stato riprovabile. I crash durante l'elaborazione vengono ripristinati completamente e non consumano dal budget di retry. Dopo `COMPLETED`, recuperi la fattura con `GET /api/v1/invoices/{invoiceNumber}` (JSON) o `/api/v1/invoices/{invoiceNumber}/pdf` (PDF). Il worker può essere disabilitato tramite la variabile d'ambiente `QUEUE_ENABLED=false`. ### Parti **Autenticazione richiesta** - includa il Bearer token nell'header `Authorization`. - `GET /api/v1/parties/client?taxId=...&vatId=...` (richiede uno tra taxId/vatId) - `GET /api/v1/parties/clients/search?q=...` (minimo 2 caratteri) - `GET /api/v1/parties/suppliers` Esempio: ```bash curl -H "Authorization: Bearer YOUR_API_KEY_HERE" \ http://localhost:8080/api/v1/parties/suppliers ``` ### Health - `GET /api/v1/health` (nessuna autenticazione richiesta) ## Modelli e PDF - I modelli si trovano in `invoice_templates` e devono includere contenuto HTML. - La generazione delle fatture richiede un modello predefinito effettivo (specifico per fornitore o globale). - I fornitori possono sovrascrivere il predefinito globale con un modello specifico. - Le fatture generate memorizzano uno snapshot HTML per un re-render coerente. - I PDF vengono renderizzati dallo snapshot HTML salvato quando disponibile. ### Requisiti del formato del modello I modelli devono essere in formato **XHTML** per un rendering PDF corretto: - Includere la dichiarazione XML: `` - Usare il namespace XHTML: `` - Tutto il CSS deve essere **inline** dentro un tag `