UNPKG

pricing4ts

Version:

![NPM Version](https://img.shields.io/npm/v/pricing4ts) Pricing4TS is a TypeScript-based toolkit designed to enhance the server-side functionality of a pricing-driven SaaS by enabling the seamless integration of pricing plans into the application logic. T

218 lines (185 loc) 11.6 kB
import { AddOn, Plan, Pricing } from '../../types'; import PricingCSP from '../models/minizinc/minizinc'; import { pricing2DZN } from '../utils/dzn-exporter/pricing-dzn-exporter'; import { PricingOperation } from '../models/minizinc/minizinc'; import { ErrorMessage } from 'minizinc'; import { CspSolution } from '../types'; import { explain } from '../utils/minizinc-explanator'; import { getPlanPrices } from '../../main/models/pricing2yaml/plan'; import { getAddOnPrices } from '../../main/models/pricing2yaml/addon'; export interface PricingAnalytics { numberOfFeatures: number; numberOfUsageLimits: number; numberOfPlans: number; numberOfAddOns: number; configurationSpaceSize: number; minSubscriptionPrice: number; maxSubscriptionPrice: number; } export interface AnalyticsOptions { printDzn: boolean; } const defaultAnalyticsOptions: AnalyticsOptions = { printDzn: false } export default class PricingService { private readonly pricing: Pricing; constructor(pricing: Pricing){ this.pricing = pricing; } async runPricingOperation(pricingOperation: PricingOperation) { const dznPricing = pricing2DZN(this.pricing); const model = new PricingCSP(); return model.runPricingOperation(pricingOperation, dznPricing); } async getConfigurationSpace(){ const dznPricing = pricing2DZN(this.pricing); try{ const configurationSpace = []; const result = await this._getConfigurationSpace(dznPricing); const allSolutions: CspSolution[] = result.allSolutions!; for (const solution of allSolutions){ const selectedPlan = Object.keys(this.pricing.plans!)[solution.selected_plan - 1]; const selectedAddons = solution.selected_addons.map((addonIndex, index) => addonIndex === 1 ? Object.keys(this.pricing.addOns!)[index] : null).filter(addon => addon !== null); const subscriptionFeatures = solution.subscription_features.map((featureIndex, index) => featureIndex === 1 ? Object.keys(this.pricing.features)[index] : null).filter(feature => feature !== null); const subscriptionUsageLimits = solution.subscription_usage_limits.map((usageLimitIndex, index) => usageLimitIndex === 1 ? Object.keys(this.pricing.usageLimits!)[index] : null).filter(usageLimit => usageLimit !== null); configurationSpace.push({ selectedPlan: selectedPlan, selectedAddons: selectedAddons, subscriptionFeatures: subscriptionFeatures, subscriptionUsageLimits: subscriptionUsageLimits, subscriptionCost: solution.subscription_cost }); } return configurationSpace; }catch(e){ throw new Error((e as ErrorMessage).message); } } async getAnalytics(analyticsOptions?: AnalyticsOptions) { if (!analyticsOptions){ analyticsOptions = defaultAnalyticsOptions; }else{ analyticsOptions = {...defaultAnalyticsOptions, ...analyticsOptions}; } try{ const dznPricing = pricing2DZN(this.pricing); if (analyticsOptions.printDzn){ console.log(dznPricing); } // Run the Minizinc models for needed anayltics const [configurationSpaceResult, minSubscriptionPriceResult, maxSubscriptionPriceResult] = await Promise.all([ this._getConfigurationSpace(dznPricing), this._getMinSubscriptionPrice(dznPricing), this._getMaxSubscriptionPrice(dznPricing) ]).catch(e => { throw new Error(explain(e, this.pricing)); }); // Extract the list of features and usage limits by type const informationFeatures = Object.values(this.pricing.features).filter(f => f.type === "INFORMATION"); const integrationFeatures = Object.values(this.pricing.features).filter(f => f.type === "INTEGRATION"); const domainFeatures = Object.values(this.pricing.features).filter(f => f.type === "DOMAIN"); const automationFeatures = Object.values(this.pricing.features).filter(f => f.type === "AUTOMATION"); const managementFeatures = Object.values(this.pricing.features).filter(f => f.type === "MANAGEMENT"); const guaranteeFeatures = Object.values(this.pricing.features).filter(f => f.type === "GUARANTEE"); const supportFeatures = Object.values(this.pricing.features).filter(f => f.type === "SUPPORT"); const paymentFeatures = Object.values(this.pricing.features).filter(f => f.type === "PAYMENT"); // Extraction of the analytics const numberOfFeatures: number = Object.values(this.pricing.features).length; const numberOfInformationFeatures: number = informationFeatures.length; const numberOfIntegrationFeatures: number = integrationFeatures.length; const numberOfIntegrationApiFeatures: number = integrationFeatures.filter(f => f.integrationType === "API").length; const numberOfIntegrationExtensionFeatures: number = integrationFeatures.filter(f => f.integrationType === "EXTENSION").length; const numberOfIntegrationIdentityProviderFeatures: number = integrationFeatures.filter(f => f.integrationType === "IDENTITY_PROVIDER").length; const numberOfIntegrationWebSaaSFeatures: number = integrationFeatures.filter(f => f.integrationType === "WEB_SAAS").length; const numberOfIntegrationMarketplaceFeatures: number = integrationFeatures.filter(f => f.integrationType === "MARKETPLACE").length; const numberOfIntegrationExternalDeviceFeatures: number = integrationFeatures.filter(f => f.integrationType === "EXTERNAL_DEVICE").length; const numberOfDomainFeatures: number = domainFeatures.length; const numberOfAutomationFeatures: number = automationFeatures.length; const numberOfBotAutomationFeatures: number = automationFeatures.filter(f => f.automationType === "BOT").length; const numberOfFilteringAutomationFeatures: number = automationFeatures.filter(f => f.automationType === "FILTERING").length; const numberOfTrackingAutomationFeatures: number = automationFeatures.filter(f => f.automationType === "TRACKING").length; const numberOfTaskAutomationFeatures: number = automationFeatures.filter(f => f.automationType === "TASK_AUTOMATION").length; const numberOfManagementFeatures: number = managementFeatures.length; const numberOfGuaranteeFeatures: number = guaranteeFeatures.length; const numberOfSupportFeatures: number = supportFeatures.length; const numberOfPaymentFeatures: number = paymentFeatures.length; const numberOfUsageLimits: number = this.pricing.usageLimits ? Object.values(this.pricing.usageLimits).length : 0; const numberOfRenewableUsageLimits: number = this.pricing.usageLimits ? Object.values(this.pricing.usageLimits).filter(ul => ul.type === "RENEWABLE").length : 0; const numberOfNonRenewableUsageLimits: number = this.pricing.usageLimits ? Object.values(this.pricing.usageLimits).filter(ul => ul.type === "NON_RENEWABLE").length : 0; const numberOfPlans: number = this.pricing.plans ? Object.values(this.pricing.plans).length : 0; const numberOfFreePlans: number = this.pricing.plans ? Object.values(this.pricing.plans).filter(p => p.price === 0).length : 0; const numberOfPaidPlans: number = this.pricing.plans ? Object.values(this.pricing.plans).filter(p => typeof(p.price) === "number" ? p.price > 0 : true).length : 0; const numberOfAddOns: number = this.pricing.addOns ? Object.values(this.pricing.addOns).length : 0; const numberOfReplacementAddons: number = this.pricing.addOns ? Object.values(this.pricing.addOns).filter(a => a.features || a.usageLimits).length : 0; const numberOfExtensionAddons: number = this.pricing.addOns ? Object.values(this.pricing.addOns).filter(a => a.features || a.usageLimits ? false : a.usageLimitsExtensions).length : 0; const configurationSpaceSize: number = configurationSpaceResult.statistics.nSolutions; const minSubscriptionPrice: number = configurationSpaceSize !== 0 ? this._computeConfigurationPrice(minSubscriptionPriceResult.solution!.output.json!) : NaN; const maxSubscriptionPrice: number = configurationSpaceSize !== 0 ? this._computeConfigurationPrice(maxSubscriptionPriceResult.solution!.output.json!) : NaN; return { numberOfFeatures: numberOfFeatures, numberOfInformationFeatures: numberOfInformationFeatures, numberOfIntegrationFeatures: numberOfIntegrationFeatures, numberOfIntegrationApiFeatures: numberOfIntegrationApiFeatures, numberOfIntegrationExtensionFeatures: numberOfIntegrationExtensionFeatures, numberOfIntegrationIdentityProviderFeatures: numberOfIntegrationIdentityProviderFeatures, numberOfIntegrationWebSaaSFeatures: numberOfIntegrationWebSaaSFeatures, numberOfIntegrationMarketplaceFeatures: numberOfIntegrationMarketplaceFeatures, numberOfIntegrationExternalDeviceFeatures: numberOfIntegrationExternalDeviceFeatures, numberOfDomainFeatures: numberOfDomainFeatures, numberOfAutomationFeatures: numberOfAutomationFeatures, numberOfBotAutomationFeatures: numberOfBotAutomationFeatures, numberOfFilteringAutomationFeatures: numberOfFilteringAutomationFeatures, numberOfTrackingAutomationFeatures: numberOfTrackingAutomationFeatures, numberOfTaskAutomationFeatures: numberOfTaskAutomationFeatures, numberOfManagementFeatures: numberOfManagementFeatures, numberOfGuaranteeFeatures: numberOfGuaranteeFeatures, numberOfSupportFeatures: numberOfSupportFeatures, numberOfPaymentFeatures: numberOfPaymentFeatures, numberOfUsageLimits: numberOfUsageLimits, numberOfRenewableUsageLimits: numberOfRenewableUsageLimits, numberOfNonRenewableUsageLimits: numberOfNonRenewableUsageLimits, numberOfPlans: numberOfPlans, numberOfFreePlans: numberOfFreePlans, numberOfPaidPlans: numberOfPaidPlans, numberOfAddOns: numberOfAddOns, numberOfReplacementAddons: numberOfReplacementAddons, numberOfExtensionAddons: numberOfExtensionAddons, configurationSpaceSize: configurationSpaceSize, minSubscriptionPrice: parseFloat(minSubscriptionPrice.toFixed(2)), maxSubscriptionPrice: parseFloat(maxSubscriptionPrice.toFixed(2)), }; }catch(e){ throw new Error((e as ErrorMessage).message); } } async _getConfigurationSpace(dznPricing: string) { const model = new PricingCSP(); return model.runPricingOperation(PricingOperation.CONFIGURATION_SPACE, dznPricing); } async _getMinSubscriptionPrice(dznPricing: string) { const model = new PricingCSP(); return model.runPricingOperation(PricingOperation.CHEAPEST_SUBSCRIPTION, dznPricing); } async _getMaxSubscriptionPrice(dznPricing: string) { const model = new PricingCSP(); return model.runPricingOperation(PricingOperation.MOST_EXPENSIVE_SUBSCRIPTION, dznPricing); } _computeConfigurationPrice(minizincSolution: Record<string, any>): number { const plansPrices = getPlanPrices(this.pricing.plans); const addOnPrices = getAddOnPrices(this.pricing.addOns); let configurationPrice = 0; if (plansPrices.length > 0){ configurationPrice += plansPrices[minizincSolution.selected_plan - 1]; } if (addOnPrices.length > 0){ for (let i = 0; i < minizincSolution.selected_addons.length; i++){ const item = minizincSolution.selected_addons[i]; if (item === 1){ configurationPrice += addOnPrices[i]; } } } return configurationPrice; } }