@sixtyninecommerce/admin 0.10.0 → 0.11.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 +4347 -171
- package/dist/catalog/assets.d.ts +120 -8
- package/dist/catalog/assets.js +98 -12
- package/dist/catalog/collections.d.ts +279 -14
- package/dist/catalog/collections.js +246 -15
- package/dist/catalog/index.d.ts +9 -2
- package/dist/catalog/index.js +4 -0
- package/dist/catalog/products.d.ts +159 -19
- package/dist/catalog/products.js +88 -13
- package/dist/catalog/properties.d.ts +102 -14
- package/dist/catalog/properties.js +13 -0
- package/dist/catalog/recommendations.d.ts +261 -0
- package/dist/catalog/recommendations.js +145 -0
- package/dist/catalog/reviews.d.ts +368 -0
- package/dist/catalog/reviews.js +318 -0
- package/dist/catalog/types.d.ts +17 -1
- package/dist/catalog/types.js +10 -0
- package/dist/catalog/variant-find.d.ts +136 -0
- package/dist/catalog/variant-find.js +239 -0
- package/dist/catalog/variants.d.ts +233 -26
- package/dist/catalog/variants.js +125 -7
- package/dist/catalog/visibility.d.ts +20 -5
- package/dist/catalog/visibility.js +19 -1
- package/dist/client.d.ts +95 -9
- package/dist/client.js +35 -7
- package/dist/content/assets.d.ts +286 -0
- package/dist/content/assets.js +181 -0
- package/dist/content/items.d.ts +269 -0
- package/dist/content/items.js +170 -0
- package/dist/content/types.d.ts +282 -0
- package/dist/content/types.js +100 -0
- package/dist/customers/age-verification.d.ts +66 -0
- package/dist/customers/age-verification.js +79 -0
- package/dist/customers/credit.d.ts +263 -0
- package/dist/customers/credit.js +244 -0
- package/dist/customers/delivery-preferences.d.ts +120 -0
- package/dist/customers/delivery-preferences.js +97 -0
- package/dist/customers/groups.d.ts +207 -0
- package/dist/customers/groups.js +438 -0
- package/dist/customers/index.d.ts +447 -37
- package/dist/customers/index.js +313 -40
- package/dist/customers/invitation-status.d.ts +57 -0
- package/dist/customers/invitation-status.js +21 -0
- package/dist/customers/notes.d.ts +67 -0
- package/dist/customers/notes.js +51 -0
- package/dist/customers/notification-recipients.d.ts +97 -0
- package/dist/customers/notification-recipients.js +84 -0
- package/dist/customers/saved-lists.d.ts +42 -0
- package/dist/customers/saved-lists.js +61 -0
- package/dist/customers/vat.d.ts +83 -0
- package/dist/customers/vat.js +46 -0
- package/dist/customers/wire.d.ts +20 -0
- package/dist/customers/wire.js +35 -0
- package/dist/documents/ar.d.ts +307 -0
- package/dist/documents/ar.js +254 -0
- package/dist/documents/index.d.ts +731 -21
- package/dist/documents/index.js +436 -24
- package/dist/documents/pdf.d.ts +2 -0
- package/dist/documents/pdf.js +11 -0
- package/dist/fulfillments/create.d.ts +167 -0
- package/dist/fulfillments/create.js +216 -0
- package/dist/fulfillments/index.d.ts +44 -10
- package/dist/fulfillments/index.js +11 -2
- package/dist/fulfillments/payment-policy.d.ts +64 -0
- package/dist/fulfillments/payment-policy.js +140 -0
- package/dist/fulfillments/warehouse.d.ts +724 -0
- package/dist/fulfillments/warehouse.js +914 -0
- package/dist/geo/index.d.ts +119 -0
- package/dist/geo/index.js +107 -0
- package/dist/index.d.ts +123 -35
- package/dist/index.js +45 -12
- package/dist/integrations/afas-actions.d.ts +234 -0
- package/dist/integrations/afas-actions.js +310 -0
- package/dist/integrations/afas-types.d.ts +483 -0
- package/dist/integrations/afas-types.js +5 -0
- package/dist/integrations/afas.d.ts +234 -0
- package/dist/integrations/afas.js +290 -0
- package/dist/integrations/connections.d.ts +103 -0
- package/dist/integrations/connections.js +55 -0
- package/dist/integrations/esp.d.ts +313 -0
- package/dist/integrations/esp.js +169 -0
- package/dist/integrations/exact-online-types.d.ts +423 -0
- package/dist/integrations/exact-online-types.js +6 -0
- package/dist/integrations/exact-online.d.ts +248 -0
- package/dist/integrations/exact-online.js +376 -0
- package/dist/integrations/index.d.ts +29 -3
- package/dist/integrations/index.js +18 -3
- package/dist/integrations/pipeline-keys.d.ts +13 -0
- package/dist/integrations/pipeline-keys.js +22 -0
- package/dist/integrations/pipelines.d.ts +15 -14
- package/dist/integrations/pipelines.js +6 -22
- package/dist/integrations/types.d.ts +146 -0
- package/dist/locations/index.d.ts +66 -0
- package/dist/locations/index.js +31 -0
- package/dist/meta/index.d.ts +162 -0
- package/dist/orders/cancel-with-money.d.ts +227 -0
- package/dist/orders/cancel-with-money.js +153 -0
- package/dist/orders/commit-edit.d.ts +7 -1
- package/dist/orders/commit-edit.js +5 -1
- package/dist/orders/drafts.d.ts +800 -0
- package/dist/orders/drafts.js +771 -0
- package/dist/orders/fields.d.ts +245 -0
- package/dist/orders/fields.js +188 -0
- package/dist/orders/get.d.ts +29 -19
- package/dist/orders/get.js +17 -8
- package/dist/orders/index.d.ts +49 -9
- package/dist/orders/index.js +22 -2
- package/dist/orders/lifecycle.d.ts +128 -0
- package/dist/orders/lifecycle.js +158 -0
- package/dist/orders/line-price-preview.d.ts +7 -2
- package/dist/orders/list.d.ts +222 -0
- package/dist/orders/list.js +377 -0
- package/dist/orders/manual-pricing.d.ts +5 -0
- package/dist/orders/order-entry.d.ts +20 -7
- package/dist/orders/plan-edit.d.ts +1 -1
- package/dist/orders/plan-edit.js +4 -1
- package/dist/orders/settings.d.ts +201 -0
- package/dist/orders/settings.js +325 -0
- package/dist/orders/status.d.ts +159 -13
- package/dist/orders/status.js +21 -0
- package/dist/orders/surcharges.d.ts +8 -1
- package/dist/orders/surcharges.js +3 -2
- package/dist/payments/account-credit.d.ts +30 -0
- package/dist/payments/account-credit.js +19 -0
- package/dist/payments/ar.d.ts +292 -0
- package/dist/payments/ar.js +223 -0
- package/dist/payments/connected-account.d.ts +47 -0
- package/dist/payments/connected-account.js +61 -0
- package/dist/payments/credit-noted-fields.d.ts +12 -0
- package/dist/payments/credit-noted-fields.js +37 -0
- package/dist/payments/index.d.ts +257 -11
- package/dist/payments/index.js +131 -1
- package/dist/payments/methods.d.ts +303 -8
- package/dist/payments/methods.js +337 -7
- package/dist/payments/mollie-methods.d.ts +159 -0
- package/dist/payments/mollie-methods.js +141 -0
- package/dist/payments/payment-links.d.ts +180 -0
- package/dist/payments/payment-links.js +97 -0
- package/dist/payments/provider-keys.d.ts +150 -0
- package/dist/payments/provider-keys.js +118 -0
- package/dist/payments/providers.d.ts +86 -11
- package/dist/payments/providers.js +29 -0
- package/dist/payments/requests.d.ts +144 -0
- package/dist/payments/requests.js +92 -0
- package/dist/payments/store-credit-fields.d.ts +15 -0
- package/dist/payments/store-credit-fields.js +51 -0
- package/dist/pickups/index.d.ts +104 -0
- package/dist/pickups/index.js +105 -0
- package/dist/platform/channels.d.ts +132 -0
- package/dist/platform/channels.js +128 -0
- package/dist/platform/geo.d.ts +123 -0
- package/dist/platform/geo.js +144 -0
- package/dist/platform/tax.d.ts +154 -0
- package/dist/platform/tax.js +139 -0
- package/dist/pos/anomalies.d.ts +158 -0
- package/dist/pos/anomalies.js +64 -0
- package/dist/pos/assisted-selling.d.ts +79 -0
- package/dist/pos/assisted-selling.js +38 -0
- package/dist/pos/exchanges.d.ts +103 -0
- package/dist/pos/exchanges.js +22 -0
- package/dist/pos/gift-cards.d.ts +135 -0
- package/dist/pos/gift-cards.js +47 -0
- package/dist/pos/index.d.ts +2125 -32
- package/dist/pos/index.js +742 -4
- package/dist/pos/no-receipt.d.ts +94 -0
- package/dist/pos/no-receipt.js +25 -0
- package/dist/pos/offline-sales.d.ts +187 -0
- package/dist/pos/offline-sales.js +116 -0
- package/dist/pos/pay-by-link.d.ts +143 -0
- package/dist/pos/pay-by-link.js +57 -0
- package/dist/pos/quick-keys.d.ts +103 -0
- package/dist/pos/quick-keys.js +45 -0
- package/dist/pos/reports.d.ts +360 -0
- package/dist/pos/reports.js +62 -0
- package/dist/pos/returns.d.ts +217 -0
- package/dist/pos/returns.js +53 -0
- package/dist/pricing/bulk-coupons.d.ts +139 -0
- package/dist/pricing/bulk-coupons.js +112 -0
- package/dist/pricing/index.d.ts +174 -4
- package/dist/pricing/index.js +174 -1
- package/dist/pricing/price-lists.d.ts +604 -0
- package/dist/pricing/price-lists.js +600 -0
- package/dist/purchasing/alerts.d.ts +89 -0
- package/dist/purchasing/alerts.js +62 -0
- package/dist/purchasing/cost.d.ts +110 -0
- package/dist/purchasing/cost.js +86 -0
- package/dist/purchasing/index.d.ts +36 -0
- package/dist/purchasing/index.js +16 -0
- package/dist/purchasing/purchase-orders.d.ts +548 -0
- package/dist/purchasing/purchase-orders.js +491 -0
- package/dist/purchasing/replenishment.d.ts +281 -0
- package/dist/purchasing/replenishment.js +240 -0
- package/dist/purchasing/settings.d.ts +152 -0
- package/dist/purchasing/settings.js +181 -0
- package/dist/purchasing/suppliers.d.ts +476 -0
- package/dist/purchasing/suppliers.js +438 -0
- package/dist/quotes/index.d.ts +501 -0
- package/dist/quotes/index.js +439 -0
- package/dist/reports/index.d.ts +71 -8
- package/dist/reports/index.js +74 -11
- package/dist/returns/index.d.ts +614 -21
- package/dist/returns/index.js +695 -11
- package/dist/search/index.d.ts +76 -1
- package/dist/search/index.js +63 -0
- package/dist/shipping/definitions.d.ts +29 -0
- package/dist/shipping/definitions.js +47 -0
- package/dist/shipping/fulfillment-profiles.d.ts +174 -0
- package/dist/shipping/fulfillment-profiles.js +104 -0
- package/dist/shipping/hendriks.d.ts +97 -0
- package/dist/shipping/hendriks.js +50 -0
- package/dist/shipping/index.d.ts +48 -0
- package/dist/shipping/index.js +25 -0
- package/dist/shipping/methods.d.ts +198 -0
- package/dist/shipping/methods.js +240 -0
- package/dist/shipping/myparcel.d.ts +254 -0
- package/dist/shipping/myparcel.js +178 -0
- package/dist/shipping/parcel-count-suggestion.d.ts +2 -0
- package/dist/shipping/parcel-rule.d.ts +6 -5
- package/dist/shipping/parcel-rule.js +21 -23
- package/dist/shipping/rate-tables.d.ts +179 -0
- package/dist/shipping/rate-tables.js +184 -0
- package/dist/stock/index.d.ts +10 -3
- package/dist/stock/index.js +3 -0
- package/dist/stock/locations.d.ts +144 -0
- package/dist/stock/locations.js +168 -0
- package/dist/stock/transfers.d.ts +216 -43
- package/dist/stock/transfers.js +140 -13
- package/dist/stored-value/index.d.ts +214 -4
- package/dist/stored-value/index.js +152 -0
- package/dist/subscriptions/index.d.ts +682 -0
- package/dist/subscriptions/index.js +815 -0
- package/dist/tax/index.d.ts +76 -1
- package/dist/tax/index.js +50 -0
- package/dist/tenant/email-style.d.ts +102 -0
- package/dist/tenant/email-style.js +72 -0
- package/dist/tenant/email-templates.d.ts +66 -0
- package/dist/tenant/email-templates.js +54 -0
- package/dist/tenant/notifications.d.ts +23 -17
- package/dist/tenant/settings-sources.d.ts +1 -1
- package/dist/tenant/time-zone.d.ts +17 -0
- package/dist/tenant/time-zone.js +11 -0
- package/dist/types.d.ts +50 -3
- package/dist/users/index.d.ts +3 -0
- package/dist/users/index.js +2 -0
- package/dist/users/warehouses.d.ts +50 -0
- package/dist/users/warehouses.js +51 -0
- package/package.json +2 -2
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `admin.fulfillments.create` and `admin.fulfillments.profileChargePreview`: the shared create
|
|
3
|
+
* flow's write and its office-only delivery-charge preview (headless W10-38; spec
|
|
4
|
+
* `decoupling-fulfillments` §13, §15.4).
|
|
5
|
+
*
|
|
6
|
+
* The create answers the warehouse API's union: a refusal of the WHOLE call is thrown through
|
|
7
|
+
* `warehouseRefusalError`, so a code reaches a caller as the same class from `create` as from
|
|
8
|
+
* `fulfillments.warehouse.append` and the warehouse reads (`INVALID_INPUT` a `ValidationError`
|
|
9
|
+
* 400, never a 422). Each group's own refusal is data in `results[].error`.
|
|
10
|
+
*/
|
|
11
|
+
import { type Transport } from '@sixtyninecommerce/core';
|
|
12
|
+
import type { Call } from '../types.js';
|
|
13
|
+
import { type KeyedWarehouseCall, type WarehouseFulfillment, type WarehouseFulfillmentLineInput, type WarehouseHandlerArg, type WarehouseRefusal } from './warehouse.js';
|
|
14
|
+
/** One fulfilment to create: the lines it ships and its carrier. */
|
|
15
|
+
export interface CreateFulfillmentGroupInput {
|
|
16
|
+
/** 1–500 lines, each a whole quantity of 1 or more, at most what is left to ship. */
|
|
17
|
+
lines: WarehouseFulfillmentLineInput[];
|
|
18
|
+
/** The carrier (fulfilment handler) code; `fulfillments.warehouse.handlers()` lists them. */
|
|
19
|
+
handlerCode: string;
|
|
20
|
+
/**
|
|
21
|
+
* The carrier's own arguments, as text. Send every name in the carrier's
|
|
22
|
+
* `argsRequiredForCreate`; the warehouse, profile and pick override are set by the engine.
|
|
23
|
+
*/
|
|
24
|
+
handlerArgs?: WarehouseHandlerArg[];
|
|
25
|
+
/** Office staff only (`OFFICE_ONLY` otherwise): the profile whose minimum and delivery charge apply. */
|
|
26
|
+
fulfillmentProfileId?: string;
|
|
27
|
+
}
|
|
28
|
+
/** What `create` sends: one fulfilment per group, all shipping from one warehouse. */
|
|
29
|
+
export interface CreateFulfillmentsInput {
|
|
30
|
+
/** The warehouse every group ships from; it must be in the caller's scope. */
|
|
31
|
+
stockLocationId: string;
|
|
32
|
+
/** 1–50 groups. */
|
|
33
|
+
groups: CreateFulfillmentGroupInput[];
|
|
34
|
+
/**
|
|
35
|
+
* Claim each created in-house fulfilment for the caller (`Picking`). Only on a store with the
|
|
36
|
+
* warehouse workspace (`WAREHOUSE_NOT_ENABLED` otherwise).
|
|
37
|
+
*/
|
|
38
|
+
startPicking?: boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Office staff with no location restriction only: ship lines whose order picked another
|
|
41
|
+
* warehouse from this one (`OFFICE_ONLY` / `LOCATION_OUT_OF_SCOPE` otherwise).
|
|
42
|
+
*/
|
|
43
|
+
overridePreferredLocation?: boolean;
|
|
44
|
+
}
|
|
45
|
+
/** What became of one group, by its position in the input. */
|
|
46
|
+
export interface CreateFulfillmentGroupResult {
|
|
47
|
+
index: number;
|
|
48
|
+
/** The fulfilment as it now is; null when the group was refused. */
|
|
49
|
+
fulfillment: WarehouseFulfillment | null;
|
|
50
|
+
/** Why the group was refused; nothing of it was written. */
|
|
51
|
+
error: WarehouseRefusal | null;
|
|
52
|
+
/** Set when the fulfilment WAS created but `startPicking` could not claim it. */
|
|
53
|
+
claimError: WarehouseRefusal | null;
|
|
54
|
+
}
|
|
55
|
+
/** Every group of a `create`, each on its own. */
|
|
56
|
+
export interface CreateFulfillmentsResult {
|
|
57
|
+
results: CreateFulfillmentGroupResult[];
|
|
58
|
+
created: number;
|
|
59
|
+
failed: number;
|
|
60
|
+
}
|
|
61
|
+
/** What `profileChargePreview` prices: a profile and the lines one fulfilment would ship. */
|
|
62
|
+
export interface FulfillmentProfileChargePreviewInput {
|
|
63
|
+
fulfillmentProfileId: string;
|
|
64
|
+
/** 1–500 lines, each a whole quantity of 1 or more. */
|
|
65
|
+
lines: Array<{
|
|
66
|
+
orderLineId: string;
|
|
67
|
+
quantity: number;
|
|
68
|
+
}>;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* A fulfilment profile's minimum and delivery charge for a selection. Nothing was written. Money
|
|
72
|
+
* is integer MINOR UNITS of `currencyCode`.
|
|
73
|
+
*/
|
|
74
|
+
export interface FulfillmentProfileChargePreview {
|
|
75
|
+
profileName: string;
|
|
76
|
+
currencyCode: string;
|
|
77
|
+
/** The profile's minimum order value, NET. */
|
|
78
|
+
minOrder: number;
|
|
79
|
+
/**
|
|
80
|
+
* The net value the minimum is judged on: the selected lines' combined net, or, for a whole
|
|
81
|
+
* order that met the minimum when it was placed, its lines that count toward minimums.
|
|
82
|
+
*/
|
|
83
|
+
selectedNet: number;
|
|
84
|
+
/** How far the selection is below the minimum; 0 when it is met. */
|
|
85
|
+
shortfall: number;
|
|
86
|
+
/** What the profile's shipping method charges for these lines; null when none would be added. */
|
|
87
|
+
deliveryCharge: number | null;
|
|
88
|
+
/** Whether `deliveryCharge` includes tax; null with no charge. */
|
|
89
|
+
deliveryChargeIncludesTax: boolean | null;
|
|
90
|
+
/**
|
|
91
|
+
* Every order the delivery is for already paid delivery at checkout, so no charge is added.
|
|
92
|
+
* When nothing is delivered (`handedOver`), whether every order is fully paid.
|
|
93
|
+
*/
|
|
94
|
+
settled: boolean;
|
|
95
|
+
/**
|
|
96
|
+
* Nothing in the selection is delivered (collected, sold at a till, nothing to ship): the
|
|
97
|
+
* minimum and the delivery charge do not apply (`minOrder` and `shortfall` 0, no charge); the
|
|
98
|
+
* profile still picks the warehouse.
|
|
99
|
+
*/
|
|
100
|
+
handedOver: boolean;
|
|
101
|
+
}
|
|
102
|
+
export interface FulfillmentCreateMethods {
|
|
103
|
+
/**
|
|
104
|
+
* Create fulfilments at one warehouse: one per group, each in its own transaction, through the
|
|
105
|
+
* same create path as the order page. A group that is refused is reported in its
|
|
106
|
+
* `results[].error` and the rest go on, so the answer can mix created and refused groups.
|
|
107
|
+
* Needs `UpdateFulfillment` (warehouse staff, on a store with the warehouse workspace, at a
|
|
108
|
+
* warehouse assigned to them) or `UpdateOrder` (office staff, on any store, at any of its
|
|
109
|
+
* warehouses).
|
|
110
|
+
*
|
|
111
|
+
* MONEY and outside effects, per created fulfilment: a fulfilment profile (office only) can
|
|
112
|
+
* add a delivery charge to its orders, shared across a consolidation; on a store without the
|
|
113
|
+
* warehouse workspace the carrier is booked at create (a label bought, or a 3PL handed the
|
|
114
|
+
* order; with the workspace it is booked later, at Packed); the customer gets the
|
|
115
|
+
* `fulfillment_created` e-mail.
|
|
116
|
+
*
|
|
117
|
+
* A line nobody picked a warehouse for, whose stock is reserved at another warehouse, has its
|
|
118
|
+
* reservation moved here: released there and taken from this warehouse, as it is for
|
|
119
|
+
* warehouse staff assigned here (a line whose order picked another warehouse is refused
|
|
120
|
+
* `LINE_PREFERS_OTHER_LOCATION`, unless the office sets `overridePreferredLocation`).
|
|
121
|
+
*
|
|
122
|
+
* One key per attempt (`options.idempotencyKey`): a repeat under the same key answers the
|
|
123
|
+
* first call's result (each fulfilment as it is now) and creates nothing, however few of a
|
|
124
|
+
* line's units it asked for. Another request under that key is `IDEMPOTENCY_CONFLICT`
|
|
125
|
+
* (`ConflictError`, 409). The groups commit one by one, so the engine spends the key before the
|
|
126
|
+
* first: a repeat while the first is still running, or after it stopped without answering, is
|
|
127
|
+
* `IDEMPOTENCY_OUTCOME_UNKNOWN` (`outcomeUnknown`): read the orders' fulfilments, never create
|
|
128
|
+
* again under a new key blind; once the first has answered, the key replays its answer. A
|
|
129
|
+
* refusal of the whole call does not spend the key. Left out, a key is made up (a UUIDv7) and
|
|
130
|
+
* returned as `idempotencyKey` on the result, and on a thrown error as an untyped own property
|
|
131
|
+
* of the error.
|
|
132
|
+
*
|
|
133
|
+
* Refusals. Of the whole call, THROWN, with the classes of every warehouse refusal:
|
|
134
|
+
* `INVALID_INPUT` (a `ValidationError`, 400; also before any request for a missing warehouse,
|
|
135
|
+
* no groups, a group without a carrier or a line without an id or a whole quantity),
|
|
136
|
+
* `LOCATION_OUT_OF_SCOPE`, `OFFICE_ONLY`, `WAREHOUSE_NOT_ENABLED` (`BusinessRuleError`, 422).
|
|
137
|
+
* Of one group, as DATA: `COMBINE_RULE_VIOLATED` (the part of the rule that broke in
|
|
138
|
+
* `field`), `LINE_OVER_FULFILLED`, `INSUFFICIENT_STOCK_AT_LOCATION`, `ORDER_NOT_COVERED`,
|
|
139
|
+
* `PROFILE_MINIMUM_NOT_MET` (a consolidation short of the profile minimum; one order short of it
|
|
140
|
+
* is `INVALID_INPUT`, its sentence naming the minimum), `SOURCE_FULFILLED_BLOCKED`, a carrier's
|
|
141
|
+
* own refusal, … A missing
|
|
142
|
+
* permission is `FORBIDDEN` and a carrier of a plugin the store does not have
|
|
143
|
+
* `PLUGIN_ACCESS_DENIED` (both `PermissionDeniedError`, 403).
|
|
144
|
+
*
|
|
145
|
+
* @effect money
|
|
146
|
+
*/
|
|
147
|
+
create(input: CreateFulfillmentsInput, options?: KeyedWarehouseCall): Promise<CreateFulfillmentsResult & {
|
|
148
|
+
idempotencyKey: string;
|
|
149
|
+
}>;
|
|
150
|
+
/**
|
|
151
|
+
* What a fulfilment profile would ask of a selection before it is created: its minimum, how
|
|
152
|
+
* far the selection is below it, and the delivery charge it would add. Office staff only
|
|
153
|
+
* (`UpdateOrder`). Who pays which share of a consolidated charge is decided at create. Money in
|
|
154
|
+
* integer minor units.
|
|
155
|
+
*
|
|
156
|
+
* A malformed or unknown profile or line, no lines or more than 500, a bad quantity, or orders
|
|
157
|
+
* in different currencies are `FULFILLMENT_PROFILE_PREVIEW_INVALID` (a `ValidationError`,
|
|
158
|
+
* 400); a selection the create would refuse answers that code (`LOCATION_OUT_OF_SCOPE`,
|
|
159
|
+
* `COMBINE_RULE_VIOLATED`, `PROFILE_CURRENCY_MISMATCH`, `BusinessRuleError`).
|
|
160
|
+
*
|
|
161
|
+
* @effect read
|
|
162
|
+
*/
|
|
163
|
+
profileChargePreview(input: FulfillmentProfileChargePreviewInput, options?: Call): Promise<FulfillmentProfileChargePreview>;
|
|
164
|
+
}
|
|
165
|
+
export declare const CREATE_WAREHOUSE_FULFILLMENTS_MUTATION = "\n mutation CreateWarehouseFulfillments(\n $input: CreateWarehouseFulfillmentsInput!\n $idempotencyKey: String\n ) {\n createWarehouseFulfillments(input: $input, idempotencyKey: $idempotencyKey) {\n __typename\n ... on WarehouseCreateResult {\n created\n failed\n results {\n index\n fulfillment {\n \n id\n state\n handlerCode\n method\n mode\n trackingCode\n stockLocationId\n stockLocationName\n createdAt\n stateChangedAt\n overdue\n claimedBy {\n id\n name\n }\n claimedAt\n packedAt\n bookingStatus\n bookingError\n parcels {\n weightGrams\n lengthMm\n widthMm\n heightMm\n }\n orders {\n id\n code\n poNumber\n orderNotes\n requestedDeliveryDate\n deliveryDue\n paymentBadge\n hold {\n reason\n note\n }\n }\n customer {\n id\n name\n phone\n }\n shippingAddress {\n \n fullName\n company\n streetLine1\n streetLine2\n city\n province\n postalCode\n countryCode\n\n }\n lineCount\n unitCount\n\n }\n error {\n code\n message\n field\n }\n claimError {\n code\n message\n field\n }\n }\n }\n ... on WarehouseError {\n errorCode\n code\n message\n field\n }\n }\n }\n";
|
|
166
|
+
export declare const FULFILLMENT_PROFILE_CHARGE_PREVIEW_QUERY = "\n query FulfillmentProfileChargePreview($input: FulfillmentProfileChargePreviewInput!) {\n fulfillmentProfileChargePreview(input: $input) {\n profileName\n currencyCode\n minOrder\n selectedNet\n shortfall\n deliveryCharge\n deliveryChargeIncludesTax\n settled\n handedOver\n }\n }\n";
|
|
167
|
+
export declare function createFulfillmentCreateMethods(transport: Transport): FulfillmentCreateMethods;
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `admin.fulfillments.create` and `admin.fulfillments.profileChargePreview`: the shared create
|
|
3
|
+
* flow's write and its office-only delivery-charge preview (headless W10-38; spec
|
|
4
|
+
* `decoupling-fulfillments` §13, §15.4).
|
|
5
|
+
*
|
|
6
|
+
* The create answers the warehouse API's union: a refusal of the WHOLE call is thrown through
|
|
7
|
+
* `warehouseRefusalError`, so a code reaches a caller as the same class from `create` as from
|
|
8
|
+
* `fulfillments.warehouse.append` and the warehouse reads (`INVALID_INPUT` a `ValidationError`
|
|
9
|
+
* 400, never a 422). Each group's own refusal is data in `results[].error`.
|
|
10
|
+
*/
|
|
11
|
+
import { idempotencyKey as mintIdempotencyKey, ValidationError, } from '@sixtyninecommerce/core';
|
|
12
|
+
import { assertId, FULFILLMENT_FIELDS, invalid, isWarehouseErrorMember, toFulfillment, toRefusal, warehouseRefusalError, withAttemptKey, } from './warehouse.js';
|
|
13
|
+
export const CREATE_WAREHOUSE_FULFILLMENTS_MUTATION = /* GraphQL */ `
|
|
14
|
+
mutation CreateWarehouseFulfillments(
|
|
15
|
+
$input: CreateWarehouseFulfillmentsInput!
|
|
16
|
+
$idempotencyKey: String
|
|
17
|
+
) {
|
|
18
|
+
createWarehouseFulfillments(input: $input, idempotencyKey: $idempotencyKey) {
|
|
19
|
+
__typename
|
|
20
|
+
... on WarehouseCreateResult {
|
|
21
|
+
created
|
|
22
|
+
failed
|
|
23
|
+
results {
|
|
24
|
+
index
|
|
25
|
+
fulfillment {
|
|
26
|
+
${FULFILLMENT_FIELDS}
|
|
27
|
+
}
|
|
28
|
+
error {
|
|
29
|
+
code
|
|
30
|
+
message
|
|
31
|
+
field
|
|
32
|
+
}
|
|
33
|
+
claimError {
|
|
34
|
+
code
|
|
35
|
+
message
|
|
36
|
+
field
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
... on WarehouseError {
|
|
41
|
+
errorCode
|
|
42
|
+
code
|
|
43
|
+
message
|
|
44
|
+
field
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
`;
|
|
49
|
+
export const FULFILLMENT_PROFILE_CHARGE_PREVIEW_QUERY = /* GraphQL */ `
|
|
50
|
+
query FulfillmentProfileChargePreview($input: FulfillmentProfileChargePreviewInput!) {
|
|
51
|
+
fulfillmentProfileChargePreview(input: $input) {
|
|
52
|
+
profileName
|
|
53
|
+
currencyCode
|
|
54
|
+
minOrder
|
|
55
|
+
selectedNet
|
|
56
|
+
shortfall
|
|
57
|
+
deliveryCharge
|
|
58
|
+
deliveryChargeIncludesTax
|
|
59
|
+
settled
|
|
60
|
+
handedOver
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
`;
|
|
64
|
+
const isObject = (value) => typeof value === 'object' && value !== null;
|
|
65
|
+
/**
|
|
66
|
+
* Refused before any request: each of these would otherwise fail GraphQL validation with no code.
|
|
67
|
+
* Bounds the engine checks (group and line counts, quantities, ids that are not uuids) are left
|
|
68
|
+
* to it.
|
|
69
|
+
*/
|
|
70
|
+
function assertCreateInput(input) {
|
|
71
|
+
if (!isObject(input))
|
|
72
|
+
throw invalid('input', 'Name the warehouse and the groups to create.');
|
|
73
|
+
assertId(input.stockLocationId, 'stockLocationId');
|
|
74
|
+
const groups = input.groups;
|
|
75
|
+
if (!Array.isArray(groups) || groups.length === 0) {
|
|
76
|
+
throw invalid('groups', 'Add at least one group of lines to fulfill.');
|
|
77
|
+
}
|
|
78
|
+
for (const [index, group] of groups.entries()) {
|
|
79
|
+
const at = `groups[${String(index)}]`;
|
|
80
|
+
if (!isObject(group))
|
|
81
|
+
throw invalid(at, `Group ${String(index + 1)} is not a group of lines.`);
|
|
82
|
+
if (typeof group.handlerCode !== 'string' || group.handlerCode.trim() === '') {
|
|
83
|
+
throw invalid(`${at}.handlerCode`, `Group ${String(index + 1)}: choose a carrier (fulfillment handler).`);
|
|
84
|
+
}
|
|
85
|
+
if (!Array.isArray(group.lines)) {
|
|
86
|
+
throw invalid(`${at}.lines`, `Group ${String(index + 1)}: list the lines it ships.`);
|
|
87
|
+
}
|
|
88
|
+
for (const [lineIndex, line] of group.lines.entries()) {
|
|
89
|
+
const lineAt = `${at}.lines[${String(lineIndex)}]`;
|
|
90
|
+
assertId(isObject(line) ? line.orderLineId : undefined, `${lineAt}.orderLineId`);
|
|
91
|
+
if (!Number.isInteger(line.quantity)) {
|
|
92
|
+
throw invalid(`${lineAt}.quantity`, 'A quantity is a whole number.');
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
if (group.handlerArgs !== undefined && !Array.isArray(group.handlerArgs)) {
|
|
96
|
+
throw invalid(`${at}.handlerArgs`, 'The carrier arguments are a list of names and values.');
|
|
97
|
+
}
|
|
98
|
+
for (const [argIndex, arg] of (group.handlerArgs ?? []).entries()) {
|
|
99
|
+
if (!isObject(arg) || typeof arg.name !== 'string' || typeof arg.value !== 'string') {
|
|
100
|
+
throw invalid(`${at}.handlerArgs[${String(argIndex)}]`, 'Each carrier argument has a name and a value, both text.');
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
function invalidPreview(field, message) {
|
|
106
|
+
return new ValidationError({
|
|
107
|
+
code: 'FULFILLMENT_PROFILE_PREVIEW_INVALID',
|
|
108
|
+
message,
|
|
109
|
+
status: 400,
|
|
110
|
+
service: 'vendure',
|
|
111
|
+
details: [{ field, code: 'FULFILLMENT_PROFILE_PREVIEW_INVALID', message }],
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
/** Refused before any request, with the engine's own code: each would fail GraphQL validation. */
|
|
115
|
+
function assertPreviewInput(input) {
|
|
116
|
+
if (!isObject(input))
|
|
117
|
+
throw invalidPreview('input', 'Name a profile and the lines to preview.');
|
|
118
|
+
const profileId = input.fulfillmentProfileId;
|
|
119
|
+
if (typeof profileId !== 'string' || profileId.trim() === '') {
|
|
120
|
+
throw invalidPreview('fulfillmentProfileId', 'A fulfillment profile id is required.');
|
|
121
|
+
}
|
|
122
|
+
if (!Array.isArray(input.lines)) {
|
|
123
|
+
throw invalidPreview('lines', 'List the lines the fulfillment would ship.');
|
|
124
|
+
}
|
|
125
|
+
for (const [index, line] of input.lines.entries()) {
|
|
126
|
+
const at = `lines[${String(index)}]`;
|
|
127
|
+
const id = isObject(line) ? line.orderLineId : undefined;
|
|
128
|
+
if (typeof id !== 'string' || id.trim() === '') {
|
|
129
|
+
throw invalidPreview(`${at}.orderLineId`, 'Each line names its order line.');
|
|
130
|
+
}
|
|
131
|
+
if (!Number.isInteger(line.quantity)) {
|
|
132
|
+
throw invalidPreview(`${at}.quantity`, 'A quantity is a whole number.');
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
export function createFulfillmentCreateMethods(transport) {
|
|
137
|
+
return {
|
|
138
|
+
async create(input, options) {
|
|
139
|
+
assertCreateInput(input);
|
|
140
|
+
const key = options?.idempotencyKey ?? mintIdempotencyKey();
|
|
141
|
+
try {
|
|
142
|
+
const data = await transport.graphql({
|
|
143
|
+
api: 'admin',
|
|
144
|
+
query: CREATE_WAREHOUSE_FULFILLMENTS_MUTATION,
|
|
145
|
+
variables: {
|
|
146
|
+
input: {
|
|
147
|
+
stockLocationId: input.stockLocationId,
|
|
148
|
+
groups: input.groups.map((group) => ({
|
|
149
|
+
lines: group.lines.map(({ orderLineId, quantity }) => ({
|
|
150
|
+
orderLineId,
|
|
151
|
+
quantity,
|
|
152
|
+
})),
|
|
153
|
+
handlerCode: group.handlerCode,
|
|
154
|
+
...(group.handlerArgs
|
|
155
|
+
? { handlerArgs: group.handlerArgs.map(({ name, value }) => ({ name, value })) }
|
|
156
|
+
: {}),
|
|
157
|
+
...(group.fulfillmentProfileId
|
|
158
|
+
? { fulfillmentProfileId: group.fulfillmentProfileId }
|
|
159
|
+
: {}),
|
|
160
|
+
})),
|
|
161
|
+
...(input.startPicking === undefined ? {} : { startPicking: input.startPicking }),
|
|
162
|
+
...(input.overridePreferredLocation === undefined
|
|
163
|
+
? {}
|
|
164
|
+
: { overridePreferredLocation: input.overridePreferredLocation }),
|
|
165
|
+
},
|
|
166
|
+
idempotencyKey: key,
|
|
167
|
+
},
|
|
168
|
+
}, { ...options, idempotencyKey: key });
|
|
169
|
+
const result = data.createWarehouseFulfillments;
|
|
170
|
+
// The whole call refused: classified like every other warehouse refusal (W10-37 F-4).
|
|
171
|
+
if (isWarehouseErrorMember(result))
|
|
172
|
+
throw warehouseRefusalError(result);
|
|
173
|
+
const outcome = result;
|
|
174
|
+
return {
|
|
175
|
+
created: outcome.created,
|
|
176
|
+
failed: outcome.failed,
|
|
177
|
+
results: outcome.results.map((item) => ({
|
|
178
|
+
index: item.index,
|
|
179
|
+
fulfillment: item.fulfillment ? toFulfillment(item.fulfillment) : null,
|
|
180
|
+
error: toRefusal(item.error),
|
|
181
|
+
claimError: toRefusal(item.claimError),
|
|
182
|
+
})),
|
|
183
|
+
idempotencyKey: key,
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
catch (error) {
|
|
187
|
+
throw withAttemptKey(error, key);
|
|
188
|
+
}
|
|
189
|
+
},
|
|
190
|
+
async profileChargePreview(input, options) {
|
|
191
|
+
assertPreviewInput(input);
|
|
192
|
+
const data = await transport.graphql({
|
|
193
|
+
api: 'admin',
|
|
194
|
+
query: FULFILLMENT_PROFILE_CHARGE_PREVIEW_QUERY,
|
|
195
|
+
variables: {
|
|
196
|
+
input: {
|
|
197
|
+
fulfillmentProfileId: input.fulfillmentProfileId,
|
|
198
|
+
lines: input.lines.map(({ orderLineId, quantity }) => ({ orderLineId, quantity })),
|
|
199
|
+
},
|
|
200
|
+
},
|
|
201
|
+
}, options);
|
|
202
|
+
const preview = data.fulfillmentProfileChargePreview;
|
|
203
|
+
return {
|
|
204
|
+
profileName: preview.profileName,
|
|
205
|
+
currencyCode: preview.currencyCode,
|
|
206
|
+
minOrder: preview.minOrder,
|
|
207
|
+
selectedNet: preview.selectedNet,
|
|
208
|
+
shortfall: preview.shortfall,
|
|
209
|
+
deliveryCharge: preview.deliveryCharge ?? null,
|
|
210
|
+
deliveryChargeIncludesTax: preview.deliveryChargeIncludesTax ?? null,
|
|
211
|
+
settled: preview.settled,
|
|
212
|
+
handedOver: preview.handedOver,
|
|
213
|
+
};
|
|
214
|
+
},
|
|
215
|
+
};
|
|
216
|
+
}
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { type Transport } from '@sixtyninecommerce/core';
|
|
2
2
|
import type { OrderOverpaymentChoice, OverpaymentOption } from '../orders/surcharges.js';
|
|
3
3
|
import type { Call } from '../types.js';
|
|
4
|
+
import { type FulfillmentCreateMethods } from './create.js';
|
|
5
|
+
import { type FulfillmentPaymentPolicyModule } from './payment-policy.js';
|
|
6
|
+
import { type WarehouseDemandRouting, type WarehouseFulfillmentsModule } from './warehouse.js';
|
|
7
|
+
export type { WarehouseDemandRouting };
|
|
4
8
|
/**
|
|
5
9
|
* A fulfillment's parcels as the order records them: one MyParcel shipment per
|
|
6
10
|
* parcel, and the barcodes the carrier has issued so far.
|
|
@@ -40,8 +44,6 @@ export interface FulfillmentParcelTracking {
|
|
|
40
44
|
parcelCount: number;
|
|
41
45
|
}
|
|
42
46
|
export type FulfillmentLabelFormat = 'A4' | 'A6';
|
|
43
|
-
/** How a warehouse's "To ship" queue routes a line (`WarehouseDemandRouting`). */
|
|
44
|
-
export type WarehouseDemandRouting = 'preferred' | 'allocated' | 'unrouted' | 'no_stock';
|
|
45
47
|
/**
|
|
46
48
|
* Which "To ship" entry of an order to read. The entry, and so whether it
|
|
47
49
|
* holds the whole order, depends on the warehouse and on the routing filters:
|
|
@@ -196,6 +198,11 @@ export interface FulfillmentCancelSettlement {
|
|
|
196
198
|
amount: number;
|
|
197
199
|
currencyCode: string;
|
|
198
200
|
choice: OrderOverpaymentChoice;
|
|
201
|
+
/**
|
|
202
|
+
* For `ACCOUNT_CREDIT`: the store's customer-credit book the credit lands in (`AR` or
|
|
203
|
+
* `STORE_CREDIT`); null for `REFUND` and from an engine before it.
|
|
204
|
+
*/
|
|
205
|
+
book?: string | null;
|
|
199
206
|
}
|
|
200
207
|
/** A cancelled fulfillment and what its cancel queued. */
|
|
201
208
|
export interface CancelledFulfillment {
|
|
@@ -262,8 +269,11 @@ export declare const FULFILLMENT_COVERAGE_REASON_MAX_LENGTH = 500;
|
|
|
262
269
|
* Fulfillment parcels (multi-parcel shipments). Each method is its own request
|
|
263
270
|
* on purpose: a caller deployed ahead of an engine without these fields sees
|
|
264
271
|
* only that call refused, never the order or fulfillment read beside it.
|
|
272
|
+
*
|
|
273
|
+
* `create` and `profileChargePreview` (the shared create flow) are declared in
|
|
274
|
+
* `fulfillments/create.ts`.
|
|
265
275
|
*/
|
|
266
|
-
export interface FulfillmentsModule {
|
|
276
|
+
export interface FulfillmentsModule extends FulfillmentCreateMethods {
|
|
267
277
|
/** Every fulfillment of an order, with its parcels. Needs `ReadOrder`. */
|
|
268
278
|
parcelsOfOrder(orderId: string, options?: Call): Promise<OrderFulfillmentParcels[]>;
|
|
269
279
|
/**
|
|
@@ -281,7 +291,11 @@ export interface FulfillmentsModule {
|
|
|
281
291
|
/**
|
|
282
292
|
* Every parcel's MyParcel label merged by MyParcel into one PDF, fetched live
|
|
283
293
|
* and never cached. Needs `ReadOrder`. Allowed up to 55s: the engine retries
|
|
284
|
-
* a slow MyParcel within a budget the default timeout does not cover.
|
|
294
|
+
* a slow MyParcel within a budget the default timeout does not cover. A
|
|
295
|
+
* credential restricted to stock locations (an API token or app installation
|
|
296
|
+
* with `locationIds`) prints it only for a fulfillment that ships from one of
|
|
297
|
+
* them; any other, another store's and a missing one included, is
|
|
298
|
+
* `LOCATION_OUT_OF_SCOPE` (422).
|
|
285
299
|
*/
|
|
286
300
|
myparcelLabel(fulfillmentId: string, format: FulfillmentLabelFormat, options?: Call): Promise<FulfillmentLabel>;
|
|
287
301
|
/**
|
|
@@ -303,8 +317,11 @@ export interface FulfillmentsModule {
|
|
|
303
317
|
* `NotFoundError` (`MYPARCEL_ORDER_NOT_FOUND_ERROR`); a cancelled one, or
|
|
304
318
|
* one holding no shipment, a `BusinessRuleError`
|
|
305
319
|
* (`MYPARCEL_VALIDATION_ERROR`), as is MyParcel switched off for the store
|
|
306
|
-
* (`MYPARCEL_NOT_CONFIGURED_ERROR`).
|
|
307
|
-
*
|
|
320
|
+
* (`MYPARCEL_NOT_CONFIGURED_ERROR`). A credential restricted to stock
|
|
321
|
+
* locations (an API token or app installation with `locationIds`) is refused
|
|
322
|
+
* `LOCATION_OUT_OF_SCOPE` (422) first for a fulfillment that does not ship
|
|
323
|
+
* from one of them, another store's and a missing one included. Only queues
|
|
324
|
+
* work, so the default timeout applies.
|
|
308
325
|
*/
|
|
309
326
|
labelMyparcelParcels(fulfillmentId: string, input?: LabelMyparcelParcelsInput, options?: Call): Promise<MyparcelParcelsLabelRequest>;
|
|
310
327
|
/**
|
|
@@ -336,7 +353,10 @@ export interface FulfillmentsModule {
|
|
|
336
353
|
* What cancelling the fulfillment would owe each order's customer back (a delivery charge
|
|
337
354
|
* they already paid), and which ways of settling it are open. Read now, without a lock; the
|
|
338
355
|
* cancel judges it again. Needs `UpdateOrder`. Another store's fulfillment is a
|
|
339
|
-
* `NotFoundError` (`ENTITY_NOT_FOUND`).
|
|
356
|
+
* `NotFoundError` (`ENTITY_NOT_FOUND`). A credential restricted to stock locations (an API
|
|
357
|
+
* token or app installation with `locationIds`) reads it only for a fulfillment that ships
|
|
358
|
+
* from one of them; any other, another store's and a missing one included, is
|
|
359
|
+
* `LOCATION_OUT_OF_SCOPE` (422).
|
|
340
360
|
*/
|
|
341
361
|
cancelPreview(fulfillmentId: string, options?: Call): Promise<FulfillmentCancelPreview>;
|
|
342
362
|
/**
|
|
@@ -355,7 +375,10 @@ export interface FulfillmentsModule {
|
|
|
355
375
|
* `AR_ACCOUNT_IN_ERP`, `ORDER_OVERPAYMENT_REFUND_UNRECONCILED`, …);
|
|
356
376
|
* `FULFILLMENT_CANCEL_REFUSED` (→ `BusinessRuleError`) when the fulfillment's process refuses
|
|
357
377
|
* the cancel (already cancelled, a carrier that cannot let go). Another store's fulfillment is
|
|
358
|
-
* a `NotFoundError`.
|
|
378
|
+
* a `NotFoundError`. A credential restricted to stock locations (an API token or app
|
|
379
|
+
* installation with `locationIds`) cancels only a fulfillment that ships from one of them; any
|
|
380
|
+
* other (one with no recorded warehouse, another store's and a missing one included) is
|
|
381
|
+
* `LOCATION_OUT_OF_SCOPE` (422), before the key is claimed.
|
|
359
382
|
*
|
|
360
383
|
* `options.idempotencyKey`: a repeat under it answers the first call's result, never a second
|
|
361
384
|
* cancel; the same key with a different choice is `IDEMPOTENCY_CONFLICT`.
|
|
@@ -389,14 +412,25 @@ export interface FulfillmentsModule {
|
|
|
389
412
|
allowUncovered(orderId: string, reason: string, options?: Call & {
|
|
390
413
|
idempotencyKey?: string;
|
|
391
414
|
}): Promise<FulfillmentCoverageOverride>;
|
|
415
|
+
/**
|
|
416
|
+
* The store's fulfilment payment policy (`fulfillmentPaymentPolicy`): whether a fulfilment of
|
|
417
|
+
* an order not paid in full is refused `ORDER_NOT_COVERED`, and which payment methods are
|
|
418
|
+
* exempt. Read as the engine applies it; written whole (`UpdateChannel`).
|
|
419
|
+
*/
|
|
420
|
+
paymentPolicy: FulfillmentPaymentPolicyModule;
|
|
421
|
+
/**
|
|
422
|
+
* The pick/pack/ship workspace: the warehouse API (price-free, location-scoped), the pick list
|
|
423
|
+
* and consolidation. See `fulfillments/warehouse.ts`.
|
|
424
|
+
*/
|
|
425
|
+
warehouse: WarehouseFulfillmentsModule;
|
|
392
426
|
}
|
|
393
427
|
/**
|
|
394
428
|
* Its own document, like every call here: on an engine without the query this call alone fails
|
|
395
429
|
* validation.
|
|
396
430
|
*/
|
|
397
|
-
export declare const FULFILLMENT_CANCEL_PREVIEW_QUERY = "\n query FulfillmentCancelPreview($fulfillmentId: ID!) {\n fulfillmentCancelPreview(fulfillmentId: $fulfillmentId) {\n fulfillmentId\n overpayments {\n orderId\n orderCode\n amount\n currencyCode\n refund { \n available\n code\n message\n }\n accountCredit { \n available\n code\n message\n }\n }\n }\n }\n";
|
|
431
|
+
export declare const FULFILLMENT_CANCEL_PREVIEW_QUERY = "\n query FulfillmentCancelPreview($fulfillmentId: ID!) {\n fulfillmentCancelPreview(fulfillmentId: $fulfillmentId) {\n fulfillmentId\n overpayments {\n orderId\n orderCode\n amount\n currencyCode\n refund { \n available\n code\n message\n book\n }\n accountCredit { \n available\n code\n message\n book\n }\n }\n }\n }\n";
|
|
398
432
|
/** `$overpayment` and `$idempotencyKey` are optional and left out of the variables when not given. */
|
|
399
|
-
export declare const CANCEL_FULFILLMENT_MUTATION = "\n mutation CancelFulfillment(\n $fulfillmentId: ID!\n $overpayment: [FulfillmentCancelOverpaymentInput!]\n $idempotencyKey: String\n ) {\n cancelFulfillment(\n fulfillmentId: $fulfillmentId\n overpayment: $overpayment\n idempotencyKey: $idempotencyKey\n ) {\n fulfillment {\n id\n state\n }\n settlements {\n orderId\n orderCode\n amount\n currencyCode\n choice\n }\n }\n }\n";
|
|
433
|
+
export declare const CANCEL_FULFILLMENT_MUTATION = "\n mutation CancelFulfillment(\n $fulfillmentId: ID!\n $overpayment: [FulfillmentCancelOverpaymentInput!]\n $idempotencyKey: String\n ) {\n cancelFulfillment(\n fulfillmentId: $fulfillmentId\n overpayment: $overpayment\n idempotencyKey: $idempotencyKey\n ) {\n fulfillment {\n id\n state\n }\n settlements {\n orderId\n orderCode\n amount\n currencyCode\n choice\n book\n }\n }\n }\n";
|
|
400
434
|
/** Its own document: on an engine without the query this call alone fails validation. */
|
|
401
435
|
export declare const ORDER_FULFILLMENT_COVERAGE_QUERY = "\n query OrderFulfillmentCoverage($orderId: ID!) {\n orderFulfillmentCoverage(orderId: $orderId) {\n orderId\n orderCode\n currencyCode\n policyActive\n covered\n outstanding\n exempt\n exemptReason\n allowed\n override { \n id\n orderId\n reason\n actorKind\n actorId\n outstandingAtGrant\n currencyCode\n createdAt\n active\n }\n }\n }\n";
|
|
402
436
|
/** `$idempotencyKey` is optional and left out of the variables when not given. */
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import { errorFromErrorResult, isErrorResult, NotFoundError, ValidationError, } from '@sixtyninecommerce/core';
|
|
2
|
+
import { storeCreditAware } from '../payments/store-credit-fields.js';
|
|
3
|
+
import { createFulfillmentCreateMethods } from './create.js';
|
|
4
|
+
import { createFulfillmentPaymentPolicyModule, } from './payment-policy.js';
|
|
5
|
+
import { createWarehouseFulfillmentsModule, } from './warehouse.js';
|
|
2
6
|
/** The longest override reason the engine stores. */
|
|
3
7
|
export const FULFILLMENT_COVERAGE_REASON_MAX_LENGTH = 500;
|
|
4
8
|
/** Every field of `OrderOverpaymentOption`. */
|
|
@@ -6,6 +10,7 @@ const OVERPAYMENT_OPTION_FIELDS = /* GraphQL */ `
|
|
|
6
10
|
available
|
|
7
11
|
code
|
|
8
12
|
message
|
|
13
|
+
book
|
|
9
14
|
`;
|
|
10
15
|
/**
|
|
11
16
|
* Its own document, like every call here: on an engine without the query this call alone fails
|
|
@@ -48,6 +53,7 @@ export const CANCEL_FULFILLMENT_MUTATION = /* GraphQL */ `
|
|
|
48
53
|
amount
|
|
49
54
|
currencyCode
|
|
50
55
|
choice
|
|
56
|
+
book
|
|
51
57
|
}
|
|
52
58
|
}
|
|
53
59
|
}
|
|
@@ -360,6 +366,9 @@ function assertLabelRequest(fulfillmentId, format) {
|
|
|
360
366
|
}
|
|
361
367
|
export function createFulfillmentsModule(transport) {
|
|
362
368
|
return {
|
|
369
|
+
...createFulfillmentCreateMethods(transport),
|
|
370
|
+
paymentPolicy: createFulfillmentPaymentPolicyModule(transport),
|
|
371
|
+
warehouse: createWarehouseFulfillmentsModule(transport),
|
|
363
372
|
async parcelsOfOrder(orderId, options) {
|
|
364
373
|
const data = await transport.graphql({ api: 'admin', query: ORDER_FULFILLMENT_PARCELS_QUERY, variables: { id: orderId } }, options);
|
|
365
374
|
if (!data.order)
|
|
@@ -442,7 +451,7 @@ export function createFulfillmentsModule(transport) {
|
|
|
442
451
|
if (typeof fulfillmentId !== 'string' || fulfillmentId.trim() === '') {
|
|
443
452
|
throw invalidCancel('fulfillmentId', 'A fulfillment id is required.');
|
|
444
453
|
}
|
|
445
|
-
const data = await transport.graphql({ api: 'admin', query: FULFILLMENT_CANCEL_PREVIEW_QUERY, variables: { fulfillmentId } }, options);
|
|
454
|
+
const data = await storeCreditAware(transport).graphql({ api: 'admin', query: FULFILLMENT_CANCEL_PREVIEW_QUERY, variables: { fulfillmentId } }, options);
|
|
446
455
|
const preview = data.fulfillmentCancelPreview;
|
|
447
456
|
return {
|
|
448
457
|
fulfillmentId: preview.fulfillmentId,
|
|
@@ -459,7 +468,7 @@ export function createFulfillmentsModule(transport) {
|
|
|
459
468
|
async cancel(fulfillmentId, input = {}, options) {
|
|
460
469
|
assertCancelRequest(fulfillmentId, input);
|
|
461
470
|
const overpayment = input.overpayment ?? [];
|
|
462
|
-
const data = await transport.graphql({
|
|
471
|
+
const data = await storeCreditAware(transport).graphql({
|
|
463
472
|
api: 'admin',
|
|
464
473
|
query: CANCEL_FULFILLMENT_MUTATION,
|
|
465
474
|
// A variable, not a header: the Admin API takes the key as an argument.
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { type Transport } from '@sixtyninecommerce/core';
|
|
2
|
+
import type { Call } from '../types.js';
|
|
3
|
+
/** The most payment method codes `exemptMethodCodes` may name. */
|
|
4
|
+
export declare const FULFILLMENT_PAYMENT_POLICY_EXEMPT_METHODS_MAX = 50;
|
|
5
|
+
/** The longest payment method code the list accepts (`PaymentMethod.code` is a varchar(255)). */
|
|
6
|
+
export declare const FULFILLMENT_PAYMENT_POLICY_METHOD_CODE_MAX_LENGTH = 255;
|
|
7
|
+
/**
|
|
8
|
+
* The store's fulfilment payment policy (`Channel.customFields.fulfillmentPaymentPolicy`):
|
|
9
|
+
* whether goods are handed over before an order is paid in full. Every field present: this is
|
|
10
|
+
* what the engine APPLIES, absent and malformed fields read as their defaults (OFF).
|
|
11
|
+
*/
|
|
12
|
+
export interface FulfillmentPaymentPolicy {
|
|
13
|
+
/**
|
|
14
|
+
* On: a fulfilment is created, extended or shipped only once the money COLLECTED for the
|
|
15
|
+
* order (settled, or held by a payment provider) covers what it bills, unless the order is
|
|
16
|
+
* exempt or an override was recorded (`fulfillments.allowUncovered`). Otherwise refused
|
|
17
|
+
* `ORDER_NOT_COVERED`.
|
|
18
|
+
*/
|
|
19
|
+
requireFullPayment: boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Payment method CODES whose orders are handed over unpaid as a matter of course (cash on
|
|
22
|
+
* delivery, a bank transfer the store ships against), judged on the order's latest live
|
|
23
|
+
* payment. Read as the engine compares them: trimmed, lower-cased, once each.
|
|
24
|
+
*/
|
|
25
|
+
exemptMethodCodes: string[];
|
|
26
|
+
}
|
|
27
|
+
/** The policy of a store that never set one, and of a column that cannot be read: off. */
|
|
28
|
+
export declare const DEFAULT_FULFILLMENT_PAYMENT_POLICY: Readonly<FulfillmentPaymentPolicy>;
|
|
29
|
+
/**
|
|
30
|
+
* The ACTIVE store's fulfilment payment policy (`admin.fulfillments.paymentPolicy`), stored as a
|
|
31
|
+
* Channel custom field and written through core `updateChannel`.
|
|
32
|
+
*
|
|
33
|
+
* Reading needs an authenticated administrator on the store; writing needs `UpdateChannel`.
|
|
34
|
+
*/
|
|
35
|
+
export interface FulfillmentPaymentPolicyModule {
|
|
36
|
+
/**
|
|
37
|
+
* The policy the engine applies, read as tolerantly as the engine reads it
|
|
38
|
+
* (`resolveFulfillmentPaymentPolicy`): a store that never set one, a malformed column and a
|
|
39
|
+
* field of the wrong type all answer OFF ({@link DEFAULT_FULFILLMENT_PAYMENT_POLICY}); the
|
|
40
|
+
* exempt codes are the non-blank ones, trimmed, lower-cased and once each.
|
|
41
|
+
*/
|
|
42
|
+
get(options?: Call): Promise<FulfillmentPaymentPolicy>;
|
|
43
|
+
/**
|
|
44
|
+
* Replace the whole policy, and answer it as the engine now applies it.
|
|
45
|
+
*
|
|
46
|
+
* Both fields are required: read it with {@link get}, change what you mean to, and send it
|
|
47
|
+
* back. Only the schema's fields are stored, the codes trimmed.
|
|
48
|
+
*
|
|
49
|
+
* Refused before any request with `FULFILLMENT_PAYMENT_POLICY_INVALID` ({@link ValidationError},
|
|
50
|
+
* one `details[]` entry per failing field): a non-boolean switch, an exempt list that is not a
|
|
51
|
+
* list, holds a blank or over-long code, names one code twice in any case, or more than 50.
|
|
52
|
+
* The engine's own write validator refuses the same values (a `ValidationError` too, carrying
|
|
53
|
+
* its sentence), so a value that slips past this check is still never stored.
|
|
54
|
+
*
|
|
55
|
+
* Absent and "set to the defaults" mean the same thing to the engine, and a store's column is
|
|
56
|
+
* never backfilled: write the policy when a merchant changed it.
|
|
57
|
+
*/
|
|
58
|
+
set(policy: FulfillmentPaymentPolicy, options?: Call): Promise<FulfillmentPaymentPolicy>;
|
|
59
|
+
}
|
|
60
|
+
export declare const FULFILLMENT_PAYMENT_POLICY_QUERY = "\n query FulfillmentPaymentPolicy {\n activeChannel { \n id\n customFields {\n fulfillmentPaymentPolicy\n }\n }\n }\n";
|
|
61
|
+
export declare const SET_FULFILLMENT_PAYMENT_POLICY_MUTATION = "\n mutation SetFulfillmentPaymentPolicy($input: UpdateChannelInput!) {\n updateChannel(input: $input) {\n ... on Channel { \n id\n customFields {\n fulfillmentPaymentPolicy\n }\n }\n ... on ErrorResult { errorCode message }\n }\n }\n";
|
|
62
|
+
/** A payment method code as the engine compares it (`normalizeMethodCode`). */
|
|
63
|
+
export declare function normalizeFulfillmentPolicyMethodCode(code: string): string;
|
|
64
|
+
export declare function createFulfillmentPaymentPolicyModule(transport: Transport): FulfillmentPaymentPolicyModule;
|