Feat: draft order api (#6797)
This commit is contained in:
committed by
GitHub
parent
883a75c4f3
commit
df0751f122
@@ -0,0 +1,2 @@
|
||||
export * from "./steps/remove-remote-links"
|
||||
export * from "./steps/use-remote-query"
|
||||
@@ -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[]
|
||||
|
||||
@@ -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[]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<ITaxModuleService>(
|
||||
ModuleRegistrationName.TAX
|
||||
)
|
||||
|
||||
@@ -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,
|
||||
})
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { ProductVariantDTO } from "@medusajs/types"
|
||||
import { BigNumberInput, ProductVariantDTO } from "@medusajs/types"
|
||||
|
||||
interface Input {
|
||||
quantity: number
|
||||
quantity: BigNumberInput
|
||||
metadata?: Record<string, any>
|
||||
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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from "./steps"
|
||||
export * from "./workflows"
|
||||
@@ -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<IOrderModuleService>(
|
||||
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<IOrderModuleService>(
|
||||
ModuleRegistrationName.ORDER
|
||||
)
|
||||
|
||||
await service.delete(createdIds)
|
||||
}
|
||||
)
|
||||
@@ -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<ITaxModuleService>(
|
||||
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)
|
||||
}
|
||||
)
|
||||
@@ -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"
|
||||
@@ -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<IOrderModuleService>(
|
||||
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<IOrderModuleService>(
|
||||
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,
|
||||
}))
|
||||
}
|
||||
@@ -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,
|
||||
})
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,39 @@
|
||||
import { BigNumberInput } from "@medusajs/types"
|
||||
|
||||
interface Input {
|
||||
quantity: BigNumberInput
|
||||
metadata?: Record<string, any>
|
||||
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<string, any>
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
@@ -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<CreateOrderDTO>): WorkflowData<OrderDTO> => {
|
||||
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
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from "./create-orders"
|
||||
export * from "./update-tax-lines"
|
||||
@@ -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<WorkflowInput>): WorkflowData<void> => {
|
||||
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,
|
||||
})
|
||||
}
|
||||
)
|
||||
Reference in New Issue
Block a user