feat(core-flows,medusa,pricing,types,utils): added price list workflows + endpoints (#6648)
Apologies for the giant PR in advance, but most of these are removing of files and migrations from old workflows and routes to new. What: - Adds CRUD endpoints for price lists - Migrate workflows from old sdk to new - Added missing updatePriceListPrices method to pricing module
This commit is contained in:
7
.changeset/blue-taxis-bathe.md
Normal file
7
.changeset/blue-taxis-bathe.md
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
"@medusajs/core-flows": patch
|
||||
"@medusajs/medusa": patch
|
||||
"@medusajs/utils": patch
|
||||
---
|
||||
|
||||
feat(core-flows,medusa,utils): added price list prices upsert and delete workflows + endpoints
|
||||
9
.changeset/nasty-waves-sparkle.md
Normal file
9
.changeset/nasty-waves-sparkle.md
Normal file
@@ -0,0 +1,9 @@
|
||||
---
|
||||
"@medusajs/core-flows": patch
|
||||
"@medusajs/pricing": patch
|
||||
"@medusajs/medusa": patch
|
||||
"@medusajs/types": patch
|
||||
"@medusajs/utils": patch
|
||||
---
|
||||
|
||||
feat(core-flows,medusa,pricing,types,utils): added price list workflows + endpoints
|
||||
@@ -1,175 +0,0 @@
|
||||
import {
|
||||
simpleCustomerGroupFactory,
|
||||
simpleProductFactory,
|
||||
simpleRegionFactory,
|
||||
} from "../../../../factories"
|
||||
|
||||
import { IPricingModuleService } from "@medusajs/types"
|
||||
import adminSeeder from "../../../../helpers/admin-seeder"
|
||||
import { createDefaultRuleTypes } from "../../../helpers/create-default-rule-types"
|
||||
import { createVariantPriceSet } from "../../../helpers/create-variant-price-set"
|
||||
import { medusaIntegrationTestRunner } from "medusa-test-utils"
|
||||
|
||||
jest.setTimeout(50000)
|
||||
|
||||
const adminHeaders = {
|
||||
headers: {
|
||||
"x-medusa-access-token": "test_token",
|
||||
},
|
||||
}
|
||||
|
||||
const env = {
|
||||
MEDUSA_FF_MEDUSA_V2: true,
|
||||
}
|
||||
|
||||
medusaIntegrationTestRunner({
|
||||
env,
|
||||
testSuite: ({ dbConnection, getContainer, api }) => {
|
||||
describe.skip("POST /admin/price-lists", () => {
|
||||
let appContainer
|
||||
let product
|
||||
let variant
|
||||
let pricingModuleService: IPricingModuleService
|
||||
|
||||
beforeAll(async () => {
|
||||
appContainer = getContainer()
|
||||
pricingModuleService = appContainer.resolve("pricingModuleService")
|
||||
})
|
||||
|
||||
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]
|
||||
})
|
||||
|
||||
it("should create price list and money amounts", async () => {
|
||||
await createVariantPriceSet({
|
||||
container: appContainer,
|
||||
variantId: variant.id,
|
||||
prices: [
|
||||
{
|
||||
amount: 3000,
|
||||
currency_code: "usd",
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
const data = {
|
||||
name: "test price list",
|
||||
description: "test",
|
||||
type: "override",
|
||||
customer_groups: [{ id: "customer-group-1" }],
|
||||
status: "active",
|
||||
starts_at: new Date(),
|
||||
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: expect.any(String),
|
||||
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),
|
||||
}),
|
||||
],
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
},
|
||||
})
|
||||
@@ -1,128 +0,0 @@
|
||||
import {
|
||||
simpleProductFactory,
|
||||
simpleRegionFactory,
|
||||
} from "../../../../factories"
|
||||
|
||||
import { IPricingModuleService } from "@medusajs/types"
|
||||
import adminSeeder from "../../../../helpers/admin-seeder"
|
||||
import { createDefaultRuleTypes } from "../../../helpers/create-default-rule-types"
|
||||
import { createVariantPriceSet } from "../../../helpers/create-variant-price-set"
|
||||
import { medusaIntegrationTestRunner } from "medusa-test-utils"
|
||||
|
||||
jest.setTimeout(50000)
|
||||
|
||||
const adminHeaders = {
|
||||
headers: {
|
||||
"x-medusa-access-token": "test_token",
|
||||
},
|
||||
}
|
||||
|
||||
const env = {
|
||||
MEDUSA_FF_MEDUSA_V2: true,
|
||||
}
|
||||
|
||||
medusaIntegrationTestRunner({
|
||||
env,
|
||||
testSuite: ({ dbConnection, getContainer, api }) => {
|
||||
describe.skip("DELETE /admin/price-lists/:id", () => {
|
||||
let appContainer
|
||||
let product
|
||||
let variant
|
||||
let pricingModuleService: IPricingModuleService
|
||||
|
||||
beforeAll(async () => {
|
||||
appContainer = getContainer()
|
||||
pricingModuleService = appContainer.resolve("pricingModuleService")
|
||||
})
|
||||
|
||||
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]
|
||||
})
|
||||
|
||||
it("should delete price list and money amounts", async () => {
|
||||
const priceSet = await createVariantPriceSet({
|
||||
container: appContainer,
|
||||
variantId: variant.id,
|
||||
prices: [
|
||||
{
|
||||
amount: 3000,
|
||||
currency_code: "usd",
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
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)
|
||||
})
|
||||
})
|
||||
},
|
||||
})
|
||||
@@ -1,184 +0,0 @@
|
||||
import { simpleProductFactory } from "../../../../factories"
|
||||
|
||||
import {
|
||||
IPricingModuleService,
|
||||
PriceListStatus,
|
||||
PriceListType,
|
||||
} from "@medusajs/types"
|
||||
import adminSeeder from "../../../../helpers/admin-seeder"
|
||||
import { createVariantPriceSet } from "../../../helpers/create-variant-price-set"
|
||||
import { medusaIntegrationTestRunner } from "medusa-test-utils"
|
||||
|
||||
jest.setTimeout(50000)
|
||||
|
||||
const adminHeaders = {
|
||||
headers: {
|
||||
"x-medusa-access-token": "test_token",
|
||||
},
|
||||
}
|
||||
|
||||
const env = {
|
||||
MEDUSA_FF_MEDUSA_V2: true,
|
||||
}
|
||||
|
||||
medusaIntegrationTestRunner({
|
||||
env,
|
||||
testSuite: ({ dbConnection, getContainer, api }) => {
|
||||
describe.skip("GET /admin/price-lists/:id", () => {
|
||||
let appContainer
|
||||
let product
|
||||
let variant
|
||||
let pricingModuleService: IPricingModuleService
|
||||
|
||||
beforeAll(async () => {
|
||||
appContainer = getContainer()
|
||||
pricingModuleService = appContainer.resolve("pricingModuleService")
|
||||
})
|
||||
|
||||
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]
|
||||
})
|
||||
|
||||
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: [],
|
||||
})
|
||||
|
||||
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 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 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",
|
||||
})
|
||||
})
|
||||
})
|
||||
},
|
||||
})
|
||||
@@ -1,152 +0,0 @@
|
||||
import { simpleProductFactory } from "../../../../factories"
|
||||
|
||||
import {
|
||||
IPricingModuleService,
|
||||
PriceListStatus,
|
||||
PriceListType,
|
||||
} from "@medusajs/types"
|
||||
import adminSeeder from "../../../../helpers/admin-seeder"
|
||||
import { createVariantPriceSet } from "../../../helpers/create-variant-price-set"
|
||||
import { medusaIntegrationTestRunner } from "medusa-test-utils"
|
||||
|
||||
jest.setTimeout(50000)
|
||||
|
||||
const adminHeaders = {
|
||||
headers: {
|
||||
"x-medusa-access-token": "test_token",
|
||||
},
|
||||
}
|
||||
|
||||
const env = {
|
||||
MEDUSA_FF_MEDUSA_V2: true,
|
||||
}
|
||||
|
||||
medusaIntegrationTestRunner({
|
||||
env,
|
||||
testSuite: ({ dbConnection, getContainer, api }) => {
|
||||
describe.skip("GET /admin/price-lists", () => {
|
||||
let appContainer
|
||||
let product
|
||||
let variant
|
||||
let pricingModuleService: IPricingModuleService
|
||||
|
||||
beforeAll(async () => {
|
||||
appContainer = getContainer()
|
||||
pricingModuleService = appContainer.resolve("pricingModuleService")
|
||||
})
|
||||
|
||||
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]
|
||||
})
|
||||
|
||||
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: [],
|
||||
})
|
||||
|
||||
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 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),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
])
|
||||
})
|
||||
})
|
||||
},
|
||||
})
|
||||
@@ -258,6 +258,351 @@ medusaIntegrationTestRunner({
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("POST /admin/price-lists", () => {
|
||||
it("should create price list and money amounts", async () => {
|
||||
await createVariantPriceSet({
|
||||
container: appContainer,
|
||||
variantId: variant.id,
|
||||
prices: [{ amount: 3000, currency_code: "usd" }],
|
||||
})
|
||||
|
||||
const data = {
|
||||
title: "test price list",
|
||||
description: "test",
|
||||
type: "override",
|
||||
status: "active",
|
||||
starts_at: new Date(),
|
||||
rules: {
|
||||
customer_group_id: [customerGroup.id],
|
||||
},
|
||||
prices: [
|
||||
{
|
||||
amount: 400,
|
||||
variant_id: variant.id,
|
||||
currency_code: "usd",
|
||||
rules: { region_id: region.id },
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
const response = await api.post(
|
||||
`admin/price-lists`,
|
||||
data,
|
||||
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),
|
||||
title: "test price list",
|
||||
description: "test",
|
||||
type: "override",
|
||||
status: "active",
|
||||
starts_at: expect.any(String),
|
||||
ends_at: null,
|
||||
rules: {
|
||||
customer_group_id: [customerGroup.id],
|
||||
},
|
||||
prices: [
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
currency_code: "usd",
|
||||
amount: 400,
|
||||
min_quantity: null,
|
||||
max_quantity: null,
|
||||
variant_id: variant.id,
|
||||
rules: {
|
||||
region_id: region.id,
|
||||
},
|
||||
}),
|
||||
],
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("should throw error when required attributes are not provided", async () => {
|
||||
const data = {
|
||||
prices: [
|
||||
{
|
||||
amount: 400,
|
||||
currency_code: "usd",
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
const errorResponse = await api
|
||||
.post(`admin/price-lists`, data, adminHeaders)
|
||||
.catch((e) => e)
|
||||
|
||||
expect(errorResponse.response.status).toEqual(400)
|
||||
expect(errorResponse.response.data.message).toEqual(
|
||||
"title must be a string, description must be a string, type must be one of the following values: sale, override, variant_id must be a string"
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("DELETE /admin/price-lists/:id", () => {
|
||||
it("should delete price list and money amounts", async () => {
|
||||
await createVariantPriceSet({
|
||||
container: appContainer,
|
||||
variantId: variant.id,
|
||||
prices: [{ amount: 3000, currency_code: "usd" }],
|
||||
})
|
||||
|
||||
const data = {
|
||||
title: "test price list",
|
||||
description: "test",
|
||||
type: "override",
|
||||
status: "active",
|
||||
starts_at: new Date(),
|
||||
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 pricingModule.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((e) => e)
|
||||
|
||||
expect(afterDelete.response.status).toEqual(404)
|
||||
|
||||
psmas = await pricingModule.listPriceSetMoneyAmounts({
|
||||
price_list_id: [priceListId],
|
||||
})
|
||||
expect(psmas.length).toEqual(0)
|
||||
})
|
||||
|
||||
it("should idempotently return a success even if price lists dont exist", async () => {
|
||||
const deleteRes = await api.delete(
|
||||
`/admin/price-lists/does-not-exist`,
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
expect(deleteRes.status).toEqual(200)
|
||||
expect(deleteRes.data).toEqual({
|
||||
id: "does-not-exist",
|
||||
object: "price_list",
|
||||
deleted: true,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("POST /admin/price-lists/:id", () => {
|
||||
it("should throw error when trying to update a price list that does not exist", async () => {
|
||||
const updateRes = await api
|
||||
.post(
|
||||
`admin/price-lists/does-not-exist`,
|
||||
{ title: "new price list name" },
|
||||
adminHeaders
|
||||
)
|
||||
.catch((e) => e)
|
||||
|
||||
expect(updateRes.response.status).toEqual(404)
|
||||
expect(updateRes.response.data.message).toEqual(
|
||||
"Price lists with id: does-not-exist was not found"
|
||||
)
|
||||
})
|
||||
|
||||
it("should update price lists successfully", async () => {
|
||||
await createVariantPriceSet({
|
||||
container: appContainer,
|
||||
variantId: variant.id,
|
||||
prices: [{ amount: 3000, currency_code: "usd" }],
|
||||
})
|
||||
|
||||
const [priceList] = await pricingModule.createPriceLists([
|
||||
{
|
||||
title: "test price list",
|
||||
description: "test",
|
||||
ends_at: new Date(),
|
||||
starts_at: new Date(),
|
||||
status: PriceListStatus.ACTIVE,
|
||||
type: PriceListType.OVERRIDE,
|
||||
},
|
||||
])
|
||||
|
||||
const data = {
|
||||
title: "new price list name",
|
||||
description: "new price list description",
|
||||
rules: {
|
||||
customer_group_id: [customerGroup.id],
|
||||
},
|
||||
prices: [
|
||||
{
|
||||
amount: 400,
|
||||
variant_id: variant.id,
|
||||
currency_code: "usd",
|
||||
rules: { region_id: region.id },
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
const response = await api.post(
|
||||
`admin/price-lists/${priceList.id}`,
|
||||
data,
|
||||
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),
|
||||
title: "new price list name",
|
||||
description: "new price list description",
|
||||
type: "override",
|
||||
status: "active",
|
||||
starts_at: expect.any(String),
|
||||
ends_at: expect.any(String),
|
||||
rules: {
|
||||
customer_group_id: [customerGroup.id],
|
||||
},
|
||||
prices: [
|
||||
{
|
||||
id: expect.any(String),
|
||||
currency_code: "usd",
|
||||
amount: 400,
|
||||
min_quantity: null,
|
||||
max_quantity: null,
|
||||
variant_id: variant.id,
|
||||
rules: {
|
||||
region_id: region.id,
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("POST /admin/price-lists/:id/prices", () => {
|
||||
it("should upsert price list prices successfully", async () => {
|
||||
const priceSet = await createVariantPriceSet({
|
||||
container: appContainer,
|
||||
variantId: variant.id,
|
||||
prices: [{ amount: 3000, currency_code: "usd" }],
|
||||
})
|
||||
|
||||
const [priceList] = await pricingModule.createPriceLists([
|
||||
{
|
||||
title: "test price list",
|
||||
description: "test",
|
||||
prices: [
|
||||
{
|
||||
id: "test-price-id",
|
||||
amount: 5000,
|
||||
currency_code: "usd",
|
||||
price_set_id: priceSet.id,
|
||||
rules: { region_id: region.id },
|
||||
},
|
||||
],
|
||||
},
|
||||
])
|
||||
|
||||
const data = {
|
||||
prices: [
|
||||
{
|
||||
amount: 400,
|
||||
variant_id: variant.id,
|
||||
currency_code: "usd",
|
||||
rules: { region_id: region.id },
|
||||
},
|
||||
{
|
||||
id: "test-price-id",
|
||||
variant_id: variant.id,
|
||||
amount: 200,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
const response = await api.post(
|
||||
`admin/price-lists/${priceList.id}/prices`,
|
||||
data,
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
expect(response.status).toEqual(200)
|
||||
expect(response.data.price_list.prices.length).toEqual(2)
|
||||
expect(response.data.price_list).toEqual(
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
prices: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
currency_code: "usd",
|
||||
amount: 400,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: "test-price-id",
|
||||
currency_code: "usd",
|
||||
amount: 200,
|
||||
}),
|
||||
]),
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("DELETE /admin/price-lists/:id/prices", () => {
|
||||
it("should delete price list prices", async () => {
|
||||
const priceSet = await createVariantPriceSet({
|
||||
container: appContainer,
|
||||
variantId: variant.id,
|
||||
prices: [],
|
||||
})
|
||||
|
||||
const [priceList] = await pricingModule.createPriceLists([
|
||||
{
|
||||
title: "test price list",
|
||||
description: "test",
|
||||
prices: [
|
||||
{
|
||||
id: "test-price-id",
|
||||
amount: 5000,
|
||||
currency_code: "usd",
|
||||
price_set_id: priceSet.id,
|
||||
rules: {
|
||||
region_id: region.id,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
])
|
||||
|
||||
let response = await api.delete(
|
||||
`/admin/price-lists/${priceList.id}/prices`,
|
||||
{ ...adminHeaders, data: { ids: ["test-price-id"] } }
|
||||
)
|
||||
|
||||
expect(response.status).toEqual(200)
|
||||
expect(response.data).toEqual({
|
||||
ids: ["test-price-id"],
|
||||
object: "price_list_prices",
|
||||
deleted: true,
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
@@ -1,265 +0,0 @@
|
||||
import {
|
||||
simpleCustomerGroupFactory,
|
||||
simpleProductFactory,
|
||||
simpleRegionFactory,
|
||||
} from "../../../../factories"
|
||||
|
||||
import {
|
||||
IPricingModuleService,
|
||||
PriceListStatus,
|
||||
PriceListType,
|
||||
} from "@medusajs/types"
|
||||
import adminSeeder from "../../../../helpers/admin-seeder"
|
||||
import { createDefaultRuleTypes } from "../../../helpers/create-default-rule-types"
|
||||
import { createVariantPriceSet } from "../../../helpers/create-variant-price-set"
|
||||
import { medusaIntegrationTestRunner } from "medusa-test-utils"
|
||||
|
||||
jest.setTimeout(50000)
|
||||
|
||||
const adminHeaders = {
|
||||
headers: {
|
||||
"x-medusa-access-token": "test_token",
|
||||
},
|
||||
}
|
||||
|
||||
const env = {
|
||||
MEDUSA_FF_MEDUSA_V2: true,
|
||||
}
|
||||
|
||||
medusaIntegrationTestRunner({
|
||||
env,
|
||||
testSuite: ({ dbConnection, getContainer, api }) => {
|
||||
describe.skip("POST /admin/price-lists/:id", () => {
|
||||
let appContainer
|
||||
let product
|
||||
let variant
|
||||
let variant2
|
||||
let pricingModuleService: IPricingModuleService
|
||||
|
||||
beforeAll(async () => {
|
||||
appContainer = getContainer()
|
||||
pricingModuleService = appContainer.resolve("pricingModuleService")
|
||||
})
|
||||
|
||||
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]
|
||||
})
|
||||
|
||||
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",
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
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,
|
||||
}),
|
||||
]),
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("should not delete customer groups if customer_groups is not passed as a param", async () => {
|
||||
await createVariantPriceSet({
|
||||
container: appContainer,
|
||||
variantId: variant2.id,
|
||||
prices: [],
|
||||
})
|
||||
|
||||
const [priceList] = await pricingModuleService.createPriceLists([
|
||||
{
|
||||
title: "test price list",
|
||||
description: "test",
|
||||
status: PriceListStatus.DRAFT,
|
||||
rules: {
|
||||
customer_group_id: ["customer-group-2"],
|
||||
},
|
||||
prices: [],
|
||||
},
|
||||
])
|
||||
|
||||
await createVariantPriceSet({
|
||||
container: appContainer,
|
||||
variantId: variant.id,
|
||||
prices: [],
|
||||
})
|
||||
|
||||
const data = {
|
||||
status: PriceListStatus.ACTIVE,
|
||||
}
|
||||
|
||||
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),
|
||||
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,
|
||||
},
|
||||
],
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
},
|
||||
})
|
||||
@@ -2,5 +2,4 @@ export * from "./cart"
|
||||
export * from "./inventory"
|
||||
export * from "./line-item"
|
||||
export * from "./payment-collection"
|
||||
export * from "./price-list"
|
||||
export * from "./product"
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
import {
|
||||
TransactionStepsDefinition,
|
||||
WorkflowManager,
|
||||
} from "@medusajs/orchestration"
|
||||
import { PricingTypes, WorkflowTypes } from "@medusajs/types"
|
||||
import { exportWorkflow, pipe } from "@medusajs/workflows-sdk"
|
||||
|
||||
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)
|
||||
@@ -1,71 +0,0 @@
|
||||
import {
|
||||
TransactionStepsDefinition,
|
||||
WorkflowManager,
|
||||
} from "@medusajs/orchestration"
|
||||
import { WorkflowTypes } from "@medusajs/types"
|
||||
import { exportWorkflow, pipe } from "@medusajs/workflows-sdk"
|
||||
|
||||
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 removePriceListPrices = exportWorkflow<
|
||||
WorkflowTypes.PriceListWorkflow.RemovePriceListPricesWorkflowInputDTO,
|
||||
string[]
|
||||
>(
|
||||
Workflows.RemovePriceListPrices,
|
||||
RemovePriceListPricesActions.removePriceListPrices
|
||||
)
|
||||
@@ -1,57 +0,0 @@
|
||||
import { WorkflowTypes } from "@medusajs/types"
|
||||
import {
|
||||
TransactionStepsDefinition,
|
||||
WorkflowManager,
|
||||
} from "@medusajs/orchestration"
|
||||
import { exportWorkflow, pipe } from "@medusajs/workflows-sdk"
|
||||
|
||||
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 },
|
||||
})),
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -1,72 +0,0 @@
|
||||
import {
|
||||
TransactionStepsDefinition,
|
||||
WorkflowManager,
|
||||
} from "@medusajs/orchestration"
|
||||
import { WorkflowTypes } from "@medusajs/types"
|
||||
import { exportWorkflow, pipe } from "@medusajs/workflows-sdk"
|
||||
|
||||
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
|
||||
)
|
||||
@@ -1,72 +0,0 @@
|
||||
import {
|
||||
TransactionStepsDefinition,
|
||||
WorkflowManager,
|
||||
} from "@medusajs/orchestration"
|
||||
import { WorkflowTypes } from "@medusajs/types"
|
||||
import { exportWorkflow, pipe } from "@medusajs/workflows-sdk"
|
||||
|
||||
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
|
||||
)
|
||||
@@ -1,65 +0,0 @@
|
||||
import {
|
||||
TransactionStepsDefinition,
|
||||
WorkflowManager,
|
||||
} from "@medusajs/orchestration"
|
||||
import { WorkflowTypes } from "@medusajs/types"
|
||||
import { exportWorkflow, pipe } from "@medusajs/workflows-sdk"
|
||||
|
||||
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)
|
||||
@@ -3,7 +3,6 @@ export * as CommonHandlers from "./common"
|
||||
export * as CustomerHandlers from "./customer"
|
||||
export * as InventoryHandlers from "./inventory"
|
||||
export * as MiddlewaresHandlers from "./middlewares"
|
||||
export * as PriceListHandlers from "./price-list"
|
||||
export * as ProductHandlers from "./product"
|
||||
export * as RegionHandlers from "./region"
|
||||
export * as SalesChannelHandlers from "./sales-channel"
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
import {
|
||||
CreatePriceListDTO,
|
||||
IPricingModuleService,
|
||||
PriceListDTO,
|
||||
} from "@medusajs/types"
|
||||
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import { WorkflowArguments } from "@medusajs/workflows-sdk"
|
||||
|
||||
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",
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
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"
|
||||
@@ -1,108 +0,0 @@
|
||||
import { CreatePriceListDTO, PriceListWorkflow } from "@medusajs/types"
|
||||
import { MedusaError } from "@medusajs/utils"
|
||||
import { WorkflowArguments } from "@medusajs/workflows-sdk"
|
||||
|
||||
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 regionService = container.resolve("regionService")
|
||||
|
||||
const { price_lists } = data
|
||||
|
||||
const variantIds = price_lists
|
||||
.map((priceList) => priceList.prices.map((price) => price.variant_id))
|
||||
.flat()
|
||||
|
||||
const variables = {
|
||||
variant_id: variantIds,
|
||||
take: null,
|
||||
}
|
||||
|
||||
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(", ")}`
|
||||
)
|
||||
}
|
||||
|
||||
const regionIds = price_lists
|
||||
.map(({ prices }) => prices?.map((price) => price.region_id) ?? [])
|
||||
.flat(2)
|
||||
const regions = await regionService.list({ id: regionIds }, {})
|
||||
const regionIdCurrencyCodeMap: Map<string, string> = new Map(
|
||||
regions.map((region: { id: string; currency_code: string }) => [
|
||||
region.id,
|
||||
region.currency_code,
|
||||
])
|
||||
)
|
||||
|
||||
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)!
|
||||
|
||||
const rules: Record<string, string> = {}
|
||||
if (price.region_id) {
|
||||
rules.region_id = price.region_id
|
||||
}
|
||||
|
||||
return {
|
||||
currency_code:
|
||||
regionIdCurrencyCodeMap.get(price.region_id as string) ??
|
||||
(price.currency_code as string),
|
||||
amount: price.amount,
|
||||
min_quantity: price.min_quantity,
|
||||
max_quantity: price.max_quantity,
|
||||
price_set_id,
|
||||
rules,
|
||||
}
|
||||
}) ?? []
|
||||
|
||||
return { priceList, tag: _associationTag }
|
||||
})
|
||||
}
|
||||
|
||||
prepareCreatePriceLists.aliases = {
|
||||
payload: "payload",
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import { IPricingModuleService } from "@medusajs/types"
|
||||
import { WorkflowArguments } from "@medusajs/workflows-sdk"
|
||||
|
||||
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",
|
||||
],
|
||||
take: null,
|
||||
}
|
||||
)
|
||||
|
||||
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",
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
import { WorkflowArguments } from "@medusajs/workflows-sdk"
|
||||
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,
|
||||
take: null,
|
||||
}
|
||||
|
||||
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"],
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
import { WorkflowArguments } from "@medusajs/workflows-sdk"
|
||||
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,
|
||||
take: null,
|
||||
}
|
||||
|
||||
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"],
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
import {
|
||||
PriceListPriceDTO,
|
||||
UpdatePriceListDTO,
|
||||
WorkflowTypes,
|
||||
} from "@medusajs/types"
|
||||
import { WorkflowArguments } from "@medusajs/workflows-sdk"
|
||||
|
||||
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 regionService = container.resolve("regionService")
|
||||
|
||||
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,
|
||||
take: null,
|
||||
}
|
||||
|
||||
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 regionIds = priceListsData
|
||||
.map(({ prices }) => prices?.map((price) => price.region_id) ?? [])
|
||||
.flat(2)
|
||||
const regions = await regionService.list({ id: regionIds })
|
||||
const regionsMap: Map<string, string> = new Map(
|
||||
regions.map((region: { id: string; currency_code: string }) => [
|
||||
region.id,
|
||||
region.currency_code,
|
||||
])
|
||||
)
|
||||
|
||||
const priceLists = priceListsData.map((priceListData) => {
|
||||
const priceListPrices: PriceListPriceDTO[] = []
|
||||
|
||||
priceListData.prices?.forEach((price) => {
|
||||
const { variant_id, ...priceData } = price
|
||||
if (!variant_id) {
|
||||
return
|
||||
}
|
||||
|
||||
const rules: Record<string, string> = {}
|
||||
if (price.region_id) {
|
||||
rules.region_id = price.region_id
|
||||
}
|
||||
|
||||
priceListPrices.push({
|
||||
id: priceData.id,
|
||||
price_set_id: variantPriceSetMap.get(variant_id) as string,
|
||||
currency_code:
|
||||
regionsMap.get(priceData.region_id as string) ??
|
||||
(priceData.currency_code as string),
|
||||
amount: priceData.amount,
|
||||
min_quantity: priceData.min_quantity,
|
||||
max_quantity: priceData.max_quantity,
|
||||
rules,
|
||||
})
|
||||
|
||||
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",
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
import { IPricingModuleService, PriceListDTO } from "@medusajs/types"
|
||||
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import { WorkflowArguments } from "@medusajs/workflows-sdk"
|
||||
|
||||
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",
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import { IPricingModuleService } from "@medusajs/types"
|
||||
import { WorkflowArguments } from "@medusajs/workflows-sdk"
|
||||
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"],
|
||||
take: null,
|
||||
}
|
||||
)
|
||||
|
||||
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",
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import { IPricingModuleService } from "@medusajs/types"
|
||||
import { WorkflowArguments } from "@medusajs/workflows-sdk"
|
||||
|
||||
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",
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
import {
|
||||
AddPriceListPricesDTO,
|
||||
IPricingModuleService,
|
||||
PriceListDTO,
|
||||
PriceListPriceDTO,
|
||||
UpdateMoneyAmountDTO,
|
||||
UpdatePriceListDTO,
|
||||
} from "@medusajs/types"
|
||||
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import { WorkflowArguments } from "@medusajs/workflows-sdk"
|
||||
|
||||
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",
|
||||
}
|
||||
@@ -8,6 +8,7 @@ export * from "./fulfillment"
|
||||
export * as Handlers from "./handlers"
|
||||
export * from "./invite"
|
||||
export * from "./payment"
|
||||
export * from "./price-list"
|
||||
export * from "./pricing"
|
||||
export * from "./product"
|
||||
export * from "./promotion"
|
||||
|
||||
2
packages/core-flows/src/price-list/index.ts
Normal file
2
packages/core-flows/src/price-list/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from "./steps"
|
||||
export * from "./workflows"
|
||||
@@ -0,0 +1,58 @@
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import {
|
||||
CreatePriceListDTO,
|
||||
CreatePriceListWorkflowInputDTO,
|
||||
IPricingModuleService,
|
||||
} from "@medusajs/types"
|
||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||
|
||||
type WorkflowStepInput = {
|
||||
data: CreatePriceListWorkflowInputDTO[]
|
||||
variant_price_map: Record<string, string>
|
||||
}
|
||||
|
||||
export const createPriceListsStepId = "create-price-lists"
|
||||
export const createPriceListsStep = createStep(
|
||||
createPriceListsStepId,
|
||||
async (stepInput: WorkflowStepInput, { container }) => {
|
||||
const { data, variant_price_map: variantPriceMap } = stepInput
|
||||
|
||||
const pricingModule = container.resolve<IPricingModuleService>(
|
||||
ModuleRegistrationName.PRICING
|
||||
)
|
||||
|
||||
const createData = data.map((priceListDTO) => {
|
||||
const { prices = [], ...rest } = priceListDTO
|
||||
const createPriceListData: CreatePriceListDTO = { ...rest }
|
||||
|
||||
createPriceListData.prices = prices.map((price) => ({
|
||||
currency_code: price.currency_code,
|
||||
amount: price.amount,
|
||||
min_quantity: price.min_quantity,
|
||||
max_quantity: price.max_quantity,
|
||||
price_set_id: variantPriceMap[price.variant_id],
|
||||
rules: price.rules,
|
||||
}))
|
||||
|
||||
return createPriceListData
|
||||
})
|
||||
|
||||
const createdPriceLists = await pricingModule.createPriceLists(createData)
|
||||
|
||||
return new StepResponse(
|
||||
createdPriceLists,
|
||||
createdPriceLists.map((createdPriceLists) => createdPriceLists.id)
|
||||
)
|
||||
},
|
||||
async (createdPriceListIds, { container }) => {
|
||||
if (!createdPriceListIds?.length) {
|
||||
return
|
||||
}
|
||||
|
||||
const pricingModule = container.resolve<IPricingModuleService>(
|
||||
ModuleRegistrationName.PRICING
|
||||
)
|
||||
|
||||
await pricingModule.deletePriceLists(createdPriceListIds)
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,30 @@
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import { IPricingModuleService } from "@medusajs/types"
|
||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||
|
||||
export const deletePriceListsStepId = "delete-campaigns"
|
||||
export const deletePriceListsStep = createStep(
|
||||
deletePriceListsStepId,
|
||||
async (ids: string[], { container }) => {
|
||||
const pricingModule = container.resolve<IPricingModuleService>(
|
||||
ModuleRegistrationName.PRICING
|
||||
)
|
||||
|
||||
// TODO: Implement soft delete price lists
|
||||
await pricingModule.deletePriceLists(ids)
|
||||
|
||||
return new StepResponse(void 0, ids)
|
||||
},
|
||||
async (idsToRestore, { container }) => {
|
||||
if (!idsToRestore?.length) {
|
||||
return
|
||||
}
|
||||
|
||||
const pricingModule = container.resolve<IPricingModuleService>(
|
||||
ModuleRegistrationName.PRICING
|
||||
)
|
||||
|
||||
// TODO: Implement restore price lists
|
||||
// await pricingModule.restorePriceLists(idsToRestore)
|
||||
}
|
||||
)
|
||||
7
packages/core-flows/src/price-list/steps/index.ts
Normal file
7
packages/core-flows/src/price-list/steps/index.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export * from "./create-price-lists"
|
||||
export * from "./delete-price-lists"
|
||||
export * from "./remove-price-list-prices"
|
||||
export * from "./update-price-lists"
|
||||
export * from "./upsert-price-list-prices"
|
||||
export * from "./validate-price-lists"
|
||||
export * from "./validate-variant-price-links"
|
||||
@@ -0,0 +1,41 @@
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import { IPricingModuleService } from "@medusajs/types"
|
||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||
|
||||
export const removePriceListPricesStepId = "remove-price-list-prices"
|
||||
export const removePriceListPricesStep = createStep(
|
||||
removePriceListPricesStepId,
|
||||
async (ids: string[], { container }) => {
|
||||
if (!ids.length) {
|
||||
return new StepResponse(null, [])
|
||||
}
|
||||
|
||||
const pricingModule = container.resolve<IPricingModuleService>(
|
||||
ModuleRegistrationName.PRICING
|
||||
)
|
||||
|
||||
const psmas = await pricingModule.listPriceSetMoneyAmounts(
|
||||
{ id: ids },
|
||||
{ relations: ["price_list"] }
|
||||
)
|
||||
|
||||
await pricingModule.removePrices(psmas.map((psma) => psma.id))
|
||||
|
||||
return new StepResponse(
|
||||
null,
|
||||
psmas.map((psma) => psma.id)
|
||||
)
|
||||
},
|
||||
async (ids, { container }) => {
|
||||
if (!ids) {
|
||||
return
|
||||
}
|
||||
|
||||
const pricingModule = container.resolve<IPricingModuleService>(
|
||||
ModuleRegistrationName.PRICING
|
||||
)
|
||||
|
||||
// TODO: This needs to be implemented
|
||||
// pricingModule.restorePrices(ids)
|
||||
}
|
||||
)
|
||||
105
packages/core-flows/src/price-list/steps/update-price-lists.ts
Normal file
105
packages/core-flows/src/price-list/steps/update-price-lists.ts
Normal file
@@ -0,0 +1,105 @@
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import {
|
||||
IPricingModuleService,
|
||||
UpdatePriceListDTO,
|
||||
UpdatePriceListWorkflowInputDTO,
|
||||
} from "@medusajs/types"
|
||||
import {
|
||||
buildPriceListRules,
|
||||
convertItemResponseToUpdateRequest,
|
||||
getSelectsAndRelationsFromObjectArray,
|
||||
} from "@medusajs/utils"
|
||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||
|
||||
export const updatePriceListsStepId = "update-price-lists"
|
||||
export const updatePriceListsStep = createStep(
|
||||
updatePriceListsStepId,
|
||||
async (data: UpdatePriceListDTO[], { container }) => {
|
||||
const pricingModule = container.resolve<IPricingModuleService>(
|
||||
ModuleRegistrationName.PRICING
|
||||
)
|
||||
|
||||
const { dataBeforeUpdate, selects, relations } = await getDataBeforeUpdate(
|
||||
pricingModule,
|
||||
data
|
||||
)
|
||||
|
||||
const updatedPriceLists = await pricingModule.updatePriceLists(data)
|
||||
|
||||
return new StepResponse(updatedPriceLists, {
|
||||
dataBeforeUpdate,
|
||||
selects,
|
||||
relations,
|
||||
})
|
||||
},
|
||||
async (revertInput, { container }) => {
|
||||
if (!revertInput) {
|
||||
return
|
||||
}
|
||||
|
||||
const { dataBeforeUpdate, selects, relations } = revertInput
|
||||
const pricingModule = container.resolve<IPricingModuleService>(
|
||||
ModuleRegistrationName.PRICING
|
||||
)
|
||||
|
||||
await pricingModule.updatePriceLists(
|
||||
dataBeforeUpdate.map((data) => {
|
||||
const { price_list_rules: priceListRules = [], rules, ...rest } = data
|
||||
|
||||
const updateData: UpdatePriceListDTO = {
|
||||
...rest,
|
||||
rules: buildPriceListRules(priceListRules),
|
||||
}
|
||||
|
||||
return convertItemResponseToUpdateRequest(
|
||||
updateData,
|
||||
selects,
|
||||
relations
|
||||
)
|
||||
})
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
// Since rules is an API level abstraction, we need to do this dance of data fetching
|
||||
// to its actual attributes in the module to do perform a revert in case a rollback needs to happen.
|
||||
// TODO: Check if there is a better way to approach this. Preferably the module should be handling this
|
||||
// if this is not the response the module provides.
|
||||
async function getDataBeforeUpdate(
|
||||
pricingModule: IPricingModuleService,
|
||||
data: UpdatePriceListWorkflowInputDTO[]
|
||||
) {
|
||||
const { selects, relations } = getSelectsAndRelationsFromObjectArray(data, {
|
||||
objectFields: ["rules"],
|
||||
})
|
||||
const selectsClone = [...selects]
|
||||
const relationsClone = [...relations]
|
||||
|
||||
if (selectsClone.includes("rules")) {
|
||||
const index = selectsClone.indexOf("rules", 0)
|
||||
|
||||
if (index > -1) {
|
||||
selectsClone.splice(index, 1)
|
||||
}
|
||||
|
||||
selectsClone.push(
|
||||
"price_list_rules.price_list_rule_values.value",
|
||||
"price_list_rules.rule_type.rule_attribute"
|
||||
)
|
||||
relationsClone.push(
|
||||
"price_list_rules.price_list_rule_values",
|
||||
"price_list_rules.rule_type"
|
||||
)
|
||||
}
|
||||
|
||||
const dataBeforeUpdate = await pricingModule.listPriceLists(
|
||||
{ id: data.map((d) => d.id) },
|
||||
{ relations: relationsClone, select: selectsClone }
|
||||
)
|
||||
|
||||
return {
|
||||
dataBeforeUpdate,
|
||||
selects,
|
||||
relations,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import {
|
||||
AddPriceListPricesDTO,
|
||||
CreatePriceListPriceDTO,
|
||||
CreatePriceListPriceWorkflowDTO,
|
||||
IPricingModuleService,
|
||||
PriceSetMoneyAmountDTO,
|
||||
UpdatePriceListPriceDTO,
|
||||
UpdatePriceListPriceWorkflowDTO,
|
||||
UpdatePriceListPricesDTO,
|
||||
UpdatePriceListWorkflowInputDTO,
|
||||
} from "@medusajs/types"
|
||||
import { buildPriceSetPricesForModule, promiseAll } from "@medusajs/utils"
|
||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||
|
||||
type WorkflowStepInput = {
|
||||
data: Pick<UpdatePriceListWorkflowInputDTO, "id" | "prices">[]
|
||||
variant_price_map: Record<string, string>
|
||||
}
|
||||
|
||||
export const upsertPriceListPricesStepId = "upsert-price-list-prices"
|
||||
export const upsertPriceListPricesStep = createStep(
|
||||
upsertPriceListPricesStepId,
|
||||
async (stepInput: WorkflowStepInput, { container }) => {
|
||||
const { data, variant_price_map: variantPriceSetMap } = stepInput
|
||||
|
||||
const priceListPricesToUpdate: UpdatePriceListPricesDTO[] = []
|
||||
const priceListPricesToAdd: AddPriceListPricesDTO[] = []
|
||||
const pricingModule = container.resolve<IPricingModuleService>(
|
||||
ModuleRegistrationName.PRICING
|
||||
)
|
||||
|
||||
for (const upsertPriceListPricesData of data) {
|
||||
const { prices = [], id } = upsertPriceListPricesData
|
||||
|
||||
const pricesToAdd: CreatePriceListPriceDTO[] = []
|
||||
const pricesToUpdate: UpdatePriceListPriceDTO[] = []
|
||||
|
||||
for (const price of prices) {
|
||||
const priceSetId = variantPriceSetMap[price.variant_id!]
|
||||
|
||||
if (isPriceUpdate(price)) {
|
||||
pricesToUpdate.push({ ...price, price_set_id: priceSetId })
|
||||
} else {
|
||||
pricesToAdd.push({ ...price, price_set_id: priceSetId })
|
||||
}
|
||||
}
|
||||
|
||||
if (pricesToUpdate.length) {
|
||||
priceListPricesToUpdate.push({
|
||||
price_list_id: id,
|
||||
prices: pricesToUpdate,
|
||||
})
|
||||
}
|
||||
|
||||
if (pricesToAdd.length) {
|
||||
priceListPricesToAdd.push({
|
||||
price_list_id: id,
|
||||
prices: pricesToAdd,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const updatedPriceSetMoneyAmounts =
|
||||
await pricingModule.listPriceSetMoneyAmounts(
|
||||
{
|
||||
id: priceListPricesToUpdate
|
||||
.map((priceListData) =>
|
||||
priceListData.prices.map((price) => price.id)
|
||||
)
|
||||
.filter(Boolean)
|
||||
.flat(1),
|
||||
},
|
||||
{ relations: ["price_list"] }
|
||||
)
|
||||
|
||||
const priceListPsmaMap = new Map<string, PriceSetMoneyAmountDTO[]>()
|
||||
const dataBeforePriceUpdate: UpdatePriceListPricesDTO[] = []
|
||||
|
||||
for (const priceSetMoneyAmount of updatedPriceSetMoneyAmounts) {
|
||||
const priceListId = priceSetMoneyAmount.price_list!.id
|
||||
const psmas = priceListPsmaMap.get(priceListId) || []
|
||||
|
||||
priceListPsmaMap.set(priceListId, psmas)
|
||||
}
|
||||
|
||||
for (const [priceListId, psmas] of Object.entries(priceListPsmaMap)) {
|
||||
dataBeforePriceUpdate.push({
|
||||
price_list_id: priceListId,
|
||||
prices: buildPriceSetPricesForModule(psmas),
|
||||
})
|
||||
}
|
||||
|
||||
// TODO: `addPriceListPrices` will return a list of price lists
|
||||
// This should be reworked to return prices instead, as we need to
|
||||
// do a revert incase this step fails
|
||||
const [createdPriceListPrices, _] = await promiseAll([
|
||||
pricingModule.addPriceListPrices(priceListPricesToAdd),
|
||||
pricingModule.updatePriceListPrices(priceListPricesToUpdate),
|
||||
])
|
||||
|
||||
return new StepResponse(null, {
|
||||
createdPriceListPrices,
|
||||
updatedPriceListPrices: dataBeforePriceUpdate,
|
||||
})
|
||||
},
|
||||
async (data, { container }) => {
|
||||
if (!data) {
|
||||
return
|
||||
}
|
||||
|
||||
const { createdPriceListPrices = [], updatedPriceListPrices = [] } = data
|
||||
const pricingModule = container.resolve<IPricingModuleService>(
|
||||
ModuleRegistrationName.PRICING
|
||||
)
|
||||
|
||||
if (createdPriceListPrices.length) {
|
||||
await pricingModule.removePrices(createdPriceListPrices.map((p) => p.id))
|
||||
}
|
||||
|
||||
if (updatedPriceListPrices.length) {
|
||||
await pricingModule.updatePriceListPrices(updatedPriceListPrices)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
function isPriceUpdate(
|
||||
data: UpdatePriceListPriceWorkflowDTO | CreatePriceListPriceWorkflowDTO
|
||||
): data is UpdatePriceListPriceWorkflowDTO {
|
||||
return "id" in data
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import {
|
||||
IPricingModuleService,
|
||||
PriceListDTO,
|
||||
UpdatePriceListDTO,
|
||||
} from "@medusajs/types"
|
||||
import { MedusaError, arrayDifference } from "@medusajs/utils"
|
||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||
|
||||
export const validatePriceListsStepId = "validate-price-lists"
|
||||
export const validatePriceListsStep = createStep(
|
||||
validatePriceListsStepId,
|
||||
async (data: Pick<UpdatePriceListDTO, "id">[], { container }) => {
|
||||
const pricingModule = container.resolve<IPricingModuleService>(
|
||||
ModuleRegistrationName.PRICING
|
||||
)
|
||||
|
||||
const priceListIds = data.map((d) => d.id)
|
||||
const priceLists = await pricingModule.listPriceLists({ id: priceListIds })
|
||||
|
||||
const diff = arrayDifference(
|
||||
priceListIds,
|
||||
priceLists.map((pl) => pl.id)
|
||||
)
|
||||
|
||||
if (diff.length) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_FOUND,
|
||||
`Price lists with id: ${diff.join(", ")} was not found`
|
||||
)
|
||||
}
|
||||
|
||||
const priceListMap: Record<string, PriceListDTO> = {}
|
||||
|
||||
for (const priceList of priceLists) {
|
||||
priceListMap[priceList.id] = priceList
|
||||
}
|
||||
|
||||
return new StepResponse(priceListMap)
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,48 @@
|
||||
import { UpdatePriceListWorkflowInputDTO } from "@medusajs/types"
|
||||
import {
|
||||
ContainerRegistrationKeys,
|
||||
MedusaError,
|
||||
remoteQueryObjectFromString,
|
||||
} from "@medusajs/utils"
|
||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||
|
||||
type WorkflowStepInput = Pick<UpdatePriceListWorkflowInputDTO, "prices">[]
|
||||
|
||||
export const validateVariantPriceLinksStepId = "validate-variant-price-links"
|
||||
export const validateVariantPriceLinksStep = createStep(
|
||||
validateVariantPriceLinksStepId,
|
||||
async (data: WorkflowStepInput, { container }) => {
|
||||
const remoteQuery = container.resolve(
|
||||
ContainerRegistrationKeys.REMOTE_QUERY
|
||||
)
|
||||
|
||||
const variantIds: string[] = data
|
||||
.map((pl) => pl?.prices?.map((price) => price.variant_id!) || [])
|
||||
.filter(Boolean)
|
||||
.flat(1)
|
||||
|
||||
const variantPricingLinkQuery = remoteQueryObjectFromString({
|
||||
entryPoint: "product_variant_price_set",
|
||||
fields: ["variant_id", "price_set_id"],
|
||||
variables: { variant_id: variantIds, take: null },
|
||||
})
|
||||
|
||||
const links = await remoteQuery(variantPricingLinkQuery)
|
||||
const variantPriceSetMap: Record<string, string> = {}
|
||||
|
||||
for (const link of links) {
|
||||
variantPriceSetMap[link.variant_id] = link.price_set_id
|
||||
}
|
||||
|
||||
const withoutLinks = variantIds.filter((id) => !variantPriceSetMap[id])
|
||||
|
||||
if (withoutLinks.length) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`No price set exist for variants: ${withoutLinks.join(", ")}`
|
||||
)
|
||||
}
|
||||
|
||||
return new StepResponse(variantPriceSetMap)
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,20 @@
|
||||
import { CreatePriceListWorkflowInputDTO, PriceListDTO } from "@medusajs/types"
|
||||
import { WorkflowData, createWorkflow } from "@medusajs/workflows-sdk"
|
||||
import { createPriceListsStep, validateVariantPriceLinksStep } from "../steps"
|
||||
|
||||
type WorkflowInput = { price_lists_data: CreatePriceListWorkflowInputDTO[] }
|
||||
|
||||
export const createPriceListsWorkflowId = "create-price-lists"
|
||||
export const createPriceListsWorkflow = createWorkflow(
|
||||
createPriceListsWorkflowId,
|
||||
(input: WorkflowData<WorkflowInput>): WorkflowData<PriceListDTO[]> => {
|
||||
const variantPriceMap = validateVariantPriceLinksStep(
|
||||
input.price_lists_data
|
||||
)
|
||||
|
||||
return createPriceListsStep({
|
||||
data: input.price_lists_data,
|
||||
variant_price_map: variantPriceMap,
|
||||
})
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,12 @@
|
||||
import { createWorkflow, WorkflowData } from "@medusajs/workflows-sdk"
|
||||
import { deletePriceListsStep } from "../steps"
|
||||
|
||||
type WorkflowInput = { ids: string[] }
|
||||
|
||||
export const deletePriceListsWorkflowId = "delete-price-lists"
|
||||
export const deletePriceListsWorkflow = createWorkflow(
|
||||
deletePriceListsWorkflowId,
|
||||
(input: WorkflowData<WorkflowInput>): WorkflowData<void> => {
|
||||
return deletePriceListsStep(input.ids)
|
||||
}
|
||||
)
|
||||
@@ -1,6 +1,5 @@
|
||||
export * from "./create-price-lists"
|
||||
export * from "./remove-price-lists"
|
||||
export * from "./delete-price-lists"
|
||||
export * from "./remove-price-list-prices"
|
||||
export * from "./remove-product-prices"
|
||||
export * from "./remove-variant-prices"
|
||||
export * from "./update-price-lists"
|
||||
export * from "./upsert-price-list-prices"
|
||||
@@ -0,0 +1,12 @@
|
||||
import { WorkflowData, createWorkflow } from "@medusajs/workflows-sdk"
|
||||
import { removePriceListPricesStep } from "../steps"
|
||||
|
||||
type WorkflowInput = { ids: string[] }
|
||||
|
||||
export const removePriceListPricesWorkflowId = "remove-price-list-prices"
|
||||
export const removePriceListPricesWorkflow = createWorkflow(
|
||||
removePriceListPricesWorkflowId,
|
||||
(input: WorkflowData<WorkflowInput>): WorkflowData<void> => {
|
||||
removePriceListPricesStep(input.ids)
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,47 @@
|
||||
import { UpdatePriceListWorkflowInputDTO } from "@medusajs/types"
|
||||
import {
|
||||
WorkflowData,
|
||||
createWorkflow,
|
||||
parallelize,
|
||||
transform,
|
||||
} from "@medusajs/workflows-sdk"
|
||||
import {
|
||||
updatePriceListsStep,
|
||||
upsertPriceListPricesStep,
|
||||
validatePriceListsStep,
|
||||
validateVariantPriceLinksStep,
|
||||
} from "../steps"
|
||||
|
||||
type WorkflowInput = { price_lists_data: UpdatePriceListWorkflowInputDTO[] }
|
||||
|
||||
export const updatePriceListsWorkflowId = "update-price-lists"
|
||||
export const updatePriceListsWorkflow = createWorkflow(
|
||||
updatePriceListsWorkflowId,
|
||||
(input: WorkflowData<WorkflowInput>): WorkflowData<void> => {
|
||||
const [priceListsMap, variantPriceMap] = parallelize(
|
||||
validatePriceListsStep(input.price_lists_data),
|
||||
validateVariantPriceLinksStep(input.price_lists_data)
|
||||
)
|
||||
|
||||
const updatePricesInput = transform(
|
||||
{ priceListsMap, variantPriceMap, input },
|
||||
(data) => ({
|
||||
data: data.input.price_lists_data,
|
||||
price_lists_map: data.priceListsMap,
|
||||
variant_price_map: data.variantPriceMap,
|
||||
})
|
||||
)
|
||||
|
||||
upsertPriceListPricesStep(updatePricesInput)
|
||||
|
||||
const updatePriceListInput = transform({ input }, (data) => {
|
||||
return data.input.price_lists_data.map((priceListData) => {
|
||||
delete priceListData.prices
|
||||
|
||||
return priceListData
|
||||
})
|
||||
})
|
||||
|
||||
updatePriceListsStep(updatePriceListInput)
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,31 @@
|
||||
import { UpdatePriceListWorkflowInputDTO } from "@medusajs/types"
|
||||
import {
|
||||
WorkflowData,
|
||||
createWorkflow,
|
||||
parallelize,
|
||||
} from "@medusajs/workflows-sdk"
|
||||
import {
|
||||
upsertPriceListPricesStep,
|
||||
validatePriceListsStep,
|
||||
validateVariantPriceLinksStep,
|
||||
} from "../steps"
|
||||
|
||||
type WorkflowInput = {
|
||||
price_lists_data: Pick<UpdatePriceListWorkflowInputDTO, "id" | "prices">[]
|
||||
}
|
||||
|
||||
export const upsertPriceListPricesWorkflowId = "upsert-price-list-prices"
|
||||
export const upsertPriceListPricesWorkflow = createWorkflow(
|
||||
upsertPriceListPricesWorkflowId,
|
||||
(input: WorkflowData<WorkflowInput>): WorkflowData<void> => {
|
||||
const [_, variantPriceMap] = parallelize(
|
||||
validatePriceListsStep(input.price_lists_data),
|
||||
validateVariantPriceLinksStep(input.price_lists_data)
|
||||
)
|
||||
|
||||
upsertPriceListPricesStep({
|
||||
data: input.price_lists_data,
|
||||
variant_price_map: variantPriceMap,
|
||||
})
|
||||
}
|
||||
)
|
||||
@@ -1,4 +1,4 @@
|
||||
import { FindParams, extendedFindParamsMixin } from "../../../types/common"
|
||||
import { Type } from "class-transformer"
|
||||
import {
|
||||
IsArray,
|
||||
IsDateString,
|
||||
@@ -9,10 +9,9 @@ import {
|
||||
IsString,
|
||||
ValidateNested,
|
||||
} from "class-validator"
|
||||
import { Transform, Type } from "class-transformer"
|
||||
import { FindParams, extendedFindParamsMixin } from "../../../types/common"
|
||||
|
||||
import { CampaignBudgetType } from "@medusajs/utils"
|
||||
import { transformOptionalDate } from "../../../utils/validators/date-transform"
|
||||
|
||||
export class AdminGetCampaignsCampaignParams extends FindParams {}
|
||||
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
import {
|
||||
removePriceListPricesWorkflow,
|
||||
upsertPriceListPricesWorkflow,
|
||||
} from "@medusajs/core-flows"
|
||||
import {
|
||||
AuthenticatedMedusaRequest,
|
||||
MedusaResponse,
|
||||
} from "../../../../../types/routing"
|
||||
import { listPriceLists } from "../../queries"
|
||||
import {
|
||||
adminPriceListRemoteQueryFields,
|
||||
defaultAdminPriceListFields,
|
||||
} from "../../query-config"
|
||||
import {
|
||||
AdminDeletePriceListsPriceListPricesReq,
|
||||
AdminPostPriceListsPriceListPricesReq,
|
||||
} from "../../validators"
|
||||
|
||||
export const POST = async (
|
||||
req: AuthenticatedMedusaRequest<AdminPostPriceListsPriceListPricesReq>,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
const { prices } = req.validatedBody
|
||||
const id = req.params.id
|
||||
const workflow = upsertPriceListPricesWorkflow(req.scope)
|
||||
const { errors } = await workflow.run({
|
||||
input: {
|
||||
price_lists_data: [{ id, prices }],
|
||||
},
|
||||
throwOnError: false,
|
||||
})
|
||||
|
||||
if (Array.isArray(errors) && errors[0]) {
|
||||
throw errors[0].error
|
||||
}
|
||||
|
||||
const [[priceList]] = await listPriceLists({
|
||||
container: req.scope,
|
||||
remoteQueryFields: adminPriceListRemoteQueryFields,
|
||||
apiFields: defaultAdminPriceListFields,
|
||||
variables: { filters: { id }, skip: 0, take: 1 },
|
||||
})
|
||||
|
||||
res.status(200).json({ price_list: priceList })
|
||||
}
|
||||
|
||||
export const DELETE = async (
|
||||
req: AuthenticatedMedusaRequest<AdminDeletePriceListsPriceListPricesReq>,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
const { ids } = req.validatedBody
|
||||
const workflow = removePriceListPricesWorkflow(req.scope)
|
||||
|
||||
const { errors } = await workflow.run({
|
||||
input: { ids },
|
||||
throwOnError: false,
|
||||
})
|
||||
|
||||
if (Array.isArray(errors) && errors[0]) {
|
||||
throw errors[0].error
|
||||
}
|
||||
|
||||
res.status(200).json({
|
||||
ids,
|
||||
object: "price_list_prices",
|
||||
deleted: true,
|
||||
})
|
||||
}
|
||||
@@ -1,10 +1,18 @@
|
||||
import {
|
||||
deletePriceListsWorkflow,
|
||||
updatePriceListsWorkflow,
|
||||
} from "@medusajs/core-flows"
|
||||
import { MedusaError } from "@medusajs/utils"
|
||||
import {
|
||||
AuthenticatedMedusaRequest,
|
||||
MedusaResponse,
|
||||
} from "../../../../types/routing"
|
||||
import { listPriceLists } from "../queries"
|
||||
import { adminPriceListRemoteQueryFields } from "../query-config"
|
||||
import {
|
||||
adminPriceListRemoteQueryFields,
|
||||
defaultAdminPriceListFields,
|
||||
} from "../query-config"
|
||||
import { AdminPostPriceListsPriceListReq } from "../validators"
|
||||
|
||||
export const GET = async (
|
||||
req: AuthenticatedMedusaRequest,
|
||||
@@ -31,3 +39,52 @@ export const GET = async (
|
||||
|
||||
res.status(200).json({ price_list: priceList })
|
||||
}
|
||||
|
||||
export const POST = async (
|
||||
req: AuthenticatedMedusaRequest<AdminPostPriceListsPriceListReq>,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
const id = req.params.id
|
||||
const workflow = updatePriceListsWorkflow(req.scope)
|
||||
|
||||
const { errors } = await workflow.run({
|
||||
input: { price_lists_data: [{ id, ...req.validatedBody }] },
|
||||
throwOnError: false,
|
||||
})
|
||||
|
||||
if (Array.isArray(errors) && errors[0]) {
|
||||
throw errors[0].error
|
||||
}
|
||||
|
||||
const [[priceList]] = await listPriceLists({
|
||||
container: req.scope,
|
||||
remoteQueryFields: adminPriceListRemoteQueryFields,
|
||||
apiFields: defaultAdminPriceListFields,
|
||||
variables: { filters: { id }, skip: 0, take: 1 },
|
||||
})
|
||||
|
||||
res.status(200).json({ price_list: priceList })
|
||||
}
|
||||
|
||||
export const DELETE = async (
|
||||
req: AuthenticatedMedusaRequest,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
const id = req.params.id
|
||||
const workflow = deletePriceListsWorkflow(req.scope)
|
||||
|
||||
const { errors } = await workflow.run({
|
||||
input: { ids: [id] },
|
||||
throwOnError: false,
|
||||
})
|
||||
|
||||
if (Array.isArray(errors) && errors[0]) {
|
||||
throw errors[0].error
|
||||
}
|
||||
|
||||
res.status(200).json({
|
||||
id,
|
||||
object: "price_list",
|
||||
deleted: true,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
import { transformQuery } from "../../../api/middlewares"
|
||||
import { transformBody, transformQuery } from "../../../api/middlewares"
|
||||
import { MiddlewareRoute } from "../../../loaders/helpers/routing/types"
|
||||
import { authenticate } from "../../../utils/authenticate-middleware"
|
||||
import * as QueryConfig from "./query-config"
|
||||
import {
|
||||
AdminDeletePriceListsPriceListPricesReq,
|
||||
AdminGetPriceListsParams,
|
||||
AdminGetPriceListsPriceListParams,
|
||||
AdminPostPriceListsPriceListPricesReq,
|
||||
AdminPostPriceListsPriceListReq,
|
||||
AdminPostPriceListsReq,
|
||||
} from "./validators"
|
||||
|
||||
export const adminPriceListsRoutesMiddlewares: MiddlewareRoute[] = [
|
||||
@@ -33,4 +37,24 @@ export const adminPriceListsRoutesMiddlewares: MiddlewareRoute[] = [
|
||||
),
|
||||
],
|
||||
},
|
||||
{
|
||||
method: ["POST"],
|
||||
matcher: "/admin/price-lists",
|
||||
middlewares: [transformBody(AdminPostPriceListsReq)],
|
||||
},
|
||||
{
|
||||
method: ["POST"],
|
||||
matcher: "/admin/price-lists/:id",
|
||||
middlewares: [transformBody(AdminPostPriceListsPriceListReq)],
|
||||
},
|
||||
{
|
||||
method: ["POST"],
|
||||
matcher: "/admin/price-lists/:id/prices",
|
||||
middlewares: [transformBody(AdminPostPriceListsPriceListPricesReq)],
|
||||
},
|
||||
{
|
||||
method: ["DELETE"],
|
||||
matcher: "/admin/price-lists/:id/prices",
|
||||
middlewares: [transformBody(AdminDeletePriceListsPriceListPricesReq)],
|
||||
},
|
||||
]
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
import {
|
||||
MedusaContainer,
|
||||
PriceListRuleDTO,
|
||||
PriceSetMoneyAmountDTO,
|
||||
ProductVariantDTO,
|
||||
} from "@medusajs/types"
|
||||
import { MedusaContainer } from "@medusajs/types"
|
||||
import {
|
||||
ContainerRegistrationKeys,
|
||||
buildPriceListRules,
|
||||
buildPriceSetPricesForCore,
|
||||
remoteQueryObjectFromString,
|
||||
} from "@medusajs/utils"
|
||||
import { cleanResponseData } from "../../../../utils/clean-response-data"
|
||||
@@ -37,7 +34,7 @@ export async function listPriceLists({
|
||||
|
||||
for (const priceList of priceLists) {
|
||||
priceList.rules = buildPriceListRules(priceList.price_list_rules || [])
|
||||
priceList.prices = buildPriceSetPrices(
|
||||
priceList.prices = buildPriceSetPricesForCore(
|
||||
priceList.price_set_money_amounts || []
|
||||
)
|
||||
}
|
||||
@@ -48,43 +45,3 @@ export async function listPriceLists({
|
||||
|
||||
return [sanitizedPriceLists, metadata.count]
|
||||
}
|
||||
|
||||
function buildPriceListRules(
|
||||
priceListRules: PriceListRuleDTO[]
|
||||
): Record<string, string[]> {
|
||||
return priceListRules.reduce((acc, curr) => {
|
||||
const ruleAttribute = curr.rule_type.rule_attribute
|
||||
const ruleValues = curr.price_list_rule_values || []
|
||||
|
||||
if (ruleAttribute) {
|
||||
acc[ruleAttribute] = ruleValues.map((ruleValue) => ruleValue.value)
|
||||
}
|
||||
|
||||
return acc
|
||||
}, {})
|
||||
}
|
||||
|
||||
function buildPriceSetPrices(
|
||||
priceSetMoneyAmounts: (PriceSetMoneyAmountDTO & {
|
||||
price_set: PriceSetMoneyAmountDTO["price_set"] & {
|
||||
variant?: ProductVariantDTO
|
||||
}
|
||||
})[]
|
||||
): Record<string, any>[] {
|
||||
return priceSetMoneyAmounts.map((priceSetMoneyAmount) => {
|
||||
const productVariant = priceSetMoneyAmount.price_set?.variant
|
||||
const rules = priceSetMoneyAmount.price_rules?.reduce((acc, curr) => {
|
||||
if (curr.rule_type.rule_attribute) {
|
||||
acc[curr.rule_type.rule_attribute] = curr.value
|
||||
}
|
||||
|
||||
return acc
|
||||
}, {})
|
||||
|
||||
return {
|
||||
...priceSetMoneyAmount.money_amount,
|
||||
variant_id: productVariant?.id ?? null,
|
||||
rules,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
import { createPriceListsWorkflow } from "@medusajs/core-flows"
|
||||
import {
|
||||
AuthenticatedMedusaRequest,
|
||||
MedusaResponse,
|
||||
} from "../../../types/routing"
|
||||
import { listPriceLists } from "./queries"
|
||||
import { adminPriceListRemoteQueryFields } from "./query-config"
|
||||
import {
|
||||
adminPriceListRemoteQueryFields,
|
||||
defaultAdminPriceListFields,
|
||||
} from "./query-config"
|
||||
import { AdminPostPriceListsReq } from "./validators"
|
||||
|
||||
export const GET = async (
|
||||
req: AuthenticatedMedusaRequest,
|
||||
@@ -29,3 +34,31 @@ export const GET = async (
|
||||
limit,
|
||||
})
|
||||
}
|
||||
|
||||
export const POST = async (
|
||||
req: AuthenticatedMedusaRequest<AdminPostPriceListsReq>,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
const workflow = createPriceListsWorkflow(req.scope)
|
||||
const { result, errors } = await workflow.run({
|
||||
input: { price_lists_data: [req.validatedBody] },
|
||||
throwOnError: false,
|
||||
})
|
||||
|
||||
if (Array.isArray(errors) && errors[0]) {
|
||||
throw errors[0].error
|
||||
}
|
||||
|
||||
const [[priceList]] = await listPriceLists({
|
||||
container: req.scope,
|
||||
apiFields: defaultAdminPriceListFields,
|
||||
remoteQueryFields: adminPriceListRemoteQueryFields,
|
||||
variables: {
|
||||
filters: { id: result[0].id },
|
||||
skip: 0,
|
||||
take: 1,
|
||||
},
|
||||
})
|
||||
|
||||
res.status(200).json({ price_list: priceList })
|
||||
}
|
||||
|
||||
@@ -1,4 +1,152 @@
|
||||
import { PriceListStatus, PriceListType } from "@medusajs/types"
|
||||
import { Transform, Type } from "class-transformer"
|
||||
import {
|
||||
IsArray,
|
||||
IsEnum,
|
||||
IsInt,
|
||||
IsObject,
|
||||
IsOptional,
|
||||
IsString,
|
||||
ValidateIf,
|
||||
ValidateNested,
|
||||
} from "class-validator"
|
||||
import { FindParams } from "../../../types/common"
|
||||
import { transformOptionalDate } from "../../../utils/validators/date-transform"
|
||||
|
||||
export class AdminGetPriceListsParams extends FindParams {}
|
||||
export class AdminGetPriceListsPriceListParams extends FindParams {}
|
||||
|
||||
export class AdminPostPriceListsReq {
|
||||
@IsString()
|
||||
title: string
|
||||
|
||||
@IsString()
|
||||
description: string
|
||||
|
||||
@IsOptional()
|
||||
@Transform(transformOptionalDate)
|
||||
starts_at?: string
|
||||
|
||||
@IsOptional()
|
||||
@Transform(transformOptionalDate)
|
||||
ends_at?: string
|
||||
|
||||
@IsOptional()
|
||||
@IsEnum(PriceListStatus)
|
||||
status?: PriceListStatus
|
||||
|
||||
@IsEnum(PriceListType)
|
||||
type: PriceListType
|
||||
|
||||
@IsArray()
|
||||
@Type(() => AdminPriceListPricesCreateReq)
|
||||
@ValidateNested({ each: true })
|
||||
prices: AdminPriceListPricesCreateReq[]
|
||||
|
||||
@IsOptional()
|
||||
@IsObject()
|
||||
rules?: Record<string, string[]>
|
||||
}
|
||||
|
||||
export class AdminPriceListPricesCreateReq {
|
||||
@IsString()
|
||||
currency_code: string
|
||||
|
||||
@IsInt()
|
||||
amount: number
|
||||
|
||||
@IsString()
|
||||
variant_id: string
|
||||
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
min_quantity?: number
|
||||
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
max_quantity?: number
|
||||
|
||||
@IsOptional()
|
||||
@IsObject()
|
||||
rules?: Record<string, string>
|
||||
}
|
||||
|
||||
export class AdminPostPriceListsPriceListReq {
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
title?: string
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
description?: string
|
||||
|
||||
@IsOptional()
|
||||
@Transform(transformOptionalDate)
|
||||
starts_at?: string
|
||||
|
||||
@IsOptional()
|
||||
@Transform(transformOptionalDate)
|
||||
ends_at?: string
|
||||
|
||||
@IsOptional()
|
||||
@IsEnum(PriceListStatus)
|
||||
status?: PriceListStatus
|
||||
|
||||
@IsOptional()
|
||||
@IsEnum(PriceListType)
|
||||
type?: PriceListType
|
||||
|
||||
@IsOptional()
|
||||
@IsArray()
|
||||
prices: (AdminPriceListPricesCreateReq | AdminPriceListPricesUpdateReq)[]
|
||||
|
||||
@IsOptional()
|
||||
@IsObject()
|
||||
rules?: Record<string, string[]>
|
||||
}
|
||||
|
||||
export class AdminPostPriceListsPriceListPricesReq {
|
||||
@IsOptional()
|
||||
@IsArray()
|
||||
prices: (AdminPriceListPricesCreateReq | AdminPriceListPricesUpdateReq)[]
|
||||
}
|
||||
|
||||
export class AdminDeletePriceListsPriceListPricesReq {
|
||||
@IsOptional()
|
||||
@IsArray()
|
||||
@IsString({ each: true })
|
||||
ids: string[]
|
||||
}
|
||||
|
||||
export class AdminPriceListPricesUpdateReq {
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
id: string
|
||||
|
||||
@IsOptional()
|
||||
@ValidateIf((object) => !object.id)
|
||||
@IsString()
|
||||
currency_code?: string
|
||||
|
||||
@IsOptional()
|
||||
@ValidateIf((object) => !object.id)
|
||||
@IsInt()
|
||||
amount?: number
|
||||
|
||||
@IsOptional()
|
||||
@ValidateIf((object) => !object.id)
|
||||
@IsString()
|
||||
variant_id: string
|
||||
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
min_quantity?: number
|
||||
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
max_quantity?: number
|
||||
|
||||
@IsOptional()
|
||||
@IsObject()
|
||||
rules?: Record<string, string>
|
||||
}
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
import { updatePriceLists } from "@medusajs/core-flows"
|
||||
import { MedusaContainer } from "@medusajs/types"
|
||||
import { MedusaV2Flag } from "@medusajs/utils"
|
||||
import { Type } from "class-transformer"
|
||||
import { IsArray, IsBoolean, IsOptional, ValidateNested } from "class-validator"
|
||||
import { EntityManager } from "typeorm"
|
||||
@@ -9,7 +6,6 @@ import { PriceList } from "../../../.."
|
||||
import PriceListService from "../../../../services/price-list"
|
||||
import { AdminPriceListPricesUpdateReq } from "../../../../types/price-list"
|
||||
import { validator } from "../../../../utils/validator"
|
||||
import { getPriceListPricingModule } from "./modules-queries"
|
||||
|
||||
/**
|
||||
* @oas [post] /admin/price-lists/{id}/prices/batch
|
||||
@@ -124,48 +120,22 @@ import { getPriceListPricingModule } from "./modules-queries"
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
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(AdminPostPriceListPricesPricesReq, req.body)
|
||||
|
||||
if (featureFlagRouter.isFeatureEnabled(MedusaV2Flag.key)) {
|
||||
const updatePriceListWorkflow = updatePriceLists(req.scope)
|
||||
await manager.transaction(async (transactionManager) => {
|
||||
await priceListService
|
||||
.withTransaction(transactionManager)
|
||||
.addPrices(id, validated.prices, validated.override)
|
||||
})
|
||||
|
||||
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,
|
||||
})
|
||||
}
|
||||
const priceList = await priceListService.retrieve(id, {
|
||||
select: defaultAdminPriceListFields as (keyof PriceList)[],
|
||||
relations: defaultAdminPriceListRelations,
|
||||
})
|
||||
|
||||
res.json({ price_list: priceList })
|
||||
}
|
||||
|
||||
@@ -1,11 +1,4 @@
|
||||
import { MedusaContainer, PricingTypes, WorkflowTypes } from "@medusajs/types"
|
||||
import {
|
||||
FlagRouter,
|
||||
MedusaV2Flag,
|
||||
PriceListStatus,
|
||||
PriceListType,
|
||||
} from "@medusajs/utils"
|
||||
import { createPriceLists } from "@medusajs/core-flows"
|
||||
import { PriceListStatus, PriceListType } from "@medusajs/utils"
|
||||
import { Transform, Type } from "class-transformer"
|
||||
import {
|
||||
IsArray,
|
||||
@@ -26,7 +19,6 @@ import {
|
||||
CreatePriceListInput,
|
||||
} from "../../../../types/price-list"
|
||||
import { FeatureFlagDecorators } from "../../../../utils/feature-flag-decorators"
|
||||
import { getPriceListPricingModule } from "./modules-queries"
|
||||
import { transformOptionalDate } from "../../../../utils/validators/date-transform"
|
||||
|
||||
/**
|
||||
@@ -155,57 +147,17 @@ export default async (req: Request, res) => {
|
||||
req.scope.resolve("priceListService")
|
||||
|
||||
const manager: EntityManager = req.scope.resolve("manager")
|
||||
const featureFlagRouter: FlagRouter = req.scope.resolve("featureFlagRouter")
|
||||
let priceList
|
||||
|
||||
const isMedusaV2FlagEnabled = featureFlagRouter.isFeatureEnabled(
|
||||
MedusaV2Flag.key
|
||||
)
|
||||
let priceList = await manager.transaction(async (transactionManager) => {
|
||||
return await priceListService
|
||||
.withTransaction(transactionManager)
|
||||
.create(req.validatedBody as CreatePriceListInput)
|
||||
})
|
||||
|
||||
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,
|
||||
})
|
||||
}
|
||||
priceList = await priceListService.retrieve(priceList.id, {
|
||||
select: defaultAdminPriceListFields as (keyof PriceList)[],
|
||||
relations: defaultAdminPriceListRelations,
|
||||
})
|
||||
|
||||
res.json({ price_list: priceList })
|
||||
}
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
import { WorkflowTypes } from "@medusajs/types"
|
||||
import { FlagRouter, MedusaV2Flag } from "@medusajs/utils"
|
||||
import { removePriceLists } from "@medusajs/core-flows"
|
||||
import { EntityManager } from "typeorm"
|
||||
import PriceListService from "../../../../services/price-list"
|
||||
|
||||
@@ -86,34 +83,13 @@ import PriceListService from "../../../../services/price-list"
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
|
||||
const featureFlagRouter: FlagRouter = req.scope.resolve("featureFlagRouter")
|
||||
const manager: EntityManager = req.scope.resolve("manager")
|
||||
|
||||
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)
|
||||
})
|
||||
}
|
||||
const priceListService: PriceListService =
|
||||
req.scope.resolve("priceListService")
|
||||
await manager.transaction(async (transactionManager) => {
|
||||
await priceListService.withTransaction(transactionManager).delete(id)
|
||||
})
|
||||
|
||||
res.json({
|
||||
id,
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
import { FlagRouter, MedusaV2Flag } from "@medusajs/utils"
|
||||
import { ArrayNotEmpty, IsString } from "class-validator"
|
||||
import { EntityManager } from "typeorm"
|
||||
import PriceListService from "../../../../services/price-list"
|
||||
import { validator } from "../../../../utils/validator"
|
||||
import { WorkflowTypes } from "@medusajs/types"
|
||||
import { removePriceListPrices } from "@medusajs/core-flows"
|
||||
|
||||
/**
|
||||
* @oas [delete] /admin/price-lists/{id}/prices/batch
|
||||
@@ -101,43 +98,20 @@ import { removePriceListPrices } from "@medusajs/core-flows"
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
|
||||
const validated = await validator(
|
||||
AdminDeletePriceListPricesPricesReq,
|
||||
req.body
|
||||
)
|
||||
|
||||
const manager: EntityManager = req.scope.resolve("manager")
|
||||
const priceListService: PriceListService =
|
||||
req.scope.resolve("priceListService")
|
||||
const featureFlagRouter: FlagRouter = req.scope.resolve("featureFlagRouter")
|
||||
|
||||
const manager: EntityManager = req.scope.resolve("manager")
|
||||
|
||||
const isMedusaV2FlagEnabled = featureFlagRouter.isFeatureEnabled(
|
||||
MedusaV2Flag.key
|
||||
)
|
||||
|
||||
if (isMedusaV2FlagEnabled) {
|
||||
const deletePriceListPricesWorkflow = removePriceListPrices(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)
|
||||
})
|
||||
}
|
||||
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 })
|
||||
}
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
import { WorkflowTypes } from "@medusajs/types"
|
||||
import { FlagRouter, MedusaV2Flag } from "@medusajs/utils"
|
||||
import { removePriceListProductPrices } from "@medusajs/core-flows"
|
||||
import { EntityManager } from "typeorm"
|
||||
import PriceListService from "../../../../services/price-list"
|
||||
|
||||
@@ -94,47 +91,18 @@ import PriceListService from "../../../../services/price-list"
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
const { id, product_id } = req.params
|
||||
|
||||
const priceListService: PriceListService =
|
||||
req.scope.resolve("priceListService")
|
||||
|
||||
const featureFlagRouter: FlagRouter = req.scope.resolve("featureFlagRouter")
|
||||
const manager: EntityManager = req.scope.resolve("manager")
|
||||
|
||||
const isMedusaV2FlagEnabled = featureFlagRouter.isFeatureEnabled(
|
||||
MedusaV2Flag.key
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
const [deletedIds] = await manager.transaction(async (transactionManager) => {
|
||||
return await priceListService
|
||||
.withTransaction(transactionManager)
|
||||
.deleteProductPrices(id, [product_id])
|
||||
})
|
||||
deletedPriceIds = deletedIds
|
||||
|
||||
return res.json({
|
||||
ids: deletedPriceIds,
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
import { FlagRouter, MedusaV2Flag } from "@medusajs/utils"
|
||||
import { removePriceListProductPrices } from "@medusajs/core-flows"
|
||||
import { ArrayNotEmpty, IsString } from "class-validator"
|
||||
import { Request, Response } from "express"
|
||||
import { EntityManager } from "typeorm"
|
||||
import PriceListService from "../../../../services/price-list"
|
||||
import { validator } from "../../../../utils/validator"
|
||||
import { WorkflowTypes } from "@medusajs/types"
|
||||
|
||||
/**
|
||||
* @oas [delete] /admin/price-lists/{id}/products/prices/batch
|
||||
@@ -114,43 +111,16 @@ export default async (req: Request, res: Response) => {
|
||||
const priceListService: PriceListService =
|
||||
req.scope.resolve("priceListService")
|
||||
|
||||
const featureFlagRouter: FlagRouter = req.scope.resolve("featureFlagRouter")
|
||||
const manager: EntityManager = req.scope.resolve("manager")
|
||||
|
||||
const isMedusaV2FlagEnabled = featureFlagRouter.isFeatureEnabled(
|
||||
MedusaV2Flag.key
|
||||
)
|
||||
|
||||
let deletedPriceIds: string[] = []
|
||||
|
||||
if (isMedusaV2FlagEnabled) {
|
||||
const deletePriceListProductsWorkflow = removePriceListProductPrices(
|
||||
req.scope
|
||||
)
|
||||
const [deletedIds] = await manager.transaction(async (transactionManager) => {
|
||||
return await priceListService
|
||||
.withTransaction(transactionManager)
|
||||
.deleteProductPrices(id, validated.product_ids)
|
||||
})
|
||||
|
||||
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
|
||||
}
|
||||
deletedPriceIds = deletedIds
|
||||
|
||||
return res.json({
|
||||
ids: deletedPriceIds,
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
import { FlagRouter, MedusaV2Flag } from "@medusajs/utils"
|
||||
import { removePriceListVariantPrices } from "@medusajs/core-flows"
|
||||
import { EntityManager } from "typeorm"
|
||||
import PriceListService from "../../../../services/price-list"
|
||||
import { WorkflowTypes } from "@medusajs/types"
|
||||
|
||||
/**
|
||||
* @oas [delete] /admin/price-lists/{id}/variants/{variant_id}/prices
|
||||
@@ -97,46 +94,16 @@ export default async (req, res) => {
|
||||
|
||||
const priceListService: PriceListService =
|
||||
req.scope.resolve("priceListService")
|
||||
const featureFlagRouter: FlagRouter = req.scope.resolve("featureFlagRouter")
|
||||
const manager: EntityManager = req.scope.resolve("manager")
|
||||
|
||||
const isMedusaV2FlagEnabled = featureFlagRouter.isFeatureEnabled(
|
||||
MedusaV2Flag.key
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
const [deletedIds] = await manager.transaction(async (transactionManager) => {
|
||||
return await priceListService
|
||||
.withTransaction(transactionManager)
|
||||
.deleteVariantPrices(id, [variant_id])
|
||||
})
|
||||
|
||||
return res.json({
|
||||
ids: deletedPriceIds,
|
||||
ids: deletedIds,
|
||||
object: "money-amount",
|
||||
deleted: true,
|
||||
})
|
||||
|
||||
@@ -11,13 +11,12 @@ import {
|
||||
extendedFindParamsMixin,
|
||||
} from "../../../../types/common"
|
||||
|
||||
import { FlagRouter, MedusaV2Flag } from "@medusajs/utils"
|
||||
import { Type } from "class-transformer"
|
||||
import { Request } from "express"
|
||||
import { ProductStatus } from "../../../../models"
|
||||
import PriceListService from "../../../../services/price-list"
|
||||
import { FilterableProductProps } from "../../../../types/product"
|
||||
import { IsType, listProducts } from "../../../../utils"
|
||||
import { IsType } from "../../../../utils"
|
||||
|
||||
/**
|
||||
* @oas [get] /admin/price-lists/{id}/products
|
||||
@@ -228,10 +227,6 @@ import { IsType, listProducts } from "../../../../utils"
|
||||
export default async (req: Request, res) => {
|
||||
const { id } = req.params
|
||||
const { offset, limit } = req.validatedQuery
|
||||
const featureFlagRouter: FlagRouter = req.scope.resolve("featureFlagRouter")
|
||||
let products
|
||||
let count
|
||||
|
||||
const priceListService: PriceListService =
|
||||
req.scope.resolve("priceListService")
|
||||
|
||||
@@ -240,19 +235,11 @@ export default async (req: Request, res) => {
|
||||
price_list_id: [id],
|
||||
}
|
||||
|
||||
if (featureFlagRouter.isFeatureEnabled(MedusaV2Flag.key)) {
|
||||
;[products, count] = await listProducts(
|
||||
req.scope,
|
||||
filterableFields,
|
||||
req.listConfig
|
||||
)
|
||||
} else {
|
||||
;[products, count] = await priceListService.listProducts(
|
||||
id,
|
||||
filterableFields,
|
||||
req.listConfig
|
||||
)
|
||||
}
|
||||
const [products, count] = await priceListService.listProducts(
|
||||
id,
|
||||
filterableFields,
|
||||
req.listConfig
|
||||
)
|
||||
|
||||
res.json({
|
||||
products,
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { MedusaContainer, PricingTypes, WorkflowTypes } from "@medusajs/types"
|
||||
import { MedusaV2Flag, PriceListStatus, PriceListType } from "@medusajs/utils"
|
||||
import { PriceListStatus, PriceListType } from "@medusajs/utils"
|
||||
import {
|
||||
IsArray,
|
||||
IsBoolean,
|
||||
@@ -10,7 +9,6 @@ import {
|
||||
} from "class-validator"
|
||||
import { defaultAdminPriceListFields, defaultAdminPriceListRelations } from "."
|
||||
|
||||
import { updatePriceLists } from "@medusajs/core-flows"
|
||||
import { Transform, Type } from "class-transformer"
|
||||
import { EntityManager } from "typeorm"
|
||||
import { PriceList } from "../../../.."
|
||||
@@ -19,7 +17,6 @@ import PriceListService from "../../../../services/price-list"
|
||||
import { AdminPriceListPricesUpdateReq } from "../../../../types/price-list"
|
||||
import { FeatureFlagDecorators } from "../../../../utils/feature-flag-decorators"
|
||||
import { validator } from "../../../../utils/validator"
|
||||
import { getPriceListPricingModule } from "./modules-queries"
|
||||
import { transformOptionalDate } from "../../../../utils/validators/date-transform"
|
||||
|
||||
/**
|
||||
@@ -119,8 +116,6 @@ import { transformOptionalDate } from "../../../../utils/validators/date-transfo
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
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")
|
||||
@@ -130,48 +125,16 @@ export default async (req, res) => {
|
||||
req.body
|
||||
)
|
||||
|
||||
if (featureFlagRouter.isFeatureEnabled(MedusaV2Flag.key)) {
|
||||
const updateVariantsWorkflow = updatePriceLists(req.scope)
|
||||
const customerGroups = validated.customer_groups
|
||||
delete validated.customer_groups
|
||||
await manager.transaction(async (transactionManager) => {
|
||||
return await priceListService
|
||||
.withTransaction(transactionManager)
|
||||
.update(id, validated)
|
||||
})
|
||||
|
||||
const updatePriceListInput = {
|
||||
id,
|
||||
...validated,
|
||||
} as PricingTypes.UpdatePriceListDTO
|
||||
|
||||
if (Array.isArray(customerGroups)) {
|
||||
updatePriceListInput.rules = {
|
||||
customer_group_id: customerGroups.map((group) => group.id),
|
||||
}
|
||||
}
|
||||
|
||||
const input = {
|
||||
price_lists: [updatePriceListInput],
|
||||
} 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,
|
||||
})
|
||||
}
|
||||
const priceList = await priceListService.retrieve(id, {
|
||||
select: defaultAdminPriceListFields as (keyof PriceList)[],
|
||||
relations: defaultAdminPriceListRelations,
|
||||
})
|
||||
|
||||
res.json({ price_list: priceList })
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import express from "express"
|
||||
import loaders from "../loaders"
|
||||
import Logger from "../loaders/logger"
|
||||
import { PriceList } from "../models"
|
||||
import { CurrencyService, PriceListService } from "../services"
|
||||
import { PriceListService } from "../services"
|
||||
import { createDefaultRuleTypes } from "./utils/create-default-rule-types"
|
||||
import { migrateProductVariantPricing } from "./utils/migrate-money-amounts-to-pricing-module"
|
||||
|
||||
@@ -109,7 +109,7 @@ const migratePriceLists = async (container: AwilixContainer) => {
|
||||
pricingModuleService.addPriceListPrices(
|
||||
priceListsToUpdate.map((priceList) => {
|
||||
return {
|
||||
priceListId: priceList.id,
|
||||
price_list_id: priceList.id,
|
||||
prices: priceList.prices
|
||||
.filter((price) =>
|
||||
variantIdPriceSetIdMap.has(price.variants?.[0]?.id)
|
||||
@@ -136,7 +136,7 @@ const migratePriceLists = async (container: AwilixContainer) => {
|
||||
pricingModuleService.createPriceLists(
|
||||
priceListsToCreate.map(
|
||||
({ name: title, prices, customer_groups, ...priceList }) => {
|
||||
const createData: PricingTypes.CreatePriceListDTO = {
|
||||
const createData: any = {
|
||||
...priceList,
|
||||
title,
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { Modules } from "@medusajs/modules-sdk"
|
||||
import {
|
||||
CreatePriceRuleDTO,
|
||||
CreatePriceSetDTO,
|
||||
@@ -5,9 +6,8 @@ import {
|
||||
PricingTypes,
|
||||
} from "@medusajs/types"
|
||||
import { PriceListType } from "@medusajs/utils"
|
||||
import { SuiteOptions, moduleIntegrationTestRunner } from "medusa-test-utils"
|
||||
import { seedPriceData } from "../../../__fixtures__/seed-price-data"
|
||||
import { Modules } from "@medusajs/modules-sdk"
|
||||
import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils"
|
||||
|
||||
jest.setTimeout(30000)
|
||||
|
||||
@@ -16,7 +16,7 @@ const defaultRules = {
|
||||
region_id: ["DE", "DK"],
|
||||
}
|
||||
|
||||
const defaultPriceListPrices: PricingTypes.PriceListPriceDTO[] = [
|
||||
const defaultPriceListPrices: PricingTypes.CreatePriceListPriceDTO[] = [
|
||||
{
|
||||
amount: 232,
|
||||
currency_code: "PLN",
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { Modules } from "@medusajs/modules-sdk"
|
||||
import { IPricingModuleService } from "@medusajs/types"
|
||||
import { SqlEntityManager } from "@mikro-orm/postgresql"
|
||||
import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils"
|
||||
import { createPriceLists } from "../../../__fixtures__/price-list"
|
||||
import { createPriceListRules } from "../../../__fixtures__/price-list-rules"
|
||||
import { createRuleTypes } from "../../../__fixtures__/rule-type"
|
||||
import { Modules } from "@medusajs/modules-sdk"
|
||||
import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils"
|
||||
|
||||
jest.setTimeout(30000)
|
||||
|
||||
@@ -240,7 +240,7 @@ moduleIntegrationTestRunner({
|
||||
])
|
||||
|
||||
await service.setPriceListRules({
|
||||
priceListId: "price-list-1",
|
||||
price_list_id: "price-list-1",
|
||||
rules: {
|
||||
sales_channel: "sc-1",
|
||||
},
|
||||
@@ -280,7 +280,7 @@ moduleIntegrationTestRunner({
|
||||
])
|
||||
|
||||
await service.setPriceListRules({
|
||||
priceListId: "price-list-1",
|
||||
price_list_id: "price-list-1",
|
||||
rules: {
|
||||
sales_channel: ["sc-1", "sc-2"],
|
||||
},
|
||||
@@ -315,7 +315,7 @@ moduleIntegrationTestRunner({
|
||||
describe("removePriceListRules", () => {
|
||||
it("should remove a priceListRule from a priceList", async () => {
|
||||
await service.removePriceListRules({
|
||||
priceListId: "price-list-1",
|
||||
price_list_id: "price-list-1",
|
||||
rules: ["currency_code"],
|
||||
})
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Modules } from "@medusajs/modules-sdk"
|
||||
import { IPricingModuleService } from "@medusajs/types"
|
||||
import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils"
|
||||
import { createPriceLists } from "../../../__fixtures__/price-list"
|
||||
import { createPriceSets } from "../../../__fixtures__/price-set"
|
||||
import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils"
|
||||
|
||||
jest.setTimeout(30000)
|
||||
|
||||
@@ -666,7 +666,7 @@ moduleIntegrationTestRunner({
|
||||
it("should add a price to a priceList successfully", async () => {
|
||||
await service.addPriceListPrices([
|
||||
{
|
||||
priceListId: "price-list-1",
|
||||
price_list_id: "price-list-1",
|
||||
prices: [
|
||||
{
|
||||
amount: 123,
|
||||
@@ -734,7 +734,7 @@ moduleIntegrationTestRunner({
|
||||
try {
|
||||
await service.addPriceListPrices([
|
||||
{
|
||||
priceListId: "price-list-1",
|
||||
price_list_id: "price-list-1",
|
||||
prices: [
|
||||
{
|
||||
amount: 123,
|
||||
@@ -774,7 +774,7 @@ moduleIntegrationTestRunner({
|
||||
|
||||
await service.addPriceListPrices([
|
||||
{
|
||||
priceListId: "price-list-1",
|
||||
price_list_id: "price-list-1",
|
||||
prices: [
|
||||
{
|
||||
amount: 123,
|
||||
@@ -843,6 +843,203 @@ moduleIntegrationTestRunner({
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("updatePriceListPrices", () => {
|
||||
it("should update a price to a priceList successfully", async () => {
|
||||
const [priceSet] = await service.create([
|
||||
{
|
||||
rules: [
|
||||
{ rule_attribute: "region_id" },
|
||||
{ rule_attribute: "customer_group_id" },
|
||||
],
|
||||
},
|
||||
])
|
||||
|
||||
await service.addPriceListPrices([
|
||||
{
|
||||
price_list_id: "price-list-1",
|
||||
prices: [
|
||||
{
|
||||
id: "test-price-id",
|
||||
amount: 123,
|
||||
currency_code: "EUR",
|
||||
price_set_id: priceSet.id,
|
||||
rules: {
|
||||
region_id: "test",
|
||||
},
|
||||
} as any,
|
||||
],
|
||||
},
|
||||
])
|
||||
|
||||
await service.updatePriceListPrices([
|
||||
{
|
||||
price_list_id: "price-list-1",
|
||||
prices: [
|
||||
{
|
||||
id: "test-price-id",
|
||||
price_set_id: priceSet.id,
|
||||
rules: {
|
||||
region_id: "new test",
|
||||
customer_group_id: "new test",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
])
|
||||
|
||||
const [priceList] = await service.listPriceLists(
|
||||
{ id: ["price-list-1"] },
|
||||
{
|
||||
relations: [
|
||||
"price_set_money_amounts.money_amount",
|
||||
"price_set_money_amounts.price_set",
|
||||
"price_set_money_amounts.price_rules",
|
||||
"price_set_money_amounts.price_rules.rule_type",
|
||||
"price_list_rules.price_list_rule_values",
|
||||
"price_list_rules.rule_type",
|
||||
],
|
||||
select: [
|
||||
"id",
|
||||
"price_set_money_amounts.price_rules.value",
|
||||
"price_set_money_amounts.price_rules.rule_type.rule_attribute",
|
||||
"price_set_money_amounts.rules_count",
|
||||
"price_set_money_amounts.money_amount.amount",
|
||||
"price_set_money_amounts.money_amount.currency_code",
|
||||
"price_set_money_amounts.money_amount.price_list_id",
|
||||
"price_list_rules.price_list_rule_values.value",
|
||||
"price_list_rules.rule_type.rule_attribute",
|
||||
],
|
||||
}
|
||||
)
|
||||
|
||||
expect(priceList).toEqual(
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
price_set_money_amounts: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
rules_count: 2,
|
||||
price_rules: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
value: "new test",
|
||||
rule_type: expect.objectContaining({
|
||||
rule_attribute: "region_id",
|
||||
}),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
value: "new test",
|
||||
rule_type: expect.objectContaining({
|
||||
rule_attribute: "customer_group_id",
|
||||
}),
|
||||
}),
|
||||
]),
|
||||
price_list: expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
}),
|
||||
money_amount: expect.objectContaining({
|
||||
amount: 123,
|
||||
currency_code: "EUR",
|
||||
}),
|
||||
}),
|
||||
]),
|
||||
price_list_rules: [],
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("should fail to add a price with non-existing rule-types in the price-set to a priceList", async () => {
|
||||
await service.createRuleTypes([
|
||||
{ name: "twitter_handle", rule_attribute: "twitter_handle" },
|
||||
{ name: "region_id", rule_attribute: "region_id" },
|
||||
])
|
||||
|
||||
const [priceSet] = await service.create([
|
||||
{ rules: [{ rule_attribute: "region_id" }] },
|
||||
])
|
||||
|
||||
await service.addPriceListPrices([
|
||||
{
|
||||
price_list_id: "price-list-1",
|
||||
prices: [
|
||||
{
|
||||
id: "test-price-id",
|
||||
amount: 123,
|
||||
currency_code: "EUR",
|
||||
price_set_id: priceSet.id,
|
||||
rules: { region_id: "test" },
|
||||
} as any,
|
||||
],
|
||||
},
|
||||
])
|
||||
|
||||
const error = await service
|
||||
.updatePriceListPrices([
|
||||
{
|
||||
price_list_id: "price-list-1",
|
||||
prices: [
|
||||
{
|
||||
id: "test-price-id",
|
||||
amount: 123,
|
||||
price_set_id: priceSet.id,
|
||||
rules: { twitter_handle: "owjuhl" },
|
||||
},
|
||||
],
|
||||
},
|
||||
])
|
||||
.catch((e) => e)
|
||||
|
||||
expect(error.message).toEqual(
|
||||
`Invalid rule type configuration: Price set rules doesn't exist for rule_attribute "twitter_handle" in price set ${priceSet.id}`
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("removePrices", () => {
|
||||
it("should remove prices from a priceList successfully", async () => {
|
||||
const [priceSet] = await service.create([
|
||||
{ rules: [{ rule_attribute: "region_id" }] },
|
||||
])
|
||||
|
||||
await service.addPriceListPrices([
|
||||
{
|
||||
price_list_id: "price-list-1",
|
||||
prices: [
|
||||
{
|
||||
amount: 123,
|
||||
currency_code: "EUR",
|
||||
price_set_id: priceSet.id,
|
||||
},
|
||||
],
|
||||
},
|
||||
])
|
||||
|
||||
let [priceList] = await service.listPriceLists(
|
||||
{ id: ["price-list-1"] },
|
||||
{
|
||||
relations: ["price_set_money_amounts"],
|
||||
select: ["price_set_money_amounts.id"],
|
||||
}
|
||||
)
|
||||
|
||||
await service.removePrices(
|
||||
priceList.price_set_money_amounts!.map((psma) => psma.id)
|
||||
)
|
||||
;[priceList] = await service.listPriceLists(
|
||||
{ id: ["price-list-1"] },
|
||||
{
|
||||
relations: ["price_set_money_amounts"],
|
||||
select: ["id", "price_set_money_amounts.id"],
|
||||
}
|
||||
)
|
||||
|
||||
expect(priceList).toEqual(
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
price_set_money_amounts: [],
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
@@ -5,10 +5,10 @@ import {
|
||||
} from "@medusajs/types"
|
||||
import { SqlEntityManager } from "@mikro-orm/postgresql"
|
||||
|
||||
import { PriceSetRuleType } from "../../../../src"
|
||||
import { seedPriceData } from "../../../__fixtures__/seed-price-data"
|
||||
import { Modules } from "@medusajs/modules-sdk"
|
||||
import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils"
|
||||
import { PriceSetRuleType } from "../../../../src"
|
||||
import { seedPriceData } from "../../../__fixtures__/seed-price-data"
|
||||
|
||||
jest.setTimeout(30000)
|
||||
|
||||
|
||||
@@ -1031,9 +1031,7 @@ export default class PricingModuleService<
|
||||
.flat()
|
||||
|
||||
const existingPriceListRules = await this.listPriceListRules(
|
||||
{
|
||||
id: priceListRuleIds,
|
||||
},
|
||||
{ id: priceListRuleIds },
|
||||
{},
|
||||
sharedContext
|
||||
)
|
||||
@@ -1046,9 +1044,7 @@ export default class PricingModuleService<
|
||||
}
|
||||
|
||||
const ruleTypes = await this.listRuleTypes(
|
||||
{
|
||||
rule_attribute: ruleAttributes,
|
||||
},
|
||||
{ rule_attribute: ruleAttributes },
|
||||
{ take: null },
|
||||
sharedContext
|
||||
)
|
||||
@@ -1059,6 +1055,7 @@ export default class PricingModuleService<
|
||||
|
||||
for (const priceListData of data) {
|
||||
const { rules, ...priceListOnlyData } = priceListData
|
||||
|
||||
const updatePriceListData: any = {
|
||||
...priceListOnlyData,
|
||||
}
|
||||
@@ -1083,12 +1080,7 @@ export default class PricingModuleService<
|
||||
|
||||
if (!ruleType) {
|
||||
;[ruleType] = await this.createRuleTypes(
|
||||
[
|
||||
{
|
||||
name: ruleAttribute,
|
||||
rule_attribute: ruleAttribute,
|
||||
},
|
||||
],
|
||||
[{ name: ruleAttribute, rule_attribute: ruleAttribute }],
|
||||
sharedContext
|
||||
)
|
||||
|
||||
@@ -1107,12 +1099,7 @@ export default class PricingModuleService<
|
||||
|
||||
for (const ruleValue of ruleValues as string[]) {
|
||||
await this.priceListRuleValueService_.create(
|
||||
[
|
||||
{
|
||||
price_list_rule: priceListRule,
|
||||
value: ruleValue,
|
||||
},
|
||||
],
|
||||
[{ price_list_rule: priceListRule, value: ruleValue }],
|
||||
sharedContext
|
||||
)
|
||||
}
|
||||
@@ -1161,6 +1148,185 @@ export default class PricingModuleService<
|
||||
})
|
||||
}
|
||||
|
||||
@InjectManager("baseRepository_")
|
||||
async updatePriceListPrices(
|
||||
data: PricingTypes.UpdatePriceListPricesDTO[],
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): Promise<PricingTypes.PriceListDTO[]> {
|
||||
return await this.updatePriceListPrices_(data, sharedContext)
|
||||
}
|
||||
|
||||
@InjectTransactionManager("baseRepository_")
|
||||
protected async updatePriceListPrices_(
|
||||
data: PricingTypes.UpdatePriceListPricesDTO[],
|
||||
sharedContext: Context = {}
|
||||
): Promise<PricingTypes.PriceListDTO[]> {
|
||||
const ruleTypeAttributes: string[] = []
|
||||
const priceListIds: string[] = []
|
||||
const moneyAmountIds: string[] = []
|
||||
const priceSetIds = data
|
||||
.map((d) => d.prices.map((price) => price.price_set_id))
|
||||
.flat()
|
||||
|
||||
for (const priceListData of data) {
|
||||
priceListIds.push(priceListData.price_list_id)
|
||||
|
||||
for (const price of priceListData.prices) {
|
||||
moneyAmountIds.push(price.id)
|
||||
ruleTypeAttributes.push(...Object.keys(price.rules || {}))
|
||||
}
|
||||
}
|
||||
|
||||
const moneyAmounts = await this.listMoneyAmounts(
|
||||
{ id: moneyAmountIds },
|
||||
{
|
||||
take: null,
|
||||
relations: [
|
||||
"price_set_money_amount",
|
||||
"price_set_money_amount.price_rules",
|
||||
],
|
||||
},
|
||||
sharedContext
|
||||
)
|
||||
|
||||
const moneyAmountMap: Map<string, PricingTypes.MoneyAmountDTO> = new Map(
|
||||
moneyAmounts.map((ma) => [ma.id, ma])
|
||||
)
|
||||
|
||||
const ruleTypes = await this.listRuleTypes(
|
||||
{ rule_attribute: ruleTypeAttributes },
|
||||
{ take: null },
|
||||
sharedContext
|
||||
)
|
||||
|
||||
const ruleTypeMap: Map<string, RuleTypeDTO> = new Map(
|
||||
ruleTypes.map((rt) => [rt.rule_attribute, rt])
|
||||
)
|
||||
|
||||
const priceSets = await this.list(
|
||||
{ id: priceSetIds },
|
||||
{ relations: ["rule_types"] },
|
||||
sharedContext
|
||||
)
|
||||
|
||||
const priceSetRuleTypeMap: Map<string, Set<string>> = priceSets.reduce(
|
||||
(acc, curr) => {
|
||||
const priceSetRuleAttributeSet: Set<string> =
|
||||
acc.get(curr.id) || new Set()
|
||||
|
||||
for (const rt of curr.rule_types ?? []) {
|
||||
priceSetRuleAttributeSet.add(rt.rule_attribute)
|
||||
}
|
||||
|
||||
acc.set(curr.id, priceSetRuleAttributeSet)
|
||||
return acc
|
||||
},
|
||||
new Map()
|
||||
)
|
||||
|
||||
const ruleTypeErrors: string[] = []
|
||||
|
||||
for (const priceListData of data) {
|
||||
for (const price of priceListData.prices) {
|
||||
for (const ruleAttribute of Object.keys(price.rules ?? {})) {
|
||||
if (
|
||||
!priceSetRuleTypeMap.get(price.price_set_id)?.has(ruleAttribute)
|
||||
) {
|
||||
ruleTypeErrors.push(
|
||||
`rule_attribute "${ruleAttribute}" in price set ${price.price_set_id}`
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ruleTypeErrors.length) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Invalid rule type configuration: Price set rules doesn't exist for ${ruleTypeErrors.join(
|
||||
", "
|
||||
)}`
|
||||
)
|
||||
}
|
||||
|
||||
const priceLists = await this.listPriceLists(
|
||||
{ id: priceListIds },
|
||||
{ take: null },
|
||||
sharedContext
|
||||
)
|
||||
|
||||
const priceListMap = new Map(priceLists.map((p) => [p.id, p]))
|
||||
|
||||
for (const { price_list_id: priceListId, prices } of data) {
|
||||
const priceList = priceListMap.get(priceListId)
|
||||
|
||||
if (!priceList) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Price list with id: ${priceListId} not found`
|
||||
)
|
||||
}
|
||||
|
||||
const moneyAmountsToUpdate: PricingTypes.UpdateMoneyAmountDTO[] = []
|
||||
const priceRulesToDelete: string[] = []
|
||||
const priceRulesToCreate: PricingTypes.CreatePriceRuleDTO[] = []
|
||||
const psmaToUpdate: ServiceTypes.UpdatePriceSetMoneyAmountDTO[] = []
|
||||
|
||||
for (const price of prices) {
|
||||
const { rules, price_set_id, ...priceData } = price
|
||||
const moneyAmount = moneyAmountMap.get(price.id)!
|
||||
const priceSetMoneyAmount = moneyAmount.price_set_money_amount!
|
||||
const priceRules = priceSetMoneyAmount.price_rules!
|
||||
|
||||
moneyAmountsToUpdate.push(priceData)
|
||||
|
||||
if (typeof rules === "undefined") {
|
||||
continue
|
||||
}
|
||||
|
||||
psmaToUpdate.push({
|
||||
id: priceSetMoneyAmount!.id,
|
||||
rules_count: Object.keys(rules).length,
|
||||
})
|
||||
|
||||
priceRulesToDelete.push(...priceRules.map((pr) => pr.id))
|
||||
priceRulesToCreate.push(
|
||||
...Object.entries(rules).map(([ruleAttribute, ruleValue]) => ({
|
||||
price_set_id: price.price_set_id,
|
||||
rule_type_id: ruleTypeMap.get(ruleAttribute)!.id,
|
||||
value: ruleValue,
|
||||
price_set_money_amount_id: priceSetMoneyAmount.id,
|
||||
}))
|
||||
)
|
||||
}
|
||||
|
||||
await Promise.all([
|
||||
this.moneyAmountService_.update(moneyAmountsToUpdate),
|
||||
this.priceRuleService_.delete(priceRulesToDelete),
|
||||
this.priceRuleService_.create(priceRulesToCreate),
|
||||
this.priceSetMoneyAmountService_.update(psmaToUpdate),
|
||||
])
|
||||
}
|
||||
|
||||
return priceLists
|
||||
}
|
||||
|
||||
@InjectManager("baseRepository_")
|
||||
async removePrices(
|
||||
ids: string[],
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): Promise<void> {
|
||||
await this.removePrices_(ids, sharedContext)
|
||||
}
|
||||
|
||||
@InjectTransactionManager("baseRepository_")
|
||||
protected async removePrices_(
|
||||
ids: string[],
|
||||
sharedContext: Context = {}
|
||||
): Promise<void> {
|
||||
await this.priceSetMoneyAmountService_.delete(ids, sharedContext)
|
||||
}
|
||||
|
||||
@InjectManager("baseRepository_")
|
||||
async addPriceListPrices(
|
||||
data: PricingTypes.AddPriceListPricesDTO[],
|
||||
@@ -1179,7 +1345,7 @@ export default class PricingModuleService<
|
||||
const priceSetIds: string[] = []
|
||||
|
||||
for (const priceListData of data) {
|
||||
priceListIds.push(priceListData.priceListId)
|
||||
priceListIds.push(priceListData.price_list_id)
|
||||
|
||||
for (const price of priceListData.prices) {
|
||||
ruleTypeAttributes.push(...Object.keys(price.rules || {}))
|
||||
@@ -1251,7 +1417,7 @@ export default class PricingModuleService<
|
||||
|
||||
const priceListMap = new Map(priceLists.map((p) => [p.id, p]))
|
||||
|
||||
for (const { priceListId, prices } of data) {
|
||||
for (const { price_list_id: priceListId, prices } of data) {
|
||||
const priceList = priceListMap.get(priceListId)
|
||||
|
||||
if (!priceList) {
|
||||
@@ -1322,7 +1488,7 @@ export default class PricingModuleService<
|
||||
sharedContext: Context = {}
|
||||
): Promise<PricingTypes.PriceListDTO[]> {
|
||||
const priceLists = await this.priceListService_.list(
|
||||
{ id: data.map((d) => d.priceListId) },
|
||||
{ id: data.map((d) => d.price_list_id) },
|
||||
{
|
||||
relations: ["price_list_rules", "price_list_rules.rule_type"],
|
||||
},
|
||||
@@ -1347,7 +1513,7 @@ export default class PricingModuleService<
|
||||
|
||||
const priceRuleValues = new Map<string, Map<string, string[]>>()
|
||||
|
||||
for (const { priceListId, rules } of data) {
|
||||
for (const { price_list_id: priceListId, rules } of data) {
|
||||
const priceList = priceListMap.get(priceListId)
|
||||
|
||||
if (!priceList) {
|
||||
@@ -1465,7 +1631,7 @@ export default class PricingModuleService<
|
||||
sharedContext: Context = {}
|
||||
): Promise<PricingTypes.PriceListDTO[]> {
|
||||
const priceLists = await this.priceListService_.list(
|
||||
{ id: data.map((d) => d.priceListId) },
|
||||
{ id: data.map((d) => d.price_list_id) },
|
||||
{
|
||||
relations: ["price_list_rules", "price_list_rules.rule_type"],
|
||||
},
|
||||
@@ -1475,7 +1641,7 @@ export default class PricingModuleService<
|
||||
const priceListMap = new Map(priceLists.map((p) => [p.id, p]))
|
||||
|
||||
const idsToDelete: string[] = []
|
||||
for (const { priceListId, rules } of data) {
|
||||
for (const { price_list_id: priceListId, rules } of data) {
|
||||
const priceList = priceListMap.get(priceListId)
|
||||
|
||||
if (!priceList) {
|
||||
|
||||
@@ -3,8 +3,8 @@ import { PriceListStatus, PriceListType } from "@medusajs/utils"
|
||||
export interface CreatePriceListDTO {
|
||||
title: string
|
||||
description: string
|
||||
starts_at?: Date | string | null
|
||||
ends_at?: Date | string | null
|
||||
starts_at?: string | null
|
||||
ends_at?: string | null
|
||||
status?: PriceListStatus
|
||||
type?: PriceListType
|
||||
rules_count?: number
|
||||
@@ -13,8 +13,8 @@ export interface CreatePriceListDTO {
|
||||
export interface UpdatePriceListDTO {
|
||||
id: string
|
||||
title?: string
|
||||
starts_at?: Date | string | null
|
||||
ends_at?: Date | string | null
|
||||
starts_at?: string | null
|
||||
ends_at?: string | null
|
||||
status?: PriceListStatus
|
||||
number_rules?: number
|
||||
}
|
||||
|
||||
@@ -18,10 +18,10 @@ export interface UpdateMoneyAmountDTO {
|
||||
|
||||
export interface MoneyAmountDTO {
|
||||
id: string
|
||||
currency_code?: string
|
||||
amount?: number
|
||||
min_quantity?: number
|
||||
max_quantity?: number
|
||||
currency_code?: string | null
|
||||
amount?: number | null
|
||||
min_quantity?: number | null
|
||||
max_quantity?: number | null
|
||||
price_set_money_amount?: PriceSetMoneyAmountDTO
|
||||
created_at: Date
|
||||
updated_at: Date
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { PriceListStatus, PriceListType } from "@medusajs/utils"
|
||||
import {
|
||||
BaseFilterable,
|
||||
MoneyAmountDTO,
|
||||
@@ -6,12 +5,13 @@ import {
|
||||
PriceSetMoneyAmountDTO,
|
||||
RuleTypeDTO,
|
||||
} from "@medusajs/types"
|
||||
import { PriceListStatus, PriceListType } from "@medusajs/utils"
|
||||
|
||||
export interface CreatePriceListDTO {
|
||||
title: string
|
||||
description: string
|
||||
starts_at?: Date | string | null
|
||||
ends_at?: Date | string | null
|
||||
starts_at?: string | null
|
||||
ends_at?: string | null
|
||||
status?: PriceListStatus
|
||||
type?: PriceListType
|
||||
number_rules?: number
|
||||
@@ -20,8 +20,8 @@ export interface CreatePriceListDTO {
|
||||
export interface UpdatePriceListDTO {
|
||||
id: string
|
||||
title?: string
|
||||
starts_at?: Date | string | null
|
||||
ends_at?: Date | string | null
|
||||
starts_at?: string | null
|
||||
ends_at?: string | null
|
||||
status?: PriceListStatus
|
||||
number_rules?: number
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ export interface UpdatePriceSetMoneyAmountDTO {
|
||||
title?: string
|
||||
price_set?: PriceSetDTO
|
||||
money_amount?: MoneyAmountDTO
|
||||
rules_count?: number
|
||||
}
|
||||
|
||||
export interface CreatePriceSetMoneyAmountDTO {
|
||||
|
||||
@@ -86,7 +86,7 @@ export interface UpdateMoneyAmountDTO {
|
||||
/**
|
||||
* The code of the currency to associate with the money amount.
|
||||
*/
|
||||
currency_code?: string
|
||||
currency_code?: string | null
|
||||
/**
|
||||
* The price of this money amount.
|
||||
*/
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import { CreateMoneyAmountDTO, MoneyAmountDTO } from "./money-amount";
|
||||
|
||||
import { BaseFilterable } from "../../dal";
|
||||
import { PriceSetMoneyAmountDTO } from "./price-set-money-amount";
|
||||
import { RuleTypeDTO } from "./rule-type";
|
||||
import { BaseFilterable } from "../../dal"
|
||||
import {
|
||||
CreateMoneyAmountDTO,
|
||||
MoneyAmountDTO,
|
||||
UpdateMoneyAmountDTO,
|
||||
} from "./money-amount"
|
||||
import { PriceSetMoneyAmountDTO } from "./price-set-money-amount"
|
||||
import { RuleTypeDTO } from "./rule-type"
|
||||
|
||||
/**
|
||||
* @enum
|
||||
@@ -106,7 +109,18 @@ export interface PriceListDTO {
|
||||
*
|
||||
* The prices associated with a price list.
|
||||
*/
|
||||
export interface PriceListPriceDTO extends CreateMoneyAmountDTO {
|
||||
export interface CreatePriceListPriceDTO extends CreateMoneyAmountDTO {
|
||||
/**
|
||||
* The ID of the associated price set.
|
||||
*/
|
||||
price_set_id: string
|
||||
/**
|
||||
* The rules to add to the price. The object's keys are rule types' `rule_attribute` attribute, and values are the value of that rule associated with this price.
|
||||
*/
|
||||
rules?: CreatePriceSetPriceRules
|
||||
}
|
||||
|
||||
export interface UpdatePriceListPriceDTO extends UpdateMoneyAmountDTO {
|
||||
/**
|
||||
* The ID of the associated price set.
|
||||
*/
|
||||
@@ -152,11 +166,11 @@ export interface CreatePriceListDTO {
|
||||
/**
|
||||
* The price list is enabled starting from this date.
|
||||
*/
|
||||
starts_at?: Date | string | null
|
||||
starts_at?: string | null
|
||||
/**
|
||||
* The price list expires after this date.
|
||||
*/
|
||||
ends_at?: Date | string | null
|
||||
ends_at?: string | null
|
||||
/**
|
||||
* The price list's status.
|
||||
*/
|
||||
@@ -176,7 +190,7 @@ export interface CreatePriceListDTO {
|
||||
/**
|
||||
* The prices associated with the price list.
|
||||
*/
|
||||
prices?: PriceListPriceDTO[]
|
||||
prices?: CreatePriceListPriceDTO[]
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -200,11 +214,11 @@ export interface UpdatePriceListDTO {
|
||||
/**
|
||||
* The price list is enabled starting from this date.
|
||||
*/
|
||||
starts_at?: Date | string | null
|
||||
starts_at?: string | null
|
||||
/**
|
||||
* The price list expires after this date.
|
||||
*/
|
||||
ends_at?: Date | string | null
|
||||
ends_at?: string | null
|
||||
/**
|
||||
* The price list's status.
|
||||
*/
|
||||
@@ -412,11 +426,27 @@ export interface AddPriceListPricesDTO {
|
||||
/**
|
||||
* The ID of the price list to add prices to.
|
||||
*/
|
||||
priceListId: string
|
||||
price_list_id: string
|
||||
/**
|
||||
* The prices to add.
|
||||
*/
|
||||
prices: PriceListPriceDTO[]
|
||||
prices: CreatePriceListPriceDTO[]
|
||||
}
|
||||
|
||||
/**
|
||||
* @interface
|
||||
*
|
||||
* The prices to be added to a price list.
|
||||
*/
|
||||
export interface UpdatePriceListPricesDTO {
|
||||
/**
|
||||
* The ID of the price list to add prices to.
|
||||
*/
|
||||
price_list_id: string
|
||||
/**
|
||||
* The prices to add.
|
||||
*/
|
||||
prices: UpdatePriceListPriceDTO[]
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -428,7 +458,7 @@ export interface SetPriceListRulesDTO {
|
||||
/**
|
||||
* The ID of the price list to add rules to.
|
||||
*/
|
||||
priceListId: string
|
||||
price_list_id: string
|
||||
/**
|
||||
* The rules to add to the price list. Each key of the object is a rule type's `rule_attribute`, and its value
|
||||
* is the value(s) of the rule.
|
||||
@@ -445,7 +475,7 @@ export interface RemovePriceListRulesDTO {
|
||||
/**
|
||||
* The ID of the price list to remove rules from.
|
||||
*/
|
||||
priceListId: string
|
||||
price_list_id: string
|
||||
/**
|
||||
* The rules to remove from the price list. Each item being a rule type's `rule_attribute`.
|
||||
*/
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { BaseFilterable } from "../../dal"
|
||||
import { PriceSetDTO } from "./price-set"
|
||||
import { PriceSetMoneyAmountDTO } from "./price-set-money-amount"
|
||||
import { RuleTypeDTO } from "./rule-type"
|
||||
|
||||
/**
|
||||
@@ -73,18 +72,10 @@ export interface CreatePriceRuleDTO {
|
||||
* The ID of the associated price set.
|
||||
*/
|
||||
price_set_id?: string
|
||||
/**
|
||||
* The ID or object of the associated price set.
|
||||
*/
|
||||
price_set?: string | PriceSetDTO
|
||||
/**
|
||||
* The ID of the associated rule type.
|
||||
*/
|
||||
rule_type_id?: string
|
||||
/**
|
||||
* The ID of the associated rule type.
|
||||
*/
|
||||
rule_type?: string | RuleTypeDTO
|
||||
/**
|
||||
* The value of the price rule.
|
||||
*/
|
||||
@@ -97,10 +88,6 @@ export interface CreatePriceRuleDTO {
|
||||
* The ID of the associated price set money amount.
|
||||
*/
|
||||
price_set_money_amount_id?: string
|
||||
/**
|
||||
* The ID or object of the associated price set money amount.
|
||||
*/
|
||||
price_set_money_amount?: string | PriceSetMoneyAmountDTO
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { BaseFilterable } from "../../dal";
|
||||
import { MoneyAmountDTO } from "./money-amount";
|
||||
import { PriceListDTO } from "./price-list";
|
||||
import { PriceRuleDTO } from "./price-rule";
|
||||
import { PriceSetDTO } from "./price-set";
|
||||
import { BaseFilterable } from "../../dal"
|
||||
import { MoneyAmountDTO } from "./money-amount"
|
||||
import { PriceListDTO } from "./price-list"
|
||||
import { PriceRuleDTO } from "./price-rule"
|
||||
import { PriceSetDTO } from "./price-set"
|
||||
|
||||
/**
|
||||
* @interface
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
export * from "./common"
|
||||
export * from "./service"
|
||||
export * from "./workflows"
|
||||
|
||||
@@ -33,6 +33,7 @@ import {
|
||||
SetPriceListRulesDTO,
|
||||
UpdateMoneyAmountDTO,
|
||||
UpdatePriceListDTO,
|
||||
UpdatePriceListPricesDTO,
|
||||
UpdatePriceListRuleDTO,
|
||||
UpdatePriceRuleDTO,
|
||||
UpdatePriceSetDTO,
|
||||
@@ -3397,6 +3398,11 @@ export interface IPricingModuleService extends IModuleService {
|
||||
sharedContext?: Context
|
||||
): Promise<PriceListDTO[]>
|
||||
|
||||
updatePriceListPrices(
|
||||
data: UpdatePriceListPricesDTO[],
|
||||
sharedContext?: Context
|
||||
): Promise<PriceListDTO[]>
|
||||
|
||||
/**
|
||||
* This method is used to set the rules of a price list.
|
||||
*
|
||||
@@ -3454,4 +3460,6 @@ export interface IPricingModuleService extends IModuleService {
|
||||
data: RemovePriceListRulesDTO,
|
||||
sharedContext?: Context
|
||||
): Promise<PriceListDTO>
|
||||
|
||||
removePrices(ids: string[], sharedContext?: Context): Promise<void>
|
||||
}
|
||||
|
||||
41
packages/types/src/pricing/workflows.ts
Normal file
41
packages/types/src/pricing/workflows.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { PriceListStatus } from "./common"
|
||||
|
||||
export interface CreatePriceListPriceWorkflowDTO {
|
||||
amount: number
|
||||
currency_code: string
|
||||
variant_id: string
|
||||
max_quantity?: number
|
||||
min_quantity?: number
|
||||
rules?: Record<string, string>
|
||||
}
|
||||
|
||||
export interface UpdatePriceListPriceWorkflowDTO {
|
||||
id: string
|
||||
amount?: number
|
||||
currency_code?: string
|
||||
variant_id?: string
|
||||
max_quantity?: number
|
||||
min_quantity?: number
|
||||
rules?: Record<string, string>
|
||||
}
|
||||
|
||||
export interface CreatePriceListWorkflowInputDTO {
|
||||
title: string
|
||||
description: string
|
||||
starts_at?: string | null
|
||||
ends_at?: string | null
|
||||
status?: PriceListStatus
|
||||
rules?: Record<string, string[]>
|
||||
prices?: CreatePriceListPriceWorkflowDTO[]
|
||||
}
|
||||
|
||||
export interface UpdatePriceListWorkflowInputDTO {
|
||||
id: string
|
||||
title?: string
|
||||
description?: string
|
||||
starts_at?: string | null
|
||||
ends_at?: string | null
|
||||
status?: PriceListStatus
|
||||
rules?: Record<string, string[]>
|
||||
prices?: (UpdatePriceListPriceWorkflowDTO | CreatePriceListPriceWorkflowDTO)[]
|
||||
}
|
||||
@@ -3,6 +3,9 @@ import { isObject } from "./is-object"
|
||||
|
||||
export function getSelectsAndRelationsFromObjectArray(
|
||||
dataArray: object[],
|
||||
options: { objectFields: string[] } = {
|
||||
objectFields: [],
|
||||
},
|
||||
prefix?: string
|
||||
): {
|
||||
selects: string[]
|
||||
@@ -13,10 +16,11 @@ export function getSelectsAndRelationsFromObjectArray(
|
||||
|
||||
for (const data of dataArray) {
|
||||
for (const [key, value] of Object.entries(data)) {
|
||||
if (isObject(value)) {
|
||||
if (isObject(value) && !options.objectFields.includes(key)) {
|
||||
relations.push(setKey(key, prefix))
|
||||
const res = getSelectsAndRelationsFromObjectArray(
|
||||
[value],
|
||||
options,
|
||||
setKey(key, prefix)
|
||||
)
|
||||
selects.push(...res.selects)
|
||||
@@ -25,6 +29,7 @@ export function getSelectsAndRelationsFromObjectArray(
|
||||
relations.push(setKey(key, prefix))
|
||||
const res = getSelectsAndRelationsFromObjectArray(
|
||||
value,
|
||||
options,
|
||||
setKey(key, prefix)
|
||||
)
|
||||
selects.push(...res.selects)
|
||||
|
||||
70
packages/utils/src/pricing/builders.ts
Normal file
70
packages/utils/src/pricing/builders.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
import {
|
||||
PriceListRuleDTO,
|
||||
PriceRuleDTO,
|
||||
PriceSetMoneyAmountDTO,
|
||||
ProductVariantDTO,
|
||||
UpdatePriceListPriceDTO,
|
||||
} from "@medusajs/types"
|
||||
|
||||
export function buildPriceListRules(
|
||||
priceListRules: PriceListRuleDTO[]
|
||||
): Record<string, string[]> {
|
||||
return priceListRules.reduce((acc, curr) => {
|
||||
const ruleAttribute = curr.rule_type.rule_attribute
|
||||
const ruleValues = curr.price_list_rule_values || []
|
||||
|
||||
acc[ruleAttribute] = ruleValues.map((ruleValue) => ruleValue.value)
|
||||
|
||||
return acc
|
||||
}, {})
|
||||
}
|
||||
|
||||
export function buildPriceSetRules(
|
||||
priceRules: PriceRuleDTO[]
|
||||
): Record<string, string> {
|
||||
return priceRules.reduce((acc, curr) => {
|
||||
const ruleAttribute = curr.rule_type.rule_attribute
|
||||
const ruleValue = curr.value
|
||||
|
||||
acc[ruleAttribute] = ruleValue
|
||||
|
||||
return acc
|
||||
}, {})
|
||||
}
|
||||
|
||||
export function buildPriceSetPricesForCore(
|
||||
priceSetMoneyAmounts: (PriceSetMoneyAmountDTO & {
|
||||
price_set: PriceSetMoneyAmountDTO["price_set"] & {
|
||||
variant?: ProductVariantDTO
|
||||
}
|
||||
})[]
|
||||
): Record<string, any>[] {
|
||||
return priceSetMoneyAmounts.map((priceSetMoneyAmount) => {
|
||||
const productVariant = (priceSetMoneyAmount.price_set as any).variant
|
||||
const rules: Record<string, string> = priceSetMoneyAmount.price_rules
|
||||
? buildPriceSetRules(priceSetMoneyAmount.price_rules)
|
||||
: {}
|
||||
|
||||
return {
|
||||
...priceSetMoneyAmount.money_amount,
|
||||
variant_id: productVariant?.id ?? null,
|
||||
rules,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function buildPriceSetPricesForModule(
|
||||
priceSetMoneyAmounts: PriceSetMoneyAmountDTO[]
|
||||
): UpdatePriceListPriceDTO[] {
|
||||
return priceSetMoneyAmounts.map((priceSetMoneyAmount) => {
|
||||
const rules: Record<string, string> = priceSetMoneyAmount.price_rules
|
||||
? buildPriceSetRules(priceSetMoneyAmount.price_rules)
|
||||
: {}
|
||||
|
||||
return {
|
||||
...priceSetMoneyAmount.money_amount!,
|
||||
price_set_id: priceSetMoneyAmount.price_set!?.id!,
|
||||
rules,
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1,2 +1,3 @@
|
||||
export * from "./builders"
|
||||
export * from "./price-list"
|
||||
export * from "./rule-type"
|
||||
|
||||
@@ -2,9 +2,9 @@ import {
|
||||
TransactionStepsDefinition,
|
||||
WorkflowManager,
|
||||
} from "@medusajs/orchestration"
|
||||
import { isString, OrchestrationUtils } from "@medusajs/utils"
|
||||
import { OrchestrationUtils, isString } from "@medusajs/utils"
|
||||
import { ulid } from "ulid"
|
||||
import { resolveValue, StepResponse } from "./helpers"
|
||||
import { StepResponse, resolveValue } from "./helpers"
|
||||
import { proxify } from "./helpers/proxy"
|
||||
import {
|
||||
CreateWorkflowComposerContext,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { OrchestrationUtils } from "@medusajs/utils"
|
||||
import { PermanentStepFailureError } from "@medusajs/orchestration"
|
||||
import { OrchestrationUtils } from "@medusajs/utils"
|
||||
|
||||
/**
|
||||
* This class is used to create the response returned by a step. A step return its data by returning an instance of `StepResponse`.
|
||||
@@ -40,7 +40,7 @@ export class StepResponse<TOutput, TCompensateInput = TOutput> {
|
||||
* Creates a StepResponse that indicates that the step has failed and the retry mechanism should not kick in anymore.
|
||||
*
|
||||
* @param message - An optional message to be logged.
|
||||
*
|
||||
*
|
||||
* @example
|
||||
* import { Product } from "@medusajs/medusa"
|
||||
* import {
|
||||
@@ -48,11 +48,11 @@ export class StepResponse<TOutput, TCompensateInput = TOutput> {
|
||||
* StepResponse,
|
||||
* createWorkflow
|
||||
* } from "@medusajs/workflows-sdk"
|
||||
*
|
||||
*
|
||||
* interface CreateProductInput {
|
||||
* title: string
|
||||
* }
|
||||
*
|
||||
*
|
||||
* export const createProductStep = createStep(
|
||||
* "createProductStep",
|
||||
* async function (
|
||||
@@ -62,7 +62,7 @@ export class StepResponse<TOutput, TCompensateInput = TOutput> {
|
||||
* const productService = context.container.resolve(
|
||||
* "productService"
|
||||
* )
|
||||
*
|
||||
*
|
||||
* try {
|
||||
* const product = await productService.create(input)
|
||||
* return new StepResponse({
|
||||
@@ -75,22 +75,22 @@ export class StepResponse<TOutput, TCompensateInput = TOutput> {
|
||||
* }
|
||||
* }
|
||||
* )
|
||||
*
|
||||
*
|
||||
* interface WorkflowInput {
|
||||
* title: string
|
||||
* }
|
||||
*
|
||||
*
|
||||
* const myWorkflow = createWorkflow<
|
||||
* WorkflowInput,
|
||||
* Product
|
||||
* >("my-workflow", (input) => {
|
||||
* // Everything here will be executed and resolved later
|
||||
* // during the execution. Including the data access.
|
||||
*
|
||||
*
|
||||
* const product = createProductStep(input)
|
||||
* }
|
||||
* )
|
||||
*
|
||||
*
|
||||
* myWorkflow()
|
||||
* .run({
|
||||
* input: {
|
||||
|
||||
Reference in New Issue
Block a user