From 17e96737698c177a7bb3c79fa96d57c1db0581c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Frane=20Poli=C4=87?= <16856471+fPolic@users.noreply.github.com> Date: Mon, 10 Feb 2025 09:13:47 +0100 Subject: [PATCH] fix(core-flows): variants update unsets prices (#11349) * fix: update variant flow for prices * fix: import add test * fix: test name --- .../__tests__/product/admin/product.spec.ts | 97 +++++++++++++++++++ .../workflows/update-product-variants.ts | 40 ++++---- 2 files changed, 120 insertions(+), 17 deletions(-) diff --git a/integration-tests/http/__tests__/product/admin/product.spec.ts b/integration-tests/http/__tests__/product/admin/product.spec.ts index 912d085f45..5152a853e3 100644 --- a/integration-tests/http/__tests__/product/admin/product.spec.ts +++ b/integration-tests/http/__tests__/product/admin/product.spec.ts @@ -3182,6 +3182,103 @@ medusaIntegrationTestRunner({ ) }) + it("should preserve variant prices when batch updating variants", async () => { + const productWithMultipleVariants = getProductFixture({ + title: "Test batch variants", + handle: "test-batch-variants", + shipping_profile_id: shippingProfile.id, + variants: [ + { + title: "Variant 1", + prices: [ + { + currency_code: "usd", + amount: 100, + }, + ], + }, + { + title: "Variant 2", + prices: [ + { + currency_code: "usd", + amount: 200, + }, + ], + }, + ], + }) + + const createdProduct = ( + await api.post( + "/admin/products", + productWithMultipleVariants, + adminHeaders + ) + ).data.product + + const variant1Id = createdProduct.variants.find( + (v) => v.title === "Variant 1" + ).id + + const variant2Id = createdProduct.variants.find( + (v) => v.title === "Variant 2" + ).id + + const updatePayload1 = { + id: variant1Id, + title: "Test batch update variant", + } + + const updatePayload2 = { + id: variant2Id, + prices: [{ currency_code: "usd", amount: 300 }], + } + + const response = await api.post( + `/admin/products/${createdProduct.id}/variants/batch`, + { + update: [updatePayload1, updatePayload2], + }, + adminHeaders + ) + + const dbData = ( + await api.get( + `/admin/products/${createdProduct.id}?fields=*variants.prices`, + adminHeaders + ) + ).data.product.variants + + expect(response.status).toEqual(200) + expect(dbData).toHaveLength(2) + expect(dbData).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + id: variant1Id, + title: "Test batch update variant", + prices: expect.arrayContaining([ + // updated title but price remains the same + expect.objectContaining({ + currency_code: "usd", + amount: 100, + }), + ]), + }), + expect.objectContaining({ + id: variant2Id, + prices: expect.arrayContaining([ + expect.objectContaining({ + // updated price + currency_code: "usd", + amount: 300, + }), + ]), + }), + ]) + ) + }) + it("successfully adds and removes products to a collection", async () => { const response = await api.post( `/admin/collections/${baseCollection.id}/products`, diff --git a/packages/core/core-flows/src/product/workflows/update-product-variants.ts b/packages/core/core-flows/src/product/workflows/update-product-variants.ts index dd162c7a62..03c38422e9 100644 --- a/packages/core/core-flows/src/product/workflows/update-product-variants.ts +++ b/packages/core/core-flows/src/product/workflows/update-product-variants.ts @@ -19,8 +19,8 @@ import { getVariantPricingLinkStep } from "../steps/get-variant-pricing-link" /** * The data to update one or more product variants, along with custom data that's passed to the workflow's hooks. */ -export type UpdateProductVariantsWorkflowInput = - (| { +export type UpdateProductVariantsWorkflowInput = ( + | { /** * A filter to select the product variants to update. */ @@ -45,20 +45,22 @@ export type UpdateProductVariantsWorkflowInput = */ prices?: Partial[] })[] - }) & AdditionalData + } +) & + AdditionalData export const updateProductVariantsWorkflowId = "update-product-variants" /** * This workflow updates one or more product variants. It's used by the [Update Product Variant Admin API Route](https://docs.medusajs.com/api/admin#products_postproductsidvariantsvariant_id). - * - * This workflow has a hook that allows you to perform custom actions on the updated product variants. For example, you can pass under `additional_data` custom data that + * + * This workflow has a hook that allows you to perform custom actions on the updated product variants. For example, you can pass under `additional_data` custom data that * allows you to update custom data models linked to the product variants. - * + * * You can also use this workflow within your customizations or your own custom workflows, allowing you to wrap custom logic around product-variant update. - * + * * @example * To update product variants by their IDs: - * + * * ```ts * const { result } = await updateProductVariantsWorkflow(container) * .run({ @@ -84,9 +86,9 @@ export const updateProductVariantsWorkflowId = "update-product-variants" * } * }) * ``` - * + * * You can also update product variants by a selector: - * + * * ```ts * const { result } = await updateProductVariantsWorkflow(container) * .run({ @@ -108,18 +110,16 @@ export const updateProductVariantsWorkflowId = "update-product-variants" * } * }) * ``` - * + * * @summary - * + * * Update one or more product variants. - * + * * @property hooks.productVariantsUpdated - This hook is executed after the product variants are updated. You can consume this hook to perform custom actions on the updated product variants. */ export const updateProductVariantsWorkflow = createWorkflow( updateProductVariantsWorkflowId, - ( - input: WorkflowData - ) => { + (input: WorkflowData) => { // Passing prices to the product module will fail, we want to keep them for after the variant is updated. const updateWithoutPrices = transform({ input }, (data) => { if ("product_variants" in data.input) { @@ -147,7 +147,13 @@ export const updateProductVariantsWorkflow = createWorkflow( // We don't want to do any pricing updates if the prices didn't change const variantIds = transform({ input, updatedVariants }, (data) => { if ("product_variants" in data.input) { - return data.updatedVariants.map((v) => v.id) + const variantsWithPriceUpdates = new Set( + data.input.product_variants.filter((v) => !!v.prices).map((v) => v.id) + ) + + return data.updatedVariants + .map((v) => v.id) + .filter((id) => variantsWithPriceUpdates.has(id)) } if (!data.input.update.prices) {