feat(medusa,core-flows): complete cart [part-1] (#7201)
what: - adds a very basic complete cart endpoint that creates an order - the complete cart workflow currently does the following: - create tax lines - create order
This commit is contained in:
1 parent
e78362c000
commit
f129415650
19 files changed
+554
-34
No files matched your search
@@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
"@medusajs/core-flows": patch
|
||||||
|
"@medusajs/medusa": patch
|
||||||
|
"@medusajs/types": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
feat(medusa,core-flows,types): added a basic endpoint for complete cart
|
||||||
@@ -16,18 +16,20 @@ import {
|
|||||||
ITaxModuleService,
|
ITaxModuleService,
|
||||||
} from "@medusajs/types"
|
} from "@medusajs/types"
|
||||||
import {
|
import {
|
||||||
|
ContainerRegistrationKeys,
|
||||||
PromotionRuleOperator,
|
PromotionRuleOperator,
|
||||||
PromotionType,
|
PromotionType,
|
||||||
RuleOperator,
|
RuleOperator,
|
||||||
} from "@medusajs/utils"
|
} from "@medusajs/utils"
|
||||||
import { medusaIntegrationTestRunner } from "medusa-test-utils"
|
import { medusaIntegrationTestRunner } from "medusa-test-utils"
|
||||||
import adminSeeder from "../../../../helpers/admin-seeder"
|
import { createAdminUser } from "../../../../helpers/create-admin-user"
|
||||||
import { createAuthenticatedCustomer } from "../../../helpers/create-authenticated-customer"
|
import { createAuthenticatedCustomer } from "../../../helpers/create-authenticated-customer"
|
||||||
import { setupTaxStructure } from "../../fixtures"
|
import { setupTaxStructure } from "../../fixtures"
|
||||||
|
|
||||||
jest.setTimeout(100000)
|
jest.setTimeout(100000)
|
||||||
|
|
||||||
const env = { MEDUSA_FF_MEDUSA_V2: true }
|
const env = { MEDUSA_FF_MEDUSA_V2: true }
|
||||||
|
const adminHeaders = { headers: { "x-medusa-access-token": "test_token" } }
|
||||||
|
|
||||||
medusaIntegrationTestRunner({
|
medusaIntegrationTestRunner({
|
||||||
env,
|
env,
|
||||||
@@ -44,8 +46,11 @@ medusaIntegrationTestRunner({
|
|||||||
let promotionModule: IPromotionModuleService
|
let promotionModule: IPromotionModuleService
|
||||||
let taxModule: ITaxModuleService
|
let taxModule: ITaxModuleService
|
||||||
let fulfillmentModule: IFulfillmentModuleService
|
let fulfillmentModule: IFulfillmentModuleService
|
||||||
|
let remoteLinkService
|
||||||
|
let regionService: IRegionModuleService
|
||||||
|
|
||||||
let defaultRegion
|
let defaultRegion
|
||||||
|
let region
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
appContainer = getContainer()
|
appContainer = getContainer()
|
||||||
@@ -58,13 +63,17 @@ medusaIntegrationTestRunner({
|
|||||||
remoteLink = appContainer.resolve(LinkModuleUtils.REMOTE_LINK)
|
remoteLink = appContainer.resolve(LinkModuleUtils.REMOTE_LINK)
|
||||||
promotionModule = appContainer.resolve(ModuleRegistrationName.PROMOTION)
|
promotionModule = appContainer.resolve(ModuleRegistrationName.PROMOTION)
|
||||||
taxModule = appContainer.resolve(ModuleRegistrationName.TAX)
|
taxModule = appContainer.resolve(ModuleRegistrationName.TAX)
|
||||||
|
regionService = appContainer.resolve(ModuleRegistrationName.REGION)
|
||||||
fulfillmentModule = appContainer.resolve(
|
fulfillmentModule = appContainer.resolve(
|
||||||
ModuleRegistrationName.FULFILLMENT
|
ModuleRegistrationName.FULFILLMENT
|
||||||
)
|
)
|
||||||
|
remoteLinkService = appContainer.resolve(
|
||||||
|
ContainerRegistrationKeys.REMOTE_LINK
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await adminSeeder(dbConnection)
|
await createAdminUser(dbConnection, adminHeaders, appContainer)
|
||||||
|
|
||||||
// Here, so we don't have to create a region for each test
|
// Here, so we don't have to create a region for each test
|
||||||
defaultRegion = await regionModule.create({
|
defaultRegion = await regionModule.create({
|
||||||
@@ -1389,6 +1398,196 @@ medusaIntegrationTestRunner({
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe("POST /store/carts/:id/complete", () => {
|
||||||
|
let salesChannel
|
||||||
|
let product
|
||||||
|
let shippingProfile
|
||||||
|
let fulfillmentSet
|
||||||
|
let shippingOption
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await setupTaxStructure(taxModule)
|
||||||
|
region = await regionService.create({
|
||||||
|
name: "Test region",
|
||||||
|
countries: ["US"],
|
||||||
|
currency_code: "usd",
|
||||||
|
})
|
||||||
|
|
||||||
|
salesChannel = (
|
||||||
|
await api.post(
|
||||||
|
"/admin/sales-channels",
|
||||||
|
{ name: "first channel", description: "channel" },
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
).data.sales_channel
|
||||||
|
|
||||||
|
product = (
|
||||||
|
await api.post(
|
||||||
|
"/admin/products",
|
||||||
|
{
|
||||||
|
title: "Test fixture",
|
||||||
|
tags: [{ value: "123" }, { value: "456" }],
|
||||||
|
options: [
|
||||||
|
{ title: "size", values: ["large", "small"] },
|
||||||
|
{ title: "color", values: ["green"] },
|
||||||
|
],
|
||||||
|
variants: [
|
||||||
|
{
|
||||||
|
title: "Test variant",
|
||||||
|
inventory_quantity: 10,
|
||||||
|
prices: [
|
||||||
|
{
|
||||||
|
currency_code: "usd",
|
||||||
|
amount: 100,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
options: {
|
||||||
|
size: "large",
|
||||||
|
color: "green",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
).data.product
|
||||||
|
|
||||||
|
const stockLocation = (
|
||||||
|
await api.post(
|
||||||
|
`/admin/stock-locations`,
|
||||||
|
{ name: "test location" },
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
).data.stock_location
|
||||||
|
|
||||||
|
await api.post(
|
||||||
|
`/admin/stock-locations/${stockLocation.id}/sales-channels`,
|
||||||
|
{ add: [salesChannel.id] },
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
shippingProfile = await fulfillmentModule.createShippingProfiles({
|
||||||
|
name: "Test",
|
||||||
|
type: "default",
|
||||||
|
})
|
||||||
|
|
||||||
|
fulfillmentSet = await fulfillmentModule.create({
|
||||||
|
name: "Test",
|
||||||
|
type: "test-type",
|
||||||
|
service_zones: [
|
||||||
|
{
|
||||||
|
name: "Test",
|
||||||
|
geo_zones: [{ type: "country", country_code: "us" }],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
await remoteLinkService.create([
|
||||||
|
{
|
||||||
|
[Modules.FULFILLMENT]: { fulfillment_set_id: fulfillmentSet.id },
|
||||||
|
[Modules.STOCK_LOCATION]: { stock_location_id: stockLocation.id },
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
shippingOption = (
|
||||||
|
await api.post(
|
||||||
|
`/admin/shipping-options`,
|
||||||
|
{
|
||||||
|
name: "Test shipping option",
|
||||||
|
service_zone_id: fulfillmentSet.service_zones[0].id,
|
||||||
|
shipping_profile_id: shippingProfile.id,
|
||||||
|
provider_id: "manual_test-provider",
|
||||||
|
price_type: "flat",
|
||||||
|
type: {
|
||||||
|
label: "Test type",
|
||||||
|
description: "Test description",
|
||||||
|
code: "test-code",
|
||||||
|
},
|
||||||
|
prices: [
|
||||||
|
{ currency_code: "usd", amount: 1000 },
|
||||||
|
{ region_id: region.id, amount: 1100 },
|
||||||
|
],
|
||||||
|
rules: [],
|
||||||
|
},
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
).data.shipping_option
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should create an order", async () => {
|
||||||
|
const cartResponse = await api.post(`/store/carts`, {
|
||||||
|
currency_code: "usd",
|
||||||
|
email: "tony@stark-industries.com",
|
||||||
|
shipping_address: {
|
||||||
|
address_1: "test address 1",
|
||||||
|
address_2: "test address 2",
|
||||||
|
city: "ny",
|
||||||
|
country_code: "us",
|
||||||
|
province: "ny",
|
||||||
|
postal_code: "94016",
|
||||||
|
},
|
||||||
|
// TODO: inventory isn't being managed on a product level
|
||||||
|
// sales_channel_id: salesChannel.id,
|
||||||
|
items: [{ quantity: 1, variant_id: product.variants[0].id }],
|
||||||
|
})
|
||||||
|
|
||||||
|
const response = await api.post(
|
||||||
|
`/store/carts/${cartResponse.data.cart.id}/complete`,
|
||||||
|
{}
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(response.status).toEqual(200)
|
||||||
|
expect(response.data.order).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
total: 106,
|
||||||
|
subtotal: 100,
|
||||||
|
tax_total: 6,
|
||||||
|
discount_total: 0,
|
||||||
|
discount_tax_total: 0,
|
||||||
|
original_total: 106,
|
||||||
|
original_tax_total: 6,
|
||||||
|
item_total: 106,
|
||||||
|
item_subtotal: 100,
|
||||||
|
item_tax_total: 6,
|
||||||
|
original_item_total: 106,
|
||||||
|
original_item_subtotal: 100,
|
||||||
|
original_item_tax_total: 6,
|
||||||
|
shipping_total: 0,
|
||||||
|
shipping_subtotal: 0,
|
||||||
|
shipping_tax_total: 0,
|
||||||
|
original_shipping_tax_total: 0,
|
||||||
|
original_shipping_tax_subtotal: 0,
|
||||||
|
original_shipping_total: 0,
|
||||||
|
items: [
|
||||||
|
expect.objectContaining({
|
||||||
|
product_id: product.id,
|
||||||
|
unit_price: 100,
|
||||||
|
quantity: 1,
|
||||||
|
tax_total: 6,
|
||||||
|
subtotal: 100,
|
||||||
|
total: 106,
|
||||||
|
original_total: 106,
|
||||||
|
discount_total: 0,
|
||||||
|
tax_lines: [
|
||||||
|
expect.objectContaining({
|
||||||
|
rate: 6,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
adjustments: [],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
shipping_address: expect.objectContaining({
|
||||||
|
city: "ny",
|
||||||
|
country_code: "us",
|
||||||
|
province: "ny",
|
||||||
|
postal_code: "94016",
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
import { CartWorkflowDTO } from "@medusajs/types"
|
||||||
|
import { OrderStatus } from "@medusajs/utils"
|
||||||
|
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||||
|
import { createOrdersWorkflow } from "../../../order/workflows/create-orders"
|
||||||
|
|
||||||
|
interface StepInput {
|
||||||
|
cart: CartWorkflowDTO
|
||||||
|
}
|
||||||
|
|
||||||
|
export const createOrderFromCartStepId = "create-order-from-cart"
|
||||||
|
export const createOrderFromCartStep = createStep(
|
||||||
|
createOrderFromCartStepId,
|
||||||
|
async (input: StepInput, { container }) => {
|
||||||
|
const { cart } = input
|
||||||
|
|
||||||
|
const itemAdjustments = (cart.items || [])
|
||||||
|
?.map((item) => item.adjustments || [])
|
||||||
|
.flat(1)
|
||||||
|
const shippingAdjustments = (cart.shipping_methods || [])
|
||||||
|
?.map((sm) => sm.adjustments || [])
|
||||||
|
.flat(1)
|
||||||
|
|
||||||
|
const promoCodes = [...itemAdjustments, ...shippingAdjustments]
|
||||||
|
.map((adjustment) => adjustment.code)
|
||||||
|
.filter((code) => Boolean) as string[]
|
||||||
|
|
||||||
|
const { transaction, result } = await createOrdersWorkflow(container).run({
|
||||||
|
input: {
|
||||||
|
region_id: cart.region?.id,
|
||||||
|
customer_id: cart.customer?.id,
|
||||||
|
sales_channel_id: cart.sales_channel_id,
|
||||||
|
status: OrderStatus.PENDING,
|
||||||
|
email: cart.email,
|
||||||
|
currency_code: cart.currency_code,
|
||||||
|
shipping_address: cart.shipping_address,
|
||||||
|
billing_address: cart.billing_address,
|
||||||
|
// TODO: This should be handle correctly
|
||||||
|
no_notification: false,
|
||||||
|
items: cart.items,
|
||||||
|
shipping_methods: cart.shipping_methods,
|
||||||
|
metadata: cart.metadata,
|
||||||
|
promo_codes: promoCodes,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
return new StepResponse(result, { transaction })
|
||||||
|
},
|
||||||
|
async (flow, { container }) => {
|
||||||
|
if (!flow) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
await createOrdersWorkflow(container).cancel({
|
||||||
|
transaction: flow.transaction,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -3,6 +3,7 @@ export * from "./add-to-cart"
|
|||||||
export * from "./confirm-inventory"
|
export * from "./confirm-inventory"
|
||||||
export * from "./create-carts"
|
export * from "./create-carts"
|
||||||
export * from "./create-line-item-adjustments"
|
export * from "./create-line-item-adjustments"
|
||||||
|
export * from "./create-order-from-cart"
|
||||||
export * from "./create-shipping-method-adjustments"
|
export * from "./create-shipping-method-adjustments"
|
||||||
export * from "./find-one-or-any-region"
|
export * from "./find-one-or-any-region"
|
||||||
export * from "./find-or-create-customer"
|
export * from "./find-or-create-customer"
|
||||||
|
|||||||
@@ -10,3 +10,43 @@ export const cartFieldsForRefreshSteps = [
|
|||||||
"customer.*",
|
"customer.*",
|
||||||
"customer.groups.*",
|
"customer.groups.*",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
export const completeCartFields = [
|
||||||
|
"id",
|
||||||
|
"currency_code",
|
||||||
|
"email",
|
||||||
|
"created_at",
|
||||||
|
"updated_at",
|
||||||
|
"total",
|
||||||
|
"subtotal",
|
||||||
|
"tax_total",
|
||||||
|
"discount_total",
|
||||||
|
"discount_tax_total",
|
||||||
|
"original_total",
|
||||||
|
"original_tax_total",
|
||||||
|
"item_total",
|
||||||
|
"item_subtotal",
|
||||||
|
"item_tax_total",
|
||||||
|
"sales_channel_id",
|
||||||
|
"original_item_total",
|
||||||
|
"original_item_subtotal",
|
||||||
|
"original_item_tax_total",
|
||||||
|
"shipping_total",
|
||||||
|
"shipping_subtotal",
|
||||||
|
"shipping_tax_total",
|
||||||
|
"original_shipping_tax_total",
|
||||||
|
"original_shipping_tax_subtotal",
|
||||||
|
"original_shipping_total",
|
||||||
|
"items.*",
|
||||||
|
"items.tax_lines.*",
|
||||||
|
"items.adjustments.*",
|
||||||
|
"customer.*",
|
||||||
|
"shipping_methods.*",
|
||||||
|
"shipping_methods.tax_lines.*",
|
||||||
|
"shipping_methods.adjustments.*",
|
||||||
|
"shipping_address.*",
|
||||||
|
"billing_address.*",
|
||||||
|
"region.*",
|
||||||
|
"payment_collection.*",
|
||||||
|
"payment_collection.payment_sessions.*",
|
||||||
|
]
|
||||||
@@ -1,15 +1,30 @@
|
|||||||
import { BigNumberInput, ProductVariantDTO } from "@medusajs/types"
|
import {
|
||||||
|
BigNumberInput,
|
||||||
|
CreateOrderAdjustmentDTO,
|
||||||
|
CreateOrderLineItemTaxLineDTO,
|
||||||
|
ProductVariantDTO,
|
||||||
|
} from "@medusajs/types"
|
||||||
|
|
||||||
interface Input {
|
interface Input {
|
||||||
quantity: BigNumberInput
|
quantity: BigNumberInput
|
||||||
metadata?: Record<string, any>
|
metadata?: Record<string, any>
|
||||||
unitPrice: BigNumberInput
|
unitPrice: BigNumberInput
|
||||||
variant: ProductVariantDTO
|
variant: ProductVariantDTO
|
||||||
|
taxLines?: CreateOrderLineItemTaxLineDTO[]
|
||||||
|
adjustments?: CreateOrderAdjustmentDTO[]
|
||||||
cartId?: string
|
cartId?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export function prepareLineItemData(data: Input) {
|
export function prepareLineItemData(data: Input) {
|
||||||
const { variant, unitPrice, quantity, metadata, cartId } = data
|
const {
|
||||||
|
variant,
|
||||||
|
unitPrice,
|
||||||
|
quantity,
|
||||||
|
metadata,
|
||||||
|
cartId,
|
||||||
|
taxLines,
|
||||||
|
adjustments,
|
||||||
|
} = data
|
||||||
|
|
||||||
if (!variant.product) {
|
if (!variant.product) {
|
||||||
throw new Error("Variant does not have a product")
|
throw new Error("Variant does not have a product")
|
||||||
@@ -39,9 +54,37 @@ export function prepareLineItemData(data: Input) {
|
|||||||
metadata,
|
metadata,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (taxLines) {
|
||||||
|
lineItem.tax_lines = prepareTaxLinesData(taxLines)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (adjustments) {
|
||||||
|
lineItem.adjustments = prepareAdjustmentsData(adjustments)
|
||||||
|
}
|
||||||
|
|
||||||
if (cartId) {
|
if (cartId) {
|
||||||
lineItem.cart_id = cartId
|
lineItem.cart_id = cartId
|
||||||
}
|
}
|
||||||
|
|
||||||
return lineItem
|
return lineItem
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function prepareTaxLinesData(data: CreateOrderLineItemTaxLineDTO[]) {
|
||||||
|
return data.map((d) => ({
|
||||||
|
description: d.description,
|
||||||
|
tax_rate_id: d.tax_rate_id,
|
||||||
|
code: d.code,
|
||||||
|
rate: d.rate,
|
||||||
|
provider_id: d.provider_id,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
export function prepareAdjustmentsData(data: CreateOrderAdjustmentDTO[]) {
|
||||||
|
return data.map((d) => ({
|
||||||
|
code: d.code,
|
||||||
|
amount: d.amount,
|
||||||
|
description: d.description,
|
||||||
|
promotion_id: d.promotion_id,
|
||||||
|
provider_id: d.promotion_id,
|
||||||
|
}))
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import { CompleteCartWorkflowInputDTO, OrderDTO } from "@medusajs/types"
|
||||||
|
import { WorkflowData, createWorkflow } from "@medusajs/workflows-sdk"
|
||||||
|
import { useRemoteQueryStep } from "../../../common"
|
||||||
|
import { createOrderFromCartStep } from "../steps"
|
||||||
|
import { updateTaxLinesStep } from "../steps/update-tax-lines"
|
||||||
|
import { completeCartFields } from "../utils/fields"
|
||||||
|
|
||||||
|
/*
|
||||||
|
- [] Create Tax Lines
|
||||||
|
- [] Authorize Payment
|
||||||
|
- fail:
|
||||||
|
- [] Delete Tax lines
|
||||||
|
- [] Reserve Item from inventory (if enabled)
|
||||||
|
- fail:
|
||||||
|
- [] Delete reservations
|
||||||
|
- [] Cancel Payment
|
||||||
|
- [] Create order
|
||||||
|
*/
|
||||||
|
export const completeCartWorkflowId = "complete-cart"
|
||||||
|
export const completeCartWorkflow = createWorkflow(
|
||||||
|
completeCartWorkflowId,
|
||||||
|
(
|
||||||
|
input: WorkflowData<CompleteCartWorkflowInputDTO>
|
||||||
|
): WorkflowData<OrderDTO> => {
|
||||||
|
const cart = useRemoteQueryStep({
|
||||||
|
entry_point: "cart",
|
||||||
|
fields: completeCartFields,
|
||||||
|
variables: { id: input.id },
|
||||||
|
list: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
updateTaxLinesStep({ cart_or_cart_id: cart, force_tax_calculation: true })
|
||||||
|
|
||||||
|
const finalCart = useRemoteQueryStep({
|
||||||
|
entry_point: "cart",
|
||||||
|
fields: completeCartFields,
|
||||||
|
variables: { id: input.id },
|
||||||
|
list: false,
|
||||||
|
}).config({ name: "final-cart" })
|
||||||
|
|
||||||
|
return createOrderFromCartStep({ cart: finalCart })
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
export * from "./add-shipping-method-to-cart"
|
export * from "./add-shipping-method-to-cart"
|
||||||
export * from "./add-to-cart"
|
export * from "./add-to-cart"
|
||||||
|
export * from "./complete-cart"
|
||||||
export * from "./create-carts"
|
export * from "./create-carts"
|
||||||
export * from "./create-payment-collection-for-cart"
|
export * from "./create-payment-collection-for-cart"
|
||||||
export * from "./list-shipping-options-for-cart"
|
export * from "./list-shipping-options-for-cart"
|
||||||
|
|||||||
@@ -7,15 +7,13 @@ import {
|
|||||||
transform,
|
transform,
|
||||||
} from "@medusajs/workflows-sdk"
|
} from "@medusajs/workflows-sdk"
|
||||||
import { useRemoteQueryStep } from "../../common"
|
import { useRemoteQueryStep } from "../../common"
|
||||||
import {
|
import { confirmInventoryStep } from "../../definition/cart/steps/confirm-inventory"
|
||||||
confirmInventoryStep,
|
import { findOneOrAnyRegionStep } from "../../definition/cart/steps/find-one-or-any-region"
|
||||||
findOneOrAnyRegionStep,
|
import { findOrCreateCustomerStep } from "../../definition/cart/steps/find-or-create-customer"
|
||||||
findOrCreateCustomerStep,
|
import { findSalesChannelStep } from "../../definition/cart/steps/find-sales-channel"
|
||||||
findSalesChannelStep,
|
import { getVariantPriceSetsStep } from "../../definition/cart/steps/get-variant-price-sets"
|
||||||
getVariantPriceSetsStep,
|
import { getVariantsStep } from "../../definition/cart/steps/get-variants"
|
||||||
getVariantsStep,
|
import { validateVariantsExistStep } from "../../definition/cart/steps/validate-variants-existence"
|
||||||
validateVariantsExistStep,
|
|
||||||
} from "../../definition/cart"
|
|
||||||
import { prepareConfirmInventoryInput } from "../../definition/cart/utils/prepare-confirm-inventory-input"
|
import { prepareConfirmInventoryInput } from "../../definition/cart/utils/prepare-confirm-inventory-input"
|
||||||
import { prepareLineItemData } from "../../definition/cart/utils/prepare-line-item-data"
|
import { prepareLineItemData } from "../../definition/cart/utils/prepare-line-item-data"
|
||||||
import { createOrdersStep, updateOrderTaxLinesStep } from "../steps"
|
import { createOrdersStep, updateOrderTaxLinesStep } from "../steps"
|
||||||
@@ -174,6 +172,8 @@ export const createOrdersWorkflow = createWorkflow(
|
|||||||
),
|
),
|
||||||
quantity: item.quantity as number,
|
quantity: item.quantity as number,
|
||||||
metadata: item?.metadata ?? {},
|
metadata: item?.metadata ?? {},
|
||||||
|
taxLines: item.tax_lines || [],
|
||||||
|
adjustments: item.adjustments || [],
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
import { completeCartWorkflow } from "@medusajs/core-flows"
|
||||||
|
import { MedusaRequest, MedusaResponse } from "../../../../../types/routing"
|
||||||
|
import { refetchOrder } from "../../../orders/helpers"
|
||||||
|
import { StoreCompleteCartType } from "../../validators"
|
||||||
|
|
||||||
|
export const POST = async (
|
||||||
|
req: MedusaRequest<StoreCompleteCartType>,
|
||||||
|
res: MedusaResponse
|
||||||
|
) => {
|
||||||
|
const { idempotency_key: idempotencyKey } = req.validatedBody
|
||||||
|
|
||||||
|
// If the idempotencyKey is present:
|
||||||
|
// - is workflow is running?
|
||||||
|
// = throw error
|
||||||
|
// - else
|
||||||
|
// - re-run the workflow at the failed step
|
||||||
|
|
||||||
|
const { errors, result } = await completeCartWorkflow(req.scope).run({
|
||||||
|
input: { id: req.params.id },
|
||||||
|
throwOnError: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (Array.isArray(errors) && errors[0]) {
|
||||||
|
throw errors[0].error
|
||||||
|
}
|
||||||
|
|
||||||
|
const order = await refetchOrder(
|
||||||
|
result.id,
|
||||||
|
req.scope,
|
||||||
|
req.remoteQueryConfig.fields
|
||||||
|
)
|
||||||
|
|
||||||
|
res.status(200).json({ order })
|
||||||
|
}
|
||||||
@@ -2,12 +2,15 @@ import { MiddlewareRoute } from "../../../loaders/helpers/routing/types"
|
|||||||
import { authenticate } from "../../../utils/authenticate-middleware"
|
import { authenticate } from "../../../utils/authenticate-middleware"
|
||||||
import { validateAndTransformBody } from "../../utils/validate-body"
|
import { validateAndTransformBody } from "../../utils/validate-body"
|
||||||
import { validateAndTransformQuery } from "../../utils/validate-query"
|
import { validateAndTransformQuery } from "../../utils/validate-query"
|
||||||
|
import * as OrderQueryConfig from "../orders/query-config"
|
||||||
|
import { StoreGetOrder } from "../orders/validators"
|
||||||
import * as QueryConfig from "./query-config"
|
import * as QueryConfig from "./query-config"
|
||||||
import {
|
import {
|
||||||
StoreAddCartLineItem,
|
StoreAddCartLineItem,
|
||||||
StoreAddCartPromotions,
|
StoreAddCartPromotions,
|
||||||
StoreAddCartShippingMethods,
|
StoreAddCartShippingMethods,
|
||||||
StoreCalculateCartTaxes,
|
StoreCalculateCartTaxes,
|
||||||
|
StoreCompleteCart,
|
||||||
StoreCreateCart,
|
StoreCreateCart,
|
||||||
StoreGetCartsCart,
|
StoreGetCartsCart,
|
||||||
StoreRemoveCartPromotions,
|
StoreRemoveCartPromotions,
|
||||||
@@ -144,4 +147,15 @@ export const storeCartRoutesMiddlewares: MiddlewareRoute[] = [
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
method: ["POST"],
|
||||||
|
matcher: "/store/carts/:id/complete",
|
||||||
|
middlewares: [
|
||||||
|
validateAndTransformBody(StoreCompleteCart),
|
||||||
|
validateAndTransformQuery(
|
||||||
|
StoreGetOrder,
|
||||||
|
OrderQueryConfig.retrieveTransformQueryConfig
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
]
|
]
|
||||||
@@ -82,3 +82,10 @@ export const StoreAddCartShippingMethods = z
|
|||||||
data: z.record(z.unknown()).optional(),
|
data: z.record(z.unknown()).optional(),
|
||||||
})
|
})
|
||||||
.strict()
|
.strict()
|
||||||
|
|
||||||
|
export const StoreCompleteCart = z
|
||||||
|
.object({
|
||||||
|
idempotency_key: z.string().optional(),
|
||||||
|
})
|
||||||
|
.strict()
|
||||||
|
export type StoreCompleteCartType = z.infer<typeof StoreCompleteCart>
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import { MedusaContainer } from "@medusajs/types"
|
||||||
|
import { refetchEntity } from "../../utils/refetch-entity"
|
||||||
|
|
||||||
|
export const refetchOrder = async (
|
||||||
|
idOrFilter: string | object,
|
||||||
|
scope: MedusaContainer,
|
||||||
|
fields: string[]
|
||||||
|
) => {
|
||||||
|
return await refetchEntity("order", idOrFilter, scope, fields)
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
// TODO: This is copied over from admin. Scope what fields and relations are allowed for store
|
||||||
|
|
||||||
|
export const defaultStoreOrderFields = [
|
||||||
|
"id",
|
||||||
|
"status",
|
||||||
|
"version",
|
||||||
|
"summary",
|
||||||
|
"metadata",
|
||||||
|
"created_at",
|
||||||
|
"updated_at",
|
||||||
|
]
|
||||||
|
|
||||||
|
export const defaultStoreRetrieveOrderFields = [
|
||||||
|
"id",
|
||||||
|
"status",
|
||||||
|
"version",
|
||||||
|
"summary",
|
||||||
|
"total",
|
||||||
|
"subtotal",
|
||||||
|
"tax_total",
|
||||||
|
"discount_total",
|
||||||
|
"discount_tax_total",
|
||||||
|
"original_total",
|
||||||
|
"original_tax_total",
|
||||||
|
"item_total",
|
||||||
|
"item_subtotal",
|
||||||
|
"item_tax_total",
|
||||||
|
"original_item_total",
|
||||||
|
"original_item_subtotal",
|
||||||
|
"original_item_tax_total",
|
||||||
|
"shipping_total",
|
||||||
|
"shipping_subtotal",
|
||||||
|
"shipping_tax_total",
|
||||||
|
"original_shipping_tax_total",
|
||||||
|
"original_shipping_tax_subtotal",
|
||||||
|
"original_shipping_total",
|
||||||
|
"created_at",
|
||||||
|
"updated_at",
|
||||||
|
"*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",
|
||||||
|
]
|
||||||
|
|
||||||
|
export const retrieveTransformQueryConfig = {
|
||||||
|
defaultFields: defaultStoreRetrieveOrderFields,
|
||||||
|
isList: false,
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import { z } from "zod"
|
||||||
|
import { createSelectParams } from "../../utils/validators"
|
||||||
|
|
||||||
|
export const StoreGetOrder = createSelectParams()
|
||||||
|
export type StoreGetOrderType = z.infer<typeof StoreGetOrder>
|
||||||
@@ -12,7 +12,7 @@ export interface PartialUpsertOrderItemDTO {
|
|||||||
return_dismissed_quantity?: BigNumberInput
|
return_dismissed_quantity?: BigNumberInput
|
||||||
written_off_quantity?: BigNumberInput
|
written_off_quantity?: BigNumberInput
|
||||||
|
|
||||||
metadata?: Record<string, unknown>
|
metadata?: Record<string, unknown> | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CreateOrderItemDTO extends PartialUpsertOrderItemDTO {
|
export interface CreateOrderItemDTO extends PartialUpsertOrderItemDTO {
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ export interface CreateOrderDTO {
|
|||||||
currency_code: string
|
currency_code: string
|
||||||
status?: OrderStatus
|
status?: OrderStatus
|
||||||
no_notification?: boolean
|
no_notification?: boolean
|
||||||
metadata?: Record<string, unknown>
|
metadata?: Record<string, unknown> | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UpdateOrderDTO {
|
export interface UpdateOrderDTO {
|
||||||
@@ -21,5 +21,5 @@ export interface UpdateOrderDTO {
|
|||||||
currency_code?: string
|
currency_code?: string
|
||||||
status?: OrderStatus
|
status?: OrderStatus
|
||||||
no_notification?: boolean
|
no_notification?: boolean
|
||||||
metadata?: Record<string, unknown>
|
metadata?: Record<string, unknown> | null
|
||||||
}
|
}
|
||||||
@@ -118,3 +118,7 @@ export interface ListShippingOptionsForCartWorkflowInputDTO {
|
|||||||
export interface PricedShippingOptionDTO extends ShippingOptionDTO {
|
export interface PricedShippingOptionDTO extends ShippingOptionDTO {
|
||||||
amount: BigNumberInput
|
amount: BigNumberInput
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface CompleteCartWorkflowInputDTO {
|
||||||
|
id: string
|
||||||
|
}
|
||||||
@@ -14,7 +14,7 @@ export interface UpsertOrderAddressDTO {
|
|||||||
province?: string
|
province?: string
|
||||||
postal_code?: string
|
postal_code?: string
|
||||||
phone?: string
|
phone?: string
|
||||||
metadata?: Record<string, unknown>
|
metadata?: Record<string, unknown> | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UpdateOrderAddressDTO extends UpsertOrderAddressDTO {
|
export interface UpdateOrderAddressDTO extends UpsertOrderAddressDTO {
|
||||||
@@ -41,7 +41,7 @@ export interface CreateOrderDTO {
|
|||||||
items?: CreateOrderLineItemDTO[]
|
items?: CreateOrderLineItemDTO[]
|
||||||
shipping_methods?: Omit<CreateOrderShippingMethodDTO, "order_id">[]
|
shipping_methods?: Omit<CreateOrderShippingMethodDTO, "order_id">[]
|
||||||
transactions?: Omit<CreateOrderTransactionDTO, "order_id">[]
|
transactions?: Omit<CreateOrderTransactionDTO, "order_id">[]
|
||||||
metadata?: Record<string, unknown>
|
metadata?: Record<string, unknown> | null
|
||||||
|
|
||||||
promo_codes?: string[]
|
promo_codes?: string[]
|
||||||
}
|
}
|
||||||
@@ -57,7 +57,7 @@ export interface UpdateOrderDTO {
|
|||||||
billing_address?: CreateOrderAddressDTO | UpdateOrderAddressDTO
|
billing_address?: CreateOrderAddressDTO | UpdateOrderAddressDTO
|
||||||
email?: string
|
email?: string
|
||||||
no_notification?: boolean
|
no_notification?: boolean
|
||||||
metadata?: Record<string, unknown>
|
metadata?: Record<string, unknown> | null
|
||||||
}
|
}
|
||||||
|
|
||||||
/** ORDER END */
|
/** ORDER END */
|
||||||
@@ -167,7 +167,7 @@ export interface CreateOrderLineItemDTO {
|
|||||||
tax_lines?: CreateOrderTaxLineDTO[]
|
tax_lines?: CreateOrderTaxLineDTO[]
|
||||||
adjustments?: CreateOrderAdjustmentDTO[]
|
adjustments?: CreateOrderAdjustmentDTO[]
|
||||||
|
|
||||||
metadata?: Record<string, unknown>
|
metadata?: Record<string, unknown> | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CreateOrderLineItemForOrderDTO extends CreateOrderLineItemDTO {
|
export interface CreateOrderLineItemForOrderDTO extends CreateOrderLineItemDTO {
|
||||||
@@ -248,7 +248,7 @@ export interface CreateOrderChangeDTO {
|
|||||||
requested_by?: string
|
requested_by?: string
|
||||||
requested_at?: Date
|
requested_at?: Date
|
||||||
created_by?: string
|
created_by?: string
|
||||||
metadata?: Record<string, unknown>
|
metadata?: Record<string, unknown> | null
|
||||||
actions?: CreateOrderChangeActionDTO[]
|
actions?: CreateOrderChangeActionDTO[]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -265,25 +265,25 @@ export interface UpdateOrderChangeDTO {
|
|||||||
declined_reason?: string
|
declined_reason?: string
|
||||||
declined_at?: Date
|
declined_at?: Date
|
||||||
canceled_by?: string
|
canceled_by?: string
|
||||||
metadata?: Record<string, unknown>
|
metadata?: Record<string, unknown> | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CancelOrderChangeDTO {
|
export interface CancelOrderChangeDTO {
|
||||||
id: string
|
id: string
|
||||||
canceled_by?: string
|
canceled_by?: string
|
||||||
metadata?: Record<string, unknown>
|
metadata?: Record<string, unknown> | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DeclineOrderChangeDTO {
|
export interface DeclineOrderChangeDTO {
|
||||||
id: string
|
id: string
|
||||||
declined_by?: string
|
declined_by?: string
|
||||||
metadata?: Record<string, unknown>
|
metadata?: Record<string, unknown> | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ConfirmOrderChangeDTO {
|
export interface ConfirmOrderChangeDTO {
|
||||||
id: string
|
id: string
|
||||||
confirmed_by?: string
|
confirmed_by?: string
|
||||||
metadata?: Record<string, unknown>
|
metadata?: Record<string, unknown> | null
|
||||||
}
|
}
|
||||||
|
|
||||||
/** ORDER CHANGE END */
|
/** ORDER CHANGE END */
|
||||||
@@ -314,7 +314,7 @@ export interface CreateOrderTransactionDTO {
|
|||||||
currency_code: string
|
currency_code: string
|
||||||
reference?: string
|
reference?: string
|
||||||
reference_id?: string
|
reference_id?: string
|
||||||
metadata?: Record<string, unknown>
|
metadata?: Record<string, unknown> | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UpdateOrderTransactionDTO {
|
export interface UpdateOrderTransactionDTO {
|
||||||
@@ -323,7 +323,7 @@ export interface UpdateOrderTransactionDTO {
|
|||||||
currency_code?: string
|
currency_code?: string
|
||||||
reference?: string
|
reference?: string
|
||||||
reference_id?: string
|
reference_id?: string
|
||||||
metadata?: Record<string, unknown>
|
metadata?: Record<string, unknown> | null
|
||||||
}
|
}
|
||||||
|
|
||||||
/** ORDER TRANSACTION END */
|
/** ORDER TRANSACTION END */
|
||||||
@@ -340,7 +340,7 @@ export interface UpdateOrderItemDTO {
|
|||||||
return_received_quantity?: BigNumberInput
|
return_received_quantity?: BigNumberInput
|
||||||
return_dismissed_quantity?: BigNumberInput
|
return_dismissed_quantity?: BigNumberInput
|
||||||
written_off_quantity?: BigNumberInput
|
written_off_quantity?: BigNumberInput
|
||||||
metadata?: Record<string, unknown>
|
metadata?: Record<string, unknown> | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UpdateOrderItemWithSelectorDTO {
|
export interface UpdateOrderItemWithSelectorDTO {
|
||||||
@@ -363,9 +363,9 @@ export interface RegisterOrderFulfillmentDTO {
|
|||||||
id: string
|
id: string
|
||||||
quantity: BigNumberInput
|
quantity: BigNumberInput
|
||||||
internal_note?: string
|
internal_note?: string
|
||||||
metadata?: Record<string, unknown>
|
metadata?: Record<string, unknown> | null
|
||||||
}[]
|
}[]
|
||||||
metadata?: Record<string, unknown>
|
metadata?: Record<string, unknown> | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RegisterOrderShipmentDTO {
|
export interface RegisterOrderShipmentDTO {
|
||||||
@@ -379,9 +379,9 @@ export interface RegisterOrderShipmentDTO {
|
|||||||
id: string
|
id: string
|
||||||
quantity: BigNumberInput
|
quantity: BigNumberInput
|
||||||
internal_note?: string
|
internal_note?: string
|
||||||
metadata?: Record<string, unknown>
|
metadata?: Record<string, unknown> | null
|
||||||
}[]
|
}[]
|
||||||
metadata?: Record<string, unknown>
|
metadata?: Record<string, unknown> | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CreateOrderReturnDTO {
|
export interface CreateOrderReturnDTO {
|
||||||
@@ -395,9 +395,9 @@ export interface CreateOrderReturnDTO {
|
|||||||
id: string
|
id: string
|
||||||
quantity: BigNumberInput
|
quantity: BigNumberInput
|
||||||
internal_note?: string
|
internal_note?: string
|
||||||
metadata?: Record<string, unknown>
|
metadata?: Record<string, unknown> | null
|
||||||
}[]
|
}[]
|
||||||
metadata?: Record<string, unknown>
|
metadata?: Record<string, unknown> | null
|
||||||
}
|
}
|
||||||
|
|
||||||
/** ORDER bundled action flows */
|
/** ORDER bundled action flows */
|
||||||
Reference in new issue
Block a user