Patterns
Product catalog and line autofill
Prefill a line via `product_id` and save on the fly
The `products` registry stores reusable products/services (unit price ex-VAT, default VAT, UN/ECE Rec 20 unit, fiscal `revenue_category`, custom organisation category). On invoice, quote or credit-note creation, a line can reference `product_id`: the API prefills description, price, VAT rate and unit from the catalog. The `save_to_catalog=true` flag on a line triggers an atomic product upsert (dedup by SKU then name) WITHIN the document transaction. Like buyers, the `line.product_id` link is soft (FK `nullOnDelete`): the emitted price/description snapshot remains the ISCA truth — deleting a catalog product never alters past documents. The `revenue_category` (goods/service/accommodation) is distinct from `product_category_id` (internal custom organisation).
Key facts
- `products` + `product_categories`, scope (tenant_id, sub_tenant_id)
- Line with `product_id` → autofill desc/price/VAT/unit
- `save_to_catalog=true` → atomic upsert inside the doc transaction
- `line.product_id` soft (FK nullOnDelete), emitted snapshot = ISCA truth
- Fiscal `revenue_category` ≠ `product_category_id` (organisation)
- Endpoints: `/api/v1/products` and `/api/v1/product-categories`
Code example
import { ScellApiClient } from '@scell/sdk'; // v3.5.0
const client = new ScellApiClient({ apiKey: process.env.SCELL_API_KEY! });
// 1. Créer un produit catalogue
const product = await client.products.create({
name: 'Licence SaaS annuelle',
sku: 'SAAS-ANNUAL',
unit_price_ht: 1200,
default_tax_rate: 20,
revenue_category: 'service',
unit: 'C62', // unité par défaut UN/ECE Rec 20
});
// 2. Facturer : la ligne référence product_id (autoremplissage serveur)
const invoice = await client.invoices.create({
format: 'factur-x',
profile: 'EN16931',
currency: 'EUR',
buyer: { siret: '98765432109876', name: 'Acme SARL', address: { street: '2 av. Demo', city: 'Lyon', postal_code: '69001', country: 'FR' } },
lines: [
{ product_id: product.id, quantity: 2 }, // desc/prix/TVA repris du catalogue
// 3. Nouvelle ligne enregistrée à la volée dans le catalogue
{ description: 'Setup initial', quantity: 1, unit_price: 500, vat_rate: 20, save_to_catalog: true },
],
});
console.log('Facture :', invoice.id);