medusa-plugin-tax-lookup 0.1.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.
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ const widgetModule = { widgets: [] };
3
+ const routeModule = {
4
+ routes: []
5
+ };
6
+ const menuItemModule = {
7
+ menuItems: []
8
+ };
9
+ const formModule = { customFields: {} };
10
+ const displayModule = {
11
+ displays: {}
12
+ };
13
+ const i18nModule = { resources: {} };
14
+ const plugin = {
15
+ widgetModule,
16
+ routeModule,
17
+ menuItemModule,
18
+ formModule,
19
+ displayModule,
20
+ i18nModule
21
+ };
22
+ module.exports = plugin;
@@ -0,0 +1,23 @@
1
+ const widgetModule = { widgets: [] };
2
+ const routeModule = {
3
+ routes: []
4
+ };
5
+ const menuItemModule = {
6
+ menuItems: []
7
+ };
8
+ const formModule = { customFields: {} };
9
+ const displayModule = {
10
+ displays: {}
11
+ };
12
+ const i18nModule = { resources: {} };
13
+ const plugin = {
14
+ widgetModule,
15
+ routeModule,
16
+ menuItemModule,
17
+ formModule,
18
+ displayModule,
19
+ i18nModule
20
+ };
21
+ export {
22
+ plugin as default
23
+ };
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const utils_1 = require("@medusajs/framework/utils");
4
+ const provider_1 = require("./provider");
5
+ exports.default = (0, utils_1.ModuleProvider)(utils_1.Modules.TAX, {
6
+ services: [provider_1.TaxLookupProvider]
7
+ });
8
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9zcmMvcHJvdmlkZXJzL3RheC1sb29rdXAvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUFBQSxxREFBbUU7QUFDbkUseUNBQThDO0FBRTlDLGtCQUFlLElBQUEsc0JBQWMsRUFBQyxlQUFPLENBQUMsR0FBRyxFQUFFO0lBQzFDLFFBQVEsRUFBRSxDQUFDLDRCQUFpQixDQUFDO0NBQzdCLENBQUMsQ0FBQSJ9
@@ -0,0 +1,88 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.TaxLookupProvider = void 0;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const utils_1 = require("@medusajs/framework/utils");
10
+ class TaxLookupProvider {
11
+ constructor({ logger }, options) {
12
+ this.taxRates_ = {};
13
+ this.logger_ = logger;
14
+ if (!options?.dataDirectory) {
15
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, `[tax-lookup] dataDirectory is required`);
16
+ }
17
+ const dir = path_1.default.isAbsolute(options.dataDirectory)
18
+ ? options.dataDirectory
19
+ : path_1.default.resolve(process.cwd(), options.dataDirectory);
20
+ if (!fs_1.default.existsSync(dir)) {
21
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, `[tax-lookup] dataDirectory does not exist: ${dir}`);
22
+ }
23
+ const csvFiles = fs_1.default.readdirSync(dir).filter((f) => f.endsWith('.csv'));
24
+ if (csvFiles.length === 0) {
25
+ this.logger_.warn(`[tax-lookup] no CSV files found in ${dir}`);
26
+ }
27
+ for (const file of csvFiles) {
28
+ const filePath = path_1.default.join(dir, file);
29
+ const content = fs_1.default.readFileSync(filePath, 'utf-8');
30
+ const lines = content.split('\n').filter((l) => l.trim());
31
+ if (lines.length === 0)
32
+ continue;
33
+ const headers = lines[0].split(',').map((h) => h.trim().toLowerCase());
34
+ const zipIdx = headers.indexOf('zipcode');
35
+ const rateIdx = headers.indexOf('estimatedcombinedrate');
36
+ if (zipIdx === -1 || rateIdx === -1) {
37
+ this.logger_.warn(`[tax-lookup] skipping ${file} — missing ZipCode or EstimatedCombinedRate column`);
38
+ continue;
39
+ }
40
+ let count = 0;
41
+ for (let i = 1; i < lines.length; i++) {
42
+ const cols = lines[i].split(',');
43
+ const zip = cols[zipIdx]?.trim();
44
+ const rate = parseFloat(cols[rateIdx]?.trim());
45
+ if (!zip || isNaN(rate))
46
+ continue;
47
+ // Store rate as percentage (e.g. 0.0975 -> 9.75)
48
+ this.taxRates_[zip] = rate < 1 ? rate * 100 : rate;
49
+ count++;
50
+ }
51
+ this.logger_.info(`[tax-lookup] loaded ${count} rates from ${file}`);
52
+ }
53
+ }
54
+ getIdentifier() {
55
+ return TaxLookupProvider.identifier;
56
+ }
57
+ async getTaxLines(itemLines, shippingLines, context) {
58
+ const { address } = context;
59
+ const zip = address?.postal_code?.trim().slice(0, 5);
60
+ if (!zip) {
61
+ return [];
62
+ }
63
+ const rate = this.taxRates_[zip] ?? 0;
64
+ const result = [];
65
+ for (const { line_item } of itemLines) {
66
+ result.push({
67
+ line_item_id: line_item.id,
68
+ rate,
69
+ name: 'Sales Tax',
70
+ code: `TAX-${zip}`,
71
+ provider_id: this.getIdentifier()
72
+ });
73
+ }
74
+ for (const { shipping_line } of shippingLines) {
75
+ result.push({
76
+ shipping_line_id: shipping_line.id,
77
+ rate,
78
+ name: 'Sales Tax',
79
+ code: `TAX-${zip}`,
80
+ provider_id: this.getIdentifier()
81
+ });
82
+ }
83
+ return result;
84
+ }
85
+ }
86
+ exports.TaxLookupProvider = TaxLookupProvider;
87
+ TaxLookupProvider.identifier = 'tax-lookup';
88
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHJvdmlkZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9zcmMvcHJvdmlkZXJzL3RheC1sb29rdXAvcHJvdmlkZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7O0FBQUEsNENBQW1CO0FBQ25CLGdEQUF1QjtBQUd2QixxREFBdUQ7QUFNdkQsTUFBYSxpQkFBaUI7SUFLN0IsWUFBWSxFQUFFLE1BQU0sRUFBc0IsRUFBRSxPQUF5QjtRQUYzRCxjQUFTLEdBQTJCLEVBQUUsQ0FBQTtRQUcvQyxJQUFJLENBQUMsT0FBTyxHQUFHLE1BQU0sQ0FBQTtRQUVyQixJQUFJLENBQUMsT0FBTyxFQUFFLGFBQWEsRUFBRSxDQUFDO1lBQzdCLE1BQU0sSUFBSSxtQkFBVyxDQUNwQixtQkFBVyxDQUFDLEtBQUssQ0FBQyxZQUFZLEVBQzlCLHdDQUF3QyxDQUN4QyxDQUFBO1FBQ0YsQ0FBQztRQUVELE1BQU0sR0FBRyxHQUFHLGNBQUksQ0FBQyxVQUFVLENBQUMsT0FBTyxDQUFDLGFBQWEsQ0FBQztZQUNqRCxDQUFDLENBQUMsT0FBTyxDQUFDLGFBQWE7WUFDdkIsQ0FBQyxDQUFDLGNBQUksQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFDLEdBQUcsRUFBRSxFQUFFLE9BQU8sQ0FBQyxhQUFhLENBQUMsQ0FBQTtRQUVyRCxJQUFJLENBQUMsWUFBRSxDQUFDLFVBQVUsQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDO1lBQ3pCLE1BQU0sSUFBSSxtQkFBVyxDQUNwQixtQkFBVyxDQUFDLEtBQUssQ0FBQyxZQUFZLEVBQzlCLDhDQUE4QyxHQUFHLEVBQUUsQ0FDbkQsQ0FBQTtRQUNGLENBQUM7UUFFRCxNQUFNLFFBQVEsR0FBRyxZQUFFLENBQUMsV0FBVyxDQUFDLEdBQUcsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFBO1FBRXRFLElB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
package/README.md ADDED
@@ -0,0 +1,75 @@
1
+ # medusa-plugin-tax-lookup
2
+
3
+ Tax provider for Medusa that looks up tax rates by ZIP code from CSV files.
4
+
5
+ If you are not familiar with Medusa, you can learn more on [the project web site](https://www.medusajs.com/).
6
+
7
+ ## Features
8
+
9
+ - Loads tax rates from CSV files at startup
10
+ - Looks up tax rates by 5-digit ZIP code
11
+ - Supports multiple CSV files in a single directory
12
+
13
+ ## Installation
14
+
15
+ Inside your medusa backend root folder:
16
+
17
+ ```bash
18
+ yarn add medusa-plugin-tax-lookup
19
+ ```
20
+
21
+ Replace "yarn add" with the correct command for your package manager if you are using (for example) npm, pnpm, or bun.
22
+
23
+ ## Configuration
24
+
25
+ Enable in your medusa-config.ts file. Example:
26
+
27
+ ```ts
28
+ module.exports = defineConfig({
29
+ //... other config
30
+ modules: [
31
+ {
32
+ resolve: '@medusajs/medusa/tax',
33
+ options: {
34
+ providers: [
35
+ {
36
+ resolve: 'medusa-plugin-tax-lookup',
37
+ id: 'tax-lookup',
38
+ options: {
39
+ dataDirectory: './tax-data'
40
+ }
41
+ }
42
+ ]
43
+ }
44
+ }
45
+ // ... other modules
46
+ ]
47
+ })
48
+ ```
49
+
50
+ ### Options
51
+
52
+ | Option | Type | Required | Description |
53
+ | --------------- | -------- | -------- | --------------------------------------------------------------------------------------------------- |
54
+ | `dataDirectory` | `string` | Yes | Path to the directory containing CSV files. Can be absolute or relative to the Medusa project root. |
55
+
56
+ ## CSV Format
57
+
58
+ Place one or more CSV files in the configured `dataDirectory`. Each file must have `ZipCode` and `EstimatedCombinedRate` columns. All other columns are ignored.
59
+
60
+ ```csv
61
+ ZipCode,EstimatedCombinedRate
62
+ 90001,0.105
63
+ 90002,0.1075
64
+ 90003,0.0975
65
+ ```
66
+
67
+ Rates can be provided as decimals (e.g. `0.0975`) or percentages (e.g. `9.75`). Decimal values less than 1 are automatically converted to percentages.
68
+
69
+ If multiple CSV files contain the same ZIP code, the last file loaded wins.
70
+
71
+ ## Data Sources
72
+
73
+ The column names the tax provider is configured to use are based on the format of csv files downloaded from Avalara: https://www.avalara.com/taxrates/en/download-tax-tables.html.
74
+
75
+ However, you can use any spreadsheet you want. Just change the name of the rate column in your data source to `EstimatedCombinedRate` and the postal code column to `ZipCode`.
package/package.json ADDED
@@ -0,0 +1,72 @@
1
+ {
2
+ "name": "medusa-plugin-tax-lookup",
3
+ "version": "0.1.0",
4
+ "description": "Tax rate lookup provider for Medusa v2",
5
+ "author": "Lacey Pevey",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/pevey/medusa-plugins"
10
+ },
11
+ "homepage": "https://pevey.com/medusa-plugin-tax-lookup",
12
+ "files": [
13
+ ".medusa/server"
14
+ ],
15
+ "exports": {
16
+ "./package.json": "./package.json",
17
+ "./.medusa/server/src/modules/*": "./.medusa/server/src/modules/*/index.js",
18
+ "./modules/*": "./.medusa/server/src/modules/*/index.js",
19
+ "./providers/*": "./.medusa/server/src/providers/*/index.js",
20
+ "./*": "./.medusa/server/src/*.js",
21
+ ".": "./.medusa/server/src/providers/tax-lookup/index.js"
22
+ },
23
+ "keywords": [
24
+ "medusa-plugin-tax",
25
+ "medusa-v2",
26
+ "medusa",
27
+ "tax",
28
+ "tax-lookup",
29
+ "tax provider"
30
+ ],
31
+ "scripts": {
32
+ "build": "medusa plugin:build",
33
+ "dev": "medusa plugin:develop",
34
+ "prepublishOnly": "medusa plugin:build",
35
+ "test:unit": "TEST_TYPE=unit NODE_OPTIONS=--experimental-vm-modules jest --silent --runInBand --detectOpenHandles --forceExit --passWithNoTests"
36
+ },
37
+ "devDependencies": {
38
+ "@medusajs/admin-sdk": "2.13.6",
39
+ "@medusajs/cli": "2.13.6",
40
+ "@medusajs/framework": "2.13.6",
41
+ "@medusajs/icons": "2.13.6",
42
+ "@medusajs/medusa": "2.13.6",
43
+ "@medusajs/test-utils": "2.13.6",
44
+ "@medusajs/ui": "4.0.25",
45
+ "@swc/core": "^1.7.28",
46
+ "@swc/jest": "^0.2.39",
47
+ "@types/jest": "^30.0.0",
48
+ "@types/node": "^20.0.0",
49
+ "@types/react": "^18.3.2",
50
+ "@types/react-dom": "^18.2.25",
51
+ "jest": "^29.7.0",
52
+ "prop-types": "^15.8.1",
53
+ "react": "^18.2.0",
54
+ "react-dom": "^18.2.0",
55
+ "ts-node": "^10.9.2",
56
+ "typescript": "^5.6.2",
57
+ "vite": "^5.2.11",
58
+ "yalc": "^1.0.0-pre.53"
59
+ },
60
+ "peerDependencies": {
61
+ "@medusajs/admin-sdk": "2.13.6",
62
+ "@medusajs/cli": "2.13.6",
63
+ "@medusajs/framework": "2.13.6",
64
+ "@medusajs/icons": "2.13.6",
65
+ "@medusajs/medusa": "2.13.6",
66
+ "@medusajs/test-utils": "2.13.6",
67
+ "@medusajs/ui": "4.0.25"
68
+ },
69
+ "engines": {
70
+ "node": ">=20"
71
+ }
72
+ }