Patterns
Devis, acomptes et facture de solde
Du devis signé à la facture acompte (TVA exigible) puis au solde
Le cycle devis → acompte → solde est un pattern fiscal sensible. Un devis (`quotes`) est hors chaîne ISCA (audit trail SHA-256 séparé). Une fois accepté, il se convertit en facture d'acompte (`invoice_type='deposit'`, code Factur-X 386) : la TVA devient immédiatement exigible (CGI art. 289). Le solde (`invoice_type='balance'`, code 380) déduit automatiquement les acomptes émis via le groupe BG-22 code '80', et sa TVA n'est plus due. Anti-pattern : émettre deux fois le même acompte ou re-numéroter au submit (trou dans la séquence ISCA). Le SDK numérote en DRAFT- provisoire et n'attribue le numéro définitif qu'à l'émission. On convertit autant d'acomptes que nécessaire (`convertToDeposit`, N fois) mais un seul solde (`convertToBalance`).
À retenir
- Devis : chaîne d'audit SHA-256 séparée de l'ISCA fiscale
- Acompte = type Factur-X 386, TVA exigible immédiatement (CGI 289)
- Solde = type 380, déduction auto des acomptes (BG-22 code '80')
- N acomptes possibles, 1 seul solde par devis
- Lien public signable : `quotes.send()` / `regeneratePublicLink()` → `public_link`
- Aperçu PDF sans persistance : `POST /quotes/preview`
Exemple de code
import { ScellApiClient } from '@scell/sdk'; // v3.5.0
const client = new ScellApiClient({ apiKey: process.env.SCELL_API_KEY! });
// 1. Créer un devis (hors chaîne ISCA)
const { data: quote } = await client.quotes.create({
issue_date: '2026-06-15',
valid_until: '2026-07-15',
currency: 'EUR',
buyer_name: 'Acme SARL',
buyer_siret: '98765432109876',
buyer_country: 'FR',
buyer_address: { line1: '2 av. Demo', postal_code: '69001', city: 'Lyon', country: 'FR' },
total_ht: 5000, total_tax: 1000, total_ttc: 6000,
signature_required: true,
lines: [{ description: 'Prestation conseil', quantity: 1, unit_price: 5000, vat_rate: 20, total_ht: 5000, total_tax: 1000, total_ttc: 6000 }],
});
// 2. Aperçu PDF non persisté (avant envoi)
const pdf = await client.quotes.preview({
issue_date: '2026-06-15', valid_until: '2026-07-15',
buyer_name: 'Acme SARL', buyer_country: 'FR',
total_ht: 5000, total_tax: 1000, total_ttc: 6000,
lines: [{ description: 'Conseil', quantity: 1, unit_price: 5000, vat_rate: 20, total_ht: 5000, total_tax: 1000, total_ttc: 6000 }],
});
// 3. Envoyer le devis → lien public signable
const { public_link } = await client.quotes.send(quote.id, { send_email: true });
console.log('Lien client :', public_link);
// 4. Acompte 30 % (TVA exigible — Factur-X 386)
const { data: deposit } = await client.quotes.convertToDeposit(quote.id, {
deposit_percent: 30,
output_format: 'facturx',
due_date: '2026-07-01',
});
console.log('Acompte :', deposit.invoice_number);
// 5. Solde (déduit l'acompte — Factur-X 380)
const { data: balance } = await client.quotes.convertToBalance(quote.id, {
due_date: '2026-08-01',
output_format: 'facturx',
});
console.log('Solde :', balance.invoice_number);