Skip to content

Releases: nullata/nullInvoice

1.1.0

17 May 22:56

Choose a tag to compare

[1.1.0] - 2026-05-18

Added

  • Async invoice generation queue: a new alternative to the synchronous POST /api/v1/invoices/generate endpoint for callers that don't want to hold an HTTP request open for the generation time. Requests posted to POST /api/v1/invoice-requests are persisted immediately to a new invoice_requests queue table (status PENDING, attempts/maxAttempts counters, full request payload serialized to a LONGTEXT column) and a background worker generates the invoice asynchronously. Clients then poll GET /api/v1/invoice-requests/{requestId} for status, which returns PENDING / COMPLETED / FAILED plus the invoiceNumber once ready - actual invoice retrieval still uses the existing GET /api/v1/invoices/{invoiceNumber} and /pdf endpoints so the surface stays unified. Both paths funnel through the same supplier-row pessimistic lock in InvoiceService.generateInvoice, so invoice numbers remain monotonically sequential across sync and async generation - no conflicts even when both paths are driven concurrently for the same supplier.
  • Idempotent queue worker with crash-safe transaction model: each queue row is processed as one atomic transaction (SELECT ... FOR UPDATE claim, idempotency check, generate, mark COMPLETED) split across two beans (InvoiceQueueWorker polling loop + InvoiceQueueProcessor per-item transaction) to keep Spring's @Transactional proxy semantics intact. A mid-processing crash rolls the whole unit back and leaves the row at PENDING for the next poll cycle - the crash doesn't count toward the retry budget. Failure recording (attempts increment, terminal FAILED when attempts >= max_attempts) runs in a separate REQUIRES_NEW transaction so it survives the outer rollback. The new invoices.request_id column (nullable, unique, FK to invoice_requests) is the idempotency key: if a retry would otherwise create a duplicate, the worker links the queue row to the existing invoice instead. Sync-path invoices leave request_id NULL and are unaffected.
  • Configuration toggle for the queue worker: new QUEUE_ENABLED env var (default true) plumbed through docker-compose.yml, .env.example, and application.yml under nullinvoice.queue.enabled. The worker bean is annotated @ConditionalOnProperty so when disabled the entire @Scheduled component is excluded - no poller, no overhead. Additional nullinvoice.queue properties: poll-interval-ms (default 2000), batch-size (default 10), max-attempts (default 3). @EnableScheduling is now on NullInvoiceApplication.
  • Async Queue documentation: docs/extended/API.md gains a full "Async Invoice Generation Queue" section covering submit / status / retrieve flow, response shapes for PENDING / COMPLETED / FAILED, the status lifecycle diagram, polling recommendations (2–4s typical end-to-end), and when to pick async over the sync endpoint. docs/extended/CONFIGURATION.md documents the new QUEUE_ENABLED toggle and the nullinvoice.queue.* properties. The "Features" list in README.md (and all localized variants under docs/) advertises Async Generation Queue as a first-class feature.

Changed

  • ApiExceptionHandler covers the new controller: InvoiceRequestController is added to the @RestControllerAdvice basePackageClasses list, and a new @ExceptionHandler(InvoiceRequestNotFoundException.class) returns a 404 JSON ErrorResponse ("invoice request not found: {id}") when callers poll an unknown request id - same shape as the existing InvoiceNotFoundException handler.
  • Invoices entity gains a request_id field: nullable Long requestId column on invoices, populated only when the row was generated via the async queue path. Not exposed through InvoiceResponse - the field is internal idempotency plumbing for the worker, not part of the public API contract. InvoiceRepository adds findByRequestId(Long) for the worker's idempotency check. InvoiceService adds an overloaded generateInvoice(request, requestId) used only by InvoiceQueueProcessor; the existing zero-arg and markUnpaid overloads keep their signatures and behavior so the sync API, UI controller, and existing tests are unaffected.

1.0.37

30 Mar 18:12

Choose a tag to compare

Bug fix: TemplateService now handles java.sql.Date - would throw on .toInstant() call (#3 )