@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.
- package/dist/accounting/index.d.ts +9 -0
- package/dist/accounting/index.js +14 -0
- package/dist/accounting/line-generator.d.ts +34 -0
- package/dist/accounting/line-generator.js +136 -0
- package/dist/accounting/tax-codes.d.ts +32 -0
- package/dist/accounting/tax-codes.js +279 -0
- package/dist/accounting/traktamente-rates.d.ts +48 -0
- package/dist/accounting/traktamente-rates.js +325 -0
- package/dist/accounting/types.d.ts +69 -0
- package/dist/accounting/types.js +5 -0
- package/dist/accounting/validation.d.ts +41 -0
- package/dist/accounting/validation.js +118 -0
- package/dist/auth/fortnox-login.d.ts +15 -0
- package/dist/auth/fortnox-login.js +170 -0
- package/dist/auth/index.d.ts +3 -0
- package/dist/auth/index.js +3 -0
- package/dist/auth/prompts.d.ts +6 -0
- package/dist/auth/prompts.js +56 -0
- package/dist/auth/token-store.d.ts +19 -0
- package/dist/auth/token-store.js +54 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +21 -0
- package/dist/progress/index.d.ts +1 -0
- package/dist/progress/index.js +1 -0
- package/dist/progress/sync-progress.d.ts +31 -0
- package/dist/progress/sync-progress.js +65 -0
- package/dist/services/bokio-journal.d.ts +29 -0
- package/dist/services/bokio-journal.js +175 -0
- package/dist/services/document-download.d.ts +46 -0
- package/dist/services/document-download.js +105 -0
- package/dist/services/fortnox-inbox.d.ts +18 -0
- package/dist/services/fortnox-inbox.js +150 -0
- package/dist/services/fortnox-journal.d.ts +22 -0
- package/dist/services/fortnox-journal.js +166 -0
- package/dist/services/index.d.ts +6 -0
- package/dist/services/index.js +6 -0
- package/dist/services/journal-sync.d.ts +23 -0
- package/dist/services/journal-sync.js +124 -0
- package/dist/services/journal.service.d.ts +45 -0
- package/dist/services/journal.service.js +204 -0
- package/dist/storage/filesystem.d.ts +49 -0
- package/dist/storage/filesystem.js +122 -0
- package/dist/storage/index.d.ts +2 -0
- package/dist/storage/index.js +1 -0
- package/dist/storage/interface.d.ts +48 -0
- package/dist/storage/interface.js +5 -0
- package/dist/sync-types.d.ts +61 -0
- package/dist/sync-types.js +1 -0
- package/dist/transformers/bokio.d.ts +10 -0
- package/dist/transformers/bokio.js +56 -0
- package/dist/transformers/fortnox.d.ts +6 -0
- package/dist/transformers/fortnox.js +39 -0
- package/dist/transformers/index.d.ts +3 -0
- package/dist/transformers/index.js +2 -0
- package/dist/types/discarded-item.d.ts +29 -0
- package/dist/types/discarded-item.js +1 -0
- package/dist/types/document.d.ts +63 -0
- package/dist/types/document.js +9 -0
- package/dist/types/exported-document.d.ts +61 -0
- package/dist/types/exported-document.js +9 -0
- package/dist/types/exported-fiscal-year.d.ts +10 -0
- package/dist/types/exported-fiscal-year.js +1 -0
- package/dist/types/exported-inbox-document.d.ts +14 -0
- package/dist/types/exported-inbox-document.js +10 -0
- package/dist/types/fiscal-year.d.ts +10 -0
- package/dist/types/fiscal-year.js +1 -0
- package/dist/types/index.d.ts +10 -0
- package/dist/types/index.js +10 -0
- package/dist/types/journal-entry.d.ts +79 -0
- package/dist/types/journal-entry.js +12 -0
- package/dist/types/ledger-account.d.ts +5 -0
- package/dist/types/ledger-account.js +1 -0
- package/dist/utils/file-namer.d.ts +48 -0
- package/dist/utils/file-namer.js +80 -0
- package/dist/utils/git.d.ts +9 -0
- package/dist/utils/git.js +41 -0
- package/dist/utils/index.d.ts +6 -0
- package/dist/utils/index.js +6 -0
- package/dist/utils/paths.d.ts +17 -0
- package/dist/utils/paths.js +24 -0
- package/dist/utils/retry.d.ts +17 -0
- package/dist/utils/retry.js +48 -0
- package/dist/utils/templates.d.ts +12 -0
- package/dist/utils/templates.js +222 -0
- package/dist/utils/yaml.d.ts +12 -0
- package/dist/utils/yaml.js +47 -0
- package/dist/yaml/entry-helpers.d.ts +57 -0
- package/dist/yaml/entry-helpers.js +125 -0
- package/dist/yaml/index.d.ts +2 -0
- package/dist/yaml/index.js +2 -0
- package/dist/yaml/yaml-serializer.d.ts +21 -0
- package/dist/yaml/yaml-serializer.js +60 -0
- 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,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 };
|