fix(medusa): Normalize discount code before querying DB (#2224)
**What** Normalize discount code before querying DB Fixes CORE-567
This commit is contained in:
1 parent
f7177c9033
commit
3f73170288
7 files changed
+143
-20
No files matched your search
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"@medusajs/medusa": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Normalize discount code before querying DB in `DiscountService.retrieveByCode`
|
||||||
@@ -403,7 +403,7 @@ describe("/admin/discounts", () => {
|
|||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
id: "dynamic-discount",
|
id: "dynamic-discount",
|
||||||
code: "Dyn100",
|
code: "Dyn100",
|
||||||
})
|
}),
|
||||||
])
|
])
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@@ -2365,4 +2365,120 @@ describe("/admin/discounts", () => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe("GET /admin/discounts/code/:code", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
const manager = dbConnection.manager
|
||||||
|
await adminSeeder(dbConnection)
|
||||||
|
|
||||||
|
await manager.insert(DiscountRule, {
|
||||||
|
id: "test-discount-rule-fixed",
|
||||||
|
description: "Test discount rule",
|
||||||
|
type: "fixed",
|
||||||
|
value: 10,
|
||||||
|
allocation: "total",
|
||||||
|
})
|
||||||
|
|
||||||
|
await simpleDiscountFactory(dbConnection, {
|
||||||
|
id: "test-discount",
|
||||||
|
code: "TEST",
|
||||||
|
rule: {
|
||||||
|
type: "percentage",
|
||||||
|
value: "10",
|
||||||
|
allocation: "total",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
const db = useDb()
|
||||||
|
await db.teardown()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should retrieve discount using uppercase code", async () => {
|
||||||
|
const api = useApi()
|
||||||
|
|
||||||
|
const response = await api
|
||||||
|
.get("/admin/discounts/code/TEST", {
|
||||||
|
headers: {
|
||||||
|
Authorization: "Bearer test_token",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log(err)
|
||||||
|
})
|
||||||
|
|
||||||
|
const disc = response.data.discount
|
||||||
|
expect(response.status).toEqual(200)
|
||||||
|
expect(disc).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: "test-discount",
|
||||||
|
code: "TEST",
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should retrieve discount using lowercase code", async () => {
|
||||||
|
const api = useApi()
|
||||||
|
|
||||||
|
const response = await api
|
||||||
|
.get("/admin/discounts/code/test", {
|
||||||
|
headers: {
|
||||||
|
Authorization: "Bearer test_token",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log(err)
|
||||||
|
})
|
||||||
|
|
||||||
|
const disc = response.data.discount
|
||||||
|
expect(response.status).toEqual(200)
|
||||||
|
expect(disc).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: "test-discount",
|
||||||
|
code: "TEST",
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should retrieve discount using mixed casing code", async () => {
|
||||||
|
const api = useApi()
|
||||||
|
|
||||||
|
const response = await api
|
||||||
|
.get("/admin/discounts/code/TesT", {
|
||||||
|
headers: {
|
||||||
|
Authorization: "Bearer test_token",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log(err)
|
||||||
|
})
|
||||||
|
|
||||||
|
const disc = response.data.discount
|
||||||
|
expect(response.status).toEqual(200)
|
||||||
|
expect(disc).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: "test-discount",
|
||||||
|
code: "TEST",
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should respond with 404 on non-existing code", async () => {
|
||||||
|
const api = useApi()
|
||||||
|
|
||||||
|
try {
|
||||||
|
await api.get("/admin/discounts/code/non-existing", {
|
||||||
|
headers: {
|
||||||
|
Authorization: "Bearer test_token",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
expect(error.response.status).toEqual(404)
|
||||||
|
expect(error.response.data.message).toBe(
|
||||||
|
"Discount with code non-existing was not found"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
@@ -420,7 +420,7 @@ describe("/admin/draft-orders", () => {
|
|||||||
code: "TEST",
|
code: "TEST",
|
||||||
}),
|
}),
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
code: "free-shipping",
|
code: "FREE-SHIPPING",
|
||||||
}),
|
}),
|
||||||
])
|
])
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -2,13 +2,13 @@ import {
|
|||||||
AllocationType,
|
AllocationType,
|
||||||
Discount,
|
Discount,
|
||||||
DiscountRule,
|
DiscountRule,
|
||||||
DiscountRuleType,
|
DiscountRuleType
|
||||||
} from "@medusajs/medusa"
|
} from "@medusajs/medusa"
|
||||||
import faker from "faker"
|
import faker from "faker"
|
||||||
import { Connection } from "typeorm"
|
import { Connection } from "typeorm"
|
||||||
import {
|
import {
|
||||||
DiscountConditionFactoryData,
|
DiscountConditionFactoryData,
|
||||||
simpleDiscountConditionFactory,
|
simpleDiscountConditionFactory
|
||||||
} from "./simple-discount-condition-factory"
|
} from "./simple-discount-condition-factory"
|
||||||
|
|
||||||
export type DiscountRuleFactoryData = {
|
export type DiscountRuleFactoryData = {
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ module.exports = async (connection, data = {}) => {
|
|||||||
|
|
||||||
const freeShippingDiscount = manager.create(Discount, {
|
const freeShippingDiscount = manager.create(Discount, {
|
||||||
id: "free-shipping-discount",
|
id: "free-shipping-discount",
|
||||||
code: "free-shipping",
|
code: "FREE-SHIPPING",
|
||||||
is_dynamic: false,
|
is_dynamic: false,
|
||||||
is_disabled: false,
|
is_disabled: false,
|
||||||
rule_id: "free-shipping-rule",
|
rule_id: "free-shipping-rule",
|
||||||
|
|||||||
@@ -6,14 +6,14 @@ import {
|
|||||||
JoinColumn,
|
JoinColumn,
|
||||||
JoinTable,
|
JoinTable,
|
||||||
ManyToMany,
|
ManyToMany,
|
||||||
ManyToOne,
|
ManyToOne
|
||||||
} from "typeorm"
|
} from "typeorm"
|
||||||
import { DbAwareColumn, resolveDbType } from "../utils/db-aware-column"
|
import { DbAwareColumn, resolveDbType } from "../utils/db-aware-column"
|
||||||
|
|
||||||
import { DiscountRule } from "./discount-rule"
|
|
||||||
import { Region } from "./region"
|
|
||||||
import { SoftDeletableEntity } from "../interfaces/models/soft-deletable-entity"
|
import { SoftDeletableEntity } from "../interfaces/models/soft-deletable-entity"
|
||||||
import { generateEntityId } from "../utils/generate-entity-id"
|
import { generateEntityId } from "../utils/generate-entity-id"
|
||||||
|
import { DiscountRule } from "./discount-rule"
|
||||||
|
import { Region } from "./region"
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
export class Discount extends SoftDeletableEntity {
|
export class Discount extends SoftDeletableEntity {
|
||||||
@@ -79,10 +79,10 @@ export class Discount extends SoftDeletableEntity {
|
|||||||
|
|
||||||
@BeforeInsert()
|
@BeforeInsert()
|
||||||
private upperCaseCode(): void {
|
private upperCaseCode(): void {
|
||||||
|
this.code = this.code.toUpperCase()
|
||||||
if (this.id) return
|
if (this.id) return
|
||||||
|
|
||||||
this.id = generateEntityId(this.id, "disc")
|
this.id = generateEntityId(this.id, "disc")
|
||||||
this.code = this.code.toUpperCase()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,19 +6,21 @@ import {
|
|||||||
DeepPartial,
|
DeepPartial,
|
||||||
EntityManager,
|
EntityManager,
|
||||||
ILike,
|
ILike,
|
||||||
SelectQueryBuilder,
|
SelectQueryBuilder
|
||||||
} from "typeorm"
|
} from "typeorm"
|
||||||
import {
|
import {
|
||||||
EventBusService,
|
EventBusService,
|
||||||
ProductService,
|
ProductService,
|
||||||
RegionService,
|
RegionService,
|
||||||
TotalsService,
|
TotalsService
|
||||||
} from "."
|
} from "."
|
||||||
|
import { TransactionBaseService } from "../interfaces"
|
||||||
|
import TaxInclusivePricingFeatureFlag from "../loaders/feature-flags/tax-inclusive-pricing"
|
||||||
import { Cart, Discount, LineItem, Region } from "../models"
|
import { Cart, Discount, LineItem, Region } from "../models"
|
||||||
import {
|
import {
|
||||||
AllocationType as DiscountAllocation,
|
AllocationType as DiscountAllocation,
|
||||||
DiscountRule,
|
DiscountRule,
|
||||||
DiscountRuleType,
|
DiscountRuleType
|
||||||
} from "../models/discount-rule"
|
} from "../models/discount-rule"
|
||||||
import { DiscountRepository } from "../repositories/discount"
|
import { DiscountRepository } from "../repositories/discount"
|
||||||
import { DiscountConditionRepository } from "../repositories/discount-condition"
|
import { DiscountConditionRepository } from "../repositories/discount-condition"
|
||||||
@@ -31,16 +33,14 @@ import {
|
|||||||
CreateDynamicDiscountInput,
|
CreateDynamicDiscountInput,
|
||||||
FilterableDiscountProps,
|
FilterableDiscountProps,
|
||||||
UpdateDiscountInput,
|
UpdateDiscountInput,
|
||||||
UpdateDiscountRuleInput,
|
UpdateDiscountRuleInput
|
||||||
} from "../types/discount"
|
} from "../types/discount"
|
||||||
|
import { buildQuery, setMetadata } from "../utils"
|
||||||
import { isFuture, isPast } from "../utils/date-helpers"
|
import { isFuture, isPast } from "../utils/date-helpers"
|
||||||
import { formatException } from "../utils/exception-formatter"
|
import { formatException } from "../utils/exception-formatter"
|
||||||
import DiscountConditionService from "./discount-condition"
|
|
||||||
import CustomerService from "./customer"
|
|
||||||
import { TransactionBaseService } from "../interfaces"
|
|
||||||
import { buildQuery, setMetadata } from "../utils"
|
|
||||||
import { FlagRouter } from "../utils/flag-router"
|
import { FlagRouter } from "../utils/flag-router"
|
||||||
import TaxInclusivePricingFeatureFlag from "../loaders/feature-flags/tax-inclusive-pricing"
|
import CustomerService from "./customer"
|
||||||
|
import DiscountConditionService from "./discount-condition"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides layer to manipulate discounts.
|
* Provides layer to manipulate discounts.
|
||||||
@@ -276,11 +276,13 @@ class DiscountService extends TransactionBaseService {
|
|||||||
const manager = this.manager_
|
const manager = this.manager_
|
||||||
const discountRepo = manager.getCustomRepository(this.discountRepository_)
|
const discountRepo = manager.getCustomRepository(this.discountRepository_)
|
||||||
|
|
||||||
let query = buildQuery({ code: discountCode, is_dynamic: false }, config)
|
const normalizedCode = discountCode.toUpperCase()
|
||||||
|
|
||||||
|
let query = buildQuery({ code: normalizedCode, is_dynamic: false }, config)
|
||||||
let discount = await discountRepo.findOne(query)
|
let discount = await discountRepo.findOne(query)
|
||||||
|
|
||||||
if (!discount) {
|
if (!discount) {
|
||||||
query = buildQuery({ code: discountCode, is_dynamic: true }, config)
|
query = buildQuery({ code: normalizedCode, is_dynamic: true }, config)
|
||||||
discount = await discountRepo.findOne(query)
|
discount = await discountRepo.findOne(query)
|
||||||
|
|
||||||
if (!discount) {
|
if (!discount) {
|
||||||
|
|||||||
Reference in new issue
Block a user