Pose les fondations pour permettre aux utilisateurs de créer leurs factures directement dans Rubis (en complément de l'upload OCR existant), avec snapshots immuables, numérotation strict séquentielle (art. 242 nonies A CGI) et 4 thèmes pré-faits paramétrables. Data model - organizations.invoice_settings (JSONB) : thème par défaut, accent color, préfixe et compteur de numérotation, mentions légales (pénalités, escompte), identité émetteur (SIREN/SIRET/TVA intra/RCS/capital), RIB. - clients enrichi : SIREN, TVA intra, adresse structurée (lines/zip/city /country). Le champ address legacy reste pour les clients pré-feature. - invoices enrichi : lines (JSONB), client_snapshot + issuer_snapshot figés à l'émission, amount_ht/tva, tva_breakdown, payment_terms_days, theme_slug + theme_accent_color, is_native, sequence_number (unique per org), pdf_generated_at. API - GET/PATCH /organizations/me/invoice-settings (resolveInvoiceSettings) - GET /invoice-themes (4 thèmes : classique, moderne, minimal, élégant) - POST /invoices/native (séquence strict allouée en transaction, totaux recalculés serveur, snapshots immuables) - POST /invoices/preview-pdf (stream PDF sans persister, stub Phase 1) Le rendu PDF lui-même (@react-pdf/renderer + templates) arrive en Phase 2 ; le storeNative crée bien la facture mais pdf_storage_key reste null jusqu'à Phase 2. Conformité Factur-X visée pour V1.5 (Q3-Q4 2026, avant l'échéance d'émission TPE-PME au 1er sept 2027). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
59 lines
2.2 KiB
TypeScript
59 lines
2.2 KiB
TypeScript
/**
|
|
* invoice_pdf — génération de PDF pour les factures natives.
|
|
*
|
|
* **Phase 1 stub.** L'implémentation réelle (templates @react-pdf/renderer
|
|
* + upload MinIO) arrive en Phase 2 avec packages/ui/invoice-templates/.
|
|
* Pour l'instant, `generateInvoicePdf` renvoie `null` (= pas de PDF stocké)
|
|
* et `previewInvoicePdf` throw `not_implemented` (501).
|
|
*
|
|
* Le contrat de l'interface est figé pour que la Phase 2 soit un drop-in
|
|
* remplacement sans toucher au controller : on remplace le corps de ces
|
|
* fonctions par l'appel à `@react-pdf/renderer.renderToBuffer(...)` puis
|
|
* `media_storage.uploadBuffer(...)`.
|
|
*/
|
|
|
|
import type Invoice from '#models/invoice'
|
|
import { Exception } from '@adonisjs/core/exceptions'
|
|
|
|
export interface InvoiceRenderContext {
|
|
/** L'invoice complet, snapshots compris. */
|
|
invoice: Invoice
|
|
/** Settings résolus (themeSlug, accentColor, issuer, rib…). */
|
|
// Le type complet est dans #services/invoice_settings → ResolvedInvoiceSettings,
|
|
// mais comme c'est un stub on garde unknown pour ne pas créer de couplage
|
|
// que Phase 2 devra de toute façon retravailler.
|
|
resolvedSettings: unknown
|
|
}
|
|
|
|
export interface GeneratedPdf {
|
|
/** Clé MinIO sous laquelle le PDF est stocké. */
|
|
storageKey: string
|
|
/** Taille du PDF en bytes. */
|
|
bytes: number
|
|
}
|
|
|
|
/**
|
|
* Génère le PDF de la facture et l'upload sur MinIO. Stub Phase 1.
|
|
*
|
|
* Phase 2 : renderToBuffer(<Theme {...props} />) → uploadBuffer → storageKey.
|
|
*/
|
|
export async function generateInvoicePdf(_ctx: InvoiceRenderContext): Promise<GeneratedPdf | null> {
|
|
// Phase 1 : pas de génération. Le controller persiste l'invoice avec
|
|
// pdfStorageKey=null et l'UI affichera "PDF en cours de génération"
|
|
// (ou un placeholder). La Phase 2 active la vraie génération.
|
|
return null
|
|
}
|
|
|
|
/**
|
|
* Renvoie un buffer PDF pour preview (sans persister). Stub Phase 1.
|
|
*
|
|
* Phase 2 : même rendu que generateInvoicePdf, mais retourne le buffer
|
|
* directement au lieu d'uploader.
|
|
*/
|
|
export async function previewInvoicePdf(_ctx: InvoiceRenderContext): Promise<Buffer> {
|
|
throw new Exception('PDF preview not yet implemented (Phase 2)', {
|
|
status: 501,
|
|
code: 'not_implemented',
|
|
})
|
|
}
|