feat(types): added computed actions for automatic promotions (#6272)

what:

- compute actions account for automatic promotions (RESOLVES CORE-1701)

RESOLVES CORE-1689
This commit is contained in:
Riqwan Thamir
2024-01-31 12:30:33 +00:00
committed by GitHub
parent 7903a15e0f
commit e749dd653c
4 changed files with 470 additions and 6 deletions
+5
View File
@@ -0,0 +1,5 @@
---
"@medusajs/types": patch
---
feat(types): added computed actions for automatic promotions
@@ -1,11 +1,11 @@
import { Modules } from "@medusajs/modules-sdk"
import { IPromotionModuleService } from "@medusajs/types"
import { PromotionType } from "@medusajs/utils"
import { SqlEntityManager } from "@mikro-orm/postgresql"
import { initModules } from "medusa-test-utils"
import { createCampaigns } from "../../../__fixtures__/campaigns"
import { MikroOrmWrapper } from "../../../utils"
import { getInitModuleConfig } from "../../../utils/get-init-module-config"
import { Modules } from "@medusajs/modules-sdk"
import { initModules } from "medusa-test-utils"
jest.setTimeout(30000)
@@ -239,6 +239,84 @@ describe("Promotion Service: computeActions", () => {
])
})
it("should compute the correct item amendments when promotion is automatic", async () => {
const [createdPromotion] = await service.create([
{
code: "PROMOTION_TEST",
is_automatic: true,
type: PromotionType.STANDARD,
rules: [
{
attribute: "customer.customer_group.id",
operator: "in",
values: ["VIP", "top100"],
},
],
application_method: {
type: "fixed",
target_type: "items",
allocation: "each",
value: "200",
max_quantity: 1,
target_rules: [
{
attribute: "product_category.id",
operator: "eq",
values: ["catg_cotton"],
},
],
},
},
])
const result = await service.computeActions([], {
customer: {
customer_group: {
id: "VIP",
},
},
items: [
{
id: "item_cotton_tshirt",
quantity: 1,
unit_price: 100,
product_category: {
id: "catg_cotton",
},
product: {
id: "prod_tshirt",
},
},
{
id: "item_cotton_sweater",
quantity: 5,
unit_price: 150,
product_category: {
id: "catg_cotton",
},
product: {
id: "prod_sweater",
},
},
],
})
expect(result).toEqual([
{
action: "addItemAdjustment",
item_id: "item_cotton_tshirt",
amount: 100,
code: "PROMOTION_TEST",
},
{
action: "addItemAdjustment",
item_id: "item_cotton_sweater",
amount: 150,
code: "PROMOTION_TEST",
},
])
})
it("should compute the correct item amendments when there are multiple promotions to apply", async () => {
const [createdPromotion] = await service.create([
{
@@ -666,6 +744,83 @@ describe("Promotion Service: computeActions", () => {
])
})
it("should compute the correct item amendments when promotion is automatic", async () => {
const [createdPromotion] = await service.create([
{
code: "PROMOTION_TEST",
type: PromotionType.STANDARD,
is_automatic: true,
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([], {
customer: {
customer_group: {
id: "VIP",
},
},
items: [
{
id: "item_cotton_tshirt",
quantity: 2,
unit_price: 100,
product_category: {
id: "catg_cotton",
},
product: {
id: "prod_tshirt",
},
},
{
id: "item_cotton_sweater",
quantity: 2,
unit_price: 300,
product_category: {
id: "catg_cotton",
},
product: {
id: "prod_sweater",
},
},
],
})
expect(result).toEqual([
{
action: "addItemAdjustment",
item_id: "item_cotton_tshirt",
amount: 100,
code: "PROMOTION_TEST",
},
{
action: "addItemAdjustment",
item_id: "item_cotton_sweater",
amount: 300,
code: "PROMOTION_TEST",
},
])
})
it("should compute the correct item amendments when there are multiple promotions to apply", async () => {
const [createdPromotion] = await service.create([
{
@@ -1099,6 +1254,151 @@ describe("Promotion Service: computeActions", () => {
])
})
it("should compute the correct shipping_method amendments when promotion is automatic", async () => {
const [createdPromotion] = await service.create([
{
code: "PROMOTION_TEST",
type: PromotionType.STANDARD,
is_automatic: true,
rules: [
{
attribute: "customer.customer_group.id",
operator: "in",
values: ["VIP", "top100"],
},
],
application_method: {
type: "fixed",
target_type: "shipping_methods",
allocation: "each",
value: "200",
max_quantity: 2,
target_rules: [
{
attribute: "shipping_option.id",
operator: "in",
values: ["express", "standard"],
},
],
},
},
])
const result = await service.computeActions([], {
customer: {
customer_group: {
id: "VIP",
},
},
shipping_methods: [
{
id: "shipping_method_express",
unit_price: 250,
shipping_option: {
id: "express",
},
},
{
id: "shipping_method_standard",
unit_price: 150,
shipping_option: {
id: "standard",
},
},
{
id: "shipping_method_snail",
unit_price: 200,
shipping_option: {
id: "snail",
},
},
],
})
expect(result).toEqual([
{
action: "addShippingMethodAdjustment",
shipping_method_id: "shipping_method_express",
amount: 200,
code: "PROMOTION_TEST",
},
{
action: "addShippingMethodAdjustment",
shipping_method_id: "shipping_method_standard",
amount: 150,
code: "PROMOTION_TEST",
},
])
})
it("should compute the correct shipping_method amendments when promotion is automatic and prevent_auto_promotions is false", async () => {
const [createdPromotion] = await service.create([
{
code: "PROMOTION_TEST",
type: PromotionType.STANDARD,
is_automatic: true,
rules: [
{
attribute: "customer.customer_group.id",
operator: "in",
values: ["VIP", "top100"],
},
],
application_method: {
type: "fixed",
target_type: "shipping_methods",
allocation: "each",
value: "200",
max_quantity: 2,
target_rules: [
{
attribute: "shipping_option.id",
operator: "in",
values: ["express", "standard"],
},
],
},
},
])
const result = await service.computeActions(
[],
{
customer: {
customer_group: {
id: "VIP",
},
},
shipping_methods: [
{
id: "shipping_method_express",
unit_price: 250,
shipping_option: {
id: "express",
},
},
{
id: "shipping_method_standard",
unit_price: 150,
shipping_option: {
id: "standard",
},
},
{
id: "shipping_method_snail",
unit_price: 200,
shipping_option: {
id: "snail",
},
},
],
},
{ prevent_auto_promotions: true }
)
expect(result).toEqual([])
})
it("should compute the correct item amendments when there are multiple promotions to apply", async () => {
const [createdPromotion] = await service.create([
{
@@ -1509,6 +1809,82 @@ describe("Promotion Service: computeActions", () => {
])
})
it("should compute the correct shipping_method amendments when promotion is automatic", async () => {
const [createdPromotion] = await service.create([
{
code: "PROMOTION_TEST",
type: PromotionType.STANDARD,
is_automatic: true,
rules: [
{
attribute: "customer.customer_group.id",
operator: "in",
values: ["VIP", "top100"],
},
],
application_method: {
type: "fixed",
target_type: "shipping_methods",
allocation: "across",
value: "200",
target_rules: [
{
attribute: "shipping_option.id",
operator: "in",
values: ["express", "standard"],
},
],
},
},
])
const result = await service.computeActions([], {
customer: {
customer_group: {
id: "VIP",
},
},
shipping_methods: [
{
id: "shipping_method_express",
unit_price: 500,
shipping_option: {
id: "express",
},
},
{
id: "shipping_method_standard",
unit_price: 100,
shipping_option: {
id: "standard",
},
},
{
id: "shipping_method_snail",
unit_price: 200,
shipping_option: {
id: "snail",
},
},
],
})
expect(result).toEqual([
{
action: "addShippingMethodAdjustment",
shipping_method_id: "shipping_method_express",
amount: 166.66666666666669,
code: "PROMOTION_TEST",
},
{
action: "addShippingMethodAdjustment",
shipping_method_id: "shipping_method_standard",
amount: 33.33333333333333,
code: "PROMOTION_TEST",
},
])
})
it("should compute the correct item amendments when there are multiple promotions to apply", async () => {
const [createdPromotion] = await service.create([
{
@@ -1913,6 +2289,76 @@ describe("Promotion Service: computeActions", () => {
])
})
it("should compute the correct item amendments when promotion is automatic", async () => {
const [createdPromotion] = await service.create([
{
code: "PROMOTION_TEST",
type: PromotionType.STANDARD,
is_automatic: true,
rules: [
{
attribute: "customer.customer_group.id",
operator: "in",
values: ["VIP", "top100"],
},
],
application_method: {
type: "fixed",
target_type: "order",
value: "200",
max_quantity: 2,
},
},
])
const result = await service.computeActions([], {
customer: {
customer_group: {
id: "VIP",
},
},
items: [
{
id: "item_cotton_tshirt",
quantity: 1,
unit_price: 100,
product_category: {
id: "catg_cotton",
},
product: {
id: "prod_tshirt",
},
},
{
id: "item_cotton_sweater",
quantity: 2,
unit_price: 150,
product_category: {
id: "catg_cotton",
},
product: {
id: "prod_sweater",
},
},
],
})
expect(result).toEqual([
{
action: "addItemAdjustment",
item_id: "item_cotton_tshirt",
amount: 50,
code: "PROMOTION_TEST",
},
{
action: "addItemAdjustment",
item_id: "item_cotton_sweater",
amount: 150,
code: "PROMOTION_TEST",
},
])
})
it("should compute the correct item amendments when there are multiple promotions to apply", async () => {
const [createdPromotion] = await service.create([
{
@@ -216,11 +216,11 @@ export default class PromotionModuleService<
}
async computeActions(
promotionCodesToApply: string[],
promotionCodes: string[],
applicationContext: PromotionTypes.ComputeActionContext,
// TODO: specify correct type with options
options: Record<string, any> = {}
options: PromotionTypes.ComputeActionOptions = {}
): Promise<PromotionTypes.ComputeActions[]> {
const { prevent_auto_promotions: preventAutoPromotions } = options
const computedActions: PromotionTypes.ComputeActions[] = []
const { items = [], shipping_methods: shippingMethods = [] } =
applicationContext
@@ -231,6 +231,15 @@ export default class PromotionModuleService<
PromotionTypes.ComputeActionAdjustmentLine
>()
const methodIdPromoValueMap = new Map<string, number>()
const automaticPromotions = preventAutoPromotions
? []
: await this.list({ is_automatic: true }, { select: ["code"] })
const automaticPromotionCodes = automaticPromotions.map((p) => p.code!)
const promotionCodesToApply = [
...promotionCodes,
...automaticPromotionCodes,
]
items.forEach((item) => {
item.adjustments?.forEach((adjustment) => {
@@ -291,7 +300,7 @@ export default class PromotionModuleService<
)
}
if (promotionCodesToApply.includes(appliedCode)) {
if (promotionCodes.includes(appliedCode)) {
continue
}
@@ -65,3 +65,7 @@ export interface ComputeActionContext extends Record<string, unknown> {
items?: ComputeActionItemLine[]
shipping_methods?: ComputeActionShippingLine[]
}
export interface ComputeActionOptions {
prevent_auto_promotions?: boolean
}