@opencxh/domain 1.293.0 → 1.295.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 (58) hide show
  1. package/dist/entities/agreement/from-quote.d.ts +18 -0
  2. package/dist/entities/agreement/from-quote.test.d.ts +1 -0
  3. package/dist/entities/agreement/index.d.ts +2 -0
  4. package/dist/entities/agreement/lifecycle.d.ts +44 -0
  5. package/dist/entities/agreement/lifecycle.test.d.ts +1 -0
  6. package/dist/entities/agreement/schedule.d.ts +4 -0
  7. package/dist/entities/agreement/types.d.ts +38 -0
  8. package/dist/entities/bank/match.d.ts +1 -1
  9. package/dist/entities/bookkeeping/receipt.d.ts +95 -2
  10. package/dist/entities/bookkeeping/types.d.ts +2 -0
  11. package/dist/entities/catalog/pricing.d.ts +5 -0
  12. package/dist/entities/catalog/types.d.ts +23 -1
  13. package/dist/entities/company/types.d.ts +6 -0
  14. package/dist/entities/financing/schedule.d.ts +40 -1
  15. package/dist/entities/financing/types.d.ts +99 -3
  16. package/dist/entities/ledger/entry.d.ts +12 -6
  17. package/dist/entities/ledger/template.d.ts +5 -0
  18. package/dist/entities/ledger/types.d.ts +9 -2
  19. package/dist/entities/money/index.d.ts +2 -0
  20. package/dist/entities/money/month-parts.d.ts +19 -0
  21. package/dist/entities/money/month-parts.test.d.ts +1 -0
  22. package/dist/entities/money/period-price.d.ts +23 -0
  23. package/dist/entities/money/period-price.test.d.ts +1 -0
  24. package/dist/entities/money/totals.d.ts +5 -1
  25. package/dist/entities/purchase/contract.d.ts +124 -0
  26. package/dist/entities/purchase/contract.test.d.ts +1 -0
  27. package/dist/entities/purchase/demand.d.ts +68 -0
  28. package/dist/entities/purchase/demand.test.d.ts +1 -0
  29. package/dist/entities/purchase/index.d.ts +7 -0
  30. package/dist/entities/purchase/kinds.d.ts +8 -0
  31. package/dist/entities/purchase/logic.d.ts +35 -0
  32. package/dist/entities/purchase/match.d.ts +98 -0
  33. package/dist/entities/purchase/match.test.d.ts +1 -0
  34. package/dist/entities/purchase/packing-slip.d.ts +82 -0
  35. package/dist/entities/purchase/packing-slip.test.d.ts +1 -0
  36. package/dist/entities/purchase/purchase.test.d.ts +1 -0
  37. package/dist/entities/purchase/types.d.ts +147 -0
  38. package/dist/entities/reports/reports.d.ts +9 -0
  39. package/dist/entities/transaction/document.d.ts +3 -0
  40. package/dist/entities/transaction/keys.d.ts +1 -7
  41. package/dist/entities/transaction/kinds.d.ts +7 -0
  42. package/dist/entities/transaction/types.d.ts +11 -1
  43. package/dist/entities/work/index.d.ts +1 -1
  44. package/dist/entities/work/types.d.ts +1 -1
  45. package/dist/index.cjs +23 -23
  46. package/dist/index.d.ts +3 -1
  47. package/dist/index.js +4187 -3117
  48. package/dist/platform/bookkeeping.d.ts +29 -3
  49. package/dist/platform/manifest.d.ts +6 -27
  50. package/dist/platform/navigation.d.ts +202 -0
  51. package/dist/platform/navigation.test.d.ts +1 -0
  52. package/dist/platform/provider.d.ts +7 -0
  53. package/dist/platform/provisioning.d.ts +88 -0
  54. package/dist/platform/resource.d.ts +19 -0
  55. package/dist/platform/resource.test.d.ts +1 -0
  56. package/dist/platform/routing.d.ts +13 -18
  57. package/package.json +1 -1
  58. 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 {};
@@ -1,3 +1,5 @@
1
+ export * from './from-quote';
2
+ export * from './lifecycle';
1
3
  export * from './period';
2
4
  export * from './schedule';
3
5
  export * from './types';
@@ -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: "paid" | "overpaid" | "unpaid" | "partially_paid";
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>;
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;
77
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`. */
@@ -86,10 +133,50 @@ export interface ReceiptLine {
86
133
  /** The account in the target books (`LedgerOption.key`). */
87
134
  accountKey?: string;
88
135
  aiSuggested?: boolean;
136
+ /** Absent = a cost. Own books only: the line becomes a fixed asset or is spread over months. */
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;
89
147
  }
148
+ /**
149
+ * What a line becomes besides its booking (plan 041 §4): a fixed asset in a group (its account
150
+ * is then the group's cost account), or a prepaid cost released over months (its account is
151
+ * prepaid costs). Made once, when the receipt is handed to the books.
152
+ */
153
+ export type ReceiptLineTreatment = {
154
+ kind: "asset";
155
+ groupId: string;
156
+ name?: string;
157
+ identifier?: string;
158
+ assignedUserId?: string;
159
+ quantity?: number;
160
+ aiSuggested?: boolean;
161
+ } | {
162
+ kind: "spread";
163
+ months: number;
164
+ /** The first month released, as its first day. */
165
+ firstMonth: string;
166
+ resultAccountId: string;
167
+ };
168
+ /**
169
+ * Proposes "activate" for a line at or above the threshold whose account is a group's cost
170
+ * account: the reader picked a fixed-asset account, the amount says it is one. Never overrides
171
+ * what a person chose; without a threshold (no rule for the country) it proposes nothing.
172
+ */
173
+ export declare function suggestTreatments(lines: readonly ReceiptLine[], groups: readonly {
174
+ id: string;
175
+ costAccountId: string;
176
+ }[], thresholdCents: number | undefined): ReceiptLine[];
90
177
  /** Totals from the lines: the lines are the truth, the totals follow. */
91
178
  export declare function receiptTotals(lines: readonly ReceiptLine[]): Receipt["totals"];
92
- export type ReceiptProblem = "no_supplier" | "no_date" | "no_lines" | "no_account";
179
+ export type ReceiptProblem = "no_supplier" | "no_date" | "no_lines" | "no_account" | "asset_without_group" | "spread_without_months";
93
180
  export declare const RECEIPT_SCOPE_KIND = "receipt";
94
181
  /** The act a sign-off on a receipt is about. */
95
182
  export declare const RECEIPT_BOOK_ACT = "book";
@@ -99,6 +186,12 @@ export declare function receiptProblems(receipt: Pick<Receipt, "fields" | "lines
99
186
  * Where it sits in "Te verwerken": ready to confirm when nothing is missing, to check when only
100
187
  * the reading needs a look, to choose when an account is missing (no proposal to confirm).
101
188
  */
102
- 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"];
103
196
  /** The receipt as the books need it: a purchase, with the account per line a person chose. */
104
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;
@@ -52,10 +52,31 @@ type PlannedPosting = Omit<ScheduledPosting, "id" | "organizationId" | "business
52
52
  export declare function monthEnd(day: string): string;
53
53
  /** `totalCents` over `months` equal parts; the last takes the rounding, so the parts add up exactly. */
54
54
  export declare function spread(totalCents: Cents, months: number): Cents[];
55
- /** Straight-line depreciation: (cost − residual) over the life, from the month of acquisition. */
55
+ /**
56
+ * The monthly amounts, from the acquisition (or the restart after a revision) on. Linear spreads
57
+ * (start − residual) over the months left; declining takes the monthly share of a yearly
58
+ * percentage of the book value until the residual is reached, the last month exactly; none is
59
+ * nothing. The sum is always start − residual, never below the residual.
60
+ */
61
+ export declare function depreciationParts(t: FixedAssetTerms): Cents[];
62
+ /** Depreciation per month by the asset's method; a disposed asset plans nothing more. */
56
63
  export declare function depreciationPostings(item: Pick<FinancingItem, "id" | "name"> & {
57
64
  terms: FixedAssetTerms;
58
65
  }, through: string): PlannedPosting[];
66
+ /**
67
+ * The memorial entry that takes an asset off the books: its cost and accumulated depreciation
68
+ * off (both on the item, so it ends at nil), the proceeds on the clearing account the sales
69
+ * invoice books against, and the difference on the group's disposal account.
70
+ */
71
+ export declare function disposalLines(item: Pick<FinancingItem, "id" | "name" | "ledgerAccountId"> & {
72
+ terms: FixedAssetTerms;
73
+ }, input: {
74
+ /** The item's booked movements: its accumulated depreciation, negative. */
75
+ movementsCents: Cents;
76
+ proceedsCents: Cents;
77
+ clearingAccountId: string;
78
+ disposalAccountId: string;
79
+ }): PlannedPosting["lines"];
59
80
  /** A transitorium released month by month onto its result account. */
60
81
  export declare function accrualPostings(item: Pick<FinancingItem, "id" | "name" | "ledgerAccountId"> & {
61
82
  terms: AccrualTerms;
@@ -70,4 +91,22 @@ export declare function startingCents(terms: FixedAssetTerms | AccrualTerms): Ce
70
91
  * show it as a short-term liability (aflossingsverplichting), the rest as long-term.
71
92
  */
72
93
  export declare function shortTermPrincipal(terms: Pick<LoanTerms, "principalCents" | "ratePercent" | "method" | "termMonths" | "firstDueOn">, yearEnd: string): Cents;
94
+ /**
95
+ * Part of a batch leaving: its share (quantity / the batch) of cost and accumulated depreciation
96
+ * comes off. The accumulated share leaves the item's sub-account; the cost share leaves the cost
97
+ * account without it, because the item's cost is its terms — they shrink by the same share.
98
+ */
99
+ export declare function partialDisposalLines(item: Pick<FinancingItem, "id" | "name" | "ledgerAccountId"> & {
100
+ terms: FixedAssetTerms;
101
+ }, input: {
102
+ quantity: number;
103
+ movementsCents: Cents;
104
+ proceedsCents: Cents;
105
+ clearingAccountId: string;
106
+ disposalAccountId: string;
107
+ }): {
108
+ lines: PlannedPosting["lines"];
109
+ costCents: Cents;
110
+ residualCents: Cents;
111
+ };
73
112
  export {};
@@ -50,10 +50,12 @@ export interface DepositTerms {
50
50
  kind: "deposit_paid";
51
51
  refundableOn?: string;
52
52
  }
53
+ /** Straight-line over the life, a yearly percentage of the book value, or none (land). */
54
+ export type DepreciationMethod = "linear" | "declining" | "none";
53
55
  /**
54
- * A fixed asset, depreciated straight-line per month from the month it was acquired. The item
55
- * sits under the cost account; depreciation lands on the accumulated-depreciation account, so the
56
- * annual accounts show cost and accumulated apart. Acquisition itself is booked by its invoice.
56
+ * A fixed asset, depreciated per month from the month it was acquired. The item sits under the
57
+ * cost account; depreciation lands on the accumulated-depreciation account, so the annual accounts
58
+ * show cost and accumulated apart. Acquisition itself is booked by its invoice.
57
59
  */
58
60
  export interface FixedAssetTerms {
59
61
  kind: "fixed_asset";
@@ -61,11 +63,95 @@ export interface FixedAssetTerms {
61
63
  costCents: Cents;
62
64
  residualCents: Cents;
63
65
  lifeMonths: number;
66
+ /** Absent on assets made before methods existed: linear. */
67
+ method?: DepreciationMethod;
68
+ /** `declining`: the yearly percentage of the book value, as a decimal string (`"25"`). */
69
+ ratePercent?: string;
64
70
  /** Expense: `Afschrijving inventaris`. */
65
71
  depreciationAccountId: string;
66
72
  /** Contra-asset: `Afschrijving inventaris` under the fixed assets. */
67
73
  accumulatedAccountId: string;
74
+ /** Register: the group it came from, what identifies it, where it is and who uses it. */
75
+ groupId?: string;
76
+ /** A licence plate, a serial number, an address. */
77
+ identifier?: string;
78
+ location?: string;
79
+ assignedUserId?: string;
80
+ /** A batch (12 laptops) is one asset with a quantity. */
81
+ quantity?: number;
82
+ /**
83
+ * A revision (new life, residual or method) from a month on: the schedule starts over from the
84
+ * book value then, numbered on from `fromSequence`, so the terms already posted stay as they are.
85
+ */
86
+ restart?: {
87
+ fromMonth: string;
88
+ bookValueCents: Cents;
89
+ fromSequence: number;
90
+ };
91
+ /**
92
+ * Part of a batch that left (3 of 12 laptops): each took its share of cost, residual and
93
+ * accumulated depreciation off; `costCents`, `residualCents` and `quantity` are what is left.
94
+ */
95
+ partialDisposals?: {
96
+ on: string;
97
+ quantity: number;
98
+ reason: "sold" | "scrapped" | "lost";
99
+ costCents: Cents;
100
+ proceedsCents: Cents;
101
+ buyerCompanyId?: string;
102
+ entryId?: string;
103
+ }[];
104
+ /** Sold, scrapped or lost: booked once, and the schedule stops. */
105
+ disposal?: {
106
+ on: string;
107
+ reason: "sold" | "scrapped" | "lost";
108
+ proceedsCents: Cents;
109
+ buyerCompanyId?: string;
110
+ entryId?: string;
111
+ };
68
112
  }
113
+ /**
114
+ * A kind of fixed asset (plan 041 §2): a flat list per administration, no tree. A new asset takes
115
+ * its accounts, method and life from the group; an asset may differ from it.
116
+ */
117
+ export interface AssetGroup {
118
+ id: string;
119
+ organizationId: string;
120
+ businessEntityId: string;
121
+ name: string;
122
+ costAccountId: string;
123
+ /** Absent for a group that is not depreciated (land). */
124
+ accumulatedAccountId?: string;
125
+ depreciationAccountId?: string;
126
+ /** Where a disposal's gain or loss lands: `Boekresultaat vervoermiddelen`. */
127
+ disposalAccountId: string;
128
+ method: DepreciationMethod;
129
+ lifeMonths?: number;
130
+ ratePercent?: string;
131
+ residualPercent?: string;
132
+ archived?: boolean;
133
+ createdAt?: number;
134
+ updatedAt?: number;
135
+ }
136
+ /**
137
+ * Below this a purchase is a cost, at or above it a fixed asset: per country, an administration
138
+ * may set its own. Only the Netherlands is filled in (€ 450, the fiscal rule); a country comes
139
+ * with its country pack. Known, not built: DE € 800 (GWG), FR € 500 HT, US $ 2,500 (de minimis).
140
+ * BE and UK have no statutory threshold: their own valuation rules decide.
141
+ */
142
+ export declare const CAPITALIZATION_THRESHOLD_CENTS: Readonly<Record<string, Cents>>;
143
+ export declare const capitalizationThreshold: (country?: string, own?: Cents) => Cents | undefined;
144
+ /** The groups a new administration starts with, by template account code. */
145
+ export declare const ASSET_GROUP_SEEDS: readonly {
146
+ name: string;
147
+ cost: string;
148
+ accumulated?: string;
149
+ depreciation?: string;
150
+ disposal: string;
151
+ method: DepreciationMethod;
152
+ lifeMonths?: number;
153
+ residualPercent?: string;
154
+ }[];
69
155
  /**
70
156
  * An amount spread over months (transitoria): prepaid insurance released to costs, revenue
71
157
  * invoiced ahead released to revenue, costs accrued before their invoice. `expense` debits the
@@ -82,6 +168,14 @@ export interface AccrualTerms {
82
168
  months: number;
83
169
  resultAccountId: string;
84
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
+ };
85
179
  }
86
180
  export type FinancingTerms = LoanTerms | CurrentAccountTerms | ParticipationTerms | DepositTerms | FixedAssetTerms | AccrualTerms;
87
181
  export interface FinancingItem {
@@ -101,6 +195,8 @@ export interface FinancingItem {
101
195
  openedOn?: string;
102
196
  closedOn?: string;
103
197
  archived?: boolean;
198
+ /** Made from a document line (`receipt:<id>:<line>`): once per line. */
199
+ sourceKey?: string;
104
200
  createdAt?: number;
105
201
  updatedAt?: number;
106
202
  }
@@ -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
- * A sale or a purchase as balanced lines (plan 039 §4.6). A sale: debtors debit the gross, revenue
77
- * credit per line, VAT payable credit per code. A purchase the mirror, on creditors and VAT to
78
- * reclaim. A credit note needs nothing of its own: its amounts are already negative.
79
- *
80
- * ponytail: a reverse-charge purchase books no self-assessed VAT yet (both sides of box 4); the
81
- * VAT return in step 7 adds it.
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;