@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,9 @@
1
+ /**
2
+ * Accounting module - Tax codes, line generation, and validation
3
+ * Shared between server API and gitops CLI
4
+ */
5
+ export * from "./types";
6
+ export * from "./tax-codes";
7
+ export * from "./line-generator";
8
+ export * from "./validation";
9
+ export * from "./traktamente-rates";
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Accounting module - Tax codes, line generation, and validation
3
+ * Shared between server API and gitops CLI
4
+ */
5
+ // Types
6
+ export * from "./types";
7
+ // Tax codes
8
+ export * from "./tax-codes";
9
+ // Line generation
10
+ export * from "./line-generator";
11
+ // Validation
12
+ export * from "./validation";
13
+ // Traktamente (Swedish per diem)
14
+ export * from "./traktamente-rates";
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Journal entry line generation from tax codes
3
+ * Shared between server API and gitops CLI
4
+ */
5
+ import type { GenerateLinesInput, GeneratedLinesResult, JournalLineInput } from "./types";
6
+ /**
7
+ * Round number to 2 decimal places
8
+ * @param value Number to round
9
+ * @returns Rounded number
10
+ */
11
+ export declare function round2(value: number): number;
12
+ /**
13
+ * Create balanced journal lines from a tax code
14
+ *
15
+ * This function generates the appropriate debit/credit lines based on the tax code:
16
+ * - Calculates VAT amounts
17
+ * - Creates appropriate accounting lines (base, VAT, balancing)
18
+ * - Handles reverse charge scenarios
19
+ * - Validates balance (total debits === total credits)
20
+ *
21
+ * @param input Line generation input parameters
22
+ * @returns Generated lines and totals
23
+ * @throws Error if validation fails or entry is unbalanced
24
+ */
25
+ export declare function createJournalLinesFromTaxCode(input: GenerateLinesInput): GeneratedLinesResult;
26
+ /**
27
+ * Calculate total debits and credits from a set of lines
28
+ * @param lines Journal lines to sum
29
+ * @returns Total debit and credit amounts
30
+ */
31
+ export declare function calculateLineTotals(lines: JournalLineInput[]): {
32
+ totalDebit: number;
33
+ totalCredit: number;
34
+ };
@@ -0,0 +1,136 @@
1
+ /**
2
+ * Journal entry line generation from tax codes
3
+ * Shared between server API and gitops CLI
4
+ */
5
+ /**
6
+ * Round number to 2 decimal places
7
+ * @param value Number to round
8
+ * @returns Rounded number
9
+ */
10
+ export function round2(value) {
11
+ return Math.round(value * 100) / 100;
12
+ }
13
+ /**
14
+ * Create balanced journal lines from a tax code
15
+ *
16
+ * This function generates the appropriate debit/credit lines based on the tax code:
17
+ * - Calculates VAT amounts
18
+ * - Creates appropriate accounting lines (base, VAT, balancing)
19
+ * - Handles reverse charge scenarios
20
+ * - Validates balance (total debits === total credits)
21
+ *
22
+ * @param input Line generation input parameters
23
+ * @returns Generated lines and totals
24
+ * @throws Error if validation fails or entry is unbalanced
25
+ */
26
+ export function createJournalLinesFromTaxCode(input) {
27
+ const { taxCode, baseAmount, baseAccountCode, balancingAccountCode, memo, startingLineNumber = 1, } = input;
28
+ // Validation
29
+ if (!baseAccountCode?.trim()) {
30
+ throw new Error("baseAccountCode is required");
31
+ }
32
+ if (!balancingAccountCode?.trim()) {
33
+ throw new Error("balancingAccountCode is required");
34
+ }
35
+ if (baseAmount <= 0) {
36
+ throw new Error("baseAmount must be > 0");
37
+ }
38
+ // Calculate amounts
39
+ const normalizedBase = round2(baseAmount);
40
+ const rate = taxCode.ratePercent / 100;
41
+ const vatAmount = round2(normalizedBase * rate);
42
+ const grossAmount = round2(normalizedBase + vatAmount);
43
+ const lines = [];
44
+ let lineNo = startingLineNumber;
45
+ const posting = taxCode.posting;
46
+ // Helper to add a line with proper normalization
47
+ const pushLine = (ledger_account_code, debit, credit) => {
48
+ const normalizedDebit = debit && debit > 0 ? round2(debit) : undefined;
49
+ const normalizedCredit = credit && credit > 0 ? round2(credit) : undefined;
50
+ if (!normalizedDebit && !normalizedCredit)
51
+ return;
52
+ lines.push({
53
+ line_number: lineNo++,
54
+ ledger_account_code,
55
+ debit: normalizedDebit,
56
+ credit: normalizedCredit,
57
+ memo,
58
+ tax_code: taxCode.code,
59
+ });
60
+ };
61
+ // Generate lines based on direction and reverse charge
62
+ if (taxCode.direction === "PURCHASE") {
63
+ if (taxCode.isReverseCharge) {
64
+ // Reverse charge purchase (e.g. EU/non-EU services)
65
+ // Base expense (debit) and supplier liability (credit) - no VAT on these
66
+ pushLine(baseAccountCode, normalizedBase, 0);
67
+ pushLine(balancingAccountCode, 0, normalizedBase);
68
+ // Reverse charge VAT accounts (if VAT rate > 0)
69
+ if (posting.reverseChargeOutputAccountCode && vatAmount > 0) {
70
+ pushLine(posting.reverseChargeOutputAccountCode, 0, vatAmount);
71
+ }
72
+ if (posting.reverseChargeInputAccountCode && vatAmount > 0) {
73
+ pushLine(posting.reverseChargeInputAccountCode, vatAmount, 0);
74
+ }
75
+ }
76
+ else {
77
+ // Normal domestic purchase
78
+ if (taxCode.ratePercent > 0 && posting.inputVatAccountCode) {
79
+ // Purchase with VAT
80
+ pushLine(baseAccountCode, normalizedBase, 0); // Expense
81
+ pushLine(posting.inputVatAccountCode, vatAmount, 0); // Input VAT
82
+ pushLine(balancingAccountCode, 0, grossAmount); // Supplier liability
83
+ }
84
+ else {
85
+ // Purchase without VAT (exempt, 0%)
86
+ pushLine(baseAccountCode, normalizedBase, 0);
87
+ pushLine(balancingAccountCode, 0, normalizedBase);
88
+ }
89
+ }
90
+ }
91
+ else if (taxCode.direction === "SALE") {
92
+ if (taxCode.isReverseCharge) {
93
+ // Reverse charge sale (e.g. domestic construction)
94
+ pushLine(balancingAccountCode, normalizedBase, 0); // AR/Bank
95
+ pushLine(baseAccountCode, 0, normalizedBase); // Revenue
96
+ }
97
+ else {
98
+ // Normal sale
99
+ if (taxCode.ratePercent > 0 && posting.outputVatAccountCode) {
100
+ // Sale with VAT
101
+ pushLine(balancingAccountCode, grossAmount, 0); // AR/Bank
102
+ pushLine(baseAccountCode, 0, normalizedBase); // Revenue
103
+ pushLine(posting.outputVatAccountCode, 0, vatAmount); // Output VAT
104
+ }
105
+ else {
106
+ // Sale without VAT (exempt, 0%, EU B2B)
107
+ pushLine(balancingAccountCode, normalizedBase, 0);
108
+ pushLine(baseAccountCode, 0, normalizedBase);
109
+ }
110
+ }
111
+ }
112
+ // Validate balance
113
+ const totalDebit = round2(lines.reduce((sum, l) => sum + (l.debit ?? 0), 0));
114
+ const totalCredit = round2(lines.reduce((sum, l) => sum + (l.credit ?? 0), 0));
115
+ if (totalDebit !== totalCredit) {
116
+ throw new Error(`Journal not balanced: debit=${totalDebit}, credit=${totalCredit}`);
117
+ }
118
+ return {
119
+ lines,
120
+ totals: {
121
+ net: normalizedBase,
122
+ vat: vatAmount,
123
+ gross: grossAmount,
124
+ },
125
+ };
126
+ }
127
+ /**
128
+ * Calculate total debits and credits from a set of lines
129
+ * @param lines Journal lines to sum
130
+ * @returns Total debit and credit amounts
131
+ */
132
+ export function calculateLineTotals(lines) {
133
+ const totalDebit = round2(lines.reduce((sum, l) => sum + (l.debit ?? 0), 0));
134
+ const totalCredit = round2(lines.reduce((sum, l) => sum + (l.credit ?? 0), 0));
135
+ return { totalDebit, totalCredit };
136
+ }
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Swedish tax code definitions and resolution logic
3
+ * Shared between server API and gitops CLI
4
+ */
5
+ import type { TaxCode } from "./types";
6
+ /**
7
+ * Swedish VAT tax codes with posting account configurations
8
+ */
9
+ export declare const SE_TAX_CODES: TaxCode[];
10
+ /**
11
+ * Resolve a tax code by its code string
12
+ * @param code Tax code string (e.g. "SE_VAT_25_PURCHASE_DOMESTIC")
13
+ * @returns Tax code configuration or undefined if not found
14
+ */
15
+ export declare function resolveTaxCode(code: string): TaxCode | undefined;
16
+ /**
17
+ * Get all available tax codes
18
+ * @returns Array of all tax codes
19
+ */
20
+ export declare function getAllTaxCodes(): TaxCode[];
21
+ /**
22
+ * Get tax codes filtered by direction (PURCHASE or SALE)
23
+ * @param direction Tax code direction
24
+ * @returns Filtered tax codes
25
+ */
26
+ export declare function getTaxCodesByDirection(direction: "PURCHASE" | "SALE"): TaxCode[];
27
+ /**
28
+ * Get tax codes filtered by territory
29
+ * @param territory Tax code territory
30
+ * @returns Filtered tax codes
31
+ */
32
+ export declare function getTaxCodesByTerritory(territory: "DOMESTIC" | "EU" | "OUTSIDE_EU"): TaxCode[];
@@ -0,0 +1,279 @@
1
+ /**
2
+ * Swedish tax code definitions and resolution logic
3
+ * Shared between server API and gitops CLI
4
+ */
5
+ /**
6
+ * Swedish VAT tax codes with posting account configurations
7
+ */
8
+ export const SE_TAX_CODES = [
9
+ // --- DOMESTIC PURCHASES (inköp i Sverige, ingående moms) ---
10
+ {
11
+ code: "SE_VAT_25_PURCHASE_DOMESTIC",
12
+ description: "Inköp i Sverige, moms 25 %",
13
+ kind: "VAT",
14
+ direction: "PURCHASE",
15
+ territory: "DOMESTIC",
16
+ ratePercent: 25,
17
+ isReverseCharge: false,
18
+ isZeroRated: false,
19
+ isExempt: false,
20
+ posting: {
21
+ inputVatAccountCode: "2641", // Ingående moms 25 %
22
+ },
23
+ },
24
+ {
25
+ code: "SE_VAT_12_PURCHASE_DOMESTIC",
26
+ description: "Inköp i Sverige, moms 12 %",
27
+ kind: "VAT",
28
+ direction: "PURCHASE",
29
+ territory: "DOMESTIC",
30
+ ratePercent: 12,
31
+ isReverseCharge: false,
32
+ isZeroRated: false,
33
+ isExempt: false,
34
+ posting: {
35
+ inputVatAccountCode: "2641", // many use same 2641 for all rates; you can split if you want
36
+ },
37
+ },
38
+ {
39
+ code: "SE_VAT_6_PURCHASE_DOMESTIC",
40
+ description: "Inköp i Sverige, moms 6 %",
41
+ kind: "VAT",
42
+ direction: "PURCHASE",
43
+ territory: "DOMESTIC",
44
+ ratePercent: 6,
45
+ isReverseCharge: false,
46
+ isZeroRated: false,
47
+ isExempt: false,
48
+ posting: {
49
+ inputVatAccountCode: "2641",
50
+ },
51
+ },
52
+ {
53
+ code: "SE_VAT_0_PURCHASE_DOMESTIC",
54
+ description: "Inköp i Sverige, 0 % moms (momspliktigt men 0 %)",
55
+ kind: "VAT",
56
+ direction: "PURCHASE",
57
+ territory: "DOMESTIC",
58
+ ratePercent: 0,
59
+ isReverseCharge: false,
60
+ isZeroRated: true,
61
+ isExempt: false,
62
+ posting: {
63
+ // no VAT accounts
64
+ },
65
+ },
66
+ {
67
+ code: "SE_VAT_EXEMPT_PURCHASE",
68
+ description: "Momsfria inköp (utanför momsens tillämpningsområde)",
69
+ kind: "VAT",
70
+ direction: "PURCHASE",
71
+ territory: "DOMESTIC",
72
+ ratePercent: 0,
73
+ isReverseCharge: false,
74
+ isZeroRated: false,
75
+ isExempt: true,
76
+ posting: {
77
+ // no VAT accounts
78
+ },
79
+ },
80
+ // --- DOMESTIC SALES (försäljning i Sverige, utgående moms) ---
81
+ {
82
+ code: "SE_VAT_25_SALE_DOMESTIC",
83
+ description: "Försäljning i Sverige, moms 25 %",
84
+ kind: "VAT",
85
+ direction: "SALE",
86
+ territory: "DOMESTIC",
87
+ ratePercent: 25,
88
+ isReverseCharge: false,
89
+ isZeroRated: false,
90
+ isExempt: false,
91
+ posting: {
92
+ outputVatAccountCode: "2611", // Utgående moms 25 %
93
+ },
94
+ },
95
+ {
96
+ code: "SE_VAT_12_SALE_DOMESTIC",
97
+ description: "Försäljning i Sverige, moms 12 %",
98
+ kind: "VAT",
99
+ direction: "SALE",
100
+ territory: "DOMESTIC",
101
+ ratePercent: 12,
102
+ isReverseCharge: false,
103
+ isZeroRated: false,
104
+ isExempt: false,
105
+ posting: {
106
+ outputVatAccountCode: "2611", // or a separate 261X if you want
107
+ },
108
+ },
109
+ {
110
+ code: "SE_VAT_6_SALE_DOMESTIC",
111
+ description: "Försäljning i Sverige, moms 6 %",
112
+ kind: "VAT",
113
+ direction: "SALE",
114
+ territory: "DOMESTIC",
115
+ ratePercent: 6,
116
+ isReverseCharge: false,
117
+ isZeroRated: false,
118
+ isExempt: false,
119
+ posting: {
120
+ outputVatAccountCode: "2611",
121
+ },
122
+ },
123
+ {
124
+ code: "SE_VAT_0_SALE_DOMESTIC",
125
+ description: "Momsfri försäljning i Sverige (undantagen/0 %)",
126
+ kind: "VAT",
127
+ direction: "SALE",
128
+ territory: "DOMESTIC",
129
+ ratePercent: 0,
130
+ isReverseCharge: false,
131
+ isZeroRated: false, // treat as exempt
132
+ isExempt: true,
133
+ posting: {
134
+ // no VAT accounts
135
+ },
136
+ },
137
+ // --- EU PURCHASES (inköp från annat EU-land, omvänd moms) ---
138
+ {
139
+ code: "SE_VAT_25_PURCHASE_EU_GOODS_RC",
140
+ description: "Inköp av varor från annat EU-land, 25 %, omvänd skattskyldighet",
141
+ kind: "VAT",
142
+ direction: "PURCHASE",
143
+ territory: "EU",
144
+ ratePercent: 25,
145
+ isReverseCharge: true,
146
+ isZeroRated: false,
147
+ isExempt: false,
148
+ posting: {
149
+ reverseChargeOutputAccountCode: "2614", // Beräknad utg moms EU varor
150
+ reverseChargeInputAccountCode: "2645", // Beräknad ing moms EU varor
151
+ },
152
+ },
153
+ {
154
+ code: "SE_VAT_25_PURCHASE_EU_SERVICES_RC",
155
+ description: "Inköp av tjänster från annat EU-land, 25 %, omvänd skattskyldighet",
156
+ kind: "VAT",
157
+ direction: "PURCHASE",
158
+ territory: "EU",
159
+ ratePercent: 25,
160
+ isReverseCharge: true,
161
+ isZeroRated: false,
162
+ isExempt: false,
163
+ posting: {
164
+ reverseChargeOutputAccountCode: "2614", // or 2615 depending on your kontoplan
165
+ reverseChargeInputAccountCode: "2645",
166
+ },
167
+ },
168
+ // --- NON-EU PURCHASES (inköp från land utanför EU, omvänd moms) ---
169
+ {
170
+ code: "SE_VAT_25_PURCHASE_NON_EU_SERVICES_RC",
171
+ description: "Inköp av tjänster från land utanför EU, 25 %, omvänd skattskyldighet",
172
+ kind: "VAT",
173
+ direction: "PURCHASE",
174
+ territory: "OUTSIDE_EU",
175
+ ratePercent: 25,
176
+ isReverseCharge: true,
177
+ isZeroRated: false,
178
+ isExempt: false,
179
+ posting: {
180
+ reverseChargeOutputAccountCode: "2614", // Beräknad utg moms tjänsteförvärv utlandet
181
+ reverseChargeInputAccountCode: "2645", // Beräknad ing moms förvärv utlandet
182
+ },
183
+ },
184
+ // --- EU SALES (försäljning till EU, 0 %) ---
185
+ {
186
+ code: "SE_VAT_0_SALE_EU_GOODS_B2B",
187
+ description: "Varuförsäljning till momsregistrerad kund i annat EU-land (0 %)",
188
+ kind: "VAT",
189
+ direction: "SALE",
190
+ territory: "EU",
191
+ ratePercent: 0,
192
+ isReverseCharge: false,
193
+ isZeroRated: true,
194
+ isExempt: false,
195
+ posting: {
196
+ // 0 % – no VAT accounts, but you track base in VAT report via this code
197
+ },
198
+ },
199
+ {
200
+ code: "SE_VAT_0_SALE_EU_SERVICES_B2B",
201
+ description: "Tjänsteförsäljning till momsregistrerad kund i annat EU-land (0 %)",
202
+ kind: "VAT",
203
+ direction: "SALE",
204
+ territory: "EU",
205
+ ratePercent: 0,
206
+ isReverseCharge: false,
207
+ isZeroRated: true,
208
+ isExempt: false,
209
+ posting: {
210
+ // 0 % – no VAT accounts
211
+ },
212
+ },
213
+ // --- DOMESTIC REVERSE CHARGE (byggtjänster m.m.) ---
214
+ {
215
+ code: "SE_VAT_RC_SALE_DOMESTIC_CONSTRUCTION",
216
+ description: "Försäljning av byggtjänster med omvänd skattskyldighet (ingen moms på fakturan)",
217
+ kind: "VAT",
218
+ direction: "SALE",
219
+ territory: "DOMESTIC",
220
+ ratePercent: 0,
221
+ isReverseCharge: true,
222
+ isZeroRated: false,
223
+ isExempt: false,
224
+ posting: {
225
+ // Seller does not post VAT on the invoice; VAT is handled by buyer.
226
+ // So no 26xx lines here.
227
+ },
228
+ },
229
+ {
230
+ code: "SE_VAT_RC_PURCHASE_DOMESTIC_CONSTRUCTION",
231
+ description: "Inköp av byggtjänster med omvänd skattskyldighet (du beräknar svensk moms)",
232
+ kind: "VAT",
233
+ direction: "PURCHASE",
234
+ territory: "DOMESTIC",
235
+ ratePercent: 25,
236
+ isReverseCharge: true,
237
+ isZeroRated: false,
238
+ isExempt: false,
239
+ posting: {
240
+ reverseChargeOutputAccountCode: "2617", // Utg moms omvänd, bygg
241
+ reverseChargeInputAccountCode: "2647", // Ing moms omvänd, bygg
242
+ },
243
+ },
244
+ ];
245
+ /**
246
+ * Tax code lookup map for fast resolution
247
+ */
248
+ const TAX_CODE_MAP = new Map(SE_TAX_CODES.map((code) => [code.code, code]));
249
+ /**
250
+ * Resolve a tax code by its code string
251
+ * @param code Tax code string (e.g. "SE_VAT_25_PURCHASE_DOMESTIC")
252
+ * @returns Tax code configuration or undefined if not found
253
+ */
254
+ export function resolveTaxCode(code) {
255
+ return TAX_CODE_MAP.get(code);
256
+ }
257
+ /**
258
+ * Get all available tax codes
259
+ * @returns Array of all tax codes
260
+ */
261
+ export function getAllTaxCodes() {
262
+ return SE_TAX_CODES;
263
+ }
264
+ /**
265
+ * Get tax codes filtered by direction (PURCHASE or SALE)
266
+ * @param direction Tax code direction
267
+ * @returns Filtered tax codes
268
+ */
269
+ export function getTaxCodesByDirection(direction) {
270
+ return SE_TAX_CODES.filter((code) => code.direction === direction);
271
+ }
272
+ /**
273
+ * Get tax codes filtered by territory
274
+ * @param territory Tax code territory
275
+ * @returns Filtered tax codes
276
+ */
277
+ export function getTaxCodesByTerritory(territory) {
278
+ return SE_TAX_CODES.filter((code) => code.territory === territory);
279
+ }
@@ -0,0 +1,48 @@
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
+ export interface TraktamenteRate {
8
+ countryCode: string;
9
+ year: number;
10
+ fullDay: number;
11
+ halfDay: number;
12
+ night?: number;
13
+ }
14
+ /**
15
+ * Get traktamente rate for a specific country and year
16
+ */
17
+ export declare function getTraktamenteRate(countryCode: string, year: number): TraktamenteRate | undefined;
18
+ /**
19
+ * Get all available rates for a year
20
+ */
21
+ export declare function getAllRatesForYear(year: number): TraktamenteRate[];
22
+ /**
23
+ * Get available years
24
+ */
25
+ export declare function getAvailableYears(): number[];
26
+ /**
27
+ * Check if a country code is supported
28
+ */
29
+ export declare function isSupportedCountry(countryCode: string, year: number): boolean;
30
+ /**
31
+ * Calculate traktamente for a trip
32
+ *
33
+ * Rules (Skatteverket):
34
+ * - Departure day: before 12:00 = full day, 12:00 or later = half day
35
+ * - Return day: after 19:00 = full day, 19:00 or earlier = half day
36
+ * - Days in between = full days
37
+ */
38
+ export interface TraktamenteCalculation {
39
+ rate: TraktamenteRate;
40
+ departureDate: string;
41
+ departureTime: string;
42
+ returnDate: string;
43
+ returnTime: string;
44
+ fullDays: number;
45
+ halfDays: number;
46
+ totalAmount: number;
47
+ }
48
+ export declare function calculateTraktamente(countryCode: string, year: number, departureDate: string, departureTime: string, returnDate: string, returnTime: string): TraktamenteCalculation | undefined;