@fin.cx/skr 1.3.0 → 2.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 (94) hide show
  1. package/.smartconfig.json +33 -6
  2. package/dist_ts/00_commitinfo_data.js +1 -1
  3. package/dist_ts/core/core.accountpolicy.d.ts +67 -0
  4. package/dist_ts/core/core.accountpolicy.js +91 -0
  5. package/dist_ts/core/core.bukeys.d.ts +38 -0
  6. package/dist_ts/core/core.bukeys.js +117 -0
  7. package/dist_ts/core/core.datev.captions.d.ts +7 -0
  8. package/dist_ts/core/core.datev.captions.js +134 -0
  9. package/dist_ts/core/core.datev.d.ts +47 -0
  10. package/dist_ts/core/core.datev.js +129 -0
  11. package/dist_ts/core/core.hash.d.ts +27 -0
  12. package/dist_ts/core/core.hash.js +72 -0
  13. package/dist_ts/core/core.plugins.d.ts +3 -0
  14. package/dist_ts/core/core.plugins.js +9 -0
  15. package/dist_ts/core/core.recipes.d.ts +118 -0
  16. package/dist_ts/core/core.recipes.js +504 -0
  17. package/dist_ts/core/core.taxscenario.d.ts +27 -0
  18. package/dist_ts/core/core.taxscenario.js +85 -0
  19. package/dist_ts/core/core.types.d.ts +48 -0
  20. package/dist_ts/core/core.types.js +2 -0
  21. package/dist_ts/core/core.validate.d.ts +17 -0
  22. package/dist_ts/core/core.validate.js +89 -0
  23. package/dist_ts/core/index.d.ts +15 -0
  24. package/dist_ts/core/index.js +16 -0
  25. package/dist_ts/index.d.ts +15 -0
  26. package/dist_ts/index.js +10 -1
  27. package/dist_ts/plugins.d.ts +2 -1
  28. package/dist_ts/plugins.js +4 -2
  29. package/dist_ts/skr.api.d.ts +91 -22
  30. package/dist_ts/skr.api.js +357 -42
  31. package/dist_ts/skr.balances.d.ts +28 -0
  32. package/dist_ts/skr.balances.js +96 -0
  33. package/dist_ts/skr.classes.journalentry.d.ts +19 -3
  34. package/dist_ts/skr.classes.journalentry.js +67 -4
  35. package/dist_ts/skr.classes.reports.js +49 -102
  36. package/dist_ts/skr.database.d.ts +2 -0
  37. package/dist_ts/skr.database.js +1 -1
  38. package/dist_ts/skr.errors.d.ts +18 -0
  39. package/dist_ts/skr.errors.js +34 -0
  40. package/dist_ts/skr.export.js +4 -7
  41. package/dist_ts/skr.invoice.adapter.d.ts +15 -1
  42. package/dist_ts/skr.invoice.adapter.js +253 -41
  43. package/dist_ts/skr.invoice.booking.js +14 -28
  44. package/dist_ts/skr.migrate.d.ts +20 -0
  45. package/dist_ts/skr.migrate.js +223 -0
  46. package/dist_ts/skr.period.d.ts +47 -0
  47. package/dist_ts/skr.period.js +95 -0
  48. package/dist_ts/skr.posting.d.ts +74 -0
  49. package/dist_ts/skr.posting.js +209 -0
  50. package/dist_ts/skr.security.d.ts +6 -9
  51. package/dist_ts/skr.security.js +16 -89
  52. package/dist_ts/skr.sequence.d.ts +32 -0
  53. package/dist_ts/skr.sequence.js +51 -0
  54. package/dist_ts/skr.txview.d.ts +46 -0
  55. package/dist_ts/skr.txview.js +235 -0
  56. package/dist_ts/skr.types.d.ts +35 -1
  57. package/dist_ts/skr.verify.d.ts +13 -0
  58. package/dist_ts/skr.verify.js +84 -0
  59. package/package.json +21 -24
  60. package/readme.hints.md +37 -0
  61. package/readme.md +94 -1
  62. package/readme.plan.md +180 -243
  63. package/ts/00_commitinfo_data.ts +1 -1
  64. package/ts/core/core.accountpolicy.ts +154 -0
  65. package/ts/core/core.bukeys.ts +146 -0
  66. package/ts/core/core.datev.captions.ts +133 -0
  67. package/ts/core/core.datev.ts +174 -0
  68. package/ts/core/core.hash.ts +76 -0
  69. package/ts/core/core.plugins.ts +9 -0
  70. package/ts/core/core.recipes.ts +659 -0
  71. package/ts/core/core.taxscenario.ts +156 -0
  72. package/ts/core/core.types.ts +74 -0
  73. package/ts/core/core.validate.ts +119 -0
  74. package/ts/core/index.ts +15 -0
  75. package/ts/index.ts +31 -0
  76. package/ts/plugins.ts +4 -0
  77. package/ts/skr.api.ts +432 -46
  78. package/ts/skr.balances.ts +127 -0
  79. package/ts/skr.classes.journalentry.ts +54 -5
  80. package/ts/skr.classes.reports.ts +54 -126
  81. package/ts/skr.database.ts +3 -0
  82. package/ts/skr.errors.ts +44 -0
  83. package/ts/skr.export.ts +3 -6
  84. package/ts/skr.invoice.adapter.ts +291 -45
  85. package/ts/skr.invoice.booking.ts +22 -34
  86. package/ts/skr.migrate.ts +268 -0
  87. package/ts/skr.period.ts +139 -0
  88. package/ts/skr.posting.ts +273 -0
  89. package/ts/skr.security.ts +18 -93
  90. package/ts/skr.sequence.ts +89 -0
  91. package/ts/skr.txview.ts +268 -0
  92. package/ts/skr.types.ts +37 -1
  93. package/ts/skr.verify.ts +99 -0
  94. /package/{license.md → license} +0 -0
@@ -0,0 +1,129 @@
1
+ /**
2
+ * DATEV Buchungsstapel (EXTF 700, Formatversion 13) row model and writer.
3
+ *
4
+ * Layout verified against a DATEV-importable reference file (2026-07-09):
5
+ * 125 data columns, 31 header fields, decimal-comma amounts, Belegdatum TTMM,
6
+ * text fields quoted, field 114 = Festschreibung. The writer emits a CSV
7
+ * STRING with CRLF; persisting it as CP1252 is the IO layer's job.
8
+ * One manual import into real DATEV per release candidate remains the final
9
+ * conformance gate (readme.plan.md, M0).
10
+ */
11
+ import * as plugins from './core.plugins.js';
12
+ import { DATEV_COLUMN_CAPTIONS } from './core.datev.captions.js';
13
+ import { DATEV_MAX_BELEGFELD, DATEV_MAX_BUCHUNGSTEXT } from './core.validate.js';
14
+ const { centsToString } = plugins.calculation;
15
+ export const DATEV_COLUMN_COUNT = 125;
16
+ export const DATEV_HEADER_FIELD_COUNT = 31;
17
+ function pad(value, width) {
18
+ return String(value).padStart(width, '0');
19
+ }
20
+ function formatYyyymmdd(date) {
21
+ return `${date.getUTCFullYear()}${pad(date.getUTCMonth() + 1, 2)}${pad(date.getUTCDate(), 2)}`;
22
+ }
23
+ function formatTtmm(date) {
24
+ return `${pad(date.getUTCDate(), 2)}${pad(date.getUTCMonth() + 1, 2)}`;
25
+ }
26
+ function formatTimestamp(date) {
27
+ return (formatYyyymmdd(date) +
28
+ pad(date.getUTCHours(), 2) +
29
+ pad(date.getUTCMinutes(), 2) +
30
+ pad(date.getUTCSeconds(), 2) +
31
+ pad(date.getUTCMilliseconds(), 3));
32
+ }
33
+ function formatAmount(cents) {
34
+ return centsToString(cents).replace('.', ',').replace('-', '-');
35
+ }
36
+ function quote(value, maxLength) {
37
+ let sanitized = value.replace(/"/g, "'");
38
+ if (maxLength && sanitized.length > maxLength) {
39
+ sanitized = sanitized.slice(0, maxLength);
40
+ }
41
+ return `"${sanitized}"`;
42
+ }
43
+ /**
44
+ * Flatten journal drafts into DATEV rows — one row per line, with the line's
45
+ * explicit counterAccount as Gegenkonto (no pair reconstruction).
46
+ */
47
+ export function journalDraftsToDatevRows(drafts, options) {
48
+ const rows = [];
49
+ for (const draft of drafts) {
50
+ for (const line of draft.lines) {
51
+ rows.push({
52
+ umsatzCents: line.amountCents,
53
+ sollHaben: line.side === 'debit' ? 'S' : 'H',
54
+ konto: line.accountNumber,
55
+ gegenKonto: line.counterAccount,
56
+ buSchluessel: line.buKey,
57
+ belegDatum: draft.date,
58
+ belegfeld1: line.belegfeld1 ?? draft.reference,
59
+ belegfeld2: line.belegfeld2,
60
+ buchungstext: line.buchungstext || draft.description,
61
+ belegLink: line.docLink,
62
+ kost1: line.costCenter1,
63
+ kost2: line.costCenter2,
64
+ festschreibung: options?.festschreibung,
65
+ });
66
+ }
67
+ }
68
+ return rows;
69
+ }
70
+ function rowToFields(row) {
71
+ const fields = new Array(DATEV_COLUMN_COUNT).fill('');
72
+ fields[0] = formatAmount(row.umsatzCents); // 1 Umsatz
73
+ fields[1] = quote(row.sollHaben); // 2 Soll/Haben-Kennzeichen
74
+ fields[6] = row.konto; // 7 Konto
75
+ fields[7] = row.gegenKonto; // 8 Gegenkonto
76
+ if (row.buSchluessel !== '')
77
+ fields[8] = quote(row.buSchluessel); // 9 BU-Schlüssel
78
+ fields[9] = formatTtmm(row.belegDatum); // 10 Belegdatum
79
+ if (row.belegfeld1)
80
+ fields[10] = quote(row.belegfeld1, DATEV_MAX_BELEGFELD); // 11
81
+ if (row.belegfeld2)
82
+ fields[11] = quote(row.belegfeld2, DATEV_MAX_BELEGFELD); // 12
83
+ fields[13] = quote(row.buchungstext, DATEV_MAX_BUCHUNGSTEXT); // 14 Buchungstext
84
+ if (row.belegLink)
85
+ fields[19] = quote(row.belegLink); // 20 Beleglink
86
+ if (row.kost1)
87
+ fields[36] = quote(row.kost1); // 37 KOST1
88
+ if (row.kost2)
89
+ fields[37] = quote(row.kost2); // 38 KOST2
90
+ if (row.festschreibung !== undefined)
91
+ fields[113] = row.festschreibung ? '1' : '0'; // 114
92
+ return fields;
93
+ }
94
+ function buildHeaderLine(header) {
95
+ const fields = new Array(DATEV_HEADER_FIELD_COUNT).fill('');
96
+ fields[0] = '"EXTF"';
97
+ fields[1] = '700';
98
+ fields[2] = '21';
99
+ fields[3] = '"Buchungsstapel"';
100
+ fields[4] = '13';
101
+ fields[5] = formatTimestamp(header.createdAt);
102
+ fields[7] = quote(header.origin ?? 'SK');
103
+ fields[8] = quote(header.exportedBy ?? 'fin.cx/skr');
104
+ fields[10] = String(header.consultantNumber);
105
+ fields[11] = String(header.clientNumber);
106
+ fields[12] = formatYyyymmdd(header.fiscalYearStart);
107
+ fields[13] = String(header.accountLength ?? 4);
108
+ fields[14] = formatYyyymmdd(header.dateFrom);
109
+ fields[15] = formatYyyymmdd(header.dateTo);
110
+ fields[16] = quote(header.label, 30);
111
+ fields[18] = '1'; // Buchungstyp: Finanzbuchführung
112
+ fields[20] = header.festschreibung ? '1' : '0';
113
+ fields[21] = '"EUR"';
114
+ return fields.join(';');
115
+ }
116
+ /**
117
+ * Produce the complete EXTF file content (header, caption row, data rows).
118
+ */
119
+ export function writeExtfBuchungsstapel(rows, header) {
120
+ const lines = [
121
+ buildHeaderLine(header),
122
+ DATEV_COLUMN_CAPTIONS.join(';'),
123
+ ...rows.map((row) => rowToFields(row).join(';')),
124
+ ];
125
+ const csv = lines.join('\r\n') + '\r\n';
126
+ const filename = `EXTF_Buchungsstapel_${formatYyyymmdd(header.dateFrom)}_${formatYyyymmdd(header.dateTo)}.csv`;
127
+ return { csv, filename };
128
+ }
129
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29yZS5kYXRldi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3RzL2NvcmUvY29yZS5kYXRldi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7O0dBU0c7QUFDSCxPQUFPLEtBQUssT0FBTyxNQUFNLG1CQUFtQixDQUFDO0FBRTdDLE9BQU8sRUFBRSxxQkFBcUIsRUFBRSxNQUFNLDBCQUEwQixDQUFDO0FBQ2pFLE9BQU8sRUFBRSxtQkFBbUIsRUFBRSxzQkFBc0IsRUFBRSxNQUFNLG9CQUFvQixDQUFDO0FBRWpGLE1BQU0sRUFBRSxhQUFhLEVBQUUsR0FBRyxPQUFPLENBQUMsV0FBVyxDQUFDO0FBRTlDLE1BQU0sQ0FBQyxNQUFNLGtCQUFrQixHQUFHLEdBQUcsQ0FBQztBQUN0QyxNQUFNLENBQUMsTUFBTSx3QkFBd0IsR0FBRyxFQUFFLENBQUM7QUFrQzNDLFNBQVMsR0FBRyxDQUFDLEtBQWEsRUFBRSxLQUFhO0lBQ3ZDLE9BQU8sTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDLFFBQVEsQ0FBQyxLQUFLLEVBQUUsR0FBRyxDQUFDLENBQUM7QUFDNUMsQ0FBQztBQUVELFNBQVMsY0FBYyxDQUFDLElBQVU7SUFDaEMsT0FBTyxHQUFHLElBQUksQ0FBQyxjQUFjLEVBQUUsR0FBRyxHQUFHLENBQUMsSUFBSSxDQUFDLFdBQVcsRUFBRSxHQUFHLENBQUMsRUFBRSxDQUFDLENBQUMsR0FBRyxHQUFHLENBQUMsSUFBSSxDQUFDLFVBQVUsRUFBRSxFQUFFLENBQUMsQ0FBQyxFQUFFLENBQUM7QUFDakcsQ0FBQztBQUVELFNBQVMsVUFBVSxDQUFDLElBQVU7SUFDNUIsT0FBTyxHQUFHLEdBQUcsQ0FBQyxJQUFJLENBQUMsVUFBVSxFQUFFLEVBQUUsQ0FBQyxDQUFDLEdBQUcsR0FBRyxDQUFDLElBQUksQ0FBQyxXQUFXLEVBQUUsR0FBRyxDQUFDLEVBQUUsQ0FBQyxDQUFDLEVBQUUsQ0FBQztBQUN6RSxDQUFDO0FBRUQsU0FBUyxl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
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Deterministic string form of a value: object keys sorted recursively,
3
+ * Dates as ISO strings, undefined properties omitted, array order preserved.
4
+ * Non-integer numbers are rejected — monetary values must be integer cents.
5
+ */
6
+ export declare function canonicalize(value: unknown): string;
7
+ /**
8
+ * Hash of one chain entry: SHA-256 over predecessor hash + canonical payload.
9
+ */
10
+ export declare function entryHash(prevHash: string, payload: unknown): string;
11
+ /**
12
+ * Chain genesis per ledger scope (one chain per skrType + fiscal year).
13
+ */
14
+ export declare function genesisHash(skrType: string, fiscalYear: number): string;
15
+ /**
16
+ * Walk a sequence of already-canonicalized entries and verify the chain.
17
+ * Returns the first mismatch or null when intact.
18
+ */
19
+ export declare function verifyChain(entries: Array<{
20
+ sequenceNumber: number;
21
+ prevHash: string;
22
+ entryHash: string;
23
+ payload: unknown;
24
+ }>, genesis: string): {
25
+ index: number;
26
+ reason: string;
27
+ } | null;
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Canonical serialization + SHA-256 chaining for the GoBD audit trail.
3
+ * Pure functions — the persistence layer decides what goes into the chain;
4
+ * this module guarantees the representation is stable across releases.
5
+ */
6
+ import * as plugins from './core.plugins.js';
7
+ /**
8
+ * Deterministic string form of a value: object keys sorted recursively,
9
+ * Dates as ISO strings, undefined properties omitted, array order preserved.
10
+ * Non-integer numbers are rejected — monetary values must be integer cents.
11
+ */
12
+ export function canonicalize(value) {
13
+ return JSON.stringify(sortValue(value));
14
+ }
15
+ function sortValue(value) {
16
+ if (value === null || value === undefined)
17
+ return value ?? null;
18
+ if (value instanceof Date)
19
+ return value.toISOString();
20
+ if (Array.isArray(value))
21
+ return value.map((entry) => sortValue(entry));
22
+ if (typeof value === 'number') {
23
+ if (!Number.isFinite(value))
24
+ throw new Error(`Cannot canonicalize non-finite number ${value}`);
25
+ if (!Number.isInteger(value)) {
26
+ throw new Error(`Cannot canonicalize non-integer number ${value} — monetary values must be integer cents`);
27
+ }
28
+ return value;
29
+ }
30
+ if (typeof value === 'object') {
31
+ const sorted = {};
32
+ for (const key of Object.keys(value).sort()) {
33
+ const entry = value[key];
34
+ if (entry !== undefined)
35
+ sorted[key] = sortValue(entry);
36
+ }
37
+ return sorted;
38
+ }
39
+ return value;
40
+ }
41
+ /**
42
+ * Hash of one chain entry: SHA-256 over predecessor hash + canonical payload.
43
+ */
44
+ export function entryHash(prevHash, payload) {
45
+ return plugins.sha256FromStringSync(`${prevHash}\n${canonicalize(payload)}`);
46
+ }
47
+ /**
48
+ * Chain genesis per ledger scope (one chain per skrType + fiscal year).
49
+ */
50
+ export function genesisHash(skrType, fiscalYear) {
51
+ return plugins.sha256FromStringSync(`GENESIS:${skrType}:${fiscalYear}`);
52
+ }
53
+ /**
54
+ * Walk a sequence of already-canonicalized entries and verify the chain.
55
+ * Returns the first mismatch or null when intact.
56
+ */
57
+ export function verifyChain(entries, genesis) {
58
+ let expectedPrev = genesis;
59
+ for (let index = 0; index < entries.length; index++) {
60
+ const entry = entries[index];
61
+ if (entry.prevHash !== expectedPrev) {
62
+ return { index, reason: `prevHash mismatch at sequence ${entry.sequenceNumber}` };
63
+ }
64
+ const recomputed = entryHash(entry.prevHash, entry.payload);
65
+ if (recomputed !== entry.entryHash) {
66
+ return { index, reason: `entryHash mismatch at sequence ${entry.sequenceNumber}` };
67
+ }
68
+ expectedPrev = entry.entryHash;
69
+ }
70
+ return null;
71
+ }
72
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29yZS5oYXNoLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vdHMvY29yZS9jb3JlLmhhc2gudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7R0FJRztBQUNILE9BQU8sS0FBSyxPQUFPLE1BQU0sbUJBQW1CLENBQUM7QUFFN0M7Ozs7R0FJRztBQUNILE1BQU0sVUFBVSxZQUFZLENBQUMsS0FBYztJQUN6QyxPQUFPLElBQUksQ0FBQyxTQUFTLENBQUMsU0FBUyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUM7QUFDMUMsQ0FBQztBQUVELFNBQVMsU0FBUyxDQUFDLEtBQWM7SUFDL0IsSUFBSSxLQUFLLEtBQUssSUFBSSxJQUFJLEtBQUssS0FBSyxTQUFTO1FBQUUsT0FBTyxLQUFLLElBQUksSUFBSSxDQUFDO0lBQ2hFLElBQUksS0FBSyxZQUFZLElBQUk7UUFBRSxPQUFPLEtBQUssQ0FBQyxXQUFXLEVBQUUsQ0FBQztJQUN0RCxJQUFJLEtBQUssQ0FBQyxPQUFPLENBQUMsS0FBSyxDQUFDO1FBQUUsT0FBTyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUMsS0FBSyxFQUFFLEVBQUUsQ0FBQyxTQUFTLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQztJQUN4RSxJQUFJLE9BQU8sS0FBSyxLQUFLLFFBQVEsRUFBRSxDQUFDO1FBQzlCLElBQUksQ0FBQyxNQUFNLENBQUMsUUFBUSxDQUFDLEtBQUssQ0FBQztZQUFFLE1BQU0sSUFBSSxLQUFLLENBQUMseUNBQXlDLEtBQUssRUFBRSxDQUFDLENBQUM7UUFDL0YsSUFBSSxDQUFDLE1BQU0sQ0FBQyxTQUFTLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FBQztZQUM3QixNQUFNLElBQUksS0FBSyxDQUNiLDBDQUEwQyxLQUFLLDBDQUEwQyxDQUMxRixDQUFDO1FBQ0osQ0FBQztRQUNELE9BQU8sS0FBSyxDQUFDO0lBQ2YsQ0FBQztJQUNELElBQUksT0FBTyxLQUFLLEtBQUssUUFBUSxFQUFFLENBQUM7UUFDOUIsTUFBTSxNQUFNLEdBQTRCLEVBQUUsQ0FBQztRQUMzQyxLQUFLLE1BQU0sR0FBRyxJQUFJLE1BQU0sQ0FBQyxJQUFJLENBQUMsS0FBZ0MsQ0FBQyxDQUFDLElBQUksRUFBRSxFQUFFLENBQUM7WUFDdkUsTUFBTSxLQUFLLEdBQUksS0FBaUMsQ0FBQyxHQUFHLENBQUMsQ0FBQztZQUN0RCxJQUFJLEtBQUssS0FBSyxTQUFTO2dCQUFFLE1BQU0sQ0FBQyxHQUFHLENBQUMsR0FBRyxTQUFTLENBQUMsS0FBSyxDQUFDLENBQUM7UUFDMUQsQ0FBQztRQUNELE9BQU8sTUFBTSxDQUFDO0lBQ2hCLENBQUM7SUFDRCxPQUFPLEtBQUssQ0FBQztBQUNmLENBQUM7QUFFRDs7R0FFRztBQUNILE1BQU0sVUFBVSxTQUFTLENBQUMsUUFBZ0IsRUFBRSxPQUFnQjtJQUMxRCxPQUFPLE9BQU8sQ0FBQyxvQkFBb0IsQ0FBQyxHQUFHLFFBQVEsS0FBSyxZQUFZLENBQUMsT0FBTyxDQUFDLEVBQUUsQ0FBQyxDQUFDO0FBQy9FLENBQUM7QUFFRDs7R0FFRztBQUNILE1BQU0sVUFBVSxXQUFXLENBQUMsT0FBZSxFQUFFLFVBQWtCO0lBQzdELE9BQU8sT0FBTyxDQUFDLG9CQUFvQixDQUFDLFdBQVcsT0FBTyxJQUFJLFVBQVUsRUFBRSxDQUFDLENBQUM7QUFDMUUsQ0FBQztBQUVEOzs7R0FHRztBQUNILE1BQU0sVUFBVSxXQUFXLENBQ3pCLE9BQWlHLEVBQ2pHLE9BQWU7SUFFZixJQUFJLFlBQVksR0FBRyxPQUFPLENBQUM7SUFDM0IsS0FBSyxJQUFJLEtBQUssR0FBRyxDQUFDLEVBQUUsS0FBSyxHQUFHLE9BQU8sQ0FBQyxNQUFNLEVBQUUsS0FBSyxFQUFFLEVBQUUsQ0FBQztRQUNwRCxNQUFNLEtBQUssR0FBRyxPQUFPLENBQUMsS0FBSyxDQUFDLENBQUM7UUFDN0IsSUFBSSxLQUFLLENBQUMsUUFBUSxLQUFLLFlBQVksRUFBRSxDQUFDO1lBQ3BDLE9BQU8sRUFBRSxLQUFLLEVBQUUsTUFBTSxFQUFFLGlDQUFpQyxLQUFLLENBQUMsY0FBYyxFQUFFLEVBQUUsQ0FBQztRQUNwRixDQUFDO1FBQ0QsTUFBTSxVQUFVLEdBQUcsU0FBUyxDQUFDLEtBQUssQ0FBQyxRQUFRLEVBQUUsS0FBSyxDQUFDLE9BQU8sQ0FBQyxDQUFDO1FBQzVELElBQUksVUFBVSxLQUFLLEtBQUssQ0FBQyxTQUFTLEVBQUUsQ0FBQztZQUNuQyxPQUFPLEVBQUUsS0FBSyxFQUFFLE1BQU0sRUFBRSxrQ0FBa0MsS0FBSyxDQUFDLGNBQWMsRUFBRSxFQUFFLENBQUM7UUFDckYsQ0FBQztRQUNELFlBQVksR0FBRyxLQUFLLENBQUMsU0FBUyxDQUFDO0lBQ2pDLENBQUM7SUFDRCxPQUFPLElBQUksQ0FBQztBQUNkLENBQUMifQ==
@@ -0,0 +1,3 @@
1
+ import * as calculation from '@fin.cx/calculation';
2
+ import { sha256FromStringSync } from '@push.rocks/smarthash';
3
+ export { calculation, sha256FromStringSync };
@@ -0,0 +1,9 @@
1
+ // The stateless core imports ONLY pure-computation dependencies.
2
+ // No smartdata, no filesystem, no network, no crypto certs — this module must
3
+ // stay usable without MongoDB or any runtime environment beyond JS itself.
4
+ // smarthash is permitted as the sole exception for SHA-256 string hashing
5
+ // (pure computation, needed for the GoBD hash chain).
6
+ import * as calculation from '@fin.cx/calculation';
7
+ import { sha256FromStringSync } from '@push.rocks/smarthash';
8
+ export { calculation, sha256FromStringSync };
9
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29yZS5wbHVnaW5zLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vdHMvY29yZS9jb3JlLnBsdWdpbnMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsaUVBQWlFO0FBQ2pFLDhFQUE4RTtBQUM5RSwyRUFBMkU7QUFDM0UsMEVBQTBFO0FBQzFFLHNEQUFzRDtBQUN0RCxPQUFPLEtBQUssV0FBVyxNQUFNLHFCQUFxQixDQUFDO0FBQ25ELE9BQU8sRUFBRSxvQkFBb0IsRUFBRSxNQUFNLHVCQUF1QixDQUFDO0FBRTdELE9BQU8sRUFBRSxXQUFXLEVBQUUsb0JBQW9CLEVBQUUsQ0FBQyJ9
@@ -0,0 +1,118 @@
1
+ import type { IJournalDraft, IJournalLineDraft, TCents, TTaxScenario } from './core.types.js';
2
+ import type { IAccountPolicy } from './core.accountpolicy.js';
3
+ export interface IVendorInvoiceInput {
4
+ date: Date;
5
+ /** creditor personal account (70000-99999) */
6
+ vendorAccount: string;
7
+ expenseAccount?: string;
8
+ /** invoice total that becomes payable (for §13b / igE this equals net) */
9
+ grossCents: TCents;
10
+ vatRatePercent?: number;
11
+ scenario?: TTaxScenario;
12
+ invoiceNumber?: string;
13
+ description: string;
14
+ costCenter1?: string;
15
+ docLink?: string;
16
+ }
17
+ export interface ICustomerInvoiceInput {
18
+ date: Date;
19
+ /** debtor personal account (10000-69999) */
20
+ customerAccount: string;
21
+ revenueAccount?: string;
22
+ grossCents: TCents;
23
+ vatRatePercent?: number;
24
+ scenario?: TTaxScenario;
25
+ invoiceNumber?: string;
26
+ description: string;
27
+ costCenter1?: string;
28
+ docLink?: string;
29
+ }
30
+ export interface IBankPaymentInput {
31
+ date: Date;
32
+ direction: 'outgoing' | 'incoming';
33
+ bankAccount?: string;
34
+ /** personal debtor/creditor account being settled */
35
+ counterpartyAccount: string;
36
+ /** open gross amount being settled */
37
+ expectedCents: TCents;
38
+ /** amount that actually moved on the bank account */
39
+ paidCents: TCents;
40
+ /** book the difference as Skonto with VAT correction at this rate */
41
+ skonto?: {
42
+ ratePercent: number;
43
+ };
44
+ /** write off differences up to this size to rounding-diff accounts (default 0) */
45
+ diffToleranceCents?: TCents;
46
+ description: string;
47
+ reference?: string;
48
+ }
49
+ export interface IInternalTransferInput {
50
+ date: Date;
51
+ fromBankAccount: string;
52
+ toBankAccount: string;
53
+ amountCents: TCents;
54
+ description: string;
55
+ reference?: string;
56
+ }
57
+ export interface ISuspenseInput {
58
+ date: Date;
59
+ direction: 'incoming' | 'outgoing';
60
+ amountCents: TCents;
61
+ bankAccount?: string;
62
+ description: string;
63
+ reference?: string;
64
+ }
65
+ export interface ISuspenseResolutionInput {
66
+ date: Date;
67
+ direction: 'incoming' | 'outgoing';
68
+ amountCents: TCents;
69
+ /** the account the suspense amount really belongs to */
70
+ targetAccount: string;
71
+ description: string;
72
+ reference?: string;
73
+ }
74
+ export interface IFxDifferenceInput {
75
+ kind: 'payable' | 'receivable';
76
+ /** EUR value at booking date */
77
+ bookedCents: TCents;
78
+ /** EUR value actually settled */
79
+ settledCents: TCents;
80
+ counterAccount: string;
81
+ buchungstext: string;
82
+ belegfeld1?: string;
83
+ }
84
+ /**
85
+ * Eingangsrechnung (vendor invoice).
86
+ * domestic: expense net + Vorsteuer + creditor gross.
87
+ * §13b / igE: expense net + paired input/output VAT + creditor net.
88
+ * tax-free/import: expense gross + creditor gross.
89
+ */
90
+ export declare function bookVendorInvoice(input: IVendorInvoiceInput, policy: IAccountPolicy): IJournalDraft;
91
+ /**
92
+ * Ausgangsrechnung (customer invoice).
93
+ */
94
+ export declare function bookCustomerInvoice(input: ICustomerInvoiceInput, policy: IAccountPolicy): IJournalDraft;
95
+ /**
96
+ * Bank payment clearing a debtor/creditor open amount, with Skonto
97
+ * (incl. VAT correction) or small-difference write-off.
98
+ */
99
+ export declare function bookBankPayment(input: IBankPaymentInput, policy: IAccountPolicy): IJournalDraft;
100
+ /**
101
+ * Bank-to-bank transfer via Geldtransit — two drafts, one per bank statement,
102
+ * so each side reconciles independently.
103
+ */
104
+ export declare function bookInternalTransfer(input: IInternalTransferInput, policy: IAccountPolicy): [IJournalDraft, IJournalDraft];
105
+ /**
106
+ * Park an unclear bank movement on the clearing account.
107
+ */
108
+ export declare function bookToSuspense(input: ISuspenseInput, policy: IAccountPolicy): IJournalDraft;
109
+ /**
110
+ * Move a parked suspense amount to its real account once clarified.
111
+ */
112
+ export declare function resolveSuspense(input: ISuspenseResolutionInput, policy: IAccountPolicy): IJournalDraft;
113
+ /**
114
+ * Realized FX difference lines for a settlement whose EUR value moved between
115
+ * booking and payment. Returns lines to embed into the settlement draft
116
+ * (empty array when there is no difference).
117
+ */
118
+ export declare function fxDifferenceLines(input: IFxDifferenceInput, policy: IAccountPolicy): IJournalLineDraft[];