@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,572 @@
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 { zonedDayKey, zonedWallTimeToInstant } from "@aglyn/shared-util-timestamp/zoned-time";
18
+ import { computePosShiftReport, posCsvCell, POS_TENDER_LABELS, POS_TENDER_METHODS } from "./commerce-pos-ops.js";
19
+ import { currencyMinorDigits } from "./order-fulfillment.js";
20
+ /*==========================================
21
+ * DAY AND EVENT TOTALS (AGL-4037): every register, every shift and every
22
+ * cashier on one site, over one day or a run of days in the store's time zone.
23
+ *
24
+ * GET /api/commerce/pos-day-report?hostId=…&from=YYYY-MM-DD&to=YYYY-MM-DD[&format=csv]
25
+ *
26
+ * The figures are the shift report's own (`computePosShiftReport`), run over
27
+ * the range instead of over one shift, and again per register and per
28
+ * cashier, so a day's total and the X/Z reports of its shifts can never
29
+ * disagree on what a gross sale, a tip or a cash sale is.
30
+ *
31
+ * What the range holds:
32
+ * - SALES: register orders (`channel: 'pos'`) rung in the range, by their
33
+ * `createdAtMs`; online orders never count. A pending sale took nothing
34
+ * and a canceled one gave it back, so neither counts, as on a Z report.
35
+ * - REFUNDS: returns rung at a register in the range, whenever the sale was.
36
+ * - SHIFTS: every shift that opened or closed in the range, that a sale in
37
+ * the range was rung in, or that is still open.
38
+ * - THE DRAWER: the floats of the shifts that opened in the range, and the
39
+ * paid in, paid out, safe drops and cash refunds recorded in it.
40
+ *
41
+ * Pure and client-safe: the route computes with it, the console renders and
42
+ * exports with it, and the native register mirrors these types.
43
+ *=========================================*/ /** The route, as the console and the native register call it. */ export const POS_DAY_REPORT_ROUTE = '/api/commerce/pos-day-report';
44
+ /** The longest range one report covers: a month of trading, or a long event. */ export const POS_DAY_REPORT_MAX_DAYS = 31;
45
+ /** The most sales one report reads; past it the report says it is partial. */ export const POS_DAY_REPORT_SALES_CEILING = 5000;
46
+ /** The most returns one report reads per register. */ export const POS_DAY_REPORT_RETURNS_CEILING = 2000;
47
+ /** The most shifts one report lists. */ export const POS_DAY_REPORT_SHIFTS_CEILING = 500;
48
+ /*==========================================
49
+ * THE RANGE
50
+ *=========================================*/ const DAY_KEY = /^(\d{4})-(\d{2})-(\d{2})$/;
51
+ function dayParts(key) {
52
+ const match = DAY_KEY.exec(key);
53
+ if (!match) return null;
54
+ const year = Number(match[1]);
55
+ const month = Number(match[2]);
56
+ const day = Number(match[3]);
57
+ const check = new Date(Date.UTC(year, month - 1, day));
58
+ if (check.getUTCFullYear() !== year || check.getUTCMonth() !== month - 1 || check.getUTCDate() !== day) {
59
+ return null;
60
+ }
61
+ return {
62
+ year,
63
+ month,
64
+ day
65
+ };
66
+ }
67
+ /**
68
+ * The days a report covers, as instants in the store's zone. No `from` is
69
+ * today there; no `to` is the same day as `from`. Midnight is found on the
70
+ * zone's own calendar, so a day a clock change falls in is 23 or 25 hours.
71
+ */ export function posDayReportRange(input) {
72
+ var _input_from, _input_to;
73
+ const rawFrom = String((_input_from = input.from) != null ? _input_from : '').trim();
74
+ const rawTo = String((_input_to = input.to) != null ? _input_to : '').trim();
75
+ const from = rawFrom || zonedDayKey(input.nowMs, input.timeZone);
76
+ const to = rawTo || from;
77
+ const start = dayParts(from);
78
+ const end = dayParts(to);
79
+ if (!start || !end) return {
80
+ ok: false,
81
+ error: 'Choose the days as YYYY-MM-DD.'
82
+ };
83
+ const days = Math.round((Date.UTC(end.year, end.month - 1, end.day) - Date.UTC(start.year, start.month - 1, start.day)) / 86400000) + 1;
84
+ if (days < 1) return {
85
+ ok: false,
86
+ error: 'The last day comes before the first.'
87
+ };
88
+ if (days > POS_DAY_REPORT_MAX_DAYS) {
89
+ return {
90
+ ok: false,
91
+ error: `Choose ${POS_DAY_REPORT_MAX_DAYS} days or fewer.`
92
+ };
93
+ }
94
+ return {
95
+ ok: true,
96
+ from,
97
+ to,
98
+ startMs: zonedWallTimeToInstant(start, input.timeZone),
99
+ endMs: zonedWallTimeToInstant(_extends({}, end, {
100
+ day: end.day + 1
101
+ }), input.timeZone),
102
+ days
103
+ };
104
+ }
105
+ const whole = (value)=>{
106
+ const number = Math.round(Number(value));
107
+ return Number.isFinite(number) ? number : 0;
108
+ };
109
+ function totalsOf(report) {
110
+ return {
111
+ orderCount: report.orderCount,
112
+ grossSalesCents: report.grossSalesCents,
113
+ discountsCents: report.discountsCents,
114
+ taxCents: report.taxCents,
115
+ tipsCents: report.tipsCents,
116
+ refundCount: report.refundCount,
117
+ refundsCents: report.refundsCents,
118
+ netSalesCents: report.netSalesCents,
119
+ salesByTender: report.salesByTender
120
+ };
121
+ }
122
+ const NO_DRAWER = {
123
+ openingFloatCents: 0,
124
+ cashEvents: []
125
+ };
126
+ /** Sales figures alone, through the shift report's calculator. */ const salesFigures = (sales, refunds)=>totalsOf(computePosShiftReport({
127
+ shift: NO_DRAWER,
128
+ sales,
129
+ refunds
130
+ }));
131
+ function groupBy(items, key) {
132
+ const groups = new Map();
133
+ for (const item of items){
134
+ const id = key(item);
135
+ const group = groups.get(id);
136
+ if (group) group.push(item);
137
+ else groups.set(id, [
138
+ item
139
+ ]);
140
+ }
141
+ return groups;
142
+ }
143
+ /** The day report from what the route read. */ export function computePosDayReport(input) {
144
+ const { startMs, endMs } = input.range;
145
+ const inRange = (atMs)=>{
146
+ const at = Number(atMs);
147
+ return Number.isFinite(at) && at >= startMs && at < endMs;
148
+ };
149
+ const shifts = [
150
+ ...input.shifts
151
+ ].sort((a, b)=>a.openedAtMs - b.openedAtMs || a.id.localeCompare(b.id));
152
+ // The drawer over the range: floats counted the day their shift opened,
153
+ // and the cash moved while the range ran.
154
+ const openingFloatCents = shifts.filter((shift)=>inRange(shift.openedAtMs)).reduce((sum, shift)=>sum + whole(shift.openingFloatCents), 0);
155
+ const cashEvents = shifts.flatMap((shift)=>{
156
+ var _shift_cashEvents;
157
+ return ((_shift_cashEvents = shift.cashEvents) != null ? _shift_cashEvents : []).filter((event)=>inRange(event == null ? void 0 : event.atMs));
158
+ });
159
+ const overall = computePosShiftReport({
160
+ shift: {
161
+ openingFloatCents,
162
+ cashEvents
163
+ },
164
+ sales: input.sales,
165
+ refunds: input.returns
166
+ });
167
+ const byTender = POS_TENDER_METHODS.filter((method)=>{
168
+ var _overall_salesByTender_method, _overall_refundsByTender_method;
169
+ return ((_overall_salesByTender_method = overall.salesByTender[method]) != null ? _overall_salesByTender_method : 0) > 0 || ((_overall_refundsByTender_method = overall.refundsByTender[method]) != null ? _overall_refundsByTender_method : 0) > 0;
170
+ }).map((method)=>{
171
+ var _overall_salesByTender_method, _overall_refundsByTender_method;
172
+ const salesCents = (_overall_salesByTender_method = overall.salesByTender[method]) != null ? _overall_salesByTender_method : 0;
173
+ const refundsCents = (_overall_refundsByTender_method = overall.refundsByTender[method]) != null ? _overall_refundsByTender_method : 0;
174
+ return {
175
+ method,
176
+ label: POS_TENDER_LABELS[method],
177
+ salesCents,
178
+ refundsCents,
179
+ netCents: salesCents - refundsCents
180
+ };
181
+ });
182
+ const registerName = (id)=>input.registerNames[id] || id || 'Unknown register';
183
+ const salesByRegister = groupBy(input.sales, (sale)=>{
184
+ var _sale_registerId;
185
+ return String((_sale_registerId = sale.registerId) != null ? _sale_registerId : '');
186
+ });
187
+ const returnsByRegister = groupBy(input.returns, (refund)=>refund.registerId);
188
+ const registerIds = new Set([
189
+ ...salesByRegister.keys(),
190
+ ...returnsByRegister.keys()
191
+ ]);
192
+ const byRegister = [
193
+ ...registerIds
194
+ ].map((registerId)=>{
195
+ var _salesByRegister_get, _returnsByRegister_get;
196
+ return _extends({
197
+ registerId,
198
+ name: registerName(registerId)
199
+ }, salesFigures((_salesByRegister_get = salesByRegister.get(registerId)) != null ? _salesByRegister_get : [], (_returnsByRegister_get = returnsByRegister.get(registerId)) != null ? _returnsByRegister_get : []));
200
+ }).filter((row)=>row.orderCount > 0 || row.refundCount > 0).sort((a, b)=>a.name.localeCompare(b.name) || a.registerId.localeCompare(b.registerId));
201
+ const memberName = (id)=>id ? input.memberNames[id] || 'Staff member' : 'No cashier recorded';
202
+ const salesByCashier = groupBy(input.sales, (sale)=>{
203
+ var _sale_cashierId;
204
+ return String((_sale_cashierId = sale.cashierId) != null ? _sale_cashierId : '');
205
+ });
206
+ const returnsByCashier = groupBy(input.returns, (refund)=>{
207
+ var _refund_cashierId;
208
+ return String((_refund_cashierId = refund.cashierId) != null ? _refund_cashierId : '');
209
+ });
210
+ const cashierIds = new Set([
211
+ ...salesByCashier.keys(),
212
+ ...returnsByCashier.keys()
213
+ ]);
214
+ const byCashier = [
215
+ ...cashierIds
216
+ ].map((memberId)=>{
217
+ var _salesByCashier_get, _returnsByCashier_get;
218
+ return _extends({
219
+ memberId,
220
+ name: memberName(memberId)
221
+ }, salesFigures((_salesByCashier_get = salesByCashier.get(memberId)) != null ? _salesByCashier_get : [], (_returnsByCashier_get = returnsByCashier.get(memberId)) != null ? _returnsByCashier_get : []));
222
+ }).filter((row)=>row.orderCount > 0 || row.refundCount > 0).sort((a, b)=>b.netSalesCents - a.netSalesCents || a.name.localeCompare(b.name));
223
+ const shiftRows = shifts.map((shift)=>{
224
+ var _ref, _ref1;
225
+ const closed = shift.status === 'closed';
226
+ const report = closed ? shift.report : shift.liveReport;
227
+ return {
228
+ shiftId: shift.id,
229
+ registerId: shift.registerId,
230
+ registerName: registerName(shift.registerId),
231
+ status: closed ? 'closed' : 'open',
232
+ openedAtMs: whole(shift.openedAtMs),
233
+ closedAtMs: closed && shift.closedAtMs != null ? whole(shift.closedAtMs) : null,
234
+ openedByName: shift.openedByName || memberName(shift.openedBy),
235
+ closedByName: closed ? shift.closedByName || (shift.closedBy ? memberName(shift.closedBy) : null) : null,
236
+ openingFloatCents: whole(shift.openingFloatCents),
237
+ netSalesCents: whole((_ref = closed ? shift.netSalesCents : null) != null ? _ref : report == null ? void 0 : report.netSalesCents),
238
+ expectedCashCents: whole((_ref1 = closed ? shift.expectedCashCents : null) != null ? _ref1 : report == null ? void 0 : report.expectedCashCents),
239
+ countedCashCents: closed && shift.countedCashCents != null ? whole(shift.countedCashCents) : null,
240
+ varianceCents: closed && shift.varianceCents != null ? whole(shift.varianceCents) : null
241
+ };
242
+ });
243
+ const closedRows = shiftRows.filter((row)=>row.status === 'closed');
244
+ const openRows = shiftRows.filter((row)=>row.status === 'open');
245
+ const sum = (rows, pick)=>rows.reduce((total, row)=>{
246
+ var _pick;
247
+ return total + ((_pick = pick(row)) != null ? _pick : 0);
248
+ }, 0);
249
+ return _extends({
250
+ hostId: input.hostId,
251
+ storeName: input.storeName,
252
+ currency: input.currency,
253
+ timeZone: input.timeZone,
254
+ from: input.range.from,
255
+ to: input.range.to,
256
+ startMs,
257
+ endMs,
258
+ days: input.range.days,
259
+ generatedAtMs: input.generatedAtMs,
260
+ totals: totalsOf(overall),
261
+ byTender,
262
+ byRegister,
263
+ byCashier,
264
+ shifts: shiftRows,
265
+ cash: {
266
+ openingFloatCents: overall.openingFloatCents,
267
+ cashSalesCents: overall.cashSalesCents,
268
+ cashRefundsCents: overall.cashRefundsCents,
269
+ paidInCents: overall.paidInCents,
270
+ paidOutCents: overall.paidOutCents,
271
+ dropsCents: overall.dropsCents,
272
+ closedShiftCount: closedRows.length,
273
+ openShiftCount: openRows.length,
274
+ expectedCashCents: sum(closedRows, (row)=>row.expectedCashCents),
275
+ countedCashCents: sum(closedRows, (row)=>row.countedCashCents),
276
+ varianceCents: sum(closedRows, (row)=>row.varianceCents),
277
+ openExpectedCashCents: sum(openRows, (row)=>row.expectedCashCents)
278
+ }
279
+ }, input.truncated ? {
280
+ truncated: true
281
+ } : {});
282
+ }
283
+ /** Whether the range had nothing to report: no sale, no return, no shift. */ export function posDayReportIsEmpty(report) {
284
+ return !report.totals.orderCount && !report.totals.refundCount && !report.shifts.length;
285
+ }
286
+ /*==========================================
287
+ * EXPORT
288
+ *=========================================*/ /** Minor units as a spreadsheet number in the store's currency: `12.34`, `1200` for yen. */ export function posDayAmount(cents, currency) {
289
+ if (cents == null) return '';
290
+ const digits = currencyMinorDigits(currency);
291
+ return (Math.round(cents) / 10 ** digits).toFixed(digits);
292
+ }
293
+ /** The range as a reader says it: `2026-10-24` or `2026-10-24 to 2026-10-26`. */ export function posDayRangeLabel(report) {
294
+ return report.from === report.to ? report.from : `${report.from} to ${report.to}`;
295
+ }
296
+ /**
297
+ * The report as one spreadsheet: a heading block, the sales by register and
298
+ * cashier under the total, the tenders, the shifts and the drawer — each a
299
+ * block of its own with its own header row, amounts in the store's currency.
300
+ */ export function posDayReportCsv(report) {
301
+ const money = (cents)=>posDayAmount(cents, report.currency);
302
+ const iso = (ms)=>ms ? new Date(ms).toISOString() : '';
303
+ const salesRow = (section, name, row)=>[
304
+ section,
305
+ name,
306
+ row.orderCount,
307
+ money(row.grossSalesCents),
308
+ money(row.discountsCents),
309
+ money(row.taxCents),
310
+ money(row.tipsCents),
311
+ row.refundCount,
312
+ money(row.refundsCents),
313
+ money(row.netSalesCents)
314
+ ];
315
+ const blocks = [
316
+ [
317
+ [
318
+ 'Report',
319
+ 'Day totals'
320
+ ],
321
+ [
322
+ 'Days',
323
+ posDayRangeLabel(report)
324
+ ],
325
+ [
326
+ 'Time zone',
327
+ report.timeZone
328
+ ],
329
+ [
330
+ 'Currency',
331
+ report.currency.toUpperCase()
332
+ ],
333
+ ...report.truncated ? [
334
+ [
335
+ 'Note',
336
+ 'More activity than one report reads; the figures are partial.'
337
+ ]
338
+ ] : []
339
+ ],
340
+ [
341
+ [
342
+ 'Section',
343
+ 'Name',
344
+ 'Orders',
345
+ 'Gross sales',
346
+ 'Discounts',
347
+ 'Tax',
348
+ 'Tips',
349
+ 'Refunds',
350
+ 'Refunded',
351
+ 'Net sales'
352
+ ],
353
+ salesRow('Total', 'Every register', report.totals),
354
+ ...report.byRegister.map((row)=>salesRow('Register', row.name, row)),
355
+ ...report.byCashier.map((row)=>salesRow('Cashier', row.name, row))
356
+ ],
357
+ [
358
+ [
359
+ 'Tender',
360
+ 'Sales',
361
+ 'Refunds',
362
+ 'Net'
363
+ ],
364
+ ...report.byTender.map((row)=>[
365
+ row.label,
366
+ money(row.salesCents),
367
+ money(row.refundsCents),
368
+ money(row.netCents)
369
+ ])
370
+ ],
371
+ [
372
+ [
373
+ 'Shift',
374
+ 'Register',
375
+ 'Status',
376
+ 'Opened',
377
+ 'Opened by',
378
+ 'Closed',
379
+ 'Closed by',
380
+ 'Starting cash',
381
+ 'Net sales',
382
+ 'Expected cash',
383
+ 'Counted cash',
384
+ 'Variance'
385
+ ],
386
+ ...report.shifts.map((row)=>{
387
+ var _row_closedByName;
388
+ return [
389
+ row.shiftId,
390
+ row.registerName,
391
+ row.status === 'open' ? 'Open' : 'Closed',
392
+ iso(row.openedAtMs),
393
+ row.openedByName,
394
+ iso(row.closedAtMs),
395
+ (_row_closedByName = row.closedByName) != null ? _row_closedByName : '',
396
+ money(row.openingFloatCents),
397
+ money(row.netSalesCents),
398
+ money(row.expectedCashCents),
399
+ money(row.countedCashCents),
400
+ money(row.varianceCents)
401
+ ];
402
+ })
403
+ ],
404
+ [
405
+ [
406
+ 'Cash drawers',
407
+ 'Amount'
408
+ ],
409
+ [
410
+ 'Starting cash',
411
+ money(report.cash.openingFloatCents)
412
+ ],
413
+ [
414
+ 'Cash sales',
415
+ money(report.cash.cashSalesCents)
416
+ ],
417
+ [
418
+ 'Paid in',
419
+ money(report.cash.paidInCents)
420
+ ],
421
+ [
422
+ 'Paid out',
423
+ money(report.cash.paidOutCents)
424
+ ],
425
+ [
426
+ 'Safe drops',
427
+ money(report.cash.dropsCents)
428
+ ],
429
+ [
430
+ 'Cash refunds',
431
+ money(report.cash.cashRefundsCents)
432
+ ],
433
+ [
434
+ 'Expected in closed drawers',
435
+ money(report.cash.expectedCashCents)
436
+ ],
437
+ [
438
+ 'Counted in closed drawers',
439
+ money(report.cash.countedCashCents)
440
+ ],
441
+ [
442
+ 'Variance',
443
+ money(report.cash.varianceCents)
444
+ ],
445
+ [
446
+ 'Expected in open drawers',
447
+ money(report.cash.openExpectedCashCents)
448
+ ]
449
+ ]
450
+ ];
451
+ return blocks.map((rows)=>rows.map((row)=>row.map(posCsvCell).join(',')).join('\r\n')).join('\r\n\r\n');
452
+ }
453
+ /** The report's rows as the screen, the browser print and an 80 mm printer all read them. */ export function posDayReportSections(report, money) {
454
+ const t = report.totals;
455
+ const c = report.cash;
456
+ return [
457
+ {
458
+ section: 'Sales',
459
+ rows: [
460
+ {
461
+ label: 'Orders',
462
+ value: String(t.orderCount)
463
+ },
464
+ {
465
+ label: 'Gross sales',
466
+ value: money(t.grossSalesCents)
467
+ },
468
+ {
469
+ label: 'Discounts',
470
+ value: money(t.discountsCents)
471
+ },
472
+ {
473
+ label: 'Tax',
474
+ value: money(t.taxCents)
475
+ },
476
+ {
477
+ label: 'Tips',
478
+ value: money(t.tipsCents)
479
+ },
480
+ {
481
+ label: `Refunds (${t.refundCount})`,
482
+ value: money(-t.refundsCents)
483
+ },
484
+ {
485
+ label: 'Net sales',
486
+ value: money(t.netSalesCents),
487
+ strong: true
488
+ }
489
+ ]
490
+ },
491
+ {
492
+ section: 'By tender',
493
+ rows: report.byTender.length ? report.byTender.map((row)=>({
494
+ label: row.label,
495
+ value: money(row.netCents)
496
+ })) : [
497
+ {
498
+ label: 'No sales',
499
+ value: ''
500
+ }
501
+ ]
502
+ },
503
+ ...report.byRegister.length ? [
504
+ {
505
+ section: 'By register',
506
+ rows: report.byRegister.map((row)=>({
507
+ label: row.name,
508
+ value: money(row.netSalesCents)
509
+ }))
510
+ }
511
+ ] : [],
512
+ ...report.byCashier.length ? [
513
+ {
514
+ section: 'By cashier',
515
+ rows: report.byCashier.map((row)=>({
516
+ label: row.name,
517
+ value: money(row.netSalesCents)
518
+ }))
519
+ }
520
+ ] : [],
521
+ {
522
+ section: 'Cash drawers',
523
+ rows: [
524
+ {
525
+ label: 'Starting cash',
526
+ value: money(c.openingFloatCents)
527
+ },
528
+ {
529
+ label: 'Cash sales',
530
+ value: money(c.cashSalesCents)
531
+ },
532
+ {
533
+ label: 'Paid in',
534
+ value: money(c.paidInCents)
535
+ },
536
+ {
537
+ label: 'Paid out',
538
+ value: money(-c.paidOutCents)
539
+ },
540
+ {
541
+ label: 'Safe drops',
542
+ value: money(-c.dropsCents)
543
+ },
544
+ {
545
+ label: 'Cash refunds',
546
+ value: money(-c.cashRefundsCents)
547
+ },
548
+ {
549
+ label: `Expected (${c.closedShiftCount} closed)`,
550
+ value: money(c.expectedCashCents)
551
+ },
552
+ {
553
+ label: 'Counted',
554
+ value: money(c.countedCashCents)
555
+ },
556
+ {
557
+ label: c.varianceCents < 0 ? 'Short' : c.varianceCents > 0 ? 'Over' : 'Balanced',
558
+ value: money(c.varianceCents),
559
+ strong: true
560
+ },
561
+ ...c.openShiftCount ? [
562
+ {
563
+ label: `Still open (${c.openShiftCount})`,
564
+ value: money(c.openExpectedCashCents)
565
+ }
566
+ ] : []
567
+ ]
568
+ }
569
+ ];
570
+ }
571
+
572
+ //# sourceMappingURL=commerce-pos-day-report.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/model/commerce-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 {\n zonedDayKey,\n zonedWallTimeToInstant,\n} from '@aglyn/shared-util-timestamp/zoned-time'\nimport {\n computePosShiftReport,\n posCsvCell,\n POS_TENDER_LABELS,\n POS_TENDER_METHODS,\n type PosCashEvent,\n type PosShift,\n type PosShiftRefund,\n type PosShiftReport,\n type PosShiftSale,\n type PosTenderMethod,\n} from './commerce-pos-ops'\nimport { currencyMinorDigits } from './order-fulfillment'\n\n/*==========================================\n * DAY AND EVENT TOTALS (AGL-4037): every register, every shift and every\n * cashier on one site, over one day or a run of days in the store's time zone.\n *\n * GET /api/commerce/pos-day-report?hostId=…&from=YYYY-MM-DD&to=YYYY-MM-DD[&format=csv]\n *\n * The figures are the shift report's own (`computePosShiftReport`), run over\n * the range instead of over one shift, and again per register and per\n * cashier, so a day's total and the X/Z reports of its shifts can never\n * disagree on what a gross sale, a tip or a cash sale is.\n *\n * What the range holds:\n * - SALES: register orders (`channel: 'pos'`) rung in the range, by their\n * `createdAtMs`; online orders never count. A pending sale took nothing\n * and a canceled one gave it back, so neither counts, as on a Z report.\n * - REFUNDS: returns rung at a register in the range, whenever the sale was.\n * - SHIFTS: every shift that opened or closed in the range, that a sale in\n * the range was rung in, or that is still open.\n * - THE DRAWER: the floats of the shifts that opened in the range, and the\n * paid in, paid out, safe drops and cash refunds recorded in it.\n *\n * Pure and client-safe: the route computes with it, the console renders and\n * exports with it, and the native register mirrors these types.\n *=========================================*/\n\n/** The route, as the console and the native register call it. */\nexport const POS_DAY_REPORT_ROUTE = '/api/commerce/pos-day-report'\n/** The longest range one report covers: a month of trading, or a long event. */\nexport const POS_DAY_REPORT_MAX_DAYS = 31\n/** The most sales one report reads; past it the report says it is partial. */\nexport const POS_DAY_REPORT_SALES_CEILING = 5000\n/** The most returns one report reads per register. */\nexport const POS_DAY_REPORT_RETURNS_CEILING = 2000\n/** The most shifts one report lists. */\nexport const POS_DAY_REPORT_SHIFTS_CEILING = 500\n\n/** The sales figures every total, register row and cashier row carries. */\nexport interface PosDayTotals {\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 refundCount: number\n refundsCents: number\n /** Gross sales less refunds. */\n netSalesCents: number\n /** Sales by tender, the shift report's keys; a tender with nothing is absent. */\n salesByTender: Partial<Record<PosTenderMethod, number>>\n}\n\n/** One tender's line: what it took, what went back to it, and the difference. */\nexport interface PosDayTenderRow {\n method: PosTenderMethod\n label: string\n salesCents: number\n refundsCents: number\n netCents: number\n}\n\nexport interface PosDayRegisterRow extends PosDayTotals {\n registerId: string\n /** The register's name, or its id when the register has since been removed. */\n name: string\n}\n\nexport interface PosDayCashierRow extends PosDayTotals {\n /** The member who rang the sales, or `''` for sales that name nobody. */\n memberId: string\n name: string\n}\n\n/** A shift in the range, as the shifts table lists it. */\nexport interface PosDayShiftRow {\n shiftId: string\n registerId: string\n registerName: string\n status: 'open' | 'closed'\n openedAtMs: number\n closedAtMs: number | null\n openedByName: string\n closedByName: string | null\n openingFloatCents: number\n /** The Z report's net sales, or the X report's so far while it is open. */\n netSalesCents: number\n /** The Z report's expected cash, or what the drawer should hold now while it is open. */\n expectedCashCents: number\n /** What the closing cashier counted; `null` while the shift is open. */\n countedCashCents: number | null\n /** counted − expected: positive is over, negative is short; `null` while open. */\n varianceCents: number | null\n}\n\n/** The cash drawers, summed over every register. */\nexport interface PosDayCash {\n /** The floats of the shifts that opened in the range. */\n openingFloatCents: number\n /** Cash taken by sales in the range (change handed back is not counted), plus cash tips. */\n cashSalesCents: number\n /** Returns paid out of a drawer in the range. */\n cashRefundsCents: number\n paidInCents: number\n paidOutCents: number\n dropsCents: number\n closedShiftCount: number\n openShiftCount: number\n /** What the closed shifts' drawers should have held when they were counted. */\n expectedCashCents: number\n /** What was counted in them. */\n countedCashCents: number\n /** counted − expected over the closed shifts: positive is over, negative is short. */\n varianceCents: number\n /** What the drawers still open should hold right now; not counted yet. */\n openExpectedCashCents: number\n}\n\n/** `GET /api/commerce/pos-day-report`'s answer. Every amount is integer minor units. */\nexport interface PosDayReport {\n hostId: string\n /** The site's name, as the printed report heads it. */\n storeName: string\n /** The store's currency, lower case ISO 4217 (`usd`). */\n currency: string\n /** The zone the days are counted in (the site's, else the workspace's, else UTC). */\n timeZone: string\n /** The first day, `YYYY-MM-DD`, inclusive. */\n from: string\n /** The last day, `YYYY-MM-DD`, inclusive. */\n to: string\n /** The instant the first day starts in `timeZone`. */\n startMs: number\n /** The instant after the last day ends in `timeZone` (exclusive). */\n endMs: number\n days: number\n generatedAtMs: number\n totals: PosDayTotals\n byTender: PosDayTenderRow[]\n byRegister: PosDayRegisterRow[]\n byCashier: PosDayCashierRow[]\n shifts: PosDayShiftRow[]\n cash: PosDayCash\n /** More sales, returns or shifts were in the range than one report reads. */\n truncated?: boolean\n}\n\n/*==========================================\n * THE RANGE\n *=========================================*/\n\nconst DAY_KEY = /^(\\d{4})-(\\d{2})-(\\d{2})$/\n\nfunction dayParts(key: string): { year: number; month: number; day: number } | null {\n const match = DAY_KEY.exec(key)\n if (!match) return null\n const year = Number(match[1])\n const month = Number(match[2])\n const day = Number(match[3])\n const check = new Date(Date.UTC(year, month - 1, day))\n if (\n check.getUTCFullYear() !== year ||\n check.getUTCMonth() !== month - 1 ||\n check.getUTCDate() !== day\n ) {\n return null\n }\n return { year, month, day }\n}\n\nexport type PosDayRange =\n | { ok: true; from: string; to: string; startMs: number; endMs: number; days: number }\n | { ok: false; error: string }\n\n/**\n * The days a report covers, as instants in the store's zone. No `from` is\n * today there; no `to` is the same day as `from`. Midnight is found on the\n * zone's own calendar, so a day a clock change falls in is 23 or 25 hours.\n */\nexport function posDayReportRange(input: {\n from?: unknown\n to?: unknown\n nowMs: number\n timeZone: string\n}): PosDayRange {\n const rawFrom = String(input.from ?? '').trim()\n const rawTo = String(input.to ?? '').trim()\n const from = rawFrom || zonedDayKey(input.nowMs, input.timeZone)\n const to = rawTo || from\n const start = dayParts(from)\n const end = dayParts(to)\n if (!start || !end) return { ok: false, error: 'Choose the days as YYYY-MM-DD.' }\n const days =\n Math.round(\n (Date.UTC(end.year, end.month - 1, end.day) - Date.UTC(start.year, start.month - 1, start.day)) /\n 86_400_000,\n ) + 1\n if (days < 1) return { ok: false, error: 'The last day comes before the first.' }\n if (days > POS_DAY_REPORT_MAX_DAYS) {\n return { ok: false, error: `Choose ${POS_DAY_REPORT_MAX_DAYS} days or fewer.` }\n }\n return {\n ok: true,\n from,\n to,\n startMs: zonedWallTimeToInstant(start, input.timeZone),\n endMs: zonedWallTimeToInstant({ ...end, day: end.day + 1 }, input.timeZone),\n days,\n }\n}\n\n/*==========================================\n * THE FIGURES\n *=========================================*/\n\n/** A register sale as the day report reads it. */\nexport interface PosDaySale extends PosShiftSale {\n registerId?: string\n cashierId?: string\n shiftId?: string\n}\n\n/** A register return as the day report reads it: its register and who rang it. */\nexport interface PosDayReturn extends PosShiftRefund {\n registerId: string\n cashierId?: string\n}\n\n/** A shift in the range, with the X report of an open one already read. */\nexport type PosDayShift = Pick<\n PosShift,\n | 'registerId'\n | 'status'\n | 'openedAtMs'\n | 'openedBy'\n | 'openedByName'\n | 'openingFloatCents'\n | 'cashEvents'\n | 'closedAtMs'\n | 'closedBy'\n | 'closedByName'\n | 'countedCashCents'\n | 'expectedCashCents'\n | 'varianceCents'\n | 'netSalesCents'\n | 'report'\n> & { id: string; liveReport?: PosShiftReport }\n\nexport interface PosDayReportInput {\n hostId: string\n storeName: string\n currency: string\n timeZone: string\n range: Extract<PosDayRange, { ok: true }>\n generatedAtMs: number\n sales: readonly PosDaySale[]\n returns: readonly PosDayReturn[]\n shifts: readonly PosDayShift[]\n /** Register id → name. */\n registerNames: Readonly<Record<string, string>>\n /** Member id → display name. */\n memberNames: Readonly<Record<string, string>>\n truncated?: boolean\n}\n\nconst whole = (value: unknown): number => {\n const number = Math.round(Number(value))\n return Number.isFinite(number) ? number : 0\n}\n\nfunction totalsOf(report: PosShiftReport): PosDayTotals {\n return {\n orderCount: report.orderCount,\n grossSalesCents: report.grossSalesCents,\n discountsCents: report.discountsCents,\n taxCents: report.taxCents,\n tipsCents: report.tipsCents,\n refundCount: report.refundCount,\n refundsCents: report.refundsCents,\n netSalesCents: report.netSalesCents,\n salesByTender: report.salesByTender,\n }\n}\n\nconst NO_DRAWER = { openingFloatCents: 0, cashEvents: [] as PosCashEvent[] }\n\n/** Sales figures alone, through the shift report's calculator. */\nconst salesFigures = (sales: readonly PosShiftSale[], refunds: readonly PosShiftRefund[]) =>\n totalsOf(computePosShiftReport({ shift: NO_DRAWER, sales, refunds }))\n\nfunction groupBy<T>(items: readonly T[], key: (item: T) => string): Map<string, T[]> {\n const groups = new Map<string, T[]>()\n for (const item of items) {\n const id = key(item)\n const group = groups.get(id)\n if (group) group.push(item)\n else groups.set(id, [item])\n }\n return groups\n}\n\n/** The day report from what the route read. */\nexport function computePosDayReport(input: PosDayReportInput): PosDayReport {\n const { startMs, endMs } = input.range\n const inRange = (atMs: unknown) => {\n const at = Number(atMs)\n return Number.isFinite(at) && at >= startMs && at < endMs\n }\n const shifts = [...input.shifts].sort((a, b) => a.openedAtMs - b.openedAtMs || a.id.localeCompare(b.id))\n // The drawer over the range: floats counted the day their shift opened,\n // and the cash moved while the range ran.\n const openingFloatCents = shifts\n .filter((shift) => inRange(shift.openedAtMs))\n .reduce((sum, shift) => sum + whole(shift.openingFloatCents), 0)\n const cashEvents = shifts.flatMap((shift) =>\n (shift.cashEvents ?? []).filter((event) => inRange(event?.atMs)),\n )\n const overall = computePosShiftReport({\n shift: { openingFloatCents, cashEvents },\n sales: input.sales,\n refunds: input.returns,\n })\n\n const byTender: PosDayTenderRow[] = POS_TENDER_METHODS.filter(\n (method) => (overall.salesByTender[method] ?? 0) > 0 || (overall.refundsByTender[method] ?? 0) > 0,\n ).map((method) => {\n const salesCents = overall.salesByTender[method] ?? 0\n const refundsCents = overall.refundsByTender[method] ?? 0\n return {\n method,\n label: POS_TENDER_LABELS[method],\n salesCents,\n refundsCents,\n netCents: salesCents - refundsCents,\n }\n })\n\n const registerName = (id: string) => input.registerNames[id] || id || 'Unknown register'\n const salesByRegister = groupBy(input.sales, (sale) => String(sale.registerId ?? ''))\n const returnsByRegister = groupBy(input.returns, (refund) => refund.registerId)\n const registerIds = new Set([...salesByRegister.keys(), ...returnsByRegister.keys()])\n const byRegister: PosDayRegisterRow[] = [...registerIds]\n .map((registerId) => ({\n registerId,\n name: registerName(registerId),\n ...salesFigures(salesByRegister.get(registerId) ?? [], returnsByRegister.get(registerId) ?? []),\n }))\n .filter((row) => row.orderCount > 0 || row.refundCount > 0)\n .sort((a, b) => a.name.localeCompare(b.name) || a.registerId.localeCompare(b.registerId))\n\n const memberName = (id: string) => (id ? input.memberNames[id] || 'Staff member' : 'No cashier recorded')\n const salesByCashier = groupBy(input.sales, (sale) => String(sale.cashierId ?? ''))\n const returnsByCashier = groupBy(input.returns, (refund) => String(refund.cashierId ?? ''))\n const cashierIds = new Set([...salesByCashier.keys(), ...returnsByCashier.keys()])\n const byCashier: PosDayCashierRow[] = [...cashierIds]\n .map((memberId) => ({\n memberId,\n name: memberName(memberId),\n ...salesFigures(salesByCashier.get(memberId) ?? [], returnsByCashier.get(memberId) ?? []),\n }))\n .filter((row) => row.orderCount > 0 || row.refundCount > 0)\n .sort((a, b) => b.netSalesCents - a.netSalesCents || a.name.localeCompare(b.name))\n\n const shiftRows: PosDayShiftRow[] = shifts.map((shift) => {\n const closed = shift.status === 'closed'\n const report = closed ? shift.report : shift.liveReport\n return {\n shiftId: shift.id,\n registerId: shift.registerId,\n registerName: registerName(shift.registerId),\n status: closed ? 'closed' : 'open',\n openedAtMs: whole(shift.openedAtMs),\n closedAtMs: closed && shift.closedAtMs != null ? whole(shift.closedAtMs) : null,\n openedByName: shift.openedByName || memberName(shift.openedBy),\n closedByName: closed ? shift.closedByName || (shift.closedBy ? memberName(shift.closedBy) : null) : null,\n openingFloatCents: whole(shift.openingFloatCents),\n netSalesCents: whole((closed ? shift.netSalesCents : null) ?? report?.netSalesCents),\n expectedCashCents: whole((closed ? shift.expectedCashCents : null) ?? report?.expectedCashCents),\n countedCashCents: closed && shift.countedCashCents != null ? whole(shift.countedCashCents) : null,\n varianceCents: closed && shift.varianceCents != null ? whole(shift.varianceCents) : null,\n }\n })\n const closedRows = shiftRows.filter((row) => row.status === 'closed')\n const openRows = shiftRows.filter((row) => row.status === 'open')\n const sum = (rows: PosDayShiftRow[], pick: (row: PosDayShiftRow) => number | null) =>\n rows.reduce((total, row) => total + (pick(row) ?? 0), 0)\n\n return {\n hostId: input.hostId,\n storeName: input.storeName,\n currency: input.currency,\n timeZone: input.timeZone,\n from: input.range.from,\n to: input.range.to,\n startMs,\n endMs,\n days: input.range.days,\n generatedAtMs: input.generatedAtMs,\n totals: totalsOf(overall),\n byTender,\n byRegister,\n byCashier,\n shifts: shiftRows,\n cash: {\n openingFloatCents: overall.openingFloatCents,\n cashSalesCents: overall.cashSalesCents,\n cashRefundsCents: overall.cashRefundsCents,\n paidInCents: overall.paidInCents,\n paidOutCents: overall.paidOutCents,\n dropsCents: overall.dropsCents,\n closedShiftCount: closedRows.length,\n openShiftCount: openRows.length,\n expectedCashCents: sum(closedRows, (row) => row.expectedCashCents),\n countedCashCents: sum(closedRows, (row) => row.countedCashCents),\n varianceCents: sum(closedRows, (row) => row.varianceCents),\n openExpectedCashCents: sum(openRows, (row) => row.expectedCashCents),\n },\n ...(input.truncated ? { truncated: true } : {}),\n }\n}\n\n/** Whether the range had nothing to report: no sale, no return, no shift. */\nexport function posDayReportIsEmpty(report: PosDayReport): boolean {\n return !report.totals.orderCount && !report.totals.refundCount && !report.shifts.length\n}\n\n/*==========================================\n * EXPORT\n *=========================================*/\n\n/** Minor units as a spreadsheet number in the store's currency: `12.34`, `1200` for yen. */\nexport function posDayAmount(cents: number | null | undefined, currency: string): string {\n if (cents == null) return ''\n const digits = currencyMinorDigits(currency)\n return (Math.round(cents) / 10 ** digits).toFixed(digits)\n}\n\n/** The range as a reader says it: `2026-10-24` or `2026-10-24 to 2026-10-26`. */\nexport function posDayRangeLabel(report: Pick<PosDayReport, 'from' | 'to'>): string {\n return report.from === report.to ? report.from : `${report.from} to ${report.to}`\n}\n\n/**\n * The report as one spreadsheet: a heading block, the sales by register and\n * cashier under the total, the tenders, the shifts and the drawer — each a\n * block of its own with its own header row, amounts in the store's currency.\n */\nexport function posDayReportCsv(report: PosDayReport): string {\n const money = (cents: number | null | undefined) => posDayAmount(cents, report.currency)\n const iso = (ms: number | null | undefined) => (ms ? new Date(ms).toISOString() : '')\n const salesRow = (section: string, name: string, row: PosDayTotals) => [\n section,\n name,\n row.orderCount,\n money(row.grossSalesCents),\n money(row.discountsCents),\n money(row.taxCents),\n money(row.tipsCents),\n row.refundCount,\n money(row.refundsCents),\n money(row.netSalesCents),\n ]\n const blocks: unknown[][][] = [\n [\n ['Report', 'Day totals'],\n ['Days', posDayRangeLabel(report)],\n ['Time zone', report.timeZone],\n ['Currency', report.currency.toUpperCase()],\n ...(report.truncated ? [['Note', 'More activity than one report reads; the figures are partial.']] : []),\n ],\n [\n ['Section', 'Name', 'Orders', 'Gross sales', 'Discounts', 'Tax', 'Tips', 'Refunds', 'Refunded', 'Net sales'],\n salesRow('Total', 'Every register', report.totals),\n ...report.byRegister.map((row) => salesRow('Register', row.name, row)),\n ...report.byCashier.map((row) => salesRow('Cashier', row.name, row)),\n ],\n [\n ['Tender', 'Sales', 'Refunds', 'Net'],\n ...report.byTender.map((row) => [row.label, money(row.salesCents), money(row.refundsCents), money(row.netCents)]),\n ],\n [\n [\n 'Shift',\n 'Register',\n 'Status',\n 'Opened',\n 'Opened by',\n 'Closed',\n 'Closed by',\n 'Starting cash',\n 'Net sales',\n 'Expected cash',\n 'Counted cash',\n 'Variance',\n ],\n ...report.shifts.map((row) => [\n row.shiftId,\n row.registerName,\n row.status === 'open' ? 'Open' : 'Closed',\n iso(row.openedAtMs),\n row.openedByName,\n iso(row.closedAtMs),\n row.closedByName ?? '',\n money(row.openingFloatCents),\n money(row.netSalesCents),\n money(row.expectedCashCents),\n money(row.countedCashCents),\n money(row.varianceCents),\n ]),\n ],\n [\n ['Cash drawers', 'Amount'],\n ['Starting cash', money(report.cash.openingFloatCents)],\n ['Cash sales', money(report.cash.cashSalesCents)],\n ['Paid in', money(report.cash.paidInCents)],\n ['Paid out', money(report.cash.paidOutCents)],\n ['Safe drops', money(report.cash.dropsCents)],\n ['Cash refunds', money(report.cash.cashRefundsCents)],\n ['Expected in closed drawers', money(report.cash.expectedCashCents)],\n ['Counted in closed drawers', money(report.cash.countedCashCents)],\n ['Variance', money(report.cash.varianceCents)],\n ['Expected in open drawers', money(report.cash.openExpectedCashCents)],\n ],\n ]\n return blocks\n .map((rows) => rows.map((row) => row.map(posCsvCell).join(',')).join('\\r\\n'))\n .join('\\r\\n\\r\\n')\n}\n\n/** The report's rows as the screen, the browser print and an 80 mm printer all read them. */\nexport function posDayReportSections(\n report: PosDayReport,\n money: (cents: number) => string,\n): Array<{ section: string; rows: Array<{ label: string; value: string; strong?: boolean }> }> {\n const t = report.totals\n const c = report.cash\n return [\n {\n section: 'Sales',\n rows: [\n { label: 'Orders', value: String(t.orderCount) },\n { label: 'Gross sales', value: money(t.grossSalesCents) },\n { label: 'Discounts', value: money(t.discountsCents) },\n { label: 'Tax', value: money(t.taxCents) },\n { label: 'Tips', value: money(t.tipsCents) },\n { label: `Refunds (${t.refundCount})`, value: money(-t.refundsCents) },\n { label: 'Net sales', value: money(t.netSalesCents), strong: true },\n ],\n },\n {\n section: 'By tender',\n rows: report.byTender.length\n ? report.byTender.map((row) => ({ label: row.label, value: money(row.netCents) }))\n : [{ label: 'No sales', value: '' }],\n },\n ...(report.byRegister.length\n ? [\n {\n section: 'By register',\n rows: report.byRegister.map((row) => ({ label: row.name, value: money(row.netSalesCents) })),\n },\n ]\n : []),\n ...(report.byCashier.length\n ? [\n {\n section: 'By cashier',\n rows: report.byCashier.map((row) => ({ label: row.name, value: money(row.netSalesCents) })),\n },\n ]\n : []),\n {\n section: 'Cash drawers',\n rows: [\n { label: 'Starting cash', value: money(c.openingFloatCents) },\n { label: 'Cash sales', value: money(c.cashSalesCents) },\n { label: 'Paid in', value: money(c.paidInCents) },\n { label: 'Paid out', value: money(-c.paidOutCents) },\n { label: 'Safe drops', value: money(-c.dropsCents) },\n { label: 'Cash refunds', value: money(-c.cashRefundsCents) },\n { label: `Expected (${c.closedShiftCount} closed)`, value: money(c.expectedCashCents) },\n { label: 'Counted', value: money(c.countedCashCents) },\n {\n label: c.varianceCents < 0 ? 'Short' : c.varianceCents > 0 ? 'Over' : 'Balanced',\n value: money(c.varianceCents),\n strong: true,\n },\n ...(c.openShiftCount\n ? [{ label: `Still open (${c.openShiftCount})`, value: money(c.openExpectedCashCents) }]\n : []),\n ],\n },\n ]\n}\n"],"names":["zonedDayKey","zonedWallTimeToInstant","computePosShiftReport","posCsvCell","POS_TENDER_LABELS","POS_TENDER_METHODS","currencyMinorDigits","POS_DAY_REPORT_ROUTE","POS_DAY_REPORT_MAX_DAYS","POS_DAY_REPORT_SALES_CEILING","POS_DAY_REPORT_RETURNS_CEILING","POS_DAY_REPORT_SHIFTS_CEILING","DAY_KEY","dayParts","key","match","exec","year","Number","month","day","check","Date","UTC","getUTCFullYear","getUTCMonth","getUTCDate","posDayReportRange","input","rawFrom","String","from","trim","rawTo","to","nowMs","timeZone","start","end","ok","error","days","Math","round","startMs","endMs","whole","value","number","isFinite","totalsOf","report","orderCount","grossSalesCents","discountsCents","taxCents","tipsCents","refundCount","refundsCents","netSalesCents","salesByTender","NO_DRAWER","openingFloatCents","cashEvents","salesFigures","sales","refunds","shift","groupBy","items","groups","Map","item","id","group","get","push","set","computePosDayReport","range","inRange","atMs","at","shifts","sort","a","b","openedAtMs","localeCompare","filter","reduce","sum","flatMap","event","overall","returns","byTender","method","refundsByTender","map","salesCents","label","netCents","registerName","registerNames","salesByRegister","sale","registerId","returnsByRegister","refund","registerIds","Set","keys","byRegister","name","row","memberName","memberNames","salesByCashier","cashierId","returnsByCashier","cashierIds","byCashier","memberId","shiftRows","closed","status","liveReport","shiftId","closedAtMs","openedByName","openedBy","closedByName","closedBy","expectedCashCents","countedCashCents","varianceCents","closedRows","openRows","rows","pick","total","hostId","storeName","currency","generatedAtMs","totals","cash","cashSalesCents","cashRefundsCents","paidInCents","paidOutCents","dropsCents","closedShiftCount","length","openShiftCount","openExpectedCashCents","truncated","posDayReportIsEmpty","posDayAmount","cents","digits","toFixed","posDayRangeLabel","posDayReportCsv","money","iso","ms","toISOString","salesRow","section","blocks","toUpperCase","join","posDayReportSections","t","c","strong"],"mappings":"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"}