The Starting Point: Entries
Accounting records each transaction in accounts. In France, the general chart of accounts (PCG) organizes these accounts into classes: class 6 groups expenses, class 7 groups revenue. The first two digits specify the nature:
| Prefix | Nature |
|---|---|
| 70 | Sales (revenue) |
| 60 | Purchases |
| 61, 62 | External services (rent, fees, telecoms…) |
| 63 | Taxes and duties |
| 64 | Personnel costs |
| 68 | Depreciation, amortization and provisions |
A management controller doesn’t present hundreds of accounts to management: they group them into readable line items. To do so, they rely on a written, shared mapping table that can be reviewed and audited.
The entries in this course are simplified: one positive amount per account per month, with no stock variation or closing entries.
Building the Mapping Table
MAPPING = {
"70": "Chiffre d'affaires",
"60": "Achats consommés",
"61": "Charges externes",
"62": "Charges externes",
"63": "Impôts et taxes",
"64": "Charges de personnel",
"68": "Dotations aux amortissements",
}
def rubrique(account):
return MAPPING.get(str(account)[:2], "Hors compte de résultat")
entries["rubrique"] = entries["account"].map(rubrique)
The default value "Hors compte de résultat" (“outside the income statement”) catches balance sheet accounts (class 5 for cash, class 4 for third parties…) that have no place in an income statement. If it shows up in your results, it is a signal to investigate, not a line to ignore.
The Monthly Income Statement
pnl = entries.pivot_table(index="rubrique", columns="month", values="amount", aggfunc="sum", fill_value=0)
One row per line item, one column per month: this is the expected shape of a monthly report. An essential check: the sum of pnl must equal the sum of the entries in scope. A difference means an entry was lost or counted twice.
Intermediate Balances, in a Simplified Version
The income statement is read as a cascade. Each balance removes one category of expenses and answers a question:
def sig(m): # m: dictionary of line item -> amount for one month
marge_brute = m["Chiffre d'affaires"] - m["Achats consommés"]
valeur_ajoutee = marge_brute - m["Charges externes"]
ebe = valeur_ajoutee - m["Impôts et taxes"] - m["Charges de personnel"]
resultat_exploitation = ebe - m["Dotations aux amortissements"]
return {"marge_brute": marge_brute, "valeur_ajoutee": valeur_ajoutee,
"ebe": ebe, "resultat_exploitation": resultat_exploitation}
print(sig(pnl["2026-01"].to_dict()))
# gross margin 30,000, value added 26,000, EBE 10,500, operating income 8,500
- The gross margin (here a trading margin, since the company resells goods) tells you what is left after paying for the products sold.
- Value added measures the wealth created by the company after its external consumption.
- The EBE (gross operating surplus, excédent brut d’exploitation) measures what operations generate before depreciation and amortization, financial costs and tax. It is a closely watched indicator because it depends little on investment and financing choices.
- Operating income deducts the wear of investments (depreciation and amortization charges).
The official definition of the intermediate management balances includes other items (production, stock variation, operating subsidies, reversals, other income and expenses); this course keeps their structure for analysis.
Relating to Revenue
A balance is compared from one month to the next as a percentage of revenue: an EBE rate of 21% in January and 26.7% in February shows that fixed costs (personnel, rent) were better absorbed by higher revenue. This is what is called operating leverage.
Key Takeaways
- Class 6 accounts are expenses, class 7 accounts are revenue; the prefixes give the nature.
- A documented mapping table turns entries into line items.
- Balances are computed as a cascade and read as a percentage of revenue.
Exercises
Solutions and tests stay on the server. In the student space, your code is run and checked, and the tutor gives hints before the solution.
From the ledger to line items
Write
rubrique(account)which returns the line item for an account number based on its first two digits (the lesson’s table;"Hors compte de résultat"by default). Add therubriquecolumn toentries, then buildpnl: a line item × month pivot table of the amounts.import pandas as pd entries = pd.DataFrame({ "month": ["2026-01"] * 7 + ["2026-02"] * 7, "account": ["707000", "607000", "613200", "626000", "635100", "641000", "681100"] * 2, "amount": [50000, 20000, 3000, 1000, 500, 15000, 2000, 60000, 24000, 3000, 1500, 500, 15000, 2000], }) def rubrique(account): ... # pnl = ...Intermediate balances
Write
sig(m)wheremis a dictionary of line item → amount for one month. It returnsmarge_brute,valeur_ajoutee,ebeandresultat_exploitation. Then computeebe_rate, a Series (index = month) of the EBE rate (EBE / revenue).import pandas as pd entries = pd.DataFrame({ "month": ["2026-01"] * 7 + ["2026-02"] * 7, "account": ["707000", "607000", "613200", "626000", "635100", "641000", "681100"] * 2, "amount": [50000, 20000, 3000, 1000, 500, 15000, 2000, 60000, 24000, 3000, 1500, 500, 15000, 2000], }) MAPPING = { "70": "Chiffre d'affaires", "60": "Achats consommés", "61": "Charges externes", "62": "Charges externes", "63": "Impôts et taxes", "64": "Charges de personnel", "68": "Dotations aux amortissements", } def rubrique(account): return MAPPING.get(str(account)[:2], "Hors compte de résultat") entries["rubrique"] = entries["account"].map(rubrique) pnl = entries.pivot_table(index="rubrique", columns="month", values="amount", aggfunc="sum", fill_value=0) def sig(m): ... # ebe_rate = ...Reading the EBE
A company buys an expensive machine depreciated over 5 years. Which balance is NOT affected by the depreciation charge for this machine?
- Operating income
- The EBE
- Net income
- None, all are affected
End-of-lesson quiz
Which category does account 641000 belong to?
- Revenue
- Personnel costs
- Cash
Revenue 100, purchases 40, external expenses 15, taxes 2, personnel 25. What is the EBE?
- 45
- 18
- 60
Why provide a default line item “Hors compte de résultat”?
- To hide awkward entries
- To spot balance sheet accounts or accounts not covered by the mapping
- Because pandas requires it
The rest of the course
- Budget and Variance Analysis18 min
- Cash Flow Forecasting18 min
- Product Profitability: Contribution Margin and Break-Even Point18 min
- Financial Dashboard and Year-End Forecast15 min