From d285e609611b7e5491e16f2257092d7369a1db39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Frane=20Poli=C4=87?= <16856471+fPolic@users.noreply.github.com> Date: Thu, 6 Jun 2024 08:58:21 +0200 Subject: [PATCH] feat(dashboard,core-flows,medusa): update fulfillment flows (#7589) * fix: fulfillment ops * fix: cancel fulfillment route * fix: adjustInventoryLevelsStep throwing * feat: cancel order and fix endpoint * fix: type * feat: order domain sdk * feat: delete unused file * fix: import --- .../fulfillment-status-cell.tsx | 4 +- .../dashboard/src/hooks/api/orders.ts | 40 ------ .../dashboard/src/hooks/api/orders.tsx | 101 ++++++++++++++ .../hooks/api/{payments.ts => payments.tsx} | 0 ...ipping-options.ts => shipping-options.tsx} | 0 .../dashboard/src/i18n/translations/en.json | 2 + .../dashboard/src/lib/client/client.ts | 1 - .../dashboard/src/lib/client/fulfillments.ts | 8 ++ .../dashboard/src/lib/client/orders.ts | 15 -- .../edit-shipping-options-pricing-form.tsx | 20 +-- .../order-create-fulfillment-form.tsx | 130 ++++++------------ .../order-fulfillment-section.tsx | 6 +- .../order-general-section.tsx | 21 ++- .../routes/orders/order-detail/constants.ts | 1 - .../src/routes/orders/order-list/const.ts | 1 - .../src/order/steps/cancel-fulfillment.ts | 2 +- .../workflows/cancel-order-fulfillment.ts | 13 +- packages/core/js-sdk/src/admin/index.ts | 3 + packages/core/js-sdk/src/admin/order.ts | 81 +++++++++++ packages/core/types/src/http/order/admin.ts | 13 ++ packages/core/types/src/order/common.ts | 4 +- .../src/api/admin/orders/[id]/cancel/route.ts | 2 +- .../[fulfillment_id]/cancel/route.ts | 1 + .../medusa/src/api/admin/orders/[id]/route.ts | 4 +- .../src/api/admin/orders/middlewares.ts | 5 +- .../medusa/src/api/admin/orders/validators.ts | 1 - 26 files changed, 296 insertions(+), 183 deletions(-) delete mode 100644 packages/admin-next/dashboard/src/hooks/api/orders.ts create mode 100644 packages/admin-next/dashboard/src/hooks/api/orders.tsx rename packages/admin-next/dashboard/src/hooks/api/{payments.ts => payments.tsx} (100%) rename packages/admin-next/dashboard/src/hooks/api/{shipping-options.ts => shipping-options.tsx} (100%) delete mode 100644 packages/admin-next/dashboard/src/lib/client/orders.ts create mode 100644 packages/core/js-sdk/src/admin/order.ts diff --git a/packages/admin-next/dashboard/src/components/table/table-cells/order/fulfillment-status-cell/fulfillment-status-cell.tsx b/packages/admin-next/dashboard/src/components/table/table-cells/order/fulfillment-status-cell/fulfillment-status-cell.tsx index ad2a983019..8c767cb816 100644 --- a/packages/admin-next/dashboard/src/components/table/table-cells/order/fulfillment-status-cell/fulfillment-status-cell.tsx +++ b/packages/admin-next/dashboard/src/components/table/table-cells/order/fulfillment-status-cell/fulfillment-status-cell.tsx @@ -1,5 +1,7 @@ -import type { FulfillmentStatus } from "@medusajs/medusa" import { useTranslation } from "react-i18next" + +import { FulfillmentStatus } from "@medusajs/types" + import { getOrderFulfillmentStatus } from "../../../../../lib/order-helpers" import { StatusCell } from "../../common/status-cell" diff --git a/packages/admin-next/dashboard/src/hooks/api/orders.ts b/packages/admin-next/dashboard/src/hooks/api/orders.ts deleted file mode 100644 index b93e844cc0..0000000000 --- a/packages/admin-next/dashboard/src/hooks/api/orders.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { QueryKey, useQuery, UseQueryOptions } from "@tanstack/react-query" - -import { client } from "../../lib/client" -import { queryKeysFactory } from "../../lib/query-key-factory" - -const ORDERS_QUERY_KEY = "orders" as const -export const ordersQueryKeys = queryKeysFactory(ORDERS_QUERY_KEY) - -export const useOrder = ( - id: string, - query?: Record, - options?: Omit< - UseQueryOptions, - "queryFn" | "queryKey" - > -) => { - const { data, ...rest } = useQuery({ - queryFn: async () => client.orders.retrieve(id, query), - queryKey: ordersQueryKeys.detail(id, query), - ...options, - }) - - return { ...data, ...rest } -} - -export const useOrders = ( - query?: Record, - options?: Omit< - UseQueryOptions, - "queryFn" | "queryKey" - > -) => { - const { data, ...rest } = useQuery({ - queryFn: async () => client.orders.list(query), - queryKey: ordersQueryKeys.list(query), - ...options, - }) - - return { ...data, ...rest } -} diff --git a/packages/admin-next/dashboard/src/hooks/api/orders.tsx b/packages/admin-next/dashboard/src/hooks/api/orders.tsx new file mode 100644 index 0000000000..a8a1e54fbc --- /dev/null +++ b/packages/admin-next/dashboard/src/hooks/api/orders.tsx @@ -0,0 +1,101 @@ +import { + QueryKey, + useMutation, + UseMutationOptions, + useQuery, + UseQueryOptions, +} from "@tanstack/react-query" + +import { queryClient } from "../../lib/query-client" +import { queryKeysFactory } from "../../lib/query-key-factory" +import { sdk } from "../../lib/client" + +const ORDERS_QUERY_KEY = "orders" as const +export const ordersQueryKeys = queryKeysFactory(ORDERS_QUERY_KEY) + +export const useOrder = ( + id: string, + query?: Record, + options?: Omit< + UseQueryOptions, + "queryFn" | "queryKey" + > +) => { + const { data, ...rest } = useQuery({ + queryFn: async () => sdk.admin.order.retrieve(id, query), + queryKey: ordersQueryKeys.detail(id, query), + ...options, + }) + + return { ...data, ...rest } +} + +export const useOrders = ( + query?: Record, + options?: Omit< + UseQueryOptions, + "queryFn" | "queryKey" + > +) => { + const { data, ...rest } = useQuery({ + queryFn: async () => sdk.admin.order.list(query), + queryKey: ordersQueryKeys.list(query), + ...options, + }) + + return { ...data, ...rest } +} + +export const useCreateOrderFulfillment = ( + orderId: string, + options?: UseMutationOptions +) => { + return useMutation({ + mutationFn: (payload: any) => + sdk.admin.order.createFulfillment(orderId, payload), + onSuccess: (data: any, variables: any, context: any) => { + queryClient.invalidateQueries({ + queryKey: ordersQueryKeys.details(), + }) + options?.onSuccess?.(data, variables, context) + }, + ...options, + }) +} + +export const useCancelOrderFulfillment = ( + orderId: string, + fulfillmentId: string, + options?: UseMutationOptions +) => { + return useMutation({ + mutationFn: (payload: { no_notification?: boolean }) => + sdk.admin.order.cancelFulfillment(orderId, fulfillmentId, payload), + onSuccess: (data: any, variables: any, context: any) => { + queryClient.invalidateQueries({ + queryKey: ordersQueryKeys.details(), + }) + options?.onSuccess?.(data, variables, context) + }, + ...options, + }) +} + +export const useCancelOrder = ( + orderId: string, + options?: UseMutationOptions +) => { + return useMutation({ + mutationFn: () => sdk.admin.order.cancel(orderId), + onSuccess: (data: any, variables: any, context: any) => { + queryClient.invalidateQueries({ + queryKey: ordersQueryKeys.details(), + }) + queryClient.invalidateQueries({ + queryKey: ordersQueryKeys.lists(), + }) + options?.onSuccess?.(data, variables, context) + }, + ...options, + }) +} diff --git a/packages/admin-next/dashboard/src/hooks/api/payments.ts b/packages/admin-next/dashboard/src/hooks/api/payments.tsx similarity index 100% rename from packages/admin-next/dashboard/src/hooks/api/payments.ts rename to packages/admin-next/dashboard/src/hooks/api/payments.tsx diff --git a/packages/admin-next/dashboard/src/hooks/api/shipping-options.ts b/packages/admin-next/dashboard/src/hooks/api/shipping-options.tsx similarity index 100% rename from packages/admin-next/dashboard/src/hooks/api/shipping-options.ts rename to packages/admin-next/dashboard/src/hooks/api/shipping-options.tsx diff --git a/packages/admin-next/dashboard/src/i18n/translations/en.json b/packages/admin-next/dashboard/src/i18n/translations/en.json index f875fec67c..0343312168 100644 --- a/packages/admin-next/dashboard/src/i18n/translations/en.json +++ b/packages/admin-next/dashboard/src/i18n/translations/en.json @@ -76,6 +76,7 @@ "back": "Back", "close": "Close", "continue": "Continue", + "reset": "Reset", "confirm": "Confirm", "edit": "Edit", "download": "Download", @@ -572,6 +573,7 @@ "inStock": "In stock", "itemsToFulfillDesc": "Choose items and quantities to fulfill", "locationDescription": "Choose which location you want to fulfill items from.", + "sendNotificationHint": "Notify customers about the created fulfillment.", "error": { "wrongQuantity": "Only one item is available for fulfillment", "wrongQuantity_other": "Quantity should be a number between 1 and {{number}}", diff --git a/packages/admin-next/dashboard/src/lib/client/client.ts b/packages/admin-next/dashboard/src/lib/client/client.ts index 0362707631..7b4e62194e 100644 --- a/packages/admin-next/dashboard/src/lib/client/client.ts +++ b/packages/admin-next/dashboard/src/lib/client/client.ts @@ -8,7 +8,6 @@ import { fulfillmentProviders } from "./fulfillment-providers" import { fulfillments } from "./fulfillments" import { inventoryItems } from "./inventory" import { invites } from "./invites" -import { orders } from "./orders" import { payments } from "./payments" import { priceLists } from "./price-lists" import { productTypes } from "./product-types" diff --git a/packages/admin-next/dashboard/src/lib/client/fulfillments.ts b/packages/admin-next/dashboard/src/lib/client/fulfillments.ts index 1751a2a159..9bbaa8c4b4 100644 --- a/packages/admin-next/dashboard/src/lib/client/fulfillments.ts +++ b/packages/admin-next/dashboard/src/lib/client/fulfillments.ts @@ -3,10 +3,18 @@ import { CreateFulfillmentDTO } from "@medusajs/types" import { FulfillmentRes } from "../../types/api-responses" import { postRequest } from "./common" +/** + * Create a fulfillment. + * NOTE: to create a fulfillment on an order use orders/:id/fulfillments endpoint + */ async function createFulfillment(payload: CreateFulfillmentDTO) { return postRequest(`/admin/fulfillments`, payload) } +/** + * Cancel a fulfillment. + * NOTE: to cancel a fulfillment on an order use orders/:id/fulfillments/:id/cancel endpoint + */ async function cancelFulfillment(id: string) { return postRequest(`/admin/fulfillments/${id}/cancel`) } diff --git a/packages/admin-next/dashboard/src/lib/client/orders.ts b/packages/admin-next/dashboard/src/lib/client/orders.ts deleted file mode 100644 index 75d32a37c4..0000000000 --- a/packages/admin-next/dashboard/src/lib/client/orders.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { getRequest } from "./common" -import { OrderListRes, OrderRes } from "../../types/api-responses" - -async function retrieveOrder(id: string, query?: Record) { - return getRequest>(`/admin/orders/${id}`, query) -} - -async function listOrders(query?: Record) { - return getRequest>(`/admin/orders`, query) -} - -export const orders = { - list: listOrders, - retrieve: retrieveOrder, -} diff --git a/packages/admin-next/dashboard/src/routes/locations/shipping-options-edit-pricing/components/create-shipping-options-form/edit-shipping-options-pricing-form.tsx b/packages/admin-next/dashboard/src/routes/locations/shipping-options-edit-pricing/components/create-shipping-options-form/edit-shipping-options-pricing-form.tsx index e19a600df1..5a6acf5905 100644 --- a/packages/admin-next/dashboard/src/routes/locations/shipping-options-edit-pricing/components/create-shipping-options-form/edit-shipping-options-pricing-form.tsx +++ b/packages/admin-next/dashboard/src/routes/locations/shipping-options-edit-pricing/components/create-shipping-options-form/edit-shipping-options-pricing-form.tsx @@ -14,22 +14,22 @@ import { useTranslation } from "react-i18next" import { HttpTypes } from "@medusajs/types" import { ColumnDef, createColumnHelper } from "@tanstack/react-table" -import { DataGrid } from "../../../../../components/grid/data-grid/index.ts" -import { CurrencyCell } from "../../../../../components/grid/grid-cells/common/currency-cell/index.ts" -import { DataGridMeta } from "../../../../../components/grid/types.ts" +import { DataGrid } from "../../../../../components/grid/data-grid/index" +import { CurrencyCell } from "../../../../../components/grid/grid-cells/common/currency-cell/index" +import { DataGridMeta } from "../../../../../components/grid/types" import { RouteFocusModal, useRouteModal, -} from "../../../../../components/route-modal/index.ts" -import { useCurrencies } from "../../../../../hooks/api/currencies.tsx" -import { useRegions } from "../../../../../hooks/api/regions.tsx" -import { useUpdateShippingOptions } from "../../../../../hooks/api/shipping-options.ts" -import { useStore } from "../../../../../hooks/api/store.tsx" +} from "../../../../../components/route-modal/index" +import { useCurrencies } from "../../../../../hooks/api/currencies" +import { useRegions } from "../../../../../hooks/api/regions" +import { useUpdateShippingOptions } from "../../../../../hooks/api/shipping-options" +import { useStore } from "../../../../../hooks/api/store" import { getDbAmount, getPresentationalAmount, -} from "../../../../../lib/money-amount-helpers.ts" -import { ExtendedProductDTO } from "../../../../../types/api-responses.ts" +} from "../../../../../lib/money-amount-helpers" +import { ExtendedProductDTO } from "../../../../../types/api-responses" const getInitialCurrencyPrices = (prices: PriceDTO[]) => { const ret: Record = {} diff --git a/packages/admin-next/dashboard/src/routes/orders/order-create-fulfillment/components/order-create-fulfillment-form/order-create-fulfillment-form.tsx b/packages/admin-next/dashboard/src/routes/orders/order-create-fulfillment/components/order-create-fulfillment-form/order-create-fulfillment-form.tsx index d3d5a9c5f3..2870f13e1a 100644 --- a/packages/admin-next/dashboard/src/routes/orders/order-create-fulfillment/components/order-create-fulfillment-form/order-create-fulfillment-form.tsx +++ b/packages/admin-next/dashboard/src/routes/orders/order-create-fulfillment/components/order-create-fulfillment-form/order-create-fulfillment-form.tsx @@ -4,7 +4,7 @@ import { useTranslation } from "react-i18next" import * as zod from "zod" import { AdminOrder } from "@medusajs/types" -import { Alert, Button, Select, toast } from "@medusajs/ui" +import { Alert, Button, Select, Switch, toast } from "@medusajs/ui" import { useForm, useWatch } from "react-hook-form" import { Form } from "../../../../../components/common/form" @@ -12,13 +12,12 @@ import { RouteFocusModal, useRouteModal, } from "../../../../../components/route-modal" -import { useCreateFulfillment } from "../../../../../hooks/api/fulfillment" import { useFulfillmentProviders } from "../../../../../hooks/api/fulfillment-providers" import { useStockLocations } from "../../../../../hooks/api/stock-locations" -import { cleanNonValues, pick } from "../../../../../lib/common" import { getFulfillableQuantity } from "../../../../../lib/order-item" -import { CreateFulfillmentSchema } from "./constants" import { OrderCreateFulfillmentItem } from "./order-create-fulfillment-item" +import { useCreateOrderFulfillment } from "../../../../../hooks/api/orders" +import { CreateFulfillmentSchema } from "./constants" type OrderCreateFulfillmentFormProps = { order: AdminOrder @@ -30,8 +29,8 @@ export function OrderCreateFulfillmentForm({ const { t } = useTranslation() const { handleSuccess } = useRouteModal() - const { mutateAsync: createOrderFulfillment, isLoading: isMutating } = - useCreateFulfillment() + const { mutateAsync: createOrderFulfillment, isPending: isMutating } = + useCreateOrderFulfillment(order.id) const { fulfillment_providers } = useFulfillmentProviders({ region_id: order.region_id, @@ -43,14 +42,11 @@ export function OrderCreateFulfillmentForm({ const form = useForm>({ defaultValues: { - quantity: fulfillableItems.reduce( - (acc, item) => { - acc[item.id] = getFulfillableQuantity(item) - return acc - }, - {} as Record - ), - // send_notification: !order.no_notification, + quantity: fulfillableItems.reduce((acc, item) => { + acc[item.id] = getFulfillableQuantity(item) + return acc + }, {} as Record), + send_notification: !order.no_notification, }, resolver: zodResolver(CreateFulfillmentSchema), }) @@ -61,49 +57,13 @@ export function OrderCreateFulfillmentForm({ try { await createOrderFulfillment({ location_id: data.location_id, - /** - * TODO: send notification flag - */ - // no_notification: !data.send_notification, - delivery_address: cleanNonValues( - pick(order.shipping_address, [ - "first_name", - "last_name", - "phone", - "company", - "address_1", - "address_2", - "city", - "country_code", - "province", - "postal_code", - "metadata", - ]) - ), // TODO: this should be pulled from order in the workflow - provider_id: fulfillment_providers[0]?.id, + no_notification: !data.send_notification, items: Object.entries(data.quantity) .filter(([, value]) => !!value) - .map(([item_id, quantity]) => { - const item = order.items.find((i) => i.id === item_id) - - return { - quantity, - line_item_id: item_id, - title: item.title, - barcode: item.variant.barcode || "", - sku: item.variant_sku || "", - } - }), - // TODO: should be optional in the enpoint? - labels: [ - { - tracking_number: "TODO", - tracking_url: "TODO", - label_url: "TODO", - }, - ], - order: {}, // TODO ? - order_id: order.id, // TEMP link for now + .map(([id, quantity]) => ({ + id, + quantity, + })), }) handleSuccess(`/orders/${order.id}`) @@ -252,36 +212,36 @@ export function OrderCreateFulfillmentForm({ )} - {/*
*/} - {/* {*/} - {/* return (*/} - {/* */} - {/*
*/} - {/* */} - {/* {t("orders.returns.sendNotification")}*/} - {/* */} - {/* */} - {/* */} - {/* */} - {/* */} - {/* */} - {/*
*/} - {/* */} - {/* {t("orders.returns.sendNotificationHint")}*/} - {/* */} - {/* */} - {/*
*/} - {/* )*/} - {/* }}*/} - {/* />*/} - {/*
*/} +
+ { + return ( + +
+ + {t("orders.returns.sendNotification")} + + + + + + +
+ + {t("orders.fulfillment.sendNotificationHint")} + + +
+ ) + }} + /> +
diff --git a/packages/admin-next/dashboard/src/routes/orders/order-detail/components/order-fulfillment-section/order-fulfillment-section.tsx b/packages/admin-next/dashboard/src/routes/orders/order-detail/components/order-fulfillment-section/order-fulfillment-section.tsx index 82714d8c71..88f14bfdeb 100644 --- a/packages/admin-next/dashboard/src/routes/orders/order-detail/components/order-fulfillment-section/order-fulfillment-section.tsx +++ b/packages/admin-next/dashboard/src/routes/orders/order-detail/components/order-fulfillment-section/order-fulfillment-section.tsx @@ -21,10 +21,10 @@ import { Link } from "react-router-dom" import { ActionMenu } from "../../../../../components/common/action-menu" import { Skeleton } from "../../../../../components/common/skeleton" import { Thumbnail } from "../../../../../components/common/thumbnail" -import { useCancelFulfillment } from "../../../../../hooks/api/fulfillment" import { useStockLocation } from "../../../../../hooks/api/stock-locations" import { formatProvider } from "../../../../../lib/format-provider" import { getLocaleAmount } from "../../../../../lib/money-amount-helpers" +import { useCancelOrderFulfillment } from "../../../../../hooks/api/orders" type OrderFulfillmentSectionProps = { order: AdminOrder @@ -186,7 +186,7 @@ const Fulfillment = ({ statusTimestamp = fulfillment.shipped_at } - const { mutateAsync } = useCancelFulfillment(fulfillment.id) + const { mutateAsync } = useCancelOrderFulfillment(order.id, fulfillment.id) const handleCancel = async () => { if (fulfillment.shipped_at) { @@ -206,7 +206,7 @@ const Fulfillment = ({ if (res) { try { - await mutateAsync(fulfillment.id) + await mutateAsync() toast.success(t("general.success"), { description: t("orders.fulfillment.toast.canceled"), diff --git a/packages/admin-next/dashboard/src/routes/orders/order-detail/components/order-general-section/order-general-section.tsx b/packages/admin-next/dashboard/src/routes/orders/order-detail/components/order-general-section/order-general-section.tsx index 16b6b59f76..3038cac591 100644 --- a/packages/admin-next/dashboard/src/routes/orders/order-detail/components/order-general-section/order-general-section.tsx +++ b/packages/admin-next/dashboard/src/routes/orders/order-detail/components/order-general-section/order-general-section.tsx @@ -15,6 +15,7 @@ import { getOrderFulfillmentStatus, getOrderPaymentStatus, } from "../../../../../lib/order-helpers" +import { useCancelOrder } from "../../../../../hooks/api/orders" type OrderGeneralSectionProps = { order: Order @@ -24,7 +25,7 @@ export const OrderGeneralSection = ({ order }: OrderGeneralSectionProps) => { const { t } = useTranslation() const prompt = usePrompt() - const { mutateAsync } = { mutateAsync: () => {} } // cancel order + const { mutateAsync } = useCancelOrder(order.id) const handleCancel = async () => { const res = await prompt({ @@ -40,7 +41,7 @@ export const OrderGeneralSection = ({ order }: OrderGeneralSectionProps) => { return } - await mutateAsync(undefined) + await mutateAsync() } return ( @@ -60,17 +61,18 @@ export const OrderGeneralSection = ({ order }: OrderGeneralSectionProps) => {
+ {/*TODO: SHOW ORDER STATUS INSTEAD OF FULFILLMENT STATUS HERE - if the last fulfillment is canceled it looks like the order is canceled*/}
, - // }, + { + label: t("actions.cancel"), + onClick: handleCancel, + icon: , + }, ], }, ]} @@ -83,11 +85,6 @@ export const OrderGeneralSection = ({ order }: OrderGeneralSectionProps) => { const FulfillmentBadge = ({ order }: { order: Order }) => { const { t } = useTranslation() - /** - * TODO: revisit when Order<>Fulfillment are linked - */ - return null - const { label, color } = getOrderFulfillmentStatus( t, order.fulfillment_status diff --git a/packages/admin-next/dashboard/src/routes/orders/order-detail/constants.ts b/packages/admin-next/dashboard/src/routes/orders/order-detail/constants.ts index 7e34881bd4..7e46b4e6d0 100644 --- a/packages/admin-next/dashboard/src/routes/orders/order-detail/constants.ts +++ b/packages/admin-next/dashboard/src/routes/orders/order-detail/constants.ts @@ -3,7 +3,6 @@ const DEFAULT_PROPERTIES = [ "status", "created_at", "email", - // "fulfillment_status", // -> TODO replacement for this // "payment_status", // -> TODO replacement for this "display_id", "currency_code", diff --git a/packages/admin-next/dashboard/src/routes/orders/order-list/const.ts b/packages/admin-next/dashboard/src/routes/orders/order-list/const.ts index bc6a37e659..b1d1c31b75 100644 --- a/packages/admin-next/dashboard/src/routes/orders/order-list/const.ts +++ b/packages/admin-next/dashboard/src/routes/orders/order-list/const.ts @@ -4,7 +4,6 @@ const DEFAULT_PROPERTIES = [ "created_at", "email", "display_id", - // "fulfillment_status", // -> TODO replacement for this // "payment_status", // -> TODO replacement for this "total", "currency_code", diff --git a/packages/core/core-flows/src/order/steps/cancel-fulfillment.ts b/packages/core/core-flows/src/order/steps/cancel-fulfillment.ts index 84a696069b..a9f707021e 100644 --- a/packages/core/core-flows/src/order/steps/cancel-fulfillment.ts +++ b/packages/core/core-flows/src/order/steps/cancel-fulfillment.ts @@ -4,7 +4,7 @@ import { StepResponse, createStep } from "@medusajs/workflows-sdk" type CancelOrderFulfillmentStepInput = CancelOrderFulfillmentDTO -export const cancelOrderFulfillmentStepId = "cancel-order-fullfillment" +export const cancelOrderFulfillmentStepId = "cancel-order-fulfillment" export const cancelOrderFulfillmentStep = createStep( cancelOrderFulfillmentStepId, async (data: CancelOrderFulfillmentStepInput, { container }) => { diff --git a/packages/core/core-flows/src/order/workflows/cancel-order-fulfillment.ts b/packages/core/core-flows/src/order/workflows/cancel-order-fulfillment.ts index d70ccada36..a7e258ccdd 100644 --- a/packages/core/core-flows/src/order/workflows/cancel-order-fulfillment.ts +++ b/packages/core/core-flows/src/order/workflows/cancel-order-fulfillment.ts @@ -87,11 +87,14 @@ function prepareInventoryUpdate({ }[] = [] for (const item of fulfillment.items) { - inventoryAdjustment.push({ - inventory_item_id: item.inventory_item_id as string, - location_id: fulfillment.location_id, - adjustment: item.quantity, - }) + // if this is `null` this means that item is from variant that has `manage_inventory` false + if (item.inventory_item_id) { + inventoryAdjustment.push({ + inventory_item_id: item.inventory_item_id as string, + location_id: fulfillment.location_id, + adjustment: item.quantity, + }) + } } return { diff --git a/packages/core/js-sdk/src/admin/index.ts b/packages/core/js-sdk/src/admin/index.ts index 47ddd920d6..fb398bd1d3 100644 --- a/packages/core/js-sdk/src/admin/index.ts +++ b/packages/core/js-sdk/src/admin/index.ts @@ -5,6 +5,7 @@ import { Product } from "./product" import { ProductCollection } from "./product-collection" import { Region } from "./region" import { Upload } from "./upload" +import { Order } from "./order" export class Admin { public invite: Invite @@ -13,6 +14,7 @@ export class Admin { public product: Product public upload: Upload public region: Region + public order: Order constructor(client: Client) { this.invite = new Invite(client) @@ -21,5 +23,6 @@ export class Admin { this.product = new Product(client) this.upload = new Upload(client) this.region = new Region(client) + this.order = new Order(client) } } diff --git a/packages/core/js-sdk/src/admin/order.ts b/packages/core/js-sdk/src/admin/order.ts new file mode 100644 index 0000000000..4c8c2574d6 --- /dev/null +++ b/packages/core/js-sdk/src/admin/order.ts @@ -0,0 +1,81 @@ +import { + AdminCancelOrderFulfillment, + FindParams, + HttpTypes, + PaginatedResponse, + SelectParams, +} from "@medusajs/types" +import { Client } from "../client" +import { ClientHeaders } from "../types" + +export class Order { + private client: Client + constructor(client: Client) { + this.client = client + } + + async retrieve(id: string, query?: SelectParams, headers?: ClientHeaders) { + return await this.client.fetch<{ order: HttpTypes.AdminOrder }>( + `/admin/orders/${id}`, + { + query, + headers, + } + ) + } + + async list( + queryParams?: FindParams & HttpTypes.AdminOrderFilters, + headers?: ClientHeaders + ) { + return await this.client.fetch< + PaginatedResponse<{ orders: HttpTypes.AdminOrder[] }> + >(`/admin/orders`, { + query: queryParams, + headers, + }) + } + + async cancel(id: string, headers?: ClientHeaders) { + return await this.client.fetch<{ order: HttpTypes.AdminOrder }>( + `/admin/orders/${id}/cancel`, + { + method: "POST", + headers, + } + ) + } + + async createFulfillment( + id: string, + body: HttpTypes.AdminCreateOrderFulfillment, + query?: SelectParams, + headers?: ClientHeaders + ) { + return await this.client.fetch<{ order: HttpTypes.AdminOrder }>( + `/admin/orders/${id}/fulfillments`, + { + method: "POST", + headers, + body, + query, + } + ) + } + + async cancelFulfillment( + id: string, + fulfillmentId: string, + body: HttpTypes.AdminCancelOrderFulfillment, + headers?: ClientHeaders + ) { + return await this.client.fetch<{ order: HttpTypes.AdminOrder }>( + `/admin/orders/${id}/fulfillments/${fulfillmentId}/cancel`, + { + method: "POST", + headers, + body, + } + ) + } +} diff --git a/packages/core/types/src/http/order/admin.ts b/packages/core/types/src/http/order/admin.ts index f7e60cb6a4..6fe2aa89df 100644 --- a/packages/core/types/src/http/order/admin.ts +++ b/packages/core/types/src/http/order/admin.ts @@ -1,11 +1,24 @@ import { BaseOrder, BaseOrderAddress, + BaseOrderFilters, BaseOrderLineItem, BaseOrderShippingMethod, } from "./common" export interface AdminOrder extends BaseOrder {} export interface AdminOrderLineItem extends BaseOrderLineItem {} +export interface AdminOrderFilters extends BaseOrderFilters {} export interface AdminOrderAddress extends BaseOrderAddress {} export interface AdminOrderShippingMethod extends BaseOrderShippingMethod {} + +export interface AdminCreateOrderFulfillment { + items: { id: string; quantity: number }[] + location_id?: string + no_notification?: boolean + metadata?: Record +} + +export interface AdminCancelOrderFulfillment { + no_notification?: boolean +} diff --git a/packages/core/types/src/order/common.ts b/packages/core/types/src/order/common.ts index 5c28a4b4bd..bb455632de 100644 --- a/packages/core/types/src/order/common.ts +++ b/packages/core/types/src/order/common.ts @@ -1109,7 +1109,7 @@ export interface OrderDTO { raw_original_shipping_tax_total: BigNumberRawValue } -type PaymentStatus = +export type PaymentStatus = | "not_paid" | "awaiting" | "authorized" @@ -1121,7 +1121,7 @@ type PaymentStatus = | "canceled" | "requires_action" -type FulfillmentStatus = +export type FulfillmentStatus = | "not_fulfilled" | "partially_fulfilled" | "fulfilled" diff --git a/packages/medusa/src/api/admin/orders/[id]/cancel/route.ts b/packages/medusa/src/api/admin/orders/[id]/cancel/route.ts index aa382c22db..2a9d505641 100644 --- a/packages/medusa/src/api/admin/orders/[id]/cancel/route.ts +++ b/packages/medusa/src/api/admin/orders/[id]/cancel/route.ts @@ -8,7 +8,7 @@ import { MedusaResponse, } from "../../../../../types/routing" -export const GET = async ( +export const POST = async ( req: AuthenticatedMedusaRequest, res: MedusaResponse ) => { diff --git a/packages/medusa/src/api/admin/orders/[id]/fulfillments/[fulfillment_id]/cancel/route.ts b/packages/medusa/src/api/admin/orders/[id]/fulfillments/[fulfillment_id]/cancel/route.ts index 474fb6c0a4..88da6c1a18 100644 --- a/packages/medusa/src/api/admin/orders/[id]/fulfillments/[fulfillment_id]/cancel/route.ts +++ b/packages/medusa/src/api/admin/orders/[id]/fulfillments/[fulfillment_id]/cancel/route.ts @@ -20,6 +20,7 @@ export const POST = async ( const input = { ...req.validatedBody, order_id: req.params.id, + fulfillment_id: req.params.fulfillment_id, } await cancelOrderFulfillmentWorkflow(req.scope).run({ diff --git a/packages/medusa/src/api/admin/orders/[id]/route.ts b/packages/medusa/src/api/admin/orders/[id]/route.ts index 22babe009f..090cc58b1d 100644 --- a/packages/medusa/src/api/admin/orders/[id]/route.ts +++ b/packages/medusa/src/api/admin/orders/[id]/route.ts @@ -12,8 +12,8 @@ export const GET = async ( req: AuthenticatedMedusaRequest, res: MedusaResponse ) => { - const worklow = getOrderDetailWorkflow(req.scope) - const { result } = await worklow.run({ + const workflow = getOrderDetailWorkflow(req.scope) + const { result } = await workflow.run({ input: { fields: req.remoteQueryConfig.fields, order_id: req.params.id, diff --git a/packages/medusa/src/api/admin/orders/middlewares.ts b/packages/medusa/src/api/admin/orders/middlewares.ts index 3ed2814784..daebd688e0 100644 --- a/packages/medusa/src/api/admin/orders/middlewares.ts +++ b/packages/medusa/src/api/admin/orders/middlewares.ts @@ -7,6 +7,7 @@ import { AdminCompleteOrder, AdminGetOrdersOrderParams, AdminGetOrdersParams, + AdminOrderCancelFulfillment, AdminOrderCreateFulfillment, } from "./validators" @@ -78,9 +79,9 @@ export const adminOrderRoutesMiddlewares: MiddlewareRoute[] = [ }, { method: ["POST"], - matcher: "/admin/orders/:id/fulfillments/cancel", + matcher: "/admin/orders/:id/fulfillments/:fulfillment_id/cancel", middlewares: [ - // validateAndTransformBody(), + validateAndTransformBody(AdminOrderCancelFulfillment), validateAndTransformQuery( AdminGetOrdersOrderParams, QueryConfig.retrieveTransformQueryConfig diff --git a/packages/medusa/src/api/admin/orders/validators.ts b/packages/medusa/src/api/admin/orders/validators.ts index 2a85125c47..3c2ab3e6b2 100644 --- a/packages/medusa/src/api/admin/orders/validators.ts +++ b/packages/medusa/src/api/admin/orders/validators.ts @@ -81,7 +81,6 @@ export type AdminOrderCreateShipmentType = z.infer< > export const AdminOrderCancelFulfillment = z.object({ - fulfillment_id: z.string(), no_notification: z.boolean().optional(), })