@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
@@ -0,0 +1,238 @@
1
+ import * as plugins from './plugins.js';
2
+ import { getDb, getDbSync } from './skr.database.js';
3
+ import type { TAccountType, TSKRType, IAccountData } from './skr.types.js';
4
+
5
+ const { SmartDataDbDoc, svDb, unI, index, searchable } = plugins.smartdata;
6
+
7
+ @plugins.smartdata.Collection(() => getDbSync())
8
+ export class Account extends SmartDataDbDoc<Account, Account> {
9
+ @unI()
10
+ public id: string;
11
+
12
+ @svDb()
13
+ @index()
14
+ public accountNumber: string;
15
+
16
+ @svDb()
17
+ @searchable()
18
+ public accountName: string;
19
+
20
+ @svDb()
21
+ @index()
22
+ public accountClass: number;
23
+
24
+ @svDb()
25
+ public accountGroup: number;
26
+
27
+ @svDb()
28
+ public accountSubgroup: number;
29
+
30
+ @svDb()
31
+ public accountType: TAccountType;
32
+
33
+ @svDb()
34
+ @index()
35
+ public skrType: TSKRType;
36
+
37
+ @svDb()
38
+ @searchable()
39
+ public description: string;
40
+
41
+ @svDb()
42
+ public vatRate: number;
43
+
44
+ @svDb()
45
+ public balance: number;
46
+
47
+ @svDb()
48
+ public debitTotal: number;
49
+
50
+ @svDb()
51
+ public creditTotal: number;
52
+
53
+ @svDb()
54
+ public isActive: boolean;
55
+
56
+ @svDb()
57
+ public isSystemAccount: boolean;
58
+
59
+ @svDb()
60
+ public createdAt: Date;
61
+
62
+ @svDb()
63
+ public updatedAt: Date;
64
+
65
+ constructor(data?: Partial<IAccountData>) {
66
+ super();
67
+
68
+ if (data) {
69
+ this.id = plugins.smartunique.shortId();
70
+ this.accountNumber = data.accountNumber || '';
71
+ this.accountName = data.accountName || '';
72
+ this.accountClass = data.accountClass || 0;
73
+ this.accountType = data.accountType || 'asset';
74
+ this.skrType = data.skrType || 'SKR03';
75
+ this.description = data.description || '';
76
+ this.vatRate = data.vatRate || 0;
77
+ this.isActive = data.isActive !== undefined ? data.isActive : true;
78
+
79
+ // Parse account structure from number
80
+ if (this.accountNumber && this.accountNumber.length === 4) {
81
+ this.accountClass = parseInt(this.accountNumber[0]);
82
+ this.accountGroup = parseInt(this.accountNumber[1]);
83
+ this.accountSubgroup = parseInt(this.accountNumber[2]);
84
+ } else {
85
+ this.accountGroup = 0;
86
+ this.accountSubgroup = 0;
87
+ }
88
+
89
+ this.balance = 0;
90
+ this.debitTotal = 0;
91
+ this.creditTotal = 0;
92
+ this.isSystemAccount = true;
93
+ this.createdAt = new Date();
94
+ this.updatedAt = new Date();
95
+ }
96
+ }
97
+
98
+ public static async createAccount(data: IAccountData): Promise<Account> {
99
+ const account = new Account(data);
100
+ await account.save();
101
+ return account;
102
+ }
103
+
104
+ public static async getAccountByNumber(
105
+ accountNumber: string,
106
+ skrType: TSKRType,
107
+ ): Promise<Account | null> {
108
+ const account = await Account.getInstance({
109
+ accountNumber,
110
+ skrType,
111
+ });
112
+ return account;
113
+ }
114
+
115
+ public static async getAccountsByClass(
116
+ accountClass: number,
117
+ skrType: TSKRType,
118
+ ): Promise<Account[]> {
119
+ const accounts = await Account.getInstances({
120
+ accountClass,
121
+ skrType,
122
+ isActive: true,
123
+ });
124
+ return accounts;
125
+ }
126
+
127
+ public static async getAccountsByType(
128
+ accountType: TAccountType,
129
+ skrType: TSKRType,
130
+ ): Promise<Account[]> {
131
+ const accounts = await Account.getInstances({
132
+ accountType,
133
+ skrType,
134
+ isActive: true,
135
+ });
136
+ return accounts;
137
+ }
138
+
139
+ public static async searchAccounts(
140
+ searchTerm: string,
141
+ skrType?: TSKRType,
142
+ ): Promise<Account[]> {
143
+ const query: any = {};
144
+ if (skrType) {
145
+ query.skrType = skrType;
146
+ }
147
+
148
+ const accounts = await Account.getInstances(query);
149
+
150
+ // Filter by search term
151
+ const lowerSearchTerm = searchTerm.toLowerCase();
152
+ return accounts.filter(
153
+ (account) =>
154
+ account.accountNumber.includes(searchTerm) ||
155
+ account.accountName.toLowerCase().includes(lowerSearchTerm) ||
156
+ account.description.toLowerCase().includes(lowerSearchTerm),
157
+ );
158
+ }
159
+
160
+ public async updateBalance(
161
+ debitAmount: number = 0,
162
+ creditAmount: number = 0,
163
+ ): Promise<void> {
164
+ this.debitTotal += debitAmount;
165
+ this.creditTotal += creditAmount;
166
+
167
+ // Calculate balance based on account type
168
+ switch (this.accountType) {
169
+ case 'asset':
170
+ case 'expense':
171
+ // Normal debit accounts
172
+ this.balance = this.debitTotal - this.creditTotal;
173
+ break;
174
+ case 'liability':
175
+ case 'equity':
176
+ case 'revenue':
177
+ // Normal credit accounts
178
+ this.balance = this.creditTotal - this.debitTotal;
179
+ break;
180
+ }
181
+
182
+ this.updatedAt = new Date();
183
+ await this.save();
184
+ }
185
+
186
+ public async deactivate(): Promise<void> {
187
+ this.isActive = false;
188
+ this.updatedAt = new Date();
189
+ await this.save();
190
+ }
191
+
192
+ public async activate(): Promise<void> {
193
+ this.isActive = true;
194
+ this.updatedAt = new Date();
195
+ await this.save();
196
+ }
197
+
198
+ public getNormalBalance(): 'debit' | 'credit' {
199
+ switch (this.accountType) {
200
+ case 'asset':
201
+ case 'expense':
202
+ return 'debit';
203
+ case 'liability':
204
+ case 'equity':
205
+ case 'revenue':
206
+ return 'credit';
207
+ }
208
+ }
209
+
210
+ public async beforeSave(): Promise<void> {
211
+ // Validate account number format
212
+ if (!this.accountNumber || this.accountNumber.length !== 4) {
213
+ throw new Error(
214
+ `Invalid account number format: ${this.accountNumber}. Must be 4 digits.`,
215
+ );
216
+ }
217
+
218
+ // Validate account number is numeric
219
+ if (!/^\d{4}$/.test(this.accountNumber)) {
220
+ throw new Error(
221
+ `Account number must contain only digits: ${this.accountNumber}`,
222
+ );
223
+ }
224
+
225
+ // Validate account class matches first digit
226
+ const firstDigit = parseInt(this.accountNumber[0]);
227
+ if (this.accountClass !== firstDigit) {
228
+ throw new Error(
229
+ `Account class ${this.accountClass} does not match account number ${this.accountNumber}`,
230
+ );
231
+ }
232
+
233
+ // Validate SKR type
234
+ if (this.skrType !== 'SKR03' && this.skrType !== 'SKR04') {
235
+ throw new Error(`Invalid SKR type: ${this.skrType}`);
236
+ }
237
+ }
238
+ }