diff --git a/integration-tests/http/__tests__/fixtures/order.ts b/integration-tests/http/__tests__/fixtures/order.ts new file mode 100644 index 0000000000..8f982f3648 --- /dev/null +++ b/integration-tests/http/__tests__/fixtures/order.ts @@ -0,0 +1,185 @@ +import { adminHeaders } from "../../../helpers/create-admin-user" + +export async function createOrderSeeder({ api }) { + const region = ( + await api.post( + "/admin/regions", + { name: "Test region", currency_code: "usd" }, + adminHeaders + ) + ).data.region + + const salesChannel = ( + await api.post( + "/admin/sales-channels", + { name: "first channel", description: "channel" }, + adminHeaders + ) + ).data.sales_channel + + const stockLocation = ( + await api.post( + `/admin/stock-locations`, + { name: "test location" }, + adminHeaders + ) + ).data.stock_location + + const inventoryItem = ( + await api.post( + `/admin/inventory-items`, + { + sku: `12345-${stockLocation.id}`, + }, + adminHeaders + ) + ).data.inventory_item + + await api.post( + `/admin/inventory-items/${inventoryItem.id}/location-levels`, + { + location_id: stockLocation.id, + stocked_quantity: 10, + }, + adminHeaders + ) + + await api.post( + `/admin/stock-locations/${stockLocation.id}/sales-channels`, + { add: [salesChannel.id] }, + adminHeaders + ) + + const shippingProfile = ( + await api.post( + `/admin/shipping-profiles`, + { name: `test-${stockLocation.id}`, type: "default" }, + adminHeaders + ) + ).data.shipping_profile + + const product = ( + await api.post( + "/admin/products", + { + title: `Test fixture ${shippingProfile.id}`, + options: [ + { title: "size", values: ["large", "small"] }, + { title: "color", values: ["green"] }, + ], + variants: [ + { + title: "Test variant", + inventory_items: [ + { + inventory_item_id: inventoryItem.id, + required_quantity: 1, + }, + ], + prices: [ + { + currency_code: "usd", + amount: 100, + }, + ], + options: { + size: "large", + color: "green", + }, + }, + ], + }, + adminHeaders + ) + ).data.product + + const fulfillmentSets = ( + await api.post( + `/admin/stock-locations/${stockLocation.id}/fulfillment-sets?fields=*fulfillment_sets`, + { + name: `Test-${shippingProfile.id}`, + type: "test-type", + }, + adminHeaders + ) + ).data.stock_location.fulfillment_sets + + const fulfillmentSet = ( + await api.post( + `/admin/fulfillment-sets/${fulfillmentSets[0].id}/service-zones`, + { + name: `Test-${shippingProfile.id}`, + geo_zones: [{ type: "country", country_code: "us" }], + }, + adminHeaders + ) + ).data.fulfillment_set + + await api.post( + `/admin/stock-locations/${stockLocation.id}/fulfillment-providers`, + { add: ["manual_test-provider"] }, + adminHeaders + ) + + const shippingOption = ( + await api.post( + `/admin/shipping-options`, + { + name: `Test shipping option ${fulfillmentSet.id}`, + service_zone_id: fulfillmentSet.service_zones[0].id, + shipping_profile_id: shippingProfile.id, + provider_id: "manual_test-provider", + price_type: "flat", + type: { + label: "Test type", + description: "Test description", + code: "test-code", + }, + prices: [ + { currency_code: "usd", amount: 1000 }, + { region_id: region.id, amount: 1100 }, + ], + rules: [], + }, + adminHeaders + ) + ).data.shipping_option + + const cart = ( + await api.post(`/store/carts`, { + currency_code: "usd", + email: "tony@stark-industries.com", + shipping_address: { + address_1: "test address 1", + address_2: "test address 2", + city: "ny", + country_code: "us", + province: "ny", + postal_code: "94016", + }, + sales_channel_id: salesChannel.id, + items: [{ quantity: 1, variant_id: product.variants[0].id }], + }) + ).data.cart + + const paymentCollection = ( + await api.post(`/store/payment-collections`, { + cart_id: cart.id, + region_id: region.id, + currency_code: region.currency_code, + amount: cart.total, + }) + ).data.payment_collection + + await api.post( + `/store/payment-collections/${paymentCollection.id}/payment-sessions`, + { provider_id: "pp_system_default" } + ) + + let order = (await api.post(`/store/carts/${cart.id}/complete`, {})).data + .order + + order = (await api.get(`/admin/orders/${order.id}`, adminHeaders)).data.order + + return order +} diff --git a/integration-tests/http/__tests__/payment/admin/payment.spec.ts b/integration-tests/http/__tests__/payment/admin/payment.spec.ts index e1b93d220d..73b0b5bf35 100644 --- a/integration-tests/http/__tests__/payment/admin/payment.spec.ts +++ b/integration-tests/http/__tests__/payment/admin/payment.spec.ts @@ -1,9 +1,12 @@ +import { IPaymentModuleService } from "@medusajs/types" import { ModuleRegistrationName } from "@medusajs/utils" import { adminHeaders } from "../../../../helpers/create-admin-user" -import { IPaymentModuleService } from "@medusajs/types" +import { seedStorefrontDefaults } from "../../../../helpers/seed-storefront-defaults" +import { setupTaxStructure } from "../../../../modules/__tests__/fixtures" -const { medusaIntegrationTestRunner } = require("medusa-test-utils") -const { createAdminUser } = require("../../../../helpers/create-admin-user") +import { medusaIntegrationTestRunner } from "medusa-test-utils" +import { createAdminUser } from "../../../../helpers/create-admin-user" +import { createOrderSeeder } from "../../fixtures/order" jest.setTimeout(30000) @@ -12,10 +15,12 @@ medusaIntegrationTestRunner({ let paymentModule: IPaymentModuleService let paymentCollection let payment + let container beforeEach(async () => { - paymentModule = getContainer().resolve(ModuleRegistrationName.PAYMENT) - await createAdminUser(dbConnection, adminHeaders, getContainer()) + container = getContainer() + paymentModule = container.resolve(ModuleRegistrationName.PAYMENT) + await createAdminUser(dbConnection, adminHeaders, container) const collection = ( await api.post( @@ -51,7 +56,7 @@ medusaIntegrationTestRunner({ payment = payments[0] }) - it("Captures an authorized payment", async () => { + it("should capture an authorized payment", async () => { const response = await api.post( `/admin/payments/${payment.id}/capture`, undefined, @@ -75,7 +80,7 @@ medusaIntegrationTestRunner({ expect(response.status).toEqual(200) }) - it("Refunds an captured payment", async () => { + it("should refund a captured payment", async () => { await api.post( `/admin/payments/${payment.id}/capture`, undefined, @@ -116,5 +121,61 @@ medusaIntegrationTestRunner({ }) ) }) + + it("should not update payment collection of other orders", async () => { + await setupTaxStructure(container.resolve(ModuleRegistrationName.TAX)) + await seedStorefrontDefaults(container, "dkk") + + let order1 = await createOrderSeeder({ api }) + + expect(order1).toEqual( + expect.objectContaining({ + id: expect.any(String), + payment_status: "authorized", + }) + ) + + const order1Payment = order1.payment_collections[0].payments[0] + + const result = await api.post( + `/admin/payments/${order1Payment.id}/capture?fields=*payment_collection`, + { + amount: order1Payment.amount, + }, + adminHeaders + ) + + order1 = (await api.get(`/admin/orders/${order1.id}`, adminHeaders)).data + .order + + expect(order1).toEqual( + expect.objectContaining({ + id: order1.id, + payment_status: "captured", + }) + ) + + let order2 = await createOrderSeeder({ api }) + + order2 = (await api.get(`/admin/orders/${order2.id}`, adminHeaders)).data + .order + + expect(order2).toEqual( + expect.objectContaining({ + id: expect.any(String), + payment_status: "authorized", + }) + ) + + order1 = (await api.get(`/admin/orders/${order1.id}`, adminHeaders)).data + .order + + expect(order1).toEqual( + expect.objectContaining({ + id: expect.any(String), + payment_status: "captured", + }) + ) + }) }, }) diff --git a/integration-tests/http/jest.config.js b/integration-tests/http/jest.config.js index e9e8a528c5..80b1d82283 100644 --- a/integration-tests/http/jest.config.js +++ b/integration-tests/http/jest.config.js @@ -4,6 +4,17 @@ module.exports = { testEnvironment: `node`, rootDir: "./", transformIgnorePatterns: ["/dist", "/node_modules/"], + testPathIgnorePatterns: [ + `/examples/`, + `/www/`, + `/dist/`, + `/node_modules/`, + `/node_modules/`, + `__tests__/fixtures`, + `__testfixtures__`, + `.cache`, + "__fixtures__", + ], transform: { "^.+\\.[jt]s$": [ "@swc/jest", 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 eb9de63022..02fb58c123 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 @@ -24,6 +24,7 @@ import { getLocaleAmount, getStylizedAmount, } from "../../../../../lib/money-amount-helpers" +import { getOrderPaymentStatus } from "../../../../../lib/order-helpers" type OrderPaymentSectionProps = { order: HttpTypes.AdminOrder @@ -46,7 +47,7 @@ export const OrderPaymentSection = ({ order }: OrderPaymentSectionProps) => { return ( -
+
{ ) } -const Header = () => { +const Header = ({ order }) => { const { t } = useTranslation() + const { label, color } = getOrderPaymentStatus(t, order.payment_status) return (
{t("orders.payment.title")} + + + {label} +
) } diff --git a/packages/core/core-flows/src/definition/cart/workflows/refresh-payment-collection.ts b/packages/core/core-flows/src/definition/cart/workflows/refresh-payment-collection.ts index cd5b9b3223..a2bad3a2e0 100644 --- a/packages/core/core-flows/src/definition/cart/workflows/refresh-payment-collection.ts +++ b/packages/core/core-flows/src/definition/cart/workflows/refresh-payment-collection.ts @@ -1,3 +1,4 @@ +import { isPresent } from "@medusajs/utils" import { StepResponse, WorkflowData, @@ -39,7 +40,7 @@ export const refreshPaymentCollectionForCartWorkflowId = export const refreshPaymentCollectionForCartWorkflow = createWorkflow( refreshPaymentCollectionForCartWorkflowId, (input: WorkflowData): WorkflowData => { - const carts = useRemoteQueryStep({ + const cart = useRemoteQueryStep({ entry_point: "cart", fields: [ "id", @@ -50,30 +51,40 @@ export const refreshPaymentCollectionForCartWorkflow = createWorkflow( ], variables: { id: input.cart_id }, throw_if_key_not_found: true, + list: false, }) - const cart = transform({ carts }, (data) => data.carts[0]) const deletePaymentSessionInput = transform( { paymentCollection: cart.payment_collection }, (data) => { return { ids: - data.paymentCollection?.payment_sessions?.map((ps) => ps.id) || [], + data.paymentCollection?.payment_sessions + ?.map((ps) => ps.id) + ?.flat(1) || [], } } ) + const updatePaymentCollectionInput = transform({ cart }, (data) => { + if (!isPresent(data.cart?.payment_collection?.id)) { + return + } + + return { + selector: { id: data.cart.payment_collection.id }, + update: { + amount: data.cart.total, + currency_code: data.cart.currency_code, + }, + } + }) + parallelize( deletePaymentSessionsWorkflow.runAsStep({ input: deletePaymentSessionInput, }), - updatePaymentCollectionStep({ - selector: { id: cart.payment_collection.id }, - update: { - amount: cart.total, - currency_code: cart.currency_code, - }, - }) + updatePaymentCollectionStep(updatePaymentCollectionInput) ) } ) diff --git a/packages/core/core-flows/src/payment-collection/steps/update-payment-collection.ts b/packages/core/core-flows/src/payment-collection/steps/update-payment-collection.ts index d50193aa3d..14199df1df 100644 --- a/packages/core/core-flows/src/payment-collection/steps/update-payment-collection.ts +++ b/packages/core/core-flows/src/payment-collection/steps/update-payment-collection.ts @@ -6,6 +6,7 @@ import { import { ModuleRegistrationName, getSelectsAndRelationsFromObjectArray, + isPresent, } from "@medusajs/utils" import { StepResponse, createStep } from "@medusajs/workflows-sdk" @@ -18,6 +19,10 @@ export const updatePaymentCollectionStepId = "update-payment-collection" export const updatePaymentCollectionStep = createStep( updatePaymentCollectionStepId, async (data: StepInput, { container }) => { + if (!isPresent(data) || !isPresent(data.selector)) { + return new StepResponse([], []) + } + const paymentModuleService = container.resolve( ModuleRegistrationName.PAYMENT ) @@ -42,7 +47,7 @@ export const updatePaymentCollectionStep = createStep( return new StepResponse(updated, prevData) }, async (prevData, { container }) => { - if (!prevData) { + if (!prevData?.length) { return } const paymentModuleService = container.resolve( diff --git a/packages/medusa/src/api/admin/orders/query-config.ts b/packages/medusa/src/api/admin/orders/query-config.ts index 72c5414ce5..5c173920a0 100644 --- a/packages/medusa/src/api/admin/orders/query-config.ts +++ b/packages/medusa/src/api/admin/orders/query-config.ts @@ -48,6 +48,8 @@ export const defaultAdminRetrieveOrderFields = [ "*shipping_methods.tax_lines", "*shipping_methods.adjustments", "*payment_collections", + "*payment_collections.payments", + "*payment_collections.payments.refunds", ] export const retrieveTransformQueryConfig = {