@opencxh/domain 1.302.0 → 1.303.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/from-quote.d.ts +8 -8
- package/dist/entities/agreement/index.d.ts +2 -0
- package/dist/entities/agreement/invoice-head.d.ts +21 -0
- package/dist/entities/agreement/lifecycle.d.ts +10 -0
- package/dist/entities/agreement/period.d.ts +8 -0
- package/dist/entities/agreement/prices.d.ts +43 -0
- package/dist/entities/agreement/prices.test.d.ts +1 -0
- package/dist/entities/agreement/types.d.ts +54 -10
- package/dist/entities/bank/match.d.ts +16 -2
- package/dist/entities/bank/types.d.ts +12 -0
- package/dist/entities/bookkeeping/receipt.d.ts +45 -6
- package/dist/entities/bookkeeping/types.d.ts +1 -2
- package/dist/entities/business-entity/types.d.ts +11 -15
- package/dist/entities/catalog/pricing.d.ts +11 -11
- package/dist/entities/catalog/types.d.ts +15 -9
- package/dist/entities/company/role.d.ts +14 -0
- package/dist/entities/financing/schedule.d.ts +9 -3
- package/dist/entities/financing/types.d.ts +17 -6
- package/dist/entities/fulfilment/adopt.d.ts +149 -0
- package/dist/entities/fulfilment/customers.d.ts +63 -0
- package/dist/entities/fulfilment/execute.d.ts +67 -0
- package/dist/entities/fulfilment/fulfilment.test.d.ts +1 -0
- package/dist/entities/fulfilment/goods.d.ts +72 -0
- package/dist/entities/fulfilment/index.d.ts +8 -0
- package/dist/entities/fulfilment/path.d.ts +8 -0
- package/dist/entities/fulfilment/path.test.d.ts +1 -0
- package/dist/entities/fulfilment/rules.d.ts +124 -0
- package/dist/entities/fulfilment/target.d.ts +131 -0
- package/dist/entities/fulfilment/types.d.ts +321 -0
- package/dist/entities/ledger/entry.d.ts +47 -9
- package/dist/entities/ledger/types.d.ts +33 -35
- package/dist/entities/ledger/xaf.d.ts +3 -0
- package/dist/entities/money/tax.d.ts +6 -0
- package/dist/entities/money/totals.d.ts +5 -5
- package/dist/entities/purchase/choose.d.ts +57 -0
- package/dist/entities/purchase/choose.test.d.ts +1 -0
- package/dist/entities/purchase/contract.d.ts +44 -25
- package/dist/entities/purchase/demand.d.ts +22 -40
- package/dist/entities/purchase/feed.d.ts +53 -5
- package/dist/entities/purchase/index.d.ts +1 -0
- package/dist/entities/purchase/kinds.d.ts +2 -1
- package/dist/entities/purchase/logic.d.ts +32 -7
- package/dist/entities/purchase/match.d.ts +48 -6
- package/dist/entities/purchase/packing-slip.d.ts +23 -0
- package/dist/entities/purchase/types.d.ts +71 -38
- package/dist/entities/reports/vat.d.ts +20 -5
- package/dist/entities/transaction/booking.d.ts +4 -4
- package/dist/entities/transaction/convert.d.ts +10 -10
- package/dist/entities/transaction/document.d.ts +8 -3
- package/dist/entities/transaction/dunning.d.ts +12 -1
- package/dist/entities/transaction/facets.test.d.ts +1 -0
- package/dist/entities/transaction/keys.d.ts +3 -0
- package/dist/entities/transaction/kinds.d.ts +11 -10
- package/dist/entities/transaction/receivable.d.ts +5 -2
- package/dist/entities/transaction/settlement.d.ts +7 -7
- package/dist/entities/transaction/types.d.ts +123 -68
- package/dist/index.cjs +25 -25
- package/dist/index.d.ts +2 -1
- package/dist/index.js +5160 -4026
- package/dist/platform/bookkeeping.d.ts +8 -8
- package/dist/platform/provisioning.d.ts +210 -13
- package/package.json +1 -1
- /package/dist/entities/{purchase/demand.test.d.ts → agreement/invoice-head.test.d.ts} +0 -0
|
@@ -30,6 +30,8 @@ export interface TaxRate {
|
|
|
30
30
|
* line from this, and the bookkeeping connector maps it onto its own tax code.
|
|
31
31
|
*/
|
|
32
32
|
export type TaxRateKind = "standard" | "reduced" | "zero" | "reverse_charge" | "exempt";
|
|
33
|
+
/** The rate a line falls back to when neither the article nor the caller names one. */
|
|
34
|
+
export declare const DEFAULT_TAX_RATE_KEY = "high";
|
|
33
35
|
/** The Dutch defaults an organisation starts with. It may edit them; it may not compute them. */
|
|
34
36
|
export declare const NL_TAX_RATES: readonly TaxRate[];
|
|
35
37
|
/**
|
|
@@ -67,11 +69,15 @@ export interface TaxCode {
|
|
|
67
69
|
selfAssessed?: boolean;
|
|
68
70
|
/** Counts in the EC sales list (ICP). */
|
|
69
71
|
icp?: boolean;
|
|
72
|
+
/** Which column of the EC sales list: absent on an ICP code = services. */
|
|
73
|
+
icpKind?: IcpKind;
|
|
70
74
|
/** Deductible share of input VAT, mixed use. Absent = 100. */
|
|
71
75
|
deductiblePercent?: number;
|
|
72
76
|
archived?: boolean;
|
|
73
77
|
order?: number;
|
|
74
78
|
}
|
|
79
|
+
/** The EC sales list reports goods, services and triangular (ABC) supplies apart. */
|
|
80
|
+
export type IcpKind = "goods" | "services" | "triangular";
|
|
75
81
|
export interface TaxCodeRate {
|
|
76
82
|
/** `"2019-01-01"`. Days, compared as strings. */
|
|
77
83
|
validFrom: string;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { SalesLine, TransactionTotals } from '../transaction/types';
|
|
2
2
|
import { Cents } from './money';
|
|
3
3
|
import { TaxRate } from './tax';
|
|
4
4
|
/**
|
|
@@ -19,10 +19,10 @@ import { TaxRate } from './tax';
|
|
|
19
19
|
* of the line total — `priceOver`, so the line total is rounded once and then split by calendar.
|
|
20
20
|
* Without a service period (a quote) it is one period's amount.
|
|
21
21
|
*/
|
|
22
|
-
export declare function lineNet(line: Pick<
|
|
22
|
+
export declare function lineNet(line: Pick<SalesLine, "kind" | "quantity" | "unitPriceCents" | "discountPercent" | "pricePer" | "servicePeriod">): Cents;
|
|
23
23
|
export interface PricedTransaction {
|
|
24
24
|
/** The same lines with `netCents` and `taxCents` filled in. */
|
|
25
|
-
lines:
|
|
25
|
+
lines: SalesLine[];
|
|
26
26
|
totals: TransactionTotals;
|
|
27
27
|
}
|
|
28
28
|
/**
|
|
@@ -36,6 +36,6 @@ export interface PricedTransaction {
|
|
|
36
36
|
* rate, because that is a property of the sale and not of the product.
|
|
37
37
|
*
|
|
38
38
|
* An **optional** line is priced like any other and then left out of every sum — see
|
|
39
|
-
* `
|
|
39
|
+
* `SalesLine.optional`.
|
|
40
40
|
*/
|
|
41
|
-
export declare function transactionTotals(lines: readonly
|
|
41
|
+
export declare function transactionTotals(lines: readonly SalesLine[], rates: readonly TaxRate[], overrideKey?: string): PricedTransaction;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { DeliverTo, SupplierOffer } from './types';
|
|
2
|
+
/** A hand-set stand ("called, they have 5") counts for a day (plan 045 B.6). */
|
|
3
|
+
export declare const MANUAL_STOCK_FRESH_MS: number;
|
|
4
|
+
/** What one need asks of a source. */
|
|
5
|
+
export interface OfferNeed {
|
|
6
|
+
/** Base units. */
|
|
7
|
+
quantity: number;
|
|
8
|
+
/** The sale's commitment (`Commitment.termMonths`); absent = cancel any time. */
|
|
9
|
+
termMonths?: number;
|
|
10
|
+
/** `own` through our warehouse, anything else straight to the customer. */
|
|
11
|
+
deliverTo?: DeliverTo["kind"];
|
|
12
|
+
/** The delivery check the sale was made on: only what it found available qualifies (6c). */
|
|
13
|
+
qualified?: readonly {
|
|
14
|
+
app: string;
|
|
15
|
+
offer: string;
|
|
16
|
+
}[];
|
|
17
|
+
/** The administration's currency; prices in another one need `rates`. */
|
|
18
|
+
currency: string;
|
|
19
|
+
/** Units of `currency` per one unit of the key (ECB, of the day). */
|
|
20
|
+
rates?: Readonly<Record<string, number>>;
|
|
21
|
+
/** A person's pick: it stands, even when it no longer fits — the reason still shows. */
|
|
22
|
+
pinned?: string;
|
|
23
|
+
}
|
|
24
|
+
/** Known enough, unknown (no feed, stale, or a count the supplier does not give), known short. */
|
|
25
|
+
export type OfferStock = "enough" | "unknown" | "short";
|
|
26
|
+
export type Unsuitable = "invalid" | "term" | "dropship" | "qualification";
|
|
27
|
+
export interface RankedOffer<T extends SupplierOffer = SupplierOffer> {
|
|
28
|
+
offer: T;
|
|
29
|
+
stock: OfferStock;
|
|
30
|
+
/** What the supplier said, when fresh. */
|
|
31
|
+
qty?: number;
|
|
32
|
+
/** Per product unit over a year, in the administration's currency; `null` = no price to compare. */
|
|
33
|
+
unitCents: number | null;
|
|
34
|
+
leadDays?: number;
|
|
35
|
+
unsuitable?: Unsuitable;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Why the chosen one: the preferred offer (`preferred`), the preferred one was short
|
|
39
|
+
* (`preferred_short`, chosen is the next best), only one qualified (`only`), the best of equals
|
|
40
|
+
* (`best`), nobody has enough (`short`: chosen has the most, the rest is a person's split), or a
|
|
41
|
+
* person picked it (`picked`).
|
|
42
|
+
*/
|
|
43
|
+
export type ChoiceWhy = "preferred" | "preferred_short" | "only" | "best" | "short" | "picked";
|
|
44
|
+
export interface OfferChoice<T extends SupplierOffer = SupplierOffer> {
|
|
45
|
+
chosen: RankedOffer<T> | null;
|
|
46
|
+
why?: ChoiceWhy;
|
|
47
|
+
/** Every other offer, best first; the unsuitable last with their reason. */
|
|
48
|
+
alternatives: RankedOffer<T>[];
|
|
49
|
+
/** What `unitCents` is in: the administration's currency. */
|
|
50
|
+
currency: string;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* The best source for one need (plan 045 B.2): suitability first, then stock, then price. Policy
|
|
54
|
+
* (Vincent 03-10): the preferred offer (`isDefault`) wins unless it is known short; the cheaper one
|
|
55
|
+
* is a hint in the alternatives. Nobody enough: the one with the most, never a split.
|
|
56
|
+
*/
|
|
57
|
+
export declare function chooseOffer<T extends SupplierOffer>(offers: readonly T[], need: OfferNeed, now: number): OfferChoice<T>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Period } from '../agreement/period';
|
|
2
|
-
import { Agreement, AgreementLine } from '../agreement/types';
|
|
2
|
+
import { Agreement, AgreementLine, LinePrice } from '../agreement/types';
|
|
3
3
|
import { Cents } from '../money/money';
|
|
4
4
|
/** A sales line, followed through its changes: the agreement and the series it belongs to. */
|
|
5
5
|
export interface SalesLineRef {
|
|
@@ -7,24 +7,28 @@ export interface SalesLineRef {
|
|
|
7
7
|
seriesId: string;
|
|
8
8
|
}
|
|
9
9
|
/**
|
|
10
|
-
* A subscription at a supplier
|
|
11
|
-
*
|
|
12
|
-
*
|
|
10
|
+
* A subscription at a supplier, one row in `purchase_contract_line` (review R5): its `price` is the
|
|
11
|
+
* snapshot taken when it was made — an offer that gets dearer later changes nothing here; a new
|
|
12
|
+
* price is a successor row in the same series from a date. What was invoiced is `purchase_match`.
|
|
13
13
|
*/
|
|
14
|
-
export interface
|
|
14
|
+
export interface SupplierContractLine extends Pick<AgreementLine, "id" | "productId" | "name" | "unitKey" | "factor" | "commitment" | "quantity" | "endsAt"> {
|
|
15
|
+
/** The supplier's price, a snapshot taken when the row was made. */
|
|
16
|
+
price?: LinePrice;
|
|
17
|
+
contractId: string;
|
|
18
|
+
/** One subscription through its changes; the first row's own id. */
|
|
19
|
+
seriesId: string;
|
|
20
|
+
startsAt: number;
|
|
15
21
|
/** Provenance only: the offer it was bought on. The price is `price`, not the offer's. */
|
|
16
22
|
offerId?: string;
|
|
17
|
-
/** Their subscription id (Pax8, Microsoft). */
|
|
23
|
+
/** Their subscription id (Pax8, Microsoft): what a supplier invoice line is matched on. */
|
|
18
24
|
externalRef?: string;
|
|
19
|
-
/**
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
externalRef?: string;
|
|
25
|
-
};
|
|
25
|
+
/**
|
|
26
|
+
* The fulfilment item this pays for (047 step 6): its sale, what it runs on and what the supplier
|
|
27
|
+
* handed back live there. A line without one is purchase's own (rent, a contract by hand).
|
|
28
|
+
*/
|
|
29
|
+
itemId?: string;
|
|
26
30
|
customerCompanyId?: string;
|
|
27
|
-
/**
|
|
31
|
+
/** A line by hand: the sales line that passes it on. A line with `itemId` reads the item's. */
|
|
28
32
|
salesLine?: SalesLineRef;
|
|
29
33
|
/**
|
|
30
34
|
* `variable`: no price, the invoice decides the amount (transit minutes). Nothing is expected;
|
|
@@ -34,18 +38,24 @@ export interface PurchaseAgreementLine extends AgreementLine {
|
|
|
34
38
|
expected?: "variable";
|
|
35
39
|
/** Days from each term's start in which the supplier still takes a change (NCE: 7). */
|
|
36
40
|
changeWindowDays?: number;
|
|
37
|
-
/**
|
|
41
|
+
/** `lineKey` of the order line it came from; a successor shares it, so it is not unique. */
|
|
38
42
|
sourceKey?: string;
|
|
39
|
-
/** What supplier invoices said per period (fase 7): the history a variable line is judged on. */
|
|
40
|
-
invoiced?: {
|
|
41
|
-
from: number;
|
|
42
|
-
cents: Cents;
|
|
43
|
-
}[];
|
|
44
43
|
}
|
|
45
|
-
/**
|
|
46
|
-
|
|
47
|
-
|
|
44
|
+
/**
|
|
45
|
+
* A supplier contract's header, one per supplier × administration (044): no sales fields, no
|
|
46
|
+
* lines — those are rows of their own. `companyId` is the supplier.
|
|
47
|
+
*/
|
|
48
|
+
export interface SupplierContract extends Pick<Agreement, "id" | "organizationId" | "businessEntityId" | "title" | "companyId" | "currency" | "status" | "startsAt" | "endsAt" | "commitment" | "createdBy" | "createdAt" | "updatedAt"> {
|
|
48
49
|
}
|
|
50
|
+
/** A header with its rows, read together for a computation over the whole contract. */
|
|
51
|
+
export type SupplierContractWithLines = SupplierContract & {
|
|
52
|
+
lines: readonly SupplierContractLine[];
|
|
53
|
+
};
|
|
54
|
+
/** What supplier invoices said per period, per line id: derived from `purchase_match`. */
|
|
55
|
+
export type InvoicedHistory = ReadonlyMap<string, readonly {
|
|
56
|
+
from: number;
|
|
57
|
+
cents: Cents;
|
|
58
|
+
}[]>;
|
|
49
59
|
export interface ExpectedLine {
|
|
50
60
|
lineId: string;
|
|
51
61
|
kind: "fixed" | "count" | "variable";
|
|
@@ -57,7 +67,9 @@ export interface ExpectedLine {
|
|
|
57
67
|
* the days it ran (the same `lineNet` the sales run bills with), or "count" / "variable" where the
|
|
58
68
|
* amount is not ours to know in advance.
|
|
59
69
|
*/
|
|
60
|
-
export declare function expectedFor(agreement: Pick<
|
|
70
|
+
export declare function expectedFor(agreement: Pick<SupplierContract, "startsAt" | "endsAt"> & {
|
|
71
|
+
lines: readonly SupplierContractLine[];
|
|
72
|
+
}, window: Period): ExpectedLine[];
|
|
61
73
|
/**
|
|
62
74
|
* A variable amount that moved more than `threshold` against the average of the (up to) three
|
|
63
75
|
* periods before: that is work for a person, not a number to wave through.
|
|
@@ -73,6 +85,11 @@ export interface SalesLineState extends SalesLineRef {
|
|
|
73
85
|
unbilled: Period[];
|
|
74
86
|
/** The sales line has ended or its agreement is not active. */
|
|
75
87
|
ended?: boolean;
|
|
88
|
+
/** The last period invoiced and how many it billed: "gefactureerd" on Producten bij klanten. */
|
|
89
|
+
billed?: {
|
|
90
|
+
quantity: number;
|
|
91
|
+
period: Period;
|
|
92
|
+
};
|
|
76
93
|
}
|
|
77
94
|
export type ContractSignal = {
|
|
78
95
|
kind: "not_covered";
|
|
@@ -111,6 +128,8 @@ export interface SignalOptions {
|
|
|
111
128
|
noticeDays?: number;
|
|
112
129
|
/** A change window closing this soon is a reminder. */
|
|
113
130
|
windowDays?: number;
|
|
131
|
+
/** What was invoiced per line, for the variable jump. */
|
|
132
|
+
invoiced?: InvoicedHistory;
|
|
114
133
|
}
|
|
115
134
|
/**
|
|
116
135
|
* Everything about a supplier contract a person should look at, apart:
|
|
@@ -121,4 +140,4 @@ export interface SignalOptions {
|
|
|
121
140
|
* - **notice due / change window**: reminders before the dates that decide the next term;
|
|
122
141
|
* - **variable jump**: a variable amount moved more than 30%.
|
|
123
142
|
*/
|
|
124
|
-
export declare function contractSignals(contracts: readonly
|
|
143
|
+
export declare function contractSignals(contracts: readonly SupplierContractWithLines[], sales: readonly SalesLineState[], now: number, options?: SignalOptions): ContractSignal[];
|
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Agreement } from '../agreement/types';
|
|
3
|
-
import { PurchaseAgreement } from './contract';
|
|
4
|
-
import { SupplierOffer } from './types';
|
|
1
|
+
import { ProvisioningAction } from '../../platform/provisioning';
|
|
2
|
+
import { Agreement, AgreementLine } from '../agreement/types';
|
|
5
3
|
/** One thing to order, change or end, as "Te bestellen" lists it. */
|
|
6
4
|
export interface DemandItem {
|
|
7
5
|
/** Stable: the sales series, or the customer's account at the supplier. */
|
|
@@ -18,14 +16,6 @@ export interface DemandItem {
|
|
|
18
16
|
targetQuantity?: number;
|
|
19
17
|
from?: number;
|
|
20
18
|
customerCompanyId: string;
|
|
21
|
-
salesLine?: {
|
|
22
|
-
agreementId: string;
|
|
23
|
-
seriesId: string;
|
|
24
|
-
};
|
|
25
|
-
contractLine?: {
|
|
26
|
-
contractId: string;
|
|
27
|
-
lineId: string;
|
|
28
|
-
};
|
|
29
19
|
/** Epoch ms it takes effect: the term's end for an end or a reduction outside the window. */
|
|
30
20
|
effectiveAt?: number;
|
|
31
21
|
/** The last day to send it for `effectiveAt`; past it, `effectiveAt` is the next term's end. */
|
|
@@ -36,33 +26,25 @@ export interface DemandItem {
|
|
|
36
26
|
nextTerm?: boolean;
|
|
37
27
|
/** The purchase runs on after the sale ends: we pay until then. */
|
|
38
28
|
runsOnUntil?: number;
|
|
39
|
-
/**
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
29
|
+
/**
|
|
30
|
+
* An end at a provider that prices it (045 D N-2): what ending costs, shown before "Maak
|
|
31
|
+
* concept" and put on the order as it was shown. `costUnknown`: the provider gave no answer.
|
|
32
|
+
*/
|
|
33
|
+
cost?: {
|
|
34
|
+
parts: {
|
|
35
|
+
label: string;
|
|
36
|
+
cents: number;
|
|
37
|
+
recurring?: "month";
|
|
38
|
+
}[];
|
|
39
|
+
warnings?: string[];
|
|
45
40
|
};
|
|
41
|
+
costUnknown?: boolean;
|
|
42
|
+
/** The request's answers (the site as a crm address id): what a per-order price is asked on. */
|
|
43
|
+
params?: Record<string, string>;
|
|
44
|
+
/** The request this waits for: the customer's account before its numbers. */
|
|
45
|
+
waitsFor?: string;
|
|
46
46
|
}
|
|
47
|
-
export
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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[];
|
|
47
|
+
export declare const seriesOf: (line: AgreementLine) => string;
|
|
48
|
+
/** The line of a series that runs on `at`, else the one that starts next, else its last. */
|
|
49
|
+
export declare function currentOf<T extends AgreementLine>(parent: Pick<Agreement, "startsAt" | "endsAt">, lines: readonly T[], at: number): T;
|
|
50
|
+
export declare function bySeries<T extends AgreementLine>(lines: readonly T[]): Map<string, T[]>;
|
|
@@ -6,11 +6,12 @@ import { OfferAvailability, SupplierOffer } from './types';
|
|
|
6
6
|
* turned into a product or an offer by itself.
|
|
7
7
|
*/
|
|
8
8
|
/** The columns a feed can fill. `vendorSku` or `gtin` is the key; the rest is optional. */
|
|
9
|
-
export type FeedColumn = "vendorSku" | "gtin" | "description" | "brand" | "group" | "cost" | "list" | "currency" | "stock" | "leadDays";
|
|
9
|
+
export type FeedColumn = "vendorSku" | "gtin" | "description" | "brand" | "group" | "cost" | "list" | "currency" | "stock" | "leadDays" | "lotSize" | "dropship";
|
|
10
10
|
export declare const FEED_COLUMNS: readonly FeedColumn[];
|
|
11
11
|
/** Per column, the header it is read from. Set once (the AI proposes, a person approves). */
|
|
12
12
|
export type FeedMapping = Partial<Record<FeedColumn, string>>;
|
|
13
|
-
|
|
13
|
+
/** `api`: the assortment from a provisioning app (plan 045 D N-1) instead of a file. */
|
|
14
|
+
export type FeedSource = "upload" | "mail" | "storage" | "api";
|
|
14
15
|
/** How often a new file is expected; it decides how long a stock figure counts as fresh. */
|
|
15
16
|
export type FeedEvery = "day" | "week" | "month";
|
|
16
17
|
export declare const FEED_EVERY_MS: Record<FeedEvery, number>;
|
|
@@ -23,6 +24,11 @@ export interface SupplierFeed {
|
|
|
23
24
|
supplierCompanyId: string;
|
|
24
25
|
name?: string;
|
|
25
26
|
source: FeedSource;
|
|
27
|
+
/** For `api`: the app that lists its assortment, and which of its connections. */
|
|
28
|
+
api?: {
|
|
29
|
+
app: string;
|
|
30
|
+
accountId: string;
|
|
31
|
+
};
|
|
26
32
|
/** For `storage`: the folder it reads the newest csv from (a dated file each time is fine). */
|
|
27
33
|
storage?: {
|
|
28
34
|
mountId: string;
|
|
@@ -61,6 +67,10 @@ export interface FeedRow {
|
|
|
61
67
|
/** `null`: the file says nothing about stock, or "on request". */
|
|
62
68
|
stock?: number | null;
|
|
63
69
|
leadDays?: number;
|
|
70
|
+
lotSize?: number;
|
|
71
|
+
dropship?: SupplierOffer["dropship"];
|
|
72
|
+
/** From an `api` feed: the provider's offer key a new offer is ordered through. */
|
|
73
|
+
offer?: string;
|
|
64
74
|
}
|
|
65
75
|
export interface FeedReadResult {
|
|
66
76
|
rows: FeedRow[];
|
|
@@ -96,8 +106,9 @@ export type FeedMatch = {
|
|
|
96
106
|
export declare function matchFeed(rows: readonly FeedRow[], offers: readonly SupplierOffer[], products: readonly FeedProduct[]): FeedMatch[];
|
|
97
107
|
export interface OfferChange {
|
|
98
108
|
offerId: string;
|
|
99
|
-
set: Partial<Pick<SupplierOffer, "costCents" | "listCents" | "currency" | "vendorSku">> & {
|
|
100
|
-
|
|
109
|
+
set: Partial<Pick<SupplierOffer, "costCents" | "listCents" | "currency" | "vendorSku" | "lotSize" | "dropship" | "validTo">> & {
|
|
110
|
+
/** Absent only when an `api` feed no longer lists it: then it ends (`validTo`). */
|
|
111
|
+
availability?: OfferAvailability;
|
|
101
112
|
};
|
|
102
113
|
/** A price moved more than the feed's threshold; applied, and said. */
|
|
103
114
|
jump?: {
|
|
@@ -116,6 +127,8 @@ export interface FeedUnmatched {
|
|
|
116
127
|
costCents?: Cents;
|
|
117
128
|
/** An article we have, with no offer from this supplier yet: "make an offer" is one click. */
|
|
118
129
|
productId?: string;
|
|
130
|
+
/** From an `api` feed: the provider's offer key (045 D N-1). */
|
|
131
|
+
offer?: string;
|
|
119
132
|
}
|
|
120
133
|
export interface FeedOutcome {
|
|
121
134
|
changes: OfferChange[];
|
|
@@ -129,7 +142,7 @@ export interface FeedOutcome {
|
|
|
129
142
|
* What the run writes. Every matched offer gets a fresh stock stamp — the freshness is the
|
|
130
143
|
* point of a feed; price, advice price and currency only when the file has them.
|
|
131
144
|
*/
|
|
132
|
-
export declare function feedOutcome(matches: readonly FeedMatch[], offers: readonly SupplierOffer[], now: number, jumpPercent?: number): FeedOutcome;
|
|
145
|
+
export declare function feedOutcome(matches: readonly FeedMatch[], offers: readonly SupplierOffer[], now: number, jumpPercent?: number, every?: FeedEvery): FeedOutcome;
|
|
133
146
|
/**
|
|
134
147
|
* A first mapping from the header alone: every exact word first, then a header that starts with
|
|
135
148
|
* one — so "Productgroep" is the group before "product" claims it as the description.
|
|
@@ -168,3 +181,38 @@ export interface FeedImportStatus {
|
|
|
168
181
|
startedAt: number;
|
|
169
182
|
finishedAt?: number;
|
|
170
183
|
}
|
|
184
|
+
/** A run as a screen reads it: the work left is a count, not the list of writes. */
|
|
185
|
+
export interface FeedRunView {
|
|
186
|
+
id: string;
|
|
187
|
+
organizationId: string;
|
|
188
|
+
feedId: string;
|
|
189
|
+
state: "applying" | "done" | "failed";
|
|
190
|
+
/** Why it failed: `feed_no_key`, `feed_missing_headers`. */
|
|
191
|
+
reason?: string;
|
|
192
|
+
missingHeaders?: string[];
|
|
193
|
+
rows: number;
|
|
194
|
+
errors: {
|
|
195
|
+
line: number;
|
|
196
|
+
message: string;
|
|
197
|
+
}[];
|
|
198
|
+
/** Offers updated; `unchanged` of them only had their freshness renewed. */
|
|
199
|
+
updated: number;
|
|
200
|
+
unchanged: number;
|
|
201
|
+
gone: number;
|
|
202
|
+
jumps: {
|
|
203
|
+
offerId: string;
|
|
204
|
+
fromCents: number;
|
|
205
|
+
toCents: number;
|
|
206
|
+
percent: number;
|
|
207
|
+
}[];
|
|
208
|
+
unmatched: FeedUnmatched[];
|
|
209
|
+
/** More lines than `unmatched` holds. */
|
|
210
|
+
unmatchedTotal: number;
|
|
211
|
+
applied: number;
|
|
212
|
+
pendingCount: number;
|
|
213
|
+
importJob?: FeedImportStatus;
|
|
214
|
+
startedAt: number;
|
|
215
|
+
finishedAt?: number;
|
|
216
|
+
createdBy: string;
|
|
217
|
+
createdAt?: number;
|
|
218
|
+
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { TransactionKind } from '../transaction/kinds';
|
|
2
|
+
import { PurchaseStatus } from './types';
|
|
2
3
|
/**
|
|
3
4
|
* What `apps/purchase` issues: the same engine as a quote, its own table. `sent` is issued (a
|
|
4
5
|
* number and the paper); the supplier confirms, and the goods coming in make it `received`.
|
|
5
6
|
* Walking back one step stays allowed — a return reopens a received order.
|
|
6
7
|
*/
|
|
7
|
-
export declare const PURCHASE_KINDS: readonly TransactionKind[];
|
|
8
|
+
export declare const PURCHASE_KINDS: readonly TransactionKind<PurchaseStatus>[];
|
|
8
9
|
export declare const PURCHASE_ORDER_KIND = "purchase_order";
|
|
@@ -1,21 +1,39 @@
|
|
|
1
|
+
import { ProductTracking } from '../catalog/types';
|
|
1
2
|
import { PricePer } from '../money/period-price';
|
|
2
|
-
import { GoodsReceipt, GoodsReceiptLine, PurchaseLine, SupplierOffer } from './types';
|
|
3
|
+
import { GoodsReceipt, GoodsReceiptLine, InvoiceState, PurchaseLine, SupplierOffer } from './types';
|
|
3
4
|
/** What came in for one order line: booked receipts in, returns out. */
|
|
4
5
|
export declare function receivedFor(poLineId: string, receipts: readonly GoodsReceipt[]): number;
|
|
5
6
|
export type ReceiptState = "none" | "partial" | "full";
|
|
6
7
|
/** Over the product lines of an order. */
|
|
7
|
-
export declare function receiptState(lines: readonly Pick<PurchaseLine, "kind" | "quantity" | "receivedQty">[]): "none" | "
|
|
8
|
+
export declare function receiptState(lines: readonly Pick<PurchaseLine, "kind" | "quantity" | "receivedQty">[]): "none" | "full" | "partial";
|
|
9
|
+
type Billable = Pick<PurchaseLine, "kind" | "quantity" | "billedQty" | "act" | "unitPriceCents">;
|
|
10
|
+
/** A supplier invoice is expected for this line: a buy, or a change that costs something. */
|
|
11
|
+
export declare const expectsInvoice: (line: Billable) => boolean;
|
|
12
|
+
/** `full` when every line that expects an invoice is billed; derived by whoever writes the lines. */
|
|
13
|
+
export declare function invoiceState(lines: readonly Billable[]): InvoiceState;
|
|
8
14
|
export interface GoodsReceiptProblem {
|
|
9
|
-
code: "receipt_over_ordered" | "receipt_return_over_received" | "receipt_serials_count";
|
|
15
|
+
code: "receipt_over_ordered" | "receipt_return_over_received" | "receipt_serials_count" | "receipt_serials_required" | "receipt_batches_count" | "receipt_batches_required";
|
|
10
16
|
poLineId: string;
|
|
11
17
|
}
|
|
12
18
|
/**
|
|
13
19
|
* 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 in base units. No serials is fine
|
|
15
|
-
* must be all.
|
|
20
|
+
* numbers that do not match the count in base units. No serials is fine for bulk goods; some
|
|
21
|
+
* serials must be all. A serial article (046 `tracking`) comes in with every serial: without them
|
|
22
|
+
* a drop shipment can never be delivered (Exact v2 nr. 8). A batch article likewise with batches
|
|
23
|
+
* that add up, in base units.
|
|
24
|
+
*/
|
|
25
|
+
export declare function goodsReceiptProblems(receipt: Pick<GoodsReceipt, "movement" | "lines">, poLines: readonly Pick<PurchaseLine, "id" | "quantity" | "factor" | "productId">[], booked: readonly GoodsReceipt[], tracking?: ReadonlyMap<string, ProductTracking>): GoodsReceiptProblem[];
|
|
26
|
+
/** `<productId>#<serial>` for every serial on these lines: a serial is unique per article. */
|
|
27
|
+
export declare function serialKeysOf(lines: readonly Pick<GoodsReceiptLine, "poLineId" | "serials">[], poLines: readonly Pick<PurchaseLine, "id" | "productId">[]): string[];
|
|
28
|
+
/**
|
|
29
|
+
* Which of `keys` are in house already: booked in more often than sent back, over every receipt
|
|
30
|
+
* (any order). A serial that went back may come in again: a swap after a repair.
|
|
31
|
+
*/
|
|
32
|
+
export declare function serialsInHouse(keys: readonly string[], receipts: readonly Pick<GoodsReceipt, "movement" | "status" | "serialKeys">[]): string[];
|
|
33
|
+
/**
|
|
34
|
+
* Lines of a receipt worth keeping: a positive quantity, serials trimmed and unique, batches with
|
|
35
|
+
* a code and a positive quantity (base units).
|
|
16
36
|
*/
|
|
17
|
-
export declare function goodsReceiptProblems(receipt: Pick<GoodsReceipt, "movement" | "lines">, poLines: readonly Pick<PurchaseLine, "id" | "quantity" | "factor">[], booked: readonly GoodsReceipt[]): GoodsReceiptProblem[];
|
|
18
|
-
/** Lines of a receipt worth keeping: a positive quantity, serials trimmed and unique. */
|
|
19
37
|
export declare function cleanReceiptLines(lines: readonly GoodsReceiptLine[]): GoodsReceiptLine[];
|
|
20
38
|
/** Valid on `at`. */
|
|
21
39
|
export declare function offerValid(offer: Pick<SupplierOffer, "validFrom" | "validTo">, at: number): boolean;
|
|
@@ -28,6 +46,12 @@ export declare function defaultOffer<T extends SupplierOffer>(offers: readonly T
|
|
|
28
46
|
export declare function toProductUnits(source: {
|
|
29
47
|
factor?: number;
|
|
30
48
|
}, qty: number): number;
|
|
49
|
+
/**
|
|
50
|
+
* Base units to purchase units: a box of 10 for 12 pieces is 2 boxes. Raised to the offer's
|
|
51
|
+
* `minQty` (Exact v2 §5), then to a multiple of its `lotSize`; what is bought beyond the need goes
|
|
52
|
+
* to stock.
|
|
53
|
+
*/
|
|
54
|
+
export declare function purchaseUnits(base: number, factor: number | undefined, offer?: Pick<SupplierOffer, "minQty" | "lotSize">): number;
|
|
31
55
|
/**
|
|
32
56
|
* The offer's cost per product unit, as amount, currency and period — what is written back to the
|
|
33
57
|
* product. Never a monthly equivalent: € 222 per year stays € 222 per year.
|
|
@@ -39,3 +63,4 @@ export declare function productCost(offer: SupplierOffer): {
|
|
|
39
63
|
};
|
|
40
64
|
/** Offers side by side: each over one year, per product unit. A one-off stays itself. */
|
|
41
65
|
export declare function costOverYear(offer: SupplierOffer): number;
|
|
66
|
+
export {};
|
|
@@ -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?:
|
|
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
|