@aglyn/plugins-commerce 1.0.0-beta.284 → 1.0.0-beta.287

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 (420) 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/payment-methods-card.component.js +1 -1
  10. package/src/lib/components/console/payment-methods-card.component.js.map +1 -1
  11. package/src/lib/components/console/pos/pos-cart-panel.component.d.ts +5 -0
  12. package/src/lib/components/console/pos/pos-cart-panel.component.js +18 -12
  13. package/src/lib/components/console/pos/pos-cart-panel.component.js.map +1 -1
  14. package/src/lib/components/console/pos/pos-hardware-api.d.ts +76 -5
  15. package/src/lib/components/console/pos/pos-hardware-api.js +53 -11
  16. package/src/lib/components/console/pos/pos-hardware-api.js.map +1 -1
  17. package/src/lib/components/console/pos/pos-hardware-buyer.component.d.ts +18 -0
  18. package/src/lib/components/console/pos/pos-hardware-buyer.component.js +629 -0
  19. package/src/lib/components/console/pos/pos-hardware-buyer.component.js.map +1 -0
  20. package/src/lib/components/console/pos/pos-hardware-sale-api.d.ts +162 -0
  21. package/src/lib/components/console/pos/pos-hardware-sale-api.js +122 -0
  22. package/src/lib/components/console/pos/pos-hardware-sale-api.js.map +1 -0
  23. package/src/lib/components/console/pos/pos-hardware-sale-terms.component.d.ts +39 -0
  24. package/src/lib/components/console/pos/pos-hardware-sale-terms.component.js +251 -0
  25. package/src/lib/components/console/pos/pos-hardware-sale-terms.component.js.map +1 -0
  26. package/src/lib/components/console/pos/use-pos-display.js +50 -0
  27. package/src/lib/components/console/pos/use-pos-display.js.map +1 -1
  28. package/src/lib/components/console/pos-cellular-control.component.d.ts +41 -0
  29. package/src/lib/components/console/pos-cellular-control.component.js +211 -0
  30. package/src/lib/components/console/pos-cellular-control.component.js.map +1 -0
  31. package/src/lib/components/console/pos-devices-card.component.d.ts +9 -1
  32. package/src/lib/components/console/pos-devices-card.component.js +47 -15
  33. package/src/lib/components/console/pos-devices-card.component.js.map +1 -1
  34. package/src/lib/components/console/pos-display/pos-display-api.d.ts +6 -0
  35. package/src/lib/components/console/pos-display/pos-display-api.js +8 -1
  36. package/src/lib/components/console/pos-display/pos-display-api.js.map +1 -1
  37. package/src/lib/components/console/pos-display-page.component.d.ts +13 -0
  38. package/src/lib/components/console/pos-display-page.component.js +44 -2
  39. package/src/lib/components/console/pos-display-page.component.js.map +1 -1
  40. package/src/lib/components/console/pos-hardware-card.component.d.ts +12 -4
  41. package/src/lib/components/console/pos-hardware-card.component.js +157 -36
  42. package/src/lib/components/console/pos-hardware-card.component.js.map +1 -1
  43. package/src/lib/components/console/pos-hardware-orders-staff-page.component.js +360 -80
  44. package/src/lib/components/console/pos-hardware-orders-staff-page.component.js.map +1 -1
  45. package/src/lib/components/console/pos-hardware-terms-page.component.d.ts +34 -0
  46. package/src/lib/components/console/pos-hardware-terms-page.component.js +257 -0
  47. package/src/lib/components/console/pos-hardware-terms-page.component.js.map +1 -0
  48. package/src/lib/components/console/pos-hardware-terms-sign-page.component.d.ts +27 -0
  49. package/src/lib/components/console/pos-hardware-terms-sign-page.component.js +104 -0
  50. package/src/lib/components/console/pos-hardware-terms-sign-page.component.js.map +1 -0
  51. package/src/lib/components/console/pos-hardware-waiting-card.component.d.ts +24 -0
  52. package/src/lib/components/console/pos-hardware-waiting-card.component.js +205 -0
  53. package/src/lib/components/console/pos-hardware-waiting-card.component.js.map +1 -0
  54. package/src/lib/components/console/pos-ops/pos-check-in-dialog.component.js +297 -201
  55. package/src/lib/components/console/pos-ops/pos-check-in-dialog.component.js.map +1 -1
  56. package/src/lib/components/console/pos-ops/pos-check-ins-card.component.d.ts +3 -0
  57. package/src/lib/components/console/pos-ops/pos-check-ins-card.component.js +260 -92
  58. package/src/lib/components/console/pos-ops/pos-check-ins-card.component.js.map +1 -1
  59. package/src/lib/components/console/pos-ops/pos-operations-bar.component.d.ts +9 -0
  60. package/src/lib/components/console/pos-ops/pos-operations-bar.component.js +15 -6
  61. package/src/lib/components/console/pos-ops/pos-operations-bar.component.js.map +1 -1
  62. package/src/lib/components/console/pos-ops/pos-rain-check-dialog.component.d.ts +25 -0
  63. package/src/lib/components/console/pos-ops/pos-rain-check-dialog.component.js +136 -0
  64. package/src/lib/components/console/pos-ops/pos-rain-check-dialog.component.js.map +1 -0
  65. package/src/lib/components/console/pos-ops/pos-shift-panel.component.d.ts +6 -0
  66. package/src/lib/components/console/pos-ops/pos-shift-panel.component.js +48 -31
  67. package/src/lib/components/console/pos-ops/pos-shift-panel.component.js.map +1 -1
  68. package/src/lib/components/console/pos-page.component.d.ts +1 -1
  69. package/src/lib/components/console/pos-page.component.js +63 -12
  70. package/src/lib/components/console/pos-page.component.js.map +1 -1
  71. package/src/lib/components/console/pos-reader-splash-setting.component.js +1 -1
  72. package/src/lib/components/console/pos-reader-splash-setting.component.js.map +1 -1
  73. package/src/lib/components/console/product-check-in-fields.component.d.ts +4 -2
  74. package/src/lib/components/console/product-check-in-fields.component.js +60 -14
  75. package/src/lib/components/console/product-check-in-fields.component.js.map +1 -1
  76. package/src/lib/components/console/product-editor-dialog.component.js +44 -1
  77. package/src/lib/components/console/product-editor-dialog.component.js.map +1 -1
  78. package/src/lib/components/console/product-hardware-sku-field.component.d.ts +19 -0
  79. package/src/lib/components/console/product-hardware-sku-field.component.js +92 -0
  80. package/src/lib/components/console/product-hardware-sku-field.component.js.map +1 -0
  81. package/src/lib/components/console/product-kiosk-offer-field.component.d.ts +22 -0
  82. package/src/lib/components/console/product-kiosk-offer-field.component.js +94 -0
  83. package/src/lib/components/console/product-kiosk-offer-field.component.js.map +1 -0
  84. package/src/lib/components/console/product-register-fields.component.d.ts +4 -0
  85. package/src/lib/components/console/product-register-fields.component.js +31 -2
  86. package/src/lib/components/console/product-register-fields.component.js.map +1 -1
  87. package/src/lib/components/console/products-hub-card.component.js +11 -2
  88. package/src/lib/components/console/products-hub-card.component.js.map +1 -1
  89. package/src/lib/components/console/register-hardware-card.component.d.ts +11 -0
  90. package/src/lib/components/console/register-hardware-card.component.js +56 -27
  91. package/src/lib/components/console/register-hardware-card.component.js.map +1 -1
  92. package/src/lib/components/console/reservation-guests-dialog.component.d.ts +28 -0
  93. package/src/lib/components/console/reservation-guests-dialog.component.js +189 -0
  94. package/src/lib/components/console/reservation-guests-dialog.component.js.map +1 -0
  95. package/src/lib/components/console/reservations-card.component.d.ts +9 -0
  96. package/src/lib/components/console/reservations-card.component.js +158 -23
  97. package/src/lib/components/console/reservations-card.component.js.map +1 -1
  98. package/src/lib/components/console/shippingeasy-card.component.js +1 -1
  99. package/src/lib/components/console/shippingeasy-card.component.js.map +1 -1
  100. package/src/lib/components/console/shipstation-card.component.d.ts +1 -1
  101. package/src/lib/components/console/shipstation-card.component.js +3 -3
  102. package/src/lib/components/console/shipstation-card.component.js.map +1 -1
  103. package/src/lib/components/product-detail.d.ts +4 -0
  104. package/src/lib/components/product-detail.js +40 -5
  105. package/src/lib/components/product-detail.js.map +1 -1
  106. package/src/lib/components/product-reviews.js +28 -11
  107. package/src/lib/components/product-reviews.js.map +1 -1
  108. package/src/lib/components/reservation-widget.js +28 -5
  109. package/src/lib/components/reservation-widget.js.map +1 -1
  110. package/src/lib/components/use-day-picker.d.ts +25 -0
  111. package/src/lib/components/use-day-picker.js +57 -0
  112. package/src/lib/components/use-day-picker.js.map +1 -0
  113. package/src/lib/constants/operator-alerts.d.ts +55 -0
  114. package/src/lib/constants/operator-alerts.js +156 -1
  115. package/src/lib/constants/operator-alerts.js.map +1 -1
  116. package/src/lib/declarations.console-server.js +54 -1
  117. package/src/lib/declarations.console-server.js.map +1 -1
  118. package/src/lib/declarations.d.ts +4 -2
  119. package/src/lib/declarations.js +11 -4
  120. package/src/lib/declarations.js.map +1 -1
  121. package/src/lib/declarations.server.js +9 -0
  122. package/src/lib/declarations.server.js.map +1 -1
  123. package/src/lib/model/commerce-cart.d.ts +9 -1
  124. package/src/lib/model/commerce-cart.js +4 -3
  125. package/src/lib/model/commerce-cart.js.map +1 -1
  126. package/src/lib/model/commerce-check-in-product.d.ts +26 -0
  127. package/src/lib/model/commerce-check-in-product.js +64 -2
  128. package/src/lib/model/commerce-check-in-product.js.map +1 -1
  129. package/src/lib/model/commerce-check-in-record.d.ts +127 -0
  130. package/src/lib/model/commerce-check-in-record.js +36 -0
  131. package/src/lib/model/commerce-check-in-record.js.map +1 -0
  132. package/src/lib/model/commerce-check-in.d.ts +91 -87
  133. package/src/lib/model/commerce-check-in.js +186 -62
  134. package/src/lib/model/commerce-check-in.js.map +1 -1
  135. package/src/lib/model/commerce-kiosk-offer.d.ts +31 -0
  136. package/src/lib/model/commerce-kiosk-offer.js +35 -0
  137. package/src/lib/model/commerce-kiosk-offer.js.map +1 -0
  138. package/src/lib/model/commerce-orders.d.ts +76 -0
  139. package/src/lib/model/commerce-orders.js +6 -0
  140. package/src/lib/model/commerce-orders.js.map +1 -1
  141. package/src/lib/model/commerce-pos-day-report.d.ts +54 -1
  142. package/src/lib/model/commerce-pos-day-report.js +39 -2
  143. package/src/lib/model/commerce-pos-day-report.js.map +1 -1
  144. package/src/lib/model/commerce-pos-offline.d.ts +1 -1
  145. package/src/lib/model/commerce-pos-offline.js +6 -1
  146. package/src/lib/model/commerce-pos-offline.js.map +1 -1
  147. package/src/lib/model/commerce-pos-ops.js +3 -2
  148. package/src/lib/model/commerce-pos-ops.js.map +1 -1
  149. package/src/lib/model/commerce-pos.d.ts +36 -3
  150. package/src/lib/model/commerce-pos.js +14 -6
  151. package/src/lib/model/commerce-pos.js.map +1 -1
  152. package/src/lib/model/commerce-receipt.js +3 -2
  153. package/src/lib/model/commerce-receipt.js.map +1 -1
  154. package/src/lib/model/commerce-reservations.d.ts +69 -4
  155. package/src/lib/model/commerce-reservations.js +57 -5
  156. package/src/lib/model/commerce-reservations.js.map +1 -1
  157. package/src/lib/model/commerce.d.ts +66 -0
  158. package/src/lib/model/commerce.js +15 -0
  159. package/src/lib/model/commerce.js.map +1 -1
  160. package/src/lib/model/order-list-fields.d.ts +11 -0
  161. package/src/lib/model/order-list-fields.js +17 -1
  162. package/src/lib/model/order-list-fields.js.map +1 -1
  163. package/src/lib/model/own-store-tax-summary.d.ts +152 -0
  164. package/src/lib/model/own-store-tax-summary.js +288 -0
  165. package/src/lib/model/own-store-tax-summary.js.map +1 -0
  166. package/src/lib/model/plugin-permissions.d.ts +67 -0
  167. package/src/lib/model/plugin-permissions.js +224 -7
  168. package/src/lib/model/plugin-permissions.js.map +1 -1
  169. package/src/lib/model/pos-cellular.d.ts +223 -0
  170. package/src/lib/model/pos-cellular.js +293 -0
  171. package/src/lib/model/pos-cellular.js.map +1 -0
  172. package/src/lib/model/pos-hardware-in-person.d.ts +98 -0
  173. package/src/lib/model/pos-hardware-in-person.js +224 -0
  174. package/src/lib/model/pos-hardware-in-person.js.map +1 -0
  175. package/src/lib/model/pos-hardware-tax-summary.d.ts +166 -0
  176. package/src/lib/model/pos-hardware-tax-summary.js +225 -0
  177. package/src/lib/model/pos-hardware-tax-summary.js.map +1 -0
  178. package/src/lib/model/pos-hardware-terms.d.ts +74 -0
  179. package/src/lib/model/pos-hardware-terms.js +115 -0
  180. package/src/lib/model/pos-hardware-terms.js.map +1 -0
  181. package/src/lib/model/pos-hardware.d.ts +349 -7
  182. package/src/lib/model/pos-hardware.js +291 -20
  183. package/src/lib/model/pos-hardware.js.map +1 -1
  184. package/src/lib/model/product-write.d.ts +10 -0
  185. package/src/lib/model/product-write.js +4 -2
  186. package/src/lib/model/product-write.js.map +1 -1
  187. package/src/lib/plugin.js +16 -0
  188. package/src/lib/plugin.js.map +1 -1
  189. package/src/lib/server/billing-webhook.js +25 -1
  190. package/src/lib/server/billing-webhook.js.map +1 -1
  191. package/src/lib/server/cancel-order.js +7 -5
  192. package/src/lib/server/cancel-order.js.map +1 -1
  193. package/src/lib/server/cart-checkout.js +20 -8
  194. package/src/lib/server/cart-checkout.js.map +1 -1
  195. package/src/lib/server/cart.js +29 -0
  196. package/src/lib/server/cart.js.map +1 -1
  197. package/src/lib/server/checkout.js +42 -13
  198. package/src/lib/server/checkout.js.map +1 -1
  199. package/src/lib/server/collection-membership.d.ts +4 -0
  200. package/src/lib/server/collection-membership.js +5 -10
  201. package/src/lib/server/collection-membership.js.map +1 -1
  202. package/src/lib/server/commerce-site-keys.d.ts +29 -0
  203. package/src/lib/server/commerce-site-keys.js +40 -0
  204. package/src/lib/server/commerce-site-keys.js.map +1 -0
  205. package/src/lib/server/draft-order.js +8 -5
  206. package/src/lib/server/draft-order.js.map +1 -1
  207. package/src/lib/server/fulfill-order.js +7 -5
  208. package/src/lib/server/fulfill-order.js.map +1 -1
  209. package/src/lib/server/gift-cards.js +8 -5
  210. package/src/lib/server/gift-cards.js.map +1 -1
  211. package/src/lib/server/local-fulfillment-status.js +9 -6
  212. package/src/lib/server/local-fulfillment-status.js.map +1 -1
  213. package/src/lib/server/member-post.js +6 -5
  214. package/src/lib/server/member-post.js.map +1 -1
  215. package/src/lib/server/membership-admin-password.d.ts +4 -2
  216. package/src/lib/server/membership-admin-password.js +10 -8
  217. package/src/lib/server/membership-admin-password.js.map +1 -1
  218. package/src/lib/server/membership-admin-remove.d.ts +7 -5
  219. package/src/lib/server/membership-admin-remove.js +9 -9
  220. package/src/lib/server/membership-admin-remove.js.map +1 -1
  221. package/src/lib/server/order-notifications.js +5 -2
  222. package/src/lib/server/order-notifications.js.map +1 -1
  223. package/src/lib/server/order-print.js +7 -5
  224. package/src/lib/server/order-print.js.map +1 -1
  225. package/src/lib/server/order-receipt-send.js +8 -5
  226. package/src/lib/server/order-receipt-send.js.map +1 -1
  227. package/src/lib/server/orders-shipping-prepare.js +9 -6
  228. package/src/lib/server/orders-shipping-prepare.js.map +1 -1
  229. package/src/lib/server/own-store-tax-return-source.d.ts +47 -0
  230. package/src/lib/server/own-store-tax-return-source.js +312 -0
  231. package/src/lib/server/own-store-tax-return-source.js.map +1 -0
  232. package/src/lib/server/payment-methods.js +13 -9
  233. package/src/lib/server/payment-methods.js.map +1 -1
  234. package/src/lib/server/pos-auth.d.ts +37 -6
  235. package/src/lib/server/pos-auth.js +40 -15
  236. package/src/lib/server/pos-auth.js.map +1 -1
  237. package/src/lib/server/pos-cellular-billing.d.ts +81 -0
  238. package/src/lib/server/pos-cellular-billing.js +711 -0
  239. package/src/lib/server/pos-cellular-billing.js.map +1 -0
  240. package/src/lib/server/pos-cellular-notify.d.ts +47 -0
  241. package/src/lib/server/pos-cellular-notify.js +150 -0
  242. package/src/lib/server/pos-cellular-notify.js.map +1 -0
  243. package/src/lib/server/pos-cellular.d.ts +88 -0
  244. package/src/lib/server/pos-cellular.js +339 -0
  245. package/src/lib/server/pos-cellular.js.map +1 -0
  246. package/src/lib/server/pos-check-in.d.ts +5 -1
  247. package/src/lib/server/pos-check-in.js +296 -28
  248. package/src/lib/server/pos-check-in.js.map +1 -1
  249. package/src/lib/server/pos-day-report.js +29 -3
  250. package/src/lib/server/pos-day-report.js.map +1 -1
  251. package/src/lib/server/pos-display.d.ts +16 -0
  252. package/src/lib/server/pos-display.js +129 -2
  253. package/src/lib/server/pos-display.js.map +1 -1
  254. package/src/lib/server/pos-hardware-admin.d.ts +10 -1
  255. package/src/lib/server/pos-hardware-admin.js +250 -84
  256. package/src/lib/server/pos-hardware-admin.js.map +1 -1
  257. package/src/lib/server/pos-hardware-attach.d.ts +105 -0
  258. package/src/lib/server/pos-hardware-attach.js +443 -0
  259. package/src/lib/server/pos-hardware-attach.js.map +1 -0
  260. package/src/lib/server/pos-hardware-buyer-lookup.d.ts +58 -0
  261. package/src/lib/server/pos-hardware-buyer-lookup.js +180 -0
  262. package/src/lib/server/pos-hardware-buyer-lookup.js.map +1 -0
  263. package/src/lib/server/pos-hardware-buyer-record.d.ts +29 -0
  264. package/src/lib/server/pos-hardware-buyer-record.js +86 -0
  265. package/src/lib/server/pos-hardware-buyer-record.js.map +1 -0
  266. package/src/lib/server/pos-hardware-cancel.d.ts +58 -0
  267. package/src/lib/server/pos-hardware-cancel.js +147 -0
  268. package/src/lib/server/pos-hardware-cancel.js.map +1 -0
  269. package/src/lib/server/pos-hardware-closure-notices.d.ts +27 -0
  270. package/src/lib/server/pos-hardware-closure-notices.js +85 -0
  271. package/src/lib/server/pos-hardware-closure-notices.js.map +1 -0
  272. package/src/lib/server/pos-hardware-closure.d.ts +60 -0
  273. package/src/lib/server/pos-hardware-closure.js +153 -0
  274. package/src/lib/server/pos-hardware-closure.js.map +1 -0
  275. package/src/lib/server/pos-hardware-in-person.d.ts +61 -0
  276. package/src/lib/server/pos-hardware-in-person.js +236 -0
  277. package/src/lib/server/pos-hardware-in-person.js.map +1 -0
  278. package/src/lib/server/pos-hardware-notify.d.ts +40 -2
  279. package/src/lib/server/pos-hardware-notify.js +195 -14
  280. package/src/lib/server/pos-hardware-notify.js.map +1 -1
  281. package/src/lib/server/pos-hardware-readers.d.ts +115 -0
  282. package/src/lib/server/pos-hardware-readers.js +430 -0
  283. package/src/lib/server/pos-hardware-readers.js.map +1 -0
  284. package/src/lib/server/pos-hardware-returns.d.ts +116 -0
  285. package/src/lib/server/pos-hardware-returns.js +502 -0
  286. package/src/lib/server/pos-hardware-returns.js.map +1 -0
  287. package/src/lib/server/pos-hardware-sale-fields.d.ts +28 -0
  288. package/src/lib/server/pos-hardware-sale-fields.js +44 -0
  289. package/src/lib/server/pos-hardware-sale-fields.js.map +1 -0
  290. package/src/lib/server/pos-hardware-sale.d.ts +54 -0
  291. package/src/lib/server/pos-hardware-sale.js +369 -0
  292. package/src/lib/server/pos-hardware-sale.js.map +1 -0
  293. package/src/lib/server/pos-hardware-tax-return-source.d.ts +99 -0
  294. package/src/lib/server/pos-hardware-tax-return-source.js +462 -0
  295. package/src/lib/server/pos-hardware-tax-return-source.js.map +1 -0
  296. package/src/lib/server/pos-hardware-terms.d.ts +64 -0
  297. package/src/lib/server/pos-hardware-terms.js +154 -0
  298. package/src/lib/server/pos-hardware-terms.js.map +1 -0
  299. package/src/lib/server/pos-hardware-user-eraser.d.ts +3 -0
  300. package/src/lib/server/pos-hardware-user-eraser.js +31 -7
  301. package/src/lib/server/pos-hardware-user-eraser.js.map +1 -1
  302. package/src/lib/server/pos-hardware-webhook.d.ts +5 -0
  303. package/src/lib/server/pos-hardware-webhook.js +51 -6
  304. package/src/lib/server/pos-hardware-webhook.js.map +1 -1
  305. package/src/lib/server/pos-hardware.d.ts +74 -1
  306. package/src/lib/server/pos-hardware.js +175 -10
  307. package/src/lib/server/pos-hardware.js.map +1 -1
  308. package/src/lib/server/pos-kiosk.d.ts +2 -0
  309. package/src/lib/server/pos-kiosk.js +17 -12
  310. package/src/lib/server/pos-kiosk.js.map +1 -1
  311. package/src/lib/server/pos-offline-card-order.d.ts +64 -0
  312. package/src/lib/server/pos-offline-card-order.js +102 -0
  313. package/src/lib/server/pos-offline-card-order.js.map +1 -0
  314. package/src/lib/server/pos-offline-card-reconcile.d.ts +67 -1
  315. package/src/lib/server/pos-offline-card-reconcile.js +215 -18
  316. package/src/lib/server/pos-offline-card-reconcile.js.map +1 -1
  317. package/src/lib/server/pos-offline-sync.d.ts +39 -0
  318. package/src/lib/server/pos-offline-sync.js +190 -55
  319. package/src/lib/server/pos-offline-sync.js.map +1 -1
  320. package/src/lib/server/pos-ops-gate.d.ts +15 -4
  321. package/src/lib/server/pos-ops-gate.js +42 -9
  322. package/src/lib/server/pos-ops-gate.js.map +1 -1
  323. package/src/lib/server/pos-ops-routes.js +24 -0
  324. package/src/lib/server/pos-ops-routes.js.map +1 -1
  325. package/src/lib/server/pos-order-refund-ids.d.ts +28 -0
  326. package/src/lib/server/pos-order-refund-ids.js +31 -0
  327. package/src/lib/server/pos-order-refund-ids.js.map +1 -0
  328. package/src/lib/server/pos-order.js +52 -49
  329. package/src/lib/server/pos-order.js.map +1 -1
  330. package/src/lib/server/pos-payment.js +1 -1
  331. package/src/lib/server/pos-payment.js.map +1 -1
  332. package/src/lib/server/pos-reader-splash-admin.js +30 -32
  333. package/src/lib/server/pos-reader-splash-admin.js.map +1 -1
  334. package/src/lib/server/pos-reader-splash-images-route.d.ts +17 -0
  335. package/src/lib/server/pos-reader-splash-images-route.js +113 -0
  336. package/src/lib/server/pos-reader-splash-images-route.js.map +1 -0
  337. package/src/lib/server/pos-reader-splash-images.d.ts +51 -0
  338. package/src/lib/server/pos-reader-splash-images.js +123 -0
  339. package/src/lib/server/pos-reader-splash-images.js.map +1 -0
  340. package/src/lib/server/pos-reader-splash.d.ts +68 -1
  341. package/src/lib/server/pos-reader-splash.js +182 -11
  342. package/src/lib/server/pos-reader-splash.js.map +1 -1
  343. package/src/lib/server/pos-readers.d.ts +29 -0
  344. package/src/lib/server/pos-readers.js +134 -35
  345. package/src/lib/server/pos-readers.js.map +1 -1
  346. package/src/lib/server/pos-return.d.ts +10 -0
  347. package/src/lib/server/pos-return.js +26 -1
  348. package/src/lib/server/pos-return.js.map +1 -1
  349. package/src/lib/server/pos-sale.js +6 -1
  350. package/src/lib/server/pos-sale.js.map +1 -1
  351. package/src/lib/server/pos-shift.js +11 -0
  352. package/src/lib/server/pos-shift.js.map +1 -1
  353. package/src/lib/server/pos-staff-pin.js +25 -20
  354. package/src/lib/server/pos-staff-pin.js.map +1 -1
  355. package/src/lib/server/pos-terminal.d.ts +14 -4
  356. package/src/lib/server/pos-terminal.js +34 -38
  357. package/src/lib/server/pos-terminal.js.map +1 -1
  358. package/src/lib/server/printers.d.ts +7 -3
  359. package/src/lib/server/printers.js +18 -12
  360. package/src/lib/server/printers.js.map +1 -1
  361. package/src/lib/server/product-drafts.d.ts +11 -1
  362. package/src/lib/server/product-drafts.js +12 -8
  363. package/src/lib/server/product-drafts.js.map +1 -1
  364. package/src/lib/server/product.d.ts +15 -1
  365. package/src/lib/server/product.js +16 -3
  366. package/src/lib/server/product.js.map +1 -1
  367. package/src/lib/server/products-write.d.ts +26 -2
  368. package/src/lib/server/products-write.js +19 -7
  369. package/src/lib/server/products-write.js.map +1 -1
  370. package/src/lib/server/refund.js +34 -11
  371. package/src/lib/server/refund.js.map +1 -1
  372. package/src/lib/server/reservation-availability-query.d.ts +8 -0
  373. package/src/lib/server/reservation-availability-query.js +11 -1
  374. package/src/lib/server/reservation-availability-query.js.map +1 -1
  375. package/src/lib/server/reservation-availability.d.ts +1 -1
  376. package/src/lib/server/reservation-availability.js +7 -2
  377. package/src/lib/server/reservation-availability.js.map +1 -1
  378. package/src/lib/server/reservation-guests.d.ts +32 -0
  379. package/src/lib/server/reservation-guests.js +285 -0
  380. package/src/lib/server/reservation-guests.js.map +1 -0
  381. package/src/lib/server/reservation-walk-in.d.ts +11 -1
  382. package/src/lib/server/reservation-walk-in.js +56 -12
  383. package/src/lib/server/reservation-walk-in.js.map +1 -1
  384. package/src/lib/server/reserve.js +18 -5
  385. package/src/lib/server/reserve.js.map +1 -1
  386. package/src/lib/server/returns.js +9 -6
  387. package/src/lib/server/returns.js.map +1 -1
  388. package/src/lib/server/reviews.d.ts +22 -2
  389. package/src/lib/server/reviews.js +88 -20
  390. package/src/lib/server/reviews.js.map +1 -1
  391. package/src/lib/server/shipping-connectors.js +10 -11
  392. package/src/lib/server/shipping-connectors.js.map +1 -1
  393. package/src/lib/server/site-page-resolver.js +20 -3
  394. package/src/lib/server/site-page-resolver.js.map +1 -1
  395. package/src/lib/server/store-day.d.ts +33 -0
  396. package/src/lib/server/store-day.js +46 -0
  397. package/src/lib/server/store-day.js.map +1 -0
  398. package/src/lib/server/store-setup-ai-writer.d.ts +2 -1
  399. package/src/lib/server/store-setup-ai-writer.js +5 -6
  400. package/src/lib/server/store-setup-ai-writer.js.map +1 -1
  401. package/src/lib/server/storefront-tax.d.ts +13 -0
  402. package/src/lib/server/storefront-tax.js +8 -1
  403. package/src/lib/server/storefront-tax.js.map +1 -1
  404. package/src/lib/server/tax-return-source.d.ts +32 -3
  405. package/src/lib/server/tax-return-source.js +99 -20
  406. package/src/lib/server/tax-return-source.js.map +1 -1
  407. package/src/lib/server.js +100 -2
  408. package/src/lib/server.js.map +1 -1
  409. package/src/lib/testing/fake-firestore.js +58 -16
  410. package/src/lib/testing/fake-firestore.js.map +1 -1
  411. package/src/lib/testing/pos-ops-memory-firestore.js +18 -4
  412. package/src/lib/testing/pos-ops-memory-firestore.js.map +1 -1
  413. package/src/lib/testing/site-register-permissions.d.ts +29 -0
  414. package/src/lib/testing/site-register-permissions.js +38 -0
  415. package/src/lib/testing/site-register-permissions.js.map +1 -0
  416. package/src/lib/transfer/tracking.server.js +8 -5
  417. package/src/lib/transfer/tracking.server.js.map +1 -1
  418. package/src/lib/utils/add-to-cart.d.ts +2 -0
  419. package/src/lib/utils/add-to-cart.js +6 -3
  420. package/src/lib/utils/add-to-cart.js.map +1 -1
@@ -0,0 +1,154 @@
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
+ */ import { readClientIp } from "@aglyn/aglyn/app-utils/request-ip";
17
+ import { firebaseAdmin } from "@aglyn/tenant-data-admin";
18
+ import { isPosHardwareRegisterSale, posHardwareTermsAcceptance, posHardwareTermsAcceptanceOf, posHardwareTermsUrl, POS_HARDWARE_TERMS_SECTIONS, POS_HARDWARE_TERMS_SUMMARY, POS_HARDWARE_TERMS_VERSION } from "../model/pos-hardware-terms.js";
19
+ import { hardwareOrdersRef, posHardwareSaleRef } from "./pos-hardware.js";
20
+ /*==========================================
21
+ * ACCEPTING THE TERMS OF SALE AT THE BOOTH (AGL-4038).
22
+ *
23
+ * The register user hands the buyer the register tablet, or puts the terms
24
+ * on the register's customer display through core's display seam, and the
25
+ * buyer accepts there before paying (Terms 4.28). Either way the acceptance
26
+ * is written here, on the register sale (`hardwareTermsAcceptance`) and, once
27
+ * the sale has become a hardware order, on that too (`termsAcceptance`),
28
+ * with the version, the time, how, the register user, and the device's
29
+ * address and browser. The first acceptance stands: accepting again changes
30
+ * nothing, and an electronic acceptance replaces only the receipt-and-sign
31
+ * notice a sale paid without one was recorded under.
32
+ *=========================================*/ export class PosHardwareTermsRefusal extends Error {
33
+ constructor(message, status){
34
+ super(message), this.status = status;
35
+ }
36
+ }
37
+ const ID = /^[A-Za-z0-9_-]{1,128}$/;
38
+ function firestore() {
39
+ return firebaseAdmin.app().firestore();
40
+ }
41
+ function registerOrderRef(config, registerOrderId) {
42
+ if (!ID.test(registerOrderId)) throw new PosHardwareTermsRefusal('Name the sale.', 400);
43
+ return firestore().collection('hosts').doc(config.sellerHostId).collection('orders').doc(registerOrderId);
44
+ }
45
+ /** What the buyer reads: the version, the sections, the link and the summary. */ export function posHardwareTermsView() {
46
+ return {
47
+ version: POS_HARDWARE_TERMS_VERSION,
48
+ sections: POS_HARDWARE_TERMS_SECTIONS,
49
+ url: posHardwareTermsUrl(),
50
+ summary: [
51
+ ...POS_HARDWARE_TERMS_SUMMARY
52
+ ]
53
+ };
54
+ }
55
+ /** The device a request came from, as an acceptance records it. */ export function posHardwareTermsDevice(headers) {
56
+ var _readClientIp, _headers_useragent;
57
+ return {
58
+ ip: (_readClientIp = readClientIp(headers)) != null ? _readClientIp : null,
59
+ userAgent: String((_headers_useragent = headers['user-agent']) != null ? _headers_useragent : '').slice(0, 400) || null
60
+ };
61
+ }
62
+ /** The sale's items and total, for the screen the buyer accepts on. */ export async function posHardwareTermsSale(config, registerOrderId) {
63
+ const snapshot = await registerOrderRef(config, registerOrderId).get();
64
+ const order = snapshot.exists ? snapshot.data() : null;
65
+ if (!order || !isPosHardwareRegisterSale(order)) throw new PosHardwareTermsRefusal('This sale has no card reader.', 404);
66
+ const items = (Array.isArray(order['lineItems']) ? order['lineItems'] : []).slice(0, 50).map((line)=>{
67
+ var _line_quantity, _line_name;
68
+ return `${Number((_line_quantity = line['quantity']) != null ? _line_quantity : 1) || 1} × ${String((_line_name = line['name']) != null ? _line_name : 'Item')}`;
69
+ });
70
+ return {
71
+ items,
72
+ acceptance: posHardwareTermsAcceptanceOf(order['hardwareTermsAcceptance'])
73
+ };
74
+ }
75
+ /** Whether the buyer has accepted, for the register waiting on the display. */ export async function posHardwareTermsStatus(config, registerOrderId) {
76
+ const { acceptance } = await posHardwareTermsSale(config, registerOrderId);
77
+ return {
78
+ acceptance
79
+ };
80
+ }
81
+ /** Notes who put the terms on the display, so the display's acceptance names them. */ export async function requestPosHardwareTermsOnDisplay(input) {
82
+ var _input_now;
83
+ const ref = registerOrderRef(input.config, input.registerOrderId);
84
+ const snapshot = await ref.get();
85
+ if (!snapshot.exists || !isPosHardwareRegisterSale(snapshot.data())) {
86
+ throw new PosHardwareTermsRefusal('This sale has no card reader.', 404);
87
+ }
88
+ await ref.set({
89
+ hardwareTermsRequest: {
90
+ by: input.uid,
91
+ atMs: (_input_now = input.now) != null ? _input_now : Date.now()
92
+ }
93
+ }, {
94
+ merge: true
95
+ });
96
+ }
97
+ /**
98
+ * Records the buyer's acceptance on the register sale and on its hardware
99
+ * order when there is one, in one transaction. The first electronic
100
+ * acceptance stands; answers the acceptance that holds.
101
+ */ export async function recordPosHardwareTermsAcceptance(input) {
102
+ const ref = registerOrderRef(input.config, input.registerOrderId);
103
+ return firestore().runTransaction(async (transaction)=>{
104
+ var _order_status, _ref, _input_now;
105
+ var _order_hardwareTermsRequest;
106
+ const snapshot = await transaction.get(ref);
107
+ const order = snapshot.exists ? snapshot.data() : null;
108
+ if (!order || !isPosHardwareRegisterSale(order)) throw new PosHardwareTermsRefusal('This sale has no card reader.', 404);
109
+ const status = String((_order_status = order['status']) != null ? _order_status : '');
110
+ if (status !== 'pending' && status !== 'paid') {
111
+ throw new PosHardwareTermsRefusal('This sale was voided or refunded.', 409);
112
+ }
113
+ const held = posHardwareTermsAcceptanceOf(order['hardwareTermsAcceptance']);
114
+ if (held && held.method !== 'notice') return held;
115
+ const staffUid = input.method === 'display' ? String((_ref = (_order_hardwareTermsRequest = order['hardwareTermsRequest']) == null ? void 0 : _order_hardwareTermsRequest['by']) != null ? _ref : '') || input.staffUid : input.staffUid;
116
+ const acceptance = posHardwareTermsAcceptance({
117
+ method: input.method,
118
+ atMs: (_input_now = input.now) != null ? _input_now : Date.now(),
119
+ staffUid,
120
+ ip: input.ip,
121
+ userAgent: input.userAgent
122
+ });
123
+ // The hardware order the sale already became, if it did: its notice is
124
+ // replaced by the acceptance.
125
+ const hardwareOrderId = typeof order['hardwareOrderId'] === 'string' ? order['hardwareOrderId'] : '';
126
+ let hardwareRef = null;
127
+ if (hardwareOrderId) {
128
+ var _sale_get;
129
+ const sale = await transaction.get(posHardwareSaleRef(hardwareOrderId));
130
+ const orgId = String((_sale_get = sale.get('orgId')) != null ? _sale_get : '');
131
+ if (orgId) {
132
+ const candidate = hardwareOrdersRef(orgId).doc(hardwareOrderId);
133
+ const hardware = await transaction.get(candidate);
134
+ const current = hardware.exists ? hardware.data() : null;
135
+ const kept = posHardwareTermsAcceptanceOf(current == null ? void 0 : current.termsAcceptance);
136
+ if (current && (!kept || kept.method === 'notice')) hardwareRef = candidate;
137
+ }
138
+ }
139
+ transaction.set(ref, {
140
+ hardwareTermsAcceptance: acceptance
141
+ }, {
142
+ merge: true
143
+ });
144
+ if (hardwareRef) transaction.set(hardwareRef, {
145
+ termsAcceptance: acceptance,
146
+ updatedAtMs: acceptance.atMs
147
+ }, {
148
+ merge: true
149
+ });
150
+ return acceptance;
151
+ });
152
+ }
153
+
154
+ //# sourceMappingURL=pos-hardware-terms.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/server/pos-hardware-terms.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 { readClientIp } from '@aglyn/aglyn/app-utils/request-ip'\nimport { firebaseAdmin } from '@aglyn/tenant-data-admin'\nimport type { PosHardwareInPersonConfig, PosHardwareOrder } from '../model/pos-hardware'\nimport {\n isPosHardwareRegisterSale,\n posHardwareTermsAcceptance,\n posHardwareTermsAcceptanceOf,\n posHardwareTermsUrl,\n POS_HARDWARE_TERMS_SECTIONS,\n POS_HARDWARE_TERMS_SUMMARY,\n POS_HARDWARE_TERMS_VERSION,\n type PosHardwareTermsAcceptance,\n} from '../model/pos-hardware-terms'\nimport { hardwareOrdersRef, posHardwareSaleRef } from './pos-hardware'\n\n/*==========================================\n * ACCEPTING THE TERMS OF SALE AT THE BOOTH (AGL-4038).\n *\n * The register user hands the buyer the register tablet, or puts the terms\n * on the register's customer display through core's display seam, and the\n * buyer accepts there before paying (Terms 4.28). Either way the acceptance\n * is written here, on the register sale (`hardwareTermsAcceptance`) and, once\n * the sale has become a hardware order, on that too (`termsAcceptance`),\n * with the version, the time, how, the register user, and the device's\n * address and browser. The first acceptance stands: accepting again changes\n * nothing, and an electronic acceptance replaces only the receipt-and-sign\n * notice a sale paid without one was recorded under.\n *=========================================*/\n\nexport class PosHardwareTermsRefusal extends Error {\n constructor(\n message: string,\n readonly status: number,\n ) {\n super(message)\n }\n}\n\nconst ID = /^[A-Za-z0-9_-]{1,128}$/\n\nfunction firestore() {\n return firebaseAdmin.app().firestore()\n}\n\nfunction registerOrderRef(config: PosHardwareInPersonConfig, registerOrderId: string) {\n if (!ID.test(registerOrderId)) throw new PosHardwareTermsRefusal('Name the sale.', 400)\n return firestore().collection('hosts').doc(config.sellerHostId).collection('orders').doc(registerOrderId)\n}\n\n/** What the buyer reads: the version, the sections, the link and the summary. */\nexport function posHardwareTermsView() {\n return {\n version: POS_HARDWARE_TERMS_VERSION,\n sections: POS_HARDWARE_TERMS_SECTIONS,\n url: posHardwareTermsUrl(),\n summary: [...POS_HARDWARE_TERMS_SUMMARY],\n }\n}\n\n/** The device a request came from, as an acceptance records it. */\nexport function posHardwareTermsDevice(headers: Record<string, unknown>): { ip: string | null; userAgent: string | null } {\n return {\n ip: readClientIp(headers as never) ?? null,\n userAgent: String(headers['user-agent'] ?? '').slice(0, 400) || null,\n }\n}\n\n/** The sale's items and total, for the screen the buyer accepts on. */\nexport async function posHardwareTermsSale(\n config: PosHardwareInPersonConfig,\n registerOrderId: string,\n): Promise<{ items: string[]; acceptance: PosHardwareTermsAcceptance | null }> {\n const snapshot = await registerOrderRef(config, registerOrderId).get()\n const order = snapshot.exists ? (snapshot.data() as Record<string, any>) : null\n if (!order || !isPosHardwareRegisterSale(order)) throw new PosHardwareTermsRefusal('This sale has no card reader.', 404)\n const items = (Array.isArray(order['lineItems']) ? order['lineItems'] : [])\n .slice(0, 50)\n .map((line: Record<string, unknown>) => `${Number(line['quantity'] ?? 1) || 1} × ${String(line['name'] ?? 'Item')}`)\n return { items, acceptance: posHardwareTermsAcceptanceOf(order['hardwareTermsAcceptance']) }\n}\n\n/** Whether the buyer has accepted, for the register waiting on the display. */\nexport async function posHardwareTermsStatus(\n config: PosHardwareInPersonConfig,\n registerOrderId: string,\n): Promise<{ acceptance: PosHardwareTermsAcceptance | null }> {\n const { acceptance } = await posHardwareTermsSale(config, registerOrderId)\n return { acceptance }\n}\n\n/** Notes who put the terms on the display, so the display's acceptance names them. */\nexport async function requestPosHardwareTermsOnDisplay(input: {\n config: PosHardwareInPersonConfig\n registerOrderId: string\n uid: string\n now?: number\n}): Promise<void> {\n const ref = registerOrderRef(input.config, input.registerOrderId)\n const snapshot = await ref.get()\n if (!snapshot.exists || !isPosHardwareRegisterSale(snapshot.data())) {\n throw new PosHardwareTermsRefusal('This sale has no card reader.', 404)\n }\n await ref.set({ hardwareTermsRequest: { by: input.uid, atMs: input.now ?? Date.now() } }, { merge: true })\n}\n\n/**\n * Records the buyer's acceptance on the register sale and on its hardware\n * order when there is one, in one transaction. The first electronic\n * acceptance stands; answers the acceptance that holds.\n */\nexport async function recordPosHardwareTermsAcceptance(input: {\n config: PosHardwareInPersonConfig\n registerOrderId: string\n method: 'tablet' | 'display'\n staffUid: string | null\n ip: string | null\n userAgent: string | null\n now?: number\n}): Promise<PosHardwareTermsAcceptance> {\n const ref = registerOrderRef(input.config, input.registerOrderId)\n return firestore().runTransaction(async (transaction: any) => {\n const snapshot = await transaction.get(ref)\n const order = snapshot.exists ? (snapshot.data() as Record<string, any>) : null\n if (!order || !isPosHardwareRegisterSale(order)) throw new PosHardwareTermsRefusal('This sale has no card reader.', 404)\n const status = String(order['status'] ?? '')\n if (status !== 'pending' && status !== 'paid') {\n throw new PosHardwareTermsRefusal('This sale was voided or refunded.', 409)\n }\n const held = posHardwareTermsAcceptanceOf(order['hardwareTermsAcceptance'])\n if (held && held.method !== 'notice') return held\n const staffUid =\n input.method === 'display'\n ? (String(order['hardwareTermsRequest']?.['by'] ?? '') || input.staffUid)\n : input.staffUid\n const acceptance = posHardwareTermsAcceptance({\n method: input.method,\n atMs: input.now ?? Date.now(),\n staffUid,\n ip: input.ip,\n userAgent: input.userAgent,\n })\n // The hardware order the sale already became, if it did: its notice is\n // replaced by the acceptance.\n const hardwareOrderId = typeof order['hardwareOrderId'] === 'string' ? order['hardwareOrderId'] : ''\n let hardwareRef: any = null\n if (hardwareOrderId) {\n const sale = await transaction.get(posHardwareSaleRef(hardwareOrderId))\n const orgId = String(sale.get('orgId') ?? '')\n if (orgId) {\n const candidate = hardwareOrdersRef(orgId).doc(hardwareOrderId)\n const hardware = await transaction.get(candidate)\n const current = hardware.exists ? (hardware.data() as PosHardwareOrder) : null\n const kept = posHardwareTermsAcceptanceOf(current?.termsAcceptance)\n if (current && (!kept || kept.method === 'notice')) hardwareRef = candidate\n }\n }\n transaction.set(ref, { hardwareTermsAcceptance: acceptance }, { merge: true })\n if (hardwareRef) transaction.set(hardwareRef, { termsAcceptance: acceptance, updatedAtMs: acceptance.atMs }, { merge: true })\n return acceptance\n })\n}\n"],"names":["readClientIp","firebaseAdmin","isPosHardwareRegisterSale","posHardwareTermsAcceptance","posHardwareTermsAcceptanceOf","posHardwareTermsUrl","POS_HARDWARE_TERMS_SECTIONS","POS_HARDWARE_TERMS_SUMMARY","POS_HARDWARE_TERMS_VERSION","hardwareOrdersRef","posHardwareSaleRef","PosHardwareTermsRefusal","Error","message","status","ID","firestore","app","registerOrderRef","config","registerOrderId","test","collection","doc","sellerHostId","posHardwareTermsView","version","sections","url","summary","posHardwareTermsDevice","headers","ip","userAgent","String","slice","posHardwareTermsSale","snapshot","get","order","exists","data","items","Array","isArray","map","line","Number","acceptance","posHardwareTermsStatus","requestPosHardwareTermsOnDisplay","input","ref","set","hardwareTermsRequest","by","uid","atMs","now","Date","merge","recordPosHardwareTermsAcceptance","runTransaction","transaction","held","method","staffUid","hardwareOrderId","hardwareRef","sale","orgId","candidate","hardware","current","kept","termsAcceptance","hardwareTermsAcceptance","updatedAtMs"],"mappings":"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"}
@@ -15,10 +15,13 @@
15
15
  * limitations under the License.
16
16
  */
17
17
  import type { PluginUserEraser } from '@aglyn/aglyn/plugin-manager/plugin-user-erasure';
18
+ import { closeUnshippedPosHardwareOrders } from './pos-hardware-closure';
18
19
  type Firestore = FirebaseFirestore.Firestore;
19
20
  export interface PosHardwareUserEraserDeps {
20
21
  firestore(): Firestore;
21
22
  now?: () => number;
23
+ /** The closure's cancel, replaceable in a spec. */
24
+ closeUnshipped?: typeof closeUnshippedPosHardwareOrders;
22
25
  }
23
26
  export declare function createPosHardwareUserEraser(deps: PosHardwareUserEraserDeps): PluginUserEraser;
24
27
  /** The eraser over the platform's own Firestore, as the declarations register it. */
@@ -15,17 +15,24 @@
15
15
  * limitations under the License.
16
16
  */ import { _ as _extends } from "@swc/helpers/_/_extends";
17
17
  import { firebaseAdmin } from "@aglyn/tenant-data-admin/server/firebase-admin";
18
- import { POS_HARDWARE_ORDERS_COLLECTION, POS_HARDWARE_SALES_COLLECTION, posHardwareBuyerErasedPatch, posHardwareSaleRecord } from "../model/pos-hardware.js";
18
+ import { POS_HARDWARE_ORDERS_COLLECTION, POS_HARDWARE_SALES_COLLECTION, posHardwareBuyerErasedPatch, posHardwareRefundable, posHardwareSaleRecord, posHardwareSaleSetOptions } from "../model/pos-hardware.js";
19
+ import { closeUnshippedPosHardwareOrders } from "./pos-hardware-closure.js";
19
20
  const LABEL = '[pos-hardware] user eraser';
21
+ /** The closure's counts where nothing was unshipped. */ const NO_CLOSURE = {
22
+ hardwareOrdersCanceled: 0,
23
+ hardwareOrdersPlacedWithStripe: 0,
24
+ hardwareRefundsFailed: 0
25
+ };
20
26
  export function createPosHardwareUserEraser(deps) {
21
27
  return async ({ uid, orgIds })=>{
22
- var _deps_now;
23
- if (!uid) return {
28
+ var _deps_now, _deps_closeUnshipped;
29
+ if (!uid) return _extends({
24
30
  hardwareOrdersRedacted: 0,
25
31
  formerWorkspacesReached: true
26
- };
32
+ }, NO_CLOSURE);
27
33
  const db = deps.firestore();
28
34
  const now = (_deps_now = deps.now) != null ? _deps_now : Date.now;
35
+ const closeUnshipped = (_deps_closeUnshipped = deps.closeUnshipped) != null ? _deps_closeUnshipped : closeUnshippedPosHardwareOrders;
29
36
  let refs;
30
37
  let formerWorkspacesReached = true;
31
38
  try {
@@ -40,6 +47,19 @@ export function createPosHardwareUserEraser(deps) {
40
47
  refs.push(...placed.docs.map((doc)=>doc.ref));
41
48
  }
42
49
  }
50
+ // Cancel and refund what has not shipped, while each order still names
51
+ // the buyer the email goes to.
52
+ const placed = [];
53
+ for (const ref of refs){
54
+ const order = (await ref.get()).data();
55
+ if (order && order.createdBy === uid && posHardwareRefundable(order.status)) {
56
+ placed.push({
57
+ orderId: ref.id,
58
+ order
59
+ });
60
+ }
61
+ }
62
+ const closure = placed.length ? await closeUnshipped(placed, 'account-closed') : _extends({}, NO_CLOSURE);
43
63
  let hardwareOrdersRedacted = 0;
44
64
  for (const ref of refs){
45
65
  const redacted = await db.runTransaction(async (transaction)=>{
@@ -48,16 +68,20 @@ export function createPosHardwareUserEraser(deps) {
48
68
  // Gone, or already someone else's: nothing of this person is on it.
49
69
  if (!snapshot.exists || !order || order.createdBy !== uid) return false;
50
70
  const patch = posHardwareBuyerErasedPatch(order, now());
71
+ const erased = _extends({}, order, patch);
51
72
  transaction.update(ref, patch);
52
- transaction.set(db.collection(POS_HARDWARE_SALES_COLLECTION).doc(ref.id), posHardwareSaleRecord(ref.id, _extends({}, order, patch)));
73
+ // The record keeps the buyer's name and address (the reseller's
74
+ // records): the erased order no longer has them, so the write leaves
75
+ // the stored ones in place.
76
+ transaction.set(db.collection(POS_HARDWARE_SALES_COLLECTION).doc(ref.id), posHardwareSaleRecord(ref.id, erased), posHardwareSaleSetOptions(erased));
53
77
  return true;
54
78
  });
55
79
  if (redacted) hardwareOrdersRedacted += 1;
56
80
  }
57
- return {
81
+ return _extends({
58
82
  hardwareOrdersRedacted,
59
83
  formerWorkspacesReached
60
- };
84
+ }, closure);
61
85
  };
62
86
  }
63
87
  /** The eraser over the platform's own Firestore, as the declarations register it. */ export const posHardwareUserEraser = (request)=>createPosHardwareUserEraser({
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/server/pos-hardware-user-eraser.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 type {\n PluginUserEraser,\n PluginUserErasureReport,\n} from '@aglyn/aglyn/plugin-manager/plugin-user-erasure'\nimport { firebaseAdmin } from '@aglyn/tenant-data-admin/server/firebase-admin'\nimport {\n POS_HARDWARE_ORDERS_COLLECTION,\n POS_HARDWARE_SALES_COLLECTION,\n posHardwareBuyerErasedPatch,\n posHardwareSaleRecord,\n type PosHardwareOrder,\n} from '../model/pos-hardware'\n\n/*==========================================\n * COMMERCE'S SHARE OF AN ACCOUNT ERASURE: HARDWARE ORDERS (AGL-4038).\n *\n * A hardware order is the workspace's purchase and the platform's sale, so\n * an account erasure keeps it and every amount on it, and takes the BUYER\n * off it: who started it, the email, the name, the phone, the street and the\n * tracking (`posHardwareBuyerErasedPatch`). The country, state and postal\n * code stay, because sales tax is sourced by them. The retained sale record\n * (`posHardwareSales/{orderId}`) never held the person, and is rewritten\n * from the redacted order in the same transaction so the two agree.\n *\n * Found by a COLLECTION-GROUP query on `createdBy`, so an order placed in a\n * workspace the person has since left is reached too — the support-thread\n * redaction's shape (AGL-1971). It needs the `hardwareOrders.createdBy`\n * collection-group override in `cloud/firebase-firestore.indexes.json`; where\n * that index is not yet deployed the query is refused, and the eraser falls\n * back to the workspaces the person belonged to when the erasure began and\n * says so (`formerWorkspacesReached: false`), rather than reporting a clean\n * pass over orders it could not see.\n *\n * Idempotent: a redacted order no longer carries the uid, so a second run\n * finds nothing to do.\n *=========================================*/\n\ntype Firestore = FirebaseFirestore.Firestore\n\nexport interface PosHardwareUserEraserDeps {\n firestore(): Firestore\n now?: () => number\n}\n\nconst LABEL = '[pos-hardware] user eraser'\n\nexport function createPosHardwareUserEraser(deps: PosHardwareUserEraserDeps): PluginUserEraser {\n return async ({ uid, orgIds }): Promise<PluginUserErasureReport> => {\n if (!uid) return { hardwareOrdersRedacted: 0, formerWorkspacesReached: true }\n const db = deps.firestore()\n const now = deps.now ?? Date.now\n\n let refs: FirebaseFirestore.DocumentReference[]\n let formerWorkspacesReached = true\n try {\n const placed = await db.collectionGroup(POS_HARDWARE_ORDERS_COLLECTION).where('createdBy', '==', uid).get()\n refs = placed.docs.map((doc) => doc.ref)\n } catch (error) {\n console.error(`${LABEL}: the collection-group query was refused; reading current workspaces only`, error)\n formerWorkspacesReached = false\n refs = []\n for (const orgId of orgIds) {\n const placed = await db\n .collection('orgs')\n .doc(orgId)\n .collection(POS_HARDWARE_ORDERS_COLLECTION)\n .where('createdBy', '==', uid)\n .get()\n refs.push(...placed.docs.map((doc) => doc.ref))\n }\n }\n\n let hardwareOrdersRedacted = 0\n for (const ref of refs) {\n const redacted = await db.runTransaction(async (transaction) => {\n const snapshot = await transaction.get(ref)\n const order = snapshot.data() as PosHardwareOrder | undefined\n // Gone, or already someone else's: nothing of this person is on it.\n if (!snapshot.exists || !order || order.createdBy !== uid) return false\n const patch = posHardwareBuyerErasedPatch(order, now())\n transaction.update(ref, patch)\n transaction.set(\n db.collection(POS_HARDWARE_SALES_COLLECTION).doc(ref.id),\n posHardwareSaleRecord(ref.id, { ...order, ...patch } as PosHardwareOrder),\n )\n return true\n })\n if (redacted) hardwareOrdersRedacted += 1\n }\n return { hardwareOrdersRedacted, formerWorkspacesReached }\n }\n}\n\n/** The eraser over the platform's own Firestore, as the declarations register it. */\nexport const posHardwareUserEraser: PluginUserEraser = (request) =>\n createPosHardwareUserEraser({ firestore: () => firebaseAdmin.app().firestore() })(request)\n"],"names":["firebaseAdmin","POS_HARDWARE_ORDERS_COLLECTION","POS_HARDWARE_SALES_COLLECTION","posHardwareBuyerErasedPatch","posHardwareSaleRecord","LABEL","createPosHardwareUserEraser","deps","uid","orgIds","hardwareOrdersRedacted","formerWorkspacesReached","db","firestore","now","Date","refs","placed","collectionGroup","where","get","docs","map","doc","ref","error","console","orgId","collection","push","redacted","runTransaction","transaction","snapshot","order","data","exists","createdBy","patch","update","set","id","posHardwareUserEraser","request","app"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC;AAMD,SAASA,aAAa,QAAQ,iDAAgD;AAC9E,SACEC,8BAA8B,EAC9BC,6BAA6B,EAC7BC,2BAA2B,EAC3BC,qBAAqB,QAEhB,2BAAuB;AAiC9B,MAAMC,QAAQ;AAEd,OAAO,SAASC,4BAA4BC,IAA+B;IACzE,OAAO,OAAO,EAAEC,GAAG,EAAEC,MAAM,EAAE;YAGfF;QAFZ,IAAI,CAACC,KAAK,OAAO;YAAEE,wBAAwB;YAAGC,yBAAyB;QAAK;QAC5E,MAAMC,KAAKL,KAAKM,SAAS;QACzB,MAAMC,OAAMP,YAAAA,KAAKO,GAAG,YAARP,YAAYQ,KAAKD,GAAG;QAEhC,IAAIE;QACJ,IAAIL,0BAA0B;QAC9B,IAAI;YACF,MAAMM,SAAS,MAAML,GAAGM,eAAe,CAACjB,gCAAgCkB,KAAK,CAAC,aAAa,MAAMX,KAAKY,GAAG;YACzGJ,OAAOC,OAAOI,IAAI,CAACC,GAAG,CAAC,CAACC,MAAQA,IAAIC,GAAG;QACzC,EAAE,OAAOC,OAAO;YACdC,QAAQD,KAAK,CAAC,GAAGpB,MAAM,yEAAyE,CAAC,EAAEoB;YACnGd,0BAA0B;YAC1BK,OAAO,EAAE;YACT,KAAK,MAAMW,SAASlB,OAAQ;gBAC1B,MAAMQ,SAAS,MAAML,GAClBgB,UAAU,CAAC,QACXL,GAAG,CAACI,OACJC,UAAU,CAAC3B,gCACXkB,KAAK,CAAC,aAAa,MAAMX,KACzBY,GAAG;gBACNJ,KAAKa,IAAI,IAAIZ,OAAOI,IAAI,CAACC,GAAG,CAAC,CAACC,MAAQA,IAAIC,GAAG;YAC/C;QACF;QAEA,IAAId,yBAAyB;QAC7B,KAAK,MAAMc,OAAOR,KAAM;YACtB,MAAMc,WAAW,MAAMlB,GAAGmB,cAAc,CAAC,OAAOC;gBAC9C,MAAMC,WAAW,MAAMD,YAAYZ,GAAG,CAACI;gBACvC,MAAMU,QAAQD,SAASE,IAAI;gBAC3B,oEAAoE;gBACpE,IAAI,CAACF,SAASG,MAAM,IAAI,CAACF,SAASA,MAAMG,SAAS,KAAK7B,KAAK,OAAO;gBAClE,MAAM8B,QAAQnC,4BAA4B+B,OAAOpB;gBACjDkB,YAAYO,MAAM,CAACf,KAAKc;gBACxBN,YAAYQ,GAAG,CACb5B,GAAGgB,UAAU,CAAC1B,+BAA+BqB,GAAG,CAACC,IAAIiB,EAAE,GACvDrC,sBAAsBoB,IAAIiB,EAAE,EAAE,aAAKP,OAAUI;gBAE/C,OAAO;YACT;YACA,IAAIR,UAAUpB,0BAA0B;QAC1C;QACA,OAAO;YAAEA;YAAwBC;QAAwB;IAC3D;AACF;AAEA,mFAAmF,GACnF,OAAO,MAAM+B,wBAA0C,CAACC,UACtDrC,4BAA4B;QAAEO,WAAW,IAAMb,cAAc4C,GAAG,GAAG/B,SAAS;IAAG,GAAG8B,SAAQ"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/server/pos-hardware-user-eraser.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 type {\n PluginUserEraser,\n PluginUserErasureReport,\n} from '@aglyn/aglyn/plugin-manager/plugin-user-erasure'\nimport { firebaseAdmin } from '@aglyn/tenant-data-admin/server/firebase-admin'\nimport {\n POS_HARDWARE_ORDERS_COLLECTION,\n POS_HARDWARE_SALES_COLLECTION,\n posHardwareBuyerErasedPatch,\n posHardwareRefundable,\n posHardwareSaleRecord,\n posHardwareSaleSetOptions,\n type PosHardwareOrder,\n} from '../model/pos-hardware'\nimport { closeUnshippedPosHardwareOrders, type PosHardwareClosureCounts } from './pos-hardware-closure'\n\n/*==========================================\n * COMMERCE'S SHARE OF AN ACCOUNT ERASURE: HARDWARE ORDERS (AGL-4038).\n *\n * A hardware order is the workspace's purchase and the platform's sale, so\n * an account erasure keeps it and every amount on it, and takes the BUYER\n * off it: who started it, the email, the name, the phone, the street and the\n * tracking (`posHardwareBuyerErasedPatch`). The country, state and postal\n * code stay, because sales tax is sourced by them.\n *\n * The retained sale record (`posHardwareSales/{orderId}`) KEEPS the buyer's\n * name and address, and nothing else of theirs: they are the reseller's\n * records Stripe's Terminal Reseller Terms §10 ask for, kept for the\n * record's seven years. It is rewritten from the redacted order in the same\n * transaction, with `posHardwareSaleSetOptions`, which leaves the stored\n * name and address in place; the email, phone and tracking were never on it.\n *\n * Found by a COLLECTION-GROUP query on `createdBy`, so an order placed in a\n * workspace the person has since left is reached too — the support-thread\n * redaction's shape (AGL-1971). It needs the `hardwareOrders.createdBy`\n * collection-group override in `cloud/firebase-firestore.indexes.json`; where\n * that index is not yet deployed the query is refused, and the eraser falls\n * back to the workspaces the person belonged to when the erasure began and\n * says so (`formerWorkspacesReached: false`), rather than reporting a clean\n * pass over orders it could not see.\n *\n * FIRST, AN ORDER THAT HAS NOT SHIPPED IS CANCELED AND REFUNDED (AGL-4038\n * closure). Closing the account that placed it cancels every `paid` or\n * `ordered` order of theirs, refunds it in full and emails them about it\n * (`pos-hardware-closure.ts`), while the order still carries the address the\n * email goes to; only then is the buyer taken off it. A refusal is alerted\n * and counted, and the erasure goes on.\n *\n * Idempotent: a redacted order no longer carries the uid, so a second run\n * finds nothing to do.\n *=========================================*/\n\ntype Firestore = FirebaseFirestore.Firestore\n\nexport interface PosHardwareUserEraserDeps {\n firestore(): Firestore\n now?: () => number\n /** The closure's cancel, replaceable in a spec. */\n closeUnshipped?: typeof closeUnshippedPosHardwareOrders\n}\n\nconst LABEL = '[pos-hardware] user eraser'\n\n/** The closure's counts where nothing was unshipped. */\nconst NO_CLOSURE: PosHardwareClosureCounts = {\n hardwareOrdersCanceled: 0,\n hardwareOrdersPlacedWithStripe: 0,\n hardwareRefundsFailed: 0,\n}\n\nexport function createPosHardwareUserEraser(deps: PosHardwareUserEraserDeps): PluginUserEraser {\n return async ({ uid, orgIds }): Promise<PluginUserErasureReport> => {\n if (!uid) return { hardwareOrdersRedacted: 0, formerWorkspacesReached: true, ...NO_CLOSURE }\n const db = deps.firestore()\n const now = deps.now ?? Date.now\n const closeUnshipped = deps.closeUnshipped ?? closeUnshippedPosHardwareOrders\n\n let refs: FirebaseFirestore.DocumentReference[]\n let formerWorkspacesReached = true\n try {\n const placed = await db.collectionGroup(POS_HARDWARE_ORDERS_COLLECTION).where('createdBy', '==', uid).get()\n refs = placed.docs.map((doc) => doc.ref)\n } catch (error) {\n console.error(`${LABEL}: the collection-group query was refused; reading current workspaces only`, error)\n formerWorkspacesReached = false\n refs = []\n for (const orgId of orgIds) {\n const placed = await db\n .collection('orgs')\n .doc(orgId)\n .collection(POS_HARDWARE_ORDERS_COLLECTION)\n .where('createdBy', '==', uid)\n .get()\n refs.push(...placed.docs.map((doc) => doc.ref))\n }\n }\n\n // Cancel and refund what has not shipped, while each order still names\n // the buyer the email goes to.\n const placed: Array<{ orderId: string; order: PosHardwareOrder }> = []\n for (const ref of refs) {\n const order = (await ref.get()).data() as PosHardwareOrder | undefined\n if (order && order.createdBy === uid && posHardwareRefundable(order.status)) {\n placed.push({ orderId: ref.id, order })\n }\n }\n const closure: PosHardwareClosureCounts = placed.length\n ? await closeUnshipped(placed, 'account-closed')\n : { ...NO_CLOSURE }\n\n let hardwareOrdersRedacted = 0\n for (const ref of refs) {\n const redacted = await db.runTransaction(async (transaction) => {\n const snapshot = await transaction.get(ref)\n const order = snapshot.data() as PosHardwareOrder | undefined\n // Gone, or already someone else's: nothing of this person is on it.\n if (!snapshot.exists || !order || order.createdBy !== uid) return false\n const patch = posHardwareBuyerErasedPatch(order, now())\n const erased = { ...order, ...patch } as PosHardwareOrder\n transaction.update(ref, patch)\n // The record keeps the buyer's name and address (the reseller's\n // records): the erased order no longer has them, so the write leaves\n // the stored ones in place.\n transaction.set(\n db.collection(POS_HARDWARE_SALES_COLLECTION).doc(ref.id),\n posHardwareSaleRecord(ref.id, erased),\n posHardwareSaleSetOptions(erased),\n )\n return true\n })\n if (redacted) hardwareOrdersRedacted += 1\n }\n return { hardwareOrdersRedacted, formerWorkspacesReached, ...closure }\n }\n}\n\n/** The eraser over the platform's own Firestore, as the declarations register it. */\nexport const posHardwareUserEraser: PluginUserEraser = (request) =>\n createPosHardwareUserEraser({ firestore: () => firebaseAdmin.app().firestore() })(request)\n"],"names":["firebaseAdmin","POS_HARDWARE_ORDERS_COLLECTION","POS_HARDWARE_SALES_COLLECTION","posHardwareBuyerErasedPatch","posHardwareRefundable","posHardwareSaleRecord","posHardwareSaleSetOptions","closeUnshippedPosHardwareOrders","LABEL","NO_CLOSURE","hardwareOrdersCanceled","hardwareOrdersPlacedWithStripe","hardwareRefundsFailed","createPosHardwareUserEraser","deps","uid","orgIds","hardwareOrdersRedacted","formerWorkspacesReached","db","firestore","now","Date","closeUnshipped","refs","placed","collectionGroup","where","get","docs","map","doc","ref","error","console","orgId","collection","push","order","data","createdBy","status","orderId","id","closure","length","redacted","runTransaction","transaction","snapshot","exists","patch","erased","update","set","posHardwareUserEraser","request","app"],"mappings":"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"}
@@ -24,6 +24,11 @@ export declare function posHardwareOrderFromSession(session: any, now?: number):
24
24
  /**
25
25
  * Writes the order once, with its retained sale record. `created` is false
26
26
  * when a delivery already did, so the caller tells people only the first time.
27
+ *
28
+ * Once means once by the ID, wherever the order now is: the sale record is
29
+ * top-level and keyed by the order id, and is written with every order, so
30
+ * an in-person order that has since moved to the buyer's workspace
31
+ * (AGL-4038) is found by it and never recorded a second time where it began.
27
32
  */
28
33
  export declare function recordPosHardwareOrder(orderId: string, order: PosHardwareOrder): Promise<{
29
34
  created: boolean;
@@ -15,9 +15,11 @@
15
15
  * limitations under the License.
16
16
  */ import { _ as _extends } from "@swc/helpers/_/_extends";
17
17
  import { firebaseAdmin } from "@aglyn/tenant-data-admin";
18
- import { decodePosHardwareLines, isPosHardwareShippingMethod, POS_HARDWARE_CURRENCY, POS_HARDWARE_METADATA_TYPE, POS_HARDWARE_ORDER_ID, POS_HARDWARE_SHIPPING_LABELS, posHardwareSaleRecord } from "../model/pos-hardware.js";
18
+ import { decodePosHardwareLines, isPosHardwareShippingMethod, POS_HARDWARE_CURRENCY, POS_HARDWARE_METADATA_TYPE, POS_HARDWARE_ORDER_ID, POS_HARDWARE_SHIPPING_LABELS, posHardwareSaleRecord, posHardwareSaleSetOptions } from "../model/pos-hardware.js";
19
19
  import { hardwareOrdersRef, posHardwareSaleRef } from "./pos-hardware.js";
20
20
  import { raisePosHardwareOrderAlert, sendPosHardwareEmail } from "./pos-hardware-notify.js";
21
+ import { posStripe } from "./pos-stripe.js";
22
+ import { stripeTaxBreakdownLines } from "./storefront-tax.js";
21
23
  /*==========================================
22
24
  * A PAID HARDWARE CHECKOUT BECOMES AN ORDER (AGL-4038).
23
25
  *
@@ -36,6 +38,13 @@ import { raisePosHardwareOrderAlert, sendPosHardwareEmail } from "./pos-hardware
36
38
  * NOTHING ON THE WIRE IS TRUSTED FOR WHAT IT NAMES. The lines are re-read
37
39
  * against the catalog; the amounts are Stripe's own totals for the session;
38
40
  * an unreadable session is logged and ignored rather than half-recorded.
41
+ *
42
+ * THE TAX BASE IS KEPT, NEVER INFERRED. Where Stripe Tax computed the tax,
43
+ * the session's breakdown — the base each rate was applied to — is read back
44
+ * (the delivered event does not carry it) and kept on the order and its sale
45
+ * record, because the platform's sales tax return reports that base as its
46
+ * taxable sales. A read that fails records no lines, and the return counts
47
+ * the order as taxed with no stated base rather than as a zero base.
39
48
  *=========================================*/ function addressFrom(raw) {
40
49
  var _raw_line1, _raw_city, _raw_postal_code, _raw_country, _raw_line2, _raw_state;
41
50
  if (!raw || typeof raw !== 'object') return null;
@@ -65,7 +74,7 @@ const cents = (value)=>{
65
74
  };
66
75
  /** The order a paid session describes, or `null` when it is not readable. */ export function posHardwareOrderFromSession(session, now = Date.now()) {
67
76
  var _ref, _metadata_orderId, _metadata_orgId, _metadata_hostId, _ref1, _ref2, _ref3, _metadata_uid, _ref4, _ref5, _ref6, _ref7, _ref8, _ref9, _ref10;
68
- var _session_collected_information, _session_shipping_cost, _session_total_details;
77
+ var _session_collected_information, _session_shipping_cost, _session_total_details, _session_automatic_tax;
69
78
  const metadata = (_ref = session == null ? void 0 : session.metadata) != null ? _ref : {};
70
79
  if (metadata.type !== POS_HARDWARE_METADATA_TYPE) return null;
71
80
  const orderId = String((_metadata_orderId = metadata.orderId) != null ? _metadata_orderId : '');
@@ -103,6 +112,10 @@ const cents = (value)=>{
103
112
  taxCents,
104
113
  totalCents,
105
114
  currency: String((_ref8 = session == null ? void 0 : session.currency) != null ? _ref8 : POS_HARDWARE_CURRENCY),
115
+ automaticTax: (session == null ? void 0 : (_session_automatic_tax = session.automatic_tax) == null ? void 0 : _session_automatic_tax.enabled) === true,
116
+ // From whatever breakdown the delivered session carries; the handler
117
+ // reads the expanded one where Stripe Tax ran.
118
+ taxLines: stripeTaxBreakdownLines(session),
106
119
  stripeCheckoutSessionId: String((_ref9 = session == null ? void 0 : session.id) != null ? _ref9 : ''),
107
120
  stripePaymentIntentId: (typeof paymentIntent === 'string' ? paymentIntent : String((_ref10 = paymentIntent == null ? void 0 : paymentIntent.id) != null ? _ref10 : '')) || null,
108
121
  stripeHardwareOrderId: null,
@@ -115,19 +128,51 @@ const cents = (value)=>{
115
128
  }
116
129
  };
117
130
  }
131
+ /**
132
+ * The session with its tax breakdown, which the delivered event does not
133
+ * carry, or the delivered session when the read fails. Never throws: the
134
+ * order is recorded either way.
135
+ */ async function withTaxBreakdown(session) {
136
+ var _ref;
137
+ var _session_automatic_tax, _session_total_details_breakdown, _session_total_details;
138
+ if ((session == null ? void 0 : (_session_automatic_tax = session.automatic_tax) == null ? void 0 : _session_automatic_tax.enabled) !== true) return session;
139
+ if (Array.isArray(session == null ? void 0 : (_session_total_details = session.total_details) == null ? void 0 : (_session_total_details_breakdown = _session_total_details.breakdown) == null ? void 0 : _session_total_details_breakdown.taxes)) return session;
140
+ const id = String((_ref = session == null ? void 0 : session.id) != null ? _ref : '');
141
+ if (!id) return session;
142
+ try {
143
+ var _read_body;
144
+ const read = await posStripe('GET', `checkout/sessions/${encodeURIComponent(id)}`, {
145
+ params: {
146
+ 'expand[]': 'total_details.breakdown'
147
+ }
148
+ });
149
+ return read.ok && ((_read_body = read.body) == null ? void 0 : _read_body.id) === id ? read.body : session;
150
+ } catch (error) {
151
+ console.error('[pos-hardware] the tax breakdown could not be read', id, error);
152
+ return session;
153
+ }
154
+ }
118
155
  /**
119
156
  * Writes the order once, with its retained sale record. `created` is false
120
157
  * when a delivery already did, so the caller tells people only the first time.
158
+ *
159
+ * Once means once by the ID, wherever the order now is: the sale record is
160
+ * top-level and keyed by the order id, and is written with every order, so
161
+ * an in-person order that has since moved to the buyer's workspace
162
+ * (AGL-4038) is found by it and never recorded a second time where it began.
121
163
  */ export async function recordPosHardwareOrder(orderId, order) {
122
164
  const ref = hardwareOrdersRef(order.orgId).doc(orderId);
123
165
  const saleRef = posHardwareSaleRef(orderId);
124
166
  return firebaseAdmin.app().firestore().runTransaction(async (transaction)=>{
125
- const existing = await transaction.get(ref);
126
- if (existing.exists) return {
167
+ const [existing, sale] = await Promise.all([
168
+ transaction.get(ref),
169
+ transaction.get(saleRef)
170
+ ]);
171
+ if (existing.exists || sale.exists) return {
127
172
  created: false
128
173
  };
129
174
  transaction.create(ref, order);
130
- transaction.set(saleRef, posHardwareSaleRecord(orderId, order));
175
+ transaction.set(saleRef, posHardwareSaleRecord(orderId, order), posHardwareSaleSetOptions(order));
131
176
  return {
132
177
  created: true
133
178
  };
@@ -148,7 +193,7 @@ export const posHardwareBillingWebhookHandler = async ({ type, object })=>{
148
193
  hostId
149
194
  } : {});
150
195
  }
151
- const parsed = posHardwareOrderFromSession(object);
196
+ const parsed = posHardwareOrderFromSession(await withTaxBreakdown(object));
152
197
  if (!parsed) {
153
198
  var _ref1;
154
199
  console.error('[pos-hardware] a paid hardware session could not be read', String((_ref1 = object == null ? void 0 : object.id) != null ? _ref1 : ''));
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/server/pos-hardware-webhook.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 type { BillingWebhookHandler } from '@aglyn/aglyn/server'\nimport { firebaseAdmin } from '@aglyn/tenant-data-admin'\nimport {\n decodePosHardwareLines,\n isPosHardwareShippingMethod,\n POS_HARDWARE_CURRENCY,\n POS_HARDWARE_METADATA_TYPE,\n POS_HARDWARE_ORDER_ID,\n POS_HARDWARE_SHIPPING_LABELS,\n posHardwareSaleRecord,\n type PosHardwareAddress,\n type PosHardwareOrder,\n} from '../model/pos-hardware'\nimport { hardwareOrdersRef, posHardwareSaleRef } from './pos-hardware'\nimport { raisePosHardwareOrderAlert, sendPosHardwareEmail } from './pos-hardware-notify'\n\n/*==========================================\n * A PAID HARDWARE CHECKOUT BECOMES AN ORDER (AGL-4038).\n *\n * The platform's Stripe webhook fans every event out to the plugins' handlers\n * (`registerBillingWebhookHandler`); this one self-selects on a\n * `checkout.session.completed` whose metadata says it is a POS hardware\n * order, and writes `orgs/{orgId}/hardwareOrders/{orderId}` and, in the same\n * transaction, the retained sale record `posHardwareSales/{orderId}` that\n * outlives the workspace (see `PosHardwareSaleRecord`).\n *\n * IDEMPOTENT BY THE ORDER ID. The id was fixed when the session was made\n * (from the merchant's attempt key), and the write is a transactional\n * create: a redelivered event finds the document and does nothing, so a\n * replay never makes a second order, a second email or a second alert.\n *\n * NOTHING ON THE WIRE IS TRUSTED FOR WHAT IT NAMES. The lines are re-read\n * against the catalog; the amounts are Stripe's own totals for the session;\n * an unreadable session is logged and ignored rather than half-recorded.\n *=========================================*/\n\nfunction addressFrom(raw: any): PosHardwareAddress | null {\n if (!raw || typeof raw !== 'object') return null\n const line1 = String(raw.line1 ?? '').trim()\n const city = String(raw.city ?? '').trim()\n const postalCode = String(raw.postal_code ?? '').trim()\n const country = String(raw.country ?? '').trim().toUpperCase()\n if (!line1 || !city || !postalCode || !country) return null\n const line2 = String(raw.line2 ?? '').trim()\n const state = String(raw.state ?? '').trim()\n return {\n line1: line1.slice(0, 200),\n ...(line2 ? { line2: line2.slice(0, 200) } : {}),\n city: city.slice(0, 100),\n ...(state ? { state: state.slice(0, 100) } : {}),\n postalCode: postalCode.slice(0, 20),\n country: country.slice(0, 2),\n }\n}\n\nconst cents = (value: unknown): number => {\n const number = Number(value)\n return Number.isSafeInteger(number) && number >= 0 ? number : 0\n}\n\n/** The order a paid session describes, or `null` when it is not readable. */\nexport function posHardwareOrderFromSession(\n session: any,\n now = Date.now(),\n): { orderId: string; order: PosHardwareOrder } | null {\n const metadata = session?.metadata ?? {}\n if (metadata.type !== POS_HARDWARE_METADATA_TYPE) return null\n const orderId = String(metadata.orderId ?? '')\n const orgId = String(metadata.orgId ?? '')\n const hostId = String(metadata.hostId ?? '')\n if (!POS_HARDWARE_ORDER_ID.test(orderId) || !orgId || !hostId) return null\n const lines = decodePosHardwareLines(metadata.lines)\n if (!lines) return null\n const method = isPosHardwareShippingMethod(metadata.shippingMethod) ? metadata.shippingMethod : 'standard'\n // Where the newer API versions keep the shipping details, then the older.\n const shippingDetails =\n session?.collected_information?.shipping_details ?? session?.shipping_details ?? null\n const customer = session?.customer_details ?? {}\n const subtotalCents = lines.reduce((sum, line) => sum + line.lineCents, 0)\n const shippingCents =\n session?.shipping_cost?.amount_total != null\n ? cents(session.shipping_cost.amount_total)\n : cents(metadata.shippingCents)\n const taxCents = cents(session?.total_details?.amount_tax)\n const totalCents =\n session?.amount_total != null ? cents(session.amount_total) : subtotalCents + shippingCents + taxCents\n const paymentIntent = session?.payment_intent\n return {\n orderId,\n order: {\n orgId,\n hostId,\n createdBy: String(metadata.uid ?? ''),\n contactEmail: String(customer?.email ?? '').trim() || null,\n lines,\n subtotalCents,\n shipping: {\n method,\n label: POS_HARDWARE_SHIPPING_LABELS[method],\n cents: shippingCents,\n name: String(shippingDetails?.name ?? customer?.name ?? '').trim() || null,\n phone: String(customer?.phone ?? '').trim() || null,\n address: addressFrom(shippingDetails?.address),\n },\n taxCents,\n totalCents,\n currency: String(session?.currency ?? POS_HARDWARE_CURRENCY),\n stripeCheckoutSessionId: String(session?.id ?? ''),\n stripePaymentIntentId:\n (typeof paymentIntent === 'string' ? paymentIntent : String(paymentIntent?.id ?? '')) || null,\n stripeHardwareOrderId: null,\n tracking: null,\n status: 'paid',\n livemode: session?.livemode === true,\n createdAtMs: now,\n updatedAtMs: now,\n paidAtMs: now,\n },\n }\n}\n\n/**\n * Writes the order once, with its retained sale record. `created` is false\n * when a delivery already did, so the caller tells people only the first time.\n */\nexport async function recordPosHardwareOrder(\n orderId: string,\n order: PosHardwareOrder,\n): Promise<{ created: boolean }> {\n const ref = hardwareOrdersRef(order.orgId).doc(orderId)\n const saleRef = posHardwareSaleRef(orderId)\n return firebaseAdmin\n .app()\n .firestore()\n .runTransaction(async (transaction: any) => {\n const existing = await transaction.get(ref)\n if (existing.exists) return { created: false }\n transaction.create(ref, order)\n transaction.set(saleRef, posHardwareSaleRecord(orderId, order))\n return { created: true }\n })\n}\n\nexport const posHardwareBillingWebhookHandler: BillingWebhookHandler = async ({ type, object }) => {\n if (type !== 'checkout.session.completed') return\n if (object?.metadata?.type !== POS_HARDWARE_METADATA_TYPE) return\n const hostId = String(object?.metadata?.hostId ?? '')\n // A session that is not paid holds nothing to fulfil. Card is the only\n // method offered, so payment is settled when the session completes.\n if (object?.mode !== 'payment' || object?.payment_status !== 'paid') {\n return { claimed: true, ...(hostId ? { hostId } : {}) }\n }\n const parsed = posHardwareOrderFromSession(object)\n if (!parsed) {\n console.error('[pos-hardware] a paid hardware session could not be read', String(object?.id ?? ''))\n return { claimed: true, ...(hostId ? { hostId } : {}) }\n }\n // Errors here propagate on purpose: the route answers 500 and Stripe\n // redelivers, and the create above makes the retry safe.\n const { created } = await recordPosHardwareOrder(parsed.orderId, parsed.order)\n if (created) {\n await Promise.all([\n sendPosHardwareEmail('received', parsed.orderId, parsed.order),\n raisePosHardwareOrderAlert(parsed.orderId, parsed.order),\n ])\n }\n return { claimed: true, hostId: parsed.order.hostId }\n}\n"],"names":["firebaseAdmin","decodePosHardwareLines","isPosHardwareShippingMethod","POS_HARDWARE_CURRENCY","POS_HARDWARE_METADATA_TYPE","POS_HARDWARE_ORDER_ID","POS_HARDWARE_SHIPPING_LABELS","posHardwareSaleRecord","hardwareOrdersRef","posHardwareSaleRef","raisePosHardwareOrderAlert","sendPosHardwareEmail","addressFrom","raw","line1","String","trim","city","postalCode","postal_code","country","toUpperCase","line2","state","slice","cents","value","number","Number","isSafeInteger","posHardwareOrderFromSession","session","now","Date","metadata","shippingDetails","type","orderId","orgId","hostId","test","lines","method","shippingMethod","collected_information","shipping_details","customer","customer_details","subtotalCents","reduce","sum","line","lineCents","shippingCents","shipping_cost","amount_total","taxCents","total_details","amount_tax","totalCents","paymentIntent","payment_intent","order","createdBy","uid","contactEmail","email","shipping","label","name","phone","address","currency","stripeCheckoutSessionId","id","stripePaymentIntentId","stripeHardwareOrderId","tracking","status","livemode","createdAtMs","updatedAtMs","paidAtMs","recordPosHardwareOrder","ref","doc","saleRef","app","firestore","runTransaction","transaction","existing","get","exists","created","create","set","posHardwareBillingWebhookHandler","object","mode","payment_status","claimed","parsed","console","error","Promise","all"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/server/pos-hardware-webhook.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 type { BillingWebhookHandler } from '@aglyn/aglyn/server'\nimport { firebaseAdmin } from '@aglyn/tenant-data-admin'\nimport {\n decodePosHardwareLines,\n isPosHardwareShippingMethod,\n POS_HARDWARE_CURRENCY,\n POS_HARDWARE_METADATA_TYPE,\n POS_HARDWARE_ORDER_ID,\n POS_HARDWARE_SHIPPING_LABELS,\n posHardwareSaleRecord,\n posHardwareSaleSetOptions,\n type PosHardwareAddress,\n type PosHardwareOrder,\n} from '../model/pos-hardware'\nimport { hardwareOrdersRef, posHardwareSaleRef } from './pos-hardware'\nimport { raisePosHardwareOrderAlert, sendPosHardwareEmail } from './pos-hardware-notify'\nimport { posStripe } from './pos-stripe'\nimport { stripeTaxBreakdownLines } from './storefront-tax'\n\n/*==========================================\n * A PAID HARDWARE CHECKOUT BECOMES AN ORDER (AGL-4038).\n *\n * The platform's Stripe webhook fans every event out to the plugins' handlers\n * (`registerBillingWebhookHandler`); this one self-selects on a\n * `checkout.session.completed` whose metadata says it is a POS hardware\n * order, and writes `orgs/{orgId}/hardwareOrders/{orderId}` and, in the same\n * transaction, the retained sale record `posHardwareSales/{orderId}` that\n * outlives the workspace (see `PosHardwareSaleRecord`).\n *\n * IDEMPOTENT BY THE ORDER ID. The id was fixed when the session was made\n * (from the merchant's attempt key), and the write is a transactional\n * create: a redelivered event finds the document and does nothing, so a\n * replay never makes a second order, a second email or a second alert.\n *\n * NOTHING ON THE WIRE IS TRUSTED FOR WHAT IT NAMES. The lines are re-read\n * against the catalog; the amounts are Stripe's own totals for the session;\n * an unreadable session is logged and ignored rather than half-recorded.\n *\n * THE TAX BASE IS KEPT, NEVER INFERRED. Where Stripe Tax computed the tax,\n * the session's breakdown — the base each rate was applied to — is read back\n * (the delivered event does not carry it) and kept on the order and its sale\n * record, because the platform's sales tax return reports that base as its\n * taxable sales. A read that fails records no lines, and the return counts\n * the order as taxed with no stated base rather than as a zero base.\n *=========================================*/\n\nfunction addressFrom(raw: any): PosHardwareAddress | null {\n if (!raw || typeof raw !== 'object') return null\n const line1 = String(raw.line1 ?? '').trim()\n const city = String(raw.city ?? '').trim()\n const postalCode = String(raw.postal_code ?? '').trim()\n const country = String(raw.country ?? '').trim().toUpperCase()\n if (!line1 || !city || !postalCode || !country) return null\n const line2 = String(raw.line2 ?? '').trim()\n const state = String(raw.state ?? '').trim()\n return {\n line1: line1.slice(0, 200),\n ...(line2 ? { line2: line2.slice(0, 200) } : {}),\n city: city.slice(0, 100),\n ...(state ? { state: state.slice(0, 100) } : {}),\n postalCode: postalCode.slice(0, 20),\n country: country.slice(0, 2),\n }\n}\n\nconst cents = (value: unknown): number => {\n const number = Number(value)\n return Number.isSafeInteger(number) && number >= 0 ? number : 0\n}\n\n/** The order a paid session describes, or `null` when it is not readable. */\nexport function posHardwareOrderFromSession(\n session: any,\n now = Date.now(),\n): { orderId: string; order: PosHardwareOrder } | null {\n const metadata = session?.metadata ?? {}\n if (metadata.type !== POS_HARDWARE_METADATA_TYPE) return null\n const orderId = String(metadata.orderId ?? '')\n const orgId = String(metadata.orgId ?? '')\n const hostId = String(metadata.hostId ?? '')\n if (!POS_HARDWARE_ORDER_ID.test(orderId) || !orgId || !hostId) return null\n const lines = decodePosHardwareLines(metadata.lines)\n if (!lines) return null\n const method = isPosHardwareShippingMethod(metadata.shippingMethod) ? metadata.shippingMethod : 'standard'\n // Where the newer API versions keep the shipping details, then the older.\n const shippingDetails =\n session?.collected_information?.shipping_details ?? session?.shipping_details ?? null\n const customer = session?.customer_details ?? {}\n const subtotalCents = lines.reduce((sum, line) => sum + line.lineCents, 0)\n const shippingCents =\n session?.shipping_cost?.amount_total != null\n ? cents(session.shipping_cost.amount_total)\n : cents(metadata.shippingCents)\n const taxCents = cents(session?.total_details?.amount_tax)\n const totalCents =\n session?.amount_total != null ? cents(session.amount_total) : subtotalCents + shippingCents + taxCents\n const paymentIntent = session?.payment_intent\n return {\n orderId,\n order: {\n orgId,\n hostId,\n createdBy: String(metadata.uid ?? ''),\n contactEmail: String(customer?.email ?? '').trim() || null,\n lines,\n subtotalCents,\n shipping: {\n method,\n label: POS_HARDWARE_SHIPPING_LABELS[method],\n cents: shippingCents,\n name: String(shippingDetails?.name ?? customer?.name ?? '').trim() || null,\n phone: String(customer?.phone ?? '').trim() || null,\n address: addressFrom(shippingDetails?.address),\n },\n taxCents,\n totalCents,\n currency: String(session?.currency ?? POS_HARDWARE_CURRENCY),\n automaticTax: session?.automatic_tax?.enabled === true,\n // From whatever breakdown the delivered session carries; the handler\n // reads the expanded one where Stripe Tax ran.\n taxLines: stripeTaxBreakdownLines(session),\n stripeCheckoutSessionId: String(session?.id ?? ''),\n stripePaymentIntentId:\n (typeof paymentIntent === 'string' ? paymentIntent : String(paymentIntent?.id ?? '')) || null,\n stripeHardwareOrderId: null,\n tracking: null,\n status: 'paid',\n livemode: session?.livemode === true,\n createdAtMs: now,\n updatedAtMs: now,\n paidAtMs: now,\n },\n }\n}\n\n/**\n * The session with its tax breakdown, which the delivered event does not\n * carry, or the delivered session when the read fails. Never throws: the\n * order is recorded either way.\n */\nasync function withTaxBreakdown(session: any): Promise<any> {\n if (session?.automatic_tax?.enabled !== true) return session\n if (Array.isArray(session?.total_details?.breakdown?.taxes)) return session\n const id = String(session?.id ?? '')\n if (!id) return session\n try {\n const read = await posStripe('GET', `checkout/sessions/${encodeURIComponent(id)}`, {\n params: { 'expand[]': 'total_details.breakdown' },\n })\n return read.ok && read.body?.id === id ? read.body : session\n } catch (error) {\n console.error('[pos-hardware] the tax breakdown could not be read', id, error)\n return session\n }\n}\n\n/**\n * Writes the order once, with its retained sale record. `created` is false\n * when a delivery already did, so the caller tells people only the first time.\n *\n * Once means once by the ID, wherever the order now is: the sale record is\n * top-level and keyed by the order id, and is written with every order, so\n * an in-person order that has since moved to the buyer's workspace\n * (AGL-4038) is found by it and never recorded a second time where it began.\n */\nexport async function recordPosHardwareOrder(\n orderId: string,\n order: PosHardwareOrder,\n): Promise<{ created: boolean }> {\n const ref = hardwareOrdersRef(order.orgId).doc(orderId)\n const saleRef = posHardwareSaleRef(orderId)\n return firebaseAdmin\n .app()\n .firestore()\n .runTransaction(async (transaction: any) => {\n const [existing, sale] = await Promise.all([transaction.get(ref), transaction.get(saleRef)])\n if (existing.exists || sale.exists) return { created: false }\n transaction.create(ref, order)\n transaction.set(saleRef, posHardwareSaleRecord(orderId, order), posHardwareSaleSetOptions(order))\n return { created: true }\n })\n}\n\nexport const posHardwareBillingWebhookHandler: BillingWebhookHandler = async ({ type, object }) => {\n if (type !== 'checkout.session.completed') return\n if (object?.metadata?.type !== POS_HARDWARE_METADATA_TYPE) return\n const hostId = String(object?.metadata?.hostId ?? '')\n // A session that is not paid holds nothing to fulfil. Card is the only\n // method offered, so payment is settled when the session completes.\n if (object?.mode !== 'payment' || object?.payment_status !== 'paid') {\n return { claimed: true, ...(hostId ? { hostId } : {}) }\n }\n const parsed = posHardwareOrderFromSession(await withTaxBreakdown(object))\n if (!parsed) {\n console.error('[pos-hardware] a paid hardware session could not be read', String(object?.id ?? ''))\n return { claimed: true, ...(hostId ? { hostId } : {}) }\n }\n // Errors here propagate on purpose: the route answers 500 and Stripe\n // redelivers, and the create above makes the retry safe.\n const { created } = await recordPosHardwareOrder(parsed.orderId, parsed.order)\n if (created) {\n await Promise.all([\n sendPosHardwareEmail('received', parsed.orderId, parsed.order),\n raisePosHardwareOrderAlert(parsed.orderId, parsed.order),\n ])\n }\n return { claimed: true, hostId: parsed.order.hostId }\n}\n"],"names":["firebaseAdmin","decodePosHardwareLines","isPosHardwareShippingMethod","POS_HARDWARE_CURRENCY","POS_HARDWARE_METADATA_TYPE","POS_HARDWARE_ORDER_ID","POS_HARDWARE_SHIPPING_LABELS","posHardwareSaleRecord","posHardwareSaleSetOptions","hardwareOrdersRef","posHardwareSaleRef","raisePosHardwareOrderAlert","sendPosHardwareEmail","posStripe","stripeTaxBreakdownLines","addressFrom","raw","line1","String","trim","city","postalCode","postal_code","country","toUpperCase","line2","state","slice","cents","value","number","Number","isSafeInteger","posHardwareOrderFromSession","session","now","Date","metadata","shippingDetails","type","orderId","orgId","hostId","test","lines","method","shippingMethod","collected_information","shipping_details","customer","customer_details","subtotalCents","reduce","sum","line","lineCents","shippingCents","shipping_cost","amount_total","taxCents","total_details","amount_tax","totalCents","paymentIntent","payment_intent","order","createdBy","uid","contactEmail","email","shipping","label","name","phone","address","currency","automaticTax","automatic_tax","enabled","taxLines","stripeCheckoutSessionId","id","stripePaymentIntentId","stripeHardwareOrderId","tracking","status","livemode","createdAtMs","updatedAtMs","paidAtMs","withTaxBreakdown","Array","isArray","breakdown","taxes","read","encodeURIComponent","params","ok","body","error","console","recordPosHardwareOrder","ref","doc","saleRef","app","firestore","runTransaction","transaction","existing","sale","Promise","all","get","exists","created","create","set","posHardwareBillingWebhookHandler","object","mode","payment_status","claimed","parsed"],"mappings":"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"}
@@ -15,9 +15,23 @@
15
15
  * limitations under the License.
16
16
  */
17
17
  import type { PluginApiHandler } from '@aglyn/aglyn/server';
18
- import { type PosHardwareConfig, type PosHardwareOrder } from '../model/pos-hardware';
18
+ import { type PosHardwareConfig, type PosHardwareInPersonConfig, type PosHardwareOrder } from '../model/pos-hardware';
19
19
  /** The shop's config from this deployment, or `null` while it is hidden. */
20
20
  export declare function posHardwareConfig(env?: Record<string, string | undefined>): PosHardwareConfig | null;
21
+ /**
22
+ * The platform's own store that sells hardware in person (AGL-4038), or
23
+ * `null` while that is off. Read like the shop's other figures; a store is
24
+ * the seller only when BOTH its id and its workspace's match, so a site moved
25
+ * to another workspace stops selling hardware rather than selling it for them.
26
+ */
27
+ export declare function posHardwareInPersonConfig(env?: Record<string, string | undefined>): PosHardwareInPersonConfig | null;
28
+ /** Whether a store, owned by `orgId`, is the platform's own hardware seller. */
29
+ export declare function isPosHardwareSellerStore(config: PosHardwareInPersonConfig | null, hostId: string, orgId: string): config is PosHardwareInPersonConfig;
30
+ /**
31
+ * An in-person order's id, from the register sale it came from: one register
32
+ * sale is one hardware order however often its completion is told.
33
+ */
34
+ export declare function posHardwareInPersonOrderId(hostId: string, registerOrderId: string): string;
21
35
  /** The order's id, derived from the attempt so a repeated press is one order. */
22
36
  export declare function posHardwareOrderId(orgId: string, hostId: string, attemptKey: string): string;
23
37
  /** The number a person reads: `HW-` and six characters of the id. */
@@ -49,10 +63,69 @@ export declare function merchantHardwareOrderView(id: string, order: PosHardware
49
63
  tracking: import("../model/pos-hardware").PosHardwareTracking;
50
64
  createdAtMs: number;
51
65
  updatedAtMs: number;
66
+ source: import("../model/pos-hardware").PosHardwareOrderSource;
67
+ discountCents: number;
68
+ /** Readers still to set up, for an in-person order that took serials. */
69
+ readersToSetUp: number;
70
+ refundedCents: number;
71
+ /** The buyer's return request and what was decided, without who asked. */
72
+ returnRequest: {
73
+ state: "requested" | "refunded" | "declined";
74
+ requestedAtMs: number;
75
+ note: string;
76
+ };
52
77
  };
53
78
  export type MerchantHardwareOrderView = ReturnType<typeof merchantHardwareOrderView>;
54
79
  /** The POS settings page of a site, as an absolute console URL. */
55
80
  export declare function posSettingsUrl(hostId: string): Promise<string>;
56
81
  /** `base` with `query` added before any `#fragment`, then `#hash`. */
57
82
  export declare function withQueryAndHash(base: string, query: string, hash: string): string;
83
+ /**
84
+ * The readers this workspace bought in person that are not set up yet
85
+ * (AGL-4038): whether its plan includes the point of sale, its stores and
86
+ * whether each can take a reader, and each order with what it waits for.
87
+ * Nothing at all while the in-person sale is off.
88
+ */
89
+ export declare function waitingInPersonReaders(orgId: string, hostId: string): Promise<{
90
+ posPlan: boolean;
91
+ stores: {
92
+ hostId: string;
93
+ name: string;
94
+ hasLocation: boolean;
95
+ setupUrl: string;
96
+ }[];
97
+ orders: {
98
+ hostId: string;
99
+ serialsTaken: number;
100
+ id: string;
101
+ number: string;
102
+ status: import("../model/pos-hardware").PosHardwareOrderStatus;
103
+ lines: {
104
+ sku: import("../model/pos-hardware").PosHardwareSkuId;
105
+ name: string;
106
+ quantity: number;
107
+ unitCents: number;
108
+ lineCents: number;
109
+ }[];
110
+ subtotalCents: number;
111
+ shippingCents: number;
112
+ shippingLabel: string;
113
+ taxCents: number;
114
+ totalCents: number;
115
+ tracking: import("../model/pos-hardware").PosHardwareTracking;
116
+ createdAtMs: number;
117
+ updatedAtMs: number;
118
+ source: import("../model/pos-hardware").PosHardwareOrderSource;
119
+ discountCents: number;
120
+ /** Readers still to set up, for an in-person order that took serials. */
121
+ readersToSetUp: number;
122
+ refundedCents: number;
123
+ /** The buyer's return request and what was decided, without who asked. */
124
+ returnRequest: {
125
+ state: "requested" | "refunded" | "declined";
126
+ requestedAtMs: number;
127
+ note: string;
128
+ };
129
+ }[];
130
+ }>;
58
131
  export declare const posHardwareHandler: PluginApiHandler;