@opencxh/domain 1.297.0 → 1.301.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/entities/agreement/types.d.ts +7 -2
- package/dist/entities/bookkeeping/types.d.ts +2 -1
- package/dist/entities/business-entity/types.d.ts +6 -0
- package/dist/entities/catalog/article.d.ts +28 -0
- package/dist/entities/catalog/article.test.d.ts +1 -0
- package/dist/entities/catalog/index.d.ts +1 -0
- package/dist/entities/catalog/pricing.d.ts +12 -6
- package/dist/entities/catalog/rating.d.ts +30 -0
- package/dist/entities/catalog/types.d.ts +133 -66
- package/dist/entities/catalog/usage-source.d.ts +11 -0
- package/dist/entities/company/address.d.ts +30 -0
- package/dist/entities/company/address.test.d.ts +1 -0
- package/dist/entities/company/index.d.ts +2 -0
- package/dist/entities/company/role.d.ts +33 -0
- package/dist/entities/company/types.d.ts +0 -22
- package/dist/entities/contact/segment.d.ts +1 -1
- package/dist/entities/purchase/feed.d.ts +132 -0
- package/dist/entities/purchase/feed.test.d.ts +1 -0
- package/dist/entities/purchase/index.d.ts +1 -0
- package/dist/entities/purchase/logic.d.ts +10 -4
- package/dist/entities/purchase/types.d.ts +28 -7
- package/dist/entities/transaction/booking.d.ts +1 -1
- package/dist/entities/transaction/convert.d.ts +2 -2
- package/dist/entities/transaction/document.d.ts +2 -0
- package/dist/entities/transaction/payment-term.d.ts +15 -1
- package/dist/entities/transaction/types.d.ts +18 -1
- package/dist/index.cjs +25 -23
- package/dist/index.d.ts +1 -0
- package/dist/index.js +3026 -2700
- package/dist/platform/bookkeeping.d.ts +4 -2
- package/dist/platform/csv.d.ts +12 -0
- package/dist/platform/csv.test.d.ts +1 -0
- package/package.json +1 -1
|
@@ -63,11 +63,10 @@ export interface Commitment {
|
|
|
63
63
|
}
|
|
64
64
|
/** A price fixed on the line when it was agreed, instead of the ladder at billing time. */
|
|
65
65
|
export interface LinePrice {
|
|
66
|
+
/** Per the line's `unitKey`. */
|
|
66
67
|
cents: Cents;
|
|
67
68
|
currency: string;
|
|
68
69
|
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
70
|
}
|
|
72
71
|
export type AgreementInterval = "month" | "quarter" | "year";
|
|
73
72
|
export interface AgreementLine {
|
|
@@ -75,6 +74,10 @@ export interface AgreementLine {
|
|
|
75
74
|
productId: string;
|
|
76
75
|
/** Copied from the product, like a transaction line: the agreement must stay readable. */
|
|
77
76
|
name: string;
|
|
77
|
+
/** The unit it is counted in (a supplier's box of 10); absent = the article's base unit. */
|
|
78
|
+
unitKey?: string;
|
|
79
|
+
/** Base units in one `unitKey`, fixed when the line was made (plan 046 #14). Absent = 1. */
|
|
80
|
+
factor?: number;
|
|
78
81
|
/**
|
|
79
82
|
* One line through its changes: 10 seats ending on 12 May and 12 seats from then share it. A
|
|
80
83
|
* billed line never changes; a change ends it and starts a successor in the same series.
|
|
@@ -161,6 +164,8 @@ export interface RunLine {
|
|
|
161
164
|
name: string;
|
|
162
165
|
productId: string;
|
|
163
166
|
unitKey: string;
|
|
167
|
+
/** Base units in one `unitKey`, from the agreement line (a box of 10). Absent = 1. */
|
|
168
|
+
factor?: number;
|
|
164
169
|
taxRateKey: string;
|
|
165
170
|
quantity: number;
|
|
166
171
|
unitPriceCents: Cents;
|
|
@@ -112,5 +112,6 @@ export interface WorkQueueItem {
|
|
|
112
112
|
/**
|
|
113
113
|
* The rule a line books under: product, then category, then VAT code, then the rule without a
|
|
114
114
|
* match. `undefined` when nothing applies — the caller refuses rather than guessing an account.
|
|
115
|
+
* With a `direction`, only rules for that direction count: a sale rule never books a purchase.
|
|
115
116
|
*/
|
|
116
|
-
export declare function ledgerRuleFor(line: Pick<BookingLine, "productId" | "ledgerCategoryKey" | "taxRateKey">, rules: readonly LedgerMappingRule[]): LedgerMappingRule | undefined;
|
|
117
|
+
export declare function ledgerRuleFor(line: Pick<BookingLine, "productId" | "ledgerCategoryKey" | "taxRateKey">, rules: readonly LedgerMappingRule[], direction?: "sale" | "purchase"): LedgerMappingRule | undefined;
|
|
@@ -67,6 +67,12 @@ export interface BusinessEntity {
|
|
|
67
67
|
vatCadence?: "quarter" | "month" | "year";
|
|
68
68
|
/** Kleineondernemersregeling: no VAT charged or deducted, so no return. */
|
|
69
69
|
vatKor?: boolean;
|
|
70
|
+
/**
|
|
71
|
+
* The first debtor and creditor number crm hands out here (plan 050); absent = 10001 and 40001.
|
|
72
|
+
* A change counts for new numbers only: one handed out keeps its number.
|
|
73
|
+
*/
|
|
74
|
+
firstDebtorNumber?: number;
|
|
75
|
+
firstCreditorNumber?: number;
|
|
70
76
|
/** The one a new document lands on. Exactly one row carries it. */
|
|
71
77
|
isDefault?: boolean;
|
|
72
78
|
/** Archived, never deleted: an issued quote names the entity it went out as. */
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Cents } from '../money/money';
|
|
2
|
+
import { PricePer } from '../money/period-price';
|
|
3
|
+
import { Product, ProductRelease, ReferenceCost } from './types';
|
|
4
|
+
/**
|
|
5
|
+
* `qty` of `unitKey` in the article's base unit: 3 rolls of 500 m are 1,500 m. Throws on a unit
|
|
6
|
+
* the article does not know — a guessed factor of 1 is how a box of ten gets billed as one.
|
|
7
|
+
*/
|
|
8
|
+
export declare function toBaseUnits(product: Pick<Product, "unitKey" | "units">, unitKey: string, qty: number): number;
|
|
9
|
+
/** Base units in one `unitKey`; 1 for the base unit itself. */
|
|
10
|
+
export declare function unitFactor(product: Pick<Product, "unitKey" | "units">, unitKey: string): number;
|
|
11
|
+
/** What is wrong with a set of units, as an error code; `null` when nothing is. */
|
|
12
|
+
export declare function unitsProblem(product: Pick<Product, "unitKey" | "units">): string | null;
|
|
13
|
+
/** A parent with variants is a container, never on a line: its variants are what is traded. */
|
|
14
|
+
export declare function isTradeable(product: Pick<Product, "id">, all: readonly Pick<Product, "parentId">[]): boolean;
|
|
15
|
+
/**
|
|
16
|
+
* Why a variant does not fit its parent, as an error code; `null` when it does. Every variant
|
|
17
|
+
* carries exactly the parent's option keys, and no two share a combination — what a shop sync
|
|
18
|
+
* (Shopify's `selectedOptions`) requires.
|
|
19
|
+
*/
|
|
20
|
+
export declare function variantOptionsProblem(parent: Pick<Product, "optionKeys">, variant: Pick<Product, "id" | "options">, siblings: readonly Pick<Product, "id" | "options">[]): string | null;
|
|
21
|
+
/**
|
|
22
|
+
* Whether a **new** commitment may name this article in this administration (a line added, a
|
|
23
|
+
* quote sent, an order confirmed, a PO sent) — as an error code, `null` when it may. Carrying out
|
|
24
|
+
* a commitment that already exists (an agreement run, receiving a sent PO) never asks this.
|
|
25
|
+
*/
|
|
26
|
+
export declare function commitProblem(product: Pick<Product, "archived">, release: Pick<ProductRelease, "blocked"> | undefined): string | null;
|
|
27
|
+
/** The reference cost restated to another period, in its own currency (€ 240/year → € 20/month). */
|
|
28
|
+
export declare function referenceCostIn(cost: ReferenceCost, per: PricePer | undefined): Cents;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { Cents } from '../money/money';
|
|
2
2
|
import { PricePer } from '../money/period-price';
|
|
3
3
|
import { TransactionLine } from '../transaction/types';
|
|
4
|
-
import { PriceRule,
|
|
4
|
+
import { PriceRule, SellableProduct } from './types';
|
|
5
|
+
/** What the ladder reads of an article: its identity and sales' price row. */
|
|
6
|
+
export type PricedProduct = Pick<SellableProduct, "id" | "categoryKey" | "listPriceCents" | "currency" | "pricePer" | "rateCardId">;
|
|
5
7
|
/**
|
|
6
8
|
* The ladder: the most specific rule wins, whole. No sum, no stacking.
|
|
7
9
|
*
|
|
@@ -24,21 +26,23 @@ export interface PriceContext {
|
|
|
24
26
|
companyId?: string;
|
|
25
27
|
segmentKey?: string;
|
|
26
28
|
contractId?: string;
|
|
29
|
+
/** In the article's **base** unit: 2 boxes of 10 reach a tier "from 20" (plan 046 #10). */
|
|
27
30
|
quantity: number;
|
|
28
31
|
/** Epoch ms; the validity window is judged against this, not against "now". */
|
|
29
32
|
at: number;
|
|
30
33
|
}
|
|
31
34
|
/** Amount, currency and period travel together: € 222 means nothing without "per year". */
|
|
32
35
|
export interface ResolvedPrice {
|
|
33
|
-
|
|
36
|
+
/** Absent with `sourceKey: "missing"`: no list price and no rule. Never a silent € 0. */
|
|
37
|
+
unitPriceCents?: Cents;
|
|
34
38
|
currency: string;
|
|
35
39
|
/** Absent = once, or per unit. */
|
|
36
40
|
per?: PricePer;
|
|
37
|
-
/** Where this amount came from. Almost free, and the only question a salesperson asks. */
|
|
41
|
+
/** Where this amount came from. Almost free, and the only question a salesperson asks. `missing` = no price. */
|
|
38
42
|
sourceKey: string;
|
|
39
43
|
ruleId?: string;
|
|
40
44
|
}
|
|
41
|
-
export declare function resolvePrice(product:
|
|
45
|
+
export declare function resolvePrice(product: PricedProduct, ctx: PriceContext, rules: readonly PriceRule[]): ResolvedPrice;
|
|
42
46
|
export interface ResolvedRateCard {
|
|
43
47
|
rateCardId: string;
|
|
44
48
|
sourceKey: string;
|
|
@@ -48,12 +52,14 @@ export interface ResolvedRateCard {
|
|
|
48
52
|
* The rate card a `rated` product is billed on for this customer: the same ladder as a price,
|
|
49
53
|
* over the rules whose outcome is a card, then the product's own card. `null` = it has none.
|
|
50
54
|
*/
|
|
51
|
-
export declare function resolveRateCard(product:
|
|
55
|
+
export declare function resolveRateCard(product: PricedProduct, ctx: PriceContext, rules: readonly PriceRule[]): ResolvedRateCard | null;
|
|
52
56
|
/** The `priceSourceKey` of a price somebody typed themselves. The ladder never overwrites it. */
|
|
53
57
|
export declare const MANUAL_PRICE_SOURCE = "manual";
|
|
58
|
+
/** Nothing priced it: sending or confirming refuses (`price_missing`) until a person types one. */
|
|
59
|
+
export declare const MISSING_PRICE_SOURCE = "missing";
|
|
54
60
|
/**
|
|
55
61
|
* The ladder again, for lines already on a document: after the quantity or the customer changed,
|
|
56
62
|
* a tier or an agreement may now apply. A typed price stays, and so does a line whose product
|
|
57
63
|
* is no longer in `products`.
|
|
58
64
|
*/
|
|
59
|
-
export declare function repriceLines(lines: readonly TransactionLine[], products: readonly
|
|
65
|
+
export declare function repriceLines(lines: readonly TransactionLine[], products: readonly PricedProduct[], rules: readonly PriceRule[], ctx: Omit<PriceContext, "quantity">): TransactionLine[];
|
|
@@ -120,3 +120,33 @@ export interface RatingResult {
|
|
|
120
120
|
}
|
|
121
121
|
/** A period's records, rated and grouped per rate. Cents are rounded once per group. */
|
|
122
122
|
export declare function rateRecords(records: readonly UsageRecord[], rates: readonly Rate[], aggregation?: UsageAggregation): RatingResult;
|
|
123
|
+
/**
|
|
124
|
+
* A rated group as an invoice line: hours × the hourly price when a flat rate says so exactly,
|
|
125
|
+
* else 1 piece × the group's total (tiers, a markup, a setup fee, a price below a cent).
|
|
126
|
+
*/
|
|
127
|
+
export declare function ratedLineAmount(group: RatedGroup, rate: Rate | undefined, display: {
|
|
128
|
+
unit: string;
|
|
129
|
+
per: number;
|
|
130
|
+
}): {
|
|
131
|
+
quantity: number;
|
|
132
|
+
unitKey: string;
|
|
133
|
+
unitPriceCents: Cents;
|
|
134
|
+
};
|
|
135
|
+
/**
|
|
136
|
+
* What a rated line counted, in the meter's display unit (372 min), for a line billed as
|
|
137
|
+
* 1 piece × its total — `null` for every other line, whose own quantity already says it.
|
|
138
|
+
* The allowance is off: the reader sees what was billed.
|
|
139
|
+
*/
|
|
140
|
+
export declare function ratedQuantity(line: {
|
|
141
|
+
unitKey: string;
|
|
142
|
+
usage?: {
|
|
143
|
+
rateId?: string;
|
|
144
|
+
measured: number;
|
|
145
|
+
included?: number;
|
|
146
|
+
per: number;
|
|
147
|
+
unit: string;
|
|
148
|
+
};
|
|
149
|
+
}): {
|
|
150
|
+
amount: number;
|
|
151
|
+
unit: string;
|
|
152
|
+
} | null;
|
|
@@ -1,18 +1,21 @@
|
|
|
1
1
|
import { Cents } from '../money/money';
|
|
2
2
|
import { PricePer } from '../money/period-price';
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
4
|
+
* An article: what we sell, buy, stock or make, variants included (plan 046). Catalog's, shared by
|
|
5
|
+
* every administration; what differs per administration is its {@link ProductRelease}. Prices are
|
|
6
|
+
* sales' ({@link SalesProduct}), offers purchase's.
|
|
5
7
|
*
|
|
6
8
|
* One self-referencing table instead of a product/variant pair: a variant has exactly the same
|
|
7
|
-
* fields as its parent and misses none of them. What it adds is {@link Product.options}
|
|
8
|
-
* is a column, not an entity.
|
|
9
|
+
* fields as its parent and misses none of them. What it adds is {@link Product.options}.
|
|
9
10
|
*/
|
|
10
11
|
export interface Product {
|
|
11
12
|
id: string;
|
|
12
13
|
organizationId: string;
|
|
13
|
-
/** The parent this is a variant of. Absent = a product in its own right. */
|
|
14
|
+
/** The parent this is a variant of. Absent = a product in its own right. A parent is never traded. */
|
|
14
15
|
parentId?: string;
|
|
15
|
-
/**
|
|
16
|
+
/** On a parent: the option keys every variant carries, in this order (`["colour", "size"]`). */
|
|
17
|
+
optionKeys?: string[];
|
|
18
|
+
/** What makes this variant different: `[{ key: "colour", value: "black" }]`. */
|
|
16
19
|
options?: {
|
|
17
20
|
key: string;
|
|
18
21
|
value: string;
|
|
@@ -23,28 +26,115 @@ export interface Product {
|
|
|
23
26
|
sku?: string;
|
|
24
27
|
/** The global code: EAN-13, UPC-12, GTIN-14. Stored normalised — see `normalizeGtin`. */
|
|
25
28
|
gtin?: string;
|
|
26
|
-
/** Whatever else is scannable
|
|
29
|
+
/** Whatever else is scannable without a quantity of its own, as scanned. */
|
|
27
30
|
barcodes?: string[];
|
|
28
|
-
/**
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
* Having to be stable is the whole requirement: together with {@link Product.taxRateKey} this is
|
|
32
|
-
* the key the bookkeeping bridge picks a ledger account on. A free text field where one person
|
|
33
|
-
* types "Services" and another "services " cannot carry that.
|
|
34
|
-
*/
|
|
31
|
+
/** References to this product elsewhere (`shopify:<shop>:<gid>`). A reference, never a copy. */
|
|
32
|
+
externalIds?: string[];
|
|
33
|
+
/** A {@link ProductCategory} key: groups the offering, and price agreements name it. */
|
|
35
34
|
categoryKey?: string;
|
|
35
|
+
/** Is anything delivered? Fulfilment reads it; a service is done, goods are shipped. */
|
|
36
|
+
kind: ProductKind;
|
|
37
|
+
/** Serial or batch numbers on receipt and delivery. Absent = none; goods only. */
|
|
38
|
+
tracking?: ProductTracking;
|
|
39
|
+
/** The base unit everything counts in: stock, deliveries, allocations. Locked once used. */
|
|
40
|
+
unitKey: string;
|
|
41
|
+
/** Other units it comes in: a roll of 500 m, a box of 10. See {@link toBaseUnits}. */
|
|
42
|
+
units?: ProductUnit[];
|
|
43
|
+
/** On quotes, invoices and agreements. Absent = yes; steel plate you only buy is `false`. */
|
|
44
|
+
sellable?: boolean;
|
|
45
|
+
/** On purchase orders. Absent = no. */
|
|
46
|
+
purchasable?: boolean;
|
|
47
|
+
/** When it was first put on a document: locks {@link Product.unitKey} and forbids a delete. */
|
|
48
|
+
usedAt?: number;
|
|
49
|
+
archived?: boolean;
|
|
50
|
+
order?: number;
|
|
51
|
+
createdBy: string;
|
|
52
|
+
createdAt?: number;
|
|
53
|
+
updatedAt?: number;
|
|
54
|
+
}
|
|
55
|
+
export type ProductKind = "goods" | "service";
|
|
56
|
+
export type ProductTracking = "none" | "serial" | "batch";
|
|
57
|
+
/** Another unit an article comes in. `factor` = base units in one: a roll of 500 m is 500. */
|
|
58
|
+
export interface ProductUnit {
|
|
59
|
+
key: string;
|
|
60
|
+
/** A pack of this article's own ("tray van 24"); absent = the fixed unit's name for `key`. */
|
|
61
|
+
label?: string;
|
|
62
|
+
factor: number;
|
|
63
|
+
/** One GTIN per packaging level (GS1): scanning the box counts ten at once. */
|
|
64
|
+
gtin?: string;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* An article in use in one administration. No release = not offered there. A variant has its own
|
|
68
|
+
* (its own cost); a parent with variants has none.
|
|
69
|
+
*/
|
|
70
|
+
export interface ProductRelease {
|
|
71
|
+
id: string;
|
|
72
|
+
organizationId: string;
|
|
73
|
+
productId: string;
|
|
74
|
+
businessEntityId: string;
|
|
75
|
+
/** Key into that administration's VAT codes, never the percentage itself. */
|
|
76
|
+
taxRateKey: string;
|
|
77
|
+
/** A {@link ProductLedgerCategory} key: which revenue or cost account a line books to. */
|
|
78
|
+
ledgerCategoryKey?: string;
|
|
36
79
|
/**
|
|
37
|
-
*
|
|
38
|
-
*
|
|
80
|
+
* What we normally pay for this. **Not** a cost price out of an inventory valuation. Shielded by
|
|
81
|
+
* `catalog.product.cost`, and never in a template context.
|
|
39
82
|
*/
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
|
|
83
|
+
referenceCost?: ReferenceCost;
|
|
84
|
+
/** Stopped in this administration only: no new commitments, running ones carry on. */
|
|
85
|
+
blocked?: boolean;
|
|
86
|
+
createdBy: string;
|
|
87
|
+
createdAt?: number;
|
|
88
|
+
updatedAt?: number;
|
|
89
|
+
}
|
|
90
|
+
/** A cost with its own currency, period and date: $ 240 per year, as of 28-09. */
|
|
91
|
+
export interface ReferenceCost {
|
|
92
|
+
cents: Cents;
|
|
93
|
+
currency: string;
|
|
94
|
+
per?: PricePer;
|
|
95
|
+
asOf: number;
|
|
96
|
+
/** The purchase offer it comes from; absent = typed by hand. Read-only in the form then. */
|
|
97
|
+
offerId?: string;
|
|
98
|
+
supplierCompanyId?: string;
|
|
99
|
+
}
|
|
100
|
+
/** A small table of its own, never free text: price agreements and reports name it. */
|
|
101
|
+
export interface ProductCategory {
|
|
102
|
+
id: string;
|
|
103
|
+
organizationId: string;
|
|
104
|
+
key: string;
|
|
105
|
+
name: string;
|
|
106
|
+
order?: number;
|
|
107
|
+
archived?: boolean;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Which account a line books to, in either direction: "hardware" is revenue 8000 on a sale and
|
|
111
|
+
* cost 7000 on a purchase, through finance's mapping. Catalog's list; finance reads it as the
|
|
112
|
+
* contract's `LedgerCategory { key, label }` and maps it.
|
|
113
|
+
*/
|
|
114
|
+
export interface ProductLedgerCategory {
|
|
115
|
+
id: string;
|
|
116
|
+
organizationId: string;
|
|
117
|
+
key: string;
|
|
118
|
+
name: string;
|
|
119
|
+
order?: number;
|
|
120
|
+
archived?: boolean;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Sales' own row per article and administration: the price and how it bills. Absent row, or no
|
|
124
|
+
* `listPriceCents` = no price — which is not € 0 (plan 046 #12).
|
|
125
|
+
*/
|
|
126
|
+
export interface SalesProduct {
|
|
127
|
+
id: string;
|
|
128
|
+
organizationId: string;
|
|
129
|
+
productId: string;
|
|
130
|
+
businessEntityId: string;
|
|
131
|
+
listPriceCents?: Cents;
|
|
132
|
+
currency: string;
|
|
43
133
|
/**
|
|
44
|
-
*
|
|
45
|
-
*
|
|
134
|
+
* The period the list price is for: € 222 per year. Absent = once, or per unit. A recurring
|
|
135
|
+
* agreement line needs one; the run bills its share of each window.
|
|
46
136
|
*/
|
|
47
|
-
|
|
137
|
+
pricePer?: PricePer;
|
|
48
138
|
/**
|
|
49
139
|
* `unit`: a price per unit through the ladder (list price, tiers, customer agreements).
|
|
50
140
|
* `rated`: every record priced on a rate card by its dimensions (calls, traffic).
|
|
@@ -52,61 +142,34 @@ export interface Product {
|
|
|
52
142
|
pricing?: ProductPricing;
|
|
53
143
|
/** The default card of a `rated` product. A price agreement may name another. */
|
|
54
144
|
rateCardId?: string;
|
|
55
|
-
/**
|
|
56
|
-
unitKey
|
|
57
|
-
/** Key into the organisation's VAT table, never the percentage itself. */
|
|
58
|
-
taxRateKey: string;
|
|
59
|
-
listPriceCents: Cents;
|
|
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;
|
|
66
|
-
/**
|
|
67
|
-
* What we normally pay for this. **Not** a cost price out of an inventory valuation.
|
|
68
|
-
*
|
|
69
|
-
* Copied onto the line when it is added, and shielded: margin is not for everyone holding
|
|
70
|
-
* `sales.product.read`. It never reaches a template context either — `{line.cost}` on a quote
|
|
71
|
-
* would be one typo away.
|
|
72
|
-
*/
|
|
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;
|
|
78
|
-
/** Who we buy it from. A `Company` in crm, so free — and all a later purchase order needs. */
|
|
79
|
-
supplierCompanyId?: string;
|
|
80
|
-
/**
|
|
81
|
-
* The administration this belongs to. Unset = the default one — see `belongsToEntity`.
|
|
82
|
-
*
|
|
83
|
-
* Exactly one, not a list: two administrations that both sell this keep two rows, with their
|
|
84
|
-
* own sku, their own price and their own ledger category. That is the price of a hard split and
|
|
85
|
-
* it is what was chosen.
|
|
86
|
-
*/
|
|
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;
|
|
92
|
-
archived?: boolean;
|
|
93
|
-
order?: number;
|
|
94
|
-
createdBy: string;
|
|
95
|
-
createdAt?: number;
|
|
145
|
+
/** The unit a new line starts in (a box); absent = the base unit. */
|
|
146
|
+
unitKey?: string;
|
|
96
147
|
updatedAt?: number;
|
|
97
148
|
}
|
|
98
|
-
/**
|
|
99
|
-
|
|
149
|
+
/**
|
|
150
|
+
* An article as sales sees it in one administration: catalog's article, its release, and sales'
|
|
151
|
+
* own row, flat. A read model — nothing stores it. See {@link sellableProduct}.
|
|
152
|
+
*/
|
|
153
|
+
export interface SellableProduct extends Product, Partial<Pick<ProductRelease, "taxRateKey" | "ledgerCategoryKey" | "referenceCost" | "blocked">>, Omit<Partial<SalesProduct>, "id" | "organizationId" | "productId" | "unitKey" | "updatedAt"> {
|
|
154
|
+
/** The price list's currency, else the administration's: what price rules are matched on. */
|
|
155
|
+
currency: string;
|
|
156
|
+
/** Sales' default unit for a new line. */
|
|
157
|
+
salesUnitKey?: string;
|
|
158
|
+
/** Whether this administration has released it at all. */
|
|
159
|
+
released: boolean;
|
|
160
|
+
}
|
|
161
|
+
/** Puts the three rows together. `currency` is the administration's, for an article without a price. */
|
|
162
|
+
export declare function sellableProduct(product: Product, release: ProductRelease | undefined, sales: SalesProduct | undefined, currency: string): SellableProduct;
|
|
100
163
|
/** Whether a sales picker offers it: absent counts as sellable, so older products stay. */
|
|
101
164
|
export declare function isSellable(product: Pick<Product, "sellable" | "archived">): boolean;
|
|
102
165
|
/** Whether a purchase picker offers it. */
|
|
103
166
|
export declare function isPurchasable(product: Pick<Product, "purchasable" | "archived">): boolean;
|
|
167
|
+
export type ProductPricing = "unit" | "rated";
|
|
104
168
|
/**
|
|
105
169
|
* Units follow work's `FIXED_TYPES` pattern: a fixed list plus a namespaced fallback, so an
|
|
106
170
|
* unknown key reads as itself instead of breaking.
|
|
107
171
|
*/
|
|
108
|
-
export
|
|
109
|
-
export declare const FIXED_UNITS: readonly ["piece", "hour", "day", "month", "km", "license", "minute", "kg", "ton", "m", "m2", "m3", "liter"];
|
|
172
|
+
export declare const FIXED_UNITS: readonly ["piece", "hour", "day", "month", "km", "license", "minute", "kg", "ton", "m", "m2", "m3", "liter", "box", "roll", "pallet", "pack"];
|
|
110
173
|
export type FixedUnit = (typeof FIXED_UNITS)[number];
|
|
111
174
|
/**
|
|
112
175
|
* A price for a customer, a segment or a contract.
|
|
@@ -179,6 +242,10 @@ export interface SaveProductRequest extends Partial<Omit<Product, "id" | "organi
|
|
|
179
242
|
id?: string;
|
|
180
243
|
name: string;
|
|
181
244
|
}
|
|
245
|
+
export interface SaveReleaseRequest extends Partial<Omit<ProductRelease, "id" | "organizationId" | "createdBy" | "createdAt" | "updatedAt">> {
|
|
246
|
+
productId: string;
|
|
247
|
+
businessEntityId: string;
|
|
248
|
+
}
|
|
182
249
|
export interface SavePriceRuleRequest extends Partial<Omit<PriceRule, "id" | "organizationId" | "createdBy" | "createdAt">> {
|
|
183
250
|
id?: string;
|
|
184
251
|
}
|
|
@@ -34,6 +34,17 @@ export interface UsageMeter {
|
|
|
34
34
|
* is nothing to pick, and the customer's id is the account. Absent = `source`.
|
|
35
35
|
*/
|
|
36
36
|
account?: "source" | "customer";
|
|
37
|
+
/**
|
|
38
|
+
* Billing increments worth offering beyond the generic ones (as used, first unit whole, per
|
|
39
|
+
* started unit): hours per started quarter. In meter units; `label` is a `locales` key.
|
|
40
|
+
*/
|
|
41
|
+
increments?: UsageIncrement[];
|
|
42
|
+
}
|
|
43
|
+
/** `{ first: 900, next: 900 }` = per started quarter of an hour, in seconds. */
|
|
44
|
+
export interface UsageIncrement {
|
|
45
|
+
first: number;
|
|
46
|
+
next: number;
|
|
47
|
+
label: string;
|
|
37
48
|
}
|
|
38
49
|
export interface UsageDimension {
|
|
39
50
|
key: string;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { PostalAddress } from '../organization/types';
|
|
2
|
+
/** What an address is for. One address can be several: billing and visiting at one door. */
|
|
3
|
+
export type CompanyAddressKind = "billing" | "delivery" | "site" | "visit";
|
|
4
|
+
export declare const COMPANY_ADDRESS_KINDS: readonly CompanyAddressKind[];
|
|
5
|
+
/** The kinds a document takes by default when nobody picks. */
|
|
6
|
+
export type PrimaryAddressKind = "billing" | "delivery";
|
|
7
|
+
/**
|
|
8
|
+
* One address of a company, with an id to point at (plan 050): a qualification, a delivery to a
|
|
9
|
+
* site, a portal order. Archived, never deleted — whatever points at it keeps pointing. A sent
|
|
10
|
+
* document freezes the address on its paper; it never reads the row again.
|
|
11
|
+
*/
|
|
12
|
+
export interface CompanyAddress {
|
|
13
|
+
id: string;
|
|
14
|
+
organizationId: string;
|
|
15
|
+
companyId: string;
|
|
16
|
+
kinds: CompanyAddressKind[];
|
|
17
|
+
/** "Vestiging Utrecht"; absent = the street says enough. */
|
|
18
|
+
label?: string;
|
|
19
|
+
address: PostalAddress;
|
|
20
|
+
primaryFor?: PrimaryAddressKind[];
|
|
21
|
+
archived?: boolean;
|
|
22
|
+
createdBy: string;
|
|
23
|
+
createdAt?: number;
|
|
24
|
+
updatedAt?: number;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* The address a document takes for `kind`: the primary one, else the first live one of that
|
|
28
|
+
* kind. A delivery falls back to billing — most small companies have one address for both.
|
|
29
|
+
*/
|
|
30
|
+
export declare function primaryAddress(addresses: readonly CompanyAddress[], kind: PrimaryAddressKind): CompanyAddress | undefined;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/** Which side of the table a company sits on, for one administration. */
|
|
2
|
+
export type CompanyRoleKind = "customer" | "supplier";
|
|
3
|
+
export declare const COMPANY_ROLE_KINDS: readonly CompanyRoleKind[];
|
|
4
|
+
/**
|
|
5
|
+
* A company in one role for one administration (plan 050): customer 10023 at BV A, supplier 40017
|
|
6
|
+
* in USD at BV B. One per `(companyId, businessEntityId, role)`; crm hands out the numbers, per
|
|
7
|
+
* administration and role, so an organisation without sales still gets creditor numbers.
|
|
8
|
+
*/
|
|
9
|
+
export interface CompanyRole {
|
|
10
|
+
id: string;
|
|
11
|
+
organizationId: string;
|
|
12
|
+
companyId: string;
|
|
13
|
+
businessEntityId: string;
|
|
14
|
+
role: CompanyRoleKind;
|
|
15
|
+
/** Debtor or creditor number; unique per administration and role. */
|
|
16
|
+
number?: string;
|
|
17
|
+
/** An organization `PaymentTerm.code`, in the direction of the role. */
|
|
18
|
+
paymentTermKey?: string;
|
|
19
|
+
/** Empty = the administration's currency. */
|
|
20
|
+
currency?: string;
|
|
21
|
+
/** Nothing new in this role: no quote to a blocked customer, no PO to a blocked supplier. */
|
|
22
|
+
blocked?: boolean;
|
|
23
|
+
createdAt?: number;
|
|
24
|
+
updatedAt?: number;
|
|
25
|
+
}
|
|
26
|
+
/** "Make sure this company is our supplier at BV B": idempotent, the existing role if there is one. */
|
|
27
|
+
export interface EnsureCompanyRoleRequest {
|
|
28
|
+
companyId: string;
|
|
29
|
+
businessEntityId: string;
|
|
30
|
+
role: CompanyRoleKind;
|
|
31
|
+
}
|
|
32
|
+
/** The payment-term direction a role pays in: a customer pays us, we pay a supplier. */
|
|
33
|
+
export declare const roleDirection: (role: CompanyRoleKind) => "sales" | "purchase";
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { LifecycleStage } from '../contact/lifecycle';
|
|
2
|
-
import { PostalAddress } from '../organization/types';
|
|
3
2
|
import { OwnerScope } from '../scope/types';
|
|
4
3
|
/** Same meaning as {@link ContactOrigin}: `shadow` is a lazily cached hit, a landed row is `own`. */
|
|
5
4
|
export type CompanyOrigin = "own" | "shadow";
|
|
@@ -48,19 +47,6 @@ export interface Company {
|
|
|
48
47
|
lifecycleStage?: LifecycleStage;
|
|
49
48
|
/** The group this company belongs to. One level up per row; a cycle is refused on write. */
|
|
50
49
|
parentCompanyId?: string;
|
|
51
|
-
/**
|
|
52
|
-
* Where the company sits. Needed by anything that puts this company on paper — a quote's
|
|
53
|
-
* recipient block is the first, and the issue gate refuses a document with a hole in it.
|
|
54
|
-
*/
|
|
55
|
-
address?: PostalAddress;
|
|
56
|
-
/**
|
|
57
|
-
* This customer's default payment condition, as a code `apps/sales` owns.
|
|
58
|
-
*
|
|
59
|
-
* An opaque key, exactly like {@link Company.locale} is an opaque language: crm stores it and
|
|
60
|
-
* never interprets it. It is a default and not a rule — a document copies the condition when it
|
|
61
|
-
* is created and keeps it from then on, so changing this never rewrites what already went out.
|
|
62
|
-
*/
|
|
63
|
-
paymentTermCode?: string;
|
|
64
50
|
/** Normalised: upper case, no spaces (`NL123456789B01`). */
|
|
65
51
|
vatNumber?: string;
|
|
66
52
|
kvkNumber?: string;
|
|
@@ -68,14 +54,6 @@ export interface Company {
|
|
|
68
54
|
ibans?: string[];
|
|
69
55
|
/** `<scheme>:<id>` (`0106:12345678`), for Peppol e-invoicing. */
|
|
70
56
|
peppolId?: string;
|
|
71
|
-
debtorNumber?: string;
|
|
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;
|
|
79
57
|
/** Raw, human-readable phone/website as entered, for display. */
|
|
80
58
|
phone?: string;
|
|
81
59
|
website?: string;
|
|
@@ -6,7 +6,7 @@ import { SavedView, SavedViewSort } from '../saved-view/types';
|
|
|
6
6
|
*/
|
|
7
7
|
export type SegmentKind = "contact" | "company";
|
|
8
8
|
/** Multi-value filters: empty or absent = no filter on that axis. */
|
|
9
|
-
export declare const SEGMENT_LIST_FILTERS: readonly ["stages", "leadStatuses", "sources", "scopes", "cities", "parentCompanyIds"];
|
|
9
|
+
export declare const SEGMENT_LIST_FILTERS: readonly ["stages", "leadStatuses", "sources", "scopes", "cities", "parentCompanyIds", "roles"];
|
|
10
10
|
export type SegmentListFilter = (typeof SEGMENT_LIST_FILTERS)[number];
|
|
11
11
|
/** Yes/no filters that need another store to answer: open deals in work, people at a company. */
|
|
12
12
|
export declare const SEGMENT_PRESENCE_FILTERS: readonly ["openDeal", "hasContacts"];
|