Skip to content

Commit 439c711

Browse files
authored
fix(dashboard, product): product attributes update with a relation update (medusajs#13019)
* fix(dashboard, product): product attributes update with a relation update * fix: rm log * chore: refactor
1 parent c71ace0 commit 439c711

File tree

7 files changed

+156
-35
lines changed

7 files changed

+156
-35
lines changed

.changeset/polite-falcons-glow.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@medusajs/dashboard": patch
3+
"@medusajs/product": patch
4+
---
5+
6+
fix(dashboard, product): update product attributes

integration-tests/http/__tests__/product/admin/product.spec.ts

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1813,6 +1813,110 @@ medusaIntegrationTestRunner({
18131813
)
18141814
})
18151815

1816+
it("updates products relations and attributes", async () => {
1817+
const shortsCategory = (
1818+
await api.post(
1819+
"/admin/product-categories",
1820+
{ name: "Shorts", is_internal: false, is_active: true },
1821+
adminHeaders
1822+
)
1823+
).data.product_category
1824+
1825+
const pantsCategory = (
1826+
await api.post(
1827+
"/admin/product-categories",
1828+
{ name: "Pants", is_internal: false, is_active: true },
1829+
adminHeaders
1830+
)
1831+
).data.product_category
1832+
1833+
const payload = {
1834+
title: "Test an update",
1835+
weight: 100,
1836+
length: 100,
1837+
width: 100,
1838+
height: 100,
1839+
options: [{ title: "size", values: ["large", "small"] }],
1840+
variants: [
1841+
{
1842+
options: { size: "large" },
1843+
title: "New variant",
1844+
prices: [
1845+
{
1846+
currency_code: "usd",
1847+
amount: 200,
1848+
},
1849+
],
1850+
},
1851+
],
1852+
}
1853+
1854+
const createdProduct = (
1855+
await api.post("/admin/products", payload, adminHeaders)
1856+
).data.product
1857+
1858+
let updatedProduct = (
1859+
await api.post(
1860+
`/admin/products/${createdProduct.id}`,
1861+
{ weight: 20, length: null },
1862+
adminHeaders
1863+
)
1864+
).data.product
1865+
1866+
expect(updatedProduct).toEqual(
1867+
expect.objectContaining({
1868+
weight: "20",
1869+
length: null,
1870+
width: "100",
1871+
height: "100",
1872+
})
1873+
)
1874+
1875+
updatedProduct = (
1876+
await api.post(
1877+
`/admin/products/${createdProduct.id}?fields=+categories.id`,
1878+
{ categories: [{ id: pantsCategory.id }] },
1879+
adminHeaders
1880+
)
1881+
).data.product
1882+
1883+
expect(updatedProduct).toEqual(
1884+
expect.objectContaining({
1885+
weight: "20",
1886+
length: null,
1887+
width: "100",
1888+
height: "100",
1889+
categories: expect.arrayContaining([
1890+
expect.objectContaining({
1891+
id: pantsCategory.id,
1892+
}),
1893+
]),
1894+
})
1895+
)
1896+
1897+
updatedProduct = (
1898+
await api.post(
1899+
`/admin/products/${createdProduct.id}?fields=+categories.id`,
1900+
{ weight: null, length: 20, width: 50 },
1901+
adminHeaders
1902+
)
1903+
).data.product
1904+
1905+
expect(updatedProduct).toEqual(
1906+
expect.objectContaining({
1907+
weight: null,
1908+
length: "20",
1909+
width: "50",
1910+
height: "100",
1911+
categories: expect.arrayContaining([
1912+
expect.objectContaining({
1913+
id: pantsCategory.id,
1914+
}),
1915+
]),
1916+
})
1917+
)
1918+
})
1919+
18161920
it("updates a product (update prices, tags, update status, delete collection, delete type, replaces images)", async () => {
18171921
const payload = {
18181922
collection_id: null,

packages/admin/dashboard/src/routes/products/product-attributes/components/product-attributes-form/product-attributes-form.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,10 @@ export const ProductAttributesForm = ({
6868
const handleSubmit = form.handleSubmit(async (data) => {
6969
await mutateAsync(
7070
{
71-
weight: data.weight ? data.weight : undefined,
72-
length: data.length ? data.length : undefined,
73-
width: data.width ? data.width : undefined,
74-
height: data.height ? data.height : undefined,
71+
weight: data.weight ? data.weight : null,
72+
length: data.length ? data.length : null,
73+
width: data.width ? data.width : null,
74+
height: data.height ? data.height : null,
7575
mid_code: data.mid_code,
7676
hs_code: data.hs_code,
7777
origin_country: data.origin_country,

packages/core/core-flows/src/order/workflows/return/cancel-return.ts

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,14 @@ export type CancelReturnValidateOrderInput = {
3232
* This step validates that a return can be canceled.
3333
* If the return is canceled, its fulfillment aren't canceled,
3434
* or it has received items, the step will throw an error.
35-
*
35+
*
3636
* :::note
37-
*
37+
*
3838
* You can retrieve a return details using [Query](https://docs.medusajs.com/learn/fundamentals/module-links/query),
3939
* or [useQueryGraphStep](https://docs.medusajs.com/resources/references/medusa-workflows/steps/useQueryGraphStep).
40-
*
40+
*
4141
* :::
42-
*
42+
*
4343
* @example
4444
* const data = cancelReturnValidateOrder({
4545
* orderReturn: {
@@ -53,9 +53,7 @@ export type CancelReturnValidateOrderInput = {
5353
*/
5454
export const cancelReturnValidateOrder = createStep(
5555
"validate-return",
56-
({
57-
orderReturn,
58-
}: CancelReturnValidateOrderInput) => {
56+
({ orderReturn }: CancelReturnValidateOrderInput) => {
5957
const orderReturn_ = orderReturn as ReturnDTO & {
6058
payment_collections: PaymentCollectionDTO[]
6159
fulfillments: FulfillmentDTO[]
@@ -92,22 +90,22 @@ export const cancelReturnValidateOrder = createStep(
9290

9391
export const cancelReturnWorkflowId = "cancel-return"
9492
/**
95-
* This workflow cancels a return. It's used by the
93+
* This workflow cancels a return. It's used by the
9694
* [Cancel Return Admin API Route](https://docs.medusajs.com/api/admin#returns_postreturnsidcancel).
97-
*
95+
*
9896
* You can use this workflow within your customizations or your own custom workflows, allowing you
9997
* to cancel a return in your custom flow.
100-
*
98+
*
10199
* @example
102100
* const { result } = await cancelReturnWorkflow(container)
103101
* .run({
104102
* input: {
105103
* return_id: "return_123",
106104
* }
107105
* })
108-
*
106+
*
109107
* @summary
110-
*
108+
*
111109
* Cancel a return.
112110
*/
113111
export const cancelReturnWorkflow = createWorkflow(

packages/core/core-flows/src/product/steps/update-products.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,29 +11,29 @@ import { StepResponse, createStep } from "@medusajs/framework/workflows-sdk"
1111
*/
1212
export type UpdateProductsStepInput =
1313
| {
14-
/**
15-
* The filters to select the products to update.
16-
*/
14+
/**
15+
* The filters to select the products to update.
16+
*/
1717
selector: ProductTypes.FilterableProductProps
1818
/**
1919
* The data to update the products with.
2020
*/
2121
update: ProductTypes.UpdateProductDTO
2222
}
2323
| {
24-
/**
25-
* The data to create or update products.
26-
*/
24+
/**
25+
* The data to create or update products.
26+
*/
2727
products: ProductTypes.UpsertProductDTO[]
2828
}
2929

3030
export const updateProductsStepId = "update-products"
3131
/**
3232
* This step updates one or more products.
33-
*
33+
*
3434
* @example
3535
* To update products by their ID:
36-
*
36+
*
3737
* ```ts
3838
* const data = updateProductsStep({
3939
* products: [
@@ -44,9 +44,9 @@ export const updateProductsStepId = "update-products"
4444
* ]
4545
* })
4646
* ```
47-
*
47+
*
4848
* To update products matching a filter:
49-
*
49+
*
5050
* ```ts
5151
* const data = updateProductsStep({
5252
* selector: {

packages/core/core-flows/src/product/workflows/update-products.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -343,12 +343,12 @@ export const updateProductsWorkflowId = "update-products"
343343
* allows you to update custom data models linked to the products.
344344
*
345345
* You can also use this workflow within your customizations or your own custom workflows, allowing you to wrap custom logic around product update.
346-
*
346+
*
347347
* :::note
348-
*
349-
* Learn more about adding rules to the product variant's prices in the Pricing Module's
348+
*
349+
* Learn more about adding rules to the product variant's prices in the Pricing Module's
350350
* [Price Rules](https://docs.medusajs.com/resources/commerce-modules/pricing/price-rules) documentation.
351-
*
351+
*
352352
* :::
353353
*
354354
* @example

packages/modules/product/src/repositories/product.ts

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
MedusaError,
99
isPresent,
1010
mergeMetadata,
11+
isDefined,
1112
} from "@medusajs/framework/utils"
1213
import { SqlEntityManager, wrap } from "@mikro-orm/postgresql"
1314

@@ -57,10 +58,18 @@ export class ProductRepository extends DALUtils.mikroOrmBaseRepositoryFactory(
5758
height?: string | number
5859
width?: string | number
5960
}) {
60-
productToUpdate.weight = productToUpdate.weight?.toString()
61-
productToUpdate.length = productToUpdate.length?.toString()
62-
productToUpdate.height = productToUpdate.height?.toString()
63-
productToUpdate.width = productToUpdate.width?.toString()
61+
if (isDefined(productToUpdate.weight)) {
62+
productToUpdate.weight = productToUpdate.weight?.toString()
63+
}
64+
if (isDefined(productToUpdate.length)) {
65+
productToUpdate.length = productToUpdate.length?.toString()
66+
}
67+
if (isDefined(productToUpdate.height)) {
68+
productToUpdate.height = productToUpdate.height?.toString()
69+
}
70+
if (isDefined(productToUpdate.width)) {
71+
productToUpdate.width = productToUpdate.width?.toString()
72+
}
6473
}
6574

6675
async deepUpdate(
@@ -72,6 +81,7 @@ export class ProductRepository extends DALUtils.mikroOrmBaseRepositoryFactory(
7281
context: Context = {}
7382
): Promise<InferEntityType<typeof Product>[]> {
7483
const productIdsToUpdate: string[] = []
84+
7585
productsToUpdate.forEach((productToUpdate) => {
7686
ProductRepository.#correctUpdateDTOTypes(productToUpdate)
7787
productIdsToUpdate.push(productToUpdate.id)
@@ -151,7 +161,10 @@ export class ProductRepository extends DALUtils.mikroOrmBaseRepositoryFactory(
151161
}
152162

153163
if (isPresent(productToUpdate.metadata)) {
154-
productToUpdate.metadata = mergeMetadata(product.metadata ?? {}, productToUpdate.metadata)
164+
productToUpdate.metadata = mergeMetadata(
165+
product.metadata ?? {},
166+
productToUpdate.metadata
167+
)
155168
}
156169

157170
wrappedProduct.assign(productToUpdate)

0 commit comments

Comments
 (0)