@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,90 @@
1
+ import { LedgerAccount } from '../ledger/types';
2
+ import { Cents } from '../money/money';
3
+ /**
4
+ * The reports, from posted lines and the chart (plan 039 §3.7). One shape for our own books and
5
+ * for a connected package's (Jortt's `reports/*`), so a screen never knows which books it reads.
6
+ */
7
+ /** One row of a report: a section, a group or an account, with the side its amount stands on. */
8
+ export interface BooksReportRow {
9
+ key: string;
10
+ label: string;
11
+ /** 0 = section, 1 = group, 2 = account. */
12
+ level: number;
13
+ side: "debit" | "credit";
14
+ /** Never negative on its own side; a position on the other side is negative. */
15
+ amountCents: Cents;
16
+ accountId?: string;
17
+ }
18
+ export interface BooksReport {
19
+ kind: "profit_and_loss" | "balance";
20
+ from: string;
21
+ to: string;
22
+ rows: BooksReportRow[];
23
+ /** P&L: the result (credit = profit). Balance: total assets and total liabilities + equity. */
24
+ totals: {
25
+ resultCents?: Cents;
26
+ debitCents?: Cents;
27
+ creditCents?: Cents;
28
+ };
29
+ /** Where the figures come from: our books, or a connected package by name. */
30
+ source: string;
31
+ }
32
+ /** A posted line as the reports need it: account, day, signed amount (debit +). */
33
+ export interface ReportLine {
34
+ accountId: string;
35
+ date: string;
36
+ functionalCents: Cents;
37
+ }
38
+ export interface AccountTotals {
39
+ accountId: string;
40
+ openingCents: Cents;
41
+ debitCents: Cents;
42
+ creditCents: Cents;
43
+ closingCents: Cents;
44
+ }
45
+ /** Per account: what stood before `from`, what moved up to `to`, and what stands after. */
46
+ export declare function accountTotals(lines: readonly ReportLine[], from: string, to: string): Map<string, AccountTotals>;
47
+ export declare const isResultAccount: (account: Pick<LedgerAccount, "type">) => boolean;
48
+ /** Profit and loss over a window: what moved on the result accounts, revenue credit, costs debit. */
49
+ export declare function profitAndLoss(accounts: readonly LedgerAccount[], totals: Map<string, AccountTotals>, window: {
50
+ from: string;
51
+ to: string;
52
+ }, labelOf?: (key: string) => string): BooksReport;
53
+ /**
54
+ * The balance at the end of a window: every balance account's closing position, and this year's
55
+ * result under equity until the year is closed.
56
+ */
57
+ export declare function balanceSheet(accounts: readonly LedgerAccount[], totals: Map<string, AccountTotals>, window: {
58
+ from: string;
59
+ to: string;
60
+ yearFrom: string;
61
+ }, yearResultCents: Cents, labelOf?: (key: string) => string): BooksReport;
62
+ /** A column balance row: the trial balance, split into the balance and the result columns. */
63
+ export interface ColumnRow extends AccountTotals {
64
+ code: string;
65
+ name: string;
66
+ /** Closing position in the balance columns, or movement in the result columns. */
67
+ balanceDebitCents: Cents;
68
+ balanceCreditCents: Cents;
69
+ resultDebitCents: Cents;
70
+ resultCreditCents: Cents;
71
+ }
72
+ export declare function columnBalance(accounts: readonly LedgerAccount[], totals: Map<string, AccountTotals>): ColumnRow[];
73
+ /** Something expected to move cash: an open invoice, a receipt to pay, an instalment. */
74
+ export interface CashEvent {
75
+ date: string;
76
+ /** + in, − out. */
77
+ cents: Cents;
78
+ kind: "receivable" | "payable" | "instalment";
79
+ label: string;
80
+ }
81
+ /**
82
+ * The cash forecast per week from today: what is on the bank now, plus what comes in and goes out
83
+ * as open items fall due. Overdue items land in the first week — that is when they are expected.
84
+ */
85
+ export declare function cashForecast(startCents: Cents, events: readonly CashEvent[], today: string, weeks?: number): {
86
+ from: string;
87
+ inCents: number;
88
+ outCents: number;
89
+ closingCents: number;
90
+ }[];
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,59 @@
1
+ import { Cents } from '../money/money';
2
+ import { TaxCode } from '../money/tax';
3
+ /**
4
+ * The VAT return, computed from posted lines (plan 039 datamodel §7): every line with a VAT code
5
+ * and a tax point in the period adds its base and VAT to the boxes of that code. Pure; the server
6
+ * turns journal lines into `VatLine`s.
7
+ */
8
+ export interface VatLine {
9
+ /** A line on a revenue account is a sale, anything else a purchase. */
10
+ side: "sales" | "purchase";
11
+ taxCode: string;
12
+ /** Positive for a sale or a purchase, negative for its credit note. */
13
+ baseCents: Cents;
14
+ taxCents: Cents;
15
+ taxPointDate: string;
16
+ counterpartyCompanyId?: string;
17
+ }
18
+ export declare const NL_VAT_BOXES: readonly ["1a", "1b", "1c", "1d", "1e", "2a", "3a", "3b", "3c", "4a", "4b", "5a", "5b", "5c", "5g"];
19
+ export type VatBox = (typeof NL_VAT_BOXES)[number];
20
+ export interface VatReturnFigures {
21
+ /** In cents, as the books hold them. */
22
+ boxes: Partial<Record<VatBox, {
23
+ baseCents?: Cents;
24
+ taxCents?: Cents;
25
+ }>>;
26
+ /** Whole euros as they go on the return: owed rounded down, input VAT rounded up, in your favour. */
27
+ euros: Partial<Record<VatBox, {
28
+ base?: number;
29
+ tax?: number;
30
+ }>>;
31
+ /** EC sales list: per customer, the base of the reverse-charged EU sales. */
32
+ icp: {
33
+ counterpartyCompanyId?: string;
34
+ baseCents: Cents;
35
+ }[];
36
+ /** Lines whose code is unknown: they are in no box, and the checklist says so. */
37
+ unknownCodes: string[];
38
+ }
39
+ /**
40
+ * The boxes for one period. Reverse charge on purchase (`selfAssessed`) is owed in its own box
41
+ * and deducted in 5b at the standard rate of its tax point; input VAT counts at the code's
42
+ * deductible share. 5a is what is owed (1a–4b), 5b what is deducted, 5c and 5g the balance.
43
+ */
44
+ export declare function vatReturn(input: {
45
+ lines: readonly VatLine[];
46
+ codes: readonly TaxCode[];
47
+ from: string;
48
+ to: string;
49
+ taxonomy?: string;
50
+ /** The code a self-assessed purchase is taxed at: the standard rate. */
51
+ standardKey?: string;
52
+ }): VatReturnFigures;
53
+ /** The periods a return is filed over, and when it is due: the month after, at its end. */
54
+ export declare function vatPeriods(year: number, cadence?: "quarter" | "month" | "year"): {
55
+ label: string;
56
+ from: string;
57
+ to: string;
58
+ dueOn: string;
59
+ }[];
@@ -0,0 +1,14 @@
1
+ import { BookingDocument } from '../../platform/bookkeeping';
2
+ import { Company } from '../company/types';
3
+ import { Transaction } from './types';
4
+ /** The kinds that reach the books. A quote or an order books nothing. */
5
+ export declare const BOOKABLE_KINDS: readonly ["invoice", "credit_note"];
6
+ export declare function isBookable(tx: Pick<Transaction, "kindKey" | "issuedAt">): boolean;
7
+ /**
8
+ * An issued invoice or credit note as the books need it (plan 039 datamodel §3.2). Pure: sales
9
+ * owns the document, and this is the only shape of it that leaves the app.
10
+ *
11
+ * Headings and options carry no amount, so they are not lines here. A reverse charge or
12
+ * exemption ticked on the document replaces every line's code, as it did when it was priced.
13
+ */
14
+ export declare function bookingDocumentOf(tx: Transaction, company: Pick<Company, "id" | "name" | "vatNumber" | "kvkNumber" | "address"> | null, sourceApp?: string): BookingDocument;
@@ -0,0 +1 @@
1
+ export {};
@@ -13,6 +13,11 @@ export declare function isFrozen(transaction: Pick<Transaction, "issuedAt">): bo
13
13
  * Everything else is a `BadRequestError`. Whoever wants to change a sent quote gets v2.
14
14
  */
15
15
  export declare const FROZEN_WRITABLE_FIELDS: readonly (keyof Transaction)[];
16
+ /**
17
+ * Issued, numbered kind, no number yet: the books number it and have not answered ("wordt
18
+ * uitgegeven"). It cannot be sent to the customer until the number is on the paper.
19
+ */
20
+ export declare function awaitsNumber(tx: Pick<Transaction, "issuedAt" | "kindKey" | "number">): boolean;
16
21
  /**
17
22
  * What a save may set on a draft. `status` is not here: it moves through `issue` and the status
18
23
  * route, which check the transition and announce it.
@@ -0,0 +1,37 @@
1
+ import { Cents } from '../money/money';
2
+ import { ReceivableRow } from './receivable';
3
+ /** One step of the schedule. Level 1 counts from the due day, every later one from the last sent. */
4
+ export interface DunningStep {
5
+ level: number;
6
+ afterDays: number;
7
+ /** The message template it sends (comms `message-template` key). */
8
+ templateKey: string;
9
+ /** ponytail: e-mail only; a letter or text message when someone asks for one. */
10
+ channel: "email";
11
+ }
12
+ /** Per administration, or shared when `businessEntityId` is empty. */
13
+ export interface DunningPolicy {
14
+ id?: string;
15
+ organizationId?: string;
16
+ businessEntityId?: string;
17
+ steps: DunningStep[];
18
+ /** Below this open amount nothing is chased. */
19
+ minOpenCents?: Cents;
20
+ }
21
+ export declare const DEFAULT_DUNNING_POLICY: DunningPolicy;
22
+ export interface DunningDue {
23
+ row: ReceivableRow;
24
+ step: DunningStep;
25
+ /** The day this step falls due. */
26
+ dueOn: string;
27
+ }
28
+ /**
29
+ * What the schedule says about one open invoice on `today`: the step that is due, `"done"` when
30
+ * every step went out (a person takes over), or `null` when nothing is due yet.
31
+ */
32
+ export declare function dunningStateOf(row: ReceivableRow, policy: DunningPolicy, today: string): DunningDue | "done" | null;
33
+ /** The whole run: what is due now per step, and what waits for a person. */
34
+ export declare function dunningRunOf(rows: readonly ReceivableRow[], policy: DunningPolicy, today: string): {
35
+ due: DunningDue[];
36
+ waiting: ReceivableRow[];
37
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -1,7 +1,11 @@
1
+ export * from './booking';
1
2
  export * from './convert';
2
3
  export * from './document';
4
+ export * from './dunning';
3
5
  export * from './keys';
4
6
  export * from './kinds';
5
7
  export * from './number-series';
6
8
  export * from './payment-term';
9
+ export * from './receivable';
10
+ export * from './settlement';
7
11
  export * from './types';
@@ -9,6 +9,8 @@
9
9
  */
10
10
  export declare const TRANSACTION_SCOPE_KINDS: readonly string[];
11
11
  export declare const PRODUCT_SCOPE_KIND = "product";
12
+ /** An agreement: what a customer takes every period. Puts it in the customer file. */
13
+ export declare const AGREEMENT_SCOPE_KIND = "agreement";
12
14
  /** `invoice:412`. The kind of a transaction *is* its kind key — no mapping table. */
13
15
  export declare function transactionScopeKey(kindKey: string, id: string): string;
14
16
  export declare function productScopeKey(id: string): string;
@@ -27,6 +29,11 @@ export declare function productHref(row: {
27
29
  id: string;
28
30
  businessEntityId?: string;
29
31
  }, fallbackEntityId?: string): string | undefined;
32
+ export declare function agreementScopeKey(id: string): string;
33
+ export declare function agreementHref(row: {
34
+ id: string;
35
+ businessEntityId?: string;
36
+ }, fallbackEntityId?: string): string | undefined;
30
37
  /** Is this scopeKey one of ours, and which id does it point at? */
31
38
  export declare function transactionIdFromScope(scopeKey: string): string | undefined;
32
39
  /**
@@ -0,0 +1,47 @@
1
+ import { SettlementState } from '../../platform/bookkeeping';
2
+ import { Cents } from '../money/money';
3
+ import { Transaction } from './types';
4
+ /** One open invoice as "Openstaand" shows it. */
5
+ export interface ReceivableRow {
6
+ id: string;
7
+ kindKey: string;
8
+ number?: string;
9
+ companyId: string;
10
+ businessEntityId?: string;
11
+ issuedAt?: number;
12
+ dueAt?: number;
13
+ /** Whole days past the due day on `today`; 0 when not yet due. */
14
+ overdueDays: number;
15
+ grossCents: Cents;
16
+ openCents: Cents;
17
+ paymentState: SettlementState;
18
+ /** The highest dunning step sent, 0 for none. */
19
+ dunningLevel: number;
20
+ lastDunningAt?: number;
21
+ }
22
+ export declare const AGING_BUCKETS: readonly ["current", "d1_30", "d31_60", "d61_90", "d90_plus"];
23
+ export type AgingBucket = (typeof AGING_BUCKETS)[number];
24
+ /** `null` for a row that is not open (a draft, cancelled, or paid). */
25
+ export declare function receivableRowOf(tx: Transaction, today: string): ReceivableRow | null;
26
+ export declare function agingBucketOf(overdueDays: number): AgingBucket;
27
+ /** Open amount and count per bucket, every bucket present so the strip never jumps. */
28
+ export declare function agingOf(rows: readonly ReceivableRow[]): Record<AgingBucket, {
29
+ openCents: Cents;
30
+ count: number;
31
+ }>;
32
+ /**
33
+ * "What does this customer still owe", across every administration, for a surface outside sales
34
+ * (the crm company rail). Over the service bus, so crm sees with `has()` whether sales is there.
35
+ */
36
+ export declare const COMPANY_RECEIVABLES_SERVICE = "receivables.company";
37
+ export interface CompanyReceivables {
38
+ count: number;
39
+ openCents: Cents;
40
+ overdueCount: number;
41
+ overdueCents: Cents;
42
+ /** Days past due of the oldest overdue invoice; 0 when nothing is overdue. */
43
+ oldestOverdueDays: number;
44
+ /** The highest dunning step sent on any open invoice, 0 for none. */
45
+ dunningLevel: number;
46
+ }
47
+ export declare function companyReceivablesOf(rows: ReceivableRow[]): CompanyReceivables;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,20 @@
1
+ import { SettledNotice, SettlementState } from '../../platform/bookkeeping';
2
+ import { Transaction } from './types';
3
+ /**
4
+ * Payment and overdue as two axes beside the lifecycle (plan 039 datamodel §2.3): an invoice can
5
+ * be partly paid and overdue at once. Pure, so the list, the job and the notice agree.
6
+ */
7
+ /** Unpaid until someone or something says otherwise. */
8
+ export declare function paymentStateOf(tx: Pick<Transaction, "settlement">): SettlementState;
9
+ export declare function isSettled(tx: Pick<Transaction, "settlement">): boolean;
10
+ /** Open means: went out, is still live, and not paid. A draft or a cancelled document is not. */
11
+ export declare function isOutstanding(tx: Pick<Transaction, "status" | "issuedAt" | "settlement">): boolean;
12
+ /** Overdue on `today` (`"2026-09-26"`): outstanding and past its due day. */
13
+ export declare function isOverdueOn(tx: Pick<Transaction, "status" | "issuedAt" | "settlement" | "dueAt">, today: string): boolean;
14
+ /**
15
+ * What a notice from the books changes on the document, or `null` when it changes nothing. A
16
+ * notice older than what we hold is ignored; posting facts only ever fill in, never overwrite.
17
+ */
18
+ export declare function settlementPatch(tx: Pick<Transaction, "settlement" | "postedAt" | "externalIds" | "number">, notice: SettledNotice): Partial<Transaction> | null;
19
+ /** A person ticking it off by hand, when no books are connected. */
20
+ export declare function manualSettlement(tx: Pick<Transaction, "totals">, state: "unpaid" | "paid", now: number): NonNullable<Transaction["settlement"]>;
@@ -0,0 +1 @@
1
+ export {};
@@ -1,4 +1,6 @@
1
+ import { SettlementState } from '../../platform/bookkeeping';
1
2
  import { DocumentBlock } from '../../platform/document-blocks';
3
+ import { LineUsage } from '../agreement/types';
2
4
  import { DocumentSender } from '../document-template/types';
3
5
  import { Cents } from '../money/money';
4
6
  import { TaxRateKind } from '../money/tax';
@@ -111,11 +113,28 @@ export interface Transaction {
111
113
  mountId: string;
112
114
  fileId: string;
113
115
  };
114
- /** Which dunning step went out when. This is the whole debtor state; there is no table. */
116
+ /** Which dunning step went out when. There is no dunning table. */
115
117
  dunning?: {
116
118
  level: number;
117
119
  sentAt: number;
118
120
  }[];
121
+ /**
122
+ * Payment, as a **derived view**: the last state the books reported (`bookable/settled`), or
123
+ * what a person ticked off when no books are connected (`source: "manual"`). The truth is the
124
+ * reconciliation in the books. A separate axis from `status`, and from `overdueSince`: an
125
+ * invoice can be partly paid and overdue at once (plan 039 datamodel §2.3).
126
+ */
127
+ settlement?: {
128
+ state: SettlementState;
129
+ paidCents: Cents;
130
+ openCents: Cents;
131
+ lastPaymentAt?: number;
132
+ source: "manual" | "books";
133
+ /** A notice older than this is ignored: whole state, never a delta. */
134
+ asOf: number;
135
+ };
136
+ /** The day it fell overdue, set by a daily job; cleared once paid. Days, not instants. */
137
+ overdueSince?: string;
119
138
  /**
120
139
  * This transaction in external systems (`moneybird:412`). The dedupe axis the ingest pipeline
121
140
  * upserts on — without it the second run of an order import creates every order again.
@@ -135,7 +154,7 @@ export interface Transaction {
135
154
  * A union and not a free string: kinds and their statuses are **ours**, not the organisation's.
136
155
  * Labels follow from the key as `status_<key>`.
137
156
  */
138
- export type TransactionStatus = "draft" | "sent" | "accepted" | "rejected" | "expired" | "confirmed" | "delivered" | "open" | "paid" | "overdue" | "settled" | "cancelled"
157
+ export type TransactionStatus = "draft" | "sent" | "accepted" | "rejected" | "expired" | "confirmed" | "delivered" | "open" | "cancelled"
139
158
  /** A newer version of this document went out. Set by the server, never by a person. */
140
159
  | "superseded";
141
160
  export interface IssuedPaper {
@@ -182,6 +201,19 @@ export interface TransactionLine {
182
201
  * the line — not the entry — is the source of truth. Never copied to a new document.
183
202
  */
184
203
  timeEntryIds?: string[];
204
+ /** How a usage line was counted: from a meter or entered per period. Never re-priced. */
205
+ usage?: LineUsage;
206
+ /**
207
+ * The agreement line this bills, and the days it covers. Together with `correction` these are
208
+ * the whole "what was billed" state of an agreement — see `dueCharges`.
209
+ */
210
+ agreementLineId?: string;
211
+ servicePeriod?: {
212
+ from: number;
213
+ to: number;
214
+ };
215
+ /** Settles days of a window billed earlier, at the price it was billed at. */
216
+ correction?: boolean;
185
217
  /**
186
218
  * An option: priced and shown, but **not** in the total.
187
219
  *