diff --git a/packages/medusa/src/services/cart.ts b/packages/medusa/src/services/cart.ts index 6e5d0c4284..c7baf16699 100644 --- a/packages/medusa/src/services/cart.ts +++ b/packages/medusa/src/services/cart.ts @@ -1,6 +1,5 @@ import _ from "lodash" import { MedusaError, Validator } from "medusa-core-utils" -import { BaseService } from "medusa-interfaces" import { DeepPartial, EntityManager, In } from "typeorm" import { IPriceSelectionStrategy } from "../interfaces/price-selection-strategy" import { Address } from "../models/address" @@ -37,6 +36,8 @@ import InventoryService from "./inventory" import CustomShippingOptionService from "./custom-shipping-option" import LineItemAdjustmentService from "./line-item-adjustment" import { LineItemRepository } from "../repositories/line-item" +import { TransactionBaseService } from "../interfaces" +import { buildQuery, setMetadata, validateId } from "../utils" type InjectedDependencies = { manager: EntityManager @@ -70,14 +71,16 @@ type TotalsConfig = { /* Provides layer to manipulate carts. * @implements BaseService */ -class CartService extends BaseService { - static Events = { +class CartService extends TransactionBaseService { + static readonly Events = { CUSTOMER_UPDATED: "cart.customer_updated", CREATED: "cart.created", UPDATED: "cart.updated", } - protected readonly manager_: EntityManager + protected manager_: EntityManager + protected transactionManager_: EntityManager | undefined + protected readonly shippingMethodRepository_: typeof ShippingMethodRepository protected readonly cartRepository_: typeof CartRepository protected readonly addressRepository_: typeof AddressRepository @@ -124,7 +127,30 @@ class CartService extends BaseService { lineItemAdjustmentService, priceSelectionStrategy, }: InjectedDependencies) { - super() + super({ + manager, + cartRepository, + shippingMethodRepository, + lineItemRepository, + eventBusService, + paymentProviderService, + productService, + productVariantService, + taxProviderService, + regionService, + lineItemService, + shippingOptionService, + customerService, + discountService, + giftCardService, + totalsService, + addressRepository, + paymentSessionRepository, + inventoryService, + customShippingOptionService, + lineItemAdjustmentService, + priceSelectionStrategy, + }) this.manager_ = manager this.shippingMethodRepository_ = shippingMethodRepository @@ -150,41 +176,6 @@ class CartService extends BaseService { this.priceSelectionStrategy_ = priceSelectionStrategy } - withTransaction(transactionManager: EntityManager): CartService { - if (!transactionManager) { - return this - } - - const cloned = new CartService({ - manager: transactionManager, - taxProviderService: this.taxProviderService_, - cartRepository: this.cartRepository_, - lineItemRepository: this.lineItemRepository_, - eventBusService: this.eventBus_, - paymentProviderService: this.paymentProviderService_, - paymentSessionRepository: this.paymentSessionRepository_, - shippingMethodRepository: this.shippingMethodRepository_, - productService: this.productService_, - productVariantService: this.productVariantService_, - regionService: this.regionService_, - lineItemService: this.lineItemService_, - shippingOptionService: this.shippingOptionService_, - customerService: this.customerService_, - discountService: this.discountService_, - totalsService: this.totalsService_, - addressRepository: this.addressRepository_, - giftCardService: this.giftCardService_, - inventoryService: this.inventoryService_, - customShippingOptionService: this.customShippingOptionService_, - lineItemAdjustmentService: this.lineItemAdjustmentService_, - priceSelectionStrategy: this.priceSelectionStrategy_, - }) - - cloned.transactionManager_ = transactionManager - - return cloned - } - protected transformQueryForTotals_( config: FindConfig ): FindConfig & { totalsToSelect: TotalField[] } { @@ -292,7 +283,7 @@ class CartService extends BaseService { this.cartRepository_ ) - const query = this.buildQuery_(selector, config) + const query = buildQuery(selector, config) return await cartRepo.find(query) } ) @@ -315,12 +306,12 @@ class CartService extends BaseService { const cartRepo = transactionManager.getCustomRepository( this.cartRepository_ ) - const validatedId = this.validateId_(cartId) + const validatedId = validateId(cartId) const { select, relations, totalsToSelect } = this.transformQueryForTotals_(options) - const query = this.buildQuery_( + const query = buildQuery( { id: validatedId }, { ...options, select, relations } ) @@ -714,16 +705,20 @@ class CartService extends BaseService { * @param shouldAdd - flag to indicate, if we should add or remove * @return void */ - async adjustFreeShipping_(cart: Cart, shouldAdd: boolean): Promise { + protected async adjustFreeShipping_( + cart: Cart, + shouldAdd: boolean + ): Promise { + const transactionManager = this.transactionManager_ ?? this.manager_ + if (cart.shipping_methods?.length) { - const shippingMethodRepository = - this.transactionManager_.getCustomRepository( - this.shippingMethodRepository_ - ) + const shippingMethodRepository = transactionManager.getCustomRepository( + this.shippingMethodRepository_ + ) // if any free shipping discounts, we ensure to update shipping method amount if (shouldAdd) { - return shippingMethodRepository.update( + await shippingMethodRepository.update( { id: In( cart.shipping_methods.map((shippingMethod) => shippingMethod.id) @@ -855,8 +850,8 @@ class CartService extends BaseService { ) } - if ("metadata" in data) { - cart.metadata = this.setMetadata_(cart, data.metadata) + if (data?.metadata) { + cart.metadata = setMetadata(cart, data.metadata) } if ("context" in data) { @@ -1352,7 +1347,7 @@ class CartService extends BaseService { * @param cartOrCartId - the id of the cart to set payment session for * @return the result of the update operation. */ - async setPaymentSessions(cartOrCartId: Cart | string): Promise { + async setPaymentSessions(cartOrCartId: Cart | string): Promise { return await this.atomicPhase_( async (transactionManager: EntityManager) => { const psRepo = transactionManager.getCustomRepository( @@ -1679,6 +1674,8 @@ class CartService extends BaseService { regionId?: string, customer_id?: string ): Promise { + const transactionManager = this.transactionManager_ ?? this.manager_ + // If the cart contains items, we update the price of the items // to match the updated region or customer id (keeping the old // value if it exists) @@ -1693,7 +1690,7 @@ class CartService extends BaseService { await Promise.all( cart.items.map(async (item) => { const availablePrice = await this.priceSelectionStrategy_ - .withTransaction(this.transactionManager_) + .withTransaction(transactionManager) .calculateVariantPrice(item.variant_id, { region_id: region.id, currency_code: region.currency_code, @@ -1708,14 +1705,14 @@ class CartService extends BaseService { availablePrice.calculatedPrice !== null ) { return this.lineItemService_ - .withTransaction(this.transactionManager_) + .withTransaction(transactionManager) .update(item.id, { has_shipping: false, unit_price: availablePrice.calculatedPrice, }) } else { await this.lineItemService_ - .withTransaction(this.transactionManager_) + .withTransaction(transactionManager) .delete(item.id) return } @@ -1737,6 +1734,8 @@ class CartService extends BaseService { regionId: string, countryCode: string | null ): Promise { + const transactionManager = this.transactionManager_ ?? this.manager_ + if (cart.completed_at || cart.payment_authorized_at) { throw new MedusaError( MedusaError.Types.NOT_ALLOWED, @@ -1750,7 +1749,7 @@ class CartService extends BaseService { .retrieve(regionId, { relations: ["countries"], }) - const addrRepo = this.transactionManager_.getCustomRepository( + const addrRepo = transactionManager.getCustomRepository( this.addressRepository_ ) cart.region = region @@ -1828,7 +1827,7 @@ class CartService extends BaseService { // new shipping method if (cart.shipping_methods && cart.shipping_methods.length) { await this.shippingOptionService_ - .withTransaction(this.transactionManager_) + .withTransaction(transactionManager) .deleteShippingMethods(cart.shipping_methods) } @@ -1841,7 +1840,7 @@ class CartService extends BaseService { cart.gift_cards = [] if (cart.payment_sessions && cart.payment_sessions.length) { - const paymentSessionRepo = this.transactionManager_.getCustomRepository( + const paymentSessionRepo = transactionManager.getCustomRepository( this.paymentSessionRepository_ ) await paymentSessionRepo.delete({ @@ -1859,7 +1858,7 @@ class CartService extends BaseService { * @param cartId - the id of the cart to delete * @return the deleted cart or undefined if the cart was not found. */ - async delete(cartId: string): Promise { + async delete(cartId: string): Promise { return await this.atomicPhase_( async (transactionManager: EntityManager) => { const cart = await this.retrieve(cartId, { @@ -1913,7 +1912,7 @@ class CartService extends BaseService { this.cartRepository_ ) - const validatedId = this.validateId_(cartId) + const validatedId = validateId(cartId) if (typeof key !== "string") { throw new MedusaError( MedusaError.Types.INVALID_ARGUMENT, @@ -1972,20 +1971,22 @@ class CartService extends BaseService { } protected async refreshAdjustments_(cart: Cart): Promise { + const transactionManager = this.transactionManager_ ?? this.manager_ + const nonReturnLineIDs = cart.items .filter((item) => !item.is_return) .map((i) => i.id) // delete all old non return line item adjustments await this.lineItemAdjustmentService_ - .withTransaction(this.transactionManager_) + .withTransaction(transactionManager) .delete({ item_id: nonReturnLineIDs, }) // potentially create/update line item adjustments await this.lineItemAdjustmentService_ - .withTransaction(this.transactionManager_) + .withTransaction(transactionManager) .createAdjustments(cart) } @@ -2001,7 +2002,7 @@ class CartService extends BaseService { const cartRepo = transactionManager.getCustomRepository( this.cartRepository_ ) - const validatedId = this.validateId_(cartId) + const validatedId = validateId(cartId) if (typeof key !== "string") { throw new MedusaError( diff --git a/packages/medusa/src/types/cart.ts b/packages/medusa/src/types/cart.ts index 249c0c9af0..dfba20b82f 100644 --- a/packages/medusa/src/types/cart.ts +++ b/packages/medusa/src/types/cart.ts @@ -71,5 +71,5 @@ export type CartUpdateProps = { discounts?: Discount[] customer_id?: string context?: object - metadata?: object + metadata?: Record } diff --git a/packages/medusa/src/utils/build-query.ts b/packages/medusa/src/utils/build-query.ts index 103af54379..f116404729 100644 --- a/packages/medusa/src/utils/build-query.ts +++ b/packages/medusa/src/utils/build-query.ts @@ -1,7 +1,20 @@ -import { FindConfig, Writable } from "../types/common" +import { + DateComparisonOperator, + FindConfig, + NumericalComparisonOperator, + StringComparisonOperator, + Writable, +} from "../types/common" import { FindOperator, In, Raw } from "typeorm" -type Selector = { [key in keyof TEntity]?: unknown } +type Selector = { + [key in keyof TEntity]?: TEntity[key] + | TEntity[key][] + | DateComparisonOperator + | StringComparisonOperator + | NumericalComparisonOperator +} + /** * Used to build TypeORM queries. * @param selector The selector @@ -16,7 +29,7 @@ export function buildQuery( withDeleted?: boolean } { const build = ( - obj: Record + obj: Selector ): Partial> => { return Object.entries(obj).reduce((acc, [key, value]: any) => { // Undefined values indicate that they have no significance to the query. diff --git a/packages/medusa/src/utils/index.ts b/packages/medusa/src/utils/index.ts new file mode 100644 index 0000000000..5d7ec9889f --- /dev/null +++ b/packages/medusa/src/utils/index.ts @@ -0,0 +1,3 @@ +export * from './build-query' +export * from './set-metadata' +export * from './validate-id' \ No newline at end of file diff --git a/packages/medusa/src/utils/set-metadata.ts b/packages/medusa/src/utils/set-metadata.ts index 38399e1dbb..d27cddc5ed 100644 --- a/packages/medusa/src/utils/set-metadata.ts +++ b/packages/medusa/src/utils/set-metadata.ts @@ -6,7 +6,7 @@ import { MedusaError } from "medusa-core-utils/dist" * @param metadata - the metadata to set * @return resolves to the updated result. */ -export function setMetadata_( +export function setMetadata( obj: { metadata: Record }, metadata: Record ): Record { diff --git a/packages/medusa/src/utils/validate-id.ts b/packages/medusa/src/utils/validate-id.ts index 2c616fad48..da6102e69a 100644 --- a/packages/medusa/src/utils/validate-id.ts +++ b/packages/medusa/src/utils/validate-id.ts @@ -8,7 +8,7 @@ */ import { MedusaError } from "medusa-core-utils/dist" -export function validateId_( +export function validateId( rawId: string, config: { prefix?: string; length?: number } = {} ): string {