@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.
- package/README.md +2739 -17
- package/dist/api-tokens/index.d.ts +15 -5
- package/dist/api-tokens/index.js +1 -0
- package/dist/apps/event-subscription.d.ts +59 -0
- package/dist/apps/event-subscription.js +32 -0
- package/dist/apps/hook-secret.d.ts +22 -0
- package/dist/apps/hook-secret.js +36 -0
- package/dist/apps/index.d.ts +54 -3
- package/dist/apps/index.js +17 -0
- package/dist/audit/index.d.ts +7 -0
- package/dist/auth/queries.d.ts +5 -1
- package/dist/auth/queries.js +6 -0
- package/dist/bulk/index.d.ts +323 -0
- package/dist/bulk/index.js +162 -0
- package/dist/catalog/assets.d.ts +147 -0
- package/dist/catalog/assets.js +164 -0
- package/dist/catalog/collections.d.ts +109 -0
- package/dist/catalog/collections.js +91 -0
- package/dist/catalog/facets.d.ts +103 -0
- package/dist/catalog/facets.js +100 -0
- package/dist/catalog/index.d.ts +115 -0
- package/dist/catalog/index.js +21 -0
- package/dist/catalog/options.d.ts +89 -0
- package/dist/catalog/options.js +88 -0
- package/dist/catalog/products-bulk.d.ts +21 -0
- package/dist/catalog/products-bulk.js +58 -0
- package/dist/catalog/products.d.ts +650 -0
- package/dist/catalog/products.js +458 -0
- package/dist/catalog/properties.d.ts +234 -0
- package/dist/catalog/properties.js +158 -0
- package/dist/catalog/types.d.ts +174 -0
- package/dist/catalog/types.js +141 -0
- package/dist/catalog/variants.d.ts +324 -0
- package/dist/catalog/variants.js +288 -0
- package/dist/catalog/visibility.d.ts +57 -0
- package/dist/catalog/visibility.js +59 -0
- package/dist/client.d.ts +192 -3
- package/dist/client.js +87 -12
- package/dist/compliance/index.d.ts +88 -0
- package/dist/compliance/index.js +88 -0
- package/dist/customer-data-redaction.d.ts +14 -0
- package/dist/customer-data-redaction.js +22 -0
- package/dist/customers/index.d.ts +428 -17
- package/dist/customers/index.js +322 -15
- package/dist/documents/index.d.ts +513 -16
- package/dist/documents/index.js +229 -3
- package/dist/events/index.d.ts +259 -0
- package/dist/events/index.js +147 -0
- package/dist/feeds/ai.d.ts +46 -0
- package/dist/feeds/ai.js +43 -0
- package/dist/feeds/categories.d.ts +50 -0
- package/dist/feeds/categories.js +56 -0
- package/dist/feeds/configs.d.ts +54 -0
- package/dist/feeds/configs.js +85 -0
- package/dist/feeds/identifier-fields.d.ts +25 -0
- package/dist/feeds/identifier-fields.js +29 -0
- package/dist/feeds/index.d.ts +54 -0
- package/dist/feeds/index.js +46 -0
- package/dist/feeds/preserve.d.ts +38 -0
- package/dist/feeds/preserve.js +74 -0
- package/dist/feeds/rules.d.ts +42 -0
- package/dist/feeds/rules.js +56 -0
- package/dist/feeds/types.d.ts +654 -0
- package/dist/feeds/types.js +135 -0
- package/dist/fulfillments/index.d.ts +437 -0
- package/dist/fulfillments/index.js +534 -0
- package/dist/heads/index.d.ts +60 -0
- package/dist/heads/index.js +45 -0
- package/dist/hooks/assignments.d.ts +90 -0
- package/dist/hooks/assignments.js +53 -0
- package/dist/hooks/index.d.ts +97 -0
- package/dist/hooks/index.js +76 -0
- package/dist/index.d.ts +95 -9
- package/dist/index.js +21 -1
- package/dist/integrations/index.d.ts +14 -0
- package/dist/integrations/index.js +9 -0
- package/dist/integrations/pipelines.d.ts +113 -0
- package/dist/integrations/pipelines.js +62 -0
- package/dist/integrations/types.d.ts +176 -0
- package/dist/integrations/types.js +8 -0
- package/dist/jobs/bulk-action-run.d.ts +74 -0
- package/dist/jobs/bulk-action-run.js +69 -0
- package/dist/locations/index.d.ts +6 -2
- package/dist/meta/index.d.ts +32 -0
- package/dist/orders/bulk.d.ts +75 -0
- package/dist/orders/bulk.js +150 -0
- package/dist/orders/commit-edit.d.ts +123 -0
- package/dist/orders/commit-edit.js +142 -0
- package/dist/orders/deposit-policy.d.ts +103 -0
- package/dist/orders/deposit-policy.js +202 -0
- package/dist/orders/get.d.ts +722 -0
- package/dist/orders/get.js +530 -0
- package/dist/orders/index.d.ts +186 -21
- package/dist/orders/index.js +57 -0
- package/dist/orders/line-price-preview.d.ts +297 -0
- package/dist/orders/line-price-preview.js +50 -0
- package/dist/orders/manual-discounts.d.ts +141 -0
- package/dist/orders/manual-discounts.js +97 -0
- package/dist/orders/manual-pricing.d.ts +192 -0
- package/dist/orders/manual-pricing.js +72 -0
- package/dist/orders/order-entry.d.ts +566 -0
- package/dist/orders/order-entry.js +129 -0
- package/dist/orders/parcel-charge.d.ts +45 -0
- package/dist/orders/parcel-charge.js +55 -0
- package/dist/orders/plan-edit.d.ts +489 -0
- package/dist/orders/plan-edit.js +93 -0
- package/dist/orders/status.d.ts +235 -0
- package/dist/orders/status.js +96 -0
- package/dist/orders/surcharges.d.ts +75 -2
- package/dist/orders/surcharges.js +110 -4
- package/dist/payments/account-credit.d.ts +171 -0
- package/dist/payments/account-credit.js +107 -0
- package/dist/payments/index.d.ts +241 -9
- package/dist/payments/index.js +92 -0
- package/dist/payments/methods.d.ts +37 -0
- package/dist/payments/methods.js +33 -0
- package/dist/platform/catalog.d.ts +148 -0
- package/dist/platform/catalog.js +74 -0
- package/dist/platform/entitlements.d.ts +111 -0
- package/dist/platform/entitlements.js +86 -0
- package/dist/platform/legal-entities.d.ts +139 -0
- package/dist/platform/legal-entities.js +126 -0
- package/dist/platform/legal-entity-numbering.d.ts +103 -0
- package/dist/platform/legal-entity-numbering.js +101 -0
- package/dist/platform/organization-membership.d.ts +80 -0
- package/dist/platform/organization-membership.js +71 -0
- package/dist/platform/organization-settings.d.ts +33 -0
- package/dist/platform/organization-settings.js +42 -0
- package/dist/platform/organization-sharing.d.ts +172 -0
- package/dist/platform/organization-sharing.js +120 -0
- package/dist/platform/organizations.d.ts +205 -0
- package/dist/platform/organizations.js +180 -0
- package/dist/platform/provision-store.d.ts +88 -8
- package/dist/platform/provision-store.js +12 -0
- package/dist/platform/reports.d.ts +46 -0
- package/dist/platform/reports.js +31 -0
- package/dist/platform/server-metrics.d.ts +58 -0
- package/dist/platform/server-metrics.js +11 -0
- package/dist/pos/index.d.ts +567 -0
- package/dist/pos/index.js +205 -0
- package/dist/pricing/index.d.ts +408 -0
- package/dist/pricing/index.js +353 -0
- package/dist/pricing/vouchers.d.ts +103 -0
- package/dist/pricing/vouchers.js +69 -0
- package/dist/provenance/index.d.ts +93 -0
- package/dist/provenance/index.js +53 -0
- package/dist/reports/index.d.ts +199 -0
- package/dist/reports/index.js +154 -0
- package/dist/returns/index.d.ts +225 -0
- package/dist/returns/index.js +160 -0
- package/dist/search/index.d.ts +99 -0
- package/dist/search/index.js +62 -0
- package/dist/shipping/parcel-count-suggestion.d.ts +52 -0
- package/dist/shipping/parcel-count-suggestion.js +73 -0
- package/dist/shipping/parcel-rule.d.ts +48 -0
- package/dist/shipping/parcel-rule.js +91 -0
- package/dist/stock/index.d.ts +166 -0
- package/dist/stock/index.js +115 -0
- package/dist/stock/transfers.d.ts +520 -0
- package/dist/stock/transfers.js +437 -0
- package/dist/stored-value/index.d.ts +154 -0
- package/dist/stored-value/index.js +130 -0
- package/dist/tenant/capabilities.d.ts +10 -0
- package/dist/tenant/entitlements.d.ts +35 -0
- package/dist/tenant/entitlements.js +15 -0
- package/dist/tenant/field-groups.d.ts +64 -0
- package/dist/tenant/field-groups.js +34 -0
- package/dist/tenant/legal-entity.d.ts +32 -0
- package/dist/tenant/legal-entity.js +41 -0
- package/dist/tenant/notifications.d.ts +148 -0
- package/dist/tenant/notifications.js +79 -0
- package/dist/tenant/retention.d.ts +10 -4
- package/dist/tenant/settings-sources.d.ts +103 -0
- package/dist/tenant/settings-sources.js +92 -0
- package/dist/types.d.ts +86 -11
- package/dist/uploads/index.d.ts +11 -7
- package/dist/users/index.d.ts +104 -0
- package/dist/users/index.js +80 -0
- package/package.json +3 -2
|
@@ -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;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { pageFromVendureList, readOffsetCursor, } from '@sixtyninecommerce/core';
|
|
2
|
+
import { requireHumanSession } from './guard.js';
|
|
3
|
+
import { createPlatformLegalEntityNumberingModule, } from './legal-entity-numbering.js';
|
|
4
|
+
export const LEGAL_ENTITY_FIELDS = /* GraphQL */ `
|
|
5
|
+
id
|
|
6
|
+
name
|
|
7
|
+
legalName
|
|
8
|
+
vatNumber
|
|
9
|
+
vatNumberUk
|
|
10
|
+
eoriNumber
|
|
11
|
+
registrationNumber
|
|
12
|
+
registeredAddressLine1
|
|
13
|
+
registeredAddressLine2
|
|
14
|
+
registeredCity
|
|
15
|
+
registeredPostalCode
|
|
16
|
+
registeredProvince
|
|
17
|
+
registeredCountry
|
|
18
|
+
timezone
|
|
19
|
+
stores {
|
|
20
|
+
id
|
|
21
|
+
code
|
|
22
|
+
}
|
|
23
|
+
storeCount
|
|
24
|
+
editable
|
|
25
|
+
createdAt
|
|
26
|
+
updatedAt
|
|
27
|
+
`;
|
|
28
|
+
export const LEGAL_ENTITIES_QUERY = /* GraphQL */ `
|
|
29
|
+
query PlatformLegalEntities($options: LegalEntityListOptions) {
|
|
30
|
+
legalEntities(options: $options) {
|
|
31
|
+
totalItems
|
|
32
|
+
items { ${LEGAL_ENTITY_FIELDS} }
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
`;
|
|
36
|
+
export const LEGAL_ENTITY_QUERY = /* GraphQL */ `
|
|
37
|
+
query PlatformLegalEntity($id: ID!) {
|
|
38
|
+
legalEntity(id: $id) { ${LEGAL_ENTITY_FIELDS} }
|
|
39
|
+
}
|
|
40
|
+
`;
|
|
41
|
+
export const CREATE_LEGAL_ENTITY_MUTATION = /* GraphQL */ `
|
|
42
|
+
mutation PlatformCreateLegalEntity($input: CreateLegalEntityInput!) {
|
|
43
|
+
createLegalEntity(input: $input) { ${LEGAL_ENTITY_FIELDS} }
|
|
44
|
+
}
|
|
45
|
+
`;
|
|
46
|
+
export const UPDATE_LEGAL_ENTITY_MUTATION = /* GraphQL */ `
|
|
47
|
+
mutation UpdateLegalEntity($id: ID!, $input: UpdateLegalEntityInput!) {
|
|
48
|
+
updateLegalEntity(id: $id, input: $input) { ${LEGAL_ENTITY_FIELDS} }
|
|
49
|
+
}
|
|
50
|
+
`;
|
|
51
|
+
/** The engine's own default; it refuses a page above 100. */
|
|
52
|
+
const DEFAULT_PAGE_SIZE = 50;
|
|
53
|
+
const FIELD_NAMES = [
|
|
54
|
+
'vatNumber',
|
|
55
|
+
'vatNumberUk',
|
|
56
|
+
'eoriNumber',
|
|
57
|
+
'registrationNumber',
|
|
58
|
+
'registeredAddressLine1',
|
|
59
|
+
'registeredAddressLine2',
|
|
60
|
+
'registeredCity',
|
|
61
|
+
'registeredPostalCode',
|
|
62
|
+
'registeredProvince',
|
|
63
|
+
'registeredCountry',
|
|
64
|
+
'timezone',
|
|
65
|
+
];
|
|
66
|
+
/**
|
|
67
|
+
* The input as the engine declares it, field by field, so a caller spreading a wider object sends
|
|
68
|
+
* only what the input type has instead of failing validation. A field left out stays out (kept);
|
|
69
|
+
* `null` is sent as `null` (cleared).
|
|
70
|
+
*/
|
|
71
|
+
export function legalEntityInputOf(input) {
|
|
72
|
+
const out = {};
|
|
73
|
+
for (const field of ['name', 'legalName', ...FIELD_NAMES]) {
|
|
74
|
+
const value = input[field];
|
|
75
|
+
if (value !== undefined)
|
|
76
|
+
out[field] = value;
|
|
77
|
+
}
|
|
78
|
+
return out;
|
|
79
|
+
}
|
|
80
|
+
export function createPlatformLegalEntitiesModule(transport, config) {
|
|
81
|
+
// Platform-wide, so a SuperAdmin must not need a tenant.
|
|
82
|
+
const unscoped = (call) => ({ ...call, allowUnscoped: true });
|
|
83
|
+
const listPage = async (skip, take, options) => {
|
|
84
|
+
requireHumanSession(config);
|
|
85
|
+
const variables = {
|
|
86
|
+
options: { skip, take, ...(options?.term === undefined ? {} : { term: options.term }) },
|
|
87
|
+
};
|
|
88
|
+
const data = await transport.graphql({ api: 'admin', query: LEGAL_ENTITIES_QUERY, variables }, unscoped(options));
|
|
89
|
+
return pageFromVendureList(data.legalEntities, skip, take, (cursor) => listPage(readOffsetCursor(cursor), take, options));
|
|
90
|
+
};
|
|
91
|
+
return {
|
|
92
|
+
numbering: createPlatformLegalEntityNumberingModule(transport, config),
|
|
93
|
+
async list(options) {
|
|
94
|
+
// Guarded here as well as per page: a bad cursor must not be reported ahead of the
|
|
95
|
+
// credential refusal.
|
|
96
|
+
requireHumanSession(config);
|
|
97
|
+
const take = options?.limit ?? DEFAULT_PAGE_SIZE;
|
|
98
|
+
const skip = options?.after === undefined ? 0 : readOffsetCursor(options.after);
|
|
99
|
+
return listPage(skip, take, options);
|
|
100
|
+
},
|
|
101
|
+
async get(id, call) {
|
|
102
|
+
requireHumanSession(config);
|
|
103
|
+
const data = await transport.graphql({ api: 'admin', query: LEGAL_ENTITY_QUERY, variables: { id } }, unscoped(call));
|
|
104
|
+
return data.legalEntity;
|
|
105
|
+
},
|
|
106
|
+
async create(input, call) {
|
|
107
|
+
requireHumanSession(config);
|
|
108
|
+
const data = await transport.graphql({
|
|
109
|
+
api: 'admin',
|
|
110
|
+
query: CREATE_LEGAL_ENTITY_MUTATION,
|
|
111
|
+
variables: { input: legalEntityInputOf(input) },
|
|
112
|
+
}, unscoped(call));
|
|
113
|
+
return data.createLegalEntity;
|
|
114
|
+
},
|
|
115
|
+
async update(id, input, call) {
|
|
116
|
+
requireHumanSession(config);
|
|
117
|
+
const { name: _ignored, ...fields } = input;
|
|
118
|
+
const data = await transport.graphql({
|
|
119
|
+
api: 'admin',
|
|
120
|
+
query: UPDATE_LEGAL_ENTITY_MUTATION,
|
|
121
|
+
variables: { id, input: legalEntityInputOf(fields) },
|
|
122
|
+
}, unscoped(call));
|
|
123
|
+
return data.updateLegalEntity;
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { type Transport } from '@sixtyninecommerce/core';
|
|
2
|
+
import type { AdminClientConfig } from '../client.js';
|
|
3
|
+
import type { Call } from '../types.js';
|
|
4
|
+
import type { LegalEntityStore } from './legal-entities.js';
|
|
5
|
+
/**
|
|
6
|
+
* A legal entity's numbering (multi-region B6c): the series its stores share. One store's invoice
|
|
7
|
+
* and credit-note numbers, and its AR credit notes where B2B credit is installed, are drawn from
|
|
8
|
+
* these, whichever store issues the document. A sandbox store numbers on its own rows and is not
|
|
9
|
+
* one of its stores here.
|
|
10
|
+
*/
|
|
11
|
+
export interface LegalEntityNumbering {
|
|
12
|
+
/** The legal entity's id. */
|
|
13
|
+
legalEntityId: string;
|
|
14
|
+
series: LegalEntityNumberingSeries[];
|
|
15
|
+
/** The stores sharing the series that the caller may read; `storeCount` counts them all. */
|
|
16
|
+
stores: LegalEntityStore[];
|
|
17
|
+
storeCount: number;
|
|
18
|
+
/**
|
|
19
|
+
* Whether the caller may change it: a SuperAdmin, or a caller holding `UpdateSettings` on every
|
|
20
|
+
* store of the entity. Show the series read-only when false.
|
|
21
|
+
*/
|
|
22
|
+
editable: boolean;
|
|
23
|
+
}
|
|
24
|
+
/** The series the engine numbers: `invoice`, `credit_note`, and `ar_credit_note` with B2B credit. */
|
|
25
|
+
export type LegalEntityNumberingSeriesName = 'invoice' | 'credit_note' | 'ar_credit_note';
|
|
26
|
+
export interface LegalEntityNumberingSeries {
|
|
27
|
+
series: LegalEntityNumberingSeriesName;
|
|
28
|
+
/**
|
|
29
|
+
* False while the entity has no shared series yet. A lone numbering store's first document then
|
|
30
|
+
* sets it up; with two or more (AR credit notes: two or more that issued), every store's
|
|
31
|
+
* documents are refused (`LEGAL_ENTITY_NUMBERING_UNSET`) until an `update` sets the series up,
|
|
32
|
+
* and any `update` does, even an empty one. Every field below is null then.
|
|
33
|
+
*/
|
|
34
|
+
configured: boolean;
|
|
35
|
+
prefix: string | null;
|
|
36
|
+
suffix: string | null;
|
|
37
|
+
digits: number | null;
|
|
38
|
+
/** `none`, `short` (`26`) or `full` (`2026`). */
|
|
39
|
+
yearFormat: string | null;
|
|
40
|
+
/** Restart at 1 each fiscal year (in the entity's time zone); only with a year in the number. */
|
|
41
|
+
yearReset: boolean | null;
|
|
42
|
+
/** The next number the series issues. Read-only: only issuing a document moves it. */
|
|
43
|
+
nextNumber: number | null;
|
|
44
|
+
/** The fiscal year the series last restarted in. Read-only. */
|
|
45
|
+
lastResetYear: number | null;
|
|
46
|
+
/** `invoice` only: the provider that numbers it; null means each store's own setting. */
|
|
47
|
+
invoicingProvider: string | null;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* A format change of one series. A field left out is kept; only the fields that differ from the
|
|
51
|
+
* entity's series count, so on a configured series sending back what was read changes nothing.
|
|
52
|
+
* On a series with `configured: false` any update, even an empty one, sets the series up.
|
|
53
|
+
*/
|
|
54
|
+
export interface UpdateLegalEntityNumberingInput {
|
|
55
|
+
series: LegalEntityNumberingSeriesName;
|
|
56
|
+
/** At most 30 characters, never starting with `TEST-` (`NUMBER_PREFIX_RESERVED`). */
|
|
57
|
+
prefix?: string;
|
|
58
|
+
/** At most 30 characters. */
|
|
59
|
+
suffix?: string;
|
|
60
|
+
/** A whole number from 3 to 8 (`NUMBERING_DIGITS_INVALID`). */
|
|
61
|
+
digits?: number;
|
|
62
|
+
/** `none`, `short` or `full` (`NUMBERING_YEAR_FORMAT_INVALID`). */
|
|
63
|
+
yearFormat?: 'none' | 'short' | 'full';
|
|
64
|
+
/** Restart at 1 each fiscal year; it has no effect without a year in the number. */
|
|
65
|
+
yearReset?: boolean;
|
|
66
|
+
}
|
|
67
|
+
export declare const LEGAL_ENTITY_NUMBERING_FIELDS = "\n legalEntityId\n series {\n series\n configured\n prefix\n suffix\n digits\n yearFormat\n yearReset\n nextNumber\n lastResetYear\n invoicingProvider\n }\n stores {\n id\n code\n }\n storeCount\n editable\n";
|
|
68
|
+
export declare const LEGAL_ENTITY_NUMBERING_QUERY = "\n query LegalEntityNumbering($legalEntityId: ID) {\n legalEntityNumbering(legalEntityId: $legalEntityId) { \n legalEntityId\n series {\n series\n configured\n prefix\n suffix\n digits\n yearFormat\n yearReset\n nextNumber\n lastResetYear\n invoicingProvider\n }\n stores {\n id\n code\n }\n storeCount\n editable\n }\n }\n";
|
|
69
|
+
export declare const UPDATE_LEGAL_ENTITY_NUMBERING_MUTATION = "\n mutation UpdateLegalEntityNumbering($input: UpdateLegalEntityNumberingInput!) {\n updateLegalEntityNumbering(input: $input) { \n legalEntityId\n series {\n series\n configured\n prefix\n suffix\n digits\n yearFormat\n yearReset\n nextNumber\n lastResetYear\n invoicingProvider\n }\n stores {\n id\n code\n }\n storeCount\n editable\n }\n }\n";
|
|
70
|
+
/**
|
|
71
|
+
* The input as the engine declares it, field by field, so a caller spreading a series it read
|
|
72
|
+
* (which carries counters and `configured`) sends only the format, and a field left out stays out.
|
|
73
|
+
*/
|
|
74
|
+
export declare function legalEntityNumberingInputOf(input: UpdateLegalEntityNumberingInput, legalEntityId?: string): Record<string, unknown>;
|
|
75
|
+
/**
|
|
76
|
+
* Refusals the engine answers on `update`: `SHARED_SELLER_NOT_ALLOWED` (403),
|
|
77
|
+
* `ENTITY_NOT_FOUND` (404), `NUMBERING_SERIES_INVALID`, `NUMBERING_YEAR_FORMAT_INVALID`,
|
|
78
|
+
* `NUMBERING_DIGITS_INVALID`, `NUMBERING_AFFIX_INVALID` (400), `NUMBER_PREFIX_RESERVED`,
|
|
79
|
+
* `LEGAL_ENTITY_NUMBERING_PROVIDERS_DIFFER`, `NUMBERING_RESTART_WOULD_REPEAT`,
|
|
80
|
+
* `NUMBERING_YEAR_FORMAT_UNKNOWN` (422).
|
|
81
|
+
*/
|
|
82
|
+
export interface PlatformLegalEntityNumberingModule {
|
|
83
|
+
/**
|
|
84
|
+
* Null when no legal entity has that id, or the caller may read none of its stores;
|
|
85
|
+
* `SHARED_SELLER_NOT_ALLOWED` (403) when it may read only some of them.
|
|
86
|
+
*/
|
|
87
|
+
get(legalEntityId: string, call?: Call): Promise<LegalEntityNumbering | null>;
|
|
88
|
+
/**
|
|
89
|
+
* Changes the format of one series, on the entity and on every store row of it, in one
|
|
90
|
+
* transaction, and sets the series up when it has none yet: it then continues past every number
|
|
91
|
+
* any store issued. Counters change only when an update sets a series up. Safe to repeat.
|
|
92
|
+
*/
|
|
93
|
+
update(legalEntityId: string, input: UpdateLegalEntityNumberingInput, call?: Call): Promise<LegalEntityNumbering>;
|
|
94
|
+
}
|
|
95
|
+
/** The same, for the ACTIVE store's legal entity: one request each, no id needed. */
|
|
96
|
+
export interface TenantLegalEntityNumberingModule {
|
|
97
|
+
/** Null on a store with no legal entity, an organization's workspace and the root channel. */
|
|
98
|
+
get(call?: Call): Promise<LegalEntityNumbering | null>;
|
|
99
|
+
/** `ENTITY_NOT_FOUND` (404) on a store with no legal entity; otherwise as the platform's. */
|
|
100
|
+
update(input: UpdateLegalEntityNumberingInput, call?: Call): Promise<LegalEntityNumbering>;
|
|
101
|
+
}
|
|
102
|
+
export declare function createPlatformLegalEntityNumberingModule(transport: Transport, config: AdminClientConfig): PlatformLegalEntityNumberingModule;
|
|
103
|
+
export declare function createTenantLegalEntityNumberingModule(transport: Transport): TenantLegalEntityNumberingModule;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { ValidationError } from '@sixtyninecommerce/core';
|
|
2
|
+
import { requireHumanSession } from './guard.js';
|
|
3
|
+
export const LEGAL_ENTITY_NUMBERING_FIELDS = /* GraphQL */ `
|
|
4
|
+
legalEntityId
|
|
5
|
+
series {
|
|
6
|
+
series
|
|
7
|
+
configured
|
|
8
|
+
prefix
|
|
9
|
+
suffix
|
|
10
|
+
digits
|
|
11
|
+
yearFormat
|
|
12
|
+
yearReset
|
|
13
|
+
nextNumber
|
|
14
|
+
lastResetYear
|
|
15
|
+
invoicingProvider
|
|
16
|
+
}
|
|
17
|
+
stores {
|
|
18
|
+
id
|
|
19
|
+
code
|
|
20
|
+
}
|
|
21
|
+
storeCount
|
|
22
|
+
editable
|
|
23
|
+
`;
|
|
24
|
+
export const LEGAL_ENTITY_NUMBERING_QUERY = /* GraphQL */ `
|
|
25
|
+
query LegalEntityNumbering($legalEntityId: ID) {
|
|
26
|
+
legalEntityNumbering(legalEntityId: $legalEntityId) { ${LEGAL_ENTITY_NUMBERING_FIELDS} }
|
|
27
|
+
}
|
|
28
|
+
`;
|
|
29
|
+
export const UPDATE_LEGAL_ENTITY_NUMBERING_MUTATION = /* GraphQL */ `
|
|
30
|
+
mutation UpdateLegalEntityNumbering($input: UpdateLegalEntityNumberingInput!) {
|
|
31
|
+
updateLegalEntityNumbering(input: $input) { ${LEGAL_ENTITY_NUMBERING_FIELDS} }
|
|
32
|
+
}
|
|
33
|
+
`;
|
|
34
|
+
const INPUT_FIELDS = ['series', 'prefix', 'suffix', 'digits', 'yearFormat', 'yearReset'];
|
|
35
|
+
/**
|
|
36
|
+
* The input as the engine declares it, field by field, so a caller spreading a series it read
|
|
37
|
+
* (which carries counters and `configured`) sends only the format, and a field left out stays out.
|
|
38
|
+
*/
|
|
39
|
+
export function legalEntityNumberingInputOf(input, legalEntityId) {
|
|
40
|
+
const out = {};
|
|
41
|
+
if (legalEntityId !== undefined)
|
|
42
|
+
out.legalEntityId = legalEntityId;
|
|
43
|
+
for (const field of INPUT_FIELDS) {
|
|
44
|
+
const value = input[field];
|
|
45
|
+
if (value !== undefined && value !== null)
|
|
46
|
+
out[field] = value;
|
|
47
|
+
}
|
|
48
|
+
return out;
|
|
49
|
+
}
|
|
50
|
+
async function read(transport, legalEntityId, call) {
|
|
51
|
+
const data = await transport.graphql({
|
|
52
|
+
api: 'admin',
|
|
53
|
+
query: LEGAL_ENTITY_NUMBERING_QUERY,
|
|
54
|
+
variables: legalEntityId === undefined ? {} : { legalEntityId },
|
|
55
|
+
}, call);
|
|
56
|
+
return data.legalEntityNumbering;
|
|
57
|
+
}
|
|
58
|
+
async function write(transport, legalEntityId, input, call) {
|
|
59
|
+
const data = await transport.graphql({
|
|
60
|
+
api: 'admin',
|
|
61
|
+
query: UPDATE_LEGAL_ENTITY_NUMBERING_MUTATION,
|
|
62
|
+
variables: { input: legalEntityNumberingInputOf(input, legalEntityId) },
|
|
63
|
+
}, call);
|
|
64
|
+
return data.updateLegalEntityNumbering;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* The platform methods name the entity: without an id the engine would answer for the ACTIVE
|
|
68
|
+
* store's entity instead, which on a tenant-scoped client is a different one than asked for.
|
|
69
|
+
*/
|
|
70
|
+
function requireLegalEntityId(legalEntityId) {
|
|
71
|
+
if (typeof legalEntityId !== 'string' || legalEntityId.trim() === '') {
|
|
72
|
+
throw new ValidationError({
|
|
73
|
+
code: 'LEGAL_ENTITY_ID_REQUIRED',
|
|
74
|
+
message: 'platform.legalEntities.numbering needs the legal entity id. For the active store’s ' +
|
|
75
|
+
'entity use tenant.legalEntity.numbering.',
|
|
76
|
+
status: 400,
|
|
77
|
+
service: 'vendure',
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
return legalEntityId;
|
|
81
|
+
}
|
|
82
|
+
export function createPlatformLegalEntityNumberingModule(transport, config) {
|
|
83
|
+
// Platform-wide, so a SuperAdmin must not need a tenant.
|
|
84
|
+
const unscoped = (call) => ({ ...call, allowUnscoped: true });
|
|
85
|
+
return {
|
|
86
|
+
async get(legalEntityId, call) {
|
|
87
|
+
requireHumanSession(config);
|
|
88
|
+
return read(transport, requireLegalEntityId(legalEntityId), unscoped(call));
|
|
89
|
+
},
|
|
90
|
+
async update(legalEntityId, input, call) {
|
|
91
|
+
requireHumanSession(config);
|
|
92
|
+
return write(transport, requireLegalEntityId(legalEntityId), input, unscoped(call));
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
export function createTenantLegalEntityNumberingModule(transport) {
|
|
97
|
+
return {
|
|
98
|
+
get: (call) => read(transport, undefined, call),
|
|
99
|
+
update: (input, call) => write(transport, undefined, input, call),
|
|
100
|
+
};
|
|
101
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { type Transport } from '@sixtyninecommerce/core';
|
|
2
|
+
import type { AdminClientConfig } from '../client.js';
|
|
3
|
+
import type { Call } from '../types.js';
|
|
4
|
+
/** A store's membership of an organization (multi-region spec §5.1, §7.10.3). */
|
|
5
|
+
export interface OrganizationMembership {
|
|
6
|
+
/** The member store's channel id. */
|
|
7
|
+
channelId: string;
|
|
8
|
+
/** The member store. Null once the store no longer exists. */
|
|
9
|
+
channel: {
|
|
10
|
+
id: string;
|
|
11
|
+
code: string;
|
|
12
|
+
} | null;
|
|
13
|
+
organizationId: string;
|
|
14
|
+
/** A free grouping label, such as `EMEA`. Null when none is set. */
|
|
15
|
+
region: string | null;
|
|
16
|
+
/** Whether the storefront region switcher lists this store. */
|
|
17
|
+
listedInRegionSwitcher: boolean;
|
|
18
|
+
/** ISO-8601 UTC. */
|
|
19
|
+
joinedAt: string;
|
|
20
|
+
}
|
|
21
|
+
export interface JoinOrganizationInput {
|
|
22
|
+
/** The organization code, not the `org-` workspace channel code. */
|
|
23
|
+
organizationCode: string;
|
|
24
|
+
/** The store's channel id. */
|
|
25
|
+
channelId: string;
|
|
26
|
+
/**
|
|
27
|
+
* A grouping label, at most 64 characters with no line breaks. Omitted keeps
|
|
28
|
+
* the region a re-join finds (a first join has none); `null` or `''` clears it.
|
|
29
|
+
*/
|
|
30
|
+
region?: string | null;
|
|
31
|
+
/**
|
|
32
|
+
* `true`: the store keeps its own values for every settings group instead of
|
|
33
|
+
* inheriting the organization's; each group can be switched to inherit later.
|
|
34
|
+
* On a re-join, a group the store inherits unlocked at that moment starts
|
|
35
|
+
* from a copy of the organization's values.
|
|
36
|
+
* Omitted or `false`: it inherits whatever the organization shares, and on a
|
|
37
|
+
* re-join nothing it already keeps changes. Needs an engine that knows the
|
|
38
|
+
* field (a `true` sent to an older one is a GraphQL validation error).
|
|
39
|
+
*/
|
|
40
|
+
keepOwnSettings?: boolean;
|
|
41
|
+
}
|
|
42
|
+
/** The membership half of `admin.platform.organizations`. */
|
|
43
|
+
export interface OrganizationMembershipMethods {
|
|
44
|
+
/**
|
|
45
|
+
* Makes a store a member of an organization and answers the membership: the
|
|
46
|
+
* organization's roles are granted on the store, and the workspace takes on
|
|
47
|
+
* the store's currencies and languages. Joining the organization the store
|
|
48
|
+
* is already in answers the same membership (`joinedAt` unchanged), changing
|
|
49
|
+
* only the region when one is given — so a join whose answer was lost is
|
|
50
|
+
* safe to repeat. `keepOwnSettings: true` makes the store keep its own
|
|
51
|
+
* settings (only the groups it does not keep yet are added on a re-join).
|
|
52
|
+
* There is no way to leave yet. SuperAdmin only.
|
|
53
|
+
*
|
|
54
|
+
* Refusals: `ORGANIZATION_MEMBERSHIP_CONFLICT` (409 `ConflictError`: the store
|
|
55
|
+
* belongs to another organization); `ORGANIZATION_NOT_FOUND` (no organization
|
|
56
|
+
* has that code) and `ORGANIZATION_MEMBER_STORE_NOT_FOUND` (no store has that
|
|
57
|
+
* id) (404 `NotFoundError`); `ORGANIZATION_MEMBER_NOT_A_STORE` (the platform's
|
|
58
|
+
* root channel or an organization's workspace) and
|
|
59
|
+
* `ORGANIZATION_MEMBER_STORE_CLOSED` (restore the store first) and
|
|
60
|
+
* `ORGANIZATION_TAX_MODE_MISMATCH` (the store's `pricesIncludeTax` differs
|
|
61
|
+
* from the organization's: every store of an organization enters prices the
|
|
62
|
+
* same way; judged on a first join only) (422
|
|
63
|
+
* `BusinessRuleError`); `ORGANIZATION_MEMBER_REGION_INVALID` (400
|
|
64
|
+
* `ValidationError`); `FORBIDDEN` (403) for a caller who is not a SuperAdmin.
|
|
65
|
+
*/
|
|
66
|
+
join(input: JoinOrganizationInput, call?: Call): Promise<OrganizationMembership>;
|
|
67
|
+
/**
|
|
68
|
+
* The organization's member stores, oldest member first. A store that no
|
|
69
|
+
* longer exists is left out. A SuperAdmin reads any organization's with no
|
|
70
|
+
* tenant; an organization admin reads its own with the workspace channel
|
|
71
|
+
* (`org-<code>`) as the tenant, and is refused elsewhere with a 403
|
|
72
|
+
* `PermissionDeniedError` (`FORBIDDEN`, or `ORGANIZATION_ACCESS_DENIED` for
|
|
73
|
+
* another organization). An unknown id is `ORGANIZATION_NOT_FOUND` (404
|
|
74
|
+
* `NotFoundError`), not an empty list.
|
|
75
|
+
*/
|
|
76
|
+
members(organizationId: string, call?: Call): Promise<OrganizationMembership[]>;
|
|
77
|
+
}
|
|
78
|
+
export declare const JOIN_ORGANIZATION_MUTATION = "\n mutation PlatformJoinOrganization($channelId: ID!, $input: JoinOrganizationInput!) {\n joinOrganization(channelId: $channelId, input: $input) { \n channelId\n channel {\n id\n code\n }\n organizationId\n region\n listedInRegionSwitcher\n joinedAt\n }\n }\n";
|
|
79
|
+
export declare const ORGANIZATION_MEMBERS_QUERY = "\n query PlatformOrganizationMembers($id: ID!) {\n organization(id: $id) {\n id\n members { \n channelId\n channel {\n id\n code\n }\n organizationId\n region\n listedInRegionSwitcher\n joinedAt\n }\n }\n }\n";
|
|
80
|
+
export declare function createOrganizationMembershipMethods(transport: Transport, config: AdminClientConfig): OrganizationMembershipMethods;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { NotFoundError } from '@sixtyninecommerce/core';
|
|
2
|
+
import { requireHumanSession } from './guard.js';
|
|
3
|
+
const MEMBERSHIP_FIELDS = /* GraphQL */ `
|
|
4
|
+
channelId
|
|
5
|
+
channel {
|
|
6
|
+
id
|
|
7
|
+
code
|
|
8
|
+
}
|
|
9
|
+
organizationId
|
|
10
|
+
region
|
|
11
|
+
listedInRegionSwitcher
|
|
12
|
+
joinedAt
|
|
13
|
+
`;
|
|
14
|
+
export const JOIN_ORGANIZATION_MUTATION = /* GraphQL */ `
|
|
15
|
+
mutation PlatformJoinOrganization($channelId: ID!, $input: JoinOrganizationInput!) {
|
|
16
|
+
joinOrganization(channelId: $channelId, input: $input) { ${MEMBERSHIP_FIELDS} }
|
|
17
|
+
}
|
|
18
|
+
`;
|
|
19
|
+
// Through `organization(id)` rather than a query of its own: the engine
|
|
20
|
+
// applies the organization reads' access rule there (a SuperAdmin, or the
|
|
21
|
+
// organization admin in its own workspace), and answers null for an unknown id.
|
|
22
|
+
export const ORGANIZATION_MEMBERS_QUERY = /* GraphQL */ `
|
|
23
|
+
query PlatformOrganizationMembers($id: ID!) {
|
|
24
|
+
organization(id: $id) {
|
|
25
|
+
id
|
|
26
|
+
members { ${MEMBERSHIP_FIELDS} }
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
`;
|
|
30
|
+
export function createOrganizationMembershipMethods(transport, config) {
|
|
31
|
+
// Platform-wide, as in `createPlatformOrganizationsModule`: the opt-in only
|
|
32
|
+
// matters when the scope is `none`, so an organization admin's workspace
|
|
33
|
+
// tenant still goes out.
|
|
34
|
+
const unscoped = (call) => ({ ...call, allowUnscoped: true });
|
|
35
|
+
return {
|
|
36
|
+
async join(input, call) {
|
|
37
|
+
requireHumanSession(config);
|
|
38
|
+
// The engine reads an ABSENT region as "keep the current one" and null as
|
|
39
|
+
// "clear it", so an omitted region must stay absent on the wire, never
|
|
40
|
+
// become null.
|
|
41
|
+
const joinInput = {
|
|
42
|
+
organizationCode: input.organizationCode,
|
|
43
|
+
};
|
|
44
|
+
if (input.region !== undefined)
|
|
45
|
+
joinInput.region = input.region;
|
|
46
|
+
// Only `true` goes on the wire: `false` means what an absent field means, and an engine
|
|
47
|
+
// older than the field refuses the whole document when it sees one it does not know.
|
|
48
|
+
if (input.keepOwnSettings === true)
|
|
49
|
+
joinInput.keepOwnSettings = true;
|
|
50
|
+
const data = await transport.graphql({
|
|
51
|
+
api: 'admin',
|
|
52
|
+
query: JOIN_ORGANIZATION_MUTATION,
|
|
53
|
+
variables: { channelId: input.channelId, input: joinInput },
|
|
54
|
+
}, unscoped(call));
|
|
55
|
+
return data.joinOrganization;
|
|
56
|
+
},
|
|
57
|
+
async members(organizationId, call) {
|
|
58
|
+
requireHumanSession(config);
|
|
59
|
+
const data = await transport.graphql({ api: 'admin', query: ORGANIZATION_MEMBERS_QUERY, variables: { id: organizationId } }, unscoped(call));
|
|
60
|
+
if (!data.organization) {
|
|
61
|
+
throw new NotFoundError({
|
|
62
|
+
code: 'ORGANIZATION_NOT_FOUND',
|
|
63
|
+
message: `No organization has the id ${organizationId}.`,
|
|
64
|
+
status: 404,
|
|
65
|
+
service: 'vendure',
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
return data.organization.members;
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { type Transport } from '@sixtyninecommerce/core';
|
|
2
|
+
import type { AdminClientConfig } from '../client.js';
|
|
3
|
+
import type { SettingsGroup } from '../tenant/settings-sources.js';
|
|
4
|
+
import type { Call } from '../types.js';
|
|
5
|
+
/** A member store that keeps its own values for one settings group. */
|
|
6
|
+
export interface OrganizationSettingsOverride {
|
|
7
|
+
/** The member store's channel id. */
|
|
8
|
+
channelId: string;
|
|
9
|
+
channelCode: string;
|
|
10
|
+
group: SettingsGroup | (string & {});
|
|
11
|
+
/**
|
|
12
|
+
* When the store started keeping its own values: ISO-8601 in UTC (`…Z`), exactly as the engine
|
|
13
|
+
* answers it. Compare instants with `Date.parse`; format it in the viewer's time zone only for
|
|
14
|
+
* display.
|
|
15
|
+
*/
|
|
16
|
+
overriddenAt: string;
|
|
17
|
+
}
|
|
18
|
+
/** The settings half of `admin.platform.organizations`. */
|
|
19
|
+
export interface OrganizationSettingsMethods {
|
|
20
|
+
/**
|
|
21
|
+
* The member stores keeping their own values, one entry per store and group, whether or not
|
|
22
|
+
* the organization shares that group today — what "Lock for all stores" would take away from
|
|
23
|
+
* them. Ordered by store code, then by group in catalog order; a store that no longer exists
|
|
24
|
+
* is left out. A SuperAdmin reads any organization's with no tenant; an organization admin
|
|
25
|
+
* reads its own with the workspace channel (`org-<code>`) as the tenant and is refused
|
|
26
|
+
* elsewhere with a 403 `PermissionDeniedError` (`FORBIDDEN`, or `ORGANIZATION_ACCESS_DENIED` for
|
|
27
|
+
* another organization). A caller who may read the organization but not manage it gets an
|
|
28
|
+
* empty list. An unknown id is `ORGANIZATION_NOT_FOUND` (404 `NotFoundError`).
|
|
29
|
+
*/
|
|
30
|
+
settingsOverrides(organizationId: string, call?: Call): Promise<OrganizationSettingsOverride[]>;
|
|
31
|
+
}
|
|
32
|
+
export declare const ORGANIZATION_SETTINGS_OVERRIDES_QUERY = "\n query PlatformOrganizationSettingsOverrides($id: ID!) {\n organization(id: $id) {\n id\n settingsOverrides {\n channelId\n channelCode\n group\n overriddenAt\n }\n }\n }\n";
|
|
33
|
+
export declare function createOrganizationSettingsMethods(transport: Transport, config: AdminClientConfig): OrganizationSettingsMethods;
|