diff --git a/integration-tests/api/__tests__/admin/order.js b/integration-tests/api/__tests__/admin/order.js index 5aeb20da59..93727e05d8 100644 --- a/integration-tests/api/__tests__/admin/order.js +++ b/integration-tests/api/__tests__/admin/order.js @@ -4,6 +4,7 @@ const { Order, LineItem, CustomShippingOption, + ShippingMethod, } = require("@medusajs/medusa") const setupServer = require("../../../helpers/setup-server") @@ -914,6 +915,215 @@ describe("/admin/orders", () => { ]) }) + it("creates a claim on a claim additional item", async () => { + const api = useApi() + + const response = await api + .post( + "/admin/orders/test-order/claims", + { + type: "replace", + shipping_methods: [ + { + id: "test-method", + }, + ], + claim_items: [ + { + item_id: "test-item", + quantity: 1, + reason: "production_failure", + tags: ["fluff"], + images: ["https://test.image.com"], + }, + ], + additional_items: [ + { + variant_id: "test-variant", + quantity: 1, + }, + ], + }, + { + headers: { + Authorization: "Bearer test_token", + }, + } + ) + .catch((err) => { + console.log(err) + }) + + const cid = response.data.order.claims[0].id + const fulRes = await api.post( + `/admin/orders/test-order/claims/${cid}/fulfillments`, + {}, + { + headers: { + Authorization: "Bearer test_token", + }, + } + ) + + const claimItemIdToClaim = + fulRes.data.order.claims[0].additional_items[0].id + + const claimRes = await api + .post( + "/admin/orders/test-order/claims", + { + type: "replace", + shipping_methods: [ + { + id: "test-method", + }, + ], + claim_items: [ + { + item_id: claimItemIdToClaim, + quantity: 1, + reason: "production_failure", + tags: ["fluff"], + images: ["https://test.image2.com"], + }, + ], + additional_items: [ + { + variant_id: "test-variant-2", + quantity: 1, + }, + ], + }, + { + headers: { + Authorization: "Bearer test_token", + }, + } + ) + .catch((err) => { + console.log(err) + }) + + expect(claimRes.status).toEqual(200) + expect(claimRes.data.order.claims.length).toEqual(2) + + const newClaim = claimRes.data.order.claims.find( + (c) => c.fulfillment_status === "not_fulfilled" + ) + + expect(newClaim.claim_items[0].item.id).toEqual(claimItemIdToClaim) + }) + + it("creates a claim on a swap additional item", async () => { + const api = useApi() + + // create a swap + const response = await api + .post( + "/admin/orders/test-order/swaps", + { + custom_shipping_options: [{ option_id: "test-option", price: 0 }], + return_items: [ + { + item_id: "test-item", + quantity: 1, + }, + ], + additional_items: [{ variant_id: "test-variant-2", quantity: 1 }], + }, + { + headers: { + authorization: "Bearer test_token", + }, + } + ) + .catch((e) => console.log(e)) + + const sid = response.data.order.swaps[0].id + const manager = dbConnection.manager + + // add a shipping method so we can fulfill the swap + const sm = await manager.create(ShippingMethod, { + id: "test-method-swap-cart", + swap_id: sid, + shipping_option_id: "test-option", + price: 0, + data: {}, + }) + + await manager.save(sm) + + // fulfill the swap + const fulRes = await api + .post( + `/admin/orders/test-order/swaps/${sid}/fulfillments`, + {}, + { + headers: { + Authorization: "Bearer test_token", + }, + } + ) + .catch((e) => console.log(e)) + + // ship the swap + await api + .post( + `/admin/orders/test-order/swaps/${sid}/shipments`, + { + fulfillment_id: fulRes.data.order.swaps[0].fulfillments[0].id, + }, + { + headers: { + Authorization: "Bearer test_token", + }, + } + ) + .catch((e) => console.log(e)) + + const claimItemIdToClaim = + fulRes.data.order.swaps[0].additional_items[0].id + + // create a claim on the exchange + const claimRes = await api + .post( + "/admin/orders/test-order/claims", + { + type: "replace", + claim_items: [ + { + item_id: claimItemIdToClaim, + quantity: 1, + reason: "production_failure", + tags: ["fluff"], + images: ["https://test.image2.com"], + }, + ], + additional_items: [ + { + variant_id: "test-variant-2", + quantity: 1, + }, + ], + }, + { + headers: { + Authorization: "Bearer test_token", + }, + } + ) + .catch((err) => { + console.log(err) + }) + + expect(claimRes.status).toEqual(200) + expect(claimRes.data.order.claims.length).toEqual(1) + + const newClaim = claimRes.data.order.claims[0] + + expect(newClaim.claim_items[0].item.id).toEqual(claimItemIdToClaim) + }) + it("Only allow canceling claim after canceling fulfillments", async () => { const order_id = "order-with-claim" diff --git a/integration-tests/api/__tests__/admin/returns.js b/integration-tests/api/__tests__/admin/returns.js new file mode 100644 index 0000000000..a8656cad7d --- /dev/null +++ b/integration-tests/api/__tests__/admin/returns.js @@ -0,0 +1,270 @@ +const path = require("path") +import { ReturnReason, ShippingMethod } from "@medusajs/medusa" + +const setupServer = require("../../../helpers/setup-server") +const { useApi } = require("../../../helpers/use-api") +const { initDb, useDb } = require("../../../helpers/use-db") + +const orderSeeder = require("../../helpers/order-seeder") +const adminSeeder = require("../../helpers/admin-seeder") + +jest.setTimeout(30000) + +describe("/admin/returns", () => { + let medusaProcess + let dbConnection + + beforeAll(async () => { + const cwd = path.resolve(path.join(__dirname, "..", "..")) + dbConnection = await initDb({ cwd }) + medusaProcess = await setupServer({ cwd }) + }) + + afterAll(async () => { + const db = useDb() + await db.shutdown() + + medusaProcess.kill() + }) + + describe("POST /admin/returns/:id", () => { + let rrId + + beforeEach(async () => { + try { + await adminSeeder(dbConnection) + await orderSeeder(dbConnection) + + const created = dbConnection.manager.create(ReturnReason, { + value: "too_big", + label: "Too Big", + }) + const resultRR = await dbConnection.manager.save(created) + rrId = resultRR.id + } catch (err) { + console.log(err) + throw err + } + }) + + afterEach(async () => { + const db = useDb() + await db.teardown() + }) + + it("should receive a return on an item added through swap additional items", async () => { + const api = useApi() + + // create a swap + const response = await api + .post( + "/admin/orders/test-order/swaps", + { + custom_shipping_options: [{ option_id: "test-option", price: 0 }], + return_items: [ + { + item_id: "test-item", + quantity: 1, + }, + ], + additional_items: [{ variant_id: "test-variant-2", quantity: 1 }], + }, + { + headers: { + authorization: "Bearer test_token", + }, + } + ) + .catch((e) => console.log(e)) + + const sid = response.data.order.swaps[0].id + const manager = dbConnection.manager + + // add a shipping method so we can fulfill the swap + const sm = await manager.create(ShippingMethod, { + id: "test-method-swap-cart", + swap_id: sid, + shipping_option_id: "test-option", + price: 0, + data: {}, + }) + + await manager.save(sm) + + // fulfill the swap + const fulRes = await api + .post( + `/admin/orders/test-order/swaps/${sid}/fulfillments`, + {}, + { + headers: { + Authorization: "Bearer test_token", + }, + } + ) + .catch((e) => console.log(e)) + + // ship the swap + await api + .post( + `/admin/orders/test-order/swaps/${sid}/shipments`, + { + fulfillment_id: fulRes.data.order.swaps[0].fulfillments[0].id, + }, + { + headers: { + Authorization: "Bearer test_token", + }, + } + ) + .catch((e) => console.log(e)) + + const swapItemId = fulRes.data.order.swaps[0].additional_items[0].id + + // request a return + const returnRes = await api + .post( + `/admin/orders/test-order/return`, + { + items: [ + { + item_id: swapItemId, + quantity: 1, + reason_id: rrId, + }, + ], + }, + { + headers: { + Authorization: "Bearer test_token", + }, + } + ) + .catch((e) => console.log(e.response)) + + const returnId = returnRes.data.order.returns[0].id + + const receiveRes = await api + .post( + `/admin/returns/${returnId}/receive`, + { + items: [ + { + item_id: swapItemId, + quantity: 1, + }, + ], + }, + { + headers: { + Authorization: "Bearer test_token", + }, + } + ) + .catch((e) => console.log(e.response)) + + expect(receiveRes.status).toEqual(200) + }) + + it("should receive a return on an item added through claim additional items", async () => { + const api = useApi() + + const response = await api + .post( + "/admin/orders/test-order/claims", + { + type: "replace", + shipping_methods: [ + { + id: "test-method", + }, + ], + claim_items: [ + { + item_id: "test-item", + quantity: 1, + reason: "production_failure", + tags: ["fluff"], + images: ["https://test.image.com"], + }, + ], + additional_items: [ + { + variant_id: "test-variant", + quantity: 1, + }, + ], + }, + { + headers: { + Authorization: "Bearer test_token", + }, + } + ) + .catch((err) => { + console.log(err) + }) + + const cid = response.data.order.claims[0].id + const fulRes = await api.post( + `/admin/orders/test-order/claims/${cid}/fulfillments`, + {}, + { + headers: { + Authorization: "Bearer test_token", + }, + } + ) + + const claimItemId = fulRes.data.order.claims[0].additional_items[0].id + + // request a return + const returnRes = await api + .post( + `/admin/orders/test-order/return`, + { + items: [ + { + item_id: claimItemId, + quantity: 1, + reason_id: rrId, + }, + ], + return_shipping: { + option_id: "test-option", + price: 0, + } + }, + { + headers: { + Authorization: "Bearer test_token", + }, + } + ) + .catch((e) => console.log(e.response)) + + const returnId = returnRes.data.order.returns[0].id + + const receiveRes = await api + .post( + `/admin/returns/${returnId}/receive`, + { + items: [ + { + item_id: claimItemId, + quantity: 1, + }, + ], + }, + { + headers: { + Authorization: "Bearer test_token", + }, + } + ) + .catch((e) => console.log(e.response)) + + expect(receiveRes.status).toEqual(200) + }) + }) +}) diff --git a/integration-tests/api/helpers/order-seeder.js b/integration-tests/api/helpers/order-seeder.js index 064bbd476b..d46bf341d7 100644 --- a/integration-tests/api/helpers/order-seeder.js +++ b/integration-tests/api/helpers/order-seeder.js @@ -153,6 +153,7 @@ module.exports = async (connection, data = {}) => { currency_code: "usd", amount_refunded: 0, provider_id: "test-pay", + captured_at: new Date(), data: {}, }, ], diff --git a/packages/medusa/src/api/routes/admin/orders/create-claim.ts b/packages/medusa/src/api/routes/admin/orders/create-claim.ts index 11fbab6049..afda9f3730 100644 --- a/packages/medusa/src/api/routes/admin/orders/create-claim.ts +++ b/packages/medusa/src/api/routes/admin/orders/create-claim.ts @@ -1,17 +1,17 @@ import { Type } from "class-transformer" import { IsArray, - IsOptional, - ValidateNested, IsBoolean, - IsObject, - IsString, + IsEnum, IsInt, IsNotEmpty, - IsEnum, + IsObject, + IsOptional, + IsString, + ValidateNested, } from "class-validator" import { MedusaError } from "medusa-core-utils" -import { defaultAdminOrdersRelations, defaultAdminOrdersFields } from "." +import { defaultAdminOrdersFields, defaultAdminOrdersRelations } from "." import { AddressPayload } from "../../../../types/common" import { validator } from "../../../../utils/validator" @@ -177,6 +177,12 @@ export default async (req, res) => { "items.tax_lines", "discounts", "discounts.rule", + "claims", + "claims.additional_items", + "claims.additional_items.tax_lines", + "swaps", + "swaps.additional_items", + "swaps.additional_items.tax_lines", ], }) diff --git a/packages/medusa/src/services/claim.js b/packages/medusa/src/services/claim.js index d8c7d5ff80..232d3f0e04 100644 --- a/packages/medusa/src/services/claim.js +++ b/packages/medusa/src/services/claim.js @@ -230,7 +230,35 @@ class ClaimService extends BaseService { let toRefund = refund_amount if (type === "refund" && typeof refund_amount === "undefined") { const lines = claim_items.map((ci) => { - const orderItem = order.items.find((oi) => oi.id === ci.item_id) + const allOrderItems = order.items + + if (order.swaps?.length) { + for (const swap of order.swaps) { + swap.additional_items.forEach((it) => { + if ( + it.shipped_quantity || + it.shipped_quantity === it.fulfilled_quantity + ) { + allOrderItems.push(it) + } + }) + } + } + + if (order.claims?.length) { + for (const claim of order.claims) { + claim.additional_items.forEach((it) => { + if ( + it.shipped_quantity || + it.shipped_quantity === it.fulfilled_quantity + ) { + allOrderItems.push(it) + } + }) + } + } + + const orderItem = allOrderItems.find((oi) => oi.id === ci.item_id) return { ...orderItem, quantity: ci.quantity, diff --git a/packages/medusa/src/services/return.js b/packages/medusa/src/services/return.js index 9457188977..81696b2472 100644 --- a/packages/medusa/src/services/return.js +++ b/packages/medusa/src/services/return.js @@ -563,6 +563,8 @@ class ReturnService extends BaseService { "region", "swaps", "swaps.additional_items", + "claims", + "claims.additional_items", ], })