Skip to content

Commit

Permalink
Merge branch 'develop' into fix/inventory-ui-fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
srindom authored Mar 14, 2023
2 parents c4edb72 + 55a1f23 commit 52912c5
Show file tree
Hide file tree
Showing 8 changed files with 261 additions and 39 deletions.
5 changes: 5 additions & 0 deletions .changeset/swift-deers-peel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@medusajs/modules-sdk": patch
---

chore: Add missing changeset for @medusajs/modules-sdk
6 changes: 6 additions & 0 deletions .changeset/weak-hats-develop.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@medusajs/admin-ui": patch
"@medusajs/medusa": patch
---

feat(medusa,admin-ui): support location_id in
59 changes: 59 additions & 0 deletions integration-tests/plugins/__tests__/inventory/order/order.js
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,65 @@ describe("/store/carts", () => {
)
})

it("increases stocked quantity when return is received at location", async () => {
const api = useApi()

const fulfillmentRes = await api.post(
`/admin/orders/${order.id}/fulfillment`,
{
items: [{ item_id: lineItemId, quantity: 1 }],
location_id: locationId,
},
adminHeaders
)

const shipmentRes = await api.post(
`/admin/orders/${order.id}/shipment`,
{
fulfillment_id: fulfillmentRes.data.order.fulfillments[0].id,
},
adminHeaders
)

expect(shipmentRes.status).toBe(200)

let inventoryItem = await api.get(
`/admin/inventory-items/${invItemId}`,
adminHeaders
)

expect(inventoryItem.data.inventory_item.location_levels[0]).toEqual(
expect.objectContaining({
stocked_quantity: 0,
reserved_quantity: 0,
available_quantity: 0,
})
)

const requestReturnRes = await api.post(
`/admin/orders/${order.id}/return`,
{
receive_now: true,
location_id: locationId,
items: [{ item_id: lineItemId, quantity: 1 }],
},
adminHeaders
)

expect(requestReturnRes.status).toBe(200)
inventoryItem = await api.get(
`/admin/inventory-items/${invItemId}`,
adminHeaders
)
expect(inventoryItem.data.inventory_item.location_levels[0]).toEqual(
expect.objectContaining({
stocked_quantity: 1,
reserved_quantity: 0,
available_quantity: 1,
})
)
})

it("adjusts inventory levels on successful fulfillment without reservation", async () => {
const api = useApi()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export type SelectOption<T> = {

type MultiSelectProps = InputHeaderProps & {
// component props
label: string
label?: string
required?: boolean
name?: string
className?: string
Expand Down
141 changes: 111 additions & 30 deletions packages/admin-ui/ui/src/domain/orders/details/receive-return/index.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
import { Order, Return } from "@medusajs/medusa"
import React from "react"
import {
AdminPostReturnsReturnReceiveReq,
Order,
Return,
StockLocationDTO,
} from "@medusajs/medusa"
import { useAdminOrder, useAdminReceiveReturn } from "medusa-react"
import { useEffect, useMemo } from "react"
import { useForm } from "react-hook-form"
import Button from "../../../../components/fundamentals/button"
import Modal from "../../../../components/molecules/modal"
import useNotification from "../../../../hooks/use-notification"
import { useFeatureFlag } from "../../../../providers/feature-flag-provider"
import { getErrorMessage } from "../../../../utils/error-messages"
import { nestedForm } from "../../../../utils/nested-form"
import { ItemsToReceiveFormType } from "../../components/items-to-receive-form"
Expand All @@ -13,6 +20,9 @@ import { RefundAmountFormType } from "../../components/refund-amount-form"
import { ReceiveReturnSummary } from "../../components/rma-summaries/receive-return-summary"
import { getDefaultReceiveReturnValues } from "../utils/get-default-values"
import useOrdersExpandParam from "../utils/use-admin-expand-paramter"
import { useAdminStockLocations } from "medusa-react"
import Select from "../../../../components/molecules/select/next-select/select"
import Spinner from "../../../../components/atoms/spinner"

type Props = {
order: Order
Expand All @@ -26,12 +36,53 @@ export type ReceiveReturnFormType = {
}

export const ReceiveReturnMenu = ({ order, returnRequest, onClose }: Props) => {
const { isFeatureEnabled } = useFeatureFlag()
const isLocationFulfillmentEnabled =
isFeatureEnabled("inventoryService") &&
isFeatureEnabled("stockLocationService")

const { mutate, isLoading } = useAdminReceiveReturn(returnRequest.id)
const { orderRelations } = useOrdersExpandParam()
const { refetch } = useAdminOrder(order.id, {
expand: orderRelations,
})

const {
stock_locations,
refetch: refetchLocations,
isLoading: isLoadingLocations,
} = useAdminStockLocations(
{},
{
enabled: isLocationFulfillmentEnabled,
}
)

React.useEffect(() => {
if (isLocationFulfillmentEnabled) {
refetchLocations()
}
}, [isLocationFulfillmentEnabled, refetchLocations])

const [selectedLocation, setSelectedLocation] = React.useState<{
value: string
label: string
} | null>(null)

useEffect(() => {
if (isLocationFulfillmentEnabled && stock_locations?.length) {
const location = stock_locations.find(
(sl: StockLocationDTO) => sl.id === returnRequest.location_id
)
if (location) {
setSelectedLocation({
value: location.id,
label: location.name,
})
}
}
}, [isLocationFulfillmentEnabled, stock_locations, returnRequest.location_id])

/**
* If the return was refunded as part of a refund claim, we do not allow the user to
* specify a refund amount, or want to display a summary.
Expand Down Expand Up @@ -104,36 +155,39 @@ export const ReceiveReturnMenu = ({ order, returnRequest, onClose }: Props) => {
refundAmount = 0
}

mutate(
{
items: data.receive_items.items.map((i) => ({
item_id: i.item_id,
quantity: i.quantity,
})),
refund: refundAmount,
const toCreate: AdminPostReturnsReturnReceiveReq = {
items: data.receive_items.items.map((i) => ({
item_id: i.item_id,
quantity: i.quantity,
})),
refund: refundAmount,
}

if (selectedLocation && isLocationFulfillmentEnabled) {
toCreate.location_id = selectedLocation.value
}

mutate(toCreate, {
onSuccess: () => {
notification(
"Successfully received return",
`Received return for order #${order.display_id}`,
"success"
)

// We need to refetch the order to get the updated state
refetch()

onClose()
},
{
onSuccess: () => {
notification(
"Successfully received return",
`Received return for order #${order.display_id}`,
"success"
)

// We need to refetch the order to get the updated state
refetch()

onClose()
},
onError: (error) => {
notification(
"Failed to receive return",
getErrorMessage(error),
"error"
)
},
}
)
onError: (error) => {
notification(
"Failed to receive return",
getErrorMessage(error),
"error"
)
},
})
})

return (
Expand All @@ -149,6 +203,33 @@ export const ReceiveReturnMenu = ({ order, returnRequest, onClose }: Props) => {
order={order}
form={nestedForm(form, "receive_items")}
/>

{isLocationFulfillmentEnabled && (
<div className="mb-8">
<h3 className="inter-base-semibold ">Location</h3>
<p className="inter-base-regular text-grey-50">
Choose which location you want to return the items to.
</p>
{isLoadingLocations ? (
<Spinner />
) : (
<Select
className="mt-2"
placeholder="Select Location to Return to"
value={selectedLocation}
isMulti={false}
onChange={setSelectedLocation}
options={
stock_locations?.map((sl: StockLocationDTO) => ({
label: sl.name,
value: sl.id,
})) || []
}
/>
)}
</div>
)}

{!isSwapOrRefundedClaim && (
<ReceiveReturnSummary
form={form}
Expand Down
Loading

0 comments on commit 52912c5

Please sign in to comment.