Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(core-flows): set SalesChannels on Product update #7272

Merged
merged 13 commits into from
May 21, 2024
5 changes: 5 additions & 0 deletions .changeset/spicy-panthers-exist.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@medusajs/core-flows": patch
---

fix(core-flows): set SalesChannels for Products on update
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { Modules } from "@medusajs/modules-sdk"
import { ContainerRegistrationKeys } from "@medusajs/utils"
import { StepResponse, createStep } from "@medusajs/workflows-sdk"

type StepInput = {
ids: string[]
}

export const getProductSalesChannelLinkStepId = "get-product-sales-channel-link"
export const getProductSalesChannelLinkStep = createStep(
fPolic marked this conversation as resolved.
Show resolved Hide resolved
getProductSalesChannelLinkStepId,
async (data: StepInput, { container }) => {
if (!data.ids.length) {
return new StepResponse([])
}

const remoteLink = container.resolve(ContainerRegistrationKeys.REMOTE_LINK)

const linkService = remoteLink.getLinkModule(
Modules.PRODUCT,
"product_id",
Modules.SALES_CHANNEL,
"sales_channel_id"
)

const existingItems = (await linkService.list(
{ product_id: data.ids },
{ select: ["product_id", "sales_channel_id"] }
)) as { product_id: string; sales_channel_id: string }[]

return new StepResponse(existingItems)
}
)
117 changes: 107 additions & 10 deletions packages/core/core-flows/src/product/workflows/update-products.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,32 @@
import { ProductTypes } from "@medusajs/types"
import { WorkflowData, createWorkflow } from "@medusajs/workflows-sdk"
import {
WorkflowData,
createWorkflow,
transform,
} from "@medusajs/workflows-sdk"
import { updateProductsStep } from "../steps/update-products"
import { getProductSalesChannelLinkStep } from "../steps/get-product-sales-channel-link"
import {
associateProductsWithSalesChannelsStep,
detachProductsFromSalesChannelsStep,
} from "../../sales-channel"

type UpdateProductsStepInputSelector = {
selector: ProductTypes.FilterableProductProps
update: ProductTypes.UpdateProductDTO & {
sales_channels?: { id: string }[]
}
}

type UpdateProductsStepInputProducts = {
products: (ProductTypes.UpsertProductDTO & {
sales_channels?: { id: string }[]
})[]
}

type UpdateProductsStepInput =
| {
selector: ProductTypes.FilterableProductProps
update: ProductTypes.UpdateProductDTO
}
| {
products: ProductTypes.UpsertProductDTO[]
}
| UpdateProductsStepInputSelector
| UpdateProductsStepInputProducts

type WorkflowInput = UpdateProductsStepInput
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Q: We have a mix of workflow types in the types package and in the core-flows package, it will require a cleanup at some point, not now but just raising it. we also have different places in the types package where the workflow types are define, workflows, workflow, and in some dir. maybe we can create a ticket

Copy link
Contributor

@olivermrbl olivermrbl May 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

prepareUpdateProductInput, updateProductIds, and prepareToDeleteLinks are all named as if they were generic transformers for updating products, but they are coupled with sales channel management as far as I can tell. So what I mean is, if we add another link to product, we will need to refactor these to account for that.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh sorry maybe i ve named them wrongly, but we can rework that, i just based the name on the workflow step they were used for. I didnt have a better idea at the moment but the main thing was to extract them (not necessarily generic) to have a better readability of the workflow and transformers themselves

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, those transformers are not exported they are only local so very specific here, i am fine with finding another name or rework the implementation a bit


Expand All @@ -20,7 +37,87 @@ export const updateProductsWorkflow = createWorkflow(
input: WorkflowData<WorkflowInput>
): WorkflowData<ProductTypes.ProductDTO[]> => {
fPolic marked this conversation as resolved.
Show resolved Hide resolved
// TODO: Delete price sets for removed variants
// TODO Update sales channel links
return updateProductsStep(input)

const toUpdateInput = transform({ input }, (data) => {
if ((data.input as UpdateProductsStepInputProducts).products) {
return (data.input as UpdateProductsStepInputProducts).products.map(
(p) => ({
...p,
sales_channels: undefined,
})
)
} else {
return {
selector: (data.input as UpdateProductsStepInputSelector).selector,
update: {
...(data.input as UpdateProductsStepInputSelector).update,
sales_channels: undefined,
},
}
}
})

const updatedProducts = updateProductsStep(toUpdateInput as any) // TODO: type

const updatedProductIds = transform({ updatedProducts, input }, (data) => {
if (
!(data.input as UpdateProductsStepInputSelector).update.sales_channels
) {
return []
}

let productIds = data.updatedProducts.map((p) => p.id)

;(data.input as UpdateProductsStepInputProducts).products?.forEach(
(p) => {
if (!p.sales_channels) {
// these products don't update sales channels so we don't want to their previous channels
productIds = productIds.filter((id) => id !== p.id)
}
}
)

return productIds
})

const currentLinks = getProductSalesChannelLinkStep({
ids: updatedProductIds,
})
fPolic marked this conversation as resolved.
Show resolved Hide resolved

detachProductsFromSalesChannelsStep({ links: currentLinks })
fPolic marked this conversation as resolved.
Show resolved Hide resolved

const salesChannelLinks = transform({ input, updatedProducts }, (data) => {
if ((data.input as UpdateProductsStepInputSelector).selector) {
if (
!(data.input as UpdateProductsStepInputSelector).update.sales_channels
) {
return []
}
return data.updatedProducts
.map((p) => {
return (
data.input as UpdateProductsStepInputSelector
).update.sales_channels!.map((channel) => ({
sales_channel_id: channel.id,
product_id: p.id,
}))
})
.flat()
fPolic marked this conversation as resolved.
Show resolved Hide resolved
}

return (data.input as UpdateProductsStepInputProducts).products
.filter((input) => input.sales_channels)
.map((input) => {
return input.sales_channels!.map((salesChannel) => ({
sales_channel_id: salesChannel.id,
product_id: input.id as string,
}))
})
.flat()
})

associateProductsWithSalesChannelsStep({ links: salesChannelLinks })
fPolic marked this conversation as resolved.
Show resolved Hide resolved

return updatedProducts
}
)
Loading