feat(medusa): Modules initializer (#3352)
This commit is contained in:
committed by
GitHub
parent
8a7421db5b
commit
aa690beed7
@@ -0,0 +1,27 @@
|
||||
---
|
||||
"@medusajs/inventory": patch
|
||||
"medusa-core-utils": patch
|
||||
"@medusajs/medusa": patch
|
||||
---
|
||||
|
||||
feat(medusa): Modules initializer
|
||||
|
||||
### Loading modules in a project
|
||||
|
||||
Example
|
||||
|
||||
``` typescript
|
||||
import { InventoryServiceInitializeOptions, initialize } from "@medusajs/inventory"
|
||||
|
||||
const options: InventoryServiceInitializeOptions = {
|
||||
database: {
|
||||
type: "postgres",
|
||||
url: DB_URL,
|
||||
},
|
||||
}
|
||||
|
||||
const inventoryService = await initialize(options)
|
||||
const newInventoryItem = await inventoryService.createInventoryItem({
|
||||
sku: "sku_123",
|
||||
})
|
||||
```
|
||||
@@ -1,6 +1,6 @@
|
||||
const path = require("path")
|
||||
|
||||
const { createConnection } = require("typeorm")
|
||||
const { DataSource } = require("typeorm")
|
||||
const { getConfigFile } = require("medusa-core-utils")
|
||||
|
||||
const DB_HOST = process.env.DB_HOST
|
||||
@@ -14,14 +14,16 @@ process.env.NODE_ENV = "development"
|
||||
require("./dev-require")
|
||||
|
||||
async function createDB() {
|
||||
const connection = await createConnection({
|
||||
const connection = new DataSource({
|
||||
type: "postgres",
|
||||
url: `postgres://${DB_USERNAME}:${DB_PASSWORD}@${DB_HOST}`,
|
||||
})
|
||||
|
||||
await connection.initialize()
|
||||
|
||||
await connection.query(`DROP DATABASE IF EXISTS "${DB_NAME}";`)
|
||||
await connection.query(`CREATE DATABASE "${DB_NAME}";`)
|
||||
await connection.close()
|
||||
await connection.destroy()
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
@@ -47,6 +49,7 @@ module.exports = {
|
||||
const {
|
||||
getEnabledMigrations,
|
||||
getModuleSharedResources,
|
||||
runIsolatedModulesMigration,
|
||||
} = require("@medusajs/medusa/dist/commands/utils/get-migrations")
|
||||
|
||||
const entities = modelsLoader({}, { register: false })
|
||||
@@ -72,7 +75,7 @@ module.exports = {
|
||||
|
||||
await createDB()
|
||||
|
||||
const dbConnection = await createConnection({
|
||||
const dbConnection = new DataSource({
|
||||
type: "postgres",
|
||||
url: DB_URL,
|
||||
entities: enabledEntities.concat(moduleModels),
|
||||
@@ -80,8 +83,12 @@ module.exports = {
|
||||
// logging: true,
|
||||
})
|
||||
|
||||
await dbConnection.initialize()
|
||||
|
||||
await dbConnection.runMigrations()
|
||||
|
||||
await runIsolatedModulesMigration(configModule)
|
||||
|
||||
return dbConnection
|
||||
},
|
||||
}
|
||||
|
||||
@@ -17,14 +17,16 @@
|
||||
"author": "Medusa",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@medusajs/medusa": "*",
|
||||
"@medusajs/types": "*",
|
||||
"cross-env": "^5.2.1",
|
||||
"jest": "^25.5.4",
|
||||
"ts-jest": "^25.5.1",
|
||||
"typescript": "^4.4.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"@medusajs/medusa": "^1.7.7",
|
||||
"@medusajs/modules-sdk": "*",
|
||||
"@medusajs/utils": "^0.0.1",
|
||||
"awilix": "^8.0.0",
|
||||
"typeorm": "^0.3.11"
|
||||
},
|
||||
@@ -36,6 +38,6 @@
|
||||
"test:unit": "jest --passWithNoTests"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@medusajs/medusa": "1.7.13"
|
||||
"@medusajs/types": "^0.0.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import loadConnection from "./loaders/connection"
|
||||
import loadContainer from "./loaders/container"
|
||||
|
||||
import migrations from "./migrations"
|
||||
import { revertMigration, runMigrations } from "./migrations/run-migration"
|
||||
import * as InventoryModels from "./models"
|
||||
import InventoryService from "./services/inventory"
|
||||
|
||||
@@ -16,6 +17,12 @@ const moduleDefinition: ModuleExports = {
|
||||
migrations,
|
||||
loaders,
|
||||
models,
|
||||
runMigrations,
|
||||
revertMigration,
|
||||
}
|
||||
|
||||
export default moduleDefinition
|
||||
|
||||
export * from "./initialize"
|
||||
export * from "./types"
|
||||
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
import { IEventBusService, IInventoryService } from "@medusajs/medusa"
|
||||
import {
|
||||
ExternalModuleDeclaration,
|
||||
InternalModuleDeclaration,
|
||||
MedusaModule,
|
||||
} from "@medusajs/modules-sdk"
|
||||
import { InventoryServiceInitializeOptions } from "../types"
|
||||
|
||||
export const initialize = async (
|
||||
options?: InventoryServiceInitializeOptions | ExternalModuleDeclaration,
|
||||
injectedDependencies?: {
|
||||
eventBusService: IEventBusService
|
||||
}
|
||||
): Promise<IInventoryService> => {
|
||||
const serviceKey = "inventoryService"
|
||||
const loaded = await MedusaModule.bootstrap(
|
||||
serviceKey,
|
||||
"@medusajs/inventory",
|
||||
options as InternalModuleDeclaration | ExternalModuleDeclaration,
|
||||
injectedDependencies
|
||||
)
|
||||
|
||||
return loaded[serviceKey] as IInventoryService
|
||||
}
|
||||
@@ -6,9 +6,10 @@ import {
|
||||
} from "@medusajs/modules-sdk"
|
||||
import { DataSource, DataSourceOptions } from "typeorm"
|
||||
|
||||
import * as InventoryModels from "../models"
|
||||
import { MedusaError } from "medusa-core-utils"
|
||||
import { asValue } from "awilix"
|
||||
import { MedusaError } from "medusa-core-utils"
|
||||
import * as InventoryModels from "../models"
|
||||
import { InventoryServiceInitializeOptions } from "../types"
|
||||
|
||||
export default async (
|
||||
{ options, container }: LoaderOptions,
|
||||
@@ -21,7 +22,8 @@ export default async (
|
||||
return
|
||||
}
|
||||
|
||||
const dbData = options?.database as Record<string, string>
|
||||
const dbData =
|
||||
options?.database as InventoryServiceInitializeOptions["database"]
|
||||
|
||||
if (!dbData) {
|
||||
throw new MedusaError(
|
||||
@@ -32,13 +34,13 @@ export default async (
|
||||
|
||||
const entities = Object.values(InventoryModels)
|
||||
const dataSource = new DataSource({
|
||||
type: dbData.database_type,
|
||||
url: dbData.database_url,
|
||||
database: dbData.database_database,
|
||||
extra: dbData.database_extra || {},
|
||||
schema: dbData.database_schema,
|
||||
type: dbData.type,
|
||||
url: dbData.url,
|
||||
database: dbData.database,
|
||||
extra: dbData.extra || {},
|
||||
schema: dbData.schema,
|
||||
entities,
|
||||
logging: dbData.database_logging,
|
||||
logging: dbData.logging,
|
||||
} as DataSourceOptions)
|
||||
|
||||
await dataSource.initialize()
|
||||
|
||||
@@ -3,7 +3,7 @@ import { InternalModuleDeclaration, LoaderOptions } from "@medusajs/modules-sdk"
|
||||
import {
|
||||
InventoryItemService,
|
||||
InventoryLevelService,
|
||||
ReservationItemService,
|
||||
ReservationItemService
|
||||
} from "../services"
|
||||
|
||||
import { asClass } from "awilix"
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
import { InternalModuleDeclaration, LoaderOptions } from "@medusajs/modules-sdk"
|
||||
import { DataSource, DataSourceOptions } from "typeorm"
|
||||
import { InventoryServiceInitializeOptions } from "../types"
|
||||
|
||||
import migrations from "./index"
|
||||
|
||||
function getDataSource(
|
||||
dbData: InventoryServiceInitializeOptions["database"]
|
||||
): DataSource {
|
||||
return new DataSource({
|
||||
type: dbData!.type,
|
||||
url: dbData!.url,
|
||||
database: dbData!.database,
|
||||
extra: dbData!.extra || {},
|
||||
migrations: migrations
|
||||
.map((migration: any): Function[] => {
|
||||
return Object.values(migration).filter(
|
||||
(fn) => typeof fn === "function"
|
||||
) as Function[]
|
||||
})
|
||||
.flat(),
|
||||
schema: dbData!.schema,
|
||||
logging: dbData!.logging,
|
||||
} as DataSourceOptions)
|
||||
}
|
||||
|
||||
export async function runMigrations(
|
||||
{ options, logger }: Omit<LoaderOptions, "container">,
|
||||
moduleDeclaration?: InternalModuleDeclaration
|
||||
) {
|
||||
const dbData =
|
||||
options?.database as InventoryServiceInitializeOptions["database"]
|
||||
|
||||
try {
|
||||
const dataSource = getDataSource(dbData)
|
||||
await dataSource.initialize()
|
||||
await dataSource.runMigrations()
|
||||
|
||||
logger?.info("Inventory module migration executed")
|
||||
} catch (error) {
|
||||
logger?.error(`Inventory module migration failed to run - Error: ${error}`)
|
||||
}
|
||||
}
|
||||
|
||||
export async function revertMigration(
|
||||
{ options, logger }: Omit<LoaderOptions, "container">,
|
||||
moduleDeclaration?: InternalModuleDeclaration
|
||||
) {
|
||||
const dbData =
|
||||
options?.database as InventoryServiceInitializeOptions["database"]
|
||||
|
||||
try {
|
||||
const dataSource = getDataSource(dbData)
|
||||
await dataSource.initialize()
|
||||
await dataSource.undoLastMigration()
|
||||
|
||||
logger?.info("Inventory module migration reverted")
|
||||
} catch (error) {
|
||||
logger?.error(
|
||||
`Inventory module migration failed to revert - Error: ${error}`
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Index, Unique, BeforeInsert, Column, Entity } from "typeorm"
|
||||
import { Index, BeforeInsert, Column, Entity } from "typeorm"
|
||||
import { SoftDeletableEntity, generateEntityId } from "@medusajs/medusa"
|
||||
|
||||
@Entity()
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { DeepPartial, EntityManager, FindManyOptions } from "typeorm"
|
||||
import { isDefined, MedusaError } from "medusa-core-utils"
|
||||
import {
|
||||
buildQuery,
|
||||
CreateInventoryItemInput,
|
||||
@@ -7,9 +5,11 @@ import {
|
||||
FindConfig,
|
||||
IEventBusService,
|
||||
InventoryItemDTO,
|
||||
TransactionBaseService,
|
||||
} from "@medusajs/medusa"
|
||||
|
||||
import { SharedContext } from "@medusajs/types"
|
||||
import { InjectEntityManager, MedusaContext } from "@medusajs/utils"
|
||||
import { isDefined, MedusaError } from "medusa-core-utils"
|
||||
import { DeepPartial, EntityManager, FindManyOptions } from "typeorm"
|
||||
import { InventoryItem } from "../models"
|
||||
import { getListQuery } from "../utils/query"
|
||||
|
||||
@@ -18,18 +18,18 @@ type InjectedDependencies = {
|
||||
manager: EntityManager
|
||||
}
|
||||
|
||||
export default class InventoryItemService extends TransactionBaseService {
|
||||
export default class InventoryItemService {
|
||||
static Events = {
|
||||
CREATED: "inventory-item.created",
|
||||
UPDATED: "inventory-item.updated",
|
||||
DELETED: "inventory-item.deleted",
|
||||
}
|
||||
|
||||
protected readonly eventBusService_: IEventBusService
|
||||
|
||||
constructor({ eventBusService }: InjectedDependencies) {
|
||||
super(arguments[0])
|
||||
protected readonly manager_: EntityManager
|
||||
protected readonly eventBusService_: IEventBusService | undefined
|
||||
|
||||
constructor({ eventBusService, manager }: InjectedDependencies) {
|
||||
this.manager_ = manager
|
||||
this.eventBusService_ = eventBusService
|
||||
}
|
||||
|
||||
@@ -38,11 +38,17 @@ export default class InventoryItemService extends TransactionBaseService {
|
||||
* @param config - Configuration for query.
|
||||
* @return Resolves to the list of inventory items that match the filter.
|
||||
*/
|
||||
@InjectEntityManager()
|
||||
async list(
|
||||
selector: FilterableInventoryItemProps = {},
|
||||
config: FindConfig<InventoryItem> = { relations: [], skip: 0, take: 10 }
|
||||
config: FindConfig<InventoryItem> = { relations: [], skip: 0, take: 10 },
|
||||
@MedusaContext() context: SharedContext = {}
|
||||
): Promise<InventoryItemDTO[]> {
|
||||
const queryBuilder = getListQuery(this.activeManager_, selector, config)
|
||||
const queryBuilder = getListQuery(
|
||||
context.transactionManager!,
|
||||
selector,
|
||||
config
|
||||
)
|
||||
return await queryBuilder.getMany()
|
||||
}
|
||||
|
||||
@@ -51,11 +57,17 @@ export default class InventoryItemService extends TransactionBaseService {
|
||||
* @param config - Configuration for query.
|
||||
* @return - Resolves to the list of inventory items that match the filter and the count of all matching items.
|
||||
*/
|
||||
@InjectEntityManager()
|
||||
async listAndCount(
|
||||
selector: FilterableInventoryItemProps = {},
|
||||
config: FindConfig<InventoryItem> = { relations: [], skip: 0, take: 10 }
|
||||
config: FindConfig<InventoryItem> = { relations: [], skip: 0, take: 10 },
|
||||
@MedusaContext() context: SharedContext = {}
|
||||
): Promise<[InventoryItemDTO[], number]> {
|
||||
const queryBuilder = getListQuery(this.activeManager_, selector, config)
|
||||
const queryBuilder = getListQuery(
|
||||
context.transactionManager!,
|
||||
selector,
|
||||
config
|
||||
)
|
||||
return await queryBuilder.getManyAndCount()
|
||||
}
|
||||
|
||||
@@ -66,9 +78,11 @@ export default class InventoryItemService extends TransactionBaseService {
|
||||
* @return The retrieved inventory item.
|
||||
* @throws If the inventory item id is not defined or if the inventory item is not found.
|
||||
*/
|
||||
@InjectEntityManager()
|
||||
async retrieve(
|
||||
inventoryItemId: string,
|
||||
config: FindConfig<InventoryItem> = {}
|
||||
config: FindConfig<InventoryItem> = {},
|
||||
@MedusaContext() context: SharedContext = {}
|
||||
): Promise<InventoryItem> {
|
||||
if (!isDefined(inventoryItemId)) {
|
||||
throw new MedusaError(
|
||||
@@ -77,7 +91,7 @@ export default class InventoryItemService extends TransactionBaseService {
|
||||
)
|
||||
}
|
||||
|
||||
const manager = this.activeManager_
|
||||
const manager = context.transactionManager!
|
||||
const itemRepository = manager.getRepository(InventoryItem)
|
||||
|
||||
const query = buildQuery({ id: inventoryItemId }, config) as FindManyOptions
|
||||
@@ -97,34 +111,35 @@ export default class InventoryItemService extends TransactionBaseService {
|
||||
* @param input - Input for creating a new inventory item.
|
||||
* @return The newly created inventory item.
|
||||
*/
|
||||
async create(data: CreateInventoryItemInput): Promise<InventoryItem> {
|
||||
return await this.atomicPhase_(async (manager) => {
|
||||
const itemRepository = manager.getRepository(InventoryItem)
|
||||
@InjectEntityManager()
|
||||
async create(
|
||||
data: CreateInventoryItemInput,
|
||||
@MedusaContext() context: SharedContext = {}
|
||||
): Promise<InventoryItem> {
|
||||
const manager = context.transactionManager!
|
||||
const itemRepository = manager.getRepository(InventoryItem)
|
||||
|
||||
const inventoryItem = itemRepository.create({
|
||||
sku: data.sku,
|
||||
origin_country: data.origin_country,
|
||||
metadata: data.metadata,
|
||||
hs_code: data.hs_code,
|
||||
mid_code: data.mid_code,
|
||||
material: data.material,
|
||||
weight: data.weight,
|
||||
length: data.length,
|
||||
height: data.height,
|
||||
width: data.width,
|
||||
requires_shipping: data.requires_shipping,
|
||||
})
|
||||
|
||||
const result = await itemRepository.save(inventoryItem)
|
||||
|
||||
await this.eventBusService_
|
||||
.withTransaction(manager)
|
||||
.emit(InventoryItemService.Events.CREATED, {
|
||||
id: result.id,
|
||||
})
|
||||
|
||||
return result
|
||||
const inventoryItem = itemRepository.create({
|
||||
sku: data.sku,
|
||||
origin_country: data.origin_country,
|
||||
metadata: data.metadata,
|
||||
hs_code: data.hs_code,
|
||||
mid_code: data.mid_code,
|
||||
material: data.material,
|
||||
weight: data.weight,
|
||||
length: data.length,
|
||||
height: data.height,
|
||||
width: data.width,
|
||||
requires_shipping: data.requires_shipping,
|
||||
})
|
||||
|
||||
const result = await itemRepository.save(inventoryItem)
|
||||
|
||||
await this.eventBusService_?.emit?.(InventoryItemService.Events.CREATED, {
|
||||
id: result.id,
|
||||
})
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -132,51 +147,51 @@ export default class InventoryItemService extends TransactionBaseService {
|
||||
* @param update - The updates to apply to the inventory item.
|
||||
* @return The updated inventory item.
|
||||
*/
|
||||
@InjectEntityManager()
|
||||
async update(
|
||||
inventoryItemId: string,
|
||||
data: Omit<
|
||||
DeepPartial<InventoryItem>,
|
||||
"id" | "created_at" | "metadata" | "deleted_at"
|
||||
>
|
||||
>,
|
||||
@MedusaContext() context: SharedContext = {}
|
||||
): Promise<InventoryItem> {
|
||||
return await this.atomicPhase_(async (manager) => {
|
||||
const itemRepository = manager.getRepository(InventoryItem)
|
||||
const manager = context.transactionManager!
|
||||
const itemRepository = manager.getRepository(InventoryItem)
|
||||
|
||||
const item = await this.retrieve(inventoryItemId)
|
||||
const item = await this.retrieve(inventoryItemId, undefined, context)
|
||||
|
||||
const shouldUpdate = Object.keys(data).some((key) => {
|
||||
return item[key] !== data[key]
|
||||
})
|
||||
|
||||
if (shouldUpdate) {
|
||||
itemRepository.merge(item, data)
|
||||
await itemRepository.save(item)
|
||||
|
||||
await this.eventBusService_
|
||||
.withTransaction(manager)
|
||||
.emit(InventoryItemService.Events.UPDATED, {
|
||||
id: item.id,
|
||||
})
|
||||
}
|
||||
|
||||
return item
|
||||
const shouldUpdate = Object.keys(data).some((key) => {
|
||||
return item[key] !== data[key]
|
||||
})
|
||||
|
||||
if (shouldUpdate) {
|
||||
itemRepository.merge(item, data)
|
||||
await itemRepository.save(item)
|
||||
|
||||
await this.eventBusService_?.emit?.(InventoryItemService.Events.UPDATED, {
|
||||
id: item.id,
|
||||
})
|
||||
}
|
||||
|
||||
return item
|
||||
}
|
||||
|
||||
/**
|
||||
* @param inventoryItemId - The id of the inventory item to delete.
|
||||
*/
|
||||
async delete(inventoryItemId: string): Promise<void> {
|
||||
await this.atomicPhase_(async (manager) => {
|
||||
const itemRepository = manager.getRepository(InventoryItem)
|
||||
@InjectEntityManager()
|
||||
async delete(
|
||||
inventoryItemId: string,
|
||||
@MedusaContext() context: SharedContext = {}
|
||||
): Promise<void> {
|
||||
const manager = context.transactionManager!
|
||||
const itemRepository = manager.getRepository(InventoryItem)
|
||||
|
||||
await itemRepository.softRemove({ id: inventoryItemId })
|
||||
await itemRepository.softRemove({ id: inventoryItemId })
|
||||
|
||||
await this.eventBusService_
|
||||
.withTransaction(manager)
|
||||
.emit(InventoryItemService.Events.DELETED, {
|
||||
id: inventoryItemId,
|
||||
})
|
||||
await this.eventBusService_?.emit?.(InventoryItemService.Events.DELETED, {
|
||||
id: inventoryItemId,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { DeepPartial, EntityManager, FindManyOptions, In } from "typeorm"
|
||||
import { isDefined, MedusaError } from "medusa-core-utils"
|
||||
import {
|
||||
buildQuery,
|
||||
CreateInventoryLevelInput,
|
||||
FilterableInventoryLevelProps,
|
||||
FindConfig,
|
||||
IEventBusService,
|
||||
TransactionBaseService,
|
||||
} from "@medusajs/medusa"
|
||||
|
||||
import { SharedContext } from "@medusajs/types"
|
||||
import { InjectEntityManager, MedusaContext } from "@medusajs/utils"
|
||||
import { isDefined, MedusaError } from "medusa-core-utils"
|
||||
import { DeepPartial, EntityManager, FindManyOptions, In } from "typeorm"
|
||||
import { InventoryLevel } from "../models"
|
||||
|
||||
type InjectedDependencies = {
|
||||
@@ -16,18 +16,18 @@ type InjectedDependencies = {
|
||||
manager: EntityManager
|
||||
}
|
||||
|
||||
export default class InventoryLevelService extends TransactionBaseService {
|
||||
export default class InventoryLevelService {
|
||||
static Events = {
|
||||
CREATED: "inventory-level.created",
|
||||
UPDATED: "inventory-level.updated",
|
||||
DELETED: "inventory-level.deleted",
|
||||
}
|
||||
|
||||
protected readonly eventBusService_: IEventBusService
|
||||
|
||||
constructor({ eventBusService }: InjectedDependencies) {
|
||||
super(arguments[0])
|
||||
protected readonly manager_: EntityManager
|
||||
protected readonly eventBusService_: IEventBusService | undefined
|
||||
|
||||
constructor({ eventBusService, manager }: InjectedDependencies) {
|
||||
this.manager_ = manager
|
||||
this.eventBusService_ = eventBusService
|
||||
}
|
||||
|
||||
@@ -37,11 +37,13 @@ export default class InventoryLevelService extends TransactionBaseService {
|
||||
* @param config - An object containing configuration options for the query.
|
||||
* @return Array of inventory levels.
|
||||
*/
|
||||
@InjectEntityManager()
|
||||
async list(
|
||||
selector: FilterableInventoryLevelProps = {},
|
||||
config: FindConfig<InventoryLevel> = { relations: [], skip: 0, take: 10 }
|
||||
config: FindConfig<InventoryLevel> = { relations: [], skip: 0, take: 10 },
|
||||
@MedusaContext() context: SharedContext = {}
|
||||
): Promise<InventoryLevel[]> {
|
||||
const manager = this.activeManager_
|
||||
const manager = context.transactionManager!
|
||||
const levelRepository = manager.getRepository(InventoryLevel)
|
||||
|
||||
const query = buildQuery(selector, config) as FindManyOptions
|
||||
@@ -54,11 +56,13 @@ export default class InventoryLevelService extends TransactionBaseService {
|
||||
* @param config - An object containing configuration options for the query.
|
||||
* @return An array of inventory levels and a count.
|
||||
*/
|
||||
@InjectEntityManager()
|
||||
async listAndCount(
|
||||
selector: FilterableInventoryLevelProps = {},
|
||||
config: FindConfig<InventoryLevel> = { relations: [], skip: 0, take: 10 }
|
||||
config: FindConfig<InventoryLevel> = { relations: [], skip: 0, take: 10 },
|
||||
@MedusaContext() context: SharedContext = {}
|
||||
): Promise<[InventoryLevel[], number]> {
|
||||
const manager = this.activeManager_
|
||||
const manager = context.transactionManager!
|
||||
const levelRepository = manager.getRepository(InventoryLevel)
|
||||
|
||||
const query = buildQuery(selector, config) as FindManyOptions
|
||||
@@ -72,9 +76,11 @@ export default class InventoryLevelService extends TransactionBaseService {
|
||||
* @return A inventory level.
|
||||
* @throws If the inventory level ID is not defined or the given ID was not found.
|
||||
*/
|
||||
@InjectEntityManager()
|
||||
async retrieve(
|
||||
inventoryLevelId: string,
|
||||
config: FindConfig<InventoryLevel> = {}
|
||||
config: FindConfig<InventoryLevel> = {},
|
||||
@MedusaContext() context: SharedContext = {}
|
||||
): Promise<InventoryLevel> {
|
||||
if (!isDefined(inventoryLevelId)) {
|
||||
throw new MedusaError(
|
||||
@@ -83,10 +89,13 @@ export default class InventoryLevelService extends TransactionBaseService {
|
||||
)
|
||||
}
|
||||
|
||||
const manager = this.activeManager_
|
||||
const manager = context.transactionManager!
|
||||
const levelRepository = manager.getRepository(InventoryLevel)
|
||||
|
||||
const query = buildQuery({ id: inventoryLevelId }, config) as FindManyOptions
|
||||
const query = buildQuery(
|
||||
{ id: inventoryLevelId },
|
||||
config
|
||||
) as FindManyOptions
|
||||
const [inventoryLevel] = await levelRepository.find(query)
|
||||
|
||||
if (!inventoryLevel) {
|
||||
@@ -104,27 +113,29 @@ export default class InventoryLevelService extends TransactionBaseService {
|
||||
* @param data - An object containing the properties for the new inventory level.
|
||||
* @return The created inventory level.
|
||||
*/
|
||||
async create(data: CreateInventoryLevelInput): Promise<InventoryLevel> {
|
||||
return await this.atomicPhase_(async (manager) => {
|
||||
const levelRepository = manager.getRepository(InventoryLevel)
|
||||
@InjectEntityManager()
|
||||
async create(
|
||||
data: CreateInventoryLevelInput,
|
||||
@MedusaContext() context: SharedContext = {}
|
||||
): Promise<InventoryLevel> {
|
||||
const manager = context.transactionManager!
|
||||
|
||||
const inventoryLevel = levelRepository.create({
|
||||
location_id: data.location_id,
|
||||
inventory_item_id: data.inventory_item_id,
|
||||
stocked_quantity: data.stocked_quantity,
|
||||
reserved_quantity: data.reserved_quantity,
|
||||
incoming_quantity: data.incoming_quantity,
|
||||
})
|
||||
const levelRepository = manager.getRepository(InventoryLevel)
|
||||
|
||||
const saved = await levelRepository.save(inventoryLevel)
|
||||
await this.eventBusService_
|
||||
.withTransaction(manager)
|
||||
.emit(InventoryLevelService.Events.CREATED, {
|
||||
id: saved.id,
|
||||
})
|
||||
|
||||
return saved
|
||||
const inventoryLevel = levelRepository.create({
|
||||
location_id: data.location_id,
|
||||
inventory_item_id: data.inventory_item_id,
|
||||
stocked_quantity: data.stocked_quantity,
|
||||
reserved_quantity: data.reserved_quantity,
|
||||
incoming_quantity: data.incoming_quantity,
|
||||
})
|
||||
|
||||
const saved = await levelRepository.save(inventoryLevel)
|
||||
await this.eventBusService_?.emit?.(InventoryLevelService.Events.CREATED, {
|
||||
id: saved.id,
|
||||
})
|
||||
|
||||
return saved
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -134,35 +145,37 @@ export default class InventoryLevelService extends TransactionBaseService {
|
||||
* @return The updated inventory level.
|
||||
* @throws If the inventory level ID is not defined or the given ID was not found.
|
||||
*/
|
||||
@InjectEntityManager()
|
||||
async update(
|
||||
inventoryLevelId: string,
|
||||
data: Omit<
|
||||
DeepPartial<InventoryLevel>,
|
||||
"id" | "created_at" | "metadata" | "deleted_at"
|
||||
>
|
||||
>,
|
||||
@MedusaContext() context: SharedContext = {}
|
||||
): Promise<InventoryLevel> {
|
||||
return await this.atomicPhase_(async (manager) => {
|
||||
const levelRepository = manager.getRepository(InventoryLevel)
|
||||
const manager = context.transactionManager!
|
||||
const levelRepository = manager.getRepository(InventoryLevel)
|
||||
|
||||
const item = await this.retrieve(inventoryLevelId)
|
||||
const item = await this.retrieve(inventoryLevelId, undefined, context)
|
||||
|
||||
const shouldUpdate = Object.keys(data).some((key) => {
|
||||
return item[key] !== data[key]
|
||||
})
|
||||
|
||||
if (shouldUpdate) {
|
||||
levelRepository.merge(item, data)
|
||||
await levelRepository.save(item)
|
||||
|
||||
await this.eventBusService_
|
||||
.withTransaction(manager)
|
||||
.emit(InventoryLevelService.Events.UPDATED, {
|
||||
id: item.id,
|
||||
})
|
||||
}
|
||||
|
||||
return item
|
||||
const shouldUpdate = Object.keys(data).some((key) => {
|
||||
return item[key] !== data[key]
|
||||
})
|
||||
|
||||
if (shouldUpdate) {
|
||||
levelRepository.merge(item, data)
|
||||
await levelRepository.save(item)
|
||||
|
||||
await this.eventBusService_?.emit?.(
|
||||
InventoryLevelService.Events.UPDATED,
|
||||
{
|
||||
id: item.id,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
return item
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -171,40 +184,45 @@ export default class InventoryLevelService extends TransactionBaseService {
|
||||
* @param locationId - The ID of the location.
|
||||
* @param quantity - The quantity to adjust from the reserved quantity.
|
||||
*/
|
||||
@InjectEntityManager()
|
||||
async adjustReservedQuantity(
|
||||
inventoryItemId: string,
|
||||
locationId: string,
|
||||
quantity: number
|
||||
quantity: number,
|
||||
@MedusaContext() context: SharedContext = {}
|
||||
): Promise<void> {
|
||||
await this.atomicPhase_(async (manager) => {
|
||||
await manager
|
||||
.createQueryBuilder()
|
||||
.update(InventoryLevel)
|
||||
.set({ reserved_quantity: () => `reserved_quantity + ${quantity}` })
|
||||
.where(
|
||||
"inventory_item_id = :inventoryItemId AND location_id = :locationId",
|
||||
{ inventoryItemId, locationId }
|
||||
)
|
||||
.execute()
|
||||
})
|
||||
const manager = context.transactionManager!
|
||||
await manager
|
||||
.createQueryBuilder()
|
||||
.update(InventoryLevel)
|
||||
.set({ reserved_quantity: () => `reserved_quantity + ${quantity}` })
|
||||
.where(
|
||||
"inventory_item_id = :inventoryItemId AND location_id = :locationId",
|
||||
{ inventoryItemId, locationId }
|
||||
)
|
||||
.execute()
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes inventory levels by inventory Item ID.
|
||||
* @param inventoryItemId - The ID or IDs of the inventory item to delete inventory levels for.
|
||||
*/
|
||||
async deleteByInventoryItemId(inventoryItemId: string | string[]): Promise<void> {
|
||||
const ids = Array.isArray(inventoryItemId) ? inventoryItemId : [inventoryItemId]
|
||||
await this.atomicPhase_(async (manager) => {
|
||||
const levelRepository = manager.getRepository(InventoryLevel)
|
||||
* Deletes inventory levels by inventory Item ID.
|
||||
* @param inventoryItemId - The ID or IDs of the inventory item to delete inventory levels for.
|
||||
*/
|
||||
@InjectEntityManager()
|
||||
async deleteByInventoryItemId(
|
||||
inventoryItemId: string | string[],
|
||||
@MedusaContext() context: SharedContext = {}
|
||||
): Promise<void> {
|
||||
const ids = Array.isArray(inventoryItemId)
|
||||
? inventoryItemId
|
||||
: [inventoryItemId]
|
||||
|
||||
await levelRepository.delete({ inventory_item_id: In(ids) })
|
||||
const manager = context.transactionManager!
|
||||
const levelRepository = manager.getRepository(InventoryLevel)
|
||||
|
||||
await this.eventBusService_
|
||||
.withTransaction(manager)
|
||||
.emit(InventoryLevelService.Events.DELETED, {
|
||||
inventory_item_id: inventoryItemId,
|
||||
})
|
||||
await levelRepository.delete({ inventory_item_id: In(ids) })
|
||||
|
||||
await this.eventBusService_?.emit?.(InventoryLevelService.Events.DELETED, {
|
||||
inventory_item_id: inventoryItemId,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -212,18 +230,22 @@ export default class InventoryLevelService extends TransactionBaseService {
|
||||
* Deletes an inventory level by ID.
|
||||
* @param inventoryLevelId - The ID or IDs of the inventory level to delete.
|
||||
*/
|
||||
async delete(inventoryLevelId: string | string[]): Promise<void> {
|
||||
const ids = Array.isArray(inventoryLevelId) ? inventoryLevelId : [inventoryLevelId]
|
||||
await this.atomicPhase_(async (manager) => {
|
||||
const levelRepository = manager.getRepository(InventoryLevel)
|
||||
@InjectEntityManager()
|
||||
async delete(
|
||||
inventoryLevelId: string | string[],
|
||||
@MedusaContext() context: SharedContext = {}
|
||||
): Promise<void> {
|
||||
const ids = Array.isArray(inventoryLevelId)
|
||||
? inventoryLevelId
|
||||
: [inventoryLevelId]
|
||||
|
||||
await levelRepository.delete({ id: In(ids) })
|
||||
const manager = context.transactionManager!
|
||||
const levelRepository = manager.getRepository(InventoryLevel)
|
||||
|
||||
await this.eventBusService_
|
||||
.withTransaction(manager)
|
||||
.emit(InventoryLevelService.Events.DELETED, {
|
||||
id: inventoryLevelId,
|
||||
})
|
||||
await levelRepository.delete({ id: In(ids) })
|
||||
|
||||
await this.eventBusService_?.emit?.(InventoryLevelService.Events.DELETED, {
|
||||
id: inventoryLevelId,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -231,17 +253,18 @@ export default class InventoryLevelService extends TransactionBaseService {
|
||||
* Deletes inventory levels by location ID.
|
||||
* @param locationId - The ID of the location to delete inventory levels for.
|
||||
*/
|
||||
async deleteByLocationId(locationId: string): Promise<void> {
|
||||
return await this.atomicPhase_(async (manager) => {
|
||||
const levelRepository = manager.getRepository(InventoryLevel)
|
||||
@InjectEntityManager()
|
||||
async deleteByLocationId(
|
||||
locationId: string,
|
||||
@MedusaContext() context: SharedContext = {}
|
||||
): Promise<void> {
|
||||
const manager = context.transactionManager!
|
||||
const levelRepository = manager.getRepository(InventoryLevel)
|
||||
|
||||
await levelRepository.delete({ location_id: locationId })
|
||||
await levelRepository.delete({ location_id: locationId })
|
||||
|
||||
await this.eventBusService_
|
||||
.withTransaction(manager)
|
||||
.emit(InventoryLevelService.Events.DELETED, {
|
||||
location_id: locationId,
|
||||
})
|
||||
await this.eventBusService_?.emit?.(InventoryLevelService.Events.DELETED, {
|
||||
location_id: locationId,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -251,15 +274,17 @@ export default class InventoryLevelService extends TransactionBaseService {
|
||||
* @param locationIds - The IDs of the locations.
|
||||
* @return The total stocked quantity.
|
||||
*/
|
||||
@InjectEntityManager()
|
||||
async getStockedQuantity(
|
||||
inventoryItemId: string,
|
||||
locationIds: string[] | string
|
||||
locationIds: string[] | string,
|
||||
@MedusaContext() context: SharedContext = {}
|
||||
): Promise<number> {
|
||||
if (!Array.isArray(locationIds)) {
|
||||
locationIds = [locationIds]
|
||||
}
|
||||
|
||||
const manager = this.activeManager_
|
||||
const manager = context.transactionManager!
|
||||
const levelRepository = manager.getRepository(InventoryLevel)
|
||||
|
||||
const result = await levelRepository
|
||||
@@ -278,15 +303,17 @@ export default class InventoryLevelService extends TransactionBaseService {
|
||||
* @param locationIds - The IDs of the locations.
|
||||
* @return The total available quantity.
|
||||
*/
|
||||
@InjectEntityManager()
|
||||
async getAvailableQuantity(
|
||||
inventoryItemId: string,
|
||||
locationIds: string[] | string
|
||||
locationIds: string[] | string,
|
||||
@MedusaContext() context: SharedContext = {}
|
||||
): Promise<number> {
|
||||
if (!Array.isArray(locationIds)) {
|
||||
locationIds = [locationIds]
|
||||
}
|
||||
|
||||
const manager = this.activeManager_
|
||||
const manager = context.transactionManager!
|
||||
const levelRepository = manager.getRepository(InventoryLevel)
|
||||
|
||||
const result = await levelRepository
|
||||
@@ -305,15 +332,17 @@ export default class InventoryLevelService extends TransactionBaseService {
|
||||
* @param locationIds - The IDs of the locations.
|
||||
* @return The total reserved quantity.
|
||||
*/
|
||||
@InjectEntityManager()
|
||||
async getReservedQuantity(
|
||||
inventoryItemId: string,
|
||||
locationIds: string[] | string
|
||||
locationIds: string[] | string,
|
||||
@MedusaContext() context: SharedContext = {}
|
||||
): Promise<number> {
|
||||
if (!Array.isArray(locationIds)) {
|
||||
locationIds = [locationIds]
|
||||
}
|
||||
|
||||
const manager = this.activeManager_
|
||||
const manager = context.transactionManager!
|
||||
const levelRepository = manager.getRepository(InventoryLevel)
|
||||
|
||||
const result = await levelRepository
|
||||
|
||||
@@ -13,18 +13,18 @@ import {
|
||||
InventoryItemDTO,
|
||||
InventoryLevelDTO,
|
||||
ReservationItemDTO,
|
||||
TransactionBaseService,
|
||||
UpdateInventoryLevelInput,
|
||||
UpdateReservationItemInput,
|
||||
} from "@medusajs/medusa"
|
||||
import { SharedContext } from "@medusajs/types"
|
||||
import { InjectEntityManager, MedusaContext } from "@medusajs/utils"
|
||||
import { MedusaError } from "medusa-core-utils"
|
||||
|
||||
import { EntityManager } from "typeorm"
|
||||
import {
|
||||
InventoryItemService,
|
||||
InventoryLevelService,
|
||||
ReservationItemService,
|
||||
} from "./"
|
||||
import { EntityManager } from "typeorm"
|
||||
|
||||
type InjectedDependencies = {
|
||||
manager: EntityManager
|
||||
@@ -33,11 +33,9 @@ type InjectedDependencies = {
|
||||
inventoryLevelService: InventoryLevelService
|
||||
reservationItemService: ReservationItemService
|
||||
}
|
||||
export default class InventoryService
|
||||
extends TransactionBaseService
|
||||
implements IInventoryService
|
||||
{
|
||||
protected readonly eventBusService_: IEventBusService
|
||||
export default class InventoryService implements IInventoryService {
|
||||
protected readonly manager_: EntityManager
|
||||
protected readonly eventBusService_: IEventBusService | undefined
|
||||
protected readonly inventoryItemService_: InventoryItemService
|
||||
protected readonly reservationItemService_: ReservationItemService
|
||||
protected readonly inventoryLevelService_: InventoryLevelService
|
||||
@@ -53,9 +51,7 @@ export default class InventoryService
|
||||
options?: unknown,
|
||||
moduleDeclaration?: InternalModuleDeclaration
|
||||
) {
|
||||
// @ts-ignore
|
||||
super(...arguments)
|
||||
|
||||
this.manager_ = manager
|
||||
this.eventBusService_ = eventBusService
|
||||
this.inventoryItemService_ = inventoryItemService
|
||||
this.inventoryLevelService_ = inventoryLevelService
|
||||
@@ -68,13 +64,17 @@ export default class InventoryService
|
||||
* @param config - the find configuration to use
|
||||
* @return A tuple of inventory items and their total count
|
||||
*/
|
||||
@InjectEntityManager()
|
||||
async listInventoryItems(
|
||||
selector: FilterableInventoryItemProps,
|
||||
config: FindConfig<InventoryItemDTO> = { relations: [], skip: 0, take: 10 }
|
||||
config: FindConfig<InventoryItemDTO> = { relations: [], skip: 0, take: 10 },
|
||||
@MedusaContext() context: SharedContext = {}
|
||||
): Promise<[InventoryItemDTO[], number]> {
|
||||
return await this.inventoryItemService_
|
||||
.withTransaction(this.activeManager_)
|
||||
.listAndCount(selector, config)
|
||||
return await this.inventoryItemService_.listAndCount(
|
||||
selector,
|
||||
config,
|
||||
context
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -83,13 +83,21 @@ export default class InventoryService
|
||||
* @param config - the find configuration to use
|
||||
* @return A tuple of inventory levels and their total count
|
||||
*/
|
||||
@InjectEntityManager()
|
||||
async listInventoryLevels(
|
||||
selector: FilterableInventoryLevelProps,
|
||||
config: FindConfig<InventoryLevelDTO> = { relations: [], skip: 0, take: 10 }
|
||||
config: FindConfig<InventoryLevelDTO> = {
|
||||
relations: [],
|
||||
skip: 0,
|
||||
take: 10,
|
||||
},
|
||||
@MedusaContext() context: SharedContext = {}
|
||||
): Promise<[InventoryLevelDTO[], number]> {
|
||||
return await this.inventoryLevelService_
|
||||
.withTransaction(this.activeManager_)
|
||||
.listAndCount(selector, config)
|
||||
return await this.inventoryLevelService_.listAndCount(
|
||||
selector,
|
||||
config,
|
||||
context
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -98,17 +106,21 @@ export default class InventoryService
|
||||
* @param config - the find configuration to use
|
||||
* @return A tuple of reservation items and their total count
|
||||
*/
|
||||
@InjectEntityManager()
|
||||
async listReservationItems(
|
||||
selector: FilterableReservationItemProps,
|
||||
config: FindConfig<ReservationItemDTO> = {
|
||||
relations: [],
|
||||
skip: 0,
|
||||
take: 10,
|
||||
}
|
||||
},
|
||||
@MedusaContext() context: SharedContext = {}
|
||||
): Promise<[ReservationItemDTO[], number]> {
|
||||
return await this.reservationItemService_
|
||||
.withTransaction(this.activeManager_)
|
||||
.listAndCount(selector, config)
|
||||
return await this.reservationItemService_.listAndCount(
|
||||
selector,
|
||||
config,
|
||||
context
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -117,13 +129,17 @@ export default class InventoryService
|
||||
* @param config - the find configuration to use
|
||||
* @return The retrieved inventory item
|
||||
*/
|
||||
@InjectEntityManager()
|
||||
async retrieveInventoryItem(
|
||||
inventoryItemId: string,
|
||||
config?: FindConfig<InventoryItemDTO>
|
||||
config?: FindConfig<InventoryItemDTO>,
|
||||
@MedusaContext() context: SharedContext = {}
|
||||
): Promise<InventoryItemDTO> {
|
||||
const inventoryItem = await this.inventoryItemService_
|
||||
.withTransaction(this.activeManager_)
|
||||
.retrieve(inventoryItemId, config)
|
||||
const inventoryItem = await this.inventoryItemService_.retrieve(
|
||||
inventoryItemId,
|
||||
config,
|
||||
context
|
||||
)
|
||||
return { ...inventoryItem }
|
||||
}
|
||||
|
||||
@@ -133,16 +149,17 @@ export default class InventoryService
|
||||
* @param locationId - the id of the location
|
||||
* @return the retrieved inventory level
|
||||
*/
|
||||
@InjectEntityManager()
|
||||
async retrieveInventoryLevel(
|
||||
inventoryItemId: string,
|
||||
locationId: string
|
||||
locationId: string,
|
||||
@MedusaContext() context: SharedContext = {}
|
||||
): Promise<InventoryLevelDTO> {
|
||||
const [inventoryLevel] = await this.inventoryLevelService_
|
||||
.withTransaction(this.activeManager_)
|
||||
.list(
|
||||
{ inventory_item_id: inventoryItemId, location_id: locationId },
|
||||
{ take: 1 }
|
||||
)
|
||||
const [inventoryLevel] = await this.inventoryLevelService_.list(
|
||||
{ inventory_item_id: inventoryItemId, location_id: locationId },
|
||||
{ take: 1 },
|
||||
context
|
||||
)
|
||||
if (!inventoryLevel) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_FOUND,
|
||||
@@ -152,15 +169,21 @@ export default class InventoryService
|
||||
return inventoryLevel
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Retrieves a reservation item
|
||||
* @param inventoryItemId - the id of the reservation item
|
||||
* @return the retrieved reservation level
|
||||
*/
|
||||
async retrieveReservationItem(reservationId: string): Promise<ReservationItemDTO> {
|
||||
return await this.reservationItemService_
|
||||
.withTransaction(this.activeManager_)
|
||||
.retrieve(reservationId)
|
||||
@InjectEntityManager()
|
||||
async retrieveReservationItem(
|
||||
reservationId: string,
|
||||
@MedusaContext() context: SharedContext = {}
|
||||
): Promise<ReservationItemDTO> {
|
||||
return await this.reservationItemService_.retrieve(
|
||||
reservationId,
|
||||
undefined,
|
||||
context
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -168,19 +191,20 @@ export default class InventoryService
|
||||
* @param input - the input object
|
||||
* @return The created reservation item
|
||||
*/
|
||||
@InjectEntityManager()
|
||||
async createReservationItem(
|
||||
input: CreateReservationItemInput
|
||||
input: CreateReservationItemInput,
|
||||
@MedusaContext() context: SharedContext = {}
|
||||
): Promise<ReservationItemDTO> {
|
||||
// Verify that the item is stocked at the location
|
||||
const [inventoryLevel] = await this.inventoryLevelService_
|
||||
.withTransaction(this.activeManager_)
|
||||
.list(
|
||||
{
|
||||
inventory_item_id: input.inventory_item_id,
|
||||
location_id: input.location_id,
|
||||
},
|
||||
{ take: 1 }
|
||||
)
|
||||
const [inventoryLevel] = await this.inventoryLevelService_.list(
|
||||
{
|
||||
inventory_item_id: input.inventory_item_id,
|
||||
location_id: input.location_id,
|
||||
},
|
||||
{ take: 1 },
|
||||
context
|
||||
)
|
||||
|
||||
if (!inventoryLevel) {
|
||||
throw new MedusaError(
|
||||
@@ -189,9 +213,10 @@ export default class InventoryService
|
||||
)
|
||||
}
|
||||
|
||||
const reservationItem = await this.reservationItemService_
|
||||
.withTransaction(this.activeManager_)
|
||||
.create(input)
|
||||
const reservationItem = await this.reservationItemService_.create(
|
||||
input,
|
||||
context
|
||||
)
|
||||
|
||||
return { ...reservationItem }
|
||||
}
|
||||
@@ -201,12 +226,15 @@ export default class InventoryService
|
||||
* @param input - the input object
|
||||
* @return The created inventory item
|
||||
*/
|
||||
@InjectEntityManager()
|
||||
async createInventoryItem(
|
||||
input: CreateInventoryItemInput
|
||||
input: CreateInventoryItemInput,
|
||||
@MedusaContext() context: SharedContext = {}
|
||||
): Promise<InventoryItemDTO> {
|
||||
const inventoryItem = await this.inventoryItemService_
|
||||
.withTransaction(this.activeManager_)
|
||||
.create(input)
|
||||
const inventoryItem = await this.inventoryItemService_.create(
|
||||
input,
|
||||
context
|
||||
)
|
||||
return { ...inventoryItem }
|
||||
}
|
||||
|
||||
@@ -215,12 +243,12 @@ export default class InventoryService
|
||||
* @param input - the input object
|
||||
* @return The created inventory level
|
||||
*/
|
||||
@InjectEntityManager()
|
||||
async createInventoryLevel(
|
||||
input: CreateInventoryLevelInput
|
||||
input: CreateInventoryLevelInput,
|
||||
@MedusaContext() context: SharedContext = {}
|
||||
): Promise<InventoryLevelDTO> {
|
||||
return await this.inventoryLevelService_
|
||||
.withTransaction(this.activeManager_)
|
||||
.create(input)
|
||||
return await this.inventoryLevelService_.create(input, context)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -229,13 +257,17 @@ export default class InventoryService
|
||||
* @param input - the input object
|
||||
* @return The updated inventory item
|
||||
*/
|
||||
@InjectEntityManager()
|
||||
async updateInventoryItem(
|
||||
inventoryItemId: string,
|
||||
input: Partial<CreateInventoryItemInput>
|
||||
input: Partial<CreateInventoryItemInput>,
|
||||
@MedusaContext() context: SharedContext = {}
|
||||
): Promise<InventoryItemDTO> {
|
||||
const inventoryItem = await this.inventoryItemService_
|
||||
.withTransaction(this.activeManager_)
|
||||
.update(inventoryItemId, input)
|
||||
const inventoryItem = await this.inventoryItemService_.update(
|
||||
inventoryItemId,
|
||||
input,
|
||||
context
|
||||
)
|
||||
return { ...inventoryItem }
|
||||
}
|
||||
|
||||
@@ -243,26 +275,39 @@ export default class InventoryService
|
||||
* Deletes an inventory item
|
||||
* @param inventoryItemId - the id of the inventory item to delete
|
||||
*/
|
||||
async deleteInventoryItem(inventoryItemId: string): Promise<void> {
|
||||
await this.inventoryLevelService_
|
||||
.withTransaction(this.activeManager_)
|
||||
.deleteByInventoryItemId(inventoryItemId)
|
||||
@InjectEntityManager()
|
||||
async deleteInventoryItem(
|
||||
inventoryItemId: string,
|
||||
@MedusaContext() context: SharedContext = {}
|
||||
): Promise<void> {
|
||||
await this.inventoryLevelService_.deleteByInventoryItemId(
|
||||
inventoryItemId,
|
||||
context
|
||||
)
|
||||
|
||||
return await this.inventoryItemService_
|
||||
.withTransaction(this.activeManager_)
|
||||
.delete(inventoryItemId)
|
||||
return await this.inventoryItemService_.delete(inventoryItemId, context)
|
||||
}
|
||||
|
||||
async deleteInventoryItemLevelByLocationId(locationId: string): Promise<void> {
|
||||
return await this.inventoryLevelService_
|
||||
.withTransaction(this.activeManager_)
|
||||
.deleteByLocationId(locationId)
|
||||
@InjectEntityManager()
|
||||
async deleteInventoryItemLevelByLocationId(
|
||||
locationId: string,
|
||||
@MedusaContext() context: SharedContext = {}
|
||||
): Promise<void> {
|
||||
return await this.inventoryLevelService_.deleteByLocationId(
|
||||
locationId,
|
||||
context
|
||||
)
|
||||
}
|
||||
|
||||
async deleteReservationItemByLocationId(locationId: string): Promise<void> {
|
||||
return await this.reservationItemService_
|
||||
.withTransaction(this.activeManager_)
|
||||
.deleteByLocationId(locationId)
|
||||
@InjectEntityManager()
|
||||
async deleteReservationItemByLocationId(
|
||||
locationId: string,
|
||||
@MedusaContext() context: SharedContext = {}
|
||||
): Promise<void> {
|
||||
return await this.reservationItemService_.deleteByLocationId(
|
||||
locationId,
|
||||
context
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -270,24 +315,23 @@ export default class InventoryService
|
||||
* @param inventoryItemId - the id of the inventory item associated with the level
|
||||
* @param locationId - the id of the location associated with the level
|
||||
*/
|
||||
@InjectEntityManager()
|
||||
async deleteInventoryLevel(
|
||||
inventoryItemId: string,
|
||||
locationId: string
|
||||
locationId: string,
|
||||
@MedusaContext() context: SharedContext = {}
|
||||
): Promise<void> {
|
||||
const [inventoryLevel] = await this.inventoryLevelService_
|
||||
.withTransaction(this.activeManager_)
|
||||
.list(
|
||||
{ inventory_item_id: inventoryItemId, location_id: locationId },
|
||||
{ take: 1 }
|
||||
)
|
||||
const [inventoryLevel] = await this.inventoryLevelService_.list(
|
||||
{ inventory_item_id: inventoryItemId, location_id: locationId },
|
||||
{ take: 1 },
|
||||
context
|
||||
)
|
||||
|
||||
if (!inventoryLevel) {
|
||||
return
|
||||
}
|
||||
|
||||
return await this.inventoryLevelService_
|
||||
.withTransaction(this.activeManager_)
|
||||
.delete(inventoryLevel.id)
|
||||
return await this.inventoryLevelService_.delete(inventoryLevel.id, context)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -297,17 +341,18 @@ export default class InventoryService
|
||||
* @param input - the input object
|
||||
* @return The updated inventory level
|
||||
*/
|
||||
@InjectEntityManager()
|
||||
async updateInventoryLevel(
|
||||
inventoryItemId: string,
|
||||
locationId: string,
|
||||
input: UpdateInventoryLevelInput
|
||||
input: UpdateInventoryLevelInput,
|
||||
@MedusaContext() context: SharedContext = {}
|
||||
): Promise<InventoryLevelDTO> {
|
||||
const [inventoryLevel] = await this.inventoryLevelService_
|
||||
.withTransaction(this.activeManager_)
|
||||
.list(
|
||||
{ inventory_item_id: inventoryItemId, location_id: locationId },
|
||||
{ take: 1 }
|
||||
)
|
||||
const [inventoryLevel] = await this.inventoryLevelService_.list(
|
||||
{ inventory_item_id: inventoryItemId, location_id: locationId },
|
||||
{ take: 1 },
|
||||
context
|
||||
)
|
||||
|
||||
if (!inventoryLevel) {
|
||||
throw new MedusaError(
|
||||
@@ -316,9 +361,11 @@ export default class InventoryService
|
||||
)
|
||||
}
|
||||
|
||||
return await this.inventoryLevelService_
|
||||
.withTransaction(this.activeManager_)
|
||||
.update(inventoryLevel.id, input)
|
||||
return await this.inventoryLevelService_.update(
|
||||
inventoryLevel.id,
|
||||
input,
|
||||
context
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -327,33 +374,44 @@ export default class InventoryService
|
||||
* @param input - the input object
|
||||
* @return The updated inventory level
|
||||
*/
|
||||
@InjectEntityManager()
|
||||
async updateReservationItem(
|
||||
reservationItemId: string,
|
||||
input: UpdateReservationItemInput
|
||||
input: UpdateReservationItemInput,
|
||||
@MedusaContext() context: SharedContext = {}
|
||||
): Promise<ReservationItemDTO> {
|
||||
return await this.reservationItemService_
|
||||
.withTransaction(this.activeManager_)
|
||||
.update(reservationItemId, input)
|
||||
return await this.reservationItemService_.update(
|
||||
reservationItemId,
|
||||
input,
|
||||
context
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes reservation items by line item
|
||||
* @param lineItemId - the id of the line item associated with the reservation item
|
||||
*/
|
||||
async deleteReservationItemsByLineItem(lineItemId: string): Promise<void> {
|
||||
return await this.reservationItemService_
|
||||
.withTransaction(this.activeManager_)
|
||||
.deleteByLineItem(lineItemId)
|
||||
@InjectEntityManager()
|
||||
async deleteReservationItemsByLineItem(
|
||||
lineItemId: string,
|
||||
@MedusaContext() context: SharedContext = {}
|
||||
): Promise<void> {
|
||||
return await this.reservationItemService_.deleteByLineItem(
|
||||
lineItemId,
|
||||
context
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a reservation item
|
||||
* @param reservationItemId - the id of the reservation item to delete
|
||||
*/
|
||||
async deleteReservationItem(reservationItemId: string | string[]): Promise<void> {
|
||||
return await this.reservationItemService_
|
||||
.withTransaction(this.activeManager_)
|
||||
.delete(reservationItemId)
|
||||
@InjectEntityManager()
|
||||
async deleteReservationItem(
|
||||
reservationItemId: string | string[],
|
||||
@MedusaContext() context: SharedContext = {}
|
||||
): Promise<void> {
|
||||
return await this.reservationItemService_.delete(reservationItemId, context)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -364,17 +422,18 @@ export default class InventoryService
|
||||
* @return The updated inventory level
|
||||
* @throws when the inventory level is not found
|
||||
*/
|
||||
@InjectEntityManager()
|
||||
async adjustInventory(
|
||||
inventoryItemId: string,
|
||||
locationId: string,
|
||||
adjustment: number
|
||||
adjustment: number,
|
||||
@MedusaContext() context: SharedContext = {}
|
||||
): Promise<InventoryLevelDTO> {
|
||||
const [inventoryLevel] = await this.inventoryLevelService_
|
||||
.withTransaction(this.activeManager_)
|
||||
.list(
|
||||
{ inventory_item_id: inventoryItemId, location_id: locationId },
|
||||
{ take: 1 }
|
||||
)
|
||||
const [inventoryLevel] = await this.inventoryLevelService_.list(
|
||||
{ inventory_item_id: inventoryItemId, location_id: locationId },
|
||||
{ take: 1 },
|
||||
context
|
||||
)
|
||||
if (!inventoryLevel) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_FOUND,
|
||||
@@ -382,11 +441,13 @@ export default class InventoryService
|
||||
)
|
||||
}
|
||||
|
||||
const updatedInventoryLevel = await this.inventoryLevelService_
|
||||
.withTransaction(this.activeManager_)
|
||||
.update(inventoryLevel.id, {
|
||||
const updatedInventoryLevel = await this.inventoryLevelService_.update(
|
||||
inventoryLevel.id,
|
||||
{
|
||||
stocked_quantity: inventoryLevel.stocked_quantity + adjustment,
|
||||
})
|
||||
},
|
||||
context
|
||||
)
|
||||
|
||||
return { ...updatedInventoryLevel }
|
||||
}
|
||||
@@ -398,20 +459,27 @@ export default class InventoryService
|
||||
* @return The available quantity
|
||||
* @throws when the inventory item is not found
|
||||
*/
|
||||
@InjectEntityManager()
|
||||
async retrieveAvailableQuantity(
|
||||
inventoryItemId: string,
|
||||
locationIds: string[]
|
||||
locationIds: string[],
|
||||
@MedusaContext() context: SharedContext = {}
|
||||
): Promise<number> {
|
||||
// Throws if item does not exist
|
||||
await this.inventoryItemService_
|
||||
.withTransaction(this.activeManager_)
|
||||
.retrieve(inventoryItemId, {
|
||||
await this.inventoryItemService_.retrieve(
|
||||
inventoryItemId,
|
||||
{
|
||||
select: ["id"],
|
||||
})
|
||||
},
|
||||
context
|
||||
)
|
||||
|
||||
const availableQuantity = await this.inventoryLevelService_
|
||||
.withTransaction(this.activeManager_)
|
||||
.getAvailableQuantity(inventoryItemId, locationIds)
|
||||
const availableQuantity =
|
||||
await this.inventoryLevelService_.getAvailableQuantity(
|
||||
inventoryItemId,
|
||||
locationIds,
|
||||
context
|
||||
)
|
||||
|
||||
return availableQuantity
|
||||
}
|
||||
@@ -423,20 +491,27 @@ export default class InventoryService
|
||||
* @return The stocked quantity
|
||||
* @throws when the inventory item is not found
|
||||
*/
|
||||
@InjectEntityManager()
|
||||
async retrieveStockedQuantity(
|
||||
inventoryItemId: string,
|
||||
locationIds: string[]
|
||||
locationIds: string[],
|
||||
@MedusaContext() context: SharedContext = {}
|
||||
): Promise<number> {
|
||||
// Throws if item does not exist
|
||||
await this.inventoryItemService_
|
||||
.withTransaction(this.activeManager_)
|
||||
.retrieve(inventoryItemId, {
|
||||
await this.inventoryItemService_.retrieve(
|
||||
inventoryItemId,
|
||||
{
|
||||
select: ["id"],
|
||||
})
|
||||
},
|
||||
context
|
||||
)
|
||||
|
||||
const stockedQuantity = await this.inventoryLevelService_
|
||||
.withTransaction(this.activeManager_)
|
||||
.getStockedQuantity(inventoryItemId, locationIds)
|
||||
const stockedQuantity =
|
||||
await this.inventoryLevelService_.getStockedQuantity(
|
||||
inventoryItemId,
|
||||
locationIds,
|
||||
context
|
||||
)
|
||||
|
||||
return stockedQuantity
|
||||
}
|
||||
@@ -448,20 +523,27 @@ export default class InventoryService
|
||||
* @return The reserved quantity
|
||||
* @throws when the inventory item is not found
|
||||
*/
|
||||
@InjectEntityManager()
|
||||
async retrieveReservedQuantity(
|
||||
inventoryItemId: string,
|
||||
locationIds: string[]
|
||||
locationIds: string[],
|
||||
@MedusaContext() context: SharedContext = {}
|
||||
): Promise<number> {
|
||||
// Throws if item does not exist
|
||||
await this.inventoryItemService_
|
||||
.withTransaction(this.activeManager_)
|
||||
.retrieve(inventoryItemId, {
|
||||
await this.inventoryItemService_.retrieve(
|
||||
inventoryItemId,
|
||||
{
|
||||
select: ["id"],
|
||||
})
|
||||
},
|
||||
context
|
||||
)
|
||||
|
||||
const reservedQuantity = await this.inventoryLevelService_
|
||||
.withTransaction(this.activeManager_)
|
||||
.getReservedQuantity(inventoryItemId, locationIds)
|
||||
const reservedQuantity =
|
||||
await this.inventoryLevelService_.getReservedQuantity(
|
||||
inventoryItemId,
|
||||
locationIds,
|
||||
context
|
||||
)
|
||||
|
||||
return reservedQuantity
|
||||
}
|
||||
@@ -473,14 +555,17 @@ export default class InventoryService
|
||||
* @param quantity - the quantity to check
|
||||
* @return Whether there is sufficient inventory
|
||||
*/
|
||||
@InjectEntityManager()
|
||||
async confirmInventory(
|
||||
inventoryItemId: string,
|
||||
locationIds: string[],
|
||||
quantity: number
|
||||
quantity: number,
|
||||
@MedusaContext() context: SharedContext = {}
|
||||
): Promise<boolean> {
|
||||
const availableQuantity = await this.retrieveAvailableQuantity(
|
||||
inventoryItemId,
|
||||
locationIds
|
||||
locationIds,
|
||||
context
|
||||
)
|
||||
return availableQuantity >= quantity
|
||||
}
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import { EntityManager, FindManyOptions } from "typeorm"
|
||||
import { isDefined, MedusaError } from "medusa-core-utils"
|
||||
import {
|
||||
buildQuery,
|
||||
CreateReservationItemInput,
|
||||
FilterableReservationItemProps,
|
||||
FindConfig,
|
||||
IEventBusService,
|
||||
TransactionBaseService,
|
||||
UpdateReservationItemInput,
|
||||
} from "@medusajs/medusa"
|
||||
|
||||
import { ReservationItem } from "../models"
|
||||
import { SharedContext } from "@medusajs/types"
|
||||
import { InjectEntityManager, MedusaContext } from "@medusajs/utils"
|
||||
import { isDefined, MedusaError } from "medusa-core-utils"
|
||||
import { EntityManager, FindManyOptions } from "typeorm"
|
||||
import { InventoryLevelService } from "."
|
||||
import { ReservationItem } from "../models"
|
||||
|
||||
type InjectedDependencies = {
|
||||
eventBusService: IEventBusService
|
||||
@@ -19,22 +19,23 @@ type InjectedDependencies = {
|
||||
inventoryLevelService: InventoryLevelService
|
||||
}
|
||||
|
||||
export default class ReservationItemService extends TransactionBaseService {
|
||||
export default class ReservationItemService {
|
||||
static Events = {
|
||||
CREATED: "reservation-item.created",
|
||||
UPDATED: "reservation-item.updated",
|
||||
DELETED: "reservation-item.deleted",
|
||||
}
|
||||
|
||||
protected readonly eventBusService_: IEventBusService
|
||||
protected readonly manager_: EntityManager
|
||||
protected readonly eventBusService_: IEventBusService | undefined
|
||||
protected readonly inventoryLevelService_: InventoryLevelService
|
||||
|
||||
constructor({
|
||||
eventBusService,
|
||||
inventoryLevelService,
|
||||
manager,
|
||||
}: InjectedDependencies) {
|
||||
super(arguments[0])
|
||||
|
||||
this.manager_ = manager
|
||||
this.eventBusService_ = eventBusService
|
||||
this.inventoryLevelService_ = inventoryLevelService
|
||||
}
|
||||
@@ -45,11 +46,13 @@ export default class ReservationItemService extends TransactionBaseService {
|
||||
* @param config - Configuration for the query.
|
||||
* @return Array of reservation items that match the selector.
|
||||
*/
|
||||
@InjectEntityManager()
|
||||
async list(
|
||||
selector: FilterableReservationItemProps = {},
|
||||
config: FindConfig<ReservationItem> = { relations: [], skip: 0, take: 10 }
|
||||
config: FindConfig<ReservationItem> = { relations: [], skip: 0, take: 10 },
|
||||
@MedusaContext() context: SharedContext = {}
|
||||
): Promise<ReservationItem[]> {
|
||||
const manager = this.activeManager_
|
||||
const manager = context.transactionManager!
|
||||
const itemRepository = manager.getRepository(ReservationItem)
|
||||
|
||||
const query = buildQuery(selector, config) as FindManyOptions
|
||||
@@ -62,11 +65,13 @@ export default class ReservationItemService extends TransactionBaseService {
|
||||
* @param config - Configuration for the query.
|
||||
* @return Array of reservation items that match the selector and the total count.
|
||||
*/
|
||||
@InjectEntityManager()
|
||||
async listAndCount(
|
||||
selector: FilterableReservationItemProps = {},
|
||||
config: FindConfig<ReservationItem> = { relations: [], skip: 0, take: 10 }
|
||||
config: FindConfig<ReservationItem> = { relations: [], skip: 0, take: 10 },
|
||||
@MedusaContext() context: SharedContext = {}
|
||||
): Promise<[ReservationItem[], number]> {
|
||||
const manager = this.activeManager_
|
||||
const manager = context.transactionManager!
|
||||
const itemRepository = manager.getRepository(ReservationItem)
|
||||
|
||||
const query = buildQuery(selector, config) as FindManyOptions
|
||||
@@ -80,9 +85,11 @@ export default class ReservationItemService extends TransactionBaseService {
|
||||
* @return The reservation item with the provided id.
|
||||
* @throws If reservationItemId is not defined or if the reservation item was not found.
|
||||
*/
|
||||
@InjectEntityManager()
|
||||
async retrieve(
|
||||
reservationItemId: string,
|
||||
config: FindConfig<ReservationItem> = {}
|
||||
config: FindConfig<ReservationItem> = {},
|
||||
@MedusaContext() context: SharedContext = {}
|
||||
): Promise<ReservationItem> {
|
||||
if (!isDefined(reservationItemId)) {
|
||||
throw new MedusaError(
|
||||
@@ -91,7 +98,7 @@ export default class ReservationItemService extends TransactionBaseService {
|
||||
)
|
||||
}
|
||||
|
||||
const manager = this.activeManager_
|
||||
const manager = context.transactionManager!
|
||||
const reservationItemRepository = manager.getRepository(ReservationItem)
|
||||
|
||||
const query = buildQuery(
|
||||
@@ -115,37 +122,37 @@ export default class ReservationItemService extends TransactionBaseService {
|
||||
* @param data - The reservation item data.
|
||||
* @return The created reservation item.
|
||||
*/
|
||||
async create(data: CreateReservationItemInput): Promise<ReservationItem> {
|
||||
return await this.atomicPhase_(async (manager) => {
|
||||
const itemRepository = manager.getRepository(ReservationItem)
|
||||
@InjectEntityManager()
|
||||
async create(
|
||||
data: CreateReservationItemInput,
|
||||
@MedusaContext() context: SharedContext = {}
|
||||
): Promise<ReservationItem> {
|
||||
const manager = context.transactionManager!
|
||||
const itemRepository = manager.getRepository(ReservationItem)
|
||||
|
||||
const inventoryItem = itemRepository.create({
|
||||
inventory_item_id: data.inventory_item_id,
|
||||
line_item_id: data.line_item_id,
|
||||
location_id: data.location_id,
|
||||
quantity: data.quantity,
|
||||
metadata: data.metadata,
|
||||
})
|
||||
|
||||
const [newInventoryItem] = await Promise.all([
|
||||
itemRepository.save(inventoryItem),
|
||||
this.inventoryLevelService_
|
||||
.withTransaction(manager)
|
||||
.adjustReservedQuantity(
|
||||
data.inventory_item_id,
|
||||
data.location_id,
|
||||
data.quantity
|
||||
),
|
||||
])
|
||||
|
||||
await this.eventBusService_
|
||||
.withTransaction(manager)
|
||||
.emit(ReservationItemService.Events.CREATED, {
|
||||
id: newInventoryItem.id,
|
||||
})
|
||||
|
||||
return newInventoryItem
|
||||
const inventoryItem = itemRepository.create({
|
||||
inventory_item_id: data.inventory_item_id,
|
||||
line_item_id: data.line_item_id,
|
||||
location_id: data.location_id,
|
||||
quantity: data.quantity,
|
||||
metadata: data.metadata,
|
||||
})
|
||||
|
||||
const [newInventoryItem] = await Promise.all([
|
||||
itemRepository.save(inventoryItem),
|
||||
this.inventoryLevelService_.adjustReservedQuantity(
|
||||
data.inventory_item_id,
|
||||
data.location_id,
|
||||
data.quantity,
|
||||
context
|
||||
),
|
||||
])
|
||||
|
||||
await this.eventBusService_?.emit?.(ReservationItemService.Events.CREATED, {
|
||||
id: newInventoryItem.id,
|
||||
})
|
||||
|
||||
return newInventoryItem
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -154,99 +161,99 @@ export default class ReservationItemService extends TransactionBaseService {
|
||||
* @param data - The reservation item data to update.
|
||||
* @return The updated reservation item.
|
||||
*/
|
||||
@InjectEntityManager()
|
||||
async update(
|
||||
reservationItemId: string,
|
||||
data: UpdateReservationItemInput
|
||||
data: UpdateReservationItemInput,
|
||||
@MedusaContext() context: SharedContext = {}
|
||||
): Promise<ReservationItem> {
|
||||
return await this.atomicPhase_(async (manager) => {
|
||||
const itemRepository = manager.getRepository(ReservationItem)
|
||||
const manager = context.transactionManager!
|
||||
const itemRepository = manager.getRepository(ReservationItem)
|
||||
|
||||
const item = await this.retrieve(reservationItemId)
|
||||
const item = await this.retrieve(reservationItemId)
|
||||
|
||||
const shouldUpdateQuantity =
|
||||
isDefined(data.quantity) && data.quantity !== item.quantity
|
||||
const shouldUpdateQuantity =
|
||||
isDefined(data.quantity) && data.quantity !== item.quantity
|
||||
|
||||
const shouldUpdateLocation =
|
||||
isDefined(data.location_id) && data.location_id !== item.location_id
|
||||
const shouldUpdateLocation =
|
||||
isDefined(data.location_id) && data.location_id !== item.location_id
|
||||
|
||||
const ops: Promise<unknown>[] = []
|
||||
const ops: Promise<unknown>[] = []
|
||||
|
||||
if (shouldUpdateLocation) {
|
||||
ops.push(
|
||||
this.inventoryLevelService_
|
||||
.withTransaction(manager)
|
||||
.adjustReservedQuantity(
|
||||
item.inventory_item_id,
|
||||
item.location_id,
|
||||
item.quantity * -1
|
||||
),
|
||||
this.inventoryLevelService_
|
||||
.withTransaction(manager)
|
||||
.adjustReservedQuantity(
|
||||
item.inventory_item_id,
|
||||
data.location_id!,
|
||||
data.quantity || item.quantity!
|
||||
)
|
||||
if (shouldUpdateLocation) {
|
||||
ops.push(
|
||||
this.inventoryLevelService_.adjustReservedQuantity(
|
||||
item.inventory_item_id,
|
||||
item.location_id,
|
||||
item.quantity * -1,
|
||||
context
|
||||
),
|
||||
this.inventoryLevelService_.adjustReservedQuantity(
|
||||
item.inventory_item_id,
|
||||
data.location_id!,
|
||||
data.quantity || item.quantity!,
|
||||
context
|
||||
)
|
||||
} else if (shouldUpdateQuantity) {
|
||||
const quantityDiff = data.quantity! - item.quantity
|
||||
ops.push(
|
||||
this.inventoryLevelService_
|
||||
.withTransaction(manager)
|
||||
.adjustReservedQuantity(
|
||||
item.inventory_item_id,
|
||||
item.location_id,
|
||||
quantityDiff
|
||||
)
|
||||
)
|
||||
} else if (shouldUpdateQuantity) {
|
||||
const quantityDiff = data.quantity! - item.quantity
|
||||
ops.push(
|
||||
this.inventoryLevelService_.adjustReservedQuantity(
|
||||
item.inventory_item_id,
|
||||
item.location_id,
|
||||
quantityDiff,
|
||||
context
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
const mergedItem = itemRepository.merge(item, data)
|
||||
const mergedItem = itemRepository.merge(item, data)
|
||||
|
||||
ops.push(itemRepository.save(item))
|
||||
ops.push(itemRepository.save(item))
|
||||
|
||||
await Promise.all(ops)
|
||||
await Promise.all(ops)
|
||||
|
||||
await this.eventBusService_
|
||||
.withTransaction(manager)
|
||||
.emit(ReservationItemService.Events.UPDATED, {
|
||||
id: mergedItem.id,
|
||||
})
|
||||
|
||||
return mergedItem
|
||||
await this.eventBusService_?.emit?.(ReservationItemService.Events.UPDATED, {
|
||||
id: mergedItem.id,
|
||||
})
|
||||
|
||||
return mergedItem
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a reservation item by line item id.
|
||||
* @param lineItemId - the id of the line item to delete.
|
||||
*/
|
||||
async deleteByLineItem(lineItemId: string): Promise<void> {
|
||||
await this.atomicPhase_(async (manager) => {
|
||||
const itemRepository = manager.getRepository(ReservationItem)
|
||||
@InjectEntityManager()
|
||||
async deleteByLineItem(
|
||||
lineItemId: string,
|
||||
@MedusaContext() context: SharedContext = {}
|
||||
): Promise<void> {
|
||||
const manager = context.transactionManager!
|
||||
const itemRepository = manager.getRepository(ReservationItem)
|
||||
|
||||
const items = await this.list({ line_item_id: lineItemId })
|
||||
const items = await this.list(
|
||||
{ line_item_id: lineItemId },
|
||||
undefined,
|
||||
context
|
||||
)
|
||||
|
||||
const ops: Promise<unknown>[] = []
|
||||
for (const item of items) {
|
||||
ops.push(itemRepository.softRemove({ line_item_id: lineItemId }))
|
||||
ops.push(
|
||||
this.inventoryLevelService_
|
||||
.withTransaction(manager)
|
||||
.adjustReservedQuantity(
|
||||
item.inventory_item_id,
|
||||
item.location_id,
|
||||
item.quantity * -1
|
||||
)
|
||||
const ops: Promise<unknown>[] = []
|
||||
for (const item of items) {
|
||||
ops.push(itemRepository.softRemove({ line_item_id: lineItemId }))
|
||||
ops.push(
|
||||
this.inventoryLevelService_.adjustReservedQuantity(
|
||||
item.inventory_item_id,
|
||||
item.location_id,
|
||||
item.quantity * -1,
|
||||
context
|
||||
)
|
||||
}
|
||||
await Promise.all(ops)
|
||||
)
|
||||
}
|
||||
await Promise.all(ops)
|
||||
|
||||
await this.eventBusService_
|
||||
.withTransaction(manager)
|
||||
.emit(ReservationItemService.Events.DELETED, {
|
||||
line_item_id: lineItemId,
|
||||
})
|
||||
await this.eventBusService_?.emit?.(ReservationItemService.Events.DELETED, {
|
||||
line_item_id: lineItemId,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -254,22 +261,23 @@ export default class ReservationItemService extends TransactionBaseService {
|
||||
* Deletes reservation items by location ID.
|
||||
* @param locationId - The ID of the location to delete reservations for.
|
||||
*/
|
||||
async deleteByLocationId(locationId: string): Promise<void> {
|
||||
return await this.atomicPhase_(async (manager) => {
|
||||
const itemRepository = manager.getRepository(ReservationItem)
|
||||
@InjectEntityManager()
|
||||
async deleteByLocationId(
|
||||
locationId: string,
|
||||
@MedusaContext() context: SharedContext = {}
|
||||
): Promise<void> {
|
||||
const manager = context.transactionManager!
|
||||
const itemRepository = manager.getRepository(ReservationItem)
|
||||
|
||||
await itemRepository
|
||||
.createQueryBuilder("reservation_item")
|
||||
.softDelete()
|
||||
.where("location_id = :locationId", { locationId })
|
||||
.andWhere("deleted_at IS NULL")
|
||||
.execute()
|
||||
await itemRepository
|
||||
.createQueryBuilder("reservation_item")
|
||||
.softDelete()
|
||||
.where("location_id = :locationId", { locationId })
|
||||
.andWhere("deleted_at IS NULL")
|
||||
.execute()
|
||||
|
||||
await this.eventBusService_
|
||||
.withTransaction(manager)
|
||||
.emit(ReservationItemService.Events.DELETED, {
|
||||
location_id: locationId,
|
||||
})
|
||||
await this.eventBusService_?.emit?.(ReservationItemService.Events.DELETED, {
|
||||
location_id: locationId,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -277,35 +285,32 @@ export default class ReservationItemService extends TransactionBaseService {
|
||||
* Deletes a reservation item by id.
|
||||
* @param reservationItemId - the id of the reservation item to delete.
|
||||
*/
|
||||
async delete(reservationItemId: string | string[]): Promise<void> {
|
||||
async delete(
|
||||
reservationItemId: string | string[],
|
||||
@MedusaContext() context: SharedContext = {}
|
||||
): Promise<void> {
|
||||
const ids = Array.isArray(reservationItemId)
|
||||
? reservationItemId
|
||||
: [reservationItemId]
|
||||
return await this.atomicPhase_(async (manager) => {
|
||||
const itemRepository = manager.getRepository(ReservationItem)
|
||||
const manager = context.transactionManager!
|
||||
const itemRepository = manager.getRepository(ReservationItem)
|
||||
const items = await this.list({ id: ids })
|
||||
|
||||
const items = await this.list({ id: ids })
|
||||
|
||||
await itemRepository.softRemove(items)
|
||||
|
||||
const inventoryServiceTx =
|
||||
this.inventoryLevelService_.withTransaction(manager)
|
||||
|
||||
await Promise.all(
|
||||
items.map(async (item) => {
|
||||
return inventoryServiceTx.adjustReservedQuantity(
|
||||
item.inventory_item_id,
|
||||
item.location_id,
|
||||
item.quantity * -1
|
||||
)
|
||||
})
|
||||
const promises: Promise<unknown>[] = items.map(async (item) => {
|
||||
this.inventoryLevelService_.adjustReservedQuantity(
|
||||
item.inventory_item_id,
|
||||
item.location_id,
|
||||
item.quantity * -1,
|
||||
context
|
||||
)
|
||||
})
|
||||
|
||||
await this.eventBusService_
|
||||
.withTransaction(manager)
|
||||
.emit(ReservationItemService.Events.DELETED, {
|
||||
id: reservationItemId,
|
||||
})
|
||||
promises.push(itemRepository.softRemove(items))
|
||||
|
||||
await Promise.all(promises)
|
||||
|
||||
await this.eventBusService_?.emit?.(ReservationItemService.Events.DELETED, {
|
||||
id: reservationItemId,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
import { DatabaseType } from "typeorm"
|
||||
|
||||
export type InventoryServiceInitializeOptions = {
|
||||
database?: {
|
||||
type?: DatabaseType | string
|
||||
url?: string
|
||||
database?: string
|
||||
extra?: Record<string, any>
|
||||
schema?: string
|
||||
logging?: boolean
|
||||
}
|
||||
}
|
||||
@@ -85,9 +85,6 @@ export default async (req, res) => {
|
||||
const orderServiceTx = orderService.withTransaction(manager)
|
||||
const cartServiceTx = cartService.withTransaction(manager)
|
||||
|
||||
const productVariantInventoryServiceTx =
|
||||
productVariantInventoryService.withTransaction(manager)
|
||||
|
||||
const draftOrder = await draftOrderServiceTx.retrieve(id)
|
||||
|
||||
const cart = await cartServiceTx.retrieveWithTotals(draftOrder.cart_id)
|
||||
@@ -116,7 +113,7 @@ export default async (req, res) => {
|
||||
})
|
||||
|
||||
await reserveQuantityForDraftOrder(order, {
|
||||
productVariantInventoryService: productVariantInventoryServiceTx,
|
||||
productVariantInventoryService,
|
||||
})
|
||||
|
||||
return order
|
||||
|
||||
@@ -59,9 +59,7 @@ export default async (req: Request, res: Response) => {
|
||||
.withTransaction(transactionManager)
|
||||
.detachInventoryItem(id)
|
||||
|
||||
await inventoryService
|
||||
.withTransaction(transactionManager)
|
||||
.deleteInventoryItem(id)
|
||||
await inventoryService.deleteInventoryItem(id)
|
||||
})
|
||||
|
||||
res.status(200).send({
|
||||
|
||||
@@ -74,11 +74,7 @@ export default async (req: Request, res: Response) => {
|
||||
)
|
||||
}
|
||||
|
||||
await manager.transaction(async (transactionManager) => {
|
||||
await inventoryService
|
||||
.withTransaction(transactionManager)
|
||||
.deleteInventoryLevel(id, location_id)
|
||||
})
|
||||
await inventoryService.deleteInventoryLevel(id, location_id)
|
||||
|
||||
const inventoryItem = await inventoryService.retrieveInventoryItem(
|
||||
id,
|
||||
|
||||
@@ -77,14 +77,10 @@ export default async (req: Request, res: Response) => {
|
||||
req.scope.resolve("inventoryService")
|
||||
const manager: EntityManager = req.scope.resolve("manager")
|
||||
|
||||
await manager.transaction(async (transactionManager) => {
|
||||
await inventoryService
|
||||
.withTransaction(transactionManager)
|
||||
.updateInventoryItem(
|
||||
id,
|
||||
req.validatedBody as AdminPostInventoryItemsInventoryItemReq
|
||||
)
|
||||
})
|
||||
await inventoryService.updateInventoryItem(
|
||||
id,
|
||||
req.validatedBody as AdminPostInventoryItemsInventoryItemReq
|
||||
)
|
||||
|
||||
const inventoryItem = await inventoryService.retrieveInventoryItem(
|
||||
id,
|
||||
|
||||
@@ -81,11 +81,7 @@ export default async (req: Request, res: Response) => {
|
||||
const validatedBody =
|
||||
req.validatedBody as AdminPostInventoryItemsItemLocationLevelsLevelReq
|
||||
|
||||
await manager.transaction(async (transactionManager) => {
|
||||
await inventoryService
|
||||
.withTransaction(transactionManager)
|
||||
.updateInventoryLevel(id, location_id, validatedBody)
|
||||
})
|
||||
await inventoryService.updateInventoryLevel(id, location_id, validatedBody)
|
||||
|
||||
const inventoryItem = await inventoryService.retrieveInventoryItem(
|
||||
id,
|
||||
|
||||
+11
-11
@@ -1,3 +1,14 @@
|
||||
import { MedusaError } from "medusa-core-utils"
|
||||
import { EntityManager } from "typeorm"
|
||||
import { ulid } from "ulid"
|
||||
import { IInventoryService } from "../../../../../interfaces"
|
||||
import { ProductVariant } from "../../../../../models"
|
||||
import {
|
||||
ProductVariantInventoryService,
|
||||
ProductVariantService,
|
||||
} from "../../../../../services"
|
||||
import { InventoryItemDTO } from "../../../../../types/inventory"
|
||||
import { CreateProductVariantInput } from "../../../../../types/product-variant"
|
||||
import {
|
||||
DistributedTransaction,
|
||||
TransactionHandlerType,
|
||||
@@ -6,17 +17,6 @@ import {
|
||||
TransactionState,
|
||||
TransactionStepsDefinition,
|
||||
} from "../../../../../utils/transaction"
|
||||
import { ulid } from "ulid"
|
||||
import { EntityManager } from "typeorm"
|
||||
import { IInventoryService } from "../../../../../interfaces"
|
||||
import {
|
||||
ProductVariantInventoryService,
|
||||
ProductVariantService,
|
||||
} from "../../../../../services"
|
||||
import { CreateProductVariantInput } from "../../../../../types/product-variant"
|
||||
import { InventoryItemDTO } from "../../../../../types/inventory"
|
||||
import { ProductVariant } from "../../../../../models"
|
||||
import { MedusaError } from "medusa-core-utils"
|
||||
|
||||
enum actions {
|
||||
createVariant = "createVariant",
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { IsNumber, IsObject, IsOptional, IsString } from "class-validator"
|
||||
import { isDefined } from "medusa-core-utils"
|
||||
import { EntityManager } from "typeorm"
|
||||
import { IInventoryService } from "../../../../interfaces"
|
||||
import { validateUpdateReservationQuantity } from "./utils/validate-reservation-quantity"
|
||||
|
||||
@@ -66,8 +65,6 @@ import { validateUpdateReservationQuantity } from "./utils/validate-reservation-
|
||||
export default async (req, res) => {
|
||||
const { validatedBody } = req as { validatedBody: AdminPostReservationsReq }
|
||||
|
||||
const manager: EntityManager = req.scope.resolve("manager")
|
||||
|
||||
const inventoryService: IInventoryService =
|
||||
req.scope.resolve("inventoryService")
|
||||
|
||||
@@ -82,11 +79,9 @@ export default async (req, res) => {
|
||||
)
|
||||
}
|
||||
|
||||
const reservation = await manager.transaction(async (manager) => {
|
||||
return await inventoryService
|
||||
.withTransaction(manager)
|
||||
.createReservationItem(validatedBody)
|
||||
})
|
||||
const reservation = await inventoryService.createReservationItem(
|
||||
validatedBody
|
||||
)
|
||||
|
||||
res.status(200).json({ reservation })
|
||||
}
|
||||
|
||||
@@ -58,9 +58,7 @@ export default async (req, res) => {
|
||||
req.scope.resolve("inventoryService")
|
||||
const manager: EntityManager = req.scope.resolve("manager")
|
||||
|
||||
await manager.transaction(async (manager) => {
|
||||
await inventoryService.withTransaction(manager).deleteReservationItem(id)
|
||||
})
|
||||
await inventoryService.deleteReservationItem(id)
|
||||
|
||||
res.json({
|
||||
id,
|
||||
|
||||
@@ -91,9 +91,7 @@ export default async (req, res) => {
|
||||
}
|
||||
|
||||
const result = await manager.transaction(async (manager) => {
|
||||
await inventoryService
|
||||
.withTransaction(manager)
|
||||
.updateReservationItem(id, validatedBody)
|
||||
await inventoryService.updateReservationItem(id, validatedBody)
|
||||
})
|
||||
|
||||
res.status(200).json({ reservation: result })
|
||||
|
||||
@@ -79,12 +79,8 @@ export default async (req, res) => {
|
||||
|
||||
if (inventoryService) {
|
||||
await Promise.all([
|
||||
inventoryService
|
||||
.withTransaction(transactionManager)
|
||||
.deleteInventoryItemLevelByLocationId(id),
|
||||
inventoryService
|
||||
.withTransaction(transactionManager)
|
||||
.deleteReservationItemByLocationId(id),
|
||||
inventoryService.deleteInventoryItemLevelByLocationId(id),
|
||||
inventoryService.deleteReservationItemByLocationId(id),
|
||||
])
|
||||
}
|
||||
})
|
||||
|
||||
@@ -3,7 +3,11 @@ import featureFlagLoader from "../loaders/feature-flags"
|
||||
import Logger from "../loaders/logger"
|
||||
import databaseLoader from "../loaders/database"
|
||||
import configModuleLoader from "../loaders/config"
|
||||
import getMigrations, { getModuleSharedResources } from "./utils/get-migrations"
|
||||
import getMigrations, {
|
||||
getModuleSharedResources,
|
||||
revertIsolatedModulesMigration,
|
||||
runIsolatedModulesMigration,
|
||||
} from "./utils/get-migrations"
|
||||
|
||||
const getDataSource = async (directory) => {
|
||||
const configModule = configModuleLoader(directory)
|
||||
@@ -34,16 +38,19 @@ const main = async function ({ directory }) {
|
||||
args.shift()
|
||||
args.shift()
|
||||
|
||||
const configModule = configModuleLoader(directory)
|
||||
const dataSource = await getDataSource(directory)
|
||||
|
||||
if (args[0] === "run") {
|
||||
await dataSource.runMigrations()
|
||||
await dataSource.destroy()
|
||||
await runIsolatedModulesMigration(configModule)
|
||||
Logger.info("Migrations completed.")
|
||||
process.exit()
|
||||
} else if (args[0] === "revert") {
|
||||
await dataSource.undoLastMigration({ transaction: "all" })
|
||||
await dataSource.destroy()
|
||||
await revertIsolatedModulesMigration(configModule)
|
||||
Logger.info("Migrations reverted.")
|
||||
process.exit()
|
||||
} else if (args[0] === "show") {
|
||||
|
||||
@@ -4,7 +4,7 @@ import { sync as existsSync } from "fs-exists-cached"
|
||||
import { getConfigFile } from "medusa-core-utils"
|
||||
import { track } from "medusa-telemetry"
|
||||
import path from "path"
|
||||
import { ConnectionOptions, createConnection } from "typeorm"
|
||||
import { DataSource, DataSourceOptions } from "typeorm"
|
||||
|
||||
import loaders from "../loaders"
|
||||
import { handleConfigError } from "../loaders/config"
|
||||
@@ -73,12 +73,14 @@ const seed = async function ({ directory, migrate, seedFile }: SeedOptions) {
|
||||
extra: configModule.projectConfig.database_extra || {},
|
||||
migrations: coreMigrations.concat(moduleMigrations),
|
||||
logging: true,
|
||||
} as ConnectionOptions
|
||||
} as DataSourceOptions
|
||||
|
||||
const connection = await createConnection(connectionOptions)
|
||||
const connection = new DataSource(connectionOptions)
|
||||
|
||||
await connection.initialize()
|
||||
await connection.runMigrations()
|
||||
await connection.close()
|
||||
await connection.destroy()
|
||||
|
||||
Logger.info("Migrations completed.")
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { registerModules } from "@medusajs/modules-sdk"
|
||||
import { MedusaModule, registerModules } from "@medusajs/modules-sdk"
|
||||
import fs from "fs"
|
||||
import { sync as existsSync } from "fs-exists-cached"
|
||||
import glob from "glob"
|
||||
@@ -96,7 +96,7 @@ function resolvePlugin(pluginName) {
|
||||
export function getInternalModules(configModule) {
|
||||
const modules = []
|
||||
|
||||
const moduleResolutions = registerModules(configModule)
|
||||
const moduleResolutions = registerModules(configModule.modules)
|
||||
|
||||
for (const moduleResolution of Object.values(moduleResolutions)) {
|
||||
if (
|
||||
@@ -200,7 +200,6 @@ export const getModuleMigrations = (configModule, isFlagEnabled) => {
|
||||
for (const loadedModule of loadedModules) {
|
||||
const mod = loadedModule.loadedModule
|
||||
|
||||
const isolatedMigrations = {}
|
||||
const moduleMigrations = (mod.migrations ?? [])
|
||||
.map((migration) => {
|
||||
if (
|
||||
@@ -218,7 +217,6 @@ export const getModuleMigrations = (configModule, isFlagEnabled) => {
|
||||
moduleDeclaration: loadedModule.moduleDeclaration,
|
||||
models: mod.models ?? [],
|
||||
migrations: moduleMigrations,
|
||||
externalMigrations: isolatedMigrations,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -249,3 +247,43 @@ export const getModuleSharedResources = (configModule, featureFlagsRouter) => {
|
||||
migrations,
|
||||
}
|
||||
}
|
||||
|
||||
export const runIsolatedModulesMigration = async (configModule) => {
|
||||
const moduleResolutions = registerModules(configModule.modules)
|
||||
|
||||
for (const moduleResolution of Object.values(moduleResolutions)) {
|
||||
if (
|
||||
!moduleResolution.resolutionPath ||
|
||||
moduleResolution.moduleDeclaration.scope !== "internal" ||
|
||||
moduleResolution.moduleDeclaration.resources !== "isolated"
|
||||
) {
|
||||
continue
|
||||
}
|
||||
|
||||
await MedusaModule.migrateUp(
|
||||
moduleResolution.definition.key,
|
||||
moduleResolution.resolutionPath,
|
||||
moduleResolution.options
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export const revertIsolatedModulesMigration = async (configModule) => {
|
||||
const moduleResolutions = registerModules(configModule.modules)
|
||||
|
||||
for (const moduleResolution of Object.values(moduleResolutions)) {
|
||||
if (
|
||||
!moduleResolution.resolutionPath ||
|
||||
moduleResolution.moduleDeclaration.scope !== "internal" ||
|
||||
moduleResolution.moduleDeclaration.resources !== "isolated"
|
||||
) {
|
||||
continue
|
||||
}
|
||||
|
||||
await MedusaModule.migrateDown(
|
||||
moduleResolution.definition.key,
|
||||
moduleResolution.resolutionPath,
|
||||
moduleResolution.options
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { EntityManager } from "typeorm"
|
||||
import { FindConfig } from "../../types/common"
|
||||
|
||||
import {
|
||||
@@ -16,8 +15,6 @@ import {
|
||||
} from "../../types/inventory"
|
||||
|
||||
export interface IInventoryService {
|
||||
withTransaction(transactionManager?: EntityManager): this
|
||||
|
||||
listInventoryItems(
|
||||
selector: FilterableInventoryItemProps,
|
||||
config?: FindConfig<InventoryItemDTO>
|
||||
|
||||
@@ -91,7 +91,7 @@ export default async ({
|
||||
track("MODULES_INIT_STARTED")
|
||||
await moduleLoader({
|
||||
container,
|
||||
moduleResolutions: registerModules(configModule),
|
||||
moduleResolutions: registerModules(configModule?.modules),
|
||||
logger: Logger,
|
||||
})
|
||||
const modAct = Logger.success(modulesActivity, "Modules initialized") || {}
|
||||
|
||||
@@ -24,11 +24,18 @@ describe("EventBusService", () => {
|
||||
find: () => Promise.resolve([]),
|
||||
})
|
||||
|
||||
eventBus = new EventBusService({
|
||||
manager: MockManager,
|
||||
stagedJobRepository,
|
||||
logger: loggerMock,
|
||||
})
|
||||
eventBus = new EventBusService(
|
||||
{
|
||||
manager: MockManager,
|
||||
stagedJobRepository,
|
||||
logger: loggerMock,
|
||||
},
|
||||
{
|
||||
projectConfig: {
|
||||
redis_url: "localhost",
|
||||
},
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
@@ -49,10 +56,17 @@ describe("EventBusService", () => {
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks()
|
||||
|
||||
eventBus = new EventBusService({
|
||||
manager: MockManager,
|
||||
logger: loggerMock,
|
||||
})
|
||||
eventBus = new EventBusService(
|
||||
{
|
||||
manager: MockManager,
|
||||
logger: loggerMock,
|
||||
},
|
||||
{
|
||||
projectConfig: {
|
||||
redis_url: "localhost",
|
||||
},
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
@@ -135,11 +149,18 @@ describe("EventBusService", () => {
|
||||
create: (data) => data,
|
||||
})
|
||||
|
||||
eventBus = new EventBusService({
|
||||
logger: loggerMock,
|
||||
manager: mockManager,
|
||||
stagedJobRepository,
|
||||
})
|
||||
eventBus = new EventBusService(
|
||||
{
|
||||
logger: loggerMock,
|
||||
manager: mockManager,
|
||||
stagedJobRepository,
|
||||
},
|
||||
{
|
||||
projectConfig: {
|
||||
redis_url: "localhost",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
eventBus.queue_.addBulk.mockImplementationOnce(() => "hi")
|
||||
})
|
||||
@@ -195,11 +216,18 @@ describe("EventBusService", () => {
|
||||
create: (data) => data,
|
||||
})
|
||||
|
||||
eventBus = new EventBusService({
|
||||
logger: loggerMock,
|
||||
manager: mockManager,
|
||||
stagedJobRepository,
|
||||
})
|
||||
eventBus = new EventBusService(
|
||||
{
|
||||
logger: loggerMock,
|
||||
manager: mockManager,
|
||||
stagedJobRepository,
|
||||
},
|
||||
{
|
||||
projectConfig: {
|
||||
redis_url: "localhost",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
eventBus.queue_.addBulk.mockImplementationOnce(() => "hi")
|
||||
})
|
||||
@@ -285,7 +313,10 @@ describe("EventBusService", () => {
|
||||
stagedJobRepository,
|
||||
},
|
||||
{
|
||||
projectConfig: { event_options: { removeOnComplete: 10 } },
|
||||
projectConfig: {
|
||||
event_options: { removeOnComplete: 10 },
|
||||
redis_url: "localhost",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
@@ -323,11 +354,18 @@ describe("EventBusService", () => {
|
||||
create: (data) => data,
|
||||
})
|
||||
|
||||
eventBus = new EventBusService({
|
||||
logger: loggerMock,
|
||||
manager: mockManager,
|
||||
stagedJobRepository,
|
||||
})
|
||||
eventBus = new EventBusService(
|
||||
{
|
||||
logger: loggerMock,
|
||||
manager: mockManager,
|
||||
stagedJobRepository,
|
||||
},
|
||||
{
|
||||
projectConfig: {
|
||||
redis_url: "localhost",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
eventBus.queue_.addBulk.mockImplementationOnce(() => "hi")
|
||||
|
||||
@@ -370,7 +408,10 @@ describe("EventBusService", () => {
|
||||
stagedJobRepository,
|
||||
},
|
||||
{
|
||||
projectConfig: { event_options: { removeOnComplete: 10 } },
|
||||
projectConfig: {
|
||||
event_options: { removeOnComplete: 10 },
|
||||
redis_url: "localhost",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
@@ -418,14 +459,11 @@ describe("EventBusService", () => {
|
||||
find: () => Promise.resolve([]),
|
||||
})
|
||||
|
||||
eventBus = new EventBusService(
|
||||
{
|
||||
manager: MockManager,
|
||||
stagedJobRepository,
|
||||
logger: loggerMock,
|
||||
},
|
||||
{}
|
||||
)
|
||||
eventBus = new EventBusService({
|
||||
manager: MockManager,
|
||||
stagedJobRepository,
|
||||
logger: loggerMock,
|
||||
})
|
||||
eventBus.subscribe("eventName", () => Promise.resolve("hi"))
|
||||
result = await eventBus.worker_({
|
||||
data: { eventName: "eventName", data: {} },
|
||||
|
||||
@@ -308,7 +308,9 @@ export default class EventBusService {
|
||||
return (!isBulkEmit ? stagedJobs[0] : stagedJobs) as unknown as TResult
|
||||
}
|
||||
|
||||
await this.queue_.addBulk(events)
|
||||
if (this.config_?.projectConfig?.redis_url) {
|
||||
await this.queue_.addBulk(events)
|
||||
}
|
||||
}
|
||||
|
||||
startEnqueuer(): void {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { isDefined, MedusaError } from "medusa-core-utils"
|
||||
import {
|
||||
Brackets,
|
||||
EntityManager,
|
||||
@@ -9,37 +10,36 @@ import {
|
||||
IsNull,
|
||||
SelectQueryBuilder,
|
||||
} from "typeorm"
|
||||
import {
|
||||
CreateProductVariantInput,
|
||||
FilterableProductVariantProps,
|
||||
GetRegionPriceContext,
|
||||
ProductVariantPrice,
|
||||
UpdateProductVariantInput,
|
||||
} from "../types/product-variant"
|
||||
import {
|
||||
FindWithRelationsOptions,
|
||||
ProductVariantRepository,
|
||||
} from "../repositories/product-variant"
|
||||
import {
|
||||
IPriceSelectionStrategy,
|
||||
PriceSelectionContext,
|
||||
TransactionBaseService,
|
||||
} from "../interfaces"
|
||||
import { MedusaError, isDefined } from "medusa-core-utils"
|
||||
import {
|
||||
MoneyAmount,
|
||||
Product,
|
||||
ProductOptionValue,
|
||||
ProductVariant,
|
||||
} from "../models"
|
||||
import {
|
||||
FindWithRelationsOptions,
|
||||
ProductVariantRepository,
|
||||
} from "../repositories/product-variant"
|
||||
import {
|
||||
CreateProductVariantInput,
|
||||
FilterableProductVariantProps,
|
||||
GetRegionPriceContext,
|
||||
ProductVariantPrice,
|
||||
UpdateProductVariantInput,
|
||||
} from "../types/product-variant"
|
||||
import { buildQuery, buildRelations, setMetadata } from "../utils"
|
||||
|
||||
import { CartRepository } from "../repositories/cart"
|
||||
import EventBusService from "./event-bus"
|
||||
import { FindConfig } from "../types/common"
|
||||
import { MoneyAmountRepository } from "../repositories/money-amount"
|
||||
import { ProductOptionValueRepository } from "../repositories/product-option-value"
|
||||
import { ProductRepository } from "../repositories/product"
|
||||
import { ProductOptionValueRepository } from "../repositories/product-option-value"
|
||||
import { FindConfig } from "../types/common"
|
||||
import EventBusService from "./event-bus"
|
||||
import RegionService from "./region"
|
||||
|
||||
class ProductVariantService extends TransactionBaseService {
|
||||
@@ -173,7 +173,9 @@ class ProductVariantService extends TransactionBaseService {
|
||||
"options",
|
||||
]),
|
||||
})) as Product
|
||||
} else if (!product.id) {
|
||||
}
|
||||
|
||||
if (!product?.id) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Product id missing`
|
||||
|
||||
@@ -42,9 +42,10 @@ class SalesChannelInventoryService extends TransactionBaseService {
|
||||
.withTransaction(this.activeManager_)
|
||||
.listLocationIds(salesChannelId)
|
||||
|
||||
return await this.inventoryService_
|
||||
.withTransaction(this.activeManager_)
|
||||
.retrieveAvailableQuantity(inventoryItemId, locationIds)
|
||||
return await this.inventoryService_.retrieveAvailableQuantity(
|
||||
inventoryItemId,
|
||||
locationIds
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,8 +18,8 @@ type InjectedDependencies = {
|
||||
|
||||
class SalesChannelLocationService extends TransactionBaseService {
|
||||
protected readonly salesChannelService_: SalesChannelService
|
||||
protected readonly eventBusService: EventBusService
|
||||
protected readonly stockLocationService: IStockLocationService
|
||||
protected readonly eventBusService_: EventBusService
|
||||
protected readonly stockLocationService_: IStockLocationService
|
||||
|
||||
constructor({
|
||||
salesChannelService,
|
||||
@@ -30,8 +30,8 @@ class SalesChannelLocationService extends TransactionBaseService {
|
||||
super(arguments[0])
|
||||
|
||||
this.salesChannelService_ = salesChannelService
|
||||
this.eventBusService = eventBusService
|
||||
this.stockLocationService = stockLocationService
|
||||
this.eventBusService_ = eventBusService
|
||||
this.stockLocationService_ = stockLocationService
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -77,9 +77,9 @@ class SalesChannelLocationService extends TransactionBaseService {
|
||||
.withTransaction(this.activeManager_)
|
||||
.retrieve(salesChannelId)
|
||||
|
||||
if (this.stockLocationService) {
|
||||
if (this.stockLocationService_) {
|
||||
// trhows error if not found
|
||||
await this.stockLocationService.retrieve(locationId)
|
||||
await this.stockLocationService_.retrieve(locationId)
|
||||
}
|
||||
|
||||
const salesChannelLocation = this.activeManager_.create(
|
||||
@@ -129,7 +129,7 @@ class SalesChannelLocationService extends TransactionBaseService {
|
||||
*/
|
||||
async listSalesChannelIds(locationId: string): Promise<string[]> {
|
||||
const manager = this.transactionManager_ || this.manager_
|
||||
const location = await this.stockLocationService.retrieve(locationId)
|
||||
const location = await this.stockLocationService_.retrieve(locationId)
|
||||
|
||||
const salesChannelLocations = await manager.find(SalesChannelLocation, {
|
||||
where: { location_id: location.id },
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { InternalModuleDeclaration } from "@medusajs/modules-sdk"
|
||||
import { MedusaContainer as coreMedusaContainer } from "medusa-core-utils"
|
||||
import {
|
||||
ExternalModuleDeclaration,
|
||||
InternalModuleDeclaration,
|
||||
} from "@medusajs/modules-sdk"
|
||||
import { Request } from "express"
|
||||
import { MedusaContainer as coreMedusaContainer } from "medusa-core-utils"
|
||||
import { LoggerOptions } from "typeorm"
|
||||
import { Logger as _Logger } from "winston"
|
||||
import { Customer, User } from "../models"
|
||||
@@ -91,7 +94,12 @@ export type ConfigModule = {
|
||||
admin_cors?: string
|
||||
}
|
||||
featureFlags: Record<string, boolean | string>
|
||||
modules?: Record<string, false | string | Partial<InternalModuleDeclaration>>
|
||||
modules?: Record<
|
||||
string,
|
||||
| false
|
||||
| string
|
||||
| Partial<InternalModuleDeclaration | ExternalModuleDeclaration>
|
||||
>
|
||||
plugins: (
|
||||
| {
|
||||
resolve: string
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export * from "./definitions"
|
||||
export * from "./loaders"
|
||||
export * from "./medusa-module"
|
||||
export * from "./module-helper"
|
||||
export * from "./types"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { EOL } from "os"
|
||||
import { AwilixContainer, ClassOrFunctionReturning, Resolver } from "awilix"
|
||||
import { createMedusaContainer } from "medusa-core-utils"
|
||||
import { EOL } from "os"
|
||||
import {
|
||||
ModuleResolution,
|
||||
MODULE_RESOURCE_TYPE,
|
||||
@@ -155,7 +155,7 @@ describe("modules loader", () => {
|
||||
await moduleLoader({ container, moduleResolutions, logger })
|
||||
|
||||
expect(logger.warn).toHaveBeenCalledWith(
|
||||
`Could not resolve module: TestService. Error: No service found in module. Make sure your module exports at least one service.${EOL}`
|
||||
`Could not resolve module: TestService. Error: No service found in module. Make sure your module exports a service.${EOL}`
|
||||
)
|
||||
})
|
||||
|
||||
@@ -186,7 +186,7 @@ describe("modules loader", () => {
|
||||
await moduleLoader({ container, moduleResolutions, logger })
|
||||
} catch (err) {
|
||||
expect(err.message).toEqual(
|
||||
"No service found in module. Make sure your module exports at least one service."
|
||||
"No service found in module. Make sure your module exports a service."
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import MODULE_DEFINITIONS from "../../definitions"
|
||||
import {
|
||||
InternalModuleDeclaration,
|
||||
ModuleDefinition,
|
||||
@@ -5,7 +6,6 @@ import {
|
||||
MODULE_SCOPE,
|
||||
} from "../../types"
|
||||
import { registerModules } from "../register-modules"
|
||||
import MODULE_DEFINITIONS from "../../definitions"
|
||||
|
||||
const RESOLVED_PACKAGE = "@medusajs/test-service-resolved"
|
||||
jest.mock("resolve-cwd", () => jest.fn(() => RESOLVED_PACKAGE))
|
||||
@@ -35,7 +35,7 @@ describe("module definitions loader", () => {
|
||||
it("Resolves module with default definition given empty config", () => {
|
||||
MODULE_DEFINITIONS.push({ ...defaultDefinition })
|
||||
|
||||
const res = registerModules({ modules: {} })
|
||||
const res = registerModules({})
|
||||
|
||||
expect(res[defaultDefinition.key]).toEqual(
|
||||
expect.objectContaining({
|
||||
@@ -54,9 +54,7 @@ describe("module definitions loader", () => {
|
||||
it("Resolves module with no resolution path when given false", () => {
|
||||
MODULE_DEFINITIONS.push({ ...defaultDefinition })
|
||||
|
||||
const res = registerModules({
|
||||
modules: { [defaultDefinition.key]: false },
|
||||
})
|
||||
const res = registerModules({ [defaultDefinition.key]: false })
|
||||
|
||||
expect(res[defaultDefinition.key]).toEqual(
|
||||
expect.objectContaining({
|
||||
@@ -72,9 +70,7 @@ describe("module definitions loader", () => {
|
||||
MODULE_DEFINITIONS.push({ ...defaultDefinition, isRequired: true })
|
||||
|
||||
try {
|
||||
registerModules({
|
||||
modules: { [defaultDefinition.key]: false },
|
||||
})
|
||||
registerModules({ [defaultDefinition.key]: false })
|
||||
} catch (err) {
|
||||
expect(err.message).toEqual(
|
||||
`Module: ${defaultDefinition.label} is required`
|
||||
@@ -90,9 +86,7 @@ describe("module definitions loader", () => {
|
||||
|
||||
MODULE_DEFINITIONS.push(definition)
|
||||
|
||||
const res = registerModules({
|
||||
modules: {},
|
||||
})
|
||||
const res = registerModules({})
|
||||
|
||||
expect(res[defaultDefinition.key]).toEqual(
|
||||
expect.objectContaining({
|
||||
@@ -113,9 +107,7 @@ describe("module definitions loader", () => {
|
||||
MODULE_DEFINITIONS.push({ ...defaultDefinition })
|
||||
|
||||
const res = registerModules({
|
||||
modules: {
|
||||
[defaultDefinition.key]: defaultDefinition.defaultPackage,
|
||||
},
|
||||
[defaultDefinition.key]: defaultDefinition.defaultPackage,
|
||||
})
|
||||
|
||||
expect(res[defaultDefinition.key]).toEqual(
|
||||
@@ -137,13 +129,11 @@ describe("module definitions loader", () => {
|
||||
MODULE_DEFINITIONS.push({ ...defaultDefinition })
|
||||
|
||||
const res = registerModules({
|
||||
modules: {
|
||||
[defaultDefinition.key]: {
|
||||
scope: MODULE_SCOPE.INTERNAL,
|
||||
resolve: defaultDefinition.defaultPackage,
|
||||
resources: MODULE_RESOURCE_TYPE.ISOLATED,
|
||||
} as InternalModuleDeclaration,
|
||||
},
|
||||
[defaultDefinition.key]: {
|
||||
scope: MODULE_SCOPE.INTERNAL,
|
||||
resolve: defaultDefinition.defaultPackage,
|
||||
resources: MODULE_RESOURCE_TYPE.ISOLATED,
|
||||
} as InternalModuleDeclaration,
|
||||
})
|
||||
|
||||
expect(res[defaultDefinition.key]).toEqual(
|
||||
@@ -164,10 +154,8 @@ describe("module definitions loader", () => {
|
||||
MODULE_DEFINITIONS.push({ ...defaultDefinition })
|
||||
|
||||
const res = registerModules({
|
||||
modules: {
|
||||
[defaultDefinition.key]: {
|
||||
options: { test: 123 },
|
||||
},
|
||||
[defaultDefinition.key]: {
|
||||
options: { test: 123 },
|
||||
},
|
||||
} as any)
|
||||
|
||||
@@ -189,13 +177,11 @@ describe("module definitions loader", () => {
|
||||
MODULE_DEFINITIONS.push({ ...defaultDefinition })
|
||||
|
||||
const res = registerModules({
|
||||
modules: {
|
||||
[defaultDefinition.key]: {
|
||||
resolve: defaultDefinition.defaultPackage,
|
||||
options: { test: 123 },
|
||||
scope: "internal",
|
||||
resources: "isolated",
|
||||
},
|
||||
[defaultDefinition.key]: {
|
||||
resolve: defaultDefinition.defaultPackage,
|
||||
options: { test: 123 },
|
||||
scope: "internal",
|
||||
resources: "isolated",
|
||||
},
|
||||
} as any)
|
||||
|
||||
|
||||
@@ -1,17 +1,22 @@
|
||||
import resolveCwd from "resolve-cwd"
|
||||
|
||||
import MODULE_DEFINITIONS from "../definitions"
|
||||
import {
|
||||
MedusaModuleConfig,
|
||||
ExternalModuleDeclaration,
|
||||
InternalModuleDeclaration,
|
||||
ModuleDefinition,
|
||||
ModuleResolution,
|
||||
MODULE_SCOPE,
|
||||
} from "../types"
|
||||
import MODULE_DEFINITIONS from "../definitions"
|
||||
|
||||
export const registerModules = ({
|
||||
modules,
|
||||
}: MedusaModuleConfig): Record<string, ModuleResolution> => {
|
||||
export const registerModules = (
|
||||
modules?: Record<
|
||||
string,
|
||||
| false
|
||||
| string
|
||||
| Partial<InternalModuleDeclaration | ExternalModuleDeclaration>
|
||||
>
|
||||
): Record<string, ModuleResolution> => {
|
||||
const moduleResolutions = {} as Record<string, ModuleResolution>
|
||||
const projectModules = modules ?? {}
|
||||
|
||||
@@ -26,10 +31,32 @@ export const registerModules = ({
|
||||
|
||||
moduleResolutions[definition.key] = getInternalModuleResolution(
|
||||
definition,
|
||||
projectModules[definition.key] as
|
||||
| InternalModuleDeclaration
|
||||
| false
|
||||
| string
|
||||
customConfig as InternalModuleDeclaration
|
||||
)
|
||||
}
|
||||
|
||||
return moduleResolutions
|
||||
}
|
||||
|
||||
export const registerMedusaModule = (
|
||||
moduleKey: string,
|
||||
moduleDeclaration: InternalModuleDeclaration | ExternalModuleDeclaration
|
||||
): Record<string, ModuleResolution> => {
|
||||
const moduleResolutions = {} as Record<string, ModuleResolution>
|
||||
|
||||
for (const definition of MODULE_DEFINITIONS) {
|
||||
if (definition.key !== moduleKey) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (moduleDeclaration.scope === MODULE_SCOPE.EXTERNAL) {
|
||||
// TODO: getExternalModuleResolution(...)a
|
||||
throw new Error("External Modules are not supported yet.")
|
||||
}
|
||||
|
||||
moduleResolutions[definition.key] = getInternalModuleResolution(
|
||||
definition,
|
||||
moduleDeclaration as InternalModuleDeclaration
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ export async function loadInternalModule(
|
||||
|
||||
return {
|
||||
error: new Error(
|
||||
"No service found in module. Make sure your module exports at least one service."
|
||||
"No service found in module. Make sure your module exports a service."
|
||||
),
|
||||
}
|
||||
}
|
||||
@@ -112,3 +112,15 @@ export async function loadInternalModule(
|
||||
"module"
|
||||
)
|
||||
}
|
||||
|
||||
export async function loadModuleMigrations(
|
||||
resolution: ModuleResolution
|
||||
): Promise<[Function | undefined, Function | undefined]> {
|
||||
let loadedModule: ModuleExports
|
||||
try {
|
||||
loadedModule = (await import(resolution.resolutionPath as string)).default
|
||||
return [loadedModule.runMigrations, loadedModule.revertMigration]
|
||||
} catch {
|
||||
return [undefined, undefined]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
import { asValue } from "awilix"
|
||||
import { createMedusaContainer } from "medusa-core-utils"
|
||||
import { moduleLoader, registerMedusaModule } from "./loaders"
|
||||
import { loadModuleMigrations } from "./loaders/utils"
|
||||
import {
|
||||
ExternalModuleDeclaration,
|
||||
InternalModuleDeclaration,
|
||||
MODULE_RESOURCE_TYPE,
|
||||
MODULE_SCOPE,
|
||||
} from "./types"
|
||||
|
||||
const logger: any = {
|
||||
log: (a) => console.log(a),
|
||||
info: (a) => console.log(a),
|
||||
warn: (a) => console.warn(a),
|
||||
error: (a) => console.error(a),
|
||||
}
|
||||
|
||||
export class MedusaModule {
|
||||
public static async bootstrap(
|
||||
moduleKey: string,
|
||||
defaultPath: string,
|
||||
declaration?: InternalModuleDeclaration | ExternalModuleDeclaration,
|
||||
injectedDependencies?: Record<string, any>
|
||||
): Promise<{
|
||||
[key: string]: any
|
||||
}> {
|
||||
let modDeclaration = declaration
|
||||
if (declaration?.scope !== MODULE_SCOPE.EXTERNAL) {
|
||||
modDeclaration = {
|
||||
scope: MODULE_SCOPE.INTERNAL,
|
||||
resources: MODULE_RESOURCE_TYPE.ISOLATED,
|
||||
resolve: defaultPath,
|
||||
options: declaration,
|
||||
}
|
||||
}
|
||||
|
||||
const container = createMedusaContainer()
|
||||
|
||||
if (injectedDependencies) {
|
||||
for (const service in injectedDependencies) {
|
||||
container.register(service, asValue(injectedDependencies[service]))
|
||||
}
|
||||
}
|
||||
|
||||
const moduleResolutions = registerMedusaModule(moduleKey, modDeclaration!)
|
||||
|
||||
await moduleLoader({ container, moduleResolutions, logger })
|
||||
|
||||
const services = {}
|
||||
|
||||
for (const resolution of Object.values(moduleResolutions)) {
|
||||
const registrationName = resolution.definition.registrationName
|
||||
|
||||
services[registrationName] = container.resolve(registrationName)
|
||||
}
|
||||
|
||||
return services
|
||||
}
|
||||
|
||||
public static async migrateUp(
|
||||
moduleKey: string,
|
||||
modulePath: string,
|
||||
options?: Record<string, any>
|
||||
): Promise<void> {
|
||||
const moduleResolutions = registerMedusaModule(moduleKey, {
|
||||
scope: MODULE_SCOPE.INTERNAL,
|
||||
resources: MODULE_RESOURCE_TYPE.ISOLATED,
|
||||
resolve: modulePath,
|
||||
options,
|
||||
})
|
||||
|
||||
for (const mod in moduleResolutions) {
|
||||
const [migrateUp] = await loadModuleMigrations(moduleResolutions[mod])
|
||||
|
||||
if (typeof migrateUp === "function") {
|
||||
await migrateUp({
|
||||
options,
|
||||
logger,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static async migrateDown(
|
||||
moduleKey: string,
|
||||
modulePath: string,
|
||||
options?: Record<string, any>
|
||||
): Promise<void> {
|
||||
const moduleResolutions = registerMedusaModule(moduleKey, {
|
||||
scope: MODULE_SCOPE.INTERNAL,
|
||||
resources: MODULE_RESOURCE_TYPE.ISOLATED,
|
||||
resolve: modulePath,
|
||||
options,
|
||||
})
|
||||
|
||||
for (const mod in moduleResolutions) {
|
||||
const [, migrateDown] = await loadModuleMigrations(moduleResolutions[mod])
|
||||
|
||||
if (typeof migrateDown === "function") {
|
||||
await migrateDown({
|
||||
options,
|
||||
logger,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -68,9 +68,9 @@ export type ModuleDefinition = {
|
||||
| ExternalModuleDeclaration
|
||||
}
|
||||
|
||||
export type LoaderOptions = {
|
||||
export type LoaderOptions<TOptions = Record<string, unknown>> = {
|
||||
container: MedusaContainer
|
||||
options?: Record<string, unknown>
|
||||
options?: TOptions
|
||||
logger?: Logger
|
||||
}
|
||||
|
||||
@@ -89,13 +89,12 @@ export type ModuleExports = {
|
||||
loaders?: ModuleLoaderFunction[]
|
||||
migrations?: any[]
|
||||
models?: Constructor<any>[]
|
||||
}
|
||||
|
||||
export type MedusaModuleConfig = {
|
||||
modules?: Record<
|
||||
string,
|
||||
| false
|
||||
| string
|
||||
| Partial<InternalModuleDeclaration | ExternalModuleDeclaration>
|
||||
>
|
||||
runMigrations?(
|
||||
options: LoaderOptions,
|
||||
moduleDeclaration: InternalModuleDeclaration
|
||||
): Promise<void>
|
||||
revertMigration?(
|
||||
options: LoaderOptions,
|
||||
moduleDeclaration: InternalModuleDeclaration
|
||||
): Promise<void>
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import { ModuleExports } from "@medusajs/modules-sdk"
|
||||
import loadConnection from "./loaders/connection"
|
||||
|
||||
import migrations from "./migrations"
|
||||
import { revertMigration, runMigrations } from "./migrations/run-migration"
|
||||
import * as StockLocationModels from "./models"
|
||||
import StockLocationService from "./services/stock-location"
|
||||
|
||||
import { ModuleExports } from "@medusajs/modules-sdk"
|
||||
|
||||
const service = StockLocationService
|
||||
const loaders = [loadConnection]
|
||||
const models = Object.values(StockLocationModels)
|
||||
@@ -15,6 +14,8 @@ const moduleDefinition: ModuleExports = {
|
||||
migrations,
|
||||
loaders,
|
||||
models,
|
||||
runMigrations,
|
||||
revertMigration,
|
||||
}
|
||||
|
||||
export default moduleDefinition
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
import { IEventBusService, IStockLocationService } from "@medusajs/medusa"
|
||||
import {
|
||||
ExternalModuleDeclaration,
|
||||
InternalModuleDeclaration,
|
||||
MedusaModule,
|
||||
} from "@medusajs/modules-sdk"
|
||||
import { StockLocationServiceInitializeOptions } from "../types"
|
||||
|
||||
export const initialize = async (
|
||||
options?: StockLocationServiceInitializeOptions | ExternalModuleDeclaration,
|
||||
injectedDependencies?: {
|
||||
eventBusService: IEventBusService
|
||||
}
|
||||
): Promise<IStockLocationService> => {
|
||||
const serviceKey = "stockLocationService"
|
||||
const loaded = await MedusaModule.bootstrap(
|
||||
serviceKey,
|
||||
"@medusajs/stock-location",
|
||||
options as InternalModuleDeclaration | ExternalModuleDeclaration,
|
||||
injectedDependencies
|
||||
)
|
||||
|
||||
return loaded[serviceKey] as IStockLocationService
|
||||
}
|
||||
@@ -2,13 +2,13 @@ import {
|
||||
InternalModuleDeclaration,
|
||||
LoaderOptions,
|
||||
MODULE_RESOURCE_TYPE,
|
||||
MODULE_SCOPE,
|
||||
MODULE_SCOPE
|
||||
} from "@medusajs/modules-sdk"
|
||||
import { DataSource, DataSourceOptions } from "typeorm"
|
||||
|
||||
import * as StockLocationModels from "../models"
|
||||
import { MedusaError } from "medusa-core-utils"
|
||||
import { asValue } from "awilix"
|
||||
import { MedusaError } from "medusa-core-utils"
|
||||
import { DataSource, DataSourceOptions } from "typeorm"
|
||||
import * as StockLocationModels from "../models"
|
||||
import { StockLocationServiceInitializeOptions } from "../types"
|
||||
|
||||
export default async (
|
||||
{ options, container }: LoaderOptions,
|
||||
@@ -21,7 +21,8 @@ export default async (
|
||||
return
|
||||
}
|
||||
|
||||
const dbData = options?.database as Record<string, string>
|
||||
const dbData =
|
||||
options?.database as StockLocationServiceInitializeOptions["database"]
|
||||
|
||||
if (!dbData) {
|
||||
throw new MedusaError(
|
||||
@@ -32,13 +33,13 @@ export default async (
|
||||
|
||||
const entities = Object.values(StockLocationModels)
|
||||
const dataSource = new DataSource({
|
||||
type: dbData.database_type,
|
||||
url: dbData.database_url,
|
||||
database: dbData.database_database,
|
||||
extra: dbData.database_extra || {},
|
||||
schema: dbData.database_schema,
|
||||
type: dbData.type,
|
||||
url: dbData.url,
|
||||
database: dbData.database,
|
||||
extra: dbData.extra || {},
|
||||
schema: dbData.schema,
|
||||
entities,
|
||||
logging: dbData.database_logging,
|
||||
logging: dbData.logging,
|
||||
} as DataSourceOptions)
|
||||
|
||||
await dataSource.initialize()
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
import { InternalModuleDeclaration, LoaderOptions } from "@medusajs/modules-sdk"
|
||||
import { DataSource, DataSourceOptions } from "typeorm"
|
||||
import { StockLocationServiceInitializeOptions } from "../types"
|
||||
|
||||
import migrations from "./index"
|
||||
|
||||
function getDataSource(
|
||||
dbData: StockLocationServiceInitializeOptions["database"]
|
||||
): DataSource {
|
||||
return new DataSource({
|
||||
type: dbData!.type,
|
||||
url: dbData!.url,
|
||||
database: dbData!.database,
|
||||
extra: dbData!.extra || {},
|
||||
migrations: migrations
|
||||
.map((migration: any): Function[] => {
|
||||
return Object.values(migration).filter(
|
||||
(fn) => typeof fn === "function"
|
||||
) as Function[]
|
||||
})
|
||||
.flat(),
|
||||
schema: dbData!.schema,
|
||||
logging: dbData!.logging,
|
||||
} as DataSourceOptions)
|
||||
}
|
||||
|
||||
export async function runMigrations(
|
||||
{ options, logger }: Omit<LoaderOptions, "container">,
|
||||
moduleDeclaration?: InternalModuleDeclaration
|
||||
) {
|
||||
const dbData =
|
||||
options?.database as StockLocationServiceInitializeOptions["database"]
|
||||
|
||||
try {
|
||||
const dataSource = getDataSource(dbData)
|
||||
await dataSource.initialize()
|
||||
await dataSource.runMigrations()
|
||||
|
||||
logger?.info("Stock Location module migration executed")
|
||||
} catch (error) {
|
||||
logger?.error(
|
||||
`Stock Location module migration failed to run - Error: ${error}`
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export async function revertMigration(
|
||||
{ options, logger }: Omit<LoaderOptions, "container">,
|
||||
moduleDeclaration?: InternalModuleDeclaration
|
||||
) {
|
||||
const dbData =
|
||||
options?.database as StockLocationServiceInitializeOptions["database"]
|
||||
|
||||
try {
|
||||
const dataSource = getDataSource(dbData)
|
||||
await dataSource.initialize()
|
||||
await dataSource.undoLastMigration()
|
||||
|
||||
logger?.info("Stock Location module migration reverted")
|
||||
} catch (error) {
|
||||
logger?.error(
|
||||
`Stock Location module migration failed to revert - Error: ${error}`
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -153,11 +153,9 @@ export default class StockLocationService {
|
||||
}
|
||||
const result = await locationRepo.save(loc)
|
||||
|
||||
await this.eventBusService_
|
||||
.withTransaction(manager)
|
||||
.emit(StockLocationService.Events.CREATED, {
|
||||
id: result.id,
|
||||
})
|
||||
await this.eventBusService_?.emit?.(StockLocationService.Events.CREATED, {
|
||||
id: result.id,
|
||||
})
|
||||
|
||||
return result
|
||||
}
|
||||
@@ -201,11 +199,9 @@ export default class StockLocationService {
|
||||
|
||||
await locationRepo.save(toSave)
|
||||
|
||||
await this.eventBusService_
|
||||
.withTransaction(manager)
|
||||
.emit(StockLocationService.Events.UPDATED, {
|
||||
id: stockLocationId,
|
||||
})
|
||||
await this.eventBusService_?.emit?.(StockLocationService.Events.UPDATED, {
|
||||
id: stockLocationId,
|
||||
})
|
||||
|
||||
return item
|
||||
}
|
||||
@@ -267,10 +263,8 @@ export default class StockLocationService {
|
||||
|
||||
await locationRepo.softRemove({ id })
|
||||
|
||||
await this.eventBusService_
|
||||
.withTransaction(manager)
|
||||
.emit(StockLocationService.Events.DELETED, {
|
||||
id,
|
||||
})
|
||||
await this.eventBusService_?.emit?.(StockLocationService.Events.DELETED, {
|
||||
id,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
import { DatabaseType } from "typeorm"
|
||||
|
||||
export type StockLocationServiceInitializeOptions = {
|
||||
database?: {
|
||||
type?: DatabaseType | string
|
||||
url?: string
|
||||
database?: string
|
||||
extra?: Record<string, any>
|
||||
schema?: string
|
||||
logging?: boolean
|
||||
}
|
||||
}
|
||||
@@ -4,16 +4,16 @@ export function MedusaContext() {
|
||||
propertyKey: string | symbol,
|
||||
parameterIndex: number
|
||||
) {
|
||||
if (!target.MedusaContext_) {
|
||||
target.MedusaContext_ = {}
|
||||
if (!target.MedusaContextIndex_) {
|
||||
target.MedusaContextIndex_ = {}
|
||||
}
|
||||
|
||||
if (propertyKey in target.MedusaContext_) {
|
||||
if (propertyKey in target.MedusaContextIndex_) {
|
||||
throw new Error(
|
||||
`Only one MedusaContext is allowed on method "${String(propertyKey)}".`
|
||||
)
|
||||
}
|
||||
|
||||
target.MedusaContext_[propertyKey] = parameterIndex
|
||||
target.MedusaContextIndex_[propertyKey] = parameterIndex
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ export function InjectEntityManager(
|
||||
propertyKey: string | symbol,
|
||||
descriptor: any
|
||||
): void {
|
||||
if (!target.MedusaContext_) {
|
||||
if (!target.MedusaContextIndex_) {
|
||||
throw new Error(
|
||||
`To apply @InjectEntityManager you have to flag a parameter using @MedusaContext`
|
||||
)
|
||||
@@ -16,7 +16,7 @@ export function InjectEntityManager(
|
||||
|
||||
const originalMethod = descriptor.value
|
||||
|
||||
const argIndex = target.MedusaContext_[propertyKey]
|
||||
const argIndex = target.MedusaContextIndex_[propertyKey]
|
||||
descriptor.value = async function (...args: any[]) {
|
||||
const context: SharedContext = args[argIndex] ?? {}
|
||||
|
||||
|
||||
@@ -5741,8 +5741,10 @@ __metadata:
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@medusajs/inventory@workspace:packages/inventory"
|
||||
dependencies:
|
||||
"@medusajs/medusa": "*"
|
||||
"@medusajs/medusa": ^1.7.7
|
||||
"@medusajs/modules-sdk": "*"
|
||||
"@medusajs/types": "*"
|
||||
"@medusajs/utils": ^0.0.1
|
||||
awilix: ^8.0.0
|
||||
cross-env: ^5.2.1
|
||||
jest: ^25.5.4
|
||||
@@ -5750,7 +5752,7 @@ __metadata:
|
||||
typeorm: ^0.3.11
|
||||
typescript: ^4.4.4
|
||||
peerDependencies:
|
||||
"@medusajs/medusa": 1.7.13
|
||||
"@medusajs/types": ^0.0.1
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
|
||||
Reference in New Issue
Block a user