diff --git a/integration-tests/http/__tests__/cart/store/cart.spec.ts b/integration-tests/http/__tests__/cart/store/cart.spec.ts index 9792ef5e30..f6ae1b3d21 100644 --- a/integration-tests/http/__tests__/cart/store/cart.spec.ts +++ b/integration-tests/http/__tests__/cart/store/cart.spec.ts @@ -1,3 +1,4 @@ +import { createCartCreditLinesWorkflow } from "@medusajs/core-flows" import { medusaIntegrationTestRunner } from "@medusajs/test-utils" import { Modules, @@ -1101,6 +1102,19 @@ medusaIntegrationTestRunner({ }) it("should successfully complete cart", async () => { + createCartCreditLinesWorkflow.run({ + input: [ + { + cart_id: cart.id, + amount: 100, + currency_code: "usd", + reference: "test", + reference_id: "test", + }, + ], + container: appContainer, + }) + const response = await api.post( `/store/carts/${cart.id}/complete`, {}, @@ -1112,6 +1126,13 @@ medusaIntegrationTestRunner({ expect.objectContaining({ id: expect.any(String), currency_code: "usd", + credit_lines: [ + expect.objectContaining({ + amount: 100, + reference: "test", + reference_id: "test", + }), + ], items: expect.arrayContaining([ expect.objectContaining({ unit_price: 1500, @@ -1463,8 +1484,6 @@ medusaIntegrationTestRunner({ }) it("should complete a cart with inventory item shared between variants", async () => { - console.log(cart) - const response = await api.post( `/store/carts/${cart.id}/complete`, {}, diff --git a/integration-tests/modules/__tests__/cart/store/cart.workflows.spec.ts b/integration-tests/modules/__tests__/cart/store/cart.workflows.spec.ts index c9e1e5bce7..0d50a7b7c4 100644 --- a/integration-tests/modules/__tests__/cart/store/cart.workflows.spec.ts +++ b/integration-tests/modules/__tests__/cart/store/cart.workflows.spec.ts @@ -2,9 +2,11 @@ import { addShippingMethodToCartWorkflow, addToCartWorkflow, completeCartWorkflow, + createCartCreditLinesWorkflow, createCartWorkflow, createPaymentCollectionForCartWorkflow, createPaymentSessionsWorkflow, + deleteCartCreditLinesWorkflow, deleteLineItemsStepId, deleteLineItemsWorkflow, findOrCreateCustomerStepId, @@ -3348,6 +3350,108 @@ medusaIntegrationTestRunner({ }) }) }) + + describe("createCartCreditLinesWorkflow", () => { + it("should create credit lines for a cart", async () => { + const cart = await cartModuleService.createCarts({ + currency_code: "dkk", + region_id: defaultRegion.id, + shipping_address: { + metadata: { + testing_tax: true, + }, + }, + items: [ + { + quantity: 1, + unit_price: 5000, + title: "Test item", + }, + ], + }) + + const { result: creditLines } = + await createCartCreditLinesWorkflow.run({ + input: [ + { + cart_id: cart.id, + amount: 1000, + reference: "test", + reference_id: "test", + metadata: { + test: "metadata", + }, + }, + ], + container: appContainer, + }) + + expect(creditLines).toEqual([ + expect.objectContaining({ + cart_id: cart.id, + reference: "test", + reference_id: "test", + amount: 1000, + metadata: { + test: "metadata", + }, + }), + ]) + }) + }) + + describe("deleteCartCreditLinesWorkflow", () => { + it("should delete credit lines from a cart", async () => { + const cart = await cartModuleService.createCarts({ + currency_code: "dkk", + region_id: defaultRegion.id, + shipping_address: { + metadata: { + testing_tax: true, + }, + }, + items: [ + { + quantity: 1, + unit_price: 5000, + title: "Test item", + }, + ], + }) + + const { + result: [creditLine], + } = await createCartCreditLinesWorkflow.run({ + input: [ + { + cart_id: cart.id, + amount: 1000, + reference: "test", + reference_id: "test", + metadata: { + test: "metadata", + }, + }, + ], + container: appContainer, + }) + + const { result } = await deleteCartCreditLinesWorkflow.run({ + input: { id: [creditLine.id] }, + container: appContainer, + }) + + const { data: creditLines } = await query.graph({ + entity: "credit_line", + filters: { + id: creditLine.id, + }, + fields: ["id"], + }) + + expect(creditLines).toEqual([]) + }) + }) }) }, }) diff --git a/packages/core/core-flows/src/cart/utils/fields.ts b/packages/core/core-flows/src/cart/utils/fields.ts index 91aeb81bfc..122d3e7f7b 100644 --- a/packages/core/core-flows/src/cart/utils/fields.ts +++ b/packages/core/core-flows/src/cart/utils/fields.ts @@ -95,6 +95,7 @@ export const completeCartFields = [ "shipping_address.*", "billing_address.*", "region.*", + "credit_lines.*", "payment_collection.*", "payment_collection.payment_sessions.*", "items.variant.id", diff --git a/packages/core/core-flows/src/cart/workflows/complete-cart.ts b/packages/core/core-flows/src/cart/workflows/complete-cart.ts index 40089b61d8..193e9a2b40 100644 --- a/packages/core/core-flows/src/cart/workflows/complete-cart.ts +++ b/packages/core/core-flows/src/cart/workflows/complete-cart.ts @@ -1,4 +1,5 @@ import { + CartCreditLineDTO, CartWorkflowDTO, UsageComputedActions, } from "@medusajs/framework/types" @@ -210,6 +211,18 @@ export const completeCartWorkflow = createWorkflow( .map((adjustment) => adjustment.code) .filter(Boolean) + const creditLines = (cart.credit_lines ?? []).map( + (creditLine: CartCreditLineDTO) => { + return { + amount: creditLine.amount, + raw_amount: creditLine.raw_amount, + reference: creditLine.reference, + reference_id: creditLine.reference_id, + metadata: creditLine.metadata, + } + } + ) + return { region_id: cart.region?.id, customer_id: cart.customer?.id, @@ -222,6 +235,7 @@ export const completeCartWorkflow = createWorkflow( no_notification: false, items: allItems, shipping_methods: shippingMethods, + credit_lines: creditLines, metadata: cart.metadata, promo_codes: promoCodes, transactions, diff --git a/packages/core/core-flows/src/cart/workflows/create-cart-credit-lines.ts b/packages/core/core-flows/src/cart/workflows/create-cart-credit-lines.ts new file mode 100644 index 0000000000..5896402737 --- /dev/null +++ b/packages/core/core-flows/src/cart/workflows/create-cart-credit-lines.ts @@ -0,0 +1,28 @@ +import { + CartCreditLineDTO, + CreateCartCreditLineDTO, +} from "@medusajs/framework/types" +import { Modules } from "@medusajs/framework/utils" +import { + WorkflowData, + WorkflowResponse, + createWorkflow, +} from "@medusajs/framework/workflows-sdk" +import { createEntitiesStep } from "../../common/steps/create-entities" + +export const createCartCreditLinesWorkflowId = "create-cart-credit-lines" +export const createCartCreditLinesWorkflow = createWorkflow( + createCartCreditLinesWorkflowId, + ( + input: WorkflowData + ): WorkflowResponse => { + const creditLines = createEntitiesStep({ + moduleRegistrationName: Modules.CART, + invokeMethod: "createCreditLines", + compensateMethod: "deleteCreditLines", + data: input, + }) + + return new WorkflowResponse(creditLines) + } +) diff --git a/packages/core/core-flows/src/cart/workflows/delete-cart-credit-lines.ts b/packages/core/core-flows/src/cart/workflows/delete-cart-credit-lines.ts new file mode 100644 index 0000000000..69ad520c36 --- /dev/null +++ b/packages/core/core-flows/src/cart/workflows/delete-cart-credit-lines.ts @@ -0,0 +1,22 @@ +import { Modules } from "@medusajs/framework/utils" +import { + WorkflowData, + WorkflowResponse, + createWorkflow, +} from "@medusajs/framework/workflows-sdk" +import { deleteEntitiesStep } from "../../common/steps/delete-entities" + +export const deleteCartCreditLinesWorkflowId = "delete-cart-credit-lines" +export const deleteCartCreditLinesWorkflow = createWorkflow( + deleteCartCreditLinesWorkflowId, + (input: WorkflowData<{ id: string[] }>) => { + deleteEntitiesStep({ + moduleRegistrationName: Modules.CART, + invokeMethod: "softDeleteCreditLines", + compensateMethod: "restoreCreditLines", + data: input.id, + }) + + return new WorkflowResponse(void 0) + } +) diff --git a/packages/core/core-flows/src/cart/workflows/index.ts b/packages/core/core-flows/src/cart/workflows/index.ts index 51b57a6a6a..c51f32478c 100644 --- a/packages/core/core-flows/src/cart/workflows/index.ts +++ b/packages/core/core-flows/src/cart/workflows/index.ts @@ -2,13 +2,15 @@ export * from "./add-shipping-method-to-cart" export * from "./add-to-cart" export * from "./complete-cart" export * from "./confirm-variant-inventory" +export * from "./create-cart-credit-lines" export * from "./create-carts" export * from "./create-payment-collection-for-cart" +export * from "./delete-cart-credit-lines" export * from "./list-shipping-options-for-cart" export * from "./list-shipping-options-for-cart-with-pricing" -export * from "./refresh-payment-collection" export * from "./refresh-cart-items" export * from "./refresh-cart-shipping-methods" +export * from "./refresh-payment-collection" export * from "./transfer-cart-customer" export * from "./update-cart" export * from "./update-cart-promotions" diff --git a/packages/core/types/src/cart/common.ts b/packages/core/types/src/cart/common.ts index 87f063beec..bafe642ea8 100644 --- a/packages/core/types/src/cart/common.ts +++ b/packages/core/types/src/cart/common.ts @@ -780,6 +780,13 @@ export interface CartDTO { */ items?: CartLineItemDTO[] + /** + * The credit lines for a cart + * + * @expandable + */ + credit_lines?: CartCreditLineDTO[] + /** * The associated shipping methods * @@ -1275,3 +1282,80 @@ export interface FilterableShippingMethodTaxLineProps */ shipping_method?: FilterableShippingMethodProps } + +/** + * The cart credit line details. + */ +export interface CartCreditLineDTO { + /** + * The ID of the cart credit line. + */ + id: string + + /** + * The ID of the cart that the credit line belongs to. + */ + cart_id: string + + /** + * The amount of the credit line. + */ + amount: BigNumberValue + + /** + * The raw amount of the credit line. + */ + raw_amount: BigNumberRawValue + + /** + * The reference model name that the credit line is generated from + */ + reference: string | null + + /** + * The reference model id that the credit line is generated from + */ + reference_id: string | null + + /** + * The metadata of the cart detail + */ + metadata: Record | null + + /** + * The date when the cart credit line was created. + */ + created_at: Date + + /** + * The date when the cart credit line was last updated. + */ + updated_at: Date +} + +export interface CreateCartCreditLineDTO { + /** + * The ID of the cart that the credit line belongs to. + */ + cart_id: string + + /** + * The amount of the credit line. + */ + amount: number + + /** + * The reference model name that the credit line is generated from + */ + reference: string | null + + /** + * The reference model id that the credit line is generated from + */ + reference_id: string | null + + /** + * The metadata of the cart detail + */ + metadata: Record | null +} diff --git a/packages/core/types/src/order/common.ts b/packages/core/types/src/order/common.ts index 24ba3c0af2..5208363c40 100644 --- a/packages/core/types/src/order/common.ts +++ b/packages/core/types/src/order/common.ts @@ -3019,6 +3019,11 @@ export interface OrderCreditLineDTO { */ order: OrderDTO + /** + * The amount of the credit line. + */ + amount: BigNumberValue + /** * The reference model name that the credit line is generated from */ diff --git a/packages/core/types/src/order/mutations.ts b/packages/core/types/src/order/mutations.ts index 0a1f7d92b5..a461eb5afb 100644 --- a/packages/core/types/src/order/mutations.ts +++ b/packages/core/types/src/order/mutations.ts @@ -1,7 +1,8 @@ -import { BigNumberInput } from "../totals" +import { BigNumberInput, BigNumberValue } from "../totals" import { ChangeActionType, OrderClaimDTO, + OrderCreditLineDTO, OrderExchangeDTO, OrderItemDTO, OrderLineItemDTO, @@ -173,6 +174,11 @@ export interface CreateOrderDTO { */ shipping_methods?: Omit[] + /** + * The credit lines of the order. + */ + credit_lines?: OrderCreditLineDTO[] + /** * The transactions of the order. */ @@ -2263,3 +2269,33 @@ export interface UpdateOrderReturnReasonWithSelectorDTO { */ data: Partial } + +/** + * The order credit line details. + */ +export interface CreateOrderCreditLineDTO { + /** + * The ID of the order that the credit line belongs to. + */ + order_id: string + + /** + * The amount of the credit line. + */ + amount: BigNumberValue + + /** + * The reference model name that the credit line is generated from + */ + reference: string | null + + /** + * The reference model id that the credit line is generated from + */ + reference_id: string | null + + /** + * The metadata of the order detail + */ + metadata?: Record | null +} diff --git a/packages/core/types/src/order/service.ts b/packages/core/types/src/order/service.ts index adf677f53f..087cef95da 100644 --- a/packages/core/types/src/order/service.ts +++ b/packages/core/types/src/order/service.ts @@ -24,6 +24,7 @@ import { OrderChangeReturn, OrderClaimDTO, OrderClaimItemDTO, + OrderCreditLineDTO, OrderDTO, OrderExchangeDTO, OrderExchangeItemDTO, @@ -52,6 +53,7 @@ import { CreateOrderChangeDTO, CreateOrderClaimDTO, CreateOrderClaimItemDTO, + CreateOrderCreditLineDTO, CreateOrderDTO, CreateOrderExchangeDTO, CreateOrderExchangeItemDTO, @@ -4925,4 +4927,9 @@ export interface IOrderModuleService extends IModuleService { data: CancelOrderExchangeDTO, sharedContext?: Context ): Promise + + createOrderCreditLines( + data: CreateOrderCreditLineDTO[], + sharedContext?: Context + ): Promise } diff --git a/packages/core/utils/src/totals/__tests__/totals.ts b/packages/core/utils/src/totals/__tests__/totals.ts index 817c49733e..c3200315a1 100644 --- a/packages/core/utils/src/totals/__tests__/totals.ts +++ b/packages/core/utils/src/totals/__tests__/totals.ts @@ -81,6 +81,9 @@ describe("Total calculation", function () { original_item_subtotal: 65, original_item_total: 73.5, original_item_tax_total: 8.5, + credit_lines_subtotal: 0, + credit_lines_tax_total: 0, + credit_lines_total: 0, }) }) @@ -149,6 +152,9 @@ describe("Total calculation", function () { original_item_total: 110, original_item_subtotal: 100, original_item_tax_total: 10, + credit_lines_subtotal: 0, + credit_lines_tax_total: 0, + credit_lines_total: 0, }) }) @@ -353,6 +359,9 @@ describe("Total calculation", function () { original_shipping_tax_total: 9.9, original_shipping_subtotal: 99, original_shipping_total: 108.9, + credit_lines_subtotal: 0, + credit_lines_tax_total: 0, + credit_lines_total: 0, }) }) @@ -438,6 +447,9 @@ describe("Total calculation", function () { original_item_total: 100, original_item_subtotal: 90.9090909090909, original_item_tax_total: 9.090909090909092, + credit_lines_subtotal: 0, + credit_lines_tax_total: 0, + credit_lines_total: 0, }) expect(serializedWithout).toEqual({ @@ -477,6 +489,9 @@ describe("Total calculation", function () { original_item_total: 110, original_item_subtotal: 100, original_item_tax_total: 10, + credit_lines_subtotal: 0, + credit_lines_tax_total: 0, + credit_lines_total: 0, }) expect(serializedMixed).toEqual({ @@ -536,6 +551,9 @@ describe("Total calculation", function () { original_item_total: 210, original_item_subtotal: 190.9090909090909, original_item_tax_total: 19.09090909090909, + credit_lines_subtotal: 0, + credit_lines_tax_total: 0, + credit_lines_total: 0, }) }) @@ -652,11 +670,21 @@ describe("Total calculation", function () { original_shipping_tax_total: 2.5, original_shipping_subtotal: 25, original_shipping_total: 27.5, + credit_lines_subtotal: 0, + credit_lines_tax_total: 0, + credit_lines_total: 0, }) }) - it("should calculate order with items + taxes + adjustments", function () { + it("should calculate order with items + taxes + adjustments + credit lines", function () { const cart = { + credit_lines: [ + { + amount: 40, + reference: "order", + reference_id: "order_123", + }, + ], items: [ { unit_price: 50, @@ -686,6 +714,15 @@ describe("Total calculation", function () { const serialized = JSON.parse(JSON.stringify(decorateCartTotals(cart))) expect(serialized).toEqual({ + credit_lines: [ + { + amount: 40, + reference: "order", + reference_id: "order_123", + subtotal: 40, + total: 40, + }, + ], items: [ { unit_price: 50, @@ -730,8 +767,8 @@ describe("Total calculation", function () { write_off_total: 44, }, ], - total: 88, - subtotal: 100, + total: 48, + subtotal: 60, tax_total: 8, discount_total: 22, discount_subtotal: 20, @@ -750,6 +787,9 @@ describe("Total calculation", function () { return_received_total: 44, return_dismissed_total: 44, write_off_total: 44, + credit_lines_subtotal: 40, + credit_lines_tax_total: 0, + credit_lines_total: 40, }) }) }) diff --git a/packages/core/utils/src/totals/cart/index.ts b/packages/core/utils/src/totals/cart/index.ts index 0192401e22..757370f680 100644 --- a/packages/core/utils/src/totals/cart/index.ts +++ b/packages/core/utils/src/totals/cart/index.ts @@ -1,5 +1,6 @@ import { BigNumberInput, CartLikeWithTotals } from "@medusajs/types" import { BigNumber } from "../big-number" +import { calculateCreditLinesTotal } from "../credit-lines" import { GetItemTotalInput, getLineItemsTotals } from "../line-item" import { MathBN } from "../math" import { @@ -13,6 +14,9 @@ interface TotalsConfig { } export interface DecorateCartLikeInputDTO { + credit_lines?: { + amount: BigNumberInput + }[] items?: { id?: string unit_price: BigNumberInput @@ -52,6 +56,7 @@ export function decorateCartTotals( "detail.written_off_quantity": "write_off_total", } + const creditLines = cartLike.credit_lines ?? [] const items = (cartLike.items ?? []) as unknown as GetItemTotalInput[] const shippingMethods = (cartLike.shipping_methods ?? []) as unknown as GetShippingMethodTotalInput[] @@ -69,6 +74,10 @@ export function decorateCartTotals( const extraTotals = {} + // TODO: Remove this once we have a way to calculate the tax rate for credit lines + const creditLinesSumTax = MathBN.convert(0) + const creditLinesSumTaxRate = MathBN.div(creditLinesSumTax, 100) + let subtotal = MathBN.convert(0) let discountTotal = MathBN.convert(0) @@ -192,6 +201,15 @@ export function decorateCartTotals( return shippingMethodTotals }) + const { creditLinesTotal, creditLinesSubtotal, creditLinesTaxTotal } = + calculateCreditLinesTotal({ + creditLines, + includesTax: false, + taxRate: creditLinesSumTaxRate, + }) + + subtotal = MathBN.sub(subtotal, creditLinesSubtotal) + const taxTotal = MathBN.add(itemsTaxTotal, shippingTaxTotal) const originalTaxTotal = MathBN.add( @@ -205,6 +223,7 @@ export function decorateCartTotals( // TODO: subtract (cart.gift_card_total + cart.gift_card_tax_total) const tempTotal = MathBN.add(subtotal, taxTotal) const total = MathBN.sub(tempTotal, discountSubtotal) + const cart = cartLike as any cart.total = new BigNumber(total) @@ -215,6 +234,10 @@ export function decorateCartTotals( cart.discount_subtotal = new BigNumber(discountSubtotal) cart.discount_tax_total = new BigNumber(discountTaxTotal) + cart.credit_lines_total = new BigNumber(creditLinesTotal) + cart.credit_lines_subtotal = new BigNumber(creditLinesSubtotal) + cart.credit_lines_tax_total = new BigNumber(creditLinesTaxTotal) + // cart.gift_card_total = giftCardTotal.total || 0 // cart.gift_card_tax_total = giftCardTotal.tax_total || 0 diff --git a/packages/core/utils/src/totals/credit-lines/index.ts b/packages/core/utils/src/totals/credit-lines/index.ts new file mode 100644 index 0000000000..fd05eb62fa --- /dev/null +++ b/packages/core/utils/src/totals/credit-lines/index.ts @@ -0,0 +1,54 @@ +import { BigNumberInput } from "@medusajs/types" +import { isDefined } from "../../common" +import { BigNumber } from "../big-number" +import { MathBN } from "../math" + +export function calculateCreditLinesTotal({ + creditLines, + includesTax, + taxRate, +}: { + creditLines: { amount: BigNumberInput }[] + includesTax?: boolean + taxRate?: BigNumberInput +}) { + // the sum of all creditLine amounts excluding tax + let creditLinesSubtotal = MathBN.convert(0) + // the sum of all creditLine amounts including tax + let creditLinesTotal = MathBN.convert(0) + // the sum of all taxes on subtotals + let creditLinesTaxTotal = MathBN.convert(0) + + for (const cl of creditLines) { + if (!isDefined(cl.amount)) { + continue + } + + const creditLineAmount = MathBN.convert(cl.amount) + creditLinesSubtotal = MathBN.add(creditLinesSubtotal, creditLineAmount) + + if (isDefined(taxRate)) { + const creditLineSubtotal = includesTax + ? MathBN.div(creditLineAmount, MathBN.add(1, taxRate)) + : creditLineAmount + + const creditLineTaxTotal = MathBN.mult(creditLineSubtotal, taxRate) + const creditLineTotal = MathBN.add(creditLineSubtotal, creditLineTaxTotal) + + cl["subtotal"] = new BigNumber(creditLineSubtotal) + cl["total"] = new BigNumber(creditLineTotal) + + creditLinesTotal = MathBN.add(creditLinesTotal, creditLineTotal) + creditLinesTaxTotal = MathBN.add(creditLinesTaxTotal, creditLineTaxTotal) + } else { + cl["subtotal"] = new BigNumber(creditLineAmount) + creditLinesTotal = MathBN.add(creditLinesTotal, creditLineAmount) + } + } + + return { + creditLinesTotal, + creditLinesSubtotal, + creditLinesTaxTotal, + } +} diff --git a/packages/medusa/src/api/store/carts/query-config.ts b/packages/medusa/src/api/store/carts/query-config.ts index 8c18db2889..eece86d150 100644 --- a/packages/medusa/src/api/store/carts/query-config.ts +++ b/packages/medusa/src/api/store/carts/query-config.ts @@ -27,6 +27,9 @@ export const defaultStoreCartFields = [ "original_shipping_tax_total", "original_shipping_subtotal", "original_shipping_total", + "credit_lines_subtotal", + "credit_lines_tax_total", + "credit_lines_total", "metadata", "sales_channel_id", "promotions.id", @@ -121,6 +124,7 @@ export const defaultStoreCartFields = [ "*region.countries", "*payment_collection", "*payment_collection.payment_sessions", + "*credit_lines", ] export const retrieveTransformQueryConfig = { diff --git a/packages/medusa/src/api/store/orders/query-config.ts b/packages/medusa/src/api/store/orders/query-config.ts index 188d8f1bb6..6bfb329c20 100644 --- a/packages/medusa/src/api/store/orders/query-config.ts +++ b/packages/medusa/src/api/store/orders/query-config.ts @@ -42,6 +42,7 @@ export const defaultStoreRetrieveOrderFields = [ "original_shipping_total", "created_at", "updated_at", + "*credit_lines", "*items", "*items.tax_lines", "*items.adjustments", diff --git a/packages/modules/cart/integration-tests/__tests__/services/cart-module/index.spec.ts b/packages/modules/cart/integration-tests/__tests__/services/cart-module/index.spec.ts index 4977a3e0dd..aac2f14112 100644 --- a/packages/modules/cart/integration-tests/__tests__/services/cart-module/index.spec.ts +++ b/packages/modules/cart/integration-tests/__tests__/services/cart-module/index.spec.ts @@ -17,6 +17,7 @@ moduleIntegrationTestRunner({ expect(Object.keys(linkable)).toEqual([ "cart", + "creditLine", "address", "lineItem", "lineItemAdjustment", @@ -40,6 +41,15 @@ moduleIntegrationTestRunner({ field: "cart", }, }, + creditLine: { + id: { + entity: "CreditLine", + field: "creditLine", + linkable: "credit_line_id", + primaryKey: "id", + serviceName: "cart", + }, + }, address: { id: { linkable: "address_id", @@ -2764,6 +2774,22 @@ moduleIntegrationTestRunner({ }, }, ], + credit_lines: [], + credit_lines_subtotal: 0, + credit_lines_tax_total: 0, + credit_lines_total: 0, + raw_credit_lines_subtotal: { + precision: 20, + value: "0", + }, + raw_credit_lines_tax_total: { + precision: 20, + value: "0", + }, + raw_credit_lines_total: { + precision: 20, + value: "0", + }, total: 210, subtotal: 510, tax_total: 0, diff --git a/packages/modules/cart/src/migrations/.snapshot-medusa-cart.json b/packages/modules/cart/src/migrations/.snapshot-medusa-cart.json index c8b7dde8b3..cb054008a8 100644 --- a/packages/modules/cart/src/migrations/.snapshot-medusa-cart.json +++ b/packages/modules/cart/src/migrations/.snapshot-medusa-cart.json @@ -163,6 +163,7 @@ "keyName": "IDX_cart_address_deleted_at", "columnNames": [], "composite": false, + "constraint": false, "primary": false, "unique": false, "expression": "CREATE INDEX IF NOT EXISTS \"IDX_cart_address_deleted_at\" ON \"cart_address\" (deleted_at) WHERE deleted_at IS NULL" @@ -173,12 +174,14 @@ "id" ], "composite": false, + "constraint": true, "primary": true, "unique": true } ], "checks": [], - "foreignKeys": {} + "foreignKeys": {}, + "nativeEnums": {} }, { "columns": { @@ -313,6 +316,7 @@ "keyName": "IDX_cart_deleted_at", "columnNames": [], "composite": false, + "constraint": false, "primary": false, "unique": false, "expression": "CREATE INDEX IF NOT EXISTS \"IDX_cart_deleted_at\" ON \"cart\" (deleted_at) WHERE deleted_at IS NULL" @@ -321,6 +325,7 @@ "keyName": "IDX_cart_region_id", "columnNames": [], "composite": false, + "constraint": false, "primary": false, "unique": false, "expression": "CREATE INDEX IF NOT EXISTS \"IDX_cart_region_id\" ON \"cart\" (region_id) WHERE deleted_at IS NULL AND region_id IS NOT NULL" @@ -329,6 +334,7 @@ "keyName": "IDX_cart_customer_id", "columnNames": [], "composite": false, + "constraint": false, "primary": false, "unique": false, "expression": "CREATE INDEX IF NOT EXISTS \"IDX_cart_customer_id\" ON \"cart\" (customer_id) WHERE deleted_at IS NULL AND customer_id IS NOT NULL" @@ -337,6 +343,7 @@ "keyName": "IDX_cart_sales_channel_id", "columnNames": [], "composite": false, + "constraint": false, "primary": false, "unique": false, "expression": "CREATE INDEX IF NOT EXISTS \"IDX_cart_sales_channel_id\" ON \"cart\" (sales_channel_id) WHERE deleted_at IS NULL AND sales_channel_id IS NOT NULL" @@ -345,6 +352,7 @@ "keyName": "IDX_cart_curency_code", "columnNames": [], "composite": false, + "constraint": false, "primary": false, "unique": false, "expression": "CREATE INDEX IF NOT EXISTS \"IDX_cart_curency_code\" ON \"cart\" (currency_code) WHERE deleted_at IS NULL" @@ -353,6 +361,7 @@ "keyName": "IDX_cart_shipping_address_id", "columnNames": [], "composite": false, + "constraint": false, "primary": false, "unique": false, "expression": "CREATE INDEX IF NOT EXISTS \"IDX_cart_shipping_address_id\" ON \"cart\" (shipping_address_id) WHERE deleted_at IS NULL AND shipping_address_id IS NOT NULL" @@ -361,6 +370,7 @@ "keyName": "IDX_cart_billing_address_id", "columnNames": [], "composite": false, + "constraint": false, "primary": false, "unique": false, "expression": "CREATE INDEX IF NOT EXISTS \"IDX_cart_billing_address_id\" ON \"cart\" (billing_address_id) WHERE deleted_at IS NULL AND billing_address_id IS NOT NULL" @@ -371,6 +381,7 @@ "id" ], "composite": false, + "constraint": true, "primary": true, "unique": true } @@ -403,7 +414,164 @@ "deleteRule": "set null", "updateRule": "cascade" } - } + }, + "nativeEnums": {} + }, + { + "columns": { + "id": { + "name": "id", + "type": "text", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "mappedType": "text" + }, + "cart_id": { + "name": "cart_id", + "type": "text", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "mappedType": "text" + }, + "reference": { + "name": "reference", + "type": "text", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": true, + "mappedType": "text" + }, + "reference_id": { + "name": "reference_id", + "type": "text", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": true, + "mappedType": "text" + }, + "amount": { + "name": "amount", + "type": "numeric", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "mappedType": "decimal" + }, + "raw_amount": { + "name": "raw_amount", + "type": "jsonb", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "mappedType": "json" + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": true, + "mappedType": "json" + }, + "created_at": { + "name": "created_at", + "type": "timestamptz", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "length": 6, + "default": "now()", + "mappedType": "datetime" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamptz", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "length": 6, + "default": "now()", + "mappedType": "datetime" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamptz", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": true, + "length": 6, + "mappedType": "datetime" + } + }, + "name": "credit_line", + "schema": "public", + "indexes": [ + { + "keyName": "IDX_credit_line_cart_id", + "columnNames": [], + "composite": false, + "constraint": false, + "primary": false, + "unique": false, + "expression": "CREATE INDEX IF NOT EXISTS \"IDX_credit_line_cart_id\" ON \"credit_line\" (cart_id) WHERE deleted_at IS NULL" + }, + { + "keyName": "IDX_credit_line_deleted_at", + "columnNames": [], + "composite": false, + "constraint": false, + "primary": false, + "unique": false, + "expression": "CREATE INDEX IF NOT EXISTS \"IDX_credit_line_deleted_at\" ON \"credit_line\" (deleted_at) WHERE deleted_at IS NULL" + }, + { + "keyName": "IDX_cart_credit_line_reference_reference_id", + "columnNames": [], + "composite": false, + "constraint": false, + "primary": false, + "unique": false, + "expression": "CREATE INDEX IF NOT EXISTS \"IDX_cart_credit_line_reference_reference_id\" ON \"credit_line\" (reference, reference_id) WHERE deleted_at IS NOT NULL" + }, + { + "keyName": "credit_line_pkey", + "columnNames": [ + "id" + ], + "composite": false, + "constraint": true, + "primary": true, + "unique": true + } + ], + "checks": [], + "foreignKeys": { + "credit_line_cart_id_foreign": { + "constraintName": "credit_line_cart_id_foreign", + "columnNames": [ + "cart_id" + ], + "localTableName": "public.credit_line", + "referencedColumnNames": [ + "id" + ], + "referencedTableName": "public.cart", + "updateRule": "cascade" + } + }, + "nativeEnums": {} }, { "columns": { @@ -703,6 +871,7 @@ "keyName": "IDX_cart_line_item_cart_id", "columnNames": [], "composite": false, + "constraint": false, "primary": false, "unique": false, "expression": "CREATE INDEX IF NOT EXISTS \"IDX_cart_line_item_cart_id\" ON \"cart_line_item\" (cart_id) WHERE deleted_at IS NULL" @@ -711,6 +880,7 @@ "keyName": "IDX_cart_line_item_deleted_at", "columnNames": [], "composite": false, + "constraint": false, "primary": false, "unique": false, "expression": "CREATE INDEX IF NOT EXISTS \"IDX_cart_line_item_deleted_at\" ON \"cart_line_item\" (deleted_at) WHERE deleted_at IS NULL" @@ -719,6 +889,7 @@ "keyName": "IDX_line_item_cart_id", "columnNames": [], "composite": false, + "constraint": false, "primary": false, "unique": false, "expression": "CREATE INDEX IF NOT EXISTS \"IDX_line_item_cart_id\" ON \"cart_line_item\" (cart_id) WHERE deleted_at IS NULL" @@ -727,6 +898,7 @@ "keyName": "IDX_line_item_variant_id", "columnNames": [], "composite": false, + "constraint": false, "primary": false, "unique": false, "expression": "CREATE INDEX IF NOT EXISTS \"IDX_line_item_variant_id\" ON \"cart_line_item\" (variant_id) WHERE deleted_at IS NULL AND variant_id IS NOT NULL" @@ -735,6 +907,7 @@ "keyName": "IDX_line_item_product_id", "columnNames": [], "composite": false, + "constraint": false, "primary": false, "unique": false, "expression": "CREATE INDEX IF NOT EXISTS \"IDX_line_item_product_id\" ON \"cart_line_item\" (product_id) WHERE deleted_at IS NULL AND product_id IS NOT NULL" @@ -743,6 +916,7 @@ "keyName": "IDX_line_item_product_type_id", "columnNames": [], "composite": false, + "constraint": false, "primary": false, "unique": false, "expression": "CREATE INDEX IF NOT EXISTS \"IDX_line_item_product_type_id\" ON \"cart_line_item\" (product_type_id) WHERE deleted_at IS NULL AND product_type_id IS NOT NULL" @@ -753,6 +927,7 @@ "id" ], "composite": false, + "constraint": true, "primary": true, "unique": true } @@ -772,7 +947,8 @@ "deleteRule": "cascade", "updateRule": "cascade" } - } + }, + "nativeEnums": {} }, { "columns": { @@ -897,6 +1073,7 @@ "keyName": "IDX_cart_line_item_adjustment_item_id", "columnNames": [], "composite": false, + "constraint": false, "primary": false, "unique": false, "expression": "CREATE INDEX IF NOT EXISTS \"IDX_cart_line_item_adjustment_item_id\" ON \"cart_line_item_adjustment\" (item_id) WHERE deleted_at IS NULL" @@ -905,6 +1082,7 @@ "keyName": "IDX_cart_line_item_adjustment_deleted_at", "columnNames": [], "composite": false, + "constraint": false, "primary": false, "unique": false, "expression": "CREATE INDEX IF NOT EXISTS \"IDX_cart_line_item_adjustment_deleted_at\" ON \"cart_line_item_adjustment\" (deleted_at) WHERE deleted_at IS NULL" @@ -913,6 +1091,7 @@ "keyName": "IDX_line_item_adjustment_promotion_id", "columnNames": [], "composite": false, + "constraint": false, "primary": false, "unique": false, "expression": "CREATE INDEX IF NOT EXISTS \"IDX_line_item_adjustment_promotion_id\" ON \"cart_line_item_adjustment\" (promotion_id) WHERE deleted_at IS NULL AND promotion_id IS NOT NULL" @@ -921,6 +1100,7 @@ "keyName": "IDX_adjustment_item_id", "columnNames": [], "composite": false, + "constraint": false, "primary": false, "unique": false, "expression": "CREATE INDEX IF NOT EXISTS \"IDX_adjustment_item_id\" ON \"cart_line_item_adjustment\" (item_id) WHERE deleted_at IS NULL" @@ -931,6 +1111,7 @@ "id" ], "composite": false, + "constraint": true, "primary": true, "unique": true } @@ -956,7 +1137,8 @@ "deleteRule": "cascade", "updateRule": "cascade" } - } + }, + "nativeEnums": {} }, { "columns": { @@ -1072,6 +1254,7 @@ "keyName": "IDX_cart_line_item_tax_line_item_id", "columnNames": [], "composite": false, + "constraint": false, "primary": false, "unique": false, "expression": "CREATE INDEX IF NOT EXISTS \"IDX_cart_line_item_tax_line_item_id\" ON \"cart_line_item_tax_line\" (item_id) WHERE deleted_at IS NULL" @@ -1080,6 +1263,7 @@ "keyName": "IDX_cart_line_item_tax_line_deleted_at", "columnNames": [], "composite": false, + "constraint": false, "primary": false, "unique": false, "expression": "CREATE INDEX IF NOT EXISTS \"IDX_cart_line_item_tax_line_deleted_at\" ON \"cart_line_item_tax_line\" (deleted_at) WHERE deleted_at IS NULL" @@ -1088,6 +1272,7 @@ "keyName": "IDX_line_item_tax_line_tax_rate_id", "columnNames": [], "composite": false, + "constraint": false, "primary": false, "unique": false, "expression": "CREATE INDEX IF NOT EXISTS \"IDX_line_item_tax_line_tax_rate_id\" ON \"cart_line_item_tax_line\" (tax_rate_id) WHERE deleted_at IS NULL AND tax_rate_id IS NOT NULL" @@ -1096,6 +1281,7 @@ "keyName": "IDX_tax_line_item_id", "columnNames": [], "composite": false, + "constraint": false, "primary": false, "unique": false, "expression": "CREATE INDEX IF NOT EXISTS \"IDX_tax_line_item_id\" ON \"cart_line_item_tax_line\" (item_id) WHERE deleted_at IS NULL" @@ -1106,6 +1292,7 @@ "id" ], "composite": false, + "constraint": true, "primary": true, "unique": true } @@ -1125,7 +1312,8 @@ "deleteRule": "cascade", "updateRule": "cascade" } - } + }, + "nativeEnums": {} }, { "columns": { @@ -1260,6 +1448,7 @@ "keyName": "IDX_cart_shipping_method_cart_id", "columnNames": [], "composite": false, + "constraint": false, "primary": false, "unique": false, "expression": "CREATE INDEX IF NOT EXISTS \"IDX_cart_shipping_method_cart_id\" ON \"cart_shipping_method\" (cart_id) WHERE deleted_at IS NULL" @@ -1268,6 +1457,7 @@ "keyName": "IDX_cart_shipping_method_deleted_at", "columnNames": [], "composite": false, + "constraint": false, "primary": false, "unique": false, "expression": "CREATE INDEX IF NOT EXISTS \"IDX_cart_shipping_method_deleted_at\" ON \"cart_shipping_method\" (deleted_at) WHERE deleted_at IS NULL" @@ -1276,6 +1466,7 @@ "keyName": "IDX_shipping_method_cart_id", "columnNames": [], "composite": false, + "constraint": false, "primary": false, "unique": false, "expression": "CREATE INDEX IF NOT EXISTS \"IDX_shipping_method_cart_id\" ON \"cart_shipping_method\" (cart_id) WHERE deleted_at IS NULL" @@ -1284,6 +1475,7 @@ "keyName": "IDX_shipping_method_option_id", "columnNames": [], "composite": false, + "constraint": false, "primary": false, "unique": false, "expression": "CREATE INDEX IF NOT EXISTS \"IDX_shipping_method_option_id\" ON \"cart_shipping_method\" (shipping_option_id) WHERE deleted_at IS NULL AND shipping_option_id IS NOT NULL" @@ -1294,6 +1486,7 @@ "id" ], "composite": false, + "constraint": true, "primary": true, "unique": true } @@ -1319,7 +1512,8 @@ "deleteRule": "cascade", "updateRule": "cascade" } - } + }, + "nativeEnums": {} }, { "columns": { @@ -1444,6 +1638,7 @@ "keyName": "IDX_cart_shipping_method_adjustment_shipping_method_id", "columnNames": [], "composite": false, + "constraint": false, "primary": false, "unique": false, "expression": "CREATE INDEX IF NOT EXISTS \"IDX_cart_shipping_method_adjustment_shipping_method_id\" ON \"cart_shipping_method_adjustment\" (shipping_method_id) WHERE deleted_at IS NULL" @@ -1452,6 +1647,7 @@ "keyName": "IDX_cart_shipping_method_adjustment_deleted_at", "columnNames": [], "composite": false, + "constraint": false, "primary": false, "unique": false, "expression": "CREATE INDEX IF NOT EXISTS \"IDX_cart_shipping_method_adjustment_deleted_at\" ON \"cart_shipping_method_adjustment\" (deleted_at) WHERE deleted_at IS NULL" @@ -1460,6 +1656,7 @@ "keyName": "IDX_shipping_method_adjustment_promotion_id", "columnNames": [], "composite": false, + "constraint": false, "primary": false, "unique": false, "expression": "CREATE INDEX IF NOT EXISTS \"IDX_shipping_method_adjustment_promotion_id\" ON \"cart_shipping_method_adjustment\" (promotion_id) WHERE deleted_at IS NULL AND promotion_id IS NOT NULL" @@ -1468,6 +1665,7 @@ "keyName": "IDX_adjustment_shipping_method_id", "columnNames": [], "composite": false, + "constraint": false, "primary": false, "unique": false, "expression": "CREATE INDEX IF NOT EXISTS \"IDX_adjustment_shipping_method_id\" ON \"cart_shipping_method_adjustment\" (shipping_method_id) WHERE deleted_at IS NULL" @@ -1478,6 +1676,7 @@ "id" ], "composite": false, + "constraint": true, "primary": true, "unique": true } @@ -1496,7 +1695,8 @@ "referencedTableName": "public.cart_shipping_method", "updateRule": "cascade" } - } + }, + "nativeEnums": {} }, { "columns": { @@ -1612,6 +1812,7 @@ "keyName": "IDX_cart_shipping_method_tax_line_shipping_method_id", "columnNames": [], "composite": false, + "constraint": false, "primary": false, "unique": false, "expression": "CREATE INDEX IF NOT EXISTS \"IDX_cart_shipping_method_tax_line_shipping_method_id\" ON \"cart_shipping_method_tax_line\" (shipping_method_id) WHERE deleted_at IS NULL" @@ -1620,6 +1821,7 @@ "keyName": "IDX_cart_shipping_method_tax_line_deleted_at", "columnNames": [], "composite": false, + "constraint": false, "primary": false, "unique": false, "expression": "CREATE INDEX IF NOT EXISTS \"IDX_cart_shipping_method_tax_line_deleted_at\" ON \"cart_shipping_method_tax_line\" (deleted_at) WHERE deleted_at IS NULL" @@ -1628,6 +1830,7 @@ "keyName": "IDX_tax_line_shipping_method_id", "columnNames": [], "composite": false, + "constraint": false, "primary": false, "unique": false, "expression": "CREATE INDEX IF NOT EXISTS \"IDX_tax_line_shipping_method_id\" ON \"cart_shipping_method_tax_line\" (shipping_method_id) WHERE deleted_at IS NULL" @@ -1636,6 +1839,7 @@ "keyName": "IDX_shipping_method_tax_line_tax_rate_id", "columnNames": [], "composite": false, + "constraint": false, "primary": false, "unique": false, "expression": "CREATE INDEX IF NOT EXISTS \"IDX_shipping_method_tax_line_tax_rate_id\" ON \"cart_shipping_method_tax_line\" (tax_rate_id) WHERE deleted_at IS NULL AND tax_rate_id IS NOT NULL" @@ -1646,6 +1850,7 @@ "id" ], "composite": false, + "constraint": true, "primary": true, "unique": true } @@ -1664,7 +1869,9 @@ "referencedTableName": "public.cart_shipping_method", "updateRule": "cascade" } - } + }, + "nativeEnums": {} } - ] + ], + "nativeEnums": {} } diff --git a/packages/modules/cart/src/migrations/Migration20250212131240.ts b/packages/modules/cart/src/migrations/Migration20250212131240.ts new file mode 100644 index 0000000000..ca054cfd64 --- /dev/null +++ b/packages/modules/cart/src/migrations/Migration20250212131240.ts @@ -0,0 +1,29 @@ +import { Migration } from "@mikro-orm/migrations" + +export class Migration20250212131240 extends Migration { + override async up(): Promise { + this.addSql( + `create table if not exists "credit_line" ("id" text not null, "cart_id" text not null, "reference" text null, "reference_id" text null, "amount" numeric not null, "raw_amount" jsonb not null, "metadata" jsonb null, "created_at" timestamptz not null default now(), "updated_at" timestamptz not null default now(), "deleted_at" timestamptz null, constraint "credit_line_pkey" primary key ("id"));` + ) + + this.addSql( + `CREATE INDEX IF NOT EXISTS "IDX_credit_line_cart_id" ON "credit_line" (cart_id) WHERE deleted_at IS NULL;` + ) + + this.addSql( + `CREATE INDEX IF NOT EXISTS "IDX_credit_line_deleted_at" ON "credit_line" (deleted_at) WHERE deleted_at IS NULL;` + ) + + this.addSql( + `CREATE INDEX IF NOT EXISTS "IDX_cart_credit_line_reference_reference_id" ON "credit_line" (reference, reference_id) WHERE deleted_at IS NOT NULL;` + ) + + this.addSql( + `alter table if exists "credit_line" add constraint "credit_line_cart_id_foreign" foreign key ("cart_id") references "cart" ("id") on update cascade;` + ) + } + + override async down(): Promise { + this.addSql(`drop table if exists "credit_line" cascade;`) + } +} diff --git a/packages/modules/cart/src/models/cart.ts b/packages/modules/cart/src/models/cart.ts index 0f40460cf3..75baecedd5 100644 --- a/packages/modules/cart/src/models/cart.ts +++ b/packages/modules/cart/src/models/cart.ts @@ -1,5 +1,6 @@ import { model } from "@medusajs/framework/utils" import Address from "./address" +import CreditLine from "./credit-line" import LineItem from "./line-item" import ShippingMethod from "./shipping-method" @@ -28,6 +29,9 @@ const Cart = model items: model.hasMany(() => LineItem, { mappedBy: "cart", }), + credit_lines: model.hasMany(() => CreditLine, { + mappedBy: "cart", + }), shipping_methods: model.hasMany(() => ShippingMethod, { mappedBy: "cart", }), diff --git a/packages/modules/cart/src/models/credit-line.ts b/packages/modules/cart/src/models/credit-line.ts new file mode 100644 index 0000000000..b9738f9b89 --- /dev/null +++ b/packages/modules/cart/src/models/credit-line.ts @@ -0,0 +1,25 @@ +import { model } from "@medusajs/framework/utils" +import Cart from "./cart" + +const CreditLine = model + .define("CreditLine", { + id: model.id({ prefix: "cacl" }).primaryKey(), + cart: model.belongsTo(() => Cart, { + mappedBy: "credit_lines", + }), + reference: model.text().nullable(), + reference_id: model.text().nullable(), + amount: model.bigNumber(), + raw_amount: model.json(), + metadata: model.json().nullable(), + }) + .indexes([ + { + name: "IDX_cart_credit_line_reference_reference_id", + on: ["reference", "reference_id"], + unique: false, + where: "deleted_at IS NOT NULL", + }, + ]) + +export default CreditLine diff --git a/packages/modules/cart/src/models/index.ts b/packages/modules/cart/src/models/index.ts index 62897db827..49afb3ef92 100644 --- a/packages/modules/cart/src/models/index.ts +++ b/packages/modules/cart/src/models/index.ts @@ -1,5 +1,6 @@ export { default as Address } from "./address" export { default as Cart } from "./cart" +export { default as CreditLine } from "./credit-line" export { default as LineItem } from "./line-item" export { default as LineItemAdjustment } from "./line-item-adjustment" export { default as LineItemTaxLine } from "./line-item-tax-line" diff --git a/packages/modules/cart/src/services/cart-module.ts b/packages/modules/cart/src/services/cart-module.ts index 46fbee69f1..40777d8b28 100644 --- a/packages/modules/cart/src/services/cart-module.ts +++ b/packages/modules/cart/src/services/cart-module.ts @@ -24,6 +24,7 @@ import { import { Address, Cart, + CreditLine, LineItem, LineItemAdjustment, LineItemTaxLine, @@ -54,6 +55,7 @@ type InjectedDependencies = { const generateMethodForModels = { Cart, + CreditLine, Address, LineItem, LineItemAdjustment, @@ -66,6 +68,7 @@ const generateMethodForModels = { export default class CartModuleService extends ModulesSdkUtils.MedusaService<{ Cart: { dto: CartTypes.CartDTO } + CreditLine: { dto: CartTypes.CartCreditLineDTO } Address: { dto: CartTypes.CartAddressDTO } LineItem: { dto: CartTypes.CartLineItemDTO } LineItemAdjustment: { dto: CartTypes.LineItemAdjustmentDTO } @@ -170,6 +173,7 @@ export default class CartModuleService const requiredFieldsForTotals = [ "items", + "credit_lines", "items.tax_lines", "items.adjustments", "shipping_methods", diff --git a/packages/modules/order/src/services/__tests__/util/actions/credit-line-add.spec.ts b/packages/modules/order/src/services/__tests__/util/actions/credit-line-add.spec.ts index 2feff0d9bf..db01639736 100644 --- a/packages/modules/order/src/services/__tests__/util/actions/credit-line-add.spec.ts +++ b/packages/modules/order/src/services/__tests__/util/actions/credit-line-add.spec.ts @@ -1,4 +1,4 @@ -import { ChangeActionType } from "@medusajs/framework/utils" +import { ChangeActionType, decorateCartTotals } from "@medusajs/framework/utils" import { VirtualOrder } from "@types" import { calculateOrderChange } from "../../../../utils" @@ -100,7 +100,6 @@ describe("Action: Credit Line Add", function () { }) const sumToJSON = JSON.parse(JSON.stringify(changes.summary)) - expect(sumToJSON).toEqual({ transaction_total: 0, original_order_total: 30, @@ -129,8 +128,10 @@ describe("Action: Credit Line Add", function () { }, ] + const order = decorateCartTotals(originalOrder) as any + const changesSecond = calculateOrderChange({ - order: originalOrder, + order, actions: actionsSecond, options: { addActionReferenceToObject: true }, }) @@ -139,12 +140,12 @@ describe("Action: Credit Line Add", function () { expect(sumToJSONSecond).toEqual({ transaction_total: 0, - original_order_total: 30, + original_order_total: 20, current_order_total: -10, pending_difference: -10, paid_total: 0, refunded_total: 0, - credit_line_total: 40, + credit_line_total: 30, accounting_total: -10, }) }) diff --git a/packages/modules/order/src/services/order-module-service.ts b/packages/modules/order/src/services/order-module-service.ts index 5d2b5a00e7..641f901661 100644 --- a/packages/modules/order/src/services/order-module-service.ts +++ b/packages/modules/order/src/services/order-module-service.ts @@ -1,6 +1,7 @@ import { BigNumberInput, Context, + CreateOrderCreditLineDTO, DAL, FilterableOrderReturnReasonProps, FindConfig, @@ -689,6 +690,7 @@ export default class OrderModuleService "billing_address", "summary", "items", + "credit_lines", "items.tax_lines", "items.adjustments", "shipping_methods", @@ -713,9 +715,10 @@ export default class OrderModuleService await this.createOrderAddresses_(data, sharedContext) const lineItemsToCreate: CreateOrderLineItemDTO[] = [] - + const creditLinesToCreate: CreateOrderCreditLineDTO[] = [] const createdOrders: InferEntityType[] = [] - for (const { items, shipping_methods, ...order } of data) { + + for (const { items, shipping_methods, credit_lines, ...order } of data) { const ord = order as any const shippingMethods = shipping_methods?.map((sm: any) => { @@ -746,6 +749,16 @@ export default class OrderModuleService const created = await this.orderService_.create(ord, sharedContext) + creditLinesToCreate.push( + ...(credit_lines || []).map((creditLine) => ({ + amount: creditLine.amount, + reference: creditLine.reference, + reference_id: creditLine.reference_id, + metadata: creditLine.metadata, + order_id: created.id, + })) + ) + createdOrders.push(created) if (items?.length) { @@ -764,6 +777,10 @@ export default class OrderModuleService await this.createOrderLineItemsBulk_(lineItemsToCreate, sharedContext) } + if (creditLinesToCreate.length) { + await super.createOrderCreditLines(creditLinesToCreate, sharedContext) + } + return createdOrders } diff --git a/packages/modules/order/src/utils/actions/credit-line-add.ts b/packages/modules/order/src/utils/actions/credit-line-add.ts index 6f087c0ef4..c5b723c05e 100644 --- a/packages/modules/order/src/utils/actions/credit-line-add.ts +++ b/packages/modules/order/src/utils/actions/credit-line-add.ts @@ -8,20 +8,19 @@ OrderChangeProcessing.registerActionType(ChangeActionType.CREDIT_LINE_ADD, { let existing = creditLines.find((cl) => cl.id === action.reference_id) if (!existing) { - existing = { - id: action.reference_id!, + const newCreditLine = { order_id: currentOrder.id, amount: action.amount!, reference: action.reference, reference_id: action.reference_id, } - creditLines.push(existing) + creditLines.push(newCreditLine as any) + + setActionReference(newCreditLine, action, options) + + currentOrder.credit_lines = creditLines } - - setActionReference(existing, action, options) - - currentOrder.credit_lines = creditLines }, validate({ action }) { if (action.amount == null) { diff --git a/packages/modules/order/src/utils/calculate-order-change.ts b/packages/modules/order/src/utils/calculate-order-change.ts index 72e27a3785..cc74f9d280 100644 --- a/packages/modules/order/src/utils/calculate-order-change.ts +++ b/packages/modules/order/src/utils/calculate-order-change.ts @@ -7,6 +7,7 @@ import { BigNumber, ChangeActionType, MathBN, + isDefined, isPresent, transformPropertiesToBigNumber, } from "@medusajs/framework/utils" @@ -101,10 +102,12 @@ export class OrderChangeProcessing { } public processActions() { - let creditLineTotal = (this.order.credit_lines || []).reduce( - (acc, creditLine) => MathBN.add(acc, creditLine.amount), - MathBN.convert(0) - ) + let newCreditLineTotal = (this.order.credit_lines || []) + .filter((cl) => !isDefined(cl.id)) + .reduce( + (acc, creditLine) => MathBN.add(acc, creditLine.amount), + MathBN.convert(0) + ) for (const action of this.actions) { this.processAction_(action) @@ -133,7 +136,11 @@ export class OrderChangeProcessing { } if (action.action === ChangeActionType.CREDIT_LINE_ADD) { - creditLineTotal = MathBN.add(creditLineTotal, amount) + newCreditLineTotal = MathBN.add(newCreditLineTotal, amount) + summary.current_order_total = MathBN.sub( + summary.current_order_total, + amount + ) } else { summary.current_order_total = MathBN.add( summary.current_order_total, @@ -142,21 +149,13 @@ export class OrderChangeProcessing { } } - summary.credit_line_total = creditLineTotal - summary.accounting_total = MathBN.sub( - summary.current_order_total, - creditLineTotal - ) + summary.credit_line_total = newCreditLineTotal + summary.accounting_total = summary.current_order_total summary.transaction_total = MathBN.sum( ...this.transactions.map((tr) => tr.amount) ) - summary.current_order_total = MathBN.sub( - summary.current_order_total, - creditLineTotal - ) - summary.pending_difference = MathBN.sub( summary.current_order_total, summary.transaction_total @@ -241,19 +240,7 @@ export class OrderChangeProcessing { 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.accounting_total = orderSummary.current_order_total orderSummary.pending_difference = MathBN.sub( orderSummary.current_order_total,