Source: index.js

'use strict';

import Utils from './utils';

export default class PackagePricing {

    constructor() {
        //DEBUG
        this.utils = new Utils(false, 25);
        // Based on the 80/20 rule .. doubled
        this.peopleToReachSinglePayer = 25;
        // Price per 1 USD in EGP
        this.buyUSD = 18;
        this.sellUSD = 17;
        // The Very Base Price
        this.priceBase = 13;
        // The money transfer fees
        this.deliveryFees = 10;
        // is the share that payfort takes  2.65 %
        this.payfortPercentage = 0.0265;
        this.payfortConstInUSD = 0.14;
        // Taxes
        this.taxes = 0.22;
        this.additionalTaxes = 0.14;
        // Set Safty Margin .. in case we miss something
        this.saftyMargin = 0.1;
        // Acquisition by Facebook Ads
        this.fbPayPerImpression = 0.00734;
        // Using Plivo in usd
        this.plivoSMSUSD = 0.035;
        // Default rounding base - will be multiplied by 10
        this.defaultRoundingBase = 5;
    }

    /**
     * Calculates the payfort fees used to transfer money from user's CC to
     * company's bank account
     * @param {Number} amount The amount in EGP
     * @return {Number} The payfort fees per amount in EGP
     */
    calcPayFortFees(amount) {
        // The constant that payfort takes in USD
        let payfortConst = this.payfortConstInUSD * this.buyUSD;
        let payfortVariable = this.payfortPercentage * amount;
        return payfortVariable + payfortConst;
    }

    /**
     * Calculates the taxes
     * @param {Number} amount The amount of money to calculate taxes on
     * @return {Number} The amount of money, with taxes considered ( added )
     */
    calcTaxes(amount) {
        let ret = amount * this.taxes + amount * this.additionalTaxes;
        return ret;
    }

    /**
     * Calculate additional fees per user for acquisition
     * @return fixed number based on the plivoSMS rate and fbAd rate
     */
    additionalFees() {
        let fbAdPerAccount = this.fbPayPerImpression * this.peopleToReachSinglePayer * this.buyUSD;
        this.utils.log("FB Ad per Account", fbAdPerAccount);
        let SMSFees = this.plivoSMSUSD * this.buyUSD;
        let smsCostUntilWeGetToABuyer = SMSFees * this.peopleToReachSinglePayer;
        this.utils.log("SMS Cost per Buyer", smsCostUntilWeGetToABuyer);
        return Math.round(fbAdPerAccount + smsCostUntilWeGetToABuyer);
    }

    /**
     * @param {Number} packageIndex 0 ( free ) 1 ( basic ) 2 ( advanced ) 3 ( extreme )
     * each packageIndex has more features than the ones before
     * @param {Number} users Buying for how many users. must be > 0
     * @param {Number} months 1 -to- 12
     * @param {Number} roundingBase - The priceBase is rounded to 10 and then
     * multiplied by this roundingBase, so that you expect the price to be
     * multiple of say ( 25 ) if the price base is 13 and the rounding base is
     * 2.5. or package is multiple of ( 50 ) if the price base is 14.5 and the
     * roundingBase is 5 ..  etc..
     * @return {Object} The price information object. ex.
     * if inputs are
     *  {
     *       name: 'Some Name',
     *       users: 1,
     *       months: 1,
     *       packageIndex: 1,
     *       roundingBase: 2.5
     *  }
     *
     * result would be
     *  {
     *      "EGP": {
     *          "originalPrice": 600,
     *          "price": 50,
     *          "priceAnnually": 600,
     *          "saving": 0,
     *          "savingPercent": 0,
     *      },
     *      "USD": {
     *          "originalPrice": 36,
     *          "price": 3,
     *          "priceAnnually": 36,
     *          "saving": 0,
     *          "savingPercent": 0,
     *      }
     *  }
     */
    price(packageIndex, users, months, roundingBase) {
        // Free Package
        if (packageIndex === 0) {
            return {
                'EGP': {
                    originalPrice: 0,
                    price: 0,
                    saving: 0,
                    savingPercent: 0
                },
                'USD': {
                    originalPrice: 0,
                    price: 0,
                    saving: 0,
                    savingPercent: 0
                }
            };
        }
        this.utils.log('Arguments', {
            index: packageIndex,
            users: users,
            months: months
        });
        // Pricing rounding base
        let roundBase = this.utils.mround(this.priceBase, 10) * (roundingBase || this.defaultRoundingBase);
        // USD Pricing rounding base
        let usdRoundBase = 3 * packageIndex;
        this.utils.log('Round Base', roundBase);
        let basePackagePrice = packageIndex * this.priceBase;
        this.utils.log('Base Package Price', basePackagePrice);
        let netPrice =  basePackagePrice * months * users ;
        this.utils.log('Net Price', netPrice);
        let withSaftey = netPrice + (netPrice * this.saftyMargin);
        this.utils.log('With Saftey', withSaftey);
        let taxes = this.calcTaxes(withSaftey);
        this.utils.log('Taxes', taxes);
        this.utils.log('Additional Fees', this.additionalFees());
        let withAcquisition = this.additionalFees() + taxes + withSaftey;
        this.utils.log('With Acquisition', withAcquisition);
        let transfereFees = this.calcPayFortFees(withAcquisition);
        this.utils.log('Transfere Fees', transfereFees);
        let gross = withAcquisition + transfereFees;
        this.utils.log('Gross', gross);
        let price = this.utils.mround(gross + (roundBase / 2), roundBase);
        this.utils.log('Price', price);
        let originalPriceInUsd = price / this.sellUSD;
        this.utils.log('Original Price In USD', originalPriceInUsd);
        let priceInUsd = this.utils.mround(originalPriceInUsd + (usdRoundBase/2), usdRoundBase);
        this.utils.log('Price In USD', priceInUsd);
        let savingsEGP = this.calcSavings(packageIndex, price, users, months, 'EGP');
        let savingsUSD = this.calcSavings(packageIndex, priceInUsd, users, months, 'usd');
        return {
            'EGP': {
                priceAnnually: price * (12/months),
                originalPrice: savingsEGP.originalPrice,
                price: price,
                saving: savingsEGP.saving,
                savingPercent: parseFloat(savingsEGP.percent.toFixed(2))
            },
            'USD': {
                priceAnnually: priceInUsd * (12/months),
                originalPrice: savingsUSD.originalPrice,
                price: priceInUsd,
                saving: savingsUSD.saving,
                savingPercent: parseFloat(savingsUSD.percent.toFixed(2))
            }
        };
    };

    /**
     * Calculates savings for a package per user annually.
     *
     * @param {Number} packageIndex The package index to multiple to for pricing
     * @param {Number} price The price already calculated for the package
     * @param {Number} users The count of users for this package
     * @param {Number} months The period of periodical payment for user
     * @param {String} currency Either "USD" or "EGP"
     * @return {Object} The savings object. ex.
     * {
     *      packageIndex: 1,
     *      originalPrice: 150,
     *      price: 100,
     *      users: 3,
     *      months: 1,
     *      currency: 'EGP',
     *      saving: 50,
     *      percent: 0.33
     *  }
     */
    calcSavings(packageIndex, price, users, months, currency) {
        let singleUserPaysMonthly = (price / users) / months;
        this.utils.log('Single User Pays Monthly', singleUserPaysMonthly);
        let singleUserPaysAnnually = singleUserPaysMonthly * 12;
        this.utils.log('Single User Pays Annually', singleUserPaysAnnually);
        let youPayAnnually = singleUserPaysAnnually * users;
        let baseSavingPrice = youPayAnnually;
        if (!(users === 1 && months === 1)) {
            if (currency.toLowerCase() === 'egp') {
                baseSavingPrice = this.price(packageIndex, 1, 1).EGP.price * 12 * users;
            } else {
                baseSavingPrice = this.price(packageIndex, 1, 1).USD.price * 12 * users;
            }
        }
        this.utils.log('Base Savinig Price', baseSavingPrice);
        let youSave = baseSavingPrice - youPayAnnually;
        this.utils.log('You Save', youSave);
        let savingPercent = youSave/baseSavingPrice
        this.utils.log('Saving Percent', savingPercent);
        return {
            packageIndex: packageIndex,
            originalPrice: baseSavingPrice,
            price: price,
            users: users,
            months: months,
            currency: currency.toUpperCase(),
            saving: youSave,
            percent: savingPercent
        };
    }
};