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
JavaScript
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();