@unchainedshop/plugins 5.0.0-alpha.2 → 5.0.0-alpha.3
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/lib/events/aws-eventbridge.d.ts +7 -1
- package/lib/events/aws-eventbridge.js +5 -11
- package/lib/events/redis.d.ts +3 -1
- package/lib/events/redis.js +5 -5
- package/lib/files/gridfs/api.js +1 -1
- package/lib/files/gridfs/handler-express.js +1 -1
- package/lib/payment/datatrans-v2/adapter.js +12 -9
- package/lib/payment/datatrans-v2/build3DSCardholder.d.ts +12 -0
- package/lib/payment/datatrans-v2/build3DSCardholder.js +44 -0
- package/lib/payment/paypal-checkout.js +17 -5
- package/lib/payment/stripe/stripe.js +1 -1
- package/lib/presets/base.js +4 -0
- package/lib/presets/countries/eu.d.ts +1 -0
- package/lib/presets/countries/eu.js +7 -0
- package/lib/presets/countries/uk.d.ts +1 -0
- package/lib/presets/countries/uk.js +7 -0
- package/lib/presets/countries/us.d.ts +1 -0
- package/lib/presets/countries/us.js +7 -0
- package/lib/pricing/delivery-eu-tax/adapter.d.ts +3 -0
- package/lib/pricing/delivery-eu-tax/adapter.js +55 -0
- package/lib/pricing/delivery-eu-tax/index.d.ts +4 -0
- package/lib/pricing/delivery-eu-tax/index.js +10 -0
- package/lib/pricing/delivery-eu-tax.d.ts +2 -0
- package/lib/pricing/delivery-eu-tax.js +55 -0
- package/lib/pricing/delivery-swiss-tax/adapter.js +7 -26
- package/lib/pricing/delivery-swiss-tax.js +7 -26
- package/lib/pricing/delivery-uk-tax/adapter.d.ts +3 -0
- package/lib/pricing/delivery-uk-tax/adapter.js +43 -0
- package/lib/pricing/delivery-uk-tax/index.d.ts +4 -0
- package/lib/pricing/delivery-uk-tax/index.js +10 -0
- package/lib/pricing/delivery-uk-tax.d.ts +2 -0
- package/lib/pricing/delivery-uk-tax.js +43 -0
- package/lib/pricing/delivery-us-sales-tax/adapter.d.ts +3 -0
- package/lib/pricing/delivery-us-sales-tax/adapter.js +58 -0
- package/lib/pricing/delivery-us-sales-tax/index.d.ts +4 -0
- package/lib/pricing/delivery-us-sales-tax/index.js +10 -0
- package/lib/pricing/delivery-us-sales-tax.d.ts +2 -0
- package/lib/pricing/delivery-us-sales-tax.js +58 -0
- package/lib/pricing/product-eu-tax/adapter.d.ts +3 -0
- package/lib/pricing/product-eu-tax/adapter.js +56 -0
- package/lib/pricing/product-eu-tax/index.d.ts +4 -0
- package/lib/pricing/product-eu-tax/index.js +10 -0
- package/lib/pricing/product-eu-tax.d.ts +2 -0
- package/lib/pricing/product-eu-tax.js +56 -0
- package/lib/pricing/product-quotation-price.d.ts +2 -0
- package/lib/pricing/product-quotation-price.js +40 -0
- package/lib/pricing/product-swiss-tax/adapter.js +7 -26
- package/lib/pricing/product-swiss-tax.js +7 -26
- package/lib/pricing/product-uk-tax/adapter.d.ts +3 -0
- package/lib/pricing/product-uk-tax/adapter.js +57 -0
- package/lib/pricing/product-uk-tax/index.d.ts +4 -0
- package/lib/pricing/product-uk-tax/index.js +10 -0
- package/lib/pricing/product-uk-tax.d.ts +2 -0
- package/lib/pricing/product-uk-tax.js +57 -0
- package/lib/pricing/product-us-sales-tax/adapter.d.ts +3 -0
- package/lib/pricing/product-us-sales-tax/adapter.js +52 -0
- package/lib/pricing/product-us-sales-tax/index.d.ts +4 -0
- package/lib/pricing/product-us-sales-tax/index.js +10 -0
- package/lib/pricing/product-us-sales-tax.d.ts +2 -0
- package/lib/pricing/product-us-sales-tax.js +52 -0
- package/lib/pricing/tax/applyTaxRateToTaxableRows.d.ts +23 -0
- package/lib/pricing/tax/applyTaxRateToTaxableRows.js +30 -0
- package/lib/pricing/tax/ch-tax-rates.json +24 -0
- package/lib/pricing/tax/ch.js +6 -30
- package/lib/pricing/tax/eraRates.d.ts +10 -0
- package/lib/pricing/tax/eraRates.js +15 -0
- package/lib/pricing/tax/eu-tax-rates.json +797 -0
- package/lib/pricing/tax/eu.d.ts +12 -0
- package/lib/pricing/tax/eu.js +38 -0
- package/lib/pricing/tax/uk-tax-rates.json +18 -0
- package/lib/pricing/tax/uk.d.ts +10 -0
- package/lib/pricing/tax/uk.js +23 -0
- package/lib/pricing/tax/us-tax-rates.json +334 -0
- package/lib/pricing/tax/us.d.ts +11 -0
- package/lib/pricing/tax/us.js +20 -0
- package/lib/pricing/utils/isDeliveryAddressInCountry.d.ts +1 -1
- package/lib/pricing/utils/isDeliveryAddressInCountry.js +3 -8
- package/lib/pricing/utils/resolveDeliveryLocation.d.ts +10 -0
- package/lib/pricing/utils/resolveDeliveryLocation.js +9 -0
- package/lib/quotations/manual/adapter.js +6 -1
- package/lib/quotations/manual.js +6 -1
- package/lib/worker/gc-guests/adapter.d.ts +9 -0
- package/lib/worker/gc-guests/adapter.js +56 -0
- package/lib/worker/gc-guests/index.d.ts +4 -0
- package/lib/worker/gc-guests/index.js +13 -0
- package/lib/worker/gc-guests.d.ts +8 -0
- package/lib/worker/gc-guests.js +55 -0
- package/lib/worker/invalidate-carts/adapter.d.ts +9 -0
- package/lib/worker/invalidate-carts/adapter.js +52 -0
- package/lib/worker/invalidate-carts/index.d.ts +4 -0
- package/lib/worker/invalidate-carts/index.js +13 -0
- package/lib/worker/invalidate-carts.d.ts +8 -0
- package/lib/worker/invalidate-carts.js +51 -0
- package/lib/worker/zombie-killer/adapter.d.ts +2 -0
- package/lib/worker/zombie-killer/adapter.js +26 -3
- package/lib/worker/zombie-killer/index.js +4 -1
- package/lib/worker/zombie-killer.d.ts +1 -0
- package/lib/worker/zombie-killer.js +24 -3
- package/package.json +24 -7
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { DeliveryProvider } from '@unchainedshop/core-delivery';
|
|
2
|
+
import type { Product } from '@unchainedshop/core-products';
|
|
3
|
+
export declare const EU_TAX_CATEGORY_TAG_PREFIX = "eu-tax-category:";
|
|
4
|
+
export declare const EU_MEMBER_COUNTRY_CODES: string[];
|
|
5
|
+
export declare const isEuMemberCountry: (countryCode?: string | null) => boolean;
|
|
6
|
+
export declare const resolveEuTaxRate: ({ countryCode, category, referenceDate, }: {
|
|
7
|
+
countryCode?: string | null;
|
|
8
|
+
category?: string | null;
|
|
9
|
+
referenceDate?: Date;
|
|
10
|
+
}) => number | null;
|
|
11
|
+
export declare const resolveEuTaxCategoryFromProduct: (product: Product) => string | null;
|
|
12
|
+
export declare const resolveEuTaxCategoryFromDeliveryProvider: (provider: DeliveryProvider) => string | null;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import euTaxRates from './eu-tax-rates.json' with { type: 'json' };
|
|
2
|
+
import { compileEraRates, rateForDate } from "./eraRates.js";
|
|
3
|
+
export const EU_TAX_CATEGORY_TAG_PREFIX = 'eu-tax-category:';
|
|
4
|
+
const PROVIDER_CONFIGURATION_KEY = 'eu-tax-category';
|
|
5
|
+
const countryRates = Object.fromEntries(Object.entries(euTaxRates.countries).map(([countryCode, categories]) => [
|
|
6
|
+
countryCode,
|
|
7
|
+
Object.fromEntries(Object.entries(categories).map(([category, eras]) => [
|
|
8
|
+
category,
|
|
9
|
+
rateForDate(compileEraRates(eras, euTaxRates.timezone)),
|
|
10
|
+
])),
|
|
11
|
+
]));
|
|
12
|
+
const normalizeCountryCode = (countryCode) => {
|
|
13
|
+
const normalized = countryCode?.toUpperCase().trim() || null;
|
|
14
|
+
return normalized === 'EL' ? 'GR' : normalized;
|
|
15
|
+
};
|
|
16
|
+
export const EU_MEMBER_COUNTRY_CODES = Object.keys(countryRates);
|
|
17
|
+
export const isEuMemberCountry = (countryCode) => {
|
|
18
|
+
const normalized = normalizeCountryCode(countryCode);
|
|
19
|
+
return Boolean(normalized && countryRates[normalized]);
|
|
20
|
+
};
|
|
21
|
+
export const resolveEuTaxRate = ({ countryCode, category, referenceDate, }) => {
|
|
22
|
+
const normalized = normalizeCountryCode(countryCode);
|
|
23
|
+
const categories = normalized ? countryRates[normalized] : null;
|
|
24
|
+
if (!categories)
|
|
25
|
+
return null;
|
|
26
|
+
const resolveRate = (category && categories[category]) || categories.standard;
|
|
27
|
+
return resolveRate(referenceDate);
|
|
28
|
+
};
|
|
29
|
+
export const resolveEuTaxCategoryFromProduct = (product) => {
|
|
30
|
+
const categoryTag = product?.tags?.find((tag) => tag?.trim().toLowerCase().startsWith(EU_TAX_CATEGORY_TAG_PREFIX));
|
|
31
|
+
if (!categoryTag)
|
|
32
|
+
return null;
|
|
33
|
+
return categoryTag.trim().toLowerCase().slice(EU_TAX_CATEGORY_TAG_PREFIX.length) || null;
|
|
34
|
+
};
|
|
35
|
+
export const resolveEuTaxCategoryFromDeliveryProvider = (provider) => {
|
|
36
|
+
const value = provider?.configuration?.find(({ key }) => key === PROVIDER_CONFIGURATION_KEY)?.value;
|
|
37
|
+
return value?.trim().toLowerCase() || null;
|
|
38
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"source": "https://www.gov.uk/vat-rates",
|
|
3
|
+
"verifiedAt": "2026-08-20",
|
|
4
|
+
"timezone": "+00:00",
|
|
5
|
+
"categories": {
|
|
6
|
+
"standard": [
|
|
7
|
+
{ "validFrom": "1991-04-01", "rate": 0.175 },
|
|
8
|
+
{ "validFrom": "2008-12-01", "rate": 0.15 },
|
|
9
|
+
{ "validFrom": "2010-01-01", "rate": 0.175 },
|
|
10
|
+
{ "validFrom": "2011-01-04", "rate": 0.2 }
|
|
11
|
+
],
|
|
12
|
+
"reduced": [
|
|
13
|
+
{ "validFrom": "1994-04-01", "rate": 0.08 },
|
|
14
|
+
{ "validFrom": "1997-09-01", "rate": 0.05 }
|
|
15
|
+
],
|
|
16
|
+
"zero": [{ "validFrom": "1973-04-01", "rate": 0 }]
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { DeliveryProvider } from '@unchainedshop/core-delivery';
|
|
2
|
+
import type { Product } from '@unchainedshop/core-products';
|
|
3
|
+
export declare const UK_VAT_COUNTRY_CODES: string[];
|
|
4
|
+
export interface UkTaxCategoryResolver {
|
|
5
|
+
value: string;
|
|
6
|
+
rate: (referenceDate?: Date) => number;
|
|
7
|
+
}
|
|
8
|
+
export declare const UkTaxCategories: Record<string, UkTaxCategoryResolver>;
|
|
9
|
+
export declare const resolveUkTaxCategoryFromDeliveryProvider: (provider: DeliveryProvider) => UkTaxCategoryResolver | null;
|
|
10
|
+
export declare const resolveUkTaxCategoryFromProduct: (product: Product) => UkTaxCategoryResolver | null;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import ukTaxRates from './uk-tax-rates.json' with { type: 'json' };
|
|
2
|
+
import { compileEraRates, rateForDate } from "./eraRates.js";
|
|
3
|
+
export const UK_VAT_COUNTRY_CODES = ['GB', 'IM'];
|
|
4
|
+
export const UkTaxCategories = Object.fromEntries(Object.entries(ukTaxRates.categories).map(([value, eras]) => [
|
|
5
|
+
value.toUpperCase(),
|
|
6
|
+
{ value, rate: rateForDate(compileEraRates(eras, ukTaxRates.timezone)) },
|
|
7
|
+
]));
|
|
8
|
+
export const resolveUkTaxCategoryFromDeliveryProvider = (provider) => {
|
|
9
|
+
const taxCategoryFromProvider = provider?.configuration
|
|
10
|
+
?.find(({ key }) => {
|
|
11
|
+
if (key === 'uk-tax-category')
|
|
12
|
+
return true;
|
|
13
|
+
return null;
|
|
14
|
+
})
|
|
15
|
+
?.value?.toUpperCase();
|
|
16
|
+
const taxCategory = taxCategoryFromProvider ? UkTaxCategories[taxCategoryFromProvider] : null;
|
|
17
|
+
return taxCategory;
|
|
18
|
+
};
|
|
19
|
+
export const resolveUkTaxCategoryFromProduct = (product) => {
|
|
20
|
+
const productSpecialTaxTag = product?.tags?.find((tag) => tag?.trim().toLowerCase().startsWith('uk-tax-category:'));
|
|
21
|
+
const taxCategory = Object.values(UkTaxCategories).find((t) => `uk-tax-category:${t.value}` === productSpecialTaxTag?.trim().toLowerCase());
|
|
22
|
+
return taxCategory || null;
|
|
23
|
+
};
|
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
{
|
|
2
|
+
"source": "https://taxfoundation.org/data/all/state/2026-sales-tax-rates-midyear/",
|
|
3
|
+
"verifiedAt": "2026-08-20",
|
|
4
|
+
"timezone": "-06:00",
|
|
5
|
+
"states": {
|
|
6
|
+
"AK": [
|
|
7
|
+
{
|
|
8
|
+
"validFrom": "2026-07-01",
|
|
9
|
+
"rate": 0.0
|
|
10
|
+
}
|
|
11
|
+
],
|
|
12
|
+
"AL": [
|
|
13
|
+
{
|
|
14
|
+
"validFrom": "2026-07-01",
|
|
15
|
+
"rate": 0.04
|
|
16
|
+
}
|
|
17
|
+
],
|
|
18
|
+
"AR": [
|
|
19
|
+
{
|
|
20
|
+
"validFrom": "2026-07-01",
|
|
21
|
+
"rate": 0.065
|
|
22
|
+
}
|
|
23
|
+
],
|
|
24
|
+
"AZ": [
|
|
25
|
+
{
|
|
26
|
+
"validFrom": "2026-07-01",
|
|
27
|
+
"rate": 0.056
|
|
28
|
+
}
|
|
29
|
+
],
|
|
30
|
+
"CA": [
|
|
31
|
+
{
|
|
32
|
+
"validFrom": "2026-07-01",
|
|
33
|
+
"rate": 0.0725,
|
|
34
|
+
"note": "6.0% state + 1.25% mandatory statewide local"
|
|
35
|
+
}
|
|
36
|
+
],
|
|
37
|
+
"CO": [
|
|
38
|
+
{
|
|
39
|
+
"validFrom": "2026-07-01",
|
|
40
|
+
"rate": 0.029
|
|
41
|
+
}
|
|
42
|
+
],
|
|
43
|
+
"CT": [
|
|
44
|
+
{
|
|
45
|
+
"validFrom": "2026-07-01",
|
|
46
|
+
"rate": 0.0635
|
|
47
|
+
}
|
|
48
|
+
],
|
|
49
|
+
"DC": [
|
|
50
|
+
{
|
|
51
|
+
"validFrom": "2026-07-01",
|
|
52
|
+
"rate": 0.06
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"validFrom": "2026-10-01",
|
|
56
|
+
"rate": 0.07,
|
|
57
|
+
"note": "enacted increase per DC OTR notice"
|
|
58
|
+
}
|
|
59
|
+
],
|
|
60
|
+
"DE": [
|
|
61
|
+
{
|
|
62
|
+
"validFrom": "2026-07-01",
|
|
63
|
+
"rate": 0.0
|
|
64
|
+
}
|
|
65
|
+
],
|
|
66
|
+
"FL": [
|
|
67
|
+
{
|
|
68
|
+
"validFrom": "2026-07-01",
|
|
69
|
+
"rate": 0.06
|
|
70
|
+
}
|
|
71
|
+
],
|
|
72
|
+
"GA": [
|
|
73
|
+
{
|
|
74
|
+
"validFrom": "2026-07-01",
|
|
75
|
+
"rate": 0.04
|
|
76
|
+
}
|
|
77
|
+
],
|
|
78
|
+
"HI": [
|
|
79
|
+
{
|
|
80
|
+
"validFrom": "2026-07-01",
|
|
81
|
+
"rate": 0.04,
|
|
82
|
+
"note": "general excise tax (GET)"
|
|
83
|
+
}
|
|
84
|
+
],
|
|
85
|
+
"IA": [
|
|
86
|
+
{
|
|
87
|
+
"validFrom": "2026-07-01",
|
|
88
|
+
"rate": 0.06
|
|
89
|
+
}
|
|
90
|
+
],
|
|
91
|
+
"ID": [
|
|
92
|
+
{
|
|
93
|
+
"validFrom": "2026-07-01",
|
|
94
|
+
"rate": 0.06
|
|
95
|
+
}
|
|
96
|
+
],
|
|
97
|
+
"IL": [
|
|
98
|
+
{
|
|
99
|
+
"validFrom": "2026-07-01",
|
|
100
|
+
"rate": 0.0625
|
|
101
|
+
}
|
|
102
|
+
],
|
|
103
|
+
"IN": [
|
|
104
|
+
{
|
|
105
|
+
"validFrom": "2026-07-01",
|
|
106
|
+
"rate": 0.07
|
|
107
|
+
}
|
|
108
|
+
],
|
|
109
|
+
"KS": [
|
|
110
|
+
{
|
|
111
|
+
"validFrom": "2026-07-01",
|
|
112
|
+
"rate": 0.065
|
|
113
|
+
}
|
|
114
|
+
],
|
|
115
|
+
"KY": [
|
|
116
|
+
{
|
|
117
|
+
"validFrom": "2026-07-01",
|
|
118
|
+
"rate": 0.06
|
|
119
|
+
}
|
|
120
|
+
],
|
|
121
|
+
"LA": [
|
|
122
|
+
{
|
|
123
|
+
"validFrom": "2018-07-01",
|
|
124
|
+
"rate": 0.0445
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
"validFrom": "2025-01-01",
|
|
128
|
+
"rate": 0.05,
|
|
129
|
+
"note": "Act 11 (2024); scheduled to drop to 4.75% from 2030"
|
|
130
|
+
}
|
|
131
|
+
],
|
|
132
|
+
"MA": [
|
|
133
|
+
{
|
|
134
|
+
"validFrom": "2026-07-01",
|
|
135
|
+
"rate": 0.0625
|
|
136
|
+
}
|
|
137
|
+
],
|
|
138
|
+
"MD": [
|
|
139
|
+
{
|
|
140
|
+
"validFrom": "2026-07-01",
|
|
141
|
+
"rate": 0.06
|
|
142
|
+
}
|
|
143
|
+
],
|
|
144
|
+
"ME": [
|
|
145
|
+
{
|
|
146
|
+
"validFrom": "2026-07-01",
|
|
147
|
+
"rate": 0.055
|
|
148
|
+
}
|
|
149
|
+
],
|
|
150
|
+
"MI": [
|
|
151
|
+
{
|
|
152
|
+
"validFrom": "2026-07-01",
|
|
153
|
+
"rate": 0.06
|
|
154
|
+
}
|
|
155
|
+
],
|
|
156
|
+
"MN": [
|
|
157
|
+
{
|
|
158
|
+
"validFrom": "2026-07-01",
|
|
159
|
+
"rate": 0.06875
|
|
160
|
+
}
|
|
161
|
+
],
|
|
162
|
+
"MO": [
|
|
163
|
+
{
|
|
164
|
+
"validFrom": "2026-07-01",
|
|
165
|
+
"rate": 0.04225
|
|
166
|
+
}
|
|
167
|
+
],
|
|
168
|
+
"MS": [
|
|
169
|
+
{
|
|
170
|
+
"validFrom": "2026-07-01",
|
|
171
|
+
"rate": 0.07
|
|
172
|
+
}
|
|
173
|
+
],
|
|
174
|
+
"MT": [
|
|
175
|
+
{
|
|
176
|
+
"validFrom": "2026-07-01",
|
|
177
|
+
"rate": 0.0
|
|
178
|
+
}
|
|
179
|
+
],
|
|
180
|
+
"NC": [
|
|
181
|
+
{
|
|
182
|
+
"validFrom": "2026-07-01",
|
|
183
|
+
"rate": 0.0475
|
|
184
|
+
}
|
|
185
|
+
],
|
|
186
|
+
"ND": [
|
|
187
|
+
{
|
|
188
|
+
"validFrom": "2026-07-01",
|
|
189
|
+
"rate": 0.05
|
|
190
|
+
}
|
|
191
|
+
],
|
|
192
|
+
"NE": [
|
|
193
|
+
{
|
|
194
|
+
"validFrom": "2026-07-01",
|
|
195
|
+
"rate": 0.055
|
|
196
|
+
}
|
|
197
|
+
],
|
|
198
|
+
"NH": [
|
|
199
|
+
{
|
|
200
|
+
"validFrom": "2026-07-01",
|
|
201
|
+
"rate": 0.0
|
|
202
|
+
}
|
|
203
|
+
],
|
|
204
|
+
"NJ": [
|
|
205
|
+
{
|
|
206
|
+
"validFrom": "2026-07-01",
|
|
207
|
+
"rate": 0.06625
|
|
208
|
+
}
|
|
209
|
+
],
|
|
210
|
+
"NM": [
|
|
211
|
+
{
|
|
212
|
+
"validFrom": "2022-07-01",
|
|
213
|
+
"rate": 0.05
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
"validFrom": "2023-07-01",
|
|
217
|
+
"rate": 0.04875,
|
|
218
|
+
"note": "gross receipts tax (HB 163, 2022)"
|
|
219
|
+
}
|
|
220
|
+
],
|
|
221
|
+
"NV": [
|
|
222
|
+
{
|
|
223
|
+
"validFrom": "2026-07-01",
|
|
224
|
+
"rate": 0.0685,
|
|
225
|
+
"note": "includes mandatory statewide-levied components"
|
|
226
|
+
}
|
|
227
|
+
],
|
|
228
|
+
"NY": [
|
|
229
|
+
{
|
|
230
|
+
"validFrom": "2026-07-01",
|
|
231
|
+
"rate": 0.04
|
|
232
|
+
}
|
|
233
|
+
],
|
|
234
|
+
"OH": [
|
|
235
|
+
{
|
|
236
|
+
"validFrom": "2026-07-01",
|
|
237
|
+
"rate": 0.0575
|
|
238
|
+
}
|
|
239
|
+
],
|
|
240
|
+
"OK": [
|
|
241
|
+
{
|
|
242
|
+
"validFrom": "2026-07-01",
|
|
243
|
+
"rate": 0.045
|
|
244
|
+
}
|
|
245
|
+
],
|
|
246
|
+
"OR": [
|
|
247
|
+
{
|
|
248
|
+
"validFrom": "2026-07-01",
|
|
249
|
+
"rate": 0.0
|
|
250
|
+
}
|
|
251
|
+
],
|
|
252
|
+
"PA": [
|
|
253
|
+
{
|
|
254
|
+
"validFrom": "2026-07-01",
|
|
255
|
+
"rate": 0.06
|
|
256
|
+
}
|
|
257
|
+
],
|
|
258
|
+
"RI": [
|
|
259
|
+
{
|
|
260
|
+
"validFrom": "2026-07-01",
|
|
261
|
+
"rate": 0.07
|
|
262
|
+
}
|
|
263
|
+
],
|
|
264
|
+
"SC": [
|
|
265
|
+
{
|
|
266
|
+
"validFrom": "2026-07-01",
|
|
267
|
+
"rate": 0.06
|
|
268
|
+
}
|
|
269
|
+
],
|
|
270
|
+
"SD": [
|
|
271
|
+
{
|
|
272
|
+
"validFrom": "2023-07-01",
|
|
273
|
+
"rate": 0.042,
|
|
274
|
+
"note": "HB 1137 temporary cut; reverts to 4.5% on 2027-07-01 unless extended"
|
|
275
|
+
}
|
|
276
|
+
],
|
|
277
|
+
"TN": [
|
|
278
|
+
{
|
|
279
|
+
"validFrom": "2026-07-01",
|
|
280
|
+
"rate": 0.07
|
|
281
|
+
}
|
|
282
|
+
],
|
|
283
|
+
"TX": [
|
|
284
|
+
{
|
|
285
|
+
"validFrom": "2026-07-01",
|
|
286
|
+
"rate": 0.0625
|
|
287
|
+
}
|
|
288
|
+
],
|
|
289
|
+
"UT": [
|
|
290
|
+
{
|
|
291
|
+
"validFrom": "2026-07-01",
|
|
292
|
+
"rate": 0.061,
|
|
293
|
+
"note": "includes 1.25% mandatory statewide local add-on"
|
|
294
|
+
}
|
|
295
|
+
],
|
|
296
|
+
"VA": [
|
|
297
|
+
{
|
|
298
|
+
"validFrom": "2026-07-01",
|
|
299
|
+
"rate": 0.053,
|
|
300
|
+
"note": "4.3% state + 1% mandatory statewide local"
|
|
301
|
+
}
|
|
302
|
+
],
|
|
303
|
+
"VT": [
|
|
304
|
+
{
|
|
305
|
+
"validFrom": "2026-07-01",
|
|
306
|
+
"rate": 0.06
|
|
307
|
+
}
|
|
308
|
+
],
|
|
309
|
+
"WA": [
|
|
310
|
+
{
|
|
311
|
+
"validFrom": "2026-07-01",
|
|
312
|
+
"rate": 0.065
|
|
313
|
+
}
|
|
314
|
+
],
|
|
315
|
+
"WI": [
|
|
316
|
+
{
|
|
317
|
+
"validFrom": "2026-07-01",
|
|
318
|
+
"rate": 0.05
|
|
319
|
+
}
|
|
320
|
+
],
|
|
321
|
+
"WV": [
|
|
322
|
+
{
|
|
323
|
+
"validFrom": "2026-07-01",
|
|
324
|
+
"rate": 0.06
|
|
325
|
+
}
|
|
326
|
+
],
|
|
327
|
+
"WY": [
|
|
328
|
+
{
|
|
329
|
+
"validFrom": "2026-07-01",
|
|
330
|
+
"rate": 0.04
|
|
331
|
+
}
|
|
332
|
+
]
|
|
333
|
+
}
|
|
334
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { DeliveryProvider } from '@unchainedshop/core-delivery';
|
|
2
|
+
import type { Product } from '@unchainedshop/core-products';
|
|
3
|
+
export declare const US_COUNTRY_CODE = "US";
|
|
4
|
+
export declare const US_SALES_TAX_EXEMPT_VALUE = "exempt";
|
|
5
|
+
export declare const US_STATE_CODES: string[];
|
|
6
|
+
export declare const resolveUsSalesTaxRate: ({ regionCode, referenceDate, }: {
|
|
7
|
+
regionCode?: string | null;
|
|
8
|
+
referenceDate?: Date;
|
|
9
|
+
}) => number | null;
|
|
10
|
+
export declare const isProductExemptFromUsSalesTax: (product: Product) => boolean;
|
|
11
|
+
export declare const isDeliveryExemptFromUsSalesTax: (provider: DeliveryProvider) => boolean;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import usTaxRates from './us-tax-rates.json' with { type: 'json' };
|
|
2
|
+
import { compileEraRates, rateForDate } from "./eraRates.js";
|
|
3
|
+
export const US_COUNTRY_CODE = 'US';
|
|
4
|
+
export const US_SALES_TAX_EXEMPT_VALUE = 'exempt';
|
|
5
|
+
const stateRates = Object.fromEntries(Object.entries(usTaxRates.states).map(([stateCode, eras]) => [
|
|
6
|
+
stateCode,
|
|
7
|
+
rateForDate(compileEraRates(eras, usTaxRates.timezone)),
|
|
8
|
+
]));
|
|
9
|
+
export const US_STATE_CODES = Object.keys(stateRates);
|
|
10
|
+
export const resolveUsSalesTaxRate = ({ regionCode, referenceDate, }) => {
|
|
11
|
+
const resolveRate = regionCode ? stateRates[regionCode.toUpperCase().trim()] : null;
|
|
12
|
+
if (!resolveRate)
|
|
13
|
+
return null;
|
|
14
|
+
return resolveRate(referenceDate);
|
|
15
|
+
};
|
|
16
|
+
export const isProductExemptFromUsSalesTax = (product) => Boolean(product?.tags?.some((tag) => tag?.trim().toLowerCase() === `us-tax-category:${US_SALES_TAX_EXEMPT_VALUE}`));
|
|
17
|
+
export const isDeliveryExemptFromUsSalesTax = (provider) => provider?.configuration
|
|
18
|
+
?.find(({ key }) => key === 'us-tax-category')
|
|
19
|
+
?.value?.trim()
|
|
20
|
+
.toLowerCase() === US_SALES_TAX_EXEMPT_VALUE;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Order, OrderDelivery } from '@unchainedshop/core-orders';
|
|
2
|
-
export default function isDeliveryAddressInCountry(
|
|
2
|
+
export default function isDeliveryAddressInCountry(params: {
|
|
3
3
|
orderDelivery?: OrderDelivery | null;
|
|
4
4
|
order?: Order | null;
|
|
5
5
|
countryCode?: string | null;
|
|
@@ -1,11 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const address = orderDelivery?.context?.address || order?.billingAddress;
|
|
5
|
-
if (address?.countryCode > '') {
|
|
6
|
-
countryCode = address.countryCode?.toUpperCase().trim();
|
|
7
|
-
}
|
|
8
|
-
}
|
|
1
|
+
import resolveDeliveryLocation from "./resolveDeliveryLocation.js";
|
|
2
|
+
export default function isDeliveryAddressInCountry(params, allowedCountryCodes) {
|
|
3
|
+
const { countryCode } = resolveDeliveryLocation(params);
|
|
9
4
|
if (!countryCode)
|
|
10
5
|
return false;
|
|
11
6
|
return allowedCountryCodes.includes(countryCode);
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Order, OrderDelivery } from '@unchainedshop/core-orders';
|
|
2
|
+
export interface DeliveryLocation {
|
|
3
|
+
countryCode: string | null;
|
|
4
|
+
regionCode: string | null;
|
|
5
|
+
}
|
|
6
|
+
export default function resolveDeliveryLocation({ orderDelivery, order, countryCode: forceCountryCode, }: {
|
|
7
|
+
orderDelivery?: OrderDelivery | null;
|
|
8
|
+
order?: Order | null;
|
|
9
|
+
countryCode?: string | null;
|
|
10
|
+
}): DeliveryLocation;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export default function resolveDeliveryLocation({ orderDelivery, order, countryCode: forceCountryCode = null, }) {
|
|
2
|
+
const address = orderDelivery?.context?.address || order?.billingAddress;
|
|
3
|
+
let countryCode = forceCountryCode?.toUpperCase().trim() || order?.countryCode || null;
|
|
4
|
+
if (address?.countryCode && address.countryCode > '') {
|
|
5
|
+
countryCode = address.countryCode.toUpperCase().trim();
|
|
6
|
+
}
|
|
7
|
+
const regionCode = address?.regionCode?.toUpperCase().trim() || null;
|
|
8
|
+
return { countryCode, regionCode };
|
|
9
|
+
}
|
|
@@ -10,8 +10,13 @@ export const ManualOffering = {
|
|
|
10
10
|
return {
|
|
11
11
|
...QuotationAdapter.actions(params),
|
|
12
12
|
quote: async () => {
|
|
13
|
+
const { quotation } = params;
|
|
14
|
+
const price = Number(quotation?.context?.price);
|
|
13
15
|
return {
|
|
14
|
-
expires:
|
|
16
|
+
expires: quotation?.context?.expires
|
|
17
|
+
? new Date(quotation.context.expires)
|
|
18
|
+
: new Date(new Date().getTime() + 3600 * 1000),
|
|
19
|
+
price: Number.isFinite(price) && price > 0 ? Math.round(price) : undefined,
|
|
15
20
|
};
|
|
16
21
|
},
|
|
17
22
|
};
|
package/lib/quotations/manual.js
CHANGED
|
@@ -10,8 +10,13 @@ const ManualOffering = {
|
|
|
10
10
|
return {
|
|
11
11
|
...QuotationAdapter.actions(params),
|
|
12
12
|
quote: async () => {
|
|
13
|
+
const { quotation } = params;
|
|
14
|
+
const price = Number(quotation?.context?.price);
|
|
13
15
|
return {
|
|
14
|
-
expires:
|
|
16
|
+
expires: quotation?.context?.expires
|
|
17
|
+
? new Date(quotation.context.expires)
|
|
18
|
+
: new Date(new Date().getTime() + 3600 * 1000),
|
|
19
|
+
price: Number.isFinite(price) && price > 0 ? Math.round(price) : undefined,
|
|
15
20
|
};
|
|
16
21
|
},
|
|
17
22
|
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type IWorkerAdapter } from '@unchainedshop/core';
|
|
2
|
+
export declare const GCGuestsWorker: IWorkerAdapter<{
|
|
3
|
+
guestUserMaxAgeInDays?: number;
|
|
4
|
+
}, {
|
|
5
|
+
scannedGuestCount: number;
|
|
6
|
+
deletedGuestCount: number;
|
|
7
|
+
}>;
|
|
8
|
+
export default GCGuestsWorker;
|
|
9
|
+
export declare const configureGCGuestsAutoscheduling: () => void;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { WorkerAdapter, WorkerDirector, schedule } from '@unchainedshop/core';
|
|
2
|
+
import { userSettings } from '@unchainedshop/core-users';
|
|
3
|
+
import { createLogger } from '@unchainedshop/logger';
|
|
4
|
+
const logger = createLogger('unchained:worker:gc-guests');
|
|
5
|
+
const ONE_DAY_IN_MILLISECONDS = 86400000;
|
|
6
|
+
const everyDayAtHalfPastTwo = schedule.parse.cron('30 2 * * *');
|
|
7
|
+
export const GCGuestsWorker = {
|
|
8
|
+
...WorkerAdapter,
|
|
9
|
+
key: 'shop.unchained.worker-plugin.gc-guests',
|
|
10
|
+
label: 'Garbage Collect Guests',
|
|
11
|
+
version: '1.0.0',
|
|
12
|
+
type: 'GC_GUESTS',
|
|
13
|
+
doWork: async ({ guestUserMaxAgeInDays } = {}, unchainedAPI) => {
|
|
14
|
+
const { modules, services } = unchainedAPI;
|
|
15
|
+
try {
|
|
16
|
+
const maxAgeInDays = guestUserMaxAgeInDays ?? userSettings.guestUserMaxAgeInDays;
|
|
17
|
+
const before = new Date(Date.now() - maxAgeInDays * ONE_DAY_IN_MILLISECONDS);
|
|
18
|
+
const userIdsToDelete = await modules.users.findGuestUserIds({ before });
|
|
19
|
+
let deletedGuestCount = 0;
|
|
20
|
+
await Array.fromAsync(userIdsToDelete, async (userId) => {
|
|
21
|
+
try {
|
|
22
|
+
await services.users.deleteUser({ userId });
|
|
23
|
+
deletedGuestCount += 1;
|
|
24
|
+
}
|
|
25
|
+
catch (err) {
|
|
26
|
+
logger.warn(`Failed to garbage-collect guest ${userId}: ${err.message}`);
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
return {
|
|
30
|
+
success: true,
|
|
31
|
+
result: {
|
|
32
|
+
scannedGuestCount: userIdsToDelete.length,
|
|
33
|
+
deletedGuestCount,
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
catch (err) {
|
|
38
|
+
return {
|
|
39
|
+
success: false,
|
|
40
|
+
error: {
|
|
41
|
+
name: err.name,
|
|
42
|
+
message: err.message,
|
|
43
|
+
stack: err.stack,
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
export default GCGuestsWorker;
|
|
50
|
+
export const configureGCGuestsAutoscheduling = () => {
|
|
51
|
+
WorkerDirector.configureAutoscheduling({
|
|
52
|
+
type: GCGuestsWorker.type,
|
|
53
|
+
schedule: everyDayAtHalfPastTwo,
|
|
54
|
+
retries: 0,
|
|
55
|
+
});
|
|
56
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import {} from '@unchainedshop/core';
|
|
2
|
+
import { GCGuestsWorker, configureGCGuestsAutoscheduling } from "./adapter.js";
|
|
3
|
+
export const GCGuestsPlugin = {
|
|
4
|
+
key: 'shop.unchained.worker-plugin.gc-guests',
|
|
5
|
+
label: 'Garbage Collect Guests Worker Plugin',
|
|
6
|
+
version: '1.0.0',
|
|
7
|
+
adapters: [GCGuestsWorker],
|
|
8
|
+
onRegister: () => {
|
|
9
|
+
configureGCGuestsAutoscheduling();
|
|
10
|
+
},
|
|
11
|
+
};
|
|
12
|
+
export default GCGuestsPlugin;
|
|
13
|
+
export * from "./adapter.js";
|