@sixtyninecommerce/admin 0.10.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.
Files changed (302) hide show
  1. package/README.md +5772 -118
  2. package/dist/auth/index.d.ts +7 -1
  3. package/dist/auth/index.js +8 -1
  4. package/dist/catalog/assets.d.ts +120 -8
  5. package/dist/catalog/assets.js +98 -12
  6. package/dist/catalog/collections.d.ts +279 -14
  7. package/dist/catalog/collections.js +246 -15
  8. package/dist/catalog/documents.d.ts +142 -0
  9. package/dist/catalog/documents.js +114 -0
  10. package/dist/catalog/index.d.ts +54 -6
  11. package/dist/catalog/index.js +10 -1
  12. package/dist/catalog/metadata.d.ts +244 -0
  13. package/dist/catalog/metadata.js +220 -0
  14. package/dist/catalog/products.d.ts +159 -19
  15. package/dist/catalog/products.js +88 -13
  16. package/dist/catalog/properties.d.ts +140 -14
  17. package/dist/catalog/properties.js +65 -1
  18. package/dist/catalog/recommendations.d.ts +288 -0
  19. package/dist/catalog/recommendations.js +237 -0
  20. package/dist/catalog/reviews.d.ts +381 -0
  21. package/dist/catalog/reviews.js +325 -0
  22. package/dist/catalog/types.d.ts +17 -1
  23. package/dist/catalog/types.js +10 -0
  24. package/dist/catalog/variant-find.d.ts +136 -0
  25. package/dist/catalog/variant-find.js +239 -0
  26. package/dist/catalog/variants.d.ts +293 -26
  27. package/dist/catalog/variants.js +164 -7
  28. package/dist/catalog/visibility.d.ts +38 -7
  29. package/dist/catalog/visibility.js +19 -1
  30. package/dist/client.d.ts +121 -9
  31. package/dist/client.js +53 -14
  32. package/dist/content/assets.d.ts +285 -0
  33. package/dist/content/assets.js +181 -0
  34. package/dist/content/forms.d.ts +332 -1
  35. package/dist/content/forms.js +140 -4
  36. package/dist/content/items.d.ts +292 -0
  37. package/dist/content/items.js +170 -0
  38. package/dist/content/menus.d.ts +254 -0
  39. package/dist/content/menus.js +70 -0
  40. package/dist/content/strings.d.ts +137 -0
  41. package/dist/content/strings.js +82 -0
  42. package/dist/content/types.d.ts +282 -0
  43. package/dist/content/types.js +100 -0
  44. package/dist/customers/age-verification.d.ts +66 -0
  45. package/dist/customers/age-verification.js +79 -0
  46. package/dist/customers/credit.d.ts +263 -0
  47. package/dist/customers/credit.js +244 -0
  48. package/dist/customers/delivery-preferences.d.ts +120 -0
  49. package/dist/customers/delivery-preferences.js +97 -0
  50. package/dist/customers/delivery-terms.d.ts +44 -0
  51. package/dist/customers/delivery-terms.js +32 -0
  52. package/dist/customers/groups.d.ts +207 -0
  53. package/dist/customers/groups.js +438 -0
  54. package/dist/customers/index.d.ts +462 -37
  55. package/dist/customers/index.js +317 -40
  56. package/dist/customers/invitation-status.d.ts +57 -0
  57. package/dist/customers/invitation-status.js +21 -0
  58. package/dist/customers/notes.d.ts +67 -0
  59. package/dist/customers/notes.js +51 -0
  60. package/dist/customers/notification-recipients.d.ts +97 -0
  61. package/dist/customers/notification-recipients.js +84 -0
  62. package/dist/customers/saved-lists.d.ts +42 -0
  63. package/dist/customers/saved-lists.js +61 -0
  64. package/dist/customers/vat.d.ts +83 -0
  65. package/dist/customers/vat.js +46 -0
  66. package/dist/customers/wire.d.ts +20 -0
  67. package/dist/customers/wire.js +35 -0
  68. package/dist/documents/ar.d.ts +307 -0
  69. package/dist/documents/ar.js +254 -0
  70. package/dist/documents/emails.d.ts +150 -0
  71. package/dist/documents/emails.js +220 -0
  72. package/dist/documents/index.d.ts +744 -21
  73. package/dist/documents/index.js +438 -24
  74. package/dist/documents/pdf.d.ts +2 -0
  75. package/dist/documents/pdf.js +11 -0
  76. package/dist/fulfillments/create.d.ts +167 -0
  77. package/dist/fulfillments/create.js +216 -0
  78. package/dist/fulfillments/index.d.ts +163 -11
  79. package/dist/fulfillments/index.js +135 -6
  80. package/dist/fulfillments/payment-policy.d.ts +64 -0
  81. package/dist/fulfillments/payment-policy.js +140 -0
  82. package/dist/fulfillments/warehouse.d.ts +724 -0
  83. package/dist/fulfillments/warehouse.js +914 -0
  84. package/dist/geo/index.d.ts +119 -0
  85. package/dist/geo/index.js +107 -0
  86. package/dist/index.d.ts +147 -39
  87. package/dist/index.js +51 -12
  88. package/dist/integrations/afas-actions.d.ts +234 -0
  89. package/dist/integrations/afas-actions.js +310 -0
  90. package/dist/integrations/afas-types.d.ts +483 -0
  91. package/dist/integrations/afas-types.js +5 -0
  92. package/dist/integrations/afas.d.ts +234 -0
  93. package/dist/integrations/afas.js +290 -0
  94. package/dist/integrations/connections.d.ts +103 -0
  95. package/dist/integrations/connections.js +55 -0
  96. package/dist/integrations/esp.d.ts +313 -0
  97. package/dist/integrations/esp.js +169 -0
  98. package/dist/integrations/exact-online-types.d.ts +965 -0
  99. package/dist/integrations/exact-online-types.js +6 -0
  100. package/dist/integrations/exact-online.d.ts +784 -0
  101. package/dist/integrations/exact-online.js +1089 -0
  102. package/dist/integrations/index.d.ts +44 -3
  103. package/dist/integrations/index.js +23 -3
  104. package/dist/integrations/pipeline-discoveries.d.ts +65 -0
  105. package/dist/integrations/pipeline-discoveries.js +65 -0
  106. package/dist/integrations/pipeline-keys.d.ts +13 -0
  107. package/dist/integrations/pipeline-keys.js +22 -0
  108. package/dist/integrations/pipeline-runs.d.ts +119 -0
  109. package/dist/integrations/pipeline-runs.js +87 -0
  110. package/dist/integrations/pipelines.d.ts +87 -15
  111. package/dist/integrations/pipelines.js +41 -22
  112. package/dist/integrations/price-import.d.ts +223 -0
  113. package/dist/integrations/price-import.js +124 -0
  114. package/dist/integrations/types.d.ts +367 -3
  115. package/dist/integrations/types.js +4 -3
  116. package/dist/locations/index.d.ts +66 -0
  117. package/dist/locations/index.js +31 -0
  118. package/dist/meta/index.d.ts +172 -0
  119. package/dist/metafields/index.d.ts +8 -1
  120. package/dist/metafields/index.js +10 -0
  121. package/dist/orders/cancel-with-money.d.ts +237 -0
  122. package/dist/orders/cancel-with-money.js +155 -0
  123. package/dist/orders/commit-edit.d.ts +23 -1
  124. package/dist/orders/commit-edit.js +19 -2
  125. package/dist/orders/deposit-policy.d.ts +3 -1
  126. package/dist/orders/drafts.d.ts +800 -0
  127. package/dist/orders/drafts.js +771 -0
  128. package/dist/orders/fields.d.ts +245 -0
  129. package/dist/orders/fields.js +188 -0
  130. package/dist/orders/get.d.ts +29 -19
  131. package/dist/orders/get.js +17 -8
  132. package/dist/orders/index.d.ts +66 -15
  133. package/dist/orders/index.js +25 -2
  134. package/dist/orders/lifecycle.d.ts +128 -0
  135. package/dist/orders/lifecycle.js +158 -0
  136. package/dist/orders/line-price-preview.d.ts +7 -2
  137. package/dist/orders/list.d.ts +243 -0
  138. package/dist/orders/list.js +487 -0
  139. package/dist/orders/manual-discounts.d.ts +13 -5
  140. package/dist/orders/manual-pricing.d.ts +5 -0
  141. package/dist/orders/money-view.d.ts +149 -0
  142. package/dist/orders/money-view.js +99 -0
  143. package/dist/orders/order-entry.d.ts +20 -7
  144. package/dist/orders/plan-edit.d.ts +34 -2
  145. package/dist/orders/plan-edit.js +112 -11
  146. package/dist/orders/settings.d.ts +201 -0
  147. package/dist/orders/settings.js +325 -0
  148. package/dist/orders/status.d.ts +159 -13
  149. package/dist/orders/status.js +21 -0
  150. package/dist/orders/surcharges.d.ts +20 -1
  151. package/dist/orders/surcharges.js +37 -11
  152. package/dist/payments/account-credit.d.ts +46 -0
  153. package/dist/payments/account-credit.js +42 -0
  154. package/dist/payments/ar.d.ts +292 -0
  155. package/dist/payments/ar.js +223 -0
  156. package/dist/payments/connected-account.d.ts +47 -0
  157. package/dist/payments/connected-account.js +61 -0
  158. package/dist/payments/credit-noted-fields.d.ts +19 -0
  159. package/dist/payments/credit-noted-fields.js +69 -0
  160. package/dist/payments/customer-consent.d.ts +30 -0
  161. package/dist/payments/customer-consent.js +49 -0
  162. package/dist/payments/index.d.ts +340 -19
  163. package/dist/payments/index.js +151 -1
  164. package/dist/payments/methods.d.ts +303 -8
  165. package/dist/payments/methods.js +337 -7
  166. package/dist/payments/mollie-methods.d.ts +159 -0
  167. package/dist/payments/mollie-methods.js +141 -0
  168. package/dist/payments/payment-links.d.ts +180 -0
  169. package/dist/payments/payment-links.js +97 -0
  170. package/dist/payments/provider-keys.d.ts +150 -0
  171. package/dist/payments/provider-keys.js +118 -0
  172. package/dist/payments/providers.d.ts +86 -11
  173. package/dist/payments/providers.js +29 -0
  174. package/dist/payments/requests.d.ts +147 -0
  175. package/dist/payments/requests.js +93 -0
  176. package/dist/payments/store-credit-fields.d.ts +15 -0
  177. package/dist/payments/store-credit-fields.js +51 -0
  178. package/dist/pickups/index.d.ts +106 -0
  179. package/dist/pickups/index.js +108 -0
  180. package/dist/platform/channels.d.ts +132 -0
  181. package/dist/platform/channels.js +128 -0
  182. package/dist/platform/email-gateways.d.ts +165 -0
  183. package/dist/platform/email-gateways.js +78 -0
  184. package/dist/platform/geo.d.ts +123 -0
  185. package/dist/platform/geo.js +144 -0
  186. package/dist/platform/tax.d.ts +162 -0
  187. package/dist/platform/tax.js +169 -0
  188. package/dist/pos/anomalies.d.ts +158 -0
  189. package/dist/pos/anomalies.js +64 -0
  190. package/dist/pos/assisted-selling.d.ts +79 -0
  191. package/dist/pos/assisted-selling.js +38 -0
  192. package/dist/pos/exchanges.d.ts +108 -0
  193. package/dist/pos/exchanges.js +22 -0
  194. package/dist/pos/gift-cards.d.ts +135 -0
  195. package/dist/pos/gift-cards.js +47 -0
  196. package/dist/pos/index.d.ts +2370 -58
  197. package/dist/pos/index.js +854 -7
  198. package/dist/pos/no-receipt.d.ts +94 -0
  199. package/dist/pos/no-receipt.js +25 -0
  200. package/dist/pos/offline-sales.d.ts +228 -0
  201. package/dist/pos/offline-sales.js +170 -0
  202. package/dist/pos/pay-by-link.d.ts +143 -0
  203. package/dist/pos/pay-by-link.js +57 -0
  204. package/dist/pos/quick-keys.d.ts +103 -0
  205. package/dist/pos/quick-keys.js +45 -0
  206. package/dist/pos/reports.d.ts +360 -0
  207. package/dist/pos/reports.js +62 -0
  208. package/dist/pos/returns.d.ts +244 -0
  209. package/dist/pos/returns.js +83 -0
  210. package/dist/pricing/bulk-coupons.d.ts +139 -0
  211. package/dist/pricing/bulk-coupons.js +112 -0
  212. package/dist/pricing/index.d.ts +174 -4
  213. package/dist/pricing/index.js +174 -1
  214. package/dist/pricing/price-lists.d.ts +606 -0
  215. package/dist/pricing/price-lists.js +600 -0
  216. package/dist/purchasing/alerts.d.ts +104 -0
  217. package/dist/purchasing/alerts.js +88 -0
  218. package/dist/purchasing/backorders.d.ts +61 -0
  219. package/dist/purchasing/backorders.js +92 -0
  220. package/dist/purchasing/cost.d.ts +110 -0
  221. package/dist/purchasing/cost.js +86 -0
  222. package/dist/purchasing/index.d.ts +54 -0
  223. package/dist/purchasing/index.js +24 -0
  224. package/dist/purchasing/links.d.ts +263 -0
  225. package/dist/purchasing/links.js +192 -0
  226. package/dist/purchasing/purchase-orders.d.ts +551 -0
  227. package/dist/purchasing/purchase-orders.js +491 -0
  228. package/dist/purchasing/receipts.d.ts +396 -0
  229. package/dist/purchasing/receipts.js +287 -0
  230. package/dist/purchasing/replenishment.d.ts +281 -0
  231. package/dist/purchasing/replenishment.js +240 -0
  232. package/dist/purchasing/returns.d.ts +273 -0
  233. package/dist/purchasing/returns.js +229 -0
  234. package/dist/purchasing/settings.d.ts +152 -0
  235. package/dist/purchasing/settings.js +181 -0
  236. package/dist/purchasing/suppliers.d.ts +476 -0
  237. package/dist/purchasing/suppliers.js +438 -0
  238. package/dist/quotes/index.d.ts +518 -0
  239. package/dist/quotes/index.js +439 -0
  240. package/dist/reports/index.d.ts +71 -8
  241. package/dist/reports/index.js +74 -11
  242. package/dist/returns/index.d.ts +675 -21
  243. package/dist/returns/index.js +756 -11
  244. package/dist/search/index.d.ts +76 -1
  245. package/dist/search/index.js +63 -0
  246. package/dist/seo/config.d.ts +128 -0
  247. package/dist/seo/config.js +95 -0
  248. package/dist/seo/redirects.d.ts +125 -0
  249. package/dist/seo/redirects.js +60 -1
  250. package/dist/seo/settings.d.ts +147 -0
  251. package/dist/seo/settings.js +209 -0
  252. package/dist/shipping/definitions.d.ts +29 -0
  253. package/dist/shipping/definitions.js +47 -0
  254. package/dist/shipping/fulfillment-profiles.d.ts +174 -0
  255. package/dist/shipping/fulfillment-profiles.js +104 -0
  256. package/dist/shipping/hendriks.d.ts +97 -0
  257. package/dist/shipping/hendriks.js +50 -0
  258. package/dist/shipping/index.d.ts +54 -0
  259. package/dist/shipping/index.js +27 -0
  260. package/dist/shipping/methods.d.ts +205 -0
  261. package/dist/shipping/methods.js +240 -0
  262. package/dist/shipping/myparcel.d.ts +668 -0
  263. package/dist/shipping/myparcel.js +466 -0
  264. package/dist/shipping/parcel-count-suggestion.d.ts +2 -0
  265. package/dist/shipping/parcel-rule.d.ts +6 -5
  266. package/dist/shipping/parcel-rule.js +21 -23
  267. package/dist/shipping/rate-tables.d.ts +324 -0
  268. package/dist/shipping/rate-tables.js +390 -0
  269. package/dist/shipping/ship-to-countries.d.ts +75 -0
  270. package/dist/shipping/ship-to-countries.js +47 -0
  271. package/dist/stock/by-location.d.ts +86 -0
  272. package/dist/stock/by-location.js +94 -0
  273. package/dist/stock/index.d.ts +18 -4
  274. package/dist/stock/index.js +7 -0
  275. package/dist/stock/locations.d.ts +144 -0
  276. package/dist/stock/locations.js +168 -0
  277. package/dist/stock/settings.d.ts +106 -0
  278. package/dist/stock/settings.js +103 -0
  279. package/dist/stock/transfers.d.ts +386 -52
  280. package/dist/stock/transfers.js +222 -17
  281. package/dist/stored-value/index.d.ts +312 -11
  282. package/dist/stored-value/index.js +223 -4
  283. package/dist/subscriptions/index.d.ts +682 -0
  284. package/dist/subscriptions/index.js +815 -0
  285. package/dist/tax/display-policy.d.ts +105 -0
  286. package/dist/tax/display-policy.js +84 -0
  287. package/dist/tax/index.d.ts +79 -1
  288. package/dist/tax/index.js +52 -0
  289. package/dist/tenant/email-style.d.ts +102 -0
  290. package/dist/tenant/email-style.js +72 -0
  291. package/dist/tenant/email-templates.d.ts +287 -0
  292. package/dist/tenant/email-templates.js +271 -0
  293. package/dist/tenant/notifications.d.ts +23 -17
  294. package/dist/tenant/settings-sources.d.ts +1 -1
  295. package/dist/tenant/time-zone.d.ts +17 -0
  296. package/dist/tenant/time-zone.js +11 -0
  297. package/dist/types.d.ts +50 -3
  298. package/dist/users/index.d.ts +3 -0
  299. package/dist/users/index.js +2 -0
  300. package/dist/users/warehouses.d.ts +50 -0
  301. package/dist/users/warehouses.js +51 -0
  302. 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
- import { type Transport } from '@sixtyninecommerce/core';
1
+ import { type PlatformError, 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:
@@ -180,6 +182,15 @@ export interface FulfillmentCancelPreview {
180
182
  export interface FulfillmentCancelChoice {
181
183
  orderId: string;
182
184
  choice: OrderOverpaymentChoice;
185
+ /**
186
+ * With `ACCOUNT_CREDIT` in a store whose book is store credit (`cancelPreview`'s
187
+ * `accountCredit.book` `STORE_CREDIT`): whether THIS order's customer expressly agreed to store
188
+ * credit instead of a refund to their payment (EU consumer law, CRD art. 13(1)), as staff
189
+ * recorded it. The settlement's move records it on its ledger entries
190
+ * (`StoredValueEntry.customerAgreed`); never a condition, nothing is refused for it. Sent only
191
+ * when given; an engine before the field (incentives sprint 8 PR 6a) refuses a call that gives it.
192
+ */
193
+ customerAgreed?: boolean;
183
194
  }
184
195
  /** What `cancel` sends besides the fulfillment. */
185
196
  export interface CancelFulfillmentInput {
@@ -196,6 +207,11 @@ export interface FulfillmentCancelSettlement {
196
207
  amount: number;
197
208
  currencyCode: string;
198
209
  choice: OrderOverpaymentChoice;
210
+ /**
211
+ * For `ACCOUNT_CREDIT`: the store's customer-credit book the credit lands in (`AR` or
212
+ * `STORE_CREDIT`); null for `REFUND` and from an engine before it.
213
+ */
214
+ book?: string | null;
199
215
  }
200
216
  /** A cancelled fulfillment and what its cancel queued. */
201
217
  export interface CancelledFulfillment {
@@ -258,12 +274,44 @@ export interface OrderFulfillmentCoverage {
258
274
  }
259
275
  /** The longest override reason the engine stores. */
260
276
  export declare const FULFILLMENT_COVERAGE_REASON_MAX_LENGTH = 500;
277
+ /** A fulfillment as `transition` leaves it. */
278
+ export interface FulfillmentStateChange {
279
+ id: string;
280
+ /** The state it is in now (`Shipped`, `Delivered`, …). */
281
+ state: string;
282
+ /** The states it may move to next, as the store's fulfillment process allows. */
283
+ nextStates: string[];
284
+ }
285
+ /**
286
+ * The facts a refused `transition` carries on `PlatformError.extensions`, beside its code:
287
+ * the move that was asked for and the refusing step's own words.
288
+ */
289
+ export interface FulfillmentTransitionRefusalFacts {
290
+ /** Core's code for every refused move: `FULFILLMENT_STATE_TRANSITION_ERROR`. */
291
+ errorCode: string;
292
+ fromState: string;
293
+ toState: string;
294
+ /** The refusing step's text, `[CODE]` prefix included when it had one. */
295
+ transitionError: string;
296
+ }
297
+ /**
298
+ * What re-sending a fulfillment's label to the store's 3PL came to (`resendLabelHandoff`):
299
+ * - `QUEUED`: the email with the label is queued for the 3PL recipient;
300
+ * - `NOT_READY`: the fulfillment has no ready MyParcel label yet, so nothing was sent;
301
+ * - `DISABLED`: the store has the label hand-off notification switched off;
302
+ * - `NO_RECIPIENT`: it is on, but names no recipient;
303
+ * - `UNAVAILABLE`: the platform's email delivery is not installed.
304
+ */
305
+ export type LabelHandoffOutcome = 'QUEUED' | 'DISABLED' | 'NO_RECIPIENT' | 'NOT_READY' | 'UNAVAILABLE';
261
306
  /**
262
307
  * Fulfillment parcels (multi-parcel shipments). Each method is its own request
263
308
  * on purpose: a caller deployed ahead of an engine without these fields sees
264
309
  * only that call refused, never the order or fulfillment read beside it.
310
+ *
311
+ * `create` and `profileChargePreview` (the shared create flow) are declared in
312
+ * `fulfillments/create.ts`.
265
313
  */
266
- export interface FulfillmentsModule {
314
+ export interface FulfillmentsModule extends FulfillmentCreateMethods {
267
315
  /** Every fulfillment of an order, with its parcels. Needs `ReadOrder`. */
268
316
  parcelsOfOrder(orderId: string, options?: Call): Promise<OrderFulfillmentParcels[]>;
269
317
  /**
@@ -281,7 +329,11 @@ export interface FulfillmentsModule {
281
329
  /**
282
330
  * Every parcel's MyParcel label merged by MyParcel into one PDF, fetched live
283
331
  * 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.
332
+ * a slow MyParcel within a budget the default timeout does not cover. A
333
+ * credential restricted to stock locations (an API token or app installation
334
+ * with `locationIds`) prints it only for a fulfillment that ships from one of
335
+ * them; any other, another store's and a missing one included, is
336
+ * `LOCATION_OUT_OF_SCOPE` (422).
285
337
  */
286
338
  myparcelLabel(fulfillmentId: string, format: FulfillmentLabelFormat, options?: Call): Promise<FulfillmentLabel>;
287
339
  /**
@@ -303,8 +355,11 @@ export interface FulfillmentsModule {
303
355
  * `NotFoundError` (`MYPARCEL_ORDER_NOT_FOUND_ERROR`); a cancelled one, or
304
356
  * one holding no shipment, a `BusinessRuleError`
305
357
  * (`MYPARCEL_VALIDATION_ERROR`), as is MyParcel switched off for the store
306
- * (`MYPARCEL_NOT_CONFIGURED_ERROR`). Only queues work, so the default
307
- * timeout applies.
358
+ * (`MYPARCEL_NOT_CONFIGURED_ERROR`). A credential restricted to stock
359
+ * locations (an API token or app installation with `locationIds`) is refused
360
+ * `LOCATION_OUT_OF_SCOPE` (422) first for a fulfillment that does not ship
361
+ * from one of them, another store's and a missing one included. Only queues
362
+ * work, so the default timeout applies.
308
363
  */
309
364
  labelMyparcelParcels(fulfillmentId: string, input?: LabelMyparcelParcelsInput, options?: Call): Promise<MyparcelParcelsLabelRequest>;
310
365
  /**
@@ -336,7 +391,10 @@ export interface FulfillmentsModule {
336
391
  * What cancelling the fulfillment would owe each order's customer back (a delivery charge
337
392
  * they already paid), and which ways of settling it are open. Read now, without a lock; the
338
393
  * cancel judges it again. Needs `UpdateOrder`. Another store's fulfillment is a
339
- * `NotFoundError` (`ENTITY_NOT_FOUND`).
394
+ * `NotFoundError` (`ENTITY_NOT_FOUND`). A credential restricted to stock locations (an API
395
+ * token or app installation with `locationIds`) reads it only for a fulfillment that ships
396
+ * from one of them; any other, another store's and a missing one included, is
397
+ * `LOCATION_OUT_OF_SCOPE` (422).
340
398
  */
341
399
  cancelPreview(fulfillmentId: string, options?: Call): Promise<FulfillmentCancelPreview>;
342
400
  /**
@@ -355,7 +413,10 @@ export interface FulfillmentsModule {
355
413
  * `AR_ACCOUNT_IN_ERP`, `ORDER_OVERPAYMENT_REFUND_UNRECONCILED`, …);
356
414
  * `FULFILLMENT_CANCEL_REFUSED` (→ `BusinessRuleError`) when the fulfillment's process refuses
357
415
  * the cancel (already cancelled, a carrier that cannot let go). Another store's fulfillment is
358
- * a `NotFoundError`.
416
+ * a `NotFoundError`. A credential restricted to stock locations (an API token or app
417
+ * installation with `locationIds`) cancels only a fulfillment that ships from one of them; any
418
+ * other (one with no recorded warehouse, another store's and a missing one included) is
419
+ * `LOCATION_OUT_OF_SCOPE` (422), before the key is claimed.
359
420
  *
360
421
  * `options.idempotencyKey`: a repeat under it answers the first call's result, never a second
361
422
  * cancel; the same key with a different choice is `IDEMPOTENCY_CONFLICT`.
@@ -389,14 +450,91 @@ export interface FulfillmentsModule {
389
450
  allowUncovered(orderId: string, reason: string, options?: Call & {
390
451
  idempotencyKey?: string;
391
452
  }): Promise<FulfillmentCoverageOverride>;
453
+ /**
454
+ * Move a fulfillment to one of its `nextStates` (the order page's "Mark as Shipped" and "Mark as
455
+ * Delivered"). Needs `UpdateOrder`. The move runs the store's own fulfillment process, so the
456
+ * order's state follows and whatever the store does on a shipped or delivered fulfillment (its
457
+ * events, its notifications: the customer gets the shipped e-mail) happens; the process has no
458
+ * way back to an earlier state. Shipping an order's last open lines moves the ORDER to
459
+ * `Shipped`, which on a store that pushes orders to its ERP when they ship (Exact Online's
460
+ * "fulfilled" auto-push) sends the order there, where it cannot be taken back.
461
+ *
462
+ * A move into `Cancelled` is a cancel: it ends the fulfillment for good and has its carrier let
463
+ * go of the shipment, and the delivery charge it added comes off what each of its orders bills.
464
+ * It is refused, before the carrier is told, while the customer has PAID such a charge
465
+ * (`FULFILLMENT_CANCEL_OVERPAYMENT_CHOICE_REQUIRED`): {@link FulfillmentsModule.cancel} takes the
466
+ * choice of how that money goes back. An unpaid charge simply leaves the bill.
467
+ *
468
+ * Tagged `money`, its strongest effect: a cancel changes what a placed order bills.
469
+ *
470
+ * Refused, with nothing moved:
471
+ * - a step of the store's process that refuses the move answers ITS OWN code, which the engine
472
+ * prefixes to its text as `[CODE]`, with the text after it as the message, classed as the
473
+ * warehouse API classes the same code: `ORDER_NOT_COVERED` (the store requires full payment
474
+ * and an order is not paid in full: `coverage` reads what it owes, `allowUncovered` records
475
+ * an override), `ORDER_ON_HOLD` (release the order's fulfilment hold first),
476
+ * `BOOKING_REQUIRED`, `PARCELS_REQUIRED`, `CONSOLIDATION_ADDRESS_DIVERGED`, …;
477
+ * - any other refusal of the process, an edge it does not have included, is
478
+ * `FULFILLMENT_STATE_TRANSITION_ERROR` (`BusinessRuleError`, 422) with the refusing step's
479
+ * text as the message;
480
+ * - both carry {@link FulfillmentTransitionRefusalFacts} on `extensions`;
481
+ * - another store's or a missing fulfillment is `ENTITY_NOT_FOUND` (`NotFoundError`); a
482
+ * credential restricted to stock locations (an API token or app installation with
483
+ * `locationIds`) moves only a fulfillment that ships from one of them
484
+ * (`LOCATION_OUT_OF_SCOPE`, 422);
485
+ * - a blank id or state is refused before any request (`FULFILLMENT_TRANSITION_INVALID`,
486
+ * `ValidationError`).
487
+ *
488
+ * Takes no idempotency key: repeating a move that went through asks for an edge the process no
489
+ * longer has, and is refused as above, never moved twice.
490
+ *
491
+ * @effect money
492
+ * @permission UpdateOrder
493
+ */
494
+ transition(fulfillmentId: string, state: string, options?: Call): Promise<FulfillmentStateChange>;
495
+ /**
496
+ * E-mail the fulfillment's ready MyParcel label again to the store's 3PL recipient (the
497
+ * `fulfillment_label_handoff` notification), whatever the store's automatic hand-off setting.
498
+ * Answers what came of it ({@link LabelHandoffOutcome}): only `QUEUED` sent anything; the
499
+ * others say why nothing was sent. Every outcome leaves an audit row. Needs `UpdateOrder`.
500
+ *
501
+ * Takes no idempotency key: a repeat sends the e-mail again.
502
+ *
503
+ * Refused: another store's or a missing fulfillment is `ENTITY_NOT_FOUND` (`NotFoundError`); a
504
+ * store without the MyParcel plugin `PLUGIN_ACCESS_DENIED` (`PermissionDeniedError`, 403) for
505
+ * an ordinary caller; a credential restricted to other stock locations `LOCATION_OUT_OF_SCOPE`
506
+ * (422). A blank id is refused before any request (`MYPARCEL_LABEL_INPUT_INVALID`,
507
+ * `ValidationError`).
508
+ *
509
+ * @effect irreversible
510
+ * @permission UpdateOrder
511
+ */
512
+ resendLabelHandoff(fulfillmentId: string, options?: Call): Promise<LabelHandoffOutcome>;
513
+ /**
514
+ * The store's fulfilment payment policy (`fulfillmentPaymentPolicy`): whether a fulfilment of
515
+ * an order not paid in full is refused `ORDER_NOT_COVERED`, and which payment methods are
516
+ * exempt. Read as the engine applies it; written whole (`UpdateChannel`).
517
+ */
518
+ paymentPolicy: FulfillmentPaymentPolicyModule;
519
+ /**
520
+ * The pick/pack/ship workspace: the warehouse API (price-free, location-scoped), the pick list
521
+ * and consolidation. See `fulfillments/warehouse.ts`.
522
+ */
523
+ warehouse: WarehouseFulfillmentsModule;
392
524
  }
393
525
  /**
394
526
  * Its own document, like every call here: on an engine without the query this call alone fails
395
527
  * validation.
396
528
  */
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";
529
+ 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
530
  /** `$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";
531
+ 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";
532
+ /**
533
+ * The refusal member is selected for its facts: `message` is core's per-code template ("Cannot
534
+ * transition Fulfillment from X to Y"), and the refusing step's reason is `transitionError` alone.
535
+ */
536
+ export declare const TRANSITION_FULFILLMENT_MUTATION = "\n mutation TransitionFulfillment($id: ID!, $state: String!) {\n transitionFulfillmentToState(id: $id, state: $state) {\n __typename\n ... on Fulfillment {\n id\n state\n nextStates\n }\n ... on FulfillmentStateTransitionError {\n errorCode\n message\n fromState\n toState\n transitionError\n }\n }\n }\n";
537
+ export declare const RESEND_FULFILLMENT_LABEL_HANDOFF_MUTATION = "\n mutation ResendFulfillmentLabelHandoff($fulfillmentId: ID!) {\n resendFulfillmentLabelHandoff(fulfillmentId: $fulfillmentId)\n }\n";
400
538
  /** Its own document: on an engine without the query this call alone fails validation. */
401
539
  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
540
  /** `$idempotencyKey` is optional and left out of the variables when not given. */
@@ -434,4 +572,18 @@ export declare const MYPARCEL_LABEL_TIMEOUT_MS = 55000;
434
572
  export declare function toUnretractedShipments(raw: readonly MyparcelUnretractedShipment[] | null | undefined): MyparcelUnretractedShipment[];
435
573
  /** The bytes of a base64 label document (`atob`: no Node `Buffer`, it ships to browsers). */
436
574
  export declare function decodeBase64(base64: string): Uint8Array;
575
+ interface RawTransitionRefusal {
576
+ errorCode: string;
577
+ message: string;
578
+ fromState?: string | null;
579
+ toState?: string | null;
580
+ transitionError?: string | null;
581
+ }
582
+ /**
583
+ * A refused move as the coded error it is. Core carries a step's refusal only as text, so the
584
+ * step's own code is read off its `[CODE]` prefix and classed through the GraphQL dialect, as the
585
+ * warehouse API's refusals of the same codes are (`warehouseRefusalError`): one code, one class,
586
+ * whichever door answered it.
587
+ */
588
+ export declare function fulfillmentTransitionRefusal(result: RawTransitionRefusal): PlatformError;
437
589
  export declare function createFulfillmentsModule(transport: Transport): FulfillmentsModule;