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(stock-location,core-flows,types): updates existing address when updating stock location #10832

Merged
merged 4 commits into from
Jan 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/five-roses-swim.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@medusajs/stock-location": patch
"@medusajs/core-flows": patch
"@medusajs/types": patch
---

fix(stock-location,core-flows,types): update existing address when updating stock location address
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,58 @@ medusaIntegrationTestRunner({
expect(response.status).toEqual(200)
expect(response.data.stock_location.name).toEqual("new name")
})

it("should update stock location address without creating new addresses", async () => {
const response = await api.post(
`/admin/stock-locations/${location1.id}`,
{
name: "new name",
address: {
address_1: "test",
country_code: "dk",
},
},
adminHeaders
)

const firstAddressId = response.data.stock_location.address.id

expect(response.status).toEqual(200)
expect(response.data.stock_location).toEqual(
expect.objectContaining({
name: "new name",
address: expect.objectContaining({
id: firstAddressId,
address_1: "test",
country_code: "dk",
}),
})
)

const response2 = await api.post(
`/admin/stock-locations/${location1.id}`,
{
name: "new name 2",
address: {
address_1: "test 2",
country_code: "dk",
},
},
adminHeaders
)

expect(response2.status).toEqual(200)
expect(response2.data.stock_location).toEqual(
expect.objectContaining({
name: "new name 2",
address: expect.objectContaining({
id: firstAddressId,
address_1: "test 2",
country_code: "dk",
}),
})
)
})
})

describe("Get stock location", () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import {
IStockLocationService,
UpsertStockLocationAddressInput,
} from "@medusajs/framework/types"
import {
getSelectsAndRelationsFromObjectArray,
promiseAll,
} from "@medusajs/framework/utils"
import { StepResponse, createStep } from "@medusajs/framework/workflows-sdk"

import { Modules } from "@medusajs/framework/utils"

export const upsertStockLocationAddressesStepId =
"upsert-stock-location-addresses-step"
/**
* This step upserts stock location addresses matching the specified filters.
*/
export const upsertStockLocationAddressesStep = createStep(
upsertStockLocationAddressesStepId,
async (input: UpsertStockLocationAddressInput[], { container }) => {
const stockLocationService = container.resolve<IStockLocationService>(
Modules.STOCK_LOCATION
)

const stockLocationAddressIds = input.map((i) => i.id!).filter(Boolean)
const { selects, relations } = getSelectsAndRelationsFromObjectArray(input)

const dataToUpdate = await stockLocationService.listStockLocationAddresses(
{ id: stockLocationAddressIds },
{ select: selects, relations }
)

const updateIds = dataToUpdate.map((du) => du.id)

const updatedAddresses =
await stockLocationService.upsertStockLocationAddresses(input)

const dataToDelete = updatedAddresses.filter(
(address) => !updateIds.includes(address.id)
)

return new StepResponse(updatedAddresses, { dataToUpdate, dataToDelete })
},
async (revertData, { container }) => {
if (!revertData) {
return
}

const stockLocationService = container.resolve<IStockLocationService>(
Modules.STOCK_LOCATION
)

const promises: any[] = []

if (revertData.dataToDelete) {
promises.push(
stockLocationService.deleteStockLocationAddresses(
revertData.dataToDelete.map((d) => d.id!)
)
)
}

if (revertData.dataToUpdate) {
promises.push(
stockLocationService.upsertStockLocationAddresses(
revertData.dataToUpdate
)
)
}

await promiseAll(promises)
}
)
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
import {
FilterableStockLocationProps,
StockLocationDTO,
UpdateStockLocationInput,
FilterableStockLocationProps,
UpsertStockLocationAddressInput,
} from "@medusajs/framework/types"
import {
WorkflowData,
WorkflowResponse,
createWorkflow,
transform,
} from "@medusajs/framework/workflows-sdk"

import { useQueryGraphStep } from "../../common"
import { updateStockLocationsStep } from "../steps"
import { upsertStockLocationAddressesStep } from "../steps/upsert-stock-location-addresses"

export interface UpdateStockLocationsWorkflowInput {
selector: FilterableStockLocationProps
Expand All @@ -24,6 +28,50 @@ export const updateStockLocationsWorkflow = createWorkflow(
(
input: WorkflowData<UpdateStockLocationsWorkflowInput>
): WorkflowResponse<StockLocationDTO[]> => {
return new WorkflowResponse(updateStockLocationsStep(input))
const stockLocationsQuery = useQueryGraphStep({
entity: "stock_location",
filters: input.selector,
fields: ["id", "address.id"],
}).config({ name: "get-stock-location" })

const stockLocations = transform(
{ stockLocationsQuery },
({ stockLocationsQuery }) => stockLocationsQuery.data
)

const normalizedData = transform(
{ input, stockLocations },
({ input, stockLocations }) => {
const { address, address_id, ...stockLocationInput } = input.update
const addressesInput: UpsertStockLocationAddressInput[] = []

if (address) {
for (const stockLocation of stockLocations) {
if (stockLocation.address?.id) {
addressesInput.push({
id: stockLocation.address?.id!,
...address,
})
} else {
addressesInput.push(address)
}
}
}

return {
stockLocationInput: {
selector: input.selector,
update: stockLocationInput,
},
addressesInput,
}
}
)

upsertStockLocationAddressesStep(normalizedData.addressesInput)

return new WorkflowResponse(
updateStockLocationsStep(normalizedData.stockLocationInput)
)
}
)
16 changes: 16 additions & 0 deletions packages/core/types/src/stock-location/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -452,3 +452,19 @@ export type UpsertStockLocationInput = Partial<UpdateStockLocationInput> & {
*/
id?: string
}

export type UpdateStockLocationAddressInput = StockLocationAddressInput & {
id: string
}

export type UpsertStockLocationAddressInput = StockLocationAddressInput & {
id?: string
}

export interface FilterableStockLocationAddressProps
extends BaseFilterable<FilterableStockLocationAddressProps> {
/**
* The IDs to filter stock location's address by.
*/
id?: string | string[]
}
57 changes: 53 additions & 4 deletions packages/core/types/src/stock-location/service.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import { FindConfig } from "../common/common"
import { RestoreReturn, SoftDeleteReturn } from "../dal"
import { IModuleService } from "../modules-sdk"
import { Context } from "../shared-context"
import {
CreateStockLocationInput,
FilterableStockLocationAddressProps,
FilterableStockLocationProps,
StockLocationAddressDTO,
StockLocationDTO,
UpdateStockLocationInput,
UpsertStockLocationAddressInput,
UpsertStockLocationInput,
} from "./common"
import { RestoreReturn, SoftDeleteReturn } from "../dal"
import { Context } from "../shared-context"
import { FindConfig } from "../common/common"
import { IModuleService } from "../modules-sdk"

/**
* The main service interface for the Stock Location Module.
Expand Down Expand Up @@ -333,4 +336,50 @@ export interface IStockLocationService extends IModuleService {
config?: RestoreReturn<TReturnableLinkableKeys>,
sharedContext?: Context
): Promise<Record<string, string[]> | void>

/**
* This method retrieves a paginated list of stock location addresses based on optional filters and configuration.
*
* @param {FilterableStockLocationAddressProps} selector - The filters to apply on the retrieved stock location address.
* @param {FindConfig<StockLocationAddressDTO>} config - The configurations determining how the stock location address is retrieved. Its properties, such as `select` or `relations`, accept the
* attributes or relations associated with a stock location address.
* @param {Context} context - A context used to share resources, such as transaction manager, between the application and the module.
* @returns {Promise<StockLocationAddressDTO[]>} The list of stock location addressess.
*
*/
listStockLocationAddresses(
selector: FilterableStockLocationAddressProps,
config?: FindConfig<StockLocationAddressDTO>,
context?: Context
): Promise<StockLocationAddressDTO[]>

/**
* This method updates or creates stock location addresses
*
* @param {Partial<UpsertStockLocationAddressInput>[]} data - The list of Make all properties in t optional
* @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module.
* @returns {Promise<StockLocationAddressDTO[]>} The created or updated stock location address
*
* @example
* {example-code}
*/
upsertStockLocationAddresses(
data: UpsertStockLocationAddressInput[],
sharedContext?: Context
): Promise<StockLocationAddressDTO[]>

/**
* This method deletes a stock location address by its ID.
*
* @param {string} id - The ID of the stock location address.
* @param {Context} context - A context used to share resources, such as transaction manager, between the application and the module.
* @returns {Promise<void>} Resolves when the stock location address is deleted successfully.
*
* @example
* await stockLocationModuleService.deleteStockLocationAddresses("sla_123")
*/
deleteStockLocationAddresses(
id: string | string[],
context?: Context
): Promise<void>
}
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,15 @@
"name": "stock_location",
"schema": "public",
"indexes": [
{
"columnNames": [
"address_id"
],
"composite": false,
"keyName": "stock_location_address_id_unique",
"primary": false,
"unique": true
},
{
"keyName": "IDX_stock_location_address_id",
"columnNames": [],
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Migration } from "@mikro-orm/migrations"

export class Migration20250106142624 extends Migration {
async up(): Promise<void> {
this.addSql(
'alter table if exists "stock_location" add constraint "stock_location_address_id_unique" unique ("address_id");'
)
}

async down(): Promise<void> {
this.addSql(
'alter table if exists "stock_location" drop constraint if exists "stock_location_address_id_unique";'
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const StockLocationAddress = model
province: model.text().nullable(),
postal_code: model.text().nullable(),
metadata: model.json().nullable(),
stock_locations: model.hasMany(() => StockLocation, {
stock_locations: model.hasOne(() => StockLocation, {
mappedBy: "address",
}),
})
Expand Down
Loading
Loading