@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
@@ -1,5 +1,5 @@
1
1
  import { type Page, type Transport } from '@sixtyninecommerce/core';
2
- import type { Call } from '../types.js';
2
+ import type { Call, ListOptions } from '../types.js';
3
3
  /**
4
4
  * The mapping used when no mapping names the country the money is taxed in.
5
5
  *
@@ -115,10 +115,85 @@ export interface TaxMappingsModule {
115
115
  /** Drop one country's mapping. Silent when there was none. */
116
116
  remove(key: string, countryCode: string, options?: Call): Promise<TaxClass>;
117
117
  }
118
+ /**
119
+ * A platform-wide tax category (`Standard`, `Reduced`, `Zero`): what a tax class maps a country
120
+ * onto, and what a tax rate prices in a zone. Shared by every store.
121
+ */
122
+ export interface TaxCategory {
123
+ id: string;
124
+ name: string;
125
+ /** The category the platform's own tax setup treats as the default. */
126
+ isDefault: boolean;
127
+ createdAt: string;
128
+ updatedAt: string;
129
+ }
130
+ /**
131
+ * A platform-wide tax rate: the percentage a category is taxed at in a zone, optionally only for
132
+ * one customer group.
133
+ */
134
+ export interface TaxRate {
135
+ id: string;
136
+ name: string;
137
+ /** A percentage: `21` is 21 %. Not money. */
138
+ value: number;
139
+ /** A disabled rate is never applied. */
140
+ enabled: boolean;
141
+ category: {
142
+ id: string;
143
+ name: string;
144
+ };
145
+ zone: {
146
+ id: string;
147
+ name: string;
148
+ };
149
+ /** Null for a rate that applies to every customer. */
150
+ customerGroup: {
151
+ id: string;
152
+ name: string;
153
+ } | null;
154
+ createdAt: string;
155
+ updatedAt: string;
156
+ }
157
+ /** The platform's tax categories (read-only here; a SuperAdmin writes them through `platform.tax.categories`). */
158
+ export interface TaxCategoriesModule {
159
+ /**
160
+ * Every tax category, in id order. Reading needs `ReadTaxCategory`, which every store role holds.
161
+ *
162
+ * A `Page`: walk it (`for await`, or `.next()` until null) for the COMPLETE list. Nothing is cut
163
+ * at a fixed size; every following page keeps the same order and per-call options.
164
+ *
165
+ * @effect read
166
+ */
167
+ list(options?: ListOptions): Promise<Page<TaxCategory>>;
168
+ }
169
+ /** The platform's tax rates (read-only here; a SuperAdmin writes them through `platform.tax.rates`). */
170
+ export interface TaxRatesModule {
171
+ /**
172
+ * Every tax rate, in id order, with its category, zone and customer group. Reading needs
173
+ * `ReadTaxRate`, which every store role holds.
174
+ *
175
+ * A `Page`: walk it (`for await`, or `.next()` until null) for the COMPLETE list. Nothing is cut
176
+ * at a fixed size (the dashboard used to stop at 100); every following page keeps the same
177
+ * order and per-call options.
178
+ *
179
+ * @effect read
180
+ */
181
+ list(options?: ListOptions): Promise<Page<TaxRate>>;
182
+ }
118
183
  export interface TaxModule {
119
184
  classes: TaxClassesModule;
120
185
  mappings: TaxMappingsModule;
186
+ /** The platform-wide tax categories every store shares. */
187
+ categories: TaxCategoriesModule;
188
+ /** The platform-wide tax rates every store shares. */
189
+ rates: TaxRatesModule;
121
190
  }
191
+ /** A {@link TaxCategory} as every read and write answers it (`platform.tax` reuses it). */
192
+ export declare const TAX_CATEGORY_FIELDS = "\n id\n name\n isDefault\n createdAt\n updatedAt\n";
193
+ /** A {@link TaxRate} as every read and write answers it (`platform.tax` reuses it). */
194
+ export declare const TAX_RATE_FIELDS = "\n id\n name\n value\n enabled\n category { id name }\n zone { id name }\n customerGroup { id name }\n createdAt\n updatedAt\n";
195
+ export declare const TAX_CATEGORIES_QUERY = "\n query TaxCategories($options: TaxCategoryListOptions) {\n taxCategories(options: $options) {\n items { \n id\n name\n isDefault\n createdAt\n updatedAt\n }\n totalItems\n }\n }\n";
196
+ export declare const TAX_RATES_QUERY = "\n query TaxRates($options: TaxRateListOptions) {\n taxRates(options: $options) {\n items { \n id\n name\n value\n enabled\n category { id name }\n zone { id name }\n customerGroup { id name }\n createdAt\n updatedAt\n }\n totalItems\n }\n }\n";
122
197
  export declare const TAX_CLASSES_QUERY = "\n query TaxClasses {\n taxClasses { \n id\n key\n label\n mappings {\n countryCode\n taxCategoryId\n taxCategory {\n id\n name\n isDefault\n }\n }\n }\n }\n";
123
198
  export declare const TAX_CLASS_QUERY = "\n query TaxClass($key: String!) {\n taxClass(key: $key) { \n id\n key\n label\n mappings {\n countryCode\n taxCategoryId\n taxCategory {\n id\n name\n isDefault\n }\n }\n }\n }\n";
124
199
  export declare const UPSERT_TAX_CLASS_MUTATION = "\n mutation UpsertTaxClass($input: UpsertTaxClassInput!) {\n upsertTaxClass(input: $input) { \n id\n key\n label\n mappings {\n countryCode\n taxCategoryId\n taxCategory {\n id\n name\n isDefault\n }\n }\n }\n }\n";
package/dist/tax/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { createPage } from '@sixtyninecommerce/core';
2
+ import { vendureListPage } from '../catalog/types.js';
2
3
  /**
3
4
  * The mapping used when no mapping names the country the money is taxed in.
4
5
  *
@@ -8,6 +9,49 @@ import { createPage } from '@sixtyninecommerce/core';
8
9
  * the same one read two ways.
9
10
  */
10
11
  export const TAX_CLASS_FALLBACK_COUNTRY = '*';
12
+ /** A {@link TaxCategory} as every read and write answers it (`platform.tax` reuses it). */
13
+ export const TAX_CATEGORY_FIELDS = /* GraphQL */ `
14
+ id
15
+ name
16
+ isDefault
17
+ createdAt
18
+ updatedAt
19
+ `;
20
+ /** A {@link TaxRate} as every read and write answers it (`platform.tax` reuses it). */
21
+ export const TAX_RATE_FIELDS = /* GraphQL */ `
22
+ id
23
+ name
24
+ value
25
+ enabled
26
+ category { id name }
27
+ zone { id name }
28
+ customerGroup { id name }
29
+ createdAt
30
+ updatedAt
31
+ `;
32
+ export const TAX_CATEGORIES_QUERY = /* GraphQL */ `
33
+ query TaxCategories($options: TaxCategoryListOptions) {
34
+ taxCategories(options: $options) {
35
+ items { ${TAX_CATEGORY_FIELDS} }
36
+ totalItems
37
+ }
38
+ }
39
+ `;
40
+ export const TAX_RATES_QUERY = /* GraphQL */ `
41
+ query TaxRates($options: TaxRateListOptions) {
42
+ taxRates(options: $options) {
43
+ items { ${TAX_RATE_FIELDS} }
44
+ totalItems
45
+ }
46
+ }
47
+ `;
48
+ /**
49
+ * Vendure's list queries add no ORDER BY of their own, and `skip/take` over an unordered query
50
+ * can repeat or skip rows between two requests. Every list here is ordered by id, the one unique
51
+ * key a sort can name (a GraphQL sort input applies its fields in the SCHEMA's order, where `id`
52
+ * comes first, so `{ name, id }` is id order too), so a walk answers each row exactly once.
53
+ */
54
+ const BY_ID = { id: 'ASC' };
11
55
  const TAX_CLASS_FIELDS = /* GraphQL */ `
12
56
  id
13
57
  key
@@ -79,6 +123,12 @@ export function createTaxModule(transport) {
79
123
  return data.deleteTaxClass;
80
124
  },
81
125
  },
126
+ categories: {
127
+ list: (options) => vendureListPage(transport, TAX_CATEGORIES_QUERY, 'taxCategories', (skip, take) => ({ options: { skip, take, sort: BY_ID } }), options),
128
+ },
129
+ rates: {
130
+ list: (options) => vendureListPage(transport, TAX_RATES_QUERY, 'taxRates', (skip, take) => ({ options: { skip, take, sort: BY_ID } }), options),
131
+ },
82
132
  mappings: {
83
133
  async set(key, countryCode, taxCategoryId, options) {
84
134
  const data = await transport.graphql({
@@ -0,0 +1,102 @@
1
+ import type { Transport } from '@sixtyninecommerce/core';
2
+ import type { Call } from '../types.js';
3
+ /**
4
+ * The look of every email (and the PDF colours) the active store sends, as the engine applies it.
5
+ *
6
+ * The four choice fields name a member of a fixed set, which the engine checks on `set`:
7
+ * `fontFamily` `system` | `georgia` | `arial` | `verdana`; `logoPosition` `center` | `left`;
8
+ * `logoVariant` `light` | `dark` | `none` (which Business profile logo heads the email);
9
+ * `buttonStyle` `filled` | `outline`. They are typed `string` because a store can hold a value
10
+ * from before that check, which `get` answers as stored and `set` accepts back unchanged.
11
+ */
12
+ export interface EmailStyle {
13
+ /** The active store. The style read is always the active store's, also while it inherits. */
14
+ channelId: string;
15
+ /**
16
+ * Buttons and links. Each colour is a hex (`#1a2b3c`, 3 to 8 digits), `rgb(r, g, b)`, a colour
17
+ * name or `transparent`.
18
+ */
19
+ accentColor: string;
20
+ /** The outer background; `transparent` lets a mail client's dark mode show through. */
21
+ backgroundColor: string;
22
+ /** The content area's background. */
23
+ contentBgColor: string;
24
+ textColor: string;
25
+ mutedTextColor: string;
26
+ fontFamily: string;
27
+ /** Pixels. */
28
+ headingFontSize: number;
29
+ /** Pixels. */
30
+ bodyFontSize: number;
31
+ logoPosition: string;
32
+ /** Pixels. */
33
+ logoMaxWidth: number;
34
+ logoVariant: string;
35
+ /** A button's corner radius, in pixels. */
36
+ borderRadius: number;
37
+ /** The content column's width, in pixels. */
38
+ contentWidth: number;
39
+ buttonStyle: string;
40
+ }
41
+ /**
42
+ * The style fields a `set` changes; an omitted field keeps its value. The sizes are whole pixels
43
+ * (GraphQL `Int`). `channelId` is not an input: `set` leaves it out, so `set({ ...style, … })`
44
+ * with a style `get` answered works.
45
+ */
46
+ export type EmailStylePatch = Partial<Omit<EmailStyle, 'channelId'>>;
47
+ /**
48
+ * `admin.tenant.emailStyle` — the active store's email style (part of the `settings.email` group).
49
+ * Always the ACTIVE channel; no channel argument.
50
+ *
51
+ * **A store that takes `settings.email` from its organization** (`admin.tenant.settingsSources`:
52
+ * locked, or inheritable and not overridden) sends its organization's style, and `get` answers
53
+ * that one. `set` then refuses any change; sending back what `get` answered is accepted and
54
+ * changes nothing. `admin.tenant.settingsSources.override('settings.email')` first to keep a style
55
+ * of the store's own.
56
+ */
57
+ export interface EmailStyleModule {
58
+ /**
59
+ * The style the store's emails use: its own, its organization's while it inherits, or the
60
+ * platform's defaults while it has saved none. Needs `ReadSettings`.
61
+ *
62
+ * @effect read
63
+ * @permission ReadSettings
64
+ */
65
+ get(options?: Call): Promise<EmailStyle>;
66
+ /**
67
+ * Change the fields `patch` names, and answer the whole style as the engine now stores it. The
68
+ * next email the store sends uses it; emails already sent keep theirs.
69
+ *
70
+ * **Coded refusals:**
71
+ * - `EMAIL_STYLE_COLOR_INVALID` → `ValidationError` (400): a colour that is not a hex, `rgb(…)`,
72
+ * colour name or `transparent`. The message names the field.
73
+ * - `EMAIL_STYLE_FONT_FAMILY_INVALID`, `EMAIL_STYLE_LOGO_POSITION_INVALID`,
74
+ * `EMAIL_STYLE_BUTTON_STYLE_INVALID`, `EMAIL_LOGO_VARIANT_INVALID` → `ValidationError` (400):
75
+ * a choice outside its set (see {@link EmailStyle}). Sending back the value the store holds
76
+ * passes, even one from before the check.
77
+ * - `BAD_USER_INPUT` → `ValidationError` (400): a key that is not a style field, or a value of
78
+ * the wrong type (a size that is not a whole number). The message names it.
79
+ * - `MANAGED_BY_ORGANIZATION` → `BusinessRuleError` (422): a changed field while the
80
+ * organization locks `settings.email`.
81
+ * - `ORGANIZATION_SETTINGS_INHERITED` → `BusinessRuleError` (422): a changed field while the
82
+ * store inherits `settings.email`; `tenant.settingsSources.override` it first.
83
+ * - `FORBIDDEN` → `PermissionDeniedError` (403): the credential lacks `UpdateSettings`.
84
+ *
85
+ * The sizes have no engine bounds yet; a UI should keep its own.
86
+ *
87
+ * @effect write
88
+ * @permission UpdateSettings
89
+ */
90
+ set(patch: EmailStylePatch, options?: Call): Promise<EmailStyle>;
91
+ }
92
+ /** The style fields, in the order the engine's input lists them. */
93
+ export declare const EMAIL_STYLE_FIELDS = "\n channelId\n accentColor\n backgroundColor\n contentBgColor\n textColor\n mutedTextColor\n fontFamily\n headingFontSize\n bodyFontSize\n logoPosition\n logoMaxWidth\n logoVariant\n borderRadius\n contentWidth\n buttonStyle\n";
94
+ export declare const EMAIL_STYLE_QUERY = "\n query EmailStyle {\n emailStyleConfig { \n channelId\n accentColor\n backgroundColor\n contentBgColor\n textColor\n mutedTextColor\n fontFamily\n headingFontSize\n bodyFontSize\n logoPosition\n logoMaxWidth\n logoVariant\n borderRadius\n contentWidth\n buttonStyle\n }\n }\n";
95
+ export declare const SET_EMAIL_STYLE_MUTATION = "\n mutation SetEmailStyle($input: UpdateEmailStyleConfigInput!) {\n updateEmailStyleConfig(input: $input) { \n channelId\n accentColor\n backgroundColor\n contentBgColor\n textColor\n mutedTextColor\n fontFamily\n headingFontSize\n bodyFontSize\n logoPosition\n logoMaxWidth\n logoVariant\n borderRadius\n contentWidth\n buttonStyle\n }\n }\n";
96
+ /**
97
+ * The patch as the engine's `UpdateEmailStyleConfigInput` takes it. Every other key goes through
98
+ * as given, so the engine refuses a misspelt or unknown one (`BAD_USER_INPUT`) instead of the SDK
99
+ * dropping it and answering success; `channelId` alone is left out (see {@link EmailStylePatch}).
100
+ */
101
+ export declare function emailStyleInputOf(patch: EmailStylePatch): Record<string, unknown>;
102
+ export declare function createEmailStyleModule(transport: Transport): EmailStyleModule;
@@ -0,0 +1,72 @@
1
+ /** The style fields, in the order the engine's input lists them. */
2
+ export const EMAIL_STYLE_FIELDS = /* GraphQL */ `
3
+ channelId
4
+ accentColor
5
+ backgroundColor
6
+ contentBgColor
7
+ textColor
8
+ mutedTextColor
9
+ fontFamily
10
+ headingFontSize
11
+ bodyFontSize
12
+ logoPosition
13
+ logoMaxWidth
14
+ logoVariant
15
+ borderRadius
16
+ contentWidth
17
+ buttonStyle
18
+ `;
19
+ export const EMAIL_STYLE_QUERY = /* GraphQL */ `
20
+ query EmailStyle {
21
+ emailStyleConfig { ${EMAIL_STYLE_FIELDS} }
22
+ }
23
+ `;
24
+ export const SET_EMAIL_STYLE_MUTATION = /* GraphQL */ `
25
+ mutation SetEmailStyle($input: UpdateEmailStyleConfigInput!) {
26
+ updateEmailStyleConfig(input: $input) { ${EMAIL_STYLE_FIELDS} }
27
+ }
28
+ `;
29
+ function fromWire(wire) {
30
+ return {
31
+ channelId: String(wire.channelId),
32
+ accentColor: wire.accentColor,
33
+ backgroundColor: wire.backgroundColor,
34
+ contentBgColor: wire.contentBgColor,
35
+ textColor: wire.textColor,
36
+ mutedTextColor: wire.mutedTextColor,
37
+ fontFamily: wire.fontFamily,
38
+ headingFontSize: wire.headingFontSize,
39
+ bodyFontSize: wire.bodyFontSize,
40
+ logoPosition: wire.logoPosition,
41
+ logoMaxWidth: wire.logoMaxWidth,
42
+ logoVariant: wire.logoVariant,
43
+ borderRadius: wire.borderRadius,
44
+ contentWidth: wire.contentWidth,
45
+ buttonStyle: wire.buttonStyle,
46
+ };
47
+ }
48
+ /**
49
+ * The patch as the engine's `UpdateEmailStyleConfigInput` takes it. Every other key goes through
50
+ * as given, so the engine refuses a misspelt or unknown one (`BAD_USER_INPUT`) instead of the SDK
51
+ * dropping it and answering success; `channelId` alone is left out (see {@link EmailStylePatch}).
52
+ */
53
+ export function emailStyleInputOf(patch) {
54
+ const { channelId: _readOnly, ...input } = patch;
55
+ return input;
56
+ }
57
+ export function createEmailStyleModule(transport) {
58
+ return {
59
+ async get(options) {
60
+ const data = await transport.graphql({ api: 'admin', query: EMAIL_STYLE_QUERY }, options);
61
+ return fromWire(data.emailStyleConfig);
62
+ },
63
+ async set(patch, options) {
64
+ const data = await transport.graphql({
65
+ api: 'admin',
66
+ query: SET_EMAIL_STYLE_MUTATION,
67
+ variables: { input: emailStyleInputOf(patch) },
68
+ }, options);
69
+ return fromWire(data.updateEmailStyleConfig);
70
+ },
71
+ };
72
+ }
@@ -0,0 +1,66 @@
1
+ import { type Transport } from '@sixtyninecommerce/core';
2
+ import type { Call } from '../types.js';
3
+ import { type EmailStylePatch } from './email-style.js';
4
+ /** One template rendered as the store would send it, filled from the store's own records. */
5
+ export interface EmailPreview {
6
+ subject: string;
7
+ /** The whole HTML document, ready for an `<iframe srcdoc>`. */
8
+ html: string;
9
+ /** Each recipient field resolved for the sample, as one comma-separated list ('' for none). */
10
+ recipients: {
11
+ to: string;
12
+ cc: string;
13
+ bcc: string;
14
+ };
15
+ /**
16
+ * `true` when the credential's customer-data level withheld part of the answer (an app
17
+ * installation below `customerData: full`): `subject` and `html` then read `''`, and so do the
18
+ * recipients below `identity`. Absent otherwise.
19
+ */
20
+ redacted?: true;
21
+ }
22
+ export interface EmailPreviewOptions extends Call {
23
+ /** The language to render the template's texts and subject in, e.g. `nl`. */
24
+ languageCode: string;
25
+ /**
26
+ * Style fields to preview instead of the stored ones, e.g. a form's unsaved values. Nothing is
27
+ * saved. A value `tenant.emailStyle.set` would refuse is not refused here: the renderer draws it
28
+ * with its own fallback.
29
+ */
30
+ style?: EmailStylePatch;
31
+ }
32
+ /**
33
+ * `admin.tenant.emailTemplates` — the active store's email templates (part of the `settings.email`
34
+ * group). Always the ACTIVE channel; no channel argument.
35
+ *
36
+ * Templates are addressed by TYPE (`order_confirmation`, `shipping_confirmation`, …): a template's
37
+ * id changes when the store starts or stops taking `settings.email` from its organization, its
38
+ * type never does. While the store inherits, every read answers the organization's template.
39
+ */
40
+ export interface EmailTemplatesModule {
41
+ /**
42
+ * Render the template of `type` as the store would send it now, optionally with unsaved style
43
+ * changes. Two requests: the template the store uses for `type` is looked up first.
44
+ *
45
+ * **The answer is customer data.** The sample is filled from the store's LATEST REAL order and
46
+ * customer (synthetic values only in a store with none), so `subject`, `html` and the
47
+ * recipients carry a real buyer's name, email and addresses. Do not log or forward them; a
48
+ * credential below `customerData: full` gets them withheld ({@link EmailPreview.redacted}).
49
+ *
50
+ * For a store on its own settings, the first look-up of a known type the store has no template
51
+ * of yet creates it from the platform's default (as the dashboard's editor does).
52
+ *
53
+ * **Coded refusals:**
54
+ * - `EMAIL_TEMPLATE_NOT_FOUND` → `NotFoundError` (404): no template of `type` (an unknown type).
55
+ * - `ENTITY_NOT_FOUND` → `NotFoundError` (404): the template changed between the two requests
56
+ * (the store's `settings.email` source moved); ask again.
57
+ * - `FORBIDDEN` → `PermissionDeniedError` (403): the credential lacks `ReadSettings`.
58
+ *
59
+ * @effect read
60
+ * @permission ReadSettings
61
+ */
62
+ preview(type: string, options: EmailPreviewOptions): Promise<EmailPreview>;
63
+ }
64
+ export declare const EMAIL_TEMPLATE_ID_BY_TYPE_QUERY = "\n query EmailTemplateIdByType($type: String!) {\n emailTemplateByType(type: $type) { id }\n }\n";
65
+ export declare const EMAIL_PREVIEW_QUERY = "\n query EmailPreview(\n $templateId: ID!\n $languageCode: String!\n $styleOverrides: UpdateEmailStyleConfigInput\n ) {\n renderEmailPreview(\n templateId: $templateId\n languageCode: $languageCode\n styleOverrides: $styleOverrides\n ) {\n subject\n html\n recipientTo\n recipientCc\n recipientBcc\n }\n }\n";
66
+ export declare function createEmailTemplatesModule(transport: Transport): EmailTemplatesModule;
@@ -0,0 +1,54 @@
1
+ import { NotFoundError } from '@sixtyninecommerce/core';
2
+ import { customerDataField } from '../customers/wire.js';
3
+ import { emailStyleInputOf } from './email-style.js';
4
+ export const EMAIL_TEMPLATE_ID_BY_TYPE_QUERY = /* GraphQL */ `
5
+ query EmailTemplateIdByType($type: String!) {
6
+ emailTemplateByType(type: $type) { id }
7
+ }
8
+ `;
9
+ export const EMAIL_PREVIEW_QUERY = /* GraphQL */ `
10
+ query EmailPreview(
11
+ $templateId: ID!
12
+ $languageCode: String!
13
+ $styleOverrides: UpdateEmailStyleConfigInput
14
+ ) {
15
+ renderEmailPreview(
16
+ templateId: $templateId
17
+ languageCode: $languageCode
18
+ styleOverrides: $styleOverrides
19
+ ) {
20
+ subject
21
+ html
22
+ recipientTo
23
+ recipientCc
24
+ recipientBcc
25
+ }
26
+ }
27
+ `;
28
+ export function createEmailTemplatesModule(transport) {
29
+ return {
30
+ async preview(type, options) {
31
+ const { languageCode, style, ...call } = options;
32
+ const found = await transport.graphql({ api: 'admin', query: EMAIL_TEMPLATE_ID_BY_TYPE_QUERY, variables: { type } }, call);
33
+ const templateId = found.emailTemplateByType?.id;
34
+ if (!templateId) {
35
+ throw new NotFoundError({
36
+ code: 'EMAIL_TEMPLATE_NOT_FOUND',
37
+ message: `The store has no email template of type "${type}".`,
38
+ status: 404,
39
+ service: 'vendure',
40
+ });
41
+ }
42
+ const { value, mark } = await customerDataField(transport, EMAIL_PREVIEW_QUERY, 'renderEmailPreview', {
43
+ templateId,
44
+ languageCode,
45
+ ...(style !== undefined ? { styleOverrides: emailStyleInputOf(style) } : {}),
46
+ }, call);
47
+ return mark({
48
+ subject: value.subject,
49
+ html: value.html,
50
+ recipients: { to: value.recipientTo, cc: value.recipientCc, bcc: value.recipientBcc },
51
+ });
52
+ },
53
+ };
54
+ }
@@ -38,8 +38,8 @@ export interface NotificationEventConfig {
38
38
  event: string;
39
39
  /**
40
40
  * `false` switches the email off altogether; nothing is logged for it. Always
41
- * `true` for `password_reset`, `email_change` and `account_setup`, which
42
- * cannot be switched off.
41
+ * `true` for `password_reset`, `email_change`, `account_setup` and
42
+ * `gift_card_issued`, which cannot be switched off.
43
43
  */
44
44
  enabled: boolean;
45
45
  sender: NotificationSender;
@@ -60,20 +60,23 @@ export interface NotificationEventConfig {
60
60
  export interface UpdateNotificationEventConfigInput {
61
61
  /**
62
62
  * `false` is refused with `EVENT_ALWAYS_ENABLED` ({@link ValidationError}) on
63
- * `password_reset`, `email_change` and `account_setup`: each is the only way
64
- * a customer resets a password (or sets the first one on an imported
65
- * account), confirms a new address, or finishes a storefront registration,
66
- * so they are always sent. `magic_link_login` and `customer_invite` can be
67
- * switched off.
63
+ * `password_reset`, `email_change`, `account_setup` and `gift_card_issued`:
64
+ * each is the only way a customer resets a password (or sets the first one
65
+ * on an imported account), confirms a new address, finishes a storefront
66
+ * registration, or receives the code of a gift card they paid for (the
67
+ * platform keeps only its hash), so they are always sent. `magic_link_login`
68
+ * and `customer_invite` can be switched off.
68
69
  */
69
70
  enabled?: boolean;
70
71
  /**
71
72
  * `none` is refused on the identity emails — the `account` category:
72
73
  * password reset, magic link, invitation, email change, email and chat
73
- * verification — with `EVENT_ALWAYS_PLATFORM_SENT` ({@link ValidationError}).
74
- * Each carries a one-time credential only the platform can issue, and no
75
- * platform event carries one, so there is no event your system could send it
76
- * from. What you control there is where its link lands (your head's URL).
74
+ * verification — and on `gift_card_issued`, with `EVENT_ALWAYS_PLATFORM_SENT`
75
+ * ({@link ValidationError}). Each carries a one-time credential, or a gift
76
+ * card's code, only the platform can issue, and no platform event carries
77
+ * one (`stored_value.gift_card.issued` names the card, never its code), so
78
+ * there is no event your system could send it from. What you control there
79
+ * is where its link lands (your head's URL).
77
80
  *
78
81
  * `none` is also refused on an email whose `replacementEventTypes` is `null`,
79
82
  * with `NO_REPLACEMENT_EVENT` ({@link ValidationError}): no platform event is
@@ -87,6 +90,8 @@ export interface UpdateNotificationEventConfigInput {
87
90
  * links or codes, and one login serves the person in every store, so they go to
88
91
  * the account holder alone: `to` is the `customer_email` variable, `cc` and
89
92
  * `bcc` are empty, and `list` answers exactly that whatever was stored before.
93
+ * So does `gift_card_issued`: whoever holds a copy of its code can spend the
94
+ * card, so it goes to the buyer alone.
90
95
  * Any other list on them is refused with `IDENTITY_EMAIL_RECIPIENTS_FIXED`
91
96
  * ({@link ValidationError}); resending the lists `list` answered is accepted.
92
97
  */
@@ -119,18 +124,19 @@ export interface UpdateNotificationEventConfigInput {
119
124
  *
120
125
  * **Coded refusals**, GraphQL errors mapped by `core`:
121
126
  * - `EVENT_ALWAYS_PLATFORM_SENT` → {@link ValidationError}: `sender: 'none'` on
122
- * an identity email. The message names the event; `details[0].field` is
123
- * `/sender`.
127
+ * an identity email or `gift_card_issued`. The message names the event;
128
+ * `details[0].field` is `/sender`.
124
129
  * - `EVENT_ALWAYS_ENABLED` → {@link ValidationError}: `enabled: false` on
125
- * `password_reset`, `email_change` or `account_setup`. The message names the
126
- * event and why it cannot be switched off; `details[0].field` is `/enabled`.
130
+ * `password_reset`, `email_change`, `account_setup` or `gift_card_issued`.
131
+ * The message names the event and why it cannot be switched off;
132
+ * `details[0].field` is `/enabled`.
127
133
  * - `NO_REPLACEMENT_EVENT` → {@link ValidationError}: `sender: 'none'` on an
128
134
  * email no emitted platform event replaces (`replacementEventTypes: null`).
129
135
  * The message names the event, and the event planned to replace it when one
130
136
  * is; `details[0].field` is `/sender`.
131
137
  * - `IDENTITY_EMAIL_RECIPIENTS_FIXED` → {@link ValidationError}: a recipient
132
- * list on an identity email (or `new_customer`) that names anyone but the
133
- * account holder. `details[].field` names each offending list
138
+ * list on an identity email (or `new_customer`, or `gift_card_issued`) that
139
+ * names anyone but the account holder. `details[].field` names each offending list
134
140
  * (`/recipientTo`, `/recipientCc`, `/recipientBcc`).
135
141
  * - `INVALID_NOTIFICATION_SENDER` → {@link ValidationError}.
136
142
  * - `NOTIFICATION_EVENT_NOT_FOUND` → {@link NotFoundError}: no such event key.
@@ -97,7 +97,7 @@ export interface SettingsSourcesModule {
97
97
  assets(group: SettingsGroup | (string & {}), call?: Call): Promise<AssetFile[]>;
98
98
  }
99
99
  export declare const CHANNEL_SETTINGS_SOURCES_QUERY = "\n query ChannelSettingsSources {\n channelSettingsSources { \n group\n source\n locked\n overridden\n inheritable\n organizationName\n values\n }\n }\n";
100
- export declare const CHANNEL_SETTINGS_ASSETS_QUERY = "\n query ChannelSettingsAssets($group: String!) {\n channelSettingsAssets(group: $group) { \n id\n createdAt\n updatedAt\n name\n type\n mimeType\n fileSize\n width\n height\n source\n preview\n focalPoint {\n x\n y\n }\n tags {\n value\n }\n }\n }\n";
100
+ export declare const CHANNEL_SETTINGS_ASSETS_QUERY = "\n query ChannelSettingsAssets($group: String!) {\n channelSettingsAssets(group: $group) { \n id\n createdAt\n updatedAt\n name\n type\n mimeType\n fileSize\n width\n height\n source\n preview\n focalPoint {\n x\n y\n }\n tags {\n value\n }\n customFields {\n altText\n }\n }\n }\n";
101
101
  export declare const OVERRIDE_ORGANIZATION_SETTINGS_MUTATION = "\n mutation OverrideOrganizationSettings($group: String!) {\n overrideOrganizationSettings(group: $group) { \n group\n source\n locked\n overridden\n inheritable\n organizationName\n values\n }\n }\n";
102
102
  export declare const INHERIT_ORGANIZATION_SETTINGS_MUTATION = "\n mutation InheritOrganizationSettings($group: String!) {\n inheritOrganizationSettings(group: $group) { \n group\n source\n locked\n overridden\n inheritable\n organizationName\n values\n }\n }\n";
103
103
  export declare function createSettingsSourcesModule(transport: Transport): SettingsSourcesModule;
@@ -0,0 +1,17 @@
1
+ import type { Transport } from '@sixtyninecommerce/core';
2
+ import type { Call } from '../types.js';
3
+ /**
4
+ * `admin.tenant.timeZone(call?)`: the ACTIVE store's IANA time zone (`Europe/Amsterdam`,
5
+ * `America/New_York`, …) as the engine resolves it: the store's own setting, or
6
+ * `Europe/Amsterdam` when it has none or an invalid one. It is the zone the API counts calendar
7
+ * days in (a requested delivery day, "shipped today", an invoice date), so render dates and
8
+ * "today" in it, never in the browser's or the server's own zone, and never re-check the
9
+ * setting yourself: the engine owns the rule.
10
+ *
11
+ * Any signed-in caller of the store may read it. No channel argument: the tenant is the store.
12
+ *
13
+ * @effect read
14
+ */
15
+ export type TenantTimeZoneReader = (call?: Call) => Promise<string>;
16
+ export declare const STORE_TIME_ZONE_QUERY = "\n query StoreTimeZone {\n storeTimeZone\n }\n";
17
+ export declare function createTenantTimeZoneReader(transport: Transport): TenantTimeZoneReader;
@@ -0,0 +1,11 @@
1
+ export const STORE_TIME_ZONE_QUERY = /* GraphQL */ `
2
+ query StoreTimeZone {
3
+ storeTimeZone
4
+ }
5
+ `;
6
+ export function createTenantTimeZoneReader(transport) {
7
+ return async (call) => {
8
+ const data = await transport.graphql({ api: 'admin', query: STORE_TIME_ZONE_QUERY }, call);
9
+ return data.storeTimeZone;
10
+ };
11
+ }
package/dist/types.d.ts CHANGED
@@ -5,15 +5,22 @@ export interface Call {
5
5
  * Sent as `Idempotency-Key` on a REST write. On a GraphQL one it is sent as
6
6
  * an ARGUMENT — the Admin API has no header for it — and only the mutations
7
7
  * that declare one act on it. Today those are the money mutations
8
- * `payments.record`, `orders.park`, `orders.settle` and all three
8
+ * `payments.record`, `orders.park`, `orders.settle`, `orders.cancelWithMoney` and all three
9
9
  * `orders.surcharges.*`, plus `payments.providers.completeOnboarding`,
10
10
  * `payments.providers.terminals.provision`, `stock.adjust`,
11
11
  * `hooks.assignments.assign`, `customers.upsertByExternalRef`,
12
12
  * `customers.groups.replaceMembers`, `pricing.coupons.apply` / `remove`,
13
- * `orders.setLinePrices` / `setOneTimeDiscounts`,
13
+ * `orders.setLinePrices` / `setOneTimeDiscounts`, `orders.correctFulfillmentQuantities`,
14
14
  * `pos.sale.applyCouponCode` / `removeCouponCode`, `storedValue.issue` / `adjust` /
15
- * `redeem`, `apps.releaseFieldGroup` and `catalog.products.bulk` (which mints one when
16
- * omitted). Repeating one under the same key
15
+ * `redeem`, `apps.releaseFieldGroup`, `catalog.products.bulk` and `orders.bulk` (both mint
16
+ * one when omitted), `catalog.products.save`, `orders.drafts.saveAddresses`,
17
+ * `orders.drafts.place` and `orders.drafts.cloneFrom` (both mint one when omitted),
18
+ * `fulfillments.create` and `fulfillments.warehouse.append` (both mint one when omitted),
19
+ * `quotes.convertToOrder` (mints one when omitted), `documents.issuePartialCreditNote` (mints
20
+ * one when omitted), and the
21
+ * accounts-receivable money doors `payments.ar.recordPayment` / `recordPrepaidDrawdown` /
22
+ * `directDebit.collectNow` and `documents.ar.issueCreditNote` / `generateInvoice` /
23
+ * `generateStatement`, and `returns.settle`. Repeating one under the same key
17
24
  * returns the first call's result instead of moving the money again; a
18
25
  * DIFFERENT request under the same key is refused with a `ConflictError`
19
26
  * (409) coded `IDEMPOTENCY_CONFLICT`, and a repeat that arrives while the
@@ -54,6 +61,46 @@ export interface ListOptions extends Call {
54
61
  after?: string;
55
62
  limit?: number;
56
63
  }
64
+ /**
65
+ * One argument a configurable operation takes, as the engine publishes its definition: a
66
+ * payment handler's `apiKey`, an eligibility checker's `customerGroupIds`.
67
+ */
68
+ export interface ConfigArgDefinition {
69
+ name: string;
70
+ /** The value's type as the engine names it: `string`, `int`, `float`, `boolean`, `datetime`, `ID`. */
71
+ type: string;
72
+ /** A list is sent as one JSON-encoded string, like every argument value. */
73
+ list: boolean;
74
+ /**
75
+ * NOT the enforced rule, in either direction. The engine publishes `true` for every argument
76
+ * whose declaration merely omits the flag, including ones nothing enforces, and can publish
77
+ * `false` for one a write then refuses. Never gate a form or a request on it: send what you
78
+ * have and read the engine's refusal.
79
+ */
80
+ required: boolean;
81
+ /** The declared default as JSON, or `null` when the argument has none. */
82
+ defaultValue: unknown;
83
+ /** In the language of the request; `null` when the declaration gives none. */
84
+ label: string | null;
85
+ /** In the language of the request; `null` when the declaration gives none. */
86
+ description: string | null;
87
+ /**
88
+ * How a form should render the argument, as declared: `{ component: 'password-form-input' }`
89
+ * marks a credential, `options` a fixed choice. `null` when nothing is declared.
90
+ */
91
+ ui: Record<string, unknown> | null;
92
+ }
93
+ /**
94
+ * A configurable operation the engine has registered (a payment handler, an eligibility
95
+ * checker, a shipping calculator, a promotion action): the `code` a stored operation names, and
96
+ * the arguments it takes.
97
+ */
98
+ export interface ConfigurableOperationDefinition {
99
+ code: string;
100
+ /** What the operation does, in the language of the request. */
101
+ description: string;
102
+ args: ConfigArgDefinition[];
103
+ }
57
104
  /** One channel the signed-in principal holds a role on. */
58
105
  export interface MeChannel {
59
106
  id: string;