@sixtyninecommerce/admin 0.8.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2739 -17
- package/dist/api-tokens/index.d.ts +15 -5
- package/dist/api-tokens/index.js +1 -0
- package/dist/apps/event-subscription.d.ts +59 -0
- package/dist/apps/event-subscription.js +32 -0
- package/dist/apps/hook-secret.d.ts +22 -0
- package/dist/apps/hook-secret.js +36 -0
- package/dist/apps/index.d.ts +54 -3
- package/dist/apps/index.js +17 -0
- package/dist/audit/index.d.ts +7 -0
- package/dist/auth/queries.d.ts +5 -1
- package/dist/auth/queries.js +6 -0
- package/dist/bulk/index.d.ts +323 -0
- package/dist/bulk/index.js +162 -0
- package/dist/catalog/assets.d.ts +147 -0
- package/dist/catalog/assets.js +164 -0
- package/dist/catalog/collections.d.ts +109 -0
- package/dist/catalog/collections.js +91 -0
- package/dist/catalog/facets.d.ts +103 -0
- package/dist/catalog/facets.js +100 -0
- package/dist/catalog/index.d.ts +115 -0
- package/dist/catalog/index.js +21 -0
- package/dist/catalog/options.d.ts +89 -0
- package/dist/catalog/options.js +88 -0
- package/dist/catalog/products-bulk.d.ts +21 -0
- package/dist/catalog/products-bulk.js +58 -0
- package/dist/catalog/products.d.ts +650 -0
- package/dist/catalog/products.js +458 -0
- package/dist/catalog/properties.d.ts +234 -0
- package/dist/catalog/properties.js +158 -0
- package/dist/catalog/types.d.ts +174 -0
- package/dist/catalog/types.js +141 -0
- package/dist/catalog/variants.d.ts +324 -0
- package/dist/catalog/variants.js +288 -0
- package/dist/catalog/visibility.d.ts +57 -0
- package/dist/catalog/visibility.js +59 -0
- package/dist/client.d.ts +192 -3
- package/dist/client.js +87 -12
- package/dist/compliance/index.d.ts +88 -0
- package/dist/compliance/index.js +88 -0
- package/dist/customer-data-redaction.d.ts +14 -0
- package/dist/customer-data-redaction.js +22 -0
- package/dist/customers/index.d.ts +428 -17
- package/dist/customers/index.js +322 -15
- package/dist/documents/index.d.ts +513 -16
- package/dist/documents/index.js +229 -3
- package/dist/events/index.d.ts +259 -0
- package/dist/events/index.js +147 -0
- package/dist/feeds/ai.d.ts +46 -0
- package/dist/feeds/ai.js +43 -0
- package/dist/feeds/categories.d.ts +50 -0
- package/dist/feeds/categories.js +56 -0
- package/dist/feeds/configs.d.ts +54 -0
- package/dist/feeds/configs.js +85 -0
- package/dist/feeds/identifier-fields.d.ts +25 -0
- package/dist/feeds/identifier-fields.js +29 -0
- package/dist/feeds/index.d.ts +54 -0
- package/dist/feeds/index.js +46 -0
- package/dist/feeds/preserve.d.ts +38 -0
- package/dist/feeds/preserve.js +74 -0
- package/dist/feeds/rules.d.ts +42 -0
- package/dist/feeds/rules.js +56 -0
- package/dist/feeds/types.d.ts +654 -0
- package/dist/feeds/types.js +135 -0
- package/dist/fulfillments/index.d.ts +437 -0
- package/dist/fulfillments/index.js +534 -0
- package/dist/heads/index.d.ts +60 -0
- package/dist/heads/index.js +45 -0
- package/dist/hooks/assignments.d.ts +90 -0
- package/dist/hooks/assignments.js +53 -0
- package/dist/hooks/index.d.ts +97 -0
- package/dist/hooks/index.js +76 -0
- package/dist/index.d.ts +95 -9
- package/dist/index.js +21 -1
- package/dist/integrations/index.d.ts +14 -0
- package/dist/integrations/index.js +9 -0
- package/dist/integrations/pipelines.d.ts +113 -0
- package/dist/integrations/pipelines.js +62 -0
- package/dist/integrations/types.d.ts +176 -0
- package/dist/integrations/types.js +8 -0
- package/dist/jobs/bulk-action-run.d.ts +74 -0
- package/dist/jobs/bulk-action-run.js +69 -0
- package/dist/locations/index.d.ts +6 -2
- package/dist/meta/index.d.ts +32 -0
- package/dist/orders/bulk.d.ts +75 -0
- package/dist/orders/bulk.js +150 -0
- package/dist/orders/commit-edit.d.ts +123 -0
- package/dist/orders/commit-edit.js +142 -0
- package/dist/orders/deposit-policy.d.ts +103 -0
- package/dist/orders/deposit-policy.js +202 -0
- package/dist/orders/get.d.ts +722 -0
- package/dist/orders/get.js +530 -0
- package/dist/orders/index.d.ts +186 -21
- package/dist/orders/index.js +57 -0
- package/dist/orders/line-price-preview.d.ts +297 -0
- package/dist/orders/line-price-preview.js +50 -0
- package/dist/orders/manual-discounts.d.ts +141 -0
- package/dist/orders/manual-discounts.js +97 -0
- package/dist/orders/manual-pricing.d.ts +192 -0
- package/dist/orders/manual-pricing.js +72 -0
- package/dist/orders/order-entry.d.ts +566 -0
- package/dist/orders/order-entry.js +129 -0
- package/dist/orders/parcel-charge.d.ts +45 -0
- package/dist/orders/parcel-charge.js +55 -0
- package/dist/orders/plan-edit.d.ts +489 -0
- package/dist/orders/plan-edit.js +93 -0
- package/dist/orders/status.d.ts +235 -0
- package/dist/orders/status.js +96 -0
- package/dist/orders/surcharges.d.ts +75 -2
- package/dist/orders/surcharges.js +110 -4
- package/dist/payments/account-credit.d.ts +171 -0
- package/dist/payments/account-credit.js +107 -0
- package/dist/payments/index.d.ts +241 -9
- package/dist/payments/index.js +92 -0
- package/dist/payments/methods.d.ts +37 -0
- package/dist/payments/methods.js +33 -0
- package/dist/platform/catalog.d.ts +148 -0
- package/dist/platform/catalog.js +74 -0
- package/dist/platform/entitlements.d.ts +111 -0
- package/dist/platform/entitlements.js +86 -0
- package/dist/platform/legal-entities.d.ts +139 -0
- package/dist/platform/legal-entities.js +126 -0
- package/dist/platform/legal-entity-numbering.d.ts +103 -0
- package/dist/platform/legal-entity-numbering.js +101 -0
- package/dist/platform/organization-membership.d.ts +80 -0
- package/dist/platform/organization-membership.js +71 -0
- package/dist/platform/organization-settings.d.ts +33 -0
- package/dist/platform/organization-settings.js +42 -0
- package/dist/platform/organization-sharing.d.ts +172 -0
- package/dist/platform/organization-sharing.js +120 -0
- package/dist/platform/organizations.d.ts +205 -0
- package/dist/platform/organizations.js +180 -0
- package/dist/platform/provision-store.d.ts +88 -8
- package/dist/platform/provision-store.js +12 -0
- package/dist/platform/reports.d.ts +46 -0
- package/dist/platform/reports.js +31 -0
- package/dist/platform/server-metrics.d.ts +58 -0
- package/dist/platform/server-metrics.js +11 -0
- package/dist/pos/index.d.ts +567 -0
- package/dist/pos/index.js +205 -0
- package/dist/pricing/index.d.ts +408 -0
- package/dist/pricing/index.js +353 -0
- package/dist/pricing/vouchers.d.ts +103 -0
- package/dist/pricing/vouchers.js +69 -0
- package/dist/provenance/index.d.ts +93 -0
- package/dist/provenance/index.js +53 -0
- package/dist/reports/index.d.ts +199 -0
- package/dist/reports/index.js +154 -0
- package/dist/returns/index.d.ts +225 -0
- package/dist/returns/index.js +160 -0
- package/dist/search/index.d.ts +99 -0
- package/dist/search/index.js +62 -0
- package/dist/shipping/parcel-count-suggestion.d.ts +52 -0
- package/dist/shipping/parcel-count-suggestion.js +73 -0
- package/dist/shipping/parcel-rule.d.ts +48 -0
- package/dist/shipping/parcel-rule.js +91 -0
- package/dist/stock/index.d.ts +166 -0
- package/dist/stock/index.js +115 -0
- package/dist/stock/transfers.d.ts +520 -0
- package/dist/stock/transfers.js +437 -0
- package/dist/stored-value/index.d.ts +154 -0
- package/dist/stored-value/index.js +130 -0
- package/dist/tenant/capabilities.d.ts +10 -0
- package/dist/tenant/entitlements.d.ts +35 -0
- package/dist/tenant/entitlements.js +15 -0
- package/dist/tenant/field-groups.d.ts +64 -0
- package/dist/tenant/field-groups.js +34 -0
- package/dist/tenant/legal-entity.d.ts +32 -0
- package/dist/tenant/legal-entity.js +41 -0
- package/dist/tenant/notifications.d.ts +148 -0
- package/dist/tenant/notifications.js +79 -0
- package/dist/tenant/retention.d.ts +10 -4
- package/dist/tenant/settings-sources.d.ts +103 -0
- package/dist/tenant/settings-sources.js +92 -0
- package/dist/types.d.ts +86 -11
- package/dist/uploads/index.d.ts +11 -7
- package/dist/users/index.d.ts +104 -0
- package/dist/users/index.js +80 -0
- package/package.json +3 -2
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `admin.orders.bulk`, `admin.orders.bulkRun` and `admin.orders.delete` (headless spec §7
|
|
3
|
+
* `orders.bulk`, plan W9-43; rulings G19, G28, G29).
|
|
4
|
+
*/
|
|
5
|
+
import { idempotencyKey, ValidationError } from '@sixtyninecommerce/core';
|
|
6
|
+
import { assertDeleted, fieldOf } from '../catalog/types.js';
|
|
7
|
+
import { bulkActionHandle, } from '../jobs/bulk-action-run.js';
|
|
8
|
+
export const ORDER_BULK_ACTIONS = [
|
|
9
|
+
'TRANSITION',
|
|
10
|
+
'CANCEL',
|
|
11
|
+
'DELETE',
|
|
12
|
+
'MARK_PAID',
|
|
13
|
+
];
|
|
14
|
+
/** The engine's bound on one bulk run (`MAX_BULK_ACTION_ITEMS`). Split a larger selection. */
|
|
15
|
+
export const MAX_BULK_ORDERS = 50;
|
|
16
|
+
/**
|
|
17
|
+
* The states a TRANSITION never names, each with its own action or door: `Cancelled` (CANCEL),
|
|
18
|
+
* `PaymentSettled` (MARK_PAID), `Modifying` (the order edit) and `AddingItems`.
|
|
19
|
+
*/
|
|
20
|
+
export const ORDER_BULK_TRANSITION_REFUSED = [
|
|
21
|
+
'Cancelled',
|
|
22
|
+
'PaymentSettled',
|
|
23
|
+
'Modifying',
|
|
24
|
+
'AddingItems',
|
|
25
|
+
];
|
|
26
|
+
export const BULK_ORDER_ACTION_MUTATION = /* GraphQL */ `
|
|
27
|
+
mutation BulkOrderAction($input: BulkOrderActionInput!, $idempotencyKey: String) {
|
|
28
|
+
bulkOrderAction(input: $input, idempotencyKey: $idempotencyKey) {
|
|
29
|
+
runId
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
`;
|
|
33
|
+
/**
|
|
34
|
+
* The run read with the order fields. Its own document, so `catalog.products.bulkRun` keeps
|
|
35
|
+
* selecting only what an engine without the orders kind has.
|
|
36
|
+
*/
|
|
37
|
+
export const ORDER_BULK_RUN_QUERY = /* GraphQL */ `
|
|
38
|
+
query OrderBulkRun($runId: ID!) {
|
|
39
|
+
bulkActionRun(runId: $runId) {
|
|
40
|
+
id
|
|
41
|
+
kind
|
|
42
|
+
action
|
|
43
|
+
status
|
|
44
|
+
total
|
|
45
|
+
done
|
|
46
|
+
items {
|
|
47
|
+
id
|
|
48
|
+
status
|
|
49
|
+
code
|
|
50
|
+
message
|
|
51
|
+
newId
|
|
52
|
+
reasonCode
|
|
53
|
+
captured {
|
|
54
|
+
paymentId
|
|
55
|
+
state
|
|
56
|
+
code
|
|
57
|
+
message
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
failure {
|
|
61
|
+
code
|
|
62
|
+
message
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
`;
|
|
67
|
+
export const DELETE_ORDER_MUTATION = /* GraphQL */ `
|
|
68
|
+
mutation DeleteOrder($orderId: ID!) {
|
|
69
|
+
deleteOrder(orderId: $orderId) {
|
|
70
|
+
result
|
|
71
|
+
message
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
`;
|
|
75
|
+
function refuse(message) {
|
|
76
|
+
return new ValidationError({
|
|
77
|
+
code: 'BULK_ACTION_INPUT_INVALID',
|
|
78
|
+
message,
|
|
79
|
+
status: 400,
|
|
80
|
+
service: 'vendure',
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
/** The engine's own checks of a submit (`BULK_ACTION_INPUT_INVALID`), before sending. */
|
|
84
|
+
function assertBulkInput(input) {
|
|
85
|
+
if (!ORDER_BULK_ACTIONS.includes(input?.action)) {
|
|
86
|
+
throw refuse(`"${String(input?.action)}" is not a bulk order action: ${ORDER_BULK_ACTIONS.join(', ')}. Nothing was sent.`);
|
|
87
|
+
}
|
|
88
|
+
const ids = input.orderIds;
|
|
89
|
+
if (!Array.isArray(ids) || ids.length === 0) {
|
|
90
|
+
throw refuse('Name at least one order. Nothing was sent.');
|
|
91
|
+
}
|
|
92
|
+
if (ids.length > MAX_BULK_ORDERS) {
|
|
93
|
+
throw refuse(`Name at most ${String(MAX_BULK_ORDERS)} orders in one bulk action; this one named ${String(ids.length)}. Split the selection into several actions. Nothing was sent.`);
|
|
94
|
+
}
|
|
95
|
+
if (new Set(ids.map((id) => String(id).toLowerCase())).size !== ids.length) {
|
|
96
|
+
throw refuse('The selection names an order more than once. Name each order once. Nothing was sent.');
|
|
97
|
+
}
|
|
98
|
+
const toState = input.toState?.trim() ?? '';
|
|
99
|
+
if (input.action === 'TRANSITION') {
|
|
100
|
+
if (toState === '')
|
|
101
|
+
throw refuse('Name the status to move the orders to. Nothing was sent.');
|
|
102
|
+
if (ORDER_BULK_TRANSITION_REFUSED.includes(toState)) {
|
|
103
|
+
throw refuse(`Orders are not moved to "${toState}" by a status change${toState === 'Cancelled'
|
|
104
|
+
? ': use CANCEL'
|
|
105
|
+
: toState === 'PaymentSettled'
|
|
106
|
+
? ': use MARK_PAID'
|
|
107
|
+
: ''}. Nothing was sent.`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
else if (toState !== '') {
|
|
111
|
+
throw refuse(`toState is only for TRANSITION, not ${input.action}. Nothing was sent.`);
|
|
112
|
+
}
|
|
113
|
+
if ((input.reason?.trim() ?? '') !== '' && input.action !== 'CANCEL') {
|
|
114
|
+
throw refuse(`reason is only for CANCEL, not ${input.action}. Nothing was sent.`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/** One read of a bulk order run. A run of another store, or a missing one, is `NotFoundError`. */
|
|
118
|
+
export function readOrderBulkRun(transport, runId, options) {
|
|
119
|
+
return fieldOf(transport, ORDER_BULK_RUN_QUERY, 'bulkActionRun', { runId }, options);
|
|
120
|
+
}
|
|
121
|
+
/** `orders.bulk`; see `OrdersModule.bulk`. */
|
|
122
|
+
export async function bulkOrders(transport, input, options) {
|
|
123
|
+
assertBulkInput(input);
|
|
124
|
+
// Minted when the caller gave none, so a retry of THIS call answers the same run instead of
|
|
125
|
+
// starting a second one (and capturing nothing twice).
|
|
126
|
+
const key = options?.idempotencyKey ?? idempotencyKey();
|
|
127
|
+
const toState = input.toState?.trim();
|
|
128
|
+
const reason = input.reason?.trim();
|
|
129
|
+
const { runId } = await fieldOf(transport, BULK_ORDER_ACTION_MUTATION, 'bulkOrderAction', {
|
|
130
|
+
input: {
|
|
131
|
+
action: input.action,
|
|
132
|
+
orderIds: [...input.orderIds],
|
|
133
|
+
// Omitted, never null, when not given: an absent option stays off the wire.
|
|
134
|
+
...(toState ? { toState } : {}),
|
|
135
|
+
...(reason ? { reason } : {}),
|
|
136
|
+
},
|
|
137
|
+
idempotencyKey: key,
|
|
138
|
+
}, { ...options, idempotencyKey: key });
|
|
139
|
+
return bulkActionHandle(transport, runId, options, (id, call) => readOrderBulkRun(transport, id, call));
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* `orders.delete`; see `OrdersModule.delete`. The engine's `NOT_DELETED` is raised as every other
|
|
143
|
+
* `*_NOT_DELETED` refusal of this SDK is (`assertDeleted`): a `ConflictError` with the engine's own
|
|
144
|
+
* reason.
|
|
145
|
+
*/
|
|
146
|
+
export async function deleteOrder(transport, orderId, options) {
|
|
147
|
+
assertDeleted('ORDER_NOT_DELETED', [
|
|
148
|
+
await fieldOf(transport, DELETE_ORDER_MUTATION, 'deleteOrder', { orderId }, options),
|
|
149
|
+
]);
|
|
150
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { type Transport } from '@sixtyninecommerce/core';
|
|
2
|
+
import type { Call } from '../types.js';
|
|
3
|
+
import type { OrderEditDiff, OrderEditPlan } from './plan-edit.js';
|
|
4
|
+
import type { OrderOverpaymentChoice } from './surcharges.js';
|
|
5
|
+
/** The order as a committed edit left it: what a head refreshes its view from. */
|
|
6
|
+
export interface OrderEditCommittedOrder {
|
|
7
|
+
id: string;
|
|
8
|
+
code: string;
|
|
9
|
+
/** The order's state: for an edit that raised a paid order's total online, `ArrangingAdditionalPayment`. */
|
|
10
|
+
state: string;
|
|
11
|
+
currencyCode: string;
|
|
12
|
+
/** Minor units. */
|
|
13
|
+
totalWithTax: number;
|
|
14
|
+
}
|
|
15
|
+
/** The refund an edit ended with. */
|
|
16
|
+
export interface OrderEditCommittedRefund {
|
|
17
|
+
id: string;
|
|
18
|
+
/**
|
|
19
|
+
* The payment provider's answer: `Pending` until a gateway confirms it, `Settled` for money
|
|
20
|
+
* returned by hand (an offline method). `Failed` is never answered here: a refusal throws.
|
|
21
|
+
*/
|
|
22
|
+
state: string;
|
|
23
|
+
/** Minor units, as the refund row records it. */
|
|
24
|
+
total: number;
|
|
25
|
+
paymentId: string;
|
|
26
|
+
}
|
|
27
|
+
/** How the edit settled its plan's `overpayment`. */
|
|
28
|
+
export interface OrderEditOverpaymentSettlement {
|
|
29
|
+
choice: OrderOverpaymentChoice;
|
|
30
|
+
/** Minor units the choice settled: the plan's `overpayment.amount`. */
|
|
31
|
+
amount: number;
|
|
32
|
+
currencyCode: string;
|
|
33
|
+
/** For `ACCOUNT_CREDIT`: the receipts on the customer's account, one per payment it came from. */
|
|
34
|
+
unappliedCashIds: string[];
|
|
35
|
+
}
|
|
36
|
+
export interface OrderEditCommit {
|
|
37
|
+
order: OrderEditCommittedOrder;
|
|
38
|
+
/**
|
|
39
|
+
* The plan that was carried out, as `planEdit` answered it. A repeat under the same
|
|
40
|
+
* `idempotencyKey` answers the plan as it was, and the order and refund as they are now.
|
|
41
|
+
*/
|
|
42
|
+
plan: OrderEditPlan;
|
|
43
|
+
/**
|
|
44
|
+
* The ONE refund the edit ended with, made last, or `null`: the plan's `refund`, or with
|
|
45
|
+
* `overpaymentChoice: 'REFUND'` its `overpayment.refundWhenChosen`.
|
|
46
|
+
*/
|
|
47
|
+
refund: OrderEditCommittedRefund | null;
|
|
48
|
+
/** How the plan's `overpayment` was settled; `null` when it had none. */
|
|
49
|
+
overpayment: OrderEditOverpaymentSettlement | null;
|
|
50
|
+
}
|
|
51
|
+
export interface CommitOrderEditCall extends Call {
|
|
52
|
+
/** The `planHash` of the plan the merchant saw (`planEdit`). Required. */
|
|
53
|
+
expectedPlanHash: string;
|
|
54
|
+
/**
|
|
55
|
+
* One key per attempt to save, re-sent unchanged on a retry of THAT attempt. Pass your own:
|
|
56
|
+
* left out, one is made up (a UUIDv7) and returned as `idempotencyKey` on the result, but on a
|
|
57
|
+
* thrown error only as an untyped own property of the error. Never reuse a key for a
|
|
58
|
+
* different edit or a fresh attempt.
|
|
59
|
+
*/
|
|
60
|
+
idempotencyKey?: string;
|
|
61
|
+
/**
|
|
62
|
+
* How the merchant settles the plan's `overpayment` (a surcharge lowered or removed on a paid
|
|
63
|
+
* order): `REFUND` returns it to the customer's payment in the same one refund as the plan's
|
|
64
|
+
* `refund`; `ACCOUNT_CREDIT` credits it to the customer's account first, and the plan's
|
|
65
|
+
* `refund` follows. Required when the plan has an `overpayment`; ignored when it has none.
|
|
66
|
+
* Part of the request: a retry under the same key re-sends the same choice.
|
|
67
|
+
*/
|
|
68
|
+
overpaymentChoice?: OrderOverpaymentChoice;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Its own document on purpose: `commitOrderEdit` exists only on an engine with the order-edit
|
|
72
|
+
* commit, so on an older one this call alone fails validation and no other order call does.
|
|
73
|
+
*/
|
|
74
|
+
export declare const COMMIT_ORDER_EDIT_MUTATION = "\n mutation CommitOrderEdit(\n $orderId: ID!\n $diff: OrderEditDiffInput!\n $expectedPlanHash: String!\n $idempotencyKey: String!\n $overpaymentChoice: OrderOverpaymentChoice\n ) {\n commitOrderEdit(\n orderId: $orderId\n diff: $diff\n expectedPlanHash: $expectedPlanHash\n idempotencyKey: $idempotencyKey\n overpaymentChoice: $overpaymentChoice\n ) {\n order {\n id\n code\n state\n currencyCode\n totalWithTax\n }\n plan {\n orderId\n planHash\n orderVersion\n steps {\n kind\n input\n moneyEffect\n }\n refund {\n paymentId\n amount\n }\n overpayment {\n amount\n currencyCode\n surchargeIds\n refund {\n available\n code\n message\n }\n accountCredit {\n available\n code\n message\n }\n refundWhenChosen {\n paymentId\n amount\n }\n }\n returnState\n blockers {\n code\n message\n details\n }\n notices {\n code\n message\n details\n }\n payment {\n collected\n reason\n method\n }\n preview {\n currencyCode\n subTotal\n subTotalWithTax\n shipping\n shippingWithTax\n surcharges\n surchargesWithTax\n total\n totalWithTax\n taxSummary {\n description\n taxRate\n taxBase\n taxTotal\n }\n totalWithTaxBefore\n }\n }\n refund {\n id\n state\n total\n paymentId\n }\n overpayment {\n choice\n amount\n currencyCode\n unappliedCashIds\n }\n }\n }\n";
|
|
75
|
+
export interface OrderCommitEditModule {
|
|
76
|
+
/**
|
|
77
|
+
* Carry out an edit of a placed order as its plan (`planEdit`) states it, once. Needs
|
|
78
|
+
* `UpdateOrder`, and an engine that serves `commitOrderEdit`.
|
|
79
|
+
*
|
|
80
|
+
* Hand it the SAME `diff` you planned and the plan's `planHash` as `expectedPlanHash`. The
|
|
81
|
+
* engine runs the plan again inside one transaction and keeps that run only when its hash is
|
|
82
|
+
* yours and it has no blockers; otherwise nothing of the edit is saved. The plan's refund, if
|
|
83
|
+
* it has one, is made LAST, after every other step, through the platform's refund guard.
|
|
84
|
+
*
|
|
85
|
+
* When the plan has an `overpayment` (a surcharge lowered or removed on a paid order), pass the
|
|
86
|
+
* merchant's `overpaymentChoice`: `REFUND` makes the plan's `overpayment.refundWhenChosen`
|
|
87
|
+
* (one refund of both parts); `ACCOUNT_CREDIT` credits the share to the customer's account,
|
|
88
|
+
* then makes the plan's `refund`.
|
|
89
|
+
*
|
|
90
|
+
* `idempotencyKey`: one per attempt to save. A retry of that attempt (a timeout, a dropped
|
|
91
|
+
* connection) re-sends it and gets the first answer back, with nothing done twice. Left out,
|
|
92
|
+
* one is made up and returned as `idempotencyKey` on the result and on a thrown error.
|
|
93
|
+
*
|
|
94
|
+
* Refusals, with nothing saved:
|
|
95
|
+
* - `ORDER_EDIT_PLAN_CONFLICT` (`ConflictError`, 409): the order changed since the plan (another
|
|
96
|
+
* change landed, a price or tax answer moved). Plan again, show the merchant the new totals,
|
|
97
|
+
* and save under a NEW key.
|
|
98
|
+
* - `ORDER_EDIT_BLOCKED` (`BusinessRuleError`, 422): the plan has blockers; `error.details`
|
|
99
|
+
* lists them, `code` = the blocker's code.
|
|
100
|
+
* - `ORDER_BUSY` (`ConflictError`, 409): another change of the order was being applied. Retry
|
|
101
|
+
* under the same key.
|
|
102
|
+
* - `ORDER_EDIT_OVERPAYMENT_CHOICE_REQUIRED` (`BusinessRuleError`, 422): the plan has an
|
|
103
|
+
* `overpayment` and no `overpaymentChoice` was given. Ask the merchant, then save under a
|
|
104
|
+
* NEW key (the choice is part of the request).
|
|
105
|
+
* - A closed option chosen anyway: its own refusal (`REFUND_EXCEEDS_PAYMENT`,
|
|
106
|
+
* `REFUND_METHOD_UNAVAILABLE`, `AR_ACCOUNT_IN_ERP`, `AR_LEDGER_NOT_ENABLED`, …), as the
|
|
107
|
+
* plan's option names it.
|
|
108
|
+
* - `IDEMPOTENCY_CONFLICT` (409): the key was used for another request.
|
|
109
|
+
* - `planEdit`'s refusals of the call, and the refund guard's `REFUND_*` when the refund was
|
|
110
|
+
* refused before money moved.
|
|
111
|
+
*
|
|
112
|
+
* `IDEMPOTENCY_OUTCOME_UNKNOWN` (`error.outcomeUnknown`): the refund reached the payment
|
|
113
|
+
* provider and its answer never came. The edit is NOT saved; the refund attempt is recorded
|
|
114
|
+
* and reserved on the order, and this key answers the same until the provider's answer is
|
|
115
|
+
* recorded (`admin.payments.reconcileRefund`), which frees it. Until then an edit that gives
|
|
116
|
+
* money back is blocked (`ORDER_OVERPAYMENT_REFUND_UNRECONCILED`). Then plan and save again:
|
|
117
|
+
* a refund recorded as made is not made a second time.
|
|
118
|
+
*/
|
|
119
|
+
commitEdit(orderId: string, diff: OrderEditDiff, options: CommitOrderEditCall): Promise<OrderEditCommit & {
|
|
120
|
+
idempotencyKey: string;
|
|
121
|
+
}>;
|
|
122
|
+
}
|
|
123
|
+
export declare function createOrderCommitEditModule(transport: Transport): OrderCommitEditModule;
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { idempotencyKey as mintIdempotencyKey } from '@sixtyninecommerce/core';
|
|
2
|
+
/**
|
|
3
|
+
* Its own document on purpose: `commitOrderEdit` exists only on an engine with the order-edit
|
|
4
|
+
* commit, so on an older one this call alone fails validation and no other order call does.
|
|
5
|
+
*/
|
|
6
|
+
export const COMMIT_ORDER_EDIT_MUTATION = /* GraphQL */ `
|
|
7
|
+
mutation CommitOrderEdit(
|
|
8
|
+
$orderId: ID!
|
|
9
|
+
$diff: OrderEditDiffInput!
|
|
10
|
+
$expectedPlanHash: String!
|
|
11
|
+
$idempotencyKey: String!
|
|
12
|
+
$overpaymentChoice: OrderOverpaymentChoice
|
|
13
|
+
) {
|
|
14
|
+
commitOrderEdit(
|
|
15
|
+
orderId: $orderId
|
|
16
|
+
diff: $diff
|
|
17
|
+
expectedPlanHash: $expectedPlanHash
|
|
18
|
+
idempotencyKey: $idempotencyKey
|
|
19
|
+
overpaymentChoice: $overpaymentChoice
|
|
20
|
+
) {
|
|
21
|
+
order {
|
|
22
|
+
id
|
|
23
|
+
code
|
|
24
|
+
state
|
|
25
|
+
currencyCode
|
|
26
|
+
totalWithTax
|
|
27
|
+
}
|
|
28
|
+
plan {
|
|
29
|
+
orderId
|
|
30
|
+
planHash
|
|
31
|
+
orderVersion
|
|
32
|
+
steps {
|
|
33
|
+
kind
|
|
34
|
+
input
|
|
35
|
+
moneyEffect
|
|
36
|
+
}
|
|
37
|
+
refund {
|
|
38
|
+
paymentId
|
|
39
|
+
amount
|
|
40
|
+
}
|
|
41
|
+
overpayment {
|
|
42
|
+
amount
|
|
43
|
+
currencyCode
|
|
44
|
+
surchargeIds
|
|
45
|
+
refund {
|
|
46
|
+
available
|
|
47
|
+
code
|
|
48
|
+
message
|
|
49
|
+
}
|
|
50
|
+
accountCredit {
|
|
51
|
+
available
|
|
52
|
+
code
|
|
53
|
+
message
|
|
54
|
+
}
|
|
55
|
+
refundWhenChosen {
|
|
56
|
+
paymentId
|
|
57
|
+
amount
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
returnState
|
|
61
|
+
blockers {
|
|
62
|
+
code
|
|
63
|
+
message
|
|
64
|
+
details
|
|
65
|
+
}
|
|
66
|
+
notices {
|
|
67
|
+
code
|
|
68
|
+
message
|
|
69
|
+
details
|
|
70
|
+
}
|
|
71
|
+
payment {
|
|
72
|
+
collected
|
|
73
|
+
reason
|
|
74
|
+
method
|
|
75
|
+
}
|
|
76
|
+
preview {
|
|
77
|
+
currencyCode
|
|
78
|
+
subTotal
|
|
79
|
+
subTotalWithTax
|
|
80
|
+
shipping
|
|
81
|
+
shippingWithTax
|
|
82
|
+
surcharges
|
|
83
|
+
surchargesWithTax
|
|
84
|
+
total
|
|
85
|
+
totalWithTax
|
|
86
|
+
taxSummary {
|
|
87
|
+
description
|
|
88
|
+
taxRate
|
|
89
|
+
taxBase
|
|
90
|
+
taxTotal
|
|
91
|
+
}
|
|
92
|
+
totalWithTaxBefore
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
refund {
|
|
96
|
+
id
|
|
97
|
+
state
|
|
98
|
+
total
|
|
99
|
+
paymentId
|
|
100
|
+
}
|
|
101
|
+
overpayment {
|
|
102
|
+
choice
|
|
103
|
+
amount
|
|
104
|
+
currencyCode
|
|
105
|
+
unappliedCashIds
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
`;
|
|
110
|
+
export function createOrderCommitEditModule(transport) {
|
|
111
|
+
return {
|
|
112
|
+
async commitEdit(orderId, diff, options) {
|
|
113
|
+
const key = options.idempotencyKey ?? mintIdempotencyKey();
|
|
114
|
+
try {
|
|
115
|
+
const data = await transport.graphql({
|
|
116
|
+
api: 'admin',
|
|
117
|
+
query: COMMIT_ORDER_EDIT_MUTATION,
|
|
118
|
+
// The diff goes out as given, never normalised (see `planEdit`): left out, `null` and
|
|
119
|
+
// a value are three different requests.
|
|
120
|
+
variables: {
|
|
121
|
+
orderId,
|
|
122
|
+
diff,
|
|
123
|
+
expectedPlanHash: options.expectedPlanHash,
|
|
124
|
+
idempotencyKey: key,
|
|
125
|
+
// Sent only when given: left out, the engine reads no choice.
|
|
126
|
+
...(options.overpaymentChoice !== undefined
|
|
127
|
+
? { overpaymentChoice: options.overpaymentChoice }
|
|
128
|
+
: {}),
|
|
129
|
+
},
|
|
130
|
+
}, { ...options, idempotencyKey: key });
|
|
131
|
+
return { ...data.commitOrderEdit, idempotencyKey: key };
|
|
132
|
+
}
|
|
133
|
+
catch (error) {
|
|
134
|
+
// The key the caller retries under, when it did not choose one.
|
|
135
|
+
if (error !== null && typeof error === 'object' && !('idempotencyKey' in error)) {
|
|
136
|
+
Object.defineProperty(error, 'idempotencyKey', { value: key, enumerable: true });
|
|
137
|
+
}
|
|
138
|
+
throw error;
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
};
|
|
142
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { type Transport } from '@sixtyninecommerce/core';
|
|
2
|
+
import type { Call } from '../types.js';
|
|
3
|
+
/** The fewest days `autoCancelAfterDays` may be; `null` means never. */
|
|
4
|
+
export declare const DEPOSIT_POLICY_AUTO_CANCEL_DAYS_MIN = 1;
|
|
5
|
+
/** The largest `storefront.minimumPercent`: a whole percentage of the order total. */
|
|
6
|
+
export declare const DEPOSIT_POLICY_MINIMUM_PERCENT_MAX = 100;
|
|
7
|
+
/** The most payment method codes `storefront.methods` may name. */
|
|
8
|
+
export declare const DEPOSIT_POLICY_STOREFRONT_METHODS_MAX = 50;
|
|
9
|
+
/** Deposits a SHOPPER pays online at checkout. Applies only while `enabled` is on as well. */
|
|
10
|
+
export interface StorefrontDepositPolicy {
|
|
11
|
+
/** The online switch. */
|
|
12
|
+
enabled: boolean;
|
|
13
|
+
/**
|
|
14
|
+
* Payment method CODES a shopper may pay a deposit with: offline methods and
|
|
15
|
+
* Mollie. Empty: none, so no shopper can pay a deposit online.
|
|
16
|
+
*/
|
|
17
|
+
methods: string[];
|
|
18
|
+
/**
|
|
19
|
+
* The least an online deposit may be, as a whole percentage (0–100) of the
|
|
20
|
+
* order's total, judged in the order's currency. Beside `minimum`, not instead
|
|
21
|
+
* of it: the larger of the two applies.
|
|
22
|
+
*/
|
|
23
|
+
minimumPercent: number;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* The store's deposit policy (`Channel.customFields.orderDepositPolicy`): whether
|
|
27
|
+
* staff may take a deposit and park an order (`orders.park`), and whether a
|
|
28
|
+
* shopper may pay one at checkout. Every field present: this is what the engine
|
|
29
|
+
* APPLIES, absent and malformed fields read as their defaults.
|
|
30
|
+
*/
|
|
31
|
+
export interface OrderDepositPolicy {
|
|
32
|
+
/**
|
|
33
|
+
* The store-wide switch. Off: a payment must cover the whole outstanding
|
|
34
|
+
* amount (`payments.record` refuses a part payment `DEPOSITS_NOT_ENABLED`) and
|
|
35
|
+
* no order can be parked.
|
|
36
|
+
*/
|
|
37
|
+
enabled: boolean;
|
|
38
|
+
/**
|
|
39
|
+
* The least a deposit must be before an order can be parked, in minor units of
|
|
40
|
+
* the store's DEFAULT currency; 0 means any deposit will do. An order in another
|
|
41
|
+
* currency is held to it converted at the rate checkout charges with.
|
|
42
|
+
*/
|
|
43
|
+
minimum: number;
|
|
44
|
+
/** Whether an order may be parked with nothing paid at all. */
|
|
45
|
+
allowZeroDepositPark: boolean;
|
|
46
|
+
/**
|
|
47
|
+
* Whether the storefront shows a parked order's reason to the shopper. Off by
|
|
48
|
+
* default: staff write that reason for staff.
|
|
49
|
+
*/
|
|
50
|
+
showParkReasonToCustomer: boolean;
|
|
51
|
+
/**
|
|
52
|
+
* RESERVED: days after which a parked order is cancelled. Saved and read back,
|
|
53
|
+
* but nothing cancels a parked order automatically yet. `null`: never.
|
|
54
|
+
*/
|
|
55
|
+
autoCancelAfterDays: number | null;
|
|
56
|
+
storefront: StorefrontDepositPolicy;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* The policy of a store that never set one, and of every field that cannot be
|
|
60
|
+
* read: deposits off, no minimum, no zero-deposit park, the reason kept from the
|
|
61
|
+
* shopper, never auto-cancelled, no online deposits.
|
|
62
|
+
*/
|
|
63
|
+
export declare const DEFAULT_ORDER_DEPOSIT_POLICY: Readonly<OrderDepositPolicy>;
|
|
64
|
+
/**
|
|
65
|
+
* The ACTIVE store's deposit policy (`admin.orders.depositPolicy`), stored as a
|
|
66
|
+
* Channel custom field and written through core `updateChannel`.
|
|
67
|
+
*
|
|
68
|
+
* Reading needs an authenticated administrator on the store; writing needs
|
|
69
|
+
* `UpdateChannel`.
|
|
70
|
+
*/
|
|
71
|
+
export interface OrderDepositPolicyModule {
|
|
72
|
+
/**
|
|
73
|
+
* The policy the engine applies, read as tolerantly as the engine reads it: a
|
|
74
|
+
* store that never set one, a malformed column and a field of the wrong type
|
|
75
|
+
* all answer their defaults ({@link DEFAULT_ORDER_DEPOSIT_POLICY}), so this is
|
|
76
|
+
* what `orders.park` and `payments.record` will hold an order to.
|
|
77
|
+
*/
|
|
78
|
+
get(options?: Call): Promise<OrderDepositPolicy>;
|
|
79
|
+
/**
|
|
80
|
+
* Replace the whole policy, and answer it as the engine now applies it.
|
|
81
|
+
*
|
|
82
|
+
* Every field is required: read it with {@link get}, change what you mean to,
|
|
83
|
+
* and send it back. Only the schema's fields are stored (anything else on the
|
|
84
|
+
* object is left out), and the method codes are stored as given.
|
|
85
|
+
*
|
|
86
|
+
* Refused before any request with `DEPOSIT_POLICY_INVALID` ({@link ValidationError},
|
|
87
|
+
* one `details[]` entry per failing field, named by its path such as
|
|
88
|
+
* `storefront.minimumPercent`): a non-boolean switch, a `minimum` that is not a
|
|
89
|
+
* whole number of 0 or more, an `autoCancelAfterDays` that is neither `null` nor
|
|
90
|
+
* a whole number of 1 or more, a `minimumPercent` outside 0–100, a blank or
|
|
91
|
+
* repeated method code, or more than 50 of them. The engine itself stores any
|
|
92
|
+
* JSON in the column and reads what it cannot use as the default, so this check
|
|
93
|
+
* is what keeps a bad value from silently turning deposits off.
|
|
94
|
+
*
|
|
95
|
+
* Absent and "set to the defaults" mean the same thing to the engine, and a
|
|
96
|
+
* store's column is never backfilled: write the policy when a merchant changed
|
|
97
|
+
* it, not as a side effect of saving something else.
|
|
98
|
+
*/
|
|
99
|
+
set(policy: OrderDepositPolicy, options?: Call): Promise<OrderDepositPolicy>;
|
|
100
|
+
}
|
|
101
|
+
export declare const ORDER_DEPOSIT_POLICY_QUERY = "\n query OrderDepositPolicy {\n activeChannel { \n id\n customFields {\n orderDepositPolicy\n }\n }\n }\n";
|
|
102
|
+
export declare const SET_ORDER_DEPOSIT_POLICY_MUTATION = "\n mutation SetOrderDepositPolicy($input: UpdateChannelInput!) {\n updateChannel(input: $input) {\n ... on Channel { \n id\n customFields {\n orderDepositPolicy\n }\n }\n ... on ErrorResult { errorCode message }\n }\n }\n";
|
|
103
|
+
export declare function createOrderDepositPolicyModule(transport: Transport): OrderDepositPolicyModule;
|