diff --git a/.changeset/sour-mails-bake.md b/.changeset/sour-mails-bake.md new file mode 100644 index 0000000000..8b0a11ed8e --- /dev/null +++ b/.changeset/sour-mails-bake.md @@ -0,0 +1,7 @@ +--- +"@medusajs/inventory": patch +"@medusajs/medusa": patch +"@medusajs/stock-location": patch +--- + +feat(medusa, inventory, stock-location): Remove unnecessary transaction usage in the modules and list products end points diff --git a/packages/inventory/src/services/inventory-item.ts b/packages/inventory/src/services/inventory-item.ts index 041f725940..9756450798 100644 --- a/packages/inventory/src/services/inventory-item.ts +++ b/packages/inventory/src/services/inventory-item.ts @@ -40,54 +40,34 @@ export default class InventoryItemService { /** * @param selector - Filter options for inventory items. * @param config - Configuration for query. + * @param context * @return Resolves to the list of inventory items that match the filter. */ - @InjectEntityManager() async list( selector: FilterableInventoryItemProps = {}, config: FindConfig = { relations: [], skip: 0, take: 10 }, - @MedusaContext() context: SharedContext = {} + context: SharedContext = {} ): Promise { const queryBuilder = getListQuery( - context.transactionManager!, + context.transactionManager ?? this.manager_, selector, config ) return await queryBuilder.getMany() } - /** - * @param selector - Filter options for inventory items. - * @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 = { relations: [], skip: 0, take: 10 }, - @MedusaContext() context: SharedContext = {} - ): Promise<[InventoryItemDTO[], number]> { - const queryBuilder = getListQuery( - context.transactionManager!, - selector, - config - ) - - return await queryBuilder.getManyAndCount() - } - /** * Retrieves an inventory item by its id. * @param inventoryItemId - the id of the inventory item to retrieve. * @param config - the configuration options for the find operation. + * @param context * @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 = {}, - @MedusaContext() context: SharedContext = {} + context: SharedContext = {} ): Promise { if (!isDefined(inventoryItemId)) { throw new MedusaError( @@ -96,7 +76,7 @@ export default class InventoryItemService { ) } - const manager = context.transactionManager! + const manager = context.transactionManager ?? this.manager_ const itemRepository = manager.getRepository(InventoryItem) const query = buildQuery({ id: inventoryItemId }, config) as FindManyOptions @@ -113,8 +93,30 @@ export default class InventoryItemService { } /** - * @param input - Input for creating a new inventory item. - * @return The newly created inventory item. + * @param selector - Filter options for inventory items. + * @param config - Configuration for query. + * @param context + * @return - Resolves to the list of inventory items that match the filter and the count of all matching items. + */ + async listAndCount( + selector: FilterableInventoryItemProps = {}, + config: FindConfig = { relations: [], skip: 0, take: 10 }, + context: SharedContext = {} + ): Promise<[InventoryItemDTO[], number]> { + const queryBuilder = getListQuery( + context.transactionManager ?? this.manager_, + selector, + config + ) + + return await queryBuilder.getManyAndCount() + } + + /** + * @param data + * @param context + * @param data + * @param context */ @InjectEntityManager() async create( @@ -152,7 +154,9 @@ export default class InventoryItemService { /** * @param inventoryItemId - The id of the inventory item to update. - * @param update - The updates to apply to the inventory item. + * @param data + * @param context + * @param context * @return The updated inventory item. */ @InjectEntityManager() @@ -187,6 +191,7 @@ export default class InventoryItemService { /** * @param inventoryItemId - The id of the inventory item to delete. + * @param context */ @InjectEntityManager() async delete( diff --git a/packages/inventory/src/services/inventory-level.ts b/packages/inventory/src/services/inventory-level.ts index 9f3fa820e1..22ca4fd40f 100644 --- a/packages/inventory/src/services/inventory-level.ts +++ b/packages/inventory/src/services/inventory-level.ts @@ -39,15 +39,15 @@ export default class InventoryLevelService { * Retrieves a list of inventory levels based on the provided selector and configuration. * @param selector - An object containing filterable properties for inventory levels. * @param config - An object containing configuration options for the query. + * @param context * @return Array of inventory levels. */ - @InjectEntityManager() async list( selector: FilterableInventoryLevelProps = {}, config: FindConfig = { relations: [], skip: 0, take: 10 }, - @MedusaContext() context: SharedContext = {} + context: SharedContext = {} ): Promise { - const manager = context.transactionManager! + const manager = context.transactionManager ?? this.manager_ const levelRepository = manager.getRepository(InventoryLevel) const query = buildQuery(selector, config) as FindManyOptions @@ -58,15 +58,15 @@ export default class InventoryLevelService { * Retrieves a list of inventory levels and a count based on the provided selector and configuration. * @param selector - An object containing filterable properties for inventory levels. * @param config - An object containing configuration options for the query. + * @param context * @return An array of inventory levels and a count. */ - @InjectEntityManager() async listAndCount( selector: FilterableInventoryLevelProps = {}, config: FindConfig = { relations: [], skip: 0, take: 10 }, - @MedusaContext() context: SharedContext = {} + context: SharedContext = {} ): Promise<[InventoryLevel[], number]> { - const manager = context.transactionManager! + const manager = context.transactionManager ?? this.manager_ const levelRepository = manager.getRepository(InventoryLevel) const query = buildQuery(selector, config) as FindManyOptions @@ -77,14 +77,14 @@ export default class InventoryLevelService { * Retrieves a single inventory level by its ID. * @param inventoryLevelId - The ID of the inventory level to retrieve. * @param config - An object containing configuration options for the query. + * @param context * @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 = {}, - @MedusaContext() context: SharedContext = {} + context: SharedContext = {} ): Promise { if (!isDefined(inventoryLevelId)) { throw new MedusaError( @@ -93,7 +93,7 @@ export default class InventoryLevelService { ) } - const manager = context.transactionManager! + const manager = context.transactionManager ?? this.manager_ const levelRepository = manager.getRepository(InventoryLevel) const query = buildQuery( @@ -115,6 +115,7 @@ export default class InventoryLevelService { /** * Creates a new inventory level. * @param data - An object containing the properties for the new inventory level. + * @param context * @return The created inventory level. */ @InjectEntityManager() @@ -146,6 +147,7 @@ export default class InventoryLevelService { * Updates an existing inventory level. * @param inventoryLevelId - The ID of the inventory level to update. * @param data - An object containing the properties to update on the inventory level. + * @param context * @return The updated inventory level. * @throws If the inventory level ID is not defined or the given ID was not found. */ @@ -187,6 +189,7 @@ export default class InventoryLevelService { * @param inventoryItemId - The ID of the inventory item. * @param locationId - The ID of the location. * @param quantity - The quantity to adjust from the reserved quantity. + * @param context */ @InjectEntityManager() async adjustReservedQuantity( @@ -210,6 +213,7 @@ export default class InventoryLevelService { /** * Deletes inventory levels by inventory Item ID. * @param inventoryItemId - The ID or IDs of the inventory item to delete inventory levels for. + * @param context */ @InjectEntityManager() async deleteByInventoryItemId( @@ -233,6 +237,7 @@ export default class InventoryLevelService { /** * Deletes an inventory level by ID. * @param inventoryLevelId - The ID or IDs of the inventory level to delete. + * @param context */ @InjectEntityManager() async delete( @@ -256,6 +261,7 @@ export default class InventoryLevelService { /** * Deletes inventory levels by location ID. * @param locationId - The ID of the location to delete inventory levels for. + * @param context */ @InjectEntityManager() async deleteByLocationId( @@ -276,19 +282,19 @@ export default class InventoryLevelService { * Gets the total stocked quantity for a specific inventory item at multiple locations. * @param inventoryItemId - The ID of the inventory item. * @param locationIds - The IDs of the locations. + * @param context * @return The total stocked quantity. */ - @InjectEntityManager() async getStockedQuantity( inventoryItemId: string, locationIds: string[] | string, - @MedusaContext() context: SharedContext = {} + context: SharedContext = {} ): Promise { if (!Array.isArray(locationIds)) { locationIds = [locationIds] } - const manager = context.transactionManager! + const manager = context.transactionManager ?? this.manager_ const levelRepository = manager.getRepository(InventoryLevel) const result = await levelRepository @@ -305,19 +311,19 @@ export default class InventoryLevelService { * Gets the total available quantity for a specific inventory item at multiple locations. * @param inventoryItemId - The ID of the inventory item. * @param locationIds - The IDs of the locations. + * @param context * @return The total available quantity. */ - @InjectEntityManager() async getAvailableQuantity( inventoryItemId: string, locationIds: string[] | string, - @MedusaContext() context: SharedContext = {} + context: SharedContext = {} ): Promise { if (!Array.isArray(locationIds)) { locationIds = [locationIds] } - const manager = context.transactionManager! + const manager = context.transactionManager ?? this.manager_ const levelRepository = manager.getRepository(InventoryLevel) const result = await levelRepository @@ -334,19 +340,19 @@ export default class InventoryLevelService { * Gets the total reserved quantity for a specific inventory item at multiple locations. * @param inventoryItemId - The ID of the inventory item. * @param locationIds - The IDs of the locations. + * @param context * @return The total reserved quantity. */ - @InjectEntityManager() async getReservedQuantity( inventoryItemId: string, locationIds: string[] | string, - @MedusaContext() context: SharedContext = {} + context: SharedContext = {} ): Promise { if (!Array.isArray(locationIds)) { locationIds = [locationIds] } - const manager = context.transactionManager! + const manager = context.transactionManager ?? this.manager_ const levelRepository = manager.getRepository(InventoryLevel) const result = await levelRepository diff --git a/packages/inventory/src/services/inventory.ts b/packages/inventory/src/services/inventory.ts index 33c03a284c..a5c5850437 100644 --- a/packages/inventory/src/services/inventory.ts +++ b/packages/inventory/src/services/inventory.ts @@ -59,16 +59,13 @@ export default class InventoryService implements IInventoryService { * Lists inventory items that match the given selector * @param selector - the selector to filter inventory items by * @param config - the find configuration to use + * @param context * @return A tuple of inventory items and their total count */ - @InjectEntityManager( - (target) => - target.moduleDeclaration?.resources === MODULE_RESOURCE_TYPE.ISOLATED - ) async listInventoryItems( selector: FilterableInventoryItemProps, config: FindConfig = { relations: [], skip: 0, take: 10 }, - @MedusaContext() context: SharedContext = {} + context: SharedContext = {} ): Promise<[InventoryItemDTO[], number]> { return await this.inventoryItemService_.listAndCount( selector, @@ -81,12 +78,9 @@ export default class InventoryService implements IInventoryService { * Lists inventory levels that match the given selector * @param selector - the selector to filter inventory levels by * @param config - the find configuration to use + * @param context * @return A tuple of inventory levels and their total count */ - @InjectEntityManager( - (target) => - target.moduleDeclaration?.resources === MODULE_RESOURCE_TYPE.ISOLATED - ) async listInventoryLevels( selector: FilterableInventoryLevelProps, config: FindConfig = { @@ -94,7 +88,7 @@ export default class InventoryService implements IInventoryService { skip: 0, take: 10, }, - @MedusaContext() context: SharedContext = {} + context: SharedContext = {} ): Promise<[InventoryLevelDTO[], number]> { return await this.inventoryLevelService_.listAndCount( selector, @@ -107,12 +101,9 @@ export default class InventoryService implements IInventoryService { * Lists reservation items that match the given selector * @param selector - the selector to filter reservation items by * @param config - the find configuration to use + * @param context * @return A tuple of reservation items and their total count */ - @InjectEntityManager( - (target) => - target.moduleDeclaration?.resources === MODULE_RESOURCE_TYPE.ISOLATED - ) async listReservationItems( selector: FilterableReservationItemProps, config: FindConfig = { @@ -120,7 +111,7 @@ export default class InventoryService implements IInventoryService { skip: 0, take: 10, }, - @MedusaContext() context: SharedContext = {} + context: SharedContext = {} ): Promise<[ReservationItemDTO[], number]> { return await this.reservationItemService_.listAndCount( selector, @@ -133,16 +124,13 @@ export default class InventoryService implements IInventoryService { * Retrieves an inventory item with the given id * @param inventoryItemId - the id of the inventory item to retrieve * @param config - the find configuration to use + * @param context * @return The retrieved inventory item */ - @InjectEntityManager( - (target) => - target.moduleDeclaration?.resources === MODULE_RESOURCE_TYPE.ISOLATED - ) async retrieveInventoryItem( inventoryItemId: string, config?: FindConfig, - @MedusaContext() context: SharedContext = {} + context: SharedContext = {} ): Promise { const inventoryItem = await this.inventoryItemService_.retrieve( inventoryItemId, @@ -156,16 +144,13 @@ export default class InventoryService implements IInventoryService { * Retrieves an inventory level for a given inventory item and location * @param inventoryItemId - the id of the inventory item * @param locationId - the id of the location + * @param context * @return the retrieved inventory level */ - @InjectEntityManager( - (target) => - target.moduleDeclaration?.resources === MODULE_RESOURCE_TYPE.ISOLATED - ) async retrieveInventoryLevel( inventoryItemId: string, locationId: string, - @MedusaContext() context: SharedContext = {} + context: SharedContext = {} ): Promise { const [inventoryLevel] = await this.inventoryLevelService_.list( { inventory_item_id: inventoryItemId, location_id: locationId }, @@ -183,16 +168,14 @@ export default class InventoryService implements IInventoryService { /** * Retrieves a reservation item - * @param inventoryItemId - the id of the reservation item - * @return the retrieved reservation level + * @param reservationId + * @param context + * @param reservationId + * @param context */ - @InjectEntityManager( - (target) => - target.moduleDeclaration?.resources === MODULE_RESOURCE_TYPE.ISOLATED - ) async retrieveReservationItem( reservationId: string, - @MedusaContext() context: SharedContext = {} + context: SharedContext = {} ): Promise { return await this.reservationItemService_.retrieve( reservationId, @@ -204,6 +187,7 @@ export default class InventoryService implements IInventoryService { /** * Creates a reservation item * @param input - the input object + * @param context * @return The created reservation item */ @InjectEntityManager( @@ -242,6 +226,7 @@ export default class InventoryService implements IInventoryService { /** * Creates an inventory item * @param input - the input object + * @param context * @return The created inventory item */ @InjectEntityManager( @@ -262,6 +247,7 @@ export default class InventoryService implements IInventoryService { /** * Creates an inventory item * @param input - the input object + * @param context * @return The created inventory level */ @InjectEntityManager( @@ -279,6 +265,7 @@ export default class InventoryService implements IInventoryService { * Updates an inventory item * @param inventoryItemId - the id of the inventory item to update * @param input - the input object + * @param context * @return The updated inventory item */ @InjectEntityManager( @@ -301,6 +288,7 @@ export default class InventoryService implements IInventoryService { /** * Deletes an inventory item * @param inventoryItemId - the id of the inventory item to delete + * @param context */ @InjectEntityManager( (target) => @@ -350,6 +338,7 @@ export default class InventoryService implements IInventoryService { * Deletes an inventory level * @param inventoryItemId - the id of the inventory item associated with the level * @param locationId - the id of the location associated with the level + * @param context */ @InjectEntityManager( (target) => @@ -378,6 +367,7 @@ export default class InventoryService implements IInventoryService { * @param inventoryItemId - the id of the inventory item associated with the level * @param locationId - the id of the location associated with the level * @param input - the input object + * @param context * @return The updated inventory level */ @InjectEntityManager( @@ -412,8 +402,10 @@ export default class InventoryService implements IInventoryService { /** * Updates a reservation item - * @param inventoryItemId - the id of the inventory item associated with the level + * @param reservationItemId * @param input - the input object + * @param context + * @param context * @return The updated inventory level */ @InjectEntityManager( @@ -435,6 +427,7 @@ export default class InventoryService implements IInventoryService { /** * Deletes reservation items by line item * @param lineItemId - the id of the line item associated with the reservation item + * @param context */ @InjectEntityManager( (target) => @@ -453,6 +446,7 @@ export default class InventoryService implements IInventoryService { /** * Deletes a reservation item * @param reservationItemId - the id of the reservation item to delete + * @param context */ @InjectEntityManager( (target) => @@ -470,6 +464,7 @@ export default class InventoryService implements IInventoryService { * @param inventoryItemId - the id of the inventory item * @param locationId - the id of the location * @param adjustment - the number to adjust the inventory by (can be positive or negative) + * @param context * @return The updated inventory level * @throws when the inventory level is not found */ @@ -510,17 +505,14 @@ export default class InventoryService implements IInventoryService { * Retrieves the available quantity of a given inventory item in a given location. * @param inventoryItemId - the id of the inventory item * @param locationIds - the ids of the locations to check + * @param context * @return The available quantity * @throws when the inventory item is not found */ - @InjectEntityManager( - (target) => - target.moduleDeclaration?.resources === MODULE_RESOURCE_TYPE.ISOLATED - ) async retrieveAvailableQuantity( inventoryItemId: string, locationIds: string[], - @MedusaContext() context: SharedContext = {} + context: SharedContext = {} ): Promise { // Throws if item does not exist await this.inventoryItemService_.retrieve( @@ -549,17 +541,14 @@ export default class InventoryService implements IInventoryService { * Retrieves the stocked quantity of a given inventory item in a given location. * @param inventoryItemId - the id of the inventory item * @param locationIds - the ids of the locations to check + * @param context * @return The stocked quantity * @throws when the inventory item is not found */ - @InjectEntityManager( - (target) => - target.moduleDeclaration?.resources === MODULE_RESOURCE_TYPE.ISOLATED - ) async retrieveStockedQuantity( inventoryItemId: string, locationIds: string[], - @MedusaContext() context: SharedContext = {} + context: SharedContext = {} ): Promise { // Throws if item does not exist await this.inventoryItemService_.retrieve( @@ -588,17 +577,14 @@ export default class InventoryService implements IInventoryService { * Retrieves the reserved quantity of a given inventory item in a given location. * @param inventoryItemId - the id of the inventory item * @param locationIds - the ids of the locations to check + * @param context * @return The reserved quantity * @throws when the inventory item is not found */ - @InjectEntityManager( - (target) => - target.moduleDeclaration?.resources === MODULE_RESOURCE_TYPE.ISOLATED - ) async retrieveReservedQuantity( inventoryItemId: string, locationIds: string[], - @MedusaContext() context: SharedContext = {} + context: SharedContext = {} ): Promise { // Throws if item does not exist await this.inventoryItemService_.retrieve( @@ -628,6 +614,7 @@ export default class InventoryService implements IInventoryService { * @param inventoryItemId - the id of the inventory item * @param locationIds - the ids of the locations to check * @param quantity - the quantity to check + * @param context * @return Whether there is sufficient inventory */ @InjectEntityManager( diff --git a/packages/inventory/src/services/reservation-item.ts b/packages/inventory/src/services/reservation-item.ts index 61743a15df..7dae650d62 100644 --- a/packages/inventory/src/services/reservation-item.ts +++ b/packages/inventory/src/services/reservation-item.ts @@ -48,15 +48,15 @@ export default class ReservationItemService { * Lists reservation items that match the provided filter. * @param selector - Filters to apply to the reservation items. * @param config - Configuration for the query. + * @param context * @return Array of reservation items that match the selector. */ - @InjectEntityManager() async list( selector: FilterableReservationItemProps = {}, config: FindConfig = { relations: [], skip: 0, take: 10 }, - @MedusaContext() context: SharedContext = {} + context: SharedContext = {} ): Promise { - const manager = context.transactionManager! + const manager = context.transactionManager ?? this.manager_ const itemRepository = manager.getRepository(ReservationItem) const query = buildQuery(selector, config) as FindManyOptions @@ -68,15 +68,15 @@ export default class ReservationItemService { * Lists reservation items that match the provided filter and returns the total count. * @param selector - Filters to apply to the reservation items. * @param config - Configuration for the query. + * @param context * @return Array of reservation items that match the selector and the total count. */ - @InjectEntityManager() async listAndCount( selector: FilterableReservationItemProps = {}, config: FindConfig = { relations: [], skip: 0, take: 10 }, - @MedusaContext() context: SharedContext = {} + context: SharedContext = {} ): Promise<[ReservationItem[], number]> { - const manager = context.transactionManager! + const manager = context.transactionManager ?? this.manager_ const itemRepository = manager.getRepository(ReservationItem) const query = buildQuery(selector, config) as FindManyOptions @@ -88,14 +88,14 @@ export default class ReservationItemService { * Retrieves a reservation item by its id. * @param reservationItemId - The id of the reservation item to retrieve. * @param config - Configuration for the query. + * @param context * @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 = {}, - @MedusaContext() context: SharedContext = {} + context: SharedContext = {} ): Promise { if (!isDefined(reservationItemId)) { throw new MedusaError( @@ -104,7 +104,7 @@ export default class ReservationItemService { ) } - const manager = context.transactionManager! + const manager = context.transactionManager ?? this.manager_ const reservationItemRepository = manager.getRepository(ReservationItem) const query = buildQuery( @@ -126,6 +126,7 @@ export default class ReservationItemService { /** * Create a new reservation item. * @param data - The reservation item data. + * @param context * @return The created reservation item. */ @InjectEntityManager() @@ -168,6 +169,7 @@ export default class ReservationItemService { * Update a reservation item. * @param reservationItemId - The reservation item's id. * @param data - The reservation item data to update. + * @param context * @return The updated reservation item. */ @InjectEntityManager() @@ -232,6 +234,7 @@ export default class ReservationItemService { /** * Deletes a reservation item by line item id. * @param lineItemId - the id of the line item to delete. + * @param context */ @InjectEntityManager() async deleteByLineItem( @@ -274,6 +277,7 @@ export default class ReservationItemService { /** * Deletes reservation items by location ID. * @param locationId - The ID of the location to delete reservations for. + * @param context */ @InjectEntityManager() async deleteByLocationId( @@ -298,7 +302,9 @@ export default class ReservationItemService { /** * Deletes a reservation item by id. * @param reservationItemId - the id of the reservation item to delete. + * @param context */ + @InjectEntityManager() async delete( reservationItemId: string | string[], @MedusaContext() context: SharedContext = {} diff --git a/packages/medusa/src/api/routes/admin/products/list-products.ts b/packages/medusa/src/api/routes/admin/products/list-products.ts index 592e9d4b63..4b1c42f348 100644 --- a/packages/medusa/src/api/routes/admin/products/list-products.ts +++ b/packages/medusa/src/api/routes/admin/products/list-products.ts @@ -233,45 +233,37 @@ export default async (req, res) => { const manager = req.scope.resolve("manager") - const [products, count] = await manager.transaction( - async (transactionManager) => { - const [rawProducts, count] = await productService - .withTransaction(transactionManager) - .listAndCount(req.filterableFields, req.listConfig) - - let products: (Product | PricedProduct)[] = rawProducts - - // We only set prices if variants.prices are requested - const shouldSetPricing = ["variants", "variants.prices"].every( - (relation) => relations?.includes(relation) - ) - - if (shouldSetPricing) { - products = await pricingService - .withTransaction(transactionManager) - .setProductPrices(rawProducts) - } - - // We only set availability if variants are requested - const shouldSetAvailability = relations?.includes("variants") - - if (inventoryService && shouldSetAvailability) { - const [salesChannelsIds] = await salesChannelService - .withTransaction(transactionManager) - .listAndCount({}, { select: ["id"] }) - - products = await productVariantInventoryService - .withTransaction(transactionManager) - .setProductAvailability( - products, - salesChannelsIds.map((salesChannel) => salesChannel.id) - ) - } - - return [products, count] - } + const [rawProducts, count] = await productService.listAndCount( + req.filterableFields, + req.listConfig ) + let products: (Product | PricedProduct)[] = rawProducts + + // We only set prices if variants.prices are requested + const shouldSetPricing = ["variants", "variants.prices"].every((relation) => + relations?.includes(relation) + ) + + if (shouldSetPricing) { + products = await pricingService.setProductPrices(rawProducts) + } + + // We only set availability if variants are requested + const shouldSetAvailability = relations?.includes("variants") + + if (inventoryService && shouldSetAvailability) { + const [salesChannelsIds] = await salesChannelService.listAndCount( + {}, + { select: ["id"] } + ) + + products = await productVariantInventoryService.setProductAvailability( + products, + salesChannelsIds.map((salesChannel) => salesChannel.id) + ) + } + res.json({ products, count, diff --git a/packages/medusa/src/api/routes/store/products/list-products.ts b/packages/medusa/src/api/routes/store/products/list-products.ts index b2bebf54e8..dc7c1c399b 100644 --- a/packages/medusa/src/api/routes/store/products/list-products.ts +++ b/packages/medusa/src/api/routes/store/products/list-products.ts @@ -216,82 +216,64 @@ export default async (req, res) => { } } - const manager = req.scope.resolve("manager") + const promises: Promise[] = [] - const [computedProducts, count] = await manager.transaction( - async (transactionManager) => { - const promises: Promise[] = [] + promises.push(productService.listAndCount(filterableFields, listConfig)) - promises.push( - productService - .withTransaction(transactionManager) - .listAndCount(filterableFields, listConfig) - ) + if (validated.cart_id) { + promises.push( + cartService.retrieve(validated.cart_id, { + select: ["id", "region_id"] as any, + relations: ["region"], + }) + ) + } - if (validated.cart_id) { - promises.push( - cartService - .withTransaction(transactionManager) - .retrieve(validated.cart_id, { - select: ["id", "region_id"] as any, - relations: ["region"], - }) - ) - } + const [[rawProducts, count], cart] = await Promise.all(promises) - const [[rawProducts, count], cart] = await Promise.all(promises) + if (validated.cart_id) { + regionId = cart.region_id + currencyCode = cart.region.currency_code + } - if (validated.cart_id) { - regionId = cart.region_id - currencyCode = cart.region.currency_code - } + // Create a new reference just for naming purpose + const computedProducts = rawProducts - // Create a new reference just for naming purpose - const computedProducts = rawProducts - - // We only set prices if variants.prices are requested - const shouldSetPricing = ["variants", "variants.prices"].every( - (relation) => listConfig.relations?.includes(relation) - ) - - // We only set availability if variants are requested - const shouldSetAvailability = listConfig.relations?.includes("variants") - - const decoratePromises: Promise[] = [] - - if (shouldSetPricing) { - decoratePromises.push( - pricingService - .withTransaction(transactionManager) - .setProductPrices(computedProducts, { - cart_id: cart_id, - region_id: regionId, - currency_code: currencyCode, - customer_id: req.user?.customer_id, - include_discount_prices: true, - }) - ) - } - - if (shouldSetAvailability) { - decoratePromises.push( - productVariantInventoryService - .withTransaction(transactionManager) - .setProductAvailability( - computedProducts, - filterableFields.sales_channel_id - ) - ) - } - - // We can run them concurrently as the new properties are assigned to the references - // of the appropriate entity - await Promise.all(decoratePromises) - - return [computedProducts, count] - } + // We only set prices if variants.prices are requested + const shouldSetPricing = ["variants", "variants.prices"].every((relation) => + listConfig.relations?.includes(relation) ) + // We only set availability if variants are requested + const shouldSetAvailability = listConfig.relations?.includes("variants") + + const decoratePromises: Promise[] = [] + + if (shouldSetPricing) { + decoratePromises.push( + pricingService.setProductPrices(computedProducts, { + cart_id: cart_id, + region_id: regionId, + currency_code: currencyCode, + customer_id: req.user?.customer_id, + include_discount_prices: true, + }) + ) + } + + if (shouldSetAvailability) { + decoratePromises.push( + productVariantInventoryService.setProductAvailability( + computedProducts, + filterableFields.sales_channel_id + ) + ) + } + + // We can run them concurrently as the new properties are assigned to the references + // of the appropriate entity + await Promise.all(decoratePromises) + res.json({ products: cleanResponseData(computedProducts, req.allowedProperties || []), count, diff --git a/packages/medusa/src/services/product-variant-inventory.ts b/packages/medusa/src/services/product-variant-inventory.ts index 5d138d4208..5af9752a7b 100644 --- a/packages/medusa/src/services/product-variant-inventory.ts +++ b/packages/medusa/src/services/product-variant-inventory.ts @@ -375,6 +375,10 @@ class ProductVariantInventoryService extends TransactionBaseService { } let locationId = context.locationId + const moduleContext = { + transactionManager: this.activeManager_, + } + if (!isDefined(locationId) && context.salesChannelId) { const locationIds = await this.salesChannelLocationService_ .withTransaction(this.activeManager_) @@ -388,10 +392,14 @@ class ProductVariantInventoryService extends TransactionBaseService { } const [locations, count] = - await this.inventoryService_.listInventoryLevels({ - location_id: locationIds, - inventory_item_id: variantInventory[0].inventory_item_id, - }) + await this.inventoryService_.listInventoryLevels( + { + location_id: locationIds, + inventory_item_id: variantInventory[0].inventory_item_id, + }, + undefined, + moduleContext + ) if (count === 0) { throw new MedusaError( @@ -406,12 +414,15 @@ class ProductVariantInventoryService extends TransactionBaseService { const reservationItems = await Promise.all( variantInventory.map(async (inventoryPart) => { const itemQuantity = inventoryPart.required_quantity * quantity - return await this.inventoryService_.createReservationItem({ - ...toReserve, - location_id: locationId as string, - inventory_item_id: inventoryPart.inventory_item_id, - quantity: itemQuantity, - }) + return await this.inventoryService_.createReservationItem( + { + ...toReserve, + location_id: locationId as string, + inventory_item_id: inventoryPart.inventory_item_id, + quantity: itemQuantity, + }, + moduleContext + ) }) ) @@ -456,6 +467,9 @@ class ProductVariantInventoryService extends TransactionBaseService { ) } + const context = { + transactionManager: this.activeManager_, + } const [reservations, reservationCount] = await this.inventoryService_.listReservationItems( { @@ -463,7 +477,8 @@ class ProductVariantInventoryService extends TransactionBaseService { }, { order: { created_at: "DESC" }, - } + }, + context ) reservations.sort((a, _) => { @@ -485,7 +500,10 @@ class ProductVariantInventoryService extends TransactionBaseService { (r) => r.quantity === deltaUpdate && r.location_id === locationId ) if (exactReservation) { - await this.inventoryService_.deleteReservationItem(exactReservation.id) + await this.inventoryService_.deleteReservationItem( + exactReservation.id, + context + ) return } @@ -505,7 +523,8 @@ class ProductVariantInventoryService extends TransactionBaseService { if (reservationsToDelete.length) { await this.inventoryService_.deleteReservationItem( - reservationsToDelete.map((r) => r.id) + reservationsToDelete.map((r) => r.id), + context ) } @@ -514,7 +533,8 @@ class ProductVariantInventoryService extends TransactionBaseService { reservationToUpdate.id, { quantity: reservationToUpdate.quantity - remainingQuantity, - } + }, + context ) } } @@ -543,11 +563,19 @@ class ProductVariantInventoryService extends TransactionBaseService { continue } + const context = { + transactionManager: this.activeManager_, + } + const [inventoryLevels, inventoryLevelCount] = - await this.inventoryService_.listInventoryLevels({ - inventory_item_id: pvInventoryItems.map((i) => i.inventory_item_id), - location_id: locationId, - }) + await this.inventoryService_.listInventoryLevels( + { + inventory_item_id: pvInventoryItems.map((i) => i.inventory_item_id), + location_id: locationId, + }, + undefined, + context + ) if (!inventoryLevelCount) { throw new MedusaError( @@ -653,7 +681,10 @@ class ProductVariantInventoryService extends TransactionBaseService { return await this.inventoryService_.adjustInventory( inventoryPart.inventory_item_id, locationId, - itemQuantity + itemQuantity, + { + transactionManager: this.activeManager_, + } ) }) ) diff --git a/packages/medusa/src/services/sales-channel-location.ts b/packages/medusa/src/services/sales-channel-location.ts index 300b56d83b..9241cb99f2 100644 --- a/packages/medusa/src/services/sales-channel-location.ts +++ b/packages/medusa/src/services/sales-channel-location.ts @@ -5,7 +5,6 @@ import { TransactionBaseService } from "../interfaces" import { SalesChannelLocation } from "../models/sales-channel-location" import SalesChannelService from "./sales-channel" - type InjectedDependencies = { stockLocationService: IStockLocationService salesChannelService: SalesChannelService @@ -80,18 +79,20 @@ class SalesChannelLocationService extends TransactionBaseService { if (this.stockLocationService_) { // trhows error if not found - await this.stockLocationService_.retrieve(locationId) + await this.stockLocationService_.retrieve(locationId, undefined, { + transactionManager: this.activeManager_, + }) } - const salesChannelLocation = this.activeManager_.create( - SalesChannelLocation, - { - sales_channel_id: salesChannel.id, - location_id: locationId, - } - ) + const salesChannelLocationRepo = + this.activeManager_.getRepository(SalesChannelLocation) - await this.activeManager_.save(salesChannelLocation) + const salesChannelLocation = salesChannelLocationRepo.create({ + sales_channel_id: salesChannel.id, + location_id: locationId, + }) + + await salesChannelLocationRepo.save(salesChannelLocation) } /** @@ -129,10 +130,18 @@ class SalesChannelLocationService extends TransactionBaseService { * @returns {Promise} A promise that resolves with an array of sales channel IDs. */ async listSalesChannelIds(locationId: string): Promise { - const manager = this.transactionManager_ || this.manager_ - const location = await this.stockLocationService_.retrieve(locationId) + const location = await this.stockLocationService_.retrieve( + locationId, + undefined, + { + transactionManager: this.activeManager_, + } + ) - const salesChannelLocations = await manager.find(SalesChannelLocation, { + const salesChannelRepo = + this.activeManager_.getRepository(SalesChannelLocation) + + const salesChannelLocations = await salesChannelRepo.find({ where: { location_id: location.id }, select: ["sales_channel_id"], }) diff --git a/packages/stock-location/src/services/stock-location.ts b/packages/stock-location/src/services/stock-location.ts index 74772d7685..764b0adc3e 100644 --- a/packages/stock-location/src/services/stock-location.ts +++ b/packages/stock-location/src/services/stock-location.ts @@ -53,18 +53,15 @@ export default class StockLocationService { * Lists all stock locations that match the given selector. * @param selector - Properties to filter by. * @param config - Additional configuration for the query. + * @param context * @return A list of stock locations. */ - @InjectEntityManager( - (target) => - target.moduleDeclaration?.resources === MODULE_RESOURCE_TYPE.ISOLATED - ) async list( selector: FilterableStockLocationProps = {}, config: FindConfig = { relations: [], skip: 0, take: 10 }, - @MedusaContext() context: SharedContext = {} + context: SharedContext = {} ): Promise { - const manager = context.transactionManager! + const manager = context.transactionManager ?? this.manager_ const locationRepo = manager.getRepository(StockLocation) const query = buildQuery(selector, config) @@ -75,18 +72,15 @@ export default class StockLocationService { * Lists all stock locations that match the given selector and returns the count of matching stock locations. * @param selector - Properties to filter by. * @param config - Additional configuration for the query. + * @param context * @return A list of stock locations and the count of matching stock locations. */ - @InjectEntityManager( - (target) => - target.moduleDeclaration?.resources === MODULE_RESOURCE_TYPE.ISOLATED - ) async listAndCount( selector: FilterableStockLocationProps = {}, config: FindConfig = { relations: [], skip: 0, take: 10 }, - @MedusaContext() context: SharedContext = {} + context: SharedContext = {} ): Promise<[StockLocation[], number]> { - const manager = context.transactionManager! + const manager = context.transactionManager ?? this.manager_ const locationRepo = manager.getRepository(StockLocation) const query = buildQuery(selector, config) @@ -97,17 +91,14 @@ export default class StockLocationService { * Retrieves a Stock Location by its ID. * @param stockLocationId - The ID of the stock location. * @param config - Additional configuration for the query. + * @param context * @return The stock location. * @throws If the stock location ID is not definedor the stock location with the given ID was not found. */ - @InjectEntityManager( - (target) => - target.moduleDeclaration?.resources === MODULE_RESOURCE_TYPE.ISOLATED - ) async retrieve( stockLocationId: string, config: FindConfig = {}, - @MedusaContext() context: SharedContext = {} + context: SharedContext = {} ): Promise { if (!isDefined(stockLocationId)) { throw new MedusaError( @@ -116,7 +107,7 @@ export default class StockLocationService { ) } - const manager = context.transactionManager! + const manager = context.transactionManager ?? this.manager_ const locationRepo = manager.getRepository(StockLocation) const query = buildQuery({ id: stockLocationId }, config) @@ -135,6 +126,7 @@ export default class StockLocationService { /** * Creates a new stock location. * @param data - The input data for creating a Stock Location. + * @param context * @returns The created stock location. */ @InjectEntityManager( @@ -182,6 +174,7 @@ export default class StockLocationService { * Updates an existing stock location. * @param stockLocationId - The ID of the stock location to update. * @param updateData - The update data for the stock location. + * @param context * @returns The updated stock location. */ @InjectEntityManager( @@ -231,6 +224,7 @@ export default class StockLocationService { * Updates an address for a Stock Location. * @param addressId - The ID of the address to update. * @param address - The update data for the address. + * @param context * @returns The updated stock location address. */ @InjectEntityManager( @@ -275,6 +269,7 @@ export default class StockLocationService { /** * Deletes a Stock Location. * @param id - The ID of the stock location to delete. + * @param context * @returns An empty promise. */ @InjectEntityManager(