feat(utils,types): add registerUsages for promotions + computeActions consider usage (#6094)
RESOLVES CORE-1639 RESOLVES CORE-1640 RESOLVES CORE-1634
This commit is contained in:
6
.changeset/cool-rockets-wash.md
Normal file
6
.changeset/cool-rockets-wash.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
"@medusajs/types": patch
|
||||
"@medusajs/utils": patch
|
||||
---
|
||||
|
||||
feat(utils,types): add registerUsages for promotion's computed actions
|
||||
@@ -2,6 +2,7 @@ import { IPromotionModuleService } from "@medusajs/types"
|
||||
import { PromotionType } from "@medusajs/utils"
|
||||
import { SqlEntityManager } from "@mikro-orm/postgresql"
|
||||
import { initialize } from "../../../../src"
|
||||
import { createCampaigns } from "../../../__fixtures__/campaigns"
|
||||
import { DB_URL, MikroOrmWrapper } from "../../../utils"
|
||||
|
||||
jest.setTimeout(30000)
|
||||
@@ -459,10 +460,10 @@ describe("Promotion Service: computeActions", () => {
|
||||
},
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe("when promotion is for items and allocation is across", () => {
|
||||
it("should compute the correct item amendments", async () => {
|
||||
it("should compute budget exceeded action when applicable total exceeds campaign budget for type spend", async () => {
|
||||
await createCampaigns(repositoryManager)
|
||||
|
||||
const [createdPromotion] = await service.create([
|
||||
{
|
||||
code: "PROMOTION_TEST",
|
||||
@@ -474,12 +475,13 @@ describe("Promotion Service: computeActions", () => {
|
||||
values: ["VIP", "top100"],
|
||||
},
|
||||
],
|
||||
campaign_id: "campaign-id-1",
|
||||
application_method: {
|
||||
type: "fixed",
|
||||
target_type: "items",
|
||||
allocation: "across",
|
||||
value: "200",
|
||||
max_quantity: 2,
|
||||
allocation: "each",
|
||||
value: "500",
|
||||
max_quantity: 5,
|
||||
target_rules: [
|
||||
{
|
||||
attribute: "product_category.id",
|
||||
@@ -500,7 +502,126 @@ describe("Promotion Service: computeActions", () => {
|
||||
items: [
|
||||
{
|
||||
id: "item_cotton_tshirt",
|
||||
quantity: 1,
|
||||
quantity: 5,
|
||||
unit_price: 1000,
|
||||
product_category: {
|
||||
id: "catg_cotton",
|
||||
},
|
||||
product: {
|
||||
id: "prod_tshirt",
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
expect(result).toEqual([
|
||||
{ action: "campaignBudgetExceeded", code: "PROMOTION_TEST" },
|
||||
])
|
||||
})
|
||||
|
||||
it("should compute budget exceeded action when applicable total exceeds campaign budget for type usage", async () => {
|
||||
await createCampaigns(repositoryManager)
|
||||
|
||||
const [createdPromotion] = await service.create([
|
||||
{
|
||||
code: "PROMOTION_TEST",
|
||||
type: PromotionType.STANDARD,
|
||||
rules: [
|
||||
{
|
||||
attribute: "customer.customer_group.id",
|
||||
operator: "in",
|
||||
values: ["VIP", "top100"],
|
||||
},
|
||||
],
|
||||
campaign_id: "campaign-id-2",
|
||||
application_method: {
|
||||
type: "fixed",
|
||||
target_type: "items",
|
||||
allocation: "each",
|
||||
value: "500",
|
||||
max_quantity: 5,
|
||||
target_rules: [
|
||||
{
|
||||
attribute: "product_category.id",
|
||||
operator: "eq",
|
||||
values: ["catg_cotton"],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
await service.updateCampaigns({
|
||||
id: "campaign-id-2",
|
||||
budget: { used: 1000 },
|
||||
})
|
||||
|
||||
const result = await service.computeActions(["PROMOTION_TEST"], {
|
||||
customer: {
|
||||
customer_group: {
|
||||
id: "VIP",
|
||||
},
|
||||
},
|
||||
items: [
|
||||
{
|
||||
id: "item_cotton_tshirt",
|
||||
quantity: 5,
|
||||
unit_price: 1000,
|
||||
product_category: {
|
||||
id: "catg_cotton",
|
||||
},
|
||||
product: {
|
||||
id: "prod_tshirt",
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
expect(result).toEqual([
|
||||
{ action: "campaignBudgetExceeded", code: "PROMOTION_TEST" },
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe("when promotion is for items and allocation is across", () => {
|
||||
it("should compute the correct item amendments", async () => {
|
||||
const [createdPromotion] = await service.create([
|
||||
{
|
||||
code: "PROMOTION_TEST",
|
||||
type: PromotionType.STANDARD,
|
||||
rules: [
|
||||
{
|
||||
attribute: "customer.customer_group.id",
|
||||
operator: "in",
|
||||
values: ["VIP", "top100"],
|
||||
},
|
||||
],
|
||||
application_method: {
|
||||
type: "fixed",
|
||||
target_type: "items",
|
||||
allocation: "across",
|
||||
value: "400",
|
||||
target_rules: [
|
||||
{
|
||||
attribute: "product_category.id",
|
||||
operator: "eq",
|
||||
values: ["catg_cotton"],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
const result = await service.computeActions(["PROMOTION_TEST"], {
|
||||
customer: {
|
||||
customer_group: {
|
||||
id: "VIP",
|
||||
},
|
||||
},
|
||||
items: [
|
||||
{
|
||||
id: "item_cotton_tshirt",
|
||||
quantity: 2,
|
||||
unit_price: 100,
|
||||
product_category: {
|
||||
id: "catg_cotton",
|
||||
@@ -512,7 +633,7 @@ describe("Promotion Service: computeActions", () => {
|
||||
{
|
||||
id: "item_cotton_sweater",
|
||||
quantity: 2,
|
||||
unit_price: 150,
|
||||
unit_price: 300,
|
||||
product_category: {
|
||||
id: "catg_cotton",
|
||||
},
|
||||
@@ -527,13 +648,13 @@ describe("Promotion Service: computeActions", () => {
|
||||
{
|
||||
action: "addItemAdjustment",
|
||||
item_id: "item_cotton_tshirt",
|
||||
amount: 50,
|
||||
amount: 100,
|
||||
code: "PROMOTION_TEST",
|
||||
},
|
||||
{
|
||||
action: "addItemAdjustment",
|
||||
item_id: "item_cotton_sweater",
|
||||
amount: 150,
|
||||
amount: 300,
|
||||
code: "PROMOTION_TEST",
|
||||
},
|
||||
])
|
||||
@@ -556,7 +677,6 @@ describe("Promotion Service: computeActions", () => {
|
||||
target_type: "items",
|
||||
allocation: "across",
|
||||
value: "30",
|
||||
max_quantity: 2,
|
||||
target_rules: [
|
||||
{
|
||||
attribute: "product_category.id",
|
||||
@@ -584,7 +704,6 @@ describe("Promotion Service: computeActions", () => {
|
||||
target_type: "items",
|
||||
allocation: "across",
|
||||
value: "50",
|
||||
max_quantity: 1,
|
||||
target_rules: [
|
||||
{
|
||||
attribute: "product_category.id",
|
||||
@@ -676,7 +795,6 @@ describe("Promotion Service: computeActions", () => {
|
||||
target_type: "items",
|
||||
allocation: "across",
|
||||
value: "500",
|
||||
max_quantity: 2,
|
||||
target_rules: [
|
||||
{
|
||||
attribute: "product_category.id",
|
||||
@@ -704,7 +822,6 @@ describe("Promotion Service: computeActions", () => {
|
||||
target_type: "items",
|
||||
allocation: "across",
|
||||
value: "50",
|
||||
max_quantity: 1,
|
||||
target_rules: [
|
||||
{
|
||||
attribute: "product_category.id",
|
||||
@@ -778,6 +895,125 @@ describe("Promotion Service: computeActions", () => {
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
it("should compute budget exceeded action when applicable total exceeds campaign budget for type spend", async () => {
|
||||
await createCampaigns(repositoryManager)
|
||||
|
||||
const [createdPromotion] = await service.create([
|
||||
{
|
||||
code: "PROMOTION_TEST",
|
||||
type: PromotionType.STANDARD,
|
||||
rules: [
|
||||
{
|
||||
attribute: "customer.customer_group.id",
|
||||
operator: "in",
|
||||
values: ["VIP", "top100"],
|
||||
},
|
||||
],
|
||||
campaign_id: "campaign-id-1",
|
||||
application_method: {
|
||||
type: "fixed",
|
||||
target_type: "items",
|
||||
allocation: "across",
|
||||
value: "1500",
|
||||
target_rules: [
|
||||
{
|
||||
attribute: "product_category.id",
|
||||
operator: "eq",
|
||||
values: ["catg_cotton"],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
const result = await service.computeActions(["PROMOTION_TEST"], {
|
||||
customer: {
|
||||
customer_group: {
|
||||
id: "VIP",
|
||||
},
|
||||
},
|
||||
items: [
|
||||
{
|
||||
id: "item_cotton_tshirt",
|
||||
quantity: 5,
|
||||
unit_price: 1000,
|
||||
product_category: {
|
||||
id: "catg_cotton",
|
||||
},
|
||||
product: {
|
||||
id: "prod_tshirt",
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
expect(result).toEqual([
|
||||
{ action: "campaignBudgetExceeded", code: "PROMOTION_TEST" },
|
||||
])
|
||||
})
|
||||
|
||||
it("should compute budget exceeded action when applicable total exceeds campaign budget for type usage", async () => {
|
||||
await createCampaigns(repositoryManager)
|
||||
|
||||
const [createdPromotion] = await service.create([
|
||||
{
|
||||
code: "PROMOTION_TEST",
|
||||
type: PromotionType.STANDARD,
|
||||
rules: [
|
||||
{
|
||||
attribute: "customer.customer_group.id",
|
||||
operator: "in",
|
||||
values: ["VIP", "top100"],
|
||||
},
|
||||
],
|
||||
campaign_id: "campaign-id-2",
|
||||
application_method: {
|
||||
type: "fixed",
|
||||
target_type: "items",
|
||||
allocation: "across",
|
||||
value: "500",
|
||||
target_rules: [
|
||||
{
|
||||
attribute: "product_category.id",
|
||||
operator: "eq",
|
||||
values: ["catg_cotton"],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
await service.updateCampaigns({
|
||||
id: "campaign-id-2",
|
||||
budget: { used: 1000 },
|
||||
})
|
||||
|
||||
const result = await service.computeActions(["PROMOTION_TEST"], {
|
||||
customer: {
|
||||
customer_group: {
|
||||
id: "VIP",
|
||||
},
|
||||
},
|
||||
items: [
|
||||
{
|
||||
id: "item_cotton_tshirt",
|
||||
quantity: 5,
|
||||
unit_price: 1000,
|
||||
product_category: {
|
||||
id: "catg_cotton",
|
||||
},
|
||||
product: {
|
||||
id: "prod_tshirt",
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
expect(result).toEqual([
|
||||
{ action: "campaignBudgetExceeded", code: "PROMOTION_TEST" },
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe("when promotion is for shipping_method and allocation is each", () => {
|
||||
@@ -1076,6 +1312,119 @@ describe("Promotion Service: computeActions", () => {
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
it("should compute budget exceeded action when applicable total exceeds campaign budget for type spend", async () => {
|
||||
await createCampaigns(repositoryManager)
|
||||
|
||||
const [createdPromotion] = await service.create([
|
||||
{
|
||||
code: "PROMOTION_TEST",
|
||||
type: PromotionType.STANDARD,
|
||||
rules: [
|
||||
{
|
||||
attribute: "customer.customer_group.id",
|
||||
operator: "in",
|
||||
values: ["VIP", "top100"],
|
||||
},
|
||||
],
|
||||
campaign_id: "campaign-id-1",
|
||||
application_method: {
|
||||
type: "fixed",
|
||||
target_type: "shipping_methods",
|
||||
allocation: "each",
|
||||
value: "1200",
|
||||
max_quantity: 2,
|
||||
target_rules: [
|
||||
{
|
||||
attribute: "shipping_option.id",
|
||||
operator: "in",
|
||||
values: ["express", "standard"],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
const result = await service.computeActions(["PROMOTION_TEST"], {
|
||||
customer: {
|
||||
customer_group: {
|
||||
id: "VIP",
|
||||
},
|
||||
},
|
||||
shipping_methods: [
|
||||
{
|
||||
id: "shipping_method_express",
|
||||
unit_price: 1200,
|
||||
shipping_option: {
|
||||
id: "express",
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
expect(result).toEqual([
|
||||
{ action: "campaignBudgetExceeded", code: "PROMOTION_TEST" },
|
||||
])
|
||||
})
|
||||
|
||||
it("should compute budget exceeded action when applicable total exceeds campaign budget for type usage", async () => {
|
||||
await createCampaigns(repositoryManager)
|
||||
|
||||
const [createdPromotion] = await service.create([
|
||||
{
|
||||
code: "PROMOTION_TEST",
|
||||
type: PromotionType.STANDARD,
|
||||
rules: [
|
||||
{
|
||||
attribute: "customer.customer_group.id",
|
||||
operator: "in",
|
||||
values: ["VIP", "top100"],
|
||||
},
|
||||
],
|
||||
campaign_id: "campaign-id-2",
|
||||
application_method: {
|
||||
type: "fixed",
|
||||
target_type: "shipping_methods",
|
||||
allocation: "each",
|
||||
value: "1200",
|
||||
max_quantity: 2,
|
||||
target_rules: [
|
||||
{
|
||||
attribute: "shipping_option.id",
|
||||
operator: "in",
|
||||
values: ["express", "standard"],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
await service.updateCampaigns({
|
||||
id: "campaign-id-2",
|
||||
budget: { used: 1000 },
|
||||
})
|
||||
|
||||
const result = await service.computeActions(["PROMOTION_TEST"], {
|
||||
customer: {
|
||||
customer_group: {
|
||||
id: "VIP",
|
||||
},
|
||||
},
|
||||
shipping_methods: [
|
||||
{
|
||||
id: "shipping_method_express",
|
||||
unit_price: 1200,
|
||||
shipping_option: {
|
||||
id: "express",
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
expect(result).toEqual([
|
||||
{ action: "campaignBudgetExceeded", code: "PROMOTION_TEST" },
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe("when promotion is for shipping_method and allocation is across", () => {
|
||||
@@ -1096,7 +1445,6 @@ describe("Promotion Service: computeActions", () => {
|
||||
target_type: "shipping_methods",
|
||||
allocation: "across",
|
||||
value: "200",
|
||||
max_quantity: 2,
|
||||
target_rules: [
|
||||
{
|
||||
attribute: "shipping_option.id",
|
||||
@@ -1172,7 +1520,6 @@ describe("Promotion Service: computeActions", () => {
|
||||
target_type: "shipping_methods",
|
||||
allocation: "across",
|
||||
value: "200",
|
||||
max_quantity: 2,
|
||||
target_rules: [
|
||||
{
|
||||
attribute: "shipping_option.id",
|
||||
@@ -1200,7 +1547,6 @@ describe("Promotion Service: computeActions", () => {
|
||||
target_type: "shipping_methods",
|
||||
allocation: "across",
|
||||
value: "200",
|
||||
max_quantity: 2,
|
||||
target_rules: [
|
||||
{
|
||||
attribute: "shipping_option.id",
|
||||
@@ -1291,7 +1637,6 @@ describe("Promotion Service: computeActions", () => {
|
||||
target_type: "shipping_methods",
|
||||
allocation: "across",
|
||||
value: "1000",
|
||||
max_quantity: 2,
|
||||
target_rules: [
|
||||
{
|
||||
attribute: "shipping_option.id",
|
||||
@@ -1319,7 +1664,6 @@ describe("Promotion Service: computeActions", () => {
|
||||
target_type: "shipping_methods",
|
||||
allocation: "across",
|
||||
value: "200",
|
||||
max_quantity: 2,
|
||||
target_rules: [
|
||||
{
|
||||
attribute: "shipping_option.id",
|
||||
@@ -1380,6 +1724,117 @@ describe("Promotion Service: computeActions", () => {
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
it("should compute budget exceeded action when applicable total exceeds campaign budget for type spend", async () => {
|
||||
await createCampaigns(repositoryManager)
|
||||
|
||||
const [createdPromotion] = await service.create([
|
||||
{
|
||||
code: "PROMOTION_TEST",
|
||||
type: PromotionType.STANDARD,
|
||||
rules: [
|
||||
{
|
||||
attribute: "customer.customer_group.id",
|
||||
operator: "in",
|
||||
values: ["VIP", "top100"],
|
||||
},
|
||||
],
|
||||
campaign_id: "campaign-id-1",
|
||||
application_method: {
|
||||
type: "fixed",
|
||||
target_type: "shipping_methods",
|
||||
allocation: "across",
|
||||
value: "1200",
|
||||
target_rules: [
|
||||
{
|
||||
attribute: "shipping_option.id",
|
||||
operator: "in",
|
||||
values: ["express", "standard"],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
const result = await service.computeActions(["PROMOTION_TEST"], {
|
||||
customer: {
|
||||
customer_group: {
|
||||
id: "VIP",
|
||||
},
|
||||
},
|
||||
shipping_methods: [
|
||||
{
|
||||
id: "shipping_method_express",
|
||||
unit_price: 1200,
|
||||
shipping_option: {
|
||||
id: "express",
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
expect(result).toEqual([
|
||||
{ action: "campaignBudgetExceeded", code: "PROMOTION_TEST" },
|
||||
])
|
||||
})
|
||||
|
||||
it("should compute budget exceeded action when applicable total exceeds campaign budget for type usage", async () => {
|
||||
await createCampaigns(repositoryManager)
|
||||
|
||||
const [createdPromotion] = await service.create([
|
||||
{
|
||||
code: "PROMOTION_TEST",
|
||||
type: PromotionType.STANDARD,
|
||||
rules: [
|
||||
{
|
||||
attribute: "customer.customer_group.id",
|
||||
operator: "in",
|
||||
values: ["VIP", "top100"],
|
||||
},
|
||||
],
|
||||
campaign_id: "campaign-id-2",
|
||||
application_method: {
|
||||
type: "fixed",
|
||||
target_type: "shipping_methods",
|
||||
allocation: "across",
|
||||
value: "1200",
|
||||
target_rules: [
|
||||
{
|
||||
attribute: "shipping_option.id",
|
||||
operator: "in",
|
||||
values: ["express", "standard"],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
await service.updateCampaigns({
|
||||
id: "campaign-id-2",
|
||||
budget: { used: 1000 },
|
||||
})
|
||||
|
||||
const result = await service.computeActions(["PROMOTION_TEST"], {
|
||||
customer: {
|
||||
customer_group: {
|
||||
id: "VIP",
|
||||
},
|
||||
},
|
||||
shipping_methods: [
|
||||
{
|
||||
id: "shipping_method_express",
|
||||
unit_price: 1200,
|
||||
shipping_option: {
|
||||
id: "express",
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
expect(result).toEqual([
|
||||
{ action: "campaignBudgetExceeded", code: "PROMOTION_TEST" },
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe("when promotion is for the entire order", () => {
|
||||
@@ -1740,6 +2195,7 @@ describe("Promotion Service: computeActions", () => {
|
||||
{
|
||||
action: "removeItemAdjustment",
|
||||
adjustment_id: "test-adjustment",
|
||||
code: "ADJUSTMENT_CODE",
|
||||
},
|
||||
{
|
||||
action: "addItemAdjustment",
|
||||
@@ -1780,7 +2236,6 @@ describe("Promotion Service: computeActions", () => {
|
||||
target_type: "shipping_methods",
|
||||
allocation: "across",
|
||||
value: "200",
|
||||
max_quantity: 2,
|
||||
target_rules: [
|
||||
{
|
||||
attribute: "shipping_option.id",
|
||||
@@ -1833,6 +2288,7 @@ describe("Promotion Service: computeActions", () => {
|
||||
{
|
||||
action: "removeShippingMethodAdjustment",
|
||||
adjustment_id: "test-adjustment",
|
||||
code: "ADJUSTMENT_CODE",
|
||||
},
|
||||
{
|
||||
action: "addShippingMethodAdjustment",
|
||||
|
||||
@@ -0,0 +1,199 @@
|
||||
import { IPromotionModuleService } from "@medusajs/types"
|
||||
import { SqlEntityManager } from "@mikro-orm/postgresql"
|
||||
import { initialize } from "../../../../src"
|
||||
import { createCampaigns } from "../../../__fixtures__/campaigns"
|
||||
import { DB_URL, MikroOrmWrapper } from "../../../utils"
|
||||
|
||||
jest.setTimeout(30000)
|
||||
|
||||
describe("Promotion Service: campaign usage", () => {
|
||||
let service: IPromotionModuleService
|
||||
let repositoryManager: SqlEntityManager
|
||||
|
||||
beforeEach(async () => {
|
||||
await MikroOrmWrapper.setupDatabase()
|
||||
repositoryManager = MikroOrmWrapper.forkManager()
|
||||
|
||||
await createCampaigns(repositoryManager)
|
||||
|
||||
service = await initialize({
|
||||
database: {
|
||||
clientUrl: DB_URL,
|
||||
schema: process.env.MEDUSA_PROMOTION_DB_SCHEMA,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
await MikroOrmWrapper.clearDatabase()
|
||||
})
|
||||
|
||||
describe("registerUsage", () => {
|
||||
it("should register usage for type spend", async () => {
|
||||
const createdPromotion = await service.create({
|
||||
code: "TEST_PROMO_SPEND",
|
||||
type: "standard",
|
||||
campaign_id: "campaign-id-1",
|
||||
})
|
||||
|
||||
await service.registerUsage([
|
||||
{
|
||||
action: "addShippingMethodAdjustment",
|
||||
shipping_method_id: "shipping_method_express",
|
||||
amount: 200,
|
||||
code: createdPromotion.code!,
|
||||
},
|
||||
{
|
||||
action: "addShippingMethodAdjustment",
|
||||
shipping_method_id: "shipping_method_standard",
|
||||
amount: 500,
|
||||
code: createdPromotion.code!,
|
||||
},
|
||||
])
|
||||
|
||||
const campaign = await service.retrieveCampaign("campaign-id-1", {
|
||||
relations: ["budget"],
|
||||
})
|
||||
|
||||
expect(campaign.budget).toEqual(
|
||||
expect.objectContaining({
|
||||
type: "spend",
|
||||
limit: 1000,
|
||||
used: 700,
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("should register usage for type usage", async () => {
|
||||
const createdPromotion = await service.create({
|
||||
code: "TEST_PROMO_USAGE",
|
||||
type: "standard",
|
||||
campaign_id: "campaign-id-2",
|
||||
})
|
||||
|
||||
await service.registerUsage([
|
||||
{
|
||||
action: "addShippingMethodAdjustment",
|
||||
shipping_method_id: "shipping_method_express",
|
||||
amount: 200,
|
||||
code: createdPromotion.code!,
|
||||
},
|
||||
{
|
||||
action: "addShippingMethodAdjustment",
|
||||
shipping_method_id: "shipping_method_standard",
|
||||
amount: 500,
|
||||
code: createdPromotion.code!,
|
||||
},
|
||||
])
|
||||
|
||||
const campaign = await service.retrieveCampaign("campaign-id-2", {
|
||||
relations: ["budget"],
|
||||
})
|
||||
|
||||
expect(campaign.budget).toEqual(
|
||||
expect.objectContaining({
|
||||
type: "usage",
|
||||
limit: 1000,
|
||||
used: 1,
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("should not throw an error when compute action with code does not exist", async () => {
|
||||
const response = await service
|
||||
.registerUsage([
|
||||
{
|
||||
action: "addShippingMethodAdjustment",
|
||||
shipping_method_id: "shipping_method_express",
|
||||
amount: 200,
|
||||
code: "DOESNOTEXIST",
|
||||
},
|
||||
])
|
||||
.catch((e) => e)
|
||||
|
||||
expect(response).toEqual(undefined)
|
||||
})
|
||||
|
||||
it("should not register usage when limit is exceed for type usage", async () => {
|
||||
const createdPromotion = await service.create({
|
||||
code: "TEST_PROMO_USAGE",
|
||||
type: "standard",
|
||||
campaign_id: "campaign-id-2",
|
||||
})
|
||||
|
||||
await service.updateCampaigns({
|
||||
id: "campaign-id-2",
|
||||
budget: { used: 1000, limit: 1000 },
|
||||
})
|
||||
|
||||
await service.registerUsage([
|
||||
{
|
||||
action: "addShippingMethodAdjustment",
|
||||
shipping_method_id: "shipping_method_express",
|
||||
amount: 200,
|
||||
code: createdPromotion.code!,
|
||||
},
|
||||
{
|
||||
action: "addShippingMethodAdjustment",
|
||||
shipping_method_id: "shipping_method_standard",
|
||||
amount: 500,
|
||||
code: createdPromotion.code!,
|
||||
},
|
||||
])
|
||||
|
||||
const campaign = await service.retrieveCampaign("campaign-id-2", {
|
||||
relations: ["budget"],
|
||||
})
|
||||
|
||||
expect(campaign).toEqual(
|
||||
expect.objectContaining({
|
||||
budget: expect.objectContaining({
|
||||
limit: 1000,
|
||||
used: 1000,
|
||||
}),
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("should not register usage above limit when exceeded for type spend", async () => {
|
||||
const createdPromotion = await service.create({
|
||||
code: "TEST_PROMO_SPEND",
|
||||
type: "standard",
|
||||
campaign_id: "campaign-id-1",
|
||||
})
|
||||
|
||||
await service.updateCampaigns({
|
||||
id: "campaign-id-1",
|
||||
budget: { used: 900, limit: 1000 },
|
||||
})
|
||||
|
||||
await service.registerUsage([
|
||||
{
|
||||
action: "addShippingMethodAdjustment",
|
||||
shipping_method_id: "shipping_method_express",
|
||||
amount: 100,
|
||||
code: createdPromotion.code!,
|
||||
},
|
||||
{
|
||||
action: "addShippingMethodAdjustment",
|
||||
shipping_method_id: "shipping_method_standard",
|
||||
amount: 100,
|
||||
code: createdPromotion.code!,
|
||||
},
|
||||
])
|
||||
|
||||
const campaign = await service.retrieveCampaign("campaign-id-1", {
|
||||
relations: ["budget"],
|
||||
})
|
||||
|
||||
expect(campaign).toEqual(
|
||||
expect.objectContaining({
|
||||
budget: expect.objectContaining({
|
||||
limit: 1000,
|
||||
used: 1000,
|
||||
}),
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -73,12 +73,14 @@ export class CampaignRepository extends DALUtils.mikroOrmBaseRepositoryFactory<
|
||||
const campaignPromotionIdsMap = new Map<string, string[]>()
|
||||
|
||||
data.forEach((campaignData) => {
|
||||
const campaignPromotionIds =
|
||||
campaignData.promotions?.map((p) => p.id) || []
|
||||
const campaignPromotionIds = campaignData.promotions?.map((p) => p.id)
|
||||
|
||||
campaignIds.push(campaignData.id)
|
||||
promotionIdsToUpsert.push(...campaignPromotionIds)
|
||||
campaignPromotionIdsMap.set(campaignData.id, campaignPromotionIds)
|
||||
|
||||
if (campaignPromotionIds) {
|
||||
promotionIdsToUpsert.push(...campaignPromotionIds)
|
||||
campaignPromotionIdsMap.set(campaignData.id, campaignPromotionIds)
|
||||
}
|
||||
|
||||
delete campaignData.promotions
|
||||
})
|
||||
@@ -109,8 +111,12 @@ export class CampaignRepository extends DALUtils.mikroOrmBaseRepositoryFactory<
|
||||
const updatedCampaigns = await super.update(data, context)
|
||||
|
||||
for (const updatedCampaign of updatedCampaigns) {
|
||||
const upsertPromotionIds =
|
||||
campaignPromotionIdsMap.get(updatedCampaign.id) || []
|
||||
const upsertPromotionIds = campaignPromotionIdsMap.get(updatedCampaign.id)
|
||||
|
||||
if (!upsertPromotionIds) {
|
||||
continue
|
||||
}
|
||||
|
||||
const existingPromotionIds = (
|
||||
existingCampaignsMap.get(updatedCampaign.id)?.promotions || []
|
||||
).map((p) => p.id)
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
} from "@medusajs/types"
|
||||
import {
|
||||
ApplicationMethodTargetType,
|
||||
CampaignBudgetType,
|
||||
InjectManager,
|
||||
InjectTransactionManager,
|
||||
MedusaContext,
|
||||
@@ -90,6 +91,110 @@ export default class PromotionModuleService<
|
||||
return joinerConfig
|
||||
}
|
||||
|
||||
@InjectManager("baseRepository_")
|
||||
async registerUsage(
|
||||
computedActions: PromotionTypes.UsageComputedActions[],
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): Promise<void> {
|
||||
const promotionCodes = computedActions
|
||||
.map((computedAction) => computedAction.code)
|
||||
.filter(Boolean)
|
||||
|
||||
const promotionCodeCampaignBudgetMap = new Map<
|
||||
string,
|
||||
UpdateCampaignBudgetDTO
|
||||
>()
|
||||
const promotionCodeUsageMap = new Map<string, boolean>()
|
||||
|
||||
const existingPromotions = await this.list(
|
||||
{ code: promotionCodes },
|
||||
{ relations: ["application_method", "campaign", "campaign.budget"] },
|
||||
sharedContext
|
||||
)
|
||||
|
||||
const existingPromotionsMap = new Map<string, PromotionTypes.PromotionDTO>(
|
||||
existingPromotions.map((promotion) => [promotion.code!, promotion])
|
||||
)
|
||||
|
||||
for (let computedAction of computedActions) {
|
||||
if (!ComputeActionUtils.canRegisterUsage(computedAction)) {
|
||||
continue
|
||||
}
|
||||
|
||||
const promotion = existingPromotionsMap.get(computedAction.code)
|
||||
|
||||
if (!promotion) {
|
||||
continue
|
||||
}
|
||||
|
||||
const campaignBudget = promotion.campaign?.budget
|
||||
|
||||
if (!campaignBudget) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (campaignBudget.type === CampaignBudgetType.SPEND) {
|
||||
const campaignBudgetData = promotionCodeCampaignBudgetMap.get(
|
||||
campaignBudget.id
|
||||
) || { id: campaignBudget.id, used: campaignBudget.used || 0 }
|
||||
|
||||
campaignBudgetData.used =
|
||||
(campaignBudgetData.used || 0) + computedAction.amount
|
||||
|
||||
if (
|
||||
campaignBudget.limit &&
|
||||
campaignBudgetData.used > campaignBudget.limit
|
||||
) {
|
||||
continue
|
||||
}
|
||||
|
||||
promotionCodeCampaignBudgetMap.set(
|
||||
campaignBudget.id,
|
||||
campaignBudgetData
|
||||
)
|
||||
}
|
||||
|
||||
if (campaignBudget.type === CampaignBudgetType.USAGE) {
|
||||
const promotionAlreadyUsed =
|
||||
promotionCodeUsageMap.get(promotion.code!) || false
|
||||
|
||||
if (promotionAlreadyUsed) {
|
||||
continue
|
||||
}
|
||||
|
||||
const campaignBudgetData = {
|
||||
id: campaignBudget.id,
|
||||
used: (campaignBudget.used || 0) + 1,
|
||||
}
|
||||
|
||||
if (
|
||||
campaignBudget.limit &&
|
||||
campaignBudgetData.used > campaignBudget.limit
|
||||
) {
|
||||
continue
|
||||
}
|
||||
|
||||
promotionCodeCampaignBudgetMap.set(
|
||||
campaignBudget.id,
|
||||
campaignBudgetData
|
||||
)
|
||||
|
||||
promotionCodeUsageMap.set(promotion.code!, true)
|
||||
}
|
||||
|
||||
const campaignBudgetsData: UpdateCampaignBudgetDTO[] = []
|
||||
|
||||
for (const [_, campaignBudgetData] of promotionCodeCampaignBudgetMap) {
|
||||
campaignBudgetsData.push(campaignBudgetData)
|
||||
}
|
||||
|
||||
await this.campaignBudgetService_.update(
|
||||
campaignBudgetsData,
|
||||
sharedContext
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
async computeActions(
|
||||
promotionCodesToApply: string[],
|
||||
applicationContext: PromotionTypes.ComputeActionContext,
|
||||
@@ -140,6 +245,8 @@ export default class PromotionModuleService<
|
||||
"application_method.target_rules.values",
|
||||
"rules",
|
||||
"rules.values",
|
||||
"campaign",
|
||||
"campaign.budget",
|
||||
],
|
||||
}
|
||||
)
|
||||
@@ -166,6 +273,7 @@ export default class PromotionModuleService<
|
||||
computedActions.push({
|
||||
action: "removeItemAdjustment",
|
||||
adjustment_id: codeAdjustmentMap.get(appliedCode)!.id,
|
||||
code: appliedCode,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -173,6 +281,7 @@ export default class PromotionModuleService<
|
||||
computedActions.push({
|
||||
action: "removeShippingMethodAdjustment",
|
||||
adjustment_id: codeAdjustmentMap.get(appliedCode)!.id,
|
||||
code: appliedCode,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -544,6 +653,7 @@ export default class PromotionModuleService<
|
||||
!allowedAllocationForQuantity.includes(applicationMethodData.allocation)
|
||||
) {
|
||||
applicationMethodData.max_quantity = null
|
||||
existingApplicationMethod.max_quantity = null
|
||||
}
|
||||
|
||||
validateApplicationMethodAttributes({
|
||||
@@ -952,12 +1062,8 @@ export default class PromotionModuleService<
|
||||
const campaignBudgetsData: UpdateCampaignBudgetDTO[] = []
|
||||
|
||||
const existingCampaigns = await this.listCampaigns(
|
||||
{
|
||||
id: campaignIds,
|
||||
},
|
||||
{
|
||||
relations: ["budget"],
|
||||
},
|
||||
{ id: campaignIds },
|
||||
{ relations: ["budget"] },
|
||||
sharedContext
|
||||
)
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
export * from "./items"
|
||||
export * from "./order"
|
||||
export * from "./shipping-methods"
|
||||
export * from "./usage"
|
||||
|
||||
@@ -5,9 +5,11 @@ import {
|
||||
import {
|
||||
ApplicationMethodAllocation,
|
||||
ApplicationMethodTargetType,
|
||||
ComputedActions,
|
||||
MedusaError,
|
||||
} from "@medusajs/utils"
|
||||
import { areRulesValidForContext } from "../validations"
|
||||
import { computeActionForBudgetExceeded } from "./usage"
|
||||
|
||||
export function getComputedActionsForItems(
|
||||
promotion: PromotionTypes.PromotionDTO,
|
||||
@@ -61,22 +63,35 @@ export function applyPromotionToItems(
|
||||
) {
|
||||
for (const method of items!) {
|
||||
const appliedPromoValue = methodIdPromoValueMap.get(method.id) || 0
|
||||
const promotionValue = parseFloat(applicationMethod!.value!)
|
||||
const quantityMultiplier = Math.min(
|
||||
method.quantity,
|
||||
applicationMethod?.max_quantity!
|
||||
)
|
||||
const promotionValue =
|
||||
parseFloat(applicationMethod!.value!) * quantityMultiplier
|
||||
const applicableTotal =
|
||||
method.unit_price *
|
||||
Math.min(method.quantity, applicationMethod?.max_quantity!) -
|
||||
appliedPromoValue
|
||||
|
||||
method.unit_price * quantityMultiplier - appliedPromoValue
|
||||
const amount = Math.min(promotionValue, applicableTotal)
|
||||
|
||||
if (amount <= 0) {
|
||||
continue
|
||||
}
|
||||
|
||||
const budgetExceededAction = computeActionForBudgetExceeded(
|
||||
promotion,
|
||||
amount
|
||||
)
|
||||
|
||||
if (budgetExceededAction) {
|
||||
computedActions.push(budgetExceededAction)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
methodIdPromoValueMap.set(method.id, appliedPromoValue + amount)
|
||||
|
||||
computedActions.push({
|
||||
action: "addItemAdjustment",
|
||||
action: ComputedActions.ADD_ITEM_ADJUSTMENT,
|
||||
item_id: method.id,
|
||||
amount,
|
||||
code: promotion.code!,
|
||||
@@ -91,35 +106,37 @@ export function applyPromotionToItems(
|
||||
) {
|
||||
const totalApplicableValue = items!.reduce((acc, method) => {
|
||||
const appliedPromoValue = methodIdPromoValueMap.get(method.id) || 0
|
||||
return (
|
||||
acc +
|
||||
method.unit_price *
|
||||
Math.min(method.quantity, applicationMethod?.max_quantity!) -
|
||||
appliedPromoValue
|
||||
)
|
||||
return acc + method.unit_price * method.quantity - appliedPromoValue
|
||||
}, 0)
|
||||
|
||||
for (const method of items!) {
|
||||
const promotionValue = parseFloat(applicationMethod!.value!)
|
||||
const appliedPromoValue = methodIdPromoValueMap.get(method.id) || 0
|
||||
|
||||
const applicableTotal =
|
||||
method.unit_price *
|
||||
Math.min(method.quantity, applicationMethod?.max_quantity!) -
|
||||
appliedPromoValue
|
||||
method.unit_price * method.quantity - appliedPromoValue
|
||||
|
||||
// TODO: should we worry about precision here?
|
||||
const applicablePromotionValue =
|
||||
(applicableTotal / totalApplicableValue) * promotionValue
|
||||
|
||||
const amount = Math.min(applicablePromotionValue, applicableTotal)
|
||||
|
||||
if (amount <= 0) {
|
||||
continue
|
||||
}
|
||||
|
||||
const budgetExceededAction = computeActionForBudgetExceeded(
|
||||
promotion,
|
||||
amount
|
||||
)
|
||||
|
||||
if (budgetExceededAction) {
|
||||
computedActions.push(budgetExceededAction)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
computedActions.push({
|
||||
action: "addItemAdjustment",
|
||||
action: ComputedActions.ADD_ITEM_ADJUSTMENT,
|
||||
item_id: method.id,
|
||||
amount,
|
||||
code: promotion.code!,
|
||||
|
||||
@@ -2,9 +2,11 @@ import { PromotionTypes } from "@medusajs/types"
|
||||
import {
|
||||
ApplicationMethodAllocation,
|
||||
ApplicationMethodTargetType,
|
||||
ComputedActions,
|
||||
MedusaError,
|
||||
} from "@medusajs/utils"
|
||||
import { areRulesValidForContext } from "../validations"
|
||||
import { computeActionForBudgetExceeded } from "./usage"
|
||||
|
||||
export function getComputedActionsForShippingMethods(
|
||||
promotion: PromotionTypes.PromotionDTO,
|
||||
@@ -61,10 +63,21 @@ export function applyPromotionToShippingMethods(
|
||||
continue
|
||||
}
|
||||
|
||||
const budgetExceededAction = computeActionForBudgetExceeded(
|
||||
promotion,
|
||||
amount
|
||||
)
|
||||
|
||||
if (budgetExceededAction) {
|
||||
computedActions.push(budgetExceededAction)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
methodIdPromoValueMap.set(method.id, appliedPromoValue + amount)
|
||||
|
||||
computedActions.push({
|
||||
action: "addShippingMethodAdjustment",
|
||||
action: ComputedActions.ADD_SHIPPING_METHOD_ADJUSTMENT,
|
||||
shipping_method_id: method.id,
|
||||
amount,
|
||||
code: promotion.code!,
|
||||
@@ -99,10 +112,21 @@ export function applyPromotionToShippingMethods(
|
||||
continue
|
||||
}
|
||||
|
||||
const budgetExceededAction = computeActionForBudgetExceeded(
|
||||
promotion,
|
||||
amount
|
||||
)
|
||||
|
||||
if (budgetExceededAction) {
|
||||
computedActions.push(budgetExceededAction)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
methodIdPromoValueMap.set(method.id, appliedPromoValue + amount)
|
||||
|
||||
computedActions.push({
|
||||
action: "addShippingMethodAdjustment",
|
||||
action: ComputedActions.ADD_SHIPPING_METHOD_ADJUSTMENT,
|
||||
shipping_method_id: method.id,
|
||||
amount,
|
||||
code: promotion.code!,
|
||||
|
||||
39
packages/promotion/src/utils/compute-actions/usage.ts
Normal file
39
packages/promotion/src/utils/compute-actions/usage.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import {
|
||||
CampaignBudgetExceededAction,
|
||||
ComputeActions,
|
||||
PromotionDTO,
|
||||
} from "@medusajs/types"
|
||||
import { CampaignBudgetType, ComputedActions } from "@medusajs/utils"
|
||||
|
||||
export function canRegisterUsage(computedAction: ComputeActions): boolean {
|
||||
return (
|
||||
[
|
||||
ComputedActions.ADD_ITEM_ADJUSTMENT,
|
||||
ComputedActions.ADD_SHIPPING_METHOD_ADJUSTMENT,
|
||||
] as string[]
|
||||
).includes(computedAction.action)
|
||||
}
|
||||
|
||||
export function computeActionForBudgetExceeded(
|
||||
promotion: PromotionDTO,
|
||||
amount: number
|
||||
): CampaignBudgetExceededAction | void {
|
||||
const campaignBudget = promotion.campaign?.budget
|
||||
|
||||
if (!campaignBudget) {
|
||||
return
|
||||
}
|
||||
|
||||
const campaignBudgetUsed = campaignBudget.used || 0
|
||||
const totalUsed =
|
||||
campaignBudget.type === CampaignBudgetType.SPEND
|
||||
? campaignBudgetUsed + amount
|
||||
: campaignBudgetUsed + 1
|
||||
|
||||
if (campaignBudget.limit && totalUsed > campaignBudget.limit) {
|
||||
return {
|
||||
action: ComputedActions.CAMPAIGN_BUDGET_EXCEEDED,
|
||||
code: promotion.code!,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
ApplicationMethodType,
|
||||
MedusaError,
|
||||
isDefined,
|
||||
isPresent,
|
||||
} from "@medusajs/utils"
|
||||
|
||||
export const allowedAllocationTargetTypes: string[] = [
|
||||
@@ -33,6 +34,16 @@ export function validateApplicationMethodAttributes(data: {
|
||||
}) {
|
||||
const allTargetTypes: string[] = Object.values(ApplicationMethodTargetType)
|
||||
|
||||
if (
|
||||
data.allocation === ApplicationMethodAllocation.ACROSS &&
|
||||
isPresent(data.max_quantity)
|
||||
) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`application_method.max_quantity is not allowed to be set for allocation (${ApplicationMethodAllocation.ACROSS})`
|
||||
)
|
||||
}
|
||||
|
||||
if (!allTargetTypes.includes(data.target_type)) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
|
||||
@@ -5,8 +5,8 @@ export type CampaignBudgetTypeValues = "spend" | "usage"
|
||||
export interface CampaignBudgetDTO {
|
||||
id: string
|
||||
type?: CampaignBudgetTypeValues
|
||||
limit?: string | null
|
||||
used?: string
|
||||
limit?: number | null
|
||||
used?: number
|
||||
}
|
||||
|
||||
export interface FilterableCampaignBudgetProps
|
||||
|
||||
@@ -3,6 +3,16 @@ export type ComputeActions =
|
||||
| RemoveItemAdjustmentAction
|
||||
| AddShippingMethodAdjustment
|
||||
| RemoveShippingMethodAdjustment
|
||||
| CampaignBudgetExceededAction
|
||||
|
||||
export type UsageComputedActions =
|
||||
| AddShippingMethodAdjustment
|
||||
| AddItemAdjustmentAction
|
||||
|
||||
export interface CampaignBudgetExceededAction {
|
||||
action: "campaignBudgetExceeded"
|
||||
code: string
|
||||
}
|
||||
|
||||
export interface AddItemAdjustmentAction {
|
||||
action: "addItemAdjustment"
|
||||
@@ -16,6 +26,7 @@ export interface RemoveItemAdjustmentAction {
|
||||
action: "removeItemAdjustment"
|
||||
adjustment_id: string
|
||||
description?: string
|
||||
code: string
|
||||
}
|
||||
|
||||
export interface AddShippingMethodAdjustment {
|
||||
@@ -29,6 +40,7 @@ export interface AddShippingMethodAdjustment {
|
||||
export interface RemoveShippingMethodAdjustment {
|
||||
action: "removeShippingMethodAdjustment"
|
||||
adjustment_id: string
|
||||
code: string
|
||||
}
|
||||
|
||||
export interface ComputeActionAdjustmentLine extends Record<string, unknown> {
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
CreateApplicationMethodDTO,
|
||||
UpdateApplicationMethodDTO,
|
||||
} from "./application-method"
|
||||
import { CampaignDTO } from "./campaign"
|
||||
import { CreatePromotionRuleDTO, PromotionRuleDTO } from "./promotion-rule"
|
||||
|
||||
export type PromotionType = "standard" | "buyget"
|
||||
@@ -16,6 +17,7 @@ export interface PromotionDTO {
|
||||
is_automatic?: boolean
|
||||
application_method?: ApplicationMethodDTO
|
||||
rules?: PromotionRuleDTO[]
|
||||
campaign?: CampaignDTO
|
||||
}
|
||||
|
||||
export interface CreatePromotionDTO {
|
||||
|
||||
@@ -17,6 +17,8 @@ import {
|
||||
import { CreateCampaignDTO, UpdateCampaignDTO } from "./mutations"
|
||||
|
||||
export interface IPromotionModuleService extends IModuleService {
|
||||
registerUsage(computedActions: ComputeActions[]): Promise<void>
|
||||
|
||||
computeActions(
|
||||
promotionCodesToApply: string[],
|
||||
applicationContext: ComputeActionContext,
|
||||
|
||||
@@ -33,3 +33,11 @@ export enum CampaignBudgetType {
|
||||
SPEND = "spend",
|
||||
USAGE = "usage",
|
||||
}
|
||||
|
||||
export enum ComputedActions {
|
||||
ADD_ITEM_ADJUSTMENT = "addItemAdjustment",
|
||||
ADD_SHIPPING_METHOD_ADJUSTMENT = "addShippingMethodAdjustment",
|
||||
REMOVE_ITEM_ADJUSTMENT = "removeItemAdjustment",
|
||||
REMOVE_SHIPPING_METHOD_ADJUSTMENT = "removeShippingMethodAdjustment",
|
||||
CAMPAIGN_BUDGET_EXCEEDED = "campaignBudgetExceeded",
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user