@sixtyninecommerce/admin 0.11.0 → 0.12.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 +1501 -23
- 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/index.d.ts +15 -0
- package/dist/customers/index.js +4 -0
- package/dist/documents/emails.d.ts +150 -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/index.d.ts +119 -1
- package/dist/fulfillments/index.js +124 -4
- 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
|
@@ -47,11 +47,17 @@ export declare const POS_OFFLINE_NOT_READY_REASONS: readonly ["prices_exclude_ta
|
|
|
47
47
|
export type PosOfflineNotReadyReason = (typeof POS_OFFLINE_NOT_READY_REASONS)[number];
|
|
48
48
|
/**
|
|
49
49
|
* Why the engine refused or flagged an offline sale, in the order an answer lists them: the
|
|
50
|
-
* refusals first (`item_gone` to `
|
|
50
|
+
* refusals first (`item_gone` to `price_over_limit`: nothing was recorded but the row), then the
|
|
51
51
|
* reviews (`price_changed` to `sold_at_suspect`: the sale is recorded). More may come.
|
|
52
52
|
*/
|
|
53
|
-
export declare const POS_OFFLINE_SALE_REASONS: readonly ["item_gone", "drawer_closed", "currency_changed", "prices_exclude_tax", "out_of_stock", "gift_card", "total_mismatch", "rounding_mismatch", "price_changed", "not_fulfilled", "cashier_changed", "policy_off", "over_cap", "sold_at_suspect"];
|
|
53
|
+
export declare const POS_OFFLINE_SALE_REASONS: readonly ["item_gone", "drawer_closed", "currency_changed", "prices_exclude_tax", "out_of_stock", "gift_card", "total_mismatch", "rounding_mismatch", "price_over_limit", "price_changed", "not_fulfilled", "cashier_changed", "policy_off", "over_cap", "sold_at_suspect", "cashier_unverified", "item_gone_stand_in", "stock_short_stand_in", "total_corrected", "rounding_as_rung"];
|
|
54
54
|
export type PosOfflineSaleReason = (typeof POS_OFFLINE_SALE_REASONS)[number];
|
|
55
|
+
/**
|
|
56
|
+
* The reasons the engine REFUSES a sale for (nothing recorded but its row): `item_gone` to
|
|
57
|
+
* `price_over_limit`. Every other reason is a review of a recorded sale. A reason this release does
|
|
58
|
+
* not know is neither: show it as it is.
|
|
59
|
+
*/
|
|
60
|
+
export declare const POS_OFFLINE_SALE_REFUSAL_REASONS: readonly PosOfflineSaleReason[];
|
|
55
61
|
/**
|
|
56
62
|
* `recorded` (the order, the cash and the receipt number exist, as the till rang it),
|
|
57
63
|
* `needs_review` (recorded, with a reason to look at) or `refused` (nothing but this row). More
|
|
@@ -132,6 +138,19 @@ export interface PosOfflineSale {
|
|
|
132
138
|
resolvedByUserName: string | null;
|
|
133
139
|
/** What the manager wrote they did about it. Staff text. */
|
|
134
140
|
resolutionNote: string | null;
|
|
141
|
+
/**
|
|
142
|
+
* When a manager recorded it again (engine with POS polish PR 6), and their name now. Answered
|
|
143
|
+
* by `recordAgain` and `list` (an engine before PR 6 answers its list without them: absent);
|
|
144
|
+
* absent from `resolve`.
|
|
145
|
+
*/
|
|
146
|
+
recordedAgainAt?: string | null;
|
|
147
|
+
recordedAgainByUserName?: string | null;
|
|
148
|
+
/**
|
|
149
|
+
* The drawer the sale's cash went into once recorded (its order's): the one it was rung in, or
|
|
150
|
+
* the one a manager recorded it again into (engine with POS polish PR 6); null while nothing is
|
|
151
|
+
* recorded, absent from an older engine (link `registerSessionId` then).
|
|
152
|
+
*/
|
|
153
|
+
cashRegisterSessionId?: string | null;
|
|
135
154
|
}
|
|
136
155
|
/** `open` (the default): the sales a manager must still handle; `all`: every offline sale. */
|
|
137
156
|
export type PosOfflineSaleFilter = 'open' | 'all';
|
|
@@ -170,11 +189,33 @@ export interface PosOfflineSalesModule {
|
|
|
170
189
|
id: string;
|
|
171
190
|
note: string;
|
|
172
191
|
}, options?: Call): Promise<PosOfflineSale>;
|
|
192
|
+
/**
|
|
193
|
+
* Record a REFUSED offline sale again (engine with POS polish PR 6): the engine builds it the
|
|
194
|
+
* way the till's replay does, into the sale's own drawer while that is open, else the open drawer
|
|
195
|
+
* `registerSessionId` names (ignored while the own one is open); it is attributed to the caller
|
|
196
|
+
* (`recordedAgainAt`, `recordedAgainByUserName`). A sale refused again stays `refused` with its new
|
|
197
|
+
* reasons; one recorded again already answers as it is. Refused `POS_OFFLINE_SALE_NOT_FOUND`
|
|
198
|
+
* (`NotFoundError`: another store's, a till the caller may not see, an unknown id, or a sale that
|
|
199
|
+
* is not refused) and `POS_OFFLINE_SALE_DRAWER_INVALID` (`ValidationError`,
|
|
200
|
+
* `extensions.registerSessionId`: the own drawer is closed and none, or no open drawer of the
|
|
201
|
+
* store, was named) and `LOCATION_NOT_ALLOWED` (`PermissionDeniedError`: the drawer, the sale's
|
|
202
|
+
* own or the one named, stands at a location a location-restricted caller may not act at).
|
|
203
|
+
* Needs `UpdateSettings` or `ManagePosDevices`, and `CreateOrder` (it records an order and its
|
|
204
|
+
* cash). An engine before PR 6 refuses the document.
|
|
205
|
+
*/
|
|
206
|
+
recordAgain(input: {
|
|
207
|
+
id: string;
|
|
208
|
+
registerSessionId?: string;
|
|
209
|
+
}, options?: Call): Promise<PosOfflineSale>;
|
|
173
210
|
}
|
|
174
211
|
export declare const POS_OFFLINE_CASH_SALES_QUERY = "query PosOfflineCashSales { getPosConfig { offlineCashSales { enabled maxSaleAmount maxSalesPerOutage maxOfflineMinutes ready notReadyReasons } } }";
|
|
175
212
|
export declare const SET_POS_OFFLINE_CASH_SALES_MUTATION = "mutation SetPosOfflineCashSales($input: PosOfflineCashSalesInput!) { setPosOfflineCashSales(input: $input) { enabled maxSaleAmount maxSalesPerOutage maxOfflineMinutes ready notReadyReasons } }";
|
|
176
|
-
|
|
213
|
+
/** The list, naming who recorded a sale again (engine POS polish PR 6). */
|
|
214
|
+
export declare const POS_OFFLINE_SALES_QUERY = "query PosOfflineSales($input: PosOfflineSaleListInput) { posOfflineSales(input: $input) { items { id deviceId deviceLabel registerSessionId offlineSaleId provisionalReceiptNumber soldAt recordedAt status reasons orderId orderCode receiptNumber currencyCode totalWithTax cashTendered changeGiven cashRounding lines { variantId quantity unitPriceWithTax serverUnitPriceWithTax } staffName claimedStaffName resolvedAt resolvedByUserName resolutionNote recordedAgainAt recordedAgainByUserName cashRegisterSessionId } totalItems } }";
|
|
215
|
+
/** The list as an engine before PR 6 reads it: sent once that engine refused the fields. */
|
|
216
|
+
export declare const POS_OFFLINE_SALES_QUERY_BEFORE_RECORD_AGAIN = "query PosOfflineSales($input: PosOfflineSaleListInput) { posOfflineSales(input: $input) { items { id deviceId deviceLabel registerSessionId offlineSaleId provisionalReceiptNumber soldAt recordedAt status reasons orderId orderCode receiptNumber currencyCode totalWithTax cashTendered changeGiven cashRounding lines { variantId quantity unitPriceWithTax serverUnitPriceWithTax } staffName claimedStaffName resolvedAt resolvedByUserName resolutionNote } totalItems } }";
|
|
177
217
|
export declare const RESOLVE_POS_OFFLINE_SALE_MUTATION = "mutation ResolvePosOfflineSale($input: ResolvePosOfflineSaleInput!) { resolvePosOfflineSale(input: $input) { id deviceId deviceLabel registerSessionId offlineSaleId provisionalReceiptNumber soldAt recordedAt status reasons orderId orderCode receiptNumber currencyCode totalWithTax cashTendered changeGiven cashRounding lines { variantId quantity unitPriceWithTax serverUnitPriceWithTax } staffName claimedStaffName resolvedAt resolvedByUserName resolutionNote } }";
|
|
218
|
+
export declare const RECORD_POS_OFFLINE_SALE_AGAIN_MUTATION = "mutation RecordPosOfflineSaleAgain($input: RecordPosOfflineSaleAgainInput!) { recordPosOfflineSaleAgain(input: $input) { id deviceId deviceLabel registerSessionId offlineSaleId provisionalReceiptNumber soldAt recordedAt status reasons orderId orderCode receiptNumber currencyCode totalWithTax cashTendered changeGiven cashRounding lines { variantId quantity unitPriceWithTax serverUnitPriceWithTax } staffName claimedStaffName resolvedAt resolvedByUserName resolutionNote recordedAgainAt recordedAgainByUserName cashRegisterSessionId } }";
|
|
178
219
|
export declare const POS_DEVICE_OFFLINE_SALES_TO_REVIEW_QUERY = "query PosDeviceOfflineSalesToReview($stockLocationId: ID) { posDeviceFleet(stockLocationId: $stockLocationId) { device { id } offlineSalesToReview } }";
|
|
179
220
|
/** `admin.pos.settings.offlineCashSales()`: null when POS was never set up. */
|
|
180
221
|
export declare function readOfflineCashSales(transport: Transport, options?: Call): Promise<PosOfflineCashSales | null>;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { pageFromVendureList, readOffsetCursor, } from '@sixtyninecommerce/core';
|
|
2
|
+
import { isUnknownFieldRejection } from '../payments/store-credit-fields.js';
|
|
2
3
|
/**
|
|
3
4
|
* Offline cash sales (engine sprint 45, shipped dark): a till that loses its connection may keep
|
|
4
5
|
* selling for cash, within the store's limits, and sends each sale when it is back. The engine
|
|
@@ -38,7 +39,7 @@ export const POS_OFFLINE_NOT_READY_REASONS = [
|
|
|
38
39
|
];
|
|
39
40
|
/**
|
|
40
41
|
* Why the engine refused or flagged an offline sale, in the order an answer lists them: the
|
|
41
|
-
* refusals first (`item_gone` to `
|
|
42
|
+
* refusals first (`item_gone` to `price_over_limit`: nothing was recorded but the row), then the
|
|
42
43
|
* reviews (`price_changed` to `sold_at_suspect`: the sale is recorded). More may come.
|
|
43
44
|
*/
|
|
44
45
|
export const POS_OFFLINE_SALE_REASONS = [
|
|
@@ -50,12 +51,34 @@ export const POS_OFFLINE_SALE_REASONS = [
|
|
|
50
51
|
'gift_card',
|
|
51
52
|
'total_mismatch',
|
|
52
53
|
'rounding_mismatch',
|
|
54
|
+
'price_over_limit',
|
|
53
55
|
'price_changed',
|
|
54
56
|
'not_fulfilled',
|
|
55
57
|
'cashier_changed',
|
|
56
58
|
'policy_off',
|
|
57
59
|
'over_cap',
|
|
58
60
|
'sold_at_suspect',
|
|
61
|
+
'cashier_unverified',
|
|
62
|
+
'item_gone_stand_in',
|
|
63
|
+
'stock_short_stand_in',
|
|
64
|
+
'total_corrected',
|
|
65
|
+
'rounding_as_rung',
|
|
66
|
+
];
|
|
67
|
+
/**
|
|
68
|
+
* The reasons the engine REFUSES a sale for (nothing recorded but its row): `item_gone` to
|
|
69
|
+
* `price_over_limit`. Every other reason is a review of a recorded sale. A reason this release does
|
|
70
|
+
* not know is neither: show it as it is.
|
|
71
|
+
*/
|
|
72
|
+
export const POS_OFFLINE_SALE_REFUSAL_REASONS = [
|
|
73
|
+
'item_gone',
|
|
74
|
+
'drawer_closed',
|
|
75
|
+
'currency_changed',
|
|
76
|
+
'prices_exclude_tax',
|
|
77
|
+
'out_of_stock',
|
|
78
|
+
'gift_card',
|
|
79
|
+
'total_mismatch',
|
|
80
|
+
'rounding_mismatch',
|
|
81
|
+
'price_over_limit',
|
|
59
82
|
];
|
|
60
83
|
const DEFAULT_PAGE = 50;
|
|
61
84
|
const MAX_PAGE = 100;
|
|
@@ -63,8 +86,13 @@ const POLICY_FIELDS = 'enabled maxSaleAmount maxSalesPerOutage maxOfflineMinutes
|
|
|
63
86
|
const SALE_FIELDS = 'id deviceId deviceLabel registerSessionId offlineSaleId provisionalReceiptNumber soldAt recordedAt status reasons orderId orderCode receiptNumber currencyCode totalWithTax cashTendered changeGiven cashRounding lines { variantId quantity unitPriceWithTax serverUnitPriceWithTax } staffName claimedStaffName resolvedAt resolvedByUserName resolutionNote';
|
|
64
87
|
export const POS_OFFLINE_CASH_SALES_QUERY = `query PosOfflineCashSales { getPosConfig { offlineCashSales { ${POLICY_FIELDS} } } }`;
|
|
65
88
|
export const SET_POS_OFFLINE_CASH_SALES_MUTATION = `mutation SetPosOfflineCashSales($input: PosOfflineCashSalesInput!) { setPosOfflineCashSales(input: $input) { ${POLICY_FIELDS} } }`;
|
|
66
|
-
|
|
89
|
+
/** The list, naming who recorded a sale again (engine POS polish PR 6). */
|
|
90
|
+
export const POS_OFFLINE_SALES_QUERY = `query PosOfflineSales($input: PosOfflineSaleListInput) { posOfflineSales(input: $input) { items { ${SALE_FIELDS} recordedAgainAt recordedAgainByUserName cashRegisterSessionId } totalItems } }`;
|
|
91
|
+
/** The list as an engine before PR 6 reads it: sent once that engine refused the fields. */
|
|
92
|
+
export const POS_OFFLINE_SALES_QUERY_BEFORE_RECORD_AGAIN = `query PosOfflineSales($input: PosOfflineSaleListInput) { posOfflineSales(input: $input) { items { ${SALE_FIELDS} } totalItems } }`;
|
|
67
93
|
export const RESOLVE_POS_OFFLINE_SALE_MUTATION = `mutation ResolvePosOfflineSale($input: ResolvePosOfflineSaleInput!) { resolvePosOfflineSale(input: $input) { ${SALE_FIELDS} } }`;
|
|
94
|
+
// Engine POS polish PR 6: only this document selects who recorded it again.
|
|
95
|
+
export const RECORD_POS_OFFLINE_SALE_AGAIN_MUTATION = `mutation RecordPosOfflineSaleAgain($input: RecordPosOfflineSaleAgainInput!) { recordPosOfflineSaleAgain(input: $input) { ${SALE_FIELDS} recordedAgainAt recordedAgainByUserName cashRegisterSessionId } }`;
|
|
68
96
|
// The fleet's count beside the device's id alone, its own document: the fleet read
|
|
69
97
|
// (`POS_DEVICE_FLEET_QUERY`) never names it, so an engine before 45 keeps answering the fleet.
|
|
70
98
|
export const POS_DEVICE_OFFLINE_SALES_TO_REVIEW_QUERY = 'query PosDeviceOfflineSalesToReview($stockLocationId: ID) { posDeviceFleet(stockLocationId: $stockLocationId) { device { id } offlineSalesToReview } }';
|
|
@@ -92,13 +120,30 @@ export async function readOfflineSalesToReview(transport, options) {
|
|
|
92
120
|
}
|
|
93
121
|
export function createPosOfflineSalesModule(transport) {
|
|
94
122
|
const gql = gqlOf(transport);
|
|
123
|
+
// An engine before PR 6 refused the list's record-again fields once: every later page asks
|
|
124
|
+
// without them (one refused request per module, not per page).
|
|
125
|
+
let listBeforeRecordAgain = false;
|
|
126
|
+
async function listDocument(variables, options) {
|
|
127
|
+
if (!listBeforeRecordAgain) {
|
|
128
|
+
try {
|
|
129
|
+
return await gql(POS_OFFLINE_SALES_QUERY, variables, options);
|
|
130
|
+
}
|
|
131
|
+
catch (error) {
|
|
132
|
+
// Refused before it ran: the same read without the fields. Any other failure is the read's.
|
|
133
|
+
if (!isUnknownFieldRejection(error))
|
|
134
|
+
throw error;
|
|
135
|
+
listBeforeRecordAgain = true;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return gql(POS_OFFLINE_SALES_QUERY_BEFORE_RECORD_AGAIN, variables, options);
|
|
139
|
+
}
|
|
95
140
|
async function salePage(skip, take, options) {
|
|
96
141
|
const input = { skip, take };
|
|
97
142
|
if (options?.filter !== undefined)
|
|
98
143
|
input.filter = options.filter;
|
|
99
144
|
if (options?.deviceId !== undefined)
|
|
100
145
|
input.deviceId = options.deviceId;
|
|
101
|
-
const data = await
|
|
146
|
+
const data = await listDocument({ input }, options);
|
|
102
147
|
return pageFromVendureList(data.posOfflineSales, skip, take, (cursor) => salePage(readOffsetCursor(cursor), take, options));
|
|
103
148
|
}
|
|
104
149
|
return {
|
|
@@ -112,5 +157,14 @@ export function createPosOfflineSalesModule(transport) {
|
|
|
112
157
|
const data = await gql(RESOLVE_POS_OFFLINE_SALE_MUTATION, { input: { id: input.id, note: input.note } }, options);
|
|
113
158
|
return data.resolvePosOfflineSale;
|
|
114
159
|
},
|
|
160
|
+
async recordAgain(input, options) {
|
|
161
|
+
const data = await gql(RECORD_POS_OFFLINE_SALE_AGAIN_MUTATION, {
|
|
162
|
+
input: {
|
|
163
|
+
id: input.id,
|
|
164
|
+
...(input.registerSessionId ? { registerSessionId: input.registerSessionId } : {}),
|
|
165
|
+
},
|
|
166
|
+
}, options);
|
|
167
|
+
return data.recordPosOfflineSaleAgain;
|
|
168
|
+
},
|
|
115
169
|
};
|
|
116
170
|
}
|
package/dist/pos/returns.d.ts
CHANGED
|
@@ -26,6 +26,13 @@ import type { Call } from '../types.js';
|
|
|
26
26
|
* refuses `POS_RETURN_STORE_CREDIT_UNAVAILABLE` (`extensions.reason`) when it cannot, nothing moved.
|
|
27
27
|
* `quote` and `create` need an engine with C6b (an older one refuses their documents); `void`
|
|
28
28
|
* keeps its sprint-20 selection and reads on any engine.
|
|
29
|
+
*
|
|
30
|
+
* The customer's consent to store credit (owner ruling Q4, EU consumer law CRD art. 13(1)): a
|
|
31
|
+
* store-credit payout asks whether the customer expressly agreed to store credit instead of a
|
|
32
|
+
* refund to their payment when `quote(...).storedValuePayout.asksConsent` says so, and `create`
|
|
33
|
+
* sends the answer as `customerAgreed`. The engine records it on the store credit; never a
|
|
34
|
+
* condition. `quote` reads `asksConsent` on an engine before it too (the document without it,
|
|
35
|
+
* `false`: such an engine takes no answer).
|
|
29
36
|
*/
|
|
30
37
|
export type PosReturnKind = 'RETURN' | 'VOID';
|
|
31
38
|
/** Where a return's value goes (C6b): REFUND (the default) or the customer's store credit. */
|
|
@@ -47,6 +54,13 @@ export interface PosReturnStoredValuePayout {
|
|
|
47
54
|
available: boolean;
|
|
48
55
|
/** no_customer, no_method or not_entitled when it cannot. More may come. */
|
|
49
56
|
reason: string | null;
|
|
57
|
+
/**
|
|
58
|
+
* Ask the customer whether they expressly agreed to store credit before this payout, and send
|
|
59
|
+
* the answer as `PosReturnCreateInput.customerAgreed` (owner ruling Q4): true exactly when the
|
|
60
|
+
* payout is available. `false` from an engine before the field, which takes no answer, and on a
|
|
61
|
+
* webshop order's quote, which never pays out to store credit.
|
|
62
|
+
*/
|
|
63
|
+
asksConsent: boolean;
|
|
50
64
|
}
|
|
51
65
|
/** The store credit a return was paid out to (C6b). */
|
|
52
66
|
export interface PosReturnStoredValueCredit {
|
|
@@ -108,6 +122,17 @@ export interface PosReturnCreateInput {
|
|
|
108
122
|
cashFirst?: boolean;
|
|
109
123
|
/** REFUND when omitted (C6b). STORED_VALUE: the whole return to the sale's customer's store credit. */
|
|
110
124
|
payout?: PosReturnPayout;
|
|
125
|
+
/**
|
|
126
|
+
* A STORED_VALUE payout whose quote said `asksConsent`: whether the customer expressly agreed to
|
|
127
|
+
* store credit instead of a refund to their payment (EU consumer law, CRD art. 13(1)), as staff
|
|
128
|
+
* asked them. The engine records it on the store credit with who stated it
|
|
129
|
+
* (`StoredValueEntry.customerAgreed`); never a condition, nothing is refused for it. Part of the
|
|
130
|
+
* request: a repeat under the same key sends the same answer, another answer is
|
|
131
|
+
* `IDEMPOTENCY_CONFLICT`. Sent only when given (true or false): left out or null, the request is
|
|
132
|
+
* what it always was. An engine before the field refuses a call that gives it, and that refusal
|
|
133
|
+
* is surfaced, never retried without the answer.
|
|
134
|
+
*/
|
|
135
|
+
customerAgreed?: boolean | null;
|
|
111
136
|
}
|
|
112
137
|
export interface PosVoidInput {
|
|
113
138
|
orderId: string;
|
|
@@ -208,7 +233,9 @@ export interface PosReturnsModule {
|
|
|
208
233
|
idempotencyKey: string;
|
|
209
234
|
}): Promise<PosReturnResult>;
|
|
210
235
|
}
|
|
211
|
-
export declare const POS_RETURN_QUOTE_QUERY = "query PosReturnQuote($input: PosReturnQuoteInput!) { posReturnQuote(input: $input) { orderId kind currencyCode receiptNumber lines { orderLineId name sku customItem restockable soldQuantity returnedQuantity returnableQuantity quantity refundAmount } total legs { paymentId kind method amount } cashPaidOut returnWindowEndsAt outsideWindow refundedOutsideReturns restockLocationName blockedBy blockedReason storedValuePayout { available reason } } }";
|
|
236
|
+
export declare const POS_RETURN_QUOTE_QUERY = "query PosReturnQuote($input: PosReturnQuoteInput!) { posReturnQuote(input: $input) { orderId kind currencyCode receiptNumber lines { orderLineId name sku customItem restockable soldQuantity returnedQuantity returnableQuantity quantity refundAmount } total legs { paymentId kind method amount } cashPaidOut returnWindowEndsAt outsideWindow refundedOutsideReturns restockLocationName blockedBy blockedReason storedValuePayout { available reason asksConsent } } }";
|
|
237
|
+
/** The quote for an engine before the till's consent (owner ruling Q4): no `asksConsent`. */
|
|
238
|
+
export declare const POS_RETURN_QUOTE_QUERY_BEFORE_CONSENT = "query PosReturnQuote($input: PosReturnQuoteInput!) { posReturnQuote(input: $input) { orderId kind currencyCode receiptNumber lines { orderLineId name sku customItem restockable soldQuantity returnedQuantity returnableQuantity quantity refundAmount } total legs { paymentId kind method amount } cashPaidOut returnWindowEndsAt outsideWindow refundedOutsideReturns restockLocationName blockedBy blockedReason storedValuePayout { available reason } } }";
|
|
212
239
|
export declare const POS_RETURN_MUTATION = "mutation PosReturn($input: PosReturnInput!) { posReturn(input: $input) { orderId returnId returnCode kind total refunds { paymentId kind method amount refundId outcome } cashPaidOut complete storedValueCredit { accountId entryId amount } } }";
|
|
213
240
|
export declare const POS_VOID_SALE_MUTATION = "mutation PosVoidSale($input: PosVoidSaleInput!) { posVoidSale(input: $input) { orderId returnId returnCode kind total refunds { paymentId kind method amount refundId outcome } cashPaidOut complete } }";
|
|
214
241
|
export declare const POS_ONLINE_ORDER_QUERY = "query PosOnlineOrder($input: PosOnlineOrderRefInput!) { posOnlineOrder(input: $input) { orderId orderCode placedAt state currencyCode totalWithTax customerName refundMethodName returnableUnits blockedBy blockedReason } }";
|
package/dist/pos/returns.js
CHANGED
|
@@ -1,17 +1,35 @@
|
|
|
1
|
-
|
|
1
|
+
import { statedCustomerAgreed } from '../payments/customer-consent.js';
|
|
2
|
+
import { isUnknownFieldRejection } from '../payments/store-credit-fields.js';
|
|
3
|
+
/** A quote's fields up to its store-credit payout, which every engine with C6b reads. */
|
|
4
|
+
const QUOTE_BASE_FIELDS = 'orderId kind currencyCode receiptNumber lines { orderLineId name sku customItem restockable soldQuantity returnedQuantity returnableQuantity quantity refundAmount } total legs { paymentId kind method amount } cashPaidOut returnWindowEndsAt outsideWindow refundedOutsideReturns restockLocationName blockedBy blockedReason';
|
|
5
|
+
// Spliced as template constants, never built by a call: the repo's GraphQL cost scan
|
|
6
|
+
// (`graphql-cost-fits-repo.spec.ts`) resolves `${CONSTANT}` statically and nothing computed.
|
|
7
|
+
/** A quote's fields before the till's consent (owner ruling Q4): what every engine with C6b reads. */
|
|
8
|
+
const QUOTE_FIELDS_BEFORE_CONSENT = `${QUOTE_BASE_FIELDS} storedValuePayout { available reason }`;
|
|
9
|
+
const QUOTE_FIELDS = `${QUOTE_BASE_FIELDS} storedValuePayout { available reason asksConsent }`;
|
|
2
10
|
/** The sprint-20 result: what a void answers (it never pays out to store credit). */
|
|
3
11
|
const VOID_RESULT_FIELDS = 'orderId returnId returnCode kind total refunds { paymentId kind method amount refundId outcome } cashPaidOut complete';
|
|
4
12
|
/** A return's result: the void's, and the store credit a STORED_VALUE payout made (C6b). */
|
|
5
13
|
const RESULT_FIELDS = `${VOID_RESULT_FIELDS} storedValueCredit { accountId entryId amount }`;
|
|
6
14
|
export const POS_RETURN_QUOTE_QUERY = `query PosReturnQuote($input: PosReturnQuoteInput!) { posReturnQuote(input: $input) { ${QUOTE_FIELDS} } }`;
|
|
15
|
+
/** The quote for an engine before the till's consent (owner ruling Q4): no `asksConsent`. */
|
|
16
|
+
export const POS_RETURN_QUOTE_QUERY_BEFORE_CONSENT = `query PosReturnQuote($input: PosReturnQuoteInput!) { posReturnQuote(input: $input) { ${QUOTE_FIELDS_BEFORE_CONSENT} } }`;
|
|
7
17
|
export const POS_RETURN_MUTATION = `mutation PosReturn($input: PosReturnInput!) { posReturn(input: $input) { ${RESULT_FIELDS} } }`;
|
|
8
18
|
export const POS_VOID_SALE_MUTATION = `mutation PosVoidSale($input: PosVoidSaleInput!) { posVoidSale(input: $input) { ${VOID_RESULT_FIELDS} } }`;
|
|
9
19
|
// Engine C3/34: their own documents, so a call to an older engine fails alone.
|
|
10
20
|
const ONLINE_ORDER_FIELDS = 'orderId orderCode placedAt state currencyCode totalWithTax customerName refundMethodName returnableUnits blockedBy blockedReason';
|
|
11
21
|
export const POS_ONLINE_ORDER_QUERY = `query PosOnlineOrder($input: PosOnlineOrderRefInput!) { posOnlineOrder(input: $input) { ${ONLINE_ORDER_FIELDS} } }`;
|
|
12
|
-
|
|
22
|
+
// Never store credit (D22), so it never asks consent: the selection before Q4, `asksConsent` false.
|
|
23
|
+
export const POS_ONLINE_RETURN_QUOTE_QUERY = `query PosOnlineReturnQuote($input: PosOnlineReturnQuoteInput!) { posOnlineReturnQuote(input: $input) { ${QUOTE_FIELDS_BEFORE_CONSENT} shippingRefund source } }`;
|
|
13
24
|
// A webshop order's return never pays out to store credit (C3 D22): the void's result fields.
|
|
14
25
|
export const POS_ONLINE_RETURN_MUTATION = `mutation PosOnlineReturn($input: PosOnlineReturnInput!) { posOnlineReturn(input: $input) { ${VOID_RESULT_FIELDS} } }`;
|
|
26
|
+
/** Such a quote as this SDK answers it: `asksConsent` false (that engine takes no answer). */
|
|
27
|
+
function withoutConsent(quote) {
|
|
28
|
+
return {
|
|
29
|
+
...quote,
|
|
30
|
+
storedValuePayout: { ...quote.storedValuePayout, asksConsent: false },
|
|
31
|
+
};
|
|
32
|
+
}
|
|
15
33
|
function keyOf(options, call) {
|
|
16
34
|
const key = options?.idempotencyKey;
|
|
17
35
|
if (typeof key !== 'string' || key.trim() === '') {
|
|
@@ -23,12 +41,24 @@ export function createPosReturnsModule(transport) {
|
|
|
23
41
|
const gql = async (query, variables, options) => transport.graphql({ api: 'admin', query, variables }, options);
|
|
24
42
|
return {
|
|
25
43
|
async quote(input, options) {
|
|
26
|
-
|
|
27
|
-
|
|
44
|
+
try {
|
|
45
|
+
const data = await gql(POS_RETURN_QUOTE_QUERY, { input }, options);
|
|
46
|
+
return data.posReturnQuote;
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
// An engine before the till's consent (Q4) refuses the whole document; the quote is still
|
|
50
|
+
// readable without it, and that engine takes no answer. Any other failure is the read's.
|
|
51
|
+
if (!isUnknownFieldRejection(error))
|
|
52
|
+
throw error;
|
|
53
|
+
const data = await gql(POS_RETURN_QUOTE_QUERY_BEFORE_CONSENT, { input }, options);
|
|
54
|
+
return withoutConsent(data.posReturnQuote);
|
|
55
|
+
}
|
|
28
56
|
},
|
|
29
57
|
async create(input, options) {
|
|
30
58
|
const idempotencyKey = keyOf(options, 'create');
|
|
31
|
-
|
|
59
|
+
// The answer only when stated: left out or null, the request is what it always was.
|
|
60
|
+
const { customerAgreed, ...rest } = input;
|
|
61
|
+
const data = await gql(POS_RETURN_MUTATION, { input: { ...rest, ...statedCustomerAgreed(customerAgreed), idempotencyKey } }, options);
|
|
32
62
|
return data.posReturn;
|
|
33
63
|
},
|
|
34
64
|
async void(input, options) {
|
|
@@ -42,7 +72,7 @@ export function createPosReturnsModule(transport) {
|
|
|
42
72
|
},
|
|
43
73
|
async onlineQuote(input, options) {
|
|
44
74
|
const data = await gql(POS_ONLINE_RETURN_QUOTE_QUERY, { input }, options);
|
|
45
|
-
return data.posOnlineReturnQuote;
|
|
75
|
+
return withoutConsent(data.posOnlineReturnQuote);
|
|
46
76
|
},
|
|
47
77
|
async onlineReturn(input, options) {
|
|
48
78
|
const idempotencyKey = keyOf(options, 'onlineReturn');
|
|
@@ -500,7 +500,9 @@ export interface PricingPriceListsModule {
|
|
|
500
500
|
/**
|
|
501
501
|
* The lists that apply to a customer of the store NOW, IN THE ORDER the price-list pricing
|
|
502
502
|
* tries them for each line: contracts first, then assigned to the customer, through one of
|
|
503
|
-
* their groups, to every customer; then higher priority first.
|
|
503
|
+
* their groups, to every customer; then higher priority first. Lists of one standing (same
|
|
504
|
+
* contract flag, assignment and priority) are tried together and the lower price wins per line,
|
|
505
|
+
* an equal price going to the lower list id; they are listed by id. Only enabled lists inside their
|
|
504
506
|
* date window, every list of the store considered; never an order's one-time discount (read
|
|
505
507
|
* that as `Order.priceList`). Each comes with its first entries and its order rules. The
|
|
506
508
|
* engine ranks them with the function its pricing uses, so a client never re-derives it.
|
|
@@ -30,13 +30,22 @@ export interface PurchasingAlertSummary {
|
|
|
30
30
|
reorderSuggestions: number;
|
|
31
31
|
/** Of those, the ones the last compute found new (not suggested before, or grown). */
|
|
32
32
|
newReorderSuggestions: number;
|
|
33
|
+
/**
|
|
34
|
+
* The ACTIVE store's backordered order lines that stock on hand can ship now (its own orders,
|
|
35
|
+
* never another store's of the legal entity): `backorders.ready()`'s total. Purchase-orders
|
|
36
|
+
* step 3.6; `null` from an engine before it (the SDK asks again without the two counts).
|
|
37
|
+
*/
|
|
38
|
+
backordersReadyLines: number | null;
|
|
39
|
+
/** The orders holding those lines, each counted once; `null` as `backordersReadyLines`. */
|
|
40
|
+
backordersReadyOrders: number | null;
|
|
33
41
|
}
|
|
34
42
|
/** Who one purchasing alert email goes to. */
|
|
35
43
|
export interface PurchasingAlertRecipients {
|
|
36
44
|
alert: PurchasingAlert;
|
|
37
45
|
/**
|
|
38
46
|
* The permission whose holders get the alert when no addresses are set:
|
|
39
|
-
* `ApprovePurchaseOrder`, `UpdatePurchaseOrder`
|
|
47
|
+
* `ApprovePurchaseOrder`, `ReceivePurchaseOrder` (`UpdatePurchaseOrder` on an engine before
|
|
48
|
+
* purchase-orders step 3.6) or `CreatePurchaseOrder`.
|
|
40
49
|
*/
|
|
41
50
|
permission: string;
|
|
42
51
|
/** The addresses set for this alert; empty when it goes to the permission's holders. */
|
|
@@ -52,9 +61,14 @@ export interface PurchasingAlertRecipientsInput {
|
|
|
52
61
|
alert: PurchasingAlert;
|
|
53
62
|
emails: string[];
|
|
54
63
|
}
|
|
55
|
-
export declare const PURCHASING_ALERT_SUMMARY_FIELDS = "\n localDay\n awaitingApproval\n overdueLines\n overdueOrders\n attentionOrders\n reorderSuggestions\n newReorderSuggestions\n";
|
|
64
|
+
export declare const PURCHASING_ALERT_SUMMARY_FIELDS = "\n \n localDay\n awaitingApproval\n overdueLines\n overdueOrders\n attentionOrders\n reorderSuggestions\n newReorderSuggestions\n\n backordersReadyLines\n backordersReadyOrders\n";
|
|
56
65
|
export declare const PURCHASING_ALERT_RECIPIENTS_FIELDS = "\n alert\n permission\n emails\n recipients\n";
|
|
57
|
-
export declare const PURCHASING_ALERTS_QUERY = "\n query PurchasingAlerts {\n purchasingAlerts { \n localDay\n awaitingApproval\n overdueLines\n overdueOrders\n attentionOrders\n reorderSuggestions\n newReorderSuggestions\n }\n }\n";
|
|
66
|
+
export declare const PURCHASING_ALERTS_QUERY = "\n query PurchasingAlerts {\n purchasingAlerts { \n \n localDay\n awaitingApproval\n overdueLines\n overdueOrders\n attentionOrders\n reorderSuggestions\n newReorderSuggestions\n\n backordersReadyLines\n backordersReadyOrders\n }\n }\n";
|
|
67
|
+
/**
|
|
68
|
+
* The summary without the backorder counts, for an engine before purchase-orders step 3.6: the SDK
|
|
69
|
+
* retries once with it when the engine refuses the fields, and answers the two counts null.
|
|
70
|
+
*/
|
|
71
|
+
export declare const LEGACY_PURCHASING_ALERTS_QUERY = "\n query PurchasingAlertsLegacy {\n purchasingAlerts { \n localDay\n awaitingApproval\n overdueLines\n overdueOrders\n attentionOrders\n reorderSuggestions\n newReorderSuggestions\n }\n }\n";
|
|
58
72
|
export declare const PURCHASING_ALERT_RECIPIENTS_QUERY = "\n query PurchasingAlertRecipients {\n purchasingAlertRecipients { \n alert\n permission\n emails\n recipients\n }\n }\n";
|
|
59
73
|
export declare const UPDATE_PURCHASING_ALERT_RECIPIENTS_MUTATION = "\n mutation UpdatePurchasingAlertRecipients(\n $input: [PurchasingAlertRecipientsInput!]!\n $idempotencyKey: String\n ) {\n updatePurchasingAlertRecipients(input: $input, idempotencyKey: $idempotencyKey) {\n \n alert\n permission\n emails\n recipients\n\n }\n }\n";
|
|
60
74
|
export interface PurchasingAlertRecipientsModule {
|
|
@@ -81,7 +95,8 @@ export interface PurchasingAlertRecipientsModule {
|
|
|
81
95
|
export interface PurchasingAlertsModule {
|
|
82
96
|
/**
|
|
83
97
|
* The counts behind the nav badges and the Overview cards. Needs `ReadPurchaseOrder`. `null`
|
|
84
|
-
* while purchasing is not switched on for the store's legal entity.
|
|
98
|
+
* while purchasing is not switched on for the store's legal entity. Against an engine before
|
|
99
|
+
* purchase-orders step 3.6 the backorder counts answer `null` and the rest as ever.
|
|
85
100
|
*/
|
|
86
101
|
summary(call?: Call): Promise<PurchasingAlertSummary | null>;
|
|
87
102
|
recipients: PurchasingAlertRecipientsModule;
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
import { isUnknownFieldRejection } from '../payments/store-credit-fields.js';
|
|
2
|
+
/** The summary's counts an engine before purchase-orders step 3.6 answers. */
|
|
3
|
+
const STEP_4_3_SUMMARY_FIELDS = /* GraphQL */ `
|
|
2
4
|
localDay
|
|
3
5
|
awaitingApproval
|
|
4
6
|
overdueLines
|
|
@@ -7,6 +9,11 @@ export const PURCHASING_ALERT_SUMMARY_FIELDS = /* GraphQL */ `
|
|
|
7
9
|
reorderSuggestions
|
|
8
10
|
newReorderSuggestions
|
|
9
11
|
`;
|
|
12
|
+
export const PURCHASING_ALERT_SUMMARY_FIELDS = /* GraphQL */ `
|
|
13
|
+
${STEP_4_3_SUMMARY_FIELDS}
|
|
14
|
+
backordersReadyLines
|
|
15
|
+
backordersReadyOrders
|
|
16
|
+
`;
|
|
10
17
|
export const PURCHASING_ALERT_RECIPIENTS_FIELDS = /* GraphQL */ `
|
|
11
18
|
alert
|
|
12
19
|
permission
|
|
@@ -18,6 +25,15 @@ export const PURCHASING_ALERTS_QUERY = /* GraphQL */ `
|
|
|
18
25
|
purchasingAlerts { ${PURCHASING_ALERT_SUMMARY_FIELDS} }
|
|
19
26
|
}
|
|
20
27
|
`;
|
|
28
|
+
/**
|
|
29
|
+
* The summary without the backorder counts, for an engine before purchase-orders step 3.6: the SDK
|
|
30
|
+
* retries once with it when the engine refuses the fields, and answers the two counts null.
|
|
31
|
+
*/
|
|
32
|
+
export const LEGACY_PURCHASING_ALERTS_QUERY = /* GraphQL */ `
|
|
33
|
+
query PurchasingAlertsLegacy {
|
|
34
|
+
purchasingAlerts { ${STEP_4_3_SUMMARY_FIELDS} }
|
|
35
|
+
}
|
|
36
|
+
`;
|
|
21
37
|
export const PURCHASING_ALERT_RECIPIENTS_QUERY = /* GraphQL */ `
|
|
22
38
|
query PurchasingAlertRecipients {
|
|
23
39
|
purchasingAlertRecipients { ${PURCHASING_ALERT_RECIPIENTS_FIELDS} }
|
|
@@ -36,8 +52,18 @@ export const UPDATE_PURCHASING_ALERT_RECIPIENTS_MUTATION = /* GraphQL */ `
|
|
|
36
52
|
export function createPurchasingAlertsModule(transport) {
|
|
37
53
|
return {
|
|
38
54
|
async summary(call) {
|
|
39
|
-
|
|
40
|
-
|
|
55
|
+
try {
|
|
56
|
+
const data = await transport.graphql({ api: 'admin', query: PURCHASING_ALERTS_QUERY }, call);
|
|
57
|
+
return data.purchasingAlerts ?? null;
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
if (!isUnknownFieldRejection(error))
|
|
61
|
+
throw error;
|
|
62
|
+
const data = await transport.graphql({ api: 'admin', query: LEGACY_PURCHASING_ALERTS_QUERY }, call);
|
|
63
|
+
return data.purchasingAlerts === null
|
|
64
|
+
? null
|
|
65
|
+
: { ...data.purchasingAlerts, backordersReadyLines: null, backordersReadyOrders: null };
|
|
66
|
+
}
|
|
41
67
|
},
|
|
42
68
|
recipients: {
|
|
43
69
|
async list(call) {
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { type Page, type Transport } from '@sixtyninecommerce/core';
|
|
2
|
+
import type { ListOptions } from '../types.js';
|
|
3
|
+
/**
|
|
4
|
+
* An open backordered order line of the active store that stock on hand can ship now
|
|
5
|
+
* (purchase-orders spec §8.11): the store's free on-hand of the variant shared out over its open
|
|
6
|
+
* backorders, oldest order first.
|
|
7
|
+
*/
|
|
8
|
+
export interface BackorderReadyLine {
|
|
9
|
+
orderId: string;
|
|
10
|
+
orderCode: string;
|
|
11
|
+
/** The order's state; a parked or approval-waiting order keeps its turn too. */
|
|
12
|
+
orderState: string;
|
|
13
|
+
/** ISO-8601 UTC; null for an order never placed. */
|
|
14
|
+
orderPlacedAt: string | null;
|
|
15
|
+
orderLineId: string;
|
|
16
|
+
productVariantId: string;
|
|
17
|
+
sku: string;
|
|
18
|
+
/** The variant's name in the request's language. */
|
|
19
|
+
name: string;
|
|
20
|
+
/** The line's quantity. */
|
|
21
|
+
quantity: number;
|
|
22
|
+
/** Its backordered units not yet in a fulfilment. */
|
|
23
|
+
backorderedQuantity: number;
|
|
24
|
+
/** Of those, what stock on hand covers now (at least 1). */
|
|
25
|
+
readyQuantity: number;
|
|
26
|
+
/** The variant's on-hand, summed over the store's stock locations. */
|
|
27
|
+
stockOnHand: number;
|
|
28
|
+
/**
|
|
29
|
+
* True when the line was ordered from a supplier for this customer (a back-to-back link) and
|
|
30
|
+
* the supplier side cancelled it: it is an ordinary backorder again ("Supplier cancelled").
|
|
31
|
+
*/
|
|
32
|
+
supplierCancelled: boolean;
|
|
33
|
+
}
|
|
34
|
+
export interface BackordersReadyOptions extends ListOptions {
|
|
35
|
+
/** Only the variants this goods receipt (of the store's legal entity) brought in. */
|
|
36
|
+
goodsReceiptId?: string;
|
|
37
|
+
}
|
|
38
|
+
export declare const BACKORDER_READY_LINE_FIELDS = "\n orderId\n orderCode\n orderState\n orderPlacedAt\n orderLineId\n productVariantId\n sku\n name\n quantity\n backorderedQuantity\n readyQuantity\n stockOnHand\n supplierCancelled\n";
|
|
39
|
+
export declare const BACKORDERS_READY_QUERY = "\n query BackordersReadyToShip($options: BackordersReadyOptions) {\n backordersReadyToShip(options: $options) {\n items { \n orderId\n orderCode\n orderState\n orderPlacedAt\n orderLineId\n productVariantId\n sku\n name\n quantity\n backorderedQuantity\n readyQuantity\n stockOnHand\n supplierCancelled\n }\n totalItems\n }\n }\n";
|
|
40
|
+
/**
|
|
41
|
+
* The list without `supplierCancelled`, for an engine before purchase-orders step 5.1: the SDK
|
|
42
|
+
* retries once with it when the engine refuses the field, and answers it `false` (that engine has
|
|
43
|
+
* no order links, so no supplier cancelled one).
|
|
44
|
+
*/
|
|
45
|
+
export declare const LEGACY_BACKORDERS_READY_QUERY = "\n query BackordersReadyToShipLegacy($options: BackordersReadyOptions) {\n backordersReadyToShip(options: $options) {\n items { \n orderId\n orderCode\n orderState\n orderPlacedAt\n orderLineId\n productVariantId\n sku\n name\n quantity\n backorderedQuantity\n readyQuantity\n stockOnHand\n }\n totalItems\n }\n }\n";
|
|
46
|
+
/**
|
|
47
|
+
* `admin.purchasing.backorders` (purchase-orders spec §8.11, §8.14): what a goods receipt let the
|
|
48
|
+
* store ship. A read only: nothing is allocated. Needs the Purchasing plugin on the store and
|
|
49
|
+
* purchasing switched on for its legal entity (`PURCHASING_NOT_ENABLED`, 422), a store that
|
|
50
|
+
* purchases (`HUB_HAS_NO_PURCHASING`, `LEGAL_ENTITY_REQUIRED`), and `ReadOrder`.
|
|
51
|
+
*/
|
|
52
|
+
export interface PurchasingBackordersModule {
|
|
53
|
+
/**
|
|
54
|
+
* The active store's open backordered order lines that stock on hand can ship now, oldest order
|
|
55
|
+
* first (pages of at most 100). `goodsReceiptId` narrows them to the variants that receipt
|
|
56
|
+
* brought in. Refusals: `GOODS_RECEIPT_NOT_FOUND` (404, a receipt of another legal entity too),
|
|
57
|
+
* `GOODS_RECEIPT_INVALID` (400, a page out of range).
|
|
58
|
+
*/
|
|
59
|
+
ready(options?: BackordersReadyOptions): Promise<Page<BackorderReadyLine>>;
|
|
60
|
+
}
|
|
61
|
+
export declare function createPurchasingBackordersModule(transport: Transport): PurchasingBackordersModule;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { pageFromVendureList, readOffsetCursor, } from '@sixtyninecommerce/core';
|
|
2
|
+
import { isUnknownFieldRejection } from '../payments/store-credit-fields.js';
|
|
3
|
+
/** The line's fields an engine before purchase-orders step 5.1 serves. */
|
|
4
|
+
const STEP_3_6_LINE_FIELDS = /* GraphQL */ `
|
|
5
|
+
orderId
|
|
6
|
+
orderCode
|
|
7
|
+
orderState
|
|
8
|
+
orderPlacedAt
|
|
9
|
+
orderLineId
|
|
10
|
+
productVariantId
|
|
11
|
+
sku
|
|
12
|
+
name
|
|
13
|
+
quantity
|
|
14
|
+
backorderedQuantity
|
|
15
|
+
readyQuantity
|
|
16
|
+
stockOnHand
|
|
17
|
+
`;
|
|
18
|
+
export const BACKORDER_READY_LINE_FIELDS = /* GraphQL */ `
|
|
19
|
+
orderId
|
|
20
|
+
orderCode
|
|
21
|
+
orderState
|
|
22
|
+
orderPlacedAt
|
|
23
|
+
orderLineId
|
|
24
|
+
productVariantId
|
|
25
|
+
sku
|
|
26
|
+
name
|
|
27
|
+
quantity
|
|
28
|
+
backorderedQuantity
|
|
29
|
+
readyQuantity
|
|
30
|
+
stockOnHand
|
|
31
|
+
supplierCancelled
|
|
32
|
+
`;
|
|
33
|
+
export const BACKORDERS_READY_QUERY = /* GraphQL */ `
|
|
34
|
+
query BackordersReadyToShip($options: BackordersReadyOptions) {
|
|
35
|
+
backordersReadyToShip(options: $options) {
|
|
36
|
+
items { ${BACKORDER_READY_LINE_FIELDS} }
|
|
37
|
+
totalItems
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
`;
|
|
41
|
+
/**
|
|
42
|
+
* The list without `supplierCancelled`, for an engine before purchase-orders step 5.1: the SDK
|
|
43
|
+
* retries once with it when the engine refuses the field, and answers it `false` (that engine has
|
|
44
|
+
* no order links, so no supplier cancelled one).
|
|
45
|
+
*/
|
|
46
|
+
export const LEGACY_BACKORDERS_READY_QUERY = /* GraphQL */ `
|
|
47
|
+
query BackordersReadyToShipLegacy($options: BackordersReadyOptions) {
|
|
48
|
+
backordersReadyToShip(options: $options) {
|
|
49
|
+
items { ${STEP_3_6_LINE_FIELDS} }
|
|
50
|
+
totalItems
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
`;
|
|
54
|
+
const DEFAULT_PAGE_SIZE = 25;
|
|
55
|
+
export function createPurchasingBackordersModule(transport) {
|
|
56
|
+
const readyPage = async (skip, take, options) => {
|
|
57
|
+
const variables = {
|
|
58
|
+
options: {
|
|
59
|
+
skip,
|
|
60
|
+
take,
|
|
61
|
+
...(options?.goodsReceiptId === undefined
|
|
62
|
+
? {}
|
|
63
|
+
: { goodsReceiptId: options.goodsReceiptId }),
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
let list;
|
|
67
|
+
try {
|
|
68
|
+
const data = await transport.graphql({ api: 'admin', query: BACKORDERS_READY_QUERY, variables }, options);
|
|
69
|
+
list = data.backordersReadyToShip;
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
if (!isUnknownFieldRejection(error))
|
|
73
|
+
throw error;
|
|
74
|
+
const data = await transport.graphql({ api: 'admin', query: LEGACY_BACKORDERS_READY_QUERY, variables }, options);
|
|
75
|
+
list = {
|
|
76
|
+
items: data.backordersReadyToShip.items.map((line) => ({
|
|
77
|
+
...line,
|
|
78
|
+
supplierCancelled: false,
|
|
79
|
+
})),
|
|
80
|
+
totalItems: data.backordersReadyToShip.totalItems,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
return pageFromVendureList(list, skip, take, (cursor) => readyPage(readOffsetCursor(cursor), take, options));
|
|
84
|
+
};
|
|
85
|
+
return {
|
|
86
|
+
async ready(options) {
|
|
87
|
+
const take = options?.limit ?? DEFAULT_PAGE_SIZE;
|
|
88
|
+
const skip = options?.after === undefined ? 0 : readOffsetCursor(options.after);
|
|
89
|
+
return readyPage(skip, take, options);
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
}
|