@sixtyninecommerce/admin 0.7.0 → 0.9.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 (161) hide show
  1. package/README.md +2445 -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 +489 -0
  28. package/dist/catalog/products.js +365 -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 +274 -0
  34. package/dist/catalog/variants.js +258 -0
  35. package/dist/catalog/visibility.d.ts +57 -0
  36. package/dist/catalog/visibility.js +59 -0
  37. package/dist/client.d.ts +171 -3
  38. package/dist/client.js +81 -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 +360 -0
  66. package/dist/fulfillments/index.js +425 -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 +84 -9
  74. package/dist/index.js +16 -1
  75. package/dist/jobs/bulk-action-run.d.ts +65 -0
  76. package/dist/jobs/bulk-action-run.js +70 -0
  77. package/dist/locations/index.d.ts +6 -2
  78. package/dist/meta/index.d.ts +32 -0
  79. package/dist/orders/commit-edit.d.ts +123 -0
  80. package/dist/orders/commit-edit.js +142 -0
  81. package/dist/orders/get.d.ts +722 -0
  82. package/dist/orders/get.js +530 -0
  83. package/dist/orders/index.d.ts +105 -20
  84. package/dist/orders/index.js +44 -0
  85. package/dist/orders/line-price-preview.d.ts +297 -0
  86. package/dist/orders/line-price-preview.js +50 -0
  87. package/dist/orders/manual-discounts.d.ts +132 -0
  88. package/dist/orders/manual-discounts.js +95 -0
  89. package/dist/orders/manual-pricing.d.ts +192 -0
  90. package/dist/orders/manual-pricing.js +72 -0
  91. package/dist/orders/order-entry.d.ts +566 -0
  92. package/dist/orders/order-entry.js +129 -0
  93. package/dist/orders/parcel-charge.d.ts +45 -0
  94. package/dist/orders/parcel-charge.js +55 -0
  95. package/dist/orders/plan-edit.d.ts +489 -0
  96. package/dist/orders/plan-edit.js +93 -0
  97. package/dist/orders/status.d.ts +235 -0
  98. package/dist/orders/status.js +96 -0
  99. package/dist/orders/surcharges.d.ts +75 -2
  100. package/dist/orders/surcharges.js +110 -4
  101. package/dist/payments/account-credit.d.ts +171 -0
  102. package/dist/payments/account-credit.js +107 -0
  103. package/dist/payments/index.d.ts +368 -2
  104. package/dist/payments/index.js +138 -0
  105. package/dist/platform/catalog.d.ts +148 -0
  106. package/dist/platform/catalog.js +74 -0
  107. package/dist/platform/entitlements.d.ts +111 -0
  108. package/dist/platform/entitlements.js +86 -0
  109. package/dist/platform/legal-entities.d.ts +139 -0
  110. package/dist/platform/legal-entities.js +126 -0
  111. package/dist/platform/legal-entity-numbering.d.ts +103 -0
  112. package/dist/platform/legal-entity-numbering.js +101 -0
  113. package/dist/platform/organization-membership.d.ts +80 -0
  114. package/dist/platform/organization-membership.js +71 -0
  115. package/dist/platform/organization-settings.d.ts +33 -0
  116. package/dist/platform/organization-settings.js +42 -0
  117. package/dist/platform/organization-sharing.d.ts +172 -0
  118. package/dist/platform/organization-sharing.js +120 -0
  119. package/dist/platform/organizations.d.ts +205 -0
  120. package/dist/platform/organizations.js +180 -0
  121. package/dist/platform/provision-store.d.ts +88 -8
  122. package/dist/platform/provision-store.js +12 -0
  123. package/dist/platform/reports.d.ts +46 -0
  124. package/dist/platform/reports.js +31 -0
  125. package/dist/platform/server-metrics.d.ts +58 -0
  126. package/dist/platform/server-metrics.js +11 -0
  127. package/dist/pos/index.d.ts +567 -0
  128. package/dist/pos/index.js +205 -0
  129. package/dist/pricing/index.d.ts +405 -0
  130. package/dist/pricing/index.js +351 -0
  131. package/dist/provenance/index.d.ts +93 -0
  132. package/dist/provenance/index.js +53 -0
  133. package/dist/reports/index.d.ts +199 -0
  134. package/dist/reports/index.js +154 -0
  135. package/dist/shipping/parcel-count-suggestion.d.ts +52 -0
  136. package/dist/shipping/parcel-count-suggestion.js +73 -0
  137. package/dist/shipping/parcel-rule.d.ts +48 -0
  138. package/dist/shipping/parcel-rule.js +91 -0
  139. package/dist/stock/index.d.ts +166 -0
  140. package/dist/stock/index.js +115 -0
  141. package/dist/stock/transfers.d.ts +520 -0
  142. package/dist/stock/transfers.js +437 -0
  143. package/dist/stored-value/index.d.ts +154 -0
  144. package/dist/stored-value/index.js +130 -0
  145. package/dist/tenant/capabilities.d.ts +10 -0
  146. package/dist/tenant/entitlements.d.ts +35 -0
  147. package/dist/tenant/entitlements.js +15 -0
  148. package/dist/tenant/field-groups.d.ts +64 -0
  149. package/dist/tenant/field-groups.js +34 -0
  150. package/dist/tenant/legal-entity.d.ts +32 -0
  151. package/dist/tenant/legal-entity.js +41 -0
  152. package/dist/tenant/notifications.d.ts +148 -0
  153. package/dist/tenant/notifications.js +79 -0
  154. package/dist/tenant/retention.d.ts +10 -4
  155. package/dist/tenant/settings-sources.d.ts +103 -0
  156. package/dist/tenant/settings-sources.js +92 -0
  157. package/dist/types.d.ts +86 -11
  158. package/dist/uploads/index.d.ts +11 -7
  159. package/dist/users/index.d.ts +104 -0
  160. package/dist/users/index.js +80 -0
  161. package/package.json +3 -2
@@ -0,0 +1,148 @@
1
+ import type { Transport } from '@sixtyninecommerce/core';
2
+ import type { AdminClientConfig } from '../client.js';
3
+ import type { Call } from '../types.js';
4
+ /** Which kind of catalogue entity a repair row is about. */
5
+ export type TranslationRepairEntityKind = 'PRODUCT' | 'VARIANT';
6
+ /**
7
+ * Why the repair left a candidate alone. A string on the wire, not an enum, so
8
+ * a reason added later never breaks a caller:
9
+ *
10
+ * - `SHARED_CHANNEL_LANGUAGES_DIFFER`: another store holds the entity and offers
11
+ * other languages, so a row foreign here may be that store's own.
12
+ * - `CARRIER_PRODUCT`: the platform's hidden external-line carrier.
13
+ * - `DEFAULT_ROW_GONE`: its default-language row disappeared before it could be
14
+ * repaired.
15
+ */
16
+ export type TranslationRepairSkipReason = 'SHARED_CHANNEL_LANGUAGES_DIFFER' | 'CARRIER_PRODUCT' | 'DEFAULT_ROW_GONE' | (string & {});
17
+ export interface RepairTranslationLanguagesInput {
18
+ /** The store to repair. The root channel and organization hubs are refused. */
19
+ channelId: string;
20
+ /**
21
+ * `true` reports what would be removed and changes nothing; `false` removes
22
+ * it. Both take the same path on the server, so a dry run is an exact preview
23
+ * of the apply that follows it (as long as the catalogue does not change in
24
+ * between).
25
+ */
26
+ dryRun: boolean;
27
+ /**
28
+ * Products and variants examined per page. The server defaults to 100 and
29
+ * treats anything above 500 as 500; below 1 is refused
30
+ * (`TRANSLATION_REPAIR_LIMIT_INVALID`).
31
+ */
32
+ limit?: number;
33
+ /**
34
+ * The `nextCursor` of the previous page of the SAME run: same store, same
35
+ * `dryRun`. Omitted or null starts from the beginning. A cursor issued for
36
+ * another store or the other mode is refused (`TRANSLATION_REPAIR_CURSOR_INVALID`),
37
+ * so a dry run's cursor can never make an apply skip the pages before it.
38
+ */
39
+ cursor?: string | null;
40
+ /**
41
+ * On the first page (no cursor) only: also list the store's single-variant
42
+ * products whose variant name differs from the product name.
43
+ */
44
+ includeNameReport?: boolean;
45
+ }
46
+ /**
47
+ * A translation row the repair removes, or in a dry run would remove. The whole
48
+ * row, so a dry run's answer is the undo record of the apply.
49
+ */
50
+ export interface TranslationRepairRemovedRow {
51
+ translationId: string;
52
+ languageCode: string;
53
+ name: string;
54
+ /** Products only; null for a variant. */
55
+ slug: string | null;
56
+ /** Products only; null for a variant. */
57
+ description: string | null;
58
+ }
59
+ /**
60
+ * A product or variant with a row in the store's default language and one or
61
+ * more rows in languages the store does not offer.
62
+ */
63
+ export interface TranslationRepairEntity {
64
+ kind: TranslationRepairEntityKind;
65
+ id: string;
66
+ /** The product itself for a `PRODUCT`, the variant's product for a `VARIANT`. */
67
+ productId: string;
68
+ /** Variants only; null for a product. */
69
+ sku: string | null;
70
+ /** The language of the row the merchant sees, which stays. */
71
+ keptLanguageCode: string;
72
+ /** The name the merchant sees, which stays. */
73
+ keptName: string;
74
+ removed: TranslationRepairRemovedRow[];
75
+ }
76
+ /** A candidate the repair leaves alone, and why. */
77
+ export interface TranslationRepairSkipped {
78
+ kind: TranslationRepairEntityKind;
79
+ id: string;
80
+ productId: string;
81
+ sku: string | null;
82
+ /** The name the merchant sees. */
83
+ name: string;
84
+ reason: TranslationRepairSkipReason;
85
+ }
86
+ /**
87
+ * A single-variant product whose variant shows another name than the product.
88
+ * Report only: the repair never renames.
89
+ */
90
+ export interface TranslationRepairNameMismatch {
91
+ productId: string;
92
+ variantId: string;
93
+ sku: string;
94
+ productName: string;
95
+ variantName: string;
96
+ }
97
+ /** One page of a repair run, exactly as the engine answers it. */
98
+ export interface TranslationRepairPage {
99
+ /** Fresh for every call; the audit rows of an apply page carry it. */
100
+ runId: string;
101
+ dryRun: boolean;
102
+ channelId: string;
103
+ channelCode: string;
104
+ defaultLanguageCode: string;
105
+ /** The default language first, then the store's other available languages. */
106
+ offeredLanguageCodes: string[];
107
+ repaired: TranslationRepairEntity[];
108
+ skipped: TranslationRepairSkipped[];
109
+ /**
110
+ * Products and variants whose rows are ALL in languages the store does not
111
+ * offer. Never touched (removing them would remove their only name). Counted
112
+ * on the first page only; 0 on later pages.
113
+ */
114
+ foreignOnlyCount: number;
115
+ /** First page with `includeNameReport` only, for the whole store; empty otherwise. */
116
+ nameMismatches: TranslationRepairNameMismatch[];
117
+ /** Pass back as `cursor` to continue this run; null when every candidate was examined. */
118
+ nextCursor: string | null;
119
+ }
120
+ /**
121
+ * `admin.platform.catalog` — SuperAdmin catalogue maintenance across stores.
122
+ * An API token or an app credential is refused BEFORE anything is sent
123
+ * (`PLATFORM_REQUIRES_SESSION`); the server refuses one anyway.
124
+ */
125
+ export interface PlatformCatalogModule {
126
+ /**
127
+ * One page of the translation-language repair for one store: removes the
128
+ * store's product and variant translation rows in languages it does not
129
+ * offer, ONLY from entities that also have a row in the store's default
130
+ * language. It never writes a name and never touches an entity whose rows
131
+ * are all foreign (`foreignOnlyCount`).
132
+ *
133
+ * Run it with `dryRun: true` first and keep the answer: its removed rows are
134
+ * the undo record. Follow `nextCursor` until it is null to cover the store;
135
+ * each page is its own transaction, so an apply that stops half-way leaves
136
+ * the pages before it applied and is safe to run again from the start.
137
+ *
138
+ * Refusals: `TRANSLATION_REPAIR_CHANNEL_NOT_FOUND` (404 `NotFoundError`),
139
+ * `TRANSLATION_REPAIR_NOT_A_STORE` (422 `BusinessRuleError`: the root channel
140
+ * or an organization hub), `TRANSLATION_REPAIR_CURSOR_INVALID` and
141
+ * `TRANSLATION_REPAIR_LIMIT_INVALID` (400 `ValidationError`), and
142
+ * `CHANNEL_ACCESS_DENIED` / `FORBIDDEN` (403 `PermissionDeniedError`) for a
143
+ * caller who is not a SuperAdmin.
144
+ */
145
+ repairTranslationLanguages(input: RepairTranslationLanguagesInput, call?: Call): Promise<TranslationRepairPage>;
146
+ }
147
+ export declare const REPAIR_TRANSLATION_LANGUAGES_MUTATION = "\n mutation PlatformRepairProductTranslationLanguages($input: ProductTranslationRepairInput!) {\n repairProductTranslationLanguages(input: $input) {\n runId\n dryRun\n channelId\n channelCode\n defaultLanguageCode\n offeredLanguageCodes\n repaired {\n kind\n id\n productId\n sku\n keptLanguageCode\n keptName\n removed {\n translationId\n languageCode\n name\n slug\n description\n }\n }\n skipped {\n kind\n id\n productId\n sku\n name\n reason\n }\n foreignOnlyCount\n nameMismatches {\n productId\n variantId\n sku\n productName\n variantName\n }\n nextCursor\n }\n }\n";
148
+ export declare function createPlatformCatalogModule(transport: Transport, config: AdminClientConfig): PlatformCatalogModule;
@@ -0,0 +1,74 @@
1
+ import { requireHumanSession } from './guard.js';
2
+ export const REPAIR_TRANSLATION_LANGUAGES_MUTATION = /* GraphQL */ `
3
+ mutation PlatformRepairProductTranslationLanguages($input: ProductTranslationRepairInput!) {
4
+ repairProductTranslationLanguages(input: $input) {
5
+ runId
6
+ dryRun
7
+ channelId
8
+ channelCode
9
+ defaultLanguageCode
10
+ offeredLanguageCodes
11
+ repaired {
12
+ kind
13
+ id
14
+ productId
15
+ sku
16
+ keptLanguageCode
17
+ keptName
18
+ removed {
19
+ translationId
20
+ languageCode
21
+ name
22
+ slug
23
+ description
24
+ }
25
+ }
26
+ skipped {
27
+ kind
28
+ id
29
+ productId
30
+ sku
31
+ name
32
+ reason
33
+ }
34
+ foreignOnlyCount
35
+ nameMismatches {
36
+ productId
37
+ variantId
38
+ sku
39
+ productName
40
+ variantName
41
+ }
42
+ nextCursor
43
+ }
44
+ }
45
+ `;
46
+ export function createPlatformCatalogModule(transport, config) {
47
+ return {
48
+ async repairTranslationLanguages(input, call) {
49
+ requireHumanSession(config);
50
+ // Only what the caller named: an omitted `limit` must reach the server as
51
+ // absent, so the SERVER's default applies rather than one copied here.
52
+ const variables = {
53
+ channelId: input.channelId,
54
+ dryRun: input.dryRun,
55
+ };
56
+ if (input.limit !== undefined)
57
+ variables.limit = input.limit;
58
+ if (input.cursor !== undefined && input.cursor !== null)
59
+ variables.cursor = input.cursor;
60
+ if (input.includeNameReport !== undefined) {
61
+ variables.includeNameReport = input.includeNameReport;
62
+ }
63
+ const data = await transport.graphql({
64
+ api: 'admin',
65
+ query: REPAIR_TRANSLATION_LANGUAGES_MUTATION,
66
+ variables: { input: variables },
67
+ },
68
+ // The store is named in the input, not by the tenant: a SuperAdmin
69
+ // repairs any store from wherever the client happens to be scoped.
70
+ { ...call, allowUnscoped: true });
71
+ return data.repairProductTranslationLanguages;
72
+ },
73
+ };
74
+ }
@@ -0,0 +1,111 @@
1
+ import type { Transport } from '@sixtyninecommerce/core';
2
+ import type { AdminClientConfig } from '../client.js';
3
+ import type { Call } from '../types.js';
4
+ /** Where a store's plugin grant comes from. */
5
+ export type PlatformEntitlementSource = 'channel' | 'organization';
6
+ /**
7
+ * One plugin grant of a named store (or of an organization's workspace), as the
8
+ * platform's control plane reads it.
9
+ */
10
+ export interface PlatformPluginEntitlement {
11
+ /**
12
+ * The grant row's id. An `organization` row carries the ORGANIZATION grant's
13
+ * id, shared by every channel that grant reaches, so pair it with
14
+ * `channelId` when you need a key that is unique across a `listAll`.
15
+ */
16
+ id: string;
17
+ /** The store, or the organization's workspace channel, the grant applies to. */
18
+ channelId: string;
19
+ /** The plugin's catalog key (`pos`, `afas`, `mollie`, …), exactly as the engine sends it. */
20
+ pluginKey: string;
21
+ /** ISO-8601 UTC. Connecting a plugin that is already connected refreshes it. */
22
+ connectedAt: string;
23
+ connectedByUserId: string | null;
24
+ notes: string | null;
25
+ /** ISO-8601 UTC. */
26
+ createdAt: string;
27
+ updatedAt: string;
28
+ /**
29
+ * `channel` for the store's own grant, `organization` for one its
30
+ * organization holds for every member store and its workspace. When both
31
+ * exist the store's own row wins, so a store lists a key once. An
32
+ * `organization` grant is changed on the organization
33
+ * (`platform.organizations.entitlements.set`), never through
34
+ * {@link PlatformEntitlementsModule.set}. A string on the wire: a newer
35
+ * engine may answer a source this SDK does not name yet.
36
+ */
37
+ source: PlatformEntitlementSource | (string & {});
38
+ }
39
+ export interface SetPlatformEntitlementOptions {
40
+ /** `true` connects the plugin for the store; `false` removes the store's own grant. */
41
+ connected: boolean;
42
+ /**
43
+ * Operator notes, kept with the store's own grant. Omitted keeps the notes
44
+ * already stored; `''` clears them. Ignored on a disconnect, which deletes
45
+ * the row. At most 512 characters: the engine stores no more, and a longer
46
+ * value fails the write.
47
+ */
48
+ notes?: string;
49
+ }
50
+ /**
51
+ * `admin.platform.entitlements` — which external plugins each store may use,
52
+ * read and switched by the platform's operators (spec §7, §9.2 `platform`).
53
+ *
54
+ * SuperAdmin with a HUMAN session only: an API token or an app credential is
55
+ * refused with `PLATFORM_REQUIRES_SESSION` (403) before anything is sent, and a
56
+ * session without SuperAdmin is refused `FORBIDDEN` (403) by the engine. The
57
+ * calls are platform-wide, so the client needs no tenant.
58
+ *
59
+ * Core plugins (search, email) are on for every store and never have a row.
60
+ * The engine reads these rows on every operation; the plugins service's own
61
+ * gates follow a change within its 60-second entitlement cache.
62
+ */
63
+ export interface PlatformEntitlementsModule {
64
+ /**
65
+ * The plugins one store may use: its own grants and the ones its
66
+ * organization holds (`source`), by plugin key. The store's own row wins
67
+ * where both exist. An organization's workspace channel lists its
68
+ * organization's grants the same way. The engine does not check that the
69
+ * channel exists: an unknown id is not refused, it answers the rows stored
70
+ * under it (normally none).
71
+ */
72
+ list(channelId: string, call?: Call): Promise<PlatformPluginEntitlement[]>;
73
+ /**
74
+ * Every grant across the platform. An organization grant appears once per
75
+ * member store and once for the organization's workspace
76
+ * (`source: 'organization'`, each under that channel's id), except where the
77
+ * channel holds the plugin itself.
78
+ */
79
+ listAll(call?: Call): Promise<PlatformPluginEntitlement[]>;
80
+ /**
81
+ * Connect (`connected: true`) or disconnect (`connected: false`) an external
82
+ * plugin for one store.
83
+ *
84
+ * - Connecting answers the store's own row; connecting again refreshes
85
+ * `connectedAt` and `connectedByUserId` (and the notes, when given).
86
+ * - Disconnecting deletes the store's OWN row and answers `null`. A plugin
87
+ * the store's organization also grants stays connected through it: read
88
+ * {@link list} again to see it as `source: 'organization'`.
89
+ *
90
+ * Refusals: `MANAGED_BY_ORGANIZATION` (422 `BusinessRuleError`: the store
91
+ * holds the plugin only through its organization; revoke it there),
92
+ * `ORGANIZATION_WORKSPACE_NOT_A_STORE` (422: an organization's workspace
93
+ * cannot be connected on its own; grant the plugin to the organization),
94
+ * `USER_INPUT_ERROR` (400 `ValidationError`: a key that is not an external
95
+ * plugin of the catalog), `FORBIDDEN` (403). The engine does not check
96
+ * that `channelId` names a store, so pass an id read from the platform's
97
+ * channel list.
98
+ *
99
+ * Not deduplicated by an idempotency key (an Admin API mutation). A second
100
+ * connect refreshes the same row. A second disconnect answers `null` again,
101
+ * unless the organization also grants the plugin: the first removed the
102
+ * store's own grant, so the second is refused `MANAGED_BY_ORGANIZATION`.
103
+ * After a disconnect whose answer was lost, read {@link list} instead of
104
+ * repeating it.
105
+ */
106
+ set(channelId: string, pluginKey: string, options: SetPlatformEntitlementOptions, call?: Call): Promise<PlatformPluginEntitlement | null>;
107
+ }
108
+ export declare const TENANT_PLUGIN_ENTITLEMENTS_QUERY = "\n query PlatformTenantPluginEntitlements($channelId: ID!) {\n tenantPluginEntitlements(channelId: $channelId) { \n id\n channelId\n pluginKey\n connectedAt\n connectedByUserId\n notes\n createdAt\n updatedAt\n source\n }\n }\n";
109
+ export declare const ALL_PLUGIN_ENTITLEMENTS_QUERY = "\n query PlatformAllPluginEntitlements {\n allPluginEntitlements { \n id\n channelId\n pluginKey\n connectedAt\n connectedByUserId\n notes\n createdAt\n updatedAt\n source\n }\n }\n";
110
+ export declare const SET_TENANT_PLUGIN_ENTITLEMENT_MUTATION = "\n mutation PlatformSetTenantPluginEntitlement(\n $channelId: ID!\n $pluginKey: String!\n $connected: Boolean!\n $notes: String\n ) {\n setTenantPluginEntitlement(\n channelId: $channelId\n pluginKey: $pluginKey\n connected: $connected\n notes: $notes\n ) { \n id\n channelId\n pluginKey\n connectedAt\n connectedByUserId\n notes\n createdAt\n updatedAt\n source\n }\n }\n";
111
+ export declare function createPlatformEntitlementsModule(transport: Transport, config: AdminClientConfig): PlatformEntitlementsModule;
@@ -0,0 +1,86 @@
1
+ import { requireHumanSession } from './guard.js';
2
+ const ENTITLEMENT_FIELDS = /* GraphQL */ `
3
+ id
4
+ channelId
5
+ pluginKey
6
+ connectedAt
7
+ connectedByUserId
8
+ notes
9
+ createdAt
10
+ updatedAt
11
+ source
12
+ `;
13
+ export const TENANT_PLUGIN_ENTITLEMENTS_QUERY = /* GraphQL */ `
14
+ query PlatformTenantPluginEntitlements($channelId: ID!) {
15
+ tenantPluginEntitlements(channelId: $channelId) { ${ENTITLEMENT_FIELDS} }
16
+ }
17
+ `;
18
+ export const ALL_PLUGIN_ENTITLEMENTS_QUERY = /* GraphQL */ `
19
+ query PlatformAllPluginEntitlements {
20
+ allPluginEntitlements { ${ENTITLEMENT_FIELDS} }
21
+ }
22
+ `;
23
+ export const SET_TENANT_PLUGIN_ENTITLEMENT_MUTATION = /* GraphQL */ `
24
+ mutation PlatformSetTenantPluginEntitlement(
25
+ $channelId: ID!
26
+ $pluginKey: String!
27
+ $connected: Boolean!
28
+ $notes: String
29
+ ) {
30
+ setTenantPluginEntitlement(
31
+ channelId: $channelId
32
+ pluginKey: $pluginKey
33
+ connected: $connected
34
+ notes: $notes
35
+ ) { ${ENTITLEMENT_FIELDS} }
36
+ }
37
+ `;
38
+ /** Only the fields this type promises, whatever else a newer engine adds to a row. */
39
+ function toEntitlement(row) {
40
+ return {
41
+ id: row.id,
42
+ channelId: row.channelId,
43
+ pluginKey: row.pluginKey,
44
+ connectedAt: row.connectedAt,
45
+ connectedByUserId: row.connectedByUserId,
46
+ notes: row.notes,
47
+ createdAt: row.createdAt,
48
+ updatedAt: row.updatedAt,
49
+ source: row.source,
50
+ };
51
+ }
52
+ export function createPlatformEntitlementsModule(transport, config) {
53
+ // Platform-wide: a SuperAdmin names the store by id, so no tenant is needed.
54
+ // Scoped to a store instead, the transport would first resolve that store
55
+ // through the CMS channel mirror and fail on one missing there.
56
+ const unscoped = (call) => ({ ...call, allowUnscoped: true });
57
+ return {
58
+ async list(channelId, call) {
59
+ requireHumanSession(config);
60
+ const data = await transport.graphql({ api: 'admin', query: TENANT_PLUGIN_ENTITLEMENTS_QUERY, variables: { channelId } }, unscoped(call));
61
+ return data.tenantPluginEntitlements.map(toEntitlement);
62
+ },
63
+ async listAll(call) {
64
+ requireHumanSession(config);
65
+ const data = await transport.graphql({ api: 'admin', query: ALL_PLUGIN_ENTITLEMENTS_QUERY }, unscoped(call));
66
+ return data.allPluginEntitlements.map(toEntitlement);
67
+ },
68
+ async set(channelId, pluginKey, options, call) {
69
+ requireHumanSession(config);
70
+ const data = await transport.graphql({
71
+ api: 'admin',
72
+ query: SET_TENANT_PLUGIN_ENTITLEMENT_MUTATION,
73
+ variables: {
74
+ channelId,
75
+ pluginKey,
76
+ connected: options.connected,
77
+ // The engine reads null as "keep the stored notes"; '' is a value
78
+ // that clears them, so it must not collapse into null here.
79
+ notes: options.notes ?? null,
80
+ },
81
+ }, unscoped(call));
82
+ const row = data.setTenantPluginEntitlement;
83
+ return row === null ? null : toEntitlement(row);
84
+ },
85
+ };
86
+ }
@@ -0,0 +1,139 @@
1
+ import { type Page, type Transport } from '@sixtyninecommerce/core';
2
+ import type { AdminClientConfig } from '../client.js';
3
+ import type { Call, ListOptions } from '../types.js';
4
+ import { type PlatformLegalEntityNumberingModule } from './legal-entity-numbering.js';
5
+ /**
6
+ * A company that issues a store's invoices (multi-region spec §7.1): its legal name, tax numbers,
7
+ * registered office and fiscal time zone. One or more stores trade under it. Its `id` is the
8
+ * engine's Seller id.
9
+ */
10
+ export interface LegalEntity {
11
+ id: string;
12
+ /** The Seller's name, set when it was created. Read-only: email store names fall back to it. */
13
+ name: string;
14
+ legalName: string | null;
15
+ vatNumber: string | null;
16
+ vatNumberUk: string | null;
17
+ eoriNumber: string | null;
18
+ registrationNumber: string | null;
19
+ registeredAddressLine1: string | null;
20
+ registeredAddressLine2: string | null;
21
+ registeredCity: string | null;
22
+ registeredPostalCode: string | null;
23
+ registeredProvince: string | null;
24
+ /** Upper-case ISO 3166-1 alpha-2. */
25
+ registeredCountry: string | null;
26
+ /** IANA time zone the fiscal year runs in (numbering resets), such as `Europe/Amsterdam`. */
27
+ timezone: string | null;
28
+ /** The entity's stores the caller may read; `storeCount` counts them all. */
29
+ stores: LegalEntityStore[];
30
+ storeCount: number;
31
+ /**
32
+ * Whether the caller may change it: a SuperAdmin, or a caller holding `UpdateSeller` on every one
33
+ * of its stores. Show the entity read-only when false.
34
+ */
35
+ editable: boolean;
36
+ /** ISO-8601 UTC. */
37
+ createdAt: string;
38
+ updatedAt: string;
39
+ }
40
+ export interface LegalEntityStore {
41
+ id: string;
42
+ code: string;
43
+ }
44
+ /**
45
+ * The legal fields of an entity. Values are trimmed by the engine. A field sent as `null` or `''`
46
+ * is cleared, the legal name aside; a field left out is kept.
47
+ */
48
+ export interface LegalEntityFields {
49
+ vatNumber?: string | null;
50
+ vatNumberUk?: string | null;
51
+ eoriNumber?: string | null;
52
+ registrationNumber?: string | null;
53
+ registeredAddressLine1?: string | null;
54
+ registeredAddressLine2?: string | null;
55
+ registeredCity?: string | null;
56
+ registeredPostalCode?: string | null;
57
+ registeredProvince?: string | null;
58
+ /** Upper-case ISO 3166-1 alpha-2, such as `NL`; anything else is `INVALID_COUNTRY_CODE`. */
59
+ registeredCountry?: string | null;
60
+ /** An IANA time zone; a fixed offset or an unknown name is `INVALID_TIMEZONE`. */
61
+ timezone?: string | null;
62
+ }
63
+ export interface CreateLegalEntityInput extends LegalEntityFields {
64
+ /** Required, and never empty: every invoice prints it. */
65
+ legalName: string;
66
+ /** Defaults to the legal name. */
67
+ name?: string;
68
+ }
69
+ export interface UpdateLegalEntityInput extends LegalEntityFields {
70
+ /** Changed when given; it can never be cleared (`LEGAL_ENTITY_FIELD_INVALID`). */
71
+ legalName?: string;
72
+ }
73
+ export interface LegalEntityListOptions extends ListOptions {
74
+ /** Matches the legal name, name, VAT or registration number, case-insensitively, as typed. */
75
+ term?: string;
76
+ }
77
+ /**
78
+ * `admin.platform.legalEntities` — the platform's legal entities (multi-region spec §7.1, decision
79
+ * 35 / Q9). Every method needs a human session: an API token or an app credential is refused with
80
+ * `PLATFORM_REQUIRES_SESSION` before anything is sent. A SuperAdmin needs no tenant on the client.
81
+ *
82
+ * - `list` and `create` are SuperAdmin-only (`FORBIDDEN` otherwise).
83
+ * - `get` answers the SuperAdmin any entity; anyone else only an entity whose every store they hold
84
+ * `ReadSettings` on, and `SHARED_SELLER_NOT_ALLOWED` (403) for one they hold it on only some
85
+ * stores of. Read the active store's entity with `admin.tenant.legalEntity` instead.
86
+ * - `update` needs `UpdateSeller` on every store of the entity (`SHARED_SELLER_NOT_ALLOWED`), and
87
+ * copies its legal name, VAT, EORI and registration numbers onto every one of them in the same
88
+ * transaction. The registered office and time zone are the entity's alone.
89
+ *
90
+ * An entity created with no store is not listed until a store is created under it, and only a
91
+ * SuperAdmin reads it by id. A store created under it, or moved onto it, takes its legal name and
92
+ * numbers.
93
+ *
94
+ * Neither write is deduplicated by an idempotency key (they are Admin API mutations). `update` is
95
+ * safe to repeat. `create` is not, and a lost answer cannot be recovered through this module: the
96
+ * new entity has no store yet, so `list` does not return it and its id went with the answer.
97
+ * Repeating the create makes a second entity, so confirm with the platform operator before you
98
+ * retry one whose outcome is unknown.
99
+ */
100
+ export interface PlatformLegalEntitiesModule {
101
+ /**
102
+ * Every legal entity with at least one store, by legal name. Pages default to 50; a `limit` above
103
+ * 100 is refused with `INVALID_LIMIT` (400), and walking on with `.next()` or `for await` reaches
104
+ * every one.
105
+ */
106
+ list(options?: LegalEntityListOptions): Promise<Page<LegalEntity>>;
107
+ /** Null when no legal entity has that id, or the caller may read none of its stores. */
108
+ get(id: string, call?: Call): Promise<LegalEntity | null>;
109
+ /**
110
+ * Creates a legal entity with no store yet. Refusals: `LEGAL_ENTITY_FIELD_INVALID`,
111
+ * `INVALID_COUNTRY_CODE`, `INVALID_TIMEZONE` (400).
112
+ */
113
+ create(input: CreateLegalEntityInput, call?: Call): Promise<LegalEntity>;
114
+ /**
115
+ * Changes the fields given. Refusals: `SHARED_SELLER_NOT_ALLOWED` (403), `ENTITY_NOT_FOUND`
116
+ * (404), `LEGAL_ENTITY_FIELD_INVALID`, `INVALID_COUNTRY_CODE`, `INVALID_TIMEZONE` (400).
117
+ */
118
+ update(id: string, input: UpdateLegalEntityInput, call?: Call): Promise<LegalEntity>;
119
+ /**
120
+ * The numbering series the entity's stores share (multi-region B6c): `get` needs `ReadSettings`
121
+ * and `update` `UpdateSettings`, on every store of the entity (`SHARED_SELLER_NOT_ALLOWED`).
122
+ */
123
+ numbering: PlatformLegalEntityNumberingModule;
124
+ }
125
+ export declare const LEGAL_ENTITY_FIELDS = "\n id\n name\n legalName\n vatNumber\n vatNumberUk\n eoriNumber\n registrationNumber\n registeredAddressLine1\n registeredAddressLine2\n registeredCity\n registeredPostalCode\n registeredProvince\n registeredCountry\n timezone\n stores {\n id\n code\n }\n storeCount\n editable\n createdAt\n updatedAt\n";
126
+ export declare const LEGAL_ENTITIES_QUERY = "\n query PlatformLegalEntities($options: LegalEntityListOptions) {\n legalEntities(options: $options) {\n totalItems\n items { \n id\n name\n legalName\n vatNumber\n vatNumberUk\n eoriNumber\n registrationNumber\n registeredAddressLine1\n registeredAddressLine2\n registeredCity\n registeredPostalCode\n registeredProvince\n registeredCountry\n timezone\n stores {\n id\n code\n }\n storeCount\n editable\n createdAt\n updatedAt\n }\n }\n }\n";
127
+ export declare const LEGAL_ENTITY_QUERY = "\n query PlatformLegalEntity($id: ID!) {\n legalEntity(id: $id) { \n id\n name\n legalName\n vatNumber\n vatNumberUk\n eoriNumber\n registrationNumber\n registeredAddressLine1\n registeredAddressLine2\n registeredCity\n registeredPostalCode\n registeredProvince\n registeredCountry\n timezone\n stores {\n id\n code\n }\n storeCount\n editable\n createdAt\n updatedAt\n }\n }\n";
128
+ export declare const CREATE_LEGAL_ENTITY_MUTATION = "\n mutation PlatformCreateLegalEntity($input: CreateLegalEntityInput!) {\n createLegalEntity(input: $input) { \n id\n name\n legalName\n vatNumber\n vatNumberUk\n eoriNumber\n registrationNumber\n registeredAddressLine1\n registeredAddressLine2\n registeredCity\n registeredPostalCode\n registeredProvince\n registeredCountry\n timezone\n stores {\n id\n code\n }\n storeCount\n editable\n createdAt\n updatedAt\n }\n }\n";
129
+ export declare const UPDATE_LEGAL_ENTITY_MUTATION = "\n mutation UpdateLegalEntity($id: ID!, $input: UpdateLegalEntityInput!) {\n updateLegalEntity(id: $id, input: $input) { \n id\n name\n legalName\n vatNumber\n vatNumberUk\n eoriNumber\n registrationNumber\n registeredAddressLine1\n registeredAddressLine2\n registeredCity\n registeredPostalCode\n registeredProvince\n registeredCountry\n timezone\n stores {\n id\n code\n }\n storeCount\n editable\n createdAt\n updatedAt\n }\n }\n";
130
+ /**
131
+ * The input as the engine declares it, field by field, so a caller spreading a wider object sends
132
+ * only what the input type has instead of failing validation. A field left out stays out (kept);
133
+ * `null` is sent as `null` (cleared).
134
+ */
135
+ export declare function legalEntityInputOf(input: LegalEntityFields & {
136
+ legalName?: string;
137
+ name?: string;
138
+ }): Record<string, string | null>;
139
+ export declare function createPlatformLegalEntitiesModule(transport: Transport, config: AdminClientConfig): PlatformLegalEntitiesModule;