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(inventory, stock-location): Remove orphaned location levels and reservations #3460

Merged

Conversation

pKorsholm
Copy link
Contributor

@pKorsholm pKorsholm commented Mar 13, 2023

What

  • Remove related inventory levels and reservation items when a stock location is removed

How

  • Add bulk deletion methods for both inventory levels and reservation items to the inventory service api
  • invoke both on location removal

Fixes CORE-1232

@pKorsholm pKorsholm requested a review from a team as a code owner March 13, 2023 10:34
@changeset-bot
Copy link

changeset-bot bot commented Mar 13, 2023

🦋 Changeset detected

Latest commit: a839372

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
@medusajs/inventory Patch
@medusajs/medusa Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@pKorsholm pKorsholm requested a review from srindom March 13, 2023 10:34
@vercel
Copy link

vercel bot commented Mar 13, 2023

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated
medusa-docs ✅ Ready (Inspect) Visit Preview 💬 Add your feedback Mar 15, 2023 at 8:57AM (UTC)

@@ -21,6 +21,7 @@ export default class InventoryLevelService extends TransactionBaseService {
CREATED: "inventory-level.created",
UPDATED: "inventory-level.updated",
DELETED: "inventory-level.deleted",
DELETED_BY_LOCATION: "inventory-level.deleted-by-location",
Copy link
Collaborator

Choose a reason for hiding this comment

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

suggestion: rename to BULK_DELETED and allow the event data to be dynamic based on the query. I.e.: Event data could be: { location_id: ... } but { inventory_item_id: ... } would also be valid.

This way we don't have to create DELETED_BY_X for every deletion dimension. People who want to build synchronization could simply listen to DELETED and BULK_DELETED events.

@adrien2p, @olivermrbl - curious to hear your thoughts on this too?

Copy link
Member

Choose a reason for hiding this comment

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

I would say that we reuse the delete event name and pass the location_id, if the user listen to delete, there is either an id or a other key. If there is an id the user will retrieve the id, if there is something else the user will list by. Does it make sense?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Could you give an example maybe? :)

Copy link
Member

@adrien2p adrien2p Mar 13, 2023

Choose a reason for hiding this comment

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

something like that

// delete by location id

await this.eventBusService_.emit(InventoryLevel.Events.DELETED, {
  location_id
})

// delete by id

await this.eventBusService_.emit(InventoryLevel.Events.DELETED, {
  id
})


eventBusService_.subscribe(InventoryLevel.Events.DELETED, (eventName, data) => {
  if (data.id) {
    return await this.handleInventoryLevelDeleteById(data.id)
  }
  
  return await this.handleInventoryLevelDelete(data)
})

async function handleInventoryLevelDeleteById(id) {
  // do something
}

async function handleInventoryLevelDelete(selector) {
  // do something
  // this.inventoryLevelService_.list(selector, { withDeleted: true })
  // Or just use the selector for something else
}

I splitted it for the example, but the id is also a selector in fact

Copy link
Collaborator

Choose a reason for hiding this comment

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

I would be fine with that too -- would just have to be documented.

Copy link
Member

Choose a reason for hiding this comment

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

Eventually, second options is

const { raw } = levelRepository.delete({ location_id: locationId })
const events = raw.map(level => ({ eventName: InventoryLevel.Events.DELETED, data: { id: level.id })
await this.eventBusService_.withTransaction(manager).emit(events)

Wdyt @srindom ?

Copy link
Member

Choose a reason for hiding this comment

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

For the later, it require to merge master in develop :D

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Going for the pure deleted event, where do you want it documented @srindom?

Copy link
Collaborator

Choose a reason for hiding this comment

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

cc: @shahednasser, can we document this behavior in the events reference :)

Copy link
Member

@shahednasser shahednasser Mar 14, 2023

Choose a reason for hiding this comment

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

@srindom sure, once it's published I'll include this behavior in the events reference (creating a linear issue for it) 👍🏻

packages/inventory/src/services/reservation-item.ts Outdated Show resolved Hide resolved
Copy link
Collaborator

@srindom srindom left a comment

Choose a reason for hiding this comment

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

Please add a test to ensure that this logic works correctly :)

Copy link
Collaborator

@srindom srindom left a comment

Choose a reason for hiding this comment

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

LGTM!

@@ -37,7 +37,7 @@ const LocationCard: React.FC<Props> = ({ location }) => {
const onDelete = async () => {
const shouldDelete = await dialog({
heading: "Delete Location",
text: "Are you sure you want to delete this location",
text: "Are you sure you want to delete this location. This will also delete all inventory levels and reservations associated with this location.",
Copy link
Collaborator

Choose a reason for hiding this comment

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

praise: great UX addition!

@srindom
Copy link
Collaborator

srindom commented Mar 14, 2023

@pKorsholm - can you check up on the failing test :)

@srindom
Copy link
Collaborator

srindom commented Mar 14, 2023

The integration problem feels like a missing await - can't find any though; could it be because we don't do return await in delete functions?

@adrien2p
Copy link
Member

adrien2p commented Mar 15, 2023

The integration problem feels like a missing await - can't find any though; could it be because we don't do return await in delete functions?

on Master I had the same issue and it was due to missing transaction during the emit, might be worth looking at the diff with master. I think that the issue comes from the fact that the plugin integration use a mock of redis and might not support bulk

@pKorsholm
Copy link
Contributor Author

Clutch @adrien2p! Thanks, that was the issue 😅

@kodiakhq kodiakhq bot deleted the fix/remove-orphaned-location-levels-and-reservations branch March 15, 2023 09:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants