@sixtyninecommerce/admin 0.9.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 +294 -0
- package/dist/catalog/products.d.ts +162 -1
- package/dist/catalog/products.js +94 -1
- package/dist/catalog/variants.d.ts +50 -0
- package/dist/catalog/variants.js +48 -18
- package/dist/client.d.ts +21 -0
- package/dist/client.js +6 -0
- package/dist/fulfillments/index.d.ts +77 -0
- package/dist/fulfillments/index.js +109 -0
- package/dist/index.d.ts +15 -4
- package/dist/index.js +5 -0
- 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 +11 -2
- package/dist/jobs/bulk-action-run.js +4 -5
- package/dist/orders/bulk.d.ts +75 -0
- package/dist/orders/bulk.js +150 -0
- package/dist/orders/deposit-policy.d.ts +103 -0
- package/dist/orders/deposit-policy.js +202 -0
- package/dist/orders/index.d.ts +80 -0
- package/dist/orders/index.js +13 -0
- package/dist/orders/manual-discounts.d.ts +11 -2
- package/dist/orders/manual-discounts.js +2 -0
- package/dist/payments/index.d.ts +7 -0
- package/dist/payments/index.js +2 -0
- package/dist/payments/methods.d.ts +37 -0
- package/dist/payments/methods.js +33 -0
- package/dist/pricing/index.d.ts +3 -0
- package/dist/pricing/index.js +2 -0
- package/dist/pricing/vouchers.d.ts +103 -0
- package/dist/pricing/vouchers.js +69 -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/types.d.ts +2 -2
- package/package.json +1 -1
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import type { Transport } from '@sixtyninecommerce/core';
|
|
2
|
+
import type { Call } from '../types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Personal vouchers (incentives sprint 9): a voucher TEMPLATE under a promotion, codes issued one
|
|
5
|
+
* by one to a customer of the store, single use, with an optional expiry and the issuing journey
|
|
6
|
+
* recorded as an opaque `source`. Applied like any coupon (`pricing.coupons.apply`, the till's
|
|
7
|
+
* `pos.sale.applyCouponCode`), which refuse `VOUCHER_NOT_FOR_CUSTOMER`, `VOUCHER_EXPIRED` and
|
|
8
|
+
* `VOUCHER_ALREADY_REDEEMED`. Issuing needs `CreatePromotion`.
|
|
9
|
+
*/
|
|
10
|
+
/** A customer of this store: exactly one of its id or a master system's key (PR #1176). */
|
|
11
|
+
export type VoucherCustomer = {
|
|
12
|
+
id: string;
|
|
13
|
+
} | {
|
|
14
|
+
externalRef: {
|
|
15
|
+
system: string;
|
|
16
|
+
ref: string;
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
export interface IssueVoucherInput {
|
|
20
|
+
templateId: string;
|
|
21
|
+
customer: VoucherCustomer;
|
|
22
|
+
/** Usable through the end of the store's local day this many days after today (1 to 3650). */
|
|
23
|
+
validityDays?: number;
|
|
24
|
+
/** Or an explicit instant, ISO-8601. Neither: the template's default, else the promotion's window. */
|
|
25
|
+
expiresAt?: string;
|
|
26
|
+
/** The issuing journey; with a `ref`, an issue is exactly-once per template, customer and source. */
|
|
27
|
+
source: {
|
|
28
|
+
system: string;
|
|
29
|
+
ref?: string;
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
export interface IssueVouchersInput {
|
|
33
|
+
templateId: string;
|
|
34
|
+
sourceSystem: string;
|
|
35
|
+
/** At most 1,000. */
|
|
36
|
+
items: Array<{
|
|
37
|
+
customer: VoucherCustomer;
|
|
38
|
+
validityDays?: number;
|
|
39
|
+
expiresAt?: string;
|
|
40
|
+
sourceRef?: string;
|
|
41
|
+
}>;
|
|
42
|
+
}
|
|
43
|
+
export interface IssuedVoucher {
|
|
44
|
+
id: string;
|
|
45
|
+
code: string;
|
|
46
|
+
customerId: string;
|
|
47
|
+
/** The earlier of its own expiry and its promotion's end; null when neither ends. */
|
|
48
|
+
expiresAt: string | null;
|
|
49
|
+
/** False when this template, customer and source had issued it already: the same code. */
|
|
50
|
+
created: boolean;
|
|
51
|
+
}
|
|
52
|
+
export interface IssueVouchersItem {
|
|
53
|
+
index: number;
|
|
54
|
+
voucher: IssuedVoucher | null;
|
|
55
|
+
/** A `VOUCHER_*` code or `ENTITY_NOT_FOUND` for a refused item; the others were issued. */
|
|
56
|
+
errorCode: string | null;
|
|
57
|
+
message: string | null;
|
|
58
|
+
}
|
|
59
|
+
export type VoucherStatus = 'OPEN' | 'REDEEMED' | 'EXPIRED' | 'REVOKED';
|
|
60
|
+
export interface Voucher {
|
|
61
|
+
id: string;
|
|
62
|
+
code: string;
|
|
63
|
+
templateId: string;
|
|
64
|
+
promotionId: string;
|
|
65
|
+
customerId: string | null;
|
|
66
|
+
status: VoucherStatus;
|
|
67
|
+
issuedAt: string;
|
|
68
|
+
expiresAt: string | null;
|
|
69
|
+
sourceSystem: string | null;
|
|
70
|
+
sourceRef: string | null;
|
|
71
|
+
/** The order that redeemed it. */
|
|
72
|
+
orderId: string | null;
|
|
73
|
+
}
|
|
74
|
+
export interface VoucherTemplate {
|
|
75
|
+
id: string;
|
|
76
|
+
promotionId: string;
|
|
77
|
+
prefix: string;
|
|
78
|
+
codeLength: number;
|
|
79
|
+
defaultValidityDays: number | null;
|
|
80
|
+
}
|
|
81
|
+
export interface PricingVouchersModule {
|
|
82
|
+
/** A voucher template under a promotion of this store (codes of 12 to 32 random characters). */
|
|
83
|
+
createTemplate(input: {
|
|
84
|
+
promotionId: string;
|
|
85
|
+
prefix: string;
|
|
86
|
+
codeLength?: number;
|
|
87
|
+
defaultValidityDays?: number;
|
|
88
|
+
}, options?: Call): Promise<VoucherTemplate>;
|
|
89
|
+
/**
|
|
90
|
+
* Issue one voucher. `options.idempotencyKey` replays the first answer; the same template,
|
|
91
|
+
* customer and `source.ref` answers the voucher already issued (`created: false`) under any key.
|
|
92
|
+
*/
|
|
93
|
+
issue(input: IssueVoucherInput, options?: Call): Promise<IssuedVoucher>;
|
|
94
|
+
/** Issue up to 1,000 vouchers of one template; a refused item answers in its place. */
|
|
95
|
+
issueMany(input: IssueVouchersInput, options?: Call): Promise<IssueVouchersItem[]>;
|
|
96
|
+
/** A customer's vouchers in this store, newest first; every status when `status` is omitted. */
|
|
97
|
+
forCustomer(customer: VoucherCustomer, status?: VoucherStatus, options?: Call): Promise<Voucher[]>;
|
|
98
|
+
}
|
|
99
|
+
export declare const CREATE_VOUCHER_TEMPLATE_MUTATION = "\n mutation CreateVoucherTemplate($input: CreateVoucherTemplateInput!) {\n createVoucherTemplate(input: $input) {\n id\n promotionId\n prefix\n codeLength\n defaultValidityDays\n }\n }\n";
|
|
100
|
+
export declare const ISSUE_VOUCHER_MUTATION = "\n mutation IssueVoucher($input: IssueVoucherInput!, $idempotencyKey: String) {\n issueVoucher(input: $input, idempotencyKey: $idempotencyKey) { id code customerId expiresAt created }\n }\n";
|
|
101
|
+
export declare const ISSUE_VOUCHERS_MUTATION = "\n mutation IssueVouchers($input: IssueVouchersInput!, $idempotencyKey: String) {\n issueVouchers(input: $input, idempotencyKey: $idempotencyKey) {\n items { index errorCode message voucher { id code customerId expiresAt created } }\n }\n }\n";
|
|
102
|
+
export declare const VOUCHERS_FOR_CUSTOMER_QUERY = "\n query VouchersForCustomer($customer: VoucherCustomerInput!, $status: VoucherStatus) {\n vouchersForCustomer(customer: $customer, status: $status) {\n id\n code\n templateId\n promotionId\n customerId\n status\n issuedAt\n expiresAt\n sourceSystem\n sourceRef\n orderId\n }\n }\n";
|
|
103
|
+
export declare function createPricingVouchersModule(transport: Transport): PricingVouchersModule;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
const ISSUED_VOUCHER_FIELDS = `id code customerId expiresAt created`;
|
|
2
|
+
export const CREATE_VOUCHER_TEMPLATE_MUTATION = /* GraphQL */ `
|
|
3
|
+
mutation CreateVoucherTemplate($input: CreateVoucherTemplateInput!) {
|
|
4
|
+
createVoucherTemplate(input: $input) {
|
|
5
|
+
id
|
|
6
|
+
promotionId
|
|
7
|
+
prefix
|
|
8
|
+
codeLength
|
|
9
|
+
defaultValidityDays
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
`;
|
|
13
|
+
export const ISSUE_VOUCHER_MUTATION = /* GraphQL */ `
|
|
14
|
+
mutation IssueVoucher($input: IssueVoucherInput!, $idempotencyKey: String) {
|
|
15
|
+
issueVoucher(input: $input, idempotencyKey: $idempotencyKey) { ${ISSUED_VOUCHER_FIELDS} }
|
|
16
|
+
}
|
|
17
|
+
`;
|
|
18
|
+
export const ISSUE_VOUCHERS_MUTATION = /* GraphQL */ `
|
|
19
|
+
mutation IssueVouchers($input: IssueVouchersInput!, $idempotencyKey: String) {
|
|
20
|
+
issueVouchers(input: $input, idempotencyKey: $idempotencyKey) {
|
|
21
|
+
items { index errorCode message voucher { ${ISSUED_VOUCHER_FIELDS} } }
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
`;
|
|
25
|
+
export const VOUCHERS_FOR_CUSTOMER_QUERY = /* GraphQL */ `
|
|
26
|
+
query VouchersForCustomer($customer: VoucherCustomerInput!, $status: VoucherStatus) {
|
|
27
|
+
vouchersForCustomer(customer: $customer, status: $status) {
|
|
28
|
+
id
|
|
29
|
+
code
|
|
30
|
+
templateId
|
|
31
|
+
promotionId
|
|
32
|
+
customerId
|
|
33
|
+
status
|
|
34
|
+
issuedAt
|
|
35
|
+
expiresAt
|
|
36
|
+
sourceSystem
|
|
37
|
+
sourceRef
|
|
38
|
+
orderId
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
`;
|
|
42
|
+
export function createPricingVouchersModule(transport) {
|
|
43
|
+
return {
|
|
44
|
+
async createTemplate(input, options) {
|
|
45
|
+
const data = await transport.graphql({ api: 'admin', query: CREATE_VOUCHER_TEMPLATE_MUTATION, variables: { input } }, options);
|
|
46
|
+
return data.createVoucherTemplate;
|
|
47
|
+
},
|
|
48
|
+
async issue(input, options) {
|
|
49
|
+
const data = await transport.graphql({
|
|
50
|
+
api: 'admin',
|
|
51
|
+
query: ISSUE_VOUCHER_MUTATION,
|
|
52
|
+
variables: { input, idempotencyKey: options?.idempotencyKey },
|
|
53
|
+
}, options);
|
|
54
|
+
return data.issueVoucher;
|
|
55
|
+
},
|
|
56
|
+
async issueMany(input, options) {
|
|
57
|
+
const data = await transport.graphql({
|
|
58
|
+
api: 'admin',
|
|
59
|
+
query: ISSUE_VOUCHERS_MUTATION,
|
|
60
|
+
variables: { input, idempotencyKey: options?.idempotencyKey },
|
|
61
|
+
}, options);
|
|
62
|
+
return data.issueVouchers.items;
|
|
63
|
+
},
|
|
64
|
+
async forCustomer(customer, status, options) {
|
|
65
|
+
const data = await transport.graphql({ api: 'admin', query: VOUCHERS_FOR_CUSTOMER_QUERY, variables: { customer, status } }, options);
|
|
66
|
+
return data.vouchersForCustomer;
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
}
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `admin.returns`: the single-return reads of the returns (RMA) plugin, and the
|
|
3
|
+
* plan for settling one (headless plan W9-47).
|
|
4
|
+
*
|
|
5
|
+
* A first, minimal module: the reads the order and return pages need. The list
|
|
6
|
+
* (`orderReturns`) and the writes (create, approve, reject, cancel, receipt,
|
|
7
|
+
* settle, notes, numbering) are phase 7. Money is in MINOR UNITS throughout,
|
|
8
|
+
* as everywhere in this SDK.
|
|
9
|
+
*
|
|
10
|
+
* Every value set below is the engine's own and grows over time, so each field
|
|
11
|
+
* that carries one also accepts a string this version does not name: treat an
|
|
12
|
+
* unknown value as "not available".
|
|
13
|
+
*/
|
|
14
|
+
import type { Transport } from '@sixtyninecommerce/core';
|
|
15
|
+
import type { Call } from '../types.js';
|
|
16
|
+
/** A return's state: the latest row of its status log. */
|
|
17
|
+
export declare const RETURN_STATES: readonly ["requested", "approved", "rejected", "cancelled", "items_received", "settlement_pending", "settlement_failed", "settled"];
|
|
18
|
+
export type ReturnState = (typeof RETURN_STATES)[number];
|
|
19
|
+
/**
|
|
20
|
+
* How a return is settled. `refund` gives money back to an original payment.
|
|
21
|
+
* `store_credit` issues a credit note: on a store with an accounts-receivable
|
|
22
|
+
* ledger it credits the customer's account, elsewhere it credits the order's
|
|
23
|
+
* invoice and moves no money.
|
|
24
|
+
*/
|
|
25
|
+
export declare const RETURN_SETTLEMENT_METHODS: readonly ["refund", "store_credit"];
|
|
26
|
+
export type ReturnSettlementMethod = (typeof RETURN_SETTLEMENT_METHODS)[number];
|
|
27
|
+
export interface OrderReturnLine {
|
|
28
|
+
id: string;
|
|
29
|
+
returnId: string;
|
|
30
|
+
orderLineId: string;
|
|
31
|
+
productVariantId: string;
|
|
32
|
+
productVariantName: string;
|
|
33
|
+
productVariantSku: string;
|
|
34
|
+
requestedQty: number;
|
|
35
|
+
/** Null until approval; 0 means the line was declined. */
|
|
36
|
+
approvedQty: number | null;
|
|
37
|
+
/** Units received back, across every receipt. */
|
|
38
|
+
receivedQty: number;
|
|
39
|
+
restockQty: number;
|
|
40
|
+
writeOffQty: number;
|
|
41
|
+
restockLocationId: string | null;
|
|
42
|
+
/** `damaged`, `defective`, `wrong_item`, `not_as_described`, `arrived_late`, `no_longer_needed`, `order_error`, `other`. */
|
|
43
|
+
reasonCode: string;
|
|
44
|
+
reasonNote: string | null;
|
|
45
|
+
/** The order line's prorated unit price incl. tax, rounded per unit. Display only. */
|
|
46
|
+
unitPriceWithTax: number;
|
|
47
|
+
/**
|
|
48
|
+
* The same price before per-unit rounding; null for returns requested before
|
|
49
|
+
* it was recorded. Multiply by a quantity and round ONCE to estimate a line.
|
|
50
|
+
*/
|
|
51
|
+
unitPriceWithTaxUnrounded: number | null;
|
|
52
|
+
/** What the line refunds; null until approved. */
|
|
53
|
+
refundAmount: number | null;
|
|
54
|
+
createdAt: string;
|
|
55
|
+
updatedAt: string;
|
|
56
|
+
}
|
|
57
|
+
/** One row of a return's status log. */
|
|
58
|
+
export interface OrderReturnStatusLogEntry {
|
|
59
|
+
state: ReturnState | (string & {});
|
|
60
|
+
/** `customer`, `admin` or `system`. */
|
|
61
|
+
actor: string;
|
|
62
|
+
actorUserId: string | null;
|
|
63
|
+
reason: string | null;
|
|
64
|
+
createdAt: string;
|
|
65
|
+
}
|
|
66
|
+
export interface OrderReturn {
|
|
67
|
+
id: string;
|
|
68
|
+
/** The human-facing RMA code, e.g. `RMA-000042`. */
|
|
69
|
+
code: string;
|
|
70
|
+
channelId: string;
|
|
71
|
+
orderId: string;
|
|
72
|
+
orderCode: string;
|
|
73
|
+
customerId: string;
|
|
74
|
+
currencyCode: string;
|
|
75
|
+
state: ReturnState | (string & {});
|
|
76
|
+
/** `customer` or `admin`. */
|
|
77
|
+
initiator: string;
|
|
78
|
+
customerNote: string | null;
|
|
79
|
+
adminNote: string | null;
|
|
80
|
+
preferredSettlementMethod: ReturnSettlementMethod | (string & {}) | null;
|
|
81
|
+
/** The method it was settled with; null until settled. */
|
|
82
|
+
settlementMethod: ReturnSettlementMethod | (string & {}) | null;
|
|
83
|
+
/** Shipping refunded on top of the lines. */
|
|
84
|
+
shippingRefundAmount: number;
|
|
85
|
+
/** What was settled; null until settled. */
|
|
86
|
+
settledAmount: number | null;
|
|
87
|
+
refundId: string | null;
|
|
88
|
+
creditNoteId: string | null;
|
|
89
|
+
creditNoteNumber: string | null;
|
|
90
|
+
requestedAt: string;
|
|
91
|
+
resolvedAt: string | null;
|
|
92
|
+
lines: OrderReturnLine[];
|
|
93
|
+
createdAt: string;
|
|
94
|
+
updatedAt: string;
|
|
95
|
+
}
|
|
96
|
+
/** {@link OrderReturn} with its status log, newest first: what `get` answers. */
|
|
97
|
+
export interface OrderReturnDetail extends OrderReturn {
|
|
98
|
+
history: OrderReturnStatusLogEntry[];
|
|
99
|
+
}
|
|
100
|
+
/** One order line and what of it can still be returned. */
|
|
101
|
+
export interface ReturnableOrderLine {
|
|
102
|
+
orderLineId: string;
|
|
103
|
+
productVariantId: string;
|
|
104
|
+
productVariantName: string;
|
|
105
|
+
productVariantSku: string;
|
|
106
|
+
/** Units shipped or delivered. */
|
|
107
|
+
fulfilledQty: number;
|
|
108
|
+
/** `fulfilledQty` less the units other open returns already claim. */
|
|
109
|
+
returnableQty: number;
|
|
110
|
+
unitPriceWithTax: number;
|
|
111
|
+
unitPriceWithTaxUnrounded: number;
|
|
112
|
+
}
|
|
113
|
+
export interface ReturnEligibility {
|
|
114
|
+
orderId: string;
|
|
115
|
+
/** False only when nothing is returnable: an admin may return outside the window. */
|
|
116
|
+
eligible: boolean;
|
|
117
|
+
/** Why not, when not eligible. */
|
|
118
|
+
reason: string | null;
|
|
119
|
+
/** ISO-8601 with offset; null when the store sets no window. */
|
|
120
|
+
returnWindowEndsAt: string | null;
|
|
121
|
+
/** Lines with a fulfilled quantity only. */
|
|
122
|
+
lines: ReturnableOrderLine[];
|
|
123
|
+
}
|
|
124
|
+
/** Why a return cannot be settled now, in the order the engine checks them. */
|
|
125
|
+
export declare const RETURN_SETTLEMENT_BLOCKERS: readonly ["OUTCOME_UNKNOWN", "ALREADY_SETTLED", "IN_PROGRESS", "RECEIPT_REQUIRED", "STATE"];
|
|
126
|
+
/**
|
|
127
|
+
* - `OUTCOME_UNKNOWN`: the refund it waits on reached the provider and was never
|
|
128
|
+
* confirmed. Record the provider's answer first (`reconcileRefundOutcome`):
|
|
129
|
+
* made completes the return, not made lets it be settled again.
|
|
130
|
+
* - `ALREADY_SETTLED`: settled, or settled by a credit note.
|
|
131
|
+
* - `IN_PROGRESS`: a refund is on its way and settles the return when the
|
|
132
|
+
* provider confirms it.
|
|
133
|
+
* - `RECEIPT_REQUIRED`: approved, and the store requires the goods received first.
|
|
134
|
+
* - `STATE`: requested, rejected or cancelled.
|
|
135
|
+
*/
|
|
136
|
+
export type ReturnSettlementBlocker = (typeof RETURN_SETTLEMENT_BLOCKERS)[number];
|
|
137
|
+
/**
|
|
138
|
+
* - `NO_REFUNDABLE_PAYMENT`: no settled payment can still give money back (net
|
|
139
|
+
* terms, everything refunded, reserved by a refund of unknown outcome, or
|
|
140
|
+
* moved to the customer's account).
|
|
141
|
+
* - `REFUND_ORPHAN_EXISTS`: a refund of the invoiced order stands that no credit
|
|
142
|
+
* note names; another refund could pay the same money back twice.
|
|
143
|
+
*/
|
|
144
|
+
export type ReturnRefundUnavailableReason = 'NO_REFUNDABLE_PAYMENT' | 'REFUND_ORPHAN_EXISTS';
|
|
145
|
+
/**
|
|
146
|
+
* - `AR`: the store keeps an accounts-receivable ledger; the credit note credits
|
|
147
|
+
* the customer's account, which is money.
|
|
148
|
+
* - `RETAIL`: the credit note credits the order's invoice and moves no money.
|
|
149
|
+
*/
|
|
150
|
+
export type ReturnCreditNoteLedger = 'AR' | 'RETAIL';
|
|
151
|
+
/**
|
|
152
|
+
* - `ISSUER_UNAVAILABLE`: the server runs without the plugin that issues it.
|
|
153
|
+
* - `CURRENCY_MISMATCH` (`AR`): the order is not in the store's default currency.
|
|
154
|
+
* - `NO_CREDITABLE_INVOICE` (`RETAIL`): the order has no live invoice to credit.
|
|
155
|
+
* - `REFUND_ORPHAN_EXISTS` (`AR`): as for the refund.
|
|
156
|
+
*/
|
|
157
|
+
export type ReturnCreditNoteUnavailableReason = 'ISSUER_UNAVAILABLE' | 'CURRENCY_MISMATCH' | 'NO_CREDITABLE_INVOICE' | 'REFUND_ORPHAN_EXISTS';
|
|
158
|
+
/** A settled payment a refund may name, with what it can still give back. */
|
|
159
|
+
export interface ReturnSettlementPayment {
|
|
160
|
+
paymentId: string;
|
|
161
|
+
method: string;
|
|
162
|
+
/** As the platform's refund position counts it. */
|
|
163
|
+
refundable: number;
|
|
164
|
+
}
|
|
165
|
+
export interface ReturnSettlementOptions {
|
|
166
|
+
returnId: string;
|
|
167
|
+
currencyCode: string;
|
|
168
|
+
settleable: boolean;
|
|
169
|
+
/** Null exactly when `settleable`. */
|
|
170
|
+
blockedBy: ReturnSettlementBlocker | (string & {}) | null;
|
|
171
|
+
refund: {
|
|
172
|
+
available: boolean;
|
|
173
|
+
/** Null exactly when `available`. */
|
|
174
|
+
reasonCode: ReturnRefundUnavailableReason | (string & {}) | null;
|
|
175
|
+
/** Empty when not available. */
|
|
176
|
+
payments: ReturnSettlementPayment[];
|
|
177
|
+
};
|
|
178
|
+
creditNote: {
|
|
179
|
+
available: boolean;
|
|
180
|
+
ledger: ReturnCreditNoteLedger | (string & {});
|
|
181
|
+
/** Null exactly when `available`. */
|
|
182
|
+
reasonCode: ReturnCreditNoteUnavailableReason | (string & {}) | null;
|
|
183
|
+
/** `RETAIL` only: the invoice it would credit, when known. */
|
|
184
|
+
invoiceNumber: string | null;
|
|
185
|
+
};
|
|
186
|
+
/** The credit note when asked for or when no refund is possible, and it is available; else `refund`. */
|
|
187
|
+
initialMethod: ReturnSettlementMethod | (string & {});
|
|
188
|
+
/** The approved line total: what a settlement without an override settles, before shipping. */
|
|
189
|
+
defaultAmount: number;
|
|
190
|
+
}
|
|
191
|
+
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 history {\n state\n actor\n actorUserId\n reason\n createdAt\n }\n }\n }\n";
|
|
192
|
+
export declare const RETURNS_FOR_ORDER_QUERY = "\n query SdkReturnsForOrder($orderId: ID!) {\n returnsForOrder(orderId: $orderId) {\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 }\n }\n";
|
|
193
|
+
export declare const RETURN_ELIGIBILITY_QUERY = "\n query SdkReturnEligibility($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";
|
|
194
|
+
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 initialMethod\n defaultAmount\n }\n }\n";
|
|
195
|
+
export interface ReturnsModule {
|
|
196
|
+
/**
|
|
197
|
+
* One return with its lines and its status log (newest first), or `null`
|
|
198
|
+
* when this store holds no return with that id. Needs `ReadOrderReturn`.
|
|
199
|
+
*/
|
|
200
|
+
get(id: string, options?: Call): Promise<OrderReturnDetail | null>;
|
|
201
|
+
/** Every return of one order, newest first. Needs `ReadOrderReturn`. */
|
|
202
|
+
forOrder(orderId: string, options?: Call): Promise<OrderReturn[]>;
|
|
203
|
+
/**
|
|
204
|
+
* What of an order can still be returned, line by line, and the return
|
|
205
|
+
* window. The window is reported, never enforced here: an admin may return
|
|
206
|
+
* outside it. Needs `ReadOrderReturn`.
|
|
207
|
+
*/
|
|
208
|
+
eligibility(orderId: string, options?: Call): Promise<ReturnEligibility>;
|
|
209
|
+
/**
|
|
210
|
+
* How a return can be settled now: whether it is settleable (and why not),
|
|
211
|
+
* which methods are open (with the payments a refund may name and what each
|
|
212
|
+
* can still give back), and which method to preselect. Needs
|
|
213
|
+
* `ReadOrderReturn` AND `ReadOrder`: it names the order's payments.
|
|
214
|
+
*
|
|
215
|
+
* This is the PLAN. The write (`settleReturn`) loads its own facts and judges
|
|
216
|
+
* again, so settling can still be refused when something changed in between,
|
|
217
|
+
* and an `amountOverride` above a payment's headroom is refused there. A
|
|
218
|
+
* return this store does not hold is a `NotFoundError` (`ENTITY_NOT_FOUND`).
|
|
219
|
+
*
|
|
220
|
+
* Needs an engine that serves `returnSettlementOptions`; against an older one
|
|
221
|
+
* this call fails validation and nothing else does.
|
|
222
|
+
*/
|
|
223
|
+
settlementOptions(returnId: string, options?: Call): Promise<ReturnSettlementOptions>;
|
|
224
|
+
}
|
|
225
|
+
export declare function createReturnsModule(transport: Transport): ReturnsModule;
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/** A return's state: the latest row of its status log. */
|
|
2
|
+
export const RETURN_STATES = [
|
|
3
|
+
'requested',
|
|
4
|
+
'approved',
|
|
5
|
+
'rejected',
|
|
6
|
+
'cancelled',
|
|
7
|
+
'items_received',
|
|
8
|
+
'settlement_pending',
|
|
9
|
+
'settlement_failed',
|
|
10
|
+
'settled',
|
|
11
|
+
];
|
|
12
|
+
/**
|
|
13
|
+
* How a return is settled. `refund` gives money back to an original payment.
|
|
14
|
+
* `store_credit` issues a credit note: on a store with an accounts-receivable
|
|
15
|
+
* ledger it credits the customer's account, elsewhere it credits the order's
|
|
16
|
+
* invoice and moves no money.
|
|
17
|
+
*/
|
|
18
|
+
export const RETURN_SETTLEMENT_METHODS = ['refund', 'store_credit'];
|
|
19
|
+
/** Why a return cannot be settled now, in the order the engine checks them. */
|
|
20
|
+
export const RETURN_SETTLEMENT_BLOCKERS = [
|
|
21
|
+
'OUTCOME_UNKNOWN',
|
|
22
|
+
'ALREADY_SETTLED',
|
|
23
|
+
'IN_PROGRESS',
|
|
24
|
+
'RECEIPT_REQUIRED',
|
|
25
|
+
'STATE',
|
|
26
|
+
];
|
|
27
|
+
/** The fields of an `OrderReturn` every read selects (a selection, spliced into each document). */
|
|
28
|
+
const RETURN_FIELDS = /* GraphQL */ `
|
|
29
|
+
id
|
|
30
|
+
code
|
|
31
|
+
channelId
|
|
32
|
+
orderId
|
|
33
|
+
orderCode
|
|
34
|
+
customerId
|
|
35
|
+
currencyCode
|
|
36
|
+
state
|
|
37
|
+
initiator
|
|
38
|
+
customerNote
|
|
39
|
+
adminNote
|
|
40
|
+
preferredSettlementMethod
|
|
41
|
+
settlementMethod
|
|
42
|
+
shippingRefundAmount
|
|
43
|
+
settledAmount
|
|
44
|
+
refundId
|
|
45
|
+
creditNoteId
|
|
46
|
+
creditNoteNumber
|
|
47
|
+
requestedAt
|
|
48
|
+
resolvedAt
|
|
49
|
+
createdAt
|
|
50
|
+
updatedAt
|
|
51
|
+
lines {
|
|
52
|
+
id
|
|
53
|
+
returnId
|
|
54
|
+
orderLineId
|
|
55
|
+
productVariantId
|
|
56
|
+
productVariantName
|
|
57
|
+
productVariantSku
|
|
58
|
+
requestedQty
|
|
59
|
+
approvedQty
|
|
60
|
+
receivedQty
|
|
61
|
+
restockQty
|
|
62
|
+
writeOffQty
|
|
63
|
+
restockLocationId
|
|
64
|
+
reasonCode
|
|
65
|
+
reasonNote
|
|
66
|
+
unitPriceWithTax
|
|
67
|
+
unitPriceWithTaxUnrounded
|
|
68
|
+
refundAmount
|
|
69
|
+
createdAt
|
|
70
|
+
updatedAt
|
|
71
|
+
}
|
|
72
|
+
`;
|
|
73
|
+
export const RETURN_QUERY = /* GraphQL */ `
|
|
74
|
+
query SdkOrderReturn($id: ID!) {
|
|
75
|
+
orderReturn(id: $id) {
|
|
76
|
+
${RETURN_FIELDS}
|
|
77
|
+
history {
|
|
78
|
+
state
|
|
79
|
+
actor
|
|
80
|
+
actorUserId
|
|
81
|
+
reason
|
|
82
|
+
createdAt
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
`;
|
|
87
|
+
export const RETURNS_FOR_ORDER_QUERY = /* GraphQL */ `
|
|
88
|
+
query SdkReturnsForOrder($orderId: ID!) {
|
|
89
|
+
returnsForOrder(orderId: $orderId) {
|
|
90
|
+
${RETURN_FIELDS}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
`;
|
|
94
|
+
export const RETURN_ELIGIBILITY_QUERY = /* GraphQL */ `
|
|
95
|
+
query SdkReturnEligibility($orderId: ID!) {
|
|
96
|
+
returnEligibilityForOrder(orderId: $orderId) {
|
|
97
|
+
orderId
|
|
98
|
+
eligible
|
|
99
|
+
reason
|
|
100
|
+
returnWindowEndsAt
|
|
101
|
+
lines {
|
|
102
|
+
orderLineId
|
|
103
|
+
productVariantId
|
|
104
|
+
productVariantName
|
|
105
|
+
productVariantSku
|
|
106
|
+
fulfilledQty
|
|
107
|
+
returnableQty
|
|
108
|
+
unitPriceWithTax
|
|
109
|
+
unitPriceWithTaxUnrounded
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
`;
|
|
114
|
+
export const RETURN_SETTLEMENT_OPTIONS_QUERY = /* GraphQL */ `
|
|
115
|
+
query SdkReturnSettlementOptions($returnId: ID!) {
|
|
116
|
+
returnSettlementOptions(returnId: $returnId) {
|
|
117
|
+
returnId
|
|
118
|
+
currencyCode
|
|
119
|
+
settleable
|
|
120
|
+
blockedBy
|
|
121
|
+
refund {
|
|
122
|
+
available
|
|
123
|
+
reasonCode
|
|
124
|
+
payments {
|
|
125
|
+
paymentId
|
|
126
|
+
method
|
|
127
|
+
refundable
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
creditNote {
|
|
131
|
+
available
|
|
132
|
+
ledger
|
|
133
|
+
reasonCode
|
|
134
|
+
invoiceNumber
|
|
135
|
+
}
|
|
136
|
+
initialMethod
|
|
137
|
+
defaultAmount
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
`;
|
|
141
|
+
export function createReturnsModule(transport) {
|
|
142
|
+
return {
|
|
143
|
+
async get(id, options) {
|
|
144
|
+
const data = await transport.graphql({ api: 'admin', query: RETURN_QUERY, variables: { id } }, options);
|
|
145
|
+
return data.orderReturn;
|
|
146
|
+
},
|
|
147
|
+
async forOrder(orderId, options) {
|
|
148
|
+
const data = await transport.graphql({ api: 'admin', query: RETURNS_FOR_ORDER_QUERY, variables: { orderId } }, options);
|
|
149
|
+
return data.returnsForOrder;
|
|
150
|
+
},
|
|
151
|
+
async eligibility(orderId, options) {
|
|
152
|
+
const data = await transport.graphql({ api: 'admin', query: RETURN_ELIGIBILITY_QUERY, variables: { orderId } }, options);
|
|
153
|
+
return data.returnEligibilityForOrder;
|
|
154
|
+
},
|
|
155
|
+
async settlementOptions(returnId, options) {
|
|
156
|
+
const data = await transport.graphql({ api: 'admin', query: RETURN_SETTLEMENT_OPTIONS_QUERY, variables: { returnId } }, options);
|
|
157
|
+
return data.returnSettlementOptions;
|
|
158
|
+
},
|
|
159
|
+
};
|
|
160
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import type { Transport } from '@sixtyninecommerce/core';
|
|
2
|
+
import type { Call } from '../types.js';
|
|
3
|
+
/** What `admin.search.entities` searches. */
|
|
4
|
+
export type SearchEntityType = 'PRODUCTS' | 'ORDERS' | 'CUSTOMERS' | 'CONTENT';
|
|
5
|
+
/**
|
|
6
|
+
* Why a type was not searched. A string on the wire: a newer engine may add a reason, so read an
|
|
7
|
+
* unknown one as "not searched".
|
|
8
|
+
*
|
|
9
|
+
* - `PERMISSION`: the credential holds none of the type's read permissions in the store
|
|
10
|
+
* (`ReadCatalog` or `ReadProduct` for products, `ReadOrder` for orders, `ReadCustomer` for
|
|
11
|
+
* customers).
|
|
12
|
+
* - `CUSTOMER_DATA_NOT_GRANTED`: the credential reads customer data below `full` (an app installed
|
|
13
|
+
* at `none` or `identity`). Orders and customers are matched on customers' names, email
|
|
14
|
+
* addresses and companies, so they are not searched for it.
|
|
15
|
+
* - `UNAVAILABLE`: the search service, or for content the content system, did not answer. Try
|
|
16
|
+
* again shortly.
|
|
17
|
+
*/
|
|
18
|
+
export type SearchSkipReason = 'PERMISSION' | 'CUSTOMER_DATA_NOT_GRANTED' | 'UNAVAILABLE' | (string & {});
|
|
19
|
+
export interface SearchProductHit {
|
|
20
|
+
id: string;
|
|
21
|
+
name: string;
|
|
22
|
+
slug: string;
|
|
23
|
+
/** The featured image's preview URL; null when the product has none. */
|
|
24
|
+
previewUrl: string | null;
|
|
25
|
+
}
|
|
26
|
+
export interface SearchOrderHit {
|
|
27
|
+
id: string;
|
|
28
|
+
code: string;
|
|
29
|
+
state: string;
|
|
30
|
+
/** ISO-8601; null for an order not placed yet. */
|
|
31
|
+
orderPlacedAt: string | null;
|
|
32
|
+
/** Minor units. */
|
|
33
|
+
totalWithTax: number;
|
|
34
|
+
currencyCode: string;
|
|
35
|
+
/** The order's lifecycle (`open`, `completed`, …); null until the engine sets it. */
|
|
36
|
+
lifecycleStatus: string | null;
|
|
37
|
+
}
|
|
38
|
+
export interface SearchCustomerHit {
|
|
39
|
+
id: string;
|
|
40
|
+
firstName: string;
|
|
41
|
+
lastName: string;
|
|
42
|
+
emailAddress: string;
|
|
43
|
+
/** The company of the default billing address, else the default shipping one, else any. */
|
|
44
|
+
company: string | null;
|
|
45
|
+
}
|
|
46
|
+
/** A content item of the store's content system. */
|
|
47
|
+
export interface SearchContentHit {
|
|
48
|
+
id: string;
|
|
49
|
+
collectionId: string;
|
|
50
|
+
collectionSlug: string;
|
|
51
|
+
slug: string | null;
|
|
52
|
+
/** `draft`, `published` or `archived`. */
|
|
53
|
+
status: string;
|
|
54
|
+
title: string | null;
|
|
55
|
+
}
|
|
56
|
+
export interface SearchSkip {
|
|
57
|
+
type: SearchEntityType | (string & {});
|
|
58
|
+
reason: SearchSkipReason;
|
|
59
|
+
}
|
|
60
|
+
/** Each list holds the type's best matches, most relevant first. */
|
|
61
|
+
export interface EntitySearchResult {
|
|
62
|
+
products: SearchProductHit[];
|
|
63
|
+
orders: SearchOrderHit[];
|
|
64
|
+
customers: SearchCustomerHit[];
|
|
65
|
+
content: SearchContentHit[];
|
|
66
|
+
/** The types that were not searched, and why; their lists are empty. */
|
|
67
|
+
skipped: SearchSkip[];
|
|
68
|
+
}
|
|
69
|
+
export interface SearchEntitiesOptions extends Call {
|
|
70
|
+
/** The types to search; all four when omitted. An empty list is refused. */
|
|
71
|
+
types?: readonly SearchEntityType[];
|
|
72
|
+
/** Results per type, from 1 to {@link SEARCH_ENTITIES_MAX_TAKE}; 5 when omitted. */
|
|
73
|
+
take?: number;
|
|
74
|
+
}
|
|
75
|
+
/** The engine's bounds, as `searchAcrossEntities` documents them. */
|
|
76
|
+
export declare const SEARCH_ENTITIES_MIN_TERM_LENGTH = 2;
|
|
77
|
+
export declare const SEARCH_ENTITIES_MAX_TAKE = 10;
|
|
78
|
+
export declare const SEARCH_ACROSS_ENTITIES_QUERY = "\n query SearchAcrossEntities($input: CommandSearchInput!) {\n searchAcrossEntities(input: $input) {\n products {\n id\n name\n slug\n previewUrl\n }\n orders {\n id\n code\n state\n orderPlacedAt\n totalWithTax\n currencyCode\n lifecycleStatus\n }\n customers {\n id\n firstName\n lastName\n emailAddress\n company\n }\n content {\n id\n collectionId\n collectionSlug\n slug\n status\n title\n }\n skipped {\n type\n reason\n }\n }\n }\n";
|
|
79
|
+
/**
|
|
80
|
+
* One search across the store (spec §7 `search.entities`): products, orders, customers and
|
|
81
|
+
* content items matching a term, in one call.
|
|
82
|
+
*
|
|
83
|
+
* - **Relevance order.** Each list is that type's best matches, most relevant first, every one
|
|
84
|
+
* already loaded in the store you act on; a match the store no longer holds is left out, so a
|
|
85
|
+
* list can be shorter than `take`.
|
|
86
|
+
* - **A type you may not read is skipped and named**, never a refusal: its list is empty and
|
|
87
|
+
* `skipped` says why (see {@link SearchSkipReason}).
|
|
88
|
+
* - **Content may be missing when the content system is down**: the other types still answer,
|
|
89
|
+
* and content is skipped `UNAVAILABLE`.
|
|
90
|
+
* - **Content** is published items, plus drafts and archived items for a `ReadCmsContent` holder.
|
|
91
|
+
*
|
|
92
|
+
* Refused before anything is searched: `SEARCH_INPUT_INVALID` (`ValidationError`, 400) for a term
|
|
93
|
+
* under {@link SEARCH_ENTITIES_MIN_TERM_LENGTH} or over 500 characters once trimmed, a `take`
|
|
94
|
+
* outside 1..{@link SEARCH_ENTITIES_MAX_TAKE}, or an empty `types` list.
|
|
95
|
+
*/
|
|
96
|
+
export interface SearchModule {
|
|
97
|
+
entities(term: string, options?: SearchEntitiesOptions): Promise<EntitySearchResult>;
|
|
98
|
+
}
|
|
99
|
+
export declare function createSearchModule(transport: Transport): SearchModule;
|