@sixtyninecommerce/admin 0.11.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (147) hide show
  1. package/README.md +1501 -23
  2. package/dist/auth/index.d.ts +7 -1
  3. package/dist/auth/index.js +8 -1
  4. package/dist/catalog/documents.d.ts +142 -0
  5. package/dist/catalog/documents.js +114 -0
  6. package/dist/catalog/index.d.ts +48 -7
  7. package/dist/catalog/index.js +6 -1
  8. package/dist/catalog/metadata.d.ts +244 -0
  9. package/dist/catalog/metadata.js +220 -0
  10. package/dist/catalog/properties.d.ts +38 -0
  11. package/dist/catalog/properties.js +52 -1
  12. package/dist/catalog/recommendations.d.ts +33 -6
  13. package/dist/catalog/recommendations.js +94 -2
  14. package/dist/catalog/reviews.d.ts +17 -4
  15. package/dist/catalog/reviews.js +7 -0
  16. package/dist/catalog/variants.d.ts +60 -0
  17. package/dist/catalog/variants.js +39 -0
  18. package/dist/catalog/visibility.d.ts +18 -2
  19. package/dist/client.d.ts +29 -3
  20. package/dist/client.js +19 -8
  21. package/dist/content/assets.d.ts +3 -4
  22. package/dist/content/forms.d.ts +332 -1
  23. package/dist/content/forms.js +140 -4
  24. package/dist/content/items.d.ts +31 -8
  25. package/dist/content/items.js +1 -1
  26. package/dist/content/menus.d.ts +254 -0
  27. package/dist/content/menus.js +70 -0
  28. package/dist/content/strings.d.ts +137 -0
  29. package/dist/content/strings.js +82 -0
  30. package/dist/customers/delivery-terms.d.ts +44 -0
  31. package/dist/customers/delivery-terms.js +32 -0
  32. package/dist/customers/index.d.ts +15 -0
  33. package/dist/customers/index.js +4 -0
  34. package/dist/documents/emails.d.ts +150 -0
  35. package/dist/documents/emails.js +220 -0
  36. package/dist/documents/index.d.ts +13 -0
  37. package/dist/documents/index.js +2 -0
  38. package/dist/fulfillments/index.d.ts +119 -1
  39. package/dist/fulfillments/index.js +124 -4
  40. package/dist/index.d.ts +43 -23
  41. package/dist/index.js +11 -5
  42. package/dist/integrations/exact-online-types.d.ts +542 -0
  43. package/dist/integrations/exact-online.d.ts +541 -5
  44. package/dist/integrations/exact-online.js +714 -1
  45. package/dist/integrations/index.d.ts +23 -8
  46. package/dist/integrations/index.js +9 -4
  47. package/dist/integrations/pipeline-discoveries.d.ts +65 -0
  48. package/dist/integrations/pipeline-discoveries.js +65 -0
  49. package/dist/integrations/pipeline-runs.d.ts +119 -0
  50. package/dist/integrations/pipeline-runs.js +87 -0
  51. package/dist/integrations/pipelines.d.ts +73 -2
  52. package/dist/integrations/pipelines.js +36 -1
  53. package/dist/integrations/price-import.d.ts +223 -0
  54. package/dist/integrations/price-import.js +124 -0
  55. package/dist/integrations/types.d.ts +221 -3
  56. package/dist/integrations/types.js +4 -3
  57. package/dist/meta/index.d.ts +10 -0
  58. package/dist/metafields/index.d.ts +8 -1
  59. package/dist/metafields/index.js +10 -0
  60. package/dist/orders/cancel-with-money.d.ts +10 -0
  61. package/dist/orders/cancel-with-money.js +2 -0
  62. package/dist/orders/commit-edit.d.ts +16 -0
  63. package/dist/orders/commit-edit.js +14 -1
  64. package/dist/orders/deposit-policy.d.ts +3 -1
  65. package/dist/orders/index.d.ts +22 -11
  66. package/dist/orders/index.js +5 -2
  67. package/dist/orders/list.d.ts +26 -5
  68. package/dist/orders/list.js +123 -13
  69. package/dist/orders/manual-discounts.d.ts +13 -5
  70. package/dist/orders/money-view.d.ts +149 -0
  71. package/dist/orders/money-view.js +99 -0
  72. package/dist/orders/plan-edit.d.ts +34 -2
  73. package/dist/orders/plan-edit.js +110 -12
  74. package/dist/orders/surcharges.d.ts +12 -0
  75. package/dist/orders/surcharges.js +34 -9
  76. package/dist/payments/account-credit.d.ts +17 -1
  77. package/dist/payments/account-credit.js +25 -2
  78. package/dist/payments/credit-noted-fields.d.ts +8 -1
  79. package/dist/payments/credit-noted-fields.js +34 -2
  80. package/dist/payments/customer-consent.d.ts +30 -0
  81. package/dist/payments/customer-consent.js +49 -0
  82. package/dist/payments/index.d.ts +89 -14
  83. package/dist/payments/index.js +22 -2
  84. package/dist/payments/requests.d.ts +6 -3
  85. package/dist/payments/requests.js +1 -0
  86. package/dist/pickups/index.d.ts +5 -3
  87. package/dist/pickups/index.js +5 -2
  88. package/dist/platform/email-gateways.d.ts +165 -0
  89. package/dist/platform/email-gateways.js +78 -0
  90. package/dist/platform/tax.d.ts +11 -3
  91. package/dist/platform/tax.js +30 -0
  92. package/dist/pos/exchanges.d.ts +6 -1
  93. package/dist/pos/index.d.ts +229 -10
  94. package/dist/pos/index.js +113 -4
  95. package/dist/pos/offline-sales.d.ts +44 -3
  96. package/dist/pos/offline-sales.js +57 -3
  97. package/dist/pos/returns.d.ts +28 -1
  98. package/dist/pos/returns.js +36 -6
  99. package/dist/pricing/price-lists.d.ts +3 -1
  100. package/dist/purchasing/alerts.d.ts +19 -4
  101. package/dist/purchasing/alerts.js +29 -3
  102. package/dist/purchasing/backorders.d.ts +61 -0
  103. package/dist/purchasing/backorders.js +92 -0
  104. package/dist/purchasing/index.d.ts +21 -3
  105. package/dist/purchasing/index.js +8 -0
  106. package/dist/purchasing/links.d.ts +263 -0
  107. package/dist/purchasing/links.js +192 -0
  108. package/dist/purchasing/purchase-orders.d.ts +4 -1
  109. package/dist/purchasing/receipts.d.ts +396 -0
  110. package/dist/purchasing/receipts.js +287 -0
  111. package/dist/purchasing/returns.d.ts +273 -0
  112. package/dist/purchasing/returns.js +229 -0
  113. package/dist/quotes/index.d.ts +20 -3
  114. package/dist/returns/index.d.ts +62 -1
  115. package/dist/returns/index.js +61 -0
  116. package/dist/seo/config.d.ts +128 -0
  117. package/dist/seo/config.js +95 -0
  118. package/dist/seo/redirects.d.ts +125 -0
  119. package/dist/seo/redirects.js +60 -1
  120. package/dist/seo/settings.d.ts +147 -0
  121. package/dist/seo/settings.js +209 -0
  122. package/dist/shipping/index.d.ts +6 -0
  123. package/dist/shipping/index.js +2 -0
  124. package/dist/shipping/methods.d.ts +8 -1
  125. package/dist/shipping/myparcel.d.ts +415 -1
  126. package/dist/shipping/myparcel.js +291 -3
  127. package/dist/shipping/rate-tables.d.ts +149 -4
  128. package/dist/shipping/rate-tables.js +229 -23
  129. package/dist/shipping/ship-to-countries.d.ts +75 -0
  130. package/dist/shipping/ship-to-countries.js +47 -0
  131. package/dist/stock/by-location.d.ts +86 -0
  132. package/dist/stock/by-location.js +94 -0
  133. package/dist/stock/index.d.ts +8 -1
  134. package/dist/stock/index.js +4 -0
  135. package/dist/stock/settings.d.ts +106 -0
  136. package/dist/stock/settings.js +103 -0
  137. package/dist/stock/transfers.d.ts +171 -10
  138. package/dist/stock/transfers.js +82 -4
  139. package/dist/stored-value/index.d.ts +98 -7
  140. package/dist/stored-value/index.js +71 -4
  141. package/dist/tax/display-policy.d.ts +105 -0
  142. package/dist/tax/display-policy.js +84 -0
  143. package/dist/tax/index.d.ts +3 -0
  144. package/dist/tax/index.js +2 -0
  145. package/dist/tenant/email-templates.d.ts +222 -1
  146. package/dist/tenant/email-templates.js +227 -10
  147. package/package.json +2 -2
@@ -19,6 +19,16 @@ export interface CancelWithMoneyCall extends Call {
19
19
  overpayment?: OrderOverpaymentChoice;
20
20
  /** The settled money stays on the cancelled order (with a `reason`). */
21
21
  retain?: boolean;
22
+ /**
23
+ * With `overpayment: 'ACCOUNT_CREDIT'` in a store whose book is store credit (the preview's
24
+ * `accountCredit.book` `STORE_CREDIT`): whether the customer expressly agreed to store credit
25
+ * instead of a refund to their payment (EU consumer law, CRD art. 13(1)), as staff recorded it.
26
+ * The move records it on its ledger entries (`StoredValueEntry.customerAgreed`); never a
27
+ * condition, nothing is refused for it. Part of the request: a repeat under the same key sends
28
+ * the same answer. Sent only when given; an engine before the field (incentives sprint 8 PR 6a)
29
+ * refuses a call that gives it.
30
+ */
31
+ customerAgreed?: boolean;
22
32
  }
23
33
  /** What a cancel with a money decision did. */
24
34
  /**
@@ -1,4 +1,5 @@
1
1
  import { creditNotedAware } from '../payments/credit-noted-fields.js';
2
+ import { customerAgreedVariable } from '../payments/customer-consent.js';
2
3
  import { STORE_CREDIT_MOVE_FIELDS, storeCreditAware } from '../payments/store-credit-fields.js';
3
4
  const OPTION_FIELDS = /* GraphQL */ `
4
5
  available
@@ -127,6 +128,7 @@ export function createOrderCancelWithMoneyModule(transport) {
127
128
  reason: options?.reason,
128
129
  overpayment: options?.overpayment,
129
130
  retain: options?.retain,
131
+ customerAgreed: customerAgreedVariable(options?.customerAgreed),
130
132
  },
131
133
  idempotencyKey: options?.idempotencyKey,
132
134
  },
@@ -72,12 +72,28 @@ export interface CommitOrderEditCall extends Call {
72
72
  * Part of the request: a retry under the same key re-sends the same choice.
73
73
  */
74
74
  overpaymentChoice?: OrderOverpaymentChoice;
75
+ /**
76
+ * With `overpaymentChoice: 'ACCOUNT_CREDIT'` in a store whose book is store credit (the plan's
77
+ * `overpayment.accountCredit.book` `STORE_CREDIT`): whether the customer expressly agreed to
78
+ * store credit instead of a refund to their payment (EU consumer law, CRD art. 13(1)), as staff
79
+ * recorded it. The move records it on its ledger entries (`StoredValueEntry.customerAgreed`);
80
+ * never a condition, nothing is refused for it. Part of the request like the choice: a retry
81
+ * under the same key re-sends the same answer, and another answer under it is
82
+ * `IDEMPOTENCY_CONFLICT`. Sent only when given; an engine before the field (incentives sprint 8
83
+ * PR 6a) refuses a call that gives it.
84
+ */
85
+ customerAgreed?: boolean;
75
86
  }
76
87
  /**
77
88
  * Its own document on purpose: `commitOrderEdit` exists only on an engine with the order-edit
78
89
  * commit, so on an older one this call alone fails validation and no other order call does.
79
90
  */
80
91
  export declare const COMMIT_ORDER_EDIT_MUTATION = "\n mutation CommitOrderEdit(\n $orderId: ID!\n $diff: OrderEditDiffInput!\n $expectedPlanHash: String!\n $idempotencyKey: String!\n $overpaymentChoice: OrderOverpaymentChoice\n ) {\n commitOrderEdit(\n orderId: $orderId\n diff: $diff\n expectedPlanHash: $expectedPlanHash\n idempotencyKey: $idempotencyKey\n overpaymentChoice: $overpaymentChoice\n ) {\n order {\n id\n code\n state\n currencyCode\n totalWithTax\n }\n plan {\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 refund {\n id\n state\n total\n paymentId\n }\n overpayment {\n choice\n amount\n currencyCode\n unappliedCashIds\n storeCredit { accountId amount currencyCode entryIds }\n }\n }\n }\n";
92
+ /**
93
+ * The same, declaring the customer's answer to store credit: sent only by a call that states it,
94
+ * so an engine before the argument still takes every call that does not.
95
+ */
96
+ export declare const COMMIT_ORDER_EDIT_WITH_CONSENT_MUTATION: string;
81
97
  export interface OrderCommitEditModule {
82
98
  /**
83
99
  * Carry out an edit of a placed order as its plan (`planEdit`) states it, once. Needs
@@ -1,4 +1,5 @@
1
1
  import { idempotencyKey as mintIdempotencyKey } from '@sixtyninecommerce/core';
2
+ import { customerAgreedVariable, withCustomerAgreed } from '../payments/customer-consent.js';
2
3
  import { STORE_CREDIT_MOVE_FIELDS, storeCreditAware } from '../payments/store-credit-fields.js';
3
4
  /**
4
5
  * Its own document on purpose: `commitOrderEdit` exists only on an engine with the order-edit
@@ -111,14 +112,25 @@ export const COMMIT_ORDER_EDIT_MUTATION = /* GraphQL */ `
111
112
  }
112
113
  }
113
114
  `;
115
+ /**
116
+ * The same, declaring the customer's answer to store credit: sent only by a call that states it,
117
+ * so an engine before the argument still takes every call that does not.
118
+ */
119
+ export const COMMIT_ORDER_EDIT_WITH_CONSENT_MUTATION = withCustomerAgreed(COMMIT_ORDER_EDIT_MUTATION, {
120
+ declaration: '$overpaymentChoice: OrderOverpaymentChoice',
121
+ argument: 'overpaymentChoice: $overpaymentChoice',
122
+ });
114
123
  export function createOrderCommitEditModule(transport) {
115
124
  return {
116
125
  async commitEdit(orderId, diff, options) {
117
126
  const key = options.idempotencyKey ?? mintIdempotencyKey();
127
+ const customerAgreed = customerAgreedVariable(options.customerAgreed);
118
128
  try {
119
129
  const data = await storeCreditAware(transport).graphql({
120
130
  api: 'admin',
121
- query: COMMIT_ORDER_EDIT_MUTATION,
131
+ query: customerAgreed === undefined
132
+ ? COMMIT_ORDER_EDIT_MUTATION
133
+ : COMMIT_ORDER_EDIT_WITH_CONSENT_MUTATION,
122
134
  // The diff goes out as given, never normalised (see `planEdit`): left out, `null` and
123
135
  // a value are three different requests.
124
136
  variables: {
@@ -130,6 +142,7 @@ export function createOrderCommitEditModule(transport) {
130
142
  ...(options.overpaymentChoice !== undefined
131
143
  ? { overpaymentChoice: options.overpaymentChoice }
132
144
  : {}),
145
+ ...(customerAgreed !== undefined ? { customerAgreed } : {}),
133
146
  },
134
147
  }, { ...options, idempotencyKey: key });
135
148
  return { ...data.commitOrderEdit, idempotencyKey: key };
@@ -32,7 +32,9 @@ export interface OrderDepositPolicy {
32
32
  /**
33
33
  * The store-wide switch. Off: a payment must cover the whole outstanding
34
34
  * amount (`payments.record` refuses a part payment `DEPOSITS_NOT_ENABLED`) and
35
- * no order can be parked.
35
+ * no order can be parked. Stored value (a gift card or store credit) is the
36
+ * exception: it may pay part of an order still arranging payment, and the
37
+ * order is placed only once its money covers it.
36
38
  */
37
39
  enabled: boolean;
38
40
  /**
@@ -14,6 +14,7 @@ import { type OrderLinePricePreviewModule } from './line-price-preview.js';
14
14
  import { type OrderListModule } from './list.js';
15
15
  import { type ManualDiscountsModule } from './manual-discounts.js';
16
16
  import { type OrderManualPricingModule } from './manual-pricing.js';
17
+ import { type OrderMoneyViewModule } from './money-view.js';
17
18
  import { type OrderEntryModule } from './order-entry.js';
18
19
  import { type OrderParcelChargeModule } from './parcel-charge.js';
19
20
  import { type OrderPlanEditModule } from './plan-edit.js';
@@ -28,7 +29,7 @@ export type { ClonedDraft, DraftAddLineInput, DraftAddressDefault, DraftAddressD
28
29
  export { DraftInsufficientStockError } from './drafts.js';
29
30
  export type { FulfillmentQuantityCorrection, FulfillmentQuantityCorrectionInput, FulfillmentQuantityCorrectionLine, FulfillmentUnitDisposition, OrderCustomerAssignment, OrderFieldsModule, OrderGoodwillRounding, OrderNote, OrderNoteInput, OrderNotesModule, OrderPurchaseOrderNumber, } from './fields.js';
30
31
  export type { FulfillmentHoldInput, OrderLifecycleModule, OrderLifecycleRecord, } from './lifecycle.js';
31
- export type { OrderListFilter, OrderListModule, OrderListOptions, OrderListSort, OrderListSortField, OrderSummary, RecentOrder, RecentOrderLine, RecentOrdersOptions, } from './list.js';
32
+ export type { OrderBillableTotals, OrderListFilter, OrderListModule, OrderListOptions, OrderListSort, OrderListSortField, OrderSummary, RecentOrder, RecentOrderLine, RecentOrdersOptions, } from './list.js';
32
33
  export { MAX_ORDER_LIST_TAKE, MAX_RECENT_ORDERS, ORDER_LIST_SORT_FIELDS } from './list.js';
33
34
  export type { DiscountPolicy, DiscountPolicyInput, DiscountPolicyModule, DiscountReason, DiscountReasonInput, DiscountReasonsModule, ManualDiscountsModule, OrderManualDiscount, } from './manual-discounts.js';
34
35
  export type { ExternalLinePriceBasis, ExternalOrder, ExternalOrderAddressInput, ExternalOrderCall, ExternalOrderCatalogLine, ExternalOrderExternalLine, ExternalOrderFulfilment, ExternalOrderInput, ExternalOrderLine, ExternalOrderLineInput, ExternalOrderSurcharge, ExternalOrderSurchargeInput, ExternalOrderTargetState, ExternalOrderTaxLine, } from './order-entry.js';
@@ -84,7 +85,7 @@ export interface OrderSettlement {
84
85
  * The manual pricing doors (`setLinePrices`, `setOneTimeDiscounts`) sit there too, beside their
85
86
  * read twin `previewLinePrices`, which prices the same changes without saving them.
86
87
  */
87
- export interface OrdersModule extends OrderEntryModule, OrderGetModule, OrderListModule, OrderParcelChargeModule, OrderLinePricePreviewModule, OrderManualPricingModule, OrderPlanEditModule, OrderCommitEditModule, OrderCancelWithMoneyModule, OrderLifecycleModule, OrderFieldsModule, OrderStatusModule {
88
+ export interface OrdersModule extends OrderEntryModule, OrderGetModule, OrderListModule, OrderParcelChargeModule, OrderLinePricePreviewModule, OrderManualPricingModule, OrderPlanEditModule, OrderCommitEditModule, OrderCancelWithMoneyModule, OrderLifecycleModule, OrderFieldsModule, OrderStatusModule, OrderMoneyViewModule {
88
89
  surcharges: SurchargesModule;
89
90
  /**
90
91
  * The draft-order builder: staff create a draft, set its customer, lines (with manual prices
@@ -242,8 +243,9 @@ export interface OrdersModule extends OrderEntryModule, OrderGetModule, OrderLis
242
243
  */
243
244
  unpark(orderId: string, options?: Call): Promise<PaidOrder>;
244
245
  /**
245
- * What the order has actually been paid, what its credit notes took off and what it still owes
246
- * (`payments.position`'s answer). Needs `ReadOrder`.
246
+ * What the order has actually been paid, what its credit notes took off, what it still owes
247
+ * and what its settled money must reach before it counts as paid (`payments.position`'s
248
+ * answer). Needs `ReadOrder`.
247
249
  *
248
250
  * @effect read
249
251
  */
@@ -252,7 +254,10 @@ export interface OrdersModule extends OrderEntryModule, OrderGetModule, OrderLis
252
254
  * Move a fully paid order to `PaymentSettled`. Needs `UpdateOrder`.
253
255
  *
254
256
  * Refuses `COVERAGE_INSUFFICIENT` while the order's SETTLED money does not
255
- * cover its total, with the position attached. Settled, not covered: an
257
+ * cover what it must be paid (`payable` on the position: its total, less what
258
+ * a live invoice's credit notes took off it; an order paid down to what its
259
+ * invoice bills net of its credit notes IS paid), with the position attached.
260
+ * Settled, not covered: an
256
261
  * authorization is permission to take money, not money, so an order whose
257
262
  * remainder is merely authorized settles once that authorization is captured
258
263
  * where it stands (`settlePayment` on the Admin API): the capture settles it,
@@ -280,17 +285,23 @@ export interface OrdersModule extends OrderEntryModule, OrderGetModule, OrderLis
280
285
  *
281
286
  * Calling this on an order the platform ALREADY settled succeeds rather than
282
287
  * refusing. Expect that to be the common case: the engine settles an order as
283
- * soon as its settled payments cover its total, so recording the payment that
284
- * clears a deposit usually settles the order on its own, and this call is then
285
- * a confirmation.
288
+ * soon as its settled payments cover what it must be paid, so recording the
289
+ * payment that clears a deposit usually settles the order on its own, and this
290
+ * call is then a confirmation. A credit note that alone makes an order paid
291
+ * moves nothing: a held order waits for this call.
286
292
  *
287
293
  * So do not wait on a settle CALL to learn that a deposit was paid off — in
288
294
  * the ordinary flow nobody makes one. The close signal is the platform event
289
295
  * `payment.partial_recorded` with `outstanding_after: 0`, on the payment that
290
296
  * cleared the balance. That figure nets settled PLUS authorized money, so it
291
- * means fully COVERED; "fully settled" is `settled >= total` on the position,
297
+ * means fully COVERED; "fully settled" is `settled >= payable` on the position,
292
298
  * which is what this method requires. For money recorded through
293
- * `payments.record` the two coincide — it only creates settled payments.
299
+ * `payments.record` the two coincide — it only creates settled payments —
300
+ * except on an order whose credit notes lowered what it must be paid, which
301
+ * settles with `outstanding_after` still naming the credited part
302
+ * (`order.settlement_completed` then carries `credit_noted`).
303
+ *
304
+ * @effect money
294
305
  */
295
306
  settle(orderId: string, options?: Call & {
296
307
  fulfil?: boolean;
@@ -307,5 +318,5 @@ export interface OrdersModule extends OrderEntryModule, OrderGetModule, OrderLis
307
318
  export declare const PARK_ORDER_MUTATION = "\n mutation ParkOrder($orderId: ID!, $reason: String, $idempotencyKey: String) {\n parkOrder(orderId: $orderId, reason: $reason, idempotencyKey: $idempotencyKey) {\n id\n code\n state\n currencyCode\n total\n totalWithTax\n }\n }\n";
308
319
  export declare const UNPARK_ORDER_MUTATION = "\n mutation UnparkOrder($orderId: ID!, $idempotencyKey: String) {\n unparkOrder(orderId: $orderId, idempotencyKey: $idempotencyKey) {\n id\n code\n state\n currencyCode\n total\n totalWithTax\n }\n }\n";
309
320
  export declare const SETTLE_ORDER_MUTATION = "\n mutation SettleOrder($orderId: ID!, $fulfil: Boolean!, $idempotencyKey: String) {\n settleOrder(orderId: $orderId, fulfil: $fulfil, idempotencyKey: $idempotencyKey) {\n \n order {\n id\n code\n state\n currencyCode\n total\n totalWithTax\n }\n position {\n total\n covered\n settled\n authorized\n refunded\n outstanding\n overpaid\n availableToCharge\n }\n fulfilRequested\n\n }\n }\n";
310
- export declare const ORDER_POSITION_QUERY = "\n query OrderPosition($orderId: ID!) {\n orderPaymentPosition(orderId: $orderId) {\n total\n covered\n settled\n authorized\n refunded\n creditNoted\n owed\n outstanding\n overpaid\n availableToCharge\n }\n }\n";
321
+ export declare const ORDER_POSITION_QUERY = "\n query OrderPosition($orderId: ID!) {\n orderPaymentPosition(orderId: $orderId) {\n total\n covered\n settled\n authorized\n refunded\n creditNoted\n owed\n payable\n outstanding\n overpaid\n availableToCharge\n }\n }\n";
311
322
  export declare function createOrdersModule(transport: Transport): OrdersModule;
@@ -1,5 +1,5 @@
1
1
  import { NotFoundError } from '@sixtyninecommerce/core';
2
- import { creditNotedAware } from '../payments/credit-noted-fields.js';
2
+ import { creditNotedAware, payableAware } from '../payments/credit-noted-fields.js';
3
3
  import { bulkOrders, deleteOrder, readOrderBulkRun, } from './bulk.js';
4
4
  import { createOrderCancelWithMoneyModule, } from './cancel-with-money.js';
5
5
  import { createOrderCommitEditModule } from './commit-edit.js';
@@ -12,6 +12,7 @@ import { createOrderLinePricePreviewModule, } from './line-price-preview.js';
12
12
  import { createOrderListModule } from './list.js';
13
13
  import { createManualDiscountsModule } from './manual-discounts.js';
14
14
  import { createOrderManualPricingModule } from './manual-pricing.js';
15
+ import { createOrderMoneyViewModule } from './money-view.js';
15
16
  import { createOrderEntryModule } from './order-entry.js';
16
17
  import { createOrderParcelChargeModule } from './parcel-charge.js';
17
18
  import { createOrderPlanEditModule } from './plan-edit.js';
@@ -97,6 +98,7 @@ export const ORDER_POSITION_QUERY = /* GraphQL */ `
97
98
  refunded
98
99
  creditNoted
99
100
  owed
101
+ payable
100
102
  outstanding
101
103
  overpaid
102
104
  availableToCharge
@@ -118,6 +120,7 @@ export function createOrdersModule(transport) {
118
120
  ...createOrderLifecycleModule(transport),
119
121
  ...createOrderFieldsModule(transport),
120
122
  ...createOrderStatusModule(transport),
123
+ ...createOrderMoneyViewModule(transport),
121
124
  surcharges: createSurchargesModule(transport),
122
125
  drafts: createOrderDraftsModule(transport, getModule),
123
126
  manualDiscounts: createManualDiscountsModule(transport),
@@ -170,7 +173,7 @@ export function createOrdersModule(transport) {
170
173
  },
171
174
  async paymentPosition(orderId, options) {
172
175
  // The credit fields fall away on an engine before them: the position itself still reads.
173
- const data = await creditNotedAware(transport).graphql({ api: 'admin', query: ORDER_POSITION_QUERY, variables: { orderId } }, options);
176
+ const data = await creditNotedAware(payableAware(transport)).graphql({ api: 'admin', query: ORDER_POSITION_QUERY, variables: { orderId } }, options);
174
177
  return data.orderPaymentPosition;
175
178
  },
176
179
  async settle(orderId, options) {
@@ -11,8 +11,17 @@ export interface OrderSummary {
11
11
  id: string;
12
12
  code: string;
13
13
  state: string;
14
+ /** Core's total excl. VAT, before a staff rounding. */
14
15
  total: number;
16
+ /** Core's total incl. VAT, before a staff rounding. Print {@link billableTotals} instead. */
15
17
  totalWithTax: number;
18
+ /**
19
+ * What the order bills: `total` incl. VAT is `totalWithTax` with a staff (goodwill) rounding
20
+ * folded in, the figure the order page (`moneyView.total`), the invoice and the payment carry;
21
+ * `totalExcludingTax` the same excl. VAT. Print these as the order's total. `null` from an
22
+ * engine before the field: print `totalWithTax` / `total` then.
23
+ */
24
+ billableTotals: OrderBillableTotals | null;
16
25
  currencyCode: string;
17
26
  /** ISO-8601; null for an order not placed yet (a cart, a draft). */
18
27
  orderPlacedAt: string | null;
@@ -59,6 +68,13 @@ export interface OrderSummary {
59
68
  */
60
69
  redacted?: true;
61
70
  }
71
+ /** What an order bills, a staff rounding included (`Order.billableTotals`). Minor units. */
72
+ export interface OrderBillableTotals {
73
+ /** Incl. VAT; equals the order's `moneyView.total`. */
74
+ total: number;
75
+ /** Excl. VAT; equals the order's `moneyView.totalExcludingTax`. */
76
+ totalExcludingTax: number;
77
+ }
62
78
  /**
63
79
  * Which orders a list holds. Every key narrows it; several keys are combined with AND. An empty
64
80
  * list (`state: []`) matches nothing.
@@ -118,10 +134,10 @@ export interface OrderListOptions extends Call {
118
134
  }
119
135
  /** The engine's `adminListQueryLimit`: a larger page is refused. */
120
136
  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";
137
+ export declare const ORDER_SUMMARY_FIELDS = "\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 billableTotals {\n total\n totalExcludingTax\n }\n";
138
+ export declare const ORDERS_LIST_QUERY = "\n query OrdersList($options: OrderListOptions) {\n orders(options: $options) {\n items {\n \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 billableTotals {\n total\n totalExcludingTax\n }\n\n }\n totalItems\n }\n }\n";
123
139
  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";
140
+ 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 \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 billableTotals {\n total\n totalExcludingTax\n }\n\n }\n totalItems\n }\n }\n }\n";
125
141
  /** The Admin API's `OrderFilterParameter` for a typed filter; unknown keys are refused. */
126
142
  export declare function toOrderFilterParameter(filter: OrderListFilter | undefined): Record<string, unknown>;
127
143
  export interface OrderListModule {
@@ -205,8 +221,13 @@ export interface RecentOrder {
205
221
  code: string;
206
222
  /** ISO-8601. */
207
223
  orderPlacedAt: string;
208
- /** Minor units of `currencyCode`. */
224
+ /** Minor units of `currencyCode`: core's total, before a staff rounding. */
209
225
  totalWithTax: number;
226
+ /**
227
+ * What the order bills (a staff rounding folded in): print `billableTotals.total`. `null` from
228
+ * an engine before the field: print `totalWithTax` then.
229
+ */
230
+ billableTotals: OrderBillableTotals | null;
210
231
  currencyCode: string;
211
232
  lines: RecentOrderLine[];
212
233
  }
@@ -214,7 +235,7 @@ export interface RecentOrdersOptions extends Call {
214
235
  /** How many orders, 1 to {@link MAX_RECENT_ORDERS}; 5 when omitted. */
215
236
  take?: number;
216
237
  }
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";
238
+ 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 billableTotals {\n total\n totalExcludingTax\n }\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
239
  /**
219
240
  * A customer's last placed orders, newest first, each with its lines, as `customers.recentOrders`
220
241
  * reads them; `null` when the store holds no customer with that id.
@@ -13,7 +13,8 @@ export const ORDER_LIST_SORT_FIELDS = [
13
13
  /** The engine's `adminListQueryLimit`: a larger page is refused. */
14
14
  export const MAX_ORDER_LIST_TAKE = 1000;
15
15
  const DEFAULT_TAKE = 25;
16
- export const ORDER_SUMMARY_FIELDS = /* GraphQL */ `
16
+ /** {@link ORDER_SUMMARY_FIELDS} as an engine before `Order.billableTotalWithTax` accepts it. */
17
+ const ORDER_SUMMARY_FIELDS_BEFORE_BILLABLE = /* GraphQL */ `
17
18
  id
18
19
  code
19
20
  state
@@ -58,6 +59,15 @@ export const ORDER_SUMMARY_FIELDS = /* GraphQL */ `
58
59
  }
59
60
  }
60
61
  `;
62
+ export const ORDER_SUMMARY_FIELDS = /* GraphQL */ `
63
+ ${ORDER_SUMMARY_FIELDS_BEFORE_BILLABLE}
64
+ billableTotals {
65
+ total
66
+ totalExcludingTax
67
+ }
68
+ `;
69
+ // Each document is spelled out with a named constant inside, never built by a function: the
70
+ // GraphQL cost spec (`graphql-cost-fits-repo.spec.ts`) resolves `${NAME}` statically.
61
71
  export const ORDERS_LIST_QUERY = /* GraphQL */ `
62
72
  query OrdersList($options: OrderListOptions) {
63
73
  orders(options: $options) {
@@ -68,6 +78,16 @@ export const ORDERS_LIST_QUERY = /* GraphQL */ `
68
78
  }
69
79
  }
70
80
  `;
81
+ const ORDERS_LIST_QUERY_BEFORE_BILLABLE = /* GraphQL */ `
82
+ query OrdersList($options: OrderListOptions) {
83
+ orders(options: $options) {
84
+ items {
85
+ ${ORDER_SUMMARY_FIELDS_BEFORE_BILLABLE}
86
+ }
87
+ totalItems
88
+ }
89
+ }
90
+ `;
71
91
  export const ORDERS_COUNT_QUERY = /* GraphQL */ `
72
92
  query OrdersCount($options: OrderListOptions) {
73
93
  orders(options: $options) {
@@ -88,6 +108,44 @@ export const CUSTOMER_ORDERS_QUERY = /* GraphQL */ `
88
108
  }
89
109
  }
90
110
  `;
111
+ const CUSTOMER_ORDERS_QUERY_BEFORE_BILLABLE = /* GraphQL */ `
112
+ query CustomerOrders($id: ID!, $options: OrderListOptions) {
113
+ customer(id: $id) {
114
+ id
115
+ orders(options: $options) {
116
+ items {
117
+ ${ORDER_SUMMARY_FIELDS_BEFORE_BILLABLE}
118
+ }
119
+ totalItems
120
+ }
121
+ }
122
+ }
123
+ `;
124
+ /**
125
+ * Whether a failure is "this engine does not know that field yet": the document refused before
126
+ * it ran, never an execution-time refusal or a transient failure (the purchasing module's rule).
127
+ */
128
+ function isUnknownFieldRejection(error) {
129
+ if (!(error instanceof ValidationError) || error.service !== 'vendure')
130
+ return false;
131
+ return (error.code === 'BAD_REQUEST' ||
132
+ error.code === 'GRAPHQL_VALIDATION_FAILED' ||
133
+ /Cannot query field/i.test(error.message));
134
+ }
135
+ /**
136
+ * Sends the current document; only an engine that refuses it as naming an unknown field (one
137
+ * before `Order.billableTotals`, services deploy separately) is sent the one before it.
138
+ */
139
+ async function withBillableFallback(send) {
140
+ try {
141
+ return await send(false);
142
+ }
143
+ catch (error) {
144
+ if (!isUnknownFieldRejection(error))
145
+ throw error;
146
+ return send(true);
147
+ }
148
+ }
91
149
  function invalid(message) {
92
150
  return new ValidationError({
93
151
  code: 'INVALID_ORDER_LIST_OPTIONS',
@@ -238,17 +296,22 @@ async function listOrders(read, options) {
238
296
  const sort = toOrderSort(options?.sort);
239
297
  const fetchPage = async (pageSkip) => {
240
298
  const redaction = redactionWatcher();
241
- const list = await read({
299
+ const variables = {
242
300
  options: {
243
301
  skip: pageSkip,
244
302
  take,
245
303
  ...(Object.keys(filter).length > 0 ? { filter, filterOperator: 'AND' } : {}),
246
304
  ...(sort ? { sort } : {}),
247
305
  },
248
- }, redaction.onExtensions);
306
+ };
307
+ const list = await withBillableFallback((beforeBillable) => read(variables, redaction.onExtensions, beforeBillable));
249
308
  if (list === null)
250
309
  return null;
251
- return pageFromVendureList({ items: list.items.map((item) => redaction.mark(item)), totalItems: list.totalItems }, pageSkip, take, async (cursor) => {
310
+ const items = list.items.map((item) => ({
311
+ ...item,
312
+ billableTotals: item.billableTotals ?? null,
313
+ }));
314
+ return pageFromVendureList({ items: items.map((item) => redaction.mark(item)), totalItems: list.totalItems }, pageSkip, take, async (cursor) => {
252
315
  const next = await fetchPage(readOffsetCursor(cursor));
253
316
  if (next === null) {
254
317
  throw new UpstreamError({
@@ -266,8 +329,9 @@ async function listOrders(read, options) {
266
329
  export function createOrderListModule(transport) {
267
330
  return {
268
331
  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);
332
+ const page = await listOrders(async (variables, onExtensions, beforeBillable) => {
333
+ const query = beforeBillable ? ORDERS_LIST_QUERY_BEFORE_BILLABLE : ORDERS_LIST_QUERY;
334
+ const data = await transport.graphql({ api: 'admin', query, variables, onExtensions }, options);
271
335
  return data.orders;
272
336
  }, options);
273
337
  // `read` above never answers null: the store's own list always exists.
@@ -298,10 +362,10 @@ export function createOrderListModule(transport) {
298
362
  * as `orders.list`, or `null` when the store holds no customer with that id.
299
363
  */
300
364
  export async function listCustomerOrders(transport, customerId, options) {
301
- return listOrders(async (variables, onExtensions) => {
365
+ return listOrders(async (variables, onExtensions, beforeBillable) => {
302
366
  const data = await transport.graphql({
303
367
  api: 'admin',
304
- query: CUSTOMER_ORDERS_QUERY,
368
+ query: beforeBillable ? CUSTOMER_ORDERS_QUERY_BEFORE_BILLABLE : CUSTOMER_ORDERS_QUERY,
305
369
  variables: { id: customerId, ...variables },
306
370
  onExtensions,
307
371
  }, options);
@@ -312,6 +376,45 @@ export async function listCustomerOrders(transport, customerId, options) {
312
376
  export const MAX_RECENT_ORDERS = 20;
313
377
  const DEFAULT_RECENT_ORDERS = 5;
314
378
  export const CUSTOMER_RECENT_ORDERS_QUERY = /* GraphQL */ `
379
+ query CustomerRecentOrders($id: ID!, $options: OrderListOptions) {
380
+ customer(id: $id) {
381
+ id
382
+ orders(options: $options) {
383
+ items {
384
+ id
385
+ code
386
+ orderPlacedAt
387
+ totalWithTax
388
+ billableTotals {
389
+ total
390
+ totalExcludingTax
391
+ }
392
+ currencyCode
393
+ lines {
394
+ id
395
+ quantity
396
+ listPriceIncludesTax
397
+ productVariant {
398
+ id
399
+ name
400
+ sku
401
+ product {
402
+ id
403
+ name
404
+ }
405
+ }
406
+ customFields {
407
+ unitPriceOverride
408
+ unitPriceImported
409
+ }
410
+ }
411
+ }
412
+ totalItems
413
+ }
414
+ }
415
+ }
416
+ `;
417
+ const CUSTOMER_RECENT_ORDERS_QUERY_BEFORE_BILLABLE = /* GraphQL */ `
315
418
  query CustomerRecentOrders($id: ID!, $options: OrderListOptions) {
316
419
  customer(id: $id) {
317
420
  id
@@ -355,9 +458,11 @@ export async function listCustomerRecentOrders(transport, customerId, options) {
355
458
  if (!Number.isInteger(take) || take < 1 || take > MAX_RECENT_ORDERS) {
356
459
  throw invalid(`take must be a whole number from 1 to ${String(MAX_RECENT_ORDERS)}.`);
357
460
  }
358
- const data = await transport.graphql({
461
+ const data = await withBillableFallback((beforeBillable) => transport.graphql({
359
462
  api: 'admin',
360
- query: CUSTOMER_RECENT_ORDERS_QUERY,
463
+ query: beforeBillable
464
+ ? CUSTOMER_RECENT_ORDERS_QUERY_BEFORE_BILLABLE
465
+ : CUSTOMER_RECENT_ORDERS_QUERY,
361
466
  variables: {
362
467
  id: customerId,
363
468
  options: {
@@ -371,7 +476,12 @@ export async function listCustomerRecentOrders(transport, customerId, options) {
371
476
  sort: { orderPlacedAt: 'DESC' },
372
477
  },
373
478
  },
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;
479
+ }, call));
480
+ // The document selects the members of `RecentOrder`; an older engine leaves out the billable one.
481
+ return data.customer
482
+ ? data.customer.orders.items.map((order) => ({
483
+ ...order,
484
+ billableTotals: order.billableTotals ?? null,
485
+ }))
486
+ : null;
377
487
  }
@@ -41,7 +41,10 @@ export interface DiscountPolicy {
41
41
  * The store's ceiling on manual discounts in basis points (1000 = 10 %), or `null` for none: how
42
42
  * far below the price the customer would get anyway (its contract included) staff without the
43
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.
44
+ * discount is never refused. The POS till is held to it too (a till discount and a lowered
45
+ * till price, cart-wide and per line): there only a session holding `OverrideDiscountLimit`, or
46
+ * a staff member holding the explicit `discount_over_limit` grant (a grant no role holds by
47
+ * default), goes past it.
45
48
  */
46
49
  maxDiscountBps: number | null;
47
50
  }
@@ -63,14 +66,16 @@ export interface OrderManualDiscount {
63
66
  /** The line the movement was on; `null` for a till discount on the whole cart. */
64
67
  orderLineId: string | null;
65
68
  /**
66
- * `price_list`, `line_override` or `till` (`pos.sale.applyDiscount`, one row per call). Accept a
67
- * value you do not know.
69
+ * `price_list`, `line_override` or `till` (`pos.sale.applyDiscount`, one row per call, and a row
70
+ * per change the till made to that money since: a removal, a cap or a re-split). Accept a value
71
+ * you do not know.
68
72
  */
69
73
  door: string;
70
74
  /**
71
75
  * The change of the line's total, net: (unit price after − before) × quantity, minor units of
72
76
  * `currencyCode`, as the order holds it. Negative when the price fell (a manual discount). For
73
- * `till`: the money the call's negative surcharges added.
77
+ * `till`: the money the call's negative surcharges added; a later row of that discount carries
78
+ * the till's change, signed, so an order's `till` rows sum to its till discount in force.
74
79
  */
75
80
  amount: number;
76
81
  /** The same movement gross. */
@@ -85,7 +90,10 @@ export interface OrderManualDiscount {
85
90
  } | null;
86
91
  /** The Vendure user of the session that moved it. */
87
92
  actorUserId: string | null;
88
- /** The vouched-for `x-on-behalf-of` ref; for a till vouched for nothing, `pos-staff:<id>`. */
93
+ /**
94
+ * The vouched-for `x-on-behalf-of` ref; for a till vouched for nothing, `pos-staff:<id>`. A `till`
95
+ * row that changes a discount carries the ref of the row that gave it.
96
+ */
89
97
  onBehalfOfRef: string | null;
90
98
  /** The vouched-for `x-device-ref` ref; for a paired till vouched for nothing, `pos-device:<id>`. */
91
99
  deviceRef: string | null;