@aglyn/plugins-commerce 1.0.0-beta.284 → 1.0.0-beta.287
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
@@ -0,0 +1,33 @@
|
|
|
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 store's calendar (AGL-4043): its time zone (the site's, else the
|
|
19
|
+
* workspace's, else UTC, as `resolveSiteTimeZone` decides for every day the
|
|
20
|
+
* store counts) and today's day key on it. What a use date a buyer picks is
|
|
21
|
+
* checked against, so "today" is the store's today wherever the buyer is.
|
|
22
|
+
*
|
|
23
|
+
* `org` and `host` are used when the caller already holds them; otherwise
|
|
24
|
+
* they are read.
|
|
25
|
+
*/
|
|
26
|
+
export declare function readStoreDay(hostRef: FirebaseFirestore.DocumentReference, options?: {
|
|
27
|
+
org?: Record<string, unknown> | null;
|
|
28
|
+
host?: Record<string, unknown> | null;
|
|
29
|
+
nowMs?: number;
|
|
30
|
+
}): Promise<{
|
|
31
|
+
timeZone: string;
|
|
32
|
+
todayKey: string;
|
|
33
|
+
}>;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/ import { resolveSiteTimeZone } from "@aglyn/aglyn/app-utils/collection-entry-date";
|
|
17
|
+
import { zonedDayKey } from "@aglyn/shared-util-timestamp/zoned-time";
|
|
18
|
+
import { getOrgForHost } from "@aglyn/tenant-data-admin";
|
|
19
|
+
/**
|
|
20
|
+
* The store's calendar (AGL-4043): its time zone (the site's, else the
|
|
21
|
+
* workspace's, else UTC, as `resolveSiteTimeZone` decides for every day the
|
|
22
|
+
* store counts) and today's day key on it. What a use date a buyer picks is
|
|
23
|
+
* checked against, so "today" is the store's today wherever the buyer is.
|
|
24
|
+
*
|
|
25
|
+
* `org` and `host` are used when the caller already holds them; otherwise
|
|
26
|
+
* they are read.
|
|
27
|
+
*/ export async function readStoreDay(hostRef, options = {}) {
|
|
28
|
+
var _options_nowMs;
|
|
29
|
+
const [host, org] = await Promise.all([
|
|
30
|
+
options.host !== undefined ? options.host : hostRef.get().then((snapshot)=>{
|
|
31
|
+
var _snapshot_data;
|
|
32
|
+
return (_snapshot_data = snapshot.data()) != null ? _snapshot_data : null;
|
|
33
|
+
}),
|
|
34
|
+
options.org !== undefined ? options.org : getOrgForHost(hostRef.id).then((owner)=>{
|
|
35
|
+
var _ref;
|
|
36
|
+
return (_ref = owner == null ? void 0 : owner.org) != null ? _ref : null;
|
|
37
|
+
})
|
|
38
|
+
]);
|
|
39
|
+
const timeZone = resolveSiteTimeZone(org, host);
|
|
40
|
+
return {
|
|
41
|
+
timeZone,
|
|
42
|
+
todayKey: zonedDayKey((_options_nowMs = options.nowMs) != null ? _options_nowMs : Date.now(), timeZone)
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
//# sourceMappingURL=store-day.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/server/store-day.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 { resolveSiteTimeZone } from '@aglyn/aglyn/app-utils/collection-entry-date'\nimport { zonedDayKey } from '@aglyn/shared-util-timestamp/zoned-time'\nimport { getOrgForHost } from '@aglyn/tenant-data-admin'\n\n/**\n * The store's calendar (AGL-4043): its time zone (the site's, else the\n * workspace's, else UTC, as `resolveSiteTimeZone` decides for every day the\n * store counts) and today's day key on it. What a use date a buyer picks is\n * checked against, so \"today\" is the store's today wherever the buyer is.\n *\n * `org` and `host` are used when the caller already holds them; otherwise\n * they are read.\n */\nexport async function readStoreDay(\n hostRef: FirebaseFirestore.DocumentReference,\n options: {\n org?: Record<string, unknown> | null\n host?: Record<string, unknown> | null\n nowMs?: number\n } = {},\n): Promise<{ timeZone: string; todayKey: string }> {\n const [host, org] = await Promise.all([\n options.host !== undefined\n ? options.host\n : hostRef.get().then((snapshot) => (snapshot.data() ?? null) as Record<string, unknown> | null),\n options.org !== undefined\n ? options.org\n : getOrgForHost(hostRef.id).then((owner) => (owner?.org ?? null) as Record<string, unknown> | null),\n ])\n const timeZone = resolveSiteTimeZone(org as { timeZone?: string } | null, host as { timeZone?: string } | null)\n return { timeZone, todayKey: zonedDayKey(options.nowMs ?? Date.now(), timeZone) }\n}\n"],"names":["resolveSiteTimeZone","zonedDayKey","getOrgForHost","readStoreDay","hostRef","options","host","org","Promise","all","undefined","get","then","snapshot","data","id","owner","timeZone","todayKey","nowMs","Date","now"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED,SAASA,mBAAmB,QAAQ,+CAA8C;AAClF,SAASC,WAAW,QAAQ,0CAAyC;AACrE,SAASC,aAAa,QAAQ,2BAA0B;AAExD;;;;;;;;CAQC,GACD,OAAO,eAAeC,aACpBC,OAA4C,EAC5CC,UAII,CAAC,CAAC;QAWmCA;IATzC,MAAM,CAACC,MAAMC,IAAI,GAAG,MAAMC,QAAQC,GAAG,CAAC;QACpCJ,QAAQC,IAAI,KAAKI,YACbL,QAAQC,IAAI,GACZF,QAAQO,GAAG,GAAGC,IAAI,CAAC,CAACC;gBAAcA;oBAAAA,iBAAAA,SAASC,IAAI,cAAbD,iBAAmB;;QACzDR,QAAQE,GAAG,KAAKG,YACZL,QAAQE,GAAG,GACXL,cAAcE,QAAQW,EAAE,EAAEH,IAAI,CAAC,CAACI;;2BAAWA,yBAAAA,MAAOT,GAAG,mBAAI;;KAC9D;IACD,MAAMU,WAAWjB,oBAAoBO,KAAqCD;IAC1E,OAAO;QAAEW;QAAUC,UAAUjB,aAAYI,iBAAAA,QAAQc,KAAK,YAAbd,iBAAiBe,KAAKC,GAAG,IAAIJ;IAAU;AAClF"}
|
|
@@ -18,7 +18,8 @@ import type { PluginDraftContext, PluginResourceDraftWriter } from '@aglyn/aglyn
|
|
|
18
18
|
type Firestore = FirebaseFirestore.Firestore;
|
|
19
19
|
type Stored = Record<string, unknown>;
|
|
20
20
|
/** The refusal for a member without the publishing role (admin or editor). */
|
|
21
|
-
|
|
21
|
+
/** Names "Publish site content" (`site.publish`, AGL-4047): store settings are live the moment they change. */
|
|
22
|
+
export declare const STORE_SETUP_ROLE_REFUSAL: string;
|
|
22
23
|
/** The refusal for a plan without commerce. */
|
|
23
24
|
export declare const STORE_SETUP_PLAN_REFUSAL = "Selling online is not included in your plan \u2014 see Billing";
|
|
24
25
|
/** What a member is told while a lockdown refuses writes to the site. */
|
|
@@ -14,10 +14,10 @@ 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 {
|
|
18
|
-
import {
|
|
17
|
+
*/ import { checkEntitlement } from "@aglyn/aglyn/app-utils/plan-entitlements";
|
|
18
|
+
import { holdsSiteKey, siteKeyRefusal } from "@aglyn/aglyn/app-utils/host-permissions";
|
|
19
19
|
import { applyStoreSetupChange, readStoreSetupChange, storeSetupFacts } from "../model/store-setup-content.js";
|
|
20
|
-
/** The refusal for a member without the publishing role (admin or editor). */
|
|
20
|
+
/** The refusal for a member without the publishing role (admin or editor). */ /** Names "Publish site content" (`site.publish`, AGL-4047): store settings are live the moment they change. */ export const STORE_SETUP_ROLE_REFUSAL = siteKeyRefusal('site.publish');
|
|
21
21
|
/** The refusal for a plan without commerce. */ export const STORE_SETUP_PLAN_REFUSAL = 'Selling online is not included in your plan — see Billing';
|
|
22
22
|
/** What a member is told while a lockdown refuses writes to the site. */ export const STORE_SETUP_LOCKED_REFUSAL = 'This site is not accepting changes right now.';
|
|
23
23
|
const SECTION_NAMES = {
|
|
@@ -41,14 +41,13 @@ export function createStoreSetupAiWriter(deps) {
|
|
|
41
41
|
const hostRef = (hostId)=>deps.firestore().collection('hosts').doc(hostId);
|
|
42
42
|
const settingsRef = (hostId)=>hostRef(hostId).collection('settings').doc('store');
|
|
43
43
|
async function refusalFor(host, context) {
|
|
44
|
-
var _host_data,
|
|
44
|
+
var _host_data, _context_org;
|
|
45
45
|
if (!host.exists) return {
|
|
46
46
|
status: 404,
|
|
47
47
|
error: 'Unknown site'
|
|
48
48
|
};
|
|
49
49
|
const data = (_host_data = host.data()) != null ? _host_data : {};
|
|
50
|
-
|
|
51
|
-
if (!hostRoleCanPublish(role)) return {
|
|
50
|
+
if (!holdsSiteKey(data, context.uid, 'site.publish')) return {
|
|
52
51
|
status: 403,
|
|
53
52
|
error: STORE_SETUP_ROLE_REFUSAL
|
|
54
53
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/server/store-setup-ai-writer.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 { hostRoleCanPublish } from '@aglyn/aglyn/app-utils/organizations'\nimport { checkEntitlement } from '@aglyn/aglyn/app-utils/plan-entitlements'\nimport type {\n PluginDraftContext,\n PluginDraftRecord,\n PluginDraftRefusal,\n PluginDraftWrite,\n PluginResourceDraftWriter,\n} from '@aglyn/aglyn/plugin-manager/plugin-resource-drafts'\nimport {\n applyStoreSetupChange,\n readStoreSetupChange,\n storeSetupFacts,\n type StoreSetupLocation,\n} from '../model/store-setup-content'\n\n/*==========================================\n * THE STORE'S SHIPPING AND TAX SETUP AN AI BUILD CHANGES (AGL-3749)\n *\n * The writer commerce registers for the `store-settings` resource on core's\n * resource-drafts seam, which the `store-shipping`, `store-pickup`,\n * `store-delivery` and `store-tax` build operations execute\n * (`store-setup-ai-capability.ts`). It writes exactly what the console's\n * Shipping, Local delivery, Taxes and Inventory locations cards write:\n *\n * - who: an admin or an editor on the site (`hostRoleCanPublish`), the\n * plan's `commerce` feature, and no lockdown refusing a write to the site, its workspace or\n * the member (`hostWritesFrozen`). The role is stricter than the cards,\n * which the Firestore rules let an author save too (`canWriteHostContent`):\n * these settings change what checkout charges, and an AI change of them\n * is live at once, so it takes the publishing role (Zach, 2026-10-12);\n * - what: the change is read and applied by `store-setup-content.ts`,\n * through the cards' own normalizers and checks — the zone-coverage\n * summary, the pickup and delivery normalizers, delivery's \"can't offer\n * it yet\" problems, and the manual-tax-without-an-origin refusal;\n * - how: one map of `settings/store` replaced whole (`mergeFields`, as the\n * Local delivery card saves), or one location's `pickup` (as the\n * Inventory locations card saves), inside a transaction, so two items of\n * one plan changing the same map never lose each other's change.\n *\n * Store settings have no draft state: the cards save them live, and so does\n * this. The plan card is where the person agrees to the change.\n *\n * Asked again for the same change, it writes nothing: a zone, a rate, a\n * delivery zone and a tax rate are found by name or region before anything\n * is added, a new one takes an id derived from the request, and a change the\n * settings already hold is reported as done (`replayed`).\n *\n * Nothing here connects an account or stores a credential: carrier rates and\n * outside tax services are set up by a person in their own cards.\n *=========================================*/\n\ntype Firestore = FirebaseFirestore.Firestore\ntype Stored = Record<string, unknown>\n\n/** The refusal for a member without the publishing role (admin or editor). */\nexport const STORE_SETUP_ROLE_REFUSAL = 'Changing store settings through AI needs an admin or editor role'\n/** The refusal for a plan without commerce. */\nexport const STORE_SETUP_PLAN_REFUSAL = 'Selling online is not included in your plan — see Billing'\n/** What a member is told while a lockdown refuses writes to the site. */\nexport const STORE_SETUP_LOCKED_REFUSAL = 'This site is not accepting changes right now.'\n\n/** Whether a lockdown refuses this member's write to this site; `true` refuses. */\nexport type StoreSetupLockdownCheck = (input: {\n uid: string\n org: PluginDraftContext['org']\n host: Stored\n}) => Promise<boolean>\n\nexport interface StoreSetupAiWriterDeps {\n firestore: () => Firestore\n lockdown: StoreSetupLockdownCheck\n}\n\nconst SECTION_NAMES: Record<string, string> = {\n shipping: 'Shipping',\n pickup: 'Local pickup',\n 'local-delivery': 'Local delivery',\n tax: 'Taxes',\n}\n\nfunction locationOf(id: string, data: Stored): StoreSetupLocation {\n const postal = (data['postalAddress'] ?? {}) as Stored\n return {\n id,\n name: typeof data['name'] === 'string' ? data['name'] : undefined,\n address: typeof data['address'] === 'string' ? data['address'] : undefined,\n city: typeof postal['city'] === 'string' ? (postal['city'] as string) : undefined,\n pickup: data['pickup'],\n }\n}\n\nexport function createStoreSetupAiWriter(deps: StoreSetupAiWriterDeps): PluginResourceDraftWriter {\n const hostRef = (hostId: string) => deps.firestore().collection('hosts').doc(hostId)\n const settingsRef = (hostId: string) => hostRef(hostId).collection('settings').doc('store')\n\n async function refusalFor(\n host: FirebaseFirestore.DocumentSnapshot,\n context: Pick<PluginDraftContext, 'uid' | 'org'>,\n ): Promise<PluginDraftRefusal | null> {\n if (!host.exists) return { status: 404, error: 'Unknown site' }\n const data = (host.data() ?? {}) as Stored\n const role = ((data['memberRoles'] ?? {}) as Stored)[context.uid]\n if (!hostRoleCanPublish(role)) return { status: 403, error: STORE_SETUP_ROLE_REFUSAL }\n if (!checkEntitlement((context.org ?? {}) as never, 'commerce')) {\n return { status: 403, error: STORE_SETUP_PLAN_REFUSAL }\n }\n if (await deps.lockdown({ uid: context.uid, org: context.org, host: data })) {\n return { status: 403, error: STORE_SETUP_LOCKED_REFUSAL }\n }\n return null\n }\n\n return {\n refusal: async (context) => refusalFor(await hostRef(context.hostId).get(), context),\n\n check: (content) => {\n const read = readStoreSetupChange(content)\n if (read.ok === false) return { ok: false, problems: read.problems }\n return { ok: true, facts: { section: read.value.section, live: true } }\n },\n\n // The settings are one document, not one per change: whatever id is\n // asked, the store's setup as it stands.\n read: async ({ hostId, id }): Promise<PluginDraftRecord | null> => {\n const snapshot = await settingsRef(hostId).get()\n if (!snapshot.exists) return null\n return { id, name: 'Store settings', versionId: null, facts: storeSetupFacts((snapshot.data() ?? {}) as Stored) }\n },\n\n write: async (request): Promise<PluginDraftWrite> => {\n const read = readStoreSetupChange(request.content)\n if (read.ok === false) return { ok: false, status: 400, error: read.problems[0] as string }\n const change = read.value\n const refusal = await refusalFor(await hostRef(request.hostId).get(), request)\n if (refusal) return { ok: false, ...refusal }\n const needsLocations = 'location' in change && Boolean(change.location)\n const db = deps.firestore()\n const ref = settingsRef(request.hostId)\n const locations = hostRef(request.hostId).collection('locations')\n return db.runTransaction(async (tx): Promise<PluginDraftWrite> => {\n // Every read before any write, which Firestore requires.\n const [settings, places] = await Promise.all([\n tx.get(ref),\n needsLocations ? tx.get(locations) : Promise.resolve(null),\n ])\n const applied = applyStoreSetupChange(\n {\n settings: (settings.data() ?? {}) as Stored,\n locations: (places?.docs ?? []).map((doc) => locationOf(doc.id, (doc.data() ?? {}) as Stored)),\n },\n change,\n { zoneId: `${request.id}-zone`, rateId: `${request.id}-rate` },\n )\n if (applied.ok === false) return { ok: false, status: applied.status, error: applied.error }\n const write = applied.write\n if (write?.target === 'settings') {\n tx.set(ref, { [write.field]: write.value }, { mergeFields: [write.field] })\n } else if (write?.target === 'location') {\n // The card's `updateDoc`: the map replaced whole, so a cleared field is cleared.\n tx.update(locations.doc(write.locationId), { pickup: write.value })\n }\n return {\n ok: true,\n replayed: write === null,\n // One per item: the settings are one document, so the item names the change.\n id: request.id,\n name: request.name.trim() || SECTION_NAMES[change.section] || 'Store settings',\n versionId: null,\n facts: { section: change.section, summary: applied.summary, warnings: applied.warnings, live: true },\n }\n })\n },\n }\n}\n"],"names":["hostRoleCanPublish","checkEntitlement","applyStoreSetupChange","readStoreSetupChange","storeSetupFacts","STORE_SETUP_ROLE_REFUSAL","STORE_SETUP_PLAN_REFUSAL","STORE_SETUP_LOCKED_REFUSAL","SECTION_NAMES","shipping","pickup","tax","locationOf","id","data","postal","name","undefined","address","city","createStoreSetupAiWriter","deps","hostRef","hostId","firestore","collection","doc","settingsRef","refusalFor","host","context","exists","status","error","role","uid","org","lockdown","refusal","get","check","content","read","ok","problems","facts","section","value","live","snapshot","versionId","write","request","change","needsLocations","Boolean","location","db","ref","locations","runTransaction","tx","settings","places","Promise","all","resolve","applied","docs","map","zoneId","rateId","target","set","field","mergeFields","update","locationId","replayed","trim","summary","warnings"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/server/store-setup-ai-writer.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 { checkEntitlement } from '@aglyn/aglyn/app-utils/plan-entitlements'\nimport { holdsSiteKey, siteKeyRefusal } from '@aglyn/aglyn/app-utils/host-permissions'\nimport type {\n PluginDraftContext,\n PluginDraftRecord,\n PluginDraftRefusal,\n PluginDraftWrite,\n PluginResourceDraftWriter,\n} from '@aglyn/aglyn/plugin-manager/plugin-resource-drafts'\nimport {\n applyStoreSetupChange,\n readStoreSetupChange,\n storeSetupFacts,\n type StoreSetupLocation,\n} from '../model/store-setup-content'\n\n/*==========================================\n * THE STORE'S SHIPPING AND TAX SETUP AN AI BUILD CHANGES (AGL-3749)\n *\n * The writer commerce registers for the `store-settings` resource on core's\n * resource-drafts seam, which the `store-shipping`, `store-pickup`,\n * `store-delivery` and `store-tax` build operations execute\n * (`store-setup-ai-capability.ts`). It writes exactly what the console's\n * Shipping, Local delivery, Taxes and Inventory locations cards write:\n *\n * - who: an admin or an editor on the site (`hostRoleCanPublish`), the\n * plan's `commerce` feature, and no lockdown refusing a write to the site, its workspace or\n * the member (`hostWritesFrozen`). The role is stricter than the cards,\n * which the Firestore rules let an author save too (`canWriteHostContent`):\n * these settings change what checkout charges, and an AI change of them\n * is live at once, so it takes the publishing role (Zach, 2026-10-12);\n * - what: the change is read and applied by `store-setup-content.ts`,\n * through the cards' own normalizers and checks — the zone-coverage\n * summary, the pickup and delivery normalizers, delivery's \"can't offer\n * it yet\" problems, and the manual-tax-without-an-origin refusal;\n * - how: one map of `settings/store` replaced whole (`mergeFields`, as the\n * Local delivery card saves), or one location's `pickup` (as the\n * Inventory locations card saves), inside a transaction, so two items of\n * one plan changing the same map never lose each other's change.\n *\n * Store settings have no draft state: the cards save them live, and so does\n * this. The plan card is where the person agrees to the change.\n *\n * Asked again for the same change, it writes nothing: a zone, a rate, a\n * delivery zone and a tax rate are found by name or region before anything\n * is added, a new one takes an id derived from the request, and a change the\n * settings already hold is reported as done (`replayed`).\n *\n * Nothing here connects an account or stores a credential: carrier rates and\n * outside tax services are set up by a person in their own cards.\n *=========================================*/\n\ntype Firestore = FirebaseFirestore.Firestore\ntype Stored = Record<string, unknown>\n\n/** The refusal for a member without the publishing role (admin or editor). */\n/** Names \"Publish site content\" (`site.publish`, AGL-4047): store settings are live the moment they change. */\nexport const STORE_SETUP_ROLE_REFUSAL = siteKeyRefusal('site.publish')\n/** The refusal for a plan without commerce. */\nexport const STORE_SETUP_PLAN_REFUSAL = 'Selling online is not included in your plan — see Billing'\n/** What a member is told while a lockdown refuses writes to the site. */\nexport const STORE_SETUP_LOCKED_REFUSAL = 'This site is not accepting changes right now.'\n\n/** Whether a lockdown refuses this member's write to this site; `true` refuses. */\nexport type StoreSetupLockdownCheck = (input: {\n uid: string\n org: PluginDraftContext['org']\n host: Stored\n}) => Promise<boolean>\n\nexport interface StoreSetupAiWriterDeps {\n firestore: () => Firestore\n lockdown: StoreSetupLockdownCheck\n}\n\nconst SECTION_NAMES: Record<string, string> = {\n shipping: 'Shipping',\n pickup: 'Local pickup',\n 'local-delivery': 'Local delivery',\n tax: 'Taxes',\n}\n\nfunction locationOf(id: string, data: Stored): StoreSetupLocation {\n const postal = (data['postalAddress'] ?? {}) as Stored\n return {\n id,\n name: typeof data['name'] === 'string' ? data['name'] : undefined,\n address: typeof data['address'] === 'string' ? data['address'] : undefined,\n city: typeof postal['city'] === 'string' ? (postal['city'] as string) : undefined,\n pickup: data['pickup'],\n }\n}\n\nexport function createStoreSetupAiWriter(deps: StoreSetupAiWriterDeps): PluginResourceDraftWriter {\n const hostRef = (hostId: string) => deps.firestore().collection('hosts').doc(hostId)\n const settingsRef = (hostId: string) => hostRef(hostId).collection('settings').doc('store')\n\n async function refusalFor(\n host: FirebaseFirestore.DocumentSnapshot,\n context: Pick<PluginDraftContext, 'uid' | 'org'>,\n ): Promise<PluginDraftRefusal | null> {\n if (!host.exists) return { status: 404, error: 'Unknown site' }\n const data = (host.data() ?? {}) as Stored\n if (!holdsSiteKey(data, context.uid, 'site.publish')) return { status: 403, error: STORE_SETUP_ROLE_REFUSAL }\n if (!checkEntitlement((context.org ?? {}) as never, 'commerce')) {\n return { status: 403, error: STORE_SETUP_PLAN_REFUSAL }\n }\n if (await deps.lockdown({ uid: context.uid, org: context.org, host: data })) {\n return { status: 403, error: STORE_SETUP_LOCKED_REFUSAL }\n }\n return null\n }\n\n return {\n refusal: async (context) => refusalFor(await hostRef(context.hostId).get(), context),\n\n check: (content) => {\n const read = readStoreSetupChange(content)\n if (read.ok === false) return { ok: false, problems: read.problems }\n return { ok: true, facts: { section: read.value.section, live: true } }\n },\n\n // The settings are one document, not one per change: whatever id is\n // asked, the store's setup as it stands.\n read: async ({ hostId, id }): Promise<PluginDraftRecord | null> => {\n const snapshot = await settingsRef(hostId).get()\n if (!snapshot.exists) return null\n return { id, name: 'Store settings', versionId: null, facts: storeSetupFacts((snapshot.data() ?? {}) as Stored) }\n },\n\n write: async (request): Promise<PluginDraftWrite> => {\n const read = readStoreSetupChange(request.content)\n if (read.ok === false) return { ok: false, status: 400, error: read.problems[0] as string }\n const change = read.value\n const refusal = await refusalFor(await hostRef(request.hostId).get(), request)\n if (refusal) return { ok: false, ...refusal }\n const needsLocations = 'location' in change && Boolean(change.location)\n const db = deps.firestore()\n const ref = settingsRef(request.hostId)\n const locations = hostRef(request.hostId).collection('locations')\n return db.runTransaction(async (tx): Promise<PluginDraftWrite> => {\n // Every read before any write, which Firestore requires.\n const [settings, places] = await Promise.all([\n tx.get(ref),\n needsLocations ? tx.get(locations) : Promise.resolve(null),\n ])\n const applied = applyStoreSetupChange(\n {\n settings: (settings.data() ?? {}) as Stored,\n locations: (places?.docs ?? []).map((doc) => locationOf(doc.id, (doc.data() ?? {}) as Stored)),\n },\n change,\n { zoneId: `${request.id}-zone`, rateId: `${request.id}-rate` },\n )\n if (applied.ok === false) return { ok: false, status: applied.status, error: applied.error }\n const write = applied.write\n if (write?.target === 'settings') {\n tx.set(ref, { [write.field]: write.value }, { mergeFields: [write.field] })\n } else if (write?.target === 'location') {\n // The card's `updateDoc`: the map replaced whole, so a cleared field is cleared.\n tx.update(locations.doc(write.locationId), { pickup: write.value })\n }\n return {\n ok: true,\n replayed: write === null,\n // One per item: the settings are one document, so the item names the change.\n id: request.id,\n name: request.name.trim() || SECTION_NAMES[change.section] || 'Store settings',\n versionId: null,\n facts: { section: change.section, summary: applied.summary, warnings: applied.warnings, live: true },\n }\n })\n },\n }\n}\n"],"names":["checkEntitlement","holdsSiteKey","siteKeyRefusal","applyStoreSetupChange","readStoreSetupChange","storeSetupFacts","STORE_SETUP_ROLE_REFUSAL","STORE_SETUP_PLAN_REFUSAL","STORE_SETUP_LOCKED_REFUSAL","SECTION_NAMES","shipping","pickup","tax","locationOf","id","data","postal","name","undefined","address","city","createStoreSetupAiWriter","deps","hostRef","hostId","firestore","collection","doc","settingsRef","refusalFor","host","context","exists","status","error","uid","org","lockdown","refusal","get","check","content","read","ok","problems","facts","section","value","live","snapshot","versionId","write","request","change","needsLocations","Boolean","location","db","ref","locations","runTransaction","tx","settings","places","Promise","all","resolve","applied","docs","map","zoneId","rateId","target","set","field","mergeFields","update","locationId","replayed","trim","summary","warnings"],"mappings":"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"}
|
|
@@ -150,6 +150,19 @@ export interface StorefrontTaxRow {
|
|
|
150
150
|
taxLines: StorefrontTaxLine[];
|
|
151
151
|
paidAt: Date | null;
|
|
152
152
|
}
|
|
153
|
+
/**
|
|
154
|
+
* The breakdown entries, from either shape.
|
|
155
|
+
*
|
|
156
|
+
* A Checkout Session expands them under `total_details.breakdown.taxes[]`
|
|
157
|
+
* with the rate object inline; an invoice carries `total_taxes[]` (rate under
|
|
158
|
+
* `tax_rate_details.tax_rate`) or the older `total_tax_amounts[]` (`tax_rate`,
|
|
159
|
+
* an id or an expanded object). All three map onto one stored line.
|
|
160
|
+
*
|
|
161
|
+
* Exported for the POS hardware shop's own sales (AGL-4038), whose tax rows
|
|
162
|
+
* keep the same lines: one reading of Stripe's breakdown for every half of
|
|
163
|
+
* the return.
|
|
164
|
+
*/
|
|
165
|
+
export declare function stripeTaxBreakdownLines(source: any): StorefrontTaxLine[];
|
|
153
166
|
export interface StorefrontTaxContext {
|
|
154
167
|
kind: 'session' | 'invoice';
|
|
155
168
|
hostId: string;
|
|
@@ -122,7 +122,14 @@ function address(source) {
|
|
|
122
122
|
* with the rate object inline; an invoice carries `total_taxes[]` (rate under
|
|
123
123
|
* `tax_rate_details.tax_rate`) or the older `total_tax_amounts[]` (`tax_rate`,
|
|
124
124
|
* an id or an expanded object). All three map onto one stored line.
|
|
125
|
-
|
|
125
|
+
*
|
|
126
|
+
* Exported for the POS hardware shop's own sales (AGL-4038), whose tax rows
|
|
127
|
+
* keep the same lines: one reading of Stripe's breakdown for every half of
|
|
128
|
+
* the return.
|
|
129
|
+
*/ export function stripeTaxBreakdownLines(source) {
|
|
130
|
+
return taxLines(source);
|
|
131
|
+
}
|
|
132
|
+
function taxLines(source) {
|
|
126
133
|
var _source_total_details_breakdown, _source_total_details;
|
|
127
134
|
const entries = Array.isArray(source == null ? void 0 : (_source_total_details = source.total_details) == null ? void 0 : (_source_total_details_breakdown = _source_total_details.breakdown) == null ? void 0 : _source_total_details_breakdown.taxes) ? source.total_details.breakdown.taxes : Array.isArray(source == null ? void 0 : source.total_taxes) ? source.total_taxes : Array.isArray(source == null ? void 0 : source.total_tax_amounts) ? source.total_tax_amounts : [];
|
|
128
135
|
return entries.map((entry)=>{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/server/storefront-tax.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 * What a storefront sale's TAX decomposes to (AGL-1904) — the record that\n * makes tax collected on a merchant's sale visible to Aglyn's own return.\n *\n * ## The observation this module exists to record\n *\n * Every storefront checkout — cart, buy-now, subscription — creates its\n * Checkout Session on the PLATFORM account: platform secret key, no\n * `Stripe-Account` header, no `on_behalf_of`, with\n * `payment_intent_data[transfer_data][destination]` pointing at the merchant's\n * connected account. `cart-checkout.ts` and `checkout.ts` then set\n * `automatic_tax[enabled]` when the store's tax mode is `stripe`.\n *\n * That was long suspected to mean Stripe Tax computes the shopper's tax\n * against AGLYN's registrations rather than the merchant's. It was MEASURED on\n * 2026-08-18, in test mode, as a two-arm experiment on exactly that session\n * shape (a $100 line, a Texas shopper address, `transfer_data[destination]`\n * set):\n *\n * - **Platform unregistered, destination connected account registered in\n * TX** → `amount_tax: 0`, one breakdown line with\n * `taxability_reason: \"not_collecting\"`, `taxable_amount: 0`.\n * - **Platform registered in TX, everything else byte-identical** →\n * `amount_tax: 825`, `taxability_reason: \"standard_rated\"`,\n * `taxable_amount: 10000`, rate 8.25% \"Texas\".\n *\n * Both sessions reported `automatic_tax.liability: { type: \"self\" }` — Stripe's\n * own word for \"the account this session was created on is the one liable\".\n * `transfer_data[destination]` had no effect on tax in either arm.\n *\n * So on a `mode: 'stripe'` store the tax charged to the shopper is computed on\n * Aglyn's registration footprint, lands in Aglyn's platform balance, and is\n * Aglyn's to remit. The live platform account has held an ACTIVE Texas state\n * sales tax registration since 2026-08-17, so this is not hypothetical.\n *\n * **This module describes that mechanism and records the money.** Aglyn IS a\n * marketplace facilitator for storefront sales, and the published Terms say so\n * at §10.7 (AGL-1956). Facilitator status still attaches by operation of\n * law rather than by our say-so, and §10.7 is deliberately narrow — Aglyn\n * collects and remits **where applicable law gives it an obligation**, not\n * universally — so this module still records the mechanism per sale and does\n * not stamp a per-merchant or per-jurisdiction verdict on any row.\n *\n * ## The two store modes are DIFFERENT FACTS and must never be summed\n *\n * `taxMode` is the whole point of this record, and it is derived from\n * `automatic_tax.enabled` — **never** from whether tax lines are present:\n *\n * - **`stripe-automatic`** — Stripe Tax computed it, against whichever\n * account `automatic_tax.liability` names. When that is `platform`, the\n * money is held under Aglyn's registration.\n * - **`manual`** — the merchant's own configured rate\n * (`checkout.ts` / `pos-order.ts`), applied to the merchant's own declared\n * origin. Aglyn's registrations never enter into it and the amount is not\n * Aglyn's to remit.\n *\n * The trap that makes the `automatic_tax.enabled` discriminator load-bearing:\n * a MANUAL-mode subscription renewal carries a real Stripe Tax Rate (AGL-1751\n * attaches one so the tax recurs), so its invoice arrives with a populated\n * `total_taxes[]` — indistinguishable from a Stripe Tax invoice if you look at\n * the tax lines. Summing tax lines would silently book merchant-configured tax\n * as Aglyn-collected. **Read the flag, not the lines.**\n *\n * ## The taxable base is kept, never inferred\n *\n * `taxable_amount` is what a return reports as \"taxable sales\". Deriving it as\n * `amount ÷ rate` needs a live read to resolve the rate id and rounds wrong at\n * the edges, so it is stored when Stripe states it and left `null` when it\n * does not — and a row whose base cannot be read is COUNTED OUT LOUD by the\n * return rather than treated as a zero-base sale.\n *\n * A Checkout Session's `total_details.breakdown` is not in the webhook payload\n * and must be re-read with `expand[]=total_details.breakdown`; an invoice\n * carries `total_taxes[]` inline. Both land in the same `taxLines` shape.\n *\n * Pure and total: unreadable input answers `null`, never throws — a recording\n * failure must never 500 a webhook into redelivery.\n */\n\nimport {\n storefrontTaxMode,\n type StorefrontTaxMode,\n} from '../model/commerce-tax-decision'\n\n/**\n * Which machinery produced the tax on this sale. See the module note.\n *\n * The union and its derivation live in the MODEL since AGL-2451, because the\n * merchant's order document now carries the same fact (`HostOrder.taxMode`)\n * and two copies of a rule that must agree are one copy too many. Re-exported\n * here so this module stays the place the mechanism is documented.\n */\nexport type { StorefrontTaxMode }\n\n/**\n * Whose registrations Stripe Tax computed against, from\n * `automatic_tax.liability.type`: `self` (the account the object lives on,\n * i.e. Aglyn's platform) or `account` (a named connected account).\n * Null whenever `taxMode` is not `stripe-automatic`.\n */\nexport type StorefrontTaxLiability = 'platform' | 'connected-account'\n\n/** One entry of the stored tax breakdown. */\nexport interface StorefrontTaxLine {\n amountCents: number\n /** Stripe's stated reason, e.g. `standard_rated`, `not_collecting`. */\n taxabilityReason: string | null\n taxRateId: string | null\n /** Stripe's `taxable_amount`; null when the shape did not carry it. */\n taxableAmountCents: number | null\n /** e.g. `Texas` — from the expanded rate, for the working papers. */\n jurisdiction: string | null\n /** The rate's own state code, which is not the shopper's address. */\n rateState: string | null\n percentage: number | null\n}\n\n/** The per-transaction record `storefrontTaxCollected/{stripeId}` stores. */\nexport interface StorefrontTaxRow {\n /** `session` for one-time sales, `invoice` for subscription cycles. */\n kind: 'session' | 'invoice'\n hostId: string\n /** The commerce path — `commerce-cart`, `commerce-order`, … */\n metadataType: string | null\n taxMode: StorefrontTaxMode\n taxLiability: StorefrontTaxLiability | null\n /** The merchant's connected account, when the object names one. */\n connectedAccountId: string | null\n /** What the shopper actually paid, tax included. */\n grossCents: number\n taxCents: number\n /** Gross minus tax. NOT Aglyn's revenue — most of it is the merchant's. */\n netCents: number\n currency: string\n /** The jurisdiction input — the shopper's address on the Stripe object. */\n customerAddress: {\n country: string | null\n state: string | null\n city: string | null\n postalCode: string | null\n } | null\n taxLines: StorefrontTaxLine[]\n paidAt: Date | null\n}\n\nfunction asCents(value: unknown): number {\n const cents = Number(value ?? 0)\n return Number.isFinite(cents) ? Math.round(cents) : 0\n}\n\nfunction asStringOrNull(value: unknown): string | null {\n return typeof value === 'string' && value ? value : null\n}\n\nfunction asNumberOrNull(value: unknown): number | null {\n const parsed = Number(value ?? NaN)\n return Number.isFinite(parsed) ? parsed : null\n}\n\n/** Seconds-since-epoch to Date, or null for anything unreadable. */\nfunction asDate(value: unknown): Date | null {\n const seconds = Number(value ?? NaN)\n return Number.isFinite(seconds) && seconds > 0\n ? new Date(seconds * 1000)\n : null\n}\n\nfunction address(source: any): StorefrontTaxRow['customerAddress'] {\n if (!source || typeof source !== 'object') return null\n return {\n country: asStringOrNull(source.country),\n state: asStringOrNull(source.state),\n city: asStringOrNull(source.city),\n postalCode: asStringOrNull(source.postal_code),\n }\n}\n\n/**\n * The breakdown entries, from either shape.\n *\n * A Checkout Session expands them under `total_details.breakdown.taxes[]`\n * with the rate object inline; an invoice carries `total_taxes[]` (rate under\n * `tax_rate_details.tax_rate`) or the older `total_tax_amounts[]` (`tax_rate`,\n * an id or an expanded object). All three map onto one stored line.\n */\nfunction taxLines(source: any): StorefrontTaxLine[] {\n const entries: any[] = Array.isArray(source?.total_details?.breakdown?.taxes)\n ? source.total_details.breakdown.taxes\n : Array.isArray(source?.total_taxes)\n ? source.total_taxes\n : Array.isArray(source?.total_tax_amounts)\n ? source.total_tax_amounts\n : []\n return entries.map((entry: any) => {\n const rate = entry?.rate ?? entry?.tax_rate_details?.tax_rate ?? entry?.tax_rate\n const rateObject = rate && typeof rate === 'object' ? rate : null\n // Distinguish an absent `taxable_amount` from a genuine zero: a zero base\n // is a real fact (`not_collecting`), an absent one means the base is\n // UNKNOWN, and the return has to be able to tell those apart.\n const taxableAmount = Number(entry?.taxable_amount ?? NaN)\n return {\n amountCents: asCents(entry?.amount),\n taxabilityReason: asStringOrNull(entry?.taxability_reason),\n taxRateId: asStringOrNull(rate) ?? asStringOrNull(rateObject?.id),\n taxableAmountCents: Number.isFinite(taxableAmount)\n ? Math.round(taxableAmount)\n : null,\n jurisdiction: asStringOrNull(rateObject?.jurisdiction),\n rateState: asStringOrNull(rateObject?.state),\n percentage:\n asNumberOrNull(rateObject?.effective_percentage) ??\n asNumberOrNull(rateObject?.percentage),\n }\n })\n}\n\n/** Sum of the stored lines' amounts. */\nfunction sumLines(lines: readonly StorefrontTaxLine[]): number {\n return lines.reduce((sum, line) => sum + line.amountCents, 0)\n}\n\nexport interface StorefrontTaxContext {\n kind: 'session' | 'invoice'\n hostId: string\n /**\n * The manual-mode tax `checkout.ts` puts over as an ordinary line item.\n * Stripe is never told it is tax, so no Stripe field carries it and the\n * session's own `metadata[taxCents]` is the only witness (AGL-1711).\n */\n manualTaxCents?: number\n}\n\n/** What one Stripe object says about the tax it carried. */\nexport interface ResolvedStorefrontTax {\n /** `automatic_tax.enabled` — the discriminator, never the tax lines. */\n automatic: boolean\n /** Stripe's own breakdown, present only on a `stripe-automatic` sale. */\n lines: StorefrontTaxLine[]\n taxCents: number\n taxMode: StorefrontTaxMode\n}\n\n/**\n * Reads the tax off one paid session or invoice — the SINGLE derivation, and\n * the reason it is its own exported function (AGL-2451).\n *\n * `storefrontTaxRow` builds the `storefrontTaxCollected` record from it, and\n * the four order-writing doors in `billing-webhook.ts` stamp `HostOrder.taxMode`\n * from it, so an order and the row filed for the same Stripe id cannot state\n * different regimes. Re-deriving the mode beside a second reading of the same\n * object is exactly how the two would drift.\n *\n * Pure and total: unreadable input answers `taxMode: 'none'`, never throws.\n */\nexport function resolveStorefrontTax(\n object: unknown,\n manualTaxCents = 0,\n): ResolvedStorefrontTax {\n const source = object as any\n const automatic = source?.automatic_tax?.enabled === true\n const lines = automatic ? taxLines(source) : []\n const manualCents = Math.max(0, asCents(manualTaxCents))\n // The flag, never the lines — see the module note. A manual-mode\n // subscription renewal carries real Stripe tax rates in `total_taxes[]`,\n // and booking those as Aglyn-collected would be the whole defect restated.\n //\n // Read on SESSIONS as well as invoices since AGL-1953: the cart and\n // draft-order paths now carry their manual tax as a real Stripe Tax Rate on\n // the line (the construction that survives a session-level coupon), so a\n // manual session reports a genuine `total_details.amount_tax`. The scalar is\n // the fallback because `total_details.breakdown` is expandable and absent\n // unless the recorder re-read it. Buy-now's `line_items[1]` construction\n // reports 0 in both shapes and still answers from `manualTaxCents` below.\n const manualLineCents = !automatic\n ? sumLines(taxLines(source)) ||\n asCents(source?.total_details?.amount_tax ?? source?.tax)\n : 0\n const taxCents = automatic\n ? // `total_details.amount_tax` on a session, `total_taxes[]` on an\n // invoice. Prefer the summed lines when they exist (a computed invoice\n // can send a 0/null scalar beside a populated array — AGL-1811's\n // measurement), and fall back to the scalar shapes otherwise.\n lines.length > 0\n ? sumLines(lines)\n : asCents(source?.total_details?.amount_tax ?? source?.tax)\n : Math.max(manualCents, manualLineCents)\n return {\n automatic,\n lines,\n taxCents,\n taxMode: storefrontTaxMode({ automatic, taxCents }),\n }\n}\n\n/**\n * Just the regime, for the order-writing doors (AGL-2451). Delegates, so this\n * is the same answer the filed tax record gives for the same object.\n */\nexport function storefrontTaxModeOf(\n object: unknown,\n manualTaxCents = 0,\n): StorefrontTaxMode {\n return resolveStorefrontTax(object, manualTaxCents).taxMode\n}\n\n/**\n * Decomposes a paid storefront session or invoice into the stored row, or\n * `null` when there is no host to attribute it to.\n *\n * The caller decides WHICH objects reach this — the commerce webhook\n * self-selects on `metadata.type`, exactly as every other section does.\n */\nexport function storefrontTaxRow(\n object: unknown,\n context: StorefrontTaxContext,\n): StorefrontTaxRow | null {\n const source = object as any\n const hostId = String(context?.hostId ?? '')\n if (!hostId) return null\n\n const { automatic, lines, taxCents, taxMode } = resolveStorefrontTax(\n source,\n context?.manualTaxCents,\n )\n\n const liabilityType = asStringOrNull(source?.automatic_tax?.liability?.type)\n const taxLiability: StorefrontTaxLiability | null = !automatic\n ? null\n : liabilityType === 'account'\n ? 'connected-account'\n : // `self`, and also an absent liability: Stripe omits the field on\n // older API versions, where the only account a session could be\n // liable under is the one it was created on — the platform.\n 'platform'\n\n const grossCents = asCents(\n context.kind === 'invoice' ? source?.amount_paid : source?.amount_total,\n )\n const transferDestination =\n source?.payment_intent_data?.transfer_data?.destination ??\n source?.transfer_data?.destination\n return {\n kind: context.kind,\n hostId,\n metadataType:\n asStringOrNull(source?.metadata?.type) ??\n asStringOrNull(source?.subscription_details?.metadata?.type) ??\n asStringOrNull(source?.parent?.subscription_details?.metadata?.type),\n taxMode,\n taxLiability,\n connectedAccountId:\n asStringOrNull(transferDestination) ??\n asStringOrNull((transferDestination as any)?.id),\n grossCents,\n taxCents,\n netCents: grossCents - taxCents,\n currency: asStringOrNull(source?.currency) ?? 'usd',\n customerAddress:\n address(source?.customer_details?.address) ??\n address(source?.customer_address),\n // Stripe's OWN breakdown, whenever Stripe stated one — never an invented\n // base. Buy-now's manual construction states none (Stripe was never told\n // the line was tax) and answers `[]`, which is the honest answer. The\n // cart, draft-order and subscription-renewal manual paths carry a real\n // Tax Rate, so Stripe DOES state a `taxable_amount` for them, and keeping\n // it gives `merchantManual` a real taxable-sales figure instead of a\n // permanent zero (AGL-1953). It never reaches the Aglyn-liable bucket:\n // the classification above is the flag, not these lines.\n taxLines: automatic ? lines : taxLines(source),\n paidAt:\n asDate(source?.status_transitions?.paid_at) ?? asDate(source?.created),\n }\n}\n"],"names":["storefrontTaxMode","asCents","value","cents","Number","isFinite","Math","round","asStringOrNull","asNumberOrNull","parsed","NaN","asDate","seconds","Date","address","source","country","state","city","postalCode","postal_code","taxLines","entries","Array","isArray","total_details","breakdown","taxes","total_taxes","total_tax_amounts","map","entry","rate","tax_rate_details","tax_rate","rateObject","taxableAmount","taxable_amount","amountCents","amount","taxabilityReason","taxability_reason","taxRateId","id","taxableAmountCents","jurisdiction","rateState","percentage","effective_percentage","sumLines","lines","reduce","sum","line","resolveStorefrontTax","object","manualTaxCents","automatic","automatic_tax","enabled","manualCents","max","manualLineCents","amount_tax","tax","taxCents","length","taxMode","storefrontTaxModeOf","storefrontTaxRow","context","hostId","String","liabilityType","liability","type","taxLiability","grossCents","kind","amount_paid","amount_total","transferDestination","payment_intent_data","transfer_data","destination","metadataType","metadata","subscription_details","parent","connectedAccountId","netCents","currency","customerAddress","customer_details","customer_address","paidAt","status_transitions","paid_at","created"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/server/storefront-tax.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 * What a storefront sale's TAX decomposes to (AGL-1904) — the record that\n * makes tax collected on a merchant's sale visible to Aglyn's own return.\n *\n * ## The observation this module exists to record\n *\n * Every storefront checkout — cart, buy-now, subscription — creates its\n * Checkout Session on the PLATFORM account: platform secret key, no\n * `Stripe-Account` header, no `on_behalf_of`, with\n * `payment_intent_data[transfer_data][destination]` pointing at the merchant's\n * connected account. `cart-checkout.ts` and `checkout.ts` then set\n * `automatic_tax[enabled]` when the store's tax mode is `stripe`.\n *\n * That was long suspected to mean Stripe Tax computes the shopper's tax\n * against AGLYN's registrations rather than the merchant's. It was MEASURED on\n * 2026-08-18, in test mode, as a two-arm experiment on exactly that session\n * shape (a $100 line, a Texas shopper address, `transfer_data[destination]`\n * set):\n *\n * - **Platform unregistered, destination connected account registered in\n * TX** → `amount_tax: 0`, one breakdown line with\n * `taxability_reason: \"not_collecting\"`, `taxable_amount: 0`.\n * - **Platform registered in TX, everything else byte-identical** →\n * `amount_tax: 825`, `taxability_reason: \"standard_rated\"`,\n * `taxable_amount: 10000`, rate 8.25% \"Texas\".\n *\n * Both sessions reported `automatic_tax.liability: { type: \"self\" }` — Stripe's\n * own word for \"the account this session was created on is the one liable\".\n * `transfer_data[destination]` had no effect on tax in either arm.\n *\n * So on a `mode: 'stripe'` store the tax charged to the shopper is computed on\n * Aglyn's registration footprint, lands in Aglyn's platform balance, and is\n * Aglyn's to remit. The live platform account has held an ACTIVE Texas state\n * sales tax registration since 2026-08-17, so this is not hypothetical.\n *\n * **This module describes that mechanism and records the money.** Aglyn IS a\n * marketplace facilitator for storefront sales, and the published Terms say so\n * at §10.7 (AGL-1956). Facilitator status still attaches by operation of\n * law rather than by our say-so, and §10.7 is deliberately narrow — Aglyn\n * collects and remits **where applicable law gives it an obligation**, not\n * universally — so this module still records the mechanism per sale and does\n * not stamp a per-merchant or per-jurisdiction verdict on any row.\n *\n * ## The two store modes are DIFFERENT FACTS and must never be summed\n *\n * `taxMode` is the whole point of this record, and it is derived from\n * `automatic_tax.enabled` — **never** from whether tax lines are present:\n *\n * - **`stripe-automatic`** — Stripe Tax computed it, against whichever\n * account `automatic_tax.liability` names. When that is `platform`, the\n * money is held under Aglyn's registration.\n * - **`manual`** — the merchant's own configured rate\n * (`checkout.ts` / `pos-order.ts`), applied to the merchant's own declared\n * origin. Aglyn's registrations never enter into it and the amount is not\n * Aglyn's to remit.\n *\n * The trap that makes the `automatic_tax.enabled` discriminator load-bearing:\n * a MANUAL-mode subscription renewal carries a real Stripe Tax Rate (AGL-1751\n * attaches one so the tax recurs), so its invoice arrives with a populated\n * `total_taxes[]` — indistinguishable from a Stripe Tax invoice if you look at\n * the tax lines. Summing tax lines would silently book merchant-configured tax\n * as Aglyn-collected. **Read the flag, not the lines.**\n *\n * ## The taxable base is kept, never inferred\n *\n * `taxable_amount` is what a return reports as \"taxable sales\". Deriving it as\n * `amount ÷ rate` needs a live read to resolve the rate id and rounds wrong at\n * the edges, so it is stored when Stripe states it and left `null` when it\n * does not — and a row whose base cannot be read is COUNTED OUT LOUD by the\n * return rather than treated as a zero-base sale.\n *\n * A Checkout Session's `total_details.breakdown` is not in the webhook payload\n * and must be re-read with `expand[]=total_details.breakdown`; an invoice\n * carries `total_taxes[]` inline. Both land in the same `taxLines` shape.\n *\n * Pure and total: unreadable input answers `null`, never throws — a recording\n * failure must never 500 a webhook into redelivery.\n */\n\nimport {\n storefrontTaxMode,\n type StorefrontTaxMode,\n} from '../model/commerce-tax-decision'\n\n/**\n * Which machinery produced the tax on this sale. See the module note.\n *\n * The union and its derivation live in the MODEL since AGL-2451, because the\n * merchant's order document now carries the same fact (`HostOrder.taxMode`)\n * and two copies of a rule that must agree are one copy too many. Re-exported\n * here so this module stays the place the mechanism is documented.\n */\nexport type { StorefrontTaxMode }\n\n/**\n * Whose registrations Stripe Tax computed against, from\n * `automatic_tax.liability.type`: `self` (the account the object lives on,\n * i.e. Aglyn's platform) or `account` (a named connected account).\n * Null whenever `taxMode` is not `stripe-automatic`.\n */\nexport type StorefrontTaxLiability = 'platform' | 'connected-account'\n\n/** One entry of the stored tax breakdown. */\nexport interface StorefrontTaxLine {\n amountCents: number\n /** Stripe's stated reason, e.g. `standard_rated`, `not_collecting`. */\n taxabilityReason: string | null\n taxRateId: string | null\n /** Stripe's `taxable_amount`; null when the shape did not carry it. */\n taxableAmountCents: number | null\n /** e.g. `Texas` — from the expanded rate, for the working papers. */\n jurisdiction: string | null\n /** The rate's own state code, which is not the shopper's address. */\n rateState: string | null\n percentage: number | null\n}\n\n/** The per-transaction record `storefrontTaxCollected/{stripeId}` stores. */\nexport interface StorefrontTaxRow {\n /** `session` for one-time sales, `invoice` for subscription cycles. */\n kind: 'session' | 'invoice'\n hostId: string\n /** The commerce path — `commerce-cart`, `commerce-order`, … */\n metadataType: string | null\n taxMode: StorefrontTaxMode\n taxLiability: StorefrontTaxLiability | null\n /** The merchant's connected account, when the object names one. */\n connectedAccountId: string | null\n /** What the shopper actually paid, tax included. */\n grossCents: number\n taxCents: number\n /** Gross minus tax. NOT Aglyn's revenue — most of it is the merchant's. */\n netCents: number\n currency: string\n /** The jurisdiction input — the shopper's address on the Stripe object. */\n customerAddress: {\n country: string | null\n state: string | null\n city: string | null\n postalCode: string | null\n } | null\n taxLines: StorefrontTaxLine[]\n paidAt: Date | null\n}\n\nfunction asCents(value: unknown): number {\n const cents = Number(value ?? 0)\n return Number.isFinite(cents) ? Math.round(cents) : 0\n}\n\nfunction asStringOrNull(value: unknown): string | null {\n return typeof value === 'string' && value ? value : null\n}\n\nfunction asNumberOrNull(value: unknown): number | null {\n const parsed = Number(value ?? NaN)\n return Number.isFinite(parsed) ? parsed : null\n}\n\n/** Seconds-since-epoch to Date, or null for anything unreadable. */\nfunction asDate(value: unknown): Date | null {\n const seconds = Number(value ?? NaN)\n return Number.isFinite(seconds) && seconds > 0\n ? new Date(seconds * 1000)\n : null\n}\n\nfunction address(source: any): StorefrontTaxRow['customerAddress'] {\n if (!source || typeof source !== 'object') return null\n return {\n country: asStringOrNull(source.country),\n state: asStringOrNull(source.state),\n city: asStringOrNull(source.city),\n postalCode: asStringOrNull(source.postal_code),\n }\n}\n\n/**\n * The breakdown entries, from either shape.\n *\n * A Checkout Session expands them under `total_details.breakdown.taxes[]`\n * with the rate object inline; an invoice carries `total_taxes[]` (rate under\n * `tax_rate_details.tax_rate`) or the older `total_tax_amounts[]` (`tax_rate`,\n * an id or an expanded object). All three map onto one stored line.\n *\n * Exported for the POS hardware shop's own sales (AGL-4038), whose tax rows\n * keep the same lines: one reading of Stripe's breakdown for every half of\n * the return.\n */\nexport function stripeTaxBreakdownLines(source: any): StorefrontTaxLine[] {\n return taxLines(source)\n}\n\nfunction taxLines(source: any): StorefrontTaxLine[] {\n const entries: any[] = Array.isArray(source?.total_details?.breakdown?.taxes)\n ? source.total_details.breakdown.taxes\n : Array.isArray(source?.total_taxes)\n ? source.total_taxes\n : Array.isArray(source?.total_tax_amounts)\n ? source.total_tax_amounts\n : []\n return entries.map((entry: any) => {\n const rate = entry?.rate ?? entry?.tax_rate_details?.tax_rate ?? entry?.tax_rate\n const rateObject = rate && typeof rate === 'object' ? rate : null\n // Distinguish an absent `taxable_amount` from a genuine zero: a zero base\n // is a real fact (`not_collecting`), an absent one means the base is\n // UNKNOWN, and the return has to be able to tell those apart.\n const taxableAmount = Number(entry?.taxable_amount ?? NaN)\n return {\n amountCents: asCents(entry?.amount),\n taxabilityReason: asStringOrNull(entry?.taxability_reason),\n taxRateId: asStringOrNull(rate) ?? asStringOrNull(rateObject?.id),\n taxableAmountCents: Number.isFinite(taxableAmount)\n ? Math.round(taxableAmount)\n : null,\n jurisdiction: asStringOrNull(rateObject?.jurisdiction),\n rateState: asStringOrNull(rateObject?.state),\n percentage:\n asNumberOrNull(rateObject?.effective_percentage) ??\n asNumberOrNull(rateObject?.percentage),\n }\n })\n}\n\n/** Sum of the stored lines' amounts. */\nfunction sumLines(lines: readonly StorefrontTaxLine[]): number {\n return lines.reduce((sum, line) => sum + line.amountCents, 0)\n}\n\nexport interface StorefrontTaxContext {\n kind: 'session' | 'invoice'\n hostId: string\n /**\n * The manual-mode tax `checkout.ts` puts over as an ordinary line item.\n * Stripe is never told it is tax, so no Stripe field carries it and the\n * session's own `metadata[taxCents]` is the only witness (AGL-1711).\n */\n manualTaxCents?: number\n}\n\n/** What one Stripe object says about the tax it carried. */\nexport interface ResolvedStorefrontTax {\n /** `automatic_tax.enabled` — the discriminator, never the tax lines. */\n automatic: boolean\n /** Stripe's own breakdown, present only on a `stripe-automatic` sale. */\n lines: StorefrontTaxLine[]\n taxCents: number\n taxMode: StorefrontTaxMode\n}\n\n/**\n * Reads the tax off one paid session or invoice — the SINGLE derivation, and\n * the reason it is its own exported function (AGL-2451).\n *\n * `storefrontTaxRow` builds the `storefrontTaxCollected` record from it, and\n * the four order-writing doors in `billing-webhook.ts` stamp `HostOrder.taxMode`\n * from it, so an order and the row filed for the same Stripe id cannot state\n * different regimes. Re-deriving the mode beside a second reading of the same\n * object is exactly how the two would drift.\n *\n * Pure and total: unreadable input answers `taxMode: 'none'`, never throws.\n */\nexport function resolveStorefrontTax(\n object: unknown,\n manualTaxCents = 0,\n): ResolvedStorefrontTax {\n const source = object as any\n const automatic = source?.automatic_tax?.enabled === true\n const lines = automatic ? taxLines(source) : []\n const manualCents = Math.max(0, asCents(manualTaxCents))\n // The flag, never the lines — see the module note. A manual-mode\n // subscription renewal carries real Stripe tax rates in `total_taxes[]`,\n // and booking those as Aglyn-collected would be the whole defect restated.\n //\n // Read on SESSIONS as well as invoices since AGL-1953: the cart and\n // draft-order paths now carry their manual tax as a real Stripe Tax Rate on\n // the line (the construction that survives a session-level coupon), so a\n // manual session reports a genuine `total_details.amount_tax`. The scalar is\n // the fallback because `total_details.breakdown` is expandable and absent\n // unless the recorder re-read it. Buy-now's `line_items[1]` construction\n // reports 0 in both shapes and still answers from `manualTaxCents` below.\n const manualLineCents = !automatic\n ? sumLines(taxLines(source)) ||\n asCents(source?.total_details?.amount_tax ?? source?.tax)\n : 0\n const taxCents = automatic\n ? // `total_details.amount_tax` on a session, `total_taxes[]` on an\n // invoice. Prefer the summed lines when they exist (a computed invoice\n // can send a 0/null scalar beside a populated array — AGL-1811's\n // measurement), and fall back to the scalar shapes otherwise.\n lines.length > 0\n ? sumLines(lines)\n : asCents(source?.total_details?.amount_tax ?? source?.tax)\n : Math.max(manualCents, manualLineCents)\n return {\n automatic,\n lines,\n taxCents,\n taxMode: storefrontTaxMode({ automatic, taxCents }),\n }\n}\n\n/**\n * Just the regime, for the order-writing doors (AGL-2451). Delegates, so this\n * is the same answer the filed tax record gives for the same object.\n */\nexport function storefrontTaxModeOf(\n object: unknown,\n manualTaxCents = 0,\n): StorefrontTaxMode {\n return resolveStorefrontTax(object, manualTaxCents).taxMode\n}\n\n/**\n * Decomposes a paid storefront session or invoice into the stored row, or\n * `null` when there is no host to attribute it to.\n *\n * The caller decides WHICH objects reach this — the commerce webhook\n * self-selects on `metadata.type`, exactly as every other section does.\n */\nexport function storefrontTaxRow(\n object: unknown,\n context: StorefrontTaxContext,\n): StorefrontTaxRow | null {\n const source = object as any\n const hostId = String(context?.hostId ?? '')\n if (!hostId) return null\n\n const { automatic, lines, taxCents, taxMode } = resolveStorefrontTax(\n source,\n context?.manualTaxCents,\n )\n\n const liabilityType = asStringOrNull(source?.automatic_tax?.liability?.type)\n const taxLiability: StorefrontTaxLiability | null = !automatic\n ? null\n : liabilityType === 'account'\n ? 'connected-account'\n : // `self`, and also an absent liability: Stripe omits the field on\n // older API versions, where the only account a session could be\n // liable under is the one it was created on — the platform.\n 'platform'\n\n const grossCents = asCents(\n context.kind === 'invoice' ? source?.amount_paid : source?.amount_total,\n )\n const transferDestination =\n source?.payment_intent_data?.transfer_data?.destination ??\n source?.transfer_data?.destination\n return {\n kind: context.kind,\n hostId,\n metadataType:\n asStringOrNull(source?.metadata?.type) ??\n asStringOrNull(source?.subscription_details?.metadata?.type) ??\n asStringOrNull(source?.parent?.subscription_details?.metadata?.type),\n taxMode,\n taxLiability,\n connectedAccountId:\n asStringOrNull(transferDestination) ??\n asStringOrNull((transferDestination as any)?.id),\n grossCents,\n taxCents,\n netCents: grossCents - taxCents,\n currency: asStringOrNull(source?.currency) ?? 'usd',\n customerAddress:\n address(source?.customer_details?.address) ??\n address(source?.customer_address),\n // Stripe's OWN breakdown, whenever Stripe stated one — never an invented\n // base. Buy-now's manual construction states none (Stripe was never told\n // the line was tax) and answers `[]`, which is the honest answer. The\n // cart, draft-order and subscription-renewal manual paths carry a real\n // Tax Rate, so Stripe DOES state a `taxable_amount` for them, and keeping\n // it gives `merchantManual` a real taxable-sales figure instead of a\n // permanent zero (AGL-1953). It never reaches the Aglyn-liable bucket:\n // the classification above is the flag, not these lines.\n taxLines: automatic ? lines : taxLines(source),\n paidAt:\n asDate(source?.status_transitions?.paid_at) ?? asDate(source?.created),\n }\n}\n"],"names":["storefrontTaxMode","asCents","value","cents","Number","isFinite","Math","round","asStringOrNull","asNumberOrNull","parsed","NaN","asDate","seconds","Date","address","source","country","state","city","postalCode","postal_code","stripeTaxBreakdownLines","taxLines","entries","Array","isArray","total_details","breakdown","taxes","total_taxes","total_tax_amounts","map","entry","rate","tax_rate_details","tax_rate","rateObject","taxableAmount","taxable_amount","amountCents","amount","taxabilityReason","taxability_reason","taxRateId","id","taxableAmountCents","jurisdiction","rateState","percentage","effective_percentage","sumLines","lines","reduce","sum","line","resolveStorefrontTax","object","manualTaxCents","automatic","automatic_tax","enabled","manualCents","max","manualLineCents","amount_tax","tax","taxCents","length","taxMode","storefrontTaxModeOf","storefrontTaxRow","context","hostId","String","liabilityType","liability","type","taxLiability","grossCents","kind","amount_paid","amount_total","transferDestination","payment_intent_data","transfer_data","destination","metadataType","metadata","subscription_details","parent","connectedAccountId","netCents","currency","customerAddress","customer_details","customer_address","paidAt","status_transitions","paid_at","created"],"mappings":"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"}
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
*/
|
|
17
17
|
import type { TaxReturnFiling, TaxReturnFilingLine, TaxReturnFinding, TaxReturnSource, TaxReturnSourceAnswer, TaxReturnSourceRequest } from '@aglyn/aglyn/plugin-manager/plugin-tax-return-sources';
|
|
18
18
|
import { type StorefrontTaxReturnRowInput, type StorefrontTaxSummary } from '../model/storefront-tax-summary';
|
|
19
|
+
import { type OwnStores } from './own-store-tax-return-source';
|
|
19
20
|
/** One row of the storefront listing, as the working papers carry it. */
|
|
20
21
|
export interface StorefrontTaxRow {
|
|
21
22
|
id: string;
|
|
@@ -111,10 +112,32 @@ export declare function storefrontTaxReturnAnswer(read: {
|
|
|
111
112
|
rows: readonly StorefrontTaxReturnRowInput[];
|
|
112
113
|
truncated: boolean;
|
|
113
114
|
undatedRows: number;
|
|
115
|
+
/** Rows of the operator's own stores, which their own card folds in instead (AGL-4038). */
|
|
116
|
+
ownStoreRows?: number;
|
|
114
117
|
}, request: TaxReturnSourceRequest): TaxReturnSourceAnswer;
|
|
115
118
|
/**
|
|
116
|
-
*
|
|
117
|
-
*
|
|
119
|
+
* Commerce's source, registered on the console's API surface: the staff
|
|
120
|
+
* return awaits that surface before it asks. It answers three sections, one
|
|
121
|
+
* per kind of sale commerce records apart:
|
|
122
|
+
*
|
|
123
|
+
* - the storefront's FACILITATED sales — merchants' money (below);
|
|
124
|
+
* - the POS hardware shop's, the operator's own
|
|
125
|
+
* (`pos-hardware-tax-return-source.ts`, AGL-4038);
|
|
126
|
+
* - the operator's own stores', its own too (`own-store-tax-return-source.ts`,
|
|
127
|
+
* AGL-4038).
|
|
128
|
+
*
|
|
129
|
+
* The operator's own stores are resolved FIRST, because each of the others
|
|
130
|
+
* leaves their sales to the third: the storefront drops their tax rows (and
|
|
131
|
+
* hands their stated bases over), and the hardware section folds in no
|
|
132
|
+
* reader sold at their registers. So every sale is counted once. Any read
|
|
133
|
+
* failing refuses all three, so a half-read commerce never reads as a
|
|
134
|
+
* complete one.
|
|
135
|
+
*/
|
|
136
|
+
export declare const commerceTaxReturnSource: TaxReturnSource;
|
|
137
|
+
/** Whether a storefront tax row is one of the operator's own stores' (AGL-4038). */
|
|
138
|
+
export declare function isOwnStoreRow(row: Pick<StorefrontTaxReturnRowInput, 'hostId' | 'orgId'>, own: OwnStores): boolean;
|
|
139
|
+
/**
|
|
140
|
+
* The storefront's section.
|
|
118
141
|
*
|
|
119
142
|
* Two queries, and the second is the honesty check: the period query ranges
|
|
120
143
|
* on `paidAt`, and a Firestore range query CANNOT match a document whose
|
|
@@ -122,4 +145,10 @@ export declare function storefrontTaxReturnAnswer(read: {
|
|
|
122
145
|
* every period. The equality probe counts those rows, over all of them, and
|
|
123
146
|
* the platform blocks the return on any.
|
|
124
147
|
*/
|
|
125
|
-
export declare
|
|
148
|
+
export declare function readStorefrontTaxReturn(request: TaxReturnSourceRequest): Promise<TaxReturnSourceAnswer>;
|
|
149
|
+
/** The storefront's rows for the period, before any is set aside. See {@link readStorefrontTaxReturn}. */
|
|
150
|
+
export declare function readStorefrontTaxRows(request: TaxReturnSourceRequest): Promise<{
|
|
151
|
+
rows: StorefrontTaxReturnRowInput[];
|
|
152
|
+
truncated: boolean;
|
|
153
|
+
undatedRows: number;
|
|
154
|
+
}>;
|
|
@@ -57,6 +57,16 @@ import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
|
57
57
|
import { PLATFORM_BRAND_NAME } from "@aglyn/aglyn/app-utils/platform-brand";
|
|
58
58
|
import { asRowDate, centsToDollars } from "@aglyn/aglyn/app-utils/tax-jurisdiction-figures";
|
|
59
59
|
import { storefrontTaxSummary } from "../model/storefront-tax-summary.js";
|
|
60
|
+
import { readPosHardwareTaxReturn } from "./pos-hardware-tax-return-source.js";
|
|
61
|
+
import { POS_HARDWARE_SALES_COLLECTION } from "../model/pos-hardware.js";
|
|
62
|
+
import { posHardwareStorefrontParts, storefrontRowsWithoutInPersonHardware } from "../model/pos-hardware-tax-summary.js";
|
|
63
|
+
import { statedTaxableBase } from "../model/own-store-tax-summary.js";
|
|
64
|
+
import { ownStoreTaxReturnAnswer, readOwnStoreOrders, resolveOwnStores } from "./own-store-tax-return-source.js";
|
|
65
|
+
/**
|
|
66
|
+
* How far either side of the period an in-person hardware record is read,
|
|
67
|
+
* to meet the storefront row its register sale filed: the two are written
|
|
68
|
+
* seconds apart, so a week is more than enough for either edge.
|
|
69
|
+
*/ const IN_PERSON_ROW_MARGIN_MS = 7 * 24 * 60 * 60 * 1000;
|
|
60
70
|
/**
|
|
61
71
|
* Storefront tax in the FILING jurisdiction that Stripe computed against the
|
|
62
72
|
* platform's own registrations.
|
|
@@ -487,38 +497,107 @@ import { storefrontTaxSummary } from "../model/storefront-tax-summary.js";
|
|
|
487
497
|
findings: storefrontTaxFindings(summary, filing),
|
|
488
498
|
filingLines: storefrontFilingLines(summary, filing),
|
|
489
499
|
tables: storefrontTables(summary, filing),
|
|
490
|
-
figures: [
|
|
500
|
+
figures: read.ownStoreRows ? [
|
|
501
|
+
{
|
|
502
|
+
label: `Rows of ${brand}’s own stores`,
|
|
503
|
+
value: String(read.ownStoreRows),
|
|
504
|
+
note: `${brand}’s own sales, not a merchant’s: on the own stores card and folded into the filing figures, never here.`
|
|
505
|
+
}
|
|
506
|
+
] : [],
|
|
491
507
|
exports: storefrontExports(summary, filing),
|
|
492
508
|
summary,
|
|
493
509
|
rows: read.rows.map(projectRow)
|
|
494
510
|
};
|
|
495
511
|
}
|
|
496
512
|
/**
|
|
497
|
-
*
|
|
498
|
-
*
|
|
513
|
+
* Commerce's source, registered on the console's API surface: the staff
|
|
514
|
+
* return awaits that surface before it asks. It answers three sections, one
|
|
515
|
+
* per kind of sale commerce records apart:
|
|
499
516
|
*
|
|
500
|
-
*
|
|
501
|
-
*
|
|
502
|
-
*
|
|
503
|
-
*
|
|
504
|
-
*
|
|
517
|
+
* - the storefront's FACILITATED sales — merchants' money (below);
|
|
518
|
+
* - the POS hardware shop's, the operator's own
|
|
519
|
+
* (`pos-hardware-tax-return-source.ts`, AGL-4038);
|
|
520
|
+
* - the operator's own stores', its own too (`own-store-tax-return-source.ts`,
|
|
521
|
+
* AGL-4038).
|
|
522
|
+
*
|
|
523
|
+
* The operator's own stores are resolved FIRST, because each of the others
|
|
524
|
+
* leaves their sales to the third: the storefront drops their tax rows (and
|
|
525
|
+
* hands their stated bases over), and the hardware section folds in no
|
|
526
|
+
* reader sold at their registers. So every sale is counted once. Any read
|
|
527
|
+
* failing refuses all three, so a half-read commerce never reads as a
|
|
528
|
+
* complete one.
|
|
505
529
|
*/ export const commerceTaxReturnSource = {
|
|
506
530
|
async read (request) {
|
|
507
|
-
|
|
508
|
-
const
|
|
509
|
-
const [
|
|
510
|
-
|
|
511
|
-
|
|
531
|
+
const own = await resolveOwnStores();
|
|
532
|
+
const ownHostIds = new Set(own.stores.map((store)=>store.hostId));
|
|
533
|
+
const [storefrontRead, hardware, ownOrders] = await Promise.all([
|
|
534
|
+
readStorefrontTaxRows(request),
|
|
535
|
+
readPosHardwareTaxReturn(request, {
|
|
536
|
+
ownHostIds
|
|
537
|
+
}),
|
|
538
|
+
readOwnStoreOrders(request, own.stores)
|
|
512
539
|
]);
|
|
513
|
-
const
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
540
|
+
const isOwn = (row)=>isOwnStoreRow(row, own);
|
|
541
|
+
const ownRows = storefrontRead.rows.filter(isOwn);
|
|
542
|
+
const storefront = storefrontTaxReturnAnswer(_extends({}, storefrontRead, {
|
|
543
|
+
rows: storefrontRead.rows.filter((row)=>!isOwn(row)),
|
|
544
|
+
ownStoreRows: ownRows.length
|
|
545
|
+
}), request);
|
|
546
|
+
// The base Stripe stated on an own store's online sale, by its order id
|
|
547
|
+
// (an online order is keyed by its Checkout Session, as its row is).
|
|
548
|
+
const statedBases = new Map();
|
|
549
|
+
for (const row of ownRows){
|
|
550
|
+
const base = statedTaxableBase(Array.isArray(row.taxLines) ? row.taxLines : []);
|
|
551
|
+
if (base !== null) statedBases.set(row.id, base);
|
|
552
|
+
}
|
|
553
|
+
const ownStores = ownStoreTaxReturnAnswer({
|
|
554
|
+
own,
|
|
555
|
+
read: ownOrders,
|
|
556
|
+
statedBases
|
|
520
557
|
}, request);
|
|
558
|
+
return [
|
|
559
|
+
storefront,
|
|
560
|
+
hardware,
|
|
561
|
+
ownStores
|
|
562
|
+
];
|
|
521
563
|
}
|
|
522
564
|
};
|
|
565
|
+
/** Whether a storefront tax row is one of the operator's own stores' (AGL-4038). */ export function isOwnStoreRow(row, own) {
|
|
566
|
+
if (typeof row.hostId === 'string' && own.stores.some((store)=>store.hostId === row.hostId)) return true;
|
|
567
|
+
return Boolean(own.orgId) && row.orgId === own.orgId;
|
|
568
|
+
}
|
|
569
|
+
/**
|
|
570
|
+
* The storefront's section.
|
|
571
|
+
*
|
|
572
|
+
* Two queries, and the second is the honesty check: the period query ranges
|
|
573
|
+
* on `paidAt`, and a Firestore range query CANNOT match a document whose
|
|
574
|
+
* field is null — so a row that failed to date itself would be invisible to
|
|
575
|
+
* every period. The equality probe counts those rows, over all of them, and
|
|
576
|
+
* the platform blocks the return on any.
|
|
577
|
+
*/ export async function readStorefrontTaxReturn(request) {
|
|
578
|
+
return storefrontTaxReturnAnswer(await readStorefrontTaxRows(request), request);
|
|
579
|
+
}
|
|
580
|
+
/** The storefront's rows for the period, before any is set aside. See {@link readStorefrontTaxReturn}. */ export async function readStorefrontTaxRows(request) {
|
|
581
|
+
var _undated_data_count;
|
|
582
|
+
const firestore = firebaseAdmin.app().firestore();
|
|
583
|
+
const collection = firestore.collection('storefrontTaxCollected');
|
|
584
|
+
const [inPeriod, undated, inPerson] = await Promise.all([
|
|
585
|
+
collection.where('paidAt', '>=', request.start).where('paidAt', '<', request.end).limit(request.rowCap + 1).get(),
|
|
586
|
+
collection.where('paidAt', '==', null).count().get(),
|
|
587
|
+
// The platform's own in-person hardware sales around the period
|
|
588
|
+
// (AGL-4038): the hardware section counts them, so their part of the
|
|
589
|
+
// register sale's storefront row comes out here (`posHardwareSales`
|
|
590
|
+
// `source, paidAtMs`).
|
|
591
|
+
firestore.collection(POS_HARDWARE_SALES_COLLECTION).where('source', '==', 'in_person').where('paidAtMs', '>=', request.start.getTime() - IN_PERSON_ROW_MARGIN_MS).where('paidAtMs', '<', request.end.getTime() + IN_PERSON_ROW_MARGIN_MS).limit(request.rowCap).get()
|
|
592
|
+
]);
|
|
593
|
+
const rows = storefrontRowsWithoutInPersonHardware(inPeriod.docs.slice(0, request.rowCap).map((doc)=>_extends({
|
|
594
|
+
id: doc.id
|
|
595
|
+
}, doc.data())), posHardwareStorefrontParts(inPerson.docs.map((doc)=>doc.data())));
|
|
596
|
+
return {
|
|
597
|
+
rows,
|
|
598
|
+
truncated: inPeriod.size > request.rowCap,
|
|
599
|
+
undatedRows: Number((_undated_data_count = undated.data().count) != null ? _undated_data_count : 0)
|
|
600
|
+
};
|
|
601
|
+
}
|
|
523
602
|
|
|
524
603
|
//# sourceMappingURL=tax-return-source.js.map
|