@opencxh/domain 1.287.0 → 1.290.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 (66) hide show
  1. package/dist/entities/agreement/index.d.ts +3 -0
  2. package/dist/entities/agreement/period.d.ts +37 -0
  3. package/dist/entities/agreement/period.test.d.ts +1 -0
  4. package/dist/entities/agreement/schedule.d.ts +80 -0
  5. package/dist/entities/agreement/schedule.test.d.ts +1 -0
  6. package/dist/entities/agreement/types.d.ts +167 -0
  7. package/dist/entities/bank/bank.test.d.ts +1 -0
  8. package/dist/entities/bank/index.d.ts +3 -0
  9. package/dist/entities/bank/match.d.ts +52 -0
  10. package/dist/entities/bank/parse.d.ts +13 -0
  11. package/dist/entities/bank/types.d.ts +197 -0
  12. package/dist/entities/bookkeeping/index.d.ts +2 -0
  13. package/dist/entities/bookkeeping/receipt.d.ts +104 -0
  14. package/dist/entities/bookkeeping/receipt.test.d.ts +1 -0
  15. package/dist/entities/bookkeeping/types.d.ts +114 -0
  16. package/dist/entities/bookkeeping/types.test.d.ts +1 -0
  17. package/dist/entities/business-entity/types.d.ts +21 -1
  18. package/dist/entities/catalog/index.d.ts +3 -0
  19. package/dist/entities/catalog/pricing.d.ts +9 -6
  20. package/dist/entities/catalog/rate-csv.d.ts +29 -0
  21. package/dist/entities/catalog/rate-csv.test.d.ts +1 -0
  22. package/dist/entities/catalog/rating.d.ts +122 -0
  23. package/dist/entities/catalog/rating.test.d.ts +1 -0
  24. package/dist/entities/catalog/types.d.ts +41 -2
  25. package/dist/entities/catalog/usage-source.d.ts +83 -0
  26. package/dist/entities/company/identifiers.d.ts +9 -0
  27. package/dist/entities/company/identifiers.test.d.ts +1 -0
  28. package/dist/entities/company/index.d.ts +1 -0
  29. package/dist/entities/company/types.d.ts +9 -0
  30. package/dist/entities/financing/closing-schedules.test.d.ts +1 -0
  31. package/dist/entities/financing/index.d.ts +2 -0
  32. package/dist/entities/financing/schedule.d.ts +71 -0
  33. package/dist/entities/financing/schedule.test.d.ts +1 -0
  34. package/dist/entities/financing/types.d.ts +135 -0
  35. package/dist/entities/ledger/closing.d.ts +18 -0
  36. package/dist/entities/ledger/closing.test.d.ts +1 -0
  37. package/dist/entities/ledger/entry.d.ts +83 -0
  38. package/dist/entities/ledger/index.d.ts +6 -0
  39. package/dist/entities/ledger/ledger.test.d.ts +1 -0
  40. package/dist/entities/ledger/periods.d.ts +9 -0
  41. package/dist/entities/ledger/template.d.ts +35 -0
  42. package/dist/entities/ledger/types.d.ts +178 -0
  43. package/dist/entities/ledger/xaf.d.ts +82 -0
  44. package/dist/entities/money/tax.d.ts +62 -0
  45. package/dist/entities/money/tax.test.d.ts +1 -0
  46. package/dist/entities/reports/index.d.ts +2 -0
  47. package/dist/entities/reports/reports.d.ts +90 -0
  48. package/dist/entities/reports/reports.test.d.ts +1 -0
  49. package/dist/entities/reports/vat.d.ts +59 -0
  50. package/dist/entities/transaction/booking.d.ts +14 -0
  51. package/dist/entities/transaction/booking.test.d.ts +1 -0
  52. package/dist/entities/transaction/convert.d.ts +5 -0
  53. package/dist/entities/transaction/dunning.d.ts +37 -0
  54. package/dist/entities/transaction/dunning.test.d.ts +1 -0
  55. package/dist/entities/transaction/index.d.ts +4 -0
  56. package/dist/entities/transaction/keys.d.ts +7 -0
  57. package/dist/entities/transaction/receivable.d.ts +47 -0
  58. package/dist/entities/transaction/receivable.test.d.ts +1 -0
  59. package/dist/entities/transaction/settlement.d.ts +20 -0
  60. package/dist/entities/transaction/settlement.test.d.ts +1 -0
  61. package/dist/entities/transaction/types.d.ts +34 -2
  62. package/dist/index.cjs +24 -21
  63. package/dist/index.d.ts +7 -0
  64. package/dist/index.js +4240 -2387
  65. package/dist/platform/bookkeeping.d.ts +276 -0
  66. package/package.json +1 -1
@@ -0,0 +1,135 @@
1
+ import { Cents } from '../money/money';
2
+ /**
3
+ * Balance items with detail under a ledger account (plan 039 §3.6b, datamodel §5): loans, the
4
+ * director's current account, participations and deposits. One row per item; `kind` says which
5
+ * terms it has. Bank accounts live apart (`BankAccount`): they hold statement lines, these hold a
6
+ * schedule.
7
+ */
8
+ export type FinancingKind = "loan_received" | "loan_given" | "current_account" | "participation" | "deposit_paid" | "fixed_asset" | "accrual";
9
+ export interface LoanTerms {
10
+ kind: "loan_received" | "loan_given";
11
+ principalCents: Cents;
12
+ /** Yearly, as a decimal string: `"4.5"`. */
13
+ ratePercent: string;
14
+ method: "annuity" | "linear" | "bullet";
15
+ termMonths: number;
16
+ /** The first instalment. Later ones fall on the same day of the month. */
17
+ firstDueOn: string;
18
+ /** Interest expense (received) or income (given). */
19
+ interestAccountId: string;
20
+ }
21
+ export interface CurrentAccountTerms {
22
+ kind: "current_account";
23
+ /** A business rate: what the tax authority expects between a BV and its director. */
24
+ ratePercent: string;
25
+ cadence: "quarterly" | "yearly";
26
+ /** Interest income when the director owes, expense when the BV owes. */
27
+ interestIncomeAccountId: string;
28
+ interestExpenseAccountId: string;
29
+ /** Wet excessief lenen: a signal above this debt of the director. A setting, default € 500.000. */
30
+ thresholdCents?: Cents;
31
+ }
32
+ export interface ParticipationTerms {
33
+ kind: "participation";
34
+ /** History of the stake: a change adds a row, nothing is overwritten. */
35
+ stakes: {
36
+ from: string;
37
+ percent: string;
38
+ shares?: number;
39
+ }[];
40
+ acquisitionCents: Cents;
41
+ valuation: "cost" | "net_asset_value";
42
+ /** Proposed at a stake of 5% or more; a person confirms it. */
43
+ participationExemption: boolean;
44
+ dividendAccountId?: string;
45
+ }
46
+ export interface DepositTerms {
47
+ kind: "deposit_paid";
48
+ refundableOn?: string;
49
+ }
50
+ /**
51
+ * A fixed asset, depreciated straight-line per month from the month it was acquired. The item
52
+ * sits under the cost account; depreciation lands on the accumulated-depreciation account, so the
53
+ * annual accounts show cost and accumulated apart. Acquisition itself is booked by its invoice.
54
+ */
55
+ export interface FixedAssetTerms {
56
+ kind: "fixed_asset";
57
+ acquiredOn: string;
58
+ costCents: Cents;
59
+ residualCents: Cents;
60
+ lifeMonths: number;
61
+ /** Expense: `Afschrijving inventaris`. */
62
+ depreciationAccountId: string;
63
+ /** Contra-asset: `Afschrijving inventaris` under the fixed assets. */
64
+ accumulatedAccountId: string;
65
+ }
66
+ /**
67
+ * An amount spread over months (transitoria): prepaid insurance released to costs, revenue
68
+ * invoiced ahead released to revenue, costs accrued before their invoice. `expense` debits the
69
+ * result account and credits the item's balance account each month; `revenue` the other way.
70
+ * `upfront`: the whole amount was booked on the balance account first (prepaid, invoiced
71
+ * ahead), so the item starts at it and runs down; otherwise it builds up (accrued).
72
+ */
73
+ export interface AccrualTerms {
74
+ kind: "accrual";
75
+ direction: "expense" | "revenue";
76
+ totalCents: Cents;
77
+ /** The first month released, as its first day. */
78
+ firstMonth: string;
79
+ months: number;
80
+ resultAccountId: string;
81
+ upfront: boolean;
82
+ }
83
+ export type FinancingTerms = LoanTerms | CurrentAccountTerms | ParticipationTerms | DepositTerms | FixedAssetTerms | AccrualTerms;
84
+ export interface FinancingItem {
85
+ id: string;
86
+ organizationId: string;
87
+ businessEntityId: string;
88
+ kind: FinancingKind;
89
+ /** `"Lening Rabobank 2026"`, `"Rekening-courant J. de Vries"`. */
90
+ name: string;
91
+ /** The ledger account it sits under; its lines carry this item as `subAccountId`. */
92
+ ledgerAccountId: string;
93
+ /** The other party: a crm company (bank, participation, landlord) or a person by name (the director). */
94
+ counterpartyCompanyId?: string;
95
+ counterpartyName?: string;
96
+ currency: string;
97
+ terms: FinancingTerms;
98
+ openedOn?: string;
99
+ closedOn?: string;
100
+ archived?: boolean;
101
+ createdAt?: number;
102
+ updatedAt?: number;
103
+ }
104
+ /**
105
+ * A prepared posting from an item's schedule (datamodel §5.2): an instalment, a quarter's
106
+ * interest. One per (item, sequence), so a repeated run makes no twin.
107
+ */
108
+ export interface ScheduledPosting {
109
+ id: string;
110
+ organizationId: string;
111
+ businessEntityId: string;
112
+ financingItemId: string;
113
+ sequence: number;
114
+ dueOn: string;
115
+ /**
116
+ * The lines as they become an entry. For `bank_line` these are the non-bank side only: the bank
117
+ * line that pays it adds its own side. Their sum is what the bank line must be, signed.
118
+ */
119
+ lines: {
120
+ accountId: string;
121
+ amountCents: Cents;
122
+ subAccountId?: string;
123
+ description?: string;
124
+ }[];
125
+ /** What the bank line should show: − for an instalment paid, + for one received. */
126
+ expectedBankCents: Cents;
127
+ /** planned: in the future · due: today or earlier · posted · skipped (by a person, with a reason). */
128
+ status: "planned" | "due" | "posted" | "skipped";
129
+ /** Paid through a bank line (an instalment) or posted by itself when due (interest on a balance). */
130
+ settlesVia: "bank_line" | "direct";
131
+ entryId?: string;
132
+ skipReason?: string;
133
+ /** For the list: `"Termijn 21"`, `"Rente Q3"`. */
134
+ label: string;
135
+ }
@@ -0,0 +1,18 @@
1
+ import { Cents } from '../money/money';
2
+ import { ReportLine } from '../reports/reports';
3
+ import { JournalLineData, LedgerAccount } from './types';
4
+ /**
5
+ * The year-end closing entry: every result account's movement over the year back to zero, the
6
+ * balance onto the equity result account (plan 039 §3.8). The P&L of a closed year leaves this
7
+ * entry out, so it still shows the year; the balance sees the result where it now sits.
8
+ */
9
+ export declare function yearEndClosingLines(input: {
10
+ accounts: readonly LedgerAccount[];
11
+ /** The year's lines only. */
12
+ lines: readonly ReportLine[];
13
+ resultAccountId: string;
14
+ currency: string;
15
+ }): {
16
+ lines: JournalLineData[];
17
+ resultCents: Cents;
18
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,83 @@
1
+ import { BookingDocument } from '../../platform/bookkeeping';
2
+ import { FiscalPeriod, JournalEntry, JournalKind, JournalLineData, LedgerAccount, PostingRule, TaxAccountMapping } from './types';
3
+ export type EntryProblem = {
4
+ code: "entry_unbalanced";
5
+ params: {
6
+ cents: string;
7
+ };
8
+ } | {
9
+ code: "entry_too_few_lines";
10
+ } | {
11
+ code: "entry_unknown_account";
12
+ params: {
13
+ account: string;
14
+ };
15
+ } | {
16
+ code: "entry_account_archived";
17
+ params: {
18
+ account: string;
19
+ };
20
+ } | {
21
+ code: "entry_sub_account_required";
22
+ params: {
23
+ account: string;
24
+ };
25
+ } | {
26
+ code: "entry_no_period";
27
+ params: {
28
+ day: string;
29
+ };
30
+ } | {
31
+ code: "entry_period_closed";
32
+ params: {
33
+ day: string;
34
+ };
35
+ };
36
+ /** Everything that stops an entry, checked in memory before the insert (plan 039 §4.4 step 1). */
37
+ export declare function entryProblems(entry: {
38
+ date: string;
39
+ lines: readonly JournalLineData[];
40
+ }, context: {
41
+ accounts: readonly LedgerAccount[];
42
+ period?: Pick<FiscalPeriod, "status">;
43
+ /**
44
+ * `closed`: into a closed period too (the accountant, later). `locked`: into any, for the
45
+ * year end only — its lines carry no VAT, so a period locked by a VAT return stays true.
46
+ */
47
+ allow?: "closed" | "locked";
48
+ }): EntryProblem[];
49
+ /** `"MEM-2026-0042"`. A split year numbers under the year it starts in. */
50
+ export declare function entryNumber(journalCode: string, fiscalYear: string, seq: number): string;
51
+ /** The fields the hash covers: everything that says what was booked, where, by whom, after what. */
52
+ export type HashedEntry = Omit<JournalEntry, "id" | "organizationId" | "hash" | "createdAt">;
53
+ export declare function hashedPartOf(entry: JournalEntry | HashedEntry): HashedEntry;
54
+ /**
55
+ * Canonical JSON: keys sorted at every depth, `undefined` dropped. The same entry always gives the
56
+ * same string, so a re-computed hash only differs when something was changed.
57
+ */
58
+ export declare function canonicalJson(value: unknown): string;
59
+ /** A reversal: the same lines, every amount the other way. */
60
+ export declare function reversedLines(lines: readonly JournalLineData[]): JournalLineData[];
61
+ export interface DocumentPostingContext {
62
+ accounts: readonly LedgerAccount[];
63
+ rules: readonly PostingRule[];
64
+ taxAccounts: readonly TaxAccountMapping[];
65
+ }
66
+ export type DocumentEntry = {
67
+ ok: true;
68
+ journalKind: JournalKind;
69
+ lines: JournalLineData[];
70
+ description: string;
71
+ } | {
72
+ ok: false;
73
+ problem: string;
74
+ };
75
+ /**
76
+ * A sale or a purchase as balanced lines (plan 039 §4.6). A sale: debtors debit the gross, revenue
77
+ * credit per line, VAT payable credit per code. A purchase the mirror, on creditors and VAT to
78
+ * reclaim. A credit note needs nothing of its own: its amounts are already negative.
79
+ *
80
+ * ponytail: a reverse-charge purchase books no self-assessed VAT yet (both sides of box 4); the
81
+ * VAT return in step 7 adds it.
82
+ */
83
+ export declare function entryLinesOf(doc: BookingDocument, context: DocumentPostingContext): DocumentEntry;
@@ -0,0 +1,6 @@
1
+ export * from './closing';
2
+ export * from './entry';
3
+ export * from './periods';
4
+ export * from './template';
5
+ export * from './types';
6
+ export * from './xaf';
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,9 @@
1
+ import { FiscalPeriod } from './types';
2
+ /**
3
+ * The twelve monthly periods of the fiscal year that starts in `startMonth` (1-12) of `year`.
4
+ * Periods are rows, not derived from the calendar: a 4-4-5 year or a thirteenth period is a
5
+ * different list, not a different rule (plan 039 §4.1 #6).
6
+ */
7
+ export declare function monthlyPeriods(year: number, startMonth?: number): Pick<FiscalPeriod, "fiscalYear" | "index" | "from" | "to" | "status">[];
8
+ /** The period a day falls in, or `undefined`: a day outside every year cannot be posted. */
9
+ export declare function periodOf<P extends Pick<FiscalPeriod, "from" | "to">>(periods: readonly P[], day: string): P | undefined;
@@ -0,0 +1,35 @@
1
+ import { JournalKind, LedgerAccountRole, LedgerAccountType } from './types';
2
+ /**
3
+ * A chart of accounts to start from, as data (plan 039 datamodel §4.1): copied into an
4
+ * administration when it is set up, never read again after, so changing it touches no one.
5
+ */
6
+ export interface TemplateAccount {
7
+ code: string;
8
+ name: string;
9
+ type: LedgerAccountType;
10
+ reportGroup: string;
11
+ /** RGS reference code (level 4, an account), from the official list of the version below. */
12
+ rgs: string;
13
+ role?: LedgerAccountRole;
14
+ requiresSubAccount?: boolean;
15
+ }
16
+ export interface LedgerTemplate {
17
+ key: string;
18
+ label: string;
19
+ /** The RGS version the codes come from: `"3.5"`. An XAF export states it. */
20
+ rgsVersion: string;
21
+ accounts: TemplateAccount[];
22
+ journals: {
23
+ code: string;
24
+ name: string;
25
+ kind: JournalKind;
26
+ }[];
27
+ }
28
+ /**
29
+ * A BV's chart, each account on its RGS 3.5 reference code (Def-versie RGS 3.5_2,
30
+ * referentiegrootboekschema.nl). Names follow the code they carry: "Omzet diensten laag" and not
31
+ * "Omzet laag", because the code says services at the low rate and a report grouped by RGS must
32
+ * mean what the name says.
33
+ */
34
+ export declare const NL_BV_TEMPLATE: LedgerTemplate;
35
+ export declare const LEDGER_TEMPLATES: readonly LedgerTemplate[];
@@ -0,0 +1,178 @@
1
+ import { BookingDocument } from '../../platform/bookkeeping';
2
+ import { Cents } from '../money/money';
3
+ /**
4
+ * Our own books (plan 039 datamodel §4). One row per posting with its lines as a column, a hash
5
+ * chain and gapless numbers guarded by unique indexes, and a line table that is only ever derived.
6
+ */
7
+ export type LedgerAccountType = "asset" | "liability" | "equity" | "revenue" | "expense";
8
+ export interface LedgerAccount {
9
+ id: string;
10
+ organizationId: string;
11
+ businessEntityId: string;
12
+ /** `"1100"`, unique per administration. */
13
+ code: string;
14
+ name: string;
15
+ type: LedgerAccountType;
16
+ /** Mapping onto reporting taxonomies, apart from the own chart. RGS first. */
17
+ taxonomy?: {
18
+ rgs?: string;
19
+ };
20
+ /** Accounts with detail (bank, loan, current account) require a `subAccountId` on every line. */
21
+ requiresSubAccount?: boolean;
22
+ /** Where it sits in the reports: `"revenue.net"`, `"costs.office"`. The reports are not hard-coded. */
23
+ reportGroup?: string;
24
+ order?: number;
25
+ /** What a control account is for, so a posting rule can find it: debtors, creditors, VAT. */
26
+ role?: LedgerAccountRole;
27
+ /** Never deleted: a posted line points at it. */
28
+ archived?: boolean;
29
+ createdAt?: number;
30
+ updatedAt?: number;
31
+ }
32
+ export type LedgerAccountRole = "debtors" | "creditors" | "vat_payable" | "vat_receivable" | "revenue_default" | "expense_default" | "suspense" | "result";
33
+ export type JournalKind = "sales" | "purchase" | "bank" | "cash" | "memorial" | "opening";
34
+ export interface Journal {
35
+ id: string;
36
+ organizationId: string;
37
+ businessEntityId: string;
38
+ /** `"VK"`, `"IK"`, `"MEM"`: also the number prefix. */
39
+ code: string;
40
+ name: string;
41
+ kind: JournalKind;
42
+ /** A bank or cash journal belongs to one sub-account (the bank account). */
43
+ subAccountId?: string;
44
+ archived?: boolean;
45
+ }
46
+ export interface FiscalPeriod {
47
+ id: string;
48
+ organizationId: string;
49
+ businessEntityId: string;
50
+ /** `"2026"`, or `"2026/2027"` for a year that does not start in January. */
51
+ fiscalYear: string;
52
+ /** 1..12 (or 13). */
53
+ index: number;
54
+ from: string;
55
+ to: string;
56
+ /** open: post freely. closed: accountant only. locked: nobody (after a VAT return or year end). */
57
+ status: "open" | "closed" | "locked";
58
+ }
59
+ /**
60
+ * A closed fiscal year: the closing entry that moved its result to equity, the loans' short-term
61
+ * reclassification (and its reversal on the first day after), and who closed it. One per year;
62
+ * its periods are locked after.
63
+ */
64
+ export interface YearEnd {
65
+ id: string;
66
+ organizationId: string;
67
+ businessEntityId: string;
68
+ fiscalYear: string;
69
+ from: string;
70
+ to: string;
71
+ closingEntryId?: string;
72
+ reclassEntryIds: string[];
73
+ /** Credit positive: a profit. */
74
+ resultCents: Cents;
75
+ closedAt: number;
76
+ closedBy: string;
77
+ }
78
+ /** A line as it sits in `JournalEntry.lines`. Debit positive, credit negative; the sum is zero. */
79
+ export interface JournalLineData {
80
+ accountId: string;
81
+ amountCents: Cents;
82
+ currency: string;
83
+ fxRate?: string;
84
+ functionalCents: Cents;
85
+ /** `TaxCode.key`, the VAT of this line, and when it counts. */
86
+ taxCode?: string;
87
+ taxCents?: Cents;
88
+ taxPointDate?: string;
89
+ subAccountId?: string;
90
+ costCenter?: string;
91
+ /** A work project as cost carrier. */
92
+ projectId?: string;
93
+ counterpartyCompanyId?: string;
94
+ dimensions?: Record<string, string>;
95
+ description?: string;
96
+ }
97
+ export interface JournalEntry {
98
+ id: string;
99
+ organizationId: string;
100
+ businessEntityId: string;
101
+ journalId: string;
102
+ /** `"MEM-2026-0042"`: journal code, fiscal year, `journalSeq`. Set in the same insert. */
103
+ number: string;
104
+ /** The per-journal, per-year sequence behind `number`. Unique with journal and year. */
105
+ journalSeq: number;
106
+ fiscalYear: string;
107
+ /** Position in this administration's hash chain: 1, 2, 3 … without gaps. Unique. */
108
+ chainSeq: number;
109
+ date: string;
110
+ periodId: string;
111
+ description: string;
112
+ /**
113
+ * Where it came from, flat and never empty, unique: `sales:<id>`, `finance:<id>`,
114
+ * `manual:<uuid>`. The same source cannot be posted twice.
115
+ */
116
+ sourceKey: string;
117
+ /** `number`: the source document's own number, so a bank line can be matched on it. */
118
+ source: {
119
+ app: string;
120
+ documentId?: string;
121
+ kind?: string;
122
+ number?: string;
123
+ dueOn?: string;
124
+ };
125
+ lines: JournalLineData[];
126
+ /** The entry this one reverses. The reversed entry itself is never changed. */
127
+ reversesEntryId?: string;
128
+ attachment?: {
129
+ mountId: string;
130
+ fileId: string;
131
+ };
132
+ /** sha256(prevHash + canonical JSON of the entry). */
133
+ hash: string;
134
+ /** `"genesis"` at chainSeq 1. */
135
+ prevHash: string;
136
+ postedAt: number;
137
+ postedBy: string;
138
+ createdAt?: number;
139
+ }
140
+ /** The derived line table: one row per line, for reports and the account card. Never written by hand. */
141
+ export interface JournalLine extends JournalLineData {
142
+ id: string;
143
+ organizationId: string;
144
+ businessEntityId: string;
145
+ entryId: string;
146
+ /** Unique with `entryId`: a repeated projection makes no twin. */
147
+ lineIndex: number;
148
+ date: string;
149
+ periodId: string;
150
+ journalId: string;
151
+ }
152
+ /** How a `BookingDocument` becomes an entry. First match wins, in the order of the mapping. */
153
+ export interface PostingRule {
154
+ id: string;
155
+ organizationId: string;
156
+ businessEntityId: string;
157
+ appliesTo: {
158
+ direction: "sale" | "purchase";
159
+ kind?: BookingDocument["kind"];
160
+ };
161
+ match: {
162
+ productId?: string;
163
+ ledgerCategoryKey?: string;
164
+ taxRateKey?: string;
165
+ };
166
+ /** Revenue or expense account for the net line. */
167
+ accountId: string;
168
+ order: number;
169
+ }
170
+ /** Which account the VAT of a key lands on. Missing = the account with the VAT role. */
171
+ export interface TaxAccountMapping {
172
+ id: string;
173
+ organizationId: string;
174
+ businessEntityId: string;
175
+ taxKey: string;
176
+ payableAccountId?: string;
177
+ receivableAccountId?: string;
178
+ }
@@ -0,0 +1,82 @@
1
+ import { Cents } from '../money/money';
2
+ import { FiscalPeriod, Journal, JournalEntry, LedgerAccount } from './types';
3
+ /**
4
+ * The XML Auditfile Financieel 4.0 (Belastingdienst, XAF v4.0.3): one fiscal year of the books
5
+ * as the tax authority and the accountant read it. A projection, built with string concatenation
6
+ * (it may run on the QuickJS server), and a scanner that reads a package's XAF (3.x or 4.0) back
7
+ * for the opening balance.
8
+ */
9
+ export declare const XAF_NAMESPACE = "http://www.odb.belastingdienst.nl/Belastingdienst/BCPP/1.1/structures/XmlauditfileXAF_4.0";
10
+ export interface XafInput {
11
+ fiscalYear: string;
12
+ startDate: string;
13
+ endDate: string;
14
+ currency: string;
15
+ createdOn: string;
16
+ software: {
17
+ desc: string;
18
+ version: string;
19
+ };
20
+ rgsVersion?: string;
21
+ company: {
22
+ name: string;
23
+ commerceNr?: string;
24
+ country: string;
25
+ vatNumber?: string;
26
+ address?: {
27
+ street?: string;
28
+ number?: string;
29
+ city?: string;
30
+ postalCode?: string;
31
+ };
32
+ };
33
+ relations: {
34
+ id: string;
35
+ name: string;
36
+ commerceNr?: string;
37
+ vatNumber?: string;
38
+ country?: string;
39
+ type?: "C" | "S" | "B";
40
+ }[];
41
+ accounts: readonly LedgerAccount[];
42
+ taxCodes: {
43
+ key: string;
44
+ label: string;
45
+ payableAccountId?: string;
46
+ receivableAccountId?: string;
47
+ }[];
48
+ periods: readonly Pick<FiscalPeriod, "id" | "index" | "from" | "to">[];
49
+ /** Balance positions at the start of the year, debit positive. */
50
+ opening: {
51
+ accountId: string;
52
+ cents: Cents;
53
+ }[];
54
+ journals: readonly Journal[];
55
+ entries: readonly JournalEntry[];
56
+ /** The VAT percentage of a code on a day, when known; a line without it carries no vat block. */
57
+ vatPercent?: (taxCode: string, day: string) => string | undefined;
58
+ }
59
+ export declare function xafAuditfile(input: XafInput): string;
60
+ export interface XafOpening {
61
+ fiscalYear?: string;
62
+ endDate?: string;
63
+ accounts: {
64
+ code: string;
65
+ name: string;
66
+ kind: "B" | "P";
67
+ rgs?: string;
68
+ }[];
69
+ /** Per account code, where it stood at the end of the file's year: debit positive. */
70
+ closing: Record<string, Cents>;
71
+ }
72
+ /**
73
+ * What a moving administration needs from its old package: the chart and where every account
74
+ * stood at the end of the year. Opening balance plus every transaction line; XAF 3.2 (`leadCode`)
75
+ * and 4.0 (`RGScode`) both read.
76
+ */
77
+ export declare function parseXafOpening(xml: string): XafOpening;
78
+ /** An account's type from its RGS code, else from the side it stands on. */
79
+ export declare function accountTypeOf(account: {
80
+ kind: "B" | "P";
81
+ rgs?: string;
82
+ }, cents: Cents): LedgerAccount["type"];
@@ -40,3 +40,65 @@ export declare const NL_TAX_RATES: readonly TaxRate[];
40
40
  * can open.
41
41
  */
42
42
  export declare function taxRateFor(key: string, rates: readonly TaxRate[]): TaxRate;
43
+ /**
44
+ * A VAT code with its rate over time: the table `apps/organization` owns (plan 039 §2.5).
45
+ *
46
+ * The key is the same key a line carries (`taxRateKey`), so every existing line stays valid. A
47
+ * rate change adds a period instead of overwriting the percent — a December invoice keeps 9% when
48
+ * January brings 21%.
49
+ */
50
+ export interface TaxCode {
51
+ key: string;
52
+ /** Empty = every administration; an administration's own row shadows the shared one. */
53
+ businessEntityId?: string;
54
+ /** i18n key, not text. */
55
+ label: string;
56
+ kind: TaxRateKind;
57
+ /** Purchase-only codes (input VAT) never show up in a sales select. */
58
+ direction: "sales" | "purchase" | "both";
59
+ /** Non-overlapping, ascending. `validTo` is inclusive and absent on the current period. */
60
+ rates: TaxCodeRate[];
61
+ /** Return boxes per taxonomy and side: `{ "nl-ob": { sales: { base: "1a", tax: "1a" } } }`. */
62
+ boxes: Record<string, {
63
+ sales?: TaxBoxes;
64
+ purchase?: TaxBoxes;
65
+ }>;
66
+ /** Reverse charge on purchase: the VAT is both owed and deductible. */
67
+ selfAssessed?: boolean;
68
+ /** Counts in the EC sales list (ICP). */
69
+ icp?: boolean;
70
+ /** Deductible share of input VAT, mixed use. Absent = 100. */
71
+ deductiblePercent?: number;
72
+ archived?: boolean;
73
+ order?: number;
74
+ }
75
+ export interface TaxCodeRate {
76
+ /** `"2019-01-01"`. Days, compared as strings. */
77
+ validFrom: string;
78
+ validTo?: string;
79
+ /** Whole percent as written on the invoice. */
80
+ percent: number;
81
+ }
82
+ export interface TaxBoxes {
83
+ base?: string;
84
+ tax?: string;
85
+ }
86
+ /**
87
+ * The Dutch codes an organisation starts with. Rubrieken per the aangifte omzetbelasting:
88
+ * zero-rated and reverse-charged supplies report their base in 1e; exempt supplies are not reported.
89
+ */
90
+ export declare const NL_TAX_CODES: readonly TaxCode[];
91
+ /** The percent that applied on `date`. No period covering it → 0, the safe direction as in {@link taxRateFor}. */
92
+ export declare function taxPercentOn(code: TaxCode, date: string): number;
93
+ /**
94
+ * The codes as flat rates on one day — the shape totals and paper already compute with. A
95
+ * {@link TaxRate} is "a code resolved on a date", so nothing downstream needs to know about periods.
96
+ */
97
+ export declare function taxRatesOn(codes: readonly TaxCode[], date: string, direction?: "sales" | "purchase"): TaxRate[];
98
+ /** Today as a VAT date, `"2026-09-26"`. The caller passes the tax point when it has one. */
99
+ export declare function taxDateOf(epochMs: number): string;
100
+ /**
101
+ * What is wrong with a code about to be saved, as an error code (`error_<code>` in the app's
102
+ * translations), or `null`. Periods must be valid days, 0–100%, ascending and not overlapping.
103
+ */
104
+ export declare function taxCodeProblem(code: Partial<TaxCode>): string | null;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,2 @@
1
+ export * from './reports';
2
+ export * from './vat';