From aba194de9aa395c9be21ea2fbf4d48bd6bdb1596 Mon Sep 17 00:00:00 2001 From: "Carlos R. L. Rodrigues" <37986729+carlos-r-l-rodrigues@users.noreply.github.com> Date: Sun, 5 May 2024 10:35:09 -0300 Subject: [PATCH] chore: complete cart part 2 (#7227) --- .../cart/store/cart.workflows.spec.ts | 13 ++- .../__tests__/cart/store/carts.spec.ts | 2 +- .../cart/steps/confirm-inventory.ts | 5 + .../src/definition/cart/utils/fields.ts | 53 +++++++++ .../utils/prepare-confirm-inventory-input.ts | 15 ++- .../definition/cart/workflows/add-to-cart.ts | 110 +++--------------- .../cart/workflows/complete-cart.ts | 44 ++++++- .../workflows/confirm-variant-inventory.ts | 96 +++++++++++++++ .../definition/cart/workflows/create-carts.ts | 106 ++--------------- .../create-payment-collection-for-cart.ts | 12 +- .../workflows/update-line-item-in-cart.ts | 108 +++-------------- packages/core/types/src/cart/workflows.ts | 29 +++++ .../src/utils/composer/create-workflow.ts | 6 +- .../api-v2/store/carts/[id]/complete/route.ts | 5 +- .../store/shipping-options/[cart_id]/route.ts | 1 - .../link-modules/src/definitions/index.ts | 10 +- .../{ => readonly}/cart-customer.ts | 0 .../src/definitions/readonly/cart-product.ts | 43 +++++++ .../definitions/{ => readonly}/cart-region.ts | 0 .../{ => readonly}/cart-sales-channel.ts | 0 .../src/definitions/readonly/index.ts | 7 ++ .../inventory-level-stock-location.ts | 0 .../{ => readonly}/order-customer.ts | 0 .../{ => readonly}/store-default-currency.ts | 0 24 files changed, 344 insertions(+), 321 deletions(-) create mode 100644 packages/core/core-flows/src/definition/cart/workflows/confirm-variant-inventory.ts rename packages/modules/link-modules/src/definitions/{ => readonly}/cart-customer.ts (100%) create mode 100644 packages/modules/link-modules/src/definitions/readonly/cart-product.ts rename packages/modules/link-modules/src/definitions/{ => readonly}/cart-region.ts (100%) rename packages/modules/link-modules/src/definitions/{ => readonly}/cart-sales-channel.ts (100%) create mode 100644 packages/modules/link-modules/src/definitions/readonly/index.ts rename packages/modules/link-modules/src/definitions/{ => readonly}/inventory-level-stock-location.ts (100%) rename packages/modules/link-modules/src/definitions/{ => readonly}/order-customer.ts (100%) rename packages/modules/link-modules/src/definitions/{ => readonly}/store-default-currency.ts (100%) diff --git a/integration-tests/modules/__tests__/cart/store/cart.workflows.spec.ts b/integration-tests/modules/__tests__/cart/store/cart.workflows.spec.ts index e88b6873d3..597eda54e9 100644 --- a/integration-tests/modules/__tests__/cart/store/cart.workflows.spec.ts +++ b/integration-tests/modules/__tests__/cart/store/cart.workflows.spec.ts @@ -415,10 +415,12 @@ medusaIntegrationTestRunner({ expect(errors).toEqual([ { - action: "confirm-inventory-step", + action: "confirm-item-inventory-as-step", handlerType: "invoke", error: expect.objectContaining({ - message: "Some variant does not have the required inventory", + message: expect.stringContaining( + "Some variant does not have the required inventory" + ), }), }, ]) @@ -707,10 +709,13 @@ medusaIntegrationTestRunner({ expect(errors).toEqual([ { - action: "confirm-inventory-step", + action: "confirm-item-inventory-as-step", handlerType: "invoke", error: expect.objectContaining({ - message: `Variants with IDs ${product.variants[0].id} do not have a price`, + // TODO: FIX runAsStep nested errors + message: expect.stringContaining( + `Variants with IDs ${product.variants[0].id} do not have a price` + ), }), }, ]) diff --git a/integration-tests/modules/__tests__/cart/store/carts.spec.ts b/integration-tests/modules/__tests__/cart/store/carts.spec.ts index 085553d4d2..a889d3ca43 100644 --- a/integration-tests/modules/__tests__/cart/store/carts.spec.ts +++ b/integration-tests/modules/__tests__/cart/store/carts.spec.ts @@ -83,7 +83,7 @@ medusaIntegrationTestRunner({ }) describe("POST /store/carts", () => { - it.skip("should create a cart", async () => { + it("should create a cart", async () => { const region = await regionModule.create({ name: "US", currency_code: "usd", diff --git a/packages/core/core-flows/src/definition/cart/steps/confirm-inventory.ts b/packages/core/core-flows/src/definition/cart/steps/confirm-inventory.ts index 2ee45c3734..a26eaae88c 100644 --- a/packages/core/core-flows/src/definition/cart/steps/confirm-inventory.ts +++ b/packages/core/core-flows/src/definition/cart/steps/confirm-inventory.ts @@ -8,6 +8,7 @@ interface StepInput { items: { inventory_item_id: string required_quantity: number + allow_backorder: boolean quantity: number location_ids: string[] }[] @@ -23,6 +24,10 @@ export const confirmInventoryStep = createStep( // TODO: Should be bulk const promises = data.items.map(async (item) => { + if (item.allow_backorder) { + return true + } + const itemQuantity = item.required_quantity * item.quantity return await inventoryService.confirmInventory( diff --git a/packages/core/core-flows/src/definition/cart/utils/fields.ts b/packages/core/core-flows/src/definition/cart/utils/fields.ts index f2cefdacf7..d9e63d4116 100644 --- a/packages/core/core-flows/src/definition/cart/utils/fields.ts +++ b/packages/core/core-flows/src/definition/cart/utils/fields.ts @@ -37,6 +37,26 @@ export const completeCartFields = [ "original_shipping_tax_total", "original_shipping_tax_subtotal", "original_shipping_total", + "raw_total", + "raw_subtotal", + "raw_tax_total", + "raw_discount_total", + "raw_discount_tax_total", + "raw_original_total", + "raw_original_tax_total", + "raw_item_total", + "raw_item_subtotal", + "raw_item_tax_total", + "raw_sales_channel_id", + "raw_original_item_total", + "raw_original_item_subtotal", + "raw_original_item_tax_total", + "raw_shipping_total", + "raw_shipping_subtotal", + "raw_shipping_tax_total", + "raw_original_shipping_tax_total", + "raw_original_shipping_tax_subtotal", + "raw_original_shipping_total", "items.*", "items.tax_lines.*", "items.adjustments.*", @@ -49,4 +69,37 @@ export const completeCartFields = [ "region.*", "payment_collection.*", "payment_collection.payment_sessions.*", + "items.variant.id", + "items.variant.manage_inventory", + "items.variant.allow_backorder", + "items.variant.inventory_items.inventory_item_id", + "items.variant.inventory_items.required_quantity", + "items.variant.inventory_items.inventory.location_levels.stock_locations.id", + "items.variant.inventory_items.inventory.location_levels.stock_locations.name", + "items.variant.inventory_items.inventory.location_levels.stock_locations.sales_channels.id", + "items.variant.inventory_items.inventory.location_levels.stock_locations.sales_channels.name", +] + +export const productVariantsFields = [ + "id", + "title", + "sku", + "manage_inventory", + "allow_backorder", + "barcode", + "product.id", + "product.title", + "product.description", + "product.subtitle", + "product.thumbnail", + "product.type", + "product.collection", + "product.handle", + "calculated_price.calculated_amount", + "inventory_items.inventory_item_id", + "inventory_items.required_quantity", + "inventory_items.inventory.location_levels.stock_locations.id", + "inventory_items.inventory.location_levels.stock_locations.name", + "inventory_items.inventory.location_levels.stock_locations.sales_channels.id", + "inventory_items.inventory.location_levels.stock_locations.sales_channels.name", ] diff --git a/packages/core/core-flows/src/definition/cart/utils/prepare-confirm-inventory-input.ts b/packages/core/core-flows/src/definition/cart/utils/prepare-confirm-inventory-input.ts index 75a7895a91..4cbb379620 100644 --- a/packages/core/core-flows/src/definition/cart/utils/prepare-confirm-inventory-input.ts +++ b/packages/core/core-flows/src/definition/cart/utils/prepare-confirm-inventory-input.ts @@ -7,14 +7,22 @@ interface ConfirmInventoryPreparationInput { inventory_item_id: string required_quantity: number }[] - items: { variant_id?: string; quantity: BigNumberInput }[] - variants: { id: string; manage_inventory?: boolean }[] + items: { + variant_id?: string + quantity: BigNumberInput + }[] + variants: { + id: string + manage_inventory?: boolean + allow_backorder?: boolean + }[] location_ids: string[] } interface ConfirmInventoryItem { inventory_item_id: string required_quantity: number + allow_backorder: boolean quantity: number location_ids: string[] } @@ -31,7 +39,7 @@ export const prepareConfirmInventoryInput = ({ const variantsMap = new Map< string, - { id: string; manage_inventory?: boolean } + ConfirmInventoryPreparationInput["variants"][0] >(variants.map((v) => [v.id, v])) const itemsToConfirm: ConfirmInventoryItem[] = [] @@ -57,6 +65,7 @@ export const prepareConfirmInventoryInput = ({ itemsToConfirm.push({ inventory_item_id: variantInventoryItem.inventory_item_id, required_quantity: variantInventoryItem.required_quantity, + allow_backorder: !!variant.allow_backorder, quantity: item.quantity as number, // TODO: update type to BigNumberInput location_ids: location_ids, }) diff --git a/packages/core/core-flows/src/definition/cart/workflows/add-to-cart.ts b/packages/core/core-flows/src/definition/cart/workflows/add-to-cart.ts index e3062a8b11..34c96cf5c2 100644 --- a/packages/core/core-flows/src/definition/cart/workflows/add-to-cart.ts +++ b/packages/core/core-flows/src/definition/cart/workflows/add-to-cart.ts @@ -7,18 +7,16 @@ import { createWorkflow, transform, } from "@medusajs/workflows-sdk" -import { MedusaError } from "medusa-core-utils" import { useRemoteQueryStep } from "../../../common/steps/use-remote-query" -import { - addToCartStep, - confirmInventoryStep, - refreshCartShippingMethodsStep, -} from "../steps" +import { addToCartStep, refreshCartShippingMethodsStep } from "../steps" import { refreshCartPromotionsStep } from "../steps/refresh-cart-promotions" import { updateTaxLinesStep } from "../steps/update-tax-lines" -import { cartFieldsForRefreshSteps } from "../utils/fields" -import { prepareConfirmInventoryInput } from "../utils/prepare-confirm-inventory-input" +import { + cartFieldsForRefreshSteps, + productVariantsFields, +} from "../utils/fields" import { prepareLineItemData } from "../utils/prepare-line-item-data" +import { confirmVariantInventoryWorkflow } from "./confirm-variant-inventory" import { refreshPaymentCollectionForCartStep } from "./refresh-payment-collection" // TODO: The AddToCartWorkflow are missing the following steps: @@ -43,32 +41,7 @@ export const addToCartWorkflow = createWorkflow( const variants = useRemoteQueryStep({ entry_point: "variants", - fields: [ - "id", - "title", - "sku", - "barcode", - "manage_inventory", - "product.id", - "product.title", - "product.description", - "product.subtitle", - "product.thumbnail", - "product.type", - "product.collection", - "product.handle", - - "calculated_price.calculated_amount", - - "inventory_items.inventory_item_id", - "inventory_items.required_quantity", - - "inventory_items.inventory.location_levels.stock_locations.id", - "inventory_items.inventory.location_levels.stock_locations.name", - - "inventory_items.inventory.location_levels.stock_locations.sales_channels.id", - "inventory_items.inventory.location_levels.stock_locations.sales_channels.name", - ], + fields: productVariantsFields, variables: { id: variantIds, calculated_price: { @@ -78,71 +51,14 @@ export const addToCartWorkflow = createWorkflow( throw_if_key_not_found: true, }) - const confirmInventoryInput = transform({ input, variants }, (data) => { - const managedVariants = data.variants.filter((v) => v.manage_inventory) - if (!managedVariants.length) { - return { items: [] } - } - - const productVariantInventoryItems: any[] = [] - - const stockLocations = data.variants - .map((v) => v.inventory_items) - .flat() - .map((ii) => { - productVariantInventoryItems.push({ - variant_id: ii.variant_id, - inventory_item_id: ii.inventory_item_id, - required_quantity: ii.required_quantity, - }) - - return ii.inventory.location_levels - }) - .flat() - .map((ll) => ll.stock_locations) - .flat() - - const salesChannelId = data.input.cart.sales_channel_id - if (salesChannelId) { - const salesChannels = stockLocations - .map((sl) => sl.sales_channels) - .flat() - .filter((sc) => sc.id === salesChannelId) - - if (!salesChannels.length) { - throw new MedusaError( - MedusaError.Types.INVALID_DATA, - `Sales channel ${salesChannelId} is not associated with any stock locations.` - ) - } - } - - const priceNotFound: string[] = data.variants - .filter((v) => !v.calculated_price) - .map((v) => v.id) - - if (priceNotFound.length) { - throw new MedusaError( - MedusaError.Types.INVALID_DATA, - `Variants with IDs ${priceNotFound.join(", ")} do not have a price` - ) - } - - const items = prepareConfirmInventoryInput({ - product_variant_inventory_items: productVariantInventoryItems, - location_ids: stockLocations.map((l) => l.id), - items: data.input.items!, - variants: data.variants.map((v) => ({ - id: v.id, - manage_inventory: v.manage_inventory, - })), - }) - - return { items } + confirmVariantInventoryWorkflow.runAsStep({ + input: { + sales_channel_id: input.cart.sales_channel_id as string, + variants, + items: input.items, + }, }) - confirmInventoryStep(confirmInventoryInput) - const lineItems = transform({ input, variants }, (data) => { const items = (data.input.items ?? []).map((item) => { const variant = data.variants.find((v) => v.id === item.variant_id)! diff --git a/packages/core/core-flows/src/definition/cart/workflows/complete-cart.ts b/packages/core/core-flows/src/definition/cart/workflows/complete-cart.ts index c035b9cd1f..28451e91fc 100644 --- a/packages/core/core-flows/src/definition/cart/workflows/complete-cart.ts +++ b/packages/core/core-flows/src/definition/cart/workflows/complete-cart.ts @@ -1,9 +1,14 @@ -import { CompleteCartWorkflowInputDTO, OrderDTO } from "@medusajs/types" -import { WorkflowData, createWorkflow } from "@medusajs/workflows-sdk" +import { OrderDTO } from "@medusajs/types" +import { + WorkflowData, + createWorkflow, + transform, +} from "@medusajs/workflows-sdk" import { useRemoteQueryStep } from "../../../common" import { createOrderFromCartStep } from "../steps" import { updateTaxLinesStep } from "../steps/update-tax-lines" import { completeCartFields } from "../utils/fields" +import { confirmVariantInventoryWorkflow } from "./confirm-variant-inventory" /* - [] Create Tax Lines @@ -19,9 +24,7 @@ import { completeCartFields } from "../utils/fields" export const completeCartWorkflowId = "complete-cart" export const completeCartWorkflow = createWorkflow( completeCartWorkflowId, - ( - input: WorkflowData - ): WorkflowData => { + (input: WorkflowData): WorkflowData => { const cart = useRemoteQueryStep({ entry_point: "cart", fields: completeCartFields, @@ -29,6 +32,37 @@ export const completeCartWorkflow = createWorkflow( list: false, }) + const { variants, items, sales_channel_id } = transform( + { cart }, + (data) => { + const allItems: any[] = [] + const allVariants: any[] = [] + data.cart.items.forEach((item) => { + allItems.push({ + id: item.id, + quantity: item.quantity, + }) + + allVariants.push(item.variant) + }) + + return { + variants: allVariants, + items: allItems, + sales_channel_id: data.cart.sales_channel_id, + } + } + ) + + confirmVariantInventoryWorkflow.runAsStep({ + input: { + ignore_price_check: true, + sales_channel_id, + variants, + items, + }, + }) + updateTaxLinesStep({ cart_or_cart_id: cart, force_tax_calculation: true }) const finalCart = useRemoteQueryStep({ diff --git a/packages/core/core-flows/src/definition/cart/workflows/confirm-variant-inventory.ts b/packages/core/core-flows/src/definition/cart/workflows/confirm-variant-inventory.ts new file mode 100644 index 0000000000..c93bfe7619 --- /dev/null +++ b/packages/core/core-flows/src/definition/cart/workflows/confirm-variant-inventory.ts @@ -0,0 +1,96 @@ +import { ConfirmVariantInventoryWorkflowInputDTO } from "@medusajs/types" +import { MedusaError, deepFlatMap, isDefined } from "@medusajs/utils" +import { + WorkflowData, + createWorkflow, + transform, +} from "@medusajs/workflows-sdk" +import { confirmInventoryStep } from "../steps" +import { prepareConfirmInventoryInput } from "../utils/prepare-confirm-inventory-input" + +export const confirmVariantInventoryWorkflowId = "confirm-item-inventory" +export const confirmVariantInventoryWorkflow = createWorkflow( + confirmVariantInventoryWorkflowId, + (input: WorkflowData) => { + const confirmInventoryInput = transform({ input }, (data) => { + const productVariantInventoryItems = new Map() + const priceNotFound: string[] = [] + const stockLocationIds = new Set() + const allVariants = new Map() + let hasSalesChannelStockLocation = false + let hasManagedInventory = false + + const salesChannelId = data.input.sales_channel_id + + deepFlatMap( + data.input, + "variants.inventory_items.inventory.location_levels.stock_locations.sales_channels", + ({ variants, inventory_items, stock_locations, sales_channels }) => { + if ( + !hasSalesChannelStockLocation && + sales_channels?.id === salesChannelId + ) { + hasSalesChannelStockLocation = true + } + + if (!isDefined(variants.calculated_price)) { + priceNotFound.push(variants.id) + } + + stockLocationIds.add(stock_locations.id) + + const inventoryItemId = inventory_items.inventory_item_id + if (!productVariantInventoryItems.has(inventoryItemId)) { + productVariantInventoryItems.set(inventoryItemId, { + variant_id: inventory_items.variant_id, + inventory_item_id: inventoryItemId, + required_quantity: inventory_items.required_quantity, + }) + } + + if (!allVariants.has(variants.id)) { + if (!hasManagedInventory && variants.manage_inventory) { + hasManagedInventory = true + } + + allVariants.set(variants.id, { + id: variants.id, + manage_inventory: variants.manage_inventory, + }) + } + } + ) + + if (!hasManagedInventory) { + return { items: [] } + } + + if (salesChannelId && !hasSalesChannelStockLocation) { + throw new MedusaError( + MedusaError.Types.INVALID_DATA, + `Sales channel ${salesChannelId} is not associated with any stock locations.` + ) + } + + if (priceNotFound.length && !data.input.ignore_price_check) { + throw new MedusaError( + MedusaError.Types.INVALID_DATA, + `Variants with IDs ${priceNotFound.join(", ")} do not have a price` + ) + } + + const items = prepareConfirmInventoryInput({ + product_variant_inventory_items: Array.from( + productVariantInventoryItems.values() + ), + location_ids: Array.from(stockLocationIds), + items: data.input.items, + variants: Array.from(allVariants.values()), + }) + + return { items } + }) + + confirmInventoryStep(confirmInventoryInput) + } +) diff --git a/packages/core/core-flows/src/definition/cart/workflows/create-carts.ts b/packages/core/core-flows/src/definition/cart/workflows/create-carts.ts index 6a2cdaf7e4..e323666e32 100644 --- a/packages/core/core-flows/src/definition/cart/workflows/create-carts.ts +++ b/packages/core/core-flows/src/definition/cart/workflows/create-carts.ts @@ -5,10 +5,8 @@ import { parallelize, transform, } from "@medusajs/workflows-sdk" -import { MedusaError } from "medusa-core-utils" import { useRemoteQueryStep } from "../../../common/steps/use-remote-query" import { - confirmInventoryStep, createCartsStep, findOneOrAnyRegionStep, findOrCreateCustomerStep, @@ -17,8 +15,9 @@ import { } from "../steps" import { refreshCartPromotionsStep } from "../steps/refresh-cart-promotions" import { updateTaxLinesStep } from "../steps/update-tax-lines" -import { prepareConfirmInventoryInput } from "../utils/prepare-confirm-inventory-input" +import { productVariantsFields } from "../utils/fields" import { prepareLineItemData } from "../utils/prepare-line-item-data" +import { confirmVariantInventoryWorkflow } from "./confirm-variant-inventory" import { refreshPaymentCollectionForCartStep } from "./refresh-payment-collection" // TODO: The createCartWorkflow are missing the following steps: @@ -59,32 +58,7 @@ export const createCartWorkflow = createWorkflow( const variants = useRemoteQueryStep({ entry_point: "variants", - fields: [ - "id", - "title", - "sku", - "manage_inventory", - "barcode", - "product.id", - "product.title", - "product.description", - "product.subtitle", - "product.thumbnail", - "product.type", - "product.collection", - "product.handle", - - "calculated_price.calculated_amount", - - "inventory_items.inventory_item_id", - "inventory_items.required_quantity", - - "inventory_items.inventory.location_levels.stock_locations.id", - "inventory_items.inventory.location_levels.stock_locations.name", - - "inventory_items.inventory.location_levels.stock_locations.sales_channels.id", - "inventory_items.inventory.location_levels.stock_locations.sales_channels.name", - ], + fields: productVariantsFields, variables: { id: variantIds, calculated_price: { @@ -94,73 +68,13 @@ export const createCartWorkflow = createWorkflow( throw_if_key_not_found: true, }) - const confirmInventoryInput = transform( - { input, salesChannel, variants }, - (data) => { - const managedVariants = data.variants.filter((v) => v.manage_inventory) - if (!managedVariants.length) { - return { items: [] } - } - - const productVariantInventoryItems: any[] = [] - - const stockLocations = managedVariants - .map((v) => v.inventory_items) - .flat() - .map((ii) => { - productVariantInventoryItems.push({ - variant_id: ii.variant_id, - inventory_item_id: ii.inventory_item_id, - required_quantity: ii.required_quantity, - }) - - return ii.inventory.location_levels - }) - .flat() - .map((ll) => ll.stock_locations) - .flat() - - const salesChannelId = data.salesChannel?.id - if (salesChannelId) { - const salesChannels = stockLocations - .map((sl) => sl.sales_channels) - .flat() - .filter((sc) => sc.id === salesChannelId) - - if (!salesChannels.length) { - throw new MedusaError( - MedusaError.Types.INVALID_DATA, - `Sales channel ${salesChannelId} is not associated with any stock locations.` - ) - } - } - - const priceNotFound: string[] = data.variants - .filter((v) => !v.calculated_price) - .map((v) => v.id) - - if (priceNotFound.length) { - throw new MedusaError( - MedusaError.Types.INVALID_DATA, - `Variants with IDs ${priceNotFound.join(", ")} do not have a price` - ) - } - - const items = prepareConfirmInventoryInput({ - product_variant_inventory_items: productVariantInventoryItems, - location_ids: stockLocations.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) + confirmVariantInventoryWorkflow.runAsStep({ + input: { + sales_channel_id: salesChannel.id, + variants, + items: input.items!, + }, + }) const priceSets = getVariantPriceSetsStep({ variantIds, diff --git a/packages/core/core-flows/src/definition/cart/workflows/create-payment-collection-for-cart.ts b/packages/core/core-flows/src/definition/cart/workflows/create-payment-collection-for-cart.ts index de6fac87b5..81b0dd18ba 100644 --- a/packages/core/core-flows/src/definition/cart/workflows/create-payment-collection-for-cart.ts +++ b/packages/core/core-flows/src/definition/cart/workflows/create-payment-collection-for-cart.ts @@ -1,13 +1,9 @@ -import { - CartDTO, - CreatePaymentCollectionForCartWorkflowInputDTO, -} from "@medusajs/types" +import { CreatePaymentCollectionForCartWorkflowInputDTO } from "@medusajs/types" import { WorkflowData, createWorkflow, transform, } from "@medusajs/workflows-sdk" -import { retrieveCartStep } from "../steps" import { createPaymentCollectionsStep } from "../steps/create-payment-collection" import { linkCartAndPaymentCollectionsStep } from "../steps/link-cart-payment-collection" @@ -17,7 +13,7 @@ export const createPaymentCollectionForCartWorkflow = createWorkflow( createPaymentCollectionForCartWorkflowId, ( input: WorkflowData - ): WorkflowData => { + ): WorkflowData => { const created = createPaymentCollectionsStep([input]) const link = transform({ cartId: input.cart_id, created }, (data) => ({ @@ -30,9 +26,5 @@ export const createPaymentCollectionForCartWorkflow = createWorkflow( })) linkCartAndPaymentCollectionsStep(link) - - const cart = retrieveCartStep({ id: input.cart_id }) - - return cart } ) diff --git a/packages/core/core-flows/src/definition/cart/workflows/update-line-item-in-cart.ts b/packages/core/core-flows/src/definition/cart/workflows/update-line-item-in-cart.ts index f4ebc48c28..1cda2b69be 100644 --- a/packages/core/core-flows/src/definition/cart/workflows/update-line-item-in-cart.ts +++ b/packages/core/core-flows/src/definition/cart/workflows/update-line-item-in-cart.ts @@ -4,13 +4,15 @@ import { createWorkflow, transform, } from "@medusajs/workflows-sdk" -import { MedusaError } from "medusa-core-utils" import { useRemoteQueryStep } from "../../../common/steps/use-remote-query" import { updateLineItemsStep } from "../../line-item/steps" -import { confirmInventoryStep, refreshCartShippingMethodsStep } from "../steps" +import { refreshCartShippingMethodsStep } from "../steps" import { refreshCartPromotionsStep } from "../steps/refresh-cart-promotions" -import { cartFieldsForRefreshSteps } from "../utils/fields" -import { prepareConfirmInventoryInput } from "../utils/prepare-confirm-inventory-input" +import { + cartFieldsForRefreshSteps, + productVariantsFields, +} from "../utils/fields" +import { confirmVariantInventoryWorkflow } from "./confirm-variant-inventory" import { refreshPaymentCollectionForCartStep } from "./refresh-payment-collection" // TODO: The UpdateLineItemsWorkflow are missing the following steps: @@ -35,32 +37,7 @@ export const updateLineItemInCartWorkflow = createWorkflow( const variants = useRemoteQueryStep({ entry_point: "variants", - fields: [ - "id", - "title", - "sku", - "barcode", - "manage_inventory", - "product.id", - "product.title", - "product.description", - "product.subtitle", - "product.thumbnail", - "product.type", - "product.collection", - "product.handle", - - "calculated_price.calculated_amount", - - "inventory_items.inventory_item_id", - "inventory_items.required_quantity", - - "inventory_items.inventory.location_levels.stock_locations.id", - "inventory_items.inventory.location_levels.stock_locations.name", - - "inventory_items.inventory.location_levels.stock_locations.sales_channels.id", - "inventory_items.inventory.location_levels.stock_locations.sales_channels.name", - ], + fields: productVariantsFields, variables: { id: variantIds, calculated_price: { @@ -70,70 +47,17 @@ export const updateLineItemInCartWorkflow = createWorkflow( throw_if_key_not_found: true, }) - const confirmInventoryInput = transform({ input, variants }, (data) => { - const managedVariants = data.variants.filter((v) => v.manage_inventory) - if (!managedVariants.length) { - return { items: [] } - } - - const productVariantInventoryItems: any[] = [] - - const stockLocations = data.variants - .map((v) => v.inventory_items) - .flat() - .map((ii) => { - productVariantInventoryItems.push({ - variant_id: ii.variant_id, - inventory_item_id: ii.inventory_item_id, - required_quantity: ii.required_quantity, - }) - - return ii.inventory.location_levels - }) - .flat() - .map((ll) => ll.stock_locations) - .flat() - - const salesChannelId = data.input.cart.sales_channel_id - if (salesChannelId) { - const salesChannels = stockLocations - .map((sl) => sl.sales_channels) - .flat() - .filter((sc) => sc.id === salesChannelId) - - if (!salesChannels.length) { - throw new MedusaError( - MedusaError.Types.INVALID_DATA, - `Sales channel ${salesChannelId} is not associated with any stock locations.` - ) - } - } - - const priceNotFound: string[] = data.variants - .filter((v) => !v.calculated_price) - .map((v) => v.id) - - if (priceNotFound.length) { - throw new MedusaError( - MedusaError.Types.INVALID_DATA, - `Variants with IDs ${priceNotFound.join(", ")} do not have a price` - ) - } - - const items = prepareConfirmInventoryInput({ - product_variant_inventory_items: productVariantInventoryItems, - location_ids: stockLocations.map((l) => l.id), - items: [data.input.item!], - variants: data.variants.map((v) => ({ - id: v.id, - manage_inventory: v.manage_inventory, - })), - }) - - return { items } + const items = transform({ input }, (data) => { + return [data.input.item] }) - confirmInventoryStep(confirmInventoryInput) + confirmVariantInventoryWorkflow.runAsStep({ + input: { + sales_channel_id: input.cart.sales_channel_id as string, + variants, + items, + }, + }) const lineItemUpdate = transform({ input, variants }, (data) => { const variant = data.variants[0] diff --git a/packages/core/types/src/cart/workflows.ts b/packages/core/types/src/cart/workflows.ts index af03fe629e..ffd4559eda 100644 --- a/packages/core/types/src/cart/workflows.ts +++ b/packages/core/types/src/cart/workflows.ts @@ -122,3 +122,32 @@ export interface PricedShippingOptionDTO extends ShippingOptionDTO { export interface CompleteCartWorkflowInputDTO { id: string } + +export interface ConfirmVariantInventoryWorkflowInputDTO { + ignore_price_check?: boolean + + sales_channel_id: string + variants: { + id: string + manage_inventory: boolean + inventory_items: { + inventory_item_id: string + variant_id: string + required_quantity: BigNumberInput + inventory: { + location_levels: { + stock_locations: { + id: string + sales_channels: { + id: string + }[] + }[] + } + }[] + }[] + }[] + items: { + variant_id?: string + quantity: BigNumberInput + }[] +} diff --git a/packages/core/workflows-sdk/src/utils/composer/create-workflow.ts b/packages/core/workflows-sdk/src/utils/composer/create-workflow.ts index 00c209633a..ac4d772990 100644 --- a/packages/core/workflows-sdk/src/utils/composer/create-workflow.ts +++ b/packages/core/workflows-sdk/src/utils/composer/create-workflow.ts @@ -4,8 +4,10 @@ import { WorkflowManager, } from "@medusajs/orchestration" import { LoadedModule, MedusaContainer } from "@medusajs/types" -import { isString, OrchestrationUtils } from "@medusajs/utils" +import { OrchestrationUtils, isString } from "@medusajs/utils" import { exportWorkflow } from "../../helper" +import { createStep } from "./create-step" +import { StepResponse } from "./helpers" import { proxify } from "./helpers/proxy" import { CreateWorkflowComposerContext, @@ -14,8 +16,6 @@ import { WorkflowData, WorkflowDataProperties, } from "./type" -import { createStep } from "./create-step" -import { StepResponse } from "./helpers" global[OrchestrationUtils.SymbolMedusaWorkflowComposerContext] = null diff --git a/packages/medusa/src/api-v2/store/carts/[id]/complete/route.ts b/packages/medusa/src/api-v2/store/carts/[id]/complete/route.ts index 89f8930ea7..7492562b19 100644 --- a/packages/medusa/src/api-v2/store/carts/[id]/complete/route.ts +++ b/packages/medusa/src/api-v2/store/carts/[id]/complete/route.ts @@ -7,7 +7,7 @@ export const POST = async ( req: MedusaRequest, res: MedusaResponse ) => { - const { idempotency_key: idempotencyKey } = req.validatedBody + const cart_id = req.params.id // If the idempotencyKey is present: // - is workflow is running? @@ -17,6 +17,9 @@ export const POST = async ( const { errors, result } = await completeCartWorkflow(req.scope).run({ input: { id: req.params.id }, + context: { + transactionId: cart_id, + }, throwOnError: false, }) diff --git a/packages/medusa/src/api-v2/store/shipping-options/[cart_id]/route.ts b/packages/medusa/src/api-v2/store/shipping-options/[cart_id]/route.ts index 9ad6e6416f..f2cdf2bd18 100644 --- a/packages/medusa/src/api-v2/store/shipping-options/[cart_id]/route.ts +++ b/packages/medusa/src/api-v2/store/shipping-options/[cart_id]/route.ts @@ -14,7 +14,6 @@ export const GET = async (req: MedusaRequest, res: MedusaResponse) => { select: [ "id", "sales_channel_id", - "subtotal", "currency_code", "shipping_address.city", "shipping_address.country_code", diff --git a/packages/modules/link-modules/src/definitions/index.ts b/packages/modules/link-modules/src/definitions/index.ts index e868554389..c09af37fc2 100644 --- a/packages/modules/link-modules/src/definitions/index.ts +++ b/packages/modules/link-modules/src/definitions/index.ts @@ -1,11 +1,6 @@ -export * from "./cart-customer" export * from "./cart-payment-collection" export * from "./cart-promotion" -export * from "./cart-region" -export * from "./cart-sales-channel" -export * from "./inventory-level-stock-location" -export * from "./location-fulfillment-set" -export * from "./order-customer" +export * from "./fulfillment-set-location" export * from "./order-promotion" export * from "./order-region" export * from "./order-sales-channel" @@ -14,8 +9,7 @@ export * from "./product-shipping-profile" export * from "./product-variant-inventory-item" export * from "./product-variant-price-set" export * from "./publishable-api-key-sales-channel" +export * from "./readonly" 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/modules/link-modules/src/definitions/cart-customer.ts b/packages/modules/link-modules/src/definitions/readonly/cart-customer.ts similarity index 100% rename from packages/modules/link-modules/src/definitions/cart-customer.ts rename to packages/modules/link-modules/src/definitions/readonly/cart-customer.ts diff --git a/packages/modules/link-modules/src/definitions/readonly/cart-product.ts b/packages/modules/link-modules/src/definitions/readonly/cart-product.ts new file mode 100644 index 0000000000..610b1b960b --- /dev/null +++ b/packages/modules/link-modules/src/definitions/readonly/cart-product.ts @@ -0,0 +1,43 @@ +import { Modules } from "@medusajs/modules-sdk" +import { ModuleJoinerConfig } from "@medusajs/types" + +export const CartProduct: ModuleJoinerConfig = { + isLink: true, + isReadOnlyLink: true, + extends: [ + { + serviceName: Modules.CART, + relationship: { + serviceName: Modules.PRODUCT, + primaryKey: "id", + foreignKey: "items.product_id", + alias: "product", + }, + }, + { + serviceName: Modules.CART, + relationship: { + serviceName: Modules.PRODUCT, + primaryKey: "id", + foreignKey: "items.variant_id", + alias: "variant", + args: { + methodSuffix: "Variants", + }, + }, + }, + { + serviceName: Modules.PRODUCT, + relationship: { + serviceName: Modules.CART, + primaryKey: "variant_id", + foreignKey: "id", + alias: "cart_items", + isList: true, + args: { + methodSuffix: "LineItems", + }, + }, + }, + ], +} diff --git a/packages/modules/link-modules/src/definitions/cart-region.ts b/packages/modules/link-modules/src/definitions/readonly/cart-region.ts similarity index 100% rename from packages/modules/link-modules/src/definitions/cart-region.ts rename to packages/modules/link-modules/src/definitions/readonly/cart-region.ts diff --git a/packages/modules/link-modules/src/definitions/cart-sales-channel.ts b/packages/modules/link-modules/src/definitions/readonly/cart-sales-channel.ts similarity index 100% rename from packages/modules/link-modules/src/definitions/cart-sales-channel.ts rename to packages/modules/link-modules/src/definitions/readonly/cart-sales-channel.ts diff --git a/packages/modules/link-modules/src/definitions/readonly/index.ts b/packages/modules/link-modules/src/definitions/readonly/index.ts new file mode 100644 index 0000000000..03579097ad --- /dev/null +++ b/packages/modules/link-modules/src/definitions/readonly/index.ts @@ -0,0 +1,7 @@ +export * from "./cart-customer" +export * from "./cart-product" +export * from "./cart-region" +export * from "./cart-sales-channel" +export * from "./inventory-level-stock-location" +export * from "./order-customer" +export * from "./store-default-currency" diff --git a/packages/modules/link-modules/src/definitions/inventory-level-stock-location.ts b/packages/modules/link-modules/src/definitions/readonly/inventory-level-stock-location.ts similarity index 100% rename from packages/modules/link-modules/src/definitions/inventory-level-stock-location.ts rename to packages/modules/link-modules/src/definitions/readonly/inventory-level-stock-location.ts diff --git a/packages/modules/link-modules/src/definitions/order-customer.ts b/packages/modules/link-modules/src/definitions/readonly/order-customer.ts similarity index 100% rename from packages/modules/link-modules/src/definitions/order-customer.ts rename to packages/modules/link-modules/src/definitions/readonly/order-customer.ts diff --git a/packages/modules/link-modules/src/definitions/store-default-currency.ts b/packages/modules/link-modules/src/definitions/readonly/store-default-currency.ts similarity index 100% rename from packages/modules/link-modules/src/definitions/store-default-currency.ts rename to packages/modules/link-modules/src/definitions/readonly/store-default-currency.ts