@opencxh/domain 1.300.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/business-entity/types.d.ts +6 -0
- package/dist/entities/catalog/rating.d.ts +18 -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/types.d.ts +17 -0
- 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 +8 -0
- package/dist/index.cjs +25 -23
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2856 -2607
- package/dist/platform/csv.d.ts +12 -0
- package/dist/platform/csv.test.d.ts +1 -0
- package/package.json +1 -1
|
@@ -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. */
|
|
@@ -132,3 +132,21 @@ export declare function ratedLineAmount(group: RatedGroup, rate: Rate | undefine
|
|
|
132
132
|
unitKey: string;
|
|
133
133
|
unitPriceCents: Cents;
|
|
134
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;
|
|
@@ -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"];
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { Cents } from '../money/money';
|
|
2
|
+
import { OfferAvailability, SupplierOffer } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* A supplier's price and stock list as a file (plan 045 C): uploaded, mailed, or picked up from a
|
|
5
|
+
* storage folder. It only updates offers we already have; a line we do not know is listed, never
|
|
6
|
+
* turned into a product or an offer by itself.
|
|
7
|
+
*/
|
|
8
|
+
/** The columns a feed can fill. `vendorSku` or `gtin` is the key; the rest is optional. */
|
|
9
|
+
export type FeedColumn = "vendorSku" | "gtin" | "description" | "cost" | "list" | "currency" | "stock" | "leadDays";
|
|
10
|
+
export declare const FEED_COLUMNS: readonly FeedColumn[];
|
|
11
|
+
/** Per column, the header it is read from. Set once (the AI proposes, a person approves). */
|
|
12
|
+
export type FeedMapping = Partial<Record<FeedColumn, string>>;
|
|
13
|
+
export type FeedSource = "upload" | "mail" | "storage";
|
|
14
|
+
/** How often a new file is expected; it decides how long a stock figure counts as fresh. */
|
|
15
|
+
export type FeedEvery = "day" | "week" | "month";
|
|
16
|
+
export declare const FEED_EVERY_MS: Record<FeedEvery, number>;
|
|
17
|
+
/** A price jump above this share is still applied, but said out loud. */
|
|
18
|
+
export declare const DEFAULT_JUMP_PERCENT = 10;
|
|
19
|
+
export interface SupplierFeed {
|
|
20
|
+
id: string;
|
|
21
|
+
organizationId: string;
|
|
22
|
+
businessEntityId: string;
|
|
23
|
+
supplierCompanyId: string;
|
|
24
|
+
name?: string;
|
|
25
|
+
source: FeedSource;
|
|
26
|
+
/** For `storage`: the folder it reads the newest csv from (a dated file each time is fine). */
|
|
27
|
+
storage?: {
|
|
28
|
+
mountId: string;
|
|
29
|
+
path: string;
|
|
30
|
+
};
|
|
31
|
+
/** The file the last storage run read, and how new it was: the same file is not read twice. */
|
|
32
|
+
lastFile?: {
|
|
33
|
+
id: string;
|
|
34
|
+
at: number;
|
|
35
|
+
};
|
|
36
|
+
mapping: FeedMapping;
|
|
37
|
+
/** When the file has no currency column. Absent = the administration's. */
|
|
38
|
+
currency?: string;
|
|
39
|
+
every: FeedEvery;
|
|
40
|
+
jumpPercent?: number;
|
|
41
|
+
lastRunAt?: number;
|
|
42
|
+
lastRunId?: string;
|
|
43
|
+
archived?: boolean;
|
|
44
|
+
createdBy: string;
|
|
45
|
+
createdAt?: number;
|
|
46
|
+
updatedAt?: number;
|
|
47
|
+
}
|
|
48
|
+
/** One line of the file, read. */
|
|
49
|
+
export interface FeedRow {
|
|
50
|
+
/** 1-based, the header included: what a person sees in the file. */
|
|
51
|
+
line: number;
|
|
52
|
+
vendorSku?: string;
|
|
53
|
+
gtin?: string;
|
|
54
|
+
description?: string;
|
|
55
|
+
costCents?: Cents;
|
|
56
|
+
listCents?: Cents;
|
|
57
|
+
currency?: string;
|
|
58
|
+
/** `null`: the file says nothing about stock, or "on request". */
|
|
59
|
+
stock?: number | null;
|
|
60
|
+
leadDays?: number;
|
|
61
|
+
}
|
|
62
|
+
export interface FeedReadResult {
|
|
63
|
+
rows: FeedRow[];
|
|
64
|
+
errors: {
|
|
65
|
+
line: number;
|
|
66
|
+
message: string;
|
|
67
|
+
}[];
|
|
68
|
+
/** A mapped header that is not in the file: the supplier changed the layout. */
|
|
69
|
+
missingHeaders: string[];
|
|
70
|
+
}
|
|
71
|
+
/** The file's cells, read through the mapping. Deterministic: the AI only proposed the mapping. */
|
|
72
|
+
export declare function readFeed(table: readonly string[][], mapping: FeedMapping): FeedReadResult;
|
|
73
|
+
/** Just enough of an article to match a feed line on its barcode. */
|
|
74
|
+
export interface FeedProduct {
|
|
75
|
+
id: string;
|
|
76
|
+
gtin?: string;
|
|
77
|
+
barcodes?: string[];
|
|
78
|
+
}
|
|
79
|
+
export type FeedMatch = {
|
|
80
|
+
row: FeedRow;
|
|
81
|
+
offer: SupplierOffer;
|
|
82
|
+
} | {
|
|
83
|
+
row: FeedRow;
|
|
84
|
+
product: FeedProduct;
|
|
85
|
+
} | {
|
|
86
|
+
row: FeedRow;
|
|
87
|
+
};
|
|
88
|
+
/**
|
|
89
|
+
* Each line to an offer of this supplier (their article number first, then the barcode of the
|
|
90
|
+
* article it is for), else to an article without an offer yet, else to nothing.
|
|
91
|
+
* `offers` are this supplier's in this administration.
|
|
92
|
+
*/
|
|
93
|
+
export declare function matchFeed(rows: readonly FeedRow[], offers: readonly SupplierOffer[], products: readonly FeedProduct[]): FeedMatch[];
|
|
94
|
+
export interface OfferChange {
|
|
95
|
+
offerId: string;
|
|
96
|
+
set: Partial<Pick<SupplierOffer, "costCents" | "listCents" | "currency" | "vendorSku">> & {
|
|
97
|
+
availability: OfferAvailability;
|
|
98
|
+
};
|
|
99
|
+
/** A price moved more than the feed's threshold; applied, and said. */
|
|
100
|
+
jump?: {
|
|
101
|
+
fromCents: Cents;
|
|
102
|
+
toCents: Cents;
|
|
103
|
+
percent: number;
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
export interface FeedUnmatched {
|
|
107
|
+
line: number;
|
|
108
|
+
vendorSku?: string;
|
|
109
|
+
gtin?: string;
|
|
110
|
+
description?: string;
|
|
111
|
+
costCents?: Cents;
|
|
112
|
+
/** An article we have, with no offer from this supplier yet: "make an offer" is one click. */
|
|
113
|
+
productId?: string;
|
|
114
|
+
}
|
|
115
|
+
export interface FeedOutcome {
|
|
116
|
+
changes: OfferChange[];
|
|
117
|
+
/** Offers whose price and stock stayed as they were (only the freshness moved). */
|
|
118
|
+
unchanged: number;
|
|
119
|
+
/** Offers this feed filled before and no longer lists: stock 0, nothing deleted. */
|
|
120
|
+
gone: string[];
|
|
121
|
+
unmatched: FeedUnmatched[];
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* What the run writes. Every matched offer gets a fresh stock stamp — the freshness is the
|
|
125
|
+
* point of a feed; price, advice price and currency only when the file has them.
|
|
126
|
+
*/
|
|
127
|
+
export declare function feedOutcome(matches: readonly FeedMatch[], offers: readonly SupplierOffer[], now: number, jumpPercent?: number): FeedOutcome;
|
|
128
|
+
/**
|
|
129
|
+
* A first mapping from the header alone: an exact word, then a header that starts with one.
|
|
130
|
+
* No AI, no guessing beyond the list — what it leaves open, the AI or a person fills.
|
|
131
|
+
*/
|
|
132
|
+
export declare function guessFeedMapping(header: readonly string[]): FeedMapping;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -55,11 +55,25 @@ export interface SupplierOffer {
|
|
|
55
55
|
app: string;
|
|
56
56
|
offer: string;
|
|
57
57
|
};
|
|
58
|
+
/** The supplier's advised price (the Microsoft ERP price), from a feed (plan 045 G). */
|
|
59
|
+
listCents?: Cents;
|
|
60
|
+
/** The last known stock; only the latest, no history (plan 045 B.1). */
|
|
61
|
+
availability?: OfferAvailability;
|
|
58
62
|
isDefault: boolean;
|
|
59
63
|
createdBy: string;
|
|
60
64
|
createdAt?: number;
|
|
61
65
|
updatedAt?: number;
|
|
62
66
|
}
|
|
67
|
+
/**
|
|
68
|
+
* What the supplier has, as last heard. `qty: null` = "can deliver, number unknown". `at` says
|
|
69
|
+
* how old it is: a stand older than the feed's rhythm counts as unknown in the choice.
|
|
70
|
+
*/
|
|
71
|
+
export interface OfferAvailability {
|
|
72
|
+
qty: number | null;
|
|
73
|
+
at: number;
|
|
74
|
+
source: "feed" | "api" | "manual";
|
|
75
|
+
leadDays?: number;
|
|
76
|
+
}
|
|
63
77
|
/** Ours to keep, or sold on to a customer — decides where the cost books (fase 6). */
|
|
64
78
|
export type PurchaseUse = "own" | "resale";
|
|
65
79
|
export interface PurchaseLine extends TransactionLine {
|
|
@@ -115,6 +129,9 @@ export interface LineProvisioning {
|
|
|
115
129
|
export interface DeliverTo {
|
|
116
130
|
kind: "own" | "customer" | "site";
|
|
117
131
|
companyId?: string;
|
|
132
|
+
/** A crm `CompanyAddress` (plan 050); absent = the company's primary delivery address. */
|
|
133
|
+
addressId?: string;
|
|
134
|
+
/** Frozen when the order is sent: what the supplier was told, whatever crm says later. */
|
|
118
135
|
address?: PostalAddress;
|
|
119
136
|
}
|
|
120
137
|
/** A purchase order: a `Transaction` of kind `purchase_order`; `companyId` is the supplier. */
|
|
@@ -11,4 +11,4 @@ export declare function isBookable(tx: Pick<Transaction, "kindKey" | "issuedAt">
|
|
|
11
11
|
* Headings and options carry no amount, so they are not lines here. A reverse charge or
|
|
12
12
|
* exemption ticked on the document replaces every line's code, as it did when it was priced.
|
|
13
13
|
*/
|
|
14
|
-
export declare function bookingDocumentOf(tx: Transaction, company: Pick<Company, "id" | "name" | "vatNumber" | "kvkNumber"
|
|
14
|
+
export declare function bookingDocumentOf(tx: Transaction, company: Pick<Company, "id" | "name" | "vatNumber" | "kvkNumber"> | null, sourceApp?: string): BookingDocument;
|
|
@@ -22,12 +22,12 @@ export declare function awaitsNumber(tx: Pick<Transaction, "issuedAt" | "kindKey
|
|
|
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", "lines"];
|
|
25
|
+
export declare const DRAFT_FIELDS: readonly ["companyId", "contactId", "dealItemId", "title", "reference", "currency", "taxOverrideKey", "introText", "termsText", "expiresAt", "deliveredAt", "periodFrom", "periodTo", "paymentTerm", "billingAddressId", "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"];
|
|
30
|
+
export declare const CLEARABLE_FIELDS: readonly ["contactId", "dealItemId", "title", "reference", "taxOverrideKey", "introText", "termsText", "expiresAt", "deliveredAt", "periodFrom", "periodTo", "billingAddressId"];
|
|
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
33
|
export type TransactionDraft = Omit<Transaction, "id" | "organizationId" | "createdBy" | "createdAt" | "updatedAt">;
|
|
@@ -59,6 +59,8 @@ export interface SlotLabels {
|
|
|
59
59
|
unitPrice: string;
|
|
60
60
|
/** After a period price: "222,00 per jaar". */
|
|
61
61
|
per: Record<PricePer, string>;
|
|
62
|
+
/** A meter's display unit after a rated quantity: `minute` → "min". Unknown = the key. */
|
|
63
|
+
units: Record<string, string>;
|
|
62
64
|
net: string;
|
|
63
65
|
tax: string;
|
|
64
66
|
gross: string;
|
|
@@ -28,7 +28,9 @@ export interface PaymentTerm {
|
|
|
28
28
|
method: PaymentTermMethod;
|
|
29
29
|
/** Days after issue until it falls due. `0` is payable immediately. */
|
|
30
30
|
dueDays: number;
|
|
31
|
-
/**
|
|
31
|
+
/** Who pays under it (plan 050): our customer (`sales`), or we a supplier. Absent = both. */
|
|
32
|
+
direction?: PaymentTermDirection;
|
|
33
|
+
/** The one a new document starts with when neither the request nor the role says. */
|
|
32
34
|
isDefault?: boolean;
|
|
33
35
|
archived?: boolean;
|
|
34
36
|
order?: number;
|
|
@@ -36,6 +38,18 @@ export interface PaymentTerm {
|
|
|
36
38
|
createdAt?: number;
|
|
37
39
|
updatedAt?: number;
|
|
38
40
|
}
|
|
41
|
+
export type PaymentTermDirection = "sales" | "purchase";
|
|
42
|
+
/** Whether a term may be picked for a document in `direction`. */
|
|
43
|
+
export declare const termApplies: (term: Pick<PaymentTerm, "direction">, direction: PaymentTermDirection) => boolean;
|
|
44
|
+
/**
|
|
45
|
+
* The term a new document takes: the one asked for, else the role's, else the default in that
|
|
46
|
+
* direction (a direction's own default beats a shared one). Archived or wrong-way codes are
|
|
47
|
+
* skipped, never an error — the next rung answers. `null` = the organisation has no terms.
|
|
48
|
+
*/
|
|
49
|
+
export declare function pickPaymentTerm(terms: readonly PaymentTerm[], direction: PaymentTermDirection, wanted?: {
|
|
50
|
+
explicit?: string;
|
|
51
|
+
role?: string;
|
|
52
|
+
}): PaymentTerm | null;
|
|
39
53
|
/**
|
|
40
54
|
* The due date of a document issued at `issuedAt` under a term of `dueDays`.
|
|
41
55
|
*
|
|
@@ -5,6 +5,7 @@ import { DocumentSender } from '../document-template/types';
|
|
|
5
5
|
import { Cents } from '../money/money';
|
|
6
6
|
import { PricePer } from '../money/period-price';
|
|
7
7
|
import { TaxRateKind } from '../money/tax';
|
|
8
|
+
import { PostalAddress } from '../organization/types';
|
|
8
9
|
import { OwnerScope } from '../scope/types';
|
|
9
10
|
import { TransactionPaymentTerm } from './payment-term';
|
|
10
11
|
/**
|
|
@@ -83,6 +84,11 @@ export interface Transaction {
|
|
|
83
84
|
* ride along because the due date is computed at issue, and by then the row may be gone.
|
|
84
85
|
*/
|
|
85
86
|
paymentTerm?: TransactionPaymentTerm;
|
|
87
|
+
/**
|
|
88
|
+
* The customer's crm address this document goes to (plan 050); absent = its primary billing
|
|
89
|
+
* address. Read while it is a draft, frozen as `issuedPaper.billingAddress` when it goes out.
|
|
90
|
+
*/
|
|
91
|
+
billingAddressId?: string;
|
|
86
92
|
/**
|
|
87
93
|
* When it falls due. Computed at issue from the condition, and only for a numbered kind: a quote
|
|
88
94
|
* does not fall due, it expires — that is `expiresAt`.
|
|
@@ -170,6 +176,8 @@ export interface IssuedPaper {
|
|
|
170
176
|
sender: DocumentSender;
|
|
171
177
|
templateId?: string;
|
|
172
178
|
customerName?: string;
|
|
179
|
+
/** The billing address as it went out (plan 050); the books take this one, not crm's today. */
|
|
180
|
+
billingAddress?: PostalAddress;
|
|
173
181
|
}
|
|
174
182
|
export interface TransactionLine {
|
|
175
183
|
/** Stable within the transaction, so a version diff can follow a line. */
|