@ekwo-ai/xbrl-cbso 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +66 -0
- package/LICENSE +21 -0
- package/README.md +202 -0
- package/dist/enterprise-number.d.ts +9 -0
- package/dist/enterprise-number.d.ts.map +1 -0
- package/dist/enterprise-number.js +18 -0
- package/dist/enterprise-number.js.map +1 -0
- package/dist/equations.d.ts +35 -0
- package/dist/equations.d.ts.map +1 -0
- package/dist/equations.js +77 -0
- package/dist/equations.js.map +1 -0
- package/dist/errors.d.ts +9 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +12 -0
- package/dist/errors.js.map +1 -0
- package/dist/fact-keys.d.ts +68 -0
- package/dist/fact-keys.d.ts.map +1 -0
- package/dist/fact-keys.js +121 -0
- package/dist/fact-keys.js.map +1 -0
- package/dist/generator.d.ts +73 -0
- package/dist/generator.d.ts.map +1 -0
- package/dist/generator.js +394 -0
- package/dist/generator.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/pcmn/index.d.ts +68 -0
- package/dist/pcmn/index.d.ts.map +1 -0
- package/dist/pcmn/index.js +266 -0
- package/dist/pcmn/index.js.map +1 -0
- package/dist/rounding.d.ts +25 -0
- package/dist/rounding.d.ts.map +1 -0
- package/dist/rounding.js +35 -0
- package/dist/rounding.js.map +1 -0
- package/dist/taxonomy/cbso-26-m01-f.d.ts +17 -0
- package/dist/taxonomy/cbso-26-m01-f.d.ts.map +1 -0
- package/dist/taxonomy/cbso-26-m01-f.js +433 -0
- package/dist/taxonomy/cbso-26-m01-f.js.map +1 -0
- package/dist/taxonomy/identification.d.ts +13 -0
- package/dist/taxonomy/identification.d.ts.map +1 -0
- package/dist/taxonomy/identification.js +16 -0
- package/dist/taxonomy/identification.js.map +1 -0
- package/dist/taxonomy/index.d.ts +27 -0
- package/dist/taxonomy/index.d.ts.map +1 -0
- package/dist/taxonomy/index.js +83 -0
- package/dist/taxonomy/index.js.map +1 -0
- package/dist/types.d.ts +149 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/validate.d.ts +4 -0
- package/dist/validate.d.ts.map +1 -0
- package/dist/validate.js +84 -0
- package/dist/validate.js.map +1 -0
- package/package.json +65 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here. The format follows
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the project adheres to
|
|
5
|
+
[Semantic Versioning](https://semver.org/).
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
## [0.2.0] — 2026-09-14
|
|
10
|
+
|
|
11
|
+
Released with Ekwo OS `v0.2.0`, the first tagged release of the repository this
|
|
12
|
+
package lives in.
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
- **A negative half was rounded the wrong way.** The equations and the PCMN
|
|
16
|
+
mapping used `Math.round(v * 100) / 100`, which goes towards positive
|
|
17
|
+
infinity and turns `-0.005` into `-0.00`, and which reads `2.675` as `2.67`
|
|
18
|
+
because `2.675 * 100` is really `267.49999999999994`. The new `roundCurrency`
|
|
19
|
+
rounds half up on the absolute value, and is the same file in the three
|
|
20
|
+
packages that hold a copy.
|
|
21
|
+
- **53 reporting codes were filed under the wrong fact**, among them nine pairs that were
|
|
22
|
+
swapped with each other: `22/27` (Immobilisations corporelles) with `24` (Mobilier et
|
|
23
|
+
matériel roulant), `20/58` (TOTAL DE L'ACTIF) with `10/49` (TOTAL DU PASSIF), `10`
|
|
24
|
+
(Capital) with `10/11` (Apport), `65` with `65/66B` (Charges financières), `14`
|
|
25
|
+
(Bénéfice à reporter) with `9906` (Bénéfice à affecter), `8179` with `8309` in the
|
|
26
|
+
fixed-assets annex, and nine rows of the social balance sheet. `29` (Créances à plus d'un
|
|
27
|
+
an), `44` (Dettes commerciales), `62` (Rémunérations) and `66A` were filed under `902`,
|
|
28
|
+
`440/4`, `1023` and `29`. Every correction is sourced in
|
|
29
|
+
[`docs/sources.md`](docs/sources.md) and pinned by `test/rubrics.test.ts`.
|
|
30
|
+
|
|
31
|
+
### Changed
|
|
32
|
+
- **Target framework is now CBSO 26.0**, in use by the Central Balance Sheet Office since
|
|
33
|
+
2 January 2026: `link:schemaRef` points at `fws/26.0/mod/m01/m01-f.xsd` and the
|
|
34
|
+
enumeration namespaces move to `26.0`. The abridged model itself did not change between
|
|
35
|
+
25.0 and 26.0 — the generator produces the same 430 facts against either framework — but
|
|
36
|
+
the postal-code enumeration follows the municipality mergers of 1 January 2025.
|
|
37
|
+
- The reporting-code table is **generated** from the NBB taxonomy package by
|
|
38
|
+
`scripts/build-taxonomy.mjs`, no longer transcribed by hand, and covers the whole model:
|
|
39
|
+
297 codes and 430 cells, against 106 codes before. `--check` verifies the committed table
|
|
40
|
+
against a package.
|
|
41
|
+
- The previous-year boxes the NBB suffixes with `P` in the annexes (`8059P`, `1003P`) stay
|
|
42
|
+
the `p` column of their base code; `14P`, which is a line of its own, keeps its own code.
|
|
43
|
+
- `CBSO_25_M01F` is renamed `CBSO_26_M01F`; `FRAMEWORK` is exported.
|
|
44
|
+
- `CbsoInput.values` becomes optional, and `unknownCodes` returns a sorted array.
|
|
45
|
+
|
|
46
|
+
### Added
|
|
47
|
+
- `CbsoInput.lines`: statement lines keyed by their fact signature
|
|
48
|
+
(`met:am1|bas:m9|rst:m2`) instead of by a reporting code, so the mapping can live in the
|
|
49
|
+
system that produced the statement rather than being copied here. `CbsoInput.values`
|
|
50
|
+
remains supported as the legacy path, and the two can be combined.
|
|
51
|
+
- `resolveFactKey`, `valuesFromFactKeys`, `parseFactKey`, `factKeyOf`, `mergeValues` and
|
|
52
|
+
`CbsoFactKeyError`, which names the candidate codes when a key fits more than one line.
|
|
53
|
+
- `unfiledValues(input)`: figures that would be dropped, because the code is unknown or
|
|
54
|
+
because the model has no such column.
|
|
55
|
+
- `docs/sources.md`: where every assertion comes from, and what still needs a filing to
|
|
56
|
+
confirm it.
|
|
57
|
+
|
|
58
|
+
## [0.1.0] — 2026-09-11
|
|
59
|
+
|
|
60
|
+
### Added
|
|
61
|
+
- `generateCbsoXbrl` / `XbrlCbso`: XBRL instance generator for the CBSO taxonomy,
|
|
62
|
+
abridged model for companies with capital (m01-f), with board members (natural persons,
|
|
63
|
+
legal entities, permanent representatives, foreign addresses).
|
|
64
|
+
- `checkBnbEquations`: arithmetic identities of the abridged model.
|
|
65
|
+
- `validateInput`, `CbsoInputError`, enterprise-number helpers.
|
|
66
|
+
- `@ekwo-ai/xbrl-cbso/pcmn`: Belgian minimum chart of accounts → reporting codes.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ekwo AI
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
# @ekwo-ai/xbrl-cbso
|
|
2
|
+
|
|
3
|
+
Generate the XBRL file of Belgian annual accounts for the National Bank of Belgium
|
|
4
|
+
(Central Balance Sheet Office, *CBSO* / *Centrale des bilans* / *Balanscentrale*),
|
|
5
|
+
taxonomy **26.0**, abridged model for companies with capital (**m01-f**).
|
|
6
|
+
|
|
7
|
+
- **Pure TypeScript, zero runtime dependencies.** Works in Node ≥ 18, Deno, Bun and browsers.
|
|
8
|
+
- **Generated from the official taxonomy.** The 297 reporting codes of the model (430 cells,
|
|
9
|
+
current and previous year), with their metric and dimension members, are read straight out of
|
|
10
|
+
the NBB taxonomy package by
|
|
11
|
+
[`scripts/build-taxonomy.mjs`](scripts/build-taxonomy.mjs) — not transcribed by hand. Sources
|
|
12
|
+
and method: [`docs/sources.md`](docs/sources.md).
|
|
13
|
+
- **Two ways in.** Reporting codes (`20/58`, `9904`), or the fact keys a book-keeping system
|
|
14
|
+
already stores next to each statement line (`met:am1|bas:m9|rst:m2`).
|
|
15
|
+
- **Partial forms welcome.** Only the codes you provide are emitted; Filing fills the rest.
|
|
16
|
+
`unknownCodes()` tells you which of your codes the model would skip.
|
|
17
|
+
- **Built-in checks.** Enterprise-number check digits, ISO dates, Belgian postal codes, and the
|
|
18
|
+
arithmetic identities of the form (`20/58 = 10/49`, `9904 = 9903 + 780 − 680 − 67/77`, …).
|
|
19
|
+
- **Optional PCMN layer.** Turn a trial balance (Belgian minimum chart of accounts) into the
|
|
20
|
+
reporting codes.
|
|
21
|
+
|
|
22
|
+
## Install
|
|
23
|
+
|
|
24
|
+
```sh
|
|
25
|
+
npm install @ekwo-ai/xbrl-cbso
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Usage
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
import { generateCbsoXbrl, checkBnbEquations, LegalForm, EnterpriseCourt } from '@ekwo-ai/xbrl-cbso';
|
|
32
|
+
|
|
33
|
+
const input = {
|
|
34
|
+
entityNumber: '0123.456.749',
|
|
35
|
+
denomination: 'Exemple Conseil',
|
|
36
|
+
legalForm: LegalForm.SA_NV, // 'lgf:m014'
|
|
37
|
+
court: EnterpriseCourt.BRUSSELS_FR, // 'cct:m31'
|
|
38
|
+
address: { street: "Rue de l'Exemple", number: '1', postalCode: '1000', city: 'Bruxelles' },
|
|
39
|
+
periodStart: '2025-01-01',
|
|
40
|
+
periodEnd: '2025-12-31',
|
|
41
|
+
prevPeriodStart: '2024-01-01',
|
|
42
|
+
prevPeriodEnd: '2024-12-31',
|
|
43
|
+
gaDate: '2026-05-15', // general meeting approving the accounts
|
|
44
|
+
deedDate: '2019-03-04', // deed of incorporation / latest amendment of the articles
|
|
45
|
+
values: {
|
|
46
|
+
'20/58': { n: 201500, p: 171000 }, // total assets, current and previous year
|
|
47
|
+
'10/49': { n: 201500, p: 171000 },
|
|
48
|
+
'9904': { n: 57500, p: 43600 },
|
|
49
|
+
'9087': { n: 3.5, p: 3 }, // full-time equivalents
|
|
50
|
+
// … any other code of the abridged model
|
|
51
|
+
},
|
|
52
|
+
administrators: [
|
|
53
|
+
{ kind: 'person', lastName: 'Dupont', firstName: 'Marie',
|
|
54
|
+
street: 'Avenue des Tilleuls', number: '12', postalCode: '1180', city: 'Uccle' },
|
|
55
|
+
{ kind: 'company', name: 'Gestion Exemple', enterpriseNumber: '0400.000.086',
|
|
56
|
+
street: 'Kerkstraat', number: '7', postalCode: '9000', city: 'Gent',
|
|
57
|
+
representative: { lastName: 'Janssens', firstName: 'Pieter' } },
|
|
58
|
+
],
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const violations = checkBnbEquations(input.values); // [] when the form adds up
|
|
62
|
+
const xml = generateCbsoXbrl(input); // string, UTF-8, ready to upload
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
See [`examples/basic.ts`](examples/basic.ts) for a runnable version (`npm run example`).
|
|
66
|
+
|
|
67
|
+
### From statement lines that already carry their taxonomy key
|
|
68
|
+
|
|
69
|
+
A book-keeping system that stores the CBSO fact key next to each line of its
|
|
70
|
+
financial statements does not need to know the reporting codes at all. Hand the
|
|
71
|
+
lines over as they are:
|
|
72
|
+
|
|
73
|
+
```ts
|
|
74
|
+
import { generateCbsoXbrl } from '@ekwo-ai/xbrl-cbso';
|
|
75
|
+
|
|
76
|
+
const xml = generateCbsoXbrl({
|
|
77
|
+
...identification,
|
|
78
|
+
lines: [
|
|
79
|
+
{ xbrl_element: 'met:am1|bas:m2|ntr:m2', amount: 12000, previous_amount: 15000 }, // 22/27
|
|
80
|
+
{ xbrl_element: 'met:am1|bas:m5|ntr:m2', amount: 8000, previous_amount: 10000 }, // 24
|
|
81
|
+
{ xbrl_element: 'met:am1|bas:m9|rst:m2', amount: 60000, previous_amount: 47500 }, // 40/41
|
|
82
|
+
],
|
|
83
|
+
});
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
A key is the metric followed by the domain members that matter; the members that
|
|
87
|
+
only place a fact in a column (`dim:part`, `dim:prd`) may be left out. It
|
|
88
|
+
resolves to the line that adds the fewest members of its own, so
|
|
89
|
+
`met:am1|bas:m2` is the heading *ACTIFS IMMOBILISÉS* (21/28) and not one of the
|
|
90
|
+
three lines under it. A key that fits two lines equally well is refused rather
|
|
91
|
+
than guessed:
|
|
92
|
+
|
|
93
|
+
```ts
|
|
94
|
+
resolveFactKey('met:am1|bas:m25', CBSO_26_M01F);
|
|
95
|
+
// CbsoFactKeyError: signature shared by 10/49, 20/58 — add the member that
|
|
96
|
+
// separates them (usually a "part:" member)
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
`met:am1|bas:m25|part:m1` is the total of the assets, `|part:m3` the total of
|
|
100
|
+
the liabilities. `resolveFactKey`, `valuesFromFactKeys` and `factKeyOf` are
|
|
101
|
+
exported for callers that want to do the mapping themselves; `mergeValues`
|
|
102
|
+
shows what a filing would carry when both `values` and `lines` are given, and
|
|
103
|
+
refuses a code that the two disagree on.
|
|
104
|
+
|
|
105
|
+
### From a trial balance
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
import { valuesFromTrialBalance } from '@ekwo-ai/xbrl-cbso/pcmn';
|
|
109
|
+
|
|
110
|
+
// balance = debit − credit, for the current and the previous year
|
|
111
|
+
const values = valuesFromTrialBalance([
|
|
112
|
+
{ account: '400000', balance: 52000, priorBalance: 41000 },
|
|
113
|
+
{ account: '440000', balance: -31000, priorBalance: -28000 },
|
|
114
|
+
{ account: '700000', balance: -320000, priorBalance: -270000 },
|
|
115
|
+
// …
|
|
116
|
+
]);
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
`valuesFromTrialBalance` computes the four sections (assets, equity and liabilities, income
|
|
120
|
+
statement, appropriation) with the sign conventions of the standardised form, and returns them in
|
|
121
|
+
the shape the generator expects. Lower-level functions (`computeBalanceSheet`,
|
|
122
|
+
`computeIncomeStatement`, `computeAppropriation`, the `ASSETS` / `LIABILITIES` /
|
|
123
|
+
`INCOME_STATEMENT` / `APPROPRIATION` tables) are exported for custom pipelines.
|
|
124
|
+
|
|
125
|
+
Annex figures that do not derive from the trial balance (fixed-assets movements `8029`–`8395`,
|
|
126
|
+
staff `9087`, …) are plain codes: add them to `values` yourself.
|
|
127
|
+
|
|
128
|
+
## API
|
|
129
|
+
|
|
130
|
+
| Export | Description |
|
|
131
|
+
|---|---|
|
|
132
|
+
| `generateCbsoXbrl(input, options?)` | One call: `CbsoInput` → XBRL string. Validates the input unless `options.validate === false`. `options.lang` sets `xml:lang` (default `fr`). |
|
|
133
|
+
| `checkBnbEquations(values, options?)` | Returns the identities that do not add up (`code`, `column`, `expected`, `actual`, `rule`). Missing operands count as zero, as in Filing. |
|
|
134
|
+
| `unknownCodes(values)` | Codes the abridged model does not carry (they would be silently skipped), sorted. |
|
|
135
|
+
| `unfiledValues(input)` | Figures that would be dropped: an unknown code, or a code whose column the model does not have. |
|
|
136
|
+
| `resolveFactKey(key, template)` | `"met:am1\|bas:m9\|rst:m2"` → `"40/41"`. Throws a `CbsoFactKeyError` naming the candidates when the key fits more than one line. |
|
|
137
|
+
| `valuesFromFactKeys(lines, template)`, `factKeyOf(fact)`, `parseFactKey(key)` | The pieces of that path, for callers doing the mapping themselves. |
|
|
138
|
+
| `mergeValues(input, template?)` | `values` plus the resolved `lines`; throws when the two disagree on a code. |
|
|
139
|
+
| `validateInput(input)` | Throws a `CbsoInputError` (`path`, `message`) on the first problem. |
|
|
140
|
+
| `normalizeEnterpriseNumber`, `isValidEnterpriseNumber` | `"BE 0123.456.749"` → `"0123456749"`; mod-97 check digits. |
|
|
141
|
+
| `LegalForm`, `EnterpriseCourt` | Taxonomy members verified against accepted filings. Any other `lgf:*` / `cct:*` member can be passed as a string. |
|
|
142
|
+
| `XbrlCbso`, `CBSO_26_M01F`, `FRAMEWORK`, `NAMESPACES` | Class API and bundled template, for a custom or updated taxonomy. |
|
|
143
|
+
|
|
144
|
+
Types: `CbsoInput`, `FactKeyLine`, `PostalAddress`, `Administrator`, `YearValues`,
|
|
145
|
+
`Declarations`, `TemplateFact`, `CbsoTemplate`.
|
|
146
|
+
|
|
147
|
+
`CbsoInput.values` is the legacy path: it makes the caller hold its own mapping
|
|
148
|
+
from its accounts to the reporting codes of the model. `CbsoInput.lines` exists
|
|
149
|
+
so that mapping can live in one place — the system that produced the statement —
|
|
150
|
+
instead of being copied here. Both stay supported, and they can be combined.
|
|
151
|
+
|
|
152
|
+
## How it works
|
|
153
|
+
|
|
154
|
+
The CBSO taxonomy carries the reporting code (*rubrique* / *rubriek*) in the **dimensions** of the
|
|
155
|
+
context (`dim:bas → bas:mXX`, `dim:part`, `dim:prd` for current vs previous year…), never in the
|
|
156
|
+
element name: amounts are all `met:am1` / `met:am2`, figures `met:int*` / `met:dec1`. A template
|
|
157
|
+
lists, for each of the 430 cells of the abridged model, that exact signature. The generator:
|
|
158
|
+
|
|
159
|
+
1. keeps the template facts that have a value (identification fields, reporting codes);
|
|
160
|
+
2. adds the enumerated identification facts (legal form, court, postal code, country) and the
|
|
161
|
+
board members (typed dimensions keyed by name);
|
|
162
|
+
3. builds the deduplicated contexts, declares only the namespaces actually used, and formats
|
|
163
|
+
numbers according to the element facets (two decimals for amounts, one for FTEs, integers).
|
|
164
|
+
|
|
165
|
+
The previous financial year is **not** a second period: both columns are reported in a context
|
|
166
|
+
whose period is the closing date of the current year, and `dim:prd` (`prd:m1` / `prd:m2`) says
|
|
167
|
+
which column. The boxes the NBB itself suffixes with `P` in the annexes (`8059P`, `1003P`) measure
|
|
168
|
+
the same thing as their base code one year earlier, so they are its `p` figure. `14P` is not in
|
|
169
|
+
that case — "profit carried forward from the previous year" is a line of its own — and keeps its
|
|
170
|
+
own code.
|
|
171
|
+
|
|
172
|
+
To rebuild the table against a newer taxonomy package:
|
|
173
|
+
|
|
174
|
+
```sh
|
|
175
|
+
node scripts/build-taxonomy.mjs --package ~/nbb-cbso-26.0.15 # rewrite
|
|
176
|
+
node scripts/build-taxonomy.mjs --package ~/nbb-cbso-26.0.15 --check # verify only
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## Scope and limitations
|
|
180
|
+
|
|
181
|
+
- Model **m01-f** only (abridged, companies with capital). Full and micro models, and the
|
|
182
|
+
non-profit taxonomy, are not covered yet — though the build script takes `--model` and
|
|
183
|
+
`--framework`, so the table for another one is a command away.
|
|
184
|
+
- The **identification** facts (section 1) are not generated: they carry no reporting code, and
|
|
185
|
+
are kept from a filing the NBB accepted. Everything else comes from the taxonomy.
|
|
186
|
+
- Only the legal form and court identifiers verified in accepted filings are named constants;
|
|
187
|
+
other members must be looked up in the taxonomy's `lgf` / `cct` domains.
|
|
188
|
+
- The generator does not validate against the XSD. The NBB *Filing* application remains the
|
|
189
|
+
authority: import the file in a draft and check its validation report before submitting.
|
|
190
|
+
|
|
191
|
+
## Development
|
|
192
|
+
|
|
193
|
+
```sh
|
|
194
|
+
npm install
|
|
195
|
+
npm run typecheck
|
|
196
|
+
npm test
|
|
197
|
+
npm run build
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
## License
|
|
201
|
+
|
|
202
|
+
[MIT](LICENSE) © Ekwo AI
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Belgian enterprise number (numéro d'entreprise / ondernemingsnummer):
|
|
3
|
+
* ten digits, the last two being `97 - (first eight mod 97)`.
|
|
4
|
+
*/
|
|
5
|
+
/** Strip dots, spaces and an optional `BE` prefix: `"BE 0123.456.749"` → `"0123456749"`. */
|
|
6
|
+
export declare function normalizeEnterpriseNumber(raw: string): string;
|
|
7
|
+
/** `true` when the ten-digit number carries valid check digits. */
|
|
8
|
+
export declare function isValidEnterpriseNumber(raw: string): boolean;
|
|
9
|
+
//# sourceMappingURL=enterprise-number.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"enterprise-number.d.ts","sourceRoot":"","sources":["../src/enterprise-number.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,4FAA4F;AAC5F,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE7D;AAED,mEAAmE;AACnE,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAM5D"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Belgian enterprise number (numéro d'entreprise / ondernemingsnummer):
|
|
3
|
+
* ten digits, the last two being `97 - (first eight mod 97)`.
|
|
4
|
+
*/
|
|
5
|
+
/** Strip dots, spaces and an optional `BE` prefix: `"BE 0123.456.749"` → `"0123456749"`. */
|
|
6
|
+
export function normalizeEnterpriseNumber(raw) {
|
|
7
|
+
return raw.replace(/^\s*BE/i, '').replace(/\D/g, '');
|
|
8
|
+
}
|
|
9
|
+
/** `true` when the ten-digit number carries valid check digits. */
|
|
10
|
+
export function isValidEnterpriseNumber(raw) {
|
|
11
|
+
const digits = normalizeEnterpriseNumber(raw);
|
|
12
|
+
if (!/^\d{10}$/.test(digits))
|
|
13
|
+
return false;
|
|
14
|
+
const base = Number(digits.slice(0, 8));
|
|
15
|
+
const check = Number(digits.slice(8));
|
|
16
|
+
return 97 - (base % 97) === check;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=enterprise-number.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"enterprise-number.js","sourceRoot":"","sources":["../src/enterprise-number.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,4FAA4F;AAC5F,MAAM,UAAU,yBAAyB,CAAC,GAAW;IACnD,OAAO,GAAG,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AACvD,CAAC;AAED,mEAAmE;AACnE,MAAM,UAAU,uBAAuB,CAAC,GAAW;IACjD,MAAM,MAAM,GAAG,yBAAyB,CAAC,GAAG,CAAC,CAAC;IAC9C,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC;QAAE,OAAO,KAAK,CAAC;IAC3C,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACxC,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IACtC,OAAO,EAAE,GAAG,CAAC,IAAI,GAAG,EAAE,CAAC,KAAK,KAAK,CAAC;AACpC,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Arithmetic identities of the abridged model, as checked by the Filing
|
|
3
|
+
* application. Run them before generating to catch inconsistent inputs early.
|
|
4
|
+
*/
|
|
5
|
+
import type { BnbCode, YearValues } from './types.js';
|
|
6
|
+
export interface EquationViolation {
|
|
7
|
+
/** Code of the total that does not add up. */
|
|
8
|
+
code: BnbCode;
|
|
9
|
+
column: 'n' | 'p';
|
|
10
|
+
expected: number;
|
|
11
|
+
actual: number;
|
|
12
|
+
/** Human-readable identity, e.g. `"20/58 = 20 + 21/28 + 29/58"`. */
|
|
13
|
+
rule: string;
|
|
14
|
+
}
|
|
15
|
+
interface Identity {
|
|
16
|
+
total: BnbCode;
|
|
17
|
+
/** Operands with their sign. */
|
|
18
|
+
terms: Array<{
|
|
19
|
+
code: BnbCode;
|
|
20
|
+
sign: 1 | -1;
|
|
21
|
+
}>;
|
|
22
|
+
}
|
|
23
|
+
/** Identities of the abridged model for companies with capital. */
|
|
24
|
+
export declare const ABRIDGED_IDENTITIES: readonly Identity[];
|
|
25
|
+
/**
|
|
26
|
+
* Checks every identity whose total and at least one operand are present in
|
|
27
|
+
* `values`. Missing operands count as zero, as in Filing. Returns the
|
|
28
|
+
* violations (empty = consistent).
|
|
29
|
+
*/
|
|
30
|
+
export declare function checkBnbEquations(values: Record<BnbCode, YearValues>, options?: {
|
|
31
|
+
tolerance?: number;
|
|
32
|
+
identities?: readonly Identity[];
|
|
33
|
+
}): EquationViolation[];
|
|
34
|
+
export {};
|
|
35
|
+
//# sourceMappingURL=equations.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"equations.d.ts","sourceRoot":"","sources":["../src/equations.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAGtD,MAAM,WAAW,iBAAiB;IAChC,8CAA8C;IAC9C,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,EAAE,GAAG,GAAG,GAAG,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,oEAAoE;IACpE,IAAI,EAAE,MAAM,CAAC;CACd;AAED,UAAU,QAAQ;IAChB,KAAK,EAAE,OAAO,CAAC;IACf,gCAAgC;IAChC,KAAK,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,OAAO,CAAC;QAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,CAAA;KAAE,CAAC,CAAC;CAC/C;AAKD,mEAAmE;AACnE,eAAO,MAAM,mBAAmB,EAAE,SAAS,QAAQ,EAiClD,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,EACnC,OAAO,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,SAAS,QAAQ,EAAE,CAAA;CAAO,GACrE,iBAAiB,EAAE,CA8BrB"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { roundCurrency } from './rounding.js';
|
|
2
|
+
const plus = (code) => ({ code, sign: 1 });
|
|
3
|
+
const minus = (code) => ({ code, sign: -1 });
|
|
4
|
+
/** Identities of the abridged model for companies with capital. */
|
|
5
|
+
export const ABRIDGED_IDENTITIES = [
|
|
6
|
+
// Balance sheet
|
|
7
|
+
{ total: '20/58', terms: [plus('20'), plus('21/28'), plus('29/58')] },
|
|
8
|
+
{ total: '21/28', terms: [plus('21'), plus('22/27'), plus('28')] },
|
|
9
|
+
{ total: '22/27', terms: [plus('22'), plus('23'), plus('24'), plus('25'), plus('26'), plus('27')] },
|
|
10
|
+
{ total: '29/58', terms: [plus('29'), plus('3'), plus('40/41'), plus('50/53'), plus('54/58'), plus('490/1')] },
|
|
11
|
+
{ total: '40/41', terms: [plus('40'), plus('41')] },
|
|
12
|
+
{ total: '10/49', terms: [plus('10/15'), plus('16'), plus('17/49')] },
|
|
13
|
+
{ total: '10/15', terms: [plus('10/11'), plus('12'), plus('13'), plus('14'), plus('15'), plus('19')] },
|
|
14
|
+
{ total: '10/11', terms: [plus('10'), plus('11')] },
|
|
15
|
+
{ total: '10', terms: [plus('100'), plus('101')] },
|
|
16
|
+
{ total: '13', terms: [plus('130/1'), plus('132'), plus('133')] },
|
|
17
|
+
{ total: '17/49', terms: [plus('17'), plus('42/48'), plus('492/3')] },
|
|
18
|
+
{ total: '42/48', terms: [plus('42'), plus('43'), plus('44'), plus('46'), plus('45'), plus('47/48')] },
|
|
19
|
+
{ total: '45', terms: [plus('450/3'), plus('454/9')] },
|
|
20
|
+
// Balance sheet total = total of liabilities and equity
|
|
21
|
+
{ total: '20/58', terms: [plus('10/49')] },
|
|
22
|
+
// Income statement
|
|
23
|
+
{ total: '75/76B', terms: [plus('75'), plus('76B')] },
|
|
24
|
+
{ total: '65/66B', terms: [plus('65'), plus('66B')] },
|
|
25
|
+
{
|
|
26
|
+
total: '9901',
|
|
27
|
+
terms: [plus('9900'), minus('62'), minus('630'), minus('631/4'), minus('635/8'), minus('640/8'), plus('649'), minus('66A')],
|
|
28
|
+
},
|
|
29
|
+
{ total: '9903', terms: [plus('9901'), plus('75/76B'), minus('65/66B')] },
|
|
30
|
+
{ total: '9904', terms: [plus('9903'), plus('780'), minus('680'), minus('67/77')] },
|
|
31
|
+
{ total: '9905', terms: [plus('9904'), plus('789'), minus('689')] },
|
|
32
|
+
// Appropriation
|
|
33
|
+
{ total: '9906', terms: [plus('9905'), plus('14P')] },
|
|
34
|
+
// Fixed assets: net book value = acquisition value − depreciation
|
|
35
|
+
{ total: '21', terms: [plus('8059'), minus('8129')] },
|
|
36
|
+
{ total: '22/27', terms: [plus('8199'), minus('8329')] },
|
|
37
|
+
{ total: '28', terms: [plus('8395')] },
|
|
38
|
+
];
|
|
39
|
+
/**
|
|
40
|
+
* Checks every identity whose total and at least one operand are present in
|
|
41
|
+
* `values`. Missing operands count as zero, as in Filing. Returns the
|
|
42
|
+
* violations (empty = consistent).
|
|
43
|
+
*/
|
|
44
|
+
export function checkBnbEquations(values, options = {}) {
|
|
45
|
+
const tolerance = options.tolerance ?? 0.005;
|
|
46
|
+
const identities = options.identities ?? ABRIDGED_IDENTITIES;
|
|
47
|
+
const violations = [];
|
|
48
|
+
for (const identity of identities) {
|
|
49
|
+
for (const column of ['n', 'p']) {
|
|
50
|
+
const actual = values[identity.total]?.[column];
|
|
51
|
+
if (actual == null)
|
|
52
|
+
continue;
|
|
53
|
+
// An identity whose operands are all absent is not part of the form
|
|
54
|
+
// being filed (e.g. the fixed-assets annex was not provided).
|
|
55
|
+
if (!identity.terms.some((t) => values[t.code]?.[column] != null))
|
|
56
|
+
continue;
|
|
57
|
+
const expected = identity.terms.reduce((sum, t) => sum + t.sign * (values[t.code]?.[column] ?? 0), 0);
|
|
58
|
+
if (Math.abs(expected - actual) > tolerance) {
|
|
59
|
+
violations.push({
|
|
60
|
+
code: identity.total,
|
|
61
|
+
column,
|
|
62
|
+
expected: round2(expected),
|
|
63
|
+
actual: round2(actual),
|
|
64
|
+
rule: `${identity.total} = ${identity.terms
|
|
65
|
+
.map((t, k) => `${t.sign < 0 ? '- ' : k === 0 ? '' : '+ '}${t.code}`)
|
|
66
|
+
.join(' ')}`,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return violations;
|
|
72
|
+
}
|
|
73
|
+
/** The one rounding rule, at two decimals. See `rounding.ts`. */
|
|
74
|
+
function round2(v) {
|
|
75
|
+
return roundCurrency(v, 2);
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=equations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"equations.js","sourceRoot":"","sources":["../src/equations.ts"],"names":[],"mappings":"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"}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** Thrown by {@link generateCbsoXbrl} when the input cannot produce a valid filing. */
|
|
2
|
+
export declare class CbsoInputError extends Error {
|
|
3
|
+
/** Dotted path of the offending field, e.g. `"address.postalCode"`. */
|
|
4
|
+
readonly path: string;
|
|
5
|
+
constructor(message: string,
|
|
6
|
+
/** Dotted path of the offending field, e.g. `"address.postalCode"`. */
|
|
7
|
+
path: string);
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,uFAAuF;AACvF,qBAAa,cAAe,SAAQ,KAAK;IAGrC,uEAAuE;aACvD,IAAI,EAAE,MAAM;gBAF5B,OAAO,EAAE,MAAM;IACf,uEAAuE;IACvD,IAAI,EAAE,MAAM;CAK/B"}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** Thrown by {@link generateCbsoXbrl} when the input cannot produce a valid filing. */
|
|
2
|
+
export class CbsoInputError extends Error {
|
|
3
|
+
path;
|
|
4
|
+
constructor(message,
|
|
5
|
+
/** Dotted path of the offending field, e.g. `"address.postalCode"`. */
|
|
6
|
+
path) {
|
|
7
|
+
super(`${path}: ${message}`);
|
|
8
|
+
this.path = path;
|
|
9
|
+
this.name = 'CbsoInputError';
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,uFAAuF;AACvF,MAAM,OAAO,cAAe,SAAQ,KAAK;IAIrB;IAHlB,YACE,OAAe;IACf,uEAAuE;IACvD,IAAY;QAE5B,KAAK,CAAC,GAAG,IAAI,KAAK,OAAO,EAAE,CAAC,CAAC;QAFb,SAAI,GAAJ,IAAI,CAAQ;QAG5B,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAC;IAC/B,CAAC;CACF"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fact keys — the dimensional way to name a line of the annual accounts.
|
|
3
|
+
*
|
|
4
|
+
* The CBSO taxonomy has no element per reporting code: a code is a generic
|
|
5
|
+
* metric plus a set of explicit dimension members. A fact key writes that
|
|
6
|
+
* signature down, metric first:
|
|
7
|
+
*
|
|
8
|
+
* met:am1|bas:m9|rst:m2 → "Créances à un an au plus" (40/41)
|
|
9
|
+
* met:am2|bas:m44|ntr:m4 → "Bénéfice (Perte) reporté(e)" (14)
|
|
10
|
+
*
|
|
11
|
+
* A key names the members that matter and leaves the rest implicit, so it can
|
|
12
|
+
* be stored next to a statement line without repeating the whole context. It
|
|
13
|
+
* resolves against the template to exactly one reporting code, or throws.
|
|
14
|
+
*/
|
|
15
|
+
import { CbsoInputError } from './errors.js';
|
|
16
|
+
import type { BnbCode, CbsoTemplate, TemplateFact, YearValues } from './types.js';
|
|
17
|
+
/**
|
|
18
|
+
* One line of a financial statement, keyed by its fact signature rather than
|
|
19
|
+
* by a reporting code. Shaped after the rows a book-keeping system produces:
|
|
20
|
+
* `xbrl_element` holds the key, `amount` the figure of the financial year.
|
|
21
|
+
*/
|
|
22
|
+
export interface FactKeyLine {
|
|
23
|
+
/** Fact key, e.g. `"met:am1|bas:m9|rst:m2"`. */
|
|
24
|
+
xbrl_element: string;
|
|
25
|
+
/** Amount of the current financial year. */
|
|
26
|
+
amount?: number | null;
|
|
27
|
+
/** Amount of the previous financial year, when the line carries one. */
|
|
28
|
+
previous_amount?: number | null;
|
|
29
|
+
}
|
|
30
|
+
/** Thrown when a fact key names no line of the model, or more than one. */
|
|
31
|
+
export declare class CbsoFactKeyError extends CbsoInputError {
|
|
32
|
+
/** The key that could not be resolved. */
|
|
33
|
+
readonly key: string;
|
|
34
|
+
/** Reporting codes the key could stand for, when it is ambiguous. */
|
|
35
|
+
readonly candidates: readonly BnbCode[];
|
|
36
|
+
constructor(message: string,
|
|
37
|
+
/** The key that could not be resolved. */
|
|
38
|
+
key: string,
|
|
39
|
+
/** Reporting codes the key could stand for, when it is ambiguous. */
|
|
40
|
+
candidates?: readonly BnbCode[]);
|
|
41
|
+
}
|
|
42
|
+
interface ParsedKey {
|
|
43
|
+
metric: string;
|
|
44
|
+
members: Record<string, string>;
|
|
45
|
+
}
|
|
46
|
+
/** `"met:am1|bas:m9|rst:m2"` → metric plus `{ "dim:bas": "bas:m9", … }`. */
|
|
47
|
+
export declare function parseFactKey(key: string): ParsedKey;
|
|
48
|
+
/**
|
|
49
|
+
* Reporting code a fact key stands for.
|
|
50
|
+
*
|
|
51
|
+
* Candidates are the facts of the template that carry the key's metric and
|
|
52
|
+
* every one of its members; the winner is the one that adds the fewest members
|
|
53
|
+
* of its own — `met:am1|bas:m2` is "ACTIFS IMMOBILISÉS" (21/28) and not one of
|
|
54
|
+
* the three lines under it. A tie means the key does not say enough: the error
|
|
55
|
+
* lists the codes it could stand for.
|
|
56
|
+
*/
|
|
57
|
+
export declare function resolveFactKey(key: string, template: CbsoTemplate): BnbCode;
|
|
58
|
+
/**
|
|
59
|
+
* Turns statement lines keyed by fact signature into the `values` map of
|
|
60
|
+
* {@link CbsoInput}. Lines whose two amounts are both absent are skipped; a
|
|
61
|
+
* code reached twice with conflicting figures is an error, not a silent
|
|
62
|
+
* overwrite.
|
|
63
|
+
*/
|
|
64
|
+
export declare function valuesFromFactKeys(lines: readonly FactKeyLine[], template: CbsoTemplate): Record<BnbCode, YearValues>;
|
|
65
|
+
/** Fact key of one template fact, in the same syntax {@link parseFactKey} reads. */
|
|
66
|
+
export declare function factKeyOf(fact: TemplateFact): string;
|
|
67
|
+
export {};
|
|
68
|
+
//# sourceMappingURL=fact-keys.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fact-keys.d.ts","sourceRoot":"","sources":["../src/fact-keys.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAElF;;;;GAIG;AACH,MAAM,WAAW,WAAW;IAC1B,gDAAgD;IAChD,YAAY,EAAE,MAAM,CAAC;IACrB,4CAA4C;IAC5C,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,wEAAwE;IACxE,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC;AAED,2EAA2E;AAC3E,qBAAa,gBAAiB,SAAQ,cAAc;IAGhD,0CAA0C;aAC1B,GAAG,EAAE,MAAM;IAC3B,qEAAqE;aACrD,UAAU,EAAE,SAAS,OAAO,EAAE;gBAJ9C,OAAO,EAAE,MAAM;IACf,0CAA0C;IAC1B,GAAG,EAAE,MAAM;IAC3B,qEAAqE;IACrD,UAAU,GAAE,SAAS,OAAO,EAAO;CAKtD;AAKD,UAAU,SAAS;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC;AAED,4EAA4E;AAC5E,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAkBnD;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,GAAG,OAAO,CAiC3E;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,SAAS,WAAW,EAAE,EAC7B,QAAQ,EAAE,YAAY,GACrB,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,CAiB7B;AAED,oFAAoF;AACpF,wBAAgB,SAAS,CAAC,IAAI,EAAE,YAAY,GAAG,MAAM,CAMpD"}
|