@aglyn/plugins-commerce 1.0.0-beta.285 → 1.0.0-beta.288

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 (430) hide show
  1. package/README.md +1 -1
  2. package/package.json +13 -13
  3. package/src/lib/components/cart.js +25 -12
  4. package/src/lib/components/cart.js.map +1 -1
  5. package/src/lib/components/commerce-console-page.js +70 -13
  6. package/src/lib/components/commerce-console-page.js.map +1 -1
  7. package/src/lib/components/console/order-detail-dialog.component.js +2 -1
  8. package/src/lib/components/console/order-detail-dialog.component.js.map +1 -1
  9. package/src/lib/components/console/order-webhooks-card.component.js +1 -1
  10. package/src/lib/components/console/order-webhooks-card.component.js.map +1 -1
  11. package/src/lib/components/console/payment-methods-card.component.js +1 -1
  12. package/src/lib/components/console/payment-methods-card.component.js.map +1 -1
  13. package/src/lib/components/console/pos/pos-cart-panel.component.d.ts +5 -0
  14. package/src/lib/components/console/pos/pos-cart-panel.component.js +18 -12
  15. package/src/lib/components/console/pos/pos-cart-panel.component.js.map +1 -1
  16. package/src/lib/components/console/pos/pos-hardware-api.d.ts +76 -5
  17. package/src/lib/components/console/pos/pos-hardware-api.js +53 -11
  18. package/src/lib/components/console/pos/pos-hardware-api.js.map +1 -1
  19. package/src/lib/components/console/pos/pos-hardware-buyer.component.d.ts +18 -0
  20. package/src/lib/components/console/pos/pos-hardware-buyer.component.js +629 -0
  21. package/src/lib/components/console/pos/pos-hardware-buyer.component.js.map +1 -0
  22. package/src/lib/components/console/pos/pos-hardware-sale-api.d.ts +162 -0
  23. package/src/lib/components/console/pos/pos-hardware-sale-api.js +122 -0
  24. package/src/lib/components/console/pos/pos-hardware-sale-api.js.map +1 -0
  25. package/src/lib/components/console/pos/pos-hardware-sale-terms.component.d.ts +39 -0
  26. package/src/lib/components/console/pos/pos-hardware-sale-terms.component.js +251 -0
  27. package/src/lib/components/console/pos/pos-hardware-sale-terms.component.js.map +1 -0
  28. package/src/lib/components/console/pos/use-pos-display.js +50 -0
  29. package/src/lib/components/console/pos/use-pos-display.js.map +1 -1
  30. package/src/lib/components/console/pos-cellular-control.component.d.ts +41 -0
  31. package/src/lib/components/console/pos-cellular-control.component.js +211 -0
  32. package/src/lib/components/console/pos-cellular-control.component.js.map +1 -0
  33. package/src/lib/components/console/pos-devices-card.component.d.ts +9 -1
  34. package/src/lib/components/console/pos-devices-card.component.js +47 -15
  35. package/src/lib/components/console/pos-devices-card.component.js.map +1 -1
  36. package/src/lib/components/console/pos-display/pos-display-api.d.ts +6 -0
  37. package/src/lib/components/console/pos-display/pos-display-api.js +8 -1
  38. package/src/lib/components/console/pos-display/pos-display-api.js.map +1 -1
  39. package/src/lib/components/console/pos-display-page.component.d.ts +13 -0
  40. package/src/lib/components/console/pos-display-page.component.js +44 -2
  41. package/src/lib/components/console/pos-display-page.component.js.map +1 -1
  42. package/src/lib/components/console/pos-hardware-card.component.d.ts +12 -4
  43. package/src/lib/components/console/pos-hardware-card.component.js +157 -36
  44. package/src/lib/components/console/pos-hardware-card.component.js.map +1 -1
  45. package/src/lib/components/console/pos-hardware-orders-staff-page.component.js +360 -80
  46. package/src/lib/components/console/pos-hardware-orders-staff-page.component.js.map +1 -1
  47. package/src/lib/components/console/pos-hardware-terms-page.component.d.ts +34 -0
  48. package/src/lib/components/console/pos-hardware-terms-page.component.js +257 -0
  49. package/src/lib/components/console/pos-hardware-terms-page.component.js.map +1 -0
  50. package/src/lib/components/console/pos-hardware-terms-sign-page.component.d.ts +27 -0
  51. package/src/lib/components/console/pos-hardware-terms-sign-page.component.js +104 -0
  52. package/src/lib/components/console/pos-hardware-terms-sign-page.component.js.map +1 -0
  53. package/src/lib/components/console/pos-hardware-waiting-card.component.d.ts +24 -0
  54. package/src/lib/components/console/pos-hardware-waiting-card.component.js +205 -0
  55. package/src/lib/components/console/pos-hardware-waiting-card.component.js.map +1 -0
  56. package/src/lib/components/console/pos-ops/pos-check-in-dialog.component.js +297 -201
  57. package/src/lib/components/console/pos-ops/pos-check-in-dialog.component.js.map +1 -1
  58. package/src/lib/components/console/pos-ops/pos-check-ins-card.component.d.ts +3 -0
  59. package/src/lib/components/console/pos-ops/pos-check-ins-card.component.js +260 -92
  60. package/src/lib/components/console/pos-ops/pos-check-ins-card.component.js.map +1 -1
  61. package/src/lib/components/console/pos-ops/pos-operations-bar.component.d.ts +9 -0
  62. package/src/lib/components/console/pos-ops/pos-operations-bar.component.js +15 -6
  63. package/src/lib/components/console/pos-ops/pos-operations-bar.component.js.map +1 -1
  64. package/src/lib/components/console/pos-ops/pos-rain-check-dialog.component.d.ts +25 -0
  65. package/src/lib/components/console/pos-ops/pos-rain-check-dialog.component.js +136 -0
  66. package/src/lib/components/console/pos-ops/pos-rain-check-dialog.component.js.map +1 -0
  67. package/src/lib/components/console/pos-ops/pos-shift-panel.component.d.ts +6 -0
  68. package/src/lib/components/console/pos-ops/pos-shift-panel.component.js +48 -31
  69. package/src/lib/components/console/pos-ops/pos-shift-panel.component.js.map +1 -1
  70. package/src/lib/components/console/pos-page.component.d.ts +1 -1
  71. package/src/lib/components/console/pos-page.component.js +63 -12
  72. package/src/lib/components/console/pos-page.component.js.map +1 -1
  73. package/src/lib/components/console/pos-reader-splash-setting.component.js +1 -1
  74. package/src/lib/components/console/pos-reader-splash-setting.component.js.map +1 -1
  75. package/src/lib/components/console/product-check-in-fields.component.d.ts +4 -2
  76. package/src/lib/components/console/product-check-in-fields.component.js +60 -14
  77. package/src/lib/components/console/product-check-in-fields.component.js.map +1 -1
  78. package/src/lib/components/console/product-editor-dialog.component.js +44 -1
  79. package/src/lib/components/console/product-editor-dialog.component.js.map +1 -1
  80. package/src/lib/components/console/product-hardware-sku-field.component.d.ts +19 -0
  81. package/src/lib/components/console/product-hardware-sku-field.component.js +92 -0
  82. package/src/lib/components/console/product-hardware-sku-field.component.js.map +1 -0
  83. package/src/lib/components/console/product-kiosk-offer-field.component.d.ts +22 -0
  84. package/src/lib/components/console/product-kiosk-offer-field.component.js +94 -0
  85. package/src/lib/components/console/product-kiosk-offer-field.component.js.map +1 -0
  86. package/src/lib/components/console/product-register-fields.component.d.ts +4 -0
  87. package/src/lib/components/console/product-register-fields.component.js +31 -2
  88. package/src/lib/components/console/product-register-fields.component.js.map +1 -1
  89. package/src/lib/components/console/products-hub-card.component.js +11 -2
  90. package/src/lib/components/console/products-hub-card.component.js.map +1 -1
  91. package/src/lib/components/console/register-hardware-card.component.d.ts +11 -0
  92. package/src/lib/components/console/register-hardware-card.component.js +56 -27
  93. package/src/lib/components/console/register-hardware-card.component.js.map +1 -1
  94. package/src/lib/components/console/reservation-guests-dialog.component.d.ts +28 -0
  95. package/src/lib/components/console/reservation-guests-dialog.component.js +189 -0
  96. package/src/lib/components/console/reservation-guests-dialog.component.js.map +1 -0
  97. package/src/lib/components/console/reservations-card.component.d.ts +9 -0
  98. package/src/lib/components/console/reservations-card.component.js +158 -23
  99. package/src/lib/components/console/reservations-card.component.js.map +1 -1
  100. package/src/lib/components/console/shippingeasy-card.component.js +1 -1
  101. package/src/lib/components/console/shippingeasy-card.component.js.map +1 -1
  102. package/src/lib/components/console/shipstation-card.component.d.ts +1 -1
  103. package/src/lib/components/console/shipstation-card.component.js +3 -3
  104. package/src/lib/components/console/shipstation-card.component.js.map +1 -1
  105. package/src/lib/components/product-detail.d.ts +4 -0
  106. package/src/lib/components/product-detail.js +40 -5
  107. package/src/lib/components/product-detail.js.map +1 -1
  108. package/src/lib/components/product-reviews.js +28 -11
  109. package/src/lib/components/product-reviews.js.map +1 -1
  110. package/src/lib/components/reservation-widget.js +28 -5
  111. package/src/lib/components/reservation-widget.js.map +1 -1
  112. package/src/lib/components/use-day-picker.d.ts +25 -0
  113. package/src/lib/components/use-day-picker.js +57 -0
  114. package/src/lib/components/use-day-picker.js.map +1 -0
  115. package/src/lib/constants/operator-alerts.d.ts +55 -0
  116. package/src/lib/constants/operator-alerts.js +156 -1
  117. package/src/lib/constants/operator-alerts.js.map +1 -1
  118. package/src/lib/declarations.console-server.js +54 -1
  119. package/src/lib/declarations.console-server.js.map +1 -1
  120. package/src/lib/declarations.d.ts +4 -2
  121. package/src/lib/declarations.js +15 -4
  122. package/src/lib/declarations.js.map +1 -1
  123. package/src/lib/declarations.server.js +9 -0
  124. package/src/lib/declarations.server.js.map +1 -1
  125. package/src/lib/model/commerce-cart.d.ts +9 -1
  126. package/src/lib/model/commerce-cart.js +4 -3
  127. package/src/lib/model/commerce-cart.js.map +1 -1
  128. package/src/lib/model/commerce-check-in-product.d.ts +26 -0
  129. package/src/lib/model/commerce-check-in-product.js +64 -2
  130. package/src/lib/model/commerce-check-in-product.js.map +1 -1
  131. package/src/lib/model/commerce-check-in-record.d.ts +127 -0
  132. package/src/lib/model/commerce-check-in-record.js +36 -0
  133. package/src/lib/model/commerce-check-in-record.js.map +1 -0
  134. package/src/lib/model/commerce-check-in.d.ts +91 -87
  135. package/src/lib/model/commerce-check-in.js +186 -62
  136. package/src/lib/model/commerce-check-in.js.map +1 -1
  137. package/src/lib/model/commerce-kiosk-offer.d.ts +31 -0
  138. package/src/lib/model/commerce-kiosk-offer.js +35 -0
  139. package/src/lib/model/commerce-kiosk-offer.js.map +1 -0
  140. package/src/lib/model/commerce-orders.d.ts +76 -0
  141. package/src/lib/model/commerce-orders.js +6 -0
  142. package/src/lib/model/commerce-orders.js.map +1 -1
  143. package/src/lib/model/commerce-pos-day-report.d.ts +54 -1
  144. package/src/lib/model/commerce-pos-day-report.js +39 -2
  145. package/src/lib/model/commerce-pos-day-report.js.map +1 -1
  146. package/src/lib/model/commerce-pos-offline.d.ts +1 -1
  147. package/src/lib/model/commerce-pos-offline.js +6 -1
  148. package/src/lib/model/commerce-pos-offline.js.map +1 -1
  149. package/src/lib/model/commerce-pos-ops.js +3 -2
  150. package/src/lib/model/commerce-pos-ops.js.map +1 -1
  151. package/src/lib/model/commerce-pos.d.ts +36 -3
  152. package/src/lib/model/commerce-pos.js +14 -6
  153. package/src/lib/model/commerce-pos.js.map +1 -1
  154. package/src/lib/model/commerce-receipt.js +3 -2
  155. package/src/lib/model/commerce-receipt.js.map +1 -1
  156. package/src/lib/model/commerce-reservations.d.ts +69 -4
  157. package/src/lib/model/commerce-reservations.js +57 -5
  158. package/src/lib/model/commerce-reservations.js.map +1 -1
  159. package/src/lib/model/commerce.d.ts +66 -0
  160. package/src/lib/model/commerce.js +15 -0
  161. package/src/lib/model/commerce.js.map +1 -1
  162. package/src/lib/model/order-list-fields.d.ts +11 -0
  163. package/src/lib/model/order-list-fields.js +17 -1
  164. package/src/lib/model/order-list-fields.js.map +1 -1
  165. package/src/lib/model/own-store-tax-summary.d.ts +152 -0
  166. package/src/lib/model/own-store-tax-summary.js +288 -0
  167. package/src/lib/model/own-store-tax-summary.js.map +1 -0
  168. package/src/lib/model/plugin-permissions.d.ts +74 -0
  169. package/src/lib/model/plugin-permissions.js +224 -7
  170. package/src/lib/model/plugin-permissions.js.map +1 -1
  171. package/src/lib/model/pos-cellular.d.ts +223 -0
  172. package/src/lib/model/pos-cellular.js +293 -0
  173. package/src/lib/model/pos-cellular.js.map +1 -0
  174. package/src/lib/model/pos-hardware-in-person.d.ts +98 -0
  175. package/src/lib/model/pos-hardware-in-person.js +224 -0
  176. package/src/lib/model/pos-hardware-in-person.js.map +1 -0
  177. package/src/lib/model/pos-hardware-tax-summary.d.ts +166 -0
  178. package/src/lib/model/pos-hardware-tax-summary.js +225 -0
  179. package/src/lib/model/pos-hardware-tax-summary.js.map +1 -0
  180. package/src/lib/model/pos-hardware-terms.d.ts +74 -0
  181. package/src/lib/model/pos-hardware-terms.js +115 -0
  182. package/src/lib/model/pos-hardware-terms.js.map +1 -0
  183. package/src/lib/model/pos-hardware.d.ts +349 -7
  184. package/src/lib/model/pos-hardware.js +291 -20
  185. package/src/lib/model/pos-hardware.js.map +1 -1
  186. package/src/lib/model/product-write.d.ts +10 -0
  187. package/src/lib/model/product-write.js +4 -2
  188. package/src/lib/model/product-write.js.map +1 -1
  189. package/src/lib/model/workspace-site-permissions.d.ts +26 -0
  190. package/src/lib/model/workspace-site-permissions.js +81 -0
  191. package/src/lib/model/workspace-site-permissions.js.map +1 -0
  192. package/src/lib/plugin.js +16 -0
  193. package/src/lib/plugin.js.map +1 -1
  194. package/src/lib/server/billing-webhook.js +25 -1
  195. package/src/lib/server/billing-webhook.js.map +1 -1
  196. package/src/lib/server/cancel-order.js +7 -5
  197. package/src/lib/server/cancel-order.js.map +1 -1
  198. package/src/lib/server/cart-checkout.js +20 -8
  199. package/src/lib/server/cart-checkout.js.map +1 -1
  200. package/src/lib/server/cart.js +29 -0
  201. package/src/lib/server/cart.js.map +1 -1
  202. package/src/lib/server/checkout.js +42 -13
  203. package/src/lib/server/checkout.js.map +1 -1
  204. package/src/lib/server/collection-membership.d.ts +4 -0
  205. package/src/lib/server/collection-membership.js +5 -10
  206. package/src/lib/server/collection-membership.js.map +1 -1
  207. package/src/lib/server/commerce-site-keys.d.ts +48 -0
  208. package/src/lib/server/commerce-site-keys.js +61 -0
  209. package/src/lib/server/commerce-site-keys.js.map +1 -0
  210. package/src/lib/server/draft-order.js +8 -5
  211. package/src/lib/server/draft-order.js.map +1 -1
  212. package/src/lib/server/fulfill-order.js +7 -5
  213. package/src/lib/server/fulfill-order.js.map +1 -1
  214. package/src/lib/server/gift-cards.js +8 -5
  215. package/src/lib/server/gift-cards.js.map +1 -1
  216. package/src/lib/server/local-fulfillment-status.js +9 -6
  217. package/src/lib/server/local-fulfillment-status.js.map +1 -1
  218. package/src/lib/server/member-post.js +6 -5
  219. package/src/lib/server/member-post.js.map +1 -1
  220. package/src/lib/server/membership-admin-password.d.ts +4 -2
  221. package/src/lib/server/membership-admin-password.js +10 -8
  222. package/src/lib/server/membership-admin-password.js.map +1 -1
  223. package/src/lib/server/membership-admin-remove.d.ts +7 -5
  224. package/src/lib/server/membership-admin-remove.js +9 -9
  225. package/src/lib/server/membership-admin-remove.js.map +1 -1
  226. package/src/lib/server/order-notifications.js +5 -2
  227. package/src/lib/server/order-notifications.js.map +1 -1
  228. package/src/lib/server/order-print.js +7 -5
  229. package/src/lib/server/order-print.js.map +1 -1
  230. package/src/lib/server/order-receipt-send.js +8 -5
  231. package/src/lib/server/order-receipt-send.js.map +1 -1
  232. package/src/lib/server/order-webhooks.js +8 -8
  233. package/src/lib/server/order-webhooks.js.map +1 -1
  234. package/src/lib/server/orders-shipping-prepare.js +9 -6
  235. package/src/lib/server/orders-shipping-prepare.js.map +1 -1
  236. package/src/lib/server/own-store-tax-return-source.d.ts +47 -0
  237. package/src/lib/server/own-store-tax-return-source.js +312 -0
  238. package/src/lib/server/own-store-tax-return-source.js.map +1 -0
  239. package/src/lib/server/payment-methods.js +13 -9
  240. package/src/lib/server/payment-methods.js.map +1 -1
  241. package/src/lib/server/pos-auth.d.ts +37 -6
  242. package/src/lib/server/pos-auth.js +40 -15
  243. package/src/lib/server/pos-auth.js.map +1 -1
  244. package/src/lib/server/pos-cellular-billing.d.ts +81 -0
  245. package/src/lib/server/pos-cellular-billing.js +711 -0
  246. package/src/lib/server/pos-cellular-billing.js.map +1 -0
  247. package/src/lib/server/pos-cellular-notify.d.ts +47 -0
  248. package/src/lib/server/pos-cellular-notify.js +150 -0
  249. package/src/lib/server/pos-cellular-notify.js.map +1 -0
  250. package/src/lib/server/pos-cellular.d.ts +88 -0
  251. package/src/lib/server/pos-cellular.js +339 -0
  252. package/src/lib/server/pos-cellular.js.map +1 -0
  253. package/src/lib/server/pos-check-in.d.ts +5 -1
  254. package/src/lib/server/pos-check-in.js +296 -28
  255. package/src/lib/server/pos-check-in.js.map +1 -1
  256. package/src/lib/server/pos-day-report.js +29 -3
  257. package/src/lib/server/pos-day-report.js.map +1 -1
  258. package/src/lib/server/pos-display.d.ts +16 -0
  259. package/src/lib/server/pos-display.js +129 -2
  260. package/src/lib/server/pos-display.js.map +1 -1
  261. package/src/lib/server/pos-hardware-admin.d.ts +10 -1
  262. package/src/lib/server/pos-hardware-admin.js +250 -84
  263. package/src/lib/server/pos-hardware-admin.js.map +1 -1
  264. package/src/lib/server/pos-hardware-attach.d.ts +105 -0
  265. package/src/lib/server/pos-hardware-attach.js +443 -0
  266. package/src/lib/server/pos-hardware-attach.js.map +1 -0
  267. package/src/lib/server/pos-hardware-buyer-lookup.d.ts +58 -0
  268. package/src/lib/server/pos-hardware-buyer-lookup.js +180 -0
  269. package/src/lib/server/pos-hardware-buyer-lookup.js.map +1 -0
  270. package/src/lib/server/pos-hardware-buyer-record.d.ts +29 -0
  271. package/src/lib/server/pos-hardware-buyer-record.js +86 -0
  272. package/src/lib/server/pos-hardware-buyer-record.js.map +1 -0
  273. package/src/lib/server/pos-hardware-cancel.d.ts +58 -0
  274. package/src/lib/server/pos-hardware-cancel.js +147 -0
  275. package/src/lib/server/pos-hardware-cancel.js.map +1 -0
  276. package/src/lib/server/pos-hardware-closure-notices.d.ts +27 -0
  277. package/src/lib/server/pos-hardware-closure-notices.js +85 -0
  278. package/src/lib/server/pos-hardware-closure-notices.js.map +1 -0
  279. package/src/lib/server/pos-hardware-closure.d.ts +60 -0
  280. package/src/lib/server/pos-hardware-closure.js +153 -0
  281. package/src/lib/server/pos-hardware-closure.js.map +1 -0
  282. package/src/lib/server/pos-hardware-in-person.d.ts +61 -0
  283. package/src/lib/server/pos-hardware-in-person.js +236 -0
  284. package/src/lib/server/pos-hardware-in-person.js.map +1 -0
  285. package/src/lib/server/pos-hardware-notify.d.ts +40 -2
  286. package/src/lib/server/pos-hardware-notify.js +195 -14
  287. package/src/lib/server/pos-hardware-notify.js.map +1 -1
  288. package/src/lib/server/pos-hardware-readers.d.ts +115 -0
  289. package/src/lib/server/pos-hardware-readers.js +430 -0
  290. package/src/lib/server/pos-hardware-readers.js.map +1 -0
  291. package/src/lib/server/pos-hardware-returns.d.ts +116 -0
  292. package/src/lib/server/pos-hardware-returns.js +502 -0
  293. package/src/lib/server/pos-hardware-returns.js.map +1 -0
  294. package/src/lib/server/pos-hardware-sale-fields.d.ts +28 -0
  295. package/src/lib/server/pos-hardware-sale-fields.js +44 -0
  296. package/src/lib/server/pos-hardware-sale-fields.js.map +1 -0
  297. package/src/lib/server/pos-hardware-sale.d.ts +54 -0
  298. package/src/lib/server/pos-hardware-sale.js +369 -0
  299. package/src/lib/server/pos-hardware-sale.js.map +1 -0
  300. package/src/lib/server/pos-hardware-tax-return-source.d.ts +99 -0
  301. package/src/lib/server/pos-hardware-tax-return-source.js +462 -0
  302. package/src/lib/server/pos-hardware-tax-return-source.js.map +1 -0
  303. package/src/lib/server/pos-hardware-terms.d.ts +64 -0
  304. package/src/lib/server/pos-hardware-terms.js +154 -0
  305. package/src/lib/server/pos-hardware-terms.js.map +1 -0
  306. package/src/lib/server/pos-hardware-user-eraser.d.ts +3 -0
  307. package/src/lib/server/pos-hardware-user-eraser.js +31 -7
  308. package/src/lib/server/pos-hardware-user-eraser.js.map +1 -1
  309. package/src/lib/server/pos-hardware-webhook.d.ts +5 -0
  310. package/src/lib/server/pos-hardware-webhook.js +51 -6
  311. package/src/lib/server/pos-hardware-webhook.js.map +1 -1
  312. package/src/lib/server/pos-hardware.d.ts +74 -1
  313. package/src/lib/server/pos-hardware.js +175 -10
  314. package/src/lib/server/pos-hardware.js.map +1 -1
  315. package/src/lib/server/pos-kiosk.d.ts +2 -0
  316. package/src/lib/server/pos-kiosk.js +17 -12
  317. package/src/lib/server/pos-kiosk.js.map +1 -1
  318. package/src/lib/server/pos-offline-card-order.d.ts +64 -0
  319. package/src/lib/server/pos-offline-card-order.js +102 -0
  320. package/src/lib/server/pos-offline-card-order.js.map +1 -0
  321. package/src/lib/server/pos-offline-card-reconcile.d.ts +67 -1
  322. package/src/lib/server/pos-offline-card-reconcile.js +215 -18
  323. package/src/lib/server/pos-offline-card-reconcile.js.map +1 -1
  324. package/src/lib/server/pos-offline-sync.d.ts +39 -0
  325. package/src/lib/server/pos-offline-sync.js +190 -55
  326. package/src/lib/server/pos-offline-sync.js.map +1 -1
  327. package/src/lib/server/pos-ops-gate.d.ts +28 -9
  328. package/src/lib/server/pos-ops-gate.js +50 -19
  329. package/src/lib/server/pos-ops-gate.js.map +1 -1
  330. package/src/lib/server/pos-ops-routes.js +24 -0
  331. package/src/lib/server/pos-ops-routes.js.map +1 -1
  332. package/src/lib/server/pos-order-refund-ids.d.ts +28 -0
  333. package/src/lib/server/pos-order-refund-ids.js +31 -0
  334. package/src/lib/server/pos-order-refund-ids.js.map +1 -0
  335. package/src/lib/server/pos-order.js +52 -49
  336. package/src/lib/server/pos-order.js.map +1 -1
  337. package/src/lib/server/pos-payment.js +1 -1
  338. package/src/lib/server/pos-payment.js.map +1 -1
  339. package/src/lib/server/pos-reader-splash-admin.js +30 -32
  340. package/src/lib/server/pos-reader-splash-admin.js.map +1 -1
  341. package/src/lib/server/pos-reader-splash-images-route.d.ts +17 -0
  342. package/src/lib/server/pos-reader-splash-images-route.js +113 -0
  343. package/src/lib/server/pos-reader-splash-images-route.js.map +1 -0
  344. package/src/lib/server/pos-reader-splash-images.d.ts +51 -0
  345. package/src/lib/server/pos-reader-splash-images.js +123 -0
  346. package/src/lib/server/pos-reader-splash-images.js.map +1 -0
  347. package/src/lib/server/pos-reader-splash.d.ts +68 -1
  348. package/src/lib/server/pos-reader-splash.js +182 -11
  349. package/src/lib/server/pos-reader-splash.js.map +1 -1
  350. package/src/lib/server/pos-readers.d.ts +29 -0
  351. package/src/lib/server/pos-readers.js +134 -35
  352. package/src/lib/server/pos-readers.js.map +1 -1
  353. package/src/lib/server/pos-refund-authority.d.ts +3 -1
  354. package/src/lib/server/pos-refund-authority.js +3 -2
  355. package/src/lib/server/pos-refund-authority.js.map +1 -1
  356. package/src/lib/server/pos-return.d.ts +10 -0
  357. package/src/lib/server/pos-return.js +29 -7
  358. package/src/lib/server/pos-return.js.map +1 -1
  359. package/src/lib/server/pos-sale.js +6 -1
  360. package/src/lib/server/pos-sale.js.map +1 -1
  361. package/src/lib/server/pos-shift.js +11 -0
  362. package/src/lib/server/pos-shift.js.map +1 -1
  363. package/src/lib/server/pos-staff-pin.js +29 -24
  364. package/src/lib/server/pos-staff-pin.js.map +1 -1
  365. package/src/lib/server/pos-terminal.d.ts +14 -4
  366. package/src/lib/server/pos-terminal.js +34 -38
  367. package/src/lib/server/pos-terminal.js.map +1 -1
  368. package/src/lib/server/printers.d.ts +7 -3
  369. package/src/lib/server/printers.js +18 -12
  370. package/src/lib/server/printers.js.map +1 -1
  371. package/src/lib/server/product-drafts.d.ts +11 -1
  372. package/src/lib/server/product-drafts.js +12 -8
  373. package/src/lib/server/product-drafts.js.map +1 -1
  374. package/src/lib/server/product.d.ts +15 -1
  375. package/src/lib/server/product.js +16 -3
  376. package/src/lib/server/product.js.map +1 -1
  377. package/src/lib/server/products-write.d.ts +26 -2
  378. package/src/lib/server/products-write.js +19 -7
  379. package/src/lib/server/products-write.js.map +1 -1
  380. package/src/lib/server/refund.js +55 -42
  381. package/src/lib/server/refund.js.map +1 -1
  382. package/src/lib/server/reservation-availability-query.d.ts +8 -0
  383. package/src/lib/server/reservation-availability-query.js +11 -1
  384. package/src/lib/server/reservation-availability-query.js.map +1 -1
  385. package/src/lib/server/reservation-availability.d.ts +1 -1
  386. package/src/lib/server/reservation-availability.js +7 -2
  387. package/src/lib/server/reservation-availability.js.map +1 -1
  388. package/src/lib/server/reservation-guests.d.ts +32 -0
  389. package/src/lib/server/reservation-guests.js +285 -0
  390. package/src/lib/server/reservation-guests.js.map +1 -0
  391. package/src/lib/server/reservation-walk-in.d.ts +11 -1
  392. package/src/lib/server/reservation-walk-in.js +56 -12
  393. package/src/lib/server/reservation-walk-in.js.map +1 -1
  394. package/src/lib/server/reserve.js +18 -5
  395. package/src/lib/server/reserve.js.map +1 -1
  396. package/src/lib/server/returns.js +9 -6
  397. package/src/lib/server/returns.js.map +1 -1
  398. package/src/lib/server/reviews.d.ts +22 -2
  399. package/src/lib/server/reviews.js +88 -20
  400. package/src/lib/server/reviews.js.map +1 -1
  401. package/src/lib/server/shipping-connectors.js +10 -11
  402. package/src/lib/server/shipping-connectors.js.map +1 -1
  403. package/src/lib/server/site-page-resolver.js +20 -3
  404. package/src/lib/server/site-page-resolver.js.map +1 -1
  405. package/src/lib/server/store-day.d.ts +33 -0
  406. package/src/lib/server/store-day.js +46 -0
  407. package/src/lib/server/store-day.js.map +1 -0
  408. package/src/lib/server/store-setup-ai-writer.d.ts +2 -1
  409. package/src/lib/server/store-setup-ai-writer.js +5 -6
  410. package/src/lib/server/store-setup-ai-writer.js.map +1 -1
  411. package/src/lib/server/storefront-tax.d.ts +13 -0
  412. package/src/lib/server/storefront-tax.js +8 -1
  413. package/src/lib/server/storefront-tax.js.map +1 -1
  414. package/src/lib/server/tax-return-source.d.ts +32 -3
  415. package/src/lib/server/tax-return-source.js +99 -20
  416. package/src/lib/server/tax-return-source.js.map +1 -1
  417. package/src/lib/server.js +100 -2
  418. package/src/lib/server.js.map +1 -1
  419. package/src/lib/testing/fake-firestore.js +58 -16
  420. package/src/lib/testing/fake-firestore.js.map +1 -1
  421. package/src/lib/testing/pos-ops-memory-firestore.js +18 -4
  422. package/src/lib/testing/pos-ops-memory-firestore.js.map +1 -1
  423. package/src/lib/testing/site-register-permissions.d.ts +29 -0
  424. package/src/lib/testing/site-register-permissions.js +38 -0
  425. package/src/lib/testing/site-register-permissions.js.map +1 -0
  426. package/src/lib/transfer/tracking.server.js +8 -5
  427. package/src/lib/transfer/tracking.server.js.map +1 -1
  428. package/src/lib/utils/add-to-cart.d.ts +2 -0
  429. package/src/lib/utils/add-to-cart.js +6 -3
  430. package/src/lib/utils/add-to-cart.js.map +1 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/server/pos-ops-gate.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 * as Aglyn from '@aglyn/aglyn/server'\nimport type { PluginApiRequest } from '@aglyn/aglyn/server'\nimport { firebaseAdmin, getOrgForHost, getPluginConfig } from '@aglyn/tenant-data-admin'\nimport { findUserByUidAcrossPools } from '@aglyn/tenant-data-admin/server/auth-pools'\nimport { resolveOrgPermissions } from '@aglyn/tenant-runtime/org-permissions'\nimport { createHmac, timingSafeEqual } from 'crypto'\nimport {\n POS_CASHIER_ASSERTION_TTL_MS,\n POS_MANAGER_ASSERTION_TTL_MS,\n} from '../model/commerce-pos-ops'\nimport type { PrintReport } from '../model/commerce-printers'\nimport { posOpsSettings, type PosOpsSettings } from '../pos-ops-config'\nimport { isRefusedIdToken } from '@aglyn/tenant-data-admin/server/id-token-refusal'\n\n/*==========================================\n * WHO MAY RUN THE REGISTER (AGL-3609), for the shift, PIN, customer and\n * return routes.\n *\n * The same two facts the sale route checks: the member's role ON THIS SITE\n * is `admin` or `editor` (the allowlist the Firestore rules enforce), and the\n * organization has not revoked `managePos` from them. Plus the plan's `pos`\n * entitlement, re-read every time because a downgrade happens between taps.\n *\n * Every Firebase and Stripe touch goes through {@link PosOpsDeps}, so the\n * routes' specs run each money path against an in-memory store and a counted\n * fetch without mocking a module.\n *=========================================*/\n\nexport interface PosOpsMembership {\n orgWide: boolean\n hostRole: string | null\n permissions: { managePos?: boolean }\n}\n\nexport interface PosOpsDeps {\n firestore(): FirebaseFirestore.Firestore\n verifyIdToken(token: string): Promise<{ uid: string }>\n membership(uid: string, hostId: string): Promise<PosOpsMembership>\n orgForHost(hostId: string): Promise<{ orgId: string; org: Record<string, any> | null } | null>\n pluginConfig(orgId: string, hostId: string): Promise<Record<string, unknown>>\n /** The plan includes the register. */\n posEntitled(org: Record<string, any> | null): boolean\n /** The HMAC secret staff assertions are signed with; throws when unset. */\n signingSecret(): string\n /** A member's name as a receipt or a report prints it. */\n memberName(uid: string): Promise<string>\n now(): number\n /** Stripe's REST API; `global.fetch` in production. */\n fetch: typeof fetch\n /** The register's cloud printers (AGL-3619): drawer kicks and shift reports. */\n printer: PosOpsPrinter\n}\n\n/** What the register's routes ask of the printer queue. Never throws. */\nexport interface PosOpsPrinter {\n kickDrawer(input: {\n hostId: string\n registerId: string\n reason: 'paid_in' | 'paid_out' | 'drop' | 'cash_refund'\n causeId: string\n createdBy?: string\n }): Promise<{ jobIds: string[] }>\n printReport(input: {\n hostId: string\n registerId: string\n shiftId: string\n report: PrintReport\n createdBy?: string\n attemptKey: string\n }): Promise<{ jobIds: string[] }>\n}\n\nexport function defaultPosOpsDeps(): PosOpsDeps {\n return {\n firestore: () => firebaseAdmin.app().firestore(),\n verifyIdToken: async (token) => {\n const decoded = await firebaseAdmin.app().auth().verifyIdToken(token)\n return { uid: decoded.uid }\n },\n membership: async (uid, hostId) => {\n const resolved = await resolveOrgPermissions(uid, { hostId })\n return {\n orgWide: resolved.orgWide === true,\n hostRole: resolved.hostRole ?? null,\n permissions: { managePos: resolved.permissions?.managePos === true },\n }\n },\n orgForHost: async (hostId) => {\n const found = await getOrgForHost(hostId)\n if (!found) return null\n return {\n orgId: String(found.org?.id ?? found.orgId ?? ''),\n org: (found.org as Record<string, any>) ?? null,\n }\n },\n pluginConfig: async (orgId, hostId) =>\n ((await getPluginConfig(orgId, 'commerce', { hostId })) ?? {}) as Record<string, unknown>,\n posEntitled: (org) => Aglyn.checkEntitlement(org as any, 'pos'),\n signingSecret: () => {\n const secret = process.env.TOKEN_SIGNING_SECRET\n if (!secret) throw new Error('TOKEN_SIGNING_SECRET is not configured')\n return secret\n },\n memberName: async (uid) => {\n // Every pool: a member who signs in through SSO lives in a GCIP tenant\n // pool, not the project one (AGL-1122).\n try {\n const user = (await findUserByUidAcrossPools(uid))?.record\n return user?.displayName || user?.email || 'Staff member'\n } catch {\n return 'Staff member'\n }\n },\n now: () => Date.now(),\n fetch: (input, init) => fetch(input, init),\n // Loaded on use: the printing module reaches the whole sale pipeline,\n // which a shift count or a PIN check never needs.\n printer: {\n kickDrawer: async (input) => (await import('./pos-print')).kickPosDrawer(input),\n printReport: async (input) => (await import('./pos-print')).printPosShiftReport(input),\n },\n }\n}\n\n/** A JSON body, whether the dispatcher parsed it or not. */\nexport function posOpsBody(req: PluginApiRequest): Record<string, any> {\n if (typeof req.body === 'string') {\n try {\n const parsed = JSON.parse(req.body)\n return parsed && typeof parsed === 'object' ? parsed : {}\n } catch {\n return {}\n }\n }\n return req.body && typeof req.body === 'object' ? (req.body as Record<string, any>) : {}\n}\n\n/** The request's idempotency key, either header spelling, bounded. */\nexport function posOpsIdempotencyKey(req: PluginApiRequest): string {\n return String(req.headers['idempotency-key'] ?? req.headers['Idempotency-Key'] ?? '')\n .trim()\n .slice(0, 200)\n}\n\n/** Who is at the register, once the gate admitted them. */\nexport interface PosOpsStaff {\n uid: string\n hostId: string\n orgId: string\n org: Record<string, any> | null\n membership: PosOpsMembership\n /** An admin of the whole workspace: the one role with no refund limit. */\n isManager: boolean\n settings: PosOpsSettings\n hostRef: FirebaseFirestore.DocumentReference\n}\n\nexport type PosOpsGate =\n | { ok: true; staff: PosOpsStaff }\n | { ok: false; status: number; error: string }\n\n/** A site id the dispatcher's own sentinels and path tricks cannot pass for. */\nfunction cleanId(value: unknown): string {\n const id = String(value ?? '').trim()\n return /^[A-Za-z0-9_-]{1,128}$/.test(id) && !/^__.*__$/.test(id) ? id : ''\n}\n\nexport { cleanId as posOpsCleanId }\n\n/** Whether a member may work this site's register, by role and permission. */\nexport function mayWorkRegister(memberRole: unknown, membership: PosOpsMembership): boolean {\n return (memberRole === 'admin' || memberRole === 'editor') && membership.permissions.managePos === true\n}\n\n/**\n * The one role whose say-so a refund above the cashier limit needs: an admin\n * of the whole workspace who is also this site's admin — the role\n * `refund.ts` has always required to refund at all.\n */\nexport function isPosManager(memberRole: unknown, membership: PosOpsMembership): boolean {\n return memberRole === 'admin' && membership.orgWide && membership.hostRole === 'admin'\n}\n\nexport async function authorizePosOps(\n deps: PosOpsDeps,\n req: PluginApiRequest,\n rawHostId: unknown,\n): Promise<PosOpsGate> {\n const authorization = String(req.headers.authorization ?? '')\n const idToken = authorization.startsWith('Bearer ') ? authorization.slice('Bearer '.length) : ''\n if (!idToken) return { ok: false, status: 401, error: 'Unauthenticated' }\n const hostId = cleanId(rawHostId)\n if (!hostId) return { ok: false, status: 400, error: 'Missing hostId' }\n let uid: string\n try {\n uid = (await deps.verifyIdToken(idToken)).uid\n } catch (error) {\n if (!isRefusedIdToken(error)) throw error\n return { ok: false, status: 401, error: 'Unauthenticated' }\n }\n const hostRef = deps.firestore().collection('hosts').doc(hostId)\n const hostSnapshot = await hostRef.get()\n if (!hostSnapshot.exists) return { ok: false, status: 404, error: 'Unknown site' }\n const memberRole = (hostSnapshot.get('memberRoles') ?? {})[uid]\n const membership = await deps.membership(uid, hostId)\n if (!mayWorkRegister(memberRole, membership)) {\n return { ok: false, status: 403, error: 'Not permitted' }\n }\n const owner = await deps.orgForHost(hostId)\n if (!deps.posEntitled(owner?.org ?? null)) {\n return { ok: false, status: 403, error: 'POS requires the Pro plan or above' }\n }\n const settings = posOpsSettings(\n owner?.orgId ? await deps.pluginConfig(owner.orgId, hostId).catch(() => ({})) : {},\n )\n return {\n ok: true,\n staff: {\n uid,\n hostId,\n orgId: owner?.orgId ?? '',\n org: owner?.org ?? null,\n membership,\n isManager: isPosManager(memberRole, membership),\n settings,\n hostRef,\n },\n }\n}\n\n/** A register on this site, or a refusal: a register id from another site is unknown here. */\nexport async function readPosRegister(\n staff: PosOpsStaff,\n rawRegisterId: unknown,\n): Promise<\n | { ok: true; ref: FirebaseFirestore.DocumentReference; data: Record<string, any> }\n | { ok: false; status: number; error: string }\n> {\n const registerId = cleanId(rawRegisterId)\n if (!registerId) return { ok: false, status: 400, error: 'Missing registerId' }\n const ref = staff.hostRef.collection('registers').doc(registerId)\n const snapshot = await ref.get()\n if (!snapshot.exists) return { ok: false, status: 404, error: 'Unknown register' }\n return { ok: true, ref, data: (snapshot.data() ?? {}) as Record<string, any> }\n}\n\n/*==========================================\n * STAFF ASSERTIONS: what a PIN buys.\n *\n * A PIN never signs anybody in. It mints a short-lived, signed statement\n * that one member of THIS site is at THIS register — `{hostId, registerId,\n * memberUid, purpose, expiry}` under an HMAC — and the routes that take one\n * re-check that member's role and `managePos` on every use, so a PIN never\n * carries more than its member holds right now, and a revoked member's\n * assertion stops working on the next tap rather than at expiry.\n *=========================================*/\n\nexport type PosAssertionPurpose = 'cashier' | 'manager'\n\ninterface AssertionClaims {\n v: 1\n h: string\n r: string\n u: string\n p: PosAssertionPurpose\n e: number\n}\n\nfunction signClaims(deps: PosOpsDeps, payload: string): string {\n return createHmac('sha256', deps.signingSecret()).update(`pos-assertion:${payload}`).digest('hex')\n}\n\nexport function mintPosAssertion(\n deps: PosOpsDeps,\n input: { hostId: string; registerId: string; memberUid: string; purpose: PosAssertionPurpose },\n): { token: string; expiresAtMs: number } {\n const expiresAtMs =\n deps.now() +\n (input.purpose === 'manager' ? POS_MANAGER_ASSERTION_TTL_MS : POS_CASHIER_ASSERTION_TTL_MS)\n const claims: AssertionClaims = {\n v: 1,\n h: input.hostId,\n r: input.registerId,\n u: input.memberUid,\n p: input.purpose,\n e: expiresAtMs,\n }\n const payload = Buffer.from(JSON.stringify(claims)).toString('base64url')\n return { token: `${payload}.${signClaims(deps, payload)}`, expiresAtMs }\n}\n\n/**\n * The member an assertion names, or `null` — for a forged, expired, wrong-site,\n * wrong-register or wrong-purpose one alike. It does NOT re-check the member:\n * {@link resolvePosStaffAssertion} does that.\n */\nexport function readPosAssertion(\n deps: PosOpsDeps,\n token: unknown,\n expected: { hostId: string; registerId: string; purpose: PosAssertionPurpose },\n): { memberUid: string; expiresAtMs: number } | null {\n const value = typeof token === 'string' ? token.trim() : ''\n const dot = value.lastIndexOf('.')\n if (dot <= 0 || value.length > 2000) return null\n const payload = value.slice(0, dot)\n const signature = value.slice(dot + 1)\n let expected64: string\n try {\n expected64 = signClaims(deps, payload)\n } catch {\n return null\n }\n const a = Buffer.from(signature)\n const b = Buffer.from(expected64)\n if (a.length !== b.length || !timingSafeEqual(new Uint8Array(a), new Uint8Array(b))) return null\n let claims: AssertionClaims\n try {\n claims = JSON.parse(Buffer.from(payload, 'base64url').toString('utf8'))\n } catch {\n return null\n }\n if (\n claims?.v !== 1 ||\n claims.h !== expected.hostId ||\n claims.r !== expected.registerId ||\n claims.p !== expected.purpose ||\n typeof claims.u !== 'string' ||\n !claims.u ||\n !(Number(claims.e) > deps.now())\n ) {\n return null\n }\n return { memberUid: claims.u, expiresAtMs: Number(claims.e) }\n}\n\n/**\n * The member an assertion puts at the register, re-checked NOW against the\n * site's roles and the organization's permissions — or `null` when the\n * assertion is bad or the member may no longer do what it was minted for.\n */\nexport async function resolvePosStaffAssertion(\n deps: PosOpsDeps,\n hostRef: FirebaseFirestore.DocumentReference,\n token: unknown,\n expected: { hostId: string; registerId: string; purpose: PosAssertionPurpose },\n): Promise<string | null> {\n const read = readPosAssertion(deps, token, expected)\n if (!read) return null\n const host = await hostRef.get()\n const memberRole = (host.get('memberRoles') ?? {})[read.memberUid]\n const membership = await deps.membership(read.memberUid, expected.hostId)\n const allowed =\n expected.purpose === 'manager'\n ? isPosManager(memberRole, membership)\n : mayWorkRegister(memberRole, membership)\n return allowed ? read.memberUid : null\n}\n\n/**\n * Who rang this: the member a valid cashier assertion names, or the member\n * signed in on the device when there is none or it no longer holds. The\n * signed-in member already passed the gate, so the fallback never admits\n * anybody new; it only decides whose name the sale carries.\n */\nexport async function resolvePosCashier(\n deps: PosOpsDeps,\n staff: Pick<PosOpsStaff, 'uid' | 'hostId' | 'hostRef'>,\n registerId: string,\n token: unknown,\n): Promise<{ cashierId: string; via: 'pin' | 'session' }> {\n if (token) {\n const memberUid = await resolvePosStaffAssertion(deps, staff.hostRef, token, {\n hostId: staff.hostId,\n registerId,\n purpose: 'cashier',\n })\n if (memberUid) return { cashierId: memberUid, via: 'pin' }\n }\n return { cashierId: staff.uid, via: 'session' }\n}\n"],"names":["Aglyn","firebaseAdmin","getOrgForHost","getPluginConfig","findUserByUidAcrossPools","resolveOrgPermissions","createHmac","timingSafeEqual","POS_CASHIER_ASSERTION_TTL_MS","POS_MANAGER_ASSERTION_TTL_MS","posOpsSettings","isRefusedIdToken","defaultPosOpsDeps","firestore","app","verifyIdToken","token","decoded","auth","uid","membership","hostId","resolved","orgWide","hostRole","permissions","managePos","orgForHost","found","orgId","String","org","id","pluginConfig","posEntitled","checkEntitlement","signingSecret","secret","process","env","TOKEN_SIGNING_SECRET","Error","memberName","user","record","displayName","email","now","Date","fetch","input","init","printer","kickDrawer","kickPosDrawer","printReport","printPosShiftReport","posOpsBody","req","body","parsed","JSON","parse","posOpsIdempotencyKey","headers","trim","slice","cleanId","value","test","posOpsCleanId","mayWorkRegister","memberRole","isPosManager","authorizePosOps","deps","rawHostId","hostSnapshot","authorization","idToken","startsWith","length","ok","status","error","hostRef","collection","doc","get","exists","owner","settings","catch","staff","isManager","readPosRegister","rawRegisterId","snapshot","registerId","ref","data","signClaims","payload","update","digest","mintPosAssertion","expiresAtMs","purpose","claims","v","h","r","u","memberUid","p","e","Buffer","from","stringify","toString","readPosAssertion","expected","dot","lastIndexOf","signature","expected64","a","b","Uint8Array","Number","resolvePosStaffAssertion","host","read","allowed","resolvePosCashier","cashierId","via"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/server/pos-ops-gate.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 * as Aglyn from '@aglyn/aglyn/server'\nimport type { PluginApiRequest } from '@aglyn/aglyn/server'\nimport { firebaseAdmin, getOrgForHost, getPluginConfig } from '@aglyn/tenant-data-admin'\nimport { findUserByUidAcrossPools } from '@aglyn/tenant-data-admin/server/auth-pools'\nimport { resolveOrgPermissions } from '@aglyn/tenant-runtime/org-permissions'\nimport { createHmac, timingSafeEqual } from 'crypto'\nimport {\n POS_CASHIER_ASSERTION_TTL_MS,\n POS_MANAGER_ASSERTION_TTL_MS,\n} from '../model/commerce-pos-ops'\nimport type { PrintReport } from '../model/commerce-printers'\nimport {\n holdsPosPermission,\n posPermissionRefusal,\n type PosPermission,\n} from '../model/plugin-permissions'\nimport { posOpsSettings, type PosOpsSettings } from '../pos-ops-config'\nimport { holdsCommerceWorkspaceSiteKey } from './commerce-site-keys'\nimport { isRefusedIdToken } from '@aglyn/tenant-data-admin/server/id-token-refusal'\n\n/*==========================================\n * WHO MAY RUN THE REGISTER (AGL-3609), for the shift, PIN, customer and\n * return routes.\n *\n * The fact the sale route checks: `managePos`, as resolved for THIS site —\n * the member's org role, custom role and overrides for an org-wide member,\n * and the role they hold here and their overrides for a site collaborator.\n * Plus the plan's `pos` entitlement, re-read every time because a downgrade\n * happens between taps.\n *\n * THE KEYS DECIDE, NOT THE SITE ROLE (AGL-4047). This also asked for `admin`\n * or `editor` in the site's `memberRoles`, exactly the set `managePos` is on\n * for by default, so nobody who had the register loses it; a site viewer the\n * workspace grants the register now works it.\n *\n * Every Firebase and Stripe touch goes through {@link PosOpsDeps}, so the\n * routes' specs run each money path against an in-memory store and a counted\n * fetch without mocking a module.\n *=========================================*/\n\nexport interface PosOpsMembership {\n orgWide: boolean\n hostRole: string | null\n /** `managePos`, and the register's finer keys (AGL-4047) as resolved. */\n permissions: { managePos?: boolean } & Partial<Record<PosPermission, boolean>>\n}\n\n/** The register's finer keys a membership carries (AGL-4047). */\nconst POS_PERMISSION_KEYS: readonly PosPermission[] = [\n 'posSell',\n 'posRefund',\n 'posDiscount',\n 'posCashDrawer',\n 'posDevices',\n 'posBuyHardware',\n]\n\nexport interface PosOpsDeps {\n firestore(): FirebaseFirestore.Firestore\n verifyIdToken(token: string): Promise<{ uid: string }>\n membership(uid: string, hostId: string): Promise<PosOpsMembership>\n orgForHost(hostId: string): Promise<{ orgId: string; org: Record<string, any> | null } | null>\n pluginConfig(orgId: string, hostId: string): Promise<Record<string, unknown>>\n /** The plan includes the register. */\n posEntitled(org: Record<string, any> | null): boolean\n /** The HMAC secret staff assertions are signed with; throws when unset. */\n signingSecret(): string\n /** A member's name as a receipt or a report prints it. */\n memberName(uid: string): Promise<string>\n now(): number\n /** Stripe's REST API; `global.fetch` in production. */\n fetch: typeof fetch\n /** The register's cloud printers (AGL-3619): drawer kicks and shift reports. */\n printer: PosOpsPrinter\n}\n\n/** What the register's routes ask of the printer queue. Never throws. */\nexport interface PosOpsPrinter {\n kickDrawer(input: {\n hostId: string\n registerId: string\n reason: 'paid_in' | 'paid_out' | 'drop' | 'cash_refund'\n causeId: string\n createdBy?: string\n }): Promise<{ jobIds: string[] }>\n printReport(input: {\n hostId: string\n registerId: string\n shiftId: string\n report: PrintReport\n createdBy?: string\n attemptKey: string\n }): Promise<{ jobIds: string[] }>\n}\n\nexport function defaultPosOpsDeps(): PosOpsDeps {\n return {\n firestore: () => firebaseAdmin.app().firestore(),\n verifyIdToken: async (token) => {\n const decoded = await firebaseAdmin.app().auth().verifyIdToken(token)\n return { uid: decoded.uid }\n },\n membership: async (uid, hostId) => {\n const resolved = await resolveOrgPermissions(uid, { hostId })\n return {\n orgWide: resolved.orgWide === true,\n hostRole: resolved.hostRole ?? null,\n permissions: {\n managePos: resolved.permissions?.managePos === true,\n // Each finer key as resolved; absent when the map carries none,\n // which `holdsPosPermission` reads as following `managePos`.\n ...Object.fromEntries(\n POS_PERMISSION_KEYS.filter(\n (key) => typeof resolved.permissions?.[key] === 'boolean',\n ).map((key) => [key, resolved.permissions[key] === true]),\n ),\n },\n }\n },\n orgForHost: async (hostId) => {\n const found = await getOrgForHost(hostId)\n if (!found) return null\n return {\n orgId: String(found.org?.id ?? found.orgId ?? ''),\n org: (found.org as Record<string, any>) ?? null,\n }\n },\n pluginConfig: async (orgId, hostId) =>\n ((await getPluginConfig(orgId, 'commerce', { hostId })) ?? {}) as Record<string, unknown>,\n posEntitled: (org) => Aglyn.checkEntitlement(org as any, 'pos'),\n signingSecret: () => {\n const secret = process.env.TOKEN_SIGNING_SECRET\n if (!secret) throw new Error('TOKEN_SIGNING_SECRET is not configured')\n return secret\n },\n memberName: async (uid) => {\n // Every pool: a member who signs in through SSO lives in a GCIP tenant\n // pool, not the project one (AGL-1122).\n try {\n const user = (await findUserByUidAcrossPools(uid))?.record\n return user?.displayName || user?.email || 'Staff member'\n } catch {\n return 'Staff member'\n }\n },\n now: () => Date.now(),\n fetch: (input, init) => fetch(input, init),\n // Loaded on use: the printing module reaches the whole sale pipeline,\n // which a shift count or a PIN check never needs.\n printer: {\n kickDrawer: async (input) => (await import('./pos-print')).kickPosDrawer(input),\n printReport: async (input) => (await import('./pos-print')).printPosShiftReport(input),\n },\n }\n}\n\n/** A JSON body, whether the dispatcher parsed it or not. */\nexport function posOpsBody(req: PluginApiRequest): Record<string, any> {\n if (typeof req.body === 'string') {\n try {\n const parsed = JSON.parse(req.body)\n return parsed && typeof parsed === 'object' ? parsed : {}\n } catch {\n return {}\n }\n }\n return req.body && typeof req.body === 'object' ? (req.body as Record<string, any>) : {}\n}\n\n/** The request's idempotency key, either header spelling, bounded. */\nexport function posOpsIdempotencyKey(req: PluginApiRequest): string {\n return String(req.headers['idempotency-key'] ?? req.headers['Idempotency-Key'] ?? '')\n .trim()\n .slice(0, 200)\n}\n\n/** Who is at the register, once the gate admitted them. */\nexport interface PosOpsStaff {\n uid: string\n hostId: string\n orgId: string\n org: Record<string, any> | null\n membership: PosOpsMembership\n /**\n * \"Approve refunds over the register's limit\" (`commerce.posApprovals`,\n * AGL-4047): no refund limit, a manager's approval, other members' PINs.\n */\n isManager: boolean\n settings: PosOpsSettings\n hostRef: FirebaseFirestore.DocumentReference\n}\n\nexport type PosOpsGate =\n | { ok: true; staff: PosOpsStaff }\n | { ok: false; status: number; error: string }\n\n/** A site id the dispatcher's own sentinels and path tricks cannot pass for. */\nfunction cleanId(value: unknown): string {\n const id = String(value ?? '').trim()\n return /^[A-Za-z0-9_-]{1,128}$/.test(id) && !/^__.*__$/.test(id) ? id : ''\n}\n\nexport { cleanId as posOpsCleanId }\n\n/**\n * Whether a member may work this site's register: `managePos`, as resolved\n * for the site (AGL-4047). A finer action also needs its own key:\n * `holdsPosPermission`.\n */\nexport function mayWorkRegister(membership: PosOpsMembership): boolean {\n return membership.permissions.managePos === true\n}\n\n/**\n * Whose say-so a refund above the cashier limit needs, and who sets other\n * members' PINs: \"Approve refunds over the register's limit\"\n * (`commerce.posApprovals`, AGL-4047). Its preset is the gate this asked\n * before: an admin of the whole workspace who is also this site's admin,\n * which someone added to just this site as its admin never was. `hostDoc` is\n * the site's document (its stamp and `memberRoles`).\n */\nexport function isPosManager(\n hostDoc: { get(field: string): unknown } | null | undefined,\n uid: string,\n membership: PosOpsMembership,\n): boolean {\n return holdsCommerceWorkspaceSiteKey(hostDoc, uid, 'commerce.posApprovals', membership)\n}\n\n/**\n * The register's gate for one route. `permission` names the finer register\n * key the action needs (AGL-4047) on top of working the register at all; a\n * member without it is answered 403 naming it.\n */\nexport async function authorizePosOps(\n deps: PosOpsDeps,\n req: PluginApiRequest,\n rawHostId: unknown,\n permission?: PosPermission,\n): Promise<PosOpsGate> {\n const authorization = String(req.headers.authorization ?? '')\n const idToken = authorization.startsWith('Bearer ') ? authorization.slice('Bearer '.length) : ''\n if (!idToken) return { ok: false, status: 401, error: 'Unauthenticated' }\n const hostId = cleanId(rawHostId)\n if (!hostId) return { ok: false, status: 400, error: 'Missing hostId' }\n let uid: string\n try {\n uid = (await deps.verifyIdToken(idToken)).uid\n } catch (error) {\n if (!isRefusedIdToken(error)) throw error\n return { ok: false, status: 401, error: 'Unauthenticated' }\n }\n const hostRef = deps.firestore().collection('hosts').doc(hostId)\n const hostSnapshot = await hostRef.get()\n if (!hostSnapshot.exists) return { ok: false, status: 404, error: 'Unknown site' }\n const membership = await deps.membership(uid, hostId)\n if (!mayWorkRegister(membership)) {\n return { ok: false, status: 403, error: 'Not permitted' }\n }\n if (permission && !holdsPosPermission(membership.permissions, permission)) {\n return { ok: false, status: 403, error: posPermissionRefusal(permission) }\n }\n const owner = await deps.orgForHost(hostId)\n if (!deps.posEntitled(owner?.org ?? null)) {\n return { ok: false, status: 403, error: 'POS requires the Pro plan or above' }\n }\n const settings = posOpsSettings(\n owner?.orgId ? await deps.pluginConfig(owner.orgId, hostId).catch(() => ({})) : {},\n )\n return {\n ok: true,\n staff: {\n uid,\n hostId,\n orgId: owner?.orgId ?? '',\n org: owner?.org ?? null,\n membership,\n isManager: isPosManager(hostSnapshot, uid, membership),\n settings,\n hostRef,\n },\n }\n}\n\n/** A register on this site, or a refusal: a register id from another site is unknown here. */\nexport async function readPosRegister(\n staff: PosOpsStaff,\n rawRegisterId: unknown,\n): Promise<\n | { ok: true; ref: FirebaseFirestore.DocumentReference; data: Record<string, any> }\n | { ok: false; status: number; error: string }\n> {\n const registerId = cleanId(rawRegisterId)\n if (!registerId) return { ok: false, status: 400, error: 'Missing registerId' }\n const ref = staff.hostRef.collection('registers').doc(registerId)\n const snapshot = await ref.get()\n if (!snapshot.exists) return { ok: false, status: 404, error: 'Unknown register' }\n return { ok: true, ref, data: (snapshot.data() ?? {}) as Record<string, any> }\n}\n\n/*==========================================\n * STAFF ASSERTIONS: what a PIN buys.\n *\n * A PIN never signs anybody in. It mints a short-lived, signed statement\n * that one member of THIS site is at THIS register — `{hostId, registerId,\n * memberUid, purpose, expiry}` under an HMAC — and the routes that take one\n * re-check that member's permissions on every use, so a PIN never\n * carries more than its member holds right now, and a revoked member's\n * assertion stops working on the next tap rather than at expiry.\n *=========================================*/\n\nexport type PosAssertionPurpose = 'cashier' | 'manager'\n\ninterface AssertionClaims {\n v: 1\n h: string\n r: string\n u: string\n p: PosAssertionPurpose\n e: number\n}\n\nfunction signClaims(deps: PosOpsDeps, payload: string): string {\n return createHmac('sha256', deps.signingSecret()).update(`pos-assertion:${payload}`).digest('hex')\n}\n\nexport function mintPosAssertion(\n deps: PosOpsDeps,\n input: { hostId: string; registerId: string; memberUid: string; purpose: PosAssertionPurpose },\n): { token: string; expiresAtMs: number } {\n const expiresAtMs =\n deps.now() +\n (input.purpose === 'manager' ? POS_MANAGER_ASSERTION_TTL_MS : POS_CASHIER_ASSERTION_TTL_MS)\n const claims: AssertionClaims = {\n v: 1,\n h: input.hostId,\n r: input.registerId,\n u: input.memberUid,\n p: input.purpose,\n e: expiresAtMs,\n }\n const payload = Buffer.from(JSON.stringify(claims)).toString('base64url')\n return { token: `${payload}.${signClaims(deps, payload)}`, expiresAtMs }\n}\n\n/**\n * The member an assertion names, or `null` — for a forged, expired, wrong-site,\n * wrong-register or wrong-purpose one alike. It does NOT re-check the member:\n * {@link resolvePosStaffAssertion} does that.\n */\nexport function readPosAssertion(\n deps: PosOpsDeps,\n token: unknown,\n expected: { hostId: string; registerId: string; purpose: PosAssertionPurpose },\n): { memberUid: string; expiresAtMs: number } | null {\n const value = typeof token === 'string' ? token.trim() : ''\n const dot = value.lastIndexOf('.')\n if (dot <= 0 || value.length > 2000) return null\n const payload = value.slice(0, dot)\n const signature = value.slice(dot + 1)\n let expected64: string\n try {\n expected64 = signClaims(deps, payload)\n } catch {\n return null\n }\n const a = Buffer.from(signature)\n const b = Buffer.from(expected64)\n if (a.length !== b.length || !timingSafeEqual(new Uint8Array(a), new Uint8Array(b))) return null\n let claims: AssertionClaims\n try {\n claims = JSON.parse(Buffer.from(payload, 'base64url').toString('utf8'))\n } catch {\n return null\n }\n if (\n claims?.v !== 1 ||\n claims.h !== expected.hostId ||\n claims.r !== expected.registerId ||\n claims.p !== expected.purpose ||\n typeof claims.u !== 'string' ||\n !claims.u ||\n !(Number(claims.e) > deps.now())\n ) {\n return null\n }\n return { memberUid: claims.u, expiresAtMs: Number(claims.e) }\n}\n\n/**\n * The member an assertion puts at the register, re-checked NOW against the\n * site's roles and the organization's permissions — or `null` when the\n * assertion is bad or the member may no longer do what it was minted for.\n */\nexport async function resolvePosStaffAssertion(\n deps: PosOpsDeps,\n hostRef: FirebaseFirestore.DocumentReference,\n token: unknown,\n expected: { hostId: string; registerId: string; purpose: PosAssertionPurpose },\n): Promise<string | null> {\n const read = readPosAssertion(deps, token, expected)\n if (!read) return null\n const membership = await deps.membership(read.memberUid, expected.hostId)\n if (expected.purpose !== 'manager') return mayWorkRegister(membership) ? read.memberUid : null\n return isPosManager(await hostRef.get(), read.memberUid, membership) ? read.memberUid : null\n}\n\n/**\n * Who rang this: the member a valid cashier assertion names, or the member\n * signed in on the device when there is none or it no longer holds. The\n * signed-in member already passed the gate, so the fallback never admits\n * anybody new; it only decides whose name the sale carries.\n */\nexport async function resolvePosCashier(\n deps: PosOpsDeps,\n staff: Pick<PosOpsStaff, 'uid' | 'hostId' | 'hostRef'>,\n registerId: string,\n token: unknown,\n): Promise<{ cashierId: string; via: 'pin' | 'session' }> {\n if (token) {\n const memberUid = await resolvePosStaffAssertion(deps, staff.hostRef, token, {\n hostId: staff.hostId,\n registerId,\n purpose: 'cashier',\n })\n if (memberUid) return { cashierId: memberUid, via: 'pin' }\n }\n return { cashierId: staff.uid, via: 'session' }\n}\n"],"names":["Aglyn","firebaseAdmin","getOrgForHost","getPluginConfig","findUserByUidAcrossPools","resolveOrgPermissions","createHmac","timingSafeEqual","POS_CASHIER_ASSERTION_TTL_MS","POS_MANAGER_ASSERTION_TTL_MS","holdsPosPermission","posPermissionRefusal","posOpsSettings","holdsCommerceWorkspaceSiteKey","isRefusedIdToken","POS_PERMISSION_KEYS","defaultPosOpsDeps","firestore","app","verifyIdToken","token","decoded","auth","uid","membership","hostId","resolved","orgWide","hostRole","permissions","managePos","Object","fromEntries","filter","key","map","orgForHost","found","orgId","String","org","id","pluginConfig","posEntitled","checkEntitlement","signingSecret","secret","process","env","TOKEN_SIGNING_SECRET","Error","memberName","user","record","displayName","email","now","Date","fetch","input","init","printer","kickDrawer","kickPosDrawer","printReport","printPosShiftReport","posOpsBody","req","body","parsed","JSON","parse","posOpsIdempotencyKey","headers","trim","slice","cleanId","value","test","posOpsCleanId","mayWorkRegister","isPosManager","hostDoc","authorizePosOps","deps","rawHostId","permission","authorization","idToken","startsWith","length","ok","status","error","hostRef","collection","doc","hostSnapshot","get","exists","owner","settings","catch","staff","isManager","readPosRegister","rawRegisterId","snapshot","registerId","ref","data","signClaims","payload","update","digest","mintPosAssertion","expiresAtMs","purpose","claims","v","h","r","u","memberUid","p","e","Buffer","from","stringify","toString","readPosAssertion","expected","dot","lastIndexOf","signature","expected64","a","b","Uint8Array","Number","resolvePosStaffAssertion","read","resolvePosCashier","cashierId","via"],"mappings":"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"}
@@ -18,6 +18,8 @@ import { posCheckInHandler, registerPosSaleCheckIns } from "./pos-check-in.js";
18
18
  import { posCustomerHandler } from "./pos-customer.js";
19
19
  import { posDayReportHandler } from "./pos-day-report.js";
20
20
  import { registerPosSalePrinting } from "./pos-print.js";
21
+ import { onPosSaleCompleted } from "./pos-sale.js";
22
+ import { parsePosHardwareInPersonConfig } from "../model/pos-hardware.js";
21
23
  import { posReturnHandler } from "./pos-return.js";
22
24
  import { posShiftHandler } from "./pos-shift.js";
23
25
  import { posStaffPinHandler } from "./pos-staff-pin.js";
@@ -43,6 +45,28 @@ import { posStaffPinHandler } from "./pos-staff-pin.js";
43
45
  // A completed sale's receipt and drawer kick on the register's cloud
44
46
  // printers (AGL-3619), through the sale-completed seam every path fires.
45
47
  registerPosSalePrinting();
48
+ // A card reader sold at the platform's own store's register becomes a
49
+ // hardware order for the buyer's workspace (AGL-4038).
50
+ registerPosSaleHardware();
51
+ }
52
+ let hardwareSubscribed = false;
53
+ /**
54
+ * Records the hardware order of a completed sale on the platform's own
55
+ * store. Every other store's sale is answered from the environment alone,
56
+ * without loading the recorder. Never throws into a sale; a sale this misses
57
+ * stays marked and the To attach list's sweep records it.
58
+ */ function registerPosSaleHardware() {
59
+ if (hardwareSubscribed) return;
60
+ hardwareSubscribed = true;
61
+ onPosSaleCompleted(async (event)=>{
62
+ var _parsePosHardwareInPersonConfig;
63
+ if (((_parsePosHardwareInPersonConfig = parsePosHardwareInPersonConfig(process.env)) == null ? void 0 : _parsePosHardwareInPersonConfig.sellerHostId) !== event.hostId) return;
64
+ try {
65
+ await (await import("./pos-hardware-in-person.js")).recordCompletedSaleHardware(event);
66
+ } catch (error) {
67
+ console.error('[pos-hardware-in-person] recorder not loaded', event.orderId, error);
68
+ }
69
+ });
46
70
  }
47
71
 
48
72
  //# sourceMappingURL=pos-ops-routes.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/server/pos-ops-routes.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 { registerPluginApiRoute } from '@aglyn/aglyn/server'\nimport { posCheckInHandler, registerPosSaleCheckIns } from './pos-check-in'\nimport { posCustomerHandler } from './pos-customer'\nimport { posDayReportHandler } from './pos-day-report'\nimport { registerPosSalePrinting } from './pos-print'\nimport { posReturnHandler } from './pos-return'\nimport { posShiftHandler } from './pos-shift'\nimport { posStaffPinHandler } from './pos-staff-pin'\n\n/**\n * The register's operations routes (AGL-3609): shifts and the drawer, staff\n * PINs, the customer lookup and returns, the day totals (AGL-4037), the\n * check-in (AGL-4043), plus the\n * sale printing. Console-auth, so they register on the console surface with\n * the sale route itself.\n */\nexport function registerPosOpsRoutes(): void {\n registerPluginApiRoute('commerce/pos-shift', posShiftHandler)\n registerPluginApiRoute('commerce/pos-staff-pin', posStaffPinHandler)\n registerPluginApiRoute('commerce/pos-customer', posCustomerHandler)\n registerPluginApiRoute('commerce/pos-return', posReturnHandler)\n // One day's or one event's totals across every register, shift and cashier\n // (AGL-4037): a GET, read with the same gate as the shift reports.\n registerPluginApiRoute('commerce/pos-day-report', posDayReportHandler)\n // Check-in at the window (AGL-4043): who is due today, checking them in\n // with a wristband, and the day's list; a register sale's check-ins are\n // written the moment it completes, through the same seam as its printing.\n registerPluginApiRoute('commerce/pos-check-in', posCheckInHandler)\n registerPosSaleCheckIns()\n // A completed sale's receipt and drawer kick on the register's cloud\n // printers (AGL-3619), through the sale-completed seam every path fires.\n registerPosSalePrinting()\n}\n"],"names":["registerPluginApiRoute","posCheckInHandler","registerPosSaleCheckIns","posCustomerHandler","posDayReportHandler","registerPosSalePrinting","posReturnHandler","posShiftHandler","posStaffPinHandler","registerPosOpsRoutes"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED,SAASA,sBAAsB,QAAQ,sBAAqB;AAC5D,SAASC,iBAAiB,EAAEC,uBAAuB,QAAQ,oBAAgB;AAC3E,SAASC,kBAAkB,QAAQ,oBAAgB;AACnD,SAASC,mBAAmB,QAAQ,sBAAkB;AACtD,SAASC,uBAAuB,QAAQ,iBAAa;AACrD,SAASC,gBAAgB,QAAQ,kBAAc;AAC/C,SAASC,eAAe,QAAQ,iBAAa;AAC7C,SAASC,kBAAkB,QAAQ,qBAAiB;AAEpD;;;;;;CAMC,GACD,OAAO,SAASC;IACdT,uBAAuB,sBAAsBO;IAC7CP,uBAAuB,0BAA0BQ;IACjDR,uBAAuB,yBAAyBG;IAChDH,uBAAuB,uBAAuBM;IAC9C,2EAA2E;IAC3E,mEAAmE;IACnEN,uBAAuB,2BAA2BI;IAClD,wEAAwE;IACxE,wEAAwE;IACxE,0EAA0E;IAC1EJ,uBAAuB,yBAAyBC;IAChDC;IACA,qEAAqE;IACrE,yEAAyE;IACzEG;AACF"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/server/pos-ops-routes.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 { registerPluginApiRoute } from '@aglyn/aglyn/server'\nimport { posCheckInHandler, registerPosSaleCheckIns } from './pos-check-in'\nimport { posCustomerHandler } from './pos-customer'\nimport { posDayReportHandler } from './pos-day-report'\nimport { registerPosSalePrinting } from './pos-print'\nimport { onPosSaleCompleted } from './pos-sale'\nimport { parsePosHardwareInPersonConfig } from '../model/pos-hardware'\nimport { posReturnHandler } from './pos-return'\nimport { posShiftHandler } from './pos-shift'\nimport { posStaffPinHandler } from './pos-staff-pin'\n\n/**\n * The register's operations routes (AGL-3609): shifts and the drawer, staff\n * PINs, the customer lookup and returns, the day totals (AGL-4037), the\n * check-in (AGL-4043), plus the\n * sale printing. Console-auth, so they register on the console surface with\n * the sale route itself.\n */\nexport function registerPosOpsRoutes(): void {\n registerPluginApiRoute('commerce/pos-shift', posShiftHandler)\n registerPluginApiRoute('commerce/pos-staff-pin', posStaffPinHandler)\n registerPluginApiRoute('commerce/pos-customer', posCustomerHandler)\n registerPluginApiRoute('commerce/pos-return', posReturnHandler)\n // One day's or one event's totals across every register, shift and cashier\n // (AGL-4037): a GET, read with the same gate as the shift reports.\n registerPluginApiRoute('commerce/pos-day-report', posDayReportHandler)\n // Check-in at the window (AGL-4043): who is due today, checking them in\n // with a wristband, and the day's list; a register sale's check-ins are\n // written the moment it completes, through the same seam as its printing.\n registerPluginApiRoute('commerce/pos-check-in', posCheckInHandler)\n registerPosSaleCheckIns()\n // A completed sale's receipt and drawer kick on the register's cloud\n // printers (AGL-3619), through the sale-completed seam every path fires.\n registerPosSalePrinting()\n // A card reader sold at the platform's own store's register becomes a\n // hardware order for the buyer's workspace (AGL-4038).\n registerPosSaleHardware()\n}\n\nlet hardwareSubscribed = false\n\n/**\n * Records the hardware order of a completed sale on the platform's own\n * store. Every other store's sale is answered from the environment alone,\n * without loading the recorder. Never throws into a sale; a sale this misses\n * stays marked and the To attach list's sweep records it.\n */\nfunction registerPosSaleHardware(): void {\n if (hardwareSubscribed) return\n hardwareSubscribed = true\n onPosSaleCompleted(async (event) => {\n if (parsePosHardwareInPersonConfig(process.env)?.sellerHostId !== event.hostId) return\n try {\n await (await import('./pos-hardware-in-person')).recordCompletedSaleHardware(event)\n } catch (error) {\n console.error('[pos-hardware-in-person] recorder not loaded', event.orderId, error)\n }\n })\n}\n"],"names":["registerPluginApiRoute","posCheckInHandler","registerPosSaleCheckIns","posCustomerHandler","posDayReportHandler","registerPosSalePrinting","onPosSaleCompleted","parsePosHardwareInPersonConfig","posReturnHandler","posShiftHandler","posStaffPinHandler","registerPosOpsRoutes","registerPosSaleHardware","hardwareSubscribed","event","process","env","sellerHostId","hostId","recordCompletedSaleHardware","error","console","orderId"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED,SAASA,sBAAsB,QAAQ,sBAAqB;AAC5D,SAASC,iBAAiB,EAAEC,uBAAuB,QAAQ,oBAAgB;AAC3E,SAASC,kBAAkB,QAAQ,oBAAgB;AACnD,SAASC,mBAAmB,QAAQ,sBAAkB;AACtD,SAASC,uBAAuB,QAAQ,iBAAa;AACrD,SAASC,kBAAkB,QAAQ,gBAAY;AAC/C,SAASC,8BAA8B,QAAQ,2BAAuB;AACtE,SAASC,gBAAgB,QAAQ,kBAAc;AAC/C,SAASC,eAAe,QAAQ,iBAAa;AAC7C,SAASC,kBAAkB,QAAQ,qBAAiB;AAEpD;;;;;;CAMC,GACD,OAAO,SAASC;IACdX,uBAAuB,sBAAsBS;IAC7CT,uBAAuB,0BAA0BU;IACjDV,uBAAuB,yBAAyBG;IAChDH,uBAAuB,uBAAuBQ;IAC9C,2EAA2E;IAC3E,mEAAmE;IACnER,uBAAuB,2BAA2BI;IAClD,wEAAwE;IACxE,wEAAwE;IACxE,0EAA0E;IAC1EJ,uBAAuB,yBAAyBC;IAChDC;IACA,qEAAqE;IACrE,yEAAyE;IACzEG;IACA,sEAAsE;IACtE,uDAAuD;IACvDO;AACF;AAEA,IAAIC,qBAAqB;AAEzB;;;;;CAKC,GACD,SAASD;IACP,IAAIC,oBAAoB;IACxBA,qBAAqB;IACrBP,mBAAmB,OAAOQ;YACpBP;QAAJ,IAAIA,EAAAA,kCAAAA,+BAA+BQ,QAAQC,GAAG,sBAA1CT,gCAA6CU,YAAY,MAAKH,MAAMI,MAAM,EAAE;QAChF,IAAI;YACF,MAAM,AAAC,CAAA,MAAM,MAAM,CAAC,8BAA0B,EAAGC,2BAA2B,CAACL;QAC/E,EAAE,OAAOM,OAAO;YACdC,QAAQD,KAAK,CAAC,gDAAgDN,MAAMQ,OAAO,EAAEF;QAC/E;IACF;AACF"}
@@ -0,0 +1,28 @@
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
+ /**
18
+ * The ids of the register refund records the day report reads
19
+ * (`registers/{registerId}/orderRefunds/{id}`, AGL-4037). Each is derived on
20
+ * purpose, so a replayed write lands on the same record instead of counting
21
+ * a refund twice; the schemes are written here, once, for every writer.
22
+ */
23
+ /** A refund Stripe reported for a register sale: the charge (else the order) and the refunded total. */
24
+ export declare function posOrderRefundStripeId(chargeOrOrderId: string, refundedCents: number): string;
25
+ /** A refund given from the console's order dialog, keyed by its Stripe claim (else its time). */
26
+ export declare function posOrderRefundDialogId(orderId: string, claimKey: string | number): string;
27
+ /** The hardware part of a register sale refunded through the staff page (AGL-4038), per attempt. */
28
+ export declare function posOrderRefundHardwareId(registerOrderId: string, attempt: number | string): string;
@@ -0,0 +1,31 @@
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
+ * The ids of the register refund records the day report reads
18
+ * (`registers/{registerId}/orderRefunds/{id}`, AGL-4037). Each is derived on
19
+ * purpose, so a replayed write lands on the same record instead of counting
20
+ * a refund twice; the schemes are written here, once, for every writer.
21
+ */ /** A refund Stripe reported for a register sale: the charge (else the order) and the refunded total. */ export function posOrderRefundStripeId(chargeOrOrderId, refundedCents) {
22
+ return `stripe-${chargeOrOrderId}-${refundedCents}`;
23
+ }
24
+ /** A refund given from the console's order dialog, keyed by its Stripe claim (else its time). */ export function posOrderRefundDialogId(orderId, claimKey) {
25
+ return `${orderId}-${claimKey}`;
26
+ }
27
+ /** The hardware part of a register sale refunded through the staff page (AGL-4038), per attempt. */ export function posOrderRefundHardwareId(registerOrderId, attempt) {
28
+ return `${registerOrderId}-hw-${attempt}`;
29
+ }
30
+
31
+ //# sourceMappingURL=pos-order-refund-ids.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/server/pos-order-refund-ids.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\n/**\n * The ids of the register refund records the day report reads\n * (`registers/{registerId}/orderRefunds/{id}`, AGL-4037). Each is derived on\n * purpose, so a replayed write lands on the same record instead of counting\n * a refund twice; the schemes are written here, once, for every writer.\n */\n\n/** A refund Stripe reported for a register sale: the charge (else the order) and the refunded total. */\nexport function posOrderRefundStripeId(chargeOrOrderId: string, refundedCents: number): string {\n return `stripe-${chargeOrOrderId}-${refundedCents}`\n}\n\n/** A refund given from the console's order dialog, keyed by its Stripe claim (else its time). */\nexport function posOrderRefundDialogId(orderId: string, claimKey: string | number): string {\n return `${orderId}-${claimKey}`\n}\n\n/** The hardware part of a register sale refunded through the staff page (AGL-4038), per attempt. */\nexport function posOrderRefundHardwareId(registerOrderId: string, attempt: number | string): string {\n return `${registerOrderId}-hw-${attempt}`\n}\n"],"names":["posOrderRefundStripeId","chargeOrOrderId","refundedCents","posOrderRefundDialogId","orderId","claimKey","posOrderRefundHardwareId","registerOrderId","attempt"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED;;;;;CAKC,GAED,sGAAsG,GACtG,OAAO,SAASA,uBAAuBC,eAAuB,EAAEC,aAAqB;IACnF,OAAO,CAAC,OAAO,EAAED,gBAAgB,CAAC,EAAEC,eAAe;AACrD;AAEA,+FAA+F,GAC/F,OAAO,SAASC,uBAAuBC,OAAe,EAAEC,QAAyB;IAC/E,OAAO,GAAGD,QAAQ,CAAC,EAAEC,UAAU;AACjC;AAEA,kGAAkG,GAClG,OAAO,SAASC,yBAAyBC,eAAuB,EAAEC,OAAwB;IACxF,OAAO,GAAGD,gBAAgB,IAAI,EAAEC,SAAS;AAC3C"}
@@ -28,9 +28,11 @@ import { raiseOrderEvent } from "./order-events.js";
28
28
  import { decrementVariantStock } from "./reserve-stock.js";
29
29
  import { holdPromotionSlot, promotionHoldKey, settlePromotionSlot } from "./promotion-hold.js";
30
30
  import { posMaxDiscountPct } from "../plugin-config.js";
31
+ import { holdsPosPermission, posPermissionRefusal } from "../model/plugin-permissions.js";
31
32
  import { offlineFeeMonthKey } from "./pos-fee-month.js";
32
33
  import { notifyPosSaleCompleted } from "./pos-sale.js";
33
34
  import { posSaleStamp } from "./pos-sale-stamp.js";
35
+ import { posHardwareSaleFields } from "./pos-hardware-sale-fields.js";
34
36
  import { quoteSaleTaxWithEngine } from "./tax-engine-quote.js";
35
37
  import { resolveOrgPermissions } from "@aglyn/tenant-runtime/org-permissions";
36
38
  import { posKioskPrincipal } from "./pos-kiosk-principal.js";
@@ -138,53 +140,31 @@ import { posKioskPrincipal } from "./pos-kiosk-principal.js";
138
140
  if (slot) await slot.release();
139
141
  };
140
142
  try {
141
- var _hostSnapshot_get, _storeSettings_get, _taxSettings_origin, _ref2, _ref3;
142
- var _registerDocs_rank, _ownerOrg_org, _ownerOrg_org1;
143
+ var _ref2, _ref3, _storeSettings_get, _taxSettings_origin, _ref4, _ref5;
144
+ var _registerDocs_rank, _ownerOrg_org, _ownerOrg_org1, _ownerOrg_org2;
143
145
  const decoded = kiosk ? {
144
146
  uid: kiosk.uid
145
147
  } : await firebaseAdmin.app().auth().verifyIdToken(idToken);
146
148
  const firestore = firebaseAdmin.app().firestore();
147
149
  const hostRef = firestore.collection('hosts').doc(hostId);
148
150
  const hostSnapshot = await hostRef.get();
149
- // AN ALLOWLIST (AGL-2262), matching `cancel-order.ts`, `fulfill-order.ts`
150
- // and the Firestore rules' own `hostMemberRole(hostId) in
151
- // ['admin','editor']`. The old `!role || role === 'viewer'` denylist
152
- // admitted every string that was not literally `viewer` on the route that
153
- // takes cash, mints a card QR and decrements real inventory.
154
- // The union has since GROWN: `HostAccessRole` is `admin | editor | author
155
- // | viewer` (AGL-2334), and `author` is grantable on this host through
156
- // `/api/hosts/members`. So this allowlist now refuses a role a real
157
- // projection CAN produce, deliberately — an author edits content and does
158
- // not take money. That is the whole reason it is an allowlist: a role
159
- // added later is refused until someone decides otherwise, rather than
160
- // admitted by the silence of a denylist.
161
- //
162
- // What it does NOT decide is WHOSE admin. `memberRoles` is a per-host
163
- // projection, so a SITE COLLABORATOR granted `admin` on this one site
164
- // (`hostAccess: { [hostId]: 'admin' }`, `allHosts: false`) is
165
- // indistinguishable here from an org owner. That is correct for ringing a
166
- // sale — they were invited to run this till — but it means this value
167
- // alone cannot carry an elevated, manager-only control such as the
168
- // AGL-2372 oversell override. The discriminator is `orgWide` on the
169
- // membership resolved just below, not the role string.
170
- const memberRole = ((_hostSnapshot_get = hostSnapshot.get('memberRoles')) != null ? _hostSnapshot_get : {})[decoded.uid];
171
- if (memberRole !== 'admin' && memberRole !== 'editor') {
172
- return res.status(403).json({
173
- error: 'Not permitted'
174
- });
175
- }
176
- // THE `managePos` PERMISSION, actually read (AGL-2474). The allowlist
177
- // above is a HOST-level fact and stays — it is what the Firestore rules
178
- // enforce and dropping it would widen this route. This is the org-level
179
- // half: `managePos` was declared by COMMERCE_PERMISSIONS, resolved into
180
- // every permission map, displayed nowhere and read by nothing, so the
181
- // capability was inert. Both must hold, matching the marketplace
182
- // install/publish routes' resolveOrgPermissions + memberRoles pairing.
151
+ if (!hostSnapshot.exists) return res.status(404).json({
152
+ error: 'Unknown site'
153
+ });
154
+ // THE KEYS DECIDE, NOT THE SITE ROLE (AGL-4047). This route asked for
155
+ // `admin` or `editor` in the site's `memberRoles` (an allowlist, AGL-2262)
156
+ // AND `managePos` (AGL-2474). Those two agreed for everyone by default —
157
+ // `managePos` is on for exactly the admin and editor tiers — so dropping
158
+ // the role check takes the register from nobody. What changes is who the
159
+ // workspace can hand it to: a site viewer granted `managePos` rings sales,
160
+ // and an admin a role narrows is refused, as the waivers doors already do.
183
161
  //
184
- // Behaviour is unchanged for anyone who has not been given an explicit
185
- // override: `managePos` defaults to true for the admin and editor tiers,
186
- // which is exactly the set the allowlist already admits. What it adds is
187
- // the ability to REVOKE POS from one admin without demoting them.
162
+ // `resolveOrgPermissions` answers for THIS site: an org-wide member from
163
+ // their org role, custom role and overrides; a site collaborator from the
164
+ // role they hold here and their overrides; and anyone without access to
165
+ // the site with nothing. Whose admin a member is stays the `orgWide`
166
+ // discriminator below, for the manager-only controls such as the AGL-2372
167
+ // oversell override; a site role alone never carried those.
188
168
  const membership = await resolveOrgPermissions(decoded.uid, {
189
169
  hostId
190
170
  });
@@ -193,6 +173,20 @@ import { posKioskPrincipal } from "./pos-kiosk-principal.js";
193
173
  error: 'Not permitted'
194
174
  });
195
175
  }
176
+ // THE REGISTER'S FINER KEYS (AGL-4047), each following `managePos` until
177
+ // a role or the member sets it: ringing a sale at all, and the cashier's
178
+ // own percentage off. A shopper's code and the store's promotions are the
179
+ // merchant's, and apply without the discount key.
180
+ if (!holdsPosPermission(membership.permissions, 'posSell')) {
181
+ return res.status(403).json({
182
+ error: posPermissionRefusal('posSell')
183
+ });
184
+ }
185
+ if (discountPct > 0 && !holdsPosPermission(membership.permissions, 'posDiscount')) {
186
+ return res.status(403).json({
187
+ error: posPermissionRefusal('posDiscount')
188
+ });
189
+ }
196
190
  const ownerOrg = await getOrgForHost(hostId);
197
191
  if (!Aglyn.checkEntitlement(ownerOrg == null ? void 0 : ownerOrg.org, 'pos')) {
198
192
  return res.status(403).json({
@@ -336,6 +330,15 @@ import { posKioskPrincipal } from "./pos-kiosk-principal.js";
336
330
  error: 'No valid lines'
337
331
  });
338
332
  }
333
+ // A sale of card readers on the platform's own store (AGL-4038) is
334
+ // marked from the moment it is written, so its hardware order is
335
+ // recorded once it is paid even if the completion listener misses it.
336
+ const hardwareFields = posHardwareSaleFields({
337
+ hostId,
338
+ orgId: String((_ref2 = (_ref3 = ownerOrg == null ? void 0 : (_ownerOrg_org = ownerOrg.org) == null ? void 0 : _ownerOrg_org.id) != null ? _ref3 : ownerOrg == null ? void 0 : ownerOrg.orgId) != null ? _ref2 : ''),
339
+ lineItems,
340
+ products: productsById
341
+ });
339
342
  // WHAT THE COUNT SAYS, said out loud (AGL-2357). Every storefront door
340
343
  // gates on `canPurchase`; a register that skips it silently downgrades a
341
344
  // merchant's `oversellPolicy: 'deny'` to `backorder` at the till.
@@ -630,7 +633,7 @@ import { posKioskPrincipal } from "./pos-kiosk-principal.js";
630
633
  // `orgs//offlineFees/…`; the sale still rings, and the fee is forgone
631
634
  // rather than written somewhere no invoice will ever find it.
632
635
  const offlineFeeMonth = offlineFeeMonthKey();
633
- const offlineFeeOrgId = String((_ref2 = ownerOrg == null ? void 0 : (_ownerOrg_org = ownerOrg.org) == null ? void 0 : _ownerOrg_org.id) != null ? _ref2 : '');
636
+ const offlineFeeOrgId = String((_ref4 = ownerOrg == null ? void 0 : (_ownerOrg_org1 = ownerOrg.org) == null ? void 0 : _ownerOrg_org1.id) != null ? _ref4 : '');
634
637
  const offlineFeeRef = offlineFeeOrgId ? firestore.collection('orgs').doc(offlineFeeOrgId).collection('offlineFees').doc(offlineFeeMonth) : null;
635
638
  // Deterministic tender rejections run BEFORE the claim is taken, so they
636
639
  // never burn the key (AGL-1691): "cash received is short" is a 400 the
@@ -686,7 +689,7 @@ import { posKioskPrincipal } from "./pos-kiosk-principal.js";
686
689
  const claimed = await claimAttempt(firestore, {
687
690
  kind: 'pos',
688
691
  scopeId: hostId,
689
- orgId: String((_ref3 = ownerOrg == null ? void 0 : (_ownerOrg_org1 = ownerOrg.org) == null ? void 0 : _ownerOrg_org1.id) != null ? _ref3 : ''),
692
+ orgId: String((_ref5 = ownerOrg == null ? void 0 : (_ownerOrg_org2 = ownerOrg.org) == null ? void 0 : _ownerOrg_org2.id) != null ? _ref5 : ''),
690
693
  key: idempotencyKey,
691
694
  busyMessage: 'This sale is already being processed'
692
695
  });
@@ -759,7 +762,7 @@ import { posKioskPrincipal } from "./pos-kiosk-principal.js";
759
762
  channel: 'pos',
760
763
  registerId,
761
764
  cashierId: saleStamp.cashierId
762
- }, saleStamp.fields, discountPct > 0 ? {
765
+ }, saleStamp.fields, hardwareFields, discountPct > 0 ? {
763
766
  discountPct,
764
767
  discountBy: saleStamp.cashierId
765
768
  } : {}, appliedDiscountId ? {
@@ -807,10 +810,10 @@ import { posKioskPrincipal } from "./pos-kiosk-principal.js";
807
810
  return res.status(200).json(openPayload);
808
811
  }
809
812
  if (payment === 'link') {
810
- var _ref4, _session_id;
811
- var _ownerOrg_org2, _ownerOrg_org3;
813
+ var _ref6, _session_id;
814
+ var _ownerOrg_org3, _ownerOrg_org4;
812
815
  // QR payment link on the merchant account, completed by webhook.
813
- const ownerProfile = await firestore.collection('profiles').doc(String((_ref4 = ownerOrg == null ? void 0 : (_ownerOrg_org2 = ownerOrg.org) == null ? void 0 : _ownerOrg_org2.ownerUid) != null ? _ref4 : '')).get();
816
+ const ownerProfile = await firestore.collection('profiles').doc(String((_ref6 = ownerOrg == null ? void 0 : (_ownerOrg_org3 = ownerOrg.org) == null ? void 0 : _ownerOrg_org3.ownerUid) != null ? _ref6 : '')).get();
814
817
  const accountId = ownerProfile.get('stripeAccountId');
815
818
  if (!merchantAccountIsReady({
816
819
  accountId,
@@ -846,7 +849,7 @@ import { posKioskPrincipal } from "./pos-kiosk-principal.js";
846
849
  channel: 'pos',
847
850
  registerId,
848
851
  cashierId: saleStamp.cashierId
849
- }, saleStamp.fields, discountPct > 0 ? {
852
+ }, saleStamp.fields, hardwareFields, discountPct > 0 ? {
850
853
  discountPct,
851
854
  discountBy: saleStamp.cashierId
852
855
  } : {}, appliedDiscountId ? {
@@ -889,7 +892,7 @@ import { posKioskPrincipal } from "./pos-kiosk-principal.js";
889
892
  // origin root rather than a fabricated dead path.
890
893
  const posOrigin = `https://${req.headers.host}`;
891
894
  const posSubdomain = (await firestore.collection('hostIndex').doc(hostId).get()).get('subdomain');
892
- const posOrgSlug = ownerOrg == null ? void 0 : (_ownerOrg_org3 = ownerOrg.org) == null ? void 0 : _ownerOrg_org3.slug;
895
+ const posOrgSlug = ownerOrg == null ? void 0 : (_ownerOrg_org4 = ownerOrg.org) == null ? void 0 : _ownerOrg_org4.slug;
893
896
  const posUrl = posOrgSlug && posSubdomain ? `${posOrigin}${buildRoute(Route.HOST_PLUGIN, {
894
897
  orgSlug: posOrgSlug,
895
898
  host: posSubdomain,
@@ -1051,7 +1054,7 @@ import { posKioskPrincipal } from "./pos-kiosk-principal.js";
1051
1054
  // THE TENDER, RECORDED (AGL-3607): one settled payment, so this sale
1052
1055
  // reads the same as a split one rather than by inference.
1053
1056
  cashierId: saleStamp.cashierId
1054
- }, saleStamp.fields, {
1057
+ }, saleStamp.fields, hardwareFields, {
1055
1058
  payments: [
1056
1059
  _extends({
1057
1060
  id: `pay_${payment}`,