feat(types): promotion delete / update / retrieve / add/remove-rules (#5988)
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import {
|
||||
ApplicationMethodAllocation,
|
||||
ApplicationMethodTargetType,
|
||||
ApplicationMethodType,
|
||||
ApplicationMethodAllocationValues,
|
||||
ApplicationMethodTargetTypeValues,
|
||||
ApplicationMethodTypeValues,
|
||||
} from "@medusajs/types"
|
||||
import { PromotionUtils, generateEntityId } from "@medusajs/utils"
|
||||
import {
|
||||
@@ -27,6 +27,7 @@ type OptionalFields =
|
||||
| "created_at"
|
||||
| "updated_at"
|
||||
| "deleted_at"
|
||||
|
||||
@Entity()
|
||||
export default class ApplicationMethod {
|
||||
[OptionalProps]?: OptionalFields
|
||||
@@ -35,25 +36,25 @@ export default class ApplicationMethod {
|
||||
id!: string
|
||||
|
||||
@Property({ columnType: "numeric", nullable: true, serializer: Number })
|
||||
value?: number | null
|
||||
value?: string | null
|
||||
|
||||
@Property({ columnType: "numeric", nullable: true, serializer: Number })
|
||||
max_quantity?: number | null
|
||||
|
||||
@Index({ name: "IDX_application_method_type" })
|
||||
@Enum(() => PromotionUtils.ApplicationMethodType)
|
||||
type: ApplicationMethodType
|
||||
type: ApplicationMethodTypeValues
|
||||
|
||||
@Index({ name: "IDX_application_method_target_type" })
|
||||
@Enum(() => PromotionUtils.ApplicationMethodTargetType)
|
||||
target_type: ApplicationMethodTargetType
|
||||
target_type: ApplicationMethodTargetTypeValues
|
||||
|
||||
@Index({ name: "IDX_application_method_allocation" })
|
||||
@Enum({
|
||||
items: () => PromotionUtils.ApplicationMethodAllocation,
|
||||
nullable: true,
|
||||
})
|
||||
allocation?: ApplicationMethodAllocation
|
||||
allocation?: ApplicationMethodAllocationValues
|
||||
|
||||
@OneToOne({
|
||||
entity: () => Promotion,
|
||||
|
||||
@@ -48,6 +48,7 @@ export default class Promotion {
|
||||
@OneToOne({
|
||||
entity: () => ApplicationMethod,
|
||||
mappedBy: (am) => am.promotion,
|
||||
cascade: ["soft-remove"] as any,
|
||||
})
|
||||
application_method: ApplicationMethod
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
InjectManager,
|
||||
InjectTransactionManager,
|
||||
MedusaContext,
|
||||
MedusaError,
|
||||
} from "@medusajs/utils"
|
||||
import { ApplicationMethod, Promotion } from "@models"
|
||||
import {
|
||||
@@ -19,8 +20,14 @@ import {
|
||||
PromotionService,
|
||||
} from "@services"
|
||||
import { joinerConfig } from "../joiner-config"
|
||||
import { CreateApplicationMethodDTO, CreatePromotionDTO } from "../types"
|
||||
import {
|
||||
CreateApplicationMethodDTO,
|
||||
CreatePromotionDTO,
|
||||
UpdateApplicationMethodDTO,
|
||||
UpdatePromotionDTO,
|
||||
} from "../types"
|
||||
import {
|
||||
allowedAllocationForQuantity,
|
||||
validateApplicationMethodAttributes,
|
||||
validatePromotionRuleAttributes,
|
||||
} from "../utils"
|
||||
@@ -64,6 +71,26 @@ export default class PromotionModuleService<
|
||||
return joinerConfig
|
||||
}
|
||||
|
||||
@InjectManager("baseRepository_")
|
||||
async retrieve(
|
||||
id: string,
|
||||
config: FindConfig<PromotionTypes.PromotionDTO> = {},
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): Promise<PromotionTypes.PromotionDTO> {
|
||||
const promotion = await this.promotionService_.retrieve(
|
||||
id,
|
||||
config,
|
||||
sharedContext
|
||||
)
|
||||
|
||||
return await this.baseRepository_.serialize<PromotionTypes.PromotionDTO>(
|
||||
promotion,
|
||||
{
|
||||
populate: true,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@InjectManager("baseRepository_")
|
||||
async list(
|
||||
filters: PromotionTypes.FilterablePromotionProps = {},
|
||||
@@ -76,7 +103,7 @@ export default class PromotionModuleService<
|
||||
sharedContext
|
||||
)
|
||||
|
||||
return this.baseRepository_.serialize<PromotionTypes.PromotionDTO[]>(
|
||||
return await this.baseRepository_.serialize<PromotionTypes.PromotionDTO[]>(
|
||||
promotions,
|
||||
{
|
||||
populate: true,
|
||||
@@ -94,7 +121,13 @@ export default class PromotionModuleService<
|
||||
return await this.list(
|
||||
{ id: promotions.map((p) => p!.id) },
|
||||
{
|
||||
relations: ["application_method", "rules", "rules.values"],
|
||||
relations: [
|
||||
"application_method",
|
||||
"application_method.target_rules",
|
||||
"application_method.target_rules.values",
|
||||
"rules",
|
||||
"rules.values",
|
||||
],
|
||||
},
|
||||
sharedContext
|
||||
)
|
||||
@@ -195,6 +228,162 @@ export default class PromotionModuleService<
|
||||
return createdPromotions
|
||||
}
|
||||
|
||||
@InjectManager("baseRepository_")
|
||||
async update(
|
||||
data: PromotionTypes.UpdatePromotionDTO[],
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): Promise<PromotionTypes.PromotionDTO[]> {
|
||||
const promotions = await this.update_(data, sharedContext)
|
||||
|
||||
return await this.list(
|
||||
{ id: promotions.map((p) => p!.id) },
|
||||
{
|
||||
relations: [
|
||||
"application_method",
|
||||
"application_method.target_rules",
|
||||
"rules",
|
||||
"rules.values",
|
||||
],
|
||||
},
|
||||
sharedContext
|
||||
)
|
||||
}
|
||||
|
||||
@InjectTransactionManager("baseRepository_")
|
||||
protected async update_(
|
||||
data: PromotionTypes.UpdatePromotionDTO[],
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
) {
|
||||
const promotionIds = data.map((d) => d.id)
|
||||
const existingPromotions = await this.promotionService_.list(
|
||||
{
|
||||
id: promotionIds,
|
||||
},
|
||||
{
|
||||
relations: ["application_method"],
|
||||
}
|
||||
)
|
||||
const existingPromotionsMap = new Map<string, Promotion>(
|
||||
existingPromotions.map((promotion) => [promotion.id, promotion])
|
||||
)
|
||||
|
||||
const promotionsData: UpdatePromotionDTO[] = []
|
||||
const applicationMethodsData: UpdateApplicationMethodDTO[] = []
|
||||
|
||||
for (const {
|
||||
application_method: applicationMethodData,
|
||||
...promotionData
|
||||
} of data) {
|
||||
promotionsData.push(promotionData)
|
||||
|
||||
if (!applicationMethodData) {
|
||||
continue
|
||||
}
|
||||
|
||||
const existingPromotion = existingPromotionsMap.get(promotionData.id)
|
||||
const existingApplicationMethod = existingPromotion?.application_method
|
||||
|
||||
if (!existingApplicationMethod) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (
|
||||
applicationMethodData.allocation &&
|
||||
!allowedAllocationForQuantity.includes(applicationMethodData.allocation)
|
||||
) {
|
||||
applicationMethodData.max_quantity = null
|
||||
}
|
||||
|
||||
validateApplicationMethodAttributes({
|
||||
type: applicationMethodData.type || existingApplicationMethod.type,
|
||||
target_type:
|
||||
applicationMethodData.target_type ||
|
||||
existingApplicationMethod.target_type,
|
||||
allocation:
|
||||
applicationMethodData.allocation ||
|
||||
existingApplicationMethod.allocation,
|
||||
max_quantity:
|
||||
applicationMethodData.max_quantity ||
|
||||
existingApplicationMethod.max_quantity,
|
||||
})
|
||||
|
||||
applicationMethodsData.push(applicationMethodData)
|
||||
}
|
||||
|
||||
const updatedPromotions = this.promotionService_.update(
|
||||
promotionsData,
|
||||
sharedContext
|
||||
)
|
||||
|
||||
if (applicationMethodsData.length) {
|
||||
await this.applicationMethodService_.update(
|
||||
applicationMethodsData,
|
||||
sharedContext
|
||||
)
|
||||
}
|
||||
|
||||
return updatedPromotions
|
||||
}
|
||||
|
||||
@InjectManager("baseRepository_")
|
||||
@InjectTransactionManager("baseRepository_")
|
||||
async addPromotionRules(
|
||||
promotionId: string,
|
||||
rulesData: PromotionTypes.CreatePromotionRuleDTO[],
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): Promise<PromotionTypes.PromotionDTO> {
|
||||
const promotion = await this.promotionService_.retrieve(promotionId)
|
||||
|
||||
await this.createPromotionRulesAndValues(
|
||||
rulesData,
|
||||
"promotions",
|
||||
promotion,
|
||||
sharedContext
|
||||
)
|
||||
|
||||
return this.retrieve(promotionId, {
|
||||
relations: ["rules", "rules.values"],
|
||||
})
|
||||
}
|
||||
|
||||
@InjectManager("baseRepository_")
|
||||
@InjectTransactionManager("baseRepository_")
|
||||
async addPromotionTargetRules(
|
||||
promotionId: string,
|
||||
rulesData: PromotionTypes.CreatePromotionRuleDTO[],
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): Promise<PromotionTypes.PromotionDTO> {
|
||||
const promotion = await this.promotionService_.retrieve(promotionId, {
|
||||
relations: ["application_method"],
|
||||
})
|
||||
|
||||
const applicationMethod = promotion.application_method
|
||||
|
||||
if (!applicationMethod) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`application_method for promotion not found`
|
||||
)
|
||||
}
|
||||
|
||||
await this.createPromotionRulesAndValues(
|
||||
rulesData,
|
||||
"application_methods",
|
||||
applicationMethod,
|
||||
sharedContext
|
||||
)
|
||||
|
||||
return this.retrieve(promotionId, {
|
||||
relations: [
|
||||
"rules",
|
||||
"rules.values",
|
||||
"application_method",
|
||||
"application_method.target_rules",
|
||||
"application_method.target_rules.values",
|
||||
],
|
||||
})
|
||||
}
|
||||
|
||||
protected async createPromotionRulesAndValues(
|
||||
rulesData: PromotionTypes.CreatePromotionRuleDTO[],
|
||||
relationName: "promotions" | "application_methods",
|
||||
@@ -224,4 +413,111 @@ export default class PromotionModuleService<
|
||||
await this.promotionRuleValueService_.create(promotionRuleValuesData)
|
||||
}
|
||||
}
|
||||
|
||||
@InjectTransactionManager("baseRepository_")
|
||||
async delete(
|
||||
ids: string[],
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): Promise<void> {
|
||||
await this.promotionService_.delete(ids, sharedContext)
|
||||
}
|
||||
|
||||
@InjectManager("baseRepository_")
|
||||
async removePromotionRules(
|
||||
promotionId: string,
|
||||
rulesData: PromotionTypes.RemovePromotionRuleDTO[],
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): Promise<PromotionTypes.PromotionDTO> {
|
||||
await this.removePromotionRules_(promotionId, rulesData, sharedContext)
|
||||
|
||||
return this.retrieve(
|
||||
promotionId,
|
||||
{ relations: ["rules", "rules.values"] },
|
||||
sharedContext
|
||||
)
|
||||
}
|
||||
|
||||
@InjectTransactionManager("baseRepository_")
|
||||
protected async removePromotionRules_(
|
||||
promotionId: string,
|
||||
rulesData: PromotionTypes.RemovePromotionRuleDTO[],
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): Promise<void> {
|
||||
const promotionRuleIdsToRemove = rulesData.map((ruleData) => ruleData.id)
|
||||
const promotion = await this.promotionService_.retrieve(
|
||||
promotionId,
|
||||
{ relations: ["rules"] },
|
||||
sharedContext
|
||||
)
|
||||
|
||||
const existingPromotionRuleIds = promotion.rules
|
||||
.toArray()
|
||||
.map((rule) => rule.id)
|
||||
|
||||
const idsToRemove = promotionRuleIdsToRemove.filter((ruleId) =>
|
||||
existingPromotionRuleIds.includes(ruleId)
|
||||
)
|
||||
|
||||
await this.promotionRuleService_.delete(idsToRemove, sharedContext)
|
||||
}
|
||||
|
||||
@InjectManager("baseRepository_")
|
||||
async removePromotionTargetRules(
|
||||
promotionId: string,
|
||||
rulesData: PromotionTypes.RemovePromotionRuleDTO[],
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): Promise<PromotionTypes.PromotionDTO> {
|
||||
await this.removePromotionTargetRules_(
|
||||
promotionId,
|
||||
rulesData,
|
||||
sharedContext
|
||||
)
|
||||
|
||||
return this.retrieve(
|
||||
promotionId,
|
||||
{
|
||||
relations: [
|
||||
"rules",
|
||||
"rules.values",
|
||||
"application_method",
|
||||
"application_method.target_rules",
|
||||
"application_method.target_rules.values",
|
||||
],
|
||||
},
|
||||
sharedContext
|
||||
)
|
||||
}
|
||||
|
||||
@InjectTransactionManager("baseRepository_")
|
||||
protected async removePromotionTargetRules_(
|
||||
promotionId: string,
|
||||
rulesData: PromotionTypes.RemovePromotionRuleDTO[],
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): Promise<void> {
|
||||
const promotionRuleIds = rulesData.map((ruleData) => ruleData.id)
|
||||
const promotion = await this.promotionService_.retrieve(
|
||||
promotionId,
|
||||
{ relations: ["application_method.target_rules"] },
|
||||
sharedContext
|
||||
)
|
||||
|
||||
const applicationMethod = promotion.application_method
|
||||
|
||||
if (!applicationMethod) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`application_method for promotion not found`
|
||||
)
|
||||
}
|
||||
|
||||
const targetRuleIdsToRemove = applicationMethod.target_rules
|
||||
.toArray()
|
||||
.filter((rule) => promotionRuleIds.includes(rule.id))
|
||||
.map((rule) => rule.id)
|
||||
|
||||
await this.promotionRuleService_.delete(
|
||||
targetRuleIdsToRemove,
|
||||
sharedContext
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,27 @@
|
||||
import {
|
||||
ApplicationMethodAllocation,
|
||||
ApplicationMethodTargetType,
|
||||
ApplicationMethodType,
|
||||
ApplicationMethodAllocationValues,
|
||||
ApplicationMethodTargetTypeValues,
|
||||
ApplicationMethodTypeValues,
|
||||
PromotionDTO,
|
||||
} from "@medusajs/types"
|
||||
|
||||
import { Promotion } from "@models"
|
||||
|
||||
export interface CreateApplicationMethodDTO {
|
||||
type: ApplicationMethodType
|
||||
target_type: ApplicationMethodTargetType
|
||||
allocation?: ApplicationMethodAllocation
|
||||
value?: number
|
||||
promotion: PromotionDTO | string
|
||||
max_quantity?: number
|
||||
type: ApplicationMethodTypeValues
|
||||
target_type: ApplicationMethodTargetTypeValues
|
||||
allocation?: ApplicationMethodAllocationValues
|
||||
value?: string | null
|
||||
promotion: Promotion | string | PromotionDTO
|
||||
max_quantity?: number | null
|
||||
}
|
||||
|
||||
export interface UpdateApplicationMethodDTO {
|
||||
id: string
|
||||
type?: ApplicationMethodTypeValues
|
||||
target_type?: ApplicationMethodTargetTypeValues
|
||||
allocation?: ApplicationMethodAllocationValues
|
||||
value?: string | null
|
||||
promotion?: Promotion | string | PromotionDTO
|
||||
max_quantity?: number | null
|
||||
}
|
||||
|
||||
@@ -8,4 +8,8 @@ export interface CreatePromotionDTO {
|
||||
|
||||
export interface UpdatePromotionDTO {
|
||||
id: string
|
||||
code?: string
|
||||
// TODO: add this when buyget is available
|
||||
// type: PromotionType
|
||||
is_automatic?: boolean
|
||||
}
|
||||
|
||||
@@ -1,44 +1,86 @@
|
||||
import {
|
||||
ApplicationMethodAllocationValues,
|
||||
ApplicationMethodTargetTypeValues,
|
||||
ApplicationMethodTypeValues,
|
||||
} from "@medusajs/types"
|
||||
import {
|
||||
ApplicationMethodAllocation,
|
||||
ApplicationMethodTargetType,
|
||||
ApplicationMethodType,
|
||||
MedusaError,
|
||||
isDefined,
|
||||
} from "@medusajs/utils"
|
||||
import { CreateApplicationMethodDTO } from "../../types"
|
||||
|
||||
const allowedTargetTypes: string[] = [
|
||||
export const allowedAllocationTargetTypes: string[] = [
|
||||
ApplicationMethodTargetType.SHIPPING,
|
||||
ApplicationMethodTargetType.ITEM,
|
||||
]
|
||||
|
||||
const allowedAllocationTypes: string[] = [
|
||||
export const allowedAllocationTypes: string[] = [
|
||||
ApplicationMethodAllocation.ACROSS,
|
||||
ApplicationMethodAllocation.EACH,
|
||||
]
|
||||
|
||||
const allowedAllocationForQuantity: string[] = [
|
||||
export const allowedAllocationForQuantity: string[] = [
|
||||
ApplicationMethodAllocation.EACH,
|
||||
]
|
||||
|
||||
export function validateApplicationMethodAttributes(
|
||||
data: CreateApplicationMethodDTO
|
||||
) {
|
||||
export function validateApplicationMethodAttributes(data: {
|
||||
type: ApplicationMethodTypeValues
|
||||
target_type: ApplicationMethodTargetTypeValues
|
||||
allocation?: ApplicationMethodAllocationValues
|
||||
max_quantity?: number | null
|
||||
}) {
|
||||
const allTargetTypes: string[] = Object.values(ApplicationMethodTargetType)
|
||||
|
||||
if (!allTargetTypes.includes(data.target_type)) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`application_method.target_type should be one of ${allTargetTypes.join(
|
||||
", "
|
||||
)}`
|
||||
)
|
||||
}
|
||||
|
||||
const allTypes: string[] = Object.values(ApplicationMethodType)
|
||||
|
||||
if (!allTypes.includes(data.type)) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`application_method.type should be one of ${allTypes.join(", ")}`
|
||||
)
|
||||
}
|
||||
|
||||
if (
|
||||
allowedTargetTypes.includes(data.target_type) &&
|
||||
allowedAllocationTargetTypes.includes(data.target_type) &&
|
||||
!allowedAllocationTypes.includes(data.allocation || "")
|
||||
) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`application_method.allocation should be either '${allowedAllocationTypes.join(
|
||||
" OR "
|
||||
)}' when application_method.target_type is either '${allowedTargetTypes.join(
|
||||
)}' when application_method.target_type is either '${allowedAllocationTargetTypes.join(
|
||||
" OR "
|
||||
)}'`
|
||||
)
|
||||
}
|
||||
|
||||
const allAllocationTypes: string[] = Object.values(
|
||||
ApplicationMethodAllocation
|
||||
)
|
||||
|
||||
if (data.allocation && !allAllocationTypes.includes(data.allocation)) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`application_method.allocation should be one of ${allAllocationTypes.join(
|
||||
", "
|
||||
)}`
|
||||
)
|
||||
}
|
||||
|
||||
if (
|
||||
allowedAllocationForQuantity.includes(data.allocation || "") &&
|
||||
data.allocation &&
|
||||
allowedAllocationForQuantity.includes(data.allocation) &&
|
||||
!isDefined(data.max_quantity)
|
||||
) {
|
||||
throw new MedusaError(
|
||||
|
||||
Reference in New Issue
Block a user