@sixtyninecommerce/admin 0.10.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (247) hide show
  1. package/README.md +4347 -171
  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 +159 -19
  9. package/dist/catalog/products.js +88 -13
  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 +233 -26
  21. package/dist/catalog/variants.js +125 -7
  22. package/dist/catalog/visibility.d.ts +20 -5
  23. package/dist/catalog/visibility.js +19 -1
  24. package/dist/client.d.ts +95 -9
  25. package/dist/client.js +35 -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 +44 -10
  63. package/dist/fulfillments/index.js +11 -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 +123 -35
  71. package/dist/index.js +45 -12
  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 +29 -3
  87. package/dist/integrations/index.js +18 -3
  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 +15 -14
  91. package/dist/integrations/pipelines.js +6 -22
  92. package/dist/integrations/types.d.ts +146 -0
  93. package/dist/locations/index.d.ts +66 -0
  94. package/dist/locations/index.js +31 -0
  95. package/dist/meta/index.d.ts +162 -0
  96. package/dist/orders/cancel-with-money.d.ts +227 -0
  97. package/dist/orders/cancel-with-money.js +153 -0
  98. package/dist/orders/commit-edit.d.ts +7 -1
  99. package/dist/orders/commit-edit.js +5 -1
  100. package/dist/orders/drafts.d.ts +800 -0
  101. package/dist/orders/drafts.js +771 -0
  102. package/dist/orders/fields.d.ts +245 -0
  103. package/dist/orders/fields.js +188 -0
  104. package/dist/orders/get.d.ts +29 -19
  105. package/dist/orders/get.js +17 -8
  106. package/dist/orders/index.d.ts +49 -9
  107. package/dist/orders/index.js +22 -2
  108. package/dist/orders/lifecycle.d.ts +128 -0
  109. package/dist/orders/lifecycle.js +158 -0
  110. package/dist/orders/line-price-preview.d.ts +7 -2
  111. package/dist/orders/list.d.ts +222 -0
  112. package/dist/orders/list.js +377 -0
  113. package/dist/orders/manual-pricing.d.ts +5 -0
  114. package/dist/orders/order-entry.d.ts +20 -7
  115. package/dist/orders/plan-edit.d.ts +1 -1
  116. package/dist/orders/plan-edit.js +4 -1
  117. package/dist/orders/settings.d.ts +201 -0
  118. package/dist/orders/settings.js +325 -0
  119. package/dist/orders/status.d.ts +159 -13
  120. package/dist/orders/status.js +21 -0
  121. package/dist/orders/surcharges.d.ts +8 -1
  122. package/dist/orders/surcharges.js +3 -2
  123. package/dist/payments/account-credit.d.ts +30 -0
  124. package/dist/payments/account-credit.js +19 -0
  125. package/dist/payments/ar.d.ts +292 -0
  126. package/dist/payments/ar.js +223 -0
  127. package/dist/payments/connected-account.d.ts +47 -0
  128. package/dist/payments/connected-account.js +61 -0
  129. package/dist/payments/credit-noted-fields.d.ts +12 -0
  130. package/dist/payments/credit-noted-fields.js +37 -0
  131. package/dist/payments/index.d.ts +257 -11
  132. package/dist/payments/index.js +131 -1
  133. package/dist/payments/methods.d.ts +303 -8
  134. package/dist/payments/methods.js +337 -7
  135. package/dist/payments/mollie-methods.d.ts +159 -0
  136. package/dist/payments/mollie-methods.js +141 -0
  137. package/dist/payments/payment-links.d.ts +180 -0
  138. package/dist/payments/payment-links.js +97 -0
  139. package/dist/payments/provider-keys.d.ts +150 -0
  140. package/dist/payments/provider-keys.js +118 -0
  141. package/dist/payments/providers.d.ts +86 -11
  142. package/dist/payments/providers.js +29 -0
  143. package/dist/payments/requests.d.ts +144 -0
  144. package/dist/payments/requests.js +92 -0
  145. package/dist/payments/store-credit-fields.d.ts +15 -0
  146. package/dist/payments/store-credit-fields.js +51 -0
  147. package/dist/pickups/index.d.ts +104 -0
  148. package/dist/pickups/index.js +105 -0
  149. package/dist/platform/channels.d.ts +132 -0
  150. package/dist/platform/channels.js +128 -0
  151. package/dist/platform/geo.d.ts +123 -0
  152. package/dist/platform/geo.js +144 -0
  153. package/dist/platform/tax.d.ts +154 -0
  154. package/dist/platform/tax.js +139 -0
  155. package/dist/pos/anomalies.d.ts +158 -0
  156. package/dist/pos/anomalies.js +64 -0
  157. package/dist/pos/assisted-selling.d.ts +79 -0
  158. package/dist/pos/assisted-selling.js +38 -0
  159. package/dist/pos/exchanges.d.ts +103 -0
  160. package/dist/pos/exchanges.js +22 -0
  161. package/dist/pos/gift-cards.d.ts +135 -0
  162. package/dist/pos/gift-cards.js +47 -0
  163. package/dist/pos/index.d.ts +2125 -32
  164. package/dist/pos/index.js +742 -4
  165. package/dist/pos/no-receipt.d.ts +94 -0
  166. package/dist/pos/no-receipt.js +25 -0
  167. package/dist/pos/offline-sales.d.ts +187 -0
  168. package/dist/pos/offline-sales.js +116 -0
  169. package/dist/pos/pay-by-link.d.ts +143 -0
  170. package/dist/pos/pay-by-link.js +57 -0
  171. package/dist/pos/quick-keys.d.ts +103 -0
  172. package/dist/pos/quick-keys.js +45 -0
  173. package/dist/pos/reports.d.ts +360 -0
  174. package/dist/pos/reports.js +62 -0
  175. package/dist/pos/returns.d.ts +217 -0
  176. package/dist/pos/returns.js +53 -0
  177. package/dist/pricing/bulk-coupons.d.ts +139 -0
  178. package/dist/pricing/bulk-coupons.js +112 -0
  179. package/dist/pricing/index.d.ts +174 -4
  180. package/dist/pricing/index.js +174 -1
  181. package/dist/pricing/price-lists.d.ts +604 -0
  182. package/dist/pricing/price-lists.js +600 -0
  183. package/dist/purchasing/alerts.d.ts +89 -0
  184. package/dist/purchasing/alerts.js +62 -0
  185. package/dist/purchasing/cost.d.ts +110 -0
  186. package/dist/purchasing/cost.js +86 -0
  187. package/dist/purchasing/index.d.ts +36 -0
  188. package/dist/purchasing/index.js +16 -0
  189. package/dist/purchasing/purchase-orders.d.ts +548 -0
  190. package/dist/purchasing/purchase-orders.js +491 -0
  191. package/dist/purchasing/replenishment.d.ts +281 -0
  192. package/dist/purchasing/replenishment.js +240 -0
  193. package/dist/purchasing/settings.d.ts +152 -0
  194. package/dist/purchasing/settings.js +181 -0
  195. package/dist/purchasing/suppliers.d.ts +476 -0
  196. package/dist/purchasing/suppliers.js +438 -0
  197. package/dist/quotes/index.d.ts +501 -0
  198. package/dist/quotes/index.js +439 -0
  199. package/dist/reports/index.d.ts +71 -8
  200. package/dist/reports/index.js +74 -11
  201. package/dist/returns/index.d.ts +614 -21
  202. package/dist/returns/index.js +695 -11
  203. package/dist/search/index.d.ts +76 -1
  204. package/dist/search/index.js +63 -0
  205. package/dist/shipping/definitions.d.ts +29 -0
  206. package/dist/shipping/definitions.js +47 -0
  207. package/dist/shipping/fulfillment-profiles.d.ts +174 -0
  208. package/dist/shipping/fulfillment-profiles.js +104 -0
  209. package/dist/shipping/hendriks.d.ts +97 -0
  210. package/dist/shipping/hendriks.js +50 -0
  211. package/dist/shipping/index.d.ts +48 -0
  212. package/dist/shipping/index.js +25 -0
  213. package/dist/shipping/methods.d.ts +198 -0
  214. package/dist/shipping/methods.js +240 -0
  215. package/dist/shipping/myparcel.d.ts +254 -0
  216. package/dist/shipping/myparcel.js +178 -0
  217. package/dist/shipping/parcel-count-suggestion.d.ts +2 -0
  218. package/dist/shipping/parcel-rule.d.ts +6 -5
  219. package/dist/shipping/parcel-rule.js +21 -23
  220. package/dist/shipping/rate-tables.d.ts +179 -0
  221. package/dist/shipping/rate-tables.js +184 -0
  222. package/dist/stock/index.d.ts +10 -3
  223. package/dist/stock/index.js +3 -0
  224. package/dist/stock/locations.d.ts +144 -0
  225. package/dist/stock/locations.js +168 -0
  226. package/dist/stock/transfers.d.ts +216 -43
  227. package/dist/stock/transfers.js +140 -13
  228. package/dist/stored-value/index.d.ts +214 -4
  229. package/dist/stored-value/index.js +152 -0
  230. package/dist/subscriptions/index.d.ts +682 -0
  231. package/dist/subscriptions/index.js +815 -0
  232. package/dist/tax/index.d.ts +76 -1
  233. package/dist/tax/index.js +50 -0
  234. package/dist/tenant/email-style.d.ts +102 -0
  235. package/dist/tenant/email-style.js +72 -0
  236. package/dist/tenant/email-templates.d.ts +66 -0
  237. package/dist/tenant/email-templates.js +54 -0
  238. package/dist/tenant/notifications.d.ts +23 -17
  239. package/dist/tenant/settings-sources.d.ts +1 -1
  240. package/dist/tenant/time-zone.d.ts +17 -0
  241. package/dist/tenant/time-zone.js +11 -0
  242. package/dist/types.d.ts +50 -3
  243. package/dist/users/index.d.ts +3 -0
  244. package/dist/users/index.js +2 -0
  245. package/dist/users/warehouses.d.ts +50 -0
  246. package/dist/users/warehouses.js +51 -0
  247. package/package.json +2 -2
@@ -0,0 +1,239 @@
1
+ import { ValidationError } from '@sixtyninecommerce/core';
2
+ import { createSearchModule, escapeSearchFilterValue } from '../search/index.js';
3
+ import { fieldOf } from './types.js';
4
+ /** The most variants one `variants.find` answers. */
5
+ export const MAX_VARIANT_FIND_TAKE = 500;
6
+ /** The most products one `variants.find` search reads (`take / perProduct`, rounded up). */
7
+ export const MAX_VARIANT_FIND_PRODUCTS = 100;
8
+ const FOUND_VARIANT_PRODUCT_FIELDS = /* GraphQL */ `
9
+ id
10
+ name
11
+ slug
12
+ featuredAsset {
13
+ id
14
+ preview
15
+ }
16
+ `;
17
+ /**
18
+ * What a picker reads of a variant. `storePrice`, never core's `price` / `priceWithTax`: those
19
+ * throw for a variant the store has no price for, and one such variant would empty the answer.
20
+ */
21
+ export const FOUND_VARIANT_FIELDS = /* GraphQL */ `
22
+ id
23
+ name
24
+ sku
25
+ enabled
26
+ storePrice {
27
+ price
28
+ priceWithTax
29
+ currencyCode
30
+ }
31
+ trackInventory
32
+ outOfStockThreshold
33
+ useGlobalOutOfStockThreshold
34
+ stockOnHand
35
+ stockAllocated
36
+ stockLevels {
37
+ stockLocationId
38
+ stockOnHand
39
+ stockAllocated
40
+ stockLocation {
41
+ id
42
+ name
43
+ }
44
+ }
45
+ `;
46
+ /** A found variant with its product: the selection of every list that answers variants flat. */
47
+ export const FOUND_VARIANT_WITH_PRODUCT_FIELDS = /* GraphQL */ `
48
+ ${FOUND_VARIANT_FIELDS}
49
+ product {
50
+ ${FOUND_VARIANT_PRODUCT_FIELDS}
51
+ }
52
+ `;
53
+ export const FIND_VARIANTS_BY_IDS_QUERY = /* GraphQL */ `
54
+ query CatalogFindVariantsByIds($options: ProductVariantListOptions) {
55
+ productVariants(options: $options) {
56
+ items {
57
+ ${FOUND_VARIANT_WITH_PRODUCT_FIELDS}
58
+ }
59
+ totalItems
60
+ }
61
+ }
62
+ `;
63
+ /**
64
+ * The variants of some products, bounded PER PRODUCT (`variantList`, never `variants`, which the
65
+ * engine answers whole and then cuts at 1,000 without saying so).
66
+ */
67
+ export const FIND_VARIANTS_OF_PRODUCTS_QUERY = /* GraphQL */ `
68
+ query CatalogFindVariantsOfProducts(
69
+ $options: ProductListOptions
70
+ $variantOptions: ProductVariantListOptions
71
+ ) {
72
+ products(options: $options) {
73
+ items {
74
+ ${FOUND_VARIANT_PRODUCT_FIELDS}
75
+ variantList(options: $variantOptions) {
76
+ items {
77
+ ${FOUND_VARIANT_FIELDS}
78
+ }
79
+ totalItems
80
+ }
81
+ }
82
+ totalItems
83
+ }
84
+ }
85
+ `;
86
+ export function toFoundVariant(wire) {
87
+ const { stockLevels, product, ...rest } = wire;
88
+ return {
89
+ ...rest,
90
+ stockLevels: stockLevels.map((level) => ({
91
+ stockLocationId: level.stockLocationId,
92
+ stockLocationName: level.stockLocation?.name ?? '',
93
+ stockOnHand: level.stockOnHand,
94
+ stockAllocated: level.stockAllocated,
95
+ })),
96
+ product: {
97
+ id: product.id,
98
+ name: product.name,
99
+ slug: product.slug,
100
+ featuredAsset: product.featuredAsset,
101
+ },
102
+ };
103
+ }
104
+ function invalid(message) {
105
+ return new ValidationError({
106
+ code: 'VARIANT_FIND_INPUT_INVALID',
107
+ message,
108
+ status: 400,
109
+ service: 'vendure',
110
+ });
111
+ }
112
+ function wholeNumber(value, low, high) {
113
+ return typeof value === 'number' && Number.isInteger(value) && value >= low && value <= high;
114
+ }
115
+ /** A literal inside a regular expression the engine evaluates (Postgres `~*`). */
116
+ function escapeRegex(value) {
117
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
118
+ }
119
+ function criterionOf(input) {
120
+ const given = ['ids', 'term', 'skuPrefix'].filter((key) => input[key] !== undefined);
121
+ if (given.length !== 1) {
122
+ throw invalid(`Say which variants with exactly one of ids, term and skuPrefix; got ${given.length === 0 ? 'none' : given.join(' and ')}.`);
123
+ }
124
+ if (input.ids !== undefined) {
125
+ if (!Array.isArray(input.ids) || input.ids.some((id) => typeof id !== 'string' || id === '')) {
126
+ throw invalid('ids must be a list of variant ids.');
127
+ }
128
+ return { kind: 'ids', ids: [...new Set(input.ids)] };
129
+ }
130
+ const key = input.term !== undefined ? 'term' : 'skuPrefix';
131
+ const text = input[key];
132
+ if (typeof text !== 'string' || text.trim() === '') {
133
+ throw invalid(`${key} must be text to search for; an empty one finds nothing.`);
134
+ }
135
+ return key === 'term'
136
+ ? { kind: 'term', term: text.trim() }
137
+ : { kind: 'skuPrefix', prefix: text.trim() };
138
+ }
139
+ /** The call's options with the currency the prices are asked in. */
140
+ function pricedIn(options, currencyCode) {
141
+ if (currencyCode === undefined)
142
+ return options;
143
+ if (typeof currencyCode !== 'string' || currencyCode.trim() === '') {
144
+ throw invalid('currencyCode must be an ISO 4217 code, or left out for the store default.');
145
+ }
146
+ return { ...options, tenant: { ...options?.tenant, currency: currencyCode } };
147
+ }
148
+ async function findByIds(transport, ids, take, options) {
149
+ if (ids.length > take) {
150
+ throw invalid(`ids names ${String(ids.length)} variants and take is ${String(take)}. Raise take, or ask for fewer.`);
151
+ }
152
+ if (ids.length === 0)
153
+ return { items: [], products: [] };
154
+ const list = await fieldOf(transport, FIND_VARIANTS_BY_IDS_QUERY, 'productVariants',
155
+ // Sorted so the engine's page is one total order; the answer is re-ordered below.
156
+ { options: { filter: { id: { in: ids } }, take: ids.length, sort: { id: 'ASC' } } }, options);
157
+ const found = new Map(list.items.map((row) => [row.id, row]));
158
+ return {
159
+ items: ids.flatMap((id) => {
160
+ const row = found.get(id);
161
+ return row ? [toFoundVariant(row)] : [];
162
+ }),
163
+ products: [],
164
+ };
165
+ }
166
+ async function findBySearch(transport, criterion, take, perProduct, options, pricedOptions) {
167
+ const productCount = Math.ceil(take / perProduct);
168
+ if (productCount > MAX_VARIANT_FIND_PRODUCTS) {
169
+ throw invalid(`take ${String(take)} at ${String(perProduct)} per product reads ${String(productCount)} products; at most ${String(MAX_VARIANT_FIND_PRODUCTS)}. Raise perProduct or lower take.`);
170
+ }
171
+ // Which products, and in what order, is the index's answer (`search.index`, never a copy of
172
+ // it): relevance for a term, the product's name for a SKU prefix.
173
+ const hits = await createSearchModule(transport).index('products', criterion.kind === 'term'
174
+ ? { term: criterion.term, take: productCount }
175
+ : {
176
+ term: '',
177
+ filters: [`skus STARTS WITH "${escapeSearchFilterValue(criterion.prefix)}"`],
178
+ sort: 'name',
179
+ take: productCount,
180
+ }, options);
181
+ const productIds = hits.items.map((hit) => hit.id);
182
+ if (productIds.length === 0)
183
+ return { items: [], products: [] };
184
+ const list = await fieldOf(transport, FIND_VARIANTS_OF_PRODUCTS_QUERY, 'products', {
185
+ options: { filter: { id: { in: productIds } }, take: productIds.length, sort: { id: 'ASC' } },
186
+ variantOptions: {
187
+ take: perProduct,
188
+ // A total order, so a bounded page is the same page on every read. By id alone: the
189
+ // engine applies the keys of a sort in the order its schema declares them, where `id`
190
+ // precedes `position`, so the `position, id` the pickers used to send was this order.
191
+ sort: { id: 'ASC' },
192
+ // Only a SKU search narrows the variants. A term matches the PRODUCT (its name, its
193
+ // description), and a variant's name is its options, so filtering variants by it would
194
+ // answer a product with nothing to pick.
195
+ ...(criterion.kind === 'skuPrefix'
196
+ ? { filter: { sku: { regex: `^${escapeRegex(criterion.prefix)}` } } }
197
+ : {}),
198
+ },
199
+ }, pricedOptions);
200
+ const read = new Map(list.items.map((product) => [product.id, product]));
201
+ const items = [];
202
+ const products = [];
203
+ for (const id of productIds) {
204
+ const room = take - items.length;
205
+ if (room <= 0)
206
+ break;
207
+ // The index can name a product the store no longer holds: it is updated after each change.
208
+ const product = read.get(id);
209
+ if (!product)
210
+ continue;
211
+ const rows = product.variantList.items.slice(0, room);
212
+ if (rows.length === 0)
213
+ continue;
214
+ for (const row of rows)
215
+ items.push(toFoundVariant({ ...row, product }));
216
+ products.push({ id: product.id, shown: rows.length, total: product.variantList.totalItems });
217
+ }
218
+ return { items, products };
219
+ }
220
+ /** `catalog.variants.find`; its contract is on `VariantsModule.find`. */
221
+ export async function findVariants(transport, input, options) {
222
+ if (typeof input !== 'object' || input === null)
223
+ throw invalid('find takes an input object.');
224
+ const criterion = criterionOf(input);
225
+ const { take, perProduct } = input;
226
+ if (!wholeNumber(take, 1, MAX_VARIANT_FIND_TAKE)) {
227
+ throw invalid(`take must be a whole number from 1 to ${String(MAX_VARIANT_FIND_TAKE)}.`);
228
+ }
229
+ const pricedOptions = pricedIn(options, input.currencyCode);
230
+ if (criterion.kind === 'ids') {
231
+ if (perProduct !== undefined)
232
+ throw invalid('perProduct bounds a search; ids takes none.');
233
+ return findByIds(transport, criterion.ids, take, pricedOptions);
234
+ }
235
+ if (perProduct !== undefined && !wholeNumber(perProduct, 1, take)) {
236
+ throw invalid(`perProduct must be a whole number from 1 to take (${String(take)}).`);
237
+ }
238
+ return findBySearch(transport, criterion, take, perProduct ?? Math.ceil(take / 10), options, pricedOptions);
239
+ }
@@ -1,6 +1,7 @@
1
1
  import { type Page, type Transport } from '@sixtyninecommerce/core';
2
2
  import type { Call, ListOptions } from '../types.js';
3
- import { type AssetRef, type Availability, type AvailabilityInput, type FacetValueRef, type LanguageCode, type PriceBasis, type TranslationInput, type VariantAvailabilityState } from './types.js';
3
+ import { type AssetRef, type Availability, type AvailabilityInput, type FacetValueRef, type LanguageCode, type PriceBasis, type TranslationInput, type VariantAvailabilityState, type WireAssetRef } from './types.js';
4
+ import { type FindVariantsInput, type FoundVariants } from './variant-find.js';
4
5
  export interface StockLevel {
5
6
  stockLocationId: string;
6
7
  stockOnHand: number;
@@ -31,16 +32,32 @@ export interface VariantPriceInput {
31
32
  price: number;
32
33
  }
33
34
  /**
34
- * A product variant — the thing that has a SKU, a price and stock.
35
+ * A variant's price in the active store, in the currency of the request (the store's default,
36
+ * unless the call names another with `tenant.currency`). Integer minor units of `currencyCode`.
35
37
  *
36
- * Money is integer minor units of `currencyCode`. On READ, `price` is ALWAYS
37
- * net and `priceWithTax` ALWAYS gross, whatever the store's basis — core
38
- * derives whichever side it does not store, at the rate of the store's default
39
- * tax zone. On WRITE, `price` is in the STORE's basis (`priceBasis`). So a
40
- * read-modify-write on a gross store (`pricesIncludeTax: true`) must start
41
- * from `priceWithTax`: writing back the `price` it read would drop the shelf
42
- * price by the whole VAT amount.
38
+ * `price` is ALWAYS net and `priceWithTax` ALWAYS gross, whatever the store's basis — core
39
+ * derives whichever side it does not store, at the rate of the store's default tax zone. A write
40
+ * states `price` in the STORE's basis (`priceBasis`), so a read-modify-write on a gross store
41
+ * (`pricesIncludeTax: true`) must start from `priceWithTax`: writing back the `price` it read
42
+ * would drop the shelf price by the whole VAT amount.
43
43
  */
44
+ export interface VariantStorePrice {
45
+ /** Net, always — never the figure to write back on a gross store. */
46
+ price: number;
47
+ /** Gross, always, at the store's default tax zone. */
48
+ priceWithTax: number;
49
+ currencyCode: string;
50
+ }
51
+ /**
52
+ * One price the store STORES for a variant, in one currency: integer minor units on the store's
53
+ * basis (gross on a store with `pricesIncludeTax`, net otherwise), exactly what a write takes. A
54
+ * currency the store converts from another has no entry; see {@link ProductVariant.storePrice}.
55
+ */
56
+ export interface VariantStoredPrice {
57
+ currencyCode: string;
58
+ price: number;
59
+ }
60
+ /** A product variant — the thing that has a SKU, a price and stock. */
44
61
  export interface ProductVariant extends Availability<VariantAvailabilityState> {
45
62
  id: string;
46
63
  productId: string;
@@ -50,11 +67,23 @@ export interface ProductVariant extends Availability<VariantAvailabilityState> {
50
67
  name: string;
51
68
  sku: string;
52
69
  enabled: boolean;
53
- /** Net, always — never the figure to write back on a gross store. */
54
- price: number;
55
- /** Gross, always, at the store's default tax zone. */
56
- priceWithTax: number;
57
- currencyCode: string;
70
+ /**
71
+ * The prices the active store stores for the variant, one per currency (see
72
+ * {@link VariantStoredPrice}); `[]` when it stores none.
73
+ */
74
+ prices: VariantStoredPrice[];
75
+ /**
76
+ * The engine's on-hand total for the variant in the active store (core's `stockOnHand`, which
77
+ * per-location out-of-stock thresholds can adjust); `stockLevels` has the count at each location.
78
+ */
79
+ stockOnHand: number;
80
+ /**
81
+ * The variant's price in the active store, or null when the store has no price for it in the
82
+ * request's currency: none stored there and none it converts from. Never 0 for a missing price;
83
+ * a 0 here is a price of 0. Needs an engine with `ProductVariant.storePrice` (vendure-server
84
+ * at or after #1504).
85
+ */
86
+ storePrice: VariantStorePrice | null;
58
87
  taxCategory: {
59
88
  id: string;
60
89
  name: string;
@@ -80,6 +109,36 @@ export interface ProductVariant extends Availability<VariantAvailabilityState> {
80
109
  parcelCapacity: number | null;
81
110
  /** Never null on read: an unset packing is `shared`, the engine's default. */
82
111
  parcelPacking: ParcelPacking;
112
+ /** The fewest units one order line may carry; null for no minimum. */
113
+ minOrderQuantity: number | null;
114
+ /** The units a line's quantity moves in (6: 6, 12, 18 …); null for one. */
115
+ salesUnit: number | null;
116
+ /**
117
+ * Where the variant sits in its product's order (the dashboard's "Reorder"); null for a variant
118
+ * nobody placed, which {@link VariantsModule.list} puts after the placed ones.
119
+ */
120
+ position: number | null;
121
+ /**
122
+ * What one unit costs the store: NET, whatever the store's price basis (`prices` may be gross),
123
+ * integer minor units of its default currency; null when unset. Informational: no price or tax
124
+ * reads it.
125
+ */
126
+ cost: number | null;
127
+ packageWeightGrams: number | null;
128
+ packageLengthCm: number | null;
129
+ packageWidthCm: number | null;
130
+ packageHeightCm: number | null;
131
+ /**
132
+ * The variant's backorder rule; null follows the store's (`inherit`). A string on the wire: a
133
+ * newer engine may add a policy.
134
+ */
135
+ backorderPolicy: VariantBackorderPolicy | (string & {}) | null;
136
+ /** The units a `limited` variant may sell below zero; null when unset. */
137
+ backorderLimit: number | null;
138
+ /** GS1 barcode (the product-feeds plugin's identifier); null when unset. */
139
+ gtin: string | null;
140
+ /** Manufacturer part number; null when unset. */
141
+ mpn: string | null;
83
142
  stockLevels: StockLevel[];
84
143
  options: Array<{
85
144
  id: string;
@@ -143,7 +202,10 @@ interface VariantFields extends AvailabilityInput<VariantAvailabilityState> {
143
202
  minOrderQuantity?: number | null;
144
203
  /** The units a line's quantity moves in (6: 6, 12, 18 …); null for one. */
145
204
  salesUnit?: number | null;
146
- /** What one unit costs the store, integer minor units of its default currency; null clears it. */
205
+ /**
206
+ * What one unit costs the store: NET, whatever the store's price basis, integer minor units of
207
+ * its default currency; null clears it.
208
+ */
147
209
  cost?: number | null;
148
210
  packageWeightGrams?: number | null;
149
211
  packageLengthCm?: number | null;
@@ -185,7 +247,7 @@ export interface VariantPriceSource {
185
247
  /**
186
248
  * What the store charges in this currency, in minor units, on the STORE's basis: gross on a
187
249
  * store with `pricesIncludeTax`, net otherwise — the basis a variant write takes, unlike
188
- * {@link ProductVariant.price}, which reads net everywhere. Null when nothing prices it: no
250
+ * {@link VariantStorePrice.price}, which reads net everywhere. Null when nothing prices it: no
189
251
  * stored price and no EUR price or exchange rate to convert from, or a currency the store
190
252
  * marked not for sale.
191
253
  */
@@ -200,6 +262,41 @@ export interface VariantPriceSource {
200
262
  /** The organization that shares the variant with the store; null for the store's own variant. */
201
263
  organizationName: string | null;
202
264
  }
265
+ /** A field {@link VariantsModule.list} can order a product's variants by. */
266
+ export type VariantSortField = 'position' | 'name' | 'sku' | 'createdAt' | 'updatedAt';
267
+ /** Options of {@link VariantsModule.list}. */
268
+ export interface VariantListOptions extends ListOptions {
269
+ /**
270
+ * Only the variants whose SKU OR name (in the request's language) contains this text, case
271
+ * insensitively. Blank or omitted: every variant.
272
+ */
273
+ term?: string;
274
+ /**
275
+ * The order of the pages. Default `{ field: 'position', direction: 'ASC' }`: the order a
276
+ * merchant arranged the variants in, the ones never placed after the placed ones. The engine
277
+ * completes every sort with the variant id, so a page boundary never repeats or skips a row.
278
+ */
279
+ sort?: {
280
+ field: VariantSortField;
281
+ direction: 'ASC' | 'DESC';
282
+ };
283
+ }
284
+ /** The variants {@link VariantsModule.optionSets} reads at most by default. */
285
+ export declare const DEFAULT_OPTION_SET_LIMIT = 1000;
286
+ /** The most variants one {@link VariantsModule.optionSets} call reads. */
287
+ export declare const MAX_OPTION_SET_LIMIT = 100000;
288
+ /** The option combinations a product's variants already have, as far as they were read. */
289
+ export interface VariantOptionSets {
290
+ /**
291
+ * One entry per variant read, in variant-id order: the ids of its options, sorted. A variant
292
+ * without options (a product's one default variant) reads `[]`.
293
+ */
294
+ combinations: string[][];
295
+ /** How many variants the product has in the store. */
296
+ totalItems: number;
297
+ /** The product has more variants than were read (`limit`): a combination absent here may exist. */
298
+ truncated: boolean;
299
+ }
203
300
  /** The engine's bound on one `revertPrice` call (`MAX_REVERT_VARIANTS`). */
204
301
  export declare const MAX_REVERT_PRICE_VARIANTS = 500;
205
302
  export interface CreateProductVariantInput extends VariantFields {
@@ -240,11 +337,99 @@ export interface UpdateProductVariantInput extends VariantFields {
240
337
  * refuses".
241
338
  */
242
339
  export interface VariantsModule {
243
- list(productId: string, options?: ListOptions): Promise<Page<ProductVariant>>;
340
+ /**
341
+ * One page of a product's variants (`limit` 1 to 1,000, default 25), in the order `sort` names
342
+ * and filtered by `term`; `page.total` is how many match. `for await` walks every page. Needs
343
+ * `ReadCatalog` or `ReadProduct`. An id this store does not hold answers an empty page.
344
+ *
345
+ * Refused before sending: `INVALID_PAGE_SIZE` (`ValidationError`, 400) for a `limit` outside
346
+ * 1 to 1,000; `INVALID_VARIANT_SORT` (`ValidationError`, 400) for a sort field or direction
347
+ * this method does not name.
348
+ *
349
+ * @effect read
350
+ */
351
+ list(productId: string, options?: VariantListOptions): Promise<Page<ProductVariant>>;
352
+ /** @effect read */
244
353
  get(id: string, options?: Call): Promise<ProductVariant | null>;
245
- /** All or nothing: the engine creates the batch in one transaction. */
354
+ /**
355
+ * The option combinations a product's variants already have, for a "create variants" form that
356
+ * must not offer a combination twice (the engine refuses one: "A ProductVariant with the
357
+ * selected options already exists"). Reads the variants in pages of up to 1,000, in id order, up
358
+ * to `limit` (default {@link DEFAULT_OPTION_SET_LIMIT}, at most {@link MAX_OPTION_SET_LIMIT});
359
+ * `truncated` says the product has more. Each page is one request: a high `limit` on a product
360
+ * with 100,000 variants is 100 of them. Needs `ReadCatalog` or `ReadProduct`; an id this store
361
+ * does not hold answers no combinations.
362
+ *
363
+ * Refused before sending: `INVALID_OPTION_SET_LIMIT` (`ValidationError`, 400) for a `limit`
364
+ * that is not a whole number from 1 to {@link MAX_OPTION_SET_LIMIT}.
365
+ *
366
+ * @effect read
367
+ */
368
+ optionSets(productId: string, options?: Call & {
369
+ limit?: number;
370
+ }): Promise<VariantOptionSets>;
371
+ /**
372
+ * The variants a picker offers, each with its store price, its stock and its product: by id
373
+ * (`ids`), by a full-text search (`term`) or by the start of a SKU (`skuPrefix`). Exactly one
374
+ * of the three.
375
+ *
376
+ * - **`ids`** answers exactly those variants, in the order asked. An id the store does not
377
+ * hold is left out.
378
+ * - **A search reads products first**: the best `take / perProduct` products (rounded up) of
379
+ * the store's product index, then at most `perProduct` variants of each, in a stable order
380
+ * (by id; not the order a merchant arranged a product's variants in). `products` says per product how many of its variants match and how many `items` holds, so
381
+ * a caller can tell a short product from a cut-off one. The index is updated by a job after
382
+ * each change: a product created seconds ago may be missing.
383
+ * - **A hidden carrier variant is never answered**, by id or by search: the platform's own
384
+ * variant behind externally priced order lines is not catalogue.
385
+ * - **A disabled or unpriced variant IS answered** (`enabled`, `storePrice: null`): the engine
386
+ * refuses to add either to an order (`ORDER_LINE_VARIANT_UNPRICED` for the second).
387
+ *
388
+ * Needs `ReadCatalog` or `ReadProduct`. Refused before anything is sent
389
+ * (`VARIANT_FIND_INPUT_INVALID`, a `ValidationError`): none or several of `ids`, `term` and
390
+ * `skuPrefix`; a blank `term` or `skuPrefix`; `take` outside 1 to
391
+ * {@link MAX_VARIANT_FIND_TAKE}; `perProduct` outside 1 to `take`, or given with `ids`; more
392
+ * than {@link MAX_VARIANT_FIND_PRODUCTS} products; more distinct ids than `take`. By the
393
+ * engine: an id that is not an id at all (`ENTITY_NOT_FOUND`, a `NotFoundError`), a currency
394
+ * the store does not sell, and `SERVICE_UNAVAILABLE` (retryable) when the search service did
395
+ * not answer a search.
396
+ *
397
+ * @effect read
398
+ */
399
+ find(input: FindVariantsInput, options?: Call): Promise<FoundVariants>;
400
+ /**
401
+ * Create variants, all or nothing: the engine creates the batch in one transaction. Needs
402
+ * `CreateCatalog` or `CreateProduct`. A `price` travels with `priceBasis` (see
403
+ * {@link PriceBasis}); its other currencies are converted until a write stores them.
404
+ *
405
+ * Refusals (nothing is written), besides the option-combination and basis refusals above:
406
+ * - `TRANSLATION_INPUT_INVALID` → `ValidationError` (400): a translation without a name
407
+ * (multi-region decision 106). One `details` entry per refused field, its message naming the
408
+ * language; the entries carry no `field` pointer.
409
+ * - `TRANSLATION_LANGUAGE_NOT_OFFERED` → `BusinessRuleError` (422): a translation in a
410
+ * language the store does not offer; `details[].field` points at the translation entry
411
+ * (`/input/<n>/translations/<i>`).
412
+ * - `FEED_IDENTIFIER_INVALID` → `ValidationError` (400): a GTIN that is not 8, 12, 13 or 14
413
+ * digits.
414
+ *
415
+ * @effect write
416
+ */
246
417
  create(inputs: CreateProductVariantInput[], options?: Call): Promise<ProductVariant[]>;
418
+ /** @effect write */
247
419
  update(inputs: UpdateProductVariantInput[], options?: Call): Promise<ProductVariant[]>;
420
+ /**
421
+ * Soft-delete variants: they are no longer listed, and orders keep the lines that name them.
422
+ * Nothing in this SDK restores one. Needs `DeleteCatalog` or `DeleteProduct`.
423
+ *
424
+ * Refused before anything is deleted: `ENTITY_NOT_FOUND` (`NotFoundError`, 404) for a variant
425
+ * this store does not hold; `SHARED_PRODUCT_VARIANT_NOT_ALLOWED` (`PermissionDeniedError`, 403)
426
+ * for one another store also holds that you may not change there; `FIELD_GROUP_LOCKED`
427
+ * (`BusinessRuleError`, 422) while an app or API token owns one of its field groups;
428
+ * `EXTERNAL_LINE_CARRIER_PROTECTED` (422) for the platform's carrier. A variant the engine
429
+ * declines is `VARIANT_NOT_DELETED` (`ConflictError`, 409) with its reasons.
430
+ *
431
+ * @effect destructive
432
+ */
248
433
  delete(ids: string[], options?: Call): Promise<void>;
249
434
  /**
250
435
  * Set stock on hand per stock location — absolute values, one update. A
@@ -286,28 +471,50 @@ export interface VariantsModule {
286
471
  currencyCodes?: readonly string[];
287
472
  }): Promise<ProductVariant[]>;
288
473
  }
289
- export declare const VARIANT_FIELDS = "\n id\n productId\n createdAt\n updatedAt\n languageCode\n name\n sku\n enabled\n price\n priceWithTax\n currencyCode\n taxCategory {\n id\n name\n }\n trackInventory\n outOfStockThreshold\n useGlobalOutOfStockThreshold\n stockLevels {\n stockLocationId\n stockOnHand\n stockAllocated\n }\n options {\n id\n code\n name\n groupId\n }\n featuredAsset { \n id\n name\n preview\n source\n }\n assets { \n id\n name\n preview\n source\n }\n facetValues { \n id\n code\n name\n facet {\n id\n code\n name\n }\n }\n translations {\n id\n languageCode\n name\n }\n customFields {\n taxClassKey\n availabilityState\n availableFrom\n preorderLimit\n parcelCapacity\n parcelPacking\n }\n";
290
- export declare const VARIANTS_QUERY = "\n query CatalogVariants($productId: ID!, $options: ProductVariantListOptions) {\n productVariants(productId: $productId, options: $options) {\n items { \n id\n productId\n createdAt\n updatedAt\n languageCode\n name\n sku\n enabled\n price\n priceWithTax\n currencyCode\n taxCategory {\n id\n name\n }\n trackInventory\n outOfStockThreshold\n useGlobalOutOfStockThreshold\n stockLevels {\n stockLocationId\n stockOnHand\n stockAllocated\n }\n options {\n id\n code\n name\n groupId\n }\n featuredAsset { \n id\n name\n preview\n source\n }\n assets { \n id\n name\n preview\n source\n }\n facetValues { \n id\n code\n name\n facet {\n id\n code\n name\n }\n }\n translations {\n id\n languageCode\n name\n }\n customFields {\n taxClassKey\n availabilityState\n availableFrom\n preorderLimit\n parcelCapacity\n parcelPacking\n }\n }\n totalItems\n }\n }\n";
291
- export declare const VARIANT_QUERY = "\n query CatalogVariant($id: ID!) {\n productVariant(id: $id) { \n id\n productId\n createdAt\n updatedAt\n languageCode\n name\n sku\n enabled\n price\n priceWithTax\n currencyCode\n taxCategory {\n id\n name\n }\n trackInventory\n outOfStockThreshold\n useGlobalOutOfStockThreshold\n stockLevels {\n stockLocationId\n stockOnHand\n stockAllocated\n }\n options {\n id\n code\n name\n groupId\n }\n featuredAsset { \n id\n name\n preview\n source\n }\n assets { \n id\n name\n preview\n source\n }\n facetValues { \n id\n code\n name\n facet {\n id\n code\n name\n }\n }\n translations {\n id\n languageCode\n name\n }\n customFields {\n taxClassKey\n availabilityState\n availableFrom\n preorderLimit\n parcelCapacity\n parcelPacking\n }\n }\n }\n";
292
- export declare const CREATE_VARIANTS_MUTATION = "\n mutation CatalogCreateVariants($input: [CreateProductVariantInput!]!) {\n createProductVariants(input: $input) { \n id\n productId\n createdAt\n updatedAt\n languageCode\n name\n sku\n enabled\n price\n priceWithTax\n currencyCode\n taxCategory {\n id\n name\n }\n trackInventory\n outOfStockThreshold\n useGlobalOutOfStockThreshold\n stockLevels {\n stockLocationId\n stockOnHand\n stockAllocated\n }\n options {\n id\n code\n name\n groupId\n }\n featuredAsset { \n id\n name\n preview\n source\n }\n assets { \n id\n name\n preview\n source\n }\n facetValues { \n id\n code\n name\n facet {\n id\n code\n name\n }\n }\n translations {\n id\n languageCode\n name\n }\n customFields {\n taxClassKey\n availabilityState\n availableFrom\n preorderLimit\n parcelCapacity\n parcelPacking\n }\n }\n }\n";
293
- export declare const UPDATE_VARIANTS_MUTATION = "\n mutation CatalogUpdateVariants($input: [UpdateProductVariantInput!]!) {\n updateProductVariants(input: $input) { \n id\n productId\n createdAt\n updatedAt\n languageCode\n name\n sku\n enabled\n price\n priceWithTax\n currencyCode\n taxCategory {\n id\n name\n }\n trackInventory\n outOfStockThreshold\n useGlobalOutOfStockThreshold\n stockLevels {\n stockLocationId\n stockOnHand\n stockAllocated\n }\n options {\n id\n code\n name\n groupId\n }\n featuredAsset { \n id\n name\n preview\n source\n }\n assets { \n id\n name\n preview\n source\n }\n facetValues { \n id\n code\n name\n facet {\n id\n code\n name\n }\n }\n translations {\n id\n languageCode\n name\n }\n customFields {\n taxClassKey\n availabilityState\n availableFrom\n preorderLimit\n parcelCapacity\n parcelPacking\n }\n }\n }\n";
474
+ export declare const VARIANT_FIELDS = "\n id\n productId\n createdAt\n updatedAt\n languageCode\n name\n sku\n enabled\n storePrice {\n price\n priceWithTax\n currencyCode\n }\n prices {\n currencyCode\n price\n }\n stockOnHand\n taxCategory {\n id\n name\n }\n trackInventory\n outOfStockThreshold\n useGlobalOutOfStockThreshold\n stockLevels {\n stockLocationId\n stockOnHand\n stockAllocated\n }\n options {\n id\n code\n name\n groupId\n }\n featuredAsset { \n id\n name\n preview\n source\n type\n mimeType\n customFields {\n altText\n }\n }\n assets { \n id\n name\n preview\n source\n type\n mimeType\n customFields {\n altText\n }\n }\n facetValues { \n id\n code\n name\n facet {\n id\n code\n name\n }\n }\n translations {\n id\n languageCode\n name\n }\n customFields {\n taxClassKey\n availabilityState\n availableFrom\n preorderLimit\n parcelCapacity\n parcelPacking\n minOrderQuantity\n salesUnit\n position\n cost\n packageWeightGrams\n packageLengthCm\n packageWidthCm\n packageHeightCm\n backorderPolicy\n backorderLimit\n gtin\n mpn\n }\n";
475
+ export declare const VARIANTS_QUERY = "\n query CatalogVariants($productId: ID!, $options: ProductVariantListOptions) {\n productVariants(productId: $productId, options: $options) {\n items { \n id\n productId\n createdAt\n updatedAt\n languageCode\n name\n sku\n enabled\n storePrice {\n price\n priceWithTax\n currencyCode\n }\n prices {\n currencyCode\n price\n }\n stockOnHand\n taxCategory {\n id\n name\n }\n trackInventory\n outOfStockThreshold\n useGlobalOutOfStockThreshold\n stockLevels {\n stockLocationId\n stockOnHand\n stockAllocated\n }\n options {\n id\n code\n name\n groupId\n }\n featuredAsset { \n id\n name\n preview\n source\n type\n mimeType\n customFields {\n altText\n }\n }\n assets { \n id\n name\n preview\n source\n type\n mimeType\n customFields {\n altText\n }\n }\n facetValues { \n id\n code\n name\n facet {\n id\n code\n name\n }\n }\n translations {\n id\n languageCode\n name\n }\n customFields {\n taxClassKey\n availabilityState\n availableFrom\n preorderLimit\n parcelCapacity\n parcelPacking\n minOrderQuantity\n salesUnit\n position\n cost\n packageWeightGrams\n packageLengthCm\n packageWidthCm\n packageHeightCm\n backorderPolicy\n backorderLimit\n gtin\n mpn\n }\n }\n totalItems\n }\n }\n";
476
+ /** Two fields a row, for {@link VariantsModule.optionSets}: the id and its options' ids. */
477
+ export declare const VARIANT_OPTION_SETS_QUERY = "\n query CatalogVariantOptionSets($productId: ID!, $options: ProductVariantListOptions) {\n productVariants(productId: $productId, options: $options) {\n items {\n id\n options {\n id\n }\n }\n totalItems\n }\n }\n";
478
+ export declare const VARIANT_QUERY = "\n query CatalogVariant($id: ID!) {\n productVariant(id: $id) { \n id\n productId\n createdAt\n updatedAt\n languageCode\n name\n sku\n enabled\n storePrice {\n price\n priceWithTax\n currencyCode\n }\n prices {\n currencyCode\n price\n }\n stockOnHand\n taxCategory {\n id\n name\n }\n trackInventory\n outOfStockThreshold\n useGlobalOutOfStockThreshold\n stockLevels {\n stockLocationId\n stockOnHand\n stockAllocated\n }\n options {\n id\n code\n name\n groupId\n }\n featuredAsset { \n id\n name\n preview\n source\n type\n mimeType\n customFields {\n altText\n }\n }\n assets { \n id\n name\n preview\n source\n type\n mimeType\n customFields {\n altText\n }\n }\n facetValues { \n id\n code\n name\n facet {\n id\n code\n name\n }\n }\n translations {\n id\n languageCode\n name\n }\n customFields {\n taxClassKey\n availabilityState\n availableFrom\n preorderLimit\n parcelCapacity\n parcelPacking\n minOrderQuantity\n salesUnit\n position\n cost\n packageWeightGrams\n packageLengthCm\n packageWidthCm\n packageHeightCm\n backorderPolicy\n backorderLimit\n gtin\n mpn\n }\n }\n }\n";
479
+ export declare const CREATE_VARIANTS_MUTATION = "\n mutation CatalogCreateVariants($input: [CreateProductVariantInput!]!) {\n createProductVariants(input: $input) { \n id\n productId\n createdAt\n updatedAt\n languageCode\n name\n sku\n enabled\n storePrice {\n price\n priceWithTax\n currencyCode\n }\n prices {\n currencyCode\n price\n }\n stockOnHand\n taxCategory {\n id\n name\n }\n trackInventory\n outOfStockThreshold\n useGlobalOutOfStockThreshold\n stockLevels {\n stockLocationId\n stockOnHand\n stockAllocated\n }\n options {\n id\n code\n name\n groupId\n }\n featuredAsset { \n id\n name\n preview\n source\n type\n mimeType\n customFields {\n altText\n }\n }\n assets { \n id\n name\n preview\n source\n type\n mimeType\n customFields {\n altText\n }\n }\n facetValues { \n id\n code\n name\n facet {\n id\n code\n name\n }\n }\n translations {\n id\n languageCode\n name\n }\n customFields {\n taxClassKey\n availabilityState\n availableFrom\n preorderLimit\n parcelCapacity\n parcelPacking\n minOrderQuantity\n salesUnit\n position\n cost\n packageWeightGrams\n packageLengthCm\n packageWidthCm\n packageHeightCm\n backorderPolicy\n backorderLimit\n gtin\n mpn\n }\n }\n }\n";
480
+ export declare const UPDATE_VARIANTS_MUTATION = "\n mutation CatalogUpdateVariants($input: [UpdateProductVariantInput!]!) {\n updateProductVariants(input: $input) { \n id\n productId\n createdAt\n updatedAt\n languageCode\n name\n sku\n enabled\n storePrice {\n price\n priceWithTax\n currencyCode\n }\n prices {\n currencyCode\n price\n }\n stockOnHand\n taxCategory {\n id\n name\n }\n trackInventory\n outOfStockThreshold\n useGlobalOutOfStockThreshold\n stockLevels {\n stockLocationId\n stockOnHand\n stockAllocated\n }\n options {\n id\n code\n name\n groupId\n }\n featuredAsset { \n id\n name\n preview\n source\n type\n mimeType\n customFields {\n altText\n }\n }\n assets { \n id\n name\n preview\n source\n type\n mimeType\n customFields {\n altText\n }\n }\n facetValues { \n id\n code\n name\n facet {\n id\n code\n name\n }\n }\n translations {\n id\n languageCode\n name\n }\n customFields {\n taxClassKey\n availabilityState\n availableFrom\n preorderLimit\n parcelCapacity\n parcelPacking\n minOrderQuantity\n salesUnit\n position\n cost\n packageWeightGrams\n packageLengthCm\n packageWidthCm\n packageHeightCm\n backorderPolicy\n backorderLimit\n gtin\n mpn\n }\n }\n }\n";
294
481
  export declare const DELETE_VARIANTS_MUTATION = "\n mutation CatalogDeleteVariants($ids: [ID!]!) {\n deleteProductVariants(ids: $ids) { \n result\n message\n }\n }\n";
295
482
  /**
296
483
  * A document of its own, never a field of `VARIANT_FIELDS`: every other variant read keeps
297
484
  * working against an engine older than the field, and pays nothing for it.
298
485
  */
299
486
  export declare const VARIANT_PRICE_SOURCES_QUERY = "\n query CatalogVariantPriceSources($id: ID!) {\n productVariant(id: $id) {\n id\n priceSources {\n currencyCode\n price\n source\n organizationPrice\n organizationName\n }\n }\n }\n";
300
- export declare const REVERT_TO_ORGANIZATION_PRICE_MUTATION = "\n mutation CatalogRevertToOrganizationPrice($variantIds: [ID!]!, $currencyCodes: [CurrencyCode!]) {\n revertToOrganizationPrice(variantIds: $variantIds, currencyCodes: $currencyCodes) { \n id\n productId\n createdAt\n updatedAt\n languageCode\n name\n sku\n enabled\n price\n priceWithTax\n currencyCode\n taxCategory {\n id\n name\n }\n trackInventory\n outOfStockThreshold\n useGlobalOutOfStockThreshold\n stockLevels {\n stockLocationId\n stockOnHand\n stockAllocated\n }\n options {\n id\n code\n name\n groupId\n }\n featuredAsset { \n id\n name\n preview\n source\n }\n assets { \n id\n name\n preview\n source\n }\n facetValues { \n id\n code\n name\n facet {\n id\n code\n name\n }\n }\n translations {\n id\n languageCode\n name\n }\n customFields {\n taxClassKey\n availabilityState\n availableFrom\n preorderLimit\n parcelCapacity\n parcelPacking\n }\n }\n }\n";
487
+ export declare const REVERT_TO_ORGANIZATION_PRICE_MUTATION = "\n mutation CatalogRevertToOrganizationPrice($variantIds: [ID!]!, $currencyCodes: [CurrencyCode!]) {\n revertToOrganizationPrice(variantIds: $variantIds, currencyCodes: $currencyCodes) { \n id\n productId\n createdAt\n updatedAt\n languageCode\n name\n sku\n enabled\n storePrice {\n price\n priceWithTax\n currencyCode\n }\n prices {\n currencyCode\n price\n }\n stockOnHand\n taxCategory {\n id\n name\n }\n trackInventory\n outOfStockThreshold\n useGlobalOutOfStockThreshold\n stockLevels {\n stockLocationId\n stockOnHand\n stockAllocated\n }\n options {\n id\n code\n name\n groupId\n }\n featuredAsset { \n id\n name\n preview\n source\n type\n mimeType\n customFields {\n altText\n }\n }\n assets { \n id\n name\n preview\n source\n type\n mimeType\n customFields {\n altText\n }\n }\n facetValues { \n id\n code\n name\n facet {\n id\n code\n name\n }\n }\n translations {\n id\n languageCode\n name\n }\n customFields {\n taxClassKey\n availabilityState\n availableFrom\n preorderLimit\n parcelCapacity\n parcelPacking\n minOrderQuantity\n salesUnit\n position\n cost\n packageWeightGrams\n packageLengthCm\n packageWidthCm\n packageHeightCm\n backorderPolicy\n backorderLimit\n gtin\n mpn\n }\n }\n }\n";
301
488
  /** The price basis the store stores, read on the SAME tenant as the write. */
302
489
  export declare const PRICE_BASIS_QUERY = "\n query CatalogPriceBasis {\n activeChannel {\n pricesIncludeTax\n }\n }\n";
303
- interface WireVariant extends Omit<ProductVariant, keyof Availability<string> | 'taxClassKey' | 'parcelCapacity' | 'parcelPacking'> {
304
- customFields: (Partial<Availability<VariantAvailabilityState>> & {
490
+ /** The variant fields read out of `customFields` as they are, null for an unset one. */
491
+ export declare const LIFTED_CUSTOM_FIELDS: readonly ["minOrderQuantity", "salesUnit", "position", "cost", "packageWeightGrams", "packageLengthCm", "packageWidthCm", "packageHeightCm", "backorderPolicy", "backorderLimit", "gtin", "mpn"];
492
+ type LiftedCustomField = (typeof LIFTED_CUSTOM_FIELDS)[number];
493
+ interface WireVariant extends Omit<ProductVariant, keyof Availability<string> | 'taxClassKey' | 'parcelCapacity' | 'parcelPacking' | LiftedCustomField | 'featuredAsset' | 'assets'> {
494
+ featuredAsset: WireAssetRef | null;
495
+ assets: WireAssetRef[];
496
+ customFields: (Partial<Availability<VariantAvailabilityState>> & Partial<Pick<ProductVariant, LiftedCustomField>> & {
305
497
  taxClassKey?: string | null;
306
498
  parcelCapacity?: number | null;
307
499
  parcelPacking?: string | null;
308
500
  }) | null;
309
501
  }
502
+ /** Lift the custom fields to first-class fields. */
310
503
  export declare function toVariant(wire: WireVariant): ProductVariant;
504
+ /**
505
+ * The engine's `ProductVariantListOptions` for {@link VariantListOptions}, without the page
506
+ * window. ONE sort key, never a tie-breaker beside it: graphql-js coerces an input object in the
507
+ * order its TYPE declares the fields, and `ProductVariantSortParameter` declares `id` before every
508
+ * field named here, so `{ position, id }` reaches the engine as `{ id, position }` and sorts by id
509
+ * alone (W10-113 finding). The engine completes the order itself: `productVariants(productId:)`
510
+ * (core's `getVariantsByProductId`) appends `id ASC` after the caller's key, which makes it total,
511
+ * so offset pages neither repeat nor skip a row even where `position` is null.
512
+ */
513
+ export declare function variantListOptions(options: VariantListOptions | undefined): {
514
+ sort: Record<string, 'ASC' | 'DESC'>;
515
+ filter?: Record<string, unknown>;
516
+ filterOperator?: 'OR';
517
+ };
311
518
  /** A write's prices and the basis it states them on. */
312
519
  export interface PricedInput {
313
520
  price?: number;