@opencxh/domain 1.302.0 → 1.304.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 (67) hide show
  1. package/dist/entities/agreement/from-quote.d.ts +8 -8
  2. package/dist/entities/agreement/index.d.ts +2 -0
  3. package/dist/entities/agreement/invoice-head.d.ts +21 -0
  4. package/dist/entities/agreement/lifecycle.d.ts +10 -0
  5. package/dist/entities/agreement/period.d.ts +8 -0
  6. package/dist/entities/agreement/prices.d.ts +43 -0
  7. package/dist/entities/agreement/prices.test.d.ts +1 -0
  8. package/dist/entities/agreement/types.d.ts +55 -10
  9. package/dist/entities/bank/match.d.ts +16 -2
  10. package/dist/entities/bank/types.d.ts +12 -0
  11. package/dist/entities/bookkeeping/receipt.d.ts +45 -6
  12. package/dist/entities/bookkeeping/types.d.ts +1 -2
  13. package/dist/entities/business-entity/types.d.ts +11 -15
  14. package/dist/entities/catalog/pricing.d.ts +14 -12
  15. package/dist/entities/catalog/types.d.ts +20 -9
  16. package/dist/entities/company/role.d.ts +14 -0
  17. package/dist/entities/financing/schedule.d.ts +9 -3
  18. package/dist/entities/financing/types.d.ts +17 -6
  19. package/dist/entities/fulfilment/adopt.d.ts +149 -0
  20. package/dist/entities/fulfilment/configuration.d.ts +99 -0
  21. package/dist/entities/fulfilment/configuration.test.d.ts +1 -0
  22. package/dist/entities/fulfilment/customers.d.ts +63 -0
  23. package/dist/entities/fulfilment/execute.d.ts +67 -0
  24. package/dist/entities/fulfilment/fulfilment.test.d.ts +1 -0
  25. package/dist/entities/fulfilment/goods.d.ts +72 -0
  26. package/dist/entities/fulfilment/index.d.ts +9 -0
  27. package/dist/entities/fulfilment/path.d.ts +8 -0
  28. package/dist/entities/fulfilment/path.test.d.ts +1 -0
  29. package/dist/entities/fulfilment/rules.d.ts +124 -0
  30. package/dist/entities/fulfilment/target.d.ts +133 -0
  31. package/dist/entities/fulfilment/types.d.ts +293 -0
  32. package/dist/entities/ledger/entry.d.ts +47 -9
  33. package/dist/entities/ledger/types.d.ts +33 -35
  34. package/dist/entities/ledger/xaf.d.ts +3 -0
  35. package/dist/entities/money/tax.d.ts +6 -0
  36. package/dist/entities/money/totals.d.ts +5 -5
  37. package/dist/entities/purchase/choose.d.ts +62 -0
  38. package/dist/entities/purchase/choose.test.d.ts +1 -0
  39. package/dist/entities/purchase/contract.d.ts +44 -25
  40. package/dist/entities/purchase/demand.d.ts +24 -40
  41. package/dist/entities/purchase/feed.d.ts +53 -5
  42. package/dist/entities/purchase/index.d.ts +2 -0
  43. package/dist/entities/purchase/kinds.d.ts +2 -1
  44. package/dist/entities/purchase/logic.d.ts +32 -7
  45. package/dist/entities/purchase/match.d.ts +48 -6
  46. package/dist/entities/purchase/packing-slip.d.ts +23 -0
  47. package/dist/entities/purchase/types.d.ts +90 -42
  48. package/dist/entities/purchase/upsell.d.ts +20 -0
  49. package/dist/entities/purchase/upsell.test.d.ts +1 -0
  50. package/dist/entities/reports/vat.d.ts +20 -5
  51. package/dist/entities/transaction/booking.d.ts +4 -4
  52. package/dist/entities/transaction/convert.d.ts +10 -10
  53. package/dist/entities/transaction/document.d.ts +8 -3
  54. package/dist/entities/transaction/dunning.d.ts +12 -1
  55. package/dist/entities/transaction/facets.test.d.ts +1 -0
  56. package/dist/entities/transaction/keys.d.ts +3 -0
  57. package/dist/entities/transaction/kinds.d.ts +11 -10
  58. package/dist/entities/transaction/receivable.d.ts +5 -2
  59. package/dist/entities/transaction/settlement.d.ts +7 -7
  60. package/dist/entities/transaction/types.d.ts +128 -68
  61. package/dist/index.cjs +25 -25
  62. package/dist/index.d.ts +2 -1
  63. package/dist/index.js +5300 -4033
  64. package/dist/platform/bookkeeping.d.ts +8 -8
  65. package/dist/platform/provisioning.d.ts +292 -17
  66. package/package.json +1 -1
  67. /package/dist/entities/{purchase/demand.test.d.ts → agreement/invoice-head.test.d.ts} +0 -0
@@ -1,5 +1,6 @@
1
1
  import { Cents } from '../money/money';
2
2
  import { PricePer } from '../money/period-price';
3
+ import { LineDimensions } from '../transaction/types';
3
4
  import { MatchCandidate, MatchedVia } from './packing-slip';
4
5
  import { SupplierOffer } from './types';
5
6
  /** What an invoice line pays for: an order line, or a contract line over a period. */
@@ -41,10 +42,26 @@ export interface InvoiceCandidate extends MatchCandidate {
41
42
  discountPercent?: number;
42
43
  /** Order line: what is still to invoice. Contract line: the agreed count, `null` when counted. */
43
44
  openQty: number | null;
45
+ /**
46
+ * Goods that come to us: received minus invoiced. Absent when no receipt is expected (a service,
47
+ * a licence, a drop shipment): then the order is the measure, as before (Exact: 3-way match).
48
+ */
49
+ receivedOpen?: number;
44
50
  /** A contract line whose invoice decides the amount: nothing expected. */
45
51
  variable?: boolean;
46
52
  use?: "own" | "resale";
47
53
  customerCompanyId?: string;
54
+ /** The order's or contract's; absent = the invoice's own. */
55
+ currency?: string;
56
+ /** A contract row: the days it runs, `to` exclusive. A line is matched to the row of its period. */
57
+ term?: {
58
+ from: number;
59
+ to?: number;
60
+ };
61
+ /** The order line's, carried to the cost it books (plan 045 F 3b). */
62
+ dimensions?: LineDimensions;
63
+ /** The article's group in this administration: its posting rule picks the cost account. */
64
+ ledgerCategoryKey?: string;
48
65
  }
49
66
  export interface MatchAllocation {
50
67
  receiptLineId: string;
@@ -56,10 +73,33 @@ export interface MatchAllocation {
56
73
  expectedCents: Cents | null;
57
74
  /** More than the order line still had open, or another count than the contract's. */
58
75
  overQuantity: boolean;
76
+ /** More than has arrived of goods that come to us: invoiced before it was received. */
77
+ notReceived?: boolean;
59
78
  businessEntityId: string;
60
79
  productId?: string;
61
80
  use?: "own" | "resale";
62
81
  customerCompanyId?: string;
82
+ dimensions?: LineDimensions;
83
+ ledgerCategoryKey?: string;
84
+ /**
85
+ * The invoice in another currency than its target: one invoice unit in the target's. The
86
+ * amount stays as printed (it is what books); the verdict compares `amountCents × rate`.
87
+ */
88
+ rate?: number;
89
+ }
90
+ /** What the invoice is in, and its rate to the euro when it is not (Receipt `fxRate`). */
91
+ export interface InvoiceMoney {
92
+ currency: string;
93
+ /** Euro per one unit of `currency`, as the receipt carries it. */
94
+ fxRate?: number;
95
+ }
96
+ export interface AllocateOptions {
97
+ /** The month a line without a printed period is for (the invoice's). */
98
+ defaultPeriod: {
99
+ from: number;
100
+ to: number;
101
+ };
102
+ invoice?: InvoiceMoney;
63
103
  }
64
104
  /**
65
105
  * Invoice lines onto order and contract lines, many to many. Each line goes to its best candidates
@@ -67,12 +107,10 @@ export interface MatchAllocation {
67
107
  * same article (10 phones over two orders of 6 and 4), the amount split by count. A contract line
68
108
  * takes the line whole, for the period printed on it or else `defaultPeriod` (the invoice's month).
69
109
  */
70
- export declare function allocateInvoice(lines: readonly InvoiceLine[], candidates: readonly InvoiceCandidate[], offers: readonly Pick<SupplierOffer, "id" | "productId" | "vendorSku">[], defaultPeriod: {
71
- from: number;
72
- to: number;
73
- }): {
110
+ export declare function allocateInvoice(lines: readonly InvoiceLine[], candidates: readonly InvoiceCandidate[], offers: readonly Pick<SupplierOffer, "id" | "productId" | "vendorSku">[], { defaultPeriod, invoice }: AllocateOptions): {
74
111
  allocations: MatchAllocation[];
75
112
  unmatched: string[];
113
+ needsRate: string[];
76
114
  };
77
115
  export interface MatchTolerance {
78
116
  percent: number;
@@ -80,7 +118,7 @@ export interface MatchTolerance {
80
118
  }
81
119
  /** One per cent or one euro, whichever is more: a rounding difference is not work. */
82
120
  export declare const DEFAULT_TOLERANCE: MatchTolerance;
83
- export type MatchIssue = "price" | "quantity" | "unmatched";
121
+ export type MatchIssue = "price" | "quantity" | "not_received" | "unmatched" | "fx_rate_required";
84
122
  export interface MatchVerdict {
85
123
  /** Everything matched, at the expected price and count: approve without a person. */
86
124
  ok: boolean;
@@ -95,4 +133,8 @@ export interface MatchVerdict {
95
133
  * Whether an invoice is what was ordered or agreed: every line matched, every count within what
96
134
  * was open, every amount within the tolerance (% and absolute). Anything else is work.
97
135
  */
98
- export declare function matchVerdict(allocations: readonly MatchAllocation[], unmatched: readonly string[], tolerance?: MatchTolerance): MatchVerdict;
136
+ export declare function matchVerdict(allocations: readonly MatchAllocation[], unmatched: readonly string[], { tolerance, needsRate, }?: {
137
+ tolerance?: MatchTolerance;
138
+ /** Lines that would match in another currency: the receipt needs its rate first. */
139
+ needsRate?: readonly string[];
140
+ }): MatchVerdict;
@@ -72,6 +72,29 @@ export interface SlipProposal {
72
72
  })[];
73
73
  problems: SlipProblem[];
74
74
  }
75
+ /** A packing slip as a screen reads it: an upload or a mail attachment, read by the AI. */
76
+ export interface PackingSlipView {
77
+ id: string;
78
+ source: "upload" | "mail";
79
+ filename: string;
80
+ mimeType: string;
81
+ mail?: {
82
+ interactionId: string;
83
+ activityId: string;
84
+ attachmentId: string;
85
+ from?: string;
86
+ };
87
+ /** The original in the packing-slip folder. */
88
+ file?: {
89
+ mountId: string;
90
+ fileId: string;
91
+ };
92
+ status: "extracting" | "needs_browser" | "to_check" | "booked" | "dismissed";
93
+ content?: PackingSlipContent;
94
+ purchaseOrderId?: string;
95
+ extractError?: string;
96
+ createdAt?: number;
97
+ }
75
98
  /**
76
99
  * A packing slip as a receipt to book: the matched lines with their counts and serials, and what a
77
100
  * person has to decide — no order, an article not on it, more than is open, serials that do not
@@ -1,9 +1,27 @@
1
- import { ProvisioningAction, ProvisioningState } from '../../platform/provisioning';
1
+ import { ProvisioningAction, ProvisioningAnswers, ProvisioningState } from '../../platform/provisioning';
2
2
  import { LineRhythm } from '../agreement/types';
3
3
  import { Cents } from '../money/money';
4
4
  import { PricePer } from '../money/period-price';
5
5
  import { PostalAddress } from '../organization/types';
6
6
  import { Transaction, TransactionLine } from '../transaction/types';
7
+ /** How an offer is ordered through an app, and what of that app's assortment it stands for. */
8
+ export interface OfferProvider {
9
+ app: string;
10
+ offer: string;
11
+ /**
12
+ * The article as a filter (plan 053): fixed answers in this provider's own words
13
+ * (`technology: fiber`, `min_download: 1000`). They go to `prepare` with every other answer.
14
+ */
15
+ answers?: ProvisioningAnswers;
16
+ /**
17
+ * An upsell article: which of the provider's options it sells — by answer key (`evc_*` is every
18
+ * circuit) and option key or label (`24x7`); no `match` = every option of that answer.
19
+ */
20
+ option?: {
21
+ answer: string;
22
+ match?: string;
23
+ };
24
+ }
7
25
  /**
8
26
  * What a supplier asks for a product: one row per supplier and product (and validity window).
9
27
  * The default offer is the product's cost price; the rest are the alternatives a buyer compares.
@@ -11,7 +29,7 @@ import { Transaction, TransactionLine } from '../transaction/types';
11
29
  export interface SupplierOffer {
12
30
  id: string;
13
31
  organizationId: string;
14
- businessEntityId?: string;
32
+ businessEntityId: string;
15
33
  supplierCompanyId: string;
16
34
  productId: string;
17
35
  /** Their article number. */
@@ -39,22 +57,23 @@ export interface SupplierOffer {
39
57
  validFrom?: number;
40
58
  validTo?: number;
41
59
  minQty?: number;
60
+ /** Ordered in multiples of this many purchase units (Exact `PurchaseLotSize`); `minQty` is no multiple. */
61
+ lotSize?: number;
62
+ /** Ships straight to a customer: never, only, or both (Exact `DropShipment`). Absent = both. */
63
+ dropship?: "no" | "yes" | "optional";
64
+ /**
65
+ * `quote`: no fixed price — the provider prices each order (a connection per address, B.6b);
66
+ * `costCents` is then 0 and never the product's cost. Absent = `list`.
67
+ */
68
+ pricedBy?: "list" | "quote";
42
69
  leadDays?: number;
43
70
  /** Days from each term's start the supplier still takes a change (NCE: 7). */
44
71
  changeWindowDays?: number;
45
- /** Ordered through an app instead of by mail (fase 9). */
46
- provider?: {
47
- app: string;
48
- offer: string;
49
- };
50
72
  /**
51
- * Where it lands when that is not the supplier (fase 9b): a Telnyx number on the customer's
52
- * Eylo account. The platform's own offer takes it in, after the purchase.
73
+ * Ordered through an app instead of by mail (fase 9). Where it lands is not the offer's to say:
74
+ * the provider of that platform declares what it needs (047 v2 §2.3).
53
75
  */
54
- deliverTo?: {
55
- app: string;
56
- offer: string;
57
- };
76
+ provider?: OfferProvider;
58
77
  /** The supplier's advised price (the Microsoft ERP price), from a feed (plan 045 G). */
59
78
  listCents?: Cents;
60
79
  /** The last known stock; only the latest, no history (plan 045 B.1). */
@@ -73,6 +92,8 @@ export interface OfferAvailability {
73
92
  at: number;
74
93
  source: "feed" | "api" | "manual";
75
94
  leadDays?: number;
95
+ /** Until when the choice trusts it: a feed's rhythm × 2, a hand-set stand a day. */
96
+ freshUntil?: number;
76
97
  }
77
98
  /** Ours to keep, or sold on to a customer — decides where the cost books (fase 6). */
78
99
  export type PurchaseUse = "own" | "resale";
@@ -83,35 +104,43 @@ export interface PurchaseLine extends TransactionLine {
83
104
  /** Matched to supplier invoices (fase 7). */
84
105
  billedQty: number;
85
106
  use: PurchaseUse;
86
- dimensions?: {
87
- costCenterId?: string;
88
- projectId?: string;
107
+ /** What the supplier confirmed (Ingram `quantity_confirmed`, `quantity_back_ordered`). */
108
+ confirmation?: {
109
+ quantity: number;
110
+ backorder?: number;
111
+ promisedAt?: number;
89
112
  };
90
- /** Absent = `purchase`. A change or an end points at the contract line it is about. */
91
- action?: ProvisioningAction;
92
- contractLine?: {
93
- contractId: string;
94
- lineId: string;
95
- };
96
- /** A change: the count it should be after. */
97
- targetQuantity?: number;
98
- /** When a change or end takes effect (epoch ms): the end of the term, not today. */
99
- effectiveAt?: number;
100
- /** Where this line came from in "Te bestellen", so it is not proposed twice. */
101
- demandKey?: string;
102
- /** The line of this order it waits for (the customer's account before its numbers). */
103
- waitsFor?: string;
104
- /** The supplier's ordering fields (a domain, a region). */
105
- params?: Record<string, string>;
106
- provisioning?: LineProvisioning;
107
- /** The second step when the offer lands elsewhere (`deliverTo`): after a buy, before an end. */
108
- delivery?: LineProvisioning;
113
+ /** The supplier's order for this line: one PO can become several (Ingram `order_split`). */
114
+ supplierRef?: string;
115
+ /**
116
+ * The fulfilment request this line pays for: fulfilment carries it out once the PO is sent. What
117
+ * the act is (a buy, a change, an end) is the request's; the line is what it costs.
118
+ */
119
+ fulfilmentRequestId?: string;
120
+ /**
121
+ * Server-set with the request: a change or an end on a running subscription. Absent = a buy. An
122
+ * act at no cost expects no invoice, so a PO of ends can be invoiced in full (review 6).
123
+ */
124
+ act?: Exclude<ProvisioningAction, "purchase">;
125
+ /**
126
+ * Goods: which of fulfilment's allocations this line buys for, in base units (047 §4). Server-
127
+ * set; on a draft the line's quantity is their sum, so two needs share one line only by merging.
128
+ */
129
+ allocations?: {
130
+ id: string;
131
+ quantity: number;
132
+ }[];
133
+ /**
134
+ * Never stored: on a read the server adds how the request this line pays for stands at
135
+ * fulfilment, so the order page can show it. The supplier's fields live on the request.
136
+ */
137
+ request?: LineRequest;
109
138
  }
110
139
  /**
111
- * A line's request at the supplier. `waiting`: its account is not there yet. `manual`: no API —
112
- * a person does it and fills in the result.
140
+ * How a line's request stands, as the order page shows it. `waiting`: not out yet (its account,
141
+ * its day). `manual`: no API — a person does it and fills in the result.
113
142
  */
114
- export interface LineProvisioning {
143
+ export interface LineRequest {
115
144
  state: ProvisioningState | "waiting" | "manual";
116
145
  requestId: string;
117
146
  app?: string;
@@ -121,6 +150,8 @@ export interface LineProvisioning {
121
150
  key: string;
122
151
  reason: string;
123
152
  }[];
153
+ /** The supplier's ordering fields as the request holds them. */
154
+ params?: Record<string, string>;
124
155
  error?: string;
125
156
  attempts?: number;
126
157
  nextAttemptAt?: number;
@@ -134,18 +165,28 @@ export interface DeliverTo {
134
165
  /** Frozen when the order is sent: what the supplier was told, whatever crm says later. */
135
166
  address?: PostalAddress;
136
167
  }
137
- /** A purchase order: a `Transaction` of kind `purchase_order`; `companyId` is the supplier. */
138
- export interface PurchaseOrder extends Omit<Transaction, "lines"> {
139
- lines: PurchaseLine[];
168
+ /** What a purchase order goes through. Received is its own axis on the lines (`receivedQty`). */
169
+ export type PurchaseStatus = "draft" | "sent" | "confirmed" | "received" | "cancelled" | "superseded";
170
+ /** A purchase order: a {@link Transaction} of kind `purchase_order`; `companyId` is the supplier. */
171
+ export interface PurchaseOrder extends Transaction<PurchaseLine, PurchaseStatus> {
140
172
  deliverTo: DeliverTo;
141
173
  /** Bought for this customer (the M365 seats for Acme). */
142
174
  customerCompanyId?: string;
175
+ /** `full` once every line that expects an invoice is billed: off the match and allocate scans. */
176
+ invoiceState?: InvoiceState;
143
177
  }
178
+ export type InvoiceState = "open" | "full";
144
179
  export interface GoodsReceiptLine {
145
180
  poLineId: string;
146
181
  /** Purchase units; positive, the movement says the direction. */
147
182
  quantity: number;
148
183
  serials: string[];
184
+ /** For a batch-tracked article (046 `tracking: "batch"`): what came in per batch. */
185
+ batches?: {
186
+ code: string;
187
+ quantity: number;
188
+ expiresAt?: number;
189
+ }[];
149
190
  /** Still to come, as the packing slip says. */
150
191
  backorderQty?: number;
151
192
  }
@@ -153,7 +194,7 @@ export interface GoodsReceiptLine {
153
194
  export interface GoodsReceipt {
154
195
  id: string;
155
196
  organizationId: string;
156
- businessEntityId?: string;
197
+ businessEntityId: string;
157
198
  movement: "in" | "return";
158
199
  purchaseOrderId: string;
159
200
  supplierCompanyId: string;
@@ -164,5 +205,12 @@ export interface GoodsReceipt {
164
205
  supplierRef?: string;
165
206
  status: "draft" | "booked";
166
207
  lines: GoodsReceiptLine[];
208
+ /** Flat and indexed: `<productId>#<serial>` of every line, for the register across orders. */
209
+ serialKeys?: string[];
167
210
  createdAt?: number;
168
211
  }
212
+ /** Per administration: the storage folder packing slips are kept in. */
213
+ export interface PurchaseSettings {
214
+ businessEntityId: string;
215
+ slipFolderId?: string;
216
+ }
@@ -0,0 +1,20 @@
1
+ import { OfferProvider, SupplierOffer } from './types';
2
+ /** One option a configuration took, as its provider named it. */
3
+ export interface TakenOption {
4
+ /** The answer it comes from: `evc_2`, `sla`, `cpe`. */
5
+ answer: string;
6
+ /** The provider's own option key (`608`) and label (`24x7`). */
7
+ key: string;
8
+ label: string;
9
+ }
10
+ /**
11
+ * Whether an upsell article's rule covers an option (plan 053): the answer by name, or every
12
+ * numbered one with a trailing `*` (`evc_*`); then the option by key or label, or any.
13
+ */
14
+ export declare function sellsOption(rule: OfferProvider["option"], option: TakenOption): boolean;
15
+ /**
16
+ * The upsell article that sells this option at this provider, or none: then a colleague is told
17
+ * "no sales article linked" and nothing is sold without a price. The exact match wins over a
18
+ * catch-all (`SLA 24x7` over a rule for every SLA).
19
+ */
20
+ export declare function upsellOfferFor(offers: readonly SupplierOffer[], app: string, option: TakenOption): SupplierOffer | undefined;
@@ -0,0 +1 @@
1
+ export {};
@@ -1,18 +1,21 @@
1
1
  import { Cents } from '../money/money';
2
- import { TaxCode } from '../money/tax';
2
+ import { IcpKind, TaxCode } from '../money/tax';
3
3
  /**
4
4
  * The VAT return, computed from posted lines (plan 039 datamodel §7): every line with a VAT code
5
5
  * and a tax point in the period adds its base and VAT to the boxes of that code. Pure; the server
6
6
  * turns journal lines into `VatLine`s.
7
7
  */
8
8
  export interface VatLine {
9
- /** A line on a revenue account is a sale, anything else a purchase. */
9
+ /** The line's `taxSide`; guessed from its account only for a line booked before that existed. */
10
10
  side: "sales" | "purchase";
11
11
  taxCode: string;
12
12
  /** Positive for a sale or a purchase, negative for its credit note. */
13
13
  baseCents: Cents;
14
14
  taxCents: Cents;
15
+ /** When the VAT arose: the rate is the one of this day. */
15
16
  taxPointDate: string;
17
+ /** The return it counts in, when that differs from the tax point's (`JournalLine.vatDate`). */
18
+ vatDate?: string;
16
19
  counterpartyCompanyId?: string;
17
20
  /** The journal line it came from: the drill-down from a box back to the bookings. */
18
21
  ref?: string;
@@ -31,9 +34,11 @@ export interface VatReturnFigures {
31
34
  tax?: number;
32
35
  }>>;
33
36
  /** EC sales list: per customer, the base of the reverse-charged EU sales. */
34
- /** `name` and `vatNumber` are filled in from crm when the return is shown, never stored. */
37
+ /** `name` and `vatNumber` come from the entries' frozen party; a filed return stores them. */
38
+ /** Per customer and column (goods, services, triangular): the list reports them apart. */
35
39
  icp: {
36
40
  counterpartyCompanyId?: string;
41
+ kind: IcpKind;
37
42
  baseCents: Cents;
38
43
  name?: string;
39
44
  vatNumber?: string;
@@ -45,8 +50,9 @@ export interface VatReturnFigures {
45
50
  }
46
51
  /**
47
52
  * The boxes for one period. Reverse charge on purchase (`selfAssessed`) is owed in its own box
48
- * and deducted in 5b at the standard rate of its tax point; input VAT counts at the code's
49
- * deductible share. 5a is what is owed (1a–4b), 5b what is deducted, 5c and 5g the balance.
53
+ * and deducted in 5b at the standard rate of its tax point; input VAT counts as booked (its
54
+ * non-deductible share is already cost, `documentLinesOf`). 5a is what is owed (1a–4b), 5b what
55
+ * is deducted, 5c and 5g the balance.
50
56
  */
51
57
  export declare function vatReturn(input: {
52
58
  lines: readonly VatLine[];
@@ -66,3 +72,12 @@ export declare function vatPeriods(year: number, cadence?: "quarter" | "month" |
66
72
  to: string;
67
73
  dueOn: string;
68
74
  }[];
75
+ /**
76
+ * The day a VAT line counts on (Exact's reporting period apart from the entry date): its tax
77
+ * point, or the first day after the last return filed before it was posted, when that is later.
78
+ * A filed return locks no period; what arrives late counts in the next one.
79
+ */
80
+ export declare function vatDateOf(taxPointDate: string, postedAt: number, filed: readonly {
81
+ to: string;
82
+ filedAt: number;
83
+ }[]): string;
@@ -1,14 +1,14 @@
1
1
  import { BookingDocument } from '../../platform/bookkeeping';
2
- import { Company } from '../company/types';
3
- import { Transaction } from './types';
2
+ import { SalesTransaction } from './types';
4
3
  /** The kinds that reach the books. A quote or an order books nothing. */
5
4
  export declare const BOOKABLE_KINDS: readonly ["invoice", "credit_note"];
6
- export declare function isBookable(tx: Pick<Transaction, "kindKey" | "issuedAt">): boolean;
5
+ export declare function isBookable(tx: Pick<SalesTransaction, "kindKey" | "issuedAt">): boolean;
7
6
  /**
8
7
  * An issued invoice or credit note as the books need it (plan 039 datamodel §3.2). Pure: sales
9
8
  * owns the document, and this is the only shape of it that leaves the app.
10
9
  *
11
10
  * Headings and options carry no amount, so they are not lines here. A reverse charge or
12
11
  * exemption ticked on the document replaces every line's code, as it did when it was priced.
12
+ * The party is the one frozen at issue (review R2): no crm read, so nothing live in the hash.
13
13
  */
14
- export declare function bookingDocumentOf(tx: Transaction, company: Pick<Company, "id" | "name" | "vatNumber" | "kvkNumber"> | null, sourceApp?: string): BookingDocument;
14
+ export declare function bookingDocumentOf(tx: SalesTransaction, sourceApp?: string): BookingDocument;
@@ -1,6 +1,6 @@
1
- import { Transaction } from './types';
1
+ import { SalesTransaction } from './types';
2
2
  /** A transaction with `issuedAt` is frozen. One question, one answer, one place. */
3
- export declare function isFrozen(transaction: Pick<Transaction, "issuedAt">): boolean;
3
+ export declare function isFrozen(transaction: Pick<SalesTransaction, "issuedAt">): boolean;
4
4
  /**
5
5
  * What a frozen transaction still accepts.
6
6
  *
@@ -12,30 +12,30 @@ export declare function isFrozen(transaction: Pick<Transaction, "issuedAt">): bo
12
12
  *
13
13
  * Everything else is a `BadRequestError`. Whoever wants to change a sent quote gets v2.
14
14
  */
15
- export declare const FROZEN_WRITABLE_FIELDS: readonly (keyof Transaction)[];
15
+ export declare const FROZEN_WRITABLE_FIELDS: readonly (keyof SalesTransaction)[];
16
16
  /**
17
17
  * Issued, numbered kind, no number yet: the books number it and have not answered ("wordt
18
18
  * uitgegeven"). It cannot be sent to the customer until the number is on the paper.
19
19
  */
20
- export declare function awaitsNumber(tx: Pick<Transaction, "issuedAt" | "kindKey" | "number">): boolean;
20
+ export declare function awaitsNumber(tx: Pick<SalesTransaction, "issuedAt" | "kindKey" | "number">): boolean;
21
21
  /**
22
22
  * What a save may set on a draft. `status` is not here: it moves through `issue` and the status
23
23
  * route, which check the transition and announce it.
24
24
  */
25
- export declare const DRAFT_FIELDS: readonly ["companyId", "contactId", "dealItemId", "title", "reference", "currency", "taxOverrideKey", "introText", "termsText", "expiresAt", "deliveredAt", "periodFrom", "periodTo", "paymentTerm", "billingAddressId", "lines"];
25
+ export declare const DRAFT_FIELDS: readonly ["companyId", "contactId", "dealItemId", "title", "reference", "currency", "taxOverrideKey", "introText", "termsText", "expiresAt", "deliveredAt", "periodFrom", "periodTo", "paymentTerm", "billingAddressId", "deliveryAddressId", "invoiceTo", "lines"];
26
26
  /**
27
27
  * The draft fields a save may empty, named in `clear`. A list and not `null` in the body:
28
28
  * `invoke` strips `null`, so "empty this" never reaches the server any other way.
29
29
  */
30
- export declare const CLEARABLE_FIELDS: readonly ["contactId", "dealItemId", "title", "reference", "taxOverrideKey", "introText", "termsText", "expiresAt", "deliveredAt", "periodFrom", "periodTo", "billingAddressId"];
30
+ export declare const CLEARABLE_FIELDS: readonly ["contactId", "dealItemId", "title", "reference", "taxOverrideKey", "introText", "termsText", "expiresAt", "deliveredAt", "periodFrom", "periodTo", "billingAddressId", "deliveryAddressId", "invoiceTo"];
31
31
  export type ClearableField = (typeof CLEARABLE_FIELDS)[number];
32
32
  /** The fields a draft carries over; the server owns id, organisation and author. */
33
- export type TransactionDraft = Omit<Transaction, "id" | "organizationId" | "createdBy" | "createdAt" | "updatedAt">;
33
+ export type TransactionDraft = Omit<SalesTransaction, "id" | "organizationId" | "createdBy" | "createdAt" | "updatedAt">;
34
34
  /**
35
35
  * Whether `source` may become a `kindKey`: offered, a step the source's kind allows, and for a
36
36
  * correcting kind (a credit note) only from the issued document it corrects.
37
37
  */
38
- export declare function canConvert(source: Pick<Transaction, "kindKey" | "issuedAt">, kindKey: string): boolean;
38
+ export declare function canConvert(source: Pick<SalesTransaction, "kindKey" | "issuedAt">, kindKey: string): boolean;
39
39
  /**
40
40
  * The conversion: quote -> order -> invoice -> credit note, as one function.
41
41
  *
@@ -47,7 +47,7 @@ export declare function canConvert(source: Pick<Transaction, "kindKey" | "issued
47
47
  * What is deliberately dropped: the number, the issue date, the due date, the file, the external
48
48
  * ids and the dunning history. Those belong to the document that went out, and this is a new one.
49
49
  */
50
- export declare function copyFrom(source: Transaction, kindKey: string): TransactionDraft;
50
+ export declare function copyFrom(source: SalesTransaction, kindKey: string): TransactionDraft;
51
51
  /**
52
52
  * The next version of the same quote.
53
53
  *
@@ -56,4 +56,4 @@ export declare function copyFrom(source: Transaction, kindKey: string): Transact
56
56
  * afterwards: without this rule you have, a year from now, a quote that was changed after it was
57
57
  * signed and no way left to see that.
58
58
  */
59
- export declare function nextVersion(source: Transaction): TransactionDraft;
59
+ export declare function nextVersion(source: SalesTransaction): TransactionDraft;
@@ -4,7 +4,7 @@ import { TemplateCondition } from '../document-template/types';
4
4
  import { PricePer } from '../money/period-price';
5
5
  import { TaxRate } from '../money/tax';
6
6
  import { PostalAddress } from '../organization/types';
7
- import { Transaction } from './types';
7
+ import { SalesTransaction } from './types';
8
8
  /**
9
9
  * The seam between a transaction and the paper it is printed on.
10
10
  *
@@ -22,6 +22,9 @@ export interface TransactionContextSources {
22
22
  /** The customer's postal address, for the recipient block. */
23
23
  companyAddress?: PostalAddress;
24
24
  contactName?: string;
25
+ /** The debtor's VAT number and debtor number, from the snapshot frozen at issue (review R2). */
26
+ companyVatNumber?: string;
27
+ companyNumber?: string;
25
28
  organizationName?: string;
26
29
  /** Who the recipient reads; drives which template row is picked. */
27
30
  locale?: string;
@@ -39,7 +42,7 @@ export interface TransactionContextSources {
39
42
  * quote would be one typo away from the customer. The shield on the way out of the server
40
43
  * (`transactionView`) is the second lock; this one is the first.
41
44
  */
42
- export declare function transactionContext(transaction: Transaction, sources?: TransactionContextSources): TemplateContext;
45
+ export declare function transactionContext(transaction: SalesTransaction, sources?: TransactionContextSources): TemplateContext;
43
46
  /**
44
47
  * The blocks a template cannot write itself: the line table and the totals.
45
48
  *
@@ -50,13 +53,15 @@ export declare function transactionContext(transaction: Transaction, sources?: T
50
53
  * Amounts arrive already formatted. The blocks layer never computes; that is the whole reason a
51
54
  * totals row carries a string.
52
55
  */
53
- export declare function transactionSlots(transaction: Transaction, rates?: readonly TaxRate[], labels?: SlotLabels): DocumentSlots;
56
+ export declare function transactionSlots(transaction: SalesTransaction, rates?: readonly TaxRate[], labels?: SlotLabels): DocumentSlots;
54
57
  export interface SlotLabels {
55
58
  description: string;
56
59
  /** Marks a line that is priced but not counted. */
57
60
  optional: string;
58
61
  quantity: string;
59
62
  unitPrice: string;
63
+ /** The line discount column, shown only when a line has one. */
64
+ discount: string;
60
65
  /** After a period price: "222,00 per jaar". */
61
66
  per: Record<PricePer, string>;
62
67
  /** A meter's display unit after a rated quantity: `minute` → "min". Unknown = the key. */
@@ -1,5 +1,7 @@
1
1
  import { Cents } from '../money/money';
2
2
  import { ReceivableRow } from './receivable';
3
+ /** ponytail: one fixed grace for every administration; a policy field when someone asks. */
4
+ export declare const UNCOLLECTED_AFTER_DAYS = 7;
3
5
  /** One step of the schedule. Level 1 counts from the due day, every later one from the last sent. */
4
6
  export interface DunningStep {
5
7
  level: number;
@@ -30,8 +32,17 @@ export interface DunningDue {
30
32
  * every step went out (a person takes over), or `null` when nothing is due yet.
31
33
  */
32
34
  export declare function dunningStateOf(row: ReceivableRow, policy: DunningPolicy, today: string): DunningDue | "done" | null;
33
- /** The whole run: what is due now per step, and what waits for a person. */
35
+ /**
36
+ * A debit, pin or cash invoice still open `UNCOLLECTED_AFTER_DAYS` past its due day: nobody
37
+ * collects it (no direct-debit run exists yet) and nobody may remind, so a person looks.
38
+ */
39
+ export declare const isUncollected: (row: ReceivableRow, today: string) => boolean;
40
+ /**
41
+ * The whole run: what is due now per step, what waits for a person after the last step, and the
42
+ * collected-by-us invoices that never came in.
43
+ */
34
44
  export declare function dunningRunOf(rows: readonly ReceivableRow[], policy: DunningPolicy, today: string): {
35
45
  due: DunningDue[];
36
46
  waiting: ReceivableRow[];
47
+ uncollected: ReceivableRow[];
37
48
  };
@@ -0,0 +1 @@
1
+ export {};
@@ -46,4 +46,7 @@ export declare function transactionDossierKeys(transaction: {
46
46
  companyId?: string;
47
47
  contactId?: string;
48
48
  dealItemId?: string;
49
+ invoiceTo?: {
50
+ companyId: string;
51
+ };
49
52
  }): string[];