@sixtyninecommerce/admin 0.9.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4598 -128
- 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 +318 -17
- package/dist/catalog/products.js +182 -14
- 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 +282 -25
- package/dist/catalog/variants.js +173 -25
- package/dist/catalog/visibility.d.ts +20 -5
- package/dist/catalog/visibility.js +19 -1
- package/dist/client.d.ts +116 -9
- package/dist/client.js +41 -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 +121 -10
- package/dist/fulfillments/index.js +120 -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 +129 -30
- package/dist/index.js +47 -9
- 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 +40 -0
- package/dist/integrations/index.js +24 -0
- package/dist/integrations/pipeline-keys.d.ts +13 -0
- package/dist/integrations/pipeline-keys.js +22 -0
- package/dist/integrations/pipelines.d.ts +114 -0
- package/dist/integrations/pipelines.js +46 -0
- package/dist/integrations/types.d.ts +322 -0
- package/dist/integrations/types.js +8 -0
- package/dist/jobs/bulk-action-run.d.ts +11 -2
- package/dist/jobs/bulk-action-run.js +4 -5
- 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/bulk.d.ts +75 -0
- package/dist/orders/bulk.js +150 -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/deposit-policy.d.ts +103 -0
- package/dist/orders/deposit-policy.js +202 -0
- 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 +125 -5
- package/dist/orders/index.js +35 -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-discounts.d.ts +11 -2
- package/dist/orders/manual-discounts.js +2 -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 +263 -10
- package/dist/payments/index.js +133 -1
- package/dist/payments/methods.d.ts +332 -0
- package/dist/payments/methods.js +363 -0
- 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 +177 -4
- package/dist/pricing/index.js +176 -1
- package/dist/pricing/price-lists.d.ts +604 -0
- package/dist/pricing/price-lists.js +600 -0
- package/dist/pricing/vouchers.d.ts +103 -0
- package/dist/pricing/vouchers.js +69 -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 +818 -0
- package/dist/returns/index.js +844 -0
- package/dist/search/index.d.ts +174 -0
- package/dist/search/index.js +125 -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 +51 -4
- 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
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import type { Transport } from '@sixtyninecommerce/core';
|
|
2
|
+
import type { Call } from '../types.js';
|
|
3
|
+
/** How the order reference sent to C.J. Hendriks is prefixed. */
|
|
4
|
+
export type HendriksWirePrefixMode = 'year' | 'custom';
|
|
5
|
+
/** A store's C.J. Hendriks 3PL settings: one record per store. */
|
|
6
|
+
export interface HendriksConfig {
|
|
7
|
+
id: string;
|
|
8
|
+
channelId: string;
|
|
9
|
+
/** Gates every Hendriks export of the store. */
|
|
10
|
+
enabled: boolean;
|
|
11
|
+
/** The plugins-service export pipeline that sends the orders; `null` is not linked. */
|
|
12
|
+
pipelineId: string | null;
|
|
13
|
+
/** `year` prefixes the current UTC year; `custom` prefixes {@link wirePrefixDigits}. */
|
|
14
|
+
wirePrefixMode: HendriksWirePrefixMode;
|
|
15
|
+
/** 1 to 4 digits without a leading zero. Only read in `custom` mode; `null` in `year` mode. */
|
|
16
|
+
wirePrefixDigits: string | null;
|
|
17
|
+
/**
|
|
18
|
+
* Refuse to export an order the source platform already sent to its own fulfillment
|
|
19
|
+
* provider. On by default: while a migrated shop is still live both systems feed the same
|
|
20
|
+
* warehouse, and neither can spot the other's reference, so the goods would ship twice.
|
|
21
|
+
*/
|
|
22
|
+
blockSourceFulfilledOrders: boolean;
|
|
23
|
+
/** ISO-8601, UTC. */
|
|
24
|
+
createdAt: string;
|
|
25
|
+
/** ISO-8601, UTC. */
|
|
26
|
+
updatedAt: string;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* A PATCH: a key left out keeps the stored value. The custom-prefix rule is judged on the
|
|
30
|
+
* RESULTING settings, so switching to `custom` needs `wirePrefixDigits` in the same call unless
|
|
31
|
+
* valid digits are already stored.
|
|
32
|
+
*/
|
|
33
|
+
export interface UpdateHendriksConfigInput {
|
|
34
|
+
enabled?: boolean;
|
|
35
|
+
/** `null` unlinks the pipeline. */
|
|
36
|
+
pipelineId?: string | null;
|
|
37
|
+
wirePrefixMode?: HendriksWirePrefixMode;
|
|
38
|
+
/** Cleared by the engine whenever the resulting mode is `year`. */
|
|
39
|
+
wirePrefixDigits?: string | null;
|
|
40
|
+
blockSourceFulfilledOrders?: boolean;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* The C.J. Hendriks 3PL settings of the store the client is scoped to
|
|
44
|
+
* (`admin.shipping.hendriks.config`). There is no channel argument: a SuperAdmin head configures
|
|
45
|
+
* ANOTHER store with `admin.withTenant({ scope: { kind: 'channel', code } })`, or a per-call
|
|
46
|
+
* `tenant`.
|
|
47
|
+
*/
|
|
48
|
+
export interface HendriksConfigModule {
|
|
49
|
+
/**
|
|
50
|
+
* The store's settings, or `null` when it never saved any. Needs `ReadSettings`, and is
|
|
51
|
+
* answered for a store without the Hendriks plugin too (a head reads it to decide whether to
|
|
52
|
+
* show the integration).
|
|
53
|
+
*
|
|
54
|
+
* @effect read
|
|
55
|
+
* @permission ReadSettings
|
|
56
|
+
*/
|
|
57
|
+
get(options?: Call): Promise<HendriksConfig | null>;
|
|
58
|
+
/**
|
|
59
|
+
* Save the settings; the first call creates the record (`enabled` off, `year` prefix,
|
|
60
|
+
* `blockSourceFulfilledOrders` on, unless given). Needs `UpdateSettings`.
|
|
61
|
+
*
|
|
62
|
+
* Refused, with nothing saved:
|
|
63
|
+
* - `HENDRIKS_CONFIG_INVALID` (`ValidationError`, 400): `custom` mode without 1 to 4 digits not
|
|
64
|
+
* starting with 0. `details[]` names each field (`wirePrefixDigits`, `wirePrefixMode`), so a
|
|
65
|
+
* form can put the reason on it;
|
|
66
|
+
* - `PLUGIN_ACCESS_DENIED` (`PermissionDeniedError`, 403): the store does not hold the
|
|
67
|
+
* Hendriks plugin. An ordinary caller only: the engine never refuses a SuperAdmin on this
|
|
68
|
+
* ground, so a SuperAdmin head that must know reads `tenant.entitlements()` on that store;
|
|
69
|
+
* - `ORGANIZATION_WORKSPACE_NOT_A_STORE` (`BusinessRuleError`, 422): switching the export on,
|
|
70
|
+
* or linking a pipeline, in an organization's workspace.
|
|
71
|
+
*
|
|
72
|
+
* `pipelineId` is not checked against the store's pipelines; an export through a pipeline
|
|
73
|
+
* that does not exist fails when it runs.
|
|
74
|
+
*
|
|
75
|
+
* @effect write
|
|
76
|
+
* @permission UpdateSettings
|
|
77
|
+
*/
|
|
78
|
+
update(input: UpdateHendriksConfigInput, options?: Call): Promise<HendriksConfig>;
|
|
79
|
+
/**
|
|
80
|
+
* Delete the store's settings record. Every Hendriks export of the store is refused until
|
|
81
|
+
* settings are saved again, and the prefix and pipeline link are gone with it. `true` when a
|
|
82
|
+
* record was deleted, `false` when the store had none. Needs `UpdateSettings`; allowed for a
|
|
83
|
+
* store that no longer holds the plugin, so it can always remove its connection.
|
|
84
|
+
*
|
|
85
|
+
* @effect destructive
|
|
86
|
+
* @permission UpdateSettings
|
|
87
|
+
*/
|
|
88
|
+
delete(options?: Call): Promise<boolean>;
|
|
89
|
+
}
|
|
90
|
+
/** `admin.shipping.hendriks`: the C.J. Hendriks 3PL integration of the store. */
|
|
91
|
+
export interface HendriksModule {
|
|
92
|
+
config: HendriksConfigModule;
|
|
93
|
+
}
|
|
94
|
+
export declare const HENDRIKS_CONFIG_QUERY = "\n query HendriksConfig {\n hendriksConfig { \n id\n channelId\n enabled\n pipelineId\n wirePrefixMode\n wirePrefixDigits\n blockSourceFulfilledOrders\n createdAt\n updatedAt\n }\n }\n";
|
|
95
|
+
export declare const UPDATE_HENDRIKS_CONFIG_MUTATION = "\n mutation UpdateHendriksConfig($input: UpdateHendriksConfigInput!) {\n updateHendriksConfig(input: $input) { \n id\n channelId\n enabled\n pipelineId\n wirePrefixMode\n wirePrefixDigits\n blockSourceFulfilledOrders\n createdAt\n updatedAt\n }\n }\n";
|
|
96
|
+
export declare const DELETE_HENDRIKS_CONFIG_MUTATION = "\n mutation DeleteHendriksConfig {\n deleteHendriksConfig\n }\n";
|
|
97
|
+
export declare function createHendriksModule(transport: Transport): HendriksModule;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
const CONFIG_FIELDS = /* GraphQL */ `
|
|
2
|
+
id
|
|
3
|
+
channelId
|
|
4
|
+
enabled
|
|
5
|
+
pipelineId
|
|
6
|
+
wirePrefixMode
|
|
7
|
+
wirePrefixDigits
|
|
8
|
+
blockSourceFulfilledOrders
|
|
9
|
+
createdAt
|
|
10
|
+
updatedAt
|
|
11
|
+
`;
|
|
12
|
+
export const HENDRIKS_CONFIG_QUERY = /* GraphQL */ `
|
|
13
|
+
query HendriksConfig {
|
|
14
|
+
hendriksConfig { ${CONFIG_FIELDS} }
|
|
15
|
+
}
|
|
16
|
+
`;
|
|
17
|
+
export const UPDATE_HENDRIKS_CONFIG_MUTATION = /* GraphQL */ `
|
|
18
|
+
mutation UpdateHendriksConfig($input: UpdateHendriksConfigInput!) {
|
|
19
|
+
updateHendriksConfig(input: $input) { ${CONFIG_FIELDS} }
|
|
20
|
+
}
|
|
21
|
+
`;
|
|
22
|
+
export const DELETE_HENDRIKS_CONFIG_MUTATION = /* GraphQL */ `
|
|
23
|
+
mutation DeleteHendriksConfig {
|
|
24
|
+
deleteHendriksConfig
|
|
25
|
+
}
|
|
26
|
+
`;
|
|
27
|
+
export function createHendriksModule(transport) {
|
|
28
|
+
return {
|
|
29
|
+
config: {
|
|
30
|
+
async get(options) {
|
|
31
|
+
const data = await transport.graphql({ api: 'admin', query: HENDRIKS_CONFIG_QUERY }, options);
|
|
32
|
+
return data.hendriksConfig ?? null;
|
|
33
|
+
},
|
|
34
|
+
async update(input, options) {
|
|
35
|
+
const data = await transport.graphql({
|
|
36
|
+
api: 'admin',
|
|
37
|
+
query: UPDATE_HENDRIKS_CONFIG_MUTATION,
|
|
38
|
+
// Sent as given: a key left out stays off the wire (the engine reads an absent
|
|
39
|
+
// key as "keep"), and the engine names anything it refuses.
|
|
40
|
+
variables: { input },
|
|
41
|
+
}, options);
|
|
42
|
+
return data.updateHendriksConfig;
|
|
43
|
+
},
|
|
44
|
+
async delete(options) {
|
|
45
|
+
const data = await transport.graphql({ api: 'admin', query: DELETE_HENDRIKS_CONFIG_MUTATION }, options);
|
|
46
|
+
return data.deleteHendriksConfig === true;
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shipping (headless spec §9.2 `shipping`): everything about getting a store's orders to its
|
|
3
|
+
* customers. Each member lives in a file of its own and is only assembled here, so a branch
|
|
4
|
+
* that adds one (shipping methods, rate tables, a carrier) adds a file and one line.
|
|
5
|
+
*/
|
|
6
|
+
import type { Transport } from '@sixtyninecommerce/core';
|
|
7
|
+
import { type ShippingDefinitionsModule } from './definitions.js';
|
|
8
|
+
import { type FulfillmentProfilesModule } from './fulfillment-profiles.js';
|
|
9
|
+
import { type HendriksModule } from './hendriks.js';
|
|
10
|
+
import { type ShippingMethodsModule } from './methods.js';
|
|
11
|
+
import { type MyparcelModule } from './myparcel.js';
|
|
12
|
+
import { type ParcelCountSuggestionMethod } from './parcel-count-suggestion.js';
|
|
13
|
+
import { type ParcelRuleModule } from './parcel-rule.js';
|
|
14
|
+
import { type RateTablesModule } from './rate-tables.js';
|
|
15
|
+
/** `admin.shipping` (spec §9.2 `shipping`). Always the store the client is scoped to. */
|
|
16
|
+
export interface ShippingModule {
|
|
17
|
+
/** The store's shipping methods: what checkout offers and what each charges. */
|
|
18
|
+
methods: ShippingMethodsModule;
|
|
19
|
+
/** What a shipping method's calculator, checker and fulfillment handler may be. */
|
|
20
|
+
definitions: ShippingDefinitionsModule;
|
|
21
|
+
/**
|
|
22
|
+
* The weight-zone rate tables a `weight-zone-calculator` method prices with: per table its
|
|
23
|
+
* zones (postal-code ranges or countries) and per zone its weight tiers' prices.
|
|
24
|
+
*/
|
|
25
|
+
rateTables: RateTablesModule;
|
|
26
|
+
/** How the store counts parcels for extra-parcel shipping charges. */
|
|
27
|
+
parcelRule: ParcelRuleModule;
|
|
28
|
+
/**
|
|
29
|
+
* How many parcels the store's rule gives a set of variant + quantity items.
|
|
30
|
+
*
|
|
31
|
+
* @effect read
|
|
32
|
+
*/
|
|
33
|
+
parcelCountSuggestion: ParcelCountSuggestionMethod;
|
|
34
|
+
/** The C.J. Hendriks 3PL integration: its settings (`config`). */
|
|
35
|
+
hendriks: HendriksModule;
|
|
36
|
+
/**
|
|
37
|
+
* The MyParcel integration: its set-up (`config`: the API key, label format, on/off and webhook
|
|
38
|
+
* secret, both secrets read back masked), the carriers MyParcel offers, and the store's stock
|
|
39
|
+
* locations as parcel senders (`senders`).
|
|
40
|
+
*/
|
|
41
|
+
myparcel: MyparcelModule;
|
|
42
|
+
/**
|
|
43
|
+
* The store's fulfillment profiles (warehouse + handler + shipping method + minimum), its
|
|
44
|
+
* default profile, and who is emailed per handler when a fulfilment is created.
|
|
45
|
+
*/
|
|
46
|
+
fulfillmentProfiles: FulfillmentProfilesModule;
|
|
47
|
+
}
|
|
48
|
+
export declare function createShippingModule(transport: Transport): ShippingModule;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shipping (headless spec §9.2 `shipping`): everything about getting a store's orders to its
|
|
3
|
+
* customers. Each member lives in a file of its own and is only assembled here, so a branch
|
|
4
|
+
* that adds one (shipping methods, rate tables, a carrier) adds a file and one line.
|
|
5
|
+
*/
|
|
6
|
+
import { createShippingDefinitionsModule } from './definitions.js';
|
|
7
|
+
import { createFulfillmentProfilesModule, } from './fulfillment-profiles.js';
|
|
8
|
+
import { createHendriksModule } from './hendriks.js';
|
|
9
|
+
import { createShippingMethodsModule } from './methods.js';
|
|
10
|
+
import { createMyparcelModule } from './myparcel.js';
|
|
11
|
+
import { createParcelCountSuggestion, } from './parcel-count-suggestion.js';
|
|
12
|
+
import { createParcelRuleModule } from './parcel-rule.js';
|
|
13
|
+
import { createRateTablesModule } from './rate-tables.js';
|
|
14
|
+
export function createShippingModule(transport) {
|
|
15
|
+
return {
|
|
16
|
+
methods: createShippingMethodsModule(transport),
|
|
17
|
+
definitions: createShippingDefinitionsModule(transport),
|
|
18
|
+
rateTables: createRateTablesModule(transport),
|
|
19
|
+
parcelRule: createParcelRuleModule(transport),
|
|
20
|
+
parcelCountSuggestion: createParcelCountSuggestion(transport),
|
|
21
|
+
hendriks: createHendriksModule(transport),
|
|
22
|
+
myparcel: createMyparcelModule(transport),
|
|
23
|
+
fulfillmentProfiles: createFulfillmentProfilesModule(transport),
|
|
24
|
+
};
|
|
25
|
+
}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { type Page, type Transport } from '@sixtyninecommerce/core';
|
|
2
|
+
import { type ConfigurableOperation, type ConfigurableOperationInput, type LanguageCode } from '../catalog/types.js';
|
|
3
|
+
import type { Call, ListOptions } from '../types.js';
|
|
4
|
+
/** A shipping method's name and description in one language. */
|
|
5
|
+
export interface ShippingMethodTranslation {
|
|
6
|
+
id: string;
|
|
7
|
+
/** As the engine spells it: `en`, `nl`, `pt_BR`. */
|
|
8
|
+
languageCode: string;
|
|
9
|
+
name: string;
|
|
10
|
+
description: string;
|
|
11
|
+
}
|
|
12
|
+
/** One shipping method of the store, as its list reads it. */
|
|
13
|
+
export interface ShippingMethodSummary {
|
|
14
|
+
id: string;
|
|
15
|
+
/** What checkout and a fulfillment profile name the method by. */
|
|
16
|
+
code: string;
|
|
17
|
+
/** In the language the client is set to (`tenant.language`), else the store's default. */
|
|
18
|
+
name: string;
|
|
19
|
+
/**
|
|
20
|
+
* The fulfillment handler of an order fulfilled WITHOUT a fulfillment profile (the profiles
|
|
21
|
+
* route every other one). `manual-fulfillment` unless a carrier's was picked.
|
|
22
|
+
*/
|
|
23
|
+
fulfillmentHandlerCode: string;
|
|
24
|
+
/** What the method charges, with its stored arguments (every value a string). */
|
|
25
|
+
calculator: ConfigurableOperation;
|
|
26
|
+
/**
|
|
27
|
+
* The master switch: a disabled method is offered nowhere (webshop, staff, till) and a cart on
|
|
28
|
+
* it re-quotes. On unless it was switched off (a method stored before the switch existed).
|
|
29
|
+
*/
|
|
30
|
+
enabled: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Offered at webshop checkout; staff orders and the till do not read it. On unless it was
|
|
33
|
+
* switched off. Only meaningful while {@link enabled}.
|
|
34
|
+
*/
|
|
35
|
+
enabledInStorefront: boolean;
|
|
36
|
+
/** ISO-8601, UTC. */
|
|
37
|
+
createdAt: string;
|
|
38
|
+
/** ISO-8601, UTC. */
|
|
39
|
+
updatedAt: string;
|
|
40
|
+
}
|
|
41
|
+
/** One shipping method of the store, as its settings read it. */
|
|
42
|
+
export interface ShippingMethod extends ShippingMethodSummary {
|
|
43
|
+
/** In the same language as {@link name}; empty when the method has none. */
|
|
44
|
+
description: string;
|
|
45
|
+
/** Which orders the method is offered for, with its stored arguments. Every method has one. */
|
|
46
|
+
checker: ConfigurableOperation;
|
|
47
|
+
/** Every language the method is named in. */
|
|
48
|
+
translations: ShippingMethodTranslation[];
|
|
49
|
+
/**
|
|
50
|
+
* The Exact Online integration created the method from one of its own shipping definitions:
|
|
51
|
+
* Exact's sync owns its code and calculator (the name is the store's). Written by that sync
|
|
52
|
+
* only; neither write here takes it.
|
|
53
|
+
*/
|
|
54
|
+
exactManaged: boolean;
|
|
55
|
+
/**
|
|
56
|
+
* Added once per parcel after the first, in minor units and the calculator's own tax basis;
|
|
57
|
+
* null charges nothing, 0 is a price. Only the two flat-rate calculators
|
|
58
|
+
* (`default-shipping-calculator`, `flat-threshold-calculator`) charge it, and the store's
|
|
59
|
+
* parcel rule decides how many parcels an order is; on any other calculator a stored value is
|
|
60
|
+
* kept but charges nothing.
|
|
61
|
+
*/
|
|
62
|
+
extraParcelPrice: number | null;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* A method's name and description in one language, as a create or an update writes them. The
|
|
66
|
+
* engine keeps one row per language: writing a language the method has replaces that row's
|
|
67
|
+
* name and description, and any other language is added beside the ones it has.
|
|
68
|
+
*/
|
|
69
|
+
export interface ShippingMethodTranslationInput {
|
|
70
|
+
/** As the engine spells it: `en`, `nl`, `pt_BR`. */
|
|
71
|
+
languageCode: LanguageCode;
|
|
72
|
+
name: string;
|
|
73
|
+
/** Empty when left out. */
|
|
74
|
+
description?: string;
|
|
75
|
+
}
|
|
76
|
+
/** A new shipping method, as {@link ShippingMethodsModule.create} takes it. */
|
|
77
|
+
export interface CreateShippingMethodInput {
|
|
78
|
+
code: string;
|
|
79
|
+
/**
|
|
80
|
+
* One of `shipping.definitions.get()`'s `fulfillmentHandlers`. The engine requires one; a
|
|
81
|
+
* store that routes by fulfillment profiles names `manual-fulfillment`.
|
|
82
|
+
*/
|
|
83
|
+
fulfillmentHandler: string;
|
|
84
|
+
/**
|
|
85
|
+
* One of `eligibilityCheckers`, with every argument it needs (a value is always a string; lists
|
|
86
|
+
* and booleans JSON-encoded). The engine requires one; "everyone" is
|
|
87
|
+
* `default-shipping-eligibility-checker` with `orderMinimum: '0'`.
|
|
88
|
+
*/
|
|
89
|
+
checker: ConfigurableOperationInput;
|
|
90
|
+
/** One of `calculators`, with its arguments. */
|
|
91
|
+
calculator: ConfigurableOperationInput;
|
|
92
|
+
/** At least the store's default language: it is the name shoppers and staff are shown. */
|
|
93
|
+
translations: ShippingMethodTranslationInput[];
|
|
94
|
+
/** On when left out. See {@link ShippingMethodSummary.enabled}. */
|
|
95
|
+
enabled?: boolean;
|
|
96
|
+
/** On when left out. */
|
|
97
|
+
enabledInStorefront?: boolean;
|
|
98
|
+
/** Minor units, or null for no charge (left out: none). See {@link ShippingMethod.extraParcelPrice}. */
|
|
99
|
+
extraParcelPrice?: number | null;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* A change to a shipping method, as {@link ShippingMethodsModule.update} takes it. A field left
|
|
103
|
+
* out keeps what the method has. The checker can be replaced, never taken away: every method has
|
|
104
|
+
* one.
|
|
105
|
+
*/
|
|
106
|
+
export interface UpdateShippingMethodInput {
|
|
107
|
+
code?: string;
|
|
108
|
+
fulfillmentHandler?: string;
|
|
109
|
+
checker?: ConfigurableOperationInput;
|
|
110
|
+
calculator?: ConfigurableOperationInput;
|
|
111
|
+
translations?: ShippingMethodTranslationInput[];
|
|
112
|
+
enabled?: boolean;
|
|
113
|
+
enabledInStorefront?: boolean;
|
|
114
|
+
/** `null` clears it (no charge); left out, the stored price stays. */
|
|
115
|
+
extraParcelPrice?: number | null;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* The store's shipping methods (`admin.shipping.methods`): what checkout offers, what a
|
|
119
|
+
* fulfillment profile and the till's delivery setting name, and what the store's shipping
|
|
120
|
+
* settings show.
|
|
121
|
+
*
|
|
122
|
+
* Every read needs `ReadSettings` or `ReadShippingMethod` (either one, so no `@permission` tag:
|
|
123
|
+
* the tag can only say "all of these"). Scoped to the channel the client is on: another store's
|
|
124
|
+
* methods are not listed, and not answered by id.
|
|
125
|
+
*/
|
|
126
|
+
export interface ShippingMethodsModule {
|
|
127
|
+
/**
|
|
128
|
+
* Every method of the store, enabled or not, in id order. Paged (`options.limit`, default 100,
|
|
129
|
+
* at most 1,000 or `INVALID_PAGE_SIZE`; and `options.after` from a previous page);
|
|
130
|
+
* `for await (const method of page)` walks them all.
|
|
131
|
+
*
|
|
132
|
+
* @effect read
|
|
133
|
+
*/
|
|
134
|
+
list(options?: ListOptions): Promise<Page<ShippingMethodSummary>>;
|
|
135
|
+
/**
|
|
136
|
+
* One method by id, or `null` when the store the client is on has no such method: an id that
|
|
137
|
+
* does not exist, a deleted method and the id of a method only ANOTHER store holds read the
|
|
138
|
+
* same.
|
|
139
|
+
*
|
|
140
|
+
* @effect read
|
|
141
|
+
*/
|
|
142
|
+
get(id: string, options?: Call): Promise<ShippingMethod | null>;
|
|
143
|
+
/**
|
|
144
|
+
* Add a shipping method to the store the client is on, and answer it as {@link get} reads it.
|
|
145
|
+
* Needs `CreateSettings` or `CreateShippingMethod` (either one). Nothing is saved when it is
|
|
146
|
+
* refused:
|
|
147
|
+
*
|
|
148
|
+
* - `INVALID_SHIPPING_METHOD_INPUT` (`ValidationError`, 400): the input's shape, checked here
|
|
149
|
+
* before anything is sent.
|
|
150
|
+
* - `ORGANIZATION_WORKSPACE_NOT_A_STORE` (`BusinessRuleError`, 422): an organization's
|
|
151
|
+
* workspace ships nothing; create the method in one of its stores.
|
|
152
|
+
* - `PLUGIN_ACCESS_DENIED` (`PermissionDeniedError`, 403): the fulfillment handler, checker or
|
|
153
|
+
* calculator belongs to a plugin the store does not hold (a carrier, Exact Online).
|
|
154
|
+
* - The engine's own input refusal (`ValidationError`, 400): a handler, checker or calculator
|
|
155
|
+
* code it has not registered, a missing argument, or an `extraParcelPrice` outside
|
|
156
|
+
* 0–10,000,000.
|
|
157
|
+
*
|
|
158
|
+
* @effect write
|
|
159
|
+
*/
|
|
160
|
+
create(input: CreateShippingMethodInput, options?: Call): Promise<ShippingMethod>;
|
|
161
|
+
/**
|
|
162
|
+
* Change a shipping method of the store the client is on, and answer it as {@link get} reads
|
|
163
|
+
* it. A field left out keeps what the method has. Needs `UpdateSettings` or
|
|
164
|
+
* `UpdateShippingMethod` (either one).
|
|
165
|
+
*
|
|
166
|
+
* One method may be held by several stores, and a change reaches every one of them. Refused,
|
|
167
|
+
* with nothing saved, as {@link create} is (`PLUGIN_ACCESS_DENIED` only for a handler, checker
|
|
168
|
+
* or calculator the patch CHANGES: one the method already has is no new pick), and:
|
|
169
|
+
*
|
|
170
|
+
* - `ENTITY_NOT_FOUND` (`NotFoundError`, 404): the store has no such method (another store's
|
|
171
|
+
* reads the same).
|
|
172
|
+
* - `SHARED_SHIPPING_METHOD_NOT_ALLOWED` (`PermissionDeniedError`, 403): the method is shared
|
|
173
|
+
* with another store where the caller lacks `UpdateSettings` or `UpdateShippingMethod`.
|
|
174
|
+
*
|
|
175
|
+
* @effect write
|
|
176
|
+
*/
|
|
177
|
+
update(id: string, patch: UpdateShippingMethodInput, options?: Call): Promise<ShippingMethod>;
|
|
178
|
+
/**
|
|
179
|
+
* Delete a shipping method. The method is withdrawn from checkout in EVERY store that holds
|
|
180
|
+
* it, not only the one the client is on; orders already shipped with it keep their shipping
|
|
181
|
+
* line. Needs `DeleteSettings` or `DeleteShippingMethod` (either one), and the same in every
|
|
182
|
+
* other store holding the method, else `SHARED_SHIPPING_METHOD_NOT_ALLOWED`
|
|
183
|
+
* (`PermissionDeniedError`, 403) and nothing is deleted.
|
|
184
|
+
*
|
|
185
|
+
* `ENTITY_NOT_FOUND` (`NotFoundError`, 404): the store has no such method, or it is already
|
|
186
|
+
* deleted. A refusal the engine answers as a result rather than an error is
|
|
187
|
+
* `SHIPPING_METHOD_NOT_DELETED` (`ConflictError`, 409) with the engine's reason.
|
|
188
|
+
*
|
|
189
|
+
* @effect destructive
|
|
190
|
+
*/
|
|
191
|
+
delete(id: string, options?: Call): Promise<void>;
|
|
192
|
+
}
|
|
193
|
+
export declare const SHIPPING_METHODS_QUERY = "\n query StoreShippingMethods($options: ShippingMethodListOptions) {\n shippingMethods(options: $options) {\n items { \n id\n createdAt\n updatedAt\n code\n name\n fulfillmentHandlerCode\n calculator { \n code\n args {\n name\n value\n }\n }\n customFields {\n enabled\n enabledInStorefront\n }\n }\n totalItems\n }\n }\n";
|
|
194
|
+
export declare const SHIPPING_METHOD_QUERY = "\n query StoreShippingMethod($id: ID!) {\n shippingMethod(id: $id) { \n id\n createdAt\n updatedAt\n code\n name\n description\n fulfillmentHandlerCode\n calculator { \n code\n args {\n name\n value\n }\n }\n checker { \n code\n args {\n name\n value\n }\n }\n translations {\n id\n languageCode\n name\n description\n }\n customFields {\n enabled\n enabledInStorefront\n exactManaged\n extraParcelPrice\n }\n }\n }\n";
|
|
195
|
+
export declare const CREATE_SHIPPING_METHOD_MUTATION = "\n mutation CreateStoreShippingMethod($input: CreateShippingMethodInput!) {\n createShippingMethod(input: $input) { \n id\n createdAt\n updatedAt\n code\n name\n description\n fulfillmentHandlerCode\n calculator { \n code\n args {\n name\n value\n }\n }\n checker { \n code\n args {\n name\n value\n }\n }\n translations {\n id\n languageCode\n name\n description\n }\n customFields {\n enabled\n enabledInStorefront\n exactManaged\n extraParcelPrice\n }\n }\n }\n";
|
|
196
|
+
export declare const UPDATE_SHIPPING_METHOD_MUTATION = "\n mutation UpdateStoreShippingMethod($input: UpdateShippingMethodInput!) {\n updateShippingMethod(input: $input) { \n id\n createdAt\n updatedAt\n code\n name\n description\n fulfillmentHandlerCode\n calculator { \n code\n args {\n name\n value\n }\n }\n checker { \n code\n args {\n name\n value\n }\n }\n translations {\n id\n languageCode\n name\n description\n }\n customFields {\n enabled\n enabledInStorefront\n exactManaged\n extraParcelPrice\n }\n }\n }\n";
|
|
197
|
+
export declare const DELETE_SHIPPING_METHOD_MUTATION = "\n mutation DeleteStoreShippingMethod($id: ID!) {\n deleteShippingMethod(id: $id) {\n result\n message\n }\n }\n";
|
|
198
|
+
export declare function createShippingMethodsModule(transport: Transport): ShippingMethodsModule;
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import { ValidationError } from '@sixtyninecommerce/core';
|
|
2
|
+
import { assertDeleted, fieldOf, vendureListPage, } from '../catalog/types.js';
|
|
3
|
+
/** A store has a handful; one page holds them. */
|
|
4
|
+
const DEFAULT_PAGE_SIZE = 100;
|
|
5
|
+
/**
|
|
6
|
+
* Vendure's list queries add no ORDER BY of their own, and `skip/take` over an unordered query
|
|
7
|
+
* can repeat or skip rows between two requests: id is the one unique key a sort can name.
|
|
8
|
+
*/
|
|
9
|
+
const BY_ID = { id: 'ASC' };
|
|
10
|
+
const OPERATION_FIELDS = /* GraphQL */ `
|
|
11
|
+
code
|
|
12
|
+
args {
|
|
13
|
+
name
|
|
14
|
+
value
|
|
15
|
+
}
|
|
16
|
+
`;
|
|
17
|
+
const SUMMARY_FIELDS = /* GraphQL */ `
|
|
18
|
+
id
|
|
19
|
+
createdAt
|
|
20
|
+
updatedAt
|
|
21
|
+
code
|
|
22
|
+
name
|
|
23
|
+
fulfillmentHandlerCode
|
|
24
|
+
calculator { ${OPERATION_FIELDS} }
|
|
25
|
+
customFields {
|
|
26
|
+
enabled
|
|
27
|
+
enabledInStorefront
|
|
28
|
+
}
|
|
29
|
+
`;
|
|
30
|
+
/** One selection for the single read and both writes, so all three answer the same method. */
|
|
31
|
+
const METHOD_FIELDS = /* GraphQL */ `
|
|
32
|
+
id
|
|
33
|
+
createdAt
|
|
34
|
+
updatedAt
|
|
35
|
+
code
|
|
36
|
+
name
|
|
37
|
+
description
|
|
38
|
+
fulfillmentHandlerCode
|
|
39
|
+
calculator { ${OPERATION_FIELDS} }
|
|
40
|
+
checker { ${OPERATION_FIELDS} }
|
|
41
|
+
translations {
|
|
42
|
+
id
|
|
43
|
+
languageCode
|
|
44
|
+
name
|
|
45
|
+
description
|
|
46
|
+
}
|
|
47
|
+
customFields {
|
|
48
|
+
enabled
|
|
49
|
+
enabledInStorefront
|
|
50
|
+
exactManaged
|
|
51
|
+
extraParcelPrice
|
|
52
|
+
}
|
|
53
|
+
`;
|
|
54
|
+
export const SHIPPING_METHODS_QUERY = /* GraphQL */ `
|
|
55
|
+
query StoreShippingMethods($options: ShippingMethodListOptions) {
|
|
56
|
+
shippingMethods(options: $options) {
|
|
57
|
+
items { ${SUMMARY_FIELDS} }
|
|
58
|
+
totalItems
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
`;
|
|
62
|
+
export const SHIPPING_METHOD_QUERY = /* GraphQL */ `
|
|
63
|
+
query StoreShippingMethod($id: ID!) {
|
|
64
|
+
shippingMethod(id: $id) { ${METHOD_FIELDS} }
|
|
65
|
+
}
|
|
66
|
+
`;
|
|
67
|
+
export const CREATE_SHIPPING_METHOD_MUTATION = /* GraphQL */ `
|
|
68
|
+
mutation CreateStoreShippingMethod($input: CreateShippingMethodInput!) {
|
|
69
|
+
createShippingMethod(input: $input) { ${METHOD_FIELDS} }
|
|
70
|
+
}
|
|
71
|
+
`;
|
|
72
|
+
export const UPDATE_SHIPPING_METHOD_MUTATION = /* GraphQL */ `
|
|
73
|
+
mutation UpdateStoreShippingMethod($input: UpdateShippingMethodInput!) {
|
|
74
|
+
updateShippingMethod(input: $input) { ${METHOD_FIELDS} }
|
|
75
|
+
}
|
|
76
|
+
`;
|
|
77
|
+
export const DELETE_SHIPPING_METHOD_MUTATION = /* GraphQL */ `
|
|
78
|
+
mutation DeleteStoreShippingMethod($id: ID!) {
|
|
79
|
+
deleteShippingMethod(id: $id) {
|
|
80
|
+
result
|
|
81
|
+
message
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
`;
|
|
85
|
+
const invalidInput = (message) => new ValidationError({
|
|
86
|
+
code: 'INVALID_SHIPPING_METHOD_INPUT',
|
|
87
|
+
message,
|
|
88
|
+
status: 400,
|
|
89
|
+
service: 'vendure',
|
|
90
|
+
});
|
|
91
|
+
const isObject = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
92
|
+
/** A handler-like operation as the engine takes it, checked here so a wrong shape never leaves. */
|
|
93
|
+
function operationInput(value, what) {
|
|
94
|
+
if (!isObject(value) || typeof value.code !== 'string' || value.code === '') {
|
|
95
|
+
throw invalidInput(`${what} needs a code.`);
|
|
96
|
+
}
|
|
97
|
+
const args = value.arguments;
|
|
98
|
+
if (!Array.isArray(args) ||
|
|
99
|
+
!args.every((arg) => isObject(arg) && typeof arg.name === 'string' && typeof arg.value === 'string')) {
|
|
100
|
+
throw invalidInput(`${what}.arguments must be a list of { name, value } strings (a list or a boolean JSON-encoded).`);
|
|
101
|
+
}
|
|
102
|
+
return {
|
|
103
|
+
code: value.code,
|
|
104
|
+
arguments: args.map((arg) => ({ name: String(arg.name), value: String(arg.value) })),
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
function translationsInput(value) {
|
|
108
|
+
if (!Array.isArray(value) ||
|
|
109
|
+
!value.every((row) => isObject(row) &&
|
|
110
|
+
typeof row.languageCode === 'string' &&
|
|
111
|
+
row.languageCode !== '' &&
|
|
112
|
+
typeof row.name === 'string' &&
|
|
113
|
+
(row.description === undefined || typeof row.description === 'string'))) {
|
|
114
|
+
throw invalidInput('translations must be a list of { languageCode, name, description? } with strings.');
|
|
115
|
+
}
|
|
116
|
+
return value.map((row) => ({
|
|
117
|
+
languageCode: String(row.languageCode),
|
|
118
|
+
name: String(row.name),
|
|
119
|
+
description: typeof row.description === 'string' ? row.description : '',
|
|
120
|
+
}));
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* The fields both writes share, in the engine's shape: the three store switches under
|
|
124
|
+
* `customFields`. The range of `extraParcelPrice` is the engine's to judge.
|
|
125
|
+
*/
|
|
126
|
+
function sharedInput(input) {
|
|
127
|
+
const out = {};
|
|
128
|
+
if (input.code !== undefined) {
|
|
129
|
+
if (typeof input.code !== 'string' || input.code === '') {
|
|
130
|
+
throw invalidInput('code must be a non-empty string.');
|
|
131
|
+
}
|
|
132
|
+
out.code = input.code;
|
|
133
|
+
}
|
|
134
|
+
if (input.fulfillmentHandler !== undefined) {
|
|
135
|
+
if (typeof input.fulfillmentHandler !== 'string' || input.fulfillmentHandler === '') {
|
|
136
|
+
throw invalidInput('fulfillmentHandler must be a fulfillment handler code.');
|
|
137
|
+
}
|
|
138
|
+
out.fulfillmentHandler = input.fulfillmentHandler;
|
|
139
|
+
}
|
|
140
|
+
if (input.checker === null) {
|
|
141
|
+
throw invalidInput('Every shipping method has a checker: send default-shipping-eligibility-checker for "everyone".');
|
|
142
|
+
}
|
|
143
|
+
if (input.checker !== undefined)
|
|
144
|
+
out.checker = operationInput(input.checker, 'checker');
|
|
145
|
+
if (input.calculator !== undefined) {
|
|
146
|
+
out.calculator = operationInput(input.calculator, 'calculator');
|
|
147
|
+
}
|
|
148
|
+
if (input.translations !== undefined)
|
|
149
|
+
out.translations = translationsInput(input.translations);
|
|
150
|
+
const customFields = {};
|
|
151
|
+
for (const flag of ['enabled', 'enabledInStorefront']) {
|
|
152
|
+
const value = input[flag];
|
|
153
|
+
if (value === undefined)
|
|
154
|
+
continue;
|
|
155
|
+
if (typeof value !== 'boolean')
|
|
156
|
+
throw invalidInput(`${flag} must be true or false.`);
|
|
157
|
+
customFields[flag] = value;
|
|
158
|
+
}
|
|
159
|
+
const extra = input.extraParcelPrice;
|
|
160
|
+
if (extra !== undefined) {
|
|
161
|
+
if (extra !== null && (typeof extra !== 'number' || !Number.isSafeInteger(extra))) {
|
|
162
|
+
throw invalidInput('extraParcelPrice must be a whole number of minor units, or null.');
|
|
163
|
+
}
|
|
164
|
+
customFields.extraParcelPrice = extra;
|
|
165
|
+
}
|
|
166
|
+
if (Object.keys(customFields).length > 0)
|
|
167
|
+
out.customFields = customFields;
|
|
168
|
+
return out;
|
|
169
|
+
}
|
|
170
|
+
const toSummary = (method) => ({
|
|
171
|
+
id: method.id,
|
|
172
|
+
code: method.code,
|
|
173
|
+
name: method.name,
|
|
174
|
+
fulfillmentHandlerCode: method.fulfillmentHandlerCode,
|
|
175
|
+
calculator: method.calculator,
|
|
176
|
+
// A switch the engine did not answer (a method stored before it existed) reads as ON: the
|
|
177
|
+
// checkout's own rule (`shipping-method-enabled.ts` in the shipping-calculator plugin) is the same fail-open one.
|
|
178
|
+
enabled: method.customFields?.enabled !== false,
|
|
179
|
+
enabledInStorefront: method.customFields?.enabledInStorefront !== false,
|
|
180
|
+
createdAt: method.createdAt,
|
|
181
|
+
updatedAt: method.updatedAt,
|
|
182
|
+
});
|
|
183
|
+
const toMethod = (method) => {
|
|
184
|
+
const extra = method.customFields?.extraParcelPrice;
|
|
185
|
+
return {
|
|
186
|
+
...toSummary(method),
|
|
187
|
+
description: method.description,
|
|
188
|
+
checker: method.checker,
|
|
189
|
+
translations: method.translations,
|
|
190
|
+
exactManaged: method.customFields?.exactManaged === true,
|
|
191
|
+
extraParcelPrice: typeof extra === 'number' ? extra : null,
|
|
192
|
+
};
|
|
193
|
+
};
|
|
194
|
+
const requireId = (id) => {
|
|
195
|
+
if (typeof id !== 'string' || id === '')
|
|
196
|
+
throw invalidInput('id must be a shipping method id.');
|
|
197
|
+
return id;
|
|
198
|
+
};
|
|
199
|
+
export function createShippingMethodsModule(transport) {
|
|
200
|
+
return {
|
|
201
|
+
list(options) {
|
|
202
|
+
return vendureListPage(transport, SHIPPING_METHODS_QUERY, 'shippingMethods', (skip, take) => ({ options: { skip, take, sort: BY_ID } }), { ...options, limit: options?.limit ?? DEFAULT_PAGE_SIZE }, toSummary);
|
|
203
|
+
},
|
|
204
|
+
async get(id, options) {
|
|
205
|
+
const method = await fieldOf(transport, SHIPPING_METHOD_QUERY, 'shippingMethod', { id: requireId(id) }, options);
|
|
206
|
+
return method ? toMethod(method) : null;
|
|
207
|
+
},
|
|
208
|
+
async create(input, options) {
|
|
209
|
+
if (!isObject(input))
|
|
210
|
+
throw invalidInput('A new shipping method must be an object.');
|
|
211
|
+
if (input.code === undefined)
|
|
212
|
+
throw invalidInput('A shipping method needs a code.');
|
|
213
|
+
if (input.fulfillmentHandler === undefined) {
|
|
214
|
+
throw invalidInput('A shipping method needs a fulfillment handler.');
|
|
215
|
+
}
|
|
216
|
+
if (input.checker === undefined)
|
|
217
|
+
throw invalidInput('A shipping method needs a checker.');
|
|
218
|
+
if (input.calculator === undefined) {
|
|
219
|
+
throw invalidInput('A shipping method needs a calculator.');
|
|
220
|
+
}
|
|
221
|
+
if (input.translations === undefined) {
|
|
222
|
+
throw invalidInput('A shipping method needs its name in at least one language.');
|
|
223
|
+
}
|
|
224
|
+
return toMethod(await fieldOf(transport, CREATE_SHIPPING_METHOD_MUTATION, 'createShippingMethod', { input: sharedInput(input) }, options));
|
|
225
|
+
},
|
|
226
|
+
async update(id, patch, options) {
|
|
227
|
+
const methodId = requireId(id);
|
|
228
|
+
if (!isObject(patch))
|
|
229
|
+
throw invalidInput('The change to a shipping method must be an object.');
|
|
230
|
+
const input = sharedInput(patch);
|
|
231
|
+
// The engine's input REQUIRES a translations list; an empty one changes no name.
|
|
232
|
+
return toMethod(await fieldOf(transport, UPDATE_SHIPPING_METHOD_MUTATION, 'updateShippingMethod', { input: { id: methodId, translations: [], ...input } }, options));
|
|
233
|
+
},
|
|
234
|
+
async delete(id, options) {
|
|
235
|
+
assertDeleted('SHIPPING_METHOD_NOT_DELETED', [
|
|
236
|
+
await fieldOf(transport, DELETE_SHIPPING_METHOD_MUTATION, 'deleteShippingMethod', { id: requireId(id) }, options),
|
|
237
|
+
]);
|
|
238
|
+
},
|
|
239
|
+
};
|
|
240
|
+
}
|