Feat: draft order api (#6797)

This commit is contained in:
Carlos R. L. Rodrigues
2024-04-06 16:35:10 +02:00
committed by GitHub
parent 883a75c4f3
commit df0751f122
63 changed files with 2179 additions and 1424 deletions
@@ -0,0 +1,375 @@
import { ModuleRegistrationName, Modules } from "@medusajs/modules-sdk"
import {
ICartModuleService,
ICustomerModuleService,
IFulfillmentModuleService,
IInventoryServiceNext,
IPaymentModuleService,
IPricingModuleService,
IProductModuleService,
IRegionModuleService,
ISalesChannelModuleService,
IStockLocationServiceNext,
ITaxModuleService,
} from "@medusajs/types"
import { ContainerRegistrationKeys } from "@medusajs/utils"
import { medusaIntegrationTestRunner } from "medusa-test-utils"
import {
adminHeaders,
createAdminUser,
} from "../../../helpers/create-admin-user"
import { setupTaxStructure } from "../fixtures"
jest.setTimeout(50000)
const env = { MEDUSA_FF_MEDUSA_V2: true }
medusaIntegrationTestRunner({
debug: true,
env,
testSuite: ({ dbConnection, getContainer, api }) => {
let appContainer
let cartModuleService: ICartModuleService
let regionModuleService: IRegionModuleService
let scModuleService: ISalesChannelModuleService
let customerModule: ICustomerModuleService
let productModule: IProductModuleService
let pricingModule: IPricingModuleService
let paymentModule: IPaymentModuleService
let inventoryModule: IInventoryServiceNext
let stockLocationModule: IStockLocationServiceNext
let fulfillmentModule: IFulfillmentModuleService
let locationModule: IStockLocationServiceNext
let taxModule: ITaxModuleService
let remoteLink, remoteQuery
beforeAll(async () => {
appContainer = getContainer()
cartModuleService = appContainer.resolve(ModuleRegistrationName.CART)
regionModuleService = appContainer.resolve(ModuleRegistrationName.REGION)
scModuleService = appContainer.resolve(
ModuleRegistrationName.SALES_CHANNEL
)
customerModule = appContainer.resolve(ModuleRegistrationName.CUSTOMER)
productModule = appContainer.resolve(ModuleRegistrationName.PRODUCT)
pricingModule = appContainer.resolve(ModuleRegistrationName.PRICING)
paymentModule = appContainer.resolve(ModuleRegistrationName.PAYMENT)
inventoryModule = appContainer.resolve(ModuleRegistrationName.INVENTORY)
stockLocationModule = appContainer.resolve(
ModuleRegistrationName.STOCK_LOCATION
)
fulfillmentModule = appContainer.resolve(
ModuleRegistrationName.FULFILLMENT
)
locationModule = appContainer.resolve(
ModuleRegistrationName.STOCK_LOCATION
)
taxModule = appContainer.resolve(ModuleRegistrationName.TAX)
remoteLink = appContainer.resolve(ContainerRegistrationKeys.REMOTE_LINK)
remoteQuery = appContainer.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
})
beforeEach(async () => {
await createAdminUser(dbConnection, adminHeaders, appContainer)
})
describe("Draft Orders - Admin", () => {
it("should create a draft order", async () => {
const region = await regionModuleService.create({
name: "US",
currency_code: "usd",
})
const salesChannel = await scModuleService.create({
name: "Webshop",
})
const location = await stockLocationModule.create({
name: "Warehouse",
})
const [product, product_2] = await productModule.create([
{
title: "Test product",
variants: [
{
title: "Test variant",
},
],
},
{
title: "Another product",
variants: [
{
title: "Variant variable",
manage_inventory: false,
},
],
},
])
const inventoryItem = await inventoryModule.create({
sku: "inv-1234",
})
await inventoryModule.createInventoryLevels([
{
inventory_item_id: inventoryItem.id,
location_id: location.id,
stocked_quantity: 2,
reserved_quantity: 0,
},
])
const [priceSet, priceSet_2] = await pricingModule.create([
{
prices: [
{
amount: 3000,
currency_code: "usd",
},
],
},
{
prices: [
{
amount: 1000,
currency_code: "usd",
},
],
},
])
await remoteLink.create([
{
[Modules.PRODUCT]: {
variant_id: product.variants[0].id,
},
[Modules.PRICING]: {
price_set_id: priceSet.id,
},
},
{
[Modules.PRODUCT]: {
variant_id: product_2.variants[0].id,
},
[Modules.PRICING]: {
price_set_id: priceSet_2.id,
},
},
{
[Modules.SALES_CHANNEL]: {
sales_channel_id: salesChannel.id,
},
[Modules.STOCK_LOCATION]: {
stock_location_id: location.id,
},
},
{
[Modules.PRODUCT]: {
variant_id: product.variants[0].id,
},
[Modules.INVENTORY]: {
inventory_item_id: inventoryItem.id,
},
},
])
await setupTaxStructure(taxModule)
const payload = {
email: "oli@test.dk",
region_id: region.id,
sales_channel_id: salesChannel.id,
currency_code: "usd",
shipping_address: {
first_name: "Test",
last_name: "Test",
address_1: "Test",
city: "Test",
country_code: "US",
postal_code: "12345",
phone: "12345",
},
billing_address: {
first_name: "Test",
last_name: "Test",
address_1: "Test",
city: "Test",
country_code: "US",
postal_code: "12345",
},
promo_codes: ["testytest"],
items: [
{
variant_id: product.variants[0].id,
quantity: 2,
},
{
variant_id: product_2.variants[0].id,
unit_price: 200,
quantity: 1,
metadata: {
note: "reduced price",
},
},
{
title: "Custom Item",
sku: "sku123",
barcode: "barcode123",
unit_price: 2200,
quantity: 1,
},
],
shipping_methods: [
{
name: "test-method",
option_id: "test-option",
amount: 100,
},
],
}
const response = await api.post(
"/admin/draft-orders",
payload,
adminHeaders
)
expect(response.data).toEqual(
expect.objectContaining({
draft_order: expect.objectContaining({
status: "draft",
version: 1,
summary: {
total: 8400,
},
items: [
expect.objectContaining({
title: "Test variant",
subtitle: "Test product",
product_title: "Test product",
product_description: null,
product_subtitle: null,
product_type: null,
product_collection: null,
product_handle: "test-product",
variant_sku: null,
variant_barcode: null,
variant_title: "Test variant",
variant_option_values: null,
requires_shipping: true,
is_discountable: true,
is_tax_inclusive: false,
raw_compare_at_unit_price: null,
raw_unit_price: expect.objectContaining({
value: "3000",
}),
metadata: {},
tax_lines: [],
adjustments: [],
unit_price: 3000,
quantity: 2,
raw_quantity: expect.objectContaining({
value: "2",
}),
detail: expect.objectContaining({
raw_quantity: expect.objectContaining({
value: "2",
}),
raw_fulfilled_quantity: expect.objectContaining({
value: "0",
}),
raw_shipped_quantity: expect.objectContaining({
value: "0",
}),
raw_return_requested_quantity: expect.objectContaining({
value: "0",
}),
raw_return_received_quantity: expect.objectContaining({
value: "0",
}),
raw_return_dismissed_quantity: expect.objectContaining({
value: "0",
}),
raw_written_off_quantity: expect.objectContaining({
value: "0",
}),
quantity: 2,
fulfilled_quantity: 0,
shipped_quantity: 0,
return_requested_quantity: 0,
return_received_quantity: 0,
return_dismissed_quantity: 0,
written_off_quantity: 0,
}),
}),
expect.objectContaining({
title: "Variant variable",
subtitle: "Another product",
raw_unit_price: expect.objectContaining({
value: "200",
}),
metadata: {
note: "reduced price",
},
unit_price: 200,
quantity: 1,
raw_quantity: expect.objectContaining({
value: "1",
}),
}),
expect.objectContaining({
title: "Custom Item",
variant_sku: "sku123",
variant_barcode: "barcode123",
variant_title: "Custom Item",
raw_unit_price: expect.objectContaining({
value: "2200",
}),
unit_price: 2200,
quantity: 1,
raw_quantity: expect.objectContaining({
value: "1",
}),
}),
],
shipping_address: expect.objectContaining({
last_name: "Test",
address_1: "Test",
city: "Test",
country_code: "US",
postal_code: "12345",
phone: "12345",
}),
billing_address: expect.objectContaining({
first_name: "Test",
last_name: "Test",
address_1: "Test",
city: "Test",
country_code: "US",
postal_code: "12345",
}),
shipping_methods: [
expect.objectContaining({
name: "test-method",
raw_amount: expect.objectContaining({
value: "100",
}),
is_tax_inclusive: false,
shipping_option_id: null,
data: {},
tax_lines: [],
adjustments: [],
amount: 100,
}),
],
}),
})
)
expect(response.status).toEqual(200)
})
})
},
})
@@ -90,6 +90,7 @@ module.exports = {
[Modules.STORE]: true,
[Modules.TAX]: true,
[Modules.CURRENCY]: true,
[Modules.ORDER]: true,
[Modules.PAYMENT]: {
resolve: "@medusajs/payment",
/** @type {import('@medusajs/payment').PaymentModuleOptions}*/
+2
View File
@@ -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,
+12 -12
View File
@@ -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 {
+3 -1
View File
@@ -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(
+2
View File
@@ -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,
})
}
)
@@ -5,6 +5,9 @@ export * from "./cart-region"
export * from "./cart-sales-channel"
export * from "./fulfillment-set-location"
export * from "./inventory-level-stock-location"
export * from "./order-customer"
export * from "./order-promotion"
export * from "./order-region"
export * from "./order-sales-channel"
export * from "./product-sales-channel"
export * from "./product-shipping-profile"
@@ -15,4 +18,3 @@ export * from "./region-payment-provider"
export * from "./sales-channel-location"
export * from "./shipping-option-price-set"
export * from "./store-default-currency"
@@ -0,0 +1,28 @@
import { Modules } from "@medusajs/modules-sdk"
import { ModuleJoinerConfig } from "@medusajs/types"
export const OrderCustomer: ModuleJoinerConfig = {
isLink: true,
isReadOnlyLink: true,
extends: [
{
serviceName: Modules.ORDER,
relationship: {
serviceName: Modules.CUSTOMER,
primaryKey: "id",
foreignKey: "customer_id",
alias: "customer",
},
},
{
serviceName: Modules.CUSTOMER,
relationship: {
serviceName: Modules.ORDER,
primaryKey: "customer_id",
foreignKey: "id",
alias: "orders",
isList: true,
},
},
],
}
@@ -0,0 +1,55 @@
import { Modules } from "@medusajs/modules-sdk"
import { ModuleJoinerConfig } from "@medusajs/types"
import { LINKS } from "@medusajs/utils"
export const OrderPromotion: ModuleJoinerConfig = {
serviceName: LINKS.OrderPromotion,
isLink: true,
databaseConfig: {
tableName: "order_promotion",
idPrefix: "orderpromo",
},
alias: [
{
name: ["order_promotion", "order_promotions"],
args: {
entity: "LinkOrderPromotion",
},
},
],
primaryKeys: ["id", "order_id", "promotion_id"],
relationships: [
{
serviceName: Modules.ORDER,
primaryKey: "id",
foreignKey: "order_id",
alias: "order",
},
{
serviceName: Modules.PROMOTION,
primaryKey: "id",
foreignKey: "promotion_id",
alias: "promotion",
},
],
extends: [
{
serviceName: Modules.ORDER,
relationship: {
serviceName: LINKS.OrderPromotion,
primaryKey: "order_id",
foreignKey: "id",
alias: "order_link",
},
},
{
serviceName: Modules.PROMOTION,
relationship: {
serviceName: LINKS.OrderPromotion,
primaryKey: "promotion_id",
foreignKey: "id",
alias: "promotion_link",
},
},
],
}
@@ -0,0 +1,28 @@
import { Modules } from "@medusajs/modules-sdk"
import { ModuleJoinerConfig } from "@medusajs/types"
export const OrderRegion: ModuleJoinerConfig = {
isLink: true,
isReadOnlyLink: true,
extends: [
{
serviceName: Modules.ORDER,
relationship: {
serviceName: Modules.REGION,
primaryKey: "id",
foreignKey: "region_id",
alias: "region",
},
},
{
serviceName: Modules.REGION,
relationship: {
serviceName: Modules.ORDER,
primaryKey: "region_id",
foreignKey: "id",
alias: "orders",
isList: true,
},
},
],
}
@@ -1,4 +1,6 @@
import { ModuleJoinerConfig } from "@medusajs/types"
import { Modules } from "@medusajs/modules-sdk"
import { LINKS } from "@medusajs/utils"
export const OrderSalesChannel: ModuleJoinerConfig = {
@@ -19,7 +21,7 @@ export const OrderSalesChannel: ModuleJoinerConfig = {
primaryKeys: ["id", "order_id", "sales_channel_id"],
relationships: [
{
serviceName: "orderService",
serviceName: Modules.ORDER,
isInternalService: true,
primaryKey: "id",
foreignKey: "order_id",
@@ -35,7 +37,7 @@ export const OrderSalesChannel: ModuleJoinerConfig = {
],
extends: [
{
serviceName: "orderService",
serviceName: Modules.ORDER,
fieldAlias: {
sales_channel: "sales_channel_link.sales_channel",
},
@@ -0,0 +1,21 @@
import {
ContainerRegistrationKeys,
remoteQueryObjectFromString,
} from "@medusajs/utils"
import { MedusaRequest, MedusaResponse } from "../../../../types/routing"
import { defaultAdminOrderFields } from "../query-config"
export const GET = async (req: MedusaRequest, res: MedusaResponse) => {
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
const variables = { id: req.params.id }
const queryObject = remoteQueryObjectFromString({
entryPoint: "order",
variables,
fields: defaultAdminOrderFields,
})
const [draft_order] = await remoteQuery(queryObject)
res.status(200).json({ draft_order })
}
@@ -0,0 +1,48 @@
import { transformBody, transformQuery } from "../../../api/middlewares"
import { MiddlewareRoute } from "../../../loaders/helpers/routing/types"
import { authenticate } from "../../../utils/authenticate-middleware"
import * as QueryConfig from "./query-config"
import {
AdminGetOrdersOrderParams,
AdminGetOrdersParams,
AdminPostDraftOrdersReq,
} from "./validators"
export const adminDraftOrderRoutesMiddlewares: MiddlewareRoute[] = [
{
method: ["ALL"],
matcher: "/admin/draft-orders*",
middlewares: [authenticate("admin", ["bearer", "session", "api-key"])],
},
{
method: ["GET"],
matcher: "/admin/draft-orders",
middlewares: [
transformQuery(
AdminGetOrdersParams,
QueryConfig.listTransformQueryConfig
),
],
},
{
method: ["GET"],
matcher: "/admin/draft-orders/:id",
middlewares: [
transformQuery(
AdminGetOrdersOrderParams,
QueryConfig.retrieveTransformQueryConfig
),
],
},
{
method: ["POST"],
matcher: "/admin/draft-orders",
middlewares: [
transformBody(AdminPostDraftOrdersReq),
transformQuery(
AdminGetOrdersOrderParams,
QueryConfig.retrieveTransformQueryConfig
),
],
},
]
@@ -0,0 +1,57 @@
export const defaultAdminOrderRelations = [
"items",
"items.tax_lines",
"items.adjustments",
"items.detail",
"shipping_address",
"billing_address",
"shipping_methods",
"shipping_methods.tax_lines",
"shipping_methods.adjustments",
]
export const allowedAdminOrderRelations = []
export const defaultAdminListOrderFields = [
"id",
"status",
"version",
"*items",
"summary",
"metadata",
"created_at",
"updated_at",
]
export const defaultAdminOrderFields = [
"id",
"status",
"version",
"*items",
"*items.tax_lines",
"*items.adjustments",
"*items.detail",
"*items.tax_lines",
"*items.adjustments",
"*shipping_address",
"*billing_address",
"*shipping_methods",
"*shipping_methods.tax_lines",
"*shipping_methods.adjustments",
"summary",
"metadata",
"created_at",
"updated_at",
]
export const retrieveTransformQueryConfig = {
defaultFields: defaultAdminOrderFields,
defaultRelations: defaultAdminOrderRelations,
allowedRelations: allowedAdminOrderRelations,
isList: false,
}
export const listTransformQueryConfig = {
defaultFields: defaultAdminListOrderFields,
defaultLimit: 20,
isList: true,
}
@@ -0,0 +1,88 @@
import { createOrdersWorkflow } from "@medusajs/core-flows"
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
import {
ContainerRegistrationKeys,
OrderStatus,
remoteQueryObjectFromString,
} from "@medusajs/utils"
import {
AuthenticatedMedusaRequest,
MedusaRequest,
MedusaResponse,
} from "../../../types/routing"
import { AdminPostDraftOrdersReq } from "./validators"
export const GET = async (req: MedusaRequest, res: MedusaResponse) => {
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
const queryObject = remoteQueryObjectFromString({
entryPoint: "order",
variables: {
filters: {
...req.filterableFields,
is_draft_order: true,
},
...req.remoteQueryConfig.pagination,
},
fields: req.remoteQueryConfig.fields,
})
const { rows: draft_orders, metadata } = await remoteQuery(queryObject)
res.json({
draft_orders,
count: metadata.count,
offset: metadata.skip,
limit: metadata.take,
})
}
export const POST = async (
req: AuthenticatedMedusaRequest<AdminPostDraftOrdersReq>,
res: MedusaResponse
) => {
const input = req.validatedBody
const workflowInput = {
...input,
no_notification: !!input.no_notification_order,
status: OrderStatus.DRAFT,
is_draft_order: true,
}
if (!input.currency_code) {
const regionService = req.scope.resolve(ModuleRegistrationName.REGION)
const region = await regionService.retrieve(input.region_id)
input.currency_code = region.currency_code
}
if (!input.email) {
const customerService = req.scope.resolve(ModuleRegistrationName.CUSTOMER)
const customer = await customerService.retrieve(input.customer_id)
input.email = customer.email
}
const { result, errors } = await createOrdersWorkflow(req.scope).run({
input: workflowInput,
throwOnError: false,
})
if (Array.isArray(errors) && errors[0]) {
throw errors[0].error
}
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
const queryObject = remoteQueryObjectFromString({
entryPoint: "order",
variables: {
filters: {
id: result.id,
},
},
fields: req.remoteQueryConfig.fields,
})
const draftOrder = await remoteQuery(queryObject)
res.status(200).json({ draft_order: draftOrder[0] })
}
@@ -0,0 +1,174 @@
import { BigNumberInput } from "@medusajs/types"
import { Type } from "class-transformer"
import {
IsArray,
IsBoolean,
IsEmail,
IsEnum,
IsNotEmpty,
IsNumber,
IsObject,
IsOptional,
IsString,
ValidateIf,
ValidateNested,
} from "class-validator"
import {
AddressPayload,
FindParams,
extendedFindParamsMixin,
} from "../../../types/common"
import { IsType } from "../../../utils/validators/is-type"
export class AdminGetOrdersOrderParams extends FindParams {}
/**
* Parameters used to filter and configure the pagination of the retrieved api keys.
*/
export class AdminGetOrdersParams extends extendedFindParamsMixin({
limit: 50,
offset: 0,
}) {
/**
* Search parameter for api keys.
*/
@IsString({ each: true })
@IsOptional()
id?: string | string[]
/**
* Filter by title
*/
@IsString({ each: true })
@IsOptional()
name?: string | string[]
// Additional filters from BaseFilterable
@IsOptional()
@ValidateNested({ each: true })
@Type(() => AdminGetOrdersParams)
$and?: AdminGetOrdersParams[]
@IsOptional()
@ValidateNested({ each: true })
@Type(() => AdminGetOrdersParams)
$or?: AdminGetOrdersParams[]
}
enum Status {
completed = "completed",
}
export class AdminPostDraftOrdersReq {
@IsEnum(Status)
@IsOptional()
status?: string
@IsEmail()
@ValidateIf((o) => !o.customer_id)
email: string
@IsString()
@IsOptional()
sales_channel_id?: string
@IsOptional()
@IsType([AddressPayload, String])
billing_address?: AddressPayload
@IsOptional()
@IsType([AddressPayload, String])
shipping_address?: AddressPayload
@IsArray()
@Type(() => Item)
@IsNotEmpty()
@ValidateNested({ each: true })
@IsOptional()
items?: Item[]
@IsString()
region_id: string
@IsArray()
@IsOptional()
promo_codes?: string[]
@IsString()
@IsOptional()
currency_code?: string
@IsString()
@IsOptional()
@ValidateIf((o) => !o.email)
customer_id?: string
@IsBoolean()
@IsOptional()
no_notification_order?: boolean
@IsArray()
@Type(() => ShippingMethod)
@IsNotEmpty()
@ValidateNested({ each: true })
shipping_methods: ShippingMethod[]
@IsObject()
@IsOptional()
metadata?: Record<string, unknown> = {}
}
class ShippingMethod {
@IsString()
@IsOptional()
shipping_method_id: string
@IsString()
@IsOptional()
order_id: string
@IsString()
name: string
@IsString()
option_id: string
@IsObject()
@IsOptional()
data?: Record<string, unknown> = {}
@IsNumber()
amount: BigNumberInput
}
class Item {
@IsString()
@ValidateIf((o) => !o.variant_id)
title: string
@IsString()
@IsOptional()
@ValidateIf((o) => !o.variant_id)
sku: string
@IsString()
@IsOptional()
@ValidateIf((o) => !o.variant_id)
barcode: string
@IsNumber()
@IsOptional()
unit_price: BigNumberInput
@IsString()
@IsOptional()
variant_id?: string
@IsNumber()
quantity: number
@IsObject()
@IsOptional()
metadata?: Record<string, unknown> = {}
}
export class AdminDeleteOrdersOrderReq {}
@@ -1,12 +1,12 @@
import { deleteUsersWorkflow, updateUsersWorkflow } from "@medusajs/core-flows"
import { IUserModuleService, UpdateUserDTO } from "@medusajs/types"
import {
AuthenticatedMedusaRequest,
MedusaResponse,
} from "../../../../types/routing"
import { IUserModuleService, UpdateUserDTO } from "@medusajs/types"
import { deleteUsersWorkflow, updateUsersWorkflow } from "@medusajs/core-flows"
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
import { AdminUpdateUserRequest } from "../validators"
import { ModuleRegistrationName } from "../../../../../../modules-sdk/dist"
// Get user
export const GET = async (
@@ -5,6 +5,7 @@ import { adminCollectionRoutesMiddlewares } from "./admin/collections/middleware
import { adminCurrencyRoutesMiddlewares } from "./admin/currencies/middlewares"
import { adminCustomerGroupRoutesMiddlewares } from "./admin/customer-groups/middlewares"
import { adminCustomerRoutesMiddlewares } from "./admin/customers/middlewares"
import { adminDraftOrderRoutesMiddlewares } from "./admin/draft-orders/middlewares"
import { adminFulfillmentRoutesMiddlewares } from "./admin/fulfillment/middlewares"
import { adminInventoryRoutesMiddlewares } from "./admin/inventory-items/middlewares"
import { adminInviteRoutesMiddlewares } from "./admin/invites/middlewares"
@@ -59,6 +60,7 @@ export const config: MiddlewaresConfig = {
...adminCollectionRoutesMiddlewares,
...adminPricingRoutesMiddlewares,
...adminFulfillmentRoutesMiddlewares,
...adminDraftOrderRoutesMiddlewares,
...adminSalesChannelRoutesMiddlewares,
...adminStockLocationRoutesMiddlewares,
...adminProductTypeRoutesMiddlewares,
+2 -1
View File
@@ -5,11 +5,12 @@ import {
} from "@medusajs/types"
import { isObject, promiseAll, toPascalCase } from "@medusajs/utils"
import { Modules } from "./definitions"
import { MedusaModule } from "./medusa-module"
import { linkingErrorMessage } from "./utils/linking-error"
export type DeleteEntityInput = {
[moduleName: string]: { [linkableKey: string]: string | string[] }
[moduleName: string | Modules]: Record<string, string | string[]>
}
export type RestoreEntityInput = DeleteEntityInput
@@ -378,7 +378,7 @@ moduleIntegrationTestRunner({
})
})
describe("addLineItems", () => {
describe("createLineItems", () => {
it("should add a line item to order succesfully", async () => {
const [createdOrder] = await service.create([
{
@@ -386,7 +386,7 @@ moduleIntegrationTestRunner({
},
])
await service.addLineItems(createdOrder.id, [
await service.createLineItems(createdOrder.id, [
{
quantity: 1,
unit_price: 100,
@@ -415,7 +415,7 @@ moduleIntegrationTestRunner({
},
])
await service.addLineItems([
await service.createLineItems([
{
quantity: 1,
unit_price: 100,
@@ -466,7 +466,7 @@ moduleIntegrationTestRunner({
},
])
const items = await service.addLineItems([
const items = await service.createLineItems([
{
order_id: eurOrder.id,
quantity: 1,
@@ -502,7 +502,7 @@ moduleIntegrationTestRunner({
it("should throw if order does not exist", async () => {
const error = await service
.addLineItems("foo", [
.createLineItems("foo", [
{
quantity: 1,
unit_price: 100,
@@ -523,7 +523,7 @@ moduleIntegrationTestRunner({
])
const error = await service
.addLineItems(createdOrder.id, [
.createLineItems(createdOrder.id, [
{
unit_price: 10,
title: "test",
@@ -544,7 +544,7 @@ moduleIntegrationTestRunner({
])
const error = await service
.addLineItems([
.createLineItems([
{
order_id: createdOrder.id,
unit_price: 10,
@@ -567,7 +567,7 @@ moduleIntegrationTestRunner({
},
])
const [item] = await service.addLineItems(createdOrder.id, [
const [item] = await service.createLineItems(createdOrder.id, [
{
quantity: 1,
unit_price: 100,
@@ -595,7 +595,7 @@ moduleIntegrationTestRunner({
},
])
const [item] = await service.addLineItems(createdOrder.id, [
const [item] = await service.createLineItems(createdOrder.id, [
{
quantity: 1,
unit_price: 100,
@@ -620,7 +620,7 @@ moduleIntegrationTestRunner({
},
])
const items = await service.addLineItems(createdOrder.id, [
const items = await service.createLineItems(createdOrder.id, [
{
quantity: 1,
unit_price: 100,
@@ -686,15 +686,15 @@ moduleIntegrationTestRunner({
})
})
describe("removeLineItems", () => {
it("should remove a line item succesfully", async () => {
describe("deleteLineItems", () => {
it("should delete a line item succesfully", async () => {
const [createdOrder] = await service.create([
{
currency_code: "eur",
},
])
const [item] = await service.addLineItems(createdOrder.id, [
const [item] = await service.createLineItems(createdOrder.id, [
{
quantity: 1,
unit_price: 100,
@@ -705,7 +705,7 @@ moduleIntegrationTestRunner({
expect(item.title).toBe("test")
await service.removeLineItems([item.id])
await service.deleteLineItems([item.id])
const order = await service.retrieve(createdOrder.id, {
relations: ["items"],
@@ -714,14 +714,14 @@ moduleIntegrationTestRunner({
expect(order.items?.length).toBe(0)
})
it("should remove multiple line items succesfully", async () => {
it("should delete multiple line items succesfully", async () => {
const [createdOrder] = await service.create([
{
currency_code: "eur",
},
])
const [item, item2] = await service.addLineItems(createdOrder.id, [
const [item, item2] = await service.createLineItems(createdOrder.id, [
{
quantity: 1,
unit_price: 100,
@@ -734,7 +734,7 @@ moduleIntegrationTestRunner({
},
])
await service.removeLineItems([item.id, item2.id])
await service.deleteLineItems([item.id, item2.id])
const order = await service.retrieve(createdOrder.id, {
relations: ["items"],
@@ -744,7 +744,7 @@ moduleIntegrationTestRunner({
})
})
describe("addShippingMethods", () => {
describe("createShippingMethods", () => {
it("should add a shipping method to order succesfully", async () => {
const [createdOrder] = await service.create([
{
@@ -752,12 +752,15 @@ moduleIntegrationTestRunner({
},
])
const [method] = await service.addShippingMethods(createdOrder.id, [
{
amount: 100,
name: "Test",
},
])
const [method] = await service.createShippingMethods(
createdOrder.id,
[
{
amount: 100,
name: "Test",
},
]
)
const order = await service.retrieve(createdOrder.id, {
relations: ["shipping_methods"],
@@ -779,7 +782,7 @@ moduleIntegrationTestRunner({
},
])
const methods = await service.addShippingMethods([
const methods = await service.createShippingMethods([
{
order_id: eurOrder.id,
amount: 100,
@@ -811,24 +814,27 @@ moduleIntegrationTestRunner({
})
})
describe("removeShippingMethods", () => {
it("should remove a line item succesfully", async () => {
describe("deleteShippingMethods", () => {
it("should delete a line item succesfully", async () => {
const [createdOrder] = await service.create([
{
currency_code: "eur",
},
])
const [method] = await service.addShippingMethods(createdOrder.id, [
{
amount: 100,
name: "test",
},
])
const [method] = await service.createShippingMethods(
createdOrder.id,
[
{
amount: 100,
name: "test",
},
]
)
expect(method.id).not.toBe(null)
await service.removeShippingMethods(method.id)
await service.deleteShippingMethods(method.id)
const order = await service.retrieve(createdOrder.id, {
relations: ["shipping_methods"],
@@ -846,7 +852,7 @@ moduleIntegrationTestRunner({
},
])
const [itemOne] = await service.addLineItems(createdOrder.id, [
const [itemOne] = await service.createLineItems(createdOrder.id, [
{
quantity: 1,
unit_price: 100,
@@ -854,7 +860,7 @@ moduleIntegrationTestRunner({
},
])
const [itemTwo] = await service.addLineItems(createdOrder.id, [
const [itemTwo] = await service.createLineItems(createdOrder.id, [
{
quantity: 2,
unit_price: 200,
@@ -901,7 +907,7 @@ moduleIntegrationTestRunner({
},
])
const [itemOne] = await service.addLineItems(createdOrder.id, [
const [itemOne] = await service.createLineItems(createdOrder.id, [
{
quantity: 1,
unit_price: 100,
@@ -961,14 +967,14 @@ moduleIntegrationTestRunner({
expect(order.items[0].adjustments?.length).toBe(1)
})
it("should remove all line item adjustments for an order", async () => {
it("should delete all line item adjustments for an order", async () => {
const [createdOrder] = await service.create([
{
currency_code: "eur",
},
])
const [itemOne] = await service.addLineItems(createdOrder.id, [
const [itemOne] = await service.createLineItems(createdOrder.id, [
{
quantity: 1,
unit_price: 100,
@@ -1023,7 +1029,7 @@ moduleIntegrationTestRunner({
},
])
const [itemOne] = await service.addLineItems(createdOrder.id, [
const [itemOne] = await service.createLineItems(createdOrder.id, [
{
quantity: 1,
unit_price: 100,
@@ -1086,7 +1092,7 @@ moduleIntegrationTestRunner({
})
})
describe("addLineItemAdjustments", () => {
describe("createLineItemAdjustments", () => {
it("should add line item adjustments for items in an order", async () => {
const [createdOrder] = await service.create([
{
@@ -1094,7 +1100,7 @@ moduleIntegrationTestRunner({
},
])
const [itemOne] = await service.addLineItems(createdOrder.id, [
const [itemOne] = await service.createLineItems(createdOrder.id, [
{
quantity: 1,
unit_price: 100,
@@ -1102,7 +1108,7 @@ moduleIntegrationTestRunner({
},
])
const adjustments = await service.addLineItemAdjustments(
const adjustments = await service.createLineItemAdjustments(
createdOrder.id,
[
{
@@ -1131,14 +1137,14 @@ moduleIntegrationTestRunner({
},
])
const [itemOne] = await service.addLineItems(createdOrder.id, [
const [itemOne] = await service.createLineItems(createdOrder.id, [
{
quantity: 1,
unit_price: 100,
title: "test",
},
])
const [itemTwo] = await service.addLineItems(createdOrder.id, [
const [itemTwo] = await service.createLineItems(createdOrder.id, [
{
quantity: 2,
unit_price: 200,
@@ -1146,7 +1152,7 @@ moduleIntegrationTestRunner({
},
])
const adjustments = await service.addLineItemAdjustments(
const adjustments = await service.createLineItemAdjustments(
createdOrder.id,
[
{
@@ -1188,14 +1194,14 @@ moduleIntegrationTestRunner({
},
])
const [itemOne] = await service.addLineItems(orderOne.id, [
const [itemOne] = await service.createLineItems(orderOne.id, [
{
quantity: 1,
unit_price: 100,
title: "test",
},
])
const [itemTwo] = await service.addLineItems(orderTwo.id, [
const [itemTwo] = await service.createLineItems(orderTwo.id, [
{
quantity: 2,
unit_price: 200,
@@ -1203,7 +1209,7 @@ moduleIntegrationTestRunner({
},
])
await service.addLineItemAdjustments([
await service.createLineItemAdjustments([
// item from order one
{
item_id: itemOne.id,
@@ -1255,15 +1261,15 @@ moduleIntegrationTestRunner({
})
})
describe("removeLineItemAdjustments", () => {
it("should remove a line item succesfully", async () => {
describe("deleteLineItemAdjustments", () => {
it("should delete a line item succesfully", async () => {
const [createdOrder] = await service.create([
{
currency_code: "eur",
},
])
const [item] = await service.addLineItems(createdOrder.id, [
const [item] = await service.createLineItems(createdOrder.id, [
{
quantity: 1,
unit_price: 100,
@@ -1271,7 +1277,7 @@ moduleIntegrationTestRunner({
},
])
const [adjustment] = await service.addLineItemAdjustments(
const [adjustment] = await service.createLineItemAdjustments(
createdOrder.id,
[
{
@@ -1283,7 +1289,7 @@ moduleIntegrationTestRunner({
expect(adjustment.item_id).toBe(item.id)
await service.removeLineItemAdjustments(adjustment.id)
await service.deleteLineItemAdjustments(adjustment.id)
const adjustments = await service.listLineItemAdjustments({
item_id: item.id,
@@ -1292,14 +1298,14 @@ moduleIntegrationTestRunner({
expect(adjustments?.length).toBe(0)
})
it("should remove a line item succesfully with selector", async () => {
it("should delete a line item succesfully with selector", async () => {
const [createdOrder] = await service.create([
{
currency_code: "eur",
},
])
const [item] = await service.addLineItems(createdOrder.id, [
const [item] = await service.createLineItems(createdOrder.id, [
{
quantity: 1,
unit_price: 100,
@@ -1307,7 +1313,7 @@ moduleIntegrationTestRunner({
},
])
const [adjustment] = await service.addLineItemAdjustments(
const [adjustment] = await service.createLineItemAdjustments(
createdOrder.id,
[
{
@@ -1319,7 +1325,7 @@ moduleIntegrationTestRunner({
expect(adjustment.item_id).toBe(item.id)
await service.removeLineItemAdjustments({ item_id: item.id })
await service.deleteLineItemAdjustments({ item_id: item.id })
const adjustments = await service.listLineItemAdjustments({
item_id: item.id,
@@ -1337,7 +1343,7 @@ moduleIntegrationTestRunner({
},
])
const [shippingMethodOne] = await service.addShippingMethods(
const [shippingMethodOne] = await service.createShippingMethods(
createdOrder.id,
[
{
@@ -1347,7 +1353,7 @@ moduleIntegrationTestRunner({
]
)
const [shippingMethodTwo] = await service.addShippingMethods(
const [shippingMethodTwo] = await service.createShippingMethods(
createdOrder.id,
[
{
@@ -1396,7 +1402,7 @@ moduleIntegrationTestRunner({
},
])
const [shippingMethodOne] = await service.addShippingMethods(
const [shippingMethodOne] = await service.createShippingMethods(
createdOrder.id,
[
{
@@ -1459,14 +1465,14 @@ moduleIntegrationTestRunner({
expect(order.shipping_methods?.[0].adjustments?.length).toBe(1)
})
it("should remove all shipping method adjustments for an order", async () => {
it("should delete all shipping method adjustments for an order", async () => {
const [createdOrder] = await service.create([
{
currency_code: "eur",
},
])
const [shippingMethodOne] = await service.addShippingMethods(
const [shippingMethodOne] = await service.createShippingMethods(
createdOrder.id,
[
{
@@ -1523,7 +1529,7 @@ moduleIntegrationTestRunner({
},
])
const [shippingMethodOne] = await service.addShippingMethods(
const [shippingMethodOne] = await service.createShippingMethods(
createdOrder.id,
[
{
@@ -1587,7 +1593,7 @@ moduleIntegrationTestRunner({
})
})
describe("addShippingMethodAdjustments", () => {
describe("createShippingMethodAdjustments", () => {
it("should add shipping method adjustments in an order", async () => {
const [createdOrder] = await service.create([
{
@@ -1595,7 +1601,7 @@ moduleIntegrationTestRunner({
},
])
const [shippingMethodOne] = await service.addShippingMethods(
const [shippingMethodOne] = await service.createShippingMethods(
createdOrder.id,
[
{
@@ -1605,7 +1611,7 @@ moduleIntegrationTestRunner({
]
)
const adjustments = await service.addShippingMethodAdjustments(
const adjustments = await service.createShippingMethodAdjustments(
createdOrder.id,
[
{
@@ -1634,7 +1640,7 @@ moduleIntegrationTestRunner({
},
])
const [shippingMethodOne] = await service.addShippingMethods(
const [shippingMethodOne] = await service.createShippingMethods(
createdOrder.id,
[
{
@@ -1643,7 +1649,7 @@ moduleIntegrationTestRunner({
},
]
)
const [shippingMethodTwo] = await service.addShippingMethods(
const [shippingMethodTwo] = await service.createShippingMethods(
createdOrder.id,
[
{
@@ -1653,7 +1659,7 @@ moduleIntegrationTestRunner({
]
)
const adjustments = await service.addShippingMethodAdjustments(
const adjustments = await service.createShippingMethodAdjustments(
createdOrder.id,
[
{
@@ -1697,7 +1703,7 @@ moduleIntegrationTestRunner({
},
])
const [shippingMethodOne] = await service.addShippingMethods(
const [shippingMethodOne] = await service.createShippingMethods(
orderOne.id,
[
{
@@ -1706,7 +1712,7 @@ moduleIntegrationTestRunner({
},
]
)
const [shippingMethodTwo] = await service.addShippingMethods(
const [shippingMethodTwo] = await service.createShippingMethods(
orderTwo.id,
[
{
@@ -1716,7 +1722,7 @@ moduleIntegrationTestRunner({
]
)
await service.addShippingMethodAdjustments([
await service.createShippingMethodAdjustments([
// item from order one
{
shipping_method_id: shippingMethodOne.id,
@@ -1782,7 +1788,7 @@ moduleIntegrationTestRunner({
},
])
const [shippingMethodOne] = await service.addShippingMethods(
const [shippingMethodOne] = await service.createShippingMethods(
orderOne.id,
[
{
@@ -1793,7 +1799,7 @@ moduleIntegrationTestRunner({
)
const error = await service
.addShippingMethodAdjustments(orderTwo.id, [
.createShippingMethodAdjustments(orderTwo.id, [
{
shipping_method_id: shippingMethodOne.id,
amount: 100,
@@ -1808,22 +1814,25 @@ moduleIntegrationTestRunner({
})
})
describe("removeShippingMethodAdjustments", () => {
it("should remove a shipping method succesfully", async () => {
describe("deleteShippingMethodAdjustments", () => {
it("should delete a shipping method succesfully", async () => {
const [createdOrder] = await service.create([
{
currency_code: "eur",
},
])
const [method] = await service.addShippingMethods(createdOrder.id, [
{
amount: 100,
name: "test",
},
])
const [method] = await service.createShippingMethods(
createdOrder.id,
[
{
amount: 100,
name: "test",
},
]
)
const [adjustment] = await service.addShippingMethodAdjustments(
const [adjustment] = await service.createShippingMethodAdjustments(
createdOrder.id,
[
{
@@ -1836,7 +1845,7 @@ moduleIntegrationTestRunner({
expect(adjustment.shipping_method_id).toBe(method.id)
await service.removeShippingMethodAdjustments(adjustment.id)
await service.deleteShippingMethodAdjustments(adjustment.id)
const adjustments = await service.listShippingMethodAdjustments({
shipping_method_id: method.id,
@@ -1845,14 +1854,14 @@ moduleIntegrationTestRunner({
expect(adjustments?.length).toBe(0)
})
it("should remove a shipping method succesfully with selector", async () => {
it("should delete a shipping method succesfully with selector", async () => {
const [createdOrder] = await service.create([
{
currency_code: "eur",
},
])
const [shippingMethod] = await service.addShippingMethods(
const [shippingMethod] = await service.createShippingMethods(
createdOrder.id,
[
{
@@ -1862,7 +1871,7 @@ moduleIntegrationTestRunner({
]
)
const [adjustment] = await service.addShippingMethodAdjustments(
const [adjustment] = await service.createShippingMethodAdjustments(
createdOrder.id,
[
{
@@ -1875,7 +1884,7 @@ moduleIntegrationTestRunner({
expect(adjustment.shipping_method_id).toBe(shippingMethod.id)
await service.removeShippingMethodAdjustments({
await service.deleteShippingMethodAdjustments({
shipping_method_id: shippingMethod.id,
})
@@ -1895,7 +1904,7 @@ moduleIntegrationTestRunner({
},
])
const [itemOne] = await service.addLineItems(createdOrder.id, [
const [itemOne] = await service.createLineItems(createdOrder.id, [
{
quantity: 1,
unit_price: 100,
@@ -1903,7 +1912,7 @@ moduleIntegrationTestRunner({
},
])
const [itemTwo] = await service.addLineItems(createdOrder.id, [
const [itemTwo] = await service.createLineItems(createdOrder.id, [
{
quantity: 2,
unit_price: 200,
@@ -1947,7 +1956,7 @@ moduleIntegrationTestRunner({
},
])
const [itemOne] = await service.addLineItems(createdOrder.id, [
const [itemOne] = await service.createLineItems(createdOrder.id, [
{
quantity: 1,
unit_price: 100,
@@ -2004,14 +2013,14 @@ moduleIntegrationTestRunner({
expect(order.items[0].tax_lines.length).toBe(1)
})
it("should remove all line item tax lines for an order", async () => {
it("should delete all line item tax lines for an order", async () => {
const [createdOrder] = await service.create([
{
currency_code: "eur",
},
])
const [itemOne] = await service.addLineItems(createdOrder.id, [
const [itemOne] = await service.createLineItems(createdOrder.id, [
{
quantity: 1,
unit_price: 100,
@@ -2063,7 +2072,7 @@ moduleIntegrationTestRunner({
},
])
const [itemOne] = await service.addLineItems(createdOrder.id, [
const [itemOne] = await service.createLineItems(createdOrder.id, [
{
quantity: 1,
unit_price: 100,
@@ -2122,14 +2131,14 @@ moduleIntegrationTestRunner({
expect(order.items[0].tax_lines.length).toBe(1)
})
it("should remove, update, and create line item tax lines for an order", async () => {
it("should delete, update, and create line item tax lines for an order", async () => {
const [createdOrder] = await service.create([
{
currency_code: "eur",
},
])
const [itemOne] = await service.addLineItems(createdOrder.id, [
const [itemOne] = await service.createLineItems(createdOrder.id, [
{
quantity: 1,
unit_price: 100,
@@ -2180,7 +2189,7 @@ moduleIntegrationTestRunner({
rate: 25,
code: "TX-2",
},
// remove: should remove the initial tax line for itemOne
// delete: should delete the initial tax line for itemOne
])
const order = await service.retrieve(createdOrder.id, {
@@ -2213,7 +2222,7 @@ moduleIntegrationTestRunner({
})
})
describe("addLineItemAdjustments", () => {
describe("createLineItemAdjustments", () => {
it("should add line item tax lines for items in an order", async () => {
const [createdOrder] = await service.create([
{
@@ -2221,7 +2230,7 @@ moduleIntegrationTestRunner({
},
])
const [itemOne] = await service.addLineItems(createdOrder.id, [
const [itemOne] = await service.createLineItems(createdOrder.id, [
{
quantity: 1,
unit_price: 100,
@@ -2229,13 +2238,16 @@ moduleIntegrationTestRunner({
},
])
const taxLines = await service.addLineItemTaxLines(createdOrder.id, [
{
item_id: itemOne.id,
rate: 20,
code: "TX",
},
])
const taxLines = await service.createLineItemTaxLines(
createdOrder.id,
[
{
item_id: itemOne.id,
rate: 20,
code: "TX",
},
]
)
expect(taxLines).toEqual(
expect.arrayContaining([
@@ -2255,14 +2267,14 @@ moduleIntegrationTestRunner({
},
])
const [itemOne] = await service.addLineItems(createdOrder.id, [
const [itemOne] = await service.createLineItems(createdOrder.id, [
{
quantity: 1,
unit_price: 100,
title: "test",
},
])
const [itemTwo] = await service.addLineItems(createdOrder.id, [
const [itemTwo] = await service.createLineItems(createdOrder.id, [
{
quantity: 2,
unit_price: 200,
@@ -2270,18 +2282,21 @@ moduleIntegrationTestRunner({
},
])
const taxLines = await service.addLineItemTaxLines(createdOrder.id, [
{
item_id: itemOne.id,
rate: 20,
code: "TX",
},
{
item_id: itemTwo.id,
rate: 20,
code: "TX",
},
])
const taxLines = await service.createLineItemTaxLines(
createdOrder.id,
[
{
item_id: itemOne.id,
rate: 20,
code: "TX",
},
{
item_id: itemTwo.id,
rate: 20,
code: "TX",
},
]
)
expect(taxLines).toEqual(
expect.arrayContaining([
@@ -2311,14 +2326,14 @@ moduleIntegrationTestRunner({
},
])
const [itemOne] = await service.addLineItems(orderOne.id, [
const [itemOne] = await service.createLineItems(orderOne.id, [
{
quantity: 1,
unit_price: 100,
title: "test",
},
])
const [itemTwo] = await service.addLineItems(orderTwo.id, [
const [itemTwo] = await service.createLineItems(orderTwo.id, [
{
quantity: 2,
unit_price: 200,
@@ -2326,7 +2341,7 @@ moduleIntegrationTestRunner({
},
])
await service.addLineItemTaxLines([
await service.createLineItemTaxLines([
// item from order one
{
item_id: itemOne.id,
@@ -2378,15 +2393,15 @@ moduleIntegrationTestRunner({
})
})
describe("removeLineItemAdjustments", () => {
it("should remove line item tax line succesfully", async () => {
describe("deleteLineItemAdjustments", () => {
it("should delete line item tax line succesfully", async () => {
const [createdOrder] = await service.create([
{
currency_code: "eur",
},
])
const [item] = await service.addLineItems(createdOrder.id, [
const [item] = await service.createLineItems(createdOrder.id, [
{
quantity: 1,
unit_price: 100,
@@ -2394,17 +2409,20 @@ moduleIntegrationTestRunner({
},
])
const [taxLine] = await service.addLineItemTaxLines(createdOrder.id, [
{
item_id: item.id,
rate: 20,
code: "TX",
},
])
const [taxLine] = await service.createLineItemTaxLines(
createdOrder.id,
[
{
item_id: item.id,
rate: 20,
code: "TX",
},
]
)
expect(taxLine.item_id).toBe(item.id)
await service.removeLineItemTaxLines(taxLine.id)
await service.deleteLineItemTaxLines(taxLine.id)
const taxLines = await service.listLineItemTaxLines({
item_id: item.id,
@@ -2413,14 +2431,14 @@ moduleIntegrationTestRunner({
expect(taxLines?.length).toBe(0)
})
it("should remove line item tax lines succesfully with selector", async () => {
it("should delete line item tax lines succesfully with selector", async () => {
const [createdOrder] = await service.create([
{
currency_code: "eur",
},
])
const [item] = await service.addLineItems(createdOrder.id, [
const [item] = await service.createLineItems(createdOrder.id, [
{
quantity: 1,
unit_price: 100,
@@ -2428,17 +2446,20 @@ moduleIntegrationTestRunner({
},
])
const [taxLine] = await service.addLineItemTaxLines(createdOrder.id, [
{
item_id: item.id,
rate: 20,
code: "TX",
},
])
const [taxLine] = await service.createLineItemTaxLines(
createdOrder.id,
[
{
item_id: item.id,
rate: 20,
code: "TX",
},
]
)
expect(taxLine.item_id).toBe(item.id)
await service.removeLineItemTaxLines({ item_id: item.id })
await service.deleteLineItemTaxLines({ item_id: item.id })
const taxLines = await service.listLineItemTaxLines({
item_id: item.id,
+12 -3
View File
@@ -1,10 +1,12 @@
import { Modules } from "@medusajs/modules-sdk"
import { ModuleJoinerConfig } from "@medusajs/types"
import { MapToConfig } from "@medusajs/utils"
import { LineItem } from "@models"
import Order from "./models/order"
export const LinkableKeys: Record<string, string> = {
order_id: "Order",
order_item_id: "OrderLineItem",
order_id: Order.name,
order_item_id: LineItem.name,
}
const entityLinkableKeysMap: MapToConfig = {}
@@ -22,5 +24,12 @@ export const joinerConfig: ModuleJoinerConfig = {
serviceName: Modules.ORDER,
primaryKeys: ["id"],
linkableKeys: LinkableKeys,
alias: [],
alias: [
{
name: ["order", "orders"],
args: {
entity: Order.name,
},
},
],
} as ModuleJoinerConfig
@@ -33,16 +33,8 @@ export class Migration20240219102530 extends Migration {
"customer_id" TEXT NULL,
"version" INTEGER NOT NULL DEFAULT 1,
"sales_channel_id" TEXT NULL,
"status" text check (
"status" IN (
'pending',
'completed',
'draft',
'archived',
'canceled',
'requires_action'
)
) NOT NULL DEFAULT 'pending',
"status" text NOT NULL,
"is_draft_order" BOOLEAN NOT NULL DEFAULT false,
"email" text NULL,
"currency_code" text NOT NULL,
"shipping_address_id" text NULL,
@@ -59,6 +51,28 @@ export class Migration20240219102530 extends Migration {
ALTER TABLE "order"
ADD COLUMN if NOT exists "deleted_at" timestamptz NULL;
ALTER TABLE "order"
ADD COLUMN if NOT exists "is_draft_order" BOOLEAN NOT NULL DEFAULT false;
ALTER TABLE "order"
ADD COLUMN if NOT exists "version" INTEGER NOT NULL DEFAULT 1;
ALTER TABLE "order" ALTER COLUMN status TYPE text;
DROP TYPE IF EXISTS order_status_enum CASCADE;
CREATE TYPE order_status_enum AS ENUM (
'pending',
'completed',
'draft',
'archived',
'canceled',
'requires_action'
);
ALTER TABLE "order" ALTER COLUMN status DROP DEFAULT;
ALTER TABLE "order" ALTER COLUMN status TYPE order_status_enum USING (status::text::order_status_enum);
ALTER TABLE "order" ALTER COLUMN status SET DEFAULT 'pending';
ALTER TABLE "order" DROP constraint if EXISTS "FK_6ff7e874f01b478c115fdd462eb" CASCADE;
ALTER TABLE "order" DROP constraint if EXISTS "FK_19b0c6293443d1b464f604c3316" CASCADE;
@@ -129,6 +143,10 @@ export class Migration20240219102530 extends Migration {
CREATE INDEX IF NOT EXISTS "IDX_order_deleted_at" ON "order" (
deleted_at
);
CREATE INDEX IF NOT EXISTS "IDX_order_is_draft_order" ON "order" (
is_draft_order
)
WHERE deleted_at IS NOT NULL;
@@ -284,6 +302,7 @@ export class Migration20240219102530 extends Migration {
"raw_compare_at_unit_price" JSONB NULL,
"unit_price" NUMERIC NOT NULL,
"raw_unit_price" JSONB NOT NULL,
"metadata" JSONB NULL,
"created_at" TIMESTAMPTZ NOT NULL DEFAULT Now(),
"updated_at" TIMESTAMPTZ NOT NULL DEFAULT Now(),
CONSTRAINT "order_line_item_pkey" PRIMARY KEY ("id")
@@ -423,14 +442,12 @@ export class Migration20240219102530 extends Migration {
ALTER TABLE if exists "order"
ADD CONSTRAINT "order_shipping_address_id_foreign" FOREIGN KEY ("shipping_address_id") REFERENCES "order_address" ("id") ON
UPDATE CASCADE ON
DELETE
SET NULL;
DELETE CASCADE;
ALTER TABLE if exists "order"
ADD CONSTRAINT "order_billing_address_id_foreign" FOREIGN KEY ("billing_address_id") REFERENCES "order_address" ("id") ON
UPDATE CASCADE ON
DELETE
SET NULL;
DELETE CASCADE;
ALTER TABLE if exists "order_change"
ADD CONSTRAINT "order_change_order_id_foreign" FOREIGN KEY ("order_id") REFERENCES "order" ("id") ON
@@ -2,13 +2,7 @@ import {
createPsqlIndexStatementHelper,
generateEntityId,
} from "@medusajs/utils"
import {
BeforeCreate,
Cascade,
Entity,
ManyToOne,
OnInit,
} from "@mikro-orm/core"
import { BeforeCreate, Entity, ManyToOne, OnInit } from "@mikro-orm/core"
import AdjustmentLine from "./adjustment-line"
import LineItem from "./line-item"
@@ -28,7 +22,7 @@ export default class LineItemAdjustment extends AdjustmentLine {
entity: () => LineItem,
columnType: "text",
fieldName: "item_id",
cascade: [Cascade.REMOVE],
onDelete: "cascade",
mapToPk: true,
})
@ItemIdIndex.MikroORMIndex()
+5 -2
View File
@@ -122,15 +122,18 @@ export default class LineItem {
raw_unit_price: BigNumberRawValue
@OneToMany(() => LineItemTaxLine, (taxLine) => taxLine.item, {
cascade: [Cascade.PERSIST],
cascade: [Cascade.PERSIST, "soft-remove" as Cascade],
})
tax_lines = new Collection<LineItemTaxLine>(this)
@OneToMany(() => LineItemAdjustment, (adjustment) => adjustment.item, {
cascade: [Cascade.PERSIST],
cascade: [Cascade.PERSIST, "soft-remove" as Cascade],
})
adjustments = new Collection<LineItemAdjustment>(this)
@Property({ columnType: "jsonb", nullable: true })
metadata: Record<string, unknown> | null = null
@Property({
onCreate: () => new Date(),
columnType: "timestamptz",
@@ -7,7 +7,6 @@ import {
} from "@medusajs/utils"
import {
BeforeCreate,
Cascade,
Entity,
ManyToOne,
OnInit,
@@ -50,7 +49,7 @@ export default class OrderChangeAction {
entity: () => Order,
columnType: "text",
fieldName: "order_id",
cascade: [Cascade.REMOVE],
onDelete: "cascade",
mapToPk: true,
nullable: true,
})
@@ -70,7 +69,7 @@ export default class OrderChangeAction {
entity: () => OrderChange,
columnType: "text",
fieldName: "order_change_id",
cascade: [Cascade.REMOVE],
onDelete: "cascade",
mapToPk: true,
nullable: true,
})
+2 -2
View File
@@ -49,7 +49,7 @@ export default class OrderChange {
entity: () => Order,
columnType: "text",
fieldName: "order_id",
cascade: [Cascade.REMOVE],
onDelete: "cascade",
mapToPk: true,
})
@OrderIdIndex.MikroORMIndex()
@@ -65,7 +65,7 @@ export default class OrderChange {
version: number
@OneToMany(() => OrderChangeAction, (action) => action.order_change, {
cascade: [Cascade.PERSIST],
cascade: [Cascade.PERSIST, "sotf-remove" as Cascade],
})
actions = new Collection<OrderChangeAction>(this)
+2 -6
View File
@@ -5,7 +5,6 @@ import {
} from "@medusajs/utils"
import {
BeforeCreate,
Cascade,
Entity,
ManyToOne,
OnInit,
@@ -55,14 +54,11 @@ export default class OrderSummary {
columnType: "text",
fieldName: "order_id",
mapToPk: true,
cascade: [Cascade.REMOVE],
onDelete: "cascade",
})
order_id: string
@ManyToOne({
entity: () => Order,
fieldName: "order_id",
cascade: [Cascade.REMOVE],
@ManyToOne(() => Order, {
persist: false,
})
order: Order
+12
View File
@@ -70,6 +70,12 @@ const BillingAddressIdIndex = createPsqlIndexStatementHelper({
where: "deleted_at IS NOT NULL",
})
const IsDraftOrderIndex = createPsqlIndexStatementHelper({
tableName: "order",
columns: "is_draft_order",
where: "deleted_at IS NOT NULL",
})
@Entity({ tableName: "order" })
export default class Order {
[OptionalProps]?: OptionalOrderProps
@@ -107,6 +113,12 @@ export default class Order {
@Enum({ items: () => OrderStatus, default: OrderStatus.PENDING })
status: OrderStatus
@Property({
columnType: "boolean",
})
@IsDraftOrderIndex.MikroORMIndex()
is_draft_order = false
@Property({ columnType: "text", nullable: true })
email: string | null = null
@@ -2,13 +2,7 @@ import {
createPsqlIndexStatementHelper,
generateEntityId,
} from "@medusajs/utils"
import {
BeforeCreate,
Cascade,
Entity,
ManyToOne,
OnInit,
} from "@mikro-orm/core"
import { BeforeCreate, Entity, ManyToOne, OnInit } from "@mikro-orm/core"
import AdjustmentLine from "./adjustment-line"
import ShippingMethod from "./shipping-method"
@@ -29,7 +23,7 @@ export default class ShippingMethodAdjustment extends AdjustmentLine {
columnType: "text",
fieldName: "shipping_method_id",
mapToPk: true,
cascade: [Cascade.REMOVE],
onDelete: "cascade",
})
@ShippingMethodIdIdIndex.MikroORMIndex()
shipping_method_id: string
@@ -2,13 +2,7 @@ import {
createPsqlIndexStatementHelper,
generateEntityId,
} from "@medusajs/utils"
import {
BeforeCreate,
Cascade,
Entity,
ManyToOne,
OnInit,
} from "@mikro-orm/core"
import { BeforeCreate, Entity, ManyToOne, OnInit } from "@mikro-orm/core"
import ShippingMethod from "./shipping-method"
import TaxLine from "./tax-line"
@@ -29,7 +23,7 @@ export default class ShippingMethodTaxLine extends TaxLine {
fieldName: "shipping_method_id",
columnType: "text",
mapToPk: true,
cascade: [Cascade.REMOVE],
onDelete: "cascade",
})
@ShippingMethodIdIdIndex.MikroORMIndex()
shipping_method_id: string
+2 -5
View File
@@ -46,15 +46,12 @@ export default class ShippingMethod {
columnType: "text",
fieldName: "order_id",
mapToPk: true,
cascade: [Cascade.REMOVE],
onDelete: "cascade",
})
@OrderIdIndex.MikroORMIndex()
order_id: string
@ManyToOne({
entity: () => Order,
fieldName: "order_id",
cascade: [Cascade.REMOVE],
@ManyToOne(() => Order, {
persist: false,
})
order: Order
+1 -2
View File
@@ -7,7 +7,6 @@ import {
} from "@medusajs/utils"
import {
BeforeCreate,
Cascade,
Entity,
ManyToOne,
OnInit,
@@ -45,7 +44,7 @@ export default class Transaction {
entity: () => Order,
columnType: "text",
fieldName: "order_id",
cascade: [Cascade.REMOVE],
onDelete: "cascade",
mapToPk: true,
})
@OrderIdIndex.MikroORMIndex()
@@ -1,7 +1,6 @@
import {
Context,
DAL,
FilterableLineItemTaxLineProps,
FindConfig,
InternalModuleDeclaration,
IOrderModuleService,
@@ -299,7 +298,7 @@ export default class OrderModuleService<
}
if (lineItemsToCreate.length) {
await this.addLineItemsBulk_(lineItemsToCreate, sharedContext)
await this.createLineItemsBulk_(lineItemsToCreate, sharedContext)
}
return createdOrders
@@ -314,7 +313,7 @@ export default class OrderModuleService<
sharedContext?: Context
): Promise<OrderTypes.OrderDTO>
async update(
selector: Partial<OrderTypes.OrderDTO>,
selector: Partial<OrderTypes.FilterableOrderProps>,
data: OrderTypes.UpdateOrderDTO,
sharedContext?: Context
): Promise<OrderTypes.OrderDTO[]>
@@ -324,7 +323,7 @@ export default class OrderModuleService<
dataOrIdOrSelector:
| OrderTypes.UpdateOrderDTO[]
| string
| Partial<OrderTypes.OrderDTO>,
| Partial<OrderTypes.FilterableOrderProps>,
data?: OrderTypes.UpdateOrderDTO,
@MedusaContext() sharedContext: Context = {}
): Promise<OrderTypes.OrderDTO[] | OrderTypes.OrderDTO> {
@@ -344,7 +343,7 @@ export default class OrderModuleService<
dataOrIdOrSelector:
| OrderTypes.UpdateOrderDTO[]
| string
| Partial<OrderTypes.OrderDTO>,
| Partial<OrderTypes.FilterableOrderProps>,
data?: OrderTypes.UpdateOrderDTO,
@MedusaContext() sharedContext: Context = {}
) {
@@ -377,20 +376,20 @@ export default class OrderModuleService<
return result
}
addLineItems(
createLineItems(
data: OrderTypes.CreateOrderLineItemForOrderDTO
): Promise<OrderTypes.OrderLineItemDTO[]>
addLineItems(
createLineItems(
data: OrderTypes.CreateOrderLineItemForOrderDTO[]
): Promise<OrderTypes.OrderLineItemDTO[]>
addLineItems(
createLineItems(
orderId: string,
items: OrderTypes.CreateOrderLineItemDTO[],
sharedContext?: Context
): Promise<OrderTypes.OrderLineItemDTO[]>
@InjectManager("baseRepository_")
async addLineItems(
async createLineItems(
orderIdOrData:
| string
| OrderTypes.CreateOrderLineItemForOrderDTO[]
@@ -402,7 +401,7 @@ export default class OrderModuleService<
): Promise<OrderTypes.OrderLineItemDTO[]> {
let items: LineItem[] = []
if (isString(orderIdOrData)) {
items = await this.addLineItems_(
items = await this.createLineItems_(
orderIdOrData,
data as OrderTypes.CreateOrderLineItemDTO[],
sharedContext
@@ -411,7 +410,7 @@ export default class OrderModuleService<
const data = Array.isArray(orderIdOrData)
? orderIdOrData
: [orderIdOrData]
items = await this.addLineItemsBulk_(data, sharedContext)
items = await this.createLineItemsBulk_(data, sharedContext)
}
return await this.baseRepository_.serialize<OrderTypes.OrderLineItemDTO[]>(
@@ -423,7 +422,7 @@ export default class OrderModuleService<
}
@InjectTransactionManager("baseRepository_")
protected async addLineItems_(
protected async createLineItems_(
orderId: string,
items: OrderTypes.CreateOrderLineItemDTO[],
@MedusaContext() sharedContext: Context = {}
@@ -442,11 +441,11 @@ export default class OrderModuleService<
}
})
return await this.addLineItemsBulk_(toUpdate, sharedContext)
return await this.createLineItemsBulk_(toUpdate, sharedContext)
}
@InjectTransactionManager("baseRepository_")
protected async addLineItemsBulk_(
protected async createLineItemsBulk_(
data: CreateOrderLineItemDTO[],
@MedusaContext() sharedContext: Context = {}
): Promise<LineItem[]> {
@@ -479,7 +478,7 @@ export default class OrderModuleService<
data: OrderTypes.UpdateOrderLineItemWithSelectorDTO[]
): Promise<OrderTypes.OrderLineItemDTO[]>
updateLineItems(
selector: Partial<OrderTypes.OrderLineItemDTO>,
selector: Partial<OrderTypes.FilterableOrderLineItemProps>,
data: OrderTypes.UpdateOrderLineItemDTO,
sharedContext?: Context
): Promise<OrderTypes.OrderLineItemDTO[]>
@@ -494,7 +493,7 @@ export default class OrderModuleService<
lineItemIdOrDataOrSelector:
| string
| OrderTypes.UpdateOrderLineItemWithSelectorDTO[]
| Partial<OrderTypes.OrderLineItemDTO>,
| Partial<OrderTypes.FilterableOrderLineItemProps>,
data?:
| OrderTypes.UpdateOrderLineItemDTO
| Partial<OrderTypes.UpdateOrderLineItemDTO>,
@@ -688,40 +687,6 @@ export default class OrderModuleService<
return await this.orderItemService_.update(toUpdate, sharedContext)
}
async removeLineItems(
itemIds: string[],
sharedContext?: Context
): Promise<void>
async removeLineItems(itemIds: string, sharedContext?: Context): Promise<void>
async removeLineItems(
selector: Partial<OrderTypes.OrderLineItemDTO>,
sharedContext?: Context
): Promise<void>
@InjectTransactionManager("baseRepository_")
async removeLineItems(
itemIdsOrSelector: string | string[] | Partial<OrderTypes.OrderLineItemDTO>,
@MedusaContext() sharedContext: Context = {}
): Promise<void> {
let toDelete: string[]
if (isObject(itemIdsOrSelector)) {
const items = await this.listLineItems(
{ ...itemIdsOrSelector } as Partial<OrderTypes.OrderLineItemDTO>,
{},
sharedContext
)
toDelete = items.map((item) => item.id)
} else {
toDelete = Array.isArray(itemIdsOrSelector)
? itemIdsOrSelector
: [itemIdsOrSelector]
}
await this.lineItemService_.delete(toDelete, sharedContext)
}
async createAddresses(
data: OrderTypes.CreateOrderAddressDTO,
sharedContext?: Context
@@ -794,20 +759,20 @@ export default class OrderModuleService<
return await this.addressService_.update(data, sharedContext)
}
async addShippingMethods(
async createShippingMethods(
data: OrderTypes.CreateOrderShippingMethodDTO
): Promise<OrderTypes.OrderShippingMethodDTO>
async addShippingMethods(
async createShippingMethods(
data: OrderTypes.CreateOrderShippingMethodDTO[]
): Promise<OrderTypes.OrderShippingMethodDTO[]>
async addShippingMethods(
async createShippingMethods(
orderId: string,
methods: OrderTypes.CreateOrderShippingMethodDTO[],
sharedContext?: Context
): Promise<OrderTypes.OrderShippingMethodDTO[]>
@InjectManager("baseRepository_")
async addShippingMethods(
async createShippingMethods(
orderIdOrData:
| string
| OrderTypes.CreateOrderShippingMethodDTO[]
@@ -819,7 +784,7 @@ export default class OrderModuleService<
> {
let methods: ShippingMethod[]
if (isString(orderIdOrData)) {
methods = await this.addShippingMethods_(
methods = await this.createShippingMethods_(
orderIdOrData,
data!,
sharedContext
@@ -828,7 +793,7 @@ export default class OrderModuleService<
const data = Array.isArray(orderIdOrData)
? orderIdOrData
: [orderIdOrData]
methods = await this.addShippingMethodsBulk_(
methods = await this.createShippingMethodsBulk_(
data as OrderTypes.CreateOrderShippingMethodDTO[],
sharedContext
)
@@ -840,7 +805,7 @@ export default class OrderModuleService<
}
@InjectTransactionManager("baseRepository_")
protected async addShippingMethods_(
protected async createShippingMethods_(
orderId: string,
data: CreateOrderShippingMethodDTO[],
@MedusaContext() sharedContext: Context = {}
@@ -859,11 +824,11 @@ export default class OrderModuleService<
}
})
return await this.addShippingMethodsBulk_(methods, sharedContext)
return await this.createShippingMethodsBulk_(methods, sharedContext)
}
@InjectTransactionManager("baseRepository_")
protected async addShippingMethodsBulk_(
protected async createShippingMethodsBulk_(
data: OrderTypes.CreateOrderShippingMethodDTO[],
@MedusaContext() sharedContext: Context = {}
): Promise<ShippingMethod[]> {
@@ -873,60 +838,20 @@ export default class OrderModuleService<
)
}
async removeShippingMethods(
methodIds: string[],
sharedContext?: Context
): Promise<void>
async removeShippingMethods(
methodIds: string,
sharedContext?: Context
): Promise<void>
async removeShippingMethods(
selector: Partial<OrderTypes.OrderShippingMethodDTO>,
sharedContext?: Context
): Promise<void>
@InjectTransactionManager("baseRepository_")
async removeShippingMethods(
methodIdsOrSelector:
| string
| string[]
| Partial<OrderTypes.OrderShippingMethodDTO>,
@MedusaContext() sharedContext: Context = {}
): Promise<void> {
let toDelete: string[]
if (isObject(methodIdsOrSelector)) {
const methods = await this.listShippingMethods(
{
...(methodIdsOrSelector as Partial<OrderTypes.OrderShippingMethodDTO>),
},
{},
sharedContext
)
toDelete = methods.map((m) => m.id)
} else {
toDelete = Array.isArray(methodIdsOrSelector)
? methodIdsOrSelector
: [methodIdsOrSelector]
}
await this.shippingMethodService_.delete(toDelete, sharedContext)
}
async addLineItemAdjustments(
async createLineItemAdjustments(
adjustments: OrderTypes.CreateOrderLineItemAdjustmentDTO[]
): Promise<OrderTypes.OrderLineItemAdjustmentDTO[]>
async addLineItemAdjustments(
async createLineItemAdjustments(
adjustment: OrderTypes.CreateOrderLineItemAdjustmentDTO
): Promise<OrderTypes.OrderLineItemAdjustmentDTO[]>
async addLineItemAdjustments(
async createLineItemAdjustments(
orderId: string,
adjustments: OrderTypes.CreateOrderLineItemAdjustmentDTO[],
sharedContext?: Context
): Promise<OrderTypes.OrderLineItemAdjustmentDTO[]>
@InjectTransactionManager("baseRepository_")
async addLineItemAdjustments(
async createLineItemAdjustments(
orderIdOrData:
| string
| OrderTypes.CreateOrderLineItemAdjustmentDTO[]
@@ -1026,46 +951,6 @@ export default class OrderModuleService<
})
}
async removeLineItemAdjustments(
adjustmentIds: string[],
sharedContext?: Context
): Promise<void>
async removeLineItemAdjustments(
adjustmentId: string,
sharedContext?: Context
): Promise<void>
async removeLineItemAdjustments(
selector: Partial<OrderTypes.OrderLineItemAdjustmentDTO>,
sharedContext?: Context
): Promise<void>
async removeLineItemAdjustments(
adjustmentIdsOrSelector:
| string
| string[]
| Partial<OrderTypes.OrderLineItemAdjustmentDTO>,
@MedusaContext() sharedContext: Context = {}
): Promise<void> {
let ids: string[]
if (isObject(adjustmentIdsOrSelector)) {
const adjustments = await this.listLineItemAdjustments(
{
...adjustmentIdsOrSelector,
} as Partial<OrderTypes.OrderLineItemAdjustmentDTO>,
{ select: ["id"] },
sharedContext
)
ids = adjustments.map((adj) => adj.id)
} else {
ids = Array.isArray(adjustmentIdsOrSelector)
? adjustmentIdsOrSelector
: [adjustmentIdsOrSelector]
}
await this.lineItemAdjustmentService_.delete(ids, sharedContext)
}
@InjectTransactionManager("baseRepository_")
async setShippingMethodAdjustments(
orderId: string,
@@ -1122,20 +1007,20 @@ export default class OrderModuleService<
})
}
async addShippingMethodAdjustments(
async createShippingMethodAdjustments(
adjustments: OrderTypes.CreateOrderShippingMethodAdjustmentDTO[]
): Promise<OrderTypes.OrderShippingMethodAdjustmentDTO[]>
async addShippingMethodAdjustments(
async createShippingMethodAdjustments(
adjustment: OrderTypes.CreateOrderShippingMethodAdjustmentDTO
): Promise<OrderTypes.OrderShippingMethodAdjustmentDTO>
async addShippingMethodAdjustments(
async createShippingMethodAdjustments(
orderId: string,
adjustments: OrderTypes.CreateOrderShippingMethodAdjustmentDTO[],
sharedContext?: Context
): Promise<OrderTypes.OrderShippingMethodAdjustmentDTO[]>
@InjectTransactionManager("baseRepository_")
async addShippingMethodAdjustments(
async createShippingMethodAdjustments(
orderIdOrData:
| string
| OrderTypes.CreateOrderShippingMethodAdjustmentDTO[]
@@ -1196,53 +1081,13 @@ export default class OrderModuleService<
})
}
async removeShippingMethodAdjustments(
adjustmentIds: string[],
sharedContext?: Context
): Promise<void>
async removeShippingMethodAdjustments(
adjustmentId: string,
sharedContext?: Context
): Promise<void>
async removeShippingMethodAdjustments(
selector: Partial<OrderTypes.OrderShippingMethodAdjustmentDTO>,
sharedContext?: Context
): Promise<void>
async removeShippingMethodAdjustments(
adjustmentIdsOrSelector:
| string
| string[]
| Partial<OrderTypes.OrderShippingMethodAdjustmentDTO>,
@MedusaContext() sharedContext: Context = {}
): Promise<void> {
let ids: string[]
if (isObject(adjustmentIdsOrSelector)) {
const adjustments = await this.listShippingMethodAdjustments(
{
...adjustmentIdsOrSelector,
} as Partial<OrderTypes.OrderShippingMethodAdjustmentDTO>,
{ select: ["id"] },
sharedContext
)
ids = adjustments.map((adj) => adj.id)
} else {
ids = Array.isArray(adjustmentIdsOrSelector)
? adjustmentIdsOrSelector
: [adjustmentIdsOrSelector]
}
await this.shippingMethodAdjustmentService_.delete(ids, sharedContext)
}
addLineItemTaxLines(
createLineItemTaxLines(
taxLines: OrderTypes.CreateOrderLineItemTaxLineDTO[]
): Promise<OrderTypes.OrderLineItemTaxLineDTO[]>
addLineItemTaxLines(
createLineItemTaxLines(
taxLine: OrderTypes.CreateOrderLineItemTaxLineDTO
): Promise<OrderTypes.OrderLineItemTaxLineDTO>
addLineItemTaxLines(
createLineItemTaxLines(
orderId: string,
taxLines:
| OrderTypes.CreateOrderLineItemTaxLineDTO[]
@@ -1251,7 +1096,7 @@ export default class OrderModuleService<
): Promise<OrderTypes.OrderLineItemTaxLineDTO[]>
@InjectTransactionManager("baseRepository_")
async addLineItemTaxLines(
async createLineItemTaxLines(
orderIdOrData:
| string
| OrderTypes.CreateOrderLineItemTaxLineDTO[]
@@ -1346,53 +1191,13 @@ export default class OrderModuleService<
})
}
removeLineItemTaxLines(
taxLineIds: string[],
sharedContext?: Context
): Promise<void>
removeLineItemTaxLines(
taxLineIds: string,
sharedContext?: Context
): Promise<void>
removeLineItemTaxLines(
selector: FilterableLineItemTaxLineProps,
sharedContext?: Context
): Promise<void>
async removeLineItemTaxLines(
taxLineIdsOrSelector:
| string
| string[]
| OrderTypes.FilterableOrderShippingMethodTaxLineProps,
@MedusaContext() sharedContext: Context = {}
): Promise<void> {
let ids: string[]
if (isObject(taxLineIdsOrSelector)) {
const taxLines = await this.listLineItemTaxLines(
{
...(taxLineIdsOrSelector as OrderTypes.FilterableOrderLineItemTaxLineProps),
},
{ select: ["id"] },
sharedContext
)
ids = taxLines.map((taxLine) => taxLine.id)
} else {
ids = Array.isArray(taxLineIdsOrSelector)
? taxLineIdsOrSelector
: [taxLineIdsOrSelector]
}
await this.lineItemTaxLineService_.delete(ids, sharedContext)
}
addShippingMethodTaxLines(
createShippingMethodTaxLines(
taxLines: OrderTypes.CreateOrderShippingMethodTaxLineDTO[]
): Promise<OrderTypes.OrderShippingMethodTaxLineDTO[]>
addShippingMethodTaxLines(
createShippingMethodTaxLines(
taxLine: OrderTypes.CreateOrderShippingMethodTaxLineDTO
): Promise<OrderTypes.OrderShippingMethodTaxLineDTO>
addShippingMethodTaxLines(
createShippingMethodTaxLines(
orderId: string,
taxLines:
| OrderTypes.CreateOrderShippingMethodTaxLineDTO[]
@@ -1401,7 +1206,7 @@ export default class OrderModuleService<
): Promise<OrderTypes.OrderShippingMethodTaxLineDTO[]>
@InjectTransactionManager("baseRepository_")
async addShippingMethodTaxLines(
async createShippingMethodTaxLines(
orderIdOrData:
| string
| OrderTypes.CreateOrderShippingMethodTaxLineDTO[]
@@ -1496,46 +1301,6 @@ export default class OrderModuleService<
})
}
removeShippingMethodTaxLines(
taxLineIds: string[],
sharedContext?: Context
): Promise<void>
removeShippingMethodTaxLines(
taxLineIds: string,
sharedContext?: Context
): Promise<void>
removeShippingMethodTaxLines(
selector: Partial<OrderTypes.OrderShippingMethodTaxLineDTO>,
sharedContext?: Context
): Promise<void>
async removeShippingMethodTaxLines(
taxLineIdsOrSelector:
| string
| string[]
| OrderTypes.FilterableOrderShippingMethodTaxLineProps,
@MedusaContext() sharedContext: Context = {}
): Promise<void> {
let ids: string[]
if (isObject(taxLineIdsOrSelector)) {
const taxLines = await this.listShippingMethodTaxLines(
{
...(taxLineIdsOrSelector as OrderTypes.FilterableOrderShippingMethodTaxLineProps),
},
{ select: ["id"] },
sharedContext
)
ids = taxLines.map((taxLine) => taxLine.id)
} else {
ids = Array.isArray(taxLineIdsOrSelector)
? taxLineIdsOrSelector
: [taxLineIdsOrSelector]
}
await this.shippingMethodTaxLineService_.delete(ids, sharedContext)
}
async createOrderChange(
data: OrderTypes.CreateOrderChangeDTO,
sharedContext?: Context
@@ -1877,8 +1642,23 @@ export default class OrderModuleService<
return orderChanges
}
async addOrderAction(
data: OrderTypes.CreateOrderChangeActionDTO,
sharedContext?: Context
): Promise<OrderTypes.OrderChangeActionDTO>
async addOrderAction(
data: OrderTypes.CreateOrderChangeActionDTO[],
sharedContext?: Context
): Promise<OrderTypes.OrderChangeActionDTO[]>
@InjectTransactionManager("baseRepository_")
async addOrderAction(data: any, sharedContext?: Context): Promise<any> {
async addOrderAction(
data:
| OrderTypes.CreateOrderChangeActionDTO
| OrderTypes.CreateOrderChangeActionDTO[],
sharedContext?: Context
): Promise<
OrderTypes.OrderChangeActionDTO | OrderTypes.OrderChangeActionDTO[]
> {
let dataArr = Array.isArray(data) ? data : [data]
const orderChangeMap = {}
@@ -1908,7 +1688,11 @@ export default class OrderModuleService<
}
}
return await this.orderChangeActionService_.create(dataArr, sharedContext)
const actions = (await this.orderChangeActionService_.create(
dataArr,
sharedContext
)) as OrderTypes.OrderChangeActionDTO[]
return Array.isArray(data) ? actions : actions[0]
}
private async applyOrderChanges_(
+2 -2
View File
@@ -2,7 +2,7 @@ import { BigNumberInput } from "@medusajs/types"
export interface CreateOrderShippingMethodDTO {
name: string
shipping_method_id: string
shipping_option_id?: string
order_id: string
version?: number
amount: BigNumberInput
@@ -11,7 +11,7 @@ export interface CreateOrderShippingMethodDTO {
export interface UpdateOrderShippingMethodDTO {
id: string
shipping_method_id: string
shipping_option_id?: string
name?: string
amount?: BigNumberInput
data?: Record<string, unknown>
+49 -21
View File
@@ -233,8 +233,7 @@ export interface OrderShippingMethodDTO {
/**
* The price of the shipping method
*/
unit_price: number
amount: BigNumberRawValue
/**
* Whether the shipping method price is tax inclusive or not
*/
@@ -416,6 +415,16 @@ export interface OrderLineItemDTO extends OrderLineItemTotalsDTO {
*/
raw_unit_price: BigNumberRawValue
/**
* The quantity of the order line item.
*/
quantity: number
/**
* The raw quantity of the order line item.
*/
raw_quantity: BigNumberRawValue
/**
* The associated tax lines.
*
@@ -443,6 +452,11 @@ export interface OrderLineItemDTO extends OrderLineItemTotalsDTO {
* The date when the order line item was last updated.
*/
updated_at: Date
/**
* Holds custom data in key-value pairs.
*/
metadata?: Record<string, unknown> | null
}
export interface OrderItemDTO {
@@ -738,11 +752,11 @@ export interface OrderChangeActionDTO {
/**
* The action of the order change action
*/
action: Record<string, unknown>
action: string
/**
* The metadata of the order change action
* The details of the order change action
*/
metadata?: Record<string, unknown> | null
details: Record<string, unknown> | null
/**
* The internal note of the order change action
*/
@@ -877,28 +891,42 @@ export interface FilterableOrderShippingMethodTaxLineProps
export interface FilterableOrderChangeProps
extends BaseFilterable<FilterableOrderChangeProps> {
id?: string | string[]
order_id?: string | string[]
status?: string | string[]
requested_by?: string | string[]
confirmed_by?: string | string[]
declined_by?: string | string[]
canceled_by?: string | string[]
id?: string | string[] | OperatorMap<string>
order_id?: string | string[] | OperatorMap<string>
status?: string | string[] | OperatorMap<string>
requested_by?: string | string[] | OperatorMap<string>
confirmed_by?: string | string[] | OperatorMap<string>
declined_by?: string | string[] | OperatorMap<string>
canceled_by?: string | string[] | OperatorMap<string>
created_at?: OperatorMap<string>
updated_at?: OperatorMap<string>
deleted_at?: OperatorMap<string>
canceled_at?: OperatorMap<string>
}
export interface FilterableOrderChangeActionProps
extends BaseFilterable<FilterableOrderChangeActionProps> {
id?: string | string[]
order_change_id?: string | string[]
reference?: string | string[]
reference_id?: string | string[]
id?: string | string[] | OperatorMap<string>
order_change_id?: string | string[] | OperatorMap<string>
reference?: string | string[] | OperatorMap<string>
reference_id?: string | string[] | OperatorMap<string>
}
export interface FilterableOrderTransactionProps
extends BaseFilterable<FilterableOrderTransactionProps> {
id?: string | string[]
order_id?: string | string[]
currency_code?: string | string[]
reference?: string | string[]
reference_id?: string | string[]
id?: string | string[] | OperatorMap<string>
order_id?: string | string[] | OperatorMap<string>
currency_code?: string | string[] | OperatorMap<string>
reference?: string | string[] | OperatorMap<string>
reference_id?: string | string[] | OperatorMap<string>
created_at?: OperatorMap<string>
}
export interface FilterableOrderItemProps
extends BaseFilterable<FilterableOrderItemProps> {
id?: string | string[] | OperatorMap<string>
order_id?: string | string[] | OperatorMap<string>
version?: string | string[] | OperatorMap<string>
item_id?: string | string[] | OperatorMap<string>
}
+1
View File
@@ -1,3 +1,4 @@
export * from "./common"
export * from "./mutations"
export * from "./service"
export * from "./workflows"
+10 -10
View File
@@ -32,16 +32,18 @@ export interface CreateOrderDTO {
sales_channel_id?: string
status?: string
email?: string
currency_code: string
currency_code?: string
shipping_address_id?: string
billing_address_id?: string
shipping_address?: CreateOrderAddressDTO | UpdateOrderAddressDTO
billing_address?: CreateOrderAddressDTO | UpdateOrderAddressDTO
no_notification?: boolean
items?: CreateOrderLineItemDTO[]
shipping_methods?: CreateOrderShippingMethodDTO[]
shipping_methods?: Omit<CreateOrderShippingMethodDTO, "order_id">[]
transactions?: CreateOrderTransactionDTO[]
metadata?: Record<string, unknown>
promo_codes?: string[]
}
export interface UpdateOrderDTO {
@@ -162,6 +164,8 @@ export interface CreateOrderLineItemDTO {
tax_lines?: CreateOrderTaxLineDTO[]
adjustments?: CreateOrderAdjustmentDTO[]
metadata?: Record<string, unknown>
}
export interface CreateOrderLineItemForOrderDTO extends CreateOrderLineItemDTO {
@@ -194,9 +198,9 @@ export interface UpdateOrderLineItemDTO
export interface CreateOrderShippingMethodDTO {
name: string
shipping_method_id: string
order_id: string
amount: BigNumberInput
shipping_option_id?: string
data?: Record<string, unknown>
tax_lines?: CreateOrderTaxLineDTO[]
adjustments?: CreateOrderAdjustmentDTO[]
@@ -205,7 +209,7 @@ export interface CreateOrderShippingMethodDTO {
export interface UpdateOrderShippingMethodDTO {
id: string
name?: string
shipping_method_id: string
shipping_option_id?: string
amount?: BigNumberInput
data?: Record<string, unknown>
tax_lines?: UpdateOrderTaxLineDTO[] | CreateOrderTaxLineDTO[]
@@ -285,18 +289,14 @@ export interface CreateOrderChangeActionDTO {
order_change_id: string
reference: string
reference_id: string
action: Record<string, unknown>
action: string
internal_note?: string
metadata?: Record<string, unknown>
details?: Record<string, unknown>
}
export interface UpdateOrderChangeActionDTO {
id: string
reference?: string
reference_id?: string
action?: Record<string, unknown>
internal_note?: string
metadata?: Record<string, unknown>
}
/** ORDER TRANSACTION START */
File diff suppressed because it is too large Load Diff
+8
View File
@@ -0,0 +1,8 @@
import { CustomerDTO } from "../customer"
import { RegionDTO } from "../region"
import { OrderDTO } from "./common"
export interface OrderWorkflowDTO extends OrderDTO {
customer?: CustomerDTO
region?: RegionDTO
}
+4 -3
View File
@@ -1,5 +1,6 @@
import { BaseFilterable } from "../dal"
import { OperatorMap } from "../dal/utils"
import { BigNumberInput } from "../totals"
/**
* The tax rate details.
@@ -386,12 +387,12 @@ export interface TaxableItemDTO {
/**
* The quantity of the taxable item.
*/
quantity?: number
quantity?: BigNumberInput
/**
* The unit price of the taxable item.
*/
unit_price?: number
unit_price?: BigNumberInput
/**
* The ISO 3 character currency code of the taxable item.
@@ -416,7 +417,7 @@ export interface TaxableShippingDTO {
/**
* The unit price of the taxable shipping.
*/
unit_price?: number
unit_price?: BigNumberInput
/**
* The ISO 3 character currency code of the taxable shipping.
+15 -9
View File
@@ -50,15 +50,21 @@ export const LINKS = {
Modules.STOCK_LOCATION,
"location_id"
),
PublishableApiKeySalesChannel: composeLinkName(
Modules.API_KEY,
"api_key_id",
OrderPromotion: composeLinkName(
Modules.ORDER,
"order_id",
Modules.PROMOTION,
"promotion_id"
),
OrderSalesChannel: composeLinkName(
Modules.ORDER,
"order_id",
Modules.SALES_CHANNEL,
"sales_channel_id"
),
ProductSalesChannel: composeLinkName(
Modules.PRODUCT,
"product_id",
PublishableApiKeySalesChannel: composeLinkName(
Modules.API_KEY,
"api_key_id",
Modules.SALES_CHANNEL,
"sales_channel_id"
),
@@ -70,9 +76,9 @@ export const LINKS = {
"shippingProfileService",
"profile_id"
),
OrderSalesChannel: composeLinkName(
"orderService",
"order_id",
ProductSalesChannel: composeLinkName(
Modules.PRODUCT,
"product_id",
Modules.SALES_CHANNEL,
"sales_channel_id"
),
+4
View File
@@ -6,6 +6,10 @@ import { BigNumber } from "./big-number"
type BNInput = BigNumberInput | BigNumber
export class MathBN {
static convert(num: BNInput): BigNumberJS {
if (num == null) {
return new BigNumberJS(0)
}
if (num instanceof BigNumber) {
return num.bigNumber!
} else if (num instanceof BigNumberJS) {
@@ -3,6 +3,7 @@ import {
createStep,
createWorkflow,
hook,
MedusaWorkflow,
parallelize,
StepResponse,
transform,
@@ -10,11 +11,16 @@ import {
jest.setTimeout(30000)
const afterEach_ = () => {
jest.clearAllMocks()
MedusaWorkflow.workflows = {}
}
describe("Workflow composer", function () {
afterEach(afterEach_)
describe("Using steps returning plain values", function () {
afterEach(async () => {
jest.clearAllMocks()
})
afterEach(afterEach_)
it("should compose a new workflow composed retryable steps", async () => {
const maxRetries = 1
@@ -657,83 +663,6 @@ describe("Workflow composer", function () {
})
})
it("should overwrite existing workflows if the same name is used", async () => {
const mockStep1Fn = jest.fn().mockImplementation((input, context) => {
return { inputs: [input], obj: "return from 1" }
})
const mockStep2Fn = jest.fn().mockImplementation((...inputs) => {
const context = inputs.pop()
return {
inputs,
obj: "return from 2",
}
})
const mockStep3Fn = jest.fn().mockImplementation((...inputs) => {
const context = inputs.pop()
return {
inputs,
obj: "return from 3",
}
})
const step1 = createStep("step1", mockStep1Fn)
const step2 = createStep("step2", mockStep2Fn)
const step3 = createStep("step3", mockStep3Fn)
createWorkflow("workflow1", function (input) {
const returnStep1 = step1(input)
const ret2 = step2(returnStep1)
return step3({ one: returnStep1, two: ret2 })
})
const overriddenWorkflow = createWorkflow("workflow1", function (input) {
const ret2 = step2(input)
const returnStep1 = step1(ret2)
return step3({ one: returnStep1, two: ret2 })
})
const workflowInput = { test: "payload1" }
const { result: workflowResult } = await overriddenWorkflow().run({
input: workflowInput,
})
expect(mockStep1Fn).toHaveBeenCalledTimes(1)
expect(mockStep1Fn.mock.calls[0]).toHaveLength(2)
expect(mockStep1Fn.mock.calls[0][0]).toEqual({
inputs: [workflowInput],
obj: "return from 2",
})
expect(mockStep2Fn).toHaveBeenCalledTimes(1)
expect(mockStep2Fn.mock.calls[0]).toHaveLength(2)
expect(mockStep2Fn.mock.calls[0][0]).toEqual(workflowInput)
expect(mockStep3Fn).toHaveBeenCalledTimes(1)
expect(mockStep3Fn.mock.calls[0]).toHaveLength(2)
expect(mockStep3Fn.mock.calls[0][0]).toEqual({
one: {
inputs: [{ inputs: [{ test: "payload1" }], obj: "return from 2" }],
obj: "return from 1",
},
two: { inputs: [{ test: "payload1" }], obj: "return from 2" },
})
expect(workflowResult).toEqual({
inputs: [
{
one: {
inputs: [
{ inputs: [{ test: "payload1" }], obj: "return from 2" },
],
obj: "return from 1",
},
two: { inputs: [{ test: "payload1" }], obj: "return from 2" },
},
],
obj: "return from 3",
})
})
it("should transform the values before forward them to the next step", async () => {
const mockStep1Fn = jest.fn().mockImplementation((obj, context) => {
const ret = {
@@ -958,9 +887,7 @@ describe("Workflow composer", function () {
})
describe("Using steps returning StepResponse", function () {
afterEach(async () => {
jest.clearAllMocks()
})
afterEach(afterEach_)
it("should compose a new workflow composed of retryable steps", async () => {
const maxRetries = 1
@@ -1636,83 +1563,6 @@ describe("Workflow composer", function () {
})
})
it("should overwrite existing workflows if the same name is used", async () => {
const mockStep1Fn = jest.fn().mockImplementation((input, context) => {
return new StepResponse({ inputs: [input], obj: "return from 1" })
})
const mockStep2Fn = jest.fn().mockImplementation((...inputs) => {
const context = inputs.pop()
return new StepResponse({
inputs,
obj: "return from 2",
})
})
const mockStep3Fn = jest.fn().mockImplementation((...inputs) => {
const context = inputs.pop()
return new StepResponse({
inputs,
obj: "return from 3",
})
})
const step1 = createStep("step1", mockStep1Fn)
const step2 = createStep("step2", mockStep2Fn)
const step3 = createStep("step3", mockStep3Fn)
createWorkflow("workflow1", function (input) {
const returnStep1 = step1(input)
const ret2 = step2(returnStep1)
return step3({ one: returnStep1, two: ret2 })
})
const overriddenWorkflow = createWorkflow("workflow1", function (input) {
const ret2 = step2(input)
const returnStep1 = step1(ret2)
return step3({ one: returnStep1, two: ret2 })
})
const workflowInput = { test: "payload1" }
const { result: workflowResult } = await overriddenWorkflow().run({
input: workflowInput,
})
expect(mockStep1Fn).toHaveBeenCalledTimes(1)
expect(mockStep1Fn.mock.calls[0]).toHaveLength(2)
expect(mockStep1Fn.mock.calls[0][0]).toEqual({
inputs: [workflowInput],
obj: "return from 2",
})
expect(mockStep2Fn).toHaveBeenCalledTimes(1)
expect(mockStep2Fn.mock.calls[0]).toHaveLength(2)
expect(mockStep2Fn.mock.calls[0][0]).toEqual(workflowInput)
expect(mockStep3Fn).toHaveBeenCalledTimes(1)
expect(mockStep3Fn.mock.calls[0]).toHaveLength(2)
expect(mockStep3Fn.mock.calls[0][0]).toEqual({
one: {
inputs: [{ inputs: [{ test: "payload1" }], obj: "return from 2" }],
obj: "return from 1",
},
two: { inputs: [{ test: "payload1" }], obj: "return from 2" },
})
expect(workflowResult).toEqual({
inputs: [
{
one: {
inputs: [
{ inputs: [{ test: "payload1" }], obj: "return from 2" },
],
obj: "return from 1",
},
two: { inputs: [{ test: "payload1" }], obj: "return from 2" },
},
],
obj: "return from 3",
})
})
it("should transform the values before forward them to the next step", async () => {
const mockStep1Fn = jest.fn().mockImplementation((obj, context) => {
const ret = new StepResponse({
@@ -1,4 +1,5 @@
import { createMedusaContainer } from "@medusajs/utils"
import { MedusaWorkflow } from "../../medusa-workflow"
import { exportWorkflow } from "../workflow-export"
jest.mock("@medusajs/orchestration", () => {
@@ -63,6 +64,10 @@ jest.mock("@medusajs/orchestration", () => {
})
describe("Export Workflow", function () {
afterEach(() => {
MedusaWorkflow.workflows = {}
})
it("should prepare the input data before initializing the transaction", async function () {
let transformedInput
const prepare = jest.fn().mockImplementation(async (data) => {
@@ -98,6 +103,10 @@ describe("Export Workflow", function () {
})
describe("Using the exported workflow run", function () {
afterEach(() => {
MedusaWorkflow.workflows = {}
})
it("should prepare the input data before initializing the transaction", async function () {
let transformedInput
const prepare = jest.fn().mockImplementation(async (data) => {
@@ -9,12 +9,16 @@ export class MedusaWorkflow {
container?: LoadedModule[] | MedusaContainer
) => Omit<
LocalWorkflow,
"run" | "registerStepSuccess" | "registerStepFailure"
"run" | "registerStepSuccess" | "registerStepFailure" | "cancel"
> &
ExportedWorkflow
> = {}
static registerWorkflow(workflowId, exportedWorkflow) {
if (workflowId in MedusaWorkflow.workflows) {
throw new Error(`Workflow with id ${workflowId} already registered.`)
}
MedusaWorkflow.workflows[workflowId] = exportedWorkflow
}
@@ -72,7 +72,7 @@ export type ReturnWorkflow<
container?: LoadedModule[] | MedusaContainer
): Omit<
LocalWorkflow,
"run" | "registerStepSuccess" | "registerStepFailure"
"run" | "registerStepSuccess" | "registerStepFailure" | "cancel"
> &
ExportedWorkflow<TData, TResult, TDataOverride, TResultOverride>
} & THooks & {