@sixtyninecommerce/admin 0.10.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4347 -171
- package/dist/catalog/assets.d.ts +120 -8
- package/dist/catalog/assets.js +98 -12
- package/dist/catalog/collections.d.ts +279 -14
- package/dist/catalog/collections.js +246 -15
- package/dist/catalog/index.d.ts +9 -2
- package/dist/catalog/index.js +4 -0
- package/dist/catalog/products.d.ts +159 -19
- package/dist/catalog/products.js +88 -13
- package/dist/catalog/properties.d.ts +102 -14
- package/dist/catalog/properties.js +13 -0
- package/dist/catalog/recommendations.d.ts +261 -0
- package/dist/catalog/recommendations.js +145 -0
- package/dist/catalog/reviews.d.ts +368 -0
- package/dist/catalog/reviews.js +318 -0
- package/dist/catalog/types.d.ts +17 -1
- package/dist/catalog/types.js +10 -0
- package/dist/catalog/variant-find.d.ts +136 -0
- package/dist/catalog/variant-find.js +239 -0
- package/dist/catalog/variants.d.ts +233 -26
- package/dist/catalog/variants.js +125 -7
- package/dist/catalog/visibility.d.ts +20 -5
- package/dist/catalog/visibility.js +19 -1
- package/dist/client.d.ts +95 -9
- package/dist/client.js +35 -7
- package/dist/content/assets.d.ts +286 -0
- package/dist/content/assets.js +181 -0
- package/dist/content/items.d.ts +269 -0
- package/dist/content/items.js +170 -0
- package/dist/content/types.d.ts +282 -0
- package/dist/content/types.js +100 -0
- package/dist/customers/age-verification.d.ts +66 -0
- package/dist/customers/age-verification.js +79 -0
- package/dist/customers/credit.d.ts +263 -0
- package/dist/customers/credit.js +244 -0
- package/dist/customers/delivery-preferences.d.ts +120 -0
- package/dist/customers/delivery-preferences.js +97 -0
- package/dist/customers/groups.d.ts +207 -0
- package/dist/customers/groups.js +438 -0
- package/dist/customers/index.d.ts +447 -37
- package/dist/customers/index.js +313 -40
- package/dist/customers/invitation-status.d.ts +57 -0
- package/dist/customers/invitation-status.js +21 -0
- package/dist/customers/notes.d.ts +67 -0
- package/dist/customers/notes.js +51 -0
- package/dist/customers/notification-recipients.d.ts +97 -0
- package/dist/customers/notification-recipients.js +84 -0
- package/dist/customers/saved-lists.d.ts +42 -0
- package/dist/customers/saved-lists.js +61 -0
- package/dist/customers/vat.d.ts +83 -0
- package/dist/customers/vat.js +46 -0
- package/dist/customers/wire.d.ts +20 -0
- package/dist/customers/wire.js +35 -0
- package/dist/documents/ar.d.ts +307 -0
- package/dist/documents/ar.js +254 -0
- package/dist/documents/index.d.ts +731 -21
- package/dist/documents/index.js +436 -24
- package/dist/documents/pdf.d.ts +2 -0
- package/dist/documents/pdf.js +11 -0
- package/dist/fulfillments/create.d.ts +167 -0
- package/dist/fulfillments/create.js +216 -0
- package/dist/fulfillments/index.d.ts +44 -10
- package/dist/fulfillments/index.js +11 -2
- package/dist/fulfillments/payment-policy.d.ts +64 -0
- package/dist/fulfillments/payment-policy.js +140 -0
- package/dist/fulfillments/warehouse.d.ts +724 -0
- package/dist/fulfillments/warehouse.js +914 -0
- package/dist/geo/index.d.ts +119 -0
- package/dist/geo/index.js +107 -0
- package/dist/index.d.ts +123 -35
- package/dist/index.js +45 -12
- package/dist/integrations/afas-actions.d.ts +234 -0
- package/dist/integrations/afas-actions.js +310 -0
- package/dist/integrations/afas-types.d.ts +483 -0
- package/dist/integrations/afas-types.js +5 -0
- package/dist/integrations/afas.d.ts +234 -0
- package/dist/integrations/afas.js +290 -0
- package/dist/integrations/connections.d.ts +103 -0
- package/dist/integrations/connections.js +55 -0
- package/dist/integrations/esp.d.ts +313 -0
- package/dist/integrations/esp.js +169 -0
- package/dist/integrations/exact-online-types.d.ts +423 -0
- package/dist/integrations/exact-online-types.js +6 -0
- package/dist/integrations/exact-online.d.ts +248 -0
- package/dist/integrations/exact-online.js +376 -0
- package/dist/integrations/index.d.ts +29 -3
- package/dist/integrations/index.js +18 -3
- package/dist/integrations/pipeline-keys.d.ts +13 -0
- package/dist/integrations/pipeline-keys.js +22 -0
- package/dist/integrations/pipelines.d.ts +15 -14
- package/dist/integrations/pipelines.js +6 -22
- package/dist/integrations/types.d.ts +146 -0
- package/dist/locations/index.d.ts +66 -0
- package/dist/locations/index.js +31 -0
- package/dist/meta/index.d.ts +162 -0
- package/dist/orders/cancel-with-money.d.ts +227 -0
- package/dist/orders/cancel-with-money.js +153 -0
- package/dist/orders/commit-edit.d.ts +7 -1
- package/dist/orders/commit-edit.js +5 -1
- package/dist/orders/drafts.d.ts +800 -0
- package/dist/orders/drafts.js +771 -0
- package/dist/orders/fields.d.ts +245 -0
- package/dist/orders/fields.js +188 -0
- package/dist/orders/get.d.ts +29 -19
- package/dist/orders/get.js +17 -8
- package/dist/orders/index.d.ts +49 -9
- package/dist/orders/index.js +22 -2
- package/dist/orders/lifecycle.d.ts +128 -0
- package/dist/orders/lifecycle.js +158 -0
- package/dist/orders/line-price-preview.d.ts +7 -2
- package/dist/orders/list.d.ts +222 -0
- package/dist/orders/list.js +377 -0
- package/dist/orders/manual-pricing.d.ts +5 -0
- package/dist/orders/order-entry.d.ts +20 -7
- package/dist/orders/plan-edit.d.ts +1 -1
- package/dist/orders/plan-edit.js +4 -1
- package/dist/orders/settings.d.ts +201 -0
- package/dist/orders/settings.js +325 -0
- package/dist/orders/status.d.ts +159 -13
- package/dist/orders/status.js +21 -0
- package/dist/orders/surcharges.d.ts +8 -1
- package/dist/orders/surcharges.js +3 -2
- package/dist/payments/account-credit.d.ts +30 -0
- package/dist/payments/account-credit.js +19 -0
- package/dist/payments/ar.d.ts +292 -0
- package/dist/payments/ar.js +223 -0
- package/dist/payments/connected-account.d.ts +47 -0
- package/dist/payments/connected-account.js +61 -0
- package/dist/payments/credit-noted-fields.d.ts +12 -0
- package/dist/payments/credit-noted-fields.js +37 -0
- package/dist/payments/index.d.ts +257 -11
- package/dist/payments/index.js +131 -1
- package/dist/payments/methods.d.ts +303 -8
- package/dist/payments/methods.js +337 -7
- package/dist/payments/mollie-methods.d.ts +159 -0
- package/dist/payments/mollie-methods.js +141 -0
- package/dist/payments/payment-links.d.ts +180 -0
- package/dist/payments/payment-links.js +97 -0
- package/dist/payments/provider-keys.d.ts +150 -0
- package/dist/payments/provider-keys.js +118 -0
- package/dist/payments/providers.d.ts +86 -11
- package/dist/payments/providers.js +29 -0
- package/dist/payments/requests.d.ts +144 -0
- package/dist/payments/requests.js +92 -0
- package/dist/payments/store-credit-fields.d.ts +15 -0
- package/dist/payments/store-credit-fields.js +51 -0
- package/dist/pickups/index.d.ts +104 -0
- package/dist/pickups/index.js +105 -0
- package/dist/platform/channels.d.ts +132 -0
- package/dist/platform/channels.js +128 -0
- package/dist/platform/geo.d.ts +123 -0
- package/dist/platform/geo.js +144 -0
- package/dist/platform/tax.d.ts +154 -0
- package/dist/platform/tax.js +139 -0
- package/dist/pos/anomalies.d.ts +158 -0
- package/dist/pos/anomalies.js +64 -0
- package/dist/pos/assisted-selling.d.ts +79 -0
- package/dist/pos/assisted-selling.js +38 -0
- package/dist/pos/exchanges.d.ts +103 -0
- package/dist/pos/exchanges.js +22 -0
- package/dist/pos/gift-cards.d.ts +135 -0
- package/dist/pos/gift-cards.js +47 -0
- package/dist/pos/index.d.ts +2125 -32
- package/dist/pos/index.js +742 -4
- package/dist/pos/no-receipt.d.ts +94 -0
- package/dist/pos/no-receipt.js +25 -0
- package/dist/pos/offline-sales.d.ts +187 -0
- package/dist/pos/offline-sales.js +116 -0
- package/dist/pos/pay-by-link.d.ts +143 -0
- package/dist/pos/pay-by-link.js +57 -0
- package/dist/pos/quick-keys.d.ts +103 -0
- package/dist/pos/quick-keys.js +45 -0
- package/dist/pos/reports.d.ts +360 -0
- package/dist/pos/reports.js +62 -0
- package/dist/pos/returns.d.ts +217 -0
- package/dist/pos/returns.js +53 -0
- package/dist/pricing/bulk-coupons.d.ts +139 -0
- package/dist/pricing/bulk-coupons.js +112 -0
- package/dist/pricing/index.d.ts +174 -4
- package/dist/pricing/index.js +174 -1
- package/dist/pricing/price-lists.d.ts +604 -0
- package/dist/pricing/price-lists.js +600 -0
- package/dist/purchasing/alerts.d.ts +89 -0
- package/dist/purchasing/alerts.js +62 -0
- package/dist/purchasing/cost.d.ts +110 -0
- package/dist/purchasing/cost.js +86 -0
- package/dist/purchasing/index.d.ts +36 -0
- package/dist/purchasing/index.js +16 -0
- package/dist/purchasing/purchase-orders.d.ts +548 -0
- package/dist/purchasing/purchase-orders.js +491 -0
- package/dist/purchasing/replenishment.d.ts +281 -0
- package/dist/purchasing/replenishment.js +240 -0
- package/dist/purchasing/settings.d.ts +152 -0
- package/dist/purchasing/settings.js +181 -0
- package/dist/purchasing/suppliers.d.ts +476 -0
- package/dist/purchasing/suppliers.js +438 -0
- package/dist/quotes/index.d.ts +501 -0
- package/dist/quotes/index.js +439 -0
- package/dist/reports/index.d.ts +71 -8
- package/dist/reports/index.js +74 -11
- package/dist/returns/index.d.ts +614 -21
- package/dist/returns/index.js +695 -11
- package/dist/search/index.d.ts +76 -1
- package/dist/search/index.js +63 -0
- package/dist/shipping/definitions.d.ts +29 -0
- package/dist/shipping/definitions.js +47 -0
- package/dist/shipping/fulfillment-profiles.d.ts +174 -0
- package/dist/shipping/fulfillment-profiles.js +104 -0
- package/dist/shipping/hendriks.d.ts +97 -0
- package/dist/shipping/hendriks.js +50 -0
- package/dist/shipping/index.d.ts +48 -0
- package/dist/shipping/index.js +25 -0
- package/dist/shipping/methods.d.ts +198 -0
- package/dist/shipping/methods.js +240 -0
- package/dist/shipping/myparcel.d.ts +254 -0
- package/dist/shipping/myparcel.js +178 -0
- package/dist/shipping/parcel-count-suggestion.d.ts +2 -0
- package/dist/shipping/parcel-rule.d.ts +6 -5
- package/dist/shipping/parcel-rule.js +21 -23
- package/dist/shipping/rate-tables.d.ts +179 -0
- package/dist/shipping/rate-tables.js +184 -0
- package/dist/stock/index.d.ts +10 -3
- package/dist/stock/index.js +3 -0
- package/dist/stock/locations.d.ts +144 -0
- package/dist/stock/locations.js +168 -0
- package/dist/stock/transfers.d.ts +216 -43
- package/dist/stock/transfers.js +140 -13
- package/dist/stored-value/index.d.ts +214 -4
- package/dist/stored-value/index.js +152 -0
- package/dist/subscriptions/index.d.ts +682 -0
- package/dist/subscriptions/index.js +815 -0
- package/dist/tax/index.d.ts +76 -1
- package/dist/tax/index.js +50 -0
- package/dist/tenant/email-style.d.ts +102 -0
- package/dist/tenant/email-style.js +72 -0
- package/dist/tenant/email-templates.d.ts +66 -0
- package/dist/tenant/email-templates.js +54 -0
- package/dist/tenant/notifications.d.ts +23 -17
- package/dist/tenant/settings-sources.d.ts +1 -1
- package/dist/tenant/time-zone.d.ts +17 -0
- package/dist/tenant/time-zone.js +11 -0
- package/dist/types.d.ts +50 -3
- package/dist/users/index.d.ts +3 -0
- package/dist/users/index.js +2 -0
- package/dist/users/warehouses.d.ts +50 -0
- package/dist/users/warehouses.js +51 -0
- package/package.json +2 -2
package/dist/tax/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type Page, type Transport } from '@sixtyninecommerce/core';
|
|
2
|
-
import type { Call } from '../types.js';
|
|
2
|
+
import type { Call, ListOptions } from '../types.js';
|
|
3
3
|
/**
|
|
4
4
|
* The mapping used when no mapping names the country the money is taxed in.
|
|
5
5
|
*
|
|
@@ -115,10 +115,85 @@ export interface TaxMappingsModule {
|
|
|
115
115
|
/** Drop one country's mapping. Silent when there was none. */
|
|
116
116
|
remove(key: string, countryCode: string, options?: Call): Promise<TaxClass>;
|
|
117
117
|
}
|
|
118
|
+
/**
|
|
119
|
+
* A platform-wide tax category (`Standard`, `Reduced`, `Zero`): what a tax class maps a country
|
|
120
|
+
* onto, and what a tax rate prices in a zone. Shared by every store.
|
|
121
|
+
*/
|
|
122
|
+
export interface TaxCategory {
|
|
123
|
+
id: string;
|
|
124
|
+
name: string;
|
|
125
|
+
/** The category the platform's own tax setup treats as the default. */
|
|
126
|
+
isDefault: boolean;
|
|
127
|
+
createdAt: string;
|
|
128
|
+
updatedAt: string;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* A platform-wide tax rate: the percentage a category is taxed at in a zone, optionally only for
|
|
132
|
+
* one customer group.
|
|
133
|
+
*/
|
|
134
|
+
export interface TaxRate {
|
|
135
|
+
id: string;
|
|
136
|
+
name: string;
|
|
137
|
+
/** A percentage: `21` is 21 %. Not money. */
|
|
138
|
+
value: number;
|
|
139
|
+
/** A disabled rate is never applied. */
|
|
140
|
+
enabled: boolean;
|
|
141
|
+
category: {
|
|
142
|
+
id: string;
|
|
143
|
+
name: string;
|
|
144
|
+
};
|
|
145
|
+
zone: {
|
|
146
|
+
id: string;
|
|
147
|
+
name: string;
|
|
148
|
+
};
|
|
149
|
+
/** Null for a rate that applies to every customer. */
|
|
150
|
+
customerGroup: {
|
|
151
|
+
id: string;
|
|
152
|
+
name: string;
|
|
153
|
+
} | null;
|
|
154
|
+
createdAt: string;
|
|
155
|
+
updatedAt: string;
|
|
156
|
+
}
|
|
157
|
+
/** The platform's tax categories (read-only here; a SuperAdmin writes them through `platform.tax.categories`). */
|
|
158
|
+
export interface TaxCategoriesModule {
|
|
159
|
+
/**
|
|
160
|
+
* Every tax category, in id order. Reading needs `ReadTaxCategory`, which every store role holds.
|
|
161
|
+
*
|
|
162
|
+
* A `Page`: walk it (`for await`, or `.next()` until null) for the COMPLETE list. Nothing is cut
|
|
163
|
+
* at a fixed size; every following page keeps the same order and per-call options.
|
|
164
|
+
*
|
|
165
|
+
* @effect read
|
|
166
|
+
*/
|
|
167
|
+
list(options?: ListOptions): Promise<Page<TaxCategory>>;
|
|
168
|
+
}
|
|
169
|
+
/** The platform's tax rates (read-only here; a SuperAdmin writes them through `platform.tax.rates`). */
|
|
170
|
+
export interface TaxRatesModule {
|
|
171
|
+
/**
|
|
172
|
+
* Every tax rate, in id order, with its category, zone and customer group. Reading needs
|
|
173
|
+
* `ReadTaxRate`, which every store role holds.
|
|
174
|
+
*
|
|
175
|
+
* A `Page`: walk it (`for await`, or `.next()` until null) for the COMPLETE list. Nothing is cut
|
|
176
|
+
* at a fixed size (the dashboard used to stop at 100); every following page keeps the same
|
|
177
|
+
* order and per-call options.
|
|
178
|
+
*
|
|
179
|
+
* @effect read
|
|
180
|
+
*/
|
|
181
|
+
list(options?: ListOptions): Promise<Page<TaxRate>>;
|
|
182
|
+
}
|
|
118
183
|
export interface TaxModule {
|
|
119
184
|
classes: TaxClassesModule;
|
|
120
185
|
mappings: TaxMappingsModule;
|
|
186
|
+
/** The platform-wide tax categories every store shares. */
|
|
187
|
+
categories: TaxCategoriesModule;
|
|
188
|
+
/** The platform-wide tax rates every store shares. */
|
|
189
|
+
rates: TaxRatesModule;
|
|
121
190
|
}
|
|
191
|
+
/** A {@link TaxCategory} as every read and write answers it (`platform.tax` reuses it). */
|
|
192
|
+
export declare const TAX_CATEGORY_FIELDS = "\n id\n name\n isDefault\n createdAt\n updatedAt\n";
|
|
193
|
+
/** A {@link TaxRate} as every read and write answers it (`platform.tax` reuses it). */
|
|
194
|
+
export declare const TAX_RATE_FIELDS = "\n id\n name\n value\n enabled\n category { id name }\n zone { id name }\n customerGroup { id name }\n createdAt\n updatedAt\n";
|
|
195
|
+
export declare const TAX_CATEGORIES_QUERY = "\n query TaxCategories($options: TaxCategoryListOptions) {\n taxCategories(options: $options) {\n items { \n id\n name\n isDefault\n createdAt\n updatedAt\n }\n totalItems\n }\n }\n";
|
|
196
|
+
export declare const TAX_RATES_QUERY = "\n query TaxRates($options: TaxRateListOptions) {\n taxRates(options: $options) {\n items { \n id\n name\n value\n enabled\n category { id name }\n zone { id name }\n customerGroup { id name }\n createdAt\n updatedAt\n }\n totalItems\n }\n }\n";
|
|
122
197
|
export declare const TAX_CLASSES_QUERY = "\n query TaxClasses {\n taxClasses { \n id\n key\n label\n mappings {\n countryCode\n taxCategoryId\n taxCategory {\n id\n name\n isDefault\n }\n }\n }\n }\n";
|
|
123
198
|
export declare const TAX_CLASS_QUERY = "\n query TaxClass($key: String!) {\n taxClass(key: $key) { \n id\n key\n label\n mappings {\n countryCode\n taxCategoryId\n taxCategory {\n id\n name\n isDefault\n }\n }\n }\n }\n";
|
|
124
199
|
export declare const UPSERT_TAX_CLASS_MUTATION = "\n mutation UpsertTaxClass($input: UpsertTaxClassInput!) {\n upsertTaxClass(input: $input) { \n id\n key\n label\n mappings {\n countryCode\n taxCategoryId\n taxCategory {\n id\n name\n isDefault\n }\n }\n }\n }\n";
|
package/dist/tax/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createPage } from '@sixtyninecommerce/core';
|
|
2
|
+
import { vendureListPage } from '../catalog/types.js';
|
|
2
3
|
/**
|
|
3
4
|
* The mapping used when no mapping names the country the money is taxed in.
|
|
4
5
|
*
|
|
@@ -8,6 +9,49 @@ import { createPage } from '@sixtyninecommerce/core';
|
|
|
8
9
|
* the same one read two ways.
|
|
9
10
|
*/
|
|
10
11
|
export const TAX_CLASS_FALLBACK_COUNTRY = '*';
|
|
12
|
+
/** A {@link TaxCategory} as every read and write answers it (`platform.tax` reuses it). */
|
|
13
|
+
export const TAX_CATEGORY_FIELDS = /* GraphQL */ `
|
|
14
|
+
id
|
|
15
|
+
name
|
|
16
|
+
isDefault
|
|
17
|
+
createdAt
|
|
18
|
+
updatedAt
|
|
19
|
+
`;
|
|
20
|
+
/** A {@link TaxRate} as every read and write answers it (`platform.tax` reuses it). */
|
|
21
|
+
export const TAX_RATE_FIELDS = /* GraphQL */ `
|
|
22
|
+
id
|
|
23
|
+
name
|
|
24
|
+
value
|
|
25
|
+
enabled
|
|
26
|
+
category { id name }
|
|
27
|
+
zone { id name }
|
|
28
|
+
customerGroup { id name }
|
|
29
|
+
createdAt
|
|
30
|
+
updatedAt
|
|
31
|
+
`;
|
|
32
|
+
export const TAX_CATEGORIES_QUERY = /* GraphQL */ `
|
|
33
|
+
query TaxCategories($options: TaxCategoryListOptions) {
|
|
34
|
+
taxCategories(options: $options) {
|
|
35
|
+
items { ${TAX_CATEGORY_FIELDS} }
|
|
36
|
+
totalItems
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
`;
|
|
40
|
+
export const TAX_RATES_QUERY = /* GraphQL */ `
|
|
41
|
+
query TaxRates($options: TaxRateListOptions) {
|
|
42
|
+
taxRates(options: $options) {
|
|
43
|
+
items { ${TAX_RATE_FIELDS} }
|
|
44
|
+
totalItems
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
`;
|
|
48
|
+
/**
|
|
49
|
+
* Vendure's list queries add no ORDER BY of their own, and `skip/take` over an unordered query
|
|
50
|
+
* can repeat or skip rows between two requests. Every list here is ordered by id, the one unique
|
|
51
|
+
* key a sort can name (a GraphQL sort input applies its fields in the SCHEMA's order, where `id`
|
|
52
|
+
* comes first, so `{ name, id }` is id order too), so a walk answers each row exactly once.
|
|
53
|
+
*/
|
|
54
|
+
const BY_ID = { id: 'ASC' };
|
|
11
55
|
const TAX_CLASS_FIELDS = /* GraphQL */ `
|
|
12
56
|
id
|
|
13
57
|
key
|
|
@@ -79,6 +123,12 @@ export function createTaxModule(transport) {
|
|
|
79
123
|
return data.deleteTaxClass;
|
|
80
124
|
},
|
|
81
125
|
},
|
|
126
|
+
categories: {
|
|
127
|
+
list: (options) => vendureListPage(transport, TAX_CATEGORIES_QUERY, 'taxCategories', (skip, take) => ({ options: { skip, take, sort: BY_ID } }), options),
|
|
128
|
+
},
|
|
129
|
+
rates: {
|
|
130
|
+
list: (options) => vendureListPage(transport, TAX_RATES_QUERY, 'taxRates', (skip, take) => ({ options: { skip, take, sort: BY_ID } }), options),
|
|
131
|
+
},
|
|
82
132
|
mappings: {
|
|
83
133
|
async set(key, countryCode, taxCategoryId, options) {
|
|
84
134
|
const data = await transport.graphql({
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import type { Transport } from '@sixtyninecommerce/core';
|
|
2
|
+
import type { Call } from '../types.js';
|
|
3
|
+
/**
|
|
4
|
+
* The look of every email (and the PDF colours) the active store sends, as the engine applies it.
|
|
5
|
+
*
|
|
6
|
+
* The four choice fields name a member of a fixed set, which the engine checks on `set`:
|
|
7
|
+
* `fontFamily` `system` | `georgia` | `arial` | `verdana`; `logoPosition` `center` | `left`;
|
|
8
|
+
* `logoVariant` `light` | `dark` | `none` (which Business profile logo heads the email);
|
|
9
|
+
* `buttonStyle` `filled` | `outline`. They are typed `string` because a store can hold a value
|
|
10
|
+
* from before that check, which `get` answers as stored and `set` accepts back unchanged.
|
|
11
|
+
*/
|
|
12
|
+
export interface EmailStyle {
|
|
13
|
+
/** The active store. The style read is always the active store's, also while it inherits. */
|
|
14
|
+
channelId: string;
|
|
15
|
+
/**
|
|
16
|
+
* Buttons and links. Each colour is a hex (`#1a2b3c`, 3 to 8 digits), `rgb(r, g, b)`, a colour
|
|
17
|
+
* name or `transparent`.
|
|
18
|
+
*/
|
|
19
|
+
accentColor: string;
|
|
20
|
+
/** The outer background; `transparent` lets a mail client's dark mode show through. */
|
|
21
|
+
backgroundColor: string;
|
|
22
|
+
/** The content area's background. */
|
|
23
|
+
contentBgColor: string;
|
|
24
|
+
textColor: string;
|
|
25
|
+
mutedTextColor: string;
|
|
26
|
+
fontFamily: string;
|
|
27
|
+
/** Pixels. */
|
|
28
|
+
headingFontSize: number;
|
|
29
|
+
/** Pixels. */
|
|
30
|
+
bodyFontSize: number;
|
|
31
|
+
logoPosition: string;
|
|
32
|
+
/** Pixels. */
|
|
33
|
+
logoMaxWidth: number;
|
|
34
|
+
logoVariant: string;
|
|
35
|
+
/** A button's corner radius, in pixels. */
|
|
36
|
+
borderRadius: number;
|
|
37
|
+
/** The content column's width, in pixels. */
|
|
38
|
+
contentWidth: number;
|
|
39
|
+
buttonStyle: string;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* The style fields a `set` changes; an omitted field keeps its value. The sizes are whole pixels
|
|
43
|
+
* (GraphQL `Int`). `channelId` is not an input: `set` leaves it out, so `set({ ...style, … })`
|
|
44
|
+
* with a style `get` answered works.
|
|
45
|
+
*/
|
|
46
|
+
export type EmailStylePatch = Partial<Omit<EmailStyle, 'channelId'>>;
|
|
47
|
+
/**
|
|
48
|
+
* `admin.tenant.emailStyle` — the active store's email style (part of the `settings.email` group).
|
|
49
|
+
* Always the ACTIVE channel; no channel argument.
|
|
50
|
+
*
|
|
51
|
+
* **A store that takes `settings.email` from its organization** (`admin.tenant.settingsSources`:
|
|
52
|
+
* locked, or inheritable and not overridden) sends its organization's style, and `get` answers
|
|
53
|
+
* that one. `set` then refuses any change; sending back what `get` answered is accepted and
|
|
54
|
+
* changes nothing. `admin.tenant.settingsSources.override('settings.email')` first to keep a style
|
|
55
|
+
* of the store's own.
|
|
56
|
+
*/
|
|
57
|
+
export interface EmailStyleModule {
|
|
58
|
+
/**
|
|
59
|
+
* The style the store's emails use: its own, its organization's while it inherits, or the
|
|
60
|
+
* platform's defaults while it has saved none. Needs `ReadSettings`.
|
|
61
|
+
*
|
|
62
|
+
* @effect read
|
|
63
|
+
* @permission ReadSettings
|
|
64
|
+
*/
|
|
65
|
+
get(options?: Call): Promise<EmailStyle>;
|
|
66
|
+
/**
|
|
67
|
+
* Change the fields `patch` names, and answer the whole style as the engine now stores it. The
|
|
68
|
+
* next email the store sends uses it; emails already sent keep theirs.
|
|
69
|
+
*
|
|
70
|
+
* **Coded refusals:**
|
|
71
|
+
* - `EMAIL_STYLE_COLOR_INVALID` → `ValidationError` (400): a colour that is not a hex, `rgb(…)`,
|
|
72
|
+
* colour name or `transparent`. The message names the field.
|
|
73
|
+
* - `EMAIL_STYLE_FONT_FAMILY_INVALID`, `EMAIL_STYLE_LOGO_POSITION_INVALID`,
|
|
74
|
+
* `EMAIL_STYLE_BUTTON_STYLE_INVALID`, `EMAIL_LOGO_VARIANT_INVALID` → `ValidationError` (400):
|
|
75
|
+
* a choice outside its set (see {@link EmailStyle}). Sending back the value the store holds
|
|
76
|
+
* passes, even one from before the check.
|
|
77
|
+
* - `BAD_USER_INPUT` → `ValidationError` (400): a key that is not a style field, or a value of
|
|
78
|
+
* the wrong type (a size that is not a whole number). The message names it.
|
|
79
|
+
* - `MANAGED_BY_ORGANIZATION` → `BusinessRuleError` (422): a changed field while the
|
|
80
|
+
* organization locks `settings.email`.
|
|
81
|
+
* - `ORGANIZATION_SETTINGS_INHERITED` → `BusinessRuleError` (422): a changed field while the
|
|
82
|
+
* store inherits `settings.email`; `tenant.settingsSources.override` it first.
|
|
83
|
+
* - `FORBIDDEN` → `PermissionDeniedError` (403): the credential lacks `UpdateSettings`.
|
|
84
|
+
*
|
|
85
|
+
* The sizes have no engine bounds yet; a UI should keep its own.
|
|
86
|
+
*
|
|
87
|
+
* @effect write
|
|
88
|
+
* @permission UpdateSettings
|
|
89
|
+
*/
|
|
90
|
+
set(patch: EmailStylePatch, options?: Call): Promise<EmailStyle>;
|
|
91
|
+
}
|
|
92
|
+
/** The style fields, in the order the engine's input lists them. */
|
|
93
|
+
export declare const EMAIL_STYLE_FIELDS = "\n channelId\n accentColor\n backgroundColor\n contentBgColor\n textColor\n mutedTextColor\n fontFamily\n headingFontSize\n bodyFontSize\n logoPosition\n logoMaxWidth\n logoVariant\n borderRadius\n contentWidth\n buttonStyle\n";
|
|
94
|
+
export declare const EMAIL_STYLE_QUERY = "\n query EmailStyle {\n emailStyleConfig { \n channelId\n accentColor\n backgroundColor\n contentBgColor\n textColor\n mutedTextColor\n fontFamily\n headingFontSize\n bodyFontSize\n logoPosition\n logoMaxWidth\n logoVariant\n borderRadius\n contentWidth\n buttonStyle\n }\n }\n";
|
|
95
|
+
export declare const SET_EMAIL_STYLE_MUTATION = "\n mutation SetEmailStyle($input: UpdateEmailStyleConfigInput!) {\n updateEmailStyleConfig(input: $input) { \n channelId\n accentColor\n backgroundColor\n contentBgColor\n textColor\n mutedTextColor\n fontFamily\n headingFontSize\n bodyFontSize\n logoPosition\n logoMaxWidth\n logoVariant\n borderRadius\n contentWidth\n buttonStyle\n }\n }\n";
|
|
96
|
+
/**
|
|
97
|
+
* The patch as the engine's `UpdateEmailStyleConfigInput` takes it. Every other key goes through
|
|
98
|
+
* as given, so the engine refuses a misspelt or unknown one (`BAD_USER_INPUT`) instead of the SDK
|
|
99
|
+
* dropping it and answering success; `channelId` alone is left out (see {@link EmailStylePatch}).
|
|
100
|
+
*/
|
|
101
|
+
export declare function emailStyleInputOf(patch: EmailStylePatch): Record<string, unknown>;
|
|
102
|
+
export declare function createEmailStyleModule(transport: Transport): EmailStyleModule;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/** The style fields, in the order the engine's input lists them. */
|
|
2
|
+
export const EMAIL_STYLE_FIELDS = /* GraphQL */ `
|
|
3
|
+
channelId
|
|
4
|
+
accentColor
|
|
5
|
+
backgroundColor
|
|
6
|
+
contentBgColor
|
|
7
|
+
textColor
|
|
8
|
+
mutedTextColor
|
|
9
|
+
fontFamily
|
|
10
|
+
headingFontSize
|
|
11
|
+
bodyFontSize
|
|
12
|
+
logoPosition
|
|
13
|
+
logoMaxWidth
|
|
14
|
+
logoVariant
|
|
15
|
+
borderRadius
|
|
16
|
+
contentWidth
|
|
17
|
+
buttonStyle
|
|
18
|
+
`;
|
|
19
|
+
export const EMAIL_STYLE_QUERY = /* GraphQL */ `
|
|
20
|
+
query EmailStyle {
|
|
21
|
+
emailStyleConfig { ${EMAIL_STYLE_FIELDS} }
|
|
22
|
+
}
|
|
23
|
+
`;
|
|
24
|
+
export const SET_EMAIL_STYLE_MUTATION = /* GraphQL */ `
|
|
25
|
+
mutation SetEmailStyle($input: UpdateEmailStyleConfigInput!) {
|
|
26
|
+
updateEmailStyleConfig(input: $input) { ${EMAIL_STYLE_FIELDS} }
|
|
27
|
+
}
|
|
28
|
+
`;
|
|
29
|
+
function fromWire(wire) {
|
|
30
|
+
return {
|
|
31
|
+
channelId: String(wire.channelId),
|
|
32
|
+
accentColor: wire.accentColor,
|
|
33
|
+
backgroundColor: wire.backgroundColor,
|
|
34
|
+
contentBgColor: wire.contentBgColor,
|
|
35
|
+
textColor: wire.textColor,
|
|
36
|
+
mutedTextColor: wire.mutedTextColor,
|
|
37
|
+
fontFamily: wire.fontFamily,
|
|
38
|
+
headingFontSize: wire.headingFontSize,
|
|
39
|
+
bodyFontSize: wire.bodyFontSize,
|
|
40
|
+
logoPosition: wire.logoPosition,
|
|
41
|
+
logoMaxWidth: wire.logoMaxWidth,
|
|
42
|
+
logoVariant: wire.logoVariant,
|
|
43
|
+
borderRadius: wire.borderRadius,
|
|
44
|
+
contentWidth: wire.contentWidth,
|
|
45
|
+
buttonStyle: wire.buttonStyle,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* The patch as the engine's `UpdateEmailStyleConfigInput` takes it. Every other key goes through
|
|
50
|
+
* as given, so the engine refuses a misspelt or unknown one (`BAD_USER_INPUT`) instead of the SDK
|
|
51
|
+
* dropping it and answering success; `channelId` alone is left out (see {@link EmailStylePatch}).
|
|
52
|
+
*/
|
|
53
|
+
export function emailStyleInputOf(patch) {
|
|
54
|
+
const { channelId: _readOnly, ...input } = patch;
|
|
55
|
+
return input;
|
|
56
|
+
}
|
|
57
|
+
export function createEmailStyleModule(transport) {
|
|
58
|
+
return {
|
|
59
|
+
async get(options) {
|
|
60
|
+
const data = await transport.graphql({ api: 'admin', query: EMAIL_STYLE_QUERY }, options);
|
|
61
|
+
return fromWire(data.emailStyleConfig);
|
|
62
|
+
},
|
|
63
|
+
async set(patch, options) {
|
|
64
|
+
const data = await transport.graphql({
|
|
65
|
+
api: 'admin',
|
|
66
|
+
query: SET_EMAIL_STYLE_MUTATION,
|
|
67
|
+
variables: { input: emailStyleInputOf(patch) },
|
|
68
|
+
}, options);
|
|
69
|
+
return fromWire(data.updateEmailStyleConfig);
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { type Transport } from '@sixtyninecommerce/core';
|
|
2
|
+
import type { Call } from '../types.js';
|
|
3
|
+
import { type EmailStylePatch } from './email-style.js';
|
|
4
|
+
/** One template rendered as the store would send it, filled from the store's own records. */
|
|
5
|
+
export interface EmailPreview {
|
|
6
|
+
subject: string;
|
|
7
|
+
/** The whole HTML document, ready for an `<iframe srcdoc>`. */
|
|
8
|
+
html: string;
|
|
9
|
+
/** Each recipient field resolved for the sample, as one comma-separated list ('' for none). */
|
|
10
|
+
recipients: {
|
|
11
|
+
to: string;
|
|
12
|
+
cc: string;
|
|
13
|
+
bcc: string;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* `true` when the credential's customer-data level withheld part of the answer (an app
|
|
17
|
+
* installation below `customerData: full`): `subject` and `html` then read `''`, and so do the
|
|
18
|
+
* recipients below `identity`. Absent otherwise.
|
|
19
|
+
*/
|
|
20
|
+
redacted?: true;
|
|
21
|
+
}
|
|
22
|
+
export interface EmailPreviewOptions extends Call {
|
|
23
|
+
/** The language to render the template's texts and subject in, e.g. `nl`. */
|
|
24
|
+
languageCode: string;
|
|
25
|
+
/**
|
|
26
|
+
* Style fields to preview instead of the stored ones, e.g. a form's unsaved values. Nothing is
|
|
27
|
+
* saved. A value `tenant.emailStyle.set` would refuse is not refused here: the renderer draws it
|
|
28
|
+
* with its own fallback.
|
|
29
|
+
*/
|
|
30
|
+
style?: EmailStylePatch;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* `admin.tenant.emailTemplates` — the active store's email templates (part of the `settings.email`
|
|
34
|
+
* group). Always the ACTIVE channel; no channel argument.
|
|
35
|
+
*
|
|
36
|
+
* Templates are addressed by TYPE (`order_confirmation`, `shipping_confirmation`, …): a template's
|
|
37
|
+
* id changes when the store starts or stops taking `settings.email` from its organization, its
|
|
38
|
+
* type never does. While the store inherits, every read answers the organization's template.
|
|
39
|
+
*/
|
|
40
|
+
export interface EmailTemplatesModule {
|
|
41
|
+
/**
|
|
42
|
+
* Render the template of `type` as the store would send it now, optionally with unsaved style
|
|
43
|
+
* changes. Two requests: the template the store uses for `type` is looked up first.
|
|
44
|
+
*
|
|
45
|
+
* **The answer is customer data.** The sample is filled from the store's LATEST REAL order and
|
|
46
|
+
* customer (synthetic values only in a store with none), so `subject`, `html` and the
|
|
47
|
+
* recipients carry a real buyer's name, email and addresses. Do not log or forward them; a
|
|
48
|
+
* credential below `customerData: full` gets them withheld ({@link EmailPreview.redacted}).
|
|
49
|
+
*
|
|
50
|
+
* For a store on its own settings, the first look-up of a known type the store has no template
|
|
51
|
+
* of yet creates it from the platform's default (as the dashboard's editor does).
|
|
52
|
+
*
|
|
53
|
+
* **Coded refusals:**
|
|
54
|
+
* - `EMAIL_TEMPLATE_NOT_FOUND` → `NotFoundError` (404): no template of `type` (an unknown type).
|
|
55
|
+
* - `ENTITY_NOT_FOUND` → `NotFoundError` (404): the template changed between the two requests
|
|
56
|
+
* (the store's `settings.email` source moved); ask again.
|
|
57
|
+
* - `FORBIDDEN` → `PermissionDeniedError` (403): the credential lacks `ReadSettings`.
|
|
58
|
+
*
|
|
59
|
+
* @effect read
|
|
60
|
+
* @permission ReadSettings
|
|
61
|
+
*/
|
|
62
|
+
preview(type: string, options: EmailPreviewOptions): Promise<EmailPreview>;
|
|
63
|
+
}
|
|
64
|
+
export declare const EMAIL_TEMPLATE_ID_BY_TYPE_QUERY = "\n query EmailTemplateIdByType($type: String!) {\n emailTemplateByType(type: $type) { id }\n }\n";
|
|
65
|
+
export declare const EMAIL_PREVIEW_QUERY = "\n query EmailPreview(\n $templateId: ID!\n $languageCode: String!\n $styleOverrides: UpdateEmailStyleConfigInput\n ) {\n renderEmailPreview(\n templateId: $templateId\n languageCode: $languageCode\n styleOverrides: $styleOverrides\n ) {\n subject\n html\n recipientTo\n recipientCc\n recipientBcc\n }\n }\n";
|
|
66
|
+
export declare function createEmailTemplatesModule(transport: Transport): EmailTemplatesModule;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { NotFoundError } from '@sixtyninecommerce/core';
|
|
2
|
+
import { customerDataField } from '../customers/wire.js';
|
|
3
|
+
import { emailStyleInputOf } from './email-style.js';
|
|
4
|
+
export const EMAIL_TEMPLATE_ID_BY_TYPE_QUERY = /* GraphQL */ `
|
|
5
|
+
query EmailTemplateIdByType($type: String!) {
|
|
6
|
+
emailTemplateByType(type: $type) { id }
|
|
7
|
+
}
|
|
8
|
+
`;
|
|
9
|
+
export const EMAIL_PREVIEW_QUERY = /* GraphQL */ `
|
|
10
|
+
query EmailPreview(
|
|
11
|
+
$templateId: ID!
|
|
12
|
+
$languageCode: String!
|
|
13
|
+
$styleOverrides: UpdateEmailStyleConfigInput
|
|
14
|
+
) {
|
|
15
|
+
renderEmailPreview(
|
|
16
|
+
templateId: $templateId
|
|
17
|
+
languageCode: $languageCode
|
|
18
|
+
styleOverrides: $styleOverrides
|
|
19
|
+
) {
|
|
20
|
+
subject
|
|
21
|
+
html
|
|
22
|
+
recipientTo
|
|
23
|
+
recipientCc
|
|
24
|
+
recipientBcc
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
`;
|
|
28
|
+
export function createEmailTemplatesModule(transport) {
|
|
29
|
+
return {
|
|
30
|
+
async preview(type, options) {
|
|
31
|
+
const { languageCode, style, ...call } = options;
|
|
32
|
+
const found = await transport.graphql({ api: 'admin', query: EMAIL_TEMPLATE_ID_BY_TYPE_QUERY, variables: { type } }, call);
|
|
33
|
+
const templateId = found.emailTemplateByType?.id;
|
|
34
|
+
if (!templateId) {
|
|
35
|
+
throw new NotFoundError({
|
|
36
|
+
code: 'EMAIL_TEMPLATE_NOT_FOUND',
|
|
37
|
+
message: `The store has no email template of type "${type}".`,
|
|
38
|
+
status: 404,
|
|
39
|
+
service: 'vendure',
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
const { value, mark } = await customerDataField(transport, EMAIL_PREVIEW_QUERY, 'renderEmailPreview', {
|
|
43
|
+
templateId,
|
|
44
|
+
languageCode,
|
|
45
|
+
...(style !== undefined ? { styleOverrides: emailStyleInputOf(style) } : {}),
|
|
46
|
+
}, call);
|
|
47
|
+
return mark({
|
|
48
|
+
subject: value.subject,
|
|
49
|
+
html: value.html,
|
|
50
|
+
recipients: { to: value.recipientTo, cc: value.recipientCc, bcc: value.recipientBcc },
|
|
51
|
+
});
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
}
|
|
@@ -38,8 +38,8 @@ export interface NotificationEventConfig {
|
|
|
38
38
|
event: string;
|
|
39
39
|
/**
|
|
40
40
|
* `false` switches the email off altogether; nothing is logged for it. Always
|
|
41
|
-
* `true` for `password_reset`, `email_change
|
|
42
|
-
* cannot be switched off.
|
|
41
|
+
* `true` for `password_reset`, `email_change`, `account_setup` and
|
|
42
|
+
* `gift_card_issued`, which cannot be switched off.
|
|
43
43
|
*/
|
|
44
44
|
enabled: boolean;
|
|
45
45
|
sender: NotificationSender;
|
|
@@ -60,20 +60,23 @@ export interface NotificationEventConfig {
|
|
|
60
60
|
export interface UpdateNotificationEventConfigInput {
|
|
61
61
|
/**
|
|
62
62
|
* `false` is refused with `EVENT_ALWAYS_ENABLED` ({@link ValidationError}) on
|
|
63
|
-
* `password_reset`, `email_change` and `
|
|
64
|
-
* a customer resets a password (or sets the first one
|
|
65
|
-
* account), confirms a new address,
|
|
66
|
-
*
|
|
67
|
-
*
|
|
63
|
+
* `password_reset`, `email_change`, `account_setup` and `gift_card_issued`:
|
|
64
|
+
* each is the only way a customer resets a password (or sets the first one
|
|
65
|
+
* on an imported account), confirms a new address, finishes a storefront
|
|
66
|
+
* registration, or receives the code of a gift card they paid for (the
|
|
67
|
+
* platform keeps only its hash), so they are always sent. `magic_link_login`
|
|
68
|
+
* and `customer_invite` can be switched off.
|
|
68
69
|
*/
|
|
69
70
|
enabled?: boolean;
|
|
70
71
|
/**
|
|
71
72
|
* `none` is refused on the identity emails — the `account` category:
|
|
72
73
|
* password reset, magic link, invitation, email change, email and chat
|
|
73
|
-
* verification — with `EVENT_ALWAYS_PLATFORM_SENT`
|
|
74
|
-
* Each carries a one-time credential
|
|
75
|
-
*
|
|
76
|
-
*
|
|
74
|
+
* verification — and on `gift_card_issued`, with `EVENT_ALWAYS_PLATFORM_SENT`
|
|
75
|
+
* ({@link ValidationError}). Each carries a one-time credential, or a gift
|
|
76
|
+
* card's code, only the platform can issue, and no platform event carries
|
|
77
|
+
* one (`stored_value.gift_card.issued` names the card, never its code), so
|
|
78
|
+
* there is no event your system could send it from. What you control there
|
|
79
|
+
* is where its link lands (your head's URL).
|
|
77
80
|
*
|
|
78
81
|
* `none` is also refused on an email whose `replacementEventTypes` is `null`,
|
|
79
82
|
* with `NO_REPLACEMENT_EVENT` ({@link ValidationError}): no platform event is
|
|
@@ -87,6 +90,8 @@ export interface UpdateNotificationEventConfigInput {
|
|
|
87
90
|
* links or codes, and one login serves the person in every store, so they go to
|
|
88
91
|
* the account holder alone: `to` is the `customer_email` variable, `cc` and
|
|
89
92
|
* `bcc` are empty, and `list` answers exactly that whatever was stored before.
|
|
93
|
+
* So does `gift_card_issued`: whoever holds a copy of its code can spend the
|
|
94
|
+
* card, so it goes to the buyer alone.
|
|
90
95
|
* Any other list on them is refused with `IDENTITY_EMAIL_RECIPIENTS_FIXED`
|
|
91
96
|
* ({@link ValidationError}); resending the lists `list` answered is accepted.
|
|
92
97
|
*/
|
|
@@ -119,18 +124,19 @@ export interface UpdateNotificationEventConfigInput {
|
|
|
119
124
|
*
|
|
120
125
|
* **Coded refusals**, GraphQL errors mapped by `core`:
|
|
121
126
|
* - `EVENT_ALWAYS_PLATFORM_SENT` → {@link ValidationError}: `sender: 'none'` on
|
|
122
|
-
* an identity email
|
|
123
|
-
* `/sender`.
|
|
127
|
+
* an identity email or `gift_card_issued`. The message names the event;
|
|
128
|
+
* `details[0].field` is `/sender`.
|
|
124
129
|
* - `EVENT_ALWAYS_ENABLED` → {@link ValidationError}: `enabled: false` on
|
|
125
|
-
* `password_reset`, `email_change` or `
|
|
126
|
-
* event and why it cannot be switched off;
|
|
130
|
+
* `password_reset`, `email_change`, `account_setup` or `gift_card_issued`.
|
|
131
|
+
* The message names the event and why it cannot be switched off;
|
|
132
|
+
* `details[0].field` is `/enabled`.
|
|
127
133
|
* - `NO_REPLACEMENT_EVENT` → {@link ValidationError}: `sender: 'none'` on an
|
|
128
134
|
* email no emitted platform event replaces (`replacementEventTypes: null`).
|
|
129
135
|
* The message names the event, and the event planned to replace it when one
|
|
130
136
|
* is; `details[0].field` is `/sender`.
|
|
131
137
|
* - `IDENTITY_EMAIL_RECIPIENTS_FIXED` → {@link ValidationError}: a recipient
|
|
132
|
-
* list on an identity email (or `new_customer`) that
|
|
133
|
-
* account holder. `details[].field` names each offending list
|
|
138
|
+
* list on an identity email (or `new_customer`, or `gift_card_issued`) that
|
|
139
|
+
* names anyone but the account holder. `details[].field` names each offending list
|
|
134
140
|
* (`/recipientTo`, `/recipientCc`, `/recipientBcc`).
|
|
135
141
|
* - `INVALID_NOTIFICATION_SENDER` → {@link ValidationError}.
|
|
136
142
|
* - `NOTIFICATION_EVENT_NOT_FOUND` → {@link NotFoundError}: no such event key.
|
|
@@ -97,7 +97,7 @@ export interface SettingsSourcesModule {
|
|
|
97
97
|
assets(group: SettingsGroup | (string & {}), call?: Call): Promise<AssetFile[]>;
|
|
98
98
|
}
|
|
99
99
|
export declare const CHANNEL_SETTINGS_SOURCES_QUERY = "\n query ChannelSettingsSources {\n channelSettingsSources { \n group\n source\n locked\n overridden\n inheritable\n organizationName\n values\n }\n }\n";
|
|
100
|
-
export declare const CHANNEL_SETTINGS_ASSETS_QUERY = "\n query ChannelSettingsAssets($group: String!) {\n channelSettingsAssets(group: $group) { \n id\n createdAt\n updatedAt\n name\n type\n mimeType\n fileSize\n width\n height\n source\n preview\n focalPoint {\n x\n y\n }\n tags {\n value\n }\n }\n }\n";
|
|
100
|
+
export declare const CHANNEL_SETTINGS_ASSETS_QUERY = "\n query ChannelSettingsAssets($group: String!) {\n channelSettingsAssets(group: $group) { \n id\n createdAt\n updatedAt\n name\n type\n mimeType\n fileSize\n width\n height\n source\n preview\n focalPoint {\n x\n y\n }\n tags {\n value\n }\n customFields {\n altText\n }\n }\n }\n";
|
|
101
101
|
export declare const OVERRIDE_ORGANIZATION_SETTINGS_MUTATION = "\n mutation OverrideOrganizationSettings($group: String!) {\n overrideOrganizationSettings(group: $group) { \n group\n source\n locked\n overridden\n inheritable\n organizationName\n values\n }\n }\n";
|
|
102
102
|
export declare const INHERIT_ORGANIZATION_SETTINGS_MUTATION = "\n mutation InheritOrganizationSettings($group: String!) {\n inheritOrganizationSettings(group: $group) { \n group\n source\n locked\n overridden\n inheritable\n organizationName\n values\n }\n }\n";
|
|
103
103
|
export declare function createSettingsSourcesModule(transport: Transport): SettingsSourcesModule;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Transport } from '@sixtyninecommerce/core';
|
|
2
|
+
import type { Call } from '../types.js';
|
|
3
|
+
/**
|
|
4
|
+
* `admin.tenant.timeZone(call?)`: the ACTIVE store's IANA time zone (`Europe/Amsterdam`,
|
|
5
|
+
* `America/New_York`, …) as the engine resolves it: the store's own setting, or
|
|
6
|
+
* `Europe/Amsterdam` when it has none or an invalid one. It is the zone the API counts calendar
|
|
7
|
+
* days in (a requested delivery day, "shipped today", an invoice date), so render dates and
|
|
8
|
+
* "today" in it, never in the browser's or the server's own zone, and never re-check the
|
|
9
|
+
* setting yourself: the engine owns the rule.
|
|
10
|
+
*
|
|
11
|
+
* Any signed-in caller of the store may read it. No channel argument: the tenant is the store.
|
|
12
|
+
*
|
|
13
|
+
* @effect read
|
|
14
|
+
*/
|
|
15
|
+
export type TenantTimeZoneReader = (call?: Call) => Promise<string>;
|
|
16
|
+
export declare const STORE_TIME_ZONE_QUERY = "\n query StoreTimeZone {\n storeTimeZone\n }\n";
|
|
17
|
+
export declare function createTenantTimeZoneReader(transport: Transport): TenantTimeZoneReader;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export const STORE_TIME_ZONE_QUERY = /* GraphQL */ `
|
|
2
|
+
query StoreTimeZone {
|
|
3
|
+
storeTimeZone
|
|
4
|
+
}
|
|
5
|
+
`;
|
|
6
|
+
export function createTenantTimeZoneReader(transport) {
|
|
7
|
+
return async (call) => {
|
|
8
|
+
const data = await transport.graphql({ api: 'admin', query: STORE_TIME_ZONE_QUERY }, call);
|
|
9
|
+
return data.storeTimeZone;
|
|
10
|
+
};
|
|
11
|
+
}
|
package/dist/types.d.ts
CHANGED
|
@@ -5,15 +5,22 @@ export interface Call {
|
|
|
5
5
|
* Sent as `Idempotency-Key` on a REST write. On a GraphQL one it is sent as
|
|
6
6
|
* an ARGUMENT — the Admin API has no header for it — and only the mutations
|
|
7
7
|
* that declare one act on it. Today those are the money mutations
|
|
8
|
-
* `payments.record`, `orders.park`, `orders.settle` and all three
|
|
8
|
+
* `payments.record`, `orders.park`, `orders.settle`, `orders.cancelWithMoney` and all three
|
|
9
9
|
* `orders.surcharges.*`, plus `payments.providers.completeOnboarding`,
|
|
10
10
|
* `payments.providers.terminals.provision`, `stock.adjust`,
|
|
11
11
|
* `hooks.assignments.assign`, `customers.upsertByExternalRef`,
|
|
12
12
|
* `customers.groups.replaceMembers`, `pricing.coupons.apply` / `remove`,
|
|
13
|
-
* `orders.setLinePrices` / `setOneTimeDiscounts`,
|
|
13
|
+
* `orders.setLinePrices` / `setOneTimeDiscounts`, `orders.correctFulfillmentQuantities`,
|
|
14
14
|
* `pos.sale.applyCouponCode` / `removeCouponCode`, `storedValue.issue` / `adjust` /
|
|
15
15
|
* `redeem`, `apps.releaseFieldGroup`, `catalog.products.bulk` and `orders.bulk` (both mint
|
|
16
|
-
* one when omitted),
|
|
16
|
+
* one when omitted), `catalog.products.save`, `orders.drafts.saveAddresses`,
|
|
17
|
+
* `orders.drafts.place` and `orders.drafts.cloneFrom` (both mint one when omitted),
|
|
18
|
+
* `fulfillments.create` and `fulfillments.warehouse.append` (both mint one when omitted),
|
|
19
|
+
* `quotes.convertToOrder` (mints one when omitted), `documents.issuePartialCreditNote` (mints
|
|
20
|
+
* one when omitted), and the
|
|
21
|
+
* accounts-receivable money doors `payments.ar.recordPayment` / `recordPrepaidDrawdown` /
|
|
22
|
+
* `directDebit.collectNow` and `documents.ar.issueCreditNote` / `generateInvoice` /
|
|
23
|
+
* `generateStatement`, and `returns.settle`. Repeating one under the same key
|
|
17
24
|
* returns the first call's result instead of moving the money again; a
|
|
18
25
|
* DIFFERENT request under the same key is refused with a `ConflictError`
|
|
19
26
|
* (409) coded `IDEMPOTENCY_CONFLICT`, and a repeat that arrives while the
|
|
@@ -54,6 +61,46 @@ export interface ListOptions extends Call {
|
|
|
54
61
|
after?: string;
|
|
55
62
|
limit?: number;
|
|
56
63
|
}
|
|
64
|
+
/**
|
|
65
|
+
* One argument a configurable operation takes, as the engine publishes its definition: a
|
|
66
|
+
* payment handler's `apiKey`, an eligibility checker's `customerGroupIds`.
|
|
67
|
+
*/
|
|
68
|
+
export interface ConfigArgDefinition {
|
|
69
|
+
name: string;
|
|
70
|
+
/** The value's type as the engine names it: `string`, `int`, `float`, `boolean`, `datetime`, `ID`. */
|
|
71
|
+
type: string;
|
|
72
|
+
/** A list is sent as one JSON-encoded string, like every argument value. */
|
|
73
|
+
list: boolean;
|
|
74
|
+
/**
|
|
75
|
+
* NOT the enforced rule, in either direction. The engine publishes `true` for every argument
|
|
76
|
+
* whose declaration merely omits the flag, including ones nothing enforces, and can publish
|
|
77
|
+
* `false` for one a write then refuses. Never gate a form or a request on it: send what you
|
|
78
|
+
* have and read the engine's refusal.
|
|
79
|
+
*/
|
|
80
|
+
required: boolean;
|
|
81
|
+
/** The declared default as JSON, or `null` when the argument has none. */
|
|
82
|
+
defaultValue: unknown;
|
|
83
|
+
/** In the language of the request; `null` when the declaration gives none. */
|
|
84
|
+
label: string | null;
|
|
85
|
+
/** In the language of the request; `null` when the declaration gives none. */
|
|
86
|
+
description: string | null;
|
|
87
|
+
/**
|
|
88
|
+
* How a form should render the argument, as declared: `{ component: 'password-form-input' }`
|
|
89
|
+
* marks a credential, `options` a fixed choice. `null` when nothing is declared.
|
|
90
|
+
*/
|
|
91
|
+
ui: Record<string, unknown> | null;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* A configurable operation the engine has registered (a payment handler, an eligibility
|
|
95
|
+
* checker, a shipping calculator, a promotion action): the `code` a stored operation names, and
|
|
96
|
+
* the arguments it takes.
|
|
97
|
+
*/
|
|
98
|
+
export interface ConfigurableOperationDefinition {
|
|
99
|
+
code: string;
|
|
100
|
+
/** What the operation does, in the language of the request. */
|
|
101
|
+
description: string;
|
|
102
|
+
args: ConfigArgDefinition[];
|
|
103
|
+
}
|
|
57
104
|
/** One channel the signed-in principal holds a role on. */
|
|
58
105
|
export interface MeChannel {
|
|
59
106
|
id: string;
|