@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
@@ -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;
@@ -20,16 +21,43 @@ export type InventoryFlag = 'TRUE' | 'FALSE' | 'INHERIT';
20
21
  */
21
22
  export type ParcelPacking = 'shared' | 'separate' | 'none';
22
23
  /**
23
- * A product variant — the thing that has a SKU, a price and stock.
24
+ * A variant's backorder rule: `inherit` follows the store's, `deny` sells nothing below zero,
25
+ * `incoming` sells up to the stock on its way, `limited` up to `backorderLimit` below zero, and
26
+ * `unlimited` without a bound.
27
+ */
28
+ export type VariantBackorderPolicy = 'inherit' | 'deny' | 'incoming' | 'limited' | 'unlimited';
29
+ /** One currency's price of a variant: integer minor units, on the write's `priceBasis`. */
30
+ export interface VariantPriceInput {
31
+ currencyCode: string;
32
+ price: number;
33
+ }
34
+ /**
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`.
24
37
  *
25
- * Money is integer minor units of `currencyCode`. On READ, `price` is ALWAYS
26
- * net and `priceWithTax` ALWAYS gross, whatever the store's basis — core
27
- * derives whichever side it does not store, at the rate of the store's default
28
- * tax zone. On WRITE, `price` is in the STORE's basis (`priceBasis`). So a
29
- * read-modify-write on a gross store (`pricesIncludeTax: true`) must start
30
- * from `priceWithTax`: writing back the `price` it read would drop the shelf
31
- * 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
+ */
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}.
32
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. */
33
61
  export interface ProductVariant extends Availability<VariantAvailabilityState> {
34
62
  id: string;
35
63
  productId: string;
@@ -39,11 +67,23 @@ export interface ProductVariant extends Availability<VariantAvailabilityState> {
39
67
  name: string;
40
68
  sku: string;
41
69
  enabled: boolean;
42
- /** Net, always — never the figure to write back on a gross store. */
43
- price: number;
44
- /** Gross, always, at the store's default tax zone. */
45
- priceWithTax: number;
46
- 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;
47
87
  taxCategory: {
48
88
  id: string;
49
89
  name: string;
@@ -69,6 +109,36 @@ export interface ProductVariant extends Availability<VariantAvailabilityState> {
69
109
  parcelCapacity: number | null;
70
110
  /** Never null on read: an unset packing is `shared`, the engine's default. */
71
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;
72
142
  stockLevels: StockLevel[];
73
143
  options: Array<{
74
144
  id: string;
@@ -128,6 +198,30 @@ interface VariantFields extends AvailabilityInput<VariantAvailabilityState> {
128
198
  * (`INVALID_PARCEL_PACKING`).
129
199
  */
130
200
  parcelPacking?: ParcelPacking | null;
201
+ /** The fewest units one order line may carry; null for no minimum. */
202
+ minOrderQuantity?: number | null;
203
+ /** The units a line's quantity moves in (6: 6, 12, 18 …); null for one. */
204
+ salesUnit?: number | null;
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
+ */
209
+ cost?: number | null;
210
+ packageWeightGrams?: number | null;
211
+ packageLengthCm?: number | null;
212
+ packageWidthCm?: number | null;
213
+ packageHeightCm?: number | null;
214
+ /** Null: `inherit`. */
215
+ backorderPolicy?: VariantBackorderPolicy | null;
216
+ /** The units a `limited` variant may sell below zero. */
217
+ backorderLimit?: number | null;
218
+ /**
219
+ * GS1 barcode: 8, 12, 13 or 14 digits when new or changed (`FEED_IDENTIFIER_INVALID`
220
+ * otherwise); null or blank clears it. A stored value sent back unchanged is never judged.
221
+ */
222
+ gtin?: string | null;
223
+ /** Manufacturer part number; null or blank clears it. */
224
+ mpn?: string | null;
131
225
  stockLevels?: StockLevelInput[];
132
226
  trackInventory?: InventoryFlag;
133
227
  outOfStockThreshold?: number;
@@ -153,7 +247,7 @@ export interface VariantPriceSource {
153
247
  /**
154
248
  * What the store charges in this currency, in minor units, on the STORE's basis: gross on a
155
249
  * store with `pricesIncludeTax`, net otherwise — the basis a variant write takes, unlike
156
- * {@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
157
251
  * stored price and no EUR price or exchange rate to convert from, or a currency the store
158
252
  * marked not for sale.
159
253
  */
@@ -168,6 +262,41 @@ export interface VariantPriceSource {
168
262
  /** The organization that shares the variant with the store; null for the store's own variant. */
169
263
  organizationName: string | null;
170
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
+ }
171
300
  /** The engine's bound on one `revertPrice` call (`MAX_REVERT_VARIANTS`). */
172
301
  export declare const MAX_REVERT_PRICE_VARIANTS = 500;
173
302
  export interface CreateProductVariantInput extends VariantFields {
@@ -179,6 +308,12 @@ export interface CreateProductVariantInput extends VariantFields {
179
308
  export interface UpdateProductVariantInput extends VariantFields {
180
309
  id: string;
181
310
  sku?: string;
311
+ /**
312
+ * The price in each named currency, integer minor units on `priceBasis` (required beside it,
313
+ * as for `price`). A currency left out keeps its price. Update only: the engine's create takes
314
+ * `price` alone and would drop this list without a word.
315
+ */
316
+ prices?: VariantPriceInput[];
182
317
  }
183
318
  /**
184
319
  * Variant writes. Reading needs `ReadCatalog`, creating `CreateCatalog`,
@@ -202,11 +337,99 @@ export interface UpdateProductVariantInput extends VariantFields {
202
337
  * refuses".
203
338
  */
204
339
  export interface VariantsModule {
205
- 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 */
206
353
  get(id: string, options?: Call): Promise<ProductVariant | null>;
207
- /** 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
+ */
208
417
  create(inputs: CreateProductVariantInput[], options?: Call): Promise<ProductVariant[]>;
418
+ /** @effect write */
209
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
+ */
210
433
  delete(ids: string[], options?: Call): Promise<void>;
211
434
  /**
212
435
  * Set stock on hand per stock location — absolute values, one update. A
@@ -248,27 +471,61 @@ export interface VariantsModule {
248
471
  currencyCodes?: readonly string[];
249
472
  }): Promise<ProductVariant[]>;
250
473
  }
251
- 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";
252
- 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";
253
- 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";
254
- 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";
255
- 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";
256
481
  export declare const DELETE_VARIANTS_MUTATION = "\n mutation CatalogDeleteVariants($ids: [ID!]!) {\n deleteProductVariants(ids: $ids) { \n result\n message\n }\n }\n";
257
482
  /**
258
483
  * A document of its own, never a field of `VARIANT_FIELDS`: every other variant read keeps
259
484
  * working against an engine older than the field, and pays nothing for it.
260
485
  */
261
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";
262
- 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";
263
488
  /** The price basis the store stores, read on the SAME tenant as the write. */
264
489
  export declare const PRICE_BASIS_QUERY = "\n query CatalogPriceBasis {\n activeChannel {\n pricesIncludeTax\n }\n }\n";
265
- interface WireVariant extends Omit<ProductVariant, keyof Availability<string> | 'taxClassKey' | 'parcelCapacity' | 'parcelPacking'> {
266
- 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>> & {
267
497
  taxClassKey?: string | null;
268
498
  parcelCapacity?: number | null;
269
499
  parcelPacking?: string | null;
270
500
  }) | null;
271
501
  }
502
+ /** Lift the custom fields to first-class fields. */
272
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
+ };
518
+ /** A write's prices and the basis it states them on. */
519
+ export interface PricedInput {
520
+ price?: number;
521
+ prices?: readonly VariantPriceInput[];
522
+ priceBasis?: PriceBasis;
523
+ }
524
+ /**
525
+ * Every priced input (`price`, or any `prices` entry) must be whole minor units and state its
526
+ * basis, and the basis must be the store's. Read per call, with the call's own options, so a
527
+ * per-call tenant is checked against ITS store rather than the client's default one.
528
+ */
529
+ export declare function assertPricesStateBasis(transport: Transport, inputs: readonly PricedInput[], options?: Call): Promise<void>;
273
530
  export declare function createVariantsModule(transport: Transport): VariantsModule;
274
531
  export {};