feat(medusa,types,workflows,utils,product): PricingModule Integration of PriceLists into Core (#5536)
This commit is contained in:
1 parent
9c7ac7332a
commit
dc5750dd66
94 files changed
+5697
-880
No files matched your search
@@ -0,0 +1,9 @@
|
|||||||
|
---
|
||||||
|
"@medusajs/workflows": patch
|
||||||
|
"@medusajs/product": patch
|
||||||
|
"@medusajs/medusa": patch
|
||||||
|
"@medusajs/types": patch
|
||||||
|
"@medusajs/utils": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
feat(medusa,types,workflows,utils,product): PricingModule Integration of PriceLists into Core
|
||||||
@@ -32,6 +32,8 @@ const keepTables = [
|
|||||||
"payment_provider",
|
"payment_provider",
|
||||||
"country",
|
"country",
|
||||||
"currency",
|
"currency",
|
||||||
|
"migrations",
|
||||||
|
"mikro_orm_migrations",
|
||||||
]
|
]
|
||||||
|
|
||||||
const DbTestUtil = {
|
const DbTestUtil = {
|
||||||
@@ -52,21 +54,23 @@ const DbTestUtil = {
|
|||||||
|
|
||||||
teardown: async function ({ forceDelete } = {}) {
|
teardown: async function ({ forceDelete } = {}) {
|
||||||
forceDelete = forceDelete || []
|
forceDelete = forceDelete || []
|
||||||
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';`)
|
||||||
|
const tableNames = await manager.query(`SELECT table_name
|
||||||
|
FROM information_schema.tables
|
||||||
|
WHERE table_schema = 'public';`)
|
||||||
|
|
||||||
for (const entity of entities) {
|
for (const { table_name } of tableNames) {
|
||||||
if (
|
if (
|
||||||
keepTables.includes(entity.tableName) &&
|
keepTables.includes(table_name) &&
|
||||||
!forceDelete.includes(entity.tableName)
|
!forceDelete.includes(table_name)
|
||||||
) {
|
) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
await manager.query(`DELETE
|
await manager.query(`DELETE
|
||||||
FROM "${entity.tableName}";`)
|
FROM "${table_name}";`)
|
||||||
}
|
}
|
||||||
|
|
||||||
await manager.query(`SET session_replication_role = 'origin';`)
|
await manager.query(`SET session_replication_role = 'origin';`)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ export * from "./simple-batch-job-factory"
|
|||||||
export * from "./simple-cart-factory"
|
export * from "./simple-cart-factory"
|
||||||
export * from "./simple-custom-shipping-option-factory"
|
export * from "./simple-custom-shipping-option-factory"
|
||||||
export * from "./simple-customer-factory"
|
export * from "./simple-customer-factory"
|
||||||
|
export * from "./simple-customer-group-factory"
|
||||||
export * from "./simple-discount-factory"
|
export * from "./simple-discount-factory"
|
||||||
export * from "./simple-gift-card-factory"
|
export * from "./simple-gift-card-factory"
|
||||||
export * from "./simple-line-item-factory"
|
export * from "./simple-line-item-factory"
|
||||||
@@ -22,5 +23,5 @@ export * from "./simple-shipping-method-factory"
|
|||||||
export * from "./simple-shipping-option-factory"
|
export * from "./simple-shipping-option-factory"
|
||||||
export * from "./simple-shipping-profile-factory"
|
export * from "./simple-shipping-profile-factory"
|
||||||
export * from "./simple-shipping-tax-rate-factory"
|
export * from "./simple-shipping-tax-rate-factory"
|
||||||
export * from "./simple-tax-rate-factory"
|
|
||||||
export * from "./simple-store-factory"
|
export * from "./simple-store-factory"
|
||||||
|
export * from "./simple-tax-rate-factory"
|
||||||
+191
@@ -0,0 +1,191 @@
|
|||||||
|
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 {
|
||||||
|
IPricingModuleService,
|
||||||
|
PriceListStatus,
|
||||||
|
PriceListType,
|
||||||
|
} from "@medusajs/types"
|
||||||
|
import path from "path"
|
||||||
|
import { startBootstrapApp } from "../../../../environment-helpers/bootstrap-app"
|
||||||
|
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_MEDUSA_V2: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("POST /admin/price-lists/:id/prices/batch", () => {
|
||||||
|
let dbConnection
|
||||||
|
let appContainer
|
||||||
|
let shutdownServer
|
||||||
|
let product
|
||||||
|
let variant
|
||||||
|
let pricingModuleService: IPricingModuleService
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
const cwd = path.resolve(path.join(__dirname, "..", "..", ".."))
|
||||||
|
dbConnection = await initDb({ cwd, env } as any)
|
||||||
|
shutdownServer = await startBootstrapApp({ cwd, env })
|
||||||
|
appContainer = getContainer()
|
||||||
|
pricingModuleService = appContainer.resolve("pricingModuleService")
|
||||||
|
})
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
const db = useDb()
|
||||||
|
await db.shutdown()
|
||||||
|
await shutdownServer()
|
||||||
|
})
|
||||||
|
|
||||||
|
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 update money amounts if variant id is present in prices", async () => {
|
||||||
|
const [priceList] = await pricingModuleService.createPriceLists([
|
||||||
|
{
|
||||||
|
title: "test price list",
|
||||||
|
description: "test",
|
||||||
|
ends_at: new Date(),
|
||||||
|
starts_at: new Date(),
|
||||||
|
status: PriceListStatus.ACTIVE,
|
||||||
|
type: PriceListType.OVERRIDE,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
await createVariantPriceSet({
|
||||||
|
container: appContainer,
|
||||||
|
variantId: variant.id,
|
||||||
|
prices: [
|
||||||
|
{
|
||||||
|
amount: 3000,
|
||||||
|
currency_code: "usd",
|
||||||
|
rules: {},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
const api = useApi() as any
|
||||||
|
const data = {
|
||||||
|
prices: [
|
||||||
|
{
|
||||||
|
variant_id: variant.id,
|
||||||
|
amount: 5000,
|
||||||
|
currency_code: "usd",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
await api.post(
|
||||||
|
`admin/price-lists/${priceList.id}/prices/batch`,
|
||||||
|
data,
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
const response = await api.get(
|
||||||
|
`/admin/price-lists/${priceList.id}`,
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(response.status).toEqual(200)
|
||||||
|
expect(response.data.price_list).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
created_at: expect.any(String),
|
||||||
|
updated_at: expect.any(String),
|
||||||
|
deleted_at: null,
|
||||||
|
name: "test price list",
|
||||||
|
description: "test",
|
||||||
|
type: "override",
|
||||||
|
status: "active",
|
||||||
|
starts_at: expect.any(String),
|
||||||
|
ends_at: expect.any(String),
|
||||||
|
customer_groups: [],
|
||||||
|
prices: [
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
created_at: expect.any(String),
|
||||||
|
updated_at: expect.any(String),
|
||||||
|
deleted_at: null,
|
||||||
|
currency_code: "usd",
|
||||||
|
amount: 5000,
|
||||||
|
min_quantity: null,
|
||||||
|
max_quantity: null,
|
||||||
|
price_list_id: expect.any(String),
|
||||||
|
region_id: null,
|
||||||
|
variant: expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
created_at: expect.any(String),
|
||||||
|
updated_at: expect.any(String),
|
||||||
|
deleted_at: null,
|
||||||
|
title: expect.any(String),
|
||||||
|
product_id: expect.any(String),
|
||||||
|
sku: null,
|
||||||
|
barcode: null,
|
||||||
|
ean: null,
|
||||||
|
upc: null,
|
||||||
|
variant_rank: 0,
|
||||||
|
inventory_quantity: 10,
|
||||||
|
allow_backorder: false,
|
||||||
|
manage_inventory: true,
|
||||||
|
hs_code: null,
|
||||||
|
origin_country: null,
|
||||||
|
mid_code: null,
|
||||||
|
material: null,
|
||||||
|
weight: null,
|
||||||
|
length: null,
|
||||||
|
height: null,
|
||||||
|
width: null,
|
||||||
|
metadata: null,
|
||||||
|
}),
|
||||||
|
variant_id: expect.any(String),
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -0,0 +1,191 @@
|
|||||||
|
import { useApi } from "../../../../environment-helpers/use-api"
|
||||||
|
import { getContainer } from "../../../../environment-helpers/use-container"
|
||||||
|
import { initDb, useDb } from "../../../../environment-helpers/use-db"
|
||||||
|
import {
|
||||||
|
simpleCustomerGroupFactory,
|
||||||
|
simpleProductFactory,
|
||||||
|
simpleRegionFactory,
|
||||||
|
} from "../../../../factories"
|
||||||
|
|
||||||
|
import { IPricingModuleService } from "@medusajs/types"
|
||||||
|
import path from "path"
|
||||||
|
import { startBootstrapApp } from "../../../../environment-helpers/bootstrap-app"
|
||||||
|
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_MEDUSA_V2: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("POST /admin/price-lists", () => {
|
||||||
|
let dbConnection
|
||||||
|
let appContainer
|
||||||
|
let shutdownServer
|
||||||
|
let product
|
||||||
|
let variant
|
||||||
|
let pricingModuleService: IPricingModuleService
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
const cwd = path.resolve(path.join(__dirname, "..", "..", ".."))
|
||||||
|
dbConnection = await initDb({ cwd, env } as any)
|
||||||
|
shutdownServer = await startBootstrapApp({ cwd, env })
|
||||||
|
appContainer = getContainer()
|
||||||
|
pricingModuleService = appContainer.resolve("pricingModuleService")
|
||||||
|
})
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
const db = useDb()
|
||||||
|
await db.shutdown()
|
||||||
|
await shutdownServer()
|
||||||
|
})
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await adminSeeder(dbConnection)
|
||||||
|
await createDefaultRuleTypes(appContainer)
|
||||||
|
await simpleCustomerGroupFactory(dbConnection, {
|
||||||
|
id: "customer-group-1",
|
||||||
|
name: "Test Group",
|
||||||
|
})
|
||||||
|
|
||||||
|
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 price list and money amounts", async () => {
|
||||||
|
await createVariantPriceSet({
|
||||||
|
container: appContainer,
|
||||||
|
variantId: variant.id,
|
||||||
|
prices: [
|
||||||
|
{
|
||||||
|
amount: 3000,
|
||||||
|
currency_code: "usd",
|
||||||
|
rules: {},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
const api = useApi() as any
|
||||||
|
const data = {
|
||||||
|
name: "test price list",
|
||||||
|
description: "test",
|
||||||
|
type: "override",
|
||||||
|
customer_groups: [{ id: "customer-group-1" }],
|
||||||
|
status: "active",
|
||||||
|
prices: [
|
||||||
|
{
|
||||||
|
amount: 400,
|
||||||
|
variant_id: variant.id,
|
||||||
|
currency_code: "usd",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await api.post(`admin/price-lists`, data, adminHeaders)
|
||||||
|
|
||||||
|
let response = await api.get(
|
||||||
|
`/admin/price-lists/${result.data.price_list.id}`,
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(response.status).toEqual(200)
|
||||||
|
expect(response.data.price_list).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
created_at: expect.any(String),
|
||||||
|
updated_at: expect.any(String),
|
||||||
|
deleted_at: null,
|
||||||
|
name: "test price list",
|
||||||
|
description: "test",
|
||||||
|
type: "override",
|
||||||
|
status: "active",
|
||||||
|
starts_at: null,
|
||||||
|
ends_at: null,
|
||||||
|
customer_groups: [
|
||||||
|
{
|
||||||
|
id: expect.any(String),
|
||||||
|
created_at: expect.any(String),
|
||||||
|
updated_at: expect.any(String),
|
||||||
|
deleted_at: null,
|
||||||
|
name: "Test Group",
|
||||||
|
metadata: null,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
prices: [
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
created_at: expect.any(String),
|
||||||
|
updated_at: expect.any(String),
|
||||||
|
deleted_at: null,
|
||||||
|
currency_code: "usd",
|
||||||
|
amount: 400,
|
||||||
|
min_quantity: null,
|
||||||
|
max_quantity: null,
|
||||||
|
price_list_id: expect.any(String),
|
||||||
|
region_id: null,
|
||||||
|
variant: expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
created_at: expect.any(String),
|
||||||
|
updated_at: expect.any(String),
|
||||||
|
deleted_at: null,
|
||||||
|
title: expect.any(String),
|
||||||
|
product_id: expect.any(String),
|
||||||
|
sku: null,
|
||||||
|
barcode: null,
|
||||||
|
ean: null,
|
||||||
|
upc: null,
|
||||||
|
variant_rank: 0,
|
||||||
|
inventory_quantity: 10,
|
||||||
|
allow_backorder: false,
|
||||||
|
manage_inventory: true,
|
||||||
|
hs_code: null,
|
||||||
|
origin_country: null,
|
||||||
|
mid_code: null,
|
||||||
|
material: null,
|
||||||
|
weight: null,
|
||||||
|
length: null,
|
||||||
|
height: null,
|
||||||
|
width: null,
|
||||||
|
metadata: null,
|
||||||
|
}),
|
||||||
|
variant_id: expect.any(String),
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
+181
@@ -0,0 +1,181 @@
|
|||||||
|
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 { IPricingModuleService } from "@medusajs/types"
|
||||||
|
import path from "path"
|
||||||
|
import { startBootstrapApp } from "../../../../environment-helpers/bootstrap-app"
|
||||||
|
import adminSeeder from "../../../../helpers/admin-seeder"
|
||||||
|
import { createDefaultRuleTypes } from "../../../helpers/create-default-rule-types"
|
||||||
|
import { createVariantPriceSet } from "../../../helpers/create-variant-price-set"
|
||||||
|
import { AxiosInstance } from "axios"
|
||||||
|
|
||||||
|
jest.setTimeout(50000)
|
||||||
|
|
||||||
|
const adminHeaders = {
|
||||||
|
headers: {
|
||||||
|
"x-medusa-access-token": "test_token",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const env = {
|
||||||
|
MEDUSA_FF_MEDUSA_V2: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("DELETE /admin/price-lists/:id/products/:productId/batch", () => {
|
||||||
|
let dbConnection
|
||||||
|
let appContainer
|
||||||
|
let shutdownServer
|
||||||
|
let product
|
||||||
|
let variant1
|
||||||
|
let priceSet
|
||||||
|
let priceListId
|
||||||
|
let pricingModuleService: IPricingModuleService
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
const cwd = path.resolve(path.join(__dirname, "..", "..", ".."))
|
||||||
|
dbConnection = await initDb({ cwd, env } as any)
|
||||||
|
shutdownServer = await startBootstrapApp({ cwd, env })
|
||||||
|
appContainer = getContainer()
|
||||||
|
pricingModuleService = appContainer.resolve("pricingModuleService")
|
||||||
|
})
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
const db = useDb()
|
||||||
|
await db.shutdown()
|
||||||
|
await shutdownServer()
|
||||||
|
})
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
const api = useApi()! as AxiosInstance
|
||||||
|
|
||||||
|
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",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
variant1 = product.variants[0]
|
||||||
|
|
||||||
|
priceSet = await createVariantPriceSet({
|
||||||
|
container: appContainer,
|
||||||
|
variantId: variant1.id,
|
||||||
|
prices: [
|
||||||
|
{
|
||||||
|
amount: 3000,
|
||||||
|
currency_code: "usd",
|
||||||
|
rules: {},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
name: "test price list",
|
||||||
|
description: "test",
|
||||||
|
type: "override",
|
||||||
|
customer_groups: [],
|
||||||
|
status: "active",
|
||||||
|
prices: [
|
||||||
|
{
|
||||||
|
amount: 400,
|
||||||
|
variant_id: variant1.id,
|
||||||
|
currency_code: "usd",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
const priceListResult = await api.post(
|
||||||
|
`admin/price-lists`,
|
||||||
|
data,
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
priceListId = priceListResult.data.price_list.id
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
const db = useDb()
|
||||||
|
await db.teardown()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should delete prices in batch based on product ids", async () => {
|
||||||
|
const api = useApi()! as AxiosInstance
|
||||||
|
|
||||||
|
let priceSetMoneyAmounts =
|
||||||
|
await pricingModuleService.listPriceSetMoneyAmounts({
|
||||||
|
price_set_id: [priceSet.id],
|
||||||
|
})
|
||||||
|
expect(priceSetMoneyAmounts.length).toEqual(2)
|
||||||
|
|
||||||
|
const deleteRes = await api.delete(
|
||||||
|
`/admin/price-lists/${priceListId}/products/prices/batch`,
|
||||||
|
{
|
||||||
|
headers: adminHeaders.headers,
|
||||||
|
data: {
|
||||||
|
product_ids: [product.id],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
expect(deleteRes.status).toEqual(200)
|
||||||
|
|
||||||
|
priceSetMoneyAmounts = await pricingModuleService.listPriceSetMoneyAmounts({
|
||||||
|
price_set_id: [priceSet.id],
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(priceSetMoneyAmounts.length).toEqual(1)
|
||||||
|
expect(priceSetMoneyAmounts).toEqual([
|
||||||
|
expect.objectContaining({
|
||||||
|
price_list: null,
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should delete prices based on single product id", async () => {
|
||||||
|
const api = useApi()! as AxiosInstance
|
||||||
|
|
||||||
|
let priceSetMoneyAmounts =
|
||||||
|
await pricingModuleService.listPriceSetMoneyAmounts({
|
||||||
|
price_set_id: [priceSet.id],
|
||||||
|
})
|
||||||
|
expect(priceSetMoneyAmounts.length).toEqual(2)
|
||||||
|
|
||||||
|
const deleteRes = await api.delete(
|
||||||
|
`/admin/price-lists/${priceListId}/products/${product.id}/prices`,
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
expect(deleteRes.status).toEqual(200)
|
||||||
|
|
||||||
|
priceSetMoneyAmounts = await pricingModuleService.listPriceSetMoneyAmounts({
|
||||||
|
price_set_id: [priceSet.id],
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(priceSetMoneyAmounts.length).toEqual(1)
|
||||||
|
expect(priceSetMoneyAmounts).toEqual([
|
||||||
|
expect.objectContaining({
|
||||||
|
price_list: null,
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
})
|
||||||
|
})
|
||||||
+132
@@ -0,0 +1,132 @@
|
|||||||
|
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 { IPricingModuleService } from "@medusajs/types"
|
||||||
|
import path from "path"
|
||||||
|
import { startBootstrapApp } from "../../../../environment-helpers/bootstrap-app"
|
||||||
|
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_MEDUSA_V2: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("DELETE /admin/price-lists/:id/variants/:variantId/prices", () => {
|
||||||
|
let dbConnection
|
||||||
|
let appContainer
|
||||||
|
let shutdownServer
|
||||||
|
let product
|
||||||
|
let variant
|
||||||
|
let pricingModuleService: IPricingModuleService
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
const cwd = path.resolve(path.join(__dirname, "..", "..", ".."))
|
||||||
|
dbConnection = await initDb({ cwd, env } as any)
|
||||||
|
shutdownServer = await startBootstrapApp({ cwd, env })
|
||||||
|
appContainer = getContainer()
|
||||||
|
pricingModuleService = appContainer.resolve("pricingModuleService")
|
||||||
|
})
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
const db = useDb()
|
||||||
|
await db.shutdown()
|
||||||
|
await shutdownServer()
|
||||||
|
})
|
||||||
|
|
||||||
|
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 delete all prices based on product variant ids", async () => {
|
||||||
|
const priceSet = await createVariantPriceSet({
|
||||||
|
container: appContainer,
|
||||||
|
variantId: variant.id,
|
||||||
|
prices: [
|
||||||
|
{
|
||||||
|
amount: 3000,
|
||||||
|
currency_code: "usd",
|
||||||
|
rules: {},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
const api = useApi() as any
|
||||||
|
const data = {
|
||||||
|
name: "test price list",
|
||||||
|
description: "test",
|
||||||
|
type: "override",
|
||||||
|
customer_groups: [],
|
||||||
|
status: "active",
|
||||||
|
prices: [
|
||||||
|
{
|
||||||
|
amount: 400,
|
||||||
|
variant_id: variant.id,
|
||||||
|
currency_code: "usd",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await api.post(`admin/price-lists`, data, adminHeaders)
|
||||||
|
const priceListId = result.data.price_list.id
|
||||||
|
|
||||||
|
let psmas = await pricingModuleService.listPriceSetMoneyAmounts({
|
||||||
|
price_list_id: [priceListId],
|
||||||
|
})
|
||||||
|
expect(psmas.length).toEqual(1)
|
||||||
|
|
||||||
|
const deleteRes = await api.delete(
|
||||||
|
`/admin/price-lists/${priceListId}/variants/${variant.id}/prices`,
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
expect(deleteRes.status).toEqual(200)
|
||||||
|
|
||||||
|
psmas = await pricingModuleService.listPriceSetMoneyAmounts({
|
||||||
|
price_list_id: [priceListId],
|
||||||
|
})
|
||||||
|
expect(psmas.length).toEqual(0)
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -0,0 +1,146 @@
|
|||||||
|
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 { IPricingModuleService } from "@medusajs/types"
|
||||||
|
import path from "path"
|
||||||
|
import { startBootstrapApp } from "../../../../environment-helpers/bootstrap-app"
|
||||||
|
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_MEDUSA_V2: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("DELETE /admin/price-lists/:id", () => {
|
||||||
|
let dbConnection
|
||||||
|
let appContainer
|
||||||
|
let shutdownServer
|
||||||
|
let product
|
||||||
|
let variant
|
||||||
|
let pricingModuleService: IPricingModuleService
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
const cwd = path.resolve(path.join(__dirname, "..", "..", ".."))
|
||||||
|
dbConnection = await initDb({ cwd, env } as any)
|
||||||
|
shutdownServer = await startBootstrapApp({ cwd, env })
|
||||||
|
appContainer = getContainer()
|
||||||
|
pricingModuleService = appContainer.resolve("pricingModuleService")
|
||||||
|
})
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
const db = useDb()
|
||||||
|
await db.shutdown()
|
||||||
|
await shutdownServer()
|
||||||
|
})
|
||||||
|
|
||||||
|
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 delete price list prices by money amount ids", async () => {
|
||||||
|
await createVariantPriceSet({
|
||||||
|
container: appContainer,
|
||||||
|
variantId: variant.id,
|
||||||
|
prices: [
|
||||||
|
{
|
||||||
|
amount: 3000,
|
||||||
|
currency_code: "usd",
|
||||||
|
rules: {},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
const api = useApi() as any
|
||||||
|
const data = {
|
||||||
|
name: "test price list",
|
||||||
|
description: "test",
|
||||||
|
type: "override",
|
||||||
|
status: "active",
|
||||||
|
prices: [
|
||||||
|
{
|
||||||
|
amount: 400,
|
||||||
|
variant_id: variant.id,
|
||||||
|
currency_code: "usd",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
amount: 4000,
|
||||||
|
variant_id: variant.id,
|
||||||
|
currency_code: "usd",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await api.post(`admin/price-lists`, data, adminHeaders)
|
||||||
|
|
||||||
|
const priceListId = res.data.price_list.id
|
||||||
|
let psmas = await pricingModuleService.listPriceSetMoneyAmounts(
|
||||||
|
{
|
||||||
|
price_list_id: [priceListId],
|
||||||
|
},
|
||||||
|
{ relations: ["money_amount"] }
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(psmas.length).toEqual(2)
|
||||||
|
|
||||||
|
const deletePrice = psmas[0].money_amount
|
||||||
|
const deleteRes = await api.delete(
|
||||||
|
`/admin/price-lists/${priceListId}/prices/batch`,
|
||||||
|
{
|
||||||
|
data: {
|
||||||
|
price_ids: [deletePrice?.id],
|
||||||
|
},
|
||||||
|
...adminHeaders,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
expect(deleteRes.status).toEqual(200)
|
||||||
|
|
||||||
|
psmas = await pricingModuleService.listPriceSetMoneyAmounts({
|
||||||
|
price_list_id: [priceListId],
|
||||||
|
})
|
||||||
|
expect(psmas.length).toEqual(1)
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -0,0 +1,145 @@
|
|||||||
|
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 { IPricingModuleService } from "@medusajs/types"
|
||||||
|
import path from "path"
|
||||||
|
import { startBootstrapApp } from "../../../../environment-helpers/bootstrap-app"
|
||||||
|
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_MEDUSA_V2: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("DELETE /admin/price-lists/:id", () => {
|
||||||
|
let dbConnection
|
||||||
|
let appContainer
|
||||||
|
let shutdownServer
|
||||||
|
let product
|
||||||
|
let variant
|
||||||
|
let pricingModuleService: IPricingModuleService
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
const cwd = path.resolve(path.join(__dirname, "..", "..", ".."))
|
||||||
|
dbConnection = await initDb({ cwd, env } as any)
|
||||||
|
shutdownServer = await startBootstrapApp({ cwd, env })
|
||||||
|
appContainer = getContainer()
|
||||||
|
pricingModuleService = appContainer.resolve("pricingModuleService")
|
||||||
|
})
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
const db = useDb()
|
||||||
|
await db.shutdown()
|
||||||
|
await shutdownServer()
|
||||||
|
})
|
||||||
|
|
||||||
|
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 delete price list and money amounts", async () => {
|
||||||
|
const priceSet = await createVariantPriceSet({
|
||||||
|
container: appContainer,
|
||||||
|
variantId: variant.id,
|
||||||
|
prices: [
|
||||||
|
{
|
||||||
|
amount: 3000,
|
||||||
|
currency_code: "usd",
|
||||||
|
rules: {},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
const api = useApi() as any
|
||||||
|
const data = {
|
||||||
|
name: "test price list",
|
||||||
|
description: "test",
|
||||||
|
type: "override",
|
||||||
|
customer_groups: [],
|
||||||
|
status: "active",
|
||||||
|
prices: [
|
||||||
|
{
|
||||||
|
amount: 400,
|
||||||
|
variant_id: variant.id,
|
||||||
|
currency_code: "usd",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await api.post(`admin/price-lists`, data, adminHeaders)
|
||||||
|
const priceListId = result.data.price_list.id
|
||||||
|
|
||||||
|
const getResponse = await api.get(
|
||||||
|
`/admin/price-lists/${priceListId}`,
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
expect(getResponse.status).toEqual(200)
|
||||||
|
|
||||||
|
let psmas = await pricingModuleService.listPriceSetMoneyAmounts({
|
||||||
|
price_list_id: [priceListId],
|
||||||
|
})
|
||||||
|
expect(psmas.length).toEqual(1)
|
||||||
|
|
||||||
|
const deleteRes = await api.delete(
|
||||||
|
`/admin/price-lists/${priceListId}`,
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
expect(deleteRes.status).toEqual(200)
|
||||||
|
|
||||||
|
const afterDelete = await api
|
||||||
|
.get(`/admin/price-lists/${priceListId}`, adminHeaders)
|
||||||
|
.catch((err) => {
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
expect(afterDelete.response.status).toEqual(404)
|
||||||
|
|
||||||
|
psmas = await pricingModuleService.listPriceSetMoneyAmounts({
|
||||||
|
price_list_id: [priceListId],
|
||||||
|
})
|
||||||
|
expect(psmas.length).toEqual(0)
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -0,0 +1,204 @@
|
|||||||
|
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 {
|
||||||
|
IPricingModuleService,
|
||||||
|
PriceListStatus,
|
||||||
|
PriceListType,
|
||||||
|
} from "@medusajs/types"
|
||||||
|
import path from "path"
|
||||||
|
import { startBootstrapApp } from "../../../../environment-helpers/bootstrap-app"
|
||||||
|
import adminSeeder from "../../../../helpers/admin-seeder"
|
||||||
|
import { createVariantPriceSet } from "../../../helpers/create-variant-price-set"
|
||||||
|
|
||||||
|
jest.setTimeout(50000)
|
||||||
|
|
||||||
|
const adminHeaders = {
|
||||||
|
headers: {
|
||||||
|
"x-medusa-access-token": "test_token",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const env = {
|
||||||
|
MEDUSA_FF_MEDUSA_V2: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("GET /admin/price-lists/:id", () => {
|
||||||
|
let dbConnection
|
||||||
|
let appContainer
|
||||||
|
let shutdownServer
|
||||||
|
let product
|
||||||
|
let variant
|
||||||
|
let pricingModuleService: IPricingModuleService
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
const cwd = path.resolve(path.join(__dirname, "..", "..", ".."))
|
||||||
|
dbConnection = await initDb({ cwd, env } as any)
|
||||||
|
shutdownServer = await startBootstrapApp({ cwd, env })
|
||||||
|
appContainer = getContainer()
|
||||||
|
pricingModuleService = appContainer.resolve("pricingModuleService")
|
||||||
|
})
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
const db = useDb()
|
||||||
|
await db.shutdown()
|
||||||
|
await shutdownServer()
|
||||||
|
})
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await adminSeeder(dbConnection)
|
||||||
|
|
||||||
|
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 get price list and its money amounts with variants", async () => {
|
||||||
|
const priceSet = await createVariantPriceSet({
|
||||||
|
container: appContainer,
|
||||||
|
variantId: variant.id,
|
||||||
|
prices: [
|
||||||
|
{
|
||||||
|
amount: 3000,
|
||||||
|
currency_code: "usd",
|
||||||
|
rules: {},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
rules: [],
|
||||||
|
})
|
||||||
|
|
||||||
|
const [priceList] = await pricingModuleService.createPriceLists([
|
||||||
|
{
|
||||||
|
title: "test price list",
|
||||||
|
description: "test",
|
||||||
|
ends_at: new Date(),
|
||||||
|
starts_at: new Date(),
|
||||||
|
status: PriceListStatus.ACTIVE,
|
||||||
|
type: PriceListType.OVERRIDE,
|
||||||
|
prices: [
|
||||||
|
{
|
||||||
|
amount: 5000,
|
||||||
|
currency_code: "usd",
|
||||||
|
price_set_id: priceSet.id,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
await pricingModuleService.createPriceLists([
|
||||||
|
{
|
||||||
|
title: "test price list 1",
|
||||||
|
description: "test 1",
|
||||||
|
ends_at: new Date(),
|
||||||
|
starts_at: new Date(),
|
||||||
|
status: PriceListStatus.ACTIVE,
|
||||||
|
type: PriceListType.OVERRIDE,
|
||||||
|
prices: [
|
||||||
|
{
|
||||||
|
amount: 5000,
|
||||||
|
currency_code: "usd",
|
||||||
|
price_set_id: priceSet.id,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
const api = useApi() as any
|
||||||
|
|
||||||
|
const response = await api.get(
|
||||||
|
`/admin/price-lists/${priceList.id}`,
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(response.status).toEqual(200)
|
||||||
|
expect(response.data.price_list).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
created_at: expect.any(String),
|
||||||
|
updated_at: expect.any(String),
|
||||||
|
deleted_at: null,
|
||||||
|
name: "test price list",
|
||||||
|
description: "test",
|
||||||
|
type: "override",
|
||||||
|
status: "active",
|
||||||
|
starts_at: expect.any(String),
|
||||||
|
ends_at: expect.any(String),
|
||||||
|
customer_groups: [],
|
||||||
|
prices: [
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
created_at: expect.any(String),
|
||||||
|
updated_at: expect.any(String),
|
||||||
|
deleted_at: null,
|
||||||
|
currency_code: "usd",
|
||||||
|
amount: 5000,
|
||||||
|
min_quantity: null,
|
||||||
|
max_quantity: null,
|
||||||
|
price_list_id: expect.any(String),
|
||||||
|
region_id: null,
|
||||||
|
variant: expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
created_at: expect.any(String),
|
||||||
|
updated_at: expect.any(String),
|
||||||
|
deleted_at: null,
|
||||||
|
title: expect.any(String),
|
||||||
|
product_id: expect.any(String),
|
||||||
|
sku: null,
|
||||||
|
barcode: null,
|
||||||
|
ean: null,
|
||||||
|
upc: null,
|
||||||
|
variant_rank: 0,
|
||||||
|
inventory_quantity: 10,
|
||||||
|
allow_backorder: false,
|
||||||
|
manage_inventory: true,
|
||||||
|
hs_code: null,
|
||||||
|
origin_country: null,
|
||||||
|
mid_code: null,
|
||||||
|
material: null,
|
||||||
|
weight: null,
|
||||||
|
length: null,
|
||||||
|
height: null,
|
||||||
|
width: null,
|
||||||
|
metadata: null,
|
||||||
|
}),
|
||||||
|
variant_id: expect.any(String),
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should throw an error when price list is not found", async () => {
|
||||||
|
const api = useApi() as any
|
||||||
|
|
||||||
|
const error = await api
|
||||||
|
.get(`/admin/price-lists/does-not-exist`, adminHeaders)
|
||||||
|
.catch((e) => e)
|
||||||
|
|
||||||
|
expect(error.response.status).toBe(404)
|
||||||
|
expect(error.response.data).toEqual({
|
||||||
|
type: "not_found",
|
||||||
|
message: "Price list with id: does-not-exist was not found",
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
+274
@@ -0,0 +1,274 @@
|
|||||||
|
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 {
|
||||||
|
IPricingModuleService,
|
||||||
|
PriceListStatus,
|
||||||
|
PriceListType,
|
||||||
|
} from "@medusajs/types"
|
||||||
|
import path from "path"
|
||||||
|
import { startBootstrapApp } from "../../../../environment-helpers/bootstrap-app"
|
||||||
|
import adminSeeder from "../../../../helpers/admin-seeder"
|
||||||
|
import { createVariantPriceSet } from "../../../helpers/create-variant-price-set"
|
||||||
|
|
||||||
|
jest.setTimeout(50000)
|
||||||
|
|
||||||
|
const adminHeaders = {
|
||||||
|
headers: {
|
||||||
|
"x-medusa-access-token": "test_token",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const env = {
|
||||||
|
MEDUSA_FF_MEDUSA_V2: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("GET /admin/price-lists/:id/products", () => {
|
||||||
|
let dbConnection
|
||||||
|
let appContainer
|
||||||
|
let shutdownServer
|
||||||
|
let product
|
||||||
|
let product2
|
||||||
|
let variant
|
||||||
|
let pricingModuleService: IPricingModuleService
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
const cwd = path.resolve(path.join(__dirname, "..", "..", ".."))
|
||||||
|
dbConnection = await initDb({ cwd, env } as any)
|
||||||
|
shutdownServer = await startBootstrapApp({ cwd, env })
|
||||||
|
appContainer = getContainer()
|
||||||
|
pricingModuleService = appContainer.resolve("pricingModuleService")
|
||||||
|
})
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
const db = useDb()
|
||||||
|
await db.shutdown()
|
||||||
|
await shutdownServer()
|
||||||
|
})
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await adminSeeder(dbConnection)
|
||||||
|
|
||||||
|
product = await simpleProductFactory(dbConnection, {
|
||||||
|
id: "test-product-with-variant",
|
||||||
|
title: "uniquely fun product",
|
||||||
|
variants: [
|
||||||
|
{
|
||||||
|
options: [{ option_id: "test-product-option-1", value: "test" }],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
id: "test-product-option-1",
|
||||||
|
title: "Test option 1",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
variant = product.variants[0]
|
||||||
|
|
||||||
|
product2 = await simpleProductFactory(dbConnection, {
|
||||||
|
id: "test-product-with-variant-2",
|
||||||
|
title: "uniquely fun product 2",
|
||||||
|
variants: [
|
||||||
|
{
|
||||||
|
options: [{ option_id: "test-product-option-2", value: "test 2" }],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
id: "test-product-option-2",
|
||||||
|
title: "Test option 2",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
const db = useDb()
|
||||||
|
await db.teardown()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should list all products in a price list", async () => {
|
||||||
|
const priceSet = await createVariantPriceSet({
|
||||||
|
container: appContainer,
|
||||||
|
variantId: variant.id,
|
||||||
|
prices: [
|
||||||
|
{
|
||||||
|
amount: 3000,
|
||||||
|
currency_code: "usd",
|
||||||
|
rules: {},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
rules: [],
|
||||||
|
})
|
||||||
|
|
||||||
|
const [priceList] = await pricingModuleService.createPriceLists([
|
||||||
|
{
|
||||||
|
title: "test price list",
|
||||||
|
description: "test",
|
||||||
|
ends_at: new Date(),
|
||||||
|
starts_at: new Date(),
|
||||||
|
status: PriceListStatus.ACTIVE,
|
||||||
|
type: PriceListType.OVERRIDE,
|
||||||
|
prices: [
|
||||||
|
{
|
||||||
|
amount: 5000,
|
||||||
|
currency_code: "usd",
|
||||||
|
price_set_id: priceSet.id,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
const api = useApi() as any
|
||||||
|
|
||||||
|
let response = await api.get(
|
||||||
|
`/admin/price-lists/${priceList.id}/products`,
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(response.status).toEqual(200)
|
||||||
|
expect(response.data.count).toEqual(1)
|
||||||
|
expect(response.data.products).toEqual([
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
title: expect.any(String),
|
||||||
|
handle: expect.any(String),
|
||||||
|
subtitle: null,
|
||||||
|
description: null,
|
||||||
|
is_giftcard: false,
|
||||||
|
status: "draft",
|
||||||
|
thumbnail: null,
|
||||||
|
weight: null,
|
||||||
|
length: null,
|
||||||
|
height: null,
|
||||||
|
width: null,
|
||||||
|
origin_country: null,
|
||||||
|
hs_code: null,
|
||||||
|
mid_code: null,
|
||||||
|
material: null,
|
||||||
|
collection_id: null,
|
||||||
|
collection: null,
|
||||||
|
type_id: null,
|
||||||
|
type: null,
|
||||||
|
discountable: true,
|
||||||
|
external_id: null,
|
||||||
|
created_at: expect.any(String),
|
||||||
|
updated_at: expect.any(String),
|
||||||
|
deleted_at: null,
|
||||||
|
metadata: null,
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
|
||||||
|
response = await api.get(
|
||||||
|
`/admin/products?price_list_id[]=${priceList.id}`,
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(response.status).toEqual(200)
|
||||||
|
expect(response.data.count).toEqual(1)
|
||||||
|
expect(response.data.products).toEqual([
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
title: expect.any(String),
|
||||||
|
handle: expect.any(String),
|
||||||
|
subtitle: null,
|
||||||
|
description: null,
|
||||||
|
is_giftcard: false,
|
||||||
|
status: "draft",
|
||||||
|
thumbnail: null,
|
||||||
|
weight: null,
|
||||||
|
length: null,
|
||||||
|
height: null,
|
||||||
|
width: null,
|
||||||
|
origin_country: null,
|
||||||
|
hs_code: null,
|
||||||
|
mid_code: null,
|
||||||
|
material: null,
|
||||||
|
collection_id: null,
|
||||||
|
collection: null,
|
||||||
|
type_id: null,
|
||||||
|
type: null,
|
||||||
|
discountable: true,
|
||||||
|
external_id: null,
|
||||||
|
created_at: expect.any(String),
|
||||||
|
updated_at: expect.any(String),
|
||||||
|
deleted_at: null,
|
||||||
|
metadata: null,
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should list all products constrained by search query in a price list", async () => {
|
||||||
|
const priceSet = await createVariantPriceSet({
|
||||||
|
container: appContainer,
|
||||||
|
variantId: variant.id,
|
||||||
|
prices: [
|
||||||
|
{
|
||||||
|
amount: 3000,
|
||||||
|
currency_code: "usd",
|
||||||
|
rules: {},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
rules: [],
|
||||||
|
})
|
||||||
|
|
||||||
|
const [priceList] = await pricingModuleService.createPriceLists([
|
||||||
|
{
|
||||||
|
title: "test price list",
|
||||||
|
description: "test",
|
||||||
|
ends_at: new Date(),
|
||||||
|
starts_at: new Date(),
|
||||||
|
status: PriceListStatus.ACTIVE,
|
||||||
|
type: PriceListType.OVERRIDE,
|
||||||
|
prices: [
|
||||||
|
{
|
||||||
|
amount: 5000,
|
||||||
|
currency_code: "usd",
|
||||||
|
price_set_id: priceSet.id,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
const api = useApi() as any
|
||||||
|
|
||||||
|
let response = await api.get(
|
||||||
|
`/admin/price-lists/${priceList.id}/products?q=shouldnotreturnanything`,
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(response.status).toEqual(200)
|
||||||
|
expect(response.data.count).toEqual(0)
|
||||||
|
expect(response.data.products).toEqual([])
|
||||||
|
|
||||||
|
response = await api.get(
|
||||||
|
`/admin/price-lists/${priceList.id}/products?q=uniquely`,
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(response.status).toEqual(200)
|
||||||
|
expect(response.data.count).toEqual(1)
|
||||||
|
expect(response.data.products).toEqual([
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
|
||||||
|
response = await api.get(
|
||||||
|
`/admin/price-lists/${priceList.id}/products?q=`,
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(response.status).toEqual(200)
|
||||||
|
expect(response.data.count).toEqual(1)
|
||||||
|
expect(response.data.products).toEqual([
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -0,0 +1,170 @@
|
|||||||
|
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 {
|
||||||
|
IPricingModuleService,
|
||||||
|
PriceListStatus,
|
||||||
|
PriceListType,
|
||||||
|
} from "@medusajs/types"
|
||||||
|
import path from "path"
|
||||||
|
import { startBootstrapApp } from "../../../../environment-helpers/bootstrap-app"
|
||||||
|
import adminSeeder from "../../../../helpers/admin-seeder"
|
||||||
|
import { createVariantPriceSet } from "../../../helpers/create-variant-price-set"
|
||||||
|
|
||||||
|
jest.setTimeout(50000)
|
||||||
|
|
||||||
|
const adminHeaders = {
|
||||||
|
headers: {
|
||||||
|
"x-medusa-access-token": "test_token",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const env = {
|
||||||
|
MEDUSA_FF_MEDUSA_V2: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("GET /admin/price-lists", () => {
|
||||||
|
let dbConnection
|
||||||
|
let appContainer
|
||||||
|
let shutdownServer
|
||||||
|
let product
|
||||||
|
let variant
|
||||||
|
let pricingModuleService: IPricingModuleService
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
const cwd = path.resolve(path.join(__dirname, "..", "..", ".."))
|
||||||
|
dbConnection = await initDb({ cwd, env } as any)
|
||||||
|
shutdownServer = await startBootstrapApp({ cwd, env })
|
||||||
|
appContainer = getContainer()
|
||||||
|
pricingModuleService = appContainer.resolve("pricingModuleService")
|
||||||
|
})
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
const db = useDb()
|
||||||
|
await db.shutdown()
|
||||||
|
await shutdownServer()
|
||||||
|
})
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await adminSeeder(dbConnection)
|
||||||
|
|
||||||
|
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 get price list and its money amounts with variants", async () => {
|
||||||
|
const priceSet = await createVariantPriceSet({
|
||||||
|
container: appContainer,
|
||||||
|
variantId: variant.id,
|
||||||
|
prices: [
|
||||||
|
{
|
||||||
|
amount: 3000,
|
||||||
|
currency_code: "usd",
|
||||||
|
rules: {},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
rules: [],
|
||||||
|
})
|
||||||
|
|
||||||
|
const [priceList] = await pricingModuleService.createPriceLists([
|
||||||
|
{
|
||||||
|
title: "test price list",
|
||||||
|
description: "test",
|
||||||
|
ends_at: new Date(),
|
||||||
|
starts_at: new Date(),
|
||||||
|
status: PriceListStatus.ACTIVE,
|
||||||
|
type: PriceListType.OVERRIDE,
|
||||||
|
prices: [
|
||||||
|
{
|
||||||
|
amount: 5000,
|
||||||
|
currency_code: "usd",
|
||||||
|
price_set_id: priceSet.id,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
const api = useApi() as any
|
||||||
|
|
||||||
|
const response = await api.get(`/admin/price-lists`, adminHeaders)
|
||||||
|
|
||||||
|
expect(response.status).toEqual(200)
|
||||||
|
expect(response.data.count).toEqual(1)
|
||||||
|
expect(response.data.price_lists).toEqual([
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
created_at: expect.any(String),
|
||||||
|
updated_at: expect.any(String),
|
||||||
|
deleted_at: null,
|
||||||
|
name: "test price list",
|
||||||
|
description: "test",
|
||||||
|
type: "override",
|
||||||
|
status: "active",
|
||||||
|
starts_at: expect.any(String),
|
||||||
|
ends_at: expect.any(String),
|
||||||
|
customer_groups: [],
|
||||||
|
prices: [
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
created_at: expect.any(String),
|
||||||
|
updated_at: expect.any(String),
|
||||||
|
deleted_at: null,
|
||||||
|
currency_code: "usd",
|
||||||
|
amount: 5000,
|
||||||
|
min_quantity: null,
|
||||||
|
max_quantity: null,
|
||||||
|
price_list_id: expect.any(String),
|
||||||
|
region_id: null,
|
||||||
|
variant: expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
created_at: expect.any(String),
|
||||||
|
updated_at: expect.any(String),
|
||||||
|
deleted_at: null,
|
||||||
|
title: expect.any(String),
|
||||||
|
product_id: expect.any(String),
|
||||||
|
sku: null,
|
||||||
|
barcode: null,
|
||||||
|
ean: null,
|
||||||
|
upc: null,
|
||||||
|
variant_rank: 0,
|
||||||
|
inventory_quantity: 10,
|
||||||
|
allow_backorder: false,
|
||||||
|
manage_inventory: true,
|
||||||
|
hs_code: null,
|
||||||
|
origin_country: null,
|
||||||
|
mid_code: null,
|
||||||
|
material: null,
|
||||||
|
weight: null,
|
||||||
|
length: null,
|
||||||
|
height: null,
|
||||||
|
width: null,
|
||||||
|
metadata: null,
|
||||||
|
}),
|
||||||
|
variant_id: expect.any(String),
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -0,0 +1,226 @@
|
|||||||
|
import { useApi } from "../../../../environment-helpers/use-api"
|
||||||
|
import { getContainer } from "../../../../environment-helpers/use-container"
|
||||||
|
import { initDb, useDb } from "../../../../environment-helpers/use-db"
|
||||||
|
import {
|
||||||
|
simpleCustomerGroupFactory,
|
||||||
|
simpleProductFactory,
|
||||||
|
simpleRegionFactory,
|
||||||
|
} from "../../../../factories"
|
||||||
|
|
||||||
|
import {
|
||||||
|
IPricingModuleService,
|
||||||
|
PriceListStatus,
|
||||||
|
PriceListType,
|
||||||
|
} from "@medusajs/types"
|
||||||
|
import path from "path"
|
||||||
|
import { startBootstrapApp } from "../../../../environment-helpers/bootstrap-app"
|
||||||
|
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_MEDUSA_V2: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("POST /admin/price-lists/:id", () => {
|
||||||
|
let dbConnection
|
||||||
|
let appContainer
|
||||||
|
let shutdownServer
|
||||||
|
let product
|
||||||
|
let variant
|
||||||
|
let variant2
|
||||||
|
let pricingModuleService: IPricingModuleService
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
const cwd = path.resolve(path.join(__dirname, "..", "..", ".."))
|
||||||
|
dbConnection = await initDb({ cwd, env } as any)
|
||||||
|
shutdownServer = await startBootstrapApp({ cwd, env })
|
||||||
|
appContainer = getContainer()
|
||||||
|
pricingModuleService = appContainer.resolve("pricingModuleService")
|
||||||
|
})
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
const db = useDb()
|
||||||
|
await db.shutdown()
|
||||||
|
await shutdownServer()
|
||||||
|
})
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await adminSeeder(dbConnection)
|
||||||
|
await createDefaultRuleTypes(appContainer)
|
||||||
|
await simpleCustomerGroupFactory(dbConnection, {
|
||||||
|
id: "customer-group-2",
|
||||||
|
name: "Test Group 2",
|
||||||
|
})
|
||||||
|
|
||||||
|
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: [{ option_id: "test-product-option-2", value: "test 2" }],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
id: "test-product-option-1",
|
||||||
|
title: "Test option 1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "test-product-option-2",
|
||||||
|
title: "Test option 2",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
variant = product.variants[0]
|
||||||
|
variant2 = product.variants[1]
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
const db = useDb()
|
||||||
|
await db.teardown()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should update price lists successfully with prices", async () => {
|
||||||
|
const var2PriceSet = await createVariantPriceSet({
|
||||||
|
container: appContainer,
|
||||||
|
variantId: variant2.id,
|
||||||
|
prices: [],
|
||||||
|
})
|
||||||
|
|
||||||
|
const [priceList] = await pricingModuleService.createPriceLists([
|
||||||
|
{
|
||||||
|
title: "test price list",
|
||||||
|
description: "test",
|
||||||
|
ends_at: new Date(),
|
||||||
|
starts_at: new Date(),
|
||||||
|
status: PriceListStatus.ACTIVE,
|
||||||
|
type: PriceListType.OVERRIDE,
|
||||||
|
prices: [
|
||||||
|
{
|
||||||
|
amount: 3000,
|
||||||
|
currency_code: "usd",
|
||||||
|
price_set_id: var2PriceSet.id,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
await createVariantPriceSet({
|
||||||
|
container: appContainer,
|
||||||
|
variantId: variant.id,
|
||||||
|
prices: [
|
||||||
|
{
|
||||||
|
amount: 3000,
|
||||||
|
currency_code: "usd",
|
||||||
|
rules: {},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
const api = useApi() as any
|
||||||
|
const data = {
|
||||||
|
name: "new price list name",
|
||||||
|
description: "new price list description",
|
||||||
|
customer_groups: [{ id: "customer-group-2" }],
|
||||||
|
prices: [
|
||||||
|
{
|
||||||
|
variant_id: variant.id,
|
||||||
|
amount: 5000,
|
||||||
|
currency_code: "usd",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: priceList?.price_set_money_amounts?.[0].money_amount?.id,
|
||||||
|
amount: 6000,
|
||||||
|
currency_code: "usd",
|
||||||
|
variant_id: variant2.id,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
await api.post(`admin/price-lists/${priceList.id}`, data, adminHeaders)
|
||||||
|
|
||||||
|
const response = await api.get(
|
||||||
|
`/admin/price-lists/${priceList.id}`,
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(response.status).toEqual(200)
|
||||||
|
expect(response.data.price_list).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
created_at: expect.any(String),
|
||||||
|
updated_at: expect.any(String),
|
||||||
|
deleted_at: null,
|
||||||
|
name: "new price list name",
|
||||||
|
description: "new price list description",
|
||||||
|
type: "override",
|
||||||
|
status: "active",
|
||||||
|
starts_at: expect.any(String),
|
||||||
|
ends_at: expect.any(String),
|
||||||
|
customer_groups: [
|
||||||
|
{
|
||||||
|
id: expect.any(String),
|
||||||
|
created_at: expect.any(String),
|
||||||
|
updated_at: expect.any(String),
|
||||||
|
deleted_at: null,
|
||||||
|
name: "Test Group 2",
|
||||||
|
metadata: null,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
prices: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
created_at: expect.any(String),
|
||||||
|
updated_at: expect.any(String),
|
||||||
|
deleted_at: null,
|
||||||
|
currency_code: "usd",
|
||||||
|
amount: 5000,
|
||||||
|
min_quantity: null,
|
||||||
|
max_quantity: null,
|
||||||
|
price_list_id: priceList.id,
|
||||||
|
region_id: null,
|
||||||
|
variant: expect.objectContaining({
|
||||||
|
id: variant.id,
|
||||||
|
}),
|
||||||
|
variant_id: variant.id,
|
||||||
|
}),
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
created_at: expect.any(String),
|
||||||
|
updated_at: expect.any(String),
|
||||||
|
deleted_at: null,
|
||||||
|
currency_code: "usd",
|
||||||
|
amount: 6000,
|
||||||
|
min_quantity: null,
|
||||||
|
max_quantity: null,
|
||||||
|
price_list_id: priceList.id,
|
||||||
|
region_id: null,
|
||||||
|
variant: expect.objectContaining({
|
||||||
|
id: variant2.id,
|
||||||
|
}),
|
||||||
|
variant_id: variant2.id,
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -0,0 +1,289 @@
|
|||||||
|
import { useApi } from "../../../../environment-helpers/use-api"
|
||||||
|
import { getContainer } from "../../../../environment-helpers/use-container"
|
||||||
|
import { initDb, useDb } from "../../../../environment-helpers/use-db"
|
||||||
|
import {
|
||||||
|
simpleCustomerFactory,
|
||||||
|
simpleCustomerGroupFactory,
|
||||||
|
simpleProductFactory,
|
||||||
|
simpleRegionFactory,
|
||||||
|
} from "../../../../factories"
|
||||||
|
|
||||||
|
import {
|
||||||
|
IPricingModuleService,
|
||||||
|
PriceListStatus,
|
||||||
|
PriceListType,
|
||||||
|
} from "@medusajs/types"
|
||||||
|
import { AxiosInstance } from "axios"
|
||||||
|
import path from "path"
|
||||||
|
import { startBootstrapApp } from "../../../../environment-helpers/bootstrap-app"
|
||||||
|
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_MEDUSA_V2: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("GET /store/products/:id", () => {
|
||||||
|
let dbConnection
|
||||||
|
let appContainer
|
||||||
|
let shutdownServer
|
||||||
|
let product
|
||||||
|
let variant
|
||||||
|
let priceSetId
|
||||||
|
let pricingModuleService: IPricingModuleService
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
const cwd = path.resolve(path.join(__dirname, "..", "..", ".."))
|
||||||
|
dbConnection = await initDb({ cwd, env } as any)
|
||||||
|
shutdownServer = await startBootstrapApp({ cwd, env })
|
||||||
|
appContainer = getContainer()
|
||||||
|
pricingModuleService = appContainer.resolve("pricingModuleService")
|
||||||
|
})
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
const db = useDb()
|
||||||
|
await db.shutdown()
|
||||||
|
await shutdownServer()
|
||||||
|
})
|
||||||
|
|
||||||
|
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",
|
||||||
|
status: "published",
|
||||||
|
variants: [
|
||||||
|
{
|
||||||
|
options: [{ option_id: "test-product-option-1", value: "test" }],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
id: "test-product-option-1",
|
||||||
|
title: "Test option 1",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
variant = product.variants[0]
|
||||||
|
|
||||||
|
const priceSet = await createVariantPriceSet({
|
||||||
|
container: appContainer,
|
||||||
|
variantId: variant.id,
|
||||||
|
prices: [
|
||||||
|
{
|
||||||
|
amount: 3000,
|
||||||
|
currency_code: "usd",
|
||||||
|
rules: {},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
amount: 4000,
|
||||||
|
currency_code: "usd",
|
||||||
|
rules: {},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
rules: [],
|
||||||
|
})
|
||||||
|
|
||||||
|
priceSetId = priceSet.id
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
const db = useDb()
|
||||||
|
await db.teardown()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should get product and its prices from price-list created through the price list workflow", async () => {
|
||||||
|
const api = useApi()! as AxiosInstance
|
||||||
|
|
||||||
|
const priceListResponse = await api.post(
|
||||||
|
`/admin/price-lists`,
|
||||||
|
{
|
||||||
|
name: "test price list",
|
||||||
|
description: "test",
|
||||||
|
status: PriceListStatus.ACTIVE,
|
||||||
|
type: PriceListType.SALE,
|
||||||
|
prices: [
|
||||||
|
{
|
||||||
|
amount: 2500,
|
||||||
|
currency_code: "usd",
|
||||||
|
variant_id: variant.id,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
let response = await api.get(
|
||||||
|
`/store/products/${product.id}?currency_code=usd`
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(response.status).toEqual(200)
|
||||||
|
expect(response.data.product.variants[0].prices).toHaveLength(2)
|
||||||
|
expect(response.data.product.variants[0].prices).toEqual([
|
||||||
|
expect.objectContaining({
|
||||||
|
currency_code: "usd",
|
||||||
|
amount: 3000,
|
||||||
|
min_quantity: null,
|
||||||
|
max_quantity: null,
|
||||||
|
price_list_id: null,
|
||||||
|
}),
|
||||||
|
expect.objectContaining({
|
||||||
|
currency_code: "usd",
|
||||||
|
amount: 2500,
|
||||||
|
min_quantity: null,
|
||||||
|
max_quantity: null,
|
||||||
|
price_list_id: priceListResponse.data.price_list.id,
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
expect(response.data.product.variants[0]).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
original_price: 3000,
|
||||||
|
calculated_price: 2500,
|
||||||
|
calculated_price_type: "sale",
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should not list prices from price-list with customer groups if not logged in", async () => {
|
||||||
|
const api = useApi()! as AxiosInstance
|
||||||
|
|
||||||
|
const { id: customerGroupId } = await simpleCustomerGroupFactory(
|
||||||
|
dbConnection
|
||||||
|
)
|
||||||
|
|
||||||
|
const priceListResponse = await api.post(
|
||||||
|
`/admin/price-lists`,
|
||||||
|
{
|
||||||
|
name: "test price list",
|
||||||
|
description: "test",
|
||||||
|
status: PriceListStatus.ACTIVE,
|
||||||
|
type: PriceListType.SALE,
|
||||||
|
prices: [
|
||||||
|
{
|
||||||
|
amount: 2500,
|
||||||
|
currency_code: "usd",
|
||||||
|
variant_id: variant.id,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
customer_groups: [{ id: customerGroupId }],
|
||||||
|
},
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
let response = await api.get(
|
||||||
|
`/store/products/${product.id}?currency_code=usd`
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(response.status).toEqual(200)
|
||||||
|
expect(response.data.product.variants[0].prices).toEqual([
|
||||||
|
expect.objectContaining({
|
||||||
|
currency_code: "usd",
|
||||||
|
amount: 3000,
|
||||||
|
min_quantity: null,
|
||||||
|
max_quantity: null,
|
||||||
|
price_list_id: null,
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
expect(response.data.product.variants[0]).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
original_price: 3000,
|
||||||
|
calculated_price: 3000,
|
||||||
|
calculated_price_type: null,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should list prices from price-list with customer groups", async () => {
|
||||||
|
const api = useApi()! as AxiosInstance
|
||||||
|
|
||||||
|
await simpleCustomerFactory(dbConnection, {
|
||||||
|
id: "test-customer-5-pl",
|
||||||
|
email: "test5@email-pl.com",
|
||||||
|
first_name: "John",
|
||||||
|
last_name: "Deere",
|
||||||
|
password_hash:
|
||||||
|
"c2NyeXB0AAEAAAABAAAAAVMdaddoGjwU1TafDLLlBKnOTQga7P2dbrfgf3fB+rCD/cJOMuGzAvRdKutbYkVpuJWTU39P7OpuWNkUVoEETOVLMJafbI8qs8Qx/7jMQXkN", // password matching "test"
|
||||||
|
has_account: true,
|
||||||
|
groups: [{ id: "customer-group-1" }],
|
||||||
|
})
|
||||||
|
|
||||||
|
const authResponse = await api.post("/store/auth", {
|
||||||
|
email: "test5@email-pl.com",
|
||||||
|
password: "test",
|
||||||
|
})
|
||||||
|
|
||||||
|
const [authCookie] = authResponse.headers["set-cookie"][0].split(";")
|
||||||
|
|
||||||
|
const priceListResponse = await api.post(
|
||||||
|
`/admin/price-lists`,
|
||||||
|
{
|
||||||
|
name: "test price list",
|
||||||
|
description: "test",
|
||||||
|
status: PriceListStatus.ACTIVE,
|
||||||
|
type: PriceListType.SALE,
|
||||||
|
prices: [
|
||||||
|
{
|
||||||
|
amount: 2500,
|
||||||
|
currency_code: "usd",
|
||||||
|
variant_id: variant.id,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
customer_groups: [{ id: "customer-group-1" }],
|
||||||
|
},
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
let response = await api.get(
|
||||||
|
`/store/products/${product.id}?currency_code=usd`,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
Cookie: authCookie,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(response.status).toEqual(200)
|
||||||
|
expect(response.data.product.variants[0].prices).toHaveLength(2)
|
||||||
|
expect(response.data.product.variants[0].prices).toEqual([
|
||||||
|
expect.objectContaining({
|
||||||
|
currency_code: "usd",
|
||||||
|
amount: 3000,
|
||||||
|
min_quantity: null,
|
||||||
|
max_quantity: null,
|
||||||
|
price_list_id: null,
|
||||||
|
}),
|
||||||
|
expect.objectContaining({
|
||||||
|
currency_code: "usd",
|
||||||
|
amount: 2500,
|
||||||
|
min_quantity: null,
|
||||||
|
max_quantity: null,
|
||||||
|
price_list_id: priceListResponse.data.price_list.id,
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
expect(response.data.product.variants[0]).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
original_price: 3000,
|
||||||
|
calculated_price: 2500,
|
||||||
|
calculated_price_type: "sale",
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -8,6 +8,7 @@ import path from "path"
|
|||||||
import { startBootstrapApp } from "../../../environment-helpers/bootstrap-app"
|
import { startBootstrapApp } from "../../../environment-helpers/bootstrap-app"
|
||||||
import { getContainer } from "../../../environment-helpers/use-container"
|
import { getContainer } from "../../../environment-helpers/use-container"
|
||||||
import adminSeeder from "../../../helpers/admin-seeder"
|
import adminSeeder from "../../../helpers/admin-seeder"
|
||||||
|
import { createDefaultRuleTypes } from "../../helpers/create-default-rule-types"
|
||||||
|
|
||||||
jest.setTimeout(5000000)
|
jest.setTimeout(5000000)
|
||||||
|
|
||||||
@@ -46,6 +47,7 @@ describe("Link Modules", () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
|
await createDefaultRuleTypes(medusaContainer)
|
||||||
await adminSeeder(dbConnection)
|
await adminSeeder(dbConnection)
|
||||||
await simpleRegionFactory(dbConnection, {
|
await simpleRegionFactory(dbConnection, {
|
||||||
id: "region-1",
|
id: "region-1",
|
||||||
|
|||||||
@@ -0,0 +1,205 @@
|
|||||||
|
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 { PricingModuleService } from "@medusajs/pricing"
|
||||||
|
import { ProductModuleService } from "@medusajs/product"
|
||||||
|
import { AxiosInstance } from "axios"
|
||||||
|
import path from "path"
|
||||||
|
import { startBootstrapApp } from "../../../../environment-helpers/bootstrap-app"
|
||||||
|
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_MEDUSA_V2: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("POST /admin/products/:id/variants", () => {
|
||||||
|
let dbConnection
|
||||||
|
let appContainer
|
||||||
|
let shutdownServer
|
||||||
|
let product
|
||||||
|
let variant
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
const cwd = path.resolve(path.join(__dirname, "..", "..", ".."))
|
||||||
|
dbConnection = await initDb({ cwd, env } as any)
|
||||||
|
shutdownServer = await startBootstrapApp({ cwd, env })
|
||||||
|
appContainer = getContainer()
|
||||||
|
})
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
const db = useDb()
|
||||||
|
await db.shutdown()
|
||||||
|
await shutdownServer()
|
||||||
|
})
|
||||||
|
|
||||||
|
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 a product variant with its price sets and prices through the workflow", async () => {
|
||||||
|
const api = useApi()! as AxiosInstance
|
||||||
|
const data = {
|
||||||
|
title: "test variant create",
|
||||||
|
prices: [
|
||||||
|
{
|
||||||
|
amount: 66600,
|
||||||
|
region_id: "test-region",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
amount: 55500,
|
||||||
|
currency_code: "usd",
|
||||||
|
region_id: null,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
material: "boo",
|
||||||
|
mid_code: "234asdfadsf",
|
||||||
|
hs_code: "asdfasdf234",
|
||||||
|
origin_country: "DE",
|
||||||
|
sku: "asdf",
|
||||||
|
ean: "234",
|
||||||
|
upc: "234",
|
||||||
|
barcode: "asdf",
|
||||||
|
inventory_quantity: 234,
|
||||||
|
manage_inventory: true,
|
||||||
|
allow_backorder: true,
|
||||||
|
weight: 234,
|
||||||
|
width: 234,
|
||||||
|
height: 234,
|
||||||
|
length: 234,
|
||||||
|
metadata: { asdf: "asdf" },
|
||||||
|
options: [{ option_id: "test-product-option-1", value: "test option" }],
|
||||||
|
}
|
||||||
|
|
||||||
|
let response = await api.post(
|
||||||
|
`/admin/products/${product.id}/variants`,
|
||||||
|
data,
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(response.status).toEqual(200)
|
||||||
|
expect(response.data.product).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
variants: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
title: "test variant create",
|
||||||
|
prices: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
amount: 66600,
|
||||||
|
currency_code: "usd",
|
||||||
|
region_id: "test-region",
|
||||||
|
}),
|
||||||
|
expect.objectContaining({
|
||||||
|
amount: 55500,
|
||||||
|
currency_code: "usd",
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should compensate creating product variants when error throws in future step", async () => {
|
||||||
|
jest
|
||||||
|
.spyOn(PricingModuleService.prototype, "create")
|
||||||
|
.mockImplementation(() => {
|
||||||
|
throw new Error("Random Error")
|
||||||
|
})
|
||||||
|
|
||||||
|
const productSpy = jest.spyOn(
|
||||||
|
ProductModuleService.prototype,
|
||||||
|
"deleteVariants"
|
||||||
|
)
|
||||||
|
|
||||||
|
const api = useApi()! as AxiosInstance
|
||||||
|
const data = {
|
||||||
|
title: "test variant create",
|
||||||
|
prices: [
|
||||||
|
{
|
||||||
|
amount: 66600,
|
||||||
|
region_id: "test-region",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
amount: 55500,
|
||||||
|
currency_code: "usd",
|
||||||
|
region_id: null,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
material: "boo",
|
||||||
|
mid_code: "234asdfadsf",
|
||||||
|
hs_code: "asdfasdf234",
|
||||||
|
origin_country: "DE",
|
||||||
|
sku: "asdf",
|
||||||
|
ean: "234",
|
||||||
|
upc: "234",
|
||||||
|
barcode: "asdf",
|
||||||
|
inventory_quantity: 234,
|
||||||
|
manage_inventory: true,
|
||||||
|
allow_backorder: true,
|
||||||
|
weight: 234,
|
||||||
|
width: 234,
|
||||||
|
height: 234,
|
||||||
|
length: 234,
|
||||||
|
metadata: { asdf: "asdf" },
|
||||||
|
options: [{ option_id: "test-product-option-1", value: "test option" }],
|
||||||
|
}
|
||||||
|
|
||||||
|
await api
|
||||||
|
.post(`/admin/products/${product.id}/variants`, data, adminHeaders)
|
||||||
|
.catch((e) => e)
|
||||||
|
|
||||||
|
expect(productSpy).toBeCalledWith([expect.any(String)])
|
||||||
|
|
||||||
|
const getProductResponse = await api.get(
|
||||||
|
`/admin/products/${product.id}`,
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
expect(getProductResponse.data.product.variants).toHaveLength(1)
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -1,11 +1,13 @@
|
|||||||
import { initDb, useDb } from "../../../../environment-helpers/use-db"
|
import { initDb, useDb } from "../../../../environment-helpers/use-db"
|
||||||
|
|
||||||
import { Region } from "@medusajs/medusa"
|
import { Region } from "@medusajs/medusa"
|
||||||
|
import { IPricingModuleService } from "@medusajs/types"
|
||||||
import { AxiosInstance } from "axios"
|
import { AxiosInstance } from "axios"
|
||||||
import path from "path"
|
import path from "path"
|
||||||
import { startBootstrapApp } from "../../../../environment-helpers/bootstrap-app"
|
import { startBootstrapApp } from "../../../../environment-helpers/bootstrap-app"
|
||||||
import { useApi } from "../../../../environment-helpers/use-api"
|
import { useApi } from "../../../../environment-helpers/use-api"
|
||||||
import { getContainer } from "../../../../environment-helpers/use-container"
|
import { getContainer } from "../../../../environment-helpers/use-container"
|
||||||
|
import { simpleSalesChannelFactory } from "../../../../factories"
|
||||||
import adminSeeder from "../../../../helpers/admin-seeder"
|
import adminSeeder from "../../../../helpers/admin-seeder"
|
||||||
import { createDefaultRuleTypes } from "../../../helpers/create-default-rule-types"
|
import { createDefaultRuleTypes } from "../../../helpers/create-default-rule-types"
|
||||||
|
|
||||||
@@ -21,7 +23,7 @@ const env = {
|
|||||||
MEDUSA_FF_MEDUSA_V2: true,
|
MEDUSA_FF_MEDUSA_V2: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
describe("[Product & Pricing Module] POST /admin/products", () => {
|
describe("POST /admin/products", () => {
|
||||||
let dbConnection
|
let dbConnection
|
||||||
let appContainer
|
let appContainer
|
||||||
let shutdownServer
|
let shutdownServer
|
||||||
@@ -50,6 +52,8 @@ describe("[Product & Pricing Module] POST /admin/products", () => {
|
|||||||
currency_code: "usd",
|
currency_code: "usd",
|
||||||
tax_rate: 0,
|
tax_rate: 0,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
await simpleSalesChannelFactory(dbConnection, { is_default: true })
|
||||||
})
|
})
|
||||||
|
|
||||||
afterEach(async () => {
|
afterEach(async () => {
|
||||||
@@ -110,5 +114,12 @@ describe("[Product & Pricing Module] POST /admin/products", () => {
|
|||||||
]),
|
]),
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const pricingModuleService: IPricingModuleService = appContainer.resolve(
|
||||||
|
"pricingModuleService"
|
||||||
|
)
|
||||||
|
|
||||||
|
const [_, count] = await pricingModuleService.listAndCount()
|
||||||
|
expect(count).toEqual(1)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -441,6 +441,7 @@ describe("/admin/products", () => {
|
|||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await productSeeder(dbConnection)
|
await productSeeder(dbConnection)
|
||||||
await adminSeeder(dbConnection)
|
await adminSeeder(dbConnection)
|
||||||
|
await createDefaultRuleTypes(medusaContainer)
|
||||||
|
|
||||||
await simpleSalesChannelFactory(dbConnection, {
|
await simpleSalesChannelFactory(dbConnection, {
|
||||||
name: "Default channel",
|
name: "Default channel",
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ const env = {
|
|||||||
MEDUSA_FF_MEDUSA_V2: true,
|
MEDUSA_FF_MEDUSA_V2: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
describe("[Product & Pricing Module] POST /admin/products/:id/variants/:id", () => {
|
describe("POST /admin/products/:id/variants/:id", () => {
|
||||||
let dbConnection
|
let dbConnection
|
||||||
let appContainer
|
let appContainer
|
||||||
let shutdownServer
|
let shutdownServer
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ const env = {
|
|||||||
MEDUSA_FF_MEDUSA_V2: true,
|
MEDUSA_FF_MEDUSA_V2: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
describe("[Product & Pricing Module] POST /admin/products/:id", () => {
|
describe("POST /admin/products/:id", () => {
|
||||||
let dbConnection
|
let dbConnection
|
||||||
let appContainer
|
let appContainer
|
||||||
let shutdownServer
|
let shutdownServer
|
||||||
@@ -109,10 +109,11 @@ describe("[Product & Pricing Module] POST /admin/products/:id", () => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
expect(response.status).toEqual(200)
|
expect(response.status).toEqual(200)
|
||||||
|
expect(response.data.product.variants).toHaveLength(1)
|
||||||
expect(response.data.product).toEqual(
|
expect(response.data.product).toEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
id: expect.any(String),
|
id: expect.any(String),
|
||||||
variants: expect.arrayContaining([
|
variants: [
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
id: variant.id,
|
id: variant.id,
|
||||||
title: "test variant update",
|
title: "test variant update",
|
||||||
@@ -128,7 +129,7 @@ describe("[Product & Pricing Module] POST /admin/products/:id", () => {
|
|||||||
}),
|
}),
|
||||||
]),
|
]),
|
||||||
}),
|
}),
|
||||||
]),
|
],
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -10,5 +10,9 @@ export const createDefaultRuleTypes = async (container) => {
|
|||||||
name: "region_id",
|
name: "region_id",
|
||||||
rule_attribute: "region_id",
|
rule_attribute: "region_id",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "customer_group_id",
|
||||||
|
rule_attribute: "customer_group_id",
|
||||||
|
},
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
@@ -1,12 +1,15 @@
|
|||||||
|
import { MedusaV2Flag } from "@medusajs/utils"
|
||||||
|
import { updatePriceLists } from "@medusajs/workflows"
|
||||||
|
import { Type } from "class-transformer"
|
||||||
import { IsArray, IsBoolean, IsOptional, ValidateNested } from "class-validator"
|
import { IsArray, IsBoolean, IsOptional, ValidateNested } from "class-validator"
|
||||||
|
import { EntityManager } from "typeorm"
|
||||||
import { defaultAdminPriceListFields, defaultAdminPriceListRelations } from "."
|
import { defaultAdminPriceListFields, defaultAdminPriceListRelations } from "."
|
||||||
|
|
||||||
import { AdminPriceListPricesUpdateReq } from "../../../../types/price-list"
|
|
||||||
import { PriceList } from "../../../.."
|
import { PriceList } from "../../../.."
|
||||||
import PriceListService from "../../../../services/price-list"
|
import PriceListService from "../../../../services/price-list"
|
||||||
import { Type } from "class-transformer"
|
import { AdminPriceListPricesUpdateReq } from "../../../../types/price-list"
|
||||||
import { validator } from "../../../../utils/validator"
|
import { validator } from "../../../../utils/validator"
|
||||||
import { EntityManager } from "typeorm"
|
import { MedusaContainer } from "@medusajs/types"
|
||||||
|
import { getPriceListPricingModule } from "./modules-queries"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @oas [post] /admin/price-lists/{id}/prices/batch
|
* @oas [post] /admin/price-lists/{id}/prices/batch
|
||||||
@@ -85,23 +88,48 @@ import { EntityManager } from "typeorm"
|
|||||||
*/
|
*/
|
||||||
export default async (req, res) => {
|
export default async (req, res) => {
|
||||||
const { id } = req.params
|
const { id } = req.params
|
||||||
|
let priceList
|
||||||
const validated = await validator(AdminPostPriceListPricesPricesReq, req.body)
|
const featureFlagRouter = req.scope.resolve("featureFlagRouter")
|
||||||
|
const manager: EntityManager = req.scope.resolve("manager")
|
||||||
const priceListService: PriceListService =
|
const priceListService: PriceListService =
|
||||||
req.scope.resolve("priceListService")
|
req.scope.resolve("priceListService")
|
||||||
|
|
||||||
const manager: EntityManager = req.scope.resolve("manager")
|
const validated = await validator(AdminPostPriceListPricesPricesReq, req.body)
|
||||||
await manager.transaction(async (transactionManager) => {
|
|
||||||
return await priceListService
|
|
||||||
.withTransaction(transactionManager)
|
|
||||||
.addPrices(id, validated.prices, validated.override)
|
|
||||||
})
|
|
||||||
|
|
||||||
const priceList = await priceListService.retrieve(id, {
|
if (featureFlagRouter.isFeatureEnabled(MedusaV2Flag.key)) {
|
||||||
select: defaultAdminPriceListFields as (keyof PriceList)[],
|
const updatePriceListWorkflow = updatePriceLists(req.scope)
|
||||||
relations: defaultAdminPriceListRelations,
|
|
||||||
})
|
const input = {
|
||||||
|
price_lists: [
|
||||||
|
{
|
||||||
|
id,
|
||||||
|
...validated,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
await updatePriceListWorkflow.run({
|
||||||
|
input,
|
||||||
|
context: {
|
||||||
|
manager,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
priceList = await getPriceListPricingModule(id, {
|
||||||
|
container: req.scope as MedusaContainer,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
await manager.transaction(async (transactionManager) => {
|
||||||
|
await priceListService
|
||||||
|
.withTransaction(transactionManager)
|
||||||
|
.addPrices(id, validated.prices, validated.override)
|
||||||
|
})
|
||||||
|
|
||||||
|
priceList = await priceListService.retrieve(id, {
|
||||||
|
select: defaultAdminPriceListFields as (keyof PriceList)[],
|
||||||
|
relations: defaultAdminPriceListRelations,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
res.json({ price_list: priceList })
|
res.json({ price_list: priceList })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,12 @@
|
|||||||
|
import { MedusaContainer, PricingTypes, WorkflowTypes } from "@medusajs/types"
|
||||||
|
import {
|
||||||
|
FlagRouter,
|
||||||
|
MedusaV2Flag,
|
||||||
|
PriceListStatus,
|
||||||
|
PriceListType,
|
||||||
|
} from "@medusajs/utils"
|
||||||
|
import { createPriceLists } from "@medusajs/workflows"
|
||||||
|
import { Type } from "class-transformer"
|
||||||
import {
|
import {
|
||||||
IsArray,
|
IsArray,
|
||||||
IsBoolean,
|
IsBoolean,
|
||||||
@@ -6,23 +15,18 @@ import {
|
|||||||
IsString,
|
IsString,
|
||||||
ValidateNested,
|
ValidateNested,
|
||||||
} from "class-validator"
|
} from "class-validator"
|
||||||
|
import { Request } from "express"
|
||||||
|
import { EntityManager } from "typeorm"
|
||||||
|
import { defaultAdminPriceListFields, defaultAdminPriceListRelations } from "."
|
||||||
|
import TaxInclusivePricingFeatureFlag from "../../../../loaders/feature-flags/tax-inclusive-pricing"
|
||||||
|
import { PriceList } from "../../../../models"
|
||||||
|
import PriceListService from "../../../../services/price-list"
|
||||||
import {
|
import {
|
||||||
AdminPriceListPricesCreateReq,
|
AdminPriceListPricesCreateReq,
|
||||||
CreatePriceListInput,
|
CreatePriceListInput,
|
||||||
} from "../../../../types/price-list"
|
} from "../../../../types/price-list"
|
||||||
|
|
||||||
import { PriceListStatus, PriceListType } from "@medusajs/utils"
|
|
||||||
import { Type } from "class-transformer"
|
|
||||||
import { Request } from "express"
|
|
||||||
import { EntityManager } from "typeorm"
|
|
||||||
import TaxInclusivePricingFeatureFlag from "../../../../loaders/feature-flags/tax-inclusive-pricing"
|
|
||||||
import { PriceList } from "../../../../models"
|
|
||||||
import PriceListService from "../../../../services/price-list"
|
|
||||||
import { FeatureFlagDecorators } from "../../../../utils/feature-flag-decorators"
|
import { FeatureFlagDecorators } from "../../../../utils/feature-flag-decorators"
|
||||||
import {
|
import { getPriceListPricingModule } from "./modules-queries"
|
||||||
defaultAdminPriceListFields,
|
|
||||||
defaultAdminPriceListRelations,
|
|
||||||
} from "./index"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @oas [post] /admin/price-lists
|
* @oas [post] /admin/price-lists
|
||||||
@@ -109,16 +113,57 @@ export default async (req: Request, res) => {
|
|||||||
req.scope.resolve("priceListService")
|
req.scope.resolve("priceListService")
|
||||||
|
|
||||||
const manager: EntityManager = req.scope.resolve("manager")
|
const manager: EntityManager = req.scope.resolve("manager")
|
||||||
let priceList = await manager.transaction(async (transactionManager) => {
|
const featureFlagRouter: FlagRouter = req.scope.resolve("featureFlagRouter")
|
||||||
return await priceListService
|
let priceList
|
||||||
.withTransaction(transactionManager)
|
|
||||||
.create(req.validatedBody as CreatePriceListInput)
|
|
||||||
})
|
|
||||||
|
|
||||||
priceList = await priceListService.retrieve(priceList.id, {
|
const isMedusaV2FlagEnabled = featureFlagRouter.isFeatureEnabled(
|
||||||
select: defaultAdminPriceListFields as (keyof PriceList)[],
|
MedusaV2Flag.key
|
||||||
relations: defaultAdminPriceListRelations,
|
)
|
||||||
})
|
|
||||||
|
if (isMedusaV2FlagEnabled) {
|
||||||
|
const createPriceListWorkflow = createPriceLists(req.scope)
|
||||||
|
const validatedInput = req.validatedBody as CreatePriceListInput
|
||||||
|
const rules: PricingTypes.CreatePriceListRules = {}
|
||||||
|
const customerGroups = validatedInput?.customer_groups || []
|
||||||
|
delete validatedInput.customer_groups
|
||||||
|
|
||||||
|
if (customerGroups.length) {
|
||||||
|
rules["customer_group_id"] = customerGroups.map((cg) => cg.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
const input = {
|
||||||
|
price_lists: [
|
||||||
|
{
|
||||||
|
...validatedInput,
|
||||||
|
rules,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
} as WorkflowTypes.PriceListWorkflow.CreatePriceListWorkflowInputDTO
|
||||||
|
|
||||||
|
const { result } = await createPriceListWorkflow.run({
|
||||||
|
input,
|
||||||
|
context: {
|
||||||
|
manager,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
priceList = result[0]!.priceList
|
||||||
|
|
||||||
|
priceList = await getPriceListPricingModule(priceList.id, {
|
||||||
|
container: req.scope as MedusaContainer,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
priceList = await manager.transaction(async (transactionManager) => {
|
||||||
|
return await priceListService
|
||||||
|
.withTransaction(transactionManager)
|
||||||
|
.create(req.validatedBody as CreatePriceListInput)
|
||||||
|
})
|
||||||
|
|
||||||
|
priceList = await priceListService.retrieve(priceList.id, {
|
||||||
|
select: defaultAdminPriceListFields as (keyof PriceList)[],
|
||||||
|
relations: defaultAdminPriceListRelations,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
res.json({ price_list: priceList })
|
res.json({ price_list: priceList })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
import { WorkflowTypes } from "@medusajs/types"
|
||||||
|
import { FlagRouter, MedusaV2Flag } from "@medusajs/utils"
|
||||||
|
import { removePriceLists } from "@medusajs/workflows"
|
||||||
import { EntityManager } from "typeorm"
|
import { EntityManager } from "typeorm"
|
||||||
import PriceListService from "../../../../services/price-list"
|
import PriceListService from "../../../../services/price-list"
|
||||||
|
|
||||||
@@ -56,12 +59,33 @@ import PriceListService from "../../../../services/price-list"
|
|||||||
export default async (req, res) => {
|
export default async (req, res) => {
|
||||||
const { id } = req.params
|
const { id } = req.params
|
||||||
|
|
||||||
const priceListService: PriceListService =
|
const featureFlagRouter: FlagRouter = req.scope.resolve("featureFlagRouter")
|
||||||
req.scope.resolve("priceListService")
|
|
||||||
const manager: EntityManager = req.scope.resolve("manager")
|
const manager: EntityManager = req.scope.resolve("manager")
|
||||||
await manager.transaction(async (transactionManager) => {
|
|
||||||
return await priceListService.withTransaction(transactionManager).delete(id)
|
const isMedusaV2FlagEnabled = featureFlagRouter.isFeatureEnabled(
|
||||||
})
|
MedusaV2Flag.key
|
||||||
|
)
|
||||||
|
|
||||||
|
if (isMedusaV2FlagEnabled) {
|
||||||
|
const removePriceListsWorkflow = removePriceLists(req.scope)
|
||||||
|
|
||||||
|
const input = {
|
||||||
|
price_lists: [id],
|
||||||
|
} as WorkflowTypes.PriceListWorkflow.RemovePriceListWorkflowInputDTO
|
||||||
|
|
||||||
|
await removePriceListsWorkflow.run({
|
||||||
|
input,
|
||||||
|
context: {
|
||||||
|
manager,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
const priceListService: PriceListService =
|
||||||
|
req.scope.resolve("priceListService")
|
||||||
|
await manager.transaction(async (transactionManager) => {
|
||||||
|
await priceListService.withTransaction(transactionManager).delete(id)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
id,
|
id,
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
|
import { FlagRouter, MedusaV2Flag } from "@medusajs/utils"
|
||||||
|
import { removePriceListProductPrices } from "@medusajs/workflows/dist/definition/price-list/remove-price-list-prices"
|
||||||
import { ArrayNotEmpty, IsString } from "class-validator"
|
import { ArrayNotEmpty, IsString } from "class-validator"
|
||||||
|
|
||||||
import { EntityManager } from "typeorm"
|
import { EntityManager } from "typeorm"
|
||||||
import PriceListService from "../../../../services/price-list"
|
import PriceListService from "../../../../services/price-list"
|
||||||
import { validator } from "../../../../utils/validator"
|
import { validator } from "../../../../utils/validator"
|
||||||
|
import { WorkflowTypes } from "@medusajs/types"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @oas [delete] /admin/price-lists/{id}/prices/batch
|
* @oas [delete] /admin/price-lists/{id}/prices/batch
|
||||||
@@ -81,13 +83,37 @@ export default async (req, res) => {
|
|||||||
|
|
||||||
const priceListService: PriceListService =
|
const priceListService: PriceListService =
|
||||||
req.scope.resolve("priceListService")
|
req.scope.resolve("priceListService")
|
||||||
|
const featureFlagRouter: FlagRouter = req.scope.resolve("featureFlagRouter")
|
||||||
|
|
||||||
const manager: EntityManager = req.scope.resolve("manager")
|
const manager: EntityManager = req.scope.resolve("manager")
|
||||||
await manager.transaction(async (transactionManager) => {
|
|
||||||
return await priceListService
|
const isMedusaV2FlagEnabled = featureFlagRouter.isFeatureEnabled(
|
||||||
.withTransaction(transactionManager)
|
MedusaV2Flag.key
|
||||||
.deletePrices(id, validated.price_ids)
|
)
|
||||||
})
|
|
||||||
|
if (isMedusaV2FlagEnabled) {
|
||||||
|
const deletePriceListPricesWorkflow = removePriceListProductPrices(
|
||||||
|
req.scope
|
||||||
|
)
|
||||||
|
|
||||||
|
const input = {
|
||||||
|
price_list_id: id,
|
||||||
|
money_amount_ids: validated.price_ids,
|
||||||
|
} as WorkflowTypes.PriceListWorkflow.RemovePriceListPricesWorkflowInputDTO
|
||||||
|
|
||||||
|
await deletePriceListPricesWorkflow.run({
|
||||||
|
input,
|
||||||
|
context: {
|
||||||
|
manager,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
await manager.transaction(async (transactionManager) => {
|
||||||
|
await priceListService
|
||||||
|
.withTransaction(transactionManager)
|
||||||
|
.deletePrices(id, validated.price_ids)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
res.json({ ids: validated.price_ids, object: "money-amount", deleted: true })
|
res.json({ ids: validated.price_ids, object: "money-amount", deleted: true })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
import { WorkflowTypes } from "@medusajs/types"
|
||||||
|
import { FlagRouter, MedusaV2Flag } from "@medusajs/utils"
|
||||||
|
import { removePriceListProductPrices } from "@medusajs/workflows/dist/definition/price-list/remove-product-prices"
|
||||||
import { EntityManager } from "typeorm"
|
import { EntityManager } from "typeorm"
|
||||||
import PriceListService from "../../../../services/price-list"
|
import PriceListService from "../../../../services/price-list"
|
||||||
|
|
||||||
@@ -60,15 +63,44 @@ export default async (req, res) => {
|
|||||||
const priceListService: PriceListService =
|
const priceListService: PriceListService =
|
||||||
req.scope.resolve("priceListService")
|
req.scope.resolve("priceListService")
|
||||||
|
|
||||||
|
const featureFlagRouter: FlagRouter = req.scope.resolve("featureFlagRouter")
|
||||||
const manager: EntityManager = req.scope.resolve("manager")
|
const manager: EntityManager = req.scope.resolve("manager")
|
||||||
const [deletedPriceIds] = await manager.transaction(
|
|
||||||
async (transactionManager) => {
|
const isMedusaV2FlagEnabled = featureFlagRouter.isFeatureEnabled(
|
||||||
return await priceListService
|
MedusaV2Flag.key
|
||||||
.withTransaction(transactionManager)
|
|
||||||
.deleteProductPrices(id, [product_id])
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
let deletedPriceIds: string[] = []
|
||||||
|
|
||||||
|
if (isMedusaV2FlagEnabled) {
|
||||||
|
const deletePriceListProductsWorkflow = removePriceListProductPrices(
|
||||||
|
req.scope
|
||||||
|
)
|
||||||
|
|
||||||
|
const input = {
|
||||||
|
product_ids: [product_id],
|
||||||
|
price_list_id: id,
|
||||||
|
} as WorkflowTypes.PriceListWorkflow.RemovePriceListProductsWorkflowInputDTO
|
||||||
|
|
||||||
|
const { result } = await deletePriceListProductsWorkflow.run({
|
||||||
|
input,
|
||||||
|
context: {
|
||||||
|
manager,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
deletedPriceIds = result
|
||||||
|
} else {
|
||||||
|
const [deletedIds] = await manager.transaction(
|
||||||
|
async (transactionManager) => {
|
||||||
|
return await priceListService
|
||||||
|
.withTransaction(transactionManager)
|
||||||
|
.deleteProductPrices(id, [product_id])
|
||||||
|
}
|
||||||
|
)
|
||||||
|
deletedPriceIds = deletedIds
|
||||||
|
}
|
||||||
|
|
||||||
return res.json({
|
return res.json({
|
||||||
ids: deletedPriceIds,
|
ids: deletedPriceIds,
|
||||||
object: "money-amount",
|
object: "money-amount",
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
|
import { FlagRouter, MedusaV2Flag } from "@medusajs/utils"
|
||||||
|
import { removePriceListProductPrices } from "@medusajs/workflows"
|
||||||
import { ArrayNotEmpty, IsString } from "class-validator"
|
import { ArrayNotEmpty, IsString } from "class-validator"
|
||||||
import { Request, Response } from "express"
|
import { Request, Response } from "express"
|
||||||
import { EntityManager } from "typeorm"
|
import { EntityManager } from "typeorm"
|
||||||
import PriceListService from "../../../../services/price-list"
|
import PriceListService from "../../../../services/price-list"
|
||||||
import { validator } from "../../../../utils/validator"
|
import { validator } from "../../../../utils/validator"
|
||||||
|
import { WorkflowTypes } from "@medusajs/types"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @oas [delete] /admin/price-lists/{id}/products/prices/batch
|
* @oas [delete] /admin/price-lists/{id}/products/prices/batch
|
||||||
@@ -79,15 +82,44 @@ export default async (req: Request, res: Response) => {
|
|||||||
const priceListService: PriceListService =
|
const priceListService: PriceListService =
|
||||||
req.scope.resolve("priceListService")
|
req.scope.resolve("priceListService")
|
||||||
|
|
||||||
|
const featureFlagRouter: FlagRouter = req.scope.resolve("featureFlagRouter")
|
||||||
const manager: EntityManager = req.scope.resolve("manager")
|
const manager: EntityManager = req.scope.resolve("manager")
|
||||||
const [deletedPriceIds] = await manager.transaction(
|
|
||||||
async (transactionManager) => {
|
const isMedusaV2FlagEnabled = featureFlagRouter.isFeatureEnabled(
|
||||||
return await priceListService
|
MedusaV2Flag.key
|
||||||
.withTransaction(transactionManager)
|
|
||||||
.deleteProductPrices(id, validated.product_ids)
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
let deletedPriceIds: string[] = []
|
||||||
|
|
||||||
|
if (isMedusaV2FlagEnabled) {
|
||||||
|
const deletePriceListProductsWorkflow = removePriceListProductPrices(
|
||||||
|
req.scope
|
||||||
|
)
|
||||||
|
|
||||||
|
const input = {
|
||||||
|
product_ids: validated.product_ids,
|
||||||
|
price_list_id: id,
|
||||||
|
} as WorkflowTypes.PriceListWorkflow.RemovePriceListProductsWorkflowInputDTO
|
||||||
|
|
||||||
|
const { result } = await deletePriceListProductsWorkflow.run({
|
||||||
|
input,
|
||||||
|
context: {
|
||||||
|
manager,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
deletedPriceIds = result
|
||||||
|
} else {
|
||||||
|
const [deletedIds] = await manager.transaction(
|
||||||
|
async (transactionManager) => {
|
||||||
|
return await priceListService
|
||||||
|
.withTransaction(transactionManager)
|
||||||
|
.deleteProductPrices(id, validated.product_ids)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
deletedPriceIds = deletedIds
|
||||||
|
}
|
||||||
|
|
||||||
return res.json({
|
return res.json({
|
||||||
ids: deletedPriceIds,
|
ids: deletedPriceIds,
|
||||||
object: "money-amount",
|
object: "money-amount",
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
|
import { FlagRouter, MedusaV2Flag } from "@medusajs/utils"
|
||||||
|
import { removePriceListVariantPrices } from "@medusajs/workflows"
|
||||||
import { EntityManager } from "typeorm"
|
import { EntityManager } from "typeorm"
|
||||||
import PriceListService from "../../../../services/price-list"
|
import PriceListService from "../../../../services/price-list"
|
||||||
|
import { WorkflowTypes } from "@medusajs/types"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @oas [delete] /admin/price-lists/{id}/variants/{variant_id}/prices
|
* @oas [delete] /admin/price-lists/{id}/variants/{variant_id}/prices
|
||||||
@@ -59,16 +62,44 @@ export default async (req, res) => {
|
|||||||
|
|
||||||
const priceListService: PriceListService =
|
const priceListService: PriceListService =
|
||||||
req.scope.resolve("priceListService")
|
req.scope.resolve("priceListService")
|
||||||
|
const featureFlagRouter: FlagRouter = req.scope.resolve("featureFlagRouter")
|
||||||
const manager: EntityManager = req.scope.resolve("manager")
|
const manager: EntityManager = req.scope.resolve("manager")
|
||||||
const [deletedPriceIds] = await manager.transaction(
|
|
||||||
async (transactionManager) => {
|
const isMedusaV2FlagEnabled = featureFlagRouter.isFeatureEnabled(
|
||||||
return await priceListService
|
MedusaV2Flag.key
|
||||||
.withTransaction(transactionManager)
|
|
||||||
.deleteVariantPrices(id, [variant_id])
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
let deletedPriceIds: string[] = []
|
||||||
|
|
||||||
|
if (isMedusaV2FlagEnabled) {
|
||||||
|
const deletePriceListProductsWorkflow = removePriceListVariantPrices(
|
||||||
|
req.scope
|
||||||
|
)
|
||||||
|
|
||||||
|
const input = {
|
||||||
|
variant_ids: [variant_id],
|
||||||
|
price_list_id: id,
|
||||||
|
} as WorkflowTypes.PriceListWorkflow.RemovePriceListVariantsWorkflowInputDTO
|
||||||
|
|
||||||
|
const { result } = await deletePriceListProductsWorkflow.run({
|
||||||
|
input,
|
||||||
|
context: {
|
||||||
|
manager,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
deletedPriceIds = result
|
||||||
|
} else {
|
||||||
|
const [deletedIds] = await manager.transaction(
|
||||||
|
async (transactionManager) => {
|
||||||
|
return await priceListService
|
||||||
|
.withTransaction(transactionManager)
|
||||||
|
.deleteVariantPrices(id, [variant_id])
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
deletedPriceIds = deletedIds
|
||||||
|
}
|
||||||
|
|
||||||
return res.json({
|
return res.json({
|
||||||
ids: deletedPriceIds,
|
ids: deletedPriceIds,
|
||||||
object: "money-amount",
|
object: "money-amount",
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
|
import { MedusaContainer } from "@medusajs/types"
|
||||||
|
import { FlagRouter, MedusaV2Flag } from "@medusajs/utils"
|
||||||
import { defaultAdminPriceListFields, defaultAdminPriceListRelations } from "."
|
import { defaultAdminPriceListFields, defaultAdminPriceListRelations } from "."
|
||||||
|
|
||||||
import { PriceList } from "../../../.."
|
import { PriceList } from "../../../.."
|
||||||
import PriceListService from "../../../../services/price-list"
|
import PriceListService from "../../../../services/price-list"
|
||||||
|
import { getPriceListPricingModule } from "./modules-queries"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @oas [get] /admin/price-lists/{id}
|
* @oas [get] /admin/price-lists/{id}
|
||||||
@@ -58,13 +60,22 @@ import PriceListService from "../../../../services/price-list"
|
|||||||
export default async (req, res) => {
|
export default async (req, res) => {
|
||||||
const { id } = req.params
|
const { id } = req.params
|
||||||
|
|
||||||
|
const featureFlagRouter: FlagRouter = req.scope.resolve("featureFlagRouter")
|
||||||
const priceListService: PriceListService =
|
const priceListService: PriceListService =
|
||||||
req.scope.resolve("priceListService")
|
req.scope.resolve("priceListService")
|
||||||
|
|
||||||
const priceList = await priceListService.retrieve(id, {
|
let priceList
|
||||||
select: defaultAdminPriceListFields as (keyof PriceList)[],
|
|
||||||
relations: defaultAdminPriceListRelations,
|
if (featureFlagRouter.isFeatureEnabled(MedusaV2Flag.key)) {
|
||||||
})
|
priceList = await getPriceListPricingModule(id, {
|
||||||
|
container: req.scope as MedusaContainer,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
priceList = await priceListService.retrieve(id, {
|
||||||
|
select: defaultAdminPriceListFields as (keyof PriceList)[],
|
||||||
|
relations: defaultAdminPriceListRelations,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
res.status(200).json({ price_list: priceList })
|
res.status(200).json({ price_list: priceList })
|
||||||
}
|
}
|
||||||
@@ -9,6 +9,7 @@ import middlewares, {
|
|||||||
import {
|
import {
|
||||||
defaultAdminProductFields,
|
defaultAdminProductFields,
|
||||||
defaultAdminProductRelations,
|
defaultAdminProductRelations,
|
||||||
|
defaultAdminProductRemoteQueryObject,
|
||||||
} from "../products"
|
} from "../products"
|
||||||
|
|
||||||
import { FlagRouter } from "@medusajs/utils"
|
import { FlagRouter } from "@medusajs/utils"
|
||||||
@@ -86,6 +87,50 @@ export default (app, featureFlagRouter: FlagRouter) => {
|
|||||||
return app
|
return app
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const defaultAdminPriceListRemoteQueryObject = {
|
||||||
|
fields: [
|
||||||
|
"created_at",
|
||||||
|
"deleted_at",
|
||||||
|
"description",
|
||||||
|
"ends_at",
|
||||||
|
"id",
|
||||||
|
"title",
|
||||||
|
"starts_at",
|
||||||
|
"status",
|
||||||
|
"type",
|
||||||
|
"updated_at",
|
||||||
|
],
|
||||||
|
price_list_rules: {
|
||||||
|
price_list_rule_values: {
|
||||||
|
fields: ["value"],
|
||||||
|
},
|
||||||
|
rule_type: {
|
||||||
|
fields: ["rule_attribute"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
price_set_money_amounts: {
|
||||||
|
money_amount: {
|
||||||
|
fields: [
|
||||||
|
"id",
|
||||||
|
"currency_code",
|
||||||
|
"amount",
|
||||||
|
"min_quantity",
|
||||||
|
"max_quantity",
|
||||||
|
"created_at",
|
||||||
|
"deleted_at",
|
||||||
|
"updated_at",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
price_set: {
|
||||||
|
variant_link: {
|
||||||
|
variant: {
|
||||||
|
fields: defaultAdminProductRemoteQueryObject.variants.fields,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
export const defaultAdminPriceListFields = [
|
export const defaultAdminPriceListFields = [
|
||||||
"id",
|
"id",
|
||||||
"name",
|
"name",
|
||||||
|
|||||||
@@ -1,7 +1,3 @@
|
|||||||
import {
|
|
||||||
DateComparisonOperator,
|
|
||||||
extendedFindParamsMixin,
|
|
||||||
} from "../../../../types/common"
|
|
||||||
import {
|
import {
|
||||||
IsArray,
|
IsArray,
|
||||||
IsBoolean,
|
IsBoolean,
|
||||||
@@ -10,14 +6,20 @@ import {
|
|||||||
IsString,
|
IsString,
|
||||||
ValidateNested,
|
ValidateNested,
|
||||||
} from "class-validator"
|
} from "class-validator"
|
||||||
import { MedusaError, isDefined } from "medusa-core-utils"
|
import { isDefined } from "medusa-core-utils"
|
||||||
|
import {
|
||||||
|
DateComparisonOperator,
|
||||||
|
extendedFindParamsMixin,
|
||||||
|
} from "../../../../types/common"
|
||||||
|
|
||||||
import { FilterableProductProps } from "../../../../types/product"
|
import { FlagRouter, MedusaV2Flag } from "@medusajs/utils"
|
||||||
import PriceListService from "../../../../services/price-list"
|
|
||||||
import { ProductStatus } from "../../../../models"
|
|
||||||
import { Request } from "express"
|
|
||||||
import { Type } from "class-transformer"
|
import { Type } from "class-transformer"
|
||||||
|
import { Request } from "express"
|
||||||
import { pickBy } from "lodash"
|
import { pickBy } from "lodash"
|
||||||
|
import { ProductStatus } from "../../../../models"
|
||||||
|
import PriceListService from "../../../../services/price-list"
|
||||||
|
import { FilterableProductProps } from "../../../../types/product"
|
||||||
|
import { listAndCountProductWithIsolatedProductModule } from "../products/list-products"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @oas [get] /admin/price-lists/{id}/products
|
* @oas [get] /admin/price-lists/{id}/products
|
||||||
@@ -181,6 +183,9 @@ import { pickBy } from "lodash"
|
|||||||
export default async (req: Request, res) => {
|
export default async (req: Request, res) => {
|
||||||
const { id } = req.params
|
const { id } = req.params
|
||||||
const { offset, limit } = req.validatedQuery
|
const { offset, limit } = req.validatedQuery
|
||||||
|
const featureFlagRouter: FlagRouter = req.scope.resolve("featureFlagRouter")
|
||||||
|
let products
|
||||||
|
let count
|
||||||
|
|
||||||
const priceListService: PriceListService =
|
const priceListService: PriceListService =
|
||||||
req.scope.resolve("priceListService")
|
req.scope.resolve("priceListService")
|
||||||
@@ -190,11 +195,19 @@ export default async (req: Request, res) => {
|
|||||||
price_list_id: [id],
|
price_list_id: [id],
|
||||||
}
|
}
|
||||||
|
|
||||||
const [products, count] = await priceListService.listProducts(
|
if (featureFlagRouter.isFeatureEnabled(MedusaV2Flag.key)) {
|
||||||
id,
|
;[products, count] = await listAndCountProductWithIsolatedProductModule(
|
||||||
pickBy(filterableFields, (val) => isDefined(val)),
|
req,
|
||||||
req.listConfig
|
filterableFields,
|
||||||
)
|
req.listConfig
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
;[products, count] = await priceListService.listProducts(
|
||||||
|
id,
|
||||||
|
pickBy(filterableFields, (val) => isDefined(val)),
|
||||||
|
req.listConfig
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
products,
|
products,
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
import { IsNumber, IsOptional, IsString } from "class-validator"
|
import { FlagRouter, MedusaV2Flag } from "@medusajs/utils"
|
||||||
|
|
||||||
import { FilterablePriceListProps } from "../../../../types/price-list"
|
|
||||||
import PriceListService from "../../../../services/price-list"
|
|
||||||
import { Request } from "express"
|
|
||||||
import { Type } from "class-transformer"
|
import { Type } from "class-transformer"
|
||||||
|
import { IsNumber, IsOptional, IsString } from "class-validator"
|
||||||
|
import { Request } from "express"
|
||||||
|
import PriceListService from "../../../../services/price-list"
|
||||||
|
import { FilterablePriceListProps } from "../../../../types/price-list"
|
||||||
|
import { MedusaContainer } from "@medusajs/types"
|
||||||
|
import { listAndCountPriceListPricingModule } from "./modules-queries"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @oas [get] /admin/price-lists
|
* @oas [get] /admin/price-lists
|
||||||
@@ -161,18 +163,30 @@ import { Type } from "class-transformer"
|
|||||||
* $ref: "#/components/responses/500_error"
|
* $ref: "#/components/responses/500_error"
|
||||||
*/
|
*/
|
||||||
export default async (req: Request, res) => {
|
export default async (req: Request, res) => {
|
||||||
|
const featureFlagRouter: FlagRouter = req.scope.resolve("featureFlagRouter")
|
||||||
|
|
||||||
const validated = req.validatedQuery
|
const validated = req.validatedQuery
|
||||||
|
let priceLists
|
||||||
|
let count
|
||||||
|
|
||||||
const priceListService: PriceListService =
|
if (featureFlagRouter.isFeatureEnabled(MedusaV2Flag.key)) {
|
||||||
req.scope.resolve("priceListService")
|
;[priceLists, count] = await listAndCountPriceListPricingModule({
|
||||||
|
filters: req.filterableFields,
|
||||||
|
listConfig: req.listConfig,
|
||||||
|
container: req.scope as MedusaContainer,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
const priceListService: PriceListService =
|
||||||
|
req.scope.resolve("priceListService")
|
||||||
|
|
||||||
const [price_lists, count] = await priceListService.listAndCount(
|
;[priceLists, count] = await priceListService.listAndCount(
|
||||||
req.filterableFields,
|
req.filterableFields,
|
||||||
req.listConfig
|
req.listConfig
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
price_lists,
|
price_lists: priceLists,
|
||||||
count,
|
count,
|
||||||
offset: validated.offset,
|
offset: validated.offset,
|
||||||
limit: validated.limit,
|
limit: validated.limit,
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import { MedusaContainer } from "@medusajs/types"
|
||||||
|
import { PriceList } from "../../../../../models"
|
||||||
|
import { MedusaError } from "medusa-core-utils"
|
||||||
|
import { listAndCountPriceListPricingModule } from "./list-and-count-price-lists"
|
||||||
|
|
||||||
|
export async function getPriceListPricingModule(
|
||||||
|
id: string,
|
||||||
|
{
|
||||||
|
container,
|
||||||
|
}: {
|
||||||
|
container: MedusaContainer
|
||||||
|
}
|
||||||
|
): Promise<PriceList> {
|
||||||
|
const [priceLists, count] = await listAndCountPriceListPricingModule({
|
||||||
|
filters: {
|
||||||
|
id: [id],
|
||||||
|
},
|
||||||
|
container,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (count === 0) {
|
||||||
|
throw new MedusaError(
|
||||||
|
MedusaError.Types.NOT_FOUND,
|
||||||
|
`Price list with id: ${id} was not found`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return priceLists[0]
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
export * from "./get-price-list"
|
||||||
|
export * from "./list-and-count-price-lists"
|
||||||
+104
@@ -0,0 +1,104 @@
|
|||||||
|
import { FilterablePriceListProps, MedusaContainer } from "@medusajs/types"
|
||||||
|
import { FindConfig } from "../../../../../types/common"
|
||||||
|
import { CustomerGroup, MoneyAmount, PriceList } from "../../../../../models"
|
||||||
|
import { CustomerGroupService } from "../../../../../services"
|
||||||
|
import { defaultAdminPriceListRemoteQueryObject } from "../index"
|
||||||
|
|
||||||
|
export async function listAndCountPriceListPricingModule({
|
||||||
|
filters,
|
||||||
|
listConfig = { skip: 0 },
|
||||||
|
container,
|
||||||
|
}: {
|
||||||
|
container: MedusaContainer
|
||||||
|
filters?: FilterablePriceListProps
|
||||||
|
listConfig?: FindConfig<PriceList>
|
||||||
|
}): Promise<[PriceList[], number]> {
|
||||||
|
const remoteQuery = container.resolve("remoteQuery")
|
||||||
|
const customerGroupService: CustomerGroupService = container.resolve(
|
||||||
|
"customerGroupService"
|
||||||
|
)
|
||||||
|
|
||||||
|
const query = {
|
||||||
|
price_list: {
|
||||||
|
__args: { filters, ...listConfig },
|
||||||
|
...defaultAdminPriceListRemoteQueryObject,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const {
|
||||||
|
rows: priceLists,
|
||||||
|
metadata: { count },
|
||||||
|
} = await remoteQuery(query)
|
||||||
|
|
||||||
|
if (!count) {
|
||||||
|
return [[], 0]
|
||||||
|
}
|
||||||
|
|
||||||
|
const customerGroupIds: string[] = priceLists
|
||||||
|
.map((priceList) =>
|
||||||
|
priceList.price_list_rules
|
||||||
|
.filter((rule) => rule.rule_type.rule_attribute === "customer_group_id")
|
||||||
|
.map((rule) =>
|
||||||
|
rule.price_list_rule_values.map((rule_value) => rule_value.value)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.flat(2)
|
||||||
|
|
||||||
|
const priceListCustomerGroups = await customerGroupService.list(
|
||||||
|
{ id: customerGroupIds },
|
||||||
|
{}
|
||||||
|
)
|
||||||
|
|
||||||
|
const customerGroupIdCustomerGroupMap = new Map(
|
||||||
|
priceListCustomerGroups.map((customerGroup) => [
|
||||||
|
customerGroup.id,
|
||||||
|
customerGroup,
|
||||||
|
])
|
||||||
|
)
|
||||||
|
|
||||||
|
for (const priceList of priceLists) {
|
||||||
|
const priceSetMoneyAmounts = priceList.price_set_money_amounts || []
|
||||||
|
const priceListRulesData = priceList.price_list_rules || []
|
||||||
|
delete priceList.price_set_money_amounts
|
||||||
|
delete priceList.price_list_rules
|
||||||
|
|
||||||
|
priceList.prices = priceSetMoneyAmounts.map((priceSetMoneyAmount) => {
|
||||||
|
const productVariant = priceSetMoneyAmount.price_set.variant_link.variant
|
||||||
|
|
||||||
|
return {
|
||||||
|
...(priceSetMoneyAmount.money_amount as MoneyAmount),
|
||||||
|
price_list_id: priceList.id,
|
||||||
|
variant_id: productVariant?.id ?? null,
|
||||||
|
variant: productVariant ?? null,
|
||||||
|
region_id: null,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
priceList.name = priceList.title
|
||||||
|
delete priceList.title
|
||||||
|
|
||||||
|
const customerGroupPriceListRule = priceListRulesData.find(
|
||||||
|
(plr) => plr.rule_type.rule_attribute === "customer_group_id"
|
||||||
|
)
|
||||||
|
|
||||||
|
if (
|
||||||
|
customerGroupPriceListRule &&
|
||||||
|
customerGroupPriceListRule?.price_list_rule_values
|
||||||
|
) {
|
||||||
|
priceList.customer_groups =
|
||||||
|
customerGroupPriceListRule?.price_list_rule_values
|
||||||
|
.map((customerGroupRule) =>
|
||||||
|
customerGroupIdCustomerGroupMap.get(customerGroupRule.value)
|
||||||
|
)
|
||||||
|
.filter(
|
||||||
|
(
|
||||||
|
customerGroup: CustomerGroup | undefined
|
||||||
|
): customerGroup is CustomerGroup => !!customerGroup
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
priceList.customer_groups = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [priceLists, count]
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import { PriceListStatus, PriceListType } from "@medusajs/utils"
|
import { MedusaContainer, PricingTypes, WorkflowTypes } from "@medusajs/types"
|
||||||
|
import { MedusaV2Flag, PriceListStatus, PriceListType } from "@medusajs/utils"
|
||||||
import {
|
import {
|
||||||
IsArray,
|
IsArray,
|
||||||
IsBoolean,
|
IsBoolean,
|
||||||
@@ -8,15 +9,17 @@ import {
|
|||||||
ValidateNested,
|
ValidateNested,
|
||||||
} from "class-validator"
|
} from "class-validator"
|
||||||
import { defaultAdminPriceListFields, defaultAdminPriceListRelations } from "."
|
import { defaultAdminPriceListFields, defaultAdminPriceListRelations } from "."
|
||||||
import { AdminPriceListPricesUpdateReq } from "../../../../types/price-list"
|
|
||||||
|
|
||||||
|
import { updatePriceLists } from "@medusajs/workflows"
|
||||||
import { Type } from "class-transformer"
|
import { Type } from "class-transformer"
|
||||||
import { EntityManager } from "typeorm"
|
import { EntityManager } from "typeorm"
|
||||||
import { PriceList } from "../../../.."
|
import { PriceList } from "../../../.."
|
||||||
import TaxInclusivePricingFeatureFlag from "../../../../loaders/feature-flags/tax-inclusive-pricing"
|
import TaxInclusivePricingFeatureFlag from "../../../../loaders/feature-flags/tax-inclusive-pricing"
|
||||||
import PriceListService from "../../../../services/price-list"
|
import PriceListService from "../../../../services/price-list"
|
||||||
|
import { AdminPriceListPricesUpdateReq } from "../../../../types/price-list"
|
||||||
import { FeatureFlagDecorators } from "../../../../utils/feature-flag-decorators"
|
import { FeatureFlagDecorators } from "../../../../utils/feature-flag-decorators"
|
||||||
import { validator } from "../../../../utils/validator"
|
import { validator } from "../../../../utils/validator"
|
||||||
|
import { getPriceListPricingModule } from "./modules-queries"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @oas [post] /admin/price-lists/{id}
|
* @oas [post] /admin/price-lists/{id}
|
||||||
@@ -83,26 +86,59 @@ import { validator } from "../../../../utils/validator"
|
|||||||
*/
|
*/
|
||||||
export default async (req, res) => {
|
export default async (req, res) => {
|
||||||
const { id } = req.params
|
const { id } = req.params
|
||||||
|
let priceList
|
||||||
|
const featureFlagRouter = req.scope.resolve("featureFlagRouter")
|
||||||
|
const manager: EntityManager = req.scope.resolve("manager")
|
||||||
|
const priceListService: PriceListService =
|
||||||
|
req.scope.resolve("priceListService")
|
||||||
|
|
||||||
const validated = await validator(
|
const validated = await validator(
|
||||||
AdminPostPriceListsPriceListPriceListReq,
|
AdminPostPriceListsPriceListPriceListReq,
|
||||||
req.body
|
req.body
|
||||||
)
|
)
|
||||||
|
|
||||||
const priceListService: PriceListService =
|
if (featureFlagRouter.isFeatureEnabled(MedusaV2Flag.key)) {
|
||||||
req.scope.resolve("priceListService")
|
const updateVariantsWorkflow = updatePriceLists(req.scope)
|
||||||
|
const rules: PricingTypes.CreatePriceListRules = {}
|
||||||
|
const customerGroups = validated.customer_groups || []
|
||||||
|
delete validated.customer_groups
|
||||||
|
|
||||||
const manager: EntityManager = req.scope.resolve("manager")
|
if (customerGroups.length) {
|
||||||
await manager.transaction(async (transactionManager) => {
|
rules["customer_group_id"] = customerGroups.map((group) => group.id)
|
||||||
return await priceListService
|
}
|
||||||
.withTransaction(transactionManager)
|
|
||||||
.update(id, validated)
|
|
||||||
})
|
|
||||||
|
|
||||||
const priceList = await priceListService.retrieve(id, {
|
const input = {
|
||||||
select: defaultAdminPriceListFields as (keyof PriceList)[],
|
price_lists: [
|
||||||
relations: defaultAdminPriceListRelations,
|
{
|
||||||
})
|
id,
|
||||||
|
...validated,
|
||||||
|
rules,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
} as WorkflowTypes.PriceListWorkflow.UpdatePriceListWorkflowInputDTO
|
||||||
|
|
||||||
|
await updateVariantsWorkflow.run({
|
||||||
|
input,
|
||||||
|
context: {
|
||||||
|
manager,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
priceList = await getPriceListPricingModule(id, {
|
||||||
|
container: req.scope as MedusaContainer,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
await manager.transaction(async (transactionManager) => {
|
||||||
|
return await priceListService
|
||||||
|
.withTransaction(transactionManager)
|
||||||
|
.update(id, validated)
|
||||||
|
})
|
||||||
|
|
||||||
|
priceList = await priceListService.retrieve(id, {
|
||||||
|
select: defaultAdminPriceListFields as (keyof PriceList)[],
|
||||||
|
relations: defaultAdminPriceListRelations,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
res.json({ price_list: priceList })
|
res.json({ price_list: priceList })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import {
|
import { IInventoryService, WorkflowTypes } from "@medusajs/types"
|
||||||
CreateProductVariantInput,
|
import { FlagRouter, MedusaV2Flag } from "@medusajs/utils"
|
||||||
ProductVariantPricesCreateReq,
|
import { CreateProductVariants } from "@medusajs/workflows"
|
||||||
} from "../../../../types/product-variant"
|
|
||||||
import {
|
import {
|
||||||
IsArray,
|
IsArray,
|
||||||
IsBoolean,
|
IsBoolean,
|
||||||
@@ -11,19 +10,22 @@ import {
|
|||||||
IsString,
|
IsString,
|
||||||
ValidateNested,
|
ValidateNested,
|
||||||
} from "class-validator"
|
} from "class-validator"
|
||||||
|
import { defaultAdminProductFields, defaultAdminProductRelations } from "."
|
||||||
import {
|
import {
|
||||||
PricingService,
|
PricingService,
|
||||||
ProductService,
|
ProductService,
|
||||||
ProductVariantInventoryService,
|
ProductVariantInventoryService,
|
||||||
ProductVariantService,
|
ProductVariantService,
|
||||||
} from "../../../../services"
|
} from "../../../../services"
|
||||||
import { defaultAdminProductFields, defaultAdminProductRelations } from "."
|
import {
|
||||||
|
CreateProductVariantInput,
|
||||||
import { EntityManager } from "typeorm"
|
ProductVariantPricesCreateReq,
|
||||||
import { IInventoryService } from "@medusajs/types"
|
} from "../../../../types/product-variant"
|
||||||
import { Type } from "class-transformer"
|
import { Type } from "class-transformer"
|
||||||
import { createVariantsTransaction } from "./transaction/create-product-variant"
|
import { EntityManager } from "typeorm"
|
||||||
import { validator } from "../../../../utils/validator"
|
import { validator } from "../../../../utils/validator"
|
||||||
|
import { getProductWithIsolatedProductModule } from "./get-product"
|
||||||
|
import { createVariantsTransaction } from "./transaction/create-product-variant"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @oas [post] /admin/products/{id}/variants
|
* @oas [post] /admin/products/{id}/variants
|
||||||
@@ -122,6 +124,8 @@ export default async (req, res) => {
|
|||||||
req.body
|
req.body
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const manager: EntityManager = req.scope.resolve("manager")
|
||||||
|
const featureFlagRouter: FlagRouter = req.scope.resolve("featureFlagRouter")
|
||||||
const inventoryService: IInventoryService | undefined =
|
const inventoryService: IInventoryService | undefined =
|
||||||
req.scope.resolve("inventoryService")
|
req.scope.resolve("inventoryService")
|
||||||
const productVariantInventoryService: ProductVariantInventoryService =
|
const productVariantInventoryService: ProductVariantInventoryService =
|
||||||
@@ -129,29 +133,56 @@ export default async (req, res) => {
|
|||||||
const productVariantService: ProductVariantService = req.scope.resolve(
|
const productVariantService: ProductVariantService = req.scope.resolve(
|
||||||
"productVariantService"
|
"productVariantService"
|
||||||
)
|
)
|
||||||
|
|
||||||
const manager: EntityManager = req.scope.resolve("manager")
|
|
||||||
|
|
||||||
await manager.transaction(async (transactionManager) => {
|
|
||||||
await createVariantsTransaction(
|
|
||||||
{
|
|
||||||
manager: transactionManager,
|
|
||||||
inventoryService,
|
|
||||||
productVariantInventoryService,
|
|
||||||
productVariantService,
|
|
||||||
},
|
|
||||||
id,
|
|
||||||
[validated as CreateProductVariantInput]
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
const productService: ProductService = req.scope.resolve("productService")
|
|
||||||
const pricingService: PricingService = req.scope.resolve("pricingService")
|
const pricingService: PricingService = req.scope.resolve("pricingService")
|
||||||
|
let rawProduct
|
||||||
|
|
||||||
const rawProduct = await productService.retrieve(id, {
|
if (featureFlagRouter.isFeatureEnabled(MedusaV2Flag.key)) {
|
||||||
select: defaultAdminProductFields,
|
const createVariantsWorkflow = CreateProductVariants.createProductVariants(
|
||||||
relations: defaultAdminProductRelations,
|
req.scope
|
||||||
})
|
)
|
||||||
|
|
||||||
|
const input = {
|
||||||
|
productVariants: [
|
||||||
|
{
|
||||||
|
product_id: id,
|
||||||
|
...validated,
|
||||||
|
},
|
||||||
|
] as WorkflowTypes.ProductWorkflow.CreateProductVariantsInputDTO[],
|
||||||
|
}
|
||||||
|
|
||||||
|
await createVariantsWorkflow.run({
|
||||||
|
input,
|
||||||
|
context: {
|
||||||
|
manager,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
rawProduct = await getProductWithIsolatedProductModule(
|
||||||
|
req,
|
||||||
|
id,
|
||||||
|
req.retrieveConfig
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
await manager.transaction(async (transactionManager) => {
|
||||||
|
await createVariantsTransaction(
|
||||||
|
{
|
||||||
|
manager: transactionManager,
|
||||||
|
inventoryService,
|
||||||
|
productVariantInventoryService,
|
||||||
|
productVariantService,
|
||||||
|
},
|
||||||
|
id,
|
||||||
|
[validated as CreateProductVariantInput]
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
const productService: ProductService = req.scope.resolve("productService")
|
||||||
|
|
||||||
|
rawProduct = await productService.retrieve(id, {
|
||||||
|
select: defaultAdminProductFields,
|
||||||
|
relations: defaultAdminProductRelations,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const [product] = await pricingService.setAdminProductPricing([rawProduct])
|
const [product] = await pricingService.setAdminProductPricing([rawProduct])
|
||||||
|
|
||||||
|
|||||||
@@ -118,7 +118,11 @@ export default async (req, res) => {
|
|||||||
res.json({ product })
|
res.json({ product })
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getProductWithIsolatedProductModule(req, id, retrieveConfig) {
|
export async function getProductWithIsolatedProductModule(
|
||||||
|
req,
|
||||||
|
id,
|
||||||
|
retrieveConfig
|
||||||
|
) {
|
||||||
// TODO: Add support for fields/expands
|
// TODO: Add support for fields/expands
|
||||||
const remoteQuery = req.scope.resolve("remoteQuery")
|
const remoteQuery = req.scope.resolve("remoteQuery")
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,11 @@ import {
|
|||||||
SalesChannelService,
|
SalesChannelService,
|
||||||
} from "../../../../services"
|
} from "../../../../services"
|
||||||
|
|
||||||
import { IInventoryService } from "@medusajs/types"
|
import {
|
||||||
|
IInventoryService,
|
||||||
|
IPricingModuleService,
|
||||||
|
IProductModuleService,
|
||||||
|
} from "@medusajs/types"
|
||||||
import { MedusaV2Flag, promiseAll } from "@medusajs/utils"
|
import { MedusaV2Flag, promiseAll } from "@medusajs/utils"
|
||||||
import { Type } from "class-transformer"
|
import { Type } from "class-transformer"
|
||||||
import { Product } from "../../../../models"
|
import { Product } from "../../../../models"
|
||||||
@@ -301,7 +305,53 @@ export default async (req, res) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async function listAndCountProductWithIsolatedProductModule(
|
async function getVariantsFromPriceList(req, priceListId) {
|
||||||
|
const remoteQuery = req.scope.resolve("remoteQuery")
|
||||||
|
const pricingModuleService: IPricingModuleService = req.scope.resolve(
|
||||||
|
"pricingModuleService"
|
||||||
|
)
|
||||||
|
const productModuleService: IProductModuleService = req.scope.resolve(
|
||||||
|
"productModuleService"
|
||||||
|
)
|
||||||
|
|
||||||
|
const [priceList] = await pricingModuleService.listPriceLists(
|
||||||
|
{ id: [priceListId] },
|
||||||
|
{
|
||||||
|
relations: [
|
||||||
|
"price_set_money_amounts",
|
||||||
|
"price_set_money_amounts.price_set",
|
||||||
|
],
|
||||||
|
select: ["price_set_money_amounts.price_set.id"],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const priceSetIds = priceList.price_set_money_amounts?.map(
|
||||||
|
(psma) => psma.price_set?.id
|
||||||
|
)
|
||||||
|
|
||||||
|
const query = {
|
||||||
|
product_variant_price_set: {
|
||||||
|
__args: {
|
||||||
|
price_set_id: priceSetIds,
|
||||||
|
},
|
||||||
|
fields: ["variant_id", "price_set_id"],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const variantPriceSets = await remoteQuery(query)
|
||||||
|
const variantIds = variantPriceSets.map((vps) => vps.variant_id)
|
||||||
|
|
||||||
|
return await productModuleService.listVariants(
|
||||||
|
{
|
||||||
|
id: variantIds,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
select: ["product_id"],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function listAndCountProductWithIsolatedProductModule(
|
||||||
req,
|
req,
|
||||||
filterableFields,
|
filterableFields,
|
||||||
listConfig
|
listConfig
|
||||||
@@ -309,6 +359,7 @@ async function listAndCountProductWithIsolatedProductModule(
|
|||||||
// TODO: Add support for fields/expands
|
// TODO: Add support for fields/expands
|
||||||
|
|
||||||
const remoteQuery = req.scope.resolve("remoteQuery")
|
const remoteQuery = req.scope.resolve("remoteQuery")
|
||||||
|
const featureFlagRouter = req.scope.resolve("featureFlagRouter")
|
||||||
|
|
||||||
const productIdsFilter: Set<string> = new Set()
|
const productIdsFilter: Set<string> = new Set()
|
||||||
const variantIdsFilter: Set<string> = new Set()
|
const variantIdsFilter: Set<string> = new Set()
|
||||||
@@ -352,22 +403,28 @@ async function listAndCountProductWithIsolatedProductModule(
|
|||||||
delete filterableFields.price_list_id
|
delete filterableFields.price_list_id
|
||||||
|
|
||||||
if (priceListId) {
|
if (priceListId) {
|
||||||
// TODO: it is working but validate the behaviour.
|
if (featureFlagRouter.isFeatureEnabled(MedusaV2Flag.key)) {
|
||||||
// e.g pricing context properly set.
|
const variants = await getVariantsFromPriceList(req, priceListId)
|
||||||
// At the moment filtering by price list but not having any customer id or
|
|
||||||
// include discount forces the query to filter with price list id is null
|
variants.forEach((pv) => variantIdsFilter.add(pv.id))
|
||||||
const priceListService = req.scope.resolve(
|
} else {
|
||||||
"priceListService"
|
// TODO: it is working but validate the behaviour.
|
||||||
) as PriceListService
|
// e.g pricing context properly set.
|
||||||
promises.push(
|
// At the moment filtering by price list but not having any customer id or
|
||||||
priceListService
|
// include discount forces the query to filter with price list id is null
|
||||||
.listPriceListsVariantIdsMap(priceListId)
|
const priceListService = req.scope.resolve(
|
||||||
.then((priceListVariantIdsMap) => {
|
"priceListService"
|
||||||
priceListVariantIdsMap[priceListId].map((variantId) =>
|
) as PriceListService
|
||||||
variantIdsFilter.add(variantId)
|
promises.push(
|
||||||
)
|
priceListService
|
||||||
})
|
.listPriceListsVariantIdsMap(priceListId)
|
||||||
)
|
.then((priceListVariantIdsMap) => {
|
||||||
|
priceListVariantIdsMap[priceListId].map((variantId) =>
|
||||||
|
variantIdsFilter.add(variantId)
|
||||||
|
)
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const discountConditionId = filterableFields.discount_condition_id
|
const discountConditionId = filterableFields.discount_condition_id
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import getMigrations, {
|
|||||||
runIsolatedModulesMigration,
|
runIsolatedModulesMigration,
|
||||||
} from "./utils/get-migrations"
|
} from "./utils/get-migrations"
|
||||||
|
|
||||||
import { MedusaV2Flag, createMedusaContainer } from "@medusajs/utils"
|
import { createMedusaContainer } from "@medusajs/utils"
|
||||||
import configModuleLoader from "../loaders/config"
|
import configModuleLoader from "../loaders/config"
|
||||||
import databaseLoader from "../loaders/database"
|
import databaseLoader from "../loaders/database"
|
||||||
import featureFlagLoader from "../loaders/feature-flags"
|
import featureFlagLoader from "../loaders/feature-flags"
|
||||||
@@ -38,6 +38,11 @@ const getDataSource = async (directory) => {
|
|||||||
const runLinkMigrations = async (directory) => {
|
const runLinkMigrations = async (directory) => {
|
||||||
const configModule = configModuleLoader(directory)
|
const configModule = configModuleLoader(directory)
|
||||||
const container = createMedusaContainer()
|
const container = createMedusaContainer()
|
||||||
|
const featureFlagRouter = featureFlagLoader(configModule)
|
||||||
|
|
||||||
|
container.register({
|
||||||
|
featureFlagRouter: asValue(featureFlagRouter),
|
||||||
|
})
|
||||||
|
|
||||||
await pgConnectionLoader({ configModule, container })
|
await pgConnectionLoader({ configModule, container })
|
||||||
|
|
||||||
@@ -63,16 +68,13 @@ 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)
|
||||||
|
|
||||||
if (featureFlagRouter.isFeatureEnabled(MedusaV2Flag.key)) {
|
await runLinkMigrations(directory)
|
||||||
await runLinkMigrations(directory)
|
|
||||||
}
|
|
||||||
process.exit()
|
process.exit()
|
||||||
|
|
||||||
Logger.info("Migrations completed.")
|
Logger.info("Migrations completed.")
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import { MedusaV2Flag } from "@medusajs/utils"
|
||||||
|
import { MigrationInterface, QueryRunner } from "typeorm"
|
||||||
|
|
||||||
|
export const featureFlag = MedusaV2Flag.key
|
||||||
|
|
||||||
|
export class DropNonNullConstraintPriceList1699371074198
|
||||||
|
implements MigrationInterface
|
||||||
|
{
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`
|
||||||
|
ALTER TABLE IF EXISTS price_list ALTER COLUMN name DROP NOT NULL;
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`
|
||||||
|
ALTER TABLE IF EXISTS price_list ALTER COLUMN name SET NOT NULL;
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -69,7 +69,7 @@ export class MoneyAmount extends SoftDeletableEntity {
|
|||||||
|
|
||||||
@Index("idx_money_amount_region_id")
|
@Index("idx_money_amount_region_id")
|
||||||
@Column({ nullable: true })
|
@Column({ nullable: true })
|
||||||
region_id: string
|
region_id: string | null
|
||||||
|
|
||||||
@ManyToOne(() => Region)
|
@ManyToOne(() => Region)
|
||||||
@JoinColumn({ name: "region_id" })
|
@JoinColumn({ name: "region_id" })
|
||||||
|
|||||||
@@ -11,17 +11,28 @@ export const createDefaultRuleTypes = async (container: AwilixContainer) => {
|
|||||||
"pricingModuleService"
|
"pricingModuleService"
|
||||||
)
|
)
|
||||||
const existing = await pricingModuleService.listRuleTypes(
|
const existing = await pricingModuleService.listRuleTypes(
|
||||||
{ rule_attribute: ["region_id"] },
|
{ rule_attribute: ["region_id", "customer_group_id"] },
|
||||||
{ take: 1 }
|
{ take: 2 }
|
||||||
)
|
)
|
||||||
|
|
||||||
if (existing.length) {
|
if (existing.length === 2) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
await pricingModuleService.createRuleTypes([
|
if (existing.length === 0) {
|
||||||
{ name: "region_id", rule_attribute: "region_id" },
|
await pricingModuleService.createRuleTypes([
|
||||||
])
|
{ name: "region_id", rule_attribute: "region_id" },
|
||||||
|
{ name: "customer_group_id", rule_attribute: "customer_group_id" },
|
||||||
|
])
|
||||||
|
} else if (existing[0].rule_attribute === "region_id") {
|
||||||
|
await pricingModuleService.createRuleTypes([
|
||||||
|
{ name: "customer_group_id", rule_attribute: "customer_group_id" },
|
||||||
|
])
|
||||||
|
} else {
|
||||||
|
await pricingModuleService.createRuleTypes([
|
||||||
|
{ name: "region_id", rule_attribute: "region_id" },
|
||||||
|
])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const migrate = async function ({ directory }) {
|
const migrate = async function ({ directory }) {
|
||||||
@@ -38,7 +49,9 @@ const migrate = async function ({ directory }) {
|
|||||||
migrate({ directory: process.cwd() })
|
migrate({ directory: process.cwd() })
|
||||||
.then(() => {
|
.then(() => {
|
||||||
console.log("Created default rule types")
|
console.log("Created default rule types")
|
||||||
|
process.exit()
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
console.log("Failed to create rule types")
|
console.log("Failed to create rule types")
|
||||||
|
process.exit(1)
|
||||||
})
|
})
|
||||||
@@ -0,0 +1,146 @@
|
|||||||
|
import { IPricingModuleService, PricingTypes } from "@medusajs/types"
|
||||||
|
import { AwilixContainer } from "awilix"
|
||||||
|
import dotenv from "dotenv"
|
||||||
|
import express from "express"
|
||||||
|
import loaders from "../loaders"
|
||||||
|
import Logger from "../loaders/logger"
|
||||||
|
import { PriceListService } from "../services"
|
||||||
|
|
||||||
|
dotenv.config()
|
||||||
|
|
||||||
|
const BATCH_SIZE = 1000
|
||||||
|
|
||||||
|
export const migratePriceLists = async (container: AwilixContainer) => {
|
||||||
|
const pricingModuleService: IPricingModuleService = container.resolve(
|
||||||
|
"pricingModuleService"
|
||||||
|
)
|
||||||
|
|
||||||
|
const priceListCoreService: PriceListService =
|
||||||
|
container.resolve("priceListService")
|
||||||
|
|
||||||
|
const remoteQuery = container.resolve("remoteQuery")
|
||||||
|
|
||||||
|
const existingRuleTypes = await pricingModuleService.listRuleTypes(
|
||||||
|
{ rule_attribute: ["customer_group_id"] },
|
||||||
|
{ take: 2 }
|
||||||
|
)
|
||||||
|
|
||||||
|
if (existingRuleTypes.length === 0) {
|
||||||
|
Logger.info(
|
||||||
|
`Run default rules migration before running this migration - node node_modules/@medusajs/medusa/dist/scripts/create-default-rule-types.js`
|
||||||
|
)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let offset = 0
|
||||||
|
let arePriceListsAvailable = true
|
||||||
|
|
||||||
|
while (arePriceListsAvailable) {
|
||||||
|
const priceLists = await priceListCoreService.list(
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
take: BATCH_SIZE,
|
||||||
|
skip: offset,
|
||||||
|
relations: ["customer_groups"],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
if (priceLists.length === 0) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
offset += BATCH_SIZE
|
||||||
|
|
||||||
|
await pricingModuleService.update(
|
||||||
|
priceLists.map((priceList) => {
|
||||||
|
const updateData: PricingTypes.UpdatePriceListDTO = {
|
||||||
|
id: priceList.id,
|
||||||
|
title: priceList.name,
|
||||||
|
}
|
||||||
|
|
||||||
|
if (priceList?.customer_groups?.length) {
|
||||||
|
updateData.rules = {
|
||||||
|
customer_group_id: priceList.customer_groups.map((cg) => cg.id),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return updateData
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
for (const priceList of priceLists) {
|
||||||
|
let productsOffset = 0
|
||||||
|
let areVariantsAvailable = true
|
||||||
|
|
||||||
|
while (areVariantsAvailable) {
|
||||||
|
const [priceListVariants, variantsCount] =
|
||||||
|
await priceListCoreService.listVariants(
|
||||||
|
priceList.id,
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
skip: productsOffset,
|
||||||
|
take: BATCH_SIZE,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
if (variantsCount === 0) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
productsOffset += BATCH_SIZE
|
||||||
|
|
||||||
|
const query = {
|
||||||
|
product_variant_price_set: {
|
||||||
|
__args: {
|
||||||
|
variant_id: priceListVariants.map((plv) => plv.id),
|
||||||
|
},
|
||||||
|
fields: ["variant_id", "price_set_id"],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const variantPriceSets = await remoteQuery(query)
|
||||||
|
const variantPriceSetMap = new Map<string, string>(
|
||||||
|
variantPriceSets.map((mps) => [mps.variant_id, mps.price_set_id])
|
||||||
|
)
|
||||||
|
|
||||||
|
const variantPrices = priceListVariants
|
||||||
|
.map((plv) => plv.prices || [])
|
||||||
|
.flat()
|
||||||
|
|
||||||
|
await pricingModuleService.addPriceListPrices([
|
||||||
|
{
|
||||||
|
priceListId: priceList.id,
|
||||||
|
prices: variantPrices.map((vp) => {
|
||||||
|
return {
|
||||||
|
id: vp.id,
|
||||||
|
price_set_id: variantPriceSetMap.get(vp.variant_id)!,
|
||||||
|
currency_code: vp.currency_code,
|
||||||
|
amount: vp.amount,
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const migrate = async function ({ directory }) {
|
||||||
|
const app = express()
|
||||||
|
const { container } = await loaders({
|
||||||
|
directory,
|
||||||
|
expressApp: app,
|
||||||
|
isTest: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
return await migratePriceLists(container)
|
||||||
|
}
|
||||||
|
|
||||||
|
migrate({ directory: process.cwd() })
|
||||||
|
.then(() => {
|
||||||
|
console.log("Migrated price lists")
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
console.log("Failed to migrate price lists")
|
||||||
|
})
|
||||||
@@ -45,7 +45,7 @@ const migrateProductVariant = async (
|
|||||||
rules: [{ rule_attribute: "region_id" }],
|
rules: [{ rule_attribute: "region_id" }],
|
||||||
prices: variant.prices.map((price) => ({
|
prices: variant.prices.map((price) => ({
|
||||||
rules: {
|
rules: {
|
||||||
region_id: price.region_id,
|
...(price.region_id ? { region_id: price.region_id } : {}),
|
||||||
},
|
},
|
||||||
currency_code: price.currency_code,
|
currency_code: price.currency_code,
|
||||||
min_quantity: price.min_quantity,
|
min_quantity: price.min_quantity,
|
||||||
|
|||||||
@@ -4,15 +4,18 @@ import {
|
|||||||
PriceSetMoneyAmountDTO,
|
PriceSetMoneyAmountDTO,
|
||||||
RemoteQueryFunction,
|
RemoteQueryFunction,
|
||||||
} from "@medusajs/types"
|
} from "@medusajs/types"
|
||||||
|
import {
|
||||||
|
CustomerService,
|
||||||
|
ProductVariantService,
|
||||||
|
RegionService,
|
||||||
|
TaxProviderService,
|
||||||
|
} from "."
|
||||||
import {
|
import {
|
||||||
FlagRouter,
|
FlagRouter,
|
||||||
MedusaV2Flag,
|
MedusaV2Flag,
|
||||||
promiseAll,
|
promiseAll,
|
||||||
removeNullish,
|
removeNullish,
|
||||||
} from "@medusajs/utils"
|
} from "@medusajs/utils"
|
||||||
import { ProductVariantService, RegionService, TaxProviderService } from "."
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
IPriceSelectionStrategy,
|
IPriceSelectionStrategy,
|
||||||
PriceSelectionContext,
|
PriceSelectionContext,
|
||||||
@@ -33,11 +36,11 @@ import {
|
|||||||
TaxedPricing,
|
TaxedPricing,
|
||||||
} from "../types/pricing"
|
} from "../types/pricing"
|
||||||
|
|
||||||
import { MedusaError } from "medusa-core-utils"
|
|
||||||
import { EntityManager } from "typeorm"
|
import { EntityManager } from "typeorm"
|
||||||
import { TransactionBaseService } from "../interfaces"
|
import { MedusaError } from "medusa-core-utils"
|
||||||
import TaxInclusivePricingFeatureFlag from "../loaders/feature-flags/tax-inclusive-pricing"
|
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 = {
|
||||||
@@ -45,6 +48,7 @@ type InjectedDependencies = {
|
|||||||
productVariantService: ProductVariantService
|
productVariantService: ProductVariantService
|
||||||
taxProviderService: TaxProviderService
|
taxProviderService: TaxProviderService
|
||||||
regionService: RegionService
|
regionService: RegionService
|
||||||
|
customerService: CustomerService
|
||||||
priceSelectionStrategy: IPriceSelectionStrategy
|
priceSelectionStrategy: IPriceSelectionStrategy
|
||||||
featureFlagRouter: FlagRouter
|
featureFlagRouter: FlagRouter
|
||||||
remoteQuery: RemoteQueryFunction
|
remoteQuery: RemoteQueryFunction
|
||||||
@@ -57,6 +61,7 @@ type InjectedDependencies = {
|
|||||||
class PricingService extends TransactionBaseService {
|
class PricingService extends TransactionBaseService {
|
||||||
protected readonly regionService: RegionService
|
protected readonly regionService: RegionService
|
||||||
protected readonly taxProviderService: TaxProviderService
|
protected readonly taxProviderService: TaxProviderService
|
||||||
|
protected readonly customerService_: CustomerService
|
||||||
protected readonly priceSelectionStrategy: IPriceSelectionStrategy
|
protected readonly priceSelectionStrategy: IPriceSelectionStrategy
|
||||||
protected readonly productVariantService: ProductVariantService
|
protected readonly productVariantService: ProductVariantService
|
||||||
protected readonly featureFlagRouter: FlagRouter
|
protected readonly featureFlagRouter: FlagRouter
|
||||||
@@ -64,6 +69,7 @@ class PricingService extends TransactionBaseService {
|
|||||||
protected get pricingModuleService(): IPricingModuleService {
|
protected get pricingModuleService(): IPricingModuleService {
|
||||||
return this.__container__.pricingModuleService
|
return this.__container__.pricingModuleService
|
||||||
}
|
}
|
||||||
|
|
||||||
protected get remoteQuery(): RemoteQueryFunction {
|
protected get remoteQuery(): RemoteQueryFunction {
|
||||||
return this.__container__.remoteQuery
|
return this.__container__.remoteQuery
|
||||||
}
|
}
|
||||||
@@ -74,6 +80,7 @@ class PricingService extends TransactionBaseService {
|
|||||||
regionService,
|
regionService,
|
||||||
priceSelectionStrategy,
|
priceSelectionStrategy,
|
||||||
featureFlagRouter,
|
featureFlagRouter,
|
||||||
|
customerService,
|
||||||
}: InjectedDependencies) {
|
}: InjectedDependencies) {
|
||||||
// eslint-disable-next-line prefer-rest-params
|
// eslint-disable-next-line prefer-rest-params
|
||||||
super(arguments[0])
|
super(arguments[0])
|
||||||
@@ -82,6 +89,7 @@ class PricingService extends TransactionBaseService {
|
|||||||
this.taxProviderService = taxProviderService
|
this.taxProviderService = taxProviderService
|
||||||
this.priceSelectionStrategy = priceSelectionStrategy
|
this.priceSelectionStrategy = priceSelectionStrategy
|
||||||
this.productVariantService = productVariantService
|
this.productVariantService = productVariantService
|
||||||
|
this.customerService_ = customerService
|
||||||
this.featureFlagRouter = featureFlagRouter
|
this.featureFlagRouter = featureFlagRouter
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -220,9 +228,20 @@ class PricingService extends TransactionBaseService {
|
|||||||
(variantPriceSet) => variantPriceSet.price_set_id
|
(variantPriceSet) => variantPriceSet.price_set_id
|
||||||
)
|
)
|
||||||
|
|
||||||
const queryContext: PriceSelectionContext = removeNullish(
|
const queryContext: PriceSelectionContext & {
|
||||||
context.price_selection
|
customer_group_id?: string[]
|
||||||
)
|
} = removeNullish(context.price_selection)
|
||||||
|
|
||||||
|
if (queryContext.customer_id) {
|
||||||
|
const { groups } = await this.customerService_.retrieve(
|
||||||
|
queryContext.customer_id,
|
||||||
|
{ relations: ["groups"] }
|
||||||
|
)
|
||||||
|
|
||||||
|
if (groups?.length) {
|
||||||
|
queryContext.customer_group_id = groups.map((group) => group.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let calculatedPrices: CalculatedPriceSet[] = []
|
let calculatedPrices: CalculatedPriceSet[] = []
|
||||||
|
|
||||||
@@ -259,25 +278,37 @@ class PricingService extends TransactionBaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (priceSetId) {
|
if (priceSetId) {
|
||||||
const calculatedPrice: CalculatedPriceSet | undefined =
|
const calculatedPrices: CalculatedPriceSet | undefined =
|
||||||
calculatedPriceMap.get(priceSetId)
|
calculatedPriceMap.get(priceSetId)
|
||||||
|
|
||||||
if (calculatedPrice) {
|
if (calculatedPrices) {
|
||||||
pricingResult.prices = [
|
pricingResult.prices.push({
|
||||||
{
|
id: calculatedPrices?.original_price?.money_amount_id,
|
||||||
id: calculatedPrice.calculated_price?.money_amount_id,
|
currency_code: calculatedPrices.currency_code,
|
||||||
currency_code: calculatedPrice.currency_code,
|
amount: calculatedPrices.original_amount,
|
||||||
amount: calculatedPrice.calculated_amount,
|
min_quantity: calculatedPrices.original_price?.min_quantity,
|
||||||
min_quantity: calculatedPrice.calculated_price?.min_quantity,
|
max_quantity: calculatedPrices.original_price?.max_quantity,
|
||||||
max_quantity: calculatedPrice.calculated_price?.max_quantity,
|
price_list_id: calculatedPrices.original_price?.price_list_id,
|
||||||
price_list_id: calculatedPrice.calculated_price?.price_list_id,
|
} as MoneyAmount)
|
||||||
},
|
|
||||||
] as MoneyAmount[]
|
|
||||||
|
|
||||||
pricingResult.original_price = calculatedPrice?.original_amount
|
if (
|
||||||
pricingResult.calculated_price = calculatedPrice?.calculated_amount
|
calculatedPrices.calculated_price?.money_amount_id !==
|
||||||
|
calculatedPrices.original_price?.money_amount_id
|
||||||
|
) {
|
||||||
|
pricingResult.prices.push({
|
||||||
|
id: calculatedPrices.calculated_price?.money_amount_id,
|
||||||
|
currency_code: calculatedPrices.currency_code,
|
||||||
|
amount: calculatedPrices.calculated_amount,
|
||||||
|
min_quantity: calculatedPrices.calculated_price?.min_quantity,
|
||||||
|
max_quantity: calculatedPrices.calculated_price?.max_quantity,
|
||||||
|
price_list_id: calculatedPrices.calculated_price?.price_list_id,
|
||||||
|
} as MoneyAmount)
|
||||||
|
}
|
||||||
|
|
||||||
|
pricingResult.original_price = calculatedPrices?.original_amount
|
||||||
|
pricingResult.calculated_price = calculatedPrices?.calculated_amount
|
||||||
pricingResult.calculated_price_type =
|
pricingResult.calculated_price_type =
|
||||||
calculatedPrice?.calculated_price?.price_list_type
|
calculatedPrices?.calculated_price?.price_list_type
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import {
|
|||||||
ValidateNested,
|
ValidateNested,
|
||||||
} from "class-validator"
|
} from "class-validator"
|
||||||
import { PriceList } from "../models/price-list"
|
import { PriceList } from "../models/price-list"
|
||||||
import { DateComparisonOperator, FindConfig } from "./common"
|
import { DateComparisonOperator } from "./common"
|
||||||
import { XorConstraint } from "./validators/xor"
|
import { XorConstraint } from "./validators/xor"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+71
@@ -1185,6 +1185,77 @@ describe("PricingModule Service - Calculate Price", () => {
|
|||||||
])
|
])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("should return best price list price first when price list conditions match", async () => {
|
||||||
|
await createPriceLists(service)
|
||||||
|
await createPriceLists(
|
||||||
|
service,
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
defaultPriceListPrices.map((price) => {
|
||||||
|
return { ...price, amount: price.amount / 2 }
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
const priceSetsResult = await service.calculatePrices(
|
||||||
|
{ id: ["price-set-EUR", "price-set-PLN"] },
|
||||||
|
{
|
||||||
|
context: {
|
||||||
|
currency_code: "PLN",
|
||||||
|
region_id: "DE",
|
||||||
|
customer_group_id: "vip-customer-group-id",
|
||||||
|
company_id: "medusa-company-id",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(priceSetsResult).toEqual([
|
||||||
|
{
|
||||||
|
id: "price-set-EUR",
|
||||||
|
is_calculated_price_price_list: false,
|
||||||
|
calculated_amount: null,
|
||||||
|
is_original_price_price_list: false,
|
||||||
|
original_amount: null,
|
||||||
|
currency_code: null,
|
||||||
|
calculated_price: {
|
||||||
|
money_amount_id: null,
|
||||||
|
price_list_id: null,
|
||||||
|
price_list_type: null,
|
||||||
|
min_quantity: null,
|
||||||
|
max_quantity: null,
|
||||||
|
},
|
||||||
|
original_price: {
|
||||||
|
money_amount_id: null,
|
||||||
|
price_list_id: null,
|
||||||
|
price_list_type: null,
|
||||||
|
min_quantity: null,
|
||||||
|
max_quantity: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "price-set-PLN",
|
||||||
|
is_calculated_price_price_list: true,
|
||||||
|
calculated_amount: 116,
|
||||||
|
is_original_price_price_list: false,
|
||||||
|
original_amount: 400,
|
||||||
|
currency_code: "PLN",
|
||||||
|
calculated_price: {
|
||||||
|
money_amount_id: expect.any(String),
|
||||||
|
price_list_id: expect.any(String),
|
||||||
|
price_list_type: "sale",
|
||||||
|
min_quantity: null,
|
||||||
|
max_quantity: null,
|
||||||
|
},
|
||||||
|
original_price: {
|
||||||
|
money_amount_id: expect.any(String),
|
||||||
|
price_list_id: null,
|
||||||
|
price_list_type: null,
|
||||||
|
min_quantity: 1,
|
||||||
|
max_quantity: 5,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
it("should return price list prices when price list dont have rules, but context is loaded", async () => {
|
it("should return price list prices when price list dont have rules, but context is loaded", async () => {
|
||||||
await createPriceLists(service, {}, {})
|
await createPriceLists(service, {}, {})
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,20 @@
|
|||||||
import { Modules } from "@medusajs/modules-sdk"
|
import { Modules } from "@medusajs/modules-sdk"
|
||||||
import { ModuleJoinerConfig } from "@medusajs/types"
|
import { ModuleJoinerConfig } from "@medusajs/types"
|
||||||
import { MapToConfig } from "@medusajs/utils"
|
import { MapToConfig } from "@medusajs/utils"
|
||||||
import { Currency, MoneyAmount, PriceSet } from "@models"
|
import {
|
||||||
|
Currency,
|
||||||
|
MoneyAmount,
|
||||||
|
PriceList,
|
||||||
|
PriceSet,
|
||||||
|
PriceSetMoneyAmount,
|
||||||
|
} from "@models"
|
||||||
|
|
||||||
export const LinkableKeys = {
|
export const LinkableKeys = {
|
||||||
money_amount_id: MoneyAmount.name,
|
money_amount_id: MoneyAmount.name,
|
||||||
currency_code: Currency.name,
|
currency_code: Currency.name,
|
||||||
price_set_id: PriceSet.name,
|
price_set_id: PriceSet.name,
|
||||||
|
price_list_id: PriceList.name,
|
||||||
|
price_set_money_amount_id: PriceSetMoneyAmount.name,
|
||||||
}
|
}
|
||||||
const entityLinkableKeysMap: MapToConfig = {}
|
const entityLinkableKeysMap: MapToConfig = {}
|
||||||
Object.entries(LinkableKeys).forEach(([key, value]) => {
|
Object.entries(LinkableKeys).forEach(([key, value]) => {
|
||||||
@@ -16,11 +24,12 @@ Object.entries(LinkableKeys).forEach(([key, value]) => {
|
|||||||
valueFrom: key.split("_").pop()!,
|
valueFrom: key.split("_").pop()!,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
export const entityNameToLinkableKeysMap: MapToConfig = entityLinkableKeysMap
|
export const entityNameToLinkableKeysMap: MapToConfig = entityLinkableKeysMap
|
||||||
|
|
||||||
export const joinerConfig: ModuleJoinerConfig = {
|
export const joinerConfig: ModuleJoinerConfig = {
|
||||||
serviceName: Modules.PRICING,
|
serviceName: Modules.PRICING,
|
||||||
primaryKeys: ["id", "currency_code"],
|
primaryKeys: ["id"],
|
||||||
linkableKeys: LinkableKeys,
|
linkableKeys: LinkableKeys,
|
||||||
alias: [
|
alias: [
|
||||||
{
|
{
|
||||||
@@ -53,5 +62,17 @@ export const joinerConfig: ModuleJoinerConfig = {
|
|||||||
methodSuffix: "Currencies",
|
methodSuffix: "Currencies",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "price_list",
|
||||||
|
args: {
|
||||||
|
methodSuffix: "PriceLists",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "price_lists",
|
||||||
|
args: {
|
||||||
|
methodSuffix: "PriceLists",
|
||||||
|
},
|
||||||
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
@@ -71,6 +71,9 @@ export class PricingRepository
|
|||||||
price_list_id: "psma1.price_list_id",
|
price_list_id: "psma1.price_list_id",
|
||||||
pl_number_rules: "pl.number_rules",
|
pl_number_rules: "pl.number_rules",
|
||||||
pl_type: "pl.type",
|
pl_type: "pl.type",
|
||||||
|
has_price_list: knex.raw(
|
||||||
|
"case when psma1.price_list_id IS NULL then False else True end"
|
||||||
|
),
|
||||||
})
|
})
|
||||||
.leftJoin("price_set_money_amount as psma1", "psma1.id", "psma1.id")
|
.leftJoin("price_set_money_amount as psma1", "psma1.id", "psma1.id")
|
||||||
.leftJoin("price_rule as pr", "pr.price_set_money_amount_id", "psma1.id")
|
.leftJoin("price_rule as pr", "pr.price_set_money_amount_id", "psma1.id")
|
||||||
@@ -154,15 +157,23 @@ export class PricingRepository
|
|||||||
.leftJoin("rule_type as rt", "rt.id", "pr.rule_type_id")
|
.leftJoin("rule_type as rt", "rt.id", "pr.rule_type_id")
|
||||||
.whereIn("ps.id", pricingFilters.id)
|
.whereIn("ps.id", pricingFilters.id)
|
||||||
.andWhere("ma.currency_code", "=", currencyCode)
|
.andWhere("ma.currency_code", "=", currencyCode)
|
||||||
|
|
||||||
.orderBy([
|
.orderBy([
|
||||||
{ column: "price_list_id", order: "asc" },
|
{ column: "psma.has_price_list", order: "asc" },
|
||||||
{ column: "number_rules", order: "desc" },
|
{ column: "number_rules", order: "desc" },
|
||||||
{ column: "default_priority", order: "desc" },
|
{ column: "default_priority", order: "desc" },
|
||||||
|
{ column: "amount", order: "asc" },
|
||||||
])
|
])
|
||||||
|
|
||||||
if (quantity) {
|
if (quantity) {
|
||||||
priceSetQueryKnex.where("ma.min_quantity", "<=", quantity)
|
priceSetQueryKnex.where("ma.min_quantity", "<=", quantity)
|
||||||
priceSetQueryKnex.andWhere("ma.max_quantity", ">=", quantity)
|
priceSetQueryKnex.andWhere("ma.max_quantity", ">=", quantity)
|
||||||
|
} else {
|
||||||
|
priceSetQueryKnex.andWhere(function () {
|
||||||
|
this.andWhere("ma.min_quantity", "<=", "1").orWhereNull(
|
||||||
|
"ma.min_quantity"
|
||||||
|
)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return await priceSetQueryKnex
|
return await priceSetQueryKnex
|
||||||
|
|||||||
@@ -13,7 +13,15 @@ import {
|
|||||||
PricingTypes,
|
PricingTypes,
|
||||||
RuleTypeDTO,
|
RuleTypeDTO,
|
||||||
} from "@medusajs/types"
|
} from "@medusajs/types"
|
||||||
import { PriceListType } from "@medusajs/utils"
|
import {
|
||||||
|
InjectManager,
|
||||||
|
InjectTransactionManager,
|
||||||
|
MedusaContext,
|
||||||
|
MedusaError,
|
||||||
|
PriceListType,
|
||||||
|
groupBy,
|
||||||
|
removeNullish,
|
||||||
|
} from "@medusajs/utils"
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Currency,
|
Currency,
|
||||||
@@ -42,15 +50,6 @@ import {
|
|||||||
PriceSetService,
|
PriceSetService,
|
||||||
RuleTypeService,
|
RuleTypeService,
|
||||||
} from "@services"
|
} from "@services"
|
||||||
|
|
||||||
import {
|
|
||||||
groupBy,
|
|
||||||
InjectManager,
|
|
||||||
InjectTransactionManager,
|
|
||||||
MedusaContext,
|
|
||||||
MedusaError,
|
|
||||||
removeNullish,
|
|
||||||
} from "@medusajs/utils"
|
|
||||||
import { joinerConfig } from "../joiner-config"
|
import { joinerConfig } from "../joiner-config"
|
||||||
import { CreatePriceListRuleValueDTO, PricingRepositoryService } from "../types"
|
import { CreatePriceListRuleValueDTO, PricingRepositoryService } from "../types"
|
||||||
|
|
||||||
@@ -147,6 +146,7 @@ export default class PricingModuleService<
|
|||||||
pricingContext,
|
pricingContext,
|
||||||
sharedContext
|
sharedContext
|
||||||
)
|
)
|
||||||
|
|
||||||
const pricesSetPricesMap = groupBy(results, "price_set_id")
|
const pricesSetPricesMap = groupBy(results, "price_set_id")
|
||||||
|
|
||||||
const calculatedPrices = pricingFilters.id.map(
|
const calculatedPrices = pricingFilters.id.map(
|
||||||
@@ -155,8 +155,9 @@ export default class PricingModuleService<
|
|||||||
// which is prioritized by number_rules first for exact match and then deafult_priority of the rule_type
|
// which is prioritized by number_rules first for exact match and then deafult_priority of the rule_type
|
||||||
// inject custom price selection here
|
// inject custom price selection here
|
||||||
const prices = pricesSetPricesMap.get(priceSetId) || []
|
const prices = pricesSetPricesMap.get(priceSetId) || []
|
||||||
const priceListPrice = prices?.find((p) => !!p.price_list_id)
|
const priceListPrice = prices.find((p) => p.price_list_id)
|
||||||
const defaultPrice = prices?.find((p) => !!!p.price_list_id)
|
|
||||||
|
const defaultPrice = prices?.find((p) => !p.price_list_id)
|
||||||
|
|
||||||
let calculatedPrice: PricingTypes.CalculatedPriceSetDTO = defaultPrice
|
let calculatedPrice: PricingTypes.CalculatedPriceSetDTO = defaultPrice
|
||||||
let originalPrice: PricingTypes.CalculatedPriceSetDTO = defaultPrice
|
let originalPrice: PricingTypes.CalculatedPriceSetDTO = defaultPrice
|
||||||
|
|||||||
+1
@@ -201,6 +201,7 @@ describe("ProductModuleService products", function () {
|
|||||||
expect(createdVariant?.options).toHaveLength(1)
|
expect(createdVariant?.options).toHaveLength(1)
|
||||||
expect(product.tags).toHaveLength(1)
|
expect(product.tags).toHaveLength(1)
|
||||||
expect(product.variants).toHaveLength(2)
|
expect(product.variants).toHaveLength(2)
|
||||||
|
|
||||||
expect(product).toEqual(
|
expect(product).toEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
id: expect.any(String),
|
id: expect.any(String),
|
||||||
|
|||||||
@@ -8,8 +8,11 @@ export class Migration20230719100648 extends Migration {
|
|||||||
this.addSql(
|
this.addSql(
|
||||||
'create index IF NOT EXISTS "IDX_product_category_path" on "product_category" ("mpath");'
|
'create index IF NOT EXISTS "IDX_product_category_path" on "product_category" ("mpath");'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
this.addSql('DROP INDEX IF EXISTS "IDX_product_category_handle";')
|
||||||
|
|
||||||
this.addSql(
|
this.addSql(
|
||||||
'alter table "product_category" add constraint "IDX_product_category_handle" unique ("handle");'
|
'alter table "product_category" ADD CONSTRAINT "IDX_product_category_handle" unique ("handle");'
|
||||||
)
|
)
|
||||||
|
|
||||||
this.addSql(
|
this.addSql(
|
||||||
|
|||||||
@@ -231,6 +231,82 @@ export default class ProductModuleService<
|
|||||||
return [JSON.parse(JSON.stringify(variants)), count]
|
return [JSON.parse(JSON.stringify(variants)), count]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async createVariants(
|
||||||
|
data: ProductTypes.CreateProductVariantDTO[],
|
||||||
|
@MedusaContext() sharedContext: Context = {}
|
||||||
|
): Promise<ProductTypes.ProductVariantDTO[]> {
|
||||||
|
const productOptionIds = data
|
||||||
|
.map((pv) => (pv.options || []).map((opt) => opt.option_id!))
|
||||||
|
.flat()
|
||||||
|
|
||||||
|
const productOptions = await this.listOptions(
|
||||||
|
{ id: productOptionIds },
|
||||||
|
{},
|
||||||
|
sharedContext
|
||||||
|
)
|
||||||
|
|
||||||
|
const productOptionsMap = new Map<string, ProductTypes.ProductOptionDTO>(
|
||||||
|
productOptions.map((po) => [po.id, po])
|
||||||
|
)
|
||||||
|
|
||||||
|
const productVariantsMap = new Map<
|
||||||
|
string,
|
||||||
|
ProductTypes.CreateProductVariantDTO[]
|
||||||
|
>()
|
||||||
|
|
||||||
|
for (const productVariantData of data) {
|
||||||
|
productVariantData.options = productVariantData.options?.map((option) => {
|
||||||
|
const productOption = productOptionsMap.get(option.option_id!)
|
||||||
|
|
||||||
|
return {
|
||||||
|
option: productOption?.id,
|
||||||
|
value: option.value,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const productVariants = productVariantsMap.get(
|
||||||
|
productVariantData.product_id!
|
||||||
|
)
|
||||||
|
|
||||||
|
if (productVariants) {
|
||||||
|
productVariants.push(productVariantData)
|
||||||
|
} else {
|
||||||
|
productVariantsMap.set(productVariantData.product_id!, [
|
||||||
|
productVariantData,
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const productVariants = (
|
||||||
|
await promiseAll(
|
||||||
|
[...productVariantsMap].map(async ([productId, variants]) => {
|
||||||
|
return await this.productVariantService_.create(
|
||||||
|
productId,
|
||||||
|
variants as unknown as ProductTypes.CreateProductVariantOnlyDTO[],
|
||||||
|
sharedContext
|
||||||
|
)
|
||||||
|
})
|
||||||
|
)
|
||||||
|
).flat()
|
||||||
|
|
||||||
|
return productVariants as unknown as ProductTypes.ProductVariantDTO[]
|
||||||
|
}
|
||||||
|
|
||||||
|
@InjectTransactionManager("baseRepository_")
|
||||||
|
async deleteVariants(
|
||||||
|
productVariantIds: string[],
|
||||||
|
@MedusaContext() sharedContext: Context = {}
|
||||||
|
): Promise<void> {
|
||||||
|
await this.productVariantService_.delete(productVariantIds, sharedContext)
|
||||||
|
|
||||||
|
await this.eventBusModuleService_?.emit<ProductEventData>(
|
||||||
|
productVariantIds.map((id) => ({
|
||||||
|
eventName: ProductEvents.PRODUCT_DELETED,
|
||||||
|
data: { id },
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
@InjectManager("baseRepository_")
|
@InjectManager("baseRepository_")
|
||||||
async retrieveTag(
|
async retrieveTag(
|
||||||
tagId: string,
|
tagId: string,
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
import { Product, ProductVariant } from "@models"
|
|
||||||
import { Context, DAL, FindConfig, ProductTypes } from "@medusajs/types"
|
import { Context, DAL, FindConfig, ProductTypes } from "@medusajs/types"
|
||||||
import { ProductVariantRepository } from "@repositories"
|
|
||||||
import {
|
import {
|
||||||
InjectManager,
|
InjectManager,
|
||||||
InjectTransactionManager,
|
InjectTransactionManager,
|
||||||
isString,
|
|
||||||
MedusaContext,
|
MedusaContext,
|
||||||
ModulesSdkUtils,
|
ModulesSdkUtils,
|
||||||
|
isString,
|
||||||
retrieveEntity,
|
retrieveEntity,
|
||||||
} from "@medusajs/utils"
|
} from "@medusajs/utils"
|
||||||
|
import { Product, ProductVariant } from "@models"
|
||||||
|
import { ProductVariantRepository } from "@repositories"
|
||||||
|
|
||||||
import { ProductVariantServiceTypes } from "../types/services"
|
import { ProductVariantServiceTypes } from "../types/services"
|
||||||
import ProductService from "./product"
|
import ProductService from "./product"
|
||||||
@@ -96,7 +96,7 @@ export default class ProductVariantService<
|
|||||||
if (isString(productOrId)) {
|
if (isString(productOrId)) {
|
||||||
product = await this.productService_.retrieve(
|
product = await this.productService_.retrieve(
|
||||||
productOrId,
|
productOrId,
|
||||||
{},
|
{ relations: ["variants"] },
|
||||||
sharedContext
|
sharedContext
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -105,6 +105,8 @@ export default class ProductVariantService<
|
|||||||
|
|
||||||
const data_ = [...data]
|
const data_ = [...data]
|
||||||
data_.forEach((variant) => {
|
data_.forEach((variant) => {
|
||||||
|
delete variant?.product_id
|
||||||
|
|
||||||
Object.assign(variant, {
|
Object.assign(variant, {
|
||||||
variant_rank: computedRank++,
|
variant_rank: computedRank++,
|
||||||
product,
|
product,
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export enum ProductStatus {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @interface
|
* @interface
|
||||||
*
|
*
|
||||||
* A product's data.
|
* A product's data.
|
||||||
*/
|
*/
|
||||||
export interface ProductDTO {
|
export interface ProductDTO {
|
||||||
@@ -152,7 +152,7 @@ export interface ProductDTO {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @interface
|
* @interface
|
||||||
*
|
*
|
||||||
* A product variant's data.
|
* A product variant's data.
|
||||||
*/
|
*/
|
||||||
export interface ProductVariantDTO {
|
export interface ProductVariantDTO {
|
||||||
@@ -264,7 +264,7 @@ export interface ProductVariantDTO {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @interface
|
* @interface
|
||||||
*
|
*
|
||||||
* A product category's data.
|
* A product category's data.
|
||||||
*/
|
*/
|
||||||
export interface ProductCategoryDTO {
|
export interface ProductCategoryDTO {
|
||||||
@@ -320,7 +320,7 @@ export interface ProductCategoryDTO {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @interface
|
* @interface
|
||||||
*
|
*
|
||||||
* A product category to create.
|
* A product category to create.
|
||||||
*/
|
*/
|
||||||
export interface CreateProductCategoryDTO {
|
export interface CreateProductCategoryDTO {
|
||||||
@@ -356,7 +356,7 @@ export interface CreateProductCategoryDTO {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @interface
|
* @interface
|
||||||
*
|
*
|
||||||
* The data to update in a product category.
|
* The data to update in a product category.
|
||||||
*/
|
*/
|
||||||
export interface UpdateProductCategoryDTO {
|
export interface UpdateProductCategoryDTO {
|
||||||
@@ -392,7 +392,7 @@ export interface UpdateProductCategoryDTO {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @interface
|
* @interface
|
||||||
*
|
*
|
||||||
* A product tag's data.
|
* A product tag's data.
|
||||||
*/
|
*/
|
||||||
export interface ProductTagDTO {
|
export interface ProductTagDTO {
|
||||||
@@ -418,7 +418,7 @@ export interface ProductTagDTO {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @interface
|
* @interface
|
||||||
*
|
*
|
||||||
* A product collection's data.
|
* A product collection's data.
|
||||||
*/
|
*/
|
||||||
export interface ProductCollectionDTO {
|
export interface ProductCollectionDTO {
|
||||||
@@ -452,7 +452,7 @@ export interface ProductCollectionDTO {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @interface
|
* @interface
|
||||||
*
|
*
|
||||||
* A product type's data.
|
* A product type's data.
|
||||||
*/
|
*/
|
||||||
export interface ProductTypeDTO {
|
export interface ProductTypeDTO {
|
||||||
@@ -476,7 +476,7 @@ export interface ProductTypeDTO {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @interface
|
* @interface
|
||||||
*
|
*
|
||||||
* A product option's data.
|
* A product option's data.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@@ -513,8 +513,13 @@ export interface ProductOptionDTO {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @interface
|
* @interface
|
||||||
*
|
*
|
||||||
* The product image's data.
|
* The product image's data.
|
||||||
|
*
|
||||||
|
* @prop id - The ID of the product image.
|
||||||
|
* @prop url - The URL of the product image.
|
||||||
|
* @prop metadata - Holds custom data in key-value pairs.
|
||||||
|
* @prop deleted_at - When the product image was deleted.
|
||||||
*/
|
*/
|
||||||
export interface ProductImageDTO {
|
export interface ProductImageDTO {
|
||||||
/**
|
/**
|
||||||
@@ -537,8 +542,15 @@ export interface ProductImageDTO {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @interface
|
* @interface
|
||||||
*
|
*
|
||||||
* The product option value's data.
|
* The product option value's data.
|
||||||
|
*
|
||||||
|
* @prop id - The ID of the product option value.
|
||||||
|
* @prop value - The value of the product option value.
|
||||||
|
* @prop option - The associated product option. It may only be available if the `option` relation is expanded.
|
||||||
|
* @prop variant - The associated product variant. It may only be available if the `variant` relation is expanded.
|
||||||
|
* @prop metadata - Holds custom data in key-value pairs.
|
||||||
|
* @prop deleted_at - When the product option value was deleted.
|
||||||
*/
|
*/
|
||||||
export interface ProductOptionValueDTO {
|
export interface ProductOptionValueDTO {
|
||||||
/**
|
/**
|
||||||
@@ -573,8 +585,15 @@ export interface ProductOptionValueDTO {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @interface
|
* @interface
|
||||||
*
|
*
|
||||||
* The filters to apply on retrieved products.
|
* The filters to apply on retrieved products.
|
||||||
|
*
|
||||||
|
* @prop q - Search through the products' attributes, such as titles and descriptions, using this search term.
|
||||||
|
* @prop handle - The handles to filter products by.
|
||||||
|
* @prop id - The IDs to filter products by.
|
||||||
|
* @prop tags - Filters on a product's tags.
|
||||||
|
* @prop categories - Filters on a product's categories.
|
||||||
|
* @prop collection_id - Filters a product by its associated collections.
|
||||||
*/
|
*/
|
||||||
export interface FilterableProductProps
|
export interface FilterableProductProps
|
||||||
extends BaseFilterable<FilterableProductProps> {
|
extends BaseFilterable<FilterableProductProps> {
|
||||||
@@ -628,8 +647,11 @@ export interface FilterableProductProps
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @interface
|
* @interface
|
||||||
*
|
*
|
||||||
* The filters to apply on retrieved product tags.
|
* The filters to apply on retrieved product tags.
|
||||||
|
*
|
||||||
|
* @prop id - The IDs to filter product tags by.
|
||||||
|
* @prop value - The value to filter product tags by.
|
||||||
*/
|
*/
|
||||||
export interface FilterableProductTagProps
|
export interface FilterableProductTagProps
|
||||||
extends BaseFilterable<FilterableProductTagProps> {
|
extends BaseFilterable<FilterableProductTagProps> {
|
||||||
@@ -645,8 +667,11 @@ export interface FilterableProductTagProps
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @interface
|
* @interface
|
||||||
*
|
*
|
||||||
* The filters to apply on retrieved product types.
|
* The filters to apply on retrieved product types.
|
||||||
|
*
|
||||||
|
* @prop id - The IDs to filter product types by.
|
||||||
|
* @prop value - The value to filter product types by.
|
||||||
*/
|
*/
|
||||||
export interface FilterableProductTypeProps
|
export interface FilterableProductTypeProps
|
||||||
extends BaseFilterable<FilterableProductTypeProps> {
|
extends BaseFilterable<FilterableProductTypeProps> {
|
||||||
@@ -662,8 +687,12 @@ export interface FilterableProductTypeProps
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @interface
|
* @interface
|
||||||
*
|
*
|
||||||
* The filters to apply on retrieved product options.
|
* The filters to apply on retrieved product options.
|
||||||
|
*
|
||||||
|
* @prop id - The IDs to filter product options by.
|
||||||
|
* @prop title - The titles to filter product options by.
|
||||||
|
* @prop product_id - Filter the product options by their associated products' IDs.
|
||||||
*/
|
*/
|
||||||
export interface FilterableProductOptionProps
|
export interface FilterableProductOptionProps
|
||||||
extends BaseFilterable<FilterableProductOptionProps> {
|
extends BaseFilterable<FilterableProductOptionProps> {
|
||||||
@@ -683,8 +712,11 @@ export interface FilterableProductOptionProps
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @interface
|
* @interface
|
||||||
*
|
*
|
||||||
* The filters to apply on retrieved product collections.
|
* The filters to apply on retrieved product collections.
|
||||||
|
*
|
||||||
|
* @prop id - The IDs to filter product collections by.
|
||||||
|
* @prop title - The title to filter product collections by.
|
||||||
*/
|
*/
|
||||||
export interface FilterableProductCollectionProps
|
export interface FilterableProductCollectionProps
|
||||||
extends BaseFilterable<FilterableProductCollectionProps> {
|
extends BaseFilterable<FilterableProductCollectionProps> {
|
||||||
@@ -704,8 +736,13 @@ export interface FilterableProductCollectionProps
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @interface
|
* @interface
|
||||||
*
|
*
|
||||||
* The filters to apply on retrieved product variants.
|
* The filters to apply on retrieved product variants.
|
||||||
|
*
|
||||||
|
* @prop id - The IDs to filter product variants by.
|
||||||
|
* @prop sku - The SKUs to filter product variants by.
|
||||||
|
* @prop product_id - Filter the product variants by their associated products' IDs.
|
||||||
|
* @prop options - Filter product variants by their associated options.
|
||||||
*/
|
*/
|
||||||
export interface FilterableProductVariantProps
|
export interface FilterableProductVariantProps
|
||||||
extends BaseFilterable<FilterableProductVariantProps> {
|
extends BaseFilterable<FilterableProductVariantProps> {
|
||||||
@@ -734,8 +771,16 @@ export interface FilterableProductVariantProps
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @interface
|
* @interface
|
||||||
*
|
*
|
||||||
* The filters to apply on retrieved product categories.
|
* The filters to apply on retrieved product categories.
|
||||||
|
*
|
||||||
|
* @prop id - The IDs to filter product categories by.
|
||||||
|
* @prop name - The names to filter product categories by.
|
||||||
|
* @prop parent_category_id - Filter product categories by their parent category's ID.
|
||||||
|
* @prop handle - The handles to filter product categories by.
|
||||||
|
* @prop is_active - Filter product categories by whether they're active.
|
||||||
|
* @prop is_internal - Filter product categories by whether they're internal.
|
||||||
|
* @prop include_descendants_tree - Whether to include children of retrieved product categories.
|
||||||
*/
|
*/
|
||||||
export interface FilterableProductCategoryProps
|
export interface FilterableProductCategoryProps
|
||||||
extends BaseFilterable<FilterableProductCategoryProps> {
|
extends BaseFilterable<FilterableProductCategoryProps> {
|
||||||
@@ -771,8 +816,13 @@ export interface FilterableProductCategoryProps
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @interface
|
* @interface
|
||||||
*
|
*
|
||||||
* A product collection to create.
|
* A product collection to create.
|
||||||
|
*
|
||||||
|
* @prop title - The product collection's title.
|
||||||
|
* @prop handle - The product collection's handle. If not provided, the value of this attribute is set to the slug version of the title.
|
||||||
|
* @prop products - The products to associate with the collection.
|
||||||
|
* @prop metadata - Holds custom data in key-value pairs.
|
||||||
*/
|
*/
|
||||||
export interface CreateProductCollectionDTO {
|
export interface CreateProductCollectionDTO {
|
||||||
/**
|
/**
|
||||||
@@ -795,7 +845,7 @@ export interface CreateProductCollectionDTO {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @interface
|
* @interface
|
||||||
*
|
*
|
||||||
* The data to update in a product collection. The `id` is used to identify which product collection to update.
|
* The data to update in a product collection. The `id` is used to identify which product collection to update.
|
||||||
*/
|
*/
|
||||||
export interface UpdateProductCollectionDTO {
|
export interface UpdateProductCollectionDTO {
|
||||||
@@ -827,7 +877,7 @@ export interface UpdateProductCollectionDTO {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @interface
|
* @interface
|
||||||
*
|
*
|
||||||
* A product type to create.
|
* A product type to create.
|
||||||
*/
|
*/
|
||||||
export interface CreateProductTypeDTO {
|
export interface CreateProductTypeDTO {
|
||||||
@@ -852,7 +902,7 @@ export interface UpsertProductTypeDTO {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @interface
|
* @interface
|
||||||
*
|
*
|
||||||
* The data to update in a product type. The `id` is used to identify which product type to update.
|
* The data to update in a product type. The `id` is used to identify which product type to update.
|
||||||
*/
|
*/
|
||||||
export interface UpdateProductTypeDTO {
|
export interface UpdateProductTypeDTO {
|
||||||
@@ -872,7 +922,7 @@ export interface UpdateProductTypeDTO {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @interface
|
* @interface
|
||||||
*
|
*
|
||||||
* A product tag to create.
|
* A product tag to create.
|
||||||
*/
|
*/
|
||||||
export interface CreateProductTagDTO {
|
export interface CreateProductTagDTO {
|
||||||
@@ -888,9 +938,9 @@ export interface UpsertProductTagDTO {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @interface
|
* @interface
|
||||||
*
|
*
|
||||||
* The data to update in a product tag. The `id` is used to identify which product tag to update.
|
* The data to update in a product tag. The `id` is used to identify which product tag to update.
|
||||||
*/
|
*/
|
||||||
export interface UpdateProductTagDTO {
|
export interface UpdateProductTagDTO {
|
||||||
@@ -906,7 +956,7 @@ export interface UpdateProductTagDTO {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @interface
|
* @interface
|
||||||
*
|
*
|
||||||
* A product option to create.
|
* A product option to create.
|
||||||
*/
|
*/
|
||||||
export interface CreateProductOptionDTO {
|
export interface CreateProductOptionDTO {
|
||||||
@@ -928,7 +978,7 @@ export interface UpdateProductOptionDTO {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @interface
|
* @interface
|
||||||
*
|
*
|
||||||
* A product variant option to create.
|
* A product variant option to create.
|
||||||
*/
|
*/
|
||||||
export interface CreateProductVariantOptionDTO {
|
export interface CreateProductVariantOptionDTO {
|
||||||
@@ -936,14 +986,19 @@ export interface CreateProductVariantOptionDTO {
|
|||||||
* The value of a product variant option.
|
* The value of a product variant option.
|
||||||
*/
|
*/
|
||||||
value: string
|
value: string
|
||||||
|
option_id?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @interface
|
* @interface
|
||||||
*
|
*
|
||||||
* A product variant to create.
|
* A product variant to create.
|
||||||
*/
|
*/
|
||||||
export interface CreateProductVariantDTO {
|
export interface CreateProductVariantDTO {
|
||||||
|
/**
|
||||||
|
* The id of the product
|
||||||
|
*/
|
||||||
|
product_id?: string
|
||||||
/**
|
/**
|
||||||
* The tile of the product variant.
|
* The tile of the product variant.
|
||||||
*/
|
*/
|
||||||
@@ -1020,7 +1075,7 @@ export interface CreateProductVariantDTO {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @interface
|
* @interface
|
||||||
*
|
*
|
||||||
* The data to update in a product variant. The `id` is used to identify which product variant to update.
|
* The data to update in a product variant. The `id` is used to identify which product variant to update.
|
||||||
*/
|
*/
|
||||||
export interface UpdateProductVariantDTO {
|
export interface UpdateProductVariantDTO {
|
||||||
@@ -1104,7 +1159,7 @@ export interface UpdateProductVariantDTO {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @interface
|
* @interface
|
||||||
*
|
*
|
||||||
* A product to create.
|
* A product to create.
|
||||||
*/
|
*/
|
||||||
export interface CreateProductDTO {
|
export interface CreateProductDTO {
|
||||||
@@ -1214,7 +1269,7 @@ export interface CreateProductDTO {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @interface
|
* @interface
|
||||||
*
|
*
|
||||||
* The data to update in a product. The `id` is used to identify which product to update.
|
* The data to update in a product. The `id` is used to identify which product to update.
|
||||||
*/
|
*/
|
||||||
export interface UpdateProductDTO {
|
export interface UpdateProductDTO {
|
||||||
@@ -1352,6 +1407,7 @@ export interface CreateProductOnlyDTO {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface CreateProductVariantOnlyDTO {
|
export interface CreateProductVariantOnlyDTO {
|
||||||
|
product_id?: string
|
||||||
title: string
|
title: string
|
||||||
sku?: string
|
sku?: string
|
||||||
barcode?: string
|
barcode?: string
|
||||||
|
|||||||
File diff suppressed because it is too large.
Load diff
@@ -2,3 +2,4 @@ export * as CartWorkflow from "./cart"
|
|||||||
export * as CommonWorkflow from "./common"
|
export * as CommonWorkflow from "./common"
|
||||||
export * as ProductWorkflow from "./product"
|
export * as ProductWorkflow from "./product"
|
||||||
export * as InventoryWorkflow from "./inventory"
|
export * as InventoryWorkflow from "./inventory"
|
||||||
|
export * as PriceListWorkflow from "./price-list"
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
import {
|
||||||
|
CreatePriceListRules,
|
||||||
|
PriceListRuleDTO,
|
||||||
|
PriceListStatus,
|
||||||
|
} from "../../pricing"
|
||||||
|
|
||||||
|
export interface CreatePriceListDTO {
|
||||||
|
starts_at?: string
|
||||||
|
ends_at?: string
|
||||||
|
status?: PriceListStatus
|
||||||
|
number_rules?: number
|
||||||
|
rules?: PriceListRuleDTO[]
|
||||||
|
prices?: {
|
||||||
|
amount: number
|
||||||
|
currency_code: string
|
||||||
|
region_id?: string
|
||||||
|
max_quantity?: number
|
||||||
|
min_quantity?: number
|
||||||
|
}[]
|
||||||
|
customer_groups?: {
|
||||||
|
id: string
|
||||||
|
}[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreatePriceListRuleDTO {
|
||||||
|
rule_attribute: string
|
||||||
|
value: string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreatePriceListPriceDTO {
|
||||||
|
amount: number
|
||||||
|
currency_code: string
|
||||||
|
price_set_id: string | null
|
||||||
|
region_id?: string
|
||||||
|
max_quantity?: number
|
||||||
|
min_quantity?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreatePriceListWorkflowInputDTO {
|
||||||
|
price_lists: CreatePriceListWorkflowDTO[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RemovePriceListProductsWorkflowInputDTO {
|
||||||
|
product_ids: string[]
|
||||||
|
price_list_id: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RemovePriceListVariantsWorkflowInputDTO {
|
||||||
|
variant_ids: string[]
|
||||||
|
price_list_id: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RemovePriceListPricesWorkflowInputDTO {
|
||||||
|
money_amount_ids: string[]
|
||||||
|
price_list_id: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreatePriceListWorkflowDTO {
|
||||||
|
title?: string
|
||||||
|
name: string
|
||||||
|
description: string
|
||||||
|
type?: string
|
||||||
|
starts_at?: string
|
||||||
|
ends_at?: string
|
||||||
|
status?: PriceListStatus
|
||||||
|
number_rules?: number
|
||||||
|
prices: InputPrice[]
|
||||||
|
rules?: CreatePriceListRules
|
||||||
|
}
|
||||||
|
|
||||||
|
interface InputPrice {
|
||||||
|
region_id?: string
|
||||||
|
currency_code: string
|
||||||
|
amount: number
|
||||||
|
variant_id: string
|
||||||
|
min_quantity?: number
|
||||||
|
max_quantity?: number
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export * from "./create-price-list"
|
||||||
|
export * from "./update-price-list"
|
||||||
|
export * from "./remove-price-list"
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export interface RemovePriceListWorkflowInputDTO {
|
||||||
|
price_lists: string[]
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import { CreatePriceListRules, PriceListStatus } from "../../pricing"
|
||||||
|
|
||||||
|
import { UpdateProductVariantPricesInputDTO } from "../product"
|
||||||
|
|
||||||
|
export type PriceListVariantPriceDTO = UpdateProductVariantPricesInputDTO & {
|
||||||
|
variant_id?: string
|
||||||
|
price_set_id?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpdatePriceListWorkflowDTO {
|
||||||
|
id: string
|
||||||
|
name?: string
|
||||||
|
starts_at?: string
|
||||||
|
ends_at?: string
|
||||||
|
status?: PriceListStatus
|
||||||
|
rules?: CreatePriceListRules
|
||||||
|
prices?: PriceListVariantPriceDTO[]
|
||||||
|
customer_groups?: {
|
||||||
|
id: string
|
||||||
|
}[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpdatePriceListWorkflowInputDTO {
|
||||||
|
price_lists: UpdatePriceListWorkflowDTO[]
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import {
|
||||||
|
UpsertProductVariantOptionInputDTO,
|
||||||
|
UpsertProductVariantPricesInputDTO,
|
||||||
|
} from "./update-product-variants"
|
||||||
|
|
||||||
|
export interface CreateProductVariantsInputDTO {
|
||||||
|
product_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 CreateProductVariantsWorkflowInputDTO {
|
||||||
|
productVariants: CreateProductVariantsInputDTO[]
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
export * from "./create-product-variants"
|
||||||
export * from "./create-products"
|
export * from "./create-products"
|
||||||
export * from "./update-product-variants"
|
export * from "./update-product-variants"
|
||||||
export * from "./update-products"
|
export * from "./update-products"
|
||||||
@@ -137,6 +137,8 @@ export abstract class MikroOrmAbstractBaseRepository<T = any>
|
|||||||
retrieveConstraintsToApply: (q: string) => any[]
|
retrieveConstraintsToApply: (q: string) => any[]
|
||||||
): void {
|
): void {
|
||||||
if (!("q" in findOptions.where) || !findOptions.where.q) {
|
if (!("q" in findOptions.where) || !findOptions.where.q) {
|
||||||
|
delete findOptions.where.q
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
export * from "./cart"
|
export * from "./cart"
|
||||||
export * from "./product"
|
export * from "./product"
|
||||||
export * from "./inventory"
|
export * from "./inventory"
|
||||||
|
export * from './price-list'
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
import {
|
||||||
|
TransactionStepsDefinition,
|
||||||
|
WorkflowManager,
|
||||||
|
} from "@medusajs/orchestration"
|
||||||
|
import { PricingTypes, WorkflowTypes } from "@medusajs/types"
|
||||||
|
import { exportWorkflow, pipe } from "../../helper"
|
||||||
|
|
||||||
|
import { Workflows } from "../../definitions"
|
||||||
|
import { PriceListHandlers } from "../../handlers"
|
||||||
|
|
||||||
|
export enum CreatePriceListActions {
|
||||||
|
prepare = "prepare",
|
||||||
|
createPriceList = "createPriceList",
|
||||||
|
}
|
||||||
|
|
||||||
|
const workflowSteps: TransactionStepsDefinition = {
|
||||||
|
next: {
|
||||||
|
action: CreatePriceListActions.prepare,
|
||||||
|
noCompensation: true,
|
||||||
|
next: {
|
||||||
|
action: CreatePriceListActions.createPriceList,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const handlers = new Map([
|
||||||
|
[
|
||||||
|
CreatePriceListActions.prepare,
|
||||||
|
{
|
||||||
|
invoke: pipe(
|
||||||
|
{
|
||||||
|
inputAlias: CreatePriceListActions.prepare,
|
||||||
|
merge: true,
|
||||||
|
invoke: {
|
||||||
|
from: CreatePriceListActions.prepare,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
PriceListHandlers.prepareCreatePriceLists
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
CreatePriceListActions.createPriceList,
|
||||||
|
{
|
||||||
|
invoke: pipe(
|
||||||
|
{
|
||||||
|
invoke: {
|
||||||
|
from: CreatePriceListActions.prepare,
|
||||||
|
alias: PriceListHandlers.createPriceLists.aliases.priceLists,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
PriceListHandlers.createPriceLists
|
||||||
|
),
|
||||||
|
compensate: pipe(
|
||||||
|
{
|
||||||
|
invoke: {
|
||||||
|
from: CreatePriceListActions.createPriceList,
|
||||||
|
alias: PriceListHandlers.removePriceLists.aliases.priceLists,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
PriceListHandlers.removePriceLists
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
])
|
||||||
|
|
||||||
|
WorkflowManager.register(Workflows.CreatePriceList, workflowSteps, handlers)
|
||||||
|
|
||||||
|
export const createPriceLists = exportWorkflow<
|
||||||
|
WorkflowTypes.PriceListWorkflow.CreatePriceListWorkflowInputDTO,
|
||||||
|
{
|
||||||
|
priceList: PricingTypes.CreatePriceListDTO
|
||||||
|
}[]
|
||||||
|
>(Workflows.CreatePriceList, CreatePriceListActions.createPriceList)
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
export * from "./create-price-lists"
|
||||||
|
export * from "./remove-price-lists"
|
||||||
|
export * from "./remove-product-prices"
|
||||||
|
export * from "./remove-variant-prices"
|
||||||
|
export * from "./update-price-lists"
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
import {
|
||||||
|
TransactionStepsDefinition,
|
||||||
|
WorkflowManager,
|
||||||
|
} from "@medusajs/orchestration"
|
||||||
|
import { WorkflowTypes } from "@medusajs/types"
|
||||||
|
import { exportWorkflow, pipe } from "../../helper"
|
||||||
|
|
||||||
|
import { Workflows } from "../../definitions"
|
||||||
|
import { PriceListHandlers } from "../../handlers"
|
||||||
|
|
||||||
|
export enum RemovePriceListPricesActions {
|
||||||
|
prepare = "prepare",
|
||||||
|
removePriceListPrices = "removePriceListPrices",
|
||||||
|
}
|
||||||
|
|
||||||
|
const workflowSteps: TransactionStepsDefinition = {
|
||||||
|
next: {
|
||||||
|
action: RemovePriceListPricesActions.prepare,
|
||||||
|
noCompensation: true,
|
||||||
|
next: {
|
||||||
|
action: RemovePriceListPricesActions.removePriceListPrices,
|
||||||
|
noCompensation: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const handlers = new Map([
|
||||||
|
[
|
||||||
|
RemovePriceListPricesActions.prepare,
|
||||||
|
{
|
||||||
|
invoke: pipe(
|
||||||
|
{
|
||||||
|
inputAlias: RemovePriceListPricesActions.prepare,
|
||||||
|
merge: true,
|
||||||
|
invoke: {
|
||||||
|
from: RemovePriceListPricesActions.prepare,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
PriceListHandlers.prepareRemovePriceListPrices
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
RemovePriceListPricesActions.removePriceListPrices,
|
||||||
|
{
|
||||||
|
invoke: pipe(
|
||||||
|
{
|
||||||
|
merge: true,
|
||||||
|
invoke: {
|
||||||
|
from: RemovePriceListPricesActions.prepare,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
PriceListHandlers.removePrices
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
])
|
||||||
|
|
||||||
|
WorkflowManager.register(
|
||||||
|
Workflows.RemovePriceListPrices,
|
||||||
|
workflowSteps,
|
||||||
|
handlers
|
||||||
|
)
|
||||||
|
|
||||||
|
export const removePriceListProductPrices = exportWorkflow<
|
||||||
|
WorkflowTypes.PriceListWorkflow.RemovePriceListPricesWorkflowInputDTO,
|
||||||
|
string[]
|
||||||
|
>(
|
||||||
|
Workflows.RemovePriceListPrices,
|
||||||
|
RemovePriceListPricesActions.removePriceListPrices
|
||||||
|
)
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
import { WorkflowTypes } from "@medusajs/types"
|
||||||
|
import {
|
||||||
|
TransactionStepsDefinition,
|
||||||
|
WorkflowManager,
|
||||||
|
} from "@medusajs/orchestration"
|
||||||
|
import { exportWorkflow, pipe } from "../../helper"
|
||||||
|
|
||||||
|
import { PriceListHandlers } from "../../handlers"
|
||||||
|
import { Workflows } from "../../definitions"
|
||||||
|
|
||||||
|
export enum RemovePriceListActions {
|
||||||
|
removePriceList = "removePriceList",
|
||||||
|
}
|
||||||
|
|
||||||
|
const workflowSteps: TransactionStepsDefinition = {
|
||||||
|
next: {
|
||||||
|
action: RemovePriceListActions.removePriceList,
|
||||||
|
noCompensation: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const handlers = new Map([
|
||||||
|
[
|
||||||
|
RemovePriceListActions.removePriceList,
|
||||||
|
{
|
||||||
|
invoke: pipe(
|
||||||
|
{
|
||||||
|
inputAlias: RemovePriceListActions.removePriceList,
|
||||||
|
merge: true,
|
||||||
|
invoke: {
|
||||||
|
from: RemovePriceListActions.removePriceList,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
PriceListHandlers.removePriceLists
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
])
|
||||||
|
|
||||||
|
WorkflowManager.register(Workflows.DeletePriceLists, workflowSteps, handlers)
|
||||||
|
|
||||||
|
export const removePriceLists = exportWorkflow<
|
||||||
|
WorkflowTypes.PriceListWorkflow.RemovePriceListWorkflowInputDTO,
|
||||||
|
{
|
||||||
|
price_list_ids: string[]
|
||||||
|
}
|
||||||
|
>(
|
||||||
|
Workflows.DeletePriceLists,
|
||||||
|
RemovePriceListActions.removePriceList,
|
||||||
|
async (data) => {
|
||||||
|
return {
|
||||||
|
price_lists: data.price_lists.map((priceListId) => ({
|
||||||
|
price_list: { id: priceListId },
|
||||||
|
})),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
import {
|
||||||
|
TransactionStepsDefinition,
|
||||||
|
WorkflowManager,
|
||||||
|
} from "@medusajs/orchestration"
|
||||||
|
import { WorkflowTypes } from "@medusajs/types"
|
||||||
|
import { exportWorkflow, pipe } from "../../helper"
|
||||||
|
|
||||||
|
import { Workflows } from "../../definitions"
|
||||||
|
import { PriceListHandlers } from "../../handlers"
|
||||||
|
|
||||||
|
export enum RemoveProductPricesActions {
|
||||||
|
prepare = "prepare",
|
||||||
|
removePriceListPriceSetPrices = "removePriceListPriceSetPrices",
|
||||||
|
}
|
||||||
|
|
||||||
|
const workflowSteps: TransactionStepsDefinition = {
|
||||||
|
next: {
|
||||||
|
action: RemoveProductPricesActions.prepare,
|
||||||
|
noCompensation: true,
|
||||||
|
next: {
|
||||||
|
action: RemoveProductPricesActions.removePriceListPriceSetPrices,
|
||||||
|
noCompensation: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const handlers = new Map([
|
||||||
|
[
|
||||||
|
RemoveProductPricesActions.prepare,
|
||||||
|
{
|
||||||
|
invoke: pipe(
|
||||||
|
{
|
||||||
|
inputAlias: RemoveProductPricesActions.prepare,
|
||||||
|
merge: true,
|
||||||
|
invoke: {
|
||||||
|
from: RemoveProductPricesActions.prepare,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
PriceListHandlers.prepareRemoveProductPrices
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
RemoveProductPricesActions.removePriceListPriceSetPrices,
|
||||||
|
{
|
||||||
|
invoke: pipe(
|
||||||
|
{
|
||||||
|
merge: true,
|
||||||
|
invoke: {
|
||||||
|
from: RemoveProductPricesActions.prepare,
|
||||||
|
alias: PriceListHandlers.createPriceLists.aliases.priceLists,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
PriceListHandlers.removePriceListPriceSetPrices
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
])
|
||||||
|
|
||||||
|
WorkflowManager.register(
|
||||||
|
Workflows.RemovePriceListProductPrices,
|
||||||
|
workflowSteps,
|
||||||
|
handlers
|
||||||
|
)
|
||||||
|
|
||||||
|
export const removePriceListProductPrices = exportWorkflow<
|
||||||
|
WorkflowTypes.PriceListWorkflow.RemovePriceListProductsWorkflowInputDTO,
|
||||||
|
string[]
|
||||||
|
>(
|
||||||
|
Workflows.RemovePriceListProductPrices,
|
||||||
|
RemoveProductPricesActions.removePriceListPriceSetPrices
|
||||||
|
)
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
import {
|
||||||
|
TransactionStepsDefinition,
|
||||||
|
WorkflowManager,
|
||||||
|
} from "@medusajs/orchestration"
|
||||||
|
import { WorkflowTypes } from "@medusajs/types"
|
||||||
|
import { exportWorkflow, pipe } from "../../helper"
|
||||||
|
|
||||||
|
import { Workflows } from "../../definitions"
|
||||||
|
import { PriceListHandlers } from "../../handlers"
|
||||||
|
|
||||||
|
export enum RemoveVariantPricesActions {
|
||||||
|
prepare = "prepare",
|
||||||
|
removePriceListPriceSetPrices = "removePriceListPriceSetPrices",
|
||||||
|
}
|
||||||
|
|
||||||
|
const workflowSteps: TransactionStepsDefinition = {
|
||||||
|
next: {
|
||||||
|
action: RemoveVariantPricesActions.prepare,
|
||||||
|
noCompensation: true,
|
||||||
|
next: {
|
||||||
|
action: RemoveVariantPricesActions.removePriceListPriceSetPrices,
|
||||||
|
noCompensation: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const handlers = new Map([
|
||||||
|
[
|
||||||
|
RemoveVariantPricesActions.prepare,
|
||||||
|
{
|
||||||
|
invoke: pipe(
|
||||||
|
{
|
||||||
|
inputAlias: RemoveVariantPricesActions.prepare,
|
||||||
|
merge: true,
|
||||||
|
invoke: {
|
||||||
|
from: RemoveVariantPricesActions.prepare,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
PriceListHandlers.prepareRemoveVariantPrices
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
RemoveVariantPricesActions.removePriceListPriceSetPrices,
|
||||||
|
{
|
||||||
|
invoke: pipe(
|
||||||
|
{
|
||||||
|
merge: true,
|
||||||
|
invoke: {
|
||||||
|
from: RemoveVariantPricesActions.prepare,
|
||||||
|
alias: PriceListHandlers.createPriceLists.aliases.priceLists,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
PriceListHandlers.removePriceListPriceSetPrices
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
])
|
||||||
|
|
||||||
|
WorkflowManager.register(
|
||||||
|
Workflows.RemovePriceListVariantPrices,
|
||||||
|
workflowSteps,
|
||||||
|
handlers
|
||||||
|
)
|
||||||
|
|
||||||
|
export const removePriceListVariantPrices = exportWorkflow<
|
||||||
|
WorkflowTypes.PriceListWorkflow.RemovePriceListVariantsWorkflowInputDTO,
|
||||||
|
string[]
|
||||||
|
>(
|
||||||
|
Workflows.RemovePriceListVariantPrices,
|
||||||
|
RemoveVariantPricesActions.removePriceListPriceSetPrices
|
||||||
|
)
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
import {
|
||||||
|
TransactionStepsDefinition,
|
||||||
|
WorkflowManager,
|
||||||
|
} from "@medusajs/orchestration"
|
||||||
|
import { WorkflowTypes } from "@medusajs/types"
|
||||||
|
import { exportWorkflow, pipe } from "../../helper"
|
||||||
|
|
||||||
|
import { Workflows } from "../../definitions"
|
||||||
|
import { PriceListHandlers } from "../../handlers"
|
||||||
|
|
||||||
|
export enum UpdatePriceListActions {
|
||||||
|
prepare = "prepare",
|
||||||
|
updatePriceList = "updatePriceList",
|
||||||
|
}
|
||||||
|
|
||||||
|
const workflowSteps: TransactionStepsDefinition = {
|
||||||
|
action: UpdatePriceListActions.prepare,
|
||||||
|
noCompensation: true,
|
||||||
|
next: {
|
||||||
|
next: {
|
||||||
|
noCompensation: true,
|
||||||
|
action: UpdatePriceListActions.updatePriceList,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const handlers = new Map([
|
||||||
|
[
|
||||||
|
UpdatePriceListActions.prepare,
|
||||||
|
{
|
||||||
|
invoke: pipe(
|
||||||
|
{
|
||||||
|
inputAlias: UpdatePriceListActions.prepare,
|
||||||
|
merge: true,
|
||||||
|
invoke: {
|
||||||
|
from: UpdatePriceListActions.prepare,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
PriceListHandlers.prepareUpdatePriceLists
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
UpdatePriceListActions.updatePriceList,
|
||||||
|
{
|
||||||
|
invoke: pipe(
|
||||||
|
{
|
||||||
|
inputAlias: UpdatePriceListActions.updatePriceList,
|
||||||
|
merge: true,
|
||||||
|
invoke: {
|
||||||
|
from: UpdatePriceListActions.prepare,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
PriceListHandlers.updatePriceLists
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
])
|
||||||
|
|
||||||
|
WorkflowManager.register(Workflows.UpdatePriceLists, workflowSteps, handlers)
|
||||||
|
|
||||||
|
export const updatePriceLists = exportWorkflow<
|
||||||
|
WorkflowTypes.PriceListWorkflow.UpdatePriceListWorkflowInputDTO,
|
||||||
|
{ priceList: WorkflowTypes.PriceListWorkflow.UpdatePriceListWorkflowDTO }[]
|
||||||
|
>(Workflows.UpdatePriceLists, UpdatePriceListActions.updatePriceList)
|
||||||
@@ -0,0 +1,121 @@
|
|||||||
|
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 CreateProductVariantsActions {
|
||||||
|
prepare = "prepare",
|
||||||
|
createProductVariants = "createProductVariants",
|
||||||
|
revertProductVariantsCreate = "revertProductVariantsCreate",
|
||||||
|
upsertPrices = "upsertPrices",
|
||||||
|
}
|
||||||
|
|
||||||
|
export const workflowSteps: TransactionStepsDefinition = {
|
||||||
|
next: {
|
||||||
|
action: CreateProductVariantsActions.prepare,
|
||||||
|
noCompensation: true,
|
||||||
|
next: {
|
||||||
|
action: CreateProductVariantsActions.createProductVariants,
|
||||||
|
next: [
|
||||||
|
{
|
||||||
|
action: CreateProductVariantsActions.upsertPrices,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const handlers = new Map([
|
||||||
|
[
|
||||||
|
CreateProductVariantsActions.prepare,
|
||||||
|
{
|
||||||
|
invoke: pipe(
|
||||||
|
{
|
||||||
|
merge: true,
|
||||||
|
inputAlias: InputAlias.ProductVariantsCreateInputData,
|
||||||
|
invoke: {
|
||||||
|
from: InputAlias.ProductVariantsCreateInputData,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ProductHandlers.createProductVariantsPrepareData
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
CreateProductVariantsActions.createProductVariants,
|
||||||
|
{
|
||||||
|
invoke: pipe(
|
||||||
|
{
|
||||||
|
merge: true,
|
||||||
|
invoke: {
|
||||||
|
from: CreateProductVariantsActions.prepare,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ProductHandlers.createProductVariants
|
||||||
|
),
|
||||||
|
compensate: pipe(
|
||||||
|
{
|
||||||
|
merge: true,
|
||||||
|
invoke: [
|
||||||
|
{
|
||||||
|
from: CreateProductVariantsActions.prepare,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
from: CreateProductVariantsActions.createProductVariants,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
ProductHandlers.removeProductVariants
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
CreateProductVariantsActions.upsertPrices,
|
||||||
|
{
|
||||||
|
invoke: pipe(
|
||||||
|
{
|
||||||
|
merge: true,
|
||||||
|
invoke: [
|
||||||
|
{
|
||||||
|
from: CreateProductVariantsActions.createProductVariants,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
ProductHandlers.upsertVariantPrices
|
||||||
|
),
|
||||||
|
compensate: pipe(
|
||||||
|
{
|
||||||
|
merge: true,
|
||||||
|
invoke: [
|
||||||
|
{
|
||||||
|
from: CreateProductVariantsActions.prepare,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
from: CreateProductVariantsActions.upsertPrices,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
ProductHandlers.revertVariantPrices
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
])
|
||||||
|
|
||||||
|
WorkflowManager.register(
|
||||||
|
Workflows.CreateProductVariants,
|
||||||
|
workflowSteps,
|
||||||
|
handlers
|
||||||
|
)
|
||||||
|
|
||||||
|
export const createProductVariants = exportWorkflow<
|
||||||
|
WorkflowTypes.ProductWorkflow.CreateProductVariantsWorkflowInputDTO,
|
||||||
|
ProductTypes.ProductVariantDTO[]
|
||||||
|
>(
|
||||||
|
Workflows.CreateProductVariants,
|
||||||
|
CreateProductVariantsActions.createProductVariants
|
||||||
|
)
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
export * as CreateProductVariants from "./create-product-variants"
|
||||||
export * from "./create-products"
|
export * from "./create-products"
|
||||||
export * as UpdateProductVariants from "./update-product-variants"
|
export * as UpdateProductVariants from "./update-product-variants"
|
||||||
export * from "./update-products"
|
export * from "./update-products"
|
||||||
@@ -4,12 +4,21 @@ export enum Workflows {
|
|||||||
UpdateProducts = "update-products",
|
UpdateProducts = "update-products",
|
||||||
|
|
||||||
// Product Variant workflows
|
// Product Variant workflows
|
||||||
|
CreateProductVariants = "create-product-variants",
|
||||||
UpdateProductVariants = "update-product-variants",
|
UpdateProductVariants = "update-product-variants",
|
||||||
|
|
||||||
// Cart workflows
|
// Cart workflows
|
||||||
CreateCart = "create-cart",
|
CreateCart = "create-cart",
|
||||||
|
|
||||||
CreateInventoryItems = "create-inventory-items",
|
CreateInventoryItems = "create-inventory-items",
|
||||||
|
|
||||||
|
// Price list workflows
|
||||||
|
CreatePriceList = "create-price-list",
|
||||||
|
UpdatePriceLists = "update-price-lists",
|
||||||
|
DeletePriceLists = "delete-price-lists",
|
||||||
|
RemovePriceListProductPrices = "remove-price-list-products",
|
||||||
|
RemovePriceListVariantPrices = "remove-price-list-variants",
|
||||||
|
RemovePriceListPrices = "remove-price-list-prices",
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum InputAlias {
|
export enum InputAlias {
|
||||||
@@ -19,6 +28,7 @@ export enum InputAlias {
|
|||||||
|
|
||||||
ProductVariants = "productVariants",
|
ProductVariants = "productVariants",
|
||||||
ProductVariantsUpdateInputData = "productVariantsUpdateInputData",
|
ProductVariantsUpdateInputData = "productVariantsUpdateInputData",
|
||||||
|
ProductVariantsCreateInputData = "productVariantsCreateInputData",
|
||||||
|
|
||||||
InventoryItems = "inventoryItems",
|
InventoryItems = "inventoryItems",
|
||||||
RemovedInventoryItems = "removedInventoryItems",
|
RemovedInventoryItems = "removedInventoryItems",
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ export * as CommonHandlers from "./common"
|
|||||||
export * as CustomerHandlers from "./customer"
|
export * as CustomerHandlers from "./customer"
|
||||||
export * as InventoryHandlers from "./inventory"
|
export * as InventoryHandlers from "./inventory"
|
||||||
export * as MiddlewaresHandlers from "./middlewares"
|
export * as MiddlewaresHandlers from "./middlewares"
|
||||||
|
export * as PriceListHandlers from "./price-list"
|
||||||
export * as ProductHandlers from "./product"
|
export * as ProductHandlers from "./product"
|
||||||
export * as RegionHandlers from "./region"
|
export * as RegionHandlers from "./region"
|
||||||
export * as SalesChannelHandlers from "./sales-channel"
|
export * as SalesChannelHandlers from "./sales-channel"
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
import {
|
||||||
|
CreatePriceListDTO,
|
||||||
|
IPricingModuleService,
|
||||||
|
PriceListDTO,
|
||||||
|
} from "@medusajs/types"
|
||||||
|
|
||||||
|
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||||
|
import { WorkflowArguments } from "../../helper"
|
||||||
|
|
||||||
|
type Result = {
|
||||||
|
priceList: PriceListDTO
|
||||||
|
}[]
|
||||||
|
|
||||||
|
type Input = {
|
||||||
|
tag?: string
|
||||||
|
priceList: CreatePriceListDTO
|
||||||
|
}[]
|
||||||
|
|
||||||
|
export async function createPriceLists({
|
||||||
|
container,
|
||||||
|
data,
|
||||||
|
}: WorkflowArguments<{
|
||||||
|
priceLists: Input
|
||||||
|
}>): Promise<Result> {
|
||||||
|
const pricingService: IPricingModuleService = container.resolve(
|
||||||
|
ModuleRegistrationName.PRICING
|
||||||
|
)
|
||||||
|
|
||||||
|
return await Promise.all(
|
||||||
|
data.priceLists.map(async (item) => {
|
||||||
|
const [priceList] = await pricingService!.createPriceLists([
|
||||||
|
item.priceList,
|
||||||
|
])
|
||||||
|
|
||||||
|
return { tag: item.tag ?? priceList.id, priceList }
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
createPriceLists.aliases = {
|
||||||
|
priceLists: "priceLists",
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
export * from "./create-price-list"
|
||||||
|
export * from "./prepare-create-price-list"
|
||||||
|
export * from "./prepare-update-price-lists"
|
||||||
|
export * from "./remove-price-list"
|
||||||
|
export * from "./update-price-lists"
|
||||||
|
export * from "./prepare-remove-product-prices"
|
||||||
|
export * from "./remove-price-set-price-list-prices"
|
||||||
|
export * from "./prepare-remove-variant-prices"
|
||||||
|
export * from "./prepare-remove-price-list-prices"
|
||||||
|
export * from "./remove-prices"
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
import { CreatePriceListDTO, PriceListWorkflow } from "@medusajs/types"
|
||||||
|
import { MedusaError } from "@medusajs/utils"
|
||||||
|
import { WorkflowArguments } from "../../helper"
|
||||||
|
|
||||||
|
type Result = {
|
||||||
|
tag?: string
|
||||||
|
priceList: CreatePriceListDTO
|
||||||
|
}[]
|
||||||
|
|
||||||
|
export async function prepareCreatePriceLists({
|
||||||
|
container,
|
||||||
|
data,
|
||||||
|
}: WorkflowArguments<{
|
||||||
|
price_lists: (PriceListWorkflow.CreatePriceListWorkflowDTO & {
|
||||||
|
_associationTag?: string
|
||||||
|
})[]
|
||||||
|
}>): Promise<Result | void> {
|
||||||
|
const remoteQuery = container.resolve("remoteQuery")
|
||||||
|
|
||||||
|
const { price_lists } = data
|
||||||
|
|
||||||
|
const variantIds = price_lists
|
||||||
|
.map((priceList) => priceList.prices.map((price) => price.variant_id))
|
||||||
|
.flat()
|
||||||
|
|
||||||
|
const variables = {
|
||||||
|
variant_id: variantIds,
|
||||||
|
}
|
||||||
|
|
||||||
|
const query = {
|
||||||
|
product_variant_price_set: {
|
||||||
|
__args: variables,
|
||||||
|
fields: ["variant_id", "price_set_id"],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const variantPriceSets = await remoteQuery(query)
|
||||||
|
|
||||||
|
const variantIdPriceSetIdMap: Map<string, string> = new Map(
|
||||||
|
variantPriceSets.map((variantPriceSet) => [
|
||||||
|
variantPriceSet.variant_id,
|
||||||
|
variantPriceSet.price_set_id,
|
||||||
|
])
|
||||||
|
)
|
||||||
|
|
||||||
|
const variantsWithoutPriceSets: string[] = []
|
||||||
|
|
||||||
|
for (const variantId of variantIds) {
|
||||||
|
if (!variantIdPriceSetIdMap.has(variantId)) {
|
||||||
|
variantsWithoutPriceSets.push(variantId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (variantsWithoutPriceSets.length) {
|
||||||
|
throw new MedusaError(
|
||||||
|
MedusaError.Types.INVALID_DATA,
|
||||||
|
`No priceSet exist for variants: ${variantsWithoutPriceSets.join(", ")}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return price_lists.map((priceListDTO) => {
|
||||||
|
priceListDTO.title ??= priceListDTO.name
|
||||||
|
const { _associationTag, name, prices, ...rest } = priceListDTO
|
||||||
|
|
||||||
|
const priceList = rest as CreatePriceListDTO
|
||||||
|
|
||||||
|
priceList.rules ??= {}
|
||||||
|
priceList.prices =
|
||||||
|
prices?.map((price) => {
|
||||||
|
const price_set_id = variantIdPriceSetIdMap.get(price.variant_id)!
|
||||||
|
|
||||||
|
return {
|
||||||
|
currency_code: price.currency_code,
|
||||||
|
amount: price.amount,
|
||||||
|
min_quantity: price.min_quantity,
|
||||||
|
max_quantity: price.max_quantity,
|
||||||
|
price_set_id,
|
||||||
|
}
|
||||||
|
}) ?? []
|
||||||
|
|
||||||
|
return { priceList, tag: _associationTag }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
prepareCreatePriceLists.aliases = {
|
||||||
|
payload: "payload",
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||||
|
import { IPricingModuleService } from "@medusajs/types"
|
||||||
|
import { WorkflowArguments } from "../../helper"
|
||||||
|
|
||||||
|
type Result = {
|
||||||
|
moneyAmountIds: string[]
|
||||||
|
priceListId: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function prepareRemovePriceListPrices({
|
||||||
|
container,
|
||||||
|
data,
|
||||||
|
}: WorkflowArguments<{
|
||||||
|
money_amount_ids: string[]
|
||||||
|
price_list_id: string
|
||||||
|
}>): Promise<Result | void> {
|
||||||
|
const pricingService: IPricingModuleService = container.resolve(
|
||||||
|
ModuleRegistrationName.PRICING
|
||||||
|
)
|
||||||
|
|
||||||
|
const {
|
||||||
|
price_list_id: priceListId,
|
||||||
|
money_amount_ids: moneyAmountIdsToDelete,
|
||||||
|
} = data
|
||||||
|
|
||||||
|
const moneyAmounts = await pricingService.listMoneyAmounts(
|
||||||
|
{ id: moneyAmountIdsToDelete },
|
||||||
|
{
|
||||||
|
relations: [
|
||||||
|
"price_set_money_amount",
|
||||||
|
"price_set_money_amount.price_list",
|
||||||
|
],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const moneyAmountIds = moneyAmounts
|
||||||
|
.filter(
|
||||||
|
(moneyAmount) =>
|
||||||
|
moneyAmount?.price_set_money_amount?.price_list?.id === priceListId
|
||||||
|
)
|
||||||
|
.map((ma) => ma.id)
|
||||||
|
|
||||||
|
return { moneyAmountIds, priceListId }
|
||||||
|
}
|
||||||
|
|
||||||
|
prepareRemovePriceListPrices.aliases = {
|
||||||
|
payload: "payload",
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
import { WorkflowArguments } from "../../helper"
|
||||||
|
import { prepareCreatePriceLists } from "./prepare-create-price-list"
|
||||||
|
|
||||||
|
type Result = {
|
||||||
|
priceSetIds: string[]
|
||||||
|
priceListId: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function prepareRemoveProductPrices({
|
||||||
|
container,
|
||||||
|
data,
|
||||||
|
}: WorkflowArguments<{
|
||||||
|
product_ids: string[]
|
||||||
|
price_list_id: string
|
||||||
|
}>): Promise<Result | void> {
|
||||||
|
const remoteQuery = container.resolve("remoteQuery")
|
||||||
|
|
||||||
|
const { price_list_id, product_ids } = data
|
||||||
|
|
||||||
|
const variables = {
|
||||||
|
id: product_ids,
|
||||||
|
}
|
||||||
|
|
||||||
|
const query = {
|
||||||
|
product: {
|
||||||
|
__args: variables,
|
||||||
|
...defaultAdminProductRemoteQueryObject,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const productsWithVariantPriceSets: QueryResult[] = await remoteQuery(query)
|
||||||
|
|
||||||
|
const priceSetIds = productsWithVariantPriceSets
|
||||||
|
.map(({ variants }) => variants.map(({ price }) => price.price_set_id))
|
||||||
|
.flat()
|
||||||
|
|
||||||
|
return { priceSetIds, priceListId: price_list_id }
|
||||||
|
}
|
||||||
|
|
||||||
|
prepareCreatePriceLists.aliases = {
|
||||||
|
payload: "payload",
|
||||||
|
}
|
||||||
|
|
||||||
|
type QueryResult = {
|
||||||
|
id: string
|
||||||
|
variants: {
|
||||||
|
id: string
|
||||||
|
price: {
|
||||||
|
price_set_id: string
|
||||||
|
variant_id: string
|
||||||
|
}
|
||||||
|
}[]
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultAdminProductRemoteQueryObject = {
|
||||||
|
fields: ["id"],
|
||||||
|
variants: {
|
||||||
|
price: {
|
||||||
|
fields: ["variant_id", "price_set_id"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
import { WorkflowArguments } from "../../helper"
|
||||||
|
import { prepareCreatePriceLists } from "./prepare-create-price-list"
|
||||||
|
|
||||||
|
type Result = {
|
||||||
|
priceSetIds: string[]
|
||||||
|
priceListId: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function prepareRemoveVariantPrices({
|
||||||
|
container,
|
||||||
|
data,
|
||||||
|
}: WorkflowArguments<{
|
||||||
|
variant_ids: string[]
|
||||||
|
price_list_id: string
|
||||||
|
}>): Promise<Result | void> {
|
||||||
|
const remoteQuery = container.resolve("remoteQuery")
|
||||||
|
|
||||||
|
const { price_list_id, variant_ids } = data
|
||||||
|
|
||||||
|
const variables = {
|
||||||
|
variant_id: variant_ids,
|
||||||
|
}
|
||||||
|
|
||||||
|
const query = {
|
||||||
|
product_variant_price_set: {
|
||||||
|
__args: variables,
|
||||||
|
fields: ["variant_id", "price_set_id"],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const productsWithVariantPriceSets: QueryResult[] = await remoteQuery(query)
|
||||||
|
|
||||||
|
const priceSetIds = productsWithVariantPriceSets.map(
|
||||||
|
(variantPriceSet) => variantPriceSet.price_set_id
|
||||||
|
)
|
||||||
|
|
||||||
|
return { priceSetIds, priceListId: price_list_id }
|
||||||
|
}
|
||||||
|
|
||||||
|
prepareCreatePriceLists.aliases = {
|
||||||
|
payload: "payload",
|
||||||
|
}
|
||||||
|
|
||||||
|
type QueryResult = {
|
||||||
|
price_set_id: string
|
||||||
|
variant_id: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultAdminProductRemoteQueryObject = {
|
||||||
|
fields: ["id"],
|
||||||
|
variants: {
|
||||||
|
price: {
|
||||||
|
fields: ["variant_id", "price_set_id"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
import {
|
||||||
|
PriceListPriceDTO,
|
||||||
|
UpdatePriceListDTO,
|
||||||
|
WorkflowTypes,
|
||||||
|
} from "@medusajs/types"
|
||||||
|
import { WorkflowArguments } from "../../helper"
|
||||||
|
|
||||||
|
type Result = {
|
||||||
|
priceLists: UpdatePriceListDTO[]
|
||||||
|
priceListPricesMap: Map<string, PriceListPriceDTO[]>
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function prepareUpdatePriceLists({
|
||||||
|
container,
|
||||||
|
data,
|
||||||
|
}: WorkflowArguments<{
|
||||||
|
price_lists: WorkflowTypes.PriceListWorkflow.UpdatePriceListWorkflowDTO[]
|
||||||
|
}>): Promise<Result> {
|
||||||
|
const { price_lists: priceListsData } = data
|
||||||
|
const remoteQuery = container.resolve("remoteQuery")
|
||||||
|
|
||||||
|
const variantPriceSetMap = new Map<string, string>()
|
||||||
|
const priceListPricesMap = new Map<string, PriceListPriceDTO[]>()
|
||||||
|
|
||||||
|
const variantIds = priceListsData
|
||||||
|
.map((priceListData) => priceListData.prices?.map((p) => p.variant_id))
|
||||||
|
.flat()
|
||||||
|
|
||||||
|
const variables = {
|
||||||
|
variant_id: variantIds,
|
||||||
|
}
|
||||||
|
|
||||||
|
const query = {
|
||||||
|
product_variant_price_set: {
|
||||||
|
__args: variables,
|
||||||
|
fields: ["variant_id", "price_set_id"],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const variantPriceSets = await remoteQuery(query)
|
||||||
|
|
||||||
|
for (const { variant_id, price_set_id } of variantPriceSets) {
|
||||||
|
variantPriceSetMap.set(variant_id, price_set_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
const priceLists = priceListsData.map((priceListData) => {
|
||||||
|
const priceListPrices: PriceListPriceDTO[] = []
|
||||||
|
|
||||||
|
priceListData.prices?.forEach((price) => {
|
||||||
|
const { variant_id, ...priceData } = price
|
||||||
|
if (!variant_id) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
priceListPrices.push({
|
||||||
|
id: priceData.id,
|
||||||
|
price_set_id: variantPriceSetMap.get(variant_id) as string,
|
||||||
|
currency_code: priceData.currency_code as string,
|
||||||
|
amount: priceData.amount,
|
||||||
|
min_quantity: priceData.min_quantity,
|
||||||
|
max_quantity: priceData.max_quantity,
|
||||||
|
})
|
||||||
|
|
||||||
|
return
|
||||||
|
})
|
||||||
|
|
||||||
|
priceListPricesMap.set(priceListData.id, priceListPrices)
|
||||||
|
|
||||||
|
delete priceListData?.prices
|
||||||
|
|
||||||
|
const priceListDataClone: UpdatePriceListDTO = {
|
||||||
|
...priceListData,
|
||||||
|
}
|
||||||
|
|
||||||
|
if (priceListData.name) {
|
||||||
|
priceListDataClone.title = priceListData.name
|
||||||
|
}
|
||||||
|
|
||||||
|
return priceListDataClone
|
||||||
|
})
|
||||||
|
|
||||||
|
return { priceLists, priceListPricesMap }
|
||||||
|
}
|
||||||
|
|
||||||
|
prepareUpdatePriceLists.aliases = {
|
||||||
|
payload: "prepare",
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import { IPricingModuleService, PriceListDTO } from "@medusajs/types"
|
||||||
|
|
||||||
|
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||||
|
import { WorkflowArguments } from "../../helper"
|
||||||
|
|
||||||
|
export async function removePriceLists({
|
||||||
|
container,
|
||||||
|
data,
|
||||||
|
}: WorkflowArguments<{
|
||||||
|
price_lists: {
|
||||||
|
price_list: PriceListDTO
|
||||||
|
}[]
|
||||||
|
}>): Promise<
|
||||||
|
{
|
||||||
|
price_list: PriceListDTO
|
||||||
|
}[]
|
||||||
|
> {
|
||||||
|
const pricingService: IPricingModuleService = container.resolve(
|
||||||
|
ModuleRegistrationName.PRICING
|
||||||
|
)
|
||||||
|
|
||||||
|
await pricingService!.deletePriceLists(
|
||||||
|
data.price_lists.map(({ price_list }) => price_list.id)
|
||||||
|
)
|
||||||
|
|
||||||
|
return data.price_lists
|
||||||
|
}
|
||||||
|
|
||||||
|
removePriceLists.aliases = {
|
||||||
|
priceLists: "priceLists",
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||||
|
import { IPricingModuleService } from "@medusajs/types"
|
||||||
|
import { WorkflowArguments } from "../../helper"
|
||||||
|
import { prepareCreatePriceLists } from "./prepare-create-price-list"
|
||||||
|
|
||||||
|
export async function removePriceListPriceSetPrices({
|
||||||
|
container,
|
||||||
|
data,
|
||||||
|
}: WorkflowArguments<{
|
||||||
|
priceSetIds: string[]
|
||||||
|
priceListId: string
|
||||||
|
}>): Promise<string[]> {
|
||||||
|
const { priceSetIds, priceListId } = data
|
||||||
|
const pricingService: IPricingModuleService = container.resolve(
|
||||||
|
ModuleRegistrationName.PRICING
|
||||||
|
)
|
||||||
|
|
||||||
|
const priceSetMoneyAmounts = await pricingService.listPriceSetMoneyAmounts(
|
||||||
|
{
|
||||||
|
price_set_id: priceSetIds,
|
||||||
|
price_list_id: [priceListId],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
relations: ["money_amount"],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const moneyAmountIDs = priceSetMoneyAmounts
|
||||||
|
.map((priceSetMoneyAmount) => priceSetMoneyAmount.money_amount?.id)
|
||||||
|
.filter((moneyAmountId): moneyAmountId is string => !!moneyAmountId)
|
||||||
|
|
||||||
|
await pricingService.deleteMoneyAmounts(moneyAmountIDs)
|
||||||
|
|
||||||
|
return moneyAmountIDs
|
||||||
|
}
|
||||||
|
|
||||||
|
prepareCreatePriceLists.aliases = {
|
||||||
|
payload: "payload",
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||||
|
import { IPricingModuleService } from "@medusajs/types"
|
||||||
|
import { WorkflowArguments } from "../../helper"
|
||||||
|
|
||||||
|
type Result = {
|
||||||
|
deletedPriceIds: string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function removePrices({
|
||||||
|
container,
|
||||||
|
data,
|
||||||
|
}: WorkflowArguments<{
|
||||||
|
moneyAmountIds: string[]
|
||||||
|
}>): Promise<Result> {
|
||||||
|
const { moneyAmountIds } = data
|
||||||
|
const pricingService: IPricingModuleService = container.resolve(
|
||||||
|
ModuleRegistrationName.PRICING
|
||||||
|
)
|
||||||
|
|
||||||
|
await pricingService.deleteMoneyAmounts(moneyAmountIds)
|
||||||
|
|
||||||
|
return {
|
||||||
|
deletedPriceIds: moneyAmountIds,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
removePrices.aliases = {
|
||||||
|
payload: "payload",
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
import {
|
||||||
|
AddPriceListPricesDTO,
|
||||||
|
IPricingModuleService,
|
||||||
|
PriceListDTO,
|
||||||
|
PriceListPriceDTO,
|
||||||
|
UpdateMoneyAmountDTO,
|
||||||
|
UpdatePriceListDTO,
|
||||||
|
} from "@medusajs/types"
|
||||||
|
|
||||||
|
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||||
|
import { WorkflowArguments } from "../../helper"
|
||||||
|
|
||||||
|
type Result = {
|
||||||
|
priceLists: PriceListDTO[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updatePriceLists({
|
||||||
|
container,
|
||||||
|
data,
|
||||||
|
}: WorkflowArguments<{
|
||||||
|
priceLists: UpdatePriceListDTO[]
|
||||||
|
priceListPricesMap: Map<string, PriceListPriceDTO[]>
|
||||||
|
}>): Promise<Result> {
|
||||||
|
const { priceLists: priceListsData, priceListPricesMap } = data
|
||||||
|
const pricingService: IPricingModuleService = container.resolve(
|
||||||
|
ModuleRegistrationName.PRICING
|
||||||
|
)
|
||||||
|
|
||||||
|
const priceLists = await pricingService.updatePriceLists(priceListsData)
|
||||||
|
const addPriceListPricesData: AddPriceListPricesDTO[] = []
|
||||||
|
const moneyAmountsToUpdate: UpdateMoneyAmountDTO[] = []
|
||||||
|
|
||||||
|
for (const [priceListId, prices] of priceListPricesMap.entries()) {
|
||||||
|
const moneyAmountsToCreate: PriceListPriceDTO[] = []
|
||||||
|
|
||||||
|
for (const price of prices) {
|
||||||
|
if (price.id) {
|
||||||
|
moneyAmountsToUpdate.push(price as UpdateMoneyAmountDTO)
|
||||||
|
} else {
|
||||||
|
moneyAmountsToCreate.push(price)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addPriceListPricesData.push({
|
||||||
|
priceListId,
|
||||||
|
prices: moneyAmountsToCreate,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (addPriceListPricesData.length) {
|
||||||
|
await pricingService.addPriceListPrices(addPriceListPricesData)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (moneyAmountsToUpdate.length) {
|
||||||
|
await pricingService.updateMoneyAmounts(moneyAmountsToUpdate)
|
||||||
|
}
|
||||||
|
|
||||||
|
return { priceLists }
|
||||||
|
}
|
||||||
|
|
||||||
|
updatePriceLists.aliases = {
|
||||||
|
payload: "updatePriceLists",
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
import { 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 CreateProductVariantsPreparedData = {
|
||||||
|
productVariants: ProductWorkflow.CreateProductVariantsInputDTO[]
|
||||||
|
variantIndexPricesMap: Map<number, VariantPrice[]>
|
||||||
|
productVariantsMap: Map<
|
||||||
|
string,
|
||||||
|
ProductWorkflow.CreateProductVariantsInputDTO[]
|
||||||
|
>
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function createProductVariantsPrepareData({
|
||||||
|
container,
|
||||||
|
data,
|
||||||
|
}: WorkflowArguments<WorkflowTypes.ProductWorkflow.CreateProductVariantsWorkflowInputDTO>): Promise<CreateProductVariantsPreparedData> {
|
||||||
|
const featureFlagRouter = container.resolve("featureFlagRouter")
|
||||||
|
const productVariants: ProductWorkflow.CreateProductVariantsInputDTO[] =
|
||||||
|
data.productVariants || []
|
||||||
|
|
||||||
|
const variantIndexPricesMap = new Map<number, VariantPrice[]>()
|
||||||
|
const productVariantsMap = new Map<
|
||||||
|
string,
|
||||||
|
ProductWorkflow.CreateProductVariantsInputDTO[]
|
||||||
|
>()
|
||||||
|
|
||||||
|
for (const [index, productVariantData] of productVariants.entries()) {
|
||||||
|
if (!productVariantData.product_id) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
variantIndexPricesMap.set(index, productVariantData.prices || [])
|
||||||
|
|
||||||
|
delete productVariantData.prices
|
||||||
|
|
||||||
|
const productVariants = productVariantsMap.get(
|
||||||
|
productVariantData.product_id
|
||||||
|
)
|
||||||
|
|
||||||
|
if (productVariants) {
|
||||||
|
productVariants.push(productVariantData)
|
||||||
|
} else {
|
||||||
|
productVariantsMap.set(productVariantData.product_id, [
|
||||||
|
productVariantData,
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
productVariants,
|
||||||
|
variantIndexPricesMap,
|
||||||
|
productVariantsMap,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
createProductVariantsPrepareData.aliases = {
|
||||||
|
payload: "payload",
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
import { Modules, ModulesDefinition } from "@medusajs/modules-sdk"
|
||||||
|
import { ProductTypes } from "@medusajs/types"
|
||||||
|
import { WorkflowArguments } from "../../helper"
|
||||||
|
|
||||||
|
type VariantPrice = {
|
||||||
|
region_id?: string
|
||||||
|
currency_code?: string
|
||||||
|
amount: number
|
||||||
|
min_quantity?: number
|
||||||
|
max_quantity?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
type HandlerInput = {
|
||||||
|
productVariantsMap: Map<string, ProductTypes.CreateProductVariantDTO[]>
|
||||||
|
variantIndexPricesMap: Map<number, VariantPrice[]>
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function createProductVariants({
|
||||||
|
container,
|
||||||
|
data,
|
||||||
|
}: WorkflowArguments<HandlerInput>): Promise<{
|
||||||
|
productVariants: ProductTypes.ProductVariantDTO[]
|
||||||
|
variantPricesMap: Map<string, VariantPrice[]>
|
||||||
|
}> {
|
||||||
|
const { productVariantsMap, variantIndexPricesMap } = data
|
||||||
|
const variantPricesMap = new Map<string, VariantPrice[]>()
|
||||||
|
const productModuleService: ProductTypes.IProductModuleService =
|
||||||
|
container.resolve(ModulesDefinition[Modules.PRODUCT].registrationName)
|
||||||
|
|
||||||
|
const productVariants = await productModuleService.createVariants(
|
||||||
|
[...productVariantsMap.values()].flat()
|
||||||
|
)
|
||||||
|
|
||||||
|
productVariants.forEach((variant, index) => {
|
||||||
|
variantPricesMap.set(variant.id, variantIndexPricesMap.get(index) || [])
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
productVariants,
|
||||||
|
variantPricesMap,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
createProductVariants.aliases = {
|
||||||
|
payload: "payload",
|
||||||
|
}
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
import { ProductTypes, SalesChannelTypes, WorkflowTypes } from "@medusajs/types"
|
import { ProductTypes, SalesChannelTypes, WorkflowTypes } from "@medusajs/types"
|
||||||
import {
|
import {
|
||||||
FeatureFlagUtils,
|
FeatureFlagUtils,
|
||||||
ShippingProfileUtils,
|
|
||||||
kebabCase,
|
kebabCase,
|
||||||
|
ShippingProfileUtils,
|
||||||
} from "@medusajs/utils"
|
} from "@medusajs/utils"
|
||||||
import { WorkflowArguments } from "../../helper"
|
import { WorkflowArguments } from "../../helper"
|
||||||
|
|
||||||
@@ -114,23 +114,17 @@ export async function createProductsPrepareData({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (product.variants) {
|
if (product.variants) {
|
||||||
const hasPrices = product.variants.some((variant) => {
|
const items =
|
||||||
return (variant.prices?.length ?? 0) > 0
|
productsHandleVariantsIndexPricesMap.get(product.handle!) ?? []
|
||||||
|
|
||||||
|
product.variants.forEach((variant, index) => {
|
||||||
|
items.push({
|
||||||
|
index,
|
||||||
|
prices: variant.prices!,
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
if (hasPrices) {
|
productsHandleVariantsIndexPricesMap.set(product.handle!, items)
|
||||||
const items =
|
|
||||||
productsHandleVariantsIndexPricesMap.get(product.handle!) ?? []
|
|
||||||
|
|
||||||
product.variants.forEach((variant, index) => {
|
|
||||||
items.push({
|
|
||||||
index,
|
|
||||||
prices: variant.prices!,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
productsHandleVariantsIndexPricesMap.set(product.handle!, items)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
export * from "./attach-sales-channel-to-products"
|
export * from "./attach-sales-channel-to-products"
|
||||||
export * from "./attach-shipping-profile-to-products"
|
export * from "./attach-shipping-profile-to-products"
|
||||||
|
export * from "./create-product-variants"
|
||||||
|
export * from "./create-product-variants-prepare-data"
|
||||||
export * from "./create-products"
|
export * from "./create-products"
|
||||||
export * from "./create-products-prepare-data"
|
export * from "./create-products-prepare-data"
|
||||||
export * from "./detach-sales-channel-from-products"
|
export * from "./detach-sales-channel-from-products"
|
||||||
export * from "./detach-shipping-profile-from-products"
|
export * from "./detach-shipping-profile-from-products"
|
||||||
export * from "./list-products"
|
export * from "./list-products"
|
||||||
|
export * from "./remove-product-variants"
|
||||||
export * from "./remove-products"
|
export * from "./remove-products"
|
||||||
export * from "./revert-update-products"
|
export * from "./revert-update-products"
|
||||||
export * from "./revert-variant-prices"
|
export * from "./revert-variant-prices"
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import { Modules, ModulesDefinition } from "@medusajs/modules-sdk"
|
||||||
|
import { IProductModuleService } from "@medusajs/types"
|
||||||
|
import { WorkflowArguments } from "../../helper"
|
||||||
|
|
||||||
|
type HandlerInput = { productVariants: { id: string }[] }
|
||||||
|
|
||||||
|
export async function removeProductVariants({
|
||||||
|
container,
|
||||||
|
data,
|
||||||
|
}: WorkflowArguments<HandlerInput>): Promise<void> {
|
||||||
|
if (!data.productVariants.length) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const productModuleService: IProductModuleService = container.resolve(
|
||||||
|
ModulesDefinition[Modules.PRODUCT].registrationName
|
||||||
|
)
|
||||||
|
|
||||||
|
await productModuleService.deleteVariants(
|
||||||
|
data.productVariants.map((p) => p.id)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
removeProductVariants.aliases = {
|
||||||
|
productVariants: "productVariants",
|
||||||
|
}
|
||||||
@@ -23,11 +23,9 @@ type HandlerInput = {
|
|||||||
|
|
||||||
export async function upsertVariantPrices({
|
export async function upsertVariantPrices({
|
||||||
container,
|
container,
|
||||||
context,
|
|
||||||
data,
|
data,
|
||||||
}: WorkflowArguments<HandlerInput>) {
|
}: WorkflowArguments<HandlerInput>) {
|
||||||
const { variantPricesMap } = data
|
const { variantPricesMap } = data
|
||||||
|
|
||||||
const featureFlagRouter = container.resolve("featureFlagRouter")
|
const featureFlagRouter = container.resolve("featureFlagRouter")
|
||||||
|
|
||||||
if (!featureFlagRouter.isFeatureEnabled(MedusaV2Flag.key)) {
|
if (!featureFlagRouter.isFeatureEnabled(MedusaV2Flag.key)) {
|
||||||
@@ -129,16 +127,16 @@ export async function upsertVariantPrices({
|
|||||||
priceSetId = createdPriceSet?.id
|
priceSetId = createdPriceSet?.id
|
||||||
|
|
||||||
createdPriceSets.push(createdPriceSet)
|
createdPriceSets.push(createdPriceSet)
|
||||||
}
|
|
||||||
|
|
||||||
linksToCreate.push({
|
linksToCreate.push({
|
||||||
productService: {
|
productService: {
|
||||||
variant_id: variantId,
|
variant_id: variantId,
|
||||||
},
|
},
|
||||||
pricingService: {
|
pricingService: {
|
||||||
price_set_id: priceSetId,
|
price_set_id: priceSetId,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const createdLinks = await remoteLink.create(linksToCreate)
|
const createdLinks = await remoteLink.create(linksToCreate)
|
||||||
|
|||||||
Reference in new issue
Block a user