@aglyn/plugins-commerce 1.0.0-beta.285 → 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.
- package/README.md +1 -1
- package/package.json +13 -13
- package/src/lib/components/cart.js +25 -12
- package/src/lib/components/cart.js.map +1 -1
- package/src/lib/components/commerce-console-page.js +70 -13
- package/src/lib/components/commerce-console-page.js.map +1 -1
- package/src/lib/components/console/order-detail-dialog.component.js +2 -1
- package/src/lib/components/console/order-detail-dialog.component.js.map +1 -1
- package/src/lib/components/console/payment-methods-card.component.js +1 -1
- package/src/lib/components/console/payment-methods-card.component.js.map +1 -1
- package/src/lib/components/console/pos/pos-cart-panel.component.d.ts +5 -0
- package/src/lib/components/console/pos/pos-cart-panel.component.js +18 -12
- package/src/lib/components/console/pos/pos-cart-panel.component.js.map +1 -1
- package/src/lib/components/console/pos/pos-hardware-api.d.ts +76 -5
- package/src/lib/components/console/pos/pos-hardware-api.js +53 -11
- package/src/lib/components/console/pos/pos-hardware-api.js.map +1 -1
- package/src/lib/components/console/pos/pos-hardware-buyer.component.d.ts +18 -0
- package/src/lib/components/console/pos/pos-hardware-buyer.component.js +629 -0
- package/src/lib/components/console/pos/pos-hardware-buyer.component.js.map +1 -0
- package/src/lib/components/console/pos/pos-hardware-sale-api.d.ts +162 -0
- package/src/lib/components/console/pos/pos-hardware-sale-api.js +122 -0
- package/src/lib/components/console/pos/pos-hardware-sale-api.js.map +1 -0
- package/src/lib/components/console/pos/pos-hardware-sale-terms.component.d.ts +39 -0
- package/src/lib/components/console/pos/pos-hardware-sale-terms.component.js +251 -0
- package/src/lib/components/console/pos/pos-hardware-sale-terms.component.js.map +1 -0
- package/src/lib/components/console/pos/use-pos-display.js +50 -0
- package/src/lib/components/console/pos/use-pos-display.js.map +1 -1
- package/src/lib/components/console/pos-cellular-control.component.d.ts +41 -0
- package/src/lib/components/console/pos-cellular-control.component.js +211 -0
- package/src/lib/components/console/pos-cellular-control.component.js.map +1 -0
- package/src/lib/components/console/pos-devices-card.component.d.ts +9 -1
- package/src/lib/components/console/pos-devices-card.component.js +47 -15
- package/src/lib/components/console/pos-devices-card.component.js.map +1 -1
- package/src/lib/components/console/pos-display/pos-display-api.d.ts +6 -0
- package/src/lib/components/console/pos-display/pos-display-api.js +8 -1
- package/src/lib/components/console/pos-display/pos-display-api.js.map +1 -1
- package/src/lib/components/console/pos-display-page.component.d.ts +13 -0
- package/src/lib/components/console/pos-display-page.component.js +44 -2
- package/src/lib/components/console/pos-display-page.component.js.map +1 -1
- package/src/lib/components/console/pos-hardware-card.component.d.ts +12 -4
- package/src/lib/components/console/pos-hardware-card.component.js +157 -36
- package/src/lib/components/console/pos-hardware-card.component.js.map +1 -1
- package/src/lib/components/console/pos-hardware-orders-staff-page.component.js +360 -80
- package/src/lib/components/console/pos-hardware-orders-staff-page.component.js.map +1 -1
- package/src/lib/components/console/pos-hardware-terms-page.component.d.ts +34 -0
- package/src/lib/components/console/pos-hardware-terms-page.component.js +257 -0
- package/src/lib/components/console/pos-hardware-terms-page.component.js.map +1 -0
- package/src/lib/components/console/pos-hardware-terms-sign-page.component.d.ts +27 -0
- package/src/lib/components/console/pos-hardware-terms-sign-page.component.js +104 -0
- package/src/lib/components/console/pos-hardware-terms-sign-page.component.js.map +1 -0
- package/src/lib/components/console/pos-hardware-waiting-card.component.d.ts +24 -0
- package/src/lib/components/console/pos-hardware-waiting-card.component.js +205 -0
- package/src/lib/components/console/pos-hardware-waiting-card.component.js.map +1 -0
- package/src/lib/components/console/pos-ops/pos-check-in-dialog.component.js +297 -201
- package/src/lib/components/console/pos-ops/pos-check-in-dialog.component.js.map +1 -1
- package/src/lib/components/console/pos-ops/pos-check-ins-card.component.d.ts +3 -0
- package/src/lib/components/console/pos-ops/pos-check-ins-card.component.js +260 -92
- package/src/lib/components/console/pos-ops/pos-check-ins-card.component.js.map +1 -1
- package/src/lib/components/console/pos-ops/pos-operations-bar.component.d.ts +9 -0
- package/src/lib/components/console/pos-ops/pos-operations-bar.component.js +15 -6
- package/src/lib/components/console/pos-ops/pos-operations-bar.component.js.map +1 -1
- package/src/lib/components/console/pos-ops/pos-rain-check-dialog.component.d.ts +25 -0
- package/src/lib/components/console/pos-ops/pos-rain-check-dialog.component.js +136 -0
- package/src/lib/components/console/pos-ops/pos-rain-check-dialog.component.js.map +1 -0
- package/src/lib/components/console/pos-ops/pos-shift-panel.component.d.ts +6 -0
- package/src/lib/components/console/pos-ops/pos-shift-panel.component.js +48 -31
- package/src/lib/components/console/pos-ops/pos-shift-panel.component.js.map +1 -1
- package/src/lib/components/console/pos-page.component.d.ts +1 -1
- package/src/lib/components/console/pos-page.component.js +63 -12
- package/src/lib/components/console/pos-page.component.js.map +1 -1
- package/src/lib/components/console/pos-reader-splash-setting.component.js +1 -1
- package/src/lib/components/console/pos-reader-splash-setting.component.js.map +1 -1
- package/src/lib/components/console/product-check-in-fields.component.d.ts +4 -2
- package/src/lib/components/console/product-check-in-fields.component.js +60 -14
- package/src/lib/components/console/product-check-in-fields.component.js.map +1 -1
- package/src/lib/components/console/product-editor-dialog.component.js +44 -1
- package/src/lib/components/console/product-editor-dialog.component.js.map +1 -1
- package/src/lib/components/console/product-hardware-sku-field.component.d.ts +19 -0
- package/src/lib/components/console/product-hardware-sku-field.component.js +92 -0
- package/src/lib/components/console/product-hardware-sku-field.component.js.map +1 -0
- package/src/lib/components/console/product-kiosk-offer-field.component.d.ts +22 -0
- package/src/lib/components/console/product-kiosk-offer-field.component.js +94 -0
- package/src/lib/components/console/product-kiosk-offer-field.component.js.map +1 -0
- package/src/lib/components/console/product-register-fields.component.d.ts +4 -0
- package/src/lib/components/console/product-register-fields.component.js +31 -2
- package/src/lib/components/console/product-register-fields.component.js.map +1 -1
- package/src/lib/components/console/products-hub-card.component.js +11 -2
- package/src/lib/components/console/products-hub-card.component.js.map +1 -1
- package/src/lib/components/console/register-hardware-card.component.d.ts +11 -0
- package/src/lib/components/console/register-hardware-card.component.js +56 -27
- package/src/lib/components/console/register-hardware-card.component.js.map +1 -1
- package/src/lib/components/console/reservation-guests-dialog.component.d.ts +28 -0
- package/src/lib/components/console/reservation-guests-dialog.component.js +189 -0
- package/src/lib/components/console/reservation-guests-dialog.component.js.map +1 -0
- package/src/lib/components/console/reservations-card.component.d.ts +9 -0
- package/src/lib/components/console/reservations-card.component.js +158 -23
- package/src/lib/components/console/reservations-card.component.js.map +1 -1
- package/src/lib/components/console/shippingeasy-card.component.js +1 -1
- package/src/lib/components/console/shippingeasy-card.component.js.map +1 -1
- package/src/lib/components/console/shipstation-card.component.d.ts +1 -1
- package/src/lib/components/console/shipstation-card.component.js +3 -3
- package/src/lib/components/console/shipstation-card.component.js.map +1 -1
- package/src/lib/components/product-detail.d.ts +4 -0
- package/src/lib/components/product-detail.js +40 -5
- package/src/lib/components/product-detail.js.map +1 -1
- package/src/lib/components/product-reviews.js +28 -11
- package/src/lib/components/product-reviews.js.map +1 -1
- package/src/lib/components/reservation-widget.js +28 -5
- package/src/lib/components/reservation-widget.js.map +1 -1
- package/src/lib/components/use-day-picker.d.ts +25 -0
- package/src/lib/components/use-day-picker.js +57 -0
- package/src/lib/components/use-day-picker.js.map +1 -0
- package/src/lib/constants/operator-alerts.d.ts +55 -0
- package/src/lib/constants/operator-alerts.js +156 -1
- package/src/lib/constants/operator-alerts.js.map +1 -1
- package/src/lib/declarations.console-server.js +54 -1
- package/src/lib/declarations.console-server.js.map +1 -1
- package/src/lib/declarations.d.ts +4 -2
- package/src/lib/declarations.js +11 -4
- package/src/lib/declarations.js.map +1 -1
- package/src/lib/declarations.server.js +9 -0
- package/src/lib/declarations.server.js.map +1 -1
- package/src/lib/model/commerce-cart.d.ts +9 -1
- package/src/lib/model/commerce-cart.js +4 -3
- package/src/lib/model/commerce-cart.js.map +1 -1
- package/src/lib/model/commerce-check-in-product.d.ts +26 -0
- package/src/lib/model/commerce-check-in-product.js +64 -2
- package/src/lib/model/commerce-check-in-product.js.map +1 -1
- package/src/lib/model/commerce-check-in-record.d.ts +127 -0
- package/src/lib/model/commerce-check-in-record.js +36 -0
- package/src/lib/model/commerce-check-in-record.js.map +1 -0
- package/src/lib/model/commerce-check-in.d.ts +91 -87
- package/src/lib/model/commerce-check-in.js +186 -62
- package/src/lib/model/commerce-check-in.js.map +1 -1
- package/src/lib/model/commerce-kiosk-offer.d.ts +31 -0
- package/src/lib/model/commerce-kiosk-offer.js +35 -0
- package/src/lib/model/commerce-kiosk-offer.js.map +1 -0
- package/src/lib/model/commerce-orders.d.ts +76 -0
- package/src/lib/model/commerce-orders.js +6 -0
- package/src/lib/model/commerce-orders.js.map +1 -1
- package/src/lib/model/commerce-pos-day-report.d.ts +54 -1
- package/src/lib/model/commerce-pos-day-report.js +39 -2
- package/src/lib/model/commerce-pos-day-report.js.map +1 -1
- package/src/lib/model/commerce-pos-offline.d.ts +1 -1
- package/src/lib/model/commerce-pos-offline.js +6 -1
- package/src/lib/model/commerce-pos-offline.js.map +1 -1
- package/src/lib/model/commerce-pos-ops.js +3 -2
- package/src/lib/model/commerce-pos-ops.js.map +1 -1
- package/src/lib/model/commerce-pos.d.ts +36 -3
- package/src/lib/model/commerce-pos.js +14 -6
- package/src/lib/model/commerce-pos.js.map +1 -1
- package/src/lib/model/commerce-receipt.js +3 -2
- package/src/lib/model/commerce-receipt.js.map +1 -1
- package/src/lib/model/commerce-reservations.d.ts +69 -4
- package/src/lib/model/commerce-reservations.js +57 -5
- package/src/lib/model/commerce-reservations.js.map +1 -1
- package/src/lib/model/commerce.d.ts +66 -0
- package/src/lib/model/commerce.js +15 -0
- package/src/lib/model/commerce.js.map +1 -1
- package/src/lib/model/order-list-fields.d.ts +11 -0
- package/src/lib/model/order-list-fields.js +17 -1
- package/src/lib/model/order-list-fields.js.map +1 -1
- package/src/lib/model/own-store-tax-summary.d.ts +152 -0
- package/src/lib/model/own-store-tax-summary.js +288 -0
- package/src/lib/model/own-store-tax-summary.js.map +1 -0
- package/src/lib/model/plugin-permissions.d.ts +67 -0
- package/src/lib/model/plugin-permissions.js +224 -7
- package/src/lib/model/plugin-permissions.js.map +1 -1
- package/src/lib/model/pos-cellular.d.ts +223 -0
- package/src/lib/model/pos-cellular.js +293 -0
- package/src/lib/model/pos-cellular.js.map +1 -0
- package/src/lib/model/pos-hardware-in-person.d.ts +98 -0
- package/src/lib/model/pos-hardware-in-person.js +224 -0
- package/src/lib/model/pos-hardware-in-person.js.map +1 -0
- package/src/lib/model/pos-hardware-tax-summary.d.ts +166 -0
- package/src/lib/model/pos-hardware-tax-summary.js +225 -0
- package/src/lib/model/pos-hardware-tax-summary.js.map +1 -0
- package/src/lib/model/pos-hardware-terms.d.ts +74 -0
- package/src/lib/model/pos-hardware-terms.js +115 -0
- package/src/lib/model/pos-hardware-terms.js.map +1 -0
- package/src/lib/model/pos-hardware.d.ts +349 -7
- package/src/lib/model/pos-hardware.js +291 -20
- package/src/lib/model/pos-hardware.js.map +1 -1
- package/src/lib/model/product-write.d.ts +10 -0
- package/src/lib/model/product-write.js +4 -2
- package/src/lib/model/product-write.js.map +1 -1
- package/src/lib/plugin.js +16 -0
- package/src/lib/plugin.js.map +1 -1
- package/src/lib/server/billing-webhook.js +25 -1
- package/src/lib/server/billing-webhook.js.map +1 -1
- package/src/lib/server/cancel-order.js +7 -5
- package/src/lib/server/cancel-order.js.map +1 -1
- package/src/lib/server/cart-checkout.js +20 -8
- package/src/lib/server/cart-checkout.js.map +1 -1
- package/src/lib/server/cart.js +29 -0
- package/src/lib/server/cart.js.map +1 -1
- package/src/lib/server/checkout.js +42 -13
- package/src/lib/server/checkout.js.map +1 -1
- package/src/lib/server/collection-membership.d.ts +4 -0
- package/src/lib/server/collection-membership.js +5 -10
- package/src/lib/server/collection-membership.js.map +1 -1
- package/src/lib/server/commerce-site-keys.d.ts +29 -0
- package/src/lib/server/commerce-site-keys.js +40 -0
- package/src/lib/server/commerce-site-keys.js.map +1 -0
- package/src/lib/server/draft-order.js +8 -5
- package/src/lib/server/draft-order.js.map +1 -1
- package/src/lib/server/fulfill-order.js +7 -5
- package/src/lib/server/fulfill-order.js.map +1 -1
- package/src/lib/server/gift-cards.js +8 -5
- package/src/lib/server/gift-cards.js.map +1 -1
- package/src/lib/server/local-fulfillment-status.js +9 -6
- package/src/lib/server/local-fulfillment-status.js.map +1 -1
- package/src/lib/server/member-post.js +6 -5
- package/src/lib/server/member-post.js.map +1 -1
- package/src/lib/server/membership-admin-password.d.ts +4 -2
- package/src/lib/server/membership-admin-password.js +10 -8
- package/src/lib/server/membership-admin-password.js.map +1 -1
- package/src/lib/server/membership-admin-remove.d.ts +7 -5
- package/src/lib/server/membership-admin-remove.js +9 -9
- package/src/lib/server/membership-admin-remove.js.map +1 -1
- package/src/lib/server/order-notifications.js +5 -2
- package/src/lib/server/order-notifications.js.map +1 -1
- package/src/lib/server/order-print.js +7 -5
- package/src/lib/server/order-print.js.map +1 -1
- package/src/lib/server/order-receipt-send.js +8 -5
- package/src/lib/server/order-receipt-send.js.map +1 -1
- package/src/lib/server/orders-shipping-prepare.js +9 -6
- package/src/lib/server/orders-shipping-prepare.js.map +1 -1
- package/src/lib/server/own-store-tax-return-source.d.ts +47 -0
- package/src/lib/server/own-store-tax-return-source.js +312 -0
- package/src/lib/server/own-store-tax-return-source.js.map +1 -0
- package/src/lib/server/payment-methods.js +13 -9
- package/src/lib/server/payment-methods.js.map +1 -1
- package/src/lib/server/pos-auth.d.ts +37 -6
- package/src/lib/server/pos-auth.js +40 -15
- package/src/lib/server/pos-auth.js.map +1 -1
- package/src/lib/server/pos-cellular-billing.d.ts +81 -0
- package/src/lib/server/pos-cellular-billing.js +711 -0
- package/src/lib/server/pos-cellular-billing.js.map +1 -0
- package/src/lib/server/pos-cellular-notify.d.ts +47 -0
- package/src/lib/server/pos-cellular-notify.js +150 -0
- package/src/lib/server/pos-cellular-notify.js.map +1 -0
- package/src/lib/server/pos-cellular.d.ts +88 -0
- package/src/lib/server/pos-cellular.js +339 -0
- package/src/lib/server/pos-cellular.js.map +1 -0
- package/src/lib/server/pos-check-in.d.ts +5 -1
- package/src/lib/server/pos-check-in.js +296 -28
- package/src/lib/server/pos-check-in.js.map +1 -1
- package/src/lib/server/pos-day-report.js +29 -3
- package/src/lib/server/pos-day-report.js.map +1 -1
- package/src/lib/server/pos-display.d.ts +16 -0
- package/src/lib/server/pos-display.js +129 -2
- package/src/lib/server/pos-display.js.map +1 -1
- package/src/lib/server/pos-hardware-admin.d.ts +10 -1
- package/src/lib/server/pos-hardware-admin.js +250 -84
- package/src/lib/server/pos-hardware-admin.js.map +1 -1
- package/src/lib/server/pos-hardware-attach.d.ts +105 -0
- package/src/lib/server/pos-hardware-attach.js +443 -0
- package/src/lib/server/pos-hardware-attach.js.map +1 -0
- package/src/lib/server/pos-hardware-buyer-lookup.d.ts +58 -0
- package/src/lib/server/pos-hardware-buyer-lookup.js +180 -0
- package/src/lib/server/pos-hardware-buyer-lookup.js.map +1 -0
- package/src/lib/server/pos-hardware-buyer-record.d.ts +29 -0
- package/src/lib/server/pos-hardware-buyer-record.js +86 -0
- package/src/lib/server/pos-hardware-buyer-record.js.map +1 -0
- package/src/lib/server/pos-hardware-cancel.d.ts +58 -0
- package/src/lib/server/pos-hardware-cancel.js +147 -0
- package/src/lib/server/pos-hardware-cancel.js.map +1 -0
- package/src/lib/server/pos-hardware-closure-notices.d.ts +27 -0
- package/src/lib/server/pos-hardware-closure-notices.js +85 -0
- package/src/lib/server/pos-hardware-closure-notices.js.map +1 -0
- package/src/lib/server/pos-hardware-closure.d.ts +60 -0
- package/src/lib/server/pos-hardware-closure.js +153 -0
- package/src/lib/server/pos-hardware-closure.js.map +1 -0
- package/src/lib/server/pos-hardware-in-person.d.ts +61 -0
- package/src/lib/server/pos-hardware-in-person.js +236 -0
- package/src/lib/server/pos-hardware-in-person.js.map +1 -0
- package/src/lib/server/pos-hardware-notify.d.ts +40 -2
- package/src/lib/server/pos-hardware-notify.js +195 -14
- package/src/lib/server/pos-hardware-notify.js.map +1 -1
- package/src/lib/server/pos-hardware-readers.d.ts +115 -0
- package/src/lib/server/pos-hardware-readers.js +430 -0
- package/src/lib/server/pos-hardware-readers.js.map +1 -0
- package/src/lib/server/pos-hardware-returns.d.ts +116 -0
- package/src/lib/server/pos-hardware-returns.js +502 -0
- package/src/lib/server/pos-hardware-returns.js.map +1 -0
- package/src/lib/server/pos-hardware-sale-fields.d.ts +28 -0
- package/src/lib/server/pos-hardware-sale-fields.js +44 -0
- package/src/lib/server/pos-hardware-sale-fields.js.map +1 -0
- package/src/lib/server/pos-hardware-sale.d.ts +54 -0
- package/src/lib/server/pos-hardware-sale.js +369 -0
- package/src/lib/server/pos-hardware-sale.js.map +1 -0
- package/src/lib/server/pos-hardware-tax-return-source.d.ts +99 -0
- package/src/lib/server/pos-hardware-tax-return-source.js +462 -0
- package/src/lib/server/pos-hardware-tax-return-source.js.map +1 -0
- package/src/lib/server/pos-hardware-terms.d.ts +64 -0
- package/src/lib/server/pos-hardware-terms.js +154 -0
- package/src/lib/server/pos-hardware-terms.js.map +1 -0
- package/src/lib/server/pos-hardware-user-eraser.d.ts +3 -0
- package/src/lib/server/pos-hardware-user-eraser.js +31 -7
- package/src/lib/server/pos-hardware-user-eraser.js.map +1 -1
- package/src/lib/server/pos-hardware-webhook.d.ts +5 -0
- package/src/lib/server/pos-hardware-webhook.js +51 -6
- package/src/lib/server/pos-hardware-webhook.js.map +1 -1
- package/src/lib/server/pos-hardware.d.ts +74 -1
- package/src/lib/server/pos-hardware.js +175 -10
- package/src/lib/server/pos-hardware.js.map +1 -1
- package/src/lib/server/pos-kiosk.d.ts +2 -0
- package/src/lib/server/pos-kiosk.js +17 -12
- package/src/lib/server/pos-kiosk.js.map +1 -1
- package/src/lib/server/pos-offline-card-order.d.ts +64 -0
- package/src/lib/server/pos-offline-card-order.js +102 -0
- package/src/lib/server/pos-offline-card-order.js.map +1 -0
- package/src/lib/server/pos-offline-card-reconcile.d.ts +67 -1
- package/src/lib/server/pos-offline-card-reconcile.js +215 -18
- package/src/lib/server/pos-offline-card-reconcile.js.map +1 -1
- package/src/lib/server/pos-offline-sync.d.ts +39 -0
- package/src/lib/server/pos-offline-sync.js +190 -55
- package/src/lib/server/pos-offline-sync.js.map +1 -1
- package/src/lib/server/pos-ops-gate.d.ts +15 -4
- package/src/lib/server/pos-ops-gate.js +42 -9
- package/src/lib/server/pos-ops-gate.js.map +1 -1
- package/src/lib/server/pos-ops-routes.js +24 -0
- package/src/lib/server/pos-ops-routes.js.map +1 -1
- package/src/lib/server/pos-order-refund-ids.d.ts +28 -0
- package/src/lib/server/pos-order-refund-ids.js +31 -0
- package/src/lib/server/pos-order-refund-ids.js.map +1 -0
- package/src/lib/server/pos-order.js +52 -49
- package/src/lib/server/pos-order.js.map +1 -1
- package/src/lib/server/pos-payment.js +1 -1
- package/src/lib/server/pos-payment.js.map +1 -1
- package/src/lib/server/pos-reader-splash-admin.js +30 -32
- package/src/lib/server/pos-reader-splash-admin.js.map +1 -1
- package/src/lib/server/pos-reader-splash-images-route.d.ts +17 -0
- package/src/lib/server/pos-reader-splash-images-route.js +113 -0
- package/src/lib/server/pos-reader-splash-images-route.js.map +1 -0
- package/src/lib/server/pos-reader-splash-images.d.ts +51 -0
- package/src/lib/server/pos-reader-splash-images.js +123 -0
- package/src/lib/server/pos-reader-splash-images.js.map +1 -0
- package/src/lib/server/pos-reader-splash.d.ts +68 -1
- package/src/lib/server/pos-reader-splash.js +182 -11
- package/src/lib/server/pos-reader-splash.js.map +1 -1
- package/src/lib/server/pos-readers.d.ts +29 -0
- package/src/lib/server/pos-readers.js +134 -35
- package/src/lib/server/pos-readers.js.map +1 -1
- package/src/lib/server/pos-return.d.ts +10 -0
- package/src/lib/server/pos-return.js +26 -1
- package/src/lib/server/pos-return.js.map +1 -1
- package/src/lib/server/pos-sale.js +6 -1
- package/src/lib/server/pos-sale.js.map +1 -1
- package/src/lib/server/pos-shift.js +11 -0
- package/src/lib/server/pos-shift.js.map +1 -1
- package/src/lib/server/pos-staff-pin.js +25 -20
- package/src/lib/server/pos-staff-pin.js.map +1 -1
- package/src/lib/server/pos-terminal.d.ts +14 -4
- package/src/lib/server/pos-terminal.js +34 -38
- package/src/lib/server/pos-terminal.js.map +1 -1
- package/src/lib/server/printers.d.ts +7 -3
- package/src/lib/server/printers.js +18 -12
- package/src/lib/server/printers.js.map +1 -1
- package/src/lib/server/product-drafts.d.ts +11 -1
- package/src/lib/server/product-drafts.js +12 -8
- package/src/lib/server/product-drafts.js.map +1 -1
- package/src/lib/server/product.d.ts +15 -1
- package/src/lib/server/product.js +16 -3
- package/src/lib/server/product.js.map +1 -1
- package/src/lib/server/products-write.d.ts +26 -2
- package/src/lib/server/products-write.js +19 -7
- package/src/lib/server/products-write.js.map +1 -1
- package/src/lib/server/refund.js +34 -11
- package/src/lib/server/refund.js.map +1 -1
- package/src/lib/server/reservation-availability-query.d.ts +8 -0
- package/src/lib/server/reservation-availability-query.js +11 -1
- package/src/lib/server/reservation-availability-query.js.map +1 -1
- package/src/lib/server/reservation-availability.d.ts +1 -1
- package/src/lib/server/reservation-availability.js +7 -2
- package/src/lib/server/reservation-availability.js.map +1 -1
- package/src/lib/server/reservation-guests.d.ts +32 -0
- package/src/lib/server/reservation-guests.js +285 -0
- package/src/lib/server/reservation-guests.js.map +1 -0
- package/src/lib/server/reservation-walk-in.d.ts +11 -1
- package/src/lib/server/reservation-walk-in.js +56 -12
- package/src/lib/server/reservation-walk-in.js.map +1 -1
- package/src/lib/server/reserve.js +18 -5
- package/src/lib/server/reserve.js.map +1 -1
- package/src/lib/server/returns.js +9 -6
- package/src/lib/server/returns.js.map +1 -1
- package/src/lib/server/reviews.d.ts +22 -2
- package/src/lib/server/reviews.js +88 -20
- package/src/lib/server/reviews.js.map +1 -1
- package/src/lib/server/shipping-connectors.js +10 -11
- package/src/lib/server/shipping-connectors.js.map +1 -1
- package/src/lib/server/site-page-resolver.js +20 -3
- package/src/lib/server/site-page-resolver.js.map +1 -1
- package/src/lib/server/store-day.d.ts +33 -0
- package/src/lib/server/store-day.js +46 -0
- package/src/lib/server/store-day.js.map +1 -0
- package/src/lib/server/store-setup-ai-writer.d.ts +2 -1
- package/src/lib/server/store-setup-ai-writer.js +5 -6
- package/src/lib/server/store-setup-ai-writer.js.map +1 -1
- package/src/lib/server/storefront-tax.d.ts +13 -0
- package/src/lib/server/storefront-tax.js +8 -1
- package/src/lib/server/storefront-tax.js.map +1 -1
- package/src/lib/server/tax-return-source.d.ts +32 -3
- package/src/lib/server/tax-return-source.js +99 -20
- package/src/lib/server/tax-return-source.js.map +1 -1
- package/src/lib/server.js +100 -2
- package/src/lib/server.js.map +1 -1
- package/src/lib/testing/fake-firestore.js +58 -16
- package/src/lib/testing/fake-firestore.js.map +1 -1
- package/src/lib/testing/pos-ops-memory-firestore.js +18 -4
- package/src/lib/testing/pos-ops-memory-firestore.js.map +1 -1
- package/src/lib/testing/site-register-permissions.d.ts +29 -0
- package/src/lib/testing/site-register-permissions.js +38 -0
- package/src/lib/testing/site-register-permissions.js.map +1 -0
- package/src/lib/transfer/tracking.server.js +8 -5
- package/src/lib/transfer/tracking.server.js.map +1 -1
- package/src/lib/utils/add-to-cart.d.ts +2 -0
- package/src/lib/utils/add-to-cart.js +6 -3
- package/src/lib/utils/add-to-cart.js.map +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/server/pos-ops-gate.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport * as Aglyn from '@aglyn/aglyn/server'\nimport type { PluginApiRequest } from '@aglyn/aglyn/server'\nimport { firebaseAdmin, getOrgForHost, getPluginConfig } from '@aglyn/tenant-data-admin'\nimport { findUserByUidAcrossPools } from '@aglyn/tenant-data-admin/server/auth-pools'\nimport { resolveOrgPermissions } from '@aglyn/tenant-runtime/org-permissions'\nimport { createHmac, timingSafeEqual } from 'crypto'\nimport {\n POS_CASHIER_ASSERTION_TTL_MS,\n POS_MANAGER_ASSERTION_TTL_MS,\n} from '../model/commerce-pos-ops'\nimport type { PrintReport } from '../model/commerce-printers'\nimport { posOpsSettings, type PosOpsSettings } from '../pos-ops-config'\nimport { isRefusedIdToken } from '@aglyn/tenant-data-admin/server/id-token-refusal'\n\n/*==========================================\n * WHO MAY RUN THE REGISTER (AGL-3609), for the shift, PIN, customer and\n * return routes.\n *\n * The same two facts the sale route checks: the member's role ON THIS SITE\n * is `admin` or `editor` (the allowlist the Firestore rules enforce), and the\n * organization has not revoked `managePos` from them. Plus the plan's `pos`\n * entitlement, re-read every time because a downgrade happens between taps.\n *\n * Every Firebase and Stripe touch goes through {@link PosOpsDeps}, so the\n * routes' specs run each money path against an in-memory store and a counted\n * fetch without mocking a module.\n *=========================================*/\n\nexport interface PosOpsMembership {\n orgWide: boolean\n hostRole: string | null\n permissions: { managePos?: boolean }\n}\n\nexport interface PosOpsDeps {\n firestore(): FirebaseFirestore.Firestore\n verifyIdToken(token: string): Promise<{ uid: string }>\n membership(uid: string, hostId: string): Promise<PosOpsMembership>\n orgForHost(hostId: string): Promise<{ orgId: string; org: Record<string, any> | null } | null>\n pluginConfig(orgId: string, hostId: string): Promise<Record<string, unknown>>\n /** The plan includes the register. */\n posEntitled(org: Record<string, any> | null): boolean\n /** The HMAC secret staff assertions are signed with; throws when unset. */\n signingSecret(): string\n /** A member's name as a receipt or a report prints it. */\n memberName(uid: string): Promise<string>\n now(): number\n /** Stripe's REST API; `global.fetch` in production. */\n fetch: typeof fetch\n /** The register's cloud printers (AGL-3619): drawer kicks and shift reports. */\n printer: PosOpsPrinter\n}\n\n/** What the register's routes ask of the printer queue. Never throws. */\nexport interface PosOpsPrinter {\n kickDrawer(input: {\n hostId: string\n registerId: string\n reason: 'paid_in' | 'paid_out' | 'drop' | 'cash_refund'\n causeId: string\n createdBy?: string\n }): Promise<{ jobIds: string[] }>\n printReport(input: {\n hostId: string\n registerId: string\n shiftId: string\n report: PrintReport\n createdBy?: string\n attemptKey: string\n }): Promise<{ jobIds: string[] }>\n}\n\nexport function defaultPosOpsDeps(): PosOpsDeps {\n return {\n firestore: () => firebaseAdmin.app().firestore(),\n verifyIdToken: async (token) => {\n const decoded = await firebaseAdmin.app().auth().verifyIdToken(token)\n return { uid: decoded.uid }\n },\n membership: async (uid, hostId) => {\n const resolved = await resolveOrgPermissions(uid, { hostId })\n return {\n orgWide: resolved.orgWide === true,\n hostRole: resolved.hostRole ?? null,\n permissions: { managePos: resolved.permissions?.managePos === true },\n }\n },\n orgForHost: async (hostId) => {\n const found = await getOrgForHost(hostId)\n if (!found) return null\n return {\n orgId: String(found.org?.id ?? found.orgId ?? ''),\n org: (found.org as Record<string, any>) ?? null,\n }\n },\n pluginConfig: async (orgId, hostId) =>\n ((await getPluginConfig(orgId, 'commerce', { hostId })) ?? {}) as Record<string, unknown>,\n posEntitled: (org) => Aglyn.checkEntitlement(org as any, 'pos'),\n signingSecret: () => {\n const secret = process.env.TOKEN_SIGNING_SECRET\n if (!secret) throw new Error('TOKEN_SIGNING_SECRET is not configured')\n return secret\n },\n memberName: async (uid) => {\n // Every pool: a member who signs in through SSO lives in a GCIP tenant\n // pool, not the project one (AGL-1122).\n try {\n const user = (await findUserByUidAcrossPools(uid))?.record\n return user?.displayName || user?.email || 'Staff member'\n } catch {\n return 'Staff member'\n }\n },\n now: () => Date.now(),\n fetch: (input, init) => fetch(input, init),\n // Loaded on use: the printing module reaches the whole sale pipeline,\n // which a shift count or a PIN check never needs.\n printer: {\n kickDrawer: async (input) => (await import('./pos-print')).kickPosDrawer(input),\n printReport: async (input) => (await import('./pos-print')).printPosShiftReport(input),\n },\n }\n}\n\n/** A JSON body, whether the dispatcher parsed it or not. */\nexport function posOpsBody(req: PluginApiRequest): Record<string, any> {\n if (typeof req.body === 'string') {\n try {\n const parsed = JSON.parse(req.body)\n return parsed && typeof parsed === 'object' ? parsed : {}\n } catch {\n return {}\n }\n }\n return req.body && typeof req.body === 'object' ? (req.body as Record<string, any>) : {}\n}\n\n/** The request's idempotency key, either header spelling, bounded. */\nexport function posOpsIdempotencyKey(req: PluginApiRequest): string {\n return String(req.headers['idempotency-key'] ?? req.headers['Idempotency-Key'] ?? '')\n .trim()\n .slice(0, 200)\n}\n\n/** Who is at the register, once the gate admitted them. */\nexport interface PosOpsStaff {\n uid: string\n hostId: string\n orgId: string\n org: Record<string, any> | null\n membership: PosOpsMembership\n /** An admin of the whole workspace: the one role with no refund limit. */\n isManager: boolean\n settings: PosOpsSettings\n hostRef: FirebaseFirestore.DocumentReference\n}\n\nexport type PosOpsGate =\n | { ok: true; staff: PosOpsStaff }\n | { ok: false; status: number; error: string }\n\n/** A site id the dispatcher's own sentinels and path tricks cannot pass for. */\nfunction cleanId(value: unknown): string {\n const id = String(value ?? '').trim()\n return /^[A-Za-z0-9_-]{1,128}$/.test(id) && !/^__.*__$/.test(id) ? id : ''\n}\n\nexport { cleanId as posOpsCleanId }\n\n/** Whether a member may work this site's register, by role and permission. */\nexport function mayWorkRegister(memberRole: unknown, membership: PosOpsMembership): boolean {\n return (memberRole === 'admin' || memberRole === 'editor') && membership.permissions.managePos === true\n}\n\n/**\n * The one role whose say-so a refund above the cashier limit needs: an admin\n * of the whole workspace who is also this site's admin — the role\n * `refund.ts` has always required to refund at all.\n */\nexport function isPosManager(memberRole: unknown, membership: PosOpsMembership): boolean {\n return memberRole === 'admin' && membership.orgWide && membership.hostRole === 'admin'\n}\n\nexport async function authorizePosOps(\n deps: PosOpsDeps,\n req: PluginApiRequest,\n rawHostId: unknown,\n): Promise<PosOpsGate> {\n const authorization = String(req.headers.authorization ?? '')\n const idToken = authorization.startsWith('Bearer ') ? authorization.slice('Bearer '.length) : ''\n if (!idToken) return { ok: false, status: 401, error: 'Unauthenticated' }\n const hostId = cleanId(rawHostId)\n if (!hostId) return { ok: false, status: 400, error: 'Missing hostId' }\n let uid: string\n try {\n uid = (await deps.verifyIdToken(idToken)).uid\n } catch (error) {\n if (!isRefusedIdToken(error)) throw error\n return { ok: false, status: 401, error: 'Unauthenticated' }\n }\n const hostRef = deps.firestore().collection('hosts').doc(hostId)\n const hostSnapshot = await hostRef.get()\n if (!hostSnapshot.exists) return { ok: false, status: 404, error: 'Unknown site' }\n const memberRole = (hostSnapshot.get('memberRoles') ?? {})[uid]\n const membership = await deps.membership(uid, hostId)\n if (!mayWorkRegister(memberRole, membership)) {\n return { ok: false, status: 403, error: 'Not permitted' }\n }\n const owner = await deps.orgForHost(hostId)\n if (!deps.posEntitled(owner?.org ?? null)) {\n return { ok: false, status: 403, error: 'POS requires the Pro plan or above' }\n }\n const settings = posOpsSettings(\n owner?.orgId ? await deps.pluginConfig(owner.orgId, hostId).catch(() => ({})) : {},\n )\n return {\n ok: true,\n staff: {\n uid,\n hostId,\n orgId: owner?.orgId ?? '',\n org: owner?.org ?? null,\n membership,\n isManager: isPosManager(memberRole, membership),\n settings,\n hostRef,\n },\n }\n}\n\n/** A register on this site, or a refusal: a register id from another site is unknown here. */\nexport async function readPosRegister(\n staff: PosOpsStaff,\n rawRegisterId: unknown,\n): Promise<\n | { ok: true; ref: FirebaseFirestore.DocumentReference; data: Record<string, any> }\n | { ok: false; status: number; error: string }\n> {\n const registerId = cleanId(rawRegisterId)\n if (!registerId) return { ok: false, status: 400, error: 'Missing registerId' }\n const ref = staff.hostRef.collection('registers').doc(registerId)\n const snapshot = await ref.get()\n if (!snapshot.exists) return { ok: false, status: 404, error: 'Unknown register' }\n return { ok: true, ref, data: (snapshot.data() ?? {}) as Record<string, any> }\n}\n\n/*==========================================\n * STAFF ASSERTIONS: what a PIN buys.\n *\n * A PIN never signs anybody in. It mints a short-lived, signed statement\n * that one member of THIS site is at THIS register — `{hostId, registerId,\n * memberUid, purpose, expiry}` under an HMAC — and the routes that take one\n * re-check that member's role and `managePos` on every use, so a PIN never\n * carries more than its member holds right now, and a revoked member's\n * assertion stops working on the next tap rather than at expiry.\n *=========================================*/\n\nexport type PosAssertionPurpose = 'cashier' | 'manager'\n\ninterface AssertionClaims {\n v: 1\n h: string\n r: string\n u: string\n p: PosAssertionPurpose\n e: number\n}\n\nfunction signClaims(deps: PosOpsDeps, payload: string): string {\n return createHmac('sha256', deps.signingSecret()).update(`pos-assertion:${payload}`).digest('hex')\n}\n\nexport function mintPosAssertion(\n deps: PosOpsDeps,\n input: { hostId: string; registerId: string; memberUid: string; purpose: PosAssertionPurpose },\n): { token: string; expiresAtMs: number } {\n const expiresAtMs =\n deps.now() +\n (input.purpose === 'manager' ? POS_MANAGER_ASSERTION_TTL_MS : POS_CASHIER_ASSERTION_TTL_MS)\n const claims: AssertionClaims = {\n v: 1,\n h: input.hostId,\n r: input.registerId,\n u: input.memberUid,\n p: input.purpose,\n e: expiresAtMs,\n }\n const payload = Buffer.from(JSON.stringify(claims)).toString('base64url')\n return { token: `${payload}.${signClaims(deps, payload)}`, expiresAtMs }\n}\n\n/**\n * The member an assertion names, or `null` — for a forged, expired, wrong-site,\n * wrong-register or wrong-purpose one alike. It does NOT re-check the member:\n * {@link resolvePosStaffAssertion} does that.\n */\nexport function readPosAssertion(\n deps: PosOpsDeps,\n token: unknown,\n expected: { hostId: string; registerId: string; purpose: PosAssertionPurpose },\n): { memberUid: string; expiresAtMs: number } | null {\n const value = typeof token === 'string' ? token.trim() : ''\n const dot = value.lastIndexOf('.')\n if (dot <= 0 || value.length > 2000) return null\n const payload = value.slice(0, dot)\n const signature = value.slice(dot + 1)\n let expected64: string\n try {\n expected64 = signClaims(deps, payload)\n } catch {\n return null\n }\n const a = Buffer.from(signature)\n const b = Buffer.from(expected64)\n if (a.length !== b.length || !timingSafeEqual(new Uint8Array(a), new Uint8Array(b))) return null\n let claims: AssertionClaims\n try {\n claims = JSON.parse(Buffer.from(payload, 'base64url').toString('utf8'))\n } catch {\n return null\n }\n if (\n claims?.v !== 1 ||\n claims.h !== expected.hostId ||\n claims.r !== expected.registerId ||\n claims.p !== expected.purpose ||\n typeof claims.u !== 'string' ||\n !claims.u ||\n !(Number(claims.e) > deps.now())\n ) {\n return null\n }\n return { memberUid: claims.u, expiresAtMs: Number(claims.e) }\n}\n\n/**\n * The member an assertion puts at the register, re-checked NOW against the\n * site's roles and the organization's permissions — or `null` when the\n * assertion is bad or the member may no longer do what it was minted for.\n */\nexport async function resolvePosStaffAssertion(\n deps: PosOpsDeps,\n hostRef: FirebaseFirestore.DocumentReference,\n token: unknown,\n expected: { hostId: string; registerId: string; purpose: PosAssertionPurpose },\n): Promise<string | null> {\n const read = readPosAssertion(deps, token, expected)\n if (!read) return null\n const host = await hostRef.get()\n const memberRole = (host.get('memberRoles') ?? {})[read.memberUid]\n const membership = await deps.membership(read.memberUid, expected.hostId)\n const allowed =\n expected.purpose === 'manager'\n ? isPosManager(memberRole, membership)\n : mayWorkRegister(memberRole, membership)\n return allowed ? read.memberUid : null\n}\n\n/**\n * Who rang this: the member a valid cashier assertion names, or the member\n * signed in on the device when there is none or it no longer holds. The\n * signed-in member already passed the gate, so the fallback never admits\n * anybody new; it only decides whose name the sale carries.\n */\nexport async function resolvePosCashier(\n deps: PosOpsDeps,\n staff: Pick<PosOpsStaff, 'uid' | 'hostId' | 'hostRef'>,\n registerId: string,\n token: unknown,\n): Promise<{ cashierId: string; via: 'pin' | 'session' }> {\n if (token) {\n const memberUid = await resolvePosStaffAssertion(deps, staff.hostRef, token, {\n hostId: staff.hostId,\n registerId,\n purpose: 'cashier',\n })\n if (memberUid) return { cashierId: memberUid, via: 'pin' }\n }\n return { cashierId: staff.uid, via: 'session' }\n}\n"],"names":["Aglyn","firebaseAdmin","getOrgForHost","getPluginConfig","findUserByUidAcrossPools","resolveOrgPermissions","createHmac","timingSafeEqual","POS_CASHIER_ASSERTION_TTL_MS","POS_MANAGER_ASSERTION_TTL_MS","posOpsSettings","isRefusedIdToken","defaultPosOpsDeps","firestore","app","verifyIdToken","token","decoded","auth","uid","membership","hostId","resolved","orgWide","hostRole","permissions","managePos","orgForHost","found","orgId","String","org","id","pluginConfig","posEntitled","checkEntitlement","signingSecret","secret","process","env","TOKEN_SIGNING_SECRET","Error","memberName","user","record","displayName","email","now","Date","fetch","input","init","printer","kickDrawer","kickPosDrawer","printReport","printPosShiftReport","posOpsBody","req","body","parsed","JSON","parse","posOpsIdempotencyKey","headers","trim","slice","cleanId","value","test","posOpsCleanId","mayWorkRegister","memberRole","isPosManager","authorizePosOps","deps","rawHostId","hostSnapshot","authorization","idToken","startsWith","length","ok","status","error","hostRef","collection","doc","get","exists","owner","settings","catch","staff","isManager","readPosRegister","rawRegisterId","snapshot","registerId","ref","data","signClaims","payload","update","digest","mintPosAssertion","expiresAtMs","purpose","claims","v","h","r","u","memberUid","p","e","Buffer","from","stringify","toString","readPosAssertion","expected","dot","lastIndexOf","signature","expected64","a","b","Uint8Array","Number","resolvePosStaffAssertion","host","read","allowed","resolvePosCashier","cashierId","via"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED,YAAYA,WAAW,sBAAqB;AAE5C,SAASC,aAAa,EAAEC,aAAa,EAAEC,eAAe,QAAQ,2BAA0B;AACxF,SAASC,wBAAwB,QAAQ,6CAA4C;AACrF,SAASC,qBAAqB,QAAQ,wCAAuC;AAC7E,SAASC,UAAU,EAAEC,eAAe,QAAQ,SAAQ;AACpD,SACEC,4BAA4B,EAC5BC,4BAA4B,QACvB,+BAA2B;AAElC,SAASC,cAAc,QAA6B,uBAAmB;AACvE,SAASC,gBAAgB,QAAQ,mDAAkD;AA4DnF,OAAO,SAASC;IACd,OAAO;QACLC,WAAW,IAAMZ,cAAca,GAAG,GAAGD,SAAS;QAC9CE,eAAe,OAAOC;YACpB,MAAMC,UAAU,MAAMhB,cAAca,GAAG,GAAGI,IAAI,GAAGH,aAAa,CAACC;YAC/D,OAAO;gBAAEG,KAAKF,QAAQE,GAAG;YAAC;QAC5B;QACAC,YAAY,OAAOD,KAAKE;gBAIVC;gBACgBA;YAJ5B,MAAMA,WAAW,MAAMjB,sBAAsBc,KAAK;gBAAEE;YAAO;YAC3D,OAAO;gBACLE,SAASD,SAASC,OAAO,KAAK;gBAC9BC,QAAQ,GAAEF,qBAAAA,SAASE,QAAQ,YAAjBF,qBAAqB;gBAC/BG,aAAa;oBAAEC,WAAWJ,EAAAA,wBAAAA,SAASG,WAAW,qBAApBH,sBAAsBI,SAAS,MAAK;gBAAK;YACrE;QACF;QACAC,YAAY,OAAON;gBAIDO,aACRA;gBADQA;YAHhB,MAAMA,QAAQ,MAAM1B,cAAcmB;YAClC,IAAI,CAACO,OAAO,OAAO;YACnB,OAAO;gBACLC,OAAOC,QAAOF,iBAAAA,cAAAA,MAAMG,GAAG,qBAATH,YAAWI,EAAE,oBAAIJ,MAAMC,KAAK,YAA5BD,OAAgC;gBAC9CG,GAAG,GAAGH,aAAAA,MAAMG,GAAG,YAATH,aAAqC;YAC7C;QACF;QACAK,cAAc,OAAOJ,OAAOR;gBACxB;oBAAA,OAAA,MAAMlB,gBAAgB0B,OAAO,YAAY;gBAAER;YAAO,cAAlD,OAAyD,CAAC;;QAC9Da,aAAa,CAACH,MAAQ/B,MAAMmC,gBAAgB,CAACJ,KAAY;QACzDK,eAAe;YACb,MAAMC,SAASC,QAAQC,GAAG,CAACC,oBAAoB;YAC/C,IAAI,CAACH,QAAQ,MAAM,IAAII,MAAM;YAC7B,OAAOJ;QACT;QACAK,YAAY,OAAOvB;YACjB,uEAAuE;YACvE,wCAAwC;YACxC,IAAI;oBACY;gBAAd,MAAMwB,QAAQ,QAAA,MAAMvC,yBAAyBe,yBAAhC,AAAC,MAAsCyB,MAAM;gBAC1D,OAAOD,CAAAA,wBAAAA,KAAME,WAAW,MAAIF,wBAAAA,KAAMG,KAAK,KAAI;YAC7C,EAAE,eAAM;gBACN,OAAO;YACT;QACF;QACAC,KAAK,IAAMC,KAAKD,GAAG;QACnBE,OAAO,CAACC,OAAOC,OAASF,MAAMC,OAAOC;QACrC,sEAAsE;QACtE,kDAAkD;QAClDC,SAAS;YACPC,YAAY,OAAOH,QAAU,AAAC,CAAA,MAAM,MAAM,CAAC,iBAAa,EAAGI,aAAa,CAACJ;YACzEK,aAAa,OAAOL,QAAU,AAAC,CAAA,MAAM,MAAM,CAAC,iBAAa,EAAGM,mBAAmB,CAACN;QAClF;IACF;AACF;AAEA,0DAA0D,GAC1D,OAAO,SAASO,WAAWC,GAAqB;IAC9C,IAAI,OAAOA,IAAIC,IAAI,KAAK,UAAU;QAChC,IAAI;YACF,MAAMC,SAASC,KAAKC,KAAK,CAACJ,IAAIC,IAAI;YAClC,OAAOC,UAAU,OAAOA,WAAW,WAAWA,SAAS,CAAC;QAC1D,EAAE,eAAM;YACN,OAAO,CAAC;QACV;IACF;IACA,OAAOF,IAAIC,IAAI,IAAI,OAAOD,IAAIC,IAAI,KAAK,WAAYD,IAAIC,IAAI,GAA2B,CAAC;AACzF;AAEA,oEAAoE,GACpE,OAAO,SAASI,qBAAqBL,GAAqB;QAC1CA,MAAAA;IAAd,OAAO5B,QAAO4B,QAAAA,8BAAAA,IAAIM,OAAO,CAAC,kBAAkB,YAA9BN,8BAAkCA,IAAIM,OAAO,CAAC,kBAAkB,YAAhEN,OAAoE,IAC/EO,IAAI,GACJC,KAAK,CAAC,GAAG;AACd;AAmBA,8EAA8E,GAC9E,SAASC,QAAQC,KAAc;IAC7B,MAAMpC,KAAKF,OAAOsC,gBAAAA,QAAS,IAAIH,IAAI;IACnC,OAAO,yBAAyBI,IAAI,CAACrC,OAAO,CAAC,WAAWqC,IAAI,CAACrC,MAAMA,KAAK;AAC1E;AAEA,SAASmC,WAAWG,aAAa,GAAE;AAEnC,4EAA4E,GAC5E,OAAO,SAASC,gBAAgBC,UAAmB,EAAEpD,UAA4B;IAC/E,OAAO,AAACoD,CAAAA,eAAe,WAAWA,eAAe,QAAO,KAAMpD,WAAWK,WAAW,CAACC,SAAS,KAAK;AACrG;AAEA;;;;CAIC,GACD,OAAO,SAAS+C,aAAaD,UAAmB,EAAEpD,UAA4B;IAC5E,OAAOoD,eAAe,WAAWpD,WAAWG,OAAO,IAAIH,WAAWI,QAAQ,KAAK;AACjF;AAEA,OAAO,eAAekD,gBACpBC,IAAgB,EAChBjB,GAAqB,EACrBkB,SAAkB;QAEWlB,4BAeTmB;IAfpB,MAAMC,gBAAgBhD,QAAO4B,6BAAAA,IAAIM,OAAO,CAACc,aAAa,YAAzBpB,6BAA6B;IAC1D,MAAMqB,UAAUD,cAAcE,UAAU,CAAC,aAAaF,cAAcZ,KAAK,CAAC,UAAUe,MAAM,IAAI;IAC9F,IAAI,CAACF,SAAS,OAAO;QAAEG,IAAI;QAAOC,QAAQ;QAAKC,OAAO;IAAkB;IACxE,MAAM/D,SAAS8C,QAAQS;IACvB,IAAI,CAACvD,QAAQ,OAAO;QAAE6D,IAAI;QAAOC,QAAQ;QAAKC,OAAO;IAAiB;IACtE,IAAIjE;IACJ,IAAI;QACFA,MAAM,AAAC,CAAA,MAAMwD,KAAK5D,aAAa,CAACgE,QAAO,EAAG5D,GAAG;IAC/C,EAAE,OAAOiE,OAAO;QACd,IAAI,CAACzE,iBAAiByE,QAAQ,MAAMA;QACpC,OAAO;YAAEF,IAAI;YAAOC,QAAQ;YAAKC,OAAO;QAAkB;IAC5D;IACA,MAAMC,UAAUV,KAAK9D,SAAS,GAAGyE,UAAU,CAAC,SAASC,GAAG,CAAClE;IACzD,MAAMwD,eAAe,MAAMQ,QAAQG,GAAG;IACtC,IAAI,CAACX,aAAaY,MAAM,EAAE,OAAO;QAAEP,IAAI;QAAOC,QAAQ;QAAKC,OAAO;IAAe;IACjF,MAAMZ,aAAa,EAACK,oBAAAA,aAAaW,GAAG,CAAC,0BAAjBX,oBAAmC,CAAC,EAAE,CAAC1D,IAAI;IAC/D,MAAMC,aAAa,MAAMuD,KAAKvD,UAAU,CAACD,KAAKE;IAC9C,IAAI,CAACkD,gBAAgBC,YAAYpD,aAAa;QAC5C,OAAO;YAAE8D,IAAI;YAAOC,QAAQ;YAAKC,OAAO;QAAgB;IAC1D;IACA,MAAMM,QAAQ,MAAMf,KAAKhD,UAAU,CAACN;IACpC,IAAI,CAACsD,KAAKzC,WAAW,SAACwD,yBAAAA,MAAO3D,GAAG,mBAAI,OAAO;QACzC,OAAO;YAAEmD,IAAI;YAAOC,QAAQ;YAAKC,OAAO;QAAqC;IAC/E;IACA,MAAMO,WAAWjF,eACfgF,CAAAA,yBAAAA,MAAO7D,KAAK,IAAG,MAAM8C,KAAK1C,YAAY,CAACyD,MAAM7D,KAAK,EAAER,QAAQuE,KAAK,CAAC,IAAO,CAAA,CAAC,CAAA,KAAM,CAAC;IAEnF,OAAO;QACLV,IAAI;QACJW,OAAO;YACL1E;YACAE;YACAQ,KAAK,WAAE6D,yBAAAA,MAAO7D,KAAK,oBAAI;YACvBE,GAAG,WAAE2D,yBAAAA,MAAO3D,GAAG,oBAAI;YACnBX;YACA0E,WAAWrB,aAAaD,YAAYpD;YACpCuE;YACAN;QACF;IACF;AACF;AAEA,4FAA4F,GAC5F,OAAO,eAAeU,gBACpBF,KAAkB,EAClBG,aAAsB;QAUSC;IAL/B,MAAMC,aAAa/B,QAAQ6B;IAC3B,IAAI,CAACE,YAAY,OAAO;QAAEhB,IAAI;QAAOC,QAAQ;QAAKC,OAAO;IAAqB;IAC9E,MAAMe,MAAMN,MAAMR,OAAO,CAACC,UAAU,CAAC,aAAaC,GAAG,CAACW;IACtD,MAAMD,WAAW,MAAME,IAAIX,GAAG;IAC9B,IAAI,CAACS,SAASR,MAAM,EAAE,OAAO;QAAEP,IAAI;QAAOC,QAAQ;QAAKC,OAAO;IAAmB;IACjF,OAAO;QAAEF,IAAI;QAAMiB;QAAKC,IAAI,GAAGH,iBAAAA,SAASG,IAAI,cAAbH,iBAAmB,CAAC;IAA0B;AAC/E;AAwBA,SAASI,WAAW1B,IAAgB,EAAE2B,OAAe;IACnD,OAAOhG,WAAW,UAAUqE,KAAKvC,aAAa,IAAImE,MAAM,CAAC,CAAC,cAAc,EAAED,SAAS,EAAEE,MAAM,CAAC;AAC9F;AAEA,OAAO,SAASC,iBACd9B,IAAgB,EAChBzB,KAA8F;IAE9F,MAAMwD,cACJ/B,KAAK5B,GAAG,KACPG,CAAAA,MAAMyD,OAAO,KAAK,YAAYlG,+BAA+BD,4BAA2B;IAC3F,MAAMoG,SAA0B;QAC9BC,GAAG;QACHC,GAAG5D,MAAM7B,MAAM;QACf0F,GAAG7D,MAAMgD,UAAU;QACnBc,GAAG9D,MAAM+D,SAAS;QAClBC,GAAGhE,MAAMyD,OAAO;QAChBQ,GAAGT;IACL;IACA,MAAMJ,UAAUc,OAAOC,IAAI,CAACxD,KAAKyD,SAAS,CAACV,SAASW,QAAQ,CAAC;IAC7D,OAAO;QAAEvG,OAAO,GAAGsF,QAAQ,CAAC,EAAED,WAAW1B,MAAM2B,UAAU;QAAEI;IAAY;AACzE;AAEA;;;;CAIC,GACD,OAAO,SAASc,iBACd7C,IAAgB,EAChB3D,KAAc,EACdyG,QAA8E;IAE9E,MAAMrD,QAAQ,OAAOpD,UAAU,WAAWA,MAAMiD,IAAI,KAAK;IACzD,MAAMyD,MAAMtD,MAAMuD,WAAW,CAAC;IAC9B,IAAID,OAAO,KAAKtD,MAAMa,MAAM,GAAG,MAAM,OAAO;IAC5C,MAAMqB,UAAUlC,MAAMF,KAAK,CAAC,GAAGwD;IAC/B,MAAME,YAAYxD,MAAMF,KAAK,CAACwD,MAAM;IACpC,IAAIG;IACJ,IAAI;QACFA,aAAaxB,WAAW1B,MAAM2B;IAChC,EAAE,eAAM;QACN,OAAO;IACT;IACA,MAAMwB,IAAIV,OAAOC,IAAI,CAACO;IACtB,MAAMG,IAAIX,OAAOC,IAAI,CAACQ;IACtB,IAAIC,EAAE7C,MAAM,KAAK8C,EAAE9C,MAAM,IAAI,CAAC1E,gBAAgB,IAAIyH,WAAWF,IAAI,IAAIE,WAAWD,KAAK,OAAO;IAC5F,IAAInB;IACJ,IAAI;QACFA,SAAS/C,KAAKC,KAAK,CAACsD,OAAOC,IAAI,CAACf,SAAS,aAAaiB,QAAQ,CAAC;IACjE,EAAE,eAAM;QACN,OAAO;IACT;IACA,IACEX,CAAAA,0BAAAA,OAAQC,CAAC,MAAK,KACdD,OAAOE,CAAC,KAAKW,SAASpG,MAAM,IAC5BuF,OAAOG,CAAC,KAAKU,SAASvB,UAAU,IAChCU,OAAOM,CAAC,KAAKO,SAASd,OAAO,IAC7B,OAAOC,OAAOI,CAAC,KAAK,YACpB,CAACJ,OAAOI,CAAC,IACT,CAAEiB,CAAAA,OAAOrB,OAAOO,CAAC,IAAIxC,KAAK5B,GAAG,EAAC,GAC9B;QACA,OAAO;IACT;IACA,OAAO;QAAEkE,WAAWL,OAAOI,CAAC;QAAEN,aAAauB,OAAOrB,OAAOO,CAAC;IAAE;AAC9D;AAEA;;;;CAIC,GACD,OAAO,eAAee,yBACpBvD,IAAgB,EAChBU,OAA4C,EAC5CrE,KAAc,EACdyG,QAA8E;QAK1DU;IAHpB,MAAMC,OAAOZ,iBAAiB7C,MAAM3D,OAAOyG;IAC3C,IAAI,CAACW,MAAM,OAAO;IAClB,MAAMD,OAAO,MAAM9C,QAAQG,GAAG;IAC9B,MAAMhB,aAAa,EAAC2D,YAAAA,KAAK3C,GAAG,CAAC,0BAAT2C,YAA2B,CAAC,EAAE,CAACC,KAAKnB,SAAS,CAAC;IAClE,MAAM7F,aAAa,MAAMuD,KAAKvD,UAAU,CAACgH,KAAKnB,SAAS,EAAEQ,SAASpG,MAAM;IACxE,MAAMgH,UACJZ,SAASd,OAAO,KAAK,YACjBlC,aAAaD,YAAYpD,cACzBmD,gBAAgBC,YAAYpD;IAClC,OAAOiH,UAAUD,KAAKnB,SAAS,GAAG;AACpC;AAEA;;;;;CAKC,GACD,OAAO,eAAeqB,kBACpB3D,IAAgB,EAChBkB,KAAsD,EACtDK,UAAkB,EAClBlF,KAAc;IAEd,IAAIA,OAAO;QACT,MAAMiG,YAAY,MAAMiB,yBAAyBvD,MAAMkB,MAAMR,OAAO,EAAErE,OAAO;YAC3EK,QAAQwE,MAAMxE,MAAM;YACpB6E;YACAS,SAAS;QACX;QACA,IAAIM,WAAW,OAAO;YAAEsB,WAAWtB;YAAWuB,KAAK;QAAM;IAC3D;IACA,OAAO;QAAED,WAAW1C,MAAM1E,GAAG;QAAEqH,KAAK;IAAU;AAChD"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/server/pos-ops-gate.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport * as Aglyn from '@aglyn/aglyn/server'\nimport type { PluginApiRequest } from '@aglyn/aglyn/server'\nimport { firebaseAdmin, getOrgForHost, getPluginConfig } from '@aglyn/tenant-data-admin'\nimport { findUserByUidAcrossPools } from '@aglyn/tenant-data-admin/server/auth-pools'\nimport { resolveOrgPermissions } from '@aglyn/tenant-runtime/org-permissions'\nimport { createHmac, timingSafeEqual } from 'crypto'\nimport {\n POS_CASHIER_ASSERTION_TTL_MS,\n POS_MANAGER_ASSERTION_TTL_MS,\n} from '../model/commerce-pos-ops'\nimport type { PrintReport } from '../model/commerce-printers'\nimport {\n holdsPosPermission,\n posPermissionRefusal,\n type PosPermission,\n} from '../model/plugin-permissions'\nimport { posOpsSettings, type PosOpsSettings } from '../pos-ops-config'\nimport { isRefusedIdToken } from '@aglyn/tenant-data-admin/server/id-token-refusal'\n\n/*==========================================\n * WHO MAY RUN THE REGISTER (AGL-3609), for the shift, PIN, customer and\n * return routes.\n *\n * The fact the sale route checks: `managePos`, as resolved for THIS site —\n * the member's org role, custom role and overrides for an org-wide member,\n * and the role they hold here and their overrides for a site collaborator.\n * Plus the plan's `pos` entitlement, re-read every time because a downgrade\n * happens between taps.\n *\n * THE KEYS DECIDE, NOT THE SITE ROLE (AGL-4047). This also asked for `admin`\n * or `editor` in the site's `memberRoles`, exactly the set `managePos` is on\n * for by default, so nobody who had the register loses it; a site viewer the\n * workspace grants the register now works it.\n *\n * Every Firebase and Stripe touch goes through {@link PosOpsDeps}, so the\n * routes' specs run each money path against an in-memory store and a counted\n * fetch without mocking a module.\n *=========================================*/\n\nexport interface PosOpsMembership {\n orgWide: boolean\n hostRole: string | null\n /** `managePos`, and the register's finer keys (AGL-4047) as resolved. */\n permissions: { managePos?: boolean } & Partial<Record<PosPermission, boolean>>\n}\n\n/** The register's finer keys a membership carries (AGL-4047). */\nconst POS_PERMISSION_KEYS: readonly PosPermission[] = [\n 'posSell',\n 'posRefund',\n 'posDiscount',\n 'posCashDrawer',\n 'posDevices',\n 'posBuyHardware',\n]\n\nexport interface PosOpsDeps {\n firestore(): FirebaseFirestore.Firestore\n verifyIdToken(token: string): Promise<{ uid: string }>\n membership(uid: string, hostId: string): Promise<PosOpsMembership>\n orgForHost(hostId: string): Promise<{ orgId: string; org: Record<string, any> | null } | null>\n pluginConfig(orgId: string, hostId: string): Promise<Record<string, unknown>>\n /** The plan includes the register. */\n posEntitled(org: Record<string, any> | null): boolean\n /** The HMAC secret staff assertions are signed with; throws when unset. */\n signingSecret(): string\n /** A member's name as a receipt or a report prints it. */\n memberName(uid: string): Promise<string>\n now(): number\n /** Stripe's REST API; `global.fetch` in production. */\n fetch: typeof fetch\n /** The register's cloud printers (AGL-3619): drawer kicks and shift reports. */\n printer: PosOpsPrinter\n}\n\n/** What the register's routes ask of the printer queue. Never throws. */\nexport interface PosOpsPrinter {\n kickDrawer(input: {\n hostId: string\n registerId: string\n reason: 'paid_in' | 'paid_out' | 'drop' | 'cash_refund'\n causeId: string\n createdBy?: string\n }): Promise<{ jobIds: string[] }>\n printReport(input: {\n hostId: string\n registerId: string\n shiftId: string\n report: PrintReport\n createdBy?: string\n attemptKey: string\n }): Promise<{ jobIds: string[] }>\n}\n\nexport function defaultPosOpsDeps(): PosOpsDeps {\n return {\n firestore: () => firebaseAdmin.app().firestore(),\n verifyIdToken: async (token) => {\n const decoded = await firebaseAdmin.app().auth().verifyIdToken(token)\n return { uid: decoded.uid }\n },\n membership: async (uid, hostId) => {\n const resolved = await resolveOrgPermissions(uid, { hostId })\n return {\n orgWide: resolved.orgWide === true,\n hostRole: resolved.hostRole ?? null,\n permissions: {\n managePos: resolved.permissions?.managePos === true,\n // Each finer key as resolved; absent when the map carries none,\n // which `holdsPosPermission` reads as following `managePos`.\n ...Object.fromEntries(\n POS_PERMISSION_KEYS.filter(\n (key) => typeof resolved.permissions?.[key] === 'boolean',\n ).map((key) => [key, resolved.permissions[key] === true]),\n ),\n },\n }\n },\n orgForHost: async (hostId) => {\n const found = await getOrgForHost(hostId)\n if (!found) return null\n return {\n orgId: String(found.org?.id ?? found.orgId ?? ''),\n org: (found.org as Record<string, any>) ?? null,\n }\n },\n pluginConfig: async (orgId, hostId) =>\n ((await getPluginConfig(orgId, 'commerce', { hostId })) ?? {}) as Record<string, unknown>,\n posEntitled: (org) => Aglyn.checkEntitlement(org as any, 'pos'),\n signingSecret: () => {\n const secret = process.env.TOKEN_SIGNING_SECRET\n if (!secret) throw new Error('TOKEN_SIGNING_SECRET is not configured')\n return secret\n },\n memberName: async (uid) => {\n // Every pool: a member who signs in through SSO lives in a GCIP tenant\n // pool, not the project one (AGL-1122).\n try {\n const user = (await findUserByUidAcrossPools(uid))?.record\n return user?.displayName || user?.email || 'Staff member'\n } catch {\n return 'Staff member'\n }\n },\n now: () => Date.now(),\n fetch: (input, init) => fetch(input, init),\n // Loaded on use: the printing module reaches the whole sale pipeline,\n // which a shift count or a PIN check never needs.\n printer: {\n kickDrawer: async (input) => (await import('./pos-print')).kickPosDrawer(input),\n printReport: async (input) => (await import('./pos-print')).printPosShiftReport(input),\n },\n }\n}\n\n/** A JSON body, whether the dispatcher parsed it or not. */\nexport function posOpsBody(req: PluginApiRequest): Record<string, any> {\n if (typeof req.body === 'string') {\n try {\n const parsed = JSON.parse(req.body)\n return parsed && typeof parsed === 'object' ? parsed : {}\n } catch {\n return {}\n }\n }\n return req.body && typeof req.body === 'object' ? (req.body as Record<string, any>) : {}\n}\n\n/** The request's idempotency key, either header spelling, bounded. */\nexport function posOpsIdempotencyKey(req: PluginApiRequest): string {\n return String(req.headers['idempotency-key'] ?? req.headers['Idempotency-Key'] ?? '')\n .trim()\n .slice(0, 200)\n}\n\n/** Who is at the register, once the gate admitted them. */\nexport interface PosOpsStaff {\n uid: string\n hostId: string\n orgId: string\n org: Record<string, any> | null\n membership: PosOpsMembership\n /** An admin of the whole workspace: the one role with no refund limit. */\n isManager: boolean\n settings: PosOpsSettings\n hostRef: FirebaseFirestore.DocumentReference\n}\n\nexport type PosOpsGate =\n | { ok: true; staff: PosOpsStaff }\n | { ok: false; status: number; error: string }\n\n/** A site id the dispatcher's own sentinels and path tricks cannot pass for. */\nfunction cleanId(value: unknown): string {\n const id = String(value ?? '').trim()\n return /^[A-Za-z0-9_-]{1,128}$/.test(id) && !/^__.*__$/.test(id) ? id : ''\n}\n\nexport { cleanId as posOpsCleanId }\n\n/**\n * Whether a member may work this site's register: `managePos`, as resolved\n * for the site (AGL-4047). A finer action also needs its own key:\n * `holdsPosPermission`.\n */\nexport function mayWorkRegister(membership: PosOpsMembership): boolean {\n return membership.permissions.managePos === true\n}\n\n/**\n * The one role whose say-so a refund above the cashier limit needs: an admin\n * of the whole workspace who is also this site's admin — the role\n * `refund.ts` has always required to refund at all.\n */\nexport function isPosManager(memberRole: unknown, membership: PosOpsMembership): boolean {\n return memberRole === 'admin' && membership.orgWide && membership.hostRole === 'admin'\n}\n\n/**\n * The register's gate for one route. `permission` names the finer register\n * key the action needs (AGL-4047) on top of working the register at all; a\n * member without it is answered 403 naming it.\n */\nexport async function authorizePosOps(\n deps: PosOpsDeps,\n req: PluginApiRequest,\n rawHostId: unknown,\n permission?: PosPermission,\n): Promise<PosOpsGate> {\n const authorization = String(req.headers.authorization ?? '')\n const idToken = authorization.startsWith('Bearer ') ? authorization.slice('Bearer '.length) : ''\n if (!idToken) return { ok: false, status: 401, error: 'Unauthenticated' }\n const hostId = cleanId(rawHostId)\n if (!hostId) return { ok: false, status: 400, error: 'Missing hostId' }\n let uid: string\n try {\n uid = (await deps.verifyIdToken(idToken)).uid\n } catch (error) {\n if (!isRefusedIdToken(error)) throw error\n return { ok: false, status: 401, error: 'Unauthenticated' }\n }\n const hostRef = deps.firestore().collection('hosts').doc(hostId)\n const hostSnapshot = await hostRef.get()\n if (!hostSnapshot.exists) return { ok: false, status: 404, error: 'Unknown site' }\n // Still read: whether the member is a workspace admin and this site's\n // admin decides who may approve above the cashier's limits.\n const memberRole = (hostSnapshot.get('memberRoles') ?? {})[uid]\n const membership = await deps.membership(uid, hostId)\n if (!mayWorkRegister(membership)) {\n return { ok: false, status: 403, error: 'Not permitted' }\n }\n if (permission && !holdsPosPermission(membership.permissions, permission)) {\n return { ok: false, status: 403, error: posPermissionRefusal(permission) }\n }\n const owner = await deps.orgForHost(hostId)\n if (!deps.posEntitled(owner?.org ?? null)) {\n return { ok: false, status: 403, error: 'POS requires the Pro plan or above' }\n }\n const settings = posOpsSettings(\n owner?.orgId ? await deps.pluginConfig(owner.orgId, hostId).catch(() => ({})) : {},\n )\n return {\n ok: true,\n staff: {\n uid,\n hostId,\n orgId: owner?.orgId ?? '',\n org: owner?.org ?? null,\n membership,\n isManager: isPosManager(memberRole, membership),\n settings,\n hostRef,\n },\n }\n}\n\n/** A register on this site, or a refusal: a register id from another site is unknown here. */\nexport async function readPosRegister(\n staff: PosOpsStaff,\n rawRegisterId: unknown,\n): Promise<\n | { ok: true; ref: FirebaseFirestore.DocumentReference; data: Record<string, any> }\n | { ok: false; status: number; error: string }\n> {\n const registerId = cleanId(rawRegisterId)\n if (!registerId) return { ok: false, status: 400, error: 'Missing registerId' }\n const ref = staff.hostRef.collection('registers').doc(registerId)\n const snapshot = await ref.get()\n if (!snapshot.exists) return { ok: false, status: 404, error: 'Unknown register' }\n return { ok: true, ref, data: (snapshot.data() ?? {}) as Record<string, any> }\n}\n\n/*==========================================\n * STAFF ASSERTIONS: what a PIN buys.\n *\n * A PIN never signs anybody in. It mints a short-lived, signed statement\n * that one member of THIS site is at THIS register — `{hostId, registerId,\n * memberUid, purpose, expiry}` under an HMAC — and the routes that take one\n * re-check that member's permissions on every use, so a PIN never\n * carries more than its member holds right now, and a revoked member's\n * assertion stops working on the next tap rather than at expiry.\n *=========================================*/\n\nexport type PosAssertionPurpose = 'cashier' | 'manager'\n\ninterface AssertionClaims {\n v: 1\n h: string\n r: string\n u: string\n p: PosAssertionPurpose\n e: number\n}\n\nfunction signClaims(deps: PosOpsDeps, payload: string): string {\n return createHmac('sha256', deps.signingSecret()).update(`pos-assertion:${payload}`).digest('hex')\n}\n\nexport function mintPosAssertion(\n deps: PosOpsDeps,\n input: { hostId: string; registerId: string; memberUid: string; purpose: PosAssertionPurpose },\n): { token: string; expiresAtMs: number } {\n const expiresAtMs =\n deps.now() +\n (input.purpose === 'manager' ? POS_MANAGER_ASSERTION_TTL_MS : POS_CASHIER_ASSERTION_TTL_MS)\n const claims: AssertionClaims = {\n v: 1,\n h: input.hostId,\n r: input.registerId,\n u: input.memberUid,\n p: input.purpose,\n e: expiresAtMs,\n }\n const payload = Buffer.from(JSON.stringify(claims)).toString('base64url')\n return { token: `${payload}.${signClaims(deps, payload)}`, expiresAtMs }\n}\n\n/**\n * The member an assertion names, or `null` — for a forged, expired, wrong-site,\n * wrong-register or wrong-purpose one alike. It does NOT re-check the member:\n * {@link resolvePosStaffAssertion} does that.\n */\nexport function readPosAssertion(\n deps: PosOpsDeps,\n token: unknown,\n expected: { hostId: string; registerId: string; purpose: PosAssertionPurpose },\n): { memberUid: string; expiresAtMs: number } | null {\n const value = typeof token === 'string' ? token.trim() : ''\n const dot = value.lastIndexOf('.')\n if (dot <= 0 || value.length > 2000) return null\n const payload = value.slice(0, dot)\n const signature = value.slice(dot + 1)\n let expected64: string\n try {\n expected64 = signClaims(deps, payload)\n } catch {\n return null\n }\n const a = Buffer.from(signature)\n const b = Buffer.from(expected64)\n if (a.length !== b.length || !timingSafeEqual(new Uint8Array(a), new Uint8Array(b))) return null\n let claims: AssertionClaims\n try {\n claims = JSON.parse(Buffer.from(payload, 'base64url').toString('utf8'))\n } catch {\n return null\n }\n if (\n claims?.v !== 1 ||\n claims.h !== expected.hostId ||\n claims.r !== expected.registerId ||\n claims.p !== expected.purpose ||\n typeof claims.u !== 'string' ||\n !claims.u ||\n !(Number(claims.e) > deps.now())\n ) {\n return null\n }\n return { memberUid: claims.u, expiresAtMs: Number(claims.e) }\n}\n\n/**\n * The member an assertion puts at the register, re-checked NOW against the\n * site's roles and the organization's permissions — or `null` when the\n * assertion is bad or the member may no longer do what it was minted for.\n */\nexport async function resolvePosStaffAssertion(\n deps: PosOpsDeps,\n hostRef: FirebaseFirestore.DocumentReference,\n token: unknown,\n expected: { hostId: string; registerId: string; purpose: PosAssertionPurpose },\n): Promise<string | null> {\n const read = readPosAssertion(deps, token, expected)\n if (!read) return null\n const membership = await deps.membership(read.memberUid, expected.hostId)\n if (expected.purpose !== 'manager') return mayWorkRegister(membership) ? read.memberUid : null\n const host = await hostRef.get()\n const memberRole = (host.get('memberRoles') ?? {})[read.memberUid]\n return isPosManager(memberRole, membership) ? read.memberUid : null\n}\n\n/**\n * Who rang this: the member a valid cashier assertion names, or the member\n * signed in on the device when there is none or it no longer holds. The\n * signed-in member already passed the gate, so the fallback never admits\n * anybody new; it only decides whose name the sale carries.\n */\nexport async function resolvePosCashier(\n deps: PosOpsDeps,\n staff: Pick<PosOpsStaff, 'uid' | 'hostId' | 'hostRef'>,\n registerId: string,\n token: unknown,\n): Promise<{ cashierId: string; via: 'pin' | 'session' }> {\n if (token) {\n const memberUid = await resolvePosStaffAssertion(deps, staff.hostRef, token, {\n hostId: staff.hostId,\n registerId,\n purpose: 'cashier',\n })\n if (memberUid) return { cashierId: memberUid, via: 'pin' }\n }\n return { cashierId: staff.uid, via: 'session' }\n}\n"],"names":["Aglyn","firebaseAdmin","getOrgForHost","getPluginConfig","findUserByUidAcrossPools","resolveOrgPermissions","createHmac","timingSafeEqual","POS_CASHIER_ASSERTION_TTL_MS","POS_MANAGER_ASSERTION_TTL_MS","holdsPosPermission","posPermissionRefusal","posOpsSettings","isRefusedIdToken","POS_PERMISSION_KEYS","defaultPosOpsDeps","firestore","app","verifyIdToken","token","decoded","auth","uid","membership","hostId","resolved","orgWide","hostRole","permissions","managePos","Object","fromEntries","filter","key","map","orgForHost","found","orgId","String","org","id","pluginConfig","posEntitled","checkEntitlement","signingSecret","secret","process","env","TOKEN_SIGNING_SECRET","Error","memberName","user","record","displayName","email","now","Date","fetch","input","init","printer","kickDrawer","kickPosDrawer","printReport","printPosShiftReport","posOpsBody","req","body","parsed","JSON","parse","posOpsIdempotencyKey","headers","trim","slice","cleanId","value","test","posOpsCleanId","mayWorkRegister","isPosManager","memberRole","authorizePosOps","deps","rawHostId","permission","hostSnapshot","authorization","idToken","startsWith","length","ok","status","error","hostRef","collection","doc","get","exists","owner","settings","catch","staff","isManager","readPosRegister","rawRegisterId","snapshot","registerId","ref","data","signClaims","payload","update","digest","mintPosAssertion","expiresAtMs","purpose","claims","v","h","r","u","memberUid","p","e","Buffer","from","stringify","toString","readPosAssertion","expected","dot","lastIndexOf","signature","expected64","a","b","Uint8Array","Number","resolvePosStaffAssertion","host","read","resolvePosCashier","cashierId","via"],"mappings":"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"}
|
|
@@ -18,6 +18,8 @@ import { posCheckInHandler, registerPosSaleCheckIns } from "./pos-check-in.js";
|
|
|
18
18
|
import { posCustomerHandler } from "./pos-customer.js";
|
|
19
19
|
import { posDayReportHandler } from "./pos-day-report.js";
|
|
20
20
|
import { registerPosSalePrinting } from "./pos-print.js";
|
|
21
|
+
import { onPosSaleCompleted } from "./pos-sale.js";
|
|
22
|
+
import { parsePosHardwareInPersonConfig } from "../model/pos-hardware.js";
|
|
21
23
|
import { posReturnHandler } from "./pos-return.js";
|
|
22
24
|
import { posShiftHandler } from "./pos-shift.js";
|
|
23
25
|
import { posStaffPinHandler } from "./pos-staff-pin.js";
|
|
@@ -43,6 +45,28 @@ import { posStaffPinHandler } from "./pos-staff-pin.js";
|
|
|
43
45
|
// A completed sale's receipt and drawer kick on the register's cloud
|
|
44
46
|
// printers (AGL-3619), through the sale-completed seam every path fires.
|
|
45
47
|
registerPosSalePrinting();
|
|
48
|
+
// A card reader sold at the platform's own store's register becomes a
|
|
49
|
+
// hardware order for the buyer's workspace (AGL-4038).
|
|
50
|
+
registerPosSaleHardware();
|
|
51
|
+
}
|
|
52
|
+
let hardwareSubscribed = false;
|
|
53
|
+
/**
|
|
54
|
+
* Records the hardware order of a completed sale on the platform's own
|
|
55
|
+
* store. Every other store's sale is answered from the environment alone,
|
|
56
|
+
* without loading the recorder. Never throws into a sale; a sale this misses
|
|
57
|
+
* stays marked and the To attach list's sweep records it.
|
|
58
|
+
*/ function registerPosSaleHardware() {
|
|
59
|
+
if (hardwareSubscribed) return;
|
|
60
|
+
hardwareSubscribed = true;
|
|
61
|
+
onPosSaleCompleted(async (event)=>{
|
|
62
|
+
var _parsePosHardwareInPersonConfig;
|
|
63
|
+
if (((_parsePosHardwareInPersonConfig = parsePosHardwareInPersonConfig(process.env)) == null ? void 0 : _parsePosHardwareInPersonConfig.sellerHostId) !== event.hostId) return;
|
|
64
|
+
try {
|
|
65
|
+
await (await import("./pos-hardware-in-person.js")).recordCompletedSaleHardware(event);
|
|
66
|
+
} catch (error) {
|
|
67
|
+
console.error('[pos-hardware-in-person] recorder not loaded', event.orderId, error);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
46
70
|
}
|
|
47
71
|
|
|
48
72
|
//# sourceMappingURL=pos-ops-routes.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/server/pos-ops-routes.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { registerPluginApiRoute } from '@aglyn/aglyn/server'\nimport { posCheckInHandler, registerPosSaleCheckIns } from './pos-check-in'\nimport { posCustomerHandler } from './pos-customer'\nimport { posDayReportHandler } from './pos-day-report'\nimport { registerPosSalePrinting } from './pos-print'\nimport { posReturnHandler } from './pos-return'\nimport { posShiftHandler } from './pos-shift'\nimport { posStaffPinHandler } from './pos-staff-pin'\n\n/**\n * The register's operations routes (AGL-3609): shifts and the drawer, staff\n * PINs, the customer lookup and returns, the day totals (AGL-4037), the\n * check-in (AGL-4043), plus the\n * sale printing. Console-auth, so they register on the console surface with\n * the sale route itself.\n */\nexport function registerPosOpsRoutes(): void {\n registerPluginApiRoute('commerce/pos-shift', posShiftHandler)\n registerPluginApiRoute('commerce/pos-staff-pin', posStaffPinHandler)\n registerPluginApiRoute('commerce/pos-customer', posCustomerHandler)\n registerPluginApiRoute('commerce/pos-return', posReturnHandler)\n // One day's or one event's totals across every register, shift and cashier\n // (AGL-4037): a GET, read with the same gate as the shift reports.\n registerPluginApiRoute('commerce/pos-day-report', posDayReportHandler)\n // Check-in at the window (AGL-4043): who is due today, checking them in\n // with a wristband, and the day's list; a register sale's check-ins are\n // written the moment it completes, through the same seam as its printing.\n registerPluginApiRoute('commerce/pos-check-in', posCheckInHandler)\n registerPosSaleCheckIns()\n // A completed sale's receipt and drawer kick on the register's cloud\n // printers (AGL-3619), through the sale-completed seam every path fires.\n registerPosSalePrinting()\n}\n"],"names":["registerPluginApiRoute","posCheckInHandler","registerPosSaleCheckIns","posCustomerHandler","posDayReportHandler","registerPosSalePrinting","posReturnHandler","posShiftHandler","posStaffPinHandler","registerPosOpsRoutes"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED,SAASA,sBAAsB,QAAQ,sBAAqB;AAC5D,SAASC,iBAAiB,EAAEC,uBAAuB,QAAQ,oBAAgB;AAC3E,SAASC,kBAAkB,QAAQ,oBAAgB;AACnD,SAASC,mBAAmB,QAAQ,sBAAkB;AACtD,SAASC,uBAAuB,QAAQ,iBAAa;AACrD,SAASC,gBAAgB,QAAQ,kBAAc;AAC/C,SAASC,eAAe,QAAQ,iBAAa;AAC7C,SAASC,kBAAkB,QAAQ,qBAAiB;AAEpD;;;;;;CAMC,GACD,OAAO,SAASC;
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/server/pos-ops-routes.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { registerPluginApiRoute } from '@aglyn/aglyn/server'\nimport { posCheckInHandler, registerPosSaleCheckIns } from './pos-check-in'\nimport { posCustomerHandler } from './pos-customer'\nimport { posDayReportHandler } from './pos-day-report'\nimport { registerPosSalePrinting } from './pos-print'\nimport { onPosSaleCompleted } from './pos-sale'\nimport { parsePosHardwareInPersonConfig } from '../model/pos-hardware'\nimport { posReturnHandler } from './pos-return'\nimport { posShiftHandler } from './pos-shift'\nimport { posStaffPinHandler } from './pos-staff-pin'\n\n/**\n * The register's operations routes (AGL-3609): shifts and the drawer, staff\n * PINs, the customer lookup and returns, the day totals (AGL-4037), the\n * check-in (AGL-4043), plus the\n * sale printing. Console-auth, so they register on the console surface with\n * the sale route itself.\n */\nexport function registerPosOpsRoutes(): void {\n registerPluginApiRoute('commerce/pos-shift', posShiftHandler)\n registerPluginApiRoute('commerce/pos-staff-pin', posStaffPinHandler)\n registerPluginApiRoute('commerce/pos-customer', posCustomerHandler)\n registerPluginApiRoute('commerce/pos-return', posReturnHandler)\n // One day's or one event's totals across every register, shift and cashier\n // (AGL-4037): a GET, read with the same gate as the shift reports.\n registerPluginApiRoute('commerce/pos-day-report', posDayReportHandler)\n // Check-in at the window (AGL-4043): who is due today, checking them in\n // with a wristband, and the day's list; a register sale's check-ins are\n // written the moment it completes, through the same seam as its printing.\n registerPluginApiRoute('commerce/pos-check-in', posCheckInHandler)\n registerPosSaleCheckIns()\n // A completed sale's receipt and drawer kick on the register's cloud\n // printers (AGL-3619), through the sale-completed seam every path fires.\n registerPosSalePrinting()\n // A card reader sold at the platform's own store's register becomes a\n // hardware order for the buyer's workspace (AGL-4038).\n registerPosSaleHardware()\n}\n\nlet hardwareSubscribed = false\n\n/**\n * Records the hardware order of a completed sale on the platform's own\n * store. Every other store's sale is answered from the environment alone,\n * without loading the recorder. Never throws into a sale; a sale this misses\n * stays marked and the To attach list's sweep records it.\n */\nfunction registerPosSaleHardware(): void {\n if (hardwareSubscribed) return\n hardwareSubscribed = true\n onPosSaleCompleted(async (event) => {\n if (parsePosHardwareInPersonConfig(process.env)?.sellerHostId !== event.hostId) return\n try {\n await (await import('./pos-hardware-in-person')).recordCompletedSaleHardware(event)\n } catch (error) {\n console.error('[pos-hardware-in-person] recorder not loaded', event.orderId, error)\n }\n })\n}\n"],"names":["registerPluginApiRoute","posCheckInHandler","registerPosSaleCheckIns","posCustomerHandler","posDayReportHandler","registerPosSalePrinting","onPosSaleCompleted","parsePosHardwareInPersonConfig","posReturnHandler","posShiftHandler","posStaffPinHandler","registerPosOpsRoutes","registerPosSaleHardware","hardwareSubscribed","event","process","env","sellerHostId","hostId","recordCompletedSaleHardware","error","console","orderId"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED,SAASA,sBAAsB,QAAQ,sBAAqB;AAC5D,SAASC,iBAAiB,EAAEC,uBAAuB,QAAQ,oBAAgB;AAC3E,SAASC,kBAAkB,QAAQ,oBAAgB;AACnD,SAASC,mBAAmB,QAAQ,sBAAkB;AACtD,SAASC,uBAAuB,QAAQ,iBAAa;AACrD,SAASC,kBAAkB,QAAQ,gBAAY;AAC/C,SAASC,8BAA8B,QAAQ,2BAAuB;AACtE,SAASC,gBAAgB,QAAQ,kBAAc;AAC/C,SAASC,eAAe,QAAQ,iBAAa;AAC7C,SAASC,kBAAkB,QAAQ,qBAAiB;AAEpD;;;;;;CAMC,GACD,OAAO,SAASC;IACdX,uBAAuB,sBAAsBS;IAC7CT,uBAAuB,0BAA0BU;IACjDV,uBAAuB,yBAAyBG;IAChDH,uBAAuB,uBAAuBQ;IAC9C,2EAA2E;IAC3E,mEAAmE;IACnER,uBAAuB,2BAA2BI;IAClD,wEAAwE;IACxE,wEAAwE;IACxE,0EAA0E;IAC1EJ,uBAAuB,yBAAyBC;IAChDC;IACA,qEAAqE;IACrE,yEAAyE;IACzEG;IACA,sEAAsE;IACtE,uDAAuD;IACvDO;AACF;AAEA,IAAIC,qBAAqB;AAEzB;;;;;CAKC,GACD,SAASD;IACP,IAAIC,oBAAoB;IACxBA,qBAAqB;IACrBP,mBAAmB,OAAOQ;YACpBP;QAAJ,IAAIA,EAAAA,kCAAAA,+BAA+BQ,QAAQC,GAAG,sBAA1CT,gCAA6CU,YAAY,MAAKH,MAAMI,MAAM,EAAE;QAChF,IAAI;YACF,MAAM,AAAC,CAAA,MAAM,MAAM,CAAC,8BAA0B,EAAGC,2BAA2B,CAACL;QAC/E,EAAE,OAAOM,OAAO;YACdC,QAAQD,KAAK,CAAC,gDAAgDN,MAAMQ,OAAO,EAAEF;QAC/E;IACF;AACF"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* The ids of the register refund records the day report reads
|
|
19
|
+
* (`registers/{registerId}/orderRefunds/{id}`, AGL-4037). Each is derived on
|
|
20
|
+
* purpose, so a replayed write lands on the same record instead of counting
|
|
21
|
+
* a refund twice; the schemes are written here, once, for every writer.
|
|
22
|
+
*/
|
|
23
|
+
/** A refund Stripe reported for a register sale: the charge (else the order) and the refunded total. */
|
|
24
|
+
export declare function posOrderRefundStripeId(chargeOrOrderId: string, refundedCents: number): string;
|
|
25
|
+
/** A refund given from the console's order dialog, keyed by its Stripe claim (else its time). */
|
|
26
|
+
export declare function posOrderRefundDialogId(orderId: string, claimKey: string | number): string;
|
|
27
|
+
/** The hardware part of a register sale refunded through the staff page (AGL-4038), per attempt. */
|
|
28
|
+
export declare function posOrderRefundHardwareId(registerOrderId: string, attempt: number | string): string;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/ /**
|
|
17
|
+
* The ids of the register refund records the day report reads
|
|
18
|
+
* (`registers/{registerId}/orderRefunds/{id}`, AGL-4037). Each is derived on
|
|
19
|
+
* purpose, so a replayed write lands on the same record instead of counting
|
|
20
|
+
* a refund twice; the schemes are written here, once, for every writer.
|
|
21
|
+
*/ /** A refund Stripe reported for a register sale: the charge (else the order) and the refunded total. */ export function posOrderRefundStripeId(chargeOrOrderId, refundedCents) {
|
|
22
|
+
return `stripe-${chargeOrOrderId}-${refundedCents}`;
|
|
23
|
+
}
|
|
24
|
+
/** A refund given from the console's order dialog, keyed by its Stripe claim (else its time). */ export function posOrderRefundDialogId(orderId, claimKey) {
|
|
25
|
+
return `${orderId}-${claimKey}`;
|
|
26
|
+
}
|
|
27
|
+
/** The hardware part of a register sale refunded through the staff page (AGL-4038), per attempt. */ export function posOrderRefundHardwareId(registerOrderId, attempt) {
|
|
28
|
+
return `${registerOrderId}-hw-${attempt}`;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
//# sourceMappingURL=pos-order-refund-ids.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/server/pos-order-refund-ids.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * The ids of the register refund records the day report reads\n * (`registers/{registerId}/orderRefunds/{id}`, AGL-4037). Each is derived on\n * purpose, so a replayed write lands on the same record instead of counting\n * a refund twice; the schemes are written here, once, for every writer.\n */\n\n/** A refund Stripe reported for a register sale: the charge (else the order) and the refunded total. */\nexport function posOrderRefundStripeId(chargeOrOrderId: string, refundedCents: number): string {\n return `stripe-${chargeOrOrderId}-${refundedCents}`\n}\n\n/** A refund given from the console's order dialog, keyed by its Stripe claim (else its time). */\nexport function posOrderRefundDialogId(orderId: string, claimKey: string | number): string {\n return `${orderId}-${claimKey}`\n}\n\n/** The hardware part of a register sale refunded through the staff page (AGL-4038), per attempt. */\nexport function posOrderRefundHardwareId(registerOrderId: string, attempt: number | string): string {\n return `${registerOrderId}-hw-${attempt}`\n}\n"],"names":["posOrderRefundStripeId","chargeOrOrderId","refundedCents","posOrderRefundDialogId","orderId","claimKey","posOrderRefundHardwareId","registerOrderId","attempt"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED;;;;;CAKC,GAED,sGAAsG,GACtG,OAAO,SAASA,uBAAuBC,eAAuB,EAAEC,aAAqB;IACnF,OAAO,CAAC,OAAO,EAAED,gBAAgB,CAAC,EAAEC,eAAe;AACrD;AAEA,+FAA+F,GAC/F,OAAO,SAASC,uBAAuBC,OAAe,EAAEC,QAAyB;IAC/E,OAAO,GAAGD,QAAQ,CAAC,EAAEC,UAAU;AACjC;AAEA,kGAAkG,GAClG,OAAO,SAASC,yBAAyBC,eAAuB,EAAEC,OAAwB;IACxF,OAAO,GAAGD,gBAAgB,IAAI,EAAEC,SAAS;AAC3C"}
|
|
@@ -28,9 +28,11 @@ import { raiseOrderEvent } from "./order-events.js";
|
|
|
28
28
|
import { decrementVariantStock } from "./reserve-stock.js";
|
|
29
29
|
import { holdPromotionSlot, promotionHoldKey, settlePromotionSlot } from "./promotion-hold.js";
|
|
30
30
|
import { posMaxDiscountPct } from "../plugin-config.js";
|
|
31
|
+
import { holdsPosPermission, posPermissionRefusal } from "../model/plugin-permissions.js";
|
|
31
32
|
import { offlineFeeMonthKey } from "./pos-fee-month.js";
|
|
32
33
|
import { notifyPosSaleCompleted } from "./pos-sale.js";
|
|
33
34
|
import { posSaleStamp } from "./pos-sale-stamp.js";
|
|
35
|
+
import { posHardwareSaleFields } from "./pos-hardware-sale-fields.js";
|
|
34
36
|
import { quoteSaleTaxWithEngine } from "./tax-engine-quote.js";
|
|
35
37
|
import { resolveOrgPermissions } from "@aglyn/tenant-runtime/org-permissions";
|
|
36
38
|
import { posKioskPrincipal } from "./pos-kiosk-principal.js";
|
|
@@ -138,53 +140,31 @@ import { posKioskPrincipal } from "./pos-kiosk-principal.js";
|
|
|
138
140
|
if (slot) await slot.release();
|
|
139
141
|
};
|
|
140
142
|
try {
|
|
141
|
-
var
|
|
142
|
-
var _registerDocs_rank, _ownerOrg_org, _ownerOrg_org1;
|
|
143
|
+
var _ref2, _ref3, _storeSettings_get, _taxSettings_origin, _ref4, _ref5;
|
|
144
|
+
var _registerDocs_rank, _ownerOrg_org, _ownerOrg_org1, _ownerOrg_org2;
|
|
143
145
|
const decoded = kiosk ? {
|
|
144
146
|
uid: kiosk.uid
|
|
145
147
|
} : await firebaseAdmin.app().auth().verifyIdToken(idToken);
|
|
146
148
|
const firestore = firebaseAdmin.app().firestore();
|
|
147
149
|
const hostRef = firestore.collection('hosts').doc(hostId);
|
|
148
150
|
const hostSnapshot = await hostRef.get();
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
//
|
|
153
|
-
//
|
|
154
|
-
//
|
|
155
|
-
//
|
|
156
|
-
//
|
|
157
|
-
//
|
|
158
|
-
//
|
|
159
|
-
// added later is refused until someone decides otherwise, rather than
|
|
160
|
-
// admitted by the silence of a denylist.
|
|
161
|
-
//
|
|
162
|
-
// What it does NOT decide is WHOSE admin. `memberRoles` is a per-host
|
|
163
|
-
// projection, so a SITE COLLABORATOR granted `admin` on this one site
|
|
164
|
-
// (`hostAccess: { [hostId]: 'admin' }`, `allHosts: false`) is
|
|
165
|
-
// indistinguishable here from an org owner. That is correct for ringing a
|
|
166
|
-
// sale — they were invited to run this till — but it means this value
|
|
167
|
-
// alone cannot carry an elevated, manager-only control such as the
|
|
168
|
-
// AGL-2372 oversell override. The discriminator is `orgWide` on the
|
|
169
|
-
// membership resolved just below, not the role string.
|
|
170
|
-
const memberRole = ((_hostSnapshot_get = hostSnapshot.get('memberRoles')) != null ? _hostSnapshot_get : {})[decoded.uid];
|
|
171
|
-
if (memberRole !== 'admin' && memberRole !== 'editor') {
|
|
172
|
-
return res.status(403).json({
|
|
173
|
-
error: 'Not permitted'
|
|
174
|
-
});
|
|
175
|
-
}
|
|
176
|
-
// THE `managePos` PERMISSION, actually read (AGL-2474). The allowlist
|
|
177
|
-
// above is a HOST-level fact and stays — it is what the Firestore rules
|
|
178
|
-
// enforce and dropping it would widen this route. This is the org-level
|
|
179
|
-
// half: `managePos` was declared by COMMERCE_PERMISSIONS, resolved into
|
|
180
|
-
// every permission map, displayed nowhere and read by nothing, so the
|
|
181
|
-
// capability was inert. Both must hold, matching the marketplace
|
|
182
|
-
// install/publish routes' resolveOrgPermissions + memberRoles pairing.
|
|
151
|
+
if (!hostSnapshot.exists) return res.status(404).json({
|
|
152
|
+
error: 'Unknown site'
|
|
153
|
+
});
|
|
154
|
+
// THE KEYS DECIDE, NOT THE SITE ROLE (AGL-4047). This route asked for
|
|
155
|
+
// `admin` or `editor` in the site's `memberRoles` (an allowlist, AGL-2262)
|
|
156
|
+
// AND `managePos` (AGL-2474). Those two agreed for everyone by default —
|
|
157
|
+
// `managePos` is on for exactly the admin and editor tiers — so dropping
|
|
158
|
+
// the role check takes the register from nobody. What changes is who the
|
|
159
|
+
// workspace can hand it to: a site viewer granted `managePos` rings sales,
|
|
160
|
+
// and an admin a role narrows is refused, as the waivers doors already do.
|
|
183
161
|
//
|
|
184
|
-
//
|
|
185
|
-
//
|
|
186
|
-
//
|
|
187
|
-
// the
|
|
162
|
+
// `resolveOrgPermissions` answers for THIS site: an org-wide member from
|
|
163
|
+
// their org role, custom role and overrides; a site collaborator from the
|
|
164
|
+
// role they hold here and their overrides; and anyone without access to
|
|
165
|
+
// the site with nothing. Whose admin a member is stays the `orgWide`
|
|
166
|
+
// discriminator below, for the manager-only controls such as the AGL-2372
|
|
167
|
+
// oversell override; a site role alone never carried those.
|
|
188
168
|
const membership = await resolveOrgPermissions(decoded.uid, {
|
|
189
169
|
hostId
|
|
190
170
|
});
|
|
@@ -193,6 +173,20 @@ import { posKioskPrincipal } from "./pos-kiosk-principal.js";
|
|
|
193
173
|
error: 'Not permitted'
|
|
194
174
|
});
|
|
195
175
|
}
|
|
176
|
+
// THE REGISTER'S FINER KEYS (AGL-4047), each following `managePos` until
|
|
177
|
+
// a role or the member sets it: ringing a sale at all, and the cashier's
|
|
178
|
+
// own percentage off. A shopper's code and the store's promotions are the
|
|
179
|
+
// merchant's, and apply without the discount key.
|
|
180
|
+
if (!holdsPosPermission(membership.permissions, 'posSell')) {
|
|
181
|
+
return res.status(403).json({
|
|
182
|
+
error: posPermissionRefusal('posSell')
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
if (discountPct > 0 && !holdsPosPermission(membership.permissions, 'posDiscount')) {
|
|
186
|
+
return res.status(403).json({
|
|
187
|
+
error: posPermissionRefusal('posDiscount')
|
|
188
|
+
});
|
|
189
|
+
}
|
|
196
190
|
const ownerOrg = await getOrgForHost(hostId);
|
|
197
191
|
if (!Aglyn.checkEntitlement(ownerOrg == null ? void 0 : ownerOrg.org, 'pos')) {
|
|
198
192
|
return res.status(403).json({
|
|
@@ -336,6 +330,15 @@ import { posKioskPrincipal } from "./pos-kiosk-principal.js";
|
|
|
336
330
|
error: 'No valid lines'
|
|
337
331
|
});
|
|
338
332
|
}
|
|
333
|
+
// A sale of card readers on the platform's own store (AGL-4038) is
|
|
334
|
+
// marked from the moment it is written, so its hardware order is
|
|
335
|
+
// recorded once it is paid even if the completion listener misses it.
|
|
336
|
+
const hardwareFields = posHardwareSaleFields({
|
|
337
|
+
hostId,
|
|
338
|
+
orgId: String((_ref2 = (_ref3 = ownerOrg == null ? void 0 : (_ownerOrg_org = ownerOrg.org) == null ? void 0 : _ownerOrg_org.id) != null ? _ref3 : ownerOrg == null ? void 0 : ownerOrg.orgId) != null ? _ref2 : ''),
|
|
339
|
+
lineItems,
|
|
340
|
+
products: productsById
|
|
341
|
+
});
|
|
339
342
|
// WHAT THE COUNT SAYS, said out loud (AGL-2357). Every storefront door
|
|
340
343
|
// gates on `canPurchase`; a register that skips it silently downgrades a
|
|
341
344
|
// merchant's `oversellPolicy: 'deny'` to `backorder` at the till.
|
|
@@ -630,7 +633,7 @@ import { posKioskPrincipal } from "./pos-kiosk-principal.js";
|
|
|
630
633
|
// `orgs//offlineFees/…`; the sale still rings, and the fee is forgone
|
|
631
634
|
// rather than written somewhere no invoice will ever find it.
|
|
632
635
|
const offlineFeeMonth = offlineFeeMonthKey();
|
|
633
|
-
const offlineFeeOrgId = String((
|
|
636
|
+
const offlineFeeOrgId = String((_ref4 = ownerOrg == null ? void 0 : (_ownerOrg_org1 = ownerOrg.org) == null ? void 0 : _ownerOrg_org1.id) != null ? _ref4 : '');
|
|
634
637
|
const offlineFeeRef = offlineFeeOrgId ? firestore.collection('orgs').doc(offlineFeeOrgId).collection('offlineFees').doc(offlineFeeMonth) : null;
|
|
635
638
|
// Deterministic tender rejections run BEFORE the claim is taken, so they
|
|
636
639
|
// never burn the key (AGL-1691): "cash received is short" is a 400 the
|
|
@@ -686,7 +689,7 @@ import { posKioskPrincipal } from "./pos-kiosk-principal.js";
|
|
|
686
689
|
const claimed = await claimAttempt(firestore, {
|
|
687
690
|
kind: 'pos',
|
|
688
691
|
scopeId: hostId,
|
|
689
|
-
orgId: String((
|
|
692
|
+
orgId: String((_ref5 = ownerOrg == null ? void 0 : (_ownerOrg_org2 = ownerOrg.org) == null ? void 0 : _ownerOrg_org2.id) != null ? _ref5 : ''),
|
|
690
693
|
key: idempotencyKey,
|
|
691
694
|
busyMessage: 'This sale is already being processed'
|
|
692
695
|
});
|
|
@@ -759,7 +762,7 @@ import { posKioskPrincipal } from "./pos-kiosk-principal.js";
|
|
|
759
762
|
channel: 'pos',
|
|
760
763
|
registerId,
|
|
761
764
|
cashierId: saleStamp.cashierId
|
|
762
|
-
}, saleStamp.fields, discountPct > 0 ? {
|
|
765
|
+
}, saleStamp.fields, hardwareFields, discountPct > 0 ? {
|
|
763
766
|
discountPct,
|
|
764
767
|
discountBy: saleStamp.cashierId
|
|
765
768
|
} : {}, appliedDiscountId ? {
|
|
@@ -807,10 +810,10 @@ import { posKioskPrincipal } from "./pos-kiosk-principal.js";
|
|
|
807
810
|
return res.status(200).json(openPayload);
|
|
808
811
|
}
|
|
809
812
|
if (payment === 'link') {
|
|
810
|
-
var
|
|
811
|
-
var
|
|
813
|
+
var _ref6, _session_id;
|
|
814
|
+
var _ownerOrg_org3, _ownerOrg_org4;
|
|
812
815
|
// QR payment link on the merchant account, completed by webhook.
|
|
813
|
-
const ownerProfile = await firestore.collection('profiles').doc(String((
|
|
816
|
+
const ownerProfile = await firestore.collection('profiles').doc(String((_ref6 = ownerOrg == null ? void 0 : (_ownerOrg_org3 = ownerOrg.org) == null ? void 0 : _ownerOrg_org3.ownerUid) != null ? _ref6 : '')).get();
|
|
814
817
|
const accountId = ownerProfile.get('stripeAccountId');
|
|
815
818
|
if (!merchantAccountIsReady({
|
|
816
819
|
accountId,
|
|
@@ -846,7 +849,7 @@ import { posKioskPrincipal } from "./pos-kiosk-principal.js";
|
|
|
846
849
|
channel: 'pos',
|
|
847
850
|
registerId,
|
|
848
851
|
cashierId: saleStamp.cashierId
|
|
849
|
-
}, saleStamp.fields, discountPct > 0 ? {
|
|
852
|
+
}, saleStamp.fields, hardwareFields, discountPct > 0 ? {
|
|
850
853
|
discountPct,
|
|
851
854
|
discountBy: saleStamp.cashierId
|
|
852
855
|
} : {}, appliedDiscountId ? {
|
|
@@ -889,7 +892,7 @@ import { posKioskPrincipal } from "./pos-kiosk-principal.js";
|
|
|
889
892
|
// origin root rather than a fabricated dead path.
|
|
890
893
|
const posOrigin = `https://${req.headers.host}`;
|
|
891
894
|
const posSubdomain = (await firestore.collection('hostIndex').doc(hostId).get()).get('subdomain');
|
|
892
|
-
const posOrgSlug = ownerOrg == null ? void 0 : (
|
|
895
|
+
const posOrgSlug = ownerOrg == null ? void 0 : (_ownerOrg_org4 = ownerOrg.org) == null ? void 0 : _ownerOrg_org4.slug;
|
|
893
896
|
const posUrl = posOrgSlug && posSubdomain ? `${posOrigin}${buildRoute(Route.HOST_PLUGIN, {
|
|
894
897
|
orgSlug: posOrgSlug,
|
|
895
898
|
host: posSubdomain,
|
|
@@ -1051,7 +1054,7 @@ import { posKioskPrincipal } from "./pos-kiosk-principal.js";
|
|
|
1051
1054
|
// THE TENDER, RECORDED (AGL-3607): one settled payment, so this sale
|
|
1052
1055
|
// reads the same as a split one rather than by inference.
|
|
1053
1056
|
cashierId: saleStamp.cashierId
|
|
1054
|
-
}, saleStamp.fields, {
|
|
1057
|
+
}, saleStamp.fields, hardwareFields, {
|
|
1055
1058
|
payments: [
|
|
1056
1059
|
_extends({
|
|
1057
1060
|
id: `pay_${payment}`,
|