From 07e5c17f8600ec8667d612c10fa0254b0c4add3c Mon Sep 17 00:00:00 2001 From: Stevche Radevski Date: Thu, 16 May 2024 15:06:21 +0200 Subject: [PATCH 1/3] chore: Remove medusa interfaces package (#7347) --- integration-tests/api/package.json | 1 - integration-tests/modules/package.json | 1 - packages/cli/medusa-dev-cli/README.md | 4 +- packages/medusa-interfaces/.babelrc | 13 - packages/medusa-interfaces/.gitignore | 7 - packages/medusa-interfaces/CHANGELOG.md | 339 ------------------ packages/medusa-interfaces/package.json | 43 --- .../src/__tests__/base-service.js | 66 ---- .../medusa-interfaces/src/base-service.js | 174 --------- .../medusa-interfaces/src/file-service.js | 28 -- .../src/fulfillment-service.js | 117 ------ packages/medusa-interfaces/src/index.js | 7 - .../src/notification-service.js | 35 -- .../medusa-interfaces/src/oauth-service.js | 31 -- .../medusa-interfaces/src/payment-service.js | 85 ----- .../medusa-interfaces/src/search-service.js | 107 ------ packages/medusa-interfaces/tsconfig.json | 28 -- packages/medusa/index.d.ts | 1 - packages/medusa/package.json | 4 - yarn.lock | 45 +-- 20 files changed, 11 insertions(+), 1125 deletions(-) delete mode 100644 packages/medusa-interfaces/.babelrc delete mode 100644 packages/medusa-interfaces/.gitignore delete mode 100644 packages/medusa-interfaces/CHANGELOG.md delete mode 100644 packages/medusa-interfaces/package.json delete mode 100644 packages/medusa-interfaces/src/__tests__/base-service.js delete mode 100644 packages/medusa-interfaces/src/base-service.js delete mode 100644 packages/medusa-interfaces/src/file-service.js delete mode 100644 packages/medusa-interfaces/src/fulfillment-service.js delete mode 100644 packages/medusa-interfaces/src/index.js delete mode 100644 packages/medusa-interfaces/src/notification-service.js delete mode 100644 packages/medusa-interfaces/src/oauth-service.js delete mode 100644 packages/medusa-interfaces/src/payment-service.js delete mode 100644 packages/medusa-interfaces/src/search-service.js delete mode 100644 packages/medusa-interfaces/tsconfig.json delete mode 100644 packages/medusa/index.d.ts diff --git a/integration-tests/api/package.json b/integration-tests/api/package.json index 3f9099df56..c708f88845 100644 --- a/integration-tests/api/package.json +++ b/integration-tests/api/package.json @@ -28,7 +28,6 @@ "@medusajs/utils": "workspace:^", "@medusajs/workflow-engine-inmemory": "workspace:*", "faker": "^5.5.3", - "medusa-interfaces": "workspace:*", "pg": "^8.11.0", "typeorm": "^0.3.16" }, diff --git a/integration-tests/modules/package.json b/integration-tests/modules/package.json index 16f61a8f31..f4b236441c 100644 --- a/integration-tests/modules/package.json +++ b/integration-tests/modules/package.json @@ -31,7 +31,6 @@ "@medusajs/utils": "workspace:^", "@medusajs/workflow-engine-inmemory": "workspace:*", "faker": "^5.5.3", - "medusa-interfaces": "workspace:*", "medusa-test-utils": "workspace:*", "pg": "^8.11.0", "typeorm": "^0.3.16" diff --git a/packages/cli/medusa-dev-cli/README.md b/packages/cli/medusa-dev-cli/README.md index b30a83954c..4cdbfb38fb 100644 --- a/packages/cli/medusa-dev-cli/README.md +++ b/packages/cli/medusa-dev-cli/README.md @@ -52,13 +52,13 @@ git checkout package.json; npm install --force You can prevent the automatic dependencies scan and instead specify a list of packages you want to link by using the `--packages` option: -`medusa-dev --packages @medusajs/medusa medusa-interfaces` +`medusa-dev --packages @medusajs/medusa` #### `--scan-once` With this flag, the tool will do an initial scan and copy and then quit. This is useful for setting up automated testing/builds of Medusa projects from the latest -code. +code. #### `--quiet` diff --git a/packages/medusa-interfaces/.babelrc b/packages/medusa-interfaces/.babelrc deleted file mode 100644 index 41dc577de6..0000000000 --- a/packages/medusa-interfaces/.babelrc +++ /dev/null @@ -1,13 +0,0 @@ -{ - "plugins": [ - "@babel/plugin-proposal-class-properties", - "@babel/plugin-transform-instanceof", - ["@babel/plugin-transform-runtime", { "regenerator": true }] - ], - "presets": ["@babel/preset-env"], - "env": { - "test": { - "plugins": ["@babel/plugin-transform-runtime"] - } - } -} \ No newline at end of file diff --git a/packages/medusa-interfaces/.gitignore b/packages/medusa-interfaces/.gitignore deleted file mode 100644 index aacc207d2e..0000000000 --- a/packages/medusa-interfaces/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -dist/ -node_modules/ -.DS_store -.env* -/*.js -!index.js -yarn.lock diff --git a/packages/medusa-interfaces/CHANGELOG.md b/packages/medusa-interfaces/CHANGELOG.md deleted file mode 100644 index 6ecc283bdb..0000000000 --- a/packages/medusa-interfaces/CHANGELOG.md +++ /dev/null @@ -1,339 +0,0 @@ -# Change Log - -## 1.3.9 - -### Patch Changes - -- [#6700](https://github.com/medusajs/medusa/pull/6700) [`8f8a4f9b13`](https://github.com/medusajs/medusa/commit/8f8a4f9b1353087d98f6cc75346d43a7f49901a8) Thanks [@olivermrbl](https://github.com/olivermrbl)! - chore: Version all modules to allow for initial testing - -## 1.3.8 - -### Patch Changes - -- [#5869](https://github.com/medusajs/medusa/pull/5869) [`45996d58a2`](https://github.com/medusajs/medusa/commit/45996d58a2665d72335faad11bea958f8da74195) Thanks [@adrien2p](https://github.com/adrien2p)! - chore(medusa, interfaces, utils, webshiper): Uniformise class checks - -## 1.3.7 - -### Patch Changes - -- [#3041](https://github.com/medusajs/medusa/pull/3041) [`121b42acf`](https://github.com/medusajs/medusa/commit/121b42acfe98c12dd593f9b1f2072ff0f3b61724) Thanks [@riqwan](https://github.com/riqwan)! - chore(medusa): Typeorm fixes / enhancements - - upgrade typeorm from 0.2.51 to 0.3.11 - - Plugin repository loader to work with Typeorm update - -## 1.3.7-rc.0 - -### Patch Changes - -- [#3041](https://github.com/medusajs/medusa/pull/3041) [`121b42acf`](https://github.com/medusajs/medusa/commit/121b42acfe98c12dd593f9b1f2072ff0f3b61724) Thanks [@riqwan](https://github.com/riqwan)! - chore(medusa): Typeorm fixes / enhancements - - upgrade typeorm from 0.2.51 to 0.3.11 - - Plugin repository loader to work with Typeorm update - -## 1.3.6 - -### Patch Changes - -- [#3217](https://github.com/medusajs/medusa/pull/3217) [`8c5219a31`](https://github.com/medusajs/medusa/commit/8c5219a31ef76ee571fbce84d7d57a63abe56eb0) Thanks [@adrien2p](https://github.com/adrien2p)! - chore: Fix npm packages files included - -- Updated dependencies [[`8c5219a31`](https://github.com/medusajs/medusa/commit/8c5219a31ef76ee571fbce84d7d57a63abe56eb0)]: - - medusa-core-utils@1.1.39 - -## 1.3.5 - -### Patch Changes - -- [#3185](https://github.com/medusajs/medusa/pull/3185) [`08324355a`](https://github.com/medusajs/medusa/commit/08324355a4466b017a0bc7ab1d333ee3cd27b8c4) Thanks [@olivermrbl](https://github.com/olivermrbl)! - chore: Patches all dependencies + minor bumps `winston` to include a [fix for a significant memory leak](https://github.com/winstonjs/winston/pull/2057) - -- Updated dependencies [[`08324355a`](https://github.com/medusajs/medusa/commit/08324355a4466b017a0bc7ab1d333ee3cd27b8c4)]: - - medusa-core-utils@1.1.38 - -## 1.3.4 - -### Patch Changes - -- [#3025](https://github.com/medusajs/medusa/pull/3025) [`93d0dc1bd`](https://github.com/medusajs/medusa/commit/93d0dc1bdcb54cf6e87428a7bb9b0dac196b4de2) Thanks [@adrien2p](https://github.com/adrien2p)! - fix(medusa): test, build and watch scripts - -## 1.3.3 - -### Patch Changes - -- [#1962](https://github.com/medusajs/medusa/pull/1962) [`c97ccd3fb`](https://github.com/medusajs/medusa/commit/c97ccd3fb5dbe796b0e4fbf37def5bb6e8201557) Thanks [@pKorsholm](https://github.com/pKorsholm)! - Convert FulfillmentService to TypeScript - -## 1.3.2 - -### Patch Changes - -- [#1914](https://github.com/medusajs/medusa/pull/1914) [`1dec44287`](https://github.com/medusajs/medusa/commit/1dec44287df5ac69b4c5769b59f9ebef58d3da68) Thanks [@fPolic](https://github.com/fPolic)! - Version bump due to missing changesets in merged PRs - -* [#1874](https://github.com/medusajs/medusa/pull/1874) [`b8ddb31f6`](https://github.com/medusajs/medusa/commit/b8ddb31f6fe296a11d2d988276ba8e991c37fa9b) Thanks [@adrien2p](https://github.com/adrien2p)! - Move search indexing into a separate subscriber to defer the work load - -All notable changes to this project will be documented in this file. -See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. - -## [1.3.1](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.3.0...medusa-interfaces@1.3.1) (2022-07-05) - -### Bug Fixes - -- **medusa:** Remove deps `mongoose` + `mongodb` ([#1218](https://github.com/medusajs/medusa/issues/1218)) ([c76e23e](https://github.com/medusajs/medusa/commit/c76e23e84dd8cb08c3c709f9f95c4c17b9685439)) - -# [1.3.0](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.2.1...medusa-interfaces@1.3.0) (2022-05-01) - -### Bug Fixes - -- ensures no duplicate tax lines when completing cart ([#1262](https://github.com/medusajs/medusa/issues/1262)) ([607a382](https://github.com/medusajs/medusa/commit/607a382b4ee190c25eafa345674b55b74a7d6349)) - -## [1.2.1](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.1.34...medusa-interfaces@1.2.1) (2022-02-28) - -### Bug Fixes - -- atomic phase error handler ([#1104](https://github.com/medusajs/medusa/issues/1104)) ([f983cfa](https://github.com/medusajs/medusa/commit/f983cfada675b9c2ad89f1dea37f862673383f54)) - -# [1.2.0](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.1.34...medusa-interfaces@1.2.0) (2022-02-25) - -### Bug Fixes - -- atomic phase error handler ([#1104](https://github.com/medusajs/medusa/issues/1104)) ([62c263c](https://github.com/medusajs/medusa/commit/62c263c36080541023fda5ae33a458c58cbaeb1e)) - -## [1.1.34](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.1.33...medusa-interfaces@1.1.34) (2022-02-06) - -### Bug Fixes - -- release ([fc3fbc8](https://github.com/medusajs/medusa/commit/fc3fbc897fad5c8a5d3eea828ac7277fba9d70af)) - -## [1.1.33](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.1.32...medusa-interfaces@1.1.33) (2022-02-06) - -**Note:** Version bump only for package medusa-interfaces - -## [1.1.32](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.1.31...medusa-interfaces@1.1.32) (2021-12-08) - -**Note:** Version bump only for package medusa-interfaces - -## [1.1.31](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.1.30...medusa-interfaces@1.1.31) (2021-11-23) - -**Note:** Version bump only for package medusa-interfaces - -## [1.1.30](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.1.29...medusa-interfaces@1.1.30) (2021-11-22) - -**Note:** Version bump only for package medusa-interfaces - -## [1.1.29](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.1.28...medusa-interfaces@1.1.29) (2021-11-19) - -**Note:** Version bump only for package medusa-interfaces - -## [1.1.28](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.1.27...medusa-interfaces@1.1.28) (2021-11-19) - -### Features - -- Allow retrieval of soft-deleted products ([#723](https://github.com/medusajs/medusa/issues/723)) ([1e50aee](https://github.com/medusajs/medusa/commit/1e50aee4feb55092560dd4a9c51a0671363e8576)) -- Typescript for API layer ([#817](https://github.com/medusajs/medusa/issues/817)) ([373532e](https://github.com/medusajs/medusa/commit/373532ecbc8196f47e71af95a8cf82a14a4b1f9e)) - -## [1.1.27](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.1.26...medusa-interfaces@1.1.27) (2021-10-18) - -**Note:** Version bump only for package medusa-interfaces - -## [1.1.26](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.1.25...medusa-interfaces@1.1.26) (2021-10-18) - -**Note:** Version bump only for package medusa-interfaces - -## [1.1.25](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.1.23...medusa-interfaces@1.1.25) (2021-10-18) - -### Bug Fixes - -- filter type in jsdoc ([c3a6045](https://github.com/medusajs/medusa/commit/c3a6045dd889a0d32d9e7f6e806d96d8333ea0a5)) -- product ordering ([57a6612](https://github.com/medusajs/medusa/commit/57a6612e845c078aec023d0cc49d6bfc175a1b37)) -- use type to choose transformer before adding or replacing documents ([24eecd2](https://github.com/medusajs/medusa/commit/24eecd2922e0c3425f2d43549b3227c756820387)) - -## [1.1.24](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.1.23...medusa-interfaces@1.1.24) (2021-10-18) - -### Bug Fixes - -- filter type in jsdoc ([c3a6045](https://github.com/medusajs/medusa/commit/c3a6045dd889a0d32d9e7f6e806d96d8333ea0a5)) -- product ordering ([57a6612](https://github.com/medusajs/medusa/commit/57a6612e845c078aec023d0cc49d6bfc175a1b37)) -- use type to choose transformer before adding or replacing documents ([24eecd2](https://github.com/medusajs/medusa/commit/24eecd2922e0c3425f2d43549b3227c756820387)) - -## [1.1.23](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.1.22...medusa-interfaces@1.1.23) (2021-09-15) - -**Note:** Version bump only for package medusa-interfaces - -## [1.1.22](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.1.21...medusa-interfaces@1.1.22) (2021-09-14) - -**Note:** Version bump only for package medusa-interfaces - -## [1.1.21](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.1.20...medusa-interfaces@1.1.21) (2021-08-05) - -**Note:** Version bump only for package medusa-interfaces - -## [1.1.20](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.1.19...medusa-interfaces@1.1.20) (2021-07-26) - -**Note:** Version bump only for package medusa-interfaces - -## [1.1.19](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.1.17...medusa-interfaces@1.1.19) (2021-07-15) - -**Note:** Version bump only for package medusa-interfaces - -## [1.1.18](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.1.17...medusa-interfaces@1.1.18) (2021-07-15) - -**Note:** Version bump only for package medusa-interfaces - -## [1.1.17](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.1.16...medusa-interfaces@1.1.17) (2021-07-02) - -**Note:** Version bump only for package medusa-interfaces - -## [1.1.16](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.1.15...medusa-interfaces@1.1.16) (2021-06-22) - -### Bug Fixes - -- release assist ([668e8a7](https://github.com/medusajs/medusa/commit/668e8a740200847fc2a41c91d2979097f1392532)) - -## [1.1.15](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.1.14...medusa-interfaces@1.1.15) (2021-06-09) - -**Note:** Version bump only for package medusa-interfaces - -## [1.1.14](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.1.13...medusa-interfaces@1.1.14) (2021-06-09) - -**Note:** Version bump only for package medusa-interfaces - -## [1.1.13](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.1.12...medusa-interfaces@1.1.13) (2021-06-09) - -**Note:** Version bump only for package medusa-interfaces - -## [1.1.12](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.1.11...medusa-interfaces@1.1.12) (2021-06-09) - -**Note:** Version bump only for package medusa-interfaces - -## [1.1.11](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.1.10...medusa-interfaces@1.1.11) (2021-06-08) - -**Note:** Version bump only for package medusa-interfaces - -## [1.1.10](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.1.7...medusa-interfaces@1.1.10) (2021-04-28) - -**Note:** Version bump only for package medusa-interfaces - -## [1.1.9](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.1.8...medusa-interfaces@1.1.9) (2021-04-20) - -**Note:** Version bump only for package medusa-interfaces - -## [1.1.8](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.1.7...medusa-interfaces@1.1.8) (2021-04-20) - -**Note:** Version bump only for package medusa-interfaces - -## [1.1.7](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.1.6...medusa-interfaces@1.1.7) (2021-04-13) - -**Note:** Version bump only for package medusa-interfaces - -## [1.1.6](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.1.5...medusa-interfaces@1.1.6) (2021-04-09) - -**Note:** Version bump only for package medusa-interfaces - -## [1.1.5](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.1.4...medusa-interfaces@1.1.5) (2021-03-30) - -**Note:** Version bump only for package medusa-interfaces - -## [1.1.4](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.1.3...medusa-interfaces@1.1.4) (2021-03-17) - -**Note:** Version bump only for package medusa-interfaces - -## [1.1.3](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.1.1...medusa-interfaces@1.1.3) (2021-03-17) - -### Features - -- **medusa:** Add support for filtering with gt, lt, gte and lte ([#190](https://github.com/medusajs/medusa/issues/190)) ([dd0491f](https://github.com/medusajs/medusa/commit/dd0491f52132aed24f642589b12fcf636b719580)) - -## [1.1.2](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.1.1...medusa-interfaces@1.1.2) (2021-03-17) - -### Features - -- **medusa:** Add support for filtering with gt, lt, gte and lte ([#190](https://github.com/medusajs/medusa/issues/190)) ([dd0491f](https://github.com/medusajs/medusa/commit/dd0491f52132aed24f642589b12fcf636b719580)) - -## [1.1.1](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.1.0...medusa-interfaces@1.1.1) (2021-02-17) - -### Features - -- notifications ([#172](https://github.com/medusajs/medusa/issues/172)) ([7308946](https://github.com/medusajs/medusa/commit/7308946e567ed4e63e1ed3d9d31b30c4f1a73f0d)) - -# [1.1.0](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.0.14...medusa-interfaces@1.1.0) (2021-01-26) - -**Note:** Version bump only for package medusa-interfaces - -## [1.0.14](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.0.13...medusa-interfaces@1.0.14) (2020-11-24) - -**Note:** Version bump only for package medusa-interfaces - -## [1.0.13](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.0.12...medusa-interfaces@1.0.13) (2020-10-20) - -### Features - -- **medusa-interfaces:** Adds schema options to base model ([cc23a3b](https://github.com/medusajs/medusa/commit/cc23a3b0706c41ec57bb25ea3de9c6e39bd04f31)) - -## [1.0.12](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.0.11...medusa-interfaces@1.0.12) (2020-10-14) - -### Features - -- return shipping and flow ([#125](https://github.com/medusajs/medusa/issues/125)) ([c1e821d](https://github.com/medusajs/medusa/commit/c1e821d9d4d33756c7309e5cf110d7aa9b67297d)) - -## 1.0.11 (2020-10-05) - -### Features - -- webshipper ([#118](https://github.com/medusajs/medusa/issues/118)) ([893a7f6](https://github.com/medusajs/medusa/commit/893a7f69afea67e854a67fc3b92c8a10c9c1b75c)) - -## 1.0.10 (2020-09-09) - -### Bug Fixes - -- updates license ([db519fb](https://github.com/medusajs/medusa/commit/db519fbaa6f8ad02c19cbecba5d4f28ba1ee81aa)) - -## 1.0.7 (2020-09-07) - -## 1.0.1 (2020-09-05) - -## 1.0.1-beta.0 (2020-09-04) - -# 1.0.0 (2020-09-03) - -# 1.0.0-alpha.30 (2020-08-28) - -# 1.0.0-alpha.27 (2020-08-27) - -### Features - -- **pagination:** Adds MVP pagination to orders and products for admin routes ([9dc6999](https://github.com/medusajs/medusa/commit/9dc6999a9a0c11d33eb9affa953ad1b44bd5e8b8)) - -# 1.0.0-alpha.24 (2020-08-27) - -# 1.0.0-alpha.3 (2020-08-20) - -# 1.0.0-alpha.2 (2020-08-20) - -# 1.0.0-alpha.1 (2020-08-20) - -# 1.0.0-alpha.0 (2020-08-20) - -### Reverts - -- Revert "[medusa-interfaces] : Adds decorator functionality to BaseService (#39)" (#41) ([2273cc5](https://github.com/medusajs/medusa/commit/2273cc519ad4d6ae16157173aba3955d16745e1d)), closes [#39](https://github.com/medusajs/medusa/issues/39) [#41](https://github.com/medusajs/medusa/issues/41) - -# 0.3.0 (2020-04-06) - -# 0.2.0 (2020-04-06) - -# 0.2.0-alpha.0 (2020-04-04) - -## 0.1.6-alpha.0 (2020-03-24) - -## 0.1.5-alpha.0 (2020-03-24) - -## 0.1.4-alpha.0 (2020-03-24) - -## 0.1.3-alpha.0 (2020-03-24) - -## 0.1.2-alpha.0 (2020-03-24) - -## 0.1.1-alpha.0 (2020-03-24) - -# 0.1.0-alpha.0 (2020-03-24) - -## [1.0.10](https://github.com/medusajs/medusa/compare/v1.0.9...v1.0.10) (2020-09-09) - -### Bug Fixes - -- updates license ([db519fb](https://github.com/medusajs/medusa/commit/db519fbaa6f8ad02c19cbecba5d4f28ba1ee81aa)) diff --git a/packages/medusa-interfaces/package.json b/packages/medusa-interfaces/package.json deleted file mode 100644 index d5636081cc..0000000000 --- a/packages/medusa-interfaces/package.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "name": "medusa-interfaces", - "version": "1.3.9", - "description": "Core interfaces for Medusa", - "main": "dist/index.js", - "repository": { - "type": "git", - "url": "https://github.com/medusajs/medusa", - "directory": "packages/medusa-interfaces" - }, - "engines": { - "node": ">=16" - }, - "scripts": { - "prepare": "cross-env NODE_ENV=production yarn run build", - "test": "jest --passWithNoTests src", - "build": "rimraf dist && babel src --out-dir dist --ignore '**/__tests__','**/__mocks__'", - "watch": "babel -w src --out-dir dist --ignore '**/__tests__','**/__mocks__'" - }, - "files": [ - "dist" - ], - "author": "Sebastian Rindom", - "license": "MIT", - "devDependencies": { - "@babel/cli": "^7.7.5", - "@babel/core": "^7.7.5", - "@babel/plugin-proposal-class-properties": "^7.7.4", - "@babel/plugin-transform-classes": "^7.9.5", - "@babel/plugin-transform-instanceof": "^7.8.3", - "@babel/plugin-transform-runtime": "^7.7.6", - "@babel/preset-env": "^7.7.5", - "@babel/preset-typescript": "^7.13.0", - "@babel/runtime": "^7.9.6", - "cross-env": "^5.2.1", - "jest": "^25.5.4", - "medusa-core-utils": "^1.2.1", - "medusa-test-utils": "^1.1.42", - "rimraf": "^5.0.1", - "typescript": "^4.4.4" - }, - "gitHead": "cd1f5afa5aa8c0b15ea957008ee19f1d695cbd2e" -} diff --git a/packages/medusa-interfaces/src/__tests__/base-service.js b/packages/medusa-interfaces/src/__tests__/base-service.js deleted file mode 100644 index 76b5055c1f..0000000000 --- a/packages/medusa-interfaces/src/__tests__/base-service.js +++ /dev/null @@ -1,66 +0,0 @@ -import BaseService from "../base-service" - -describe("BaseService", () => { - describe("addDecorator", () => { - const baseService = new BaseService() - - it("successfully adds decorator", () => { - baseService.addDecorator(obj => { - return (obj.decorator1 = true) - }) - - expect(baseService.decorators_.length).toEqual(1) - }) - - it("throws if decorator is not a function", () => { - expect(() => baseService.addDecorator("not a function")).toThrow( - "Decorators must be of type function" - ) - }) - }) - - describe("runDecorators_", () => { - it("returns success when passwords match", async () => { - const baseService = new BaseService() - - baseService.addDecorator(obj => { - obj.decorator1 = true - return obj - }) - baseService.addDecorator(obj => { - obj.decorator2 = true - return obj - }) - - const result = await baseService.runDecorators_({ data: "initial" }) - expect(result).toEqual({ - data: "initial", - decorator1: true, - decorator2: true, - }) - }) - - it("skips failing decorator", async () => { - const baseService = new BaseService() - - baseService.addDecorator(obj => { - obj.decorator1 = true - return obj - }) - baseService.addDecorator(obj => { - return Promise.reject("fail") - }) - baseService.addDecorator(obj => { - obj.decorator3 = true - return Promise.resolve(obj) - }) - - const result = await baseService.runDecorators_({ data: "initial" }) - expect(result).toEqual({ - data: "initial", - decorator1: true, - decorator3: true, - }) - }) - }) -}) diff --git a/packages/medusa-interfaces/src/base-service.js b/packages/medusa-interfaces/src/base-service.js deleted file mode 100644 index 09f876a3fe..0000000000 --- a/packages/medusa-interfaces/src/base-service.js +++ /dev/null @@ -1,174 +0,0 @@ -// Import from dist to avoid circular deps which result in the base service to be undefined -import { - buildQuery, - setMetadata, - validateId, -} from "@medusajs/medusa/dist/utils" - -/** - * Common functionality for Services - * @interface - * @deprecated use TransactionBaseService from @medusajs/medusa instead - */ -export default class BaseService { - constructor() { - this.decorators_ = [] - } - - withTransaction() { - console.log("WARN: withTransaction called without custom implementation") - return this - } - - /** - * Used to build TypeORM queries. - */ - buildQuery_(selector, config = {}) { - return buildQuery(selector, config) - } - - /** - * Confirms whether a given raw id is valid. Fails if the provided - * id is null or undefined. The validate function takes an optional config - * param, to support checking id prefix and length. - * @param {string} rawId - the id to validate. - * @param {object?} config - optional config - * @returns {string} the rawId given that nothing failed - */ - validateId_(rawId, config = {}) { - return validateId(rawId, config) - } - - shouldRetryTransaction(err) { - const code = typeof err === "object" ? String(err.code) : null - return code === "40001" || code === "40P01" - } - - /** - * Wraps some work within a transactional block. If the service already has - * a transaction manager attached this will be reused, otherwise a new - * transaction manager is created. - * @param {function} work - the transactional work to be done - * @param {string} isolation - the isolation level to be used for the work. - * @return {any} the result of the transactional work - */ - async atomicPhase_( - work, - isolationOrErrorHandler, - maybeErrorHandlerOrDontFail - ) { - let errorHandler = maybeErrorHandlerOrDontFail - let isolation = isolationOrErrorHandler - let dontFail = false - if (typeof isolationOrErrorHandler === "function") { - isolation = null - errorHandler = isolationOrErrorHandler - dontFail = !!maybeErrorHandlerOrDontFail - } - - if (this.transactionManager_) { - const doWork = async (m) => { - this.manager_ = m - this.transactionManager_ = m - try { - const result = await work(m) - return result - } catch (error) { - if (errorHandler) { - const queryRunner = this.transactionManager_.queryRunner - if (queryRunner.isTransactionActive) { - await queryRunner.rollbackTransaction() - } - - await errorHandler(error) - } - throw error - } - } - - return doWork(this.transactionManager_) - } else { - const temp = this.manager_ - const doWork = async (m) => { - this.manager_ = m - this.transactionManager_ = m - try { - const result = await work(m) - this.manager_ = temp - this.transactionManager_ = undefined - return result - } catch (error) { - this.manager_ = temp - this.transactionManager_ = undefined - throw error - } - } - - if (isolation) { - let result - try { - result = await this.manager_.transaction(isolation, (m) => doWork(m)) - return result - } catch (error) { - if (this.shouldRetryTransaction(error)) { - return this.manager_.transaction(isolation, (m) => doWork(m)) - } else { - if (errorHandler) { - await errorHandler(error) - } - throw error - } - } - } - - try { - return await this.manager_.transaction((m) => doWork(m)) - } catch (error) { - if (errorHandler) { - const result = await errorHandler(error) - if (dontFail) { - return result - } - } - - throw error - } - } - } - - /** - * Dedicated method to set metadata. - * @param {string} obj - the entity to apply metadata to. - * @param {object} metadata - the metadata to set - * @return {Promise} resolves to the updated result. - */ - setMetadata_(obj, metadata) { - return setMetadata(obj, metadata) - } - - /** - * Adds a decorator to a service. The decorator must be a function and should - * return a decorated object. - * @param {function} fn - the decorator to add to the service - */ - addDecorator(fn) { - if (typeof fn !== "function") { - throw Error("Decorators must be of type function") - } - - this.decorators_.push(fn) - } - - /** - * Runs the decorators registered on the service. The decorators are run in - * the order they have been registered in. Failing decorators will be skipped - * in order to ensure deliverability in spite of breaking code. - * @param {object} obj - the object to decorate. - * @return {object} the decorated object. - */ - runDecorators_(obj, fields = [], expandFields = []) { - return this.decorators_.reduce(async (acc, next) => { - return acc.then((res) => next(res, fields, expandFields)).catch(() => acc) - }, Promise.resolve(obj)) - } -} diff --git a/packages/medusa-interfaces/src/file-service.js b/packages/medusa-interfaces/src/file-service.js deleted file mode 100644 index d59ded3128..0000000000 --- a/packages/medusa-interfaces/src/file-service.js +++ /dev/null @@ -1,28 +0,0 @@ -import BaseService from "./base-service" - -/** - * Interface for file connectors - * @interface - * @deprecated use AbstractFileService from @medusajs/medusa instead - */ -class BaseFileService extends BaseService { - static _isFileService = true - - static isFileService(obj) { - return obj?.constructor?._isFileService - } - - constructor() { - super() - } - - upload() { - throw Error("upload must be overridden by the child class") - } - - delete() { - throw Error("delete must be overridden by the child class") - } -} - -export default BaseFileService diff --git a/packages/medusa-interfaces/src/fulfillment-service.js b/packages/medusa-interfaces/src/fulfillment-service.js deleted file mode 100644 index 880c7d6b08..0000000000 --- a/packages/medusa-interfaces/src/fulfillment-service.js +++ /dev/null @@ -1,117 +0,0 @@ -import BaseService from "./base-service" - -/** - * The interface that all fulfillment services must inherit from. The intercace - * provides the necessary methods for creating, authorizing and managing - * fulfillment orders. - * @interface - * @deprecated use AbstractFulfillmentService from @medusajs/medusa instead - */ -class BaseFulfillmentService extends BaseService { - static _isFulfillmentService = true - - static isFulfillmentService(obj) { - return obj?.constructor?._isFulfillmentService - } - - constructor() { - super() - } - - getIdentifier() { - return this.constructor.identifier - } - - /** - * Called before a shipping option is created in Admin. The method should - * return all of the options that the fulfillment provider can be used with, - * and it is here the distinction between different shipping options are - * enforced. For example, a fulfillment provider may offer Standard Shipping - * and Express Shipping as fulfillment options, it is up to the store operator - * to create shipping options in Medusa that can be chosen between by the - * customer. - */ - getFulfillmentOptions() { - throw Error("getFulfillmentOptions must be overridden by the child class") - } - - /** - * Called before a shipping method is set on a cart to ensure that the data - * sent with the shipping method is valid. The data object may contain extra - * data about the shipment such as an id of a drop point. It is up to the - * fulfillment provider to enforce that the correct data is being sent - * through. - * @param {object} optionData - the data to validate - * @param {object} data - the data to validate - * @param {object | undefined} cart - the cart to which the shipping method will be applied - * @return {object} the data to populate `cart.shipping_methods.$.data` this - * is usually important for future actions like generating shipping labels - */ - validateFulfillmentData(optionData, data, cart) { - throw Error("validateFulfillmentData must be overridden by the child class") - } - - /** - * Called before a shipping option is created in Admin. Use this to ensure - * that a fulfillment option does in fact exist. - */ - validateOption(data) { - throw Error("validateOption must be overridden by the child class") - } - - canCalculate(data) { - throw Error("canCalculate must be overridden by the child class") - } - - /** - * Used to calculate a price for a given shipping option. - */ - calculatePrice(optionData, data, cart) { - throw Error("calculatePrice must be overridden by the child class") - } - - createFulfillment(data, items, order, fulfillment) { - throw Error("createFulfillment must be overridden by the child class") - } - - cancelFulfillment(fulfillment) { - throw Error("cancelFulfillment must be overridden by the child class") - } - - /** - * Used to retrieve documents associated with a fulfillment. - * Will default to returning no documents. - */ - getFulfillmentDocuments(data) { - return [] - } - - /** - * Used to create a return order. Should return the data necessary for future - * operations on the return; in particular the data may be used to receive - * documents attached to the return. - */ - createReturn(fromData) { - throw Error("createReturn must be overridden by the child class") - } - - /** - * Used to retrieve documents related to a return order. - */ - getReturnDocuments(data) { - return [] - } - - /** - * Used to retrieve documents related to a shipment. - */ - getShipmentDocuments(data) { - return [] - } - - retrieveDocuments(fulfillmentData, documentType) { - throw Error("retrieveDocuments must be overridden by the child class") - } -} - -export default BaseFulfillmentService diff --git a/packages/medusa-interfaces/src/index.js b/packages/medusa-interfaces/src/index.js deleted file mode 100644 index 9cf865cf57..0000000000 --- a/packages/medusa-interfaces/src/index.js +++ /dev/null @@ -1,7 +0,0 @@ -export { default as BaseService } from "./base-service" -export { default as PaymentService } from "./payment-service" -export { default as FulfillmentService } from "./fulfillment-service" -export { default as FileService } from "./file-service" -export { default as NotificationService } from "./notification-service" -export { default as OauthService } from "./oauth-service" -export { default as SearchService } from "./search-service" diff --git a/packages/medusa-interfaces/src/notification-service.js b/packages/medusa-interfaces/src/notification-service.js deleted file mode 100644 index 04ed883a11..0000000000 --- a/packages/medusa-interfaces/src/notification-service.js +++ /dev/null @@ -1,35 +0,0 @@ -import BaseService from "./base-service" - -/** - * Interface for Notification Providers - * @interface - * @deprecated use AbstractNotificationService from @medusajs/medusa instead - */ -class BaseNotificationService extends BaseService { - static _isNotificationService = true - - static isNotificationService(obj) { - return obj?.constructor?._isNotificationService - } - - constructor() { - super() - } - - getIdentifier() { - return this.constructor.identifier - } - - /** - * Used to retrieve documents related to a shipment. - */ - sendNotification(event, data) { - throw new Error("Must be overridden by child") - } - - resendNotification(notification, config = {}) { - throw new Error("Must be overridden by child") - } -} - -export default BaseNotificationService diff --git a/packages/medusa-interfaces/src/oauth-service.js b/packages/medusa-interfaces/src/oauth-service.js deleted file mode 100644 index f001d75254..0000000000 --- a/packages/medusa-interfaces/src/oauth-service.js +++ /dev/null @@ -1,31 +0,0 @@ -import BaseService from "./base-service" - -/** - * Interface for file connectors - * @interface - */ -class BaseOauthService extends BaseService { - static _isOauthService = true - - static isOauthService(obj) { - return obj?.constructor?._isOauthService - } - - constructor() { - super() - } - - generateToken() { - throw Error("generateToken must be overridden by the child class") - } - - refreshToken() { - throw Error("refreshToken must be overridden by the child class") - } - - destroyToken() { - throw Error("destroyToken must be overridden by the child class") - } -} - -export default BaseOauthService diff --git a/packages/medusa-interfaces/src/payment-service.js b/packages/medusa-interfaces/src/payment-service.js deleted file mode 100644 index 7492d4304e..0000000000 --- a/packages/medusa-interfaces/src/payment-service.js +++ /dev/null @@ -1,85 +0,0 @@ -import BaseService from "./base-service" - -/** - * The interface that all payment services must inherit from. The intercace - * provides the necessary methods for creating, authorizing and managing - * payments. - * @interface - * @deprecated use AbstractPaymentProcessor from @medusajs/medusa instead - */ -class BasePaymentService extends BaseService { - static _isPaymentService = true - - static isPaymentService(obj) { - return obj?.constructor?._isPaymentService - } - - constructor() { - super() - } - - getIdentifier() { - return this.constructor.identifier - } - - /** - * Used to create a payment to be processed with the service's payment gateway. - * @param cart {object} - the cart that the payment should cover. - * @return {Promise<{object}>} - returns a promise that resolves to an object - * containing the payment data. This data will be saved to the cart for later - * use. - */ - createPayment(cart) { - throw Error("createPayment must be overridden by the child class") - } - - /** - * Used to retrieve a payment. - * @param cart {object} - the cart whose payment should be retrieved. - * @return {Promise<{object}>} - returns a promise that resolves to the - * payment object as stored with the provider. - */ - retrievePayment(cart) { - throw Error("getPayment must be overridden by the child class") - } - - /** - * Used to update a payment. This method is called when the cart is updated. - * @param cart {object} - the cart whose payment should be updated. - * @return {Promise<{object}>} - returns a promise that resolves to the - * payment object as stored with the provider. - */ - updatePayment(cart) { - throw Error("updatePayment must be overridden by the child class") - } - - getStatus() { - throw Error("getStatus must be overridden by the child class") - } - - authorizePayment() { - throw Error("authorizePayment must be overridden by the child class") - } - - capturePayment() { - throw Error("capturePayment must be overridden by the child class") - } - - refundPayment() { - throw Error("refundPayment must be overridden by the child class") - } - - deletePayment() { - throw Error("deletePayment must be overridden by the child class") - } - - /** - * If the payment provider can save a payment method this function will - * retrieve them. - */ - retrieveSavedMethods(customer) { - return Promise.resolve([]) - } -} - -export default BasePaymentService diff --git a/packages/medusa-interfaces/src/search-service.js b/packages/medusa-interfaces/src/search-service.js deleted file mode 100644 index df818a2d83..0000000000 --- a/packages/medusa-interfaces/src/search-service.js +++ /dev/null @@ -1,107 +0,0 @@ -import BaseService from "./base-service" - -/** - * The interface that all search services must implement. - * @interface - * @deprecated use AbstractSearchService from @medusajs/utils instead - */ -class SearchService extends BaseService { - static _isSearchService = true - - static isSearchService(obj) { - return obj?.constructor?._isSearchService - } - - constructor() { - super() - } - - get options() { - return this.options_ ?? {} - } - - /** - * Used to create an index - * @param indexName {string} - the index name - * @param [options] {string} - the index name - * @return {Promise<{object}>} - returns response from search engine provider - */ - createIndex(indexName, options) { - throw Error("createIndex must be overridden by a child class") - } - - /** - * Used to get an index - * @param indexName {string} - the index name. - * @return {Promise<{object}>} - returns response from search engine provider - */ - getIndex(indexName) { - throw Error("getIndex must be overridden by a child class") - } - - /** - * Used to index documents by the search engine provider - * @param indexName {string} - the index name - * @param documents {Array.} - documents array to be indexed - * @param type {string} - type of documents to be added (e.g: products, regions, orders, etc) - * @return {Promise<{object}>} - returns response from search engine provider - */ - addDocuments(indexName, documents, type) { - throw Error("addDocuments must be overridden by a child class") - } - - /** - * Used to replace documents - * @param indexName {string} - the index name. - * @param documents {Object} - array of document objects that will replace existing documents - * @param type {Array.} - type of documents to be replaced (e.g: products, regions, orders, etc) - * @return {Promise<{object}>} - returns response from search engine provider - */ - replaceDocuments(indexName, documents, type) { - throw Error("updateDocument must be overridden by a child class") - } - - /** - * Used to delete document - * @param indexName {string} - the index name - * @param document_id {string} - the id of the document - * @return {Promise<{object}>} - returns response from search engine provider - */ - deleteDocument(indexName, document_id) { - throw Error("deleteDocument must be overridden by a child class") - } - - /** - * Used to delete all documents - * @param indexName {string} - the index name - * @return {Promise<{object}>} - returns response from search engine provider - */ - deleteAllDocuments(indexName) { - throw Error("deleteAllDocuments must be overridden by a child class") - } - - /** - * Used to search for a document in an index - * @param indexName {string} - the index name - * @param query {string} - the search query - * @param options {{ paginationOptions: { limit: number, offset: number }, filter: any, additionalOptions: any}} - * - any options passed to the request object other than the query and indexName - * - additionalOptions contain any provider specific options - * @return {Promise<{ hits: any[]; [k: string]: any; }>} returns response from search engine provider - */ - search(indexName, query, options) { - throw Error("search must be overridden by a child class") - } - - /** - * Used to update the settings of an index - * @param indexName {string} - the index name - * @param settings {object} - settings object - * @return {Promise<{object}>} - returns response from search engine provider - */ - updateSettings(indexName, settings) { - throw Error("updateSettings must be overridden by a child class") - } -} - -export default SearchService diff --git a/packages/medusa-interfaces/tsconfig.json b/packages/medusa-interfaces/tsconfig.json deleted file mode 100644 index 6052d283a2..0000000000 --- a/packages/medusa-interfaces/tsconfig.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "compilerOptions": { - "lib": ["es2021"], - "target": "es2021", - "outDir": "./dist", - "esModuleInterop": true, - "declaration": true, - "module": "commonjs", - "moduleResolution": "node", - "emitDecoratorMetadata": true, - "experimentalDecorators": true, - "sourceMap": true, - "noImplicitReturns": true, - "strictNullChecks": true, - "strictFunctionTypes": true, - "noImplicitThis": true, - "allowJs": true, - "skipLibCheck": true, - "downlevelIteration": true // to use ES5 specific tooling - }, - "include": ["./src/**/*", "index.d.ts"], - "exclude": [ - "./dist/**/*", - "./src/**/__tests__", - "./src/**/__mocks__", - "node_modules" - ] -} diff --git a/packages/medusa/index.d.ts b/packages/medusa/index.d.ts deleted file mode 100644 index 80c99ed531..0000000000 --- a/packages/medusa/index.d.ts +++ /dev/null @@ -1 +0,0 @@ -declare module "medusa-interfaces" diff --git a/packages/medusa/package.json b/packages/medusa/package.json index d0f7dcd302..e4663ee790 100644 --- a/packages/medusa/package.json +++ b/packages/medusa/package.json @@ -31,7 +31,6 @@ "@types/multer": "^1.4.7", "cross-env": "^5.2.1", "jest": "^25.5.4", - "medusa-interfaces": "^1.3.9", "medusa-test-utils": "^1.1.44", "rimraf": "^5.0.1", "supertest": "^4.0.2", @@ -45,9 +44,6 @@ "serve": "node dist/app.js", "test": "jest --silent --bail --maxWorkers=50% --forceExit" }, - "peerDependencies": { - "medusa-interfaces": "^1.3.7" - }, "dependencies": { "@medusajs/admin-sdk": "0.0.1", "@medusajs/core-flows": "^0.0.9", diff --git a/yarn.lock b/yarn.lock index 7cc06b870d..6fd0dca3d3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -870,7 +870,7 @@ __metadata: languageName: node linkType: hard -"@babel/cli@npm:^7.12.10, @babel/cli@npm:^7.14.3, @babel/cli@npm:^7.7.5": +"@babel/cli@npm:^7.12.10, @babel/cli@npm:^7.14.3": version: 7.24.5 resolution: "@babel/cli@npm:7.24.5" dependencies: @@ -1366,7 +1366,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-proposal-class-properties@npm:^7.10.4, @babel/plugin-proposal-class-properties@npm:^7.12.1, @babel/plugin-proposal-class-properties@npm:^7.7.4": +"@babel/plugin-proposal-class-properties@npm:^7.10.4, @babel/plugin-proposal-class-properties@npm:^7.12.1": version: 7.18.6 resolution: "@babel/plugin-proposal-class-properties@npm:7.18.6" dependencies: @@ -1875,7 +1875,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-classes@npm:^7.10.4, @babel/plugin-transform-classes@npm:^7.12.1, @babel/plugin-transform-classes@npm:^7.24.5, @babel/plugin-transform-classes@npm:^7.9.5": +"@babel/plugin-transform-classes@npm:^7.10.4, @babel/plugin-transform-classes@npm:^7.12.1, @babel/plugin-transform-classes@npm:^7.24.5": version: 7.24.5 resolution: "@babel/plugin-transform-classes@npm:7.24.5" dependencies: @@ -2012,7 +2012,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-instanceof@npm:^7.10.4, @babel/plugin-transform-instanceof@npm:^7.12.1, @babel/plugin-transform-instanceof@npm:^7.8.3": +"@babel/plugin-transform-instanceof@npm:^7.10.4, @babel/plugin-transform-instanceof@npm:^7.12.1": version: 7.24.1 resolution: "@babel/plugin-transform-instanceof@npm:7.24.1" dependencies: @@ -2360,7 +2360,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-runtime@npm:^7.11.5, @babel/plugin-transform-runtime@npm:^7.12.1, @babel/plugin-transform-runtime@npm:^7.7.6": +"@babel/plugin-transform-runtime@npm:^7.11.5, @babel/plugin-transform-runtime@npm:^7.12.1": version: 7.24.3 resolution: "@babel/plugin-transform-runtime@npm:7.24.3" dependencies: @@ -2571,7 +2571,7 @@ __metadata: languageName: node linkType: hard -"@babel/preset-env@npm:^7.11.5, @babel/preset-env@npm:^7.12.11, @babel/preset-env@npm:^7.12.7, @babel/preset-env@npm:^7.23.2, @babel/preset-env@npm:^7.7.5": +"@babel/preset-env@npm:^7.11.5, @babel/preset-env@npm:^7.12.11, @babel/preset-env@npm:^7.12.7, @babel/preset-env@npm:^7.23.2": version: 7.24.5 resolution: "@babel/preset-env@npm:7.24.5" dependencies: @@ -2732,7 +2732,7 @@ __metadata: languageName: node linkType: hard -"@babel/preset-typescript@npm:^7.13.0, @babel/preset-typescript@npm:^7.15.0, @babel/preset-typescript@npm:^7.16.0, @babel/preset-typescript@npm:^7.23.0": +"@babel/preset-typescript@npm:^7.15.0, @babel/preset-typescript@npm:^7.16.0, @babel/preset-typescript@npm:^7.23.0": version: 7.24.1 resolution: "@babel/preset-typescript@npm:7.24.1" dependencies: @@ -2769,7 +2769,7 @@ __metadata: languageName: node linkType: hard -"@babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.13.10, @babel/runtime@npm:^7.15.4, @babel/runtime@npm:^7.17.8, @babel/runtime@npm:^7.20.1, @babel/runtime@npm:^7.21.0, @babel/runtime@npm:^7.22.10, @babel/runtime@npm:^7.22.5, @babel/runtime@npm:^7.23.2, @babel/runtime@npm:^7.23.8, @babel/runtime@npm:^7.24.1, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.9.2, @babel/runtime@npm:^7.9.6": +"@babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.13.10, @babel/runtime@npm:^7.15.4, @babel/runtime@npm:^7.17.8, @babel/runtime@npm:^7.20.1, @babel/runtime@npm:^7.21.0, @babel/runtime@npm:^7.22.10, @babel/runtime@npm:^7.22.5, @babel/runtime@npm:^7.23.2, @babel/runtime@npm:^7.23.8, @babel/runtime@npm:^7.24.1, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.9.2": version: 7.24.5 resolution: "@babel/runtime@npm:7.24.5" dependencies: @@ -5672,7 +5672,6 @@ __metadata: jsonwebtoken: ^9.0.0 lodash: ^4.17.21 medusa-core-utils: ^1.2.2 - medusa-interfaces: ^1.3.9 medusa-telemetry: ^0.0.17 medusa-test-utils: ^1.1.44 morgan: ^1.9.1 @@ -5693,8 +5692,6 @@ __metadata: typescript: ^4.4.4 uuid: ^9.0.0 zod: 3.22.4 - peerDependencies: - medusa-interfaces: ^1.3.7 bin: medusa: ./cli.js languageName: unknown @@ -20250,7 +20247,6 @@ __metadata: form-data: ^4.0.0 jest: ^26.6.3 jest-environment-node: 26.6.2 - medusa-interfaces: "workspace:*" pg: ^8.11.0 typeorm: ^0.3.16 languageName: unknown @@ -20290,7 +20286,6 @@ __metadata: faker: ^5.5.3 jest: ^26.6.3 jest-environment-node: 26.6.2 - medusa-interfaces: "workspace:*" medusa-test-utils: "workspace:*" pg: ^8.11.0 typeorm: ^0.3.16 @@ -24207,7 +24202,7 @@ __metadata: languageName: node linkType: hard -"medusa-core-utils@^1.2.0, medusa-core-utils@^1.2.1, medusa-core-utils@^1.2.2, medusa-core-utils@workspace:packages/medusa-core-utils": +"medusa-core-utils@^1.2.0, medusa-core-utils@^1.2.2, medusa-core-utils@workspace:packages/medusa-core-utils": version: 0.0.0-use.local resolution: "medusa-core-utils@workspace:packages/medusa-core-utils" dependencies: @@ -24246,28 +24241,6 @@ __metadata: languageName: unknown linkType: soft -"medusa-interfaces@^1.3.9, medusa-interfaces@workspace:*, medusa-interfaces@workspace:packages/medusa-interfaces": - version: 0.0.0-use.local - resolution: "medusa-interfaces@workspace:packages/medusa-interfaces" - dependencies: - "@babel/cli": ^7.7.5 - "@babel/core": ^7.7.5 - "@babel/plugin-proposal-class-properties": ^7.7.4 - "@babel/plugin-transform-classes": ^7.9.5 - "@babel/plugin-transform-instanceof": ^7.8.3 - "@babel/plugin-transform-runtime": ^7.7.6 - "@babel/preset-env": ^7.7.5 - "@babel/preset-typescript": ^7.13.0 - "@babel/runtime": ^7.9.6 - cross-env: ^5.2.1 - jest: ^25.5.4 - medusa-core-utils: ^1.2.1 - medusa-test-utils: ^1.1.42 - rimraf: ^5.0.1 - typescript: ^4.4.4 - languageName: unknown - linkType: soft - "medusa-react@npm:latest": version: 9.0.17 resolution: "medusa-react@npm:9.0.17" From a775d57255368d9a3d0727bef3eff3fce6199d34 Mon Sep 17 00:00:00 2001 From: Adrien de Peretti Date: Thu, 16 May 2024 16:10:54 +0200 Subject: [PATCH 2/3] feat(core-flows, types): Create return order (#7319) **what** - Create return workflow partial implementation - Update some order domain types - create order fulfillment link **NOTE** this PR is partially done but can still be merged as is, it will require some discussions around the flow and some unknowns or uncertainty in regards to some data and some behaviour --- .../order/workflows/create-return.spec.ts | 473 ++++++++++++++++++ packages/core/core-flows/src/common/index.ts | 1 + .../src/common/steps/create-remote-links.ts | 29 ++ .../src/order/steps/create-return.ts | 29 ++ .../src/order/steps/get-item-tax-lines.ts | 28 +- .../src/order/workflows/create-return.ts | 339 +++++++++++++ .../core-flows/src/order/workflows/index.ts | 1 + packages/core/modules-sdk/src/remote-link.ts | 2 +- packages/core/types/src/common/index.ts | 1 + .../core/types/src/common/with-calculated.ts | 3 + packages/core/types/src/order/common.ts | 15 +- packages/core/types/src/order/mutations.ts | 3 +- packages/core/types/src/workflow/index.ts | 1 + .../src/workflow/order/create-return-order.ts | 26 + .../core/types/src/workflow/order/index.ts | 1 + packages/core/utils/src/link/links.ts | 6 + .../src/modules-sdk/decorators/module.ts | 0 .../utils/composer/helpers/resolve-value.ts | 2 +- .../workflows-sdk/src/utils/composer/type.ts | 2 +- .../services/fulfillment-module-service.ts | 6 +- .../definitions/fulfillment-set-location.ts | 3 + .../link-modules/src/definitions/index.ts | 1 + .../src/definitions/order-fulfillment.ts | 63 +++ .../src/services/order-module-service.ts | 2 +- 24 files changed, 1016 insertions(+), 21 deletions(-) create mode 100644 integration-tests/modules/__tests__/order/workflows/create-return.spec.ts create mode 100644 packages/core/core-flows/src/common/steps/create-remote-links.ts create mode 100644 packages/core/core-flows/src/order/steps/create-return.ts create mode 100644 packages/core/core-flows/src/order/workflows/create-return.ts create mode 100644 packages/core/types/src/common/with-calculated.ts create mode 100644 packages/core/types/src/workflow/order/create-return-order.ts create mode 100644 packages/core/types/src/workflow/order/index.ts create mode 100644 packages/core/utils/src/modules-sdk/decorators/module.ts create mode 100644 packages/modules/link-modules/src/definitions/order-fulfillment.ts diff --git a/integration-tests/modules/__tests__/order/workflows/create-return.spec.ts b/integration-tests/modules/__tests__/order/workflows/create-return.spec.ts new file mode 100644 index 0000000000..858f801626 --- /dev/null +++ b/integration-tests/modules/__tests__/order/workflows/create-return.spec.ts @@ -0,0 +1,473 @@ +import { ModuleRegistrationName, Modules } from "@medusajs/modules-sdk" +import { + FulfillmentWorkflow, + IOrderModuleService, + IRegionModuleService, + IStockLocationServiceNext, + OrderWorkflow, + ProductDTO, + RegionDTO, + ShippingOptionDTO, + StockLocationDTO, +} from "@medusajs/types" +import { medusaIntegrationTestRunner } from "medusa-test-utils/dist" +import { + createReturnOrderWorkflow, + createShippingOptionsWorkflow, +} from "@medusajs/core-flows" +import { + ContainerRegistrationKeys, + remoteQueryObjectFromString, + RuleOperator, +} from "@medusajs/utils" + +jest.setTimeout(500000) + +const env = { MEDUSA_FF_MEDUSA_V2: true } +const providerId = "manual_test-provider" + +async function prepareDataFixtures({ container }) { + const fulfillmentService = container.resolve( + ModuleRegistrationName.FULFILLMENT + ) + const salesChannelService = container.resolve( + ModuleRegistrationName.SALES_CHANNEL + ) + const stockLocationModule: IStockLocationServiceNext = container.resolve( + ModuleRegistrationName.STOCK_LOCATION + ) + const productModule = container.resolve(ModuleRegistrationName.PRODUCT) + const inventoryModule = container.resolve(ModuleRegistrationName.INVENTORY) + + const shippingProfile = await fulfillmentService.createShippingProfiles({ + name: "test", + type: "default", + }) + + const fulfillmentSet = await fulfillmentService.create({ + name: "Test fulfillment set", + type: "manual_test", + }) + + const serviceZone = await fulfillmentService.createServiceZones({ + name: "Test service zone", + fulfillment_set_id: fulfillmentSet.id, + geo_zones: [ + { + type: "country", + country_code: "US", + }, + ], + }) + + const regionService = container.resolve( + ModuleRegistrationName.REGION + ) as IRegionModuleService + + const [region] = await regionService.create([ + { + name: "Test region", + currency_code: "eur", + countries: ["fr"], + }, + ]) + + const salesChannel = await salesChannelService.create({ + name: "Webshop", + }) + + const location: StockLocationDTO = await stockLocationModule.create({ + name: "Warehouse", + address: { + address_1: "Test", + city: "Test", + country_code: "US", + postal_code: "12345", + phone: "12345", + }, + }) + + const [product] = await productModule.create([ + { + title: "Test product", + variants: [ + { + title: "Test variant", + sku: "test-variant", + }, + ], + }, + ]) + + const inventoryItem = await inventoryModule.create({ + sku: "inv-1234", + }) + + await inventoryModule.createInventoryLevels([ + { + inventory_item_id: inventoryItem.id, + location_id: location.id, + stocked_quantity: 2, + reserved_quantity: 0, + }, + ]) + + const remoteLink = container.resolve(ContainerRegistrationKeys.REMOTE_LINK) + + await remoteLink.create([ + { + [Modules.STOCK_LOCATION]: { + stock_location_id: location.id, + }, + [Modules.FULFILLMENT]: { + fulfillment_set_id: fulfillmentSet.id, + }, + }, + { + [Modules.SALES_CHANNEL]: { + sales_channel_id: salesChannel.id, + }, + [Modules.STOCK_LOCATION]: { + stock_location_id: location.id, + }, + }, + { + [Modules.PRODUCT]: { + variant_id: product.variants[0].id, + }, + [Modules.INVENTORY]: { + inventory_item_id: inventoryItem.id, + }, + }, + ]) + + const shippingOptionData: FulfillmentWorkflow.CreateShippingOptionsWorkflowInput = + { + name: "Return shipping option", + price_type: "flat", + service_zone_id: serviceZone.id, + shipping_profile_id: shippingProfile.id, + provider_id: providerId, + type: { + code: "manual-type", + label: "Manual Type", + description: "Manual Type Description", + }, + prices: [ + { + currency_code: "usd", + amount: 10, + }, + { + region_id: region.id, + amount: 100, + }, + ], + rules: [ + { + attribute: "is_return", + operator: RuleOperator.EQ, + value: '"true"', + }, + ], + } + + const { result } = await createShippingOptionsWorkflow(container).run({ + input: [shippingOptionData], + }) + + const remoteQueryObject = remoteQueryObjectFromString({ + entryPoint: "shipping_option", + variables: { + id: result[0].id, + }, + fields: [ + "id", + "name", + "price_type", + "service_zone_id", + "shipping_profile_id", + "provider_id", + "data", + "metadata", + "type.*", + "created_at", + "updated_at", + "deleted_at", + "shipping_option_type_id", + "prices.*", + ], + }) + + const remoteQuery = container.resolve(ContainerRegistrationKeys.REMOTE_QUERY) + + const [createdShippingOption] = await remoteQuery(remoteQueryObject) + return { + shippingOption: createdShippingOption, + region, + salesChannel, + location, + product, + } +} + +async function createOrderFixture({ container, product }) { + const orderService: IOrderModuleService = container.resolve( + ModuleRegistrationName.ORDER + ) + let order = await orderService.create({ + region_id: "test_region_idclear", + email: "foo@bar.com", + items: [ + { + title: "Custom Item 2", + variant_sku: product.variants[0].sku, + variant_title: product.variants[0].title, + quantity: 1, + unit_price: 50, + adjustments: [ + { + code: "VIP_25 ETH", + amount: "0.000000000000000005", + description: "VIP discount", + promotion_id: "prom_123", + provider_id: "coupon_kings", + }, + ], + } as any, + ], + transactions: [ + { + amount: 50, // TODO: check calculation, I think it should be 60 wit the shipping but the order total is 50 + currency_code: "usd", + }, + ], + sales_channel_id: "test", + shipping_address: { + first_name: "Test", + last_name: "Test", + address_1: "Test", + city: "Test", + country_code: "US", + postal_code: "12345", + phone: "12345", + }, + billing_address: { + first_name: "Test", + last_name: "Test", + address_1: "Test", + city: "Test", + country_code: "US", + postal_code: "12345", + }, + shipping_methods: [ + { + name: "Test shipping method", + amount: 10, + data: {}, + tax_lines: [ + { + description: "shipping Tax 1", + tax_rate_id: "tax_usa_shipping", + code: "code", + rate: 10, + }, + ], + adjustments: [ + { + code: "VIP_10", + amount: 1, + description: "VIP discount", + promotion_id: "prom_123", + }, + ], + }, + ], + currency_code: "usd", + customer_id: "joe", + }) + + await orderService.addOrderAction([ + { + action: "FULFILL_ITEM", + order_id: order.id, + version: order.version, + reference: "fullfilment", + reference_id: "fulfill_123", + details: { + reference_id: order.items![0].id, + quantity: 1, + }, + }, + { + action: "SHIP_ITEM", + order_id: order.id, + version: order.version, + reference: "fullfilment", + reference_id: "fulfill_123", + details: { + reference_id: order.items![0].id, + quantity: 1, + }, + }, + ]) + + await orderService.applyPendingOrderActions(order.id) + + order = await orderService.retrieve(order.id, { + relations: ["items"], + }) + + return order +} + +medusaIntegrationTestRunner({ + env, + testSuite: ({ getContainer }) => { + let container + + beforeAll(() => { + container = getContainer() + }) + + describe("Create return order workflow", () => { + let shippingOption: ShippingOptionDTO + let region: RegionDTO + let location: StockLocationDTO + let product: ProductDTO + + let orderService: IOrderModuleService + + beforeEach(async () => { + const fixtures = await prepareDataFixtures({ + container, + }) + + shippingOption = fixtures.shippingOption + region = fixtures.region + location = fixtures.location + product = fixtures.product + + orderService = container.resolve(ModuleRegistrationName.ORDER) + }) + + it("should create a return order", async () => { + const order = await createOrderFixture({ container, product }) + const createReturnOrderData: OrderWorkflow.CreateOrderReturnWorkflowInput = + { + order_id: order.id, + return_shipping: { + option_id: shippingOption.id, + }, + items: [ + { + id: order.items![0].id, + quantity: 1, + }, + ], + } + + await createReturnOrderWorkflow(container).run({ + input: createReturnOrderData, + throwOnError: false, + }) + + const remoteQuery = container.resolve( + ContainerRegistrationKeys.REMOTE_QUERY + ) + const remoteQueryObject = remoteQueryObjectFromString({ + entryPoint: "order", + variables: { + id: order.id, + }, + fields: [ + "*", + "items.*", + "shipping_methods.*", + "total", + "item_total", + "fulfillments.*", + ], + }) + + const [returnOrder] = await remoteQuery(remoteQueryObject) + + expect(returnOrder).toEqual( + expect.objectContaining({ + id: expect.any(String), + display_id: 1, + region_id: "test_region_idclear", + customer_id: "joe", + version: 2, + sales_channel_id: "test", // TODO: What about order with a sales channel but a shipping option link to a stock from another channel? + status: "pending", + is_draft_order: false, + email: "foo@bar.com", + currency_code: "usd", + shipping_address_id: expect.any(String), + billing_address_id: expect.any(String), + items: [ + expect.objectContaining({ + id: order.items![0].id, + title: "Custom Item 2", + variant_sku: product.variants[0].sku, + variant_title: product.variants[0].title, + requires_shipping: true, + is_discountable: true, + is_tax_inclusive: false, + compare_at_unit_price: null, + unit_price: 50, + quantity: 1, + detail: expect.objectContaining({ + id: expect.any(String), + order_id: expect.any(String), + version: 2, + item_id: expect.any(String), + quantity: 1, + fulfilled_quantity: 1, + shipped_quantity: 1, + return_requested_quantity: 1, + return_received_quantity: 0, + return_dismissed_quantity: 0, + written_off_quantity: 0, + }), + }), + ], + shipping_methods: expect.arrayContaining([ + expect.objectContaining({ + id: expect.any(String), + name: "Test shipping method", + description: null, + is_tax_inclusive: false, + shipping_option_id: null, + amount: 10, + order_id: expect.any(String), + }), + expect.objectContaining({ + id: expect.any(String), + name: shippingOption.name, + description: null, + is_tax_inclusive: false, + shipping_option_id: shippingOption.id, + amount: 10, + order_id: expect.any(String), + }), + ]), + fulfillments: [ + expect.objectContaining({ + id: expect.any(String), + location_id: location.id, + provider_id: providerId, + shipping_option_id: shippingOption.id, + // TODO: Validate the address once we are fixed on it + /*delivery_address: { + id: "fuladdr_01HY0RTAP0P1EEAFK7BXJ0BKBN", + },*/ + }), + ], + }) + ) + }) + }) + }, +}) diff --git a/packages/core/core-flows/src/common/index.ts b/packages/core/core-flows/src/common/index.ts index e99533ae88..013c43af22 100644 --- a/packages/core/core-flows/src/common/index.ts +++ b/packages/core/core-flows/src/common/index.ts @@ -1,2 +1,3 @@ export * from "./steps/remove-remote-links" export * from "./steps/use-remote-query" +export * from "./steps/create-remote-links" diff --git a/packages/core/core-flows/src/common/steps/create-remote-links.ts b/packages/core/core-flows/src/common/steps/create-remote-links.ts new file mode 100644 index 0000000000..08002cc391 --- /dev/null +++ b/packages/core/core-flows/src/common/steps/create-remote-links.ts @@ -0,0 +1,29 @@ +import { LinkDefinition, RemoteLink } from "@medusajs/modules-sdk" +import { createStep, StepResponse } from "@medusajs/workflows-sdk" + +import { ContainerRegistrationKeys } from "@medusajs/utils" + +type CreateRemoteLinksStepInput = LinkDefinition[] + +export const createLinksStepId = "create-links" +export const createLinkStep = createStep( + createLinksStepId, + async (data: CreateRemoteLinksStepInput, { container }) => { + const link = container.resolve( + ContainerRegistrationKeys.REMOTE_LINK + ) + await link.create(data) + + return new StepResponse(data, data) + }, + async (createdLinks, { container }) => { + if (!createdLinks) { + return + } + + const link = container.resolve( + ContainerRegistrationKeys.REMOTE_LINK + ) + await link.dismiss(createdLinks) + } +) diff --git a/packages/core/core-flows/src/order/steps/create-return.ts b/packages/core/core-flows/src/order/steps/create-return.ts new file mode 100644 index 0000000000..3d021256af --- /dev/null +++ b/packages/core/core-flows/src/order/steps/create-return.ts @@ -0,0 +1,29 @@ +import { ModuleRegistrationName } from "@medusajs/modules-sdk" +import { CreateOrderReturnDTO, IOrderModuleService } from "@medusajs/types" +import { createStep, StepResponse } from "@medusajs/workflows-sdk" + +type CreateReturnStepInput = CreateOrderReturnDTO + +export const createReturnStepId = "create-return" +export const createReturnStep = createStep( + createReturnStepId, + async (data: CreateReturnStepInput, { container }) => { + const service = container.resolve( + ModuleRegistrationName.ORDER + ) + + const created = await service.createReturn(data) + return new StepResponse(created, created) + }, + async (createdId, { container }) => { + if (!createdId) { + return + } + + const service = container.resolve( + ModuleRegistrationName.ORDER + ) + + // TODO: delete return + } +) diff --git a/packages/core/core-flows/src/order/steps/get-item-tax-lines.ts b/packages/core/core-flows/src/order/steps/get-item-tax-lines.ts index 50294f0f82..3ecb11bd1e 100644 --- a/packages/core/core-flows/src/order/steps/get-item-tax-lines.ts +++ b/packages/core/core-flows/src/order/steps/get-item-tax-lines.ts @@ -6,12 +6,12 @@ import { OrderShippingMethodDTO, OrderWorkflowDTO, ShippingTaxLineDTO, - TaxCalculationContext, TaxableItemDTO, TaxableShippingDTO, + TaxCalculationContext, } from "@medusajs/types" import { MedusaError } from "@medusajs/utils" -import { StepResponse, createStep } from "@medusajs/workflows-sdk" +import { createStep, StepResponse } from "@medusajs/workflows-sdk" interface StepInput { order: OrderWorkflowDTO @@ -104,8 +104,8 @@ export const getOrderItemTaxLinesStep = createStep( async (data: StepInput, { container }) => { const { order, - items, - shipping_methods: shippingMethods, + items = [], + shipping_methods: shippingMethods = [], force_tax_calculation: forceTaxCalculation = false, } = data const taxService = container.resolve( @@ -123,15 +123,19 @@ export const getOrderItemTaxLinesStep = createStep( return new StepResponse(stepResponseData) } - stepResponseData.lineItemTaxLines = (await taxService.getTaxLines( - normalizeLineItemsForTax(order, items), - taxContext - )) as ItemTaxLineDTO[] + if (items.length) { + stepResponseData.lineItemTaxLines = (await taxService.getTaxLines( + normalizeLineItemsForTax(order, items), + taxContext + )) as ItemTaxLineDTO[] + } - stepResponseData.shippingMethodsTaxLines = (await taxService.getTaxLines( - normalizeLineItemsForShipping(order, shippingMethods), - taxContext - )) as ShippingTaxLineDTO[] + if (shippingMethods.length) { + stepResponseData.shippingMethodsTaxLines = (await taxService.getTaxLines( + normalizeLineItemsForShipping(order, shippingMethods), + taxContext + )) as ShippingTaxLineDTO[] + } return new StepResponse(stepResponseData) } diff --git a/packages/core/core-flows/src/order/workflows/create-return.ts b/packages/core/core-flows/src/order/workflows/create-return.ts new file mode 100644 index 0000000000..4cfc4d1431 --- /dev/null +++ b/packages/core/core-flows/src/order/workflows/create-return.ts @@ -0,0 +1,339 @@ +import { + CreateOrderShippingMethodDTO, + FulfillmentWorkflow, + OrderDTO, + OrderWorkflow, + ShippingOptionDTO, + WithCalculatedPrice, +} from "@medusajs/types" +import { + createWorkflow, + transform, + WorkflowData, +} from "@medusajs/workflows-sdk" +import { createLinkStep, useRemoteQueryStep } from "../../common" +import { + arrayDifference, + ContainerRegistrationKeys, + isDefined, + MathBN, + MedusaError, + Modules, +} from "@medusajs/utils" +import { updateOrderTaxLinesStep } from "../steps" +import { createReturnStep } from "../steps/create-return" +import { createFulfillmentWorkflow } from "../../fulfillment" + +function throwIfOrderIsCancelled({ order }: { order: OrderDTO }) { + // TODO: need work, check canceled + if (false /*order.canceled_at*/) { + throw new MedusaError( + MedusaError.Types.INVALID_DATA, + `Order with id ${order.id} has been cancelled.` + ) + } +} + +function throwIfItemsDoesNotExistsInOrder({ + order, + inputItems, +}: { + order: Pick + inputItems: OrderWorkflow.CreateOrderReturnWorkflowInput["items"] +}) { + const orderItemIds = order.items?.map((i) => i.id) ?? [] + const inputItemIds = inputItems.map((i) => i.id) + const diff = arrayDifference(inputItemIds, orderItemIds) + + if (diff.length) { + throw new MedusaError( + MedusaError.Types.INVALID_DATA, + `Items with ids ${diff.join(", ")} does not exist in order with id ${ + order.id + }.` + ) + } +} + +function validateReturnReasons( + { + orderId, + inputItems, + }: { + orderId: string + inputItems: OrderWorkflow.CreateOrderReturnWorkflowInput["items"] + }, + { container } +) { + const reasonIds = inputItems.map((i) => i.reason_id).filter(Boolean) + + if (!reasonIds.length) { + return + } + + const remoteQuery = container.resolve(ContainerRegistrationKeys.REMOTE_QUERY) + + const returnReasons = remoteQuery({ + entry_point: "return_reasons", + fields: ["return_reason_children.*"], + variables: { id: [inputItems.map((item) => item.reason_id)] }, + }) + + const reasons = returnReasons.map((r) => r.id) + const hasInvalidReasons = reasons.filter( + // We do not allow for root reason to be applied + (reason) => reason.return_reason_children.length > 0 + ) + const hasNonExistingReasons = arrayDifference(reasonIds, reasons) + + if (hasNonExistingReasons.length) { + throw new MedusaError( + MedusaError.Types.INVALID_DATA, + `Return reason with id ${hasNonExistingReasons.join( + ", " + )} does not exists.` + ) + } + + if (hasInvalidReasons.length()) { + throw new MedusaError( + MedusaError.Types.INVALID_DATA, + `Cannot apply return reason with id ${hasInvalidReasons.join( + ", " + )} to order with id ${orderId}. Return reason has nested reasons.` + ) + } +} + +function prepareShippingMethodData({ + orderId, + inputShippingOption, + returnShippingOption, +}: { + orderId: string + inputShippingOption: OrderWorkflow.CreateOrderReturnWorkflowInput["return_shipping"] + returnShippingOption: ShippingOptionDTO & WithCalculatedPrice +}) { + const obj: CreateOrderShippingMethodDTO = { + name: returnShippingOption.name, + order_id: orderId, + shipping_option_id: returnShippingOption.id, + amount: 0, + data: {}, + // Computed later in the flow + tax_lines: [], + adjustments: [], + } + + if (isDefined(inputShippingOption.price) && inputShippingOption.price >= 0) { + obj.amount = inputShippingOption.price + } else { + if (returnShippingOption.price_type === "calculated") { + // TODO: retrieve calculated price and assign to amount + } else { + obj.amount = returnShippingOption.calculated_price.calculated_amount + } + } + + return obj +} + +function validateCustomRefundAmount({ + order, + refundAmount, +}: { + order: Pick + refundAmount?: number +}) { + // validate that the refund prop input is less than order.item_total (item total) + // TODO: Probably this amount should be retrieved from the payments linked to the order + if (refundAmount && MathBN.gt(refundAmount, order.item_total)) { + throw new MedusaError( + MedusaError.Types.INVALID_DATA, + `Refund amount cannot be greater than order total.` + ) + } +} + +function prepareFulfillmentData({ + order, + input, + returnShippingOption, +}: { + order: OrderDTO + input: OrderWorkflow.CreateOrderReturnWorkflowInput + returnShippingOption: { + id: string + provider_id: string + service_zone: { fulfillment_set: { location?: { id: string } } } + } +}) { + const inputItems = input.items + const orderItemsMap = new Map["items"][0]>( + order.items!.map((i) => [i.id, i]) + ) + const fulfillmentItems = inputItems.map((i) => { + const orderItem = orderItemsMap.get(i.id)! + return { + line_item_id: i.id, + quantity: i.quantity, + return_quantity: i.quantity, + title: orderItem.variant_title ?? orderItem.title, + sku: orderItem.variant_sku || "", + barcode: orderItem.variant_barcode || "", + } as FulfillmentWorkflow.CreateFulfillmentItemWorkflowDTO + }) + + let locationId: string | undefined = input.location_id + if (!locationId) { + locationId = returnShippingOption.service_zone.fulfillment_set.location?.id + } + + if (!locationId) { + throw new MedusaError( + MedusaError.Types.INVALID_DATA, + `Cannot create return without stock location, either provide a location or you should link the shipping option ${returnShippingOption.id} to a stock location.` + ) + } + + return { + input: { + location_id: locationId, + provider_id: returnShippingOption.provider_id, + shipping_option_id: input.return_shipping.option_id, + items: fulfillmentItems, + labels: [] as FulfillmentWorkflow.CreateFulfillmentLabelWorkflowDTO[], + delivery_address: order.shipping_address ?? ({} as any), // TODO: should it be the stock location address? + order: {} as FulfillmentWorkflow.CreateFulfillmentOrderWorkflowDTO, // TODO see what todo here, is that even necessary? + }, + } +} + +function prepareReturnShippingOptionQueryVariables({ + order, + input, +}: { + order: { + currency_code: string + region_id?: string + } + input: { + return_shipping: OrderWorkflow.CreateOrderReturnWorkflowInput["return_shipping"] + } +}) { + const variables = { + id: input.return_shipping.option_id, + calculated_price: { + context: { + currency_code: order.currency_code, + }, + }, + } + + if (order.region_id) { + variables.calculated_price.context["region_id"] = order.region_id + } + + return variables +} + +export const createReturnOrderWorkflowId = "create-return-order" +export const createReturnOrderWorkflow = createWorkflow( + createReturnOrderWorkflowId, + ( + input: WorkflowData + ): WorkflowData => { + const order: OrderDTO = useRemoteQueryStep({ + entry_point: "orders", + fields: [ + "id", + "region_id", + "currency_code", + "total", + "item_total", + "items.*", + ], + variables: { id: input.order_id }, + list: false, + throw_if_key_not_found: true, + }) + + transform({ order }, throwIfOrderIsCancelled) + transform( + { order, inputItems: input.items }, + throwIfItemsDoesNotExistsInOrder + ) + transform( + { orderId: input.order_id, inputItems: input.items }, + validateReturnReasons + ) + transform( + { order, refundAmount: input.refund_amount }, + validateCustomRefundAmount + ) + + const returnShippingOptionsVariables = transform( + { input, order }, + prepareReturnShippingOptionQueryVariables + ) + + const returnShippingOption = useRemoteQueryStep({ + entry_point: "shipping_options", + fields: [ + "id", + "price_type", + "name", + "provider_id", + "calculated_price.calculated_amount", + "service_zone.fulfillment_set.location.id", + ], + variables: returnShippingOptionsVariables, + list: false, + throw_if_key_not_found: true, + }).config({ name: "return-shipping-option" }) + + const shippingMethodData = transform( + { + orderId: input.order_id, + inputShippingOption: input.return_shipping, + returnShippingOption, + }, + prepareShippingMethodData + ) + + createReturnStep({ + order_id: input.order_id, + items: input.items, + shipping_method: shippingMethodData, + created_by: input.created_by, + }) + + updateOrderTaxLinesStep({ + order_id: input.order_id, + shipping_methods: [shippingMethodData as any], // The types does not seems correct in that step and expect too many things compared to the actual needs + }) + + const fulfillmentData = transform( + { order, input, returnShippingOption }, + prepareFulfillmentData + ) + + const fulfillment = createFulfillmentWorkflow.runAsStep(fulfillmentData) + + // TODO call the createReturn from the fulfillment provider + + const link = transform( + { order_id: input.order_id, fulfillment }, + (data) => { + return [ + { + [Modules.ORDER]: { order_id: data.order_id }, + [Modules.FULFILLMENT]: { fulfillment_id: data.fulfillment.id }, + }, + ] + } + ) + + createLinkStep(link) + } +) diff --git a/packages/core/core-flows/src/order/workflows/index.ts b/packages/core/core-flows/src/order/workflows/index.ts index 40bedfb9fb..cd814fee4c 100644 --- a/packages/core/core-flows/src/order/workflows/index.ts +++ b/packages/core/core-flows/src/order/workflows/index.ts @@ -1,2 +1,3 @@ export * from "./create-orders" export * from "./update-tax-lines" +export * from "./create-return" diff --git a/packages/core/modules-sdk/src/remote-link.ts b/packages/core/modules-sdk/src/remote-link.ts index 21b25d7b7f..2eb5209660 100644 --- a/packages/core/modules-sdk/src/remote-link.ts +++ b/packages/core/modules-sdk/src/remote-link.ts @@ -14,7 +14,7 @@ export type DeleteEntityInput = { } export type RestoreEntityInput = DeleteEntityInput -type LinkDefinition = { +export type LinkDefinition = { [moduleName: string]: { [fieldName: string]: string } diff --git a/packages/core/types/src/common/index.ts b/packages/core/types/src/common/index.ts index d3a280a643..2717f4ecc6 100644 --- a/packages/core/types/src/common/index.ts +++ b/packages/core/types/src/common/index.ts @@ -3,3 +3,4 @@ export * from "./rule" export * from "./batch" export * from "./config-module" export * from "./medusa-container" +export * from "./with-calculated" diff --git a/packages/core/types/src/common/with-calculated.ts b/packages/core/types/src/common/with-calculated.ts new file mode 100644 index 0000000000..374fc116a1 --- /dev/null +++ b/packages/core/types/src/common/with-calculated.ts @@ -0,0 +1,3 @@ +export interface WithCalculatedPrice { + calculated_price: { calculated_amount: number } +} diff --git a/packages/core/types/src/order/common.ts b/packages/core/types/src/order/common.ts index 9342d3517d..61b4685c38 100644 --- a/packages/core/types/src/order/common.ts +++ b/packages/core/types/src/order/common.ts @@ -2,6 +2,19 @@ import { BaseFilterable } from "../dal" import { OperatorMap } from "../dal/utils" import { BigNumberRawValue, BigNumberValue } from "../totals" +export type ChangeActionType = + | "CANCEL" + | "CANCEL_RETURN" + | "FULFILL_ITEM" + | "ITEM_ADD" + | "ITEM_REMOVE" + | "RECEIVE_DAMAGED_RETURN_ITEM" + | "RECEIVE_RETURN_ITEM" + | "RETURN_ITEM" + | "SHIPPING_ADD" + | "SHIP_ITEM" + | "WRITE_OFF_ITEM" + export type OrderSummaryDTO = { total: BigNumberValue subtotal: BigNumberValue @@ -1187,7 +1200,7 @@ export interface OrderChangeActionDTO { /** * The action of the order change action */ - action: string + action: ChangeActionType /** * The details of the order change action */ diff --git a/packages/core/types/src/order/mutations.ts b/packages/core/types/src/order/mutations.ts index ba594a8db4..dcbc5327fb 100644 --- a/packages/core/types/src/order/mutations.ts +++ b/packages/core/types/src/order/mutations.ts @@ -1,5 +1,6 @@ import { BigNumberInput } from "../totals" import { + ChangeActionType, OrderItemDTO, OrderLineItemDTO, OrderReturnReasonDTO, @@ -300,7 +301,7 @@ export interface CreateOrderChangeActionDTO { version?: number reference?: string reference_id?: string - action: string + action: ChangeActionType internal_note?: string amount?: BigNumberInput details?: Record diff --git a/packages/core/types/src/workflow/index.ts b/packages/core/types/src/workflow/index.ts index 4c44c78b6c..9b592a1093 100644 --- a/packages/core/types/src/workflow/index.ts +++ b/packages/core/types/src/workflow/index.ts @@ -9,3 +9,4 @@ export * as ProductCategoryWorkflow from "./product-category" export * as RegionWorkflow from "./region" export * as ReservationWorkflow from "./reservation" export * as UserWorkflow from "./user" +export * as OrderWorkflow from "./order" diff --git a/packages/core/types/src/workflow/order/create-return-order.ts b/packages/core/types/src/workflow/order/create-return-order.ts new file mode 100644 index 0000000000..8ce1a3b01e --- /dev/null +++ b/packages/core/types/src/workflow/order/create-return-order.ts @@ -0,0 +1,26 @@ +import { BigNumberInput } from "../../totals" + +interface CreateOrderReturnItem { + id: string + quantity: BigNumberInput + internal_note?: string + reason_id?: string + metadata?: Record +} + +export interface CreateOrderReturnWorkflowInput { + order_id: string + created_by?: string // The id of the authenticated user + items: CreateOrderReturnItem[] + return_shipping: { + option_id: string + price?: number + } + note?: string + receive_now?: boolean + refund_amount?: number + /** + * Default fallback to the shipping option location id + */ + location_id?: string +} diff --git a/packages/core/types/src/workflow/order/index.ts b/packages/core/types/src/workflow/order/index.ts new file mode 100644 index 0000000000..29b175eae9 --- /dev/null +++ b/packages/core/types/src/workflow/order/index.ts @@ -0,0 +1 @@ +export * from "./create-return-order" diff --git a/packages/core/utils/src/link/links.ts b/packages/core/utils/src/link/links.ts index 745da8aa52..1e4d3cec66 100644 --- a/packages/core/utils/src/link/links.ts +++ b/packages/core/utils/src/link/links.ts @@ -80,4 +80,10 @@ export const LINKS = { Modules.PAYMENT, "payment_collection_id" ), + OrderFulfillment: composeLinkName( + Modules.ORDER, + "order_id", + Modules.FULFILLMENT, + "fulfillment_id" + ), } diff --git a/packages/core/utils/src/modules-sdk/decorators/module.ts b/packages/core/utils/src/modules-sdk/decorators/module.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/core/workflows-sdk/src/utils/composer/helpers/resolve-value.ts b/packages/core/workflows-sdk/src/utils/composer/helpers/resolve-value.ts index b8189fb8ea..599713a6fd 100644 --- a/packages/core/workflows-sdk/src/utils/composer/helpers/resolve-value.ts +++ b/packages/core/workflows-sdk/src/utils/composer/helpers/resolve-value.ts @@ -57,7 +57,7 @@ export async function resolveValue(input, transactionContext) { ) if (typeof parentRef[key] === "object") { - await unwrapInput(parentRef[key], parentRef[key]) + parentRef[key] = await unwrapInput(parentRef[key], parentRef[key]) } } diff --git a/packages/core/workflows-sdk/src/utils/composer/type.ts b/packages/core/workflows-sdk/src/utils/composer/type.ts index e7f2691042..31fa467ce8 100644 --- a/packages/core/workflows-sdk/src/utils/composer/type.ts +++ b/packages/core/workflows-sdk/src/utils/composer/type.ts @@ -201,7 +201,7 @@ export type ReturnWorkflow< runAsStep: ({ input, }: { - input: TData + input: TData | WorkflowData }) => ReturnType> run: ( ...args: Parameters< diff --git a/packages/modules/fulfillment/src/services/fulfillment-module-service.ts b/packages/modules/fulfillment/src/services/fulfillment-module-service.ts index 27bcdba30d..0aa40c53c9 100644 --- a/packages/modules/fulfillment/src/services/fulfillment-module-service.ts +++ b/packages/modules/fulfillment/src/services/fulfillment-module-service.ts @@ -14,17 +14,17 @@ import { UpdateServiceZoneDTO, } from "@medusajs/types" import { + arrayDifference, EmitEvents, FulfillmentUtils, + getSetDifference, InjectManager, InjectTransactionManager, + isString, MedusaContext, MedusaError, Modules, ModulesSdkUtils, - arrayDifference, - getSetDifference, - isString, promiseAll, } from "@medusajs/utils" import { diff --git a/packages/modules/link-modules/src/definitions/fulfillment-set-location.ts b/packages/modules/link-modules/src/definitions/fulfillment-set-location.ts index cc39659544..579d01afdb 100644 --- a/packages/modules/link-modules/src/definitions/fulfillment-set-location.ts +++ b/packages/modules/link-modules/src/definitions/fulfillment-set-location.ts @@ -49,6 +49,9 @@ export const LocationFulfillmentSet: ModuleJoinerConfig = { }, { serviceName: Modules.FULFILLMENT, + fieldAlias: { + location: "locations_link.location", + }, relationship: { serviceName: LINKS.LocationFulfillmentSet, primaryKey: "fulfillment_set_id", diff --git a/packages/modules/link-modules/src/definitions/index.ts b/packages/modules/link-modules/src/definitions/index.ts index 288b03e548..37498fc482 100644 --- a/packages/modules/link-modules/src/definitions/index.ts +++ b/packages/modules/link-modules/src/definitions/index.ts @@ -12,3 +12,4 @@ export * from "./readonly" export * from "./region-payment-provider" export * from "./sales-channel-location" export * from "./shipping-option-price-set" +export * from "./order-fulfillment" diff --git a/packages/modules/link-modules/src/definitions/order-fulfillment.ts b/packages/modules/link-modules/src/definitions/order-fulfillment.ts new file mode 100644 index 0000000000..f134c3370c --- /dev/null +++ b/packages/modules/link-modules/src/definitions/order-fulfillment.ts @@ -0,0 +1,63 @@ +import { Modules } from "@medusajs/modules-sdk" +import { ModuleJoinerConfig } from "@medusajs/types" +import { LINKS } from "@medusajs/utils" + +export const OrderFulfillment: ModuleJoinerConfig = { + serviceName: LINKS.OrderFulfillment, + isLink: true, + databaseConfig: { + tableName: "order_fulfillment", + idPrefix: "orderful", + }, + alias: [ + { + name: ["order_fulfillment", "order_fulfillments"], + args: { + entity: "LinkOrderFulfillment", + }, + }, + ], + primaryKeys: ["id", "order_id", "fulfillment_id"], + relationships: [ + { + serviceName: Modules.ORDER, + primaryKey: "id", + foreignKey: "order_id", + alias: "order", + }, + { + serviceName: Modules.FULFILLMENT, + primaryKey: "id", + foreignKey: "fulfillment_id", + alias: "fulfillments", + args: { + // TODO: We are not suppose to know the module implementation here, wait for later to think about inferring it + methodSuffix: "Fulfillments", + }, + }, + ], + extends: [ + { + serviceName: Modules.ORDER, + fieldAlias: { + fulfillments: "fulfillment_link.fulfillments", + }, + relationship: { + serviceName: LINKS.OrderFulfillment, + primaryKey: "order_id", + foreignKey: "id", + alias: "fulfillment_link", + isList: true, + }, + }, + { + serviceName: Modules.FULFILLMENT, + relationship: { + serviceName: LINKS.OrderFulfillment, + primaryKey: "fulfillment_id", + foreignKey: "id", + alias: "order_link", + }, + }, + ], +} diff --git a/packages/modules/order/src/services/order-module-service.ts b/packages/modules/order/src/services/order-module-service.ts index 41fa14ad26..70f2fe89ab 100644 --- a/packages/modules/order/src/services/order-module-service.ts +++ b/packages/modules/order/src/services/order-module-service.ts @@ -2214,7 +2214,7 @@ export default class OrderModuleService< if (!isString(data.shipping_method)) { const methods = await this.createShippingMethods( data.order_id, - data.shipping_method as any, + [{ order_id: data.order_id, ...data.shipping_method }], sharedContext ) shippingMethodId = methods[0].id From 694434d51a0a035fb9ff42318ededde6e13a5286 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Frane=20Poli=C4=87?= <16856471+fPolic@users.noreply.github.com> Date: Thu, 16 May 2024 19:34:49 +0200 Subject: [PATCH 3/3] fix(dashboard): location UI fixes (#7288) * fix: a few UI fixes * fix: domain name * feat: edit service zone areas * feat: edit shipping option prices * fix: sorting --- .../public/locales/en-US/translation.json | 11 +- .../src/hooks/api/stock-locations.tsx | 2 +- .../providers/router-provider/route-map.tsx | 14 + .../location-general-section.tsx | 17 +- .../components/location/location.tsx | 2 +- .../edit-service-zone-areas-form.tsx | 344 ++++++++++++++++++ .../edit-region-areas-form/index.ts | 1 + .../shipping/service-zone-areas-edit/index.ts | 1 + .../service-zone-areas-edit.tsx | 45 +++ .../create-service-zone-form.tsx | 26 +- .../shipping/service-zone-create/loader.ts | 4 +- .../create-shipping-options-form.tsx | 10 +- .../shipping-options-create/loader.ts | 5 +- .../edit-shipping-options-pricing-form.tsx | 319 ++++++++++++++++ .../create-shipping-options-form/index.ts | 1 + .../shipping-options-edit-pricing/index.ts | 1 + .../shipping-options-edit-pricing.tsx | 30 ++ .../fulfillment/mutations/shipping-option.ts | 8 + 18 files changed, 811 insertions(+), 30 deletions(-) create mode 100644 packages/admin-next/dashboard/src/v2-routes/shipping/service-zone-areas-edit/components/edit-region-areas-form/edit-service-zone-areas-form.tsx create mode 100644 packages/admin-next/dashboard/src/v2-routes/shipping/service-zone-areas-edit/components/edit-region-areas-form/index.ts create mode 100644 packages/admin-next/dashboard/src/v2-routes/shipping/service-zone-areas-edit/index.ts create mode 100644 packages/admin-next/dashboard/src/v2-routes/shipping/service-zone-areas-edit/service-zone-areas-edit.tsx create mode 100644 packages/admin-next/dashboard/src/v2-routes/shipping/shipping-options-edit-pricing/components/create-shipping-options-form/edit-shipping-options-pricing-form.tsx create mode 100644 packages/admin-next/dashboard/src/v2-routes/shipping/shipping-options-edit-pricing/components/create-shipping-options-form/index.ts create mode 100644 packages/admin-next/dashboard/src/v2-routes/shipping/shipping-options-edit-pricing/index.ts create mode 100644 packages/admin-next/dashboard/src/v2-routes/shipping/shipping-options-edit-pricing/shipping-options-edit-pricing.tsx diff --git a/packages/admin-next/dashboard/public/locales/en-US/translation.json b/packages/admin-next/dashboard/public/locales/en-US/translation.json index 89eb9c41db..3fb5f382ca 100644 --- a/packages/admin-next/dashboard/public/locales/en-US/translation.json +++ b/packages/admin-next/dashboard/public/locales/en-US/translation.json @@ -62,6 +62,7 @@ }, "actions": { "save": "Save", + "select": "Select", "saveAsDraft": "Save as draft", "publish": "Publish", "create": "Create", @@ -653,8 +654,8 @@ } }, "shipping": { - "title": "Location & Shipping", - "domain": "Location & Shipping", + "title": "Locations & Shipping", + "domain": "Locations & Shipping", "description": "Choose where you ship and how much you charge for shipping at checkout. Define shipping options specific for your locations.", "createLocation": "Create location", "createLocationDetailsHint": "Specify the details of the location.", @@ -714,10 +715,12 @@ "edit": { "title": "Edit Service Zone" }, + "editAreasTitle": "Manage {{zone}} areas", "deleteWarning": "Are you sure you want to delete \"{{name}}\". This will also delete all assocciated shipping options.", "toast": { "delete": "Zone \"{{name}}\" deleted successfully." }, + "manageAreas": "Manage areas", "editPrices": "Edit prices", "editOption": "Edit option", "optionsLength_one": "shipping option", @@ -745,8 +748,8 @@ "allocation": "Shipping amount", "fixed": "Fixed", "fixedDescription": "Shipping option's price is always the same amount.", - "enable": "Enable in store", - "enableDescription": "Enable or disable the shipping option visiblity in store", + "enable": "Show publicly", + "enableDescription": "When disabled, the shipping option can only be applied by admins.", "calculated": "Calculated", "calculatedDescription": "Shipping option's price is calculated by the fulfillment provider.", "profile": "Shipping profile" diff --git a/packages/admin-next/dashboard/src/hooks/api/stock-locations.tsx b/packages/admin-next/dashboard/src/hooks/api/stock-locations.tsx index 555f94b40b..9683d301b5 100644 --- a/packages/admin-next/dashboard/src/hooks/api/stock-locations.tsx +++ b/packages/admin-next/dashboard/src/hooks/api/stock-locations.tsx @@ -41,7 +41,7 @@ export const useStockLocation = ( ) => { const { data, ...rest } = useQuery({ queryFn: () => client.stockLocations.retrieve(id, query), - queryKey: stockLocationsQueryKeys.detail(id, query), + queryKey: stockLocationsQueryKeys.details(), ...options, }) diff --git a/packages/admin-next/dashboard/src/providers/router-provider/route-map.tsx b/packages/admin-next/dashboard/src/providers/router-provider/route-map.tsx index 4601f7a80e..87701f6484 100644 --- a/packages/admin-next/dashboard/src/providers/router-provider/route-map.tsx +++ b/packages/admin-next/dashboard/src/providers/router-provider/route-map.tsx @@ -744,6 +744,13 @@ export const RouteMap: RouteObject[] = [ "../../v2-routes/shipping/service-zone-edit" ), }, + { + path: "edit-areas", + lazy: () => + import( + "../../v2-routes/shipping/service-zone-areas-edit" + ), + }, { path: "shipping-option", children: [ @@ -764,6 +771,13 @@ export const RouteMap: RouteObject[] = [ "../../v2-routes/shipping/shipping-option-edit" ), }, + { + path: "edit-pricing", + lazy: () => + import( + "../../v2-routes/shipping/shipping-options-edit-pricing" + ), + }, ], }, ], diff --git a/packages/admin-next/dashboard/src/v2-routes/shipping/location-details/components/location-general-section/location-general-section.tsx b/packages/admin-next/dashboard/src/v2-routes/shipping/location-details/components/location-general-section/location-general-section.tsx index 10ad9d22cd..1d1827a234 100644 --- a/packages/admin-next/dashboard/src/v2-routes/shipping/location-details/components/location-general-section/location-general-section.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/shipping/location-details/components/location-general-section/location-general-section.tsx @@ -156,7 +156,7 @@ function ShippingOption({ { label: t("shipping.serviceZone.editPrices"), icon: , - disabled: true, + to: `/settings/shipping/${locationId}/fulfillment-set/${fulfillmentSetId}/service-zone/${option.service_zone_id}/shipping-option/${option.id}/edit-pricing`, }, { label: t("actions.delete"), @@ -199,7 +199,7 @@ function ServiceZoneOptions({ {t("shipping.serviceZone.shippingOptions")} + + + + ) +} + +const EditeServiceZoneSchema = zod.object({ + countries: zod.array(zod.string().length(2)).min(1), +}) + +type EditServiceZoneAreasFormProps = { + fulfillmentSetId: string + locationId: string + zone: ServiceZoneDTO +} + +export function EditServiceZoneAreasForm({ + fulfillmentSetId, + locationId, + zone, +}: EditServiceZoneAreasFormProps) { + const { t } = useTranslation() + const { handleSuccess } = useRouteModal() + const [open, setOpen] = useState(false) + const [rowSelection, setRowSelection] = useState( + zone.geo_zones + .map((z) => z.country_code) + .reduce((acc, v) => { + acc[v] = true + return acc + }, {}) + ) + + const form = useForm>({ + defaultValues: { + countries: zone.geo_zones.map((z) => z.country_code), + }, + resolver: zodResolver(EditeServiceZoneSchema), + }) + + const { mutateAsync: editServiceZone, isPending: isLoading } = + useUpdateServiceZone(fulfillmentSetId, zone.id, locationId) + + const handleSubmit = form.handleSubmit(async (data) => { + try { + await editServiceZone({ + geo_zones: data.countries.map((iso2) => ({ + country_code: iso2, + type: "country", + })), + }) + } catch (e) { + toast.error(t("general.error"), { + description: e.message, + dismissLabel: t("general.close"), + }) + } + + handleSuccess() + }) + + const handleOpenChange = (open: boolean) => { + setOpen(open) + } + + const { searchParams, raw } = useCountryTableQuery({ + pageSize: PAGE_SIZE, + prefix: PREFIX, + }) + const { countries, count } = useCountries({ + countries: staticCountries.map((c, i) => ({ + display_name: c.display_name, + name: c.name, + id: i as any, + iso_2: c.iso_2, + iso_3: c.iso_3, + num_code: c.num_code, + region_id: null, + region: {} as RegionDTO, + })), + ...searchParams, + }) + + const columns = useColumns() + + const { table } = useDataTable({ + data: countries || [], + columns, + count, + enablePagination: true, + enableRowSelection: true, + getRowId: (row) => row.iso_2, + pageSize: PAGE_SIZE, + rowSelection: { + state: rowSelection, + updater: setRowSelection, + }, + prefix: PREFIX, + }) + + const countriesWatch = form.watch("countries") + + const onCountriesSave = () => { + form.setValue("countries", Object.keys(rowSelection)) + setOpen(false) + } + + const removeCountry = (iso2: string) => { + const state = { ...rowSelection } + delete state[iso2] + setRowSelection(state) + + form.setValue( + "countries", + countriesWatch.filter((c) => c !== iso2) + ) + } + + const clearAll = () => { + setRowSelection({}) + form.setValue("countries", []) + } + + const selectedCountries = useMemo(() => { + return staticCountries.filter((c) => c.iso_2 in rowSelection) + }, [countriesWatch]) + + useEffect(() => { + // set selected rows from form state on open + if (open) { + setRowSelection( + countriesWatch.reduce((acc, c) => { + acc[c] = true + return acc + }, {}) + ) + } + }, [open]) + + const showAreasError = + form.formState.errors["countries"]?.type === "too_small" + + return ( + +
+ +
+ + + + +
+
+ + + + +
+ + {t("shipping.serviceZone.editAreasTitle", { + zone: zone.name, + })} + +
+ +
+
+ + {t("shipping.serviceZone.areas.title")} + + + {t("shipping.serviceZone.areas.description")} + +
+ +
+ {!!selectedCountries.length && ( +
+ {selectedCountries.map((c) => ( + + {c.display_name} + removeCountry(c.iso_2)} + className="text-ui-fg-subtle p-0 px-1 pt-[1px]" + variant="transparent" + > + + + + ))} + +
+ )} + {showAreasError && ( + + {t("shipping.serviceZone.areas.error")} + + )} +
+ +
+ + +
+
+
+
+
+
+ ) +} + +const columnHelper = createColumnHelper() + +const useColumns = () => { + const base = useCountryTableColumns() + + return useMemo( + () => [ + columnHelper.display({ + id: "select", + header: ({ table }) => { + return ( + + table.toggleAllPageRowsSelected(!!value) + } + /> + ) + }, + cell: ({ row }) => { + const isPreselected = !row.getCanSelect() + + return ( + row.toggleSelected(!!value)} + onClick={(e) => { + e.stopPropagation() + }} + /> + ) + }, + }), + ...base, + ], + [base] + ) as ColumnDef[] +} diff --git a/packages/admin-next/dashboard/src/v2-routes/shipping/service-zone-areas-edit/components/edit-region-areas-form/index.ts b/packages/admin-next/dashboard/src/v2-routes/shipping/service-zone-areas-edit/components/edit-region-areas-form/index.ts new file mode 100644 index 0000000000..522cccf066 --- /dev/null +++ b/packages/admin-next/dashboard/src/v2-routes/shipping/service-zone-areas-edit/components/edit-region-areas-form/index.ts @@ -0,0 +1 @@ +export * from "./edit-service-zone-areas-form" diff --git a/packages/admin-next/dashboard/src/v2-routes/shipping/service-zone-areas-edit/index.ts b/packages/admin-next/dashboard/src/v2-routes/shipping/service-zone-areas-edit/index.ts new file mode 100644 index 0000000000..fc088b9f88 --- /dev/null +++ b/packages/admin-next/dashboard/src/v2-routes/shipping/service-zone-areas-edit/index.ts @@ -0,0 +1 @@ +export { ServiceZoneAreasEdit as Component } from "./service-zone-areas-edit" diff --git a/packages/admin-next/dashboard/src/v2-routes/shipping/service-zone-areas-edit/service-zone-areas-edit.tsx b/packages/admin-next/dashboard/src/v2-routes/shipping/service-zone-areas-edit/service-zone-areas-edit.tsx new file mode 100644 index 0000000000..0bccfa6b4a --- /dev/null +++ b/packages/admin-next/dashboard/src/v2-routes/shipping/service-zone-areas-edit/service-zone-areas-edit.tsx @@ -0,0 +1,45 @@ +import { json, useParams } from "react-router-dom" + +import { RouteFocusModal } from "../../../components/route-modal" +import { EditServiceZoneAreasForm } from "./components/edit-region-areas-form" +import { useStockLocation } from "../../../hooks/api/stock-locations" + +export const ServiceZoneAreasEdit = () => { + const { location_id, fset_id, zone_id } = useParams() + + const { stock_location, isPending, isError, error } = useStockLocation( + location_id!, + { + // NOTE: use same query for all details page subroutes & fetches + fields: + "name,*sales_channels,address.city,address.country_code,fulfillment_sets.type,fulfillment_sets.name,*fulfillment_sets.service_zones.geo_zones,*fulfillment_sets.service_zones,*fulfillment_sets.service_zones.shipping_options,*fulfillment_sets.service_zones.shipping_options.rules,*fulfillment_sets.service_zones.shipping_options.shipping_profile", + } + ) + + const zone = stock_location?.fulfillment_sets + .find((f) => f.id === fset_id) + ?.service_zones.find((z) => z.id === zone_id) + + if (isError) { + throw error + } + + if (!isPending && !zone) { + throw json( + { message: `Service zone with ID ${zone_id} was not found` }, + 404 + ) + } + + return ( + + {!isPending && zone && ( + + )} + + ) +} diff --git a/packages/admin-next/dashboard/src/v2-routes/shipping/service-zone-create/components/create-service-zone-form/create-service-zone-form.tsx b/packages/admin-next/dashboard/src/v2-routes/shipping/service-zone-create/components/create-service-zone-form/create-service-zone-form.tsx index a29b5e412b..3263a0f0e4 100644 --- a/packages/admin-next/dashboard/src/v2-routes/shipping/service-zone-create/components/create-service-zone-form/create-service-zone-form.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/shipping/service-zone-create/components/create-service-zone-form/create-service-zone-form.tsx @@ -16,10 +16,11 @@ import { IconButton, Input, Text, + toast, } from "@medusajs/ui" import { FulfillmentSetDTO, RegionCountryDTO, RegionDTO } from "@medusajs/types" import { useTranslation } from "react-i18next" -import { Map, XMark, XMarkMini } from "@medusajs/icons" +import { XMarkMini } from "@medusajs/icons" import { RouteFocusModal, @@ -87,13 +88,20 @@ export function CreateServiceZoneForm({ useCreateServiceZone(locationId, fulfillmentSet.id) const handleSubmit = form.handleSubmit(async (data) => { - await createServiceZone({ - name: data.name, - geo_zones: data.countries.map((iso2) => ({ - country_code: iso2, - type: "country", - })), - }) + try { + await createServiceZone({ + name: data.name, + geo_zones: data.countries.map((iso2) => ({ + country_code: iso2, + type: "country", + })), + }) + } catch (e) { + toast.error(t("general.error"), { + description: e.message, + dismissLabel: t("general.close"), + }) + } handleSuccess() }) @@ -201,7 +209,7 @@ export function CreateServiceZoneForm({ -
+
{t("shipping.fulfillmentSet.create.title", { fulfillmentSet: fulfillmentSet.name, diff --git a/packages/admin-next/dashboard/src/v2-routes/shipping/service-zone-create/loader.ts b/packages/admin-next/dashboard/src/v2-routes/shipping/service-zone-create/loader.ts index d390458e11..5d04be15b1 100644 --- a/packages/admin-next/dashboard/src/v2-routes/shipping/service-zone-create/loader.ts +++ b/packages/admin-next/dashboard/src/v2-routes/shipping/service-zone-create/loader.ts @@ -6,7 +6,9 @@ import { StockLocationRes } from "../../../types/api-responses" import { stockLocationsQueryKeys } from "../../../hooks/api/stock-locations" const fulfillmentSetCreateQuery = (id: string) => ({ - queryKey: stockLocationsQueryKeys.detail(id), + queryKey: stockLocationsQueryKeys.detail(id, { + fields: "*fulfillment_sets", + }), queryFn: async () => client.stockLocations.retrieve(id, { fields: "*fulfillment_sets", diff --git a/packages/admin-next/dashboard/src/v2-routes/shipping/shipping-options-create/components/create-shipping-options-form/create-shipping-options-form.tsx b/packages/admin-next/dashboard/src/v2-routes/shipping/shipping-options-create/components/create-shipping-options-form/create-shipping-options-form.tsx index 4698810e51..87283058ec 100644 --- a/packages/admin-next/dashboard/src/v2-routes/shipping/shipping-options-create/components/create-shipping-options-form/create-shipping-options-form.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/shipping/shipping-options-create/components/create-shipping-options-form/create-shipping-options-form.tsx @@ -45,7 +45,7 @@ type StepStatus = { [key in Tab]: ProgressStatus } -const CreateServiceZoneSchema = zod.object({ +const CreateShippingOptionSchema = zod.object({ name: zod.string().min(1), price_type: zod.nativeEnum(ShippingAllocation), enabled_in_store: zod.boolean().optional(), @@ -55,7 +55,7 @@ const CreateServiceZoneSchema = zod.object({ currency_prices: zod.record(zod.string(), zod.string().optional()), }) -type CreateServiceZoneFormProps = { +type CreateShippingOptionFormProps = { zone: ServiceZoneDTO isReturn?: boolean } @@ -63,7 +63,7 @@ type CreateServiceZoneFormProps = { export function CreateShippingOptionsForm({ zone, isReturn, -}: CreateServiceZoneFormProps) { +}: CreateShippingOptionFormProps) { const { t } = useTranslation() const { handleSuccess } = useRouteModal() const [tab, setTab] = React.useState(Tab.DETAILS) @@ -77,7 +77,7 @@ export function CreateShippingOptionsForm({ fields: "id,currency_code", }) - const form = useForm>({ + const form = useForm>({ defaultValues: { name: "", price_type: ShippingAllocation.FlatRate, @@ -87,7 +87,7 @@ export function CreateShippingOptionsForm({ region_prices: {}, currency_prices: {}, }, - resolver: zodResolver(CreateServiceZoneSchema), + resolver: zodResolver(CreateShippingOptionSchema), }) const isCalculatedPriceType = diff --git a/packages/admin-next/dashboard/src/v2-routes/shipping/shipping-options-create/loader.ts b/packages/admin-next/dashboard/src/v2-routes/shipping/shipping-options-create/loader.ts index 234ea0cb14..ed7d531f00 100644 --- a/packages/admin-next/dashboard/src/v2-routes/shipping/shipping-options-create/loader.ts +++ b/packages/admin-next/dashboard/src/v2-routes/shipping/shipping-options-create/loader.ts @@ -6,7 +6,10 @@ import { StockLocationRes } from "../../../types/api-responses" import { stockLocationsQueryKeys } from "../../../hooks/api/stock-locations" const fulfillmentSetCreateQuery = (id: string) => ({ - queryKey: stockLocationsQueryKeys.list(), // Use the list cache key for now + queryKey: stockLocationsQueryKeys.detail(id, { + fields: + "*fulfillment_sets,*fulfillment_sets.service_zones,*fulfillment_sets.service_zones.shipping_options", + }), queryFn: async () => client.stockLocations.retrieve(id, { fields: diff --git a/packages/admin-next/dashboard/src/v2-routes/shipping/shipping-options-edit-pricing/components/create-shipping-options-form/edit-shipping-options-pricing-form.tsx b/packages/admin-next/dashboard/src/v2-routes/shipping/shipping-options-edit-pricing/components/create-shipping-options-form/edit-shipping-options-pricing-form.tsx new file mode 100644 index 0000000000..c5d0dbcc68 --- /dev/null +++ b/packages/admin-next/dashboard/src/v2-routes/shipping/shipping-options-edit-pricing/components/create-shipping-options-form/edit-shipping-options-pricing-form.tsx @@ -0,0 +1,319 @@ +import { useForm } from "react-hook-form" +import { zodResolver } from "@hookform/resolvers/zod" +import React, { useEffect, useMemo, useState } from "react" +import * as zod from "zod" + +import { Button, toast } from "@medusajs/ui" +import { + CurrencyDTO, + PriceDTO, + ProductVariantDTO, + RegionDTO, + ShippingOptionDTO, +} from "@medusajs/types" +import { useTranslation } from "react-i18next" + +import { + RouteFocusModal, + useRouteModal, +} from "../../../../../components/route-modal" +import { + getDbAmount, + getPresentationalAmount, +} from "../../../../../lib/money-amount-helpers" +import { useRegions } from "../../../../../hooks/api/regions" +import { useStore } from "../../../../../hooks/api/store.tsx" +import { useCurrencies } from "../../../../../hooks/api/currencies" +import { ColumnDef, createColumnHelper } from "@tanstack/react-table" +import { ExtendedProductDTO } from "../../../../../types/api-responses" +import { CurrencyCell } from "../../../../../components/grid/grid-cells/common/currency-cell" +import { DataGridMeta } from "../../../../../components/grid/types" +import { DataGrid } from "../../../../../components/grid/data-grid" +import { useUpdateShippingOptions } from "../../../../../hooks/api/shipping-options.ts" + +const getInitialCurrencyPrices = (prices: PriceDTO[]) => { + const ret: Record = {} + prices.forEach((p) => { + if (p.price_rules!.length) { + // this is a region price + return + } + ret[p.currency_code!] = getPresentationalAmount( + p.amount as number, + p.currency_code! + ) + }) + return ret +} + +const getInitialRegionPrices = (prices: PriceDTO[]) => { + const ret: Record = {} + prices.forEach((p) => { + if (p.price_rules!.length) { + const regionId = p.price_rules![0].value + ret[regionId] = getPresentationalAmount( + p.amount as number, + p.currency_code! + ) + } + }) + + return ret +} + +const EditShippingOptionPricingSchema = zod.object({ + region_prices: zod.record( + zod.string(), + zod.string().or(zod.number()).optional() + ), + currency_prices: zod.record( + zod.string(), + zod.string().or(zod.number()).optional() + ), +}) + +enum ColumnType { + REGION = "region", + CURRENCY = "currency", +} + +type EnabledColumnRecord = Record + +type EditShippingOptionPricingFormProps = { + shippingOption: ShippingOptionDTO & { prices: PriceDTO[] } +} + +export function EditShippingOptionsPricingForm({ + shippingOption, +}: EditShippingOptionPricingFormProps) { + const { t } = useTranslation() + const { handleSuccess } = useRouteModal() + + const form = useForm>({ + defaultValues: { + region_prices: getInitialRegionPrices(shippingOption.prices), + currency_prices: getInitialCurrencyPrices(shippingOption.prices), + }, + resolver: zodResolver(EditShippingOptionPricingSchema), + }) + + const { mutateAsync, isPending: isLoading } = useUpdateShippingOptions( + shippingOption.id + ) + + const { regions } = useRegions() + + const { store, isLoading: isStoreLoading } = useStore() + + const { currencies, isLoading: isCurrenciesLoading } = useCurrencies( + { + code: store?.supported_currency_codes, + }, + { + enabled: !!store, + } + ) + + const [enabledColumns, setEnabledColumns] = useState({}) + + useEffect(() => { + if ( + store?.default_currency_code && + Object.keys(enabledColumns).length === 0 + ) { + setEnabledColumns({ + ...enabledColumns, + [store.default_currency_code]: ColumnType.CURRENCY, + }) + } + }, [store, enabledColumns]) + + const columns = useColumns({ + currencies, + regions, + }) + + const data = useMemo( + () => [[...(currencies || []), ...(regions || [])]], + [currencies, regions] + ) + + const handleSubmit = form.handleSubmit(async (data) => { + const currencyPrices = Object.entries(data.currency_prices) + .map(([code, value]) => { + if (value === "") { + return undefined + } + + const amount = getDbAmount(Number(value), code) + + const priceRecord = { + currency_code: code, + amount: amount, + } + + const price = shippingOption.prices.find( + (p) => p.currency_code === code && !p.price_rules!.length + ) + + // if that currency price is already defined for the SO, we will do an update + if (price) { + priceRecord["id"] = price.id + } + + return priceRecord + }) + .filter((p) => !!p) + + const regionsMap = new Map(regions.map((r) => [r.id, r.currency_code])) + + const regionPrices = Object.entries(data.region_prices) + .map(([region_id, value]) => { + if (value === "") { + return undefined + } + + const code = regionsMap.get(region_id)! + + const amount = getDbAmount(Number(value), code) + + const priceRecord = { + region_id, + amount: amount, + } + + /** + * HACK - when trying to update prices which already have a region price + * we get error: `Price rule with price_id: , rule_type_id: already exist`, + * so for now, we recreate region prices. + */ + + // const price = shippingOption.prices.find( + // (p) => p.price_rules?.[0]?.value === region_id + // ) + // + // if (price) { + // priceRecord["id"] = price.id + // } + + return priceRecord + }) + .filter((p) => !!p) + + try { + await mutateAsync({ + prices: [...currencyPrices, ...regionPrices], + }) + toast.error(t("general.success"), { + dismissLabel: t("general.close"), + }) + handleSuccess() + } catch (e) { + toast.error(t("general.error"), { + description: e.message, + dismissLabel: t("general.close"), + }) + } + }) + + const initializing = + isStoreLoading || isCurrenciesLoading || !store || !currencies + + return ( + +
+ +
+ + + + +
+
+ + +
+ +
+
+
+
+ ) +} + +const columnHelper = createColumnHelper< + ExtendedProductDTO | ProductVariantDTO +>() + +const useColumns = ({ + currencies = [], + regions = [], +}: { + currencies?: CurrencyDTO[] + regions?: RegionDTO[] +}) => { + const { t } = useTranslation() + + const colDefs: ColumnDef[] = + useMemo(() => { + return [ + ...currencies.map((currency) => { + return columnHelper.display({ + header: t("fields.priceTemplate", { + regionOrCountry: currency.code.toUpperCase(), + }), + cell: ({ row, table }) => { + return ( + } + field={`currency_prices.${currency.code}`} + /> + ) + }, + }) + }), + ...regions.map((region) => { + return columnHelper.display({ + header: t("fields.priceTemplate", { + regionOrCountry: region.name, + }), + cell: ({ row, table }) => { + return ( + c.code === region.currency_code + )} + meta={table.options.meta as DataGridMeta} + field={`region_prices.${region.id}`} + /> + ) + }, + }) + }), + ] + }, [t, currencies, regions]) + + return colDefs +} diff --git a/packages/admin-next/dashboard/src/v2-routes/shipping/shipping-options-edit-pricing/components/create-shipping-options-form/index.ts b/packages/admin-next/dashboard/src/v2-routes/shipping/shipping-options-edit-pricing/components/create-shipping-options-form/index.ts new file mode 100644 index 0000000000..9839576e71 --- /dev/null +++ b/packages/admin-next/dashboard/src/v2-routes/shipping/shipping-options-edit-pricing/components/create-shipping-options-form/index.ts @@ -0,0 +1 @@ +export * from "./edit-shipping-options-pricing-form.tsx" diff --git a/packages/admin-next/dashboard/src/v2-routes/shipping/shipping-options-edit-pricing/index.ts b/packages/admin-next/dashboard/src/v2-routes/shipping/shipping-options-edit-pricing/index.ts new file mode 100644 index 0000000000..d841b62cdb --- /dev/null +++ b/packages/admin-next/dashboard/src/v2-routes/shipping/shipping-options-edit-pricing/index.ts @@ -0,0 +1 @@ +export { ShippingOptionsEditPricing as Component } from "./shipping-options-edit-pricing" diff --git a/packages/admin-next/dashboard/src/v2-routes/shipping/shipping-options-edit-pricing/shipping-options-edit-pricing.tsx b/packages/admin-next/dashboard/src/v2-routes/shipping/shipping-options-edit-pricing/shipping-options-edit-pricing.tsx new file mode 100644 index 0000000000..9c4c70fa84 --- /dev/null +++ b/packages/admin-next/dashboard/src/v2-routes/shipping/shipping-options-edit-pricing/shipping-options-edit-pricing.tsx @@ -0,0 +1,30 @@ +import { useParams } from "react-router-dom" + +import { RouteFocusModal } from "../../../components/route-modal" +import { useShippingOptions } from "../../../hooks/api/shipping-options" +import { EditShippingOptionsPricingForm } from "./components/create-shipping-options-form" + +export function ShippingOptionsEditPricing() { + const { so_id } = useParams() + + const { shipping_options, isPending } = useShippingOptions({ + // TODO: change this when GET option by id endpoint is implemented + id: [so_id], + fields: "*prices,*prices.price_rules", + limit: 999, + }) + + const shippingOption = shipping_options?.find((so) => so.id === so_id) + + if (!isPending && !shippingOption) { + throw new Error(`Shipping option with id: ${so_id} not found`) + } + + return ( + + {shippingOption && ( + + )} + + ) +} diff --git a/packages/core/types/src/fulfillment/mutations/shipping-option.ts b/packages/core/types/src/fulfillment/mutations/shipping-option.ts index a4931eb8fb..06ab9c209c 100644 --- a/packages/core/types/src/fulfillment/mutations/shipping-option.ts +++ b/packages/core/types/src/fulfillment/mutations/shipping-option.ts @@ -120,6 +120,14 @@ export interface UpdateShippingOptionDTO { id: string } )[] + + /** + * The shipping option pricing + */ + prices: ( + | { currency_code: string; amount: number; id?: string } + | { region_id: string; amount: number; id?: string } + )[] } /**