@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.
- package/dist/entities/agreement/index.d.ts +3 -0
- package/dist/entities/agreement/period.d.ts +37 -0
- package/dist/entities/agreement/period.test.d.ts +1 -0
- package/dist/entities/agreement/schedule.d.ts +80 -0
- package/dist/entities/agreement/schedule.test.d.ts +1 -0
- package/dist/entities/agreement/types.d.ts +167 -0
- package/dist/entities/bank/bank.test.d.ts +1 -0
- package/dist/entities/bank/index.d.ts +3 -0
- package/dist/entities/bank/match.d.ts +52 -0
- package/dist/entities/bank/parse.d.ts +13 -0
- package/dist/entities/bank/types.d.ts +197 -0
- package/dist/entities/bookkeeping/index.d.ts +2 -0
- package/dist/entities/bookkeeping/receipt.d.ts +104 -0
- package/dist/entities/bookkeeping/receipt.test.d.ts +1 -0
- package/dist/entities/bookkeeping/types.d.ts +114 -0
- package/dist/entities/bookkeeping/types.test.d.ts +1 -0
- package/dist/entities/business-entity/types.d.ts +21 -1
- package/dist/entities/catalog/index.d.ts +3 -0
- package/dist/entities/catalog/pricing.d.ts +9 -6
- package/dist/entities/catalog/rate-csv.d.ts +29 -0
- package/dist/entities/catalog/rate-csv.test.d.ts +1 -0
- package/dist/entities/catalog/rating.d.ts +122 -0
- package/dist/entities/catalog/rating.test.d.ts +1 -0
- package/dist/entities/catalog/types.d.ts +41 -2
- package/dist/entities/catalog/usage-source.d.ts +83 -0
- package/dist/entities/company/identifiers.d.ts +9 -0
- package/dist/entities/company/identifiers.test.d.ts +1 -0
- package/dist/entities/company/index.d.ts +1 -0
- package/dist/entities/company/types.d.ts +9 -0
- package/dist/entities/financing/closing-schedules.test.d.ts +1 -0
- package/dist/entities/financing/index.d.ts +2 -0
- package/dist/entities/financing/schedule.d.ts +71 -0
- package/dist/entities/financing/schedule.test.d.ts +1 -0
- package/dist/entities/financing/types.d.ts +135 -0
- package/dist/entities/ledger/closing.d.ts +18 -0
- package/dist/entities/ledger/closing.test.d.ts +1 -0
- package/dist/entities/ledger/entry.d.ts +83 -0
- package/dist/entities/ledger/index.d.ts +6 -0
- package/dist/entities/ledger/ledger.test.d.ts +1 -0
- package/dist/entities/ledger/periods.d.ts +9 -0
- package/dist/entities/ledger/template.d.ts +35 -0
- package/dist/entities/ledger/types.d.ts +178 -0
- package/dist/entities/ledger/xaf.d.ts +82 -0
- package/dist/entities/money/tax.d.ts +62 -0
- package/dist/entities/money/tax.test.d.ts +1 -0
- package/dist/entities/reports/index.d.ts +2 -0
- package/dist/entities/reports/reports.d.ts +90 -0
- package/dist/entities/reports/reports.test.d.ts +1 -0
- package/dist/entities/reports/vat.d.ts +59 -0
- package/dist/entities/transaction/booking.d.ts +14 -0
- package/dist/entities/transaction/booking.test.d.ts +1 -0
- package/dist/entities/transaction/convert.d.ts +5 -0
- package/dist/entities/transaction/dunning.d.ts +37 -0
- package/dist/entities/transaction/dunning.test.d.ts +1 -0
- package/dist/entities/transaction/index.d.ts +4 -0
- package/dist/entities/transaction/keys.d.ts +7 -0
- package/dist/entities/transaction/receivable.d.ts +47 -0
- package/dist/entities/transaction/receivable.test.d.ts +1 -0
- package/dist/entities/transaction/settlement.d.ts +20 -0
- package/dist/entities/transaction/settlement.test.d.ts +1 -0
- package/dist/entities/transaction/types.d.ts +34 -2
- package/dist/index.cjs +24 -21
- package/dist/index.d.ts +7 -0
- package/dist/index.js +4240 -2387
- package/dist/platform/bookkeeping.d.ts +276 -0
- package/package.json +1 -1
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Agreement, AgreementInterval, AgreementLine } from './types';
|
|
2
|
+
/** `a` bills more often than `b`: a monthly line in a yearly agreement would bill 12 at once. */
|
|
3
|
+
export declare function isShorter(a: AgreementInterval, b: AgreementInterval): boolean;
|
|
4
|
+
export interface Period {
|
|
5
|
+
from: number;
|
|
6
|
+
/** Exclusive. */
|
|
7
|
+
to: number;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* The period of `interval` that contains `at`, on calendar boundaries.
|
|
11
|
+
*
|
|
12
|
+
* ponytail: UTC boundaries — a call at 00:30 Amsterdam time on the 1st lands in the previous
|
|
13
|
+
* month. Pass the organisation's zone offset here when that difference starts to matter.
|
|
14
|
+
*/
|
|
15
|
+
export declare function periodAt(at: number, interval: AgreementInterval): Period;
|
|
16
|
+
/** When a line runs: its own dates inside the agreement's term. `to` absent = open-ended. */
|
|
17
|
+
export declare function lineTerm(agreement: Pick<Agreement, "startsAt" | "endsAt">, line: Pick<AgreementLine, "startsAt" | "endsAt">): {
|
|
18
|
+
from: number;
|
|
19
|
+
to?: number;
|
|
20
|
+
};
|
|
21
|
+
/** The part of `period` a term covers, or `null` when they do not meet. */
|
|
22
|
+
export declare function overlap(term: {
|
|
23
|
+
from: number;
|
|
24
|
+
to?: number;
|
|
25
|
+
}, period: Period): Period | null;
|
|
26
|
+
/**
|
|
27
|
+
* How much of a period a fixed line bills: 1 for a whole period, 0 when it ran only part of it
|
|
28
|
+
* and `proration` is off, the share of days when it is on (12 of 31 days = 12/31).
|
|
29
|
+
*/
|
|
30
|
+
export declare function periodShare(term: {
|
|
31
|
+
from: number;
|
|
32
|
+
to?: number;
|
|
33
|
+
}, period: Period, proration: boolean): number;
|
|
34
|
+
export type LineStatus = "planned" | "active" | "ended";
|
|
35
|
+
export declare function lineStatus(agreement: Pick<Agreement, "startsAt" | "endsAt">, line: Pick<AgreementLine, "startsAt" | "endsAt">, now: number): LineStatus;
|
|
36
|
+
/** Lines that run at `at` or will later — the ones whose accounts are taken. */
|
|
37
|
+
export declare function currentLines<T extends Pick<AgreementLine, "startsAt" | "endsAt">>(agreement: Pick<Agreement, "startsAt" | "endsAt">, lines: readonly T[], at: number): T[];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { Cents } from '../money/money';
|
|
2
|
+
import { Period } from './period';
|
|
3
|
+
import { Agreement, AgreementLine, LineBilling, LineRhythm } from './types';
|
|
4
|
+
/** A fixed line keeps its own rhythm; usage follows the agreement, it is read per period. */
|
|
5
|
+
export declare function rhythmOf(agreement: Pick<Agreement, "interval">, line: AgreementLine): LineRhythm;
|
|
6
|
+
/** Fixed lines bill in advance unless told otherwise; usage is only known afterwards. */
|
|
7
|
+
export declare function timingOf(line: AgreementLine): LineBilling;
|
|
8
|
+
/** An invoice line an agreement wrote earlier, as far as scheduling cares. */
|
|
9
|
+
export interface BilledLine {
|
|
10
|
+
agreementLineId: string;
|
|
11
|
+
servicePeriod: Period;
|
|
12
|
+
quantity: number;
|
|
13
|
+
unitPriceCents: Cents;
|
|
14
|
+
correction?: boolean;
|
|
15
|
+
}
|
|
16
|
+
/** One window of one line that is due and not yet on an invoice. */
|
|
17
|
+
export interface Charge {
|
|
18
|
+
lineId: string;
|
|
19
|
+
/** The line's whole window in its rhythm: what "billed" is matched on. */
|
|
20
|
+
window: Period;
|
|
21
|
+
/** The part of it the line ran, which the invoice line says it covers. */
|
|
22
|
+
part: Period;
|
|
23
|
+
/** Fixed lines: the share of the window billed, 1 unless prorated. Usage: 1. */
|
|
24
|
+
share: number;
|
|
25
|
+
timing: LineBilling;
|
|
26
|
+
}
|
|
27
|
+
/** A window billed before the line's term changed, settled at the price it was billed at. */
|
|
28
|
+
export interface Correction {
|
|
29
|
+
lineId: string;
|
|
30
|
+
window: Period;
|
|
31
|
+
/** The days added (positive quantity) or taken off (negative). */
|
|
32
|
+
part: Period;
|
|
33
|
+
quantity: number;
|
|
34
|
+
unitPriceCents: Cents;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* What an agreement owes on `now`, per line: every window that is due minus the windows an
|
|
38
|
+
* invoice already covers, plus corrections for billed windows whose days changed since.
|
|
39
|
+
*
|
|
40
|
+
* Due: a window in advance on its first day, in arrears after its last, plus grace either way,
|
|
41
|
+
* so "fixed October" and "usage September" land on the same run. A window a line only partly
|
|
42
|
+
* runs, and a correction, wait for the window's end — they ride along with the next regular
|
|
43
|
+
* invoice instead of producing one of their own.
|
|
44
|
+
*/
|
|
45
|
+
export declare function dueCharges(agreement: Pick<Agreement, "startsAt" | "endsAt" | "interval" | "graceDays" | "proration">, lines: readonly AgreementLine[], billed: readonly BilledLine[], now: number): {
|
|
46
|
+
charges: Charge[];
|
|
47
|
+
corrections: Correction[];
|
|
48
|
+
};
|
|
49
|
+
export interface RunSpan {
|
|
50
|
+
timing: LineBilling;
|
|
51
|
+
from: number;
|
|
52
|
+
/** Exclusive. */
|
|
53
|
+
to: number;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* What a run or an invoice covers, for a list: per timing the first and last service day, and how
|
|
57
|
+
* many corrections — "Vooraf okt · Achteraf sep · 1 correctie".
|
|
58
|
+
*/
|
|
59
|
+
export declare function runSpans(lines: readonly {
|
|
60
|
+
timing: LineBilling;
|
|
61
|
+
servicePeriod: Period;
|
|
62
|
+
correction?: boolean;
|
|
63
|
+
}[]): {
|
|
64
|
+
spans: RunSpan[];
|
|
65
|
+
corrections: number;
|
|
66
|
+
};
|
|
67
|
+
export type DueStatus = "ready" | "needs_quantity" | "unrated" | "error";
|
|
68
|
+
/** One row of "to bill": what a run on this agreement would do right now. */
|
|
69
|
+
export interface DueRun {
|
|
70
|
+
agreementId: string;
|
|
71
|
+
title: string;
|
|
72
|
+
companyId: string;
|
|
73
|
+
status: DueStatus;
|
|
74
|
+
netCents: number;
|
|
75
|
+
spans: RunSpan[];
|
|
76
|
+
corrections: number;
|
|
77
|
+
needsQuantity: number;
|
|
78
|
+
unrated: number;
|
|
79
|
+
error?: string;
|
|
80
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { PriceBand } from '../catalog/rating';
|
|
2
|
+
import { Cents } from '../money/money';
|
|
3
|
+
/**
|
|
4
|
+
* A standing arrangement with a customer that bills every period: number rental, licences, and
|
|
5
|
+
* the usage of an account at a source (calls on a PBX tenant).
|
|
6
|
+
*
|
|
7
|
+
* It does not hold the invoices it produced. Each invoice line it wrote carries the agreement line
|
|
8
|
+
* and the service period it covers, and those lines are the whole "what was billed" state.
|
|
9
|
+
*/
|
|
10
|
+
export interface Agreement {
|
|
11
|
+
id: string;
|
|
12
|
+
organizationId: string;
|
|
13
|
+
title: string;
|
|
14
|
+
companyId: string;
|
|
15
|
+
businessEntityId?: string;
|
|
16
|
+
currency: string;
|
|
17
|
+
status: AgreementStatus;
|
|
18
|
+
/** Epoch ms. The first period starts on the interval boundary at or before this. */
|
|
19
|
+
startsAt: number;
|
|
20
|
+
/** Exclusive: the first moment it no longer runs. A screen shows the day before as "last day". */
|
|
21
|
+
endsAt?: number;
|
|
22
|
+
/**
|
|
23
|
+
* A fixed line that starts or stops inside a period: `false` bills whole periods only (from the
|
|
24
|
+
* first full one, up to the last full one); `true` bills the days it ran, pro rata, and settles
|
|
25
|
+
* a window billed in advance when the line's term changes afterwards.
|
|
26
|
+
*/
|
|
27
|
+
proration?: boolean;
|
|
28
|
+
interval: AgreementInterval;
|
|
29
|
+
/**
|
|
30
|
+
* Days after a window opens (in advance) or closes (in arrears) before it is due: late call
|
|
31
|
+
* records still arrive, and both land on the same run.
|
|
32
|
+
*/
|
|
33
|
+
graceDays: number;
|
|
34
|
+
paymentTermKey?: string;
|
|
35
|
+
/**
|
|
36
|
+
* The account usage is read from (a PBX tenant). Usage lines without an account of their own
|
|
37
|
+
* take this one; a line may still name another, say texts from a second source.
|
|
38
|
+
*/
|
|
39
|
+
sourceAccount?: UsageAccountRef;
|
|
40
|
+
lines: AgreementLine[];
|
|
41
|
+
createdBy: string;
|
|
42
|
+
createdAt?: number;
|
|
43
|
+
updatedAt?: number;
|
|
44
|
+
}
|
|
45
|
+
export type AgreementStatus = "draft" | "active" | "ended";
|
|
46
|
+
export type AgreementInterval = "month" | "quarter" | "year";
|
|
47
|
+
export interface AgreementLine {
|
|
48
|
+
id: string;
|
|
49
|
+
productId: string;
|
|
50
|
+
/** Copied from the product, like a transaction line: the agreement must stay readable. */
|
|
51
|
+
name: string;
|
|
52
|
+
/**
|
|
53
|
+
* Where the quantity comes from. How it is priced is the product's business (`pricing`), and a
|
|
54
|
+
* price for this agreement is a price agreement with `contractId` = the agreement.
|
|
55
|
+
*/
|
|
56
|
+
quantity: LineQuantity;
|
|
57
|
+
/** Taken off the quantity before the price: "the first 5 are included". */
|
|
58
|
+
included?: number;
|
|
59
|
+
/**
|
|
60
|
+
* When this line runs, inside the agreement's own term. Absent = with the agreement. A change
|
|
61
|
+
* "from 1 May" ends one line and starts another, so billed periods keep saying what they said.
|
|
62
|
+
*/
|
|
63
|
+
startsAt?: number;
|
|
64
|
+
/** Exclusive, like `Agreement.endsAt`. */
|
|
65
|
+
endsAt?: number;
|
|
66
|
+
/** A fixed line only: usage is known after the period, so it always bills in arrears. */
|
|
67
|
+
billing?: LineBilling;
|
|
68
|
+
/** A fixed line's own rhythm; never shorter than the agreement's. Absent = the agreement's. */
|
|
69
|
+
every?: LineRhythm;
|
|
70
|
+
}
|
|
71
|
+
export type LineBilling = "advance" | "arrears";
|
|
72
|
+
export type LineRhythm = AgreementInterval | "once";
|
|
73
|
+
export type LineQuantity = {
|
|
74
|
+
from: "fixed";
|
|
75
|
+
value: number;
|
|
76
|
+
}
|
|
77
|
+
/** Entered per period, when billing. */
|
|
78
|
+
| {
|
|
79
|
+
from: "manual";
|
|
80
|
+
}
|
|
81
|
+
/** Read from a usage source. No `accountRef` = the agreement's `sourceAccount`. */
|
|
82
|
+
| {
|
|
83
|
+
from: "meter";
|
|
84
|
+
app: string;
|
|
85
|
+
meter: string;
|
|
86
|
+
accountRef?: string;
|
|
87
|
+
};
|
|
88
|
+
/** An account at a usage source: which app reports it, and its id there. */
|
|
89
|
+
export interface UsageAccountRef {
|
|
90
|
+
app: string;
|
|
91
|
+
accountRef: string;
|
|
92
|
+
}
|
|
93
|
+
/** The account a meter line reads, own or inherited. `null` when neither names one. */
|
|
94
|
+
export declare function usageAccountOf(agreement: Pick<Agreement, "sourceAccount">, line: AgreementLine): UsageAccountRef | null;
|
|
95
|
+
/** Every account an agreement reads usage from, deduplicated. */
|
|
96
|
+
export declare function usageAccountsOf(agreement: Pick<Agreement, "sourceAccount" | "lines">): UsageAccountRef[];
|
|
97
|
+
export interface SaveAgreementRequest extends Partial<Omit<Agreement, "id" | "organizationId" | "createdBy" | "createdAt" | "updatedAt">> {
|
|
98
|
+
id?: string;
|
|
99
|
+
}
|
|
100
|
+
/** What a billing run would put on the invoice, before anything is written. */
|
|
101
|
+
export interface AgreementRunPreview {
|
|
102
|
+
/** The moment it was computed for: everything due by then and not yet billed. */
|
|
103
|
+
at: number;
|
|
104
|
+
/** The earliest and latest service day on it. Absent when nothing is due. */
|
|
105
|
+
covers?: {
|
|
106
|
+
from: number;
|
|
107
|
+
to: number;
|
|
108
|
+
};
|
|
109
|
+
lines: RunLine[];
|
|
110
|
+
netCents: Cents;
|
|
111
|
+
/** Records without a rate, per dimension combination. Billing needs `skipUnrated` then. */
|
|
112
|
+
unrated: {
|
|
113
|
+
meter: string;
|
|
114
|
+
dimensions: Record<string, string>;
|
|
115
|
+
count: number;
|
|
116
|
+
}[];
|
|
117
|
+
/** Quantity keys of manual lines still waiting for a number. Billing refuses until given. */
|
|
118
|
+
needsQuantity: string[];
|
|
119
|
+
/** Records seen across all meter lines, rated or not. */
|
|
120
|
+
records: number;
|
|
121
|
+
}
|
|
122
|
+
export interface RunLine {
|
|
123
|
+
/** The agreement line it came from; a rated line yields one invoice line per rate. */
|
|
124
|
+
lineId: string;
|
|
125
|
+
name: string;
|
|
126
|
+
productId: string;
|
|
127
|
+
unitKey: string;
|
|
128
|
+
taxRateKey: string;
|
|
129
|
+
quantity: number;
|
|
130
|
+
unitPriceCents: Cents;
|
|
131
|
+
priceSourceKey: string;
|
|
132
|
+
/** What it covers: the part of the line's window it ran. */
|
|
133
|
+
servicePeriod: {
|
|
134
|
+
from: number;
|
|
135
|
+
to: number;
|
|
136
|
+
};
|
|
137
|
+
timing: LineBilling;
|
|
138
|
+
/** Settles a window that was billed before the line's term changed. */
|
|
139
|
+
correction?: boolean;
|
|
140
|
+
/** Manual lines: the key its entered quantity travels under — see `quantityKey`. */
|
|
141
|
+
quantityKey?: string;
|
|
142
|
+
/** A fixed line that ran part of its window: the share billed. */
|
|
143
|
+
share?: number;
|
|
144
|
+
/** Hours read from the time app: the invoice stamps them, so nothing bills them again. */
|
|
145
|
+
timeEntryIds?: string[];
|
|
146
|
+
usage?: LineUsage;
|
|
147
|
+
}
|
|
148
|
+
/** Manual quantities are entered per line and window: a catch-up run may ask for two months. */
|
|
149
|
+
export declare function quantityKey(lineId: string, windowFrom: number): string;
|
|
150
|
+
/**
|
|
151
|
+
* How a usage line was counted, kept on the invoice line. A rated group bills 1 × its total (it
|
|
152
|
+
* has no whole-cent unit price), so the real quantity and the tier bands live here.
|
|
153
|
+
*/
|
|
154
|
+
export interface LineUsage {
|
|
155
|
+
from: "manual" | "meter";
|
|
156
|
+
/** Rated lines: the rate this group is. */
|
|
157
|
+
rateId?: string;
|
|
158
|
+
records?: number;
|
|
159
|
+
/** Aggregated, before `included`. */
|
|
160
|
+
measured: number;
|
|
161
|
+
included?: number;
|
|
162
|
+
/** For display: the meter's display unit (`minute`) or the product's unit. */
|
|
163
|
+
unit: string;
|
|
164
|
+
/** Meter units per display unit (60 seconds per minute). */
|
|
165
|
+
per: number;
|
|
166
|
+
bands?: PriceBand[];
|
|
167
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { Cents } from '../money/money';
|
|
2
|
+
import { BankLine, BankRule, MatchProposal, Reconciliation } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* A proposal for one bank line, in the order of the datamodel (§6.1): a reference with the
|
|
5
|
+
* amount, then an IBAN with the amount, then a transfer between own accounts, then a rule.
|
|
6
|
+
* Nothing guessed: no proposal is better than a wrong one in "Klaar om te bevestigen".
|
|
7
|
+
*/
|
|
8
|
+
/** An open sales invoice (money in) or receipt (money out), with what is still open. */
|
|
9
|
+
export interface OpenDocument {
|
|
10
|
+
kind: "sales_invoice" | "receipt";
|
|
11
|
+
id: string;
|
|
12
|
+
sourceApp?: string;
|
|
13
|
+
number?: string;
|
|
14
|
+
/** IBANs this party pays from or to (crm `Company.ibans`, or the receipt's). */
|
|
15
|
+
ibans: string[];
|
|
16
|
+
/** Positive: what is still to receive (an invoice) or to pay (a receipt). */
|
|
17
|
+
openCents: Cents;
|
|
18
|
+
label: string;
|
|
19
|
+
}
|
|
20
|
+
export interface MatchContext {
|
|
21
|
+
documents: readonly OpenDocument[];
|
|
22
|
+
rules: readonly BankRule[];
|
|
23
|
+
/** Our own accounts, to recognise a transfer. */
|
|
24
|
+
ownIbans: readonly {
|
|
25
|
+
iban: string;
|
|
26
|
+
bankAccountId: string;
|
|
27
|
+
}[];
|
|
28
|
+
/** Instalments waiting for their bank line: the amount to expect, when, and the party's IBANs. */
|
|
29
|
+
scheduled?: readonly {
|
|
30
|
+
id: string;
|
|
31
|
+
expectedBankCents: Cents;
|
|
32
|
+
dueOn: string;
|
|
33
|
+
ibans: string[];
|
|
34
|
+
label: string;
|
|
35
|
+
}[];
|
|
36
|
+
/** Unbooked lines on other own accounts, for the other side of a transfer. */
|
|
37
|
+
otherLines: readonly Pick<BankLine, "id" | "bankAccountId" | "date" | "amountCents" | "state">[];
|
|
38
|
+
}
|
|
39
|
+
export declare function proposeMatch(line: Pick<BankLine, "bankAccountId" | "date" | "amountCents" | "counterpartyIban" | "description" | "endToEndId">, context: MatchContext): MatchProposal | undefined;
|
|
40
|
+
/** What is left of an item: its amount minus every active allocation on it. Computed, never stored. */
|
|
41
|
+
export declare function openCentsOf(itemKey: string, amountCents: Cents, reconciliations: readonly Pick<Reconciliation, "status" | "allocations">[]): Cents;
|
|
42
|
+
/** The invariant of a reconciliation: its allocations cancel out (a difference is in the entry). */
|
|
43
|
+
export declare function reconciliationBalances(rec: Pick<Reconciliation, "allocations">): boolean;
|
|
44
|
+
/**
|
|
45
|
+
* The whole payment state of an item from its amount and what is still open, both signed like
|
|
46
|
+
* the item (an invoice +, a receipt −). What sales hears, and what the books show.
|
|
47
|
+
*/
|
|
48
|
+
export declare function paymentOfItem(grossCents: Cents, openCents: Cents): {
|
|
49
|
+
state: "paid" | "overpaid" | "unpaid" | "partially_paid";
|
|
50
|
+
paidCents: number;
|
|
51
|
+
openCents: number;
|
|
52
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ParsedStatement } from './types';
|
|
2
|
+
/** Every `<tag>…</tag>` body at any depth, in order. Tags are matched by local name. */
|
|
3
|
+
declare function blocks(xml: string, tag: string): string[];
|
|
4
|
+
/** The text at a path of tags: `first(x, "BookgDt", "Dt")`. */
|
|
5
|
+
declare function first(xml: string, ...path: string[]): string | undefined;
|
|
6
|
+
/** ISO 20022 camt.053: one statement per file is the common case; every `Stmt` is read. */
|
|
7
|
+
export declare function parseCamt053(input: string): ParsedStatement;
|
|
8
|
+
/** SWIFT MT940, as Dutch banks export it. Several statements in one file are read in order. */
|
|
9
|
+
export declare function parseMt940(input: string): ParsedStatement;
|
|
10
|
+
/** Which format a file is in, by its content, not its name. */
|
|
11
|
+
export declare function parseStatement(input: string): ParsedStatement;
|
|
12
|
+
/** The same scanner for other flat XML the browser reads (the XAF import). */
|
|
13
|
+
export { blocks as xmlBlocks, first as xmlText };
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { Cents } from '../money/money';
|
|
2
|
+
/**
|
|
3
|
+
* Bank accounts, statements and their lines, and settling one with the other (plan 039
|
|
4
|
+
* datamodel §5.1, §6.1–6.3). Only the bank kinds of `SubAccount` are built here; loans, the
|
|
5
|
+
* director's current account and participations follow in step 6b on the same row.
|
|
6
|
+
*/
|
|
7
|
+
export type BankSubAccountKind = "bank" | "savings" | "deposit" | "credit_card" | "psp" | "cash";
|
|
8
|
+
/** A bank account (or cash, a card, a payment provider balance) under a ledger account. */
|
|
9
|
+
export interface BankAccount {
|
|
10
|
+
id: string;
|
|
11
|
+
organizationId: string;
|
|
12
|
+
businessEntityId: string;
|
|
13
|
+
kind: BankSubAccountKind;
|
|
14
|
+
name: string;
|
|
15
|
+
/** The ledger account it sits under (`1100 Bank`), and the bank journal its lines post in. */
|
|
16
|
+
ledgerAccountId: string;
|
|
17
|
+
journalId: string;
|
|
18
|
+
iban?: string;
|
|
19
|
+
currency: string;
|
|
20
|
+
archived?: boolean;
|
|
21
|
+
createdAt?: number;
|
|
22
|
+
updatedAt?: number;
|
|
23
|
+
}
|
|
24
|
+
export interface BankImport {
|
|
25
|
+
id: string;
|
|
26
|
+
organizationId: string;
|
|
27
|
+
businessEntityId: string;
|
|
28
|
+
bankAccountId: string;
|
|
29
|
+
format: "camt053" | "mt940";
|
|
30
|
+
from: string;
|
|
31
|
+
to: string;
|
|
32
|
+
/** What the statement says. A difference with our balance is a signal, never a silent fix. */
|
|
33
|
+
statementOpeningCents?: Cents;
|
|
34
|
+
statementClosingCents?: Cents;
|
|
35
|
+
lineCount: number;
|
|
36
|
+
importedBy: string;
|
|
37
|
+
createdAt?: number;
|
|
38
|
+
}
|
|
39
|
+
/** A line as a parser reads it from a statement, before it is stored. */
|
|
40
|
+
export interface ParsedBankLine {
|
|
41
|
+
date: string;
|
|
42
|
+
valueDate?: string;
|
|
43
|
+
/** Positive = money in. */
|
|
44
|
+
amountCents: Cents;
|
|
45
|
+
currency: string;
|
|
46
|
+
counterpartyName?: string;
|
|
47
|
+
counterpartyIban?: string;
|
|
48
|
+
description: string;
|
|
49
|
+
endToEndId?: string;
|
|
50
|
+
/**
|
|
51
|
+
* Unique per account: the bank's own reference (CAMT `AcctSvcrRef`, MT940 `:61:` reference), or
|
|
52
|
+
* else statement number + line number. Never date + amount + text alone: two coffees of € 3,20
|
|
53
|
+
* on one day are two lines.
|
|
54
|
+
*/
|
|
55
|
+
dedupeKey: string;
|
|
56
|
+
}
|
|
57
|
+
export interface ParsedStatement {
|
|
58
|
+
format: BankImport["format"];
|
|
59
|
+
iban?: string;
|
|
60
|
+
currency?: string;
|
|
61
|
+
from: string;
|
|
62
|
+
to: string;
|
|
63
|
+
openingCents?: Cents;
|
|
64
|
+
closingCents?: Cents;
|
|
65
|
+
lines: ParsedBankLine[];
|
|
66
|
+
}
|
|
67
|
+
/** What a bank line is paired with. The pair lives on the line only; nothing points back. */
|
|
68
|
+
export type MatchTarget = {
|
|
69
|
+
kind: "sales_invoice";
|
|
70
|
+
transactionId: string;
|
|
71
|
+
sourceApp: string;
|
|
72
|
+
} | {
|
|
73
|
+
kind: "receipt";
|
|
74
|
+
receiptId: string;
|
|
75
|
+
}
|
|
76
|
+
/** A prepared posting from a balance item's schedule: a loan instalment. */
|
|
77
|
+
| {
|
|
78
|
+
kind: "scheduled_posting";
|
|
79
|
+
scheduledPostingId: string;
|
|
80
|
+
}
|
|
81
|
+
/** The other side of a transfer between two own accounts. */
|
|
82
|
+
| {
|
|
83
|
+
kind: "transfer";
|
|
84
|
+
bankLineId: string;
|
|
85
|
+
}
|
|
86
|
+
/** Straight to an account (bank costs, a rule). */
|
|
87
|
+
| {
|
|
88
|
+
kind: "account";
|
|
89
|
+
accountId: string;
|
|
90
|
+
taxCode?: string;
|
|
91
|
+
description?: string;
|
|
92
|
+
};
|
|
93
|
+
export type MatchReason = "amount" | "iban" | "reference" | "invoice_number" | "date_window" | "rule" | "own_account";
|
|
94
|
+
export interface MatchProposal {
|
|
95
|
+
target: MatchTarget;
|
|
96
|
+
via: "exact" | "rule" | "manual";
|
|
97
|
+
confidence: "high" | "medium" | "low";
|
|
98
|
+
ruleId?: string;
|
|
99
|
+
/** What agreed, for the explanation on screen: "same amount, same IBAN". */
|
|
100
|
+
reasons: MatchReason[];
|
|
101
|
+
/** What the target is called, so the queue need not look it up: "F2026-0190 · Studio Noord". */
|
|
102
|
+
label?: string;
|
|
103
|
+
}
|
|
104
|
+
export interface BankLine extends ParsedBankLine {
|
|
105
|
+
id: string;
|
|
106
|
+
organizationId: string;
|
|
107
|
+
businessEntityId: string;
|
|
108
|
+
bankAccountId: string;
|
|
109
|
+
importId: string;
|
|
110
|
+
/** Computed at import and after, stored so the queue loads fast. */
|
|
111
|
+
proposal?: MatchProposal;
|
|
112
|
+
/** unmatched → booked (entry and reconciliation written) · ignored. */
|
|
113
|
+
state: "unmatched" | "booked" | "ignored";
|
|
114
|
+
/** What it was booked against, and the entry that did it. */
|
|
115
|
+
pairedWith?: MatchTarget;
|
|
116
|
+
entryId?: string;
|
|
117
|
+
/**
|
|
118
|
+
* How often it was booked and undone: the entry's source key is `bank:<id>:<attempt>`, so booking
|
|
119
|
+
* again after an undo makes a new entry instead of finding the reversed one.
|
|
120
|
+
*/
|
|
121
|
+
attempt?: number;
|
|
122
|
+
createdAt?: number;
|
|
123
|
+
updatedAt?: number;
|
|
124
|
+
}
|
|
125
|
+
/** Made by a person, or offered after a manual pairing ("Voortaan automatisch?"). */
|
|
126
|
+
export interface BankRule {
|
|
127
|
+
id: string;
|
|
128
|
+
organizationId: string;
|
|
129
|
+
businessEntityId: string;
|
|
130
|
+
name: string;
|
|
131
|
+
/** Every condition given must hold. */
|
|
132
|
+
when: {
|
|
133
|
+
bankAccountId?: string;
|
|
134
|
+
counterpartyIban?: string;
|
|
135
|
+
/** Case-insensitive, in memory (the store has no `$regex`). */
|
|
136
|
+
descriptionContains?: string;
|
|
137
|
+
direction?: "in" | "out";
|
|
138
|
+
};
|
|
139
|
+
action: {
|
|
140
|
+
accountId: string;
|
|
141
|
+
taxCode?: string;
|
|
142
|
+
description?: string;
|
|
143
|
+
};
|
|
144
|
+
/** High lands in "Klaar om te bevestigen", medium in "Controleren". */
|
|
145
|
+
confidence: "high" | "medium";
|
|
146
|
+
order: number;
|
|
147
|
+
}
|
|
148
|
+
/** An item that can be open: a posted line (debtor, creditor), or a bank line. */
|
|
149
|
+
export type OpenItemRef = {
|
|
150
|
+
kind: "journal_line";
|
|
151
|
+
id: string;
|
|
152
|
+
} | {
|
|
153
|
+
kind: "bank_line";
|
|
154
|
+
id: string;
|
|
155
|
+
};
|
|
156
|
+
/**
|
|
157
|
+
* One reconciliation: amounts that cancel out. Immutable once active; undoing sets `reversed`
|
|
158
|
+
* and deletes nothing, so the history stays (datamodel §6.3).
|
|
159
|
+
*/
|
|
160
|
+
export interface Reconciliation {
|
|
161
|
+
id: string;
|
|
162
|
+
organizationId: string;
|
|
163
|
+
businessEntityId: string;
|
|
164
|
+
/** `bank:<bankLineId>` while active; a reversed one gets `:reversed:<at>` so the line can pair again. Unique. */
|
|
165
|
+
reconKey: string;
|
|
166
|
+
kind: "payment" | "credit_offset" | "transfer" | "write_off" | "manual";
|
|
167
|
+
/**
|
|
168
|
+
* The sign follows the item: a debtor line is +, its receipt −. The allocations cancel out; a
|
|
169
|
+
* difference is booked in the payment's entry and listed below for the audit.
|
|
170
|
+
*/
|
|
171
|
+
allocations: {
|
|
172
|
+
item: OpenItemRef;
|
|
173
|
+
amountCents: Cents;
|
|
174
|
+
}[];
|
|
175
|
+
differences?: {
|
|
176
|
+
amountCents: Cents;
|
|
177
|
+
reason: "payment_difference" | "bank_costs" | "fx" | "write_off";
|
|
178
|
+
accountId: string;
|
|
179
|
+
entryId: string;
|
|
180
|
+
}[];
|
|
181
|
+
/** Flat and indexed: which reconciliations touch this item is an `$in` on this field. */
|
|
182
|
+
itemKeys: string[];
|
|
183
|
+
status: "active" | "reversed";
|
|
184
|
+
origin: {
|
|
185
|
+
via: "exact" | "rule" | "manual";
|
|
186
|
+
ruleId?: string;
|
|
187
|
+
proposalReasons?: MatchReason[];
|
|
188
|
+
};
|
|
189
|
+
createdBy: string;
|
|
190
|
+
createdAt?: number;
|
|
191
|
+
reversal?: {
|
|
192
|
+
at: number;
|
|
193
|
+
by: string;
|
|
194
|
+
reason: string;
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
export declare const itemKeyOf: (item: OpenItemRef) => string;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { BookingDocument } from '../../platform/bookkeeping';
|
|
2
|
+
import { Cents } from '../money/money';
|
|
3
|
+
import { TaxRateKind } from '../money/tax';
|
|
4
|
+
import { WorkQueueItem } from './types';
|
|
5
|
+
/**
|
|
6
|
+
* A receipt or purchase invoice, as finance holds it until it is booked (plan 039 datamodel §6.4).
|
|
7
|
+
* Finance is its own `bookable-source` for these, so a receipt reaches Jortt the way an invoice
|
|
8
|
+
* does, through the same `BookingSync`.
|
|
9
|
+
*/
|
|
10
|
+
export interface Receipt {
|
|
11
|
+
id: string;
|
|
12
|
+
organizationId: string;
|
|
13
|
+
businessEntityId: string;
|
|
14
|
+
/**
|
|
15
|
+
* Where it came from, flat and unique: `mail:<activityId>:<attachmentId>` or
|
|
16
|
+
* `upload:<fileId>`. The same attachment sent to the macro twice is one receipt.
|
|
17
|
+
*/
|
|
18
|
+
sourceKey: string;
|
|
19
|
+
/** The copy in storage: the books keep it seven years, a mailbox does not. */
|
|
20
|
+
file: {
|
|
21
|
+
mountId: string;
|
|
22
|
+
fileId: string;
|
|
23
|
+
filename: string;
|
|
24
|
+
mimeType: string;
|
|
25
|
+
};
|
|
26
|
+
receivedVia: "mail" | "upload";
|
|
27
|
+
receivedAt: number;
|
|
28
|
+
mail?: {
|
|
29
|
+
interactionId: string;
|
|
30
|
+
activityId: string;
|
|
31
|
+
from?: string;
|
|
32
|
+
subject?: string;
|
|
33
|
+
};
|
|
34
|
+
supplierCompanyId?: string;
|
|
35
|
+
fields: ReceiptFields;
|
|
36
|
+
lines: ReceiptLine[];
|
|
37
|
+
totals: {
|
|
38
|
+
netCents: Cents;
|
|
39
|
+
taxCents: Cents;
|
|
40
|
+
grossCents: Cents;
|
|
41
|
+
};
|
|
42
|
+
status: ReceiptStatus;
|
|
43
|
+
/** Why extraction gave nothing, when it did not: shown on the item, a person fills it in. */
|
|
44
|
+
extractError?: string;
|
|
45
|
+
/** Only the reference; the state lives on the approval request (one approval system, plan 023). */
|
|
46
|
+
approvalRequestId?: string;
|
|
47
|
+
/** When it was handed to the books: the "issue" moment the sweeper and `activeFrom` go by. */
|
|
48
|
+
bookedAt?: number;
|
|
49
|
+
postedAt?: number;
|
|
50
|
+
externalIds?: string[];
|
|
51
|
+
createdBy?: string;
|
|
52
|
+
createdAt?: number;
|
|
53
|
+
updatedAt?: number;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* extracting: the AI is reading it.
|
|
57
|
+
* to_check: something is filled in and not confirmed yet.
|
|
58
|
+
* awaiting_approval: confirmed, above the administration's threshold, waiting on a sign-off.
|
|
59
|
+
* booked: handed to the books (the `BookingSync` says whether it arrived).
|
|
60
|
+
* rejected: not a receipt, or a duplicate.
|
|
61
|
+
*/
|
|
62
|
+
export type ReceiptStatus = "extracting" | "to_check" | "awaiting_approval" | "booked" | "rejected";
|
|
63
|
+
/** A recognised value: whether the AI proposed it, and whether a person has looked. */
|
|
64
|
+
export interface Field<T> {
|
|
65
|
+
value: T;
|
|
66
|
+
ai?: boolean;
|
|
67
|
+
}
|
|
68
|
+
export interface ReceiptFields {
|
|
69
|
+
supplierName?: Field<string>;
|
|
70
|
+
supplierVatNumber?: Field<string>;
|
|
71
|
+
supplierIban?: Field<string>;
|
|
72
|
+
invoiceNumber?: Field<string>;
|
|
73
|
+
/** `"2026-09-24"`. */
|
|
74
|
+
issueDate?: Field<string>;
|
|
75
|
+
dueDate?: Field<string>;
|
|
76
|
+
currency?: Field<string>;
|
|
77
|
+
}
|
|
78
|
+
export interface ReceiptLine {
|
|
79
|
+
description?: string;
|
|
80
|
+
netCents: Cents;
|
|
81
|
+
taxCents: Cents;
|
|
82
|
+
/** `TaxCode.key`, e.g. `high`. */
|
|
83
|
+
taxRateKey: string;
|
|
84
|
+
taxPercent?: number;
|
|
85
|
+
taxKind?: TaxRateKind;
|
|
86
|
+
/** The account in the target books (`LedgerOption.key`). */
|
|
87
|
+
accountKey?: string;
|
|
88
|
+
aiSuggested?: boolean;
|
|
89
|
+
}
|
|
90
|
+
/** Totals from the lines: the lines are the truth, the totals follow. */
|
|
91
|
+
export declare function receiptTotals(lines: readonly ReceiptLine[]): Receipt["totals"];
|
|
92
|
+
export type ReceiptProblem = "no_supplier" | "no_date" | "no_lines" | "no_account";
|
|
93
|
+
export declare const RECEIPT_SCOPE_KIND = "receipt";
|
|
94
|
+
/** The act a sign-off on a receipt is about. */
|
|
95
|
+
export declare const RECEIPT_BOOK_ACT = "book";
|
|
96
|
+
/** What still stands between this receipt and the books. Empty = it can be confirmed. */
|
|
97
|
+
export declare function receiptProblems(receipt: Pick<Receipt, "fields" | "lines">): ReceiptProblem[];
|
|
98
|
+
/**
|
|
99
|
+
* Where it sits in "Te verwerken": ready to confirm when nothing is missing, to check when only
|
|
100
|
+
* the reading needs a look, to choose when an account is missing (no proposal to confirm).
|
|
101
|
+
*/
|
|
102
|
+
export declare function receiptGroupOf(receipt: Pick<Receipt, "fields" | "lines" | "status">): WorkQueueItem["group"] | null;
|
|
103
|
+
/** The receipt as the books need it: a purchase, with the account per line a person chose. */
|
|
104
|
+
export declare function bookingDocumentOfReceipt(receipt: Receipt, sourceApp?: string): BookingDocument;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|