Opérations comptables¶
Au-delà de la facturation, l'API expose le cœur comptable : journaux, plan comptable, écritures, lettrage, balance générale et exports légaux.
Plan comptable et journaux¶
<?php
use Pennylane\Sdk\Pennylane;
$client = new Pennylane(); // lit PENNYLANE_API_TOKEN
// Plan comptable
foreach ($client->ledgerAccounts->list() as $account) {
echo $account->number . ' ' . $account->label . PHP_EOL;
}
$client->ledgerAccounts->create(number: '706100', label: 'Prestations de services');
// Journaux
foreach ($client->journals->list() as $journal) {
echo $journal->code . ' ' . $journal->label . PHP_EOL;
}
Écritures comptables (écritures)¶
Une écriture est un ensemble équilibré de lignes de débit et de crédit :
$entry = $client->ledgerEntries->create(
date: '2026-07-08',
label: 'Vente prestation',
journalId: 12,
ledgerEntryLines: [
['ledger_account_id' => 411, 'debit' => '120.00', 'credit' => '0.00'],
['ledger_account_id' => 706, 'debit' => '0.00', 'credit' => '100.00'],
['ledger_account_id' => 445, 'debit' => '0.00', 'credit' => '20.00'],
],
);
Débit et crédit sont tous deux requis
Chaque ligne d'écriture doit porter à la fois une clé debit et une clé credit, même si un seul côté est réellement utilisé : mettez "0.00" sur le côté non utilisé. Les lignes doivent être équilibrées, c'est-à-dire que le total des débits doit égaler le total des crédits sur l'ensemble de l'écriture. Si ce n'est pas le cas, l'API répond 422 et le SDK lève une ValidationException avec les totaux dans $exception->details.
Pas d'idempotence sur les créations
Poster deux fois la même écriture crée un doublon : l'API ne déduplique pas. Le SDK ne relance jamais automatiquement un POST après une erreur serveur pour cette raison. Dédupliquez de votre côté si votre pipeline peut rejouer les requêtes.
Lettrage (lettrage)¶
Le lettrage relie des lignes d'écriture qui se soldent mutuellement, typiquement une ligne de facture avec sa ligne de paiement :
$client->ledgerEntryLines->letter(
ledgerEntryLineIds: [111, 222],
unbalancedLetteringStrategy: 'none',
);
$client->ledgerEntryLines->unletter(
ledgerEntryLineIds: [111, 222],
unbalancedLetteringStrategy: 'none',
);
$client->ledgerEntryLines->listLetteredLines(111);
unbalancedLetteringStrategy vaut "none" pour rejeter un lettrage non équilibré avec une erreur, ou "partial" pour l'autoriser.
Balance générale (balance générale)¶
foreach ($client->trialBalance->list(
periodStart: '2026-01-01',
periodEnd: '2026-06-30',
) as $row) {
echo $row->number . ' ' . $row->debits . ' ' . $row->credits . PHP_EOL;
}
Les bornes de période sont obligatoires
Contrairement à la plupart des endpoints de liste, periodStart et periodEnd sont des arguments obligatoires ici, pas des filtres optionnels : la balance générale a toujours besoin d'une période explicite pour être calculée.
Catégories analytiques¶
L'analytique de Pennylane permet de tagger presque tout (factures, transactions, lignes d'écriture) avec des catégories organisées en groupes (axes) :
$groups = $client->categoryGroups->list();
$client->categories->create(label: 'Agence Lyon', categoryGroupId: 3);
$client->customerInvoices->categorize(
$invoiceId,
categories: [['id' => 42, 'weight' => '1.0']],
);
Les lignes d'écriture portent elles-mêmes leurs propres catégories analytiques :
$client->ledgerEntryLines->categorize(
$ledgerEntryLineId,
categories: [['id' => 42, 'weight' => '1.0']],
);
Exports légaux : FEC, grand livre¶
Les exports s'exécutent de façon asynchrone : créez l'export, puis interrogez-le jusqu'à ce que le fichier soit prêt.
$export = $client->exports->fecs->create(periodStart: '2026-01-01', periodEnd: '2026-12-31');
while (!in_array($export->status, ['ready', 'failed'], true)) {
sleep(5);
$export = $client->exports->fecs->get($export->id);
}
echo $export->fileUrl; // lien de téléchargement
Même logique pour $client->exports->generalLedgers et $client->exports->analyticalGeneralLedgers.
Données bancaires¶
use Pennylane\Sdk\Filter;
foreach ($client->bankAccounts->list() as $account) {
// ...
}
foreach ($client->transactions->list(filter: [Filter::gte('date', '2026-01-01')]) as $tx) {
// ...
}
// Rapprochement : quelles factures cette transaction règle-t-elle ?
$client->transactions->listMatchedInvoices($tx->id);