@sixtyninecommerce/admin 0.10.0 → 0.11.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 +4347 -171
- package/dist/catalog/assets.d.ts +120 -8
- package/dist/catalog/assets.js +98 -12
- package/dist/catalog/collections.d.ts +279 -14
- package/dist/catalog/collections.js +246 -15
- package/dist/catalog/index.d.ts +9 -2
- package/dist/catalog/index.js +4 -0
- package/dist/catalog/products.d.ts +159 -19
- package/dist/catalog/products.js +88 -13
- package/dist/catalog/properties.d.ts +102 -14
- package/dist/catalog/properties.js +13 -0
- package/dist/catalog/recommendations.d.ts +261 -0
- package/dist/catalog/recommendations.js +145 -0
- package/dist/catalog/reviews.d.ts +368 -0
- package/dist/catalog/reviews.js +318 -0
- package/dist/catalog/types.d.ts +17 -1
- package/dist/catalog/types.js +10 -0
- package/dist/catalog/variant-find.d.ts +136 -0
- package/dist/catalog/variant-find.js +239 -0
- package/dist/catalog/variants.d.ts +233 -26
- package/dist/catalog/variants.js +125 -7
- package/dist/catalog/visibility.d.ts +20 -5
- package/dist/catalog/visibility.js +19 -1
- package/dist/client.d.ts +95 -9
- package/dist/client.js +35 -7
- package/dist/content/assets.d.ts +286 -0
- package/dist/content/assets.js +181 -0
- package/dist/content/items.d.ts +269 -0
- package/dist/content/items.js +170 -0
- package/dist/content/types.d.ts +282 -0
- package/dist/content/types.js +100 -0
- package/dist/customers/age-verification.d.ts +66 -0
- package/dist/customers/age-verification.js +79 -0
- package/dist/customers/credit.d.ts +263 -0
- package/dist/customers/credit.js +244 -0
- package/dist/customers/delivery-preferences.d.ts +120 -0
- package/dist/customers/delivery-preferences.js +97 -0
- package/dist/customers/groups.d.ts +207 -0
- package/dist/customers/groups.js +438 -0
- package/dist/customers/index.d.ts +447 -37
- package/dist/customers/index.js +313 -40
- package/dist/customers/invitation-status.d.ts +57 -0
- package/dist/customers/invitation-status.js +21 -0
- package/dist/customers/notes.d.ts +67 -0
- package/dist/customers/notes.js +51 -0
- package/dist/customers/notification-recipients.d.ts +97 -0
- package/dist/customers/notification-recipients.js +84 -0
- package/dist/customers/saved-lists.d.ts +42 -0
- package/dist/customers/saved-lists.js +61 -0
- package/dist/customers/vat.d.ts +83 -0
- package/dist/customers/vat.js +46 -0
- package/dist/customers/wire.d.ts +20 -0
- package/dist/customers/wire.js +35 -0
- package/dist/documents/ar.d.ts +307 -0
- package/dist/documents/ar.js +254 -0
- package/dist/documents/index.d.ts +731 -21
- package/dist/documents/index.js +436 -24
- package/dist/documents/pdf.d.ts +2 -0
- package/dist/documents/pdf.js +11 -0
- package/dist/fulfillments/create.d.ts +167 -0
- package/dist/fulfillments/create.js +216 -0
- package/dist/fulfillments/index.d.ts +44 -10
- package/dist/fulfillments/index.js +11 -2
- package/dist/fulfillments/payment-policy.d.ts +64 -0
- package/dist/fulfillments/payment-policy.js +140 -0
- package/dist/fulfillments/warehouse.d.ts +724 -0
- package/dist/fulfillments/warehouse.js +914 -0
- package/dist/geo/index.d.ts +119 -0
- package/dist/geo/index.js +107 -0
- package/dist/index.d.ts +123 -35
- package/dist/index.js +45 -12
- package/dist/integrations/afas-actions.d.ts +234 -0
- package/dist/integrations/afas-actions.js +310 -0
- package/dist/integrations/afas-types.d.ts +483 -0
- package/dist/integrations/afas-types.js +5 -0
- package/dist/integrations/afas.d.ts +234 -0
- package/dist/integrations/afas.js +290 -0
- package/dist/integrations/connections.d.ts +103 -0
- package/dist/integrations/connections.js +55 -0
- package/dist/integrations/esp.d.ts +313 -0
- package/dist/integrations/esp.js +169 -0
- package/dist/integrations/exact-online-types.d.ts +423 -0
- package/dist/integrations/exact-online-types.js +6 -0
- package/dist/integrations/exact-online.d.ts +248 -0
- package/dist/integrations/exact-online.js +376 -0
- package/dist/integrations/index.d.ts +29 -3
- package/dist/integrations/index.js +18 -3
- package/dist/integrations/pipeline-keys.d.ts +13 -0
- package/dist/integrations/pipeline-keys.js +22 -0
- package/dist/integrations/pipelines.d.ts +15 -14
- package/dist/integrations/pipelines.js +6 -22
- package/dist/integrations/types.d.ts +146 -0
- package/dist/locations/index.d.ts +66 -0
- package/dist/locations/index.js +31 -0
- package/dist/meta/index.d.ts +162 -0
- package/dist/orders/cancel-with-money.d.ts +227 -0
- package/dist/orders/cancel-with-money.js +153 -0
- package/dist/orders/commit-edit.d.ts +7 -1
- package/dist/orders/commit-edit.js +5 -1
- package/dist/orders/drafts.d.ts +800 -0
- package/dist/orders/drafts.js +771 -0
- package/dist/orders/fields.d.ts +245 -0
- package/dist/orders/fields.js +188 -0
- package/dist/orders/get.d.ts +29 -19
- package/dist/orders/get.js +17 -8
- package/dist/orders/index.d.ts +49 -9
- package/dist/orders/index.js +22 -2
- package/dist/orders/lifecycle.d.ts +128 -0
- package/dist/orders/lifecycle.js +158 -0
- package/dist/orders/line-price-preview.d.ts +7 -2
- package/dist/orders/list.d.ts +222 -0
- package/dist/orders/list.js +377 -0
- package/dist/orders/manual-pricing.d.ts +5 -0
- package/dist/orders/order-entry.d.ts +20 -7
- package/dist/orders/plan-edit.d.ts +1 -1
- package/dist/orders/plan-edit.js +4 -1
- package/dist/orders/settings.d.ts +201 -0
- package/dist/orders/settings.js +325 -0
- package/dist/orders/status.d.ts +159 -13
- package/dist/orders/status.js +21 -0
- package/dist/orders/surcharges.d.ts +8 -1
- package/dist/orders/surcharges.js +3 -2
- package/dist/payments/account-credit.d.ts +30 -0
- package/dist/payments/account-credit.js +19 -0
- package/dist/payments/ar.d.ts +292 -0
- package/dist/payments/ar.js +223 -0
- package/dist/payments/connected-account.d.ts +47 -0
- package/dist/payments/connected-account.js +61 -0
- package/dist/payments/credit-noted-fields.d.ts +12 -0
- package/dist/payments/credit-noted-fields.js +37 -0
- package/dist/payments/index.d.ts +257 -11
- package/dist/payments/index.js +131 -1
- package/dist/payments/methods.d.ts +303 -8
- package/dist/payments/methods.js +337 -7
- package/dist/payments/mollie-methods.d.ts +159 -0
- package/dist/payments/mollie-methods.js +141 -0
- package/dist/payments/payment-links.d.ts +180 -0
- package/dist/payments/payment-links.js +97 -0
- package/dist/payments/provider-keys.d.ts +150 -0
- package/dist/payments/provider-keys.js +118 -0
- package/dist/payments/providers.d.ts +86 -11
- package/dist/payments/providers.js +29 -0
- package/dist/payments/requests.d.ts +144 -0
- package/dist/payments/requests.js +92 -0
- package/dist/payments/store-credit-fields.d.ts +15 -0
- package/dist/payments/store-credit-fields.js +51 -0
- package/dist/pickups/index.d.ts +104 -0
- package/dist/pickups/index.js +105 -0
- package/dist/platform/channels.d.ts +132 -0
- package/dist/platform/channels.js +128 -0
- package/dist/platform/geo.d.ts +123 -0
- package/dist/platform/geo.js +144 -0
- package/dist/platform/tax.d.ts +154 -0
- package/dist/platform/tax.js +139 -0
- package/dist/pos/anomalies.d.ts +158 -0
- package/dist/pos/anomalies.js +64 -0
- package/dist/pos/assisted-selling.d.ts +79 -0
- package/dist/pos/assisted-selling.js +38 -0
- package/dist/pos/exchanges.d.ts +103 -0
- package/dist/pos/exchanges.js +22 -0
- package/dist/pos/gift-cards.d.ts +135 -0
- package/dist/pos/gift-cards.js +47 -0
- package/dist/pos/index.d.ts +2125 -32
- package/dist/pos/index.js +742 -4
- package/dist/pos/no-receipt.d.ts +94 -0
- package/dist/pos/no-receipt.js +25 -0
- package/dist/pos/offline-sales.d.ts +187 -0
- package/dist/pos/offline-sales.js +116 -0
- package/dist/pos/pay-by-link.d.ts +143 -0
- package/dist/pos/pay-by-link.js +57 -0
- package/dist/pos/quick-keys.d.ts +103 -0
- package/dist/pos/quick-keys.js +45 -0
- package/dist/pos/reports.d.ts +360 -0
- package/dist/pos/reports.js +62 -0
- package/dist/pos/returns.d.ts +217 -0
- package/dist/pos/returns.js +53 -0
- package/dist/pricing/bulk-coupons.d.ts +139 -0
- package/dist/pricing/bulk-coupons.js +112 -0
- package/dist/pricing/index.d.ts +174 -4
- package/dist/pricing/index.js +174 -1
- package/dist/pricing/price-lists.d.ts +604 -0
- package/dist/pricing/price-lists.js +600 -0
- package/dist/purchasing/alerts.d.ts +89 -0
- package/dist/purchasing/alerts.js +62 -0
- package/dist/purchasing/cost.d.ts +110 -0
- package/dist/purchasing/cost.js +86 -0
- package/dist/purchasing/index.d.ts +36 -0
- package/dist/purchasing/index.js +16 -0
- package/dist/purchasing/purchase-orders.d.ts +548 -0
- package/dist/purchasing/purchase-orders.js +491 -0
- package/dist/purchasing/replenishment.d.ts +281 -0
- package/dist/purchasing/replenishment.js +240 -0
- package/dist/purchasing/settings.d.ts +152 -0
- package/dist/purchasing/settings.js +181 -0
- package/dist/purchasing/suppliers.d.ts +476 -0
- package/dist/purchasing/suppliers.js +438 -0
- package/dist/quotes/index.d.ts +501 -0
- package/dist/quotes/index.js +439 -0
- package/dist/reports/index.d.ts +71 -8
- package/dist/reports/index.js +74 -11
- package/dist/returns/index.d.ts +614 -21
- package/dist/returns/index.js +695 -11
- package/dist/search/index.d.ts +76 -1
- package/dist/search/index.js +63 -0
- package/dist/shipping/definitions.d.ts +29 -0
- package/dist/shipping/definitions.js +47 -0
- package/dist/shipping/fulfillment-profiles.d.ts +174 -0
- package/dist/shipping/fulfillment-profiles.js +104 -0
- package/dist/shipping/hendriks.d.ts +97 -0
- package/dist/shipping/hendriks.js +50 -0
- package/dist/shipping/index.d.ts +48 -0
- package/dist/shipping/index.js +25 -0
- package/dist/shipping/methods.d.ts +198 -0
- package/dist/shipping/methods.js +240 -0
- package/dist/shipping/myparcel.d.ts +254 -0
- package/dist/shipping/myparcel.js +178 -0
- package/dist/shipping/parcel-count-suggestion.d.ts +2 -0
- package/dist/shipping/parcel-rule.d.ts +6 -5
- package/dist/shipping/parcel-rule.js +21 -23
- package/dist/shipping/rate-tables.d.ts +179 -0
- package/dist/shipping/rate-tables.js +184 -0
- package/dist/stock/index.d.ts +10 -3
- package/dist/stock/index.js +3 -0
- package/dist/stock/locations.d.ts +144 -0
- package/dist/stock/locations.js +168 -0
- package/dist/stock/transfers.d.ts +216 -43
- package/dist/stock/transfers.js +140 -13
- package/dist/stored-value/index.d.ts +214 -4
- package/dist/stored-value/index.js +152 -0
- package/dist/subscriptions/index.d.ts +682 -0
- package/dist/subscriptions/index.js +815 -0
- package/dist/tax/index.d.ts +76 -1
- package/dist/tax/index.js +50 -0
- package/dist/tenant/email-style.d.ts +102 -0
- package/dist/tenant/email-style.js +72 -0
- package/dist/tenant/email-templates.d.ts +66 -0
- package/dist/tenant/email-templates.js +54 -0
- package/dist/tenant/notifications.d.ts +23 -17
- package/dist/tenant/settings-sources.d.ts +1 -1
- package/dist/tenant/time-zone.d.ts +17 -0
- package/dist/tenant/time-zone.js +11 -0
- package/dist/types.d.ts +50 -3
- package/dist/users/index.d.ts +3 -0
- package/dist/users/index.js +2 -0
- package/dist/users/warehouses.d.ts +50 -0
- package/dist/users/warehouses.js +51 -0
- package/package.json +2 -2
package/dist/orders/plan-edit.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { storeCreditAware } from '../payments/store-credit-fields.js';
|
|
1
2
|
/**
|
|
2
3
|
* Its own document on purpose: `planOrderEdit` exists only on an engine with
|
|
3
4
|
* the order-edit plan, so on an older one this call alone fails validation and
|
|
@@ -26,11 +27,13 @@ export const PLAN_ORDER_EDIT_QUERY = /* GraphQL */ `
|
|
|
26
27
|
available
|
|
27
28
|
code
|
|
28
29
|
message
|
|
30
|
+
book
|
|
29
31
|
}
|
|
30
32
|
accountCredit {
|
|
31
33
|
available
|
|
32
34
|
code
|
|
33
35
|
message
|
|
36
|
+
book
|
|
34
37
|
}
|
|
35
38
|
refundWhenChosen {
|
|
36
39
|
paymentId
|
|
@@ -77,7 +80,7 @@ export const PLAN_ORDER_EDIT_QUERY = /* GraphQL */ `
|
|
|
77
80
|
export function createOrderPlanEditModule(transport) {
|
|
78
81
|
return {
|
|
79
82
|
async planEdit(orderId, diff, options) {
|
|
80
|
-
const data = await transport.graphql({
|
|
83
|
+
const data = await storeCreditAware(transport).graphql({
|
|
81
84
|
api: 'admin',
|
|
82
85
|
query: PLAN_ORDER_EDIT_QUERY,
|
|
83
86
|
// The diff goes out as given, never normalised: `orderDiscount` left
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import { type Transport } from '@sixtyninecommerce/core';
|
|
2
|
+
import type { SettingsSourceKind } from '../tenant/settings-sources.js';
|
|
3
|
+
import type { Call } from '../types.js';
|
|
4
|
+
/** The states an order status rule may move a placed order to. */
|
|
5
|
+
export declare const ORDER_STATUS_RULE_TARGET_STATES: readonly ["AwaitingApproval", "PaymentAuthorized", "PaymentSettled"];
|
|
6
|
+
/** What an order status rule matches on. */
|
|
7
|
+
export declare const ORDER_STATUS_RULE_CONDITION_TYPES: readonly ["product", "coupon", "collection", "facet_value"];
|
|
8
|
+
/** The fewest zero-padded digits of a sequential order number. */
|
|
9
|
+
export declare const ORDER_CODE_DIGITS_MIN = 4;
|
|
10
|
+
/** The most zero-padded digits of a sequential order number. */
|
|
11
|
+
export declare const ORDER_CODE_DIGITS_MAX = 8;
|
|
12
|
+
/**
|
|
13
|
+
* The longest order-number prefix. The engine stores more, but every order code is
|
|
14
|
+
* `NAMESPACE-PREFIX00001` in a 255-character column: a long prefix fails EVERY order placement.
|
|
15
|
+
*/
|
|
16
|
+
export declare const ORDER_CODE_PREFIX_MAX_LENGTH = 20;
|
|
17
|
+
/** The youngest minimum purchase age (the engine checks no age below 1: `minimumAge > 0`). */
|
|
18
|
+
export declare const MINIMUM_AGE_MIN = 1;
|
|
19
|
+
/** The oldest minimum purchase age the platform accepts. */
|
|
20
|
+
export declare const MINIMUM_AGE_MAX = 99;
|
|
21
|
+
/**
|
|
22
|
+
* The largest amount (minor units) the minimum and the thresholds hold: the columns are Vendure
|
|
23
|
+
* `int` custom fields, GraphQL `Int` (32 bits). ₩30,000,000 is 3,000,000,000 hundredths, past it.
|
|
24
|
+
*/
|
|
25
|
+
export declare const ORDER_SETTINGS_AMOUNT_MAX = 2147483647;
|
|
26
|
+
export type OrderStatusRuleTargetState = (typeof ORDER_STATUS_RULE_TARGET_STATES)[number];
|
|
27
|
+
export type OrderStatusRuleConditionType = (typeof ORDER_STATUS_RULE_CONDITION_TYPES)[number];
|
|
28
|
+
/**
|
|
29
|
+
* A rule that moves an order into `targetState` on its FIRST placement when it matches. Rules run
|
|
30
|
+
* in array order and the first enabled match wins; a rule whose list for its `conditionType` is
|
|
31
|
+
* empty never fires.
|
|
32
|
+
*/
|
|
33
|
+
export interface OrderStatusRule {
|
|
34
|
+
id: string;
|
|
35
|
+
/** For people only. `''` for a stored rule that has none: the engine fires it all the same. */
|
|
36
|
+
name: string;
|
|
37
|
+
targetState: OrderStatusRuleTargetState;
|
|
38
|
+
conditionType: OrderStatusRuleConditionType;
|
|
39
|
+
/** Read for `product`. */
|
|
40
|
+
productVariantIds?: string[];
|
|
41
|
+
/** Read for `coupon`, compared without case. */
|
|
42
|
+
couponCodes?: string[];
|
|
43
|
+
/** Read for `collection`. */
|
|
44
|
+
collectionIds?: string[];
|
|
45
|
+
/** Read for `facet_value`. */
|
|
46
|
+
facetValueIds?: string[];
|
|
47
|
+
enabled: boolean;
|
|
48
|
+
}
|
|
49
|
+
/** Where the minimum purchase age the engine applies comes from (`settings.age_verification`). */
|
|
50
|
+
export interface MinimumAgeSource {
|
|
51
|
+
/** `channel`: the store's own `minimumAge`; `organization`: its organization's. */
|
|
52
|
+
source: SettingsSourceKind | (string & {});
|
|
53
|
+
/**
|
|
54
|
+
* The organization locked the group: {@link OrderSettingsModule.set} refuses a changed
|
|
55
|
+
* `minimumAge` with `MANAGED_BY_ORGANIZATION`, SuperAdmins included.
|
|
56
|
+
*/
|
|
57
|
+
locked: boolean;
|
|
58
|
+
/** The organization shares the group without locking it. */
|
|
59
|
+
inheritable: boolean;
|
|
60
|
+
/** The store keeps its own value although the organization shares the group. */
|
|
61
|
+
overridden: boolean;
|
|
62
|
+
/** The age the engine applies to this store's checkout; null when none. */
|
|
63
|
+
effective: number | null;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* The ACTIVE store's order settings, as its channel stores them. Every amount is in minor units
|
|
67
|
+
* (hundredths, for every currency: ¥5,000 is 500000, as the platform stores all money) of
|
|
68
|
+
* {@link OrderSettings.currencyCode}, the store's default currency.
|
|
69
|
+
*/
|
|
70
|
+
export interface OrderSettings {
|
|
71
|
+
/** The store these are the settings of (its channel id). */
|
|
72
|
+
channelId: string;
|
|
73
|
+
/** The store's default currency (ISO 4217): the minimum and both thresholds are in it. */
|
|
74
|
+
currencyCode: string;
|
|
75
|
+
/**
|
|
76
|
+
* The least an order may cost (with tax) before it can be placed; null when there is no
|
|
77
|
+
* minimum. An order in another currency is held to it converted at the checkout's rate.
|
|
78
|
+
*/
|
|
79
|
+
minOrderAmount: number | null;
|
|
80
|
+
/** Customer groups whose members are not held to the minimum. */
|
|
81
|
+
exemptCustomerGroupIds: string[];
|
|
82
|
+
/** Collections whose items do not count toward the minimum. */
|
|
83
|
+
exemptCollectionIds: string[];
|
|
84
|
+
/** Placed orders get sequential numbers (`NAMESPACE-PREFIX00001`); off: random codes. */
|
|
85
|
+
orderCodeEnabled: boolean;
|
|
86
|
+
/**
|
|
87
|
+
* The store's part of every sequential order number, set by the platform (from the store's
|
|
88
|
+
* code) and never by a caller; read-only here. Null until sequential numbers were first on.
|
|
89
|
+
*/
|
|
90
|
+
orderCodeNamespace: string | null;
|
|
91
|
+
/** The merchant's part after the namespace, e.g. `VIN-`; null for none. */
|
|
92
|
+
orderCodePrefix: string | null;
|
|
93
|
+
/** How many zero-padded digits the number has, 4 to 8; null is the platform's 5. */
|
|
94
|
+
orderCodeDigits: number | null;
|
|
95
|
+
/**
|
|
96
|
+
* The store's OWN minimum purchase age, what {@link OrderSettingsModule.set} writes; null for
|
|
97
|
+
* none. While the store takes `settings.age_verification` from its organization the engine
|
|
98
|
+
* applies the organization's instead: see {@link OrderSettings.minimumAgeSource}.
|
|
99
|
+
*/
|
|
100
|
+
minimumAge: number | null;
|
|
101
|
+
minimumAgeSource: MinimumAgeSource;
|
|
102
|
+
/**
|
|
103
|
+
* The rules the engine applies, read as the engine reads them: an entry without a string `id`,
|
|
104
|
+
* a known `targetState` and a known `conditionType` is left out (the engine never fires it);
|
|
105
|
+
* a rule without a name reads `name: ''`, its `enabled` as the engine takes it, and its lists
|
|
106
|
+
* as their non-empty strings.
|
|
107
|
+
*/
|
|
108
|
+
orderStatusRules: OrderStatusRule[];
|
|
109
|
+
/** A customer's balance above this shows as a warning; null for no warning tier. */
|
|
110
|
+
outstandingBalanceWarningThreshold: number | null;
|
|
111
|
+
/** A customer's balance above this shows as critical; null for no critical tier. */
|
|
112
|
+
outstandingBalanceCriticalThreshold: number | null;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* A PATCH of {@link OrderSettings}: a key left out is unchanged, `null` clears it. At least one
|
|
116
|
+
* key. There is no `orderCodeNamespace`: the platform sets it (ruling G125).
|
|
117
|
+
*/
|
|
118
|
+
export interface UpdateOrderSettingsInput {
|
|
119
|
+
/** Whole minor units, 0 to {@link ORDER_SETTINGS_AMOUNT_MAX}; null for no minimum. */
|
|
120
|
+
minOrderAmount?: number | null;
|
|
121
|
+
/** The whole list (it replaces the stored one); empty for none. */
|
|
122
|
+
exemptCustomerGroupIds?: string[];
|
|
123
|
+
/** The whole list (it replaces the stored one); empty for none. */
|
|
124
|
+
exemptCollectionIds?: string[];
|
|
125
|
+
/**
|
|
126
|
+
* Switching it on for a store that never had sequential numbers also sets its namespace to the
|
|
127
|
+
* store's code in capitals, as provisioning does for a new store.
|
|
128
|
+
*/
|
|
129
|
+
orderCodeEnabled?: boolean;
|
|
130
|
+
/** At most 20 characters; null for none. */
|
|
131
|
+
orderCodePrefix?: string | null;
|
|
132
|
+
/** A whole number from 4 to 8; null for the platform's 5. */
|
|
133
|
+
orderCodeDigits?: number | null;
|
|
134
|
+
/** A whole number of years from 1 to 99; null for no age check. */
|
|
135
|
+
minimumAge?: number | null;
|
|
136
|
+
/** The whole list, in priority order (it replaces the stored one); empty for none. */
|
|
137
|
+
orderStatusRules?: OrderStatusRule[];
|
|
138
|
+
/** Whole minor units, 0 to {@link ORDER_SETTINGS_AMOUNT_MAX}; null for no warning tier. */
|
|
139
|
+
outstandingBalanceWarningThreshold?: number | null;
|
|
140
|
+
/** Whole minor units, 0 to {@link ORDER_SETTINGS_AMOUNT_MAX}; null for no critical tier. */
|
|
141
|
+
outstandingBalanceCriticalThreshold?: number | null;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* `admin.orders.settings`: the ACTIVE store's order settings (Settings → Orders): its order
|
|
145
|
+
* minimum and who is exempt from it, how its orders are numbered, the minimum purchase age, the
|
|
146
|
+
* rules that route a placed order into a state, and the outstanding-balance warning tiers. Stored
|
|
147
|
+
* as Channel custom fields; written through core `updateChannel` (typed per domain, ruling G80).
|
|
148
|
+
*
|
|
149
|
+
* The deposit policy (`orders.depositPolicy`) and the fulfilment payment policy
|
|
150
|
+
* (`fulfillments.paymentPolicy`) have doors of their own.
|
|
151
|
+
*/
|
|
152
|
+
export interface OrderSettingsModule {
|
|
153
|
+
/**
|
|
154
|
+
* The settings as the engine applies them, with where the minimum age comes from. Reading
|
|
155
|
+
* needs an administrator of the store with any permission beyond `Authenticated`.
|
|
156
|
+
*
|
|
157
|
+
* @effect read
|
|
158
|
+
*/
|
|
159
|
+
get(options?: Call): Promise<OrderSettings>;
|
|
160
|
+
/**
|
|
161
|
+
* Change the settings `patch` names, and answer them all as the engine now stores them.
|
|
162
|
+
*
|
|
163
|
+
* Changing the minimum or its exemptions changes which orders the store accepts from the next
|
|
164
|
+
* checkout on; orders already placed keep the rule they were placed under.
|
|
165
|
+
*
|
|
166
|
+
* **Coded refusals:**
|
|
167
|
+
* - `ORDER_SETTINGS_INVALID` → {@link ValidationError} (400), before any request: an empty
|
|
168
|
+
* patch; a key that is not an order setting (`orderCodeNamespace` included: the platform
|
|
169
|
+
* sets it); an amount that is not a whole number of minor units from 0 to
|
|
170
|
+
* {@link ORDER_SETTINGS_AMOUNT_MAX} (the column is a 32-bit `Int`); digits outside
|
|
171
|
+
* 4–8; a prefix longer than 20 characters; an age outside 1–99; a list that is not an array.
|
|
172
|
+
* One `details[]` entry per failing key, `field` naming it.
|
|
173
|
+
* - `CHANNEL_SETTINGS_INVALID` → {@link ValidationError} (400), from the engine: an exemption
|
|
174
|
+
* list that is not distinct non-empty ids (at most 255 characters encoded, about six ids),
|
|
175
|
+
* a malformed status rule, or a critical threshold not above the warning threshold
|
|
176
|
+
* (`code: 'MUST_EXCEED_WARNING'`). `details[].field` is the input path, such as
|
|
177
|
+
* `customFields.outstandingBalanceCriticalThreshold`; `details[].message` is the engine's
|
|
178
|
+
* sentence, with the amounts in the store's currency.
|
|
179
|
+
* - `MANAGED_BY_ORGANIZATION` → `BusinessRuleError` (422): a changed `minimumAge` while the
|
|
180
|
+
* organization locks `settings.age_verification`.
|
|
181
|
+
* - `ORGANIZATION_SETTINGS_INHERITED` → `BusinessRuleError` (422): a changed `minimumAge`
|
|
182
|
+
* while the store inherits the group; `tenant.settingsSources.override` it first. Sending
|
|
183
|
+
* the stored value back unchanged is accepted and changes nothing.
|
|
184
|
+
* - `FORBIDDEN` → `PermissionDeniedError` (403): the credential lacks `UpdateChannel`.
|
|
185
|
+
*
|
|
186
|
+
* @effect write
|
|
187
|
+
* @permission UpdateChannel
|
|
188
|
+
*/
|
|
189
|
+
set(patch: UpdateOrderSettingsInput, options?: Call): Promise<OrderSettings>;
|
|
190
|
+
}
|
|
191
|
+
export declare const ORDER_SETTINGS_QUERY = "\n query OrderSettings {\n activeChannel { \n id\n code\n defaultCurrencyCode\n customFields {\n minOrderAmount\n exemptCustomerGroupIds\n exemptCollectionIds\n orderCodeNamespace\n orderCodePrefix\n orderCodeDigits\n orderCodeEnabled\n minimumAge\n orderStatusRules\n outstandingBalanceWarningThreshold\n outstandingBalanceCriticalThreshold\n }\n }\n channelSettingsSources { group source locked overridden inheritable values }\n }\n";
|
|
192
|
+
export declare const UPDATE_ORDER_SETTINGS_MUTATION = "\n mutation UpdateOrderSettings($input: UpdateChannelInput!) {\n updateChannel(input: $input) {\n ... on Channel { \n id\n code\n defaultCurrencyCode\n customFields {\n minOrderAmount\n exemptCustomerGroupIds\n exemptCollectionIds\n orderCodeNamespace\n orderCodePrefix\n orderCodeDigits\n orderCodeEnabled\n minimumAge\n orderStatusRules\n outstandingBalanceWarningThreshold\n outstandingBalanceCriticalThreshold\n }\n }\n ... on ErrorResult { errorCode message }\n }\n }\n";
|
|
193
|
+
/**
|
|
194
|
+
* The column as the engine's `parseRules` keeps it: an entry with a string `id`, a known
|
|
195
|
+
* `targetState` and a known `conditionType`. A rule the engine keeps is never dropped here (a
|
|
196
|
+
* stricter reader would erase it on the next save, turning off the transition it performs), so
|
|
197
|
+
* a missing `name` reads `''` and `enabled` reads as the engine takes it (truthy fires). Only the
|
|
198
|
+
* rule's own keys come back, so the list can be sent back as the engine's schema wants it.
|
|
199
|
+
*/
|
|
200
|
+
export declare function readOrderStatusRules(raw: unknown): OrderStatusRule[];
|
|
201
|
+
export declare function createOrderSettingsModule(transport: Transport): OrderSettingsModule;
|
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
import { errorFromErrorResult, isErrorResult, NotFoundError, ValidationError, } from '@sixtyninecommerce/core';
|
|
2
|
+
// Mirrors, pinned by `__tests__/order-settings.spec.ts`: the engine's `VALID_TARGET_STATES` /
|
|
3
|
+
// `VALID_CONDITION_TYPES` (`order-constraints/order-status-rules-process.ts`) and the shared
|
|
4
|
+
// `ORDER_STATUS_RULE_*` the meta catalog publishes; `ORDER_CODE_DIGITS_MIN/MAX` in
|
|
5
|
+
// `order-code/channel-aware-order-code-strategy.ts`. Change both sides together.
|
|
6
|
+
/** The states an order status rule may move a placed order to. */
|
|
7
|
+
export const ORDER_STATUS_RULE_TARGET_STATES = [
|
|
8
|
+
'AwaitingApproval',
|
|
9
|
+
'PaymentAuthorized',
|
|
10
|
+
'PaymentSettled',
|
|
11
|
+
];
|
|
12
|
+
/** What an order status rule matches on. */
|
|
13
|
+
export const ORDER_STATUS_RULE_CONDITION_TYPES = [
|
|
14
|
+
'product',
|
|
15
|
+
'coupon',
|
|
16
|
+
'collection',
|
|
17
|
+
'facet_value',
|
|
18
|
+
];
|
|
19
|
+
/** The fewest zero-padded digits of a sequential order number. */
|
|
20
|
+
export const ORDER_CODE_DIGITS_MIN = 4;
|
|
21
|
+
/** The most zero-padded digits of a sequential order number. */
|
|
22
|
+
export const ORDER_CODE_DIGITS_MAX = 8;
|
|
23
|
+
/**
|
|
24
|
+
* The longest order-number prefix. The engine stores more, but every order code is
|
|
25
|
+
* `NAMESPACE-PREFIX00001` in a 255-character column: a long prefix fails EVERY order placement.
|
|
26
|
+
*/
|
|
27
|
+
export const ORDER_CODE_PREFIX_MAX_LENGTH = 20;
|
|
28
|
+
/** The youngest minimum purchase age (the engine checks no age below 1: `minimumAge > 0`). */
|
|
29
|
+
export const MINIMUM_AGE_MIN = 1;
|
|
30
|
+
/** The oldest minimum purchase age the platform accepts. */
|
|
31
|
+
export const MINIMUM_AGE_MAX = 99;
|
|
32
|
+
/**
|
|
33
|
+
* The largest amount (minor units) the minimum and the thresholds hold: the columns are Vendure
|
|
34
|
+
* `int` custom fields, GraphQL `Int` (32 bits). ₩30,000,000 is 3,000,000,000 hundredths, past it.
|
|
35
|
+
*/
|
|
36
|
+
export const ORDER_SETTINGS_AMOUNT_MAX = 2_147_483_647;
|
|
37
|
+
/** The settings group `minimumAge` belongs to (multi-region spec §5.3). */
|
|
38
|
+
const AGE_GROUP = 'settings.age_verification';
|
|
39
|
+
const CHANNEL_FIELDS = /* GraphQL */ `
|
|
40
|
+
id
|
|
41
|
+
code
|
|
42
|
+
defaultCurrencyCode
|
|
43
|
+
customFields {
|
|
44
|
+
minOrderAmount
|
|
45
|
+
exemptCustomerGroupIds
|
|
46
|
+
exemptCollectionIds
|
|
47
|
+
orderCodeNamespace
|
|
48
|
+
orderCodePrefix
|
|
49
|
+
orderCodeDigits
|
|
50
|
+
orderCodeEnabled
|
|
51
|
+
minimumAge
|
|
52
|
+
orderStatusRules
|
|
53
|
+
outstandingBalanceWarningThreshold
|
|
54
|
+
outstandingBalanceCriticalThreshold
|
|
55
|
+
}
|
|
56
|
+
`;
|
|
57
|
+
export const ORDER_SETTINGS_QUERY = /* GraphQL */ `
|
|
58
|
+
query OrderSettings {
|
|
59
|
+
activeChannel { ${CHANNEL_FIELDS} }
|
|
60
|
+
channelSettingsSources { group source locked overridden inheritable values }
|
|
61
|
+
}
|
|
62
|
+
`;
|
|
63
|
+
export const UPDATE_ORDER_SETTINGS_MUTATION = /* GraphQL */ `
|
|
64
|
+
mutation UpdateOrderSettings($input: UpdateChannelInput!) {
|
|
65
|
+
updateChannel(input: $input) {
|
|
66
|
+
... on Channel { ${CHANNEL_FIELDS} }
|
|
67
|
+
... on ErrorResult { errorCode message }
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
`;
|
|
71
|
+
const INVALID = 'ORDER_SETTINGS_INVALID';
|
|
72
|
+
const isRecord = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
73
|
+
const isWhole = (value) => typeof value === 'number' && Number.isSafeInteger(value);
|
|
74
|
+
const numberOrNull = (value) => typeof value === 'number' && Number.isFinite(value) ? value : null;
|
|
75
|
+
const stringOrNull = (value) => (typeof value === 'string' ? value : null);
|
|
76
|
+
/** The non-empty strings of `value`, when it is an array; otherwise none. */
|
|
77
|
+
function strings(value) {
|
|
78
|
+
return Array.isArray(value)
|
|
79
|
+
? value.filter((item) => typeof item === 'string' && item.length > 0)
|
|
80
|
+
: [];
|
|
81
|
+
}
|
|
82
|
+
function parsedJson(raw) {
|
|
83
|
+
if (typeof raw !== 'string' || raw === '')
|
|
84
|
+
return null;
|
|
85
|
+
try {
|
|
86
|
+
return JSON.parse(raw);
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* An exemption list as the order-minimum checker reads it: the column's non-empty string ids,
|
|
94
|
+
* once each; anything unreadable is no exemption.
|
|
95
|
+
*/
|
|
96
|
+
function readIdList(raw) {
|
|
97
|
+
return [...new Set(strings(parsedJson(raw)))];
|
|
98
|
+
}
|
|
99
|
+
const LIST_OF = {
|
|
100
|
+
product: 'productVariantIds',
|
|
101
|
+
coupon: 'couponCodes',
|
|
102
|
+
collection: 'collectionIds',
|
|
103
|
+
facet_value: 'facetValueIds',
|
|
104
|
+
};
|
|
105
|
+
const TARGET_STATES = new Set(ORDER_STATUS_RULE_TARGET_STATES);
|
|
106
|
+
const CONDITION_TYPES = new Set(ORDER_STATUS_RULE_CONDITION_TYPES);
|
|
107
|
+
/**
|
|
108
|
+
* The column as the engine's `parseRules` keeps it: an entry with a string `id`, a known
|
|
109
|
+
* `targetState` and a known `conditionType`. A rule the engine keeps is never dropped here (a
|
|
110
|
+
* stricter reader would erase it on the next save, turning off the transition it performs), so
|
|
111
|
+
* a missing `name` reads `''` and `enabled` reads as the engine takes it (truthy fires). Only the
|
|
112
|
+
* rule's own keys come back, so the list can be sent back as the engine's schema wants it.
|
|
113
|
+
*/
|
|
114
|
+
export function readOrderStatusRules(raw) {
|
|
115
|
+
const parsed = parsedJson(raw);
|
|
116
|
+
if (!Array.isArray(parsed))
|
|
117
|
+
return [];
|
|
118
|
+
const rules = [];
|
|
119
|
+
for (const entry of parsed) {
|
|
120
|
+
if (!isRecord(entry))
|
|
121
|
+
continue;
|
|
122
|
+
const { id, targetState, conditionType } = entry;
|
|
123
|
+
if (typeof id !== 'string' || !TARGET_STATES.has(targetState))
|
|
124
|
+
continue;
|
|
125
|
+
if (!CONDITION_TYPES.has(conditionType))
|
|
126
|
+
continue;
|
|
127
|
+
const rule = {
|
|
128
|
+
id,
|
|
129
|
+
name: typeof entry.name === 'string' ? entry.name : '',
|
|
130
|
+
targetState: targetState,
|
|
131
|
+
conditionType: conditionType,
|
|
132
|
+
enabled: Boolean(entry.enabled),
|
|
133
|
+
};
|
|
134
|
+
for (const list of Object.values(LIST_OF)) {
|
|
135
|
+
if (Array.isArray(entry[list]))
|
|
136
|
+
rule[list] = strings(entry[list]);
|
|
137
|
+
}
|
|
138
|
+
rules.push(rule);
|
|
139
|
+
}
|
|
140
|
+
return rules;
|
|
141
|
+
}
|
|
142
|
+
/** An age the engine checks (`minimumAge > 0`), else none. */
|
|
143
|
+
const checkedAge = (age) => (age !== null && age > 0 ? age : null);
|
|
144
|
+
function sourceOf(sources, own) {
|
|
145
|
+
const group = sources.find((item) => item.group === AGE_GROUP);
|
|
146
|
+
if (!group) {
|
|
147
|
+
return {
|
|
148
|
+
source: 'channel',
|
|
149
|
+
locked: false,
|
|
150
|
+
inheritable: false,
|
|
151
|
+
overridden: false,
|
|
152
|
+
effective: checkedAge(own),
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
// The store's own value when its own applies: after `set` the sources are the ones read BEFORE
|
|
156
|
+
// the write, so their values would answer the old age. The organization's values otherwise.
|
|
157
|
+
const applied = group.source !== 'channel' && group.values ? numberOrNull(group.values.minimumAge) : own;
|
|
158
|
+
return {
|
|
159
|
+
source: group.source,
|
|
160
|
+
locked: group.locked,
|
|
161
|
+
inheritable: group.inheritable,
|
|
162
|
+
overridden: group.overridden,
|
|
163
|
+
effective: checkedAge(applied),
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
function settingsOf(channel, sources) {
|
|
167
|
+
const fields = channel.customFields ?? {};
|
|
168
|
+
const minimumAge = numberOrNull(fields.minimumAge);
|
|
169
|
+
return {
|
|
170
|
+
channelId: channel.id,
|
|
171
|
+
currencyCode: channel.defaultCurrencyCode,
|
|
172
|
+
minOrderAmount: numberOrNull(fields.minOrderAmount),
|
|
173
|
+
exemptCustomerGroupIds: readIdList(fields.exemptCustomerGroupIds),
|
|
174
|
+
exemptCollectionIds: readIdList(fields.exemptCollectionIds),
|
|
175
|
+
orderCodeEnabled: fields.orderCodeEnabled === true,
|
|
176
|
+
orderCodeNamespace: stringOrNull(fields.orderCodeNamespace) || null,
|
|
177
|
+
orderCodePrefix: stringOrNull(fields.orderCodePrefix),
|
|
178
|
+
orderCodeDigits: numberOrNull(fields.orderCodeDigits),
|
|
179
|
+
minimumAge,
|
|
180
|
+
minimumAgeSource: sourceOf(sources, minimumAge),
|
|
181
|
+
orderStatusRules: readOrderStatusRules(fields.orderStatusRules),
|
|
182
|
+
outstandingBalanceWarningThreshold: numberOrNull(fields.outstandingBalanceWarningThreshold),
|
|
183
|
+
outstandingBalanceCriticalThreshold: numberOrNull(fields.outstandingBalanceCriticalThreshold),
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
const PATCH_KEYS = [
|
|
187
|
+
'minOrderAmount',
|
|
188
|
+
'exemptCustomerGroupIds',
|
|
189
|
+
'exemptCollectionIds',
|
|
190
|
+
'orderCodeEnabled',
|
|
191
|
+
'orderCodePrefix',
|
|
192
|
+
'orderCodeDigits',
|
|
193
|
+
'minimumAge',
|
|
194
|
+
'orderStatusRules',
|
|
195
|
+
'outstandingBalanceWarningThreshold',
|
|
196
|
+
'outstandingBalanceCriticalThreshold',
|
|
197
|
+
];
|
|
198
|
+
const KNOWN_KEYS = new Set(PATCH_KEYS);
|
|
199
|
+
const AMOUNT_KEYS = [
|
|
200
|
+
'minOrderAmount',
|
|
201
|
+
'outstandingBalanceWarningThreshold',
|
|
202
|
+
'outstandingBalanceCriticalThreshold',
|
|
203
|
+
];
|
|
204
|
+
const LIST_KEYS = ['exemptCustomerGroupIds', 'exemptCollectionIds', 'orderStatusRules'];
|
|
205
|
+
/** Every way `patch` breaks the input's shape, one entry per key. */
|
|
206
|
+
function problemsOf(patch) {
|
|
207
|
+
const problems = [];
|
|
208
|
+
const add = (field, message) => problems.push({ field, code: INVALID, message });
|
|
209
|
+
if (!isRecord(patch)) {
|
|
210
|
+
add('patch', 'Pass the settings to change as an object, e.g. { minOrderAmount: 5000 }.');
|
|
211
|
+
return problems;
|
|
212
|
+
}
|
|
213
|
+
const named = Object.keys(patch).filter((key) => patch[key] !== undefined);
|
|
214
|
+
if (named.length === 0) {
|
|
215
|
+
add('patch', `Name at least one setting to change: ${PATCH_KEYS.join(', ')}.`);
|
|
216
|
+
}
|
|
217
|
+
for (const key of named) {
|
|
218
|
+
if (KNOWN_KEYS.has(key))
|
|
219
|
+
continue;
|
|
220
|
+
add(key, key === 'orderCodeNamespace'
|
|
221
|
+
? 'orderCodeNamespace is set by the platform from the store code, never by a caller: switch orderCodeEnabled on and it is set for you.'
|
|
222
|
+
: `${key} is not an order setting; the settings are ${PATCH_KEYS.join(', ')}.`);
|
|
223
|
+
}
|
|
224
|
+
for (const key of AMOUNT_KEYS) {
|
|
225
|
+
const value = patch[key];
|
|
226
|
+
if (value === undefined || value === null)
|
|
227
|
+
continue;
|
|
228
|
+
if (!(isWhole(value) && value >= 0 && value <= ORDER_SETTINGS_AMOUNT_MAX)) {
|
|
229
|
+
add(key, `${key} must be a whole number of minor units from 0 to ${String(ORDER_SETTINGS_AMOUNT_MAX)}, or null to clear it.`);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
for (const key of LIST_KEYS) {
|
|
233
|
+
const value = patch[key];
|
|
234
|
+
if (value !== undefined && !Array.isArray(value)) {
|
|
235
|
+
add(key, `${key} must be an array (the whole list); send [] for none.`);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
if (patch.orderCodeEnabled !== undefined && typeof patch.orderCodeEnabled !== 'boolean') {
|
|
239
|
+
add('orderCodeEnabled', 'orderCodeEnabled must be true or false.');
|
|
240
|
+
}
|
|
241
|
+
const prefix = patch.orderCodePrefix;
|
|
242
|
+
if (prefix !== undefined &&
|
|
243
|
+
prefix !== null &&
|
|
244
|
+
!(typeof prefix === 'string' && prefix.length <= ORDER_CODE_PREFIX_MAX_LENGTH)) {
|
|
245
|
+
add('orderCodePrefix', `orderCodePrefix must be text of at most ${String(ORDER_CODE_PREFIX_MAX_LENGTH)} characters, or null for none.`);
|
|
246
|
+
}
|
|
247
|
+
const digits = patch.orderCodeDigits;
|
|
248
|
+
if (digits !== undefined &&
|
|
249
|
+
digits !== null &&
|
|
250
|
+
!(isWhole(digits) && digits >= ORDER_CODE_DIGITS_MIN && digits <= ORDER_CODE_DIGITS_MAX)) {
|
|
251
|
+
add('orderCodeDigits', `orderCodeDigits must be a whole number from ${String(ORDER_CODE_DIGITS_MIN)} to ${String(ORDER_CODE_DIGITS_MAX)}, or null for the default.`);
|
|
252
|
+
}
|
|
253
|
+
const age = patch.minimumAge;
|
|
254
|
+
if (age !== undefined &&
|
|
255
|
+
age !== null &&
|
|
256
|
+
!(isWhole(age) && age >= MINIMUM_AGE_MIN && age <= MINIMUM_AGE_MAX)) {
|
|
257
|
+
add('minimumAge', `minimumAge must be a whole number of years from ${String(MINIMUM_AGE_MIN)} to ${String(MINIMUM_AGE_MAX)}, or null for no age check.`);
|
|
258
|
+
}
|
|
259
|
+
return problems;
|
|
260
|
+
}
|
|
261
|
+
/** A list column as the engine stores it: JSON text, or null for an empty list. */
|
|
262
|
+
const listColumn = (list) => list.length > 0 ? JSON.stringify(list) : null;
|
|
263
|
+
/** The custom fields `patch` writes; `before` is the store as read just before. */
|
|
264
|
+
function customFieldsOf(patch, before) {
|
|
265
|
+
const fields = {};
|
|
266
|
+
for (const key of PATCH_KEYS) {
|
|
267
|
+
const value = patch[key];
|
|
268
|
+
if (value === undefined)
|
|
269
|
+
continue;
|
|
270
|
+
fields[key] = Array.isArray(value) ? listColumn(value) : value;
|
|
271
|
+
}
|
|
272
|
+
// Ruling G125: the namespace is the platform's, never the caller's. A store switching
|
|
273
|
+
// sequential numbers on for the first time gets its code in capitals, which is what
|
|
274
|
+
// provisioning gives a new store (`provision-store.saga.ts`) and is unique because codes are.
|
|
275
|
+
const namespace = stringOrNull(before.customFields?.orderCodeNamespace);
|
|
276
|
+
if (patch.orderCodeEnabled === true && !namespace) {
|
|
277
|
+
fields.orderCodeNamespace = before.code.toUpperCase();
|
|
278
|
+
}
|
|
279
|
+
return fields;
|
|
280
|
+
}
|
|
281
|
+
export function createOrderSettingsModule(transport) {
|
|
282
|
+
const read = async (options) => {
|
|
283
|
+
const data = await transport.graphql({ api: 'admin', query: ORDER_SETTINGS_QUERY }, options);
|
|
284
|
+
if (!data.activeChannel) {
|
|
285
|
+
throw new NotFoundError({
|
|
286
|
+
code: 'CHANNEL_NOT_FOUND',
|
|
287
|
+
message: 'No store is active for this session, so it has no order settings to read.',
|
|
288
|
+
status: 404,
|
|
289
|
+
service: 'vendure',
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
return { channel: data.activeChannel, sources: data.channelSettingsSources ?? [] };
|
|
293
|
+
};
|
|
294
|
+
return {
|
|
295
|
+
async get(options) {
|
|
296
|
+
const { channel, sources } = await read(options);
|
|
297
|
+
return settingsOf(channel, sources);
|
|
298
|
+
},
|
|
299
|
+
async set(patch, options) {
|
|
300
|
+
const problems = problemsOf(patch);
|
|
301
|
+
if (problems.length > 0) {
|
|
302
|
+
throw new ValidationError({
|
|
303
|
+
code: INVALID,
|
|
304
|
+
message: problems.map((problem) => problem.message).join(' '),
|
|
305
|
+
status: 400,
|
|
306
|
+
service: 'vendure',
|
|
307
|
+
details: problems,
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
// The id `updateChannel` needs and the namespace rule's "before", read with the SAME call
|
|
311
|
+
// options, so a per-call tenant writes its own store rather than the client's.
|
|
312
|
+
const { channel, sources } = await read(options);
|
|
313
|
+
const data = await transport.graphql({
|
|
314
|
+
api: 'admin',
|
|
315
|
+
query: UPDATE_ORDER_SETTINGS_MUTATION,
|
|
316
|
+
variables: { input: { id: channel.id, customFields: customFieldsOf(patch, channel) } },
|
|
317
|
+
}, options);
|
|
318
|
+
if (isErrorResult(data.updateChannel))
|
|
319
|
+
throw errorFromErrorResult(data.updateChannel);
|
|
320
|
+
// An order settings write never changes where the age comes from, so the sources read
|
|
321
|
+
// just before still hold; `sourceOf` takes the new own value when the store's applies.
|
|
322
|
+
return settingsOf(data.updateChannel, sources);
|
|
323
|
+
},
|
|
324
|
+
};
|
|
325
|
+
}
|