@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.
- package/dist_ts/index.d.ts +10 -0
- package/dist_ts/index.js +11 -0
- package/dist_ts/plugins.d.ts +5 -0
- package/dist_ts/plugins.js +7 -0
- package/dist_ts/skr.api.d.ts +181 -0
- package/dist_ts/skr.api.js +402 -0
- package/dist_ts/skr.classes.account.d.ts +34 -0
- package/dist_ts/skr.classes.account.js +224 -0
- package/dist_ts/skr.classes.chartofaccounts.d.ts +99 -0
- package/dist_ts/skr.classes.chartofaccounts.js +377 -0
- package/dist_ts/skr.classes.journalentry.d.ts +33 -0
- package/dist_ts/skr.classes.journalentry.js +296 -0
- package/dist_ts/skr.classes.ledger.d.ts +61 -0
- package/dist_ts/skr.classes.ledger.js +363 -0
- package/dist_ts/skr.classes.reports.d.ts +55 -0
- package/dist_ts/skr.classes.reports.js +514 -0
- package/dist_ts/skr.classes.transaction.d.ts +37 -0
- package/dist_ts/skr.classes.transaction.js +263 -0
- package/dist_ts/skr.database.d.ts +5 -0
- package/dist_ts/skr.database.js +28 -0
- package/dist_ts/skr.types.d.ts +126 -0
- package/dist_ts/skr.types.js +2 -0
- package/dist_ts/skr03.data.d.ts +18 -0
- package/dist_ts/skr03.data.js +890 -0
- package/dist_ts/skr04.data.d.ts +18 -0
- package/dist_ts/skr04.data.js +912 -0
- package/npmextra.json +17 -0
- package/package.json +61 -0
- package/readme.hints.md +3 -0
- package/readme.md +409 -0
- package/readme.plan.md +243 -0
- package/ts/index.ts +10 -0
- package/ts/plugins.ts +7 -0
- package/ts/skr.api.ts +533 -0
- package/ts/skr.classes.account.ts +238 -0
- package/ts/skr.classes.chartofaccounts.ts +508 -0
- package/ts/skr.classes.journalentry.ts +318 -0
- package/ts/skr.classes.ledger.ts +528 -0
- package/ts/skr.classes.reports.ts +721 -0
- package/ts/skr.classes.transaction.ts +300 -0
- package/ts/skr.database.ts +39 -0
- package/ts/skr.types.ts +154 -0
- package/ts/skr03.data.ts +901 -0
- package/ts/skr04.data.ts +923 -0
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
import { Account } from './skr.classes.account.js';
|
|
3
|
+
import { Transaction } from './skr.classes.transaction.js';
|
|
4
|
+
import { JournalEntry } from './skr.classes.journalentry.js';
|
|
5
|
+
export class Ledger {
|
|
6
|
+
constructor(skrType) {
|
|
7
|
+
this.skrType = skrType;
|
|
8
|
+
this.logger = new plugins.smartlog.Smartlog({
|
|
9
|
+
logContext: {
|
|
10
|
+
company: 'fin.cx',
|
|
11
|
+
companyunit: 'skr',
|
|
12
|
+
containerName: 'Ledger',
|
|
13
|
+
environment: 'local',
|
|
14
|
+
runtime: 'node',
|
|
15
|
+
zone: 'local',
|
|
16
|
+
},
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Post a transaction with validation
|
|
21
|
+
*/
|
|
22
|
+
async postTransaction(transactionData) {
|
|
23
|
+
this.logger.log('info', `Posting transaction: ${transactionData.description}`);
|
|
24
|
+
// Ensure SKR type matches
|
|
25
|
+
const fullTransactionData = {
|
|
26
|
+
...transactionData,
|
|
27
|
+
skrType: this.skrType,
|
|
28
|
+
};
|
|
29
|
+
// Validate accounts exist
|
|
30
|
+
await this.validateAccounts([
|
|
31
|
+
transactionData.debitAccount,
|
|
32
|
+
transactionData.creditAccount,
|
|
33
|
+
]);
|
|
34
|
+
// Create and post transaction
|
|
35
|
+
const transaction = await Transaction.createTransaction(fullTransactionData);
|
|
36
|
+
this.logger.log('info', `Transaction ${transaction.transactionNumber} posted successfully`);
|
|
37
|
+
return transaction;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Post a journal entry with validation
|
|
41
|
+
*/
|
|
42
|
+
async postJournalEntry(journalData) {
|
|
43
|
+
this.logger.log('info', `Posting journal entry: ${journalData.description}`);
|
|
44
|
+
// Ensure SKR type matches
|
|
45
|
+
const fullJournalData = {
|
|
46
|
+
...journalData,
|
|
47
|
+
skrType: this.skrType,
|
|
48
|
+
};
|
|
49
|
+
// Validate all accounts exist
|
|
50
|
+
const accountNumbers = journalData.lines.map((line) => line.accountNumber);
|
|
51
|
+
await this.validateAccounts(accountNumbers);
|
|
52
|
+
// Validate journal entry is balanced
|
|
53
|
+
this.validateJournalBalance(journalData.lines);
|
|
54
|
+
// Create and post journal entry
|
|
55
|
+
const journalEntry = await JournalEntry.createJournalEntry(fullJournalData);
|
|
56
|
+
await journalEntry.post();
|
|
57
|
+
this.logger.log('info', `Journal entry ${journalEntry.journalNumber} posted successfully`);
|
|
58
|
+
return journalEntry;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Validate that accounts exist and are active
|
|
62
|
+
*/
|
|
63
|
+
async validateAccounts(accountNumbers) {
|
|
64
|
+
const uniqueAccountNumbers = [...new Set(accountNumbers)];
|
|
65
|
+
for (const accountNumber of uniqueAccountNumbers) {
|
|
66
|
+
const account = await Account.getAccountByNumber(accountNumber, this.skrType);
|
|
67
|
+
if (!account) {
|
|
68
|
+
throw new Error(`Account ${accountNumber} not found for ${this.skrType}`);
|
|
69
|
+
}
|
|
70
|
+
if (!account.isActive) {
|
|
71
|
+
throw new Error(`Account ${accountNumber} is not active`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Validate journal entry balance
|
|
77
|
+
*/
|
|
78
|
+
validateJournalBalance(lines) {
|
|
79
|
+
let totalDebits = 0;
|
|
80
|
+
let totalCredits = 0;
|
|
81
|
+
for (const line of lines) {
|
|
82
|
+
if (line.debit)
|
|
83
|
+
totalDebits += line.debit;
|
|
84
|
+
if (line.credit)
|
|
85
|
+
totalCredits += line.credit;
|
|
86
|
+
}
|
|
87
|
+
const difference = Math.abs(totalDebits - totalCredits);
|
|
88
|
+
if (difference >= 0.01) {
|
|
89
|
+
throw new Error(`Journal entry is not balanced. Debits: ${totalDebits}, Credits: ${totalCredits}`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Reverse a transaction
|
|
94
|
+
*/
|
|
95
|
+
async reverseTransaction(transactionId) {
|
|
96
|
+
this.logger.log('info', `Reversing transaction: ${transactionId}`);
|
|
97
|
+
const transaction = await Transaction.getTransactionById(transactionId);
|
|
98
|
+
if (!transaction) {
|
|
99
|
+
throw new Error(`Transaction ${transactionId} not found`);
|
|
100
|
+
}
|
|
101
|
+
if (transaction.skrType !== this.skrType) {
|
|
102
|
+
throw new Error(`Transaction ${transactionId} belongs to different SKR type`);
|
|
103
|
+
}
|
|
104
|
+
const reversalTransaction = await transaction.reverseTransaction();
|
|
105
|
+
this.logger.log('info', `Transaction reversed: ${reversalTransaction.transactionNumber}`);
|
|
106
|
+
return reversalTransaction;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Reverse a journal entry
|
|
110
|
+
*/
|
|
111
|
+
async reverseJournalEntry(journalId) {
|
|
112
|
+
this.logger.log('info', `Reversing journal entry: ${journalId}`);
|
|
113
|
+
const journalEntry = await JournalEntry.getInstance({ id: journalId });
|
|
114
|
+
if (!journalEntry) {
|
|
115
|
+
throw new Error(`Journal entry ${journalId} not found`);
|
|
116
|
+
}
|
|
117
|
+
if (journalEntry.skrType !== this.skrType) {
|
|
118
|
+
throw new Error(`Journal entry ${journalId} belongs to different SKR type`);
|
|
119
|
+
}
|
|
120
|
+
const reversalEntry = await journalEntry.reverse();
|
|
121
|
+
this.logger.log('info', `Journal entry reversed: ${reversalEntry.journalNumber}`);
|
|
122
|
+
return reversalEntry;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Get account history (all transactions for an account)
|
|
126
|
+
*/
|
|
127
|
+
async getAccountHistory(accountNumber, dateFrom, dateTo) {
|
|
128
|
+
const account = await Account.getAccountByNumber(accountNumber, this.skrType);
|
|
129
|
+
if (!account) {
|
|
130
|
+
throw new Error(`Account ${accountNumber} not found`);
|
|
131
|
+
}
|
|
132
|
+
let transactions = await Transaction.getTransactionsByAccount(accountNumber, this.skrType);
|
|
133
|
+
// Apply date filter if provided
|
|
134
|
+
if (dateFrom || dateTo) {
|
|
135
|
+
transactions = transactions.filter((transaction) => {
|
|
136
|
+
if (dateFrom && transaction.date < dateFrom)
|
|
137
|
+
return false;
|
|
138
|
+
if (dateTo && transaction.date > dateTo)
|
|
139
|
+
return false;
|
|
140
|
+
return true;
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
// Sort by date
|
|
144
|
+
transactions.sort((a, b) => a.date.getTime() - b.date.getTime());
|
|
145
|
+
return transactions;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Get account balance at a specific date
|
|
149
|
+
*/
|
|
150
|
+
async getAccountBalance(accountNumber, asOfDate) {
|
|
151
|
+
const account = await Account.getAccountByNumber(accountNumber, this.skrType);
|
|
152
|
+
if (!account) {
|
|
153
|
+
throw new Error(`Account ${accountNumber} not found`);
|
|
154
|
+
}
|
|
155
|
+
let transactions = await Transaction.getTransactionsByAccount(accountNumber, this.skrType);
|
|
156
|
+
// Filter transactions up to the specified date
|
|
157
|
+
if (asOfDate) {
|
|
158
|
+
transactions = transactions.filter((t) => t.date <= asOfDate);
|
|
159
|
+
}
|
|
160
|
+
// Calculate balance
|
|
161
|
+
let debitTotal = 0;
|
|
162
|
+
let creditTotal = 0;
|
|
163
|
+
for (const transaction of transactions) {
|
|
164
|
+
if (transaction.debitAccount === accountNumber) {
|
|
165
|
+
debitTotal += transaction.amount;
|
|
166
|
+
}
|
|
167
|
+
if (transaction.creditAccount === accountNumber) {
|
|
168
|
+
creditTotal += transaction.amount;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
// Calculate net balance based on account type
|
|
172
|
+
let balance;
|
|
173
|
+
switch (account.accountType) {
|
|
174
|
+
case 'asset':
|
|
175
|
+
case 'expense':
|
|
176
|
+
// Normal debit accounts
|
|
177
|
+
balance = debitTotal - creditTotal;
|
|
178
|
+
break;
|
|
179
|
+
case 'liability':
|
|
180
|
+
case 'equity':
|
|
181
|
+
case 'revenue':
|
|
182
|
+
// Normal credit accounts
|
|
183
|
+
balance = creditTotal - debitTotal;
|
|
184
|
+
break;
|
|
185
|
+
}
|
|
186
|
+
return {
|
|
187
|
+
accountNumber,
|
|
188
|
+
debitTotal,
|
|
189
|
+
creditTotal,
|
|
190
|
+
balance,
|
|
191
|
+
lastUpdated: new Date(),
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Close accounting period (create closing entries)
|
|
196
|
+
*/
|
|
197
|
+
async closeAccountingPeriod(period, // Format: YYYY-MM
|
|
198
|
+
closingAccountNumber = '9400') {
|
|
199
|
+
this.logger.log('info', `Closing accounting period: ${period}`);
|
|
200
|
+
const closingEntries = [];
|
|
201
|
+
// Get all revenue and expense accounts
|
|
202
|
+
const revenueAccounts = await Account.getAccountsByType('revenue', this.skrType);
|
|
203
|
+
const expenseAccounts = await Account.getAccountsByType('expense', this.skrType);
|
|
204
|
+
// Calculate totals for each account in the period
|
|
205
|
+
const periodTransactions = await Transaction.getTransactionsByPeriod(period, this.skrType);
|
|
206
|
+
// Create closing entry for revenue accounts
|
|
207
|
+
const revenueLines = [];
|
|
208
|
+
let totalRevenue = 0;
|
|
209
|
+
for (const account of revenueAccounts) {
|
|
210
|
+
const balance = await this.getAccountBalanceForPeriod(account.accountNumber, periodTransactions);
|
|
211
|
+
if (balance !== 0) {
|
|
212
|
+
// Revenue accounts have credit balance, so debit to close
|
|
213
|
+
revenueLines.push({
|
|
214
|
+
accountNumber: account.accountNumber,
|
|
215
|
+
debit: Math.abs(balance),
|
|
216
|
+
description: `Closing ${account.accountName}`,
|
|
217
|
+
});
|
|
218
|
+
totalRevenue += Math.abs(balance);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
if (totalRevenue > 0) {
|
|
222
|
+
// Credit the closing account
|
|
223
|
+
revenueLines.push({
|
|
224
|
+
accountNumber: closingAccountNumber,
|
|
225
|
+
credit: totalRevenue,
|
|
226
|
+
description: 'Revenue closing to P&L',
|
|
227
|
+
});
|
|
228
|
+
const revenueClosingEntry = await this.postJournalEntry({
|
|
229
|
+
date: new Date(),
|
|
230
|
+
description: `Closing revenue accounts for period ${period}`,
|
|
231
|
+
reference: `CLOSE-REV-${period}`,
|
|
232
|
+
lines: revenueLines,
|
|
233
|
+
skrType: this.skrType,
|
|
234
|
+
});
|
|
235
|
+
closingEntries.push(revenueClosingEntry);
|
|
236
|
+
}
|
|
237
|
+
// Create closing entry for expense accounts
|
|
238
|
+
const expenseLines = [];
|
|
239
|
+
let totalExpense = 0;
|
|
240
|
+
for (const account of expenseAccounts) {
|
|
241
|
+
const balance = await this.getAccountBalanceForPeriod(account.accountNumber, periodTransactions);
|
|
242
|
+
if (balance !== 0) {
|
|
243
|
+
// Expense accounts have debit balance, so credit to close
|
|
244
|
+
expenseLines.push({
|
|
245
|
+
accountNumber: account.accountNumber,
|
|
246
|
+
credit: Math.abs(balance),
|
|
247
|
+
description: `Closing ${account.accountName}`,
|
|
248
|
+
});
|
|
249
|
+
totalExpense += Math.abs(balance);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
if (totalExpense > 0) {
|
|
253
|
+
// Debit the closing account
|
|
254
|
+
expenseLines.push({
|
|
255
|
+
accountNumber: closingAccountNumber,
|
|
256
|
+
debit: totalExpense,
|
|
257
|
+
description: 'Expense closing to P&L',
|
|
258
|
+
});
|
|
259
|
+
const expenseClosingEntry = await this.postJournalEntry({
|
|
260
|
+
date: new Date(),
|
|
261
|
+
description: `Closing expense accounts for period ${period}`,
|
|
262
|
+
reference: `CLOSE-EXP-${period}`,
|
|
263
|
+
lines: expenseLines,
|
|
264
|
+
skrType: this.skrType,
|
|
265
|
+
});
|
|
266
|
+
closingEntries.push(expenseClosingEntry);
|
|
267
|
+
}
|
|
268
|
+
this.logger.log('info', `Period ${period} closed with ${closingEntries.length} entries`);
|
|
269
|
+
return closingEntries;
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Calculate account balance for a specific set of transactions
|
|
273
|
+
*/
|
|
274
|
+
async getAccountBalanceForPeriod(accountNumber, transactions) {
|
|
275
|
+
const account = await Account.getAccountByNumber(accountNumber, this.skrType);
|
|
276
|
+
if (!account)
|
|
277
|
+
return 0;
|
|
278
|
+
let debitTotal = 0;
|
|
279
|
+
let creditTotal = 0;
|
|
280
|
+
for (const transaction of transactions) {
|
|
281
|
+
if (transaction.debitAccount === accountNumber) {
|
|
282
|
+
debitTotal += transaction.amount;
|
|
283
|
+
}
|
|
284
|
+
if (transaction.creditAccount === accountNumber) {
|
|
285
|
+
creditTotal += transaction.amount;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
// Calculate net balance based on account type
|
|
289
|
+
switch (account.accountType) {
|
|
290
|
+
case 'asset':
|
|
291
|
+
case 'expense':
|
|
292
|
+
return debitTotal - creditTotal;
|
|
293
|
+
case 'liability':
|
|
294
|
+
case 'equity':
|
|
295
|
+
case 'revenue':
|
|
296
|
+
return creditTotal - debitTotal;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Validate double-entry rules
|
|
301
|
+
*/
|
|
302
|
+
validateDoubleEntry(debitAmount, creditAmount) {
|
|
303
|
+
return Math.abs(debitAmount - creditAmount) < 0.01;
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Get unbalanced transactions (for audit)
|
|
307
|
+
*/
|
|
308
|
+
async getUnbalancedTransactions() {
|
|
309
|
+
// In a proper double-entry system, all posted transactions should be balanced
|
|
310
|
+
// This method is mainly for audit purposes
|
|
311
|
+
const allTransactions = await Transaction.getInstances({
|
|
312
|
+
skrType: this.skrType,
|
|
313
|
+
status: 'posted',
|
|
314
|
+
});
|
|
315
|
+
// Group transactions by journal entry if they have one
|
|
316
|
+
const unbalanced = [];
|
|
317
|
+
// Since our system ensures balance at posting time,
|
|
318
|
+
// this should typically return an empty array
|
|
319
|
+
// But we include it for completeness and audit purposes
|
|
320
|
+
return unbalanced;
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Recalculate all account balances
|
|
324
|
+
*/
|
|
325
|
+
async recalculateAllBalances() {
|
|
326
|
+
this.logger.log('info', 'Recalculating all account balances');
|
|
327
|
+
// Get all accounts
|
|
328
|
+
const accounts = await Account.getInstances({ skrType: this.skrType });
|
|
329
|
+
for (const account of accounts) {
|
|
330
|
+
// Reset balances
|
|
331
|
+
account.debitTotal = 0;
|
|
332
|
+
account.creditTotal = 0;
|
|
333
|
+
account.balance = 0;
|
|
334
|
+
// Get all transactions for this account
|
|
335
|
+
const transactions = await Transaction.getTransactionsByAccount(account.accountNumber, this.skrType);
|
|
336
|
+
// Recalculate totals
|
|
337
|
+
for (const transaction of transactions) {
|
|
338
|
+
if (transaction.debitAccount === account.accountNumber) {
|
|
339
|
+
account.debitTotal += transaction.amount;
|
|
340
|
+
}
|
|
341
|
+
if (transaction.creditAccount === account.accountNumber) {
|
|
342
|
+
account.creditTotal += transaction.amount;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
// Calculate balance based on account type
|
|
346
|
+
switch (account.accountType) {
|
|
347
|
+
case 'asset':
|
|
348
|
+
case 'expense':
|
|
349
|
+
account.balance = account.debitTotal - account.creditTotal;
|
|
350
|
+
break;
|
|
351
|
+
case 'liability':
|
|
352
|
+
case 'equity':
|
|
353
|
+
case 'revenue':
|
|
354
|
+
account.balance = account.creditTotal - account.debitTotal;
|
|
355
|
+
break;
|
|
356
|
+
}
|
|
357
|
+
account.updatedAt = new Date();
|
|
358
|
+
await account.save();
|
|
359
|
+
}
|
|
360
|
+
this.logger.log('info', `Recalculated balances for ${accounts.length} accounts`);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2tyLmNsYXNzZXMubGVkZ2VyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvc2tyLmNsYXNzZXMubGVkZ2VyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sY0FBYyxDQUFDO0FBQ3hDLE9BQU8sRUFBRSxPQUFPLEVBQUUsTUFBTSwwQkFBMEIsQ0FBQztBQUNuRCxPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0sOEJBQThCLENBQUM7QUFDM0QsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLCtCQUErQixDQUFDO0FBUzdELE1BQU0sT0FBTyxNQUFNO0lBR2pCLFlBQW9CLE9BQWlCO1FBQWpCLFlBQU8sR0FBUCxPQUFPLENBQVU7UUFDbkMsSUFBSSxDQUFDLE1BQU0sR0FBRyxJQUFJLE9BQU8sQ0FBQyxRQUFRLENBQUMsUUFBUSxDQUFDO1lBQzFDLFVBQVUsRUFBRTtnQkFDVixPQUFPLEVBQUUsUUFBUTtnQkFDakIsV0FBVyxFQUFFLEtBQUs7Z0JBQ2xCLGFBQWEsRUFBRSxRQUFRO2dCQUN2QixXQUFXLEVBQUUsT0FBTztnQkFDcEIsT0FBTyxFQUFFLE1BQU07Z0JBQ2YsSUFBSSxFQUFFLE9BQU87YUFDZDtTQUNGLENBQUMsQ0FBQztJQUNMLENBQUM7SUFFRDs7T0FFRztJQUNJLEtBQUssQ0FBQyxlQUFlLENBQzFCLGVBQWlDO1FBRWpDLElBQUksQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUNiLE1BQU0sRUFDTix3QkFBd0IsZUFBZSxDQUFDLFdBQVcsRUFBRSxDQUN0RCxDQUFDO1FBRUYsMEJBQTBCO1FBQzFCLE1BQU0sbUJBQW1CLEdBQXFCO1lBQzVDLEdBQUcsZUFBZTtZQUNsQixPQUFPLEVBQUUsSUFBSSxDQUFDLE9BQU87U0FDdEIsQ0FBQztRQUVGLDBCQUEwQjtRQUMxQixNQUFNLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQztZQUMxQixlQUFlLENBQUMsWUFBWTtZQUM1QixlQUFlLENBQUMsYUFBYTtT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
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { TSKRType, ITrialBalanceReport, IIncomeStatement, IBalanceSheet, IReportParams } from './skr.types.js';
|
|
2
|
+
export declare class Reports {
|
|
3
|
+
private skrType;
|
|
4
|
+
private logger;
|
|
5
|
+
private ledger;
|
|
6
|
+
constructor(skrType: TSKRType);
|
|
7
|
+
/**
|
|
8
|
+
* Generate Trial Balance
|
|
9
|
+
*/
|
|
10
|
+
getTrialBalance(params?: IReportParams): Promise<ITrialBalanceReport>;
|
|
11
|
+
/**
|
|
12
|
+
* Generate Income Statement (P&L)
|
|
13
|
+
*/
|
|
14
|
+
getIncomeStatement(params?: IReportParams): Promise<IIncomeStatement>;
|
|
15
|
+
/**
|
|
16
|
+
* Generate Balance Sheet
|
|
17
|
+
*/
|
|
18
|
+
getBalanceSheet(params?: IReportParams): Promise<IBalanceSheet>;
|
|
19
|
+
/**
|
|
20
|
+
* Get account balance for a specific period
|
|
21
|
+
*/
|
|
22
|
+
private getAccountBalanceForPeriod;
|
|
23
|
+
/**
|
|
24
|
+
* Generate General Ledger report
|
|
25
|
+
*/
|
|
26
|
+
getGeneralLedger(params?: IReportParams): Promise<any>;
|
|
27
|
+
/**
|
|
28
|
+
* Get account transactions for reporting
|
|
29
|
+
*/
|
|
30
|
+
private getAccountTransactions;
|
|
31
|
+
/**
|
|
32
|
+
* Generate Cash Flow Statement
|
|
33
|
+
*/
|
|
34
|
+
getCashFlowStatement(params?: IReportParams): Promise<any>;
|
|
35
|
+
/**
|
|
36
|
+
* Export report to CSV format
|
|
37
|
+
*/
|
|
38
|
+
exportToCSV(reportType: 'trial_balance' | 'income_statement' | 'balance_sheet', params?: IReportParams): Promise<string>;
|
|
39
|
+
/**
|
|
40
|
+
* Convert trial balance to CSV
|
|
41
|
+
*/
|
|
42
|
+
private trialBalanceToCSV;
|
|
43
|
+
/**
|
|
44
|
+
* Convert income statement to CSV
|
|
45
|
+
*/
|
|
46
|
+
private incomeStatementToCSV;
|
|
47
|
+
/**
|
|
48
|
+
* Convert balance sheet to CSV
|
|
49
|
+
*/
|
|
50
|
+
private balanceSheetToCSV;
|
|
51
|
+
/**
|
|
52
|
+
* Export to DATEV format
|
|
53
|
+
*/
|
|
54
|
+
exportToDATEV(params?: IReportParams): Promise<string>;
|
|
55
|
+
}
|