@sixtyninecommerce/admin 0.8.0 → 0.10.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/README.md +2739 -17
- package/dist/api-tokens/index.d.ts +15 -5
- package/dist/api-tokens/index.js +1 -0
- package/dist/apps/event-subscription.d.ts +59 -0
- package/dist/apps/event-subscription.js +32 -0
- package/dist/apps/hook-secret.d.ts +22 -0
- package/dist/apps/hook-secret.js +36 -0
- package/dist/apps/index.d.ts +54 -3
- package/dist/apps/index.js +17 -0
- package/dist/audit/index.d.ts +7 -0
- package/dist/auth/queries.d.ts +5 -1
- package/dist/auth/queries.js +6 -0
- package/dist/bulk/index.d.ts +323 -0
- package/dist/bulk/index.js +162 -0
- package/dist/catalog/assets.d.ts +147 -0
- package/dist/catalog/assets.js +164 -0
- package/dist/catalog/collections.d.ts +109 -0
- package/dist/catalog/collections.js +91 -0
- package/dist/catalog/facets.d.ts +103 -0
- package/dist/catalog/facets.js +100 -0
- package/dist/catalog/index.d.ts +115 -0
- package/dist/catalog/index.js +21 -0
- package/dist/catalog/options.d.ts +89 -0
- package/dist/catalog/options.js +88 -0
- package/dist/catalog/products-bulk.d.ts +21 -0
- package/dist/catalog/products-bulk.js +58 -0
- package/dist/catalog/products.d.ts +650 -0
- package/dist/catalog/products.js +458 -0
- package/dist/catalog/properties.d.ts +234 -0
- package/dist/catalog/properties.js +158 -0
- package/dist/catalog/types.d.ts +174 -0
- package/dist/catalog/types.js +141 -0
- package/dist/catalog/variants.d.ts +324 -0
- package/dist/catalog/variants.js +288 -0
- package/dist/catalog/visibility.d.ts +57 -0
- package/dist/catalog/visibility.js +59 -0
- package/dist/client.d.ts +192 -3
- package/dist/client.js +87 -12
- package/dist/compliance/index.d.ts +88 -0
- package/dist/compliance/index.js +88 -0
- package/dist/customer-data-redaction.d.ts +14 -0
- package/dist/customer-data-redaction.js +22 -0
- package/dist/customers/index.d.ts +428 -17
- package/dist/customers/index.js +322 -15
- package/dist/documents/index.d.ts +513 -16
- package/dist/documents/index.js +229 -3
- package/dist/events/index.d.ts +259 -0
- package/dist/events/index.js +147 -0
- package/dist/feeds/ai.d.ts +46 -0
- package/dist/feeds/ai.js +43 -0
- package/dist/feeds/categories.d.ts +50 -0
- package/dist/feeds/categories.js +56 -0
- package/dist/feeds/configs.d.ts +54 -0
- package/dist/feeds/configs.js +85 -0
- package/dist/feeds/identifier-fields.d.ts +25 -0
- package/dist/feeds/identifier-fields.js +29 -0
- package/dist/feeds/index.d.ts +54 -0
- package/dist/feeds/index.js +46 -0
- package/dist/feeds/preserve.d.ts +38 -0
- package/dist/feeds/preserve.js +74 -0
- package/dist/feeds/rules.d.ts +42 -0
- package/dist/feeds/rules.js +56 -0
- package/dist/feeds/types.d.ts +654 -0
- package/dist/feeds/types.js +135 -0
- package/dist/fulfillments/index.d.ts +437 -0
- package/dist/fulfillments/index.js +534 -0
- package/dist/heads/index.d.ts +60 -0
- package/dist/heads/index.js +45 -0
- package/dist/hooks/assignments.d.ts +90 -0
- package/dist/hooks/assignments.js +53 -0
- package/dist/hooks/index.d.ts +97 -0
- package/dist/hooks/index.js +76 -0
- package/dist/index.d.ts +95 -9
- package/dist/index.js +21 -1
- package/dist/integrations/index.d.ts +14 -0
- package/dist/integrations/index.js +9 -0
- package/dist/integrations/pipelines.d.ts +113 -0
- package/dist/integrations/pipelines.js +62 -0
- package/dist/integrations/types.d.ts +176 -0
- package/dist/integrations/types.js +8 -0
- package/dist/jobs/bulk-action-run.d.ts +74 -0
- package/dist/jobs/bulk-action-run.js +69 -0
- package/dist/locations/index.d.ts +6 -2
- package/dist/meta/index.d.ts +32 -0
- package/dist/orders/bulk.d.ts +75 -0
- package/dist/orders/bulk.js +150 -0
- package/dist/orders/commit-edit.d.ts +123 -0
- package/dist/orders/commit-edit.js +142 -0
- package/dist/orders/deposit-policy.d.ts +103 -0
- package/dist/orders/deposit-policy.js +202 -0
- package/dist/orders/get.d.ts +722 -0
- package/dist/orders/get.js +530 -0
- package/dist/orders/index.d.ts +186 -21
- package/dist/orders/index.js +57 -0
- package/dist/orders/line-price-preview.d.ts +297 -0
- package/dist/orders/line-price-preview.js +50 -0
- package/dist/orders/manual-discounts.d.ts +141 -0
- package/dist/orders/manual-discounts.js +97 -0
- package/dist/orders/manual-pricing.d.ts +192 -0
- package/dist/orders/manual-pricing.js +72 -0
- package/dist/orders/order-entry.d.ts +566 -0
- package/dist/orders/order-entry.js +129 -0
- package/dist/orders/parcel-charge.d.ts +45 -0
- package/dist/orders/parcel-charge.js +55 -0
- package/dist/orders/plan-edit.d.ts +489 -0
- package/dist/orders/plan-edit.js +93 -0
- package/dist/orders/status.d.ts +235 -0
- package/dist/orders/status.js +96 -0
- package/dist/orders/surcharges.d.ts +75 -2
- package/dist/orders/surcharges.js +110 -4
- package/dist/payments/account-credit.d.ts +171 -0
- package/dist/payments/account-credit.js +107 -0
- package/dist/payments/index.d.ts +241 -9
- package/dist/payments/index.js +92 -0
- package/dist/payments/methods.d.ts +37 -0
- package/dist/payments/methods.js +33 -0
- package/dist/platform/catalog.d.ts +148 -0
- package/dist/platform/catalog.js +74 -0
- package/dist/platform/entitlements.d.ts +111 -0
- package/dist/platform/entitlements.js +86 -0
- package/dist/platform/legal-entities.d.ts +139 -0
- package/dist/platform/legal-entities.js +126 -0
- package/dist/platform/legal-entity-numbering.d.ts +103 -0
- package/dist/platform/legal-entity-numbering.js +101 -0
- package/dist/platform/organization-membership.d.ts +80 -0
- package/dist/platform/organization-membership.js +71 -0
- package/dist/platform/organization-settings.d.ts +33 -0
- package/dist/platform/organization-settings.js +42 -0
- package/dist/platform/organization-sharing.d.ts +172 -0
- package/dist/platform/organization-sharing.js +120 -0
- package/dist/platform/organizations.d.ts +205 -0
- package/dist/platform/organizations.js +180 -0
- package/dist/platform/provision-store.d.ts +88 -8
- package/dist/platform/provision-store.js +12 -0
- package/dist/platform/reports.d.ts +46 -0
- package/dist/platform/reports.js +31 -0
- package/dist/platform/server-metrics.d.ts +58 -0
- package/dist/platform/server-metrics.js +11 -0
- package/dist/pos/index.d.ts +567 -0
- package/dist/pos/index.js +205 -0
- package/dist/pricing/index.d.ts +408 -0
- package/dist/pricing/index.js +353 -0
- package/dist/pricing/vouchers.d.ts +103 -0
- package/dist/pricing/vouchers.js +69 -0
- package/dist/provenance/index.d.ts +93 -0
- package/dist/provenance/index.js +53 -0
- package/dist/reports/index.d.ts +199 -0
- package/dist/reports/index.js +154 -0
- package/dist/returns/index.d.ts +225 -0
- package/dist/returns/index.js +160 -0
- package/dist/search/index.d.ts +99 -0
- package/dist/search/index.js +62 -0
- package/dist/shipping/parcel-count-suggestion.d.ts +52 -0
- package/dist/shipping/parcel-count-suggestion.js +73 -0
- package/dist/shipping/parcel-rule.d.ts +48 -0
- package/dist/shipping/parcel-rule.js +91 -0
- package/dist/stock/index.d.ts +166 -0
- package/dist/stock/index.js +115 -0
- package/dist/stock/transfers.d.ts +520 -0
- package/dist/stock/transfers.js +437 -0
- package/dist/stored-value/index.d.ts +154 -0
- package/dist/stored-value/index.js +130 -0
- package/dist/tenant/capabilities.d.ts +10 -0
- package/dist/tenant/entitlements.d.ts +35 -0
- package/dist/tenant/entitlements.js +15 -0
- package/dist/tenant/field-groups.d.ts +64 -0
- package/dist/tenant/field-groups.js +34 -0
- package/dist/tenant/legal-entity.d.ts +32 -0
- package/dist/tenant/legal-entity.js +41 -0
- package/dist/tenant/notifications.d.ts +148 -0
- package/dist/tenant/notifications.js +79 -0
- package/dist/tenant/retention.d.ts +10 -4
- package/dist/tenant/settings-sources.d.ts +103 -0
- package/dist/tenant/settings-sources.js +92 -0
- package/dist/types.d.ts +86 -11
- package/dist/uploads/index.d.ts +11 -7
- package/dist/users/index.d.ts +104 -0
- package/dist/users/index.js +80 -0
- package/package.json +3 -2
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Its own document on purpose: `previewOrderLinePrices` exists only on an
|
|
3
|
+
* engine with the line price preview, so on an older one this call alone fails
|
|
4
|
+
* validation and no other order call does.
|
|
5
|
+
*/
|
|
6
|
+
export const PREVIEW_ORDER_LINE_PRICES_QUERY = /* GraphQL */ `
|
|
7
|
+
query PreviewOrderLinePrices($input: PreviewOrderLinePricesInput!) {
|
|
8
|
+
previewOrderLinePrices(input: $input) {
|
|
9
|
+
lineId
|
|
10
|
+
productVariantId
|
|
11
|
+
purchasable
|
|
12
|
+
notPurchasableCode
|
|
13
|
+
chargedUnitPrice
|
|
14
|
+
chargedUnitPriceWithTax
|
|
15
|
+
basis
|
|
16
|
+
basisKnown
|
|
17
|
+
retailBase
|
|
18
|
+
catalogueChanged
|
|
19
|
+
stacking {
|
|
20
|
+
source
|
|
21
|
+
amount
|
|
22
|
+
}
|
|
23
|
+
overrideInert
|
|
24
|
+
taxProvisional
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
`;
|
|
28
|
+
export function createOrderLinePricePreviewModule(transport) {
|
|
29
|
+
return {
|
|
30
|
+
async previewLinePrices(orderId, lines, options) {
|
|
31
|
+
const data = await transport.graphql({
|
|
32
|
+
api: 'admin',
|
|
33
|
+
query: PREVIEW_ORDER_LINE_PRICES_QUERY,
|
|
34
|
+
variables: {
|
|
35
|
+
input: {
|
|
36
|
+
orderId,
|
|
37
|
+
// The lines and `orderDiscount` go out as given, never
|
|
38
|
+
// normalised: left out, `null` and a value are three different
|
|
39
|
+
// requests to the engine (keep, clear, stage). `JSON.stringify`
|
|
40
|
+
// drops a key that is `undefined` and keeps a `null`, so adding
|
|
41
|
+
// a `?? null` or a default here would turn "keep" into "clear".
|
|
42
|
+
lines,
|
|
43
|
+
orderDiscount: options?.orderDiscount,
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
}, options);
|
|
47
|
+
return data.previewOrderLinePrices;
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import type { Transport } from '@sixtyninecommerce/core';
|
|
2
|
+
import type { Call } from '../types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Manual discounts with a reason (incentives spec §4 decision 6): a store's list of discount
|
|
5
|
+
* reasons, its discount policy, and the log of the price movements the two pricing doors made on
|
|
6
|
+
* an order. The discounts themselves are given through those two doors: a manual unit price
|
|
7
|
+
* (`orders.setLinePrices`, the Admin API's `applyOrderLinePriceOverrides`) and one-time pricing
|
|
8
|
+
* (`orders.setOneTimeDiscounts`, `setOrderPriceListDiscounts`). Each takes a reason code, and a
|
|
9
|
+
* call that lowers a line's price follows the store's policy.
|
|
10
|
+
*
|
|
11
|
+
* WHO GAVE IT is never an argument: a token's own user is recorded, and the staff member and
|
|
12
|
+
* device it acts for are the `x-on-behalf-of` / `x-device-ref` refs the SDK already sends from
|
|
13
|
+
* `tenant.actor` (`TenantContext.actor`). The platform records them only when the credential is
|
|
14
|
+
* a machine token or an app installation, which it vouches for.
|
|
15
|
+
*/
|
|
16
|
+
/** One of the store's reasons for a manual discount. */
|
|
17
|
+
export interface DiscountReason {
|
|
18
|
+
id: string;
|
|
19
|
+
/** Stable code the store chose: lower-case letters, digits, `-` and `_`. */
|
|
20
|
+
code: string;
|
|
21
|
+
label: string;
|
|
22
|
+
/** An inactive reason stays on the records that name it and is refused for new ones. */
|
|
23
|
+
active: boolean;
|
|
24
|
+
createdAt: string;
|
|
25
|
+
updatedAt: string;
|
|
26
|
+
}
|
|
27
|
+
export interface DiscountReasonInput {
|
|
28
|
+
code: string;
|
|
29
|
+
label: string;
|
|
30
|
+
/** Omitted keeps the stored state; a new reason starts active. */
|
|
31
|
+
active?: boolean;
|
|
32
|
+
}
|
|
33
|
+
/** The store's rule for the manual discount doors. */
|
|
34
|
+
export interface DiscountPolicy {
|
|
35
|
+
/**
|
|
36
|
+
* When true, a call that lowers a line's pre-promotion unit price needs one of the store's
|
|
37
|
+
* active reasons for that line. A till discount (`pos.sale.applyDiscount`) is not held to it.
|
|
38
|
+
*/
|
|
39
|
+
reasonRequired: boolean;
|
|
40
|
+
/**
|
|
41
|
+
* The store's ceiling on manual discounts in basis points (1000 = 10 %), or `null` for none: how
|
|
42
|
+
* far below the price the customer would get anyway (its contract included) staff without the
|
|
43
|
+
* `OverrideDiscountLimit` permission may take a line (`DISCOUNT_OVER_LIMIT`). Reducing a
|
|
44
|
+
* discount is never refused. The POS till is not held to it.
|
|
45
|
+
*/
|
|
46
|
+
maxDiscountBps: number | null;
|
|
47
|
+
}
|
|
48
|
+
export interface DiscountPolicyInput {
|
|
49
|
+
/** Omitted keeps the stored value. */
|
|
50
|
+
reasonRequired?: boolean;
|
|
51
|
+
/** 0..10000 basis points; omitted keeps the stored value, `null` removes the ceiling. */
|
|
52
|
+
maxDiscountBps?: number | null;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* One price movement a manual-discount door made on an order. Append-only. Each row is a price
|
|
56
|
+
* movement that door made; the sum of an order's rows is the net manual price movement recorded
|
|
57
|
+
* since the layer was enabled, not a statement of what discount is in force.
|
|
58
|
+
*/
|
|
59
|
+
export interface OrderManualDiscount {
|
|
60
|
+
id: string;
|
|
61
|
+
createdAt: string;
|
|
62
|
+
orderId: string;
|
|
63
|
+
/** The line the movement was on; `null` for a till discount on the whole cart. */
|
|
64
|
+
orderLineId: string | null;
|
|
65
|
+
/**
|
|
66
|
+
* `price_list`, `line_override` or `till` (`pos.sale.applyDiscount`, one row per call). Accept a
|
|
67
|
+
* value you do not know.
|
|
68
|
+
*/
|
|
69
|
+
door: string;
|
|
70
|
+
/**
|
|
71
|
+
* The change of the line's total, net: (unit price after − before) × quantity, minor units of
|
|
72
|
+
* `currencyCode`, as the order holds it. Negative when the price fell (a manual discount). For
|
|
73
|
+
* `till`: the money the call's negative surcharges added.
|
|
74
|
+
*/
|
|
75
|
+
amount: number;
|
|
76
|
+
/** The same movement gross. */
|
|
77
|
+
amountWithTax: number;
|
|
78
|
+
currencyCode: string;
|
|
79
|
+
/** The reason the price fell under; `null` when it rose, or fell with none asked. */
|
|
80
|
+
reason: {
|
|
81
|
+
id: string;
|
|
82
|
+
code: string;
|
|
83
|
+
label: string;
|
|
84
|
+
active: boolean;
|
|
85
|
+
} | null;
|
|
86
|
+
/** The Vendure user of the session that moved it. */
|
|
87
|
+
actorUserId: string | null;
|
|
88
|
+
/** The vouched-for `x-on-behalf-of` ref; for a till vouched for nothing, `pos-staff:<id>`. */
|
|
89
|
+
onBehalfOfRef: string | null;
|
|
90
|
+
/** The vouched-for `x-device-ref` ref; for a paired till vouched for nothing, `pos-device:<id>`. */
|
|
91
|
+
deviceRef: string | null;
|
|
92
|
+
/** Shared by the rows of one call. */
|
|
93
|
+
callId: string;
|
|
94
|
+
}
|
|
95
|
+
export interface DiscountReasonsModule {
|
|
96
|
+
/**
|
|
97
|
+
* The store's reasons, active first. Needs `ReadOrder`, `ReadSettings` or `OperatePos` (the
|
|
98
|
+
* `pos` token, whose till sends one with `pos.sale.applyDiscount`).
|
|
99
|
+
*/
|
|
100
|
+
list(options?: Call & {
|
|
101
|
+
includeInactive?: boolean;
|
|
102
|
+
}): Promise<DiscountReason[]>;
|
|
103
|
+
/**
|
|
104
|
+
* Create the reason, or change the label and active flag of the one with this code. A reason
|
|
105
|
+
* is never deleted: retire it with `active: false`. Needs `UpdateSettings`. Refused
|
|
106
|
+
* `DISCOUNT_REASON_CODE_INVALID` / `DISCOUNT_REASON_LABEL_INVALID` (→ `ValidationError`), and
|
|
107
|
+
* `DISCOUNT_REASON_LABEL_CONFLICT` when an active reason would share a label or code with
|
|
108
|
+
* another, so each name means one reason (staff read labels, integrations send codes). Only a
|
|
109
|
+
* `reasonCode` satisfies the policy; a price note is free text and never read as a reason.
|
|
110
|
+
*/
|
|
111
|
+
upsert(input: DiscountReasonInput, options?: Call): Promise<DiscountReason>;
|
|
112
|
+
}
|
|
113
|
+
export interface DiscountPolicyModule {
|
|
114
|
+
/**
|
|
115
|
+
* The store's policy; the default (no reason required) when it never set one. Needs
|
|
116
|
+
* `ReadOrder`, `ReadSettings` or `OperatePos`.
|
|
117
|
+
*/
|
|
118
|
+
get(options?: Call): Promise<DiscountPolicy>;
|
|
119
|
+
/** Needs `UpdateSettings`. */
|
|
120
|
+
update(input: DiscountPolicyInput, options?: Call): Promise<DiscountPolicy>;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* A store's discount reasons and policy, and what an order was given (`ReadOrder`).
|
|
124
|
+
*
|
|
125
|
+
* The policy is applied by the doors that give a manual discount (`orders.setLinePrices`,
|
|
126
|
+
* `orders.setOneTimeDiscounts`), which refuse with `DISCOUNT_REASON_REQUIRED` and
|
|
127
|
+
* `DISCOUNT_REASON_UNKNOWN`. The till's
|
|
128
|
+
* `pos.sale.applyDiscount` records the reason it is given and refuses only an unknown code.
|
|
129
|
+
*/
|
|
130
|
+
export interface ManualDiscountsModule {
|
|
131
|
+
/** Every price movement recorded on the order, newest first. Another store's order answers `[]`. */
|
|
132
|
+
list(orderId: string, options?: Call): Promise<OrderManualDiscount[]>;
|
|
133
|
+
reasons: DiscountReasonsModule;
|
|
134
|
+
policy: DiscountPolicyModule;
|
|
135
|
+
}
|
|
136
|
+
export declare const DISCOUNT_REASONS_QUERY = "\n query DiscountReasons($includeInactive: Boolean) {\n discountReasons(includeInactive: $includeInactive) {\n \n id\n code\n label\n active\n createdAt\n updatedAt\n\n }\n }\n";
|
|
137
|
+
export declare const UPSERT_DISCOUNT_REASON_MUTATION = "\n mutation UpsertDiscountReason($input: UpsertDiscountReasonInput!) {\n upsertDiscountReason(input: $input) {\n \n id\n code\n label\n active\n createdAt\n updatedAt\n\n }\n }\n";
|
|
138
|
+
export declare const DISCOUNT_POLICY_QUERY = "\n query DiscountPolicy {\n discountPolicy {\n reasonRequired\n maxDiscountBps\n }\n }\n";
|
|
139
|
+
export declare const UPDATE_DISCOUNT_POLICY_MUTATION = "\n mutation UpdateDiscountPolicy($input: UpdateDiscountPolicyInput!) {\n updateDiscountPolicy(input: $input) {\n reasonRequired\n maxDiscountBps\n }\n }\n";
|
|
140
|
+
export declare const ORDER_MANUAL_DISCOUNTS_QUERY = "\n query OrderManualDiscounts($orderId: ID!) {\n orderManualDiscounts(orderId: $orderId) {\n \n id\n createdAt\n orderId\n orderLineId\n door\n amount\n amountWithTax\n currencyCode\n reason {\n id\n code\n label\n active\n }\n actorUserId\n onBehalfOfRef\n deviceRef\n callId\n\n }\n }\n";
|
|
141
|
+
export declare function createManualDiscountsModule(transport: Transport): ManualDiscountsModule;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
const REASON_FIELDS = /* GraphQL */ `
|
|
2
|
+
id
|
|
3
|
+
code
|
|
4
|
+
label
|
|
5
|
+
active
|
|
6
|
+
createdAt
|
|
7
|
+
updatedAt
|
|
8
|
+
`;
|
|
9
|
+
const RECORD_FIELDS = /* GraphQL */ `
|
|
10
|
+
id
|
|
11
|
+
createdAt
|
|
12
|
+
orderId
|
|
13
|
+
orderLineId
|
|
14
|
+
door
|
|
15
|
+
amount
|
|
16
|
+
amountWithTax
|
|
17
|
+
currencyCode
|
|
18
|
+
reason {
|
|
19
|
+
id
|
|
20
|
+
code
|
|
21
|
+
label
|
|
22
|
+
active
|
|
23
|
+
}
|
|
24
|
+
actorUserId
|
|
25
|
+
onBehalfOfRef
|
|
26
|
+
deviceRef
|
|
27
|
+
callId
|
|
28
|
+
`;
|
|
29
|
+
export const DISCOUNT_REASONS_QUERY = /* GraphQL */ `
|
|
30
|
+
query DiscountReasons($includeInactive: Boolean) {
|
|
31
|
+
discountReasons(includeInactive: $includeInactive) {
|
|
32
|
+
${REASON_FIELDS}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
`;
|
|
36
|
+
export const UPSERT_DISCOUNT_REASON_MUTATION = /* GraphQL */ `
|
|
37
|
+
mutation UpsertDiscountReason($input: UpsertDiscountReasonInput!) {
|
|
38
|
+
upsertDiscountReason(input: $input) {
|
|
39
|
+
${REASON_FIELDS}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
`;
|
|
43
|
+
export const DISCOUNT_POLICY_QUERY = /* GraphQL */ `
|
|
44
|
+
query DiscountPolicy {
|
|
45
|
+
discountPolicy {
|
|
46
|
+
reasonRequired
|
|
47
|
+
maxDiscountBps
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
`;
|
|
51
|
+
export const UPDATE_DISCOUNT_POLICY_MUTATION = /* GraphQL */ `
|
|
52
|
+
mutation UpdateDiscountPolicy($input: UpdateDiscountPolicyInput!) {
|
|
53
|
+
updateDiscountPolicy(input: $input) {
|
|
54
|
+
reasonRequired
|
|
55
|
+
maxDiscountBps
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
`;
|
|
59
|
+
export const ORDER_MANUAL_DISCOUNTS_QUERY = /* GraphQL */ `
|
|
60
|
+
query OrderManualDiscounts($orderId: ID!) {
|
|
61
|
+
orderManualDiscounts(orderId: $orderId) {
|
|
62
|
+
${RECORD_FIELDS}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
`;
|
|
66
|
+
export function createManualDiscountsModule(transport) {
|
|
67
|
+
return {
|
|
68
|
+
async list(orderId, options) {
|
|
69
|
+
const data = await transport.graphql({ api: 'admin', query: ORDER_MANUAL_DISCOUNTS_QUERY, variables: { orderId } }, options);
|
|
70
|
+
return data.orderManualDiscounts;
|
|
71
|
+
},
|
|
72
|
+
reasons: {
|
|
73
|
+
async list(options) {
|
|
74
|
+
const data = await transport.graphql({
|
|
75
|
+
api: 'admin',
|
|
76
|
+
query: DISCOUNT_REASONS_QUERY,
|
|
77
|
+
variables: { includeInactive: options?.includeInactive },
|
|
78
|
+
}, options);
|
|
79
|
+
return data.discountReasons;
|
|
80
|
+
},
|
|
81
|
+
async upsert(input, options) {
|
|
82
|
+
const data = await transport.graphql({ api: 'admin', query: UPSERT_DISCOUNT_REASON_MUTATION, variables: { input } }, options);
|
|
83
|
+
return data.upsertDiscountReason;
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
policy: {
|
|
87
|
+
async get(options) {
|
|
88
|
+
const data = await transport.graphql({ api: 'admin', query: DISCOUNT_POLICY_QUERY }, options);
|
|
89
|
+
return data.discountPolicy;
|
|
90
|
+
},
|
|
91
|
+
async update(input, options) {
|
|
92
|
+
const data = await transport.graphql({ api: 'admin', query: UPDATE_DISCOUNT_POLICY_MUTATION, variables: { input } }, options);
|
|
93
|
+
return data.updateDiscountPolicy;
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import type { Transport } from '@sixtyninecommerce/core';
|
|
2
|
+
import { type OrderDiscounts } from '../pricing/index.js';
|
|
3
|
+
import type { Call } from '../types.js';
|
|
4
|
+
/**
|
|
5
|
+
* The two doors that move a manual price on an order (incentives spec §4 decision 6): a line's
|
|
6
|
+
* manual unit price (`applyOrderLinePriceOverrides`) and the order's one-time discounts
|
|
7
|
+
* (`setOrderPriceListDiscounts`). Their read twin is `orders.previewLinePrices`, which prices the
|
|
8
|
+
* same changes without saving them; what each call moved is read back with
|
|
9
|
+
* `orders.manualDiscounts.list`.
|
|
10
|
+
*
|
|
11
|
+
* Both run in one transaction, re-price the lines they change, and re-total the order. Both move
|
|
12
|
+
* money with NO refund, so both refuse an order whose payment was collected. Change such an
|
|
13
|
+
* order through an order modification: plan it with `orders.planEdit` (which saves and refunds
|
|
14
|
+
* nothing), then carry it out through the Admin API's `modifyOrder` directly, which issues the
|
|
15
|
+
* refund. This SDK has no method for that step until `orders.commitEdit` ships.
|
|
16
|
+
*/
|
|
17
|
+
/** How a one-time discount is stated (the engine's `OrderPriceListDiscountType`). */
|
|
18
|
+
export type OneTimeDiscountType = 'PERCENTAGE' | 'FIXED_AMOUNT' | 'FIXED_PRICE';
|
|
19
|
+
/** One line's change for {@link OrderManualPricingModule.setLinePrices}. */
|
|
20
|
+
export interface LinePriceInput {
|
|
21
|
+
orderLineId: string;
|
|
22
|
+
/**
|
|
23
|
+
* The manual unit price, a whole number of minor units, 0 or more, in the LINE's tax basis:
|
|
24
|
+
* gross when the line's `listPriceIncludesTax` is true (`orders.get`), net otherwise. That is
|
|
25
|
+
* the `basis` `orders.previewLinePrices` answers for the line, and it is not always the store's:
|
|
26
|
+
* a line priced in a customer-country tax zone is net on a store that prices gross. The engine
|
|
27
|
+
* takes no basis with it and converts nothing.
|
|
28
|
+
*
|
|
29
|
+
* Three states: left out keeps the stored price (a note-only change), `null` clears it and
|
|
30
|
+
* re-prices the line from the catalogue, a number sets it.
|
|
31
|
+
*/
|
|
32
|
+
unitPriceOverride?: number | null;
|
|
33
|
+
/** A free-text note on the price. Left out keeps the stored one; `null` or `''` clears it. */
|
|
34
|
+
priceOverrideReason?: string | null;
|
|
35
|
+
/**
|
|
36
|
+
* One of the store's active discount reasons (`manualDiscounts.reasons`), by code, for this
|
|
37
|
+
* line when the call lowers its price. Wins over the call's `reasonCode` for this line.
|
|
38
|
+
*/
|
|
39
|
+
reasonCode?: string | null;
|
|
40
|
+
}
|
|
41
|
+
export interface SetLinePricesInput {
|
|
42
|
+
/** At least one line; a line once per call. Lines not named keep the price they have. */
|
|
43
|
+
lines: LinePriceInput[];
|
|
44
|
+
/** The reason for every line whose price the call lowers and that names none of its own. */
|
|
45
|
+
reasonCode?: string | null;
|
|
46
|
+
}
|
|
47
|
+
/** A one-time discount on one line's product variant. */
|
|
48
|
+
export interface OneTimeLineDiscountInput {
|
|
49
|
+
orderLineId: string;
|
|
50
|
+
discountType: OneTimeDiscountType;
|
|
51
|
+
/**
|
|
52
|
+
* A whole number. `PERCENTAGE`: basis points, 1 to 10000. `FIXED_AMOUNT`: minor units off each
|
|
53
|
+
* unit, 1 or more. `FIXED_PRICE`: the unit price itself, minor units, 0 or more. The two money
|
|
54
|
+
* kinds are in the STORE's tax basis (as its catalogue prices are entered), which is not always
|
|
55
|
+
* the line's, and in the order's currency.
|
|
56
|
+
*/
|
|
57
|
+
discountValue: number;
|
|
58
|
+
/** One of the store's active discount reasons, by code, for this line when its price falls. */
|
|
59
|
+
reasonCode?: string | null;
|
|
60
|
+
}
|
|
61
|
+
/** The whole-order one-time discount: every live line without an entry of its own. */
|
|
62
|
+
export interface OneTimeOrderDiscountInput {
|
|
63
|
+
/** `FIXED_PRICE` is refused: one absolute price for every product is never what is meant. */
|
|
64
|
+
discountType: Exclude<OneTimeDiscountType, 'FIXED_PRICE'>;
|
|
65
|
+
/**
|
|
66
|
+
* `PERCENTAGE`: basis points, 1 to 10000. `FIXED_AMOUNT`: minor units in the store's tax basis
|
|
67
|
+
* taken off EVERY UNIT, 1 or more. An amount off the order total is a surcharge
|
|
68
|
+
* (`orders.surcharges.add` with a negative price), not this.
|
|
69
|
+
*/
|
|
70
|
+
discountValue: number;
|
|
71
|
+
/**
|
|
72
|
+
* One of the store's active discount reasons, for every line this discount covers whose price
|
|
73
|
+
* falls. Wins over the call's `reasonCode` for those lines.
|
|
74
|
+
*/
|
|
75
|
+
reasonCode?: string | null;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* The COMPLETE one-time pricing the order should end with. Not a change: a live line that is not
|
|
79
|
+
* in `lines` has no line discount afterwards, and `orderDiscount: null` removes the whole-order
|
|
80
|
+
* one. To keep what the order has, send it again (`orders.get(id, { include: ['priceList'] })`
|
|
81
|
+
* reads it).
|
|
82
|
+
*/
|
|
83
|
+
export interface SetOneTimeDiscountsInput {
|
|
84
|
+
/** Required, so that removing it is said out loud: `null` for no whole-order discount. */
|
|
85
|
+
orderDiscount: OneTimeOrderDiscountInput | null;
|
|
86
|
+
/** Every line discount the order should have. An empty list removes them all. */
|
|
87
|
+
lines: OneTimeLineDiscountInput[];
|
|
88
|
+
/** A free-text note kept once for the order. Left out or `null` keeps it; `''` clears it. */
|
|
89
|
+
reason?: string | null;
|
|
90
|
+
/** The reason for every line whose price the call lowers and that no other `reasonCode` covers. */
|
|
91
|
+
reasonCode?: string | null;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* The two manual pricing doors. Each answers the order as the engine left it, read with the same
|
|
95
|
+
* fields as `pricing.discounts(orderId)`: every line's `unitPrice` / `unitPriceWithTax` is its
|
|
96
|
+
* price before promotions, which is where a manual price or a one-time discount shows (neither is
|
|
97
|
+
* a promotion, so neither appears in `discounts`). The record of what the call moved is
|
|
98
|
+
* `orders.manualDiscounts.list(orderId)`: one row per line whose price changed, with the reason
|
|
99
|
+
* when the price fell.
|
|
100
|
+
*
|
|
101
|
+
* **Reasons.** A line whose price before promotions the call LOWERS received a manual discount.
|
|
102
|
+
* When the store requires a reason (`orders.manualDiscounts.policy`), every such line needs one,
|
|
103
|
+
* from its own entry or from the call. A line whose price does not fall needs none: a raise, a
|
|
104
|
+
* price a contract's fixed price wins over, a note-only change. Only a `reasonCode` counts as a
|
|
105
|
+
* reason; a note is free text.
|
|
106
|
+
*
|
|
107
|
+
* **Idempotency.** Takes `options.idempotencyKey`, as the other money writes do. The first answer
|
|
108
|
+
* under a key is kept for 24 hours and given again to the same request without running it: a
|
|
109
|
+
* success (the order, re-read as it is now) and EVERY refusal with a 4xx status, the doors' own
|
|
110
|
+
* `USER_INPUT_ERROR` guards included (a collected payment, a completed order, a bad value). Only a
|
|
111
|
+
* failure with a 5xx, 408, 425 or 429 status frees the key for a retry. So once you have fixed
|
|
112
|
+
* the cause of a refusal (reopened the order, picked a reason), send the corrected request under a
|
|
113
|
+
* NEW key: the same key on a different request, the other door included, is
|
|
114
|
+
* `IDEMPOTENCY_CONFLICT`.
|
|
115
|
+
*
|
|
116
|
+
* **Coded refusals.** Nothing is written by a refused call.
|
|
117
|
+
* - `DISCOUNT_REASON_REQUIRED` (`BusinessRuleError`, 422): the store requires a reason, the call
|
|
118
|
+
* lowered a line's price, and no reason covers that line. Ask the merchant for one of
|
|
119
|
+
* `manualDiscounts.reasons.list()` and send it on the line or the call.
|
|
120
|
+
* - `DISCOUNT_REASON_UNKNOWN` (`BusinessRuleError`, 422): a code sent, at any level, is not one of
|
|
121
|
+
* the store's active reasons (unknown, retired, or another store's). Checked before anything is
|
|
122
|
+
* written, also on a call that lowers no price.
|
|
123
|
+
* - `IDEMPOTENCY_CONFLICT` (`ConflictError`, 409): the key was used for another request. Use a
|
|
124
|
+
* fresh key per intended write. `IDEMPOTENCY_IN_PROGRESS` (`ConflictError`, 409): the first call
|
|
125
|
+
* under the key is still running; ask again shortly. `IDEMPOTENCY_KEY_INVALID`
|
|
126
|
+
* (`ValidationError`, 400): a key is 1 to 255 characters.
|
|
127
|
+
*
|
|
128
|
+
* **Uncoded refusals.** The doors' own guards are core `UserInputError`s: `ValidationError` (400)
|
|
129
|
+
* with code `USER_INPUT_ERROR` and the engine's sentence, which says what to do. Branch on the
|
|
130
|
+
* class and show the message; the engine gives these no codes of their own. They are:
|
|
131
|
+
* - the payment was collected (a settled payment, or one authorized on a card or online gateway):
|
|
132
|
+
* plan the edit with `orders.planEdit`, then carry it out through the Admin API's `modifyOrder`
|
|
133
|
+
* (no SDK method until `orders.commitEdit`), which refunds the difference. A payment authorized
|
|
134
|
+
* on an offline method (bank transfer, invoice, net terms) collected nothing and does not
|
|
135
|
+
* block. A payment whose method is no longer on the store cannot be judged: restore the method;
|
|
136
|
+
* - the order is invoiced (locked), completed (reopen it first) or cancelled;
|
|
137
|
+
* - the call was made from the default channel instead of the order's own;
|
|
138
|
+
* - the order is not this store's (the same answer as a missing one), or a named line is not on
|
|
139
|
+
* it, has been cancelled, or is named twice.
|
|
140
|
+
*
|
|
141
|
+
* Each method lists the refusals of its own below. An id that is not a uuid is refused before all
|
|
142
|
+
* of this with core's `ENTITY_NOT_FOUND` (`NotFoundError`, 404).
|
|
143
|
+
*
|
|
144
|
+
* Both need `UpdateOrder`, and an engine with the discount-reason layer (one that serves
|
|
145
|
+
* `discountReasons`); on an older one only these two calls fail.
|
|
146
|
+
*/
|
|
147
|
+
export interface OrderManualPricingModule {
|
|
148
|
+
/**
|
|
149
|
+
* Set or clear the manual unit price, or its note, on lines of an order, and re-total it. Only
|
|
150
|
+
* the named lines are re-priced; every other line keeps its price. Taxes and totals are
|
|
151
|
+
* recalculated across the whole order.
|
|
152
|
+
*
|
|
153
|
+
* The manual price is a layer, not the final price: the customer's price list and the order's
|
|
154
|
+
* one-time discounts still apply on top of it, and a contract's fixed price wins over it
|
|
155
|
+
* (`previewLinePrices` answers `overrideInert` then). Setting or clearing a price takes the line
|
|
156
|
+
* out of the "exact price" state an imported, quoted or till-priced line arrives in
|
|
157
|
+
* (`customFields.unitPriceImported`), so those layers apply to it from then on; this door never
|
|
158
|
+
* sets that flag.
|
|
159
|
+
*
|
|
160
|
+
* Refusals of its own (`ValidationError`, `USER_INPUT_ERROR`): an empty `lines`; a line with
|
|
161
|
+
* neither `unitPriceOverride` nor `priceOverrideReason` (it asks for nothing); a negative price;
|
|
162
|
+
* `unitPriceOverride: null` on a line an external system priced (`orders.createOrRecover`
|
|
163
|
+
* external lines), which has no catalogue price to fall back to: state the price instead.
|
|
164
|
+
*/
|
|
165
|
+
setLinePrices(orderId: string, input: SetLinePricesInput, options?: Call): Promise<OrderDiscounts>;
|
|
166
|
+
/**
|
|
167
|
+
* Replace the order's one-time pricing with `input`, its complete desired state, and re-price
|
|
168
|
+
* the lines it affects. A one-time discount is a price list of the order's own, ranked above
|
|
169
|
+
* every other price list and applied on top of a manual unit price. It applies to a line's
|
|
170
|
+
* product VARIANT, so the engine refuses it on a variant that is on two live lines (merge them
|
|
171
|
+
* first). Under a whole-order discount, lines an external system or an import priced exactly
|
|
172
|
+
* and free lines are skipped. Fixed money is in the order's currency at the time of the call.
|
|
173
|
+
* A call that removes everything on an order that has nothing writes nothing.
|
|
174
|
+
*
|
|
175
|
+
* Refusals of its own (`ValidationError`, `USER_INPUT_ERROR`): a value out of range or not a
|
|
176
|
+
* whole number; a `FIXED_PRICE` whole-order discount; a line entry on a line priced exactly
|
|
177
|
+
* (an import, an accepted quote, a till price: give it a manual unit price instead), on a free
|
|
178
|
+
* line, or on a variant that is on another live line; a discount the pricing rules could not
|
|
179
|
+
* apply (a `FIXED_AMOUNT` larger than the unit price is skipped, never clamped, and the message
|
|
180
|
+
* names the SKU); fixed money stored in another currency than the order's now (remove the
|
|
181
|
+
* one-time pricing, then set it again); and two first writes on one order at once (reload the
|
|
182
|
+
* order, then try again).
|
|
183
|
+
*/
|
|
184
|
+
setOneTimeDiscounts(orderId: string, input: SetOneTimeDiscountsInput, options?: Call): Promise<OrderDiscounts>;
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Each door its own document: the reason and key arguments exist only on an engine with the
|
|
188
|
+
* discount-reason layer, so on an older one these calls alone fail validation.
|
|
189
|
+
*/
|
|
190
|
+
export declare const APPLY_ORDER_LINE_PRICE_OVERRIDES_MUTATION = "\n mutation ApplyOrderLinePriceOverrides(\n $orderId: ID!\n $lines: [OrderLinePriceOverrideInput!]!\n $reasonCode: String\n $idempotencyKey: String\n ) {\n applyOrderLinePriceOverrides(\n orderId: $orderId\n lines: $lines\n reasonCode: $reasonCode\n idempotencyKey: $idempotencyKey\n ) {\n \n id\n code\n state\n currencyCode\n couponCodes\n subTotal\n subTotalWithTax\n shipping\n shippingWithTax\n total\n totalWithTax\n discounts { promotionId adjustmentSource type description amount amountWithTax }\n promotions { id name }\n lines {\n id\n quantity\n productVariant { id sku name }\n unitPrice\n unitPriceWithTax\n linePrice\n linePriceWithTax\n discountedLinePrice\n discountedLinePriceWithTax\n proratedLinePrice\n proratedLinePriceWithTax\n discounts { promotionId adjustmentSource type description amount amountWithTax }\n }\n surcharges { id description sku price priceWithTax }\n packages { promotionId label price priceWithTax lineIds quantity }\n\n }\n }\n";
|
|
191
|
+
export declare const SET_ORDER_PRICE_LIST_DISCOUNTS_MUTATION = "\n mutation SetOrderPriceListDiscounts(\n $input: SetOrderPriceListDiscountsInput!\n $idempotencyKey: String\n ) {\n setOrderPriceListDiscounts(input: $input, idempotencyKey: $idempotencyKey) {\n \n id\n code\n state\n currencyCode\n couponCodes\n subTotal\n subTotalWithTax\n shipping\n shippingWithTax\n total\n totalWithTax\n discounts { promotionId adjustmentSource type description amount amountWithTax }\n promotions { id name }\n lines {\n id\n quantity\n productVariant { id sku name }\n unitPrice\n unitPriceWithTax\n linePrice\n linePriceWithTax\n discountedLinePrice\n discountedLinePriceWithTax\n proratedLinePrice\n proratedLinePriceWithTax\n discounts { promotionId adjustmentSource type description amount amountWithTax }\n }\n surcharges { id description sku price priceWithTax }\n packages { promotionId label price priceWithTax lineIds quantity }\n\n }\n }\n";
|
|
192
|
+
export declare function createOrderManualPricingModule(transport: Transport): OrderManualPricingModule;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { ORDER_DISCOUNTS_FIELDS } from '../pricing/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Each door its own document: the reason and key arguments exist only on an engine with the
|
|
4
|
+
* discount-reason layer, so on an older one these calls alone fail validation.
|
|
5
|
+
*/
|
|
6
|
+
export const APPLY_ORDER_LINE_PRICE_OVERRIDES_MUTATION = /* GraphQL */ `
|
|
7
|
+
mutation ApplyOrderLinePriceOverrides(
|
|
8
|
+
$orderId: ID!
|
|
9
|
+
$lines: [OrderLinePriceOverrideInput!]!
|
|
10
|
+
$reasonCode: String
|
|
11
|
+
$idempotencyKey: String
|
|
12
|
+
) {
|
|
13
|
+
applyOrderLinePriceOverrides(
|
|
14
|
+
orderId: $orderId
|
|
15
|
+
lines: $lines
|
|
16
|
+
reasonCode: $reasonCode
|
|
17
|
+
idempotencyKey: $idempotencyKey
|
|
18
|
+
) {
|
|
19
|
+
${ORDER_DISCOUNTS_FIELDS}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
`;
|
|
23
|
+
export const SET_ORDER_PRICE_LIST_DISCOUNTS_MUTATION = /* GraphQL */ `
|
|
24
|
+
mutation SetOrderPriceListDiscounts(
|
|
25
|
+
$input: SetOrderPriceListDiscountsInput!
|
|
26
|
+
$idempotencyKey: String
|
|
27
|
+
) {
|
|
28
|
+
setOrderPriceListDiscounts(input: $input, idempotencyKey: $idempotencyKey) {
|
|
29
|
+
${ORDER_DISCOUNTS_FIELDS}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
`;
|
|
33
|
+
export function createOrderManualPricingModule(transport) {
|
|
34
|
+
return {
|
|
35
|
+
async setLinePrices(orderId, input, options) {
|
|
36
|
+
const data = await transport.graphql({
|
|
37
|
+
api: 'admin',
|
|
38
|
+
query: APPLY_ORDER_LINE_PRICE_OVERRIDES_MUTATION,
|
|
39
|
+
// The lines go out as given, never normalised: a `unitPriceOverride` left out keeps the
|
|
40
|
+
// stored price and `null` clears it. `JSON.stringify` drops a key that is `undefined`
|
|
41
|
+
// and keeps a `null`, so a default here would turn "keep" into "clear". The same holds
|
|
42
|
+
// for the reason code and the key: absent, they never reach the wire as null.
|
|
43
|
+
variables: {
|
|
44
|
+
orderId,
|
|
45
|
+
lines: input.lines,
|
|
46
|
+
reasonCode: input.reasonCode,
|
|
47
|
+
idempotencyKey: options?.idempotencyKey,
|
|
48
|
+
},
|
|
49
|
+
}, options);
|
|
50
|
+
return data.applyOrderLinePriceOverrides;
|
|
51
|
+
},
|
|
52
|
+
async setOneTimeDiscounts(orderId, input, options) {
|
|
53
|
+
const data = await transport.graphql({
|
|
54
|
+
api: 'admin',
|
|
55
|
+
query: SET_ORDER_PRICE_LIST_DISCOUNTS_MUTATION,
|
|
56
|
+
variables: {
|
|
57
|
+
input: {
|
|
58
|
+
orderId,
|
|
59
|
+
// The engine reads a missing whole-order discount as none, so the request states it:
|
|
60
|
+
// the wire then carries the whole desired state, `null` included.
|
|
61
|
+
orderDiscount: input.orderDiscount ?? null,
|
|
62
|
+
lines: input.lines,
|
|
63
|
+
reason: input.reason,
|
|
64
|
+
reasonCode: input.reasonCode,
|
|
65
|
+
},
|
|
66
|
+
idempotencyKey: options?.idempotencyKey,
|
|
67
|
+
},
|
|
68
|
+
}, options);
|
|
69
|
+
return data.setOrderPriceListDiscounts;
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
}
|