@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
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/server/pos-check-in.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 */\nimport { resolveSiteTimeZone } from '@aglyn/aglyn/app-utils/collection-entry-date'\nimport { zonedDayKey } from '@aglyn/shared-util-timestamp/zoned-time'\nimport type { PluginApiHandler, PluginApiRequest } from '@aglyn/aglyn/server'\nimport {\n changedCheckInCustomerFields,\n checkInCustomerKey,\n checkInCustomers,\n checkInDayCsv,\n checkInDayMs,\n checkInDayTotals,\n checkInSearchToken,\n CHECK_IN_BATCH_MAX,\n CHECK_IN_DAY_CEILING,\n CHECK_IN_SEARCH_CUSTOMERS,\n CHECK_IN_SEARCH_ROWS,\n dueCheckInsForOrder,\n dueCheckInsForReservation,\n newCheckInDocument,\n normalizeWristband,\n sortCheckInRows,\n type CheckInRow,\n type CheckInWristband,\n type DueCheckIn,\n type HostCheckIn,\n} from '../model/commerce-check-in'\nimport { posDayReportRange } from '../model/commerce-pos-day-report'\nimport type { HostOrder } from '../model/commerce-orders'\nimport {\n authorizePosOps,\n defaultPosOpsDeps,\n posOpsBody,\n posOpsCleanId,\n posOpsIdempotencyKey,\n resolvePosCashier,\n type PosOpsDeps,\n type PosOpsStaff,\n} from './pos-ops-gate'\nimport { onPosSaleCompleted, type PosSaleCompletedEvent } from './pos-sale'\n\n/*==========================================\n * CHECK-IN AT THE WINDOW (AGL-4043): `/api/commerce/pos-check-in`.\n *\n * POST { hostId, action: 'search', text, day? }\n * POST { hostId, action: 'customer', customerKey | email | contactId, day?, reconcile?: false | 'if-used' }\n * POST { hostId, action: 'check_in', checkIns: [{ id, wristband? }], registerId?, cashierAssertion? }\n * POST { hostId, action: 'undo', id }\n * POST { hostId, action: 'wristband', id, wristband }\n * GET ?hostId=…&day=YYYY-MM-DD[&format=csv] the day's list\n *\n * `day` is today in the site's time zone when absent. Who may use it is who\n * may work the register (`authorizePosOps`). A check-in is idempotent on the\n * `Idempotency-Key` header: a retried tap answers the check-in it made, not\n * \"already checked in\".\n *\n * WHAT IS DUE is written down, not filtered at read: each due person is a\n * `checkIns` document (see `model/commerce-check-in.ts`), so a search is a\n * query on `dayKey ==` and `searchTokens array-contains` with an index. The\n * documents are written from their sources:\n *\n * - a register sale, the moment it completes (the sale-completed seam);\n * - and every source, by {@link reconcileCheckInDay}: the day's orders\n * (`createdAtMs` in the day, the automatic single-field index) and the\n * stays that began within a year before it (`checkInDayMs`, likewise),\n * at most every {@link CHECK_IN_RECONCILE_MS} per day, on the next search\n * or list. So an online order paid a minute ago is found on the next\n * search after that.\n *\n * Reconciling only ever CREATES what is missing, refreshes a waiting\n * person's name and address, and removes a waiting person whose order was\n * refunded or cancelled. A checked-in person is never touched: each change\n * to an existing document is a transaction that re-reads it first.\n *=========================================*/\n\n/** How stale a day's reconcile may be before a search or the list redoes it. */\nexport const CHECK_IN_RECONCILE_MS = 30_000\n\n/** How far back a stay may have begun and still cover the day. */\nconst STAY_LOOKBACK_MS = 366 * 86_400_000\n\ntype Outcome =\n | { status: number; body: Record<string, unknown> }\n | { status: 200; csv: string; filename: string }\n\nconst queryValue = (value: unknown): string =>\n String(Array.isArray(value) ? (value[0] ?? '') : (value ?? '')).trim()\n\n/** A check-in id as the routes accept one. */\nfunction cleanCheckInId(value: unknown): string {\n const id = String(value ?? '').trim()\n return /^[or]_[A-Za-z0-9_-]{1,300}$/.test(id) ? id : ''\n}\n\nexport interface CheckInDay {\n dayKey: string\n startMs: number\n endMs: number\n timeZone: string\n}\n\n/** The day asked for, in the site's zone; today there when absent. */\nexport async function readCheckInDay(\n deps: PosOpsDeps,\n hostRef: FirebaseFirestore.DocumentReference,\n org: Record<string, any> | null,\n rawDay: unknown,\n): Promise<({ ok: true } & CheckInDay) | { ok: false; error: string }> {\n const host = await hostRef.get()\n const timeZone = resolveSiteTimeZone(\n org as { timeZone?: string } | null,\n (host.data() ?? null) as { timeZone?: string } | null,\n )\n const day = String(rawDay ?? '').trim()\n const range = posDayReportRange({ from: day, to: day, nowMs: deps.now(), timeZone })\n if ('error' in range) return { ok: false, error: 'Choose the day as YYYY-MM-DD.' }\n return { ok: true, dayKey: range.from, startMs: range.startMs, endMs: range.endMs, timeZone }\n}\n\n/** Reads documents by reference, a few hundred at a time. */\nasync function readAll(\n firestore: FirebaseFirestore.Firestore,\n refs: FirebaseFirestore.DocumentReference[],\n): Promise<FirebaseFirestore.DocumentSnapshot[]> {\n const out: FirebaseFirestore.DocumentSnapshot[] = []\n for (let at = 0; at < refs.length; at += 300) {\n out.push(...(await firestore.getAll(...refs.slice(at, at + 300))))\n }\n return out\n}\n\n/** The products an order set names, as stored now. */\nasync function productsFor(\n deps: PosOpsDeps,\n hostRef: FirebaseFirestore.DocumentReference,\n orders: ReadonlyArray<Partial<HostOrder>>,\n): Promise<Map<string, Record<string, any>>> {\n const ids = new Set<string>()\n for (const order of orders) {\n for (const line of Array.isArray(order.lineItems) ? order.lineItems : []) {\n if (line?.productId && posOpsCleanId(line.productId)) ids.add(line.productId)\n }\n }\n const snapshots = await readAll(\n deps.firestore(),\n [...ids].map((id) => hostRef.collection('products').doc(id)),\n )\n const products = new Map<string, Record<string, any>>()\n for (const snapshot of snapshots) {\n if (snapshot.exists) products.set(snapshot.id, (snapshot.data() ?? {}) as Record<string, any>)\n }\n return products\n}\n\nconst isAlreadyExists = (error: unknown): boolean => {\n const code = (error as { code?: unknown })?.code\n return code === 6 || code === 'already-exists' || /ALREADY_EXISTS/.test(String((error as Error)?.message))\n}\n\n/** Creates the due check-ins that are missing; an existing one is left exactly as it is. */\nasync function createMissing(\n checkIns: FirebaseFirestore.CollectionReference,\n due: ReadonlyMap<string, DueCheckIn>,\n existing: ReadonlySet<string>,\n nowMs: number,\n): Promise<number> {\n const missing = [...due].filter(([id]) => !existing.has(id))\n let created = 0\n for (let at = 0; at < missing.length; at += 100) {\n await Promise.all(\n missing.slice(at, at + 100).map(async ([id, entry]) => {\n try {\n await checkIns.doc(id).create(newCheckInDocument(entry, nowMs))\n created += 1\n } catch (error) {\n // Another device wrote it first, which is the same document.\n if (!isAlreadyExists(error)) throw error\n }\n }),\n )\n }\n return created\n}\n\n/**\n * Brings a day's check-ins in line with its orders and stays, at most every\n * {@link CHECK_IN_RECONCILE_MS} unless `force`. Answers whether a source\n * held more than one day reads.\n */\nexport async function reconcileCheckInDay(\n deps: PosOpsDeps,\n hostRef: FirebaseFirestore.DocumentReference,\n day: CheckInDay,\n options: { force?: boolean; onlyIfUsed?: boolean } = {},\n): Promise<{ truncated: boolean }> {\n const nowMs = deps.now()\n const markerRef = hostRef.collection('checkInDays').doc(day.dayKey)\n if (!options.force) {\n const marker = await markerRef.get()\n // A day nobody has checked anyone in on yet is left alone by a caller\n // that only asks in passing: a store that never checks anybody in pays\n // nothing for the register's customer panel.\n if (!marker.exists && options.onlyIfUsed) return { truncated: false }\n if (marker.exists && nowMs - Number(marker.get('reconciledAtMs') ?? 0) < CHECK_IN_RECONCILE_MS) {\n return { truncated: marker.get('truncated') === true }\n }\n }\n const dayMs = checkInDayMs(day.dayKey) ?? 0\n const checkIns = hostRef.collection('checkIns')\n const [ordersSnapshot, staysSnapshot, existingSnapshot] = await Promise.all([\n hostRef\n .collection('orders')\n .where('createdAtMs', '>=', day.startMs)\n .where('createdAtMs', '<', day.endMs)\n .orderBy('createdAtMs')\n .limit(CHECK_IN_DAY_CEILING + 1)\n .get(),\n hostRef\n .collection('reservations')\n .where('checkInDayMs', '<=', dayMs)\n .where('checkInDayMs', '>', dayMs - STAY_LOOKBACK_MS)\n .orderBy('checkInDayMs', 'desc')\n .limit(CHECK_IN_DAY_CEILING + 1)\n .get(),\n checkIns.where('dayKey', '==', day.dayKey).limit(CHECK_IN_DAY_CEILING + 1).get(),\n ])\n const truncated =\n ordersSnapshot.docs.length > CHECK_IN_DAY_CEILING ||\n staysSnapshot.docs.length > CHECK_IN_DAY_CEILING ||\n existingSnapshot.docs.length > CHECK_IN_DAY_CEILING\n const orders = ordersSnapshot.docs.slice(0, CHECK_IN_DAY_CEILING)\n const stays = staysSnapshot.docs.slice(0, CHECK_IN_DAY_CEILING)\n\n const products = await productsFor(\n deps,\n hostRef,\n orders.map((doc) => doc.data() as Partial<HostOrder>),\n )\n const resourceIds = [\n ...new Set(stays.map((doc) => String(doc.get('resourceId') ?? '')).filter((id) => posOpsCleanId(id))),\n ]\n const resourceNames = new Map<string, string>()\n for (const snapshot of await readAll(\n deps.firestore(),\n resourceIds.map((id) => hostRef.collection('resources').doc(id)),\n )) {\n if (snapshot.exists) resourceNames.set(snapshot.id, String(snapshot.get('name') ?? ''))\n }\n\n const due = new Map<string, DueCheckIn>()\n const readOrders = new Set<string>()\n const readStays = new Set<string>()\n for (const doc of orders) {\n readOrders.add(doc.id)\n for (const [id, entry] of dueCheckInsForOrder({\n orderId: doc.id,\n order: doc.data() as Partial<HostOrder>,\n products,\n dayKey: day.dayKey,\n })) {\n due.set(id, entry)\n }\n }\n for (const doc of stays) {\n readStays.add(doc.id)\n for (const [id, entry] of dueCheckInsForReservation({\n reservationId: doc.id,\n reservation: doc.data() as Record<string, any>,\n resourceName: resourceNames.get(String(doc.get('resourceId') ?? '')) ?? null,\n dayKey: day.dayKey,\n })) {\n due.set(id, entry)\n }\n }\n\n const existing = new Set(existingSnapshot.docs.map((doc) => doc.id))\n await createMissing(checkIns, due, existing, nowMs)\n\n // A waiting person whose source this pass READ and no longer finds due\n // goes; one whose name or address changed is refreshed. Each re-reads its\n // document in a transaction, so a check-in made meanwhile stands.\n const changes = existingSnapshot.docs.slice(0, CHECK_IN_DAY_CEILING).flatMap((doc) => {\n const stored = doc.data() as Partial<HostCheckIn>\n if (stored.status !== 'expected') return []\n const wanted = due.get(doc.id)\n if (!wanted) {\n const sourceRead =\n (stored.source === 'order' && stored.orderId && readOrders.has(stored.orderId)) ||\n (stored.source === 'reservation' && stored.reservationId && readStays.has(stored.reservationId))\n return sourceRead ? [{ ref: doc.ref, patch: null }] : []\n }\n const patch = changedCheckInCustomerFields(stored, wanted)\n return patch ? [{ ref: doc.ref, patch }] : []\n })\n const firestore = deps.firestore()\n for (const change of changes) {\n await firestore.runTransaction(async (transaction) => {\n const fresh = await transaction.get(change.ref)\n if (!fresh.exists || fresh.get('status') !== 'expected') return\n if (change.patch) transaction.update(change.ref, { ...change.patch, updatedAtMs: nowMs })\n else transaction.delete(change.ref)\n })\n }\n await markerRef.set({ dayKey: day.dayKey, reconciledAtMs: nowMs, truncated })\n return { truncated }\n}\n\n/**\n * The check-ins a register sale makes, written the moment it completes so\n * the person who just paid at the window is found on the next search. The\n * day is the sale's own, in the site's zone.\n */\nexport async function recordSaleCheckIns(deps: PosOpsDeps, event: PosSaleCompletedEvent): Promise<number> {\n const hostId = posOpsCleanId(event.hostId)\n const orderId = posOpsCleanId(event.orderId)\n if (!hostId || !orderId) return 0\n const lines = Array.isArray(event.order?.lineItems) ? event.order.lineItems : []\n if (!lines.length) return 0\n const hostRef = deps.firestore().collection('hosts').doc(hostId)\n const products = await productsFor(deps, hostRef, [event.order])\n if (![...products.values()].length) return 0\n const [host, owner] = await Promise.all([hostRef.get(), deps.orgForHost(hostId)])\n const timeZone = resolveSiteTimeZone(\n (owner?.org ?? null) as { timeZone?: string } | null,\n (host.data() ?? null) as { timeZone?: string } | null,\n )\n const dayKey = zonedDayKey(Number(event.order.createdAtMs ?? deps.now()), timeZone)\n const due = dueCheckInsForOrder({ orderId, order: event.order, products, dayKey })\n if (!due.size) return 0\n return createMissing(hostRef.collection('checkIns'), due, new Set(), deps.now())\n}\n\nlet subscribed = false\n\n/** Writes a register sale's check-ins as it completes. Never throws into the sale. */\nexport function registerPosSaleCheckIns(): void {\n if (subscribed) return\n subscribed = true\n onPosSaleCompleted(async (event) => {\n try {\n await recordSaleCheckIns(defaultPosOpsDeps(), event)\n } catch (error) {\n console.error('[pos-check-in] sale check-ins not written', event.orderId, error)\n }\n })\n}\n\n/*==========================================\n * THE ROUTE\n *=========================================*/\n\nconst rowOf = (doc: FirebaseFirestore.DocumentSnapshot): CheckInRow =>\n ({ ...(doc.data() as HostCheckIn), id: doc.id })\n\n/** Who checks people in: the member a PIN put at the register, else the signed-in one. */\nasync function checkerOf(\n deps: PosOpsDeps,\n staff: PosOpsStaff,\n body: Record<string, any>,\n): Promise<{ uid: string; name: string }> {\n const registerId = posOpsCleanId(body['registerId'])\n const uid = registerId\n ? (await resolvePosCashier(deps, staff, registerId, body['cashierAssertion'])).cashierId\n : staff.uid\n return { uid, name: await deps.memberName(uid) }\n}\n\nexport async function handlePosCheckIn(deps: PosOpsDeps, req: PluginApiRequest): Promise<Outcome> {\n if (req.method === 'GET') return handleDayList(deps, req)\n if (req.method !== 'POST') return { status: 405, body: { error: 'Method not allowed' } }\n const body = posOpsBody(req)\n const gate = await authorizePosOps(deps, req, body['hostId'])\n if ('error' in gate) return { status: gate.status, body: { error: gate.error } }\n const staff = gate.staff\n const checkIns = staff.hostRef.collection('checkIns')\n const action = String(body['action'] ?? '')\n\n switch (action) {\n case 'search': {\n const day = await readCheckInDay(deps, staff.hostRef, staff.org, body['day'])\n if ('error' in day) return { status: 400, body: { error: day.error } }\n const text = String(body['text'] ?? '').trim().slice(0, 120)\n const token = checkInSearchToken(text)\n if (text.length < 2 || !token) {\n return { status: 200, body: { dayKey: day.dayKey, timeZone: day.timeZone, customers: [] } }\n }\n await reconcileCheckInDay(deps, staff.hostRef, day)\n const snapshot = await checkIns\n .where('dayKey', '==', day.dayKey)\n .where('searchTokens', 'array-contains', token)\n .limit(CHECK_IN_SEARCH_ROWS)\n .get()\n return {\n status: 200,\n body: {\n dayKey: day.dayKey,\n timeZone: day.timeZone,\n customers: checkInCustomers(snapshot.docs.map(rowOf)).slice(0, CHECK_IN_SEARCH_CUSTOMERS),\n },\n }\n }\n\n case 'customer': {\n const day = await readCheckInDay(deps, staff.hostRef, staff.org, body['day'])\n if ('error' in day) return { status: 400, body: { error: day.error } }\n const typedKey = String(body['customerKey'] ?? '').trim().slice(0, 300)\n const customerKey =\n typedKey ||\n checkInCustomerKey({\n email: String(body['email'] ?? '').slice(0, 200),\n contactId: posOpsCleanId(body['contactId']) || null,\n fallback: '',\n })\n if (!customerKey) {\n return { status: 400, body: { error: 'Choose a customer.' } }\n }\n // The register's customer panel asks on every customer it attaches\n // (`reconcile: 'if-used'`), so it brings the day in line only on a day\n // the window is already checking people in.\n if (body['reconcile'] !== false) {\n await reconcileCheckInDay(deps, staff.hostRef, day, { onlyIfUsed: body['reconcile'] === 'if-used' })\n }\n const snapshot = await checkIns\n .where('dayKey', '==', day.dayKey)\n .where('customerKey', '==', customerKey)\n .limit(CHECK_IN_DAY_CEILING)\n .get()\n const rows = sortCheckInRows(snapshot.docs.map(rowOf))\n return {\n status: 200,\n body: {\n dayKey: day.dayKey,\n timeZone: day.timeZone,\n customer: checkInCustomers(rows)[0] ?? null,\n checkIns: rows,\n },\n }\n }\n\n case 'check_in': {\n const entries = Array.isArray(body['checkIns']) ? (body['checkIns'] as unknown[]) : []\n if (!entries.length) return { status: 400, body: { error: 'Choose who to check in.' } }\n if (entries.length > CHECK_IN_BATCH_MAX) {\n return { status: 400, body: { error: `Check in ${CHECK_IN_BATCH_MAX} people or fewer at a time.` } }\n }\n const wanted = new Map<string, CheckInWristband | null>()\n for (const entry of entries) {\n const id = cleanCheckInId((entry as Record<string, unknown>)?.['id'])\n if (!id) return { status: 400, body: { error: 'That is not a check-in.' } }\n const wristband = normalizeWristband((entry as Record<string, unknown>)['wristband'])\n if ('error' in wristband) return { status: 400, body: { error: wristband.error } }\n wanted.set(id, wristband.wristband)\n }\n const attempt = posOpsIdempotencyKey(req) || null\n const checker = await checkerOf(deps, staff, body)\n const nowMs = deps.now()\n const outcome = await deps.firestore().runTransaction(async (transaction) => {\n const done: CheckInRow[] = []\n const already: CheckInRow[] = []\n const missing: string[] = []\n const writes: Array<{ ref: FirebaseFirestore.DocumentReference; patch: Partial<HostCheckIn> }> = []\n for (const [id, wristband] of wanted) {\n const ref = checkIns.doc(id)\n const snapshot = await transaction.get(ref)\n if (!snapshot.exists) {\n missing.push(id)\n continue\n }\n const row = rowOf(snapshot)\n if (row.status === 'checked_in') {\n if (attempt && row.checkInAttempt === attempt) done.push(row)\n else already.push(row)\n continue\n }\n const patch: Partial<HostCheckIn> = {\n status: 'checked_in',\n checkedInAtMs: nowMs,\n checkedInBy: checker.uid,\n checkedInByName: checker.name,\n wristband,\n checkInAttempt: attempt,\n updatedAtMs: nowMs,\n }\n writes.push({ ref, patch })\n done.push({ ...row, ...patch })\n }\n for (const write of writes) transaction.update(write.ref, write.patch)\n return { done, already, missing }\n })\n if (outcome.missing.length && !outcome.done.length && !outcome.already.length) {\n return { status: 404, body: { error: 'That check-in is no longer due.' } }\n }\n return {\n status: 200,\n body: { checkedIn: outcome.done, alreadyCheckedIn: outcome.already, missing: outcome.missing },\n }\n }\n\n case 'undo':\n case 'wristband': {\n const id = cleanCheckInId(body['id'])\n if (!id) return { status: 400, body: { error: 'That is not a check-in.' } }\n let wristband: CheckInWristband | null = null\n if (action === 'wristband') {\n const parsed = normalizeWristband(body['wristband'])\n if ('error' in parsed) return { status: 400, body: { error: parsed.error } }\n wristband = parsed.wristband\n }\n const checker = action === 'undo' ? await checkerOf(deps, staff, body) : null\n const nowMs = deps.now()\n const ref = checkIns.doc(id)\n const outcome = await deps.firestore().runTransaction(async (transaction) => {\n const snapshot = await transaction.get(ref)\n if (!snapshot.exists) return { status: 404, error: 'That check-in is no longer due.' } as const\n const row = rowOf(snapshot)\n if (row.status !== 'checked_in') {\n return { status: 409, error: 'They are not checked in yet.' } as const\n }\n const patch: Partial<HostCheckIn> =\n action === 'undo'\n ? {\n status: 'expected',\n checkedInAtMs: null,\n checkedInBy: null,\n checkedInByName: null,\n wristband: null,\n checkInAttempt: null,\n undoneAtMs: nowMs,\n undoneBy: checker?.uid ?? staff.uid,\n updatedAtMs: nowMs,\n }\n : { wristband, updatedAtMs: nowMs }\n transaction.update(ref, patch)\n return { status: 200, row: { ...row, ...patch } } as const\n })\n if ('error' in outcome) return { status: outcome.status, body: { error: outcome.error } }\n return { status: 200, body: { checkIn: outcome.row } }\n }\n\n default:\n return { status: 400, body: { error: 'Unknown action' } }\n }\n}\n\nasync function handleDayList(deps: PosOpsDeps, req: PluginApiRequest): Promise<Outcome> {\n const query = req.query ?? {}\n const gate = await authorizePosOps(deps, req, queryValue(query['hostId']))\n if ('error' in gate) return { status: gate.status, body: { error: gate.error } }\n const format = queryValue(query['format']).toLowerCase()\n if (format && format !== 'json' && format !== 'csv') {\n return { status: 400, body: { error: 'Choose json or csv.' } }\n }\n const { staff } = gate\n const day = await readCheckInDay(deps, staff.hostRef, staff.org, queryValue(query['day']))\n if ('error' in day) return { status: 400, body: { error: day.error } }\n const { truncated } = await reconcileCheckInDay(deps, staff.hostRef, day)\n const snapshot = await staff.hostRef\n .collection('checkIns')\n .where('dayKey', '==', day.dayKey)\n .limit(CHECK_IN_DAY_CEILING + 1)\n .get()\n const rows = sortCheckInRows(snapshot.docs.slice(0, CHECK_IN_DAY_CEILING).map(rowOf))\n if (format === 'csv') {\n return { status: 200, csv: checkInDayCsv(rows, day.timeZone), filename: `check-ins-${day.dayKey}.csv` }\n }\n return {\n status: 200,\n body: {\n dayKey: day.dayKey,\n timeZone: day.timeZone,\n totals: checkInDayTotals(rows),\n checkIns: rows,\n truncated: truncated || snapshot.docs.length > CHECK_IN_DAY_CEILING,\n },\n }\n}\n\nexport function createPosCheckInHandler(deps: () => PosOpsDeps = defaultPosOpsDeps): PluginApiHandler {\n return async (req, res) => {\n try {\n const outcome = await handlePosCheckIn(deps(), req)\n res.setHeader('Cache-Control', 'private, no-store')\n if ('csv' in outcome) {\n res.setHeader('Content-Type', 'text/csv; charset=utf-8')\n res.setHeader('Content-Disposition', `attachment; filename=\"${outcome.filename}\"`)\n return res.status(200).send(outcome.csv)\n }\n return res.status(outcome.status).json(outcome.body)\n } catch (error) {\n console.error('[pos-check-in] failed', error)\n return res.status(500).json({ error: 'Check-in failed. Try again.' })\n }\n }\n}\n\nexport const posCheckInHandler = createPosCheckInHandler()\n"],"names":["resolveSiteTimeZone","zonedDayKey","changedCheckInCustomerFields","checkInCustomerKey","checkInCustomers","checkInDayCsv","checkInDayMs","checkInDayTotals","checkInSearchToken","CHECK_IN_BATCH_MAX","CHECK_IN_DAY_CEILING","CHECK_IN_SEARCH_CUSTOMERS","CHECK_IN_SEARCH_ROWS","dueCheckInsForOrder","dueCheckInsForReservation","newCheckInDocument","normalizeWristband","sortCheckInRows","posDayReportRange","authorizePosOps","defaultPosOpsDeps","posOpsBody","posOpsCleanId","posOpsIdempotencyKey","resolvePosCashier","onPosSaleCompleted","CHECK_IN_RECONCILE_MS","STAY_LOOKBACK_MS","queryValue","value","String","Array","isArray","trim","cleanCheckInId","id","test","readCheckInDay","deps","hostRef","org","rawDay","host","get","timeZone","data","day","range","from","to","nowMs","now","ok","error","dayKey","startMs","endMs","readAll","firestore","refs","out","at","length","push","getAll","slice","productsFor","orders","ids","Set","order","line","lineItems","productId","add","snapshots","map","collection","doc","products","Map","snapshot","exists","set","isAlreadyExists","code","message","createMissing","checkIns","due","existing","missing","filter","has","created","Promise","all","entry","create","reconcileCheckInDay","options","markerRef","force","marker","onlyIfUsed","truncated","Number","dayMs","ordersSnapshot","staysSnapshot","existingSnapshot","where","orderBy","limit","docs","stays","resourceIds","resourceNames","readOrders","readStays","orderId","reservationId","reservation","resourceName","changes","flatMap","stored","status","wanted","sourceRead","source","ref","patch","change","runTransaction","transaction","fresh","update","updatedAtMs","delete","reconciledAtMs","recordSaleCheckIns","event","hostId","lines","values","owner","orgForHost","createdAtMs","size","subscribed","registerPosSaleCheckIns","console","rowOf","checkerOf","staff","body","registerId","uid","cashierId","name","memberName","handlePosCheckIn","req","method","handleDayList","gate","action","text","token","customers","typedKey","customerKey","email","contactId","fallback","rows","customer","entries","wristband","attempt","checker","outcome","done","already","writes","row","checkInAttempt","checkedInAtMs","checkedInBy","checkedInByName","write","checkedIn","alreadyCheckedIn","parsed","undoneAtMs","undoneBy","checkIn","query","format","toLowerCase","csv","filename","totals","createPosCheckInHandler","res","setHeader","send","json","posCheckInHandler"],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ import type { PluginApiHandler, PluginApiRequest } from '@aglyn/aglyn/server';
18
+ import { type PosDayReport } from '../model/commerce-pos-day-report';
19
+ import { type PosOpsDeps } from './pos-ops-gate';
20
+ type Outcome = {
21
+ status: number;
22
+ body: Record<string, unknown>;
23
+ } | {
24
+ status: 200;
25
+ csv: string;
26
+ filename: string;
27
+ };
28
+ /** Reads the range and computes the report; the route and its specs both call this. */
29
+ export declare function readPosDayReport(deps: PosOpsDeps, input: {
30
+ hostRef: FirebaseFirestore.DocumentReference;
31
+ hostId: string;
32
+ org: Record<string, any> | null;
33
+ from?: unknown;
34
+ to?: unknown;
35
+ }): Promise<{
36
+ ok: true;
37
+ report: PosDayReport;
38
+ } | {
39
+ ok: false;
40
+ status: number;
41
+ error: string;
42
+ }>;
43
+ export declare function handlePosDayReport(deps: PosOpsDeps, req: PluginApiRequest): Promise<Outcome>;
44
+ export declare function createPosDayReportHandler(deps?: () => PosOpsDeps): PluginApiHandler;
45
+ export declare const posDayReportHandler: PluginApiHandler;
46
+ export {};
@@ -0,0 +1,299 @@
1
+ import { _ as _extends } from "@swc/helpers/_/_extends";
2
+ /**
3
+ * @license
4
+ * Copyright 2026 Aglyn LLC
5
+ *
6
+ * Licensed under the Apache License, Version 2.0 (the "License");
7
+ * you may not use this file except in compliance with the License.
8
+ * You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing, software
13
+ * distributed under the License is distributed on an "AS IS" BASIS,
14
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ * See the License for the specific language governing permissions and
16
+ * limitations under the License.
17
+ */ import { resolveSiteTimeZone } from "@aglyn/aglyn/app-utils/collection-entry-date";
18
+ import { computePosDayReport, posDayReportCsv, posDayReportRange, POS_DAY_REPORT_RETURNS_CEILING, POS_DAY_REPORT_SALES_CEILING, POS_DAY_REPORT_SHIFTS_CEILING } from "../model/commerce-pos-day-report.js";
19
+ import { authorizePosOps, defaultPosOpsDeps } from "./pos-ops-gate.js";
20
+ import { readPosShiftReport } from "./pos-shift.js";
21
+ import { POS_CURRENCY } from "./pos-stripe.js";
22
+ /*==========================================
23
+ * DAY AND EVENT TOTALS (AGL-4037): `GET /api/commerce/pos-day-report`.
24
+ *
25
+ * ?hostId=… the site (required)
26
+ * &from=YYYY-MM-DD the first day; today in the store's zone when absent
27
+ * &to=YYYY-MM-DD the last day, inclusive; `from` when absent; 31 days at most
28
+ * &format=csv the same report as a spreadsheet download
29
+ *
30
+ * Who may read it is who may work the register (`authorizePosOps`): a site
31
+ * admin or editor whose Manage POS permission is on, on a plan with POS. The
32
+ * dispatcher's lockdown gate runs before this, as for every register route.
33
+ *
34
+ * Every read is a query an index serves, never a loaded window filtered here:
35
+ *
36
+ * orders channel == 'pos', createdAtMs in range (channel ASC, createdAtMs DESC)
37
+ * returns per register, atMs in range automatic single field
38
+ * shifts per register, openedAtMs in range automatic single field
39
+ * shifts per register, closedAtMs in range automatic single field
40
+ * shifts the open one and those a sale names by id (`getAll`)
41
+ *
42
+ * The figures are `computePosDayReport`, which is the shift report's own
43
+ * calculator over the range.
44
+ *=========================================*/ /** The most registers a report walks: the console lists 25. */ const REGISTERS_CEILING = 50;
45
+ const queryValue = (value)=>{
46
+ var _value_;
47
+ return String(Array.isArray(value) ? (_value_ = value[0]) != null ? _value_ : '' : value != null ? value : '').trim();
48
+ };
49
+ function shiftOf(id, data) {
50
+ var _data_openedAtMs, _data_openingFloatCents;
51
+ if (!data) return null;
52
+ return _extends({}, data, {
53
+ id,
54
+ status: data['status'] === 'closed' ? 'closed' : 'open',
55
+ openedAtMs: Number((_data_openedAtMs = data['openedAtMs']) != null ? _data_openedAtMs : 0),
56
+ openingFloatCents: Number((_data_openingFloatCents = data['openingFloatCents']) != null ? _data_openingFloatCents : 0),
57
+ cashEvents: Array.isArray(data['cashEvents']) ? data['cashEvents'] : []
58
+ });
59
+ }
60
+ /** The store's currency (`settings/store.currency`), lower case; the register's when unset. */ async function storeCurrency(hostRef) {
61
+ var _snapshot_get;
62
+ const snapshot = await hostRef.collection('settings').doc('store').get().catch(()=>null);
63
+ const chosen = String((snapshot == null ? void 0 : snapshot.exists) ? (_snapshot_get = snapshot.get('currency')) != null ? _snapshot_get : '' : '').trim().toLowerCase();
64
+ return /^[a-z]{3}$/.test(chosen) ? chosen : POS_CURRENCY;
65
+ }
66
+ /** Reads the range and computes the report; the route and its specs both call this. */ export async function readPosDayReport(deps, input) {
67
+ var _hostSnapshot_data, _register_get, _hostSnapshot_get;
68
+ const { hostRef } = input;
69
+ const [hostSnapshot, currency] = await Promise.all([
70
+ hostRef.get(),
71
+ storeCurrency(hostRef)
72
+ ]);
73
+ const timeZone = resolveSiteTimeZone(input.org, (_hostSnapshot_data = hostSnapshot.data()) != null ? _hostSnapshot_data : null);
74
+ const nowMs = deps.now();
75
+ const range = posDayReportRange({
76
+ from: input.from,
77
+ to: input.to,
78
+ nowMs,
79
+ timeZone
80
+ });
81
+ if ('error' in range) return {
82
+ ok: false,
83
+ status: 400,
84
+ error: range.error
85
+ };
86
+ const { startMs, endMs } = range;
87
+ const registersSnapshot = await hostRef.collection('registers').limit(REGISTERS_CEILING).get();
88
+ const registers = registersSnapshot.docs;
89
+ const registerNames = {};
90
+ for (const register of registers)registerNames[register.id] = String((_register_get = register.get('name')) != null ? _register_get : '');
91
+ const salesSnapshot = await hostRef.collection('orders').where('channel', '==', 'pos').where('createdAtMs', '>=', startMs).where('createdAtMs', '<', endMs).orderBy('createdAtMs', 'desc').limit(POS_DAY_REPORT_SALES_CEILING + 1).get();
92
+ let truncated = salesSnapshot.docs.length > POS_DAY_REPORT_SALES_CEILING;
93
+ const sales = salesSnapshot.docs.slice(0, POS_DAY_REPORT_SALES_CEILING).map((doc)=>{
94
+ var _doc_data;
95
+ return (_doc_data = doc.data()) != null ? _doc_data : {};
96
+ });
97
+ const perRegister = await Promise.all(registers.map(async (register)=>{
98
+ const [returnsSnapshot, openedSnapshot, closedSnapshot] = await Promise.all([
99
+ register.ref.collection('returns').where('atMs', '>=', startMs).where('atMs', '<', endMs).limit(POS_DAY_REPORT_RETURNS_CEILING + 1).get(),
100
+ register.ref.collection('shifts').where('openedAtMs', '>=', startMs).where('openedAtMs', '<', endMs).limit(POS_DAY_REPORT_SHIFTS_CEILING + 1).get(),
101
+ register.ref.collection('shifts').where('closedAtMs', '>=', startMs).where('closedAtMs', '<', endMs).limit(POS_DAY_REPORT_SHIFTS_CEILING + 1).get()
102
+ ]);
103
+ if (returnsSnapshot.docs.length > POS_DAY_REPORT_RETURNS_CEILING || openedSnapshot.docs.length > POS_DAY_REPORT_SHIFTS_CEILING || closedSnapshot.docs.length > POS_DAY_REPORT_SHIFTS_CEILING) {
104
+ truncated = true;
105
+ }
106
+ const returns = returnsSnapshot.docs.slice(0, POS_DAY_REPORT_RETURNS_CEILING).map((doc)=>{
107
+ var _doc_data, _data_refundedCents;
108
+ const data = (_doc_data = doc.data()) != null ? _doc_data : {};
109
+ const tenders = Array.isArray(data['tenders']) ? data['tenders'] : [];
110
+ return _extends({
111
+ registerId: register.id
112
+ }, data['cashierId'] ? {
113
+ cashierId: String(data['cashierId'])
114
+ } : {}, {
115
+ refundedCents: Number((_data_refundedCents = data['refundedCents']) != null ? _data_refundedCents : 0),
116
+ // A tender whose refund failed gave nothing back, as on a Z report.
117
+ tenders: tenders.filter((tender)=>(tender == null ? void 0 : tender.status) !== 'failed').map((tender)=>{
118
+ var _tender_amountCents;
119
+ return {
120
+ method: tender.method,
121
+ amountCents: Number((_tender_amountCents = tender.amountCents) != null ? _tender_amountCents : 0)
122
+ };
123
+ })
124
+ });
125
+ });
126
+ return {
127
+ register,
128
+ returns,
129
+ shiftDocs: [
130
+ ...openedSnapshot.docs,
131
+ ...closedSnapshot.docs
132
+ ].slice(0, 2 * POS_DAY_REPORT_SHIFTS_CEILING)
133
+ };
134
+ }));
135
+ // Every shift in the range, once: opened or closed in it, the open one on
136
+ // each register, and the shift each sale in the range was rung in (a shift
137
+ // that ran across the whole range).
138
+ const shifts = new Map();
139
+ const keyOf = (registerId, shiftId)=>`${registerId}/${shiftId}`;
140
+ for (const entry of perRegister){
141
+ for (const doc of entry.shiftDocs){
142
+ const shift = shiftOf(doc.id, doc.data());
143
+ if (shift) shifts.set(keyOf(entry.register.id, doc.id), _extends({}, shift, {
144
+ registerId: entry.register.id
145
+ }));
146
+ }
147
+ }
148
+ const byId = [];
149
+ for (const register of registers){
150
+ var _register_get1;
151
+ const openShiftId = String((_register_get1 = register.get('openShiftId')) != null ? _register_get1 : '');
152
+ if (openShiftId) byId.push({
153
+ registerId: register.id,
154
+ shiftId: openShiftId
155
+ });
156
+ }
157
+ for (const sale of sales){
158
+ if (sale.shiftId && sale.registerId) byId.push({
159
+ registerId: sale.registerId,
160
+ shiftId: sale.shiftId
161
+ });
162
+ }
163
+ const missing = [
164
+ ...new Map(byId.filter((ref)=>registerNames[ref.registerId] !== undefined && !shifts.has(keyOf(ref.registerId, ref.shiftId))).map((ref)=>[
165
+ keyOf(ref.registerId, ref.shiftId),
166
+ ref
167
+ ])).values()
168
+ ];
169
+ if (missing.length) {
170
+ const firestore = deps.firestore();
171
+ const snapshots = await firestore.getAll(...missing.map((ref)=>hostRef.collection('registers').doc(ref.registerId).collection('shifts').doc(ref.shiftId)));
172
+ snapshots.forEach((snapshot, index)=>{
173
+ const ref = missing[index];
174
+ const shift = shiftOf(snapshot.id, snapshot.exists ? snapshot.data() : undefined);
175
+ // The open shift counts only if it began before the range ended.
176
+ if (shift && shift.openedAtMs < endMs) {
177
+ shifts.set(keyOf(ref.registerId, ref.shiftId), _extends({}, shift, {
178
+ registerId: ref.registerId
179
+ }));
180
+ }
181
+ });
182
+ }
183
+ let shiftList = [
184
+ ...shifts.values()
185
+ ].sort((a, b)=>a.openedAtMs - b.openedAtMs || a.id.localeCompare(b.id));
186
+ if (shiftList.length > POS_DAY_REPORT_SHIFTS_CEILING) {
187
+ truncated = true;
188
+ shiftList = shiftList.slice(-POS_DAY_REPORT_SHIFTS_CEILING);
189
+ }
190
+ // An open shift's drawer is read live, through the X report's own reader.
191
+ shiftList = await Promise.all(shiftList.map(async (shift)=>shift.status === 'open' ? _extends({}, shift, {
192
+ liveReport: await readPosShiftReport(hostRef.collection('registers').doc(shift.registerId), hostRef, shift.id, shift)
193
+ }) : shift));
194
+ const memberIds = new Set();
195
+ for (const sale of sales)if (sale.cashierId) memberIds.add(String(sale.cashierId));
196
+ for (const entry of perRegister)for (const refund of entry.returns)if (refund.cashierId) memberIds.add(refund.cashierId);
197
+ for (const shift of shiftList){
198
+ if (!shift.openedByName && shift.openedBy) memberIds.add(shift.openedBy);
199
+ if (!shift.closedByName && shift.closedBy) memberIds.add(shift.closedBy);
200
+ }
201
+ const memberNames = {};
202
+ await Promise.all([
203
+ ...memberIds
204
+ ].map(async (uid)=>{
205
+ memberNames[uid] = await deps.memberName(uid);
206
+ }));
207
+ return {
208
+ ok: true,
209
+ report: computePosDayReport({
210
+ hostId: input.hostId,
211
+ storeName: String((_hostSnapshot_get = hostSnapshot.get('displayName')) != null ? _hostSnapshot_get : ''),
212
+ currency,
213
+ timeZone,
214
+ range,
215
+ generatedAtMs: nowMs,
216
+ sales,
217
+ returns: perRegister.flatMap((entry)=>entry.returns),
218
+ shifts: shiftList,
219
+ registerNames,
220
+ memberNames,
221
+ truncated
222
+ })
223
+ };
224
+ }
225
+ export async function handlePosDayReport(deps, req) {
226
+ var _req_query;
227
+ if (req.method !== 'GET') return {
228
+ status: 405,
229
+ body: {
230
+ error: 'Method not allowed'
231
+ }
232
+ };
233
+ const query = (_req_query = req.query) != null ? _req_query : {};
234
+ const gate = await authorizePosOps(deps, req, queryValue(query['hostId']));
235
+ if ('error' in gate) return {
236
+ status: gate.status,
237
+ body: {
238
+ error: gate.error
239
+ }
240
+ };
241
+ const format = queryValue(query['format']).toLowerCase();
242
+ if (format && format !== 'json' && format !== 'csv') {
243
+ return {
244
+ status: 400,
245
+ body: {
246
+ error: 'Choose json or csv.'
247
+ }
248
+ };
249
+ }
250
+ const read = await readPosDayReport(deps, {
251
+ hostRef: gate.staff.hostRef,
252
+ hostId: gate.staff.hostId,
253
+ org: gate.staff.org,
254
+ from: queryValue(query['from']),
255
+ to: queryValue(query['to'])
256
+ });
257
+ if ('error' in read) return {
258
+ status: read.status,
259
+ body: {
260
+ error: read.error
261
+ }
262
+ };
263
+ if (format === 'csv') {
264
+ const { from, to } = read.report;
265
+ return {
266
+ status: 200,
267
+ csv: posDayReportCsv(read.report),
268
+ filename: `day-totals-${from}${to === from ? '' : `-to-${to}`}.csv`
269
+ };
270
+ }
271
+ return {
272
+ status: 200,
273
+ body: {
274
+ report: read.report
275
+ }
276
+ };
277
+ }
278
+ export function createPosDayReportHandler(deps = defaultPosOpsDeps) {
279
+ return async (req, res)=>{
280
+ try {
281
+ const outcome = await handlePosDayReport(deps(), req);
282
+ res.setHeader('Cache-Control', 'private, no-store');
283
+ if ('csv' in outcome) {
284
+ res.setHeader('Content-Type', 'text/csv; charset=utf-8');
285
+ res.setHeader('Content-Disposition', `attachment; filename="${outcome.filename}"`);
286
+ return res.status(200).send(outcome.csv);
287
+ }
288
+ return res.status(outcome.status).json(outcome.body);
289
+ } catch (error) {
290
+ console.error('[pos-day-report] failed', error);
291
+ return res.status(500).json({
292
+ error: 'The day totals could not be read'
293
+ });
294
+ }
295
+ };
296
+ }
297
+ export const posDayReportHandler = createPosDayReportHandler();
298
+
299
+ //# sourceMappingURL=pos-day-report.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/server/pos-day-report.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 { resolveSiteTimeZone } from '@aglyn/aglyn/app-utils/collection-entry-date'\nimport type { PluginApiHandler, PluginApiRequest } from '@aglyn/aglyn/server'\nimport {\n computePosDayReport,\n posDayReportCsv,\n posDayReportRange,\n POS_DAY_REPORT_RETURNS_CEILING,\n POS_DAY_REPORT_SALES_CEILING,\n POS_DAY_REPORT_SHIFTS_CEILING,\n type PosDayReport,\n type PosDayReturn,\n type PosDaySale,\n type PosDayShift,\n} from '../model/commerce-pos-day-report'\nimport type { PosCashEvent, PosShift } from '../model/commerce-pos-ops'\nimport { authorizePosOps, defaultPosOpsDeps, type PosOpsDeps } from './pos-ops-gate'\nimport { readPosShiftReport } from './pos-shift'\nimport { POS_CURRENCY } from './pos-stripe'\n\n/*==========================================\n * DAY AND EVENT TOTALS (AGL-4037): `GET /api/commerce/pos-day-report`.\n *\n * ?hostId=… the site (required)\n * &from=YYYY-MM-DD the first day; today in the store's zone when absent\n * &to=YYYY-MM-DD the last day, inclusive; `from` when absent; 31 days at most\n * &format=csv the same report as a spreadsheet download\n *\n * Who may read it is who may work the register (`authorizePosOps`): a site\n * admin or editor whose Manage POS permission is on, on a plan with POS. The\n * dispatcher's lockdown gate runs before this, as for every register route.\n *\n * Every read is a query an index serves, never a loaded window filtered here:\n *\n * orders channel == 'pos', createdAtMs in range (channel ASC, createdAtMs DESC)\n * returns per register, atMs in range automatic single field\n * shifts per register, openedAtMs in range automatic single field\n * shifts per register, closedAtMs in range automatic single field\n * shifts the open one and those a sale names by id (`getAll`)\n *\n * The figures are `computePosDayReport`, which is the shift report's own\n * calculator over the range.\n *=========================================*/\n\n/** The most registers a report walks: the console lists 25. */\nconst REGISTERS_CEILING = 50\n\ntype Outcome =\n | { status: number; body: Record<string, unknown> }\n | { status: 200; csv: string; filename: string }\n\nconst queryValue = (value: unknown): string =>\n String(Array.isArray(value) ? (value[0] ?? '') : (value ?? '')).trim()\n\nfunction shiftOf(id: string, data: Record<string, any> | undefined): PosDayShift | null {\n if (!data) return null\n return {\n ...(data as PosShift),\n id,\n status: data['status'] === 'closed' ? 'closed' : 'open',\n openedAtMs: Number(data['openedAtMs'] ?? 0),\n openingFloatCents: Number(data['openingFloatCents'] ?? 0),\n cashEvents: Array.isArray(data['cashEvents']) ? (data['cashEvents'] as PosCashEvent[]) : [],\n }\n}\n\n/** The store's currency (`settings/store.currency`), lower case; the register's when unset. */\nasync function storeCurrency(hostRef: FirebaseFirestore.DocumentReference): Promise<string> {\n const snapshot = await hostRef.collection('settings').doc('store').get().catch(() => null)\n const chosen = String(snapshot?.exists ? (snapshot.get('currency') ?? '') : '')\n .trim()\n .toLowerCase()\n return /^[a-z]{3}$/.test(chosen) ? chosen : POS_CURRENCY\n}\n\n/** Reads the range and computes the report; the route and its specs both call this. */\nexport async function readPosDayReport(\n deps: PosOpsDeps,\n input: {\n hostRef: FirebaseFirestore.DocumentReference\n hostId: string\n org: Record<string, any> | null\n from?: unknown\n to?: unknown\n },\n): Promise<{ ok: true; report: PosDayReport } | { ok: false; status: number; error: string }> {\n const { hostRef } = input\n const [hostSnapshot, currency] = await Promise.all([hostRef.get(), storeCurrency(hostRef)])\n const timeZone = resolveSiteTimeZone(\n input.org as { timeZone?: string } | null,\n (hostSnapshot.data() ?? null) as { timeZone?: string } | null,\n )\n const nowMs = deps.now()\n const range = posDayReportRange({ from: input.from, to: input.to, nowMs, timeZone })\n if ('error' in range) return { ok: false, status: 400, error: range.error }\n const { startMs, endMs } = range\n\n const registersSnapshot = await hostRef.collection('registers').limit(REGISTERS_CEILING).get()\n const registers = registersSnapshot.docs\n const registerNames: Record<string, string> = {}\n for (const register of registers) registerNames[register.id] = String(register.get('name') ?? '')\n\n const salesSnapshot = await hostRef\n .collection('orders')\n .where('channel', '==', 'pos')\n .where('createdAtMs', '>=', startMs)\n .where('createdAtMs', '<', endMs)\n .orderBy('createdAtMs', 'desc')\n .limit(POS_DAY_REPORT_SALES_CEILING + 1)\n .get()\n let truncated = salesSnapshot.docs.length > POS_DAY_REPORT_SALES_CEILING\n const sales = salesSnapshot.docs\n .slice(0, POS_DAY_REPORT_SALES_CEILING)\n .map((doc) => (doc.data() ?? {}) as PosDaySale)\n\n const perRegister = await Promise.all(\n registers.map(async (register) => {\n const [returnsSnapshot, openedSnapshot, closedSnapshot] = await Promise.all([\n register.ref\n .collection('returns')\n .where('atMs', '>=', startMs)\n .where('atMs', '<', endMs)\n .limit(POS_DAY_REPORT_RETURNS_CEILING + 1)\n .get(),\n register.ref\n .collection('shifts')\n .where('openedAtMs', '>=', startMs)\n .where('openedAtMs', '<', endMs)\n .limit(POS_DAY_REPORT_SHIFTS_CEILING + 1)\n .get(),\n register.ref\n .collection('shifts')\n .where('closedAtMs', '>=', startMs)\n .where('closedAtMs', '<', endMs)\n .limit(POS_DAY_REPORT_SHIFTS_CEILING + 1)\n .get(),\n ])\n if (\n returnsSnapshot.docs.length > POS_DAY_REPORT_RETURNS_CEILING ||\n openedSnapshot.docs.length > POS_DAY_REPORT_SHIFTS_CEILING ||\n closedSnapshot.docs.length > POS_DAY_REPORT_SHIFTS_CEILING\n ) {\n truncated = true\n }\n const returns: PosDayReturn[] = returnsSnapshot.docs\n .slice(0, POS_DAY_REPORT_RETURNS_CEILING)\n .map((doc) => {\n const data = (doc.data() ?? {}) as Record<string, any>\n const tenders = Array.isArray(data['tenders']) ? data['tenders'] : []\n return {\n registerId: register.id,\n ...(data['cashierId'] ? { cashierId: String(data['cashierId']) } : {}),\n refundedCents: Number(data['refundedCents'] ?? 0),\n // A tender whose refund failed gave nothing back, as on a Z report.\n tenders: tenders\n .filter((tender: any) => tender?.status !== 'failed')\n .map((tender: any) => ({ method: tender.method, amountCents: Number(tender.amountCents ?? 0) })),\n }\n })\n return {\n register,\n returns,\n shiftDocs: [...openedSnapshot.docs, ...closedSnapshot.docs].slice(0, 2 * POS_DAY_REPORT_SHIFTS_CEILING),\n }\n }),\n )\n\n // Every shift in the range, once: opened or closed in it, the open one on\n // each register, and the shift each sale in the range was rung in (a shift\n // that ran across the whole range).\n const shifts = new Map<string, PosDayShift>()\n const keyOf = (registerId: string, shiftId: string) => `${registerId}/${shiftId}`\n for (const entry of perRegister) {\n for (const doc of entry.shiftDocs) {\n const shift = shiftOf(doc.id, doc.data())\n if (shift) shifts.set(keyOf(entry.register.id, doc.id), { ...shift, registerId: entry.register.id })\n }\n }\n const byId: Array<{ registerId: string; shiftId: string }> = []\n for (const register of registers) {\n const openShiftId = String(register.get('openShiftId') ?? '')\n if (openShiftId) byId.push({ registerId: register.id, shiftId: openShiftId })\n }\n for (const sale of sales) {\n if (sale.shiftId && sale.registerId) byId.push({ registerId: sale.registerId, shiftId: sale.shiftId })\n }\n const missing = [\n ...new Map(\n byId\n .filter((ref) => registerNames[ref.registerId] !== undefined && !shifts.has(keyOf(ref.registerId, ref.shiftId)))\n .map((ref) => [keyOf(ref.registerId, ref.shiftId), ref]),\n ).values(),\n ]\n if (missing.length) {\n const firestore = deps.firestore()\n const snapshots = await firestore.getAll(\n ...missing.map((ref) => hostRef.collection('registers').doc(ref.registerId).collection('shifts').doc(ref.shiftId)),\n )\n snapshots.forEach((snapshot, index) => {\n const ref = missing[index]!\n const shift = shiftOf(snapshot.id, snapshot.exists ? snapshot.data() : undefined)\n // The open shift counts only if it began before the range ended.\n if (shift && shift.openedAtMs < endMs) {\n shifts.set(keyOf(ref.registerId, ref.shiftId), { ...shift, registerId: ref.registerId })\n }\n })\n }\n let shiftList = [...shifts.values()].sort((a, b) => a.openedAtMs - b.openedAtMs || a.id.localeCompare(b.id))\n if (shiftList.length > POS_DAY_REPORT_SHIFTS_CEILING) {\n truncated = true\n shiftList = shiftList.slice(-POS_DAY_REPORT_SHIFTS_CEILING)\n }\n\n // An open shift's drawer is read live, through the X report's own reader.\n shiftList = await Promise.all(\n shiftList.map(async (shift) =>\n shift.status === 'open'\n ? {\n ...shift,\n liveReport: await readPosShiftReport(\n hostRef.collection('registers').doc(shift.registerId),\n hostRef,\n shift.id,\n shift,\n ),\n }\n : shift,\n ),\n )\n\n const memberIds = new Set<string>()\n for (const sale of sales) if (sale.cashierId) memberIds.add(String(sale.cashierId))\n for (const entry of perRegister) for (const refund of entry.returns) if (refund.cashierId) memberIds.add(refund.cashierId)\n for (const shift of shiftList) {\n if (!shift.openedByName && shift.openedBy) memberIds.add(shift.openedBy)\n if (!shift.closedByName && shift.closedBy) memberIds.add(shift.closedBy)\n }\n const memberNames: Record<string, string> = {}\n await Promise.all(\n [...memberIds].map(async (uid) => {\n memberNames[uid] = await deps.memberName(uid)\n }),\n )\n\n return {\n ok: true,\n report: computePosDayReport({\n hostId: input.hostId,\n storeName: String(hostSnapshot.get('displayName') ?? ''),\n currency,\n timeZone,\n range,\n generatedAtMs: nowMs,\n sales,\n returns: perRegister.flatMap((entry) => entry.returns),\n shifts: shiftList,\n registerNames,\n memberNames,\n truncated,\n }),\n }\n}\n\nexport async function handlePosDayReport(deps: PosOpsDeps, req: PluginApiRequest): Promise<Outcome> {\n if (req.method !== 'GET') return { status: 405, body: { error: 'Method not allowed' } }\n const query = req.query ?? {}\n const gate = await authorizePosOps(deps, req, queryValue(query['hostId']))\n if ('error' in gate) return { status: gate.status, body: { error: gate.error } }\n const format = queryValue(query['format']).toLowerCase()\n if (format && format !== 'json' && format !== 'csv') {\n return { status: 400, body: { error: 'Choose json or csv.' } }\n }\n const read = await readPosDayReport(deps, {\n hostRef: gate.staff.hostRef,\n hostId: gate.staff.hostId,\n org: gate.staff.org,\n from: queryValue(query['from']),\n to: queryValue(query['to']),\n })\n if ('error' in read) return { status: read.status, body: { error: read.error } }\n if (format === 'csv') {\n const { from, to } = read.report\n return {\n status: 200,\n csv: posDayReportCsv(read.report),\n filename: `day-totals-${from}${to === from ? '' : `-to-${to}`}.csv`,\n }\n }\n return { status: 200, body: { report: read.report } }\n}\n\nexport function createPosDayReportHandler(deps: () => PosOpsDeps = defaultPosOpsDeps): PluginApiHandler {\n return async (req, res) => {\n try {\n const outcome = await handlePosDayReport(deps(), req)\n res.setHeader('Cache-Control', 'private, no-store')\n if ('csv' in outcome) {\n res.setHeader('Content-Type', 'text/csv; charset=utf-8')\n res.setHeader('Content-Disposition', `attachment; filename=\"${outcome.filename}\"`)\n return res.status(200).send(outcome.csv)\n }\n return res.status(outcome.status).json(outcome.body)\n } catch (error) {\n console.error('[pos-day-report] failed', error)\n return res.status(500).json({ error: 'The day totals could not be read' })\n }\n }\n}\n\nexport const posDayReportHandler = createPosDayReportHandler()\n"],"names":["resolveSiteTimeZone","computePosDayReport","posDayReportCsv","posDayReportRange","POS_DAY_REPORT_RETURNS_CEILING","POS_DAY_REPORT_SALES_CEILING","POS_DAY_REPORT_SHIFTS_CEILING","authorizePosOps","defaultPosOpsDeps","readPosShiftReport","POS_CURRENCY","REGISTERS_CEILING","queryValue","value","String","Array","isArray","trim","shiftOf","id","data","status","openedAtMs","Number","openingFloatCents","cashEvents","storeCurrency","hostRef","snapshot","collection","doc","get","catch","chosen","exists","toLowerCase","test","readPosDayReport","deps","input","hostSnapshot","register","currency","Promise","all","timeZone","org","nowMs","now","range","from","to","ok","error","startMs","endMs","registersSnapshot","limit","registers","docs","registerNames","salesSnapshot","where","orderBy","truncated","length","sales","slice","map","perRegister","returnsSnapshot","openedSnapshot","closedSnapshot","ref","returns","tenders","registerId","cashierId","refundedCents","filter","tender","method","amountCents","shiftDocs","shifts","Map","keyOf","shiftId","entry","shift","set","byId","openShiftId","push","sale","missing","undefined","has","values","firestore","snapshots","getAll","forEach","index","shiftList","sort","a","b","localeCompare","liveReport","memberIds","Set","add","refund","openedByName","openedBy","closedByName","closedBy","memberNames","uid","memberName","report","hostId","storeName","generatedAtMs","flatMap","handlePosDayReport","req","body","query","gate","format","read","staff","csv","filename","createPosDayReportHandler","res","outcome","setHeader","send","json","console","posDayReportHandler"],"mappings":"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"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ import { type PosHardwareOrder, type PosHardwareOrderStatus, type PosHardwareTracking } from '../model/pos-hardware';
18
+ /** An order as the staff page lists it. */
19
+ declare function staffView(id: string, order: PosHardwareOrder): {
20
+ id: string;
21
+ number: string;
22
+ orgId: string;
23
+ hostId: string;
24
+ status: PosHardwareOrderStatus;
25
+ contactEmail: string;
26
+ lines: import("../model/pos-hardware").PosHardwareLine[];
27
+ subtotalCents: number;
28
+ shipping: {
29
+ method: import("../model/pos-hardware").PosHardwareShippingMethod;
30
+ label: string;
31
+ cents: number;
32
+ name: string | null;
33
+ phone: string | null;
34
+ address: import("../model/pos-hardware").PosHardwareAddress | null;
35
+ taxRegion?: import("../model/pos-hardware").PosHardwareTaxRegion | null;
36
+ };
37
+ taxCents: number;
38
+ totalCents: number;
39
+ stripeCheckoutSessionId: string;
40
+ stripePaymentIntentId: string;
41
+ stripeHardwareOrderId: string;
42
+ tracking: PosHardwareTracking;
43
+ livemode: boolean;
44
+ refundPending: boolean;
45
+ createdAtMs: number;
46
+ updatedAtMs: number;
47
+ };
48
+ export type StaffHardwareOrderView = ReturnType<typeof staffView>;
49
+ declare function handler(request: Request): Promise<Response>;
50
+ export { handler as posHardwareAdminOrders };