@aglyn/plugins-commerce 1.0.0-beta.230 → 1.0.0-beta.232
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/package.json +13 -13
- package/src/index.d.ts +0 -1
- package/src/index.js +0 -1
- package/src/index.js.map +1 -1
- package/src/lib/barcode/barcode-decode.d.ts +71 -0
- package/src/lib/barcode/barcode-decode.js +498 -0
- package/src/lib/barcode/barcode-decode.js.map +1 -0
- package/src/lib/barcode/barcode-encode.d.ts +44 -0
- package/src/lib/barcode/barcode-encode.js +138 -0
- package/src/lib/barcode/barcode-encode.js.map +1 -0
- package/src/lib/barcode/barcode-scanner.component.d.ts +23 -0
- package/src/lib/barcode/barcode-scanner.component.js +260 -0
- package/src/lib/barcode/barcode-scanner.component.js.map +1 -0
- package/src/lib/barcode/scan-button.component.d.ts +10 -0
- package/src/lib/barcode/scan-button.component.js +62 -0
- package/src/lib/barcode/scan-button.component.js.map +1 -0
- package/src/lib/barcode/scanner-wedge.d.ts +35 -0
- package/src/lib/barcode/scanner-wedge.js +108 -0
- package/src/lib/barcode/scanner-wedge.js.map +1 -0
- package/src/lib/components/account.js +10 -1
- package/src/lib/components/account.js.map +1 -1
- package/src/lib/components/cart-extras.d.ts +45 -0
- package/src/lib/components/cart-extras.js +127 -0
- package/src/lib/components/cart-extras.js.map +1 -0
- package/src/lib/components/cart.js +82 -15
- package/src/lib/components/cart.js.map +1 -1
- package/src/lib/components/checkout-return-notice.d.ts +26 -0
- package/src/lib/components/checkout-return-notice.js +48 -0
- package/src/lib/components/checkout-return-notice.js.map +1 -0
- package/src/lib/components/commerce-console-page.js +124 -0
- package/src/lib/components/commerce-console-page.js.map +1 -1
- package/src/lib/components/commerce-console-sections.d.ts +1 -1
- package/src/lib/components/commerce-console-sections.js +4 -0
- package/src/lib/components/commerce-console-sections.js.map +1 -1
- package/src/lib/components/console/bulk-fulfill.d.ts +44 -0
- package/src/lib/components/console/bulk-fulfill.js +84 -0
- package/src/lib/components/console/bulk-fulfill.js.map +1 -0
- package/src/lib/components/console/catalog-organization-card.component.js +1 -0
- package/src/lib/components/console/catalog-organization-card.component.js.map +1 -1
- package/src/lib/components/console/customer-notifications-card.component.d.ts +19 -0
- package/src/lib/components/console/customer-notifications-card.component.js +149 -0
- package/src/lib/components/console/customer-notifications-card.component.js.map +1 -0
- package/src/lib/components/console/host-orders-card.component.js +88 -6
- package/src/lib/components/console/host-orders-card.component.js.map +1 -1
- package/src/lib/components/console/locations-card.component.js +164 -57
- package/src/lib/components/console/locations-card.component.js.map +1 -1
- package/src/lib/components/console/order-detail-dialog.component.js +209 -61
- package/src/lib/components/console/order-detail-dialog.component.js.map +1 -1
- package/src/lib/components/console/order-fulfillment-panel.component.d.ts +48 -0
- package/src/lib/components/console/order-fulfillment-panel.component.js +542 -0
- package/src/lib/components/console/order-fulfillment-panel.component.js.map +1 -0
- package/src/lib/components/console/order-invoice.component.d.ts +33 -0
- package/src/lib/components/console/order-invoice.component.js +81 -0
- package/src/lib/components/console/order-invoice.component.js.map +1 -0
- package/src/lib/components/console/order-receipt-resend.component.d.ts +21 -0
- package/src/lib/components/console/order-receipt-resend.component.js +203 -0
- package/src/lib/components/console/order-receipt-resend.component.js.map +1 -0
- package/src/lib/components/console/order-returns.component.d.ts +36 -0
- package/src/lib/components/console/order-returns.component.js +142 -0
- package/src/lib/components/console/order-returns.component.js.map +1 -0
- package/src/lib/components/console/order-shipping-actions.component.d.ts +54 -0
- package/src/lib/components/console/order-shipping-actions.component.js +149 -0
- package/src/lib/components/console/order-shipping-actions.component.js.map +1 -0
- package/src/lib/components/console/order-webhooks-card.component.d.ts +50 -0
- package/src/lib/components/console/order-webhooks-card.component.js +623 -0
- package/src/lib/components/console/order-webhooks-card.component.js.map +1 -0
- package/src/lib/components/console/order-zones.d.ts +155 -0
- package/src/lib/components/console/order-zones.js +40 -0
- package/src/lib/components/console/order-zones.js.map +1 -0
- package/src/lib/components/console/payment-methods-card.component.d.ts +67 -0
- package/src/lib/components/console/payment-methods-card.component.js +395 -0
- package/src/lib/components/console/payment-methods-card.component.js.map +1 -0
- package/src/lib/components/console/pos/pos-api.d.ts +124 -0
- package/src/lib/components/console/pos/pos-api.js +101 -0
- package/src/lib/components/console/pos/pos-api.js.map +1 -0
- package/src/lib/components/console/pos/pos-cart-panel.component.d.ts +36 -0
- package/src/lib/components/console/pos/pos-cart-panel.component.js +160 -0
- package/src/lib/components/console/pos/pos-cart-panel.component.js.map +1 -0
- package/src/lib/components/console/pos/pos-item-dialog.component.d.ts +39 -0
- package/src/lib/components/console/pos/pos-item-dialog.component.js +352 -0
- package/src/lib/components/console/pos/pos-item-dialog.component.js.map +1 -0
- package/src/lib/components/console/pos/pos-product-grid.component.d.ts +47 -0
- package/src/lib/components/console/pos/pos-product-grid.component.js +272 -0
- package/src/lib/components/console/pos/pos-product-grid.component.js.map +1 -0
- package/src/lib/components/console/pos/pos-receipt-panel.component.d.ts +27 -0
- package/src/lib/components/console/pos/pos-receipt-panel.component.js +282 -0
- package/src/lib/components/console/pos/pos-receipt-panel.component.js.map +1 -0
- package/src/lib/components/console/pos/pos-tender-dialogs.component.d.ts +53 -0
- package/src/lib/components/console/pos/pos-tender-dialogs.component.js +445 -0
- package/src/lib/components/console/pos/pos-tender-dialogs.component.js.map +1 -0
- package/src/lib/components/console/pos/pos-tender-panel.component.d.ts +31 -0
- package/src/lib/components/console/pos/pos-tender-panel.component.js +640 -0
- package/src/lib/components/console/pos/pos-tender-panel.component.js.map +1 -0
- package/src/lib/components/console/pos/use-pos-display.d.ts +43 -0
- package/src/lib/components/console/pos/use-pos-display.js +161 -0
- package/src/lib/components/console/pos/use-pos-display.js.map +1 -0
- package/src/lib/components/console/pos-devices-card.component.d.ts +14 -0
- package/src/lib/components/console/pos-devices-card.component.js +546 -0
- package/src/lib/components/console/pos-devices-card.component.js.map +1 -0
- package/src/lib/components/console/pos-display/brand-screen.d.ts +33 -0
- package/src/lib/components/console/pos-display/brand-screen.js +85 -0
- package/src/lib/components/console/pos-display/brand-screen.js.map +1 -0
- package/src/lib/components/console/pos-display/cart-screen.d.ts +29 -0
- package/src/lib/components/console/pos-display/cart-screen.js +166 -0
- package/src/lib/components/console/pos-display/cart-screen.js.map +1 -0
- package/src/lib/components/console/pos-display/pairing-screen.d.ts +24 -0
- package/src/lib/components/console/pos-display/pairing-screen.js +127 -0
- package/src/lib/components/console/pos-display/pairing-screen.js.map +1 -0
- package/src/lib/components/console/pos-display/pos-display-api.d.ts +90 -0
- package/src/lib/components/console/pos-display/pos-display-api.js +140 -0
- package/src/lib/components/console/pos-display/pos-display-api.js.map +1 -0
- package/src/lib/components/console/pos-display/receipt-screen.d.ts +35 -0
- package/src/lib/components/console/pos-display/receipt-screen.js +200 -0
- package/src/lib/components/console/pos-display/receipt-screen.js.map +1 -0
- package/src/lib/components/console/pos-display/tip-screen.d.ts +36 -0
- package/src/lib/components/console/pos-display/tip-screen.js +211 -0
- package/src/lib/components/console/pos-display/tip-screen.js.map +1 -0
- package/src/lib/components/console/pos-display-page.component.d.ts +16 -0
- package/src/lib/components/console/pos-display-page.component.js +407 -0
- package/src/lib/components/console/pos-display-page.component.js.map +1 -0
- package/src/lib/components/console/pos-ops/pos-customer-lookup.component.d.ts +40 -0
- package/src/lib/components/console/pos-ops/pos-customer-lookup.component.js +289 -0
- package/src/lib/components/console/pos-ops/pos-customer-lookup.component.js.map +1 -0
- package/src/lib/components/console/pos-ops/pos-last-receipt.component.d.ts +34 -0
- package/src/lib/components/console/pos-ops/pos-last-receipt.component.js +61 -0
- package/src/lib/components/console/pos-ops/pos-last-receipt.component.js.map +1 -0
- package/src/lib/components/console/pos-ops/pos-operations-bar.component.d.ts +38 -0
- package/src/lib/components/console/pos-ops/pos-operations-bar.component.js +129 -0
- package/src/lib/components/console/pos-ops/pos-operations-bar.component.js.map +1 -0
- package/src/lib/components/console/pos-ops/pos-ops-api.d.ts +37 -0
- package/src/lib/components/console/pos-ops/pos-ops-api.js +60 -0
- package/src/lib/components/console/pos-ops/pos-ops-api.js.map +1 -0
- package/src/lib/components/console/pos-ops/pos-pin-pad.component.d.ts +48 -0
- package/src/lib/components/console/pos-ops/pos-pin-pad.component.js +220 -0
- package/src/lib/components/console/pos-ops/pos-pin-pad.component.js.map +1 -0
- package/src/lib/components/console/pos-ops/pos-receipt-actions.component.d.ts +63 -0
- package/src/lib/components/console/pos-ops/pos-receipt-actions.component.js +186 -0
- package/src/lib/components/console/pos-ops/pos-receipt-actions.component.js.map +1 -0
- package/src/lib/components/console/pos-ops/pos-receipt.d.ts +39 -0
- package/src/lib/components/console/pos-ops/pos-receipt.js +182 -0
- package/src/lib/components/console/pos-ops/pos-receipt.js.map +1 -0
- package/src/lib/components/console/pos-ops/pos-return-dialog.component.d.ts +40 -0
- package/src/lib/components/console/pos-ops/pos-return-dialog.component.js +465 -0
- package/src/lib/components/console/pos-ops/pos-return-dialog.component.js.map +1 -0
- package/src/lib/components/console/pos-ops/pos-shift-history-card.component.d.ts +29 -0
- package/src/lib/components/console/pos-ops/pos-shift-history-card.component.js +253 -0
- package/src/lib/components/console/pos-ops/pos-shift-history-card.component.js.map +1 -0
- package/src/lib/components/console/pos-ops/pos-shift-panel.component.d.ts +34 -0
- package/src/lib/components/console/pos-ops/pos-shift-panel.component.js +494 -0
- package/src/lib/components/console/pos-ops/pos-shift-panel.component.js.map +1 -0
- package/src/lib/components/console/pos-ops/pos-shift-report.component.d.ts +37 -0
- package/src/lib/components/console/pos-ops/pos-shift-report.component.js +102 -0
- package/src/lib/components/console/pos-ops/pos-shift-report.component.js.map +1 -0
- package/src/lib/components/console/pos-ops/pos-staff-pins-card.component.d.ts +30 -0
- package/src/lib/components/console/pos-ops/pos-staff-pins-card.component.js +277 -0
- package/src/lib/components/console/pos-ops/pos-staff-pins-card.component.js.map +1 -0
- package/src/lib/components/console/pos-ops/register-ops.d.ts +28 -0
- package/src/lib/components/console/pos-ops/register-ops.js +28 -0
- package/src/lib/components/console/pos-ops/register-ops.js.map +1 -0
- package/src/lib/components/console/pos-ops/use-pos-cashier.d.ts +46 -0
- package/src/lib/components/console/pos-ops/use-pos-cashier.js +128 -0
- package/src/lib/components/console/pos-ops/use-pos-cashier.js.map +1 -0
- package/src/lib/components/console/pos-ops/use-pos-sale-receipt.d.ts +36 -0
- package/src/lib/components/console/pos-ops/use-pos-sale-receipt.js +64 -0
- package/src/lib/components/console/pos-ops/use-pos-sale-receipt.js.map +1 -0
- package/src/lib/components/console/pos-page.component.d.ts +13 -5
- package/src/lib/components/console/pos-page.component.js +756 -654
- package/src/lib/components/console/pos-page.component.js.map +1 -1
- package/src/lib/components/console/pos-zones.d.ts +23 -0
- package/src/lib/components/console/pos-zones.js +31 -0
- package/src/lib/components/console/pos-zones.js.map +1 -0
- package/src/lib/components/console/product-editor-dialog.component.js +61 -71
- package/src/lib/components/console/product-editor-dialog.component.js.map +1 -1
- package/src/lib/components/console/product-labels-dialog.component.d.ts +13 -0
- package/src/lib/components/console/product-labels-dialog.component.js +197 -0
- package/src/lib/components/console/product-labels-dialog.component.js.map +1 -0
- package/src/lib/components/console/product-register-fields.component.d.ts +19 -0
- package/src/lib/components/console/product-register-fields.component.js +241 -0
- package/src/lib/components/console/product-register-fields.component.js.map +1 -0
- package/src/lib/components/console/product-zones.d.ts +44 -0
- package/src/lib/components/console/product-zones.js.map +1 -1
- package/src/lib/components/console/products-hub-card.component.js +23 -20
- package/src/lib/components/console/products-hub-card.component.js.map +1 -1
- package/src/lib/components/console/register-hardware-card.component.d.ts +33 -0
- package/src/lib/components/console/register-hardware-card.component.js +826 -0
- package/src/lib/components/console/register-hardware-card.component.js.map +1 -0
- package/src/lib/components/console/registers-card.component.js +24 -5
- package/src/lib/components/console/registers-card.component.js.map +1 -1
- package/src/lib/components/console/return-actions.d.ts +38 -0
- package/src/lib/components/console/return-actions.js +76 -0
- package/src/lib/components/console/return-actions.js.map +1 -0
- package/src/lib/components/console/return-detail-dialog.component.d.ts +34 -0
- package/src/lib/components/console/return-detail-dialog.component.js +718 -0
- package/src/lib/components/console/return-detail-dialog.component.js.map +1 -0
- package/src/lib/components/console/return-settings-card.component.d.ts +34 -0
- package/src/lib/components/console/return-settings-card.component.js +195 -0
- package/src/lib/components/console/return-settings-card.component.js.map +1 -0
- package/src/lib/components/console/return-zones.d.ts +87 -0
- package/src/lib/components/console/return-zones.js +31 -0
- package/src/lib/components/console/return-zones.js.map +1 -0
- package/src/lib/components/console/returns-card.component.d.ts +45 -0
- package/src/lib/components/console/returns-card.component.js +159 -0
- package/src/lib/components/console/returns-card.component.js.map +1 -0
- package/src/lib/components/console/shipping-settings-card.component.js +161 -5
- package/src/lib/components/console/shipping-settings-card.component.js.map +1 -1
- package/src/lib/components/console/shippingeasy-card.component.d.ts +59 -0
- package/src/lib/components/console/shippingeasy-card.component.js +378 -0
- package/src/lib/components/console/shippingeasy-card.component.js.map +1 -0
- package/src/lib/components/console/shipstation-card.component.d.ts +35 -0
- package/src/lib/components/console/shipstation-card.component.js +345 -0
- package/src/lib/components/console/shipstation-card.component.js.map +1 -0
- package/src/lib/components/console/start-return-dialog.component.d.ts +40 -0
- package/src/lib/components/console/start-return-dialog.component.js +243 -0
- package/src/lib/components/console/start-return-dialog.component.js.map +1 -0
- package/src/lib/components/console/stock-movements-card.component.js +2 -1
- package/src/lib/components/console/stock-movements-card.component.js.map +1 -1
- package/src/lib/components/console/store-settings-card.component.js +2 -66
- package/src/lib/components/console/store-settings-card.component.js.map +1 -1
- package/src/lib/components/console/store-zones.d.ts +42 -0
- package/src/lib/components/console/store-zones.js +39 -0
- package/src/lib/components/console/store-zones.js.map +1 -0
- package/src/lib/components/console/storefront-tax-summary-card.component.js +1 -0
- package/src/lib/components/console/storefront-tax-summary-card.component.js.map +1 -1
- package/src/lib/components/order-status.d.ts +36 -0
- package/src/lib/components/order-status.js +502 -0
- package/src/lib/components/order-status.js.map +1 -0
- package/src/lib/components/product-detail.js +41 -2
- package/src/lib/components/product-detail.js.map +1 -1
- package/src/lib/components/return-request.d.ts +32 -0
- package/src/lib/components/return-request.js +576 -0
- package/src/lib/components/return-request.js.map +1 -0
- package/src/lib/components/storefront-payment-appearance.d.ts +32 -0
- package/src/lib/components/storefront-payment-appearance.js +114 -0
- package/src/lib/components/storefront-payment-appearance.js.map +1 -0
- package/src/lib/components/storefront-payment-element.d.ts +57 -1
- package/src/lib/components/storefront-payment-element.js +332 -33
- package/src/lib/components/storefront-payment-element.js.map +1 -1
- package/src/lib/constants/order-status.d.ts +22 -0
- package/src/lib/constants/order-status.js +22 -0
- package/src/lib/constants/order-status.js.map +1 -0
- package/src/lib/constants/return-request.d.ts +27 -0
- package/src/lib/constants/return-request.js +32 -0
- package/src/lib/constants/return-request.js.map +1 -0
- package/src/lib/constants/stock-movement-query.js +2 -1
- package/src/lib/constants/stock-movement-query.js.map +1 -1
- package/src/lib/declarations.console-server.js +9 -0
- package/src/lib/declarations.console-server.js.map +1 -1
- package/src/lib/declarations.server.d.ts +2 -1
- package/src/lib/declarations.server.js +68 -1
- package/src/lib/declarations.server.js.map +1 -1
- package/src/lib/model/buyer-notifications.d.ts +59 -0
- package/src/lib/model/buyer-notifications.js +97 -0
- package/src/lib/model/buyer-notifications.js.map +1 -0
- package/src/lib/model/commerce-orders.d.ts +171 -2
- package/src/lib/model/commerce-orders.js +26 -16
- package/src/lib/model/commerce-orders.js.map +1 -1
- package/src/lib/model/commerce-payment-methods.d.ts +146 -0
- package/src/lib/model/commerce-payment-methods.js +247 -0
- package/src/lib/model/commerce-payment-methods.js.map +1 -0
- package/src/lib/model/commerce-pos-ops.d.ts +437 -0
- package/src/lib/model/commerce-pos-ops.js +721 -0
- package/src/lib/model/commerce-pos-ops.js.map +1 -0
- package/src/lib/model/commerce-pos.d.ts +263 -0
- package/src/lib/model/commerce-pos.js +375 -0
- package/src/lib/model/commerce-pos.js.map +1 -0
- package/src/lib/model/commerce-printers.d.ts +211 -0
- package/src/lib/model/commerce-printers.js +181 -0
- package/src/lib/model/commerce-printers.js.map +1 -0
- package/src/lib/model/commerce-receipt.d.ts +97 -0
- package/src/lib/model/commerce-receipt.js +180 -0
- package/src/lib/model/commerce-receipt.js.map +1 -0
- package/src/lib/model/commerce-returns.d.ts +207 -0
- package/src/lib/model/commerce-returns.js +300 -0
- package/src/lib/model/commerce-returns.js.map +1 -0
- package/src/lib/model/commerce-shipping.d.ts +44 -1
- package/src/lib/model/commerce-shipping.js +53 -9
- package/src/lib/model/commerce-shipping.js.map +1 -1
- package/src/lib/model/commerce-tax-engine.d.ts +90 -0
- package/src/lib/model/commerce-tax-engine.js +129 -0
- package/src/lib/model/commerce-tax-engine.js.map +1 -0
- package/src/lib/model/commerce.d.ts +91 -2
- package/src/lib/model/commerce.js +89 -1
- package/src/lib/model/commerce.js.map +1 -1
- package/src/lib/model/index.d.ts +16 -0
- package/src/lib/model/index.js +16 -0
- package/src/lib/model/index.js.map +1 -1
- package/src/lib/model/order-events.d.ts +154 -0
- package/src/lib/model/order-events.js +153 -0
- package/src/lib/model/order-events.js.map +1 -0
- package/src/lib/model/order-fulfillment.d.ts +117 -0
- package/src/lib/model/order-fulfillment.js +217 -0
- package/src/lib/model/order-fulfillment.js.map +1 -0
- package/src/lib/model/order-history.d.ts +58 -0
- package/src/lib/model/order-history.js +22 -0
- package/src/lib/model/order-history.js.map +1 -0
- package/src/lib/model/order-list-fields.d.ts +9 -1
- package/src/lib/model/order-list-fields.js +7 -1
- package/src/lib/model/order-list-fields.js.map +1 -1
- package/src/lib/model/order-shipping-export.d.ts +91 -0
- package/src/lib/model/order-shipping-export.js +192 -0
- package/src/lib/model/order-shipping-export.js.map +1 -0
- package/src/lib/model/order-status-view.d.ts +103 -0
- package/src/lib/model/order-status-view.js +156 -0
- package/src/lib/model/order-status-view.js.map +1 -0
- package/src/lib/model/order-webhooks.d.ts +142 -0
- package/src/lib/model/order-webhooks.js +91 -0
- package/src/lib/model/order-webhooks.js.map +1 -0
- package/src/lib/model/product-channel.d.ts +56 -0
- package/src/lib/model/product-channel.js +86 -0
- package/src/lib/model/product-channel.js.map +1 -0
- package/src/lib/model/product-modifiers.d.ts +80 -0
- package/src/lib/model/product-modifiers.js +140 -0
- package/src/lib/model/product-modifiers.js.map +1 -0
- package/src/lib/model/product-write.d.ts +186 -0
- package/src/lib/model/product-write.js +116 -0
- package/src/lib/model/product-write.js.map +1 -0
- package/src/lib/model/shippingeasy.d.ts +94 -0
- package/src/lib/model/shippingeasy.js +267 -0
- package/src/lib/model/shippingeasy.js.map +1 -0
- package/src/lib/model/shipstation.d.ts +125 -0
- package/src/lib/model/shipstation.js +399 -0
- package/src/lib/model/shipstation.js.map +1 -0
- package/src/lib/model/smart-collection-reads.d.ts +32 -0
- package/src/lib/model/smart-collection-reads.js +63 -0
- package/src/lib/model/smart-collection-reads.js.map +1 -0
- package/src/lib/model/tracking-url.d.ts +74 -0
- package/src/lib/model/tracking-url.js +159 -0
- package/src/lib/model/tracking-url.js.map +1 -0
- package/src/lib/plugin-config.d.ts +13 -0
- package/src/lib/plugin-config.js +77 -5
- package/src/lib/plugin-config.js.map +1 -1
- package/src/lib/plugin.js +26 -0
- package/src/lib/plugin.js.map +1 -1
- package/src/lib/pos-ops-config.d.ts +39 -0
- package/src/lib/pos-ops-config.js +88 -0
- package/src/lib/pos-ops-config.js.map +1 -0
- package/src/lib/printing/print-document.d.ts +106 -0
- package/src/lib/printing/print-document.js +464 -0
- package/src/lib/printing/print-document.js.map +1 -0
- package/src/lib/printing/product-labels.d.ts +58 -0
- package/src/lib/printing/product-labels.js +116 -0
- package/src/lib/printing/product-labels.js.map +1 -0
- package/src/lib/printing/render-epson.d.ts +57 -0
- package/src/lib/printing/render-epson.js +135 -0
- package/src/lib/printing/render-epson.js.map +1 -0
- package/src/lib/printing/render-star.d.ts +45 -0
- package/src/lib/printing/render-star.js +100 -0
- package/src/lib/printing/render-star.js.map +1 -0
- package/src/lib/printing/render-text.d.ts +25 -0
- package/src/lib/printing/render-text.js +53 -0
- package/src/lib/printing/render-text.js.map +1 -0
- package/src/lib/server/api-v1/openapi.js +27 -4
- package/src/lib/server/api-v1/openapi.js.map +1 -1
- package/src/lib/server/api-v1/order-view.d.ts +67 -0
- package/src/lib/server/api-v1/order-view.js +120 -0
- package/src/lib/server/api-v1/order-view.js.map +1 -0
- package/src/lib/server/api-v1/orders-and-products.js +58 -65
- package/src/lib/server/api-v1/orders-and-products.js.map +1 -1
- package/src/lib/server/billing-webhook.js +135 -28
- package/src/lib/server/billing-webhook.js.map +1 -1
- package/src/lib/server/cancel-order.js +29 -0
- package/src/lib/server/cancel-order.js.map +1 -1
- package/src/lib/server/carrier-rates-availability.d.ts +26 -0
- package/src/lib/server/carrier-rates-availability.js +76 -0
- package/src/lib/server/carrier-rates-availability.js.map +1 -0
- package/src/lib/server/carrier-shipping.d.ts +84 -0
- package/src/lib/server/carrier-shipping.js +135 -0
- package/src/lib/server/carrier-shipping.js.map +1 -0
- package/src/lib/server/cart-checkout.d.ts +2 -0
- package/src/lib/server/cart-checkout.js +226 -22
- package/src/lib/server/cart-checkout.js.map +1 -1
- package/src/lib/server/channel-order-updates.d.ts +50 -0
- package/src/lib/server/channel-order-updates.js +290 -0
- package/src/lib/server/channel-order-updates.js.map +1 -0
- package/src/lib/server/channel-orders.d.ts +85 -0
- package/src/lib/server/channel-orders.js +543 -0
- package/src/lib/server/channel-orders.js.map +1 -0
- package/src/lib/server/checkout-extras.d.ts +64 -0
- package/src/lib/server/checkout-extras.js +157 -0
- package/src/lib/server/checkout-extras.js.map +1 -0
- package/src/lib/server/{feed.d.ts → checkout-status.d.ts} +2 -7
- package/src/lib/server/checkout-status.js +111 -0
- package/src/lib/server/checkout-status.js.map +1 -0
- package/src/lib/server/checkout.js +86 -9
- package/src/lib/server/checkout.js.map +1 -1
- package/src/lib/server/draft-order.js +34 -5
- package/src/lib/server/draft-order.js.map +1 -1
- package/src/lib/server/fulfill-order.d.ts +127 -10
- package/src/lib/server/fulfill-order.js +451 -49
- package/src/lib/server/fulfill-order.js.map +1 -1
- package/src/lib/server/legacy-feed.d.ts +28 -0
- package/src/lib/server/legacy-feed.js +52 -0
- package/src/lib/server/legacy-feed.js.map +1 -0
- package/src/lib/server/native-checkout.d.ts +34 -15
- package/src/lib/server/native-checkout.js +32 -17
- package/src/lib/server/native-checkout.js.map +1 -1
- package/src/lib/server/order-event-host-emit.d.ts +23 -0
- package/src/lib/server/order-event-host-emit.js +30 -0
- package/src/lib/server/order-event-host-emit.js.map +1 -0
- package/src/lib/server/order-event-triggers.d.ts +37 -0
- package/src/lib/server/order-event-triggers.js +119 -0
- package/src/lib/server/order-event-triggers.js.map +1 -0
- package/src/lib/server/order-events.d.ts +62 -0
- package/src/lib/server/order-events.js +109 -0
- package/src/lib/server/order-events.js.map +1 -0
- package/src/lib/server/order-history.d.ts +23 -0
- package/src/lib/server/order-history.js +65 -0
- package/src/lib/server/order-history.js.map +1 -0
- package/src/lib/server/order-notifications.d.ts +185 -0
- package/src/lib/server/order-notifications.js +567 -0
- package/src/lib/server/order-notifications.js.map +1 -0
- package/src/lib/server/order-receipt-send.d.ts +39 -0
- package/src/lib/server/order-receipt-send.js +151 -0
- package/src/lib/server/order-receipt-send.js.map +1 -0
- package/src/lib/server/order-status-token.d.ts +31 -0
- package/src/lib/server/order-status-token.js +75 -0
- package/src/lib/server/order-status-token.js.map +1 -0
- package/src/lib/server/order-status.d.ts +58 -0
- package/src/lib/server/order-status.js +141 -0
- package/src/lib/server/order-status.js.map +1 -0
- package/src/lib/server/order-webhooks.d.ts +108 -0
- package/src/lib/server/order-webhooks.js +622 -0
- package/src/lib/server/order-webhooks.js.map +1 -0
- package/src/lib/server/orders-shipping-prepare.d.ts +26 -0
- package/src/lib/server/orders-shipping-prepare.js +121 -0
- package/src/lib/server/orders-shipping-prepare.js.map +1 -0
- package/src/lib/server/payment-method-domains.d.ts +209 -0
- package/src/lib/server/payment-method-domains.js +444 -0
- package/src/lib/server/payment-method-domains.js.map +1 -0
- package/src/lib/server/payment-methods.d.ts +77 -0
- package/src/lib/server/payment-methods.js +284 -0
- package/src/lib/server/payment-methods.js.map +1 -0
- package/src/lib/server/pos-auth.d.ts +50 -0
- package/src/lib/server/pos-auth.js +121 -0
- package/src/lib/server/pos-auth.js.map +1 -0
- package/src/lib/server/pos-customer.d.ts +49 -0
- package/src/lib/server/pos-customer.js +257 -0
- package/src/lib/server/pos-customer.js.map +1 -0
- package/src/lib/server/pos-display.d.ts +57 -0
- package/src/lib/server/pos-display.js +419 -0
- package/src/lib/server/pos-display.js.map +1 -0
- package/src/lib/server/pos-fee-month.d.ts +34 -0
- package/src/lib/server/pos-fee-month.js +36 -0
- package/src/lib/server/pos-fee-month.js.map +1 -0
- package/src/lib/server/pos-ops-gate.d.ts +161 -0
- package/src/lib/server/pos-ops-gate.js +295 -0
- package/src/lib/server/pos-ops-gate.js.map +1 -0
- package/src/lib/server/pos-ops-routes.d.ts +22 -0
- package/src/lib/server/pos-ops-routes.js +36 -0
- package/src/lib/server/pos-ops-routes.js.map +1 -0
- package/src/lib/server/pos-order.d.ts +1 -18
- package/src/lib/server/pos-order.js +222 -42
- package/src/lib/server/pos-order.js.map +1 -1
- package/src/lib/server/pos-payment.d.ts +39 -0
- package/src/lib/server/pos-payment.js +922 -0
- package/src/lib/server/pos-payment.js.map +1 -0
- package/src/lib/server/pos-print.d.ts +82 -0
- package/src/lib/server/pos-print.js +193 -0
- package/src/lib/server/pos-print.js.map +1 -0
- package/src/lib/server/pos-readers.d.ts +27 -0
- package/src/lib/server/pos-readers.js +276 -0
- package/src/lib/server/pos-readers.js.map +1 -0
- package/src/lib/server/pos-refund-authority.d.ts +60 -0
- package/src/lib/server/pos-refund-authority.js +72 -0
- package/src/lib/server/pos-refund-authority.js.map +1 -0
- package/src/lib/server/pos-return.d.ts +58 -0
- package/src/lib/server/pos-return.js +891 -0
- package/src/lib/server/pos-return.js.map +1 -0
- package/src/lib/server/pos-sale-stamp.d.ts +66 -0
- package/src/lib/server/pos-sale-stamp.js +84 -0
- package/src/lib/server/pos-sale-stamp.js.map +1 -0
- package/src/lib/server/pos-sale.d.ts +170 -0
- package/src/lib/server/pos-sale.js +461 -0
- package/src/lib/server/pos-sale.js.map +1 -0
- package/src/lib/server/pos-shift.d.ts +45 -0
- package/src/lib/server/pos-shift.js +484 -0
- package/src/lib/server/pos-shift.js.map +1 -0
- package/src/lib/server/pos-staff-pin.d.ts +26 -0
- package/src/lib/server/pos-staff-pin.js +339 -0
- package/src/lib/server/pos-staff-pin.js.map +1 -0
- package/src/lib/server/pos-stripe.d.ts +91 -0
- package/src/lib/server/pos-stripe.js +129 -0
- package/src/lib/server/pos-stripe.js.map +1 -0
- package/src/lib/server/pos-terminal-connection-token.d.ts +48 -0
- package/src/lib/server/pos-terminal-connection-token.js +166 -0
- package/src/lib/server/pos-terminal-connection-token.js.map +1 -0
- package/src/lib/server/pos-terminal.d.ts +218 -0
- package/src/lib/server/pos-terminal.js +1014 -0
- package/src/lib/server/pos-terminal.js.map +1 -0
- package/src/lib/server/print-queue.d.ts +124 -0
- package/src/lib/server/print-queue.js +301 -0
- package/src/lib/server/print-queue.js.map +1 -0
- package/src/lib/server/printer-poll.d.ts +75 -0
- package/src/lib/server/printer-poll.js +325 -0
- package/src/lib/server/printer-poll.js.map +1 -0
- package/src/lib/server/printer-secret.d.ts +39 -0
- package/src/lib/server/printer-secret.js +56 -0
- package/src/lib/server/printer-secret.js.map +1 -0
- package/src/lib/server/printers.d.ts +114 -0
- package/src/lib/server/printers.js +546 -0
- package/src/lib/server/printers.js.map +1 -0
- package/src/lib/server/product-catalog.d.ts +42 -0
- package/src/lib/server/product-catalog.js +309 -0
- package/src/lib/server/product-catalog.js.map +1 -0
- package/src/lib/server/product-drafts.d.ts +5 -1
- package/src/lib/server/product-drafts.js +3 -3
- package/src/lib/server/product-drafts.js.map +1 -1
- package/src/lib/server/product-writer.d.ts +19 -0
- package/src/lib/server/product-writer.js +261 -0
- package/src/lib/server/product-writer.js.map +1 -0
- package/src/lib/server/products-write.d.ts +61 -0
- package/src/lib/server/products-write.js +375 -0
- package/src/lib/server/products-write.js.map +1 -0
- package/src/lib/server/refund.js +110 -45
- package/src/lib/server/refund.js.map +1 -1
- package/src/lib/server/reserve.js +2 -0
- package/src/lib/server/reserve.js.map +1 -1
- package/src/lib/server/return-status-action.d.ts +37 -0
- package/src/lib/server/return-status-action.js +57 -0
- package/src/lib/server/return-status-action.js.map +1 -0
- package/src/lib/server/returns.d.ts +119 -0
- package/src/lib/server/returns.js +938 -0
- package/src/lib/server/returns.js.map +1 -0
- package/src/lib/server/shipment-records.d.ts +36 -0
- package/src/lib/server/shipment-records.js +289 -0
- package/src/lib/server/shipment-records.js.map +1 -0
- package/src/lib/server/shipping-connector-gates.d.ts +40 -0
- package/src/lib/server/shipping-connector-gates.js +97 -0
- package/src/lib/server/shipping-connector-gates.js.map +1 -0
- package/src/lib/server/shipping-connectors.d.ts +29 -0
- package/src/lib/server/shipping-connectors.js +236 -0
- package/src/lib/server/shipping-connectors.js.map +1 -0
- package/src/lib/server/shippingeasy.d.ts +238 -0
- package/src/lib/server/shippingeasy.js +787 -0
- package/src/lib/server/shippingeasy.js.map +1 -0
- package/src/lib/server/shipstation.d.ts +129 -0
- package/src/lib/server/shipstation.js +483 -0
- package/src/lib/server/shipstation.js.map +1 -0
- package/src/lib/server/site-page-resolver.d.ts +5 -0
- package/src/lib/server/site-page-resolver.js +156 -1
- package/src/lib/server/site-page-resolver.js.map +1 -1
- package/src/lib/server/stock-levels.d.ts +21 -0
- package/src/lib/server/stock-levels.js +263 -0
- package/src/lib/server/stock-levels.js.map +1 -0
- package/src/lib/server/stripe-refund.d.ts +47 -0
- package/src/lib/server/stripe-refund.js +87 -0
- package/src/lib/server/stripe-refund.js.map +1 -0
- package/src/lib/server/supplier-update.js +46 -5
- package/src/lib/server/supplier-update.js.map +1 -1
- package/src/lib/server/tax-engine-quote.d.ts +47 -0
- package/src/lib/server/tax-engine-quote.js +105 -0
- package/src/lib/server/tax-engine-quote.js.map +1 -0
- package/src/lib/server.js +143 -2
- package/src/lib/server.js.map +1 -1
- package/src/lib/site.js +12 -0
- package/src/lib/site.js.map +1 -1
- package/src/lib/subprocessors.d.ts +26 -0
- package/src/lib/subprocessors.js +50 -0
- package/src/lib/subprocessors.js.map +1 -0
- package/src/lib/tenant-emails.js +461 -3
- package/src/lib/tenant-emails.js.map +1 -1
- package/src/lib/testing/fake-firestore.d.ts +45 -0
- package/src/lib/testing/fake-firestore.js +189 -0
- package/src/lib/testing/fake-firestore.js.map +1 -0
- package/src/lib/testing/memory-firestore.d.ts +136 -0
- package/src/lib/testing/memory-firestore.js +282 -0
- package/src/lib/testing/memory-firestore.js.map +1 -0
- package/src/lib/testing/pos-ops-harness.d.ts +50 -0
- package/src/lib/testing/pos-ops-harness.js +184 -0
- package/src/lib/testing/pos-ops-harness.js.map +1 -0
- package/src/lib/testing/pos-ops-memory-firestore.d.ts +55 -0
- package/src/lib/testing/pos-ops-memory-firestore.js +376 -0
- package/src/lib/testing/pos-ops-memory-firestore.js.map +1 -0
- package/src/lib/transfer/products.server.d.ts +5 -0
- package/src/lib/transfer/products.server.js +2 -2
- package/src/lib/transfer/products.server.js.map +1 -1
- package/src/lib/transfer/records-transfer.d.ts +11 -1
- package/src/lib/transfer/records-transfer.js +127 -5
- package/src/lib/transfer/records-transfer.js.map +1 -1
- package/src/lib/transfer/records.server.d.ts +9 -2
- package/src/lib/transfer/records.server.js +58 -7
- package/src/lib/transfer/records.server.js.map +1 -1
- package/src/lib/transfer/register-transfer-resources.js +40 -1
- package/src/lib/transfer/register-transfer-resources.js.map +1 -1
- package/src/lib/transfer/register-transfer-ui.js +4 -1
- package/src/lib/transfer/register-transfer-ui.js.map +1 -1
- package/src/lib/transfer/shipping-presets.d.ts +23 -0
- package/src/lib/transfer/shipping-presets.js +156 -0
- package/src/lib/transfer/shipping-presets.js.map +1 -0
- package/src/lib/transfer/tracking-transfer.d.ts +101 -0
- package/src/lib/transfer/tracking-transfer.js +546 -0
- package/src/lib/transfer/tracking-transfer.js.map +1 -0
- package/src/lib/transfer/tracking.server.d.ts +37 -0
- package/src/lib/transfer/tracking.server.js +393 -0
- package/src/lib/transfer/tracking.server.js.map +1 -0
- package/src/lib/transfer/transfer-keys.d.ts +2 -1
- package/src/lib/transfer/transfer-keys.js +6 -2
- package/src/lib/transfer/transfer-keys.js.map +1 -1
- package/src/lib/utils/code128.d.ts +37 -0
- package/src/lib/utils/code128.js +199 -0
- package/src/lib/utils/code128.js.map +1 -0
- package/src/lib/utils/order-invoice.d.ts +55 -0
- package/src/lib/utils/order-invoice.js +127 -0
- package/src/lib/utils/order-invoice.js.map +1 -0
- package/src/lib/utils/use-checkout-return-notice.d.ts +34 -0
- package/src/lib/utils/use-checkout-return-notice.js +78 -0
- package/src/lib/utils/use-checkout-return-notice.js.map +1 -0
- package/src/lib/server/feed.js +0 -69
- package/src/lib/server/feed.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/model/commerce-shipping.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 * Shipping v1 (AGL-288): zones own countries; rates belong to a zone and\n * price as flat, free-over-threshold, or subtotal/weight tiers. Settings\n * live on `hosts/{hostId}/settings/store` under `shipping`. Pure.\n *\n * `resolveShippingRates` is the per-destination resolver;\n * `resolveCheckoutShippingOptions` is the Stripe Checkout adapter,\n * `planCheckoutShipping` decides which destinations one session may serve,\n * `appendCheckoutShippingParams` emits it onto a session, and\n * `summarizeShippingCoverage` answers the same question for the console, so a\n * merchant can see which destinations their zones refuse. AGL-288 shipped this\n * model with no production call site at all — the doc comment here claimed the\n * cart estimator, checkout and POS pickup used it, and none of them did, so\n * nothing ever read the settings a merchant saved and no shipping was ever\n * charged. Cart checkout calls it (AGL-1707), buy-now calls it for physical\n * one-time products (AGL-1720), and a draft order's payment link calls it for\n * physical products (AGL-1792).\n *\n * POS DOES NOT, AND SHOULD NOT (AGL-1792). A register has no destination\n * anywhere in it — no address field, no delivery choice — and all three\n * tenders settle at the counter: cash, a card QR the customer scans while\n * standing there, and a room folio collected at check-out. There is nothing to\n * price a parcel against, so the only rate POS could resolve is the free\n * `Local pickup` one this module appends, which is 0¢ and would add a step to\n * a register flow in exchange for nothing. A POS sale that must be shipped is\n * a draft order.\n */\n\nexport interface ShippingZone {\n id: string\n name: string\n /** ISO-3166 alpha-2 codes; '*' = rest of world. */\n countries: string[]\n}\n\nexport interface ShippingTier {\n /** Tier applies while subtotal (cents) or weight (grams) ≤ this. */\n upTo: number\n amountCents: number\n}\n\nexport interface ShippingRate {\n id: string\n zoneId: string\n name: string\n kind: 'flat' | 'free_over' | 'price_tiers' | 'weight_tiers'\n /** flat + free_over base amount. */\n amountCents?: number\n /** free_over: order subtotal (cents) at/above which shipping is free. */\n freeOverCents?: number\n /** price_tiers (upTo = subtotal cents) / weight_tiers (upTo = grams). */\n tiers?: ShippingTier[]\n}\n\nexport interface ShippingSettings {\n zones?: ShippingZone[]\n rates?: ShippingRate[]\n /**\n * Offer free local pickup as a delivery choice. Does NOT widen where the\n * store ships: a destination no rate reaches is still refused (AGL-2325).\n */\n localPickup?: boolean\n}\n\nexport interface ResolvedShippingRate {\n rateId: string\n name: string\n amountCents: number\n}\n\n/**\n * The rate id appended for local pickup (AGL-2325).\n *\n * Exported because pickup is the one entry in a resolved list that is NOT a\n * shipping promise: it is appended AFTER and INDEPENDENT of zone matching, so\n * it resolves for every destination on earth. Anything asking \"does this store\n * ship there\" — the checkout refusal, the console coverage card — has to be\n * able to tell it apart from a rate, or it reads a merchant who offers\n * collection as a merchant who delivers worldwide.\n */\nexport const LOCAL_PICKUP_RATE_ID = 'pickup'\n\n/**\n * One stored country code, normalised (AGL-2298).\n *\n * TRIMMED as well as upper-cased. The console trims on save, so this only\n * matters for a document written any other way — an import, the REST API, a\n * hand edit — but the failure it produces is silent and expensive: `' US'`\n * upper-cases to `' US'`, matches nothing, and the store refuses every US\n * shopper with \"This store does not ship to the United States\" while its\n * settings card shows `US` right there.\n */\nconst zoneCode = (code: unknown): string => String(code ?? '').trim().toUpperCase()\n\n/** Whether a stored code is the rest-of-world wildcard. */\nconst isWildcard = (code: unknown): boolean => zoneCode(code) === '*'\n\nfunction zoneMatches(zone: ShippingZone, country: string): boolean {\n return (zone.countries ?? []).some(\n (code) => isWildcard(code) || zoneCode(code) === country,\n )\n}\n\nfunction tierAmount(\n tiers: ShippingTier[] | undefined,\n value: number,\n): number | null {\n const sorted = [...(tiers ?? [])].sort((a, b) => a.upTo - b.upTo)\n for (const tier of sorted) {\n if (value <= tier.upTo) return tier.amountCents\n }\n // Beyond the last tier the rate does not apply (merchant should add a\n // catch-all tier with a large upTo).\n return null\n}\n\n/**\n * Rates available for a destination + cart, cheapest first. Specific\n * country zones beat '*' zones: when any specific zone matches, '*'\n * zones are ignored (rest-of-world semantics).\n *\n * Local pickup is appended AFTER and INDEPENDENT of zone matching, so this\n * list is never empty for a merchant who offers it, at any destination. That\n * is correct for a resolver — a shopper anywhere may choose to come and\n * collect — but it means A NON-EMPTY RESULT IS NOT A PROMISE TO SHIP THERE.\n * Callers deciding whether a destination is served must filter pickup out\n * first; `planCheckoutShipping` and `summarizeShippingCoverage` do (AGL-2325).\n */\nexport function resolveShippingRates(\n settings: ShippingSettings | undefined,\n destinationCountry: string | undefined,\n cart: { subtotalCents: number; totalGrams?: number },\n): ResolvedShippingRate[] {\n if (!settings || !destinationCountry) return []\n const country = destinationCountry.toUpperCase()\n const zones = settings.zones ?? []\n // A zone is SPECIFIC when it names this country outright — not merely when\n // it is \"not wildcard-only\" (AGL-2298). The old test was `!every(code ===\n // '*')`, so a zone spelled `['*','US']` counted as specific for all six\n // destinations (it matches every country through the `*`) and suppressed\n // every genuine rest-of-world zone globally. Asking the narrower question\n // makes such a zone specific for `US` and wildcard for the rest, which is\n // what its two entries actually say.\n const specific = zones.filter((zone) =>\n (zone.countries ?? []).some((code) => zoneCode(code) === country),\n )\n const matched = specific.length\n ? specific\n : zones.filter((zone) => zoneMatches(zone, country))\n const zoneIds = new Set(matched.map((zone) => zone.id))\n const resolved: ResolvedShippingRate[] = []\n for (const rate of settings.rates ?? []) {\n if (!zoneIds.has(rate.zoneId)) continue\n let amountCents: number | null = null\n switch (rate.kind) {\n case 'flat':\n amountCents = Math.max(0, Math.round(rate.amountCents ?? 0))\n break\n case 'free_over':\n amountCents =\n rate.freeOverCents != null &&\n cart.subtotalCents >= rate.freeOverCents\n ? 0\n : Math.max(0, Math.round(rate.amountCents ?? 0))\n break\n case 'price_tiers':\n amountCents = tierAmount(rate.tiers, cart.subtotalCents)\n break\n case 'weight_tiers':\n amountCents = tierAmount(rate.tiers, cart.totalGrams ?? 0)\n break\n }\n if (amountCents == null) continue\n resolved.push({ rateId: rate.id, name: rate.name, amountCents })\n }\n if (settings.localPickup) {\n resolved.push({\n rateId: LOCAL_PICKUP_RATE_ID,\n name: 'Local pickup',\n amountCents: 0,\n })\n }\n return resolved.sort((a, b) => a.amountCents - b.amountCents)\n}\n\n/**\n * The resolved options that actually carry a parcel (AGL-2325).\n *\n * Safe to apply AFTER `MAX_CHECKOUT_SHIPPING_OPTIONS` truncation: pickup is\n * 0¢ and sorts first, so it occupies at most one of the five slots and a\n * merchant with any shippable rate at all keeps at least one of them.\n */\nfunction shippableOnly(\n options: readonly ResolvedShippingRate[],\n): ResolvedShippingRate[] {\n return options.filter((option) => option.rateId !== LOCAL_PICKUP_RATE_ID)\n}\n\n/** Stripe Checkout accepts at most 5 `shipping_options` on one session. */\nexport const MAX_CHECKOUT_SHIPPING_OPTIONS = 5\n\n/**\n * Destinations a storefront Checkout Session collects an address for, and\n * therefore the destinations shipping rates are resolved against (AGL-1707).\n *\n * NOT narrowed to the zones the merchant configured: narrowing would block\n * checkouts that complete today, turning a money fix into lost sales. The\n * wider list only means a shopper outside the merchant's zones is offered some\n * rate rather than none. Shared by cart checkout and buy-now (AGL-1720) so the\n * two paths cannot drift into offering different destinations.\n */\nexport const CHECKOUT_SHIPPING_COUNTRIES = [\n 'US',\n 'CA',\n 'GB',\n 'AU',\n 'DE',\n 'FR',\n] as const\n\n/**\n * The rates to declare as `shipping_options` on a Stripe Checkout Session\n * (AGL-1707), unioned over `destinationCountries`. Stripe charges shipping\n * ONLY when the session declares them.\n *\n * A UNION IS ONLY HONEST OVER DESTINATIONS THE SESSION IS ALSO RESTRICTED TO.\n * Called with every collectable country while the session accepted an address\n * in any of them — which is what AGL-1707 shipped — it offers a shopper in one\n * zone a rate belonging to another, and Stripe applies whichever the shopper\n * picks (a `shipping_rate_data` carries no country of its own, so nothing\n * downstream re-checks it against the address). That is AGL-1721. Callers now\n * reach this through `planCheckoutShipping`, which pairs the country list it\n * unions over with the country list the session allows, so the two cannot\n * disagree. Do not call it directly from a handler.\n *\n * Returns `[]` for a merchant who has configured nothing, and the caller must\n * then declare no `shipping_options` at all rather than an empty array — a\n * merchant who never set shipping up keeps a session identical to today's.\n */\nexport function resolveCheckoutShippingOptions(\n settings: ShippingSettings | undefined,\n destinationCountries: readonly string[],\n cart: { subtotalCents: number; totalGrams?: number },\n): ResolvedShippingRate[] {\n if (!settings) return []\n const byRateId = new Map<string, ResolvedShippingRate>()\n for (const country of destinationCountries) {\n for (const rate of resolveShippingRates(settings, country, cart)) {\n // Stripe requires a display name, and a rate id is the dedupe key —\n // the console lets a merchant add a blank row, so neither is assured.\n if (!rate.rateId || !rate.name) continue\n const amountCents = Math.max(0, Math.round(Number(rate.amountCents) || 0))\n const existing = byRateId.get(rate.rateId)\n // One rate resolving differently for two countries should not be\n // possible (a zone selects a rate, it does not reprice one), but if it\n // ever is, keep the dearer. Under-charging is the defect being fixed.\n if (!existing || amountCents > existing.amountCents) {\n byRateId.set(rate.rateId, { ...rate, amountCents })\n }\n }\n }\n return (\n [...byRateId.values()]\n // Cheapest first, id as a tie-break so the emitted params are stable.\n .sort(\n (a, b) =>\n a.amountCents - b.amountCents || a.rateId.localeCompare(b.rateId),\n )\n // Past Stripe's cap the dearest are dropped, which is the harmless end\n // to lose: a shopper offered the cheap options would have taken one.\n .slice(0, MAX_CHECKOUT_SHIPPING_OPTIONS)\n )\n}\n\n/** Shopper-facing names for the collectable destinations (AGL-1721). */\nexport const CHECKOUT_SHIPPING_COUNTRY_NAMES: Readonly<Record<string, string>> =\n {\n US: 'United States',\n CA: 'Canada',\n GB: 'United Kingdom',\n AU: 'Australia',\n DE: 'Germany',\n FR: 'France',\n }\n\n/**\n * A destination a shopper declared, or `undefined` for anything this session\n * could not collect an address for anyway. Never throws: an unknown code is\n * an undeclared destination, not an error, so a stale storefront that posts\n * one is answered by the plan below rather than by a 400 nobody can act on.\n */\nexport function normalizeCheckoutShippingCountry(\n value: unknown,\n): string | undefined {\n // `typeof` rather than `String(value)`: a JSON body is caller-shaped, and\n // `String(['US'])` is 'US'. Coercing would let an array declare a\n // destination, which is not a spelling any storefront sends.\n if (typeof value !== 'string') return undefined\n const code = value.trim().toUpperCase()\n return (CHECKOUT_SHIPPING_COUNTRIES as readonly string[]).includes(code)\n ? code\n : undefined\n}\n\nexport interface CheckoutShippingPlan {\n /** `shipping_address_collection[allowed_countries]` for this session. */\n countries: readonly string[]\n /** `shipping_options` for this session — valid for EVERY `countries` entry. */\n options: ResolvedShippingRate[]\n /**\n * Set when no session may be created at all:\n *\n * - `destination-required` — the merchant's rates differ by destination, so\n * there is no set of options honest for every address Stripe would accept.\n * Ask the shopper where they are shipping and plan again.\n * - `destination-unserved` — the merchant ships somewhere, but not there.\n * - `cart-unpriceable` — the merchant priced shipping, but no rate of theirs\n * resolves for THIS cart at ANY destination (AGL-2232). A tier table that\n * stops at 2 kg says nothing about a 3 kg parcel, and the honest answer is\n * to refuse rather than to ship it for nothing.\n */\n refusal?: 'destination-required' | 'destination-unserved' | 'cart-unpriceable'\n}\n\n/**\n * Whether this merchant prices shipping AT ALL (AGL-2232).\n *\n * A pure question about the SETTINGS, deliberately taking no cart, and that is\n * the whole point of it existing. `planCheckoutShipping` used to ask \"does any\n * rate resolve for any destination?\" and pass the live cart into the probe, so\n * a cart that exhausted every tier made the probe empty too — and an empty\n * probe reads as \"this merchant never set shipping up\", the one case that must\n * complete with no `shipping_options` and nothing charged. A 3 kg order at a\n * store whose weight tiers stop at 2 kg therefore shipped free, silently, on\n * every one of the three doors.\n *\n * Answered from the stored zones and rates instead: a rate bound to a zone\n * that exists, or local pickup, means the merchant made a shipping decision\n * and a cart their table cannot price is a REFUSAL. A merchant with no zones,\n * no rates and no pickup has made no such decision, and their session stays\n * byte-identical to the one AGL-1707 shipped.\n */\nexport function merchantPricesShipping(\n settings: ShippingSettings | undefined,\n): boolean {\n if (!settings) return false\n if (settings.localPickup) return true\n const zoneIds = new Set((settings.zones ?? []).map((zone) => zone.id))\n // A rate whose `zoneId` names no zone is unreachable by every resolver here,\n // so it is not evidence of a decision — counting it would refuse carts at a\n // store with nothing but orphaned rows.\n return (settings.rates ?? []).some((rate) => zoneIds.has(rate.zoneId))\n}\n\n/**\n * What a shopper is told when `cart-unpriceable` refuses (AGL-2232).\n *\n * One string, shared by all three doors, because the three used to phrase the\n * two existing refusals identically and drifting here would make the same\n * store say different things depending on which button was pressed. It names\n * the cart rather than the destination on purpose — a `Ship to` select cannot\n * fix a parcel no tier covers, so the refusal deliberately does NOT set\n * `needsShippingCountry`.\n */\nexport const CART_UNPRICEABLE_SHIPPING_MESSAGE =\n 'This store cannot price shipping for this order — it falls outside every ' +\n 'shipping rate the store has set up. Try removing an item, or contact the ' +\n 'store.'\n\n/** Identity of a resolved list, for comparing two destinations' offers. */\nfunction optionsKey(options: readonly ResolvedShippingRate[]): string {\n return options\n .map((option) => `${option.rateId}:${option.amountCents}`)\n .join('|')\n}\n\n/**\n * The whole shipping half of a Checkout Session: which destinations it may\n * accept an address for, and which rates it may charge (AGL-1721).\n *\n * THE INVARIANT IS THAT EVERY RETURNED OPTION IS VALID FOR EVERY RETURNED\n * COUNTRY. Stripe offers a session's `shipping_options` to whoever reaches the\n * page and charges whichever the shopper picks — it does not filter them by\n * the address entered, and there is no field on a `shipping_rate_data` that\n * would let it. So the ONLY place a zone can be enforced is here, by choosing\n * the two lists together: narrowing the allowed countries to the declared\n * destination is what makes the rates offered for it unpickable by anyone\n * else. Filtering the rate list alone would leave a session that still accepts\n * a French address against a US rate.\n *\n * Three shapes come out of it:\n *\n * 1. A merchant whose rates resolve the SAME for every collectable country —\n * one zone, a single '*' zone, or none at all — needs no destination and\n * keeps the session AGL-1707 shipped, byte for byte. The issue notes this\n * is the common configuration, and it is the one that must not regress.\n * 2. A declared destination resolves exactly, and the session is restricted\n * to it. A caller may not skip the restriction: the country arrives in a\n * request body a shopper can craft, so a session that took the country's\n * cheap rate while still accepting any address would hand that shopper\n * the defect on purpose.\n * 3. Otherwise the caller must refuse — see `refusal`. Refusing is not lost\n * revenue in either case: with rates that differ by zone there is no\n * correct amount to charge without knowing where the parcel is going, and\n * the union AGL-1707 shipped chose the wrong one silently.\n *\n * `cart.subtotalCents` is the PRE-discount items total, matching what the cart\n * displays, so a free-over threshold means what the shopper saw it mean.\n */\nexport function planCheckoutShipping(\n settings: ShippingSettings | undefined,\n cart: { subtotalCents: number; totalGrams?: number },\n destinationCountry?: unknown,\n): CheckoutShippingPlan {\n const countries = CHECKOUT_SHIPPING_COUNTRIES as readonly string[]\n const destination = normalizeCheckoutShippingCountry(destinationCountry)\n if (destination) {\n const options = resolveCheckoutShippingOptions(\n settings,\n [destination],\n cart,\n )\n // REFUSAL IS DECIDED ON SHIPPABLE RATES, PICKUP IS OFFERED AFTERWARDS\n // (AGL-2325). `resolveShippingRates` appends pickup independent of zone\n // matching, so a merchant who turned it on had a non-empty list for every\n // destination on earth and this branch returned before it could refuse\n // anyone: a shopper in France picked `Local pickup — Free` on a physical\n // order from a US-only store, Stripe collected their French address\n // because the session still allowed it, and the merchant was paid nothing\n // to carry the parcel there. Pickup is collection, not delivery, so it\n // cannot answer \"do you ship to France\".\n //\n // `shipsElsewhere` is the same question the old probe asked, narrowed to\n // shippable rates, and it keeps the same two-way fork: a merchant who\n // configured NO shipping is not refusing anyone, so that store keeps\n // completing exactly as it does today, while a merchant who ships\n // elsewhere but not here is refusing and saying so is the honest answer.\n // A merchant who ships NOWHERE is untouched by this: pickup-only is a\n // real configuration — they deliver to no one and refuse no one — and it\n // falls through to the offer below exactly as it did.\n const shipsElsewhere = countries.some(\n (country) =>\n shippableOnly(resolveCheckoutShippingOptions(settings, [country], cart))\n .length > 0,\n )\n if (shippableOnly(options).length === 0 && shipsElsewhere) {\n return { countries, options: [], refusal: 'destination-unserved' }\n }\n if (options.length > 0) return { countries: [destination], options }\n // NOTHING resolves anywhere for this cart, and the two reasons that can be\n // true of are opposite (AGL-2232). Asked of the SETTINGS rather than of\n // this cart, because the probe above cannot tell them apart: a merchant\n // who priced shipping and whose table simply does not reach this order is\n // refusing, and used to be handed the \"never set shipping up\" branch.\n return merchantPricesShipping(settings)\n ? { countries, options: [], refusal: 'cart-unpriceable' }\n : { countries, options: [] }\n }\n const offers = new Set(\n countries.map((country) =>\n optionsKey(resolveCheckoutShippingOptions(settings, [country], cart)),\n ),\n )\n // Compared as EMITTED — capped and sanitized — because that is the list a\n // shopper can pick from. Two destinations whose rates differ only past\n // Stripe's cap still offer the same five, and offering them is honest.\n if (offers.size > 1) {\n return { countries, options: [], refusal: 'destination-required' }\n }\n const union = resolveCheckoutShippingOptions(settings, countries, cart)\n // Every destination agrees, and what they agree on is NOTHING. The same\n // fork as the declared-destination branch above (AGL-2232): a store with no\n // shipping settings completes untouched, a store whose rates cannot reach\n // this cart refuses instead of carrying the parcel for free.\n if (union.length === 0 && merchantPricesShipping(settings)) {\n return { countries, options: [], refusal: 'cart-unpriceable' }\n }\n return { countries, options: union }\n}\n\n/**\n * A cart chosen so that every rate a merchant could have written resolves if\n * it can resolve at all (AGL-1791). The settings page has no cart, and two of\n * the four rate kinds price against one: `tierAmount` takes the first tier\n * whose `upTo` is at least the value, so a zero subtotal and zero weight clear\n * every tier a merchant would write, and a `free_over` rate resolves at any\n * subtotal.\n *\n * That makes a coverage answer a LOWER BOUND on the gaps. A destination\n * reported unserved is unserved for every cart; a destination reported served\n * may still be refused for a cart dearer or heavier than the merchant's last\n * tier. Erring in this direction is the deliberate half: the console must not\n * warn a merchant whose settings are fine.\n */\nconst COVERAGE_PROBE_CART = { subtotalCents: 0, totalGrams: 0 }\n\n/** What a merchant's saved zones and rates cost their shoppers (AGL-1791). */\nexport interface ShippingCoverage {\n /** Collectable destinations at least one rate reaches. */\n served: string[]\n /**\n * Collectable destinations CHECKOUT REFUSES — `destination-unserved`.\n *\n * EMPTY FOR A MERCHANT WHOSE RATES REACH NOWHERE, who is not misconfigured:\n * `planCheckoutShipping` refuses nobody when no rate resolves anywhere, so\n * that store charges no shipping and completes exactly as it always has.\n * The rule lives here rather than in the caller because reading `served`\n * against a bare partition is how a surface comes to warn every store that\n * does not ship.\n */\n unserved: string[]\n /** No rate reaches anywhere: no shipping is charged, and nobody refused. */\n shipsNowhere: boolean\n /**\n * Rates differ by destination, so the storefront asks the shopper where the\n * parcel is going before it can price one — a checkout step the merchant\n * added by saving these zones, and never chose.\n */\n asksDestination: boolean\n /** Some zone already claims rest of world, so `*` is not the missing piece. */\n hasRestOfWorldZone: boolean\n /**\n * The merchant offers local pickup, so checkout presents a free collection\n * choice at every destination it does not refuse (AGL-2325).\n *\n * Deliberately NOT folded into `served`: pickup resolves for every country\n * because it is appended independent of zone matching, so counting it as\n * coverage told a US-only merchant who had switched it on that checkout\n * \"can price shipping to every destination\", which is the sentence that\n * hid the defect. It is reported alongside instead, so the card can say\n * both true things: these destinations are refused, and pickup is offered\n * to the ones that are not.\n */\n offersLocalPickup: boolean\n}\n\n/**\n * Which of the collectable destinations a merchant's settings actually cover\n * (AGL-1791), for a console surface to show them.\n *\n * Measured against `CHECKOUT_SHIPPING_COUNTRIES` and nothing wider: those six\n * are the only destinations a storefront session collects an address for, so\n * they are the only ones a merchant can lose an order to. Counting coverage\n * against every country on earth would report a gap for destinations no\n * shopper can reach checkout from.\n *\n * Answered through `resolveCheckoutShippingOptions` — the same function the\n * session is built from — so a row the console allows but Stripe would reject\n * (blank id, blank name) counts as the nothing it resolves to, and a specific\n * zone with no rates still hides a `*` zone here exactly as it does at\n * checkout.\n */\nexport function summarizeShippingCoverage(\n settings: ShippingSettings | undefined,\n): ShippingCoverage {\n const served: string[] = []\n const unserved: string[] = []\n const offers = new Set<string>()\n for (const country of CHECKOUT_SHIPPING_COUNTRIES) {\n const options = resolveCheckoutShippingOptions(\n settings,\n [country],\n COVERAGE_PROBE_CART,\n )\n // `asksDestination` is asked of the FULL list — pickup is a real option a\n // shopper picks, so a merchant whose US destination offers a rate plus\n // pickup while France offers only pickup genuinely does make checkout ask.\n offers.add(optionsKey(options))\n // `served` is asked of the SHIPPABLE list, so that it answers the same\n // question `planCheckoutShipping` refuses on (AGL-2325).\n ;(shippableOnly(options).length > 0 ? served : unserved).push(country)\n }\n return {\n served,\n unserved: served.length > 0 ? unserved : [],\n shipsNowhere: served.length === 0,\n asksDestination: offers.size > 1,\n offersLocalPickup: Boolean(settings?.localPickup),\n hasRestOfWorldZone: (settings?.zones ?? []).some((zone) =>\n zone.countries.some((code) => code === '*'),\n ),\n }\n}\n\n/**\n * Emit `shipping_address_collection[allowed_countries][n]` onto a session's\n * form body. Stripe will not apply a `shipping_options` rate without an\n * address to ship to, so the two always travel together — and the countries\n * are the OTHER half of what makes a rate honest (AGL-1721), so pass the list\n * `planCheckoutShipping` returned rather than defaulting it beside a rate list\n * resolved for something narrower.\n */\nexport function appendShippingAddressCollectionParams(\n params: URLSearchParams,\n countries: readonly string[] = CHECKOUT_SHIPPING_COUNTRIES,\n): void {\n countries.forEach((code, index) => {\n params.set(`shipping_address_collection[allowed_countries][${index}]`, code)\n })\n}\n\n/**\n * Emit resolved rates as `shipping_options` on a Checkout Session's form body.\n *\n * THIS IS THE ONLY THING THAT MAKES STRIPE CHARGE SHIPPING. Without these keys\n * Stripe presents no shipping choice and `total_details.amount_shipping` is 0\n * however many zones and rates the merchant saved (AGL-1707).\n *\n * An empty list emits NOTHING — not an empty array — so a merchant who\n * configured no shipping keeps a session byte-identical to the one built\n * before shipping was wired at all. That guarantee lives here rather than in\n * each caller, so neither checkout path can lose it independently (AGL-1720).\n */\nexport function appendCheckoutShippingParams(\n params: URLSearchParams,\n options: readonly ResolvedShippingRate[],\n): void {\n options.forEach((option, index) => {\n const field = `shipping_options[${index}][shipping_rate_data]`\n params.set(`${field}[type]`, 'fixed_amount')\n params.set(`${field}[display_name]`, option.name.slice(0, 100))\n params.set(`${field}[fixed_amount][amount]`, String(option.amountCents))\n params.set(`${field}[fixed_amount][currency]`, 'usd')\n })\n}\n"],"names":["LOCAL_PICKUP_RATE_ID","zoneCode","code","String","trim","toUpperCase","isWildcard","zoneMatches","zone","country","countries","some","tierAmount","tiers","value","sorted","sort","a","b","upTo","tier","amountCents","resolveShippingRates","settings","destinationCountry","cart","zones","specific","filter","matched","length","zoneIds","Set","map","id","resolved","rate","rates","has","zoneId","kind","Math","max","round","freeOverCents","subtotalCents","totalGrams","push","rateId","name","localPickup","shippableOnly","options","option","MAX_CHECKOUT_SHIPPING_OPTIONS","CHECKOUT_SHIPPING_COUNTRIES","resolveCheckoutShippingOptions","destinationCountries","byRateId","Map","Number","existing","get","set","values","localeCompare","slice","CHECKOUT_SHIPPING_COUNTRY_NAMES","US","CA","GB","AU","DE","FR","normalizeCheckoutShippingCountry","undefined","includes","merchantPricesShipping","CART_UNPRICEABLE_SHIPPING_MESSAGE","optionsKey","join","planCheckoutShipping","destination","shipsElsewhere","refusal","offers","size","union","COVERAGE_PROBE_CART","summarizeShippingCoverage","served","unserved","add","shipsNowhere","asksDestination","offersLocalPickup","Boolean","hasRestOfWorldZone","appendShippingAddressCollectionParams","params","forEach","index","appendCheckoutShippingParams","field"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED;;;;;;;;;;;;;;;;;;;;;;;;;;CA0BC;AA4CD;;;;;;;;;CASC,GACD,OAAO,MAAMA,uBAAuB,SAAQ;AAE5C;;;;;;;;;CASC,GACD,MAAMC,WAAW,CAACC,OAA0BC,OAAOD,eAAAA,OAAQ,IAAIE,IAAI,GAAGC,WAAW;AAEjF,yDAAyD,GACzD,MAAMC,aAAa,CAACJ,OAA2BD,SAASC,UAAU;AAElE,SAASK,YAAYC,IAAkB,EAAEC,OAAe;QAC9CD;IAAR,OAAO,EAACA,kBAAAA,KAAKE,SAAS,YAAdF,kBAAkB,EAAE,EAAEG,IAAI,CAChC,CAACT,OAASI,WAAWJ,SAASD,SAASC,UAAUO;AAErD;AAEA,SAASG,WACPC,KAAiC,EACjCC,KAAa;IAEb,MAAMC,SAAS;WAAKF,gBAAAA,QAAS,EAAE;KAAE,CAACG,IAAI,CAAC,CAACC,GAAGC,IAAMD,EAAEE,IAAI,GAAGD,EAAEC,IAAI;IAChE,KAAK,MAAMC,QAAQL,OAAQ;QACzB,IAAID,SAASM,KAAKD,IAAI,EAAE,OAAOC,KAAKC,WAAW;IACjD;IACA,sEAAsE;IACtE,qCAAqC;IACrC,OAAO;AACT;AAEA;;;;;;;;;;;CAWC,GACD,OAAO,SAASC,qBACdC,QAAsC,EACtCC,kBAAsC,EACtCC,IAAoD;QAItCF,iBAgBKA;IAlBnB,IAAI,CAACA,YAAY,CAACC,oBAAoB,OAAO,EAAE;IAC/C,MAAMf,UAAUe,mBAAmBnB,WAAW;IAC9C,MAAMqB,SAAQH,kBAAAA,SAASG,KAAK,YAAdH,kBAAkB,EAAE;IAClC,2EAA2E;IAC3E,0EAA0E;IAC1E,wEAAwE;IACxE,yEAAyE;IACzE,0EAA0E;IAC1E,0EAA0E;IAC1E,qCAAqC;IACrC,MAAMI,WAAWD,MAAME,MAAM,CAAC,CAACpB;YAC5BA;eAAD,EAACA,kBAAAA,KAAKE,SAAS,YAAdF,kBAAkB,EAAE,EAAEG,IAAI,CAAC,CAACT,OAASD,SAASC,UAAUO;;IAE3D,MAAMoB,UAAUF,SAASG,MAAM,GAC3BH,WACAD,MAAME,MAAM,CAAC,CAACpB,OAASD,YAAYC,MAAMC;IAC7C,MAAMsB,UAAU,IAAIC,IAAIH,QAAQI,GAAG,CAAC,CAACzB,OAASA,KAAK0B,EAAE;IACrD,MAAMC,WAAmC,EAAE;IAC3C,KAAK,MAAMC,SAAQb,kBAAAA,SAASc,KAAK,YAAdd,kBAAkB,EAAE,CAAE;QACvC,IAAI,CAACQ,QAAQO,GAAG,CAACF,KAAKG,MAAM,GAAG;QAC/B,IAAIlB,cAA6B;QACjC,OAAQe,KAAKI,IAAI;YACf,KAAK;oBACkCJ;gBAArCf,cAAcoB,KAAKC,GAAG,CAAC,GAAGD,KAAKE,KAAK,EAACP,oBAAAA,KAAKf,WAAW,YAAhBe,oBAAoB;gBACzD;YACF,KAAK;oBAK0BA;gBAJ7Bf,cACEe,KAAKQ,aAAa,IAAI,QACtBnB,KAAKoB,aAAa,IAAIT,KAAKQ,aAAa,GACpC,IACAH,KAAKC,GAAG,CAAC,GAAGD,KAAKE,KAAK,EAACP,qBAAAA,KAAKf,WAAW,YAAhBe,qBAAoB;gBACjD;YACF,KAAK;gBACHf,cAAcT,WAAWwB,KAAKvB,KAAK,EAAEY,KAAKoB,aAAa;gBACvD;YACF,KAAK;oBACkCpB;gBAArCJ,cAAcT,WAAWwB,KAAKvB,KAAK,GAAEY,mBAAAA,KAAKqB,UAAU,YAAfrB,mBAAmB;gBACxD;QACJ;QACA,IAAIJ,eAAe,MAAM;QACzBc,SAASY,IAAI,CAAC;YAAEC,QAAQZ,KAAKF,EAAE;YAAEe,MAAMb,KAAKa,IAAI;YAAE5B;QAAY;IAChE;IACA,IAAIE,SAAS2B,WAAW,EAAE;QACxBf,SAASY,IAAI,CAAC;YACZC,QAAQhD;YACRiD,MAAM;YACN5B,aAAa;QACf;IACF;IACA,OAAOc,SAASnB,IAAI,CAAC,CAACC,GAAGC,IAAMD,EAAEI,WAAW,GAAGH,EAAEG,WAAW;AAC9D;AAEA;;;;;;CAMC,GACD,SAAS8B,cACPC,OAAwC;IAExC,OAAOA,QAAQxB,MAAM,CAAC,CAACyB,SAAWA,OAAOL,MAAM,KAAKhD;AACtD;AAEA,yEAAyE,GACzE,OAAO,MAAMsD,gCAAgC,EAAC;AAE9C;;;;;;;;;CASC,GACD,OAAO,MAAMC,8BAA8B;IACzC;IACA;IACA;IACA;IACA;IACA;CACD,CAAS;AAEV;;;;;;;;;;;;;;;;;;CAkBC,GACD,OAAO,SAASC,+BACdjC,QAAsC,EACtCkC,oBAAuC,EACvChC,IAAoD;IAEpD,IAAI,CAACF,UAAU,OAAO,EAAE;IACxB,MAAMmC,WAAW,IAAIC;IACrB,KAAK,MAAMlD,WAAWgD,qBAAsB;QAC1C,KAAK,MAAMrB,QAAQd,qBAAqBC,UAAUd,SAASgB,MAAO;YAChE,oEAAoE;YACpE,sEAAsE;YACtE,IAAI,CAACW,KAAKY,MAAM,IAAI,CAACZ,KAAKa,IAAI,EAAE;YAChC,MAAM5B,cAAcoB,KAAKC,GAAG,CAAC,GAAGD,KAAKE,KAAK,CAACiB,OAAOxB,KAAKf,WAAW,KAAK;YACvE,MAAMwC,WAAWH,SAASI,GAAG,CAAC1B,KAAKY,MAAM;YACzC,iEAAiE;YACjE,uEAAuE;YACvE,sEAAsE;YACtE,IAAI,CAACa,YAAYxC,cAAcwC,SAASxC,WAAW,EAAE;gBACnDqC,SAASK,GAAG,CAAC3B,KAAKY,MAAM,EAAE,aAAKZ;oBAAMf;;YACvC;QACF;IACF;IACA,OACE;WAAIqC,SAASM,MAAM;KAAG,AACpB,sEAAsE;KACrEhD,IAAI,CACH,CAACC,GAAGC,IACFD,EAAEI,WAAW,GAAGH,EAAEG,WAAW,IAAIJ,EAAE+B,MAAM,CAACiB,aAAa,CAAC/C,EAAE8B,MAAM,EAEpE,uEAAuE;IACvE,qEAAqE;KACpEkB,KAAK,CAAC,GAAGZ;AAEhB;AAEA,sEAAsE,GACtE,OAAO,MAAMa,kCACX;IACEC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;AACN,EAAC;AAEH;;;;;CAKC,GACD,OAAO,SAASC,iCACd5D,KAAc;IAEd,0EAA0E;IAC1E,kEAAkE;IAClE,6DAA6D;IAC7D,IAAI,OAAOA,UAAU,UAAU,OAAO6D;IACtC,MAAMzE,OAAOY,MAAMV,IAAI,GAAGC,WAAW;IACrC,OAAO,AAACkD,4BAAkDqB,QAAQ,CAAC1E,QAC/DA,OACAyE;AACN;AAsBA;;;;;;;;;;;;;;;;;CAiBC,GACD,OAAO,SAASE,uBACdtD,QAAsC;QAIbA,iBAIjBA;IANR,IAAI,CAACA,UAAU,OAAO;IACtB,IAAIA,SAAS2B,WAAW,EAAE,OAAO;IACjC,MAAMnB,UAAU,IAAIC,IAAI,EAACT,kBAAAA,SAASG,KAAK,YAAdH,kBAAkB,EAAE,EAAEU,GAAG,CAAC,CAACzB,OAASA,KAAK0B,EAAE;IACpE,6EAA6E;IAC7E,4EAA4E;IAC5E,wCAAwC;IACxC,OAAO,EAACX,kBAAAA,SAASc,KAAK,YAAdd,kBAAkB,EAAE,EAAEZ,IAAI,CAAC,CAACyB,OAASL,QAAQO,GAAG,CAACF,KAAKG,MAAM;AACtE;AAEA;;;;;;;;;CASC,GACD,OAAO,MAAMuC,oCACX,8EACA,8EACA,SAAQ;AAEV,yEAAyE,GACzE,SAASC,WAAW3B,OAAwC;IAC1D,OAAOA,QACJnB,GAAG,CAAC,CAACoB,SAAW,GAAGA,OAAOL,MAAM,CAAC,CAAC,EAAEK,OAAOhC,WAAW,EAAE,EACxD2D,IAAI,CAAC;AACV;AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgCC,GACD,OAAO,SAASC,qBACd1D,QAAsC,EACtCE,IAAoD,EACpDD,kBAA4B;IAE5B,MAAMd,YAAY6C;IAClB,MAAM2B,cAAcR,iCAAiClD;IACrD,IAAI0D,aAAa;QACf,MAAM9B,UAAUI,+BACdjC,UACA;YAAC2D;SAAY,EACbzD;QAEF,sEAAsE;QACtE,wEAAwE;QACxE,0EAA0E;QAC1E,uEAAuE;QACvE,yEAAyE;QACzE,oEAAoE;QACpE,0EAA0E;QAC1E,uEAAuE;QACvE,yCAAyC;QACzC,EAAE;QACF,yEAAyE;QACzE,sEAAsE;QACtE,qEAAqE;QACrE,kEAAkE;QAClE,yEAAyE;QACzE,sEAAsE;QACtE,yEAAyE;QACzE,sDAAsD;QACtD,MAAM0D,iBAAiBzE,UAAUC,IAAI,CACnC,CAACF,UACC0C,cAAcK,+BAA+BjC,UAAU;gBAACd;aAAQ,EAAEgB,OAC/DK,MAAM,GAAG;QAEhB,IAAIqB,cAAcC,SAAStB,MAAM,KAAK,KAAKqD,gBAAgB;YACzD,OAAO;gBAAEzE;gBAAW0C,SAAS,EAAE;gBAAEgC,SAAS;YAAuB;QACnE;QACA,IAAIhC,QAAQtB,MAAM,GAAG,GAAG,OAAO;YAAEpB,WAAW;gBAACwE;aAAY;YAAE9B;QAAQ;QACnE,2EAA2E;QAC3E,wEAAwE;QACxE,wEAAwE;QACxE,0EAA0E;QAC1E,sEAAsE;QACtE,OAAOyB,uBAAuBtD,YAC1B;YAAEb;YAAW0C,SAAS,EAAE;YAAEgC,SAAS;QAAmB,IACtD;YAAE1E;YAAW0C,SAAS,EAAE;QAAC;IAC/B;IACA,MAAMiC,SAAS,IAAIrD,IACjBtB,UAAUuB,GAAG,CAAC,CAACxB,UACbsE,WAAWvB,+BAA+BjC,UAAU;YAACd;SAAQ,EAAEgB;IAGnE,0EAA0E;IAC1E,uEAAuE;IACvE,uEAAuE;IACvE,IAAI4D,OAAOC,IAAI,GAAG,GAAG;QACnB,OAAO;YAAE5E;YAAW0C,SAAS,EAAE;YAAEgC,SAAS;QAAuB;IACnE;IACA,MAAMG,QAAQ/B,+BAA+BjC,UAAUb,WAAWe;IAClE,wEAAwE;IACxE,4EAA4E;IAC5E,0EAA0E;IAC1E,6DAA6D;IAC7D,IAAI8D,MAAMzD,MAAM,KAAK,KAAK+C,uBAAuBtD,WAAW;QAC1D,OAAO;YAAEb;YAAW0C,SAAS,EAAE;YAAEgC,SAAS;QAAmB;IAC/D;IACA,OAAO;QAAE1E;QAAW0C,SAASmC;IAAM;AACrC;AAEA;;;;;;;;;;;;;CAaC,GACD,MAAMC,sBAAsB;IAAE3C,eAAe;IAAGC,YAAY;AAAE;AA0C9D;;;;;;;;;;;;;;;CAeC,GACD,OAAO,SAAS2C,0BACdlE,QAAsC;;IAEtC,MAAMmE,SAAmB,EAAE;IAC3B,MAAMC,WAAqB,EAAE;IAC7B,MAAMN,SAAS,IAAIrD;IACnB,KAAK,MAAMvB,WAAW8C,4BAA6B;QACjD,MAAMH,UAAUI,+BACdjC,UACA;YAACd;SAAQ,EACT+E;QAEF,0EAA0E;QAC1E,uEAAuE;QACvE,2EAA2E;QAC3EH,OAAOO,GAAG,CAACb,WAAW3B;QAGpBD,CAAAA,cAAcC,SAAStB,MAAM,GAAG,IAAI4D,SAASC,QAAO,EAAG5C,IAAI,CAACtC;IAChE;IACA,OAAO;QACLiF;QACAC,UAAUD,OAAO5D,MAAM,GAAG,IAAI6D,WAAW,EAAE;QAC3CE,cAAcH,OAAO5D,MAAM,KAAK;QAChCgE,iBAAiBT,OAAOC,IAAI,GAAG;QAC/BS,mBAAmBC,QAAQzE,4BAAAA,SAAU2B,WAAW;QAChD+C,oBAAoB,SAAC1E,4BAAAA,SAAUG,KAAK,mBAAI,EAAE,EAAEf,IAAI,CAAC,CAACH,OAChDA,KAAKE,SAAS,CAACC,IAAI,CAAC,CAACT,OAASA,SAAS;IAE3C;AACF;AAEA;;;;;;;CAOC,GACD,OAAO,SAASgG,sCACdC,MAAuB,EACvBzF,YAA+B6C,2BAA2B;IAE1D7C,UAAU0F,OAAO,CAAC,CAAClG,MAAMmG;QACvBF,OAAOpC,GAAG,CAAC,CAAC,+CAA+C,EAAEsC,MAAM,CAAC,CAAC,EAAEnG;IACzE;AACF;AAEA;;;;;;;;;;;CAWC,GACD,OAAO,SAASoG,6BACdH,MAAuB,EACvB/C,OAAwC;IAExCA,QAAQgD,OAAO,CAAC,CAAC/C,QAAQgD;QACvB,MAAME,QAAQ,CAAC,iBAAiB,EAAEF,MAAM,qBAAqB,CAAC;QAC9DF,OAAOpC,GAAG,CAAC,GAAGwC,MAAM,MAAM,CAAC,EAAE;QAC7BJ,OAAOpC,GAAG,CAAC,GAAGwC,MAAM,cAAc,CAAC,EAAElD,OAAOJ,IAAI,CAACiB,KAAK,CAAC,GAAG;QAC1DiC,OAAOpC,GAAG,CAAC,GAAGwC,MAAM,sBAAsB,CAAC,EAAEpG,OAAOkD,OAAOhC,WAAW;QACtE8E,OAAOpC,GAAG,CAAC,GAAGwC,MAAM,wBAAwB,CAAC,EAAE;IACjD;AACF"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/model/commerce-shipping.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 * Shipping v1 (AGL-288): zones own countries; rates belong to a zone and\n * price as flat, free-over-threshold, or subtotal/weight tiers. Settings\n * live on `hosts/{hostId}/settings/store` under `shipping`. Pure.\n *\n * `resolveShippingRates` is the per-destination resolver;\n * `resolveCheckoutShippingOptions` is the Stripe Checkout adapter,\n * `planCheckoutShipping` decides which destinations one session may serve,\n * `appendCheckoutShippingParams` emits it onto a session, and\n * `summarizeShippingCoverage` answers the same question for the console, so a\n * merchant can see which destinations their zones refuse. AGL-288 shipped this\n * model with no production call site at all — the doc comment here claimed the\n * cart estimator, checkout and POS pickup used it, and none of them did, so\n * nothing ever read the settings a merchant saved and no shipping was ever\n * charged. Cart checkout calls it (AGL-1707), buy-now calls it for physical\n * one-time products (AGL-1720), and a draft order's payment link calls it for\n * physical products (AGL-1792).\n *\n * POS DOES NOT, AND SHOULD NOT (AGL-1792). A register has no destination\n * anywhere in it — no address field, no delivery choice — and all three\n * tenders settle at the counter: cash, a card QR the customer scans while\n * standing there, and a room folio collected at check-out. There is nothing to\n * price a parcel against, so the only rate POS could resolve is the free\n * `Local pickup` one this module appends, which is 0¢ and would add a step to\n * a register flow in exchange for nothing. A POS sale that must be shipped is\n * a draft order.\n */\n\nexport interface ShippingZone {\n id: string\n name: string\n /** ISO-3166 alpha-2 codes; '*' = rest of world. */\n countries: string[]\n}\n\nexport interface ShippingTier {\n /** Tier applies while subtotal (cents) or weight (grams) ≤ this. */\n upTo: number\n amountCents: number\n}\n\n/**\n * A live carrier rate (AGL-3612): the zone's price is what a carrier quotes\n * for the parcel to the shopper's address, asked through core's\n * `core.shipping-rate-quoter` seam of whichever plugin quotes carriers. With\n * no quoter, a quoter that is not available for the site, no postal code to\n * quote against, or a quote slower than {@link CARRIER_QUOTE_TIMEOUT_MS}, it\n * offers nothing and its fallback rate is offered instead.\n */\nexport interface CarrierRateSettings {\n /** Service keys (`carrier:service`) to offer; empty offers every quoted service. */\n services?: string[]\n /** Added to each quote, in percent. */\n markupPct?: number\n /** Added to each quote after the markup, in cents. */\n handlingCents?: number\n /**\n * A rate of this zone offered when no live quote is: it is HIDDEN while\n * quotes are offered, so the shopper picks between carriers, not between a\n * carrier and the merchant's guess.\n */\n fallbackRateId?: string\n}\n\n/** How long checkout waits for live carrier quotes before it falls back. */\nexport const CARRIER_QUOTE_TIMEOUT_MS = 3_000\n\nexport interface ShippingRate {\n id: string\n zoneId: string\n name: string\n kind: 'flat' | 'free_over' | 'price_tiers' | 'weight_tiers' | 'carrier'\n /** flat + free_over base amount. */\n amountCents?: number\n /** free_over: order subtotal (cents) at/above which shipping is free. */\n freeOverCents?: number\n /** price_tiers (upTo = subtotal cents) / weight_tiers (upTo = grams). */\n tiers?: ShippingTier[]\n /** carrier: the live rate's options (AGL-3612). */\n carrier?: CarrierRateSettings\n}\n\nexport interface ShippingSettings {\n zones?: ShippingZone[]\n rates?: ShippingRate[]\n /**\n * Offer free local pickup as a delivery choice. Does NOT widen where the\n * store ships: a destination no rate reaches is still refused (AGL-2325).\n */\n localPickup?: boolean\n}\n\nexport interface ResolvedShippingRate {\n rateId: string\n name: string\n amountCents: number\n}\n\n/**\n * The rate id appended for local pickup (AGL-2325).\n *\n * Exported because pickup is the one entry in a resolved list that is NOT a\n * shipping promise: it is appended AFTER and INDEPENDENT of zone matching, so\n * it resolves for every destination on earth. Anything asking \"does this store\n * ship there\" — the checkout refusal, the console coverage card — has to be\n * able to tell it apart from a rate, or it reads a merchant who offers\n * collection as a merchant who delivers worldwide.\n */\nexport const LOCAL_PICKUP_RATE_ID = 'pickup'\n\n/**\n * One stored country code, normalised (AGL-2298).\n *\n * TRIMMED as well as upper-cased. The console trims on save, so this only\n * matters for a document written any other way — an import, the REST API, a\n * hand edit — but the failure it produces is silent and expensive: `' US'`\n * upper-cases to `' US'`, matches nothing, and the store refuses every US\n * shopper with \"This store does not ship to the United States\" while its\n * settings card shows `US` right there.\n */\nconst zoneCode = (code: unknown): string => String(code ?? '').trim().toUpperCase()\n\n/** Whether a stored code is the rest-of-world wildcard. */\nconst isWildcard = (code: unknown): boolean => zoneCode(code) === '*'\n\nfunction zoneMatches(zone: ShippingZone, country: string): boolean {\n return (zone.countries ?? []).some(\n (code) => isWildcard(code) || zoneCode(code) === country,\n )\n}\n\nfunction tierAmount(\n tiers: ShippingTier[] | undefined,\n value: number,\n): number | null {\n const sorted = [...(tiers ?? [])].sort((a, b) => a.upTo - b.upTo)\n for (const tier of sorted) {\n if (value <= tier.upTo) return tier.amountCents\n }\n // Beyond the last tier the rate does not apply (merchant should add a\n // catch-all tier with a large upTo).\n return null\n}\n\n/**\n * The zones a destination falls in (AGL-1707, factored out for AGL-3612).\n * Specific country zones beat '*' zones: when any zone names the country\n * outright, rest-of-world zones are ignored.\n */\nexport function matchedZoneIds(\n settings: ShippingSettings | undefined,\n destinationCountry: string,\n): Set<string> {\n const country = destinationCountry.toUpperCase()\n const zones = settings?.zones ?? []\n // A zone is SPECIFIC when it names this country outright — not merely when\n // it is \"not wildcard-only\" (AGL-2298). The old test was `!every(code ===\n // '*')`, so a zone spelled `['*','US']` counted as specific for all six\n // destinations (it matches every country through the `*`) and suppressed\n // every genuine rest-of-world zone globally. Asking the narrower question\n // makes such a zone specific for `US` and wildcard for the rest, which is\n // what its two entries actually say.\n const specific = zones.filter((zone) =>\n (zone.countries ?? []).some((code) => zoneCode(code) === country),\n )\n const matched = specific.length\n ? specific\n : zones.filter((zone) => zoneMatches(zone, country))\n return new Set(matched.map((zone) => zone.id))\n}\n\n/** The live carrier rates a destination's zones carry (AGL-3612). */\nexport function carrierRatesFor(\n settings: ShippingSettings | undefined,\n destinationCountry: string,\n): ShippingRate[] {\n if (!settings) return []\n const zoneIds = matchedZoneIds(settings, destinationCountry)\n return (settings.rates ?? []).filter(\n (rate) => rate.kind === 'carrier' && zoneIds.has(rate.zoneId),\n )\n}\n\n/** Whether any zone carries a live carrier rate (AGL-3612). */\nexport function hasCarrierRates(settings: ShippingSettings | undefined): boolean {\n return (settings?.rates ?? []).some((rate) => rate.kind === 'carrier')\n}\n\n/** The rate id a quoted service is offered under: stable per rate and service. */\nexport function carrierOptionRateId(rateId: string, serviceKey: string): string {\n return `carrier:${rateId}:${serviceKey}`\n}\n\n/**\n * What a shopper pays for a carrier's quote under one carrier rate: the\n * quote, plus the markup, plus the handling fee, in whole cents.\n */\nexport function carrierOptionAmountCents(\n quotedCents: number,\n settings: CarrierRateSettings | undefined,\n): number {\n const quote = Math.max(0, Math.round(Number(quotedCents) || 0))\n const pct = Number(settings?.markupPct)\n const handling = Number(settings?.handlingCents)\n return (\n quote +\n (Number.isFinite(pct) && pct > 0 ? Math.round((quote * Math.min(pct, 500)) / 100) : 0) +\n (Number.isFinite(handling) && handling > 0 ? Math.round(handling) : 0)\n )\n}\n\n/**\n * Rates available for a destination + cart, cheapest first. Specific\n * country zones beat '*' zones: when any specific zone matches, '*'\n * zones are ignored (rest-of-world semantics).\n *\n * Local pickup is appended AFTER and INDEPENDENT of zone matching, so this\n * list is never empty for a merchant who offers it, at any destination. That\n * is correct for a resolver — a shopper anywhere may choose to come and\n * collect — but it means A NON-EMPTY RESULT IS NOT A PROMISE TO SHIP THERE.\n * Callers deciding whether a destination is served must filter pickup out\n * first; `planCheckoutShipping` and `summarizeShippingCoverage` do (AGL-2325).\n */\nexport function resolveShippingRates(\n settings: ShippingSettings | undefined,\n destinationCountry: string | undefined,\n cart: { subtotalCents: number; totalGrams?: number },\n): ResolvedShippingRate[] {\n if (!settings || !destinationCountry) return []\n const zoneIds = matchedZoneIds(settings, destinationCountry)\n // A zone is SPECIFIC when it names this country outright — not merely when\n // it is \"not wildcard-only\" (AGL-2298). The old test was `!every(code ===\n // '*')`, so a zone spelled `['*','US']` counted as specific for all six\n // destinations (it matches every country through the `*`) and suppressed\n // every genuine rest-of-world zone globally. Asking the narrower question\n // makes such a zone specific for `US` and wildcard for the rest, which is\n // what its two entries actually say.\n const resolved: ResolvedShippingRate[] = []\n for (const rate of settings.rates ?? []) {\n if (!zoneIds.has(rate.zoneId)) continue\n let amountCents: number | null = null\n switch (rate.kind) {\n case 'flat':\n amountCents = Math.max(0, Math.round(rate.amountCents ?? 0))\n break\n case 'free_over':\n amountCents =\n rate.freeOverCents != null &&\n cart.subtotalCents >= rate.freeOverCents\n ? 0\n : Math.max(0, Math.round(rate.amountCents ?? 0))\n break\n case 'price_tiers':\n amountCents = tierAmount(rate.tiers, cart.subtotalCents)\n break\n case 'weight_tiers':\n amountCents = tierAmount(rate.tiers, cart.totalGrams ?? 0)\n break\n // A carrier's price exists only once a carrier quoted it for an\n // address (AGL-3612), so a table read offers nothing for it and its\n // fallback rate is what resolves here. `carrier-shipping.ts` adds the\n // quotes where one can be had.\n case 'carrier':\n break\n }\n if (amountCents == null) continue\n resolved.push({ rateId: rate.id, name: rate.name, amountCents })\n }\n if (settings.localPickup) {\n resolved.push({\n rateId: LOCAL_PICKUP_RATE_ID,\n name: 'Local pickup',\n amountCents: 0,\n })\n }\n return resolved.sort((a, b) => a.amountCents - b.amountCents)\n}\n\n/**\n * The resolved options that actually carry a parcel (AGL-2325).\n *\n * Safe to apply AFTER `MAX_CHECKOUT_SHIPPING_OPTIONS` truncation: pickup is\n * 0¢ and sorts first, so it occupies at most one of the five slots and a\n * merchant with any shippable rate at all keeps at least one of them.\n */\nfunction shippableOnly(\n options: readonly ResolvedShippingRate[],\n): ResolvedShippingRate[] {\n return options.filter((option) => option.rateId !== LOCAL_PICKUP_RATE_ID)\n}\n\n/** Stripe Checkout accepts at most 5 `shipping_options` on one session. */\nexport const MAX_CHECKOUT_SHIPPING_OPTIONS = 5\n\n/**\n * Destinations a storefront Checkout Session collects an address for, and\n * therefore the destinations shipping rates are resolved against (AGL-1707).\n *\n * NOT narrowed to the zones the merchant configured: narrowing would block\n * checkouts that complete today, turning a money fix into lost sales. The\n * wider list only means a shopper outside the merchant's zones is offered some\n * rate rather than none. Shared by cart checkout and buy-now (AGL-1720) so the\n * two paths cannot drift into offering different destinations.\n */\nexport const CHECKOUT_SHIPPING_COUNTRIES = [\n 'US',\n 'CA',\n 'GB',\n 'AU',\n 'DE',\n 'FR',\n] as const\n\n/**\n * The rates to declare as `shipping_options` on a Stripe Checkout Session\n * (AGL-1707), unioned over `destinationCountries`. Stripe charges shipping\n * ONLY when the session declares them.\n *\n * A UNION IS ONLY HONEST OVER DESTINATIONS THE SESSION IS ALSO RESTRICTED TO.\n * Called with every collectable country while the session accepted an address\n * in any of them — which is what AGL-1707 shipped — it offers a shopper in one\n * zone a rate belonging to another, and Stripe applies whichever the shopper\n * picks (a `shipping_rate_data` carries no country of its own, so nothing\n * downstream re-checks it against the address). That is AGL-1721. Callers now\n * reach this through `planCheckoutShipping`, which pairs the country list it\n * unions over with the country list the session allows, so the two cannot\n * disagree. Do not call it directly from a handler.\n *\n * Returns `[]` for a merchant who has configured nothing, and the caller must\n * then declare no `shipping_options` at all rather than an empty array — a\n * merchant who never set shipping up keeps a session identical to today's.\n */\nexport function resolveCheckoutShippingOptions(\n settings: ShippingSettings | undefined,\n destinationCountries: readonly string[],\n cart: { subtotalCents: number; totalGrams?: number },\n): ResolvedShippingRate[] {\n if (!settings) return []\n const byRateId = new Map<string, ResolvedShippingRate>()\n for (const country of destinationCountries) {\n for (const rate of resolveShippingRates(settings, country, cart)) {\n // Stripe requires a display name, and a rate id is the dedupe key —\n // the console lets a merchant add a blank row, so neither is assured.\n if (!rate.rateId || !rate.name) continue\n const amountCents = Math.max(0, Math.round(Number(rate.amountCents) || 0))\n const existing = byRateId.get(rate.rateId)\n // One rate resolving differently for two countries should not be\n // possible (a zone selects a rate, it does not reprice one), but if it\n // ever is, keep the dearer. Under-charging is the defect being fixed.\n if (!existing || amountCents > existing.amountCents) {\n byRateId.set(rate.rateId, { ...rate, amountCents })\n }\n }\n }\n return (\n [...byRateId.values()]\n // Cheapest first, id as a tie-break so the emitted params are stable.\n .sort(\n (a, b) =>\n a.amountCents - b.amountCents || a.rateId.localeCompare(b.rateId),\n )\n // Past Stripe's cap the dearest are dropped, which is the harmless end\n // to lose: a shopper offered the cheap options would have taken one.\n .slice(0, MAX_CHECKOUT_SHIPPING_OPTIONS)\n )\n}\n\n/** Shopper-facing names for the collectable destinations (AGL-1721). */\nexport const CHECKOUT_SHIPPING_COUNTRY_NAMES: Readonly<Record<string, string>> =\n {\n US: 'United States',\n CA: 'Canada',\n GB: 'United Kingdom',\n AU: 'Australia',\n DE: 'Germany',\n FR: 'France',\n }\n\n/**\n * A destination a shopper declared, or `undefined` for anything this session\n * could not collect an address for anyway. Never throws: an unknown code is\n * an undeclared destination, not an error, so a stale storefront that posts\n * one is answered by the plan below rather than by a 400 nobody can act on.\n */\nexport function normalizeCheckoutShippingCountry(\n value: unknown,\n): string | undefined {\n // `typeof` rather than `String(value)`: a JSON body is caller-shaped, and\n // `String(['US'])` is 'US'. Coercing would let an array declare a\n // destination, which is not a spelling any storefront sends.\n if (typeof value !== 'string') return undefined\n const code = value.trim().toUpperCase()\n return (CHECKOUT_SHIPPING_COUNTRIES as readonly string[]).includes(code)\n ? code\n : undefined\n}\n\nexport interface CheckoutShippingPlan {\n /** `shipping_address_collection[allowed_countries]` for this session. */\n countries: readonly string[]\n /** `shipping_options` for this session — valid for EVERY `countries` entry. */\n options: ResolvedShippingRate[]\n /**\n * Set when no session may be created at all:\n *\n * - `destination-required` — the merchant's rates differ by destination, so\n * there is no set of options honest for every address Stripe would accept.\n * Ask the shopper where they are shipping and plan again.\n * - `destination-unserved` — the merchant ships somewhere, but not there.\n * - `cart-unpriceable` — the merchant priced shipping, but no rate of theirs\n * resolves for THIS cart at ANY destination (AGL-2232). A tier table that\n * stops at 2 kg says nothing about a 3 kg parcel, and the honest answer is\n * to refuse rather than to ship it for nothing.\n */\n refusal?: 'destination-required' | 'destination-unserved' | 'cart-unpriceable'\n}\n\n/**\n * Whether this merchant prices shipping AT ALL (AGL-2232).\n *\n * A pure question about the SETTINGS, deliberately taking no cart, and that is\n * the whole point of it existing. `planCheckoutShipping` used to ask \"does any\n * rate resolve for any destination?\" and pass the live cart into the probe, so\n * a cart that exhausted every tier made the probe empty too — and an empty\n * probe reads as \"this merchant never set shipping up\", the one case that must\n * complete with no `shipping_options` and nothing charged. A 3 kg order at a\n * store whose weight tiers stop at 2 kg therefore shipped free, silently, on\n * every one of the three doors.\n *\n * Answered from the stored zones and rates instead: a rate bound to a zone\n * that exists, or local pickup, means the merchant made a shipping decision\n * and a cart their table cannot price is a REFUSAL. A merchant with no zones,\n * no rates and no pickup has made no such decision, and their session stays\n * byte-identical to the one AGL-1707 shipped.\n */\nexport function merchantPricesShipping(\n settings: ShippingSettings | undefined,\n): boolean {\n if (!settings) return false\n if (settings.localPickup) return true\n const zoneIds = new Set((settings.zones ?? []).map((zone) => zone.id))\n // A rate whose `zoneId` names no zone is unreachable by every resolver here,\n // so it is not evidence of a decision — counting it would refuse carts at a\n // store with nothing but orphaned rows.\n return (settings.rates ?? []).some((rate) => zoneIds.has(rate.zoneId))\n}\n\n/**\n * What a shopper is told when `cart-unpriceable` refuses (AGL-2232).\n *\n * One string, shared by all three doors, because the three used to phrase the\n * two existing refusals identically and drifting here would make the same\n * store say different things depending on which button was pressed. It names\n * the cart rather than the destination on purpose — a `Ship to` select cannot\n * fix a parcel no tier covers, so the refusal deliberately does NOT set\n * `needsShippingCountry`.\n */\nexport const CART_UNPRICEABLE_SHIPPING_MESSAGE =\n 'This store cannot price shipping for this order — it falls outside every ' +\n 'shipping rate the store has set up. Try removing an item, or contact the ' +\n 'store.'\n\n/** Identity of a resolved list, for comparing two destinations' offers. */\nfunction optionsKey(options: readonly ResolvedShippingRate[]): string {\n return options\n .map((option) => `${option.rateId}:${option.amountCents}`)\n .join('|')\n}\n\n/**\n * The whole shipping half of a Checkout Session: which destinations it may\n * accept an address for, and which rates it may charge (AGL-1721).\n *\n * THE INVARIANT IS THAT EVERY RETURNED OPTION IS VALID FOR EVERY RETURNED\n * COUNTRY. Stripe offers a session's `shipping_options` to whoever reaches the\n * page and charges whichever the shopper picks — it does not filter them by\n * the address entered, and there is no field on a `shipping_rate_data` that\n * would let it. So the ONLY place a zone can be enforced is here, by choosing\n * the two lists together: narrowing the allowed countries to the declared\n * destination is what makes the rates offered for it unpickable by anyone\n * else. Filtering the rate list alone would leave a session that still accepts\n * a French address against a US rate.\n *\n * Three shapes come out of it:\n *\n * 1. A merchant whose rates resolve the SAME for every collectable country —\n * one zone, a single '*' zone, or none at all — needs no destination and\n * keeps the session AGL-1707 shipped, byte for byte. The issue notes this\n * is the common configuration, and it is the one that must not regress.\n * 2. A declared destination resolves exactly, and the session is restricted\n * to it. A caller may not skip the restriction: the country arrives in a\n * request body a shopper can craft, so a session that took the country's\n * cheap rate while still accepting any address would hand that shopper\n * the defect on purpose.\n * 3. Otherwise the caller must refuse — see `refusal`. Refusing is not lost\n * revenue in either case: with rates that differ by zone there is no\n * correct amount to charge without knowing where the parcel is going, and\n * the union AGL-1707 shipped chose the wrong one silently.\n *\n * `cart.subtotalCents` is the PRE-discount items total, matching what the cart\n * displays, so a free-over threshold means what the shopper saw it mean.\n */\nexport function planCheckoutShipping(\n settings: ShippingSettings | undefined,\n cart: { subtotalCents: number; totalGrams?: number },\n destinationCountry?: unknown,\n): CheckoutShippingPlan {\n const countries = CHECKOUT_SHIPPING_COUNTRIES as readonly string[]\n const destination = normalizeCheckoutShippingCountry(destinationCountry)\n if (destination) {\n const options = resolveCheckoutShippingOptions(\n settings,\n [destination],\n cart,\n )\n // REFUSAL IS DECIDED ON SHIPPABLE RATES, PICKUP IS OFFERED AFTERWARDS\n // (AGL-2325). `resolveShippingRates` appends pickup independent of zone\n // matching, so a merchant who turned it on had a non-empty list for every\n // destination on earth and this branch returned before it could refuse\n // anyone: a shopper in France picked `Local pickup — Free` on a physical\n // order from a US-only store, Stripe collected their French address\n // because the session still allowed it, and the merchant was paid nothing\n // to carry the parcel there. Pickup is collection, not delivery, so it\n // cannot answer \"do you ship to France\".\n //\n // `shipsElsewhere` is the same question the old probe asked, narrowed to\n // shippable rates, and it keeps the same two-way fork: a merchant who\n // configured NO shipping is not refusing anyone, so that store keeps\n // completing exactly as it does today, while a merchant who ships\n // elsewhere but not here is refusing and saying so is the honest answer.\n // A merchant who ships NOWHERE is untouched by this: pickup-only is a\n // real configuration — they deliver to no one and refuse no one — and it\n // falls through to the offer below exactly as it did.\n const shipsElsewhere = countries.some(\n (country) =>\n shippableOnly(resolveCheckoutShippingOptions(settings, [country], cart))\n .length > 0,\n )\n if (shippableOnly(options).length === 0 && shipsElsewhere) {\n return { countries, options: [], refusal: 'destination-unserved' }\n }\n if (options.length > 0) return { countries: [destination], options }\n // NOTHING resolves anywhere for this cart, and the two reasons that can be\n // true of are opposite (AGL-2232). Asked of the SETTINGS rather than of\n // this cart, because the probe above cannot tell them apart: a merchant\n // who priced shipping and whose table simply does not reach this order is\n // refusing, and used to be handed the \"never set shipping up\" branch.\n return merchantPricesShipping(settings)\n ? { countries, options: [], refusal: 'cart-unpriceable' }\n : { countries, options: [] }\n }\n const offers = new Set(\n countries.map((country) =>\n optionsKey(resolveCheckoutShippingOptions(settings, [country], cart)),\n ),\n )\n // Compared as EMITTED — capped and sanitized — because that is the list a\n // shopper can pick from. Two destinations whose rates differ only past\n // Stripe's cap still offer the same five, and offering them is honest.\n if (offers.size > 1) {\n return { countries, options: [], refusal: 'destination-required' }\n }\n const union = resolveCheckoutShippingOptions(settings, countries, cart)\n // Every destination agrees, and what they agree on is NOTHING. The same\n // fork as the declared-destination branch above (AGL-2232): a store with no\n // shipping settings completes untouched, a store whose rates cannot reach\n // this cart refuses instead of carrying the parcel for free.\n if (union.length === 0 && merchantPricesShipping(settings)) {\n return { countries, options: [], refusal: 'cart-unpriceable' }\n }\n return { countries, options: union }\n}\n\n/**\n * A cart chosen so that every rate a merchant could have written resolves if\n * it can resolve at all (AGL-1791). The settings page has no cart, and two of\n * the four rate kinds price against one: `tierAmount` takes the first tier\n * whose `upTo` is at least the value, so a zero subtotal and zero weight clear\n * every tier a merchant would write, and a `free_over` rate resolves at any\n * subtotal.\n *\n * That makes a coverage answer a LOWER BOUND on the gaps. A destination\n * reported unserved is unserved for every cart; a destination reported served\n * may still be refused for a cart dearer or heavier than the merchant's last\n * tier. Erring in this direction is the deliberate half: the console must not\n * warn a merchant whose settings are fine.\n */\nconst COVERAGE_PROBE_CART = { subtotalCents: 0, totalGrams: 0 }\n\n/** What a merchant's saved zones and rates cost their shoppers (AGL-1791). */\nexport interface ShippingCoverage {\n /** Collectable destinations at least one rate reaches. */\n served: string[]\n /**\n * Collectable destinations CHECKOUT REFUSES — `destination-unserved`.\n *\n * EMPTY FOR A MERCHANT WHOSE RATES REACH NOWHERE, who is not misconfigured:\n * `planCheckoutShipping` refuses nobody when no rate resolves anywhere, so\n * that store charges no shipping and completes exactly as it always has.\n * The rule lives here rather than in the caller because reading `served`\n * against a bare partition is how a surface comes to warn every store that\n * does not ship.\n */\n unserved: string[]\n /** No rate reaches anywhere: no shipping is charged, and nobody refused. */\n shipsNowhere: boolean\n /**\n * Rates differ by destination, so the storefront asks the shopper where the\n * parcel is going before it can price one — a checkout step the merchant\n * added by saving these zones, and never chose.\n */\n asksDestination: boolean\n /** Some zone already claims rest of world, so `*` is not the missing piece. */\n hasRestOfWorldZone: boolean\n /**\n * The merchant offers local pickup, so checkout presents a free collection\n * choice at every destination it does not refuse (AGL-2325).\n *\n * Deliberately NOT folded into `served`: pickup resolves for every country\n * because it is appended independent of zone matching, so counting it as\n * coverage told a US-only merchant who had switched it on that checkout\n * \"can price shipping to every destination\", which is the sentence that\n * hid the defect. It is reported alongside instead, so the card can say\n * both true things: these destinations are refused, and pickup is offered\n * to the ones that are not.\n */\n offersLocalPickup: boolean\n}\n\n/**\n * Which of the collectable destinations a merchant's settings actually cover\n * (AGL-1791), for a console surface to show them.\n *\n * Measured against `CHECKOUT_SHIPPING_COUNTRIES` and nothing wider: those six\n * are the only destinations a storefront session collects an address for, so\n * they are the only ones a merchant can lose an order to. Counting coverage\n * against every country on earth would report a gap for destinations no\n * shopper can reach checkout from.\n *\n * Answered through `resolveCheckoutShippingOptions` — the same function the\n * session is built from — so a row the console allows but Stripe would reject\n * (blank id, blank name) counts as the nothing it resolves to, and a specific\n * zone with no rates still hides a `*` zone here exactly as it does at\n * checkout.\n */\nexport function summarizeShippingCoverage(\n settings: ShippingSettings | undefined,\n): ShippingCoverage {\n const served: string[] = []\n const unserved: string[] = []\n const offers = new Set<string>()\n for (const country of CHECKOUT_SHIPPING_COUNTRIES) {\n const options = resolveCheckoutShippingOptions(\n settings,\n [country],\n COVERAGE_PROBE_CART,\n )\n // `asksDestination` is asked of the FULL list — pickup is a real option a\n // shopper picks, so a merchant whose US destination offers a rate plus\n // pickup while France offers only pickup genuinely does make checkout ask.\n offers.add(optionsKey(options))\n // `served` is asked of the SHIPPABLE list, so that it answers the same\n // question `planCheckoutShipping` refuses on (AGL-2325).\n ;(shippableOnly(options).length > 0 ? served : unserved).push(country)\n }\n return {\n served,\n unserved: served.length > 0 ? unserved : [],\n shipsNowhere: served.length === 0,\n asksDestination: offers.size > 1,\n offersLocalPickup: Boolean(settings?.localPickup),\n hasRestOfWorldZone: (settings?.zones ?? []).some((zone) =>\n zone.countries.some((code) => code === '*'),\n ),\n }\n}\n\n/**\n * Emit `shipping_address_collection[allowed_countries][n]` onto a session's\n * form body. Stripe will not apply a `shipping_options` rate without an\n * address to ship to, so the two always travel together — and the countries\n * are the OTHER half of what makes a rate honest (AGL-1721), so pass the list\n * `planCheckoutShipping` returned rather than defaulting it beside a rate list\n * resolved for something narrower.\n */\nexport function appendShippingAddressCollectionParams(\n params: URLSearchParams,\n countries: readonly string[] = CHECKOUT_SHIPPING_COUNTRIES,\n): void {\n countries.forEach((code, index) => {\n params.set(`shipping_address_collection[allowed_countries][${index}]`, code)\n })\n}\n\n/**\n * Emit resolved rates as `shipping_options` on a Checkout Session's form body.\n *\n * THIS IS THE ONLY THING THAT MAKES STRIPE CHARGE SHIPPING. Without these keys\n * Stripe presents no shipping choice and `total_details.amount_shipping` is 0\n * however many zones and rates the merchant saved (AGL-1707).\n *\n * An empty list emits NOTHING — not an empty array — so a merchant who\n * configured no shipping keeps a session byte-identical to the one built\n * before shipping was wired at all. That guarantee lives here rather than in\n * each caller, so neither checkout path can lose it independently (AGL-1720).\n */\nexport function appendCheckoutShippingParams(\n params: URLSearchParams,\n options: readonly ResolvedShippingRate[],\n): void {\n options.forEach((option, index) => {\n const field = `shipping_options[${index}][shipping_rate_data]`\n params.set(`${field}[type]`, 'fixed_amount')\n params.set(`${field}[display_name]`, option.name.slice(0, 100))\n params.set(`${field}[fixed_amount][amount]`, String(option.amountCents))\n params.set(`${field}[fixed_amount][currency]`, 'usd')\n })\n}\n"],"names":["CARRIER_QUOTE_TIMEOUT_MS","LOCAL_PICKUP_RATE_ID","zoneCode","code","String","trim","toUpperCase","isWildcard","zoneMatches","zone","country","countries","some","tierAmount","tiers","value","sorted","sort","a","b","upTo","tier","amountCents","matchedZoneIds","settings","destinationCountry","zones","specific","filter","matched","length","Set","map","id","carrierRatesFor","zoneIds","rates","rate","kind","has","zoneId","hasCarrierRates","carrierOptionRateId","rateId","serviceKey","carrierOptionAmountCents","quotedCents","quote","Math","max","round","Number","pct","markupPct","handling","handlingCents","isFinite","min","resolveShippingRates","cart","resolved","freeOverCents","subtotalCents","totalGrams","push","name","localPickup","shippableOnly","options","option","MAX_CHECKOUT_SHIPPING_OPTIONS","CHECKOUT_SHIPPING_COUNTRIES","resolveCheckoutShippingOptions","destinationCountries","byRateId","Map","existing","get","set","values","localeCompare","slice","CHECKOUT_SHIPPING_COUNTRY_NAMES","US","CA","GB","AU","DE","FR","normalizeCheckoutShippingCountry","undefined","includes","merchantPricesShipping","CART_UNPRICEABLE_SHIPPING_MESSAGE","optionsKey","join","planCheckoutShipping","destination","shipsElsewhere","refusal","offers","size","union","COVERAGE_PROBE_CART","summarizeShippingCoverage","served","unserved","add","shipsNowhere","asksDestination","offersLocalPickup","Boolean","hasRestOfWorldZone","appendShippingAddressCollectionParams","params","forEach","index","appendCheckoutShippingParams","field"],"mappings":"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"}
|
|
@@ -0,0 +1,90 @@
|
|
|
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
|
+
* WHEN AN OUTSIDE TAX SERVICE PRICED A SALE (AGL-3631).
|
|
19
|
+
*
|
|
20
|
+
* A merchant may connect their own Avalara AvaTax or TaxJar account through
|
|
21
|
+
* another plugin, which answers core's `core.tax-engine` contract. Commerce
|
|
22
|
+
* asks it only where the store collects tax at its OWN rates (`mode:
|
|
23
|
+
* 'manual'`, prices exclusive of tax): that is the regime where the merchant
|
|
24
|
+
* is the one liable for the tax, so an engine of theirs answering for it
|
|
25
|
+
* changes the arithmetic and not whose money it is. A Stripe Tax store is
|
|
26
|
+
* never sent to an engine — that tax is computed against Aglyn's
|
|
27
|
+
* registrations and is Aglyn's to remit (AGL-1904).
|
|
28
|
+
*
|
|
29
|
+
* The order's `taxMode` therefore stays what `storefrontTaxModeOf` says,
|
|
30
|
+
* `manual` (or `none` at zero), and accounting posts it as tax the merchant
|
|
31
|
+
* owes. What this module adds beside it is the `taxEngine` stamp: which
|
|
32
|
+
* service answered, or that it was asked and did not, so the order says the
|
|
33
|
+
* sale fell back to the store's own rates.
|
|
34
|
+
*
|
|
35
|
+
* Pure: no Firestore, no fetch. Imported by path, not through the model
|
|
36
|
+
* barrel, so it costs the console bundle nothing.
|
|
37
|
+
*/
|
|
38
|
+
/** Which outside service priced a sale, or that it was asked and the sale fell back. */
|
|
39
|
+
export interface OrderTaxEngineStamp {
|
|
40
|
+
/** The service's id, e.g. `avalara`. */
|
|
41
|
+
provider: string;
|
|
42
|
+
/** Its name in the merchant's words, e.g. `Avalara AvaTax`. */
|
|
43
|
+
providerLabel: string;
|
|
44
|
+
/** `quoted`: the service's tax was charged. `fallback`: the store's own rates were. */
|
|
45
|
+
status: 'quoted' | 'fallback';
|
|
46
|
+
/** Why it fell back: `timeout` or `error`; `null` when quoted. */
|
|
47
|
+
reason: string | null;
|
|
48
|
+
/** Whether the service's test environment answered. */
|
|
49
|
+
sandbox: boolean;
|
|
50
|
+
}
|
|
51
|
+
/** Whether a store's tax settings let an outside service price its sales. */
|
|
52
|
+
export declare function storeTaxAllowsEngine(settings: {
|
|
53
|
+
mode?: string;
|
|
54
|
+
pricesIncludeTax?: boolean;
|
|
55
|
+
} | null | undefined): boolean;
|
|
56
|
+
/**
|
|
57
|
+
* The stamp as Checkout Session metadata, for the webhook that writes the
|
|
58
|
+
* order to restate. Stripe caps a metadata value at 500 characters; these
|
|
59
|
+
* are a few dozen.
|
|
60
|
+
*/
|
|
61
|
+
export declare function taxEngineSessionMetadata(stamp: OrderTaxEngineStamp | null | undefined): Record<string, string>;
|
|
62
|
+
/** The stamp read back off a session's metadata, spread onto the order. */
|
|
63
|
+
export declare function taxEngineStampFromMetadata(metadata: Record<string, unknown> | null | undefined): {
|
|
64
|
+
taxEngine?: OrderTaxEngineStamp;
|
|
65
|
+
};
|
|
66
|
+
/** The stamp as the public order view carries it: `null` when no service was asked. */
|
|
67
|
+
export declare function orderTaxEngineView(value: unknown): OrderTaxEngineStamp | null;
|
|
68
|
+
/**
|
|
69
|
+
* Splits `totalCents` across `weights` so the parts sum exactly to the
|
|
70
|
+
* total: each part floored, the leftover cents handed out by the largest
|
|
71
|
+
* remainders. Stripe spreads a session coupon across lines in proportion to
|
|
72
|
+
* their amounts; this is the same proportion, so a rate derived from it is
|
|
73
|
+
* applied by Stripe to (within a cent) the amount it was derived from.
|
|
74
|
+
*/
|
|
75
|
+
export declare function allocateCentsByWeight(totalCents: number, weights: readonly number[]): number[];
|
|
76
|
+
/**
|
|
77
|
+
* A quoted tax as one PERCENTAGE per line, for the cart's hosted checkout.
|
|
78
|
+
*
|
|
79
|
+
* The cart charges tax through real Stripe Tax Rates on each line (AGL-1953)
|
|
80
|
+
* because its discounts are session coupons Stripe spreads across every line,
|
|
81
|
+
* and a rate is applied after them. An engine answers in cents per line, so
|
|
82
|
+
* each line's cents become the rate that yields them on that line's
|
|
83
|
+
* discounted amount, to Stripe's four decimal places — exact to the cent for
|
|
84
|
+
* any line under $10,000. A line the engine taxed at zero, or one with
|
|
85
|
+
* nothing left to tax, carries no rate (`null`).
|
|
86
|
+
*/
|
|
87
|
+
export declare function engineLineTaxPercentages(lines: ReadonlyArray<{
|
|
88
|
+
amountCents: number;
|
|
89
|
+
taxCents: number;
|
|
90
|
+
}>, discountCents: number): Array<number | null>;
|
|
@@ -0,0 +1,129 @@
|
|
|
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
|
+
* WHEN AN OUTSIDE TAX SERVICE PRICED A SALE (AGL-3631).
|
|
18
|
+
*
|
|
19
|
+
* A merchant may connect their own Avalara AvaTax or TaxJar account through
|
|
20
|
+
* another plugin, which answers core's `core.tax-engine` contract. Commerce
|
|
21
|
+
* asks it only where the store collects tax at its OWN rates (`mode:
|
|
22
|
+
* 'manual'`, prices exclusive of tax): that is the regime where the merchant
|
|
23
|
+
* is the one liable for the tax, so an engine of theirs answering for it
|
|
24
|
+
* changes the arithmetic and not whose money it is. A Stripe Tax store is
|
|
25
|
+
* never sent to an engine — that tax is computed against Aglyn's
|
|
26
|
+
* registrations and is Aglyn's to remit (AGL-1904).
|
|
27
|
+
*
|
|
28
|
+
* The order's `taxMode` therefore stays what `storefrontTaxModeOf` says,
|
|
29
|
+
* `manual` (or `none` at zero), and accounting posts it as tax the merchant
|
|
30
|
+
* owes. What this module adds beside it is the `taxEngine` stamp: which
|
|
31
|
+
* service answered, or that it was asked and did not, so the order says the
|
|
32
|
+
* sale fell back to the store's own rates.
|
|
33
|
+
*
|
|
34
|
+
* Pure: no Firestore, no fetch. Imported by path, not through the model
|
|
35
|
+
* barrel, so it costs the console bundle nothing.
|
|
36
|
+
*/ /** Which outside service priced a sale, or that it was asked and the sale fell back. */ import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
37
|
+
/** Whether a store's tax settings let an outside service price its sales. */ export function storeTaxAllowsEngine(settings) {
|
|
38
|
+
return (settings == null ? void 0 : settings.mode) === 'manual' && !settings.pricesIncludeTax;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* The stamp as Checkout Session metadata, for the webhook that writes the
|
|
42
|
+
* order to restate. Stripe caps a metadata value at 500 characters; these
|
|
43
|
+
* are a few dozen.
|
|
44
|
+
*/ export function taxEngineSessionMetadata(stamp) {
|
|
45
|
+
if (!stamp) return {};
|
|
46
|
+
return _extends({
|
|
47
|
+
'metadata[taxEngine]': stamp.provider.slice(0, 40),
|
|
48
|
+
'metadata[taxEngineLabel]': stamp.providerLabel.slice(0, 80),
|
|
49
|
+
'metadata[taxEngineStatus]': stamp.status
|
|
50
|
+
}, stamp.reason ? {
|
|
51
|
+
'metadata[taxEngineReason]': stamp.reason.slice(0, 40)
|
|
52
|
+
} : {}, stamp.sandbox ? {
|
|
53
|
+
'metadata[taxEngineSandbox]': 'true'
|
|
54
|
+
} : {});
|
|
55
|
+
}
|
|
56
|
+
/** The stamp read back off a session's metadata, spread onto the order. */ export function taxEngineStampFromMetadata(metadata) {
|
|
57
|
+
var _ref, _ref1, _ref2;
|
|
58
|
+
const provider = String((_ref = metadata == null ? void 0 : metadata['taxEngine']) != null ? _ref : '').trim();
|
|
59
|
+
if (!provider) return {};
|
|
60
|
+
const status = (metadata == null ? void 0 : metadata['taxEngineStatus']) === 'quoted' ? 'quoted' : 'fallback';
|
|
61
|
+
return {
|
|
62
|
+
taxEngine: {
|
|
63
|
+
provider,
|
|
64
|
+
providerLabel: String((_ref1 = metadata == null ? void 0 : metadata['taxEngineLabel']) != null ? _ref1 : '') || provider,
|
|
65
|
+
status,
|
|
66
|
+
reason: status === 'fallback' ? String((_ref2 = metadata == null ? void 0 : metadata['taxEngineReason']) != null ? _ref2 : '') || 'error' : null,
|
|
67
|
+
sandbox: (metadata == null ? void 0 : metadata['taxEngineSandbox']) === 'true'
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
/** The stamp as the public order view carries it: `null` when no service was asked. */ export function orderTaxEngineView(value) {
|
|
72
|
+
var _stamp_providerLabel, _stamp_reason;
|
|
73
|
+
const stamp = value != null ? value : null;
|
|
74
|
+
if (!stamp || typeof stamp['provider'] !== 'string' || !stamp['provider']) return null;
|
|
75
|
+
const status = stamp['status'] === 'quoted' ? 'quoted' : 'fallback';
|
|
76
|
+
return {
|
|
77
|
+
provider: stamp['provider'],
|
|
78
|
+
providerLabel: String((_stamp_providerLabel = stamp['providerLabel']) != null ? _stamp_providerLabel : '') || stamp['provider'],
|
|
79
|
+
status,
|
|
80
|
+
reason: status === 'fallback' ? String((_stamp_reason = stamp['reason']) != null ? _stamp_reason : '') || 'error' : null,
|
|
81
|
+
sandbox: stamp['sandbox'] === true
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Splits `totalCents` across `weights` so the parts sum exactly to the
|
|
86
|
+
* total: each part floored, the leftover cents handed out by the largest
|
|
87
|
+
* remainders. Stripe spreads a session coupon across lines in proportion to
|
|
88
|
+
* their amounts; this is the same proportion, so a rate derived from it is
|
|
89
|
+
* applied by Stripe to (within a cent) the amount it was derived from.
|
|
90
|
+
*/ export function allocateCentsByWeight(totalCents, weights) {
|
|
91
|
+
const total = Math.max(0, Math.round(Number(totalCents) || 0));
|
|
92
|
+
const clean = weights.map((weight)=>Number.isFinite(weight) && weight > 0 ? weight : 0);
|
|
93
|
+
const sum = clean.reduce((a, b)=>a + b, 0);
|
|
94
|
+
if (sum <= 0 || total === 0) return clean.map(()=>0);
|
|
95
|
+
const raw = clean.map((weight)=>total * weight / sum);
|
|
96
|
+
const parts = raw.map(Math.floor);
|
|
97
|
+
let left = total - parts.reduce((a, b)=>a + b, 0);
|
|
98
|
+
const order = raw.map((value, index)=>({
|
|
99
|
+
index,
|
|
100
|
+
remainder: value - Math.floor(value)
|
|
101
|
+
})).filter(({ index })=>clean[index] > 0).sort((a, b)=>b.remainder - a.remainder || a.index - b.index);
|
|
102
|
+
for(let i = 0; left > 0 && order.length > 0; i = (i + 1) % order.length){
|
|
103
|
+
parts[order[i].index] += 1;
|
|
104
|
+
left -= 1;
|
|
105
|
+
}
|
|
106
|
+
return parts;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* A quoted tax as one PERCENTAGE per line, for the cart's hosted checkout.
|
|
110
|
+
*
|
|
111
|
+
* The cart charges tax through real Stripe Tax Rates on each line (AGL-1953)
|
|
112
|
+
* because its discounts are session coupons Stripe spreads across every line,
|
|
113
|
+
* and a rate is applied after them. An engine answers in cents per line, so
|
|
114
|
+
* each line's cents become the rate that yields them on that line's
|
|
115
|
+
* discounted amount, to Stripe's four decimal places — exact to the cent for
|
|
116
|
+
* any line under $10,000. A line the engine taxed at zero, or one with
|
|
117
|
+
* nothing left to tax, carries no rate (`null`).
|
|
118
|
+
*/ export function engineLineTaxPercentages(lines, discountCents) {
|
|
119
|
+
const amounts = lines.map((line)=>Math.max(0, Math.round(Number(line.amountCents) || 0)));
|
|
120
|
+
const discounts = allocateCentsByWeight(Math.min(Math.max(0, discountCents), amounts.reduce((a, b)=>a + b, 0)), amounts);
|
|
121
|
+
return lines.map((line, index)=>{
|
|
122
|
+
const net = amounts[index] - discounts[index];
|
|
123
|
+
const tax = Math.max(0, Math.round(Number(line.taxCents) || 0));
|
|
124
|
+
if (net <= 0 || tax <= 0) return null;
|
|
125
|
+
return Math.round(tax / net * 100 * 10000) / 10000;
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
//# sourceMappingURL=commerce-tax-engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/model/commerce-tax-engine.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 * WHEN AN OUTSIDE TAX SERVICE PRICED A SALE (AGL-3631).\n *\n * A merchant may connect their own Avalara AvaTax or TaxJar account through\n * another plugin, which answers core's `core.tax-engine` contract. Commerce\n * asks it only where the store collects tax at its OWN rates (`mode:\n * 'manual'`, prices exclusive of tax): that is the regime where the merchant\n * is the one liable for the tax, so an engine of theirs answering for it\n * changes the arithmetic and not whose money it is. A Stripe Tax store is\n * never sent to an engine — that tax is computed against Aglyn's\n * registrations and is Aglyn's to remit (AGL-1904).\n *\n * The order's `taxMode` therefore stays what `storefrontTaxModeOf` says,\n * `manual` (or `none` at zero), and accounting posts it as tax the merchant\n * owes. What this module adds beside it is the `taxEngine` stamp: which\n * service answered, or that it was asked and did not, so the order says the\n * sale fell back to the store's own rates.\n *\n * Pure: no Firestore, no fetch. Imported by path, not through the model\n * barrel, so it costs the console bundle nothing.\n */\n\n/** Which outside service priced a sale, or that it was asked and the sale fell back. */\nexport interface OrderTaxEngineStamp {\n /** The service's id, e.g. `avalara`. */\n provider: string\n /** Its name in the merchant's words, e.g. `Avalara AvaTax`. */\n providerLabel: string\n /** `quoted`: the service's tax was charged. `fallback`: the store's own rates were. */\n status: 'quoted' | 'fallback'\n /** Why it fell back: `timeout` or `error`; `null` when quoted. */\n reason: string | null\n /** Whether the service's test environment answered. */\n sandbox: boolean\n}\n\n/** Whether a store's tax settings let an outside service price its sales. */\nexport function storeTaxAllowsEngine(settings: {\n mode?: string\n pricesIncludeTax?: boolean\n} | null | undefined): boolean {\n return settings?.mode === 'manual' && !settings.pricesIncludeTax\n}\n\n/**\n * The stamp as Checkout Session metadata, for the webhook that writes the\n * order to restate. Stripe caps a metadata value at 500 characters; these\n * are a few dozen.\n */\nexport function taxEngineSessionMetadata(\n stamp: OrderTaxEngineStamp | null | undefined,\n): Record<string, string> {\n if (!stamp) return {}\n return {\n 'metadata[taxEngine]': stamp.provider.slice(0, 40),\n 'metadata[taxEngineLabel]': stamp.providerLabel.slice(0, 80),\n 'metadata[taxEngineStatus]': stamp.status,\n ...(stamp.reason ? { 'metadata[taxEngineReason]': stamp.reason.slice(0, 40) } : {}),\n ...(stamp.sandbox ? { 'metadata[taxEngineSandbox]': 'true' } : {}),\n }\n}\n\n/** The stamp read back off a session's metadata, spread onto the order. */\nexport function taxEngineStampFromMetadata(\n metadata: Record<string, unknown> | null | undefined,\n): { taxEngine?: OrderTaxEngineStamp } {\n const provider = String(metadata?.['taxEngine'] ?? '').trim()\n if (!provider) return {}\n const status = metadata?.['taxEngineStatus'] === 'quoted' ? 'quoted' : 'fallback'\n return {\n taxEngine: {\n provider,\n providerLabel: String(metadata?.['taxEngineLabel'] ?? '') || provider,\n status,\n reason: status === 'fallback' ? String(metadata?.['taxEngineReason'] ?? '') || 'error' : null,\n sandbox: metadata?.['taxEngineSandbox'] === 'true',\n },\n }\n}\n\n/** The stamp as the public order view carries it: `null` when no service was asked. */\nexport function orderTaxEngineView(value: unknown): OrderTaxEngineStamp | null {\n const stamp = (value ?? null) as Record<string, unknown> | null\n if (!stamp || typeof stamp['provider'] !== 'string' || !stamp['provider']) return null\n const status = stamp['status'] === 'quoted' ? 'quoted' : 'fallback'\n return {\n provider: stamp['provider'],\n providerLabel: String(stamp['providerLabel'] ?? '') || stamp['provider'],\n status,\n reason: status === 'fallback' ? String(stamp['reason'] ?? '') || 'error' : null,\n sandbox: stamp['sandbox'] === true,\n }\n}\n\n/**\n * Splits `totalCents` across `weights` so the parts sum exactly to the\n * total: each part floored, the leftover cents handed out by the largest\n * remainders. Stripe spreads a session coupon across lines in proportion to\n * their amounts; this is the same proportion, so a rate derived from it is\n * applied by Stripe to (within a cent) the amount it was derived from.\n */\nexport function allocateCentsByWeight(totalCents: number, weights: readonly number[]): number[] {\n const total = Math.max(0, Math.round(Number(totalCents) || 0))\n const clean = weights.map((weight) => (Number.isFinite(weight) && weight > 0 ? weight : 0))\n const sum = clean.reduce((a, b) => a + b, 0)\n if (sum <= 0 || total === 0) return clean.map(() => 0)\n const raw = clean.map((weight) => (total * weight) / sum)\n const parts = raw.map(Math.floor)\n let left = total - parts.reduce((a, b) => a + b, 0)\n const order = raw\n .map((value, index) => ({ index, remainder: value - Math.floor(value) }))\n .filter(({ index }) => clean[index] > 0)\n .sort((a, b) => b.remainder - a.remainder || a.index - b.index)\n for (let i = 0; left > 0 && order.length > 0; i = (i + 1) % order.length) {\n parts[order[i].index] += 1\n left -= 1\n }\n return parts\n}\n\n/**\n * A quoted tax as one PERCENTAGE per line, for the cart's hosted checkout.\n *\n * The cart charges tax through real Stripe Tax Rates on each line (AGL-1953)\n * because its discounts are session coupons Stripe spreads across every line,\n * and a rate is applied after them. An engine answers in cents per line, so\n * each line's cents become the rate that yields them on that line's\n * discounted amount, to Stripe's four decimal places — exact to the cent for\n * any line under $10,000. A line the engine taxed at zero, or one with\n * nothing left to tax, carries no rate (`null`).\n */\nexport function engineLineTaxPercentages(\n lines: ReadonlyArray<{ amountCents: number; taxCents: number }>,\n discountCents: number,\n): Array<number | null> {\n const amounts = lines.map((line) => Math.max(0, Math.round(Number(line.amountCents) || 0)))\n const discounts = allocateCentsByWeight(\n Math.min(Math.max(0, discountCents), amounts.reduce((a, b) => a + b, 0)),\n amounts,\n )\n return lines.map((line, index) => {\n const net = amounts[index] - discounts[index]\n const tax = Math.max(0, Math.round(Number(line.taxCents) || 0))\n if (net <= 0 || tax <= 0) return null\n return Math.round((tax / net) * 100 * 10_000) / 10_000\n })\n}\n"],"names":["storeTaxAllowsEngine","settings","mode","pricesIncludeTax","taxEngineSessionMetadata","stamp","provider","slice","providerLabel","status","reason","sandbox","taxEngineStampFromMetadata","metadata","String","trim","taxEngine","orderTaxEngineView","value","allocateCentsByWeight","totalCents","weights","total","Math","max","round","Number","clean","map","weight","isFinite","sum","reduce","a","b","raw","parts","floor","left","order","index","remainder","filter","sort","i","length","engineLineTaxPercentages","lines","discountCents","amounts","line","amountCents","discounts","min","net","tax","taxCents"],"mappings":"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"}
|
|
@@ -14,6 +14,8 @@
|
|
|
14
14
|
* See the License for the specific language governing permissions and
|
|
15
15
|
* limitations under the License.
|
|
16
16
|
*/
|
|
17
|
+
import { type ProductModifierGroup } from './product-modifiers';
|
|
18
|
+
import { type ProductChannelFacts } from './product-channel';
|
|
17
19
|
/**
|
|
18
20
|
* Commerce catalog v1 (AGL-276): products with options/variants,
|
|
19
21
|
* hierarchical categories, tags, and manual/smart collections. Pure
|
|
@@ -54,12 +56,56 @@ export interface ProductVariant {
|
|
|
54
56
|
/** Media-library image URL shown when this variant is selected. */
|
|
55
57
|
imageUrl?: string;
|
|
56
58
|
}
|
|
59
|
+
/** A product's packed size and customs facts (AGL-3612). */
|
|
60
|
+
export interface ProductShippingFacts {
|
|
61
|
+
lengthCm?: number | null;
|
|
62
|
+
widthCm?: number | null;
|
|
63
|
+
heightCm?: number | null;
|
|
64
|
+
/** Harmonized System tariff code. */
|
|
65
|
+
hsCode?: string;
|
|
66
|
+
/** ISO-3166 alpha-2 country the product was made in. */
|
|
67
|
+
originCountry?: string;
|
|
68
|
+
}
|
|
69
|
+
/** A place's postal address, in parts (AGL-3612). Country is ISO-3166 alpha-2. */
|
|
70
|
+
export interface PostalAddress {
|
|
71
|
+
line1?: string;
|
|
72
|
+
line2?: string;
|
|
73
|
+
city?: string;
|
|
74
|
+
state?: string;
|
|
75
|
+
postalCode?: string;
|
|
76
|
+
country?: string;
|
|
77
|
+
phone?: string;
|
|
78
|
+
}
|
|
57
79
|
/** `hosts/{hostId}/locations/{id}` doc (AGL-286). */
|
|
58
80
|
export interface InventoryLocation {
|
|
59
81
|
name: string;
|
|
60
82
|
isDefault?: boolean;
|
|
83
|
+
/** Free text, as the location was first described. */
|
|
61
84
|
address?: string;
|
|
85
|
+
/**
|
|
86
|
+
* The same place as a postal address (AGL-3612), so parcels can ship from
|
|
87
|
+
* it. Optional: a location written before this has only `address`.
|
|
88
|
+
*/
|
|
89
|
+
postalAddress?: PostalAddress;
|
|
62
90
|
}
|
|
91
|
+
/** A product's packed size for one unit, when all three sides are known. */
|
|
92
|
+
export declare function productParcelDimensions(product: Pick<HostProduct, 'shipping'> | null | undefined): {
|
|
93
|
+
lengthCm: number;
|
|
94
|
+
widthCm: number;
|
|
95
|
+
heightCm: number;
|
|
96
|
+
} | undefined;
|
|
97
|
+
/**
|
|
98
|
+
* Shipping facts as the product editor stages them, keystroke by keystroke
|
|
99
|
+
* (AGL-3647): {@link normalizeProductShippingFacts}, except that a country
|
|
100
|
+
* of origin being typed keeps its first letter. The editor runs this on
|
|
101
|
+
* every keystroke, and the strict form drops anything short of two letters,
|
|
102
|
+
* so the field could never be typed into. Readers normalize what is stored.
|
|
103
|
+
*/
|
|
104
|
+
export declare function draftProductShippingFacts(value: unknown): ProductShippingFacts | undefined;
|
|
105
|
+
/** A stored product's shipping facts made safe (AGL-3612); never throws. */
|
|
106
|
+
export declare function normalizeProductShippingFacts(value: unknown): ProductShippingFacts | undefined;
|
|
107
|
+
/** A stored location's postal address made safe (AGL-3612); never throws. */
|
|
108
|
+
export declare function normalizePostalAddress(value: unknown): PostalAddress | undefined;
|
|
63
109
|
/**
|
|
64
110
|
* `hosts/{hostId}/registers/{id}` doc (AGL-472): a named point-of-sale
|
|
65
111
|
* register. Creation is capped by the plan's `posRegisters` quota (Pro 1,
|
|
@@ -120,6 +166,12 @@ export interface HostProduct {
|
|
|
120
166
|
description?: string;
|
|
121
167
|
imageUrl?: string;
|
|
122
168
|
};
|
|
169
|
+
/**
|
|
170
|
+
* What shopping channels ask of the product (AGL-3637): brand, GTIN, MPN,
|
|
171
|
+
* condition and Google category. Absent means nothing was entered; the
|
|
172
|
+
* channel feeds fall back to the store's defaults.
|
|
173
|
+
*/
|
|
174
|
+
channel?: ProductChannelFacts;
|
|
123
175
|
/** Supplier for dropship routing (AGL-289). */
|
|
124
176
|
supplierId?: string;
|
|
125
177
|
/** Out-of-stock behavior (AGL-281): deny (default) or allow backorder. */
|
|
@@ -160,10 +212,25 @@ export interface HostProduct {
|
|
|
160
212
|
}>;
|
|
161
213
|
/** Manual related products for the upsell block (AGL-325). */
|
|
162
214
|
relatedProductIds?: string[];
|
|
215
|
+
/**
|
|
216
|
+
* What one unit ships as (AGL-3612): its packed size, for a carrier to
|
|
217
|
+
* price, and its customs facts, for a parcel that crosses a border. Weight
|
|
218
|
+
* stays per variant. Absent means unknown, and a label is priced in the
|
|
219
|
+
* merchant's default box.
|
|
220
|
+
*/
|
|
221
|
+
shipping?: ProductShippingFacts;
|
|
163
222
|
/** Buying this issues a gift-card code for its price (AGL-322). */
|
|
164
223
|
giftCard?: boolean;
|
|
165
224
|
/** Tracked-total at/below this alerts host managers (AGL-281). */
|
|
166
225
|
lowStockThreshold?: number;
|
|
226
|
+
/**
|
|
227
|
+
* Choices added to the item at the register (AGL-3607) — "Oat milk",
|
|
228
|
+
* "Extra shot" — priced by the server from this list. See
|
|
229
|
+
* `product-modifiers.ts`.
|
|
230
|
+
*/
|
|
231
|
+
modifierGroups?: ProductModifierGroup[];
|
|
232
|
+
/** Shown under Quick keys on the register's first screen (AGL-3607). */
|
|
233
|
+
posQuickKey?: boolean;
|
|
167
234
|
createdAtMs?: number;
|
|
168
235
|
updatedAtMs?: number;
|
|
169
236
|
deletedAt?: number | null;
|
|
@@ -359,6 +426,18 @@ export interface ProductCopyValues {
|
|
|
359
426
|
optionNames?: string[];
|
|
360
427
|
seoTitle?: string;
|
|
361
428
|
seoDescription?: string;
|
|
429
|
+
/**
|
|
430
|
+
* Packed size and customs facts (AGL-3612), merged over what the product
|
|
431
|
+
* has; a side set to `null` is cleared.
|
|
432
|
+
*/
|
|
433
|
+
shipping?: Partial<ProductShippingFacts>;
|
|
434
|
+
/**
|
|
435
|
+
* Shopping-channel facts (AGL-3637), merged over what the product has; a
|
|
436
|
+
* field set to `''` is cleared.
|
|
437
|
+
*/
|
|
438
|
+
channel?: {
|
|
439
|
+
[Field in keyof ProductChannelFacts]?: string;
|
|
440
|
+
};
|
|
362
441
|
}
|
|
363
442
|
/**
|
|
364
443
|
* The fields proposed copy changes on a product (AGL-2916), in the shape the
|
|
@@ -367,7 +446,7 @@ export interface ProductCopyValues {
|
|
|
367
446
|
* rename the product's options through `renameProductOptions`, keeping every
|
|
368
447
|
* variant, only when they name each option once with a name of its own.
|
|
369
448
|
*/
|
|
370
|
-
export declare function productCopyPatch(product: Pick<HostProduct, 'options' | 'variants' | 'seo'>, values: ProductCopyValues): Partial<Pick<HostProduct, 'description' | 'tags' | 'categoryIds' | 'seo' | 'options' | 'variants'>>;
|
|
449
|
+
export declare function productCopyPatch(product: Pick<HostProduct, 'options' | 'variants' | 'seo' | 'shipping'> & Pick<Partial<HostProduct>, 'channel'>, values: ProductCopyValues): Partial<Pick<HostProduct, 'description' | 'tags' | 'categoryIds' | 'seo' | 'options' | 'variants' | 'shipping' | 'channel'>>;
|
|
371
450
|
/** A product proposed for a store, before anyone has priced it (AGL-2916). */
|
|
372
451
|
export interface ProposedProduct {
|
|
373
452
|
name: string;
|
|
@@ -408,8 +487,13 @@ export declare function productInventory(product: Pick<HostProduct, 'variants'>)
|
|
|
408
487
|
* picks when the count disagreed with the shelf for a reason nobody recorded.
|
|
409
488
|
* Kept out of the "Adjust stock" dialog's menu on purpose: a merchant reaching
|
|
410
489
|
* for it by hand means something else happened.
|
|
490
|
+
*
|
|
491
|
+
* `sync` is a count another warehouse reported (AGL-3634): a fulfillment
|
|
492
|
+
* network holding the goods said how many it can ship, and the count was set
|
|
493
|
+
* to that. Written only through core's `core.stock-levels`, with `source`
|
|
494
|
+
* naming who counted, and kept out of the dialog's menu for the same reason.
|
|
411
495
|
*/
|
|
412
|
-
export type InventoryAdjustmentReason = 'sale' | 'refund' | 'restock' | 'correction' | 'damage' | 'cancellation';
|
|
496
|
+
export type InventoryAdjustmentReason = 'sale' | 'refund' | 'restock' | 'correction' | 'damage' | 'cancellation' | 'sync';
|
|
413
497
|
/** `hosts/{hostId}/inventoryAdjustments/{id}` doc. */
|
|
414
498
|
export interface InventoryAdjustment {
|
|
415
499
|
productId: string;
|
|
@@ -436,6 +520,11 @@ export interface InventoryAdjustment {
|
|
|
436
520
|
orderId?: string;
|
|
437
521
|
/** Location for multi-location stock (AGL-286); absent = default. */
|
|
438
522
|
locationId?: string;
|
|
523
|
+
/**
|
|
524
|
+
* Who counted, for a `sync` row: the warehouse that reported it (AGL-3634);
|
|
525
|
+
* for a `sale` another channel made, that channel (AGL-3638).
|
|
526
|
+
*/
|
|
527
|
+
source?: string;
|
|
439
528
|
atMs: number;
|
|
440
529
|
}
|
|
441
530
|
/**
|