@sixtyninecommerce/admin 0.11.0 → 0.13.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 +1509 -28
- package/dist/auth/index.d.ts +7 -1
- package/dist/auth/index.js +8 -1
- package/dist/catalog/documents.d.ts +142 -0
- package/dist/catalog/documents.js +114 -0
- package/dist/catalog/index.d.ts +48 -7
- package/dist/catalog/index.js +6 -1
- package/dist/catalog/metadata.d.ts +244 -0
- package/dist/catalog/metadata.js +220 -0
- package/dist/catalog/properties.d.ts +38 -0
- package/dist/catalog/properties.js +52 -1
- package/dist/catalog/recommendations.d.ts +33 -6
- package/dist/catalog/recommendations.js +94 -2
- package/dist/catalog/reviews.d.ts +17 -4
- package/dist/catalog/reviews.js +7 -0
- package/dist/catalog/variants.d.ts +60 -0
- package/dist/catalog/variants.js +39 -0
- package/dist/catalog/visibility.d.ts +18 -2
- package/dist/client.d.ts +29 -3
- package/dist/client.js +19 -8
- package/dist/content/assets.d.ts +3 -4
- package/dist/content/forms.d.ts +332 -1
- package/dist/content/forms.js +140 -4
- package/dist/content/items.d.ts +31 -8
- package/dist/content/items.js +1 -1
- package/dist/content/menus.d.ts +254 -0
- package/dist/content/menus.js +70 -0
- package/dist/content/strings.d.ts +137 -0
- package/dist/content/strings.js +82 -0
- package/dist/customers/delivery-terms.d.ts +44 -0
- package/dist/customers/delivery-terms.js +32 -0
- package/dist/customers/groups.d.ts +16 -5
- package/dist/customers/groups.js +6 -2
- package/dist/customers/index.d.ts +15 -0
- package/dist/customers/index.js +4 -0
- package/dist/documents/emails.d.ts +154 -0
- package/dist/documents/emails.js +220 -0
- package/dist/documents/index.d.ts +13 -0
- package/dist/documents/index.js +2 -0
- package/dist/fulfillments/create.d.ts +1 -1
- package/dist/fulfillments/create.js +5 -2
- package/dist/fulfillments/index.d.ts +119 -1
- package/dist/fulfillments/index.js +124 -4
- package/dist/fulfillments/warehouse.d.ts +29 -17
- package/dist/fulfillments/warehouse.js +32 -1
- package/dist/index.d.ts +43 -23
- package/dist/index.js +11 -5
- package/dist/integrations/exact-online-types.d.ts +542 -0
- package/dist/integrations/exact-online.d.ts +541 -5
- package/dist/integrations/exact-online.js +714 -1
- package/dist/integrations/index.d.ts +23 -8
- package/dist/integrations/index.js +9 -4
- package/dist/integrations/pipeline-discoveries.d.ts +65 -0
- package/dist/integrations/pipeline-discoveries.js +65 -0
- package/dist/integrations/pipeline-runs.d.ts +119 -0
- package/dist/integrations/pipeline-runs.js +87 -0
- package/dist/integrations/pipelines.d.ts +73 -2
- package/dist/integrations/pipelines.js +36 -1
- package/dist/integrations/price-import.d.ts +223 -0
- package/dist/integrations/price-import.js +124 -0
- package/dist/integrations/types.d.ts +221 -3
- package/dist/integrations/types.js +4 -3
- package/dist/meta/index.d.ts +10 -0
- package/dist/metafields/index.d.ts +8 -1
- package/dist/metafields/index.js +10 -0
- package/dist/orders/cancel-with-money.d.ts +10 -0
- package/dist/orders/cancel-with-money.js +2 -0
- package/dist/orders/commit-edit.d.ts +16 -0
- package/dist/orders/commit-edit.js +14 -1
- package/dist/orders/deposit-policy.d.ts +3 -1
- package/dist/orders/index.d.ts +22 -11
- package/dist/orders/index.js +5 -2
- package/dist/orders/list.d.ts +26 -5
- package/dist/orders/list.js +123 -13
- package/dist/orders/manual-discounts.d.ts +13 -5
- package/dist/orders/money-view.d.ts +149 -0
- package/dist/orders/money-view.js +99 -0
- package/dist/orders/plan-edit.d.ts +34 -2
- package/dist/orders/plan-edit.js +110 -12
- package/dist/orders/surcharges.d.ts +12 -0
- package/dist/orders/surcharges.js +34 -9
- package/dist/payments/account-credit.d.ts +17 -1
- package/dist/payments/account-credit.js +25 -2
- package/dist/payments/credit-noted-fields.d.ts +8 -1
- package/dist/payments/credit-noted-fields.js +34 -2
- package/dist/payments/customer-consent.d.ts +30 -0
- package/dist/payments/customer-consent.js +49 -0
- package/dist/payments/index.d.ts +89 -14
- package/dist/payments/index.js +22 -2
- package/dist/payments/requests.d.ts +6 -3
- package/dist/payments/requests.js +1 -0
- package/dist/pickups/index.d.ts +5 -3
- package/dist/pickups/index.js +5 -2
- package/dist/platform/email-gateways.d.ts +165 -0
- package/dist/platform/email-gateways.js +78 -0
- package/dist/platform/tax.d.ts +11 -3
- package/dist/platform/tax.js +30 -0
- package/dist/pos/exchanges.d.ts +6 -1
- package/dist/pos/index.d.ts +229 -10
- package/dist/pos/index.js +113 -4
- package/dist/pos/offline-sales.d.ts +44 -3
- package/dist/pos/offline-sales.js +57 -3
- package/dist/pos/returns.d.ts +28 -1
- package/dist/pos/returns.js +36 -6
- package/dist/pricing/price-lists.d.ts +3 -1
- package/dist/purchasing/alerts.d.ts +19 -4
- package/dist/purchasing/alerts.js +29 -3
- package/dist/purchasing/backorders.d.ts +61 -0
- package/dist/purchasing/backorders.js +92 -0
- package/dist/purchasing/index.d.ts +21 -3
- package/dist/purchasing/index.js +8 -0
- package/dist/purchasing/links.d.ts +263 -0
- package/dist/purchasing/links.js +192 -0
- package/dist/purchasing/purchase-orders.d.ts +4 -1
- package/dist/purchasing/receipts.d.ts +396 -0
- package/dist/purchasing/receipts.js +287 -0
- package/dist/purchasing/returns.d.ts +273 -0
- package/dist/purchasing/returns.js +229 -0
- package/dist/quotes/index.d.ts +20 -3
- package/dist/returns/index.d.ts +62 -1
- package/dist/returns/index.js +61 -0
- package/dist/seo/config.d.ts +128 -0
- package/dist/seo/config.js +95 -0
- package/dist/seo/redirects.d.ts +125 -0
- package/dist/seo/redirects.js +60 -1
- package/dist/seo/settings.d.ts +147 -0
- package/dist/seo/settings.js +209 -0
- package/dist/shipping/index.d.ts +6 -0
- package/dist/shipping/index.js +2 -0
- package/dist/shipping/methods.d.ts +8 -1
- package/dist/shipping/myparcel.d.ts +415 -1
- package/dist/shipping/myparcel.js +291 -3
- package/dist/shipping/rate-tables.d.ts +149 -4
- package/dist/shipping/rate-tables.js +229 -23
- package/dist/shipping/ship-to-countries.d.ts +75 -0
- package/dist/shipping/ship-to-countries.js +47 -0
- package/dist/stock/by-location.d.ts +86 -0
- package/dist/stock/by-location.js +94 -0
- package/dist/stock/index.d.ts +8 -1
- package/dist/stock/index.js +4 -0
- package/dist/stock/settings.d.ts +106 -0
- package/dist/stock/settings.js +103 -0
- package/dist/stock/transfers.d.ts +171 -10
- package/dist/stock/transfers.js +82 -4
- package/dist/stored-value/index.d.ts +98 -7
- package/dist/stored-value/index.js +71 -4
- package/dist/tax/display-policy.d.ts +105 -0
- package/dist/tax/display-policy.js +84 -0
- package/dist/tax/index.d.ts +3 -0
- package/dist/tax/index.js +2 -0
- package/dist/tenant/email-templates.d.ts +222 -1
- package/dist/tenant/email-templates.js +227 -10
- package/package.json +2 -2
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import { pageFromVendureList, readOffsetCursor, } from '@sixtyninecommerce/core';
|
|
2
|
+
export const SUPPLIER_RETURN_LINE_FIELDS = /* GraphQL */ `
|
|
3
|
+
id
|
|
4
|
+
supplierReturnId
|
|
5
|
+
position
|
|
6
|
+
goodsReceiptId
|
|
7
|
+
goodsReceiptCode
|
|
8
|
+
goodsReceiptLineId
|
|
9
|
+
purchaseOrderId
|
|
10
|
+
purchaseOrderCode
|
|
11
|
+
purchaseOrderLineId
|
|
12
|
+
productVariantId
|
|
13
|
+
sku
|
|
14
|
+
name
|
|
15
|
+
supplierSku
|
|
16
|
+
purchaseUnitLabel
|
|
17
|
+
purchaseUnitFactor
|
|
18
|
+
baseQuantity
|
|
19
|
+
reason
|
|
20
|
+
outcome
|
|
21
|
+
resolution
|
|
22
|
+
currencyCode
|
|
23
|
+
creditAmount
|
|
24
|
+
resolutionReference
|
|
25
|
+
unitCost
|
|
26
|
+
cancelledBaseQuantity
|
|
27
|
+
reopenedBaseQuantity
|
|
28
|
+
resolvedAt
|
|
29
|
+
resolvedByRef
|
|
30
|
+
stockMovementId
|
|
31
|
+
createdAt
|
|
32
|
+
`;
|
|
33
|
+
export const SUPPLIER_RETURN_FIELDS = /* GraphQL */ `
|
|
34
|
+
id
|
|
35
|
+
scopeId
|
|
36
|
+
legalEntityId
|
|
37
|
+
originChannelId
|
|
38
|
+
code
|
|
39
|
+
status
|
|
40
|
+
origin
|
|
41
|
+
supplierId
|
|
42
|
+
supplier { id code name status }
|
|
43
|
+
stockLocationId
|
|
44
|
+
stockLocation { id name }
|
|
45
|
+
supplierReference
|
|
46
|
+
notesToSupplier
|
|
47
|
+
carrier
|
|
48
|
+
trackingCode
|
|
49
|
+
shippedAt
|
|
50
|
+
resolvedAt
|
|
51
|
+
managedBy
|
|
52
|
+
externalId
|
|
53
|
+
createdByRef
|
|
54
|
+
documentIssuedAt
|
|
55
|
+
lines { ${SUPPLIER_RETURN_LINE_FIELDS} }
|
|
56
|
+
statusHistory { id fromStatus status actorRef reason metadata createdAt }
|
|
57
|
+
createdAt
|
|
58
|
+
updatedAt
|
|
59
|
+
`;
|
|
60
|
+
export const SUPPLIER_RETURNS_QUERY = /* GraphQL */ `
|
|
61
|
+
query SupplierReturns($options: SupplierReturnListOptions) {
|
|
62
|
+
supplierReturns(options: $options) {
|
|
63
|
+
items { ${SUPPLIER_RETURN_FIELDS} }
|
|
64
|
+
totalItems
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
`;
|
|
68
|
+
export const SUPPLIER_RETURN_QUERY = /* GraphQL */ `
|
|
69
|
+
query SupplierReturn($id: ID!) {
|
|
70
|
+
supplierReturn(id: $id) { ${SUPPLIER_RETURN_FIELDS} }
|
|
71
|
+
}
|
|
72
|
+
`;
|
|
73
|
+
export const CREATE_SUPPLIER_RETURN_MUTATION = /* GraphQL */ `
|
|
74
|
+
mutation CreateSupplierReturn($input: CreateSupplierReturnInput!, $idempotencyKey: String) {
|
|
75
|
+
createSupplierReturn(input: $input, idempotencyKey: $idempotencyKey) {
|
|
76
|
+
${SUPPLIER_RETURN_FIELDS}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
`;
|
|
80
|
+
export const UPDATE_SUPPLIER_RETURN_MUTATION = /* GraphQL */ `
|
|
81
|
+
mutation UpdateSupplierReturn($input: UpdateSupplierReturnInput!, $idempotencyKey: String) {
|
|
82
|
+
updateSupplierReturn(input: $input, idempotencyKey: $idempotencyKey) {
|
|
83
|
+
${SUPPLIER_RETURN_FIELDS}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
`;
|
|
87
|
+
export const SHIP_SUPPLIER_RETURN_MUTATION = /* GraphQL */ `
|
|
88
|
+
mutation ShipSupplierReturn($input: ShipSupplierReturnInput!, $idempotencyKey: String!) {
|
|
89
|
+
shipSupplierReturn(input: $input, idempotencyKey: $idempotencyKey) {
|
|
90
|
+
${SUPPLIER_RETURN_FIELDS}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
`;
|
|
94
|
+
export const RESOLVE_SUPPLIER_RETURN_LINE_MUTATION = /* GraphQL */ `
|
|
95
|
+
mutation ResolveSupplierReturnLine(
|
|
96
|
+
$input: ResolveSupplierReturnLineInput!
|
|
97
|
+
$idempotencyKey: String
|
|
98
|
+
) {
|
|
99
|
+
resolveSupplierReturnLine(input: $input, idempotencyKey: $idempotencyKey) {
|
|
100
|
+
${SUPPLIER_RETURN_FIELDS}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
`;
|
|
104
|
+
export const CANCEL_SUPPLIER_RETURN_MUTATION = /* GraphQL */ `
|
|
105
|
+
mutation CancelSupplierReturn($id: ID!, $reason: String, $idempotencyKey: String) {
|
|
106
|
+
cancelSupplierReturn(id: $id, reason: $reason, idempotencyKey: $idempotencyKey) {
|
|
107
|
+
${SUPPLIER_RETURN_FIELDS}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
`;
|
|
111
|
+
export const CREATE_SUPPLIER_RETURN_DOCUMENT_URL_MUTATION = /* GraphQL */ `
|
|
112
|
+
mutation CreateSupplierReturnDocumentUrl($supplierReturnId: ID!) {
|
|
113
|
+
createSupplierReturnDocumentUrl(supplierReturnId: $supplierReturnId) {
|
|
114
|
+
url
|
|
115
|
+
expiresAt
|
|
116
|
+
filename
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
`;
|
|
120
|
+
const DEFAULT_PAGE_SIZE = 25;
|
|
121
|
+
const LIST_FILTERS = [
|
|
122
|
+
'status',
|
|
123
|
+
'origin',
|
|
124
|
+
'supplierId',
|
|
125
|
+
'stockLocationId',
|
|
126
|
+
'purchaseOrderId',
|
|
127
|
+
'goodsReceiptId',
|
|
128
|
+
'term',
|
|
129
|
+
];
|
|
130
|
+
const LINE_INPUT_FIELDS = [
|
|
131
|
+
'goodsReceiptLineId',
|
|
132
|
+
'baseQuantity',
|
|
133
|
+
'reason',
|
|
134
|
+
'outcome',
|
|
135
|
+
];
|
|
136
|
+
const CREATE_INPUT_FIELDS = [
|
|
137
|
+
'origin',
|
|
138
|
+
'supplierId',
|
|
139
|
+
'stockLocationId',
|
|
140
|
+
'supplierReference',
|
|
141
|
+
'notesToSupplier',
|
|
142
|
+
'carrier',
|
|
143
|
+
'trackingCode',
|
|
144
|
+
];
|
|
145
|
+
const UPDATE_INPUT_FIELDS = [
|
|
146
|
+
'stockLocationId',
|
|
147
|
+
'supplierReference',
|
|
148
|
+
'notesToSupplier',
|
|
149
|
+
'carrier',
|
|
150
|
+
'trackingCode',
|
|
151
|
+
];
|
|
152
|
+
const SHIP_INPUT_FIELDS = [
|
|
153
|
+
'carrier',
|
|
154
|
+
'trackingCode',
|
|
155
|
+
'notifySupplier',
|
|
156
|
+
];
|
|
157
|
+
const RESOLVE_INPUT_FIELDS = [
|
|
158
|
+
'supplierReturnLineId',
|
|
159
|
+
'resolution',
|
|
160
|
+
'creditAmount',
|
|
161
|
+
'resolutionReference',
|
|
162
|
+
];
|
|
163
|
+
function picked(input, fields) {
|
|
164
|
+
const out = {};
|
|
165
|
+
for (const field of fields) {
|
|
166
|
+
const value = input[field];
|
|
167
|
+
if (value !== undefined)
|
|
168
|
+
out[field] = value;
|
|
169
|
+
}
|
|
170
|
+
return out;
|
|
171
|
+
}
|
|
172
|
+
export function createPurchasingReturnsModule(transport) {
|
|
173
|
+
const listPage = async (skip, take, options) => {
|
|
174
|
+
const filters = options ? picked(options, LIST_FILTERS) : {};
|
|
175
|
+
const data = await transport.graphql({
|
|
176
|
+
api: 'admin',
|
|
177
|
+
query: SUPPLIER_RETURNS_QUERY,
|
|
178
|
+
variables: { options: { skip, take, ...filters } },
|
|
179
|
+
}, options);
|
|
180
|
+
return pageFromVendureList(data.supplierReturns, skip, take, (cursor) => listPage(readOffsetCursor(cursor), take, options));
|
|
181
|
+
};
|
|
182
|
+
const mutate = async (field, query, variables, call) => {
|
|
183
|
+
const data = await transport.graphql({ api: 'admin', query, variables: { ...variables, idempotencyKey: call?.idempotencyKey } }, call);
|
|
184
|
+
return data[field];
|
|
185
|
+
};
|
|
186
|
+
return {
|
|
187
|
+
async list(options) {
|
|
188
|
+
const take = options?.limit ?? DEFAULT_PAGE_SIZE;
|
|
189
|
+
const skip = options?.after === undefined ? 0 : readOffsetCursor(options.after);
|
|
190
|
+
return listPage(skip, take, options);
|
|
191
|
+
},
|
|
192
|
+
async get(id, call) {
|
|
193
|
+
const data = await transport.graphql({ api: 'admin', query: SUPPLIER_RETURN_QUERY, variables: { id } }, call);
|
|
194
|
+
return data.supplierReturn;
|
|
195
|
+
},
|
|
196
|
+
create(input, call) {
|
|
197
|
+
return mutate('createSupplierReturn', CREATE_SUPPLIER_RETURN_MUTATION, {
|
|
198
|
+
input: {
|
|
199
|
+
...picked(input, CREATE_INPUT_FIELDS),
|
|
200
|
+
lines: input.lines.map((line) => picked(line, LINE_INPUT_FIELDS)),
|
|
201
|
+
},
|
|
202
|
+
}, call);
|
|
203
|
+
},
|
|
204
|
+
update(id, input, call) {
|
|
205
|
+
const fields = { ...picked(input, UPDATE_INPUT_FIELDS), id };
|
|
206
|
+
if (input.lines !== undefined) {
|
|
207
|
+
fields.lines = input.lines.map((line) => picked(line, LINE_INPUT_FIELDS));
|
|
208
|
+
}
|
|
209
|
+
return mutate('updateSupplierReturn', UPDATE_SUPPLIER_RETURN_MUTATION, { input: fields }, call);
|
|
210
|
+
},
|
|
211
|
+
ship(id, input, call) {
|
|
212
|
+
return mutate('shipSupplierReturn', SHIP_SUPPLIER_RETURN_MUTATION, { input: { ...picked(input, SHIP_INPUT_FIELDS), id } }, call);
|
|
213
|
+
},
|
|
214
|
+
resolveLine(input, call) {
|
|
215
|
+
return mutate('resolveSupplierReturnLine', RESOLVE_SUPPLIER_RETURN_LINE_MUTATION, { input: picked(input, RESOLVE_INPUT_FIELDS) }, call);
|
|
216
|
+
},
|
|
217
|
+
cancel(id, options) {
|
|
218
|
+
return mutate('cancelSupplierReturn', CANCEL_SUPPLIER_RETURN_MUTATION, { id, ...(options?.reason === undefined ? {} : { reason: options.reason }) }, options);
|
|
219
|
+
},
|
|
220
|
+
async document(id, call) {
|
|
221
|
+
const data = await transport.graphql({
|
|
222
|
+
api: 'admin',
|
|
223
|
+
query: CREATE_SUPPLIER_RETURN_DOCUMENT_URL_MUTATION,
|
|
224
|
+
variables: { supplierReturnId: id },
|
|
225
|
+
}, call);
|
|
226
|
+
return data.createSupplierReturnDocumentUrl;
|
|
227
|
+
},
|
|
228
|
+
};
|
|
229
|
+
}
|
package/dist/quotes/index.d.ts
CHANGED
|
@@ -65,16 +65,27 @@ export interface QuoteLine {
|
|
|
65
65
|
discountValue: number;
|
|
66
66
|
discountLabel: string | null;
|
|
67
67
|
position: number;
|
|
68
|
-
/**
|
|
68
|
+
/**
|
|
69
|
+
* What the converted order takes off the line, in the store's primary tax mode: (`unitPrice` −
|
|
70
|
+
* `effectiveUnitPrice`) × quantity. It can differ from the typed discount by the per-unit
|
|
71
|
+
* rounding (€1.00 off 3 × €10.00 takes €0.99). Null while unpriced.
|
|
72
|
+
*/
|
|
69
73
|
discountAmount: number | null;
|
|
70
74
|
/**
|
|
71
75
|
* The per-unit price the converted order line gets: the discounted line total ÷ quantity,
|
|
72
76
|
* rounded. The figure to show as the line's unit price. Null while unpriced.
|
|
73
77
|
*/
|
|
74
78
|
effectiveUnitPrice: number | null;
|
|
75
|
-
/**
|
|
79
|
+
/**
|
|
80
|
+
* Net total of the order line the quote converts into. Null while unpriced or when the variant
|
|
81
|
+
* is unavailable.
|
|
82
|
+
*/
|
|
76
83
|
lineTotal: number | null;
|
|
77
|
-
/**
|
|
84
|
+
/**
|
|
85
|
+
* Gross total of the order line the quote converts into, at `taxRate`: the order charges
|
|
86
|
+
* exactly this unless its customer is taxed in another zone or reverse-charged (conversion
|
|
87
|
+
* then records a `priceNotice`). Null while unpriced or when the variant is unavailable.
|
|
88
|
+
*/
|
|
78
89
|
lineTotalWithTax: number | null;
|
|
79
90
|
/** `lineTotalWithTax − lineTotal`. Null as those are. */
|
|
80
91
|
taxAmount: number | null;
|
|
@@ -95,6 +106,12 @@ export interface QuoteStatusLogEntry {
|
|
|
95
106
|
status: QuoteStatus;
|
|
96
107
|
createdAt: string;
|
|
97
108
|
actorUserId: string | null;
|
|
109
|
+
/**
|
|
110
|
+
* What the change recorded. An `approved` row carries the offer the customer was sent
|
|
111
|
+
* (`offeredSubTotal`, `offeredTotalWithTax`); a `converted_to_order` row the order
|
|
112
|
+
* (`orderId`, `orderCode`), both totals, and `priceNotice`, a plain-words sentence, when the
|
|
113
|
+
* order does not total what the quote offered (the order's amount applies).
|
|
114
|
+
*/
|
|
98
115
|
metadata: Record<string, unknown> | null;
|
|
99
116
|
}
|
|
100
117
|
export interface QuoteSalesRepresentative {
|
package/dist/returns/index.d.ts
CHANGED
|
@@ -269,6 +269,26 @@ export interface SettleReturnInput {
|
|
|
269
269
|
shippingRefundAmount?: number | null;
|
|
270
270
|
/** Recorded on the refund and the return's history. */
|
|
271
271
|
reason?: string | null;
|
|
272
|
+
/**
|
|
273
|
+
* `account` in a store whose book is store credit (`settlementOptions`' `account.book`
|
|
274
|
+
* `STORE_CREDIT`): whether the customer expressly agreed to store credit instead of a refund to
|
|
275
|
+
* their payment (EU consumer law, CRD art. 13(1)), as staff recorded it. The move records it on
|
|
276
|
+
* its ledger entries (`StoredValueEntry.customerAgreed`); never a condition, nothing is refused
|
|
277
|
+
* for it. Part of the request: a repeat under the same key sends the same answer. Sent only when
|
|
278
|
+
* given; an engine before the field (incentives sprint 8 PR 6a) refuses a call that gives it.
|
|
279
|
+
*/
|
|
280
|
+
customerAgreed?: boolean | null;
|
|
281
|
+
/**
|
|
282
|
+
* The VAT rate, a percentage from 0 to 100, that the return's invoice credit note states: the
|
|
283
|
+
* note splits its lump sum at that rate (USER PD11). {@link ReturnSettlementOptions.creditNoteTaxRate}
|
|
284
|
+
* suggests one and lists the rates to pick. Used only where the settlement issues an invoice
|
|
285
|
+
* credit note (the methods `creditNoteTaxRate.methods` names). Left out, the note splits as the
|
|
286
|
+
* order allows. Outside 0 to 100:
|
|
287
|
+
* `RETURN_SETTLE_INPUT_INVALID` (`details[0].code` `NOT_A_RATE`) before anything moves. Part of
|
|
288
|
+
* the request: a repeat under the same key sends the same rate. Sent only when given; an engine
|
|
289
|
+
* before the field (whose plan has no `creditNoteTaxRate`) refuses a call that gives it.
|
|
290
|
+
*/
|
|
291
|
+
taxRate?: number | null;
|
|
272
292
|
}
|
|
273
293
|
/** The store's RMA code format: `{prefix}{year}{number}{suffix}`, e.g. `RMA-000042`. */
|
|
274
294
|
export interface ReturnNumberingConfig {
|
|
@@ -433,6 +453,34 @@ export interface ReturnSettlementAccountOption {
|
|
|
433
453
|
*/
|
|
434
454
|
book?: 'AR' | 'STORE_CREDIT' | 'NONE' | (string & {}) | null;
|
|
435
455
|
}
|
|
456
|
+
/** Why the plan suggests the credit note's VAT rate it does. */
|
|
457
|
+
export type ReturnCreditNoteTaxRateBasis = 'RETURNED_GOODS' | 'ORDER_MAIN_RATE';
|
|
458
|
+
/** The VAT rate a return's invoice credit note states, which staff pick (USER PD11). */
|
|
459
|
+
export interface ReturnCreditNoteTaxRateOption {
|
|
460
|
+
/** The rate to preselect, a percentage. */
|
|
461
|
+
suggested: number;
|
|
462
|
+
/**
|
|
463
|
+
* `RETURNED_GOODS`: every returned item carries this rate. `ORDER_MAIN_RATE`: the rate carrying
|
|
464
|
+
* most of the order's money, shipping included.
|
|
465
|
+
*/
|
|
466
|
+
basis: ReturnCreditNoteTaxRateBasis | (string & {});
|
|
467
|
+
/** The rates to pick from, ascending, each once: the store's own, the order's and the suggestion. */
|
|
468
|
+
rates: number[];
|
|
469
|
+
/**
|
|
470
|
+
* The methods whose settlement issues the invoice credit note that takes the rate: ask for the
|
|
471
|
+
* rate on these only (a refund the accounts ledger documents, or an accounts store's own credit
|
|
472
|
+
* note, takes none).
|
|
473
|
+
*/
|
|
474
|
+
methods: Array<ReturnSettlementMethod | (string & {})>;
|
|
475
|
+
/**
|
|
476
|
+
* The gross (minor units) of the invoice the note credits, when a settlement can still credit
|
|
477
|
+
* ALL of it; null once an earlier credit note took part of it, or when the engine does not
|
|
478
|
+
* know. A settlement reaching it credits the whole invoice: leave `taxRate` out and the note
|
|
479
|
+
* states the invoice's own VAT rows per rate, which no single rate can. Absent from an engine
|
|
480
|
+
* before it.
|
|
481
|
+
*/
|
|
482
|
+
invoiceTotal?: number | null;
|
|
483
|
+
}
|
|
436
484
|
export interface ReturnSettlementOptions {
|
|
437
485
|
returnId: string;
|
|
438
486
|
currencyCode: string;
|
|
@@ -495,6 +543,14 @@ export interface ReturnSettlementOptions {
|
|
|
495
543
|
* 2026-10-10 (the method does not exist there).
|
|
496
544
|
*/
|
|
497
545
|
account?: ReturnSettlementAccountOption;
|
|
546
|
+
/**
|
|
547
|
+
* The VAT rate for the invoice credit note a settlement issues: the suggestion, the rates to
|
|
548
|
+
* pick and the methods that issue the note (send the pick as {@link SettleReturnInput.taxRate}).
|
|
549
|
+
* Null when no settlement of it issues an invoice credit note (the order has no invoice) or
|
|
550
|
+
* nothing can be suggested. Absent from an engine before it: that engine refuses
|
|
551
|
+
* `taxRate`, so leave it out.
|
|
552
|
+
*/
|
|
553
|
+
creditNoteTaxRate?: ReturnCreditNoteTaxRateOption | null;
|
|
498
554
|
}
|
|
499
555
|
export declare const RETURN_QUERY = "\n query SdkOrderReturn($id: ID!) {\n orderReturn(id: $id) {\n \n id\n code\n channelId\n orderId\n orderCode\n customerId\n currencyCode\n state\n initiator\n customerNote\n adminNote\n preferredSettlementMethod\n settlementMethod\n shippingRefundAmount\n settledAmount\n refundId\n creditNoteId\n creditNoteNumber\n requestedAt\n resolvedAt\n createdAt\n updatedAt\n lines {\n id\n returnId\n orderLineId\n productVariantId\n productVariantName\n productVariantSku\n requestedQty\n approvedQty\n receivedQty\n restockQty\n writeOffQty\n restockLocationId\n reasonCode\n reasonNote\n unitPriceWithTax\n unitPriceWithTaxUnrounded\n refundAmount\n createdAt\n updatedAt\n }\n\n alreadyCreditedBy {\n \n creditNoteId\n creditNoteNumber\n ledger\n\n }\n \n history {\n state\n actor\n actorUserId\n reason\n createdAt\n }\n\n \n refundEstimate\n customer {\n id\n firstName\n lastName\n emailAddress\n addresses {\n company\n defaultBillingAddress\n defaultShippingAddress\n }\n }\n lines {\n refundEstimate\n }\n\n allowedActions {\n action\n allowed\n reasonCode\n }\n }\n }\n";
|
|
500
556
|
/**
|
|
@@ -530,7 +586,12 @@ export declare const RETURN_ELIGIBILITY_QUERY = "\n query SdkReturnEligibility(
|
|
|
530
586
|
* `creditedQty`/`correctionCredits`: GraphQL refuses a whole document over one unknown field.
|
|
531
587
|
*/
|
|
532
588
|
export declare const RETURN_ELIGIBILITY_QUERY_LEGACY = "\n query SdkReturnEligibilityLegacy($orderId: ID!) {\n returnEligibilityForOrder(orderId: $orderId) {\n orderId\n eligible\n reason\n returnWindowEndsAt\n lines {\n orderLineId\n productVariantId\n productVariantName\n productVariantSku\n fulfilledQty\n returnableQty\n unitPriceWithTax\n unitPriceWithTaxUnrounded\n }\n }\n }\n";
|
|
533
|
-
export declare const RETURN_SETTLEMENT_OPTIONS_QUERY = "\n query SdkReturnSettlementOptions($returnId: ID!) {\n returnSettlementOptions(returnId: $returnId) {\n returnId\n currencyCode\n settleable\n blockedBy\n refund {\n available\n reasonCode\n payments {\n paymentId\n method\n refundable\n }\n }\n creditNote {\n available\n ledger\n reasonCode\n invoiceNumber\n }\n account {\n available\n reasonCode\n message\n held\n book\n }\n initialMethod\n defaultAmount\n amountCeiling\n correctionCredits {\n \n orderLineId\n creditNoteId\n creditNoteNumber\n ledger\n quantity\n amount\n\n }\n shippingCeiling\n alreadyCredited {\n available\n reasonCode\n creditNotes {\n \n creditNoteId\n creditNoteNumber\n ledger\n\n }\n }\n }\n }\n";
|
|
589
|
+
export declare const RETURN_SETTLEMENT_OPTIONS_QUERY = "\n query SdkReturnSettlementOptions($returnId: ID!) {\n returnSettlementOptions(returnId: $returnId) {\n returnId\n currencyCode\n settleable\n blockedBy\n refund {\n available\n reasonCode\n payments {\n paymentId\n method\n refundable\n }\n }\n creditNote {\n available\n ledger\n reasonCode\n invoiceNumber\n }\n account {\n available\n reasonCode\n message\n held\n book\n }\n initialMethod\n defaultAmount\n amountCeiling\n correctionCredits {\n \n orderLineId\n creditNoteId\n creditNoteNumber\n ledger\n quantity\n amount\n\n }\n shippingCeiling\n alreadyCredited {\n available\n reasonCode\n creditNotes {\n \n creditNoteId\n creditNoteNumber\n ledger\n\n }\n }\n creditNoteTaxRate {\n suggested\n basis\n rates\n methods\n invoiceTotal\n }\n }\n }\n";
|
|
590
|
+
/**
|
|
591
|
+
* {@link RETURN_SETTLEMENT_OPTIONS_QUERY} for an engine with the account's book but released before
|
|
592
|
+
* the credit note's VAT rate (USER PD11, B6b).
|
|
593
|
+
*/
|
|
594
|
+
export declare const RETURN_SETTLEMENT_OPTIONS_QUERY_BEFORE_TAX_RATE = "\n query SdkReturnSettlementOptionsBeforeTaxRate($returnId: ID!) {\n returnSettlementOptions(returnId: $returnId) {\n returnId\n currencyCode\n settleable\n blockedBy\n refund {\n available\n reasonCode\n payments {\n paymentId\n method\n refundable\n }\n }\n creditNote {\n available\n ledger\n reasonCode\n invoiceNumber\n }\n account {\n available\n reasonCode\n message\n held\n book\n }\n initialMethod\n defaultAmount\n amountCeiling\n correctionCredits {\n \n orderLineId\n creditNoteId\n creditNoteNumber\n ledger\n quantity\n amount\n\n }\n shippingCeiling\n alreadyCredited {\n available\n reasonCode\n creditNotes {\n \n creditNoteId\n creditNoteNumber\n ledger\n\n }\n }\n }\n }\n";
|
|
534
595
|
/**
|
|
535
596
|
* {@link RETURN_SETTLEMENT_OPTIONS_QUERY} for an engine with `alreadyCredited` but released before
|
|
536
597
|
* the account option named the store's book (incentives sprint 8 PR 4).
|
package/dist/returns/index.js
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
* unknown value as "not available".
|
|
13
13
|
*/
|
|
14
14
|
import { NotFoundError, ValidationError } from '@sixtyninecommerce/core';
|
|
15
|
+
import { statedCustomerAgreed } from '../payments/customer-consent.js';
|
|
15
16
|
/** A return's state: the latest row of its status log. */
|
|
16
17
|
export const RETURN_STATES = [
|
|
17
18
|
'requested',
|
|
@@ -392,6 +393,63 @@ export const RETURN_ELIGIBILITY_QUERY_LEGACY = /* GraphQL */ `
|
|
|
392
393
|
`;
|
|
393
394
|
export const RETURN_SETTLEMENT_OPTIONS_QUERY = /* GraphQL */ `
|
|
394
395
|
query SdkReturnSettlementOptions($returnId: ID!) {
|
|
396
|
+
returnSettlementOptions(returnId: $returnId) {
|
|
397
|
+
returnId
|
|
398
|
+
currencyCode
|
|
399
|
+
settleable
|
|
400
|
+
blockedBy
|
|
401
|
+
refund {
|
|
402
|
+
available
|
|
403
|
+
reasonCode
|
|
404
|
+
payments {
|
|
405
|
+
paymentId
|
|
406
|
+
method
|
|
407
|
+
refundable
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
creditNote {
|
|
411
|
+
available
|
|
412
|
+
ledger
|
|
413
|
+
reasonCode
|
|
414
|
+
invoiceNumber
|
|
415
|
+
}
|
|
416
|
+
account {
|
|
417
|
+
available
|
|
418
|
+
reasonCode
|
|
419
|
+
message
|
|
420
|
+
held
|
|
421
|
+
book
|
|
422
|
+
}
|
|
423
|
+
initialMethod
|
|
424
|
+
defaultAmount
|
|
425
|
+
amountCeiling
|
|
426
|
+
correctionCredits {
|
|
427
|
+
${CORRECTION_CREDIT_FIELDS}
|
|
428
|
+
}
|
|
429
|
+
shippingCeiling
|
|
430
|
+
alreadyCredited {
|
|
431
|
+
available
|
|
432
|
+
reasonCode
|
|
433
|
+
creditNotes {
|
|
434
|
+
${COVERING_CREDIT_NOTE_FIELDS}
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
creditNoteTaxRate {
|
|
438
|
+
suggested
|
|
439
|
+
basis
|
|
440
|
+
rates
|
|
441
|
+
methods
|
|
442
|
+
invoiceTotal
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
`;
|
|
447
|
+
/**
|
|
448
|
+
* {@link RETURN_SETTLEMENT_OPTIONS_QUERY} for an engine with the account's book but released before
|
|
449
|
+
* the credit note's VAT rate (USER PD11, B6b).
|
|
450
|
+
*/
|
|
451
|
+
export const RETURN_SETTLEMENT_OPTIONS_QUERY_BEFORE_TAX_RATE = /* GraphQL */ `
|
|
452
|
+
query SdkReturnSettlementOptionsBeforeTaxRate($returnId: ID!) {
|
|
395
453
|
returnSettlementOptions(returnId: $returnId) {
|
|
396
454
|
returnId
|
|
397
455
|
currencyCode
|
|
@@ -646,6 +704,7 @@ function isUnknownFieldRejection(error) {
|
|
|
646
704
|
/** The plan's documents, newest first: each drops the newest field the one before it asks. */
|
|
647
705
|
const SETTLEMENT_OPTIONS_DOCUMENTS = [
|
|
648
706
|
RETURN_SETTLEMENT_OPTIONS_QUERY,
|
|
707
|
+
RETURN_SETTLEMENT_OPTIONS_QUERY_BEFORE_TAX_RATE,
|
|
649
708
|
RETURN_SETTLEMENT_OPTIONS_QUERY_BEFORE_BOOK,
|
|
650
709
|
RETURN_SETTLEMENT_OPTIONS_QUERY_BEFORE_CLOSE,
|
|
651
710
|
RETURN_SETTLEMENT_OPTIONS_QUERY_BEFORE_ACCOUNT,
|
|
@@ -788,6 +847,8 @@ export function createReturnsModule(transport) {
|
|
|
788
847
|
? { shippingRefundAmount: input.shippingRefundAmount }
|
|
789
848
|
: {}),
|
|
790
849
|
...(input.reason != null ? { reason: input.reason } : {}),
|
|
850
|
+
...statedCustomerAgreed(input.customerAgreed),
|
|
851
|
+
...(input.taxRate != null ? { taxRate: input.taxRate } : {}),
|
|
791
852
|
},
|
|
792
853
|
// A variable, not a header: the Admin API takes the key as an argument. `undefined`
|
|
793
854
|
// when omitted, never null: `JSON.stringify` drops it, so no key stays off the wire.
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { type Transport } from '@sixtyninecommerce/core';
|
|
2
|
+
import type { SettingsSourceKind } from '../tenant/settings-sources.js';
|
|
3
|
+
import type { Call } from '../types.js';
|
|
4
|
+
/**
|
|
5
|
+
* How often the SEO audit (the storefront crawl included) runs. Mirrors `@IsIn` on the plugins
|
|
6
|
+
* service's `UpdateSeoConfigDto` and the meta catalog's `SEO_CRAWL_SCHEDULES`, pinned by
|
|
7
|
+
* `__tests__/seo-config.spec.ts`.
|
|
8
|
+
*/
|
|
9
|
+
export declare const SEO_CRAWL_SCHEDULES: readonly ["off", "daily", "weekly"];
|
|
10
|
+
export type SeoCrawlSchedule = (typeof SEO_CRAWL_SCHEDULES)[number];
|
|
11
|
+
/**
|
|
12
|
+
* The ACTIVE store's SEO automation, as the plugins service applies it. The switches
|
|
13
|
+
* (`autoRedirectsEnabled`, `indexnowEnabled`, `cruxEnabled`, the crawl settings) are part of the
|
|
14
|
+
* `settings.seo` group: while the store takes the group from its organization they are the
|
|
15
|
+
* organization's, and `source` says so. The IndexNow key and the Search Console connection are
|
|
16
|
+
* always the store's own.
|
|
17
|
+
*
|
|
18
|
+
* The keys follow the service's wire names camelised (`indexnow_enabled` → `indexnowEnabled`), as
|
|
19
|
+
* the meta catalog's `seo` schema publishes them, so a head that renders that schema maps each
|
|
20
|
+
* property mechanically.
|
|
21
|
+
*/
|
|
22
|
+
export interface SeoConfig {
|
|
23
|
+
/** Changed URLs are submitted to IndexNow (needs {@link SeoConfig.indexnowKey} to send any). */
|
|
24
|
+
indexnowEnabled: boolean;
|
|
25
|
+
/** The store's IndexNow key, served at `/<key>.txt`; null until one is generated. */
|
|
26
|
+
indexnowKey: string | null;
|
|
27
|
+
/** The Search Console property the store connected; null when none. */
|
|
28
|
+
gscPropertyUrl: string | null;
|
|
29
|
+
/** The store holds a Search Console connection. The credential itself is never answered. */
|
|
30
|
+
gscConnected: boolean;
|
|
31
|
+
/** When the connection was made (ISO 8601, UTC); null when there is none. */
|
|
32
|
+
gscConnectedAt: string | null;
|
|
33
|
+
/** Core Web Vitals (CrUX). Stored and shown; nothing on the platform reads it yet. */
|
|
34
|
+
cruxEnabled: boolean;
|
|
35
|
+
/** A string on the wire: a newer service may answer a schedule this SDK does not name yet. */
|
|
36
|
+
crawlSchedule: SeoCrawlSchedule | (string & {});
|
|
37
|
+
/** The most storefront pages one audit crawls (10 to 5,000). */
|
|
38
|
+
crawlMaxPages: number;
|
|
39
|
+
/** How fast the audit crawls, in pages per second (1 to 10). */
|
|
40
|
+
crawlRatePerSec: number;
|
|
41
|
+
/** A renamed product, collection or page keeps its old URL alive with a redirect. */
|
|
42
|
+
autoRedirectsEnabled: boolean;
|
|
43
|
+
/** `organization` while the switches above are the organization's, else `channel`. */
|
|
44
|
+
source: SettingsSourceKind | (string & {});
|
|
45
|
+
/** The organization locks `settings.seo`: {@link SeoConfigModule.set} refuses any change. */
|
|
46
|
+
locked: boolean;
|
|
47
|
+
/** The store keeps its own switches although the organization shares the group. */
|
|
48
|
+
overridden: boolean;
|
|
49
|
+
/** The organization shares the group without locking it, so the store may override it. */
|
|
50
|
+
inheritable: boolean;
|
|
51
|
+
/** The store's organization; null for a store outside any organization. */
|
|
52
|
+
organizationName: string | null;
|
|
53
|
+
}
|
|
54
|
+
/** The switches {@link SeoConfigModule.set} changes; a key left out keeps its value. */
|
|
55
|
+
export interface SeoConfigPatch {
|
|
56
|
+
indexnowEnabled?: boolean;
|
|
57
|
+
cruxEnabled?: boolean;
|
|
58
|
+
crawlSchedule?: SeoCrawlSchedule;
|
|
59
|
+
/** A whole number from 10 to 5,000. */
|
|
60
|
+
crawlMaxPages?: number;
|
|
61
|
+
/** A whole number from 1 to 10. */
|
|
62
|
+
crawlRatePerSec?: number;
|
|
63
|
+
autoRedirectsEnabled?: boolean;
|
|
64
|
+
}
|
|
65
|
+
/** What {@link SeoConfigModule.rotateIndexnowKey} answers. */
|
|
66
|
+
export interface IndexnowKeyRotation {
|
|
67
|
+
/** The new key. The store's `/<key>.txt` serves it from now on; the old key is void. */
|
|
68
|
+
key: string;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* `admin.seo.config` — the ACTIVE store's SEO automation on the plugins service (Settings → SEO):
|
|
72
|
+
* automatic redirects, IndexNow, Core Web Vitals and the audit schedule. Needs the store's `seo`
|
|
73
|
+
* plugin: without it every method is `PermissionDeniedError` (403) naming the plugin.
|
|
74
|
+
*
|
|
75
|
+
* The other half of the store's SEO settings, the channel defaults (storefront URL, share and AI
|
|
76
|
+
* crawler defaults), is `admin.seo.settings`.
|
|
77
|
+
*/
|
|
78
|
+
export interface SeoConfigModule {
|
|
79
|
+
/**
|
|
80
|
+
* The automation as the service applies it, and where its switches come from. Needs
|
|
81
|
+
* `ReadSettings`.
|
|
82
|
+
*
|
|
83
|
+
* @effect read
|
|
84
|
+
* @permission ReadSettings
|
|
85
|
+
*/
|
|
86
|
+
get(options?: Call): Promise<SeoConfig>;
|
|
87
|
+
/**
|
|
88
|
+
* Change the switches `patch` names, and answer the whole configuration as it now applies.
|
|
89
|
+
* The next audit, URL change or IndexNow submission follows it.
|
|
90
|
+
*
|
|
91
|
+
* **Coded refusals:**
|
|
92
|
+
* - `SEO_CONFIG_INVALID` → `ValidationError` (400), before any request: an empty patch, a
|
|
93
|
+
* key that is not a switch here (the IndexNow key and Search Console have doors of their
|
|
94
|
+
* own), or a null (no switch can be cleared). One `details[]` entry per key, `field` naming
|
|
95
|
+
* it.
|
|
96
|
+
* - `VALIDATION_FAILED` → `ValidationError` (400), from the service: a value of the wrong type,
|
|
97
|
+
* a schedule outside {@link SEO_CRAWL_SCHEDULES}, or a number outside its range.
|
|
98
|
+
* - `MANAGED_BY_ORGANIZATION` → `BusinessRuleError` (422): a changed switch while the
|
|
99
|
+
* organization locks `settings.seo`.
|
|
100
|
+
* - `ORGANIZATION_SETTINGS_INHERITED` → `BusinessRuleError` (422): a changed switch while the
|
|
101
|
+
* store inherits the group; `tenant.settingsSources.override('settings.seo')` first. Sending
|
|
102
|
+
* back the values `get` answered is not a change and passes.
|
|
103
|
+
* - `ORGANIZATION_SETTINGS_SWITCH_OFF_CONFLICT` → `ConflictError` (409): in an organization's
|
|
104
|
+
* workspace, while the organization is switching its SEO defaults off and copying them into
|
|
105
|
+
* its stores. Nothing changed; try again once that finishes.
|
|
106
|
+
* - `PermissionDeniedError` (403): the credential lacks `UpdateSettings`.
|
|
107
|
+
*
|
|
108
|
+
* @effect write
|
|
109
|
+
* @permission UpdateSettings
|
|
110
|
+
*/
|
|
111
|
+
set(patch: SeoConfigPatch, options?: Call): Promise<SeoConfig>;
|
|
112
|
+
/**
|
|
113
|
+
* Generate a new IndexNow key for the store and answer it. The old key is void at once: the
|
|
114
|
+
* store's `/<key>.txt` serves only the new one, and submissions use it from now on. The key is
|
|
115
|
+
* the store's own, so it is never refused for a store taking `settings.seo` from its
|
|
116
|
+
* organization.
|
|
117
|
+
*
|
|
118
|
+
* **Coded refusals:**
|
|
119
|
+
* - `ORGANIZATION_WORKSPACE_NOT_A_STORE` → `BusinessRuleError` (422): the active store is an
|
|
120
|
+
* organization's workspace, which has no storefront to prove.
|
|
121
|
+
* - `PermissionDeniedError` (403): the credential lacks `UpdateSettings`.
|
|
122
|
+
*
|
|
123
|
+
* @effect destructive
|
|
124
|
+
* @permission UpdateSettings
|
|
125
|
+
*/
|
|
126
|
+
rotateIndexnowKey(options?: Call): Promise<IndexnowKeyRotation>;
|
|
127
|
+
}
|
|
128
|
+
export declare function createSeoConfigModule(transport: Transport): SeoConfigModule;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { ValidationError } from '@sixtyninecommerce/core';
|
|
2
|
+
/**
|
|
3
|
+
* How often the SEO audit (the storefront crawl included) runs. Mirrors `@IsIn` on the plugins
|
|
4
|
+
* service's `UpdateSeoConfigDto` and the meta catalog's `SEO_CRAWL_SCHEDULES`, pinned by
|
|
5
|
+
* `__tests__/seo-config.spec.ts`.
|
|
6
|
+
*/
|
|
7
|
+
export const SEO_CRAWL_SCHEDULES = ['off', 'daily', 'weekly'];
|
|
8
|
+
const INVALID = 'SEO_CONFIG_INVALID';
|
|
9
|
+
const PATCH_KEYS = [
|
|
10
|
+
'indexnowEnabled',
|
|
11
|
+
'cruxEnabled',
|
|
12
|
+
'crawlSchedule',
|
|
13
|
+
'crawlMaxPages',
|
|
14
|
+
'crawlRatePerSec',
|
|
15
|
+
'autoRedirectsEnabled',
|
|
16
|
+
];
|
|
17
|
+
const KNOWN_KEYS = new Set(PATCH_KEYS);
|
|
18
|
+
/** Not a switch: answered by `get`, written through a door of its own. */
|
|
19
|
+
const OWN_DOORS = {
|
|
20
|
+
indexnowKey: 'indexnowKey is generated by rotateIndexnowKey(), never set.',
|
|
21
|
+
gscPropertyUrl: 'Search Console is connected through its OAuth flow, never set here.',
|
|
22
|
+
gscConnected: 'Search Console is connected through its OAuth flow, never set here.',
|
|
23
|
+
gscConnectedAt: 'Search Console is connected through its OAuth flow, never set here.',
|
|
24
|
+
};
|
|
25
|
+
const isRecord = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
26
|
+
function problemsOf(patch) {
|
|
27
|
+
const problems = [];
|
|
28
|
+
const add = (field, message) => problems.push({ field, code: INVALID, message });
|
|
29
|
+
if (!isRecord(patch)) {
|
|
30
|
+
add('patch', 'Pass the switches to change as an object, e.g. { autoRedirectsEnabled: false }.');
|
|
31
|
+
return problems;
|
|
32
|
+
}
|
|
33
|
+
const named = Object.keys(patch).filter((key) => patch[key] !== undefined);
|
|
34
|
+
if (named.length === 0) {
|
|
35
|
+
add('patch', `Name at least one switch to change: ${PATCH_KEYS.join(', ')}.`);
|
|
36
|
+
}
|
|
37
|
+
for (const key of named) {
|
|
38
|
+
if (!KNOWN_KEYS.has(key)) {
|
|
39
|
+
add(key, OWN_DOORS[key] ??
|
|
40
|
+
`${key} is not an SEO automation switch; they are ${PATCH_KEYS.join(', ')}.`);
|
|
41
|
+
}
|
|
42
|
+
else if (patch[key] === null) {
|
|
43
|
+
// The service's validation lets a null through to a NOT NULL column, whose refusal names
|
|
44
|
+
// no field (the meta catalog's `SEO_NULL`); nothing here has a "cleared" state.
|
|
45
|
+
add(key, `${key} cannot be cleared; send a value, or leave it out to keep it.`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return problems;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* The camelised wire body, its source fields filled in: they came with #882, and a service from
|
|
52
|
+
* before it leaves them out, which is the store's own settings. Every other field is the
|
|
53
|
+
* service's, as answered.
|
|
54
|
+
*/
|
|
55
|
+
function configOf(body) {
|
|
56
|
+
return {
|
|
57
|
+
...body,
|
|
58
|
+
source: body.source ?? 'channel',
|
|
59
|
+
locked: body.locked === true,
|
|
60
|
+
overridden: body.overridden === true,
|
|
61
|
+
inheritable: body.inheritable === true,
|
|
62
|
+
organizationName: body.organizationName ?? null,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
export function createSeoConfigModule(transport) {
|
|
66
|
+
return {
|
|
67
|
+
async get(options) {
|
|
68
|
+
return configOf(await transport.rest('plugins', { method: 'GET', path: '/seo/config' }, options));
|
|
69
|
+
},
|
|
70
|
+
async set(patch, options) {
|
|
71
|
+
const problems = problemsOf(patch);
|
|
72
|
+
if (problems.length > 0) {
|
|
73
|
+
throw new ValidationError({
|
|
74
|
+
code: INVALID,
|
|
75
|
+
message: problems.map((problem) => problem.message).join(' '),
|
|
76
|
+
status: 400,
|
|
77
|
+
service: 'plugins',
|
|
78
|
+
details: problems,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
// Only the named switches go out. The transport snake-cases them, and every key here
|
|
82
|
+
// camelises from a wire name, so the round trip is exact (`indexnowEnabled` →
|
|
83
|
+
// `indexnow_enabled`); a key spelt any other way would be refused whole by the service.
|
|
84
|
+
const body = {};
|
|
85
|
+
for (const key of PATCH_KEYS) {
|
|
86
|
+
if (patch[key] !== undefined)
|
|
87
|
+
body[key] = patch[key];
|
|
88
|
+
}
|
|
89
|
+
return configOf(await transport.rest('plugins', { method: 'PUT', path: '/seo/config', body }, options));
|
|
90
|
+
},
|
|
91
|
+
rotateIndexnowKey(options) {
|
|
92
|
+
return transport.rest('plugins', { method: 'POST', path: '/seo/config/indexnow-key/rotate' }, options);
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
}
|