@sixtyninecommerce/admin 0.8.0 → 0.10.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 (179) hide show
  1. package/README.md +2739 -17
  2. package/dist/api-tokens/index.d.ts +15 -5
  3. package/dist/api-tokens/index.js +1 -0
  4. package/dist/apps/event-subscription.d.ts +59 -0
  5. package/dist/apps/event-subscription.js +32 -0
  6. package/dist/apps/hook-secret.d.ts +22 -0
  7. package/dist/apps/hook-secret.js +36 -0
  8. package/dist/apps/index.d.ts +54 -3
  9. package/dist/apps/index.js +17 -0
  10. package/dist/audit/index.d.ts +7 -0
  11. package/dist/auth/queries.d.ts +5 -1
  12. package/dist/auth/queries.js +6 -0
  13. package/dist/bulk/index.d.ts +323 -0
  14. package/dist/bulk/index.js +162 -0
  15. package/dist/catalog/assets.d.ts +147 -0
  16. package/dist/catalog/assets.js +164 -0
  17. package/dist/catalog/collections.d.ts +109 -0
  18. package/dist/catalog/collections.js +91 -0
  19. package/dist/catalog/facets.d.ts +103 -0
  20. package/dist/catalog/facets.js +100 -0
  21. package/dist/catalog/index.d.ts +115 -0
  22. package/dist/catalog/index.js +21 -0
  23. package/dist/catalog/options.d.ts +89 -0
  24. package/dist/catalog/options.js +88 -0
  25. package/dist/catalog/products-bulk.d.ts +21 -0
  26. package/dist/catalog/products-bulk.js +58 -0
  27. package/dist/catalog/products.d.ts +650 -0
  28. package/dist/catalog/products.js +458 -0
  29. package/dist/catalog/properties.d.ts +234 -0
  30. package/dist/catalog/properties.js +158 -0
  31. package/dist/catalog/types.d.ts +174 -0
  32. package/dist/catalog/types.js +141 -0
  33. package/dist/catalog/variants.d.ts +324 -0
  34. package/dist/catalog/variants.js +288 -0
  35. package/dist/catalog/visibility.d.ts +57 -0
  36. package/dist/catalog/visibility.js +59 -0
  37. package/dist/client.d.ts +192 -3
  38. package/dist/client.js +87 -12
  39. package/dist/compliance/index.d.ts +88 -0
  40. package/dist/compliance/index.js +88 -0
  41. package/dist/customer-data-redaction.d.ts +14 -0
  42. package/dist/customer-data-redaction.js +22 -0
  43. package/dist/customers/index.d.ts +428 -17
  44. package/dist/customers/index.js +322 -15
  45. package/dist/documents/index.d.ts +513 -16
  46. package/dist/documents/index.js +229 -3
  47. package/dist/events/index.d.ts +259 -0
  48. package/dist/events/index.js +147 -0
  49. package/dist/feeds/ai.d.ts +46 -0
  50. package/dist/feeds/ai.js +43 -0
  51. package/dist/feeds/categories.d.ts +50 -0
  52. package/dist/feeds/categories.js +56 -0
  53. package/dist/feeds/configs.d.ts +54 -0
  54. package/dist/feeds/configs.js +85 -0
  55. package/dist/feeds/identifier-fields.d.ts +25 -0
  56. package/dist/feeds/identifier-fields.js +29 -0
  57. package/dist/feeds/index.d.ts +54 -0
  58. package/dist/feeds/index.js +46 -0
  59. package/dist/feeds/preserve.d.ts +38 -0
  60. package/dist/feeds/preserve.js +74 -0
  61. package/dist/feeds/rules.d.ts +42 -0
  62. package/dist/feeds/rules.js +56 -0
  63. package/dist/feeds/types.d.ts +654 -0
  64. package/dist/feeds/types.js +135 -0
  65. package/dist/fulfillments/index.d.ts +437 -0
  66. package/dist/fulfillments/index.js +534 -0
  67. package/dist/heads/index.d.ts +60 -0
  68. package/dist/heads/index.js +45 -0
  69. package/dist/hooks/assignments.d.ts +90 -0
  70. package/dist/hooks/assignments.js +53 -0
  71. package/dist/hooks/index.d.ts +97 -0
  72. package/dist/hooks/index.js +76 -0
  73. package/dist/index.d.ts +95 -9
  74. package/dist/index.js +21 -1
  75. package/dist/integrations/index.d.ts +14 -0
  76. package/dist/integrations/index.js +9 -0
  77. package/dist/integrations/pipelines.d.ts +113 -0
  78. package/dist/integrations/pipelines.js +62 -0
  79. package/dist/integrations/types.d.ts +176 -0
  80. package/dist/integrations/types.js +8 -0
  81. package/dist/jobs/bulk-action-run.d.ts +74 -0
  82. package/dist/jobs/bulk-action-run.js +69 -0
  83. package/dist/locations/index.d.ts +6 -2
  84. package/dist/meta/index.d.ts +32 -0
  85. package/dist/orders/bulk.d.ts +75 -0
  86. package/dist/orders/bulk.js +150 -0
  87. package/dist/orders/commit-edit.d.ts +123 -0
  88. package/dist/orders/commit-edit.js +142 -0
  89. package/dist/orders/deposit-policy.d.ts +103 -0
  90. package/dist/orders/deposit-policy.js +202 -0
  91. package/dist/orders/get.d.ts +722 -0
  92. package/dist/orders/get.js +530 -0
  93. package/dist/orders/index.d.ts +186 -21
  94. package/dist/orders/index.js +57 -0
  95. package/dist/orders/line-price-preview.d.ts +297 -0
  96. package/dist/orders/line-price-preview.js +50 -0
  97. package/dist/orders/manual-discounts.d.ts +141 -0
  98. package/dist/orders/manual-discounts.js +97 -0
  99. package/dist/orders/manual-pricing.d.ts +192 -0
  100. package/dist/orders/manual-pricing.js +72 -0
  101. package/dist/orders/order-entry.d.ts +566 -0
  102. package/dist/orders/order-entry.js +129 -0
  103. package/dist/orders/parcel-charge.d.ts +45 -0
  104. package/dist/orders/parcel-charge.js +55 -0
  105. package/dist/orders/plan-edit.d.ts +489 -0
  106. package/dist/orders/plan-edit.js +93 -0
  107. package/dist/orders/status.d.ts +235 -0
  108. package/dist/orders/status.js +96 -0
  109. package/dist/orders/surcharges.d.ts +75 -2
  110. package/dist/orders/surcharges.js +110 -4
  111. package/dist/payments/account-credit.d.ts +171 -0
  112. package/dist/payments/account-credit.js +107 -0
  113. package/dist/payments/index.d.ts +241 -9
  114. package/dist/payments/index.js +92 -0
  115. package/dist/payments/methods.d.ts +37 -0
  116. package/dist/payments/methods.js +33 -0
  117. package/dist/platform/catalog.d.ts +148 -0
  118. package/dist/platform/catalog.js +74 -0
  119. package/dist/platform/entitlements.d.ts +111 -0
  120. package/dist/platform/entitlements.js +86 -0
  121. package/dist/platform/legal-entities.d.ts +139 -0
  122. package/dist/platform/legal-entities.js +126 -0
  123. package/dist/platform/legal-entity-numbering.d.ts +103 -0
  124. package/dist/platform/legal-entity-numbering.js +101 -0
  125. package/dist/platform/organization-membership.d.ts +80 -0
  126. package/dist/platform/organization-membership.js +71 -0
  127. package/dist/platform/organization-settings.d.ts +33 -0
  128. package/dist/platform/organization-settings.js +42 -0
  129. package/dist/platform/organization-sharing.d.ts +172 -0
  130. package/dist/platform/organization-sharing.js +120 -0
  131. package/dist/platform/organizations.d.ts +205 -0
  132. package/dist/platform/organizations.js +180 -0
  133. package/dist/platform/provision-store.d.ts +88 -8
  134. package/dist/platform/provision-store.js +12 -0
  135. package/dist/platform/reports.d.ts +46 -0
  136. package/dist/platform/reports.js +31 -0
  137. package/dist/platform/server-metrics.d.ts +58 -0
  138. package/dist/platform/server-metrics.js +11 -0
  139. package/dist/pos/index.d.ts +567 -0
  140. package/dist/pos/index.js +205 -0
  141. package/dist/pricing/index.d.ts +408 -0
  142. package/dist/pricing/index.js +353 -0
  143. package/dist/pricing/vouchers.d.ts +103 -0
  144. package/dist/pricing/vouchers.js +69 -0
  145. package/dist/provenance/index.d.ts +93 -0
  146. package/dist/provenance/index.js +53 -0
  147. package/dist/reports/index.d.ts +199 -0
  148. package/dist/reports/index.js +154 -0
  149. package/dist/returns/index.d.ts +225 -0
  150. package/dist/returns/index.js +160 -0
  151. package/dist/search/index.d.ts +99 -0
  152. package/dist/search/index.js +62 -0
  153. package/dist/shipping/parcel-count-suggestion.d.ts +52 -0
  154. package/dist/shipping/parcel-count-suggestion.js +73 -0
  155. package/dist/shipping/parcel-rule.d.ts +48 -0
  156. package/dist/shipping/parcel-rule.js +91 -0
  157. package/dist/stock/index.d.ts +166 -0
  158. package/dist/stock/index.js +115 -0
  159. package/dist/stock/transfers.d.ts +520 -0
  160. package/dist/stock/transfers.js +437 -0
  161. package/dist/stored-value/index.d.ts +154 -0
  162. package/dist/stored-value/index.js +130 -0
  163. package/dist/tenant/capabilities.d.ts +10 -0
  164. package/dist/tenant/entitlements.d.ts +35 -0
  165. package/dist/tenant/entitlements.js +15 -0
  166. package/dist/tenant/field-groups.d.ts +64 -0
  167. package/dist/tenant/field-groups.js +34 -0
  168. package/dist/tenant/legal-entity.d.ts +32 -0
  169. package/dist/tenant/legal-entity.js +41 -0
  170. package/dist/tenant/notifications.d.ts +148 -0
  171. package/dist/tenant/notifications.js +79 -0
  172. package/dist/tenant/retention.d.ts +10 -4
  173. package/dist/tenant/settings-sources.d.ts +103 -0
  174. package/dist/tenant/settings-sources.js +92 -0
  175. package/dist/types.d.ts +86 -11
  176. package/dist/uploads/index.d.ts +11 -7
  177. package/dist/users/index.d.ts +104 -0
  178. package/dist/users/index.js +80 -0
  179. package/package.json +3 -2
@@ -0,0 +1,141 @@
1
+ import { ConflictError, errorFromErrorResult, isErrorResult, pageFromVendureList, readOffsetCursor, UpstreamError, ValidationError, } from '@sixtyninecommerce/core';
2
+ /** `catalogueOwnership` on the top-level row of each catalogue read (never on nested refs). */
3
+ export const CATALOGUE_OWNERSHIP_FIELDS = /* GraphQL */ `
4
+ catalogueOwnership {
5
+ state
6
+ organizationName
7
+ editableHere
8
+ }
9
+ `;
10
+ export const ASSET_REF_FIELDS = /* GraphQL */ `
11
+ id
12
+ name
13
+ preview
14
+ source
15
+ `;
16
+ export const FACET_VALUE_REF_FIELDS = /* GraphQL */ `
17
+ id
18
+ code
19
+ name
20
+ facet {
21
+ id
22
+ code
23
+ name
24
+ }
25
+ `;
26
+ const INSTANT = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(?::\d{2}(?:\.\d{1,9})?)?(?:Z|[+-]\d{2}:\d{2})$/;
27
+ /**
28
+ * Refuse a datetime with no offset, or one that is not a date at all (spec
29
+ * §9.3). Returns the value unchanged — the engine stores the instant, and
30
+ * re-serialising it here would only risk changing its precision.
31
+ */
32
+ export function assertInstant(field, value) {
33
+ if (value === null || value === undefined)
34
+ return;
35
+ if (typeof value === 'string' && INSTANT.test(value) && !Number.isNaN(Date.parse(value)))
36
+ return;
37
+ throw new ValidationError({
38
+ code: 'INVALID_DATETIME',
39
+ message: `${field} must be an ISO-8601 instant with an explicit offset, such as "2026-10-01T09:00:00+02:00" or "2026-10-01T07:00:00Z"; got ${JSON.stringify(value)}.`,
40
+ status: 400,
41
+ service: 'vendure',
42
+ details: [{ field, code: 'INVALID_DATETIME', message: 'An offset (Z or ±HH:mm) is required.' }],
43
+ });
44
+ }
45
+ /**
46
+ * Split an availability input into the custom fields the Admin API takes. Only
47
+ * PRESENT keys are copied — an omitted field must reach the server as omitted,
48
+ * never as null, or a partial update would clear it.
49
+ */
50
+ export function availabilityCustomFields(input) {
51
+ assertInstant('availableFrom', input.availableFrom);
52
+ const fields = {};
53
+ if (input.availabilityState !== undefined)
54
+ fields.availabilityState = input.availabilityState;
55
+ if (input.availableFrom !== undefined)
56
+ fields.availableFrom = input.availableFrom;
57
+ if (input.preorderLimit !== undefined)
58
+ fields.preorderLimit = input.preorderLimit;
59
+ return fields;
60
+ }
61
+ export const DELETION_FIELDS = /* GraphQL */ `
62
+ result
63
+ message
64
+ `;
65
+ /**
66
+ * `NOT_DELETED` arrives at HTTP 200 with no error, so a caller awaiting a void
67
+ * promise would read a refusal as success. It becomes a `ConflictError`
68
+ * carrying the server's own sentence ("The FacetValue … is assigned to 3
69
+ * Products").
70
+ */
71
+ export function assertDeleted(code, responses) {
72
+ const refused = responses.filter((response) => response.result !== 'DELETED');
73
+ if (refused.length === 0)
74
+ return;
75
+ throw new ConflictError({
76
+ code,
77
+ message: refused.map((response) => response.message ?? 'The server gave no reason.').join('; '),
78
+ status: 409,
79
+ service: 'vendure',
80
+ });
81
+ }
82
+ /** Throw a Vendure ErrorResult (`MimeTypeError`, `ProductOptionInUseError`) as itself. */
83
+ export function unwrapResult(value) {
84
+ if (isErrorResult(value))
85
+ throw errorFromErrorResult(value);
86
+ return value;
87
+ }
88
+ export const DEFAULT_PAGE_SIZE = 25;
89
+ /** Vendure's `adminListQueryLimit` default; a larger `take` is refused by the engine. */
90
+ export const MAX_PAGE_SIZE = 1000;
91
+ /**
92
+ * Wrap one of Vendure's `skip/take` lists in a `Page<T>`. The offset hides
93
+ * inside the cursor (spec §9.3), and every following page re-sends the same
94
+ * filter, sort and per-call options — a page that forgot its tenant would walk
95
+ * another store's catalogue.
96
+ */
97
+ export async function vendureListPage(transport, document, field, variables, options, map = (wire) => wire) {
98
+ const take = options?.limit ?? DEFAULT_PAGE_SIZE;
99
+ if (!Number.isInteger(take) || take < 1 || take > MAX_PAGE_SIZE) {
100
+ throw new ValidationError({
101
+ code: 'INVALID_PAGE_SIZE',
102
+ message: `limit must be a whole number from 1 to ${String(MAX_PAGE_SIZE)}; got ${String(take)}.`,
103
+ status: 400,
104
+ service: 'vendure',
105
+ });
106
+ }
107
+ const fetchPage = async (skip) => {
108
+ const data = await transport.graphql({ api: 'admin', query: document, variables: variables(skip, take) }, options);
109
+ const list = data[field];
110
+ if (!list) {
111
+ throw new UpstreamError({
112
+ code: 'UNEXPECTED_RESPONSE',
113
+ message: `The engine answered without "${field}".`,
114
+ status: 502,
115
+ service: 'vendure',
116
+ });
117
+ }
118
+ return pageFromVendureList({ items: list.items.map(map), totalItems: list.totalItems }, skip, take, (cursor) => fetchPage(readOffsetCursor(cursor)));
119
+ };
120
+ return fetchPage(options?.after === undefined ? 0 : readOffsetCursor(options.after));
121
+ }
122
+ /**
123
+ * Run one Admin API document and answer its single root field — the shape of
124
+ * almost every catalog call. The field is read by NAME from the response, so a
125
+ * document and its field name must be kept in step: an ABSENT field is refused
126
+ * rather than answered as `undefined` (a null one is a real answer), and
127
+ * `catalog-call-sites.spec.ts` drives every call site with a responder keyed
128
+ * on the document's own root field.
129
+ */
130
+ export async function fieldOf(transport, document, field, variables, options) {
131
+ const data = await transport.graphql({ api: 'admin', query: document, variables }, options);
132
+ if (data === null || typeof data !== 'object' || !Object.hasOwn(data, field)) {
133
+ throw new UpstreamError({
134
+ code: 'UNEXPECTED_RESPONSE',
135
+ message: `The engine answered without "${field}".`,
136
+ status: 502,
137
+ service: 'vendure',
138
+ });
139
+ }
140
+ return data[field];
141
+ }
@@ -0,0 +1,324 @@
1
+ import { type Page, type Transport } from '@sixtyninecommerce/core';
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';
4
+ export interface StockLevel {
5
+ stockLocationId: string;
6
+ stockOnHand: number;
7
+ stockAllocated: number;
8
+ }
9
+ export interface StockLevelInput {
10
+ stockLocationId: string;
11
+ /** Absolute, not a delta. Refused below zero by the engine ("stockOnHand cannot be a negative value"). */
12
+ stockOnHand: number;
13
+ }
14
+ export type InventoryFlag = 'TRUE' | 'FALSE' | 'INHERIT';
15
+ /**
16
+ * How a variant takes parcel space (ADR 2026-09-17 parcel-count rule):
17
+ * `shared` packs with the rest of the order up to the items per parcel,
18
+ * `separate` never shares a parcel with other items (one unit per parcel unless
19
+ * `parcelCapacity` is set), `none` never adds one.
20
+ */
21
+ export type ParcelPacking = 'shared' | 'separate' | 'none';
22
+ /**
23
+ * A variant's backorder rule: `inherit` follows the store's, `deny` sells nothing below zero,
24
+ * `incoming` sells up to the stock on its way, `limited` up to `backorderLimit` below zero, and
25
+ * `unlimited` without a bound.
26
+ */
27
+ export type VariantBackorderPolicy = 'inherit' | 'deny' | 'incoming' | 'limited' | 'unlimited';
28
+ /** One currency's price of a variant: integer minor units, on the write's `priceBasis`. */
29
+ export interface VariantPriceInput {
30
+ currencyCode: string;
31
+ price: number;
32
+ }
33
+ /**
34
+ * A product variant — the thing that has a SKU, a price and stock.
35
+ *
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.
43
+ */
44
+ export interface ProductVariant extends Availability<VariantAvailabilityState> {
45
+ id: string;
46
+ productId: string;
47
+ createdAt: string;
48
+ updatedAt: string;
49
+ languageCode: LanguageCode;
50
+ name: string;
51
+ sku: string;
52
+ 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;
58
+ taxCategory: {
59
+ id: string;
60
+ name: string;
61
+ };
62
+ /**
63
+ * The store's tax class this variant belongs to (spec §8.6), or null. When
64
+ * set, `taxCategory` is the category that class meant for the store's own
65
+ * country at the moment the variant was last written with it — editing the
66
+ * class's mappings later does not re-price it.
67
+ *
68
+ * A write that sets `taxCategoryId` WITHOUT naming the key (the dashboard's
69
+ * variant form does this) to a category the class does not mean DETACHES the
70
+ * class: the key is cleared in the same write, and this reads null.
71
+ */
72
+ taxClassKey: string | null;
73
+ trackInventory: InventoryFlag;
74
+ outOfStockThreshold: number;
75
+ useGlobalOutOfStockThreshold: boolean;
76
+ /**
77
+ * Units of this variant that fill one parcel. Null uses the store's items per parcel, or 1
78
+ * when `parcelPacking` is `separate`.
79
+ */
80
+ parcelCapacity: number | null;
81
+ /** Never null on read: an unset packing is `shared`, the engine's default. */
82
+ parcelPacking: ParcelPacking;
83
+ stockLevels: StockLevel[];
84
+ options: Array<{
85
+ id: string;
86
+ code: string;
87
+ name: string;
88
+ groupId: string;
89
+ }>;
90
+ featuredAsset: AssetRef | null;
91
+ assets: AssetRef[];
92
+ facetValues: FacetValueRef[];
93
+ translations: Array<{
94
+ id: string;
95
+ languageCode: LanguageCode;
96
+ name: string;
97
+ }>;
98
+ }
99
+ export interface VariantTranslationInput extends TranslationInput {
100
+ name?: string;
101
+ }
102
+ interface VariantFields extends AvailabilityInput<VariantAvailabilityState> {
103
+ enabled?: boolean;
104
+ translations?: VariantTranslationInput[];
105
+ /** One option from EACH of the product's option groups, and a combination no other variant has. */
106
+ optionIds?: string[];
107
+ /**
108
+ * Integer minor units in the store's default currency, stated on
109
+ * `priceBasis`. The two travel together: a price without its basis is
110
+ * refused (`PRICE_BASIS_REQUIRED`), and a basis other than the store's is
111
+ * refused (`PRICE_BASIS_MISMATCH`) — see {@link PriceBasis}.
112
+ */
113
+ price?: number;
114
+ priceBasis?: PriceBasis;
115
+ /**
116
+ * Set the category directly. Mutually exclusive with a `taxClassKey` naming
117
+ * a different category (`INVALID_TAX_CATEGORY_FOR_CLASS`).
118
+ */
119
+ taxCategoryId?: string;
120
+ /**
121
+ * The store's tax class (spec §8.6). The engine resolves it for the store's
122
+ * own business country and writes the resulting `taxCategoryId` in the same
123
+ * write. Refusals, in the engine's words: `TAX_CLASS_NOT_FOUND`
124
+ * (`NotFoundError`) — "This store has no tax class with the key "…"." — and
125
+ * `TAX_CLASS_UNMAPPED` (`ValidationError`) — "The tax class "…" has no tax
126
+ * category for NL and no "*" fallback. Map it before using it here."
127
+ *
128
+ * Null clears the key and leaves the category as it is.
129
+ */
130
+ taxClassKey?: string | null;
131
+ /**
132
+ * A whole number from 1 to 10,000, or null for the default (the store's items per
133
+ * parcel, or 1 when `parcelPacking` is `separate`). Anything else is refused before sending (`INVALID_PARCEL_CAPACITY`).
134
+ */
135
+ parcelCapacity?: number | null;
136
+ /**
137
+ * `shared` and null both store null — the one way "shared" is stored, so a
138
+ * read never tells them apart. Another value is refused before sending
139
+ * (`INVALID_PARCEL_PACKING`).
140
+ */
141
+ parcelPacking?: ParcelPacking | null;
142
+ /** The fewest units one order line may carry; null for no minimum. */
143
+ minOrderQuantity?: number | null;
144
+ /** The units a line's quantity moves in (6: 6, 12, 18 …); null for one. */
145
+ salesUnit?: number | null;
146
+ /** What one unit costs the store, integer minor units of its default currency; null clears it. */
147
+ cost?: number | null;
148
+ packageWeightGrams?: number | null;
149
+ packageLengthCm?: number | null;
150
+ packageWidthCm?: number | null;
151
+ packageHeightCm?: number | null;
152
+ /** Null: `inherit`. */
153
+ backorderPolicy?: VariantBackorderPolicy | null;
154
+ /** The units a `limited` variant may sell below zero. */
155
+ backorderLimit?: number | null;
156
+ /**
157
+ * GS1 barcode: 8, 12, 13 or 14 digits when new or changed (`FEED_IDENTIFIER_INVALID`
158
+ * otherwise); null or blank clears it. A stored value sent back unchanged is never judged.
159
+ */
160
+ gtin?: string | null;
161
+ /** Manufacturer part number; null or blank clears it. */
162
+ mpn?: string | null;
163
+ stockLevels?: StockLevelInput[];
164
+ trackInventory?: InventoryFlag;
165
+ outOfStockThreshold?: number;
166
+ useGlobalOutOfStockThreshold?: boolean;
167
+ featuredAssetId?: string | null;
168
+ assetIds?: string[];
169
+ facetValueIds?: string[];
170
+ }
171
+ /**
172
+ * Where a store's price in one currency comes from (multi-region decision 59):
173
+ * - `OWN` — a price the store stored itself, or its own change of its organization's price.
174
+ * - `INHERITED` — the organization's stored price, copied to the store and unchanged since; it
175
+ * follows the organization's later changes.
176
+ * - `CONVERTED` — nothing stored: the storefront converts the store's EUR price at the store's
177
+ * exchange rate.
178
+ *
179
+ * A string on the wire: a newer engine may answer a source this SDK does not name yet.
180
+ */
181
+ export type VariantPriceSourceKind = 'OWN' | 'INHERITED' | 'CONVERTED';
182
+ /** One currency the active store sells, and where the variant's price in it comes from. */
183
+ export interface VariantPriceSource {
184
+ currencyCode: string;
185
+ /**
186
+ * What the store charges in this currency, in minor units, on the STORE's basis: gross on a
187
+ * 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
189
+ * stored price and no EUR price or exchange rate to convert from, or a currency the store
190
+ * marked not for sale.
191
+ */
192
+ price: number | null;
193
+ source: VariantPriceSourceKind | (string & {});
194
+ /**
195
+ * The organization's stored price in this currency, on the same basis (an organization and its
196
+ * stores enter prices the same way). Null when no organization shares the variant with the
197
+ * store, or the organization stores no price in this currency.
198
+ */
199
+ organizationPrice: number | null;
200
+ /** The organization that shares the variant with the store; null for the store's own variant. */
201
+ organizationName: string | null;
202
+ }
203
+ /** The engine's bound on one `revertPrice` call (`MAX_REVERT_VARIANTS`). */
204
+ export declare const MAX_REVERT_PRICE_VARIANTS = 500;
205
+ export interface CreateProductVariantInput extends VariantFields {
206
+ productId: string;
207
+ sku: string;
208
+ translations: VariantTranslationInput[];
209
+ }
210
+ /** A PATCH: omitted fields are left alone. */
211
+ export interface UpdateProductVariantInput extends VariantFields {
212
+ id: string;
213
+ sku?: string;
214
+ /**
215
+ * The price in each named currency, integer minor units on `priceBasis` (required beside it,
216
+ * as for `price`). A currency left out keeps its price. Update only: the engine's create takes
217
+ * `price` alone and would drop this list without a word.
218
+ */
219
+ prices?: VariantPriceInput[];
220
+ }
221
+ /**
222
+ * Variant writes. Reading needs `ReadCatalog`, creating `CreateCatalog`,
223
+ * updating (stock included) `UpdateCatalog`, deleting `DeleteCatalog`.
224
+ *
225
+ * Refusals, in the engine's words: `optionIds` that do not name one option per
226
+ * group — "ProductVariant optionIds must include one optionId from each of the
227
+ * groups: …" — or repeat another variant's combination — "A ProductVariant with
228
+ * the selected options already exists: …" — and a negative stock level —
229
+ * "stockOnHand cannot be a negative value" — all arrive as `ValidationError`
230
+ * (`USER_INPUT_ERROR`). An id this store does not have is a `NotFoundError`
231
+ * (`ENTITY_NOT_FOUND`). A product may have ONE variant without options; a
232
+ * second is refused the same way.
233
+ *
234
+ * While an app or API token of the store owns a field group (`admin.tenant.fieldGroupOwners()`),
235
+ * an update or delete of a variant that would change its Price, Stock or
236
+ * Product details by anyone else is refused `FIELD_GROUP_LOCKED`
237
+ * (`BusinessRuleError`, 422) and nothing is written; `error.details` names each refused field
238
+ * (`field` is a JSON pointer into the call, e.g. `/input/0/price`). A value equal to the stored
239
+ * one passes and is left out of the write. See the README, "Field groups: what an owned group
240
+ * refuses".
241
+ */
242
+ export interface VariantsModule {
243
+ list(productId: string, options?: ListOptions): Promise<Page<ProductVariant>>;
244
+ get(id: string, options?: Call): Promise<ProductVariant | null>;
245
+ /** All or nothing: the engine creates the batch in one transaction. */
246
+ create(inputs: CreateProductVariantInput[], options?: Call): Promise<ProductVariant[]>;
247
+ update(inputs: UpdateProductVariantInput[], options?: Call): Promise<ProductVariant[]>;
248
+ delete(ids: string[], options?: Call): Promise<void>;
249
+ /**
250
+ * Set stock on hand per stock location — absolute values, one update. A
251
+ * location not named keeps its level. Stock MOVEMENTS with a reason are the
252
+ * `stock` module's (spec §9.2), not this.
253
+ */
254
+ setStockLevels(variantId: string, levels: StockLevelInput[], options?: Call): Promise<ProductVariant>;
255
+ /**
256
+ * Where each of the variant's prices in the ACTIVE store comes from: one entry per currency the
257
+ * store sells, in the store's currency order. A store outside any organization, and a variant
258
+ * of the store's own, read `OWN` or `CONVERTED` only. Needs `ReadCatalog`. An id this store
259
+ * does not have is a `NotFoundError` (`VARIANT_NOT_FOUND`).
260
+ */
261
+ priceSources(variantId: string, options?: Call): Promise<VariantPriceSource[]>;
262
+ /**
263
+ * The active store's prices of variants its organization shares with it become the
264
+ * organization's stored prices again (`INHERITED`), and follow its later changes. Without
265
+ * `currencyCodes`: every currency the store sells and the organization prices, bar one the store
266
+ * marked not for sale and stores no price in (a stored price sells the currency whatever its
267
+ * mark, so it is reverted); an empty list reverts nothing. Needs `UpdateCatalog` (or
268
+ * `UpdateProduct`), what a variant price write needs. All or nothing, and audited per variant.
269
+ * Answers the variants as the store sees them. An Admin API mutation: an idempotency key
270
+ * deduplicates nothing, and a repeat answers the same prices.
271
+ *
272
+ * Refusals (nothing is written), GraphQL errors mapped by `core`:
273
+ * - `ORGANIZATION_PRICE_LOCAL_VARIANT` → `BusinessRuleError` (422): a variant of the store's
274
+ * own, or the organization does not share its catalog.
275
+ * - `ORGANIZATION_PRICE_MISSING` → `BusinessRuleError` (422): the organization stores no price
276
+ * in a currency named (or in any the store sells).
277
+ * - `ORGANIZATION_PRICE_NOT_REVERTIBLE` → `BusinessRuleError` (422): a named currency the store
278
+ * does not sell, or marked not for sale for the variant with no price stored in it.
279
+ * - `ORGANIZATION_WORKSPACE_NOT_A_STORE` → `BusinessRuleError` (422): called in an
280
+ * organization's workspace, whose prices are the organization's own.
281
+ * - `ORGANIZATION_PRICE_REVERT_INVALID` → `ValidationError` (400): more than
282
+ * {@link MAX_REVERT_PRICE_VARIANTS} distinct ids; refused before sending too.
283
+ * - `ENTITY_NOT_FOUND` → `NotFoundError` (404): an id this store does not have.
284
+ */
285
+ revertPrice(variantIds: string[], options?: Call & {
286
+ currencyCodes?: readonly string[];
287
+ }): Promise<ProductVariant[]>;
288
+ }
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";
294
+ export declare const DELETE_VARIANTS_MUTATION = "\n mutation CatalogDeleteVariants($ids: [ID!]!) {\n deleteProductVariants(ids: $ids) { \n result\n message\n }\n }\n";
295
+ /**
296
+ * A document of its own, never a field of `VARIANT_FIELDS`: every other variant read keeps
297
+ * working against an engine older than the field, and pays nothing for it.
298
+ */
299
+ 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";
301
+ /** The price basis the store stores, read on the SAME tenant as the write. */
302
+ 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>> & {
305
+ taxClassKey?: string | null;
306
+ parcelCapacity?: number | null;
307
+ parcelPacking?: string | null;
308
+ }) | null;
309
+ }
310
+ export declare function toVariant(wire: WireVariant): ProductVariant;
311
+ /** A write's prices and the basis it states them on. */
312
+ export interface PricedInput {
313
+ price?: number;
314
+ prices?: readonly VariantPriceInput[];
315
+ priceBasis?: PriceBasis;
316
+ }
317
+ /**
318
+ * Every priced input (`price`, or any `prices` entry) must be whole minor units and state its
319
+ * basis, and the basis must be the store's. Read per call, with the call's own options, so a
320
+ * per-call tenant is checked against ITS store rather than the client's default one.
321
+ */
322
+ export declare function assertPricesStateBasis(transport: Transport, inputs: readonly PricedInput[], options?: Call): Promise<void>;
323
+ export declare function createVariantsModule(transport: Transport): VariantsModule;
324
+ export {};