From 724d17fe75cad14ad50ce0f323402d4e2d598c87 Mon Sep 17 00:00:00 2001 From: ArtChaos189 Date: Fri, 2 Jun 2023 17:10:26 +0300 Subject: [PATCH] =?UTF-8?q?=D0=98=D0=B7=D0=BC=D0=B5=D0=BD=D0=B5=D0=BD?= =?UTF-8?q?=D0=B8=D0=B5=20=D1=82=D0=B0=D0=B1=D0=BB=D0=B8=D1=86=D1=8B,=20?= =?UTF-8?q?=D0=B4=D0=BE=D0=BF=D0=BE=D0=BB=D0=BD=D0=B5=D0=BD=D0=B8=D0=B5=20?= =?UTF-8?q?=D0=BF=D1=80=D0=B8=D0=B2=D0=B5=D0=BB=D0=B5=D0=B3=D0=B8=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/useCombinedPrivileges.hook.ts | 30 + src/index.tsx | 4 +- src/kitUI/Cart/calc.ts | 454 ++-- src/model/tariff.ts | 2 +- src/pages/Setting/CardPrivilegie.tsx | 4 +- src/pages/Setting/ListPrivilegie.tsx | 6 +- .../Content/Tariffs/CreateTariff.tsx | 257 +- .../Tariffs/Privileges/ChangePriceModal.tsx | 71 +- .../Content/Tariffs/Privileges/Privileges.tsx | 65 +- .../dashboard/Content/Tariffs/tariffsDG.tsx | 12 +- src/stores/mocks/exampleCartValues.ts | 2103 ++++++++--------- src/stores/privileges.ts | 84 +- 12 files changed, 1559 insertions(+), 1533 deletions(-) create mode 100644 src/hooks/useCombinedPrivileges.hook.ts diff --git a/src/hooks/useCombinedPrivileges.hook.ts b/src/hooks/useCombinedPrivileges.hook.ts new file mode 100644 index 0000000..c9b652e --- /dev/null +++ b/src/hooks/useCombinedPrivileges.hook.ts @@ -0,0 +1,30 @@ +import { usePrivilegeStore } from "@root/stores/privileges"; + +import { usePrivilegies } from "./privilege.hook"; + +export type mergedPrivilege = { + createdAt?: string; + description: string; + isDeleted?: boolean; + name: string; + price: string | number; + privilegeId: string; + serviceKey: string; + type: "count" | "day" | "mb"; + updatedAt?: string; + value?: string; + _id?: string; +}; + +export const useCombinedPrivileges = () => { + const { privilegies, isError, errorMessage } = usePrivilegies(); + const examplePrivileges = usePrivilegeStore((state) => state.privileges); + + const mergedPrivileges: mergedPrivilege[] = []; + + if (privilegies) { + mergedPrivileges.push(...privilegies.Шаблонизатор, ...examplePrivileges); + } + + return { mergedPrivileges, isError, errorMessage }; +}; diff --git a/src/index.tsx b/src/index.tsx index e1348c6..cb0597d 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -102,8 +102,8 @@ root.render( } /> - {componentsArray.map((e: any, i) => ( - + {componentsArray.map((element: any) => ( + ))} diff --git a/src/kitUI/Cart/calc.ts b/src/kitUI/Cart/calc.ts index 82b6435..76dd276 100644 --- a/src/kitUI/Cart/calc.ts +++ b/src/kitUI/Cart/calc.ts @@ -1,263 +1,291 @@ -import { CartItem, AnyDiscount, CartTotal, CartItemTotal, PrivilegeDiscount, CartPurchasesAmountDiscount, PurchasesAmountDiscount, ServiceToPriceMap, ServiceDiscount, UserDiscount } from "@root/model/cart"; +import { + CartItem, + AnyDiscount, + CartTotal, + CartItemTotal, + PrivilegeDiscount, + CartPurchasesAmountDiscount, + PurchasesAmountDiscount, + ServiceToPriceMap, + ServiceDiscount, + UserDiscount, +} from "@root/model/cart"; import { ServiceType, SERVICE_LIST, Tariff } from "../../model/tariff"; import { User } from "../../model/user"; import { findPrivilegeById } from "@root/stores/privileges"; - -export function calcCartData({ user, purchasesAmount, cartItems, discounts, isNonCommercial = false, coupon }: { - user: User; - purchasesAmount: number; - cartItems: CartItem[]; - discounts: AnyDiscount[]; - isNonCommercial?: boolean; - coupon?: string; +export function calcCartData({ + user, + purchasesAmount, + cartItems, + discounts, + isNonCommercial = false, + coupon, +}: { + user: User; + purchasesAmount: number; + cartItems: CartItem[]; + discounts: AnyDiscount[]; + isNonCommercial?: boolean; + coupon?: string; }): CartTotal | Error | null { - let isIncompatibleTariffs = false; + let isIncompatibleTariffs = false; - const defaultTariffTypePresent: { [Key in ServiceType]: boolean } = { - dwarfener: false, - squiz: false, - templategen: false, - }; + const defaultTariffTypePresent: { [Key in ServiceType]: boolean } = { + dwarfener: false, + squiz: false, + templategen: false, + }; - cartItems.forEach(cartItem => { - const privilege = findPrivilegeById(cartItem.tariff.privilegeId); - if (!privilege) throw new Error(`Привилегия с id ${cartItem.tariff} не найдена в тарифе ${cartItem.tariff.name} с id ${cartItem.tariff.id}`); + cartItems.forEach((cartItem) => { + const privilege = findPrivilegeById(cartItem.tariff.privilegeId); + if (!privilege) + throw new Error( + `Привилегия с id ${cartItem.tariff} не найдена в тарифе ${cartItem.tariff.name} с id ${cartItem.tariff.id}` + ); - if (cartItem.tariff.customPricePerUnit === undefined) return defaultTariffTypePresent[privilege.serviceKey] = true; + if (cartItem.tariff.customPricePerUnit === undefined) + return (defaultTariffTypePresent[privilege.serviceKey] = true); - if ( - defaultTariffTypePresent[privilege.serviceKey] && - cartItem.tariff.customPricePerUnit !== undefined - ) isIncompatibleTariffs = true; + if (defaultTariffTypePresent[privilege.serviceKey] && cartItem.tariff.customPricePerUnit !== undefined) + isIncompatibleTariffs = true; + }); + + if (isIncompatibleTariffs) + return new Error("Если взят готовый тариф, то кастомный на этот сервис сделать уже нельзя"); + + if (!cartItems.length) return null; + + const cartTotal: CartTotal = { + items: [], + totalPrice: 0, + priceByService: { + templategen: 0, + squiz: 0, + dwarfener: 0, + }, + discountsByService: { + templategen: null, + squiz: null, + dwarfener: null, + }, + envolvedCartDiscounts: [], + couponState: coupon ? "not found" : null, + }; + + // layer 0 + for (const discount of discounts) { + if (discount.conditionType !== "userType" || !isNonCommercial) continue; + + cartItems.forEach((cartItem) => { + cartTotal.items.push({ + envolvedDiscounts: [], + tariff: cartItem.tariff, + totalPrice: cartItem.price, + }); + + const privilege = findPrivilegeById(cartItem.tariff.privilegeId); + if (!privilege) + throw new Error(`Привилегия не найдена в тарифе ${cartItem.tariff.name} с id ${cartItem.tariff.id}`); + + cartTotal.priceByService[privilege.serviceKey] += cartItem.price; + cartTotal.totalPrice += cartItem.price; }); - if (isIncompatibleTariffs) return new Error("Если взят готовый тариф, то кастомный на этот сервис сделать уже нельзя"); - - if (!cartItems.length) return null; - - const cartTotal: CartTotal = { - items: [], - totalPrice: 0, - priceByService: { - templategen: 0, - squiz: 0, - dwarfener: 0, - }, - discountsByService: { - templategen: null, - squiz: null, - dwarfener: null, - }, - envolvedCartDiscounts: [], - couponState: coupon ? "not found" : null, - }; - - // layer 0 - for (const discount of discounts) { - if (discount.conditionType !== "userType" || !isNonCommercial) continue; - - cartItems.forEach(cartItem => { - cartTotal.items.push({ - envolvedDiscounts: [], - tariff: cartItem.tariff, - totalPrice: cartItem.price, - }); - - const privilege = findPrivilegeById(cartItem.tariff.privilegeId); - if (!privilege) throw new Error(`Привилегия не найдена в тарифе ${cartItem.tariff.name} с id ${cartItem.tariff.id}`); - - cartTotal.priceByService[privilege.serviceKey] += cartItem.price; - cartTotal.totalPrice += cartItem.price; - }); - - cartTotal.totalPrice *= discount.target.factor; - cartTotal.envolvedCartDiscounts.push(discount); - - return cartTotal; - } - - const couponDiscount = coupon ? findUserDiscount(discounts, user, coupon) : null; - - // layer 1 - for (const cartItem of cartItems) { - const cartItemTotal: CartItemTotal = { - tariff: cartItem.tariff, - envolvedDiscounts: [], - totalPrice: cartItem.price, - }; - - const tariff = cartItem.tariff; - const privilegesAffectedByCoupon: string[] = []; - - couponDiscount?.target.products.forEach(product => { - if (product.privilegeId !== tariff.privilegeId) return; - if (tariff.customPricePerUnit !== undefined && !couponDiscount.overwhelm) return; - - cartItemTotal.totalPrice *= product.factor; - cartItemTotal.envolvedDiscounts.push(couponDiscount); - cartTotal.couponState = "applied"; - privilegesAffectedByCoupon.push(product.privilegeId); - }); - - const privilegeDiscount = findMaxApplicablePrivilegeDiscount(discounts, tariff); - - privilegeDiscount?.target.products.forEach(product => { - if (product.privilegeId !== tariff.privilegeId) return; - if (tariff.customPricePerUnit !== undefined) return; - if (privilegesAffectedByCoupon.includes(privilegeDiscount.condition.privilege.id)) return; - - cartItemTotal.totalPrice *= product.factor; - cartItemTotal.envolvedDiscounts.push(privilegeDiscount); - }); - - const privilege = findPrivilegeById(cartItem.tariff.privilegeId); - if (!privilege) throw new Error(`Привилегия не найдена в тарифе ${cartItem.tariff.name} с id ${cartItem.tariff.id}`); - - cartTotal.items.push(cartItemTotal); - cartTotal.priceByService[privilege.serviceKey] += cartItemTotal.totalPrice; - } - - // layer 2 - SERVICE_LIST.map(service => service.serviceKey).forEach(service => { - const serviceDiscount = findMaxServiceDiscount(service, discounts, cartTotal.priceByService); - if (serviceDiscount) { - cartTotal.priceByService[service] *= serviceDiscount.target.factor; - cartTotal.discountsByService[service] = serviceDiscount; - } - - cartTotal.totalPrice += cartTotal.priceByService[service]; - }); - - // layer 3 - const cartPurchasesAmountDiscount = findMaxCartPurchasesAmountDiscount(discounts, cartTotal); - if (cartPurchasesAmountDiscount) { - cartTotal.totalPrice *= cartPurchasesAmountDiscount.factor; - cartTotal.envolvedCartDiscounts.push(cartPurchasesAmountDiscount); - } - - // layer 4 - const totalPurchasesAmountDiscount = findMaxTotalPurchasesAmountDiscount(discounts, purchasesAmount); - if (totalPurchasesAmountDiscount) { - cartTotal.totalPrice *= totalPurchasesAmountDiscount.factor; - cartTotal.envolvedCartDiscounts.push(totalPurchasesAmountDiscount); - } + cartTotal.totalPrice *= discount.target.factor; + cartTotal.envolvedCartDiscounts.push(discount); return cartTotal; + } + + const couponDiscount = coupon ? findUserDiscount(discounts, user, coupon) : null; + + // layer 1 + for (const cartItem of cartItems) { + const cartItemTotal: CartItemTotal = { + tariff: cartItem.tariff, + envolvedDiscounts: [], + totalPrice: cartItem.price, + }; + + const tariff = cartItem.tariff; + const privilegesAffectedByCoupon: string[] = []; + + couponDiscount?.target.products.forEach((product) => { + if (product.privilegeId !== tariff.privilegeId) return; + if (tariff.customPricePerUnit !== undefined && !couponDiscount.overwhelm) return; + + cartItemTotal.totalPrice *= product.factor; + cartItemTotal.envolvedDiscounts.push(couponDiscount); + cartTotal.couponState = "applied"; + privilegesAffectedByCoupon.push(product.privilegeId); + }); + + const privilegeDiscount = findMaxApplicablePrivilegeDiscount(discounts, tariff); + + privilegeDiscount?.target.products.forEach((product) => { + if (product.privilegeId !== tariff.privilegeId) return; + if (tariff.customPricePerUnit !== undefined) return; + if (privilegesAffectedByCoupon.includes(privilegeDiscount.condition.privilege.id)) return; + + cartItemTotal.totalPrice *= product.factor; + cartItemTotal.envolvedDiscounts.push(privilegeDiscount); + }); + + const privilege = findPrivilegeById(cartItem.tariff.privilegeId); + if (!privilege) + throw new Error(`Привилегия не найдена в тарифе ${cartItem.tariff.name} с id ${cartItem.tariff.id}`); + + cartTotal.items.push(cartItemTotal); + cartTotal.priceByService[privilege.serviceKey] += cartItemTotal.totalPrice; + } + + // layer 2 + SERVICE_LIST.map((service) => service.serviceKey).forEach((service) => { + const serviceDiscount = findMaxServiceDiscount(service, discounts, cartTotal.priceByService); + if (serviceDiscount) { + cartTotal.priceByService[service] *= serviceDiscount.target.factor; + cartTotal.discountsByService[service] = serviceDiscount; + } + + cartTotal.totalPrice += cartTotal.priceByService[service]; + }); + + // layer 3 + const cartPurchasesAmountDiscount = findMaxCartPurchasesAmountDiscount(discounts, cartTotal); + if (cartPurchasesAmountDiscount) { + cartTotal.totalPrice *= cartPurchasesAmountDiscount.factor; + cartTotal.envolvedCartDiscounts.push(cartPurchasesAmountDiscount); + } + + // layer 4 + const totalPurchasesAmountDiscount = findMaxTotalPurchasesAmountDiscount(discounts, purchasesAmount); + if (totalPurchasesAmountDiscount) { + cartTotal.totalPrice *= totalPurchasesAmountDiscount.factor; + cartTotal.envolvedCartDiscounts.push(totalPurchasesAmountDiscount); + } + + return cartTotal; } function findMaxApplicablePrivilegeDiscount(discounts: AnyDiscount[], tariff: Tariff): PrivilegeDiscount | null { - const applicableDiscounts = discounts.filter((discount): discount is PrivilegeDiscount => { - return ( - discount.conditionType === "privilege" && - tariff.privilegeId === discount.condition.privilege.id && - tariff.amount >= discount.condition.privilege.value - ); - }); - - if (!applicableDiscounts.length) return null; - - const maxValueDiscount = applicableDiscounts.reduce( - (prev, current) => current.condition.privilege.value > prev.condition.privilege.value ? current : prev + const applicableDiscounts = discounts.filter((discount): discount is PrivilegeDiscount => { + return ( + discount.conditionType === "privilege" && + tariff.privilegeId === discount.condition.privilege.id && + tariff.amount >= discount.condition.privilege.value ); + }); - return maxValueDiscount; + if (!applicableDiscounts.length) return null; + + const maxValueDiscount = applicableDiscounts.reduce((prev, current) => + current.condition.privilege.value > prev.condition.privilege.value ? current : prev + ); + + return maxValueDiscount; } -function findMaxCartPurchasesAmountDiscount(discounts: AnyDiscount[], cartTotal: CartTotal): CartPurchasesAmountDiscount | null { - const applicableDiscounts = discounts.filter((discount): discount is CartPurchasesAmountDiscount => { - return discount.conditionType === "cartPurchasesAmount" && cartTotal.totalPrice >= discount.condition.cartPurchasesAmount; - }); - - if (!applicableDiscounts.length) return null; - - const maxValueDiscount = applicableDiscounts.reduce( - (prev, current) => current.condition.cartPurchasesAmount > prev.condition.cartPurchasesAmount ? current : prev +function findMaxCartPurchasesAmountDiscount( + discounts: AnyDiscount[], + cartTotal: CartTotal +): CartPurchasesAmountDiscount | null { + const applicableDiscounts = discounts.filter((discount): discount is CartPurchasesAmountDiscount => { + return ( + discount.conditionType === "cartPurchasesAmount" && cartTotal.totalPrice >= discount.condition.cartPurchasesAmount ); + }); - return maxValueDiscount; + if (!applicableDiscounts.length) return null; + + const maxValueDiscount = applicableDiscounts.reduce((prev, current) => + current.condition.cartPurchasesAmount > prev.condition.cartPurchasesAmount ? current : prev + ); + + return maxValueDiscount; } -function findMaxTotalPurchasesAmountDiscount(discounts: AnyDiscount[], purchasesAmount: number): PurchasesAmountDiscount | null { - const applicableDiscounts = discounts.filter((discount): discount is PurchasesAmountDiscount => { - return discount.conditionType === "purchasesAmount" && purchasesAmount >= discount.condition.purchasesAmount; - }); +function findMaxTotalPurchasesAmountDiscount( + discounts: AnyDiscount[], + purchasesAmount: number +): PurchasesAmountDiscount | null { + const applicableDiscounts = discounts.filter((discount): discount is PurchasesAmountDiscount => { + return discount.conditionType === "purchasesAmount" && purchasesAmount >= discount.condition.purchasesAmount; + }); - if (!applicableDiscounts.length) return null; + if (!applicableDiscounts.length) return null; - const maxValueDiscount = applicableDiscounts.reduce( - (prev, current) => current.condition.purchasesAmount > prev.condition.purchasesAmount ? current : prev - ); + const maxValueDiscount = applicableDiscounts.reduce((prev, current) => + current.condition.purchasesAmount > prev.condition.purchasesAmount ? current : prev + ); - return maxValueDiscount; + return maxValueDiscount; } function findMaxServiceDiscount( - service: ServiceType, - discounts: AnyDiscount[], - priceByService: ServiceToPriceMap, + service: ServiceType, + discounts: AnyDiscount[], + priceByService: ServiceToPriceMap ): ServiceDiscount | null { - const discountsForTariffService = discounts.filter((discount): discount is ServiceDiscount => { - return ( - discount.conditionType === "service" && - discount.condition.service.id === service && - priceByService[service] >= discount.condition.service.value - ); - }); + const discountsForTariffService = discounts.filter((discount): discount is ServiceDiscount => { + return ( + discount.conditionType === "service" && + discount.condition.service.id === service && + priceByService[service] >= discount.condition.service.value + ); + }); - if (!discountsForTariffService.length) return null; + if (!discountsForTariffService.length) return null; - const maxValueDiscount = discountsForTariffService.reduce((prev, current) => { - return current.condition.service.value > prev.condition.service.value ? current : prev; - }); + const maxValueDiscount = discountsForTariffService.reduce((prev, current) => { + return current.condition.service.value > prev.condition.service.value ? current : prev; + }); - return maxValueDiscount; + return maxValueDiscount; } -function findUserDiscount(discounts: AnyDiscount[], user: User, coupon: string,): UserDiscount | null { - const userDiscount = discounts.find((discount): discount is UserDiscount => { - return ( - discount.conditionType === "user" && - discount.condition.user === user.ID && - discount.condition.coupon === coupon - ); - }); +function findUserDiscount(discounts: AnyDiscount[], user: User, coupon: string): UserDiscount | null { + const userDiscount = discounts.find((discount): discount is UserDiscount => { + return ( + discount.conditionType === "user" && discount.condition.user === user.ID && discount.condition.coupon === coupon + ); + }); - return userDiscount ?? null; + return userDiscount ?? null; } export function createCartItem(tariff: Tariff): CartItem { - const pricePerUnit = tariff.customPricePerUnit ?? findPrivilegeById(tariff.privilegeId)?.pricePerUnit ?? 0; - const price = pricePerUnit * tariff.amount; + const pricePerUnit = tariff.customPricePerUnit ?? findPrivilegeById(tariff.privilegeId)?.price ?? 0; + const price = pricePerUnit * tariff.amount; - return { tariff, price, id: "someId" }; + return { tariff, price, id: "someId" }; } export function findDiscountFactor(discount: AnyDiscount): number { - switch (discount.conditionType) { - case "cartPurchasesAmount": - return discount.factor; - case "purchasesAmount": - return discount.factor; - case "privilege": { - const product = discount.target.products[0]; - if (!product) throw new Error("Discount target product not found"); + switch (discount.conditionType) { + case "cartPurchasesAmount": + return discount.factor; + case "purchasesAmount": + return discount.factor; + case "privilege": { + const product = discount.target.products[0]; + if (!product) throw new Error("Discount target product not found"); - return product.factor; - } - case "user": { - const product = discount.target.products[0]; - if (!product) throw new Error("Discount target product not found"); - - return product.factor; - } - case "service": - return discount.target.factor; - case "userType": - return discount.target.factor; + return product.factor; } + case "user": { + const product = discount.target.products[0]; + if (!product) throw new Error("Discount target product not found"); + + return product.factor; + } + case "service": + return discount.target.factor; + case "userType": + return discount.target.factor; + } } export function formatDiscountFactor(factor: number): string { - return `${((1 - factor) * 100).toFixed(1)}%`; + return `${((1 - factor) * 100).toFixed(1)}%`; } diff --git a/src/model/tariff.ts b/src/model/tariff.ts index 25c18ec..c65c985 100644 --- a/src/model/tariff.ts +++ b/src/model/tariff.ts @@ -25,7 +25,7 @@ export interface Privilege { /** Единица измерения привелегии: время в днях/кол-во */ type: "day" | "count"; /** Стоимость одной единицы привелегии */ - pricePerUnit: number; + price: number; } export interface Tariff { diff --git a/src/pages/Setting/CardPrivilegie.tsx b/src/pages/Setting/CardPrivilegie.tsx index 1436775..8703046 100644 --- a/src/pages/Setting/CardPrivilegie.tsx +++ b/src/pages/Setting/CardPrivilegie.tsx @@ -7,9 +7,9 @@ import ModeEditOutlineOutlinedIcon from "@mui/icons-material/ModeEditOutlineOutl interface CardPrivilegie { name: string; type: "count" | "day" | "mb"; - price: string; + price: string | number; description: string; - value: string; + value?: string; privilegeId: string; serviceKey: string; } diff --git a/src/pages/Setting/ListPrivilegie.tsx b/src/pages/Setting/ListPrivilegie.tsx index e6ceadc..2f344d6 100644 --- a/src/pages/Setting/ListPrivilegie.tsx +++ b/src/pages/Setting/ListPrivilegie.tsx @@ -1,16 +1,16 @@ import { Typography } from "@mui/material"; import { СardPrivilegie } from "./CardPrivilegie"; -import { usePrivilegies } from "@root/hooks/privilege.hook"; +import { useCombinedPrivileges } from "@root/hooks/useCombinedPrivileges.hook"; export default function ListPrivilegie() { - const { privilegies, isError, isLoading, errorMessage } = usePrivilegies(); + const { mergedPrivileges, isError, errorMessage } = useCombinedPrivileges(); return ( <> {isError ? ( {errorMessage} ) : ( - privilegies?.Шаблонизатор.map(({ name, type, price, description, value, privilegeId, serviceKey, _id }) => ( + mergedPrivileges.map(({ name, type, price, description, value, privilegeId, serviceKey, _id }) => ( <СardPrivilegie key={_id} name={name} diff --git a/src/pages/dashboard/Content/Tariffs/CreateTariff.tsx b/src/pages/dashboard/Content/Tariffs/CreateTariff.tsx index 2b450fd..756fb65 100644 --- a/src/pages/dashboard/Content/Tariffs/CreateTariff.tsx +++ b/src/pages/dashboard/Content/Tariffs/CreateTariff.tsx @@ -1,131 +1,158 @@ import { Typography, Container, Button, Select, MenuItem, FormControl, InputLabel, useTheme, Box } from "@mui/material"; +import { useCombinedPrivileges } from "@root/hooks/useCombinedPrivileges.hook"; import { CustomTextField } from "@root/kitUI/CustomTextField"; import { Tariff } from "@root/model/tariff"; -import { findPrivilegeById, usePrivilegeStore } from "@root/stores/privileges"; import { addTariffs } from "@root/stores/tariffs"; import { nanoid } from "nanoid"; import { useState } from "react"; - export default function CreateTariff() { - const theme = useTheme(); - const privileges = usePrivilegeStore(store => store.privileges); - const [nameField, setNameField] = useState(""); - const [amountField, setAmountField] = useState(""); - const [customPriceField, setCustomPriceField] = useState(""); - const [privilegeIdField, setPrivilegeIdField] = useState(""); + const theme = useTheme(); + const [nameField, setNameField] = useState(""); + const [amountField, setAmountField] = useState(""); + const [customPriceField, setCustomPriceField] = useState(""); + const [privilegeIdField, setPrivilegeIdField] = useState(""); + const { mergedPrivileges, isError, errorMessage } = useCombinedPrivileges(); - const privilege = findPrivilegeById(privilegeIdField); + const findPrivilegeById = (privilegeId: string) => { + return mergedPrivileges.find((privilege) => privilege.privilegeId === privilegeId) ?? null; + }; - function handleCreateTariffClick() { - const amount = Number(amountField); - const customPrice = Number(customPriceField); + const privilege = findPrivilegeById(privilegeIdField); - if (isNaN(amount) || !privilege) return; + function handleCreateTariffClick() { + const amount = Number(amountField); + const customPrice = Number(customPriceField); - const newTariff: Tariff = { - id: nanoid(5), - name: nameField, - amount, - privilegeId: privilege.privilegeId, - customPricePerUnit: customPrice ? customPrice / amount : undefined, - }; + if (isNaN(amount) || !privilege) return; - addTariffs([newTariff]); - } + const newTariff: Tariff = { + id: nanoid(5), + name: nameField, + amount, + privilegeId: privilege.privilegeId, + customPricePerUnit: customPrice ? customPrice / amount : undefined, + }; - return ( - + + Создание тарифа + + + + Привелегия + + {isError ? ( + {errorMessage} + ) : ( + + )} + + {privilege && ( + - Создание тарифа - - Привелегия - - - {privilege && - - Имя: {privilege.name} - Сервис: {privilege.serviceKey} - Единица: {privilege.type} - Стандартная цена за единицу: {privilege.pricePerUnit} - - } - setNameField(e.target.value)} - /> - setAmountField(e.target.value)} - type="number" - /> - setCustomPriceField(e.target.value)} - type="number" - /> - - - ); -} \ No newline at end of file + }} + > + + Имя: {privilege.name} + + + Сервис: {privilege.serviceKey} + + + Единица: {privilege.type} + + + Стандартная цена за единицу: {privilege.price} + + + )} + setNameField(e.target.value)} + /> + setAmountField(e.target.value)} + type="number" + /> + setCustomPriceField(e.target.value)} + type="number" + /> + + + ); +} diff --git a/src/pages/dashboard/Content/Tariffs/Privileges/ChangePriceModal.tsx b/src/pages/dashboard/Content/Tariffs/Privileges/ChangePriceModal.tsx index f0665eb..b233493 100644 --- a/src/pages/dashboard/Content/Tariffs/Privileges/ChangePriceModal.tsx +++ b/src/pages/dashboard/Content/Tariffs/Privileges/ChangePriceModal.tsx @@ -1,42 +1,43 @@ import { Box, Button, Dialog, useTheme } from "@mui/material"; import { CustomTextField } from "@root/kitUI/CustomTextField"; -import { changeModalPriceField, changePrivilegePrice, closePrivilegePriceModal, usePrivilegeStore } from "@root/stores/privileges"; +import { + changeModalPriceField, + changePrivilegePrice, + closePrivilegePriceModal, + usePrivilegeStore, +} from "@root/stores/privileges"; import { enqueueSnackbar } from "notistack"; - export default function ChangePriceModal() { - const theme = useTheme(); - const isModalOpen = usePrivilegeStore(state => state.isModalOpen); - const modalPriceField = usePrivilegeStore(state => state.modalPriceField); + const theme = useTheme(); + const isModalOpen = usePrivilegeStore((state) => state.isModalOpen); + const modalPriceField = usePrivilegeStore((state) => state.modalPriceField); - function handleSaveChange() { - const errorMessage = changePrivilegePrice(); - if (errorMessage) enqueueSnackbar(errorMessage); - } + function handleSaveChange() { + const errorMessage = changePrivilegePrice(); + if (errorMessage) enqueueSnackbar(errorMessage); + } - return ( - - - changeModalPriceField(e.target.value)} - type="number" - /> - - - - ); -} \ No newline at end of file + return ( + + + changeModalPriceField(e.target.value)} + type="number" + /> + + + + ); +} diff --git a/src/pages/dashboard/Content/Tariffs/Privileges/Privileges.tsx b/src/pages/dashboard/Content/Tariffs/Privileges/Privileges.tsx index 752af0d..b4a9952 100644 --- a/src/pages/dashboard/Content/Tariffs/Privileges/Privileges.tsx +++ b/src/pages/dashboard/Content/Tariffs/Privileges/Privileges.tsx @@ -1,51 +1,38 @@ import { GridColDef } from "@mui/x-data-grid"; import DataGrid from "@kitUI/datagrid"; -import { openPrivilegePriceModal, usePrivilegeStore } from "@stores/privileges"; -import { IconButton } from "@mui/material"; -import { MouseEventHandler } from "react"; -import EditIcon from '@mui/icons-material/Edit'; - +import { useCombinedPrivileges } from "@root/hooks/useCombinedPrivileges.hook"; +import { Typography } from "@mui/material"; const columns: GridColDef[] = [ - { field: 'id', headerName: 'id', width: 40 }, - { field: 'name', headerName: 'Привелегия', width: 150 }, - { field: 'description', headerName: 'Описание', width: 550 },//инфо из гитлаба. - { field: 'type', headerName: 'Тип', width: 150 }, - { field: 'price', headerName: 'Стоимость', width: 50 }, - { - field: "changeValue", - headerName: "Изменить", - sortable: false, - renderCell: (params) => { - const onClick: MouseEventHandler = () => { - openPrivilegePriceModal(params.row.id, params.row.price); - }; - - return ( - - - - ); - }, - }, + { field: "id", headerName: "id", width: 40 }, + { field: "name", headerName: "Привелегия", width: 150 }, + { field: "description", headerName: "Описание", width: 550 }, //инфо из гитлаба. + { field: "type", headerName: "Тип", width: 150 }, + { field: "price", headerName: "Стоимость", width: 100 }, ]; export default function Privileges() { - const privileges = usePrivilegeStore(state => state.privileges); + const { mergedPrivileges, isError, errorMessage } = useCombinedPrivileges(); - const privilegesGridData = privileges.map(privilege => ({ - id: privilege.privilegeId, - name: privilege.name, - description: privilege.description, - type: privilege.type, - price: privilege.pricePerUnit, - })); + const privilegesGridData = mergedPrivileges.map((privilege) => ({ + id: privilege.privilegeId, + name: privilege.name, + description: privilege.description, + type: privilege.type, + price: privilege.price, + })); - return ( + return ( + <> + {isError ? ( + {errorMessage} + ) : ( - ); + )} + + ); } diff --git a/src/pages/dashboard/Content/Tariffs/tariffsDG.tsx b/src/pages/dashboard/Content/Tariffs/tariffsDG.tsx index a43270f..96220b3 100644 --- a/src/pages/dashboard/Content/Tariffs/tariffsDG.tsx +++ b/src/pages/dashboard/Content/Tariffs/tariffsDG.tsx @@ -29,13 +29,17 @@ export default function TariffsDG({ handleSelectionChange }: Props) { const gridData = tariffs.map((tariff) => ({ id: tariff.id, name: tariff.name, - serviceName: SERVICE_LIST.find((service) => service.serviceKey === findPrivilegeById(tariff.privilegeId)?.serviceKey)?.displayName, - privilege: `(${tariff.privilegeId}) ${findPrivilegeById(tariff.privilegeId)?.description ?? "Привилегия не найдена"}`, + serviceName: SERVICE_LIST.find( + (service) => service.serviceKey === findPrivilegeById(tariff.privilegeId)?.serviceKey + )?.displayName, + privilege: `(${tariff.privilegeId}) ${ + findPrivilegeById(tariff.privilegeId)?.description ?? "Привилегия не найдена" + }`, amount: tariff.amount, type: findPrivilegeById(tariff.privilegeId)?.type === "count" ? "день" : "шт.", - pricePerUnit: tariff.customPricePerUnit ?? findPrivilegeById(tariff.privilegeId)?.pricePerUnit, + pricePerUnit: tariff.customPricePerUnit ?? findPrivilegeById(tariff.privilegeId)?.price, isCustomPrice: tariff.customPricePerUnit === undefined ? "Нет" : "Да", - total: tariff.amount * (tariff.customPricePerUnit ?? findPrivilegeById(tariff.privilegeId)?.pricePerUnit ?? 0), + total: tariff.amount * (tariff.customPricePerUnit ?? findPrivilegeById(tariff.privilegeId)?.price ?? 0), })); return ( diff --git a/src/stores/mocks/exampleCartValues.ts b/src/stores/mocks/exampleCartValues.ts index bf05c15..3633a7d 100644 --- a/src/stores/mocks/exampleCartValues.ts +++ b/src/stores/mocks/exampleCartValues.ts @@ -3,1123 +3,1076 @@ import { Privilege } from "@root/model/tariff"; import { User } from "../../model/user"; export type TestCase = { - input: { - UserInformation: User; - Products: Array<{ - ID: string; - Amount: number; - Price?: number; - }>; - }; - expect: { - price: number; - envolvedDiscounts: string[]; - }; + input: { + UserInformation: User; + Products: Array<{ + ID: string; + Amount: number; + Price?: number; + }>; + }; + expect: { + price: number; + envolvedDiscounts: string[]; + }; }; type ExampleCartValues = { - privileges: Privilege[]; - discounts: AnyDiscount[]; - testCases: TestCase[]; + privileges: Privilege[]; + discounts: AnyDiscount[]; + testCases: TestCase[]; }; export const exampleCartValues: ExampleCartValues = { - "privileges": [ - { - "serviceKey": "templategen", - "name": "unlim", - "privilegeId": "p1", - "description": "привилегия безлимитного доступа к шаблонизатору на время. в днях", - "type": "day", - "pricePerUnit": 0.5 + privileges: [ + { + serviceKey: "templategen", + name: "unlim", + privilegeId: "1", + description: "привилегия безлимитного доступа к шаблонизатору на время. в днях", + type: "day", + price: 0.5, + }, + { + serviceKey: "templategen", + name: "gencount", + privilegeId: "2", + description: "привилегия на определённое количество генераций", + type: "count", + price: 0.1, + }, + { + serviceKey: "squiz", + name: "unlim", + privilegeId: "3", + description: "привилегия безлимитного доступа к опроснику. в днях", + type: "day", + price: 3.0, + }, + { + serviceKey: "squiz", + name: "activequiz", + privilegeId: "4", + description: "привилегия создания ограниченного количества опросов", + type: "count", + price: 1.0, + }, + { + serviceKey: "dwarfener", + name: "unlim", + privilegeId: "5", + description: "привилегия безлимитного доступа к сокращателю на время. в днях", + type: "day", + price: 0.1, + }, + { + serviceKey: "dwarfener", + name: "abcount", + privilegeId: "6", + description: "привилегия на количество активных ссылок в абтестах", + type: "count", + price: 0.7, + }, + { + serviceKey: "dwarfener", + name: "extended", + privilegeId: "7", + description: "привилегия расширенной статистики, в днях", + type: "day", + price: 2, + }, + ], + discounts: [ + { + _id: "id1", + name: "Лояльность 1", + description: + "постоянная скидка для юзеров, внёсших на проект от 10 000 рублей. Применяется на итоговую сумму, после скидок за сумму в корзине", + conditionType: "purchasesAmount", + layer: 4, // "слой", т.е. этап применения скидки + condition: { + purchasesAmount: 10000, + }, + factor: 0.99, // множитель, применяемый к сумме + }, + { + _id: "id2", + name: "Лояльность 2", + description: + "постоянная скидка для юзеров, внёсших на проект от 25 000 рублей. Применяется на итоговую сумму, после скидок за сумму в корзине", + conditionType: "purchasesAmount", + layer: 4, + condition: { + purchasesAmount: 25000, + }, + factor: 0.98, + }, + { + _id: "id3", + name: "Лояльность 3", + description: + "постоянная скидка для юзеров, внёсших на проект от 50 000 рублей. Применяется на итоговую сумму, после скидок за сумму в корзине", + conditionType: "purchasesAmount", + layer: 4, + condition: { + purchasesAmount: 50000, + }, + factor: 0.975, + }, + { + _id: "id4", + name: "Корзина 1", + description: "Скидка на размер корзины от 5 000 р. Применяется на итоговую сумму, после суммирования корзины", + conditionType: "cartPurchasesAmount", + layer: 3, + condition: { + cartPurchasesAmount: 5000, + }, + factor: 0.985, + }, + { + _id: "id5", + name: "Корзина 2", + description: "Скидка на размер корзины от 50 000 р. Применяется на итоговую сумму, после суммирования корзины", + conditionType: "cartPurchasesAmount", + layer: 3, + condition: { + cartPurchasesAmount: 50000, + }, + factor: 0.965, + }, + { + _id: "id6", + name: "Анлим Шабло 1", + description: "Скидка на количество безлимитных дней работы от 30 дней", + conditionType: "privilege", + layer: 1, + condition: { + privilege: { + // если в condition условие для поиска, то вот я добавил в него условие для поиска по привилегии + id: "p1", // айди привилегии + value: 30, // скидка работает, если значние больше либо равно этому значению }, - { - "serviceKey": "templategen", - "name": "gencount", - "privilegeId": "p2", - "description": "привилегия на определённое количество генераций", - "type": "count", - "pricePerUnit": 0.1 + }, + target: { + products: [ + { + privilegeId: "p1", // не знаю, стоит ли тут оставлять массив products, но на всякий случай оставлю. т.е. скидка, при срабатывании, применяется к этой привилегии в корзине, т.е. умножает её сумму на factor + factor: 0.975, + }, + ], + }, + }, + { + _id: "id7", + name: "Анлим Шабло 2", + description: "Скидка на количество безлимитных дней работы от 90 дней", + conditionType: "privilege", + layer: 1, + condition: { + privilege: { + id: "p1", + value: 90, }, - { - "serviceKey": "squiz", - "name": "unlim", - "privilegeId": "p3", - "description": "привилегия безлимитного доступа к опроснику. в днях", - "type": "day", - "pricePerUnit": 3.0 + }, + target: { + products: [ + { + privilegeId: "p1", + factor: 0.975, + }, + ], + }, + }, + { + _id: "id8.rev", + name: "Анлим Шабло 3", + description: "Скидка на количество безлимитных дней работы от 180 дней", + conditionType: "privilege", + layer: 1, + condition: { + privilege: { + id: "p1", + value: 180, }, - { - "serviceKey": "squiz", - "name": "activequiz", - "privilegeId": "p4", - "description": "привилегия создания ограниченного количества опросов", - "type": "count", - "pricePerUnit": 1.0 + }, + target: { + products: [ + { + privilegeId: "p1", + factor: 0.93, + }, + ], + }, + }, + { + _id: "id8", + name: "Генерации Шабло 1", + description: "Скидка на количество генераций от 100 шт", + conditionType: "privilege", + layer: 1, + condition: { + privilege: { + id: "p2", + value: 100, }, - { - "serviceKey": "dwarfener", - "name": "unlim", - "privilegeId": "p5", - "description": "привилегия безлимитного доступа к сокращателю на время. в днях", - "type": "day", - "pricePerUnit": 0.1 + }, + target: { + products: [ + { + privilegeId: "p2", + factor: 0.995, + }, + ], + }, + }, + { + _id: "id9", + name: "Генерации Шабло 2", + description: "Скидка на количество генераций от 350 шт", + conditionType: "privilege", + layer: 1, + condition: { + privilege: { + id: "p2", + value: 350, }, - { - "serviceKey": "dwarfener", - "name": "abcount", - "privilegeId": "p6", - "description": "привилегия на количество активных ссылок в абтестах", - "type": "count", - "pricePerUnit": 0.7 + }, + target: { + products: [ + { + privilegeId: "p2", + factor: 0.98, + }, + ], + }, + }, + { + _id: "id10", + name: "Генерации Шабло 3", + description: "Скидка на количество генераций от 500 шт", + conditionType: "privilege", + layer: 1, + condition: { + privilege: { + id: "p2", + value: 500, }, - { - "serviceKey": "dwarfener", - "name": "extended", - "privilegeId": "p7", - "description": "привилегия расширенной статистики, в днях", - "type": "day", - "pricePerUnit": 2 - } - ], - "discounts": [ - { - "_id": "id1", - "name": "Лояльность 1", - "description": "постоянная скидка для юзеров, внёсших на проект от 10 000 рублей. Применяется на итоговую сумму, после скидок за сумму в корзине", - "conditionType": "purchasesAmount", - "layer": 4, // "слой", т.е. этап применения скидки - "condition": { - "purchasesAmount": 10000 - }, - "factor": 0.99 // множитель, применяемый к сумме + }, + target: { + products: [ + { + privilegeId: "p2", + factor: 0.945, + }, + ], + }, + }, + { + _id: "id11", + name: "Анлим Квиз 1", + description: "Скидка на количество дней безлимитного использования опросника, от 30 дней", + conditionType: "privilege", + layer: 1, + condition: { + privilege: { + id: "p3", + value: 30, }, - { - "_id": "id2", - "name": "Лояльность 2", - "description": "постоянная скидка для юзеров, внёсших на проект от 25 000 рублей. Применяется на итоговую сумму, после скидок за сумму в корзине", - "conditionType": "purchasesAmount", - "layer": 4, - "condition": { - "purchasesAmount": 25000 - }, - "factor": 0.98 + }, + target: { + products: [ + { + privilegeId: "p3", + factor: 0.97, + }, + ], + }, + }, + { + _id: "id12", + name: "Анлим Квиз 2", + description: "Скидка на количество дней безлимитного использования опросника, от 90 дней", + conditionType: "privilege", + layer: 1, + condition: { + privilege: { + id: "p3", + value: 90, }, - { - "_id": "id3", - "name": "Лояльность 3", - "description": "постоянная скидка для юзеров, внёсших на проект от 50 000 рублей. Применяется на итоговую сумму, после скидок за сумму в корзине", - "conditionType": "purchasesAmount", - "layer": 4, - "condition": { - "purchasesAmount": 50000 - }, - "factor": 0.975 + }, + target: { + products: [ + { + privilegeId: "p3", + factor: 0.93, + }, + ], + }, + }, + { + _id: "id13", + name: "Анлим Квиз 3", + description: "Скидка на количество дней безлимитного использования опросника, от 180 дней", + conditionType: "privilege", + layer: 1, + condition: { + privilege: { + id: "p3", + value: 180, }, - { - "_id": "id4", - "name": "Корзина 1", - "description": "Скидка на размер корзины от 5 000 р. Применяется на итоговую сумму, после суммирования корзины", - "conditionType": "cartPurchasesAmount", - "layer": 3, - "condition": { - "cartPurchasesAmount": 5000 - }, - "factor": 0.985 + }, + target: { + products: [ + { + privilegeId: "p3", + factor: 0.85, + }, + ], + }, + }, + { + _id: "id14", + name: "Актив квиз 1", + description: "Скидка на количество опросов от 100 шт", + conditionType: "privilege", + layer: 1, + condition: { + privilege: { + id: "p4", + value: 100, }, - { - "_id": "id5", - "name": "Корзина 2", - "description": "Скидка на размер корзины от 50 000 р. Применяется на итоговую сумму, после суммирования корзины", - "conditionType": "cartPurchasesAmount", - "layer": 3, - "condition": { - "cartPurchasesAmount": 50000 - }, - "factor": 0.965 + }, + target: { + products: [ + { + privilegeId: "p4", + factor: 0.98, + }, + ], + }, + }, + { + _id: "id15", + name: "Актив квиз 2", + description: "Скидка на количество опросов от 200 шт", + conditionType: "privilege", + layer: 1, + condition: { + privilege: { + id: "p4", + value: 200, }, - { - "_id": "id6", - "name": "Анлим Шабло 1", - "description": "Скидка на количество безлимитных дней работы от 30 дней", - "conditionType": "privilege", - "layer": 1, - "condition": { - "privilege": { // если в condition условие для поиска, то вот я добавил в него условие для поиска по привилегии - "id": "p1", // айди привилегии - "value": 30 // скидка работает, если значние больше либо равно этому значению - } - }, - "target": { - "products": [ - { - "privilegeId": "p1", // не знаю, стоит ли тут оставлять массив products, но на всякий случай оставлю. т.е. скидка, при срабатывании, применяется к этой привилегии в корзине, т.е. умножает её сумму на factor - "factor": 0.975 - } - ] - } + }, + target: { + products: [ + { + privilegeId: "p4", + factor: 0.96, + }, + ], + }, + }, + { + _id: "id16", + name: "Актив квиз 3", + description: "Скидка на количество опросов от 350 шт", + conditionType: "privilege", + layer: 1, + condition: { + privilege: { + id: "p4", + value: 350, }, - { - "_id": "id7", - "name": "Анлим Шабло 2", - "description": "Скидка на количество безлимитных дней работы от 90 дней", - "conditionType": "privilege", - "layer": 1, - "condition": { - "privilege": { - "id": "p1", - "value": 90 - } - }, - "target": { - "products": [ - { - "privilegeId": "p1", - "factor": 0.975 - } - ] - } + }, + target: { + products: [ + { + privilegeId: "p4", + factor: 0.9, + }, + ], + }, + }, + { + _id: "id17", + name: "Анлим Сокращатель 1", + description: "Скидка на безлимитное использование сокращателя от 30 дней", + conditionType: "privilege", + layer: 1, + condition: { + privilege: { + id: "p5", + value: 30, }, - { - "_id": "id8.rev", - "name": "Анлим Шабло 3", - "description": "Скидка на количество безлимитных дней работы от 180 дней", - "conditionType": "privilege", - "layer": 1, - "condition": { - "privilege": { - "id": "p1", - "value": 180 - } - }, - "target": { - "products": [ - { - "privilegeId": "p1", - "factor": 0.93 - } - ] - } + }, + target: { + products: [ + { + privilegeId: "p5", + factor: 0.99, + }, + ], + }, + }, + { + _id: "id18", + name: "Анлим Сокращатель 2", + description: "Скидка на безлимитное использование сокращателя от 60 дней", + conditionType: "privilege", + layer: 1, + condition: { + privilege: { + id: "p5", + value: 60, }, - { - "_id": "id8", - "name": "Генерации Шабло 1", - "description": "Скидка на количество генераций от 100 шт", - "conditionType": "privilege", - "layer": 1, - "condition": { - "privilege": { - "id": "p2", - "value": 100 - } - }, - "target": { - "products": [ - { - "privilegeId": "p2", - "factor": 0.995 - } - ] - } + }, + target: { + products: [ + { + privilegeId: "p5", + factor: 0.98, + }, + ], + }, + }, + { + _id: "id19", + name: "Анлим Сокращатель 3", + description: "Скидка на безлимитное использование сокращателя от 90 дней", + conditionType: "privilege", + layer: 1, + condition: { + privilege: { + id: "p5", + value: 90, }, - { - "_id": "id9", - "name": "Генерации Шабло 2", - "description": "Скидка на количество генераций от 350 шт", - "conditionType": "privilege", - "layer": 1, - "condition": { - "privilege": { - "id": "p2", - "value": 350 - } - }, - "target": { - "products": [ - { - "privilegeId": "p2", - "factor": 0.98 - } - ] - } + }, + target: { + products: [ + { + privilegeId: "p5", + factor: 0.97, + }, + ], + }, + }, + { + _id: "id20", + name: "АБ Сокращатель 1", + description: "Скидка на количество АБ тестов, от 10 штук", + conditionType: "privilege", + layer: 1, + condition: { + privilege: { + id: "p6", + value: 10, }, - { - "_id": "id10", - "name": "Генерации Шабло 3", - "description": "Скидка на количество генераций от 500 шт", - "conditionType": "privilege", - "layer": 1, - "condition": { - "privilege": { - "id": "p2", - "value": 500 - } - }, - "target": { - "products": [ - { - "privilegeId": "p2", - "factor": 0.945 - } - ] - } + }, + target: { + products: [ + { + privilegeId: "p6", + factor: 0.99, + }, + ], + }, + }, + { + _id: "id22", + name: "АБ Сокращатель 2", + description: "Скидка на количество АБ тестов, от 25 штук", + conditionType: "privilege", + layer: 1, + condition: { + privilege: { + id: "p6", + value: 25, }, - { - "_id": "id11", - "name": "Анлим Квиз 1", - "description": "Скидка на количество дней безлимитного использования опросника, от 30 дней", - "conditionType": "privilege", - "layer": 1, - "condition": { - "privilege": { - "id": "p3", - "value": 30 - } - }, - "target": { - "products": [ - { - "privilegeId": "p3", - "factor": 0.97 - } - ] - } + }, + target: { + products: [ + { + privilegeId: "p6", + factor: 0.965, + }, + ], + }, + }, + { + _id: "id23", + name: "АБ Сокращатель 3", + description: "Скидка на количество АБ тестов, от 50 штук", + conditionType: "privilege", + layer: 1, + condition: { + privilege: { + id: "p6", + value: 50, }, - { - "_id": "id12", - "name": "Анлим Квиз 2", - "description": "Скидка на количество дней безлимитного использования опросника, от 90 дней", - "conditionType": "privilege", - "layer": 1, - "condition": { - "privilege": { - "id": "p3", - "value": 90 - } - }, - "target": { - "products": [ - { - "privilegeId": "p3", - "factor": 0.93 - } - ] - } + }, + target: { + products: [ + { + privilegeId: "p6", + factor: 0.935, + }, + ], + }, + }, + { + _id: "id24.1", + name: "Стата Сокращатель 1", + description: "Скидка на дни сбора расширенной статистики от 30 дней", + conditionType: "privilege", + layer: 1, + condition: { + privilege: { + id: "p7", + value: 30, }, - { - "_id": "id13", - "name": "Анлим Квиз 3", - "description": "Скидка на количество дней безлимитного использования опросника, от 180 дней", - "conditionType": "privilege", - "layer": 1, - "condition": { - "privilege": { - "id": "p3", - "value": 180 - } - }, - "target": { - "products": [ - { - "privilegeId": "p3", - "factor": 0.85 - } - ] - } + }, + target: { + products: [ + { + privilegeId: "p7", + factor: 0.935, + }, + ], + }, + }, + { + _id: "id24.2", + name: "Стата Сокращатель 2", + description: "Скидка на дни сбора расширенной статистики от 90 дней", + conditionType: "privilege", + layer: 1, + condition: { + privilege: { + id: "p7", + value: 90, }, - { - "_id": "id14", - "name": "Актив квиз 1", - "description": "Скидка на количество опросов от 100 шт", - "conditionType": "privilege", - "layer": 1, - "condition": { - "privilege": { - "id": "p4", - "value": 100 - } - }, - "target": { - "products": [ - { - "privilegeId": "p4", - "factor": 0.98 - } - ] - } + }, + target: { + products: [ + { + privilegeId: "p7", + factor: 0.875, + }, + ], + }, + }, + { + _id: "id25", + name: "Стата Сокращатель 3", + description: "Скидка на дни сбора расширенной статистики от 180 дней", + conditionType: "privilege", + layer: 1, + condition: { + privilege: { + id: "p7", + value: 180, }, - { - "_id": "id15", - "name": "Актив квиз 2", - "description": "Скидка на количество опросов от 200 шт", - "conditionType": "privilege", - "layer": 1, - "condition": { - "privilege": { - "id": "p4", - "value": 200 - } - }, - "target": { - "products": [ - { - "privilegeId": "p4", - "factor": 0.96 - } - ] - } + }, + target: { + products: [ + { + privilegeId: "p7", + factor: 0.83, + }, + ], + }, + }, + { + _id: "id26", + name: "Шаблонизатор 1", + description: "Скидка на сумму стоимостей товаров, принадлежащих сервису шаблонизации, от 1000 р", + conditionType: "service", + layer: 2, + condition: { + service: { + id: "templategen", + value: 1000, }, - { - "_id": "id16", - "name": "Актив квиз 3", - "description": "Скидка на количество опросов от 350 шт", - "conditionType": "privilege", - "layer": 1, - "condition": { - "privilege": { - "id": "p4", - "value": 350 - } - }, - "target": { - "products": [ - { - "privilegeId": "p4", - "factor": 0.9 - } - ] - } + }, + target: { + service: "templategen", + factor: 0.996, + }, + }, + { + _id: "id27", + name: "Шаблонизатор 2", + description: "Скидка на сумму стоимостей товаров, принадлежащих сервису шаблонизации, от 5000 р", + conditionType: "service", + layer: 2, + condition: { + service: { + id: "templategen", + value: 5000, }, - { - "_id": "id17", - "name": "Анлим Сокращатель 1", - "description": "Скидка на безлимитное использование сокращателя от 30 дней", - "conditionType": "privilege", - "layer": 1, - "condition": { - "privilege": { - "id": "p5", - "value": 30 - } - }, - "target": { - "products": [ - { - "privilegeId": "p5", - "factor": 0.99 - } - ] - } + }, + target: { + service: "templategen", + factor: 0.983, + }, + }, + { + _id: "id28", + name: "Опросник 1", + description: "Скидка на сумму стоимостей товаров, принадлежащих сервису опросника, от 2000 р", + conditionType: "service", + layer: 2, + condition: { + service: { + id: "squiz", + value: 2000, }, - { - "_id": "id18", - "name": "Анлим Сокращатель 2", - "description": "Скидка на безлимитное использование сокращателя от 60 дней", - "conditionType": "privilege", - "layer": 1, - "condition": { - "privilege": { - "id": "p5", - "value": 60 - } - }, - "target": { - "products": [ - { - "privilegeId": "p5", - "factor": 0.98 - } - ] - } + }, + target: { + service: "squiz", + factor: 0.983, + }, + }, + { + _id: "id29", + name: "Опросник 2", + description: "Скидка на сумму стоимостей товаров, принадлежащих сервису опросника, от 6000 р", + conditionType: "service", + layer: 2, + condition: { + service: { + id: "squiz", + value: 6000, }, - { - "_id": "id19", - "name": "Анлим Сокращатель 3", - "description": "Скидка на безлимитное использование сокращателя от 90 дней", - "conditionType": "privilege", - "layer": 1, - "condition": { - "privilege": { - "id": "p5", - "value": 90 - } - }, - "target": { - "products": [ - { - "privilegeId": "p5", - "factor": 0.97 - } - ] - } + }, + target: { + service: "squiz", + factor: 0.969, + }, + }, + { + _id: "id30", + name: "Сокращатель 1", + description: "Скидка на сумму стоимостей товаров, принадлежащих сервису сокращателя, от 500 р", + conditionType: "service", + layer: 2, + condition: { + service: { + id: "dwarfener", + value: 500, }, - { - "_id": "id20", - "name": "АБ Сокращатель 1", - "description": "Скидка на количество АБ тестов, от 10 штук", - "conditionType": "privilege", - "layer": 1, - "condition": { - "privilege": { - "id": "p6", - "value": 10 - } - }, - "target": { - "products": [ - { - "privilegeId": "p6", - "factor": 0.99 - } - ] - } + }, + target: { + service: "dwarfener", + factor: 0.99, + }, + }, + { + _id: "id31", + name: "Сокращатель 2", + description: "Скидка на сумму стоимостей товаров, принадлежащих сервису сокращателя, от 2500 р", + conditionType: "service", + layer: 2, + condition: { + service: { + id: "dwarfener", + value: 2500, }, - { - "_id": "id22", - "name": "АБ Сокращатель 2", - "description": "Скидка на количество АБ тестов, от 25 штук", - "conditionType": "privilege", - "layer": 1, - "condition": { - "privilege": { - "id": "p6", - "value": 25 - } - }, - "target": { - "products": [ - { - "privilegeId": "p6", - "factor": 0.965 - } - ] - } + }, + target: { + service: "dwarfener", + factor: 0.96, + }, + }, + { + _id: "id32", + name: "НКО", + description: + "Скидка всем подтвердившим статус НКО. Перекрывает ВСЕ остальные скидки. Если эта скидка срабатывает, остальные можно не вычислять. Т.е. если на уровне 0 находится какая-лидо скидка для выданных условий, просто суммируем корзину и применяем к сумме указанный множитель, после чего прекращаем процесс рассчёта", + conditionType: "userType", + layer: 0, + condition: { + userType: "nko", + }, + target: { + IsAllProducts: true, + factor: 0.2, + }, + overwhelm: true, + }, + { + _id: "id33", + name: "Промокод На АБ тесты", + description: "Скидка, полученная конкретным юзером, после введения промокода. Заменяет собой не промокодовую", + conditionType: "user", + layer: 1, + condition: { + coupon: "ABCD", // на мой вкус, стоит при активации промокода создавать скидку, привязанную к юзеру по айдишнику, и удалять после использования. т.е. кондишн не по coupon, а по + user: "buddy", + }, + target: { + products: [ + { + privilegeId: "p6", + factor: 0.5, + }, + ], + }, + overwhelm: true, + }, + ], + testCases: [ + { + input: { + UserInformation: { + ID: "buddy", + Type: "", + PurchasesAmount: 0, }, - { - "_id": "id23", - "name": "АБ Сокращатель 3", - "description": "Скидка на количество АБ тестов, от 50 штук", - "conditionType": "privilege", - "layer": 1, - "condition": { - "privilege": { - "id": "p6", - "value": 50 - } - }, - "target": { - "products": [ - { - "privilegeId": "p6", - "factor": 0.935 - } - ] - } + Products: [ + { + ID: "p1", + Amount: 1, // нужно ввести количество, потому что у нас может смениться стоимость одной единицы привилегии, но при этом не поменяться логика скидки, потому что она привязана к количеству единиц привилегии. т.е. чтобы при изменении цены не появились скидки "от 27 дней безлимита" или "от 117 генераций" + Price: 1.0, // если указан прайс, то слои 1 и 2 не применяются, если они overwhelm == false. Т.е. промокоды и скидки на тип юзера всё ещё работают. необходимо для функционирования тарифов, созданных менеджером. для пущщей понятности, тариф создаётся в рамках одного сервиса и если взят готовый тариф, то кастомный на этот сервис сделать уже нельзя, следуя логике "если тебя не устраивает готовый тариф - делай кастомный, не нужно брать готовый и добирать туда недостающего в рамках одной оплаты". как вариант, можно всегда передавать прайс, просто добавить поле кастом. короче, предложенное решение может быть неоптимальным, приведено оно только для того, чтобы показать возможный сценарий. + }, + { + ID: "p2", + Amount: 1, + Price: 2.0, + }, + ], + }, + expect: { + price: 3.0, + envolvedDiscounts: [], + }, + }, + { + input: { + UserInformation: { + ID: "buddy", + Type: "", + PurchasesAmount: 3.0, }, - { - "_id": "id24.1", - "name": "Стата Сокращатель 1", - "description": "Скидка на дни сбора расширенной статистики от 30 дней", - "conditionType": "privilege", - "layer": 1, - "condition": { - "privilege": { - "id": "p7", - "value": 30 - } - }, - "target": { - "products": [ - { - "privilegeId": "p7", - "factor": 0.935 - } - ] - } + Products: [ + { + ID: "p1", + Amount: 40, + Price: 2000.0, + }, + { + ID: "p2", + Amount: 450, + Price: 3000.0, + }, + ], + }, + expect: { + price: 4925.0, + envolvedDiscounts: ["id4"], // сумма в корзине достигла 5к, поэтому применилась скидка + }, + }, + { + input: { + UserInformation: { + ID: "buddy", + Type: "", + PurchasesAmount: 4928.0, }, - { - "_id": "id24.2", - "name": "Стата Сокращатель 2", - "description": "Скидка на дни сбора расширенной статистики от 90 дней", - "conditionType": "privilege", - "layer": 1, - "condition": { - "privilege": { - "id": "p7", - "value": 90 - } - }, - "target": { - "products": [ - { - "privilegeId": "p7", - "factor": 0.875 - } - ] - } + Products: [ + { + ID: "p1", + Amount: 40, + Price: 2000.0, + }, + { + ID: "p2", + Amount: 450, + Price: 3000.0, + }, + { + ID: "p3", + Amount: 35, + }, + ], + }, + expect: { + price: 5025.32, + envolvedDiscounts: ["id4", "id11"], // добавил кастомный тариф такой, чтобы пофвилась скидка на продукт + }, + }, + { + input: { + UserInformation: { + ID: "buddy", + Type: "", + PurchasesAmount: 9953.32, }, - { - "_id": "id25", - "name": "Стата Сокращатель 3", - "description": "Скидка на дни сбора расширенной статистики от 180 дней", - "conditionType": "privilege", - "layer": 1, - "condition": { - "privilege": { - "id": "p7", - "value": 180 - } - }, - "target": { - "products": [ - { - "privilegeId": "p7", - "factor": 0.83 - } - ] - } + Products: [ + { + ID: "p1", + Amount: 40, + Price: 2000.0, + }, + { + ID: "p2", + Amount: 450, + Price: 3000.0, + }, + { + ID: "p3", + Amount: 35, + }, + { + ID: "p4", + Amount: 210, + }, + ], + }, + expect: { + price: 5223.9, + envolvedDiscounts: ["id4", "id11", "id15"], // т.е. применилась не id14, а id15, потому что применяется наибольшая подходящая. в то же время, на скидку за лояльность ещё не хватает + }, + }, + { + input: { + UserInformation: { + ID: "buddy", + Type: "", + PurchasesAmount: 15177.22, // округляю до копеек }, - { - "_id": "id26", - "name": "Шаблонизатор 1", - "description": "Скидка на сумму стоимостей товаров, принадлежащих сервису шаблонизации, от 1000 р", - "conditionType": "service", - "layer": 2, - "condition": { - "service": { - "id": "templategen", - "value": 1000 - } - }, - "target": { - "service": "templategen", - "factor": 0.996 - } + Products: [ + { + ID: "p1", + Amount: 40, + Price: 2000.0, + }, + { + ID: "p2", + Amount: 450, + Price: 3000.0, + }, + { + ID: "p3", + Amount: 35, + }, + { + ID: "p4", + Amount: 210, + }, + ], + }, + expect: { + price: 5171.66, + envolvedDiscounts: ["id4", "id11", "id15", "id1"], + }, + }, + { + input: { + UserInformation: { + ID: "buddy", + Type: "", + PurchasesAmount: 20348.88, }, - { - "_id": "id27", - "name": "Шаблонизатор 2", - "description": "Скидка на сумму стоимостей товаров, принадлежащих сервису шаблонизации, от 5000 р", - "conditionType": "service", - "layer": 2, - "condition": { - "service": { - "id": "templategen", - "value": 5000 - } - }, - "target": { - "service": "templategen", - "factor": 0.983 - } + Products: [ + { + ID: "p5", + Amount: 300, + }, + { + ID: "p6", + Amount: 100, + }, + { + ID: "p7", + Amount: 200, + }, + ], + }, + expect: { + price: 422.28, + envolvedDiscounts: ["id19", "id23", "id25", "id1"], // история про то, как скидки за привилегии помешали получить скидку за сервис + }, + }, + { + input: { + UserInformation: { + ID: "dude", + Type: "", + PurchasesAmount: 0.0, }, - { - "_id": "id28", - "name": "Опросник 1", - "description": "Скидка на сумму стоимостей товаров, принадлежащих сервису опросника, от 2000 р", - "conditionType": "service", - "layer": 2, - "condition": { - "service": { - "id": "squiz", - "value": 2000 - } - }, - "target": { - "service": "squiz", - "factor": 0.983 - } + Products: [ + { + ID: "p5", + Amount: 300, + }, + { + ID: "p6", + Amount: 100, + }, + { + ID: "p7", + Amount: 200, + }, + ], + }, + expect: { + price: 426.55, + envolvedDiscounts: ["id19", "id23", "id25"], // то же что и выше, но без лояльности + }, + }, + { + input: { + UserInformation: { + ID: "buddy", + Type: "", + PurchasesAmount: 20771.16, }, - { - "_id": "id29", - "name": "Опросник 2", - "description": "Скидка на сумму стоимостей товаров, принадлежащих сервису опросника, от 6000 р", - "conditionType": "service", - "layer": 2, - "condition": { - "service": { - "id": "squiz", - "value": 6000 - } - }, - "target": { - "service": "squiz", - "factor": 0.969 - } + Products: [ + { + ID: "p5", + Amount: 300, + }, + { + ID: "p6", + Amount: 100, + }, + { + ID: "p7", + Amount: 400, + }, + ], + }, + expect: { + price: 743.45, + envolvedDiscounts: ["id19", "id23", "id25", "id30", "id1"], // история про то, как получилось получить скидку за сервис + }, + }, + { + input: { + UserInformation: { + ID: "buddy", + Type: "", + PurchasesAmount: 21522.12, }, - { - "_id": "id30", - "name": "Сокращатель 1", - "description": "Скидка на сумму стоимостей товаров, принадлежащих сервису сокращателя, от 500 р", - "conditionType": "service", - "layer": 2, - "condition": { - "service": { - "id": "dwarfener", - "value": 500 - } - }, - "target": { - "service": "dwarfener", - "factor": 0.99 - } + Products: [ + { + ID: "p1", + Amount: 3000, + }, + { + ID: "p2", + Amount: 3000, + }, + { + ID: "p5", + Amount: 300, + }, + { + ID: "p6", + Amount: 100, + }, + { + ID: "p7", + Amount: 400, + }, + ], + }, + expect: { + price: 2398.53, + envolvedDiscounts: ["id8.rev", "id10", "id19", "id23", "id25", "id26", "id30", "id1"], // две скидки за сервис + }, + }, + { + input: { + UserInformation: { + ID: "buddy", + Type: "", + PurchasesAmount: 23920.65, }, - { - "_id": "id31", - "name": "Сокращатель 2", - "description": "Скидка на сумму стоимостей товаров, принадлежащих сервису сокращателя, от 2500 р", - "conditionType": "service", - "layer": 2, - "condition": { - "service": { - "id": "dwarfener", - "value": 2500 - } - }, - "target": { - "service": "dwarfener", - "factor": 0.96 - } + Products: [ + { + ID: "p1", + Amount: 3000, + }, + { + ID: "p2", + Amount: 3000, + }, + { + ID: "p5", + Amount: 300, + }, + { + ID: "p6", + Amount: 100, + }, + { + ID: "p7", + Amount: 400, + }, + ], + }, + expect: { + price: 2368.68, + envolvedDiscounts: ["id8.rev", "id10", "id19", "id33", "id25", "id26", "id30", "id1"], // юзер использовал промокод id33. он заменяет скидку на p6 собой. в один момент времени может быть активирован только 1 промокод, т.е. после активации следующего, предыдущий заменяется. но в промокоде может быть несколько скидок. промокоды имеют скидки только на привелеги + }, + }, + { + input: { + UserInformation: { + ID: "buddy", + Type: "nko", + PurchasesAmount: 26289.33, }, - { - "_id": "id32", - "name": "НКО", - "description": "Скидка всем подтвердившим статус НКО. Перекрывает ВСЕ остальные скидки. Если эта скидка срабатывает, остальные можно не вычислять. Т.е. если на уровне 0 находится какая-лидо скидка для выданных условий, просто суммируем корзину и применяем к сумме указанный множитель, после чего прекращаем процесс рассчёта", - "conditionType": "userType", - "layer": 0, - "condition": { - "userType": "nko" - }, - "target": { - "IsAllProducts": true, - "factor": 0.2 - }, - "overwhelm": true + Products: [ + { + ID: "p1", + Amount: 3000, + }, + { + ID: "p2", + Amount: 3000, + }, + { + ID: "p5", + Amount: 300, + }, + { + ID: "p6", + Amount: 100, + }, + { + ID: "p7", + Amount: 400, + }, + ], + }, + expect: { + price: 540, // сложил всю корзину и умножил на показатель скидки + envolvedDiscounts: ["id32"], // юзер подтвердил свой статус НКО, поэтому, не смотря на то что он достиг по лояльности уровня скидки id2, она не применилась, а применилась id32 + }, + }, + { + input: { + UserInformation: { + ID: "buddy", + Type: "", + PurchasesAmount: 0, }, - { - "_id": "id33", - "name": "Промокод На АБ тесты", - "description": "Скидка, полученная конкретным юзером, после введения промокода. Заменяет собой не промокодовую", - "conditionType": "user", - "layer": 1, - "condition": { - "coupon": "ABCD", // на мой вкус, стоит при активации промокода создавать скидку, привязанную к юзеру по айдишнику, и удалять после использования. т.е. кондишн не по coupon, а по - "user": "buddy" - }, - "target": { - "products": [ - { - "privilegeId": "p6", - "factor": 0.5 - } - ] - }, - "overwhelm": true - } - ], - "testCases": [ - { - "input": { - "UserInformation": { - "ID": "buddy", - "Type": "", - "PurchasesAmount": 0 - }, - "Products": [ - { - "ID": "p1", - "Amount": 1, // нужно ввести количество, потому что у нас может смениться стоимость одной единицы привилегии, но при этом не поменяться логика скидки, потому что она привязана к количеству единиц привилегии. т.е. чтобы при изменении цены не появились скидки "от 27 дней безлимита" или "от 117 генераций" - "Price": 1.0 // если указан прайс, то слои 1 и 2 не применяются, если они overwhelm == false. Т.е. промокоды и скидки на тип юзера всё ещё работают. необходимо для функционирования тарифов, созданных менеджером. для пущщей понятности, тариф создаётся в рамках одного сервиса и если взят готовый тариф, то кастомный на этот сервис сделать уже нельзя, следуя логике "если тебя не устраивает готовый тариф - делай кастомный, не нужно брать готовый и добирать туда недостающего в рамках одной оплаты". как вариант, можно всегда передавать прайс, просто добавить поле кастом. короче, предложенное решение может быть неоптимальным, приведено оно только для того, чтобы показать возможный сценарий. - }, - { - "ID": "p2", - "Amount": 1, - "Price": 2.0 - }, - ] - }, - "expect": { - "price": 3.0, - "envolvedDiscounts": [] - } - }, - { - "input": { - "UserInformation": { - "ID": "buddy", - "Type": "", - "PurchasesAmount": 3.0 - }, - "Products": [ - { - "ID": "p1", - "Amount": 40, - "Price": 2000.0 - }, - { - "ID": "p2", - "Amount": 450, - "Price": 3000.0 - }, - ] - }, - "expect": { - "price": 4925.0, - "envolvedDiscounts": [ - "id4" - ] // сумма в корзине достигла 5к, поэтому применилась скидка - } - }, - { - "input": { - "UserInformation": { - "ID": "buddy", - "Type": "", - "PurchasesAmount": 4928.0 - }, - "Products": [ - { - "ID": "p1", - "Amount": 40, - "Price": 2000.0 - }, - { - "ID": "p2", - "Amount": 450, - "Price": 3000.0 - }, - { - "ID": "p3", - "Amount": 35, - } - ] - }, - "expect": { - "price": 5025.32, - "envolvedDiscounts": [ - "id4", - "id11" - ] // добавил кастомный тариф такой, чтобы пофвилась скидка на продукт - } - }, - { - "input": { - "UserInformation": { - "ID": "buddy", - "Type": "", - "PurchasesAmount": 9953.32 - }, - "Products": [ - { - "ID": "p1", - "Amount": 40, - "Price": 2000.0 - }, - { - "ID": "p2", - "Amount": 450, - "Price": 3000.0 - }, - { - "ID": "p3", - "Amount": 35, - }, - { - "ID": "p4", - "Amount": 210, - } - ] - }, - "expect": { - "price": 5223.9, - "envolvedDiscounts": [ - "id4", - "id11", - "id15" - ] // т.е. применилась не id14, а id15, потому что применяется наибольшая подходящая. в то же время, на скидку за лояльность ещё не хватает - } - }, - { - "input": { - "UserInformation": { - "ID": "buddy", - "Type": "", - "PurchasesAmount": 15177.22 // округляю до копеек - }, - "Products": [ - { - "ID": "p1", - "Amount": 40, - "Price": 2000.0 - }, - { - "ID": "p2", - "Amount": 450, - "Price": 3000.0 - }, - { - "ID": "p3", - "Amount": 35, - }, - { - "ID": "p4", - "Amount": 210, - } - ] - }, - "expect": { - "price": 5171.66, - "envolvedDiscounts": [ - "id4", - "id11", - "id15", - "id1" - ] - } - }, - { - "input": { - "UserInformation": { - "ID": "buddy", - "Type": "", - "PurchasesAmount": 20348.88 - }, - "Products": [ - { - "ID": "p5", - "Amount": 300, - }, - { - "ID": "p6", - "Amount": 100, - }, - { - "ID": "p7", - "Amount": 200, - } - ] - }, - "expect": { - "price": 422.28, - "envolvedDiscounts": [ - "id19", - "id23", - "id25", - "id1" - ] // история про то, как скидки за привилегии помешали получить скидку за сервис - } - }, - { - "input": { - "UserInformation": { - "ID": "dude", - "Type": "", - "PurchasesAmount": 0.0 - }, - "Products": [ - { - "ID": "p5", - "Amount": 300, - }, - { - "ID": "p6", - "Amount": 100, - }, - { - "ID": "p7", - "Amount": 200, - } - ] - }, - "expect": { - "price": 426.55, - "envolvedDiscounts": [ - "id19", - "id23", - "id25" - ] // то же что и выше, но без лояльности - } - }, - { - "input": { - "UserInformation": { - "ID": "buddy", - "Type": "", - "PurchasesAmount": 20771.16 - }, - "Products": [ - { - "ID": "p5", - "Amount": 300, - }, - { - "ID": "p6", - "Amount": 100, - }, - { - "ID": "p7", - "Amount": 400, - } - ] - }, - "expect": { - "price": 743.45, - "envolvedDiscounts": [ - "id19", - "id23", - "id25", - "id30", - "id1" - ] // история про то, как получилось получить скидку за сервис - } - }, - { - "input": { - "UserInformation": { - "ID": "buddy", - "Type": "", - "PurchasesAmount": 21522.12 - }, - "Products": [ - { - "ID": "p1", - "Amount": 3000, - }, - { - "ID": "p2", - "Amount": 3000, - }, - { - "ID": "p5", - "Amount": 300, - }, - { - "ID": "p6", - "Amount": 100, - }, - { - "ID": "p7", - "Amount": 400, - } - ] - }, - "expect": { - "price": 2398.53, - "envolvedDiscounts": [ - "id8.rev", - "id10", - "id19", - "id23", - "id25", - "id26", - "id30", - "id1" - ] // две скидки за сервис - } - }, - { - "input": { - "UserInformation": { - "ID": "buddy", - "Type": "", - "PurchasesAmount": 23920.65 - }, - "Products": [ - { - "ID": "p1", - "Amount": 3000, - }, - { - "ID": "p2", - "Amount": 3000, - }, - { - "ID": "p5", - "Amount": 300, - }, - { - "ID": "p6", - "Amount": 100, - }, - { - "ID": "p7", - "Amount": 400, - } - ] - }, - "expect": { - "price": 2368.68, - "envolvedDiscounts": [ - "id8.rev", - "id10", - "id19", - "id33", - "id25", - "id26", - "id30", - "id1" - ] // юзер использовал промокод id33. он заменяет скидку на p6 собой. в один момент времени может быть активирован только 1 промокод, т.е. после активации следующего, предыдущий заменяется. но в промокоде может быть несколько скидок. промокоды имеют скидки только на привелеги - } - }, - { - "input": { - "UserInformation": { - "ID": "buddy", - "Type": "nko", - "PurchasesAmount": 26289.33 - }, - "Products": [ - { - "ID": "p1", - "Amount": 3000, - }, - { - "ID": "p2", - "Amount": 3000, - }, - { - "ID": "p5", - "Amount": 300, - }, - { - "ID": "p6", - "Amount": 100, - }, - { - "ID": "p7", - "Amount": 400, - } - ] - }, - "expect": { - "price": 540, // сложил всю корзину и умножил на показатель скидки - "envolvedDiscounts": [ - "id32" - ] // юзер подтвердил свой статус НКО, поэтому, не смотря на то что он достиг по лояльности уровня скидки id2, она не применилась, а применилась id32 - } - }, - { - "input": { - "UserInformation": { - "ID": "buddy", - "Type": "", - "PurchasesAmount": 0 - }, - "Products": [ - { - "ID": "p7", - "Amount": 4000, - "Price": 12000 - }, - ] - }, - "expect": { - "price": 12000 * 0.985 * 0.96, - "envolvedDiscounts": [ - "id4", - "id31" - ] - } - }, - ] -}; \ No newline at end of file + Products: [ + { + ID: "p7", + Amount: 4000, + Price: 12000, + }, + ], + }, + expect: { + price: 12000 * 0.985 * 0.96, + envolvedDiscounts: ["id4", "id31"], + }, + }, + ], +}; diff --git a/src/stores/privileges.ts b/src/stores/privileges.ts index 073f80f..e99d7ec 100644 --- a/src/stores/privileges.ts +++ b/src/stores/privileges.ts @@ -3,66 +3,62 @@ import { create } from "zustand"; import { devtools } from "zustand/middleware"; import { exampleCartValues } from "./mocks/exampleCartValues"; - interface PrivilegeStore { - privileges: Privilege[]; - isModalOpen: boolean; - modalPrivilegeId: string | null; - modalPriceField: string; - addPrivileges: (newPrivileges: Privilege[]) => void; + privileges: Privilege[]; + isModalOpen: boolean; + modalPrivilegeId: string | null; + modalPriceField: string; + addPrivileges: (newPrivileges: Privilege[]) => void; } export const usePrivilegeStore = create()( - devtools( - (set, get) => ({ - privileges: exampleCartValues.privileges, - isModalOpen: false, - modalPrivilegeId: null, - modalPriceField: "", - addPrivileges: newPrivileges => set(state => ( - { privileges: [...state.privileges, ...newPrivileges] } - )), - }), - { - name: "Privilege store" - } - ) + devtools( + (set, get) => ({ + privileges: exampleCartValues.privileges, + isModalOpen: false, + modalPrivilegeId: null, + modalPriceField: "", + addPrivileges: (newPrivileges) => set((state) => ({ privileges: [...state.privileges, ...newPrivileges] })), + }), + { + name: "Privilege store", + } + ) ); export const closePrivilegePriceModal = () => usePrivilegeStore.setState({ isModalOpen: false }); -export const openPrivilegePriceModal = (modalPrivilegeId: string | null, defaultPrice: number) => usePrivilegeStore.setState( - { - isModalOpen: true, - modalPriceField: defaultPrice.toString(), - modalPrivilegeId, - } -); +export const openPrivilegePriceModal = (modalPrivilegeId: string | null, defaultPrice: number) => + usePrivilegeStore.setState({ + isModalOpen: true, + modalPriceField: defaultPrice.toString(), + modalPrivilegeId, + }); export const changeModalPriceField = (modalPriceField: string) => usePrivilegeStore.setState({ modalPriceField }); export const changePrivilegePrice = () => { - const { privileges, modalPrivilegeId, modalPriceField } = usePrivilegeStore.getState(); + const { privileges, modalPrivilegeId, modalPriceField } = usePrivilegeStore.getState(); - const privilegeIndex = privileges.findIndex(privilege => privilege.privilegeId === modalPrivilegeId); - if (privilegeIndex === -1) throw new Error("Privilege not found by id"); + const privilegeIndex = privileges.findIndex((privilege) => privilege.privilegeId === modalPrivilegeId); + if (privilegeIndex === -1) throw new Error("Privilege not found by id"); - const price = parseFloat(modalPriceField.replace(",", ".")); - if (!isFinite(price)) return "Error parsing price"; + const price = parseFloat(modalPriceField.replace(",", ".")); + if (!isFinite(price)) return "Error parsing price"; - const newPrivilege: Privilege = { - ...privileges[privilegeIndex], - pricePerUnit: price, - }; + const newPrivilege: Privilege = { + ...privileges[privilegeIndex], + price: price, + }; - const newPrivileges = [...privileges]; - newPrivileges.splice(privilegeIndex, 1, newPrivilege); + const newPrivileges = [...privileges]; + newPrivileges.splice(privilegeIndex, 1, newPrivilege); - usePrivilegeStore.setState({ - privileges: newPrivileges, - isModalOpen: false, - }); + usePrivilegeStore.setState({ + privileges: newPrivileges, + isModalOpen: false, + }); }; export const findPrivilegeById = (privilegeId: string) => { - return usePrivilegeStore.getState().privileges.find(privilege => privilege.privilegeId === privilegeId) ?? null; -}; \ No newline at end of file + return usePrivilegeStore.getState().privileges.find((privilege) => privilege.privilegeId === privilegeId) ?? null; +};