Aller au contenu

Cycle de vie de la facturation

Ce guide parcourt toute la vie d'une facture client : brouillon, finalisation, envoi, encaissement, rapprochement.

Créer un brouillon

<?php

use Pennylane\Sdk\Pennylane;

$client = new Pennylane(); // lit PENNYLANE_API_TOKEN

$invoice = $client->customerInvoices->create(
    date: '2026-07-08',
    deadline: '2026-08-07',
    customerId: 123,
    invoiceLines: [
        // Avec un produit : le prix unitaire, la TVA et le libellé viennent du catalogue
        ['product_id' => 45, 'quantity' => '2'],
        // Ou une ligne standard
        [
            'label' => 'On-site setup',
            'quantity' => '1',
            'raw_currency_unit_price' => '500.00',
            'unit' => 'day',
            'vat_rate' => 'FR_200',
        ],
    ],
);
echo $invoice->status; // draft

Les brouillons peuvent être modifiés (update()) et supprimés (delete()) librement.

Variantes de ligne de facture

Une ligne de facture est soit basée sur un produit (seuls product_id et quantity sont requis, le reste est complété à partir du produit), soit standard (label, quantity, raw_currency_unit_price, unit et vat_rate sont tous requis). unit est obligatoire sur les lignes standard : il n'existe aucune valeur de repli pour ce champ.

Les montants sont des chaînes numériques

L'API transporte les montants sous forme de chaînes pour éviter les arrondis liés aux flottants. Passez "500.00", jamais un flottant. Les champs monétaires de la réponse (currencyAmount, tax, debit, credit, etc.) sont typés comme des chaînes nullables pour la même raison.

Pas d'idempotence sur les créations

Poster deux fois la même facture crée un doublon : l'API ne déduplique pas. C'est pourquoi le SDK ne relance jamais automatiquement un POST après une erreur serveur ou une connexion perdue. Ne relancez jamais vous-même un appel de création à l'aveugle. Si votre pipeline peut rejouer des requêtes, dédupliquez de votre côté, par exemple en suivant votre propre externalReference et en vérifiant sa présence avant de retenter.

Finaliser

La finalisation attribue à la facture son numéro légal définitif. Il n'y a pas de retour en arrière (règles comptables), seulement des avoirs.

$invoice = $client->customerInvoices->finalize($invoice->id);
echo $invoice->invoiceNumber; // ex. F-2026-0042

Envoyer

$client->customerInvoices->sendByEmail($invoice->id);

Ou via le réseau français de facturation électronique pour le B2B, voir le guide de facturation électronique :

$client->customerInvoices->sendToPa($invoice->id);

À partir d'un devis

$quote = $client->quotes->create(
    date: '2026-07-08',
    deadline: '2026-08-07',
    customerId: 123,
    invoiceLines: [/* ... */],
);
$client->quotes->sendByEmail($quote->id);
// une fois accepté :
$invoice = $client->customerInvoices->createFromQuote(quoteId: $quote->id, draft: true);

Encaisser et rapprocher

// Marquer comme payée manuellement
$client->customerInvoices->markAsPaid($invoice->id);

// Ou rapprocher une transaction bancaire réelle avec la facture
$client->customerInvoices->matchTransaction($invoice->id, transactionId: 987);
$client->customerInvoices->listMatchedTransactions($invoice->id);

// Paiements enregistrés sur la facture
$client->customerInvoices->listPayments($invoice->id);

Avoirs

Un avoir (credit note) est une facture client à montants négatifs. Reliez-le à la facture qu'il corrige :

$creditNote = $client->customerInvoices->create(
    date: '2026-07-08',
    deadline: '2026-08-07',
    customerId: 123,
    invoiceLines: [['product_id' => 45, 'quantity' => '-2']],
);
$client->customerInvoices->finalize($creditNote->id);
$client->customerInvoices->linkCreditNote($invoice->id, creditNoteId: $creditNote->id);

Importer des factures existantes

Les factures produites en dehors de Pennylane (un autre outil, une marketplace) peuvent être importées. Pour un simple PDF, téléversez d'abord le fichier, puis référencez-le :

use Pennylane\Sdk\FileUpload;

// 1. Téléverser le PDF en tant que pièce jointe
$attachment = $client->fileAttachments->create(FileUpload::fromPath('path/to/invoice.pdf'));

// 2. Importer la facture en la référençant
$imported = $client->customerInvoices->importFromFile(
    fileAttachmentId: $attachment->id,
    date: '2026-07-01',
    deadline: '2026-07-31',
    customerId: 123,
    currencyAmountBeforeTax: '100.00',
    currencyAmount: '120.00',
    currencyTax: '20.00',
    invoiceLines: [/* ... */],
);

// Les factures électroniques structurées (Factur-X, UBL, CII) se téléversent directement
$imported = $client->customerInvoices->importEInvoice(FileUpload::fromPath('path/to/factur-x.pdf'));

Factures récurrentes

$client->billingSubscriptions crée des abonnements de facturation qui génèrent des factures selon un calendrier (hebdomadaire, mensuel, annuel, etc.). Consultez la référence de l'API pour les options de récurrence.

Synchroniser les changements de façon incrémentale

Plutôt que de tout relister, interrogez le flux de changements :

foreach ($client->changelogs->customerInvoices() as $event) {
    // ...
}