diff --git a/integration-tests/modules/__tests__/order/draft-order.spec.ts b/integration-tests/modules/__tests__/order/draft-order.spec.ts new file mode 100644 index 0000000000..d4783f6479 --- /dev/null +++ b/integration-tests/modules/__tests__/order/draft-order.spec.ts @@ -0,0 +1,375 @@ +import { ModuleRegistrationName, Modules } from "@medusajs/modules-sdk" +import { + ICartModuleService, + ICustomerModuleService, + IFulfillmentModuleService, + IInventoryServiceNext, + IPaymentModuleService, + IPricingModuleService, + IProductModuleService, + IRegionModuleService, + ISalesChannelModuleService, + IStockLocationServiceNext, + ITaxModuleService, +} from "@medusajs/types" +import { ContainerRegistrationKeys } from "@medusajs/utils" +import { medusaIntegrationTestRunner } from "medusa-test-utils" +import { + adminHeaders, + createAdminUser, +} from "../../../helpers/create-admin-user" +import { setupTaxStructure } from "../fixtures" + +jest.setTimeout(50000) + +const env = { MEDUSA_FF_MEDUSA_V2: true } + +medusaIntegrationTestRunner({ + debug: true, + env, + testSuite: ({ dbConnection, getContainer, api }) => { + let appContainer + let cartModuleService: ICartModuleService + let regionModuleService: IRegionModuleService + let scModuleService: ISalesChannelModuleService + let customerModule: ICustomerModuleService + let productModule: IProductModuleService + let pricingModule: IPricingModuleService + let paymentModule: IPaymentModuleService + let inventoryModule: IInventoryServiceNext + let stockLocationModule: IStockLocationServiceNext + let fulfillmentModule: IFulfillmentModuleService + let locationModule: IStockLocationServiceNext + let taxModule: ITaxModuleService + let remoteLink, remoteQuery + + beforeAll(async () => { + appContainer = getContainer() + cartModuleService = appContainer.resolve(ModuleRegistrationName.CART) + regionModuleService = appContainer.resolve(ModuleRegistrationName.REGION) + scModuleService = appContainer.resolve( + ModuleRegistrationName.SALES_CHANNEL + ) + customerModule = appContainer.resolve(ModuleRegistrationName.CUSTOMER) + productModule = appContainer.resolve(ModuleRegistrationName.PRODUCT) + pricingModule = appContainer.resolve(ModuleRegistrationName.PRICING) + paymentModule = appContainer.resolve(ModuleRegistrationName.PAYMENT) + inventoryModule = appContainer.resolve(ModuleRegistrationName.INVENTORY) + stockLocationModule = appContainer.resolve( + ModuleRegistrationName.STOCK_LOCATION + ) + fulfillmentModule = appContainer.resolve( + ModuleRegistrationName.FULFILLMENT + ) + locationModule = appContainer.resolve( + ModuleRegistrationName.STOCK_LOCATION + ) + taxModule = appContainer.resolve(ModuleRegistrationName.TAX) + remoteLink = appContainer.resolve(ContainerRegistrationKeys.REMOTE_LINK) + remoteQuery = appContainer.resolve(ContainerRegistrationKeys.REMOTE_QUERY) + }) + + beforeEach(async () => { + await createAdminUser(dbConnection, adminHeaders, appContainer) + }) + + describe("Draft Orders - Admin", () => { + it("should create a draft order", async () => { + const region = await regionModuleService.create({ + name: "US", + currency_code: "usd", + }) + + const salesChannel = await scModuleService.create({ + name: "Webshop", + }) + + const location = await stockLocationModule.create({ + name: "Warehouse", + }) + + const [product, product_2] = await productModule.create([ + { + title: "Test product", + variants: [ + { + title: "Test variant", + }, + ], + }, + { + title: "Another product", + variants: [ + { + title: "Variant variable", + manage_inventory: false, + }, + ], + }, + ]) + + const inventoryItem = await inventoryModule.create({ + sku: "inv-1234", + }) + + await inventoryModule.createInventoryLevels([ + { + inventory_item_id: inventoryItem.id, + location_id: location.id, + stocked_quantity: 2, + reserved_quantity: 0, + }, + ]) + + const [priceSet, priceSet_2] = await pricingModule.create([ + { + prices: [ + { + amount: 3000, + currency_code: "usd", + }, + ], + }, + { + prices: [ + { + amount: 1000, + currency_code: "usd", + }, + ], + }, + ]) + + await remoteLink.create([ + { + [Modules.PRODUCT]: { + variant_id: product.variants[0].id, + }, + [Modules.PRICING]: { + price_set_id: priceSet.id, + }, + }, + { + [Modules.PRODUCT]: { + variant_id: product_2.variants[0].id, + }, + [Modules.PRICING]: { + price_set_id: priceSet_2.id, + }, + }, + { + [Modules.SALES_CHANNEL]: { + sales_channel_id: salesChannel.id, + }, + [Modules.STOCK_LOCATION]: { + stock_location_id: location.id, + }, + }, + { + [Modules.PRODUCT]: { + variant_id: product.variants[0].id, + }, + [Modules.INVENTORY]: { + inventory_item_id: inventoryItem.id, + }, + }, + ]) + + await setupTaxStructure(taxModule) + + const payload = { + email: "oli@test.dk", + region_id: region.id, + sales_channel_id: salesChannel.id, + currency_code: "usd", + shipping_address: { + first_name: "Test", + last_name: "Test", + address_1: "Test", + city: "Test", + country_code: "US", + postal_code: "12345", + phone: "12345", + }, + billing_address: { + first_name: "Test", + last_name: "Test", + address_1: "Test", + city: "Test", + country_code: "US", + postal_code: "12345", + }, + promo_codes: ["testytest"], + items: [ + { + variant_id: product.variants[0].id, + quantity: 2, + }, + { + variant_id: product_2.variants[0].id, + unit_price: 200, + quantity: 1, + metadata: { + note: "reduced price", + }, + }, + { + title: "Custom Item", + sku: "sku123", + barcode: "barcode123", + unit_price: 2200, + quantity: 1, + }, + ], + shipping_methods: [ + { + name: "test-method", + option_id: "test-option", + amount: 100, + }, + ], + } + + const response = await api.post( + "/admin/draft-orders", + payload, + adminHeaders + ) + + expect(response.data).toEqual( + expect.objectContaining({ + draft_order: expect.objectContaining({ + status: "draft", + version: 1, + summary: { + total: 8400, + }, + items: [ + expect.objectContaining({ + title: "Test variant", + subtitle: "Test product", + product_title: "Test product", + product_description: null, + product_subtitle: null, + product_type: null, + product_collection: null, + product_handle: "test-product", + variant_sku: null, + variant_barcode: null, + variant_title: "Test variant", + variant_option_values: null, + requires_shipping: true, + is_discountable: true, + is_tax_inclusive: false, + raw_compare_at_unit_price: null, + raw_unit_price: expect.objectContaining({ + value: "3000", + }), + metadata: {}, + tax_lines: [], + adjustments: [], + unit_price: 3000, + quantity: 2, + raw_quantity: expect.objectContaining({ + value: "2", + }), + detail: expect.objectContaining({ + raw_quantity: expect.objectContaining({ + value: "2", + }), + raw_fulfilled_quantity: expect.objectContaining({ + value: "0", + }), + raw_shipped_quantity: expect.objectContaining({ + value: "0", + }), + raw_return_requested_quantity: expect.objectContaining({ + value: "0", + }), + raw_return_received_quantity: expect.objectContaining({ + value: "0", + }), + raw_return_dismissed_quantity: expect.objectContaining({ + value: "0", + }), + raw_written_off_quantity: expect.objectContaining({ + value: "0", + }), + quantity: 2, + fulfilled_quantity: 0, + shipped_quantity: 0, + return_requested_quantity: 0, + return_received_quantity: 0, + return_dismissed_quantity: 0, + written_off_quantity: 0, + }), + }), + expect.objectContaining({ + title: "Variant variable", + subtitle: "Another product", + raw_unit_price: expect.objectContaining({ + value: "200", + }), + metadata: { + note: "reduced price", + }, + unit_price: 200, + quantity: 1, + raw_quantity: expect.objectContaining({ + value: "1", + }), + }), + expect.objectContaining({ + title: "Custom Item", + variant_sku: "sku123", + variant_barcode: "barcode123", + variant_title: "Custom Item", + raw_unit_price: expect.objectContaining({ + value: "2200", + }), + unit_price: 2200, + quantity: 1, + raw_quantity: expect.objectContaining({ + value: "1", + }), + }), + ], + shipping_address: expect.objectContaining({ + last_name: "Test", + address_1: "Test", + city: "Test", + country_code: "US", + postal_code: "12345", + phone: "12345", + }), + billing_address: expect.objectContaining({ + first_name: "Test", + last_name: "Test", + address_1: "Test", + city: "Test", + country_code: "US", + postal_code: "12345", + }), + shipping_methods: [ + expect.objectContaining({ + name: "test-method", + raw_amount: expect.objectContaining({ + value: "100", + }), + is_tax_inclusive: false, + shipping_option_id: null, + data: {}, + tax_lines: [], + adjustments: [], + amount: 100, + }), + ], + }), + }) + ) + + expect(response.status).toEqual(200) + }) + }) + }, +}) diff --git a/integration-tests/modules/medusa-config.js b/integration-tests/modules/medusa-config.js index 39f6c25d0c..814355bad2 100644 --- a/integration-tests/modules/medusa-config.js +++ b/integration-tests/modules/medusa-config.js @@ -90,6 +90,7 @@ module.exports = { [Modules.STORE]: true, [Modules.TAX]: true, [Modules.CURRENCY]: true, + [Modules.ORDER]: true, [Modules.PAYMENT]: { resolve: "@medusajs/payment", /** @type {import('@medusajs/payment').PaymentModuleOptions}*/ diff --git a/packages/core-flows/src/common/index.ts b/packages/core-flows/src/common/index.ts new file mode 100644 index 0000000000..e99533ae88 --- /dev/null +++ b/packages/core-flows/src/common/index.ts @@ -0,0 +1,2 @@ +export * from "./steps/remove-remote-links" +export * from "./steps/use-remote-query" diff --git a/packages/core-flows/src/definition/cart/steps/add-shipping-method-to-cart.ts b/packages/core-flows/src/definition/cart/steps/add-shipping-method-to-cart.ts index 2c28799058..eb3045947d 100644 --- a/packages/core-flows/src/definition/cart/steps/add-shipping-method-to-cart.ts +++ b/packages/core-flows/src/definition/cart/steps/add-shipping-method-to-cart.ts @@ -1,6 +1,6 @@ +import { ModuleRegistrationName } from "@medusajs/modules-sdk" import { CreateShippingMethodDTO, ICartModuleService } from "@medusajs/types" import { StepResponse, createStep } from "@medusajs/workflows-sdk" -import { ModuleRegistrationName } from "../../../../../modules-sdk/dist" interface StepInput { shipping_methods: CreateShippingMethodDTO[] diff --git a/packages/core-flows/src/definition/cart/steps/add-to-cart.ts b/packages/core-flows/src/definition/cart/steps/add-to-cart.ts index b4363a1532..caca51b73d 100644 --- a/packages/core-flows/src/definition/cart/steps/add-to-cart.ts +++ b/packages/core-flows/src/definition/cart/steps/add-to-cart.ts @@ -1,6 +1,6 @@ +import { ModuleRegistrationName } from "@medusajs/modules-sdk" import { CreateLineItemForCartDTO, ICartModuleService } from "@medusajs/types" import { StepResponse, createStep } from "@medusajs/workflows-sdk" -import { ModuleRegistrationName } from "../../../../../modules-sdk/dist" interface StepInput { items: CreateLineItemForCartDTO[] diff --git a/packages/core-flows/src/definition/cart/steps/confirm-inventory.ts b/packages/core-flows/src/definition/cart/steps/confirm-inventory.ts index 3ec730a396..2ee45c3734 100644 --- a/packages/core-flows/src/definition/cart/steps/confirm-inventory.ts +++ b/packages/core-flows/src/definition/cart/steps/confirm-inventory.ts @@ -1,8 +1,8 @@ +import { ModuleRegistrationName } from "@medusajs/modules-sdk" import { IInventoryService } from "@medusajs/types" import { promiseAll } from "@medusajs/utils" import { StepResponse, createStep } from "@medusajs/workflows-sdk" import { MedusaError } from "medusa-core-utils" -import { ModuleRegistrationName } from "../../../../../modules-sdk/dist" interface StepInput { items: { @@ -22,10 +22,10 @@ export const confirmInventoryStep = createStep( ) // TODO: Should be bulk - const promises = data.items.map((item) => { + const promises = data.items.map(async (item) => { const itemQuantity = item.required_quantity * item.quantity - return inventoryService.confirmInventory( + return await inventoryService.confirmInventory( item.inventory_item_id, item.location_ids, itemQuantity diff --git a/packages/core-flows/src/definition/cart/steps/get-item-tax-lines.ts b/packages/core-flows/src/definition/cart/steps/get-item-tax-lines.ts index 9b73965873..c98d13e431 100644 --- a/packages/core-flows/src/definition/cart/steps/get-item-tax-lines.ts +++ b/packages/core-flows/src/definition/cart/steps/get-item-tax-lines.ts @@ -1,3 +1,4 @@ +import { ModuleRegistrationName } from "@medusajs/modules-sdk" import { CartLineItemDTO, CartShippingMethodDTO, @@ -11,7 +12,6 @@ import { } from "@medusajs/types" import { MedusaError } from "@medusajs/utils" import { StepResponse, createStep } from "@medusajs/workflows-sdk" -import { ModuleRegistrationName } from "../../../../../modules-sdk/dist" interface StepInput { cart: CartWorkflowDTO @@ -42,7 +42,7 @@ function normalizeTaxModuleContext( return null } - let customer = cart.customer + const customer = cart.customer ? { id: cart.customer.id, email: cart.customer.email, @@ -104,6 +104,7 @@ export const getItemTaxLinesStep = createStep( shipping_methods: shippingMethods, force_tax_calculation: forceTaxCalculation = false, } = data + const taxService = container.resolve( ModuleRegistrationName.TAX ) diff --git a/packages/core-flows/src/definition/cart/steps/update-tax-lines.ts b/packages/core-flows/src/definition/cart/steps/update-tax-lines.ts index 444d96cffb..a941e8fdc4 100644 --- a/packages/core-flows/src/definition/cart/steps/update-tax-lines.ts +++ b/packages/core-flows/src/definition/cart/steps/update-tax-lines.ts @@ -16,7 +16,7 @@ interface StepInput { export const updateTaxLinesStepId = "update-tax-lines-step" export const updateTaxLinesStep = createStep( updateTaxLinesStepId, - async (input: StepInput, { container, idempotencyKey }) => { + async (input: StepInput, { container }) => { const { transaction } = await updateTaxLinesWorkflow(container).run({ input, }) diff --git a/packages/core-flows/src/definition/cart/utils/prepare-confirm-inventory-input.ts b/packages/core-flows/src/definition/cart/utils/prepare-confirm-inventory-input.ts index ae908b5f66..75a7895a91 100644 --- a/packages/core-flows/src/definition/cart/utils/prepare-confirm-inventory-input.ts +++ b/packages/core-flows/src/definition/cart/utils/prepare-confirm-inventory-input.ts @@ -1,3 +1,4 @@ +import { BigNumberInput } from "@medusajs/types" import { MedusaError } from "medusa-core-utils" interface ConfirmInventoryPreparationInput { @@ -6,7 +7,7 @@ interface ConfirmInventoryPreparationInput { inventory_item_id: string required_quantity: number }[] - items: { variant_id?: string; quantity: number }[] + items: { variant_id?: string; quantity: BigNumberInput }[] variants: { id: string; manage_inventory?: boolean }[] location_ids: string[] } @@ -36,6 +37,12 @@ export const prepareConfirmInventoryInput = ({ const itemsToConfirm: ConfirmInventoryItem[] = [] items.forEach((item) => { + const variant = variantsMap.get(item.variant_id!) + + if (!variant?.manage_inventory) { + return + } + const variantInventoryItem = product_variant_inventory_items.find( (i) => i.variant_id === item.variant_id ) @@ -47,16 +54,12 @@ export const prepareConfirmInventoryInput = ({ ) } - const variant = variantsMap.get(item.variant_id!) - - if (variant?.manage_inventory) { - itemsToConfirm.push({ - inventory_item_id: variantInventoryItem.inventory_item_id, - required_quantity: variantInventoryItem.required_quantity, - quantity: item.quantity, - location_ids: location_ids, - }) - } + itemsToConfirm.push({ + inventory_item_id: variantInventoryItem.inventory_item_id, + required_quantity: variantInventoryItem.required_quantity, + quantity: item.quantity as number, // TODO: update type to BigNumberInput + location_ids: location_ids, + }) }) return itemsToConfirm diff --git a/packages/core-flows/src/definition/cart/utils/prepare-line-item-data.ts b/packages/core-flows/src/definition/cart/utils/prepare-line-item-data.ts index 8e563e6a41..e4af0bbc8e 100644 --- a/packages/core-flows/src/definition/cart/utils/prepare-line-item-data.ts +++ b/packages/core-flows/src/definition/cart/utils/prepare-line-item-data.ts @@ -1,9 +1,9 @@ -import { ProductVariantDTO } from "@medusajs/types" +import { BigNumberInput, ProductVariantDTO } from "@medusajs/types" interface Input { - quantity: number + quantity: BigNumberInput metadata?: Record - unitPrice: number + unitPrice: BigNumberInput variant: ProductVariantDTO cartId?: string } @@ -27,7 +27,7 @@ export function prepareLineItemData(data: Input) { product_description: variant.product.description, product_subtitle: variant.product.subtitle, product_type: variant.product.type?.[0].value ?? null, - product_collection: variant.product.collection?.[0].value ?? null, + product_collection: variant.product.collection?.[0]?.value ?? null, product_handle: variant.product.handle, variant_id: variant.id, diff --git a/packages/core-flows/src/definitions.ts b/packages/core-flows/src/definitions.ts index 645b88ce4e..27a5938e85 100644 --- a/packages/core-flows/src/definitions.ts +++ b/packages/core-flows/src/definitions.ts @@ -1,24 +1,24 @@ export enum Workflows { // Product workflows - CreateProducts = "create-products", - UpdateProducts = "update-products", + CreateProducts = "create-products-old", + UpdateProducts = "update-products-old", // Product Variant workflows - CreateProductVariants = "create-product-variants", - UpdateProductVariants = "update-product-variants", + CreateProductVariants = "create-product-variants-old", + UpdateProductVariants = "update-product-variants-old", // Cart workflows - CreateCart = "create-cart", + CreateCart = "create-cart-old", - CreateInventoryItems = "create-inventory-items", + CreateInventoryItems = "create-inventory-items-old", // Price list workflows - CreatePriceList = "create-price-list", - UpdatePriceLists = "update-price-lists", - DeletePriceLists = "delete-price-lists", - RemovePriceListProductPrices = "remove-price-list-products", - RemovePriceListVariantPrices = "remove-price-list-variants", - RemovePriceListPrices = "remove-price-list-prices", + CreatePriceList = "create-price-list-old", + UpdatePriceLists = "update-price-lists-old", + DeletePriceLists = "delete-price-lists-old", + RemovePriceListProductPrices = "remove-price-list-products-old", + RemovePriceListVariantPrices = "remove-price-list-variants-old", + RemovePriceListPrices = "remove-price-list-prices-old", } export enum InputAlias { diff --git a/packages/core-flows/src/index.ts b/packages/core-flows/src/index.ts index cafab60bca..f72bcf928f 100644 --- a/packages/core-flows/src/index.ts +++ b/packages/core-flows/src/index.ts @@ -1,5 +1,6 @@ export * from "./api-key" export * from "./auth" +export * from "./common" export * from "./customer" export * from "./customer-group" export * from "./defaults" @@ -7,8 +8,9 @@ export * from "./definition" export * from "./definitions" export * from "./fulfillment" export * as Handlers from "./handlers" -export * from "./invite" export * from "./inventory" +export * from "./invite" +export * from "./order" export * from "./payment" export * from "./price-list" export * from "./pricing" diff --git a/packages/core-flows/src/inventory/steps/create-inventory-items.ts b/packages/core-flows/src/inventory/steps/create-inventory-items.ts index efe2365eca..ec0203c523 100644 --- a/packages/core-flows/src/inventory/steps/create-inventory-items.ts +++ b/packages/core-flows/src/inventory/steps/create-inventory-items.ts @@ -1,10 +1,7 @@ import { StepResponse, createStep } from "@medusajs/workflows-sdk" -import { CreateInventoryItemInput } from "@medusajs/types" -import { IInventoryServiceNext } from "@medusajs/types" -import { InventoryNext } from "@medusajs/types" -import { ModuleRegistrationName } from "../../../../modules-sdk/dist" -import { promiseAll } from "@medusajs/utils" +import { ModuleRegistrationName } from "@medusajs/modules-sdk" +import { IInventoryServiceNext, InventoryNext } from "@medusajs/types" export const createInventoryItemsStepId = "create-inventory-items" export const createInventoryItemsStep = createStep( diff --git a/packages/core-flows/src/order/index.ts b/packages/core-flows/src/order/index.ts new file mode 100644 index 0000000000..68de82c9f9 --- /dev/null +++ b/packages/core-flows/src/order/index.ts @@ -0,0 +1,2 @@ +export * from "./steps" +export * from "./workflows" diff --git a/packages/core-flows/src/order/steps/create-orders.ts b/packages/core-flows/src/order/steps/create-orders.ts new file mode 100644 index 0000000000..40f1ff7f6c --- /dev/null +++ b/packages/core-flows/src/order/steps/create-orders.ts @@ -0,0 +1,32 @@ +import { ModuleRegistrationName } from "@medusajs/modules-sdk" +import { CreateOrderDTO, IOrderModuleService } from "@medusajs/types" +import { StepResponse, createStep } from "@medusajs/workflows-sdk" + +type CreateOrdersStepInput = CreateOrderDTO[] + +export const createOrdersStepId = "create-orders" +export const createOrdersStep = createStep( + createOrdersStepId, + async (data: CreateOrdersStepInput, { container }) => { + const service = container.resolve( + ModuleRegistrationName.ORDER + ) + + const created = await service.create(data) + return new StepResponse( + created, + created.map((store) => store.id) + ) + }, + async (createdIds, { container }) => { + if (!createdIds?.length) { + return + } + + const service = container.resolve( + ModuleRegistrationName.ORDER + ) + + await service.delete(createdIds) + } +) diff --git a/packages/core-flows/src/order/steps/get-item-tax-lines.ts b/packages/core-flows/src/order/steps/get-item-tax-lines.ts new file mode 100644 index 0000000000..50294f0f82 --- /dev/null +++ b/packages/core-flows/src/order/steps/get-item-tax-lines.ts @@ -0,0 +1,138 @@ +import { ModuleRegistrationName } from "@medusajs/modules-sdk" +import { + ITaxModuleService, + ItemTaxLineDTO, + OrderLineItemDTO, + OrderShippingMethodDTO, + OrderWorkflowDTO, + ShippingTaxLineDTO, + TaxCalculationContext, + TaxableItemDTO, + TaxableShippingDTO, +} from "@medusajs/types" +import { MedusaError } from "@medusajs/utils" +import { StepResponse, createStep } from "@medusajs/workflows-sdk" + +interface StepInput { + order: OrderWorkflowDTO + items: OrderLineItemDTO[] + shipping_methods: OrderShippingMethodDTO[] + force_tax_calculation?: boolean +} + +function normalizeTaxModuleContext( + order: OrderWorkflowDTO, + forceTaxCalculation: boolean +): TaxCalculationContext | null { + const address = order.shipping_address + const shouldCalculateTax = + forceTaxCalculation || order.region?.automatic_taxes + + if (!shouldCalculateTax) { + return null + } + + if (forceTaxCalculation && !address?.country_code) { + throw new MedusaError( + MedusaError.Types.INVALID_DATA, + `country code is required to calculate taxes` + ) + } + + if (!address?.country_code) { + return null + } + + const customer = order.customer && { + id: order.customer.id, + email: order.customer.email, + customer_groups: order.customer.groups?.map((g) => g.id) || [], + } + + return { + address: { + country_code: address.country_code, + province_code: address.province, + address_1: address.address_1, + address_2: address.address_2, + city: address.city, + postal_code: address.postal_code, + }, + customer, + is_return: false, + } +} + +function normalizeLineItemsForTax( + order: OrderWorkflowDTO, + items: OrderLineItemDTO[] +): TaxableItemDTO[] { + return items.map( + (item) => + ({ + id: item.id, + product_id: item.product_id!, + product_name: item.variant_title, + product_sku: item.variant_sku, + product_type: item.product_type, + product_type_id: item.product_type, + quantity: item.quantity, + unit_price: item.unit_price, + currency_code: order.currency_code, + } as TaxableItemDTO) + ) +} + +function normalizeLineItemsForShipping( + order: OrderWorkflowDTO, + shippingMethods: OrderShippingMethodDTO[] +): TaxableShippingDTO[] { + return shippingMethods.map( + (shippingMethod) => + ({ + id: shippingMethod.id, + shipping_option_id: shippingMethod.shipping_option_id!, + unit_price: shippingMethod.amount, + currency_code: order.currency_code, + } as TaxableShippingDTO) + ) +} + +export const getOrderItemTaxLinesStepId = "get-order-item-tax-lines" +export const getOrderItemTaxLinesStep = createStep( + getOrderItemTaxLinesStepId, + async (data: StepInput, { container }) => { + const { + order, + items, + shipping_methods: shippingMethods, + force_tax_calculation: forceTaxCalculation = false, + } = data + const taxService = container.resolve( + ModuleRegistrationName.TAX + ) + + const taxContext = normalizeTaxModuleContext(order, forceTaxCalculation) + + const stepResponseData = { + lineItemTaxLines: [] as ItemTaxLineDTO[], + shippingMethodsTaxLines: [] as ShippingTaxLineDTO[], + } + + if (!taxContext) { + return new StepResponse(stepResponseData) + } + + stepResponseData.lineItemTaxLines = (await taxService.getTaxLines( + normalizeLineItemsForTax(order, items), + taxContext + )) as ItemTaxLineDTO[] + + stepResponseData.shippingMethodsTaxLines = (await taxService.getTaxLines( + normalizeLineItemsForShipping(order, shippingMethods), + taxContext + )) as ShippingTaxLineDTO[] + + return new StepResponse(stepResponseData) + } +) diff --git a/packages/core-flows/src/order/steps/index.ts b/packages/core-flows/src/order/steps/index.ts new file mode 100644 index 0000000000..5694ff1539 --- /dev/null +++ b/packages/core-flows/src/order/steps/index.ts @@ -0,0 +1,4 @@ +export * from "./create-orders" +export * from "./get-item-tax-lines" +export * from "./set-tax-lines-for-items" +export * from "./update-tax-lines" diff --git a/packages/core-flows/src/order/steps/set-tax-lines-for-items.ts b/packages/core-flows/src/order/steps/set-tax-lines-for-items.ts new file mode 100644 index 0000000000..40dbbb6844 --- /dev/null +++ b/packages/core-flows/src/order/steps/set-tax-lines-for-items.ts @@ -0,0 +1,129 @@ +import { ModuleRegistrationName } from "@medusajs/modules-sdk" +import { + CreateLineItemTaxLineDTO, + CreateShippingMethodTaxLineDTO, + IOrderModuleService, + ItemTaxLineDTO, + OrderDTO, + ShippingTaxLineDTO, +} from "@medusajs/types" +import { promiseAll } from "@medusajs/utils" +import { StepResponse, createStep } from "@medusajs/workflows-sdk" + +interface StepInput { + order: OrderDTO + item_tax_lines: ItemTaxLineDTO[] + shipping_tax_lines: ShippingTaxLineDTO[] +} + +export const setOrderTaxLinesForItemsStepId = "set-order-tax-lines-for-items" +export const setOrderTaxLinesForItemsStep = createStep( + setOrderTaxLinesForItemsStepId, + async (data: StepInput, { container }) => { + const { order, item_tax_lines, shipping_tax_lines } = data + const orderService = container.resolve( + ModuleRegistrationName.ORDER + ) + + const getShippingTaxLinesPromise = + await orderService.listShippingMethodTaxLines({ + shipping_method_id: shipping_tax_lines.map((t) => t.shipping_line_id), + }) + + const getItemTaxLinesPromise = await orderService.listLineItemTaxLines({ + item_id: item_tax_lines.map((t) => t.line_item_id), + }) + + const itemsTaxLinesData = normalizeItemTaxLinesForOrder(item_tax_lines) + const setItemTaxLinesPromise = itemsTaxLinesData.length + ? orderService.setLineItemTaxLines(order.id, itemsTaxLinesData) + : void 0 + + const shippingTaxLinesData = + normalizeShippingTaxLinesForOrder(shipping_tax_lines) + const setShippingTaxLinesPromise = shippingTaxLinesData.length + ? await orderService.setShippingMethodTaxLines( + order.id, + shippingTaxLinesData + ) + : void 0 + + const [existingShippingMethodTaxLines, existingLineItemTaxLines] = + await promiseAll([ + getShippingTaxLinesPromise, + getItemTaxLinesPromise, + setItemTaxLinesPromise, + setShippingTaxLinesPromise, + ]) + + return new StepResponse(void 0, { + order, + existingLineItemTaxLines, + existingShippingMethodTaxLines, + }) + }, + async (revertData, { container }) => { + if (!revertData) { + return + } + + const { order, existingLineItemTaxLines, existingShippingMethodTaxLines } = + revertData + + const orderService = container.resolve( + ModuleRegistrationName.ORDER + ) + + if (existingLineItemTaxLines) { + await orderService.setLineItemTaxLines( + order.id, + existingLineItemTaxLines.map((taxLine) => ({ + description: taxLine.description, + tax_rate_id: taxLine.tax_rate_id, + code: taxLine.code, + rate: taxLine.rate, + provider_id: taxLine.provider_id, + item_id: taxLine.item_id, + })) + ) + } + + await orderService.setShippingMethodTaxLines( + order.id, + existingShippingMethodTaxLines.map((taxLine) => ({ + description: taxLine.description, + tax_rate_id: taxLine.tax_rate_id, + code: taxLine.code, + rate: taxLine.rate, + provider_id: taxLine.provider_id, + shipping_method_id: taxLine.shipping_method_id, + })) + ) + } +) + +function normalizeItemTaxLinesForOrder( + taxLines: ItemTaxLineDTO[] +): CreateLineItemTaxLineDTO[] { + return taxLines.map((taxLine) => ({ + description: taxLine.name, + tax_rate_id: taxLine.rate_id, + code: taxLine.code!, + rate: taxLine.rate!, + provider_id: taxLine.provider_id, + item_id: taxLine.line_item_id, + })) +} + +function normalizeShippingTaxLinesForOrder( + taxLines: ShippingTaxLineDTO[] +): CreateShippingMethodTaxLineDTO[] { + return taxLines.map((taxLine) => ({ + description: taxLine.name, + tax_rate_id: taxLine.rate_id, + code: taxLine.code!, + rate: taxLine.rate!, + provider_id: taxLine.provider_id, + shipping_method_id: taxLine.shipping_line_id, + })) +} diff --git a/packages/core-flows/src/order/steps/update-tax-lines.ts b/packages/core-flows/src/order/steps/update-tax-lines.ts new file mode 100644 index 0000000000..24f0d4563b --- /dev/null +++ b/packages/core-flows/src/order/steps/update-tax-lines.ts @@ -0,0 +1,31 @@ +import { OrderLineItemDTO, OrderShippingMethodDTO } from "@medusajs/types" +import { StepResponse, createStep } from "@medusajs/workflows-sdk" +import { updateOrderTaxLinesWorkflow } from "../workflows/update-tax-lines" + +interface StepInput { + order_id: string + items?: OrderLineItemDTO[] + shipping_methods?: OrderShippingMethodDTO[] + force_tax_calculation?: boolean +} + +export const updateOrderTaxLinesStepId = "update-order-tax-lines-step" +export const updateOrderTaxLinesStep = createStep( + updateOrderTaxLinesStepId, + async (input: StepInput, { container }) => { + const { transaction } = await updateOrderTaxLinesWorkflow(container).run({ + input, + }) + + return new StepResponse(null, { transaction }) + }, + async (flow, { container }) => { + if (!flow) { + return + } + + await updateOrderTaxLinesWorkflow(container).cancel({ + transaction: flow.transaction, + }) + } +) diff --git a/packages/core-flows/src/order/utils/prepare-custom-line-item-data.ts b/packages/core-flows/src/order/utils/prepare-custom-line-item-data.ts new file mode 100644 index 0000000000..bd92d2e3fa --- /dev/null +++ b/packages/core-flows/src/order/utils/prepare-custom-line-item-data.ts @@ -0,0 +1,39 @@ +import { BigNumberInput } from "@medusajs/types" + +interface Input { + quantity: BigNumberInput + metadata?: Record + unitPrice: BigNumberInput + variant: { + title: string + sku?: string + barcode?: string + } +} + +interface Output { + quantity: BigNumberInput + title: string + variant_sku?: string + variant_barcode?: string + variant_title?: string + unit_price: BigNumberInput + metadata?: Record +} + +export function prepareCustomLineItemData(data: Input): Output { + const { variant, unitPrice, quantity, metadata } = data + + const lineItem: any = { + quantity, + title: variant.title, + variant_sku: variant.sku, + variant_barcode: variant.barcode, + variant_title: variant.title, + + unit_price: unitPrice, + metadata, + } + + return lineItem +} diff --git a/packages/core-flows/src/order/workflows/create-orders.ts b/packages/core-flows/src/order/workflows/create-orders.ts new file mode 100644 index 0000000000..3c0600fbbd --- /dev/null +++ b/packages/core-flows/src/order/workflows/create-orders.ts @@ -0,0 +1,204 @@ +import { CreateOrderDTO, OrderDTO } from "@medusajs/types" +import { MathBN, MedusaError } from "@medusajs/utils" +import { + WorkflowData, + createWorkflow, + parallelize, + transform, +} from "@medusajs/workflows-sdk" +import { useRemoteQueryStep } from "../../common" +import { + confirmInventoryStep, + findOneOrAnyRegionStep, + findOrCreateCustomerStep, + findSalesChannelStep, + getVariantPriceSetsStep, + getVariantsStep, + validateVariantsExistStep, +} from "../../definition/cart" +import { prepareConfirmInventoryInput } from "../../definition/cart/utils/prepare-confirm-inventory-input" +import { prepareLineItemData } from "../../definition/cart/utils/prepare-line-item-data" +import { createOrdersStep, updateOrderTaxLinesStep } from "../steps" +import { prepareCustomLineItemData } from "../utils/prepare-custom-line-item-data" + +export const createOrdersWorkflowId = "create-orders" +export const createOrdersWorkflow = createWorkflow( + createOrdersWorkflowId, + (input: WorkflowData): WorkflowData => { + const variantIds = transform({ input }, (data) => { + return (data.input.items ?? []) + .map((item) => item.variant_id) + .filter(Boolean) as string[] + }) + + const [salesChannel, region, customerData] = parallelize( + findSalesChannelStep({ + salesChannelId: input.sales_channel_id, + }), + findOneOrAnyRegionStep({ + regionId: input.region_id, + }), + findOrCreateCustomerStep({ + customerId: input.customer_id, + email: input.email, + }), + validateVariantsExistStep({ variantIds }) + ) + + const variants = getVariantsStep({ + filter: { id: variantIds }, + config: { + select: [ + "id", + "title", + "sku", + "manage_inventory", + "barcode", + "product.id", + "product.title", + "product.description", + "product.subtitle", + "product.thumbnail", + "product.type", + "product.collection", + "product.handle", + ], + relations: ["product"], + }, + }) + + const salesChannelLocations = useRemoteQueryStep({ + entry_point: "sales_channels", + fields: ["id", "name", "stock_locations.id", "stock_locations.name"], + variables: { id: salesChannel.id }, + }) + + const productVariantInventoryItems = useRemoteQueryStep({ + entry_point: "product_variant_inventory_items", + fields: ["variant_id", "inventory_item_id", "required_quantity"], + variables: { variant_id: variantIds }, + }).config({ name: "inventory-items" }) + + const confirmInventoryInput = transform( + { productVariantInventoryItems, salesChannelLocations, input, variants }, + (data) => { + if (!data.input.items) { + return { items: [] } + } + + if (!data.salesChannelLocations.length) { + throw new MedusaError( + MedusaError.Types.INVALID_DATA, + `Sales channel ${data.input.sales_channel_id} is not associated with any stock locations.` + ) + } + + const items = prepareConfirmInventoryInput({ + product_variant_inventory_items: data.productVariantInventoryItems, + location_ids: data.salesChannelLocations[0].stock_locations.map( + (l) => l.id + ), + items: data.input.items!, + variants: data.variants.map((v) => ({ + id: v.id, + manage_inventory: v.manage_inventory, + })), + }) + + return { items } + } + ) + + confirmInventoryStep(confirmInventoryInput) + + // TODO: This is on par with the context used in v1.*, but we can be more flexible. + const pricingContext = transform( + { input, region, customerData }, + (data) => { + return { + currency_code: data.input.currency_code ?? data.region.currency_code, + region_id: data.region.id, + customer_id: data.customerData.customer?.id, + } + } + ) + + const priceSets = getVariantPriceSetsStep({ + variantIds, + context: pricingContext, + }) + + const orderInput = transform( + { input, region, customerData, salesChannel }, + (data) => { + const data_ = { + ...data.input, + currency_code: data.input.currency_code ?? data.region.currency_code, + region_id: data.region.id, + } + + if (data.customerData.customer?.id) { + data_.customer_id = data.customerData.customer.id + data_.email = data.input?.email ?? data.customerData.customer.email + } + + if (data.salesChannel?.id) { + data_.sales_channel_id = data.salesChannel.id + } + + return data_ + } + ) + + const lineItems = transform({ priceSets, input, variants }, (data) => { + const items = (data.input.items ?? []).map((item) => { + const variant = data.variants.find((v) => v.id === item.variant_id)! + + if (!variant) { + return prepareCustomLineItemData({ + variant: { + ...item, + }, + unitPrice: MathBN.max(0, item.unit_price), + quantity: item.quantity as number, + metadata: item?.metadata ?? {}, + }) + } + + return prepareLineItemData({ + variant: variant, + unitPrice: MathBN.max( + 0, + item.unit_price ?? + data.priceSets[item.variant_id!]?.calculated_amount + ), + quantity: item.quantity as number, + metadata: item?.metadata ?? {}, + }) + }) + + return items + }) + + const orderToCreate = transform({ lineItems, orderInput }, (data) => { + return { + ...data.orderInput, + items: data.lineItems, + } + }) + + const orders = createOrdersStep([orderToCreate]) + const order = transform({ orders }, (data) => data.orders?.[0]) + + /* TODO: Implement Order promotions + refreshOrderPromotionsStep({ + id: order.id, + promo_codes: input.promo_codes, + }) + */ + + updateOrderTaxLinesStep({ order_id: order.id }) + + return order + } +) diff --git a/packages/core-flows/src/order/workflows/index.ts b/packages/core-flows/src/order/workflows/index.ts new file mode 100644 index 0000000000..40bedfb9fb --- /dev/null +++ b/packages/core-flows/src/order/workflows/index.ts @@ -0,0 +1,2 @@ +export * from "./create-orders" +export * from "./update-tax-lines" diff --git a/packages/core-flows/src/order/workflows/update-tax-lines.ts b/packages/core-flows/src/order/workflows/update-tax-lines.ts new file mode 100644 index 0000000000..5f1c2cd317 --- /dev/null +++ b/packages/core-flows/src/order/workflows/update-tax-lines.ts @@ -0,0 +1,92 @@ +import { OrderLineItemDTO, OrderShippingMethodDTO } from "@medusajs/types" +import { + WorkflowData, + createWorkflow, + transform, +} from "@medusajs/workflows-sdk" +import { useRemoteQueryStep } from "../../common" +import { + getOrderItemTaxLinesStep, + setOrderTaxLinesForItemsStep, +} from "../steps" + +const orderFields = [ + "id", + "currency_code", + "email", + "region.id", + "region.automatic_taxes", + "items.id", + "items.variant_id", + "items.product_id", + "items.product_title", + "items.product_description", + "items.product_subtitle", + "items.product_type", + "items.product_collection", + "items.product_handle", + "items.variant_sku", + "items.variant_barcode", + "items.variant_title", + "items.title", + "items.quantity", + "items.unit_price", + "items.tax_lines.id", + "items.tax_lines.description", + "items.tax_lines.code", + "items.tax_lines.rate", + "items.tax_lines.provider_id", + "shipping_methods.tax_lines.id", + "shipping_methods.tax_lines.description", + "shipping_methods.tax_lines.code", + "shipping_methods.tax_lines.rate", + "shipping_methods.tax_lines.provider_id", + "shipping_methods.shipping_option_id", + "shipping_methods.amount", + "customer.id", + "customer.email", + "customer.groups.id", + "shipping_address.id", + "shipping_address.address_1", + "shipping_address.address_2", + "shipping_address.city", + "shipping_address.postal_code", + "shipping_address.country_code", + "shipping_address.region_code", + "shipping_address.province", +] + +type WorkflowInput = { + order_id: string + items?: OrderLineItemDTO[] + shipping_methods?: OrderShippingMethodDTO[] + force_tax_calculation?: boolean +} + +export const updateOrderTaxLinesWorkflowId = "update-order-tax-lines" +export const updateOrderTaxLinesWorkflow = createWorkflow( + updateOrderTaxLinesWorkflowId, + (input: WorkflowData): WorkflowData => { + const order = useRemoteQueryStep({ + entry_point: "order", + fields: orderFields, + variables: { id: input.order_id }, + }) + + const taxLineItems = getOrderItemTaxLinesStep( + transform({ input, order }, (data) => ({ + order: data.order, + items: data.input.items || data.order.items, + shipping_methods: + data.input.shipping_methods || data.order.shipping_methods, + force_tax_calculation: data.input.force_tax_calculation, + })) + ) + + setOrderTaxLinesForItemsStep({ + order, + item_tax_lines: taxLineItems.lineItemTaxLines, + shipping_tax_lines: taxLineItems.shippingMethodsTaxLines, + }) + } +) diff --git a/packages/link-modules/src/definitions/index.ts b/packages/link-modules/src/definitions/index.ts index f7246f0717..4eb511e3ba 100644 --- a/packages/link-modules/src/definitions/index.ts +++ b/packages/link-modules/src/definitions/index.ts @@ -5,6 +5,9 @@ export * from "./cart-region" export * from "./cart-sales-channel" export * from "./fulfillment-set-location" export * from "./inventory-level-stock-location" +export * from "./order-customer" +export * from "./order-promotion" +export * from "./order-region" export * from "./order-sales-channel" export * from "./product-sales-channel" export * from "./product-shipping-profile" @@ -15,4 +18,3 @@ export * from "./region-payment-provider" export * from "./sales-channel-location" export * from "./shipping-option-price-set" export * from "./store-default-currency" - diff --git a/packages/link-modules/src/definitions/order-customer.ts b/packages/link-modules/src/definitions/order-customer.ts new file mode 100644 index 0000000000..03c7bfc852 --- /dev/null +++ b/packages/link-modules/src/definitions/order-customer.ts @@ -0,0 +1,28 @@ +import { Modules } from "@medusajs/modules-sdk" +import { ModuleJoinerConfig } from "@medusajs/types" + +export const OrderCustomer: ModuleJoinerConfig = { + isLink: true, + isReadOnlyLink: true, + extends: [ + { + serviceName: Modules.ORDER, + relationship: { + serviceName: Modules.CUSTOMER, + primaryKey: "id", + foreignKey: "customer_id", + alias: "customer", + }, + }, + { + serviceName: Modules.CUSTOMER, + relationship: { + serviceName: Modules.ORDER, + primaryKey: "customer_id", + foreignKey: "id", + alias: "orders", + isList: true, + }, + }, + ], +} diff --git a/packages/link-modules/src/definitions/order-promotion.ts b/packages/link-modules/src/definitions/order-promotion.ts new file mode 100644 index 0000000000..5ce92b1de8 --- /dev/null +++ b/packages/link-modules/src/definitions/order-promotion.ts @@ -0,0 +1,55 @@ +import { Modules } from "@medusajs/modules-sdk" +import { ModuleJoinerConfig } from "@medusajs/types" +import { LINKS } from "@medusajs/utils" + +export const OrderPromotion: ModuleJoinerConfig = { + serviceName: LINKS.OrderPromotion, + isLink: true, + databaseConfig: { + tableName: "order_promotion", + idPrefix: "orderpromo", + }, + alias: [ + { + name: ["order_promotion", "order_promotions"], + args: { + entity: "LinkOrderPromotion", + }, + }, + ], + primaryKeys: ["id", "order_id", "promotion_id"], + relationships: [ + { + serviceName: Modules.ORDER, + primaryKey: "id", + foreignKey: "order_id", + alias: "order", + }, + { + serviceName: Modules.PROMOTION, + primaryKey: "id", + foreignKey: "promotion_id", + alias: "promotion", + }, + ], + extends: [ + { + serviceName: Modules.ORDER, + relationship: { + serviceName: LINKS.OrderPromotion, + primaryKey: "order_id", + foreignKey: "id", + alias: "order_link", + }, + }, + { + serviceName: Modules.PROMOTION, + relationship: { + serviceName: LINKS.OrderPromotion, + primaryKey: "promotion_id", + foreignKey: "id", + alias: "promotion_link", + }, + }, + ], +} diff --git a/packages/link-modules/src/definitions/order-region.ts b/packages/link-modules/src/definitions/order-region.ts new file mode 100644 index 0000000000..5c7640c5b5 --- /dev/null +++ b/packages/link-modules/src/definitions/order-region.ts @@ -0,0 +1,28 @@ +import { Modules } from "@medusajs/modules-sdk" +import { ModuleJoinerConfig } from "@medusajs/types" + +export const OrderRegion: ModuleJoinerConfig = { + isLink: true, + isReadOnlyLink: true, + extends: [ + { + serviceName: Modules.ORDER, + relationship: { + serviceName: Modules.REGION, + primaryKey: "id", + foreignKey: "region_id", + alias: "region", + }, + }, + { + serviceName: Modules.REGION, + relationship: { + serviceName: Modules.ORDER, + primaryKey: "region_id", + foreignKey: "id", + alias: "orders", + isList: true, + }, + }, + ], +} diff --git a/packages/link-modules/src/definitions/order-sales-channel.ts b/packages/link-modules/src/definitions/order-sales-channel.ts index c771fb7857..0204b278ee 100644 --- a/packages/link-modules/src/definitions/order-sales-channel.ts +++ b/packages/link-modules/src/definitions/order-sales-channel.ts @@ -1,4 +1,6 @@ import { ModuleJoinerConfig } from "@medusajs/types" + +import { Modules } from "@medusajs/modules-sdk" import { LINKS } from "@medusajs/utils" export const OrderSalesChannel: ModuleJoinerConfig = { @@ -19,7 +21,7 @@ export const OrderSalesChannel: ModuleJoinerConfig = { primaryKeys: ["id", "order_id", "sales_channel_id"], relationships: [ { - serviceName: "orderService", + serviceName: Modules.ORDER, isInternalService: true, primaryKey: "id", foreignKey: "order_id", @@ -35,7 +37,7 @@ export const OrderSalesChannel: ModuleJoinerConfig = { ], extends: [ { - serviceName: "orderService", + serviceName: Modules.ORDER, fieldAlias: { sales_channel: "sales_channel_link.sales_channel", }, diff --git a/packages/medusa/src/api-v2/admin/draft-orders/[id]/route.ts b/packages/medusa/src/api-v2/admin/draft-orders/[id]/route.ts new file mode 100644 index 0000000000..ecfda86cd9 --- /dev/null +++ b/packages/medusa/src/api-v2/admin/draft-orders/[id]/route.ts @@ -0,0 +1,21 @@ +import { + ContainerRegistrationKeys, + remoteQueryObjectFromString, +} from "@medusajs/utils" +import { MedusaRequest, MedusaResponse } from "../../../../types/routing" +import { defaultAdminOrderFields } from "../query-config" + +export const GET = async (req: MedusaRequest, res: MedusaResponse) => { + const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY) + + const variables = { id: req.params.id } + + const queryObject = remoteQueryObjectFromString({ + entryPoint: "order", + variables, + fields: defaultAdminOrderFields, + }) + + const [draft_order] = await remoteQuery(queryObject) + res.status(200).json({ draft_order }) +} diff --git a/packages/medusa/src/api-v2/admin/draft-orders/middlewares.ts b/packages/medusa/src/api-v2/admin/draft-orders/middlewares.ts new file mode 100644 index 0000000000..7063a7b2b6 --- /dev/null +++ b/packages/medusa/src/api-v2/admin/draft-orders/middlewares.ts @@ -0,0 +1,48 @@ +import { transformBody, transformQuery } from "../../../api/middlewares" +import { MiddlewareRoute } from "../../../loaders/helpers/routing/types" +import { authenticate } from "../../../utils/authenticate-middleware" +import * as QueryConfig from "./query-config" +import { + AdminGetOrdersOrderParams, + AdminGetOrdersParams, + AdminPostDraftOrdersReq, +} from "./validators" + +export const adminDraftOrderRoutesMiddlewares: MiddlewareRoute[] = [ + { + method: ["ALL"], + matcher: "/admin/draft-orders*", + middlewares: [authenticate("admin", ["bearer", "session", "api-key"])], + }, + { + method: ["GET"], + matcher: "/admin/draft-orders", + middlewares: [ + transformQuery( + AdminGetOrdersParams, + QueryConfig.listTransformQueryConfig + ), + ], + }, + { + method: ["GET"], + matcher: "/admin/draft-orders/:id", + middlewares: [ + transformQuery( + AdminGetOrdersOrderParams, + QueryConfig.retrieveTransformQueryConfig + ), + ], + }, + { + method: ["POST"], + matcher: "/admin/draft-orders", + middlewares: [ + transformBody(AdminPostDraftOrdersReq), + transformQuery( + AdminGetOrdersOrderParams, + QueryConfig.retrieveTransformQueryConfig + ), + ], + }, +] diff --git a/packages/medusa/src/api-v2/admin/draft-orders/query-config.ts b/packages/medusa/src/api-v2/admin/draft-orders/query-config.ts new file mode 100644 index 0000000000..edbddfc150 --- /dev/null +++ b/packages/medusa/src/api-v2/admin/draft-orders/query-config.ts @@ -0,0 +1,57 @@ +export const defaultAdminOrderRelations = [ + "items", + "items.tax_lines", + "items.adjustments", + "items.detail", + "shipping_address", + "billing_address", + "shipping_methods", + "shipping_methods.tax_lines", + "shipping_methods.adjustments", +] +export const allowedAdminOrderRelations = [] + +export const defaultAdminListOrderFields = [ + "id", + "status", + "version", + "*items", + "summary", + "metadata", + "created_at", + "updated_at", +] + +export const defaultAdminOrderFields = [ + "id", + "status", + "version", + "*items", + "*items.tax_lines", + "*items.adjustments", + "*items.detail", + "*items.tax_lines", + "*items.adjustments", + "*shipping_address", + "*billing_address", + "*shipping_methods", + "*shipping_methods.tax_lines", + "*shipping_methods.adjustments", + "summary", + "metadata", + "created_at", + "updated_at", +] + +export const retrieveTransformQueryConfig = { + defaultFields: defaultAdminOrderFields, + defaultRelations: defaultAdminOrderRelations, + allowedRelations: allowedAdminOrderRelations, + isList: false, +} + +export const listTransformQueryConfig = { + defaultFields: defaultAdminListOrderFields, + defaultLimit: 20, + isList: true, +} diff --git a/packages/medusa/src/api-v2/admin/draft-orders/route.ts b/packages/medusa/src/api-v2/admin/draft-orders/route.ts new file mode 100644 index 0000000000..2f75605945 --- /dev/null +++ b/packages/medusa/src/api-v2/admin/draft-orders/route.ts @@ -0,0 +1,88 @@ +import { createOrdersWorkflow } from "@medusajs/core-flows" +import { ModuleRegistrationName } from "@medusajs/modules-sdk" +import { + ContainerRegistrationKeys, + OrderStatus, + remoteQueryObjectFromString, +} from "@medusajs/utils" +import { + AuthenticatedMedusaRequest, + MedusaRequest, + MedusaResponse, +} from "../../../types/routing" +import { AdminPostDraftOrdersReq } from "./validators" + +export const GET = async (req: MedusaRequest, res: MedusaResponse) => { + const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY) + + const queryObject = remoteQueryObjectFromString({ + entryPoint: "order", + variables: { + filters: { + ...req.filterableFields, + is_draft_order: true, + }, + ...req.remoteQueryConfig.pagination, + }, + fields: req.remoteQueryConfig.fields, + }) + + const { rows: draft_orders, metadata } = await remoteQuery(queryObject) + + res.json({ + draft_orders, + count: metadata.count, + offset: metadata.skip, + limit: metadata.take, + }) +} + +export const POST = async ( + req: AuthenticatedMedusaRequest, + res: MedusaResponse +) => { + const input = req.validatedBody + const workflowInput = { + ...input, + no_notification: !!input.no_notification_order, + status: OrderStatus.DRAFT, + is_draft_order: true, + } + + if (!input.currency_code) { + const regionService = req.scope.resolve(ModuleRegistrationName.REGION) + const region = await regionService.retrieve(input.region_id) + input.currency_code = region.currency_code + } + + if (!input.email) { + const customerService = req.scope.resolve(ModuleRegistrationName.CUSTOMER) + const customer = await customerService.retrieve(input.customer_id) + input.email = customer.email + } + + const { result, errors } = await createOrdersWorkflow(req.scope).run({ + input: workflowInput, + throwOnError: false, + }) + + if (Array.isArray(errors) && errors[0]) { + throw errors[0].error + } + + const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY) + + const queryObject = remoteQueryObjectFromString({ + entryPoint: "order", + variables: { + filters: { + id: result.id, + }, + }, + fields: req.remoteQueryConfig.fields, + }) + + const draftOrder = await remoteQuery(queryObject) + + res.status(200).json({ draft_order: draftOrder[0] }) +} diff --git a/packages/medusa/src/api-v2/admin/draft-orders/validators.ts b/packages/medusa/src/api-v2/admin/draft-orders/validators.ts new file mode 100644 index 0000000000..a64064a502 --- /dev/null +++ b/packages/medusa/src/api-v2/admin/draft-orders/validators.ts @@ -0,0 +1,174 @@ +import { BigNumberInput } from "@medusajs/types" +import { Type } from "class-transformer" +import { + IsArray, + IsBoolean, + IsEmail, + IsEnum, + IsNotEmpty, + IsNumber, + IsObject, + IsOptional, + IsString, + ValidateIf, + ValidateNested, +} from "class-validator" +import { + AddressPayload, + FindParams, + extendedFindParamsMixin, +} from "../../../types/common" +import { IsType } from "../../../utils/validators/is-type" + +export class AdminGetOrdersOrderParams extends FindParams {} +/** + * Parameters used to filter and configure the pagination of the retrieved api keys. + */ +export class AdminGetOrdersParams extends extendedFindParamsMixin({ + limit: 50, + offset: 0, +}) { + /** + * Search parameter for api keys. + */ + @IsString({ each: true }) + @IsOptional() + id?: string | string[] + + /** + * Filter by title + */ + @IsString({ each: true }) + @IsOptional() + name?: string | string[] + + // Additional filters from BaseFilterable + @IsOptional() + @ValidateNested({ each: true }) + @Type(() => AdminGetOrdersParams) + $and?: AdminGetOrdersParams[] + + @IsOptional() + @ValidateNested({ each: true }) + @Type(() => AdminGetOrdersParams) + $or?: AdminGetOrdersParams[] +} + +enum Status { + completed = "completed", +} + +export class AdminPostDraftOrdersReq { + @IsEnum(Status) + @IsOptional() + status?: string + + @IsEmail() + @ValidateIf((o) => !o.customer_id) + email: string + + @IsString() + @IsOptional() + sales_channel_id?: string + + @IsOptional() + @IsType([AddressPayload, String]) + billing_address?: AddressPayload + + @IsOptional() + @IsType([AddressPayload, String]) + shipping_address?: AddressPayload + + @IsArray() + @Type(() => Item) + @IsNotEmpty() + @ValidateNested({ each: true }) + @IsOptional() + items?: Item[] + + @IsString() + region_id: string + + @IsArray() + @IsOptional() + promo_codes?: string[] + + @IsString() + @IsOptional() + currency_code?: string + + @IsString() + @IsOptional() + @ValidateIf((o) => !o.email) + customer_id?: string + + @IsBoolean() + @IsOptional() + no_notification_order?: boolean + + @IsArray() + @Type(() => ShippingMethod) + @IsNotEmpty() + @ValidateNested({ each: true }) + shipping_methods: ShippingMethod[] + + @IsObject() + @IsOptional() + metadata?: Record = {} +} + +class ShippingMethod { + @IsString() + @IsOptional() + shipping_method_id: string + + @IsString() + @IsOptional() + order_id: string + + @IsString() + name: string + + @IsString() + option_id: string + + @IsObject() + @IsOptional() + data?: Record = {} + + @IsNumber() + amount: BigNumberInput +} + +class Item { + @IsString() + @ValidateIf((o) => !o.variant_id) + title: string + + @IsString() + @IsOptional() + @ValidateIf((o) => !o.variant_id) + sku: string + + @IsString() + @IsOptional() + @ValidateIf((o) => !o.variant_id) + barcode: string + + @IsNumber() + @IsOptional() + unit_price: BigNumberInput + + @IsString() + @IsOptional() + variant_id?: string + + @IsNumber() + quantity: number + + @IsObject() + @IsOptional() + metadata?: Record = {} +} + +export class AdminDeleteOrdersOrderReq {} diff --git a/packages/medusa/src/api-v2/admin/users/[id]/route.ts b/packages/medusa/src/api-v2/admin/users/[id]/route.ts index b20b52f3a5..c15fb54f76 100644 --- a/packages/medusa/src/api-v2/admin/users/[id]/route.ts +++ b/packages/medusa/src/api-v2/admin/users/[id]/route.ts @@ -1,12 +1,12 @@ +import { deleteUsersWorkflow, updateUsersWorkflow } from "@medusajs/core-flows" +import { IUserModuleService, UpdateUserDTO } from "@medusajs/types" import { AuthenticatedMedusaRequest, MedusaResponse, } from "../../../../types/routing" -import { IUserModuleService, UpdateUserDTO } from "@medusajs/types" -import { deleteUsersWorkflow, updateUsersWorkflow } from "@medusajs/core-flows" +import { ModuleRegistrationName } from "@medusajs/modules-sdk" import { AdminUpdateUserRequest } from "../validators" -import { ModuleRegistrationName } from "../../../../../../modules-sdk/dist" // Get user export const GET = async ( diff --git a/packages/medusa/src/api-v2/middlewares.ts b/packages/medusa/src/api-v2/middlewares.ts index edbc074c54..644af58eb9 100644 --- a/packages/medusa/src/api-v2/middlewares.ts +++ b/packages/medusa/src/api-v2/middlewares.ts @@ -5,6 +5,7 @@ import { adminCollectionRoutesMiddlewares } from "./admin/collections/middleware import { adminCurrencyRoutesMiddlewares } from "./admin/currencies/middlewares" import { adminCustomerGroupRoutesMiddlewares } from "./admin/customer-groups/middlewares" import { adminCustomerRoutesMiddlewares } from "./admin/customers/middlewares" +import { adminDraftOrderRoutesMiddlewares } from "./admin/draft-orders/middlewares" import { adminFulfillmentRoutesMiddlewares } from "./admin/fulfillment/middlewares" import { adminInventoryRoutesMiddlewares } from "./admin/inventory-items/middlewares" import { adminInviteRoutesMiddlewares } from "./admin/invites/middlewares" @@ -59,6 +60,7 @@ export const config: MiddlewaresConfig = { ...adminCollectionRoutesMiddlewares, ...adminPricingRoutesMiddlewares, ...adminFulfillmentRoutesMiddlewares, + ...adminDraftOrderRoutesMiddlewares, ...adminSalesChannelRoutesMiddlewares, ...adminStockLocationRoutesMiddlewares, ...adminProductTypeRoutesMiddlewares, diff --git a/packages/modules-sdk/src/remote-link.ts b/packages/modules-sdk/src/remote-link.ts index b65d0d119b..21b25d7b7f 100644 --- a/packages/modules-sdk/src/remote-link.ts +++ b/packages/modules-sdk/src/remote-link.ts @@ -5,11 +5,12 @@ import { } from "@medusajs/types" import { isObject, promiseAll, toPascalCase } from "@medusajs/utils" +import { Modules } from "./definitions" import { MedusaModule } from "./medusa-module" import { linkingErrorMessage } from "./utils/linking-error" export type DeleteEntityInput = { - [moduleName: string]: { [linkableKey: string]: string | string[] } + [moduleName: string | Modules]: Record } export type RestoreEntityInput = DeleteEntityInput diff --git a/packages/order/integration-tests/__tests__/order-items-shipping.spec.ts b/packages/order/integration-tests/__tests__/order-items-shipping.spec.ts index bfa793d87a..45f5eb05a4 100644 --- a/packages/order/integration-tests/__tests__/order-items-shipping.spec.ts +++ b/packages/order/integration-tests/__tests__/order-items-shipping.spec.ts @@ -378,7 +378,7 @@ moduleIntegrationTestRunner({ }) }) - describe("addLineItems", () => { + describe("createLineItems", () => { it("should add a line item to order succesfully", async () => { const [createdOrder] = await service.create([ { @@ -386,7 +386,7 @@ moduleIntegrationTestRunner({ }, ]) - await service.addLineItems(createdOrder.id, [ + await service.createLineItems(createdOrder.id, [ { quantity: 1, unit_price: 100, @@ -415,7 +415,7 @@ moduleIntegrationTestRunner({ }, ]) - await service.addLineItems([ + await service.createLineItems([ { quantity: 1, unit_price: 100, @@ -466,7 +466,7 @@ moduleIntegrationTestRunner({ }, ]) - const items = await service.addLineItems([ + const items = await service.createLineItems([ { order_id: eurOrder.id, quantity: 1, @@ -502,7 +502,7 @@ moduleIntegrationTestRunner({ it("should throw if order does not exist", async () => { const error = await service - .addLineItems("foo", [ + .createLineItems("foo", [ { quantity: 1, unit_price: 100, @@ -523,7 +523,7 @@ moduleIntegrationTestRunner({ ]) const error = await service - .addLineItems(createdOrder.id, [ + .createLineItems(createdOrder.id, [ { unit_price: 10, title: "test", @@ -544,7 +544,7 @@ moduleIntegrationTestRunner({ ]) const error = await service - .addLineItems([ + .createLineItems([ { order_id: createdOrder.id, unit_price: 10, @@ -567,7 +567,7 @@ moduleIntegrationTestRunner({ }, ]) - const [item] = await service.addLineItems(createdOrder.id, [ + const [item] = await service.createLineItems(createdOrder.id, [ { quantity: 1, unit_price: 100, @@ -595,7 +595,7 @@ moduleIntegrationTestRunner({ }, ]) - const [item] = await service.addLineItems(createdOrder.id, [ + const [item] = await service.createLineItems(createdOrder.id, [ { quantity: 1, unit_price: 100, @@ -620,7 +620,7 @@ moduleIntegrationTestRunner({ }, ]) - const items = await service.addLineItems(createdOrder.id, [ + const items = await service.createLineItems(createdOrder.id, [ { quantity: 1, unit_price: 100, @@ -686,15 +686,15 @@ moduleIntegrationTestRunner({ }) }) - describe("removeLineItems", () => { - it("should remove a line item succesfully", async () => { + describe("deleteLineItems", () => { + it("should delete a line item succesfully", async () => { const [createdOrder] = await service.create([ { currency_code: "eur", }, ]) - const [item] = await service.addLineItems(createdOrder.id, [ + const [item] = await service.createLineItems(createdOrder.id, [ { quantity: 1, unit_price: 100, @@ -705,7 +705,7 @@ moduleIntegrationTestRunner({ expect(item.title).toBe("test") - await service.removeLineItems([item.id]) + await service.deleteLineItems([item.id]) const order = await service.retrieve(createdOrder.id, { relations: ["items"], @@ -714,14 +714,14 @@ moduleIntegrationTestRunner({ expect(order.items?.length).toBe(0) }) - it("should remove multiple line items succesfully", async () => { + it("should delete multiple line items succesfully", async () => { const [createdOrder] = await service.create([ { currency_code: "eur", }, ]) - const [item, item2] = await service.addLineItems(createdOrder.id, [ + const [item, item2] = await service.createLineItems(createdOrder.id, [ { quantity: 1, unit_price: 100, @@ -734,7 +734,7 @@ moduleIntegrationTestRunner({ }, ]) - await service.removeLineItems([item.id, item2.id]) + await service.deleteLineItems([item.id, item2.id]) const order = await service.retrieve(createdOrder.id, { relations: ["items"], @@ -744,7 +744,7 @@ moduleIntegrationTestRunner({ }) }) - describe("addShippingMethods", () => { + describe("createShippingMethods", () => { it("should add a shipping method to order succesfully", async () => { const [createdOrder] = await service.create([ { @@ -752,12 +752,15 @@ moduleIntegrationTestRunner({ }, ]) - const [method] = await service.addShippingMethods(createdOrder.id, [ - { - amount: 100, - name: "Test", - }, - ]) + const [method] = await service.createShippingMethods( + createdOrder.id, + [ + { + amount: 100, + name: "Test", + }, + ] + ) const order = await service.retrieve(createdOrder.id, { relations: ["shipping_methods"], @@ -779,7 +782,7 @@ moduleIntegrationTestRunner({ }, ]) - const methods = await service.addShippingMethods([ + const methods = await service.createShippingMethods([ { order_id: eurOrder.id, amount: 100, @@ -811,24 +814,27 @@ moduleIntegrationTestRunner({ }) }) - describe("removeShippingMethods", () => { - it("should remove a line item succesfully", async () => { + describe("deleteShippingMethods", () => { + it("should delete a line item succesfully", async () => { const [createdOrder] = await service.create([ { currency_code: "eur", }, ]) - const [method] = await service.addShippingMethods(createdOrder.id, [ - { - amount: 100, - name: "test", - }, - ]) + const [method] = await service.createShippingMethods( + createdOrder.id, + [ + { + amount: 100, + name: "test", + }, + ] + ) expect(method.id).not.toBe(null) - await service.removeShippingMethods(method.id) + await service.deleteShippingMethods(method.id) const order = await service.retrieve(createdOrder.id, { relations: ["shipping_methods"], @@ -846,7 +852,7 @@ moduleIntegrationTestRunner({ }, ]) - const [itemOne] = await service.addLineItems(createdOrder.id, [ + const [itemOne] = await service.createLineItems(createdOrder.id, [ { quantity: 1, unit_price: 100, @@ -854,7 +860,7 @@ moduleIntegrationTestRunner({ }, ]) - const [itemTwo] = await service.addLineItems(createdOrder.id, [ + const [itemTwo] = await service.createLineItems(createdOrder.id, [ { quantity: 2, unit_price: 200, @@ -901,7 +907,7 @@ moduleIntegrationTestRunner({ }, ]) - const [itemOne] = await service.addLineItems(createdOrder.id, [ + const [itemOne] = await service.createLineItems(createdOrder.id, [ { quantity: 1, unit_price: 100, @@ -961,14 +967,14 @@ moduleIntegrationTestRunner({ expect(order.items[0].adjustments?.length).toBe(1) }) - it("should remove all line item adjustments for an order", async () => { + it("should delete all line item adjustments for an order", async () => { const [createdOrder] = await service.create([ { currency_code: "eur", }, ]) - const [itemOne] = await service.addLineItems(createdOrder.id, [ + const [itemOne] = await service.createLineItems(createdOrder.id, [ { quantity: 1, unit_price: 100, @@ -1023,7 +1029,7 @@ moduleIntegrationTestRunner({ }, ]) - const [itemOne] = await service.addLineItems(createdOrder.id, [ + const [itemOne] = await service.createLineItems(createdOrder.id, [ { quantity: 1, unit_price: 100, @@ -1086,7 +1092,7 @@ moduleIntegrationTestRunner({ }) }) - describe("addLineItemAdjustments", () => { + describe("createLineItemAdjustments", () => { it("should add line item adjustments for items in an order", async () => { const [createdOrder] = await service.create([ { @@ -1094,7 +1100,7 @@ moduleIntegrationTestRunner({ }, ]) - const [itemOne] = await service.addLineItems(createdOrder.id, [ + const [itemOne] = await service.createLineItems(createdOrder.id, [ { quantity: 1, unit_price: 100, @@ -1102,7 +1108,7 @@ moduleIntegrationTestRunner({ }, ]) - const adjustments = await service.addLineItemAdjustments( + const adjustments = await service.createLineItemAdjustments( createdOrder.id, [ { @@ -1131,14 +1137,14 @@ moduleIntegrationTestRunner({ }, ]) - const [itemOne] = await service.addLineItems(createdOrder.id, [ + const [itemOne] = await service.createLineItems(createdOrder.id, [ { quantity: 1, unit_price: 100, title: "test", }, ]) - const [itemTwo] = await service.addLineItems(createdOrder.id, [ + const [itemTwo] = await service.createLineItems(createdOrder.id, [ { quantity: 2, unit_price: 200, @@ -1146,7 +1152,7 @@ moduleIntegrationTestRunner({ }, ]) - const adjustments = await service.addLineItemAdjustments( + const adjustments = await service.createLineItemAdjustments( createdOrder.id, [ { @@ -1188,14 +1194,14 @@ moduleIntegrationTestRunner({ }, ]) - const [itemOne] = await service.addLineItems(orderOne.id, [ + const [itemOne] = await service.createLineItems(orderOne.id, [ { quantity: 1, unit_price: 100, title: "test", }, ]) - const [itemTwo] = await service.addLineItems(orderTwo.id, [ + const [itemTwo] = await service.createLineItems(orderTwo.id, [ { quantity: 2, unit_price: 200, @@ -1203,7 +1209,7 @@ moduleIntegrationTestRunner({ }, ]) - await service.addLineItemAdjustments([ + await service.createLineItemAdjustments([ // item from order one { item_id: itemOne.id, @@ -1255,15 +1261,15 @@ moduleIntegrationTestRunner({ }) }) - describe("removeLineItemAdjustments", () => { - it("should remove a line item succesfully", async () => { + describe("deleteLineItemAdjustments", () => { + it("should delete a line item succesfully", async () => { const [createdOrder] = await service.create([ { currency_code: "eur", }, ]) - const [item] = await service.addLineItems(createdOrder.id, [ + const [item] = await service.createLineItems(createdOrder.id, [ { quantity: 1, unit_price: 100, @@ -1271,7 +1277,7 @@ moduleIntegrationTestRunner({ }, ]) - const [adjustment] = await service.addLineItemAdjustments( + const [adjustment] = await service.createLineItemAdjustments( createdOrder.id, [ { @@ -1283,7 +1289,7 @@ moduleIntegrationTestRunner({ expect(adjustment.item_id).toBe(item.id) - await service.removeLineItemAdjustments(adjustment.id) + await service.deleteLineItemAdjustments(adjustment.id) const adjustments = await service.listLineItemAdjustments({ item_id: item.id, @@ -1292,14 +1298,14 @@ moduleIntegrationTestRunner({ expect(adjustments?.length).toBe(0) }) - it("should remove a line item succesfully with selector", async () => { + it("should delete a line item succesfully with selector", async () => { const [createdOrder] = await service.create([ { currency_code: "eur", }, ]) - const [item] = await service.addLineItems(createdOrder.id, [ + const [item] = await service.createLineItems(createdOrder.id, [ { quantity: 1, unit_price: 100, @@ -1307,7 +1313,7 @@ moduleIntegrationTestRunner({ }, ]) - const [adjustment] = await service.addLineItemAdjustments( + const [adjustment] = await service.createLineItemAdjustments( createdOrder.id, [ { @@ -1319,7 +1325,7 @@ moduleIntegrationTestRunner({ expect(adjustment.item_id).toBe(item.id) - await service.removeLineItemAdjustments({ item_id: item.id }) + await service.deleteLineItemAdjustments({ item_id: item.id }) const adjustments = await service.listLineItemAdjustments({ item_id: item.id, @@ -1337,7 +1343,7 @@ moduleIntegrationTestRunner({ }, ]) - const [shippingMethodOne] = await service.addShippingMethods( + const [shippingMethodOne] = await service.createShippingMethods( createdOrder.id, [ { @@ -1347,7 +1353,7 @@ moduleIntegrationTestRunner({ ] ) - const [shippingMethodTwo] = await service.addShippingMethods( + const [shippingMethodTwo] = await service.createShippingMethods( createdOrder.id, [ { @@ -1396,7 +1402,7 @@ moduleIntegrationTestRunner({ }, ]) - const [shippingMethodOne] = await service.addShippingMethods( + const [shippingMethodOne] = await service.createShippingMethods( createdOrder.id, [ { @@ -1459,14 +1465,14 @@ moduleIntegrationTestRunner({ expect(order.shipping_methods?.[0].adjustments?.length).toBe(1) }) - it("should remove all shipping method adjustments for an order", async () => { + it("should delete all shipping method adjustments for an order", async () => { const [createdOrder] = await service.create([ { currency_code: "eur", }, ]) - const [shippingMethodOne] = await service.addShippingMethods( + const [shippingMethodOne] = await service.createShippingMethods( createdOrder.id, [ { @@ -1523,7 +1529,7 @@ moduleIntegrationTestRunner({ }, ]) - const [shippingMethodOne] = await service.addShippingMethods( + const [shippingMethodOne] = await service.createShippingMethods( createdOrder.id, [ { @@ -1587,7 +1593,7 @@ moduleIntegrationTestRunner({ }) }) - describe("addShippingMethodAdjustments", () => { + describe("createShippingMethodAdjustments", () => { it("should add shipping method adjustments in an order", async () => { const [createdOrder] = await service.create([ { @@ -1595,7 +1601,7 @@ moduleIntegrationTestRunner({ }, ]) - const [shippingMethodOne] = await service.addShippingMethods( + const [shippingMethodOne] = await service.createShippingMethods( createdOrder.id, [ { @@ -1605,7 +1611,7 @@ moduleIntegrationTestRunner({ ] ) - const adjustments = await service.addShippingMethodAdjustments( + const adjustments = await service.createShippingMethodAdjustments( createdOrder.id, [ { @@ -1634,7 +1640,7 @@ moduleIntegrationTestRunner({ }, ]) - const [shippingMethodOne] = await service.addShippingMethods( + const [shippingMethodOne] = await service.createShippingMethods( createdOrder.id, [ { @@ -1643,7 +1649,7 @@ moduleIntegrationTestRunner({ }, ] ) - const [shippingMethodTwo] = await service.addShippingMethods( + const [shippingMethodTwo] = await service.createShippingMethods( createdOrder.id, [ { @@ -1653,7 +1659,7 @@ moduleIntegrationTestRunner({ ] ) - const adjustments = await service.addShippingMethodAdjustments( + const adjustments = await service.createShippingMethodAdjustments( createdOrder.id, [ { @@ -1697,7 +1703,7 @@ moduleIntegrationTestRunner({ }, ]) - const [shippingMethodOne] = await service.addShippingMethods( + const [shippingMethodOne] = await service.createShippingMethods( orderOne.id, [ { @@ -1706,7 +1712,7 @@ moduleIntegrationTestRunner({ }, ] ) - const [shippingMethodTwo] = await service.addShippingMethods( + const [shippingMethodTwo] = await service.createShippingMethods( orderTwo.id, [ { @@ -1716,7 +1722,7 @@ moduleIntegrationTestRunner({ ] ) - await service.addShippingMethodAdjustments([ + await service.createShippingMethodAdjustments([ // item from order one { shipping_method_id: shippingMethodOne.id, @@ -1782,7 +1788,7 @@ moduleIntegrationTestRunner({ }, ]) - const [shippingMethodOne] = await service.addShippingMethods( + const [shippingMethodOne] = await service.createShippingMethods( orderOne.id, [ { @@ -1793,7 +1799,7 @@ moduleIntegrationTestRunner({ ) const error = await service - .addShippingMethodAdjustments(orderTwo.id, [ + .createShippingMethodAdjustments(orderTwo.id, [ { shipping_method_id: shippingMethodOne.id, amount: 100, @@ -1808,22 +1814,25 @@ moduleIntegrationTestRunner({ }) }) - describe("removeShippingMethodAdjustments", () => { - it("should remove a shipping method succesfully", async () => { + describe("deleteShippingMethodAdjustments", () => { + it("should delete a shipping method succesfully", async () => { const [createdOrder] = await service.create([ { currency_code: "eur", }, ]) - const [method] = await service.addShippingMethods(createdOrder.id, [ - { - amount: 100, - name: "test", - }, - ]) + const [method] = await service.createShippingMethods( + createdOrder.id, + [ + { + amount: 100, + name: "test", + }, + ] + ) - const [adjustment] = await service.addShippingMethodAdjustments( + const [adjustment] = await service.createShippingMethodAdjustments( createdOrder.id, [ { @@ -1836,7 +1845,7 @@ moduleIntegrationTestRunner({ expect(adjustment.shipping_method_id).toBe(method.id) - await service.removeShippingMethodAdjustments(adjustment.id) + await service.deleteShippingMethodAdjustments(adjustment.id) const adjustments = await service.listShippingMethodAdjustments({ shipping_method_id: method.id, @@ -1845,14 +1854,14 @@ moduleIntegrationTestRunner({ expect(adjustments?.length).toBe(0) }) - it("should remove a shipping method succesfully with selector", async () => { + it("should delete a shipping method succesfully with selector", async () => { const [createdOrder] = await service.create([ { currency_code: "eur", }, ]) - const [shippingMethod] = await service.addShippingMethods( + const [shippingMethod] = await service.createShippingMethods( createdOrder.id, [ { @@ -1862,7 +1871,7 @@ moduleIntegrationTestRunner({ ] ) - const [adjustment] = await service.addShippingMethodAdjustments( + const [adjustment] = await service.createShippingMethodAdjustments( createdOrder.id, [ { @@ -1875,7 +1884,7 @@ moduleIntegrationTestRunner({ expect(adjustment.shipping_method_id).toBe(shippingMethod.id) - await service.removeShippingMethodAdjustments({ + await service.deleteShippingMethodAdjustments({ shipping_method_id: shippingMethod.id, }) @@ -1895,7 +1904,7 @@ moduleIntegrationTestRunner({ }, ]) - const [itemOne] = await service.addLineItems(createdOrder.id, [ + const [itemOne] = await service.createLineItems(createdOrder.id, [ { quantity: 1, unit_price: 100, @@ -1903,7 +1912,7 @@ moduleIntegrationTestRunner({ }, ]) - const [itemTwo] = await service.addLineItems(createdOrder.id, [ + const [itemTwo] = await service.createLineItems(createdOrder.id, [ { quantity: 2, unit_price: 200, @@ -1947,7 +1956,7 @@ moduleIntegrationTestRunner({ }, ]) - const [itemOne] = await service.addLineItems(createdOrder.id, [ + const [itemOne] = await service.createLineItems(createdOrder.id, [ { quantity: 1, unit_price: 100, @@ -2004,14 +2013,14 @@ moduleIntegrationTestRunner({ expect(order.items[0].tax_lines.length).toBe(1) }) - it("should remove all line item tax lines for an order", async () => { + it("should delete all line item tax lines for an order", async () => { const [createdOrder] = await service.create([ { currency_code: "eur", }, ]) - const [itemOne] = await service.addLineItems(createdOrder.id, [ + const [itemOne] = await service.createLineItems(createdOrder.id, [ { quantity: 1, unit_price: 100, @@ -2063,7 +2072,7 @@ moduleIntegrationTestRunner({ }, ]) - const [itemOne] = await service.addLineItems(createdOrder.id, [ + const [itemOne] = await service.createLineItems(createdOrder.id, [ { quantity: 1, unit_price: 100, @@ -2122,14 +2131,14 @@ moduleIntegrationTestRunner({ expect(order.items[0].tax_lines.length).toBe(1) }) - it("should remove, update, and create line item tax lines for an order", async () => { + it("should delete, update, and create line item tax lines for an order", async () => { const [createdOrder] = await service.create([ { currency_code: "eur", }, ]) - const [itemOne] = await service.addLineItems(createdOrder.id, [ + const [itemOne] = await service.createLineItems(createdOrder.id, [ { quantity: 1, unit_price: 100, @@ -2180,7 +2189,7 @@ moduleIntegrationTestRunner({ rate: 25, code: "TX-2", }, - // remove: should remove the initial tax line for itemOne + // delete: should delete the initial tax line for itemOne ]) const order = await service.retrieve(createdOrder.id, { @@ -2213,7 +2222,7 @@ moduleIntegrationTestRunner({ }) }) - describe("addLineItemAdjustments", () => { + describe("createLineItemAdjustments", () => { it("should add line item tax lines for items in an order", async () => { const [createdOrder] = await service.create([ { @@ -2221,7 +2230,7 @@ moduleIntegrationTestRunner({ }, ]) - const [itemOne] = await service.addLineItems(createdOrder.id, [ + const [itemOne] = await service.createLineItems(createdOrder.id, [ { quantity: 1, unit_price: 100, @@ -2229,13 +2238,16 @@ moduleIntegrationTestRunner({ }, ]) - const taxLines = await service.addLineItemTaxLines(createdOrder.id, [ - { - item_id: itemOne.id, - rate: 20, - code: "TX", - }, - ]) + const taxLines = await service.createLineItemTaxLines( + createdOrder.id, + [ + { + item_id: itemOne.id, + rate: 20, + code: "TX", + }, + ] + ) expect(taxLines).toEqual( expect.arrayContaining([ @@ -2255,14 +2267,14 @@ moduleIntegrationTestRunner({ }, ]) - const [itemOne] = await service.addLineItems(createdOrder.id, [ + const [itemOne] = await service.createLineItems(createdOrder.id, [ { quantity: 1, unit_price: 100, title: "test", }, ]) - const [itemTwo] = await service.addLineItems(createdOrder.id, [ + const [itemTwo] = await service.createLineItems(createdOrder.id, [ { quantity: 2, unit_price: 200, @@ -2270,18 +2282,21 @@ moduleIntegrationTestRunner({ }, ]) - const taxLines = await service.addLineItemTaxLines(createdOrder.id, [ - { - item_id: itemOne.id, - rate: 20, - code: "TX", - }, - { - item_id: itemTwo.id, - rate: 20, - code: "TX", - }, - ]) + const taxLines = await service.createLineItemTaxLines( + createdOrder.id, + [ + { + item_id: itemOne.id, + rate: 20, + code: "TX", + }, + { + item_id: itemTwo.id, + rate: 20, + code: "TX", + }, + ] + ) expect(taxLines).toEqual( expect.arrayContaining([ @@ -2311,14 +2326,14 @@ moduleIntegrationTestRunner({ }, ]) - const [itemOne] = await service.addLineItems(orderOne.id, [ + const [itemOne] = await service.createLineItems(orderOne.id, [ { quantity: 1, unit_price: 100, title: "test", }, ]) - const [itemTwo] = await service.addLineItems(orderTwo.id, [ + const [itemTwo] = await service.createLineItems(orderTwo.id, [ { quantity: 2, unit_price: 200, @@ -2326,7 +2341,7 @@ moduleIntegrationTestRunner({ }, ]) - await service.addLineItemTaxLines([ + await service.createLineItemTaxLines([ // item from order one { item_id: itemOne.id, @@ -2378,15 +2393,15 @@ moduleIntegrationTestRunner({ }) }) - describe("removeLineItemAdjustments", () => { - it("should remove line item tax line succesfully", async () => { + describe("deleteLineItemAdjustments", () => { + it("should delete line item tax line succesfully", async () => { const [createdOrder] = await service.create([ { currency_code: "eur", }, ]) - const [item] = await service.addLineItems(createdOrder.id, [ + const [item] = await service.createLineItems(createdOrder.id, [ { quantity: 1, unit_price: 100, @@ -2394,17 +2409,20 @@ moduleIntegrationTestRunner({ }, ]) - const [taxLine] = await service.addLineItemTaxLines(createdOrder.id, [ - { - item_id: item.id, - rate: 20, - code: "TX", - }, - ]) + const [taxLine] = await service.createLineItemTaxLines( + createdOrder.id, + [ + { + item_id: item.id, + rate: 20, + code: "TX", + }, + ] + ) expect(taxLine.item_id).toBe(item.id) - await service.removeLineItemTaxLines(taxLine.id) + await service.deleteLineItemTaxLines(taxLine.id) const taxLines = await service.listLineItemTaxLines({ item_id: item.id, @@ -2413,14 +2431,14 @@ moduleIntegrationTestRunner({ expect(taxLines?.length).toBe(0) }) - it("should remove line item tax lines succesfully with selector", async () => { + it("should delete line item tax lines succesfully with selector", async () => { const [createdOrder] = await service.create([ { currency_code: "eur", }, ]) - const [item] = await service.addLineItems(createdOrder.id, [ + const [item] = await service.createLineItems(createdOrder.id, [ { quantity: 1, unit_price: 100, @@ -2428,17 +2446,20 @@ moduleIntegrationTestRunner({ }, ]) - const [taxLine] = await service.addLineItemTaxLines(createdOrder.id, [ - { - item_id: item.id, - rate: 20, - code: "TX", - }, - ]) + const [taxLine] = await service.createLineItemTaxLines( + createdOrder.id, + [ + { + item_id: item.id, + rate: 20, + code: "TX", + }, + ] + ) expect(taxLine.item_id).toBe(item.id) - await service.removeLineItemTaxLines({ item_id: item.id }) + await service.deleteLineItemTaxLines({ item_id: item.id }) const taxLines = await service.listLineItemTaxLines({ item_id: item.id, diff --git a/packages/order/src/joiner-config.ts b/packages/order/src/joiner-config.ts index f8336e6ac2..ed28ce8f01 100644 --- a/packages/order/src/joiner-config.ts +++ b/packages/order/src/joiner-config.ts @@ -1,10 +1,12 @@ import { Modules } from "@medusajs/modules-sdk" import { ModuleJoinerConfig } from "@medusajs/types" import { MapToConfig } from "@medusajs/utils" +import { LineItem } from "@models" +import Order from "./models/order" export const LinkableKeys: Record = { - order_id: "Order", - order_item_id: "OrderLineItem", + order_id: Order.name, + order_item_id: LineItem.name, } const entityLinkableKeysMap: MapToConfig = {} @@ -22,5 +24,12 @@ export const joinerConfig: ModuleJoinerConfig = { serviceName: Modules.ORDER, primaryKeys: ["id"], linkableKeys: LinkableKeys, - alias: [], + alias: [ + { + name: ["order", "orders"], + args: { + entity: Order.name, + }, + }, + ], } as ModuleJoinerConfig diff --git a/packages/order/src/migrations/Migration20240219102530.ts b/packages/order/src/migrations/Migration20240219102530.ts index 308d670acb..66bdabe072 100644 --- a/packages/order/src/migrations/Migration20240219102530.ts +++ b/packages/order/src/migrations/Migration20240219102530.ts @@ -33,16 +33,8 @@ export class Migration20240219102530 extends Migration { "customer_id" TEXT NULL, "version" INTEGER NOT NULL DEFAULT 1, "sales_channel_id" TEXT NULL, - "status" text check ( - "status" IN ( - 'pending', - 'completed', - 'draft', - 'archived', - 'canceled', - 'requires_action' - ) - ) NOT NULL DEFAULT 'pending', + "status" text NOT NULL, + "is_draft_order" BOOLEAN NOT NULL DEFAULT false, "email" text NULL, "currency_code" text NOT NULL, "shipping_address_id" text NULL, @@ -59,6 +51,28 @@ export class Migration20240219102530 extends Migration { ALTER TABLE "order" ADD COLUMN if NOT exists "deleted_at" timestamptz NULL; + ALTER TABLE "order" + ADD COLUMN if NOT exists "is_draft_order" BOOLEAN NOT NULL DEFAULT false; + + ALTER TABLE "order" + ADD COLUMN if NOT exists "version" INTEGER NOT NULL DEFAULT 1; + + + ALTER TABLE "order" ALTER COLUMN status TYPE text; + DROP TYPE IF EXISTS order_status_enum CASCADE; + CREATE TYPE order_status_enum AS ENUM ( + 'pending', + 'completed', + 'draft', + 'archived', + 'canceled', + 'requires_action' + ); + ALTER TABLE "order" ALTER COLUMN status DROP DEFAULT; + ALTER TABLE "order" ALTER COLUMN status TYPE order_status_enum USING (status::text::order_status_enum); + ALTER TABLE "order" ALTER COLUMN status SET DEFAULT 'pending'; + + ALTER TABLE "order" DROP constraint if EXISTS "FK_6ff7e874f01b478c115fdd462eb" CASCADE; ALTER TABLE "order" DROP constraint if EXISTS "FK_19b0c6293443d1b464f604c3316" CASCADE; @@ -129,6 +143,10 @@ export class Migration20240219102530 extends Migration { CREATE INDEX IF NOT EXISTS "IDX_order_deleted_at" ON "order" ( deleted_at + ); + + CREATE INDEX IF NOT EXISTS "IDX_order_is_draft_order" ON "order" ( + is_draft_order ) WHERE deleted_at IS NOT NULL; @@ -284,6 +302,7 @@ export class Migration20240219102530 extends Migration { "raw_compare_at_unit_price" JSONB NULL, "unit_price" NUMERIC NOT NULL, "raw_unit_price" JSONB NOT NULL, + "metadata" JSONB NULL, "created_at" TIMESTAMPTZ NOT NULL DEFAULT Now(), "updated_at" TIMESTAMPTZ NOT NULL DEFAULT Now(), CONSTRAINT "order_line_item_pkey" PRIMARY KEY ("id") @@ -423,14 +442,12 @@ export class Migration20240219102530 extends Migration { ALTER TABLE if exists "order" ADD CONSTRAINT "order_shipping_address_id_foreign" FOREIGN KEY ("shipping_address_id") REFERENCES "order_address" ("id") ON UPDATE CASCADE ON - DELETE - SET NULL; + DELETE CASCADE; ALTER TABLE if exists "order" ADD CONSTRAINT "order_billing_address_id_foreign" FOREIGN KEY ("billing_address_id") REFERENCES "order_address" ("id") ON UPDATE CASCADE ON - DELETE - SET NULL; + DELETE CASCADE; ALTER TABLE if exists "order_change" ADD CONSTRAINT "order_change_order_id_foreign" FOREIGN KEY ("order_id") REFERENCES "order" ("id") ON diff --git a/packages/order/src/models/line-item-adjustment.ts b/packages/order/src/models/line-item-adjustment.ts index 710deddcdf..be1f27b346 100644 --- a/packages/order/src/models/line-item-adjustment.ts +++ b/packages/order/src/models/line-item-adjustment.ts @@ -2,13 +2,7 @@ import { createPsqlIndexStatementHelper, generateEntityId, } from "@medusajs/utils" -import { - BeforeCreate, - Cascade, - Entity, - ManyToOne, - OnInit, -} from "@mikro-orm/core" +import { BeforeCreate, Entity, ManyToOne, OnInit } from "@mikro-orm/core" import AdjustmentLine from "./adjustment-line" import LineItem from "./line-item" @@ -28,7 +22,7 @@ export default class LineItemAdjustment extends AdjustmentLine { entity: () => LineItem, columnType: "text", fieldName: "item_id", - cascade: [Cascade.REMOVE], + onDelete: "cascade", mapToPk: true, }) @ItemIdIndex.MikroORMIndex() diff --git a/packages/order/src/models/line-item.ts b/packages/order/src/models/line-item.ts index b4e3096b94..5d981d9b0c 100644 --- a/packages/order/src/models/line-item.ts +++ b/packages/order/src/models/line-item.ts @@ -122,15 +122,18 @@ export default class LineItem { raw_unit_price: BigNumberRawValue @OneToMany(() => LineItemTaxLine, (taxLine) => taxLine.item, { - cascade: [Cascade.PERSIST], + cascade: [Cascade.PERSIST, "soft-remove" as Cascade], }) tax_lines = new Collection(this) @OneToMany(() => LineItemAdjustment, (adjustment) => adjustment.item, { - cascade: [Cascade.PERSIST], + cascade: [Cascade.PERSIST, "soft-remove" as Cascade], }) adjustments = new Collection(this) + @Property({ columnType: "jsonb", nullable: true }) + metadata: Record | null = null + @Property({ onCreate: () => new Date(), columnType: "timestamptz", diff --git a/packages/order/src/models/order-change-action.ts b/packages/order/src/models/order-change-action.ts index 1542e2beed..8f550e48a4 100644 --- a/packages/order/src/models/order-change-action.ts +++ b/packages/order/src/models/order-change-action.ts @@ -7,7 +7,6 @@ import { } from "@medusajs/utils" import { BeforeCreate, - Cascade, Entity, ManyToOne, OnInit, @@ -50,7 +49,7 @@ export default class OrderChangeAction { entity: () => Order, columnType: "text", fieldName: "order_id", - cascade: [Cascade.REMOVE], + onDelete: "cascade", mapToPk: true, nullable: true, }) @@ -70,7 +69,7 @@ export default class OrderChangeAction { entity: () => OrderChange, columnType: "text", fieldName: "order_change_id", - cascade: [Cascade.REMOVE], + onDelete: "cascade", mapToPk: true, nullable: true, }) diff --git a/packages/order/src/models/order-change.ts b/packages/order/src/models/order-change.ts index 7b1ffbf40b..03a35e1591 100644 --- a/packages/order/src/models/order-change.ts +++ b/packages/order/src/models/order-change.ts @@ -49,7 +49,7 @@ export default class OrderChange { entity: () => Order, columnType: "text", fieldName: "order_id", - cascade: [Cascade.REMOVE], + onDelete: "cascade", mapToPk: true, }) @OrderIdIndex.MikroORMIndex() @@ -65,7 +65,7 @@ export default class OrderChange { version: number @OneToMany(() => OrderChangeAction, (action) => action.order_change, { - cascade: [Cascade.PERSIST], + cascade: [Cascade.PERSIST, "sotf-remove" as Cascade], }) actions = new Collection(this) diff --git a/packages/order/src/models/order-summary.ts b/packages/order/src/models/order-summary.ts index a77b7f553d..2d83ef49b7 100644 --- a/packages/order/src/models/order-summary.ts +++ b/packages/order/src/models/order-summary.ts @@ -5,7 +5,6 @@ import { } from "@medusajs/utils" import { BeforeCreate, - Cascade, Entity, ManyToOne, OnInit, @@ -55,14 +54,11 @@ export default class OrderSummary { columnType: "text", fieldName: "order_id", mapToPk: true, - cascade: [Cascade.REMOVE], + onDelete: "cascade", }) order_id: string - @ManyToOne({ - entity: () => Order, - fieldName: "order_id", - cascade: [Cascade.REMOVE], + @ManyToOne(() => Order, { persist: false, }) order: Order diff --git a/packages/order/src/models/order.ts b/packages/order/src/models/order.ts index 86c2d02ced..8f61856643 100644 --- a/packages/order/src/models/order.ts +++ b/packages/order/src/models/order.ts @@ -70,6 +70,12 @@ const BillingAddressIdIndex = createPsqlIndexStatementHelper({ where: "deleted_at IS NOT NULL", }) +const IsDraftOrderIndex = createPsqlIndexStatementHelper({ + tableName: "order", + columns: "is_draft_order", + where: "deleted_at IS NOT NULL", +}) + @Entity({ tableName: "order" }) export default class Order { [OptionalProps]?: OptionalOrderProps @@ -107,6 +113,12 @@ export default class Order { @Enum({ items: () => OrderStatus, default: OrderStatus.PENDING }) status: OrderStatus + @Property({ + columnType: "boolean", + }) + @IsDraftOrderIndex.MikroORMIndex() + is_draft_order = false + @Property({ columnType: "text", nullable: true }) email: string | null = null diff --git a/packages/order/src/models/shipping-method-adjustment.ts b/packages/order/src/models/shipping-method-adjustment.ts index 9ff8849339..9f72328b09 100644 --- a/packages/order/src/models/shipping-method-adjustment.ts +++ b/packages/order/src/models/shipping-method-adjustment.ts @@ -2,13 +2,7 @@ import { createPsqlIndexStatementHelper, generateEntityId, } from "@medusajs/utils" -import { - BeforeCreate, - Cascade, - Entity, - ManyToOne, - OnInit, -} from "@mikro-orm/core" +import { BeforeCreate, Entity, ManyToOne, OnInit } from "@mikro-orm/core" import AdjustmentLine from "./adjustment-line" import ShippingMethod from "./shipping-method" @@ -29,7 +23,7 @@ export default class ShippingMethodAdjustment extends AdjustmentLine { columnType: "text", fieldName: "shipping_method_id", mapToPk: true, - cascade: [Cascade.REMOVE], + onDelete: "cascade", }) @ShippingMethodIdIdIndex.MikroORMIndex() shipping_method_id: string diff --git a/packages/order/src/models/shipping-method-tax-line.ts b/packages/order/src/models/shipping-method-tax-line.ts index 09d0358b19..3244b18f42 100644 --- a/packages/order/src/models/shipping-method-tax-line.ts +++ b/packages/order/src/models/shipping-method-tax-line.ts @@ -2,13 +2,7 @@ import { createPsqlIndexStatementHelper, generateEntityId, } from "@medusajs/utils" -import { - BeforeCreate, - Cascade, - Entity, - ManyToOne, - OnInit, -} from "@mikro-orm/core" +import { BeforeCreate, Entity, ManyToOne, OnInit } from "@mikro-orm/core" import ShippingMethod from "./shipping-method" import TaxLine from "./tax-line" @@ -29,7 +23,7 @@ export default class ShippingMethodTaxLine extends TaxLine { fieldName: "shipping_method_id", columnType: "text", mapToPk: true, - cascade: [Cascade.REMOVE], + onDelete: "cascade", }) @ShippingMethodIdIdIndex.MikroORMIndex() shipping_method_id: string diff --git a/packages/order/src/models/shipping-method.ts b/packages/order/src/models/shipping-method.ts index 15d725c3a9..cadc462644 100644 --- a/packages/order/src/models/shipping-method.ts +++ b/packages/order/src/models/shipping-method.ts @@ -46,15 +46,12 @@ export default class ShippingMethod { columnType: "text", fieldName: "order_id", mapToPk: true, - cascade: [Cascade.REMOVE], + onDelete: "cascade", }) @OrderIdIndex.MikroORMIndex() order_id: string - @ManyToOne({ - entity: () => Order, - fieldName: "order_id", - cascade: [Cascade.REMOVE], + @ManyToOne(() => Order, { persist: false, }) order: Order diff --git a/packages/order/src/models/transaction.ts b/packages/order/src/models/transaction.ts index c068ee8d1e..a9ffa3d0af 100644 --- a/packages/order/src/models/transaction.ts +++ b/packages/order/src/models/transaction.ts @@ -7,7 +7,6 @@ import { } from "@medusajs/utils" import { BeforeCreate, - Cascade, Entity, ManyToOne, OnInit, @@ -45,7 +44,7 @@ export default class Transaction { entity: () => Order, columnType: "text", fieldName: "order_id", - cascade: [Cascade.REMOVE], + onDelete: "cascade", mapToPk: true, }) @OrderIdIndex.MikroORMIndex() diff --git a/packages/order/src/services/order-module-service.ts b/packages/order/src/services/order-module-service.ts index d06c381530..65d90218a4 100644 --- a/packages/order/src/services/order-module-service.ts +++ b/packages/order/src/services/order-module-service.ts @@ -1,7 +1,6 @@ import { Context, DAL, - FilterableLineItemTaxLineProps, FindConfig, InternalModuleDeclaration, IOrderModuleService, @@ -299,7 +298,7 @@ export default class OrderModuleService< } if (lineItemsToCreate.length) { - await this.addLineItemsBulk_(lineItemsToCreate, sharedContext) + await this.createLineItemsBulk_(lineItemsToCreate, sharedContext) } return createdOrders @@ -314,7 +313,7 @@ export default class OrderModuleService< sharedContext?: Context ): Promise async update( - selector: Partial, + selector: Partial, data: OrderTypes.UpdateOrderDTO, sharedContext?: Context ): Promise @@ -324,7 +323,7 @@ export default class OrderModuleService< dataOrIdOrSelector: | OrderTypes.UpdateOrderDTO[] | string - | Partial, + | Partial, data?: OrderTypes.UpdateOrderDTO, @MedusaContext() sharedContext: Context = {} ): Promise { @@ -344,7 +343,7 @@ export default class OrderModuleService< dataOrIdOrSelector: | OrderTypes.UpdateOrderDTO[] | string - | Partial, + | Partial, data?: OrderTypes.UpdateOrderDTO, @MedusaContext() sharedContext: Context = {} ) { @@ -377,20 +376,20 @@ export default class OrderModuleService< return result } - addLineItems( + createLineItems( data: OrderTypes.CreateOrderLineItemForOrderDTO ): Promise - addLineItems( + createLineItems( data: OrderTypes.CreateOrderLineItemForOrderDTO[] ): Promise - addLineItems( + createLineItems( orderId: string, items: OrderTypes.CreateOrderLineItemDTO[], sharedContext?: Context ): Promise @InjectManager("baseRepository_") - async addLineItems( + async createLineItems( orderIdOrData: | string | OrderTypes.CreateOrderLineItemForOrderDTO[] @@ -402,7 +401,7 @@ export default class OrderModuleService< ): Promise { let items: LineItem[] = [] if (isString(orderIdOrData)) { - items = await this.addLineItems_( + items = await this.createLineItems_( orderIdOrData, data as OrderTypes.CreateOrderLineItemDTO[], sharedContext @@ -411,7 +410,7 @@ export default class OrderModuleService< const data = Array.isArray(orderIdOrData) ? orderIdOrData : [orderIdOrData] - items = await this.addLineItemsBulk_(data, sharedContext) + items = await this.createLineItemsBulk_(data, sharedContext) } return await this.baseRepository_.serialize( @@ -423,7 +422,7 @@ export default class OrderModuleService< } @InjectTransactionManager("baseRepository_") - protected async addLineItems_( + protected async createLineItems_( orderId: string, items: OrderTypes.CreateOrderLineItemDTO[], @MedusaContext() sharedContext: Context = {} @@ -442,11 +441,11 @@ export default class OrderModuleService< } }) - return await this.addLineItemsBulk_(toUpdate, sharedContext) + return await this.createLineItemsBulk_(toUpdate, sharedContext) } @InjectTransactionManager("baseRepository_") - protected async addLineItemsBulk_( + protected async createLineItemsBulk_( data: CreateOrderLineItemDTO[], @MedusaContext() sharedContext: Context = {} ): Promise { @@ -479,7 +478,7 @@ export default class OrderModuleService< data: OrderTypes.UpdateOrderLineItemWithSelectorDTO[] ): Promise updateLineItems( - selector: Partial, + selector: Partial, data: OrderTypes.UpdateOrderLineItemDTO, sharedContext?: Context ): Promise @@ -494,7 +493,7 @@ export default class OrderModuleService< lineItemIdOrDataOrSelector: | string | OrderTypes.UpdateOrderLineItemWithSelectorDTO[] - | Partial, + | Partial, data?: | OrderTypes.UpdateOrderLineItemDTO | Partial, @@ -688,40 +687,6 @@ export default class OrderModuleService< return await this.orderItemService_.update(toUpdate, sharedContext) } - async removeLineItems( - itemIds: string[], - sharedContext?: Context - ): Promise - async removeLineItems(itemIds: string, sharedContext?: Context): Promise - async removeLineItems( - selector: Partial, - sharedContext?: Context - ): Promise - - @InjectTransactionManager("baseRepository_") - async removeLineItems( - itemIdsOrSelector: string | string[] | Partial, - @MedusaContext() sharedContext: Context = {} - ): Promise { - let toDelete: string[] - - if (isObject(itemIdsOrSelector)) { - const items = await this.listLineItems( - { ...itemIdsOrSelector } as Partial, - {}, - sharedContext - ) - - toDelete = items.map((item) => item.id) - } else { - toDelete = Array.isArray(itemIdsOrSelector) - ? itemIdsOrSelector - : [itemIdsOrSelector] - } - - await this.lineItemService_.delete(toDelete, sharedContext) - } - async createAddresses( data: OrderTypes.CreateOrderAddressDTO, sharedContext?: Context @@ -794,20 +759,20 @@ export default class OrderModuleService< return await this.addressService_.update(data, sharedContext) } - async addShippingMethods( + async createShippingMethods( data: OrderTypes.CreateOrderShippingMethodDTO ): Promise - async addShippingMethods( + async createShippingMethods( data: OrderTypes.CreateOrderShippingMethodDTO[] ): Promise - async addShippingMethods( + async createShippingMethods( orderId: string, methods: OrderTypes.CreateOrderShippingMethodDTO[], sharedContext?: Context ): Promise @InjectManager("baseRepository_") - async addShippingMethods( + async createShippingMethods( orderIdOrData: | string | OrderTypes.CreateOrderShippingMethodDTO[] @@ -819,7 +784,7 @@ export default class OrderModuleService< > { let methods: ShippingMethod[] if (isString(orderIdOrData)) { - methods = await this.addShippingMethods_( + methods = await this.createShippingMethods_( orderIdOrData, data!, sharedContext @@ -828,7 +793,7 @@ export default class OrderModuleService< const data = Array.isArray(orderIdOrData) ? orderIdOrData : [orderIdOrData] - methods = await this.addShippingMethodsBulk_( + methods = await this.createShippingMethodsBulk_( data as OrderTypes.CreateOrderShippingMethodDTO[], sharedContext ) @@ -840,7 +805,7 @@ export default class OrderModuleService< } @InjectTransactionManager("baseRepository_") - protected async addShippingMethods_( + protected async createShippingMethods_( orderId: string, data: CreateOrderShippingMethodDTO[], @MedusaContext() sharedContext: Context = {} @@ -859,11 +824,11 @@ export default class OrderModuleService< } }) - return await this.addShippingMethodsBulk_(methods, sharedContext) + return await this.createShippingMethodsBulk_(methods, sharedContext) } @InjectTransactionManager("baseRepository_") - protected async addShippingMethodsBulk_( + protected async createShippingMethodsBulk_( data: OrderTypes.CreateOrderShippingMethodDTO[], @MedusaContext() sharedContext: Context = {} ): Promise { @@ -873,60 +838,20 @@ export default class OrderModuleService< ) } - async removeShippingMethods( - methodIds: string[], - sharedContext?: Context - ): Promise - async removeShippingMethods( - methodIds: string, - sharedContext?: Context - ): Promise - async removeShippingMethods( - selector: Partial, - sharedContext?: Context - ): Promise - - @InjectTransactionManager("baseRepository_") - async removeShippingMethods( - methodIdsOrSelector: - | string - | string[] - | Partial, - @MedusaContext() sharedContext: Context = {} - ): Promise { - let toDelete: string[] - if (isObject(methodIdsOrSelector)) { - const methods = await this.listShippingMethods( - { - ...(methodIdsOrSelector as Partial), - }, - {}, - sharedContext - ) - - toDelete = methods.map((m) => m.id) - } else { - toDelete = Array.isArray(methodIdsOrSelector) - ? methodIdsOrSelector - : [methodIdsOrSelector] - } - await this.shippingMethodService_.delete(toDelete, sharedContext) - } - - async addLineItemAdjustments( + async createLineItemAdjustments( adjustments: OrderTypes.CreateOrderLineItemAdjustmentDTO[] ): Promise - async addLineItemAdjustments( + async createLineItemAdjustments( adjustment: OrderTypes.CreateOrderLineItemAdjustmentDTO ): Promise - async addLineItemAdjustments( + async createLineItemAdjustments( orderId: string, adjustments: OrderTypes.CreateOrderLineItemAdjustmentDTO[], sharedContext?: Context ): Promise @InjectTransactionManager("baseRepository_") - async addLineItemAdjustments( + async createLineItemAdjustments( orderIdOrData: | string | OrderTypes.CreateOrderLineItemAdjustmentDTO[] @@ -1026,46 +951,6 @@ export default class OrderModuleService< }) } - async removeLineItemAdjustments( - adjustmentIds: string[], - sharedContext?: Context - ): Promise - async removeLineItemAdjustments( - adjustmentId: string, - sharedContext?: Context - ): Promise - async removeLineItemAdjustments( - selector: Partial, - sharedContext?: Context - ): Promise - - async removeLineItemAdjustments( - adjustmentIdsOrSelector: - | string - | string[] - | Partial, - @MedusaContext() sharedContext: Context = {} - ): Promise { - let ids: string[] - if (isObject(adjustmentIdsOrSelector)) { - const adjustments = await this.listLineItemAdjustments( - { - ...adjustmentIdsOrSelector, - } as Partial, - { select: ["id"] }, - sharedContext - ) - - ids = adjustments.map((adj) => adj.id) - } else { - ids = Array.isArray(adjustmentIdsOrSelector) - ? adjustmentIdsOrSelector - : [adjustmentIdsOrSelector] - } - - await this.lineItemAdjustmentService_.delete(ids, sharedContext) - } - @InjectTransactionManager("baseRepository_") async setShippingMethodAdjustments( orderId: string, @@ -1122,20 +1007,20 @@ export default class OrderModuleService< }) } - async addShippingMethodAdjustments( + async createShippingMethodAdjustments( adjustments: OrderTypes.CreateOrderShippingMethodAdjustmentDTO[] ): Promise - async addShippingMethodAdjustments( + async createShippingMethodAdjustments( adjustment: OrderTypes.CreateOrderShippingMethodAdjustmentDTO ): Promise - async addShippingMethodAdjustments( + async createShippingMethodAdjustments( orderId: string, adjustments: OrderTypes.CreateOrderShippingMethodAdjustmentDTO[], sharedContext?: Context ): Promise @InjectTransactionManager("baseRepository_") - async addShippingMethodAdjustments( + async createShippingMethodAdjustments( orderIdOrData: | string | OrderTypes.CreateOrderShippingMethodAdjustmentDTO[] @@ -1196,53 +1081,13 @@ export default class OrderModuleService< }) } - async removeShippingMethodAdjustments( - adjustmentIds: string[], - sharedContext?: Context - ): Promise - async removeShippingMethodAdjustments( - adjustmentId: string, - sharedContext?: Context - ): Promise - async removeShippingMethodAdjustments( - selector: Partial, - sharedContext?: Context - ): Promise - - async removeShippingMethodAdjustments( - adjustmentIdsOrSelector: - | string - | string[] - | Partial, - @MedusaContext() sharedContext: Context = {} - ): Promise { - let ids: string[] - if (isObject(adjustmentIdsOrSelector)) { - const adjustments = await this.listShippingMethodAdjustments( - { - ...adjustmentIdsOrSelector, - } as Partial, - { select: ["id"] }, - sharedContext - ) - - ids = adjustments.map((adj) => adj.id) - } else { - ids = Array.isArray(adjustmentIdsOrSelector) - ? adjustmentIdsOrSelector - : [adjustmentIdsOrSelector] - } - - await this.shippingMethodAdjustmentService_.delete(ids, sharedContext) - } - - addLineItemTaxLines( + createLineItemTaxLines( taxLines: OrderTypes.CreateOrderLineItemTaxLineDTO[] ): Promise - addLineItemTaxLines( + createLineItemTaxLines( taxLine: OrderTypes.CreateOrderLineItemTaxLineDTO ): Promise - addLineItemTaxLines( + createLineItemTaxLines( orderId: string, taxLines: | OrderTypes.CreateOrderLineItemTaxLineDTO[] @@ -1251,7 +1096,7 @@ export default class OrderModuleService< ): Promise @InjectTransactionManager("baseRepository_") - async addLineItemTaxLines( + async createLineItemTaxLines( orderIdOrData: | string | OrderTypes.CreateOrderLineItemTaxLineDTO[] @@ -1346,53 +1191,13 @@ export default class OrderModuleService< }) } - removeLineItemTaxLines( - taxLineIds: string[], - sharedContext?: Context - ): Promise - removeLineItemTaxLines( - taxLineIds: string, - sharedContext?: Context - ): Promise - removeLineItemTaxLines( - selector: FilterableLineItemTaxLineProps, - sharedContext?: Context - ): Promise - - async removeLineItemTaxLines( - taxLineIdsOrSelector: - | string - | string[] - | OrderTypes.FilterableOrderShippingMethodTaxLineProps, - @MedusaContext() sharedContext: Context = {} - ): Promise { - let ids: string[] - if (isObject(taxLineIdsOrSelector)) { - const taxLines = await this.listLineItemTaxLines( - { - ...(taxLineIdsOrSelector as OrderTypes.FilterableOrderLineItemTaxLineProps), - }, - { select: ["id"] }, - sharedContext - ) - - ids = taxLines.map((taxLine) => taxLine.id) - } else { - ids = Array.isArray(taxLineIdsOrSelector) - ? taxLineIdsOrSelector - : [taxLineIdsOrSelector] - } - - await this.lineItemTaxLineService_.delete(ids, sharedContext) - } - - addShippingMethodTaxLines( + createShippingMethodTaxLines( taxLines: OrderTypes.CreateOrderShippingMethodTaxLineDTO[] ): Promise - addShippingMethodTaxLines( + createShippingMethodTaxLines( taxLine: OrderTypes.CreateOrderShippingMethodTaxLineDTO ): Promise - addShippingMethodTaxLines( + createShippingMethodTaxLines( orderId: string, taxLines: | OrderTypes.CreateOrderShippingMethodTaxLineDTO[] @@ -1401,7 +1206,7 @@ export default class OrderModuleService< ): Promise @InjectTransactionManager("baseRepository_") - async addShippingMethodTaxLines( + async createShippingMethodTaxLines( orderIdOrData: | string | OrderTypes.CreateOrderShippingMethodTaxLineDTO[] @@ -1496,46 +1301,6 @@ export default class OrderModuleService< }) } - removeShippingMethodTaxLines( - taxLineIds: string[], - sharedContext?: Context - ): Promise - removeShippingMethodTaxLines( - taxLineIds: string, - sharedContext?: Context - ): Promise - removeShippingMethodTaxLines( - selector: Partial, - sharedContext?: Context - ): Promise - - async removeShippingMethodTaxLines( - taxLineIdsOrSelector: - | string - | string[] - | OrderTypes.FilterableOrderShippingMethodTaxLineProps, - @MedusaContext() sharedContext: Context = {} - ): Promise { - let ids: string[] - if (isObject(taxLineIdsOrSelector)) { - const taxLines = await this.listShippingMethodTaxLines( - { - ...(taxLineIdsOrSelector as OrderTypes.FilterableOrderShippingMethodTaxLineProps), - }, - { select: ["id"] }, - sharedContext - ) - - ids = taxLines.map((taxLine) => taxLine.id) - } else { - ids = Array.isArray(taxLineIdsOrSelector) - ? taxLineIdsOrSelector - : [taxLineIdsOrSelector] - } - - await this.shippingMethodTaxLineService_.delete(ids, sharedContext) - } - async createOrderChange( data: OrderTypes.CreateOrderChangeDTO, sharedContext?: Context @@ -1877,8 +1642,23 @@ export default class OrderModuleService< return orderChanges } + async addOrderAction( + data: OrderTypes.CreateOrderChangeActionDTO, + sharedContext?: Context + ): Promise + async addOrderAction( + data: OrderTypes.CreateOrderChangeActionDTO[], + sharedContext?: Context + ): Promise @InjectTransactionManager("baseRepository_") - async addOrderAction(data: any, sharedContext?: Context): Promise { + async addOrderAction( + data: + | OrderTypes.CreateOrderChangeActionDTO + | OrderTypes.CreateOrderChangeActionDTO[], + sharedContext?: Context + ): Promise< + OrderTypes.OrderChangeActionDTO | OrderTypes.OrderChangeActionDTO[] + > { let dataArr = Array.isArray(data) ? data : [data] const orderChangeMap = {} @@ -1908,7 +1688,11 @@ export default class OrderModuleService< } } - return await this.orderChangeActionService_.create(dataArr, sharedContext) + const actions = (await this.orderChangeActionService_.create( + dataArr, + sharedContext + )) as OrderTypes.OrderChangeActionDTO[] + return Array.isArray(data) ? actions : actions[0] } private async applyOrderChanges_( diff --git a/packages/order/src/types/shipping-method.ts b/packages/order/src/types/shipping-method.ts index 3bc3570427..8069a78317 100644 --- a/packages/order/src/types/shipping-method.ts +++ b/packages/order/src/types/shipping-method.ts @@ -2,7 +2,7 @@ import { BigNumberInput } from "@medusajs/types" export interface CreateOrderShippingMethodDTO { name: string - shipping_method_id: string + shipping_option_id?: string order_id: string version?: number amount: BigNumberInput @@ -11,7 +11,7 @@ export interface CreateOrderShippingMethodDTO { export interface UpdateOrderShippingMethodDTO { id: string - shipping_method_id: string + shipping_option_id?: string name?: string amount?: BigNumberInput data?: Record diff --git a/packages/types/src/order/common.ts b/packages/types/src/order/common.ts index c35bcec029..321f901d9c 100644 --- a/packages/types/src/order/common.ts +++ b/packages/types/src/order/common.ts @@ -233,8 +233,7 @@ export interface OrderShippingMethodDTO { /** * The price of the shipping method */ - unit_price: number - + amount: BigNumberRawValue /** * Whether the shipping method price is tax inclusive or not */ @@ -416,6 +415,16 @@ export interface OrderLineItemDTO extends OrderLineItemTotalsDTO { */ raw_unit_price: BigNumberRawValue + /** + * The quantity of the order line item. + */ + quantity: number + + /** + * The raw quantity of the order line item. + */ + raw_quantity: BigNumberRawValue + /** * The associated tax lines. * @@ -443,6 +452,11 @@ export interface OrderLineItemDTO extends OrderLineItemTotalsDTO { * The date when the order line item was last updated. */ updated_at: Date + + /** + * Holds custom data in key-value pairs. + */ + metadata?: Record | null } export interface OrderItemDTO { @@ -738,11 +752,11 @@ export interface OrderChangeActionDTO { /** * The action of the order change action */ - action: Record + action: string /** - * The metadata of the order change action + * The details of the order change action */ - metadata?: Record | null + details: Record | null /** * The internal note of the order change action */ @@ -877,28 +891,42 @@ export interface FilterableOrderShippingMethodTaxLineProps export interface FilterableOrderChangeProps extends BaseFilterable { - id?: string | string[] - order_id?: string | string[] - status?: string | string[] - requested_by?: string | string[] - confirmed_by?: string | string[] - declined_by?: string | string[] - canceled_by?: string | string[] + id?: string | string[] | OperatorMap + order_id?: string | string[] | OperatorMap + status?: string | string[] | OperatorMap + requested_by?: string | string[] | OperatorMap + confirmed_by?: string | string[] | OperatorMap + declined_by?: string | string[] | OperatorMap + canceled_by?: string | string[] | OperatorMap + created_at?: OperatorMap + updated_at?: OperatorMap + deleted_at?: OperatorMap + canceled_at?: OperatorMap } export interface FilterableOrderChangeActionProps extends BaseFilterable { - id?: string | string[] - order_change_id?: string | string[] - reference?: string | string[] - reference_id?: string | string[] + id?: string | string[] | OperatorMap + order_change_id?: string | string[] | OperatorMap + reference?: string | string[] | OperatorMap + reference_id?: string | string[] | OperatorMap } export interface FilterableOrderTransactionProps extends BaseFilterable { - id?: string | string[] - order_id?: string | string[] - currency_code?: string | string[] - reference?: string | string[] - reference_id?: string | string[] + id?: string | string[] | OperatorMap + order_id?: string | string[] | OperatorMap + currency_code?: string | string[] | OperatorMap + reference?: string | string[] | OperatorMap + reference_id?: string | string[] | OperatorMap + + created_at?: OperatorMap +} + +export interface FilterableOrderItemProps + extends BaseFilterable { + id?: string | string[] | OperatorMap + order_id?: string | string[] | OperatorMap + version?: string | string[] | OperatorMap + item_id?: string | string[] | OperatorMap } diff --git a/packages/types/src/order/index.ts b/packages/types/src/order/index.ts index 0c73656566..dfae9af8a2 100644 --- a/packages/types/src/order/index.ts +++ b/packages/types/src/order/index.ts @@ -1,3 +1,4 @@ export * from "./common" export * from "./mutations" export * from "./service" +export * from "./workflows" diff --git a/packages/types/src/order/mutations.ts b/packages/types/src/order/mutations.ts index 6b29fdb786..6e1725789c 100644 --- a/packages/types/src/order/mutations.ts +++ b/packages/types/src/order/mutations.ts @@ -32,16 +32,18 @@ export interface CreateOrderDTO { sales_channel_id?: string status?: string email?: string - currency_code: string + currency_code?: string shipping_address_id?: string billing_address_id?: string shipping_address?: CreateOrderAddressDTO | UpdateOrderAddressDTO billing_address?: CreateOrderAddressDTO | UpdateOrderAddressDTO no_notification?: boolean items?: CreateOrderLineItemDTO[] - shipping_methods?: CreateOrderShippingMethodDTO[] + shipping_methods?: Omit[] transactions?: CreateOrderTransactionDTO[] metadata?: Record + + promo_codes?: string[] } export interface UpdateOrderDTO { @@ -162,6 +164,8 @@ export interface CreateOrderLineItemDTO { tax_lines?: CreateOrderTaxLineDTO[] adjustments?: CreateOrderAdjustmentDTO[] + + metadata?: Record } export interface CreateOrderLineItemForOrderDTO extends CreateOrderLineItemDTO { @@ -194,9 +198,9 @@ export interface UpdateOrderLineItemDTO export interface CreateOrderShippingMethodDTO { name: string - shipping_method_id: string order_id: string amount: BigNumberInput + shipping_option_id?: string data?: Record tax_lines?: CreateOrderTaxLineDTO[] adjustments?: CreateOrderAdjustmentDTO[] @@ -205,7 +209,7 @@ export interface CreateOrderShippingMethodDTO { export interface UpdateOrderShippingMethodDTO { id: string name?: string - shipping_method_id: string + shipping_option_id?: string amount?: BigNumberInput data?: Record tax_lines?: UpdateOrderTaxLineDTO[] | CreateOrderTaxLineDTO[] @@ -285,18 +289,14 @@ export interface CreateOrderChangeActionDTO { order_change_id: string reference: string reference_id: string - action: Record + action: string internal_note?: string - metadata?: Record + details?: Record } export interface UpdateOrderChangeActionDTO { id: string - reference?: string - reference_id?: string - action?: Record internal_note?: string - metadata?: Record } /** ORDER TRANSACTION START */ diff --git a/packages/types/src/order/service.ts b/packages/types/src/order/service.ts index 12b370c925..b3a8ba1e7c 100644 --- a/packages/types/src/order/service.ts +++ b/packages/types/src/order/service.ts @@ -1,4 +1,5 @@ import { FindConfig } from "../common" +import { RestoreReturn, SoftDeleteReturn } from "../dal" import { IModuleService } from "../modules-sdk" import { Context } from "../shared-context" import { @@ -11,6 +12,7 @@ import { FilterableOrderShippingMethodProps, FilterableOrderShippingMethodTaxLineProps, OrderAddressDTO, + OrderChangeActionDTO, OrderChangeDTO, OrderDTO, OrderItemDTO, @@ -26,6 +28,7 @@ import { ConfirmOrderChangeDTO, CreateOrderAddressDTO, CreateOrderAdjustmentDTO, + CreateOrderChangeActionDTO, CreateOrderChangeDTO, CreateOrderDTO, CreateOrderLineItemDTO, @@ -225,7 +228,7 @@ export interface IOrderModuleService extends IModuleService { * */ update( - selector: Partial, + selector: Partial, data: UpdateOrderDTO, sharedContext?: Context ): Promise @@ -260,21 +263,18 @@ export interface IOrderModuleService extends IModuleService { */ delete(orderId: string, sharedContext?: Context): Promise - /** - * This method retrieves a paginated list of {return type}(s) based on optional filters and configuration. - * - * @param {FilterableOrderAddressProps} filters - The filters to apply on the retrieved order address. - * @param {FindConfig} config - The configurations determining how the order address is retrieved. Its properties, such as `select` or `relations`, accept the - * attributes or relations associated with a order address. - * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. - * @returns {Promise} The list of {return type}(s). - * - * @example - * ```typescript - * await orderModuleService.listAddresses(); - * ``` - * - */ + softDelete( + storeIds: string[], + config?: SoftDeleteReturn, + sharedContext?: Context + ): Promise | void> + + restore( + storeIds: string[], + config?: RestoreReturn, + sharedContext?: Context + ): Promise | void> + listAddresses( filters?: FilterableOrderAddressProps, config?: FindConfig, @@ -467,75 +467,13 @@ export interface IOrderModuleService extends IModuleService { sharedContext?: Context ): Promise - /** - * This method Represents the completion of an asynchronous operation - * - * @param {CreateOrderLineItemForOrderDTO} data - The order line item for order to be created. - * @returns {Promise} Represents the completion of an asynchronous operation - * - * @example - * ```typescript - * await orderModuleService.addLineItems({ - * order_id: "123456", - * { - * product_id: "abc123", - * quantity: 2, - * unit_price: 1999, // Assuming type is an integer for price in cents - * } - * }); - * ``` - * - */ - addLineItems( + createLineItems( data: CreateOrderLineItemForOrderDTO ): Promise - - /** - * This method Represents the completion of an asynchronous operation - * - * @param {CreateOrderLineItemForOrderDTO[]} data - The order line item for order to be created. - * @returns {Promise} Represents the completion of an asynchronous operation - * - * @example - * ```typescript - * const lineItems: CreateOrderLineItemForOrderDTO[] = [{ - * order_id: "order_123", - * product_id: "prod_456", - * quantity: 2, - * unit_price: 1999 - * }]; - * - * const result = await orderModuleService.addLineItems(lineItems); - * ``` - * - */ - addLineItems( + createLineItems( data: CreateOrderLineItemForOrderDTO[] ): Promise - - /** - * This method Represents the completion of an asynchronous operation - * - * @param {string} orderId - The order's ID. - * @param {CreateOrderLineItemDTO[]} items - The order line item to be created. - * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. - * @returns {Promise} Represents the completion of an asynchronous operation - * - * @example - * ```typescript - * const result = await orderModuleService.addLineItems("orderIdValue", [ - * { - * title: "Item Title", - * requires_shipping: true, - * is_discountable: true, - * is_tax_inclusive: true, - * unit_price: 100, - * } - * ]); - * ``` - * - */ - addLineItems( + createLineItems( orderId: string, items: CreateOrderLineItemDTO[], sharedContext?: Context @@ -588,7 +526,7 @@ export interface IOrderModuleService extends IModuleService { * */ updateLineItems( - selector: Partial, + selector: Partial, data: Partial, sharedContext?: Context ): Promise @@ -618,51 +556,10 @@ export interface IOrderModuleService extends IModuleService { sharedContext?: Context ): Promise - /** - * This method Represents the completion of an asynchronous operation - * - * @param {string[]} itemIds - The list of {summary} - * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. - * @returns {Promise} Resolves when {summary} - * - * @example - * ```typescript - * await orderModuleService.removeLineItems(["itemId1", "itemId2"]); - * ``` - * - */ - removeLineItems(itemIds: string[], sharedContext?: Context): Promise - - /** - * This method Represents the completion of an asynchronous operation - * - * @param {string} itemIds - {summary} - * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. - * @returns {Promise} Resolves when {summary} - * - * @example - * ```typescript - * await orderModuleService.removeLineItems(itemId1); - * ``` - * - */ - removeLineItems(itemIds: string, sharedContext?: Context): Promise - - /** - * This method Represents the completion of an asynchronous operation - * - * @param {Partial} selector - Make all properties in T optional - * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. - * @returns {Promise} Resolves when {summary} - * - * @example - * ```typescript - * await orderModuleService.removeLineItems({ id: 'item-123' }); - * ``` - * - */ - removeLineItems( - selector: Partial, + deleteLineItems(itemIds: string[], sharedContext?: Context): Promise + deleteLineItems(itemIds: string, sharedContext?: Context): Promise + deleteLineItems( + selector: Partial, sharedContext?: Context ): Promise @@ -684,7 +581,7 @@ export interface IOrderModuleService extends IModuleService { * */ updateOrderItem( - selector: Partial, + selector: Partial, data: UpdateOrderItemDTO, sharedContext?: Context ): Promise @@ -768,138 +665,28 @@ export interface IOrderModuleService extends IModuleService { sharedContext?: Context ): Promise - /** - * This method Represents the completion of an asynchronous operation - * - * @param {CreateOrderShippingMethodDTO} data - The order shipping method to be created. - * @returns {Promise} Represents the completion of an asynchronous operation - * - * @example - * ```typescript - * const result = await orderModuleService.addShippingMethods({ - * name: "Standard Shipping", - * shipping_method_id: "1", - * order_id: "123", - * amount: 1000 - * }); - * ``` - * - */ - addShippingMethods( + createShippingMethods( data: CreateOrderShippingMethodDTO ): Promise - - /** - * This method Represents the completion of an asynchronous operation - * - * @param {CreateOrderShippingMethodDTO[]} data - The order shipping method to be created. - * @returns {Promise} Represents the completion of an asynchronous operation - * - * @example - * ```typescript - * const shippingMethods: CreateOrderShippingMethodDTO[] = [ - * { - * name: 'Standard Shipping', - * shipping_method_id: 'std_ship_001', - * order_id: 'order_12345', - * amount: 1000 - * }, - * { - * name: 'Express Shipping', - * shipping_method_id: 'exp_ship_002', - * order_id: 'order_12345', - * amount: 1000 - * } - * ]; - * - * const addedShippingMethods = await orderModuleService.addShippingMethods(shippingMethods); - * ``` - * - */ - addShippingMethods( + createShippingMethods( data: CreateOrderShippingMethodDTO[] ): Promise - - /** - * This method Represents the completion of an asynchronous operation - * - * @param {string} orderId - The order's ID. - * @param {CreateOrderShippingMethodDTO[]} methods - The order shipping method to be created. - * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. - * @returns {Promise} Represents the completion of an asynchronous operation - * - * @example - * ```typescript - * const createOrderShippingMethodDTOs: CreateOrderShippingMethodDTO[] = [{ - * name: "Standard Shipping", - * shipping_method_id: "123", - * order_id: "abc", - * amount: 1000 - * }]; - * - * await orderModuleService.addShippingMethods("orderId123", createOrderShippingMethodDTOs); - * ``` - * - */ - addShippingMethods( + createShippingMethods( orderId: string, methods: CreateOrderShippingMethodDTO[], sharedContext?: Context ): Promise - /** - * This method Represents the completion of an asynchronous operation - * - * @param {string[]} methodIds - The list of {summary} - * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. - * @returns {Promise} Resolves when {summary} - * - * @example - * ```typescript - * await orderModuleService.removeShippingMethods(['method1', 'method2']); - * ``` - * - */ - removeShippingMethods( + deleteShippingMethods( methodIds: string[], sharedContext?: Context ): Promise - - /** - * This method Represents the completion of an asynchronous operation - * - * @param {string} methodIds - {summary} - * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. - * @returns {Promise} Resolves when {summary} - * - * @example - * ```typescript - * await orderModuleService.removeShippingMethods("methodId12345"); - * ``` - * - */ - removeShippingMethods( + deleteShippingMethods( methodIds: string, sharedContext?: Context ): Promise - - /** - * This method Represents the completion of an asynchronous operation - * - * @param {Partial} selector - Make all properties in T optional - * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. - * @returns {Promise} Resolves when {summary} - * - * @example - * ```typescript - * await orderModuleService.removeShippingMethods({ - * id: "shipping-method-123", - * }); - * ``` - * - */ - removeShippingMethods( - selector: Partial, + deleteShippingMethods( + selector: Partial, sharedContext?: Context ): Promise @@ -924,62 +711,13 @@ export interface IOrderModuleService extends IModuleService { sharedContext?: Context ): Promise - /** - * This method Represents the completion of an asynchronous operation - * - * @param {CreateOrderAdjustmentDTO[]} data - The order adjustment to be created. - * @returns {Promise} Represents the completion of an asynchronous operation - * - * @example - * ```typescript - * await orderModuleService.addLineItemAdjustments([ - * { - * amount: 1000, - * } - * ]); - * ``` - * - */ - addLineItemAdjustments( + createLineItemAdjustments( data: CreateOrderAdjustmentDTO[] ): Promise - - /** - * This method Represents the completion of an asynchronous operation - * - * @param {CreateOrderAdjustmentDTO} data - The order adjustment to be created. - * @returns {Promise} Represents the completion of an asynchronous operation - * - * @example - * ```typescript - * await orderModuleService.addLineItemAdjustments({ - * amount: 1000, - * }); - * ``` - * - */ - addLineItemAdjustments( + createLineItemAdjustments( data: CreateOrderAdjustmentDTO ): Promise - - /** - * This method Represents the completion of an asynchronous operation - * - * @param {string} orderId - The order's ID. - * @param {CreateOrderAdjustmentDTO[]} data - The order adjustment to be created. - * @returns {Promise} Represents the completion of an asynchronous operation - * - * @example - * ```typescript - * await orderModuleService.addLineItemAdjustments("12345", [ - * { - * amount: 1000, - * } - * ]); - * ``` - * - */ - addLineItemAdjustments( + createLineItemAdjustments( orderId: string, data: CreateOrderAdjustmentDTO[] ): Promise @@ -1009,57 +747,15 @@ export interface IOrderModuleService extends IModuleService { sharedContext?: Context ): Promise - /** - * This method Represents the completion of an asynchronous operation - * - * @param {string[]} adjustmentIds - The list of {summary} - * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. - * @returns {Promise} Resolves when {summary} - * - * @example - * ```typescript - * await orderModuleService.removeLineItemAdjustments(["adjustmentId1", "adjustmentId2"]); - * - */ - removeLineItemAdjustments( + deleteLineItemAdjustments( adjustmentIds: string[], sharedContext?: Context ): Promise - - /** - * This method Represents the completion of an asynchronous operation - * - * @param {string} adjustmentIds - {summary} - * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. - * @returns {Promise} Resolves when {summary} - * - * @example - * ```typescript - * await orderModuleService.removeLineItemAdjustments("adjustmentId123"); - * ``` - * - */ - removeLineItemAdjustments( + deleteLineItemAdjustments( adjustmentIds: string, sharedContext?: Context ): Promise - - /** - * This method Represents the completion of an asynchronous operation - * - * @param {Partial} selector - Make all properties in T optional - * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. - * @returns {Promise} Resolves when {summary} - * - * @example - * ```typescript - * await orderModuleService.removeLineItemAdjustments({ - * item_id: "123" - * }); - * ``` - * - */ - removeLineItemAdjustments( + deleteLineItemAdjustments( selector: Partial, sharedContext?: Context ): Promise @@ -1087,74 +783,13 @@ export interface IOrderModuleService extends IModuleService { sharedContext?: Context ): Promise - /** - * This method Represents the completion of an asynchronous operation - * - * @param {CreateOrderShippingMethodAdjustmentDTO[]} data - The order shipping method adjustment to be created. - * @returns {Promise} Represents the completion of an asynchronous operation - * - * @example - * ```typescript - * const adjustmentsData: CreateOrderShippingMethodAdjustmentDTO[] = [{ - * shipping_method_id: '123', - * code: "ADJUSTMENT_CODE", - * amount: 1000, - * description: 'Discount', - * promotion_id: 'promo-456' - * }]; - * - * const result = await orderModuleService.addShippingMethodAdjustments(adjustmentsData); - * ``` - * - */ - addShippingMethodAdjustments( + createShippingMethodAdjustments( data: CreateOrderShippingMethodAdjustmentDTO[] ): Promise - - /** - * This method Represents the completion of an asynchronous operation - * - * @param {CreateOrderShippingMethodAdjustmentDTO} data - The order shipping method adjustment to be created. - * @returns {Promise} Represents the completion of an asynchronous operation - * - * @example - * ```typescript - * // Example usage of addShippingMethodAdjustments method - * const adjustmentData: CreateOrderShippingMethodAdjustmentDTO = { - * shipping_method_id: "shipping_method_123", - * code: "ADJUSTMENT_CODE", - * amount: 1000 - * }; - * - * const result = await orderModuleService.addShippingMethodAdjustments(adjustmentData); - * ``` - * - */ - addShippingMethodAdjustments( + createShippingMethodAdjustments( data: CreateOrderShippingMethodAdjustmentDTO ): Promise - - /** - * This method Represents the completion of an asynchronous operation - * - * @param {string} orderId - The order's ID. - * @param {CreateOrderShippingMethodAdjustmentDTO[]} data - The order shipping method adjustment to be created. - * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. - * @returns {Promise} Represents the completion of an asynchronous operation - * - * @example - * ```typescript - * await orderModuleService.addShippingMethodAdjustments("orderId123", [ - * { - * shipping_method_id: "shippingMethodId456", - * code: "CODE123", - * amount: 1000 - * } - * ]); - * ``` - * - */ - addShippingMethodAdjustments( + createShippingMethodAdjustments( orderId: string, data: CreateOrderShippingMethodAdjustmentDTO[], sharedContext?: Context @@ -1189,56 +824,15 @@ export interface IOrderModuleService extends IModuleService { sharedContext?: Context ): Promise - /** - * This method Represents the completion of an asynchronous operation - * - * @param {string[]} adjustmentIds - The list of {summary} - * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. - * @returns {Promise} Resolves when {summary} - * - * @example - * ```typescript - * await orderModuleService.removeShippingMethodAdjustments(["adjustmentId1", "adjustmentId2"]); - * ``` - * - */ - removeShippingMethodAdjustments( + deleteShippingMethodAdjustments( adjustmentIds: string[], sharedContext?: Context ): Promise - - /** - * This method Represents the completion of an asynchronous operation - * - * @param {string} adjustmentId - The adjustment's ID. - * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. - * @returns {Promise} Resolves when {summary} - * - * @example - * ```typescript - * await orderModuleService.removeShippingMethodAdjustments("adjustmentId123"); - * ``` - * - */ - removeShippingMethodAdjustments( + deleteShippingMethodAdjustments( adjustmentId: string, sharedContext?: Context ): Promise - - /** - * This method Represents the completion of an asynchronous operation - * - * @param {Partial} selector - Make all properties in T optional - * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. - * @returns {Promise} Resolves when {summary} - * - * @example - * ```typescript - * await orderModuleService.removeShippingMethodAdjustments({ id: "adjustment123" }); - * ``` - * - */ - removeShippingMethodAdjustments( + deleteShippingMethodAdjustments( selector: Partial, sharedContext?: Context ): Promise @@ -1264,69 +858,13 @@ export interface IOrderModuleService extends IModuleService { sharedContext?: Context ): Promise - /** - * This method Represents the completion of an asynchronous operation - * - * @param {CreateOrderLineItemTaxLineDTO[]} taxLines - The order line item tax line to be created. - * @returns {Promise} Represents the completion of an asynchronous operation - * - * @example - * ```typescript - * const taxLines: CreateOrderLineItemTaxLineDTO[] = [{ - * code: 'VAT', - * rate: 20, - * tax_rate_id: 'tax_rate_id_value' - * }]; - * - * const result = await orderModuleService.addLineItemTaxLines(taxLines); - * ``` - * - */ - addLineItemTaxLines( + createLineItemTaxLines( taxLines: CreateOrderLineItemTaxLineDTO[] ): Promise - - /** - * This method Represents the completion of an asynchronous operation - * - * @param {CreateOrderLineItemTaxLineDTO} taxLine - The order line item tax line to be created. - * @returns {Promise} Represents the completion of an asynchronous operation - * - * @example - * ```typescript - * const taxLine: CreateOrderLineItemTaxLineDTO = { - * code: 'TAX100', - * rate: 10 - * }; - * - * const response = await orderModuleService.addLineItemTaxLines(taxLine); - * ``` - * - */ - addLineItemTaxLines( + createLineItemTaxLines( taxLine: CreateOrderLineItemTaxLineDTO ): Promise - - /** - * This method Represents the completion of an asynchronous operation - * - * @param {string} orderId - The order's ID. - * @param {CreateOrderLineItemTaxLineDTO | CreateOrderLineItemTaxLineDTO[]} taxLines - The order line item tax line d t o | create order line item tax line to be created. - * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. - * @returns {Promise} Represents the completion of an asynchronous operation - * - * @example - * ```typescript - * await orderModuleService.addLineItemTaxLines("orderId123", [ - * { - * code: "TAX1001", - * rate: 70, - * } - * ]); - * ``` - * - */ - addLineItemTaxLines( + createLineItemTaxLines( orderId: string, taxLines: CreateOrderLineItemTaxLineDTO[] | CreateOrderLineItemTaxLineDTO, sharedContext?: Context @@ -1362,59 +900,15 @@ export interface IOrderModuleService extends IModuleService { sharedContext?: Context ): Promise - /** - * This method Represents the completion of an asynchronous operation - * - * @param {string[]} taxLineIds - The list of {summary} - * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. - * @returns {Promise} Resolves when {summary} - * - * @example - * ```typescript - * // Example usage of removeLineItemTaxLines method from IOrderModuleService - * await orderModuleService.removeLineItemTaxLines(["taxLineId1", "taxLineId2"]); - * ``` - * - */ - removeLineItemTaxLines( + deleteLineItemTaxLines( taxLineIds: string[], sharedContext?: Context ): Promise - - /** - * This method Represents the completion of an asynchronous operation - * - * @param {string} taxLineIds - {summary} - * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. - * @returns {Promise} Resolves when {summary} - * - * @example - * ```typescript - * await orderModuleService.removeLineItemTaxLines("yourTaxLineId"); - * ``` - * - */ - removeLineItemTaxLines( + deleteLineItemTaxLines( taxLineIds: string, sharedContext?: Context ): Promise - - /** - * This method Represents the completion of an asynchronous operation - * - * @param {FilterableOrderLineItemTaxLineProps} selector - The filters to apply on the retrieved order line item tax line. - * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. - * @returns {Promise} Resolves when {summary} - * - * @example - * ```typescript - * await orderModuleService.removeLineItemTaxLines({ - * id: "yourTaxLineId" - * }); - * ``` - * - */ - removeLineItemTaxLines( + deleteLineItemTaxLines( selector: FilterableOrderLineItemTaxLineProps, sharedContext?: Context ): Promise @@ -1444,73 +938,13 @@ export interface IOrderModuleService extends IModuleService { sharedContext?: Context ): Promise - /** - * This method Represents the completion of an asynchronous operation - * - * @param {CreateOrderShippingMethodTaxLineDTO[]} taxLines - The order shipping method tax line to be created. - * @returns {Promise} Represents the completion of an asynchronous operation - * - * @example - * ```typescript - * const result = await orderModuleService.addShippingMethodTaxLines([ - * { - * code: "VAT20", - * rate: 20, - * } - * ]); - * ``` - * - */ - addShippingMethodTaxLines( + createShippingMethodTaxLines( taxLines: CreateOrderShippingMethodTaxLineDTO[] ): Promise - - /** - * This method Represents the completion of an asynchronous operation - * - * @param {CreateOrderShippingMethodTaxLineDTO} taxLine - The order shipping method tax line to be created. - * @returns {Promise} Represents the completion of an asynchronous operation - * - * @example - * ```typescript - * const taxLine: CreateOrderShippingMethodTaxLineDTO = { - * code: "VAT20", - * rate: 20, - * }; - * - * orderModuleService.addShippingMethodTaxLines(taxLine).then((result) => { - * console.log(result); - * }); - * ``` - * - */ - addShippingMethodTaxLines( + createShippingMethodTaxLines( taxLine: CreateOrderShippingMethodTaxLineDTO ): Promise - - /** - * This method Represents the completion of an asynchronous operation - * - * @param {string} orderId - The order's ID. - * @param {CreateOrderShippingMethodTaxLineDTO | CreateOrderShippingMethodTaxLineDTO[]} taxLines - The order shipping method tax line d t o | create order shipping method tax line to be created. - * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. - * @returns {Promise} Represents the completion of an asynchronous operation - * - * @example - * ```typescript - * const orderId = '123'; - * const taxLines = [ - * { - * code: "VAT20", - * rate: 20, - * } - * ]; - * - * const addedTaxLines = await orderModuleService.addShippingMethodTaxLines(orderId, taxLines); - * ``` - * - */ - addShippingMethodTaxLines( + createShippingMethodTaxLines( orderId: string, taxLines: | CreateOrderShippingMethodTaxLineDTO[] @@ -1549,58 +983,15 @@ export interface IOrderModuleService extends IModuleService { sharedContext?: Context ): Promise - /** - * This method Represents the completion of an asynchronous operation - * - * @param {string[]} taxLineIds - The list of {summary} - * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. - * @returns {Promise} Resolves when {summary} - * - * @example - * ```typescript - * await orderModuleService.removeShippingMethodTaxLines(["taxLine1", "taxLine2"]); - * ``` - * - */ - removeShippingMethodTaxLines( + deleteShippingMethodTaxLines( taxLineIds: string[], sharedContext?: Context ): Promise - - /** - * This method Represents the completion of an asynchronous operation - * - * @param {string} taxLineIds - {summary} - * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. - * @returns {Promise} Resolves when {summary} - * - * @example - * ```typescript - * await orderModuleService.removeShippingMethodTaxLines("12345"); - * ``` - * - */ - removeShippingMethodTaxLines( + deleteShippingMethodTaxLines( taxLineIds: string, sharedContext?: Context ): Promise - - /** - * This method Represents the completion of an asynchronous operation - * - * @param {FilterableOrderShippingMethodTaxLineProps} selector - The filters to apply on the retrieved order shipping method tax line. - * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. - * @returns {Promise} Resolves when {summary} - * - * @example - * ```typescript - * await orderModuleService.removeShippingMethodTaxLines({ - * id: "shippingMethodTaxLineId" - * }); - * ``` - * - */ - removeShippingMethodTaxLines( + deleteShippingMethodTaxLines( selector: FilterableOrderShippingMethodTaxLineProps, sharedContext?: Context ): Promise @@ -1885,25 +1276,99 @@ export interface IOrderModuleService extends IModuleService { */ applyPendingOrderActions(orderId: string | string[], sharedContext?: Context) - /** - * This method {summary} - * - * @param {any} data - {summary} - * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. - * @returns {(data: any, sharedContext?: Context) => any} {summary} - * - * @example - * ```typescript - * await orderModuleService.addOrderAction({ - * action: 'create', - * orderId: '12345', - * details: { - * productId: 'abc123', - * quantity: 2 - * } - * }); - * ``` - * - */ - addOrderAction(data: any, sharedContext?: Context) + addOrderAction( + data: CreateOrderChangeActionDTO, + sharedContext?: Context + ): Promise + addOrderAction( + data: CreateOrderChangeActionDTO[], + sharedContext?: Context + ): Promise + + softDeleteAddresses( + ids: string[], + config?: SoftDeleteReturn, + sharedContext?: Context + ): Promise | void> + restoreAddresses( + ids: string[], + config?: RestoreReturn, + sharedContext?: Context + ): Promise | void> + + softDeleteLineItems( + ids: string[], + config?: SoftDeleteReturn, + sharedContext?: Context + ): Promise | void> + restoreLineItems( + ids: string[], + config?: RestoreReturn, + sharedContext?: Context + ): Promise | void> + + softDeleteShippingMethods( + ids: string[], + config?: SoftDeleteReturn, + sharedContext?: Context + ): Promise | void> + restoreShippingMethods( + ids: string[], + config?: RestoreReturn, + sharedContext?: Context + ): Promise | void> + + softDeleteLineItemAdjustments< + TReturnableLinkableKeys extends string = string + >( + ids: string[], + config?: SoftDeleteReturn, + sharedContext?: Context + ): Promise | void> + restoreLineItemAdjustments( + ids: string[], + config?: RestoreReturn, + sharedContext?: Context + ): Promise | void> + + softDeleteShippingMethodAdjustments< + TReturnableLinkableKeys extends string = string + >( + ids: string[], + config?: SoftDeleteReturn, + sharedContext?: Context + ): Promise | void> + restoreShippingMethodAdjustments< + TReturnableLinkableKeys extends string = string + >( + ids: string[], + config?: RestoreReturn, + sharedContext?: Context + ): Promise | void> + + softDeleteLineItemTaxLines( + ids: string[], + config?: SoftDeleteReturn, + sharedContext?: Context + ): Promise | void> + restoreLineItemTaxLines( + ids: string[], + config?: RestoreReturn, + sharedContext?: Context + ): Promise | void> + + softDeleteShippingMethodTaxLines< + TReturnableLinkableKeys extends string = string + >( + ids: string[], + config?: SoftDeleteReturn, + sharedContext?: Context + ): Promise | void> + restoreShippingMethodTaxLines< + TReturnableLinkableKeys extends string = string + >( + ids: string[], + config?: RestoreReturn, + sharedContext?: Context + ): Promise | void> } diff --git a/packages/types/src/order/workflows.ts b/packages/types/src/order/workflows.ts new file mode 100644 index 0000000000..5532de4068 --- /dev/null +++ b/packages/types/src/order/workflows.ts @@ -0,0 +1,8 @@ +import { CustomerDTO } from "../customer" +import { RegionDTO } from "../region" +import { OrderDTO } from "./common" + +export interface OrderWorkflowDTO extends OrderDTO { + customer?: CustomerDTO + region?: RegionDTO +} diff --git a/packages/types/src/tax/common.ts b/packages/types/src/tax/common.ts index c192831810..6a054289e4 100644 --- a/packages/types/src/tax/common.ts +++ b/packages/types/src/tax/common.ts @@ -1,5 +1,6 @@ import { BaseFilterable } from "../dal" import { OperatorMap } from "../dal/utils" +import { BigNumberInput } from "../totals" /** * The tax rate details. @@ -386,12 +387,12 @@ export interface TaxableItemDTO { /** * The quantity of the taxable item. */ - quantity?: number + quantity?: BigNumberInput /** * The unit price of the taxable item. */ - unit_price?: number + unit_price?: BigNumberInput /** * The ISO 3 character currency code of the taxable item. @@ -416,7 +417,7 @@ export interface TaxableShippingDTO { /** * The unit price of the taxable shipping. */ - unit_price?: number + unit_price?: BigNumberInput /** * The ISO 3 character currency code of the taxable shipping. diff --git a/packages/utils/src/link/links.ts b/packages/utils/src/link/links.ts index 6fcad2867c..a68fd4e99f 100644 --- a/packages/utils/src/link/links.ts +++ b/packages/utils/src/link/links.ts @@ -50,15 +50,21 @@ export const LINKS = { Modules.STOCK_LOCATION, "location_id" ), - PublishableApiKeySalesChannel: composeLinkName( - Modules.API_KEY, - "api_key_id", + OrderPromotion: composeLinkName( + Modules.ORDER, + "order_id", + Modules.PROMOTION, + "promotion_id" + ), + OrderSalesChannel: composeLinkName( + Modules.ORDER, + "order_id", Modules.SALES_CHANNEL, "sales_channel_id" ), - ProductSalesChannel: composeLinkName( - Modules.PRODUCT, - "product_id", + PublishableApiKeySalesChannel: composeLinkName( + Modules.API_KEY, + "api_key_id", Modules.SALES_CHANNEL, "sales_channel_id" ), @@ -70,9 +76,9 @@ export const LINKS = { "shippingProfileService", "profile_id" ), - OrderSalesChannel: composeLinkName( - "orderService", - "order_id", + ProductSalesChannel: composeLinkName( + Modules.PRODUCT, + "product_id", Modules.SALES_CHANNEL, "sales_channel_id" ), diff --git a/packages/utils/src/totals/math.ts b/packages/utils/src/totals/math.ts index 5c2bfc27ba..77a434fdd5 100644 --- a/packages/utils/src/totals/math.ts +++ b/packages/utils/src/totals/math.ts @@ -6,6 +6,10 @@ import { BigNumber } from "./big-number" type BNInput = BigNumberInput | BigNumber export class MathBN { static convert(num: BNInput): BigNumberJS { + if (num == null) { + return new BigNumberJS(0) + } + if (num instanceof BigNumber) { return num.bigNumber! } else if (num instanceof BigNumberJS) { diff --git a/packages/workflows-sdk/src/helper/__tests__/compose.ts b/packages/workflows-sdk/src/helper/__tests__/compose.ts index cddc739d40..4aa510e8fa 100644 --- a/packages/workflows-sdk/src/helper/__tests__/compose.ts +++ b/packages/workflows-sdk/src/helper/__tests__/compose.ts @@ -3,6 +3,7 @@ import { createStep, createWorkflow, hook, + MedusaWorkflow, parallelize, StepResponse, transform, @@ -10,11 +11,16 @@ import { jest.setTimeout(30000) +const afterEach_ = () => { + jest.clearAllMocks() + MedusaWorkflow.workflows = {} +} + describe("Workflow composer", function () { + afterEach(afterEach_) + describe("Using steps returning plain values", function () { - afterEach(async () => { - jest.clearAllMocks() - }) + afterEach(afterEach_) it("should compose a new workflow composed retryable steps", async () => { const maxRetries = 1 @@ -657,83 +663,6 @@ describe("Workflow composer", function () { }) }) - it("should overwrite existing workflows if the same name is used", async () => { - const mockStep1Fn = jest.fn().mockImplementation((input, context) => { - return { inputs: [input], obj: "return from 1" } - }) - const mockStep2Fn = jest.fn().mockImplementation((...inputs) => { - const context = inputs.pop() - return { - inputs, - obj: "return from 2", - } - }) - const mockStep3Fn = jest.fn().mockImplementation((...inputs) => { - const context = inputs.pop() - return { - inputs, - obj: "return from 3", - } - }) - - const step1 = createStep("step1", mockStep1Fn) - const step2 = createStep("step2", mockStep2Fn) - const step3 = createStep("step3", mockStep3Fn) - - createWorkflow("workflow1", function (input) { - const returnStep1 = step1(input) - const ret2 = step2(returnStep1) - return step3({ one: returnStep1, two: ret2 }) - }) - - const overriddenWorkflow = createWorkflow("workflow1", function (input) { - const ret2 = step2(input) - const returnStep1 = step1(ret2) - return step3({ one: returnStep1, two: ret2 }) - }) - - const workflowInput = { test: "payload1" } - const { result: workflowResult } = await overriddenWorkflow().run({ - input: workflowInput, - }) - - expect(mockStep1Fn).toHaveBeenCalledTimes(1) - expect(mockStep1Fn.mock.calls[0]).toHaveLength(2) - expect(mockStep1Fn.mock.calls[0][0]).toEqual({ - inputs: [workflowInput], - obj: "return from 2", - }) - - expect(mockStep2Fn).toHaveBeenCalledTimes(1) - expect(mockStep2Fn.mock.calls[0]).toHaveLength(2) - expect(mockStep2Fn.mock.calls[0][0]).toEqual(workflowInput) - - expect(mockStep3Fn).toHaveBeenCalledTimes(1) - expect(mockStep3Fn.mock.calls[0]).toHaveLength(2) - expect(mockStep3Fn.mock.calls[0][0]).toEqual({ - one: { - inputs: [{ inputs: [{ test: "payload1" }], obj: "return from 2" }], - obj: "return from 1", - }, - two: { inputs: [{ test: "payload1" }], obj: "return from 2" }, - }) - - expect(workflowResult).toEqual({ - inputs: [ - { - one: { - inputs: [ - { inputs: [{ test: "payload1" }], obj: "return from 2" }, - ], - obj: "return from 1", - }, - two: { inputs: [{ test: "payload1" }], obj: "return from 2" }, - }, - ], - obj: "return from 3", - }) - }) - it("should transform the values before forward them to the next step", async () => { const mockStep1Fn = jest.fn().mockImplementation((obj, context) => { const ret = { @@ -958,9 +887,7 @@ describe("Workflow composer", function () { }) describe("Using steps returning StepResponse", function () { - afterEach(async () => { - jest.clearAllMocks() - }) + afterEach(afterEach_) it("should compose a new workflow composed of retryable steps", async () => { const maxRetries = 1 @@ -1636,83 +1563,6 @@ describe("Workflow composer", function () { }) }) - it("should overwrite existing workflows if the same name is used", async () => { - const mockStep1Fn = jest.fn().mockImplementation((input, context) => { - return new StepResponse({ inputs: [input], obj: "return from 1" }) - }) - const mockStep2Fn = jest.fn().mockImplementation((...inputs) => { - const context = inputs.pop() - return new StepResponse({ - inputs, - obj: "return from 2", - }) - }) - const mockStep3Fn = jest.fn().mockImplementation((...inputs) => { - const context = inputs.pop() - return new StepResponse({ - inputs, - obj: "return from 3", - }) - }) - - const step1 = createStep("step1", mockStep1Fn) - const step2 = createStep("step2", mockStep2Fn) - const step3 = createStep("step3", mockStep3Fn) - - createWorkflow("workflow1", function (input) { - const returnStep1 = step1(input) - const ret2 = step2(returnStep1) - return step3({ one: returnStep1, two: ret2 }) - }) - - const overriddenWorkflow = createWorkflow("workflow1", function (input) { - const ret2 = step2(input) - const returnStep1 = step1(ret2) - return step3({ one: returnStep1, two: ret2 }) - }) - - const workflowInput = { test: "payload1" } - const { result: workflowResult } = await overriddenWorkflow().run({ - input: workflowInput, - }) - - expect(mockStep1Fn).toHaveBeenCalledTimes(1) - expect(mockStep1Fn.mock.calls[0]).toHaveLength(2) - expect(mockStep1Fn.mock.calls[0][0]).toEqual({ - inputs: [workflowInput], - obj: "return from 2", - }) - - expect(mockStep2Fn).toHaveBeenCalledTimes(1) - expect(mockStep2Fn.mock.calls[0]).toHaveLength(2) - expect(mockStep2Fn.mock.calls[0][0]).toEqual(workflowInput) - - expect(mockStep3Fn).toHaveBeenCalledTimes(1) - expect(mockStep3Fn.mock.calls[0]).toHaveLength(2) - expect(mockStep3Fn.mock.calls[0][0]).toEqual({ - one: { - inputs: [{ inputs: [{ test: "payload1" }], obj: "return from 2" }], - obj: "return from 1", - }, - two: { inputs: [{ test: "payload1" }], obj: "return from 2" }, - }) - - expect(workflowResult).toEqual({ - inputs: [ - { - one: { - inputs: [ - { inputs: [{ test: "payload1" }], obj: "return from 2" }, - ], - obj: "return from 1", - }, - two: { inputs: [{ test: "payload1" }], obj: "return from 2" }, - }, - ], - obj: "return from 3", - }) - }) - it("should transform the values before forward them to the next step", async () => { const mockStep1Fn = jest.fn().mockImplementation((obj, context) => { const ret = new StepResponse({ diff --git a/packages/workflows-sdk/src/helper/__tests__/workflow-export.spec.ts b/packages/workflows-sdk/src/helper/__tests__/workflow-export.spec.ts index 2cc329e125..aa62f7a79c 100644 --- a/packages/workflows-sdk/src/helper/__tests__/workflow-export.spec.ts +++ b/packages/workflows-sdk/src/helper/__tests__/workflow-export.spec.ts @@ -1,4 +1,5 @@ import { createMedusaContainer } from "@medusajs/utils" +import { MedusaWorkflow } from "../../medusa-workflow" import { exportWorkflow } from "../workflow-export" jest.mock("@medusajs/orchestration", () => { @@ -63,6 +64,10 @@ jest.mock("@medusajs/orchestration", () => { }) describe("Export Workflow", function () { + afterEach(() => { + MedusaWorkflow.workflows = {} + }) + it("should prepare the input data before initializing the transaction", async function () { let transformedInput const prepare = jest.fn().mockImplementation(async (data) => { @@ -98,6 +103,10 @@ describe("Export Workflow", function () { }) describe("Using the exported workflow run", function () { + afterEach(() => { + MedusaWorkflow.workflows = {} + }) + it("should prepare the input data before initializing the transaction", async function () { let transformedInput const prepare = jest.fn().mockImplementation(async (data) => { diff --git a/packages/workflows-sdk/src/medusa-workflow.ts b/packages/workflows-sdk/src/medusa-workflow.ts index 0a48ef2d72..baea7536c7 100644 --- a/packages/workflows-sdk/src/medusa-workflow.ts +++ b/packages/workflows-sdk/src/medusa-workflow.ts @@ -9,12 +9,16 @@ export class MedusaWorkflow { container?: LoadedModule[] | MedusaContainer ) => Omit< LocalWorkflow, - "run" | "registerStepSuccess" | "registerStepFailure" + "run" | "registerStepSuccess" | "registerStepFailure" | "cancel" > & ExportedWorkflow > = {} static registerWorkflow(workflowId, exportedWorkflow) { + if (workflowId in MedusaWorkflow.workflows) { + throw new Error(`Workflow with id ${workflowId} already registered.`) + } + MedusaWorkflow.workflows[workflowId] = exportedWorkflow } diff --git a/packages/workflows-sdk/src/utils/composer/create-workflow.ts b/packages/workflows-sdk/src/utils/composer/create-workflow.ts index c9f6b66964..dd4806e3bf 100644 --- a/packages/workflows-sdk/src/utils/composer/create-workflow.ts +++ b/packages/workflows-sdk/src/utils/composer/create-workflow.ts @@ -72,7 +72,7 @@ export type ReturnWorkflow< container?: LoadedModule[] | MedusaContainer ): Omit< LocalWorkflow, - "run" | "registerStepSuccess" | "registerStepFailure" + "run" | "registerStepSuccess" | "registerStepFailure" | "cancel" > & ExportedWorkflow } & THooks & {