Skip to main content

Sales & Revenue Management

Record sales revenue, manage customer receivables, and track inventory exits following SYSCOHADA standards. Handle complex scenarios including financial discounts, transport charges, packaging deposits, and split payments.

The Sales Lifecycle

In SYSCOHADA, a sale is recorded in up to three steps:

  1. Constatation — Record the invoice and customer claim/receivable (Account 4111).
  2. Sortie de Stock (optional) — Record the cost of goods sold (Account 6031 / 311).
  3. Réglement — Record payment(s) received to settle the receivable.

Each step is a separate balanced journal entry for clear audit trail.

Quick Examples

Simple Sale on Credit

const journal = ohada.recordSale({
amount: 100000,
label: "Marchandises",
saleType: 'GOODS',
vatRate: 18
});

Result: 1 entry (Constatation only)

Sale with Immediate Payment

const journal = ohada.recordSale({
amount: 100000,
label: "Vente comptant",
saleType: 'GOODS',
vatRate: 18,
payments: [{ method: 'cash', amount: 118000 }]
});

Result: 2 entries (Constatation + Réglement)


Type Reference

SaleInput

interface SaleInput {
// Required
amount: number; // Sale amount (ex-VAT), in currency units
label: string; // Description (e.g., "Vente marchandises")
saleType: SaleType; // 'GOODS' | 'MANUFACTURED' | 'SERVICES'

// Optional
date?: Date; // Transaction date (default: now)
vatRate?: number; // VAT rate in % (default: 18)

// Advanced Options
financialDiscount?: FinancialDiscount; // Escompte (recorded as expense)
packagingDeposit?: PackagingDeposit; // Consignation (no VAT)
transportCharge?: TransportCharge; // Port facturé (7071)
inventoryExit?: InventoryExit; // Stock exit (cost of goods sold)
payments?: SalePayment[]; // One or more payment entries
}

SaleType

type SaleType = 'GOODS' | 'MANUFACTURED' | 'SERVICES';

SalePayment

interface SalePayment {
method: 'cash' | 'bank'; // Payment method
amount: number; // Amount received
}

FinancialDiscount

interface FinancialDiscount {
percentage: number; // e.g., 2 for 2% discount (recorded as expense 673)
}

TransportCharge

interface TransportCharge {
amount: number; // Transport amount (added to taxable base)
description?: string;
}

PackagingDeposit

interface PackagingDeposit {
amount: number; // Deposit amount (NOT subject to VAT)
description?: string;
}

InventoryExit

interface InventoryExit {
costPrice: number; // Cost of goods sold (CMUP - weighted average cost)
}

Core Features

Revenue Classification

Revenue is automatically mapped to the correct SYSCOHADA account based on sale type:

Sale TypeAccountDescription
GOODS701Sale of merchandise.
MANUFACTURED702Sale of manufactured products.
SERVICES706Service revenue.
const goodsSale = ohada.recordSale({
amount: 50000,
label: "Stock Sale",
saleType: 'GOODS'
});

const serviceSale = ohada.recordSale({
amount: 25000,
label: "Consulting",
saleType: 'SERVICES'
});

Transport & Packaging

Include delivery charges and deposit amounts in your invoices.

const journal = ohada.recordSale({
amount: 100000,
label: "Vente avec frais",
saleType: 'GOODS',
vatRate: 18,
transportCharge: { amount: 5000 },
packagingDeposit: { amount: 1000 }
});

Key Points:

  • Transport (5,000) is added to the taxable base → VAT is calculated on 105,000
  • Packaging Deposit (1,000) is a liability → NOT subject to VAT

Financial Discount (Escompte)

Record discounts for early payment as an expense (Account 673).

const journal = ohada.recordSale({
amount: 100000,
label: "Vente avec escompte",
saleType: 'GOODS',
vatRate: 18,
financialDiscount: { percentage: 2 } // 2% = 2,000
});

Generated Entry:

  • D 4111: 116,360 (Customer receivable = 100,000 - 2,000 + 18% VAT)
  • D 673: 2,000 (Financial charge - escompte accordé)
  • C 701: 100,000 (Revenue)
  • C 4431: 18,360 (VAT on 102,000)

Inventory Exit (Cost of Goods Sold)

Automatically record stock reduction when goods are sold.

const journal = ohada.recordSale({
amount: 100000,
label: "Vente marchandises",
saleType: 'GOODS',
inventoryExit: { costPrice: 65000 }
});

Generated Entries:

  1. Constatation — Invoice (4111/701)
  2. Sortie de Stock — Cost of goods:
    • D 6031: 65,000 (Cost of Sales)
    • C 311: 65,000 (Stock reduction)

Multi-Step Payments (Split Payments)

Receive payment in multiple installments across different methods.

const journal = ohada.recordSale({
amount: 100000,
label: "Vente split payment",
saleType: 'GOODS',
vatRate: 18,
payments: [
{ method: 'cash', amount: 59000 },
{ method: 'bank', amount: 59000 }
]
});

Generated Entries:

  1. Constatation (invoice)
  2. Réglement #1 (cash, -59,000)
  3. Réglement #2 (bank, -59,000)

Each payment is a separate entry for clear tracking.


Credit Notes (Negative Sales/Returns)

Record returns by using a negative amount.

const journal = ohada.recordSale({
amount: -50000,
label: "Retour produit défectueux",
saleType: 'GOODS',
vatRate: 18
});

This generates a credit note with reversed amounts (debits become credits and vice versa).


Account Mapping

FeatureAccountDescription
Sale of Goods701Merchandise sales.
Mfg Products702Manufactured goods.
Services706Service revenue.
Transport Revenue7071Transport/freight charged to customer.
VAT Collected4431VAT on sales (tax liability).
Financial Discount673Discounts accorded (escompte).
Customer Receivable4111Accounts receivable.
Packaging Deposit4194Customer deposits (liability).
Cost of Sales6031Cost of goods sold.
Stock311Merchandise inventory.
Cash Account5711Cash on hand.
Bank Account5211Bank deposits.

Full Example: Complex Sale

A realistic sale including transport, packaging, discount, inventory exit, and split payment:

const journal = ohada.recordSale({
amount: 1000000,
label: "Vente - Commande #X2026",
date: new Date('2026-01-15'),
saleType: 'GOODS',
vatRate: 18,

// Ancillary costs
transportCharge: { amount: 50000 },
packagingDeposit: { amount: 10000 },

// Early payment discount
financialDiscount: { percentage: 2 },

// Track standard cost of goods
inventoryExit: { costPrice: 650000 },

// Receive split payment
payments: [
{ method: 'bank', amount: 588000 },
{ method: 'cash', amount: 588000 }
]
});

Generated Journal Entries (4 total):

#TypeAccountDebitCreditDescription
1Constatation41111,175,600Total receivable
1Constatation67320,000Early payment discount (2%)
1Constatation7011,000,000Revenue
1Constatation707150,000Transport revenue
1Constatation4431189,000VAT (18% on 1,050,000)
1Constatation419410,000Packaging deposit
2Sortie Stock6031650,000Cost of goods sold
2Sortie Stock311650,000Stock reduction
3Réglement5211588,000Bank in
3Réglement4111588,000Reduce receivable
4Réglement5711588,000Cash in
4Réglement4111588,000Reduce receivable

Key Rules

Revenue classification — Automatic mapping based on sale type (GOODS/MANUFACTURED/SERVICES).

Taxable base — Transport is included; packaging deposit is excluded from VAT.

Financial discount — Recorded as expense (673), reduces customer receivable.

Inventory tracking — Separate entry for cost of goods sold if provided.

Split payments — Each payment is its own entry for audit trail clarity.

Audit trail — All transaction phases are separate entries with full traceability.


Parameter Reference

ParameterTypeRequiredExampleNotes
amountnumberYes100000Sale amount (ex-VAT).
labelstringYes"Vente marchandises"Transaction description.
saleTypeSaleTypeYes'GOODS'Determines revenue account (701/702/706).
dateDateNonew Date()Defaults to now.
vatRatenumberNo18VAT % (default: 18, ignored if disableVAT: true).
financialDiscountFinancialDiscountNo{percentage: 2}Early payment discount (expense 673).
transportChargeTransportChargeNo{amount: 5000}Included in taxable base.
packagingDepositPackagingDepositNo{amount: 1000}Excluded from VAT.
inventoryExitInventoryExitNo{costPrice: 65000}Generates separate stock exit entry.
paymentsSalePayment[]No[{method: 'bank', amount: 100000}]One or more payments; each generates separate entry.