@aglyn/plugins-commerce 1.0.0-beta.285 → 1.0.0-beta.288
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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/order-webhooks-card.component.js +1 -1
- package/src/lib/components/console/order-webhooks-card.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 +15 -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 +74 -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/model/workspace-site-permissions.d.ts +26 -0
- package/src/lib/model/workspace-site-permissions.js +81 -0
- package/src/lib/model/workspace-site-permissions.js.map +1 -0
- 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 +48 -0
- package/src/lib/server/commerce-site-keys.js +61 -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/order-webhooks.js +8 -8
- package/src/lib/server/order-webhooks.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 +28 -9
- package/src/lib/server/pos-ops-gate.js +50 -19
- 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-refund-authority.d.ts +3 -1
- package/src/lib/server/pos-refund-authority.js +3 -2
- package/src/lib/server/pos-refund-authority.js.map +1 -1
- package/src/lib/server/pos-return.d.ts +10 -0
- package/src/lib/server/pos-return.js +29 -7
- 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 +29 -24
- 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 +55 -42
- 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
|
@@ -14,7 +14,9 @@ import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
|
14
14
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
15
|
* See the License for the specific language governing permissions and
|
|
16
16
|
* limitations under the License.
|
|
17
|
-
*/ import
|
|
17
|
+
*/ import { holdsCommerceSiteKey } from "./commerce-site-keys.js";
|
|
18
|
+
import { commerceSitePermissionRefusal } from "../model/plugin-permissions.js";
|
|
19
|
+
import * as CommerceModel from "../model/index.js";
|
|
18
20
|
import { firebaseAdmin } from "@aglyn/tenant-data-admin";
|
|
19
21
|
import { notifyOrderBuyer } from "./order-notifications.js";
|
|
20
22
|
import { ORDER_CANCELLED_EVENT } from "../model/order-events.js";
|
|
@@ -116,7 +118,6 @@ import { capRestockLines, resolveTrackedRestockLines, saleReleaseCaps } from "./
|
|
|
116
118
|
});
|
|
117
119
|
}
|
|
118
120
|
try {
|
|
119
|
-
var _hostSnapshot_get;
|
|
120
121
|
const decoded = await firebaseAdmin.app().auth().verifyIdToken(idToken);
|
|
121
122
|
const firestore = firebaseAdmin.app().firestore();
|
|
122
123
|
const hostRef = firestore.collection('hosts').doc(hostId);
|
|
@@ -131,10 +132,11 @@ import { capRestockLines, resolveTrackedRestockLines, saleReleaseCaps } from "./
|
|
|
131
132
|
// `editor` — narrowing to admin here would take the cancel button away
|
|
132
133
|
// from editors who have always had it, and widening it past what the rules
|
|
133
134
|
// allow would make this endpoint the way around them.
|
|
134
|
-
|
|
135
|
-
|
|
135
|
+
// "Manage orders" (`commerce.orders`, AGL-4047), whose preset is the site's Admin
|
|
136
|
+
// and Editor this asked for, read as the rules would read it.
|
|
137
|
+
if (!holdsCommerceSiteKey(hostSnapshot, decoded.uid, 'commerce.orders')) {
|
|
136
138
|
return res.status(403).json({
|
|
137
|
-
error: '
|
|
139
|
+
error: commerceSitePermissionRefusal('commerce.orders')
|
|
138
140
|
});
|
|
139
141
|
}
|
|
140
142
|
const orderRef = hostRef.collection('orders').doc(orderId);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/server/cancel-order.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 CommerceModel from '../model'\nimport { firebaseAdmin } from '@aglyn/tenant-data-admin'\nimport { notifyOrderBuyer } from './order-notifications'\nimport { ORDER_CANCELLED_EVENT } from '../model/order-events'\nimport { raiseOrderEvent } from './order-events'\nimport { type PluginApiHandler } from '@aglyn/aglyn/server'\nimport {\n capRestockLines,\n resolveTrackedRestockLines,\n saleReleaseCaps,\n} from './restock-flag'\n\n/**\n * Cancelling an order RELEASES its stock (AGL-1808) — the opposite of what the\n * reversal doors do, and for a structural reason rather than a preference.\n *\n * AGL-1797 refused to increment on a refund or a chargeback because the goods\n * may well be gone: a refund with no return, or a shopper who kept the item and\n * took the money, leaves stock that incrementing would INVENT. It flags and\n * lets the merchant answer. Cancellation carries no such question, and the\n * guarantee is `ORDER_TRANSITIONS`:\n *\n * pending: ['paid', 'cancelled'],\n * paid: ['partially_fulfilled', 'fulfilled', 'cancelled', 'refunded'],\n * partially_fulfilled | fulfilled | delivered: no 'cancelled'\n *\n * so a cancelled order was never fulfilled and nothing ever shipped. The units\n * are still on the shelf; the only wrong number is ours.\n *\n * THE GUARANTEE HAD TO BE ENFORCED BEFORE IT COULD BE RELIED ON, and that is\n * why this is a handler rather than a few lines in the console. The transition\n * table said the right thing but nothing consulted it on the way IN: the order\n * dialog cancelled with a client `updateDoc`, and `canTransitionOrder` only\n * decided whether to render the button. The Firestore rules impose no status\n * machine — an order update is allowed to any admin or editor — so a dialog\n * held open while the order shipped in another tab (or any other client) could\n * write `cancelled` straight onto a `fulfilled` order, and an automatic\n * increment behind that would restock goods already in the post. Re-asking the\n * transition INSIDE the transaction that writes the status is what turns the\n * comment into a guarantee, and it is what makes the release safe.\n *\n * `pending` RELEASES NOTHING, which is the other half the issue's premise\n * missed. Stock is decremented when the money lands, not when the order is\n * written: `draft-order.ts` and the POS card path both open a `pending` order\n * and decrement only in the webhook that pays it. Incrementing on a cancelled\n * `pending` order would invent inventory exactly the way AGL-1797 refused to —\n * so only a `paid` order, the one status reachable here whose sale provably\n * decremented, releases anything.\n *\n * ONE LEDGER, NOT A SECOND STOCK WRITER. The increment goes through\n * `adjustVariantInventory` and logs an `InventoryAdjustment` per line, the same\n * pair the sale paths and the products hub's \"Adjust stock\" write, with the new\n * `cancellation` reason and the `orderId`. An increment with no ledger row\n * would leave the hub's stock history unable to explain a count that moved on\n * its own — the mirror of AGL-1797's argument against writing a row for stock\n * that had NOT moved.\n *\n * ONLY WHAT THE SALE DECREMENTED comes back: `resolveTrackedRestockLines`, the\n * helper AGL-1797 already built, applies the sale path's own\n * `variant.inventory != null` test to the products as they stand now. A store\n * that tracks no stock gets a plain cancellation and no adjustment rows.\n *\n * IT FAILS THE CANCEL RATHER THAN LOSING THE RELEASE. `flagOrderRestock`\n * swallows its errors because the money had already moved and the flag was a\n * postscript; here the status flip and the stock write are one act, so they\n * share a transaction and a failure leaves the order uncancelled for the\n * merchant to retry. A cancelled order whose stock never came back is the\n * original bug.\n *\n * ONCE. The transaction's own read is the guard: a second cancel — a retried\n * request, two admins clicking together — finds `cancelled`, which\n * `canTransitionOrder` cannot leave, and returns without touching stock. Two\n * increments of the same units is the failure mode this shape exists to\n * prevent, and it is measured rather than asserted (`cancel-order.spec.ts`).\n *\n * REACHED FROM THE CONSOLE since AGL-1818: `order-detail-dialog`'s\n * `handleCancel` POSTs here with the console id token, and the client\n * `updateDoc` cancel it replaced is gone. (The wiring landed separately from\n * the route because the dialog belonged to a concurrent agent, AGL-1806,\n * when the route was written.)\n */\nexport const cancelOrderHandler: PluginApiHandler = async (req, res) => {\n if (req.method !== 'POST') {\n return res.status(405).json({ error: 'Method not allowed' })\n }\n const authorization = String(req.headers.authorization ?? '')\n const idToken = authorization.startsWith('Bearer ')\n ? authorization.slice('Bearer '.length)\n : undefined\n if (!idToken) return res.status(401).json({ error: 'Unauthenticated' })\n const body =\n typeof req.body === 'string' ? JSON.parse(req.body) : (req.body ?? {})\n const hostId = String(body.hostId ?? '')\n const orderId = String(body.orderId ?? '')\n if (!hostId || !orderId) {\n return res.status(400).json({ error: 'Missing hostId or orderId' })\n }\n // Firestore reserves `__…__` ids and `.doc()` throws SYNCHRONOUSLY on one,\n // which would surface as a 500 on a caller's typo rather than the 400 it is.\n if (/^__.*__$/.test(hostId) || /^__.*__$/.test(orderId)) {\n return res.status(400).json({ error: 'Missing hostId or orderId' })\n }\n\n try {\n const decoded = await firebaseAdmin.app().auth().verifyIdToken(idToken)\n const firestore = firebaseAdmin.app().firestore()\n const hostRef = firestore.collection('hosts').doc(hostId)\n const hostSnapshot = await hostRef.get()\n if (!hostSnapshot.exists) {\n return res.status(404).json({ error: 'Unknown site' })\n }\n // The role the CLIENT write already had, not a stricter one. Order updates\n // fall through the rules' host catch-all, which admits `admin` and\n // `editor` — narrowing to admin here would take the cancel button away\n // from editors who have always had it, and widening it past what the rules\n // allow would make this endpoint the way around them.\n const memberRole = (hostSnapshot.get('memberRoles') ?? {})[decoded.uid]\n if (memberRole !== 'admin' && memberRole !== 'editor') {\n return res.status(403).json({ error: 'Not permitted' })\n }\n const orderRef = hostRef.collection('orders').doc(orderId)\n\n const outcome = await firestore.runTransaction(async (transaction) => {\n const snapshot = await transaction.get(orderRef)\n if (!snapshot.exists) {\n return { status: 404, body: { error: 'Unknown order' } }\n }\n const order = CommerceModel.liftLegacyOrder(\n (snapshot.data() ?? {}) as never,\n )\n // A redelivered click, or the second of two admins. Answered as success\n // because it IS the state the caller asked for — reporting 409 here\n // teaches an admin that the cancel failed and invites a third attempt —\n // but nothing is written, so the units are released exactly once.\n if (order.status === 'cancelled') {\n return {\n status: 200,\n body: { ok: true, alreadyCancelled: true, released: 0, units: 0 },\n }\n }\n if (!CommerceModel.canTransitionOrder(order.status, 'cancelled')) {\n return {\n status: 409,\n body: { error: `Orders in \"${order.status}\" cannot cancel` },\n }\n }\n // An OPEN register sale that has taken money (AGL-3607) is voided at\n // the register, which hands each payment back first. Cancelling it here\n // would mark it cancelled with a card charge, a gift card debit or a\n // room charge still standing.\n if (\n order.status === 'pending' &&\n CommerceModel.orderPayments(order).some(\n (payment) => payment.status === 'succeeded' || payment.status === 'pending',\n ) &&\n Array.isArray(order.payments)\n ) {\n return {\n status: 409,\n body: {\n error:\n 'This register sale has payments on it. Void it at the register, ' +\n 'which hands each payment back first.',\n },\n }\n }\n\n // A POS CARD order's decrement happens in the webhook that pays it —\n // and before AGL-1825 it did not happen AT ALL: the paying branch's\n // decrement was gated on `productId` metadata the register's session\n // never carried. Releasing such an order restocks units that were never\n // taken, turning the silent under-count into a silent over-count. The\n // sale's own ledger is the discriminator: every decrement pairs with a\n // `reason: 'sale'` row (AGL-1807 closed the last hole, and the AGL-1825\n // decrement writes them from day one), so a paid card order with no\n // sale row is one whose sale took nothing — and nothing is released.\n //\n // Scoped to card orders (the `pos-card-pending` timeline event) rather\n // than applied to every order, because one historical path decremented\n // WITHOUT rows — draft links paid before AGL-1807 — and asking the\n // ledger to vouch for those would wrongly strand their stock. The\n // residual is stated plainly: the sibling-shaped decrement/ledger pair\n // is two swallowed sequential writes, so a card order whose ledger add\n // alone failed will not release — a bounded under-count on that one\n // order, visible as a sale missing from the hub's history, against the\n // alternative of inflating stock on every pre-fix cancel.\n //\n // One equality filter, so the automatic single-field index answers it;\n // the reason test runs here, over the handful of rows one order has.\n //\n // THE SAME READ NOW ALSO BOUNDS HOW MUCH COMES BACK (AGL-2149), so it is\n // no longer scoped to card orders. `adjustVariantInventory` floors at\n // zero, and that floor silently absorbs the overshoot on a BACKORDER\n // product: stock 0, `canPurchase` admits the sale because the policy says\n // to, three units sell, `Math.max(0, 0 + -3)` leaves the count at 0 —\n // and a `+3` here would hand the merchant three units nobody has and a\n // stock badge offering them. The sale rows carry `appliedDelta` for\n // exactly this: `delta` stays the units sold (the merchant's history),\n // `appliedDelta` says how many the count could actually give up.\n //\n // Only lines the ledger DESCRIBES are capped. A pair the ledger says\n // nothing about restores as before, which is what keeps the pre-AGL-1807\n // draft links — decremented with no row at all — from having their stock\n // stranded, and what keeps a single swallowed ledger write from turning\n // into a lost restock on an unrelated line of the same order.\n let saleDecremented = true\n // The cap rule itself lives beside the restock flag, which now bounds\n // the question it ASKS by the same ledger this bounds the stock it\n // RETURNS (AGL-2325). One implementation, so a prompt naming more units\n // than a cancellation would put back is not expressible.\n let releaseCap: ReadonlyMap<string, number> = new Map<string, number>()\n if (order.status === 'paid') {\n const orderAdjustments = await transaction.get(\n hostRef\n .collection('inventoryAdjustments')\n .where('orderId', '==', orderId),\n )\n const saleRows = orderAdjustments.docs.filter(\n (row) => row.get('reason') === 'sale',\n )\n if (\n order.channel === 'pos' &&\n (order.timeline ?? []).some(\n (event) => event.event === 'pos-card-pending',\n )\n ) {\n saleDecremented = saleRows.length > 0\n }\n releaseCap = saleReleaseCaps(saleRows)\n }\n\n // `paid` is the only status reachable here whose sale decremented; see\n // the header. Reads happen before any write, as a transaction requires.\n const { lines, products } =\n order.status === 'paid' && saleDecremented\n ? await resolveTrackedRestockLines(hostRef, order, (ref) =>\n transaction.get(ref),\n )\n : {\n lines: [],\n products: new Map<string, CommerceModel.HostProduct>(),\n }\n\n const atMs = Date.now()\n // Grouped by product, because two lines of one product (two variants, or\n // the same variant twice) are two deltas against ONE document: writing\n // them separately would compute both from the same read and land only\n // the last, silently dropping units.\n const byProduct = new Map<string, CommerceModel.OrderRestockLine[]>()\n // Never more than the sale actually took off the shelf (AGL-2149).\n for (const line of capRestockLines(lines, releaseCap)) {\n byProduct.set(line.productId, [\n ...(byProduct.get(line.productId) ?? []),\n line,\n ])\n }\n for (const [productId, productLines] of byProduct) {\n const product = products.get(productId)\n if (!product) continue\n let variants = product.variants\n for (const line of productLines) {\n variants = CommerceModel.adjustVariantInventory(\n { variants },\n line.variantId,\n line.quantity,\n // The bucket the sale came out of, for a location-tracked store\n // (AGL-286). Absent on every path that decremented the flat count,\n // where `adjustVariantInventory` puts it back the same way.\n order.locationId || undefined,\n )\n }\n // `update()`, not `set(…, { merge: true })` as the sale paths use: the\n // transaction has just read this product, so it exists, and a product\n // deleted under us must not be conjured back as a stub holding nothing\n // but a variants array.\n transaction.update(hostRef.collection('products').doc(productId), {\n variants,\n // The total and the storefront's In stock verdict (AGL-3321).\n ...CommerceModel.productStockFields({ ...product, variants }),\n updatedAtMs: atMs,\n })\n for (const line of productLines) {\n transaction.create(hostRef.collection('inventoryAdjustments').doc(), {\n productId,\n variantId: line.variantId,\n delta: line.quantity,\n reason: 'cancellation',\n orderId,\n ...(order.locationId ? { locationId: order.locationId } : {}),\n atMs,\n } satisfies CommerceModel.InventoryAdjustment)\n }\n }\n\n // From the CAPPED lines, not the order's, so the timeline reports what\n // was actually put back (AGL-2149).\n const releasedLines = [...byProduct.values()].flat()\n const units = releasedLines.reduce((sum, line) => sum + line.quantity, 0)\n const patch: Record<string, unknown> = {\n status: 'cancelled',\n // What a shipping tool's feed asks to learn of the cancel (AGL-3613).\n updatedAtMs: atMs,\n timeline: CommerceModel.appendOrderEvent(\n order,\n 'cancelled',\n units > 0\n ? `${units} ${units === 1 ? 'unit' : 'units'} returned to stock`\n : undefined,\n atMs,\n ),\n }\n // An open restock question about THIS order's stock, raised by a partial\n // refund before the cancellation (AGL-1797). The units it asked about are\n // the ones just put back, so leaving it open would invite the merchant to\n // restock them a second time from the order dialog — the double-count\n // that flag exists to avoid. Answered with what actually happened.\n if (units > 0 && order.restockCheck && !order.restockCheck.resolution) {\n patch.restockCheck = {\n ...order.restockCheck,\n resolution: 'restocked',\n resolvedAtMs: atMs,\n resolvedBy: decoded.uid,\n } satisfies CommerceModel.OrderRestockCheck\n }\n transaction.update(orderRef, patch)\n return {\n status: 200,\n body: { ok: true, released: releasedLines.length, units },\n // The live Stripe page this cancellation orphans, if any (AGL-2244).\n // Read inside the transaction that flips the status so the expiry\n // below cannot act on an order some other writer paid meanwhile.\n expireSessionId:\n order.status === 'pending' ? (order.checkoutSessionId ?? '') : '',\n }\n })\n // EXPIRE THE PAYMENT LINK (AGL-2244). Cancelling a `pending` draft order or\n // POS card sale left its Checkout Session live: the URL is on the\n // customer's phone or in the email the merchant sent, and Stripe keeps it\n // payable for 24 hours. Paying it captured real money on the merchant's\n // account and the completing webhook then discarded the event — its\n // `status !== 'pending'` guard cannot tell a redelivery from a cancelled\n // order — so the sale existed in Stripe and nowhere in the product: no\n // order, no stock move, no contact, no notification, not even a timeline\n // line. The webhook now says so when it happens; this is the half that\n // stops it happening.\n //\n // OUTSIDE the transaction, because it is a network call, and best-effort\n // by design: the cancellation itself already landed and is what the\n // merchant asked for. A failure here leaves the pre-existing behaviour\n // (a payable link) plus the webhook's new record of it, which is strictly\n // better than failing a cancel that has already released stock. Stripe\n // answers 400 for a session that is already expired or already paid, and\n // both are states this has nothing to add to.\n const expireSessionId = (outcome as { expireSessionId?: string })\n .expireSessionId\n if (expireSessionId && process.env.STRIPE_SECRET_KEY) {\n const expiry = await fetch(\n `https://api.stripe.com/v1/checkout/sessions/${expireSessionId}/expire`,\n {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${process.env.STRIPE_SECRET_KEY}`,\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n },\n ).catch(() => null)\n if (!expiry?.ok) {\n console.error(\n 'Cancelled order: Stripe session not expired, the payment link may still be payable',\n { hostId, orderId, sessionId: expireSessionId },\n )\n }\n }\n // The buyer is told (AGL-3610) — only by the request that cancelled it.\n if (outcome.status === 200 && !(outcome.body as { alreadyCancelled?: boolean }).alreadyCancelled) {\n await notifyOrderBuyer({ hostId, orderId }, 'cancelled')\n await raiseOrderEvent(ORDER_CANCELLED_EVENT, { hostId, orderId, key: 'cancelled' })\n }\n return res.status(outcome.status).json(outcome.body)\n } catch (error) {\n // Nothing landed — the transaction is all-or-nothing — so the order is\n // still open and the merchant can try again. Deliberately NOT the swallow\n // `flagOrderRestock` uses: there the money had already moved and only a\n // postscript was at risk; here a swallowed failure is a cancelled order\n // whose stock never came back, which is the bug this closes.\n console.error('cancelOrder failed', error)\n return res.status(500).json({ error: 'Cancel failed' })\n }\n}\n"],"names":["CommerceModel","firebaseAdmin","notifyOrderBuyer","ORDER_CANCELLED_EVENT","raiseOrderEvent","capRestockLines","resolveTrackedRestockLines","saleReleaseCaps","cancelOrderHandler","req","res","body","method","status","json","error","authorization","String","headers","idToken","startsWith","slice","length","undefined","JSON","parse","hostId","orderId","test","hostSnapshot","decoded","app","auth","verifyIdToken","firestore","hostRef","collection","doc","get","exists","memberRole","uid","orderRef","outcome","runTransaction","transaction","snapshot","order","liftLegacyOrder","data","ok","alreadyCancelled","released","units","canTransitionOrder","orderPayments","some","payment","Array","isArray","payments","saleDecremented","releaseCap","Map","orderAdjustments","where","saleRows","docs","filter","row","channel","timeline","event","lines","products","ref","atMs","Date","now","byProduct","line","set","productId","productLines","product","variants","adjustVariantInventory","variantId","quantity","locationId","update","productStockFields","updatedAtMs","create","delta","reason","releasedLines","values","flat","reduce","sum","patch","appendOrderEvent","restockCheck","resolution","resolvedAtMs","resolvedBy","expireSessionId","checkoutSessionId","process","env","STRIPE_SECRET_KEY","expiry","fetch","Authorization","catch","console","sessionId","key"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/server/cancel-order.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 { holdsCommerceSiteKey } from './commerce-site-keys'\nimport { commerceSitePermissionRefusal } from '../model/plugin-permissions'\nimport * as CommerceModel from '../model'\nimport { firebaseAdmin } from '@aglyn/tenant-data-admin'\nimport { notifyOrderBuyer } from './order-notifications'\nimport { ORDER_CANCELLED_EVENT } from '../model/order-events'\nimport { raiseOrderEvent } from './order-events'\nimport { type PluginApiHandler } from '@aglyn/aglyn/server'\nimport {\n capRestockLines,\n resolveTrackedRestockLines,\n saleReleaseCaps,\n} from './restock-flag'\n\n/**\n * Cancelling an order RELEASES its stock (AGL-1808) — the opposite of what the\n * reversal doors do, and for a structural reason rather than a preference.\n *\n * AGL-1797 refused to increment on a refund or a chargeback because the goods\n * may well be gone: a refund with no return, or a shopper who kept the item and\n * took the money, leaves stock that incrementing would INVENT. It flags and\n * lets the merchant answer. Cancellation carries no such question, and the\n * guarantee is `ORDER_TRANSITIONS`:\n *\n * pending: ['paid', 'cancelled'],\n * paid: ['partially_fulfilled', 'fulfilled', 'cancelled', 'refunded'],\n * partially_fulfilled | fulfilled | delivered: no 'cancelled'\n *\n * so a cancelled order was never fulfilled and nothing ever shipped. The units\n * are still on the shelf; the only wrong number is ours.\n *\n * THE GUARANTEE HAD TO BE ENFORCED BEFORE IT COULD BE RELIED ON, and that is\n * why this is a handler rather than a few lines in the console. The transition\n * table said the right thing but nothing consulted it on the way IN: the order\n * dialog cancelled with a client `updateDoc`, and `canTransitionOrder` only\n * decided whether to render the button. The Firestore rules impose no status\n * machine — an order update is allowed to any admin or editor — so a dialog\n * held open while the order shipped in another tab (or any other client) could\n * write `cancelled` straight onto a `fulfilled` order, and an automatic\n * increment behind that would restock goods already in the post. Re-asking the\n * transition INSIDE the transaction that writes the status is what turns the\n * comment into a guarantee, and it is what makes the release safe.\n *\n * `pending` RELEASES NOTHING, which is the other half the issue's premise\n * missed. Stock is decremented when the money lands, not when the order is\n * written: `draft-order.ts` and the POS card path both open a `pending` order\n * and decrement only in the webhook that pays it. Incrementing on a cancelled\n * `pending` order would invent inventory exactly the way AGL-1797 refused to —\n * so only a `paid` order, the one status reachable here whose sale provably\n * decremented, releases anything.\n *\n * ONE LEDGER, NOT A SECOND STOCK WRITER. The increment goes through\n * `adjustVariantInventory` and logs an `InventoryAdjustment` per line, the same\n * pair the sale paths and the products hub's \"Adjust stock\" write, with the new\n * `cancellation` reason and the `orderId`. An increment with no ledger row\n * would leave the hub's stock history unable to explain a count that moved on\n * its own — the mirror of AGL-1797's argument against writing a row for stock\n * that had NOT moved.\n *\n * ONLY WHAT THE SALE DECREMENTED comes back: `resolveTrackedRestockLines`, the\n * helper AGL-1797 already built, applies the sale path's own\n * `variant.inventory != null` test to the products as they stand now. A store\n * that tracks no stock gets a plain cancellation and no adjustment rows.\n *\n * IT FAILS THE CANCEL RATHER THAN LOSING THE RELEASE. `flagOrderRestock`\n * swallows its errors because the money had already moved and the flag was a\n * postscript; here the status flip and the stock write are one act, so they\n * share a transaction and a failure leaves the order uncancelled for the\n * merchant to retry. A cancelled order whose stock never came back is the\n * original bug.\n *\n * ONCE. The transaction's own read is the guard: a second cancel — a retried\n * request, two admins clicking together — finds `cancelled`, which\n * `canTransitionOrder` cannot leave, and returns without touching stock. Two\n * increments of the same units is the failure mode this shape exists to\n * prevent, and it is measured rather than asserted (`cancel-order.spec.ts`).\n *\n * REACHED FROM THE CONSOLE since AGL-1818: `order-detail-dialog`'s\n * `handleCancel` POSTs here with the console id token, and the client\n * `updateDoc` cancel it replaced is gone. (The wiring landed separately from\n * the route because the dialog belonged to a concurrent agent, AGL-1806,\n * when the route was written.)\n */\nexport const cancelOrderHandler: PluginApiHandler = async (req, res) => {\n if (req.method !== 'POST') {\n return res.status(405).json({ error: 'Method not allowed' })\n }\n const authorization = String(req.headers.authorization ?? '')\n const idToken = authorization.startsWith('Bearer ')\n ? authorization.slice('Bearer '.length)\n : undefined\n if (!idToken) return res.status(401).json({ error: 'Unauthenticated' })\n const body =\n typeof req.body === 'string' ? JSON.parse(req.body) : (req.body ?? {})\n const hostId = String(body.hostId ?? '')\n const orderId = String(body.orderId ?? '')\n if (!hostId || !orderId) {\n return res.status(400).json({ error: 'Missing hostId or orderId' })\n }\n // Firestore reserves `__…__` ids and `.doc()` throws SYNCHRONOUSLY on one,\n // which would surface as a 500 on a caller's typo rather than the 400 it is.\n if (/^__.*__$/.test(hostId) || /^__.*__$/.test(orderId)) {\n return res.status(400).json({ error: 'Missing hostId or orderId' })\n }\n\n try {\n const decoded = await firebaseAdmin.app().auth().verifyIdToken(idToken)\n const firestore = firebaseAdmin.app().firestore()\n const hostRef = firestore.collection('hosts').doc(hostId)\n const hostSnapshot = await hostRef.get()\n if (!hostSnapshot.exists) {\n return res.status(404).json({ error: 'Unknown site' })\n }\n // The role the CLIENT write already had, not a stricter one. Order updates\n // fall through the rules' host catch-all, which admits `admin` and\n // `editor` — narrowing to admin here would take the cancel button away\n // from editors who have always had it, and widening it past what the rules\n // allow would make this endpoint the way around them.\n // \"Manage orders\" (`commerce.orders`, AGL-4047), whose preset is the site's Admin\n // and Editor this asked for, read as the rules would read it.\n if (!holdsCommerceSiteKey(hostSnapshot, decoded.uid, 'commerce.orders')) {\n return res.status(403).json({ error: commerceSitePermissionRefusal('commerce.orders') })\n }\n const orderRef = hostRef.collection('orders').doc(orderId)\n\n const outcome = await firestore.runTransaction(async (transaction) => {\n const snapshot = await transaction.get(orderRef)\n if (!snapshot.exists) {\n return { status: 404, body: { error: 'Unknown order' } }\n }\n const order = CommerceModel.liftLegacyOrder(\n (snapshot.data() ?? {}) as never,\n )\n // A redelivered click, or the second of two admins. Answered as success\n // because it IS the state the caller asked for — reporting 409 here\n // teaches an admin that the cancel failed and invites a third attempt —\n // but nothing is written, so the units are released exactly once.\n if (order.status === 'cancelled') {\n return {\n status: 200,\n body: { ok: true, alreadyCancelled: true, released: 0, units: 0 },\n }\n }\n if (!CommerceModel.canTransitionOrder(order.status, 'cancelled')) {\n return {\n status: 409,\n body: { error: `Orders in \"${order.status}\" cannot cancel` },\n }\n }\n // An OPEN register sale that has taken money (AGL-3607) is voided at\n // the register, which hands each payment back first. Cancelling it here\n // would mark it cancelled with a card charge, a gift card debit or a\n // room charge still standing.\n if (\n order.status === 'pending' &&\n CommerceModel.orderPayments(order).some(\n (payment) => payment.status === 'succeeded' || payment.status === 'pending',\n ) &&\n Array.isArray(order.payments)\n ) {\n return {\n status: 409,\n body: {\n error:\n 'This register sale has payments on it. Void it at the register, ' +\n 'which hands each payment back first.',\n },\n }\n }\n\n // A POS CARD order's decrement happens in the webhook that pays it —\n // and before AGL-1825 it did not happen AT ALL: the paying branch's\n // decrement was gated on `productId` metadata the register's session\n // never carried. Releasing such an order restocks units that were never\n // taken, turning the silent under-count into a silent over-count. The\n // sale's own ledger is the discriminator: every decrement pairs with a\n // `reason: 'sale'` row (AGL-1807 closed the last hole, and the AGL-1825\n // decrement writes them from day one), so a paid card order with no\n // sale row is one whose sale took nothing — and nothing is released.\n //\n // Scoped to card orders (the `pos-card-pending` timeline event) rather\n // than applied to every order, because one historical path decremented\n // WITHOUT rows — draft links paid before AGL-1807 — and asking the\n // ledger to vouch for those would wrongly strand their stock. The\n // residual is stated plainly: the sibling-shaped decrement/ledger pair\n // is two swallowed sequential writes, so a card order whose ledger add\n // alone failed will not release — a bounded under-count on that one\n // order, visible as a sale missing from the hub's history, against the\n // alternative of inflating stock on every pre-fix cancel.\n //\n // One equality filter, so the automatic single-field index answers it;\n // the reason test runs here, over the handful of rows one order has.\n //\n // THE SAME READ NOW ALSO BOUNDS HOW MUCH COMES BACK (AGL-2149), so it is\n // no longer scoped to card orders. `adjustVariantInventory` floors at\n // zero, and that floor silently absorbs the overshoot on a BACKORDER\n // product: stock 0, `canPurchase` admits the sale because the policy says\n // to, three units sell, `Math.max(0, 0 + -3)` leaves the count at 0 —\n // and a `+3` here would hand the merchant three units nobody has and a\n // stock badge offering them. The sale rows carry `appliedDelta` for\n // exactly this: `delta` stays the units sold (the merchant's history),\n // `appliedDelta` says how many the count could actually give up.\n //\n // Only lines the ledger DESCRIBES are capped. A pair the ledger says\n // nothing about restores as before, which is what keeps the pre-AGL-1807\n // draft links — decremented with no row at all — from having their stock\n // stranded, and what keeps a single swallowed ledger write from turning\n // into a lost restock on an unrelated line of the same order.\n let saleDecremented = true\n // The cap rule itself lives beside the restock flag, which now bounds\n // the question it ASKS by the same ledger this bounds the stock it\n // RETURNS (AGL-2325). One implementation, so a prompt naming more units\n // than a cancellation would put back is not expressible.\n let releaseCap: ReadonlyMap<string, number> = new Map<string, number>()\n if (order.status === 'paid') {\n const orderAdjustments = await transaction.get(\n hostRef\n .collection('inventoryAdjustments')\n .where('orderId', '==', orderId),\n )\n const saleRows = orderAdjustments.docs.filter(\n (row) => row.get('reason') === 'sale',\n )\n if (\n order.channel === 'pos' &&\n (order.timeline ?? []).some(\n (event) => event.event === 'pos-card-pending',\n )\n ) {\n saleDecremented = saleRows.length > 0\n }\n releaseCap = saleReleaseCaps(saleRows)\n }\n\n // `paid` is the only status reachable here whose sale decremented; see\n // the header. Reads happen before any write, as a transaction requires.\n const { lines, products } =\n order.status === 'paid' && saleDecremented\n ? await resolveTrackedRestockLines(hostRef, order, (ref) =>\n transaction.get(ref),\n )\n : {\n lines: [],\n products: new Map<string, CommerceModel.HostProduct>(),\n }\n\n const atMs = Date.now()\n // Grouped by product, because two lines of one product (two variants, or\n // the same variant twice) are two deltas against ONE document: writing\n // them separately would compute both from the same read and land only\n // the last, silently dropping units.\n const byProduct = new Map<string, CommerceModel.OrderRestockLine[]>()\n // Never more than the sale actually took off the shelf (AGL-2149).\n for (const line of capRestockLines(lines, releaseCap)) {\n byProduct.set(line.productId, [\n ...(byProduct.get(line.productId) ?? []),\n line,\n ])\n }\n for (const [productId, productLines] of byProduct) {\n const product = products.get(productId)\n if (!product) continue\n let variants = product.variants\n for (const line of productLines) {\n variants = CommerceModel.adjustVariantInventory(\n { variants },\n line.variantId,\n line.quantity,\n // The bucket the sale came out of, for a location-tracked store\n // (AGL-286). Absent on every path that decremented the flat count,\n // where `adjustVariantInventory` puts it back the same way.\n order.locationId || undefined,\n )\n }\n // `update()`, not `set(…, { merge: true })` as the sale paths use: the\n // transaction has just read this product, so it exists, and a product\n // deleted under us must not be conjured back as a stub holding nothing\n // but a variants array.\n transaction.update(hostRef.collection('products').doc(productId), {\n variants,\n // The total and the storefront's In stock verdict (AGL-3321).\n ...CommerceModel.productStockFields({ ...product, variants }),\n updatedAtMs: atMs,\n })\n for (const line of productLines) {\n transaction.create(hostRef.collection('inventoryAdjustments').doc(), {\n productId,\n variantId: line.variantId,\n delta: line.quantity,\n reason: 'cancellation',\n orderId,\n ...(order.locationId ? { locationId: order.locationId } : {}),\n atMs,\n } satisfies CommerceModel.InventoryAdjustment)\n }\n }\n\n // From the CAPPED lines, not the order's, so the timeline reports what\n // was actually put back (AGL-2149).\n const releasedLines = [...byProduct.values()].flat()\n const units = releasedLines.reduce((sum, line) => sum + line.quantity, 0)\n const patch: Record<string, unknown> = {\n status: 'cancelled',\n // What a shipping tool's feed asks to learn of the cancel (AGL-3613).\n updatedAtMs: atMs,\n timeline: CommerceModel.appendOrderEvent(\n order,\n 'cancelled',\n units > 0\n ? `${units} ${units === 1 ? 'unit' : 'units'} returned to stock`\n : undefined,\n atMs,\n ),\n }\n // An open restock question about THIS order's stock, raised by a partial\n // refund before the cancellation (AGL-1797). The units it asked about are\n // the ones just put back, so leaving it open would invite the merchant to\n // restock them a second time from the order dialog — the double-count\n // that flag exists to avoid. Answered with what actually happened.\n if (units > 0 && order.restockCheck && !order.restockCheck.resolution) {\n patch.restockCheck = {\n ...order.restockCheck,\n resolution: 'restocked',\n resolvedAtMs: atMs,\n resolvedBy: decoded.uid,\n } satisfies CommerceModel.OrderRestockCheck\n }\n transaction.update(orderRef, patch)\n return {\n status: 200,\n body: { ok: true, released: releasedLines.length, units },\n // The live Stripe page this cancellation orphans, if any (AGL-2244).\n // Read inside the transaction that flips the status so the expiry\n // below cannot act on an order some other writer paid meanwhile.\n expireSessionId:\n order.status === 'pending' ? (order.checkoutSessionId ?? '') : '',\n }\n })\n // EXPIRE THE PAYMENT LINK (AGL-2244). Cancelling a `pending` draft order or\n // POS card sale left its Checkout Session live: the URL is on the\n // customer's phone or in the email the merchant sent, and Stripe keeps it\n // payable for 24 hours. Paying it captured real money on the merchant's\n // account and the completing webhook then discarded the event — its\n // `status !== 'pending'` guard cannot tell a redelivery from a cancelled\n // order — so the sale existed in Stripe and nowhere in the product: no\n // order, no stock move, no contact, no notification, not even a timeline\n // line. The webhook now says so when it happens; this is the half that\n // stops it happening.\n //\n // OUTSIDE the transaction, because it is a network call, and best-effort\n // by design: the cancellation itself already landed and is what the\n // merchant asked for. A failure here leaves the pre-existing behaviour\n // (a payable link) plus the webhook's new record of it, which is strictly\n // better than failing a cancel that has already released stock. Stripe\n // answers 400 for a session that is already expired or already paid, and\n // both are states this has nothing to add to.\n const expireSessionId = (outcome as { expireSessionId?: string })\n .expireSessionId\n if (expireSessionId && process.env.STRIPE_SECRET_KEY) {\n const expiry = await fetch(\n `https://api.stripe.com/v1/checkout/sessions/${expireSessionId}/expire`,\n {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${process.env.STRIPE_SECRET_KEY}`,\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n },\n ).catch(() => null)\n if (!expiry?.ok) {\n console.error(\n 'Cancelled order: Stripe session not expired, the payment link may still be payable',\n { hostId, orderId, sessionId: expireSessionId },\n )\n }\n }\n // The buyer is told (AGL-3610) — only by the request that cancelled it.\n if (outcome.status === 200 && !(outcome.body as { alreadyCancelled?: boolean }).alreadyCancelled) {\n await notifyOrderBuyer({ hostId, orderId }, 'cancelled')\n await raiseOrderEvent(ORDER_CANCELLED_EVENT, { hostId, orderId, key: 'cancelled' })\n }\n return res.status(outcome.status).json(outcome.body)\n } catch (error) {\n // Nothing landed — the transaction is all-or-nothing — so the order is\n // still open and the merchant can try again. Deliberately NOT the swallow\n // `flagOrderRestock` uses: there the money had already moved and only a\n // postscript was at risk; here a swallowed failure is a cancelled order\n // whose stock never came back, which is the bug this closes.\n console.error('cancelOrder failed', error)\n return res.status(500).json({ error: 'Cancel failed' })\n }\n}\n"],"names":["holdsCommerceSiteKey","commerceSitePermissionRefusal","CommerceModel","firebaseAdmin","notifyOrderBuyer","ORDER_CANCELLED_EVENT","raiseOrderEvent","capRestockLines","resolveTrackedRestockLines","saleReleaseCaps","cancelOrderHandler","req","res","body","method","status","json","error","authorization","String","headers","idToken","startsWith","slice","length","undefined","JSON","parse","hostId","orderId","test","decoded","app","auth","verifyIdToken","firestore","hostRef","collection","doc","hostSnapshot","get","exists","uid","orderRef","outcome","runTransaction","transaction","snapshot","order","liftLegacyOrder","data","ok","alreadyCancelled","released","units","canTransitionOrder","orderPayments","some","payment","Array","isArray","payments","saleDecremented","releaseCap","Map","orderAdjustments","where","saleRows","docs","filter","row","channel","timeline","event","lines","products","ref","atMs","Date","now","byProduct","line","set","productId","productLines","product","variants","adjustVariantInventory","variantId","quantity","locationId","update","productStockFields","updatedAtMs","create","delta","reason","releasedLines","values","flat","reduce","sum","patch","appendOrderEvent","restockCheck","resolution","resolvedAtMs","resolvedBy","expireSessionId","checkoutSessionId","process","env","STRIPE_SECRET_KEY","expiry","fetch","Authorization","catch","console","sessionId","key"],"mappings":"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"}
|
|
@@ -26,6 +26,7 @@ import { merchantAccountIsReady } from "@aglyn/tenant-data-admin/server/payment-
|
|
|
26
26
|
import { checkoutSessionCardAuthenticationParams } from "@aglyn/tenant-data-admin/server/stripe-card-authentication";
|
|
27
27
|
import { CARRIER_POSTAL_CODE_MESSAGE, planCheckoutShippingWithCarriers } from "./carrier-shipping.js";
|
|
28
28
|
import { readCartId } from "./cart-cookie.js";
|
|
29
|
+
import { readStoreDay } from "./store-day.js";
|
|
29
30
|
import { resolveManualTaxRateId } from "./manual-tax-rate.js";
|
|
30
31
|
import { allocateCentsByWeight, engineLineTaxPercentages, taxEngineSessionMetadata } from "../model/commerce-tax-engine.js";
|
|
31
32
|
import { quoteSaleTaxWithEngine } from "./tax-engine-quote.js";
|
|
@@ -104,8 +105,8 @@ import { boundedCreditCents, checkoutCreditProviderForCode, encodeCheckoutCredit
|
|
|
104
105
|
if (held) await held.release();
|
|
105
106
|
};
|
|
106
107
|
try {
|
|
107
|
-
var _cartSnapshot_data, _ref1, _ref2, _storeSettings_get, _storeSettings_get1, _req_headers_referer, _req_headers_host, _ref3, _ref4, _session_id;
|
|
108
|
-
var
|
|
108
|
+
var _cartSnapshot_data, _ownerOrg_org, _ref1, _ref2, _storeSettings_get, _storeSettings_get1, _req_headers_referer, _req_headers_host, _ref3, _ref4, _session_id;
|
|
109
|
+
var _ownerOrg_org1, _ownerOrg_org2, _ownerOrg_org3, _engineTax_quote;
|
|
109
110
|
const firestore = firebaseAdmin.app().firestore();
|
|
110
111
|
const hostRef = firestore.collection('hosts').doc(hostId);
|
|
111
112
|
const cartSnapshot = await hostRef.collection('carts').doc(cartId).get();
|
|
@@ -118,7 +119,7 @@ import { boundedCreditCents, checkoutCreditProviderForCode, encodeCheckoutCredit
|
|
|
118
119
|
});
|
|
119
120
|
}
|
|
120
121
|
const ownerOrg = await getOrgForHost(hostId);
|
|
121
|
-
const ownerId = ownerOrg == null ? void 0 : (
|
|
122
|
+
const ownerId = ownerOrg == null ? void 0 : (_ownerOrg_org1 = ownerOrg.org) == null ? void 0 : _ownerOrg_org1.ownerUid;
|
|
122
123
|
if (!ownerId) {
|
|
123
124
|
return res.status(409).json({
|
|
124
125
|
error: 'This site cannot sell yet'
|
|
@@ -188,15 +189,20 @@ import { boundedCreditCents, checkoutCreditProviderForCode, encodeCheckoutCredit
|
|
|
188
189
|
const params = new URLSearchParams(_extends({
|
|
189
190
|
mode: 'payment'
|
|
190
191
|
}, checkoutSessionCardAuthenticationParams('payment')));
|
|
192
|
+
// The store's today, for the use days lines carry (AGL-4043): a pass
|
|
193
|
+
// for a day that has passed since it went in the cart is not sold.
|
|
194
|
+
const storeDay = cart.lines.some((line)=>line.useDayKey) ? await readStoreDay(hostRef, {
|
|
195
|
+
org: (_ownerOrg_org = ownerOrg.org) != null ? _ownerOrg_org : null
|
|
196
|
+
}) : null;
|
|
191
197
|
cart.lines.forEach((line, index)=>{
|
|
192
|
-
var _product_type, _variant_id, _variant_sku, _variant_weightGrams, _product_type1, _product_type2, _variant_options, _variant_options1;
|
|
198
|
+
var _ref, _product_type, _variant_id, _variant_sku, _variant_weightGrams, _product_type1, _product_type2, _variant_options, _variant_options1;
|
|
193
199
|
const product = productsById.get(line.productId);
|
|
194
200
|
// Sold in person only (AGL-4060) is not for sale here, whatever the
|
|
195
201
|
// cart was holding when the switch went off.
|
|
196
202
|
if (!product || !CommerceModel.productListedOnline(product)) {
|
|
197
|
-
var
|
|
203
|
+
var _ref1;
|
|
198
204
|
throw Object.assign(new Error('unavailable'), {
|
|
199
|
-
visible: `"${(
|
|
205
|
+
visible: `"${(_ref1 = product == null ? void 0 : product.name) != null ? _ref1 : 'A product'}" is no longer available`
|
|
200
206
|
});
|
|
201
207
|
}
|
|
202
208
|
const variant = line.variantId ? product.variants.find((item)=>item.id === line.variantId) : product.variants[0];
|
|
@@ -212,6 +218,12 @@ import { boundedCreditCents, checkoutCreditProviderForCode, encodeCheckoutCredit
|
|
|
212
218
|
visible: `"${product.name}" is not available`
|
|
213
219
|
});
|
|
214
220
|
}
|
|
221
|
+
const useDayProblem = !line.useDayKey ? null : !CommerceModel.productAsksUseDate(product, variant.id) ? 'It has no day to choose.' : CommerceModel.checkInUseDayProblem(line.useDayKey, (_ref = storeDay == null ? void 0 : storeDay.todayKey) != null ? _ref : '');
|
|
222
|
+
if (useDayProblem) {
|
|
223
|
+
throw Object.assign(new Error('unavailable'), {
|
|
224
|
+
visible: `"${product.name}": ${useDayProblem} Remove it and add it again with a new day.`
|
|
225
|
+
});
|
|
226
|
+
}
|
|
215
227
|
// Gift cards are a Business+ entitlement, checked per sale (AGL-470).
|
|
216
228
|
if (product.giftCard && !Aglyn.checkEntitlement(ownerOrg.org, 'giftCards')) {
|
|
217
229
|
throw Object.assign(new Error('unavailable'), {
|
|
@@ -511,7 +523,7 @@ import { boundedCreditCents, checkoutCreditProviderForCode, encodeCheckoutCredit
|
|
|
511
523
|
const claimed = await claimAttempt(firestore, {
|
|
512
524
|
kind: 'commerce-cart',
|
|
513
525
|
scopeId: `${hostId}:${cartId}`,
|
|
514
|
-
orgId: String((_ref2 = ownerOrg == null ? void 0 : (
|
|
526
|
+
orgId: String((_ref2 = ownerOrg == null ? void 0 : (_ownerOrg_org2 = ownerOrg.org) == null ? void 0 : _ownerOrg_org2.id) != null ? _ref2 : ''),
|
|
515
527
|
key: idempotencyKey,
|
|
516
528
|
busyMessage: 'This checkout is already being processed'
|
|
517
529
|
});
|
|
@@ -1274,7 +1286,7 @@ import { boundedCreditCents, checkoutCreditProviderForCode, encodeCheckoutCredit
|
|
|
1274
1286
|
// and is identical on both checkout paths. This swaps the redirect's URL
|
|
1275
1287
|
// pair for `ui_mode` + `return_url` and stops. Off unless the flag is on for
|
|
1276
1288
|
// this org AND a publishable key exists; see `resolveNativeCheckoutMode`.
|
|
1277
|
-
const nativeMode = await resolveNativeCheckoutMode(String((_ref3 = ownerOrg == null ? void 0 : (
|
|
1289
|
+
const nativeMode = await resolveNativeCheckoutMode(String((_ref3 = ownerOrg == null ? void 0 : (_ownerOrg_org3 = ownerOrg.org) == null ? void 0 : _ownerOrg_org3.id) != null ? _ref3 : ''));
|
|
1278
1290
|
if (nativeMode.native) {
|
|
1279
1291
|
applyNativeCheckoutParams(params, `${backUrl}${separator}order=success&session_id={CHECKOUT_SESSION_ID}`);
|
|
1280
1292
|
}
|