fix(order): calculate taxes on order edit flows (#11518)

* fix(order): calcualte taxes on order edit flows

* merge summary

* tests

* fix pending difference

* comments

* claim test

* revert method
This commit is contained in:
Carlos R. L. Rodrigues
2025-02-21 15:27:40 +01:00
committed by GitHub
parent 065df75e7d
commit 0c957350a6
19 changed files with 501 additions and 442 deletions
+6
View File
@@ -0,0 +1,6 @@
---
"@medusajs/order": patch
"@medusajs/types": patch
---
fix(order): order edit flows with calculated taxes
@@ -436,7 +436,6 @@ medusaIntegrationTestRunner({
expect(orderResult.summary).toEqual(
expect.objectContaining({
paid_total: 61,
difference_sum: 0,
refunded_total: 0,
transaction_total: 61,
pending_difference: 0,
@@ -732,7 +731,7 @@ medusaIntegrationTestRunner({
expect(paymentCollections[0]).toEqual(
expect.objectContaining({
status: "not_paid",
amount: 109.5,
amount: 113.21,
currency_code: "usd",
})
)
@@ -776,7 +775,7 @@ medusaIntegrationTestRunner({
})
it("should create a payment collection successfully & mark as paid", async () => {
const paymentDelta = 109.5
const paymentDelta = 113.21
const orderForPayment = (
await api.get(`/admin/orders/${order.id}`, adminHeaders)
).data.order
@@ -1182,9 +1181,9 @@ medusaIntegrationTestRunner({
expect(orderCheck.summary).toEqual(
expect.objectContaining({
pending_difference: -11,
current_order_total: 50,
original_order_total: 60,
pending_difference: -9.7,
current_order_total: 76.3,
original_order_total: 61,
})
)
@@ -368,7 +368,7 @@ medusaIntegrationTestRunner({
)
).data.order_preview
expect(result.summary.current_order_total).toEqual(84)
expect(result.summary.current_order_total).toEqual(86.4)
expect(result.summary.original_order_total).toEqual(60)
// Update item quantity and unit_price with the same amount as we have originally should not change totals
@@ -383,7 +383,7 @@ medusaIntegrationTestRunner({
)
).data.order_preview
expect(result.summary.current_order_total).toEqual(84)
expect(result.summary.current_order_total).toEqual(86.4)
expect(result.summary.original_order_total).toEqual(60)
// Update item quantity, but keep the price as it was originally, should add + 25 to previous amount
@@ -398,7 +398,7 @@ medusaIntegrationTestRunner({
)
).data.order_preview
expect(result.summary.current_order_total).toEqual(109)
expect(result.summary.current_order_total).toEqual(111.4)
expect(result.summary.original_order_total).toEqual(60)
// Update item quantity, with a new price
@@ -417,7 +417,7 @@ medusaIntegrationTestRunner({
)
).data.order_preview
expect(result.summary.current_order_total).toEqual(124)
expect(result.summary.current_order_total).toEqual(126.4)
expect(result.summary.original_order_total).toEqual(60)
const updatedItem = result.items.find((i) => i.id === item.id)
@@ -456,7 +456,7 @@ medusaIntegrationTestRunner({
)
).data.order_preview
expect(result.summary.current_order_total).toEqual(34)
expect(result.summary.current_order_total).toEqual(36.4)
expect(result.summary.original_order_total).toEqual(60)
expect(result.items.length).toEqual(2)
@@ -469,7 +469,7 @@ medusaIntegrationTestRunner({
).data.order_preview
expect(result.order_change.status).toEqual(OrderChangeStatus.REQUESTED)
expect(result.summary.current_order_total).toEqual(34)
expect(result.summary.current_order_total).toEqual(36.4)
expect(result.summary.original_order_total).toEqual(60)
expect(result.items.length).toEqual(2)
@@ -387,7 +387,6 @@ medusaIntegrationTestRunner({
status: "canceled",
summary: expect.objectContaining({
credit_line_total: 106,
current_order_total: 0,
accounting_total: 0,
}),
@@ -448,7 +447,6 @@ medusaIntegrationTestRunner({
status: "canceled",
summary: expect.objectContaining({
credit_line_total: 106,
current_order_total: 0,
accounting_total: 0,
}),
@@ -518,7 +516,6 @@ medusaIntegrationTestRunner({
status: "canceled",
summary: expect.objectContaining({
credit_line_total: 106,
current_order_total: 0,
accounting_total: 0,
}),
@@ -232,7 +232,6 @@ medusaIntegrationTestRunner({
tax_total: 6,
summary: expect.objectContaining({
paid_total: 0,
difference_sum: 0,
refunded_total: 0,
transaction_total: 0,
pending_difference: 106,
@@ -267,7 +266,6 @@ medusaIntegrationTestRunner({
tax_total: 6,
summary: expect.objectContaining({
paid_total: 0,
difference_sum: 0,
refunded_total: 0,
transaction_total: 0,
pending_difference: 106,
@@ -300,24 +298,18 @@ medusaIntegrationTestRunner({
orderResult = (await api.get(`/admin/orders/${order.id}`, adminHeaders))
.data.order
// After confirming a claim with an outbound item, the tax totals are not updated
// I suspect this will be the same for promotions and discount totals
// Additionally, the items during claim don't have taxes included in them.
// TODO: this needs to be fixed
expect(orderResult).toEqual(
expect.objectContaining({
total: 206,
total: 212,
subtotal: 200,
tax_total: 6,
tax_total: 12,
summary: expect.objectContaining({
paid_total: 0,
difference_sum: 100,
refunded_total: 0,
transaction_total: 0,
pending_difference: 200,
// TODO: I think the current_order_total and original_order_total should include taxes and adjustments as well
current_order_total: 200,
original_order_total: 100,
pending_difference: 212,
current_order_total: 212,
original_order_total: 106,
}),
})
)
@@ -329,8 +321,7 @@ medusaIntegrationTestRunner({
expect(pendingPaymentCollection).toEqual(
expect.objectContaining({
status: "not_paid",
// TODO: The payment should also include taxes
amount: 200,
amount: 212,
})
)
@@ -342,21 +333,20 @@ medusaIntegrationTestRunner({
)
).data.payment_collection
// TODO: The payment, payment sessions and collection should also include taxes
expect(paymentCollection).toEqual(
expect.objectContaining({
amount: 200,
amount: 212,
status: "completed",
payment_sessions: [
expect.objectContaining({
status: "authorized",
amount: 200,
amount: 212,
}),
],
payments: [
expect.objectContaining({
provider_id: "pp_system_default",
amount: 200,
amount: 212,
}),
],
})
@@ -365,25 +355,19 @@ medusaIntegrationTestRunner({
orderResult = (await api.get(`/admin/orders/${order.id}`, adminHeaders))
.data.order
// Totals summary after payment has been marked as paid
// Totals summarked as paidy after payment has been mar
expect(orderResult).toEqual(
expect.objectContaining({
total: 206,
total: 212,
subtotal: 200,
tax_total: 6,
tax_total: 12,
summary: expect.objectContaining({
// TODO: Paid total should include taxes
paid_total: 200,
// TODO: difference_sum should include taxes
difference_sum: 100,
paid_total: 212,
refunded_total: 0,
// TODO: difference_sum should include taxes
transaction_total: 200,
transaction_total: 212,
pending_difference: 0,
// TODO: difference_sum should include taxes
current_order_total: 200,
// TODO: difference_sum should include taxes
original_order_total: 100,
current_order_total: 212,
original_order_total: 106,
}),
})
)
@@ -419,20 +403,18 @@ medusaIntegrationTestRunner({
// After fulfillment, the taxes seems to now be considered.
// The case now is that you need to now request additional payment from
// the customer
// TODO: This shouldn't be a surprise during fulfillment
expect(orderResult).toEqual(
expect.objectContaining({
total: 206,
total: 212,
subtotal: 200,
tax_total: 6,
tax_total: 12,
summary: expect.objectContaining({
paid_total: 200,
difference_sum: 0,
paid_total: 212,
refunded_total: 0,
transaction_total: 200,
pending_difference: 6,
current_order_total: 206,
original_order_total: 206,
transaction_total: 212,
pending_difference: 0,
current_order_total: 212,
original_order_total: 212,
}),
})
)
@@ -460,17 +442,16 @@ medusaIntegrationTestRunner({
// Nothing changes from the previous expectation
expect(orderResult).toEqual(
expect.objectContaining({
total: 206,
total: 212,
subtotal: 200,
tax_total: 6,
tax_total: 12,
summary: expect.objectContaining({
paid_total: 200,
difference_sum: 0,
paid_total: 212,
refunded_total: 0,
transaction_total: 200,
pending_difference: 6,
current_order_total: 206,
original_order_total: 206,
transaction_total: 212,
pending_difference: 0,
current_order_total: 212,
original_order_total: 212,
}),
})
)
@@ -516,18 +497,16 @@ medusaIntegrationTestRunner({
expect(orderResult).toEqual(
expect.objectContaining({
// This now adds a shipping_tax_total, but the item_tax_total hasn't been updated
total: 321.9,
total: 333.9,
subtotal: 315,
tax_total: 6.9,
tax_total: 18.9,
summary: expect.objectContaining({
paid_total: 200,
difference_sum: 15,
paid_total: 212,
refunded_total: 0,
transaction_total: 200,
// TODO: what happened to the previous pending difference of 6
pending_difference: 15,
current_order_total: 215,
original_order_total: 200,
transaction_total: 212,
pending_difference: 15.9,
current_order_total: 333.9,
original_order_total: 212,
}),
})
)
@@ -551,17 +530,16 @@ medusaIntegrationTestRunner({
// Nothing changes from the previous expectation
expect(orderResult).toEqual(
expect.objectContaining({
total: 321.9,
total: 333.9,
subtotal: 315,
tax_total: 6.9,
tax_total: 18.9,
summary: expect.objectContaining({
paid_total: 200,
difference_sum: 15,
paid_total: 212,
refunded_total: 0,
transaction_total: 200,
pending_difference: 15,
current_order_total: 215,
original_order_total: 200,
transaction_total: 212,
pending_difference: 15.9,
current_order_total: 333.9,
original_order_total: 212,
}),
})
)
@@ -598,18 +576,16 @@ medusaIntegrationTestRunner({
expect(orderResult).toEqual(
expect.objectContaining({
total: 421.9,
total: 439.9,
subtotal: 415,
tax_total: 6.9,
tax_total: 24.9,
summary: expect.objectContaining({
paid_total: 200,
difference_sum: 0,
paid_total: 212,
refunded_total: 0,
transaction_total: 200,
// TODO: Tax totals seems to be added after every claim confirmation as well
pending_difference: 115.9,
current_order_total: 315.9,
original_order_total: 315.9,
transaction_total: 212,
pending_difference: 15.9,
current_order_total: 439.9,
original_order_total: 333.9,
}),
})
)
@@ -621,8 +597,7 @@ medusaIntegrationTestRunner({
expect(pendingPaymentCollection).toEqual(
expect.objectContaining({
status: "not_paid",
// TODO: The payment should also include taxes
amount: 115.9,
amount: 15.9,
})
)
@@ -634,21 +609,20 @@ medusaIntegrationTestRunner({
)
).data.payment_collection
// TODO: The payment, payment sessions and collection should also include taxes
expect(paymentCollection).toEqual(
expect.objectContaining({
amount: 115.9,
amount: 15.9,
status: "completed",
payment_sessions: [
expect.objectContaining({
status: "authorized",
amount: 115.9,
amount: 15.9,
}),
],
payments: [
expect.objectContaining({
provider_id: "pp_system_default",
amount: 115.9,
amount: 15.9,
}),
],
})
@@ -658,20 +632,18 @@ medusaIntegrationTestRunner({
.data.order
// Totals summary after payment has been marked as paid
// TODO: There is a discrepancy between total and paid_total
expect(orderResult).toEqual(
expect.objectContaining({
total: 421.9,
total: 439.9,
subtotal: 415,
tax_total: 6.9,
tax_total: 24.9,
summary: expect.objectContaining({
paid_total: 315.9,
difference_sum: 0,
paid_total: 227.9,
refunded_total: 0,
transaction_total: 315.9,
transaction_total: 227.9,
pending_difference: 0,
current_order_total: 315.9,
original_order_total: 315.9,
current_order_total: 439.9,
original_order_total: 333.9,
}),
})
)
@@ -74,14 +74,13 @@ export const setOrderTaxLinesForItemsStep = createStep(
const itemsTaxLinesData = normalizeItemTaxLinesForOrder(item_tax_lines)
const setItemTaxLinesPromise = itemsTaxLinesData.length
? orderService.setOrderLineItemTaxLines(order.id, itemsTaxLinesData)
? orderService.upsertOrderLineItemTaxLines(itemsTaxLinesData)
: void 0
const shippingTaxLinesData =
normalizeShippingTaxLinesForOrder(shipping_tax_lines)
const setShippingTaxLinesPromise = shippingTaxLinesData.length
? await orderService.setOrderShippingMethodTaxLines(
order.id,
? await orderService.upsertOrderShippingMethodTaxLines(
shippingTaxLinesData
)
: void 0
@@ -105,14 +104,13 @@ export const setOrderTaxLinesForItemsStep = createStep(
return
}
const { order, existingLineItemTaxLines, existingShippingMethodTaxLines } =
const { existingLineItemTaxLines, existingShippingMethodTaxLines } =
revertData
const orderService = container.resolve<IOrderModuleService>(Modules.ORDER)
if (existingLineItemTaxLines) {
await orderService.setOrderLineItemTaxLines(
order.id,
await orderService.upsertOrderLineItemTaxLines(
existingLineItemTaxLines.map((taxLine) => ({
description: taxLine.description,
tax_rate_id: taxLine.tax_rate_id,
@@ -124,8 +122,7 @@ export const setOrderTaxLinesForItemsStep = createStep(
)
}
await orderService.setOrderShippingMethodTaxLines(
order.id,
await orderService.upsertOrderShippingMethodTaxLines(
existingShippingMethodTaxLines.map((taxLine) => ({
description: taxLine.description,
tax_rate_id: taxLine.tax_rate_id,
@@ -1,3 +1,4 @@
import { OrderSummaryDTO } from "../../order/common"
import { BigNumberRawValue, BigNumberValue } from "../../totals"
/**
@@ -1086,91 +1087,4 @@ interface FulfillmentOrderShippingMethodDTO {
*
* The order summary details.
*/
type FulfillmentOrderSummaryDTO = {
/**
* The total of the order summary.
*/
total: BigNumberValue
/**
* The subtotal of the order summary.
*/
subtotal: BigNumberValue
/**
* The total tax of the order summary.
*/
total_tax: BigNumberValue
/**
* The ordered total of the order summary.
*/
ordered_total: BigNumberValue
/**
* The fulfilled total of the order summary.
*/
fulfilled_total: BigNumberValue
/**
* The returned total of the order summary.
*/
returned_total: BigNumberValue
/**
* The return request total of the order summary.
*/
return_request_total: BigNumberValue
/**
* The write off total of the order summary.
*/
write_off_total: BigNumberValue
/**
* The projected total of the order summary.
*/
projected_total: BigNumberValue
/**
* The net total of the order summary.
*/
net_total: BigNumberValue
/**
* The net subtotal of the order summary.
*/
net_subtotal: BigNumberValue
/**
* The net total tax of the order summary.
*/
net_total_tax: BigNumberValue
/**
* The balance of the order summary.
*/
balance: BigNumberValue
/**
* The paid total of the order summary.
*/
paid_total: BigNumberValue
/**
* The refunded total of the order summary.
*/
refunded_total: BigNumberValue
/**
* The pending difference of the order.
*/
pending_difference: BigNumberValue
/**
* The raw pending difference of the order.
*
* @ignore
*/
raw_pending_difference: BigNumberRawValue
}
type FulfillmentOrderSummaryDTO = OrderSummaryDTO
+5 -38
View File
@@ -8,46 +8,13 @@ import { BaseProduct, BaseProductVariant } from "../product/common"
import { BaseReturn } from "../return/common"
export interface BaseOrderSummary {
/**
* The total of the order including taxes and promotions.
*/
total: number
/**
* The total of the order excluding taxes, including promotions.
*/
subtotal: number
/**
* The tax totals of the order including promotions.
*/
total_tax: number
/**
* The total ordered amount.
*/
ordered_total: number
/**
* The total fulfilled amount.
*/
fulfilled_total: number
/**
* The total amount of returned items.
*/
returned_total: number
/**
* The total amount of the items requested to be returned.
*/
return_request_total: number
/**
* The total amount of the items removed from the order.
*/
write_off_total: number
/**
* The total amount paid.
*/
pending_difference: number
current_order_total: number
original_order_total: number
transaction_total: number
paid_total: number
/**
* The total amount refunded
*/
refunded_total: number
accounting_total: number
}
export interface BaseOrderAdjustmentLine {
+19 -95
View File
@@ -41,104 +41,23 @@ export type OrderChangeStatus =
* The order summary details.
*/
export type OrderSummaryDTO = {
/**
* The total of the order summary.
*/
total: BigNumberValue
/**
* The subtotal of the order summary.
*/
subtotal: BigNumberValue
/**
* The total tax of the order summary.
*/
total_tax: BigNumberValue
/**
* The ordered total of the order summary.
*/
ordered_total: BigNumberValue
/**
* The fulfilled total of the order summary.
*/
fulfilled_total: BigNumberValue
/**
* The returned total of the order summary.
*/
returned_total: BigNumberValue
/**
* The return request total of the order summary.
*/
return_request_total: BigNumberValue
/**
* The write off total of the order summary.
*/
write_off_total: BigNumberValue
/**
* The projected total of the order summary.
*/
projected_total: BigNumberValue
/**
* The net total of the order summary.
*/
net_total: BigNumberValue
/**
* The net subtotal of the order summary.
*/
net_subtotal: BigNumberValue
/**
* The net total tax of the order summary.
*/
net_total_tax: BigNumberValue
/**
* The balance of the order summary.
*/
balance: BigNumberValue
/**
* The paid total of the order summary.
*/
paid_total: BigNumberValue
/**
* The refunded total of the order summary.
*/
refunded_total: BigNumberValue
/**
* The pending difference of the order.
*/
pending_difference: BigNumberValue
current_order_total: BigNumberValue
original_order_total: BigNumberValue
transaction_total: BigNumberValue
paid_total: BigNumberValue
refunded_total: BigNumberValue
credit_line_total: BigNumberValue
accounting_total: BigNumberValue
/**
* The raw pending difference of the order.
*
* @ignore
*/
raw_pending_difference: BigNumberRawValue
/**
* The sum difference of all actions
*/
difference_sum: BigNumberValue
/**
* The raw sum difference of all actions
*
* @ignore
*/
raw_difference_sum: BigNumberRawValue
raw_current_order_total: BigNumberRawValue
raw_original_order_total: BigNumberRawValue
raw_transaction_total: BigNumberRawValue
raw_paid_total: BigNumberRawValue
raw_refunded_total: BigNumberRawValue
raw_credit_line_total: BigNumberRawValue
raw_accounting_total: BigNumberRawValue
}
/**
@@ -2359,6 +2278,11 @@ export interface OrderTransactionDTO {
*/
order_id: string
/**
* The associated order version
*/
version: number
/**
* The associated order
*
+119
View File
@@ -1590,6 +1590,35 @@ export interface IOrderModuleService extends IModuleService {
sharedContext?: Context
): Promise<OrderLineItemAdjustmentDTO[]>
/**
* This method upserts line item adjustments.
*
* @param {UpsertOrderLineItemAdjustmentDTO[]} data - The line item adjustments to create or update. If the `id` property is provided
* in an object, it means an existing line item adjustment will be updated. Otherwise, a new one is created.
* @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module.
* @returns {Promise<OrderLineItemAdjustmentDTO[]>} The line item adjustments.
*
* @example
* const lineItemAdjustments = await orderModuleService.upsertOrderLineItemAdjustments(
* [
* {
* item_id: "1234",
* amount: 10
* },
* {
* id: "123",
* item_id: "4321",
* amount: 20
* }
* ]
* )
*
*/
upsertOrderLineItemAdjustments(
data: UpsertOrderLineItemAdjustmentDTO[],
sharedContext?: Context
): Promise<OrderLineItemAdjustmentDTO[]>
/**
* This method sets the line item adjustments of an order.
*
@@ -1786,6 +1815,39 @@ export interface IOrderModuleService extends IModuleService {
sharedContext?: Context
): Promise<OrderShippingMethodAdjustmentDTO[]>
/**
* This method upserts shipping method adjustments.
*
* @param {(CreateOrderShippingMethodAdjustmentDTO | UpdateOrderShippingMethodAdjustmentDTO)[]} data - The shipping method adjustments to be created
* or updated. If an adjustment object has an `id` property, it's updated. Otherwise, a new adjustment is created.
* @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module.
* @returns {Promise<OrderShippingMethodAdjustmentDTO[]>} The shipping method adjustments.
*
* @example
* const shippingMethodAdjustments = await orderModuleService
* .upsertOrderShippingMethodAdjustments(
* [
* {
* shipping_method_id: "123",
* code: "50OFF",
* amount: 5
* },
* {
* id: "321",
* amount: 5
* }
* ]
* )
*
*/
upsertOrderShippingMethodAdjustments(
data: (
| CreateOrderShippingMethodAdjustmentDTO
| UpdateOrderShippingMethodAdjustmentDTO
)[],
sharedContext?: Context
): Promise<OrderShippingMethodAdjustmentDTO[]>
/**
* This method sets the shipping method adjustments of an order.
*
@@ -1985,6 +2047,31 @@ export interface IOrderModuleService extends IModuleService {
sharedContext?: Context
): Promise<OrderLineItemTaxLineDTO[]>
/**
* This method upserts line item tax lines.
*
* @param {(CreateOrderLineItemTaxLineDTO | UpdateOrderLineItemTaxLineDTO)[]} taxLines - The line item tax lines to create or update. If the
* tax line object has an `id` property, it'll be updated. Otherwise, a tax line is created.
* @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module.
* @returns {Promise<OrderLineItemTaxLineDTO[]>} The line item tax lines.
*
* @example
* const lineItemTaxLines = await orderModuleService
* .upsertOrderLineItemTaxLines(
* [
* {
* code: "123",
* rate: 2
* }
* ]
* )
*
*/
upsertOrderLineItemTaxLines(
taxLines: (CreateOrderLineItemTaxLineDTO | UpdateOrderLineItemTaxLineDTO)[],
sharedContext?: Context
): Promise<OrderLineItemTaxLineDTO[]>
/**
* This method sets the line item tax lines of an order.
*
@@ -2176,6 +2263,38 @@ export interface IOrderModuleService extends IModuleService {
sharedContext?: Context
): Promise<OrderShippingMethodTaxLineDTO[]>
/**
* This method upsert shipping method tax lines.
*
* @param {(CreateOrderShippingMethodTaxLineDTO | UpdateOrderShippingMethodTaxLineDTO)[]} taxLines - The shipping method tax lines to create or update.
* If a tax line object has an `id` property, it's updated. Otherwise, a tax line is created.
* @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module.
* @returns {Promise<OrderShippingMethodTaxLineDTO[]>} The shipping method tax lines.
*
* @example
* const shippingMethodTaxLines = await orderModuleService
* .upsertOrderShippingMethodTaxLines(
* [
* {
* code: "123",
* rate: 2
* },
* {
* id: "321",
* rate: 2
* }
* ]
* )
*
*/
upsertOrderShippingMethodTaxLines(
taxLines: (
| CreateOrderShippingMethodTaxLineDTO
| UpdateOrderShippingMethodTaxLineDTO
)[],
sharedContext?: Context
): Promise<OrderShippingMethodTaxLineDTO[]>
/**
* This method set the shipping method tax lines of an order.
*
@@ -36,13 +36,13 @@ moduleIntegrationTestRunner<IOrderModuleService>({
is_discountable: true,
is_tax_inclusive: true,
compare_at_unit_price: 10,
unit_price: 8,
unit_price: 20,
tax_lines: [
{
description: "Tax 1",
tax_rate_id: "tax_usa",
code: "code",
rate: 0.1,
rate: 10,
provider_id: "taxify_master",
},
],
@@ -110,7 +110,7 @@ moduleIntegrationTestRunner<IOrderModuleService>({
],
transactions: [
{
amount: 58,
amount: 48.9,
currency_code: "USD",
reference: "payment",
reference_id: "pay_123",
@@ -211,9 +211,9 @@ moduleIntegrationTestRunner<IOrderModuleService>({
expect(created.summary).toEqual(
expect.objectContaining({
transaction_total: 68,
pending_difference: -20.10799200799201,
paid_total: 68,
transaction_total: 58.9,
pending_difference: 0,
paid_total: 58.9,
refunded_total: 0,
})
)
@@ -236,9 +236,9 @@ moduleIntegrationTestRunner<IOrderModuleService>({
expect(serializedOrder.summary).toEqual(
expect.objectContaining({
transaction_total: 48,
pending_difference: -0.10799200799201,
paid_total: 68,
transaction_total: 38.9,
pending_difference: 20,
paid_total: 58.9,
refunded_total: 20,
})
)
@@ -255,9 +255,9 @@ moduleIntegrationTestRunner<IOrderModuleService>({
expect(serializedOrder2.summary).toEqual(
expect.objectContaining({
transaction_total: 68,
pending_difference: -20.10799200799201,
paid_total: 68,
transaction_total: 58.9,
pending_difference: 0,
paid_total: 58.9,
refunded_total: 0,
})
)
@@ -280,10 +280,10 @@ moduleIntegrationTestRunner<IOrderModuleService>({
expect(serializedOrder3.summary).toEqual(
expect.objectContaining({
paid_total: 68,
paid_total: 58.9,
refunded_total: 50,
transaction_total: 18,
pending_difference: 29.89200799200799,
transaction_total: 8.9,
pending_difference: 50,
})
)
@@ -299,10 +299,10 @@ moduleIntegrationTestRunner<IOrderModuleService>({
expect(serializedOrder4.summary).toEqual(
expect.objectContaining({
paid_total: 68,
paid_total: 58.9,
refunded_total: 70,
transaction_total: -2,
pending_difference: 49.89200799200799,
transaction_total: -11.1,
pending_difference: 70,
})
)
})
@@ -365,7 +365,7 @@ moduleIntegrationTestRunner<IOrderModuleService>({
id: createdOrder.id,
transactions: [
expect.objectContaining({
amount: 58,
amount: 48.9,
reference: "payment",
reference_id: "pay_123",
}),
@@ -2016,6 +2016,7 @@ moduleIntegrationTestRunner<IOrderModuleService>({
])
)
await service.deleteOrderLineItemTaxLines(taxLines.map((o) => o.id))
await service.setOrderLineItemTaxLines(createdOrder.id, [
{
item_id: itemOne.id,
@@ -2087,7 +2088,7 @@ moduleIntegrationTestRunner<IOrderModuleService>({
])
)
await service.setOrderLineItemTaxLines(createdOrder.id, [])
await service.deleteOrderLineItemTaxLines(taxLines.map((o) => o.id))
const order = await service.retrieveOrder(createdOrder.id, {
relations: ["items.item.tax_lines"],
@@ -2240,14 +2241,13 @@ moduleIntegrationTestRunner<IOrderModuleService>({
// create
{
item_id: itemOne.id,
rate: 25,
rate: 32,
code: "TX-2",
} as CreateOrderLineItemTaxLineDTO,
// delete: should delete the initial tax line for itemOne
])
const order = await service.retrieveOrder(createdOrder.id, {
relations: ["items.item.tax_lines"],
relations: ["items.tax_lines"],
})
const serialized = JSON.parse(JSON.stringify(order))
@@ -2265,7 +2265,7 @@ moduleIntegrationTestRunner<IOrderModuleService>({
}),
expect.objectContaining({
item_id: itemOne.id,
rate: 25,
rate: 32,
code: "TX-2",
}),
]),
+13 -14
View File
@@ -17,23 +17,22 @@ enum ChangeActionType {
}
type OrderSummary {
total: Float
subtotal: Float
total_tax: Float
ordered_total: Float
fulfilled_total: Float
returned_total: Float
return_request_total: Float
write_off_total: Float
projected_total: Float
net_total: Float
net_subtotal: Float
net_total_tax: Float
balance: Float
pending_difference: Float
current_order_total: Float
original_order_total: Float
transaction_total: Float
paid_total: Float
refunded_total: Float
pending_difference: Float
credit_line_total: Float
accounting_total: Float
raw_pending_difference: JSON
raw_current_order_total: JSON
raw_original_order_total: JSON
raw_transaction_total: JSON
raw_paid_total: JSON
raw_refunded_total: JSON
raw_credit_line_total: JSON
raw_accounting_total: JSON
}
type OrderShippingMethodAdjustment {
@@ -45,21 +45,18 @@ describe("Action: Credit Line Add", function () {
"original_order_total": 30,
"current_order_total": 30,
"pending_difference": 30,
"difference_sum": 0,
"paid_total": 0,
"refunded_total": 0,
"credit_line_total": 0
}
Upon adding a credit line, the current order total will decrease with the difference_sum going in
the negatives making it possible for the merchant to balance the order to then enable a refund.
Upon adding a credit line, the current order total will decrease making it possible for the merchant to balance the order to then enable a refund.
{
"transaction_total": 0,
"original_order_total": 30,
"current_order_total": 60,
"pending_difference": 0,
"difference_sum": -30,
"paid_total": 0,
"refunded_total": 0,
"credit_line_total": 30
@@ -81,7 +78,6 @@ describe("Action: Credit Line Add", function () {
original_order_total: 30,
current_order_total: 30,
pending_difference: 30,
difference_sum: 0,
paid_total: 0,
refunded_total: 0,
credit_line_total: 0,
@@ -110,7 +106,6 @@ describe("Action: Credit Line Add", function () {
original_order_total: 30,
current_order_total: 0,
pending_difference: 0,
difference_sum: 0,
paid_total: 0,
refunded_total: 0,
credit_line_total: 30,
@@ -147,7 +142,6 @@ describe("Action: Credit Line Add", function () {
original_order_total: 30,
current_order_total: -10,
pending_difference: -10,
difference_sum: 0,
paid_total: 0,
refunded_total: 0,
credit_line_total: 40,
@@ -119,7 +119,6 @@ describe("Order Exchange - Actions", function () {
original_order_total: 270,
current_order_total: 312.5,
pending_difference: 312.5,
difference_sum: 42.5,
paid_total: 0,
refunded_total: 0,
credit_line_total: 0,
@@ -731,11 +731,13 @@ export default class OrderModuleService
shipping_methods,
items,
}) as any
const calculated = calculateOrderChange({
order: orderWithTotals,
actions: [],
transactions: order.transactions,
})
createRawPropertiesFromBigNumber(calculated)
ord.summary = {
@@ -1469,6 +1471,26 @@ export default class OrderModuleService
})
}
@InjectTransactionManager()
async upsertOrderLineItemAdjustments(
adjustments: (
| OrderTypes.CreateOrderLineItemAdjustmentDTO
| OrderTypes.UpdateOrderLineItemAdjustmentDTO
)[],
@MedusaContext() sharedContext: Context = {}
): Promise<OrderTypes.OrderLineItemAdjustmentDTO[]> {
let result = await this.orderLineItemAdjustmentService_.upsert(
adjustments,
sharedContext
)
return await this.baseRepository_.serialize<
OrderTypes.OrderLineItemAdjustmentDTO[]
>(result, {
populate: true,
})
}
@InjectTransactionManager()
async setOrderLineItemAdjustments(
orderId: string,
@@ -1520,6 +1542,26 @@ export default class OrderModuleService
})
}
@InjectTransactionManager()
async upsertOrderShippingMethodAdjustments(
adjustments: (
| OrderTypes.CreateOrderShippingMethodAdjustmentDTO
| OrderTypes.UpdateOrderShippingMethodAdjustmentDTO
)[],
@MedusaContext() sharedContext: Context = {}
): Promise<OrderTypes.OrderShippingMethodAdjustmentDTO[]> {
const result = await this.orderShippingMethodAdjustmentService_.upsert(
adjustments,
sharedContext
)
return await this.baseRepository_.serialize<
OrderTypes.OrderShippingMethodAdjustmentDTO[]
>(result, {
populate: true,
})
}
@InjectTransactionManager()
async setOrderShippingMethodAdjustments(
orderId: string,
@@ -1721,6 +1763,26 @@ export default class OrderModuleService
return serialized
}
@InjectTransactionManager()
async upsertOrderLineItemTaxLines(
taxLines: (
| OrderTypes.CreateOrderLineItemTaxLineDTO
| OrderTypes.UpdateOrderLineItemTaxLineDTO
)[],
@MedusaContext() sharedContext: Context = {}
): Promise<OrderTypes.OrderLineItemTaxLineDTO[]> {
const result = await this.orderLineItemTaxLineService_.upsert(
taxLines as UpdateOrderLineItemTaxLineDTO[],
sharedContext
)
return await this.baseRepository_.serialize<
OrderTypes.OrderLineItemTaxLineDTO[]
>(result, {
populate: true,
})
}
@InjectTransactionManager()
async setOrderLineItemTaxLines(
orderId: string,
@@ -1834,6 +1896,26 @@ export default class OrderModuleService
return serialized
}
@InjectTransactionManager()
async upsertOrderShippingMethodTaxLines(
taxLines: (
| OrderTypes.CreateOrderShippingMethodTaxLineDTO
| OrderTypes.UpdateOrderShippingMethodTaxLineDTO
)[],
@MedusaContext() sharedContext: Context = {}
): Promise<OrderTypes.OrderShippingMethodTaxLineDTO[]> {
const result = await this.orderShippingMethodTaxLineService_.upsert(
taxLines as UpdateOrderShippingMethodTaxLineDTO[],
sharedContext
)
return await this.baseRepository_.serialize<
OrderTypes.OrderShippingMethodTaxLineDTO[]
>(result, {
populate: true,
})
}
@InjectTransactionManager()
async setOrderShippingMethodTaxLines(
orderId: string,
@@ -2115,7 +2197,7 @@ export default class OrderModuleService
orderId,
{
select: ["id", "version", "items.detail", "summary", "total"],
relations: ["transactions", "items", "shipping_methods"],
relations: ["transactions", "credit_lines"],
},
sharedContext
)
@@ -2131,7 +2213,7 @@ export default class OrderModuleService
)
const { itemsToUpsert, shippingMethodsToUpsert, calculatedOrders } =
applyChangesToOrder(
await applyChangesToOrder(
[order],
{ [order.id]: orderChange.actions },
{ addActionReferenceToObject: true }
@@ -2139,9 +2221,34 @@ export default class OrderModuleService
const calculated = calculatedOrders[order.id]
await this.includeTaxLinesAndAdjustementsToPreview(
calculated.order,
itemsToUpsert,
shippingMethodsToUpsert,
sharedContext
)
const calcOrder = calculated.order
const orderWithTotals = decorateCartTotals(
calcOrder as DecorateCartLikeInputDTO
)
calcOrder.summary = calculated.getSummaryFromOrder(orderWithTotals)
createRawPropertiesFromBigNumber(calcOrder)
return calcOrder
}
private async includeTaxLinesAndAdjustementsToPreview(
order,
itemsToUpsert,
shippingMethodsToUpsert,
sharedContext
) {
const addedItems = {}
const addedShippingMethods = {}
for (const item of calculated.order.items) {
for (const item of order.items) {
const isExistingItem = item.id === item.detail?.item_id
if (!isExistingItem) {
addedItems[item.id] = {
@@ -2156,7 +2263,7 @@ export default class OrderModuleService
}
}
for (const sm of calculated.order.shipping_methods) {
for (const sm of order.shipping_methods) {
if (!isDefined(sm.shipping_option_id)) {
addedShippingMethods[sm.id] = sm
}
@@ -2171,7 +2278,7 @@ export default class OrderModuleService
sharedContext
)
calculated.order.items.forEach((item, idx) => {
order.items.forEach((item, idx) => {
if (!addedItems[item.id]) {
return
}
@@ -2187,7 +2294,7 @@ export default class OrderModuleService
const compareAtUnitPrice =
newItem?.compare_at_unit_price ?? item.compare_at_unit_price
calculated.order.items[idx] = {
order.items[idx] = {
...lineItem,
actions,
quantity: newItem.quantity,
@@ -2210,7 +2317,7 @@ export default class OrderModuleService
sharedContext
)
calculated.order.shipping_methods.forEach((sm, idx) => {
order.shipping_methods.forEach((sm, idx) => {
if (!addedShippingMethods[sm.id]) {
return
}
@@ -2227,7 +2334,7 @@ export default class OrderModuleService
sm.shipping_method_id = sm.id
delete sm.id
calculated.order.shipping_methods[idx] = {
order.shipping_methods[idx] = {
...shippingMethod,
actions,
detail: {
@@ -2237,15 +2344,6 @@ export default class OrderModuleService
}
})
}
const calcOrder = calculated.order
decorateCartTotals(calcOrder as DecorateCartLikeInputDTO)
calcOrder.summary = calculated.summary
createRawPropertiesFromBigNumber(calcOrder)
return calcOrder
}
async cancelOrderChange(
@@ -2982,27 +3080,25 @@ export default class OrderModuleService
{ id: deduplicate(ordersIds) },
{
select: ["id", "version", "items.detail", "summary", "total"],
relations: [
"transactions",
"items",
"items.detail",
"shipping_methods",
],
relations: ["transactions", "credit_lines"],
},
sharedContext
)
orders = formatOrder(orders, {
entity: Order,
}) as OrderDTO[]
const {
itemsToUpsert,
shippingMethodsToUpsert,
summariesToUpsert,
orderToUpdate,
} = applyChangesToOrder(orders, actionsMap, {
} = await applyChangesToOrder(orders, actionsMap, {
addActionReferenceToObject: true,
includeTaxLinesAndAdjustementsToPreview: async (...args) => {
args.push(sharedContext)
return await this.includeTaxLinesAndAdjustementsToPreview.apply(
this,
args
)
},
})
await promiseAll([
@@ -3130,7 +3226,7 @@ export default class OrderModuleService
id: transactionIds,
},
{
select: ["order_id", "amount"],
select: ["order_id", "version", "amount"],
},
sharedContext
)
@@ -3162,7 +3258,7 @@ export default class OrderModuleService
id: transactionIds,
},
{
select: ["order_id", "amount"],
select: ["order_id", "version", "amount"],
withDeleted: true,
},
sharedContext
@@ -3187,6 +3283,7 @@ export default class OrderModuleService
private async updateOrderPaidRefundableAmount_(
transactionData: {
order_id: string
version: number
amount: BigNumber | number | BigNumberInput
}[],
isRemoved: boolean,
@@ -3195,6 +3292,7 @@ export default class OrderModuleService
const summaries: any = await super.listOrderSummaries(
{
order_id: transactionData.map((trx) => trx.order_id),
version: transactionData[0].version,
},
{},
sharedContext
@@ -3212,6 +3310,8 @@ export default class OrderModuleService
const op = isRemoved ? MathBN.sub : MathBN.add
const initialTrxTotal = summary.totals.transaction_total
for (const trx of trxs) {
if (MathBN.gt(trx.amount, 0)) {
summary.totals.paid_total = new BigNumber(
@@ -3228,11 +3328,12 @@ export default class OrderModuleService
)
}
const initialDiff = MathBN.sub(
summary.totals.transaction_total,
initialTrxTotal
)
summary.totals.pending_difference = new BigNumber(
MathBN.sub(
summary.totals.current_order_total,
summary.totals.transaction_total
)
MathBN.sub(summary.totals.pending_difference, initialDiff)
)
})
@@ -62,6 +62,17 @@ export type VirtualOrder = {
amount: BigNumberInput
}[]
summary?: {
pending_difference: BigNumberInput
current_order_total: BigNumberInput
original_order_total: BigNumberInput
transaction_total: BigNumberInput
paid_total: BigNumberInput
refunded_total: BigNumberInput
credit_line_total: BigNumberInput
accounting_total: BigNumberInput
}
total: BigNumberInput
customer_id?: string
@@ -80,7 +91,6 @@ export interface OrderSummaryCalculated {
original_order_total: BigNumberInput
transaction_total: BigNumberInput
pending_difference: BigNumberInput
difference_sum: BigNumberInput
paid_total: BigNumberInput
refunded_total: BigNumberInput
credit_line_total: BigNumberInput
@@ -1,11 +1,13 @@
import {
InferEntityType,
OrderChangeActionDTO,
OrderDTO,
} from "@medusajs/framework/types"
import {
ChangeActionType,
MathBN,
createRawPropertiesFromBigNumber,
decorateCartTotals,
isDefined,
} from "@medusajs/framework/utils"
import { OrderItem, OrderShippingMethod } from "@models"
@@ -18,11 +20,12 @@ export interface ApplyOrderChangeDTO extends OrderChangeActionDTO {
applied: boolean
}
export function applyChangesToOrder(
export async function applyChangesToOrder(
orders: any[],
actionsMap: Record<string, any[]>,
options?: {
addActionReferenceToObject?: boolean
includeTaxLinesAndAdjustementsToPreview?: (...args) => void
}
) {
const itemsToUpsert: InferEntityType<typeof OrderItem>[] = []
@@ -49,8 +52,6 @@ export function applyChangesToOrder(
createRawPropertiesFromBigNumber(calculated)
calculatedOrders[order.id] = calculated
const version = actionsMap[order.id]?.[0]?.version ?? order.version
const orderAttributes: {
version?: number
@@ -95,14 +96,6 @@ export function applyChangesToOrder(
itemsToUpsert.push(itemToUpsert)
}
const orderSummary = order.summary as any
summariesToUpsert.push({
id: orderSummary?.version === version ? orderSummary.id : undefined,
order_id: order.id,
version,
totals: calculated.summary,
})
if (version > order.version) {
for (const shippingMethod of calculated.order.shipping_methods ?? []) {
const shippingMethod_ = shippingMethod as any
@@ -140,6 +133,26 @@ export function applyChangesToOrder(
orderAttributes.version = version
}
// Including tax lines and adjustments for added items and shipping methods
if (options?.includeTaxLinesAndAdjustementsToPreview) {
await options?.includeTaxLinesAndAdjustementsToPreview(
calculated.order,
itemsToUpsert,
shippingMethodsToUpsert
)
decorateCartTotals(calculated.order)
}
const orderSummary = order.summary
summariesToUpsert.push({
id: orderSummary?.version === version ? orderSummary.id : undefined,
order_id: order.id,
version,
totals: calculated.getSummaryFromOrder(
calculated.order as unknown as OrderDTO
),
})
if (Object.keys(orderAttributes).length > 0) {
orderToUpdate.push({
selector: {
@@ -150,6 +163,8 @@ export function applyChangesToOrder(
},
})
}
calculatedOrders[order.id] = calculated
}
return {
@@ -1,4 +1,8 @@
import { BigNumberInput, OrderSummaryDTO } from "@medusajs/framework/types"
import {
BigNumberInput,
OrderDTO,
OrderSummaryDTO,
} from "@medusajs/framework/types"
import {
BigNumber,
ChangeActionType,
@@ -77,7 +81,6 @@ export class OrderChangeProcessing {
this.summary = {
pending_difference: 0,
difference_sum: 0,
current_order_total: this.order.total ?? 0,
original_order_total: this.order.total ?? 0,
transaction_total: transactionTotal,
@@ -96,10 +99,6 @@ export class OrderChangeProcessing {
status === EVENT_STATUS.DONE
)
}
private isEventDone(action: InternalOrderChangeEvent): boolean {
const status = action.status
return status === EVENT_STATUS.DONE
}
public processActions() {
let creditLineTotal = (this.order.credit_lines || []).reduce(
@@ -136,10 +135,6 @@ export class OrderChangeProcessing {
if (action.action === ChangeActionType.CREDIT_LINE_ADD) {
creditLineTotal = MathBN.add(creditLineTotal, amount)
} else {
if (!this.isEventDone(action) && !action.change_id) {
summary.difference_sum = MathBN.add(summary.difference_sum, amount)
}
summary.current_order_total = MathBN.add(
summary.current_order_total,
amount
@@ -147,8 +142,6 @@ export class OrderChangeProcessing {
}
}
const groupSum = MathBN.add(...Object.values(this.groupTotal))
summary.difference_sum = MathBN.add(summary.difference_sum, groupSum)
summary.credit_line_total = creditLineTotal
summary.accounting_total = MathBN.sub(
summary.current_order_total,
@@ -224,7 +217,6 @@ export class OrderChangeProcessing {
original_order_total: new BigNumber(summary.original_order_total),
current_order_total: new BigNumber(summary.current_order_total),
pending_difference: new BigNumber(summary.pending_difference),
difference_sum: new BigNumber(summary.difference_sum),
paid_total: new BigNumber(summary.paid_total),
refunded_total: new BigNumber(summary.refunded_total),
credit_line_total: new BigNumber(summary.credit_line_total),
@@ -234,6 +226,58 @@ export class OrderChangeProcessing {
return orderSummary
}
// Calculate the order summary from a calculated order including taxes
public getSummaryFromOrder(order: OrderDTO): OrderSummaryDTO {
const summary_ = this.summary
const total = order.total
const orderSummary = {
transaction_total: new BigNumber(summary_.transaction_total),
original_order_total: new BigNumber(summary_.original_order_total),
current_order_total: new BigNumber(total),
pending_difference: new BigNumber(summary_.pending_difference),
paid_total: new BigNumber(summary_.paid_total),
refunded_total: new BigNumber(summary_.refunded_total),
credit_line_total: new BigNumber(summary_.credit_line_total),
accounting_total: new BigNumber(summary_.accounting_total),
} as any
orderSummary.accounting_total = new BigNumber(
MathBN.sub(
orderSummary.current_order_total,
orderSummary.credit_line_total
)
)
orderSummary.current_order_total = new BigNumber(
MathBN.sub(
orderSummary.current_order_total,
orderSummary.credit_line_total
)
)
orderSummary.pending_difference = MathBN.sub(
orderSummary.current_order_total,
orderSummary.transaction_total
)
// return requested becomes pending difference
for (const item of order.items ?? []) {
const item_ = item as any
if (MathBN.gt(item_.return_requested_total, 0)) {
orderSummary.pending_difference = MathBN.sub(
orderSummary.pending_difference,
item_.return_requested_total
)
}
}
orderSummary.pending_difference = new BigNumber(
orderSummary.pending_difference
)
return orderSummary
}
public getCurrentOrder(): VirtualOrder {
return this.order
}
@@ -259,7 +303,9 @@ export function calculateOrderChange({
calc.processActions()
return {
instance: calc,
summary: calc.getSummary(),
getSummaryFromOrder: (order: OrderDTO) => calc.getSummaryFromOrder(order),
order: calc.getCurrentOrder(),
}
}