UNPKG

installment-calculations

Version:

Calculate payment installments (and other useful details) based on start/end dates, interval and target amount

152 lines (121 loc) 6.19 kB
var moment = require('moment'), WEEKLY = 'WEEKLY', MONTHLY = 'MONTHLY'; function InstallmentCalculations() { return { getTransactionsArray: function(savingsTarget, dateTarget, startDate, depositInterval) { var numberOfInstallments = this.getNumberOfInstallments(startDate, dateTarget, depositInterval); var installmentAmount = this.calculateInstallmentAmount(savingsTarget, numberOfInstallments); var isUnEqual = this.installmentAmountsAreUnequal(savingsTarget, numberOfInstallments); var results = []; // This is the format the DB expects. var outputFormat = 'YYYY-MM-DD HH:mm:ss'; var currentDate = moment.utc(startDate).startOf('day').format(outputFormat); for (var ctr = 0; ctr < numberOfInstallments; ctr++) { var installmentAmount = this.calculateInstallmentAmount(savingsTarget, numberOfInstallments); if (isUnEqual && ctr === 0) { installmentAmount = this.calculateLastInstallmentAmount(savingsTarget, numberOfInstallments); } results.push({ amount: installmentAmount, date: currentDate }); if (depositInterval === 'MONTHLY') { currentDate = moment.utc(startDate).add(ctr+1, 'months').startOf('day').format(outputFormat); } else { currentDate = moment.utc(currentDate).add(7, 'days').startOf('day').format(outputFormat); }; } return results; }, // Find number of weekly or monthly installments between two dates. getNumberOfInstallments: function(startDate, endDate, interval) { var numberOfUnitsBetween; if (interval === MONTHLY) { numberOfUnitsBetween = this.numberOfMonthsBetween(startDate, endDate); } else if (interval === WEEKLY) { numberOfUnitsBetween = this.numberOfWeeksBetween(startDate, endDate); } // We wnt to round up what this returns. // It it returns 0, then we assume they're the same day and one transaction should be schedulesd. // Returns a whole number, then no change. Means that the dates are apart by a whole month // Returns a float, then we round up. Means that there's room for a final transaction return numberOfUnitsBetween < 1 ? 1 : Math.ceil(numberOfUnitsBetween); }, // Calculate the installment amount for each transaction given the savings target // and the number of transactions. The return value is approximate. calculateInstallmentAmount: function(savingsTarget, numberOfInstallments) { return Math.floor(savingsTarget / numberOfInstallments); }, // Use this when we're in charge of calculating the installmentAmount // TODO: Need to rename this to `calcSupplementInstallmentAmount` (or something, // since it can be either the last or first payment) calculateLastInstallmentAmount: function(savingsTarget, numberOfInstallments) { var installmentAmount = this.calculateInstallmentAmount(savingsTarget, numberOfInstallments); var installmentsMinusOne = numberOfInstallments - 1; return savingsTarget - (installmentAmount * installmentsMinusOne); }, // Use this when the installmentAmount is set by the User. calculateLastInstallmentAmountFixed: function(savingsTarget, installmentAmount, numberOfInstallments) { var total = installmentAmount * numberOfInstallments; var lastInstallmentAmount = installmentAmount - (total - savingsTarget); return lastInstallmentAmount === 0 ? installmentAmount : lastInstallmentAmount; }, // Calculate the number of intallments needed given the target amount // and amount per installment calculateInstallmentNumber: function(savingsTarget, installmentAmount) { return Math.ceil(savingsTarget / installmentAmount); }, // check if installment amounts are unequal. installmentAmountsAreUnequal: function(savingsTarget, numberOfInstallments) { var installmentAmount = savingsTarget / numberOfInstallments; return installmentAmount.toString().indexOf('.') > -1 ? true : false; }, // find the number of full weeks between two dates. numberOfWeeksBetween: function(startDate, endDate) { var start = moment.utc(startDate); var end = moment.utc(endDate); var weeksBetween = end.diff(start, 'week', true) return weeksBetween; }, // find the number of full months between two dates. numberOfMonthsBetween: function(startDate, endDate) { // Takes two utc times. var start = moment.utc(startDate); var end = moment.utc(endDate); var monthsBetween = end.diff(start, 'month', true); return monthsBetween; }, // Get the date for the next time the date of month occurs getNextInstanceOfDate: function(dayOfMonth, startDate) { var currentDate = moment().utc(startDate).startOf('day'); var currentDateOfMonth = currentDate.clone().date(); if (currentDateOfMonth > dayOfMonth || currentDateOfMonth === dayOfMonth) { return currentDate.endOf('month').add(dayOfMonth, 'd').startOf('day').toDate(); } return currentDate.date(dayOfMonth).startOf('day').toDate(); }, // Get the date for the next time a dayOfWeek occurs. // dayOfWeek is given as an integer with 1 being Monday. getNextInstanceOfDay: function(dayOfWeek, startDate) { var fromDate = moment.utc(startDate).startOf('day'); var currentDay = fromDate.clone().weekday(); if (currentDay > dayOfWeek || currentDay === dayOfWeek) { return fromDate.endOf('week').weekday(7 + dayOfWeek).startOf('day').toDate(); } return fromDate.weekday(dayOfWeek).startOf('day').toDate(); }, // Takes to date object and checks if they're the same date isSameDay: function(dateA, dateB) { return moment(dateA).isSame(moment(dateB), 'day'); }, // Set the time of a date to 00:00:00, return js date object. normalizeDate: function(date) { return moment(date).startOf('day').toDate(); }, daysLeft: function(from, to) { return moment(to).diff(moment(from), 'days'); } }; } module.exports = InstallmentCalculations();