@aglyn/plugins-commerce 1.0.0-beta.277 → 1.0.0-beta.282

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 (212) hide show
  1. package/package.json +17 -14
  2. package/src/lib/components/commerce-console-page.js +33 -1
  3. package/src/lib/components/commerce-console-page.js.map +1 -1
  4. package/src/lib/components/console/payments-settings-card.component.js +1 -1
  5. package/src/lib/components/console/payments-settings-card.component.js.map +1 -1
  6. package/src/lib/components/console/pos/pos-hardware-api.d.ts +122 -0
  7. package/src/lib/components/console/pos/pos-hardware-api.js +90 -0
  8. package/src/lib/components/console/pos/pos-hardware-api.js.map +1 -0
  9. package/src/lib/components/console/pos/pos-reader-splash-api.d.ts +58 -0
  10. package/src/lib/components/console/pos/pos-reader-splash-api.js +101 -0
  11. package/src/lib/components/console/pos/pos-reader-splash-api.js.map +1 -0
  12. package/src/lib/components/console/pos-devices-card.component.js +5 -0
  13. package/src/lib/components/console/pos-devices-card.component.js.map +1 -1
  14. package/src/lib/components/console/pos-hardware-card.component.d.ts +26 -0
  15. package/src/lib/components/console/pos-hardware-card.component.js +456 -0
  16. package/src/lib/components/console/pos-hardware-card.component.js.map +1 -0
  17. package/src/lib/components/console/pos-hardware-orders-staff-page.component.d.ts +14 -0
  18. package/src/lib/components/console/pos-hardware-orders-staff-page.component.js +519 -0
  19. package/src/lib/components/console/pos-hardware-orders-staff-page.component.js.map +1 -0
  20. package/src/lib/components/console/pos-ops/pos-check-in-dialog.component.d.ts +27 -0
  21. package/src/lib/components/console/pos-ops/pos-check-in-dialog.component.js +444 -0
  22. package/src/lib/components/console/pos-ops/pos-check-in-dialog.component.js.map +1 -0
  23. package/src/lib/components/console/pos-ops/pos-check-in-zone.component.d.ts +9 -0
  24. package/src/lib/components/console/pos-ops/pos-check-in-zone.component.js +37 -0
  25. package/src/lib/components/console/pos-ops/pos-check-in-zone.component.js.map +1 -0
  26. package/src/lib/components/console/pos-ops/pos-check-ins-card.component.d.ts +24 -0
  27. package/src/lib/components/console/pos-ops/pos-check-ins-card.component.js +346 -0
  28. package/src/lib/components/console/pos-ops/pos-check-ins-card.component.js.map +1 -0
  29. package/src/lib/components/console/pos-ops/pos-customer-lookup.component.d.ts +5 -1
  30. package/src/lib/components/console/pos-ops/pos-customer-lookup.component.js +83 -4
  31. package/src/lib/components/console/pos-ops/pos-customer-lookup.component.js.map +1 -1
  32. package/src/lib/components/console/pos-ops/pos-day-totals-card.component.d.ts +35 -0
  33. package/src/lib/components/console/pos-ops/pos-day-totals-card.component.js +546 -0
  34. package/src/lib/components/console/pos-ops/pos-day-totals-card.component.js.map +1 -0
  35. package/src/lib/components/console/pos-ops/pos-operations-bar.component.d.ts +2 -1
  36. package/src/lib/components/console/pos-ops/pos-operations-bar.component.js +20 -1
  37. package/src/lib/components/console/pos-ops/pos-operations-bar.component.js.map +1 -1
  38. package/src/lib/components/console/pos-ops/pos-ops-api.d.ts +1 -1
  39. package/src/lib/components/console/pos-ops/pos-ops-api.js.map +1 -1
  40. package/src/lib/components/console/pos-page.component.js +6 -3
  41. package/src/lib/components/console/pos-page.component.js.map +1 -1
  42. package/src/lib/components/console/pos-reader-splash-setting.component.d.ts +22 -0
  43. package/src/lib/components/console/pos-reader-splash-setting.component.js +275 -0
  44. package/src/lib/components/console/pos-reader-splash-setting.component.js.map +1 -0
  45. package/src/lib/components/console/pos-reader-splash-staff-page.component.d.ts +14 -0
  46. package/src/lib/components/console/pos-reader-splash-staff-page.component.js +301 -0
  47. package/src/lib/components/console/pos-reader-splash-staff-page.component.js.map +1 -0
  48. package/src/lib/components/console/pos-zones.d.ts +37 -3
  49. package/src/lib/components/console/pos-zones.js +11 -1
  50. package/src/lib/components/console/pos-zones.js.map +1 -1
  51. package/src/lib/components/console/product-check-in-fields.component.d.ts +20 -0
  52. package/src/lib/components/console/product-check-in-fields.component.js +172 -0
  53. package/src/lib/components/console/product-check-in-fields.component.js.map +1 -0
  54. package/src/lib/components/console/product-editor-dialog.component.js +48 -9
  55. package/src/lib/components/console/product-editor-dialog.component.js.map +1 -1
  56. package/src/lib/components/console/product-register-fields.component.d.ts +12 -0
  57. package/src/lib/components/console/product-register-fields.component.js +39 -1
  58. package/src/lib/components/console/product-register-fields.component.js.map +1 -1
  59. package/src/lib/components/console/products-hub-card.component.js +21 -6
  60. package/src/lib/components/console/products-hub-card.component.js.map +1 -1
  61. package/src/lib/components/console/reservations-card.component.js +239 -70
  62. package/src/lib/components/console/reservations-card.component.js.map +1 -1
  63. package/src/lib/components/reservation-widget.js +16 -0
  64. package/src/lib/components/reservation-widget.js.map +1 -1
  65. package/src/lib/constants/operator-alerts.d.ts +6 -0
  66. package/src/lib/constants/operator-alerts.js +20 -1
  67. package/src/lib/constants/operator-alerts.js.map +1 -1
  68. package/src/lib/declarations.console-server.js +7 -0
  69. package/src/lib/declarations.console-server.js.map +1 -1
  70. package/src/lib/declarations.d.ts +26 -0
  71. package/src/lib/declarations.js +34 -0
  72. package/src/lib/declarations.js.map +1 -0
  73. package/src/lib/model/commerce-check-in-product.d.ts +37 -0
  74. package/src/lib/model/commerce-check-in-product.js +49 -0
  75. package/src/lib/model/commerce-check-in-product.js.map +1 -0
  76. package/src/lib/model/commerce-check-in.d.ts +187 -0
  77. package/src/lib/model/commerce-check-in.js +468 -0
  78. package/src/lib/model/commerce-check-in.js.map +1 -0
  79. package/src/lib/model/commerce-pos-day-report.d.ts +209 -0
  80. package/src/lib/model/commerce-pos-day-report.js +572 -0
  81. package/src/lib/model/commerce-pos-day-report.js.map +1 -0
  82. package/src/lib/model/commerce-pos-offline.d.ts +65 -3
  83. package/src/lib/model/commerce-pos-offline.js +104 -7
  84. package/src/lib/model/commerce-pos-offline.js.map +1 -1
  85. package/src/lib/model/commerce-pos-ops.d.ts +3 -0
  86. package/src/lib/model/commerce-pos-ops.js +1 -0
  87. package/src/lib/model/commerce-pos-ops.js.map +1 -1
  88. package/src/lib/model/commerce-pos.d.ts +43 -3
  89. package/src/lib/model/commerce-pos.js +73 -5
  90. package/src/lib/model/commerce-pos.js.map +1 -1
  91. package/src/lib/model/commerce-reservations.d.ts +73 -7
  92. package/src/lib/model/commerce-reservations.js +90 -10
  93. package/src/lib/model/commerce-reservations.js.map +1 -1
  94. package/src/lib/model/commerce.d.ts +40 -0
  95. package/src/lib/model/commerce.js +22 -4
  96. package/src/lib/model/commerce.js.map +1 -1
  97. package/src/lib/model/index.d.ts +1 -0
  98. package/src/lib/model/index.js +1 -0
  99. package/src/lib/model/index.js.map +1 -1
  100. package/src/lib/model/plugin-permissions.d.ts +1 -1
  101. package/src/lib/model/plugin-permissions.js +2 -1
  102. package/src/lib/model/plugin-permissions.js.map +1 -1
  103. package/src/lib/model/pos-hardware.d.ts +288 -0
  104. package/src/lib/model/pos-hardware.js +499 -0
  105. package/src/lib/model/pos-hardware.js.map +1 -0
  106. package/src/lib/model/pos-offline-card-limits.d.ts +20 -0
  107. package/src/lib/model/pos-offline-card-limits.js +28 -0
  108. package/src/lib/model/pos-offline-card-limits.js.map +1 -0
  109. package/src/lib/model/pos-reader-splash.d.ts +114 -0
  110. package/src/lib/model/pos-reader-splash.js +141 -0
  111. package/src/lib/model/pos-reader-splash.js.map +1 -0
  112. package/src/lib/model/product-write.d.ts +6 -0
  113. package/src/lib/plugin-config.d.ts +10 -0
  114. package/src/lib/plugin-config.js +40 -0
  115. package/src/lib/plugin-config.js.map +1 -1
  116. package/src/lib/plugin.js +28 -0
  117. package/src/lib/plugin.js.map +1 -1
  118. package/src/lib/server/cart-checkout.js +3 -1
  119. package/src/lib/server/cart-checkout.js.map +1 -1
  120. package/src/lib/server/cart.js +12 -1
  121. package/src/lib/server/cart.js.map +1 -1
  122. package/src/lib/server/catalog.js +49 -12
  123. package/src/lib/server/catalog.js.map +1 -1
  124. package/src/lib/server/checkout-extras.js +1 -1
  125. package/src/lib/server/checkout-extras.js.map +1 -1
  126. package/src/lib/server/checkout.js +7 -0
  127. package/src/lib/server/checkout.js.map +1 -1
  128. package/src/lib/server/person-eraser.d.ts +8 -1
  129. package/src/lib/server/person-eraser.js +23 -1
  130. package/src/lib/server/person-eraser.js.map +1 -1
  131. package/src/lib/server/pos-check-in.d.ts +49 -0
  132. package/src/lib/server/pos-check-in.js +652 -0
  133. package/src/lib/server/pos-check-in.js.map +1 -0
  134. package/src/lib/server/pos-day-report.d.ts +46 -0
  135. package/src/lib/server/pos-day-report.js +299 -0
  136. package/src/lib/server/pos-day-report.js.map +1 -0
  137. package/src/lib/server/pos-hardware-admin.d.ts +50 -0
  138. package/src/lib/server/pos-hardware-admin.js +366 -0
  139. package/src/lib/server/pos-hardware-admin.js.map +1 -0
  140. package/src/lib/server/pos-hardware-notify.d.ts +27 -0
  141. package/src/lib/server/pos-hardware-notify.js +142 -0
  142. package/src/lib/server/pos-hardware-notify.js.map +1 -0
  143. package/src/lib/server/pos-hardware-user-eraser.d.ts +26 -0
  144. package/src/lib/server/pos-hardware-user-eraser.js +67 -0
  145. package/src/lib/server/pos-hardware-user-eraser.js.map +1 -0
  146. package/src/lib/server/pos-hardware-webhook.d.ts +31 -0
  147. package/src/lib/server/pos-hardware-webhook.js +176 -0
  148. package/src/lib/server/pos-hardware-webhook.js.map +1 -0
  149. package/src/lib/server/pos-hardware.d.ts +58 -0
  150. package/src/lib/server/pos-hardware.js +234 -0
  151. package/src/lib/server/pos-hardware.js.map +1 -0
  152. package/src/lib/server/pos-offline-card-reconcile.d.ts +38 -0
  153. package/src/lib/server/pos-offline-card-reconcile.js +58 -0
  154. package/src/lib/server/pos-offline-card-reconcile.js.map +1 -0
  155. package/src/lib/server/pos-offline-sync.d.ts +32 -0
  156. package/src/lib/server/pos-offline-sync.js +191 -15
  157. package/src/lib/server/pos-offline-sync.js.map +1 -1
  158. package/src/lib/server/pos-ops-routes.d.ts +4 -2
  159. package/src/lib/server/pos-ops-routes.js +14 -2
  160. package/src/lib/server/pos-ops-routes.js.map +1 -1
  161. package/src/lib/server/pos-order.js +39 -51
  162. package/src/lib/server/pos-order.js.map +1 -1
  163. package/src/lib/server/pos-payment.js +8 -4
  164. package/src/lib/server/pos-payment.js.map +1 -1
  165. package/src/lib/server/pos-reader-splash-admin.d.ts +36 -0
  166. package/src/lib/server/pos-reader-splash-admin.js +295 -0
  167. package/src/lib/server/pos-reader-splash-admin.js.map +1 -0
  168. package/src/lib/server/pos-reader-splash-render.d.ts +40 -0
  169. package/src/lib/server/pos-reader-splash-render.js +138 -0
  170. package/src/lib/server/pos-reader-splash-render.js.map +1 -0
  171. package/src/lib/server/pos-reader-splash-server.d.ts +32 -0
  172. package/src/lib/server/pos-reader-splash-server.js +32 -0
  173. package/src/lib/server/pos-reader-splash-server.js.map +1 -0
  174. package/src/lib/server/pos-reader-splash.d.ts +105 -0
  175. package/src/lib/server/pos-reader-splash.js +548 -0
  176. package/src/lib/server/pos-reader-splash.js.map +1 -0
  177. package/src/lib/server/pos-sale.d.ts +2 -1
  178. package/src/lib/server/pos-sale.js +3 -1
  179. package/src/lib/server/pos-sale.js.map +1 -1
  180. package/src/lib/server/pos-terminal.d.ts +67 -4
  181. package/src/lib/server/pos-terminal.js +239 -32
  182. package/src/lib/server/pos-terminal.js.map +1 -1
  183. package/src/lib/server/process-restock.js +4 -0
  184. package/src/lib/server/process-restock.js.map +1 -1
  185. package/src/lib/server/product-card.js +2 -2
  186. package/src/lib/server/product-card.js.map +1 -1
  187. package/src/lib/server/product-catalog.d.ts +2 -0
  188. package/src/lib/server/product-catalog.js +11 -4
  189. package/src/lib/server/product-catalog.js.map +1 -1
  190. package/src/lib/server/product.js +3 -1
  191. package/src/lib/server/product.js.map +1 -1
  192. package/src/lib/server/related.js +8 -1
  193. package/src/lib/server/related.js.map +1 -1
  194. package/src/lib/server/reservation-availability-query.d.ts +35 -0
  195. package/src/lib/server/reservation-availability-query.js +34 -0
  196. package/src/lib/server/reservation-availability-query.js.map +1 -0
  197. package/src/lib/server/reservation-availability.d.ts +21 -0
  198. package/src/lib/server/reservation-availability.js +64 -10
  199. package/src/lib/server/reservation-availability.js.map +1 -1
  200. package/src/lib/server/reservation-walk-in.d.ts +28 -0
  201. package/src/lib/server/reservation-walk-in.js +166 -0
  202. package/src/lib/server/reservation-walk-in.js.map +1 -0
  203. package/src/lib/server/reserve.js +13 -10
  204. package/src/lib/server/reserve.js.map +1 -1
  205. package/src/lib/server/reviews.js +3 -1
  206. package/src/lib/server/reviews.js.map +1 -1
  207. package/src/lib/server/site-page-resolver.js +5 -2
  208. package/src/lib/server/site-page-resolver.js.map +1 -1
  209. package/src/lib/server.js +26 -0
  210. package/src/lib/server.js.map +1 -1
  211. package/src/lib/testing/fake-firestore.js +72 -0
  212. package/src/lib/testing/fake-firestore.js.map +1 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/model/commerce-pos-ops.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { apportionCents, type HostOrder } from './commerce-orders'\nimport { orderPayments } from './commerce-pos'\nimport type { PrintReport } from './commerce-printers'\nimport { receiptDataFromOrder } from './commerce-receipt'\n\n/*==========================================\n * RUNNING THE REGISTER (AGL-3609): shifts and the cash drawer, register\n * returns and the receipt. Pure and client-safe: the routes, the register\n * and the reports all compute from these, so a drawer's expected cash is one\n * formula wherever it is shown.\n *\n * Where each thing lives:\n *\n * hosts/{hostId}/registers/{registerId}\n * `openShiftId` — the one open shift, claimed in a transaction\n * hosts/{hostId}/registers/{registerId}/shifts/{shiftId} {@link PosShift}\n * hosts/{hostId}/registers/{registerId}/returns/{returnId} {@link PosRegisterReturn}\n * hosts/{hostId}/posStaffPins/{uid} server-only\n *\n * Everything under `registers` is written by the Admin SDK only (the\n * register pool is billed), and every member of the site may read it.\n *=========================================*/\n\n/** How one payment toward a sale was taken, in the register's tender words. */\nexport type PosTenderMethod =\n | 'cash'\n | 'card_present'\n | 'card_keyed'\n | 'card_link'\n | 'gift_card'\n | 'folio'\n /** Store credit another plugin keeps (AGL-3640). */\n | 'credit'\n\nexport const POS_TENDER_METHODS: readonly PosTenderMethod[] = [\n 'cash',\n 'card_present',\n 'card_keyed',\n 'card_link',\n 'gift_card',\n 'folio',\n 'credit',\n]\n\n/** What each tender is called on a report, a receipt and a refund. */\nexport const POS_TENDER_LABELS: Readonly<Record<PosTenderMethod, string>> = {\n cash: 'Cash',\n card_present: 'Card',\n card_keyed: 'Card (keyed)',\n card_link: 'Card (payment link)',\n gift_card: 'Gift card',\n folio: 'Room charge',\n credit: 'Store credit',\n}\n\n/** The tenders whose money moved through Stripe and goes back through it. */\nexport const POS_STRIPE_TENDERS: ReadonlySet<PosTenderMethod> = new Set([\n 'card_present',\n 'card_keyed',\n 'card_link',\n])\n\n/**\n * One settled payment toward a sale, as the shift report and a register\n * return read it.\n */\nexport interface PosOrderTender {\n /** The payment's id on the order, or `legacy` for an order before the tender ledger. */\n id: string\n method: PosTenderMethod\n /** Cents put toward the sale; never the tip, never the change. */\n amountCents: number\n tipCents: number\n paymentIntentId?: string\n giftCardId?: string\n reservationId?: string\n /** For `credit` (AGL-3640): the provider, its account handle, and its name. */\n creditProviderId?: string\n creditReference?: string\n creditLabel?: string\n cardBrand?: string\n last4?: string\n cashTenderedCents?: number\n changeCents?: number\n}\n\nconst wholeCents = (value: unknown): number => {\n const number = Math.round(Number(value))\n return Number.isFinite(number) && number > 0 ? number : 0\n}\n\n\nconst isTenderMethod = (value: unknown): value is PosTenderMethod =>\n typeof value === 'string' && (POS_TENDER_METHODS as readonly string[]).includes(value)\n\n/** The order fields the tender reader looks at. */\nexport interface PosTenderSource {\n payments?: unknown\n channel?: string\n status?: string\n totals?: { totalCents?: number; tipCents?: number } | null\n amountCents?: number\n reservationId?: string\n checkoutSessionId?: string\n paymentIntentId?: string\n}\n\n/**\n * THE ONE PLACE THE TENDER LEDGER IS READ (AGL-3609).\n *\n * A register sale may carry `payments[]` — one entry per tender, the ledger\n * the split-tender register writes (AGL-3607) — and only the payments that\n * SUCCEEDED count: a declined card or a voided gift card took nothing. An\n * order written before that ledger is one payment, inferred the way the\n * ledger's own reader infers it: a folio when it names a reservation, a card\n * link when Stripe saw it (or it came through the storefront), cash otherwise.\n *\n * The ledger's own reader (`orderPayments`) does both; this narrows it to the\n * settled payments and the fields a report or a refund reads.\n */\nexport function posOrderTenders(order: PosTenderSource | null | undefined): PosOrderTender[] {\n if (!order) return []\n return orderPayments(order as Parameters<typeof orderPayments>[0])\n .filter((payment) => payment.status === 'succeeded' && isTenderMethod(payment.method))\n .map((payment) => ({\n id: payment.id,\n method: payment.method as PosTenderMethod,\n amountCents: wholeCents(payment.amountCents),\n tipCents: wholeCents(payment.tipCents ?? (payment.id === 'legacy' ? order.totals?.tipCents : 0)),\n ...(payment.paymentIntentId ? { paymentIntentId: payment.paymentIntentId } : {}),\n ...(payment.giftCardId ? { giftCardId: payment.giftCardId } : {}),\n ...(payment.reservationId ? { reservationId: payment.reservationId } : {}),\n ...(payment.creditProviderId ? { creditProviderId: payment.creditProviderId } : {}),\n ...(payment.creditReference ? { creditReference: payment.creditReference } : {}),\n ...(payment.creditLabel ? { creditLabel: payment.creditLabel } : {}),\n ...(payment.cardBrand ? { cardBrand: payment.cardBrand } : {}),\n ...(payment.last4 ? { last4: payment.last4 } : {}),\n ...(payment.cashTenderedCents != null ? { cashTenderedCents: wholeCents(payment.cashTenderedCents) } : {}),\n ...(payment.changeCents != null ? { changeCents: wholeCents(payment.changeCents) } : {}),\n }))\n}\n\n/** A tender as one line of a receipt: `Card •• 4242`. */\nexport function posTenderLabel(\n tender: Pick<PosOrderTender, 'method' | 'cardBrand' | 'last4'> & { creditLabel?: string },\n): string {\n const base = (tender.method === 'credit' && tender.creditLabel) || POS_TENDER_LABELS[tender.method] || tender.method\n if (tender.last4) {\n return `${tender.cardBrand ? capitalize(tender.cardBrand) : base} •• ${tender.last4}`\n }\n return base\n}\n\nfunction capitalize(value: string): string {\n return value ? value.charAt(0).toUpperCase() + value.slice(1) : value\n}\n\n/*==========================================\n * SHIFTS AND THE CASH DRAWER\n *=========================================*/\n\n/**\n * Cash that moved through the drawer without a sale: money put in, money\n * paid out for a reason (a delivery, petty cash), a drop to the safe, and\n * a register return paid out in cash.\n */\nexport type PosCashEventType = 'paid_in' | 'paid_out' | 'drop' | 'refund'\n\nexport const POS_CASH_EVENT_LABELS: Readonly<Record<PosCashEventType, string>> = {\n paid_in: 'Paid in',\n paid_out: 'Paid out',\n drop: 'Safe drop',\n refund: 'Cash refund',\n}\n\nexport interface PosCashEvent {\n /** The register's attempt id, so a retried tap records one event. */\n id: string\n type: PosCashEventType\n amountCents: number\n reason: string\n /** The member who moved the cash. */\n by: string\n atMs: number\n /** The order a `refund` paid out against. */\n orderId?: string\n}\n\n/** Sales, refunds and cash movements over one shift. */\nexport interface PosShiftReport {\n orderCount: number\n /** What customers paid for the goods: order totals, tax in, tips out. */\n grossSalesCents: number\n discountsCents: number\n taxCents: number\n tipsCents: number\n salesByTender: Partial<Record<PosTenderMethod, number>>\n refundCount: number\n refundsCents: number\n refundsByTender: Partial<Record<PosTenderMethod, number>>\n /** Gross sales less refunds. */\n netSalesCents: number\n openingFloatCents: number\n cashSalesCents: number\n paidInCents: number\n paidOutCents: number\n dropsCents: number\n cashRefundsCents: number\n /** float + cash sales + paid in − paid out − drops − cash refunds. */\n expectedCashCents: number\n /** More sales were stamped with the shift than the report could read. */\n truncated?: boolean\n}\n\n/** `hosts/{hostId}/registers/{registerId}/shifts/{shiftId}`. */\nexport interface PosShift {\n hostId: string\n registerId: string\n status: 'open' | 'closed'\n openedBy: string\n /** The member's name when the shift opened, for the history and the export. */\n openedByName?: string\n openedAtMs: number\n openingFloatCents: number\n cashEvents: PosCashEvent[]\n closedBy?: string\n closedByName?: string\n /*\n * The history sorts by each of the five below (AGL-3680), and an `orderBy`\n * drops a shift that lacks the field, so the shift is opened with each one\n * `null` and the close fills them — see `POS_SHIFT_LIST_QUERY`.\n */\n closedAtMs?: number | null\n /** What the closing cashier counted in the drawer. */\n countedCashCents?: number | null\n expectedCashCents?: number | null\n /** counted − expected: positive is over, negative is short. */\n varianceCents?: number | null\n /** The Z report's `netSalesCents`, flattened so the history can order by it. */\n netSalesCents?: number | null\n closingNote?: string\n /** The Z report, frozen when the shift closed. */\n report?: PosShiftReport\n}\n\n/** The cash-event sums one formula reads. */\nexport function posCashEventTotals(events: readonly PosCashEvent[] | null | undefined): {\n paidInCents: number\n paidOutCents: number\n dropsCents: number\n cashRefundsCents: number\n} {\n const sum = (type: PosCashEventType) =>\n (events ?? [])\n .filter((event) => event?.type === type)\n .reduce((total, event) => total + wholeCents(event.amountCents), 0)\n return {\n paidInCents: sum('paid_in'),\n paidOutCents: sum('paid_out'),\n dropsCents: sum('drop'),\n cashRefundsCents: sum('refund'),\n }\n}\n\n/**\n * THE DRAWER'S EXPECTED CASH: what should be in it when it is counted.\n *\n * opening float + cash sales + paid in − paid out − safe drops − cash refunds\n *\n * Cash SALES are the cash tender's `amountCents`, which is what the sale took\n * and never what the customer handed over: the change went straight back out\n * of the same drawer. A tip left in cash is in the drawer too, so it counts.\n */\nexport function posExpectedCashCents(input: {\n openingFloatCents: number\n cashSalesCents: number\n paidInCents: number\n paidOutCents: number\n dropsCents: number\n cashRefundsCents: number\n}): number {\n return (\n wholeCents(input.openingFloatCents) +\n wholeCents(input.cashSalesCents) +\n wholeCents(input.paidInCents) -\n wholeCents(input.paidOutCents) -\n wholeCents(input.dropsCents) -\n wholeCents(input.cashRefundsCents)\n )\n}\n\n/** A sale as the shift report reads it. */\nexport interface PosShiftSale extends PosTenderSource {\n totals?: {\n totalCents?: number\n discountCents?: number\n taxCents?: number\n tipCents?: number\n } | null\n}\n\n/** A register return as the shift report reads it. */\nexport interface PosShiftRefund {\n refundedCents: number\n tenders: Array<{ method: PosTenderMethod; amountCents: number }>\n}\n\n/**\n * The X report (mid-shift) and the Z report (at close) are this one function\n * over the sales stamped with the shift, the returns rung at the register\n * during it and the shift's own cash events. A pending sale took no money and\n * a cancelled one gave it back, so neither counts; a sale refunded LATER\n * still counts as a sale, and its refund is counted where it was paid out.\n */\nexport function computePosShiftReport(input: {\n shift: Pick<PosShift, 'openingFloatCents' | 'cashEvents'>\n sales: readonly PosShiftSale[]\n refunds: readonly PosShiftRefund[]\n truncated?: boolean\n}): PosShiftReport {\n const salesByTender: Partial<Record<PosTenderMethod, number>> = {}\n const refundsByTender: Partial<Record<PosTenderMethod, number>> = {}\n let orderCount = 0\n let grossSalesCents = 0\n let discountsCents = 0\n let taxCents = 0\n let tipsCents = 0\n let cashSalesCents = 0\n for (const sale of input.sales) {\n if (sale.status === 'pending' || sale.status === 'cancelled') continue\n const tenders = posOrderTenders(sale)\n orderCount += 1\n grossSalesCents += wholeCents(sale.totals?.totalCents ?? sale.amountCents)\n discountsCents += wholeCents(sale.totals?.discountCents)\n taxCents += wholeCents(sale.totals?.taxCents)\n const tenderTips = tenders.reduce((sum, tender) => sum + tender.tipCents, 0)\n tipsCents += Math.max(wholeCents(sale.totals?.tipCents), tenderTips)\n for (const tender of tenders) {\n salesByTender[tender.method] = (salesByTender[tender.method] ?? 0) + tender.amountCents\n if (tender.method === 'cash') cashSalesCents += tender.amountCents + tender.tipCents\n }\n }\n let refundsCents = 0\n for (const refund of input.refunds) {\n refundsCents += wholeCents(refund.refundedCents)\n for (const tender of refund.tenders ?? []) {\n if (!isTenderMethod(tender.method)) continue\n refundsByTender[tender.method] =\n (refundsByTender[tender.method] ?? 0) + wholeCents(tender.amountCents)\n }\n }\n const cash = posCashEventTotals(input.shift.cashEvents)\n const openingFloatCents = wholeCents(input.shift.openingFloatCents)\n return {\n orderCount,\n grossSalesCents,\n discountsCents,\n taxCents,\n tipsCents,\n salesByTender,\n refundCount: input.refunds.length,\n refundsCents,\n refundsByTender,\n netSalesCents: grossSalesCents - refundsCents,\n openingFloatCents,\n cashSalesCents,\n ...cash,\n expectedCashCents: posExpectedCashCents({ openingFloatCents, cashSalesCents, ...cash }),\n ...(input.truncated ? { truncated: true } : {}),\n }\n}\n\n/** counted − expected; positive is over, negative is short. */\nexport function posCashVarianceCents(countedCashCents: number, expectedCashCents: number): number {\n return Math.round(Number(countedCashCents) || 0) - Math.round(Number(expectedCashCents) || 0)\n}\n\n/** `$12.34`, `-$0.50` — the register's money format, store currency USD. */\nexport function posMoney(cents: number): string {\n const value = Math.round(Number(cents) || 0)\n return `${value < 0 ? '-' : ''}$${(Math.abs(value) / 100).toFixed(2)}`\n}\n\n/** One CSV cell, quoted when it needs to be and never read as a formula. */\nfunction csvCell(value: unknown): string {\n let cell = value == null ? '' : String(value)\n if (/^[=+\\-@\\t\\r]/.test(cell) && !/^-?\\d+(\\.\\d+)?$/.test(cell)) cell = `'${cell}`\n return /[\",\\r\\n]/.test(cell) ? `\"${cell.replace(/\"/g, '\"\"')}\"` : cell\n}\n\nconst dollars = (cents: number | null | undefined): string =>\n cents == null ? '' : (Math.round(cents) / 100).toFixed(2)\n\n/** The shift history as a spreadsheet: one row per shift, money in dollars. */\nexport function posShiftsCsv(\n shifts: ReadonlyArray<PosShift & { id: string; registerName?: string }>,\n memberName: (uid: string) => string = (uid) => uid,\n): string {\n const header = [\n 'Shift',\n 'Register',\n 'Opened',\n 'Opened by',\n 'Closed',\n 'Closed by',\n 'Orders',\n 'Gross sales',\n 'Refunds',\n 'Net sales',\n 'Discounts',\n 'Tax',\n 'Tips',\n 'Cash sales',\n 'Card sales',\n 'Gift card sales',\n 'Room charges',\n 'Opening float',\n 'Paid in',\n 'Paid out',\n 'Safe drops',\n 'Cash refunds',\n 'Expected cash',\n 'Counted cash',\n 'Variance',\n ]\n const iso = (ms: number | null | undefined) => (ms ? new Date(ms).toISOString() : '')\n const rows = shifts.map((shift) => {\n const report = shift.report\n const card = report\n ? (report.salesByTender.card_present ?? 0) +\n (report.salesByTender.card_keyed ?? 0) +\n (report.salesByTender.card_link ?? 0)\n : undefined\n return [\n shift.id,\n shift.registerName ?? shift.registerId,\n iso(shift.openedAtMs),\n shift.openedByName ?? memberName(shift.openedBy),\n iso(shift.closedAtMs),\n shift.closedBy ? (shift.closedByName ?? memberName(shift.closedBy)) : '',\n report?.orderCount ?? '',\n dollars(report?.grossSalesCents),\n dollars(report?.refundsCents),\n dollars(report?.netSalesCents),\n dollars(report?.discountsCents),\n dollars(report?.taxCents),\n dollars(report?.tipsCents),\n dollars(report?.cashSalesCents),\n dollars(card),\n dollars(report ? (report.salesByTender.gift_card ?? 0) : undefined),\n dollars(report ? (report.salesByTender.folio ?? 0) : undefined),\n dollars(shift.openingFloatCents),\n dollars(report?.paidInCents),\n dollars(report?.paidOutCents),\n dollars(report?.dropsCents),\n dollars(report?.cashRefundsCents),\n dollars(shift.expectedCashCents ?? report?.expectedCashCents),\n dollars(shift.countedCashCents),\n dollars(shift.varianceCents),\n ]\n })\n return [header, ...rows].map((row) => row.map(csvCell).join(',')).join('\\r\\n')\n}\n\n/*==========================================\n * RETURNS AT THE REGISTER\n *=========================================*/\n\n/** One line picked for return: its index on the order and how many units. */\nexport interface PosReturnPick {\n index: number\n quantity: number\n}\n\n/** The order fields a return reads. */\nexport interface PosReturnSource extends PosTenderSource {\n lineItems?: Array<{ unitAmountCents?: number; quantity?: number; name?: string }> | null\n totals?: {\n totalCents?: number\n discountCents?: number\n taxCents?: number\n shippingCents?: number\n tipCents?: number\n } | null\n refundedCents?: number\n refundedLineItemIds?: number[]\n /** Units of each line already returned at a register, by line index. */\n returnedQuantities?: Record<string, number>\n /** Cents already refunded to each payment by a register return. */\n posPaymentRefunds?: Record<string, number>\n}\n\n/**\n * What each line is worth back, whole: its price less its share of the\n * order's discount, plus its share of the order's tax — the same\n * apportionment `orderLineRefundCents` uses for the discount, extended to the\n * tax a register sale charged on top. Shipping and tips are not a line's.\n */\nexport function posReturnLineValues(order: PosReturnSource): number[] {\n const lines = order.lineItems ?? []\n const gross = lines.map(\n (line) =>\n Math.max(0, Math.round(Number(line?.unitAmountCents ?? 0))) *\n Math.max(1, Math.round(Number(line?.quantity ?? 1))),\n )\n const discountShares = apportionCents(gross, wholeCents(order.totals?.discountCents))\n const net = gross.map((cents, index) => Math.max(0, cents - (discountShares[index] ?? 0)))\n const taxShares = apportionCents(net, wholeCents(order.totals?.taxCents))\n return net.map((cents, index) => cents + (taxShares[index] ?? 0))\n}\n\n/** Units of each line already returned, a line refunded whole counting as all of it. */\nexport function posReturnedQuantities(order: PosReturnSource): number[] {\n const lines = order.lineItems ?? []\n const wholeLines = new Set(order.refundedLineItemIds ?? [])\n return lines.map((line, index) => {\n const quantity = Math.max(1, Math.round(Number(line?.quantity ?? 1)))\n if (wholeLines.has(index)) return quantity\n const returned = Math.round(Number(order.returnedQuantities?.[String(index)] ?? 0))\n return Math.min(quantity, Math.max(0, Number.isFinite(returned) ? returned : 0))\n })\n}\n\nexport type PosReturnPlan =\n | {\n ok: true\n refundCents: number\n /** Cents per picked line, in the order picked. */\n lineCents: number[]\n /** The order's per-line returned quantities after this return. */\n returnedQuantities: Record<string, number>\n /** Lines this return finishes, for `refundedLineItemIds`. */\n completedLines: number[]\n }\n | { ok: false; error: string }\n\n/**\n * What a return of these picks refunds, to the cent.\n *\n * Each line's value is split across its units CUMULATIVELY: returning `k` of\n * `n` units already returned `r` refunds `round(V·(r+k)/n) − round(V·r/n)`,\n * so returning a line one unit at a time refunds exactly what returning it\n * whole would have, and the rounding never strands or invents a cent. The\n * sum is capped at what is left to refund on the order — a refund given\n * from the order dialog already took its share.\n *\n * `heldElsewhere` is, per line, the units an online return (the returns\n * flow) already holds or took back. They cap what the register may take, but\n * they are not written into `returnedQuantities`, which counts only what the\n * register itself took.\n */\nexport function planPosReturn(\n order: PosReturnSource,\n picks: readonly PosReturnPick[],\n heldElsewhere: readonly number[] = [],\n): PosReturnPlan {\n const lines = order.lineItems ?? []\n if (!picks.length) return { ok: false, error: 'Pick at least one item to return.' }\n const values = posReturnLineValues(order)\n const returned = posReturnedQuantities(order)\n const seen = new Set<number>()\n const lineCents: number[] = []\n const nextReturned: Record<string, number> = {}\n for (const [index, count] of returned.entries()) if (count > 0) nextReturned[String(index)] = count\n const completedLines: number[] = []\n for (const pick of picks) {\n const index = Number(pick.index)\n const quantity = Number(pick.quantity)\n if (!Number.isInteger(index) || index < 0 || index >= lines.length) {\n return { ok: false, error: `Line ${pick.index} is not on this order.` }\n }\n if (seen.has(index)) return { ok: false, error: 'Each line can be picked once.' }\n seen.add(index)\n if (!Number.isInteger(quantity) || quantity < 1) {\n return { ok: false, error: 'Return at least one of each line you pick.' }\n }\n const total = Math.max(1, Math.round(Number(lines[index]?.quantity ?? 1)))\n const already = returned[index] ?? 0\n const held = Math.max(0, Math.round(Number(heldElsewhere[index] ?? 0)))\n if (already + held + quantity > total) {\n const left = Math.max(0, total - already - held)\n return {\n ok: false,\n error:\n left > 0\n ? `Only ${left} of \"${lines[index]?.name ?? 'that item'}\" can still be returned.`\n : held > 0\n ? `\"${lines[index]?.name ?? 'That item'}\" is already in a return. Finish it under Returns.`\n : `\"${lines[index]?.name ?? 'That item'}\" has already been returned.`,\n }\n }\n const value = values[index] ?? 0\n lineCents.push(\n Math.round((value * (already + quantity)) / total) - Math.round((value * already) / total),\n )\n nextReturned[String(index)] = already + quantity\n if (already + quantity === total) completedLines.push(index)\n }\n const remaining = Math.max(\n 0,\n wholeCents(order.totals?.totalCents) - wholeCents(order.refundedCents),\n )\n const asked = lineCents.reduce((sum, cents) => sum + cents, 0)\n const refundCents = Math.min(asked, remaining)\n if (!(refundCents > 0)) return { ok: false, error: 'Nothing is left to refund on this order.' }\n return { ok: true, refundCents, lineCents, returnedQuantities: nextReturned, completedLines }\n}\n\n/** One slice of a refund, sent back to one of the sale's payments. */\nexport interface PosRefundAllocation {\n paymentId: string\n method: PosTenderMethod\n amountCents: number\n}\n\n/**\n * What each payment can still take back: what it put toward the sale, less\n * what register returns already sent to it. A refund given from the order\n * dialog refunds through Stripe without naming a payment, so whatever it\n * took that no payment accounts for comes off the Stripe payments first.\n */\nexport function posRefundableByTender(order: PosReturnSource): Array<PosOrderTender & { refundableCents: number }> {\n const tenders = posOrderTenders(order)\n const attributed = Object.values(order.posPaymentRefunds ?? {}).reduce(\n (sum, cents) => sum + wholeCents(cents),\n 0,\n )\n let unattributed = Math.max(0, wholeCents(order.refundedCents) - attributed)\n const ordered = [...tenders].sort(\n (a, b) => Number(POS_STRIPE_TENDERS.has(b.method)) - Number(POS_STRIPE_TENDERS.has(a.method)),\n )\n const refundable = new Map<string, number>()\n for (const tender of ordered) {\n let left = Math.max(0, tender.amountCents - wholeCents(order.posPaymentRefunds?.[tender.id]))\n if (POS_STRIPE_TENDERS.has(tender.method) && unattributed > 0) {\n const taken = Math.min(left, unattributed)\n left -= taken\n unattributed -= taken\n }\n refundable.set(tender.id, left)\n }\n return tenders.map((tender) => ({ ...tender, refundableCents: refundable.get(tender.id) ?? 0 }))\n}\n\n/**\n * The order a refund goes back in, when the cashier does not split it by\n * hand: the cards first, then store credit, then the room, and the drawer\n * LAST — so cash leaves the till only for what the customer paid in cash.\n */\nconst REFUND_ORDER: readonly PosTenderMethod[] = [\n 'card_present',\n 'card_keyed',\n 'card_link',\n 'gift_card',\n 'credit',\n 'folio',\n 'cash',\n]\n\nexport type PosRefundSplit =\n | { ok: true; allocations: PosRefundAllocation[] }\n | { ok: false; error: string }\n\n/**\n * Sends `amountCents` back to the sale's own payments.\n *\n * With no `requested` split, each payment takes what it can in\n * {@link REFUND_ORDER}. With one, every slice must name a payment of this\n * sale and fit what that payment can still take, and the slices must add up\n * to the refund exactly — a refund is never sent to a tender the sale was\n * not paid with.\n */\nexport function splitPosRefund(\n order: PosReturnSource,\n amountCents: number,\n requested?: ReadonlyArray<{ paymentId: string; amountCents: number }> | null,\n): PosRefundSplit {\n const amount = wholeCents(amountCents)\n const tenders = posRefundableByTender(order)\n if (!tenders.length) return { ok: false, error: 'This order has no payment to refund to.' }\n if (requested && requested.length) {\n const allocations: PosRefundAllocation[] = []\n const used = new Set<string>()\n for (const slice of requested) {\n const tender = tenders.find((candidate) => candidate.id === slice.paymentId)\n if (!tender) return { ok: false, error: 'That payment is not on this order.' }\n if (used.has(tender.id)) return { ok: false, error: 'Each payment can be named once.' }\n used.add(tender.id)\n const cents = Math.round(Number(slice.amountCents))\n if (!Number.isFinite(cents) || cents < 0) return { ok: false, error: 'Refund amounts must be positive.' }\n if (cents === 0) continue\n if (cents > tender.refundableCents) {\n return {\n ok: false,\n error: `${posTenderLabel(tender)} can take back at most ${posMoney(tender.refundableCents)}.`,\n }\n }\n allocations.push({ paymentId: tender.id, method: tender.method, amountCents: cents })\n }\n const total = allocations.reduce((sum, slice) => sum + slice.amountCents, 0)\n if (total !== amount) {\n return {\n ok: false,\n error: `The refund is ${posMoney(amount)}; the split adds up to ${posMoney(total)}.`,\n }\n }\n return { ok: true, allocations }\n }\n const ordered = [...tenders].sort(\n (a, b) => REFUND_ORDER.indexOf(a.method) - REFUND_ORDER.indexOf(b.method),\n )\n const allocations: PosRefundAllocation[] = []\n let left = amount\n for (const tender of ordered) {\n if (!(left > 0)) break\n const cents = Math.min(left, tender.refundableCents)\n if (!(cents > 0)) continue\n allocations.push({ paymentId: tender.id, method: tender.method, amountCents: cents })\n left -= cents\n }\n if (left > 0) {\n return {\n ok: false,\n error: `Only ${posMoney(amount - left)} can go back to this sale's payments.`,\n }\n }\n return { ok: true, allocations }\n}\n\n/** `hosts/{hostId}/registers/{registerId}/returns/{returnId}`. */\nexport interface PosRegisterReturn {\n hostId: string\n registerId: string\n orderId: string\n orderNumber?: number\n /** The shift the drawer was in when the return was rung, if one was open. */\n shiftId?: string\n lines: Array<{ index: number; quantity: number; name: string; refundCents: number }>\n refundedCents: number\n tenders: Array<PosRefundAllocation & { status: 'refunded' | 'failed'; refundId?: string; error?: string }>\n restocked: boolean\n locationId?: string\n /** The member at the register (by PIN or by sign-in). */\n cashierId: string\n /** The manager whose PIN allowed a refund above the cashier's limit. */\n approvedBy?: string\n reason?: string\n status: 'refunded' | 'partial'\n atMs: number\n}\n\n/*==========================================\n * STAFF PINS\n *=========================================*/\n\n/** How long a cashier PIN switch lasts before the register asks again. */\nexport const POS_CASHIER_ASSERTION_TTL_MS = 15 * 60 * 1000\n/** A manager's approval is for the one refund in front of them. */\nexport const POS_MANAGER_ASSERTION_TTL_MS = 2 * 60 * 1000\n/** Wrong PINs in a row before the member is locked out. */\nexport const POS_PIN_MAX_ATTEMPTS = 5\n/** How long a lockout lasts. */\nexport const POS_PIN_LOCKOUT_MS = 15 * 60 * 1000\n\n/**\n * Why a PIN cannot be used, or `null` when it can: 4 to 6 digits, and not\n * one a stranger tries first — every digit the same, or a straight run.\n */\nexport function posPinProblem(pin: unknown): string | null {\n const value = typeof pin === 'string' ? pin : ''\n if (!/^\\d{4,6}$/.test(value)) return 'A PIN is 4 to 6 digits.'\n if (/^(\\d)\\1+$/.test(value)) return 'Pick a PIN that is not one digit repeated.'\n const digits = [...value].map(Number)\n const step = (digits[1] ?? 0) - (digits[0] ?? 0)\n if (\n (step === 1 || step === -1) &&\n digits.every((digit, index) => index === 0 || digit - (digits[index - 1] ?? 0) === step)\n ) {\n return 'Pick a PIN that is not a straight run like 1234.'\n }\n return null\n}\n\n/*==========================================\n * THE THERMAL RECEIPT\n *=========================================*/\n\nexport interface PosReceiptLine {\n name: string\n variantLabel?: string\n quantity: number\n unitAmountCents: number\n lineCents: number\n}\n\nexport interface PosReceipt {\n storeName: string\n logoUrl?: string\n addressLines: string[]\n orderNumber: string\n /** The digits the barcode encodes and the register's return lookup reads. */\n barcodeValue: string\n dateLabel: string\n cashierName?: string\n registerName?: string\n customerName?: string\n lines: PosReceiptLine[]\n subtotalCents: number\n discountCents: number\n taxCents: number\n totalCents: number\n tipCents: number\n tenders: Array<{ label: string; amountCents: number }>\n changeCents: number\n refundedCents: number\n footer?: string\n returnPolicy?: string\n /** A gift receipt: the items and nothing about what they cost. */\n gift: boolean\n}\n\n/** The order fields a receipt prints. */\nexport interface PosReceiptOrder extends PosReturnSource {\n number?: number\n createdAtMs?: number\n customerName?: string | null\n lineItems?: Array<{\n name?: string\n variantLabel?: string\n unitAmountCents?: number\n quantity?: number\n }> | null\n}\n\n/**\n * The receipt for one sale, from the order alone plus what the store prints\n * on every receipt. The sale's lines, totals, tenders and change are the\n * receipt model's (`receiptDataFromOrder`, the one the cloud printers print\n * from), so a browser print and a cloud print never disagree; this adds the\n * logo, the address, the return policy and the gift receipt. A gift receipt\n * keeps the lines and the barcode — the recipient's way to return — and\n * drops every price, total and tender.\n */\nexport function buildPosReceipt(input: {\n orderId: string\n order: PosReceiptOrder\n store: {\n name: string\n logoUrl?: string\n address?: string\n footer?: string\n returnPolicy?: string\n }\n cashierName?: string\n registerName?: string\n gift?: boolean\n /** How the date prints; the register's own locale by default. */\n formatDate?: (ms: number) => string\n}): PosReceipt {\n const { order } = input\n const gift = input.gift === true\n const data = receiptDataFromOrder(input.orderId, order as unknown as HostOrder & Record<string, any>, {\n storeName: input.store.name,\n })\n const lines: PosReceiptLine[] = data.lines.map((line) => ({\n name: line.name,\n ...(line.detail ? { variantLabel: line.detail } : {}),\n quantity: line.quantity,\n unitAmountCents: gift ? 0 : line.unitCents,\n lineCents: gift ? 0 : line.totalCents,\n }))\n const number = Number(order.number)\n const digits = Number.isInteger(number) && number > 0 ? String(number) : ''\n const createdAtMs = Number(order.createdAtMs) || 0\n const formatDate =\n input.formatDate ?? ((ms: number) => (ms ? new Date(ms).toLocaleString() : ''))\n const money = (cents: number | undefined) => (gift ? 0 : wholeCents(cents))\n return {\n storeName: input.store.name,\n ...(input.store.logoUrl ? { logoUrl: input.store.logoUrl } : {}),\n addressLines: String(input.store.address ?? '')\n .split(/\\r?\\n/)\n .map((line) => line.trim())\n .filter(Boolean),\n orderNumber: digits ? `#${digits}` : `#${input.orderId.slice(0, 8)}`,\n barcodeValue: digits || input.orderId,\n dateLabel: formatDate(createdAtMs),\n ...(input.cashierName ? { cashierName: input.cashierName } : {}),\n ...(input.registerName ? { registerName: input.registerName } : {}),\n ...(order.customerName ? { customerName: String(order.customerName) } : {}),\n lines,\n subtotalCents: money(data.subtotalCents),\n discountCents: money(data.discountCents),\n taxCents: money(data.taxCents),\n totalCents: money(data.totalCents),\n tipCents: money(data.tipCents),\n tenders: gift ? [] : (data.tenders ?? []),\n changeCents: money(data.changeCents),\n refundedCents: money(data.refundedCents),\n ...(input.store.footer ? { footer: input.store.footer } : {}),\n ...(input.store.returnPolicy ? { returnPolicy: input.store.returnPolicy } : {}),\n gift,\n }\n}\n\n/** One titled block of an X or Z report: what the screen, the browser print and a cloud printer all show. */\nexport interface PosShiftReportSection {\n section: string\n rows: Array<{ label: string; value: string; strong?: boolean }>\n}\n\n/** The rows of an X or Z report, in the order a manager reads them. */\nexport function posShiftReportRows(\n report: PosShiftReport,\n shift?: Pick<PosShift, 'countedCashCents' | 'varianceCents'>,\n): PosShiftReportSection[] {\n const tenders = POS_TENDER_METHODS.filter(\n (method) => (report.salesByTender[method] ?? 0) > 0 || (report.refundsByTender[method] ?? 0) > 0,\n )\n return [\n {\n section: 'Sales',\n rows: [\n { label: 'Orders', value: String(report.orderCount) },\n { label: 'Gross sales', value: posMoney(report.grossSalesCents) },\n { label: 'Discounts', value: posMoney(report.discountsCents) },\n { label: 'Tax', value: posMoney(report.taxCents) },\n { label: 'Tips', value: posMoney(report.tipsCents) },\n { label: `Refunds (${report.refundCount})`, value: `-${posMoney(report.refundsCents)}` },\n { label: 'Net sales', value: posMoney(report.netSalesCents), strong: true },\n ],\n },\n {\n section: 'By tender',\n rows: tenders.length\n ? tenders.map((method) => ({\n label: POS_TENDER_LABELS[method],\n value:\n posMoney(report.salesByTender[method] ?? 0) +\n ((report.refundsByTender[method] ?? 0) > 0\n ? ` (−${posMoney(report.refundsByTender[method] ?? 0)})`\n : ''),\n }))\n : [{ label: 'No sales yet', value: '' }],\n },\n {\n section: 'Cash drawer',\n rows: [\n { label: 'Starting cash', value: posMoney(report.openingFloatCents) },\n { label: 'Cash sales', value: posMoney(report.cashSalesCents) },\n { label: 'Paid in', value: posMoney(report.paidInCents) },\n { label: 'Paid out', value: `-${posMoney(report.paidOutCents)}` },\n { label: 'Safe drops', value: `-${posMoney(report.dropsCents)}` },\n { label: 'Cash refunds', value: `-${posMoney(report.cashRefundsCents)}` },\n { label: 'Expected in drawer', value: posMoney(report.expectedCashCents), strong: true },\n ...(shift?.countedCashCents != null\n ? [\n { label: 'Counted', value: posMoney(shift.countedCashCents) },\n {\n label: (shift.varianceCents ?? 0) < 0 ? 'Short' : (shift.varianceCents ?? 0) > 0 ? 'Over' : 'Balanced',\n value: posMoney(shift.varianceCents ?? 0),\n strong: true,\n },\n ]\n : []),\n ],\n },\n ]\n}\n\n/** A shift's X or Z report as the receipt printer prints it. */\nexport function posShiftPrintReport(input: {\n title: 'X REPORT' | 'Z REPORT'\n storeName: string\n registerName?: string\n report: PosShiftReport\n shift?: Pick<PosShift, 'countedCashCents' | 'varianceCents'>\n subtitle?: string\n}): PrintReport {\n return {\n title: input.title,\n storeName: input.storeName,\n ...(input.subtitle || input.registerName\n ? { subtitle: [input.registerName, input.subtitle].filter(Boolean).join(' - ') }\n : {}),\n sections: posShiftReportRows(input.report, input.shift),\n }\n}\n"],"names":["apportionCents","orderPayments","receiptDataFromOrder","POS_TENDER_METHODS","POS_TENDER_LABELS","cash","card_present","card_keyed","card_link","gift_card","folio","credit","POS_STRIPE_TENDERS","Set","wholeCents","value","number","Math","round","Number","isFinite","isTenderMethod","includes","posOrderTenders","order","filter","payment","status","method","map","id","amountCents","tipCents","totals","paymentIntentId","giftCardId","reservationId","creditProviderId","creditReference","creditLabel","cardBrand","last4","cashTenderedCents","changeCents","posTenderLabel","tender","base","capitalize","charAt","toUpperCase","slice","POS_CASH_EVENT_LABELS","paid_in","paid_out","drop","refund","posCashEventTotals","events","sum","type","event","reduce","total","paidInCents","paidOutCents","dropsCents","cashRefundsCents","posExpectedCashCents","input","openingFloatCents","cashSalesCents","computePosShiftReport","salesByTender","refundsByTender","orderCount","grossSalesCents","discountsCents","taxCents","tipsCents","sale","sales","tenders","totalCents","discountCents","tenderTips","max","refundsCents","refunds","refundedCents","shift","cashEvents","refundCount","length","netSalesCents","expectedCashCents","truncated","posCashVarianceCents","countedCashCents","posMoney","cents","abs","toFixed","csvCell","cell","String","test","replace","dollars","posShiftsCsv","shifts","memberName","uid","header","iso","ms","Date","toISOString","rows","report","card","undefined","registerName","registerId","openedAtMs","openedByName","openedBy","closedAtMs","closedBy","closedByName","varianceCents","row","join","posReturnLineValues","lines","lineItems","gross","line","unitAmountCents","quantity","discountShares","net","index","taxShares","posReturnedQuantities","wholeLines","refundedLineItemIds","has","returned","returnedQuantities","min","planPosReturn","picks","heldElsewhere","ok","error","values","seen","lineCents","nextReturned","count","entries","completedLines","pick","isInteger","add","already","held","left","name","push","remaining","asked","refundCents","posRefundableByTender","attributed","Object","posPaymentRefunds","unattributed","ordered","sort","a","b","refundable","Map","taken","set","refundableCents","get","REFUND_ORDER","splitPosRefund","requested","amount","allocations","used","find","candidate","paymentId","indexOf","POS_CASHIER_ASSERTION_TTL_MS","POS_MANAGER_ASSERTION_TTL_MS","POS_PIN_MAX_ATTEMPTS","POS_PIN_LOCKOUT_MS","posPinProblem","pin","digits","step","every","digit","buildPosReceipt","data","gift","orderId","storeName","store","detail","variantLabel","unitCents","createdAtMs","formatDate","toLocaleString","money","logoUrl","addressLines","address","split","trim","Boolean","orderNumber","barcodeValue","dateLabel","cashierName","customerName","subtotalCents","footer","returnPolicy","posShiftReportRows","section","label","strong","posShiftPrintReport","title","subtitle","sections"],"mappings":";AAAA;;;;;;;;;;;;;;;CAeC,GAED,SAASA,cAAc,QAAwB,uBAAmB;AAClE,SAASC,aAAa,QAAQ,oBAAgB;AAE9C,SAASC,oBAAoB,QAAQ,wBAAoB;AA+BzD,OAAO,MAAMC,qBAAiD;IAC5D;IACA;IACA;IACA;IACA;IACA;IACA;CACD,CAAA;AAED,oEAAoE,GACpE,OAAO,MAAMC,oBAA+D;IAC1EC,MAAM;IACNC,cAAc;IACdC,YAAY;IACZC,WAAW;IACXC,WAAW;IACXC,OAAO;IACPC,QAAQ;AACV,EAAC;AAED,2EAA2E,GAC3E,OAAO,MAAMC,qBAAmD,IAAIC,IAAI;IACtE;IACA;IACA;CACD,EAAC;AA0BF,MAAMC,aAAa,CAACC;IAClB,MAAMC,SAASC,KAAKC,KAAK,CAACC,OAAOJ;IACjC,OAAOI,OAAOC,QAAQ,CAACJ,WAAWA,SAAS,IAAIA,SAAS;AAC1D;AAGA,MAAMK,iBAAiB,CAACN,QACtB,OAAOA,UAAU,YAAY,AAACZ,mBAAyCmB,QAAQ,CAACP;AAclF;;;;;;;;;;;;CAYC,GACD,OAAO,SAASQ,gBAAgBC,KAAyC;IACvE,IAAI,CAACA,OAAO,OAAO,EAAE;IACrB,OAAOvB,cAAcuB,OAClBC,MAAM,CAAC,CAACC,UAAYA,QAAQC,MAAM,KAAK,eAAeN,eAAeK,QAAQE,MAAM,GACnFC,GAAG,CAAC,CAACH;YAIiBA;YAA+CF;eAJnD;YACjBM,IAAIJ,QAAQI,EAAE;YACdF,QAAQF,QAAQE,MAAM;YACtBG,aAAajB,WAAWY,QAAQK,WAAW;YAC3CC,UAAUlB,YAAWY,oBAAAA,QAAQM,QAAQ,YAAhBN,oBAAqBA,QAAQI,EAAE,KAAK,YAAWN,gBAAAA,MAAMS,MAAM,qBAAZT,cAAcQ,QAAQ,GAAG;WACzFN,QAAQQ,eAAe,GAAG;YAAEA,iBAAiBR,QAAQQ,eAAe;QAAC,IAAI,CAAC,GAC1ER,QAAQS,UAAU,GAAG;YAAEA,YAAYT,QAAQS,UAAU;QAAC,IAAI,CAAC,GAC3DT,QAAQU,aAAa,GAAG;YAAEA,eAAeV,QAAQU,aAAa;QAAC,IAAI,CAAC,GACpEV,QAAQW,gBAAgB,GAAG;YAAEA,kBAAkBX,QAAQW,gBAAgB;QAAC,IAAI,CAAC,GAC7EX,QAAQY,eAAe,GAAG;YAAEA,iBAAiBZ,QAAQY,eAAe;QAAC,IAAI,CAAC,GAC1EZ,QAAQa,WAAW,GAAG;YAAEA,aAAab,QAAQa,WAAW;QAAC,IAAI,CAAC,GAC9Db,QAAQc,SAAS,GAAG;YAAEA,WAAWd,QAAQc,SAAS;QAAC,IAAI,CAAC,GACxDd,QAAQe,KAAK,GAAG;YAAEA,OAAOf,QAAQe,KAAK;QAAC,IAAI,CAAC,GAC5Cf,QAAQgB,iBAAiB,IAAI,OAAO;YAAEA,mBAAmB5B,WAAWY,QAAQgB,iBAAiB;QAAE,IAAI,CAAC,GACpGhB,QAAQiB,WAAW,IAAI,OAAO;YAAEA,aAAa7B,WAAWY,QAAQiB,WAAW;QAAE,IAAI,CAAC;;AAE5F;AAEA,uDAAuD,GACvD,OAAO,SAASC,eACdC,MAAyF;IAEzF,MAAMC,OAAO,AAACD,OAAOjB,MAAM,KAAK,YAAYiB,OAAON,WAAW,IAAKnC,iBAAiB,CAACyC,OAAOjB,MAAM,CAAC,IAAIiB,OAAOjB,MAAM;IACpH,IAAIiB,OAAOJ,KAAK,EAAE;QAChB,OAAO,GAAGI,OAAOL,SAAS,GAAGO,WAAWF,OAAOL,SAAS,IAAIM,KAAK,IAAI,EAAED,OAAOJ,KAAK,EAAE;IACvF;IACA,OAAOK;AACT;AAEA,SAASC,WAAWhC,KAAa;IAC/B,OAAOA,QAAQA,MAAMiC,MAAM,CAAC,GAAGC,WAAW,KAAKlC,MAAMmC,KAAK,CAAC,KAAKnC;AAClE;AAaA,OAAO,MAAMoC,wBAAoE;IAC/EC,SAAS;IACTC,UAAU;IACVC,MAAM;IACNC,QAAQ;AACV,EAAC;AAwED,2CAA2C,GAC3C,OAAO,SAASC,mBAAmBC,MAAkD;IAMnF,MAAMC,MAAM,CAACC,OACX,CAACF,iBAAAA,SAAU,EAAE,EACVhC,MAAM,CAAC,CAACmC,QAAUA,CAAAA,yBAAAA,MAAOD,IAAI,MAAKA,MAClCE,MAAM,CAAC,CAACC,OAAOF,QAAUE,QAAQhD,WAAW8C,MAAM7B,WAAW,GAAG;IACrE,OAAO;QACLgC,aAAaL,IAAI;QACjBM,cAAcN,IAAI;QAClBO,YAAYP,IAAI;QAChBQ,kBAAkBR,IAAI;IACxB;AACF;AAEA;;;;;;;;CAQC,GACD,OAAO,SAASS,qBAAqBC,KAOpC;IACC,OACEtD,WAAWsD,MAAMC,iBAAiB,IAClCvD,WAAWsD,MAAME,cAAc,IAC/BxD,WAAWsD,MAAML,WAAW,IAC5BjD,WAAWsD,MAAMJ,YAAY,IAC7BlD,WAAWsD,MAAMH,UAAU,IAC3BnD,WAAWsD,MAAMF,gBAAgB;AAErC;AAkBA;;;;;;CAMC,GACD,OAAO,SAASK,sBAAsBH,KAKrC;IACC,MAAMI,gBAA0D,CAAC;IACjE,MAAMC,kBAA4D,CAAC;IACnE,IAAIC,aAAa;IACjB,IAAIC,kBAAkB;IACtB,IAAIC,iBAAiB;IACrB,IAAIC,WAAW;IACf,IAAIC,YAAY;IAChB,IAAIR,iBAAiB;IACrB,KAAK,MAAMS,QAAQX,MAAMY,KAAK,CAAE;;YAIAD,cACDA,eACNA,eAEUA;QAPjC,IAAIA,KAAKpD,MAAM,KAAK,aAAaoD,KAAKpD,MAAM,KAAK,aAAa;QAC9D,MAAMsD,UAAU1D,gBAAgBwD;QAChCL,cAAc;QACdC,mBAAmB7D,oBAAWiE,eAAAA,KAAK9C,MAAM,qBAAX8C,aAAaG,UAAU,mBAAIH,KAAKhD,WAAW;QACzE6C,kBAAkB9D,YAAWiE,gBAAAA,KAAK9C,MAAM,qBAAX8C,cAAaI,aAAa;QACvDN,YAAY/D,YAAWiE,gBAAAA,KAAK9C,MAAM,qBAAX8C,cAAaF,QAAQ;QAC5C,MAAMO,aAAaH,QAAQpB,MAAM,CAAC,CAACH,KAAKb,SAAWa,MAAMb,OAAOb,QAAQ,EAAE;QAC1E8C,aAAa7D,KAAKoE,GAAG,CAACvE,YAAWiE,gBAAAA,KAAK9C,MAAM,qBAAX8C,cAAa/C,QAAQ,GAAGoD;QACzD,KAAK,MAAMvC,UAAUoC,QAAS;gBACIT;YAAhCA,aAAa,CAAC3B,OAAOjB,MAAM,CAAC,GAAG,EAAC4C,+BAAAA,aAAa,CAAC3B,OAAOjB,MAAM,CAAC,YAA5B4C,+BAAgC,KAAK3B,OAAOd,WAAW;YACvF,IAAIc,OAAOjB,MAAM,KAAK,QAAQ0C,kBAAkBzB,OAAOd,WAAW,GAAGc,OAAOb,QAAQ;QACtF;IACF;IACA,IAAIsD,eAAe;IACnB,KAAK,MAAM/B,UAAUa,MAAMmB,OAAO,CAAE;YAEbhC;QADrB+B,gBAAgBxE,WAAWyC,OAAOiC,aAAa;QAC/C,KAAK,MAAM3C,WAAUU,kBAAAA,OAAO0B,OAAO,YAAd1B,kBAAkB,EAAE,CAAE;gBAGtCkB;YAFH,IAAI,CAACpD,eAAewB,OAAOjB,MAAM,GAAG;YACpC6C,eAAe,CAAC5B,OAAOjB,MAAM,CAAC,GAC5B,EAAC6C,iCAAAA,eAAe,CAAC5B,OAAOjB,MAAM,CAAC,YAA9B6C,iCAAkC,KAAK3D,WAAW+B,OAAOd,WAAW;QACzE;IACF;IACA,MAAM1B,OAAOmD,mBAAmBY,MAAMqB,KAAK,CAACC,UAAU;IACtD,MAAMrB,oBAAoBvD,WAAWsD,MAAMqB,KAAK,CAACpB,iBAAiB;IAClE,OAAO;QACLK;QACAC;QACAC;QACAC;QACAC;QACAN;QACAmB,aAAavB,MAAMmB,OAAO,CAACK,MAAM;QACjCN;QACAb;QACAoB,eAAelB,kBAAkBW;QACjCjB;QACAC;OACGjE;QACHyF,mBAAmB3B,qBAAqB;YAAEE;YAAmBC;WAAmBjE;OAC5E+D,MAAM2B,SAAS,GAAG;QAAEA,WAAW;IAAK,IAAI,CAAC;AAEjD;AAEA,6DAA6D,GAC7D,OAAO,SAASC,qBAAqBC,gBAAwB,EAAEH,iBAAyB;IACtF,OAAO7E,KAAKC,KAAK,CAACC,OAAO8E,qBAAqB,KAAKhF,KAAKC,KAAK,CAACC,OAAO2E,sBAAsB;AAC7F;AAEA,0EAA0E,GAC1E,OAAO,SAASI,SAASC,KAAa;IACpC,MAAMpF,QAAQE,KAAKC,KAAK,CAACC,OAAOgF,UAAU;IAC1C,OAAO,GAAGpF,QAAQ,IAAI,MAAM,GAAG,CAAC,EAAE,AAACE,CAAAA,KAAKmF,GAAG,CAACrF,SAAS,GAAE,EAAGsF,OAAO,CAAC,IAAI;AACxE;AAEA,0EAA0E,GAC1E,SAASC,QAAQvF,KAAc;IAC7B,IAAIwF,OAAOxF,SAAS,OAAO,KAAKyF,OAAOzF;IACvC,IAAI,eAAe0F,IAAI,CAACF,SAAS,CAAC,kBAAkBE,IAAI,CAACF,OAAOA,OAAO,CAAC,CAAC,EAAEA,MAAM;IACjF,OAAO,WAAWE,IAAI,CAACF,QAAQ,CAAC,CAAC,EAAEA,KAAKG,OAAO,CAAC,MAAM,MAAM,CAAC,CAAC,GAAGH;AACnE;AAEA,MAAMI,UAAU,CAACR,QACfA,SAAS,OAAO,KAAK,AAAClF,CAAAA,KAAKC,KAAK,CAACiF,SAAS,GAAE,EAAGE,OAAO,CAAC;AAEzD,6EAA6E,GAC7E,OAAO,SAASO,aACdC,MAAuE,EACvEC,aAAsC,CAACC,MAAQA,GAAG;IAElD,MAAMC,SAAS;QACb;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;KACD;IACD,MAAMC,MAAM,CAACC,KAAmCA,KAAK,IAAIC,KAAKD,IAAIE,WAAW,KAAK;IAClF,MAAMC,OAAOR,OAAOhF,GAAG,CAAC,CAAC4D;YAGlB6B,oCACAA,kCACAA,iCAIH7B,qBAEAA,qBAEkBA,2BAUA6B,iCACAA,6BAMV7B;QA7BV,MAAM6B,SAAS7B,MAAM6B,MAAM;QAC3B,MAAMC,OAAOD,SACT,EAACA,qCAAAA,OAAO9C,aAAa,CAAClE,YAAY,YAAjCgH,qCAAqC,OACrCA,mCAAAA,OAAO9C,aAAa,CAACjE,UAAU,YAA/B+G,mCAAmC,OACnCA,kCAAAA,OAAO9C,aAAa,CAAChE,SAAS,YAA9B8G,kCAAkC,KACnCE;QACJ,OAAO;YACL/B,MAAM3D,EAAE;aACR2D,sBAAAA,MAAMgC,YAAY,YAAlBhC,sBAAsBA,MAAMiC,UAAU;YACtCT,IAAIxB,MAAMkC,UAAU;aACpBlC,sBAAAA,MAAMmC,YAAY,YAAlBnC,sBAAsBqB,WAAWrB,MAAMoC,QAAQ;YAC/CZ,IAAIxB,MAAMqC,UAAU;YACpBrC,MAAMsC,QAAQ,IAAItC,sBAAAA,MAAMuC,YAAY,YAAlBvC,sBAAsBqB,WAAWrB,MAAMsC,QAAQ,IAAK;oBACtET,0BAAAA,OAAQ5C,UAAU,mBAAI;YACtBiC,QAAQW,0BAAAA,OAAQ3C,eAAe;YAC/BgC,QAAQW,0BAAAA,OAAQhC,YAAY;YAC5BqB,QAAQW,0BAAAA,OAAQzB,aAAa;YAC7Bc,QAAQW,0BAAAA,OAAQ1C,cAAc;YAC9B+B,QAAQW,0BAAAA,OAAQzC,QAAQ;YACxB8B,QAAQW,0BAAAA,OAAQxC,SAAS;YACzB6B,QAAQW,0BAAAA,OAAQhD,cAAc;YAC9BqC,QAAQY;YACRZ,QAAQW,UAAUA,kCAAAA,OAAO9C,aAAa,CAAC/D,SAAS,YAA9B6G,kCAAkC,IAAKE;YACzDb,QAAQW,UAAUA,8BAAAA,OAAO9C,aAAa,CAAC9D,KAAK,YAA1B4G,8BAA8B,IAAKE;YACrDb,QAAQlB,MAAMpB,iBAAiB;YAC/BsC,QAAQW,0BAAAA,OAAQvD,WAAW;YAC3B4C,QAAQW,0BAAAA,OAAQtD,YAAY;YAC5B2C,QAAQW,0BAAAA,OAAQrD,UAAU;YAC1B0C,QAAQW,0BAAAA,OAAQpD,gBAAgB;YAChCyC,SAAQlB,2BAAAA,MAAMK,iBAAiB,YAAvBL,2BAA2B6B,0BAAAA,OAAQxB,iBAAiB;YAC5Da,QAAQlB,MAAMQ,gBAAgB;YAC9BU,QAAQlB,MAAMwC,aAAa;SAC5B;IACH;IACA,OAAO;QAACjB;WAAWK;KAAK,CAACxF,GAAG,CAAC,CAACqG,MAAQA,IAAIrG,GAAG,CAACyE,SAAS6B,IAAI,CAAC,MAAMA,IAAI,CAAC;AACzE;AA8BA;;;;;CAKC,GACD,OAAO,SAASC,oBAAoB5G,KAAsB;QAC1CA;QAM0CA,eAEPA;IARjD,MAAM6G,SAAQ7G,mBAAAA,MAAM8G,SAAS,YAAf9G,mBAAmB,EAAE;IACnC,MAAM+G,QAAQF,MAAMxG,GAAG,CACrB,CAAC2G;;eACCvH,KAAKoE,GAAG,CAAC,GAAGpE,KAAKC,KAAK,CAACC,eAAOqH,wBAAAA,KAAMC,eAAe,mBAAI,OACvDxH,KAAKoE,GAAG,CAAC,GAAGpE,KAAKC,KAAK,CAACC,gBAAOqH,wBAAAA,KAAME,QAAQ,oBAAI;;IAEpD,MAAMC,iBAAiB3I,eAAeuI,OAAOzH,YAAWU,gBAAAA,MAAMS,MAAM,qBAAZT,cAAc2D,aAAa;IACnF,MAAMyD,MAAML,MAAM1G,GAAG,CAAC,CAACsE,OAAO0C;YAA+BF;eAArB1H,KAAKoE,GAAG,CAAC,GAAGc,UAASwC,wBAAAA,cAAc,CAACE,MAAM,YAArBF,wBAAyB;;IACtF,MAAMG,YAAY9I,eAAe4I,KAAK9H,YAAWU,iBAAAA,MAAMS,MAAM,qBAAZT,eAAcqD,QAAQ;IACvE,OAAO+D,IAAI/G,GAAG,CAAC,CAACsE,OAAO0C;YAAmBC;eAAT3C,UAAS2C,mBAAAA,SAAS,CAACD,MAAM,YAAhBC,mBAAoB;;AAChE;AAEA,sFAAsF,GACtF,OAAO,SAASC,sBAAsBvH,KAAsB;QAC5CA,kBACaA;IAD3B,MAAM6G,SAAQ7G,mBAAAA,MAAM8G,SAAS,YAAf9G,mBAAmB,EAAE;IACnC,MAAMwH,aAAa,IAAInI,KAAIW,6BAAAA,MAAMyH,mBAAmB,YAAzBzH,6BAA6B,EAAE;IAC1D,OAAO6G,MAAMxG,GAAG,CAAC,CAAC2G,MAAMK;;YAGarH;QAFnC,MAAMkH,WAAWzH,KAAKoE,GAAG,CAAC,GAAGpE,KAAKC,KAAK,CAACC,eAAOqH,wBAAAA,KAAME,QAAQ,mBAAI;QACjE,IAAIM,WAAWE,GAAG,CAACL,QAAQ,OAAOH;QAClC,MAAMS,WAAWlI,KAAKC,KAAK,CAACC,iBAAOK,4BAAAA,MAAM4H,kBAAkB,qBAAxB5H,yBAA0B,CAACgF,OAAOqC,OAAO,oBAAI;QAChF,OAAO5H,KAAKoI,GAAG,CAACX,UAAUzH,KAAKoE,GAAG,CAAC,GAAGlE,OAAOC,QAAQ,CAAC+H,YAAYA,WAAW;IAC/E;AACF;AAeA;;;;;;;;;;;;;;CAcC,GACD,OAAO,SAASG,cACd9H,KAAsB,EACtB+H,KAA+B,EAC/BC,gBAAmC,EAAE;QAEvBhI;QA4CDA;IA5Cb,MAAM6G,SAAQ7G,mBAAAA,MAAM8G,SAAS,YAAf9G,mBAAmB,EAAE;IACnC,IAAI,CAAC+H,MAAM3D,MAAM,EAAE,OAAO;QAAE6D,IAAI;QAAOC,OAAO;IAAoC;IAClF,MAAMC,SAASvB,oBAAoB5G;IACnC,MAAM2H,WAAWJ,sBAAsBvH;IACvC,MAAMoI,OAAO,IAAI/I;IACjB,MAAMgJ,YAAsB,EAAE;IAC9B,MAAMC,eAAuC,CAAC;IAC9C,KAAK,MAAM,CAACjB,OAAOkB,MAAM,IAAIZ,SAASa,OAAO,GAAI,IAAID,QAAQ,GAAGD,YAAY,CAACtD,OAAOqC,OAAO,GAAGkB;IAC9F,MAAME,iBAA2B,EAAE;IACnC,KAAK,MAAMC,QAAQX,MAAO;kBAYRJ,iBAC2BK,sBAa7BG;YAf8BtB;QAV5C,MAAMQ,QAAQ1H,OAAO+I,KAAKrB,KAAK;QAC/B,MAAMH,WAAWvH,OAAO+I,KAAKxB,QAAQ;QACrC,IAAI,CAACvH,OAAOgJ,SAAS,CAACtB,UAAUA,QAAQ,KAAKA,SAASR,MAAMzC,MAAM,EAAE;YAClE,OAAO;gBAAE6D,IAAI;gBAAOC,OAAO,CAAC,KAAK,EAAEQ,KAAKrB,KAAK,CAAC,sBAAsB,CAAC;YAAC;QACxE;QACA,IAAIe,KAAKV,GAAG,CAACL,QAAQ,OAAO;YAAEY,IAAI;YAAOC,OAAO;QAAgC;QAChFE,KAAKQ,GAAG,CAACvB;QACT,IAAI,CAAC1H,OAAOgJ,SAAS,CAACzB,aAAaA,WAAW,GAAG;YAC/C,OAAO;gBAAEe,IAAI;gBAAOC,OAAO;YAA6C;QAC1E;QACA,MAAM5F,QAAQ7C,KAAKoE,GAAG,CAAC,GAAGpE,KAAKC,KAAK,CAACC,gBAAOkH,eAAAA,KAAK,CAACQ,MAAM,qBAAZR,aAAcK,QAAQ,mBAAI;QACtE,MAAM2B,WAAUlB,kBAAAA,QAAQ,CAACN,MAAM,YAAfM,kBAAmB;QACnC,MAAMmB,OAAOrJ,KAAKoE,GAAG,CAAC,GAAGpE,KAAKC,KAAK,CAACC,QAAOqI,uBAAAA,aAAa,CAACX,MAAM,YAApBW,uBAAwB;QACnE,IAAIa,UAAUC,OAAO5B,WAAW5E,OAAO;;gBAMTuE,eAEdA,eACAA;YARd,MAAMkC,OAAOtJ,KAAKoE,GAAG,CAAC,GAAGvB,QAAQuG,UAAUC;YAC3C,OAAO;gBACLb,IAAI;gBACJC,OACEa,OAAO,IACH,CAAC,KAAK,EAAEA,KAAK,KAAK,YAAElC,gBAAAA,KAAK,CAACQ,MAAM,qBAAZR,cAAcmC,IAAI,oBAAI,YAAY,wBAAwB,CAAC,GAC/EF,OAAO,IACL,CAAC,CAAC,YAAEjC,gBAAAA,KAAK,CAACQ,MAAM,qBAAZR,cAAcmC,IAAI,oBAAI,YAAY,kDAAkD,CAAC,GACzF,CAAC,CAAC,YAAEnC,gBAAAA,KAAK,CAACQ,MAAM,qBAAZR,cAAcmC,IAAI,oBAAI,YAAY,4BAA4B,CAAC;YAC7E;QACF;QACA,MAAMzJ,SAAQ4I,gBAAAA,MAAM,CAACd,MAAM,YAAbc,gBAAiB;QAC/BE,UAAUY,IAAI,CACZxJ,KAAKC,KAAK,CAAC,AAACH,QAASsJ,CAAAA,UAAU3B,QAAO,IAAM5E,SAAS7C,KAAKC,KAAK,CAAC,AAACH,QAAQsJ,UAAWvG;QAEtFgG,YAAY,CAACtD,OAAOqC,OAAO,GAAGwB,UAAU3B;QACxC,IAAI2B,UAAU3B,aAAa5E,OAAOmG,eAAeQ,IAAI,CAAC5B;IACxD;IACA,MAAM6B,YAAYzJ,KAAKoE,GAAG,CACxB,GACAvE,YAAWU,gBAAAA,MAAMS,MAAM,qBAAZT,cAAc0D,UAAU,IAAIpE,WAAWU,MAAMgE,aAAa;IAEvE,MAAMmF,QAAQd,UAAUhG,MAAM,CAAC,CAACH,KAAKyC,QAAUzC,MAAMyC,OAAO;IAC5D,MAAMyE,cAAc3J,KAAKoI,GAAG,CAACsB,OAAOD;IACpC,IAAI,CAAEE,CAAAA,cAAc,CAAA,GAAI,OAAO;QAAEnB,IAAI;QAAOC,OAAO;IAA2C;IAC9F,OAAO;QAAED,IAAI;QAAMmB;QAAaf;QAAWT,oBAAoBU;QAAcG;IAAe;AAC9F;AASA;;;;;CAKC,GACD,OAAO,SAASY,sBAAsBrJ,KAAsB;QAEzBA;IADjC,MAAMyD,UAAU1D,gBAAgBC;IAChC,MAAMsJ,aAAaC,OAAOpB,MAAM,EAACnI,2BAAAA,MAAMwJ,iBAAiB,YAAvBxJ,2BAA2B,CAAC,GAAGqC,MAAM,CACpE,CAACH,KAAKyC,QAAUzC,MAAM5C,WAAWqF,QACjC;IAEF,IAAI8E,eAAehK,KAAKoE,GAAG,CAAC,GAAGvE,WAAWU,MAAMgE,aAAa,IAAIsF;IACjE,MAAMI,UAAU;WAAIjG;KAAQ,CAACkG,IAAI,CAC/B,CAACC,GAAGC,IAAMlK,OAAOP,mBAAmBsI,GAAG,CAACmC,EAAEzJ,MAAM,KAAKT,OAAOP,mBAAmBsI,GAAG,CAACkC,EAAExJ,MAAM;IAE7F,MAAM0J,aAAa,IAAIC;IACvB,KAAK,MAAM1I,UAAUqI,QAAS;YAC2B1J;QAAvD,IAAI+I,OAAOtJ,KAAKoE,GAAG,CAAC,GAAGxC,OAAOd,WAAW,GAAGjB,YAAWU,4BAAAA,MAAMwJ,iBAAiB,qBAAvBxJ,yBAAyB,CAACqB,OAAOf,EAAE,CAAC;QAC3F,IAAIlB,mBAAmBsI,GAAG,CAACrG,OAAOjB,MAAM,KAAKqJ,eAAe,GAAG;YAC7D,MAAMO,QAAQvK,KAAKoI,GAAG,CAACkB,MAAMU;YAC7BV,QAAQiB;YACRP,gBAAgBO;QAClB;QACAF,WAAWG,GAAG,CAAC5I,OAAOf,EAAE,EAAEyI;IAC5B;IACA,OAAOtF,QAAQpD,GAAG,CAAC,CAACgB;YAA0CyI;eAA9B,aAAKzI;YAAQ6I,eAAe,GAAEJ,kBAAAA,WAAWK,GAAG,CAAC9I,OAAOf,EAAE,aAAxBwJ,kBAA6B;;;AAC7F;AAEA;;;;CAIC,GACD,MAAMM,eAA2C;IAC/C;IACA;IACA;IACA;IACA;IACA;IACA;CACD;AAMD;;;;;;;;CAQC,GACD,OAAO,SAASC,eACdrK,KAAsB,EACtBO,WAAmB,EACnB+J,SAA4E;IAE5E,MAAMC,SAASjL,WAAWiB;IAC1B,MAAMkD,UAAU4F,sBAAsBrJ;IACtC,IAAI,CAACyD,QAAQW,MAAM,EAAE,OAAO;QAAE6D,IAAI;QAAOC,OAAO;IAA0C;IAC1F,IAAIoC,aAAaA,UAAUlG,MAAM,EAAE;QACjC,MAAMoG,cAAqC,EAAE;QAC7C,MAAMC,OAAO,IAAIpL;QACjB,KAAK,MAAMqC,SAAS4I,UAAW;YAC7B,MAAMjJ,SAASoC,QAAQiH,IAAI,CAAC,CAACC,YAAcA,UAAUrK,EAAE,KAAKoB,MAAMkJ,SAAS;YAC3E,IAAI,CAACvJ,QAAQ,OAAO;gBAAE4G,IAAI;gBAAOC,OAAO;YAAqC;YAC7E,IAAIuC,KAAK/C,GAAG,CAACrG,OAAOf,EAAE,GAAG,OAAO;gBAAE2H,IAAI;gBAAOC,OAAO;YAAkC;YACtFuC,KAAK7B,GAAG,CAACvH,OAAOf,EAAE;YAClB,MAAMqE,QAAQlF,KAAKC,KAAK,CAACC,OAAO+B,MAAMnB,WAAW;YACjD,IAAI,CAACZ,OAAOC,QAAQ,CAAC+E,UAAUA,QAAQ,GAAG,OAAO;gBAAEsD,IAAI;gBAAOC,OAAO;YAAmC;YACxG,IAAIvD,UAAU,GAAG;YACjB,IAAIA,QAAQtD,OAAO6I,eAAe,EAAE;gBAClC,OAAO;oBACLjC,IAAI;oBACJC,OAAO,GAAG9G,eAAeC,QAAQ,uBAAuB,EAAEqD,SAASrD,OAAO6I,eAAe,EAAE,CAAC,CAAC;gBAC/F;YACF;YACAM,YAAYvB,IAAI,CAAC;gBAAE2B,WAAWvJ,OAAOf,EAAE;gBAAEF,QAAQiB,OAAOjB,MAAM;gBAAEG,aAAaoE;YAAM;QACrF;QACA,MAAMrC,QAAQkI,YAAYnI,MAAM,CAAC,CAACH,KAAKR,QAAUQ,MAAMR,MAAMnB,WAAW,EAAE;QAC1E,IAAI+B,UAAUiI,QAAQ;YACpB,OAAO;gBACLtC,IAAI;gBACJC,OAAO,CAAC,cAAc,EAAExD,SAAS6F,QAAQ,uBAAuB,EAAE7F,SAASpC,OAAO,CAAC,CAAC;YACtF;QACF;QACA,OAAO;YAAE2F,IAAI;YAAMuC;QAAY;IACjC;IACA,MAAMd,UAAU;WAAIjG;KAAQ,CAACkG,IAAI,CAC/B,CAACC,GAAGC,IAAMO,aAAaS,OAAO,CAACjB,EAAExJ,MAAM,IAAIgK,aAAaS,OAAO,CAAChB,EAAEzJ,MAAM;IAE1E,MAAMoK,cAAqC,EAAE;IAC7C,IAAIzB,OAAOwB;IACX,KAAK,MAAMlJ,UAAUqI,QAAS;QAC5B,IAAI,CAAEX,CAAAA,OAAO,CAAA,GAAI;QACjB,MAAMpE,QAAQlF,KAAKoI,GAAG,CAACkB,MAAM1H,OAAO6I,eAAe;QACnD,IAAI,CAAEvF,CAAAA,QAAQ,CAAA,GAAI;QAClB6F,YAAYvB,IAAI,CAAC;YAAE2B,WAAWvJ,OAAOf,EAAE;YAAEF,QAAQiB,OAAOjB,MAAM;YAAEG,aAAaoE;QAAM;QACnFoE,QAAQpE;IACV;IACA,IAAIoE,OAAO,GAAG;QACZ,OAAO;YACLd,IAAI;YACJC,OAAO,CAAC,KAAK,EAAExD,SAAS6F,SAASxB,MAAM,qCAAqC,CAAC;QAC/E;IACF;IACA,OAAO;QAAEd,IAAI;QAAMuC;IAAY;AACjC;AAwBA;;2CAE2C,GAE3C,wEAAwE,GACxE,OAAO,MAAMM,+BAA+B,KAAK,KAAK,KAAI;AAC1D,iEAAiE,GACjE,OAAO,MAAMC,+BAA+B,IAAI,KAAK,KAAI;AACzD,yDAAyD,GACzD,OAAO,MAAMC,uBAAuB,EAAC;AACrC,8BAA8B,GAC9B,OAAO,MAAMC,qBAAqB,KAAK,KAAK,KAAI;AAEhD;;;CAGC,GACD,OAAO,SAASC,cAAcC,GAAY;QAK1BC,UAAmBA;IAJjC,MAAM7L,QAAQ,OAAO4L,QAAQ,WAAWA,MAAM;IAC9C,IAAI,CAAC,YAAYlG,IAAI,CAAC1F,QAAQ,OAAO;IACrC,IAAI,YAAY0F,IAAI,CAAC1F,QAAQ,OAAO;IACpC,MAAM6L,SAAS;WAAI7L;KAAM,CAACc,GAAG,CAACV;IAC9B,MAAM0L,OAAO,EAACD,WAAAA,MAAM,CAAC,EAAE,YAATA,WAAa,OAAMA,YAAAA,MAAM,CAAC,EAAE,YAATA,YAAa;IAC9C,IACE,AAACC,CAAAA,SAAS,KAAKA,SAAS,CAAC,CAAA,KACzBD,OAAOE,KAAK,CAAC,CAACC,OAAOlE;YAAkC+D;eAAxB/D,UAAU,KAAKkE,UAASH,WAAAA,MAAM,CAAC/D,QAAQ,EAAE,YAAjB+D,WAAqB,OAAOC;QACnF;QACA,OAAO;IACT;IACA,OAAO;AACT;AAqDA;;;;;;;;CAQC,GACD,OAAO,SAASG,gBAAgB5I,KAe/B;QAiBGA,mBAKqBA,sBAgBC6I;IArCxB,MAAM,EAAEzL,KAAK,EAAE,GAAG4C;IAClB,MAAM8I,OAAO9I,MAAM8I,IAAI,KAAK;IAC5B,MAAMD,OAAO/M,qBAAqBkE,MAAM+I,OAAO,EAAE3L,OAAqD;QACpG4L,WAAWhJ,MAAMiJ,KAAK,CAAC7C,IAAI;IAC7B;IACA,MAAMnC,QAA0B4E,KAAK5E,KAAK,CAACxG,GAAG,CAAC,CAAC2G,OAAU;YACxDgC,MAAMhC,KAAKgC,IAAI;WACXhC,KAAK8E,MAAM,GAAG;YAAEC,cAAc/E,KAAK8E,MAAM;QAAC,IAAI,CAAC;YACnD5E,UAAUF,KAAKE,QAAQ;YACvBD,iBAAiByE,OAAO,IAAI1E,KAAKgF,SAAS;YAC1C3D,WAAWqD,OAAO,IAAI1E,KAAKtD,UAAU;;IAEvC,MAAMlE,SAASG,OAAOK,MAAMR,MAAM;IAClC,MAAM4L,SAASzL,OAAOgJ,SAAS,CAACnJ,WAAWA,SAAS,IAAIwF,OAAOxF,UAAU;IACzE,MAAMyM,cAActM,OAAOK,MAAMiM,WAAW,KAAK;IACjD,MAAMC,cACJtJ,oBAAAA,MAAMsJ,UAAU,YAAhBtJ,oBAAqB,CAAC8C,KAAgBA,KAAK,IAAIC,KAAKD,IAAIyG,cAAc,KAAK;IAC7E,MAAMC,QAAQ,CAACzH,QAA+B+G,OAAO,IAAIpM,WAAWqF;IACpE,OAAO;QACLiH,WAAWhJ,MAAMiJ,KAAK,CAAC7C,IAAI;OACvBpG,MAAMiJ,KAAK,CAACQ,OAAO,GAAG;QAAEA,SAASzJ,MAAMiJ,KAAK,CAACQ,OAAO;IAAC,IAAI,CAAC;QAC9DC,cAActH,QAAOpC,uBAAAA,MAAMiJ,KAAK,CAACU,OAAO,YAAnB3J,uBAAuB,IACzC4J,KAAK,CAAC,SACNnM,GAAG,CAAC,CAAC2G,OAASA,KAAKyF,IAAI,IACvBxM,MAAM,CAACyM;QACVC,aAAavB,SAAS,CAAC,CAAC,EAAEA,QAAQ,GAAG,CAAC,CAAC,EAAExI,MAAM+I,OAAO,CAACjK,KAAK,CAAC,GAAG,IAAI;QACpEkL,cAAcxB,UAAUxI,MAAM+I,OAAO;QACrCkB,WAAWX,WAAWD;OAClBrJ,MAAMkK,WAAW,GAAG;QAAEA,aAAalK,MAAMkK,WAAW;IAAC,IAAI,CAAC,GAC1DlK,MAAMqD,YAAY,GAAG;QAAEA,cAAcrD,MAAMqD,YAAY;IAAC,IAAI,CAAC,GAC7DjG,MAAM+M,YAAY,GAAG;QAAEA,cAAc/H,OAAOhF,MAAM+M,YAAY;IAAE,IAAI,CAAC;QACzElG;QACAmG,eAAeZ,MAAMX,KAAKuB,aAAa;QACvCrJ,eAAeyI,MAAMX,KAAK9H,aAAa;QACvCN,UAAU+I,MAAMX,KAAKpI,QAAQ;QAC7BK,YAAY0I,MAAMX,KAAK/H,UAAU;QACjClD,UAAU4L,MAAMX,KAAKjL,QAAQ;QAC7BiD,SAASiI,OAAO,EAAE,IAAID,gBAAAA,KAAKhI,OAAO,YAAZgI,gBAAgB,EAAE;QACxCtK,aAAaiL,MAAMX,KAAKtK,WAAW;QACnC6C,eAAeoI,MAAMX,KAAKzH,aAAa;OACnCpB,MAAMiJ,KAAK,CAACoB,MAAM,GAAG;QAAEA,QAAQrK,MAAMiJ,KAAK,CAACoB,MAAM;IAAC,IAAI,CAAC,GACvDrK,MAAMiJ,KAAK,CAACqB,YAAY,GAAG;QAAEA,cAActK,MAAMiJ,KAAK,CAACqB,YAAY;IAAC,IAAI,CAAC;QAC7ExB;;AAEJ;AAQA,qEAAqE,GACrE,OAAO,SAASyB,mBACdrH,MAAsB,EACtB7B,KAA4D;QA6CtCA,sBAA2CA,uBACnCA;IA5C9B,MAAMR,UAAU9E,mBAAmBsB,MAAM,CACvC,CAACG;YAAY0F,8BAA2CA;eAA5C,EAACA,+BAAAA,OAAO9C,aAAa,CAAC5C,OAAO,YAA5B0F,+BAAgC,KAAK,KAAK,EAACA,iCAAAA,OAAO7C,eAAe,CAAC7C,OAAO,YAA9B0F,iCAAkC,KAAK;;IAEjG,OAAO;QACL;YACEsH,SAAS;YACTvH,MAAM;gBACJ;oBAAEwH,OAAO;oBAAU9N,OAAOyF,OAAOc,OAAO5C,UAAU;gBAAE;gBACpD;oBAAEmK,OAAO;oBAAe9N,OAAOmF,SAASoB,OAAO3C,eAAe;gBAAE;gBAChE;oBAAEkK,OAAO;oBAAa9N,OAAOmF,SAASoB,OAAO1C,cAAc;gBAAE;gBAC7D;oBAAEiK,OAAO;oBAAO9N,OAAOmF,SAASoB,OAAOzC,QAAQ;gBAAE;gBACjD;oBAAEgK,OAAO;oBAAQ9N,OAAOmF,SAASoB,OAAOxC,SAAS;gBAAE;gBACnD;oBAAE+J,OAAO,CAAC,SAAS,EAAEvH,OAAO3B,WAAW,CAAC,CAAC,CAAC;oBAAE5E,OAAO,CAAC,CAAC,EAAEmF,SAASoB,OAAOhC,YAAY,GAAG;gBAAC;gBACvF;oBAAEuJ,OAAO;oBAAa9N,OAAOmF,SAASoB,OAAOzB,aAAa;oBAAGiJ,QAAQ;gBAAK;aAC3E;QACH;QACA;YACEF,SAAS;YACTvH,MAAMpC,QAAQW,MAAM,GAChBX,QAAQpD,GAAG,CAAC,CAACD;oBAGA0F,8BACPA,gCACiBA;uBALE;oBACvBuH,OAAOzO,iBAAiB,CAACwB,OAAO;oBAChCb,OACEmF,UAASoB,+BAAAA,OAAO9C,aAAa,CAAC5C,OAAO,YAA5B0F,+BAAgC,KACxC,CAAA,EAACA,iCAAAA,OAAO7C,eAAe,CAAC7C,OAAO,YAA9B0F,iCAAkC,KAAK,IACrC,CAAC,GAAG,EAAEpB,UAASoB,kCAAAA,OAAO7C,eAAe,CAAC7C,OAAO,YAA9B0F,kCAAkC,GAAG,CAAC,CAAC,GACtD,EAAC;gBACT;iBACA;gBAAC;oBAAEuH,OAAO;oBAAgB9N,OAAO;gBAAG;aAAE;QAC5C;QACA;YACE6N,SAAS;YACTvH,MAAM;gBACJ;oBAAEwH,OAAO;oBAAiB9N,OAAOmF,SAASoB,OAAOjD,iBAAiB;gBAAE;gBACpE;oBAAEwK,OAAO;oBAAc9N,OAAOmF,SAASoB,OAAOhD,cAAc;gBAAE;gBAC9D;oBAAEuK,OAAO;oBAAW9N,OAAOmF,SAASoB,OAAOvD,WAAW;gBAAE;gBACxD;oBAAE8K,OAAO;oBAAY9N,OAAO,CAAC,CAAC,EAAEmF,SAASoB,OAAOtD,YAAY,GAAG;gBAAC;gBAChE;oBAAE6K,OAAO;oBAAc9N,OAAO,CAAC,CAAC,EAAEmF,SAASoB,OAAOrD,UAAU,GAAG;gBAAC;gBAChE;oBAAE4K,OAAO;oBAAgB9N,OAAO,CAAC,CAAC,EAAEmF,SAASoB,OAAOpD,gBAAgB,GAAG;gBAAC;gBACxE;oBAAE2K,OAAO;oBAAsB9N,OAAOmF,SAASoB,OAAOxB,iBAAiB;oBAAGgJ,QAAQ;gBAAK;mBACnFrJ,CAAAA,yBAAAA,MAAOQ,gBAAgB,KAAI,OAC3B;oBACE;wBAAE4I,OAAO;wBAAW9N,OAAOmF,SAAST,MAAMQ,gBAAgB;oBAAE;oBAC5D;wBACE4I,OAAO,EAACpJ,uBAAAA,MAAMwC,aAAa,YAAnBxC,uBAAuB,KAAK,IAAI,UAAU,EAACA,wBAAAA,MAAMwC,aAAa,YAAnBxC,wBAAuB,KAAK,IAAI,SAAS;wBAC5F1E,OAAOmF,UAAST,wBAAAA,MAAMwC,aAAa,YAAnBxC,wBAAuB;wBACvCqJ,QAAQ;oBACV;iBACD,GACD,EAAE;aACP;QACH;KACD;AACH;AAEA,8DAA8D,GAC9D,OAAO,SAASC,oBAAoB3K,KAOnC;IACC,OAAO;QACL4K,OAAO5K,MAAM4K,KAAK;QAClB5B,WAAWhJ,MAAMgJ,SAAS;OACtBhJ,MAAM6K,QAAQ,IAAI7K,MAAMqD,YAAY,GACpC;QAAEwH,UAAU;YAAC7K,MAAMqD,YAAY;YAAErD,MAAM6K,QAAQ;SAAC,CAACxN,MAAM,CAACyM,SAAS/F,IAAI,CAAC;IAAO,IAC7E,CAAC;QACL+G,UAAUP,mBAAmBvK,MAAMkD,MAAM,EAAElD,MAAMqB,KAAK;;AAE1D"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/model/commerce-pos-ops.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { apportionCents, type HostOrder } from './commerce-orders'\nimport { orderPayments } from './commerce-pos'\nimport type { PrintReport } from './commerce-printers'\nimport { receiptDataFromOrder } from './commerce-receipt'\n\n/*==========================================\n * RUNNING THE REGISTER (AGL-3609): shifts and the cash drawer, register\n * returns and the receipt. Pure and client-safe: the routes, the register\n * and the reports all compute from these, so a drawer's expected cash is one\n * formula wherever it is shown.\n *\n * Where each thing lives:\n *\n * hosts/{hostId}/registers/{registerId}\n * `openShiftId` — the one open shift, claimed in a transaction\n * hosts/{hostId}/registers/{registerId}/shifts/{shiftId} {@link PosShift}\n * hosts/{hostId}/registers/{registerId}/returns/{returnId} {@link PosRegisterReturn}\n * hosts/{hostId}/posStaffPins/{uid} server-only\n *\n * Everything under `registers` is written by the Admin SDK only (the\n * register pool is billed), and every member of the site may read it.\n *=========================================*/\n\n/** How one payment toward a sale was taken, in the register's tender words. */\nexport type PosTenderMethod =\n | 'cash'\n | 'card_present'\n | 'card_keyed'\n | 'card_link'\n | 'gift_card'\n | 'folio'\n /** Store credit another plugin keeps (AGL-3640). */\n | 'credit'\n\nexport const POS_TENDER_METHODS: readonly PosTenderMethod[] = [\n 'cash',\n 'card_present',\n 'card_keyed',\n 'card_link',\n 'gift_card',\n 'folio',\n 'credit',\n]\n\n/** What each tender is called on a report, a receipt and a refund. */\nexport const POS_TENDER_LABELS: Readonly<Record<PosTenderMethod, string>> = {\n cash: 'Cash',\n card_present: 'Card',\n card_keyed: 'Card (keyed)',\n card_link: 'Card (payment link)',\n gift_card: 'Gift card',\n folio: 'Room charge',\n credit: 'Store credit',\n}\n\n/** The tenders whose money moved through Stripe and goes back through it. */\nexport const POS_STRIPE_TENDERS: ReadonlySet<PosTenderMethod> = new Set([\n 'card_present',\n 'card_keyed',\n 'card_link',\n])\n\n/**\n * One settled payment toward a sale, as the shift report and a register\n * return read it.\n */\nexport interface PosOrderTender {\n /** The payment's id on the order, or `legacy` for an order before the tender ledger. */\n id: string\n method: PosTenderMethod\n /** Cents put toward the sale; never the tip, never the change. */\n amountCents: number\n tipCents: number\n paymentIntentId?: string\n giftCardId?: string\n reservationId?: string\n /** For `credit` (AGL-3640): the provider, its account handle, and its name. */\n creditProviderId?: string\n creditReference?: string\n creditLabel?: string\n cardBrand?: string\n last4?: string\n cashTenderedCents?: number\n changeCents?: number\n}\n\nconst wholeCents = (value: unknown): number => {\n const number = Math.round(Number(value))\n return Number.isFinite(number) && number > 0 ? number : 0\n}\n\n\nconst isTenderMethod = (value: unknown): value is PosTenderMethod =>\n typeof value === 'string' && (POS_TENDER_METHODS as readonly string[]).includes(value)\n\n/** The order fields the tender reader looks at. */\nexport interface PosTenderSource {\n payments?: unknown\n channel?: string\n status?: string\n totals?: { totalCents?: number; tipCents?: number } | null\n amountCents?: number\n reservationId?: string\n checkoutSessionId?: string\n paymentIntentId?: string\n}\n\n/**\n * THE ONE PLACE THE TENDER LEDGER IS READ (AGL-3609).\n *\n * A register sale may carry `payments[]` — one entry per tender, the ledger\n * the split-tender register writes (AGL-3607) — and only the payments that\n * SUCCEEDED count: a declined card or a voided gift card took nothing. An\n * order written before that ledger is one payment, inferred the way the\n * ledger's own reader infers it: a folio when it names a reservation, a card\n * link when Stripe saw it (or it came through the storefront), cash otherwise.\n *\n * The ledger's own reader (`orderPayments`) does both; this narrows it to the\n * settled payments and the fields a report or a refund reads.\n */\nexport function posOrderTenders(order: PosTenderSource | null | undefined): PosOrderTender[] {\n if (!order) return []\n return orderPayments(order as Parameters<typeof orderPayments>[0])\n .filter((payment) => payment.status === 'succeeded' && isTenderMethod(payment.method))\n .map((payment) => ({\n id: payment.id,\n method: payment.method as PosTenderMethod,\n amountCents: wholeCents(payment.amountCents),\n tipCents: wholeCents(payment.tipCents ?? (payment.id === 'legacy' ? order.totals?.tipCents : 0)),\n ...(payment.paymentIntentId ? { paymentIntentId: payment.paymentIntentId } : {}),\n ...(payment.giftCardId ? { giftCardId: payment.giftCardId } : {}),\n ...(payment.reservationId ? { reservationId: payment.reservationId } : {}),\n ...(payment.creditProviderId ? { creditProviderId: payment.creditProviderId } : {}),\n ...(payment.creditReference ? { creditReference: payment.creditReference } : {}),\n ...(payment.creditLabel ? { creditLabel: payment.creditLabel } : {}),\n ...(payment.cardBrand ? { cardBrand: payment.cardBrand } : {}),\n ...(payment.last4 ? { last4: payment.last4 } : {}),\n ...(payment.cashTenderedCents != null ? { cashTenderedCents: wholeCents(payment.cashTenderedCents) } : {}),\n ...(payment.changeCents != null ? { changeCents: wholeCents(payment.changeCents) } : {}),\n }))\n}\n\n/** A tender as one line of a receipt: `Card •• 4242`. */\nexport function posTenderLabel(\n tender: Pick<PosOrderTender, 'method' | 'cardBrand' | 'last4'> & { creditLabel?: string },\n): string {\n const base = (tender.method === 'credit' && tender.creditLabel) || POS_TENDER_LABELS[tender.method] || tender.method\n if (tender.last4) {\n return `${tender.cardBrand ? capitalize(tender.cardBrand) : base} •• ${tender.last4}`\n }\n return base\n}\n\nfunction capitalize(value: string): string {\n return value ? value.charAt(0).toUpperCase() + value.slice(1) : value\n}\n\n/*==========================================\n * SHIFTS AND THE CASH DRAWER\n *=========================================*/\n\n/**\n * Cash that moved through the drawer without a sale: money put in, money\n * paid out for a reason (a delivery, petty cash), a drop to the safe, and\n * a register return paid out in cash.\n */\nexport type PosCashEventType = 'paid_in' | 'paid_out' | 'drop' | 'refund'\n\nexport const POS_CASH_EVENT_LABELS: Readonly<Record<PosCashEventType, string>> = {\n paid_in: 'Paid in',\n paid_out: 'Paid out',\n drop: 'Safe drop',\n refund: 'Cash refund',\n}\n\nexport interface PosCashEvent {\n /** The register's attempt id, so a retried tap records one event. */\n id: string\n type: PosCashEventType\n amountCents: number\n reason: string\n /** The member who moved the cash. */\n by: string\n atMs: number\n /** The order a `refund` paid out against. */\n orderId?: string\n}\n\n/** Sales, refunds and cash movements over one shift. */\nexport interface PosShiftReport {\n orderCount: number\n /** What customers paid for the goods: order totals, tax in, tips out. */\n grossSalesCents: number\n discountsCents: number\n taxCents: number\n tipsCents: number\n salesByTender: Partial<Record<PosTenderMethod, number>>\n refundCount: number\n refundsCents: number\n refundsByTender: Partial<Record<PosTenderMethod, number>>\n /** Gross sales less refunds. */\n netSalesCents: number\n openingFloatCents: number\n cashSalesCents: number\n paidInCents: number\n paidOutCents: number\n dropsCents: number\n cashRefundsCents: number\n /** float + cash sales + paid in − paid out − drops − cash refunds. */\n expectedCashCents: number\n /** More sales were stamped with the shift than the report could read. */\n truncated?: boolean\n}\n\n/** `hosts/{hostId}/registers/{registerId}/shifts/{shiftId}`. */\nexport interface PosShift {\n hostId: string\n registerId: string\n status: 'open' | 'closed'\n openedBy: string\n /** The member's name when the shift opened, for the history and the export. */\n openedByName?: string\n openedAtMs: number\n openingFloatCents: number\n cashEvents: PosCashEvent[]\n closedBy?: string\n closedByName?: string\n /*\n * The history sorts by each of the five below (AGL-3680), and an `orderBy`\n * drops a shift that lacks the field, so the shift is opened with each one\n * `null` and the close fills them — see `POS_SHIFT_LIST_QUERY`.\n */\n closedAtMs?: number | null\n /** What the closing cashier counted in the drawer. */\n countedCashCents?: number | null\n expectedCashCents?: number | null\n /** counted − expected: positive is over, negative is short. */\n varianceCents?: number | null\n /** The Z report's `netSalesCents`, flattened so the history can order by it. */\n netSalesCents?: number | null\n closingNote?: string\n /** The Z report, frozen when the shift closed. */\n report?: PosShiftReport\n}\n\n/** The cash-event sums one formula reads. */\nexport function posCashEventTotals(events: readonly PosCashEvent[] | null | undefined): {\n paidInCents: number\n paidOutCents: number\n dropsCents: number\n cashRefundsCents: number\n} {\n const sum = (type: PosCashEventType) =>\n (events ?? [])\n .filter((event) => event?.type === type)\n .reduce((total, event) => total + wholeCents(event.amountCents), 0)\n return {\n paidInCents: sum('paid_in'),\n paidOutCents: sum('paid_out'),\n dropsCents: sum('drop'),\n cashRefundsCents: sum('refund'),\n }\n}\n\n/**\n * THE DRAWER'S EXPECTED CASH: what should be in it when it is counted.\n *\n * opening float + cash sales + paid in − paid out − safe drops − cash refunds\n *\n * Cash SALES are the cash tender's `amountCents`, which is what the sale took\n * and never what the customer handed over: the change went straight back out\n * of the same drawer. A tip left in cash is in the drawer too, so it counts.\n */\nexport function posExpectedCashCents(input: {\n openingFloatCents: number\n cashSalesCents: number\n paidInCents: number\n paidOutCents: number\n dropsCents: number\n cashRefundsCents: number\n}): number {\n return (\n wholeCents(input.openingFloatCents) +\n wholeCents(input.cashSalesCents) +\n wholeCents(input.paidInCents) -\n wholeCents(input.paidOutCents) -\n wholeCents(input.dropsCents) -\n wholeCents(input.cashRefundsCents)\n )\n}\n\n/** A sale as the shift report reads it. */\nexport interface PosShiftSale extends PosTenderSource {\n totals?: {\n totalCents?: number\n discountCents?: number\n taxCents?: number\n tipCents?: number\n } | null\n}\n\n/** A register return as the shift report reads it. */\nexport interface PosShiftRefund {\n refundedCents: number\n tenders: Array<{ method: PosTenderMethod; amountCents: number }>\n}\n\n/**\n * The X report (mid-shift) and the Z report (at close) are this one function\n * over the sales stamped with the shift, the returns rung at the register\n * during it and the shift's own cash events. A pending sale took no money and\n * a cancelled one gave it back, so neither counts; a sale refunded LATER\n * still counts as a sale, and its refund is counted where it was paid out.\n */\nexport function computePosShiftReport(input: {\n shift: Pick<PosShift, 'openingFloatCents' | 'cashEvents'>\n sales: readonly PosShiftSale[]\n refunds: readonly PosShiftRefund[]\n truncated?: boolean\n}): PosShiftReport {\n const salesByTender: Partial<Record<PosTenderMethod, number>> = {}\n const refundsByTender: Partial<Record<PosTenderMethod, number>> = {}\n let orderCount = 0\n let grossSalesCents = 0\n let discountsCents = 0\n let taxCents = 0\n let tipsCents = 0\n let cashSalesCents = 0\n for (const sale of input.sales) {\n if (sale.status === 'pending' || sale.status === 'cancelled') continue\n const tenders = posOrderTenders(sale)\n orderCount += 1\n grossSalesCents += wholeCents(sale.totals?.totalCents ?? sale.amountCents)\n discountsCents += wholeCents(sale.totals?.discountCents)\n taxCents += wholeCents(sale.totals?.taxCents)\n const tenderTips = tenders.reduce((sum, tender) => sum + tender.tipCents, 0)\n tipsCents += Math.max(wholeCents(sale.totals?.tipCents), tenderTips)\n for (const tender of tenders) {\n salesByTender[tender.method] = (salesByTender[tender.method] ?? 0) + tender.amountCents\n if (tender.method === 'cash') cashSalesCents += tender.amountCents + tender.tipCents\n }\n }\n let refundsCents = 0\n for (const refund of input.refunds) {\n refundsCents += wholeCents(refund.refundedCents)\n for (const tender of refund.tenders ?? []) {\n if (!isTenderMethod(tender.method)) continue\n refundsByTender[tender.method] =\n (refundsByTender[tender.method] ?? 0) + wholeCents(tender.amountCents)\n }\n }\n const cash = posCashEventTotals(input.shift.cashEvents)\n const openingFloatCents = wholeCents(input.shift.openingFloatCents)\n return {\n orderCount,\n grossSalesCents,\n discountsCents,\n taxCents,\n tipsCents,\n salesByTender,\n refundCount: input.refunds.length,\n refundsCents,\n refundsByTender,\n netSalesCents: grossSalesCents - refundsCents,\n openingFloatCents,\n cashSalesCents,\n ...cash,\n expectedCashCents: posExpectedCashCents({ openingFloatCents, cashSalesCents, ...cash }),\n ...(input.truncated ? { truncated: true } : {}),\n }\n}\n\n/** counted − expected; positive is over, negative is short. */\nexport function posCashVarianceCents(countedCashCents: number, expectedCashCents: number): number {\n return Math.round(Number(countedCashCents) || 0) - Math.round(Number(expectedCashCents) || 0)\n}\n\n/** `$12.34`, `-$0.50` — the register's money format, store currency USD. */\nexport function posMoney(cents: number): string {\n const value = Math.round(Number(cents) || 0)\n return `${value < 0 ? '-' : ''}$${(Math.abs(value) / 100).toFixed(2)}`\n}\n\n/** One CSV cell, quoted when it needs to be and never read as a formula. */\nfunction csvCell(value: unknown): string {\n let cell = value == null ? '' : String(value)\n if (/^[=+\\-@\\t\\r]/.test(cell) && !/^-?\\d+(\\.\\d+)?$/.test(cell)) cell = `'${cell}`\n return /[\",\\r\\n]/.test(cell) ? `\"${cell.replace(/\"/g, '\"\"')}\"` : cell\n}\n\nexport { csvCell as posCsvCell }\n\nconst dollars = (cents: number | null | undefined): string =>\n cents == null ? '' : (Math.round(cents) / 100).toFixed(2)\n\n/** The shift history as a spreadsheet: one row per shift, money in dollars. */\nexport function posShiftsCsv(\n shifts: ReadonlyArray<PosShift & { id: string; registerName?: string }>,\n memberName: (uid: string) => string = (uid) => uid,\n): string {\n const header = [\n 'Shift',\n 'Register',\n 'Opened',\n 'Opened by',\n 'Closed',\n 'Closed by',\n 'Orders',\n 'Gross sales',\n 'Refunds',\n 'Net sales',\n 'Discounts',\n 'Tax',\n 'Tips',\n 'Cash sales',\n 'Card sales',\n 'Gift card sales',\n 'Room charges',\n 'Opening float',\n 'Paid in',\n 'Paid out',\n 'Safe drops',\n 'Cash refunds',\n 'Expected cash',\n 'Counted cash',\n 'Variance',\n ]\n const iso = (ms: number | null | undefined) => (ms ? new Date(ms).toISOString() : '')\n const rows = shifts.map((shift) => {\n const report = shift.report\n const card = report\n ? (report.salesByTender.card_present ?? 0) +\n (report.salesByTender.card_keyed ?? 0) +\n (report.salesByTender.card_link ?? 0)\n : undefined\n return [\n shift.id,\n shift.registerName ?? shift.registerId,\n iso(shift.openedAtMs),\n shift.openedByName ?? memberName(shift.openedBy),\n iso(shift.closedAtMs),\n shift.closedBy ? (shift.closedByName ?? memberName(shift.closedBy)) : '',\n report?.orderCount ?? '',\n dollars(report?.grossSalesCents),\n dollars(report?.refundsCents),\n dollars(report?.netSalesCents),\n dollars(report?.discountsCents),\n dollars(report?.taxCents),\n dollars(report?.tipsCents),\n dollars(report?.cashSalesCents),\n dollars(card),\n dollars(report ? (report.salesByTender.gift_card ?? 0) : undefined),\n dollars(report ? (report.salesByTender.folio ?? 0) : undefined),\n dollars(shift.openingFloatCents),\n dollars(report?.paidInCents),\n dollars(report?.paidOutCents),\n dollars(report?.dropsCents),\n dollars(report?.cashRefundsCents),\n dollars(shift.expectedCashCents ?? report?.expectedCashCents),\n dollars(shift.countedCashCents),\n dollars(shift.varianceCents),\n ]\n })\n return [header, ...rows].map((row) => row.map(csvCell).join(',')).join('\\r\\n')\n}\n\n/*==========================================\n * RETURNS AT THE REGISTER\n *=========================================*/\n\n/** One line picked for return: its index on the order and how many units. */\nexport interface PosReturnPick {\n index: number\n quantity: number\n}\n\n/** The order fields a return reads. */\nexport interface PosReturnSource extends PosTenderSource {\n lineItems?: Array<{ unitAmountCents?: number; quantity?: number; name?: string }> | null\n totals?: {\n totalCents?: number\n discountCents?: number\n taxCents?: number\n shippingCents?: number\n tipCents?: number\n } | null\n refundedCents?: number\n refundedLineItemIds?: number[]\n /** Units of each line already returned at a register, by line index. */\n returnedQuantities?: Record<string, number>\n /** Cents already refunded to each payment by a register return. */\n posPaymentRefunds?: Record<string, number>\n}\n\n/**\n * What each line is worth back, whole: its price less its share of the\n * order's discount, plus its share of the order's tax — the same\n * apportionment `orderLineRefundCents` uses for the discount, extended to the\n * tax a register sale charged on top. Shipping and tips are not a line's.\n */\nexport function posReturnLineValues(order: PosReturnSource): number[] {\n const lines = order.lineItems ?? []\n const gross = lines.map(\n (line) =>\n Math.max(0, Math.round(Number(line?.unitAmountCents ?? 0))) *\n Math.max(1, Math.round(Number(line?.quantity ?? 1))),\n )\n const discountShares = apportionCents(gross, wholeCents(order.totals?.discountCents))\n const net = gross.map((cents, index) => Math.max(0, cents - (discountShares[index] ?? 0)))\n const taxShares = apportionCents(net, wholeCents(order.totals?.taxCents))\n return net.map((cents, index) => cents + (taxShares[index] ?? 0))\n}\n\n/** Units of each line already returned, a line refunded whole counting as all of it. */\nexport function posReturnedQuantities(order: PosReturnSource): number[] {\n const lines = order.lineItems ?? []\n const wholeLines = new Set(order.refundedLineItemIds ?? [])\n return lines.map((line, index) => {\n const quantity = Math.max(1, Math.round(Number(line?.quantity ?? 1)))\n if (wholeLines.has(index)) return quantity\n const returned = Math.round(Number(order.returnedQuantities?.[String(index)] ?? 0))\n return Math.min(quantity, Math.max(0, Number.isFinite(returned) ? returned : 0))\n })\n}\n\nexport type PosReturnPlan =\n | {\n ok: true\n refundCents: number\n /** Cents per picked line, in the order picked. */\n lineCents: number[]\n /** The order's per-line returned quantities after this return. */\n returnedQuantities: Record<string, number>\n /** Lines this return finishes, for `refundedLineItemIds`. */\n completedLines: number[]\n }\n | { ok: false; error: string }\n\n/**\n * What a return of these picks refunds, to the cent.\n *\n * Each line's value is split across its units CUMULATIVELY: returning `k` of\n * `n` units already returned `r` refunds `round(V·(r+k)/n) − round(V·r/n)`,\n * so returning a line one unit at a time refunds exactly what returning it\n * whole would have, and the rounding never strands or invents a cent. The\n * sum is capped at what is left to refund on the order — a refund given\n * from the order dialog already took its share.\n *\n * `heldElsewhere` is, per line, the units an online return (the returns\n * flow) already holds or took back. They cap what the register may take, but\n * they are not written into `returnedQuantities`, which counts only what the\n * register itself took.\n */\nexport function planPosReturn(\n order: PosReturnSource,\n picks: readonly PosReturnPick[],\n heldElsewhere: readonly number[] = [],\n): PosReturnPlan {\n const lines = order.lineItems ?? []\n if (!picks.length) return { ok: false, error: 'Pick at least one item to return.' }\n const values = posReturnLineValues(order)\n const returned = posReturnedQuantities(order)\n const seen = new Set<number>()\n const lineCents: number[] = []\n const nextReturned: Record<string, number> = {}\n for (const [index, count] of returned.entries()) if (count > 0) nextReturned[String(index)] = count\n const completedLines: number[] = []\n for (const pick of picks) {\n const index = Number(pick.index)\n const quantity = Number(pick.quantity)\n if (!Number.isInteger(index) || index < 0 || index >= lines.length) {\n return { ok: false, error: `Line ${pick.index} is not on this order.` }\n }\n if (seen.has(index)) return { ok: false, error: 'Each line can be picked once.' }\n seen.add(index)\n if (!Number.isInteger(quantity) || quantity < 1) {\n return { ok: false, error: 'Return at least one of each line you pick.' }\n }\n const total = Math.max(1, Math.round(Number(lines[index]?.quantity ?? 1)))\n const already = returned[index] ?? 0\n const held = Math.max(0, Math.round(Number(heldElsewhere[index] ?? 0)))\n if (already + held + quantity > total) {\n const left = Math.max(0, total - already - held)\n return {\n ok: false,\n error:\n left > 0\n ? `Only ${left} of \"${lines[index]?.name ?? 'that item'}\" can still be returned.`\n : held > 0\n ? `\"${lines[index]?.name ?? 'That item'}\" is already in a return. Finish it under Returns.`\n : `\"${lines[index]?.name ?? 'That item'}\" has already been returned.`,\n }\n }\n const value = values[index] ?? 0\n lineCents.push(\n Math.round((value * (already + quantity)) / total) - Math.round((value * already) / total),\n )\n nextReturned[String(index)] = already + quantity\n if (already + quantity === total) completedLines.push(index)\n }\n const remaining = Math.max(\n 0,\n wholeCents(order.totals?.totalCents) - wholeCents(order.refundedCents),\n )\n const asked = lineCents.reduce((sum, cents) => sum + cents, 0)\n const refundCents = Math.min(asked, remaining)\n if (!(refundCents > 0)) return { ok: false, error: 'Nothing is left to refund on this order.' }\n return { ok: true, refundCents, lineCents, returnedQuantities: nextReturned, completedLines }\n}\n\n/** One slice of a refund, sent back to one of the sale's payments. */\nexport interface PosRefundAllocation {\n paymentId: string\n method: PosTenderMethod\n amountCents: number\n}\n\n/**\n * What each payment can still take back: what it put toward the sale, less\n * what register returns already sent to it. A refund given from the order\n * dialog refunds through Stripe without naming a payment, so whatever it\n * took that no payment accounts for comes off the Stripe payments first.\n */\nexport function posRefundableByTender(order: PosReturnSource): Array<PosOrderTender & { refundableCents: number }> {\n const tenders = posOrderTenders(order)\n const attributed = Object.values(order.posPaymentRefunds ?? {}).reduce(\n (sum, cents) => sum + wholeCents(cents),\n 0,\n )\n let unattributed = Math.max(0, wholeCents(order.refundedCents) - attributed)\n const ordered = [...tenders].sort(\n (a, b) => Number(POS_STRIPE_TENDERS.has(b.method)) - Number(POS_STRIPE_TENDERS.has(a.method)),\n )\n const refundable = new Map<string, number>()\n for (const tender of ordered) {\n let left = Math.max(0, tender.amountCents - wholeCents(order.posPaymentRefunds?.[tender.id]))\n if (POS_STRIPE_TENDERS.has(tender.method) && unattributed > 0) {\n const taken = Math.min(left, unattributed)\n left -= taken\n unattributed -= taken\n }\n refundable.set(tender.id, left)\n }\n return tenders.map((tender) => ({ ...tender, refundableCents: refundable.get(tender.id) ?? 0 }))\n}\n\n/**\n * The order a refund goes back in, when the cashier does not split it by\n * hand: the cards first, then store credit, then the room, and the drawer\n * LAST — so cash leaves the till only for what the customer paid in cash.\n */\nconst REFUND_ORDER: readonly PosTenderMethod[] = [\n 'card_present',\n 'card_keyed',\n 'card_link',\n 'gift_card',\n 'credit',\n 'folio',\n 'cash',\n]\n\nexport type PosRefundSplit =\n | { ok: true; allocations: PosRefundAllocation[] }\n | { ok: false; error: string }\n\n/**\n * Sends `amountCents` back to the sale's own payments.\n *\n * With no `requested` split, each payment takes what it can in\n * {@link REFUND_ORDER}. With one, every slice must name a payment of this\n * sale and fit what that payment can still take, and the slices must add up\n * to the refund exactly — a refund is never sent to a tender the sale was\n * not paid with.\n */\nexport function splitPosRefund(\n order: PosReturnSource,\n amountCents: number,\n requested?: ReadonlyArray<{ paymentId: string; amountCents: number }> | null,\n): PosRefundSplit {\n const amount = wholeCents(amountCents)\n const tenders = posRefundableByTender(order)\n if (!tenders.length) return { ok: false, error: 'This order has no payment to refund to.' }\n if (requested && requested.length) {\n const allocations: PosRefundAllocation[] = []\n const used = new Set<string>()\n for (const slice of requested) {\n const tender = tenders.find((candidate) => candidate.id === slice.paymentId)\n if (!tender) return { ok: false, error: 'That payment is not on this order.' }\n if (used.has(tender.id)) return { ok: false, error: 'Each payment can be named once.' }\n used.add(tender.id)\n const cents = Math.round(Number(slice.amountCents))\n if (!Number.isFinite(cents) || cents < 0) return { ok: false, error: 'Refund amounts must be positive.' }\n if (cents === 0) continue\n if (cents > tender.refundableCents) {\n return {\n ok: false,\n error: `${posTenderLabel(tender)} can take back at most ${posMoney(tender.refundableCents)}.`,\n }\n }\n allocations.push({ paymentId: tender.id, method: tender.method, amountCents: cents })\n }\n const total = allocations.reduce((sum, slice) => sum + slice.amountCents, 0)\n if (total !== amount) {\n return {\n ok: false,\n error: `The refund is ${posMoney(amount)}; the split adds up to ${posMoney(total)}.`,\n }\n }\n return { ok: true, allocations }\n }\n const ordered = [...tenders].sort(\n (a, b) => REFUND_ORDER.indexOf(a.method) - REFUND_ORDER.indexOf(b.method),\n )\n const allocations: PosRefundAllocation[] = []\n let left = amount\n for (const tender of ordered) {\n if (!(left > 0)) break\n const cents = Math.min(left, tender.refundableCents)\n if (!(cents > 0)) continue\n allocations.push({ paymentId: tender.id, method: tender.method, amountCents: cents })\n left -= cents\n }\n if (left > 0) {\n return {\n ok: false,\n error: `Only ${posMoney(amount - left)} can go back to this sale's payments.`,\n }\n }\n return { ok: true, allocations }\n}\n\n/** `hosts/{hostId}/registers/{registerId}/returns/{returnId}`. */\nexport interface PosRegisterReturn {\n hostId: string\n registerId: string\n orderId: string\n orderNumber?: number\n /** The shift the drawer was in when the return was rung, if one was open. */\n shiftId?: string\n lines: Array<{ index: number; quantity: number; name: string; refundCents: number }>\n refundedCents: number\n tenders: Array<PosRefundAllocation & { status: 'refunded' | 'failed'; refundId?: string; error?: string }>\n restocked: boolean\n locationId?: string\n /** The member at the register (by PIN or by sign-in). */\n cashierId: string\n /** The manager whose PIN allowed a refund above the cashier's limit. */\n approvedBy?: string\n reason?: string\n status: 'refunded' | 'partial'\n atMs: number\n}\n\n/*==========================================\n * STAFF PINS\n *=========================================*/\n\n/** How long a cashier PIN switch lasts before the register asks again. */\nexport const POS_CASHIER_ASSERTION_TTL_MS = 15 * 60 * 1000\n/** A manager's approval is for the one refund in front of them. */\nexport const POS_MANAGER_ASSERTION_TTL_MS = 2 * 60 * 1000\n/** Wrong PINs in a row before the member is locked out. */\nexport const POS_PIN_MAX_ATTEMPTS = 5\n/** How long a lockout lasts. */\nexport const POS_PIN_LOCKOUT_MS = 15 * 60 * 1000\n\n/**\n * Why a PIN cannot be used, or `null` when it can: 4 to 6 digits, and not\n * one a stranger tries first — every digit the same, or a straight run.\n */\nexport function posPinProblem(pin: unknown): string | null {\n const value = typeof pin === 'string' ? pin : ''\n if (!/^\\d{4,6}$/.test(value)) return 'A PIN is 4 to 6 digits.'\n if (/^(\\d)\\1+$/.test(value)) return 'Pick a PIN that is not one digit repeated.'\n const digits = [...value].map(Number)\n const step = (digits[1] ?? 0) - (digits[0] ?? 0)\n if (\n (step === 1 || step === -1) &&\n digits.every((digit, index) => index === 0 || digit - (digits[index - 1] ?? 0) === step)\n ) {\n return 'Pick a PIN that is not a straight run like 1234.'\n }\n return null\n}\n\n/*==========================================\n * THE THERMAL RECEIPT\n *=========================================*/\n\nexport interface PosReceiptLine {\n name: string\n variantLabel?: string\n quantity: number\n unitAmountCents: number\n lineCents: number\n}\n\nexport interface PosReceipt {\n storeName: string\n logoUrl?: string\n addressLines: string[]\n orderNumber: string\n /** The digits the barcode encodes and the register's return lookup reads. */\n barcodeValue: string\n dateLabel: string\n cashierName?: string\n registerName?: string\n customerName?: string\n lines: PosReceiptLine[]\n subtotalCents: number\n discountCents: number\n taxCents: number\n totalCents: number\n tipCents: number\n tenders: Array<{ label: string; amountCents: number }>\n changeCents: number\n refundedCents: number\n footer?: string\n returnPolicy?: string\n /** A gift receipt: the items and nothing about what they cost. */\n gift: boolean\n}\n\n/** The order fields a receipt prints. */\nexport interface PosReceiptOrder extends PosReturnSource {\n number?: number\n createdAtMs?: number\n customerName?: string | null\n lineItems?: Array<{\n name?: string\n variantLabel?: string\n unitAmountCents?: number\n quantity?: number\n }> | null\n}\n\n/**\n * The receipt for one sale, from the order alone plus what the store prints\n * on every receipt. The sale's lines, totals, tenders and change are the\n * receipt model's (`receiptDataFromOrder`, the one the cloud printers print\n * from), so a browser print and a cloud print never disagree; this adds the\n * logo, the address, the return policy and the gift receipt. A gift receipt\n * keeps the lines and the barcode — the recipient's way to return — and\n * drops every price, total and tender.\n */\nexport function buildPosReceipt(input: {\n orderId: string\n order: PosReceiptOrder\n store: {\n name: string\n logoUrl?: string\n address?: string\n footer?: string\n returnPolicy?: string\n }\n cashierName?: string\n registerName?: string\n gift?: boolean\n /** How the date prints; the register's own locale by default. */\n formatDate?: (ms: number) => string\n}): PosReceipt {\n const { order } = input\n const gift = input.gift === true\n const data = receiptDataFromOrder(input.orderId, order as unknown as HostOrder & Record<string, any>, {\n storeName: input.store.name,\n })\n const lines: PosReceiptLine[] = data.lines.map((line) => ({\n name: line.name,\n ...(line.detail ? { variantLabel: line.detail } : {}),\n quantity: line.quantity,\n unitAmountCents: gift ? 0 : line.unitCents,\n lineCents: gift ? 0 : line.totalCents,\n }))\n const number = Number(order.number)\n const digits = Number.isInteger(number) && number > 0 ? String(number) : ''\n const createdAtMs = Number(order.createdAtMs) || 0\n const formatDate =\n input.formatDate ?? ((ms: number) => (ms ? new Date(ms).toLocaleString() : ''))\n const money = (cents: number | undefined) => (gift ? 0 : wholeCents(cents))\n return {\n storeName: input.store.name,\n ...(input.store.logoUrl ? { logoUrl: input.store.logoUrl } : {}),\n addressLines: String(input.store.address ?? '')\n .split(/\\r?\\n/)\n .map((line) => line.trim())\n .filter(Boolean),\n orderNumber: digits ? `#${digits}` : `#${input.orderId.slice(0, 8)}`,\n barcodeValue: digits || input.orderId,\n dateLabel: formatDate(createdAtMs),\n ...(input.cashierName ? { cashierName: input.cashierName } : {}),\n ...(input.registerName ? { registerName: input.registerName } : {}),\n ...(order.customerName ? { customerName: String(order.customerName) } : {}),\n lines,\n subtotalCents: money(data.subtotalCents),\n discountCents: money(data.discountCents),\n taxCents: money(data.taxCents),\n totalCents: money(data.totalCents),\n tipCents: money(data.tipCents),\n tenders: gift ? [] : (data.tenders ?? []),\n changeCents: money(data.changeCents),\n refundedCents: money(data.refundedCents),\n ...(input.store.footer ? { footer: input.store.footer } : {}),\n ...(input.store.returnPolicy ? { returnPolicy: input.store.returnPolicy } : {}),\n gift,\n }\n}\n\n/** One titled block of an X or Z report: what the screen, the browser print and a cloud printer all show. */\nexport interface PosShiftReportSection {\n section: string\n rows: Array<{ label: string; value: string; strong?: boolean }>\n}\n\n/** The rows of an X or Z report, in the order a manager reads them. */\nexport function posShiftReportRows(\n report: PosShiftReport,\n shift?: Pick<PosShift, 'countedCashCents' | 'varianceCents'>,\n): PosShiftReportSection[] {\n const tenders = POS_TENDER_METHODS.filter(\n (method) => (report.salesByTender[method] ?? 0) > 0 || (report.refundsByTender[method] ?? 0) > 0,\n )\n return [\n {\n section: 'Sales',\n rows: [\n { label: 'Orders', value: String(report.orderCount) },\n { label: 'Gross sales', value: posMoney(report.grossSalesCents) },\n { label: 'Discounts', value: posMoney(report.discountsCents) },\n { label: 'Tax', value: posMoney(report.taxCents) },\n { label: 'Tips', value: posMoney(report.tipsCents) },\n { label: `Refunds (${report.refundCount})`, value: `-${posMoney(report.refundsCents)}` },\n { label: 'Net sales', value: posMoney(report.netSalesCents), strong: true },\n ],\n },\n {\n section: 'By tender',\n rows: tenders.length\n ? tenders.map((method) => ({\n label: POS_TENDER_LABELS[method],\n value:\n posMoney(report.salesByTender[method] ?? 0) +\n ((report.refundsByTender[method] ?? 0) > 0\n ? ` (−${posMoney(report.refundsByTender[method] ?? 0)})`\n : ''),\n }))\n : [{ label: 'No sales yet', value: '' }],\n },\n {\n section: 'Cash drawer',\n rows: [\n { label: 'Starting cash', value: posMoney(report.openingFloatCents) },\n { label: 'Cash sales', value: posMoney(report.cashSalesCents) },\n { label: 'Paid in', value: posMoney(report.paidInCents) },\n { label: 'Paid out', value: `-${posMoney(report.paidOutCents)}` },\n { label: 'Safe drops', value: `-${posMoney(report.dropsCents)}` },\n { label: 'Cash refunds', value: `-${posMoney(report.cashRefundsCents)}` },\n { label: 'Expected in drawer', value: posMoney(report.expectedCashCents), strong: true },\n ...(shift?.countedCashCents != null\n ? [\n { label: 'Counted', value: posMoney(shift.countedCashCents) },\n {\n label: (shift.varianceCents ?? 0) < 0 ? 'Short' : (shift.varianceCents ?? 0) > 0 ? 'Over' : 'Balanced',\n value: posMoney(shift.varianceCents ?? 0),\n strong: true,\n },\n ]\n : []),\n ],\n },\n ]\n}\n\n/** A shift's X or Z report as the receipt printer prints it. */\nexport function posShiftPrintReport(input: {\n title: 'X REPORT' | 'Z REPORT'\n storeName: string\n registerName?: string\n report: PosShiftReport\n shift?: Pick<PosShift, 'countedCashCents' | 'varianceCents'>\n subtitle?: string\n}): PrintReport {\n return {\n title: input.title,\n storeName: input.storeName,\n ...(input.subtitle || input.registerName\n ? { subtitle: [input.registerName, input.subtitle].filter(Boolean).join(' - ') }\n : {}),\n sections: posShiftReportRows(input.report, input.shift),\n }\n}\n"],"names":["apportionCents","orderPayments","receiptDataFromOrder","POS_TENDER_METHODS","POS_TENDER_LABELS","cash","card_present","card_keyed","card_link","gift_card","folio","credit","POS_STRIPE_TENDERS","Set","wholeCents","value","number","Math","round","Number","isFinite","isTenderMethod","includes","posOrderTenders","order","filter","payment","status","method","map","id","amountCents","tipCents","totals","paymentIntentId","giftCardId","reservationId","creditProviderId","creditReference","creditLabel","cardBrand","last4","cashTenderedCents","changeCents","posTenderLabel","tender","base","capitalize","charAt","toUpperCase","slice","POS_CASH_EVENT_LABELS","paid_in","paid_out","drop","refund","posCashEventTotals","events","sum","type","event","reduce","total","paidInCents","paidOutCents","dropsCents","cashRefundsCents","posExpectedCashCents","input","openingFloatCents","cashSalesCents","computePosShiftReport","salesByTender","refundsByTender","orderCount","grossSalesCents","discountsCents","taxCents","tipsCents","sale","sales","tenders","totalCents","discountCents","tenderTips","max","refundsCents","refunds","refundedCents","shift","cashEvents","refundCount","length","netSalesCents","expectedCashCents","truncated","posCashVarianceCents","countedCashCents","posMoney","cents","abs","toFixed","csvCell","cell","String","test","replace","posCsvCell","dollars","posShiftsCsv","shifts","memberName","uid","header","iso","ms","Date","toISOString","rows","report","card","undefined","registerName","registerId","openedAtMs","openedByName","openedBy","closedAtMs","closedBy","closedByName","varianceCents","row","join","posReturnLineValues","lines","lineItems","gross","line","unitAmountCents","quantity","discountShares","net","index","taxShares","posReturnedQuantities","wholeLines","refundedLineItemIds","has","returned","returnedQuantities","min","planPosReturn","picks","heldElsewhere","ok","error","values","seen","lineCents","nextReturned","count","entries","completedLines","pick","isInteger","add","already","held","left","name","push","remaining","asked","refundCents","posRefundableByTender","attributed","Object","posPaymentRefunds","unattributed","ordered","sort","a","b","refundable","Map","taken","set","refundableCents","get","REFUND_ORDER","splitPosRefund","requested","amount","allocations","used","find","candidate","paymentId","indexOf","POS_CASHIER_ASSERTION_TTL_MS","POS_MANAGER_ASSERTION_TTL_MS","POS_PIN_MAX_ATTEMPTS","POS_PIN_LOCKOUT_MS","posPinProblem","pin","digits","step","every","digit","buildPosReceipt","data","gift","orderId","storeName","store","detail","variantLabel","unitCents","createdAtMs","formatDate","toLocaleString","money","logoUrl","addressLines","address","split","trim","Boolean","orderNumber","barcodeValue","dateLabel","cashierName","customerName","subtotalCents","footer","returnPolicy","posShiftReportRows","section","label","strong","posShiftPrintReport","title","subtitle","sections"],"mappings":"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"}
@@ -95,6 +95,15 @@ export interface OrderPayment {
95
95
  /** The tenders whose money moves through Stripe. */
96
96
  export declare const POS_CARD_METHODS: ReadonlySet<OrderPaymentMethod>;
97
97
  export declare function isCardPaymentMethod(method: OrderPaymentMethod): boolean;
98
+ /** The tenders that carry no platform fee (AGL-4036). */
99
+ export declare const POS_FEE_FREE_METHODS: ReadonlySet<OrderPaymentMethod>;
100
+ /** Whether a payment by `method` carries its share of the platform's take. */
101
+ export declare function paymentCarriesPlatformFee(method: OrderPaymentMethod): boolean;
102
+ /**
103
+ * The take a sale owes when ONE tender pays all of it: the whole take, or
104
+ * nothing for a fee-free tender.
105
+ */
106
+ export declare function posTenderTakeCents(method: OrderPaymentMethod, takeFeeCents: number): number;
98
107
  /**
99
108
  * The order's payments, read through one door.
100
109
  *
@@ -147,16 +156,47 @@ export declare function posTakeShareCents(input: {
147
156
  totalCents: number;
148
157
  amountCents: number;
149
158
  }): number;
159
+ /**
160
+ * {@link posTakeShareCents} for a payment by `method`: its share of the take,
161
+ * or 0 for a tender that carries no platform fee (AGL-4036).
162
+ */
163
+ export declare function posPaymentTakeShareCents(input: {
164
+ method: OrderPaymentMethod;
165
+ takeFeeCents: number;
166
+ totalCents: number;
167
+ amountCents: number;
168
+ }): number;
169
+ /**
170
+ * The part of the sale's take its tenders owe (AGL-4036): the whole take when
171
+ * every settled payment carries the fee, and otherwise the share for the part
172
+ * of the sale the fee-carrying payments paid. Cash and gift-card parts owe
173
+ * nothing, so a sale paid all in cash owes 0.
174
+ *
175
+ * Worked from the payments' METHODS, not from the `takeFeeCents` each one
176
+ * recorded, so a sale that was opened before this rule and finished after it
177
+ * is charged by the rule it finished under.
178
+ */
179
+ export declare function posOwedTakeCents(input: {
180
+ takeFeeCents: number;
181
+ totalCents: number;
182
+ payments: readonly OrderPayment[];
183
+ }): number;
150
184
  /**
151
185
  * What of the sale's take was NOT netted from a card payout, and therefore
152
- * goes on the merchant's invoice: the whole take less every succeeded card
153
- * payment's share. Never negative.
186
+ * goes on the merchant's invoice: the take the tenders owe
187
+ * ({@link posOwedTakeCents}) less every succeeded card payment's share. Never
188
+ * negative, and 0 for a sale paid only by card, cash, gift card and store credit.
154
189
  */
155
190
  export declare function posInvoiceTakeCents(input: {
156
191
  takeFeeCents: number;
192
+ totalCents: number;
157
193
  payments: readonly OrderPayment[];
158
194
  }): number;
159
- /** How the sale's fee reaches the platform, from what actually paid it. */
195
+ /**
196
+ * How the sale's fee reaches the platform, from the payments that carried it.
197
+ * A cash, gift-card or store-credit part carries none (AGL-4036), so a card + cash sale is
198
+ * `payout`, not `split`.
199
+ */
160
200
  export declare function posFeeCollection(payments: readonly OrderPayment[]): 'payout' | 'invoice' | 'split';
161
201
  /** A one-line description of a payment for the register, receipt and timeline. */
162
202
  export declare function describeOrderPayment(payment: OrderPayment): string;
@@ -48,6 +48,42 @@ export const ORDER_PAYMENT_METHODS = [
48
48
  export function isCardPaymentMethod(method) {
49
49
  return POS_CARD_METHODS.has(method);
50
50
  }
51
+ /*==========================================
52
+ * WHICH TENDERS CARRY THE PLATFORM FEE (AGL-4036).
53
+ *
54
+ * The take used to follow the sale whatever paid it (AGL-2111): a cash sale
55
+ * owed the same percentage as a card sale and had it added to the monthly
56
+ * invoice. In-person rivals charge nothing on cash, and an event register that
57
+ * bills its operator for the cash in their own apron is not one they will
58
+ * choose, so three tenders now carry none:
59
+ *
60
+ * - cash: no payment service touches it, and the merchant keeps all of it;
61
+ * - gift card: the fee was taken when the card was SOLD, so charging it again
62
+ * when the card is spent would charge the same money twice;
63
+ * - store credit (`credit`): rewards or referral value the merchant gave
64
+ * away, so no money changes hands and an invoiced fee on it would only
65
+ * penalize running a rewards program.
66
+ *
67
+ * Everything else still carries it. Card payments net it from their payout,
68
+ * as always. A room charge (`folio`) is new revenue booked to a stay, so its
69
+ * share goes on the invoice.
70
+ *
71
+ * One predicate, so the register, the completion, the offline sync and the
72
+ * docs cannot disagree about which tender is which.
73
+ *=========================================*/ /** The tenders that carry no platform fee (AGL-4036). */ export const POS_FEE_FREE_METHODS = new Set([
74
+ 'cash',
75
+ 'gift_card',
76
+ 'credit'
77
+ ]);
78
+ /** Whether a payment by `method` carries its share of the platform's take. */ export function paymentCarriesPlatformFee(method) {
79
+ return !POS_FEE_FREE_METHODS.has(method);
80
+ }
81
+ /**
82
+ * The take a sale owes when ONE tender pays all of it: the whole take, or
83
+ * nothing for a fee-free tender.
84
+ */ export function posTenderTakeCents(method, takeFeeCents) {
85
+ return paymentCarriesPlatformFee(method) ? wholeCents(takeFeeCents) : 0;
86
+ }
51
87
  /** Whole non-negative cents from an untrusted value. */ function wholeCents(value) {
52
88
  const number = Math.round(Number(value));
53
89
  return Number.isFinite(number) && number > 0 ? number : 0;
@@ -122,16 +158,48 @@ export function isCardPaymentMethod(method) {
122
158
  if (!(take > 0) || !(total > 0) || !(amount > 0)) return 0;
123
159
  return Math.min(take, Math.round(take * amount / total));
124
160
  }
161
+ /**
162
+ * {@link posTakeShareCents} for a payment by `method`: its share of the take,
163
+ * or 0 for a tender that carries no platform fee (AGL-4036).
164
+ */ export function posPaymentTakeShareCents(input) {
165
+ if (!paymentCarriesPlatformFee(input.method)) return 0;
166
+ return posTakeShareCents(input);
167
+ }
168
+ /**
169
+ * The part of the sale's take its tenders owe (AGL-4036): the whole take when
170
+ * every settled payment carries the fee, and otherwise the share for the part
171
+ * of the sale the fee-carrying payments paid. Cash and gift-card parts owe
172
+ * nothing, so a sale paid all in cash owes 0.
173
+ *
174
+ * Worked from the payments' METHODS, not from the `takeFeeCents` each one
175
+ * recorded, so a sale that was opened before this rule and finished after it
176
+ * is charged by the rule it finished under.
177
+ */ export function posOwedTakeCents(input) {
178
+ const take = wholeCents(input.takeFeeCents);
179
+ const settled = input.payments.filter((payment)=>payment.status === 'succeeded');
180
+ if (settled.every((payment)=>paymentCarriesPlatformFee(payment.method))) return take;
181
+ const carryingCents = settled.filter((payment)=>paymentCarriesPlatformFee(payment.method)).reduce((sum, payment)=>sum + wholeCents(payment.amountCents), 0);
182
+ return posTakeShareCents({
183
+ takeFeeCents: take,
184
+ totalCents: input.totalCents,
185
+ amountCents: carryingCents
186
+ });
187
+ }
125
188
  /**
126
189
  * What of the sale's take was NOT netted from a card payout, and therefore
127
- * goes on the merchant's invoice: the whole take less every succeeded card
128
- * payment's share. Never negative.
190
+ * goes on the merchant's invoice: the take the tenders owe
191
+ * ({@link posOwedTakeCents}) less every succeeded card payment's share. Never
192
+ * negative, and 0 for a sale paid only by card, cash, gift card and store credit.
129
193
  */ export function posInvoiceTakeCents(input) {
130
194
  const netted = input.payments.filter((payment)=>payment.status === 'succeeded' && isCardPaymentMethod(payment.method)).reduce((sum, payment)=>sum + wholeCents(payment.takeFeeCents), 0);
131
- return Math.max(0, wholeCents(input.takeFeeCents) - netted);
195
+ return Math.max(0, posOwedTakeCents(input) - netted);
132
196
  }
133
- /** How the sale's fee reaches the platform, from what actually paid it. */ export function posFeeCollection(payments) {
134
- const settled = payments.filter((payment)=>payment.status === 'succeeded');
197
+ /**
198
+ * How the sale's fee reaches the platform, from the payments that carried it.
199
+ * A cash, gift-card or store-credit part carries none (AGL-4036), so a card + cash sale is
200
+ * `payout`, not `split`.
201
+ */ export function posFeeCollection(payments) {
202
+ const settled = payments.filter((payment)=>payment.status === 'succeeded' && paymentCarriesPlatformFee(payment.method));
135
203
  const card = settled.some((payment)=>isCardPaymentMethod(payment.method));
136
204
  const other = settled.some((payment)=>!isCardPaymentMethod(payment.method));
137
205
  if (card && other) return 'split';