@sixtyninecommerce/admin 0.9.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.
Files changed (258) hide show
  1. package/README.md +4598 -128
  2. package/dist/catalog/assets.d.ts +120 -8
  3. package/dist/catalog/assets.js +98 -12
  4. package/dist/catalog/collections.d.ts +279 -14
  5. package/dist/catalog/collections.js +246 -15
  6. package/dist/catalog/index.d.ts +9 -2
  7. package/dist/catalog/index.js +4 -0
  8. package/dist/catalog/products.d.ts +318 -17
  9. package/dist/catalog/products.js +182 -14
  10. package/dist/catalog/properties.d.ts +102 -14
  11. package/dist/catalog/properties.js +13 -0
  12. package/dist/catalog/recommendations.d.ts +261 -0
  13. package/dist/catalog/recommendations.js +145 -0
  14. package/dist/catalog/reviews.d.ts +368 -0
  15. package/dist/catalog/reviews.js +318 -0
  16. package/dist/catalog/types.d.ts +17 -1
  17. package/dist/catalog/types.js +10 -0
  18. package/dist/catalog/variant-find.d.ts +136 -0
  19. package/dist/catalog/variant-find.js +239 -0
  20. package/dist/catalog/variants.d.ts +282 -25
  21. package/dist/catalog/variants.js +173 -25
  22. package/dist/catalog/visibility.d.ts +20 -5
  23. package/dist/catalog/visibility.js +19 -1
  24. package/dist/client.d.ts +116 -9
  25. package/dist/client.js +41 -7
  26. package/dist/content/assets.d.ts +286 -0
  27. package/dist/content/assets.js +181 -0
  28. package/dist/content/items.d.ts +269 -0
  29. package/dist/content/items.js +170 -0
  30. package/dist/content/types.d.ts +282 -0
  31. package/dist/content/types.js +100 -0
  32. package/dist/customers/age-verification.d.ts +66 -0
  33. package/dist/customers/age-verification.js +79 -0
  34. package/dist/customers/credit.d.ts +263 -0
  35. package/dist/customers/credit.js +244 -0
  36. package/dist/customers/delivery-preferences.d.ts +120 -0
  37. package/dist/customers/delivery-preferences.js +97 -0
  38. package/dist/customers/groups.d.ts +207 -0
  39. package/dist/customers/groups.js +438 -0
  40. package/dist/customers/index.d.ts +447 -37
  41. package/dist/customers/index.js +313 -40
  42. package/dist/customers/invitation-status.d.ts +57 -0
  43. package/dist/customers/invitation-status.js +21 -0
  44. package/dist/customers/notes.d.ts +67 -0
  45. package/dist/customers/notes.js +51 -0
  46. package/dist/customers/notification-recipients.d.ts +97 -0
  47. package/dist/customers/notification-recipients.js +84 -0
  48. package/dist/customers/saved-lists.d.ts +42 -0
  49. package/dist/customers/saved-lists.js +61 -0
  50. package/dist/customers/vat.d.ts +83 -0
  51. package/dist/customers/vat.js +46 -0
  52. package/dist/customers/wire.d.ts +20 -0
  53. package/dist/customers/wire.js +35 -0
  54. package/dist/documents/ar.d.ts +307 -0
  55. package/dist/documents/ar.js +254 -0
  56. package/dist/documents/index.d.ts +731 -21
  57. package/dist/documents/index.js +436 -24
  58. package/dist/documents/pdf.d.ts +2 -0
  59. package/dist/documents/pdf.js +11 -0
  60. package/dist/fulfillments/create.d.ts +167 -0
  61. package/dist/fulfillments/create.js +216 -0
  62. package/dist/fulfillments/index.d.ts +121 -10
  63. package/dist/fulfillments/index.js +120 -2
  64. package/dist/fulfillments/payment-policy.d.ts +64 -0
  65. package/dist/fulfillments/payment-policy.js +140 -0
  66. package/dist/fulfillments/warehouse.d.ts +724 -0
  67. package/dist/fulfillments/warehouse.js +914 -0
  68. package/dist/geo/index.d.ts +119 -0
  69. package/dist/geo/index.js +107 -0
  70. package/dist/index.d.ts +129 -30
  71. package/dist/index.js +47 -9
  72. package/dist/integrations/afas-actions.d.ts +234 -0
  73. package/dist/integrations/afas-actions.js +310 -0
  74. package/dist/integrations/afas-types.d.ts +483 -0
  75. package/dist/integrations/afas-types.js +5 -0
  76. package/dist/integrations/afas.d.ts +234 -0
  77. package/dist/integrations/afas.js +290 -0
  78. package/dist/integrations/connections.d.ts +103 -0
  79. package/dist/integrations/connections.js +55 -0
  80. package/dist/integrations/esp.d.ts +313 -0
  81. package/dist/integrations/esp.js +169 -0
  82. package/dist/integrations/exact-online-types.d.ts +423 -0
  83. package/dist/integrations/exact-online-types.js +6 -0
  84. package/dist/integrations/exact-online.d.ts +248 -0
  85. package/dist/integrations/exact-online.js +376 -0
  86. package/dist/integrations/index.d.ts +40 -0
  87. package/dist/integrations/index.js +24 -0
  88. package/dist/integrations/pipeline-keys.d.ts +13 -0
  89. package/dist/integrations/pipeline-keys.js +22 -0
  90. package/dist/integrations/pipelines.d.ts +114 -0
  91. package/dist/integrations/pipelines.js +46 -0
  92. package/dist/integrations/types.d.ts +322 -0
  93. package/dist/integrations/types.js +8 -0
  94. package/dist/jobs/bulk-action-run.d.ts +11 -2
  95. package/dist/jobs/bulk-action-run.js +4 -5
  96. package/dist/locations/index.d.ts +66 -0
  97. package/dist/locations/index.js +31 -0
  98. package/dist/meta/index.d.ts +162 -0
  99. package/dist/orders/bulk.d.ts +75 -0
  100. package/dist/orders/bulk.js +150 -0
  101. package/dist/orders/cancel-with-money.d.ts +227 -0
  102. package/dist/orders/cancel-with-money.js +153 -0
  103. package/dist/orders/commit-edit.d.ts +7 -1
  104. package/dist/orders/commit-edit.js +5 -1
  105. package/dist/orders/deposit-policy.d.ts +103 -0
  106. package/dist/orders/deposit-policy.js +202 -0
  107. package/dist/orders/drafts.d.ts +800 -0
  108. package/dist/orders/drafts.js +771 -0
  109. package/dist/orders/fields.d.ts +245 -0
  110. package/dist/orders/fields.js +188 -0
  111. package/dist/orders/get.d.ts +29 -19
  112. package/dist/orders/get.js +17 -8
  113. package/dist/orders/index.d.ts +125 -5
  114. package/dist/orders/index.js +35 -2
  115. package/dist/orders/lifecycle.d.ts +128 -0
  116. package/dist/orders/lifecycle.js +158 -0
  117. package/dist/orders/line-price-preview.d.ts +7 -2
  118. package/dist/orders/list.d.ts +222 -0
  119. package/dist/orders/list.js +377 -0
  120. package/dist/orders/manual-discounts.d.ts +11 -2
  121. package/dist/orders/manual-discounts.js +2 -0
  122. package/dist/orders/manual-pricing.d.ts +5 -0
  123. package/dist/orders/order-entry.d.ts +20 -7
  124. package/dist/orders/plan-edit.d.ts +1 -1
  125. package/dist/orders/plan-edit.js +4 -1
  126. package/dist/orders/settings.d.ts +201 -0
  127. package/dist/orders/settings.js +325 -0
  128. package/dist/orders/status.d.ts +159 -13
  129. package/dist/orders/status.js +21 -0
  130. package/dist/orders/surcharges.d.ts +8 -1
  131. package/dist/orders/surcharges.js +3 -2
  132. package/dist/payments/account-credit.d.ts +30 -0
  133. package/dist/payments/account-credit.js +19 -0
  134. package/dist/payments/ar.d.ts +292 -0
  135. package/dist/payments/ar.js +223 -0
  136. package/dist/payments/connected-account.d.ts +47 -0
  137. package/dist/payments/connected-account.js +61 -0
  138. package/dist/payments/credit-noted-fields.d.ts +12 -0
  139. package/dist/payments/credit-noted-fields.js +37 -0
  140. package/dist/payments/index.d.ts +263 -10
  141. package/dist/payments/index.js +133 -1
  142. package/dist/payments/methods.d.ts +332 -0
  143. package/dist/payments/methods.js +363 -0
  144. package/dist/payments/mollie-methods.d.ts +159 -0
  145. package/dist/payments/mollie-methods.js +141 -0
  146. package/dist/payments/payment-links.d.ts +180 -0
  147. package/dist/payments/payment-links.js +97 -0
  148. package/dist/payments/provider-keys.d.ts +150 -0
  149. package/dist/payments/provider-keys.js +118 -0
  150. package/dist/payments/providers.d.ts +86 -11
  151. package/dist/payments/providers.js +29 -0
  152. package/dist/payments/requests.d.ts +144 -0
  153. package/dist/payments/requests.js +92 -0
  154. package/dist/payments/store-credit-fields.d.ts +15 -0
  155. package/dist/payments/store-credit-fields.js +51 -0
  156. package/dist/pickups/index.d.ts +104 -0
  157. package/dist/pickups/index.js +105 -0
  158. package/dist/platform/channels.d.ts +132 -0
  159. package/dist/platform/channels.js +128 -0
  160. package/dist/platform/geo.d.ts +123 -0
  161. package/dist/platform/geo.js +144 -0
  162. package/dist/platform/tax.d.ts +154 -0
  163. package/dist/platform/tax.js +139 -0
  164. package/dist/pos/anomalies.d.ts +158 -0
  165. package/dist/pos/anomalies.js +64 -0
  166. package/dist/pos/assisted-selling.d.ts +79 -0
  167. package/dist/pos/assisted-selling.js +38 -0
  168. package/dist/pos/exchanges.d.ts +103 -0
  169. package/dist/pos/exchanges.js +22 -0
  170. package/dist/pos/gift-cards.d.ts +135 -0
  171. package/dist/pos/gift-cards.js +47 -0
  172. package/dist/pos/index.d.ts +2125 -32
  173. package/dist/pos/index.js +742 -4
  174. package/dist/pos/no-receipt.d.ts +94 -0
  175. package/dist/pos/no-receipt.js +25 -0
  176. package/dist/pos/offline-sales.d.ts +187 -0
  177. package/dist/pos/offline-sales.js +116 -0
  178. package/dist/pos/pay-by-link.d.ts +143 -0
  179. package/dist/pos/pay-by-link.js +57 -0
  180. package/dist/pos/quick-keys.d.ts +103 -0
  181. package/dist/pos/quick-keys.js +45 -0
  182. package/dist/pos/reports.d.ts +360 -0
  183. package/dist/pos/reports.js +62 -0
  184. package/dist/pos/returns.d.ts +217 -0
  185. package/dist/pos/returns.js +53 -0
  186. package/dist/pricing/bulk-coupons.d.ts +139 -0
  187. package/dist/pricing/bulk-coupons.js +112 -0
  188. package/dist/pricing/index.d.ts +177 -4
  189. package/dist/pricing/index.js +176 -1
  190. package/dist/pricing/price-lists.d.ts +604 -0
  191. package/dist/pricing/price-lists.js +600 -0
  192. package/dist/pricing/vouchers.d.ts +103 -0
  193. package/dist/pricing/vouchers.js +69 -0
  194. package/dist/purchasing/alerts.d.ts +89 -0
  195. package/dist/purchasing/alerts.js +62 -0
  196. package/dist/purchasing/cost.d.ts +110 -0
  197. package/dist/purchasing/cost.js +86 -0
  198. package/dist/purchasing/index.d.ts +36 -0
  199. package/dist/purchasing/index.js +16 -0
  200. package/dist/purchasing/purchase-orders.d.ts +548 -0
  201. package/dist/purchasing/purchase-orders.js +491 -0
  202. package/dist/purchasing/replenishment.d.ts +281 -0
  203. package/dist/purchasing/replenishment.js +240 -0
  204. package/dist/purchasing/settings.d.ts +152 -0
  205. package/dist/purchasing/settings.js +181 -0
  206. package/dist/purchasing/suppliers.d.ts +476 -0
  207. package/dist/purchasing/suppliers.js +438 -0
  208. package/dist/quotes/index.d.ts +501 -0
  209. package/dist/quotes/index.js +439 -0
  210. package/dist/reports/index.d.ts +71 -8
  211. package/dist/reports/index.js +74 -11
  212. package/dist/returns/index.d.ts +818 -0
  213. package/dist/returns/index.js +844 -0
  214. package/dist/search/index.d.ts +174 -0
  215. package/dist/search/index.js +125 -0
  216. package/dist/shipping/definitions.d.ts +29 -0
  217. package/dist/shipping/definitions.js +47 -0
  218. package/dist/shipping/fulfillment-profiles.d.ts +174 -0
  219. package/dist/shipping/fulfillment-profiles.js +104 -0
  220. package/dist/shipping/hendriks.d.ts +97 -0
  221. package/dist/shipping/hendriks.js +50 -0
  222. package/dist/shipping/index.d.ts +48 -0
  223. package/dist/shipping/index.js +25 -0
  224. package/dist/shipping/methods.d.ts +198 -0
  225. package/dist/shipping/methods.js +240 -0
  226. package/dist/shipping/myparcel.d.ts +254 -0
  227. package/dist/shipping/myparcel.js +178 -0
  228. package/dist/shipping/parcel-count-suggestion.d.ts +2 -0
  229. package/dist/shipping/parcel-rule.d.ts +6 -5
  230. package/dist/shipping/parcel-rule.js +21 -23
  231. package/dist/shipping/rate-tables.d.ts +179 -0
  232. package/dist/shipping/rate-tables.js +184 -0
  233. package/dist/stock/index.d.ts +10 -3
  234. package/dist/stock/index.js +3 -0
  235. package/dist/stock/locations.d.ts +144 -0
  236. package/dist/stock/locations.js +168 -0
  237. package/dist/stock/transfers.d.ts +216 -43
  238. package/dist/stock/transfers.js +140 -13
  239. package/dist/stored-value/index.d.ts +214 -4
  240. package/dist/stored-value/index.js +152 -0
  241. package/dist/subscriptions/index.d.ts +682 -0
  242. package/dist/subscriptions/index.js +815 -0
  243. package/dist/tax/index.d.ts +76 -1
  244. package/dist/tax/index.js +50 -0
  245. package/dist/tenant/email-style.d.ts +102 -0
  246. package/dist/tenant/email-style.js +72 -0
  247. package/dist/tenant/email-templates.d.ts +66 -0
  248. package/dist/tenant/email-templates.js +54 -0
  249. package/dist/tenant/notifications.d.ts +23 -17
  250. package/dist/tenant/settings-sources.d.ts +1 -1
  251. package/dist/tenant/time-zone.d.ts +17 -0
  252. package/dist/tenant/time-zone.js +11 -0
  253. package/dist/types.d.ts +51 -4
  254. package/dist/users/index.d.ts +3 -0
  255. package/dist/users/index.js +2 -0
  256. package/dist/users/warehouses.d.ts +50 -0
  257. package/dist/users/warehouses.js +51 -0
  258. package/package.json +2 -2
@@ -13,19 +13,27 @@ export type ExternalLinePriceBasis = 'net' | 'gross';
13
13
  /**
14
14
  * What is going to happen to the goods.
15
15
  *
16
- * A fact the vertical STATES about the order rather than an instruction
17
- * commerce acts on in this release: nothing here creates a fulfilment or picks
18
- * a shipping method. It is recorded as the order's `platform.fulfilment`
19
- * metafield so a document, a WMS push or a later fulfilment flow can read back
20
- * what the caller actually asked for.
16
+ * Commerce ACTS on it. Nothing here creates a fulfilment or picks a shipping
17
+ * method, but the value decides whether the order needs a shipping method to be
18
+ * placed and whether a fulfilment of it owes a delivery charge. It is recorded
19
+ * as the order's `platform.fulfilment` metafield (only the platform writes it),
20
+ * so a document, a WMS push or a merchant can also read back what you asked for.
21
21
  *
22
22
  * - `pickup` — collected at a location. Needs no shipping method and no
23
- * address.
23
+ * address. Handed over through an ordinary fulfilment, which adds no delivery
24
+ * charge and is not held to a fulfilment profile's minimum.
24
25
  * - `ship` — shipped. The order still needs a shipping method before it can
25
26
  * leave `Draft`, and `transitionTo: 'ArrangingPayment'` on an order that has
26
27
  * none is refused by the platform's own placement gate, not by this call.
28
+ * Delivery charges and profile minimums apply as for any delivered order.
27
29
  * - `none` — neither shipped nor collected: a service, a subscription set-up,
28
- * a fee.
30
+ * a fee. Needs no shipping method, owes no delivery charge and takes no
31
+ * profile minimum. The order is marked as having nothing to ship, so it
32
+ * completes on its own once it is paid (after the store's completion grace
33
+ * period). It may not hold goods that are stock-tracked: such a catalog line
34
+ * is refused `LINE_NEEDS_FULFILMENT` here, and so is placing (or modifying)
35
+ * the order to carry one later. Send goods as a `pickup` order, a service as
36
+ * an external line.
29
37
  */
30
38
  export type ExternalOrderFulfilment = 'pickup' | 'ship' | 'none';
31
39
  /**
@@ -478,6 +486,11 @@ export interface OrderEntryModule {
478
486
  * - `LINE_DUPLICATE_VARIANT` — two catalog lines name the same variant.
479
487
  * `details` carries the second one's `position` and the first one's
480
488
  * `firstPosition`. Send one line with the quantity you want.
489
+ * - `LINE_NEEDS_FULFILMENT` — a catalog line whose variant is stock-tracked on
490
+ * an order with `fulfilment: 'none'`. Nothing ships or is handed over on
491
+ * such an order, so nothing would ever sell or release that stock.
492
+ * `details` carries its `position` and `variantId`. Send the order as
493
+ * `pickup` (or `ship`), or the service as an external line.
481
494
  * - `EXTERNAL_LINE_PROMOTION_UNSUPPORTED` — a promotion discounted one of your
482
495
  * externally priced lines. An order-level promotion is shared across every
483
496
  * line of an order by the engine, so the platform refuses rather than
@@ -422,7 +422,7 @@ export interface OrderEditPlan {
422
422
  * the order-edit plan, so on an older one this call alone fails validation and
423
423
  * no other order call does.
424
424
  */
425
- export declare const PLAN_ORDER_EDIT_QUERY = "\n query PlanOrderEdit($orderId: ID!, $diff: OrderEditDiffInput!) {\n planOrderEdit(orderId: $orderId, diff: $diff) {\n orderId\n planHash\n orderVersion\n steps {\n kind\n input\n moneyEffect\n }\n refund {\n paymentId\n amount\n }\n overpayment {\n amount\n currencyCode\n surchargeIds\n refund {\n available\n code\n message\n }\n accountCredit {\n available\n code\n message\n }\n refundWhenChosen {\n paymentId\n amount\n }\n }\n returnState\n blockers {\n code\n message\n details\n }\n notices {\n code\n message\n details\n }\n payment {\n collected\n reason\n method\n }\n preview {\n currencyCode\n subTotal\n subTotalWithTax\n shipping\n shippingWithTax\n surcharges\n surchargesWithTax\n total\n totalWithTax\n taxSummary {\n description\n taxRate\n taxBase\n taxTotal\n }\n totalWithTaxBefore\n }\n }\n }\n";
425
+ export declare const PLAN_ORDER_EDIT_QUERY = "\n query PlanOrderEdit($orderId: ID!, $diff: OrderEditDiffInput!) {\n planOrderEdit(orderId: $orderId, diff: $diff) {\n orderId\n planHash\n orderVersion\n steps {\n kind\n input\n moneyEffect\n }\n refund {\n paymentId\n amount\n }\n overpayment {\n amount\n currencyCode\n surchargeIds\n refund {\n available\n code\n message\n book\n }\n accountCredit {\n available\n code\n message\n book\n }\n refundWhenChosen {\n paymentId\n amount\n }\n }\n returnState\n blockers {\n code\n message\n details\n }\n notices {\n code\n message\n details\n }\n payment {\n collected\n reason\n method\n }\n preview {\n currencyCode\n subTotal\n subTotalWithTax\n shipping\n shippingWithTax\n surcharges\n surchargesWithTax\n total\n totalWithTax\n taxSummary {\n description\n taxRate\n taxBase\n taxTotal\n }\n totalWithTaxBefore\n }\n }\n }\n";
426
426
  export interface OrderPlanEditModule {
427
427
  /**
428
428
  * How an edit of a placed order would be carried out, and what the order
@@ -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
+ }