feat(medusa): integrate pricing module to core (#5304)
* add pricing integraiton feature flag * init * first endpoint * cleanup * remove console.logs * refactor to util and implement across endpoints * add changeset * rename variables * remove mistype * feat(medusa): move price module integration to pricing service (#5322) * initial changes * chore: make product service always internal for pricing module * add notes --------- Co-authored-by: Riqwan Thamir <rmthamir@gmail.com> * nit * cleanup * update to object querying * update cart integration test * remove uppercase currency_code * nit * Feat/admin product pricing module reads (#5354) * initial changes to list prices for admin * working price module implementation of list prices * nit * variant pricing * redo integration test changes * cleanup * cleanup * fix unit tests * [wip] Core <> Pricing - price updates (#5364) * chore: update medusa-app * wip * get links and modules working with migration * wip * chore: make test pass * Feat/rule type utils (#5371) * initial rule type utils * update migration script * chore: cleanup * ensure prices are always decorated * chore: use seed instead * chore: fix oas conflict * region id add to admin price read! --------- Co-authored-by: Philip Korsholm <88927411+pKorsholm@users.noreply.github.com> Co-authored-by: Philip Korsholm <philip.korsholm@hotmail.com> * pr feedback * create remoteQueryFunction type * fix merge * fix loaders issue * Feat(medusa, types, pricing): pricing module migration script (#5409) * add migration script for money amounts in pricing module * add changeset * rename file * cleanup imports * update changeset * add check for pricing module and ff * feat(medusa,workflows,types): update prices on product and variant update (#5412) * wip * chore: update product prices through workflow * chore: cleanup * chore: update product handler updates prices for variants * chore: handle reverts * chore: address pr comments * chore: scope workflow handlers to flag handlers * chore: update return * chore: update db url * chore: remove migration * chore: increase jest timeout * Feat(medusa): update migration and initDb to run link-migrations (#5437) * initial * loader update * more progress on loaders * update integration tests and remote-query loader * remove helper * migrate isolated modules * fix test * fix integration test * update with pr feedback * unregister medusa-app * re-register medusaApp * fix featureflag * set timeout * set timeout * conditionally run link-module migrations * pr feedback 1 * add driver options for db * throw if link is not defined in migration script * pass config module directly * include container in migrate command * chore: increase timeout * rm redis from api integration tests to test * chore: temporarily skip tests * chore: undo skips + add timeout for workflow tests * chore: increase timeout for order edits * re-add redis * include final resolution * add sharedcontainer to medusaapp loader * chore: move migration under run command * try removing redis_url from api tests * chore: cleanup server on process exit * chore: clear container on exit * chore: adjustments * chore: remove consoles * chore: close express app on finish * chore: destroy pg connection on shutdown * chore: skip * chore: unskip test * chore: cleanup container pg connection * chore: skip --------- Co-authored-by: Riqwan Thamir <rmthamir@gmail.com>
This commit is contained in:
84 files changed
+2677
-259
No files matched your search
@@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
"@medusajs/medusa": patch
|
||||||
|
"@medusajs/types": patch
|
||||||
|
"@medusajs/pricing": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
feat(medusa): add migration script for pricing module
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"@medusajs/medusa": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
feat(medusa): integrate pricing into reads
|
||||||
@@ -21,7 +21,7 @@ const {
|
|||||||
} = require("../../../../factories")
|
} = require("../../../../factories")
|
||||||
const setupServer = require("../../../../environment-helpers/setup-server")
|
const setupServer = require("../../../../environment-helpers/setup-server")
|
||||||
|
|
||||||
jest.setTimeout(30000)
|
jest.setTimeout(100000)
|
||||||
|
|
||||||
const adminHeaders = {
|
const adminHeaders = {
|
||||||
headers: {
|
headers: {
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ const {
|
|||||||
simpleDiscountFactory,
|
simpleDiscountFactory,
|
||||||
simpleSalesChannelFactory,
|
simpleSalesChannelFactory,
|
||||||
simpleRegionFactory,
|
simpleRegionFactory,
|
||||||
|
simplePriceListFactory,
|
||||||
} = require("../../../factories")
|
} = require("../../../factories")
|
||||||
const { DiscountRuleType, AllocationType } = require("@medusajs/medusa/dist")
|
const { DiscountRuleType, AllocationType } = require("@medusajs/medusa/dist")
|
||||||
const { IdMap } = require("medusa-test-utils")
|
const { IdMap } = require("medusa-test-utils")
|
||||||
@@ -117,6 +118,39 @@ describe("/admin/products", () => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("should return prices not in price list for list product endpoint", async () => {
|
||||||
|
const api = useApi()
|
||||||
|
|
||||||
|
await simplePriceListFactory(dbConnection, {
|
||||||
|
prices: [
|
||||||
|
{
|
||||||
|
variant_id: "test-variant",
|
||||||
|
amount: 100,
|
||||||
|
currency_code: "usd",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
const res = await api.get("/admin/products?id=test-product", adminHeaders)
|
||||||
|
|
||||||
|
const prices = res.data.products[0].variants.map((v) => v.prices).flat()
|
||||||
|
|
||||||
|
expect(res.status).toEqual(200)
|
||||||
|
expect(res.data.products).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
id: "test-product",
|
||||||
|
status: "draft",
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
expect(prices).toEqual(
|
||||||
|
expect.not.arrayContaining([
|
||||||
|
expect.objectContaining({ price_list_id: expect.any(String) }),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
it("returns a list of products where status is proposed", async () => {
|
it("returns a list of products where status is proposed", async () => {
|
||||||
const api = useApi()
|
const api = useApi()
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ const enableResponseCompression =
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
plugins: [],
|
plugins: [],
|
||||||
projectConfig: {
|
projectConfig: {
|
||||||
redis_url: redisUrl,
|
// redis_url: redisUrl,
|
||||||
database_url: `postgres://${DB_USERNAME}:${DB_PASSWORD}@${DB_HOST}/${DB_NAME}`,
|
database_url: `postgres://${DB_USERNAME}:${DB_PASSWORD}@${DB_HOST}/${DB_NAME}`,
|
||||||
database_type: "postgres",
|
database_type: "postgres",
|
||||||
jwt_secret: "test",
|
jwt_secret: "test",
|
||||||
|
|||||||
+6
-1
@@ -1,11 +1,16 @@
|
|||||||
const path = require("path")
|
const path = require("path")
|
||||||
const express = require("express")
|
const express = require("express")
|
||||||
const getPort = require("get-port")
|
const getPort = require("get-port")
|
||||||
|
const { isObject } = require("@medusajs/utils")
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
bootstrapApp: async ({ cwd } = {}) => {
|
bootstrapApp: async ({ cwd, env = {} } = {}) => {
|
||||||
const app = express()
|
const app = express()
|
||||||
|
|
||||||
|
if (isObject(env)) {
|
||||||
|
Object.entries(env).forEach(([k, v]) => (process.env[k] = v))
|
||||||
|
}
|
||||||
|
|
||||||
const loaders = require("@medusajs/medusa/dist/loaders").default
|
const loaders = require("@medusajs/medusa/dist/loaders").default
|
||||||
|
|
||||||
const { container, dbConnection } = await loaders({
|
const { container, dbConnection } = await loaders({
|
||||||
|
|||||||
@@ -1,10 +1,22 @@
|
|||||||
const path = require("path")
|
const path = require("path")
|
||||||
const { spawn } = require("child_process")
|
const { spawn } = require("child_process")
|
||||||
const { setPort } = require("./use-api")
|
const { setPort, useExpressServer } = require("./use-api")
|
||||||
|
const { setContainer } = require("./use-container")
|
||||||
|
|
||||||
module.exports = ({ cwd, redisUrl, uploadDir, verbose, env }) => {
|
module.exports = ({
|
||||||
|
cwd,
|
||||||
|
redisUrl,
|
||||||
|
uploadDir,
|
||||||
|
verbose,
|
||||||
|
env,
|
||||||
|
bootstrapApp = false,
|
||||||
|
}) => {
|
||||||
const serverPath = path.join(__dirname, "test-server.js")
|
const serverPath = path.join(__dirname, "test-server.js")
|
||||||
|
|
||||||
|
if (bootstrapApp) {
|
||||||
|
require(serverPath)
|
||||||
|
}
|
||||||
|
|
||||||
// in order to prevent conflicts in redis, use a different db for each worker
|
// in order to prevent conflicts in redis, use a different db for each worker
|
||||||
// same fix as for databases (works with up to 15)
|
// same fix as for databases (works with up to 15)
|
||||||
// redis dbs are 0-indexed and jest worker ids are indexed from 1
|
// redis dbs are 0-indexed and jest worker ids are indexed from 1
|
||||||
@@ -44,5 +56,15 @@ module.exports = ({ cwd, redisUrl, uploadDir, verbose, env }) => {
|
|||||||
setPort(port)
|
setPort(port)
|
||||||
resolve(medusaProcess)
|
resolve(medusaProcess)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
medusaProcess.on("exit", () => {
|
||||||
|
const expressServer = useExpressServer()
|
||||||
|
|
||||||
|
setContainer(null)
|
||||||
|
|
||||||
|
if (expressServer) {
|
||||||
|
expressServer.close()
|
||||||
|
}
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -1,11 +1,18 @@
|
|||||||
const { bootstrapApp } = require("./bootstrap-app")
|
const { bootstrapApp } = require("./bootstrap-app")
|
||||||
|
const { setContainer } = require("./use-container")
|
||||||
|
const { setPort, setExpressServer } = require("./use-api")
|
||||||
|
|
||||||
const setup = async () => {
|
const setup = async () => {
|
||||||
const { app, port } = await bootstrapApp()
|
const { app, port, container } = await bootstrapApp()
|
||||||
|
|
||||||
app.listen(port, (err) => {
|
setContainer(container)
|
||||||
|
|
||||||
|
const expressServer = app.listen(port, (err) => {
|
||||||
|
setPort(port)
|
||||||
process.send(port)
|
process.send(port)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
setExpressServer(expressServer)
|
||||||
}
|
}
|
||||||
|
|
||||||
setup()
|
setup()
|
||||||
@@ -3,10 +3,15 @@ const axios = require("axios").default
|
|||||||
const ServerTestUtil = {
|
const ServerTestUtil = {
|
||||||
port_: null,
|
port_: null,
|
||||||
client_: null,
|
client_: null,
|
||||||
|
expressServer_: null,
|
||||||
|
|
||||||
setPort: function (port) {
|
setPort: function (port) {
|
||||||
this.client_ = axios.create({ baseURL: `http://localhost:${port}` })
|
this.client_ = axios.create({ baseURL: `http://localhost:${port}` })
|
||||||
},
|
},
|
||||||
|
|
||||||
|
setExpressServer: function (expressServer) {
|
||||||
|
this.expressServer_ = expressServer
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
const instance = ServerTestUtil
|
const instance = ServerTestUtil
|
||||||
@@ -15,7 +20,13 @@ module.exports = {
|
|||||||
setPort: function (port) {
|
setPort: function (port) {
|
||||||
instance.setPort(port)
|
instance.setPort(port)
|
||||||
},
|
},
|
||||||
|
setExpressServer: function (expressServer) {
|
||||||
|
instance.setExpressServer(expressServer)
|
||||||
|
},
|
||||||
useApi: function () {
|
useApi: function () {
|
||||||
return instance.client_
|
return instance.client_
|
||||||
},
|
},
|
||||||
|
useExpressServer: function () {
|
||||||
|
return instance.expressServer_
|
||||||
|
},
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
const path = require("path")
|
||||||
|
const express = require("express")
|
||||||
|
const getPort = require("get-port")
|
||||||
|
const { isObject } = require("@medusajs/utils")
|
||||||
|
|
||||||
|
const AppUtils = {
|
||||||
|
container_: null,
|
||||||
|
|
||||||
|
setContainer: function (container) {
|
||||||
|
this.container_ = container
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const instance = AppUtils
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
setContainer: (container) => {
|
||||||
|
instance.setContainer(container)
|
||||||
|
},
|
||||||
|
getContainer: () => {
|
||||||
|
return instance.container_
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -1,9 +1,12 @@
|
|||||||
const path = require("path")
|
const path = require("path")
|
||||||
|
|
||||||
const { getConfigFile } = require("medusa-core-utils")
|
const { getConfigFile } = require("medusa-core-utils")
|
||||||
|
const { isObject, createMedusaContainer } = require("@medusajs/utils")
|
||||||
const { dropDatabase } = require("pg-god")
|
const { dropDatabase } = require("pg-god")
|
||||||
const { DataSource } = require("typeorm")
|
const { DataSource } = require("typeorm")
|
||||||
const dbFactory = require("./use-template-db")
|
const dbFactory = require("./use-template-db")
|
||||||
|
const { getContainer } = require("./use-container")
|
||||||
|
const { ContainerRegistrationKeys } = require("@medusajs/utils")
|
||||||
|
|
||||||
const DB_HOST = process.env.DB_HOST
|
const DB_HOST = process.env.DB_HOST
|
||||||
const DB_USERNAME = process.env.DB_USERNAME
|
const DB_USERNAME = process.env.DB_USERNAME
|
||||||
@@ -29,20 +32,23 @@ const keepTables = [
|
|||||||
|
|
||||||
const DbTestUtil = {
|
const DbTestUtil = {
|
||||||
db_: null,
|
db_: null,
|
||||||
|
pgConnection_: null,
|
||||||
|
|
||||||
setDb: function (dataSource) {
|
setDb: function (dataSource) {
|
||||||
this.db_ = dataSource
|
this.db_ = dataSource
|
||||||
},
|
},
|
||||||
|
|
||||||
|
setPgConnection: function (pgConnection) {
|
||||||
|
this.pgConnection_ = pgConnection
|
||||||
|
},
|
||||||
|
|
||||||
clear: async function () {
|
clear: async function () {
|
||||||
this.db_.synchronize(true)
|
this.db_.synchronize(true)
|
||||||
},
|
},
|
||||||
|
|
||||||
teardown: async function ({ forceDelete } = {}) {
|
teardown: async function ({ forceDelete } = {}) {
|
||||||
forceDelete = forceDelete || []
|
forceDelete = forceDelete || []
|
||||||
|
|
||||||
const entities = this.db_.entityMetadatas
|
const entities = this.db_.entityMetadatas
|
||||||
|
|
||||||
const manager = this.db_.manager
|
const manager = this.db_.manager
|
||||||
|
|
||||||
await manager.query(`SET session_replication_role = 'replica';`)
|
await manager.query(`SET session_replication_role = 'replica';`)
|
||||||
@@ -63,7 +69,15 @@ const DbTestUtil = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
shutdown: async function () {
|
shutdown: async function () {
|
||||||
|
const container = getContainer()
|
||||||
|
const containerPgConnection = container.resolve(
|
||||||
|
ContainerRegistrationKeys.PG_CONNECTION
|
||||||
|
)
|
||||||
|
|
||||||
await this.db_.destroy()
|
await this.db_.destroy()
|
||||||
|
await this.pgConnection_?.context?.destroy()
|
||||||
|
await containerPgConnection?.context?.destroy()
|
||||||
|
|
||||||
return await dropDatabase({ DB_NAME }, pgGodCredentials)
|
return await dropDatabase({ DB_NAME }, pgGodCredentials)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -71,14 +85,17 @@ const DbTestUtil = {
|
|||||||
const instance = DbTestUtil
|
const instance = DbTestUtil
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
initDb: async function ({ cwd, database_extra }) {
|
initDb: async function ({ cwd, database_extra, env }) {
|
||||||
|
if (isObject(env)) {
|
||||||
|
Object.entries(env).forEach(([k, v]) => (process.env[k] = v))
|
||||||
|
}
|
||||||
|
|
||||||
const { configModule } = getConfigFile(cwd, `medusa-config`)
|
const { configModule } = getConfigFile(cwd, `medusa-config`)
|
||||||
const { featureFlags } = configModule
|
|
||||||
|
|
||||||
const featureFlagsLoader =
|
const featureFlagsLoader =
|
||||||
require("@medusajs/medusa/dist/loaders/feature-flags").default
|
require("@medusajs/medusa/dist/loaders/feature-flags").default
|
||||||
|
|
||||||
const featureFlagsRouter = featureFlagsLoader({ featureFlags })
|
const featureFlagRouter = featureFlagsLoader(configModule)
|
||||||
const modelsLoader = require("@medusajs/medusa/dist/loaders/models").default
|
const modelsLoader = require("@medusajs/medusa/dist/loaders/models").default
|
||||||
const entities = modelsLoader({}, { register: false })
|
const entities = modelsLoader({}, { register: false })
|
||||||
|
|
||||||
@@ -104,10 +121,10 @@ module.exports = {
|
|||||||
} = require("@medusajs/medusa/dist/commands/utils/get-migrations")
|
} = require("@medusajs/medusa/dist/commands/utils/get-migrations")
|
||||||
|
|
||||||
const { migrations: moduleMigrations, models: moduleModels } =
|
const { migrations: moduleMigrations, models: moduleModels } =
|
||||||
getModuleSharedResources(configModule, featureFlagsRouter)
|
getModuleSharedResources(configModule, featureFlagRouter)
|
||||||
|
|
||||||
const enabledMigrations = getEnabledMigrations([migrationDir], (flag) =>
|
const enabledMigrations = getEnabledMigrations([migrationDir], (flag) =>
|
||||||
featureFlagsRouter.isFeatureEnabled(flag)
|
featureFlagRouter.isFeatureEnabled(flag)
|
||||||
)
|
)
|
||||||
|
|
||||||
const enabledEntities = entities.filter(
|
const enabledEntities = entities.filter(
|
||||||
@@ -128,6 +145,40 @@ module.exports = {
|
|||||||
await dbDataSource.runMigrations()
|
await dbDataSource.runMigrations()
|
||||||
|
|
||||||
instance.setDb(dbDataSource)
|
instance.setDb(dbDataSource)
|
||||||
|
|
||||||
|
const IsolateProductDomainFeatureFlag =
|
||||||
|
require("@medusajs/medusa/dist/loaders/feature-flags/isolate-product-domain").default
|
||||||
|
const IsolatePricingDomainFeatureFlag =
|
||||||
|
require("@medusajs/medusa/dist/loaders/feature-flags/isolate-pricing-domain").default
|
||||||
|
|
||||||
|
if (
|
||||||
|
featureFlagRouter.isFeatureEnabled(IsolateProductDomainFeatureFlag.key) ||
|
||||||
|
featureFlagRouter.isFeatureEnabled(IsolatePricingDomainFeatureFlag.key)
|
||||||
|
) {
|
||||||
|
const pgConnectionLoader =
|
||||||
|
require("@medusajs/medusa/dist/loaders/pg-connection").default
|
||||||
|
|
||||||
|
const medusaAppLoader =
|
||||||
|
require("@medusajs/medusa/dist/loaders/medusa-app").default
|
||||||
|
|
||||||
|
const container = createMedusaContainer()
|
||||||
|
|
||||||
|
const pgConnection = await pgConnectionLoader({ configModule, container })
|
||||||
|
instance.setPgConnection(pgConnection)
|
||||||
|
|
||||||
|
const { runMigrations } = await medusaAppLoader(
|
||||||
|
{ configModule, container },
|
||||||
|
{ registerInContainer: false }
|
||||||
|
)
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
database: {
|
||||||
|
clientUrl: DB_URL,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
await runMigrations(options)
|
||||||
|
}
|
||||||
|
|
||||||
return dbDataSource
|
return dbDataSource
|
||||||
},
|
},
|
||||||
useDb: function () {
|
useDb: function () {
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import { MoneyAmount, PriceList, Region } from "@medusajs/medusa"
|
import { MoneyAmount, PriceList, Region } from "@medusajs/medusa"
|
||||||
import path from "path"
|
import path from "path"
|
||||||
|
|
||||||
|
import { ProductVariantMoneyAmount } from "@medusajs/medusa"
|
||||||
import { bootstrapApp } from "../../../../environment-helpers/bootstrap-app"
|
import { bootstrapApp } from "../../../../environment-helpers/bootstrap-app"
|
||||||
import setupServer from "../../../../environment-helpers/setup-server"
|
import setupServer from "../../../../environment-helpers/setup-server"
|
||||||
import { setPort, useApi } from "../../../../environment-helpers/use-api"
|
import { setPort, useApi } from "../../../../environment-helpers/use-api"
|
||||||
import { initDb, useDb } from "../../../../environment-helpers/use-db"
|
import { initDb, useDb } from "../../../../environment-helpers/use-db"
|
||||||
import { simpleProductFactory } from "../../../../factories"
|
import { simpleProductFactory } from "../../../../factories"
|
||||||
import { ProductVariantMoneyAmount } from "@medusajs/medusa"
|
|
||||||
|
|
||||||
jest.setTimeout(30000)
|
jest.setTimeout(30000)
|
||||||
|
|
||||||
@@ -23,7 +23,7 @@ describe("/store/carts", () => {
|
|||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
const cwd = path.resolve(path.join(__dirname, "..", "..", ".."))
|
const cwd = path.resolve(path.join(__dirname, "..", "..", ".."))
|
||||||
dbConnection = await initDb({ cwd })
|
dbConnection = await initDb({ cwd })
|
||||||
medusaProcess = await setupServer({ cwd, verbose: true })
|
medusaProcess = await setupServer({ cwd })
|
||||||
const { app, port } = await bootstrapApp({ cwd })
|
const { app, port } = await bootstrapApp({ cwd })
|
||||||
setPort(port)
|
setPort(port)
|
||||||
express = app.listen(port, () => {
|
express = app.listen(port, () => {
|
||||||
@@ -134,7 +134,7 @@ describe("/store/carts", () => {
|
|||||||
await dbConnection.manager.save(ma_sale_1)
|
await dbConnection.manager.save(ma_sale_1)
|
||||||
|
|
||||||
await dbConnection.manager.insert(ProductVariantMoneyAmount, {
|
await dbConnection.manager.insert(ProductVariantMoneyAmount, {
|
||||||
id: 'pvma-test',
|
id: "pvma-test",
|
||||||
variant_id: prodSale.variants[0].id,
|
variant_id: prodSale.variants[0].id,
|
||||||
money_amount_id: ma_sale_1.id,
|
money_amount_id: ma_sale_1.id,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ describe("Inventory Items endpoints", () => {
|
|||||||
const cwd = path.resolve(path.join(__dirname, "..", "..", ".."))
|
const cwd = path.resolve(path.join(__dirname, "..", "..", ".."))
|
||||||
dbConnection = await initDb({ cwd })
|
dbConnection = await initDb({ cwd })
|
||||||
|
|
||||||
const { container, app, port } = await bootstrapApp({ cwd, verbose: true })
|
const { container, app, port } = await bootstrapApp({ cwd })
|
||||||
appContainer = container
|
appContainer = container
|
||||||
|
|
||||||
// Set feature flag
|
// Set feature flag
|
||||||
|
|||||||
@@ -0,0 +1,139 @@
|
|||||||
|
import { setPort, useApi } from "../../../environment-helpers/use-api"
|
||||||
|
import { initDb, useDb } from "../../../environment-helpers/use-db"
|
||||||
|
import { simpleCartFactory, simpleRegionFactory } from "../../../factories"
|
||||||
|
|
||||||
|
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||||
|
import { AxiosInstance } from "axios"
|
||||||
|
import path from "path"
|
||||||
|
import { bootstrapApp } from "../../../environment-helpers/bootstrap-app"
|
||||||
|
import adminSeeder from "../../../helpers/admin-seeder"
|
||||||
|
|
||||||
|
jest.setTimeout(5000000)
|
||||||
|
|
||||||
|
const DB_HOST = process.env.DB_HOST
|
||||||
|
const DB_USERNAME = process.env.DB_USERNAME
|
||||||
|
const DB_PASSWORD = process.env.DB_PASSWORD
|
||||||
|
const DB_NAME = process.env.DB_TEMP_NAME
|
||||||
|
const DB_URL = `postgres://${DB_USERNAME}:${DB_PASSWORD}@${DB_HOST}/${DB_NAME}`
|
||||||
|
|
||||||
|
const adminHeaders = {
|
||||||
|
headers: {
|
||||||
|
"x-medusa-access-token": "test_token",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const env = {
|
||||||
|
MEDUSA_FF_ISOLATE_PRICING_DOMAIN: true,
|
||||||
|
MEDUSA_FF_ISOLATE_PRODUCT_DOMAIN: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("Link Modules", () => {
|
||||||
|
let medusaContainer
|
||||||
|
let dbConnection
|
||||||
|
let express
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
const cwd = path.resolve(path.join(__dirname, "..", ".."))
|
||||||
|
dbConnection = await initDb({ cwd, env } as any)
|
||||||
|
|
||||||
|
const { container, app, port } = await bootstrapApp({ cwd, env })
|
||||||
|
medusaContainer = container
|
||||||
|
setPort(port)
|
||||||
|
|
||||||
|
express = app.listen(port)
|
||||||
|
})
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
const db = useDb()
|
||||||
|
await db.shutdown()
|
||||||
|
|
||||||
|
express.close()
|
||||||
|
})
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await adminSeeder(dbConnection)
|
||||||
|
await simpleRegionFactory(dbConnection, {
|
||||||
|
id: "region-1",
|
||||||
|
currency_code: "usd",
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("get product price", () => {
|
||||||
|
let ruleType
|
||||||
|
let priceSet
|
||||||
|
let productId
|
||||||
|
const cartId = "test-cart"
|
||||||
|
beforeEach(async () => {
|
||||||
|
const pricingModuleService = medusaContainer.resolve(
|
||||||
|
ModuleRegistrationName.PRICING
|
||||||
|
)
|
||||||
|
const api = useApi()! as AxiosInstance
|
||||||
|
|
||||||
|
await simpleCartFactory(dbConnection, { id: cartId, region: "region-1" })
|
||||||
|
|
||||||
|
const payload = {
|
||||||
|
title: "Test",
|
||||||
|
description: "test-product-description",
|
||||||
|
images: ["test-image.png", "test-image-2.png"],
|
||||||
|
variants: [
|
||||||
|
{
|
||||||
|
title: "Test variant",
|
||||||
|
prices: [],
|
||||||
|
options: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await api.post("/admin/products", payload, adminHeaders)
|
||||||
|
|
||||||
|
productId = response.data.product.id
|
||||||
|
const variant = response.data.product.variants[0]
|
||||||
|
|
||||||
|
ruleType = await pricingModuleService.createRuleTypes([
|
||||||
|
{ name: "region_id", rule_attribute: "region_id" },
|
||||||
|
])
|
||||||
|
|
||||||
|
priceSet = await pricingModuleService.create({
|
||||||
|
rules: [{ rule_attribute: "region_id" }],
|
||||||
|
prices: [
|
||||||
|
{
|
||||||
|
amount: 1000,
|
||||||
|
currency_code: "usd",
|
||||||
|
rules: { region_id: "region-1" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
amount: 900,
|
||||||
|
currency_code: "usd",
|
||||||
|
rules: { region_id: "region-2" },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
const remoteLink = medusaContainer.resolve("remoteLink") as any
|
||||||
|
|
||||||
|
await remoteLink.create({
|
||||||
|
productService: {
|
||||||
|
variant_id: variant.id,
|
||||||
|
},
|
||||||
|
pricingService: {
|
||||||
|
price_set_id: priceSet.id,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it("Should get prices declared in pricing module", async () => {
|
||||||
|
const api = useApi()! as AxiosInstance
|
||||||
|
|
||||||
|
const response = await api.get(
|
||||||
|
`/store/products/${productId}?cart_id=${cartId}`
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(response.data.product.variants[0].prices).toEqual([
|
||||||
|
expect.objectContaining({
|
||||||
|
amount: 1000,
|
||||||
|
currency_code: "usd",
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
import { initDb, useDb } from "../../../../environment-helpers/use-db"
|
||||||
|
|
||||||
|
import { Region } from "@medusajs/medusa"
|
||||||
|
import { AxiosInstance } from "axios"
|
||||||
|
import path from "path"
|
||||||
|
import setupServer from "../../../../environment-helpers/setup-server"
|
||||||
|
import { useApi } from "../../../../environment-helpers/use-api"
|
||||||
|
import { getContainer } from "../../../../environment-helpers/use-container"
|
||||||
|
import adminSeeder from "../../../../helpers/admin-seeder"
|
||||||
|
import { createDefaultRuleTypes } from "../../../helpers/create-default-rule-types"
|
||||||
|
|
||||||
|
jest.setTimeout(50000)
|
||||||
|
|
||||||
|
const adminHeaders = {
|
||||||
|
headers: {
|
||||||
|
"x-medusa-access-token": "test_token",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const env = {
|
||||||
|
MEDUSA_FF_ISOLATE_PRICING_DOMAIN: true,
|
||||||
|
MEDUSA_FF_ISOLATE_PRODUCT_DOMAIN: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("[Product & Pricing Module] POST /admin/products", () => {
|
||||||
|
let dbConnection
|
||||||
|
let appContainer
|
||||||
|
let medusaProcess
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
const cwd = path.resolve(path.join(__dirname, "..", "..", ".."))
|
||||||
|
dbConnection = await initDb({ cwd, env } as any)
|
||||||
|
medusaProcess = await setupServer({ cwd, env, bootstrapApp: true } as any)
|
||||||
|
appContainer = getContainer()
|
||||||
|
})
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
const db = useDb()
|
||||||
|
await db.shutdown()
|
||||||
|
medusaProcess.kill()
|
||||||
|
})
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
const manager = dbConnection.manager
|
||||||
|
await adminSeeder(dbConnection)
|
||||||
|
await createDefaultRuleTypes(appContainer)
|
||||||
|
|
||||||
|
await manager.insert(Region, {
|
||||||
|
id: "test-region",
|
||||||
|
name: "Test Region",
|
||||||
|
currency_code: "usd",
|
||||||
|
tax_rate: 0,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
const db = useDb()
|
||||||
|
await db.teardown()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should create prices with region_id and currency_code context", async () => {
|
||||||
|
const api = useApi()! as AxiosInstance
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
title: "test product",
|
||||||
|
options: [{ title: "test-option" }],
|
||||||
|
variants: [
|
||||||
|
{
|
||||||
|
title: "test variant",
|
||||||
|
prices: [
|
||||||
|
{
|
||||||
|
amount: 66600,
|
||||||
|
region_id: "test-region",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
amount: 55500,
|
||||||
|
currency_code: "usd",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
options: [{ value: "test-option" }],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
let response = await api.post(
|
||||||
|
"/admin/products?relations=variants.prices",
|
||||||
|
data,
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(response.status).toEqual(200)
|
||||||
|
expect(response.data).toEqual({
|
||||||
|
product: expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
title: "test product",
|
||||||
|
variants: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
title: "test variant",
|
||||||
|
prices: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
amount: 66600,
|
||||||
|
currency_code: "usd",
|
||||||
|
}),
|
||||||
|
expect.objectContaining({
|
||||||
|
amount: 55500,
|
||||||
|
currency_code: "usd",
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -23,7 +23,6 @@ const adminHeaders = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
describe("/admin/products", () => {
|
describe("/admin/products", () => {
|
||||||
let medusaProcess
|
|
||||||
let dbConnection
|
let dbConnection
|
||||||
let express
|
let express
|
||||||
let medusaContainer
|
let medusaContainer
|
||||||
@@ -44,7 +43,7 @@ describe("/admin/products", () => {
|
|||||||
const db = useDb()
|
const db = useDb()
|
||||||
await db.shutdown()
|
await db.shutdown()
|
||||||
|
|
||||||
medusaProcess.kill()
|
express.close()
|
||||||
})
|
})
|
||||||
|
|
||||||
it("Should have loaded the product module", function () {
|
it("Should have loaded the product module", function () {
|
||||||
|
|||||||
@@ -0,0 +1,253 @@
|
|||||||
|
import setupServer from "../../../../environment-helpers/setup-server"
|
||||||
|
import { useApi } from "../../../../environment-helpers/use-api"
|
||||||
|
import { getContainer } from "../../../../environment-helpers/use-container"
|
||||||
|
import { initDb, useDb } from "../../../../environment-helpers/use-db"
|
||||||
|
import {
|
||||||
|
simpleProductFactory,
|
||||||
|
simpleRegionFactory,
|
||||||
|
} from "../../../../factories"
|
||||||
|
|
||||||
|
import path from "path"
|
||||||
|
import adminSeeder from "../../../../helpers/admin-seeder"
|
||||||
|
import { createDefaultRuleTypes } from "../../../helpers/create-default-rule-types"
|
||||||
|
import { createVariantPriceSet } from "../../../helpers/create-variant-price-set"
|
||||||
|
|
||||||
|
jest.setTimeout(50000)
|
||||||
|
|
||||||
|
const adminHeaders = {
|
||||||
|
headers: {
|
||||||
|
"x-medusa-access-token": "test_token",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const env = {
|
||||||
|
MEDUSA_FF_ISOLATE_PRICING_DOMAIN: true,
|
||||||
|
MEDUSA_FF_ISOLATE_PRODUCT_DOMAIN: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("[Product & Pricing Module] POST /admin/products/:id/variants/:id", () => {
|
||||||
|
let dbConnection
|
||||||
|
let appContainer
|
||||||
|
let medusaProcess
|
||||||
|
let product
|
||||||
|
let variant
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
const cwd = path.resolve(path.join(__dirname, "..", "..", ".."))
|
||||||
|
dbConnection = await initDb({ cwd, env } as any)
|
||||||
|
medusaProcess = await setupServer({ cwd, env, bootstrapApp: true } as any)
|
||||||
|
appContainer = getContainer()
|
||||||
|
})
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
const db = useDb()
|
||||||
|
await db.shutdown()
|
||||||
|
medusaProcess.kill()
|
||||||
|
})
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await adminSeeder(dbConnection)
|
||||||
|
await createDefaultRuleTypes(appContainer)
|
||||||
|
|
||||||
|
await simpleRegionFactory(dbConnection, {
|
||||||
|
id: "test-region",
|
||||||
|
name: "Test Region",
|
||||||
|
currency_code: "usd",
|
||||||
|
tax_rate: 0,
|
||||||
|
})
|
||||||
|
|
||||||
|
product = await simpleProductFactory(dbConnection, {
|
||||||
|
id: "test-product-with-variant",
|
||||||
|
variants: [
|
||||||
|
{
|
||||||
|
options: [{ option_id: "test-product-option-1", value: "test" }],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
id: "test-product-option-1",
|
||||||
|
title: "Test option 1",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
variant = product.variants[0]
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
const db = useDb()
|
||||||
|
await db.teardown()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should create product variant price sets and prices", async () => {
|
||||||
|
const api = useApi()
|
||||||
|
const data = {
|
||||||
|
title: "test variant update",
|
||||||
|
prices: [
|
||||||
|
{
|
||||||
|
amount: 66600,
|
||||||
|
region_id: "test-region",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
amount: 55500,
|
||||||
|
currency_code: "usd",
|
||||||
|
region_id: null,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
let response = await api.post(
|
||||||
|
`/admin/products/${product.id}/variants/${variant.id}`,
|
||||||
|
data,
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
response = await api.get(`/admin/products/${product.id}`, adminHeaders)
|
||||||
|
|
||||||
|
expect(response.status).toEqual(200)
|
||||||
|
expect(response.data.product).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
variants: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
id: variant.id,
|
||||||
|
title: "test variant update",
|
||||||
|
prices: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
amount: 66600,
|
||||||
|
currency_code: "usd",
|
||||||
|
region_id: "test-region",
|
||||||
|
}),
|
||||||
|
expect.objectContaining({
|
||||||
|
amount: 55500,
|
||||||
|
currency_code: "usd",
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should update money amounts if money amount id is present in prices", async () => {
|
||||||
|
const priceSet = await createVariantPriceSet({
|
||||||
|
container: appContainer,
|
||||||
|
variantId: variant.id,
|
||||||
|
prices: [
|
||||||
|
{
|
||||||
|
amount: 3000,
|
||||||
|
currency_code: "usd",
|
||||||
|
rules: {},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
const moneyAmountToUpdate = priceSet.money_amounts?.[0]
|
||||||
|
|
||||||
|
const api = useApi()
|
||||||
|
const data = {
|
||||||
|
title: "test variant update",
|
||||||
|
prices: [
|
||||||
|
{
|
||||||
|
amount: 66600,
|
||||||
|
region_id: "test-region",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: moneyAmountToUpdate?.id,
|
||||||
|
amount: 2222,
|
||||||
|
currency_code: "usd",
|
||||||
|
region_id: null,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
let response = await api.post(
|
||||||
|
`/admin/products/${product.id}/variants/${variant.id}`,
|
||||||
|
data,
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
response = await api.get(`/admin/products/${product.id}`, adminHeaders)
|
||||||
|
|
||||||
|
expect(response.status).toEqual(200)
|
||||||
|
expect(response.data.product).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
variants: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
id: variant.id,
|
||||||
|
title: "test variant update",
|
||||||
|
prices: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
amount: 66600,
|
||||||
|
currency_code: "usd",
|
||||||
|
region_id: "test-region",
|
||||||
|
}),
|
||||||
|
expect.objectContaining({
|
||||||
|
id: moneyAmountToUpdate?.id,
|
||||||
|
amount: 2222,
|
||||||
|
currency_code: "usd",
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should add prices if price set is already present", async () => {
|
||||||
|
await createVariantPriceSet({
|
||||||
|
container: appContainer,
|
||||||
|
variantId: variant.id,
|
||||||
|
prices: [],
|
||||||
|
})
|
||||||
|
|
||||||
|
const api = useApi()
|
||||||
|
const data = {
|
||||||
|
title: "test variant update",
|
||||||
|
prices: [
|
||||||
|
{
|
||||||
|
amount: 123,
|
||||||
|
region_id: "test-region",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
amount: 456,
|
||||||
|
currency_code: "usd",
|
||||||
|
region_id: null,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
let response = await api.post(
|
||||||
|
`/admin/products/${product.id}/variants/${variant.id}`,
|
||||||
|
data,
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
response = await api.get(`/admin/products/${product.id}`, adminHeaders)
|
||||||
|
|
||||||
|
expect(response.status).toEqual(200)
|
||||||
|
expect(response.data.product).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
variants: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
id: variant.id,
|
||||||
|
title: "test variant update",
|
||||||
|
prices: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
amount: 123,
|
||||||
|
currency_code: "usd",
|
||||||
|
region_id: "test-region",
|
||||||
|
}),
|
||||||
|
expect.objectContaining({
|
||||||
|
amount: 456,
|
||||||
|
currency_code: "usd",
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -0,0 +1,283 @@
|
|||||||
|
import setupServer from "../../../../environment-helpers/setup-server"
|
||||||
|
import { useApi } from "../../../../environment-helpers/use-api"
|
||||||
|
import { getContainer } from "../../../../environment-helpers/use-container"
|
||||||
|
import { initDb, useDb } from "../../../../environment-helpers/use-db"
|
||||||
|
import { simpleProductFactory } from "../../../../factories"
|
||||||
|
|
||||||
|
import { Region } from "@medusajs/medusa"
|
||||||
|
import { AxiosInstance } from "axios"
|
||||||
|
import path from "path"
|
||||||
|
import adminSeeder from "../../../../helpers/admin-seeder"
|
||||||
|
import { createDefaultRuleTypes } from "../../../helpers/create-default-rule-types"
|
||||||
|
import { createVariantPriceSet } from "../../../helpers/create-variant-price-set"
|
||||||
|
|
||||||
|
jest.setTimeout(50000)
|
||||||
|
|
||||||
|
const adminHeaders = {
|
||||||
|
headers: {
|
||||||
|
"x-medusa-access-token": "test_token",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const env = {
|
||||||
|
MEDUSA_FF_ISOLATE_PRICING_DOMAIN: true,
|
||||||
|
MEDUSA_FF_ISOLATE_PRODUCT_DOMAIN: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
describe.skip("[Product & Pricing Module] POST /admin/products/:id", () => {
|
||||||
|
let dbConnection
|
||||||
|
let appContainer
|
||||||
|
let medusaProcess
|
||||||
|
let product
|
||||||
|
let variant
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
const cwd = path.resolve(path.join(__dirname, "..", "..", ".."))
|
||||||
|
dbConnection = await initDb({ cwd, env } as any)
|
||||||
|
medusaProcess = await setupServer({ cwd, env, bootstrapApp: true } as any)
|
||||||
|
appContainer = getContainer()
|
||||||
|
})
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
const db = useDb()
|
||||||
|
await db.shutdown()
|
||||||
|
medusaProcess.kill()
|
||||||
|
})
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
const manager = dbConnection.manager
|
||||||
|
await adminSeeder(dbConnection)
|
||||||
|
await createDefaultRuleTypes(appContainer)
|
||||||
|
|
||||||
|
await manager.insert(Region, {
|
||||||
|
id: "test-region",
|
||||||
|
name: "Test Region",
|
||||||
|
currency_code: "usd",
|
||||||
|
tax_rate: 0,
|
||||||
|
})
|
||||||
|
|
||||||
|
product = await simpleProductFactory(dbConnection, {
|
||||||
|
id: "test-product-with-variant",
|
||||||
|
variants: [
|
||||||
|
{
|
||||||
|
options: [{ option_id: "test-product-option-1", value: "test" }],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
id: "test-product-option-1",
|
||||||
|
title: "Test option 1",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
variant = product.variants[0]
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
const db = useDb()
|
||||||
|
await db.teardown()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should update product variant price sets and prices", async () => {
|
||||||
|
const api = useApi()
|
||||||
|
const data = {
|
||||||
|
title: "test product update",
|
||||||
|
variants: [
|
||||||
|
{
|
||||||
|
id: variant.id,
|
||||||
|
title: "test variant update",
|
||||||
|
prices: [
|
||||||
|
{
|
||||||
|
amount: 66600,
|
||||||
|
region_id: "test-region",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
amount: 55500,
|
||||||
|
currency_code: "usd",
|
||||||
|
region_id: null,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
let response = await api.post(
|
||||||
|
`/admin/products/${product.id}`,
|
||||||
|
data,
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
response = await api.get(`/admin/products/${product.id}`, adminHeaders)
|
||||||
|
|
||||||
|
expect(response.status).toEqual(200)
|
||||||
|
expect(response.data.product).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
variants: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
id: variant.id,
|
||||||
|
title: "test variant update",
|
||||||
|
prices: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
amount: 66600,
|
||||||
|
currency_code: "usd",
|
||||||
|
region_id: "test-region",
|
||||||
|
}),
|
||||||
|
expect.objectContaining({
|
||||||
|
amount: 55500,
|
||||||
|
currency_code: "usd",
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should update money amounts if money amount id is present in prices", async () => {
|
||||||
|
const priceSet = await createVariantPriceSet({
|
||||||
|
container: appContainer,
|
||||||
|
variantId: variant.id,
|
||||||
|
prices: [
|
||||||
|
{
|
||||||
|
amount: 3000,
|
||||||
|
currency_code: "usd",
|
||||||
|
rules: {},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
const moneyAmountToUpdate = priceSet.money_amounts?.[0]
|
||||||
|
|
||||||
|
const api = useApi()
|
||||||
|
const data = {
|
||||||
|
title: "test product update",
|
||||||
|
variants: [
|
||||||
|
{
|
||||||
|
id: variant.id,
|
||||||
|
title: "test variant update",
|
||||||
|
prices: [
|
||||||
|
{
|
||||||
|
amount: 66600,
|
||||||
|
region_id: "test-region",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: moneyAmountToUpdate?.id,
|
||||||
|
amount: 2222,
|
||||||
|
currency_code: "usd",
|
||||||
|
region_id: null,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
let response = await api.post(
|
||||||
|
`/admin/products/${product.id}`,
|
||||||
|
data,
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
response = await api.get(`/admin/products/${product.id}`, adminHeaders)
|
||||||
|
|
||||||
|
expect(response.status).toEqual(200)
|
||||||
|
expect(response.data.product).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
variants: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
id: variant.id,
|
||||||
|
title: "test variant update",
|
||||||
|
prices: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
amount: 66600,
|
||||||
|
currency_code: "usd",
|
||||||
|
region_id: "test-region",
|
||||||
|
}),
|
||||||
|
expect.objectContaining({
|
||||||
|
id: moneyAmountToUpdate?.id,
|
||||||
|
amount: 2222,
|
||||||
|
currency_code: "usd",
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should add prices if price set is already present", async () => {
|
||||||
|
const remoteLink = appContainer.resolve("remoteLink")
|
||||||
|
const pricingModuleService = appContainer.resolve("pricingModuleService")
|
||||||
|
|
||||||
|
const priceSet = await pricingModuleService.create({
|
||||||
|
rules: [{ rule_attribute: "region_id" }],
|
||||||
|
prices: [],
|
||||||
|
})
|
||||||
|
|
||||||
|
await remoteLink.create({
|
||||||
|
productService: {
|
||||||
|
variant_id: variant.id,
|
||||||
|
},
|
||||||
|
pricingService: {
|
||||||
|
price_set_id: priceSet.id,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const api = useApi()! as AxiosInstance
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
title: "test product update",
|
||||||
|
variants: [
|
||||||
|
{
|
||||||
|
id: variant.id,
|
||||||
|
title: "test variant update",
|
||||||
|
prices: [
|
||||||
|
{
|
||||||
|
amount: 123,
|
||||||
|
region_id: "test-region",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
amount: 456,
|
||||||
|
currency_code: "usd",
|
||||||
|
region_id: null,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
let response = await api.post(
|
||||||
|
`/admin/products/${product.id}`,
|
||||||
|
data,
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
response = await api.get(`/admin/products/${product.id}`, adminHeaders)
|
||||||
|
|
||||||
|
expect(response.status).toEqual(200)
|
||||||
|
expect(response.data.product).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
variants: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
id: variant.id,
|
||||||
|
title: "test variant update",
|
||||||
|
prices: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
amount: 123,
|
||||||
|
currency_code: "usd",
|
||||||
|
region_id: "test-region",
|
||||||
|
}),
|
||||||
|
expect.objectContaining({
|
||||||
|
amount: 456,
|
||||||
|
currency_code: "usd",
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -1,16 +1,18 @@
|
|||||||
|
import { WorkflowTypes } from "@medusajs/types"
|
||||||
import {
|
import {
|
||||||
Handlers,
|
Handlers,
|
||||||
pipe,
|
pipe,
|
||||||
updateProducts,
|
updateProducts,
|
||||||
UpdateProductsActions,
|
UpdateProductsActions,
|
||||||
} from "@medusajs/workflows"
|
} from "@medusajs/workflows"
|
||||||
import { WorkflowTypes } from "@medusajs/types"
|
|
||||||
import path from "path"
|
import path from "path"
|
||||||
|
|
||||||
import { initDb, useDb } from "../../../../environment-helpers/use-db"
|
|
||||||
import { bootstrapApp } from "../../../../environment-helpers/bootstrap-app"
|
import { bootstrapApp } from "../../../../environment-helpers/bootstrap-app"
|
||||||
|
import { initDb, useDb } from "../../../../environment-helpers/use-db"
|
||||||
import { simpleProductFactory } from "../../../../factories"
|
import { simpleProductFactory } from "../../../../factories"
|
||||||
|
|
||||||
|
jest.setTimeout(30000)
|
||||||
|
|
||||||
describe("UpdateProduct workflow", function () {
|
describe("UpdateProduct workflow", function () {
|
||||||
let medusaProcess
|
let medusaProcess
|
||||||
let dbConnection
|
let dbConnection
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import { IPricingModuleService } from "@medusajs/types"
|
||||||
|
|
||||||
|
export const createDefaultRuleTypes = async (container) => {
|
||||||
|
const pricingModuleService: IPricingModuleService = container.resolve(
|
||||||
|
"pricingModuleService"
|
||||||
|
)
|
||||||
|
|
||||||
|
return pricingModuleService.createRuleTypes([
|
||||||
|
{
|
||||||
|
name: "region_id",
|
||||||
|
rule_attribute: "region_id",
|
||||||
|
},
|
||||||
|
])
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
import { MedusaContainer } from "@medusajs/modules-sdk"
|
||||||
|
import {
|
||||||
|
CreatePriceSetDTO,
|
||||||
|
IPricingModuleService,
|
||||||
|
PriceSetDTO,
|
||||||
|
} from "@medusajs/types"
|
||||||
|
|
||||||
|
const defaultPrices = [
|
||||||
|
{
|
||||||
|
amount: 3000,
|
||||||
|
currency_code: "usd",
|
||||||
|
rules: {},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
const defaultPriceSetRules = [{ rule_attribute: "region_id" }]
|
||||||
|
|
||||||
|
export const createVariantPriceSet = async ({
|
||||||
|
container,
|
||||||
|
variantId,
|
||||||
|
prices = defaultPrices,
|
||||||
|
rules = defaultPriceSetRules,
|
||||||
|
}: {
|
||||||
|
container: MedusaContainer
|
||||||
|
variantId: string
|
||||||
|
prices?: CreatePriceSetDTO["prices"]
|
||||||
|
rules?: CreatePriceSetDTO["rules"]
|
||||||
|
}): Promise<PriceSetDTO> => {
|
||||||
|
const remoteLink = container.resolve("remoteLink")
|
||||||
|
const pricingModuleService: IPricingModuleService = container.resolve(
|
||||||
|
"pricingModuleService"
|
||||||
|
)
|
||||||
|
|
||||||
|
const priceSet = await pricingModuleService.create({
|
||||||
|
rules,
|
||||||
|
prices,
|
||||||
|
})
|
||||||
|
|
||||||
|
await remoteLink.create({
|
||||||
|
productService: {
|
||||||
|
variant_id: variantId,
|
||||||
|
},
|
||||||
|
pricingService: {
|
||||||
|
price_set_id: priceSet.id,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
return await pricingModuleService.retrieve(priceSet.id, {
|
||||||
|
relations: ["money_amounts"],
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -5,6 +5,10 @@ const DB_USERNAME = process.env.DB_USERNAME
|
|||||||
const DB_PASSWORD = process.env.DB_PASSWORD
|
const DB_PASSWORD = process.env.DB_PASSWORD
|
||||||
const DB_NAME = process.env.DB_TEMP_NAME
|
const DB_NAME = process.env.DB_TEMP_NAME
|
||||||
const DB_URL = `postgres://${DB_USERNAME}:${DB_PASSWORD}@${DB_HOST}/${DB_NAME}`
|
const DB_URL = `postgres://${DB_USERNAME}:${DB_PASSWORD}@${DB_HOST}/${DB_NAME}`
|
||||||
|
process.env.POSTGRES_URL = DB_URL
|
||||||
|
|
||||||
|
const enablePricing = process.env.MEDUSA_FF_ISOLATE_PRICING_DOMAIN == "true"
|
||||||
|
const enableProduct = process.env.MEDUSA_FF_ISOLATE_PRODUCT_DOMAIN == "true"
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
plugins: [
|
plugins: [
|
||||||
@@ -33,6 +37,8 @@ module.exports = {
|
|||||||
database_extra: { idle_in_transaction_session_timeout: 0 },
|
database_extra: { idle_in_transaction_session_timeout: 0 },
|
||||||
},
|
},
|
||||||
featureFlags: {
|
featureFlags: {
|
||||||
|
isolate_product_domain: enableProduct,
|
||||||
|
isolate_pricing_domain: enablePricing,
|
||||||
workflows: {
|
workflows: {
|
||||||
[Workflows.CreateProducts]: true,
|
[Workflows.CreateProducts]: true,
|
||||||
[Workflows.UpdateProducts]: true,
|
[Workflows.UpdateProducts]: true,
|
||||||
@@ -59,5 +65,10 @@ module.exports = {
|
|||||||
resources: "shared",
|
resources: "shared",
|
||||||
resolve: "@medusajs/product",
|
resolve: "@medusajs/product",
|
||||||
},
|
},
|
||||||
|
[Modules.PRICING]: {
|
||||||
|
scope: "internal",
|
||||||
|
resources: "shared",
|
||||||
|
resolve: "@medusajs/pricing",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -13,6 +13,8 @@
|
|||||||
"@medusajs/event-bus-local": "workspace:*",
|
"@medusajs/event-bus-local": "workspace:*",
|
||||||
"@medusajs/inventory": "workspace:^",
|
"@medusajs/inventory": "workspace:^",
|
||||||
"@medusajs/medusa": "workspace:*",
|
"@medusajs/medusa": "workspace:*",
|
||||||
|
"@medusajs/modules-sdk": "workspace:^",
|
||||||
|
"@medusajs/pricing": "workspace:^",
|
||||||
"@medusajs/product": "workspace:^",
|
"@medusajs/product": "workspace:^",
|
||||||
"faker": "^5.5.3",
|
"faker": "^5.5.3",
|
||||||
"medusa-fulfillment-webshipper": "workspace:*",
|
"medusa-fulfillment-webshipper": "workspace:*",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Modules } from "@medusajs/modules-sdk"
|
|
||||||
import { ModuleJoinerConfig } from "@medusajs/types"
|
|
||||||
import { LINKS } from "../links"
|
import { LINKS } from "../links"
|
||||||
|
import { ModuleJoinerConfig } from "@medusajs/types"
|
||||||
|
import { Modules } from "@medusajs/modules-sdk"
|
||||||
|
|
||||||
export const ProductVariantPriceSet: ModuleJoinerConfig = {
|
export const ProductVariantPriceSet: ModuleJoinerConfig = {
|
||||||
serviceName: LINKS.ProductVariantPriceSet,
|
serviceName: LINKS.ProductVariantPriceSet,
|
||||||
|
|||||||
@@ -178,10 +178,8 @@ export async function runMigrations(
|
|||||||
allLinks.add(serviceKey)
|
allLinks.add(serviceKey)
|
||||||
|
|
||||||
if (
|
if (
|
||||||
(!primary.isInternalService &&
|
!modulesLoadedKeys.includes(primary.serviceName) ||
|
||||||
!modulesLoadedKeys.includes(primary.serviceName)) ||
|
!modulesLoadedKeys.includes(foreign.serviceName)
|
||||||
(!foreign.isInternalService &&
|
|
||||||
!modulesLoadedKeys.includes(foreign.serviceName))
|
|
||||||
) {
|
) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { IdMap } from "medusa-test-utils"
|
import { IdMap } from "medusa-test-utils"
|
||||||
import { request } from "../../../../../helpers/test-request"
|
|
||||||
import { ProductServiceMock } from "../../../../../services/__mocks__/product"
|
import { ProductServiceMock } from "../../../../../services/__mocks__/product"
|
||||||
|
import { request } from "../../../../../helpers/test-request"
|
||||||
|
|
||||||
describe("GET /admin/products/:id", () => {
|
describe("GET /admin/products/:id", () => {
|
||||||
describe("successfully gets a product", () => {
|
describe("successfully gets a product", () => {
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { PricingService, ProductService } from "../../../../services"
|
import { PricingService, ProductService } from "../../../../services"
|
||||||
import { defaultAdminProductFields, defaultAdminProductRelations } from "."
|
import { defaultAdminProductFields, defaultAdminProductRelations } from "."
|
||||||
|
|
||||||
|
import { EntityManager } from "typeorm"
|
||||||
import { IsString } from "class-validator"
|
import { IsString } from "class-validator"
|
||||||
import { validator } from "../../../../utils/validator"
|
import { validator } from "../../../../utils/validator"
|
||||||
import { EntityManager } from "typeorm"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @oas [post] /admin/products/{id}/options
|
* @oas [post] /admin/products/{id}/options
|
||||||
@@ -91,7 +91,7 @@ export default async (req, res) => {
|
|||||||
relations: defaultAdminProductRelations,
|
relations: defaultAdminProductRelations,
|
||||||
})
|
})
|
||||||
|
|
||||||
const [product] = await pricingService.setProductPrices([rawProduct])
|
const [product] = await pricingService.setAdminProductPricing([rawProduct])
|
||||||
|
|
||||||
res.json({ product })
|
res.json({ product })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { IInventoryService, WorkflowTypes } from "@medusajs/types"
|
||||||
|
import { Workflows, createProducts } from "@medusajs/workflows"
|
||||||
import {
|
import {
|
||||||
IsArray,
|
IsArray,
|
||||||
IsBoolean,
|
IsBoolean,
|
||||||
@@ -37,17 +39,15 @@ import {
|
|||||||
} from "./transaction/create-product-variant"
|
} from "./transaction/create-product-variant"
|
||||||
|
|
||||||
import { DistributedTransaction } from "@medusajs/orchestration"
|
import { DistributedTransaction } from "@medusajs/orchestration"
|
||||||
import { IInventoryService, WorkflowTypes } from "@medusajs/types"
|
|
||||||
import { FlagRouter } from "@medusajs/utils"
|
import { FlagRouter } from "@medusajs/utils"
|
||||||
import { createProducts, Workflows } from "@medusajs/workflows"
|
|
||||||
import { Type } from "class-transformer"
|
import { Type } from "class-transformer"
|
||||||
import { EntityManager } from "typeorm"
|
import { EntityManager } from "typeorm"
|
||||||
|
import IsolateProductDomainFeatureFlag from "../../../../loaders/feature-flags/isolate-product-domain"
|
||||||
import SalesChannelFeatureFlag from "../../../../loaders/feature-flags/sales-channels"
|
import SalesChannelFeatureFlag from "../../../../loaders/feature-flags/sales-channels"
|
||||||
import { ProductStatus } from "../../../../models"
|
import { ProductStatus } from "../../../../models"
|
||||||
import { Logger } from "../../../../types/global"
|
import { Logger } from "../../../../types/global"
|
||||||
import { validator } from "../../../../utils"
|
import { validator } from "../../../../utils"
|
||||||
import { FeatureFlagDecorators } from "../../../../utils/feature-flag-decorators"
|
import { FeatureFlagDecorators } from "../../../../utils/feature-flag-decorators"
|
||||||
import IsolateProductDomainFeatureFlag from "../../../../loaders/feature-flags/isolate-product-domain"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @oas [post] /admin/products
|
* @oas [post] /admin/products
|
||||||
@@ -269,7 +269,9 @@ export default async (req, res) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const [pricedProduct] = await pricingService.setProductPrices([rawProduct])
|
const [pricedProduct] = await pricingService.setAdminProductPricing([
|
||||||
|
rawProduct,
|
||||||
|
])
|
||||||
|
|
||||||
res.json({ product: pricedProduct })
|
res.json({ product: pricedProduct })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import { IInventoryService } from "@medusajs/types"
|
import {
|
||||||
import { Type } from "class-transformer"
|
CreateProductVariantInput,
|
||||||
|
ProductVariantPricesCreateReq,
|
||||||
|
} from "../../../../types/product-variant"
|
||||||
import {
|
import {
|
||||||
IsArray,
|
IsArray,
|
||||||
IsBoolean,
|
IsBoolean,
|
||||||
@@ -9,20 +11,19 @@ import {
|
|||||||
IsString,
|
IsString,
|
||||||
ValidateNested,
|
ValidateNested,
|
||||||
} from "class-validator"
|
} from "class-validator"
|
||||||
import { EntityManager } from "typeorm"
|
|
||||||
import { defaultAdminProductFields, defaultAdminProductRelations } from "."
|
|
||||||
import {
|
import {
|
||||||
PricingService,
|
PricingService,
|
||||||
ProductService,
|
ProductService,
|
||||||
ProductVariantInventoryService,
|
ProductVariantInventoryService,
|
||||||
ProductVariantService,
|
ProductVariantService,
|
||||||
} from "../../../../services"
|
} from "../../../../services"
|
||||||
import {
|
import { defaultAdminProductFields, defaultAdminProductRelations } from "."
|
||||||
CreateProductVariantInput,
|
|
||||||
ProductVariantPricesCreateReq,
|
import { EntityManager } from "typeorm"
|
||||||
} from "../../../../types/product-variant"
|
import { IInventoryService } from "@medusajs/types"
|
||||||
import { validator } from "../../../../utils/validator"
|
import { Type } from "class-transformer"
|
||||||
import { createVariantsTransaction } from "./transaction/create-product-variant"
|
import { createVariantsTransaction } from "./transaction/create-product-variant"
|
||||||
|
import { validator } from "../../../../utils/validator"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @oas [post] /admin/products/{id}/variants
|
* @oas [post] /admin/products/{id}/variants
|
||||||
@@ -152,7 +153,7 @@ export default async (req, res) => {
|
|||||||
relations: defaultAdminProductRelations,
|
relations: defaultAdminProductRelations,
|
||||||
})
|
})
|
||||||
|
|
||||||
const [product] = await pricingService.setProductPrices([rawProduct])
|
const [product] = await pricingService.setAdminProductPricing([rawProduct])
|
||||||
|
|
||||||
res.json({ product })
|
res.json({ product })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ export default async (req, res) => {
|
|||||||
relations: defaultAdminProductRelations,
|
relations: defaultAdminProductRelations,
|
||||||
})
|
})
|
||||||
|
|
||||||
const [product] = await pricingService.setProductPrices([data])
|
const [product] = await pricingService.setAdminProductPricing([data])
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
variant_id,
|
variant_id,
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import { PricingService, ProductService } from "../../../../services"
|
import { PricingService, ProductService } from "../../../../services"
|
||||||
|
|
||||||
import IsolateProductDomainFeatureFlag from "../../../../loaders/feature-flags/isolate-product-domain"
|
import IsolateProductDomainFeatureFlag from "../../../../loaders/feature-flags/isolate-product-domain"
|
||||||
import { defaultAdminProductRemoteQueryObject } from "./index"
|
|
||||||
import { MedusaError } from "@medusajs/utils"
|
import { MedusaError } from "@medusajs/utils"
|
||||||
|
import { defaultAdminProductRemoteQueryObject } from "./index"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @oas [get] /admin/products/{id}
|
* @oas [get] /admin/products/{id}
|
||||||
@@ -80,8 +81,8 @@ export default async (req, res) => {
|
|||||||
|
|
||||||
const product = rawProduct
|
const product = rawProduct
|
||||||
|
|
||||||
if (!shouldSetPricing) {
|
if (shouldSetPricing) {
|
||||||
await pricingService.setProductPrices([product])
|
await pricingService.setAdminProductPricing([product])
|
||||||
}
|
}
|
||||||
|
|
||||||
res.json({ product })
|
res.json({ product })
|
||||||
|
|||||||
@@ -9,10 +9,10 @@ import {
|
|||||||
|
|
||||||
import { FilterableProductProps } from "../../../../types/product"
|
import { FilterableProductProps } from "../../../../types/product"
|
||||||
import { IInventoryService } from "@medusajs/types"
|
import { IInventoryService } from "@medusajs/types"
|
||||||
|
import IsolateProductDomainFeatureFlag from "../../../../loaders/feature-flags/isolate-product-domain"
|
||||||
import { PricedProduct } from "../../../../types/pricing"
|
import { PricedProduct } from "../../../../types/pricing"
|
||||||
import { Product } from "../../../../models"
|
import { Product } from "../../../../models"
|
||||||
import { Type } from "class-transformer"
|
import { Type } from "class-transformer"
|
||||||
import IsolateProductDomainFeatureFlag from "../../../../loaders/feature-flags/isolate-product-domain"
|
|
||||||
import { defaultAdminProductRemoteQueryObject } from "./index"
|
import { defaultAdminProductRemoteQueryObject } from "./index"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -275,7 +275,7 @@ export default async (req, res) => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (shouldSetPricing) {
|
if (shouldSetPricing) {
|
||||||
products = await pricingService.setProductPrices(rawProducts)
|
products = await pricingService.setAdminProductPricing(rawProducts)
|
||||||
}
|
}
|
||||||
|
|
||||||
// We only set availability if variants are requested
|
// We only set availability if variants are requested
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { defaultAdminProductFields, defaultAdminProductRelations } from "."
|
import { defaultAdminProductFields, defaultAdminProductRelations } from "."
|
||||||
|
|
||||||
import { IsString } from "class-validator"
|
|
||||||
import { validator } from "../../../../utils/validator"
|
|
||||||
import { EntityManager } from "typeorm"
|
import { EntityManager } from "typeorm"
|
||||||
|
import { IsString } from "class-validator"
|
||||||
import { PricingService } from "../../../../services"
|
import { PricingService } from "../../../../services"
|
||||||
|
import { validator } from "../../../../utils/validator"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @oas [post] /admin/products/{id}/metadata
|
* @oas [post] /admin/products/{id}/metadata
|
||||||
@@ -96,7 +96,7 @@ export default async (req, res) => {
|
|||||||
relations: defaultAdminProductRelations,
|
relations: defaultAdminProductRelations,
|
||||||
})
|
})
|
||||||
|
|
||||||
const [product] = await pricingService.setProductPrices([rawProduct])
|
const [product] = await pricingService.setAdminProductPricing([rawProduct])
|
||||||
|
|
||||||
res.status(200).json({ product })
|
res.status(200).json({ product })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
|
import { PricingService, ProductService } from "../../../../services"
|
||||||
import { defaultAdminProductFields, defaultAdminProductRelations } from "."
|
import { defaultAdminProductFields, defaultAdminProductRelations } from "."
|
||||||
|
|
||||||
import { IsString } from "class-validator"
|
|
||||||
import { PricingService, ProductService } from "../../../../services"
|
|
||||||
import { validator } from "../../../../utils/validator"
|
|
||||||
import { EntityManager } from "typeorm"
|
import { EntityManager } from "typeorm"
|
||||||
|
import { IsString } from "class-validator"
|
||||||
|
import { validator } from "../../../../utils/validator"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @oas [post] /admin/products/{id}/options/{option_id}
|
* @oas [post] /admin/products/{id}/options/{option_id}
|
||||||
@@ -92,7 +92,7 @@ export default async (req, res) => {
|
|||||||
relations: defaultAdminProductRelations,
|
relations: defaultAdminProductRelations,
|
||||||
})
|
})
|
||||||
|
|
||||||
const [product] = await pricingService.setProductPrices([rawProduct])
|
const [product] = await pricingService.setAdminProductPricing([rawProduct])
|
||||||
|
|
||||||
res.json({ product })
|
res.json({ product })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -137,7 +137,6 @@ export default async (req, res) => {
|
|||||||
req.scope.resolve("inventoryService")
|
req.scope.resolve("inventoryService")
|
||||||
|
|
||||||
const manager: EntityManager = req.scope.resolve("manager")
|
const manager: EntityManager = req.scope.resolve("manager")
|
||||||
|
|
||||||
const productModuleService = req.scope.resolve("productModuleService")
|
const productModuleService = req.scope.resolve("productModuleService")
|
||||||
|
|
||||||
const featureFlagRouter: FlagRouter = req.scope.resolve("featureFlagRouter")
|
const featureFlagRouter: FlagRouter = req.scope.resolve("featureFlagRouter")
|
||||||
@@ -211,6 +210,7 @@ export default async (req, res) => {
|
|||||||
prices: variant.prices || [],
|
prices: variant.prices || [],
|
||||||
})
|
})
|
||||||
variantsToCreate.push(variant)
|
variantsToCreate.push(variant)
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -298,7 +298,7 @@ export default async (req, res) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const [product] = await pricingService.setProductPrices([rawProduct])
|
const [product] = await pricingService.setAdminProductPricing([rawProduct])
|
||||||
|
|
||||||
res.json({ product })
|
res.json({ product })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
|
import { WorkflowTypes } from "@medusajs/types"
|
||||||
|
import { FlagRouter } from "@medusajs/utils"
|
||||||
|
import { UpdateProductVariants } from "@medusajs/workflows"
|
||||||
|
import { Type } from "class-transformer"
|
||||||
import {
|
import {
|
||||||
IsArray,
|
IsArray,
|
||||||
IsBoolean,
|
IsBoolean,
|
||||||
@@ -7,15 +11,15 @@ import {
|
|||||||
IsString,
|
IsString,
|
||||||
ValidateNested,
|
ValidateNested,
|
||||||
} from "class-validator"
|
} from "class-validator"
|
||||||
|
import { EntityManager } from "typeorm"
|
||||||
|
|
||||||
import { defaultAdminProductFields, defaultAdminProductRelations } from "."
|
import { defaultAdminProductFields, defaultAdminProductRelations } from "."
|
||||||
|
import IsolatePricingDomainFeatureFlag from "../../../../loaders/feature-flags/isolate-pricing-domain"
|
||||||
import {
|
import {
|
||||||
PricingService,
|
PricingService,
|
||||||
ProductService,
|
ProductService,
|
||||||
ProductVariantService,
|
ProductVariantService,
|
||||||
} from "../../../../services"
|
} from "../../../../services"
|
||||||
|
|
||||||
import { Type } from "class-transformer"
|
|
||||||
import { EntityManager } from "typeorm"
|
|
||||||
import { PriceSelectionParams } from "../../../../types/price-selection"
|
import { PriceSelectionParams } from "../../../../types/price-selection"
|
||||||
import { ProductVariantPricesUpdateReq } from "../../../../types/product-variant"
|
import { ProductVariantPricesUpdateReq } from "../../../../types/product-variant"
|
||||||
import { validator } from "../../../../utils/validator"
|
import { validator } from "../../../../utils/validator"
|
||||||
@@ -106,6 +110,13 @@ import { validator } from "../../../../utils/validator"
|
|||||||
export default async (req, res) => {
|
export default async (req, res) => {
|
||||||
const { id, variant_id } = req.params
|
const { id, variant_id } = req.params
|
||||||
|
|
||||||
|
const manager: EntityManager = req.scope.resolve("manager")
|
||||||
|
const productService: ProductService = req.scope.resolve("productService")
|
||||||
|
const pricingService: PricingService = req.scope.resolve("pricingService")
|
||||||
|
const featureFlagRouter: FlagRouter = req.scope.resolve("featureFlagRouter")
|
||||||
|
const productVariantService: ProductVariantService = req.scope.resolve(
|
||||||
|
"productVariantService"
|
||||||
|
)
|
||||||
const validated = await validator(
|
const validated = await validator(
|
||||||
AdminPostProductsProductVariantsVariantReq,
|
AdminPostProductsProductVariantsVariantReq,
|
||||||
req.body
|
req.body
|
||||||
@@ -113,13 +124,27 @@ export default async (req, res) => {
|
|||||||
|
|
||||||
const validatedQueryParams = await validator(PriceSelectionParams, req.query)
|
const validatedQueryParams = await validator(PriceSelectionParams, req.query)
|
||||||
|
|
||||||
const productService: ProductService = req.scope.resolve("productService")
|
if (featureFlagRouter.isFeatureEnabled(IsolatePricingDomainFeatureFlag.key)) {
|
||||||
const pricingService: PricingService = req.scope.resolve("pricingService")
|
const updateVariantsWorkflow = UpdateProductVariants.updateProductVariants(
|
||||||
const productVariantService: ProductVariantService = req.scope.resolve(
|
req.scope
|
||||||
"productVariantService"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const manager: EntityManager = req.scope.resolve("manager")
|
const input = {
|
||||||
|
productVariants: [
|
||||||
|
{
|
||||||
|
id: variant_id,
|
||||||
|
...validated,
|
||||||
|
},
|
||||||
|
] as WorkflowTypes.ProductWorkflow.UpdateProductVariantsInputDTO[],
|
||||||
|
}
|
||||||
|
|
||||||
|
await updateVariantsWorkflow.run({
|
||||||
|
input,
|
||||||
|
context: {
|
||||||
|
manager,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
} else {
|
||||||
await manager.transaction(async (transactionManager) => {
|
await manager.transaction(async (transactionManager) => {
|
||||||
await productVariantService
|
await productVariantService
|
||||||
.withTransaction(transactionManager)
|
.withTransaction(transactionManager)
|
||||||
@@ -128,6 +153,7 @@ export default async (req, res) => {
|
|||||||
...validated,
|
...validated,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const rawProduct = await productService.retrieve(id, {
|
const rawProduct = await productService.retrieve(id, {
|
||||||
select: defaultAdminProductFields,
|
select: defaultAdminProductFields,
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ export default async (req, res) => {
|
|||||||
req.retrieveConfig
|
req.retrieveConfig
|
||||||
)
|
)
|
||||||
|
|
||||||
const [variant] = await pricingService.setVariantPrices([rawVariant])
|
const [variant] = await pricingService.setAdminVariantPricing([rawVariant])
|
||||||
|
|
||||||
res.status(200).json({ variant })
|
res.status(200).json({ variant })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -192,7 +192,7 @@ export default async (req, res) => {
|
|||||||
currencyCode = region.currency_code
|
currencyCode = region.currency_code
|
||||||
}
|
}
|
||||||
|
|
||||||
let variants = await pricingService.setVariantPrices(rawVariants, {
|
let variants = await pricingService.setAdminVariantPricing(rawVariants, {
|
||||||
cart_id: req.validatedQuery.cart_id,
|
cart_id: req.validatedQuery.cart_id,
|
||||||
region_id: regionId,
|
region_id: regionId,
|
||||||
currency_code: currencyCode,
|
currency_code: currencyCode,
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { IsOptional, IsString } from "class-validator"
|
||||||
import {
|
import {
|
||||||
CartService,
|
CartService,
|
||||||
PricingService,
|
PricingService,
|
||||||
@@ -5,13 +6,12 @@ import {
|
|||||||
ProductVariantInventoryService,
|
ProductVariantInventoryService,
|
||||||
RegionService,
|
RegionService,
|
||||||
} from "../../../../services"
|
} from "../../../../services"
|
||||||
import { IsOptional, IsString } from "class-validator"
|
|
||||||
|
|
||||||
|
import { MedusaError } from "@medusajs/utils"
|
||||||
|
import IsolateProductDomain from "../../../../loaders/feature-flags/isolate-product-domain"
|
||||||
import { PriceSelectionParams } from "../../../../types/price-selection"
|
import { PriceSelectionParams } from "../../../../types/price-selection"
|
||||||
import { cleanResponseData } from "../../../../utils"
|
import { cleanResponseData } from "../../../../utils"
|
||||||
import IsolateProductDomain from "../../../../loaders/feature-flags/isolate-product-domain"
|
|
||||||
import { defaultStoreProductRemoteQueryObject } from "./index"
|
import { defaultStoreProductRemoteQueryObject } from "./index"
|
||||||
import { MedusaError } from "@medusajs/utils"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @oas [get] /store/products/{id}
|
* @oas [get] /store/products/{id}
|
||||||
|
|||||||
@@ -17,14 +17,14 @@ import { Transform, Type } from "class-transformer"
|
|||||||
import { DateComparisonOperator } from "../../../../types/common"
|
import { DateComparisonOperator } from "../../../../types/common"
|
||||||
import { FeatureFlagDecorators } from "../../../../utils/feature-flag-decorators"
|
import { FeatureFlagDecorators } from "../../../../utils/feature-flag-decorators"
|
||||||
import { IsType } from "../../../../utils/validators/is-type"
|
import { IsType } from "../../../../utils/validators/is-type"
|
||||||
|
import IsolateProductDomain from "../../../../loaders/feature-flags/isolate-product-domain"
|
||||||
import { PriceSelectionParams } from "../../../../types/price-selection"
|
import { PriceSelectionParams } from "../../../../types/price-selection"
|
||||||
import PricingService from "../../../../services/pricing"
|
import PricingService from "../../../../services/pricing"
|
||||||
import SalesChannelFeatureFlag from "../../../../loaders/feature-flags/sales-channels"
|
import SalesChannelFeatureFlag from "../../../../loaders/feature-flags/sales-channels"
|
||||||
import { cleanResponseData } from "../../../../utils/clean-response-data"
|
import { cleanResponseData } from "../../../../utils/clean-response-data"
|
||||||
import { defaultStoreCategoryScope } from "../product-categories"
|
import { defaultStoreCategoryScope } from "../product-categories"
|
||||||
import { optionalBooleanMapper } from "../../../../utils/validators/is-boolean"
|
|
||||||
import IsolateProductDomain from "../../../../loaders/feature-flags/isolate-product-domain"
|
|
||||||
import { defaultStoreProductRemoteQueryObject } from "./index"
|
import { defaultStoreProductRemoteQueryObject } from "./index"
|
||||||
|
import { optionalBooleanMapper } from "../../../../utils/validators/is-boolean"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @oas [get] /store/products
|
* @oas [get] /store/products
|
||||||
|
|||||||
@@ -7,9 +7,7 @@ import {
|
|||||||
} from "../../../../services"
|
} from "../../../../services"
|
||||||
import { IsOptional, IsString } from "class-validator"
|
import { IsOptional, IsString } from "class-validator"
|
||||||
|
|
||||||
import { FindParams } from "../../../../types/common"
|
|
||||||
import { PriceSelectionParams } from "../../../../types/price-selection"
|
import { PriceSelectionParams } from "../../../../types/price-selection"
|
||||||
import { defaultStoreVariantRelations } from "."
|
|
||||||
import { validator } from "../../../../utils/validator"
|
import { validator } from "../../../../utils/validator"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -81,10 +79,11 @@ export default async (req, res) => {
|
|||||||
req.scope.resolve("productVariantInventoryService")
|
req.scope.resolve("productVariantInventoryService")
|
||||||
const cartService: CartService = req.scope.resolve("cartService")
|
const cartService: CartService = req.scope.resolve("cartService")
|
||||||
const regionService: RegionService = req.scope.resolve("regionService")
|
const regionService: RegionService = req.scope.resolve("regionService")
|
||||||
|
const featureFlagRouter = req.scope.resolve("featureFlagRouter")
|
||||||
|
|
||||||
const customer_id = req.user?.customer_id
|
const customer_id = req.user?.customer_id
|
||||||
|
|
||||||
const rawVariant = await variantService.retrieve(id, req.retrieveConfig)
|
const variant = await variantService.retrieve(id, req.retrieveConfig)
|
||||||
|
|
||||||
let sales_channel_id = validated.sales_channel_id
|
let sales_channel_id = validated.sales_channel_id
|
||||||
if (req.publishableApiKeyScopes?.sales_channel_ids.length === 1) {
|
if (req.publishableApiKeyScopes?.sales_channel_ids.length === 1) {
|
||||||
@@ -104,19 +103,27 @@ export default async (req, res) => {
|
|||||||
currencyCode = region.currency_code
|
currencyCode = region.currency_code
|
||||||
}
|
}
|
||||||
|
|
||||||
const variantRes = await pricingService.setVariantPrices([rawVariant], {
|
const decoratePromises: Promise<any>[] = []
|
||||||
|
|
||||||
|
decoratePromises.push(
|
||||||
|
(await pricingService.setVariantPrices([variant], {
|
||||||
cart_id: validated.cart_id,
|
cart_id: validated.cart_id,
|
||||||
customer_id: customer_id,
|
customer_id: customer_id,
|
||||||
region_id: regionId,
|
region_id: regionId,
|
||||||
currency_code: currencyCode,
|
currency_code: currencyCode,
|
||||||
include_discount_prices: true,
|
include_discount_prices: true,
|
||||||
})
|
})) as any
|
||||||
|
|
||||||
const [variant] = await productVariantInventoryService.setVariantAvailability(
|
|
||||||
variantRes,
|
|
||||||
sales_channel_id
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
decoratePromises.push(
|
||||||
|
(await productVariantInventoryService.setVariantAvailability(
|
||||||
|
[variant],
|
||||||
|
sales_channel_id
|
||||||
|
)) as any
|
||||||
|
)
|
||||||
|
|
||||||
|
await Promise.all(decoratePromises)
|
||||||
|
|
||||||
res.json({ variant })
|
res.json({ variant })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { IsInt, IsOptional, IsString } from "class-validator"
|
|
||||||
import {
|
import {
|
||||||
CartService,
|
CartService,
|
||||||
PricingService,
|
PricingService,
|
||||||
@@ -6,12 +5,13 @@ import {
|
|||||||
ProductVariantService,
|
ProductVariantService,
|
||||||
RegionService,
|
RegionService,
|
||||||
} from "../../../../services"
|
} from "../../../../services"
|
||||||
|
import { IsInt, IsOptional, IsString } from "class-validator"
|
||||||
|
|
||||||
import { Type } from "class-transformer"
|
import { IsType } from "../../../../utils/validators/is-type"
|
||||||
import { NumericalComparisonOperator } from "../../../../types/common"
|
import { NumericalComparisonOperator } from "../../../../types/common"
|
||||||
import { PriceSelectionParams } from "../../../../types/price-selection"
|
import { PriceSelectionParams } from "../../../../types/price-selection"
|
||||||
|
import { Type } from "class-transformer"
|
||||||
import { validator } from "../../../../utils/validator"
|
import { validator } from "../../../../utils/validator"
|
||||||
import { IsType } from "../../../../utils/validators/is-type"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @oas [get] /store/variants
|
* @oas [get] /store/variants
|
||||||
@@ -154,11 +154,9 @@ export default async (req, res) => {
|
|||||||
const productVariantInventoryService: ProductVariantInventoryService =
|
const productVariantInventoryService: ProductVariantInventoryService =
|
||||||
req.scope.resolve("productVariantInventoryService")
|
req.scope.resolve("productVariantInventoryService")
|
||||||
const regionService: RegionService = req.scope.resolve("regionService")
|
const regionService: RegionService = req.scope.resolve("regionService")
|
||||||
|
const featureFlagRouter = req.scope.resolve("featureFlagRouter")
|
||||||
|
|
||||||
const rawVariants = await variantService.list(
|
const variants = await variantService.list(filterableFields, req.listConfig)
|
||||||
filterableFields,
|
|
||||||
req.listConfig
|
|
||||||
)
|
|
||||||
|
|
||||||
let regionId = validated.region_id
|
let regionId = validated.region_id
|
||||||
let currencyCode = validated.currency_code
|
let currencyCode = validated.currency_code
|
||||||
@@ -173,19 +171,26 @@ export default async (req, res) => {
|
|||||||
currencyCode = region.currency_code
|
currencyCode = region.currency_code
|
||||||
}
|
}
|
||||||
|
|
||||||
const pricedVariants = await pricingService.setVariantPrices(rawVariants, {
|
const decoratePromises: Promise<any>[] = []
|
||||||
|
|
||||||
|
decoratePromises.push(
|
||||||
|
(await pricingService.setVariantPrices(variants, {
|
||||||
cart_id: validated.cart_id,
|
cart_id: validated.cart_id,
|
||||||
region_id: regionId,
|
region_id: regionId,
|
||||||
currency_code: currencyCode,
|
currency_code: currencyCode,
|
||||||
customer_id: customer_id,
|
customer_id: customer_id,
|
||||||
include_discount_prices: true,
|
include_discount_prices: true,
|
||||||
})
|
})) as any
|
||||||
|
|
||||||
const variants = await productVariantInventoryService.setVariantAvailability(
|
|
||||||
pricedVariants,
|
|
||||||
sales_channel_id
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
decoratePromises.push(
|
||||||
|
(await productVariantInventoryService.setVariantAvailability(
|
||||||
|
variants,
|
||||||
|
sales_channel_id
|
||||||
|
)) as any
|
||||||
|
)
|
||||||
|
await Promise.all(decoratePromises)
|
||||||
|
|
||||||
res.json({ variants })
|
res.json({ variants })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,20 @@
|
|||||||
import { asValue, createContainer } from "awilix"
|
import { asValue, createContainer } from "awilix"
|
||||||
import featureFlagLoader from "../loaders/feature-flags"
|
|
||||||
import Logger from "../loaders/logger"
|
|
||||||
import databaseLoader from "../loaders/database"
|
|
||||||
import configModuleLoader from "../loaders/config"
|
|
||||||
import getMigrations, {
|
import getMigrations, {
|
||||||
getModuleSharedResources,
|
getModuleSharedResources,
|
||||||
revertIsolatedModulesMigration,
|
revertIsolatedModulesMigration,
|
||||||
runIsolatedModulesMigration,
|
runIsolatedModulesMigration,
|
||||||
} from "./utils/get-migrations"
|
} from "./utils/get-migrations"
|
||||||
|
|
||||||
|
import { createMedusaContainer } from "@medusajs/utils"
|
||||||
|
import configModuleLoader from "../loaders/config"
|
||||||
|
import databaseLoader from "../loaders/database"
|
||||||
|
import featureFlagLoader from "../loaders/feature-flags"
|
||||||
|
import IsolatePricingDomainFeatureFlag from "../loaders/feature-flags/isolate-pricing-domain"
|
||||||
|
import IsolateProductDomainFeatureFlag from "../loaders/feature-flags/isolate-product-domain"
|
||||||
|
import Logger from "../loaders/logger"
|
||||||
|
import { loadMedusaApp } from "../loaders/medusa-app"
|
||||||
|
import pgConnectionLoader from "../loaders/pg-connection"
|
||||||
|
|
||||||
const getDataSource = async (directory) => {
|
const getDataSource = async (directory) => {
|
||||||
const configModule = configModuleLoader(directory)
|
const configModule = configModuleLoader(directory)
|
||||||
const featureFlagRouter = featureFlagLoader(configModule)
|
const featureFlagRouter = featureFlagLoader(configModule)
|
||||||
@@ -31,6 +37,25 @@ const getDataSource = async (directory) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const runLinkMigrations = async (directory) => {
|
||||||
|
const configModule = configModuleLoader(directory)
|
||||||
|
const container = createMedusaContainer()
|
||||||
|
|
||||||
|
await pgConnectionLoader({ configModule, container })
|
||||||
|
|
||||||
|
const { runMigrations } = await loadMedusaApp(
|
||||||
|
{ configModule, container },
|
||||||
|
{ registerInContainer: false }
|
||||||
|
)
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
database: {
|
||||||
|
clientUrl: configModule.projectConfig.database_url,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
await runMigrations(options)
|
||||||
|
}
|
||||||
|
|
||||||
const main = async function ({ directory }) {
|
const main = async function ({ directory }) {
|
||||||
const args = process.argv
|
const args = process.argv
|
||||||
|
|
||||||
@@ -40,19 +65,26 @@ const main = async function ({ directory }) {
|
|||||||
|
|
||||||
const configModule = configModuleLoader(directory)
|
const configModule = configModuleLoader(directory)
|
||||||
const dataSource = await getDataSource(directory)
|
const dataSource = await getDataSource(directory)
|
||||||
|
const featureFlagRouter = featureFlagLoader(configModule)
|
||||||
|
|
||||||
if (args[0] === "run") {
|
if (args[0] === "run") {
|
||||||
await dataSource.runMigrations()
|
await dataSource.runMigrations()
|
||||||
await dataSource.destroy()
|
await dataSource.destroy()
|
||||||
await runIsolatedModulesMigration(configModule)
|
await runIsolatedModulesMigration(configModule)
|
||||||
Logger.info("Migrations completed.")
|
if (
|
||||||
|
featureFlagRouter.isFeatureEnabled(IsolateProductDomainFeatureFlag.key) ||
|
||||||
|
featureFlagRouter.isFeatureEnabled(IsolatePricingDomainFeatureFlag.key)
|
||||||
|
) {
|
||||||
|
await runLinkMigrations(directory)
|
||||||
|
}
|
||||||
process.exit()
|
process.exit()
|
||||||
|
|
||||||
|
Logger.info("Migrations completed.")
|
||||||
} else if (args[0] === "revert") {
|
} else if (args[0] === "revert") {
|
||||||
await dataSource.undoLastMigration({ transaction: "all" })
|
await dataSource.undoLastMigration({ transaction: "all" })
|
||||||
await dataSource.destroy()
|
await dataSource.destroy()
|
||||||
await revertIsolatedModulesMigration(configModule)
|
await revertIsolatedModulesMigration(configModule)
|
||||||
Logger.info("Migrations reverted.")
|
Logger.info("Migrations reverted.")
|
||||||
process.exit()
|
|
||||||
} else if (args[0] === "show") {
|
} else if (args[0] === "show") {
|
||||||
const unapplied = await dataSource.showMigrations()
|
const unapplied = await dataSource.showMigrations()
|
||||||
Logger.info(unapplied)
|
Logger.info(unapplied)
|
||||||
|
|||||||
@@ -1,17 +1,4 @@
|
|||||||
import express from "express"
|
|
||||||
import fs from "fs"
|
|
||||||
import { sync as existsSync } from "fs-exists-cached"
|
|
||||||
import { getConfigFile } from "medusa-core-utils"
|
|
||||||
import { track } from "medusa-telemetry"
|
|
||||||
import path from "path"
|
|
||||||
import { DataSource, DataSourceOptions } from "typeorm"
|
import { DataSource, DataSourceOptions } from "typeorm"
|
||||||
|
|
||||||
import loaders from "../loaders"
|
|
||||||
import { handleConfigError } from "../loaders/config"
|
|
||||||
import Logger from "../loaders/logger"
|
|
||||||
|
|
||||||
import featureFlagLoader from "../loaders/feature-flags"
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ProductCategoryService,
|
ProductCategoryService,
|
||||||
ProductService,
|
ProductService,
|
||||||
@@ -23,12 +10,25 @@ import {
|
|||||||
StoreService,
|
StoreService,
|
||||||
UserService,
|
UserService,
|
||||||
} from "../services"
|
} from "../services"
|
||||||
import { ConfigModule } from "../types/global"
|
|
||||||
import { CreateProductInput } from "../types/product"
|
|
||||||
import { CreateProductCategoryInput } from "../types/product-category"
|
|
||||||
import getMigrations, { getModuleSharedResources } from "./utils/get-migrations"
|
import getMigrations, { getModuleSharedResources } from "./utils/get-migrations"
|
||||||
|
|
||||||
|
import { ConfigModule } from "../types/global"
|
||||||
|
import { CreateProductCategoryInput } from "../types/product-category"
|
||||||
|
import { CreateProductInput } from "../types/product"
|
||||||
|
import { IPricingModuleService } from "@medusajs/types"
|
||||||
|
import IsolatePricingDomainFeatureFlag from "../loaders/feature-flags/isolate-pricing-domain"
|
||||||
|
import Logger from "../loaders/logger"
|
||||||
import PublishableApiKeyService from "../services/publishable-api-key"
|
import PublishableApiKeyService from "../services/publishable-api-key"
|
||||||
import { SalesChannel } from "../models"
|
import { SalesChannel } from "../models"
|
||||||
|
import { sync as existsSync } from "fs-exists-cached"
|
||||||
|
import express from "express"
|
||||||
|
import featureFlagLoader from "../loaders/feature-flags"
|
||||||
|
import fs from "fs"
|
||||||
|
import { getConfigFile } from "medusa-core-utils"
|
||||||
|
import { handleConfigError } from "../loaders/config"
|
||||||
|
import loaders from "../loaders"
|
||||||
|
import path from "path"
|
||||||
|
import { track } from "medusa-telemetry"
|
||||||
|
|
||||||
type SeedOptions = {
|
type SeedOptions = {
|
||||||
directory: string
|
directory: string
|
||||||
@@ -121,6 +121,9 @@ const seed = async function ({ directory, migrate, seedFile }: SeedOptions) {
|
|||||||
const shippingProfileService: ShippingProfileService = container.resolve(
|
const shippingProfileService: ShippingProfileService = container.resolve(
|
||||||
"shippingProfileService"
|
"shippingProfileService"
|
||||||
)
|
)
|
||||||
|
const pricingModuleService: IPricingModuleService = container.resolve(
|
||||||
|
"pricingModuleService"
|
||||||
|
)
|
||||||
/* eslint-enable */
|
/* eslint-enable */
|
||||||
|
|
||||||
await manager.transaction(async (tx) => {
|
await manager.transaction(async (tx) => {
|
||||||
@@ -131,6 +134,7 @@ const seed = async function ({ directory, migrate, seedFile }: SeedOptions) {
|
|||||||
categories = [],
|
categories = [],
|
||||||
shipping_options,
|
shipping_options,
|
||||||
users,
|
users,
|
||||||
|
rule_types = [],
|
||||||
publishable_api_keys = [],
|
publishable_api_keys = [],
|
||||||
} = JSON.parse(fs.readFileSync(resolvedPath, `utf-8`))
|
} = JSON.parse(fs.readFileSync(resolvedPath, `utf-8`))
|
||||||
|
|
||||||
@@ -270,6 +274,14 @@ const seed = async function ({ directory, migrate, seedFile }: SeedOptions) {
|
|||||||
])
|
])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
featureFlagRouter.isFeatureEnabled(IsolatePricingDomainFeatureFlag.key)
|
||||||
|
) {
|
||||||
|
for (const ruleType of rule_types) {
|
||||||
|
await pricingModuleService.createRuleTypes(ruleType)
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
track("CLI_SEED_COMPLETED")
|
track("CLI_SEED_COMPLETED")
|
||||||
|
|||||||
@@ -10,3 +10,5 @@ export * from "./types/routing"
|
|||||||
export * from "./types/global"
|
export * from "./types/global"
|
||||||
export * from "./types/price-list"
|
export * from "./types/price-list"
|
||||||
export * from "./utils"
|
export * from "./utils"
|
||||||
|
export * from "./joiner-config"
|
||||||
|
export * from "./modules-config"
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
import { FlagSettings } from "../../types/feature-flags"
|
import { FlagSettings } from "../../types/feature-flags"
|
||||||
|
|
||||||
const PricingIntegrationFeatureFlag: FlagSettings = {
|
const IsolatePricingDomainFeatureFlag: FlagSettings = {
|
||||||
key: "isolate_pricing_domain",
|
key: "isolate_pricing_domain",
|
||||||
default_val: false,
|
default_val: false,
|
||||||
env_key: "MEDUSA_FF_ISOLATE_PRICING_DOMAIN",
|
env_key: "MEDUSA_FF_ISOLATE_PRICING_DOMAIN",
|
||||||
description: "[WIP] use price module integration for pricing",
|
description: "[WIP] use price module integration for pricing",
|
||||||
}
|
}
|
||||||
|
|
||||||
export default PricingIntegrationFeatureFlag
|
export default IsolatePricingDomainFeatureFlag
|
||||||
@@ -535,7 +535,7 @@ export class RoutesLoader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async load() {
|
async load() {
|
||||||
performance.mark("file-base-routing-start" + this.rootDir)
|
performance && performance.mark("file-base-routing-start" + this.rootDir)
|
||||||
|
|
||||||
let apiExists = true
|
let apiExists = true
|
||||||
|
|
||||||
@@ -563,8 +563,10 @@ export class RoutesLoader {
|
|||||||
await this.registerRoutes()
|
await this.registerRoutes()
|
||||||
}
|
}
|
||||||
|
|
||||||
performance.mark("file-base-routing-end" + this.rootDir)
|
performance && performance.mark("file-base-routing-end" + this.rootDir)
|
||||||
const timeSpent = performance
|
const timeSpent =
|
||||||
|
performance &&
|
||||||
|
performance
|
||||||
.measure(
|
.measure(
|
||||||
"file-base-routing-measure" + this.rootDir,
|
"file-base-routing-measure" + this.rootDir,
|
||||||
"file-base-routing-start" + this.rootDir,
|
"file-base-routing-start" + this.rootDir,
|
||||||
|
|||||||
@@ -1,43 +1,42 @@
|
|||||||
import {
|
import {
|
||||||
ExternalModuleDeclaration,
|
|
||||||
InternalModuleDeclaration,
|
|
||||||
MedusaApp,
|
MedusaApp,
|
||||||
moduleLoader,
|
|
||||||
ModulesDefinition,
|
ModulesDefinition,
|
||||||
|
moduleLoader,
|
||||||
registerModules,
|
registerModules,
|
||||||
} from "@medusajs/modules-sdk"
|
} from "@medusajs/modules-sdk"
|
||||||
|
import { Express, NextFunction, Request, Response } from "express"
|
||||||
|
|
||||||
|
import databaseLoader, { dataSource } from "./database"
|
||||||
|
import pluginsLoader, { registerPluginModels } from "./plugins"
|
||||||
|
|
||||||
|
import { Connection } from "typeorm"
|
||||||
import { ContainerRegistrationKeys } from "@medusajs/utils"
|
import { ContainerRegistrationKeys } from "@medusajs/utils"
|
||||||
import { asValue } from "awilix"
|
import { asValue } from "awilix"
|
||||||
import { Express, NextFunction, Request, Response } from "express"
|
|
||||||
import { createMedusaContainer } from "medusa-core-utils"
|
import { createMedusaContainer } from "medusa-core-utils"
|
||||||
import { track } from "medusa-telemetry"
|
import { track } from "medusa-telemetry"
|
||||||
import { EOL } from "os"
|
import { EOL } from "os"
|
||||||
import "reflect-metadata"
|
|
||||||
import requestIp from "request-ip"
|
import requestIp from "request-ip"
|
||||||
import { Connection } from "typeorm"
|
|
||||||
import { joinerConfig } from "../joiner-config"
|
|
||||||
import modulesConfig from "../modules-config"
|
import modulesConfig from "../modules-config"
|
||||||
import { MedusaContainer } from "../types/global"
|
import { MedusaContainer } from "../types/global"
|
||||||
import { isObject, remoteQueryFetchData } from "../utils"
|
|
||||||
import apiLoader from "./api"
|
import apiLoader from "./api"
|
||||||
import loadConfig from "./config"
|
|
||||||
import databaseLoader, { dataSource } from "./database"
|
|
||||||
import defaultsLoader from "./defaults"
|
import defaultsLoader from "./defaults"
|
||||||
import expressLoader from "./express"
|
import expressLoader from "./express"
|
||||||
import featureFlagsLoader from "./feature-flags"
|
import featureFlagsLoader from "./feature-flags"
|
||||||
|
import IsolatePricingDomainFeatureFlag from "./feature-flags/isolate-pricing-domain"
|
||||||
import IsolateProductDomainFeatureFlag from "./feature-flags/isolate-product-domain"
|
import IsolateProductDomainFeatureFlag from "./feature-flags/isolate-product-domain"
|
||||||
import Logger from "./logger"
|
import Logger from "./logger"
|
||||||
|
import { joinerConfig } from "../joiner-config"
|
||||||
|
import loadConfig from "./config"
|
||||||
import modelsLoader from "./models"
|
import modelsLoader from "./models"
|
||||||
import passportLoader from "./passport"
|
import passportLoader from "./passport"
|
||||||
import pgConnectionLoader from "./pg-connection"
|
import pgConnectionLoader from "./pg-connection"
|
||||||
import pluginsLoader, { registerPluginModels } from "./plugins"
|
|
||||||
import redisLoader from "./redis"
|
import redisLoader from "./redis"
|
||||||
import repositoriesLoader from "./repositories"
|
import repositoriesLoader from "./repositories"
|
||||||
import searchIndexLoader from "./search-index"
|
import searchIndexLoader from "./search-index"
|
||||||
import servicesLoader from "./services"
|
import servicesLoader from "./services"
|
||||||
import strategiesLoader from "./strategies"
|
import strategiesLoader from "./strategies"
|
||||||
import subscribersLoader from "./subscribers"
|
import subscribersLoader from "./subscribers"
|
||||||
import { ConfigModule } from "@medusajs/types"
|
import loadMedusaApp from "./medusa-app"
|
||||||
|
|
||||||
type Options = {
|
type Options = {
|
||||||
directory: string
|
directory: string
|
||||||
@@ -45,34 +44,6 @@ type Options = {
|
|||||||
isTest: boolean
|
isTest: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Merge the modules config from the medusa-config file with the modules config from medusa package
|
|
||||||
* @param modules
|
|
||||||
* @param medusaInternalModulesConfig
|
|
||||||
*/
|
|
||||||
function mergeModulesConfig(
|
|
||||||
modules: ConfigModule["modules"],
|
|
||||||
medusaInternalModulesConfig
|
|
||||||
) {
|
|
||||||
for (const [moduleName, moduleConfig] of Object.entries(modules as any)) {
|
|
||||||
const moduleDefinition = ModulesDefinition[moduleName]
|
|
||||||
|
|
||||||
if (moduleDefinition?.isLegacy) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
const isModuleEnabled = moduleConfig === true || isObject(moduleConfig)
|
|
||||||
|
|
||||||
if (!isModuleEnabled) {
|
|
||||||
delete medusaInternalModulesConfig[moduleName]
|
|
||||||
} else {
|
|
||||||
medusaInternalModulesConfig[moduleName] = moduleConfig as Partial<
|
|
||||||
InternalModuleDeclaration | ExternalModuleDeclaration
|
|
||||||
>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default async ({
|
export default async ({
|
||||||
directory: rootDirectory,
|
directory: rootDirectory,
|
||||||
expressApp,
|
expressApp,
|
||||||
@@ -138,11 +109,7 @@ export default async ({
|
|||||||
track("MODULES_INIT_STARTED")
|
track("MODULES_INIT_STARTED")
|
||||||
await moduleLoader({
|
await moduleLoader({
|
||||||
container,
|
container,
|
||||||
moduleResolutions: registerModules(configModule?.modules, {
|
moduleResolutions: registerModules(configModule?.modules),
|
||||||
loadLegacyOnly: featureFlagRouter.isFeatureEnabled(
|
|
||||||
IsolateProductDomainFeatureFlag.key
|
|
||||||
),
|
|
||||||
}),
|
|
||||||
logger: Logger,
|
logger: Logger,
|
||||||
})
|
})
|
||||||
const modAct = Logger.success(modulesActivity, "Modules initialized") || {}
|
const modAct = Logger.success(modulesActivity, "Modules initialized") || {}
|
||||||
@@ -167,6 +134,15 @@ export default async ({
|
|||||||
[ContainerRegistrationKeys.MANAGER]: asValue(dataSource.manager),
|
[ContainerRegistrationKeys.MANAGER]: asValue(dataSource.manager),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
container.register("remoteQuery", asValue(null)) // ensure remoteQuery is always registered
|
||||||
|
// Only load non legacy modules, the legacy modules (non migrated yet) are retrieved by the registerModule above
|
||||||
|
if (
|
||||||
|
featureFlagRouter.isFeatureEnabled(IsolateProductDomainFeatureFlag.key) ||
|
||||||
|
featureFlagRouter.isFeatureEnabled(IsolatePricingDomainFeatureFlag.key)
|
||||||
|
) {
|
||||||
|
await loadMedusaApp({ configModule, container })
|
||||||
|
}
|
||||||
|
|
||||||
const servicesActivity = Logger.activity(`Initializing services${EOL}`)
|
const servicesActivity = Logger.activity(`Initializing services${EOL}`)
|
||||||
track("SERVICES_INIT_STARTED")
|
track("SERVICES_INIT_STARTED")
|
||||||
servicesLoader({ container, configModule, isTest })
|
servicesLoader({ container, configModule, isTest })
|
||||||
@@ -226,32 +202,5 @@ export default async ({
|
|||||||
Logger.success(searchActivity, "Indexing event emitted") || {}
|
Logger.success(searchActivity, "Indexing event emitted") || {}
|
||||||
track("SEARCH_ENGINE_INDEXING_COMPLETED", { duration: searchAct.duration })
|
track("SEARCH_ENGINE_INDEXING_COMPLETED", { duration: searchAct.duration })
|
||||||
|
|
||||||
// Only load non legacy modules, the legacy modules (non migrated yet) are retrieved by the registerModule above
|
|
||||||
if (featureFlagRouter.isFeatureEnabled(IsolateProductDomainFeatureFlag.key)) {
|
|
||||||
mergeModulesConfig(configModule.modules ?? {}, modulesConfig)
|
|
||||||
|
|
||||||
const { query, modules } = await MedusaApp({
|
|
||||||
modulesConfig,
|
|
||||||
servicesConfig: joinerConfig,
|
|
||||||
remoteFetchData: remoteQueryFetchData(container),
|
|
||||||
sharedContainer: container,
|
|
||||||
injectedDependencies: {
|
|
||||||
[ContainerRegistrationKeys.PG_CONNECTION]: container.resolve(
|
|
||||||
ContainerRegistrationKeys.PG_CONNECTION
|
|
||||||
),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
// Medusa app load all non legacy modules, so we need to register them in the container since they are into their own container
|
|
||||||
// We might decide to do it elsewhere but for now I think it is fine
|
|
||||||
for (const [serviceKey, moduleService] of Object.entries(modules)) {
|
|
||||||
container.register(
|
|
||||||
ModulesDefinition[serviceKey].registrationName,
|
|
||||||
asValue(moduleService)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
container.register("remoteQuery", asValue(query))
|
|
||||||
}
|
|
||||||
|
|
||||||
return { container, dbConnection, app: expressApp }
|
return { container, dbConnection, app: expressApp }
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
import { CommonTypes, MedusaContainer } from "@medusajs/types"
|
||||||
|
import {
|
||||||
|
MedusaApp,
|
||||||
|
MedusaAppOutput,
|
||||||
|
ModulesDefinition,
|
||||||
|
} from "@medusajs/modules-sdk"
|
||||||
|
|
||||||
|
import { ContainerRegistrationKeys } from "@medusajs/utils"
|
||||||
|
import { asValue } from "awilix"
|
||||||
|
import { joinerConfig } from "../joiner-config"
|
||||||
|
import { mergeModulesConfig } from "../utils/merge-modules-config"
|
||||||
|
import modulesConfig from "../modules-config"
|
||||||
|
import { remoteQueryFetchData } from ".."
|
||||||
|
|
||||||
|
export const loadMedusaApp = async (
|
||||||
|
{
|
||||||
|
configModule,
|
||||||
|
container,
|
||||||
|
}: { configModule: CommonTypes.ConfigModule; container: MedusaContainer },
|
||||||
|
config = { registerInContainer: true }
|
||||||
|
): Promise<MedusaAppOutput> => {
|
||||||
|
mergeModulesConfig(configModule.modules ?? {}, modulesConfig)
|
||||||
|
|
||||||
|
const injectedDependencies = {
|
||||||
|
[ContainerRegistrationKeys.PG_CONNECTION]: container.resolve(
|
||||||
|
ContainerRegistrationKeys.PG_CONNECTION
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
const sharedResourcesConfig = {
|
||||||
|
database: {
|
||||||
|
clientUrl: configModule.projectConfig.database_url,
|
||||||
|
driverOptions: configModule.projectConfig.database_extra,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const medusaApp = await MedusaApp({
|
||||||
|
modulesConfig,
|
||||||
|
servicesConfig: joinerConfig,
|
||||||
|
remoteFetchData: remoteQueryFetchData(container),
|
||||||
|
sharedContainer: container,
|
||||||
|
sharedResourcesConfig,
|
||||||
|
injectedDependencies,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!config.registerInContainer) {
|
||||||
|
return medusaApp
|
||||||
|
}
|
||||||
|
|
||||||
|
container.register("remoteLink", asValue(medusaApp.link))
|
||||||
|
|
||||||
|
const { query, modules } = medusaApp
|
||||||
|
|
||||||
|
// Medusa app load all non legacy modules, so we need to register them in the container since they are into their own container
|
||||||
|
// We might decide to do it elsewhere but for now I think it is fine
|
||||||
|
for (const [serviceKey, moduleService] of Object.entries(modules)) {
|
||||||
|
container.register(
|
||||||
|
ModulesDefinition[serviceKey].registrationName,
|
||||||
|
asValue(moduleService)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
container.register("remoteQuery", asValue(query))
|
||||||
|
|
||||||
|
return medusaApp
|
||||||
|
}
|
||||||
|
|
||||||
|
export default loadMedusaApp
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
import { asValue, AwilixContainer } from "awilix"
|
|
||||||
import { ConfigModule } from "../types/global"
|
|
||||||
import { ContainerRegistrationKeys, ModulesSdkUtils } from "@medusajs/utils"
|
import { ContainerRegistrationKeys, ModulesSdkUtils } from "@medusajs/utils"
|
||||||
|
import { AwilixContainer, asValue } from "awilix"
|
||||||
|
import { ConfigModule } from "../types/global"
|
||||||
|
|
||||||
type Options = {
|
type Options = {
|
||||||
configModule: ConfigModule
|
configModule: ConfigModule
|
||||||
container: AwilixContainer
|
container: AwilixContainer
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async ({ container, configModule }: Options): Promise<void> => {
|
export default async ({ container, configModule }: Options): Promise<any> => {
|
||||||
if (container.hasRegistration(ContainerRegistrationKeys.PG_CONNECTION)) {
|
if (container.hasRegistration(ContainerRegistrationKeys.PG_CONNECTION)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -36,4 +36,6 @@ export default async ({ container, configModule }: Options): Promise<void> => {
|
|||||||
ContainerRegistrationKeys.PG_CONNECTION,
|
ContainerRegistrationKeys.PG_CONNECTION,
|
||||||
asValue(pgConnection)
|
asValue(pgConnection)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
return pgConnection
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
import { AwilixContainer } from "awilix"
|
||||||
|
import { IPricingModuleService } from "@medusajs/types"
|
||||||
|
import dotenv from "dotenv"
|
||||||
|
import express from "express"
|
||||||
|
import loaders from "../loaders"
|
||||||
|
|
||||||
|
dotenv.config()
|
||||||
|
|
||||||
|
export const createDefaultRuleTypes = async (container: AwilixContainer) => {
|
||||||
|
const pricingModuleService: IPricingModuleService = container.resolve(
|
||||||
|
"pricingModuleService"
|
||||||
|
)
|
||||||
|
const existing = await pricingModuleService.listRuleTypes(
|
||||||
|
{ rule_attribute: ["region_id"] },
|
||||||
|
{ take: 1 }
|
||||||
|
)
|
||||||
|
|
||||||
|
if (existing.length) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
await pricingModuleService.createRuleTypes([
|
||||||
|
{ name: "region_id", rule_attribute: "region_id" },
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
const migrate = async function ({ directory }) {
|
||||||
|
const app = express()
|
||||||
|
const { container } = await loaders({
|
||||||
|
directory,
|
||||||
|
expressApp: app,
|
||||||
|
isTest: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
return await createDefaultRuleTypes(container)
|
||||||
|
}
|
||||||
|
|
||||||
|
migrate({ directory: process.cwd() })
|
||||||
|
.then(() => {
|
||||||
|
console.log("Created default rule types")
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
console.log("Failed to create rule types")
|
||||||
|
})
|
||||||
@@ -0,0 +1,142 @@
|
|||||||
|
import { FlagRouter, MedusaError } from "@medusajs/utils"
|
||||||
|
import { IPricingModuleService, MedusaContainer } from "@medusajs/types"
|
||||||
|
|
||||||
|
import { AwilixContainer } from "awilix"
|
||||||
|
import { EntityManager } from "typeorm"
|
||||||
|
import IsolatePricingDomainFeatureFlag from "../loaders/feature-flags/isolate-pricing-domain"
|
||||||
|
import { Modules } from "@medusajs/modules-sdk"
|
||||||
|
import { ProductVariant } from "../models"
|
||||||
|
import { ProductVariantService } from "../services"
|
||||||
|
import { createDefaultRuleTypes } from "./create-default-rule-types"
|
||||||
|
import dotenv from "dotenv"
|
||||||
|
import express from "express"
|
||||||
|
import loadMedusaApp from "../loaders/medusa-app"
|
||||||
|
import loaders from "../loaders"
|
||||||
|
|
||||||
|
dotenv.config()
|
||||||
|
|
||||||
|
const BATCH_SIZE = 100
|
||||||
|
|
||||||
|
const migrateProductVariant = async (
|
||||||
|
variant: ProductVariant,
|
||||||
|
{
|
||||||
|
container,
|
||||||
|
}: { container: MedusaContainer; transactionManager: EntityManager }
|
||||||
|
) => {
|
||||||
|
const pricingService: IPricingModuleService = container.resolve(
|
||||||
|
"pricingModuleService"
|
||||||
|
)
|
||||||
|
|
||||||
|
const configModule = await container.resolve("configModule")
|
||||||
|
const { link } = await loadMedusaApp(
|
||||||
|
{ configModule, container },
|
||||||
|
{ registerInContainer: false }
|
||||||
|
)
|
||||||
|
|
||||||
|
if (!link) {
|
||||||
|
throw new MedusaError(
|
||||||
|
MedusaError.Types.NOT_ALLOWED,
|
||||||
|
"Can't migrate money_amounts: Pricing module is not configured correctly"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const priceSet = await pricingService.create({
|
||||||
|
rules: [{ rule_attribute: "region_id" }],
|
||||||
|
prices: variant.prices.map((price) => ({
|
||||||
|
rules: {
|
||||||
|
region_id: price.region_id,
|
||||||
|
},
|
||||||
|
currency_code: price.currency_code,
|
||||||
|
min_quantity: price.min_quantity,
|
||||||
|
max_quantity: price.max_quantity,
|
||||||
|
amount: price.amount,
|
||||||
|
})),
|
||||||
|
})
|
||||||
|
|
||||||
|
await link.create({
|
||||||
|
productService: {
|
||||||
|
variant_id: variant.id,
|
||||||
|
},
|
||||||
|
pricingService: {
|
||||||
|
price_set_id: priceSet.id,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const processBatch = async (
|
||||||
|
variants: ProductVariant[],
|
||||||
|
container: MedusaContainer
|
||||||
|
) => {
|
||||||
|
const manager = container.resolve("manager")
|
||||||
|
return await manager.transaction(async (transactionManager) => {
|
||||||
|
await Promise.all(
|
||||||
|
variants.map(async (variant) => {
|
||||||
|
await migrateProductVariant(variant, {
|
||||||
|
container,
|
||||||
|
transactionManager,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const migrate = async function ({ directory }) {
|
||||||
|
const app = express()
|
||||||
|
const { container } = await loaders({
|
||||||
|
directory,
|
||||||
|
expressApp: app,
|
||||||
|
isTest: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
const variantService: ProductVariantService = await container.resolve(
|
||||||
|
"productVariantService"
|
||||||
|
)
|
||||||
|
const featureFlagRouter: FlagRouter = await container.resolve(
|
||||||
|
"featureFlagRouter"
|
||||||
|
)
|
||||||
|
|
||||||
|
if (
|
||||||
|
!featureFlagRouter.isFeatureEnabled(IsolatePricingDomainFeatureFlag.key) &&
|
||||||
|
!featureFlagRouter.isFeatureEnabled(Modules.PRICING)
|
||||||
|
) {
|
||||||
|
throw new MedusaError(
|
||||||
|
MedusaError.Types.NOT_ALLOWED,
|
||||||
|
"Pricing module not enabled"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
await createDefaultRuleTypes(container)
|
||||||
|
|
||||||
|
const [variants, totalCount] = await variantService.listAndCount(
|
||||||
|
{},
|
||||||
|
{ take: BATCH_SIZE, order: { id: "ASC" }, relations: ["prices"] }
|
||||||
|
)
|
||||||
|
|
||||||
|
await processBatch(variants, container)
|
||||||
|
|
||||||
|
let processedCount = variants.length
|
||||||
|
|
||||||
|
console.log(`Processed ${processedCount} of ${totalCount}`)
|
||||||
|
|
||||||
|
while (processedCount < totalCount) {
|
||||||
|
const nextBatch = await variantService.list(
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
skip: processedCount,
|
||||||
|
take: BATCH_SIZE,
|
||||||
|
order: { id: "ASC" },
|
||||||
|
relations: ["prices"],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
await processBatch(nextBatch, container)
|
||||||
|
|
||||||
|
processedCount += nextBatch.length
|
||||||
|
console.log(`Processed ${processedCount} of ${totalCount}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("Done")
|
||||||
|
process.exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
migrate({ directory: process.cwd() })
|
||||||
@@ -5,9 +5,15 @@ export const PricingServiceMock = {
|
|||||||
setProductPrices: jest.fn().mockImplementation((prod) => {
|
setProductPrices: jest.fn().mockImplementation((prod) => {
|
||||||
return Promise.resolve(prod)
|
return Promise.resolve(prod)
|
||||||
}),
|
}),
|
||||||
|
setAdminProductPricing: jest.fn().mockImplementation((prod) => {
|
||||||
|
return Promise.resolve(prod)
|
||||||
|
}),
|
||||||
setVariantPrices: jest.fn().mockImplementation(([variant]) => {
|
setVariantPrices: jest.fn().mockImplementation(([variant]) => {
|
||||||
return Promise.resolve([variant])
|
return Promise.resolve([variant])
|
||||||
}),
|
}),
|
||||||
|
setAdminVariantPricing: jest.fn().mockImplementation(([variant]) => {
|
||||||
|
return Promise.resolve([variant])
|
||||||
|
}),
|
||||||
setShippingOptionPrices: jest.fn().mockImplementation((opts) => {
|
setShippingOptionPrices: jest.fn().mockImplementation((opts) => {
|
||||||
return Promise.resolve(opts)
|
return Promise.resolve(opts)
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -1,28 +1,29 @@
|
|||||||
import { FlagRouter } from "@medusajs/utils"
|
|
||||||
import { asClass, asValue, createContainer } from "awilix"
|
|
||||||
import _ from "lodash"
|
|
||||||
import { MedusaError } from "medusa-core-utils"
|
|
||||||
import { IdMap, MockManager, MockRepository } from "medusa-test-utils"
|
import { IdMap, MockManager, MockRepository } from "medusa-test-utils"
|
||||||
import { IsNull, Not } from "typeorm"
|
import { IsNull, Not } from "typeorm"
|
||||||
import { PaymentSessionStatus } from "../../models"
|
import { NewTotalsService, PricingService, TaxProviderService } from "../index"
|
||||||
import TaxCalculationStrategy from "../../strategies/tax-calculation"
|
import { asClass, asValue, createContainer } from "awilix"
|
||||||
import { cacheServiceMock } from "../__mocks__/cache"
|
|
||||||
|
import CartService from "../cart"
|
||||||
import { CustomerServiceMock } from "../__mocks__/customer"
|
import { CustomerServiceMock } from "../__mocks__/customer"
|
||||||
import { EventBusServiceMock } from "../__mocks__/event-bus"
|
import { EventBusServiceMock } from "../__mocks__/event-bus"
|
||||||
import { LineItemServiceMock } from "../__mocks__/line-item"
|
import { FlagRouter } from "@medusajs/utils"
|
||||||
import { LineItemAdjustmentServiceMock } from "../__mocks__/line-item-adjustment"
|
import { LineItemAdjustmentServiceMock } from "../__mocks__/line-item-adjustment"
|
||||||
import { newTotalsServiceMock } from "../__mocks__/new-totals"
|
import { LineItemServiceMock } from "../__mocks__/line-item"
|
||||||
|
import { MedusaError } from "medusa-core-utils"
|
||||||
import { PaymentProviderServiceMock } from "../__mocks__/payment-provider"
|
import { PaymentProviderServiceMock } from "../__mocks__/payment-provider"
|
||||||
|
import { PaymentSessionStatus } from "../../models"
|
||||||
import { ProductServiceMock } from "../__mocks__/product"
|
import { ProductServiceMock } from "../__mocks__/product"
|
||||||
import { ProductVariantServiceMock } from "../__mocks__/product-variant"
|
|
||||||
import { ProductVariantInventoryServiceMock } from "../__mocks__/product-variant-inventory"
|
import { ProductVariantInventoryServiceMock } from "../__mocks__/product-variant-inventory"
|
||||||
|
import { ProductVariantServiceMock } from "../__mocks__/product-variant"
|
||||||
import { RegionServiceMock } from "../__mocks__/region"
|
import { RegionServiceMock } from "../__mocks__/region"
|
||||||
import { ShippingOptionServiceMock } from "../__mocks__/shipping-option"
|
import { ShippingOptionServiceMock } from "../__mocks__/shipping-option"
|
||||||
import { ShippingProfileServiceMock } from "../__mocks__/shipping-profile"
|
import { ShippingProfileServiceMock } from "../__mocks__/shipping-profile"
|
||||||
import { taxProviderServiceMock } from "../__mocks__/tax-provider"
|
|
||||||
import CartService from "../cart"
|
|
||||||
import { NewTotalsService, PricingService, TaxProviderService } from "../index"
|
|
||||||
import SystemTaxService from "../system-tax"
|
import SystemTaxService from "../system-tax"
|
||||||
|
import TaxCalculationStrategy from "../../strategies/tax-calculation"
|
||||||
|
import _ from "lodash"
|
||||||
|
import { cacheServiceMock } from "../__mocks__/cache"
|
||||||
|
import { newTotalsServiceMock } from "../__mocks__/new-totals"
|
||||||
|
import { taxProviderServiceMock } from "../__mocks__/tax-provider"
|
||||||
|
|
||||||
const eventBusService = {
|
const eventBusService = {
|
||||||
emit: jest.fn(),
|
emit: jest.fn(),
|
||||||
@@ -2657,6 +2658,8 @@ describe("CartService", () => {
|
|||||||
.register("taxProviderService", asClass(TaxProviderService))
|
.register("taxProviderService", asClass(TaxProviderService))
|
||||||
.register("newTotalsService", asClass(NewTotalsService))
|
.register("newTotalsService", asClass(NewTotalsService))
|
||||||
.register("cartService", asClass(CartService))
|
.register("cartService", asClass(CartService))
|
||||||
|
.register("remoteQuery", asValue(null))
|
||||||
|
.register("pricingModuleService", asValue(undefined))
|
||||||
.register("pricingService", asClass(PricingService))
|
.register("pricingService", asClass(PricingService))
|
||||||
|
|
||||||
const cartService = container.resolve("cartService")
|
const cartService = container.resolve("cartService")
|
||||||
|
|||||||
@@ -1,14 +1,22 @@
|
|||||||
import { FlagRouter } from "@medusajs/utils"
|
import {
|
||||||
import { MedusaError } from "medusa-core-utils"
|
CalculatedPriceSetDTO,
|
||||||
import { EntityManager } from "typeorm"
|
IPricingModuleService,
|
||||||
import { ProductVariantService, RegionService, TaxProviderService } from "."
|
PriceSetMoneyAmountDTO,
|
||||||
import { TransactionBaseService } from "../interfaces"
|
RemoteJoinerQuery,
|
||||||
|
RemoteQueryFunction,
|
||||||
|
} from "@medusajs/types"
|
||||||
|
import { FlagRouter, removeNullish } from "@medusajs/utils"
|
||||||
import {
|
import {
|
||||||
IPriceSelectionStrategy,
|
IPriceSelectionStrategy,
|
||||||
PriceSelectionContext,
|
PriceSelectionContext,
|
||||||
} from "../interfaces/price-selection-strategy"
|
} from "../interfaces/price-selection-strategy"
|
||||||
import TaxInclusivePricingFeatureFlag from "../loaders/feature-flags/tax-inclusive-pricing"
|
import {
|
||||||
import { Product, ProductVariant, Region, ShippingOption } from "../models"
|
MoneyAmount,
|
||||||
|
Product,
|
||||||
|
ProductVariant,
|
||||||
|
Region,
|
||||||
|
ShippingOption,
|
||||||
|
} from "../models"
|
||||||
import {
|
import {
|
||||||
PricedProduct,
|
PricedProduct,
|
||||||
PricedShippingOption,
|
PricedShippingOption,
|
||||||
@@ -17,7 +25,15 @@ import {
|
|||||||
ProductVariantPricing,
|
ProductVariantPricing,
|
||||||
TaxedPricing,
|
TaxedPricing,
|
||||||
} from "../types/pricing"
|
} from "../types/pricing"
|
||||||
|
import { ProductVariantService, RegionService, TaxProviderService } from "."
|
||||||
|
|
||||||
|
import { EntityManager } from "typeorm"
|
||||||
|
import IsolatePricingDomainFeatureFlag from "../loaders/feature-flags/isolate-pricing-domain"
|
||||||
|
import IsolateProductDomainFeatureFlag from "../loaders/feature-flags/isolate-product-domain"
|
||||||
|
import { MedusaError } from "medusa-core-utils"
|
||||||
|
import TaxInclusivePricingFeatureFlag from "../loaders/feature-flags/tax-inclusive-pricing"
|
||||||
import { TaxServiceRate } from "../types/tax-service"
|
import { TaxServiceRate } from "../types/tax-service"
|
||||||
|
import { TransactionBaseService } from "../interfaces"
|
||||||
import { calculatePriceTaxAmount } from "../utils"
|
import { calculatePriceTaxAmount } from "../utils"
|
||||||
|
|
||||||
type InjectedDependencies = {
|
type InjectedDependencies = {
|
||||||
@@ -27,6 +43,8 @@ type InjectedDependencies = {
|
|||||||
regionService: RegionService
|
regionService: RegionService
|
||||||
priceSelectionStrategy: IPriceSelectionStrategy
|
priceSelectionStrategy: IPriceSelectionStrategy
|
||||||
featureFlagRouter: FlagRouter
|
featureFlagRouter: FlagRouter
|
||||||
|
remoteQuery: RemoteQueryFunction
|
||||||
|
pricingModuleService: IPricingModuleService
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -38,6 +56,8 @@ class PricingService extends TransactionBaseService {
|
|||||||
protected readonly priceSelectionStrategy: IPriceSelectionStrategy
|
protected readonly priceSelectionStrategy: IPriceSelectionStrategy
|
||||||
protected readonly productVariantService: ProductVariantService
|
protected readonly productVariantService: ProductVariantService
|
||||||
protected readonly featureFlagRouter: FlagRouter
|
protected readonly featureFlagRouter: FlagRouter
|
||||||
|
protected readonly pricingModuleService: IPricingModuleService
|
||||||
|
protected readonly remoteQuery: RemoteQueryFunction
|
||||||
|
|
||||||
constructor({
|
constructor({
|
||||||
productVariantService,
|
productVariantService,
|
||||||
@@ -45,6 +65,8 @@ class PricingService extends TransactionBaseService {
|
|||||||
regionService,
|
regionService,
|
||||||
priceSelectionStrategy,
|
priceSelectionStrategy,
|
||||||
featureFlagRouter,
|
featureFlagRouter,
|
||||||
|
remoteQuery,
|
||||||
|
pricingModuleService,
|
||||||
}: InjectedDependencies) {
|
}: InjectedDependencies) {
|
||||||
// eslint-disable-next-line prefer-rest-params
|
// eslint-disable-next-line prefer-rest-params
|
||||||
super(arguments[0])
|
super(arguments[0])
|
||||||
@@ -54,6 +76,8 @@ class PricingService extends TransactionBaseService {
|
|||||||
this.priceSelectionStrategy = priceSelectionStrategy
|
this.priceSelectionStrategy = priceSelectionStrategy
|
||||||
this.productVariantService = productVariantService
|
this.productVariantService = productVariantService
|
||||||
this.featureFlagRouter = featureFlagRouter
|
this.featureFlagRouter = featureFlagRouter
|
||||||
|
this.pricingModuleService = pricingModuleService
|
||||||
|
this.remoteQuery = remoteQuery
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -160,6 +184,90 @@ class PricingService extends TransactionBaseService {
|
|||||||
return taxedPricing
|
return taxedPricing
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async getProductVariantPricingModulePricing_(
|
||||||
|
variantPriceData: {
|
||||||
|
variantId: string
|
||||||
|
quantity?: number
|
||||||
|
}[],
|
||||||
|
context: PricingContext
|
||||||
|
) {
|
||||||
|
const variables = {
|
||||||
|
variant_id: variantPriceData.map((pricedata) => pricedata.variantId),
|
||||||
|
}
|
||||||
|
|
||||||
|
const query = {
|
||||||
|
product_variant_price_set: {
|
||||||
|
__args: variables,
|
||||||
|
fields: ["variant_id", "price_set_id"],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const variantPriceSets = await this.remoteQuery(query)
|
||||||
|
|
||||||
|
const variantIdToPriceSetIdMap: Map<string, string> = new Map(
|
||||||
|
variantPriceSets.map((variantPriceSet) => [
|
||||||
|
variantPriceSet.variant_id,
|
||||||
|
variantPriceSet.price_set_id,
|
||||||
|
])
|
||||||
|
)
|
||||||
|
|
||||||
|
const priceSetIds: string[] = variantPriceSets.map(
|
||||||
|
(variantPriceSet) => variantPriceSet.price_set_id
|
||||||
|
)
|
||||||
|
|
||||||
|
const queryContext: PriceSelectionContext = removeNullish(
|
||||||
|
context.price_selection
|
||||||
|
)
|
||||||
|
|
||||||
|
let priceSets: CalculatedPriceSetDTO[] = []
|
||||||
|
|
||||||
|
if (queryContext.currency_code) {
|
||||||
|
priceSets = (await this.pricingModuleService.calculatePrices(
|
||||||
|
{ id: priceSetIds },
|
||||||
|
{
|
||||||
|
context: queryContext as any,
|
||||||
|
}
|
||||||
|
)) as unknown as CalculatedPriceSetDTO[]
|
||||||
|
}
|
||||||
|
|
||||||
|
const priceSetMap = new Map<string, CalculatedPriceSetDTO>(
|
||||||
|
priceSets.map((priceSet) => [priceSet.id, priceSet])
|
||||||
|
)
|
||||||
|
|
||||||
|
const pricingResultMap = new Map()
|
||||||
|
|
||||||
|
variantPriceData.forEach(({ variantId }) => {
|
||||||
|
const priceSetId = variantIdToPriceSetIdMap.get(variantId)
|
||||||
|
|
||||||
|
const pricingResult: ProductVariantPricing = {
|
||||||
|
prices: [] as MoneyAmount[],
|
||||||
|
original_price: null,
|
||||||
|
calculated_price: null,
|
||||||
|
calculated_price_type: null,
|
||||||
|
original_price_includes_tax: null,
|
||||||
|
calculated_price_includes_tax: null,
|
||||||
|
original_price_incl_tax: null,
|
||||||
|
calculated_price_incl_tax: null,
|
||||||
|
original_tax: null,
|
||||||
|
calculated_tax: null,
|
||||||
|
tax_rates: null,
|
||||||
|
}
|
||||||
|
|
||||||
|
if (priceSetId) {
|
||||||
|
const prices = priceSetMap.get(priceSetId)
|
||||||
|
|
||||||
|
if (prices) {
|
||||||
|
pricingResult.prices = [prices] as MoneyAmount[]
|
||||||
|
pricingResult.original_price = prices.amount
|
||||||
|
pricingResult.calculated_price = prices.amount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pricingResultMap.set(variantId, pricingResult)
|
||||||
|
})
|
||||||
|
|
||||||
|
return pricingResultMap
|
||||||
|
}
|
||||||
|
|
||||||
private async getProductVariantPricing_(
|
private async getProductVariantPricing_(
|
||||||
data: {
|
data: {
|
||||||
variantId: string
|
variantId: string
|
||||||
@@ -167,6 +275,17 @@ class PricingService extends TransactionBaseService {
|
|||||||
}[],
|
}[],
|
||||||
context: PricingContext
|
context: PricingContext
|
||||||
): Promise<Map<string, ProductVariantPricing>> {
|
): Promise<Map<string, ProductVariantPricing>> {
|
||||||
|
if (
|
||||||
|
this.featureFlagRouter.isFeatureEnabled(
|
||||||
|
IsolateProductDomainFeatureFlag.key
|
||||||
|
) &&
|
||||||
|
this.featureFlagRouter.isFeatureEnabled(
|
||||||
|
IsolatePricingDomainFeatureFlag.key
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return await this.getProductVariantPricingModulePricing_(data, context)
|
||||||
|
}
|
||||||
|
|
||||||
const variantsPricing = await this.priceSelectionStrategy
|
const variantsPricing = await this.priceSelectionStrategy
|
||||||
.withTransaction(this.activeManager_)
|
.withTransaction(this.activeManager_)
|
||||||
.calculateVariantPrice(data, context.price_selection)
|
.calculateVariantPrice(data, context.price_selection)
|
||||||
@@ -509,6 +628,7 @@ class PricingService extends TransactionBaseService {
|
|||||||
product.variants.map((productVariant): PricedVariant => {
|
product.variants.map((productVariant): PricedVariant => {
|
||||||
const variantPricing = productsVariantsPricingMap.get(product.id)!
|
const variantPricing = productsVariantsPricingMap.get(product.id)!
|
||||||
const pricing = variantPricing[productVariant.id]
|
const pricing = variantPricing[productVariant.id]
|
||||||
|
|
||||||
Object.assign(productVariant, pricing)
|
Object.assign(productVariant, pricing)
|
||||||
return productVariant as unknown as PricedVariant
|
return productVariant as unknown as PricedVariant
|
||||||
})
|
})
|
||||||
@@ -517,6 +637,167 @@ class PricingService extends TransactionBaseService {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async getPricingModuleVariantMoneyAmounts(
|
||||||
|
variantIds: string[]
|
||||||
|
): Promise<Map<string, MoneyAmount[]>> {
|
||||||
|
const variables = {
|
||||||
|
variant_id: variantIds,
|
||||||
|
}
|
||||||
|
|
||||||
|
const query = {
|
||||||
|
product_variant_price_set: {
|
||||||
|
__args: variables,
|
||||||
|
fields: ["variant_id", "price_set_id"],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const variantPriceSets = await this.remoteQuery(query)
|
||||||
|
|
||||||
|
const priceSetIdToVariantIdMap: Map<string, string> = new Map(
|
||||||
|
variantPriceSets.map((variantPriceSet) => [
|
||||||
|
variantPriceSet.price_set_id,
|
||||||
|
variantPriceSet.variant_id,
|
||||||
|
])
|
||||||
|
)
|
||||||
|
|
||||||
|
const priceSetIds: string[] = variantPriceSets.map(
|
||||||
|
(variantPriceSet) => variantPriceSet.price_set_id
|
||||||
|
)
|
||||||
|
|
||||||
|
const priceSetMoneyAmounts: PriceSetMoneyAmountDTO[] =
|
||||||
|
await this.pricingModuleService.listPriceSetMoneyAmounts(
|
||||||
|
{
|
||||||
|
price_set_id: priceSetIds,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
relations: [
|
||||||
|
"money_amount",
|
||||||
|
"price_set",
|
||||||
|
"price_rules",
|
||||||
|
"price_rules.rule_type",
|
||||||
|
],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const variantIdMoneyAmountMap = priceSetMoneyAmounts.reduce(
|
||||||
|
(map, priceSetMoneyAmount) => {
|
||||||
|
const variantId = priceSetIdToVariantIdMap.get(
|
||||||
|
priceSetMoneyAmount.price_set!.id
|
||||||
|
)
|
||||||
|
if (!variantId) {
|
||||||
|
return map
|
||||||
|
}
|
||||||
|
|
||||||
|
const regionId = priceSetMoneyAmount.price_rules!.find(
|
||||||
|
(pr) => pr.rule_type.rule_attribute === "region_id"
|
||||||
|
)?.value
|
||||||
|
|
||||||
|
const moneyAmount = {
|
||||||
|
...priceSetMoneyAmount.money_amount,
|
||||||
|
region_id: null as null | string,
|
||||||
|
}
|
||||||
|
|
||||||
|
if (regionId) {
|
||||||
|
moneyAmount.region_id = regionId
|
||||||
|
}
|
||||||
|
|
||||||
|
if (map.has(variantId)) {
|
||||||
|
map.get(variantId).push(moneyAmount)
|
||||||
|
} else {
|
||||||
|
map.set(variantId, [moneyAmount])
|
||||||
|
}
|
||||||
|
return map
|
||||||
|
},
|
||||||
|
new Map()
|
||||||
|
)
|
||||||
|
|
||||||
|
return variantIdMoneyAmountMap
|
||||||
|
}
|
||||||
|
|
||||||
|
async setAdminVariantPricing(
|
||||||
|
variants: ProductVariant[],
|
||||||
|
context: PriceSelectionContext = {}
|
||||||
|
): Promise<PricedVariant[]> {
|
||||||
|
if (
|
||||||
|
!this.featureFlagRouter.isFeatureEnabled(
|
||||||
|
IsolatePricingDomainFeatureFlag.key
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return await this.setVariantPrices(variants, context)
|
||||||
|
}
|
||||||
|
|
||||||
|
const variantIds = variants.map((variant) => variant.id)
|
||||||
|
|
||||||
|
const variantIdMoneyAmountMap =
|
||||||
|
await this.getPricingModuleVariantMoneyAmounts(variantIds)
|
||||||
|
|
||||||
|
return variants.map((variant) => {
|
||||||
|
const pricing: ProductVariantPricing = {
|
||||||
|
prices: variantIdMoneyAmountMap.get(variant.id) ?? [],
|
||||||
|
original_price: null,
|
||||||
|
calculated_price: null,
|
||||||
|
calculated_price_type: null,
|
||||||
|
original_price_includes_tax: null,
|
||||||
|
calculated_price_includes_tax: null,
|
||||||
|
original_price_incl_tax: null,
|
||||||
|
calculated_price_incl_tax: null,
|
||||||
|
original_tax: null,
|
||||||
|
calculated_tax: null,
|
||||||
|
tax_rates: null,
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.assign(variant, pricing)
|
||||||
|
return variant as unknown as PricedVariant
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async setAdminProductPricing(
|
||||||
|
products: Product[]
|
||||||
|
): Promise<(Product | PricedProduct)[]> {
|
||||||
|
if (
|
||||||
|
!this.featureFlagRouter.isFeatureEnabled(
|
||||||
|
IsolatePricingDomainFeatureFlag.key
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return await this.setProductPrices(products)
|
||||||
|
}
|
||||||
|
|
||||||
|
const variantIds = products
|
||||||
|
.map((product) => product.variants.map((variant) => variant.id).flat())
|
||||||
|
.flat()
|
||||||
|
|
||||||
|
const variantIdMoneyAmountMap =
|
||||||
|
await this.getPricingModuleVariantMoneyAmounts(variantIds)
|
||||||
|
|
||||||
|
return products.map((product) => {
|
||||||
|
if (!product?.variants?.length) {
|
||||||
|
return product
|
||||||
|
}
|
||||||
|
|
||||||
|
product.variants.map((productVariant): PricedVariant => {
|
||||||
|
const pricing: ProductVariantPricing = {
|
||||||
|
prices: variantIdMoneyAmountMap.get(productVariant.id) ?? [],
|
||||||
|
original_price: null,
|
||||||
|
calculated_price: null,
|
||||||
|
calculated_price_type: null,
|
||||||
|
original_price_includes_tax: null,
|
||||||
|
calculated_price_includes_tax: null,
|
||||||
|
original_price_incl_tax: null,
|
||||||
|
calculated_price_incl_tax: null,
|
||||||
|
original_tax: null,
|
||||||
|
calculated_tax: null,
|
||||||
|
tax_rates: null,
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.assign(productVariant, pricing)
|
||||||
|
|
||||||
|
return productVariant as unknown as PricedVariant
|
||||||
|
})
|
||||||
|
|
||||||
|
return product
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the prices for a shipping option.
|
* Gets the prices for a shipping option.
|
||||||
* @param shippingOption - the shipping option to get prices for
|
* @param shippingOption - the shipping option to get prices for
|
||||||
|
|||||||
@@ -124,3 +124,14 @@ export type PricedVariant = Partial<ProductVariant> & ProductVariantPricing
|
|||||||
export type PricedProduct = Omit<Partial<Product>, "variants"> & {
|
export type PricedProduct = Omit<Partial<Product>, "variants"> & {
|
||||||
variants: PricedVariant[]
|
variants: PricedVariant[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type VariantPriceSetRes = {
|
||||||
|
id: string
|
||||||
|
title: string
|
||||||
|
price: PriceModulePrice | PriceModulePrice[]
|
||||||
|
}
|
||||||
|
|
||||||
|
type PriceModulePrice = {
|
||||||
|
variant_id: string
|
||||||
|
price_set_id: string
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import {
|
||||||
|
ConfigModule,
|
||||||
|
ExternalModuleDeclaration,
|
||||||
|
InternalModuleDeclaration,
|
||||||
|
} from "@medusajs/types"
|
||||||
|
|
||||||
|
import { ModulesDefinition } from "@medusajs/modules-sdk"
|
||||||
|
import { isObject } from "./is-object"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merge the modules config from the medusa-config file with the modules config from medusa package
|
||||||
|
* @param modules
|
||||||
|
* @param medusaInternalModulesConfig
|
||||||
|
*/
|
||||||
|
export function mergeModulesConfig(
|
||||||
|
modules: ConfigModule["modules"],
|
||||||
|
medusaInternalModulesConfig
|
||||||
|
) {
|
||||||
|
for (const [moduleName, moduleConfig] of Object.entries(modules as any)) {
|
||||||
|
const moduleDefinition = ModulesDefinition[moduleName]
|
||||||
|
|
||||||
|
if (moduleDefinition?.isLegacy) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
const isModuleEnabled = moduleConfig === true || isObject(moduleConfig)
|
||||||
|
|
||||||
|
if (!isModuleEnabled) {
|
||||||
|
delete medusaInternalModulesConfig[moduleName]
|
||||||
|
} else {
|
||||||
|
medusaInternalModulesConfig[moduleName] = moduleConfig as Partial<
|
||||||
|
InternalModuleDeclaration | ExternalModuleDeclaration
|
||||||
|
>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -139,7 +139,8 @@ export async function loadModuleMigrations(
|
|||||||
): Promise<[Function | undefined, Function | undefined]> {
|
): Promise<[Function | undefined, Function | undefined]> {
|
||||||
let loadedModule: ModuleExports
|
let loadedModule: ModuleExports
|
||||||
try {
|
try {
|
||||||
loadedModule = (await import(resolution.resolutionPath as string)).default
|
loadedModule = await import(resolution.resolutionPath as string)
|
||||||
|
|
||||||
return [loadedModule.runMigrations, loadedModule.revertMigration]
|
return [loadedModule.runMigrations, loadedModule.revertMigration]
|
||||||
} catch {
|
} catch {
|
||||||
return [undefined, undefined]
|
return [undefined, undefined]
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
import { mergeTypeDefs } from "@graphql-tools/merge"
|
|
||||||
import { makeExecutableSchema } from "@graphql-tools/schema"
|
|
||||||
import { RemoteFetchDataCallback } from "@medusajs/orchestration"
|
|
||||||
import {
|
import {
|
||||||
ExternalModuleDeclaration,
|
ExternalModuleDeclaration,
|
||||||
InternalModuleDeclaration,
|
InternalModuleDeclaration,
|
||||||
@@ -26,15 +23,18 @@ import {
|
|||||||
Modules,
|
Modules,
|
||||||
} from "./definitions"
|
} from "./definitions"
|
||||||
import { MedusaModule } from "./medusa-module"
|
import { MedusaModule } from "./medusa-module"
|
||||||
|
import { RemoteFetchDataCallback } from "@medusajs/orchestration"
|
||||||
import { RemoteLink } from "./remote-link"
|
import { RemoteLink } from "./remote-link"
|
||||||
import { RemoteQuery } from "./remote-query"
|
import { RemoteQuery } from "./remote-query"
|
||||||
import { cleanGraphQLSchema } from "./utils"
|
import { cleanGraphQLSchema } from "./utils"
|
||||||
import { asValue } from "awilix"
|
import { asValue } from "awilix"
|
||||||
|
import { makeExecutableSchema } from "@graphql-tools/schema"
|
||||||
|
import { mergeTypeDefs } from "@graphql-tools/merge"
|
||||||
|
|
||||||
const LinkModulePackage = "@medusajs/link-modules"
|
const LinkModulePackage = "@medusajs/link-modules"
|
||||||
|
|
||||||
export type RunMigrationFn = (
|
export type RunMigrationFn = (
|
||||||
options: Omit<LoaderOptions<ModuleServiceInitializeOptions>, "container">,
|
options?: ModuleServiceInitializeOptions,
|
||||||
injectedDependencies?: Record<any, any>
|
injectedDependencies?: Record<any, any>
|
||||||
) => Promise<void>
|
) => Promise<void>
|
||||||
|
|
||||||
@@ -162,6 +162,18 @@ function registerCustomJoinerConfigs(servicesConfig: ModuleJoinerConfig[]) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type MedusaAppOutput = {
|
||||||
|
modules: Record<string, LoadedModule | LoadedModule[]>
|
||||||
|
link: RemoteLink | undefined
|
||||||
|
query: (
|
||||||
|
query: string | RemoteJoinerQuery | object,
|
||||||
|
variables?: Record<string, unknown>
|
||||||
|
) => Promise<any>
|
||||||
|
entitiesMap?: Record<string, any>
|
||||||
|
notFound?: Record<string, Record<string, string>>
|
||||||
|
runMigrations: RunMigrationFn
|
||||||
|
}
|
||||||
|
|
||||||
export async function MedusaApp(
|
export async function MedusaApp(
|
||||||
{
|
{
|
||||||
sharedContainer,
|
sharedContainer,
|
||||||
@@ -276,19 +288,24 @@ export async function MedusaApp(
|
|||||||
return await remoteQuery.query(query, variables)
|
return await remoteQuery.query(query, variables)
|
||||||
}
|
}
|
||||||
|
|
||||||
const runMigrations: RunMigrationFn = async (): Promise<void> => {
|
const runMigrations: RunMigrationFn = async (
|
||||||
|
linkModuleOptions
|
||||||
|
): Promise<void> => {
|
||||||
for (const moduleName of Object.keys(allModules)) {
|
for (const moduleName of Object.keys(allModules)) {
|
||||||
const loadedModule = allModules[moduleName]
|
const moduleResolution = MedusaModule.getModuleResolutions(moduleName)
|
||||||
|
|
||||||
await MedusaModule.migrateUp(
|
await MedusaModule.migrateUp(
|
||||||
loadedModule.definition.key,
|
moduleResolution.definition.key,
|
||||||
loadedModule.resolutionPath,
|
moduleResolution.resolutionPath as string,
|
||||||
loadedModule.options
|
moduleResolution.options
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
linkModuleMigration &&
|
linkModuleMigration &&
|
||||||
(await linkModuleMigration(linkResolution.options, injectedDependencies))
|
(await linkModuleMigration({
|
||||||
|
options: linkModuleOptions,
|
||||||
|
injectedDependencies,
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ export class MedusaModule {
|
|||||||
private static modules_: Map<string, ModuleAlias[]> = new Map()
|
private static modules_: Map<string, ModuleAlias[]> = new Map()
|
||||||
private static loading_: Map<string, Promise<any>> = new Map()
|
private static loading_: Map<string, Promise<any>> = new Map()
|
||||||
private static joinerConfig_: Map<string, ModuleJoinerConfig> = new Map()
|
private static joinerConfig_: Map<string, ModuleJoinerConfig> = new Map()
|
||||||
|
private static moduleResolutions_: Map<string, ModuleResolution> = new Map()
|
||||||
|
|
||||||
public static getLoadedModules(
|
public static getLoadedModules(
|
||||||
aliases?: Map<string, string>
|
aliases?: Map<string, string>
|
||||||
@@ -88,6 +89,7 @@ export class MedusaModule {
|
|||||||
MedusaModule.instances_.clear()
|
MedusaModule.instances_.clear()
|
||||||
MedusaModule.modules_.clear()
|
MedusaModule.modules_.clear()
|
||||||
MedusaModule.joinerConfig_.clear()
|
MedusaModule.joinerConfig_.clear()
|
||||||
|
MedusaModule.moduleResolutions_.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
public static isInstalled(moduleKey: string, alias?: string): boolean {
|
public static isInstalled(moduleKey: string, alias?: string): boolean {
|
||||||
@@ -109,11 +111,29 @@ export class MedusaModule {
|
|||||||
return [...MedusaModule.joinerConfig_.values()]
|
return [...MedusaModule.joinerConfig_.values()]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static getModuleResolutions(moduleKey: string): ModuleResolution {
|
||||||
|
return MedusaModule.moduleResolutions_.get(moduleKey)!
|
||||||
|
}
|
||||||
|
|
||||||
|
public static getAllModuleResolutions(): ModuleResolution[] {
|
||||||
|
return [...MedusaModule.moduleResolutions_.values()]
|
||||||
|
}
|
||||||
|
|
||||||
|
public static setModuleResolution(
|
||||||
|
moduleKey: string,
|
||||||
|
resolution: ModuleResolution
|
||||||
|
): ModuleResolution {
|
||||||
|
MedusaModule.moduleResolutions_.set(moduleKey, resolution)
|
||||||
|
|
||||||
|
return resolution
|
||||||
|
}
|
||||||
|
|
||||||
public static setJoinerConfig(
|
public static setJoinerConfig(
|
||||||
moduleKey: string,
|
moduleKey: string,
|
||||||
config: ModuleJoinerConfig
|
config: ModuleJoinerConfig
|
||||||
): ModuleJoinerConfig {
|
): ModuleJoinerConfig {
|
||||||
MedusaModule.joinerConfig_.set(moduleKey, config)
|
MedusaModule.joinerConfig_.set(moduleKey, config)
|
||||||
|
|
||||||
return config
|
return config
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -261,6 +281,8 @@ export class MedusaModule {
|
|||||||
MedusaModule.setJoinerConfig(keyName, joinerConfig)
|
MedusaModule.setJoinerConfig(keyName, joinerConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MedusaModule.setModuleResolution(keyName, resolution)
|
||||||
|
|
||||||
MedusaModule.registerModule(keyName, {
|
MedusaModule.registerModule(keyName, {
|
||||||
key: keyName,
|
key: keyName,
|
||||||
hash: hashKey,
|
hash: hashKey,
|
||||||
@@ -379,6 +401,7 @@ export class MedusaModule {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MedusaModule.setModuleResolution(keyName, resolution)
|
||||||
MedusaModule.registerModule(keyName, {
|
MedusaModule.registerModule(keyName, {
|
||||||
key: keyName,
|
key: keyName,
|
||||||
hash: hashKey,
|
hash: hashKey,
|
||||||
|
|||||||
@@ -1,8 +1,3 @@
|
|||||||
import {
|
|
||||||
RemoteFetchDataCallback,
|
|
||||||
RemoteJoiner,
|
|
||||||
toRemoteJoinerQuery,
|
|
||||||
} from "@medusajs/orchestration"
|
|
||||||
import {
|
import {
|
||||||
JoinerRelationship,
|
JoinerRelationship,
|
||||||
JoinerServiceConfig,
|
JoinerServiceConfig,
|
||||||
@@ -11,7 +6,13 @@ import {
|
|||||||
RemoteExpandProperty,
|
RemoteExpandProperty,
|
||||||
RemoteJoinerQuery,
|
RemoteJoinerQuery,
|
||||||
} from "@medusajs/types"
|
} from "@medusajs/types"
|
||||||
|
import {
|
||||||
|
RemoteFetchDataCallback,
|
||||||
|
RemoteJoiner,
|
||||||
|
toRemoteJoinerQuery,
|
||||||
|
} from "@medusajs/orchestration"
|
||||||
import { isString, toPascalCase } from "@medusajs/utils"
|
import { isString, toPascalCase } from "@medusajs/utils"
|
||||||
|
|
||||||
import { MedusaModule } from "./medusa-module"
|
import { MedusaModule } from "./medusa-module"
|
||||||
|
|
||||||
export class RemoteQuery {
|
export class RemoteQuery {
|
||||||
@@ -28,14 +29,14 @@ export class RemoteQuery {
|
|||||||
customRemoteFetchData?: RemoteFetchDataCallback
|
customRemoteFetchData?: RemoteFetchDataCallback
|
||||||
servicesConfig?: ModuleJoinerConfig[]
|
servicesConfig?: ModuleJoinerConfig[]
|
||||||
}) {
|
}) {
|
||||||
|
const servicesConfig_ = [...servicesConfig]
|
||||||
|
|
||||||
if (!modulesLoaded?.length) {
|
if (!modulesLoaded?.length) {
|
||||||
modulesLoaded = MedusaModule.getLoadedModules().map(
|
modulesLoaded = MedusaModule.getLoadedModules().map(
|
||||||
(mod) => Object.values(mod)[0]
|
(mod) => Object.values(mod)[0]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const servicesConfig_ = [...servicesConfig]
|
|
||||||
|
|
||||||
for (const mod of modulesLoaded) {
|
for (const mod of modulesLoaded) {
|
||||||
if (!mod.__definition.isQueryable) {
|
if (!mod.__definition.isQueryable) {
|
||||||
continue
|
continue
|
||||||
@@ -54,6 +55,7 @@ export class RemoteQuery {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.customRemoteFetchData = customRemoteFetchData
|
this.customRemoteFetchData = customRemoteFetchData
|
||||||
|
|
||||||
this.remoteJoiner = new RemoteJoiner(
|
this.remoteJoiner = new RemoteJoiner(
|
||||||
servicesConfig_ as JoinerServiceConfig[],
|
servicesConfig_ as JoinerServiceConfig[],
|
||||||
this.remoteFetchData.bind(this)
|
this.remoteFetchData.bind(this)
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ import {
|
|||||||
MedusaContext,
|
MedusaContext,
|
||||||
MedusaError,
|
MedusaError,
|
||||||
groupBy,
|
groupBy,
|
||||||
|
removeNullish,
|
||||||
shouldForceTransaction,
|
shouldForceTransaction,
|
||||||
} from "@medusajs/utils"
|
} from "@medusajs/utils"
|
||||||
|
|
||||||
@@ -312,7 +313,9 @@ export default class PricingModuleService<
|
|||||||
sharedContext
|
sharedContext
|
||||||
)
|
)
|
||||||
|
|
||||||
const numberOfRules = ma.rules ? Object.entries(ma.rules).length : 0
|
const cleanRules = ma.rules ? removeNullish(ma.rules) : {}
|
||||||
|
|
||||||
|
const numberOfRules = Object.entries(cleanRules).length
|
||||||
|
|
||||||
const [priceSetMoneyAmount] =
|
const [priceSetMoneyAmount] =
|
||||||
await this.priceSetMoneyAmountService_.create(
|
await this.priceSetMoneyAmountService_.create(
|
||||||
@@ -328,7 +331,7 @@ export default class PricingModuleService<
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (numberOfRules) {
|
if (numberOfRules) {
|
||||||
const priceSetRulesCreate = Object.entries(ma.rules).map(
|
const priceSetRulesCreate = Object.entries(cleanRules).map(
|
||||||
([k, v]) => ({
|
([k, v]) => ({
|
||||||
price_set_money_amount: priceSetMoneyAmount,
|
price_set_money_amount: priceSetMoneyAmount,
|
||||||
rule_type: ruleTypeMap.get(k),
|
rule_type: ruleTypeMap.get(k),
|
||||||
@@ -1017,6 +1020,48 @@ export default class PricingModuleService<
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@InjectManager("baseRepository_")
|
||||||
|
async listPriceSetMoneyAmounts(
|
||||||
|
filters: PricingTypes.FilterablePriceSetMoneyAmountProps = {},
|
||||||
|
config: FindConfig<PricingTypes.PriceSetMoneyAmountDTO> = {},
|
||||||
|
@MedusaContext() sharedContext: Context = {}
|
||||||
|
): Promise<PricingTypes.PriceSetMoneyAmountDTO[]> {
|
||||||
|
const records = await this.priceSetMoneyAmountService_.list(
|
||||||
|
filters,
|
||||||
|
config,
|
||||||
|
sharedContext
|
||||||
|
)
|
||||||
|
|
||||||
|
return this.baseRepository_.serialize<
|
||||||
|
PricingTypes.PriceSetMoneyAmountRulesDTO[]
|
||||||
|
>(records, {
|
||||||
|
populate: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
@InjectManager("baseRepository_")
|
||||||
|
async listAndCountPriceSetMoneyAmounts(
|
||||||
|
filters: PricingTypes.FilterablePriceSetMoneyAmountProps = {},
|
||||||
|
config: FindConfig<PricingTypes.PriceSetMoneyAmountDTO> = {},
|
||||||
|
@MedusaContext() sharedContext: Context = {}
|
||||||
|
): Promise<[PricingTypes.PriceSetMoneyAmountDTO[], number]> {
|
||||||
|
const [records, count] =
|
||||||
|
await this.priceSetMoneyAmountService_.listAndCount(
|
||||||
|
filters,
|
||||||
|
config,
|
||||||
|
sharedContext
|
||||||
|
)
|
||||||
|
|
||||||
|
return [
|
||||||
|
await this.baseRepository_.serialize<
|
||||||
|
PricingTypes.PriceSetMoneyAmountRulesDTO[]
|
||||||
|
>(records, {
|
||||||
|
populate: true,
|
||||||
|
}),
|
||||||
|
count,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
@InjectTransactionManager(shouldForceTransaction, "baseRepository_")
|
@InjectTransactionManager(shouldForceTransaction, "baseRepository_")
|
||||||
async createPriceSetMoneyAmountRules(
|
async createPriceSetMoneyAmountRules(
|
||||||
data: PricingTypes.CreatePriceSetMoneyAmountRulesDTO[],
|
data: PricingTypes.CreatePriceSetMoneyAmountRulesDTO[],
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { JoinerRelationship, JoinerServiceConfig } from "../joiner"
|
import { JoinerRelationship, JoinerServiceConfig, RemoteJoinerQuery } from "../joiner"
|
||||||
|
|
||||||
|
import { Logger } from "../logger"
|
||||||
import { MedusaContainer } from "../common"
|
import { MedusaContainer } from "../common"
|
||||||
import { RepositoryService } from "../dal"
|
import { RepositoryService } from "../dal"
|
||||||
import { Logger } from "../logger"
|
|
||||||
|
|
||||||
export type Constructor<T> = new (...args: any[]) => T
|
export type Constructor<T> = new (...args: any[]) => T
|
||||||
export * from "../common/medusa-container"
|
export * from "../common/medusa-container"
|
||||||
@@ -260,3 +260,8 @@ export type ModuleServiceInitializeCustomDataLayerOptions = {
|
|||||||
[key: string]: Constructor<RepositoryService>
|
[key: string]: Constructor<RepositoryService>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type RemoteQueryFunction = (
|
||||||
|
query: string | RemoteJoinerQuery | object,
|
||||||
|
variables?: Record<string, unknown>
|
||||||
|
) => Promise<any> | null
|
||||||
@@ -39,8 +39,8 @@ export interface CreateMoneyAmountDTO {
|
|||||||
currency_code: string
|
currency_code: string
|
||||||
currency?: CreateCurrencyDTO
|
currency?: CreateCurrencyDTO
|
||||||
amount?: number
|
amount?: number
|
||||||
min_quantity?: number
|
min_quantity?: number | null
|
||||||
max_quantity?: number
|
max_quantity?: number | null
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
|
import { BaseFilterable } from "../../dal"
|
||||||
import { MoneyAmountDTO } from "./money-amount"
|
import { MoneyAmountDTO } from "./money-amount"
|
||||||
|
import { PriceRuleDTO } from "./price-rule"
|
||||||
import { PriceSetDTO } from "./price-set"
|
import { PriceSetDTO } from "./price-set"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -15,6 +17,8 @@ export interface PriceSetMoneyAmountDTO {
|
|||||||
id: string
|
id: string
|
||||||
title?: string
|
title?: string
|
||||||
price_set?: PriceSetDTO
|
price_set?: PriceSetDTO
|
||||||
|
price_set_id?: string
|
||||||
|
price_rules?: PriceRuleDTO[]
|
||||||
money_amount?: MoneyAmountDTO
|
money_amount?: MoneyAmountDTO
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,3 +34,9 @@ export interface CreatePriceSetMoneyAmountDTO {
|
|||||||
price_set?: PriceSetDTO | string
|
price_set?: PriceSetDTO | string
|
||||||
money_amount?: MoneyAmountDTO | string
|
money_amount?: MoneyAmountDTO | string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface FilterablePriceSetMoneyAmountProps
|
||||||
|
extends BaseFilterable<FilterablePriceSetMoneyAmountProps> {
|
||||||
|
id?: string[]
|
||||||
|
price_set_id?: string[]
|
||||||
|
}
|
||||||
@@ -12,12 +12,14 @@ import {
|
|||||||
FilterableCurrencyProps,
|
FilterableCurrencyProps,
|
||||||
FilterableMoneyAmountProps,
|
FilterableMoneyAmountProps,
|
||||||
FilterablePriceRuleProps,
|
FilterablePriceRuleProps,
|
||||||
|
FilterablePriceSetMoneyAmountProps,
|
||||||
FilterablePriceSetMoneyAmountRulesProps,
|
FilterablePriceSetMoneyAmountRulesProps,
|
||||||
FilterablePriceSetProps,
|
FilterablePriceSetProps,
|
||||||
FilterableRuleTypeProps,
|
FilterableRuleTypeProps,
|
||||||
MoneyAmountDTO,
|
MoneyAmountDTO,
|
||||||
PriceRuleDTO,
|
PriceRuleDTO,
|
||||||
PriceSetDTO,
|
PriceSetDTO,
|
||||||
|
PriceSetMoneyAmountDTO,
|
||||||
PriceSetMoneyAmountRulesDTO,
|
PriceSetMoneyAmountRulesDTO,
|
||||||
PricingContext,
|
PricingContext,
|
||||||
PricingFilters,
|
PricingFilters,
|
||||||
@@ -31,9 +33,9 @@ import {
|
|||||||
UpdateRuleTypeDTO,
|
UpdateRuleTypeDTO,
|
||||||
} from "./common"
|
} from "./common"
|
||||||
|
|
||||||
|
import { Context } from "../shared-context"
|
||||||
import { FindConfig } from "../common"
|
import { FindConfig } from "../common"
|
||||||
import { ModuleJoinerConfig } from "../modules-sdk"
|
import { ModuleJoinerConfig } from "../modules-sdk"
|
||||||
import { Context } from "../shared-context"
|
|
||||||
|
|
||||||
export interface IPricingModuleService {
|
export interface IPricingModuleService {
|
||||||
/**
|
/**
|
||||||
@@ -2198,6 +2200,20 @@ export interface IPricingModuleService {
|
|||||||
sharedContext?: Context
|
sharedContext?: Context
|
||||||
): Promise<[PriceSetMoneyAmountRulesDTO[], number]>
|
): Promise<[PriceSetMoneyAmountRulesDTO[], number]>
|
||||||
|
|
||||||
|
|
||||||
|
listPriceSetMoneyAmounts(
|
||||||
|
filters?: FilterablePriceSetMoneyAmountProps,
|
||||||
|
config?: FindConfig<PriceSetMoneyAmountDTO>,
|
||||||
|
sharedContext?: Context
|
||||||
|
): Promise<PriceSetMoneyAmountDTO[]>
|
||||||
|
|
||||||
|
listAndCountPriceSetMoneyAmounts(
|
||||||
|
filters?: FilterablePriceSetMoneyAmountProps,
|
||||||
|
config?: FindConfig<PriceSetMoneyAmountDTO>,
|
||||||
|
sharedContext?: Context
|
||||||
|
): Promise<[PriceSetMoneyAmountDTO[], number]>
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method is used to create new price set money amount rules. A price set money amount rule creates an association between a price set money amount and
|
* This method is used to create new price set money amount rules. A price set money amount rule creates an association between a price set money amount and
|
||||||
* a rule type.
|
* a rule type.
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
export * from "./create-products"
|
export * from "./create-products"
|
||||||
|
export * from "./update-product-variants"
|
||||||
export * from "./update-products"
|
export * from "./update-products"
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
export interface UpsertProductVariantPricesInputDTO {
|
||||||
|
id?: string
|
||||||
|
region_id?: string
|
||||||
|
currency_code?: string
|
||||||
|
amount: number
|
||||||
|
min_quantity?: number
|
||||||
|
max_quantity?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpsertProductVariantOptionInputDTO {
|
||||||
|
value: string
|
||||||
|
option_id: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpdateProductVariantsInputDTO {
|
||||||
|
id: string
|
||||||
|
title?: string
|
||||||
|
sku?: string
|
||||||
|
ean?: string
|
||||||
|
upc?: string
|
||||||
|
barcode?: string
|
||||||
|
hs_code?: string
|
||||||
|
inventory_quantity?: number
|
||||||
|
allow_backorder?: boolean
|
||||||
|
manage_inventory?: boolean
|
||||||
|
weight?: number
|
||||||
|
length?: number
|
||||||
|
height?: number
|
||||||
|
width?: number
|
||||||
|
origin_country?: string
|
||||||
|
mid_code?: string
|
||||||
|
material?: string
|
||||||
|
metadata?: Record<string, unknown>
|
||||||
|
|
||||||
|
prices?: UpsertProductVariantPricesInputDTO[]
|
||||||
|
options?: UpsertProductVariantOptionInputDTO[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpdateProductVariantsWorkflowInputDTO {
|
||||||
|
productVariants: UpdateProductVariantsInputDTO[]
|
||||||
|
}
|
||||||
@@ -18,6 +18,7 @@ export * from "./object-from-string-path"
|
|||||||
export * from "./object-to-string-path"
|
export * from "./object-to-string-path"
|
||||||
export * from "./remote-query-object-from-string"
|
export * from "./remote-query-object-from-string"
|
||||||
export * from "./remote-query-object-to-string"
|
export * from "./remote-query-object-to-string"
|
||||||
|
export * from './remove-nullisih'
|
||||||
export * from "./set-metadata"
|
export * from "./set-metadata"
|
||||||
export * from "./simple-hash"
|
export * from "./simple-hash"
|
||||||
export * from "./string-to-select-relation-object"
|
export * from "./string-to-select-relation-object"
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import { isDefined } from "./is-defined"
|
||||||
|
|
||||||
|
export const removeNullish = (
|
||||||
|
obj: Record<string, unknown>
|
||||||
|
): Record<string, unknown> =>
|
||||||
|
Object.entries(obj).reduce((resultObject, [currentKey, currentValue]) => {
|
||||||
|
if (!isDefined(currentValue) || currentValue === null) {
|
||||||
|
return resultObject
|
||||||
|
}
|
||||||
|
|
||||||
|
resultObject[currentKey] = currentValue
|
||||||
|
|
||||||
|
return resultObject
|
||||||
|
}, {})
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { ModulesSdkTypes } from "@medusajs/types"
|
|
||||||
import { MedusaError } from "../common"
|
import { MedusaError } from "../common"
|
||||||
|
import { ModulesSdkTypes } from "@medusajs/types"
|
||||||
|
|
||||||
function getEnv(key: string, moduleName: string): string {
|
function getEnv(key: string, moduleName: string): string {
|
||||||
const value =
|
const value =
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
export * from "./create-products"
|
export * from "./create-products"
|
||||||
|
export * as UpdateProductVariants from "./update-product-variants"
|
||||||
export * from "./update-products"
|
export * from "./update-products"
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
import {
|
||||||
|
TransactionStepsDefinition,
|
||||||
|
WorkflowManager,
|
||||||
|
} from "@medusajs/orchestration"
|
||||||
|
import { InputAlias, Workflows } from "../../definitions"
|
||||||
|
import { exportWorkflow, pipe } from "../../helper"
|
||||||
|
|
||||||
|
import { ProductTypes, WorkflowTypes } from "@medusajs/types"
|
||||||
|
import { ProductHandlers } from "../../handlers"
|
||||||
|
|
||||||
|
export enum UpdateProductVariantsActions {
|
||||||
|
prepare = "prepare",
|
||||||
|
updateProductVariants = "updateProductVariants",
|
||||||
|
revertProductVariantsUpdate = "revertProductVariantsUpdate",
|
||||||
|
upsertPrices = "upsertPrices",
|
||||||
|
}
|
||||||
|
|
||||||
|
export const workflowSteps: TransactionStepsDefinition = {
|
||||||
|
next: {
|
||||||
|
action: UpdateProductVariantsActions.prepare,
|
||||||
|
noCompensation: true,
|
||||||
|
next: {
|
||||||
|
action: UpdateProductVariantsActions.updateProductVariants,
|
||||||
|
noCompensation: true,
|
||||||
|
next: [
|
||||||
|
{
|
||||||
|
action: UpdateProductVariantsActions.upsertPrices,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const handlers = new Map([
|
||||||
|
[
|
||||||
|
UpdateProductVariantsActions.prepare,
|
||||||
|
{
|
||||||
|
invoke: pipe(
|
||||||
|
{
|
||||||
|
merge: true,
|
||||||
|
inputAlias: InputAlias.ProductVariantsUpdateInputData,
|
||||||
|
invoke: {
|
||||||
|
from: InputAlias.ProductVariantsUpdateInputData,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ProductHandlers.updateProductVariantsPrepareData
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
UpdateProductVariantsActions.updateProductVariants,
|
||||||
|
{
|
||||||
|
invoke: pipe(
|
||||||
|
{
|
||||||
|
merge: true,
|
||||||
|
invoke: {
|
||||||
|
from: UpdateProductVariantsActions.prepare,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ProductHandlers.updateProductVariants
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
UpdateProductVariantsActions.upsertPrices,
|
||||||
|
{
|
||||||
|
invoke: pipe(
|
||||||
|
{
|
||||||
|
merge: true,
|
||||||
|
invoke: [
|
||||||
|
{
|
||||||
|
from: UpdateProductVariantsActions.prepare,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
ProductHandlers.upsertVariantPrices
|
||||||
|
),
|
||||||
|
compensate: pipe(
|
||||||
|
{
|
||||||
|
merge: true,
|
||||||
|
invoke: [
|
||||||
|
{
|
||||||
|
from: UpdateProductVariantsActions.prepare,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
from: UpdateProductVariantsActions.upsertPrices,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
ProductHandlers.revertVariantPrices
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
])
|
||||||
|
|
||||||
|
WorkflowManager.register(
|
||||||
|
Workflows.UpdateProductVariants,
|
||||||
|
workflowSteps,
|
||||||
|
handlers
|
||||||
|
)
|
||||||
|
|
||||||
|
export const updateProductVariants = exportWorkflow<
|
||||||
|
WorkflowTypes.ProductWorkflow.UpdateProductVariantsWorkflowInputDTO,
|
||||||
|
ProductTypes.ProductVariantDTO[]
|
||||||
|
>(
|
||||||
|
Workflows.UpdateProductVariants,
|
||||||
|
UpdateProductVariantsActions.updateProductVariants
|
||||||
|
)
|
||||||
@@ -1,16 +1,17 @@
|
|||||||
import { ProductTypes, WorkflowTypes } from "@medusajs/types"
|
import { ProductTypes, WorkflowTypes } from "@medusajs/types"
|
||||||
|
|
||||||
import { InputAlias, Workflows } from "../../definitions"
|
|
||||||
import {
|
import {
|
||||||
TransactionStepsDefinition,
|
TransactionStepsDefinition,
|
||||||
WorkflowManager,
|
WorkflowManager,
|
||||||
} from "@medusajs/orchestration"
|
} from "@medusajs/orchestration"
|
||||||
import { exportWorkflow, pipe } from "../../helper"
|
import { InputAlias, Workflows } from "../../definitions"
|
||||||
import { CreateProductsActions } from "./create-products"
|
|
||||||
import { InventoryHandlers, ProductHandlers } from "../../handlers"
|
import { InventoryHandlers, ProductHandlers } from "../../handlers"
|
||||||
import * as MiddlewareHandlers from "../../handlers/middlewares"
|
import * as MiddlewareHandlers from "../../handlers/middlewares"
|
||||||
import { detachSalesChannelFromProducts } from "../../handlers/product"
|
import { detachSalesChannelFromProducts } from "../../handlers/product"
|
||||||
|
import { exportWorkflow, pipe } from "../../helper"
|
||||||
|
import { CreateProductsActions } from "./create-products"
|
||||||
import { prepareCreateInventoryItems } from "./prepare-create-inventory-items"
|
import { prepareCreateInventoryItems } from "./prepare-create-inventory-items"
|
||||||
|
import { UpdateProductVariantsActions } from "./update-product-variants"
|
||||||
|
|
||||||
export enum UpdateProductsActions {
|
export enum UpdateProductsActions {
|
||||||
prepare = "prepare",
|
prepare = "prepare",
|
||||||
@@ -32,6 +33,10 @@ export const updateProductsWorkflowSteps: TransactionStepsDefinition = {
|
|||||||
next: {
|
next: {
|
||||||
action: UpdateProductsActions.updateProducts,
|
action: UpdateProductsActions.updateProducts,
|
||||||
next: [
|
next: [
|
||||||
|
{
|
||||||
|
action: UpdateProductVariantsActions.upsertPrices,
|
||||||
|
saveResponse: false,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
action: UpdateProductsActions.attachSalesChannels,
|
action: UpdateProductsActions.attachSalesChannels,
|
||||||
saveResponse: false,
|
saveResponse: false,
|
||||||
@@ -59,7 +64,7 @@ export const updateProductsWorkflowSteps: TransactionStepsDefinition = {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
const handlers = new Map([
|
const handlers = new Map<string, any>([
|
||||||
[
|
[
|
||||||
UpdateProductsActions.prepare,
|
UpdateProductsActions.prepare,
|
||||||
{
|
{
|
||||||
@@ -350,6 +355,37 @@ const handlers = new Map([
|
|||||||
),
|
),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
UpdateProductVariantsActions.upsertPrices,
|
||||||
|
{
|
||||||
|
invoke: pipe(
|
||||||
|
{
|
||||||
|
merge: true,
|
||||||
|
invoke: [
|
||||||
|
{
|
||||||
|
from: InputAlias.ProductsInputData,
|
||||||
|
alias: ProductHandlers.updateProducts.aliases.products,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
from: UpdateProductsActions.prepare,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
ProductHandlers.upsertVariantPrices
|
||||||
|
),
|
||||||
|
compensate: pipe(
|
||||||
|
{
|
||||||
|
merge: true,
|
||||||
|
invoke: [
|
||||||
|
{
|
||||||
|
from: UpdateProductVariantsActions.upsertPrices,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
ProductHandlers.revertVariantPrices
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
])
|
])
|
||||||
|
|
||||||
WorkflowManager.register(
|
WorkflowManager.register(
|
||||||
|
|||||||
@@ -3,6 +3,9 @@ export enum Workflows {
|
|||||||
CreateProducts = "create-products",
|
CreateProducts = "create-products",
|
||||||
UpdateProducts = "update-products",
|
UpdateProducts = "update-products",
|
||||||
|
|
||||||
|
// Product Variant workflows
|
||||||
|
UpdateProductVariants = "update-product-variants",
|
||||||
|
|
||||||
// Cart workflows
|
// Cart workflows
|
||||||
CreateCart = "create-cart",
|
CreateCart = "create-cart",
|
||||||
|
|
||||||
@@ -14,6 +17,9 @@ export enum InputAlias {
|
|||||||
ProductsInputData = "productsInputData",
|
ProductsInputData = "productsInputData",
|
||||||
RemovedProducts = "removedProducts",
|
RemovedProducts = "removedProducts",
|
||||||
|
|
||||||
|
ProductVariants = "productVariants",
|
||||||
|
ProductVariantsUpdateInputData = "productVariantsUpdateInputData",
|
||||||
|
|
||||||
InventoryItems = "inventoryItems",
|
InventoryItems = "inventoryItems",
|
||||||
RemovedInventoryItems = "removedInventoryItems",
|
RemovedInventoryItems = "removedInventoryItems",
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { ProductTypes, SalesChannelTypes, WorkflowTypes } from "@medusajs/types"
|
import { ProductTypes, SalesChannelTypes, WorkflowTypes } from "@medusajs/types"
|
||||||
import {
|
import {
|
||||||
FeatureFlagUtils,
|
FeatureFlagUtils,
|
||||||
kebabCase,
|
|
||||||
ShippingProfileUtils,
|
ShippingProfileUtils,
|
||||||
|
kebabCase,
|
||||||
} from "@medusajs/utils"
|
} from "@medusajs/utils"
|
||||||
import { WorkflowArguments } from "../../helper"
|
import { WorkflowArguments } from "../../helper"
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,16 @@
|
|||||||
export * from "./create-products-prepare-data"
|
|
||||||
export * from "./create-products"
|
|
||||||
export * from "./detach-sales-channel-from-products"
|
|
||||||
export * from "./attach-sales-channel-to-products"
|
export * from "./attach-sales-channel-to-products"
|
||||||
export * from "./detach-shipping-profile-from-products"
|
|
||||||
export * from "./remove-products"
|
|
||||||
export * from "./attach-shipping-profile-to-products"
|
export * from "./attach-shipping-profile-to-products"
|
||||||
|
export * from "./create-products"
|
||||||
|
export * from "./create-products-prepare-data"
|
||||||
|
export * from "./detach-sales-channel-from-products"
|
||||||
|
export * from "./detach-shipping-profile-from-products"
|
||||||
export * from "./list-products"
|
export * from "./list-products"
|
||||||
|
export * from "./remove-products"
|
||||||
|
export * from "./revert-update-products"
|
||||||
|
export * from "./revert-variant-prices"
|
||||||
|
export * from "./update-product-variants"
|
||||||
|
export * from "./update-product-variants-prepare-data"
|
||||||
export * from "./update-products"
|
export * from "./update-products"
|
||||||
export * from "./update-products-prepare-data"
|
export * from "./update-products-prepare-data"
|
||||||
export * from "./revert-update-products"
|
|
||||||
export * from "./update-products-variants-prices"
|
export * from "./update-products-variants-prices"
|
||||||
|
export * from "./upsert-variant-prices"
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
import { PricingTypes } from "@medusajs/types"
|
||||||
|
import { WorkflowArguments } from "../../helper"
|
||||||
|
|
||||||
|
type HandlerInput = {
|
||||||
|
createdLinks: Record<any, any>[]
|
||||||
|
originalMoneyAmounts: PricingTypes.MoneyAmountDTO[]
|
||||||
|
createdPriceSets: PricingTypes.PriceSetDTO[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function revertVariantPrices({
|
||||||
|
container,
|
||||||
|
context,
|
||||||
|
data,
|
||||||
|
}: WorkflowArguments<HandlerInput>): Promise<void> {
|
||||||
|
const {
|
||||||
|
createdLinks = [],
|
||||||
|
originalMoneyAmounts = [],
|
||||||
|
createdPriceSets = [],
|
||||||
|
} = data
|
||||||
|
|
||||||
|
const featureFlagRouter = container.resolve("featureFlagRouter")
|
||||||
|
const isPricingDomainEnabled = featureFlagRouter.isFeatureEnabled(
|
||||||
|
"isolate_pricing_domain"
|
||||||
|
)
|
||||||
|
|
||||||
|
if (!isPricingDomainEnabled) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const pricingModuleService = container.resolve("pricingModuleService")
|
||||||
|
const remoteLink = container.resolve("remoteLink")
|
||||||
|
|
||||||
|
await remoteLink.remove(createdLinks)
|
||||||
|
|
||||||
|
if (originalMoneyAmounts.length) {
|
||||||
|
await pricingModuleService.updateMoneyAmounts(originalMoneyAmounts)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (createdPriceSets.length) {
|
||||||
|
await pricingModuleService.delete({
|
||||||
|
id: createdPriceSets.map((cps) => cps.id),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
revertVariantPrices.aliases = {
|
||||||
|
productVariantsPrices: "productVariantsPrices",
|
||||||
|
}
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
import { Modules, ModulesDefinition } from "@medusajs/modules-sdk"
|
||||||
|
import { ProductTypes, ProductWorkflow, WorkflowTypes } from "@medusajs/types"
|
||||||
|
|
||||||
|
import { WorkflowArguments } from "../../helper"
|
||||||
|
|
||||||
|
type VariantPrice = {
|
||||||
|
region_id?: string
|
||||||
|
currency_code?: string
|
||||||
|
amount: number
|
||||||
|
min_quantity?: number
|
||||||
|
max_quantity?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UpdateProductVariantsPreparedData = {
|
||||||
|
productVariants: ProductWorkflow.UpdateProductVariantsInputDTO[]
|
||||||
|
variantPricesMap: Map<string, VariantPrice[]>
|
||||||
|
productVariantsMap: Map<
|
||||||
|
string,
|
||||||
|
ProductWorkflow.UpdateProductVariantsInputDTO[]
|
||||||
|
>
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updateProductVariantsPrepareData({
|
||||||
|
container,
|
||||||
|
context,
|
||||||
|
data,
|
||||||
|
}: WorkflowArguments<WorkflowTypes.ProductWorkflow.UpdateProductVariantsWorkflowInputDTO>): Promise<UpdateProductVariantsPreparedData> {
|
||||||
|
const featureFlagRouter = container.resolve("featureFlagRouter")
|
||||||
|
const isPricingDomainEnabled = featureFlagRouter.isFeatureEnabled(
|
||||||
|
"isolate_pricing_domain"
|
||||||
|
)
|
||||||
|
let productVariants: ProductWorkflow.UpdateProductVariantsInputDTO[] =
|
||||||
|
data.productVariants || []
|
||||||
|
|
||||||
|
const variantsDataMap = new Map<
|
||||||
|
string,
|
||||||
|
ProductWorkflow.UpdateProductVariantsInputDTO
|
||||||
|
>(
|
||||||
|
productVariants.map((productVariantData) => [
|
||||||
|
productVariantData.id,
|
||||||
|
productVariantData,
|
||||||
|
])
|
||||||
|
)
|
||||||
|
|
||||||
|
const variantIds = productVariants.map((pv) => pv.id) as string[]
|
||||||
|
const productVariantsMap = new Map<
|
||||||
|
string,
|
||||||
|
ProductWorkflow.UpdateProductVariantsInputDTO[]
|
||||||
|
>()
|
||||||
|
const variantPricesMap = new Map<string, VariantPrice[]>()
|
||||||
|
|
||||||
|
const productModuleService: ProductTypes.IProductModuleService =
|
||||||
|
container.resolve(ModulesDefinition[Modules.PRODUCT].registrationName)
|
||||||
|
|
||||||
|
const variantsWithProductIds = await productModuleService.listVariants(
|
||||||
|
{
|
||||||
|
id: variantIds,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
select: ["id", "product_id"],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
for (const variantWithProductID of variantsWithProductIds) {
|
||||||
|
const variantData = variantsDataMap.get(variantWithProductID.id)
|
||||||
|
|
||||||
|
if (!variantData) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
variantPricesMap.set(variantWithProductID.id, variantData.prices || [])
|
||||||
|
if (isPricingDomainEnabled) {
|
||||||
|
delete variantData.prices
|
||||||
|
}
|
||||||
|
|
||||||
|
const variantsData: ProductWorkflow.UpdateProductVariantsInputDTO[] =
|
||||||
|
productVariantsMap.get(variantWithProductID.product_id) || []
|
||||||
|
|
||||||
|
if (variantData) {
|
||||||
|
variantsData.push(variantData)
|
||||||
|
}
|
||||||
|
|
||||||
|
productVariantsMap.set(variantWithProductID.product_id, variantsData)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
productVariants,
|
||||||
|
variantPricesMap,
|
||||||
|
productVariantsMap,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateProductVariantsPrepareData.aliases = {
|
||||||
|
payload: "payload",
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import { Modules, ModulesDefinition } from "@medusajs/modules-sdk"
|
||||||
|
import { ProductTypes } from "@medusajs/types"
|
||||||
|
import { WorkflowArguments } from "../../helper"
|
||||||
|
|
||||||
|
type HandlerInput = {
|
||||||
|
productVariantsMap: Map<string, ProductTypes.UpdateProductVariantDTO[]>
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updateProductVariants({
|
||||||
|
container,
|
||||||
|
data,
|
||||||
|
}: WorkflowArguments<HandlerInput>): Promise<
|
||||||
|
ProductTypes.UpdateProductVariantDTO[]
|
||||||
|
> {
|
||||||
|
const { productVariantsMap } = data
|
||||||
|
const productsVariants: ProductTypes.UpdateProductVariantDTO[] = []
|
||||||
|
const updateProductsData: ProductTypes.UpdateProductDTO[] = []
|
||||||
|
const productModuleService: ProductTypes.IProductModuleService =
|
||||||
|
container.resolve(ModulesDefinition[Modules.PRODUCT].registrationName)
|
||||||
|
|
||||||
|
for (const [productId, variantsData = []] of productVariantsMap) {
|
||||||
|
updateProductsData.push({
|
||||||
|
id: productId,
|
||||||
|
variants: variantsData,
|
||||||
|
})
|
||||||
|
|
||||||
|
productsVariants.push(...variantsData)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (updateProductsData.length) {
|
||||||
|
await productModuleService.update(updateProductsData)
|
||||||
|
}
|
||||||
|
|
||||||
|
return productsVariants
|
||||||
|
}
|
||||||
|
|
||||||
|
updateProductVariants.aliases = {
|
||||||
|
payload: "payload",
|
||||||
|
}
|
||||||
@@ -6,10 +6,19 @@ type ProductWithSalesChannelsDTO = ProductDTO & {
|
|||||||
sales_channels?: SalesChannelDTO[]
|
sales_channels?: SalesChannelDTO[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type VariantPrice = {
|
||||||
|
region_id?: string
|
||||||
|
currency_code?: string
|
||||||
|
amount: number
|
||||||
|
min_quantity?: number
|
||||||
|
max_quantity?: number
|
||||||
|
}
|
||||||
|
|
||||||
export type UpdateProductsPreparedData = {
|
export type UpdateProductsPreparedData = {
|
||||||
originalProducts: ProductWithSalesChannelsDTO[]
|
originalProducts: ProductWithSalesChannelsDTO[]
|
||||||
productHandleAddedChannelsMap: Map<string, string[]>
|
productHandleAddedChannelsMap: Map<string, string[]>
|
||||||
productHandleRemovedChannelsMap: Map<string, string[]>
|
productHandleRemovedChannelsMap: Map<string, string[]>
|
||||||
|
variantPricesMap: Map<string, VariantPrice[]>
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function updateProductsPrepareData({
|
export async function updateProductsPrepareData({
|
||||||
@@ -17,6 +26,12 @@ export async function updateProductsPrepareData({
|
|||||||
context,
|
context,
|
||||||
data,
|
data,
|
||||||
}: WorkflowArguments<WorkflowTypes.ProductWorkflow.UpdateProductsWorkflowInputDTO>): Promise<UpdateProductsPreparedData> {
|
}: WorkflowArguments<WorkflowTypes.ProductWorkflow.UpdateProductsWorkflowInputDTO>): Promise<UpdateProductsPreparedData> {
|
||||||
|
const featureFlagRouter = container.resolve("featureFlagRouter")
|
||||||
|
const isPricingDomainEnabled = featureFlagRouter.isFeatureEnabled(
|
||||||
|
"isolate_pricing_domain"
|
||||||
|
)
|
||||||
|
|
||||||
|
const variantPricesMap = new Map<string, VariantPrice[]>()
|
||||||
const ids = data.products.map((product) => product.id)
|
const ids = data.products.map((product) => product.id)
|
||||||
|
|
||||||
const productHandleAddedChannelsMap = new Map<string, string[]>()
|
const productHandleAddedChannelsMap = new Map<string, string[]>()
|
||||||
@@ -65,6 +80,16 @@ export async function updateProductsPrepareData({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const variantInput of productInput.variants || []) {
|
||||||
|
if (variantInput.id) {
|
||||||
|
variantPricesMap.set(variantInput.id, variantInput.prices || [])
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isPricingDomainEnabled) {
|
||||||
|
delete variantInput.prices
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
productHandleAddedChannelsMap.set(currentProduct.handle!, addedChannels)
|
productHandleAddedChannelsMap.set(currentProduct.handle!, addedChannels)
|
||||||
productHandleRemovedChannelsMap.set(currentProduct.handle!, removedChannels)
|
productHandleRemovedChannelsMap.set(currentProduct.handle!, removedChannels)
|
||||||
})
|
})
|
||||||
@@ -73,6 +98,7 @@ export async function updateProductsPrepareData({
|
|||||||
originalProducts: products,
|
originalProducts: products,
|
||||||
productHandleAddedChannelsMap,
|
productHandleAddedChannelsMap,
|
||||||
productHandleRemovedChannelsMap,
|
productHandleRemovedChannelsMap,
|
||||||
|
variantPricesMap,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { ProductTypes, WorkflowTypes } from "@medusajs/types"
|
import { ProductTypes, WorkflowTypes } from "@medusajs/types"
|
||||||
|
|
||||||
import { MedusaError } from "@medusajs/utils"
|
import { MedusaError } from "@medusajs/utils"
|
||||||
import { WorkflowArguments } from "../../helper"
|
import { WorkflowArguments } from "../../helper"
|
||||||
|
|
||||||
@@ -26,9 +27,12 @@ export async function updateProductsVariantsPrices({
|
|||||||
data.productsHandleVariantsIndexPricesMap
|
data.productsHandleVariantsIndexPricesMap
|
||||||
|
|
||||||
const productVariantService = container.resolve("productVariantService")
|
const productVariantService = container.resolve("productVariantService")
|
||||||
|
const regionService = container.resolve("regionService")
|
||||||
|
const featureFlagRouter = container.resolve("featureFlagRouter")
|
||||||
const productVariantServiceTx = productVariantService.withTransaction(manager)
|
const productVariantServiceTx = productVariantService.withTransaction(manager)
|
||||||
|
|
||||||
const variantIdsPricesData: any[] = []
|
const variantIdsPricesData: any[] = []
|
||||||
|
const variantPricesMap = new Map<string, any[]>()
|
||||||
|
|
||||||
const productsMap = new Map<string, ProductTypes.ProductDTO>(
|
const productsMap = new Map<string, ProductTypes.ProductDTO>(
|
||||||
products.map((p) => [p.handle!, p])
|
products.map((p) => [p.handle!, p])
|
||||||
)
|
)
|
||||||
@@ -50,10 +54,52 @@ export async function updateProductsVariantsPrices({
|
|||||||
variantId: variant.id,
|
variantId: variant.id,
|
||||||
prices: item.prices,
|
prices: item.prices,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
variantPricesMap.set(variant.id, [])
|
||||||
|
|
||||||
|
item.prices.forEach(async (price) => {
|
||||||
|
const obj = {
|
||||||
|
amount: price.amount,
|
||||||
|
currency_code: price.currency_code,
|
||||||
|
rules: {},
|
||||||
|
}
|
||||||
|
|
||||||
|
if (price.region_id) {
|
||||||
|
const region = await regionService.retrieve(price.region_id)
|
||||||
|
obj.currency_code = region.currency_code
|
||||||
|
obj.rules = {
|
||||||
|
region_id: price.region_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const variantPrices = variantPricesMap.get(variant.id)
|
||||||
|
variantPrices?.push(obj)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (featureFlagRouter.isFeatureEnabled("isolate_pricing_domain")) {
|
||||||
|
const remoteLink = container.resolve("remoteLink")
|
||||||
|
const pricingModuleService = container.resolve("pricingModuleService")
|
||||||
|
|
||||||
|
for (let { variantId } of variantIdsPricesData) {
|
||||||
|
const priceSet = await pricingModuleService.create({
|
||||||
|
rules: [{ rule_attribute: "region_id" }],
|
||||||
|
prices: variantPricesMap.get(variantId),
|
||||||
|
})
|
||||||
|
|
||||||
|
await remoteLink.create({
|
||||||
|
productService: {
|
||||||
|
variant_id: variantId,
|
||||||
|
},
|
||||||
|
pricingService: {
|
||||||
|
price_set_id: priceSet.id,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
await productVariantServiceTx.updateVariantPrices(variantIdsPricesData)
|
await productVariantServiceTx.updateVariantPrices(variantIdsPricesData)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateProductsVariantsPrices.aliases = {
|
updateProductsVariantsPrices.aliases = {
|
||||||
|
|||||||
@@ -0,0 +1,168 @@
|
|||||||
|
import { PricingTypes } from "@medusajs/types"
|
||||||
|
|
||||||
|
import { WorkflowArguments } from "../../helper"
|
||||||
|
|
||||||
|
type VariantPrice = {
|
||||||
|
id?: string
|
||||||
|
region_id?: string
|
||||||
|
currency_code: string
|
||||||
|
amount: number
|
||||||
|
min_quantity?: number
|
||||||
|
max_quantity?: number
|
||||||
|
rules: Record<string, string>
|
||||||
|
}
|
||||||
|
|
||||||
|
type RegionDTO = {
|
||||||
|
id: string
|
||||||
|
currency_code: string
|
||||||
|
}
|
||||||
|
|
||||||
|
type HandlerInput = {
|
||||||
|
variantPricesMap: Map<string, VariantPrice[]>
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function upsertVariantPrices({
|
||||||
|
container,
|
||||||
|
context,
|
||||||
|
data,
|
||||||
|
}: WorkflowArguments<HandlerInput>) {
|
||||||
|
const { variantPricesMap } = data
|
||||||
|
|
||||||
|
const featureFlagRouter = container.resolve("featureFlagRouter")
|
||||||
|
|
||||||
|
if (!featureFlagRouter.isFeatureEnabled("isolate_pricing_domain")) {
|
||||||
|
return {
|
||||||
|
createdLinks: [],
|
||||||
|
originalMoneyAmounts: [],
|
||||||
|
createdPriceSets: [],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const pricingModuleService = container.resolve("pricingModuleService")
|
||||||
|
const regionService = container.resolve("regionService")
|
||||||
|
const remoteLink = container.resolve("remoteLink")
|
||||||
|
const remoteQuery = container.resolve("remoteQuery")
|
||||||
|
|
||||||
|
const variables = {
|
||||||
|
variant_id: [...variantPricesMap.keys()],
|
||||||
|
}
|
||||||
|
|
||||||
|
const query = {
|
||||||
|
product_variant_price_set: {
|
||||||
|
__args: variables,
|
||||||
|
fields: ["variant_id", "price_set_id"],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const variantPriceSets = await remoteQuery(query)
|
||||||
|
|
||||||
|
const variantIdToPriceSetIdMap: Map<string, string> = new Map(
|
||||||
|
variantPriceSets.map((variantPriceSet) => [
|
||||||
|
variantPriceSet.variant_id,
|
||||||
|
variantPriceSet.price_set_id,
|
||||||
|
])
|
||||||
|
)
|
||||||
|
|
||||||
|
const moneyAmountsToUpdate: PricingTypes.UpdateMoneyAmountDTO[] = []
|
||||||
|
const createdPriceSets: PricingTypes.PriceSetDTO[] = []
|
||||||
|
const ruleSetPricesToAdd: PricingTypes.CreatePricesDTO[] = []
|
||||||
|
const linksToCreate: any[] = []
|
||||||
|
|
||||||
|
for (const [variantId, prices = []] of variantPricesMap) {
|
||||||
|
const priceSetToCreate: PricingTypes.CreatePriceSetDTO = {
|
||||||
|
rules: [{ rule_attribute: "region_id" }],
|
||||||
|
prices: [],
|
||||||
|
}
|
||||||
|
const regionIds = prices.map((price) => price.region_id)
|
||||||
|
const regions = await regionService.list({ id: regionIds })
|
||||||
|
const regionsMap: Map<string, RegionDTO> = new Map(
|
||||||
|
regions.map((region: RegionDTO) => [region.id, region])
|
||||||
|
)
|
||||||
|
|
||||||
|
for (const price of prices) {
|
||||||
|
if (price.id) {
|
||||||
|
moneyAmountsToUpdate.push({
|
||||||
|
id: price.id,
|
||||||
|
min_quantity: price.min_quantity,
|
||||||
|
max_quantity: price.max_quantity,
|
||||||
|
amount: price.amount,
|
||||||
|
currency_code: price.currency_code,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
const region = price.region_id && regionsMap.get(price.region_id)
|
||||||
|
const variantPrice: PricingTypes.CreatePricesDTO = {
|
||||||
|
min_quantity: price.min_quantity,
|
||||||
|
max_quantity: price.max_quantity,
|
||||||
|
amount: price.amount,
|
||||||
|
currency_code: price.currency_code,
|
||||||
|
rules: {},
|
||||||
|
}
|
||||||
|
|
||||||
|
if (region) {
|
||||||
|
variantPrice.currency_code = region.currency_code
|
||||||
|
variantPrice.rules = {
|
||||||
|
region_id: region.id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delete price.region_id
|
||||||
|
|
||||||
|
if (variantIdToPriceSetIdMap.get(variantId)) {
|
||||||
|
ruleSetPricesToAdd.push(variantPrice)
|
||||||
|
} else {
|
||||||
|
priceSetToCreate.prices?.push(variantPrice)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let priceSetId = variantIdToPriceSetIdMap.get(variantId)
|
||||||
|
|
||||||
|
if (priceSetId) {
|
||||||
|
await pricingModuleService.addPrices({
|
||||||
|
priceSetId,
|
||||||
|
prices: ruleSetPricesToAdd,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
const createdPriceSet = await pricingModuleService.create(
|
||||||
|
priceSetToCreate
|
||||||
|
)
|
||||||
|
priceSetId = createdPriceSet?.id
|
||||||
|
|
||||||
|
createdPriceSets.push(createdPriceSet)
|
||||||
|
}
|
||||||
|
|
||||||
|
linksToCreate.push({
|
||||||
|
productService: {
|
||||||
|
variant_id: variantId,
|
||||||
|
},
|
||||||
|
pricingService: {
|
||||||
|
price_set_id: priceSetId,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const createdLinks = await remoteLink.create(linksToCreate)
|
||||||
|
|
||||||
|
let originalMoneyAmounts = await pricingModuleService.listMoneyAmounts(
|
||||||
|
{
|
||||||
|
id: moneyAmountsToUpdate.map((matu) => matu.id),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
select: ["id", "currency_code", "amount", "min_quantity", "max_quantity"],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
if (moneyAmountsToUpdate.length) {
|
||||||
|
await pricingModuleService.updateMoneyAmounts(moneyAmountsToUpdate)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
createdLinks,
|
||||||
|
originalMoneyAmounts,
|
||||||
|
createdPriceSets,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
upsertVariantPrices.aliases = {
|
||||||
|
productVariantsPrices: "productVariantsPrices",
|
||||||
|
}
|
||||||
@@ -6705,7 +6705,7 @@ __metadata:
|
|||||||
languageName: unknown
|
languageName: unknown
|
||||||
linkType: soft
|
linkType: soft
|
||||||
|
|
||||||
"@medusajs/modules-sdk@^1.12.0, @medusajs/modules-sdk@^1.12.1, @medusajs/modules-sdk@^1.8.8, @medusajs/modules-sdk@workspace:packages/modules-sdk":
|
"@medusajs/modules-sdk@^1.12.0, @medusajs/modules-sdk@^1.12.1, @medusajs/modules-sdk@^1.8.8, @medusajs/modules-sdk@workspace:^, @medusajs/modules-sdk@workspace:packages/modules-sdk":
|
||||||
version: 0.0.0-use.local
|
version: 0.0.0-use.local
|
||||||
resolution: "@medusajs/modules-sdk@workspace:packages/modules-sdk"
|
resolution: "@medusajs/modules-sdk@workspace:packages/modules-sdk"
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -6788,7 +6788,7 @@ __metadata:
|
|||||||
languageName: unknown
|
languageName: unknown
|
||||||
linkType: soft
|
linkType: soft
|
||||||
|
|
||||||
"@medusajs/pricing@workspace:packages/pricing":
|
"@medusajs/pricing@workspace:^, @medusajs/pricing@workspace:packages/pricing":
|
||||||
version: 0.0.0-use.local
|
version: 0.0.0-use.local
|
||||||
resolution: "@medusajs/pricing@workspace:packages/pricing"
|
resolution: "@medusajs/pricing@workspace:packages/pricing"
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -26517,6 +26517,8 @@ __metadata:
|
|||||||
"@medusajs/event-bus-local": "workspace:*"
|
"@medusajs/event-bus-local": "workspace:*"
|
||||||
"@medusajs/inventory": "workspace:^"
|
"@medusajs/inventory": "workspace:^"
|
||||||
"@medusajs/medusa": "workspace:*"
|
"@medusajs/medusa": "workspace:*"
|
||||||
|
"@medusajs/modules-sdk": "workspace:^"
|
||||||
|
"@medusajs/pricing": "workspace:^"
|
||||||
"@medusajs/product": "workspace:^"
|
"@medusajs/product": "workspace:^"
|
||||||
babel-preset-medusa-package: "*"
|
babel-preset-medusa-package: "*"
|
||||||
faker: ^5.5.3
|
faker: ^5.5.3
|
||||||
|
|||||||
Reference in new issue
Block a user