fix(utils,core-flows): subtotal calculation and returns location (#13497)

* fix(utils,core-flows): subtotal calculation and returns location

* changeset

* fix test

* var

* rm extra field from test

* fix original total

* fix partial refunds and pending difference

* fix test

* fix test

* test

* extract to util

* original total and update payment when receive return

* original_subtotal

* default fields

* test

* calculate pending difference

* revert claims test

* pending difference

* creadit line fix

* if
This commit is contained in:
Carlos R. L. Rodrigues authored and GitHub committed 2025-09-18 17:50:40 +02:00
1 parent 4736c58da5
commit 9563ee446f
37 files changed
+721 -179

No files matched your search

+9
View File
@@ -0,0 +1,9 @@
---
"@medusajs/utils": patch
"@medusajs/types": patch
"@medusajs/core-flows": patch
"@medusajs/order": patch
"@medusajs/medusa": patch
---
fix(utils): subtotal calculation discounting returned items
@@ -22,8 +22,10 @@ medusaIntegrationTestRunner({
let returnReason
let inventoryItem
let inventoryItemExtra
let inventoryItemExtra2
let location
let productExtra
let productExtra2
const shippingProviderId = "manual_test-provider"
beforeEach(async () => {
@@ -123,6 +125,31 @@ medusaIntegrationTestRunner({
)
).data.product
productExtra2 = (
await api.post(
"/admin/products",
{
title: "Extra product 2, same price",
shipping_profile_id: shippingProfile.id,
options: [{ title: "size", values: ["large", "small"] }],
variants: [
{
title: "my variant 2",
sku: "variant-sku-2",
options: { size: "large" },
prices: [
{
currency_code: "usd",
amount: 25,
},
],
},
],
},
adminHeaders
)
).data.product
returnReason = (
await api.post(
"/admin/return-reasons",
@@ -269,6 +296,10 @@ medusaIntegrationTestRunner({
await api.get(`/admin/inventory-items?sku=variant-sku`, adminHeaders)
).data.inventory_items[0]
inventoryItemExtra2 = (
await api.get(`/admin/inventory-items?sku=variant-sku-2`, adminHeaders)
).data.inventory_items[0]
await api.post(
`/admin/inventory-items/${inventoryItemExtra.id}/location-levels`,
{
@@ -278,6 +309,15 @@ medusaIntegrationTestRunner({
adminHeaders
)
await api.post(
`/admin/inventory-items/${inventoryItemExtra2.id}/location-levels`,
{
location_id: location.id,
stocked_quantity: 2,
},
adminHeaders
)
const remoteLink = container.resolve(
ContainerRegistrationKeys.REMOTE_LINK
)
@@ -323,6 +363,14 @@ medusaIntegrationTestRunner({
inventory_item_id: inventoryItemExtra.id,
},
},
{
[Modules.PRODUCT]: {
variant_id: productExtra2.variants[0].id,
},
[Modules.INVENTORY]: {
inventory_item_id: inventoryItemExtra2.id,
},
},
])
// create reservation for inventory item that is initially on the order
@@ -440,6 +488,95 @@ medusaIntegrationTestRunner({
})
describe("Exchanges lifecycle", () => {
it("test full exchange flow", async () => {
const orderBefore = (
await api.get(`/admin/orders/${order.id}`, adminHeaders)
).data.order
let result = await api.post(
"/admin/exchanges",
{
order_id: order.id,
description: "Test",
},
adminHeaders
)
expect(result.data.exchange.created_by).toEqual(expect.any(String))
const exchangeId = result.data.exchange.id
const item = order.items[0]
result = await api.post(
`/admin/exchanges/${exchangeId}/inbound/items`,
{
items: [
{
id: item.id,
reason_id: returnReason.id,
quantity: 2,
},
],
},
adminHeaders
)
// New Item
result = await api.post(
`/admin/exchanges/${exchangeId}/outbound/items`,
{
items: [
{
variant_id: productExtra2.variants[0].id,
quantity: 2,
},
],
},
adminHeaders
)
result = await api.post(
`/admin/exchanges/${exchangeId}/request`,
{},
adminHeaders
)
const returnId = result.data.exchange.return_id
result = (await api.get(`/admin/orders/${order.id}`, adminHeaders)).data
.order
expect(orderBefore.total).toBe(61)
expect(result.total).toBe(112)
// receive return
await api.post(`/admin/returns/${returnId}/receive`, {}, adminHeaders)
await api.post(
`/admin/returns/${returnId}/receive-items`,
{
items: [
{
id: item.id,
quantity: 2,
},
],
},
adminHeaders
)
await api.post(
`/admin/returns/${returnId}/receive/confirm`,
{},
adminHeaders
)
result = (await api.get(`/admin/orders/${order.id}`, adminHeaders)).data
.order
expect(orderBefore.total).toBe(61)
expect(result.total).toBe(62) // +1 is from taxes of the new item
})
it("Full flow with 2 orders", async () => {
let result = await api.post(
"/admin/exchanges",
@@ -6,6 +6,7 @@ import {
AdminStockLocation,
MedusaContainer,
} from "@medusajs/types"
import { ContainerRegistrationKeys, Modules } from "@medusajs/utils"
import {
adminHeaders,
generatePublishableKey,
@@ -175,6 +176,26 @@ export async function createOrderSeeder({
adminHeaders
)
const remoteLink = container.resolve(ContainerRegistrationKeys.LINK)
await remoteLink.create([
{
[Modules.SALES_CHANNEL]: {
sales_channel_id: salesChannel.id,
},
[Modules.STOCK_LOCATION]: {
stock_location_id: stockLocation.id,
},
},
{
[Modules.PRODUCT]: {
variant_id: product.variants[0].id,
},
[Modules.INVENTORY]: {
inventory_item_id: inventoryItem.id,
},
},
])
/**
* Create shipping options for each shipping profile provided
*/
@@ -32,7 +32,35 @@ medusaIntegrationTestRunner({
adminHeaders
)
await api.post(`/admin/claims/${claim.id}/request`, {}, adminHeaders)
const createdClaim = await api.post(
`/admin/claims/${claim.id}/request`,
{},
adminHeaders
)
const returnOrder = createdClaim.data.return
const returnId = returnOrder.id
await api.post(`/admin/returns/${returnId}/receive`, {}, adminHeaders)
let lineItem = returnOrder.items[0].item
await api.post(
`/admin/returns/${returnId}/receive-items`,
{
items: [
{
id: lineItem.id,
quantity: returnOrder.items[0].quantity,
},
],
},
adminHeaders
)
await api.post(
`/admin/returns/${returnId}/receive/confirm`,
{},
adminHeaders
)
}
beforeEach(async () => {
@@ -64,10 +92,6 @@ medusaIntegrationTestRunner({
})
describe("with outstanding amount due to claim", () => {
beforeEach(async () => {
await createClaim({ order })
})
it("should capture an authorized payment", async () => {
const payment = order.payment_collections[0].payments[0]
@@ -189,6 +213,8 @@ medusaIntegrationTestRunner({
adminHeaders
)
await createClaim({ order })
const refundReason = (
await api.post(
`/admin/refund-reasons`,
@@ -253,6 +279,8 @@ medusaIntegrationTestRunner({
)
).data.refund_reason
await createClaim({ order })
await api.post(
`/admin/payments/${payment.id}/refund`,
{
@@ -311,6 +339,8 @@ medusaIntegrationTestRunner({
adminHeaders
)
await createClaim({ order })
await api.post(
`/admin/payments/${payment.id}/refund`,
{ amount: 25 },
@@ -1,9 +1,9 @@
import { medusaIntegrationTestRunner } from "@medusajs/test-utils"
import {
ContainerRegistrationKeys,
Modules,
RuleOperator,
} from "@medusajs/utils"
import { medusaIntegrationTestRunner } from "@medusajs/test-utils"
import {
adminHeaders,
createAdminUser,
@@ -478,6 +478,16 @@ medusaIntegrationTestRunner({
adminHeaders
)
expect(result.data.order_preview.summary).toEqual(
expect.objectContaining({
transaction_total: 0,
current_order_total: 61,
pending_difference: 11,
paid_total: 0,
refunded_total: 0,
})
)
expect(result.data.order_preview).toEqual(
expect.objectContaining({
id: order.id,
@@ -1,8 +1,18 @@
import { createOrderChangeWorkflow, createOrderWorkflow, } from "@medusajs/core-flows"
import {
createOrderChangeWorkflow,
createOrderWorkflow,
} from "@medusajs/core-flows"
import { medusaIntegrationTestRunner } from "@medusajs/test-utils"
import { CreateOrderLineItemDTO, IOrderModuleService, OrderDTO, } from "@medusajs/types"
import {
CreateOrderLineItemDTO,
IOrderModuleService,
OrderDTO,
} from "@medusajs/types"
import { Modules, ProductStatus } from "@medusajs/utils"
import { adminHeaders, createAdminUser, } from "../../../helpers/create-admin-user"
import {
adminHeaders,
createAdminUser,
} from "../../../helpers/create-admin-user"
jest.setTimeout(50000)
@@ -288,6 +298,7 @@ medusaIntegrationTestRunner({
discount_total: 1.1,
discount_tax_total: 0.1,
original_total: 61,
original_subtotal: 60,
original_tax_total: 1,
item_total: 50,
item_subtotal: 50,
@@ -433,6 +444,7 @@ medusaIntegrationTestRunner({
subtotal: 50,
total: 50,
original_total: 50,
original_subtotal: 50,
discount_total: 0,
discount_tax_total: 0,
discount_subtotal: 0,
@@ -490,6 +502,10 @@ medusaIntegrationTestRunner({
precision: 20,
value: "0",
},
raw_original_subtotal: {
precision: 20,
value: "50",
},
raw_return_dismissed_total: {
precision: 20,
value: "0",
@@ -31,24 +31,43 @@ export const addOrderTransactionStep = createStep(
return new StepResponse(null)
}
const existingQuery: any[] = []
for (const trx of trxsData) {
const existing = await service.listOrderTransactions(
{
order_id: trx.order_id,
reference: trx.reference,
reference_id: trx.reference_id,
},
{
select: ["id"],
}
)
existingQuery.push({
order_id: trx.order_id,
reference: trx.reference,
reference_id: trx.reference_id,
})
}
if (existing.length) {
return new StepResponse(null)
const existing = await service.listOrderTransactions(
{
$or: existingQuery,
},
{
select: ["order_id", "reference", "reference_id"],
}
)
const existingSet = new Set<string>(
existing.map(
(trx) => `${trx.order_id}-${trx.reference}-${trx.reference_id}`
)
)
const selectedData: CreateOrderTransactionDTO[] = []
for (const trx of trxsData) {
if (
!existingSet.has(`${trx.order_id}-${trx.reference}-${trx.reference_id}`)
) {
selectedData.push(trx)
}
}
const created = await service.addOrderTransactions(trxsData)
if (!selectedData.length) {
return new StepResponse(null)
}
const created = await service.addOrderTransactions(selectedData)
return new StepResponse(
(Array.isArray(data)
@@ -9,6 +9,7 @@ import {
MedusaError,
OrderStatus,
arrayDifference,
deepFlatMap,
isPresent,
} from "@medusajs/framework/utils"
@@ -21,6 +22,58 @@ export function throwIfOrderIsCancelled({ order }: { order: OrderDTO }) {
}
}
export function throwIfManagedItemsNotStockedAtReturnLocation({
order,
orderReturn,
inputItems,
}: {
order: Pick<OrderDTO, "items">
orderReturn: Pick<ReturnDTO, "location_id">
inputItems: OrderWorkflow.CreateOrderFulfillmentWorkflowInput["items"]
}) {
if (!orderReturn?.location_id) {
return
}
const inputItemIds = new Set(inputItems.map((i) => i.id))
const requestedOrderItems = order.items?.filter((oi: any) =>
inputItemIds.has(oi.id)
)
const invalidManagedItems: string[] = []
for (const orderItem of requestedOrderItems ?? []) {
const variant = (orderItem as any)?.variant
if (!variant?.manage_inventory) {
continue
}
let hasStockAtLocation = false
deepFlatMap(
orderItem,
"variant.inventory_items.inventory.location_levels",
({ location_levels }) => {
if (location_levels?.location_id === orderReturn.location_id) {
hasStockAtLocation = true
}
}
)
if (!hasStockAtLocation) {
invalidManagedItems.push(orderItem.id)
}
}
if (invalidManagedItems.length) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
`Cannot request item return at location ${
orderReturn.location_id
} for managed inventory items: ${invalidManagedItems.join(", ")}`
)
}
}
export function throwIfItemsDoesNotExistsInOrder({
order,
inputItems,
@@ -6,7 +6,12 @@ import {
OrderWorkflow,
ReturnDTO,
} from "@medusajs/framework/types"
import { ChangeActionType, OrderChangeStatus } from "@medusajs/framework/utils"
import {
ChangeActionType,
OrderChangeStatus,
deepFlatMap,
isDefined,
} from "@medusajs/framework/utils"
import {
WorkflowData,
WorkflowResponse,
@@ -23,6 +28,7 @@ import { updateOrderChangesStep } from "../../steps/update-order-changes"
import {
throwIfIsCancelled,
throwIfItemsDoesNotExistsInOrder,
throwIfManagedItemsNotStockedAtReturnLocation,
throwIfOrderChangeIsNotActive,
} from "../../utils/order-validation"
import { createOrderChangeActionsWorkflow } from "../create-order-change-actions"
@@ -105,6 +111,11 @@ export const orderClaimRequestItemReturnValidationStep = createStep(
throwIfIsCancelled(orderReturn, "Return")
throwIfOrderChangeIsNotActive({ orderChange })
throwIfItemsDoesNotExistsInOrder({ order, inputItems: items })
throwIfManagedItemsNotStockedAtReturnLocation({
order,
orderReturn,
inputItems: items,
})
}
)
@@ -154,34 +165,23 @@ export const orderClaimRequestItemReturnWorkflow = createWorkflow(
}).then(() => {
return useRemoteQueryStep({
entry_point: "return",
fields: ["id", "status", "order_id", "canceled_at"],
fields: ["id", "status", "order_id", "location_id", "canceled_at"],
variables: { id: orderClaim.return_id },
list: false,
throw_if_key_not_found: true,
}).config({ name: "return-query" }) as ReturnDTO
})
const createdReturn = when({ orderClaim }, ({ orderClaim }) => {
return !orderClaim.return_id
}).then(() => {
return createReturnsStep([
{
order_id: orderClaim.order_id,
claim_id: orderClaim.id,
},
])
})
const orderReturn: ReturnDTO = transform(
{ createdReturn, existingOrderReturn },
({ createdReturn, existingOrderReturn }) => {
return existingOrderReturn ?? (createdReturn?.[0] as ReturnDTO)
}
)
const order: OrderDTO = useRemoteQueryStep({
entry_point: "orders",
fields: ["id", "status", "items.*"],
fields: [
"id",
"status",
"items.*",
"items.variant.manage_inventory",
"items.variant.inventory_items.inventory_item_id",
"items.variant.inventory_items.inventory.location_levels.location_id",
],
variables: { id: orderClaim.order_id },
list: false,
throw_if_key_not_found: true,
@@ -202,6 +202,51 @@ export const orderClaimRequestItemReturnWorkflow = createWorkflow(
name: "order-change-query",
})
const pickItemLocationId = transform(
{ order, input },
({ order, input }) => {
if (input.location_id) {
return input.location_id
}
// pick the first item location
const item = order?.items?.find(
(item) => item.id === input.items[0].id
) as any
let locationId: string | undefined
deepFlatMap(
item,
"variant.inventory_items.inventory.location_levels",
({ location_levels }) => {
if (!locationId && isDefined(location_levels?.location_id)) {
locationId = location_levels.location_id
}
}
)
return locationId
}
)
const createdReturn = when({ orderClaim }, ({ orderClaim }) => {
return !orderClaim.return_id
}).then(() => {
return createReturnsStep([
{
order_id: orderClaim.order_id,
claim_id: orderClaim.id,
location_id: pickItemLocationId,
},
])
})
const orderReturn: ReturnDTO = transform(
{ createdReturn, existingOrderReturn },
({ createdReturn, existingOrderReturn }) => {
return existingOrderReturn ?? (createdReturn?.[0] as ReturnDTO)
}
)
when({ createdReturn }, ({ createdReturn }) => {
return !!createdReturn?.length
}).then(() => {
@@ -13,8 +13,8 @@ import {
} from "@medusajs/framework/workflows-sdk"
import { useRemoteQueryStep } from "../../common"
import { updatePaymentCollectionStep } from "../../payment-collection"
import { createOrderPaymentCollectionWorkflow } from "./create-order-payment-collection"
import { cancelPaymentCollectionWorkflow } from "../../payment-collection/workflows/cancel-payment-collection"
import { createOrderPaymentCollectionWorkflow } from "./create-order-payment-collection"
/**
* The details of the order payment collection to create or update.
@@ -65,7 +65,7 @@ export const createOrUpdateOrderPaymentCollectionWorkflow = createWorkflow(
) => {
const order = useRemoteQueryStep({
entry_point: "order",
fields: ["id", "summary", "currency_code", "region_id"],
fields: ["id", "summary", "total", "currency_code", "region_id"],
variables: { id: input.order_id },
throw_if_key_not_found: true,
list: false,
@@ -90,7 +90,7 @@ export const createOrderCreditLinesWorkflow = createWorkflow(
) => {
const orderQuery = useQueryGraphStep({
entity: "orders",
fields: ["id", "status", "summary"],
fields: ["id", "status", "summary", "total"],
filters: { id: input.id },
options: { throwIfKeyNotFound: true },
}).config({ name: "get-order" })
@@ -5,8 +5,8 @@ import {
createWorkflow,
transform,
} from "@medusajs/framework/workflows-sdk"
import { createRemoteLinkStep, useRemoteQueryStep } from "../../common"
import { createPaymentCollectionsStep } from "../../cart"
import { createRemoteLinkStep, useRemoteQueryStep } from "../../common"
/**
* The details of the payment collection to create.
@@ -46,12 +46,10 @@ export const createOrderPaymentCollectionWorkflowId =
*/
export const createOrderPaymentCollectionWorkflow = createWorkflow(
createOrderPaymentCollectionWorkflowId,
(
input: WorkflowData<CreateOrderPaymentCollectionWorkflowInput>
) => {
(input: WorkflowData<CreateOrderPaymentCollectionWorkflowInput>) => {
const order = useRemoteQueryStep({
entry_point: "order",
fields: ["id", "summary", "currency_code", "region_id"],
fields: ["id", "summary", "total", "currency_code", "region_id"],
variables: { id: input.order_id },
throw_if_key_not_found: true,
list: false,
@@ -6,14 +6,19 @@ import {
OrderWorkflow,
ReturnDTO,
} from "@medusajs/framework/types"
import { ChangeActionType, OrderChangeStatus } from "@medusajs/framework/utils"
import {
WorkflowData,
WorkflowResponse,
ChangeActionType,
deepFlatMap,
isDefined,
OrderChangeStatus,
} from "@medusajs/framework/utils"
import {
createStep,
createWorkflow,
transform,
when,
WorkflowData,
WorkflowResponse,
} from "@medusajs/framework/workflows-sdk"
import { useRemoteQueryStep } from "../../../common"
import { updateOrderExchangesStep } from "../../steps/exchange/update-order-exchanges"
@@ -23,6 +28,7 @@ import { updateOrderChangesStep } from "../../steps/update-order-changes"
import {
throwIfIsCancelled,
throwIfItemsDoesNotExistsInOrder,
throwIfManagedItemsNotStockedAtReturnLocation,
throwIfOrderChangeIsNotActive,
} from "../../utils/order-validation"
import { createOrderChangeActionsWorkflow } from "../create-order-change-actions"
@@ -106,6 +112,11 @@ export const exchangeRequestItemReturnValidationStep = createStep(
throwIfIsCancelled(orderReturn, "Return")
throwIfOrderChangeIsNotActive({ orderChange })
throwIfItemsDoesNotExistsInOrder({ order, inputItems: items })
throwIfManagedItemsNotStockedAtReturnLocation({
order,
orderReturn,
inputItems: items,
})
}
)
@@ -144,7 +155,7 @@ export const orderExchangeRequestItemReturnWorkflow = createWorkflow(
): WorkflowResponse<OrderPreviewDTO> {
const orderExchange = useRemoteQueryStep({
entry_point: "order_exchange",
fields: ["id", "order_id", "return_id", "canceled_at"],
fields: ["id", "order_id", "return_id", "location_id", "canceled_at"],
variables: { id: input.exchange_id },
list: false,
throw_if_key_not_found: true,
@@ -162,27 +173,16 @@ export const orderExchangeRequestItemReturnWorkflow = createWorkflow(
}).config({ name: "return-query" }) as ReturnDTO
})
const createdReturn = when({ orderExchange }, ({ orderExchange }) => {
return !orderExchange.return_id
}).then(() => {
return createReturnsStep([
{
order_id: orderExchange.order_id,
exchange_id: orderExchange.id,
},
])
})
const orderReturn: ReturnDTO = transform(
{ createdReturn, existingOrderReturn, orderExchange },
({ createdReturn, existingOrderReturn, orderExchange }) => {
return existingOrderReturn ?? (createdReturn?.[0] as ReturnDTO)
}
)
const order: OrderDTO = useRemoteQueryStep({
entry_point: "orders",
fields: ["id", "status", "items.*"],
fields: [
"id",
"status",
"items.*",
"items.variant.manage_inventory",
"items.variant.inventory_items.inventory_item_id",
"items.variant.inventory_items.inventory.location_levels.location_id",
],
variables: { id: orderExchange.order_id },
list: false,
throw_if_key_not_found: true,
@@ -204,6 +204,51 @@ export const orderExchangeRequestItemReturnWorkflow = createWorkflow(
status: [OrderChangeStatus.PENDING, OrderChangeStatus.REQUESTED],
})
const pickItemLocationId = transform(
{ order, input },
({ order, input }) => {
if (input.location_id) {
return input.location_id
}
// pick the first item location
const item = order?.items?.find(
(item) => item.id === input.items[0].id
) as any
let locationId: string | undefined
deepFlatMap(
item,
"variant.inventory_items.inventory.location_levels",
({ location_levels }) => {
if (!locationId && isDefined(location_levels?.location_id)) {
locationId = location_levels.location_id
}
}
)
return locationId
}
)
const createdReturn = when({ orderExchange }, ({ orderExchange }) => {
return !orderExchange.return_id
}).then(() => {
return createReturnsStep([
{
order_id: orderExchange.order_id,
location_id: pickItemLocationId,
exchange_id: orderExchange.id,
},
])
})
const orderReturn: ReturnDTO = transform(
{ createdReturn, existingOrderReturn, orderExchange },
({ createdReturn, existingOrderReturn, orderExchange }) => {
return existingOrderReturn ?? (createdReturn?.[0] as ReturnDTO)
}
)
when({ createdReturn }, ({ createdReturn }) => {
return !!createdReturn?.length
}).then(() => {
@@ -234,6 +234,7 @@ export const markOrderFulfillmentAsDeliveredWorkflow = createWorkflow(
fields: [
"id",
"summary",
"total",
"currency_code",
"region_id",
"fulfillments.id",
@@ -42,7 +42,7 @@ export const createOrderRefundCreditLinesWorkflow = createWorkflow(
) {
const orderQuery = useQueryGraphStep({
entity: "orders",
fields: ["id", "status", "summary", "payment_collections.id"],
fields: ["id", "status", "summary", "total", "payment_collections.id"],
filters: { id: input.order_id },
options: { throwIfKeyNotFound: true },
}).config({ name: "get-order" })
@@ -29,6 +29,7 @@ export const refundCapturedPaymentsWorkflow = createWorkflow(
"id",
"status",
"summary",
"total",
"payment_collections.payments.id",
"payment_collections.payments.amount",
"payment_collections.payments.refunds.id",
@@ -34,6 +34,7 @@ import {
throwIfIsCancelled,
throwIfOrderChangeIsNotActive,
} from "../../utils/order-validation"
import { createOrUpdateOrderPaymentCollectionWorkflow } from "../create-or-update-order-payment-collection"
/**
* The data to validate that a return receival can be confirmed.
@@ -363,7 +364,15 @@ export const confirmReturnReceiveWorkflow = createWorkflow(
orderId: order.id,
confirmed_by: input.confirmed_by,
}),
adjustInventoryLevelsStep(inventoryAdjustment),
adjustInventoryLevelsStep(inventoryAdjustment)
)
parallelize(
createOrUpdateOrderPaymentCollectionWorkflow.runAsStep({
input: {
order_id: order.id,
},
}),
emitEventStep({
eventName: OrderWorkflowEvents.RETURN_RECEIVED,
data: {
@@ -1,13 +1,13 @@
import {
AdditionalData,
BigNumberInput,
CreateOrderShippingMethodDTO,
FulfillmentWorkflow,
OrderDTO,
ReturnDTO,
OrderWorkflow,
ReturnDTO,
ShippingOptionDTO,
WithCalculatedPrice,
AdditionalData,
} from "@medusajs/framework/types"
import {
MathBN,
@@ -25,6 +25,7 @@ import {
parallelize,
transform,
} from "@medusajs/framework/workflows-sdk"
import { pricingContextResult } from "../../../cart/utils/schemas"
import {
createRemoteLinkStep,
emitEventStep,
@@ -38,7 +39,6 @@ import {
throwIfOrderIsCancelled,
} from "../../utils/order-validation"
import { validateReturnReasons } from "../../utils/validate-return-reason"
import { pricingContextResult } from "../../../cart/utils/schemas"
function prepareShippingMethodData({
orderId,
@@ -423,6 +423,7 @@ export const createAndCompleteReturnOrderWorkflow = createWorkflow(
const returnCreated = createCompleteReturnStep({
order_id: input.order_id,
location_id: input.location_id,
items: input.items,
shipping_method: shippingMethodData,
created_by: input.created_by,
@@ -146,31 +146,33 @@ export const refundPaymentWorkflow = createWorkflow(
const order = useRemoteQueryStep({
entry_point: "order",
fields: ["id", "summary", "currency_code", "region_id"],
fields: ["id", "summary", "total", "currency_code", "region_id"],
variables: { id: orderPaymentCollection.order.id },
throw_if_key_not_found: true,
list: false,
}).config({ name: "order" })
validateRefundStep({ order, payment, amount: input.amount })
refundPaymentStep(input)
const refundPayment = refundPaymentStep(input)
when({ orderPaymentCollection }, ({ orderPaymentCollection }) => {
return !!orderPaymentCollection?.order?.id
}).then(() => {
const orderTransactionData = transform(
{ input, payment, orderPaymentCollection },
({ input, payment, orderPaymentCollection }) => {
return {
order_id: orderPaymentCollection.order.id,
amount: MathBN.mult(
input.amount ?? payment.raw_amount ?? payment.amount,
-1
),
currency_code: payment.currency_code ?? order.currency_code,
reference_id: payment.id,
reference: "refund",
}
{ input, refundPayment, orderPaymentCollection, order },
({ input, refundPayment, orderPaymentCollection, order }) => {
return refundPayment.refunds?.map((refund) => {
return {
order_id: orderPaymentCollection.order.id,
amount: MathBN.mult(
input.amount ?? refund.raw_amount ?? refund.amount,
-1
),
currency_code: refundPayment.currency_code ?? order.currency_code,
reference_id: refund.id,
reference: "refund",
}
})
}
)
+1 -1
View File
@@ -1578,7 +1578,7 @@ export interface CreateOrderReturnDTO extends BaseOrderBundledActionsDTO {
/**
* The ID of the location to return the items to.
*/
location_id?: string
location_id?: string | null
/**
* The items of the return.
@@ -73,6 +73,10 @@ export interface OrderExchangeRequestItemReturnWorkflowInput {
* The ID of the return that's associated with the exchange.
*/
return_id: string
/**
* The ID of the location to return the items to.
*/
location_id?: string
/**
* The ID of the exchange to add the inbound items to.
*/
@@ -95,6 +99,10 @@ export interface OrderClaimRequestItemReturnWorkflowInput {
* The ID of the return that's associated with the claim.
*/
return_id: string
/**
* The ID of the location to return the items to.
*/
location_id?: string
/**
* The ID of the claim to add the items to.
*/
@@ -41,6 +41,7 @@ describe("Total calculation", function () {
subtotal: 60,
total: 66,
original_total: 66,
original_subtotal: 60,
discount_total: 0,
discount_subtotal: 0,
discount_tax_total: 0,
@@ -60,6 +61,7 @@ describe("Total calculation", function () {
subtotal: 5,
total: 7.5,
original_total: 7.5,
original_subtotal: 5,
discount_total: 0,
discount_subtotal: 0,
discount_tax_total: 0,
@@ -78,6 +80,7 @@ describe("Total calculation", function () {
item_tax_total: 8.5,
item_discount_total: 0,
original_total: 73.5,
original_subtotal: 65,
original_tax_total: 8.5,
original_item_subtotal: 65,
original_item_total: 73.5,
@@ -132,6 +135,7 @@ describe("Total calculation", function () {
subtotal: 100,
total: 99,
original_total: 110,
original_subtotal: 100,
discount_total: 11,
discount_subtotal: 10,
discount_tax_total: 1,
@@ -146,6 +150,7 @@ describe("Total calculation", function () {
discount_subtotal: 10,
discount_tax_total: 1,
original_total: 110,
original_subtotal: 100,
original_tax_total: 10,
item_total: 99,
item_subtotal: 100,
@@ -253,6 +258,7 @@ describe("Total calculation", function () {
subtotal: 90,
total: 89.1,
original_total: 99,
original_subtotal: 90,
discount_total: 9.9,
discount_subtotal: 9,
discount_tax_total: 0.9,
@@ -280,6 +286,7 @@ describe("Total calculation", function () {
subtotal: 9,
total: 6.6,
original_total: 9.9,
original_subtotal: 9,
discount_total: 3.3,
discount_subtotal: 3,
discount_tax_total: 0.3,
@@ -308,6 +315,7 @@ describe("Total calculation", function () {
subtotal: 90,
total: 89.1,
original_total: 99,
original_subtotal: 90,
discount_total: 9.9,
discount_subtotal: 9,
discount_tax_total: 0.9,
@@ -334,6 +342,7 @@ describe("Total calculation", function () {
subtotal: 9,
total: 6.6,
original_total: 9.9,
original_subtotal: 9,
discount_total: 3.3,
discount_subtotal: 3,
discount_tax_total: 0.3,
@@ -348,6 +357,7 @@ describe("Total calculation", function () {
discount_subtotal: 24,
discount_tax_total: 2.4,
original_total: 217.8,
original_subtotal: 198,
original_tax_total: 19.8,
item_total: 95.7,
item_subtotal: 99,
@@ -431,6 +441,7 @@ describe("Total calculation", function () {
subtotal: 90.9090909090909,
total: 100,
original_total: 100,
original_subtotal: 90.9090909090909,
discount_total: 0,
discount_tax_total: 0,
tax_total: 9.090909090909092,
@@ -444,6 +455,7 @@ describe("Total calculation", function () {
discount_total: 0,
discount_tax_total: 0,
original_total: 100,
original_subtotal: 90.9090909090909,
original_tax_total: 9.090909090909092,
item_total: 100,
item_subtotal: 90.9090909090909,
@@ -478,6 +490,7 @@ describe("Total calculation", function () {
discount_tax_total: 0,
tax_total: 10,
original_tax_total: 10,
original_subtotal: 100,
},
],
total: 110,
@@ -487,6 +500,7 @@ describe("Total calculation", function () {
discount_total: 0,
discount_tax_total: 0,
original_total: 110,
original_subtotal: 100,
original_tax_total: 10,
item_total: 110,
item_subtotal: 100,
@@ -516,6 +530,7 @@ describe("Total calculation", function () {
],
subtotal: 90.9090909090909,
total: 100,
original_subtotal: 90.9090909090909,
original_total: 100,
discount_total: 0,
discount_tax_total: 0,
@@ -537,6 +552,7 @@ describe("Total calculation", function () {
subtotal: 100,
total: 110,
original_total: 110,
original_subtotal: 100,
discount_total: 0,
discount_tax_total: 0,
tax_total: 10,
@@ -550,6 +566,7 @@ describe("Total calculation", function () {
discount_total: 0,
discount_tax_total: 0,
original_total: 210,
original_subtotal: 190.9090909090909,
original_tax_total: 19.09090909090909,
item_total: 210,
item_subtotal: 190.9090909090909,
@@ -606,6 +623,7 @@ describe("Total calculation", function () {
is_tax_inclusive: true,
original_total: 120,
original_subtotal: 100,
original_tax_total: 20,
discount_subtotal: 8.333333333333334,
@@ -637,6 +655,7 @@ describe("Total calculation", function () {
original_item_total: 120,
original_tax_total: 20,
original_total: 120,
original_subtotal: 100,
discount_subtotal: 8.333333333333334,
discount_tax_total: 1.6666666666666667,
@@ -712,6 +731,7 @@ describe("Total calculation", function () {
subtotal: 100,
total: 88,
original_total: 110,
original_subtotal: 100,
discount_total: 22,
discount_subtotal: 20,
discount_tax_total: 2,
@@ -739,6 +759,7 @@ describe("Total calculation", function () {
subtotal: 25,
total: 25.3,
original_total: 27.5,
original_subtotal: 25,
discount_total: 2.2,
discount_subtotal: 2,
discount_tax_total: 0.2,
@@ -753,6 +774,7 @@ describe("Total calculation", function () {
discount_subtotal: 22,
discount_tax_total: 2.2,
original_total: 137.5,
original_subtotal: 125,
original_tax_total: 12.5,
item_total: 88,
item_subtotal: 100,
@@ -836,8 +858,8 @@ describe("Total calculation", function () {
tax_lines: [
{
rate: 10,
total: 8,
subtotal: 10,
total: 0,
subtotal: 0,
},
],
adjustments: [
@@ -847,14 +869,15 @@ describe("Total calculation", function () {
total: 22,
},
],
subtotal: 100,
total: 88,
original_total: 110,
discount_total: 22,
discount_subtotal: 20,
discount_tax_total: 2,
tax_total: 8,
original_tax_total: 10,
subtotal: 0,
total: 0,
original_total: 0,
original_subtotal: 0,
discount_total: 0,
discount_subtotal: 0,
discount_tax_total: 0,
tax_total: 0,
original_tax_total: 0,
refundable_total_per_unit: 0,
refundable_total: 0,
fulfilled_total: 88,
@@ -865,21 +888,22 @@ describe("Total calculation", function () {
write_off_total: 44,
},
],
total: 48,
subtotal: 100,
tax_total: 8,
discount_total: 22,
discount_subtotal: 20,
discount_tax_total: 2,
original_total: 110,
original_tax_total: 10,
item_total: 88,
item_subtotal: 100,
item_tax_total: 8,
item_discount_total: 22,
original_item_total: 110,
original_item_subtotal: 100,
original_item_tax_total: 10,
total: -40,
subtotal: 0,
tax_total: 0,
discount_total: 0,
discount_subtotal: 0,
discount_tax_total: 0,
original_total: 0,
original_subtotal: 0,
original_tax_total: 0,
item_total: 0,
item_subtotal: 0,
item_tax_total: 0,
item_discount_total: 0,
original_item_total: 0,
original_item_subtotal: 0,
original_item_tax_total: 0,
fulfilled_total: 88,
shipped_total: 88,
return_requested_total: 0,
@@ -923,6 +947,7 @@ describe("Total calculation", function () {
{
rate: 19,
subtotal: 22.61,
total: 0,
},
],
adjustments: [
@@ -935,6 +960,7 @@ describe("Total calculation", function () {
subtotal: 119,
total: 0,
original_total: 141.61,
original_subtotal: 119,
discount_total: 141.61,
discount_subtotal: 119,
discount_tax_total: 22.61,
@@ -949,6 +975,7 @@ describe("Total calculation", function () {
discount_subtotal: 119,
discount_tax_total: 22.61,
original_total: 141.61,
original_subtotal: 119,
original_tax_total: 22.61,
item_total: 0,
item_subtotal: 119,
@@ -1026,6 +1053,7 @@ describe("Total calculation", function () {
discount_total: 0,
is_tax_inclusive: true,
original_tax_total: 19,
original_subtotal: 100,
original_total: 119,
quantity: 1,
subtotal: 100,
@@ -1045,6 +1073,7 @@ describe("Total calculation", function () {
original_item_tax_total: 19,
original_item_total: 119,
original_tax_total: 19,
original_subtotal: 100,
original_total: 119,
subtotal: 100,
tax_total: 19,
@@ -1069,6 +1098,7 @@ describe("Total calculation", function () {
discount_total: 0,
is_tax_inclusive: false,
original_tax_total: 22.61,
original_subtotal: 119,
original_total: 141.61,
quantity: 1,
subtotal: 119,
@@ -1088,6 +1118,7 @@ describe("Total calculation", function () {
original_item_tax_total: 22.61,
original_item_total: 141.61,
original_tax_total: 22.61,
original_subtotal: 119,
original_total: 141.61,
subtotal: 119,
tax_total: 22.61,
@@ -1113,6 +1144,7 @@ describe("Total calculation", function () {
is_tax_inclusive: true,
original_tax_total: 19,
original_total: 119,
original_subtotal: 100,
quantity: 1,
subtotal: 100,
tax_lines: [
@@ -1132,6 +1164,7 @@ describe("Total calculation", function () {
discount_total: 0,
is_tax_inclusive: false,
original_tax_total: 22.61,
original_subtotal: 119,
original_total: 141.61,
quantity: 1,
subtotal: 119,
@@ -1150,6 +1183,7 @@ describe("Total calculation", function () {
original_item_subtotal: 219,
original_item_tax_total: 41.61,
original_item_total: 260.61,
original_subtotal: 219,
original_tax_total: 41.61,
original_total: 260.61,
subtotal: 219,
@@ -1206,6 +1240,7 @@ describe("Total calculation", function () {
discount_total: 119,
is_tax_inclusive: true,
original_tax_total: 19,
original_subtotal: 100,
original_total: 119,
quantity: 1,
subtotal: 100,
@@ -1213,6 +1248,7 @@ describe("Total calculation", function () {
{
rate: 19,
subtotal: 19,
total: 0,
},
],
tax_total: 0,
@@ -1224,6 +1260,7 @@ describe("Total calculation", function () {
original_item_tax_total: 19,
original_item_total: 119,
original_tax_total: 19,
original_subtotal: 100,
original_total: 119,
subtotal: 100,
tax_total: 0,
@@ -4,9 +4,11 @@ import { BigNumber } from "../big-number"
import { MathBN } from "../math"
export function calculateAdjustmentTotal({
item,
adjustments,
taxRate,
}: {
item?: { quantity: BigNumberInput }
adjustments: Pick<AdjustmentLineDTO, "amount" | "is_tax_inclusive">[]
taxRate?: BigNumberInput
}) {
@@ -40,9 +42,24 @@ export function calculateAdjustmentTotal({
adj["total"] = new BigNumber(adjustmentsTotal)
}
const quantity = item?.quantity || MathBN.convert(1)
let adjustmentPerItem = MathBN.convert(0)
let adjustmentSubtotalPerItem = MathBN.convert(0)
let adjustmentTaxTotalPerItem = MathBN.convert(0)
if (!MathBN.eq(quantity, 0)) {
adjustmentPerItem = MathBN.div(adjustmentsTotal, quantity)
adjustmentSubtotalPerItem = MathBN.div(adjustmentsSubtotal, quantity)
adjustmentTaxTotalPerItem = MathBN.div(adjustmentsTaxTotal, quantity)
}
return {
adjustmentsTotal,
adjustmentsSubtotal,
adjustmentsTaxTotal,
adjustmentPerItem,
adjustmentSubtotalPerItem,
adjustmentTaxTotalPerItem,
}
}
+24 -1
View File
@@ -101,7 +101,8 @@ export function decorateCartTotals(
let shippingDiscountTotal = MathBN.convert(0)
const cartItems = items.map((item, index) => {
const itemTotals = Object.assign(item, itemsTotals[item.id ?? index] ?? {})
const rawTotals = itemsTotals[item.id ?? index] ?? {}
const itemTotals = Object.assign(item, rawTotals)
const itemSubtotal = itemTotals.subtotal
const itemTotal = MathBN.convert(itemTotals.total)
@@ -208,6 +209,10 @@ export function decorateCartTotals(
// TODO: Gift Card calculations
const originalTotal = MathBN.add(itemsOriginalTotal, shippingOriginalTotal)
const originalSubtotal = MathBN.add(
itemsOriginalSubtotal,
shippingOriginalSubtotal
)
// TODO: subtract (cart.gift_card_total + cart.gift_card_tax_total)
const tempTotal = MathBN.add(subtotal, taxTotal)
@@ -231,6 +236,7 @@ export function decorateCartTotals(
// cart.gift_card_tax_total = giftCardTotal.tax_total || 0
cart.original_total = new BigNumber(originalTotal)
cart.original_subtotal = new BigNumber(originalSubtotal)
cart.original_tax_total = new BigNumber(originalTaxTotal)
// cart.original_gift_card_total =
@@ -264,5 +270,22 @@ export function decorateCartTotals(
cart.original_shipping_total = new BigNumber(shippingOriginalTotal)
}
// Calculate pending return total
if (cart.summary) {
const pendingReturnTotal = MathBN.sum(
0,
...(cart.items?.map((item) => item.return_requested_total ?? 0) ?? [0])
)
const pendingDifference = new BigNumber(
MathBN.sub(
MathBN.sub(cart.total, pendingReturnTotal),
cart.summary?.transaction_total ?? 0
)
)
cart.summary.pending_difference = pendingDifference
}
return cart
}
@@ -33,6 +33,7 @@ export interface GetItemTotalOutput {
unit_price: BigNumber
subtotal: BigNumber
original_subtotal: BigNumber
total: BigNumber
original_total: BigNumber
@@ -76,8 +77,7 @@ export function getLineItemsTotals(
function setRefundableTotal(
item: GetItemTotalInput,
discountsTotal: BigNumberInput,
totals: GetItemTotalOutput,
context: GetLineItemsTotalsContext
totals: GetItemTotalOutput
) {
const itemDetail = item.detail!
const totalReturnedQuantity = MathBN.sum(
@@ -127,54 +127,107 @@ function getLineItemTotals(
? MathBN.div(totalItemPrice, MathBN.add(1, sumTaxRate))
: totalItemPrice
// Proportional discounts to current quantity and compute taxes on the current net amount
const {
adjustmentsTotal: discountsTotal,
adjustmentsSubtotal: discountsSubtotal,
adjustmentsTaxTotal: discountTaxTotal,
adjustmentsSubtotal: discountsSubtotalFull,
adjustmentSubtotalPerItem,
} = calculateAdjustmentTotal({
item,
adjustments: item.adjustments || [],
taxRate: sumTaxRate,
})
const itemDetail = item.detail!
const totalReturnedQuantity = MathBN.sum(
itemDetail?.return_received_quantity ?? 0,
itemDetail?.return_dismissed_quantity ?? 0
)
const currentQuantity = MathBN.sub(item.quantity, totalReturnedQuantity)
const currentTotalItemPrice = MathBN.mult(item.unit_price, currentQuantity)
const currentSubtotal = isTaxInclusive
? MathBN.div(currentTotalItemPrice, MathBN.add(1, sumTaxRate))
: currentTotalItemPrice
const currentDiscountsSubtotal = MathBN.mult(
adjustmentSubtotalPerItem ?? 0,
currentQuantity
)
const taxTotal = calculateTaxTotal({
taxLines: item.tax_lines || [],
taxableAmount: MathBN.sub(subtotal, discountsSubtotal),
taxableAmount: MathBN.sub(currentSubtotal, currentDiscountsSubtotal),
setTotalField: "total",
})
const originalTaxTotal = calculateTaxTotal({
taxLines: item.tax_lines || [],
taxableAmount: subtotal,
taxableAmount: currentSubtotal,
setTotalField: "subtotal",
})
// Compute full-quantity net total after discounts and taxes to derive per-unit totals
const fullDiscountedTaxable = MathBN.sub(subtotal, discountsSubtotalFull ?? 0)
const taxTotalFull = calculateTaxTotal({
taxLines: item.tax_lines || [],
taxableAmount: fullDiscountedTaxable,
})
const fullNetTotal = MathBN.sum(fullDiscountedTaxable, taxTotalFull)
const totals: GetItemTotalOutput = {
quantity: item.quantity,
unit_price: item.unit_price,
subtotal: new BigNumber(subtotal),
subtotal: new BigNumber(currentSubtotal),
total: new BigNumber(
MathBN.sum(MathBN.sub(subtotal, discountsSubtotal), taxTotal)
MathBN.sum(
MathBN.sub(currentSubtotal, currentDiscountsSubtotal),
taxTotal
)
),
original_subtotal: new BigNumber(
MathBN.sub(
isTaxInclusive
? currentTotalItemPrice
: MathBN.add(currentSubtotal, originalTaxTotal),
originalTaxTotal
)
),
original_total: new BigNumber(
isTaxInclusive ? totalItemPrice : MathBN.add(subtotal, originalTaxTotal)
isTaxInclusive
? currentTotalItemPrice
: MathBN.add(currentSubtotal, originalTaxTotal)
),
discount_total: new BigNumber(discountsTotal),
discount_subtotal: new BigNumber(discountsSubtotal),
discount_tax_total: new BigNumber(discountTaxTotal),
// Discount values prorated to the current quantity
discount_subtotal: new BigNumber(currentDiscountsSubtotal),
discount_tax_total: new BigNumber(MathBN.sub(originalTaxTotal, taxTotal)),
discount_total: new BigNumber(
MathBN.add(
currentDiscountsSubtotal,
MathBN.sub(originalTaxTotal, taxTotal)
)
),
tax_total: new BigNumber(taxTotal),
original_tax_total: new BigNumber(originalTaxTotal),
}
if (isDefined(item.detail?.return_requested_quantity)) {
setRefundableTotal(item, discountsTotal, totals, context)
if (
isDefined(item.detail?.return_requested_quantity) ||
isDefined(item.detail?.return_received_quantity) ||
isDefined(item.detail?.return_dismissed_quantity)
) {
setRefundableTotal(item, discountsTotal, totals)
}
// Per-unit total should be based on full-quantity net total to support lifecycle totals consistently
const div = MathBN.eq(item.quantity, 0) ? 1 : item.quantity
const totalPerUnit = MathBN.div(totals.total, div)
const totalPerUnit = MathBN.div(fullNetTotal, div)
const optionalFields = {
...(context.extraQuantityFields ?? {}),
@@ -183,10 +236,9 @@ function getLineItemTotals(
for (const field in optionalFields) {
const totalField = optionalFields[field]
let target = item[totalField]
if (field.includes(".")) {
target = pickValueFromObject(field, item)
}
let target = field.includes(".")
? pickValueFromObject(field, item)
: item[field]
if (!isDefined(target)) {
continue
@@ -20,6 +20,7 @@ export interface GetShippingMethodTotalOutput {
amount: BigNumber
subtotal: BigNumber
original_subtotal: BigNumber
total: BigNumber
original_total: BigNumber
@@ -96,6 +97,14 @@ export function getShippingMethodTotals(
total: new BigNumber(
MathBN.sum(MathBN.sub(subtotal, discountsSubtotal), taxTotal)
),
original_subtotal: new BigNumber(
MathBN.sub(
isTaxInclusive
? shippingMethodAmount
: MathBN.add(subtotal, originalTaxTotal),
originalTaxTotal
)
),
original_total: new BigNumber(
isTaxInclusive
? shippingMethodAmount
@@ -11,10 +11,6 @@ export function calculateTaxTotal({
taxableAmount: BigNumberInput
setTotalField?: string
}) {
if (MathBN.lte(taxableAmount, 0)) {
return MathBN.convert(0)
}
let taxTotal = MathBN.convert(0)
for (const taxLine of taxLines) {
@@ -128,6 +128,7 @@ export type AdminPostClaimsAddItemsReqSchemaType = z.infer<
>
export const AdminPostClaimsRequestReturnItemsReqSchema = z.object({
location_id: z.string().optional(),
items: z.array(
z.object({
id: z.string(),
@@ -7,6 +7,7 @@ export const defaultAdminListOrderFields = [
"region_id",
"*items",
"summary",
"total",
"metadata",
"created_at",
"updated_at",
@@ -31,6 +32,7 @@ export const defaultAdminOrderFields = [
"*shipping_methods.tax_lines",
"*shipping_methods.adjustments",
"summary",
"total",
"metadata",
"created_at",
"updated_at",
@@ -127,6 +127,7 @@ export type AdminPostExchangesAddItemsReqSchemaType = z.infer<
>
export const AdminPostExchangesReturnRequestItemsReqSchema = z.object({
location_id: z.string().optional(),
items: z.array(
z.object({
id: z.string(),
@@ -4,6 +4,7 @@ export const defaultAdminOrderFields = [
"status",
"version",
"summary",
"total",
"metadata",
"created_at",
"updated_at",
@@ -19,6 +20,7 @@ export const defaultAdminRetrieveOrderFields = [
"discount_total",
"discount_tax_total",
"original_total",
"original_subtotal",
"original_tax_total",
"item_total",
"item_subtotal",
@@ -27,6 +27,7 @@ export const defaultStoreRetrieveOrderFields = [
"discount_subtotal",
"discount_tax_total",
"original_total",
"original_subtotal",
"original_tax_total",
"item_total",
"item_subtotal",
@@ -2934,6 +2934,7 @@ moduleIntegrationTestRunner<ICartModuleService>({
subtotal: 100,
total: 0,
original_total: 100,
original_subtotal: 100,
discount_total: 100,
discount_subtotal: 100,
discount_tax_total: 0,
@@ -2951,6 +2952,10 @@ moduleIntegrationTestRunner<ICartModuleService>({
value: "100",
precision: 20,
},
raw_original_subtotal: {
value: "100",
precision: 20,
},
raw_discount_total: {
value: "100",
precision: 20,
@@ -3042,6 +3047,7 @@ moduleIntegrationTestRunner<ICartModuleService>({
subtotal: 400,
total: 200,
original_total: 400,
original_subtotal: 400,
discount_total: 200,
discount_subtotal: 200,
discount_tax_total: 0,
@@ -3059,6 +3065,10 @@ moduleIntegrationTestRunner<ICartModuleService>({
value: "400",
precision: 20,
},
raw_original_subtotal: {
value: "400",
precision: 20,
},
raw_discount_total: {
value: "200",
precision: 20,
@@ -3104,6 +3114,7 @@ moduleIntegrationTestRunner<ICartModuleService>({
subtotal: 10,
total: 10,
original_total: 10,
original_subtotal: 10,
discount_total: 0,
discount_subtotal: 0,
discount_tax_total: 0,
@@ -3121,6 +3132,10 @@ moduleIntegrationTestRunner<ICartModuleService>({
value: "10",
precision: 20,
},
raw_original_subtotal: {
value: "10",
precision: 20,
},
raw_discount_total: {
value: "0",
precision: 20,
@@ -3166,6 +3181,7 @@ moduleIntegrationTestRunner<ICartModuleService>({
discount_subtotal: 300,
discount_tax_total: 0,
original_total: 510,
original_subtotal: 510,
original_tax_total: 0,
item_total: 200,
item_subtotal: 500,
@@ -3217,6 +3233,10 @@ moduleIntegrationTestRunner<ICartModuleService>({
value: "510",
precision: 20,
},
raw_original_subtotal: {
value: "510",
precision: 20,
},
raw_original_tax_total: {
value: "0",
precision: 20,
@@ -227,7 +227,7 @@ moduleIntegrationTestRunner<IOrderModuleService>({
const serializedOrder = JSON.parse(
JSON.stringify(
await service.retrieveOrder(created.id, {
select: ["id", "summary"],
select: ["id", "summary", "total"],
})
)
)
@@ -246,7 +246,7 @@ moduleIntegrationTestRunner<IOrderModuleService>({
const serializedOrder2 = JSON.parse(
JSON.stringify(
await service.retrieveOrder(created.id, {
select: ["id", "summary"],
select: ["id", "summary", "total"],
})
)
)
@@ -271,7 +271,7 @@ moduleIntegrationTestRunner<IOrderModuleService>({
const serializedOrder3 = JSON.parse(
JSON.stringify(
await service.retrieveOrder(created.id, {
select: ["id", "summary"],
select: ["id", "summary", "total"],
})
)
)
@@ -290,7 +290,7 @@ moduleIntegrationTestRunner<IOrderModuleService>({
const serializedOrder4 = JSON.parse(
JSON.stringify(
await service.retrieveOrder(created.id, {
select: ["id", "summary"],
select: ["id", "summary", "total"],
})
)
)
@@ -414,7 +414,7 @@ moduleIntegrationTestRunner<IOrderModuleService>({
})
const changedOrder = await service.retrieveOrder(createdOrder.id, {
select: ["total", "items.detail", "summary"],
select: ["total", "items.detail", "summary", "total"],
relations: ["items"],
})
@@ -492,7 +492,7 @@ moduleIntegrationTestRunner<IOrderModuleService>({
})
const modified = await service.retrieveOrder(createdOrder.id, {
select: ["total", "items.detail", "summary"],
select: ["total", "items.detail", "summary", "total"],
relations: ["items"],
})
@@ -358,6 +358,7 @@ export default class OrderModuleService
"discount_tax_total",
"original_total",
"original_tax_total",
"pending_difference",
"item_total",
"item_subtotal",
"item_tax_total",
@@ -370,9 +371,13 @@ export default class OrderModuleService
"original_shipping_tax_total",
"original_shipping_subtotal",
"original_shipping_total",
"original_total",
"original_subtotal",
"original_tax_total",
"credit_line_total",
"credit_line_tax_total",
"credit_line_subtotal",
"refundable_amount",
]
const includeTotals = (config?.select ?? []).some((field) =>
@@ -224,7 +224,7 @@ export class OrderChangeProcessing {
return orderSummary
}
// Calculate the order summary from a calculated order including taxes
// Returns the order summary from a calculated order including taxes
public getSummaryFromOrder(order: OrderDTO): OrderSummaryDTO {
const summary_ = this.summary
const total = order.total
@@ -241,35 +241,6 @@ export class OrderChangeProcessing {
orderSummary.accounting_total = orderSummary.current_order_total
orderSummary.pending_difference = MathBN.sub(
orderSummary.current_order_total,
orderSummary.transaction_total
)
// return total becomes pending difference
for (const item of order.items ?? []) {
const item_ = item as any
;[
"return_requested_total",
"return_received_total",
// TODO: revisit this when we settle on which dismissed items need to be refunded
// "return_dismissed_total",
].forEach((returnTotalKey) => {
const returnTotal = item_[returnTotalKey]
if (MathBN.gt(returnTotal, 0)) {
orderSummary.pending_difference = MathBN.sub(
orderSummary.pending_difference,
returnTotal
)
}
})
}
orderSummary.pending_difference = new BigNumber(
orderSummary.pending_difference
)
return orderSummary
}