@moatless/bookkeeping 0.1.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 (93) hide show
  1. package/dist/accounting/index.d.ts +9 -0
  2. package/dist/accounting/index.js +14 -0
  3. package/dist/accounting/line-generator.d.ts +34 -0
  4. package/dist/accounting/line-generator.js +136 -0
  5. package/dist/accounting/tax-codes.d.ts +32 -0
  6. package/dist/accounting/tax-codes.js +279 -0
  7. package/dist/accounting/traktamente-rates.d.ts +48 -0
  8. package/dist/accounting/traktamente-rates.js +325 -0
  9. package/dist/accounting/types.d.ts +69 -0
  10. package/dist/accounting/types.js +5 -0
  11. package/dist/accounting/validation.d.ts +41 -0
  12. package/dist/accounting/validation.js +118 -0
  13. package/dist/auth/fortnox-login.d.ts +15 -0
  14. package/dist/auth/fortnox-login.js +170 -0
  15. package/dist/auth/index.d.ts +3 -0
  16. package/dist/auth/index.js +3 -0
  17. package/dist/auth/prompts.d.ts +6 -0
  18. package/dist/auth/prompts.js +56 -0
  19. package/dist/auth/token-store.d.ts +19 -0
  20. package/dist/auth/token-store.js +54 -0
  21. package/dist/index.d.ts +10 -0
  22. package/dist/index.js +21 -0
  23. package/dist/progress/index.d.ts +1 -0
  24. package/dist/progress/index.js +1 -0
  25. package/dist/progress/sync-progress.d.ts +31 -0
  26. package/dist/progress/sync-progress.js +65 -0
  27. package/dist/services/bokio-journal.d.ts +29 -0
  28. package/dist/services/bokio-journal.js +175 -0
  29. package/dist/services/document-download.d.ts +46 -0
  30. package/dist/services/document-download.js +105 -0
  31. package/dist/services/fortnox-inbox.d.ts +18 -0
  32. package/dist/services/fortnox-inbox.js +150 -0
  33. package/dist/services/fortnox-journal.d.ts +22 -0
  34. package/dist/services/fortnox-journal.js +166 -0
  35. package/dist/services/index.d.ts +6 -0
  36. package/dist/services/index.js +6 -0
  37. package/dist/services/journal-sync.d.ts +23 -0
  38. package/dist/services/journal-sync.js +124 -0
  39. package/dist/services/journal.service.d.ts +45 -0
  40. package/dist/services/journal.service.js +204 -0
  41. package/dist/storage/filesystem.d.ts +49 -0
  42. package/dist/storage/filesystem.js +122 -0
  43. package/dist/storage/index.d.ts +2 -0
  44. package/dist/storage/index.js +1 -0
  45. package/dist/storage/interface.d.ts +48 -0
  46. package/dist/storage/interface.js +5 -0
  47. package/dist/sync-types.d.ts +61 -0
  48. package/dist/sync-types.js +1 -0
  49. package/dist/transformers/bokio.d.ts +10 -0
  50. package/dist/transformers/bokio.js +56 -0
  51. package/dist/transformers/fortnox.d.ts +6 -0
  52. package/dist/transformers/fortnox.js +39 -0
  53. package/dist/transformers/index.d.ts +3 -0
  54. package/dist/transformers/index.js +2 -0
  55. package/dist/types/discarded-item.d.ts +29 -0
  56. package/dist/types/discarded-item.js +1 -0
  57. package/dist/types/document.d.ts +63 -0
  58. package/dist/types/document.js +9 -0
  59. package/dist/types/exported-document.d.ts +61 -0
  60. package/dist/types/exported-document.js +9 -0
  61. package/dist/types/exported-fiscal-year.d.ts +10 -0
  62. package/dist/types/exported-fiscal-year.js +1 -0
  63. package/dist/types/exported-inbox-document.d.ts +14 -0
  64. package/dist/types/exported-inbox-document.js +10 -0
  65. package/dist/types/fiscal-year.d.ts +10 -0
  66. package/dist/types/fiscal-year.js +1 -0
  67. package/dist/types/index.d.ts +10 -0
  68. package/dist/types/index.js +10 -0
  69. package/dist/types/journal-entry.d.ts +79 -0
  70. package/dist/types/journal-entry.js +12 -0
  71. package/dist/types/ledger-account.d.ts +5 -0
  72. package/dist/types/ledger-account.js +1 -0
  73. package/dist/utils/file-namer.d.ts +48 -0
  74. package/dist/utils/file-namer.js +80 -0
  75. package/dist/utils/git.d.ts +9 -0
  76. package/dist/utils/git.js +41 -0
  77. package/dist/utils/index.d.ts +6 -0
  78. package/dist/utils/index.js +6 -0
  79. package/dist/utils/paths.d.ts +17 -0
  80. package/dist/utils/paths.js +24 -0
  81. package/dist/utils/retry.d.ts +17 -0
  82. package/dist/utils/retry.js +48 -0
  83. package/dist/utils/templates.d.ts +12 -0
  84. package/dist/utils/templates.js +222 -0
  85. package/dist/utils/yaml.d.ts +12 -0
  86. package/dist/utils/yaml.js +47 -0
  87. package/dist/yaml/entry-helpers.d.ts +57 -0
  88. package/dist/yaml/entry-helpers.js +125 -0
  89. package/dist/yaml/index.d.ts +2 -0
  90. package/dist/yaml/index.js +2 -0
  91. package/dist/yaml/yaml-serializer.d.ts +21 -0
  92. package/dist/yaml/yaml-serializer.js +60 -0
  93. package/package.json +37 -0
@@ -0,0 +1,325 @@
1
+ /**
2
+ * Swedish traktamente (per diem) rates from Skatteverket
3
+ *
4
+ * Rates are updated annually and published by Skatteverket.
5
+ * Source: https://skatteverket.se/utlandstraktamente
6
+ */
7
+ /**
8
+ * Traktamente rates by country code and year (fullDay amount in SEK)
9
+ * halfDay = Math.floor(fullDay / 2)
10
+ */
11
+ const TRAKTAMENTE_RATES = {
12
+ SE: { 2025: 290, 2026: 300 }, // Sweden (domestic)
13
+ AL: { 2025: 345, 2026: 351 }, // Albania
14
+ DZ: { 2025: 376, 2026: 334 }, // Algeria
15
+ AS: { 2025: 1020, 2026: 889 }, // American Samoa
16
+ AO: { 2025: 502, 2026: 540 }, // Angola
17
+ AI: { 2025: 1170, 2026: 1037 }, // Anguilla
18
+ AG: { 2025: 829, 2026: 742 }, // Antigua and Barbuda
19
+ AR: { 2025: 415, 2026: 388 }, // Argentina
20
+ AM: { 2025: 521, 2026: 469 }, // Armenia
21
+ AW: { 2025: 925, 2026: 864 }, // Aruba
22
+ AU: { 2025: 833, 2026: 727 }, // Australia
23
+ AZ: { 2025: 473, 2026: 437 }, // Azerbaijan
24
+ BS: { 2025: 1291, 2026: 1146 }, // Bahamas
25
+ BH: { 2025: 1011, 2026: 881 }, // Bahrain
26
+ BD: { 2025: 424, 2026: 399 }, // Bangladesh
27
+ BB: { 2025: 1067, 2026: 930 }, // Barbados
28
+ BY: { 2025: 290, 2026: 300 }, // Belarus
29
+ BE: { 2025: 924, 2026: 897 }, // Belgium
30
+ BZ: { 2025: 683, 2026: 591 }, // Belize
31
+ BJ: { 2025: 592, 2026: 562 }, // Benin
32
+ BM: { 2025: 1361, 2026: 1217 }, // Bermuda
33
+ BO: { 2025: 435, 2026: 406 }, // Bolivia
34
+ BQ: { 2025: 824, 2026: 722 }, // Bonaire
35
+ BA: { 2025: 374, 2026: 355 }, // Bosnia and Herzegovina
36
+ BW: { 2025: 423, 2026: 358 }, // Botswana
37
+ BR: { 2025: 402, 2026: 376 }, // Brazil
38
+ BN: { 2025: 523, 2026: 458 }, // Brunei
39
+ BG: { 2025: 449, 2026: 459 }, // Bulgaria
40
+ BF: { 2025: 460, 2026: 421 }, // Burkina Faso
41
+ BI: { 2025: 389, 2026: 426 }, // Burundi
42
+ KY: { 2025: 993, 2026: 887 }, // Cayman Islands
43
+ CF: { 2025: 488, 2026: 459 }, // Central African Republic
44
+ CL: { 2025: 521, 2026: 479 }, // Chile
45
+ CO: { 2025: 343, 2026: 378 }, // Colombia
46
+ CK: { 2025: 674, 2026: 557 }, // Cook Islands
47
+ CR: { 2025: 770, 2026: 677 }, // Costa Rica
48
+ CW: { 2025: 740, 2026: 645 }, // Curacao
49
+ CY: { 2025: 650, 2026: 601 }, // Cyprus
50
+ DK: { 2025: 1268, 2026: 1226 }, // Denmark
51
+ DJ: { 2025: 699, 2026: 619 }, // Djibouti
52
+ DO: { 2025: 444, 2026: 457 }, // Dominican Republic
53
+ EC: { 2025: 660, 2026: 573 }, // Ecuador
54
+ EG: { 2025: 290, 2026: 300 }, // Egypt
55
+ GQ: { 2025: 701, 2026: 652 }, // Equatorial Guinea
56
+ CI: { 2025: 757, 2026: 755 }, // Ivory Coast
57
+ SV: { 2025: 537, 2026: 462 }, // El Salvador
58
+ ER: { 2025: 467, 2026: 399 }, // Eritrea
59
+ EE: { 2025: 731, 2026: 683 }, // Estonia
60
+ SZ: { 2025: 310, 2026: 300 }, // Eswatini
61
+ ET: { 2025: 290, 2026: 300 }, // Ethiopia
62
+ FJ: { 2025: 491, 2026: 415 }, // Fiji
63
+ PH: { 2025: 537, 2026: 466 }, // Philippines
64
+ FI: { 2025: 952, 2026: 968 }, // Finland
65
+ FR: { 2025: 910, 2026: 850 }, // France
66
+ GF: { 2025: 765, 2026: 735 }, // French Guiana
67
+ PF: { 2025: 986, 2026: 926 }, // French Polynesia
68
+ AE: { 2025: 1012, 2026: 883 }, // United Arab Emirates
69
+ GA: { 2025: 706, 2026: 650 }, // Gabon
70
+ GM: { 2025: 337, 2026: 304 }, // Gambia
71
+ GE: { 2025: 370, 2026: 331 }, // Georgia
72
+ GH: { 2025: 335, 2026: 580 }, // Ghana
73
+ GI: { 2025: 702, 2026: 654 }, // Gibraltar
74
+ GR: { 2025: 746, 2026: 695 }, // Greece
75
+ GD: { 2025: 677, 2026: 603 }, // Grenada
76
+ GP: { 2025: 772, 2026: 749 }, // Guadeloupe
77
+ GU: { 2025: 868, 2026: 765 }, // Guam
78
+ GT: { 2025: 650, 2026: 561 }, // Guatemala
79
+ GN: { 2025: 658, 2026: 566 }, // Guinea
80
+ GY: { 2025: 738, 2026: 716 }, // Guyana
81
+ HT: { 2025: 838, 2026: 870 }, // Haiti
82
+ HN: { 2025: 511, 2026: 423 }, // Honduras
83
+ HK: { 2025: 1018, 2026: 895 }, // Hong Kong
84
+ IN: { 2025: 347, 2026: 300 }, // India
85
+ ID: { 2025: 518, 2026: 418 }, // Indonesia
86
+ IQ: { 2025: 676, 2026: 590 }, // Iraq
87
+ IE: { 2025: 1099, 2026: 1038 }, // Ireland
88
+ IS: { 2025: 1167, 2026: 1125 }, // Iceland
89
+ IL: { 2025: 947, 2026: 956 }, // Israel
90
+ IT: { 2025: 828, 2026: 802 }, // Italy
91
+ JM: { 2025: 494, 2026: 425 }, // Jamaica
92
+ JP: { 2025: 454, 2026: 386 }, // Japan
93
+ JO: { 2025: 925, 2026: 803 }, // Jordan
94
+ KH: { 2025: 579, 2026: 532 }, // Cambodia
95
+ CM: { 2025: 546, 2026: 524 }, // Cameroon
96
+ CA: { 2025: 951, 2026: 895 }, // Canada
97
+ KZ: { 2025: 383, 2026: 353 }, // Kazakhstan
98
+ KE: { 2025: 518, 2026: 472 }, // Kenya
99
+ CN: { 2025: 672, 2026: 581 }, // China
100
+ KG: { 2025: 290, 2026: 300 }, // Kyrgyzstan
101
+ KI: { 2025: 367, 2026: 309 }, // Kiribati
102
+ CG: { 2025: 741, 2026: 734 }, // Congo (Brazzaville)
103
+ CD: { 2025: 650, 2026: 990 }, // Congo (DRC)
104
+ XK: { 2025: 290, 2026: 300 }, // Kosovo
105
+ HR: { 2025: 558, 2026: 550 }, // Croatia
106
+ CU: { 2025: 478, 2026: 587 }, // Cuba
107
+ KW: { 2025: 882, 2026: 849 }, // Kuwait
108
+ LA: { 2025: 290, 2026: 300 }, // Laos
109
+ LS: { 2025: 290, 2026: 300 }, // Lesotho
110
+ LV: { 2025: 804, 2026: 763 }, // Latvia
111
+ LR: { 2025: 711, 2026: 649 }, // Liberia
112
+ LY: { 2025: 290, 2026: 300 }, // Libya
113
+ LI: { 2025: 1180, 2026: 1120 }, // Liechtenstein
114
+ LT: { 2025: 671, 2026: 635 }, // Lithuania
115
+ LU: { 2025: 1022, 2026: 953 }, // Luxembourg
116
+ MO: { 2025: 682, 2026: 590 }, // Macao
117
+ MG: { 2025: 290, 2026: 300 }, // Madagascar
118
+ MW: { 2025: 327, 2026: 308 }, // Malawi
119
+ MY: { 2025: 332, 2026: 319 }, // Malaysia
120
+ MV: { 2025: 545, 2026: 503 }, // Maldives
121
+ ML: { 2025: 502, 2026: 488 }, // Mali
122
+ MT: { 2025: 658, 2026: 659 }, // Malta
123
+ MA: { 2025: 506, 2026: 507 }, // Morocco
124
+ MQ: { 2025: 811, 2026: 753 }, // Martinique
125
+ MR: { 2025: 388, 2026: 350 }, // Mauritania
126
+ MU: { 2025: 383, 2026: 349 }, // Mauritius
127
+ MX: { 2025: 577, 2026: 562 }, // Mexico
128
+ FM: { 2025: 643, 2026: 560 }, // Micronesia
129
+ MZ: { 2025: 440, 2026: 403 }, // Mozambique
130
+ MD: { 2025: 429, 2026: 414 }, // Moldova
131
+ MC: { 2025: 1070, 2026: 1073 }, // Monaco
132
+ MN: { 2025: 398, 2026: 307 }, // Mongolia
133
+ ME: { 2025: 411, 2026: 391 }, // Montenegro
134
+ MM: { 2025: 336, 2026: 367 }, // Myanmar
135
+ NA: { 2025: 311, 2026: 300 }, // Namibia
136
+ NL: { 2025: 756, 2026: 745 }, // Netherlands
137
+ NP: { 2025: 290, 2026: 300 }, // Nepal
138
+ NI: { 2025: 527, 2026: 469 }, // Nicaragua
139
+ NE: { 2025: 410, 2026: 394 }, // Niger
140
+ MK: { 2025: 315, 2026: 312 }, // North Macedonia
141
+ NO: { 2025: 1095, 2026: 1054 }, // Norway
142
+ NC: { 2025: 923, 2026: 862 }, // New Caledonia
143
+ NZ: { 2025: 640, 2026: 525 }, // New Zealand
144
+ OM: { 2025: 944, 2026: 822 }, // Oman
145
+ PK: { 2025: 290, 2026: 300 }, // Pakistan
146
+ PA: { 2025: 757, 2026: 673 }, // Panama
147
+ PG: { 2025: 598, 2026: 507 }, // Papua New Guinea
148
+ PY: { 2025: 315, 2026: 349 }, // Paraguay
149
+ PE: { 2025: 484, 2026: 482 }, // Peru
150
+ PL: { 2025: 615, 2026: 597 }, // Poland
151
+ PT: { 2025: 619, 2026: 616 }, // Portugal
152
+ PR: { 2025: 806, 2026: 701 }, // Puerto Rico
153
+ QA: { 2025: 931, 2026: 837 }, // Qatar
154
+ RE: { 2025: 763, 2026: 751 }, // Reunion
155
+ RO: { 2025: 462, 2026: 414 }, // Romania
156
+ RW: { 2025: 290, 2026: 300 }, // Rwanda
157
+ RU: { 2025: 589, 2026: 661 }, // Russia
158
+ LC: { 2025: 789, 2026: 706 }, // Saint Lucia
159
+ VC: { 2025: 514, 2026: 441 }, // Saint Vincent and the Grenadines
160
+ SB: { 2025: 729, 2026: 648 }, // Solomon Islands
161
+ WS: { 2025: 616, 2026: 537 }, // Samoa
162
+ SA: { 2025: 1158, 2026: 1016 }, // Saudi Arabia
163
+ CH: { 2025: 1402, 2026: 1332 }, // Switzerland
164
+ SN: { 2025: 673, 2026: 626 }, // Senegal
165
+ RS: { 2025: 515, 2026: 533 }, // Serbia
166
+ SC: { 2025: 1011, 2026: 846 }, // Seychelles
167
+ SL: { 2025: 391, 2026: 345 }, // Sierra Leone
168
+ SG: { 2025: 961, 2026: 845 }, // Singapore
169
+ SX: { 2025: 831, 2026: 724 }, // Sint Maarten
170
+ SK: { 2025: 763, 2026: 737 }, // Slovakia
171
+ SI: { 2025: 617, 2026: 574 }, // Slovenia
172
+ ES: { 2025: 676, 2026: 635 }, // Spain
173
+ LK: { 2025: 515, 2026: 424 }, // Sri Lanka
174
+ GB: { 2025: 986, 2026: 901 }, // United Kingdom
175
+ SR: { 2025: 543, 2026: 424 }, // Suriname
176
+ ZA: { 2025: 383, 2026: 349 }, // South Africa
177
+ KR: { 2025: 617, 2026: 517 }, // South Korea
178
+ TJ: { 2025: 290, 2026: 300 }, // Tajikistan
179
+ TW: { 2025: 590, 2026: 554 }, // Taiwan
180
+ TZ: { 2025: 341, 2026: 331 }, // Tanzania
181
+ TD: { 2025: 624, 2026: 580 }, // Chad
182
+ TH: { 2025: 567, 2026: 521 }, // Thailand
183
+ CZ: { 2025: 684, 2026: 695 }, // Czech Republic
184
+ TG: { 2025: 560, 2026: 533 }, // Togo
185
+ TO: { 2025: 498, 2026: 438 }, // Tonga
186
+ TT: { 2025: 923, 2026: 801 }, // Trinidad and Tobago
187
+ TN: { 2025: 290, 2026: 300 }, // Tunisia
188
+ TR: { 2025: 342, 2026: 366 }, // Turkey
189
+ TM: { 2025: 1628, 2026: 1454 }, // Turkmenistan
190
+ DE: { 2025: 774, 2026: 760 }, // Germany
191
+ UG: { 2025: 458, 2026: 446 }, // Uganda
192
+ UA: { 2025: 334, 2026: 300 }, // Ukraine
193
+ HU: { 2025: 637, 2026: 679 }, // Hungary
194
+ UY: { 2025: 604, 2026: 595 }, // Uruguay
195
+ US: { 2025: 1152, 2026: 1049 }, // United States
196
+ UZ: { 2025: 318, 2026: 324 }, // Uzbekistan
197
+ VU: { 2025: 697, 2026: 603 }, // Vanuatu
198
+ VE: { 2025: 549, 2026: 300 }, // Venezuela
199
+ VN: { 2025: 404, 2026: 328 }, // Vietnam
200
+ ZM: { 2025: 322, 2026: 384 }, // Zambia
201
+ AT: { 2025: 781, 2026: 730 }, // Austria
202
+ TL: { 2025: 525, 2026: 451 }, // East Timor
203
+ XX: { 2025: 499, 2026: 470 }, // Other countries (fallback)
204
+ };
205
+ /**
206
+ * Get traktamente rate for a specific country and year
207
+ */
208
+ export function getTraktamenteRate(countryCode, year) {
209
+ const code = countryCode.toUpperCase();
210
+ const countryRates = TRAKTAMENTE_RATES[code];
211
+ if (!countryRates) {
212
+ return undefined;
213
+ }
214
+ const fullDay = countryRates[year];
215
+ if (fullDay === undefined) {
216
+ return undefined;
217
+ }
218
+ const halfDay = Math.floor(fullDay / 2);
219
+ const isDomestic = code === "SE";
220
+ return {
221
+ countryCode: code,
222
+ year,
223
+ fullDay,
224
+ halfDay,
225
+ ...(isDomestic ? { night: halfDay } : {}),
226
+ };
227
+ }
228
+ /**
229
+ * Get all available rates for a year
230
+ */
231
+ export function getAllRatesForYear(year) {
232
+ const rates = [];
233
+ for (const [code, yearRates] of Object.entries(TRAKTAMENTE_RATES)) {
234
+ const fullDay = yearRates[year];
235
+ if (fullDay !== undefined) {
236
+ const halfDay = Math.floor(fullDay / 2);
237
+ const isDomestic = code === "SE";
238
+ rates.push({
239
+ countryCode: code,
240
+ year,
241
+ fullDay,
242
+ halfDay,
243
+ ...(isDomestic ? { night: halfDay } : {}),
244
+ });
245
+ }
246
+ }
247
+ return rates;
248
+ }
249
+ /**
250
+ * Get available years
251
+ */
252
+ export function getAvailableYears() {
253
+ const years = new Set();
254
+ for (const yearRates of Object.values(TRAKTAMENTE_RATES)) {
255
+ for (const year of Object.keys(yearRates)) {
256
+ years.add(Number(year));
257
+ }
258
+ }
259
+ return [...years].sort((a, b) => b - a);
260
+ }
261
+ /**
262
+ * Check if a country code is supported
263
+ */
264
+ export function isSupportedCountry(countryCode, year) {
265
+ const countryRates = TRAKTAMENTE_RATES[countryCode.toUpperCase()];
266
+ return countryRates !== undefined && year in countryRates;
267
+ }
268
+ export function calculateTraktamente(countryCode, year, departureDate, departureTime, returnDate, returnTime) {
269
+ const rate = getTraktamenteRate(countryCode, year);
270
+ if (!rate) {
271
+ return undefined;
272
+ }
273
+ const departure = new Date(departureDate);
274
+ const returnD = new Date(returnDate);
275
+ const msPerDay = 24 * 60 * 60 * 1000;
276
+ const totalDays = Math.round((returnD.getTime() - departure.getTime()) / msPerDay) + 1;
277
+ if (totalDays < 1) {
278
+ return undefined;
279
+ }
280
+ const depParts = departureTime.split(":").map(Number);
281
+ const retParts = returnTime.split(":").map(Number);
282
+ const depHour = depParts[0] ?? 0;
283
+ const retHour = retParts[0] ?? 0;
284
+ const retMin = retParts[1] ?? 0;
285
+ const returnMinutes = retHour * 60 + retMin;
286
+ const departureIsFullDay = depHour < 12;
287
+ const returnIsFullDay = returnMinutes > 19 * 60;
288
+ let fullDays = 0;
289
+ let halfDays = 0;
290
+ if (totalDays === 1) {
291
+ if (departureIsFullDay && returnIsFullDay) {
292
+ fullDays = 1;
293
+ }
294
+ else {
295
+ halfDays = 1;
296
+ }
297
+ }
298
+ else {
299
+ const middleDays = Math.max(0, totalDays - 2);
300
+ fullDays += middleDays;
301
+ if (departureIsFullDay) {
302
+ fullDays += 1;
303
+ }
304
+ else {
305
+ halfDays += 1;
306
+ }
307
+ if (returnIsFullDay) {
308
+ fullDays += 1;
309
+ }
310
+ else {
311
+ halfDays += 1;
312
+ }
313
+ }
314
+ const totalAmount = fullDays * rate.fullDay + halfDays * rate.halfDay;
315
+ return {
316
+ rate,
317
+ departureDate,
318
+ departureTime,
319
+ returnDate,
320
+ returnTime,
321
+ fullDays,
322
+ halfDays,
323
+ totalAmount,
324
+ };
325
+ }
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Shared accounting types for tax codes, journal entries, and line generation
3
+ * Used by both server API and gitops CLI tools
4
+ */
5
+ export type TaxCodeDirection = "PURCHASE" | "SALE";
6
+ export type TaxCodeTerritory = "DOMESTIC" | "EU" | "OUTSIDE_EU";
7
+ export type TaxCodeKind = "VAT" | "SALES_TAX" | "OTHER";
8
+ export interface TaxCodePostingConfig {
9
+ inputVatAccountCode?: string;
10
+ outputVatAccountCode?: string;
11
+ reverseChargeOutputAccountCode?: string;
12
+ reverseChargeInputAccountCode?: string;
13
+ }
14
+ export interface TaxCode {
15
+ code: string;
16
+ description: string;
17
+ kind: TaxCodeKind;
18
+ direction: TaxCodeDirection;
19
+ territory: TaxCodeTerritory;
20
+ ratePercent: number;
21
+ isReverseCharge: boolean;
22
+ isZeroRated: boolean;
23
+ isExempt: boolean;
24
+ posting: TaxCodePostingConfig;
25
+ }
26
+ export interface JournalLineInput {
27
+ line_number: number;
28
+ ledger_account_code: string;
29
+ debit?: number;
30
+ credit?: number;
31
+ memo?: string;
32
+ tax_code?: string;
33
+ cost_center_code?: string;
34
+ }
35
+ export interface GenerateLinesInput {
36
+ taxCode: TaxCode;
37
+ baseAmount: number;
38
+ baseAccountCode: string;
39
+ balancingAccountCode: string;
40
+ memo?: string;
41
+ startingLineNumber?: number;
42
+ }
43
+ export interface GeneratedLinesResult {
44
+ lines: JournalLineInput[];
45
+ totals: {
46
+ net: number;
47
+ vat: number;
48
+ gross: number;
49
+ };
50
+ }
51
+ export interface CurrencyConversionDetails {
52
+ from_currency: string;
53
+ to_currency: string;
54
+ rate: number;
55
+ rate_date: string;
56
+ target_date: string;
57
+ source: string;
58
+ original_amount: number;
59
+ converted_amount: number;
60
+ }
61
+ export interface ValidationError {
62
+ field: string;
63
+ issue: string;
64
+ suggestion: string;
65
+ }
66
+ export interface ValidationResult {
67
+ valid: boolean;
68
+ errors: ValidationError[];
69
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Shared accounting types for tax codes, journal entries, and line generation
3
+ * Used by both server API and gitops CLI tools
4
+ */
5
+ export {};
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Validation utilities for journal entries and tax codes
3
+ * Shared between server API and gitops CLI
4
+ */
5
+ import type { JournalLineInput, ValidationResult } from "./types";
6
+ /**
7
+ * Validate that journal lines are balanced (debits === credits)
8
+ * @param lines Journal lines to validate
9
+ * @returns Validation result with any errors
10
+ */
11
+ export declare function validateBalance(lines: JournalLineInput[]): ValidationResult;
12
+ /**
13
+ * Validate date format (YYYY-MM-DD)
14
+ * @param date Date string to validate
15
+ * @returns True if valid format
16
+ */
17
+ export declare function validateDateFormat(date: string): boolean;
18
+ /**
19
+ * Validate amount is positive
20
+ * @param amount Amount to validate
21
+ * @returns True if amount is positive
22
+ */
23
+ export declare function validateAmountPositive(amount: number): boolean;
24
+ /**
25
+ * Validate account code format (basic 4-digit check)
26
+ * @param accountCode Account code to validate
27
+ * @returns True if valid format
28
+ */
29
+ export declare function validateAccountCodeFormat(accountCode: string): boolean;
30
+ /**
31
+ * Validate that all required line fields are present
32
+ * @param line Journal line to validate
33
+ * @returns Validation result with any errors
34
+ */
35
+ export declare function validateJournalLine(line: JournalLineInput): ValidationResult;
36
+ /**
37
+ * Validate all lines in a journal entry
38
+ * @param lines All journal lines
39
+ * @returns Validation result with any errors
40
+ */
41
+ export declare function validateAllLines(lines: JournalLineInput[]): ValidationResult;
@@ -0,0 +1,118 @@
1
+ /**
2
+ * Validation utilities for journal entries and tax codes
3
+ * Shared between server API and gitops CLI
4
+ */
5
+ import { round2 } from "./line-generator";
6
+ /**
7
+ * Validate that journal lines are balanced (debits === credits)
8
+ * @param lines Journal lines to validate
9
+ * @returns Validation result with any errors
10
+ */
11
+ export function validateBalance(lines) {
12
+ const totalDebit = round2(lines.reduce((sum, l) => sum + (l.debit ?? 0), 0));
13
+ const totalCredit = round2(lines.reduce((sum, l) => sum + (l.credit ?? 0), 0));
14
+ if (totalDebit !== totalCredit) {
15
+ return {
16
+ valid: false,
17
+ errors: [
18
+ {
19
+ field: "lines",
20
+ issue: `Entry not balanced: total debit (${totalDebit}) ≠ total credit (${totalCredit})`,
21
+ suggestion: "Ensure the sum of all debit amounts equals the sum of all credit amounts",
22
+ },
23
+ ],
24
+ };
25
+ }
26
+ return { valid: true, errors: [] };
27
+ }
28
+ /**
29
+ * Validate date format (YYYY-MM-DD)
30
+ * @param date Date string to validate
31
+ * @returns True if valid format
32
+ */
33
+ export function validateDateFormat(date) {
34
+ const dateRegex = /^\d{4}-\d{2}-\d{2}$/;
35
+ if (!dateRegex.test(date)) {
36
+ return false;
37
+ }
38
+ // Check if it's a valid date
39
+ const dateObj = new Date(date);
40
+ return !Number.isNaN(dateObj.getTime()) && dateObj.toISOString().startsWith(date);
41
+ }
42
+ /**
43
+ * Validate amount is positive
44
+ * @param amount Amount to validate
45
+ * @returns True if amount is positive
46
+ */
47
+ export function validateAmountPositive(amount) {
48
+ return amount > 0;
49
+ }
50
+ /**
51
+ * Validate account code format (basic 4-digit check)
52
+ * @param accountCode Account code to validate
53
+ * @returns True if valid format
54
+ */
55
+ export function validateAccountCodeFormat(accountCode) {
56
+ // Basic validation: 4 digits (Swedish BAS kontoplan)
57
+ return /^\d{4}$/.test(accountCode);
58
+ }
59
+ /**
60
+ * Validate that all required line fields are present
61
+ * @param line Journal line to validate
62
+ * @returns Validation result with any errors
63
+ */
64
+ export function validateJournalLine(line) {
65
+ const errors = [];
66
+ if (!line.ledger_account_code || !line.ledger_account_code.trim()) {
67
+ errors.push({
68
+ field: `line_${line.line_number}.ledger_account_code`,
69
+ issue: "Account code is required",
70
+ suggestion: "Provide a valid ledger account code",
71
+ });
72
+ }
73
+ if (!line.debit && !line.credit) {
74
+ errors.push({
75
+ field: `line_${line.line_number}`,
76
+ issue: "Line must have either debit or credit amount",
77
+ suggestion: "Set either debit or credit to a non-zero value",
78
+ });
79
+ }
80
+ if (line.debit && line.debit < 0) {
81
+ errors.push({
82
+ field: `line_${line.line_number}.debit`,
83
+ issue: "Debit amount cannot be negative",
84
+ suggestion: "Use credit for negative amounts",
85
+ });
86
+ }
87
+ if (line.credit && line.credit < 0) {
88
+ errors.push({
89
+ field: `line_${line.line_number}.credit`,
90
+ issue: "Credit amount cannot be negative",
91
+ suggestion: "Use debit for negative amounts",
92
+ });
93
+ }
94
+ return {
95
+ valid: errors.length === 0,
96
+ errors,
97
+ };
98
+ }
99
+ /**
100
+ * Validate all lines in a journal entry
101
+ * @param lines All journal lines
102
+ * @returns Validation result with any errors
103
+ */
104
+ export function validateAllLines(lines) {
105
+ const allErrors = [];
106
+ // Validate each line
107
+ for (const line of lines) {
108
+ const lineResult = validateJournalLine(line);
109
+ allErrors.push(...lineResult.errors);
110
+ }
111
+ // Validate balance
112
+ const balanceResult = validateBalance(lines);
113
+ allErrors.push(...balanceResult.errors);
114
+ return {
115
+ valid: allErrors.length === 0,
116
+ errors: allErrors,
117
+ };
118
+ }
@@ -0,0 +1,15 @@
1
+ import { type StoredFortnoxToken } from "./token-store";
2
+ declare const DEFAULT_SCOPES: ("inbox" | "bookkeeping" | "companyinformation" | "archive" | "connectfile")[];
3
+ export interface LoginOptions {
4
+ cwd: string;
5
+ clientId?: string;
6
+ clientSecret?: string;
7
+ scopes?: string[];
8
+ port?: number;
9
+ /** If true, skip prompts for existing token overwrite */
10
+ force?: boolean;
11
+ /** Callback for status messages */
12
+ onStatus?: (message: string) => void;
13
+ }
14
+ export declare function loginFortnox(options: LoginOptions): Promise<StoredFortnoxToken>;
15
+ export { DEFAULT_SCOPES as FORTNOX_DEFAULT_SCOPES };