@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.
Files changed (247) hide show
  1. package/README.md +4347 -171
  2. package/dist/catalog/assets.d.ts +120 -8
  3. package/dist/catalog/assets.js +98 -12
  4. package/dist/catalog/collections.d.ts +279 -14
  5. package/dist/catalog/collections.js +246 -15
  6. package/dist/catalog/index.d.ts +9 -2
  7. package/dist/catalog/index.js +4 -0
  8. package/dist/catalog/products.d.ts +159 -19
  9. package/dist/catalog/products.js +88 -13
  10. package/dist/catalog/properties.d.ts +102 -14
  11. package/dist/catalog/properties.js +13 -0
  12. package/dist/catalog/recommendations.d.ts +261 -0
  13. package/dist/catalog/recommendations.js +145 -0
  14. package/dist/catalog/reviews.d.ts +368 -0
  15. package/dist/catalog/reviews.js +318 -0
  16. package/dist/catalog/types.d.ts +17 -1
  17. package/dist/catalog/types.js +10 -0
  18. package/dist/catalog/variant-find.d.ts +136 -0
  19. package/dist/catalog/variant-find.js +239 -0
  20. package/dist/catalog/variants.d.ts +233 -26
  21. package/dist/catalog/variants.js +125 -7
  22. package/dist/catalog/visibility.d.ts +20 -5
  23. package/dist/catalog/visibility.js +19 -1
  24. package/dist/client.d.ts +95 -9
  25. package/dist/client.js +35 -7
  26. package/dist/content/assets.d.ts +286 -0
  27. package/dist/content/assets.js +181 -0
  28. package/dist/content/items.d.ts +269 -0
  29. package/dist/content/items.js +170 -0
  30. package/dist/content/types.d.ts +282 -0
  31. package/dist/content/types.js +100 -0
  32. package/dist/customers/age-verification.d.ts +66 -0
  33. package/dist/customers/age-verification.js +79 -0
  34. package/dist/customers/credit.d.ts +263 -0
  35. package/dist/customers/credit.js +244 -0
  36. package/dist/customers/delivery-preferences.d.ts +120 -0
  37. package/dist/customers/delivery-preferences.js +97 -0
  38. package/dist/customers/groups.d.ts +207 -0
  39. package/dist/customers/groups.js +438 -0
  40. package/dist/customers/index.d.ts +447 -37
  41. package/dist/customers/index.js +313 -40
  42. package/dist/customers/invitation-status.d.ts +57 -0
  43. package/dist/customers/invitation-status.js +21 -0
  44. package/dist/customers/notes.d.ts +67 -0
  45. package/dist/customers/notes.js +51 -0
  46. package/dist/customers/notification-recipients.d.ts +97 -0
  47. package/dist/customers/notification-recipients.js +84 -0
  48. package/dist/customers/saved-lists.d.ts +42 -0
  49. package/dist/customers/saved-lists.js +61 -0
  50. package/dist/customers/vat.d.ts +83 -0
  51. package/dist/customers/vat.js +46 -0
  52. package/dist/customers/wire.d.ts +20 -0
  53. package/dist/customers/wire.js +35 -0
  54. package/dist/documents/ar.d.ts +307 -0
  55. package/dist/documents/ar.js +254 -0
  56. package/dist/documents/index.d.ts +731 -21
  57. package/dist/documents/index.js +436 -24
  58. package/dist/documents/pdf.d.ts +2 -0
  59. package/dist/documents/pdf.js +11 -0
  60. package/dist/fulfillments/create.d.ts +167 -0
  61. package/dist/fulfillments/create.js +216 -0
  62. package/dist/fulfillments/index.d.ts +44 -10
  63. package/dist/fulfillments/index.js +11 -2
  64. package/dist/fulfillments/payment-policy.d.ts +64 -0
  65. package/dist/fulfillments/payment-policy.js +140 -0
  66. package/dist/fulfillments/warehouse.d.ts +724 -0
  67. package/dist/fulfillments/warehouse.js +914 -0
  68. package/dist/geo/index.d.ts +119 -0
  69. package/dist/geo/index.js +107 -0
  70. package/dist/index.d.ts +123 -35
  71. package/dist/index.js +45 -12
  72. package/dist/integrations/afas-actions.d.ts +234 -0
  73. package/dist/integrations/afas-actions.js +310 -0
  74. package/dist/integrations/afas-types.d.ts +483 -0
  75. package/dist/integrations/afas-types.js +5 -0
  76. package/dist/integrations/afas.d.ts +234 -0
  77. package/dist/integrations/afas.js +290 -0
  78. package/dist/integrations/connections.d.ts +103 -0
  79. package/dist/integrations/connections.js +55 -0
  80. package/dist/integrations/esp.d.ts +313 -0
  81. package/dist/integrations/esp.js +169 -0
  82. package/dist/integrations/exact-online-types.d.ts +423 -0
  83. package/dist/integrations/exact-online-types.js +6 -0
  84. package/dist/integrations/exact-online.d.ts +248 -0
  85. package/dist/integrations/exact-online.js +376 -0
  86. package/dist/integrations/index.d.ts +29 -3
  87. package/dist/integrations/index.js +18 -3
  88. package/dist/integrations/pipeline-keys.d.ts +13 -0
  89. package/dist/integrations/pipeline-keys.js +22 -0
  90. package/dist/integrations/pipelines.d.ts +15 -14
  91. package/dist/integrations/pipelines.js +6 -22
  92. package/dist/integrations/types.d.ts +146 -0
  93. package/dist/locations/index.d.ts +66 -0
  94. package/dist/locations/index.js +31 -0
  95. package/dist/meta/index.d.ts +162 -0
  96. package/dist/orders/cancel-with-money.d.ts +227 -0
  97. package/dist/orders/cancel-with-money.js +153 -0
  98. package/dist/orders/commit-edit.d.ts +7 -1
  99. package/dist/orders/commit-edit.js +5 -1
  100. package/dist/orders/drafts.d.ts +800 -0
  101. package/dist/orders/drafts.js +771 -0
  102. package/dist/orders/fields.d.ts +245 -0
  103. package/dist/orders/fields.js +188 -0
  104. package/dist/orders/get.d.ts +29 -19
  105. package/dist/orders/get.js +17 -8
  106. package/dist/orders/index.d.ts +49 -9
  107. package/dist/orders/index.js +22 -2
  108. package/dist/orders/lifecycle.d.ts +128 -0
  109. package/dist/orders/lifecycle.js +158 -0
  110. package/dist/orders/line-price-preview.d.ts +7 -2
  111. package/dist/orders/list.d.ts +222 -0
  112. package/dist/orders/list.js +377 -0
  113. package/dist/orders/manual-pricing.d.ts +5 -0
  114. package/dist/orders/order-entry.d.ts +20 -7
  115. package/dist/orders/plan-edit.d.ts +1 -1
  116. package/dist/orders/plan-edit.js +4 -1
  117. package/dist/orders/settings.d.ts +201 -0
  118. package/dist/orders/settings.js +325 -0
  119. package/dist/orders/status.d.ts +159 -13
  120. package/dist/orders/status.js +21 -0
  121. package/dist/orders/surcharges.d.ts +8 -1
  122. package/dist/orders/surcharges.js +3 -2
  123. package/dist/payments/account-credit.d.ts +30 -0
  124. package/dist/payments/account-credit.js +19 -0
  125. package/dist/payments/ar.d.ts +292 -0
  126. package/dist/payments/ar.js +223 -0
  127. package/dist/payments/connected-account.d.ts +47 -0
  128. package/dist/payments/connected-account.js +61 -0
  129. package/dist/payments/credit-noted-fields.d.ts +12 -0
  130. package/dist/payments/credit-noted-fields.js +37 -0
  131. package/dist/payments/index.d.ts +257 -11
  132. package/dist/payments/index.js +131 -1
  133. package/dist/payments/methods.d.ts +303 -8
  134. package/dist/payments/methods.js +337 -7
  135. package/dist/payments/mollie-methods.d.ts +159 -0
  136. package/dist/payments/mollie-methods.js +141 -0
  137. package/dist/payments/payment-links.d.ts +180 -0
  138. package/dist/payments/payment-links.js +97 -0
  139. package/dist/payments/provider-keys.d.ts +150 -0
  140. package/dist/payments/provider-keys.js +118 -0
  141. package/dist/payments/providers.d.ts +86 -11
  142. package/dist/payments/providers.js +29 -0
  143. package/dist/payments/requests.d.ts +144 -0
  144. package/dist/payments/requests.js +92 -0
  145. package/dist/payments/store-credit-fields.d.ts +15 -0
  146. package/dist/payments/store-credit-fields.js +51 -0
  147. package/dist/pickups/index.d.ts +104 -0
  148. package/dist/pickups/index.js +105 -0
  149. package/dist/platform/channels.d.ts +132 -0
  150. package/dist/platform/channels.js +128 -0
  151. package/dist/platform/geo.d.ts +123 -0
  152. package/dist/platform/geo.js +144 -0
  153. package/dist/platform/tax.d.ts +154 -0
  154. package/dist/platform/tax.js +139 -0
  155. package/dist/pos/anomalies.d.ts +158 -0
  156. package/dist/pos/anomalies.js +64 -0
  157. package/dist/pos/assisted-selling.d.ts +79 -0
  158. package/dist/pos/assisted-selling.js +38 -0
  159. package/dist/pos/exchanges.d.ts +103 -0
  160. package/dist/pos/exchanges.js +22 -0
  161. package/dist/pos/gift-cards.d.ts +135 -0
  162. package/dist/pos/gift-cards.js +47 -0
  163. package/dist/pos/index.d.ts +2125 -32
  164. package/dist/pos/index.js +742 -4
  165. package/dist/pos/no-receipt.d.ts +94 -0
  166. package/dist/pos/no-receipt.js +25 -0
  167. package/dist/pos/offline-sales.d.ts +187 -0
  168. package/dist/pos/offline-sales.js +116 -0
  169. package/dist/pos/pay-by-link.d.ts +143 -0
  170. package/dist/pos/pay-by-link.js +57 -0
  171. package/dist/pos/quick-keys.d.ts +103 -0
  172. package/dist/pos/quick-keys.js +45 -0
  173. package/dist/pos/reports.d.ts +360 -0
  174. package/dist/pos/reports.js +62 -0
  175. package/dist/pos/returns.d.ts +217 -0
  176. package/dist/pos/returns.js +53 -0
  177. package/dist/pricing/bulk-coupons.d.ts +139 -0
  178. package/dist/pricing/bulk-coupons.js +112 -0
  179. package/dist/pricing/index.d.ts +174 -4
  180. package/dist/pricing/index.js +174 -1
  181. package/dist/pricing/price-lists.d.ts +604 -0
  182. package/dist/pricing/price-lists.js +600 -0
  183. package/dist/purchasing/alerts.d.ts +89 -0
  184. package/dist/purchasing/alerts.js +62 -0
  185. package/dist/purchasing/cost.d.ts +110 -0
  186. package/dist/purchasing/cost.js +86 -0
  187. package/dist/purchasing/index.d.ts +36 -0
  188. package/dist/purchasing/index.js +16 -0
  189. package/dist/purchasing/purchase-orders.d.ts +548 -0
  190. package/dist/purchasing/purchase-orders.js +491 -0
  191. package/dist/purchasing/replenishment.d.ts +281 -0
  192. package/dist/purchasing/replenishment.js +240 -0
  193. package/dist/purchasing/settings.d.ts +152 -0
  194. package/dist/purchasing/settings.js +181 -0
  195. package/dist/purchasing/suppliers.d.ts +476 -0
  196. package/dist/purchasing/suppliers.js +438 -0
  197. package/dist/quotes/index.d.ts +501 -0
  198. package/dist/quotes/index.js +439 -0
  199. package/dist/reports/index.d.ts +71 -8
  200. package/dist/reports/index.js +74 -11
  201. package/dist/returns/index.d.ts +614 -21
  202. package/dist/returns/index.js +695 -11
  203. package/dist/search/index.d.ts +76 -1
  204. package/dist/search/index.js +63 -0
  205. package/dist/shipping/definitions.d.ts +29 -0
  206. package/dist/shipping/definitions.js +47 -0
  207. package/dist/shipping/fulfillment-profiles.d.ts +174 -0
  208. package/dist/shipping/fulfillment-profiles.js +104 -0
  209. package/dist/shipping/hendriks.d.ts +97 -0
  210. package/dist/shipping/hendriks.js +50 -0
  211. package/dist/shipping/index.d.ts +48 -0
  212. package/dist/shipping/index.js +25 -0
  213. package/dist/shipping/methods.d.ts +198 -0
  214. package/dist/shipping/methods.js +240 -0
  215. package/dist/shipping/myparcel.d.ts +254 -0
  216. package/dist/shipping/myparcel.js +178 -0
  217. package/dist/shipping/parcel-count-suggestion.d.ts +2 -0
  218. package/dist/shipping/parcel-rule.d.ts +6 -5
  219. package/dist/shipping/parcel-rule.js +21 -23
  220. package/dist/shipping/rate-tables.d.ts +179 -0
  221. package/dist/shipping/rate-tables.js +184 -0
  222. package/dist/stock/index.d.ts +10 -3
  223. package/dist/stock/index.js +3 -0
  224. package/dist/stock/locations.d.ts +144 -0
  225. package/dist/stock/locations.js +168 -0
  226. package/dist/stock/transfers.d.ts +216 -43
  227. package/dist/stock/transfers.js +140 -13
  228. package/dist/stored-value/index.d.ts +214 -4
  229. package/dist/stored-value/index.js +152 -0
  230. package/dist/subscriptions/index.d.ts +682 -0
  231. package/dist/subscriptions/index.js +815 -0
  232. package/dist/tax/index.d.ts +76 -1
  233. package/dist/tax/index.js +50 -0
  234. package/dist/tenant/email-style.d.ts +102 -0
  235. package/dist/tenant/email-style.js +72 -0
  236. package/dist/tenant/email-templates.d.ts +66 -0
  237. package/dist/tenant/email-templates.js +54 -0
  238. package/dist/tenant/notifications.d.ts +23 -17
  239. package/dist/tenant/settings-sources.d.ts +1 -1
  240. package/dist/tenant/time-zone.d.ts +17 -0
  241. package/dist/tenant/time-zone.js +11 -0
  242. package/dist/types.d.ts +50 -3
  243. package/dist/users/index.d.ts +3 -0
  244. package/dist/users/index.js +2 -0
  245. package/dist/users/warehouses.d.ts +50 -0
  246. package/dist/users/warehouses.js +51 -0
  247. package/package.json +2 -2
@@ -0,0 +1,222 @@
1
+ import { type Page, type Transport } from '@sixtyninecommerce/core';
2
+ import type { Call } from '../types.js';
3
+ /**
4
+ * One order as a list shows it (`admin.orders.list`, `admin.customers.orders`): the totals, the
5
+ * status dimensions, who it is for, and the three company sources a display name reads
6
+ * (`billingAddress.company`, `shippingAddress.company`, the customer's address book).
7
+ *
8
+ * Money is an integer in minor units of the order's `currencyCode`.
9
+ */
10
+ export interface OrderSummary {
11
+ id: string;
12
+ code: string;
13
+ state: string;
14
+ total: number;
15
+ totalWithTax: number;
16
+ currencyCode: string;
17
+ /** ISO-8601; null for an order not placed yet (a cart, a draft). */
18
+ orderPlacedAt: string | null;
19
+ customer: {
20
+ id: string;
21
+ firstName: string;
22
+ lastName: string;
23
+ emailAddress: string;
24
+ addresses: Array<{
25
+ company: string | null;
26
+ defaultBillingAddress: boolean | null;
27
+ defaultShippingAddress: boolean | null;
28
+ }> | null;
29
+ } | null;
30
+ billingAddress: {
31
+ company: string | null;
32
+ } | null;
33
+ shippingAddress: {
34
+ company: string | null;
35
+ } | null;
36
+ lines: Array<{
37
+ quantity: number;
38
+ }>;
39
+ payments: Array<{
40
+ id: string;
41
+ state: string;
42
+ }> | null;
43
+ customFields: {
44
+ externalOrderId: string | null;
45
+ externalPlatform: string | null;
46
+ isGift: boolean | null;
47
+ /** The status dimensions; null until the engine has set them on an older order. */
48
+ paymentStatus: string | null;
49
+ fulfillmentStatus: string | null;
50
+ returnStatus: string | null;
51
+ lifecycleStatus: string | null;
52
+ statusOverrides: {
53
+ fulfillmentHoldReason: string | null;
54
+ } | null;
55
+ } | null;
56
+ /**
57
+ * Set when the caller's customer-data level blanked part of the row (an app installation below
58
+ * `customerData: full`): read an empty name or company as "not granted", never as "none".
59
+ */
60
+ redacted?: true;
61
+ }
62
+ /**
63
+ * Which orders a list holds. Every key narrows it; several keys are combined with AND. An empty
64
+ * list (`state: []`) matches nothing.
65
+ */
66
+ export interface OrderListFilter {
67
+ ids?: readonly string[];
68
+ /** Any of these engine states (`PaymentSettled`, `Draft`, …). */
69
+ state?: readonly string[];
70
+ /**
71
+ * The order code. `startsWith` and `contains` ignore case; `contains` treats `%` and `_` as
72
+ * wildcards.
73
+ */
74
+ code?: {
75
+ eq?: string;
76
+ contains?: string;
77
+ startsWith?: string;
78
+ };
79
+ /**
80
+ * When the order was placed. `isNull: true` keeps the orders never placed (carts, drafts),
81
+ * `false` the placed ones. `before`/`after` are ISO-8601 instants WITH an offset or `Z`, both
82
+ * exclusive; an order never placed matches neither.
83
+ */
84
+ orderPlacedAt?: {
85
+ isNull?: boolean;
86
+ before?: string;
87
+ after?: string;
88
+ };
89
+ /** True for a cart its shopper still has open; a draft, placed or cancelled order is not. */
90
+ active?: boolean;
91
+ currencyCode?: string;
92
+ /**
93
+ * The platform an imported order came from (`woocommerce`, …); `null` keeps only the orders
94
+ * that were not imported.
95
+ */
96
+ externalPlatform?: string | null;
97
+ /** Any of these payment dimensions (`paid`, `partially_paid`, …). */
98
+ paymentStatus?: readonly string[];
99
+ /** Any of these lifecycles (`open`, `completed`, …). */
100
+ lifecycleStatus?: readonly string[];
101
+ /** Part of the customer's last name, ignoring case. */
102
+ customerLastName?: string;
103
+ }
104
+ /** The fields an order list sorts on. */
105
+ export type OrderListSortField = 'orderPlacedAt' | 'createdAt' | 'updatedAt' | 'code' | 'state' | 'total' | 'totalWithTax' | 'customerLastName';
106
+ export declare const ORDER_LIST_SORT_FIELDS: readonly OrderListSortField[];
107
+ export interface OrderListSort {
108
+ field: OrderListSortField;
109
+ direction: 'ASC' | 'DESC';
110
+ }
111
+ export interface OrderListOptions extends Call {
112
+ filter?: OrderListFilter;
113
+ sort?: OrderListSort;
114
+ /** Rows to skip, 0 when omitted. */
115
+ skip?: number;
116
+ /** Rows per page, 1 to {@link MAX_ORDER_LIST_TAKE}; 25 when omitted. */
117
+ take?: number;
118
+ }
119
+ /** The engine's `adminListQueryLimit`: a larger page is refused. */
120
+ export declare const MAX_ORDER_LIST_TAKE = 1000;
121
+ export declare const ORDER_SUMMARY_FIELDS = "\n id\n code\n state\n total\n totalWithTax\n currencyCode\n orderPlacedAt\n customer {\n id\n firstName\n lastName\n emailAddress\n addresses {\n company\n defaultBillingAddress\n defaultShippingAddress\n }\n }\n billingAddress {\n company\n }\n shippingAddress {\n company\n }\n lines {\n quantity\n }\n payments {\n id\n state\n }\n customFields {\n externalOrderId\n externalPlatform\n isGift\n paymentStatus\n fulfillmentStatus\n returnStatus\n lifecycleStatus\n statusOverrides {\n fulfillmentHoldReason\n }\n }\n";
122
+ export declare const ORDERS_LIST_QUERY = "\n query OrdersList($options: OrderListOptions) {\n orders(options: $options) {\n items {\n \n id\n code\n state\n total\n totalWithTax\n currencyCode\n orderPlacedAt\n customer {\n id\n firstName\n lastName\n emailAddress\n addresses {\n company\n defaultBillingAddress\n defaultShippingAddress\n }\n }\n billingAddress {\n company\n }\n shippingAddress {\n company\n }\n lines {\n quantity\n }\n payments {\n id\n state\n }\n customFields {\n externalOrderId\n externalPlatform\n isGift\n paymentStatus\n fulfillmentStatus\n returnStatus\n lifecycleStatus\n statusOverrides {\n fulfillmentHoldReason\n }\n }\n\n }\n totalItems\n }\n }\n";
123
+ export declare const ORDERS_COUNT_QUERY = "\n query OrdersCount($options: OrderListOptions) {\n orders(options: $options) {\n totalItems\n }\n }\n";
124
+ export declare const CUSTOMER_ORDERS_QUERY = "\n query CustomerOrders($id: ID!, $options: OrderListOptions) {\n customer(id: $id) {\n id\n orders(options: $options) {\n items {\n \n id\n code\n state\n total\n totalWithTax\n currencyCode\n orderPlacedAt\n customer {\n id\n firstName\n lastName\n emailAddress\n addresses {\n company\n defaultBillingAddress\n defaultShippingAddress\n }\n }\n billingAddress {\n company\n }\n shippingAddress {\n company\n }\n lines {\n quantity\n }\n payments {\n id\n state\n }\n customFields {\n externalOrderId\n externalPlatform\n isGift\n paymentStatus\n fulfillmentStatus\n returnStatus\n lifecycleStatus\n statusOverrides {\n fulfillmentHoldReason\n }\n }\n\n }\n totalItems\n }\n }\n }\n";
125
+ /** The Admin API's `OrderFilterParameter` for a typed filter; unknown keys are refused. */
126
+ export declare function toOrderFilterParameter(filter: OrderListFilter | undefined): Record<string, unknown>;
127
+ export interface OrderListModule {
128
+ /**
129
+ * One page of the store's orders, with `total` the exact count of every order the filter
130
+ * matches. Needs `ReadOrder`.
131
+ *
132
+ * - **Carts are orders too.** A cart a shopper never checked out, and a draft staff never placed,
133
+ * are listed unless the filter leaves them out (`orderPlacedAt: { isNull: false }`).
134
+ * - **Sorting by `orderPlacedAt` descending puts the never-placed rows FIRST** (the database
135
+ * sorts an empty date above every date). To show the newest placed order first, list the
136
+ * placed orders, then continue with `orderPlacedAt: { isNull: true }` sorted by `createdAt`.
137
+ * - `page.next()` asks for the following page with the same filter and sort; `skip` starts
138
+ * somewhere else.
139
+ *
140
+ * Refused before anything is asked: `INVALID_ORDER_LIST_OPTIONS` (`ValidationError`, 400) for a
141
+ * filter key or operator it does not list, a date without an offset, a sort field it does not
142
+ * list, or `take`/`skip` out of range.
143
+ *
144
+ * @effect read
145
+ */
146
+ list(options?: OrderListOptions): Promise<Page<OrderSummary>>;
147
+ /**
148
+ * How many of the store's orders the filter matches, without reading any. Carts count unless
149
+ * the filter leaves them out. Needs `ReadOrder`. Refused like {@link list}.
150
+ *
151
+ * @effect read
152
+ */
153
+ count(filter?: OrderListFilter, options?: Call): Promise<number>;
154
+ }
155
+ export declare function createOrderListModule(transport: Transport): OrderListModule;
156
+ /**
157
+ * One customer's orders, as `customers.orders` reads them: the same rows, filter, sort and paging
158
+ * as `orders.list`, or `null` when the store holds no customer with that id.
159
+ */
160
+ export declare function listCustomerOrders(transport: Transport, customerId: string, options: OrderListOptions | undefined): Promise<Page<OrderSummary> | null>;
161
+ /** The most orders `customers.recentOrders` answers. */
162
+ export declare const MAX_RECENT_ORDERS = 20;
163
+ /**
164
+ * One line of a customer's earlier order: what was bought, and how many. The members are named
165
+ * as on every other order line of this SDK (`orders.get`, `orders.drafts`).
166
+ */
167
+ export interface RecentOrderLine {
168
+ id: string;
169
+ /** 0 for a line that was cancelled on the order. */
170
+ quantity: number;
171
+ /**
172
+ * Whether the line is priced gross. `customFields.unitPriceOverride` is stated in this basis,
173
+ * which follows the customer's tax zone, not the store's setting: a store that prices gross
174
+ * still holds net lines for a customer in a tax-exclusive country.
175
+ */
176
+ listPriceIncludesTax: boolean;
177
+ /**
178
+ * The variant as it is NOW (its name and SKU may have changed since the order). A line an
179
+ * external system priced sits on the platform's hidden carrier variant: read it through
180
+ * `catalog.variants.find({ ids })`, which leaves such a variant out, before offering it.
181
+ */
182
+ productVariant: {
183
+ id: string;
184
+ name: string;
185
+ sku: string;
186
+ product: {
187
+ id: string;
188
+ name: string;
189
+ };
190
+ };
191
+ customFields: {
192
+ /**
193
+ * The unit price the line states in place of the catalogue's, in minor units of the
194
+ * order's currency and in the line's basis (`listPriceIncludesTax`); null for a line the
195
+ * catalogue priced.
196
+ */
197
+ unitPriceOverride: number | null;
198
+ /** True when that price arrived with the line (an import, a quote, a till price). */
199
+ unitPriceImported: boolean | null;
200
+ } | null;
201
+ }
202
+ /** A placed order of a customer, with its lines. */
203
+ export interface RecentOrder {
204
+ id: string;
205
+ code: string;
206
+ /** ISO-8601. */
207
+ orderPlacedAt: string;
208
+ /** Minor units of `currencyCode`. */
209
+ totalWithTax: number;
210
+ currencyCode: string;
211
+ lines: RecentOrderLine[];
212
+ }
213
+ export interface RecentOrdersOptions extends Call {
214
+ /** How many orders, 1 to {@link MAX_RECENT_ORDERS}; 5 when omitted. */
215
+ take?: number;
216
+ }
217
+ export declare const CUSTOMER_RECENT_ORDERS_QUERY = "\n query CustomerRecentOrders($id: ID!, $options: OrderListOptions) {\n customer(id: $id) {\n id\n orders(options: $options) {\n items {\n id\n code\n orderPlacedAt\n totalWithTax\n currencyCode\n lines {\n id\n quantity\n listPriceIncludesTax\n productVariant {\n id\n name\n sku\n product {\n id\n name\n }\n }\n customFields {\n unitPriceOverride\n unitPriceImported\n }\n }\n }\n totalItems\n }\n }\n }\n";
218
+ /**
219
+ * A customer's last placed orders, newest first, each with its lines, as `customers.recentOrders`
220
+ * reads them; `null` when the store holds no customer with that id.
221
+ */
222
+ export declare function listCustomerRecentOrders(transport: Transport, customerId: string, options: RecentOrdersOptions | undefined): Promise<RecentOrder[] | null>;
@@ -0,0 +1,377 @@
1
+ import { pageFromVendureList, readOffsetCursor, UpstreamError, ValidationError, } from '@sixtyninecommerce/core';
2
+ import { redactionWatcher } from '../customer-data-redaction.js';
3
+ export const ORDER_LIST_SORT_FIELDS = [
4
+ 'orderPlacedAt',
5
+ 'createdAt',
6
+ 'updatedAt',
7
+ 'code',
8
+ 'state',
9
+ 'total',
10
+ 'totalWithTax',
11
+ 'customerLastName',
12
+ ];
13
+ /** The engine's `adminListQueryLimit`: a larger page is refused. */
14
+ export const MAX_ORDER_LIST_TAKE = 1000;
15
+ const DEFAULT_TAKE = 25;
16
+ export const ORDER_SUMMARY_FIELDS = /* GraphQL */ `
17
+ id
18
+ code
19
+ state
20
+ total
21
+ totalWithTax
22
+ currencyCode
23
+ orderPlacedAt
24
+ customer {
25
+ id
26
+ firstName
27
+ lastName
28
+ emailAddress
29
+ addresses {
30
+ company
31
+ defaultBillingAddress
32
+ defaultShippingAddress
33
+ }
34
+ }
35
+ billingAddress {
36
+ company
37
+ }
38
+ shippingAddress {
39
+ company
40
+ }
41
+ lines {
42
+ quantity
43
+ }
44
+ payments {
45
+ id
46
+ state
47
+ }
48
+ customFields {
49
+ externalOrderId
50
+ externalPlatform
51
+ isGift
52
+ paymentStatus
53
+ fulfillmentStatus
54
+ returnStatus
55
+ lifecycleStatus
56
+ statusOverrides {
57
+ fulfillmentHoldReason
58
+ }
59
+ }
60
+ `;
61
+ export const ORDERS_LIST_QUERY = /* GraphQL */ `
62
+ query OrdersList($options: OrderListOptions) {
63
+ orders(options: $options) {
64
+ items {
65
+ ${ORDER_SUMMARY_FIELDS}
66
+ }
67
+ totalItems
68
+ }
69
+ }
70
+ `;
71
+ export const ORDERS_COUNT_QUERY = /* GraphQL */ `
72
+ query OrdersCount($options: OrderListOptions) {
73
+ orders(options: $options) {
74
+ totalItems
75
+ }
76
+ }
77
+ `;
78
+ export const CUSTOMER_ORDERS_QUERY = /* GraphQL */ `
79
+ query CustomerOrders($id: ID!, $options: OrderListOptions) {
80
+ customer(id: $id) {
81
+ id
82
+ orders(options: $options) {
83
+ items {
84
+ ${ORDER_SUMMARY_FIELDS}
85
+ }
86
+ totalItems
87
+ }
88
+ }
89
+ }
90
+ `;
91
+ function invalid(message) {
92
+ return new ValidationError({
93
+ code: 'INVALID_ORDER_LIST_OPTIONS',
94
+ message,
95
+ status: 400,
96
+ service: 'vendure',
97
+ });
98
+ }
99
+ function stringList(value, path) {
100
+ if (!Array.isArray(value) || value.some((item) => typeof item !== 'string')) {
101
+ throw invalid(`${path} must be a list of strings.`);
102
+ }
103
+ return [...value];
104
+ }
105
+ function text(value, path) {
106
+ if (typeof value !== 'string')
107
+ throw invalid(`${path} must be a string.`);
108
+ return value;
109
+ }
110
+ /**
111
+ * An instant the engine compares without guessing a zone: a string with no offset would be read
112
+ * in the database server's zone, so a range would shift by hours with the server.
113
+ */
114
+ const INSTANT = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?(?:Z|[+-]\d{2}:\d{2})$/;
115
+ function instant(value, path) {
116
+ if (typeof value !== 'string' || !INSTANT.test(value) || Number.isNaN(Date.parse(value))) {
117
+ throw invalid(`${path} must be an ISO-8601 instant with an offset or Z.`);
118
+ }
119
+ return value;
120
+ }
121
+ function escapeRegex(value) {
122
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
123
+ }
124
+ /** The Admin API's `OrderFilterParameter` for a typed filter; unknown keys are refused. */
125
+ export function toOrderFilterParameter(filter) {
126
+ if (filter === undefined)
127
+ return {};
128
+ if (typeof filter !== 'object' || filter === null || Array.isArray(filter)) {
129
+ throw invalid('filter must be an object.');
130
+ }
131
+ const out = {};
132
+ for (const [key, value] of Object.entries(filter)) {
133
+ if (value === undefined)
134
+ continue;
135
+ switch (key) {
136
+ case 'ids':
137
+ out.id = { in: stringList(value, 'filter.ids') };
138
+ break;
139
+ case 'state':
140
+ case 'paymentStatus':
141
+ case 'lifecycleStatus':
142
+ out[key] = { in: stringList(value, `filter.${key}`) };
143
+ break;
144
+ case 'code': {
145
+ const code = value;
146
+ if (typeof code !== 'object' || code === null)
147
+ throw invalid('filter.code must be an object.');
148
+ const operators = {};
149
+ for (const [operator, operand] of Object.entries(code)) {
150
+ if (operand === undefined)
151
+ continue;
152
+ const path = `filter.code.${operator}`;
153
+ if (operator === 'eq' || operator === 'contains')
154
+ operators[operator] = text(operand, path);
155
+ else if (operator === 'startsWith')
156
+ operators.regex = `^${escapeRegex(text(operand, path))}`;
157
+ else
158
+ throw invalid(`${path} is not a code filter; use eq, contains or startsWith.`);
159
+ }
160
+ out.code = operators;
161
+ break;
162
+ }
163
+ case 'orderPlacedAt': {
164
+ const placed = value;
165
+ if (typeof placed !== 'object' || placed === null) {
166
+ throw invalid('filter.orderPlacedAt must be an object.');
167
+ }
168
+ const operators = {};
169
+ for (const [operator, operand] of Object.entries(placed)) {
170
+ if (operand === undefined)
171
+ continue;
172
+ const path = `filter.orderPlacedAt.${operator}`;
173
+ if (operator === 'isNull') {
174
+ if (typeof operand !== 'boolean')
175
+ throw invalid(`${path} must be a boolean.`);
176
+ operators.isNull = operand;
177
+ }
178
+ else if (operator === 'before' || operator === 'after') {
179
+ operators[operator] = instant(operand, path);
180
+ }
181
+ else {
182
+ throw invalid(`${path} is not a date filter; use isNull, before or after.`);
183
+ }
184
+ }
185
+ out.orderPlacedAt = operators;
186
+ break;
187
+ }
188
+ case 'active':
189
+ if (typeof value !== 'boolean')
190
+ throw invalid('filter.active must be a boolean.');
191
+ out.active = { eq: value };
192
+ break;
193
+ case 'currencyCode':
194
+ out.currencyCode = { eq: text(value, 'filter.currencyCode') };
195
+ break;
196
+ case 'externalPlatform':
197
+ out.externalPlatform =
198
+ value === null ? { isNull: true } : { eq: text(value, 'filter.externalPlatform') };
199
+ break;
200
+ case 'customerLastName':
201
+ out.customerLastName = { contains: text(value, 'filter.customerLastName') };
202
+ break;
203
+ default:
204
+ throw invalid(`filter.${key} is not an order list filter.`);
205
+ }
206
+ }
207
+ return out;
208
+ }
209
+ function toOrderSort(sort) {
210
+ if (sort === undefined)
211
+ return undefined;
212
+ if (typeof sort !== 'object' ||
213
+ sort === null ||
214
+ !ORDER_LIST_SORT_FIELDS.includes(sort.field) ||
215
+ (sort.direction !== 'ASC' && sort.direction !== 'DESC')) {
216
+ throw invalid(`sort must be { field: ${ORDER_LIST_SORT_FIELDS.join(' | ')}, direction: ASC | DESC }.`);
217
+ }
218
+ return { [sort.field]: sort.direction };
219
+ }
220
+ function pageBounds(options) {
221
+ const take = options?.take ?? DEFAULT_TAKE;
222
+ const skip = options?.skip ?? 0;
223
+ if (!Number.isInteger(take) || take < 1 || take > MAX_ORDER_LIST_TAKE) {
224
+ throw invalid(`take must be a whole number from 1 to ${String(MAX_ORDER_LIST_TAKE)}.`);
225
+ }
226
+ if (!Number.isInteger(skip) || skip < 0)
227
+ throw invalid('skip must be a whole number of 0 or more.');
228
+ return { skip, take };
229
+ }
230
+ /**
231
+ * Pages through one order list. `read` answers the list (or `null` when its owner is gone); each
232
+ * following page re-sends the same filter, sort and per-call options, so a page cannot walk
233
+ * another store's orders.
234
+ */
235
+ async function listOrders(read, options) {
236
+ const { skip, take } = pageBounds(options);
237
+ const filter = toOrderFilterParameter(options?.filter);
238
+ const sort = toOrderSort(options?.sort);
239
+ const fetchPage = async (pageSkip) => {
240
+ const redaction = redactionWatcher();
241
+ const list = await read({
242
+ options: {
243
+ skip: pageSkip,
244
+ take,
245
+ ...(Object.keys(filter).length > 0 ? { filter, filterOperator: 'AND' } : {}),
246
+ ...(sort ? { sort } : {}),
247
+ },
248
+ }, redaction.onExtensions);
249
+ if (list === null)
250
+ return null;
251
+ return pageFromVendureList({ items: list.items.map((item) => redaction.mark(item)), totalItems: list.totalItems }, pageSkip, take, async (cursor) => {
252
+ const next = await fetchPage(readOffsetCursor(cursor));
253
+ if (next === null) {
254
+ throw new UpstreamError({
255
+ code: 'UNEXPECTED_RESPONSE',
256
+ message: 'The customer was gone by the next page of their orders.',
257
+ status: 502,
258
+ service: 'vendure',
259
+ });
260
+ }
261
+ return next;
262
+ });
263
+ };
264
+ return fetchPage(skip);
265
+ }
266
+ export function createOrderListModule(transport) {
267
+ return {
268
+ async list(options) {
269
+ const page = await listOrders(async (variables, onExtensions) => {
270
+ const data = await transport.graphql({ api: 'admin', query: ORDERS_LIST_QUERY, variables, onExtensions }, options);
271
+ return data.orders;
272
+ }, options);
273
+ // `read` above never answers null: the store's own list always exists.
274
+ return page;
275
+ },
276
+ async count(filter, options) {
277
+ const parameter = toOrderFilterParameter(filter);
278
+ const data = await transport.graphql({
279
+ api: 'admin',
280
+ query: ORDERS_COUNT_QUERY,
281
+ variables: {
282
+ options: {
283
+ // Never 0: Vendure passes it through and TypeORM then sends no LIMIT, so a
284
+ // "count" would load every matching order. One row costs nothing.
285
+ take: 1,
286
+ ...(Object.keys(parameter).length > 0
287
+ ? { filter: parameter, filterOperator: 'AND' }
288
+ : {}),
289
+ },
290
+ },
291
+ }, options);
292
+ return data.orders.totalItems;
293
+ },
294
+ };
295
+ }
296
+ /**
297
+ * One customer's orders, as `customers.orders` reads them: the same rows, filter, sort and paging
298
+ * as `orders.list`, or `null` when the store holds no customer with that id.
299
+ */
300
+ export async function listCustomerOrders(transport, customerId, options) {
301
+ return listOrders(async (variables, onExtensions) => {
302
+ const data = await transport.graphql({
303
+ api: 'admin',
304
+ query: CUSTOMER_ORDERS_QUERY,
305
+ variables: { id: customerId, ...variables },
306
+ onExtensions,
307
+ }, options);
308
+ return data.customer?.orders ?? null;
309
+ }, options);
310
+ }
311
+ /** The most orders `customers.recentOrders` answers. */
312
+ export const MAX_RECENT_ORDERS = 20;
313
+ const DEFAULT_RECENT_ORDERS = 5;
314
+ export const CUSTOMER_RECENT_ORDERS_QUERY = /* GraphQL */ `
315
+ query CustomerRecentOrders($id: ID!, $options: OrderListOptions) {
316
+ customer(id: $id) {
317
+ id
318
+ orders(options: $options) {
319
+ items {
320
+ id
321
+ code
322
+ orderPlacedAt
323
+ totalWithTax
324
+ currencyCode
325
+ lines {
326
+ id
327
+ quantity
328
+ listPriceIncludesTax
329
+ productVariant {
330
+ id
331
+ name
332
+ sku
333
+ product {
334
+ id
335
+ name
336
+ }
337
+ }
338
+ customFields {
339
+ unitPriceOverride
340
+ unitPriceImported
341
+ }
342
+ }
343
+ }
344
+ totalItems
345
+ }
346
+ }
347
+ }
348
+ `;
349
+ /**
350
+ * A customer's last placed orders, newest first, each with its lines, as `customers.recentOrders`
351
+ * reads them; `null` when the store holds no customer with that id.
352
+ */
353
+ export async function listCustomerRecentOrders(transport, customerId, options) {
354
+ const { take = DEFAULT_RECENT_ORDERS, ...call } = options ?? {};
355
+ if (!Number.isInteger(take) || take < 1 || take > MAX_RECENT_ORDERS) {
356
+ throw invalid(`take must be a whole number from 1 to ${String(MAX_RECENT_ORDERS)}.`);
357
+ }
358
+ const data = await transport.graphql({
359
+ api: 'admin',
360
+ query: CUSTOMER_RECENT_ORDERS_QUERY,
361
+ variables: {
362
+ id: customerId,
363
+ options: {
364
+ take,
365
+ // Placed orders only: sorted on its own, `orderPlacedAt` descending lists the carts
366
+ // and drafts (no date) first.
367
+ filter: { orderPlacedAt: { isNull: false } },
368
+ // ONE key, on purpose. The engine applies the keys of a sort in the order its schema
369
+ // declares them, not the order they are sent in, and `id` is declared before
370
+ // `orderPlacedAt`: an id "tie-breaker" here sorted the orders by id (found live).
371
+ sort: { orderPlacedAt: 'DESC' },
372
+ },
373
+ },
374
+ }, call);
375
+ // The document selects exactly the members of `RecentOrder`: the answer is the wire's.
376
+ return data.customer ? data.customer.orders.items : null;
377
+ }
@@ -161,6 +161,11 @@ export interface OrderManualPricingModule {
161
161
  * neither `unitPriceOverride` nor `priceOverrideReason` (it asks for nothing); a negative price;
162
162
  * `unitPriceOverride: null` on a line an external system priced (`orders.createOrRecover`
163
163
  * external lines), which has no catalogue price to fall back to: state the price instead.
164
+ *
165
+ * `ORDER_LINE_VARIANT_UNPRICED` (`BusinessRuleError`, 422; absent from an engine released before
166
+ * 2026-10-10): `unitPriceOverride: null` on a line that states a manual price while its variant
167
+ * has no price in the store, which would leave the line nothing to charge. Nothing is written:
168
+ * state the price instead, or set a price for the variant in the store first.
164
169
  */
165
170
  setLinePrices(orderId: string, input: SetLinePricesInput, options?: Call): Promise<OrderDiscounts>;
166
171
  /**
@@ -13,19 +13,27 @@ export type ExternalLinePriceBasis = 'net' | 'gross';
13
13
  /**
14
14
  * What is going to happen to the goods.
15
15
  *
16
- * A fact the vertical STATES about the order rather than an instruction
17
- * commerce acts on in this release: nothing here creates a fulfilment or picks
18
- * a shipping method. It is recorded as the order's `platform.fulfilment`
19
- * metafield so a document, a WMS push or a later fulfilment flow can read back
20
- * what the caller actually asked for.
16
+ * Commerce ACTS on it. Nothing here creates a fulfilment or picks a shipping
17
+ * method, but the value decides whether the order needs a shipping method to be
18
+ * placed and whether a fulfilment of it owes a delivery charge. It is recorded
19
+ * as the order's `platform.fulfilment` metafield (only the platform writes it),
20
+ * so a document, a WMS push or a merchant can also read back what you asked for.
21
21
  *
22
22
  * - `pickup` — collected at a location. Needs no shipping method and no
23
- * address.
23
+ * address. Handed over through an ordinary fulfilment, which adds no delivery
24
+ * charge and is not held to a fulfilment profile's minimum.
24
25
  * - `ship` — shipped. The order still needs a shipping method before it can
25
26
  * leave `Draft`, and `transitionTo: 'ArrangingPayment'` on an order that has
26
27
  * none is refused by the platform's own placement gate, not by this call.
28
+ * Delivery charges and profile minimums apply as for any delivered order.
27
29
  * - `none` — neither shipped nor collected: a service, a subscription set-up,
28
- * a fee.
30
+ * a fee. Needs no shipping method, owes no delivery charge and takes no
31
+ * profile minimum. The order is marked as having nothing to ship, so it
32
+ * completes on its own once it is paid (after the store's completion grace
33
+ * period). It may not hold goods that are stock-tracked: such a catalog line
34
+ * is refused `LINE_NEEDS_FULFILMENT` here, and so is placing (or modifying)
35
+ * the order to carry one later. Send goods as a `pickup` order, a service as
36
+ * an external line.
29
37
  */
30
38
  export type ExternalOrderFulfilment = 'pickup' | 'ship' | 'none';
31
39
  /**
@@ -478,6 +486,11 @@ export interface OrderEntryModule {
478
486
  * - `LINE_DUPLICATE_VARIANT` — two catalog lines name the same variant.
479
487
  * `details` carries the second one's `position` and the first one's
480
488
  * `firstPosition`. Send one line with the quantity you want.
489
+ * - `LINE_NEEDS_FULFILMENT` — a catalog line whose variant is stock-tracked on
490
+ * an order with `fulfilment: 'none'`. Nothing ships or is handed over on
491
+ * such an order, so nothing would ever sell or release that stock.
492
+ * `details` carries its `position` and `variantId`. Send the order as
493
+ * `pickup` (or `ship`), or the service as an external line.
481
494
  * - `EXTERNAL_LINE_PROMOTION_UNSUPPORTED` — a promotion discounted one of your
482
495
  * externally priced lines. An order-level promotion is shared across every
483
496
  * line of an order by the engine, so the platform refuses rather than
@@ -422,7 +422,7 @@ export interface OrderEditPlan {
422
422
  * the order-edit plan, so on an older one this call alone fails validation and
423
423
  * no other order call does.
424
424
  */
425
- export declare const PLAN_ORDER_EDIT_QUERY = "\n query PlanOrderEdit($orderId: ID!, $diff: OrderEditDiffInput!) {\n planOrderEdit(orderId: $orderId, diff: $diff) {\n orderId\n planHash\n orderVersion\n steps {\n kind\n input\n moneyEffect\n }\n refund {\n paymentId\n amount\n }\n overpayment {\n amount\n currencyCode\n surchargeIds\n refund {\n available\n code\n message\n }\n accountCredit {\n available\n code\n message\n }\n refundWhenChosen {\n paymentId\n amount\n }\n }\n returnState\n blockers {\n code\n message\n details\n }\n notices {\n code\n message\n details\n }\n payment {\n collected\n reason\n method\n }\n preview {\n currencyCode\n subTotal\n subTotalWithTax\n shipping\n shippingWithTax\n surcharges\n surchargesWithTax\n total\n totalWithTax\n taxSummary {\n description\n taxRate\n taxBase\n taxTotal\n }\n totalWithTaxBefore\n }\n }\n }\n";
425
+ export declare const PLAN_ORDER_EDIT_QUERY = "\n query PlanOrderEdit($orderId: ID!, $diff: OrderEditDiffInput!) {\n planOrderEdit(orderId: $orderId, diff: $diff) {\n orderId\n planHash\n orderVersion\n steps {\n kind\n input\n moneyEffect\n }\n refund {\n paymentId\n amount\n }\n overpayment {\n amount\n currencyCode\n surchargeIds\n refund {\n available\n code\n message\n book\n }\n accountCredit {\n available\n code\n message\n book\n }\n refundWhenChosen {\n paymentId\n amount\n }\n }\n returnState\n blockers {\n code\n message\n details\n }\n notices {\n code\n message\n details\n }\n payment {\n collected\n reason\n method\n }\n preview {\n currencyCode\n subTotal\n subTotalWithTax\n shipping\n shippingWithTax\n surcharges\n surchargesWithTax\n total\n totalWithTax\n taxSummary {\n description\n taxRate\n taxBase\n taxTotal\n }\n totalWithTaxBefore\n }\n }\n }\n";
426
426
  export interface OrderPlanEditModule {
427
427
  /**
428
428
  * How an edit of a placed order would be carried out, and what the order