diff --git a/integration-tests/api/__tests__/admin/order.js b/integration-tests/api/__tests__/admin/order.js index eecfeee6dd..8fb15770ef 100644 --- a/integration-tests/api/__tests__/admin/order.js +++ b/integration-tests/api/__tests__/admin/order.js @@ -32,7 +32,7 @@ describe("/admin/orders", () => { beforeAll(async () => { const cwd = path.resolve(path.join(__dirname, "..", "..")) dbConnection = await initDb({ cwd }) - medusaProcess = await setupServer({ cwd }) + medusaProcess = await setupServer({ cwd, verbose: true }) }) afterAll(async () => { diff --git a/integration-tests/api/__tests__/store/cart.js b/integration-tests/api/__tests__/store/cart.js index b13953b446..cad84175a0 100644 --- a/integration-tests/api/__tests__/store/cart.js +++ b/integration-tests/api/__tests__/store/cart.js @@ -30,7 +30,7 @@ describe("/store/carts", () => { const cwd = path.resolve(path.join(__dirname, "..", "..")) try { dbConnection = await initDb({ cwd }) - medusaProcess = await setupServer({ cwd }) + medusaProcess = await setupServer({ cwd, verbose: true }) } catch (error) { console.log(error) } @@ -467,6 +467,13 @@ describe("/store/carts", () => { }) cartWithCustomSo = await manager.save(_cart) + + await manager.insert(CustomShippingOption, { + id: "another-cso-test", + cart_id: "test-cart-with-cso", + shipping_option_id: "test-option", + price: 5, + }) } catch (err) { console.log(err) } @@ -494,8 +501,7 @@ describe("/store/carts", () => { }) it("given a cart with custom options and a shipping option already belonging to said cart, then it should add a shipping method based on the given custom shipping option", async () => { - const shippingOptionId = - cartWithCustomSo.custom_shipping_options[0].shipping_option_id + const shippingOptionId = "test-option" const api = useApi() diff --git a/integration-tests/api/__tests__/store/shipping-options.js b/integration-tests/api/__tests__/store/shipping-options.js index 06814a5cf6..3235e878e4 100644 --- a/integration-tests/api/__tests__/store/shipping-options.js +++ b/integration-tests/api/__tests__/store/shipping-options.js @@ -16,7 +16,7 @@ describe("/store/shipping-options", () => { beforeAll(async () => { const cwd = path.resolve(path.join(__dirname, "..", "..")) dbConnection = await initDb({ cwd }) - medusaProcess = await setupServer({ cwd }) + medusaProcess = await setupServer({ cwd, verbose: true }) }) afterAll(async () => { diff --git a/integration-tests/api/helpers/swap-seeder.js b/integration-tests/api/helpers/swap-seeder.js index 70ab54e11f..f3f098a637 100644 --- a/integration-tests/api/helpers/swap-seeder.js +++ b/integration-tests/api/helpers/swap-seeder.js @@ -15,6 +15,9 @@ const { Cart, Return, } = require("@medusajs/medusa") +const { + CustomShippingOption, +} = require("@medusajs/medusa/dist/models/custom-shipping-option") module.exports = async (connection, data = {}) => { const manager = connection.manager @@ -109,12 +112,6 @@ module.exports = async (connection, data = {}) => { billing_address_id: "test-billing-address", region_id: "test-region", type: "swap", - custom_shipping_options: [ - { - shipping_option_id: "test-option", - price: 0, - }, - ], metadata: { swap_id: "test-swap", parent_order_id: orderWithSwap.id, @@ -123,6 +120,13 @@ module.exports = async (connection, data = {}) => { await manager.save(cartWithCustomSo) + manager.insert(CustomShippingOption, { + id: "cso-test", + cart_id: cartWithCustomSo.id, + price: 0, + shipping_option_id: "test-option", + }) + const swapWithRMAMethod = manager.create(Swap, { id: "test-swap-rma", order_id: "order-with-swap", diff --git a/integration-tests/api/package.json b/integration-tests/api/package.json index a71da1bac7..7c694041db 100644 --- a/integration-tests/api/package.json +++ b/integration-tests/api/package.json @@ -8,15 +8,15 @@ "build": "babel src -d dist --extensions \".ts,.js\"" }, "dependencies": { - "@medusajs/medusa": "1.1.41-dev-1633520747607", - "medusa-interfaces": "1.1.23-dev-1633520747607", + "@medusajs/medusa": "1.1.41-dev-1634206968632", + "medusa-interfaces": "1.1.23-dev-1634206968632", "typeorm": "^0.2.31" }, "devDependencies": { "@babel/cli": "^7.12.10", "@babel/core": "^7.12.10", "@babel/node": "^7.12.10", - "babel-preset-medusa-package": "1.1.15-dev-1633520747607", + "babel-preset-medusa-package": "1.1.15-dev-1634206968632", "jest": "^26.6.3" } } diff --git a/integration-tests/api/yarn.lock b/integration-tests/api/yarn.lock index 4497855c7e..2fd0b45a7d 100644 --- a/integration-tests/api/yarn.lock +++ b/integration-tests/api/yarn.lock @@ -1223,10 +1223,10 @@ "@types/yargs" "^15.0.0" chalk "^4.0.0" -"@medusajs/medusa-cli@1.1.18-dev-1633520747607": - version "1.1.18-dev-1633520747607" - resolved "http://localhost:4873/@medusajs%2fmedusa-cli/-/medusa-cli-1.1.18-dev-1633520747607.tgz#46a3f73f6aff016f50a8123f47d69c64b7a52037" - integrity sha512-4KLnR6gq8R3sCAhOcYCI5SE+G9vyt2l6RyImvvPw7I03iDiNMc3URwoq2cFwV+V4dSrW59WFIlMJR7ALCTygdA== +"@medusajs/medusa-cli@1.1.18-dev-1634206968632": + version "1.1.18-dev-1634206968632" + resolved "http://localhost:4873/@medusajs%2fmedusa-cli/-/medusa-cli-1.1.18-dev-1634206968632.tgz#26c12ed689f9d0485c14eccdd377b67bff505f90" + integrity sha512-oQwdVu2M2v5JRC+gwNXoL/fyGkH6nRaEBDG1rJk0YAmMawMxOIAAd/t1tMK16P69xK4eIUb4fkwX7BpC7kWKiA== dependencies: "@babel/polyfill" "^7.8.7" "@babel/runtime" "^7.9.6" @@ -1244,8 +1244,8 @@ is-valid-path "^0.1.1" joi-objectid "^3.0.1" meant "^1.0.1" - medusa-core-utils "1.1.22-dev-1633520747607" - medusa-telemetry "0.0.5-dev-1633520747607" + medusa-core-utils "1.1.22-dev-1634206968632" + medusa-telemetry "0.0.5-dev-1634206968632" netrc-parser "^3.1.6" open "^8.0.6" ora "^5.4.1" @@ -1259,13 +1259,13 @@ winston "^3.3.3" yargs "^15.3.1" -"@medusajs/medusa@1.1.41-dev-1633520747607": - version "1.1.41-dev-1633520747607" - resolved "http://localhost:4873/@medusajs%2fmedusa/-/medusa-1.1.41-dev-1633520747607.tgz#82efd5b0bbaaaac76ce740dc9b35fe47c780592f" - integrity sha512-D+1WVpMWDaVki+Ti3rXYAl+3rb3eaX16BZ1RU/bG7FbhFJt8P1a1LXwMMQLV2rG/mUDgs6kDyl2qhYTzZ7X7aA== +"@medusajs/medusa@1.1.41-dev-1634206968632": + version "1.1.41-dev-1634206968632" + resolved "http://localhost:4873/@medusajs%2fmedusa/-/medusa-1.1.41-dev-1634206968632.tgz#f4fb24416aab594401d4a8de91f2bcd63f4078d9" + integrity sha512-3nbKX3vcB1sDCkt9FUs611ZxBrGGWBGArEXwtwHpJ/PScesPE1Gl9y+q8lfnu8NInYD3hw1Tpp4JMaOrfpifgw== dependencies: "@hapi/joi" "^16.1.8" - "@medusajs/medusa-cli" "1.1.18-dev-1633520747607" + "@medusajs/medusa-cli" "1.1.18-dev-1634206968632" "@types/lodash" "^4.14.168" awilix "^4.2.3" body-parser "^1.19.0" @@ -1287,8 +1287,8 @@ joi "^17.3.0" joi-objectid "^3.0.1" jsonwebtoken "^8.5.1" - medusa-core-utils "1.1.22-dev-1633520747607" - medusa-test-utils "1.1.25-dev-1633520747607" + medusa-core-utils "1.1.22-dev-1634206968632" + medusa-test-utils "1.1.25-dev-1634206968632" morgan "^1.9.1" multer "^1.4.2" passport "^0.4.0" @@ -1933,10 +1933,10 @@ babel-preset-jest@^26.6.2: babel-plugin-jest-hoist "^26.6.2" babel-preset-current-node-syntax "^1.0.0" -babel-preset-medusa-package@1.1.15-dev-1633520747607: - version "1.1.15-dev-1633520747607" - resolved "http://localhost:4873/babel-preset-medusa-package/-/babel-preset-medusa-package-1.1.15-dev-1633520747607.tgz#ac970d565ac9803333bb5fecc7d16c162302c97b" - integrity sha512-XpU4J3xmdk8y2QRrxXFbkCq+9OzqUNeZlPAffiGlFTWYx0VRD4tZS4DWazrA5ub3RfqMFl/cxZA6NBcbJhZ/iQ== +babel-preset-medusa-package@1.1.15-dev-1634206968632: + version "1.1.15-dev-1634206968632" + resolved "http://localhost:4873/babel-preset-medusa-package/-/babel-preset-medusa-package-1.1.15-dev-1634206968632.tgz#752d6b645d0d09ad53d955bc823572b8d05ecda6" + integrity sha512-AvhdeUwC2uUnMFN9qA4oPDyZ+P0IO+rmiG/Udl7mssh43o3thDATllZSNbRBIwr4jy9M67oj+dbtrO6o8vJoBQ== dependencies: "@babel/plugin-proposal-class-properties" "^7.12.1" "@babel/plugin-proposal-decorators" "^7.12.1" @@ -5110,25 +5110,25 @@ media-typer@0.3.0: resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= -medusa-core-utils@1.1.22-dev-1633520747607: - version "1.1.22-dev-1633520747607" - resolved "http://localhost:4873/medusa-core-utils/-/medusa-core-utils-1.1.22-dev-1633520747607.tgz#d18c79e4ba9ee8b373bfc02024e23763f079ab87" - integrity sha512-pP1FdbrXbHqRxzMh2nKX6ByxtCrNSjJzOIJ45k1/Kl41VortD9EYiOpuLGOdodkD1jbuf6aV4x0zNIoWpGyHew== +medusa-core-utils@1.1.22-dev-1634206968632: + version "1.1.22-dev-1634206968632" + resolved "http://localhost:4873/medusa-core-utils/-/medusa-core-utils-1.1.22-dev-1634206968632.tgz#7eb0e6f95d8bd494aec706aa081ffbba8825e4ba" + integrity sha512-htkx9927dyEWdfOXw2xj58O7GPtPaTY5p9JO/Sja6LuN9QaotMPVrOkqy7drHiGtdn3IzjIMq4i7iub2DgJy0w== dependencies: joi "^17.3.0" joi-objectid "^3.0.1" -medusa-interfaces@1.1.23-dev-1633520747607: - version "1.1.23-dev-1633520747607" - resolved "http://localhost:4873/medusa-interfaces/-/medusa-interfaces-1.1.23-dev-1633520747607.tgz#9487b2979cee9784b219a2117cf7199fbfadceb7" - integrity sha512-jyoc0wemXrZBtJMA6e7FcdnB/7do7j6oyLevCon7A6HibqQkuxLZ7TfWLHGROj4qy8aP8sAvtuXq9gpwkIm3CQ== +medusa-interfaces@1.1.23-dev-1634206968632: + version "1.1.23-dev-1634206968632" + resolved "http://localhost:4873/medusa-interfaces/-/medusa-interfaces-1.1.23-dev-1634206968632.tgz#1b9de0742daf85ddeacb11424857f5d6cc75e8f0" + integrity sha512-8KsXxuMF+jFczyGa088DLVHzfejwD0X74g/AuXhCTkHbc52cbCZr9o7GMok7hfOCz7wp2kKUDBACL0y6PzxHrQ== dependencies: - medusa-core-utils "1.1.22-dev-1633520747607" + medusa-core-utils "1.1.22-dev-1634206968632" -medusa-telemetry@0.0.5-dev-1633520747607: - version "0.0.5-dev-1633520747607" - resolved "http://localhost:4873/medusa-telemetry/-/medusa-telemetry-0.0.5-dev-1633520747607.tgz#c8d23d5acf744eea8c88102a792b481051f84fb9" - integrity sha512-YX2hKGIANC0Ha9QKkMz5se9wDLJ940VEXwlMMGZzIRHoCoNpmxnsgXQXgeCtC1LKmlcaOaOzMqYyk19fgVCybQ== +medusa-telemetry@0.0.5-dev-1634206968632: + version "0.0.5-dev-1634206968632" + resolved "http://localhost:4873/medusa-telemetry/-/medusa-telemetry-0.0.5-dev-1634206968632.tgz#a9a1161aacfe0aaa476ba3ce3c15cb199c201cef" + integrity sha512-z3a5mXplM+XxkY3vJ8t159odgmkdMUHAWBZEM9PGaltkCh9Ew3RWkNXZeC60nW7tc85sfn1/nRxWpLgIywbruQ== dependencies: axios "^0.21.1" axios-retry "^3.1.9" @@ -5140,13 +5140,13 @@ medusa-telemetry@0.0.5-dev-1633520747607: remove-trailing-slash "^0.1.1" uuid "^8.3.2" -medusa-test-utils@1.1.25-dev-1633520747607: - version "1.1.25-dev-1633520747607" - resolved "http://localhost:4873/medusa-test-utils/-/medusa-test-utils-1.1.25-dev-1633520747607.tgz#7c283312d241e012ce9814841ab0fcb0e141245e" - integrity sha512-rW4GRGXlKBoqHeX3AvPWpV9R6AWe5CXEhDcTxxPNk/Nog8E8zBlWDHsff1057RagVIMjd4wkFx4Kz3mgUMgQAg== +medusa-test-utils@1.1.25-dev-1634206968632: + version "1.1.25-dev-1634206968632" + resolved "http://localhost:4873/medusa-test-utils/-/medusa-test-utils-1.1.25-dev-1634206968632.tgz#7306ff294c23bc2f229ae3112e8fc5b5ea362500" + integrity sha512-P+aa/Z7qywZt7z+6vj/owZhX8bHJCPsn1/Ak/9cGM0oJM/QGPzrQ00auikE13XyD95/53fPMh5G5mB8cJ+QbNg== dependencies: "@babel/plugin-transform-classes" "^7.9.5" - medusa-core-utils "1.1.22-dev-1633520747607" + medusa-core-utils "1.1.22-dev-1634206968632" randomatic "^3.1.1" merge-descriptors@1.0.1: diff --git a/packages/medusa/src/api/routes/store/shipping-options/__tests__/list-shipping-options.js b/packages/medusa/src/api/routes/store/shipping-options/__tests__/list-shipping-options.js index 113e820266..256f0d461d 100644 --- a/packages/medusa/src/api/routes/store/shipping-options/__tests__/list-shipping-options.js +++ b/packages/medusa/src/api/routes/store/shipping-options/__tests__/list-shipping-options.js @@ -29,7 +29,6 @@ describe("GET /store/shipping-options", () => { "items", "items.variant", "items.variant.product", - "custom_shipping_options", ], } ) diff --git a/packages/medusa/src/api/routes/store/shipping-options/list-shipping-options.js b/packages/medusa/src/api/routes/store/shipping-options/list-shipping-options.js index bde062eacd..e89a9d92f0 100644 --- a/packages/medusa/src/api/routes/store/shipping-options/list-shipping-options.js +++ b/packages/medusa/src/api/routes/store/shipping-options/list-shipping-options.js @@ -37,13 +37,7 @@ export default async (req, res) => { const cart = await cartService.retrieve(value.cart_id, { select: ["subtotal"], - relations: [ - "region", - "items", - "items.variant", - "items.variant.product", - "custom_shipping_options", - ], + relations: ["region", "items", "items.variant", "items.variant.product"], }) const options = await shippingProfileService.fetchCartOptions(cart) diff --git a/packages/medusa/src/models/cart.ts b/packages/medusa/src/models/cart.ts index f5ea91a5de..5588d24c3b 100644 --- a/packages/medusa/src/models/cart.ts +++ b/packages/medusa/src/models/cart.ts @@ -44,8 +44,6 @@ * $ref: "#/components/schemas/payment_session" * payment: * $ref: "#/components/schemas/payment" - * custom_shipping_options: - * $ref: "#/components/schemas/custom_shipping_option" * shipping_methods: * type: array * items: @@ -221,13 +219,6 @@ export class Cart { @JoinColumn({ name: "payment_id" }) payment: Payment - @OneToMany( - () => CustomShippingOption, - method => method.cart, - { cascade: ["insert"] } - ) - custom_shipping_options: CustomShippingOption[] - @OneToMany( () => ShippingMethod, method => method.cart, diff --git a/packages/medusa/src/models/custom-shipping-option.ts b/packages/medusa/src/models/custom-shipping-option.ts index ebe8b3f960..04e7bfaf06 100644 --- a/packages/medusa/src/models/custom-shipping-option.ts +++ b/packages/medusa/src/models/custom-shipping-option.ts @@ -29,7 +29,7 @@ export class CustomShippingOption { @Column() shipping_option_id: string; - @ManyToOne(() => ShippingOption, { eager: true }) + @ManyToOne(() => ShippingOption) @JoinColumn({ name: "shipping_option_id" }) shipping_option: ShippingOption diff --git a/packages/medusa/src/repositories/custom-shipping-option.ts b/packages/medusa/src/repositories/custom-shipping-option.ts new file mode 100644 index 0000000000..07c9ebea18 --- /dev/null +++ b/packages/medusa/src/repositories/custom-shipping-option.ts @@ -0,0 +1,5 @@ +import { EntityRepository, Repository } from "typeorm" +import { CustomShippingOption } from './../models/custom-shipping-option'; + +@EntityRepository(CustomShippingOption) +export class CustomShippingOptionRepository extends Repository {} diff --git a/packages/medusa/src/services/__tests__/cart.js b/packages/medusa/src/services/__tests__/cart.js index 4b84178204..be537e6ab1 100644 --- a/packages/medusa/src/services/__tests__/cart.js +++ b/packages/medusa/src/services/__tests__/cart.js @@ -1316,13 +1316,10 @@ describe("CartService", () => { let cartService = new CartService({}) it("given a cart with custom shipping options and a shipping option id corresponding to a custom shipping option, then it should return a custom shipping option", async () => { - const cart = { - id: "cart-with-so", - custom_shipping_options: [ - { id: "cso-test", shipping_option_id: "test-so", price: 20 }, - ], - } - const result = cartService.findCustomShippingOption(cart, "test-so") + const cartCSO = [ + { id: "cso-test", shipping_option_id: "test-so", price: 20 }, + ] + const result = cartService.findCustomShippingOption(cartCSO, "test-so") expect(result).toEqual({ id: "cso-test", @@ -1332,25 +1329,20 @@ describe("CartService", () => { }) it("given a cart with empty custom shipping options and shipping option id, then it should return undefined", async () => { - const cart = { - id: "cart-with-so", - custom_shipping_options: [], - } - const result = cartService.findCustomShippingOption(cart, "test-so") + const cartCSO = [] + + const result = cartService.findCustomShippingOption(cartCSO, "test-so") expect(result).toBeUndefined() }) it("given a cart with custom shipping options and a shipping option id that does not belong to the cart, then it should throw an invalid error", async () => { - const cart = { - id: "cart-with-so", - custom_shipping_options: [ - { id: "cso-test", shipping_option_id: "test-so", price: 500 }, - ], - } + const cartCSO = [ + { id: "cso-test", shipping_option_id: "test-so", price: 500 }, + ] expect(() => { - cartService.findCustomShippingOption(cart, "some-other-so") + cartService.findCustomShippingOption(cartCSO, "some-other-so") }).toThrow(MedusaError) }) }) @@ -1373,13 +1365,6 @@ describe("CartService", () => { profile_id: IdMap.getId(m.profile), }, })), - custom_shipping_options: (config.custom_shipping_options || []).map( - cso => ({ - ...cso, - id: IdMap.getId(cso.id), - shipping_option_id: IdMap.getId(cso.shipping_option_id), - }) - ), discounts: [], } } @@ -1391,11 +1376,7 @@ describe("CartService", () => { const cart3 = buildCart("lines", { items: [{ id: "line", profile: "profile1" }], }) - const cartWithCustomSO = buildCart("cart-with-custom-so", { - custom_shipping_options: [ - { id: "cso-test", shipping_option_id: "test-so" }, - ], - }) + const cartWithCustomSO = buildCart("cart-with-custom-so") const cartRepository = MockRepository({ findOneWithRelations: (rels, q) => { @@ -1432,6 +1413,20 @@ describe("CartService", () => { }, } + const customShippingOptionService = { + list: jest.fn().mockImplementation(({ cart_id }) => { + if (cart_id === IdMap.getId("cart-with-custom-so")) { + return [ + { + id: "cso-test", + shipping_profile_id: "test-so", + cart_id: IdMap.getId("cart-with-custom-so"), + }, + ] + } + }), + } + const cartService = new CartService({ manager: MockManager, totalsService, @@ -1439,6 +1434,7 @@ describe("CartService", () => { shippingOptionService, lineItemService, eventBusService, + customShippingOptionService, }) beforeEach(() => { @@ -1538,11 +1534,9 @@ describe("CartService", () => { cartService.findCustomShippingOption = jest .fn() - .mockImplementation(cart => { - if (cart.id === IdMap.getId("cart-with-custom-so")) { - return { - price: 0, - } + .mockImplementation(cartCustomShippingOptions => { + return { + price: 0, } }) diff --git a/packages/medusa/src/services/__tests__/custom-shipping-option.js b/packages/medusa/src/services/__tests__/custom-shipping-option.js new file mode 100644 index 0000000000..a4a8d31cfb --- /dev/null +++ b/packages/medusa/src/services/__tests__/custom-shipping-option.js @@ -0,0 +1,130 @@ +import CustomShippingOptionService from "../custom-shipping-option" +import { MockManager, MockRepository, IdMap } from "medusa-test-utils" + +describe("CustomShippingOptionService", () => { + describe("list", () => { + const customShippingOptionRepository = MockRepository({ + find: q => { + return Promise.resolve([ + { + id: "cso-test", + shipping_option_id: "test-so", + price: 0, + cart_id: "test-cso-cart", + }, + ]) + }, + }) + + const customShippingOptionService = new CustomShippingOptionService({ + manager: MockManager, + customShippingOptionRepository, + }) + + beforeAll(async () => { + jest.clearAllMocks() + }) + + it("calls customShippingOptionRepository find method", async () => { + await customShippingOptionService.list( + { cart_id: "test-cso-cart" }, + { + relations: ["shipping_option"], + } + ) + expect(customShippingOptionRepository.find).toHaveBeenCalledTimes(1) + expect(customShippingOptionRepository.find).toHaveBeenCalledWith({ + where: { + cart_id: "test-cso-cart", + }, + relations: ["shipping_option"], + }) + }) + }) + + describe("retrieve", () => { + const customShippingOptionRepository = MockRepository({ + findOne: q => { + if (q.where.id === "cso-test") { + return Promise.resolve({ + id: "cso-test", + shipping_option_id: "test-so", + price: 0, + cart_id: "test-cso-cart", + }) + } + }, + }) + + const customShippingOptionService = new CustomShippingOptionService({ + manager: MockManager, + customShippingOptionRepository, + }) + + beforeAll(async () => { + jest.clearAllMocks() + }) + + it("calls customShippingOptionRepository findOne method", async () => { + await customShippingOptionService.retrieve("cso-test", { + relations: ["shipping_option", "cart"], + }) + + expect(customShippingOptionRepository.findOne).toHaveBeenCalledTimes(1) + expect(customShippingOptionRepository.findOne).toHaveBeenCalledWith({ + where: { id: "cso-test" }, + relations: ["shipping_option", "cart"], + }) + }) + + it("fails when custom shipping option is not found", async () => { + expect(customShippingOptionService.retrieve("bad-cso")).rejects.toThrow( + `Custom shipping option with id: bad-cso was not found.` + ) + }) + }) + + describe("create", () => { + const customShippingOptionRepository = MockRepository({ + create: jest + .fn() + .mockImplementation(f => Promise.resolve({ id: "test-cso", ...f })), + save: jest.fn().mockImplementation(f => Promise.resolve(f)), + }) + + const customShippingOptionService = new CustomShippingOptionService({ + manager: MockManager, + customShippingOptionRepository, + }) + + beforeAll(async () => { + jest.clearAllMocks() + }) + + it("calls customShippingOptionRepository create method", async () => { + const customShippingOption = { + cart_id: "test-cso-cart", + shipping_option_id: "test-so", + price: 30, + } + await customShippingOptionService.create(customShippingOption) + + expect(customShippingOptionRepository.create).toHaveBeenCalledTimes(1) + expect(customShippingOptionRepository.create).toHaveBeenCalledWith({ + cart_id: "test-cso-cart", + shipping_option_id: "test-so", + price: 30, + metadata: {}, + }) + + expect(customShippingOptionRepository.save).toHaveBeenCalledTimes(1) + expect(customShippingOptionRepository.save).toHaveBeenCalledWith({ + id: "test-cso", + cart_id: "test-cso-cart", + shipping_option_id: "test-so", + price: 30, + metadata: {}, + }) + }) + }) +}) diff --git a/packages/medusa/src/services/__tests__/shipping-profile.js b/packages/medusa/src/services/__tests__/shipping-profile.js index 92c9a5e802..b46737ebe6 100644 --- a/packages/medusa/src/services/__tests__/shipping-profile.js +++ b/packages/medusa/src/services/__tests__/shipping-profile.js @@ -192,43 +192,53 @@ describe("ShippingProfileService", () => { }, } + const customShippingOptionService = { + list: jest.fn().mockImplementation(({ cart_id }, config) => { + if (cart_id === "cso-cart") { + return Promise.resolve([ + { + id: "cso_1", + cart_id: "cso-cart", + shipping_option: { + id: "test-option", + amount: 200, + name: "Test option", + }, + price: 0, + }, + ]) + } + return Promise.resolve([]) + }), + } + const profileService = new ShippingProfileService({ manager: MockManager, shippingProfileRepository: profRepo, shippingOptionService, + customShippingOptionService, }) beforeEach(() => { jest.clearAllMocks() }) - it("given a swap cart with custom shipping options, should return correct custom shipping options ", async () => { + it("given a cart with custom shipping options, should return correct custom shipping options ", async () => { const cart = { - id: "swap-cart", + id: "cso-cart", type: "swap", - custom_shipping_options: [ - { - shipping_option_id: "test-option1", - id: "cso-option1", - shipping_option: { id: "test-option1" }, - price: 10, - }, - { - shipping_option_id: "test-option2", - id: "cso-option2", - shipping_option: { id: "test-option2" }, - price: 0, - }, - ], } await expect(profileService.fetchCartOptions(cart)).resolves.toEqual([ - expect.objectContaining({ id: "test-option1", amount: 10 }), - expect.objectContaining({ id: "test-option2", amount: 0 }), + expect.objectContaining({ + id: "test-option", + amount: 0, + name: "Test option", + }), ]) }) - it("given correct options when cart has no custom shipping options, should return normal shipping options", async () => { + it("given a cart with no custom shipping options, should return normal shipping options", async () => { const cart = { items: [ { diff --git a/packages/medusa/src/services/__tests__/swap.js b/packages/medusa/src/services/__tests__/swap.js index 4b5af9a5d5..7bbde84fb0 100644 --- a/packages/medusa/src/services/__tests__/swap.js +++ b/packages/medusa/src/services/__tests__/swap.js @@ -170,6 +170,14 @@ describe("SwapService", () => { findOneWithRelations: () => Promise.resolve(existing), }) + const customShippingOptionService = { + create: jest.fn().mockReturnValue(Promise.resolve({ id: "cso-test" })), + update: jest.fn().mockReturnValue(Promise.resolve()), + withTransaction: function() { + return this + }, + } + const lineItemService = { create: jest.fn().mockImplementation(d => Promise.resolve(d)), update: jest.fn().mockImplementation(d => Promise.resolve(d)), @@ -185,6 +193,7 @@ describe("SwapService", () => { swapRepository: swapRepo, cartService, lineItemService, + customShippingOptionService, }) it("finds swap and calls return create cart", async () => { @@ -216,9 +225,6 @@ describe("SwapService", () => { discounts: testOrder.discounts, region_id: testOrder.region_id, customer_id: testOrder.customer_id, - custom_shipping_options: [ - { shipping_option_id: "test-option", price: 10 }, - ], type: "swap", metadata: { swap_id: IdMap.getId("test-swap"), diff --git a/packages/medusa/src/services/cart.js b/packages/medusa/src/services/cart.js index d61507b551..1c5831295c 100644 --- a/packages/medusa/src/services/cart.js +++ b/packages/medusa/src/services/cart.js @@ -31,6 +31,7 @@ class CartService extends BaseService { addressRepository, paymentSessionRepository, inventoryService, + customShippingOptionService, }) { super() @@ -84,6 +85,9 @@ class CartService extends BaseService { /** @private @const {InventoryService} */ this.inventoryService_ = inventoryService + + /** @private @const {CustomShippingOptionService} */ + this.customShippingOptionService_ = customShippingOptionService } withTransaction(transactionManager) { @@ -109,6 +113,7 @@ class CartService extends BaseService { addressRepository: this.addressRepository_, giftCardService: this.giftCardService_, inventoryService: this.inventoryService_, + customShippingOptionService: this.customShippingOptionService_, }) cloned.transactionManager_ = transactionManager @@ -1317,11 +1322,17 @@ class CartService extends BaseService { "items.variant", "payment_sessions", "items.variant.product", - "custom_shipping_options", ], }) - let customShippingOption = this.findCustomShippingOption(cart, optionId) + let cartCustomShippingOptions = await this.customShippingOptionService_.list( + { cart_id: cart.id } + ) + + let customShippingOption = this.findCustomShippingOption( + cartCustomShippingOptions, + optionId + ) const { shipping_methods } = cart @@ -1374,17 +1385,17 @@ class CartService extends BaseService { } /** - * Finds the cart's custom shipping option based on the passed option id. + * Finds the cart's custom shipping options based on the passed option id. * throws if custom options is not empty and no shipping option corresponds to optionId - * @param {Object} cart - the cart object - * @param {string} option - id of the normal or custom shipping option to add as valid method + * @param {Object} cartCustomShippingOptions - the cart's custom shipping options + * @param {string} option - id of the normal or custom shipping option to find in the cartCustomShippingOptions * @returns {CustomShippingOption | undefined} */ - findCustomShippingOption(cart, optionId) { - let customOption = cart.custom_shipping_options?.find( + findCustomShippingOption(cartCustomShippingOptions, optionId) { + let customOption = cartCustomShippingOptions?.find( cso => cso.shipping_option_id === optionId ) - const hasCustomOptions = cart.custom_shipping_options?.length + const hasCustomOptions = cartCustomShippingOptions?.length if (hasCustomOptions && !customOption) { throw new MedusaError( diff --git a/packages/medusa/src/services/custom-shipping-option.js b/packages/medusa/src/services/custom-shipping-option.js new file mode 100644 index 0000000000..a8369e5a96 --- /dev/null +++ b/packages/medusa/src/services/custom-shipping-option.js @@ -0,0 +1,112 @@ +import { MedusaError } from "medusa-core-utils" +import { BaseService } from "medusa-interfaces" +import _ from "lodash" + +class CustomShippingOptionService extends BaseService { + constructor({ manager, customShippingOptionRepository }) { + super() + + /** @private @const {EntityManager} */ + this.manager_ = manager + + /** @private @const {customShippingOptionRepository} */ + this.customShippingOptionRepository_ = customShippingOptionRepository + } + + /** + * Sets the service's manager to a given transaction manager + * @param {EntityManager} manager - the manager to use + * @return {CustomShippingOptionService} a cloned CustomShippingOption service + */ + withTransaction(manager) { + if (!manager) { + return this + } + + const cloned = new CustomShippingOptionService({ + manager, + customShippingOptionRepository: this.customShippingOptionRepository_, + }) + + cloned.transactionManager_ = manager + return cloned + } + + /** + * Retrieves a specific shipping option. + * @param {string} id - the id of the custom shipping option to retrieve. + * @param {*} config - any options needed to query for the result. + * @returns {Promise} which resolves to the requested custom shipping option. + */ + async retrieve(id, config = {}) { + const customShippingOptionRepo = this.manager_.getCustomRepository( + this.customShippingOptionRepository_ + ) + + const validatedId = this.validateId_(id) + const query = this.buildQuery_({ id: validatedId }, config) + + const customShippingOption = await customShippingOptionRepo.findOne(query) + + if (!customShippingOption) { + throw new MedusaError( + MedusaError.Types.NOT_FOUND, + `Custom shipping option with id: ${id} was not found.` + ) + } + + return customShippingOption + } + + /** Fetches all custom shipping options related to the given selector + * @param {Object} selector - the query object for find + * @param {Object} config - the configuration used to find the objects. contains relations, skip, and take. + * @return {Promise} custom shipping options matching the query + */ + async list( + selector, + config = { + skip: 0, + take: 50, + relations: [], + } + ) { + const customShippingOptionRepo = this.manager_.getCustomRepository( + this.customShippingOptionRepository_ + ) + + const query = this.buildQuery_(selector, config) + + return customShippingOptionRepo.find(query) + } + + /** + * Creates a custom shipping option associated with a given author + * @param {object} data - the custom shipping option to create + * @param {*} config - any configurations if needed, including meta data + * @returns {Promise} resolves to the creation result + */ + async create(data, config = { metadata: {} }) { + const { metadata } = config + + const { cart_id, shipping_option_id, price } = data + + return this.atomicPhase_(async manager => { + const customShippingOptionRepo = manager.getCustomRepository( + this.customShippingOptionRepository_ + ) + + const customShippingOption = await customShippingOptionRepo.create({ + cart_id, + shipping_option_id, + price, + metadata, + }) + const result = await customShippingOptionRepo.save(customShippingOption) + + return result + }) + } +} + +export default CustomShippingOptionService diff --git a/packages/medusa/src/services/shipping-profile.js b/packages/medusa/src/services/shipping-profile.js index 3e68d77b20..3f1f121ce9 100644 --- a/packages/medusa/src/services/shipping-profile.js +++ b/packages/medusa/src/services/shipping-profile.js @@ -14,6 +14,7 @@ class ShippingProfileService extends BaseService { productService, productRepository, shippingOptionService, + customShippingOptionService, }) { super() @@ -31,6 +32,9 @@ class ShippingProfileService extends BaseService { /** @private @const {ShippingOptionService} */ this.shippingOptionService_ = shippingOptionService + + /** @private @const {CustomShippingOptionService} */ + this.customShippingOptionService_ = customShippingOptionService } withTransaction(transactionManager) { @@ -43,6 +47,7 @@ class ShippingProfileService extends BaseService { shippingProfileRepository: this.shippingProfileRepository_, productService: this.productService_, shippingOptionService: this.shippingOptionService_, + customShippingOptionService: this.customShippingOptionService_, }) cloned.transactionManager_ = transactionManager @@ -413,8 +418,15 @@ class ShippingProfileService extends BaseService { * @return {[ShippingOption]} a list of the available shipping options */ async fetchCartOptions(cart) { - if (cart.custom_shipping_options?.length) { - return cart.custom_shipping_options.map(cso => ({ + const customShippingOptions = await this.customShippingOptionService_.list( + { + cart_id: cart.id, + }, + { relations: ["shipping_option"] } + ) + + if (customShippingOptions?.length) { + return customShippingOptions.map(cso => ({ ...cso.shipping_option, amount: cso.price, })) diff --git a/packages/medusa/src/services/swap.js b/packages/medusa/src/services/swap.js index 074a7be1d3..4c081474aa 100644 --- a/packages/medusa/src/services/swap.js +++ b/packages/medusa/src/services/swap.js @@ -32,6 +32,7 @@ class SwapService extends BaseService { fulfillmentService, orderService, inventoryService, + customShippingOptionService, }) { super() @@ -70,6 +71,9 @@ class SwapService extends BaseService { /** @private @const {EventBusService} */ this.eventBus_ = eventBusService + + /** @private @const {CustomShippingOptionService} */ + this.customShippingOptionService_ = customShippingOptionService } withTransaction(transactionManager) { @@ -90,6 +94,7 @@ class SwapService extends BaseService { orderService: this.orderService_, inventoryService: this.inventoryService_, fulfillmentService: this.fulfillmentService_, + customShippingOptionService: this.customShippingOptionService_, }) cloned.transactionManager_ = transactionManager @@ -568,16 +573,22 @@ class SwapService extends BaseService { region_id: order.region_id, customer_id: order.customer_id, type: "swap", - custom_shipping_options: customShippingOptions.map(so => ({ - price: so.price, - shipping_option_id: so.option_id, - })), metadata: { swap_id: swap.id, parent_order_id: order.id, }, }) + for (const customShippingOption of customShippingOptions) { + await this.customShippingOptionService_ + .withTransaction(manager) + .create({ + cart_id: cart.id, + shipping_option_id: customShippingOption.option_id, + price: customShippingOption.price, + }) + } + for (const item of swap.additional_items) { await this.lineItemService_.withTransaction(manager).update(item.id, { cart_id: cart.id,