@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 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { ProvisionableOffer, ProvisioningAction } from '../../platform/provisioning';
|
|
2
|
+
import { Agreement } from '../agreement/types';
|
|
3
|
+
import { PurchaseAgreement } from './contract';
|
|
4
|
+
import { SupplierOffer } from './types';
|
|
5
|
+
/** One thing to order, change or end, as "Te bestellen" lists it. */
|
|
6
|
+
export interface DemandItem {
|
|
7
|
+
/** Stable: the sales series, or the customer's account at the supplier. */
|
|
8
|
+
key: string;
|
|
9
|
+
action: ProvisioningAction;
|
|
10
|
+
level: "account" | "item";
|
|
11
|
+
supplierCompanyId: string;
|
|
12
|
+
offerId: string;
|
|
13
|
+
productId: string;
|
|
14
|
+
name: string;
|
|
15
|
+
/** Purchase: how many. Change: by how many. End: the current count. */
|
|
16
|
+
quantity: number;
|
|
17
|
+
/** A change: the count it should be after, and the count it is now. */
|
|
18
|
+
targetQuantity?: number;
|
|
19
|
+
from?: number;
|
|
20
|
+
customerCompanyId: string;
|
|
21
|
+
salesLine?: {
|
|
22
|
+
agreementId: string;
|
|
23
|
+
seriesId: string;
|
|
24
|
+
};
|
|
25
|
+
contractLine?: {
|
|
26
|
+
contractId: string;
|
|
27
|
+
lineId: string;
|
|
28
|
+
};
|
|
29
|
+
/** Epoch ms it takes effect: the term's end for an end or a reduction outside the window. */
|
|
30
|
+
effectiveAt?: number;
|
|
31
|
+
/** The last day to send it for `effectiveAt`; past it, `effectiveAt` is the next term's end. */
|
|
32
|
+
deadline?: number;
|
|
33
|
+
/** A reduction outside the change window: not renewing the difference, not lowering now. */
|
|
34
|
+
atRenewal?: boolean;
|
|
35
|
+
/** The notice was too late for this term: it ends at the next one. */
|
|
36
|
+
nextTerm?: boolean;
|
|
37
|
+
/** The purchase runs on after the sale ends: we pay until then. */
|
|
38
|
+
runsOnUntil?: number;
|
|
39
|
+
/** The account item this waits for. */
|
|
40
|
+
waitsFor?: string;
|
|
41
|
+
/** It lands on another platform than the supplier's (a Telnyx number on Eylo). */
|
|
42
|
+
deliverTo?: {
|
|
43
|
+
app: string;
|
|
44
|
+
offer: string;
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
export interface DemandInput {
|
|
48
|
+
agreements: readonly Agreement[];
|
|
49
|
+
contracts: readonly PurchaseAgreement[];
|
|
50
|
+
offers: readonly SupplierOffer[];
|
|
51
|
+
/** Provider describes, by `app:offer`. */
|
|
52
|
+
provisionable: ReadonlyMap<string, ProvisionableOffer>;
|
|
53
|
+
/** Keys already on an order that has not settled: never proposed twice. */
|
|
54
|
+
open: ReadonlySet<string>;
|
|
55
|
+
now: number;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* What to order, change or end so purchase follows sales (fase 9). Per sales series with a fixed
|
|
59
|
+
* count and a default offer:
|
|
60
|
+
* - no contract line passes it on: buy the count;
|
|
61
|
+
* - the contract has fewer: one change to the new count;
|
|
62
|
+
* - the contract has more: lower it now inside the supplier's change window, else do not renew
|
|
63
|
+
* the difference at the term's end;
|
|
64
|
+
* - the sale has an end: end the purchase at the first term end the notice still reaches, with
|
|
65
|
+
* the deadline to send it by, and a signal when we pay on after the customer stopped.
|
|
66
|
+
* An item that needs the customer's account at the supplier gets that account first.
|
|
67
|
+
*/
|
|
68
|
+
export declare function demandFor(input: DemandInput): DemandItem[];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { TransactionKind } from '../transaction/kinds';
|
|
2
|
+
/**
|
|
3
|
+
* What `apps/purchase` issues: the same engine as a quote, its own table. `sent` is issued (a
|
|
4
|
+
* number and the paper); the supplier confirms, and the goods coming in make it `received`.
|
|
5
|
+
* Walking back one step stays allowed — a return reopens a received order.
|
|
6
|
+
*/
|
|
7
|
+
export declare const PURCHASE_KINDS: readonly TransactionKind[];
|
|
8
|
+
export declare const PURCHASE_ORDER_KIND = "purchase_order";
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { PricePer } from '../money/period-price';
|
|
2
|
+
import { GoodsReceipt, GoodsReceiptLine, PurchaseLine, SupplierOffer } from './types';
|
|
3
|
+
/** What came in for one order line: booked receipts in, returns out. */
|
|
4
|
+
export declare function receivedFor(poLineId: string, receipts: readonly GoodsReceipt[]): number;
|
|
5
|
+
export type ReceiptState = "none" | "partial" | "full";
|
|
6
|
+
/** Over the product lines of an order. */
|
|
7
|
+
export declare function receiptState(lines: readonly Pick<PurchaseLine, "kind" | "quantity" | "receivedQty">[]): "none" | "partial" | "full";
|
|
8
|
+
export interface GoodsReceiptProblem {
|
|
9
|
+
code: "receipt_over_ordered" | "receipt_return_over_received" | "receipt_serials_count";
|
|
10
|
+
poLineId: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Why a receipt cannot be booked: more in than still open, more back than came in, or serial
|
|
14
|
+
* numbers that do not match the count. No serials is fine (bulk goods); some serials must be all.
|
|
15
|
+
*/
|
|
16
|
+
export declare function goodsReceiptProblems(receipt: Pick<GoodsReceipt, "movement" | "lines">, poLines: readonly Pick<PurchaseLine, "id" | "quantity">[], booked: readonly GoodsReceipt[]): GoodsReceiptProblem[];
|
|
17
|
+
/** Lines of a receipt worth keeping: a positive quantity, serials trimmed and unique. */
|
|
18
|
+
export declare function cleanReceiptLines(lines: readonly GoodsReceiptLine[]): GoodsReceiptLine[];
|
|
19
|
+
/** Valid on `at`. */
|
|
20
|
+
export declare function offerValid(offer: Pick<SupplierOffer, "validFrom" | "validTo">, at: number): boolean;
|
|
21
|
+
/** The offer that prices the product: the marked default if valid, else the first valid one. */
|
|
22
|
+
export declare function defaultOffer<T extends SupplierOffer>(offers: readonly T[], at: number): T | null;
|
|
23
|
+
/** Purchase units → product units: two rolls of 500 m are 1,000 m. */
|
|
24
|
+
export declare function toProductUnits(offer: Pick<SupplierOffer, "unitsPerPurchaseUnit">, qty: number): number;
|
|
25
|
+
/**
|
|
26
|
+
* The offer's cost per product unit, as amount, currency and period — what is written back to the
|
|
27
|
+
* product. Never a monthly equivalent: € 222 per year stays € 222 per year.
|
|
28
|
+
*/
|
|
29
|
+
export declare function productCost(offer: SupplierOffer): {
|
|
30
|
+
cents: number;
|
|
31
|
+
currency: string;
|
|
32
|
+
per?: PricePer;
|
|
33
|
+
};
|
|
34
|
+
/** Offers side by side: each over one year, per product unit. A one-off stays itself. */
|
|
35
|
+
export declare function costOverYear(offer: SupplierOffer): number;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { Cents } from '../money/money';
|
|
2
|
+
import { PricePer } from '../money/period-price';
|
|
3
|
+
import { MatchCandidate, MatchedVia } from './packing-slip';
|
|
4
|
+
import { SupplierOffer } from './types';
|
|
5
|
+
/** What an invoice line pays for: an order line, or a contract line over a period. */
|
|
6
|
+
export type InvoiceTarget = {
|
|
7
|
+
kind: "po_line";
|
|
8
|
+
orderId: string;
|
|
9
|
+
lineId: string;
|
|
10
|
+
} | {
|
|
11
|
+
kind: "contract_line";
|
|
12
|
+
contractId: string;
|
|
13
|
+
lineId: string;
|
|
14
|
+
period: {
|
|
15
|
+
from: number;
|
|
16
|
+
to: number;
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
/** A supplier invoice line as the match reads it — `ReceiptLine`, narrowed. */
|
|
20
|
+
export interface InvoiceLine {
|
|
21
|
+
id: string;
|
|
22
|
+
description?: string;
|
|
23
|
+
sku?: string;
|
|
24
|
+
externalRef?: string;
|
|
25
|
+
quantity?: number;
|
|
26
|
+
netCents: Cents;
|
|
27
|
+
/** As printed: `to` inclusive. */
|
|
28
|
+
period?: {
|
|
29
|
+
from: string;
|
|
30
|
+
to: string;
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
/** An open order line or a running contract line of this supplier, with what it expects. */
|
|
34
|
+
export interface InvoiceCandidate extends MatchCandidate {
|
|
35
|
+
kind: "po_line" | "contract_line";
|
|
36
|
+
/** The order or contract. */
|
|
37
|
+
parentId: string;
|
|
38
|
+
businessEntityId: string;
|
|
39
|
+
unitPriceCents: Cents;
|
|
40
|
+
pricePer?: PricePer;
|
|
41
|
+
discountPercent?: number;
|
|
42
|
+
/** Order line: what is still to invoice. Contract line: the agreed count, `null` when counted. */
|
|
43
|
+
openQty: number | null;
|
|
44
|
+
/** A contract line whose invoice decides the amount: nothing expected. */
|
|
45
|
+
variable?: boolean;
|
|
46
|
+
use?: "own" | "resale";
|
|
47
|
+
customerCompanyId?: string;
|
|
48
|
+
}
|
|
49
|
+
export interface MatchAllocation {
|
|
50
|
+
receiptLineId: string;
|
|
51
|
+
target: InvoiceTarget;
|
|
52
|
+
via: MatchedVia;
|
|
53
|
+
quantity: number;
|
|
54
|
+
amountCents: Cents;
|
|
55
|
+
/** What this part should have cost; `null` for a variable line. */
|
|
56
|
+
expectedCents: Cents | null;
|
|
57
|
+
/** More than the order line still had open, or another count than the contract's. */
|
|
58
|
+
overQuantity: boolean;
|
|
59
|
+
businessEntityId: string;
|
|
60
|
+
productId?: string;
|
|
61
|
+
use?: "own" | "resale";
|
|
62
|
+
customerCompanyId?: string;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Invoice lines onto order and contract lines, many to many. Each line goes to its best candidates
|
|
66
|
+
* ({@link lineAffinity}); a count larger than one order line has open runs on to the next of the
|
|
67
|
+
* same article (10 phones over two orders of 6 and 4), the amount split by count. A contract line
|
|
68
|
+
* takes the line whole, for the period printed on it or else `defaultPeriod` (the invoice's month).
|
|
69
|
+
*/
|
|
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
|
+
}): {
|
|
74
|
+
allocations: MatchAllocation[];
|
|
75
|
+
unmatched: string[];
|
|
76
|
+
};
|
|
77
|
+
export interface MatchTolerance {
|
|
78
|
+
percent: number;
|
|
79
|
+
cents: Cents;
|
|
80
|
+
}
|
|
81
|
+
/** One per cent or one euro, whichever is more: a rounding difference is not work. */
|
|
82
|
+
export declare const DEFAULT_TOLERANCE: MatchTolerance;
|
|
83
|
+
export type MatchIssue = "price" | "quantity" | "unmatched";
|
|
84
|
+
export interface MatchVerdict {
|
|
85
|
+
/** Everything matched, at the expected price and count: approve without a person. */
|
|
86
|
+
ok: boolean;
|
|
87
|
+
issues: {
|
|
88
|
+
receiptLineId: string;
|
|
89
|
+
issue: MatchIssue;
|
|
90
|
+
expectedCents?: Cents;
|
|
91
|
+
amountCents?: Cents;
|
|
92
|
+
}[];
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Whether an invoice is what was ordered or agreed: every line matched, every count within what
|
|
96
|
+
* was open, every amount within the tolerance (% and absolute). Anything else is work.
|
|
97
|
+
*/
|
|
98
|
+
export declare function matchVerdict(allocations: readonly MatchAllocation[], unmatched: readonly string[], tolerance?: MatchTolerance): MatchVerdict;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { GoodsReceipt, GoodsReceiptLine, PurchaseLine, PurchaseOrder, SupplierOffer } from './types';
|
|
2
|
+
/** One line as a supplier's document states it: their code, their words, a count, serials. */
|
|
3
|
+
export interface DocumentLine {
|
|
4
|
+
sku?: string;
|
|
5
|
+
description: string;
|
|
6
|
+
quantity: number;
|
|
7
|
+
serials: string[];
|
|
8
|
+
}
|
|
9
|
+
/** What a packing slip says, read from a pdf, a picture or a sheet. */
|
|
10
|
+
export interface PackingSlipContent {
|
|
11
|
+
/** Their packing-slip number. */
|
|
12
|
+
supplierRef?: string;
|
|
13
|
+
/** Our order number as they printed it ("Uw referentie IO2026-0013"). */
|
|
14
|
+
orderRef?: string;
|
|
15
|
+
/** `YYYY-MM-DD`. */
|
|
16
|
+
deliveredOn?: string;
|
|
17
|
+
lines: DocumentLine[];
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* One line per article: a sheet lists devices one per row (1 × toestel, serial S1), and a person
|
|
21
|
+
* reads that as 4 × toestel with four serials.
|
|
22
|
+
*/
|
|
23
|
+
export declare function groupDocumentLines(lines: readonly DocumentLine[]): DocumentLine[];
|
|
24
|
+
/** The order a document is about: our number first, then their reference on it. `null` = none. */
|
|
25
|
+
export declare function orderForDocument<T extends Pick<PurchaseOrder, "number" | "reference">>(doc: Pick<PackingSlipContent, "orderRef" | "supplierRef">, orders: readonly T[]): T | null;
|
|
26
|
+
export type MatchedVia = "reference" | "sku" | "description";
|
|
27
|
+
/** Something a document line can be matched to: an order line, a contract line. */
|
|
28
|
+
export interface MatchCandidate {
|
|
29
|
+
id: string;
|
|
30
|
+
productId?: string;
|
|
31
|
+
offerId?: string;
|
|
32
|
+
name: string;
|
|
33
|
+
/** Their subscription id, when the candidate knows it (a contract line). */
|
|
34
|
+
externalRef?: string;
|
|
35
|
+
}
|
|
36
|
+
export interface LineMatch {
|
|
37
|
+
docIndex: number;
|
|
38
|
+
poLineId: string;
|
|
39
|
+
via: MatchedVia;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* How well a document line is this candidate, or not at all: their subscription id, then their
|
|
43
|
+
* article code (on the offer it was bought on, or any offer of that product), then the words.
|
|
44
|
+
* One ranking for packing slips and invoices alike.
|
|
45
|
+
*/
|
|
46
|
+
export declare function lineAffinity(doc: {
|
|
47
|
+
sku?: string;
|
|
48
|
+
description?: string;
|
|
49
|
+
externalRef?: string;
|
|
50
|
+
}, candidate: MatchCandidate, offers: readonly Pick<SupplierOffer, "id" | "productId" | "vendorSku">[]): {
|
|
51
|
+
via: MatchedVia;
|
|
52
|
+
rank: number;
|
|
53
|
+
} | null;
|
|
54
|
+
/**
|
|
55
|
+
* Which order line each document line is, by {@link lineAffinity}; each order line is taken once,
|
|
56
|
+
* the best free one wins. What stays unmatched is an exception for a person.
|
|
57
|
+
*/
|
|
58
|
+
export declare function matchDocumentLines(docLines: readonly DocumentLine[], poLines: readonly Pick<PurchaseLine, "id" | "kind" | "productId" | "offerId" | "name">[], offers: readonly Pick<SupplierOffer, "id" | "productId" | "vendorSku">[]): {
|
|
59
|
+
matches: LineMatch[];
|
|
60
|
+
unmatched: number[];
|
|
61
|
+
};
|
|
62
|
+
export type SlipProblemCode = "slip_no_order" | "slip_unknown_item" | "receipt_over_ordered" | "receipt_serials_count";
|
|
63
|
+
export interface SlipProblem {
|
|
64
|
+
code: SlipProblemCode;
|
|
65
|
+
docIndex?: number;
|
|
66
|
+
poLineId?: string;
|
|
67
|
+
}
|
|
68
|
+
export interface SlipProposal {
|
|
69
|
+
lines: (GoodsReceiptLine & {
|
|
70
|
+
docIndex: number;
|
|
71
|
+
via: MatchedVia;
|
|
72
|
+
})[];
|
|
73
|
+
problems: SlipProblem[];
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* A packing slip as a receipt to book: the matched lines with their counts and serials, and what a
|
|
77
|
+
* person has to decide — no order, an article not on it, more than is open, serials that do not
|
|
78
|
+
* match the count. Recomputed against the receipts booked so far, never stored stale.
|
|
79
|
+
*/
|
|
80
|
+
export declare function slipProposal(slip: PackingSlipContent, order: Pick<PurchaseOrder, "lines"> | null, offers: readonly Pick<SupplierOffer, "id" | "productId" | "vendorSku">[], booked: readonly GoodsReceipt[]): SlipProposal;
|
|
81
|
+
/** A model's answer as slip content, as far as it holds; `null` when it is no JSON at all. */
|
|
82
|
+
export declare function parsePackingSlip(answer: string): PackingSlipContent | null;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { ProvisioningAction, ProvisioningState } from '../../platform/provisioning';
|
|
2
|
+
import { Cents } from '../money/money';
|
|
3
|
+
import { PricePer } from '../money/period-price';
|
|
4
|
+
import { PostalAddress } from '../organization/types';
|
|
5
|
+
import { Transaction, TransactionLine } from '../transaction/types';
|
|
6
|
+
/**
|
|
7
|
+
* What a supplier asks for a product: one row per supplier and product (and validity window).
|
|
8
|
+
* The default offer is the product's cost price; the rest are the alternatives a buyer compares.
|
|
9
|
+
*/
|
|
10
|
+
export interface SupplierOffer {
|
|
11
|
+
id: string;
|
|
12
|
+
organizationId: string;
|
|
13
|
+
businessEntityId?: string;
|
|
14
|
+
supplierCompanyId: string;
|
|
15
|
+
productId: string;
|
|
16
|
+
/** Their article number. */
|
|
17
|
+
vendorSku?: string;
|
|
18
|
+
/**
|
|
19
|
+
* `unit`: a price per purchase unit. `rated`: per record on their rate card (telecom transit) —
|
|
20
|
+
* refused until that part exists, so nobody stores a price that means nothing yet.
|
|
21
|
+
*/
|
|
22
|
+
pricing: "unit" | "rated";
|
|
23
|
+
/** Per purchase unit, for `per` (€ 222 per year) or once. */
|
|
24
|
+
costCents: Cents;
|
|
25
|
+
currency: string;
|
|
26
|
+
per?: PricePer;
|
|
27
|
+
/** The supplier's term: an NCE yearly licence is `year` even when billed monthly. */
|
|
28
|
+
term?: "month" | "year" | "3year";
|
|
29
|
+
/** How often they invoice; `costCents` is per `per`, not per invoice. */
|
|
30
|
+
billedEvery?: "month" | "year" | "once";
|
|
31
|
+
/** What you order in: a roll, a pallet, a box of 50. Absent = the product's own unit. */
|
|
32
|
+
purchaseUnit?: string;
|
|
33
|
+
/** Product units in one purchase unit: a roll of 500 m is 500. */
|
|
34
|
+
unitsPerPurchaseUnit?: number;
|
|
35
|
+
validFrom?: number;
|
|
36
|
+
validTo?: number;
|
|
37
|
+
minQty?: number;
|
|
38
|
+
leadDays?: number;
|
|
39
|
+
/** Days from each term's start the supplier still takes a change (NCE: 7). */
|
|
40
|
+
changeWindowDays?: number;
|
|
41
|
+
/** Ordered through an app instead of by mail (fase 9). */
|
|
42
|
+
provider?: {
|
|
43
|
+
app: string;
|
|
44
|
+
offer: string;
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* Where it lands when that is not the supplier (fase 9b): a Telnyx number on the customer's
|
|
48
|
+
* Eylo account. The platform's own offer takes it in, after the purchase.
|
|
49
|
+
*/
|
|
50
|
+
deliverTo?: {
|
|
51
|
+
app: string;
|
|
52
|
+
offer: string;
|
|
53
|
+
};
|
|
54
|
+
isDefault: boolean;
|
|
55
|
+
createdBy: string;
|
|
56
|
+
createdAt?: number;
|
|
57
|
+
updatedAt?: number;
|
|
58
|
+
}
|
|
59
|
+
/** Ours to keep, or sold on to a customer — decides where the cost books (fase 6). */
|
|
60
|
+
export type PurchaseUse = "own" | "resale";
|
|
61
|
+
export interface PurchaseLine extends TransactionLine {
|
|
62
|
+
offerId?: string;
|
|
63
|
+
/** Sum of the booked receipts, in purchase units; recomputed, never added to. */
|
|
64
|
+
receivedQty: number;
|
|
65
|
+
/** Matched to supplier invoices (fase 7). */
|
|
66
|
+
billedQty: number;
|
|
67
|
+
use: PurchaseUse;
|
|
68
|
+
dimensions?: {
|
|
69
|
+
costCenterId?: string;
|
|
70
|
+
projectId?: string;
|
|
71
|
+
};
|
|
72
|
+
/** Absent = `purchase`. A change or an end points at the contract line it is about. */
|
|
73
|
+
action?: ProvisioningAction;
|
|
74
|
+
contractLine?: {
|
|
75
|
+
contractId: string;
|
|
76
|
+
lineId: string;
|
|
77
|
+
};
|
|
78
|
+
/** A change: the count it should be after. */
|
|
79
|
+
targetQuantity?: number;
|
|
80
|
+
/** When a change or end takes effect (epoch ms): the end of the term, not today. */
|
|
81
|
+
effectiveAt?: number;
|
|
82
|
+
/** Where this line came from in "Te bestellen", so it is not proposed twice. */
|
|
83
|
+
demandKey?: string;
|
|
84
|
+
/** The line of this order it waits for (the customer's account before its numbers). */
|
|
85
|
+
waitsFor?: string;
|
|
86
|
+
/** The supplier's ordering fields (a domain, a region). */
|
|
87
|
+
params?: Record<string, string>;
|
|
88
|
+
provisioning?: LineProvisioning;
|
|
89
|
+
/** The second step when the offer lands elsewhere (`deliverTo`): after a buy, before an end. */
|
|
90
|
+
delivery?: LineProvisioning;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* A line's request at the supplier. `waiting`: its account is not there yet. `manual`: no API —
|
|
94
|
+
* a person does it and fills in the result.
|
|
95
|
+
*/
|
|
96
|
+
export interface LineProvisioning {
|
|
97
|
+
state: ProvisioningState | "waiting" | "manual";
|
|
98
|
+
requestId: string;
|
|
99
|
+
app?: string;
|
|
100
|
+
externalRef?: string;
|
|
101
|
+
fulfillment?: Record<string, string>;
|
|
102
|
+
missing?: {
|
|
103
|
+
key: string;
|
|
104
|
+
reason: string;
|
|
105
|
+
}[];
|
|
106
|
+
error?: string;
|
|
107
|
+
attempts?: number;
|
|
108
|
+
nextAttemptAt?: number;
|
|
109
|
+
updatedAt?: number;
|
|
110
|
+
}
|
|
111
|
+
export interface DeliverTo {
|
|
112
|
+
kind: "own" | "customer" | "site";
|
|
113
|
+
companyId?: string;
|
|
114
|
+
address?: PostalAddress;
|
|
115
|
+
}
|
|
116
|
+
/** A purchase order: a `Transaction` of kind `purchase_order`; `companyId` is the supplier. */
|
|
117
|
+
export interface PurchaseOrder extends Omit<Transaction, "lines"> {
|
|
118
|
+
lines: PurchaseLine[];
|
|
119
|
+
deliverTo: DeliverTo;
|
|
120
|
+
/** Bought for this customer (the M365 seats for Acme). */
|
|
121
|
+
customerCompanyId?: string;
|
|
122
|
+
}
|
|
123
|
+
export interface GoodsReceiptLine {
|
|
124
|
+
poLineId: string;
|
|
125
|
+
/** Purchase units; positive, the movement says the direction. */
|
|
126
|
+
quantity: number;
|
|
127
|
+
serials: string[];
|
|
128
|
+
/** Still to come, as the packing slip says. */
|
|
129
|
+
backorderQty?: number;
|
|
130
|
+
}
|
|
131
|
+
/** Goods in (or back out). Booked is final: a mistake is corrected with a return. */
|
|
132
|
+
export interface GoodsReceipt {
|
|
133
|
+
id: string;
|
|
134
|
+
organizationId: string;
|
|
135
|
+
businessEntityId?: string;
|
|
136
|
+
movement: "in" | "return";
|
|
137
|
+
purchaseOrderId: string;
|
|
138
|
+
supplierCompanyId: string;
|
|
139
|
+
receivedAt: number;
|
|
140
|
+
receivedBy: string;
|
|
141
|
+
source: "manual" | "upload" | "mail";
|
|
142
|
+
fileId?: string;
|
|
143
|
+
supplierRef?: string;
|
|
144
|
+
status: "draft" | "booked";
|
|
145
|
+
lines: GoodsReceiptLine[];
|
|
146
|
+
createdAt?: number;
|
|
147
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { DocumentBlock } from '../../platform/document-blocks';
|
|
2
2
|
import { DocumentSlots, TemplateContext } from '../document-template/tokens';
|
|
3
3
|
import { TemplateCondition } from '../document-template/types';
|
|
4
|
+
import { PricePer } from '../money/period-price';
|
|
4
5
|
import { TaxRate } from '../money/tax';
|
|
5
6
|
import { PostalAddress } from '../organization/types';
|
|
6
7
|
import { Transaction } from './types';
|
|
@@ -56,6 +57,8 @@ export interface SlotLabels {
|
|
|
56
57
|
optional: string;
|
|
57
58
|
quantity: string;
|
|
58
59
|
unitPrice: string;
|
|
60
|
+
/** After a period price: "222,00 per jaar". */
|
|
61
|
+
per: Record<PricePer, string>;
|
|
59
62
|
net: string;
|
|
60
63
|
tax: string;
|
|
61
64
|
gross: string;
|
|
@@ -7,19 +7,13 @@
|
|
|
7
7
|
* `invoice:412` even if invoices ever move to a finance app; one branded `sales_document:412`
|
|
8
8
|
* would mean a backfill across four apps.
|
|
9
9
|
*/
|
|
10
|
-
export declare const
|
|
10
|
+
export declare const SALES_SCOPE_KINDS: readonly string[];
|
|
11
11
|
export declare const PRODUCT_SCOPE_KIND = "product";
|
|
12
12
|
/** An agreement: what a customer takes every period. Puts it in the customer file. */
|
|
13
13
|
export declare const AGREEMENT_SCOPE_KIND = "agreement";
|
|
14
14
|
/** `invoice:412`. The kind of a transaction *is* its kind key — no mapping table. */
|
|
15
15
|
export declare function transactionScopeKey(kindKey: string, id: string): string;
|
|
16
16
|
export declare function productScopeKey(id: string): string;
|
|
17
|
-
/**
|
|
18
|
-
* The shell path of a document, under the administration it belongs to.
|
|
19
|
-
*
|
|
20
|
-
* `fallbackEntityId` is for a row that predates administrations (`""` = the default one); without
|
|
21
|
-
* either there is no screen to point at, and no link beats a dead one.
|
|
22
|
-
*/
|
|
23
17
|
export declare function transactionHref(row: {
|
|
24
18
|
id: string;
|
|
25
19
|
kindKey: string;
|
|
@@ -53,6 +53,13 @@ export interface TransactionKind {
|
|
|
53
53
|
*/
|
|
54
54
|
credits?: string;
|
|
55
55
|
}
|
|
56
|
+
/** What `apps/sales` issues. Another app's kinds live in their own list, same shape. */
|
|
57
|
+
export declare const SALES_KINDS: readonly TransactionKind[];
|
|
58
|
+
/**
|
|
59
|
+
* Every app's kinds, for looking one up by key. Keys are unique across apps (`invoice` is sales',
|
|
60
|
+
* `purchase_order` will be purchase's), so a key alone names its kind. Never enumerate this in an
|
|
61
|
+
* app: sales lists {@link SALES_KINDS}, or a purchase kind claims sales scopes and series.
|
|
62
|
+
*/
|
|
56
63
|
export declare const TRANSACTION_KINDS: readonly TransactionKind[];
|
|
57
64
|
/**
|
|
58
65
|
* Which kinds the app actually offers today.
|
|
@@ -3,6 +3,7 @@ import { DocumentBlock } from '../../platform/document-blocks';
|
|
|
3
3
|
import { LineUsage } from '../agreement/types';
|
|
4
4
|
import { DocumentSender } from '../document-template/types';
|
|
5
5
|
import { Cents } from '../money/money';
|
|
6
|
+
import { PricePer } from '../money/period-price';
|
|
6
7
|
import { TaxRateKind } from '../money/tax';
|
|
7
8
|
import { OwnerScope } from '../scope/types';
|
|
8
9
|
import { TransactionPaymentTerm } from './payment-term';
|
|
@@ -47,6 +48,11 @@ export interface Transaction {
|
|
|
47
48
|
title?: string;
|
|
48
49
|
reference?: string;
|
|
49
50
|
currency: string;
|
|
51
|
+
/**
|
|
52
|
+
* Euro per unit of `currency`, set when issued in a currency the administration does not keep
|
|
53
|
+
* its books in (the ECB rate of that day). Absent in the books' own currency.
|
|
54
|
+
*/
|
|
55
|
+
fxRate?: string;
|
|
50
56
|
status: TransactionStatus;
|
|
51
57
|
/**
|
|
52
58
|
* The lines, as a column and not a table.
|
|
@@ -154,7 +160,9 @@ export interface Transaction {
|
|
|
154
160
|
* A union and not a free string: kinds and their statuses are **ours**, not the organisation's.
|
|
155
161
|
* Labels follow from the key as `status_<key>`.
|
|
156
162
|
*/
|
|
157
|
-
export type TransactionStatus = "draft" | "sent" | "accepted" | "rejected" | "expired" | "confirmed" | "delivered"
|
|
163
|
+
export type TransactionStatus = "draft" | "sent" | "accepted" | "rejected" | "expired" | "confirmed" | "delivered"
|
|
164
|
+
/** A purchase order whose every line came in. */
|
|
165
|
+
| "received" | "open" | "cancelled"
|
|
158
166
|
/** A newer version of this document went out. Set by the server, never by a person. */
|
|
159
167
|
| "superseded";
|
|
160
168
|
export interface IssuedPaper {
|
|
@@ -175,6 +183,8 @@ export interface TransactionLine {
|
|
|
175
183
|
unitKey: string;
|
|
176
184
|
quantity: number;
|
|
177
185
|
unitPriceCents: Cents;
|
|
186
|
+
/** The period the unit price is for ("€ 25 per month"). Absent = once, per unit. */
|
|
187
|
+
pricePer?: PricePer;
|
|
178
188
|
discountPercent?: number;
|
|
179
189
|
taxRateKey: string;
|
|
180
190
|
/** Which rung of the price ladder produced this amount. Purely to account for it. */
|