@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
package/dist/documents/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createPage, ValidationError } from '@sixtyninecommerce/core';
|
|
1
|
+
import { createPage, idempotencyKey as mintIdempotencyKey, pageFromVendureList, readOffsetCursor, ValidationError, } from '@sixtyninecommerce/core';
|
|
2
2
|
const API_DOCUMENT_TYPE = {
|
|
3
3
|
invoice: 'INVOICE',
|
|
4
4
|
'credit-note': 'CREDIT_NOTE',
|
|
@@ -10,6 +10,18 @@ const API_DOCUMENT_TYPE = {
|
|
|
10
10
|
* dropped the overflow — a document is a page, not a feed.
|
|
11
11
|
*/
|
|
12
12
|
export const MAX_DOCUMENT_EXTENSIONS = 10;
|
|
13
|
+
const DOCUMENT_LINE_FIELDS = /* GraphQL */ `
|
|
14
|
+
description
|
|
15
|
+
sku
|
|
16
|
+
orderLineId
|
|
17
|
+
quantity
|
|
18
|
+
unitPriceNet
|
|
19
|
+
unitPriceWithTax
|
|
20
|
+
taxRate
|
|
21
|
+
lineTotalNet
|
|
22
|
+
lineTotalWithTax
|
|
23
|
+
unitPriceBasis
|
|
24
|
+
`;
|
|
13
25
|
const INVOICE_FIELDS = /* GraphQL */ `
|
|
14
26
|
id
|
|
15
27
|
invoiceNumber
|
|
@@ -21,7 +33,13 @@ const INVOICE_FIELDS = /* GraphQL */ `
|
|
|
21
33
|
currencyCode
|
|
22
34
|
note
|
|
23
35
|
voidedOn
|
|
36
|
+
reversedByCreditNoteId
|
|
37
|
+
supersededByInvoiceId
|
|
38
|
+
reissueOfInvoiceId
|
|
24
39
|
kind
|
|
40
|
+
supplementsInvoiceId
|
|
41
|
+
lines { ${DOCUMENT_LINE_FIELDS} }
|
|
42
|
+
numberingProvider
|
|
25
43
|
createdAt
|
|
26
44
|
`;
|
|
27
45
|
const CREDIT_NOTE_FIELDS = /* GraphQL */ `
|
|
@@ -33,6 +51,11 @@ const CREDIT_NOTE_FIELDS = /* GraphQL */ `
|
|
|
33
51
|
reason
|
|
34
52
|
invoiceId
|
|
35
53
|
kind
|
|
54
|
+
reversalOfInvoiceId
|
|
55
|
+
returnId
|
|
56
|
+
lines { ${DOCUMENT_LINE_FIELDS} }
|
|
57
|
+
refundId
|
|
58
|
+
numberingProvider
|
|
36
59
|
storageKey
|
|
37
60
|
createdAt
|
|
38
61
|
`;
|
|
@@ -88,6 +111,15 @@ export const DOCUMENT_DATA_QUERY = /* GraphQL */ `
|
|
|
88
111
|
documentData(documentId: $documentId, type: $type)
|
|
89
112
|
}
|
|
90
113
|
`;
|
|
114
|
+
export const CREATE_DOCUMENT_DOWNLOAD_URL_MUTATION = /* GraphQL */ `
|
|
115
|
+
mutation CreateDocumentDownloadUrl($documentId: ID!, $type: DocumentType!) {
|
|
116
|
+
createDocumentDownloadUrl(documentId: $documentId, type: $type) {
|
|
117
|
+
url
|
|
118
|
+
expiresAt
|
|
119
|
+
filename
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
`;
|
|
91
123
|
export const ORDER_INVOICES_QUERY = /* GraphQL */ `
|
|
92
124
|
query OrderInvoices($orderId: ID!) {
|
|
93
125
|
orderInvoices(orderId: $orderId) { ${INVOICE_FIELDS} }
|
|
@@ -98,6 +130,20 @@ export const ORDER_CREDIT_NOTES_QUERY = /* GraphQL */ `
|
|
|
98
130
|
orderCreditNotes(orderId: $orderId) { ${CREDIT_NOTE_FIELDS} }
|
|
99
131
|
}
|
|
100
132
|
`;
|
|
133
|
+
export const CUSTOMER_DOCUMENTS_QUERY = /* GraphQL */ `
|
|
134
|
+
query CustomerDocuments($customerId: ID!, $options: CustomerDocumentListOptions) {
|
|
135
|
+
customerDocuments(customerId: $customerId, options: $options) {
|
|
136
|
+
totalItems
|
|
137
|
+
items {
|
|
138
|
+
type
|
|
139
|
+
orderId
|
|
140
|
+
orderCode
|
|
141
|
+
invoice { ${INVOICE_FIELDS} }
|
|
142
|
+
creditNote { ${CREDIT_NOTE_FIELDS} }
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
`;
|
|
101
147
|
export const PREVIEW_INVOICE_MUTATION = /* GraphQL */ `
|
|
102
148
|
mutation PreviewInvoice($orderId: ID!, $invoicedOn: String!, $languageCode: String, $note: String) {
|
|
103
149
|
previewInvoice(orderId: $orderId, invoicedOn: $invoicedOn, languageCode: $languageCode, note: $note)
|
|
@@ -108,6 +154,23 @@ export const SEND_INVOICE_EMAIL_MUTATION = /* GraphQL */ `
|
|
|
108
154
|
sendInvoiceEmail(input: $input)
|
|
109
155
|
}
|
|
110
156
|
`;
|
|
157
|
+
export const SEND_CREDIT_NOTE_EMAIL_MUTATION = /* GraphQL */ `
|
|
158
|
+
mutation SendCreditNoteEmail($input: SendCreditNoteEmailInput!) {
|
|
159
|
+
sendCreditNoteEmail(input: $input)
|
|
160
|
+
}
|
|
161
|
+
`;
|
|
162
|
+
export const ORDER_CREDIT_NOTE_EMAIL_SENDS_QUERY = /* GraphQL */ `
|
|
163
|
+
query OrderCreditNoteEmailSends($orderId: ID!) {
|
|
164
|
+
orderCreditNoteEmailSends(orderId: $orderId) {
|
|
165
|
+
id
|
|
166
|
+
orderId
|
|
167
|
+
creditNoteId
|
|
168
|
+
recipientsTo
|
|
169
|
+
recipientsCc
|
|
170
|
+
createdAt
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
`;
|
|
111
174
|
export const ISSUE_PARTIAL_CREDIT_NOTE_MUTATION = /* GraphQL */ `
|
|
112
175
|
mutation IssuePartialCreditNote($input: IssuePartialCreditNoteInput!) {
|
|
113
176
|
issuePartialCreditNote(input: $input) { ${CREDIT_NOTE_FIELDS} }
|
|
@@ -118,6 +181,98 @@ export const ISSUE_SUPPLEMENTARY_INVOICE_MUTATION = /* GraphQL */ `
|
|
|
118
181
|
issueSupplementaryInvoice(input: $input) { ${INVOICE_FIELDS} }
|
|
119
182
|
}
|
|
120
183
|
`;
|
|
184
|
+
const TOTALS_FIELDS = /* GraphQL */ `
|
|
185
|
+
net
|
|
186
|
+
tax
|
|
187
|
+
gross
|
|
188
|
+
taxSummary { taxRate net tax gross }
|
|
189
|
+
`;
|
|
190
|
+
const INVOICE_CORRECTION_PLAN_FIELDS = /* GraphQL */ `
|
|
191
|
+
planHash
|
|
192
|
+
ledger
|
|
193
|
+
invoice { id invoiceNumber kind totalWithTax creditable currencyCode orderIds voidedOn }
|
|
194
|
+
outcomes {
|
|
195
|
+
outcome
|
|
196
|
+
available
|
|
197
|
+
unavailableCode
|
|
198
|
+
unavailableMessage
|
|
199
|
+
document
|
|
200
|
+
needsLines
|
|
201
|
+
prefillsLines
|
|
202
|
+
needsAddresses
|
|
203
|
+
allowsRefund
|
|
204
|
+
allowsNote
|
|
205
|
+
}
|
|
206
|
+
prefill {
|
|
207
|
+
source
|
|
208
|
+
basis
|
|
209
|
+
lines { description sku orderLineId quantity unitPrice unitPriceBasis taxRate }
|
|
210
|
+
seededGross
|
|
211
|
+
billedGross
|
|
212
|
+
differs
|
|
213
|
+
}
|
|
214
|
+
outcome
|
|
215
|
+
operation
|
|
216
|
+
lineTotals { ${TOTALS_FIELDS} }
|
|
217
|
+
documents {
|
|
218
|
+
type
|
|
219
|
+
kind
|
|
220
|
+
documentDate
|
|
221
|
+
currencyCode
|
|
222
|
+
lines { ${DOCUMENT_LINE_FIELDS} }
|
|
223
|
+
totals { ${TOTALS_FIELDS} }
|
|
224
|
+
issuer
|
|
225
|
+
numbering { provider providerLabel external ready legalEntityId testStore }
|
|
226
|
+
}
|
|
227
|
+
refund {
|
|
228
|
+
amount
|
|
229
|
+
available
|
|
230
|
+
unavailableCode
|
|
231
|
+
unavailableMessage
|
|
232
|
+
options { paymentId method amount refundable }
|
|
233
|
+
recommendedPaymentId
|
|
234
|
+
}
|
|
235
|
+
accountCredit { available unavailableCode message }
|
|
236
|
+
settlement
|
|
237
|
+
blockers { code message path }
|
|
238
|
+
`;
|
|
239
|
+
export const PLAN_INVOICE_CORRECTION_QUERY = /* GraphQL */ `
|
|
240
|
+
query PlanInvoiceCorrection($input: InvoiceCorrectionInput!) {
|
|
241
|
+
planInvoiceCorrection(input: $input) { ${INVOICE_CORRECTION_PLAN_FIELDS} }
|
|
242
|
+
}
|
|
243
|
+
`;
|
|
244
|
+
/**
|
|
245
|
+
* Its own document on purpose: `commitInvoiceCorrection` exists only on an engine with the
|
|
246
|
+
* correction commit, so on an older one this call alone fails validation.
|
|
247
|
+
*/
|
|
248
|
+
export const COMMIT_INVOICE_CORRECTION_MUTATION = /* GraphQL */ `
|
|
249
|
+
mutation CommitInvoiceCorrection(
|
|
250
|
+
$input: InvoiceCorrectionInput!
|
|
251
|
+
$expectedPlanHash: String!
|
|
252
|
+
$idempotencyKey: String!
|
|
253
|
+
) {
|
|
254
|
+
commitInvoiceCorrection(
|
|
255
|
+
input: $input
|
|
256
|
+
expectedPlanHash: $expectedPlanHash
|
|
257
|
+
idempotencyKey: $idempotencyKey
|
|
258
|
+
) {
|
|
259
|
+
plan { ${INVOICE_CORRECTION_PLAN_FIELDS} }
|
|
260
|
+
documents {
|
|
261
|
+
ledger
|
|
262
|
+
type
|
|
263
|
+
id
|
|
264
|
+
number
|
|
265
|
+
kind
|
|
266
|
+
totalWithTax
|
|
267
|
+
currencyCode
|
|
268
|
+
issuedOn
|
|
269
|
+
numberingProvider
|
|
270
|
+
}
|
|
271
|
+
refund { id state total paymentId }
|
|
272
|
+
accountCredit { amount currencyCode unappliedCashIds }
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
`;
|
|
121
276
|
export const DOCUMENT_EXTENSIONS_QUERY = /* GraphQL */ `
|
|
122
277
|
query DocumentExtensions {
|
|
123
278
|
documentExtensions { ${DOCUMENT_EXTENSION_FIELDS} }
|
|
@@ -157,9 +312,12 @@ function toApiType(type) {
|
|
|
157
312
|
* sees that the block is registered for something — which is why the decoded
|
|
158
313
|
* field is typed `string[]`.
|
|
159
314
|
*/
|
|
315
|
+
const DOCUMENT_TYPE_BY_API = new Map(Object.entries(API_DOCUMENT_TYPE).map(([type, api]) => [api, type]));
|
|
316
|
+
function fromApiType(value) {
|
|
317
|
+
return DOCUMENT_TYPE_BY_API.get(value) ?? value;
|
|
318
|
+
}
|
|
160
319
|
function fromApiTypes(values) {
|
|
161
|
-
|
|
162
|
-
return values.map((value) => byApi.get(value) ?? value);
|
|
320
|
+
return values.map(fromApiType);
|
|
163
321
|
}
|
|
164
322
|
function toSchema(raw) {
|
|
165
323
|
return { ...raw, documentTypes: fromApiTypes(raw.documentTypes) };
|
|
@@ -175,7 +333,19 @@ function decodePdf(base64) {
|
|
|
175
333
|
}
|
|
176
334
|
return bytes;
|
|
177
335
|
}
|
|
336
|
+
/** The server's default page; the SDK states it so the cursor arithmetic knows the size. */
|
|
337
|
+
const CUSTOMER_DOCUMENTS_PAGE_SIZE = 25;
|
|
178
338
|
export function createDocumentsModule(transport) {
|
|
339
|
+
const customerDocumentsPage = async (customerId, skip, take, options) => {
|
|
340
|
+
const data = await transport.graphql({
|
|
341
|
+
api: 'admin',
|
|
342
|
+
query: CUSTOMER_DOCUMENTS_QUERY,
|
|
343
|
+
variables: { customerId, options: { skip, take } },
|
|
344
|
+
}, options);
|
|
345
|
+
const list = data.customerDocuments;
|
|
346
|
+
const items = list.items.map((item) => ({ ...item, type: fromApiType(item.type) }));
|
|
347
|
+
return pageFromVendureList({ items, totalItems: list.totalItems }, skip, take, (cursor) => customerDocumentsPage(customerId, readOffsetCursor(cursor), take, options));
|
|
348
|
+
};
|
|
179
349
|
return {
|
|
180
350
|
// One implementation behind the two overloads the interface declares; the
|
|
181
351
|
// cast is the standard TypeScript shape for that, since an implementation
|
|
@@ -232,6 +402,14 @@ export function createDocumentsModule(transport) {
|
|
|
232
402
|
}, options);
|
|
233
403
|
return data.documentData;
|
|
234
404
|
},
|
|
405
|
+
async downloadUrl(documentId, type, options) {
|
|
406
|
+
const data = await transport.graphql({
|
|
407
|
+
api: 'admin',
|
|
408
|
+
query: CREATE_DOCUMENT_DOWNLOAD_URL_MUTATION,
|
|
409
|
+
variables: { documentId, type: toApiType(type) },
|
|
410
|
+
}, options);
|
|
411
|
+
return data.createDocumentDownloadUrl;
|
|
412
|
+
},
|
|
235
413
|
async invoices(orderId, options) {
|
|
236
414
|
const data = await transport.graphql({ api: 'admin', query: ORDER_INVOICES_QUERY, variables: { orderId } }, options);
|
|
237
415
|
return wholeList(data.orderInvoices, 'orderInvoices');
|
|
@@ -240,6 +418,12 @@ export function createDocumentsModule(transport) {
|
|
|
240
418
|
const data = await transport.graphql({ api: 'admin', query: ORDER_CREDIT_NOTES_QUERY, variables: { orderId } }, options);
|
|
241
419
|
return wholeList(data.orderCreditNotes, 'orderCreditNotes');
|
|
242
420
|
},
|
|
421
|
+
forCustomer(customerId, options) {
|
|
422
|
+
// The Admin API is offset-paged; the offset hides inside the cursor (spec §9.3).
|
|
423
|
+
const take = options?.limit ?? CUSTOMER_DOCUMENTS_PAGE_SIZE;
|
|
424
|
+
const skip = options?.after === undefined ? 0 : readOffsetCursor(options.after);
|
|
425
|
+
return customerDocumentsPage(customerId, skip, take, options);
|
|
426
|
+
},
|
|
243
427
|
async previewInvoice(orderId, options) {
|
|
244
428
|
const data = await transport.graphql({
|
|
245
429
|
api: 'admin',
|
|
@@ -257,10 +441,52 @@ export function createDocumentsModule(transport) {
|
|
|
257
441
|
const data = await transport.graphql({ api: 'admin', query: SEND_INVOICE_EMAIL_MUTATION, variables: { input } }, options);
|
|
258
442
|
return data.sendInvoiceEmail;
|
|
259
443
|
},
|
|
444
|
+
async sendCreditNoteEmail(input, options) {
|
|
445
|
+
const data = await transport.graphql({
|
|
446
|
+
api: 'admin',
|
|
447
|
+
query: SEND_CREDIT_NOTE_EMAIL_MUTATION,
|
|
448
|
+
variables: {
|
|
449
|
+
input: {
|
|
450
|
+
creditNoteId: input.creditNoteId,
|
|
451
|
+
recipientsTo: input.recipientsTo,
|
|
452
|
+
recipientsCc: input.recipientsCc ?? null,
|
|
453
|
+
},
|
|
454
|
+
},
|
|
455
|
+
}, options);
|
|
456
|
+
return data.sendCreditNoteEmail;
|
|
457
|
+
},
|
|
458
|
+
async creditNoteEmailSends(orderId, options) {
|
|
459
|
+
const data = await transport.graphql({ api: 'admin', query: ORDER_CREDIT_NOTE_EMAIL_SENDS_QUERY, variables: { orderId } }, options);
|
|
460
|
+
return wholeList(data.orderCreditNoteEmailSends, 'orderCreditNoteEmailSends');
|
|
461
|
+
},
|
|
260
462
|
async issuePartialCreditNote(input, options) {
|
|
261
463
|
const data = await transport.graphql({ api: 'admin', query: ISSUE_PARTIAL_CREDIT_NOTE_MUTATION, variables: { input } }, options);
|
|
262
464
|
return data.issuePartialCreditNote;
|
|
263
465
|
},
|
|
466
|
+
async planCorrection(input, options) {
|
|
467
|
+
const data = await transport.graphql({ api: 'admin', query: PLAN_INVOICE_CORRECTION_QUERY, variables: { input } }, options);
|
|
468
|
+
return data.planInvoiceCorrection;
|
|
469
|
+
},
|
|
470
|
+
async commitCorrection(input, options) {
|
|
471
|
+
const key = options.idempotencyKey ?? mintIdempotencyKey();
|
|
472
|
+
try {
|
|
473
|
+
const data = await transport.graphql({
|
|
474
|
+
api: 'admin',
|
|
475
|
+
query: COMMIT_INVOICE_CORRECTION_MUTATION,
|
|
476
|
+
// The input goes out as given, never normalised: it must be the request the plan was
|
|
477
|
+
// made for, and a retry under the same key must send the same one.
|
|
478
|
+
variables: { input, expectedPlanHash: options.expectedPlanHash, idempotencyKey: key },
|
|
479
|
+
}, { ...options, idempotencyKey: key });
|
|
480
|
+
return { ...data.commitInvoiceCorrection, idempotencyKey: key };
|
|
481
|
+
}
|
|
482
|
+
catch (error) {
|
|
483
|
+
// The key the caller retries under, when it did not choose one.
|
|
484
|
+
if (error !== null && typeof error === 'object' && !('idempotencyKey' in error)) {
|
|
485
|
+
Object.defineProperty(error, 'idempotencyKey', { value: key, enumerable: true });
|
|
486
|
+
}
|
|
487
|
+
throw error;
|
|
488
|
+
}
|
|
489
|
+
},
|
|
264
490
|
async issueSupplementaryInvoice(input, options) {
|
|
265
491
|
const data = await transport.graphql({ api: 'admin', query: ISSUE_SUPPLEMENTARY_INVOICE_MUTATION, variables: { input } }, options);
|
|
266
492
|
return data.issueSupplementaryInvoice;
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
import { createEventHandler, type EventsClient, type Page, type Transport } from '@sixtyninecommerce/core';
|
|
2
|
+
import type { Call, ListOptions } from '../types.js';
|
|
3
|
+
/**
|
|
4
|
+
* A webhook subscription: where the platform POSTs this store's events, which ones, and
|
|
5
|
+
* how the destination is doing. The index signature keeps fields a newer platform adds.
|
|
6
|
+
*/
|
|
7
|
+
export interface EventSubscription {
|
|
8
|
+
id: string;
|
|
9
|
+
name: string;
|
|
10
|
+
/** The endpoint. Must resolve to a public address; the platform re-checks it per delivery. */
|
|
11
|
+
url: string;
|
|
12
|
+
/** Exact types, family wildcards (`order.*`) or `*`, as given. */
|
|
13
|
+
eventFilters: string[];
|
|
14
|
+
enabled: boolean;
|
|
15
|
+
/** Failures in a row that count toward auto-disable (a transient failure does not). */
|
|
16
|
+
consecutiveFailures: number;
|
|
17
|
+
/** ISO-8601 UTC, or null while enabled. */
|
|
18
|
+
disabledAt: string | null;
|
|
19
|
+
disabledReason: string | null;
|
|
20
|
+
/** Set when retention destroyed events this subscription had not yet received. */
|
|
21
|
+
gapDetectedAt: string | null;
|
|
22
|
+
lastDeliveredAt: string | null;
|
|
23
|
+
createdAt: string;
|
|
24
|
+
/** Whether this destination receives customer identity stripped. */
|
|
25
|
+
redactCustomerIdentity: boolean;
|
|
26
|
+
/**
|
|
27
|
+
* The app installation this subscription belongs to, or null for the store's
|
|
28
|
+
* own. An app's subscription follows the app's declarations: `update`,
|
|
29
|
+
* `setEnabled`, `rotateSecret` and `remove` refuse it (`ConflictError`,
|
|
30
|
+
* `SUBSCRIPTION_OWNED_BY_APP`) — uninstall the app or approve its declarations
|
|
31
|
+
* instead. Absent from a platform older than app subscriptions.
|
|
32
|
+
*/
|
|
33
|
+
ownerInstallationId?: string | null;
|
|
34
|
+
/** `filters`, or `compliance_only` for an uninstalled app awaiting its redact request. */
|
|
35
|
+
deliveryScope?: string;
|
|
36
|
+
/** How the most recent attempt ended (`delivered`, `transient`, …), null before the first. */
|
|
37
|
+
lastOutcome?: string | null;
|
|
38
|
+
lastAttemptAt?: string | null;
|
|
39
|
+
[extra: string]: unknown;
|
|
40
|
+
}
|
|
41
|
+
/** What `create` answers: the subscription plus its signing secret, shown this ONCE. */
|
|
42
|
+
export interface CreatedEventSubscription extends EventSubscription {
|
|
43
|
+
/**
|
|
44
|
+
* The signing secret (hex). Store it now — no read returns it again. Pass it to
|
|
45
|
+
* `createEventHandler` / `verifyEventDelivery`; its Standard Webhooks spelling for other
|
|
46
|
+
* libraries is `whsec_` + base64 of this string's UTF-8 bytes.
|
|
47
|
+
*/
|
|
48
|
+
secret: string;
|
|
49
|
+
}
|
|
50
|
+
/** What `rotateSecret` answers: the NEW secret, shown this once. */
|
|
51
|
+
export interface RotatedEventSubscriptionSecret {
|
|
52
|
+
/** The new secret (hex), the spelling `create` answers. Store it now. */
|
|
53
|
+
secret: string;
|
|
54
|
+
/** The same key material as a Standard Webhooks `whsec_` secret, for stock libraries. */
|
|
55
|
+
secretStandard: string;
|
|
56
|
+
/**
|
|
57
|
+
* ISO-8601 UTC. Until then every delivery is signed with BOTH secrets
|
|
58
|
+
* (`webhook-signature: v1,<new> v1,<old>`) and the legacy `x-webhook-signature` with the
|
|
59
|
+
* OLD one, so verify with `[newSecret, oldSecret]` meanwhile and drop the old one after.
|
|
60
|
+
*/
|
|
61
|
+
previousExpiresAt: string;
|
|
62
|
+
[extra: string]: unknown;
|
|
63
|
+
}
|
|
64
|
+
export interface EventSubscriptionInput {
|
|
65
|
+
name: string;
|
|
66
|
+
url: string;
|
|
67
|
+
eventFilters: string[];
|
|
68
|
+
/** Strip customer identity before signing. Default false. */
|
|
69
|
+
redactCustomerIdentity?: boolean;
|
|
70
|
+
}
|
|
71
|
+
export type EventSubscriptionUpdate = Partial<EventSubscriptionInput>;
|
|
72
|
+
/** One delivery attempt, newest first. */
|
|
73
|
+
export interface EventDelivery {
|
|
74
|
+
id: string;
|
|
75
|
+
eventId: string;
|
|
76
|
+
eventType: string;
|
|
77
|
+
logSeq: string;
|
|
78
|
+
attempt: number;
|
|
79
|
+
/** `delivered` | `transient` | `fault` | `dead_lettered` | … */
|
|
80
|
+
outcome: string;
|
|
81
|
+
/** The subscriber's HTTP status, or null when nothing came back. */
|
|
82
|
+
responseStatus: number | null;
|
|
83
|
+
durationMs: number | null;
|
|
84
|
+
error: string | null;
|
|
85
|
+
/** Per-attempt evidence the dispatcher recorded; keys as stored. */
|
|
86
|
+
evidence: Record<string, unknown> | null;
|
|
87
|
+
createdAt: string;
|
|
88
|
+
[extra: string]: unknown;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* What `sendTest` answered: how the endpoint took ONE synthetic delivery. An endpoint that
|
|
92
|
+
* refused it is still an answer (`delivered: false`), not a thrown error.
|
|
93
|
+
*/
|
|
94
|
+
export interface EventSendTestResult {
|
|
95
|
+
/** True when the endpoint answered 2xx. */
|
|
96
|
+
delivered: boolean;
|
|
97
|
+
/** `delivered` | `transient` | `subscription_fault` | `dead_lettered` — as a real delivery's would. */
|
|
98
|
+
outcome: string;
|
|
99
|
+
/** The endpoint's HTTP status, or null when nothing came back. */
|
|
100
|
+
status: number | null;
|
|
101
|
+
durationMs: number;
|
|
102
|
+
/** The synthetic envelope's `id` (its `webhook-id`). It is not in the event log. */
|
|
103
|
+
webhookId: string;
|
|
104
|
+
/** Why nothing came back — a timeout, a refused connection, an endpoint the platform refused. */
|
|
105
|
+
error: string | null;
|
|
106
|
+
[extra: string]: unknown;
|
|
107
|
+
}
|
|
108
|
+
export interface EventSendTestOptions extends Call {
|
|
109
|
+
/**
|
|
110
|
+
* The envelope `data` to send, verbatim (a JSON object, at most 64 KiB). Omit it for the
|
|
111
|
+
* platform's example payload of the type.
|
|
112
|
+
*/
|
|
113
|
+
data?: Record<string, unknown>;
|
|
114
|
+
}
|
|
115
|
+
/** Most event ids one `redrive` call may name. */
|
|
116
|
+
export declare const MAX_REDRIVE_EVENT_IDS = 100;
|
|
117
|
+
/**
|
|
118
|
+
* What `redrive` answered for one id. `queued`: it will be delivered again;
|
|
119
|
+
* `already_queued`: a request for it is still waiting; `filtered_out`: the subscription does
|
|
120
|
+
* not take its type; `expired`: it has left the event log; `not_found`: no such event in this
|
|
121
|
+
* store's log.
|
|
122
|
+
*/
|
|
123
|
+
export type RedriveRequestState = 'queued' | 'already_queued' | 'filtered_out' | 'expired' | 'not_found' | (string & {});
|
|
124
|
+
export interface RedriveAnswer {
|
|
125
|
+
items: Array<{
|
|
126
|
+
eventId: string;
|
|
127
|
+
state: RedriveRequestState;
|
|
128
|
+
}>;
|
|
129
|
+
}
|
|
130
|
+
/** One redrive request, as the platform tracks it. */
|
|
131
|
+
export interface EventRedrive {
|
|
132
|
+
id: string;
|
|
133
|
+
eventId: string;
|
|
134
|
+
logSeq: string;
|
|
135
|
+
occurredAt: string;
|
|
136
|
+
requestedAt: string;
|
|
137
|
+
/** `<kind>:<id>` of the principal that asked (`user:`, `api_token:`, `app_installation:`). */
|
|
138
|
+
requestedBy: string | null;
|
|
139
|
+
/** `queued` until the platform settles it, then `delivered` or `failed`. */
|
|
140
|
+
state: 'queued' | 'delivered' | 'failed' | (string & {});
|
|
141
|
+
attempts: number;
|
|
142
|
+
/** The last delivery outcome, or why it settled without one (`expired`, `filtered_out`). */
|
|
143
|
+
lastOutcome: string | null;
|
|
144
|
+
completedAt: string | null;
|
|
145
|
+
[extra: string]: unknown;
|
|
146
|
+
}
|
|
147
|
+
export interface EventSubscriptionsModule {
|
|
148
|
+
/** Needs `ReadSettings` or `ManageEventSubscriptions`. Newest first. */
|
|
149
|
+
list(options?: ListOptions): Promise<Page<EventSubscription>>;
|
|
150
|
+
/**
|
|
151
|
+
* Needs `ManageEventSubscriptions` (or `UpdateSettings`). Answers the secret ONCE. Takes no idempotency key: the route is
|
|
152
|
+
* deliberately not idempotent, because a stored replay would keep the secret for 24 hours
|
|
153
|
+
* — so a retry after a lost response creates a second subscription; `list` and `remove`
|
|
154
|
+
* the spare.
|
|
155
|
+
*/
|
|
156
|
+
create(input: EventSubscriptionInput, options?: Omit<Call, 'idempotencyKey'>): Promise<CreatedEventSubscription>;
|
|
157
|
+
/** Needs `ManageEventSubscriptions` (or `UpdateSettings`). Name, endpoint, filters and the redaction flag. */
|
|
158
|
+
update(id: string, input: EventSubscriptionUpdate, options?: Call): Promise<EventSubscription>;
|
|
159
|
+
/** Needs `ManageEventSubscriptions` (or `UpdateSettings`). Turning a subscription on clears its failure count. */
|
|
160
|
+
setEnabled(id: string, enabled: boolean, options?: Call): Promise<EventSubscription>;
|
|
161
|
+
/** Needs `ManageEventSubscriptions` (or `UpdateSettings`). */
|
|
162
|
+
remove(id: string, options?: Call): Promise<void>;
|
|
163
|
+
/**
|
|
164
|
+
* Needs `ManageEventSubscriptions` (or `UpdateSettings`). Issues a new signing secret and answers it ONCE. The secret it
|
|
165
|
+
* replaces keeps co-signing for 24 hours (`previousExpiresAt`), so a receiver switches
|
|
166
|
+
* over without dropping a delivery:
|
|
167
|
+
*
|
|
168
|
+
* ```ts
|
|
169
|
+
* const { secret } = await admin.events.subscriptions.rotateSecret(id);
|
|
170
|
+
* // deploy the receiver with both, newest first …
|
|
171
|
+
* createEventHandler({ secret: [secret, oldSecret], on: { … } });
|
|
172
|
+
* // … and drop oldSecret after previousExpiresAt.
|
|
173
|
+
* ```
|
|
174
|
+
*
|
|
175
|
+
* A second rotation inside that window is refused (`ConflictError`, 409,
|
|
176
|
+
* `ROTATION_IN_PROGRESS`). `{ force: true }` rotates anyway and the OLDER secret stops
|
|
177
|
+
* signing at once — receivers still holding only it stop verifying. The secret being
|
|
178
|
+
* replaced always keeps co-signing, forced or not, so to revoke a LEAKED current secret
|
|
179
|
+
* immediately call `rotateSecret(id, { force: true })` TWICE: the second call drops the
|
|
180
|
+
* leaked secret. (Both forced: a plain call is refused while an overlap is running.) Takes no idempotency key, like `create`: the answer carries a secret. A retry
|
|
181
|
+
* after a lost answer is refused `ROTATION_IN_PROGRESS`; recover with `force`.
|
|
182
|
+
*/
|
|
183
|
+
rotateSecret(id: string, options?: {
|
|
184
|
+
force?: boolean;
|
|
185
|
+
} & Omit<Call, 'idempotencyKey'>): Promise<RotatedEventSubscriptionSecret>;
|
|
186
|
+
/**
|
|
187
|
+
* Needs `ReadSettings` or `ManageEventSubscriptions`. Recent attempts, newest first. A `sendTest` attempt is listed
|
|
188
|
+
* too, marked `evidence.test === true` (its `logSeq` is `"0"`: it was never in the log).
|
|
189
|
+
*/
|
|
190
|
+
deliveries(id: string, options?: ListOptions): Promise<Page<EventDelivery>>;
|
|
191
|
+
/**
|
|
192
|
+
* Needs `ManageEventSubscriptions` (or `UpdateSettings`). Sign and POST ONE synthetic event of `type` to this
|
|
193
|
+
* subscription's endpoint — to prove a receiver verifies and parses a delivery without
|
|
194
|
+
* waiting for the store to produce one:
|
|
195
|
+
*
|
|
196
|
+
* ```ts
|
|
197
|
+
* const result = await admin.events.subscriptions.sendTest(id, 'order.created');
|
|
198
|
+
* if (!result.delivered) console.error(result.status ?? result.error);
|
|
199
|
+
* ```
|
|
200
|
+
*
|
|
201
|
+
* The envelope is signed, redacted and SSRF-checked like a real delivery, with a fresh
|
|
202
|
+
* `id`, `actor: { type: 'system', id: 'system:send-test' }` and `test: true` — acknowledge
|
|
203
|
+
* it and do not act on it. It is never written to the event log (not in `poll` or
|
|
204
|
+
* `stream`), never moves the subscription's position and never counts toward auto-disable.
|
|
205
|
+
* `data` defaults to the platform's example payload for the type.
|
|
206
|
+
*
|
|
207
|
+
* Works on a disabled subscription (check a fixed endpoint before `setEnabled(id, true)`).
|
|
208
|
+
* Refused: an uncatalogued type (`ValidationError`, `UNKNOWN_EVENT_TYPE`); a type outside
|
|
209
|
+
* the subscription's filters (`BusinessRuleError`, 422, `EVENT_TYPE_NOT_SUBSCRIBED`); an
|
|
210
|
+
* app's subscription (`ConflictError`, `SUBSCRIPTION_OWNED_BY_APP`); a closed store
|
|
211
|
+
* (`PermissionDeniedError`, 403, `CHANNEL_SOFT_DELETED`, as everywhere else). Takes an idempotency key: a retry under the same key
|
|
212
|
+
* replays the first answer instead of sending a second test.
|
|
213
|
+
*/
|
|
214
|
+
sendTest(id: string, type: string, options?: EventSendTestOptions): Promise<EventSendTestResult>;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Platform events (headless spec §6.9): pull the store's event log, answer webhooks, and
|
|
218
|
+
* manage where they are pushed.
|
|
219
|
+
*
|
|
220
|
+
* `poll` / `pollAll` / `replay` / `head` read `GET /events`, and `stream` / `streamTicket`
|
|
221
|
+
* push the same events over Server-Sent Events (`GET /events/stream`); all of them need a
|
|
222
|
+
* MACHINE credential holding `ReadPlatformEvents` and scoped to one channel, and a stream
|
|
223
|
+
* cursor and a feed cursor are interchangeable. A credential's live streams are limited
|
|
224
|
+
* (one by default): opening another closes the oldest (`EventStreamClosedError`,
|
|
225
|
+
* `superseded`). Events reach the log
|
|
226
|
+
* through relays that tick once a minute, so a stream delivers them up to about a minute
|
|
227
|
+
* after the change.
|
|
228
|
+
*
|
|
229
|
+
* `subscriptions.*`, `redrive` and `redrives` need `ManageEventSubscriptions` — the `events`
|
|
230
|
+
* API-token template grants it beside `ReadPlatformEvents`. Reads also accept `ReadSettings`;
|
|
231
|
+
* writes still accept `UpdateSettings` for credentials minted before the permission existed,
|
|
232
|
+
* a transition that a later release ends (announced in the changelog first).
|
|
233
|
+
*/
|
|
234
|
+
export interface EventsModule extends EventsClient {
|
|
235
|
+
/**
|
|
236
|
+
* `events.subscribe` — build a webhook endpoint `(Request) => Promise<Response>` with typed
|
|
237
|
+
* per-event handlers. Needs no credential: it verifies with the subscription secret.
|
|
238
|
+
*/
|
|
239
|
+
subscribe: typeof createEventHandler;
|
|
240
|
+
subscriptions: EventSubscriptionsModule;
|
|
241
|
+
/**
|
|
242
|
+
* Needs `ManageEventSubscriptions` (or `UpdateSettings`). Queue chosen events to be delivered to ONE subscription again — a
|
|
243
|
+
* dead letter, or any event still in the log that passes the subscription's filters (a
|
|
244
|
+
* replay to one destination). 1-100 ids. Answers a state per id; only `queued` ids will be
|
|
245
|
+
* delivered.
|
|
246
|
+
*
|
|
247
|
+
* A queued id is delivered again with the same envelope `id`, so `webhook-id` dedupe holds;
|
|
248
|
+
* only `delivered_at` / `webhook-timestamp` move. `redrives` shows it `delivered`, or
|
|
249
|
+
* `failed` when the endpoint dead-letters it again or keeps refusing it (8 attempts). Events
|
|
250
|
+
* that have left the log (`expired`) are recovered from the pull feed (`events.replay`).
|
|
251
|
+
* A disabled subscription is refused
|
|
252
|
+
* (`BusinessRuleError`, 422, `SUBSCRIPTION_DISABLED`). Takes an idempotency key: a retry
|
|
253
|
+
* under the same key replays the first answer.
|
|
254
|
+
*/
|
|
255
|
+
redrive(subscriptionId: string, eventIds: readonly string[], options?: Call): Promise<RedriveAnswer>;
|
|
256
|
+
/** Needs `ReadSettings` or `ManageEventSubscriptions`. One subscription's redrive requests, newest first. */
|
|
257
|
+
redrives(subscriptionId: string, options?: ListOptions): Promise<Page<EventRedrive>>;
|
|
258
|
+
}
|
|
259
|
+
export declare function createEventsModule(transport: Transport): EventsModule;
|