From 2c2d3739bb5415fb165da59d4cfc22f337218dca Mon Sep 17 00:00:00 2001 From: Riqwan Thamir Date: Fri, 16 Aug 2024 12:07:10 +0200 Subject: [PATCH 1/4] feat(dashboard,core-flows,js-sdk,types,link-modules,payment): ability to copy payment link --- .../http/__tests__/claims/claims.spec.ts | 13 ++ .../dashboard/src/hooks/api/index.ts | 1 + .../src/hooks/api/payment-collections.tsx | 63 +++++++++ .../src/hooks/api/refund-reasons.tsx | 2 +- .../dashboard/src/i18n/translations/en.json | 4 +- .../admin-next/dashboard/src/lib/payment.ts | 9 ++ .../copy-payment-link/copy-payment-link.tsx | 131 ++++++++++++++++++ .../components/copy-payment-link/index.ts | 1 + .../order-payment-section.tsx | 35 +++-- .../order-summary-section.tsx | 72 +++++----- .../order-receive-return-form.tsx | 16 +-- .../admin-next/dashboard/src/vite-env.d.ts | 2 + packages/admin-next/dashboard/vite.config.mts | 3 + .../src/common/steps/delete-entities.ts | 49 +++++++ .../create-order-payment-collection.ts | 11 +- .../delete-order-payment-collection.ts | 47 +++++++ .../core-flows/src/order/workflows/index.ts | 1 + packages/core/js-sdk/src/admin/index.ts | 3 + .../js-sdk/src/admin/payment-collection.ts | 63 +++++++++ .../types/src/http/payment/admin/payloads.ts | 5 + .../types/src/http/payment/admin/responses.ts | 9 +- packages/core/types/src/payment/service.ts | 37 +++++ .../admin/payment-collections/[id]/route.ts | 23 +++ .../admin/payment-collections/middlewares.ts | 5 + .../definitions/order-payment-collection.ts | 1 + .../payment/src/models/payment-collection.ts | 4 +- .../payment/src/models/payment-session.ts | 4 + .../modules/payment/src/models/payment.ts | 4 + 28 files changed, 560 insertions(+), 58 deletions(-) create mode 100644 packages/admin-next/dashboard/src/hooks/api/payment-collections.tsx create mode 100644 packages/admin-next/dashboard/src/routes/orders/order-detail/components/copy-payment-link/copy-payment-link.tsx create mode 100644 packages/admin-next/dashboard/src/routes/orders/order-detail/components/copy-payment-link/index.ts create mode 100644 packages/core/core-flows/src/common/steps/delete-entities.ts create mode 100644 packages/core/core-flows/src/order/workflows/delete-order-payment-collection.ts create mode 100644 packages/core/js-sdk/src/admin/payment-collection.ts create mode 100644 packages/medusa/src/api/admin/payment-collections/[id]/route.ts diff --git a/integration-tests/http/__tests__/claims/claims.spec.ts b/integration-tests/http/__tests__/claims/claims.spec.ts index 80743e8671986..24368790ea238 100644 --- a/integration-tests/http/__tests__/claims/claims.spec.ts +++ b/integration-tests/http/__tests__/claims/claims.spec.ts @@ -752,6 +752,19 @@ medusaIntegrationTestRunner({ message: "Active payment collections were found. Complete existing ones or delete them before proceeding.", }) + + const deleted = ( + await api.delete( + `/admin/payment-collections/${paymentCollection.id}`, + adminHeaders + ) + ).data + + expect(deleted).toEqual({ + id: expect.any(String), + object: "payment-collection", + deleted: true, + }) }) }) diff --git a/packages/admin-next/dashboard/src/hooks/api/index.ts b/packages/admin-next/dashboard/src/hooks/api/index.ts index 4d0cda2c9c114..59a39978f26bf 100644 --- a/packages/admin-next/dashboard/src/hooks/api/index.ts +++ b/packages/admin-next/dashboard/src/hooks/api/index.ts @@ -13,6 +13,7 @@ export * from "./inventory" export * from "./invites" export * from "./notification" export * from "./orders" +export * from "./payment-collections" export * from "./payments" export * from "./price-lists" export * from "./product-types" diff --git a/packages/admin-next/dashboard/src/hooks/api/payment-collections.tsx b/packages/admin-next/dashboard/src/hooks/api/payment-collections.tsx new file mode 100644 index 0000000000000..6990d99fd4983 --- /dev/null +++ b/packages/admin-next/dashboard/src/hooks/api/payment-collections.tsx @@ -0,0 +1,63 @@ +import { FetchError } from "@medusajs/js-sdk" +import { HttpTypes } from "@medusajs/types" +import { useMutation, UseMutationOptions } from "@tanstack/react-query" +import { sdk } from "../../lib/client" +import { queryClient } from "../../lib/query-client" +import { queryKeysFactory } from "../../lib/query-key-factory" +import { ordersQueryKeys } from "./orders" + +const PAYMENT_COLLECTION_QUERY_KEY = "payment-collection" as const +export const paymentCollectionQueryKeys = queryKeysFactory( + PAYMENT_COLLECTION_QUERY_KEY +) + +export const useCreatePaymentCollection = ( + options?: UseMutationOptions< + HttpTypes.AdminPaymentCollectionResponse, + Error, + HttpTypes.AdminCreatePaymentCollection + > +) => { + return useMutation({ + mutationFn: (payload) => sdk.admin.paymentCollection.create(payload), + onSuccess: (data, variables, context) => { + queryClient.invalidateQueries({ + queryKey: ordersQueryKeys.details(), + }) + + queryClient.invalidateQueries({ + queryKey: ordersQueryKeys.lists(), + }) + + options?.onSuccess?.(data, variables, context) + }, + ...options, + }) +} + +export const useDeletePaymentCollection = ( + options?: Omit< + UseMutationOptions< + HttpTypes.AdminDeletePaymentCollectionResponse, + FetchError, + string + >, + "mutationFn" + > +) => { + return useMutation({ + mutationFn: (id: string) => sdk.admin.paymentCollection.delete(id), + onSuccess: async (data, variables, context) => { + await queryClient.invalidateQueries({ + queryKey: ordersQueryKeys.all, + }) + + await queryClient.invalidateQueries({ + queryKey: paymentCollectionQueryKeys.all, + }) + + options?.onSuccess?.(data, variables, context) + }, + ...options, + }) +} diff --git a/packages/admin-next/dashboard/src/hooks/api/refund-reasons.tsx b/packages/admin-next/dashboard/src/hooks/api/refund-reasons.tsx index 6dd8992066d8f..4465f47744d20 100644 --- a/packages/admin-next/dashboard/src/hooks/api/refund-reasons.tsx +++ b/packages/admin-next/dashboard/src/hooks/api/refund-reasons.tsx @@ -4,7 +4,7 @@ import { sdk } from "../../lib/client" import { queryKeysFactory } from "../../lib/query-key-factory" const REFUND_REASON_QUERY_KEY = "refund-reason" as const -export const paymentQueryKeys = queryKeysFactory(REFUND_REASON_QUERY_KEY) +export const refundReasonQueryKeys = queryKeysFactory(REFUND_REASON_QUERY_KEY) export const useRefundReasons = ( query?: HttpTypes.RefundReasonFilters, diff --git a/packages/admin-next/dashboard/src/i18n/translations/en.json b/packages/admin-next/dashboard/src/i18n/translations/en.json index 224980d0ba9dc..4f58dcdf39abe 100644 --- a/packages/admin-next/dashboard/src/i18n/translations/en.json +++ b/packages/admin-next/dashboard/src/i18n/translations/en.json @@ -830,7 +830,9 @@ "capturePaymentSuccess": "Payment of {{amount}} successfully captured", "createRefund": "Create Refund", "refundPaymentSuccess": "Refund of amount {{amount}} successful", - "createRefundWrongQuantity": "Quantity should be a number between 1 and {{number}}" + "createRefundWrongQuantity": "Quantity should be a number between 1 and {{number}}", + "refundAmount": "Refund {{ amount }}", + "paymentLink": "Copy payment link for {{ amount }}" }, "edits": { diff --git a/packages/admin-next/dashboard/src/lib/payment.ts b/packages/admin-next/dashboard/src/lib/payment.ts index 8096ebe601911..646892f30e363 100644 --- a/packages/admin-next/dashboard/src/lib/payment.ts +++ b/packages/admin-next/dashboard/src/lib/payment.ts @@ -10,3 +10,12 @@ export const getTotalCaptured = ( (paymentCollection.refunded_amount as number)) return acc }, 0) + +export const getTotalPending = (paymentCollections: AdminPaymentCollection[]) => + paymentCollections.reduce((acc, paymentCollection) => { + acc += + (paymentCollection.amount as number) - + (paymentCollection.captured_amount as number) + + return acc + }, 0) diff --git a/packages/admin-next/dashboard/src/routes/orders/order-detail/components/copy-payment-link/copy-payment-link.tsx b/packages/admin-next/dashboard/src/routes/orders/order-detail/components/copy-payment-link/copy-payment-link.tsx new file mode 100644 index 0000000000000..f231ee0932583 --- /dev/null +++ b/packages/admin-next/dashboard/src/routes/orders/order-detail/components/copy-payment-link/copy-payment-link.tsx @@ -0,0 +1,131 @@ +import { CheckCircleSolid, SquareTwoStack } from "@medusajs/icons" +import { AdminOrder } from "@medusajs/types" +import { Button, toast, Tooltip } from "@medusajs/ui" +import copy from "copy-to-clipboard" +import React, { useState } from "react" +import { useTranslation } from "react-i18next" +import { + useCreatePaymentCollection, + useDeletePaymentCollection, +} from "../../../../../hooks/api" +import { getStylizedAmount } from "../../../../../lib/money-amount-helpers" + +export const MEDUSA_BACKEND_URL = __STOREFRONT_URL__ ?? "http://localhost:8000" + +type CopyPaymentLinkProps = { + order: AdminOrder +} + +/** + * This component is based on the `button` element and supports all of its props + */ +const CopyPaymentLink = React.forwardRef( + ({ order }: CopyPaymentLinkProps, ref) => { + const [isCreating, setIsCreating] = useState(false) + const [url, setUrl] = useState("") + const [done, setDone] = useState(false) + const [open, setOpen] = useState(false) + const [text, setText] = useState("CopyPaymentLink") + const { t } = useTranslation() + const { mutateAsync: createPaymentCollection } = + useCreatePaymentCollection() + + const { mutateAsync: deletePaymentCollection } = + useDeletePaymentCollection() + + const copyToClipboard = async ( + e: + | React.MouseEvent + | React.MouseEvent + ) => { + e.stopPropagation() + + if (!url?.length) { + const activePaymentCollection = order.payment_collections.find( + (pc) => + pc.status === "not_paid" && + pc.amount === order.summary?.pending_difference + ) + + if (!activePaymentCollection) { + setIsCreating(true) + + const paymentCollectionsToDelete = order.payment_collections.filter( + (pc) => pc.status === "not_paid" + ) + + const promises = paymentCollectionsToDelete.map((paymentCollection) => + deletePaymentCollection(paymentCollection.id) + ) + + await Promise.all(promises) + + await createPaymentCollection( + { order_id: order.id }, + { + onSuccess: (data) => { + setUrl( + `${MEDUSA_BACKEND_URL}/payment-collection/${data.payment_collection.id}` + ) + }, + onError: (err) => { + toast.error(err.message) + }, + onSettled: () => setIsCreating(false), + } + ) + } else { + setUrl( + `${MEDUSA_BACKEND_URL}/payment-collection/${activePaymentCollection.id}` + ) + } + } + + setDone(true) + copy(url) + + setTimeout(() => { + setDone(false) + }, 2000) + } + + React.useEffect(() => { + if (done) { + setText("Copied") + return + } + + setTimeout(() => { + setText("CopyPaymentLink") + }, 500) + }, [done]) + + return ( + + + + ) + } +) +CopyPaymentLink.displayName = "CopyPaymentLink" + +export { CopyPaymentLink } diff --git a/packages/admin-next/dashboard/src/routes/orders/order-detail/components/copy-payment-link/index.ts b/packages/admin-next/dashboard/src/routes/orders/order-detail/components/copy-payment-link/index.ts new file mode 100644 index 0000000000000..e496ff07bcf2c --- /dev/null +++ b/packages/admin-next/dashboard/src/routes/orders/order-detail/components/copy-payment-link/index.ts @@ -0,0 +1 @@ +export * from "./copy-payment-link" diff --git a/packages/admin-next/dashboard/src/routes/orders/order-detail/components/order-payment-section/order-payment-section.tsx b/packages/admin-next/dashboard/src/routes/orders/order-detail/components/order-payment-section/order-payment-section.tsx index ccab6cc2b8b34..e2fe50c898530 100644 --- a/packages/admin-next/dashboard/src/routes/orders/order-detail/components/order-payment-section/order-payment-section.tsx +++ b/packages/admin-next/dashboard/src/routes/orders/order-detail/components/order-payment-section/order-payment-section.tsx @@ -25,7 +25,7 @@ import { getStylizedAmount, } from "../../../../../lib/money-amount-helpers" import { getOrderPaymentStatus } from "../../../../../lib/order-helpers" -import { getTotalCaptured } from "../../../../../lib/payment" +import { getTotalCaptured, getTotalPending } from "../../../../../lib/payment" type OrderPaymentSectionProps = { order: HttpTypes.AdminOrder @@ -321,15 +321,34 @@ const Total = ({ currencyCode: string }) => { const { t } = useTranslation() + const totalPending = getTotalPending(paymentCollections) return ( -
- - {t("orders.payment.totalPaidByCustomer")} - - - {getStylizedAmount(getTotalCaptured(paymentCollections), currencyCode)} - +
+
+ + {t("orders.payment.totalPaidByCustomer")} + + + + {getStylizedAmount( + getTotalCaptured(paymentCollections), + currencyCode + )} + +
+ + {totalPending > 0 && ( +
+ + Total pending + + + + {getStylizedAmount(totalPending, currencyCode)} + +
+ )}
) } diff --git a/packages/admin-next/dashboard/src/routes/orders/order-detail/components/order-summary-section/order-summary-section.tsx b/packages/admin-next/dashboard/src/routes/orders/order-detail/components/order-summary-section/order-summary-section.tsx index 1d0db2071111f..ba3b877317b80 100644 --- a/packages/admin-next/dashboard/src/routes/orders/order-detail/components/order-summary-section/order-summary-section.tsx +++ b/packages/admin-next/dashboard/src/routes/orders/order-detail/components/order-summary-section/order-summary-section.tsx @@ -44,11 +44,16 @@ import { getStylizedAmount, } from "../../../../../lib/money-amount-helpers" import { getTotalCaptured } from "../../../../../lib/payment.ts" +import { CopyPaymentLink } from "../copy-payment-link/copy-payment-link.tsx" type OrderSummarySectionProps = { order: AdminOrder } +function delay(t, val) { + return new Promise((resolve) => setTimeout(resolve, t, val)) +} + export const OrderSummarySection = ({ order }: OrderSummarySectionProps) => { const { t } = useTranslation() const navigate = useNavigate() @@ -95,6 +100,9 @@ export const OrderSummarySection = ({ order }: OrderSummarySectionProps) => { return false }, [reservations]) + const showPayment = (orderPreview?.summary?.pending_difference || 0) > 0 + const showRefund = (orderPreview?.summary?.pending_difference || 0) < 0 + return (
@@ -102,38 +110,40 @@ export const OrderSummarySection = ({ order }: OrderSummarySectionProps) => { - {showAllocateButton || - (showReturns && ( -
- {showReturns && ( - ({ - label: t("orders.returns.receive.receive", { - label: `#${r.id.slice(-7)}`, - }), - icon: , - to: `/orders/${order.id}/returns/${r.id}/receive`, - })), - }, - ]} - > - - - )} - {showAllocateButton && ( - - )} -
- ))} + + )} + + {showAllocateButton && ( + + )} + + {showPayment && } +
+ )} ) } diff --git a/packages/admin-next/dashboard/src/routes/orders/order-receive-return/components/order-receive-return-form/order-receive-return-form.tsx b/packages/admin-next/dashboard/src/routes/orders/order-receive-return/components/order-receive-return-form/order-receive-return-form.tsx index de2bf8e9371db..02e9178bbc56f 100644 --- a/packages/admin-next/dashboard/src/routes/orders/order-receive-return/components/order-receive-return-form/order-receive-return-form.tsx +++ b/packages/admin-next/dashboard/src/routes/orders/order-receive-return/components/order-receive-return-form/order-receive-return-form.tsx @@ -1,18 +1,16 @@ -import { useTranslation } from "react-i18next" +import { zodResolver } from "@hookform/resolvers/zod" +import { ArrrowRight } from "@medusajs/icons" import { AdminOrder, AdminReturn } from "@medusajs/types" import { Alert, Button, Input, Switch, Text, toast } from "@medusajs/ui" -import React, { useEffect, useMemo } from "react" +import { useEffect, useMemo } from "react" import { useForm } from "react-hook-form" -import { zodResolver } from "@hookform/resolvers/zod" -import { ArrrowRight } from "@medusajs/icons" +import { useTranslation } from "react-i18next" import * as zod from "zod" +import { Form } from "../../../../../components/common/form" import { Thumbnail } from "../../../../../components/common/thumbnail" import { RouteDrawer, useRouteModal } from "../../../../../components/modals" import { useStockLocation } from "../../../../../hooks/api" -import { ReceiveReturnSchema } from "./constants" -import { Form } from "../../../../../components/common/form" -import { getStylizedAmount } from "../../../../../lib/money-amount-helpers" import { useAddReceiveItems, useCancelReceiveReturn, @@ -20,6 +18,8 @@ import { useRemoveReceiveItems, useUpdateReceiveItem, } from "../../../../../hooks/api/returns" +import { getStylizedAmount } from "../../../../../lib/money-amount-helpers" +import { ReceiveReturnSchema } from "./constants" import WrittenOffQuantity from "./written-off-quantity" type OrderAllocateItemsFormProps = { @@ -318,7 +318,7 @@ export function OrderReceiveReturnForm({ {getStylizedAmount( - preview.summary.difference_sum || 0, + preview.summary.pending_difference || 0, order.currency_code )} diff --git a/packages/admin-next/dashboard/src/vite-env.d.ts b/packages/admin-next/dashboard/src/vite-env.d.ts index 7f76c97df999a..5c23008d6eae6 100644 --- a/packages/admin-next/dashboard/src/vite-env.d.ts +++ b/packages/admin-next/dashboard/src/vite-env.d.ts @@ -2,6 +2,7 @@ interface ImportMetaEnv { readonly VITE_MEDUSA_ADMIN_BACKEND_URL: string + readonly VITE_MEDUSA_STOREFRONT_URL: string readonly VITE_MEDUSA_V2: "true" | "false" } @@ -10,4 +11,5 @@ interface ImportMeta { } declare const __BACKEND_URL__: string | undefined +declare const __STOREFRONT_URL__: string | undefined declare const __BASE__: string diff --git a/packages/admin-next/dashboard/vite.config.mts b/packages/admin-next/dashboard/vite.config.mts index ae25c274ec9ce..54377cf616c51 100644 --- a/packages/admin-next/dashboard/vite.config.mts +++ b/packages/admin-next/dashboard/vite.config.mts @@ -8,6 +8,8 @@ export default defineConfig(({ mode }) => { const BASE = env.VITE_MEDUSA_BASE || "/" const BACKEND_URL = env.VITE_MEDUSA_BACKEND_URL || "http://localhost:9000" + const STOREFRONT_URL = + env.VITE_MEDUSA_STOREFRONT_URL || "http://localhost:8000" /** * Add this to your .env file to specify the project to load admin extensions from. @@ -25,6 +27,7 @@ export default defineConfig(({ mode }) => { define: { __BASE__: JSON.stringify(BASE), __BACKEND_URL__: JSON.stringify(BACKEND_URL), + __STOREFRONT_URL__: JSON.stringify(STOREFRONT_URL), }, server: { open: true, diff --git a/packages/core/core-flows/src/common/steps/delete-entities.ts b/packages/core/core-flows/src/common/steps/delete-entities.ts new file mode 100644 index 0000000000000..ee55b25256e58 --- /dev/null +++ b/packages/core/core-flows/src/common/steps/delete-entities.ts @@ -0,0 +1,49 @@ +import { StepResponse, createStep } from "@medusajs/workflows-sdk" + +export interface DeleteEntitiesStepType { + moduleRegistrationName: string + invokeMethod: string + compensateMethod: string + entityIdentifier?: string + data: any[] +} + +export const deleteEntitiesStepId = "delete-entities-step" +/** + * This step deletes one or more entities. + */ +export const deleteEntitiesStep = createStep( + deleteEntitiesStepId, + async (input: DeleteEntitiesStepType, { container }) => { + const { + moduleRegistrationName, + invokeMethod, + compensateMethod, + data = [], + } = input + + const module = container.resolve(moduleRegistrationName) + data.length ? await module[invokeMethod](data) : [] + + return new StepResponse(void 0, { + entityIdentifiers: input.data, + moduleRegistrationName, + compensateMethod, + }) + }, + async (compensateInput, { container }) => { + const { + entityIdentifiers = [], + moduleRegistrationName, + compensateMethod, + } = compensateInput! + + if (!entityIdentifiers?.length) { + return + } + + const module = container.resolve(moduleRegistrationName) + + await module[compensateMethod](entityIdentifiers) + } +) diff --git a/packages/core/core-flows/src/order/workflows/create-order-payment-collection.ts b/packages/core/core-flows/src/order/workflows/create-order-payment-collection.ts index b8e055efeba56..95083006c37de 100644 --- a/packages/core/core-flows/src/order/workflows/create-order-payment-collection.ts +++ b/packages/core/core-flows/src/order/workflows/create-order-payment-collection.ts @@ -65,13 +65,12 @@ export const createOrderPaymentCollectionWorkflow = createWorkflow( const paymentCollection = useRemoteQueryStep({ entry_point: "payment_collection", - fields: ["id"], + fields: ["id", "status"], variables: { - id: orderPaymentCollectionIds, - status: [ - PaymentCollectionStatus.NOT_PAID, - PaymentCollectionStatus.AWAITING, - ], + filters: { + id: orderPaymentCollectionIds, + status: [PaymentCollectionStatus.NOT_PAID], + }, }, list: false, }).config({ name: "payment-collection-query" }) diff --git a/packages/core/core-flows/src/order/workflows/delete-order-payment-collection.ts b/packages/core/core-flows/src/order/workflows/delete-order-payment-collection.ts new file mode 100644 index 0000000000000..6915010efc73a --- /dev/null +++ b/packages/core/core-flows/src/order/workflows/delete-order-payment-collection.ts @@ -0,0 +1,47 @@ +import { PaymentCollectionDTO } from "@medusajs/types" +import { MedusaError, Modules, PaymentCollectionStatus } from "@medusajs/utils" +import { + WorkflowData, + createStep, + createWorkflow, +} from "@medusajs/workflows-sdk" +import { removeRemoteLinkStep, useRemoteQueryStep } from "../../common" + +/** + * This step validates that the order doesn't have an active payment collection. + */ +export const throwUnlessStatusIsNotPaid = createStep( + "validate-payment-collection", + ({ paymentCollection }: { paymentCollection: PaymentCollectionDTO }) => { + if (paymentCollection.status !== PaymentCollectionStatus.NOT_PAID) { + throw new MedusaError( + MedusaError.Types.NOT_ALLOWED, + `Can only delete payment collections where status is not_paid` + ) + } + } +) + +export const deleteOrderPaymentCollectionsId = + "delete-order-payment-collectionworkflow" +/** + * This workflow deletes one or more invites. + */ +export const deleteOrderPaymentCollections = createWorkflow( + deleteOrderPaymentCollectionsId, + (input: WorkflowData<{ id: string }>): WorkflowData => { + const paymentCollection = useRemoteQueryStep({ + entry_point: "payment_collection", + fields: ["id", "status"], + variables: { id: input.id }, + throw_if_key_not_found: true, + list: false, + }).config({ name: "payment-collection-query" }) + + throwUnlessStatusIsNotPaid({ paymentCollection }) + + removeRemoteLinkStep({ + [Modules.PAYMENT]: { payment_collection_id: input.id }, + }) + } +) diff --git a/packages/core/core-flows/src/order/workflows/index.ts b/packages/core/core-flows/src/order/workflows/index.ts index 85f67f1592cf6..8a7c5dda9fef8 100644 --- a/packages/core/core-flows/src/order/workflows/index.ts +++ b/packages/core/core-flows/src/order/workflows/index.ts @@ -27,6 +27,7 @@ export * from "./create-shipment" export * from "./decline-order-change" export * from "./delete-order-change" export * from "./delete-order-change-actions" +export * from "./delete-order-payment-collection" export * from "./exchange/begin-order-exchange" export * from "./exchange/cancel-begin-order-exchange" export * from "./exchange/cancel-exchange" diff --git a/packages/core/js-sdk/src/admin/index.ts b/packages/core/js-sdk/src/admin/index.ts index 1d7a8c7271494..7cede07ac29f8 100644 --- a/packages/core/js-sdk/src/admin/index.ts +++ b/packages/core/js-sdk/src/admin/index.ts @@ -11,6 +11,7 @@ import { Invite } from "./invite" import { Notification } from "./notification" import { Order } from "./order" import { Payment } from "./payment" +import { PaymentCollection } from "./payment-collection" import { PriceList } from "./price-list" import { PricePreference } from "./price-preference" import { Product } from "./product" @@ -67,6 +68,7 @@ export class Admin { public payment: Payment public productVariant: ProductVariant public refundReason: RefundReason + public paymentCollection: PaymentCollection constructor(client: Client) { this.invite = new Invite(client) @@ -102,5 +104,6 @@ export class Admin { this.productVariant = new ProductVariant(client) this.refundReason = new RefundReason(client) this.exchange = new Exchange(client) + this.paymentCollection = new PaymentCollection(client) } } diff --git a/packages/core/js-sdk/src/admin/payment-collection.ts b/packages/core/js-sdk/src/admin/payment-collection.ts new file mode 100644 index 0000000000000..ca8dacc0cfb3c --- /dev/null +++ b/packages/core/js-sdk/src/admin/payment-collection.ts @@ -0,0 +1,63 @@ +import { HttpTypes, SelectParams } from "@medusajs/types" +import { Client } from "../client" +import { ClientHeaders } from "../types" + +export class PaymentCollection { + private client: Client + constructor(client: Client) { + this.client = client + } + + async list( + query?: HttpTypes.AdminPaymentCollectionFilters, + headers?: ClientHeaders + ) { + return await this.client.fetch( + `/admin/payment-collections`, + { + query, + headers, + } + ) + } + + async retrieve( + id: string, + query?: HttpTypes.AdminPaymentCollectionFilters, + headers?: ClientHeaders + ) { + return await this.client.fetch( + `/admin/payment-collections/${id}`, + { + query, + headers, + } + ) + } + + async create( + body: HttpTypes.AdminCreatePaymentCollection, + query?: SelectParams, + headers?: ClientHeaders + ) { + return await this.client.fetch( + `/admin/payment-collections`, + { + method: "POST", + headers, + body, + query, + } + ) + } + + async delete(id: string, headers?: ClientHeaders) { + return await this.client.fetch( + `/admin/payment-collections/${id}`, + { + method: "DELETE", + headers, + } + ) + } +} diff --git a/packages/core/types/src/http/payment/admin/payloads.ts b/packages/core/types/src/http/payment/admin/payloads.ts index d04fabc721274..3396965b1a0ef 100644 --- a/packages/core/types/src/http/payment/admin/payloads.ts +++ b/packages/core/types/src/http/payment/admin/payloads.ts @@ -12,3 +12,8 @@ export interface AdminCreateRefundReason { label: string description?: string } + +export interface AdminCreatePaymentCollection { + order_id: string + amount?: number +} diff --git a/packages/core/types/src/http/payment/admin/responses.ts b/packages/core/types/src/http/payment/admin/responses.ts index 2999daf591fa0..ae99ee87cb168 100644 --- a/packages/core/types/src/http/payment/admin/responses.ts +++ b/packages/core/types/src/http/payment/admin/responses.ts @@ -1,4 +1,4 @@ -import { PaginatedResponse } from "../../common" +import { DeleteResponse, PaginatedResponse } from "../../common" import { AdminPayment, AdminPaymentCollection, @@ -11,6 +11,13 @@ export interface AdminPaymentCollectionResponse { payment_collection: AdminPaymentCollection } +export interface AdminDeletePaymentCollectionResponse + extends DeleteResponse<"payment-collection"> {} + +export interface AdminPaymentCollectionsResponse { + payment_collections: AdminPaymentCollection[] +} + export interface AdminPaymentResponse { payment: AdminPayment } diff --git a/packages/core/types/src/payment/service.ts b/packages/core/types/src/payment/service.ts index c3c25fc29ef9b..e521baa8e100e 100644 --- a/packages/core/types/src/payment/service.ts +++ b/packages/core/types/src/payment/service.ts @@ -368,6 +368,43 @@ export interface IPaymentModuleService extends IModuleService { sharedContext?: Context ): Promise + /** + * This method soft deletes payment collections by their IDs. + * + * @param {string[]} id - The IDs of payment collections. + * @param {SoftDeleteReturn} config - An object that is used to specify an entity's related entities that should be soft-deleted when the main entity is soft-deleted. + * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. + * @returns {Promise>} An object that includes the IDs of related records that were also soft deleted. + * If there are no related records, the promise resolves to `void`. + * + * @example + * await paymentModule.softDeletePaymentCollections(["paycol_123"]) + */ + softDeletePaymentCollections( + id: string[], + config?: SoftDeleteReturn, + sharedContext?: Context + ): Promise | void> + + /** + * This method restores soft deleted payment collection by their IDs. + * + * @param {string[]} id - The IDs of payment collections. + * @param {RestoreReturn} config - Configurations determining which relations to restore along with each of the payment collection. You can pass to its `returnLinkableKeys` + * property any of the payment collection's relation attribute names. + * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. + * @returns {Promise>} An object that includes the IDs of related records that were restored. + * If there are no related records restored, the promise resolves to `void`. + * + * @example + * await paymentModule.restorePaymentCollections(["paycol_123"]) + */ + restorePaymentCollections( + id: string[], + config?: RestoreReturn, + sharedContext?: Context + ): Promise | void> + /** * This method marks a payment collection as completed by settings its `completed_at` field to the current date and time. * diff --git a/packages/medusa/src/api/admin/payment-collections/[id]/route.ts b/packages/medusa/src/api/admin/payment-collections/[id]/route.ts new file mode 100644 index 0000000000000..8c0123206364e --- /dev/null +++ b/packages/medusa/src/api/admin/payment-collections/[id]/route.ts @@ -0,0 +1,23 @@ +import { deleteOrderPaymentCollections } from "@medusajs/core-flows" +import { DeleteResponse } from "@medusajs/types" +import { + AuthenticatedMedusaRequest, + MedusaResponse, +} from "../../../../types/routing" + +export const DELETE = async ( + req: AuthenticatedMedusaRequest, + res: MedusaResponse> +) => { + const { id } = req.params + + await deleteOrderPaymentCollections(req.scope).run({ + input: { id }, + }) + + res.status(200).json({ + id, + object: "payment-collection", + deleted: true, + }) +} diff --git a/packages/medusa/src/api/admin/payment-collections/middlewares.ts b/packages/medusa/src/api/admin/payment-collections/middlewares.ts index b318dad45a10b..4d8400dad6573 100644 --- a/packages/medusa/src/api/admin/payment-collections/middlewares.ts +++ b/packages/medusa/src/api/admin/payment-collections/middlewares.ts @@ -19,4 +19,9 @@ export const adminPaymentCollectionsMiddlewares: MiddlewareRoute[] = [ ), ], }, + { + method: ["DELETE"], + matcher: "/admin/payment-collections/:id", + middlewares: [], + }, ] diff --git a/packages/modules/link-modules/src/definitions/order-payment-collection.ts b/packages/modules/link-modules/src/definitions/order-payment-collection.ts index 3fe36fcdc2218..a4e04475fc0fa 100644 --- a/packages/modules/link-modules/src/definitions/order-payment-collection.ts +++ b/packages/modules/link-modules/src/definitions/order-payment-collection.ts @@ -35,6 +35,7 @@ export const OrderPaymentCollection: ModuleJoinerConfig = { args: { methodSuffix: "PaymentCollections", }, + deleteCascade: true, }, ], extends: [ diff --git a/packages/modules/payment/src/models/payment-collection.ts b/packages/modules/payment/src/models/payment-collection.ts index fefa36eb74328..aca053831db2c 100644 --- a/packages/modules/payment/src/models/payment-collection.ts +++ b/packages/modules/payment/src/models/payment-collection.ts @@ -103,12 +103,12 @@ export default class PaymentCollection { payment_providers = new Collection>(this) @OneToMany(() => PaymentSession, (ps) => ps.payment_collection, { - cascade: [Cascade.PERSIST, "soft-remove"] as any, + cascade: [Cascade.PERSIST] as any, }) payment_sessions = new Collection>(this) @OneToMany(() => Payment, (payment) => payment.payment_collection, { - cascade: [Cascade.PERSIST, "soft-remove"] as any, + cascade: [Cascade.PERSIST] as any, }) payments = new Collection>(this) diff --git a/packages/modules/payment/src/models/payment-session.ts b/packages/modules/payment/src/models/payment-session.ts index c6afe35411aee..d4341ae475483 100644 --- a/packages/modules/payment/src/models/payment-session.ts +++ b/packages/modules/payment/src/models/payment-session.ts @@ -107,10 +107,14 @@ export default class PaymentSession { @BeforeCreate() onCreate() { this.id = generateEntityId(this.id, "payses") + this.payment_collection_id ??= + this.payment_collection_id ?? this.payment_collection?.id } @OnInit() onInit() { this.id = generateEntityId(this.id, "payses") + this.payment_collection_id ??= + this.payment_collection_id ?? this.payment_collection?.id } } diff --git a/packages/modules/payment/src/models/payment.ts b/packages/modules/payment/src/models/payment.ts index 4e6fdaf821768..ae3248bf43b6d 100644 --- a/packages/modules/payment/src/models/payment.ts +++ b/packages/modules/payment/src/models/payment.ts @@ -144,10 +144,14 @@ export default class Payment { @BeforeCreate() onCreate() { this.id = generateEntityId(this.id, "pay") + this.payment_collection_id ??= + this.payment_collection_id ?? this.payment_collection?.id } @OnInit() onInit() { this.id = generateEntityId(this.id, "pay") + this.payment_collection_id ??= + this.payment_collection_id ?? this.payment_collection?.id } } From e5557fc8978c355914ab9a1facf01fcfda1958a2 Mon Sep 17 00:00:00 2001 From: Riqwan Thamir Date: Mon, 19 Aug 2024 15:01:04 +0200 Subject: [PATCH 2/4] chore: fix issues with invalidation --- .../dashboard/src/hooks/api/orders.tsx | 9 ++---- .../src/hooks/api/payment-collections.tsx | 6 +--- .../copy-payment-link/copy-payment-link.tsx | 1 + .../order-fulfillment-section.tsx | 9 ++++-- .../order-general-section.tsx | 6 ++-- .../order-summary-section.tsx | 28 +++++++++++++------ .../create-order-payment-collection.ts | 10 +++---- .../types/src/http/order/admin/entities.ts | 6 +++- 8 files changed, 44 insertions(+), 31 deletions(-) diff --git a/packages/admin-next/dashboard/src/hooks/api/orders.tsx b/packages/admin-next/dashboard/src/hooks/api/orders.tsx index 1783e69bb72dd..592b96f06ba1c 100644 --- a/packages/admin-next/dashboard/src/hooks/api/orders.tsx +++ b/packages/admin-next/dashboard/src/hooks/api/orders.tsx @@ -136,18 +136,15 @@ export const useCreateOrderShipment = ( } export const useCancelOrder = ( - orderId: string, options?: UseMutationOptions ) => { return useMutation({ - mutationFn: () => sdk.admin.order.cancel(orderId), + mutationFn: (id) => sdk.admin.order.cancel(id), onSuccess: (data: any, variables: any, context: any) => { queryClient.invalidateQueries({ - queryKey: ordersQueryKeys.details(), - }) - queryClient.invalidateQueries({ - queryKey: ordersQueryKeys.lists(), + queryKey: ordersQueryKeys.all, }) + options?.onSuccess?.(data, variables, context) }, ...options, diff --git a/packages/admin-next/dashboard/src/hooks/api/payment-collections.tsx b/packages/admin-next/dashboard/src/hooks/api/payment-collections.tsx index 6990d99fd4983..fd99b20176972 100644 --- a/packages/admin-next/dashboard/src/hooks/api/payment-collections.tsx +++ b/packages/admin-next/dashboard/src/hooks/api/payment-collections.tsx @@ -22,11 +22,7 @@ export const useCreatePaymentCollection = ( mutationFn: (payload) => sdk.admin.paymentCollection.create(payload), onSuccess: (data, variables, context) => { queryClient.invalidateQueries({ - queryKey: ordersQueryKeys.details(), - }) - - queryClient.invalidateQueries({ - queryKey: ordersQueryKeys.lists(), + queryKey: ordersQueryKeys.all, }) options?.onSuccess?.(data, variables, context) diff --git a/packages/admin-next/dashboard/src/routes/orders/order-detail/components/copy-payment-link/copy-payment-link.tsx b/packages/admin-next/dashboard/src/routes/orders/order-detail/components/copy-payment-link/copy-payment-link.tsx index f231ee0932583..68fb73d4b7ceb 100644 --- a/packages/admin-next/dashboard/src/routes/orders/order-detail/components/copy-payment-link/copy-payment-link.tsx +++ b/packages/admin-next/dashboard/src/routes/orders/order-detail/components/copy-payment-link/copy-payment-link.tsx @@ -86,6 +86,7 @@ const CopyPaymentLink = React.forwardRef( setTimeout(() => { setDone(false) + setUrl("") }, 2000) } 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 9e9074b807ff9..ef45e87b8ca29 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 @@ -1,5 +1,10 @@ import { Buildings, XCircle } from "@medusajs/icons" -import { AdminOrder, FulfillmentDTO, OrderLineItemDTO } from "@medusajs/types" +import { + AdminOrder, + AdminOrderFulfillment, + HttpTypes, + OrderLineItemDTO, +} from "@medusajs/types" import { Button, Container, @@ -152,7 +157,7 @@ const Fulfillment = ({ order, index, }: { - fulfillment: FulfillmentDTO + fulfillment: AdminOrderFulfillment order: AdminOrder index: number }) => { 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 3038cac591f47..3e7c8cdf87450 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 @@ -11,11 +11,11 @@ import { import { format } from "date-fns" import { useTranslation } from "react-i18next" import { ActionMenu } from "../../../../../components/common/action-menu" +import { useCancelOrder } from "../../../../../hooks/api/orders" import { getOrderFulfillmentStatus, getOrderPaymentStatus, } from "../../../../../lib/order-helpers" -import { useCancelOrder } from "../../../../../hooks/api/orders" type OrderGeneralSectionProps = { order: Order @@ -25,7 +25,7 @@ export const OrderGeneralSection = ({ order }: OrderGeneralSectionProps) => { const { t } = useTranslation() const prompt = usePrompt() - const { mutateAsync } = useCancelOrder(order.id) + const { mutateAsync: cancelOrder } = useCancelOrder() const handleCancel = async () => { const res = await prompt({ @@ -41,7 +41,7 @@ export const OrderGeneralSection = ({ order }: OrderGeneralSectionProps) => { return } - await mutateAsync() + await cancelOrder(order.id) } return ( diff --git a/packages/admin-next/dashboard/src/routes/orders/order-detail/components/order-summary-section/order-summary-section.tsx b/packages/admin-next/dashboard/src/routes/orders/order-detail/components/order-summary-section/order-summary-section.tsx index 020132f8a9661..0327ef077f999 100644 --- a/packages/admin-next/dashboard/src/routes/orders/order-detail/components/order-summary-section/order-summary-section.tsx +++ b/packages/admin-next/dashboard/src/routes/orders/order-detail/components/order-summary-section/order-summary-section.tsx @@ -44,24 +44,23 @@ import { getStylizedAmount, } from "../../../../../lib/money-amount-helpers" import { getTotalCaptured } from "../../../../../lib/payment.ts" -import { CopyPaymentLink } from "../copy-payment-link/copy-payment-link.tsx" import { getReturnableQuantity } from "../../../../../lib/rma.ts" +import { CopyPaymentLink } from "../copy-payment-link/copy-payment-link.tsx" type OrderSummarySectionProps = { order: AdminOrder } -function delay(t, val) { - return new Promise((resolve) => setTimeout(resolve, t, val)) -} - export const OrderSummarySection = ({ order }: OrderSummarySectionProps) => { const { t } = useTranslation() const navigate = useNavigate() - const { reservations } = useReservationItems({ - line_item_id: order.items.map((i) => i.id), - }) + const { reservations } = useReservationItems( + { + line_item_id: order?.items?.map((i) => i.id), + }, + { enabled: Array.isArray(order?.items) } + ) const { order: orderPreview } = useOrderPreview(order.id!) @@ -101,7 +100,18 @@ export const OrderSummarySection = ({ order }: OrderSummarySectionProps) => { return false }, [reservations]) - const showPayment = (orderPreview?.summary?.pending_difference || 0) > 0 + // TODO: We need a way to link payment collections to a change order to + // accurately differentiate order payments and order change payments + // This fix should be temporary. + const authorizedPaymentCollection = order.payment_collections.find( + (pc) => + pc.status === "authorized" && + pc.amount === order.summary?.pending_difference + ) + + const showPayment = + typeof authorizedPaymentCollection === "undefined" && + (orderPreview?.summary?.pending_difference || 0) > 0 const showRefund = (orderPreview?.summary?.pending_difference || 0) < 0 return ( diff --git a/packages/core/core-flows/src/order/workflows/create-order-payment-collection.ts b/packages/core/core-flows/src/order/workflows/create-order-payment-collection.ts index 95083006c37de..38fd4d8d18306 100644 --- a/packages/core/core-flows/src/order/workflows/create-order-payment-collection.ts +++ b/packages/core/core-flows/src/order/workflows/create-order-payment-collection.ts @@ -80,10 +80,7 @@ export const createOrderPaymentCollectionWorkflow = createWorkflow( const paymentCollectionData = transform( { order, input }, ({ order, input }) => { - const pendingPayment = MathBN.sub( - order.summary.raw_current_order_total, - order.summary.raw_original_order_total - ) + const pendingPayment = order.summary.raw_pending_difference if (MathBN.lte(pendingPayment, 0)) { throw new MedusaError( @@ -92,7 +89,10 @@ export const createOrderPaymentCollectionWorkflow = createWorkflow( ) } - if (input.amount && MathBN.gt(input.amount, pendingPayment)) { + if ( + input.amount && + MathBN.gt(input.amount ?? pendingPayment, pendingPayment) + ) { throw new MedusaError( MedusaError.Types.NOT_ALLOWED, `Cannot create a payment collection for amount greater than ${pendingPayment}` diff --git a/packages/core/types/src/http/order/admin/entities.ts b/packages/core/types/src/http/order/admin/entities.ts index ba1f9c77aaf27..f55555844b27b 100644 --- a/packages/core/types/src/http/order/admin/entities.ts +++ b/packages/core/types/src/http/order/admin/entities.ts @@ -4,14 +4,18 @@ import { BaseOrderAddress, BaseOrderChange, BaseOrderChangeAction, + BaseOrderFulfillment, BaseOrderLineItem, BaseOrderShippingMethod, } from "../common" export interface AdminOrder extends BaseOrder { payment_collections: AdminPaymentCollection[] + fulfillments?: BaseOrderFulfillment[] } +export interface AdminOrderFulfillment extends BaseOrderFulfillment {} + export interface AdminOrderLineItem extends BaseOrderLineItem {} export interface AdminOrderAddress extends BaseOrderAddress {} export interface AdminOrderShippingMethod extends BaseOrderShippingMethod {} @@ -23,4 +27,4 @@ export interface AdminOrderPreview shipping_methods: (BaseOrderShippingMethod & { actions?: BaseOrderChangeAction[] })[] -} \ No newline at end of file +} From 541067d132f58f93cbcad997d1b85cc948dc4fa1 Mon Sep 17 00:00:00 2001 From: Riqwan Thamir Date: Mon, 19 Aug 2024 17:25:06 +0200 Subject: [PATCH 3/4] chore: fix more invalidation issues --- .../admin-next/dashboard/src/hooks/api/orders.tsx | 15 +++++++++++++++ .../src/hooks/api/payment-collections.tsx | 10 +++++++--- .../dashboard/src/hooks/api/payments.tsx | 12 ++---------- .../order-create-fulfillment-form.tsx | 13 ++++++++++++- .../order-create-shipment-form.tsx | 6 +++--- .../order-summary-section.tsx | 4 ++-- 6 files changed, 41 insertions(+), 19 deletions(-) diff --git a/packages/admin-next/dashboard/src/hooks/api/orders.tsx b/packages/admin-next/dashboard/src/hooks/api/orders.tsx index 592b96f06ba1c..533658be4ccdc 100644 --- a/packages/admin-next/dashboard/src/hooks/api/orders.tsx +++ b/packages/admin-next/dashboard/src/hooks/api/orders.tsx @@ -89,6 +89,11 @@ export const useCreateOrderFulfillment = ( queryClient.invalidateQueries({ queryKey: ordersQueryKeys.details(), }) + + queryClient.invalidateQueries({ + queryKey: ordersQueryKeys.preview(orderId), + }) + options?.onSuccess?.(data, variables, context) }, ...options, @@ -107,6 +112,11 @@ export const useCancelOrderFulfillment = ( queryClient.invalidateQueries({ queryKey: ordersQueryKeys.details(), }) + + queryClient.invalidateQueries({ + queryKey: ordersQueryKeys.preview(orderId), + }) + options?.onSuccess?.(data, variables, context) }, ...options, @@ -129,6 +139,11 @@ export const useCreateOrderShipment = ( queryClient.invalidateQueries({ queryKey: ordersQueryKeys.details(), }) + + queryClient.invalidateQueries({ + queryKey: ordersQueryKeys.preview(orderId), + }) + options?.onSuccess?.(data, variables, context) }, ...options, diff --git a/packages/admin-next/dashboard/src/hooks/api/payment-collections.tsx b/packages/admin-next/dashboard/src/hooks/api/payment-collections.tsx index fd99b20176972..8164cda952c8e 100644 --- a/packages/admin-next/dashboard/src/hooks/api/payment-collections.tsx +++ b/packages/admin-next/dashboard/src/hooks/api/payment-collections.tsx @@ -25,6 +25,10 @@ export const useCreatePaymentCollection = ( queryKey: ordersQueryKeys.all, }) + queryClient.invalidateQueries({ + queryKey: paymentCollectionQueryKeys.all, + }) + options?.onSuccess?.(data, variables, context) }, ...options, @@ -43,12 +47,12 @@ export const useDeletePaymentCollection = ( ) => { return useMutation({ mutationFn: (id: string) => sdk.admin.paymentCollection.delete(id), - onSuccess: async (data, variables, context) => { - await queryClient.invalidateQueries({ + onSuccess: (data, variables, context) => { + queryClient.invalidateQueries({ queryKey: ordersQueryKeys.all, }) - await queryClient.invalidateQueries({ + queryClient.invalidateQueries({ queryKey: paymentCollectionQueryKeys.all, }) diff --git a/packages/admin-next/dashboard/src/hooks/api/payments.tsx b/packages/admin-next/dashboard/src/hooks/api/payments.tsx index 9e8141d88f7fb..6fb272de96a16 100644 --- a/packages/admin-next/dashboard/src/hooks/api/payments.tsx +++ b/packages/admin-next/dashboard/src/hooks/api/payments.tsx @@ -70,11 +70,7 @@ export const useCapturePayment = ( mutationFn: (payload) => sdk.admin.payment.capture(paymentId, payload), onSuccess: (data, variables, context) => { queryClient.invalidateQueries({ - queryKey: ordersQueryKeys.details(), - }) - - queryClient.invalidateQueries({ - queryKey: ordersQueryKeys.lists(), + queryKey: ordersQueryKeys.all, }) options?.onSuccess?.(data, variables, context) @@ -95,11 +91,7 @@ export const useRefundPayment = ( mutationFn: (payload) => sdk.admin.payment.refund(paymentId, payload), onSuccess: (data, variables, context) => { queryClient.invalidateQueries({ - queryKey: ordersQueryKeys.details(), - }) - - queryClient.invalidateQueries({ - queryKey: ordersQueryKeys.lists(), + queryKey: ordersQueryKeys.all, }) options?.onSuccess?.(data, variables, context) 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 16457326d0ec2..5148559fa119c 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 @@ -7,6 +7,7 @@ import { AdminOrder } from "@medusajs/types" import { Alert, Button, Select, Switch, toast } from "@medusajs/ui" import { useForm, useWatch } from "react-hook-form" +import { OrderLineItemDTO } from "@medusajs/types" import { Form } from "../../../../../components/common/form" import { RouteFocusModal, @@ -118,8 +119,18 @@ export function OrderCreateFulfillmentForm({ if (itemsToFulfill?.length) { setFulfillableItems(itemsToFulfill) + + const quantityMap = fulfillableItems.reduce( + (acc, item) => { + acc[item.id] = getFulfillableQuantity(item as OrderLineItemDTO) + return acc + }, + {} as Record + ) + + form.setValue("quantity", quantityMap) } - }, [order.items]) + }, [order.items?.length]) return ( diff --git a/packages/admin-next/dashboard/src/routes/orders/order-create-shipment/components/order-create-shipment-form/order-create-shipment-form.tsx b/packages/admin-next/dashboard/src/routes/orders/order-create-shipment/components/order-create-shipment-form/order-create-shipment-form.tsx index 0e8e26b83aee3..a56f7e10dd440 100644 --- a/packages/admin-next/dashboard/src/routes/orders/order-create-shipment/components/order-create-shipment-form/order-create-shipment-form.tsx +++ b/packages/admin-next/dashboard/src/routes/orders/order-create-shipment/components/order-create-shipment-form/order-create-shipment-form.tsx @@ -11,8 +11,8 @@ import { RouteFocusModal, useRouteModal, } from "../../../../../components/modals" -import { CreateShipmentSchema } from "./constants" import { useCreateOrderShipment } from "../../../../../hooks/api" +import { CreateShipmentSchema } from "./constants" type OrderCreateFulfillmentFormProps = { order: AdminOrder @@ -27,7 +27,7 @@ export function OrderCreateShipmentForm({ const { handleSuccess } = useRouteModal() const { mutateAsync: createShipment, isPending: isMutating } = - useCreateOrderShipment(order.id, fulfillment.id) + useCreateOrderShipment(order.id, fulfillment?.id) const form = useForm>({ defaultValues: { @@ -44,7 +44,7 @@ export function OrderCreateShipmentForm({ const handleSubmit = form.handleSubmit(async (data) => { await createShipment( { - items: fulfillment.items.map((i) => ({ + items: fulfillment?.items?.map((i) => ({ id: i.line_item_id, quantity: i.quantity, })), diff --git a/packages/admin-next/dashboard/src/routes/orders/order-detail/components/order-summary-section/order-summary-section.tsx b/packages/admin-next/dashboard/src/routes/orders/order-detail/components/order-summary-section/order-summary-section.tsx index 0327ef077f999..c616021064904 100644 --- a/packages/admin-next/dashboard/src/routes/orders/order-detail/components/order-summary-section/order-summary-section.tsx +++ b/packages/admin-next/dashboard/src/routes/orders/order-detail/components/order-summary-section/order-summary-section.tsx @@ -111,8 +111,8 @@ export const OrderSummarySection = ({ order }: OrderSummarySectionProps) => { const showPayment = typeof authorizedPaymentCollection === "undefined" && - (orderPreview?.summary?.pending_difference || 0) > 0 - const showRefund = (orderPreview?.summary?.pending_difference || 0) < 0 + (order?.summary?.pending_difference || 0) > 0 + const showRefund = (order?.summary?.pending_difference || 0) < 0 return ( From c78cab076a67e0eae6ea2c9ec83483e707b88d13 Mon Sep 17 00:00:00 2001 From: Riqwan Thamir Date: Mon, 19 Aug 2024 22:20:34 +0200 Subject: [PATCH 4/4] chore: fix calculations --- integration-tests/http/__tests__/claims/claims.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration-tests/http/__tests__/claims/claims.spec.ts b/integration-tests/http/__tests__/claims/claims.spec.ts index 24368790ea238..0118e0bc58a44 100644 --- a/integration-tests/http/__tests__/claims/claims.spec.ts +++ b/integration-tests/http/__tests__/claims/claims.spec.ts @@ -721,7 +721,7 @@ medusaIntegrationTestRunner({ }) it("should create a payment collection successfully and throw on multiple", async () => { - const paymentDelta = 110.5 + const paymentDelta = 171.5 const paymentCollection = ( await api.post(