import { CartItem, AnyDiscount, CartTotal, CartItemTotal, PrivilegeDiscount, CartPurchasesAmountDiscount, PurchasesAmountDiscount, ServiceToPriceMap, ServiceDiscount, UserDiscount } from "@root/model/cart"; import { ServiceType, SERVICE_LIST, Tariff } from "@root/model/tariff"; import { User } from "@root/model/user"; export function calcCartData( user: User, cartItems: CartItem[], discounts: AnyDiscount[], coupon?: string, ): CartTotal { const cartTotal: CartTotal = { items: [], totalPrice: 0, priceByService: { templategen: { defaultTariffs: 0, customTariffs: 0, }, squiz: { defaultTariffs: 0, customTariffs: 0, }, dwarfener: { defaultTariffs: 0, customTariffs: 0, }, }, envolvedCartDiscounts: [], }; // layer 0 for (const discount of discounts) { if (discount.conditionType !== "userType" || discount.condition.userType !== user.Type) continue; cartItems.forEach(cartItem => { cartTotal.items.push({ envolvedDiscounts: [], tariff: cartItem.tariff, totalPrice: cartItem.price, }); cartTotal.priceByService[cartItem.tariff.privilege.serviceKey].defaultTariffs += cartItem.price; cartTotal.totalPrice += cartItem.price; }); cartTotal.totalPrice *= discount.target.factor; cartTotal.envolvedCartDiscounts.push(discount._id); 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.privilege.privilegeId && (tariff.customPricePerUnit === undefined || couponDiscount.overwhelm) ) { cartItemTotal.totalPrice *= product.factor; cartItemTotal.envolvedDiscounts.push(couponDiscount._id); privilegesAffectedByCoupon.push(product.privilegeId); } }); const privilegeDiscount = findMaxApplicablePrivilegeDiscount(discounts, tariff); privilegeDiscount?.target.products.forEach(product => { if ( product.privilegeId === tariff.privilege.privilegeId && tariff.customPricePerUnit === undefined && !privilegesAffectedByCoupon.includes(privilegeDiscount.condition.privilege.id) ) { cartItemTotal.totalPrice *= product.factor; cartItemTotal.envolvedDiscounts.push(privilegeDiscount._id); } }); cartTotal.items.push(cartItemTotal); if (tariff.customPricePerUnit === undefined) cartTotal.priceByService[tariff.privilege.serviceKey].defaultTariffs += cartItemTotal.totalPrice; else cartTotal.priceByService[tariff.privilege.serviceKey].customTariffs += cartItemTotal.totalPrice; } // layer 2 SERVICE_LIST.map(service => service.serviceKey).forEach(service => { const serviceDiscount = findMaxServiceDiscount(service, discounts, cartTotal.priceByService); if (serviceDiscount) { cartTotal.priceByService[service].defaultTariffs *= serviceDiscount.target.factor; if (cartTotal.priceByService[service].defaultTariffs > 0) cartTotal.envolvedCartDiscounts.push(serviceDiscount._id); } cartTotal.totalPrice += cartTotal.priceByService[service].defaultTariffs; cartTotal.totalPrice += cartTotal.priceByService[service].customTariffs; }); // layer 3 const cartPurchasesAmountDiscount = findMaxCartPurchasesAmountDiscount(discounts, cartTotal); if (cartPurchasesAmountDiscount) { cartTotal.totalPrice *= cartPurchasesAmountDiscount.factor; cartTotal.envolvedCartDiscounts.push(cartPurchasesAmountDiscount._id); } // layer 4 const totalPurchasesAmountDiscount = findMaxTotalPurchasesAmountDiscount(discounts, user); if (totalPurchasesAmountDiscount) { cartTotal.totalPrice *= totalPurchasesAmountDiscount.factor; cartTotal.envolvedCartDiscounts.push(totalPurchasesAmountDiscount._id); } return cartTotal; } function findMaxApplicablePrivilegeDiscount(discounts: AnyDiscount[], tariff: Tariff): PrivilegeDiscount | null { const applicableDiscounts = discounts.filter((discount): discount is PrivilegeDiscount => { return ( discount.conditionType === "privilege" && tariff.privilege.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 ); 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 ); return maxValueDiscount; } function findMaxTotalPurchasesAmountDiscount(discounts: AnyDiscount[], user: User): PurchasesAmountDiscount | null { const applicableDiscounts = discounts.filter((discount): discount is PurchasesAmountDiscount => { return discount.conditionType === "purchasesAmount" && user.PurchasesAmount >= discount.condition.purchasesAmount; }); if (!applicableDiscounts.length) return null; const maxValueDiscount = applicableDiscounts.reduce( (prev, current) => current.condition.purchasesAmount > prev.condition.purchasesAmount ? current : prev ); return maxValueDiscount; } function findMaxServiceDiscount( 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].defaultTariffs + priceByService[service].customTariffs >= discount.condition.service.value ); }); if (!discountsForTariffService.length) return null; const maxValueDiscount = discountsForTariffService.reduce((prev, current) => { return current.condition.service.value > prev.condition.service.value ? current : prev; }); 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 ); }); return userDiscount ?? null; } export function createCartItem(tariff: Tariff): CartItem { const pricePerUnit = tariff.customPricePerUnit ?? tariff.privilege.pricePerUnit; const price = pricePerUnit * tariff.amount; return { tariff, price, id: "someId" }; } export function findDiscountById(discounts: AnyDiscount[], id: string) { const discount = discounts.find(discount => discount._id === id); if (!discount) throw new Error("Discount not found by id"); return discount; } export function findDiscountFactorById(discounts: AnyDiscount[], id: string, privilegeId?: string) { const discount = discounts.find(discount => discount._id === id); if (!discount) throw new Error("Discount not found by id"); switch (discount.conditionType) { case "cartPurchasesAmount": return discount.factor; case "purchasesAmount": return discount.factor; case "privilege": { const product = discount.target.products.find(product => product.privilegeId === privilegeId); if (!product) throw new Error("Discount target product not found"); return product.factor; } case "user": { const product = discount.target.products.find(product => product.privilegeId === privilegeId); 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)}%`; } export const PositiveInput = (event: any) => { const numberInput = parseInt(event.target.value); if (isNaN(numberInput) || numberInput < 0) { event.target.value = '0'; } };