refactor: LineItemService migration to TS + refactoring + fix (#1254)
* refactor(medusa): LineItemService migration to TS + refactoring * feat(medusa): Cleanup line-item service * feat(medusa): Rebase develop * test(medusa): Fix integration cart tests * fix(medusa): Cart service updateUnitPrices
This commit is contained in:
committed by
GitHub
parent
6830a12b46
commit
530bbd4cac
@@ -1739,7 +1739,7 @@ describe("/store/carts", () => {
|
||||
.catch((err) => console.log(err))
|
||||
|
||||
// Ensure that the discount is only applied to the standard item
|
||||
const itemId = cartWithGiftcard.data.cart.items[0].id
|
||||
const itemId = cartWithGiftcard.data.cart.items.find(item => !item.is_giftcard).id
|
||||
expect(cartWithGiftcard.data.cart.items).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
|
||||
@@ -74,5 +74,5 @@ export class StorePostCartsCartLineItemsReq {
|
||||
quantity: number
|
||||
|
||||
@IsOptional()
|
||||
metadata?: object
|
||||
metadata?: Record<string, unknown> | undefined
|
||||
}
|
||||
|
||||
@@ -1717,11 +1717,11 @@ class CartService extends BaseService {
|
||||
await this.lineItemService_
|
||||
.withTransaction(this.transactionManager_)
|
||||
.delete(item.id)
|
||||
return null
|
||||
return
|
||||
}
|
||||
})
|
||||
)
|
||||
).filter((item): item is LineItem => item)
|
||||
).filter((item): item is LineItem => !!item)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
322
packages/medusa/src/services/line-item.ts
Normal file
322
packages/medusa/src/services/line-item.ts
Normal file
@@ -0,0 +1,322 @@
|
||||
import { MedusaError } from "medusa-core-utils"
|
||||
import { BaseService } from "medusa-interfaces"
|
||||
import { EntityManager } from "typeorm"
|
||||
import { LineItemRepository } from "../repositories/line-item"
|
||||
import { LineItemTaxLineRepository } from "../repositories/line-item-tax-line"
|
||||
import { ProductService, RegionService, ProductVariantService } from "./index"
|
||||
import { CartRepository } from "../repositories/cart"
|
||||
import { LineItem } from "../models/line-item"
|
||||
import LineItemAdjustmentService from "./line-item-adjustment"
|
||||
import { Cart } from "../models/cart"
|
||||
import { LineItemAdjustment } from "../models/line-item-adjustment"
|
||||
|
||||
type InjectedDependencies = {
|
||||
manager: EntityManager
|
||||
lineItemRepository: typeof LineItemRepository
|
||||
lineItemTaxLineRepository: typeof LineItemTaxLineRepository
|
||||
cartRepository: typeof CartRepository
|
||||
productVariantService: ProductVariantService
|
||||
productService: ProductService
|
||||
regionService: RegionService
|
||||
lineItemAdjustmentService: LineItemAdjustmentService
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides layer to manipulate line items.
|
||||
* @extends BaseService
|
||||
*/
|
||||
class LineItemService extends BaseService {
|
||||
protected readonly manager_: EntityManager
|
||||
protected readonly lineItemRepository_: typeof LineItemRepository
|
||||
protected readonly itemTaxLineRepo_: typeof LineItemTaxLineRepository
|
||||
protected readonly cartRepository_: typeof CartRepository
|
||||
protected readonly productVariantService_: ProductVariantService
|
||||
protected readonly productService_: ProductService
|
||||
protected readonly regionService_: RegionService
|
||||
protected readonly lineItemAdjustmentService_: LineItemAdjustmentService
|
||||
|
||||
constructor({
|
||||
manager,
|
||||
lineItemRepository,
|
||||
lineItemTaxLineRepository,
|
||||
productVariantService,
|
||||
productService,
|
||||
regionService,
|
||||
cartRepository,
|
||||
lineItemAdjustmentService
|
||||
}: InjectedDependencies) {
|
||||
super()
|
||||
|
||||
this.manager_ = manager
|
||||
this.lineItemRepository_ = lineItemRepository
|
||||
this.itemTaxLineRepo_ = lineItemTaxLineRepository
|
||||
this.productVariantService_ = productVariantService
|
||||
this.productService_ = productService
|
||||
this.regionService_ = regionService
|
||||
this.cartRepository_ = cartRepository
|
||||
this.lineItemAdjustmentService_ = lineItemAdjustmentService
|
||||
}
|
||||
|
||||
withTransaction(transactionManager: EntityManager): LineItemService {
|
||||
if (!transactionManager) {
|
||||
return this
|
||||
}
|
||||
|
||||
const cloned = new LineItemService({
|
||||
manager: transactionManager,
|
||||
lineItemRepository: this.lineItemRepository_,
|
||||
lineItemTaxLineRepository: this.itemTaxLineRepo_,
|
||||
productVariantService: this.productVariantService_,
|
||||
productService: this.productService_,
|
||||
regionService: this.regionService_,
|
||||
cartRepository: this.cartRepository_,
|
||||
lineItemAdjustmentService: this.lineItemAdjustmentService_,
|
||||
})
|
||||
|
||||
cloned.transactionManager_ = transactionManager
|
||||
|
||||
return cloned
|
||||
}
|
||||
|
||||
async list(
|
||||
selector,
|
||||
config = { skip: 0, take: 50, order: { created_at: "DESC" } }
|
||||
): Promise<LineItem[]> {
|
||||
return await this.atomicPhase_(
|
||||
async (transactionManager: EntityManager) => {
|
||||
const lineItemRepo = transactionManager.getCustomRepository(
|
||||
this.lineItemRepository_
|
||||
)
|
||||
const query = this.buildQuery_(selector, config)
|
||||
return await lineItemRepo.find(query)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a line item by its id.
|
||||
* @param {string} id - the id of the line item to retrieve
|
||||
* @param {object} config - the config to be used at query building
|
||||
* @return {Promise<LineItem | never>} the line item
|
||||
*/
|
||||
async retrieve(id: string, config = {}): Promise<LineItem | never> {
|
||||
return await this.atomicPhase_(
|
||||
async (transactionManager: EntityManager) => {
|
||||
const lineItemRepository = transactionManager.getCustomRepository(
|
||||
this.lineItemRepository_
|
||||
)
|
||||
|
||||
const validatedId = this.validateId_(id)
|
||||
const query = this.buildQuery_({ id: validatedId }, config)
|
||||
|
||||
const lineItem = await lineItemRepository.findOne(query)
|
||||
|
||||
if (!lineItem) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_FOUND,
|
||||
`Line item with ${id} was not found`
|
||||
)
|
||||
}
|
||||
|
||||
return lineItem
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates return line items for a given cart based on the return items in a
|
||||
* return.
|
||||
* @param {string} returnId - the id to generate return items from.
|
||||
* @param {string} cartId - the cart to assign the return line items to.
|
||||
* @return {Promise<LineItem[]>} the created line items
|
||||
*/
|
||||
async createReturnLines(
|
||||
returnId: string,
|
||||
cartId: string
|
||||
): Promise<LineItem[]> {
|
||||
return await this.atomicPhase_(
|
||||
async (transactionManager: EntityManager) => {
|
||||
const lineItemRepo = transactionManager.getCustomRepository(
|
||||
this.lineItemRepository_
|
||||
)
|
||||
|
||||
const itemTaxLineRepo = transactionManager.getCustomRepository(
|
||||
this.itemTaxLineRepo_
|
||||
)
|
||||
|
||||
const returnLineItems = await lineItemRepo
|
||||
.findByReturn(returnId)
|
||||
.then((lineItems) => {
|
||||
return lineItems.map((lineItem) =>
|
||||
lineItemRepo.create({
|
||||
cart_id: cartId,
|
||||
thumbnail: lineItem.thumbnail,
|
||||
is_return: true,
|
||||
title: lineItem.title,
|
||||
variant_id: lineItem.variant_id,
|
||||
unit_price: -1 * lineItem.unit_price,
|
||||
quantity: lineItem.return_item.quantity,
|
||||
allow_discounts: lineItem.allow_discounts,
|
||||
tax_lines: lineItem.tax_lines.map((taxLine) => {
|
||||
return itemTaxLineRepo.create({
|
||||
name: taxLine.name,
|
||||
code: taxLine.code,
|
||||
rate: taxLine.rate,
|
||||
metadata: taxLine.metadata,
|
||||
})
|
||||
}),
|
||||
metadata: lineItem.metadata,
|
||||
adjustments: lineItem.adjustments.map((adjustment) => {
|
||||
return {
|
||||
amount: -1 * adjustment.amount,
|
||||
description: adjustment.description,
|
||||
discount_id: adjustment.discount_id,
|
||||
metadata: adjustment.metadata,
|
||||
}
|
||||
}),
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
return await lineItemRepo.save(returnLineItems)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
async generate(
|
||||
variantId: string,
|
||||
regionId: string,
|
||||
quantity: number,
|
||||
context: {
|
||||
unit_price?: number
|
||||
metadata?: Record<string, unknown>
|
||||
customer_id?: string
|
||||
cart?: Cart
|
||||
} = {}
|
||||
): Promise<LineItem> {
|
||||
return await this.atomicPhase_(
|
||||
async (transactionManager: EntityManager) => {
|
||||
const [variant, region] = await Promise.all([
|
||||
this.productVariantService_
|
||||
.withTransaction(transactionManager)
|
||||
.retrieve(variantId, {
|
||||
relations: ["product"],
|
||||
include_discount_prices: true,
|
||||
}),
|
||||
this.regionService_
|
||||
.withTransaction(transactionManager)
|
||||
.retrieve(regionId),
|
||||
])
|
||||
|
||||
let unit_price = Number(context.unit_price) < 0 ? 0 : context.unit_price
|
||||
let shouldMerge = false
|
||||
|
||||
if (context.unit_price === undefined || context.unit_price === null) {
|
||||
shouldMerge = true
|
||||
unit_price = await this.productVariantService_
|
||||
.withTransaction(transactionManager)
|
||||
.getRegionPrice(variant.id, {
|
||||
regionId: region.id,
|
||||
quantity: quantity,
|
||||
customer_id: context?.customer_id,
|
||||
include_discount_prices: true,
|
||||
})
|
||||
}
|
||||
|
||||
const rawLineItem: Partial<LineItem> = {
|
||||
unit_price: unit_price as number,
|
||||
title: variant.product.title,
|
||||
description: variant.title,
|
||||
thumbnail: variant.product.thumbnail,
|
||||
variant_id: variant.id,
|
||||
quantity: quantity || 1,
|
||||
allow_discounts: variant.product.discountable,
|
||||
is_giftcard: variant.product.is_giftcard,
|
||||
metadata: context?.metadata || {},
|
||||
should_merge: shouldMerge
|
||||
}
|
||||
|
||||
const lineLitemRepo = transactionManager.getCustomRepository(this.lineItemRepository_)
|
||||
const lineItem = lineLitemRepo.create(rawLineItem)
|
||||
|
||||
if (context.cart) {
|
||||
const adjustments = await this.lineItemAdjustmentService_
|
||||
.withTransaction(transactionManager)
|
||||
.generateAdjustments(context.cart, lineItem, { variant })
|
||||
lineItem.adjustments = adjustments as unknown as LineItemAdjustment[]
|
||||
}
|
||||
|
||||
return lineItem
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a line item
|
||||
* @param {Partial<LineItem>} data - the line item object to create
|
||||
* @return {Promise<LineItem>} the created line item
|
||||
*/
|
||||
async create(data: Partial<LineItem>): Promise<LineItem> {
|
||||
return await this.atomicPhase_(
|
||||
async (transactionManager: EntityManager) => {
|
||||
const lineItemRepository = transactionManager.getCustomRepository(
|
||||
this.lineItemRepository_
|
||||
)
|
||||
|
||||
const lineItem = lineItemRepository.create(data)
|
||||
return await lineItemRepository.save(lineItem)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a line item
|
||||
* @param {string} id - the id of the line item to update
|
||||
* @param {Partial<LineItem>} data - the properties to update on line item
|
||||
* @return {Promise<LineItem>} the update line item
|
||||
*/
|
||||
async update(id: string, data: Partial<LineItem>): Promise<LineItem> {
|
||||
const { metadata, ...rest } = data
|
||||
|
||||
return await this.atomicPhase_(
|
||||
async (transactionManager: EntityManager) => {
|
||||
const lineItemRepository = transactionManager.getCustomRepository(
|
||||
this.lineItemRepository_
|
||||
)
|
||||
|
||||
const lineItem = await this.retrieve(id).then((lineItem) => {
|
||||
const lineItemMetadata = metadata
|
||||
? this.setMetadata_(lineItem, metadata)
|
||||
: lineItem.metadata
|
||||
|
||||
return Object.assign(lineItem, {
|
||||
...rest,
|
||||
metadata: lineItemMetadata,
|
||||
})
|
||||
})
|
||||
return await lineItemRepository.save(lineItem)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a line item.
|
||||
* @param {string} id - the id of the line item to delete
|
||||
* @return {Promise<LineItem | undefined>} the result of the delete operation
|
||||
*/
|
||||
async delete(id: string): Promise<LineItem | undefined> {
|
||||
return await this.atomicPhase_(
|
||||
async (transactionManager: EntityManager) => {
|
||||
const lineItemRepository = transactionManager.getCustomRepository(
|
||||
this.lineItemRepository_
|
||||
)
|
||||
|
||||
return await lineItemRepository
|
||||
.findOne({ where: { id } })
|
||||
.then((lineItem) => lineItem && lineItemRepository.remove(lineItem))
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default LineItemService
|
||||
Reference in New Issue
Block a user