fix(medusa): totals calculation with gift card (#5075)
**What**
Resolve potential discrepency between what is calculated in the `createFromCart` and the cart/order totals calculation.
When the giftCard is taxable then the following calculation is applied
```ts
cart.subtotal + cart.shipping_total - cart.discount_total
```
otherwise
```ts
cart.subtotal +
cart.shipping_total +
cart.tax_total -
cart.discount_total
```
Co-authored-by: Oli Juhl <59018053+olivermrbl@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
8bb8eb530b
commit
e64823d1b9
5
.changeset/fifty-cooks-occur.md
Normal file
5
.changeset/fifty-cooks-occur.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@medusajs/medusa": patch
|
||||
---
|
||||
|
||||
fix(medusa): totals calculation with gift card
|
||||
@@ -1,3 +1,5 @@
|
||||
const NewTotalsService = require("../new-totals")
|
||||
|
||||
export const newTotalsServiceMock = {
|
||||
withTransaction: function () {
|
||||
return this
|
||||
@@ -16,6 +18,23 @@ export const newTotalsServiceMock = {
|
||||
getShippingMethodTotals: jest.fn().mockImplementation((order, lineItems) => {
|
||||
return Promise.resolve({})
|
||||
}),
|
||||
getGiftCardableAmount: jest
|
||||
.fn()
|
||||
.mockImplementation(
|
||||
({
|
||||
gift_cards_taxable,
|
||||
subtotal,
|
||||
shipping_total,
|
||||
discount_total,
|
||||
tax_total,
|
||||
}) => {
|
||||
return Promise.resolve(
|
||||
(gift_cards_taxable
|
||||
? subtotal + shipping_total - discount_total
|
||||
: subtotal + shipping_total + tax_total - discount_total) || 0
|
||||
)
|
||||
}
|
||||
),
|
||||
}
|
||||
|
||||
const mock = jest.fn().mockImplementation(() => {
|
||||
|
||||
@@ -364,6 +364,7 @@ describe("OrderService", () => {
|
||||
subtotal: 100,
|
||||
total: 100,
|
||||
discount_total: 0,
|
||||
gift_card_total: 100,
|
||||
}
|
||||
|
||||
orderService.cartService_.retrieveWithTotals = () => {
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import { FlagRouter, isDefined, MedusaError } from "@medusajs/utils"
|
||||
import { isEmpty, isEqual } from "lodash"
|
||||
import { DeepPartial, EntityManager, In, IsNull, Not } from "typeorm"
|
||||
import {
|
||||
Address,
|
||||
Cart,
|
||||
CustomShippingOption,
|
||||
Customer,
|
||||
CustomShippingOption,
|
||||
Discount,
|
||||
DiscountRule,
|
||||
DiscountRuleType,
|
||||
@@ -22,13 +25,13 @@ import {
|
||||
CartCreateProps,
|
||||
CartUpdateProps,
|
||||
FilterableCartProps,
|
||||
isCart,
|
||||
LineItemUpdate,
|
||||
LineItemValidateData,
|
||||
isCart,
|
||||
} from "../types/cart"
|
||||
import {
|
||||
CustomShippingOptionService,
|
||||
CustomerService,
|
||||
CustomShippingOptionService,
|
||||
DiscountService,
|
||||
EventBusService,
|
||||
GiftCardService,
|
||||
@@ -48,21 +51,16 @@ import {
|
||||
TaxProviderService,
|
||||
TotalsService,
|
||||
} from "."
|
||||
import { DeepPartial, EntityManager, In, IsNull, Not } from "typeorm"
|
||||
import { IPriceSelectionStrategy, TransactionBaseService } from "../interfaces"
|
||||
import { MedusaError, isDefined } from "medusa-core-utils"
|
||||
import IsolateProductDomainFeatureFlag from "../loaders/feature-flags/isolate-product-domain"
|
||||
import SalesChannelFeatureFlag from "../loaders/feature-flags/sales-channels"
|
||||
import { buildQuery, isString, setMetadata } from "../utils"
|
||||
import { isEmpty, isEqual } from "lodash"
|
||||
|
||||
import { AddressRepository } from "../repositories/address"
|
||||
import { CartRepository } from "../repositories/cart"
|
||||
import { FlagRouter } from "@medusajs/utils"
|
||||
import { IsNumber } from "class-validator"
|
||||
import IsolateProductDomainFeatureFlag from "../loaders/feature-flags/isolate-product-domain"
|
||||
import { LineItemRepository } from "../repositories/line-item"
|
||||
import { PaymentSessionInput } from "../types/payment"
|
||||
import { PaymentSessionRepository } from "../repositories/payment-session"
|
||||
import SalesChannelFeatureFlag from "../loaders/feature-flags/sales-channels"
|
||||
import { ShippingMethodRepository } from "../repositories/shipping-method"
|
||||
import { validateEmail } from "../utils/is-email"
|
||||
|
||||
@@ -2753,8 +2751,21 @@ class CartService extends TransactionBaseService {
|
||||
}
|
||||
)
|
||||
|
||||
cart.tax_total = cart.item_tax_total + cart.shipping_tax_total
|
||||
|
||||
cart.raw_discount_total = cart.discount_total
|
||||
cart.discount_total = Math.round(cart.discount_total)
|
||||
|
||||
const giftCardableAmount = this.newTotalsService_.getGiftCardableAmount({
|
||||
gift_cards_taxable: cart.region?.gift_cards_taxable,
|
||||
subtotal: cart.subtotal,
|
||||
discount_total: cart.discount_total,
|
||||
shipping_total: cart.shipping_total,
|
||||
tax_total: cart.tax_total,
|
||||
})
|
||||
|
||||
const giftCardTotal = await this.newTotalsService_.getGiftCardTotals(
|
||||
cart.subtotal - cart.discount_total,
|
||||
giftCardableAmount,
|
||||
{
|
||||
region: cart.region,
|
||||
giftCards: cart.gift_cards,
|
||||
@@ -2764,11 +2775,6 @@ class CartService extends TransactionBaseService {
|
||||
cart.gift_card_total = giftCardTotal.total || 0
|
||||
cart.gift_card_tax_total = giftCardTotal.tax_total || 0
|
||||
|
||||
cart.tax_total = cart.item_tax_total + cart.shipping_tax_total
|
||||
|
||||
cart.raw_discount_total = cart.discount_total
|
||||
cart.discount_total = Math.round(cart.discount_total)
|
||||
|
||||
cart.total =
|
||||
cart.subtotal +
|
||||
cart.shipping_total +
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { FlagRouter } from "@medusajs/utils"
|
||||
import { MedusaError, isDefined } from "medusa-core-utils"
|
||||
import { isDefined, MedusaError } from "medusa-core-utils"
|
||||
import { EntityManager } from "typeorm"
|
||||
import {
|
||||
ITaxCalculationStrategy,
|
||||
@@ -635,6 +635,26 @@ export default class NewTotalsService extends TransactionBaseService {
|
||||
return shippingMethodsTotals
|
||||
}
|
||||
|
||||
getGiftCardableAmount({
|
||||
gift_cards_taxable,
|
||||
subtotal,
|
||||
shipping_total,
|
||||
discount_total,
|
||||
tax_total,
|
||||
}: {
|
||||
gift_cards_taxable?: boolean
|
||||
subtotal: number
|
||||
shipping_total: number
|
||||
discount_total: number
|
||||
tax_total: number
|
||||
}): number {
|
||||
return (
|
||||
(gift_cards_taxable
|
||||
? subtotal + shipping_total - discount_total
|
||||
: subtotal + shipping_total + tax_total - discount_total) || 0
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate and return the shipping method totals
|
||||
* @param shippingMethod
|
||||
|
||||
@@ -725,12 +725,8 @@ class OrderService extends TransactionBaseService {
|
||||
)
|
||||
}
|
||||
|
||||
const giftCardableAmount =
|
||||
(cart.region?.gift_cards_taxable
|
||||
? cart.subtotal! - cart.discount_total!
|
||||
: cart.total! + cart.gift_card_total!) || 0 // we re add the gift card total to compensate the fact that the decorate total already removed this amount from the total
|
||||
let giftCardableAmountBalance = cart.gift_card_total ?? 0
|
||||
|
||||
let giftCardableAmountBalance = giftCardableAmount
|
||||
const giftCardService = this.giftCardService_.withTransaction(manager)
|
||||
|
||||
// Order the gift cards by first ends_at date, then remaining amount. To ensure largest possible amount left, for longest possible time.
|
||||
@@ -739,6 +735,7 @@ class OrderService extends TransactionBaseService {
|
||||
const 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,
|
||||
@@ -1898,8 +1895,20 @@ class OrderService extends TransactionBaseService {
|
||||
}
|
||||
)
|
||||
|
||||
order.item_tax_total = item_tax_total
|
||||
order.shipping_tax_total = shipping_tax_total
|
||||
order.tax_total = item_tax_total + shipping_tax_total
|
||||
|
||||
const giftCardableAmount = this.newTotalsService_.getGiftCardableAmount({
|
||||
gift_cards_taxable: order.region?.gift_cards_taxable,
|
||||
subtotal: order.subtotal,
|
||||
discount_total: order.discount_total,
|
||||
shipping_total: order.shipping_total,
|
||||
tax_total: order.tax_total,
|
||||
})
|
||||
|
||||
const giftCardTotal = await this.newTotalsService_.getGiftCardTotals(
|
||||
order.subtotal - order.discount_total,
|
||||
giftCardableAmount,
|
||||
{
|
||||
region: order.region,
|
||||
giftCards: order.gift_cards,
|
||||
@@ -1909,12 +1918,7 @@ class OrderService extends TransactionBaseService {
|
||||
order.gift_card_total = giftCardTotal.total || 0
|
||||
order.gift_card_tax_total = giftCardTotal.tax_total || 0
|
||||
|
||||
order.item_tax_total = item_tax_total
|
||||
order.shipping_tax_total = shipping_tax_total
|
||||
order.tax_total =
|
||||
order.item_tax_total +
|
||||
order.shipping_tax_total -
|
||||
order.gift_card_tax_total
|
||||
order.tax_total -= order.gift_card_tax_total
|
||||
|
||||
for (const swap of order.swaps ?? []) {
|
||||
swap.additional_items = swap.additional_items.map((item) => {
|
||||
|
||||
Reference in New Issue
Block a user