@sixtyninecommerce/admin 0.9.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 (258) hide show
  1. package/README.md +4598 -128
  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 +318 -17
  9. package/dist/catalog/products.js +182 -14
  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 +282 -25
  21. package/dist/catalog/variants.js +173 -25
  22. package/dist/catalog/visibility.d.ts +20 -5
  23. package/dist/catalog/visibility.js +19 -1
  24. package/dist/client.d.ts +116 -9
  25. package/dist/client.js +41 -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 +121 -10
  63. package/dist/fulfillments/index.js +120 -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 +129 -30
  71. package/dist/index.js +47 -9
  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 +40 -0
  87. package/dist/integrations/index.js +24 -0
  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 +114 -0
  91. package/dist/integrations/pipelines.js +46 -0
  92. package/dist/integrations/types.d.ts +322 -0
  93. package/dist/integrations/types.js +8 -0
  94. package/dist/jobs/bulk-action-run.d.ts +11 -2
  95. package/dist/jobs/bulk-action-run.js +4 -5
  96. package/dist/locations/index.d.ts +66 -0
  97. package/dist/locations/index.js +31 -0
  98. package/dist/meta/index.d.ts +162 -0
  99. package/dist/orders/bulk.d.ts +75 -0
  100. package/dist/orders/bulk.js +150 -0
  101. package/dist/orders/cancel-with-money.d.ts +227 -0
  102. package/dist/orders/cancel-with-money.js +153 -0
  103. package/dist/orders/commit-edit.d.ts +7 -1
  104. package/dist/orders/commit-edit.js +5 -1
  105. package/dist/orders/deposit-policy.d.ts +103 -0
  106. package/dist/orders/deposit-policy.js +202 -0
  107. package/dist/orders/drafts.d.ts +800 -0
  108. package/dist/orders/drafts.js +771 -0
  109. package/dist/orders/fields.d.ts +245 -0
  110. package/dist/orders/fields.js +188 -0
  111. package/dist/orders/get.d.ts +29 -19
  112. package/dist/orders/get.js +17 -8
  113. package/dist/orders/index.d.ts +125 -5
  114. package/dist/orders/index.js +35 -2
  115. package/dist/orders/lifecycle.d.ts +128 -0
  116. package/dist/orders/lifecycle.js +158 -0
  117. package/dist/orders/line-price-preview.d.ts +7 -2
  118. package/dist/orders/list.d.ts +222 -0
  119. package/dist/orders/list.js +377 -0
  120. package/dist/orders/manual-discounts.d.ts +11 -2
  121. package/dist/orders/manual-discounts.js +2 -0
  122. package/dist/orders/manual-pricing.d.ts +5 -0
  123. package/dist/orders/order-entry.d.ts +20 -7
  124. package/dist/orders/plan-edit.d.ts +1 -1
  125. package/dist/orders/plan-edit.js +4 -1
  126. package/dist/orders/settings.d.ts +201 -0
  127. package/dist/orders/settings.js +325 -0
  128. package/dist/orders/status.d.ts +159 -13
  129. package/dist/orders/status.js +21 -0
  130. package/dist/orders/surcharges.d.ts +8 -1
  131. package/dist/orders/surcharges.js +3 -2
  132. package/dist/payments/account-credit.d.ts +30 -0
  133. package/dist/payments/account-credit.js +19 -0
  134. package/dist/payments/ar.d.ts +292 -0
  135. package/dist/payments/ar.js +223 -0
  136. package/dist/payments/connected-account.d.ts +47 -0
  137. package/dist/payments/connected-account.js +61 -0
  138. package/dist/payments/credit-noted-fields.d.ts +12 -0
  139. package/dist/payments/credit-noted-fields.js +37 -0
  140. package/dist/payments/index.d.ts +263 -10
  141. package/dist/payments/index.js +133 -1
  142. package/dist/payments/methods.d.ts +332 -0
  143. package/dist/payments/methods.js +363 -0
  144. package/dist/payments/mollie-methods.d.ts +159 -0
  145. package/dist/payments/mollie-methods.js +141 -0
  146. package/dist/payments/payment-links.d.ts +180 -0
  147. package/dist/payments/payment-links.js +97 -0
  148. package/dist/payments/provider-keys.d.ts +150 -0
  149. package/dist/payments/provider-keys.js +118 -0
  150. package/dist/payments/providers.d.ts +86 -11
  151. package/dist/payments/providers.js +29 -0
  152. package/dist/payments/requests.d.ts +144 -0
  153. package/dist/payments/requests.js +92 -0
  154. package/dist/payments/store-credit-fields.d.ts +15 -0
  155. package/dist/payments/store-credit-fields.js +51 -0
  156. package/dist/pickups/index.d.ts +104 -0
  157. package/dist/pickups/index.js +105 -0
  158. package/dist/platform/channels.d.ts +132 -0
  159. package/dist/platform/channels.js +128 -0
  160. package/dist/platform/geo.d.ts +123 -0
  161. package/dist/platform/geo.js +144 -0
  162. package/dist/platform/tax.d.ts +154 -0
  163. package/dist/platform/tax.js +139 -0
  164. package/dist/pos/anomalies.d.ts +158 -0
  165. package/dist/pos/anomalies.js +64 -0
  166. package/dist/pos/assisted-selling.d.ts +79 -0
  167. package/dist/pos/assisted-selling.js +38 -0
  168. package/dist/pos/exchanges.d.ts +103 -0
  169. package/dist/pos/exchanges.js +22 -0
  170. package/dist/pos/gift-cards.d.ts +135 -0
  171. package/dist/pos/gift-cards.js +47 -0
  172. package/dist/pos/index.d.ts +2125 -32
  173. package/dist/pos/index.js +742 -4
  174. package/dist/pos/no-receipt.d.ts +94 -0
  175. package/dist/pos/no-receipt.js +25 -0
  176. package/dist/pos/offline-sales.d.ts +187 -0
  177. package/dist/pos/offline-sales.js +116 -0
  178. package/dist/pos/pay-by-link.d.ts +143 -0
  179. package/dist/pos/pay-by-link.js +57 -0
  180. package/dist/pos/quick-keys.d.ts +103 -0
  181. package/dist/pos/quick-keys.js +45 -0
  182. package/dist/pos/reports.d.ts +360 -0
  183. package/dist/pos/reports.js +62 -0
  184. package/dist/pos/returns.d.ts +217 -0
  185. package/dist/pos/returns.js +53 -0
  186. package/dist/pricing/bulk-coupons.d.ts +139 -0
  187. package/dist/pricing/bulk-coupons.js +112 -0
  188. package/dist/pricing/index.d.ts +177 -4
  189. package/dist/pricing/index.js +176 -1
  190. package/dist/pricing/price-lists.d.ts +604 -0
  191. package/dist/pricing/price-lists.js +600 -0
  192. package/dist/pricing/vouchers.d.ts +103 -0
  193. package/dist/pricing/vouchers.js +69 -0
  194. package/dist/purchasing/alerts.d.ts +89 -0
  195. package/dist/purchasing/alerts.js +62 -0
  196. package/dist/purchasing/cost.d.ts +110 -0
  197. package/dist/purchasing/cost.js +86 -0
  198. package/dist/purchasing/index.d.ts +36 -0
  199. package/dist/purchasing/index.js +16 -0
  200. package/dist/purchasing/purchase-orders.d.ts +548 -0
  201. package/dist/purchasing/purchase-orders.js +491 -0
  202. package/dist/purchasing/replenishment.d.ts +281 -0
  203. package/dist/purchasing/replenishment.js +240 -0
  204. package/dist/purchasing/settings.d.ts +152 -0
  205. package/dist/purchasing/settings.js +181 -0
  206. package/dist/purchasing/suppliers.d.ts +476 -0
  207. package/dist/purchasing/suppliers.js +438 -0
  208. package/dist/quotes/index.d.ts +501 -0
  209. package/dist/quotes/index.js +439 -0
  210. package/dist/reports/index.d.ts +71 -8
  211. package/dist/reports/index.js +74 -11
  212. package/dist/returns/index.d.ts +818 -0
  213. package/dist/returns/index.js +844 -0
  214. package/dist/search/index.d.ts +174 -0
  215. package/dist/search/index.js +125 -0
  216. package/dist/shipping/definitions.d.ts +29 -0
  217. package/dist/shipping/definitions.js +47 -0
  218. package/dist/shipping/fulfillment-profiles.d.ts +174 -0
  219. package/dist/shipping/fulfillment-profiles.js +104 -0
  220. package/dist/shipping/hendriks.d.ts +97 -0
  221. package/dist/shipping/hendriks.js +50 -0
  222. package/dist/shipping/index.d.ts +48 -0
  223. package/dist/shipping/index.js +25 -0
  224. package/dist/shipping/methods.d.ts +198 -0
  225. package/dist/shipping/methods.js +240 -0
  226. package/dist/shipping/myparcel.d.ts +254 -0
  227. package/dist/shipping/myparcel.js +178 -0
  228. package/dist/shipping/parcel-count-suggestion.d.ts +2 -0
  229. package/dist/shipping/parcel-rule.d.ts +6 -5
  230. package/dist/shipping/parcel-rule.js +21 -23
  231. package/dist/shipping/rate-tables.d.ts +179 -0
  232. package/dist/shipping/rate-tables.js +184 -0
  233. package/dist/stock/index.d.ts +10 -3
  234. package/dist/stock/index.js +3 -0
  235. package/dist/stock/locations.d.ts +144 -0
  236. package/dist/stock/locations.js +168 -0
  237. package/dist/stock/transfers.d.ts +216 -43
  238. package/dist/stock/transfers.js +140 -13
  239. package/dist/stored-value/index.d.ts +214 -4
  240. package/dist/stored-value/index.js +152 -0
  241. package/dist/subscriptions/index.d.ts +682 -0
  242. package/dist/subscriptions/index.js +815 -0
  243. package/dist/tax/index.d.ts +76 -1
  244. package/dist/tax/index.js +50 -0
  245. package/dist/tenant/email-style.d.ts +102 -0
  246. package/dist/tenant/email-style.js +72 -0
  247. package/dist/tenant/email-templates.d.ts +66 -0
  248. package/dist/tenant/email-templates.js +54 -0
  249. package/dist/tenant/notifications.d.ts +23 -17
  250. package/dist/tenant/settings-sources.d.ts +1 -1
  251. package/dist/tenant/time-zone.d.ts +17 -0
  252. package/dist/tenant/time-zone.js +11 -0
  253. package/dist/types.d.ts +51 -4
  254. package/dist/users/index.d.ts +3 -0
  255. package/dist/users/index.js +2 -0
  256. package/dist/users/warehouses.d.ts +50 -0
  257. package/dist/users/warehouses.js +51 -0
  258. 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
+ }
@@ -37,10 +37,19 @@ export interface DiscountPolicy {
37
37
  * active reasons for that line. A till discount (`pos.sale.applyDiscount`) is not held to it.
38
38
  */
39
39
  reasonRequired: boolean;
40
+ /**
41
+ * The store's ceiling on manual discounts in basis points (1000 = 10 %), or `null` for none: how
42
+ * far below the price the customer would get anyway (its contract included) staff without the
43
+ * `OverrideDiscountLimit` permission may take a line (`DISCOUNT_OVER_LIMIT`). Reducing a
44
+ * discount is never refused. The POS till is not held to it.
45
+ */
46
+ maxDiscountBps: number | null;
40
47
  }
41
48
  export interface DiscountPolicyInput {
42
49
  /** Omitted keeps the stored value. */
43
50
  reasonRequired?: boolean;
51
+ /** 0..10000 basis points; omitted keeps the stored value, `null` removes the ceiling. */
52
+ maxDiscountBps?: number | null;
44
53
  }
45
54
  /**
46
55
  * One price movement a manual-discount door made on an order. Append-only. Each row is a price
@@ -126,7 +135,7 @@ export interface ManualDiscountsModule {
126
135
  }
127
136
  export declare const DISCOUNT_REASONS_QUERY = "\n query DiscountReasons($includeInactive: Boolean) {\n discountReasons(includeInactive: $includeInactive) {\n \n id\n code\n label\n active\n createdAt\n updatedAt\n\n }\n }\n";
128
137
  export declare const UPSERT_DISCOUNT_REASON_MUTATION = "\n mutation UpsertDiscountReason($input: UpsertDiscountReasonInput!) {\n upsertDiscountReason(input: $input) {\n \n id\n code\n label\n active\n createdAt\n updatedAt\n\n }\n }\n";
129
- export declare const DISCOUNT_POLICY_QUERY = "\n query DiscountPolicy {\n discountPolicy {\n reasonRequired\n }\n }\n";
130
- export declare const UPDATE_DISCOUNT_POLICY_MUTATION = "\n mutation UpdateDiscountPolicy($input: UpdateDiscountPolicyInput!) {\n updateDiscountPolicy(input: $input) {\n reasonRequired\n }\n }\n";
138
+ export declare const DISCOUNT_POLICY_QUERY = "\n query DiscountPolicy {\n discountPolicy {\n reasonRequired\n maxDiscountBps\n }\n }\n";
139
+ export declare const UPDATE_DISCOUNT_POLICY_MUTATION = "\n mutation UpdateDiscountPolicy($input: UpdateDiscountPolicyInput!) {\n updateDiscountPolicy(input: $input) {\n reasonRequired\n maxDiscountBps\n }\n }\n";
131
140
  export declare const ORDER_MANUAL_DISCOUNTS_QUERY = "\n query OrderManualDiscounts($orderId: ID!) {\n orderManualDiscounts(orderId: $orderId) {\n \n id\n createdAt\n orderId\n orderLineId\n door\n amount\n amountWithTax\n currencyCode\n reason {\n id\n code\n label\n active\n }\n actorUserId\n onBehalfOfRef\n deviceRef\n callId\n\n }\n }\n";
132
141
  export declare function createManualDiscountsModule(transport: Transport): ManualDiscountsModule;
@@ -44,6 +44,7 @@ export const DISCOUNT_POLICY_QUERY = /* GraphQL */ `
44
44
  query DiscountPolicy {
45
45
  discountPolicy {
46
46
  reasonRequired
47
+ maxDiscountBps
47
48
  }
48
49
  }
49
50
  `;
@@ -51,6 +52,7 @@ export const UPDATE_DISCOUNT_POLICY_MUTATION = /* GraphQL */ `
51
52
  mutation UpdateDiscountPolicy($input: UpdateDiscountPolicyInput!) {
52
53
  updateDiscountPolicy(input: $input) {
53
54
  reasonRequired
55
+ maxDiscountBps
54
56
  }
55
57
  }
56
58
  `;
@@ -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
  /**