feat: Add DiscountConditions (#1230)

* feat: Add DiscountCondition entity + Join table per relation (#1146)

* feat: Convert DiscountService to TypeScript (#1149)

* feat: Add DiscountRepository + bulk insert and remove (#1156)

* feat: Add `conditions` to payload in `POST /discounts` and `POST /discounts/:id` (#1170)

* feat: Add DiscountRuleCondition entity

* fix relation

* fix join key

* Add discount rule condition repo

* add join table per relation

* Convert DiscountService to TypeScript

* feat: Add DiscountConditionRepository

* Add migration + remove use of valid_for

* revert changes to files, not done yet

* init work on create discount endpoint

* Add conditions to create discount endpoint

* Add conditions to update discount endpoint

* Add unique constraint to discount condition

* integration tests passing

* fix imports of models

* fix tests (excluding totals calculations)

* Fix commented code

* add unique constraint on discount condition

* Add generic way of generating retrieve configs

* Requested changes + ExactlyOne validator

* Remove isLocal flag from error handler

* Use postgres error constant

* remove commented code

* feat: Add `isValidForProduct` to check if Discount is valid for a given Product (#1172)

* feat: Add `canApplyForCustomer` to check if Discount is valid for customer groups (#1212)

* feat: Add `calculateDiscountForLineItem` (#1224)

* feat: Adds discount condition test factory (#1228)

* Remove use of valid_for

* Tests passing

* Remove valid_for form relations

* Add integration tests for applying discounts to cart
This commit is contained in:
Oliver Windall Juhl
2022-03-24 16:47:50 +01:00
committed by GitHub
parent b7f699654b
commit a610805917
60 changed files with 4805 additions and 2021 deletions
+547 -17
View File
@@ -1,5 +1,11 @@
const path = require("path")
const { Region, DiscountRule, Discount } = require("@medusajs/medusa")
const {
Region,
DiscountRule,
Discount,
Customer,
CustomerGroup,
} = require("@medusajs/medusa")
const setupServer = require("../../../helpers/setup-server")
const { useApi } = require("../../../helpers/use-api")
@@ -7,6 +13,10 @@ const { initDb, useDb } = require("../../../helpers/use-db")
const adminSeeder = require("../../helpers/admin-seeder")
const discountSeeder = require("../../helpers/discount-seeder")
const { exportAllDeclaration } = require("@babel/types")
const { simpleProductFactory } = require("../../factories")
const {
simpleDiscountFactory,
} = require("../../factories/simple-discount-factory")
jest.setTimeout(30000)
@@ -26,6 +36,153 @@ describe("/admin/discounts", () => {
medusaProcess.kill()
})
describe("GET /admin/discounts/:id", () => {
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",
})
const prod = await simpleProductFactory(dbConnection, { type: "pants" })
await simpleDiscountFactory(dbConnection, {
id: "test-discount",
code: "TEST",
rule: {
type: "percentage",
value: "10",
allocation: "total",
conditions: [
{
type: "products",
operator: "in",
products: [prod.id],
},
{
type: "product_types",
operator: "not_in",
product_types: [prod.type_id],
},
],
},
})
})
afterEach(async () => {
const db = useDb()
await db.teardown()
})
it("should retrieve discount with customer conditions created with factory", async () => {
const api = useApi()
const group = await dbConnection.manager.insert(CustomerGroup, {
id: "customer-group-1",
name: "vip-customers",
})
await dbConnection.manager.insert(Customer, {
id: "cus_1234",
email: "oli@email.com",
groups: [group],
})
await simpleDiscountFactory(dbConnection, {
id: "test-discount",
code: "TEST",
rule: {
type: "percentage",
value: "10",
allocation: "total",
conditions: [
{
type: "customer_groups",
operator: "in",
customer_groups: ["customer-group-1"],
},
],
},
})
const response = await api
.get(
"/admin/discounts/test-discount?expand=rule,rule.conditions,rule.conditions.customer_groups",
{
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",
})
)
expect(disc.rule.conditions).toEqual(
expect.arrayContaining([
expect.objectContaining({
type: "customer_groups",
operator: "in",
discount_rule_id: disc.rule.id,
}),
])
)
})
it("should retrieve discount with product conditions created with factory", async () => {
const api = useApi()
const response = await api
.get(
"/admin/discounts/test-discount?expand=rule,rule.conditions,rule.conditions.products,rule.conditions.product_types",
{
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",
})
)
expect(disc.rule.conditions).toEqual(
expect.arrayContaining([
expect.objectContaining({
type: "products",
operator: "in",
discount_rule_id: disc.rule.id,
}),
expect.objectContaining({
type: "product_types",
operator: "not_in",
discount_rule_id: disc.rule.id,
}),
])
)
})
})
describe("GET /admin/discounts", () => {
beforeEach(async () => {
const manager = dbConnection.manager
@@ -267,25 +424,398 @@ describe("/admin/discounts", () => {
usage_limit: 10,
})
)
})
const test = await api.get(
`/admin/discounts/${response.data.discount.id}`,
{ headers: { Authorization: "Bearer test_token" } }
)
it("creates a discount with conditions", async () => {
const api = useApi()
expect(test.status).toEqual(200)
expect(test.data.discount).toEqual(
expect.objectContaining({
code: "HELLOWORLD",
usage_limit: 10,
rule: expect.objectContaining({
value: 10,
type: "percentage",
description: "test",
allocation: "total",
}),
const product = await simpleProductFactory(dbConnection, {
type: "pants",
tags: ["ss22"],
})
const anotherProduct = await simpleProductFactory(dbConnection, {
type: "blouses",
tags: ["ss23"],
})
const response = await api
.post(
"/admin/discounts",
{
code: "HELLOWORLD",
rule: {
description: "test",
type: "percentage",
value: 10,
allocation: "total",
conditions: [
{
products: [product.id],
operator: "in",
},
{
products: [anotherProduct.id],
operator: "not_in",
},
{
product_types: [product.type_id],
operator: "not_in",
},
{
product_types: [anotherProduct.type_id],
operator: "in",
},
{
product_tags: [product.tags[0].id],
operator: "not_in",
},
{
product_tags: [anotherProduct.tags[0].id],
operator: "in",
},
],
},
usage_limit: 10,
},
{
headers: {
Authorization: "Bearer test_token",
},
}
)
.catch((err) => {
console.log(err)
})
)
expect(response.status).toEqual(200)
expect(response.data.discount.rule.conditions).toEqual([
expect.objectContaining({
type: "products",
operator: "in",
}),
expect.objectContaining({
type: "products",
operator: "not_in",
}),
expect.objectContaining({
type: "product_types",
operator: "not_in",
}),
expect.objectContaining({
type: "product_types",
operator: "in",
}),
expect.objectContaining({
type: "product_tags",
operator: "not_in",
}),
expect.objectContaining({
type: "product_tags",
operator: "in",
}),
])
})
it("creates a discount with conditions and updates said conditions", async () => {
const api = useApi()
const product = await simpleProductFactory(dbConnection, {
type: "pants",
})
const anotherProduct = await simpleProductFactory(dbConnection, {
type: "pants",
})
const response = await api
.post(
"/admin/discounts?expand=rule,rule.conditions",
{
code: "HELLOWORLD",
rule: {
description: "test",
type: "percentage",
value: 10,
allocation: "total",
conditions: [
{
products: [product.id],
operator: "in",
},
{
product_types: [product.type_id],
operator: "not_in",
},
],
},
usage_limit: 10,
},
{
headers: {
Authorization: "Bearer test_token",
},
}
)
.catch((err) => {
console.log(err)
})
expect(response.status).toEqual(200)
expect(response.data.discount.rule.conditions).toEqual([
expect.objectContaining({
type: "products",
operator: "in",
}),
expect.objectContaining({
type: "product_types",
operator: "not_in",
}),
])
const createdRule = response.data.discount.rule
const condsToUpdate = createdRule.conditions[0]
const updated = await api
.post(
`/admin/discounts/${response.data.discount.id}?expand=rule,rule.conditions,rule.conditions.products`,
{
rule: {
id: createdRule.id,
type: createdRule.type,
value: createdRule.value,
allocation: createdRule.allocation,
conditions: [
{
id: condsToUpdate.id,
products: [product.id, anotherProduct.id],
},
],
},
},
{
headers: {
Authorization: "Bearer test_token",
},
}
)
.catch((err) => {
console.log(err)
})
expect(updated.status).toEqual(200)
expect(updated.data.discount.rule.conditions).toEqual([
expect.objectContaining({
type: "products",
operator: "in",
products: expect.arrayContaining([
expect.objectContaining({
id: product.id,
}),
expect.objectContaining({
id: anotherProduct.id,
}),
]),
}),
expect.objectContaining({
type: "product_types",
operator: "not_in",
}),
])
})
it("fails to add condition on rule with existing comb. of type and operator", async () => {
const api = useApi()
const product = await simpleProductFactory(dbConnection, {
type: "pants",
})
const anotherProduct = await simpleProductFactory(dbConnection, {
type: "pants",
})
const response = await api
.post(
"/admin/discounts",
{
code: "HELLOWORLD",
rule: {
description: "test",
type: "percentage",
value: 10,
allocation: "total",
conditions: [
{
products: [product.id],
operator: "in",
},
],
},
usage_limit: 10,
},
{
headers: {
Authorization: "Bearer test_token",
},
}
)
.catch((err) => {
console.log(err)
})
expect(response.status).toEqual(200)
const createdRule = response.data.discount.rule
try {
await api.post(
`/admin/discounts/${response.data.discount.id}?expand=rule,rule.conditions,rule.conditions.products`,
{
rule: {
id: createdRule.id,
type: createdRule.type,
value: createdRule.value,
allocation: createdRule.allocation,
conditions: [
{
products: [anotherProduct.id],
operator: "in",
},
],
},
},
{
headers: {
Authorization: "Bearer test_token",
},
}
)
} catch (error) {
console.log(error)
expect(error.response.data.type).toEqual("duplicate_error")
expect(error.response.data.message).toEqual(
`Discount Condition with operator 'in' and type 'products' already exist on a Discount Rule`
)
}
})
it("fails if multiple types of resources are provided on create", async () => {
const api = useApi()
const product = await simpleProductFactory(dbConnection, {
type: "pants",
})
try {
await api.post(
"/admin/discounts",
{
code: "HELLOWORLD",
rule: {
description: "test",
type: "percentage",
value: 10,
allocation: "total",
conditions: [
{
products: [product.id],
product_types: [product.type_id],
operator: "in",
},
],
},
usage_limit: 10,
},
{
headers: {
Authorization: "Bearer test_token",
},
}
)
} catch (error) {
expect(error.response.data.type).toEqual("invalid_data")
expect(error.response.data.message).toEqual(
"Only one of products, product_types is allowed, Only one of product_types, products is allowed"
)
}
})
it("fails if multiple types of resources are provided on update", async () => {
const api = useApi()
const product = await simpleProductFactory(dbConnection, {
type: "pants",
})
const anotherProduct = await simpleProductFactory(dbConnection, {
type: "pants",
})
const response = await api
.post(
"/admin/discounts",
{
code: "HELLOWORLD",
rule: {
description: "test",
type: "percentage",
value: 10,
allocation: "total",
conditions: [
{
products: [product.id],
operator: "in",
},
],
},
usage_limit: 10,
},
{
headers: {
Authorization: "Bearer test_token",
},
}
)
.catch((err) => {
console.log(err)
})
expect(response.status).toEqual(200)
const createdRule = response.data.discount.rule
try {
await api.post(
`/admin/discounts/${response.data.discount.id}?expand=rule,rule.conditions,rule.conditions.products`,
{
rule: {
id: createdRule.id,
type: createdRule.type,
value: createdRule.value,
allocation: createdRule.allocation,
conditions: [
{
products: [anotherProduct.id],
product_types: [product.type_id],
operator: "in",
},
],
},
},
{
headers: {
Authorization: "Bearer test_token",
},
}
)
} catch (error) {
console.log(error)
expect(error.response.data.type).toEqual("invalid_data")
expect(error.response.data.message).toEqual(
`Only one of products, product_types is allowed, Only one of product_types, products is allowed`
)
}
})
it("creates a discount and updates it", async () => {
@@ -16,6 +16,16 @@ const { initDb, useDb } = require("../../../helpers/use-db")
const cartSeeder = require("../../helpers/cart-seeder")
const productSeeder = require("../../helpers/product-seeder")
const swapSeeder = require("../../helpers/swap-seeder")
const { simpleCartFactory } = require("../../factories")
const {
simpleDiscountFactory,
} = require("../../factories/simple-discount-factory")
const {
simpleCustomerFactory,
} = require("../../factories/simple-customer-factory")
const {
simpleCustomerGroupFactory,
} = require("../../factories/simple-customer-group-factory")
jest.setTimeout(30000)
@@ -354,6 +364,348 @@ describe("/store/carts", () => {
})
})
it("successfully passes customer conditions with `in` operator and applies discount", async () => {
const api = useApi()
await simpleCustomerFactory(dbConnection, {
id: "cus_1234",
email: "oli@medusajs.com",
groups: [
{
id: "customer-group-1",
name: "VIP Customer",
},
],
})
await simpleCustomerGroupFactory(dbConnection, {
id: "customer-group-2",
name: "Loyal",
})
await simpleCartFactory(
dbConnection,
{
id: "test-customer-discount",
region: {
id: "test-region",
name: "Test region",
tax_rate: 12,
},
customer: "cus_1234",
line_items: [
{
variant_id: "test-variant",
unit_price: 100,
},
],
},
100
)
await simpleDiscountFactory(dbConnection, {
id: "test-discount",
code: "TEST",
regions: ["test-region"],
rule: {
type: "percentage",
value: "10",
allocation: "total",
conditions: [
{
type: "customer_groups",
operator: "in",
customer_groups: ["customer-group-1", "customer-group-2"],
},
],
},
})
const response = await api.post("/store/carts/test-customer-discount", {
discounts: [{ code: "TEST" }],
})
const cartRes = response.data.cart
expect(cartRes.discounts).toEqual(
expect.arrayContaining([
expect.objectContaining({
code: "TEST",
}),
])
)
expect(response.status).toEqual(200)
})
it("successfully passes customer conditions with `not_in` operator and applies discount", async () => {
const api = useApi()
await simpleCustomerFactory(dbConnection, {
id: "cus_1234",
email: "oli@medusajs.com",
groups: [
{
id: "customer-group-2",
name: "VIP Customer",
},
],
})
await simpleCustomerGroupFactory(dbConnection, {
id: "customer-group-1",
name: "Customer group 1",
})
await simpleCustomerGroupFactory(dbConnection, {
id: "customer-group-3",
name: "Customer group 3",
})
await simpleCartFactory(
dbConnection,
{
id: "test-customer-discount",
region: {
id: "test-region",
name: "Test region",
tax_rate: 12,
},
customer: "cus_1234",
line_items: [
{
variant_id: "test-variant",
unit_price: 100,
},
],
},
100
)
await simpleDiscountFactory(dbConnection, {
id: "test-discount",
code: "TEST",
regions: ["test-region"],
rule: {
type: "percentage",
value: "10",
allocation: "total",
conditions: [
{
type: "customer_groups",
operator: "not_in",
customer_groups: ["customer-group-1", "customer-group-3"],
},
],
},
})
const response = await api.post("/store/carts/test-customer-discount", {
discounts: [{ code: "TEST" }],
})
const cartRes = response.data.cart
expect(cartRes.discounts).toEqual(
expect.arrayContaining([
expect.objectContaining({
code: "TEST",
}),
])
)
expect(response.status).toEqual(200)
})
it("successfully applies discount in case no conditions is defined for group", async () => {
const api = useApi()
await simpleCustomerFactory(dbConnection, {
id: "cus_1234",
email: "oli@medusajs.com",
groups: [
{
id: "customer-group-1",
name: "VIP Customer",
},
],
})
await simpleCartFactory(
dbConnection,
{
id: "test-customer-discount",
region: {
id: "test-region",
name: "Test region",
tax_rate: 12,
},
customer: "cus_1234",
line_items: [
{
variant_id: "test-variant",
unit_price: 100,
},
],
},
100
)
await simpleDiscountFactory(dbConnection, {
id: "test-discount",
code: "TEST",
regions: ["test-region"],
rule: {
type: "percentage",
value: "10",
allocation: "total",
},
})
const response = await api.post("/store/carts/test-customer-discount", {
discounts: [{ code: "TEST" }],
})
const cartRes = response.data.cart
expect(cartRes.discounts).toEqual(
expect.arrayContaining([
expect.objectContaining({
code: "TEST",
}),
])
)
expect(response.status).toEqual(200)
})
it("fails to apply discount if customer group is part of `not_in` conditions", async () => {
const api = useApi()
await simpleCustomerFactory(dbConnection, {
id: "cus_1234",
email: "oli@medusajs.com",
groups: [
{
id: "customer-group-1",
name: "VIP Customer",
},
],
})
await simpleCartFactory(
dbConnection,
{
id: "test-customer-discount",
region: {
id: "test-region",
name: "Test region",
tax_rate: 12,
},
customer: "cus_1234",
line_items: [
{
variant_id: "test-variant",
unit_price: 100,
},
],
},
100
)
await simpleDiscountFactory(dbConnection, {
id: "test-discount",
code: "TEST",
regions: ["test-region"],
rule: {
type: "percentage",
value: "10",
allocation: "total",
conditions: [
{
type: "customer_groups",
operator: "not_in",
customer_groups: ["customer-group-1"],
},
],
},
})
try {
await api.post("/store/carts/test-customer-discount", {
discounts: [{ code: "TEST" }],
})
} catch (error) {
expect(error.response.status).toEqual(400)
expect(error.response.data.message).toEqual(
"Discount is not valid for customer"
)
}
})
it("fails to apply discount if customer group is not part of `in` conditions", async () => {
const api = useApi()
await simpleCustomerFactory(dbConnection, {
id: "cus_1234",
email: "oli@medusajs.com",
groups: [
{
id: "customer-group-2",
name: "VIP Customer",
},
],
})
await simpleCustomerGroupFactory(dbConnection, {
id: "customer-group-1",
name: "Customer group 1",
})
await simpleCartFactory(
dbConnection,
{
id: "test-customer-discount",
region: {
id: "test-region",
name: "Test region",
tax_rate: 12,
},
customer: "cus_1234",
line_items: [
{
variant_id: "test-variant",
unit_price: 100,
},
],
},
100
)
await simpleDiscountFactory(dbConnection, {
id: "test-discount",
code: "TEST",
regions: ["test-region"],
rule: {
type: "percentage",
value: "10",
allocation: "total",
conditions: [
{
type: "customer_groups",
operator: "in",
customer_groups: ["customer-group-1"],
},
],
},
})
try {
await api.post("/store/carts/test-customer-discount", {
discounts: [{ code: "TEST" }],
})
} catch (error) {
expect(error.response.status).toEqual(400)
expect(error.response.data.message).toEqual(
"Discount is not valid for customer"
)
}
})
it("fails to apply expired discount", async () => {
expect.assertions(2)
const api = useApi()
@@ -1,16 +1,16 @@
import { Connection } from "typeorm"
import faker from "faker"
import { Cart } from "@medusajs/medusa"
import { RegionFactoryData, simpleRegionFactory } from "./simple-region-factory"
import {
LineItemFactoryData,
simpleLineItemFactory,
} from "./simple-line-item-factory"
import faker from "faker"
import { Connection } from "typeorm"
import {
AddressFactoryData,
simpleAddressFactory,
} from "./simple-address-factory"
import { simpleCustomerFactory } from "./simple-customer-factory"
import {
LineItemFactoryData,
simpleLineItemFactory,
} from "./simple-line-item-factory"
import { RegionFactoryData, simpleRegionFactory } from "./simple-region-factory"
import {
ShippingMethodFactoryData,
simpleShippingMethodFactory,
@@ -18,6 +18,7 @@ import {
export type CartFactoryData = {
id?: string
customer?: string | { email: string }
region?: RegionFactoryData | string
email?: string | null
line_items?: LineItemFactoryData[]
@@ -43,6 +44,22 @@ export const simpleCartFactory = async (
const region = await simpleRegionFactory(connection, data.region)
regionId = region.id
}
let customerId: string
if (typeof data.customer === "string") {
customerId = data.customer
} else {
if (data?.customer?.email) {
const customer = await simpleCustomerFactory(connection, data.customer)
customerId = customer.id
} else if (data.email) {
const customer = await simpleCustomerFactory(connection, {
email: data.email,
})
customerId = customer.id
}
}
const address = await simpleAddressFactory(connection, data.shipping_address)
const id = data.id || `simple-cart-${Math.random() * 1000}`
@@ -51,6 +68,7 @@ export const simpleCartFactory = async (
email:
typeof data.email !== "undefined" ? data.email : faker.internet.email(),
region_id: regionId,
customer_id: customerId,
shipping_address_id: address.id,
})
@@ -0,0 +1,46 @@
import { Customer } from "@medusajs/medusa"
import faker from "faker"
import { Connection } from "typeorm"
import {
CustomerGroupFactoryData,
simpleCustomerGroupFactory,
} from "./simple-customer-group-factory"
export type CustomerFactoryData = {
id?: string
email?: string
groups?: CustomerGroupFactoryData[]
}
export const simpleCustomerFactory = async (
connection: Connection,
data: CustomerFactoryData = {},
seed?: number
): Promise<Customer> => {
if (typeof seed !== "undefined") {
faker.seed(seed)
}
const manager = connection.manager
const customerId = data.id || `simple-customer-${Math.random() * 1000}`
const c = manager.create(Customer, {
id: customerId,
email: data.email,
})
const customer = await manager.save(c)
if (data.groups) {
const groups = []
for (const g of data.groups) {
const created = await simpleCustomerGroupFactory(connection, g)
groups.push(created)
}
customer.groups = groups
await manager.save(customer)
}
return customer
}
@@ -0,0 +1,31 @@
import { CustomerGroup } from "@medusajs/medusa"
import faker from "faker"
import { Connection } from "typeorm"
export type CustomerGroupFactoryData = {
id?: string
name?: string
}
export const simpleCustomerGroupFactory = async (
connection: Connection,
data: CustomerGroupFactoryData = {},
seed?: number
): Promise<CustomerGroup> => {
if (typeof seed !== "undefined") {
faker.seed(seed)
}
const manager = connection.manager
const customerGroupId =
data.id || `simple-customer-group-${Math.random() * 1000}`
const c = manager.create(CustomerGroup, {
id: customerGroupId,
name: data.name,
})
const group = await manager.save(c)
return group
}
@@ -0,0 +1,116 @@
import {
DiscountCondition,
DiscountConditionOperator,
DiscountConditionType,
} from "@medusajs/medusa/dist/models/discount-condition"
import { DiscountConditionCustomerGroup } from "@medusajs/medusa/dist/models/discount-condition-customer-group"
import { DiscountConditionProduct } from "@medusajs/medusa/dist/models/discount-condition-product"
import { DiscountConditionProductCollection } from "@medusajs/medusa/dist/models/discount-condition-product-collection"
import { DiscountConditionProductTag } from "@medusajs/medusa/dist/models/discount-condition-product-tag"
import { DiscountConditionProductType } from "@medusajs/medusa/dist/models/discount-condition-product-type"
import { DiscountConditionJoinTableForeignKey } from "@medusajs/medusa/dist/repositories/discount-condition"
import faker from "faker"
import { Connection } from "typeorm"
export type DiscuntConditionFactoryData = {
rule_id: string
type: DiscountConditionType
operator: DiscountConditionOperator
products: string[]
product_collections: string[]
product_types: string[]
product_tags: string[]
customer_groups: string[]
}
const getJoinTableResourceIdentifiers = (type: string) => {
let conditionTable: any
let resourceKey
switch (type) {
case DiscountConditionType.PRODUCTS: {
resourceKey = DiscountConditionJoinTableForeignKey.PRODUCT_ID
conditionTable = DiscountConditionProduct
break
}
case DiscountConditionType.PRODUCT_TYPES: {
resourceKey = DiscountConditionJoinTableForeignKey.PRODUCT_TYPE_ID
conditionTable = DiscountConditionProductType
break
}
case DiscountConditionType.PRODUCT_COLLECTIONS: {
resourceKey = DiscountConditionJoinTableForeignKey.PRODUCT_COLLECTION_ID
conditionTable = DiscountConditionProductCollection
break
}
case DiscountConditionType.PRODUCT_TAGS: {
resourceKey = DiscountConditionJoinTableForeignKey.PRODUCT_TAG_ID
conditionTable = DiscountConditionProductTag
break
}
case DiscountConditionType.CUSTOMER_GROUPS: {
resourceKey = DiscountConditionJoinTableForeignKey.CUSTOMER_GROUP_ID
conditionTable = DiscountConditionCustomerGroup
break
}
default:
break
}
return {
resourceKey,
conditionTable,
}
}
export const simpleDiscountConditionFactory = async (
connection: Connection,
data: DiscuntConditionFactoryData,
seed?: number
): Promise<void> => {
if (typeof seed !== "undefined") {
faker.seed(seed)
}
const manager = connection.manager
let resources = []
if (data.products) {
resources = data.products
}
if (data.product_collections) {
resources = data.product_collections
}
if (data.product_types) {
resources = data.product_types
}
if (data.product_tags) {
resources = data.product_tags
}
if (data.customer_groups) {
resources = data.customer_groups
}
const condToSave = manager.create(DiscountCondition, {
type: data.type,
operator: data.operator,
discount_rule_id: data.rule_id,
})
const { conditionTable, resourceKey } = getJoinTableResourceIdentifiers(
data.type
)
const condition = await manager.save(condToSave)
for (const resourceCond of resources) {
const toSave = manager.create(conditionTable, {
[resourceKey]: resourceCond,
condition_id: condition.id,
})
await manager.save(toSave)
}
}
@@ -1,16 +1,21 @@
import { Connection } from "typeorm"
import faker from "faker"
import {
AllocationType,
Discount,
DiscountRule,
DiscountRuleType,
AllocationType,
} from "@medusajs/medusa"
import faker from "faker"
import { Connection } from "typeorm"
import {
DiscuntConditionFactoryData,
simpleDiscountConditionFactory,
} from "./simple-discount-condition-factory"
export type DiscountRuleFactoryData = {
type?: DiscountRuleType
value?: number
allocation?: AllocationType
conditions: DiscuntConditionFactoryData[]
}
export type DiscountFactoryData = {
@@ -41,6 +46,16 @@ export const simpleDiscountFactory = async (
const dRule = await manager.save(ruleToSave)
if (data?.rule?.conditions) {
for (const condition of data.rule.conditions) {
await simpleDiscountConditionFactory(
connection,
{ ...condition, rule_id: dRule.id },
1
)
}
}
const toSave = manager.create(Discount, {
id: data.id,
is_dynamic: data.is_dynamic ?? false,
@@ -1,16 +1,16 @@
import { Connection } from "typeorm"
import faker from "faker"
import {
ShippingProfileType,
ShippingProfile,
Product,
ProductType,
ProductOption,
ProductTag,
ProductType,
ShippingProfile,
ShippingProfileType,
} from "@medusajs/medusa"
import faker from "faker"
import { Connection } from "typeorm"
import {
simpleProductVariantFactory,
ProductVariantFactoryData,
simpleProductVariantFactory,
} from "./simple-product-variant-factory"
export type ProductFactoryData = {
@@ -19,6 +19,7 @@ export type ProductFactoryData = {
status?: string
title?: string
type?: string
tags?: string[]
options?: { id: string; title: string }[]
variants?: ProductVariantFactoryData[]
}
@@ -42,27 +43,40 @@ export const simpleProductFactory = async (
type: ShippingProfileType.GIFT_CARD,
})
let typeId: string
const prodId = data.id || `simple-product-${Math.random() * 1000}`
const productToCreate = {
id: prodId,
title: data.title || faker.commerce.productName(),
is_giftcard: data.is_giftcard || false,
discountable: !data.is_giftcard,
tags: [],
profile_id: data.is_giftcard ? gcProfile.id : defaultProfile.id,
}
if (typeof data.tags !== "undefined") {
for (let i = 0; i < data.tags.length; i++) {
const createdTag = manager.create(ProductTag, {
id: `tag-${Math.random() * 1000}`,
value: data.tags[i],
})
const tagRes = await manager.save(createdTag)
productToCreate.tags.push(tagRes)
}
}
if (typeof data.type !== "undefined") {
const toSave = manager.create(ProductType, {
value: data.type,
})
const res = await manager.save(toSave)
typeId = res.id
productToCreate["type_id"] = res.id
}
const prodId = data.id || `simple-product-${Math.random() * 1000}`
const toSave = manager.create(Product, {
id: prodId,
type_id: typeId,
status: data.status,
title: data.title || faker.commerce.productName(),
is_giftcard: data.is_giftcard || false,
discountable: !data.is_giftcard,
profile_id: data.is_giftcard ? gcProfile.id : defaultProfile.id,
})
const toSave = manager.create(Product, productToCreate)
const product = await manager.save(toSave)
await manager.save(toSave)
const optionId = `${prodId}-option`
const options = data.options || [{ id: optionId, title: "Size" }]
@@ -97,5 +111,5 @@ export const simpleProductFactory = async (
await simpleProductVariantFactory(connection, factoryData)
}
return product
return await manager.findOne(Product, { id: prodId }, { relations: ["tags"] })
}
+3 -3
View File
@@ -8,16 +8,16 @@
"build": "babel src -d dist --extensions \".ts,.js\""
},
"dependencies": {
"@medusajs/medusa": "1.2.0-dev-1647336201011",
"@medusajs/medusa": "1.2.1-dev-1648026403166",
"faker": "^5.5.3",
"medusa-interfaces": "1.2.0-dev-1647336201011",
"medusa-interfaces": "1.2.1-dev-1648026403166",
"typeorm": "^0.2.31"
},
"devDependencies": {
"@babel/cli": "^7.12.10",
"@babel/core": "^7.12.10",
"@babel/node": "^7.12.10",
"babel-preset-medusa-package": "1.1.19-dev-1647336201011",
"babel-preset-medusa-package": "1.1.19-dev-1648026403166",
"jest": "^26.6.3"
}
}
File diff suppressed because it is too large Load Diff