feat(workflows): update product workflow (#4982)
**What** - added "update product" workflow Co-authored-by: Riqwan Thamir <5105988+riqwan@users.noreply.github.com>
This commit is contained in:
@@ -1 +1,2 @@
|
||||
export * from "./create-products"
|
||||
export * from "./update-products"
|
||||
|
||||
364
packages/workflows/src/definition/product/update-products.ts
Normal file
364
packages/workflows/src/definition/product/update-products.ts
Normal file
@@ -0,0 +1,364 @@
|
||||
import { ProductTypes, WorkflowTypes } from "@medusajs/types"
|
||||
|
||||
import { InputAlias, Workflows } from "../../definitions"
|
||||
import {
|
||||
TransactionStepsDefinition,
|
||||
WorkflowManager,
|
||||
} from "@medusajs/orchestration"
|
||||
import { exportWorkflow, pipe } from "../../helper"
|
||||
import { CreateProductsActions } from "./create-products"
|
||||
import { InventoryHandlers, ProductHandlers } from "../../handlers"
|
||||
import * as MiddlewareHandlers from "../../handlers/middlewares"
|
||||
import { detachSalesChannelFromProducts } from "../../handlers/product"
|
||||
import { prepareCreateInventoryItems } from "./prepare-create-inventory-items"
|
||||
|
||||
export enum UpdateProductsActions {
|
||||
prepare = "prepare",
|
||||
updateProducts = "updateProducts",
|
||||
|
||||
attachSalesChannels = "attachSalesChannels",
|
||||
detachSalesChannels = "detachSalesChannels",
|
||||
|
||||
createInventoryItems = "createInventoryItems",
|
||||
attachInventoryItems = "attachInventoryItems",
|
||||
detachInventoryItems = "detachInventoryItems",
|
||||
removeInventoryItems = "removeInventoryItems",
|
||||
}
|
||||
|
||||
export const updateProductsWorkflowSteps: TransactionStepsDefinition = {
|
||||
next: {
|
||||
action: CreateProductsActions.prepare,
|
||||
noCompensation: true,
|
||||
next: {
|
||||
action: UpdateProductsActions.updateProducts,
|
||||
next: [
|
||||
{
|
||||
action: UpdateProductsActions.attachSalesChannels,
|
||||
saveResponse: false,
|
||||
},
|
||||
{
|
||||
action: UpdateProductsActions.detachSalesChannels,
|
||||
saveResponse: false,
|
||||
},
|
||||
{
|
||||
// for created variants
|
||||
action: UpdateProductsActions.createInventoryItems,
|
||||
next: {
|
||||
action: UpdateProductsActions.attachInventoryItems,
|
||||
},
|
||||
},
|
||||
{
|
||||
// for deleted variants
|
||||
action: UpdateProductsActions.detachInventoryItems,
|
||||
next: {
|
||||
action: UpdateProductsActions.removeInventoryItems,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const handlers = new Map([
|
||||
[
|
||||
UpdateProductsActions.prepare,
|
||||
{
|
||||
invoke: pipe(
|
||||
{
|
||||
merge: true,
|
||||
inputAlias: InputAlias.ProductsInputData,
|
||||
invoke: {
|
||||
from: InputAlias.ProductsInputData,
|
||||
},
|
||||
},
|
||||
ProductHandlers.updateProductsPrepareData
|
||||
),
|
||||
},
|
||||
],
|
||||
[
|
||||
UpdateProductsActions.updateProducts,
|
||||
{
|
||||
invoke: pipe(
|
||||
{
|
||||
merge: true,
|
||||
invoke: [
|
||||
{
|
||||
from: InputAlias.ProductsInputData,
|
||||
alias: ProductHandlers.updateProducts.aliases.products,
|
||||
},
|
||||
{
|
||||
from: UpdateProductsActions.prepare,
|
||||
},
|
||||
],
|
||||
},
|
||||
ProductHandlers.updateProducts
|
||||
),
|
||||
compensate: pipe(
|
||||
{
|
||||
merge: true,
|
||||
invoke: [
|
||||
{
|
||||
from: UpdateProductsActions.prepare,
|
||||
alias: ProductHandlers.revertUpdateProducts.aliases.preparedData,
|
||||
},
|
||||
{
|
||||
from: UpdateProductsActions.updateProducts,
|
||||
alias:
|
||||
MiddlewareHandlers.updateProductsExtractDeletedVariants.aliases
|
||||
.products,
|
||||
},
|
||||
],
|
||||
},
|
||||
MiddlewareHandlers.updateProductsExtractDeletedVariants,
|
||||
ProductHandlers.revertUpdateProducts
|
||||
),
|
||||
},
|
||||
],
|
||||
[
|
||||
UpdateProductsActions.attachSalesChannels,
|
||||
{
|
||||
invoke: pipe(
|
||||
{
|
||||
merge: true,
|
||||
invoke: [
|
||||
{
|
||||
from: UpdateProductsActions.prepare,
|
||||
alias: "preparedData",
|
||||
},
|
||||
{
|
||||
from: UpdateProductsActions.updateProducts,
|
||||
alias:
|
||||
ProductHandlers.attachSalesChannelToProducts.aliases.products,
|
||||
},
|
||||
],
|
||||
},
|
||||
MiddlewareHandlers.mapData((d) => ({
|
||||
productsHandleSalesChannelsMap:
|
||||
d.preparedData.productHandleAddedChannelsMap,
|
||||
})),
|
||||
ProductHandlers.attachSalesChannelToProducts
|
||||
),
|
||||
compensate: pipe(
|
||||
{
|
||||
merge: true,
|
||||
invoke: [
|
||||
{
|
||||
from: UpdateProductsActions.prepare,
|
||||
alias: "preparedData",
|
||||
},
|
||||
{
|
||||
from: UpdateProductsActions.updateProducts,
|
||||
alias: detachSalesChannelFromProducts.aliases.products,
|
||||
},
|
||||
],
|
||||
},
|
||||
MiddlewareHandlers.mapData((d) => ({
|
||||
productsHandleSalesChannelsMap:
|
||||
d.preparedData.productHandleAddedChannelsMap,
|
||||
})),
|
||||
ProductHandlers.detachSalesChannelFromProducts
|
||||
),
|
||||
},
|
||||
],
|
||||
[
|
||||
UpdateProductsActions.detachSalesChannels,
|
||||
{
|
||||
invoke: pipe(
|
||||
{
|
||||
merge: true,
|
||||
invoke: [
|
||||
{
|
||||
from: UpdateProductsActions.prepare,
|
||||
alias: "preparedData",
|
||||
},
|
||||
{
|
||||
from: UpdateProductsActions.updateProducts,
|
||||
alias:
|
||||
ProductHandlers.detachSalesChannelFromProducts.aliases.products,
|
||||
},
|
||||
],
|
||||
},
|
||||
MiddlewareHandlers.mapData((d) => ({
|
||||
productsHandleSalesChannelsMap:
|
||||
d.preparedData.productHandleRemovedChannelsMap,
|
||||
})),
|
||||
ProductHandlers.detachSalesChannelFromProducts
|
||||
),
|
||||
compensate: pipe(
|
||||
{
|
||||
merge: true,
|
||||
invoke: [
|
||||
{
|
||||
from: UpdateProductsActions.prepare,
|
||||
alias: "preparedData",
|
||||
},
|
||||
{
|
||||
from: UpdateProductsActions.updateProducts,
|
||||
alias:
|
||||
ProductHandlers.attachSalesChannelToProducts.aliases.products,
|
||||
},
|
||||
],
|
||||
},
|
||||
MiddlewareHandlers.mapData((d) => ({
|
||||
productsHandleSalesChannelsMap:
|
||||
d.preparedData.productHandleRemovedChannelsMap,
|
||||
})),
|
||||
ProductHandlers.attachSalesChannelToProducts
|
||||
),
|
||||
},
|
||||
],
|
||||
[
|
||||
UpdateProductsActions.createInventoryItems,
|
||||
{
|
||||
invoke: pipe(
|
||||
{
|
||||
merge: true,
|
||||
invoke: [
|
||||
{
|
||||
from: UpdateProductsActions.prepare,
|
||||
alias:
|
||||
MiddlewareHandlers.updateProductsExtractCreatedVariants.aliases
|
||||
.preparedData,
|
||||
},
|
||||
{
|
||||
from: UpdateProductsActions.updateProducts,
|
||||
alias:
|
||||
MiddlewareHandlers.updateProductsExtractCreatedVariants.aliases
|
||||
.products,
|
||||
},
|
||||
],
|
||||
},
|
||||
MiddlewareHandlers.updateProductsExtractCreatedVariants,
|
||||
prepareCreateInventoryItems,
|
||||
InventoryHandlers.createInventoryItems
|
||||
),
|
||||
compensate: pipe(
|
||||
{
|
||||
merge: true,
|
||||
invoke: {
|
||||
from: UpdateProductsActions.createInventoryItems,
|
||||
alias:
|
||||
InventoryHandlers.removeInventoryItems.aliases.inventoryItems,
|
||||
},
|
||||
},
|
||||
InventoryHandlers.removeInventoryItems
|
||||
),
|
||||
},
|
||||
],
|
||||
[
|
||||
UpdateProductsActions.attachInventoryItems,
|
||||
{
|
||||
invoke: pipe(
|
||||
{
|
||||
merge: true,
|
||||
invoke: {
|
||||
from: UpdateProductsActions.createInventoryItems,
|
||||
alias:
|
||||
InventoryHandlers.attachInventoryItems.aliases.inventoryItems,
|
||||
},
|
||||
},
|
||||
InventoryHandlers.attachInventoryItems
|
||||
),
|
||||
compensate: pipe(
|
||||
{
|
||||
merge: true,
|
||||
invoke: {
|
||||
from: UpdateProductsActions.attachInventoryItems,
|
||||
alias:
|
||||
InventoryHandlers.detachInventoryItems.aliases.inventoryItems,
|
||||
},
|
||||
},
|
||||
InventoryHandlers.detachInventoryItems
|
||||
),
|
||||
},
|
||||
],
|
||||
[
|
||||
UpdateProductsActions.detachInventoryItems,
|
||||
{
|
||||
invoke: pipe(
|
||||
{
|
||||
merge: true,
|
||||
invoke: [
|
||||
{
|
||||
from: UpdateProductsActions.prepare,
|
||||
alias:
|
||||
MiddlewareHandlers.updateProductsExtractDeletedVariants.aliases
|
||||
.preparedData,
|
||||
},
|
||||
{
|
||||
from: UpdateProductsActions.updateProducts,
|
||||
alias:
|
||||
MiddlewareHandlers.updateProductsExtractDeletedVariants.aliases
|
||||
.products,
|
||||
},
|
||||
],
|
||||
},
|
||||
MiddlewareHandlers.updateProductsExtractDeletedVariants,
|
||||
MiddlewareHandlers.useVariantsInventoryItems,
|
||||
InventoryHandlers.detachInventoryItems
|
||||
),
|
||||
compensate: pipe(
|
||||
{
|
||||
merge: true,
|
||||
invoke: [
|
||||
{
|
||||
from: UpdateProductsActions.prepare,
|
||||
alias:
|
||||
MiddlewareHandlers.updateProductsExtractDeletedVariants.aliases
|
||||
.preparedData,
|
||||
},
|
||||
{
|
||||
from: UpdateProductsActions.updateProducts,
|
||||
alias:
|
||||
MiddlewareHandlers.updateProductsExtractDeletedVariants.aliases
|
||||
.products,
|
||||
},
|
||||
],
|
||||
},
|
||||
MiddlewareHandlers.updateProductsExtractDeletedVariants,
|
||||
MiddlewareHandlers.useVariantsInventoryItems,
|
||||
InventoryHandlers.attachInventoryItems
|
||||
),
|
||||
},
|
||||
],
|
||||
[
|
||||
UpdateProductsActions.removeInventoryItems,
|
||||
{
|
||||
invoke: pipe(
|
||||
{
|
||||
merge: true,
|
||||
invoke: {
|
||||
from: UpdateProductsActions.detachInventoryItems,
|
||||
alias:
|
||||
InventoryHandlers.removeInventoryItems.aliases.inventoryItems,
|
||||
},
|
||||
},
|
||||
InventoryHandlers.removeInventoryItems
|
||||
),
|
||||
compensate: pipe(
|
||||
{
|
||||
merge: true,
|
||||
invoke: [
|
||||
{
|
||||
from: UpdateProductsActions.removeInventoryItems,
|
||||
alias:
|
||||
InventoryHandlers.restoreInventoryItems.aliases.inventoryItems,
|
||||
},
|
||||
],
|
||||
},
|
||||
InventoryHandlers.restoreInventoryItems
|
||||
),
|
||||
},
|
||||
],
|
||||
])
|
||||
|
||||
WorkflowManager.register(
|
||||
Workflows.UpdateProducts,
|
||||
updateProductsWorkflowSteps,
|
||||
handlers
|
||||
)
|
||||
|
||||
export const updateProducts = exportWorkflow<
|
||||
WorkflowTypes.ProductWorkflow.UpdateProductsWorkflowInputDTO,
|
||||
ProductTypes.ProductDTO[]
|
||||
>(Workflows.UpdateProducts, UpdateProductsActions.updateProducts)
|
||||
@@ -1,6 +1,7 @@
|
||||
export enum Workflows {
|
||||
// Product workflows
|
||||
CreateProducts = "create-products",
|
||||
UpdateProducts = "update-products",
|
||||
|
||||
// Cart workflows
|
||||
CreateCart = "create-cart",
|
||||
|
||||
@@ -17,7 +17,7 @@ export async function attachInventoryItems({
|
||||
.withTransaction(manager)
|
||||
|
||||
if (!data?.inventoryItems?.length) {
|
||||
return
|
||||
return []
|
||||
}
|
||||
|
||||
const inventoryData = data.inventoryItems.map(({ tag, inventoryItem }) => ({
|
||||
@@ -25,7 +25,9 @@ export async function attachInventoryItems({
|
||||
inventoryItemId: inventoryItem.id,
|
||||
}))
|
||||
|
||||
return await productVariantInventoryService.attachInventoryItem(inventoryData)
|
||||
await productVariantInventoryService.attachInventoryItem(inventoryData)
|
||||
|
||||
return data.inventoryItems
|
||||
}
|
||||
|
||||
attachInventoryItems.aliases = {
|
||||
|
||||
@@ -23,7 +23,7 @@ export async function createInventoryItems({
|
||||
return void 0
|
||||
}
|
||||
|
||||
const result = await Promise.all(
|
||||
return await Promise.all(
|
||||
data.inventoryItems.map(async (item) => {
|
||||
const inventoryItem = await inventoryService!.createInventoryItem({
|
||||
sku: item.sku!,
|
||||
@@ -40,8 +40,6 @@ export async function createInventoryItems({
|
||||
return { tag: item._associationTag ?? inventoryItem.id, inventoryItem }
|
||||
})
|
||||
)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
createInventoryItems.aliases = {
|
||||
|
||||
@@ -10,15 +10,15 @@ export async function detachInventoryItems({
|
||||
tag: string
|
||||
inventoryItem: InventoryItemDTO
|
||||
}[]
|
||||
}>): Promise<void> {
|
||||
}>) {
|
||||
const { manager } = context
|
||||
|
||||
const productVariantInventoryService = container
|
||||
.resolve("productVariantInventoryService")
|
||||
.withTransaction(manager)
|
||||
|
||||
if (!data?.inventoryItems.length) {
|
||||
return
|
||||
if (!data?.inventoryItems?.length) {
|
||||
return []
|
||||
}
|
||||
|
||||
await Promise.all(
|
||||
@@ -29,6 +29,8 @@ export async function detachInventoryItems({
|
||||
)
|
||||
})
|
||||
)
|
||||
|
||||
return data.inventoryItems
|
||||
}
|
||||
|
||||
detachInventoryItems.aliases = {
|
||||
|
||||
@@ -2,3 +2,4 @@ export * from "./detach-inventory-items"
|
||||
export * from "./attach-inventory-items"
|
||||
export * from "./create-inventory-items"
|
||||
export * from "./remove-inventory-items"
|
||||
export * from "./restore-inventory-items"
|
||||
|
||||
@@ -14,12 +14,14 @@ export async function removeInventoryItems({
|
||||
logger.warn(
|
||||
`Inventory service not found. You should install the @medusajs/inventory package to use inventory. The 'removeInventoryItems' will be skipped.`
|
||||
)
|
||||
return
|
||||
return []
|
||||
}
|
||||
|
||||
return await inventoryService!.deleteInventoryItem(
|
||||
await inventoryService!.deleteInventoryItem(
|
||||
data.inventoryItems.map(({ inventoryItem }) => inventoryItem.id)
|
||||
)
|
||||
|
||||
return data.inventoryItems
|
||||
}
|
||||
|
||||
removeInventoryItems.aliases = {
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import {
|
||||
IInventoryService,
|
||||
InventoryItemDTO,
|
||||
SharedContext,
|
||||
} from "@medusajs/types"
|
||||
import { WorkflowArguments } from "../../helper"
|
||||
|
||||
export async function restoreInventoryItems({
|
||||
container,
|
||||
context,
|
||||
data,
|
||||
}: WorkflowArguments<{
|
||||
inventoryItems: { inventoryItem: InventoryItemDTO }[]
|
||||
}>) {
|
||||
const { manager } = context as SharedContext
|
||||
const inventoryService: IInventoryService =
|
||||
container.resolve("inventoryService")
|
||||
|
||||
if (!inventoryService) {
|
||||
const logger = container.resolve("logger")
|
||||
logger.warn(
|
||||
`Inventory service not found. You should install the @medusajs/inventory package to use inventory. The 'removeInventoryItems' will be skipped.`
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
return await inventoryService!.restoreInventoryItem(
|
||||
data.inventoryItems.map(({ inventoryItem }) => inventoryItem.id),
|
||||
{ transactionManager: manager }
|
||||
)
|
||||
}
|
||||
|
||||
restoreInventoryItems.aliases = {
|
||||
inventoryItems: "inventoryItems",
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import { ProductTypes } from "@medusajs/types"
|
||||
|
||||
import { WorkflowArguments } from "../../helper"
|
||||
|
||||
export async function extractVariants({
|
||||
data,
|
||||
}: WorkflowArguments<{
|
||||
object: { variants?: ProductTypes.ProductVariantDTO[] }[]
|
||||
}>) {
|
||||
const variants = data.object.reduce((acc, object) => {
|
||||
if (object.variants?.length) {
|
||||
return acc.concat(object.variants)
|
||||
}
|
||||
return acc
|
||||
}, [] as ProductTypes.ProductVariantDTO[])
|
||||
|
||||
return {
|
||||
alias: extractVariants.aliases.output,
|
||||
value: {
|
||||
variants,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
extractVariants.aliases = {
|
||||
output: "extractVariantsFromProductOutput",
|
||||
object: "object",
|
||||
}
|
||||
@@ -1 +1,6 @@
|
||||
export * from "./create-products-prepare-create-prices-compensation"
|
||||
export * from "./update-products-extract-created-variants"
|
||||
export * from "./update-products-extract-deleted-variants"
|
||||
export * from "./use-variants-inventory-items"
|
||||
export * from "./extract-variants"
|
||||
export * from "./map-data"
|
||||
|
||||
16
packages/workflows/src/handlers/middlewares/map-data.ts
Normal file
16
packages/workflows/src/handlers/middlewares/map-data.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { WorkflowArguments } from "../../helper"
|
||||
|
||||
/**
|
||||
* Middleware for map input data to a key/s.
|
||||
*
|
||||
* @param mapFn - apply function on the input data and return result as the middleware output
|
||||
* @param alias - key to save output under (if `merge === false`)
|
||||
*/
|
||||
export function mapData<T, S>(mapFn: (arg: T) => S, alias = "mapData") {
|
||||
return async function ({ data }: WorkflowArguments<T>) {
|
||||
return {
|
||||
alias,
|
||||
value: mapFn(data),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
import { ProductTypes, ProductVariantDTO } from "@medusajs/types"
|
||||
|
||||
import { WorkflowArguments } from "../../helper"
|
||||
import { UpdateProductsPreparedData } from "../product"
|
||||
|
||||
export async function updateProductsExtractCreatedVariants({
|
||||
data,
|
||||
}: WorkflowArguments<{
|
||||
preparedData: UpdateProductsPreparedData // products state before the update
|
||||
products: ProductTypes.ProductDTO[] // updated products
|
||||
}>) {
|
||||
const createdVariants: ProductVariantDTO[] = []
|
||||
|
||||
data.products.forEach((product) => {
|
||||
const addedVariants: ProductVariantDTO[] = []
|
||||
|
||||
const originalProduct = data.preparedData.originalProducts.find(
|
||||
(p) => p.id === product.id
|
||||
)!
|
||||
|
||||
product.variants.forEach((variant) => {
|
||||
if (!originalProduct.variants.find((v) => v.id === variant.id)) {
|
||||
addedVariants.push(variant)
|
||||
}
|
||||
})
|
||||
|
||||
createdVariants.push(...addedVariants)
|
||||
})
|
||||
|
||||
return {
|
||||
alias: updateProductsExtractCreatedVariants.aliases.output,
|
||||
value: [{ variants: createdVariants }],
|
||||
}
|
||||
}
|
||||
|
||||
updateProductsExtractCreatedVariants.aliases = {
|
||||
preparedData: "preparedData",
|
||||
products: "products",
|
||||
output: "products",
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
import { ProductTypes, ProductVariantDTO } from "@medusajs/types"
|
||||
|
||||
import { WorkflowArguments } from "../../helper"
|
||||
import { UpdateProductsPreparedData } from "../product"
|
||||
|
||||
export async function updateProductsExtractDeletedVariants({
|
||||
data,
|
||||
container,
|
||||
}: WorkflowArguments<{
|
||||
preparedData: UpdateProductsPreparedData // products state before the update
|
||||
products: ProductTypes.ProductDTO[] // updated products
|
||||
}>) {
|
||||
const deletedVariants: ProductVariantDTO[] = []
|
||||
|
||||
data.products.forEach((product) => {
|
||||
const removedVariants: ProductVariantDTO[] = []
|
||||
|
||||
const originalProduct = data.preparedData.originalProducts.find(
|
||||
(p) => p.id === product.id
|
||||
)!
|
||||
|
||||
originalProduct.variants.forEach((variant) => {
|
||||
if (!product.variants.find((v) => v.id === variant.id)) {
|
||||
removedVariants.push(variant)
|
||||
}
|
||||
})
|
||||
|
||||
deletedVariants.push(...removedVariants)
|
||||
})
|
||||
|
||||
return {
|
||||
alias: updateProductsExtractDeletedVariants.aliases.output,
|
||||
value: {
|
||||
variants: deletedVariants,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
updateProductsExtractDeletedVariants.aliases = {
|
||||
preparedData: "preparedData",
|
||||
products: "products",
|
||||
output: "updateProductsExtractDeletedVariantsOutput",
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
import { WorkflowArguments } from "../../helper"
|
||||
import { IInventoryService, ProductVariantDTO } from "@medusajs/types"
|
||||
|
||||
export async function useVariantsInventoryItems({
|
||||
data,
|
||||
container,
|
||||
}: WorkflowArguments<{
|
||||
updateProductsExtractDeletedVariantsOutput: { variants: ProductVariantDTO[] }
|
||||
}>) {
|
||||
const inventoryService: IInventoryService =
|
||||
container.resolve("inventoryService")
|
||||
|
||||
if (!inventoryService) {
|
||||
const logger = container.resolve("logger")
|
||||
logger.warn(
|
||||
`Inventory service not found. You should install the @medusajs/inventory package to use inventory. The 'useVariantsInventoryItems' will be skipped.`
|
||||
)
|
||||
return {
|
||||
alias: useVariantsInventoryItems.aliases.output,
|
||||
value: null,
|
||||
}
|
||||
}
|
||||
|
||||
const [inventoryItems] = await inventoryService!.listInventoryItems({
|
||||
sku: data.updateProductsExtractDeletedVariantsOutput.variants.map(
|
||||
(v) => v.id
|
||||
),
|
||||
})
|
||||
|
||||
const variantItems = inventoryItems.map((item) => ({
|
||||
inventoryItem: item,
|
||||
tag: data.updateProductsExtractDeletedVariantsOutput.variants.find(
|
||||
(variant) => variant.sku === item.sku
|
||||
)!.id,
|
||||
}))
|
||||
|
||||
return {
|
||||
alias: useVariantsInventoryItems.aliases.output,
|
||||
value: { inventoryItems: variantItems },
|
||||
}
|
||||
}
|
||||
|
||||
useVariantsInventoryItems.aliases = {
|
||||
variants: "variants",
|
||||
output: "useVariantsInventoryItemsOutput",
|
||||
}
|
||||
@@ -6,4 +6,7 @@ export * from "./detach-shipping-profile-from-products"
|
||||
export * from "./remove-products"
|
||||
export * from "./attach-shipping-profile-to-products"
|
||||
export * from "./list-products"
|
||||
export * from "./update-products"
|
||||
export * from "./update-products-prepare-data"
|
||||
export * from "./revert-update-products"
|
||||
export * from "./update-products-variants-prices"
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
import { Modules, ModulesDefinition } from "@medusajs/modules-sdk"
|
||||
import {
|
||||
ProductDTO,
|
||||
ProductTypes,
|
||||
ProductVariantDTO,
|
||||
UpdateProductDTO,
|
||||
} from "@medusajs/types"
|
||||
|
||||
import { WorkflowArguments } from "../../helper"
|
||||
import { UpdateProductsPreparedData } from "./update-products-prepare-data"
|
||||
|
||||
type HandlerInput = UpdateProductsPreparedData & {
|
||||
variants: ProductVariantDTO[]
|
||||
}
|
||||
|
||||
export async function revertUpdateProducts({
|
||||
container,
|
||||
data,
|
||||
}: WorkflowArguments<HandlerInput>): Promise<ProductDTO[]> {
|
||||
const productModuleService: ProductTypes.IProductModuleService =
|
||||
container.resolve(ModulesDefinition[Modules.PRODUCT].registrationName)
|
||||
|
||||
// restore variants that have been soft deleted during update products step
|
||||
await productModuleService.restoreVariants(data.variants.map((v) => v.id))
|
||||
data.originalProducts.forEach((product) => {
|
||||
// @ts-ignore
|
||||
product.variants = product.variants.map((v) => ({ id: v.id }))
|
||||
})
|
||||
|
||||
return await productModuleService.update(
|
||||
data.originalProducts as unknown as UpdateProductDTO[]
|
||||
)
|
||||
}
|
||||
|
||||
revertUpdateProducts.aliases = {
|
||||
preparedData: "preparedData",
|
||||
variants: "variants",
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
import { ProductDTO, SalesChannelDTO, WorkflowTypes } from "@medusajs/types"
|
||||
|
||||
import { WorkflowArguments } from "../../helper"
|
||||
|
||||
type ProductWithSalesChannelsDTO = ProductDTO & {
|
||||
sales_channels?: SalesChannelDTO[]
|
||||
}
|
||||
|
||||
export type UpdateProductsPreparedData = {
|
||||
originalProducts: ProductWithSalesChannelsDTO[]
|
||||
productHandleAddedChannelsMap: Map<string, string[]>
|
||||
productHandleRemovedChannelsMap: Map<string, string[]>
|
||||
}
|
||||
|
||||
export async function updateProductsPrepareData({
|
||||
container,
|
||||
context,
|
||||
data,
|
||||
}: WorkflowArguments<WorkflowTypes.ProductWorkflow.UpdateProductsWorkflowInputDTO>): Promise<UpdateProductsPreparedData> {
|
||||
const ids = data.products.map((product) => product.id)
|
||||
|
||||
const productHandleAddedChannelsMap = new Map<string, string[]>()
|
||||
const productHandleRemovedChannelsMap = new Map<string, string[]>()
|
||||
|
||||
const productService = container.resolve("productService")
|
||||
const productServiceTx = productService.withTransaction(context.manager)
|
||||
|
||||
const products = await productServiceTx.list(
|
||||
// TODO: use RemoteQuery - sales_channels needs to be added to the joiner config
|
||||
{ id: ids },
|
||||
{
|
||||
relations: [
|
||||
"variants",
|
||||
"variants.options",
|
||||
"images",
|
||||
"options",
|
||||
"tags",
|
||||
"collection",
|
||||
"sales_channels",
|
||||
],
|
||||
}
|
||||
)
|
||||
|
||||
data.products.forEach((productInput) => {
|
||||
const removedChannels: string[] = []
|
||||
const addedChannels: string[] = []
|
||||
|
||||
const currentProduct = products.find(
|
||||
(p) => p.id === productInput.id
|
||||
) as unknown as ProductWithSalesChannelsDTO
|
||||
|
||||
if (productInput.sales_channels) {
|
||||
productInput.sales_channels.forEach((channel) => {
|
||||
if (
|
||||
!currentProduct.sales_channels?.find((sc) => sc.id === channel.id)
|
||||
) {
|
||||
addedChannels.push(channel.id)
|
||||
}
|
||||
})
|
||||
|
||||
currentProduct.sales_channels?.forEach((channel) => {
|
||||
if (!productInput.sales_channels!.find((sc) => sc.id === channel.id)) {
|
||||
removedChannels.push(channel.id)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
productHandleAddedChannelsMap.set(currentProduct.handle!, addedChannels)
|
||||
productHandleRemovedChannelsMap.set(currentProduct.handle!, removedChannels)
|
||||
})
|
||||
|
||||
return {
|
||||
originalProducts: products,
|
||||
productHandleAddedChannelsMap,
|
||||
productHandleRemovedChannelsMap,
|
||||
}
|
||||
}
|
||||
|
||||
updateProductsPrepareData.aliases = {
|
||||
preparedData: "preparedData",
|
||||
}
|
||||
44
packages/workflows/src/handlers/product/update-products.ts
Normal file
44
packages/workflows/src/handlers/product/update-products.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import { Modules, ModulesDefinition } from "@medusajs/modules-sdk"
|
||||
import { ProductDTO, ProductTypes } from "@medusajs/types"
|
||||
|
||||
import { WorkflowArguments } from "../../helper"
|
||||
|
||||
type HandlerInput = {
|
||||
products: ProductTypes.UpdateProductDTO[]
|
||||
}
|
||||
|
||||
export async function updateProducts({
|
||||
container,
|
||||
context,
|
||||
data,
|
||||
}: WorkflowArguments<HandlerInput>): Promise<ProductDTO[]> {
|
||||
if (!data.products.length) {
|
||||
return []
|
||||
}
|
||||
|
||||
const productModuleService: ProductTypes.IProductModuleService =
|
||||
container.resolve(ModulesDefinition[Modules.PRODUCT].registrationName)
|
||||
|
||||
const products = await productModuleService.update(data.products)
|
||||
|
||||
return await productModuleService.list(
|
||||
{ id: products.map((p) => p.id) },
|
||||
{
|
||||
relations: [
|
||||
"variants",
|
||||
"variants.options",
|
||||
"images",
|
||||
"options",
|
||||
"tags",
|
||||
// "type",
|
||||
"collection",
|
||||
// "profiles",
|
||||
// "sales_channels",
|
||||
],
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
updateProducts.aliases = {
|
||||
products: "products",
|
||||
}
|
||||
Reference in New Issue
Block a user