@fin.cx/skr 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/dist_ts/index.d.ts +10 -0
  2. package/dist_ts/index.js +11 -0
  3. package/dist_ts/plugins.d.ts +5 -0
  4. package/dist_ts/plugins.js +7 -0
  5. package/dist_ts/skr.api.d.ts +181 -0
  6. package/dist_ts/skr.api.js +402 -0
  7. package/dist_ts/skr.classes.account.d.ts +34 -0
  8. package/dist_ts/skr.classes.account.js +224 -0
  9. package/dist_ts/skr.classes.chartofaccounts.d.ts +99 -0
  10. package/dist_ts/skr.classes.chartofaccounts.js +377 -0
  11. package/dist_ts/skr.classes.journalentry.d.ts +33 -0
  12. package/dist_ts/skr.classes.journalentry.js +296 -0
  13. package/dist_ts/skr.classes.ledger.d.ts +61 -0
  14. package/dist_ts/skr.classes.ledger.js +363 -0
  15. package/dist_ts/skr.classes.reports.d.ts +55 -0
  16. package/dist_ts/skr.classes.reports.js +514 -0
  17. package/dist_ts/skr.classes.transaction.d.ts +37 -0
  18. package/dist_ts/skr.classes.transaction.js +263 -0
  19. package/dist_ts/skr.database.d.ts +5 -0
  20. package/dist_ts/skr.database.js +28 -0
  21. package/dist_ts/skr.types.d.ts +126 -0
  22. package/dist_ts/skr.types.js +2 -0
  23. package/dist_ts/skr03.data.d.ts +18 -0
  24. package/dist_ts/skr03.data.js +890 -0
  25. package/dist_ts/skr04.data.d.ts +18 -0
  26. package/dist_ts/skr04.data.js +912 -0
  27. package/npmextra.json +17 -0
  28. package/package.json +61 -0
  29. package/readme.hints.md +3 -0
  30. package/readme.md +409 -0
  31. package/readme.plan.md +243 -0
  32. package/ts/index.ts +10 -0
  33. package/ts/plugins.ts +7 -0
  34. package/ts/skr.api.ts +533 -0
  35. package/ts/skr.classes.account.ts +238 -0
  36. package/ts/skr.classes.chartofaccounts.ts +508 -0
  37. package/ts/skr.classes.journalentry.ts +318 -0
  38. package/ts/skr.classes.ledger.ts +528 -0
  39. package/ts/skr.classes.reports.ts +721 -0
  40. package/ts/skr.classes.transaction.ts +300 -0
  41. package/ts/skr.database.ts +39 -0
  42. package/ts/skr.types.ts +154 -0
  43. package/ts/skr03.data.ts +901 -0
  44. package/ts/skr04.data.ts +923 -0
package/ts/skr.api.ts ADDED
@@ -0,0 +1,533 @@
1
+ import * as plugins from './plugins.js';
2
+ import { ChartOfAccounts } from './skr.classes.chartofaccounts.js';
3
+ import { Ledger } from './skr.classes.ledger.js';
4
+ import { Reports } from './skr.classes.reports.js';
5
+ import { Account } from './skr.classes.account.js';
6
+ import { Transaction } from './skr.classes.transaction.js';
7
+ import { JournalEntry } from './skr.classes.journalentry.js';
8
+ import type {
9
+ IDatabaseConfig,
10
+ TSKRType,
11
+ IAccountData,
12
+ IAccountFilter,
13
+ ITransactionData,
14
+ ITransactionFilter,
15
+ IJournalEntry,
16
+ IReportParams,
17
+ ITrialBalanceReport,
18
+ IIncomeStatement,
19
+ IBalanceSheet,
20
+ } from './skr.types.js';
21
+
22
+ /**
23
+ * Main API class for SKR accounting operations
24
+ */
25
+ export class SkrApi {
26
+ private chartOfAccounts: ChartOfAccounts;
27
+ private ledger: Ledger | null = null;
28
+ private reports: Reports | null = null;
29
+ private logger: plugins.smartlog.Smartlog;
30
+ private initialized: boolean = false;
31
+ private currentSKRType: TSKRType | null = null;
32
+
33
+ constructor(private config: IDatabaseConfig) {
34
+ this.chartOfAccounts = new ChartOfAccounts(config);
35
+ this.logger = new plugins.smartlog.Smartlog({
36
+ logContext: {
37
+ company: 'fin.cx',
38
+ companyunit: 'skr',
39
+ containerName: 'SkrApi',
40
+ environment: 'local',
41
+ runtime: 'node',
42
+ zone: 'local',
43
+ },
44
+ });
45
+ }
46
+
47
+ /**
48
+ * Initialize the API with specified SKR type
49
+ */
50
+ public async initialize(skrType: TSKRType): Promise<void> {
51
+ this.logger.log('info', `Initializing SKR API with ${skrType}`);
52
+
53
+ // Initialize chart of accounts
54
+ if (skrType === 'SKR03') {
55
+ await this.chartOfAccounts.initializeSKR03();
56
+ } else if (skrType === 'SKR04') {
57
+ await this.chartOfAccounts.initializeSKR04();
58
+ } else {
59
+ throw new Error(`Invalid SKR type: ${skrType}`);
60
+ }
61
+
62
+ this.currentSKRType = skrType;
63
+ this.ledger = new Ledger(skrType);
64
+ this.reports = new Reports(skrType);
65
+ this.initialized = true;
66
+
67
+ this.logger.log('info', 'SKR API initialized successfully');
68
+ }
69
+
70
+ /**
71
+ * Ensure API is initialized
72
+ */
73
+ private ensureInitialized(): void {
74
+ if (!this.initialized || !this.currentSKRType) {
75
+ throw new Error('API not initialized. Call initialize() first.');
76
+ }
77
+ }
78
+
79
+ // ========== Account Management ==========
80
+
81
+ /**
82
+ * Create a new account
83
+ */
84
+ public async createAccount(
85
+ accountData: Partial<IAccountData>,
86
+ ): Promise<Account> {
87
+ this.ensureInitialized();
88
+ return await this.chartOfAccounts.createCustomAccount(accountData);
89
+ }
90
+
91
+ /**
92
+ * Get account by number
93
+ */
94
+ public async getAccount(accountNumber: string): Promise<Account | null> {
95
+ this.ensureInitialized();
96
+ return await this.chartOfAccounts.getAccountByNumber(accountNumber);
97
+ }
98
+
99
+ /**
100
+ * Update an account
101
+ */
102
+ public async updateAccount(
103
+ accountNumber: string,
104
+ updates: Partial<IAccountData>,
105
+ ): Promise<Account> {
106
+ this.ensureInitialized();
107
+ return await this.chartOfAccounts.updateAccount(accountNumber, updates);
108
+ }
109
+
110
+ /**
111
+ * Delete an account
112
+ */
113
+ public async deleteAccount(accountNumber: string): Promise<void> {
114
+ this.ensureInitialized();
115
+ await this.chartOfAccounts.deleteAccount(accountNumber);
116
+ }
117
+
118
+ /**
119
+ * List accounts with optional filter
120
+ */
121
+ public async listAccounts(filter?: IAccountFilter): Promise<Account[]> {
122
+ this.ensureInitialized();
123
+ return await this.chartOfAccounts.getAllAccounts(filter);
124
+ }
125
+
126
+ /**
127
+ * Search accounts by term
128
+ */
129
+ public async searchAccounts(searchTerm: string): Promise<Account[]> {
130
+ this.ensureInitialized();
131
+ return await this.chartOfAccounts.searchAccounts(searchTerm);
132
+ }
133
+
134
+ /**
135
+ * Get accounts by class
136
+ */
137
+ public async getAccountsByClass(accountClass: number): Promise<Account[]> {
138
+ this.ensureInitialized();
139
+ return await this.chartOfAccounts.getAccountsByClass(accountClass);
140
+ }
141
+
142
+ /**
143
+ * Get accounts by type
144
+ */
145
+ public async getAccountsByType(
146
+ accountType: IAccountData['accountType'],
147
+ ): Promise<Account[]> {
148
+ this.ensureInitialized();
149
+ return await this.chartOfAccounts.getAccountsByType(accountType);
150
+ }
151
+
152
+ // ========== Transaction Management ==========
153
+
154
+ /**
155
+ * Post a simple transaction
156
+ */
157
+ public async postTransaction(
158
+ transactionData: ITransactionData,
159
+ ): Promise<Transaction> {
160
+ this.ensureInitialized();
161
+ return await this.chartOfAccounts.postTransaction(transactionData);
162
+ }
163
+
164
+ /**
165
+ * Post a journal entry
166
+ */
167
+ public async postJournalEntry(
168
+ journalData: IJournalEntry,
169
+ ): Promise<JournalEntry> {
170
+ this.ensureInitialized();
171
+ return await this.chartOfAccounts.postJournalEntry(journalData);
172
+ }
173
+
174
+ /**
175
+ * Get transaction by ID
176
+ */
177
+ public async getTransaction(
178
+ transactionId: string,
179
+ ): Promise<Transaction | null> {
180
+ this.ensureInitialized();
181
+ return await Transaction.getTransactionById(transactionId);
182
+ }
183
+
184
+ /**
185
+ * List transactions with optional filter
186
+ */
187
+ public async listTransactions(
188
+ filter?: ITransactionFilter,
189
+ ): Promise<Transaction[]> {
190
+ this.ensureInitialized();
191
+ return await this.chartOfAccounts.getTransactions(filter);
192
+ }
193
+
194
+ /**
195
+ * Get transactions for specific account
196
+ */
197
+ public async getAccountTransactions(
198
+ accountNumber: string,
199
+ ): Promise<Transaction[]> {
200
+ this.ensureInitialized();
201
+ return await this.chartOfAccounts.getAccountTransactions(accountNumber);
202
+ }
203
+
204
+ /**
205
+ * Reverse a transaction
206
+ */
207
+ public async reverseTransaction(transactionId: string): Promise<Transaction> {
208
+ this.ensureInitialized();
209
+ return await this.chartOfAccounts.reverseTransaction(transactionId);
210
+ }
211
+
212
+ /**
213
+ * Reverse a journal entry
214
+ */
215
+ public async reverseJournalEntry(journalId: string): Promise<JournalEntry> {
216
+ this.ensureInitialized();
217
+ if (!this.ledger) throw new Error('Ledger not initialized');
218
+ return await this.ledger.reverseJournalEntry(journalId);
219
+ }
220
+
221
+ // ========== Reporting ==========
222
+
223
+ /**
224
+ * Generate trial balance
225
+ */
226
+ public async generateTrialBalance(
227
+ params?: IReportParams,
228
+ ): Promise<ITrialBalanceReport> {
229
+ this.ensureInitialized();
230
+ if (!this.reports) throw new Error('Reports not initialized');
231
+ return await this.reports.getTrialBalance(params);
232
+ }
233
+
234
+ /**
235
+ * Generate income statement
236
+ */
237
+ public async generateIncomeStatement(
238
+ params?: IReportParams,
239
+ ): Promise<IIncomeStatement> {
240
+ this.ensureInitialized();
241
+ if (!this.reports) throw new Error('Reports not initialized');
242
+ return await this.reports.getIncomeStatement(params);
243
+ }
244
+
245
+ /**
246
+ * Generate balance sheet
247
+ */
248
+ public async generateBalanceSheet(
249
+ params?: IReportParams,
250
+ ): Promise<IBalanceSheet> {
251
+ this.ensureInitialized();
252
+ if (!this.reports) throw new Error('Reports not initialized');
253
+ return await this.reports.getBalanceSheet(params);
254
+ }
255
+
256
+ /**
257
+ * Generate general ledger
258
+ */
259
+ public async generateGeneralLedger(params?: IReportParams): Promise<any> {
260
+ this.ensureInitialized();
261
+ if (!this.reports) throw new Error('Reports not initialized');
262
+ return await this.reports.getGeneralLedger(params);
263
+ }
264
+
265
+ /**
266
+ * Generate cash flow statement
267
+ */
268
+ public async generateCashFlowStatement(params?: IReportParams): Promise<any> {
269
+ this.ensureInitialized();
270
+ if (!this.reports) throw new Error('Reports not initialized');
271
+ return await this.reports.getCashFlowStatement(params);
272
+ }
273
+
274
+ /**
275
+ * Export report to CSV
276
+ */
277
+ public async exportReportToCSV(
278
+ reportType: 'trial_balance' | 'income_statement' | 'balance_sheet',
279
+ params?: IReportParams,
280
+ ): Promise<string> {
281
+ this.ensureInitialized();
282
+ if (!this.reports) throw new Error('Reports not initialized');
283
+ return await this.reports.exportToCSV(reportType, params);
284
+ }
285
+
286
+ /**
287
+ * Export to DATEV format
288
+ */
289
+ public async exportToDATEV(params?: IReportParams): Promise<string> {
290
+ this.ensureInitialized();
291
+ if (!this.reports) throw new Error('Reports not initialized');
292
+ return await this.reports.exportToDATEV(params);
293
+ }
294
+
295
+ // ========== Period Management ==========
296
+
297
+ /**
298
+ * Close accounting period
299
+ */
300
+ public async closePeriod(
301
+ period: string,
302
+ closingAccountNumber?: string,
303
+ ): Promise<JournalEntry[]> {
304
+ this.ensureInitialized();
305
+ if (!this.ledger) throw new Error('Ledger not initialized');
306
+ return await this.ledger.closeAccountingPeriod(
307
+ period,
308
+ closingAccountNumber,
309
+ );
310
+ }
311
+
312
+ /**
313
+ * Get account balance
314
+ */
315
+ public async getAccountBalance(
316
+ accountNumber: string,
317
+ asOfDate?: Date,
318
+ ): Promise<any> {
319
+ this.ensureInitialized();
320
+ if (!this.ledger) throw new Error('Ledger not initialized');
321
+ return await this.ledger.getAccountBalance(accountNumber, asOfDate);
322
+ }
323
+
324
+ /**
325
+ * Recalculate all account balances
326
+ */
327
+ public async recalculateBalances(): Promise<void> {
328
+ this.ensureInitialized();
329
+ if (!this.ledger) throw new Error('Ledger not initialized');
330
+ await this.ledger.recalculateAllBalances();
331
+ }
332
+
333
+ // ========== Import/Export ==========
334
+
335
+ /**
336
+ * Import accounts from CSV
337
+ */
338
+ public async importAccountsFromCSV(csvContent: string): Promise<number> {
339
+ this.ensureInitialized();
340
+ return await this.chartOfAccounts.importAccountsFromCSV(csvContent);
341
+ }
342
+
343
+ /**
344
+ * Export accounts to CSV
345
+ */
346
+ public async exportAccountsToCSV(): Promise<string> {
347
+ this.ensureInitialized();
348
+ return await this.chartOfAccounts.exportAccountsToCSV();
349
+ }
350
+
351
+ // ========== Utility Methods ==========
352
+
353
+ /**
354
+ * Get current SKR type
355
+ */
356
+ public getSKRType(): TSKRType | null {
357
+ return this.currentSKRType;
358
+ }
359
+
360
+ /**
361
+ * Get account class description
362
+ */
363
+ public getAccountClassDescription(accountClass: number): string {
364
+ this.ensureInitialized();
365
+ return this.chartOfAccounts.getAccountClassDescription(accountClass);
366
+ }
367
+
368
+ /**
369
+ * Validate double-entry rules
370
+ */
371
+ public validateDoubleEntry(
372
+ debitAmount: number,
373
+ creditAmount: number,
374
+ ): boolean {
375
+ if (!this.ledger) throw new Error('Ledger not initialized');
376
+ return this.ledger.validateDoubleEntry(debitAmount, creditAmount);
377
+ }
378
+
379
+ /**
380
+ * Get unbalanced transactions (for audit)
381
+ */
382
+ public async getUnbalancedTransactions(): Promise<Transaction[]> {
383
+ this.ensureInitialized();
384
+ if (!this.ledger) throw new Error('Ledger not initialized');
385
+ return await this.ledger.getUnbalancedTransactions();
386
+ }
387
+
388
+ /**
389
+ * Close the API and database connection
390
+ */
391
+ public async close(): Promise<void> {
392
+ await this.chartOfAccounts.close();
393
+ this.initialized = false;
394
+ this.currentSKRType = null;
395
+ this.ledger = null;
396
+ this.reports = null;
397
+ this.logger.log('info', 'SKR API closed');
398
+ }
399
+
400
+ // ========== Batch Operations ==========
401
+
402
+ /**
403
+ * Post multiple transactions
404
+ */
405
+ public async postBatchTransactions(
406
+ transactions: ITransactionData[],
407
+ ): Promise<Transaction[]> {
408
+ this.ensureInitialized();
409
+
410
+ const results: Transaction[] = [];
411
+ const errors: Array<{ index: number; error: string }> = [];
412
+
413
+ for (let i = 0; i < transactions.length; i++) {
414
+ try {
415
+ const transaction = await this.postTransaction(transactions[i]);
416
+ results.push(transaction);
417
+ } catch (error) {
418
+ errors.push({ index: i, error: error.message });
419
+ }
420
+ }
421
+
422
+ if (errors.length > 0) {
423
+ this.logger.log(
424
+ 'warn',
425
+ `Batch transaction posting completed with ${errors.length} errors`,
426
+ );
427
+ throw new Error(
428
+ `Batch posting failed for ${errors.length} transactions: ${JSON.stringify(errors)}`,
429
+ );
430
+ }
431
+
432
+ return results;
433
+ }
434
+
435
+ /**
436
+ * Create multiple accounts
437
+ */
438
+ public async createBatchAccounts(
439
+ accounts: IAccountData[],
440
+ ): Promise<Account[]> {
441
+ this.ensureInitialized();
442
+
443
+ const results: Account[] = [];
444
+ const errors: Array<{ index: number; error: string }> = [];
445
+
446
+ for (let i = 0; i < accounts.length; i++) {
447
+ try {
448
+ const account = await this.createAccount(accounts[i]);
449
+ results.push(account);
450
+ } catch (error) {
451
+ errors.push({ index: i, error: error.message });
452
+ }
453
+ }
454
+
455
+ if (errors.length > 0) {
456
+ this.logger.log(
457
+ 'warn',
458
+ `Batch account creation completed with ${errors.length} errors`,
459
+ );
460
+ throw new Error(
461
+ `Batch creation failed for ${errors.length} accounts: ${JSON.stringify(errors)}`,
462
+ );
463
+ }
464
+
465
+ return results;
466
+ }
467
+
468
+ // ========== Pagination Support ==========
469
+
470
+ /**
471
+ * Get paginated accounts
472
+ */
473
+ public async getAccountsPaginated(
474
+ page: number = 1,
475
+ pageSize: number = 50,
476
+ filter?: IAccountFilter,
477
+ ): Promise<{
478
+ data: Account[];
479
+ total: number;
480
+ page: number;
481
+ pageSize: number;
482
+ totalPages: number;
483
+ }> {
484
+ this.ensureInitialized();
485
+
486
+ const allAccounts = await this.listAccounts(filter);
487
+ const total = allAccounts.length;
488
+ const totalPages = Math.ceil(total / pageSize);
489
+ const start = (page - 1) * pageSize;
490
+ const end = start + pageSize;
491
+ const data = allAccounts.slice(start, end);
492
+
493
+ return {
494
+ data,
495
+ total,
496
+ page,
497
+ pageSize,
498
+ totalPages,
499
+ };
500
+ }
501
+
502
+ /**
503
+ * Get paginated transactions
504
+ */
505
+ public async getTransactionsPaginated(
506
+ page: number = 1,
507
+ pageSize: number = 50,
508
+ filter?: ITransactionFilter,
509
+ ): Promise<{
510
+ data: Transaction[];
511
+ total: number;
512
+ page: number;
513
+ pageSize: number;
514
+ totalPages: number;
515
+ }> {
516
+ this.ensureInitialized();
517
+
518
+ const allTransactions = await this.listTransactions(filter);
519
+ const total = allTransactions.length;
520
+ const totalPages = Math.ceil(total / pageSize);
521
+ const start = (page - 1) * pageSize;
522
+ const end = start + pageSize;
523
+ const data = allTransactions.slice(start, end);
524
+
525
+ return {
526
+ data,
527
+ total,
528
+ page,
529
+ pageSize,
530
+ totalPages,
531
+ };
532
+ }
533
+ }