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:
committed by
GitHub
parent
4736c58da5
commit
9563ee446f
@@ -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.
|
||||
@@ -27,10 +27,10 @@ export const createOrderPaymentCollectionWorkflowId =
|
||||
/**
|
||||
* This workflow creates a payment collection for an order. It's used by the
|
||||
* [Create Payment Collection Admin API Route](https://docs.medusajs.com/api/admin#payment-collections_postpaymentcollections).
|
||||
*
|
||||
*
|
||||
* You can use this workflow within your customizations or your own custom workflows, allowing you to wrap custom logic around
|
||||
* creating a payment collection for an order.
|
||||
*
|
||||
*
|
||||
* @example
|
||||
* const { result } = await createOrderPaymentCollectionWorkflow(container)
|
||||
* .run({
|
||||
@@ -39,19 +39,17 @@ export const createOrderPaymentCollectionWorkflowId =
|
||||
* amount: 10,
|
||||
* }
|
||||
* })
|
||||
*
|
||||
*
|
||||
* @summary
|
||||
*
|
||||
*
|
||||
* Create a payment collection for an order.
|
||||
*/
|
||||
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.
|
||||
@@ -56,14 +57,14 @@ export type ConfirmReceiveReturnValidationStepInput = {
|
||||
/**
|
||||
* This step validates that a return receival can be confirmed.
|
||||
* If the order or return is canceled, or the order change is not active, the step will throw an error.
|
||||
*
|
||||
*
|
||||
* :::note
|
||||
*
|
||||
*
|
||||
* You can retrieve an order, return, and order change details using [Query](https://docs.medusajs.com/learn/fundamentals/module-links/query),
|
||||
* or [useQueryGraphStep](https://docs.medusajs.com/resources/references/medusa-workflows/steps/useQueryGraphStep).
|
||||
*
|
||||
*
|
||||
* :::
|
||||
*
|
||||
*
|
||||
* @example
|
||||
* const data = confirmReceiveReturnValidationStep({
|
||||
* order: {
|
||||
@@ -182,10 +183,10 @@ export const confirmReturnReceiveWorkflowId = "confirm-return-receive"
|
||||
/**
|
||||
* This workflow confirms a return receival request. It's used by the
|
||||
* [Confirm Return Receival Admin API Route](https://docs.medusajs.com/api/admin#returns_postreturnsidreceiveconfirm).
|
||||
*
|
||||
*
|
||||
* You can use this workflow within your customizations or your own custom workflows, allowing you
|
||||
* to confirm a return receival in your custom flow.
|
||||
*
|
||||
*
|
||||
* @example
|
||||
* const { result } = await confirmReturnReceiveWorkflow(container)
|
||||
* .run({
|
||||
@@ -193,9 +194,9 @@ export const confirmReturnReceiveWorkflowId = "confirm-return-receive"
|
||||
* return_id: "return_123",
|
||||
* }
|
||||
* })
|
||||
*
|
||||
*
|
||||
* @summary
|
||||
*
|
||||
*
|
||||
* Confirm a return receival request.
|
||||
*/
|
||||
export const confirmReturnReceiveWorkflow = createWorkflow(
|
||||
@@ -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,
|
||||
@@ -311,11 +311,11 @@ export const createAndCompleteReturnOrderWorkflowId =
|
||||
* @summary
|
||||
*
|
||||
* Create and complete a return for an order.
|
||||
*
|
||||
*
|
||||
* @property hooks.setPricingContext - This hook is executed before the return's shipping method is created. You can consume this hook to return any custom context useful for the prices retrieval of the shipping method's option.
|
||||
*
|
||||
*
|
||||
* For example, assuming you have the following custom pricing rule:
|
||||
*
|
||||
*
|
||||
* ```json
|
||||
* {
|
||||
* "attribute": "location_id",
|
||||
@@ -323,13 +323,13 @@ export const createAndCompleteReturnOrderWorkflowId =
|
||||
* "value": "sloc_123",
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
*
|
||||
* You can consume the `setPricingContext` hook to add the `location_id` context to the prices calculation:
|
||||
*
|
||||
*
|
||||
* ```ts
|
||||
* import { createAndCompleteReturnOrderWorkflow } from "@medusajs/medusa/core-flows";
|
||||
* import { StepResponse } from "@medusajs/workflows-sdk";
|
||||
*
|
||||
*
|
||||
* createAndCompleteReturnOrderWorkflow.hooks.setPricingContext((
|
||||
* { order, additional_data }, { container }
|
||||
* ) => {
|
||||
@@ -338,13 +338,13 @@ export const createAndCompleteReturnOrderWorkflowId =
|
||||
* });
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
*
|
||||
* The price of the shipping method's option will now be retrieved using the context you return.
|
||||
*
|
||||
*
|
||||
* :::note
|
||||
*
|
||||
*
|
||||
* Learn more about prices calculation context in the [Prices Calculation](https://docs.medusajs.com/resources/commerce-modules/pricing/price-calculation) documentation.
|
||||
*
|
||||
*
|
||||
* :::
|
||||
*/
|
||||
export const createAndCompleteReturnOrderWorkflow = createWorkflow(
|
||||
@@ -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",
|
||||
}
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
@@ -124,11 +132,11 @@ export interface DeleteRequestItemReturnWorkflowInput {
|
||||
|
||||
/**
|
||||
* The details of the received item to be removed.
|
||||
*
|
||||
*
|
||||
* @property return_id - The ID of the return to remove the item from.
|
||||
* @property action_id - The ID of the action associated with the item to remove.
|
||||
* Every item has an `actions` property, whose value is an array of actions.
|
||||
* You can find an action with the name `RECEIVE_RETURN_ITEM` using its `action` property,
|
||||
* Every item has an `actions` property, whose value is an array of actions.
|
||||
* You can find an action with the name `RECEIVE_RETURN_ITEM` using its `action` property,
|
||||
* and use the value of its `id` property.
|
||||
*/
|
||||
export interface DeleteRequestItemReceiveReturnWorkflowInput
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user