feat(medusa): Add endpoints specific to DiscountConditions (#1355)
This commit is contained in:
1 parent
a69b52e031
commit
9ca45ea492
20 files changed
+1433
-159
No files matched your search
@@ -0,0 +1,107 @@
|
||||
import { IsOptional, IsString } from "class-validator"
|
||||
import { defaultAdminDiscountsFields, defaultAdminDiscountsRelations } from "."
|
||||
import { Discount } from "../../../../models/discount"
|
||||
import { DiscountConditionOperator } from "../../../../models/discount-condition"
|
||||
import { DiscountService } from "../../../../services"
|
||||
import DiscountConditionService from "../../../../services/discount-condition"
|
||||
import { AdminUpsertConditionsReq } from "../../../../types/discount"
|
||||
import { getRetrieveConfig } from "../../../../utils/get-query-config"
|
||||
import { validator } from "../../../../utils/validator"
|
||||
/**
|
||||
* @oas [post] /discounts/{discount_id}/conditions
|
||||
* operationId: "PostDiscountsDiscountConditions"
|
||||
* summary: "Creates a DiscountCondition"
|
||||
* x-authenticated: true
|
||||
* parameters:
|
||||
* - (path) discount_id=* {string} The id of the Product.
|
||||
* - (query) expand {string} (Comma separated) Which fields should be expanded in each product of the result.
|
||||
* - (query) fields {string} (Comma separated) Which fields should be included in each product of the result.
|
||||
* description: "Creates a DiscountCondition"
|
||||
* requestBody:
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* operator:
|
||||
* description: Operator of the condition
|
||||
* type: string
|
||||
* items:
|
||||
* properties:
|
||||
* products:
|
||||
* type: array
|
||||
* description: list of products
|
||||
* product_types:
|
||||
* type: array
|
||||
* description: list of product types
|
||||
* product_collections:
|
||||
* type: array
|
||||
* description: list of product collections
|
||||
* product_tags:
|
||||
* type: array
|
||||
* description: list of product tags
|
||||
* customer_groups:
|
||||
* type: array
|
||||
* description: list of customer_groups
|
||||
* tags:
|
||||
* - Discount
|
||||
* responses:
|
||||
* 200:
|
||||
* description: OK
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* discount:
|
||||
* $ref: "#/components/schemas/discount"
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
const { discount_id } = req.params
|
||||
|
||||
const validatedCondition = await validator(
|
||||
AdminPostDiscountsDiscountConditions,
|
||||
req.body
|
||||
)
|
||||
|
||||
const validatedParams = await validator(
|
||||
AdminPostDiscountsDiscountConditionsParams,
|
||||
req.query
|
||||
)
|
||||
|
||||
const conditionService: DiscountConditionService = req.scope.resolve(
|
||||
"discountConditionService"
|
||||
)
|
||||
const discountService: DiscountService = req.scope.resolve("discountService")
|
||||
|
||||
let discount = await discountService.retrieve(discount_id)
|
||||
|
||||
await conditionService.upsertCondition({
|
||||
...validatedCondition,
|
||||
rule_id: discount.rule_id,
|
||||
})
|
||||
|
||||
const config = getRetrieveConfig<Discount>(
|
||||
defaultAdminDiscountsFields,
|
||||
defaultAdminDiscountsRelations,
|
||||
validatedParams?.fields?.split(",") as (keyof Discount)[],
|
||||
validatedParams?.expand?.split(",")
|
||||
)
|
||||
|
||||
discount = await discountService.retrieve(discount.id, config)
|
||||
|
||||
res.status(200).json({ discount })
|
||||
}
|
||||
|
||||
export class AdminPostDiscountsDiscountConditions extends AdminUpsertConditionsReq {
|
||||
@IsString()
|
||||
operator: DiscountConditionOperator
|
||||
}
|
||||
|
||||
export class AdminPostDiscountsDiscountConditionsParams {
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
expand?: string
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
fields?: string
|
||||
}
|
||||
@@ -83,8 +83,6 @@ import { AdminPostDiscountsDiscountParams } from "./update-discount"
|
||||
export default async (req, res) => {
|
||||
const validated = await validator(AdminPostDiscountsReq, req.body)
|
||||
|
||||
console.log(validated.rule.conditions)
|
||||
|
||||
const validatedParams = await validator(
|
||||
AdminPostDiscountsDiscountParams,
|
||||
req.query
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
import { IsOptional, IsString } from "class-validator"
|
||||
import { MedusaError } from "medusa-core-utils"
|
||||
import { defaultAdminDiscountsFields, defaultAdminDiscountsRelations } from "."
|
||||
import { Discount } from "../../../../models"
|
||||
import { DiscountService } from "../../../../services"
|
||||
import DiscountConditionService from "../../../../services/discount-condition"
|
||||
import { getRetrieveConfig } from "../../../../utils/get-query-config"
|
||||
import { validator } from "../../../../utils/validator"
|
||||
|
||||
/**
|
||||
* @oas [delete] /discounts/{discount_id}/conditions/{condition_id}
|
||||
* operationId: "DeleteDiscountsDiscountConditionsCondition"
|
||||
* summary: "Delete a DiscountCondition"
|
||||
* description: "Deletes a DiscountCondition"
|
||||
* x-authenticated: true
|
||||
* parameters:
|
||||
* - (path) id=* {string} The id of the Discount
|
||||
* - (path) condition_id=* {string} The id of the DiscountCondition
|
||||
* tags:
|
||||
* - Discount
|
||||
* responses:
|
||||
* 200:
|
||||
* description: OK
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* id:
|
||||
* type: string
|
||||
* description: The id of the deleted DiscountCondition
|
||||
* object:
|
||||
* type: string
|
||||
* description: The type of the object that was deleted.
|
||||
* deleted:
|
||||
* type: boolean
|
||||
* discount:
|
||||
* type: object
|
||||
* description: The Discount to which the condition used to belong
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
const { discount_id, condition_id } = req.params
|
||||
|
||||
const validatedParams = await validator(
|
||||
AdminDeleteDiscountsDiscountConditionsConditionParams,
|
||||
req.query
|
||||
)
|
||||
|
||||
const conditionService: DiscountConditionService = req.scope.resolve(
|
||||
"discountConditionService"
|
||||
)
|
||||
|
||||
const condition = await conditionService
|
||||
.retrieve(condition_id)
|
||||
.catch(() => void 0)
|
||||
|
||||
if (!condition) {
|
||||
// resolves idempotently in case of non-existing condition
|
||||
return res.json({
|
||||
id: condition_id,
|
||||
object: "discount-condition",
|
||||
deleted: true,
|
||||
})
|
||||
}
|
||||
|
||||
const discountService: DiscountService = req.scope.resolve("discountService")
|
||||
|
||||
let discount = await discountService.retrieve(discount_id, {
|
||||
relations: ["rule", "rule.conditions"],
|
||||
})
|
||||
|
||||
const existsOnDiscount = discount.rule.conditions.some(
|
||||
(c) => c.id === condition_id
|
||||
)
|
||||
|
||||
if (!existsOnDiscount) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_FOUND,
|
||||
`Condition with id ${condition_id} does not belong to Discount with id ${discount_id}`
|
||||
)
|
||||
}
|
||||
|
||||
await conditionService.delete(condition_id)
|
||||
|
||||
const config = getRetrieveConfig<Discount>(
|
||||
defaultAdminDiscountsFields,
|
||||
defaultAdminDiscountsRelations,
|
||||
validatedParams?.fields?.split(",") as (keyof Discount)[],
|
||||
validatedParams?.expand?.split(",")
|
||||
)
|
||||
|
||||
discount = await discountService.retrieve(discount_id, config)
|
||||
|
||||
res.json({
|
||||
id: condition_id,
|
||||
object: "discount-condition",
|
||||
deleted: true,
|
||||
discount,
|
||||
})
|
||||
}
|
||||
|
||||
export class AdminDeleteDiscountsDiscountConditionsConditionParams {
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
expand?: string
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
fields?: string
|
||||
}
|
||||
@@ -49,6 +49,20 @@ export default (app) => {
|
||||
middlewares.wrap(require("./remove-region").default)
|
||||
)
|
||||
|
||||
// Discount condition management
|
||||
route.post(
|
||||
"/:discount_id/conditions/:condition_id",
|
||||
middlewares.wrap(require("./update-condition").default)
|
||||
)
|
||||
route.post(
|
||||
"/:discount_id/conditions",
|
||||
middlewares.wrap(require("./create-condition").default)
|
||||
)
|
||||
route.delete(
|
||||
"/:discount_id/conditions/:condition_id",
|
||||
middlewares.wrap(require("./delete-condition").default)
|
||||
)
|
||||
|
||||
return app
|
||||
}
|
||||
|
||||
@@ -88,12 +102,15 @@ export type AdminDiscountsListRes = PaginatedResponse & {
|
||||
}
|
||||
|
||||
export * from "./add-region"
|
||||
export * from "./create-condition"
|
||||
export * from "./create-discount"
|
||||
export * from "./create-dynamic-code"
|
||||
export * from "./delete-condition"
|
||||
export * from "./delete-discount"
|
||||
export * from "./delete-dynamic-code"
|
||||
export * from "./get-discount"
|
||||
export * from "./get-discount-by-code"
|
||||
export * from "./list-discounts"
|
||||
export * from "./remove-region"
|
||||
export * from "./update-condition"
|
||||
export * from "./update-discount"
|
||||
@@ -0,0 +1,109 @@
|
||||
import { IsOptional, IsString } from "class-validator"
|
||||
import { defaultAdminDiscountsFields, defaultAdminDiscountsRelations } from "."
|
||||
import { Discount } from "../../../../models/discount"
|
||||
import { DiscountService } from "../../../../services"
|
||||
import DiscountConditionService from "../../../../services/discount-condition"
|
||||
import { AdminUpsertConditionsReq } from "../../../../types/discount"
|
||||
import { getRetrieveConfig } from "../../../../utils/get-query-config"
|
||||
import { validator } from "../../../../utils/validator"
|
||||
/**
|
||||
* @oas [post] /discounts/{discount_id}/conditions/{condition_id}
|
||||
* operationId: "PostDiscountsDiscountConditionsCondition"
|
||||
* summary: "Updates a DiscountCondition"
|
||||
* x-authenticated: true
|
||||
* parameters:
|
||||
* - (path) discount_id=* {string} The id of the Product.
|
||||
* - (query) expand {string} (Comma separated) Which fields should be expanded in each product of the result.
|
||||
* - (query) fields {string} (Comma separated) Which fields should be included in each product of the result.
|
||||
* description: "Updates a DiscountCondition"
|
||||
* requestBody:
|
||||
* content:
|
||||
* application/json:
|
||||
* required:
|
||||
* - id
|
||||
* schema:
|
||||
* properties:
|
||||
* items:
|
||||
* properties:
|
||||
* products:
|
||||
* type: array
|
||||
* description: list of products
|
||||
* product_types:
|
||||
* type: array
|
||||
* description: list of product types
|
||||
* product_collections:
|
||||
* type: array
|
||||
* description: list of product collections
|
||||
* product_tags:
|
||||
* type: array
|
||||
* description: list of product tags
|
||||
* customer_groups:
|
||||
* type: array
|
||||
* description: list of customer_groups
|
||||
* tags:
|
||||
* - Discount
|
||||
* responses:
|
||||
* 200:
|
||||
* description: OK
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* discount:
|
||||
* $ref: "#/components/schemas/discount"
|
||||
*/
|
||||
|
||||
export default async (req, res) => {
|
||||
const { discount_id, condition_id } = req.params
|
||||
|
||||
const validatedCondition = await validator(
|
||||
AdminPostDiscountsDiscountConditionsCondition,
|
||||
req.body
|
||||
)
|
||||
|
||||
const validatedParams = await validator(
|
||||
AdminPostDiscountsDiscountConditionsConditionParams,
|
||||
req.query
|
||||
)
|
||||
|
||||
const conditionService: DiscountConditionService = req.scope.resolve(
|
||||
"discountConditionService"
|
||||
)
|
||||
|
||||
const condition = await conditionService.retrieve(condition_id)
|
||||
|
||||
const discountService: DiscountService = req.scope.resolve("discountService")
|
||||
|
||||
let discount = await discountService.retrieve(discount_id)
|
||||
|
||||
const updateObj = {
|
||||
...validatedCondition,
|
||||
rule_id: discount.rule_id,
|
||||
id: condition.id,
|
||||
}
|
||||
|
||||
await conditionService.upsertCondition(updateObj)
|
||||
|
||||
const config = getRetrieveConfig<Discount>(
|
||||
defaultAdminDiscountsFields,
|
||||
defaultAdminDiscountsRelations,
|
||||
validatedParams?.fields?.split(",") as (keyof Discount)[],
|
||||
validatedParams?.expand?.split(",")
|
||||
)
|
||||
|
||||
discount = await discountService.retrieve(discount.id, config)
|
||||
|
||||
res.status(200).json({ discount })
|
||||
}
|
||||
|
||||
export class AdminPostDiscountsDiscountConditionsCondition extends AdminUpsertConditionsReq {}
|
||||
|
||||
export class AdminPostDiscountsDiscountConditionsConditionParams {
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
expand?: string
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
fields?: string
|
||||
}
|
||||
@@ -145,6 +145,10 @@ export class DiscountCondition {
|
||||
|
||||
@BeforeInsert()
|
||||
private beforeInsert() {
|
||||
if (this.id) {
|
||||
return
|
||||
}
|
||||
|
||||
const id = ulid()
|
||||
this.id = `discon_${id}`
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
Not,
|
||||
Repository,
|
||||
} from "typeorm"
|
||||
import { Discount } from "../models"
|
||||
import {
|
||||
DiscountCondition,
|
||||
DiscountConditionOperator,
|
||||
@@ -35,6 +36,21 @@ type DiscountConditionResourceType = EntityTarget<
|
||||
|
||||
@EntityRepository(DiscountCondition)
|
||||
export class DiscountConditionRepository extends Repository<DiscountCondition> {
|
||||
async findOneWithDiscount(
|
||||
conditionId: string,
|
||||
discountId: string
|
||||
): Promise<(DiscountCondition & { discount: Discount }) | undefined> {
|
||||
return (await this.createQueryBuilder("condition")
|
||||
.leftJoinAndMapOne(
|
||||
"condition.discount",
|
||||
Discount,
|
||||
"discount",
|
||||
`condition.discount_rule_id = discount.rule_id and discount.id = :discId and condition.id = :dcId`,
|
||||
{ discId: discountId, dcId: conditionId }
|
||||
)
|
||||
.getOne()) as (DiscountCondition & { discount: Discount }) | undefined
|
||||
}
|
||||
|
||||
getJoinTableResourceIdentifiers(type: string): {
|
||||
joinTable: string
|
||||
resourceKey: string
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { flatten, groupBy, map, merge } from "lodash"
|
||||
import { EntityRepository, Repository, FindManyOptions } from "typeorm"
|
||||
import { EntityRepository, FindManyOptions, Repository } from "typeorm"
|
||||
import { Order } from "../models/order"
|
||||
|
||||
@EntityRepository(Order)
|
||||
|
||||
@@ -0,0 +1,179 @@
|
||||
import { MedusaError } from "medusa-core-utils"
|
||||
import { BaseService } from "medusa-interfaces"
|
||||
import { EntityManager } from "typeorm"
|
||||
import { EventBusService } from "."
|
||||
import { DiscountCondition, DiscountConditionType } from "../models"
|
||||
import { DiscountConditionRepository } from "../repositories/discount-condition"
|
||||
import { FindConfig } from "../types/common"
|
||||
import { UpsertDiscountConditionInput } from "../types/discount"
|
||||
import { PostgresError } from "../utils/exception-formatter"
|
||||
|
||||
/**
|
||||
* Provides layer to manipulate discount conditions.
|
||||
* @implements {BaseService}
|
||||
*/
|
||||
class DiscountConditionService extends BaseService {
|
||||
protected readonly manager_: EntityManager
|
||||
protected readonly discountConditionRepository_: typeof DiscountConditionRepository
|
||||
protected readonly eventBus_: EventBusService
|
||||
protected transactionManager_?: EntityManager
|
||||
|
||||
constructor({ manager, discountConditionRepository, eventBusService }) {
|
||||
super()
|
||||
|
||||
this.manager_ = manager
|
||||
this.discountConditionRepository_ = discountConditionRepository
|
||||
this.eventBus_ = eventBusService
|
||||
}
|
||||
|
||||
withTransaction(transactionManager: EntityManager): DiscountConditionService {
|
||||
if (!transactionManager) {
|
||||
return this
|
||||
}
|
||||
|
||||
const cloned = new DiscountConditionService({
|
||||
manager: transactionManager,
|
||||
discountConditionRepository: this.discountConditionRepository_,
|
||||
eventBusService: this.eventBus_,
|
||||
})
|
||||
|
||||
cloned.transactionManager_ = transactionManager
|
||||
|
||||
return cloned
|
||||
}
|
||||
|
||||
async retrieve(
|
||||
conditionId: string,
|
||||
config?: FindConfig<DiscountCondition>
|
||||
): Promise<DiscountCondition | never> {
|
||||
return await this.atomicPhase_(async (manager: EntityManager) => {
|
||||
const conditionRepo = manager.getCustomRepository(
|
||||
this.discountConditionRepository_
|
||||
)
|
||||
|
||||
const query = this.buildQuery_({ id: conditionId }, config)
|
||||
|
||||
const condition = await conditionRepo.findOne(query)
|
||||
|
||||
if (!condition) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_FOUND,
|
||||
`DiscountCondition with id ${conditionId} was not found`
|
||||
)
|
||||
}
|
||||
|
||||
return condition
|
||||
})
|
||||
}
|
||||
|
||||
protected static resolveConditionType_(data: UpsertDiscountConditionInput):
|
||||
| {
|
||||
type: DiscountConditionType
|
||||
resource_ids: string[]
|
||||
}
|
||||
| undefined {
|
||||
switch (true) {
|
||||
case !!data.products?.length:
|
||||
return {
|
||||
type: DiscountConditionType.PRODUCTS,
|
||||
resource_ids: data.products!,
|
||||
}
|
||||
case !!data.product_collections?.length:
|
||||
return {
|
||||
type: DiscountConditionType.PRODUCT_COLLECTIONS,
|
||||
resource_ids: data.product_collections!,
|
||||
}
|
||||
case !!data.product_types?.length:
|
||||
return {
|
||||
type: DiscountConditionType.PRODUCT_TYPES,
|
||||
resource_ids: data.product_types!,
|
||||
}
|
||||
case !!data.product_tags?.length:
|
||||
return {
|
||||
type: DiscountConditionType.PRODUCT_TAGS,
|
||||
resource_ids: data.product_tags!,
|
||||
}
|
||||
case !!data.customer_groups?.length:
|
||||
return {
|
||||
type: DiscountConditionType.CUSTOMER_GROUPS,
|
||||
resource_ids: data.customer_groups!,
|
||||
}
|
||||
default:
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
async upsertCondition(data: UpsertDiscountConditionInput): Promise<void> {
|
||||
let resolvedConditionType
|
||||
|
||||
return await this.atomicPhase_(
|
||||
async (manager: EntityManager) => {
|
||||
resolvedConditionType =
|
||||
DiscountConditionService.resolveConditionType_(data)
|
||||
|
||||
if (!resolvedConditionType) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Missing one of products, collections, tags, types or customer groups in data`
|
||||
)
|
||||
}
|
||||
|
||||
const discountConditionRepo: DiscountConditionRepository =
|
||||
manager.getCustomRepository(this.discountConditionRepository_)
|
||||
|
||||
if (data.id) {
|
||||
return await discountConditionRepo.addConditionResources(
|
||||
data.id,
|
||||
resolvedConditionType.resource_ids,
|
||||
resolvedConditionType.type,
|
||||
true
|
||||
)
|
||||
}
|
||||
|
||||
const created = discountConditionRepo.create({
|
||||
discount_rule_id: data.rule_id,
|
||||
operator: data.operator,
|
||||
type: resolvedConditionType.type,
|
||||
})
|
||||
|
||||
const discountCondition = await discountConditionRepo.save(created)
|
||||
|
||||
return await discountConditionRepo.addConditionResources(
|
||||
discountCondition.id,
|
||||
resolvedConditionType.resource_ids,
|
||||
resolvedConditionType.type
|
||||
)
|
||||
},
|
||||
async (err: { code: string }) => {
|
||||
if (err.code === PostgresError.DUPLICATE_ERROR) {
|
||||
// A unique key constraint failed meaning the combination of
|
||||
// discount rule id, type, and operator already exists in the db.
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.DUPLICATE_ERROR,
|
||||
`Discount Condition with operator '${data.operator}' and type '${resolvedConditionType?.type}' already exist on a Discount Rule`
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
async delete(discountConditionId: string): Promise<DiscountCondition> {
|
||||
return await this.atomicPhase_(async (manager: EntityManager) => {
|
||||
const conditionRepo = manager.getCustomRepository(
|
||||
this.discountConditionRepository_
|
||||
)
|
||||
|
||||
const condition = await conditionRepo.findOne({
|
||||
where: { id: discountConditionId },
|
||||
})
|
||||
|
||||
if (!condition) {
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
return await conditionRepo.remove(condition)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export default DiscountConditionService
|
||||
@@ -11,7 +11,6 @@ import {
|
||||
} from "."
|
||||
import { Cart } from "../models/cart"
|
||||
import { Discount } from "../models/discount"
|
||||
import { DiscountConditionType } from "../models/discount-condition"
|
||||
import {
|
||||
AllocationType as DiscountAllocation,
|
||||
DiscountRule,
|
||||
@@ -28,10 +27,10 @@ import {
|
||||
CreateDynamicDiscountInput,
|
||||
FilterableDiscountProps,
|
||||
UpdateDiscountInput,
|
||||
UpsertDiscountConditionInput,
|
||||
} from "../types/discount"
|
||||
import { isFuture, isPast } from "../utils/date-helpers"
|
||||
import { formatException, PostgresError } from "../utils/exception-formatter"
|
||||
import { formatException } from "../utils/exception-formatter"
|
||||
import DiscountConditionService from "./discount-condition"
|
||||
|
||||
/**
|
||||
* Provides layer to manipulate discounts.
|
||||
@@ -43,6 +42,7 @@ class DiscountService extends BaseService {
|
||||
private discountRuleRepository_: typeof DiscountRuleRepository
|
||||
private giftCardRepository_: typeof GiftCardRepository
|
||||
private discountConditionRepository_: typeof DiscountConditionRepository
|
||||
private discountConditionService_: DiscountConditionService
|
||||
private totalsService_: TotalsService
|
||||
private productService_: ProductService
|
||||
private regionService_: RegionService
|
||||
@@ -54,6 +54,7 @@ class DiscountService extends BaseService {
|
||||
discountRuleRepository,
|
||||
giftCardRepository,
|
||||
discountConditionRepository,
|
||||
discountConditionService,
|
||||
totalsService,
|
||||
productService,
|
||||
regionService,
|
||||
@@ -77,6 +78,9 @@ class DiscountService extends BaseService {
|
||||
/** @private @const {DiscountConditionRepository} */
|
||||
this.discountConditionRepository_ = discountConditionRepository
|
||||
|
||||
/** @private @const {DiscountConditionRepository} */
|
||||
this.discountConditionService_ = discountConditionService
|
||||
|
||||
/** @private @const {TotalsService} */
|
||||
this.totalsService_ = totalsService
|
||||
|
||||
@@ -104,6 +108,7 @@ class DiscountService extends BaseService {
|
||||
discountRuleRepository: this.discountRuleRepository_,
|
||||
giftCardRepository: this.giftCardRepository_,
|
||||
discountConditionRepository: this.discountConditionRepository_,
|
||||
discountConditionService: this.discountConditionService_,
|
||||
totalsService: this.totalsService_,
|
||||
productService: this.productService_,
|
||||
regionService: this.regionService_,
|
||||
@@ -263,9 +268,13 @@ class DiscountService extends BaseService {
|
||||
const result = await discountRepo.save(created)
|
||||
|
||||
if (conditions?.length) {
|
||||
for (const cond of conditions) {
|
||||
await this.upsertDiscountCondition_(result.id, cond)
|
||||
}
|
||||
await Promise.all(
|
||||
conditions.map(async (cond) => {
|
||||
await this.discountConditionService_
|
||||
.withTransaction(manager)
|
||||
.upsertCondition({ rule_id: result.rule_id, ...cond })
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
return result
|
||||
@@ -382,9 +391,13 @@ class DiscountService extends BaseService {
|
||||
}
|
||||
|
||||
if (conditions?.length) {
|
||||
for (const cond of conditions) {
|
||||
await this.upsertDiscountCondition_(discount.id, cond)
|
||||
}
|
||||
await Promise.all(
|
||||
conditions.map(async (cond) => {
|
||||
await this.discountConditionService_
|
||||
.withTransaction(manager)
|
||||
.upsertCondition({ rule_id: discount.rule_id, ...cond })
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
if (regions) {
|
||||
@@ -573,103 +586,6 @@ class DiscountService extends BaseService {
|
||||
})
|
||||
}
|
||||
|
||||
resolveConditionType_(data: UpsertDiscountConditionInput):
|
||||
| {
|
||||
type: DiscountConditionType
|
||||
resource_ids: string[]
|
||||
}
|
||||
| undefined {
|
||||
switch (true) {
|
||||
case !!data.products?.length:
|
||||
return {
|
||||
type: DiscountConditionType.PRODUCTS,
|
||||
resource_ids: data.products!,
|
||||
}
|
||||
case !!data.product_collections?.length:
|
||||
return {
|
||||
type: DiscountConditionType.PRODUCT_COLLECTIONS,
|
||||
resource_ids: data.product_collections!,
|
||||
}
|
||||
case !!data.product_types?.length:
|
||||
return {
|
||||
type: DiscountConditionType.PRODUCT_TYPES,
|
||||
resource_ids: data.product_types!,
|
||||
}
|
||||
case !!data.product_tags?.length:
|
||||
return {
|
||||
type: DiscountConditionType.PRODUCT_TAGS,
|
||||
resource_ids: data.product_tags!,
|
||||
}
|
||||
case !!data.customer_groups?.length:
|
||||
return {
|
||||
type: DiscountConditionType.CUSTOMER_GROUPS,
|
||||
resource_ids: data.customer_groups!,
|
||||
}
|
||||
default:
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
async upsertDiscountCondition_(
|
||||
discountId: string,
|
||||
data: UpsertDiscountConditionInput
|
||||
): Promise<void> {
|
||||
const resolvedConditionType = this.resolveConditionType_(data)
|
||||
|
||||
const res = this.atomicPhase_(
|
||||
async (manager) => {
|
||||
const discountConditionRepo: DiscountConditionRepository =
|
||||
manager.getCustomRepository(this.discountConditionRepository_)
|
||||
|
||||
if (!resolvedConditionType) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Missing one of products, collections, tags, types or customer groups in data`
|
||||
)
|
||||
}
|
||||
|
||||
if (data.id) {
|
||||
return await discountConditionRepo.addConditionResources(
|
||||
data.id,
|
||||
resolvedConditionType.resource_ids,
|
||||
resolvedConditionType.type,
|
||||
true
|
||||
)
|
||||
}
|
||||
|
||||
const discount = await this.retrieve(discountId, {
|
||||
relations: ["rule", "rule.conditions"],
|
||||
})
|
||||
|
||||
const created = discountConditionRepo.create({
|
||||
discount_rule_id: discount.rule_id,
|
||||
operator: data.operator,
|
||||
type: resolvedConditionType.type,
|
||||
})
|
||||
|
||||
const discountCondition = await discountConditionRepo.save(created)
|
||||
|
||||
return await discountConditionRepo.addConditionResources(
|
||||
discountCondition.id,
|
||||
resolvedConditionType.resource_ids,
|
||||
resolvedConditionType.type
|
||||
)
|
||||
},
|
||||
async (err: { code: string }) => {
|
||||
if (err.code === PostgresError.DUPLICATE_ERROR) {
|
||||
// A unique key constraint failed meaning the combination of
|
||||
// discount rule id, type, and operator already exists in the db.
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.DUPLICATE_ERROR,
|
||||
`Discount Condition with operator '${data.operator}' and type '${resolvedConditionType?.type}' already exist on a Discount Rule`
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
async validateDiscountForProduct(
|
||||
discountRuleId: string,
|
||||
productId: string | undefined
|
||||
|
||||
@@ -672,7 +672,7 @@ class ProductService extends BaseService {
|
||||
)
|
||||
|
||||
if (!product) {
|
||||
return Promise.resolve()
|
||||
return
|
||||
}
|
||||
|
||||
await productRepo.softRemove(product)
|
||||
@@ -1069,22 +1069,25 @@ class ProductService extends BaseService {
|
||||
|
||||
const productArray = Array.isArray(products) ? products : [products]
|
||||
|
||||
const priceSelectionStrategy =
|
||||
this.priceSelectionStrategy_.withTransaction(manager)
|
||||
const priceSelectionStrategy = this.priceSelectionStrategy_.withTransaction(
|
||||
manager
|
||||
)
|
||||
|
||||
const productsWithPrices = await Promise.all(
|
||||
productArray.map(async (p) => {
|
||||
if (p.variants?.length) {
|
||||
p.variants = await Promise.all(
|
||||
p.variants.map(async (v) => {
|
||||
const prices =
|
||||
await priceSelectionStrategy.calculateVariantPrice(v.id, {
|
||||
const prices = await priceSelectionStrategy.calculateVariantPrice(
|
||||
v.id,
|
||||
{
|
||||
region_id: regionId,
|
||||
currency_code: currencyCode,
|
||||
cart_id: cart_id,
|
||||
customer_id: customer_id,
|
||||
include_discount_prices,
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
return {
|
||||
...v,
|
||||
|
||||
@@ -105,6 +105,7 @@ export class AdminUpsertConditionsReq {
|
||||
}
|
||||
|
||||
export type UpsertDiscountConditionInput = {
|
||||
rule_id?: string
|
||||
id?: string
|
||||
operator?: DiscountConditionOperator
|
||||
products?: string[]
|
||||
|
||||
Reference in new issue
Block a user