diff --git a/integration-tests/api/__tests__/store/gift-cards.js b/integration-tests/api/__tests__/store/gift-cards.js index a139867e67..da17752ac9 100644 --- a/integration-tests/api/__tests__/store/gift-cards.js +++ b/integration-tests/api/__tests__/store/gift-cards.js @@ -1,9 +1,15 @@ const path = require("path") -const { Region, GiftCard } = require("@medusajs/medusa") +const { Region, GiftCard, GiftCardTransaction } = require("@medusajs/medusa") const setupServer = require("../../../environment-helpers/setup-server") const { useApi } = require("../../../environment-helpers/use-api") const { initDb, useDb } = require("../../../environment-helpers/use-db") +const { + simpleProductFactory, + simpleCartFactory, + simpleRegionFactory, + simpleGiftCardFactory, +} = require("../../../factories") jest.setTimeout(30000) @@ -60,4 +66,180 @@ describe("/store/gift-cards", () => { }) }) }) + + describe("gift card transactions", () => { + afterEach(async () => { + const db = useDb() + await db.teardown() + }) + + it("only creates transactions for gift cards used", async () => { + const api = useApi() + + const region = await simpleRegionFactory(dbConnection, { + currency_code: "usd", + }) + const cart = await simpleCartFactory(dbConnection, { + region: region.id, + }) + const product = await simpleProductFactory(dbConnection) + const giftCard1 = await simpleGiftCardFactory(dbConnection, { + code: "GC_1", + region_id: region.id, + balance: 50000, + value: 50000, + }) + const giftCard2 = await simpleGiftCardFactory(dbConnection, { + code: "GC_2", + region_id: region.id, + balance: 50000, + value: 50000, + }) + + await api.post(`/store/carts/${cart.id}/line-items`, { + variant_id: product.variants[0].id, + quantity: 1, + }) + + await api.post(`/store/carts/${cart.id}`, { + email: "some@customer.com", + gift_cards: [{ code: "GC_1" }, { code: "GC_2" }], + }) + + const response = await api.post(`/store/carts/${cart.id}/complete`) + expect(response.status).toEqual(200) + const orderId = response.data.data.id + + const transaction1 = await dbConnection.manager.find( + GiftCardTransaction, + { + order_id: orderId, + } + ) + + expect(transaction1.length).toEqual(1) + expect(transaction1[0].amount).toEqual(100) + }) + }) + + describe("gift card transaction ordering", () => { + afterEach(async () => { + const db = useDb() + await db.teardown() + }) + + it("choose the gift card with shortest expiry", async () => { + const api = useApi() + + const region = await simpleRegionFactory(dbConnection, { + currency_code: "usd", + }) + const cart = await simpleCartFactory(dbConnection, { + region: region.id, + }) + const product = await simpleProductFactory(dbConnection) + const ends_first = new Date(2050, 1, 1) + const ends_later = new Date(2050, 1, 2) + const giftCard1 = await simpleGiftCardFactory(dbConnection, { + id: "GC_1", + code: "GC_1", + region_id: region.id, + balance: 50000, + value: 50000, + ends_at: ends_later + }) + const giftCard2 = await simpleGiftCardFactory(dbConnection, { + id: "GC_2", + code: "GC_2", + region_id: region.id, + balance: 50000, + value: 50000, + ends_at: ends_first + }) + + await api.post(`/store/carts/${cart.id}/line-items`, { + variant_id: product.variants[0].id, + quantity: 1, + }) + + await api.post(`/store/carts/${cart.id}`, { + email: "some@customer.com", + gift_cards: [{ code: "GC_1" }, { code: "GC_2" }], + }) + + const response = await api.post(`/store/carts/${cart.id}/complete`) + expect(response.status).toEqual(200) + const orderId = response.data.data.id + + const transaction1 = await dbConnection.manager.find( + GiftCardTransaction, + { + order_id: orderId, + } + ) + + expect(transaction1.length).toEqual(1) + expect(transaction1[0].gift_card_id).toEqual(giftCard2.id) + }) + }) + + it("choose the gift card with lowest balance if same expiry", async () => { + const api = useApi() + + const region = await simpleRegionFactory(dbConnection, { + currency_code: "usd", + }) + const cart = await simpleCartFactory(dbConnection, { + region: region.id, + }) + const product = await simpleProductFactory(dbConnection) + const same_end = new Date(2050, 1, 1) + const giftCard1 = await simpleGiftCardFactory(dbConnection, { + id: "GC_1", + code: "GC_1", + region_id: region.id, + balance: 50000, + value: 50000, + ends_at: same_end + }) + const giftCard2 = await simpleGiftCardFactory(dbConnection, { + id: "GC_2", + code: "GC_2", + region_id: region.id, + balance: 30000, + value: 50000, + ends_at: same_end + }) + const giftCard3 = await simpleGiftCardFactory(dbConnection, { + id: "GC_3", + code: "GC_3", + region_id: region.id, + balance: 20000, + value: 50000 + }) + + await api.post(`/store/carts/${cart.id}/line-items`, { + variant_id: product.variants[0].id, + quantity: 1, + }) + + await api.post(`/store/carts/${cart.id}`, { + email: "some@customer.com", + gift_cards: [{ code: "GC_1" }, { code: "GC_2" }, { code: "GC_3" }], + }) + + const response = await api.post(`/store/carts/${cart.id}/complete`) + expect(response.status).toEqual(200) + const orderId = response.data.data.id + + const transaction1 = await dbConnection.manager.find( + GiftCardTransaction, + { + order_id: orderId, + } + ) + + expect(transaction1.length).toEqual(1) + expect(transaction1[0].gift_card_id).toEqual(giftCard2.id) + }) }) diff --git a/integration-tests/factories/simple-gift-card-factory.ts b/integration-tests/factories/simple-gift-card-factory.ts index 05d5fa9755..9bb679e0cf 100644 --- a/integration-tests/factories/simple-gift-card-factory.ts +++ b/integration-tests/factories/simple-gift-card-factory.ts @@ -9,6 +9,7 @@ export type GiftCardFactoryData = { value: number balance: number tax_rate?: number + ends_at?: Date } export const simpleGiftCardFactory = async ( @@ -29,6 +30,7 @@ export const simpleGiftCardFactory = async ( value: data.value, balance: data.balance, tax_rate: data.tax_rate, + ends_at: data.ends_at }) return await manager.save(toSave) diff --git a/packages/admin-ui/ui/src/domain/orders/details/detail-cards/summary.tsx b/packages/admin-ui/ui/src/domain/orders/details/detail-cards/summary.tsx index 618a9df801..37676b67a8 100644 --- a/packages/admin-ui/ui/src/domain/orders/details/detail-cards/summary.tsx +++ b/packages/admin-ui/ui/src/domain/orders/details/detail-cards/summary.tsx @@ -221,20 +221,20 @@ const SummaryCard: React.FC = ({ order, reservations }) => { } /> ))} - {order?.gift_cards?.map((giftCard, index) => ( + {order?.gift_card_transactions?.map((gcTransaction, index) => ( Gift card: - {giftCard.code} + {gcTransaction.gift_card.code}
diff --git a/packages/medusa/src/services/order.ts b/packages/medusa/src/services/order.ts index 7fff4cbcf9..55b26d9620 100644 --- a/packages/medusa/src/services/order.ts +++ b/packages/medusa/src/services/order.ts @@ -733,7 +733,13 @@ class OrderService extends TransactionBaseService { let giftCardableAmountBalance = giftCardableAmount const giftCardService = this.giftCardService_.withTransaction(manager) - for (const giftCard of cart.gift_cards) { + //Order the gift cards by first ends_at date, then remaining amount. To ensure largest possible amount left, for longest possible time. + const orderedGiftCards = cart.gift_cards.sort((a, b) => { + let aEnd = a.ends_at ?? new Date(2100, 1, 1) + let bEnd = b.ends_at ?? new Date(2100, 1, 1) + return aEnd.getTime() - bEnd.getTime() || a.balance - b.balance + }) + for (const giftCard of orderedGiftCards) { const newGiftCardBalance = Math.max( 0, giftCard.balance - giftCardableAmountBalance @@ -755,6 +761,9 @@ class OrderService extends TransactionBaseService { giftCardableAmountBalance = giftCardableAmountBalance - giftCardBalanceUsed + + if (giftCardableAmountBalance == 0) + break; } const shippingOptionServiceTx =