@opencxh/domain 1.294.0 → 1.295.1
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/from-quote.d.ts +18 -0
- package/dist/entities/agreement/from-quote.test.d.ts +1 -0
- package/dist/entities/agreement/index.d.ts +2 -0
- package/dist/entities/agreement/lifecycle.d.ts +44 -0
- package/dist/entities/agreement/lifecycle.test.d.ts +1 -0
- package/dist/entities/agreement/schedule.d.ts +4 -0
- package/dist/entities/agreement/types.d.ts +38 -0
- package/dist/entities/bank/match.d.ts +1 -1
- package/dist/entities/bookkeeping/receipt.d.ts +63 -1
- package/dist/entities/bookkeeping/types.d.ts +2 -0
- package/dist/entities/catalog/pricing.d.ts +5 -0
- package/dist/entities/catalog/types.d.ts +23 -1
- package/dist/entities/company/types.d.ts +6 -0
- package/dist/entities/financing/types.d.ts +8 -0
- package/dist/entities/ledger/entry.d.ts +12 -6
- package/dist/entities/ledger/types.d.ts +3 -1
- package/dist/entities/money/index.d.ts +2 -0
- package/dist/entities/money/month-parts.d.ts +19 -0
- package/dist/entities/money/month-parts.test.d.ts +1 -0
- package/dist/entities/money/period-price.d.ts +23 -0
- package/dist/entities/money/period-price.test.d.ts +1 -0
- package/dist/entities/money/totals.d.ts +5 -1
- package/dist/entities/purchase/contract.d.ts +124 -0
- package/dist/entities/purchase/contract.test.d.ts +1 -0
- package/dist/entities/purchase/demand.d.ts +68 -0
- package/dist/entities/purchase/demand.test.d.ts +1 -0
- package/dist/entities/purchase/index.d.ts +7 -0
- package/dist/entities/purchase/kinds.d.ts +8 -0
- package/dist/entities/purchase/logic.d.ts +35 -0
- package/dist/entities/purchase/match.d.ts +98 -0
- package/dist/entities/purchase/match.test.d.ts +1 -0
- package/dist/entities/purchase/packing-slip.d.ts +82 -0
- package/dist/entities/purchase/packing-slip.test.d.ts +1 -0
- package/dist/entities/purchase/purchase.test.d.ts +1 -0
- package/dist/entities/purchase/types.d.ts +147 -0
- package/dist/entities/scope/types.d.ts +1 -1
- package/dist/entities/transaction/document.d.ts +3 -0
- package/dist/entities/transaction/keys.d.ts +1 -7
- package/dist/entities/transaction/kinds.d.ts +7 -0
- package/dist/entities/transaction/types.d.ts +11 -1
- package/dist/index.cjs +23 -23
- package/dist/index.d.ts +3 -1
- package/dist/index.js +3928 -3042
- package/dist/platform/bookkeeping.d.ts +14 -1
- package/dist/platform/manifest.d.ts +6 -27
- package/dist/platform/navigation.d.ts +202 -0
- package/dist/platform/navigation.test.d.ts +1 -0
- package/dist/platform/provider.d.ts +7 -0
- package/dist/platform/provisioning.d.ts +88 -0
- package/dist/platform/resource.d.ts +19 -0
- package/dist/platform/resource.test.d.ts +1 -0
- package/dist/platform/routing.d.ts +13 -18
- package/package.json +1 -1
- package/dist/platform/menu.d.ts +0 -26
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Transaction, TransactionLine } from '../transaction/types';
|
|
2
|
+
import { SaveAgreementRequest } from './types';
|
|
3
|
+
/** A line that runs every period: a product line priced per month, quarter or year. */
|
|
4
|
+
export declare function isRecurringLine(line: TransactionLine): boolean;
|
|
5
|
+
/**
|
|
6
|
+
* An accepted quote as an agreement: its recurring lines, each with its price as a snapshot —
|
|
7
|
+
* what the customer accepted, not what the ladder says on some later day. One-off lines stay on
|
|
8
|
+
* the quote (they are invoiced from it).
|
|
9
|
+
*
|
|
10
|
+
* ponytail: a line discount is folded into the unit price, rounded per unit; a discount field on
|
|
11
|
+
* `LinePrice` if a cent per unit ever matters.
|
|
12
|
+
*/
|
|
13
|
+
export declare function agreementFromQuote(quote: Transaction, startsAt: number): SaveAgreementRequest;
|
|
14
|
+
/**
|
|
15
|
+
* What a quote is worth to the deal, excluding VAT: the one-off lines once, the recurring ones
|
|
16
|
+
* over a year (the term a quote does not state). Optional lines count for nothing.
|
|
17
|
+
*/
|
|
18
|
+
export declare function dealValueCents(quote: Pick<Transaction, "lines">): number;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { Agreement, AgreementLine, AgreementStatus, Commitment } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Where an agreement may go. `draft → active` is starting it; `active → ended` is not a click but
|
|
4
|
+
* the run, once the last window and its grace are billed (see {@link finishedAt}) — a person ends
|
|
5
|
+
* an agreement by giving it an end date. `ended → active` is a new term: no catch-up billing.
|
|
6
|
+
*/
|
|
7
|
+
export declare const AGREEMENT_TRANSITIONS: Record<AgreementStatus, readonly AgreementStatus[]>;
|
|
8
|
+
export declare function canMoveAgreement(from: AgreementStatus, to: AgreementStatus): boolean;
|
|
9
|
+
/**
|
|
10
|
+
* The last moment anything is still due on an agreement with an end: the close of each line's
|
|
11
|
+
* last window plus grace, so usage in arrears and a prorated settlement still ride along.
|
|
12
|
+
* `null` while any line runs open-ended.
|
|
13
|
+
*/
|
|
14
|
+
export declare function finishedAt(agreement: Pick<Agreement, "startsAt" | "endsAt" | "interval" | "graceDays" | "lines">): number | null;
|
|
15
|
+
/**
|
|
16
|
+
* Where a series' commitment counts from: its first line. A change (10 → 12 seats) is a successor
|
|
17
|
+
* line, but the term is the subscription's — seats added under NCE end with the rest.
|
|
18
|
+
*/
|
|
19
|
+
export declare function seriesStart(parent: Pick<Agreement, "startsAt" | "endsAt">, series: readonly Pick<AgreementLine, "startsAt" | "endsAt">[]): number;
|
|
20
|
+
/** The end (exclusive) of the commitment term that contains `at`, counted from `start`. */
|
|
21
|
+
export declare function termEndAt(start: number, commitment: Commitment, at: number): number;
|
|
22
|
+
/** The start of the commitment term that contains `at`: where a change window opens. */
|
|
23
|
+
export declare function termStartAt(start: number, commitment: Commitment, at: number): number;
|
|
24
|
+
/**
|
|
25
|
+
* The last day to give notice so the line stops at the end of the term running on `now`; past it,
|
|
26
|
+
* the deadline of the next term. `null` without a commitment (cancel any time) or when a
|
|
27
|
+
* non-renewing term simply runs out.
|
|
28
|
+
*/
|
|
29
|
+
export declare function noticeDeadline(start: number, commitment: Commitment | undefined, now: number): number | null;
|
|
30
|
+
/**
|
|
31
|
+
* When the line really ends if notice is given on `noticeAt`: the end of the running term when
|
|
32
|
+
* the notice is in time, else the end of the next one. Without a commitment, at once.
|
|
33
|
+
* Not the same as stopping the service today — that is a different decision.
|
|
34
|
+
*/
|
|
35
|
+
export declare function effectiveEndAt(start: number, commitment: Commitment | undefined, noticeAt: number): number;
|
|
36
|
+
/** The whole agreement: the latest effective end over the lines still running. */
|
|
37
|
+
export declare function agreementEffectiveEnd(agreement: Pick<Agreement, "startsAt" | "endsAt" | "commitment" | "lines">, noticeAt: number): number;
|
|
38
|
+
/** A billed line may only get (or move) its end; anything else must be a successor. */
|
|
39
|
+
export declare function changesBilledLine(before: AgreementLine, after: AgreementLine): boolean;
|
|
40
|
+
/**
|
|
41
|
+
* A change from `at`: the line ends there and a successor in the same series takes over with the
|
|
42
|
+
* changed fields, up to the old end. What was billed keeps saying what it said.
|
|
43
|
+
*/
|
|
44
|
+
export declare function splitLine(line: AgreementLine, at: number, changed: Partial<AgreementLine>, successorId: string): [AgreementLine, AgreementLine];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Cents } from '../money/money';
|
|
2
|
+
import { PricePer } from '../money/period-price';
|
|
2
3
|
import { Period } from './period';
|
|
3
4
|
import { Agreement, AgreementLine, LineBilling, LineRhythm } from './types';
|
|
4
5
|
/** A fixed line keeps its own rhythm; usage follows the agreement, it is read per period. */
|
|
@@ -11,6 +12,7 @@ export interface BilledLine {
|
|
|
11
12
|
servicePeriod: Period;
|
|
12
13
|
quantity: number;
|
|
13
14
|
unitPriceCents: Cents;
|
|
15
|
+
pricePer?: PricePer;
|
|
14
16
|
correction?: boolean;
|
|
15
17
|
}
|
|
16
18
|
/** One window of one line that is due and not yet on an invoice. */
|
|
@@ -32,6 +34,8 @@ export interface Correction {
|
|
|
32
34
|
part: Period;
|
|
33
35
|
quantity: number;
|
|
34
36
|
unitPriceCents: Cents;
|
|
37
|
+
/** From the billed line: a period price settles by calendar share, never at today's price. */
|
|
38
|
+
pricePer?: PricePer;
|
|
35
39
|
}
|
|
36
40
|
/**
|
|
37
41
|
* What an agreement owes on `now`, per line: every window that is due minus the windows an
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { PriceBand } from '../catalog/rating';
|
|
2
2
|
import { Cents } from '../money/money';
|
|
3
|
+
import { PricePer } from '../money/period-price';
|
|
3
4
|
/**
|
|
4
5
|
* A standing arrangement with a customer that bills every period: number rental, licences, and
|
|
5
6
|
* the usage of an account at a source (calls on a PBX tenant).
|
|
@@ -32,6 +33,12 @@ export interface Agreement {
|
|
|
32
33
|
*/
|
|
33
34
|
graceDays: number;
|
|
34
35
|
paymentTermKey?: string;
|
|
36
|
+
/** "Reverse charge / exempt" for this customer: every invoice the run writes carries it. */
|
|
37
|
+
taxOverrideKey?: string;
|
|
38
|
+
/** The default commitment of its lines; a line may carry its own. Absent = cancel any time. */
|
|
39
|
+
commitment?: Commitment;
|
|
40
|
+
/** The quote it was made from, if any. */
|
|
41
|
+
sourceTransactionId?: string;
|
|
35
42
|
/**
|
|
36
43
|
* The account usage is read from (a PBX tenant). Usage lines without an account of their own
|
|
37
44
|
* take this one; a line may still name another, say texts from a second source.
|
|
@@ -42,13 +49,42 @@ export interface Agreement {
|
|
|
42
49
|
createdAt?: number;
|
|
43
50
|
updatedAt?: number;
|
|
44
51
|
}
|
|
52
|
+
/** Moves only along `AGREEMENT_TRANSITIONS`; `ended` is set by the run once nothing is left. */
|
|
45
53
|
export type AgreementStatus = "draft" | "active" | "ended";
|
|
54
|
+
/**
|
|
55
|
+
* What a customer (or we, at a supplier) committed to: a first term, notice before its end, and
|
|
56
|
+
* what happens after — `term` renews for the same length, `month` runs on month by month (the
|
|
57
|
+
* Dutch default after a minimum term), `none` stops at the end of the first term.
|
|
58
|
+
*/
|
|
59
|
+
export interface Commitment {
|
|
60
|
+
termMonths: number;
|
|
61
|
+
noticeDays: number;
|
|
62
|
+
renew: "term" | "month" | "none";
|
|
63
|
+
}
|
|
64
|
+
/** A price fixed on the line when it was agreed, instead of the ladder at billing time. */
|
|
65
|
+
export interface LinePrice {
|
|
66
|
+
cents: Cents;
|
|
67
|
+
currency: string;
|
|
68
|
+
per?: PricePer;
|
|
69
|
+
/** The unit the price is for, when it is not the product's (a supplier's box of 10). */
|
|
70
|
+
unit?: string;
|
|
71
|
+
}
|
|
46
72
|
export type AgreementInterval = "month" | "quarter" | "year";
|
|
47
73
|
export interface AgreementLine {
|
|
48
74
|
id: string;
|
|
49
75
|
productId: string;
|
|
50
76
|
/** Copied from the product, like a transaction line: the agreement must stay readable. */
|
|
51
77
|
name: string;
|
|
78
|
+
/**
|
|
79
|
+
* One line through its changes: 10 seats ending on 12 May and 12 seats from then share it. A
|
|
80
|
+
* billed line never changes; a change ends it and starts a successor in the same series.
|
|
81
|
+
* Absent = the line's own id.
|
|
82
|
+
*/
|
|
83
|
+
seriesId?: string;
|
|
84
|
+
/** Agreed price, snapshot (from the quote). Absent = the price ladder on the window's day. */
|
|
85
|
+
price?: LinePrice;
|
|
86
|
+
/** Overrides the agreement's commitment for this line. */
|
|
87
|
+
commitment?: Commitment;
|
|
52
88
|
/**
|
|
53
89
|
* Where the quantity comes from. How it is priced is the product's business (`pricing`), and a
|
|
54
90
|
* price for this agreement is a price agreement with `contractId` = the agreement.
|
|
@@ -128,6 +164,8 @@ export interface RunLine {
|
|
|
128
164
|
taxRateKey: string;
|
|
129
165
|
quantity: number;
|
|
130
166
|
unitPriceCents: Cents;
|
|
167
|
+
/** The period the unit price is for; the line then bills its share of `servicePeriod`. */
|
|
168
|
+
pricePer?: PricePer;
|
|
131
169
|
priceSourceKey: string;
|
|
132
170
|
/** What it covers: the part of the line's window it ran. */
|
|
133
171
|
servicePeriod: {
|
|
@@ -46,7 +46,7 @@ export declare function reconciliationBalances(rec: Pick<Reconciliation, "alloca
|
|
|
46
46
|
* the item (an invoice +, a receipt −). What sales hears, and what the books show.
|
|
47
47
|
*/
|
|
48
48
|
export declare function paymentOfItem(grossCents: Cents, openCents: Cents): {
|
|
49
|
-
state: "
|
|
49
|
+
state: "unpaid" | "partially_paid" | "paid" | "overpaid";
|
|
50
50
|
paidCents: number;
|
|
51
51
|
openCents: number;
|
|
52
52
|
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { BookingDocument } from '../../platform/bookkeeping';
|
|
2
2
|
import { Cents } from '../money/money';
|
|
3
3
|
import { TaxRateKind } from '../money/tax';
|
|
4
|
+
import { MatchAllocation, MatchVerdict } from '../purchase/match';
|
|
4
5
|
import { WorkQueueItem } from './types';
|
|
5
6
|
/**
|
|
6
7
|
* A receipt or purchase invoice, as finance holds it until it is booked (plan 039 datamodel §6.4).
|
|
@@ -25,6 +26,20 @@ export interface Receipt {
|
|
|
25
26
|
};
|
|
26
27
|
receivedVia: "mail" | "upload";
|
|
27
28
|
receivedAt: number;
|
|
29
|
+
/** A supplier's credit note carries negative amounts and names the invoice it credits. */
|
|
30
|
+
kind?: "invoice" | "credit_note";
|
|
31
|
+
creditsReceiptId?: string;
|
|
32
|
+
/**
|
|
33
|
+
* The order and contract match purchase proposed (fase 7): what each line pays for, and whether
|
|
34
|
+
* it all is what was ordered or agreed. `ok` approves it without a person.
|
|
35
|
+
*/
|
|
36
|
+
match?: ReceiptMatch;
|
|
37
|
+
/** From the bank reconciliation, whole state: what is still to pay. */
|
|
38
|
+
payment?: {
|
|
39
|
+
state: "unpaid" | "partially_paid" | "paid" | "overpaid";
|
|
40
|
+
openCents: Cents;
|
|
41
|
+
lastPaymentAt?: number;
|
|
42
|
+
};
|
|
28
43
|
mail?: {
|
|
29
44
|
interactionId: string;
|
|
30
45
|
activityId: string;
|
|
@@ -33,6 +48,11 @@ export interface Receipt {
|
|
|
33
48
|
};
|
|
34
49
|
supplierCompanyId?: string;
|
|
35
50
|
fields: ReceiptFields;
|
|
51
|
+
/**
|
|
52
|
+
* Every line as printed, never merged: a Pax8 invoice with twelve subscriptions keeps twelve.
|
|
53
|
+
* The one truth — booking lines are grouped from these when it is booked, and an order match
|
|
54
|
+
* reads them per subscription and period.
|
|
55
|
+
*/
|
|
36
56
|
lines: ReceiptLine[];
|
|
37
57
|
totals: {
|
|
38
58
|
netCents: Cents;
|
|
@@ -74,9 +94,36 @@ export interface ReceiptFields {
|
|
|
74
94
|
issueDate?: Field<string>;
|
|
75
95
|
dueDate?: Field<string>;
|
|
76
96
|
currency?: Field<string>;
|
|
97
|
+
/**
|
|
98
|
+
* Euro per unit of `currency`, a decimal string: the ECB rate on the invoice date as a proposal
|
|
99
|
+
* (`ai` then means "proposed"), a person may overwrite it. Absent for a document in euro.
|
|
100
|
+
*/
|
|
101
|
+
fxRate?: Field<string>;
|
|
77
102
|
}
|
|
103
|
+
export interface ReceiptMatch {
|
|
104
|
+
allocations: MatchAllocation[];
|
|
105
|
+
unmatched: string[];
|
|
106
|
+
verdict: MatchVerdict;
|
|
107
|
+
/** The one administration every target sits in; absent when mixed or nothing matched. */
|
|
108
|
+
businessEntityId?: string;
|
|
109
|
+
mixed: boolean;
|
|
110
|
+
}
|
|
111
|
+
/** One printed line of a supplier invoice, with how it books. */
|
|
78
112
|
export interface ReceiptLine {
|
|
113
|
+
/** Stable for the line's life; what a treatment or a match points at (`receipt:<id>#<lineId>`). */
|
|
114
|
+
id?: string;
|
|
79
115
|
description?: string;
|
|
116
|
+
quantity?: number;
|
|
117
|
+
unitPriceCents?: Cents;
|
|
118
|
+
/** Their article or product code. */
|
|
119
|
+
sku?: string;
|
|
120
|
+
/** Their subscription or order id on the line (Pax8 `subscriptionId`). */
|
|
121
|
+
externalRef?: string;
|
|
122
|
+
/** The service period it bills, `YYYY-MM-DD` both, `to` inclusive as printed. */
|
|
123
|
+
period?: {
|
|
124
|
+
from: string;
|
|
125
|
+
to: string;
|
|
126
|
+
};
|
|
80
127
|
netCents: Cents;
|
|
81
128
|
taxCents: Cents;
|
|
82
129
|
/** `TaxCode.key`, e.g. `high`. */
|
|
@@ -88,6 +135,15 @@ export interface ReceiptLine {
|
|
|
88
135
|
aiSuggested?: boolean;
|
|
89
136
|
/** Absent = a cost. Own books only: the line becomes a fixed asset or is spread over months. */
|
|
90
137
|
treatment?: ReceiptLineTreatment;
|
|
138
|
+
/**
|
|
139
|
+
* Sold on (`resale`, the M365 seats for Acme) or ours. Resale is cost of sales for that
|
|
140
|
+
* customer and never a fixed asset, whatever the amount. Set by the order match or a person.
|
|
141
|
+
*/
|
|
142
|
+
use?: "own" | "resale";
|
|
143
|
+
/** Whose cost this is: reaches the journal line as a dimension, for margin per customer. */
|
|
144
|
+
customerCompanyId?: string;
|
|
145
|
+
/** The product it was ordered as (from the match): posting rules on product apply. */
|
|
146
|
+
productId?: string;
|
|
91
147
|
}
|
|
92
148
|
/**
|
|
93
149
|
* What a line becomes besides its booking (plan 041 §4): a fixed asset in a group (its account
|
|
@@ -130,6 +186,12 @@ export declare function receiptProblems(receipt: Pick<Receipt, "fields" | "lines
|
|
|
130
186
|
* Where it sits in "Te verwerken": ready to confirm when nothing is missing, to check when only
|
|
131
187
|
* the reading needs a look, to choose when an account is missing (no proposal to confirm).
|
|
132
188
|
*/
|
|
133
|
-
export declare function receiptGroupOf(receipt: Pick<Receipt, "fields" | "lines" | "status">): WorkQueueItem["group"] | null;
|
|
189
|
+
export declare function receiptGroupOf(receipt: Pick<Receipt, "fields" | "lines" | "status" | "match">): WorkQueueItem["group"] | null;
|
|
190
|
+
/**
|
|
191
|
+
* The printed lines grouped as the books take them: one per account, VAT rate and customer, the
|
|
192
|
+
* descriptions joined. Grouped here, at booking, and never stored — what is booked is always
|
|
193
|
+
* exactly the lines, and the lines stay what the supplier printed.
|
|
194
|
+
*/
|
|
195
|
+
export declare function bookingLinesOf(lines: readonly ReceiptLine[], fallback?: string): BookingDocument["lines"];
|
|
134
196
|
/** The receipt as the books need it: a purchase, with the account per line a person chose. */
|
|
135
197
|
export declare function bookingDocumentOfReceipt(receipt: Receipt, sourceApp?: string): BookingDocument;
|
|
@@ -27,6 +27,8 @@ export interface LedgerMappingRule {
|
|
|
27
27
|
ledgerCategoryKey?: string;
|
|
28
28
|
taxRateKey?: string;
|
|
29
29
|
};
|
|
30
|
+
/** Sales or purchases. Absent = sales: every rule made before purchases could be mapped. */
|
|
31
|
+
direction?: "sale" | "purchase";
|
|
30
32
|
/** `BookkeepingDescribe.ledgerOptions[].key`. */
|
|
31
33
|
targetKey: string;
|
|
32
34
|
externalTaxCode?: string;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Cents } from '../money/money';
|
|
2
|
+
import { PricePer } from '../money/period-price';
|
|
2
3
|
import { TransactionLine } from '../transaction/types';
|
|
3
4
|
import { PriceRule, Product } from './types';
|
|
4
5
|
/**
|
|
@@ -27,8 +28,12 @@ export interface PriceContext {
|
|
|
27
28
|
/** Epoch ms; the validity window is judged against this, not against "now". */
|
|
28
29
|
at: number;
|
|
29
30
|
}
|
|
31
|
+
/** Amount, currency and period travel together: € 222 means nothing without "per year". */
|
|
30
32
|
export interface ResolvedPrice {
|
|
31
33
|
unitPriceCents: Cents;
|
|
34
|
+
currency: string;
|
|
35
|
+
/** Absent = once, or per unit. */
|
|
36
|
+
per?: PricePer;
|
|
32
37
|
/** Where this amount came from. Almost free, and the only question a salesperson asks. */
|
|
33
38
|
sourceKey: string;
|
|
34
39
|
ruleId?: string;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Cents } from '../money/money';
|
|
2
|
+
import { PricePer } from '../money/period-price';
|
|
2
3
|
/**
|
|
3
4
|
* What we sell, variants included.
|
|
4
5
|
*
|
|
@@ -57,6 +58,11 @@ export interface Product {
|
|
|
57
58
|
taxRateKey: string;
|
|
58
59
|
listPriceCents: Cents;
|
|
59
60
|
currency: string;
|
|
61
|
+
/**
|
|
62
|
+
* The period the list and cost price are for: € 222 per year. Absent = once, or per unit.
|
|
63
|
+
* A recurring agreement line needs one; the run bills its share of each window.
|
|
64
|
+
*/
|
|
65
|
+
pricePer?: PricePer;
|
|
60
66
|
/**
|
|
61
67
|
* What we normally pay for this. **Not** a cost price out of an inventory valuation.
|
|
62
68
|
*
|
|
@@ -65,6 +71,10 @@ export interface Product {
|
|
|
65
71
|
* would be one typo away.
|
|
66
72
|
*/
|
|
67
73
|
costPriceCents?: Cents;
|
|
74
|
+
/** The period the cost price is for, when it differs from {@link Product.pricePer}. */
|
|
75
|
+
costPricePer?: PricePer;
|
|
76
|
+
/** The purchase offer the cost comes from; absent = typed by hand. Read-only in the form then. */
|
|
77
|
+
costOfferId?: string;
|
|
68
78
|
/** Who we buy it from. A `Company` in crm, so free — and all a later purchase order needs. */
|
|
69
79
|
supplierCompanyId?: string;
|
|
70
80
|
/**
|
|
@@ -75,18 +85,28 @@ export interface Product {
|
|
|
75
85
|
* it is what was chosen.
|
|
76
86
|
*/
|
|
77
87
|
businessEntityId?: string;
|
|
88
|
+
/** On quotes, invoices and agreements. Absent = yes; steel plate you only buy is `false`. */
|
|
89
|
+
sellable?: boolean;
|
|
90
|
+
/** On purchase orders. Absent = no. */
|
|
91
|
+
purchasable?: boolean;
|
|
78
92
|
archived?: boolean;
|
|
79
93
|
order?: number;
|
|
80
94
|
createdBy: string;
|
|
81
95
|
createdAt?: number;
|
|
82
96
|
updatedAt?: number;
|
|
83
97
|
}
|
|
98
|
+
/** The cost price in the period of the list price, for a margin. Absent without a cost price. */
|
|
99
|
+
export declare function costInPricePeriod(product: Pick<Product, "costPriceCents" | "costPricePer" | "pricePer">): Cents | undefined;
|
|
100
|
+
/** Whether a sales picker offers it: absent counts as sellable, so older products stay. */
|
|
101
|
+
export declare function isSellable(product: Pick<Product, "sellable" | "archived">): boolean;
|
|
102
|
+
/** Whether a purchase picker offers it. */
|
|
103
|
+
export declare function isPurchasable(product: Pick<Product, "purchasable" | "archived">): boolean;
|
|
84
104
|
/**
|
|
85
105
|
* Units follow work's `FIXED_TYPES` pattern: a fixed list plus a namespaced fallback, so an
|
|
86
106
|
* unknown key reads as itself instead of breaking.
|
|
87
107
|
*/
|
|
88
108
|
export type ProductPricing = "unit" | "rated";
|
|
89
|
-
export declare const FIXED_UNITS: readonly ["piece", "hour", "day", "month", "km", "license", "minute"];
|
|
109
|
+
export declare const FIXED_UNITS: readonly ["piece", "hour", "day", "month", "km", "license", "minute", "kg", "ton", "m", "m2", "m3", "liter"];
|
|
90
110
|
export type FixedUnit = (typeof FIXED_UNITS)[number];
|
|
91
111
|
/**
|
|
92
112
|
* A price for a customer, a segment or a contract.
|
|
@@ -111,6 +131,8 @@ export interface PriceRule {
|
|
|
111
131
|
* product ("this customer calls on Enterprise") and is ignored by the unit ladder.
|
|
112
132
|
*/
|
|
113
133
|
unitPriceCents?: Cents;
|
|
134
|
+
/** The period a fixed price is for; absent = the product's. A discount needs none. */
|
|
135
|
+
pricePer?: PricePer;
|
|
114
136
|
discountPercent?: number;
|
|
115
137
|
rateCardId?: string;
|
|
116
138
|
currency: string;
|
|
@@ -70,6 +70,12 @@ export interface Company {
|
|
|
70
70
|
peppolId?: string;
|
|
71
71
|
debtorNumber?: string;
|
|
72
72
|
creditorNumber?: string;
|
|
73
|
+
/** We buy from them. Loose fields on the company, as in Exact — not a supplier entity. */
|
|
74
|
+
isSupplier?: boolean;
|
|
75
|
+
/** Their payment condition towards us; {@link Company.paymentTermCode} is ours towards them. */
|
|
76
|
+
supplierPaymentTermKey?: string;
|
|
77
|
+
/** The currency they invoice us in; empty = the administration's. */
|
|
78
|
+
supplierCurrency?: string;
|
|
73
79
|
/** Raw, human-readable phone/website as entered, for display. */
|
|
74
80
|
phone?: string;
|
|
75
81
|
website?: string;
|
|
@@ -168,6 +168,14 @@ export interface AccrualTerms {
|
|
|
168
168
|
months: number;
|
|
169
169
|
resultAccountId: string;
|
|
170
170
|
upfront: boolean;
|
|
171
|
+
/**
|
|
172
|
+
* The exact service period, when there is one: released by the days of each calendar month
|
|
173
|
+
* (`monthParts`), the same split the other side of the margin uses. Absent = equal months.
|
|
174
|
+
*/
|
|
175
|
+
period?: {
|
|
176
|
+
from: string;
|
|
177
|
+
to: string;
|
|
178
|
+
};
|
|
171
179
|
}
|
|
172
180
|
export type FinancingTerms = LoanTerms | CurrentAccountTerms | ParticipationTerms | DepositTerms | FixedAssetTerms | AccrualTerms;
|
|
173
181
|
export interface FinancingItem {
|
|
@@ -62,6 +62,8 @@ export interface DocumentPostingContext {
|
|
|
62
62
|
accounts: readonly LedgerAccount[];
|
|
63
63
|
rules: readonly PostingRule[];
|
|
64
64
|
taxAccounts: readonly TaxAccountMapping[];
|
|
65
|
+
/** The administration's own currency; `EUR` when unset. */
|
|
66
|
+
functionalCurrency?: string;
|
|
65
67
|
}
|
|
66
68
|
export type DocumentEntry = {
|
|
67
69
|
ok: true;
|
|
@@ -73,11 +75,15 @@ export type DocumentEntry = {
|
|
|
73
75
|
problem: string;
|
|
74
76
|
};
|
|
75
77
|
/**
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
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.
|
|
78
|
+
* The lines of a sale or purchase that run over more than one calendar month: a year of M365
|
|
79
|
+
* invoiced in October is not October's revenue or cost. Own books take these through the
|
|
80
|
+
* balance and release them month by month. A journal is booked as sent.
|
|
82
81
|
*/
|
|
82
|
+
export declare function transitoryLines(doc: BookingDocument): {
|
|
83
|
+
index: number;
|
|
84
|
+
period: {
|
|
85
|
+
from: string;
|
|
86
|
+
to: string;
|
|
87
|
+
};
|
|
88
|
+
}[];
|
|
83
89
|
export declare function entryLinesOf(doc: BookingDocument, context: DocumentPostingContext): DocumentEntry;
|
|
@@ -34,7 +34,9 @@ export interface LedgerAccount {
|
|
|
34
34
|
createdAt?: number;
|
|
35
35
|
updatedAt?: number;
|
|
36
36
|
}
|
|
37
|
-
export type LedgerAccountRole = "debtors" | "creditors" | "vat_payable" | "vat_receivable" | "revenue_default" | "expense_default" | "suspense" | "result"
|
|
37
|
+
export type LedgerAccountRole = "debtors" | "creditors" | "vat_payable" | "vat_receivable" | "revenue_default" | "expense_default" | "suspense" | "result"
|
|
38
|
+
/** Where the difference lands when a foreign document is paid at another rate than booked. */
|
|
39
|
+
| "fx_difference";
|
|
38
40
|
export type JournalKind = "sales" | "purchase" | "bank" | "cash" | "memorial" | "opening";
|
|
39
41
|
export interface Journal {
|
|
40
42
|
id: string;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Cents } from './money';
|
|
2
|
+
/**
|
|
3
|
+
* `total` over the calendar months of a period (`to` inclusive, as printed): a whole month one
|
|
4
|
+
* share, a part of one by its days, in whole cents that add up exactly — leftover cents to the
|
|
5
|
+
* first months, as `allocate` does. One distribution for revenue and cost alike, so a month's
|
|
6
|
+
* margin is the same sum on both sides. `month` is each month's first day.
|
|
7
|
+
*/
|
|
8
|
+
export declare function monthParts(total: Cents, period: {
|
|
9
|
+
from: string;
|
|
10
|
+
to: string;
|
|
11
|
+
}): {
|
|
12
|
+
month: string;
|
|
13
|
+
cents: Cents;
|
|
14
|
+
}[];
|
|
15
|
+
/** How many calendar months a period touches. */
|
|
16
|
+
export declare function monthsIn(period: {
|
|
17
|
+
from: string;
|
|
18
|
+
to: string;
|
|
19
|
+
}): number;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Cents } from './money';
|
|
2
|
+
/** What a recurring price is for: € 25 per month, € 222 per year. */
|
|
3
|
+
export type PricePer = "month" | "quarter" | "year";
|
|
4
|
+
export declare const PRICE_PERS: readonly PricePer[];
|
|
5
|
+
/**
|
|
6
|
+
* `total` (one `per`, all units together) over `window`, in whole cents.
|
|
7
|
+
*
|
|
8
|
+
* The distribution key is the calendar: a yearly total is split over its 12 months with
|
|
9
|
+
* `allocate` (leftover cents to the first months), a quarterly one over its 3. A month is billed
|
|
10
|
+
* as its own share, a part of a month by its days. So month 7 on its own is exactly month 7 of a
|
|
11
|
+
* yearly run, and twelve months of € 100/year add up to € 100,00. Rounded once, at the end.
|
|
12
|
+
*/
|
|
13
|
+
export declare function priceOver(total: Cents, per: PricePer, window: {
|
|
14
|
+
from: number;
|
|
15
|
+
to: number;
|
|
16
|
+
}): Cents;
|
|
17
|
+
/**
|
|
18
|
+
* An amount per `from` restated per `to` (€ 222 per year → € 18,50 per month), for comparing two
|
|
19
|
+
* prices side by side — a margin, a cost next to a list price. **Never stored**: billing goes
|
|
20
|
+
* through {@link priceOver}, which keeps the calendar split exact.
|
|
21
|
+
*/
|
|
22
|
+
export declare function restate(cents: Cents, from: PricePer | undefined, to: PricePer | undefined): Cents;
|
|
23
|
+
export declare function isPricePer(value: unknown): value is PricePer;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -14,8 +14,12 @@ import { TaxRate } from './tax';
|
|
|
14
14
|
* and the tax office do.
|
|
15
15
|
*
|
|
16
16
|
* `quantity` may be fractional (3.5 hours); a *price* never is.
|
|
17
|
+
*
|
|
18
|
+
* A price with a period (`pricePer`) on a line with a service period bills that period's share
|
|
19
|
+
* of the line total — `priceOver`, so the line total is rounded once and then split by calendar.
|
|
20
|
+
* Without a service period (a quote) it is one period's amount.
|
|
17
21
|
*/
|
|
18
|
-
export declare function lineNet(line: TransactionLine): Cents;
|
|
22
|
+
export declare function lineNet(line: Pick<TransactionLine, "kind" | "quantity" | "unitPriceCents" | "discountPercent" | "pricePer" | "servicePeriod">): Cents;
|
|
19
23
|
export interface PricedTransaction {
|
|
20
24
|
/** The same lines with `netCents` and `taxCents` filled in. */
|
|
21
25
|
lines: TransactionLine[];
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { Period } from '../agreement/period';
|
|
2
|
+
import { Agreement, AgreementLine } from '../agreement/types';
|
|
3
|
+
import { Cents } from '../money/money';
|
|
4
|
+
/** A sales line, followed through its changes: the agreement and the series it belongs to. */
|
|
5
|
+
export interface SalesLineRef {
|
|
6
|
+
agreementId: string;
|
|
7
|
+
seriesId: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* A subscription at a supplier: the shape of a sales agreement line, plus where it came from and
|
|
11
|
+
* whom it is for. Its `price` is the snapshot taken when it was made — an offer that gets dearer
|
|
12
|
+
* later changes nothing here; a new price is a successor line from a date.
|
|
13
|
+
*/
|
|
14
|
+
export interface PurchaseAgreementLine extends AgreementLine {
|
|
15
|
+
/** Provenance only: the offer it was bought on. The price is `price`, not the offer's. */
|
|
16
|
+
offerId?: string;
|
|
17
|
+
/** Their subscription id (Pax8, Microsoft). */
|
|
18
|
+
externalRef?: string;
|
|
19
|
+
/** What the supplier handed back when it was made (a tenant id, an account id). */
|
|
20
|
+
fulfillment?: Record<string, string>;
|
|
21
|
+
/** Where it landed when that is not the supplier, so an end takes it off there first. */
|
|
22
|
+
deliveredTo?: {
|
|
23
|
+
app: string;
|
|
24
|
+
externalRef?: string;
|
|
25
|
+
};
|
|
26
|
+
customerCompanyId?: string;
|
|
27
|
+
/** The sales line that passes this on, followed by series. */
|
|
28
|
+
salesLine?: SalesLineRef;
|
|
29
|
+
/**
|
|
30
|
+
* `variable`: no price, the invoice decides the amount (transit minutes). Nothing is expected;
|
|
31
|
+
* only a jump against the last three months is work. A varying *count* (seats) is `quantity:
|
|
32
|
+
* manual|meter` with a fixed price instead — never this.
|
|
33
|
+
*/
|
|
34
|
+
expected?: "variable";
|
|
35
|
+
/** Days from each term's start in which the supplier still takes a change (NCE: 7). */
|
|
36
|
+
changeWindowDays?: number;
|
|
37
|
+
/** The order line it came from, so a second receipt updates instead of adding. */
|
|
38
|
+
sourceKey?: string;
|
|
39
|
+
/** What supplier invoices said per period (fase 7): the history a variable line is judged on. */
|
|
40
|
+
invoiced?: {
|
|
41
|
+
from: number;
|
|
42
|
+
cents: Cents;
|
|
43
|
+
}[];
|
|
44
|
+
}
|
|
45
|
+
/** A supplier contract: `companyId` is the supplier; one contract holds many subscriptions. */
|
|
46
|
+
export interface PurchaseAgreement extends Omit<Agreement, "lines"> {
|
|
47
|
+
lines: PurchaseAgreementLine[];
|
|
48
|
+
}
|
|
49
|
+
export interface ExpectedLine {
|
|
50
|
+
lineId: string;
|
|
51
|
+
kind: "fixed" | "count" | "variable";
|
|
52
|
+
/** Fixed lines only; a count waits for the count, a variable line expects nothing. */
|
|
53
|
+
cents?: Cents;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* What the supplier should invoice over `window`, per line: a fixed count at the agreed price over
|
|
57
|
+
* the days it ran (the same `lineNet` the sales run bills with), or "count" / "variable" where the
|
|
58
|
+
* amount is not ours to know in advance.
|
|
59
|
+
*/
|
|
60
|
+
export declare function expectedFor(agreement: Pick<PurchaseAgreement, "startsAt" | "endsAt" | "lines">, window: Period): ExpectedLine[];
|
|
61
|
+
/**
|
|
62
|
+
* A variable amount that moved more than `threshold` against the average of the (up to) three
|
|
63
|
+
* periods before: that is work for a person, not a number to wave through.
|
|
64
|
+
*/
|
|
65
|
+
export declare function variableJump(previous: readonly Cents[], current: Cents, threshold?: number): boolean;
|
|
66
|
+
/** A sales line as sales reports it now, by series. */
|
|
67
|
+
export interface SalesLineState extends SalesLineRef {
|
|
68
|
+
/** Fixed count now; `null` for a count from a meter or entered per period. */
|
|
69
|
+
quantity: number | null;
|
|
70
|
+
/** When it really ends if the customer gives notice today. */
|
|
71
|
+
effectiveEndAt: number;
|
|
72
|
+
/** Windows due now without an invoice line (`agreementLineId` + `servicePeriod`). */
|
|
73
|
+
unbilled: Period[];
|
|
74
|
+
/** The sales line has ended or its agreement is not active. */
|
|
75
|
+
ended?: boolean;
|
|
76
|
+
}
|
|
77
|
+
export type ContractSignal = {
|
|
78
|
+
kind: "not_covered";
|
|
79
|
+
contractId: string;
|
|
80
|
+
lineId: string;
|
|
81
|
+
reason: "no_sales_line" | "quantity" | "sales_ended";
|
|
82
|
+
} | {
|
|
83
|
+
kind: "not_billed";
|
|
84
|
+
contractId: string;
|
|
85
|
+
lineId: string;
|
|
86
|
+
windows: Period[];
|
|
87
|
+
} | {
|
|
88
|
+
kind: "term_mismatch";
|
|
89
|
+
contractId: string;
|
|
90
|
+
lineId: string;
|
|
91
|
+
purchaseEnd: number;
|
|
92
|
+
salesEnd: number;
|
|
93
|
+
} | {
|
|
94
|
+
kind: "notice_due";
|
|
95
|
+
contractId: string;
|
|
96
|
+
lineId: string;
|
|
97
|
+
deadline: number;
|
|
98
|
+
} | {
|
|
99
|
+
kind: "change_window";
|
|
100
|
+
contractId: string;
|
|
101
|
+
lineId: string;
|
|
102
|
+
closesAt: number;
|
|
103
|
+
} | {
|
|
104
|
+
kind: "variable_jump";
|
|
105
|
+
contractId: string;
|
|
106
|
+
lineId: string;
|
|
107
|
+
cents: Cents;
|
|
108
|
+
};
|
|
109
|
+
export interface SignalOptions {
|
|
110
|
+
/** A notice deadline this close is a reminder. */
|
|
111
|
+
noticeDays?: number;
|
|
112
|
+
/** A change window closing this soon is a reminder. */
|
|
113
|
+
windowDays?: number;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Everything about a supplier contract a person should look at, apart:
|
|
117
|
+
* - **not covered**: bought for a customer, but no sales line passes it on, or another count;
|
|
118
|
+
* - **not billed**: the sales line exists but a period that is due has no invoice line — a sales
|
|
119
|
+
* line that exists proves nothing, so this reads what sales actually invoiced;
|
|
120
|
+
* - **term mismatch**: we are bound longer than the customer is;
|
|
121
|
+
* - **notice due / change window**: reminders before the dates that decide the next term;
|
|
122
|
+
* - **variable jump**: a variable amount moved more than 30%.
|
|
123
|
+
*/
|
|
124
|
+
export declare function contractSignals(contracts: readonly PurchaseAgreement[], sales: readonly SalesLineState[], now: number, options?: SignalOptions): ContractSignal[];
|