feat: Add support for shipping options prices update (#7028)
This commit is contained in:
1 parent
51acd1da5b
commit
c78915c7c5
13 files changed
+450
-77
No files matched your search
@@ -0,0 +1,8 @@
|
|||||||
|
---
|
||||||
|
"@medusajs/medusa": patch
|
||||||
|
"@medusajs/core-flows": patch
|
||||||
|
"@medusajs/pricing": patch
|
||||||
|
"@medusajs/types": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
feat(): Add support for shipping options prices update
|
||||||
@@ -856,8 +856,14 @@ medusaIntegrationTestRunner({
|
|||||||
expect(response.data.values.length).toEqual(2)
|
expect(response.data.values.length).toEqual(2)
|
||||||
expect(response.data.values).toEqual(
|
expect(response.data.values).toEqual(
|
||||||
expect.arrayContaining([
|
expect.arrayContaining([
|
||||||
{ label: "Afghanistan", value: "af" },
|
{
|
||||||
{ label: "Albania", value: "al" },
|
label: "Andorra",
|
||||||
|
value: "ad",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "United Arab Emirates",
|
||||||
|
value: "ae",
|
||||||
|
},
|
||||||
])
|
])
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
+39
-8
@@ -264,7 +264,7 @@ medusaIntegrationTestRunner({
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should create a shipping option successfully", async () => {
|
it("should update a shipping option successfully", async () => {
|
||||||
const shippingOptionPayload = {
|
const shippingOptionPayload = {
|
||||||
name: "Test shipping option",
|
name: "Test shipping option",
|
||||||
service_zone_id: fulfillmentSet.service_zones[0].id,
|
service_zone_id: fulfillmentSet.service_zones[0].id,
|
||||||
@@ -295,15 +295,44 @@ medusaIntegrationTestRunner({
|
|||||||
adminHeaders
|
adminHeaders
|
||||||
)
|
)
|
||||||
|
|
||||||
expect(response.status).toEqual(200)
|
const shippingOptionId = response.data.shipping_option.id
|
||||||
expect(response.data.shipping_option).toEqual(
|
|
||||||
|
const eurPrice = response.data.shipping_option.prices.find(
|
||||||
|
(p) => p.currency_code === "eur"
|
||||||
|
)
|
||||||
|
const updateShippingOptionPayload = {
|
||||||
|
id: shippingOptionId,
|
||||||
|
name: "Updated shipping option",
|
||||||
|
provider_id: "manual_test-provider",
|
||||||
|
price_type: "flat",
|
||||||
|
prices: [
|
||||||
|
{
|
||||||
|
currency_code: "dkk",
|
||||||
|
amount: 10,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: eurPrice.id,
|
||||||
|
amount: 10000,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateResponse = await api.post(
|
||||||
|
`/admin/shipping-options/${shippingOptionId}`,
|
||||||
|
updateShippingOptionPayload,
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(updateResponse.status).toEqual(200)
|
||||||
|
expect(updateResponse.data.shipping_option.prices).toHaveLength(2)
|
||||||
|
expect(updateResponse.data.shipping_option).toEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
id: expect.any(String),
|
id: expect.any(String),
|
||||||
name: shippingOptionPayload.name,
|
name: updateShippingOptionPayload.name,
|
||||||
provider: expect.objectContaining({
|
provider: expect.objectContaining({
|
||||||
id: shippingOptionPayload.provider_id,
|
id: shippingOptionPayload.provider_id,
|
||||||
}),
|
}),
|
||||||
price_type: shippingOptionPayload.price_type,
|
price_type: updateShippingOptionPayload.price_type,
|
||||||
type: expect.objectContaining({
|
type: expect.objectContaining({
|
||||||
id: expect.any(String),
|
id: expect.any(String),
|
||||||
label: shippingOptionPayload.type.label,
|
label: shippingOptionPayload.type.label,
|
||||||
@@ -315,13 +344,15 @@ medusaIntegrationTestRunner({
|
|||||||
prices: expect.arrayContaining([
|
prices: expect.arrayContaining([
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
id: expect.any(String),
|
id: expect.any(String),
|
||||||
currency_code: "usd",
|
currency_code: "dkk",
|
||||||
amount: 1000,
|
rules_count: 0,
|
||||||
|
amount: 10,
|
||||||
}),
|
}),
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
id: expect.any(String),
|
id: expect.any(String),
|
||||||
currency_code: "eur",
|
currency_code: "eur",
|
||||||
amount: 1000,
|
rules_count: 1,
|
||||||
|
amount: 10000,
|
||||||
}),
|
}),
|
||||||
]),
|
]),
|
||||||
rules: expect.arrayContaining([
|
rules: expect.arrayContaining([
|
||||||
|
|||||||
+55
-21
@@ -97,6 +97,10 @@ medusaIntegrationTestRunner({
|
|||||||
region_id: region.id,
|
region_id: region.id,
|
||||||
amount: 100,
|
amount: 100,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
currency_code: "dkk",
|
||||||
|
amount: 1000,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
@@ -111,26 +115,11 @@ medusaIntegrationTestRunner({
|
|||||||
input: [shippingOptionData],
|
input: [shippingOptionData],
|
||||||
})
|
})
|
||||||
|
|
||||||
const updateData: UpdateShippingOptionsWorkflowInput = {
|
|
||||||
id: result[0].id,
|
|
||||||
name: "Test shipping option",
|
|
||||||
price_type: "flat",
|
|
||||||
type: {
|
|
||||||
code: "manual-type",
|
|
||||||
label: "Manual Type",
|
|
||||||
description: "Manual Type Description",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
await updateShippingOptionsWorkflow(container).run({
|
|
||||||
input: [updateData],
|
|
||||||
})
|
|
||||||
|
|
||||||
const remoteQuery = container.resolve(
|
const remoteQuery = container.resolve(
|
||||||
ContainerRegistrationKeys.REMOTE_QUERY
|
ContainerRegistrationKeys.REMOTE_QUERY
|
||||||
)
|
)
|
||||||
|
|
||||||
const remoteQueryObject = remoteQueryObjectFromString({
|
let remoteQueryObject = remoteQueryObjectFromString({
|
||||||
entryPoint: "shipping_option",
|
entryPoint: "shipping_option",
|
||||||
variables: {
|
variables: {
|
||||||
id: result[0].id,
|
id: result[0].id,
|
||||||
@@ -155,10 +144,50 @@ medusaIntegrationTestRunner({
|
|||||||
|
|
||||||
const [createdShippingOption] = await remoteQuery(remoteQueryObject)
|
const [createdShippingOption] = await remoteQuery(remoteQueryObject)
|
||||||
|
|
||||||
const prices = createdShippingOption.prices
|
const usdPrice = createdShippingOption.prices.find((price) => {
|
||||||
delete createdShippingOption.prices
|
return price.currency_code === "usd"
|
||||||
|
})
|
||||||
|
|
||||||
expect(createdShippingOption).toEqual(
|
const dkkPrice = createdShippingOption.prices.find((price) => {
|
||||||
|
return price.currency_code === "dkk"
|
||||||
|
})
|
||||||
|
|
||||||
|
const updateData: UpdateShippingOptionsWorkflowInput = {
|
||||||
|
id: createdShippingOption.id,
|
||||||
|
name: "Test shipping option",
|
||||||
|
price_type: "flat",
|
||||||
|
type: {
|
||||||
|
code: "manual-type",
|
||||||
|
label: "Manual Type",
|
||||||
|
description: "Manual Type Description",
|
||||||
|
},
|
||||||
|
prices: [
|
||||||
|
// We keep the usd price as is
|
||||||
|
// update the dkk price to 100
|
||||||
|
// delete the third price eur
|
||||||
|
// create a new eur one instead
|
||||||
|
usdPrice,
|
||||||
|
{
|
||||||
|
...dkkPrice,
|
||||||
|
amount: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
region_id: region.id,
|
||||||
|
amount: 1000,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
await updateShippingOptionsWorkflow(container).run({
|
||||||
|
input: [updateData],
|
||||||
|
})
|
||||||
|
|
||||||
|
const [updatedShippingOption] = await remoteQuery(remoteQueryObject)
|
||||||
|
|
||||||
|
const prices = updatedShippingOption.prices
|
||||||
|
delete updatedShippingOption.prices
|
||||||
|
|
||||||
|
expect(updatedShippingOption).toEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
id: result[0].id,
|
id: result[0].id,
|
||||||
name: updateData.name,
|
name: updateData.name,
|
||||||
@@ -178,7 +207,7 @@ medusaIntegrationTestRunner({
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
expect(prices).toHaveLength(2)
|
expect(prices).toHaveLength(3)
|
||||||
expect(prices).toContainEqual(
|
expect(prices).toContainEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
currency_code: "usd",
|
currency_code: "usd",
|
||||||
@@ -188,8 +217,13 @@ medusaIntegrationTestRunner({
|
|||||||
expect(prices).toContainEqual(
|
expect(prices).toContainEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
currency_code: "eur",
|
currency_code: "eur",
|
||||||
|
amount: 1000,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
expect(prices).toContainEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
currency_code: "dkk",
|
||||||
amount: 100,
|
amount: 100,
|
||||||
rules_count: 1,
|
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -7,3 +7,4 @@ export * from "./delete-service-zones"
|
|||||||
export * from "./delete-shipping-options"
|
export * from "./delete-shipping-options"
|
||||||
export * from "./create-shipping-profiles"
|
export * from "./create-shipping-profiles"
|
||||||
export * from "./remove-rules-from-fulfillment-shipping-option"
|
export * from "./remove-rules-from-fulfillment-shipping-option"
|
||||||
|
export * from "./set-shipping-options-prices"
|
||||||
@@ -0,0 +1,193 @@
|
|||||||
|
import {
|
||||||
|
CreatePriceDTO,
|
||||||
|
CreatePricesDTO,
|
||||||
|
FulfillmentWorkflow,
|
||||||
|
IPricingModuleService,
|
||||||
|
IRegionModuleService,
|
||||||
|
PriceDTO,
|
||||||
|
PriceSetDTO,
|
||||||
|
RemoteQueryFunction,
|
||||||
|
} from "@medusajs/types"
|
||||||
|
import { createStep, StepResponse } from "@medusajs/workflows-sdk"
|
||||||
|
import {
|
||||||
|
ContainerRegistrationKeys,
|
||||||
|
isDefined,
|
||||||
|
LINKS,
|
||||||
|
remoteQueryObjectFromString,
|
||||||
|
} from "@medusajs/utils"
|
||||||
|
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||||
|
|
||||||
|
interface PriceRegionId {
|
||||||
|
region_id: string
|
||||||
|
amount: number
|
||||||
|
}
|
||||||
|
|
||||||
|
type SetShippingOptionsPricesStepInput = {
|
||||||
|
id: string
|
||||||
|
prices?: FulfillmentWorkflow.UpdateShippingOptionsWorkflowInput["prices"]
|
||||||
|
}[]
|
||||||
|
|
||||||
|
async function getCurrentShippingOptionPrices(
|
||||||
|
shippingOptionIds: string[],
|
||||||
|
{ remoteQuery }: { remoteQuery: RemoteQueryFunction }
|
||||||
|
): Promise<
|
||||||
|
{ shipping_option_id: string; price_set_id: string; prices: PriceDTO[] }[]
|
||||||
|
> {
|
||||||
|
const query = remoteQueryObjectFromString({
|
||||||
|
service: LINKS.ShippingOptionPriceSet,
|
||||||
|
variables: {
|
||||||
|
filters: { shipping_option_id: shippingOptionIds },
|
||||||
|
take: null,
|
||||||
|
},
|
||||||
|
fields: ["shipping_option_id", "price_set_id", "price_set.prices.*"],
|
||||||
|
})
|
||||||
|
|
||||||
|
const shippingOptionPrices = (await remoteQuery(query)) as {
|
||||||
|
shipping_option_id: string
|
||||||
|
price_set_id: string
|
||||||
|
price_set: PriceSetDTO
|
||||||
|
}[]
|
||||||
|
|
||||||
|
return shippingOptionPrices.map((shippingOption) => {
|
||||||
|
const prices = shippingOption.price_set?.prices ?? []
|
||||||
|
const price_set_id = shippingOption.price_set_id
|
||||||
|
return {
|
||||||
|
shipping_option_id: shippingOption.shipping_option_id,
|
||||||
|
price_set_id,
|
||||||
|
prices,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildPrices(
|
||||||
|
prices: SetShippingOptionsPricesStepInput[0]["prices"],
|
||||||
|
regionToCurrencyMap: Map<string, string>
|
||||||
|
): CreatePriceDTO[] {
|
||||||
|
if (!prices) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
const shippingOptionPrices = prices.map((price) => {
|
||||||
|
if ("region_id" in price) {
|
||||||
|
const currency_code = regionToCurrencyMap.get(price.region_id!)!
|
||||||
|
const regionId = price.region_id
|
||||||
|
delete price.region_id
|
||||||
|
return {
|
||||||
|
...price,
|
||||||
|
currency_code: currency_code,
|
||||||
|
amount: price.amount,
|
||||||
|
rules: {
|
||||||
|
region_id: regionId,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return price
|
||||||
|
})
|
||||||
|
|
||||||
|
return shippingOptionPrices as CreatePriceDTO[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export const setShippingOptionsPricesStepId = "set-shipping-options-prices-step"
|
||||||
|
export const setShippingOptionsPricesStep = createStep(
|
||||||
|
setShippingOptionsPricesStepId,
|
||||||
|
async (data: SetShippingOptionsPricesStepInput, { container }) => {
|
||||||
|
if (!data.length) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const regionIds = data
|
||||||
|
.map((input) => input.prices)
|
||||||
|
.flat()
|
||||||
|
.filter((price): price is PriceRegionId => "region_id" in (price ?? {}))
|
||||||
|
.map((price) => price.region_id)
|
||||||
|
|
||||||
|
let regionToCurrencyMap: Map<string, string> = new Map()
|
||||||
|
|
||||||
|
if (regionIds.length) {
|
||||||
|
const regionService = container.resolve<IRegionModuleService>(
|
||||||
|
ModuleRegistrationName.REGION
|
||||||
|
)
|
||||||
|
const regions = await regionService.list(
|
||||||
|
{
|
||||||
|
id: [...new Set(regionIds)],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
select: ["id", "currency_code"],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
regionToCurrencyMap = new Map(
|
||||||
|
regions.map((region) => [region.id, region.currency_code])
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const remoteQuery = container.resolve<RemoteQueryFunction>(
|
||||||
|
ContainerRegistrationKeys.REMOTE_QUERY
|
||||||
|
)
|
||||||
|
|
||||||
|
const currentShippingOptionPricesData =
|
||||||
|
await getCurrentShippingOptionPrices(
|
||||||
|
data.map((d) => d.id),
|
||||||
|
{ remoteQuery }
|
||||||
|
)
|
||||||
|
|
||||||
|
const shippingOptionPricesMap = new Map(
|
||||||
|
currentShippingOptionPricesData.map((currentShippingOptionDataItem) => {
|
||||||
|
const shippingOptionData = data.find(
|
||||||
|
(d) => d.id === currentShippingOptionDataItem.shipping_option_id
|
||||||
|
)!
|
||||||
|
const pricesData = shippingOptionData?.prices?.map((priceData) => {
|
||||||
|
return {
|
||||||
|
...priceData,
|
||||||
|
price_set_id: currentShippingOptionDataItem.price_set_id,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const buildPricesData =
|
||||||
|
pricesData && buildPrices(pricesData, regionToCurrencyMap)
|
||||||
|
return [
|
||||||
|
currentShippingOptionDataItem.shipping_option_id,
|
||||||
|
{
|
||||||
|
price_set_id: currentShippingOptionDataItem.price_set_id,
|
||||||
|
prices: buildPricesData,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
const pricingService = container.resolve<IPricingModuleService>(
|
||||||
|
ModuleRegistrationName.PRICING
|
||||||
|
)
|
||||||
|
|
||||||
|
for (const data_ of data) {
|
||||||
|
const shippingOptionData = shippingOptionPricesMap.get(data_.id)!
|
||||||
|
|
||||||
|
if (!isDefined(shippingOptionData.prices)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
await pricingService.update(shippingOptionData.price_set_id, {
|
||||||
|
prices: shippingOptionData.prices,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return new StepResponse(void 0, currentShippingOptionPricesData)
|
||||||
|
},
|
||||||
|
async (rollbackData, { container }) => {
|
||||||
|
if (!rollbackData?.length) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const pricingService = container.resolve<IPricingModuleService>(
|
||||||
|
ModuleRegistrationName.PRICING
|
||||||
|
)
|
||||||
|
|
||||||
|
for (const data_ of rollbackData) {
|
||||||
|
const prices = data_.prices as CreatePricesDTO[]
|
||||||
|
if (!isDefined(prices)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
await pricingService.update(data_.price_set_id, { prices })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -22,9 +22,10 @@ export const createShippingOptionsWorkflow = createWorkflow(
|
|||||||
): WorkflowData<FulfillmentWorkflow.CreateShippingOptionsWorkflowOutput> => {
|
): WorkflowData<FulfillmentWorkflow.CreateShippingOptionsWorkflowOutput> => {
|
||||||
const data = transform(input, (data) => {
|
const data = transform(input, (data) => {
|
||||||
const shippingOptionsIndexToPrices = data.map((option, index) => {
|
const shippingOptionsIndexToPrices = data.map((option, index) => {
|
||||||
|
const prices = option.prices
|
||||||
return {
|
return {
|
||||||
shipping_option_index: index,
|
shipping_option_index: index,
|
||||||
prices: option.prices,
|
prices,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,18 @@
|
|||||||
import { FulfillmentWorkflow } from "@medusajs/types"
|
import {
|
||||||
|
CreateRuleTypeDTO,
|
||||||
|
FulfillmentWorkflow,
|
||||||
|
RuleTypeDTO,
|
||||||
|
} from "@medusajs/types"
|
||||||
import {
|
import {
|
||||||
createWorkflow,
|
createWorkflow,
|
||||||
transform,
|
transform,
|
||||||
WorkflowData,
|
WorkflowData,
|
||||||
} from "@medusajs/workflows-sdk"
|
} from "@medusajs/workflows-sdk"
|
||||||
import { upsertShippingOptionsStep } from "../steps"
|
import {
|
||||||
|
setShippingOptionsPricesStep,
|
||||||
|
upsertShippingOptionsStep,
|
||||||
|
} from "../steps"
|
||||||
|
import { createPricingRuleTypesStep } from "../../pricing"
|
||||||
|
|
||||||
export const updateShippingOptionsWorkflowId =
|
export const updateShippingOptionsWorkflowId =
|
||||||
"update-shipping-options-workflow"
|
"update-shipping-options-workflow"
|
||||||
@@ -17,9 +25,11 @@ export const updateShippingOptionsWorkflow = createWorkflow(
|
|||||||
): WorkflowData<FulfillmentWorkflow.UpdateShippingOptionsWorkflowOutput> => {
|
): WorkflowData<FulfillmentWorkflow.UpdateShippingOptionsWorkflowOutput> => {
|
||||||
const data = transform(input, (data) => {
|
const data = transform(input, (data) => {
|
||||||
const shippingOptionsIndexToPrices = data.map((option, index) => {
|
const shippingOptionsIndexToPrices = data.map((option, index) => {
|
||||||
|
const prices = option.prices
|
||||||
|
delete option.prices
|
||||||
return {
|
return {
|
||||||
shipping_option_index: index,
|
shipping_option_index: index,
|
||||||
prices: option.prices,
|
prices,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -33,7 +43,7 @@ export const updateShippingOptionsWorkflow = createWorkflow(
|
|||||||
data.shippingOptions
|
data.shippingOptions
|
||||||
)
|
)
|
||||||
|
|
||||||
/*const normalizedShippingOptionsPrices = transform(
|
const normalizedShippingOptionsPrices = transform(
|
||||||
{
|
{
|
||||||
shippingOptions: updatedShippingOptions,
|
shippingOptions: updatedShippingOptions,
|
||||||
shippingOptionsIndexToPrices: data.shippingOptionsIndexToPrices,
|
shippingOptionsIndexToPrices: data.shippingOptionsIndexToPrices,
|
||||||
@@ -60,32 +70,16 @@ export const updateShippingOptionsWorkflow = createWorkflow(
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
shippingOptionsPrices,
|
shippingOptionsPrices,
|
||||||
ruleTypes: Array.from(ruleTypes) as UpdateRuleTypeDTO[],
|
ruleTypes: Array.from(ruleTypes) as CreateRuleTypeDTO[],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)*/
|
|
||||||
|
|
||||||
/*updatePricingRuleTypesStep(normalizedShippingOptionsPrices.ruleTypes)*/
|
|
||||||
|
|
||||||
/*const shippingOptionsPriceSetsLinkData = updateShippingOptionsPriceSetsStep(
|
|
||||||
normalizedShippingOptionsPrices.shippingOptionsPrices
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const normalizedLinkData = transform(
|
createPricingRuleTypesStep(normalizedShippingOptionsPrices.ruleTypes)
|
||||||
{
|
|
||||||
shippingOptionsPriceSetsLinkData,
|
|
||||||
},
|
|
||||||
(data) => {
|
|
||||||
return data.shippingOptionsPriceSetsLinkData.map((item) => {
|
|
||||||
return {
|
|
||||||
id: item.id,
|
|
||||||
price_sets: [item.priceSetId],
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
)*/
|
|
||||||
|
|
||||||
/*setShippingOptionsPriceSetsStep(normalizedLinkData)*/
|
setShippingOptionsPricesStep(
|
||||||
|
normalizedShippingOptionsPrices.shippingOptionsPrices
|
||||||
|
)
|
||||||
|
|
||||||
return updatedShippingOptions
|
return updatedShippingOptions
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,6 +70,23 @@ export const AdminCreateShippingOptionPriceWithRegion = z
|
|||||||
})
|
})
|
||||||
.strict()
|
.strict()
|
||||||
|
|
||||||
|
export const AdminUpdateShippingOptionPriceWithCurrency =z
|
||||||
|
.object({
|
||||||
|
id: z.string().optional(),
|
||||||
|
currency_code: z.string().optional(),
|
||||||
|
amount: z.number().optional(),
|
||||||
|
})
|
||||||
|
.strict()
|
||||||
|
|
||||||
|
export const AdminUpdateShippingOptionPriceWithRegion =
|
||||||
|
z
|
||||||
|
.object({
|
||||||
|
id: z.string().optional(),
|
||||||
|
region_id: z.string().optional(),
|
||||||
|
amount: z.number().optional(),
|
||||||
|
})
|
||||||
|
.strict()
|
||||||
|
|
||||||
export const AdminCreateShippingOption = z
|
export const AdminCreateShippingOption = z
|
||||||
.object({
|
.object({
|
||||||
name: z.string(),
|
name: z.string(),
|
||||||
@@ -98,6 +115,11 @@ export const AdminUpdateShippingOption = z
|
|||||||
price_type: z.nativeEnum(ShippingOptionPriceTypeEnum).optional(),
|
price_type: z.nativeEnum(ShippingOptionPriceTypeEnum).optional(),
|
||||||
provider_id: z.string().optional(),
|
provider_id: z.string().optional(),
|
||||||
type: AdminCreateShippingOptionTypeObject.optional(),
|
type: AdminCreateShippingOptionTypeObject.optional(),
|
||||||
|
prices: AdminUpdateShippingOptionPriceWithCurrency.or(
|
||||||
|
AdminUpdateShippingOptionPriceWithRegion
|
||||||
|
)
|
||||||
|
.array()
|
||||||
|
.optional(),
|
||||||
})
|
})
|
||||||
.strict()
|
.strict()
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,14 @@
|
|||||||
import { getSetDifference, stringToSelectRelationObject } from "@medusajs/utils"
|
import {
|
||||||
|
getSetDifference,
|
||||||
|
isPresent,
|
||||||
|
stringToSelectRelationObject,
|
||||||
|
} from "@medusajs/utils"
|
||||||
import { pick } from "lodash"
|
import { pick } from "lodash"
|
||||||
import { MedusaError, isDefined } from "medusa-core-utils"
|
import { isDefined, MedusaError } from "medusa-core-utils"
|
||||||
import { BaseEntity } from "../interfaces"
|
import { BaseEntity } from "../interfaces"
|
||||||
import { FindConfig, QueryConfig, RequestQueryFields } from "../types/common"
|
import { FindConfig, QueryConfig, RequestQueryFields } from "../types/common"
|
||||||
|
import { featureFlagRouter } from "../loaders/feature-flags"
|
||||||
|
import MedusaV2 from "../loaders/feature-flags/medusa-v2"
|
||||||
|
|
||||||
export function pickByConfig<TModel extends BaseEntity>(
|
export function pickByConfig<TModel extends BaseEntity>(
|
||||||
obj: TModel | TModel[],
|
obj: TModel | TModel[],
|
||||||
@@ -24,7 +30,7 @@ export function prepareListQuery<
|
|||||||
T extends RequestQueryFields,
|
T extends RequestQueryFields,
|
||||||
TEntity extends BaseEntity
|
TEntity extends BaseEntity
|
||||||
>(validated: T, queryConfig: QueryConfig<TEntity> = {}) {
|
>(validated: T, queryConfig: QueryConfig<TEntity> = {}) {
|
||||||
const isMedusaV2 = process.env.MEDUSA_FF_MEDUSA_V2 == "true"
|
const isMedusaV2 = featureFlagRouter.isFeatureEnabled(MedusaV2.key)
|
||||||
|
|
||||||
// TODO: this function will be simplified a lot once we drop support for the old api
|
// TODO: this function will be simplified a lot once we drop support for the old api
|
||||||
const { order, fields, limit = 50, expand, offset = 0 } = validated
|
const { order, fields, limit = 50, expand, offset = 0 } = validated
|
||||||
@@ -189,13 +195,14 @@ export function prepareListQuery<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const finalOrder = isPresent(orderBy) ? orderBy : undefined
|
||||||
return {
|
return {
|
||||||
listConfig: {
|
listConfig: {
|
||||||
select: select.length ? select : undefined,
|
select: select.length ? select : undefined,
|
||||||
relations: Array.from(allRelations),
|
relations: Array.from(allRelations),
|
||||||
skip: offset,
|
skip: offset,
|
||||||
take: limit ?? defaultLimit,
|
take: limit ?? defaultLimit,
|
||||||
order: orderBy,
|
order: finalOrder,
|
||||||
},
|
},
|
||||||
remoteQueryConfig: {
|
remoteQueryConfig: {
|
||||||
// Add starFields that are relations only on which we want all properties with a dedicated format to the remote query
|
// Add starFields that are relations only on which we want all properties with a dedicated format to the remote query
|
||||||
@@ -207,7 +214,7 @@ export function prepareListQuery<
|
|||||||
? {
|
? {
|
||||||
skip: offset,
|
skip: offset,
|
||||||
take: limit ?? defaultLimit,
|
take: limit ?? defaultLimit,
|
||||||
order: orderBy,
|
order: finalOrder,
|
||||||
}
|
}
|
||||||
: {},
|
: {},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ import {
|
|||||||
AddPricesDTO,
|
AddPricesDTO,
|
||||||
Context,
|
Context,
|
||||||
CreatePriceListRuleDTO,
|
CreatePriceListRuleDTO,
|
||||||
CreatePriceSetDTO,
|
|
||||||
CreatePricesDTO,
|
CreatePricesDTO,
|
||||||
|
CreatePriceSetDTO,
|
||||||
DAL,
|
DAL,
|
||||||
InternalModuleDeclaration,
|
InternalModuleDeclaration,
|
||||||
ModuleJoinerConfig,
|
ModuleJoinerConfig,
|
||||||
@@ -23,7 +23,7 @@ import {
|
|||||||
groupBy,
|
groupBy,
|
||||||
InjectManager,
|
InjectManager,
|
||||||
InjectTransactionManager,
|
InjectTransactionManager,
|
||||||
isDefined,
|
isDefined, isPresent,
|
||||||
isString,
|
isString,
|
||||||
MedusaContext,
|
MedusaContext,
|
||||||
MedusaError,
|
MedusaError,
|
||||||
@@ -44,12 +44,12 @@ import {
|
|||||||
RuleType,
|
RuleType,
|
||||||
} from "@models"
|
} from "@models"
|
||||||
|
|
||||||
import { PriceListService, RuleTypeService } from "@services"
|
import {PriceListService, RuleTypeService} from "@services"
|
||||||
import { validatePriceListDates } from "@utils"
|
import {validatePriceListDates} from "@utils"
|
||||||
import { entityNameToLinkableKeysMap, joinerConfig } from "../joiner-config"
|
import {entityNameToLinkableKeysMap, joinerConfig} from "../joiner-config"
|
||||||
import { PriceSetIdPrefix } from "../models/price-set"
|
import {PriceSetIdPrefix} from "../models/price-set"
|
||||||
import { PriceListIdPrefix } from "../models/price-list"
|
import {PriceListIdPrefix} from "../models/price-list"
|
||||||
import { UpdatePriceSetInput } from "src/types/services"
|
import {UpdatePriceSetInput} from "src/types/services"
|
||||||
|
|
||||||
type InjectedDependencies = {
|
type InjectedDependencies = {
|
||||||
baseRepository: DAL.RepositoryService
|
baseRepository: DAL.RepositoryService
|
||||||
@@ -336,6 +336,54 @@ export default class PricingModuleService<
|
|||||||
return isString(idOrSelector) ? priceSets[0] : priceSets
|
return isString(idOrSelector) ? priceSets[0] : priceSets
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async normalizeUpdateData(
|
||||||
|
data: UpdatePriceSetInput[],
|
||||||
|
sharedContext
|
||||||
|
) {
|
||||||
|
const ruleAttributes = data
|
||||||
|
.map((d) => d.prices?.map((p) => Object.keys(p.rules ?? [])) ?? [])
|
||||||
|
.flat(Infinity)
|
||||||
|
.filter(Boolean)
|
||||||
|
|
||||||
|
const ruleTypes = await this.ruleTypeService_.list(
|
||||||
|
{ rule_attribute: ruleAttributes },
|
||||||
|
{ take: null },
|
||||||
|
sharedContext
|
||||||
|
)
|
||||||
|
|
||||||
|
const ruleTypeMap = ruleTypes.reduce((acc, curr) => {
|
||||||
|
acc.set(curr.rule_attribute, curr)
|
||||||
|
return acc
|
||||||
|
}, new Map())
|
||||||
|
|
||||||
|
return data.map((priceSet) => {
|
||||||
|
const prices = priceSet.prices?.map((price) => {
|
||||||
|
const rules = Object.entries(price.rules ?? {}).map(
|
||||||
|
([attribute, value]) => {
|
||||||
|
return {
|
||||||
|
price_set_id: priceSet.id,
|
||||||
|
rule_type_id: ruleTypeMap.get(attribute)!.id,
|
||||||
|
value,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
const hasRulesInput = isPresent(price.rules)
|
||||||
|
delete price.rules
|
||||||
|
return {
|
||||||
|
...price,
|
||||||
|
price_set_id: priceSet.id,
|
||||||
|
price_rules: hasRulesInput ? rules : undefined,
|
||||||
|
rules_count: hasRulesInput ? rules.length : undefined
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
...priceSet,
|
||||||
|
prices,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
@InjectTransactionManager("baseRepository_")
|
@InjectTransactionManager("baseRepository_")
|
||||||
protected async update_(
|
protected async update_(
|
||||||
data: UpdatePriceSetInput[],
|
data: UpdatePriceSetInput[],
|
||||||
@@ -344,8 +392,33 @@ export default class PricingModuleService<
|
|||||||
// TODO: We are not handling rule types, rules, etc. here, add support after data models are finalized
|
// TODO: We are not handling rule types, rules, etc. here, add support after data models are finalized
|
||||||
// TODO: Since money IDs are rarely passed, this will delete all previous data and insert new entries.
|
// TODO: Since money IDs are rarely passed, this will delete all previous data and insert new entries.
|
||||||
// We can make the `insert` inside upsertWithReplace do an `upsert` instead to avoid this
|
// We can make the `insert` inside upsertWithReplace do an `upsert` instead to avoid this
|
||||||
return this.priceSetService_.upsertWithReplace(
|
const normalizedData = await this.normalizeUpdateData(data, sharedContext)
|
||||||
data,
|
|
||||||
|
const prices = normalizedData.flatMap((priceSet) => priceSet.prices || [])
|
||||||
|
const upsertedPrices = await this.priceService_.upsertWithReplace(
|
||||||
|
prices,
|
||||||
|
{
|
||||||
|
relations: ["price_rules"],
|
||||||
|
},
|
||||||
|
sharedContext
|
||||||
|
)
|
||||||
|
|
||||||
|
const priceSetsToUpsert = normalizedData.map((priceSet) => {
|
||||||
|
const { prices, ...rest } = priceSet
|
||||||
|
return {
|
||||||
|
...rest,
|
||||||
|
prices: upsertedPrices
|
||||||
|
.filter((p) => p.price_set_id === priceSet.id)
|
||||||
|
.map((price) => {
|
||||||
|
// @ts-ignore
|
||||||
|
delete price.price_rules
|
||||||
|
return price
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return await this.priceSetService_.upsertWithReplace(
|
||||||
|
priceSetsToUpsert,
|
||||||
{ relations: ["prices"] },
|
{ relations: ["prices"] },
|
||||||
sharedContext
|
sharedContext
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -16,12 +16,14 @@ export interface UpdateShippingOptionsWorkflowInput {
|
|||||||
}
|
}
|
||||||
prices?: (
|
prices?: (
|
||||||
| {
|
| {
|
||||||
currency_code: string
|
id?: string
|
||||||
amount: number
|
currency_code?: string
|
||||||
|
amount?: number
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
region_id: string
|
id?: string
|
||||||
amount: number
|
region_id?: string
|
||||||
|
amount?: number
|
||||||
}
|
}
|
||||||
)[]
|
)[]
|
||||||
rules?: {
|
rules?: {
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import {
|
|||||||
doNotForceTransaction,
|
doNotForceTransaction,
|
||||||
isDefined,
|
isDefined,
|
||||||
isObject,
|
isObject,
|
||||||
|
isPresent,
|
||||||
isString,
|
isString,
|
||||||
lowerCaseFirst,
|
lowerCaseFirst,
|
||||||
MedusaError,
|
MedusaError,
|
||||||
@@ -85,7 +86,7 @@ export function internalModuleServiceFactory<
|
|||||||
* @param config
|
* @param config
|
||||||
*/
|
*/
|
||||||
static applyDefaultOrdering(config: FindConfig<any>) {
|
static applyDefaultOrdering(config: FindConfig<any>) {
|
||||||
if (config.order) {
|
if (isPresent(config.order)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user