@sixtyninecommerce/client 0.23.1 → 0.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -0
- package/dist/admin/forms.js +19 -0
- package/dist/admin/forms.js.map +1 -1
- package/dist/admin/http.d.ts +9 -0
- package/dist/admin/http.d.ts.map +1 -1
- package/dist/admin/http.js +3 -0
- package/dist/admin/http.js.map +1 -1
- package/dist/admin/index.d.ts +63 -0
- package/dist/admin/index.d.ts.map +1 -1
- package/dist/admin/index.js +67 -0
- package/dist/admin/index.js.map +1 -1
- package/dist/admin/redirects.d.ts +6 -0
- package/dist/admin/redirects.d.ts.map +1 -1
- package/dist/admin/redirects.js +12 -0
- package/dist/admin/redirects.js.map +1 -1
- package/dist/admin/types.d.ts +63 -0
- package/dist/admin/types.d.ts.map +1 -1
- package/dist/cache-tags.d.ts +19 -0
- package/dist/cache-tags.d.ts.map +1 -1
- package/dist/cache-tags.js +19 -0
- package/dist/cache-tags.js.map +1 -1
- package/dist/client.d.ts +17 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +17 -0
- package/dist/client.js.map +1 -1
- package/dist/commerce/age-verification.js +3 -3
- package/dist/commerce/age-verification.js.map +1 -1
- package/dist/commerce/branding.js +7 -0
- package/dist/commerce/branding.js.map +1 -1
- package/dist/commerce/cart.js +25 -0
- package/dist/commerce/cart.js.map +1 -1
- package/dist/commerce/checkout.d.ts +68 -0
- package/dist/commerce/checkout.d.ts.map +1 -1
- package/dist/commerce/checkout.js +83 -0
- package/dist/commerce/checkout.js.map +1 -1
- package/dist/commerce/customer.d.ts +58 -0
- package/dist/commerce/customer.d.ts.map +1 -1
- package/dist/commerce/customer.js +65 -0
- package/dist/commerce/customer.js.map +1 -1
- package/dist/commerce/index.d.ts +17 -1
- package/dist/commerce/index.d.ts.map +1 -1
- package/dist/commerce/pickup-location-parser.d.ts +18 -0
- package/dist/commerce/pickup-location-parser.d.ts.map +1 -1
- package/dist/commerce/pickup-location-parser.js +28 -0
- package/dist/commerce/pickup-location-parser.js.map +1 -1
- package/dist/commerce/placement-error.d.ts +52 -0
- package/dist/commerce/placement-error.d.ts.map +1 -1
- package/dist/commerce/placement-error.js +87 -0
- package/dist/commerce/placement-error.js.map +1 -1
- package/dist/commerce/products.d.ts +48 -0
- package/dist/commerce/products.d.ts.map +1 -1
- package/dist/commerce/products.js +95 -2
- package/dist/commerce/products.js.map +1 -1
- package/dist/commerce/queries.d.ts +114 -27
- package/dist/commerce/queries.d.ts.map +1 -1
- package/dist/commerce/queries.js +219 -1
- package/dist/commerce/queries.js.map +1 -1
- package/dist/commerce/quotes.js +33 -0
- package/dist/commerce/quotes.js.map +1 -1
- package/dist/commerce/saved-lists.d.ts +7 -7
- package/dist/commerce/saved-lists.d.ts.map +1 -1
- package/dist/commerce/saved-lists.js +1 -1
- package/dist/commerce/search.js +3 -0
- package/dist/commerce/search.js.map +1 -1
- package/dist/commerce/shipping.js +3 -3
- package/dist/commerce/shipping.js.map +1 -1
- package/dist/commerce/stock-notifications.d.ts +11 -0
- package/dist/commerce/stock-notifications.d.ts.map +1 -1
- package/dist/commerce/stock-notifications.js +11 -0
- package/dist/commerce/stock-notifications.js.map +1 -1
- package/dist/commerce/subscriptions.d.ts +32 -8
- package/dist/commerce/subscriptions.d.ts.map +1 -1
- package/dist/commerce/subscriptions.js +90 -2
- package/dist/commerce/subscriptions.js.map +1 -1
- package/dist/commerce/vat-validation.js +4 -4
- package/dist/commerce/vat-validation.js.map +1 -1
- package/dist/constants.d.ts +31 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +32 -0
- package/dist/constants.js.map +1 -1
- package/dist/content/asset-urls.d.ts +50 -0
- package/dist/content/asset-urls.d.ts.map +1 -1
- package/dist/content/asset-urls.js +92 -0
- package/dist/content/asset-urls.js.map +1 -1
- package/dist/content/assets.d.ts +15 -0
- package/dist/content/assets.d.ts.map +1 -1
- package/dist/content/assets.js +15 -0
- package/dist/content/assets.js.map +1 -1
- package/dist/content/collection-pages.d.ts +5 -0
- package/dist/content/collection-pages.d.ts.map +1 -1
- package/dist/content/collection-pages.js +8 -0
- package/dist/content/collection-pages.js.map +1 -1
- package/dist/content/collections.d.ts +14 -0
- package/dist/content/collections.d.ts.map +1 -1
- package/dist/content/collections.js +16 -0
- package/dist/content/collections.js.map +1 -1
- package/dist/content/filtered-products.d.ts +18 -0
- package/dist/content/filtered-products.d.ts.map +1 -1
- package/dist/content/filtered-products.js +16 -0
- package/dist/content/filtered-products.js.map +1 -1
- package/dist/content/form-field-types.d.ts +6 -0
- package/dist/content/form-field-types.d.ts.map +1 -1
- package/dist/content/form-field-types.js +12 -0
- package/dist/content/form-field-types.js.map +1 -1
- package/dist/content/forms.d.ts +16 -0
- package/dist/content/forms.d.ts.map +1 -1
- package/dist/content/forms.js +23 -0
- package/dist/content/forms.js.map +1 -1
- package/dist/content/items.d.ts +26 -0
- package/dist/content/items.d.ts.map +1 -1
- package/dist/content/items.js +33 -0
- package/dist/content/items.js.map +1 -1
- package/dist/content/resolve.js +6 -0
- package/dist/content/resolve.js.map +1 -1
- package/dist/content/sitemap.d.ts +5 -0
- package/dist/content/sitemap.d.ts.map +1 -1
- package/dist/content/sitemap.js +5 -0
- package/dist/content/sitemap.js.map +1 -1
- package/dist/core/context.js +6 -0
- package/dist/core/context.js.map +1 -1
- package/dist/core/headers.js +2 -0
- package/dist/core/headers.js.map +1 -1
- package/dist/core/http.d.ts +8 -0
- package/dist/core/http.d.ts.map +1 -1
- package/dist/core/http.js +89 -1
- package/dist/core/http.js.map +1 -1
- package/dist/credit/credit-status.js +2 -2
- package/dist/credit/credit-status.js.map +1 -1
- package/dist/credit/payment-terms.js +2 -2
- package/dist/credit/payment-terms.js.map +1 -1
- package/dist/currency/exchange.d.ts +11 -0
- package/dist/currency/exchange.d.ts.map +1 -1
- package/dist/currency/exchange.js +11 -0
- package/dist/currency/exchange.js.map +1 -1
- package/dist/errors.d.ts +50 -2
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +50 -3
- package/dist/errors.js.map +1 -1
- package/dist/events/envelope.d.ts +36 -0
- package/dist/events/envelope.d.ts.map +1 -1
- package/dist/events/envelope.js +12 -0
- package/dist/events/envelope.js.map +1 -1
- package/dist/events/index.d.ts +13 -0
- package/dist/events/index.d.ts.map +1 -1
- package/dist/events/index.js +13 -0
- package/dist/events/index.js.map +1 -1
- package/dist/events/payloads.d.ts +19 -0
- package/dist/events/payloads.d.ts.map +1 -1
- package/dist/events/payloads.js +12 -0
- package/dist/events/payloads.js.map +1 -1
- package/dist/index.d.ts +8 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -1
- package/dist/media/index.js +2 -0
- package/dist/media/index.js.map +1 -1
- package/dist/media/urls.d.ts +52 -0
- package/dist/media/urls.d.ts.map +1 -1
- package/dist/media/urls.js +38 -0
- package/dist/media/urls.js.map +1 -1
- package/dist/media/video.d.ts +16 -0
- package/dist/media/video.d.ts.map +1 -1
- package/dist/media/video.js +16 -0
- package/dist/media/video.js.map +1 -1
- package/dist/next/artifact-handler.d.ts +22 -0
- package/dist/next/artifact-handler.d.ts.map +1 -1
- package/dist/next/artifact-handler.js +22 -0
- package/dist/next/artifact-handler.js.map +1 -1
- package/dist/next/event-revalidate-handler.d.ts +67 -0
- package/dist/next/event-revalidate-handler.d.ts.map +1 -1
- package/dist/next/event-revalidate-handler.js +48 -0
- package/dist/next/event-revalidate-handler.js.map +1 -1
- package/dist/next/index.d.ts +6 -0
- package/dist/next/index.d.ts.map +1 -1
- package/dist/next/index.js +6 -0
- package/dist/next/index.js.map +1 -1
- package/dist/next/json-ld.d.ts +9 -0
- package/dist/next/json-ld.d.ts.map +1 -1
- package/dist/next/json-ld.js +9 -0
- package/dist/next/json-ld.js.map +1 -1
- package/dist/next/metadata.d.ts +18 -0
- package/dist/next/metadata.d.ts.map +1 -1
- package/dist/next/metadata.js +21 -0
- package/dist/next/metadata.js.map +1 -1
- package/dist/next/redirect-middleware.d.ts +39 -0
- package/dist/next/redirect-middleware.d.ts.map +1 -1
- package/dist/next/redirect-middleware.js +32 -0
- package/dist/next/redirect-middleware.js.map +1 -1
- package/dist/next/revalidate-handler.d.ts +30 -0
- package/dist/next/revalidate-handler.d.ts.map +1 -1
- package/dist/next/revalidate-handler.js +22 -0
- package/dist/next/revalidate-handler.js.map +1 -1
- package/dist/order-scope.d.ts +27 -0
- package/dist/order-scope.d.ts.map +1 -1
- package/dist/order-scope.js +29 -0
- package/dist/order-scope.js.map +1 -1
- package/dist/pricing/format.d.ts +17 -0
- package/dist/pricing/format.d.ts.map +1 -1
- package/dist/pricing/format.js +19 -0
- package/dist/pricing/format.js.map +1 -1
- package/dist/pricing/index.d.ts +8 -1
- package/dist/pricing/index.d.ts.map +1 -1
- package/dist/pricing/index.js +18 -2
- package/dist/pricing/index.js.map +1 -1
- package/dist/pricing/tax-display.d.ts +77 -0
- package/dist/pricing/tax-display.d.ts.map +1 -1
- package/dist/pricing/tax-display.js +85 -0
- package/dist/pricing/tax-display.js.map +1 -1
- package/dist/pricing/tiers.d.ts +9 -0
- package/dist/pricing/tiers.d.ts.map +1 -1
- package/dist/pricing/tiers.js +9 -0
- package/dist/pricing/tiers.js.map +1 -1
- package/dist/pricing/validation.d.ts +56 -1
- package/dist/pricing/validation.d.ts.map +1 -1
- package/dist/pricing/validation.js +61 -0
- package/dist/pricing/validation.js.map +1 -1
- package/dist/redirects/index.d.ts +1 -0
- package/dist/redirects/index.d.ts.map +1 -1
- package/dist/redirects/index.js +1 -0
- package/dist/redirects/index.js.map +1 -1
- package/dist/redirects/store.d.ts +20 -0
- package/dist/redirects/store.d.ts.map +1 -1
- package/dist/redirects/store.js +57 -0
- package/dist/redirects/store.js.map +1 -1
- package/dist/redirects/types.d.ts +24 -0
- package/dist/redirects/types.d.ts.map +1 -1
- package/dist/redirects/types.js +1 -0
- package/dist/redirects/types.js.map +1 -1
- package/dist/revalidation/event-plan.d.ts +48 -0
- package/dist/revalidation/event-plan.d.ts.map +1 -1
- package/dist/revalidation/event-plan.js +61 -0
- package/dist/revalidation/event-plan.js.map +1 -1
- package/dist/revalidation/event-verify.d.ts +25 -0
- package/dist/revalidation/event-verify.d.ts.map +1 -1
- package/dist/revalidation/event-verify.js +27 -0
- package/dist/revalidation/event-verify.js.map +1 -1
- package/dist/revalidation/types.d.ts +20 -0
- package/dist/revalidation/types.d.ts.map +1 -1
- package/dist/revalidation/types.js +10 -0
- package/dist/revalidation/types.js.map +1 -1
- package/dist/revalidation/url-context.d.ts +25 -0
- package/dist/revalidation/url-context.d.ts.map +1 -1
- package/dist/revalidation/url-context.js +17 -0
- package/dist/revalidation/url-context.js.map +1 -1
- package/dist/revalidation/verify.d.ts +8 -0
- package/dist/revalidation/verify.d.ts.map +1 -1
- package/dist/revalidation/verify.js +8 -0
- package/dist/revalidation/verify.js.map +1 -1
- package/dist/seo/ai.d.ts +17 -0
- package/dist/seo/ai.d.ts.map +1 -1
- package/dist/seo/ai.js +12 -0
- package/dist/seo/ai.js.map +1 -1
- package/dist/seo/alternates.js +13 -0
- package/dist/seo/alternates.js.map +1 -1
- package/dist/seo/artifacts.d.ts +13 -0
- package/dist/seo/artifacts.d.ts.map +1 -1
- package/dist/seo/artifacts.js +19 -0
- package/dist/seo/artifacts.js.map +1 -1
- package/dist/seo/canonical.d.ts +15 -0
- package/dist/seo/canonical.d.ts.map +1 -1
- package/dist/seo/canonical.js +24 -0
- package/dist/seo/canonical.js.map +1 -1
- package/dist/seo/index.js +6 -0
- package/dist/seo/index.js.map +1 -1
- package/dist/seo/json-ld.d.ts +13 -0
- package/dist/seo/json-ld.d.ts.map +1 -1
- package/dist/seo/json-ld.js +32 -0
- package/dist/seo/json-ld.js.map +1 -1
- package/dist/seo/locale-tag.d.ts +31 -0
- package/dist/seo/locale-tag.d.ts.map +1 -1
- package/dist/seo/locale-tag.js +31 -0
- package/dist/seo/locale-tag.js.map +1 -1
- package/dist/seo/meta-tags.d.ts +29 -0
- package/dist/seo/meta-tags.d.ts.map +1 -1
- package/dist/seo/meta-tags.js +28 -0
- package/dist/seo/meta-tags.js.map +1 -1
- package/dist/seo/metadata-parser.d.ts +24 -0
- package/dist/seo/metadata-parser.d.ts.map +1 -1
- package/dist/seo/metadata-parser.js +38 -0
- package/dist/seo/metadata-parser.js.map +1 -1
- package/dist/seo/metadata-types.d.ts +16 -0
- package/dist/seo/metadata-types.d.ts.map +1 -1
- package/dist/seo/product-path.d.ts +96 -0
- package/dist/seo/product-path.d.ts.map +1 -1
- package/dist/seo/product-path.js +96 -0
- package/dist/seo/product-path.js.map +1 -1
- package/dist/seo/robots.d.ts +17 -0
- package/dist/seo/robots.d.ts.map +1 -1
- package/dist/seo/robots.js +12 -0
- package/dist/seo/robots.js.map +1 -1
- package/dist/seo/types.d.ts +39 -0
- package/dist/seo/types.d.ts.map +1 -1
- package/dist/seo/url-builder.d.ts +31 -0
- package/dist/seo/url-builder.d.ts.map +1 -1
- package/dist/seo/url-builder.js +36 -0
- package/dist/seo/url-builder.js.map +1 -1
- package/dist/types.d.ts +1283 -2
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -1,9 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which tax figure a shopper sees: incl. (`gross`) or excl. (`net`) tax.
|
|
3
|
+
*
|
|
4
|
+
* `Channel.pricesIncludeTax` is a STORAGE mode — it says which of `price` /
|
|
5
|
+
* `priceWithTax` the merchant typed in, not which one this shopper should be
|
|
6
|
+
* quoted. Those differ in real shops: US sales tax varies by state and even by
|
|
7
|
+
* district, so a US shopper must never be quoted a tax-inclusive price, and
|
|
8
|
+
* Dutch B2B buyers work in ex-VAT numbers, while Dutch consumers keep seeing
|
|
9
|
+
* the gross price the law requires. One channel, three audiences.
|
|
10
|
+
*
|
|
11
|
+
* So the engine resolves the mode per request and exposes it as
|
|
12
|
+
* `Query.activeTaxDisplayMode` — that query is the authority, and
|
|
13
|
+
* `client.pricing.getTaxDisplayMode()` is how a storefront reads it. The pure
|
|
14
|
+
* helpers below mirror the engine's rules
|
|
15
|
+
* (`services/vendure/src/tax-display/tax-display-policy.ts`) RULE FOR RULE so a
|
|
16
|
+
* client can preview a mode — a "who sees what" table, an SSR guess before the
|
|
17
|
+
* first round-trip — without one. Change one, change both.
|
|
18
|
+
*
|
|
19
|
+
* The channel policy behind it (three `public: true` Channel customFields, all
|
|
20
|
+
* defaulting to today's behaviour):
|
|
21
|
+
*
|
|
22
|
+
* taxDisplayDefault channel | gross | net (default `channel`)
|
|
23
|
+
* taxDisplayB2b inherit | gross | net (default `inherit`)
|
|
24
|
+
* taxExclusiveCountries JSON ["US"] (default empty)
|
|
25
|
+
*/
|
|
1
26
|
import type { StoreContext, TaxDisplayInputs, TaxDisplayMode, TaxDisplayPolicy } from '../types.js';
|
|
27
|
+
/** Every channel reads this until a merchant changes something. */
|
|
2
28
|
export declare const DEFAULT_TAX_DISPLAY_POLICY: TaxDisplayPolicy;
|
|
29
|
+
/**
|
|
30
|
+
* The resolved mode for the active shopper. Public, anonymous-safe, and it
|
|
31
|
+
* never errors server-side — a lookup failure there falls back to the channel
|
|
32
|
+
* default rather than failing the request.
|
|
33
|
+
*
|
|
34
|
+
* The answer depends on the request's identity, so the SDK sends it with the
|
|
35
|
+
* same `Authorization` / `x-order-scope` headers as every other call.
|
|
36
|
+
*/
|
|
3
37
|
export declare const ACTIVE_TAX_DISPLAY_MODE = "\n query ActiveTaxDisplayMode {\n activeTaxDisplayMode\n }\n";
|
|
38
|
+
/**
|
|
39
|
+
* Resolve the mode for one shopper — the engine's rule, offline.
|
|
40
|
+
*
|
|
41
|
+
* Precedence — country, then B2B, then the default — and the order is the
|
|
42
|
+
* point. A US dealer is a US shopper first: their state's sales tax is the
|
|
43
|
+
* reason the price cannot be quoted gross, and that reason does not go away
|
|
44
|
+
* because they also happen to be a trade account.
|
|
45
|
+
*
|
|
46
|
+
* This is a PREVIEW. `getTaxDisplayMode()` is what a storefront renders from;
|
|
47
|
+
* only the server knows the shopper's resolved country (active order shipping
|
|
48
|
+
* address, else their default shipping address).
|
|
49
|
+
*/
|
|
4
50
|
export declare function resolveTaxDisplayMode(policy: TaxDisplayPolicy, inputs: TaxDisplayInputs): TaxDisplayMode;
|
|
51
|
+
/**
|
|
52
|
+
* Read the policy off `activeChannel.customFields`. LENIENT by design: an
|
|
53
|
+
* unreadable value degrades to the pre-policy default rather than throwing
|
|
54
|
+
* while a catalogue page renders. The dashboard's write-side validation is what
|
|
55
|
+
* stops a bad value being stored.
|
|
56
|
+
*/
|
|
5
57
|
export declare function parseTaxDisplayPolicy(fields: unknown): TaxDisplayPolicy;
|
|
58
|
+
/**
|
|
59
|
+
* The tax-exclusive country list, normalised to upper-case alpha-2 with
|
|
60
|
+
* duplicates dropped. Anything unreadable is an EMPTY list — the rule is
|
|
61
|
+
* opt-in, so falling open means "no country override", never "every country".
|
|
62
|
+
*/
|
|
6
63
|
export declare function parseTaxExclusiveCountries(raw: unknown): readonly string[];
|
|
64
|
+
/**
|
|
65
|
+
* Map the Shop API enum (`GROSS` / `NET`, upper-case like `OrderScope`) to the
|
|
66
|
+
* SDK's lower-case union. Returns `null` for anything else — an engine that
|
|
67
|
+
* predates the enum, a null, a typo — so the caller can fall back to the
|
|
68
|
+
* channel storage mode instead of rendering a guess.
|
|
69
|
+
*/
|
|
7
70
|
export declare function taxDisplayModeFromEnum(value: unknown): TaxDisplayMode | null;
|
|
71
|
+
/**
|
|
72
|
+
* Identity fingerprint of the current request context — the cache key for a
|
|
73
|
+
* resolved mode.
|
|
74
|
+
*
|
|
75
|
+
* The mode is a function of WHO is asking: the session (`Authorization`) and
|
|
76
|
+
* the storefront scope (`x-order-scope`) are the two things the SDK can change
|
|
77
|
+
* mid-session that also change the answer. Sign in, sign out, or switch to the
|
|
78
|
+
* trade portal and the stamp changes, so the next read re-resolves instead of
|
|
79
|
+
* showing the previous shopper's mode.
|
|
80
|
+
*
|
|
81
|
+
* A country change is the one non-identity input that can flip the mode; it is
|
|
82
|
+
* handled by clearing the cache in `setShippingAddress`, since the stamp cannot
|
|
83
|
+
* see it.
|
|
84
|
+
*/
|
|
8
85
|
export declare function identityStamp(ctx: Pick<StoreContext, 'authToken' | 'orderScope'>): string;
|
|
9
86
|
//# sourceMappingURL=tax-display.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tax-display.d.ts","sourceRoot":"","sources":["../../src/pricing/tax-display.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"tax-display.d.ts","sourceRoot":"","sources":["../../src/pricing/tax-display.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,KAAK,EACV,YAAY,EAGZ,gBAAgB,EAChB,cAAc,EACd,gBAAgB,EACjB,MAAM,aAAa,CAAC;AAWrB,mEAAmE;AACnE,eAAO,MAAM,0BAA0B,EAAE,gBAIxC,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,uBAAuB,sEAInC,CAAC;AAaF;;;;;;;;;;;GAWG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,gBAAgB,EACxB,MAAM,EAAE,gBAAgB,GACvB,cAAc,CAShB;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,OAAO,GAAG,gBAAgB,CAWvE;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,OAAO,GAAG,SAAS,MAAM,EAAE,CAoB1E;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,OAAO,GAAG,cAAc,GAAG,IAAI,CAM5E;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,YAAY,EAAE,WAAW,GAAG,YAAY,CAAC,GAAG,MAAM,CAEzF"}
|
|
@@ -1,22 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which tax figure a shopper sees: incl. (`gross`) or excl. (`net`) tax.
|
|
3
|
+
*
|
|
4
|
+
* `Channel.pricesIncludeTax` is a STORAGE mode — it says which of `price` /
|
|
5
|
+
* `priceWithTax` the merchant typed in, not which one this shopper should be
|
|
6
|
+
* quoted. Those differ in real shops: US sales tax varies by state and even by
|
|
7
|
+
* district, so a US shopper must never be quoted a tax-inclusive price, and
|
|
8
|
+
* Dutch B2B buyers work in ex-VAT numbers, while Dutch consumers keep seeing
|
|
9
|
+
* the gross price the law requires. One channel, three audiences.
|
|
10
|
+
*
|
|
11
|
+
* So the engine resolves the mode per request and exposes it as
|
|
12
|
+
* `Query.activeTaxDisplayMode` — that query is the authority, and
|
|
13
|
+
* `client.pricing.getTaxDisplayMode()` is how a storefront reads it. The pure
|
|
14
|
+
* helpers below mirror the engine's rules
|
|
15
|
+
* (`services/vendure/src/tax-display/tax-display-policy.ts`) RULE FOR RULE so a
|
|
16
|
+
* client can preview a mode — a "who sees what" table, an SSR guess before the
|
|
17
|
+
* first round-trip — without one. Change one, change both.
|
|
18
|
+
*
|
|
19
|
+
* The channel policy behind it (three `public: true` Channel customFields, all
|
|
20
|
+
* defaulting to today's behaviour):
|
|
21
|
+
*
|
|
22
|
+
* taxDisplayDefault channel | gross | net (default `channel`)
|
|
23
|
+
* taxDisplayB2b inherit | gross | net (default `inherit`)
|
|
24
|
+
* taxExclusiveCountries JSON ["US"] (default empty)
|
|
25
|
+
*/
|
|
26
|
+
/** ISO-3166 alpha-2: exactly two ASCII letters. */
|
|
1
27
|
const ALPHA2 = /^[A-Za-z]{2}$/;
|
|
28
|
+
/** Channel-wide default. `channel` follows `Channel.pricesIncludeTax`. */
|
|
2
29
|
const TAX_DISPLAY_DEFAULTS = ['channel', 'gross', 'net'];
|
|
30
|
+
/** B2B override. `inherit` leaves B2B shoppers on the channel default. */
|
|
3
31
|
const TAX_DISPLAY_B2B_MODES = ['inherit', 'gross', 'net'];
|
|
32
|
+
/** Every channel reads this until a merchant changes something. */
|
|
4
33
|
export const DEFAULT_TAX_DISPLAY_POLICY = {
|
|
5
34
|
default: 'channel',
|
|
6
35
|
b2b: 'inherit',
|
|
7
36
|
countries: [],
|
|
8
37
|
};
|
|
38
|
+
/**
|
|
39
|
+
* The resolved mode for the active shopper. Public, anonymous-safe, and it
|
|
40
|
+
* never errors server-side — a lookup failure there falls back to the channel
|
|
41
|
+
* default rather than failing the request.
|
|
42
|
+
*
|
|
43
|
+
* The answer depends on the request's identity, so the SDK sends it with the
|
|
44
|
+
* same `Authorization` / `x-order-scope` headers as every other call.
|
|
45
|
+
*/
|
|
9
46
|
export const ACTIVE_TAX_DISPLAY_MODE = `
|
|
10
47
|
query ActiveTaxDisplayMode {
|
|
11
48
|
activeTaxDisplayMode
|
|
12
49
|
}
|
|
13
50
|
`;
|
|
51
|
+
/**
|
|
52
|
+
* Trim and upper-case one country code, or `null` when it is not a plausible
|
|
53
|
+
* alpha-2 code. Applied to both the stored list and the country being judged,
|
|
54
|
+
* so `"us"` and `" US "` can never disagree with `"US"`.
|
|
55
|
+
*/
|
|
14
56
|
function normalizeCountryCode(raw) {
|
|
15
57
|
if (typeof raw !== 'string')
|
|
16
58
|
return null;
|
|
17
59
|
const trimmed = raw.trim();
|
|
18
60
|
return ALPHA2.test(trimmed) ? trimmed.toUpperCase() : null;
|
|
19
61
|
}
|
|
62
|
+
/**
|
|
63
|
+
* Resolve the mode for one shopper — the engine's rule, offline.
|
|
64
|
+
*
|
|
65
|
+
* Precedence — country, then B2B, then the default — and the order is the
|
|
66
|
+
* point. A US dealer is a US shopper first: their state's sales tax is the
|
|
67
|
+
* reason the price cannot be quoted gross, and that reason does not go away
|
|
68
|
+
* because they also happen to be a trade account.
|
|
69
|
+
*
|
|
70
|
+
* This is a PREVIEW. `getTaxDisplayMode()` is what a storefront renders from;
|
|
71
|
+
* only the server knows the shopper's resolved country (active order shipping
|
|
72
|
+
* address, else their default shipping address).
|
|
73
|
+
*/
|
|
20
74
|
export function resolveTaxDisplayMode(policy, inputs) {
|
|
21
75
|
const country = normalizeCountryCode(inputs.countryCode);
|
|
22
76
|
if (country !== null && policy.countries.includes(country))
|
|
@@ -27,6 +81,12 @@ export function resolveTaxDisplayMode(policy, inputs) {
|
|
|
27
81
|
return policy.default;
|
|
28
82
|
return inputs.pricesIncludeTax ? 'gross' : 'net';
|
|
29
83
|
}
|
|
84
|
+
/**
|
|
85
|
+
* Read the policy off `activeChannel.customFields`. LENIENT by design: an
|
|
86
|
+
* unreadable value degrades to the pre-policy default rather than throwing
|
|
87
|
+
* while a catalogue page renders. The dashboard's write-side validation is what
|
|
88
|
+
* stops a bad value being stored.
|
|
89
|
+
*/
|
|
30
90
|
export function parseTaxDisplayPolicy(fields) {
|
|
31
91
|
const raw = (fields ?? {});
|
|
32
92
|
return {
|
|
@@ -35,6 +95,11 @@ export function parseTaxDisplayPolicy(fields) {
|
|
|
35
95
|
countries: parseTaxExclusiveCountries(raw.taxExclusiveCountries),
|
|
36
96
|
};
|
|
37
97
|
}
|
|
98
|
+
/**
|
|
99
|
+
* The tax-exclusive country list, normalised to upper-case alpha-2 with
|
|
100
|
+
* duplicates dropped. Anything unreadable is an EMPTY list — the rule is
|
|
101
|
+
* opt-in, so falling open means "no country override", never "every country".
|
|
102
|
+
*/
|
|
38
103
|
export function parseTaxExclusiveCountries(raw) {
|
|
39
104
|
if (typeof raw !== 'string')
|
|
40
105
|
return [];
|
|
@@ -59,6 +124,12 @@ export function parseTaxExclusiveCountries(raw) {
|
|
|
59
124
|
}
|
|
60
125
|
return codes;
|
|
61
126
|
}
|
|
127
|
+
/**
|
|
128
|
+
* Map the Shop API enum (`GROSS` / `NET`, upper-case like `OrderScope`) to the
|
|
129
|
+
* SDK's lower-case union. Returns `null` for anything else — an engine that
|
|
130
|
+
* predates the enum, a null, a typo — so the caller can fall back to the
|
|
131
|
+
* channel storage mode instead of rendering a guess.
|
|
132
|
+
*/
|
|
62
133
|
export function taxDisplayModeFromEnum(value) {
|
|
63
134
|
if (typeof value !== 'string')
|
|
64
135
|
return null;
|
|
@@ -69,6 +140,20 @@ export function taxDisplayModeFromEnum(value) {
|
|
|
69
140
|
return 'net';
|
|
70
141
|
return null;
|
|
71
142
|
}
|
|
143
|
+
/**
|
|
144
|
+
* Identity fingerprint of the current request context — the cache key for a
|
|
145
|
+
* resolved mode.
|
|
146
|
+
*
|
|
147
|
+
* The mode is a function of WHO is asking: the session (`Authorization`) and
|
|
148
|
+
* the storefront scope (`x-order-scope`) are the two things the SDK can change
|
|
149
|
+
* mid-session that also change the answer. Sign in, sign out, or switch to the
|
|
150
|
+
* trade portal and the stamp changes, so the next read re-resolves instead of
|
|
151
|
+
* showing the previous shopper's mode.
|
|
152
|
+
*
|
|
153
|
+
* A country change is the one non-identity input that can flip the mode; it is
|
|
154
|
+
* handled by clearing the cache in `setShippingAddress`, since the stamp cannot
|
|
155
|
+
* see it.
|
|
156
|
+
*/
|
|
72
157
|
export function identityStamp(ctx) {
|
|
73
158
|
return `${ctx.authToken ?? ''}|${ctx.orderScope ?? ''}`;
|
|
74
159
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tax-display.js","sourceRoot":"","sources":["../../src/pricing/tax-display.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"tax-display.js","sourceRoot":"","sources":["../../src/pricing/tax-display.ts"],"names":[],"mappings":"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"}
|
package/dist/pricing/tiers.d.ts
CHANGED
|
@@ -1,4 +1,13 @@
|
|
|
1
1
|
import type { PriceTier, TierTableEntry, VariantPriceData } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Find the active price tier for a given quantity.
|
|
4
|
+
* Tiers are matched by the highest minQuantity that does not exceed the given quantity.
|
|
5
|
+
* Returns null if no tiers or quantity is below all thresholds.
|
|
6
|
+
*/
|
|
2
7
|
export declare function getTierForQuantity(tiers: PriceTier[], quantity: number): PriceTier | null;
|
|
8
|
+
/**
|
|
9
|
+
* Build a tier table with savings percentages relative to the retail price.
|
|
10
|
+
* If no retail price is available, savings are computed relative to the variant's base price.
|
|
11
|
+
*/
|
|
3
12
|
export declare function computeTierTable(variant: VariantPriceData): TierTableEntry[];
|
|
4
13
|
//# sourceMappingURL=tiers.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tiers.d.ts","sourceRoot":"","sources":["../../src/pricing/tiers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"tiers.d.ts","sourceRoot":"","sources":["../../src/pricing/tiers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAE/E;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,SAAS,EAAE,EAAE,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAYzF;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,gBAAgB,GAAG,cAAc,EAAE,CAkB5E"}
|
package/dist/pricing/tiers.js
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Find the active price tier for a given quantity.
|
|
3
|
+
* Tiers are matched by the highest minQuantity that does not exceed the given quantity.
|
|
4
|
+
* Returns null if no tiers or quantity is below all thresholds.
|
|
5
|
+
*/
|
|
1
6
|
export function getTierForQuantity(tiers, quantity) {
|
|
2
7
|
if (!tiers.length)
|
|
3
8
|
return null;
|
|
@@ -11,6 +16,10 @@ export function getTierForQuantity(tiers, quantity) {
|
|
|
11
16
|
}
|
|
12
17
|
return matched;
|
|
13
18
|
}
|
|
19
|
+
/**
|
|
20
|
+
* Build a tier table with savings percentages relative to the retail price.
|
|
21
|
+
* If no retail price is available, savings are computed relative to the variant's base price.
|
|
22
|
+
*/
|
|
14
23
|
export function computeTierTable(variant) {
|
|
15
24
|
const tiers = variant.priceTiers;
|
|
16
25
|
if (!tiers?.length)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tiers.js","sourceRoot":"","sources":["../../src/pricing/tiers.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"tiers.js","sourceRoot":"","sources":["../../src/pricing/tiers.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAAkB,EAAE,QAAgB;IACrE,IAAI,CAAC,KAAK,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IAE/B,IAAI,OAAO,GAAqB,IAAI,CAAC;IACrC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,QAAQ,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACjC,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;gBACvD,OAAO,GAAG,IAAI,CAAC;YACjB,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,OAAyB;IACxD,MAAM,KAAK,GAAG,OAAO,CAAC,UAAU,CAAC;IACjC,IAAI,CAAC,KAAK,EAAE,MAAM;QAAE,OAAO,EAAE,CAAC;IAE9B,MAAM,cAAc,GAAG,OAAO,CAAC,WAAW,IAAI,OAAO,CAAC,KAAK,CAAC;IAC5D,IAAI,cAAc,IAAI,CAAC;QAAE,OAAO,EAAE,CAAC;IAEnC,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QAC1B,WAAW,EAAE,IAAI,CAAC,WAAW;QAC7B,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,YAAY,EAAE,IAAI,CAAC,YAAY;QAC/B,OAAO,EAAE,OAAO,CAAC,cAAc,EAAE,KAAK,IAAI,IAAI;QAC9C,aAAa,EAAE,IAAI,CAAC,aAAa;QACjC,iBAAiB,EACf,cAAc,GAAG,CAAC;YAChB,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,cAAc,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,cAAc,CAAC,GAAG,GAAG,CAAC;YACpE,CAAC,CAAC,IAAI;KACX,CAAC,CAAC,CAAC;AACN,CAAC"}
|
|
@@ -1,14 +1,69 @@
|
|
|
1
|
-
import type { OrderValidationResult } from '../types.js';
|
|
1
|
+
import type { OrderableStockInput, OrderValidationResult } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Validate an order against the channel's order minimums.
|
|
4
|
+
* Call before `transitionToArrangingPayment()` to provide early feedback.
|
|
5
|
+
*
|
|
6
|
+
* Pass the object from `getOrderConstraints()` straight through — its
|
|
7
|
+
* `minOrderAmountGross` / `minOrderQuantity` fields are read directly:
|
|
8
|
+
*
|
|
9
|
+
* ```ts
|
|
10
|
+
* const constraints = await client.checkout.getOrderConstraints();
|
|
11
|
+
* const result = client.pricing.validateOrderMinimums(order, constraints);
|
|
12
|
+
* ```
|
|
13
|
+
*
|
|
14
|
+
* The server enforces both minimums at the transition regardless, and its
|
|
15
|
+
* message may name the price list that binds ("Minimum order for Wholesale
|
|
16
|
+
* is …"), which the client cannot know. Display the server's message as-is on
|
|
17
|
+
* failure; these messages are for the pre-checkout shortfall hint.
|
|
18
|
+
*/
|
|
2
19
|
export declare function validateOrderMinimums(order: {
|
|
3
20
|
totalWithTax: number;
|
|
4
21
|
currencyCode: string;
|
|
5
22
|
totalQuantity?: number;
|
|
23
|
+
/**
|
|
24
|
+
* Net (tax-exclusive) total, minor units. Only needed when the channel
|
|
25
|
+
* returns `minOrderAmountNet`; `subTotal` is preferred over `total` because
|
|
26
|
+
* the server compares line totals, which exclude shipping.
|
|
27
|
+
*/
|
|
6
28
|
subTotal?: number;
|
|
7
29
|
total?: number;
|
|
8
30
|
}, constraints: {
|
|
31
|
+
/** Minimum vs the GROSS order total, minor units. As returned by `getOrderConstraints()`. */
|
|
9
32
|
minOrderAmountGross?: number | null;
|
|
33
|
+
/**
|
|
34
|
+
* Minimum vs the NET order total, minor units. Independent of the gross
|
|
35
|
+
* minimum — both bind. As returned by `getOrderConstraints()`.
|
|
36
|
+
*/
|
|
10
37
|
minOrderAmountNet?: number | null;
|
|
38
|
+
/** Minimum total item count. As returned by `getOrderConstraints()`. */
|
|
11
39
|
minOrderQuantity?: number | null;
|
|
40
|
+
/** @deprecated Use `minOrderAmountGross` — the field `getOrderConstraints()` actually returns. */
|
|
12
41
|
minOrderAmount?: number | null;
|
|
13
42
|
}): OrderValidationResult;
|
|
43
|
+
export type { OrderableStockInput };
|
|
44
|
+
/**
|
|
45
|
+
* How many units of a variant the current shopper may put in the cart:
|
|
46
|
+
* `max(0, saleable) + backorderAllowance`.
|
|
47
|
+
*
|
|
48
|
+
* Returns `null` when the channel does not expose stock (`saleable == null`) —
|
|
49
|
+
* the storefront cannot know a ceiling then and should let the server decide,
|
|
50
|
+
* exactly as before backorders existed. A `null` allowance counts as 0, so a
|
|
51
|
+
* shopper the policy does not admit gets plain `saleable`.
|
|
52
|
+
*/
|
|
53
|
+
export declare function maxOrderableQuantity(variant: OrderableStockInput): number | null;
|
|
54
|
+
/**
|
|
55
|
+
* Whether a variant is orderable only on backorder: nothing saleable, but an
|
|
56
|
+
* allowance. The signal to label "Backorder" instead of "Out of stock".
|
|
57
|
+
*/
|
|
58
|
+
export declare function isBackorderOnly(variant: OrderableStockInput): boolean;
|
|
59
|
+
/**
|
|
60
|
+
* Units of a cart line physical stock will not cover — the "N on backorder"
|
|
61
|
+
* hint BEFORE placement, when `backorderedQuantity` is still 0 — computed the
|
|
62
|
+
* way the server stamps it at placement: `max(0, quantity − max(0, saleable))`.
|
|
63
|
+
*
|
|
64
|
+
* `null` when there is nothing to say: the channel hides stock
|
|
65
|
+
* (`saleable == null` — never read that as 0, or every in-stock line would
|
|
66
|
+
* be "on backorder"), or stock covers the line. Pass the line's variant.
|
|
67
|
+
*/
|
|
68
|
+
export declare function backorderedQuantityForCart(variant: Pick<OrderableStockInput, 'saleable'>, quantity: number): number | null;
|
|
14
69
|
//# sourceMappingURL=validation.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../src/pricing/validation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAwB,qBAAqB,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../src/pricing/validation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAwB,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAGpG;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE;IACL,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,EACD,WAAW,EAAE;IACX,6FAA6F;IAC7F,mBAAmB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC;;;OAGG;IACH,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,wEAAwE;IACxE,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,kGAAkG;IAClG,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC,GACA,qBAAqB,CAgDvB;AAED,YAAY,EAAE,mBAAmB,EAAE,CAAC;AAEpC;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,mBAAmB,GAAG,MAAM,GAAG,IAAI,CAGhF;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAErE;AAED;;;;;;;;GAQG;AACH,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,IAAI,CAAC,mBAAmB,EAAE,UAAU,CAAC,EAC9C,QAAQ,EAAE,MAAM,GACf,MAAM,GAAG,IAAI,CAIf"}
|
|
@@ -1,4 +1,21 @@
|
|
|
1
1
|
import { formatPrice } from './format.js';
|
|
2
|
+
/**
|
|
3
|
+
* Validate an order against the channel's order minimums.
|
|
4
|
+
* Call before `transitionToArrangingPayment()` to provide early feedback.
|
|
5
|
+
*
|
|
6
|
+
* Pass the object from `getOrderConstraints()` straight through — its
|
|
7
|
+
* `minOrderAmountGross` / `minOrderQuantity` fields are read directly:
|
|
8
|
+
*
|
|
9
|
+
* ```ts
|
|
10
|
+
* const constraints = await client.checkout.getOrderConstraints();
|
|
11
|
+
* const result = client.pricing.validateOrderMinimums(order, constraints);
|
|
12
|
+
* ```
|
|
13
|
+
*
|
|
14
|
+
* The server enforces both minimums at the transition regardless, and its
|
|
15
|
+
* message may name the price list that binds ("Minimum order for Wholesale
|
|
16
|
+
* is …"), which the client cannot know. Display the server's message as-is on
|
|
17
|
+
* failure; these messages are for the pre-checkout shortfall hint.
|
|
18
|
+
*/
|
|
2
19
|
export function validateOrderMinimums(order, constraints) {
|
|
3
20
|
const errors = [];
|
|
4
21
|
const minAmount = constraints.minOrderAmountGross ?? constraints.minOrderAmount;
|
|
@@ -10,17 +27,25 @@ export function validateOrderMinimums(order, constraints) {
|
|
|
10
27
|
actual: order.totalWithTax,
|
|
11
28
|
});
|
|
12
29
|
}
|
|
30
|
+
// A net minimum is checked separately, never folded into the gross one: the
|
|
31
|
+
// two are different bases and converting between them needs a tax rate the
|
|
32
|
+
// client does not have. Both can fail on the same cart.
|
|
13
33
|
const minAmountNet = constraints.minOrderAmountNet;
|
|
14
34
|
const netTotal = order.subTotal ?? order.total;
|
|
15
35
|
if (minAmountNet != null && minAmountNet > 0 && netTotal != null && netTotal < minAmountNet) {
|
|
16
36
|
errors.push({
|
|
17
37
|
type: 'min_amount',
|
|
38
|
+
// Says "excl. tax" so the figure is not read against the gross total the
|
|
39
|
+
// buyer sees on screen, where it would look wrong.
|
|
18
40
|
message: `Minimum order amount is ${formatPrice(minAmountNet, order.currencyCode)} excl. tax`,
|
|
19
41
|
required: minAmountNet,
|
|
20
42
|
actual: netTotal,
|
|
21
43
|
});
|
|
22
44
|
}
|
|
23
45
|
const minQuantity = constraints.minOrderQuantity;
|
|
46
|
+
// `totalQuantity` is optional so a caller passing only totals still validates
|
|
47
|
+
// the amount. Skipping the quantity check when it is absent is correct;
|
|
48
|
+
// silently failing it against `undefined` would not be.
|
|
24
49
|
if (minQuantity != null &&
|
|
25
50
|
minQuantity > 0 &&
|
|
26
51
|
order.totalQuantity != null &&
|
|
@@ -34,4 +59,40 @@ export function validateOrderMinimums(order, constraints) {
|
|
|
34
59
|
}
|
|
35
60
|
return { valid: errors.length === 0, errors };
|
|
36
61
|
}
|
|
62
|
+
/**
|
|
63
|
+
* How many units of a variant the current shopper may put in the cart:
|
|
64
|
+
* `max(0, saleable) + backorderAllowance`.
|
|
65
|
+
*
|
|
66
|
+
* Returns `null` when the channel does not expose stock (`saleable == null`) —
|
|
67
|
+
* the storefront cannot know a ceiling then and should let the server decide,
|
|
68
|
+
* exactly as before backorders existed. A `null` allowance counts as 0, so a
|
|
69
|
+
* shopper the policy does not admit gets plain `saleable`.
|
|
70
|
+
*/
|
|
71
|
+
export function maxOrderableQuantity(variant) {
|
|
72
|
+
if (variant.saleable == null)
|
|
73
|
+
return null;
|
|
74
|
+
return Math.max(0, variant.saleable) + Math.max(0, variant.backorderAllowance ?? 0);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Whether a variant is orderable only on backorder: nothing saleable, but an
|
|
78
|
+
* allowance. The signal to label "Backorder" instead of "Out of stock".
|
|
79
|
+
*/
|
|
80
|
+
export function isBackorderOnly(variant) {
|
|
81
|
+
return (variant.saleable ?? 0) <= 0 && (variant.backorderAllowance ?? 0) > 0;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Units of a cart line physical stock will not cover — the "N on backorder"
|
|
85
|
+
* hint BEFORE placement, when `backorderedQuantity` is still 0 — computed the
|
|
86
|
+
* way the server stamps it at placement: `max(0, quantity − max(0, saleable))`.
|
|
87
|
+
*
|
|
88
|
+
* `null` when there is nothing to say: the channel hides stock
|
|
89
|
+
* (`saleable == null` — never read that as 0, or every in-stock line would
|
|
90
|
+
* be "on backorder"), or stock covers the line. Pass the line's variant.
|
|
91
|
+
*/
|
|
92
|
+
export function backorderedQuantityForCart(variant, quantity) {
|
|
93
|
+
if (variant.saleable == null)
|
|
94
|
+
return null;
|
|
95
|
+
const units = Math.max(0, quantity - Math.max(0, variant.saleable));
|
|
96
|
+
return units > 0 ? units : null;
|
|
97
|
+
}
|
|
37
98
|
//# sourceMappingURL=validation.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validation.js","sourceRoot":"","sources":["../../src/pricing/validation.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"validation.js","sourceRoot":"","sources":["../../src/pricing/validation.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { StoreContext } from '../types.js';
|
|
2
2
|
import type { RedirectStore, RedirectStoreConfig } from './types.js';
|
|
3
|
+
/** Wire a redirect store from an initialized client's context. */
|
|
3
4
|
export declare function createRedirectsModule(ctx: StoreContext, options?: Pick<RedirectStoreConfig, 'ttlMs' | 'timeoutMs' | 'onError'>): RedirectStore;
|
|
4
5
|
export { createRedirectStore, normalizeLookupPath } from './store.js';
|
|
5
6
|
export { REDIRECT_STATUS_CODES, type RedirectMatch, type RedirectRule, type RedirectSnapshot, type RedirectStatusCode, type RedirectStore, type RedirectStoreConfig, } from './types.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/redirects/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,OAAO,KAAK,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/redirects/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,OAAO,KAAK,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAErE,kEAAkE;AAClE,wBAAgB,qBAAqB,CACnC,GAAG,EAAE,YAAY,EACjB,OAAO,CAAC,EAAE,IAAI,CAAC,mBAAmB,EAAE,OAAO,GAAG,WAAW,GAAG,SAAS,CAAC,GACrE,aAAa,CAOf;AAED,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AACtE,OAAO,EACL,qBAAqB,EACrB,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,EACvB,KAAK,aAAa,EAClB,KAAK,mBAAmB,GACzB,MAAM,YAAY,CAAC"}
|
package/dist/redirects/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/redirects/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/redirects/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAGjD,kEAAkE;AAClE,MAAM,UAAU,qBAAqB,CACnC,GAAiB,EACjB,OAAsE;IAEtE,OAAO,mBAAmB,CAAC;QACzB,SAAS,EAAE,GAAG,CAAC,OAAO,CAAC,IAAI;QAC3B,UAAU,EAAE,GAAG,CAAC,UAAU;QAC1B,KAAK,EAAE,GAAG,CAAC,KAAK;QAChB,GAAG,OAAO;KACX,CAAC,CAAC;AACL,CAAC;AAED,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AACtE,OAAO,EACL,qBAAqB,GAOtB,MAAM,YAAY,CAAC"}
|
|
@@ -1,4 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-process redirect map for a storefront.
|
|
3
|
+
*
|
|
4
|
+
* Fetches the platform's public per-channel snapshot once per TTL and matches
|
|
5
|
+
* pathnames against it in memory, so a middleware costs a Map lookup rather
|
|
6
|
+
* than a network hop. Revalidation is ETag-conditional: an unchanged map comes
|
|
7
|
+
* back as a 304 with no body.
|
|
8
|
+
*
|
|
9
|
+
* Deliberately fail-open — a refresh failure keeps the last good map in service
|
|
10
|
+
* and reports through `onError` rather than throwing. A platform blip must not
|
|
11
|
+
* turn a tenant's working redirects into 404s.
|
|
12
|
+
*
|
|
13
|
+
* Only the FIRST load is awaited. Once a map exists, revalidation happens in the
|
|
14
|
+
* background and callers are served the current one: this runs in middleware
|
|
15
|
+
* ahead of every page render, so a slow platform must never become a slow store.
|
|
16
|
+
*/
|
|
1
17
|
import { type RedirectStore, type RedirectStoreConfig } from './types.js';
|
|
18
|
+
/**
|
|
19
|
+
* Match the platform's own source normalization: trailing slashes are stripped
|
|
20
|
+
* so `/a` and `/a/` are the same rule. The root path keeps its slash.
|
|
21
|
+
*/
|
|
2
22
|
export declare function normalizeLookupPath(pathname: string): string;
|
|
3
23
|
export declare function createRedirectStore(config: RedirectStoreConfig): RedirectStore;
|
|
4
24
|
//# sourceMappingURL=store.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/redirects/store.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/redirects/store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,EAML,KAAK,aAAa,EAClB,KAAK,mBAAmB,EACzB,MAAM,YAAY,CAAC;AAuBpB;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAe5D;AAsCD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,mBAAmB,GAAG,aAAa,CA+J9E"}
|