ohadakit 1.0.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.
Files changed (44) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +385 -0
  3. package/dist/browser/ohadakit.iife.js +3 -0
  4. package/dist/browser/ohadakit.umd.js +3 -0
  5. package/dist/cjs/index.js +6456 -0
  6. package/dist/esm/index.js +6456 -0
  7. package/dist/types/AccountBook.d.ts +58 -0
  8. package/dist/types/LedgerEngine.d.ts +45 -0
  9. package/dist/types/core/Account.d.ts +49 -0
  10. package/dist/types/core/AccountIndex.d.ts +31 -0
  11. package/dist/types/core/AccountRegistry.d.ts +39 -0
  12. package/dist/types/core/types.d.ts +127 -0
  13. package/dist/types/custom/CustomAccountIndex.d.ts +28 -0
  14. package/dist/types/custom/CustomAccountManager.d.ts +44 -0
  15. package/dist/types/custom/CustomAccountValidator.d.ts +23 -0
  16. package/dist/types/custom/errors.d.ts +37 -0
  17. package/dist/types/custom/index.d.ts +8 -0
  18. package/dist/types/custom/types.d.ts +52 -0
  19. package/dist/types/data/ohada/accounts-flat.d.ts +27 -0
  20. package/dist/types/data/ohada/class-1-ressources.d.ts +3 -0
  21. package/dist/types/data/ohada/class-2-immobilisations.d.ts +3 -0
  22. package/dist/types/data/ohada/class-3-stocks.d.ts +3 -0
  23. package/dist/types/data/ohada/class-4-tiers.d.ts +3 -0
  24. package/dist/types/data/ohada/class-5-tresorerie.d.ts +3 -0
  25. package/dist/types/data/ohada/class-6-charges.d.ts +3 -0
  26. package/dist/types/data/ohada/class-7-produits.d.ts +3 -0
  27. package/dist/types/data/ohada/class-8-hao.d.ts +3 -0
  28. package/dist/types/data/ohada/class-9-engagements.d.ts +3 -0
  29. package/dist/types/data/ohada/classes.d.ts +3 -0
  30. package/dist/types/data/ohada/index.d.ts +26 -0
  31. package/dist/types/data/ohada/types.d.ts +39 -0
  32. package/dist/types/data/ohada-accounts.d.ts +6 -0
  33. package/dist/types/export/Exporter.d.ts +11 -0
  34. package/dist/types/i18n/TranslationService.d.ts +15 -0
  35. package/dist/types/i18n/account-names.d.ts +3 -0
  36. package/dist/types/i18n/index.d.ts +4 -0
  37. package/dist/types/i18n/types.d.ts +13 -0
  38. package/dist/types/index.d.ts +25 -0
  39. package/dist/types/query/QueryEngine.d.ts +30 -0
  40. package/dist/types/storage/LocalStorage.d.ts +14 -0
  41. package/dist/types/storage/MemoryStorage.d.ts +11 -0
  42. package/dist/types/validation/errors.d.ts +43 -0
  43. package/dist/types/validation/validators.d.ts +14 -0
  44. package/package.json +75 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025-Present Justin Dah-kenangnon <dah.kenangnon@gmail.com>
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,385 @@
1
+ <div align="center">
2
+
3
+ # OhadaKit
4
+
5
+ **Production-ready TypeScript SDK for the OHADA/SYSCOHADA accounting chart of accounts**
6
+
7
+ [![npm version](https://img.shields.io/npm/v/ohadakit.svg)](https://www.npmjs.com/package/ohadakit)
8
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
9
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.0+-3178c6.svg)](https://www.typescriptlang.org/)
10
+
11
+ [Installation](#installation) · [Quick Start](#quick-start) · [API](#core-api) · [GitHub](https://github.com/Dahkenangnon/OhadaKit)
12
+
13
+ </div>
14
+
15
+ ---
16
+
17
+ ## Features
18
+
19
+ - **Complete OHADA Chart** — All 1000+ accounts across 9 classes
20
+ - **O(1) Lookups** — Map-based indexes and Trie prefix search for instant retrieval
21
+ - **Type-Safe** — Full TypeScript support with branded types and `Result<T, E>` pattern
22
+ - **Query Builder** — Fluent API with filters, fuzzy search, and sorting
23
+ - **i18n** — Localized account names in French, English, Portuguese, and Spanish
24
+ - **Custom Accounts** — Overlay pattern for creating custom sub-accounts and overriding labels
25
+ - **Pluggable Storage** — Attach notes with any storage backend (memory, localStorage, custom)
26
+ - **Export** — JSON (flat/hierarchical) and CSV output formats
27
+ - **Zero Dependencies** — Core SDK has no external dependencies
28
+
29
+ ## Installation
30
+
31
+ ```bash
32
+ npm install ohadakit
33
+ ```
34
+
35
+ ## Quick Start
36
+
37
+ ```typescript
38
+ import { LedgerEngine } from 'ohadakit';
39
+
40
+ const ledger = new LedgerEngine();
41
+
42
+ // Get an account (Result type — explicit error handling)
43
+ const result = ledger.get('4111');
44
+ if (result.ok) {
45
+ console.log(result.data.name); // "Clients"
46
+ console.log(result.data.pathString); // "41 > 411 > 4111"
47
+ }
48
+
49
+ // Or use convenience accessors
50
+ const account = ledger.getOrNull('4111'); // Account | null
51
+ const account2 = ledger.getOrThrow('411'); // Account (throws on error)
52
+
53
+ // Query builder
54
+ const expenses = ledger.query()
55
+ .inClass('6')
56
+ .atLevel(3)
57
+ .nameContains('personnel')
58
+ .sortBy('code', 'asc')
59
+ .execute();
60
+ ```
61
+
62
+ ## Core API
63
+
64
+ ### Account Access
65
+
66
+ ```typescript
67
+ ledger.get('4111') // Result<Account> with validation
68
+ ledger.getOrNull('4111') // Account | null
69
+ ledger.getOrThrow('4111') // Account (throws on error)
70
+
71
+ // Direct registry access
72
+ ledger.registry.has('4111') // boolean
73
+ ledger.registry.getByClass('4') // Account[]
74
+ ledger.registry.getByLevel(3) // Account[]
75
+ ledger.registry.searchByPrefix('41') // Account[] (Trie-based)
76
+ ```
77
+
78
+ ### Query Builder
79
+
80
+ ```typescript
81
+ ledger.query()
82
+ .inClass(['4', '5']) // Filter by class(es)
83
+ .atLevel([3, 4]) // Filter by level(s)
84
+ .withParent('41') // Filter by parent
85
+ .nameContains('client') // Search in name
86
+ .codeMatches(/^6[0-3]/) // Regex pattern
87
+ .where(acc => acc.isLeaf) // Custom predicate
88
+ .sortBy('code', 'asc') // Sort results
89
+ .offset(10).limit(20) // Pagination
90
+ .execute(); // Get Account[]
91
+
92
+ // Convenience methods
93
+ ledger.query().inClass('4').count(); // number
94
+ ledger.query().inClass('4').first(); // Account | null
95
+ ledger.query().inClass('4').exists(); // boolean
96
+
97
+ // Fuzzy search
98
+ ledger.query()
99
+ .search('cliens', { fuzzy: true, threshold: 0.6 })
100
+ .execute();
101
+ ```
102
+
103
+ ### Account Relationships
104
+
105
+ ```typescript
106
+ const account = ledger.getOrThrow('4111');
107
+
108
+ account.parent // Account | null
109
+ account.children // Account[]
110
+ account.ancestors // Account[] (nearest parent first)
111
+ account.siblings // Account[]
112
+ account.path // Account[] (root to self)
113
+ account.pathString // "41 > 411 > 4111"
114
+ account.isLeaf // boolean
115
+ account.isRoot // boolean
116
+ account.depth // number
117
+
118
+ account.isDescendantOf('4') // true
119
+ account.isAncestorOf('41111') // true (if exists)
120
+ account.getDescendants() // Account[]
121
+ account.getDescendantsAtLevel(2) // grandchildren only
122
+ ```
123
+
124
+ ### Internationalization (i18n)
125
+
126
+ Supports 4 OHADA locales: French (fr), English (en), Portuguese (pt), Spanish (es).
127
+
128
+ ```typescript
129
+ // Set locale at creation
130
+ const ledger = new LedgerEngine({ locale: 'en' });
131
+
132
+ // Or change later
133
+ ledger.setLocale('en');
134
+ ledger.getLocale(); // 'en'
135
+ ledger.getAvailableLocales(); // ['fr', 'en', 'pt', 'es']
136
+
137
+ // Get localized account name (falls back to French if translation missing)
138
+ ledger.getLocalizedName('4111'); // English name or French fallback
139
+
140
+ // Direct TranslationService access
141
+ ledger.i18n.getAccountName('10', 'Capital');
142
+ ledger.i18n.hasTranslation('10');
143
+ ```
144
+
145
+ ### Custom Accounts
146
+
147
+ Extend the immutable OHADA chart with custom sub-accounts and label overrides.
148
+
149
+ ```typescript
150
+ import { CustomAccountManager, MemoryStorage } from 'ohadakit';
151
+
152
+ const manager = new CustomAccountManager({
153
+ storage: new MemoryStorage()
154
+ });
155
+ await manager.initialize();
156
+
157
+ // Create a custom sub-account (3+ characters, must start with parent code)
158
+ const result = await manager.createAccount({
159
+ code: '411-VIP',
160
+ name: 'Clients VIP',
161
+ parentCode: '411'
162
+ });
163
+
164
+ // Override label of an existing account
165
+ await manager.updateLabel('4111', 'Clients - Particuliers');
166
+
167
+ // Query custom + official accounts together
168
+ manager.getByCode('411-VIP'); // Account
169
+ manager.getAll(); // All official + custom accounts
170
+ manager.getCustomAccounts(); // Only custom accounts
171
+ ```
172
+
173
+ ### AccountBook (Unified Facade)
174
+
175
+ `AccountBook` wraps all OhadaKit features into a single entry point: official accounts, custom accounts, label overrides, notes, i18n, and export — with snapshot/restore for state management.
176
+
177
+ ```typescript
178
+ import { AccountBook, MemoryStorage } from 'ohadakit';
179
+
180
+ const book = new AccountBook({ storage: new MemoryStorage() });
181
+ await book.initialize();
182
+
183
+ // Access any account (official + custom, with label overrides applied)
184
+ const account = book.getAccountOrNull('411');
185
+
186
+ // Create custom sub-accounts
187
+ await book.createAccount({ code: '411-VIP', name: 'Clients VIP', parentCode: '411' });
188
+
189
+ // Override labels
190
+ await book.updateLabel('4111', 'Clients - Particuliers');
191
+
192
+ // Notes work on both official and custom accounts
193
+ await book.setNote('411-VIP', 'High-value clients');
194
+
195
+ // Export merged data (official + custom + overrides)
196
+ const json = book.exportToJSON({ pretty: true });
197
+ const csv = book.exportToCSV();
198
+
199
+ // i18n
200
+ book.setLocale('en');
201
+ book.getLocalizedName('10'); // English name
202
+
203
+ // Stats
204
+ const stats = await book.getStats();
205
+ // { total, byClass, byLevel, customAccountCount, labelOverrideCount, noteCount }
206
+ ```
207
+
208
+ #### Snapshot / Restore
209
+
210
+ Capture and restore the entire book state (custom accounts, label overrides, notes) as a plain JSON object:
211
+
212
+ ```typescript
213
+ // Take a snapshot
214
+ const snapshot = await book.snapshot();
215
+ // { version, timestamp, locale, customAccounts, labelOverrides, notes }
216
+
217
+ // Store it anywhere (database, file, API)
218
+ const json = JSON.stringify(snapshot);
219
+
220
+ // Restore into a fresh or existing book
221
+ const result = await book.restore(JSON.parse(json));
222
+ if (!result.ok) {
223
+ console.error('Restore failed:', result.error.message);
224
+ }
225
+ ```
226
+
227
+ #### When to use AccountBook vs LedgerEngine
228
+
229
+ | Use case | Recommendation |
230
+ |----------|---------------|
231
+ | Quick lookups on official accounts only | `LedgerEngine` |
232
+ | Custom accounts + label overrides + notes | `AccountBook` |
233
+ | App needs snapshot/restore of chart state | `AccountBook` |
234
+ | Prototype without persistence wiring | `LedgerEngine` |
235
+
236
+ ### Notes Storage
237
+
238
+ ```typescript
239
+ import { LedgerEngine, LocalStorageAdapter } from 'ohadakit';
240
+
241
+ // In-memory (default)
242
+ const ledger = new LedgerEngine();
243
+
244
+ // Browser localStorage
245
+ const ledger = new LedgerEngine({
246
+ storage: new LocalStorageAdapter('myapp:')
247
+ });
248
+
249
+ await ledger.setNote('5121', 'Mobile Money Orange');
250
+ await ledger.getNote('5121'); // 'Mobile Money Orange'
251
+ await ledger.deleteNote('5121');
252
+ await ledger.hasNote('5121'); // false
253
+ await ledger.getAllNotes(); // Map<string, string>
254
+ ```
255
+
256
+ ### Export
257
+
258
+ ```typescript
259
+ // JSON (flat or hierarchical)
260
+ ledger.exportToJSON({ structure: 'flat', pretty: true });
261
+ ledger.exportToJSON({ structure: 'hierarchical' });
262
+
263
+ // CSV
264
+ ledger.exportToCSV({ columns: ['code', 'name', 'level'] });
265
+ ledger.exportToCSV({ delimiter: ';', includeHeader: true });
266
+
267
+ // Export specific class
268
+ ledger.exportClass('4', 'json', { structure: 'flat' });
269
+ ledger.exportClass('4', 'csv', { columns: ['code', 'name'] });
270
+ ```
271
+
272
+ ### Validation
273
+
274
+ ```typescript
275
+ import { validateAccountCodeFormat, AccountNotFoundError } from 'ohadakit';
276
+
277
+ // Format validation (no registry lookup)
278
+ const formatResult = validateAccountCodeFormat('4111'); // Result<string>
279
+
280
+ // Full validation with Result type
281
+ const result = ledger.get('9999');
282
+ if (!result.ok && result.error instanceof AccountNotFoundError) {
283
+ console.error(result.error.message);
284
+ }
285
+
286
+ // Batch validation
287
+ const { valid, invalid } = ledger.validateBatch(['4111', '5121', '9999']);
288
+ // valid: [{ code: '4111', account }, { code: '5121', account }]
289
+ // invalid: [{ code: '9999', error }]
290
+ ```
291
+
292
+ ## Using OhadaKit in an Accounting App
293
+
294
+ OhadaKit manages the **chart of accounts** — it does not own journal entries, balances, or transactions. Your app links to OhadaKit via account codes used as foreign keys.
295
+
296
+ ```
297
+ ┌──────────────────────────┐ ┌──────────────────────────┐
298
+ │ Your App │ │ OhadaKit │
299
+ │ │ │ │
300
+ │ Journal Entries ────────┼─FK──▶ AccountBook │
301
+ │ Balances ───────────────┼─FK──▶ ├─ Official Accounts │
302
+ │ Trial Balance ──────────┼─FK──▶ ├─ Custom Accounts │
303
+ │ Financial Statements │ │ ├─ Label Overrides │
304
+ │ │ │ ├─ Notes │
305
+ │ Your DB / API │ │ └─ i18n + Export │
306
+ └──────────────────────────┘ └──────────────────────────┘
307
+ ```
308
+
309
+ ### Step-by-step Integration
310
+
311
+ ```typescript
312
+ import { AccountBook, MemoryStorage } from 'ohadakit';
313
+
314
+ // 1. Initialize the book
315
+ const book = new AccountBook({ storage: new MemoryStorage() });
316
+ await book.initialize();
317
+
318
+ // 2. Validate account codes when creating journal entries
319
+ function addJournalEntry(debitCode: string, creditCode: string, amount: number) {
320
+ if (!book.has(debitCode)) throw new Error(`Unknown account: ${debitCode}`);
321
+ if (!book.has(creditCode)) throw new Error(`Unknown account: ${creditCode}`);
322
+
323
+ // Store in your database with account codes as FKs
324
+ return { debitCode, creditCode, amount, date: new Date() };
325
+ }
326
+
327
+ // 3. Resolve names for display
328
+ function getAccountName(code: string): string {
329
+ return book.getAccountOrNull(code)?.name ?? `Unknown (${code})`;
330
+ }
331
+
332
+ // 4. Save/restore chart state alongside your app data
333
+ async function saveAppState(db: any) {
334
+ const snapshot = await book.snapshot();
335
+ await db.put('chart-state', JSON.stringify(snapshot));
336
+ }
337
+ ```
338
+
339
+ ### What OhadaKit Owns vs What Your App Owns
340
+
341
+ | Concern | Owner |
342
+ |---------|-------|
343
+ | Chart of accounts (official 1000+ accounts) | OhadaKit |
344
+ | Custom sub-accounts & label overrides | OhadaKit (via AccountBook) |
345
+ | Notes attached to accounts | OhadaKit (via AccountBook) |
346
+ | Account name translations (fr/en/pt/es) | OhadaKit |
347
+ | Journal entries, postings | Your app |
348
+ | Account balances, trial balance | Your app |
349
+ | Financial statements | Your app |
350
+ | User authentication, permissions | Your app |
351
+
352
+ ## OHADA Structure
353
+
354
+ ```
355
+ Level 1: Class (1-9)
356
+ └─ Level 2: Main account (10, 11...)
357
+ └─ Level 3: Sub-account (101, 102...)
358
+ └─ Level 4: Detail account (1011, 1012...)
359
+ ```
360
+
361
+ | Class | Description |
362
+ |-------|-------------|
363
+ | 1 | Comptes de ressources durables |
364
+ | 2 | Comptes d'actif immobilise |
365
+ | 3 | Comptes de stocks |
366
+ | 4 | Comptes de tiers |
367
+ | 5 | Comptes de tresorerie |
368
+ | 6 | Comptes de charges des activites ordinaires |
369
+ | 7 | Comptes de produits des activites ordinaires |
370
+ | 8 | Comptes des autres charges et autres produits |
371
+ | 9 | Comptes des engagements hors bilan |
372
+
373
+ ## License
374
+
375
+ MIT © [@Dahkenangnon](https://github.com/Dahkenangnon)
376
+
377
+ ---
378
+
379
+ <div align="center">
380
+
381
+ **[GitHub](https://github.com/Dahkenangnon/OhadaKit)** · **[Issues](https://github.com/Dahkenangnon/OhadaKit/issues)** · **[npm](https://www.npmjs.com/package/ohadakit)**
382
+
383
+ Questions or feedback? Reach out at **dah.kenangnon@gmail.com**
384
+
385
+ </div>