From 4ef70d37bf43bf5c3e01c37688e4235c13f328dd Mon Sep 17 00:00:00 2001 From: "Carlos R. L. Rodrigues" <37986729+carlos-r-l-rodrigues@users.noreply.github.com> Date: Tue, 14 May 2024 08:35:31 -0300 Subject: [PATCH] feat(order): receive return and additional calculations (#7314) --- packages/core/types/src/order/mutations.ts | 17 +++ packages/core/types/src/order/service.ts | 6 + .../core/utils/src/totals/__tests__/totals.ts | 90 +++++++++++++++ packages/core/utils/src/totals/cart/index.ts | 23 ++++ .../core/utils/src/totals/line-item/index.ts | 28 +++++ .../__tests__/util/actions/exchanges.ts | 16 +-- .../src/services/order-module-service.ts | 41 ++++++- .../modules/order/src/types/utils/index.ts | 16 +-- .../order/src/utils/calculate-order-change.ts | 109 +++++++----------- 9 files changed, 259 insertions(+), 87 deletions(-) diff --git a/packages/core/types/src/order/mutations.ts b/packages/core/types/src/order/mutations.ts index 0bd2350d50..ba594a8db4 100644 --- a/packages/core/types/src/order/mutations.ts +++ b/packages/core/types/src/order/mutations.ts @@ -403,6 +403,7 @@ export interface CreateOrderReturnDTO { order_id: string description?: string reference?: string + reference_id?: string internal_note?: string created_by?: string shipping_method: Omit | string @@ -415,6 +416,22 @@ export interface CreateOrderReturnDTO { metadata?: Record | null } +export interface ReceiveOrderReturnDTO { + order_id: string + description?: string + internal_note?: string + reference?: string + reference_id?: string + created_by?: string + items: { + id: string + quantity: BigNumberInput + internal_note?: string + metadata?: Record | null + }[] + metadata?: Record | null +} + /** ORDER bundled action flows */ export interface CreateOrderReturnReasonDTO { diff --git a/packages/core/types/src/order/service.ts b/packages/core/types/src/order/service.ts index 8e2051cd36..5a6556946f 100644 --- a/packages/core/types/src/order/service.ts +++ b/packages/core/types/src/order/service.ts @@ -45,6 +45,7 @@ import { CreateOrderShippingMethodTaxLineDTO, CreateOrderTransactionDTO, DeclineOrderChangeDTO, + ReceiveOrderReturnDTO, RegisterOrderFulfillmentDTO, RegisterOrderShipmentDTO, UpdateOrderAddressDTO, @@ -1520,4 +1521,9 @@ export interface IOrderModuleService extends IModuleService { returnData: CreateOrderReturnDTO, sharedContext?: Context ): Promise + + receiveReturn( + returnData: ReceiveOrderReturnDTO, + sharedContext?: Context + ): Promise } diff --git a/packages/core/utils/src/totals/__tests__/totals.ts b/packages/core/utils/src/totals/__tests__/totals.ts index 6925f9631b..8b19688fb4 100644 --- a/packages/core/utils/src/totals/__tests__/totals.ts +++ b/packages/core/utils/src/totals/__tests__/totals.ts @@ -634,4 +634,94 @@ describe("Total calculation", function () { original_shipping_total: 27.5, }) }) + + it("should calculate order with items + taxes + adjustments", function () { + const cart = { + items: [ + { + unit_price: 50, + quantity: 2, + fulfilled_quantity: 2, + shipped_quantity: 2, + return_requested_quantity: 2, + return_received_quantity: 1, + return_dismissed_quantity: 1, + written_off_quantity: 0, + tax_lines: [ + { + rate: 10, + }, + ], + adjustments: [ + { + amount: 20, + }, + ], + }, + ], + } + + const serialized = JSON.parse(JSON.stringify(decorateCartTotals(cart))) + + expect(serialized).toEqual({ + items: [ + { + unit_price: 50, + quantity: 2, + fulfilled_quantity: 2, + shipped_quantity: 2, + return_requested_quantity: 2, + return_received_quantity: 1, + return_dismissed_quantity: 1, + written_off_quantity: 0, + tax_lines: [ + { + rate: 10, + total: 8, + subtotal: 10, + }, + ], + adjustments: [ + { + amount: 20, + subtotal: 20, + total: 22, + }, + ], + subtotal: 100, + total: 88, + original_total: 110, + discount_total: 20, + discount_tax_total: 2, + tax_total: 8, + original_tax_total: 10, + fulfilled_total: 88, + shipped_total: 88, + return_requested_total: 88, + return_received_total: 44, + return_dismissed_total: 44, + write_off_total: 0, + }, + ], + total: 88, + subtotal: 100, + tax_total: 8, + discount_total: 20, + discount_tax_total: 2, + original_total: 90, + original_tax_total: 10, + item_total: 88, + item_subtotal: 100, + item_tax_total: 8, + original_item_total: 110, + original_item_subtotal: 100, + original_item_tax_total: 10, + fulfilled_total: 88, + shipped_total: 88, + return_requested_total: 88, + return_received_total: 44, + return_dismissed_total: 44, + write_off_total: 0, + }) + }) }) diff --git a/packages/core/utils/src/totals/cart/index.ts b/packages/core/utils/src/totals/cart/index.ts index afd4b38120..1b5bdebf6d 100644 --- a/packages/core/utils/src/totals/cart/index.ts +++ b/packages/core/utils/src/totals/cart/index.ts @@ -43,6 +43,15 @@ export function decorateCartTotals( ): CartLikeWithTotals { transformPropertiesToBigNumber(cartLike) + const optionalFields = { + fulfilled_quantity: "fulfilled_total", + shipped_quantity: "shipped_total", + return_requested_quantity: "return_requested_total", + return_received_quantity: "return_received_total", + return_dismissed_quantity: "return_dismissed_total", + written_off_quantity: "write_off_total", + } + const items = (cartLike.items ?? []) as unknown as GetItemTotalInput[] const shippingMethods = (cartLike.shipping_methods ?? []) as unknown as GetShippingMethodTotalInput[] @@ -51,12 +60,15 @@ export function decorateCartTotals( const itemsTotals = getLineItemsTotals(items, { includeTax, + extraQuantityFields: optionalFields, }) const shippingMethodsTotals = getShippingMethodsTotals(shippingMethods, { includeTax, }) + const extraTotals = {} + let subtotal = MathBN.convert(0) let discountTotal = MathBN.convert(0) @@ -117,6 +129,13 @@ export function decorateCartTotals( itemOriginalTaxTotal ) + for (const key of Object.values(optionalFields)) { + if (key in itemTotals) { + extraTotals[key] ??= MathBN.convert(0) + extraTotals[key] = MathBN.add(extraTotals[key], itemTotals[key] ?? 0) + } + } + return itemTotals }) @@ -213,6 +232,10 @@ export function decorateCartTotals( cart.original_item_total = new BigNumber(itemsOriginalTotal) cart.original_item_subtotal = new BigNumber(itemsOriginalSubtotal) cart.original_item_tax_total = new BigNumber(itemsOriginalTaxTotal) + + for (const key of Object.keys(extraTotals)) { + cart[key] = new BigNumber(extraTotals[key]) + } } if (cart.shipping_methods) { diff --git a/packages/core/utils/src/totals/line-item/index.ts b/packages/core/utils/src/totals/line-item/index.ts index 79aabe6c0d..b45faa7a13 100644 --- a/packages/core/utils/src/totals/line-item/index.ts +++ b/packages/core/utils/src/totals/line-item/index.ts @@ -6,6 +6,7 @@ import { calculateTaxTotal } from "../tax" interface GetLineItemsTotalsContext { includeTax?: boolean + extraQuantityFields?: Record } export interface GetItemTotalInput { @@ -15,6 +16,13 @@ export interface GetItemTotalInput { is_tax_inclusive?: boolean tax_lines?: Pick[] adjustments?: Pick[] + + fulfilled_quantity?: BigNumber + shipped_quantity?: BigNumber + return_requested_quantity?: BigNumber + return_received_quantity?: BigNumber + return_dismissed_quantity?: BigNumber + written_off_quantity?: BigNumber } export interface GetItemTotalOutput { @@ -31,6 +39,13 @@ export interface GetItemTotalOutput { tax_total: BigNumber original_tax_total: BigNumber + + fulfilled_total?: BigNumber + shipped_total?: BigNumber + return_requested_total?: BigNumber + return_received_total?: BigNumber + return_dismissed_total?: BigNumber + write_off_total?: BigNumber } export function getLineItemsTotals( @@ -43,6 +58,7 @@ export function getLineItemsTotals( for (const item of items) { itemsTotals[item.id ?? index] = getLineItemTotals(item, { includeTax: context.includeTax || item.is_tax_inclusive, + extraQuantityFields: context.extraQuantityFields, }) index++ } @@ -122,5 +138,17 @@ function getLineItemTotals( totals.original_total = new BigNumber(originalTotal) } + const totalPerUnit = MathBN.div(totals.total, item.quantity) + const optionalFields = { + ...(context.extraQuantityFields ?? {}), + } + + for (const field in optionalFields) { + if (field in item) { + const totalField = optionalFields[field] + totals[totalField] = new BigNumber(MathBN.mult(totalPerUnit, item[field])) + } + } + return totals } diff --git a/packages/modules/order/src/services/__tests__/util/actions/exchanges.ts b/packages/modules/order/src/services/__tests__/util/actions/exchanges.ts index 1a463f2bb4..c3df9d3948 100644 --- a/packages/modules/order/src/services/__tests__/util/actions/exchanges.ts +++ b/packages/modules/order/src/services/__tests__/util/actions/exchanges.ts @@ -96,14 +96,14 @@ describe("Order Exchange - Actions", function () { const sumToJSON = JSON.parse(JSON.stringify(changes.summary)) expect(sumToJSON).toEqual({ - transactionTotal: 0, - originalOrderTotal: 270, - currentOrderTotal: 312.5, - temporaryDifference: 62.5, - futureDifference: 0, - futureTemporaryDifference: 0, - pendingDifference: 312.5, - differenceSum: 42.5, + transaction_total: 0, + original_order_total: 270, + current_order_total: 312.5, + temporary_difference: 62.5, + future_difference: 0, + future_temporary_difference: 0, + pending_difference: 312.5, + difference_sum: 42.5, }) const toJson = JSON.parse(JSON.stringify(changes.order.items)) diff --git a/packages/modules/order/src/services/order-module-service.ts b/packages/modules/order/src/services/order-module-service.ts index bd1a84837f..41fa14ad26 100644 --- a/packages/modules/order/src/services/order-module-service.ts +++ b/packages/modules/order/src/services/order-module-service.ts @@ -2238,8 +2238,8 @@ export default class OrderModuleService< return { action: ChangeActionType.RETURN_ITEM, internal_note: item.internal_note, - reference: data.reference, - reference_id: shippingMethodId, + reference: data.reference ?? "fulfillment", + reference_id: data.reference_id ?? shippingMethodId, details: { reference_id: item.id, quantity: item.quantity, @@ -2251,8 +2251,8 @@ export default class OrderModuleService< if (shippingMethodId) { actions.push({ action: ChangeActionType.SHIPPING_ADD, - reference: data.reference, - reference_id: shippingMethodId, + reference: data.reference ?? "fulfillment", + reference_id: data.reference_id ?? shippingMethodId, amount: calculatedAmount.total, }) } @@ -2544,4 +2544,37 @@ export default class OrderModuleService< return await this.returnReasonService_.update(toUpdate, sharedContext) } + + public async receiveReturn( + data: OrderTypes.ReceiveOrderReturnDTO, + sharedContext?: Context + ): Promise { + const items = data.items.map((item) => { + return { + action: ChangeActionType.RECEIVE_RETURN_ITEM, + internal_note: item.internal_note, + reference: data.reference, + reference_id: data.reference_id, + details: { + reference_id: item.id, + quantity: item.quantity, + metadata: item.metadata, + }, + } + }) + + const change = await this.createOrderChange_( + { + order_id: data.order_id, + description: data.description, + internal_note: data.internal_note, + created_by: data.created_by, + metadata: data.metadata, + actions: items, + }, + sharedContext + ) + + await this.confirmOrderChange(change[0].id, sharedContext) + } } diff --git a/packages/modules/order/src/types/utils/index.ts b/packages/modules/order/src/types/utils/index.ts index 1182d98f90..8617cd7b50 100644 --- a/packages/modules/order/src/types/utils/index.ts +++ b/packages/modules/order/src/types/utils/index.ts @@ -36,14 +36,14 @@ export enum EVENT_STATUS { } export interface OrderSummaryCalculated { - currentOrderTotal: BigNumberInput - originalOrderTotal: BigNumberInput - transactionTotal: BigNumberInput - futureDifference: BigNumberInput - pendingDifference: BigNumberInput - futureTemporaryDifference: BigNumberInput - temporaryDifference: BigNumberInput - differenceSum: BigNumberInput + current_order_total: BigNumberInput + original_order_total: BigNumberInput + transaction_total: BigNumberInput + future_difference: BigNumberInput + pending_difference: BigNumberInput + future_temporary_difference: BigNumberInput + temporary_difference: BigNumberInput + difference_sum: BigNumberInput } export interface OrderTransaction { diff --git a/packages/modules/order/src/utils/calculate-order-change.ts b/packages/modules/order/src/utils/calculate-order-change.ts index 21a503c6ea..66087958a4 100644 --- a/packages/modules/order/src/utils/calculate-order-change.ts +++ b/packages/modules/order/src/utils/calculate-order-change.ts @@ -15,8 +15,8 @@ import { VirtualOrder, } from "@types" -type InternalOrderSummary = OrderSummaryCalculated & { - futureTemporarySum: BigNumberInput +interface InternalOrderSummary extends OrderSummaryCalculated { + future_temporary_sum: BigNumberInput } export class OrderChangeProcessing { @@ -56,15 +56,15 @@ export class OrderChangeProcessing { transformPropertiesToBigNumber(this.order.metadata) this.summary = { - futureDifference: 0, - futureTemporaryDifference: 0, - temporaryDifference: 0, - pendingDifference: 0, - futureTemporarySum: 0, - differenceSum: 0, - currentOrderTotal: this.order.total ?? 0, - originalOrderTotal: this.order.total ?? 0, - transactionTotal, + future_difference: 0, + future_temporary_difference: 0, + temporary_difference: 0, + pending_difference: 0, + future_temporary_sum: 0, + difference_sum: 0, + current_order_total: this.order.total ?? 0, + original_order_total: this.order.total ?? 0, + transaction_total: transactionTotal, } } @@ -114,26 +114,29 @@ export class OrderChangeProcessing { if (type.awaitRequired && !this.isEventDone(action)) { if (action.evaluationOnly) { - summary.futureTemporarySum = MathBN.add( - summary.futureTemporarySum, + summary.future_temporary_sum = MathBN.add( + summary.future_temporary_sum, amount ) } else { - summary.temporaryDifference = MathBN.add( - summary.temporaryDifference, + summary.temporary_difference = MathBN.add( + summary.temporary_difference, amount ) } } if (action.evaluationOnly) { - summary.futureDifference = MathBN.add(summary.futureDifference, amount) + summary.future_difference = MathBN.add( + summary.future_difference, + amount + ) } else { if (!this.isEventDone(action) && !action.group_id) { - summary.differenceSum = MathBN.add(summary.differenceSum, amount) + summary.difference_sum = MathBN.add(summary.difference_sum, amount) } - summary.currentOrderTotal = MathBN.add( - summary.currentOrderTotal, + summary.current_order_total = MathBN.add( + summary.current_order_total, amount ) } @@ -141,25 +144,25 @@ export class OrderChangeProcessing { const groupSum = MathBN.add(...Object.values(this.groupTotal)) - summary.differenceSum = MathBN.add(summary.differenceSum, groupSum) + summary.difference_sum = MathBN.add(summary.difference_sum, groupSum) - summary.transactionTotal = MathBN.sum( + summary.transaction_total = MathBN.sum( ...this.transactions.map((tr) => tr.amount) ) - summary.futureTemporaryDifference = MathBN.sub( - summary.futureDifference, - summary.futureTemporarySum + summary.future_temporary_difference = MathBN.sub( + summary.future_difference, + summary.future_temporary_sum ) - summary.temporaryDifference = MathBN.sub( - summary.differenceSum, - summary.temporaryDifference + summary.temporary_difference = MathBN.sub( + summary.difference_sum, + summary.temporary_difference ) - summary.pendingDifference = MathBN.sub( - summary.currentOrderTotal, - summary.transactionTotal + summary.pending_difference = MathBN.sub( + summary.current_order_total, + summary.transaction_total ) } @@ -345,46 +348,18 @@ export class OrderChangeProcessing { public getSummary(): OrderSummaryDTO { const summary = this.summary const orderSummary = { - transactionTotal: new BigNumber(summary.transactionTotal), - originalOrderTotal: new BigNumber(summary.originalOrderTotal), - currentOrderTotal: new BigNumber(summary.currentOrderTotal), - temporaryDifference: new BigNumber(summary.temporaryDifference), - futureDifference: new BigNumber(summary.futureDifference), - futureTemporaryDifference: new BigNumber( - summary.futureTemporaryDifference + transaction_total: new BigNumber(summary.transaction_total), + original_order_total: new BigNumber(summary.original_order_total), + current_order_total: new BigNumber(summary.current_order_total), + temporary_difference: new BigNumber(summary.temporary_difference), + future_difference: new BigNumber(summary.future_difference), + future_temporary_difference: new BigNumber( + summary.future_temporary_difference ), - pendingDifference: new BigNumber(summary.pendingDifference), - differenceSum: new BigNumber(summary.differenceSum), + pending_difference: new BigNumber(summary.pending_difference), + difference_sum: new BigNumber(summary.difference_sum), } as unknown as OrderSummaryDTO - /* - { - total: summary.currentOrderTotal - - subtotal: number - total_tax: number - - ordered_total: summary.originalOrderTotal - fulfilled_total: number - returned_total: number - return_request_total: number - write_off_total: number - projected_total: number - - net_total: number - net_subtotal: number - net_total_tax: number - - future_total: number - future_subtotal: number - future_total_tax: number - future_projected_total: number - - balance: summary.pendingDifference - future_balance: number - } - */ - return orderSummary }