UNPKG

daniel-san

Version:

a node-based budget-projection engine that helps your routines and finances find balance. The program features aggregates, terminal and file-based reporting output, multi-currency conversion capability and multi-frequency accounting triggers, including: o

911 lines (789 loc) 31.9 kB
"use strict"; var moment = require('moment'); var _require = require('../utility/errorHandling'), errorDisc = _require.errorDisc; var _require2 = require('../utility/validation'), isUndefinedOrNull = _require2.isUndefinedOrNull; var _require3 = require('../utility/dataStructures'), deepCopy = _require3.deepCopy; var _require4 = require('../timeZone'), initializeTimeZoneData = _require4.initializeTimeZoneData, createTimeZone = _require4.createTimeZone, convertTimeZone = _require4.convertTimeZone; var _require5 = require('../timeStream'), TimeStream = _require5.TimeStream; var _require6 = require('../constants'), DATE_FORMAT_STRING = _require6.DATE_FORMAT_STRING, ONCE = _require6.ONCE, SUNDAY = _require6.SUNDAY, MONDAY = _require6.MONDAY, TUESDAY = _require6.TUESDAY, WEDNESDAY = _require6.WEDNESDAY, THURSDAY = _require6.THURSDAY, FRIDAY = _require6.FRIDAY, SATURDAY = _require6.SATURDAY, POSITIVE = _require6.POSITIVE, NEGATIVE = _require6.NEGATIVE, BOTH = _require6.BOTH, ANY = _require6.ANY, UNION = _require6.UNION, INTERSECTION = _require6.INTERSECTION, DEFAULT = _require6.DEFAULT, ASCENDING = _require6.ASCENDING, DESCENDING = _require6.DESCENDING, SUM = _require6.SUM, AVERAGE = _require6.AVERAGE, MEDIANS = _require6.MEDIANS, MODES = _require6.MODES, MIN = _require6.MIN, MAX = _require6.MAX, DEFAULT_SELECTION_LIMIT = _require6.DEFAULT_SELECTION_LIMIT; var dropEventsWithDuplicateKey = function dropEventsWithDuplicateKey(_ref) { var events = _ref.events, uniqueKey = _ref.key; if (isUndefinedOrNull(uniqueKey)) { return events; } else { var set = new Set(); var newEvents = []; events.forEach(function (event) { if (!set.has(event[uniqueKey])) { set.add(event[uniqueKey]); newEvents.push(event); } }); return newEvents; } }; var sortEventsForReports = function sortEventsForReports(_ref2) { var _ref2$sortKey = _ref2.sortKey, sortKey = _ref2$sortKey === void 0 ? null : _ref2$sortKey, _ref2$sortDirection = _ref2.sortDirection, sortDirection = _ref2$sortDirection === void 0 ? ASCENDING : _ref2$sortDirection, _ref2$selectionLimit = _ref2.selectionLimit, selectionLimit = _ref2$selectionLimit === void 0 ? null : _ref2$selectionLimit, _ref2$uniqueKey = _ref2.uniqueKey, uniqueKey = _ref2$uniqueKey === void 0 ? null : _ref2$uniqueKey, events = _ref2.events; var eventsWithUniqueKeyCheck = dropEventsWithDuplicateKey({ events: events, key: uniqueKey }); // if uniqueKey was defined on reporting rule, then remove duplicates if (isUndefinedOrNull(sortKey) || sortKey === DEFAULT || sortDirection === DEFAULT) { var finalResult = eventsWithUniqueKeyCheck.slice(0, !isUndefinedOrNull(selectionLimit) && selectionLimit <= eventsWithUniqueKeyCheck.length ? selectionLimit : eventsWithUniqueKeyCheck.length); return finalResult; // eslint-disable-next-line no-else-return } else { var sortingDirection = sortDirection === DESCENDING ? -1 : 1; var sortedEvents = deepCopy(eventsWithUniqueKeyCheck).sort(function (aObj, bObj) { var a = aObj[sortKey]; var b = bObj[sortKey]; if (a > b) { return 1 * sortingDirection; } else if (a < b) { return -1 * sortingDirection; // eslint-disable-next-line no-else-return } else { return 0; } }); var finalCollection = sortedEvents.slice(0, !isUndefinedOrNull(selectionLimit) && selectionLimit <= sortedEvents.length ? selectionLimit : sortedEvents.length); return finalCollection; } }; var sortAggregates = function sortAggregates(_ref3) { var aggregateConfig = _ref3.aggregateConfig, aggregates = _ref3.aggregates; var newAggregates; var configSortKey = aggregateConfig.sortKey; var sortKey; var sortDirection; if (isUndefinedOrNull(aggregateConfig.sortKey)) { configSortKey = DEFAULT; } if (configSortKey === DEFAULT || aggregateConfig.sortDirection === DEFAULT) { return aggregates; } if (isUndefinedOrNull(aggregateConfig.sortDirection)) { sortDirection = 1; // defaults to ASCENDING order } else { sortDirection = aggregateConfig.sortDirection === DESCENDING ? -1 : 1; } switch (configSortKey) { case SUM: sortKey = 'sum'; break; case AVERAGE: sortKey = 'average'; break; case MEDIANS: sortKey = 'medians'; break; case MODES: sortKey = 'modes'; break; case MIN: sortKey = 'min'; break; case MAX: sortKey = 'max'; break; case DEFAULT: return aggregates; default: sortKey = aggregateConfig.sortKey; } if (sortKey === 'medians' || sortKey === 'modes') { newAggregates = deepCopy(aggregates).sort(function (aObj, bObj) { var aList = aObj[sortKey]; var bList = bObj[sortKey]; var sortResult = -1; aList.some(function (a) { return bList.some(function (b) { if (a > b) { sortResult = 1 * sortDirection; if (sortDirection === ASCENDING) return true; } else if (a < b) { sortResult = -1 * sortDirection; if (sortDirection === DESCENDING) return true; // eslint-disable-next-line no-else-return } else { sortResult = 0; } }); }); return sortResult; }); } else { newAggregates = deepCopy(aggregates).sort(function (aObj, bObj) { var a = aObj[sortKey]; var b = bObj[sortKey]; if (a > b) { return 1 * sortDirection; } else if (a < b) { return -1 * sortDirection; // eslint-disable-next-line no-else-return } else { return 0; } }); } var finalCollection = newAggregates.slice(0, !isUndefinedOrNull(aggregateConfig.selectionLimit) && aggregateConfig.selectionLimit <= newAggregates.length ? aggregateConfig.selectionLimit : newAggregates.length); return finalCollection; }; var isThisWithinXPercentOfThat = function isThisWithinXPercentOfThat(_ref4) { var value = _ref4.value, _ref4$xPercentRange = _ref4.xPercentRange, xPercentRange = _ref4$xPercentRange === void 0 ? 0 : _ref4$xPercentRange, xPercentTarget = _ref4.xPercentTarget; // const difference = value - xPercentTarget; var xPercentOfValue = Math.abs(xPercentRange * xPercentTarget); var lowValue = xPercentTarget - xPercentOfValue; var highValue = xPercentTarget + xPercentOfValue; if (value >= lowValue && value <= highValue) { return true; // eslint-disable-next-line no-else-return } else { return false; } }; // TODO: delete // const isThisWithinXPercentOfThat = ({ value, xPercentRange = 0, xPercentTarget }) => { // // const difference = value - xPercentTarget; // const xPercentOfValue = Math.abs(xPercentRange * xPercentTarget); // const lowValue = value - xPercentOfValue; // const highValue = value + xPercentOfValue; // if (xPercentTarget >= lowValue && xPercentTarget <= highValue) { // return true; // // eslint-disable-next-line no-else-return // } else { // return false; // } // }; var findEventsWithinXPercentOfValue = function findEventsWithinXPercentOfValue(_ref5) { var events = _ref5.events, _ref5$propertyKey = _ref5.propertyKey, propertyKey = _ref5$propertyKey === void 0 ? 'balanceEnding' : _ref5$propertyKey, _ref5$xPercentRange = _ref5.xPercentRange, xPercentRange = _ref5$xPercentRange === void 0 ? 0 : _ref5$xPercentRange, xPercentTarget = _ref5.xPercentTarget; var newEvents = events.filter(function (event) { return isThisWithinXPercentOfThat({ value: event[propertyKey], xPercentRange: xPercentRange, xPercentTarget: xPercentTarget }); }); return newEvents || []; }; var getWeekdayNumFromString = function getWeekdayNumFromString(weekdayString) { var weekdayNum; switch (weekdayString) { case 'sunday': weekdayNum = SUNDAY; break; case 'monday': weekdayNum = MONDAY; break; case 'tuesday': weekdayNum = TUESDAY; break; case 'wednesday': weekdayNum = WEDNESDAY; break; case 'thursday': weekdayNum = THURSDAY; break; case 'friday': weekdayNum = FRIDAY; break; case 'saturday': weekdayNum = SATURDAY; break; default: weekdayNum = MONDAY; break; } return weekdayNum; }; var defaultStringComparator = function defaultStringComparator(filterKey, filterValue) { return filterKey === filterValue; }; var filterEventsByKeysAndValues = function filterEventsByKeysAndValues(_ref6) { var events = _ref6.events, _ref6$filterKeys = _ref6.filterKeys, filterKeys = _ref6$filterKeys === void 0 ? [] : _ref6$filterKeys, _ref6$filterValues = _ref6.filterValues, filterValues = _ref6$filterValues === void 0 ? [] : _ref6$filterValues, _ref6$filterType = _ref6.filterType, filterType = _ref6$filterType === void 0 ? UNION : _ref6$filterType, _ref6$filterComparato = _ref6.filterComparator, filterComparator = _ref6$filterComparato === void 0 ? defaultStringComparator : _ref6$filterComparato; if (isUndefinedOrNull(filterComparator)) { filterComparator = defaultStringComparator; } if (isUndefinedOrNull(filterType)) { filterType = UNION; } var transientEvent; // for errorDisc try { var newEvents = events; // default value if (filterKeys && filterKeys.length > 0 && filterValues && filterValues.length === filterKeys.length) { var duplicateEvents = deepCopy(events); newEvents = duplicateEvents.filter(function (event) { transientEvent = event; var includeEvent = false; for (var looper = 0; looper < filterKeys.length; looper++) { if (!isUndefinedOrNull(event[filterKeys[looper]]) && (filterValues[looper] === ANY || filterComparator(event[filterKeys[looper]], filterValues[looper]))) { includeEvent = true; if (isUndefinedOrNull(filterType) || filterType === UNION) { break; } } else { includeEvent = false; if (filterType === INTERSECTION) { break; } } } return includeEvent; }); } return newEvents; } catch (err) { throw errorDisc({ err: err, data: { event: transientEvent, filterKeys: filterKeys, filterValues: filterValues, filterType: filterType } }); } }; // you should probably run the validation functions in core/validation.js prior to using this // finds rules with end dates that are less than the beginning date range of the budget projection var findRulesToRetire = function findRulesToRetire(danielSan) { var effectiveDateStart = danielSan.config.effectiveDateStart; var newRulesToRetire = []; // eslint-disable-next-line array-callback-return danielSan.rules.forEach(function (rule, index) { var dateToStartConfig = initializeTimeZoneData(danielSan); var dateToStart = createTimeZone({ timeZone: dateToStartConfig.timeZone, timeZoneType: dateToStartConfig.timeZoneType, dateString: effectiveDateStart }); var convertedDateToStartConfig = initializeTimeZoneData(rule); var convertedDateToStart = convertTimeZone({ timeZone: convertedDateToStartConfig.timeZone, timeZoneType: convertedDateToStartConfig.timeZoneType, date: dateToStart }).date; var convertedDateStartString = convertedDateToStart.format(DATE_FORMAT_STRING); if (!isUndefinedOrNull(rule.effectiveDateEnd) && rule.effectiveDateEnd < convertedDateStartString) { rule.ruleIndex = index; newRulesToRetire.push(rule); } else if (rule.frequency === ONCE && typeof rule.processDate === 'string' && rule.processDate < convertedDateStartString) { rule.ruleIndex = index; newRulesToRetire.push(rule); } }); // TODO: below - couldn't we simply return newToRetire? if (newRulesToRetire.length > 0) { return newRulesToRetire; // eslint-disable-next-line no-else-return } else { return []; } // eslint-disable-next-line no-unreachable return []; // this line satisfies another linting error }; // you should probably run the validation functions in core/validation.js prior to using this // finds rules that have no chance of being triggered via the current configuration var findIrrelevantRules = function findIrrelevantRules(danielSan) { var rules = danielSan.rules, _danielSan$config = danielSan.config, effectiveDateStart = _danielSan$config.effectiveDateStart, effectiveDateEnd = _danielSan$config.effectiveDateEnd, timeZone = _danielSan$config.timeZone, timeZoneType = _danielSan$config.timeZoneType; var relevantRules = []; var irrelevantRules = []; rules.forEach(function (rule, index) { var dateToStart = createTimeZone({ timeZone: timeZone, timeZoneType: timeZoneType, dateString: effectiveDateStart }); var convertedDateToStart = convertTimeZone({ timeZone: rule.timeZone, timeZoneType: rule.timeZoneType, date: dateToStart }).date; var convertedDateToStartString = convertedDateToStart.format(DATE_FORMAT_STRING); var dateToEnd = createTimeZone({ timeZone: timeZone, timeZoneType: timeZoneType, dateString: effectiveDateEnd }); var convertedDateToEnd = convertTimeZone({ timeZone: rule.timeZone, timeZoneType: rule.timeZoneType, date: dateToEnd }).date; var convertedDateToEndString = convertedDateToEnd.format(DATE_FORMAT_STRING); var allowToLive = true; if (!isUndefinedOrNull(rule.effectiveDateEnd) && rule.effectiveDateEnd < convertedDateToStartString || !isUndefinedOrNull(rule.effectiveDateStart) && rule.effectiveDateStart > convertedDateToEndString) { // exclude allowToLive = false; // eslint-disable-next-line no-else-return } else if (rule.frequency === ONCE && typeof rule.EventProcessDate === 'string' && rule.EventProcessDate < convertedDateToStartString) { // exclude: allowToLive = false; } else if (isUndefinedOrNull(rule.effectiveDateEnd)) { // include allowToLive = true; } else { // include allowToLive = true; } if (allowToLive) { relevantRules.push(rule); } else { rule.ruleIndex = index; irrelevantRules.push(rule); } }); return { relevantRules: relevantRules, irrelevantRules: irrelevantRules }; }; var findEventsWithProperty = function findEventsWithProperty(_ref7) { var events = _ref7.events, propertyKey = _ref7.propertyKey; // eslint-disable-next-line array-callback-return var eventsFound = events.filter(function (event) { // eslint-disable-line consistent-return if (!isUndefinedOrNull(event[propertyKey])) { return event; // eslint-disable-next-line no-else-return } else { return false; } }); if (eventsFound.length > 0) { return eventsFound; // eslint-disable-next-line no-else-return } else { return null; } // eslint-disable-next-line no-unreachable return null; // this line satisfies another linting error }; var findEventsByPropertyKeyAndValues = function findEventsByPropertyKeyAndValues(_ref8) { var events = _ref8.events, propertyKey = _ref8.propertyKey, searchValues = _ref8.searchValues; if (events && propertyKey && searchValues && Array.isArray(searchValues) && searchValues.length > 0) { var eventsFound = []; events.forEach(function (event) { searchValues.forEach(function (searchCriteriaValue) { var eventProperty = event[propertyKey]; if (!isUndefinedOrNull(eventProperty) && typeof eventProperty === 'string') { eventProperty = eventProperty.toLowerCase(); } var newSearchCriteriaValue = typeof searchCriteriaValue === 'string' ? searchCriteriaValue.toLowerCase() : searchCriteriaValue; if (!isUndefinedOrNull(eventProperty) && eventProperty === newSearchCriteriaValue) { eventsFound.push(event); } }); }); if (eventsFound && eventsFound.length > 0) { return eventsFound; // eslint-disable-next-line no-else-return } else { return null; } // eslint-disable-next-line no-else-return } else { return null; } }; var findEventsWithPropertyKeyContainingSubstring = function findEventsWithPropertyKeyContainingSubstring(_ref9) { var events = _ref9.events, propertyKey = _ref9.propertyKey, substring = _ref9.substring; var substrings; if (typeof substring === 'string') { substrings = []; substrings.push(substring); } else { substrings = substring; } // eslint-disable-next-line array-callback-return var eventsFound = events.filter(function (event) { // eslint-disable-next-line consistent-return return substrings.some(function (string) { // eslint-disable-line array-callback-return // eslint-disable-line consistent-return if (!isUndefinedOrNull(event[propertyKey]) && event[propertyKey].includes(string)) { return true; } }); }); if (eventsFound.length > 0) { return eventsFound; // eslint-disable-next-line no-else-return } else { return null; } // eslint-disable-next-line no-unreachable return null; // this line satisfies another linting error }; var findCriticalSnapshots = function findCriticalSnapshots(_ref10) { var _ref10$events = _ref10.events, events = _ref10$events === void 0 ? [] : _ref10$events, _ref10$criticalThresh = _ref10.criticalThreshold, criticalThreshold = _ref10$criticalThresh === void 0 ? 0 : _ref10$criticalThresh, _ref10$propertyKey = _ref10.propertyKey, propertyKey = _ref10$propertyKey === void 0 ? 'balanceEnding' : _ref10$propertyKey; var criticalSnapshots = []; if (!isUndefinedOrNull(criticalThreshold)) { criticalSnapshots = events.filter(function (event) { if (!isUndefinedOrNull(event[propertyKey])) { return event[propertyKey] < criticalThreshold; // eslint-disable-next-line no-else-return } else { return false; } }); } return criticalSnapshots; }; var findSnapshotsGreaterThanSupport = function findSnapshotsGreaterThanSupport(_ref11) { var _ref11$events = _ref11.events, events = _ref11$events === void 0 ? [] : _ref11$events, _ref11$amount = _ref11.amount, amount = _ref11$amount === void 0 ? 0 : _ref11$amount, _ref11$propertyKey = _ref11.propertyKey, propertyKey = _ref11$propertyKey === void 0 ? 'balanceEnding' : _ref11$propertyKey; var newCollection = events.filter(function (element) { if (!isUndefinedOrNull(element[propertyKey]) && element[propertyKey] > amount) { return element; // eslint-disable-next-line no-else-return } else { return false; } }); return newCollection; }; var findSnapshotsLessThanResistance = function findSnapshotsLessThanResistance(_ref12) { var _ref12$events = _ref12.events, events = _ref12$events === void 0 ? [] : _ref12$events, _ref12$amount = _ref12.amount, amount = _ref12$amount === void 0 ? 0 : _ref12$amount, _ref12$propertyKey = _ref12.propertyKey, propertyKey = _ref12$propertyKey === void 0 ? 'balanceEnding' : _ref12$propertyKey; var newCollection = events.filter(function (element) { if (!isUndefinedOrNull(element[propertyKey]) && element[propertyKey] < amount) { return element; // eslint-disable-next-line no-else-return } else { return false; } }); return newCollection; }; var findPositiveSnapshotsGreaterThanSupport = function findPositiveSnapshotsGreaterThanSupport(_ref13) { var _ref13$events = _ref13.events, events = _ref13$events === void 0 ? [] : _ref13$events, _ref13$amount = _ref13.amount, amount = _ref13$amount === void 0 ? 0 : _ref13$amount, _ref13$propertyKey = _ref13.propertyKey, propertyKey = _ref13$propertyKey === void 0 ? 'balanceEnding' : _ref13$propertyKey; var newCollection = events.filter(function (element) { if (!isUndefinedOrNull(element[propertyKey]) && element[propertyKey] > 0 && element[propertyKey] > amount) { return element; // eslint-disable-next-line no-else-return } else { return false; } }); return newCollection; }; var findPositiveSnapshotsLessThanResistance = function findPositiveSnapshotsLessThanResistance(_ref14) { var _ref14$events = _ref14.events, events = _ref14$events === void 0 ? [] : _ref14$events, _ref14$amount = _ref14.amount, amount = _ref14$amount === void 0 ? 0 : _ref14$amount, _ref14$propertyKey = _ref14.propertyKey, propertyKey = _ref14$propertyKey === void 0 ? 'balanceEnding' : _ref14$propertyKey; var newCollection = events.filter(function (element) { if (!isUndefinedOrNull(element[propertyKey]) && element[propertyKey] > 0 && element[propertyKey] < amount) { return element; // eslint-disable-next-line no-else-return } else { return false; } }); return newCollection; }; var findNegativeSnapshotsGreaterThanSupport = function findNegativeSnapshotsGreaterThanSupport(_ref15) { var _ref15$events = _ref15.events, events = _ref15$events === void 0 ? [] : _ref15$events, _ref15$amount = _ref15.amount, amount = _ref15$amount === void 0 ? 0 : _ref15$amount, _ref15$propertyKey = _ref15.propertyKey, propertyKey = _ref15$propertyKey === void 0 ? 'balanceEnding' : _ref15$propertyKey; var newCollection = events.filter(function (element) { if (!isUndefinedOrNull(element[propertyKey]) && element[propertyKey] < 0 && Math.abs(element[propertyKey]) > Math.abs(amount)) { return element; // eslint-disable-next-line no-else-return } else { return false; } }); return newCollection; }; var findNegativeSnapshotsLessThanResistance = function findNegativeSnapshotsLessThanResistance(_ref16) { var _ref16$events = _ref16.events, events = _ref16$events === void 0 ? [] : _ref16$events, _ref16$amount = _ref16.amount, amount = _ref16$amount === void 0 ? 0 : _ref16$amount, _ref16$propertyKey = _ref16.propertyKey, propertyKey = _ref16$propertyKey === void 0 ? 'balanceEnding' : _ref16$propertyKey; var newCollection = events.filter(function (element) { if (!isUndefinedOrNull(element[propertyKey]) && element[propertyKey] < 0 && Math.abs(element[propertyKey]) < Math.abs(amount)) { return element; // eslint-disable-next-line no-else-return } else { return false; } }); return newCollection; }; var isVal1GreaterThanVal2 = function isVal1GreaterThanVal2(val1, val2) { return val1 > val2; }; var isAbsVal1GreaterThanAbsVal2 = function isAbsVal1GreaterThanAbsVal2(val1, val2) { return Math.abs(val1) > Math.abs(val2); }; var isVal1LessThanVal2 = function isVal1LessThanVal2(val1, val2) { return val1 < val2; }; var isAbsVal1LessThanAbsVal2 = function isAbsVal1LessThanAbsVal2(val1, val2) { return Math.abs(val1) < Math.abs(val2); }; var filterEventsByFlowDirection = function filterEventsByFlowDirection(_ref17) { var events = _ref17.events, _ref17$propertyKey = _ref17.propertyKey, propertyKey = _ref17$propertyKey === void 0 ? 'balanceEnding' : _ref17$propertyKey, flowDirection = _ref17.flowDirection; var deepCopyOfEvents = deepCopy(events); var newEvents; switch (flowDirection) { case POSITIVE: newEvents = deepCopyOfEvents.filter(function (event) { return event[propertyKey] > 0; }); break; case NEGATIVE: newEvents = deepCopyOfEvents.filter(function (event) { return event[propertyKey] < 0; }); break; case BOTH: newEvents = deepCopyOfEvents; break; default: newEvents = deepCopyOfEvents; break; } return newEvents; }; // TODO: performance could be increased by making a separete function for findLeastValueSnapshots // TODO cont: instead of passing reverse as a parameter and reverse the sort var findGreatestValueSnapshots = function findGreatestValueSnapshots(_ref18) { var _ref18$events = _ref18.events, events = _ref18$events === void 0 ? [] : _ref18$events, _ref18$propertyKey = _ref18.propertyKey, propertyKey = _ref18$propertyKey === void 0 ? 'balanceEnding' : _ref18$propertyKey, _ref18$selectionLimit = _ref18.selectionLimit, selectionLimit = _ref18$selectionLimit === void 0 ? DEFAULT_SELECTION_LIMIT : _ref18$selectionLimit, _ref18$flowDirection = _ref18.flowDirection, flowDirection = _ref18$flowDirection === void 0 ? BOTH : _ref18$flowDirection, _ref18$reverse = _ref18.reverse, reverse = _ref18$reverse === void 0 ? false : _ref18$reverse, _ref18$alreadyFiltere = _ref18.alreadyFiltered, alreadyFiltered = _ref18$alreadyFiltere === void 0 ? false : _ref18$alreadyFiltere; var sortedCollection = []; var newEvents = deepCopy(events); // TODO: should refactor to only send this function pre-filtered events: for eg. with filterEventsByFlowDirection, whatever you do make sure you test it a lot! var greaterThanComparator; var lessThanComparator; switch (flowDirection) { case POSITIVE: greaterThanComparator = isVal1GreaterThanVal2; lessThanComparator = isVal1LessThanVal2; break; case NEGATIVE: greaterThanComparator = isAbsVal1GreaterThanAbsVal2; lessThanComparator = isAbsVal1LessThanAbsVal2; break; case BOTH: greaterThanComparator = isVal1GreaterThanVal2; lessThanComparator = isVal1LessThanVal2; break; default: greaterThanComparator = isVal1GreaterThanVal2; lessThanComparator = isVal1LessThanVal2; break; } sortedCollection = newEvents.sort(function (a, b) { if (isUndefinedOrNull(a) && !isUndefinedOrNull(b)) { return -1; } else if (!isUndefinedOrNull(a) && isUndefinedOrNull(b)) { return 1; } else if (isUndefinedOrNull(a) && isUndefinedOrNull(b)) { return 0; } else if (greaterThanComparator(a[propertyKey], b[propertyKey])) { return -1; } else if (lessThanComparator(a[propertyKey], b[propertyKey])) { return 1; // eslint-disable-next-line no-else-return } else { return 0; } }); if (reverse) { sortedCollection = sortedCollection.reverse(); } return sortedCollection; }; var findGreatestPositiveValueSnapshots = function findGreatestPositiveValueSnapshots(_ref19) { var _ref19$events = _ref19.events, events = _ref19$events === void 0 ? [] : _ref19$events, _ref19$propertyKey = _ref19.propertyKey, propertyKey = _ref19$propertyKey === void 0 ? 'balanceEnding' : _ref19$propertyKey, _ref19$selectionLimit = _ref19.selectionLimit, selectionLimit = _ref19$selectionLimit === void 0 ? DEFAULT_SELECTION_LIMIT : _ref19$selectionLimit, _ref19$reverse = _ref19.reverse, reverse = _ref19$reverse === void 0 ? false : _ref19$reverse; var newEvents = deepCopy(events); var sortedCollection = newEvents.filter(function (e) { return e[propertyKey] > 0; }).sort(function (a, b) { if (isUndefinedOrNull(a) && !isUndefinedOrNull(b)) { return -1; } else if (!isUndefinedOrNull(a) && isUndefinedOrNull(b)) { return 1; } else if (isUndefinedOrNull(a) && isUndefinedOrNull(b)) { return 0; } else if (a[propertyKey] > b[propertyKey]) { return -1; } else if (a[propertyKey] < b[propertyKey]) { return 1; // eslint-disable-next-line no-else-return } else { return 0; } }); if (reverse) { sortedCollection = sortedCollection.reverse(); } return sortedCollection; }; var findGreatestNegativeValueSnapshots = function findGreatestNegativeValueSnapshots(_ref20) { var _ref20$events = _ref20.events, events = _ref20$events === void 0 ? [] : _ref20$events, _ref20$propertyKey = _ref20.propertyKey, propertyKey = _ref20$propertyKey === void 0 ? 'balanceEnding' : _ref20$propertyKey, _ref20$selectionLimit = _ref20.selectionLimit, selectionLimit = _ref20$selectionLimit === void 0 ? DEFAULT_SELECTION_LIMIT : _ref20$selectionLimit, _ref20$reverse = _ref20.reverse, reverse = _ref20$reverse === void 0 ? false : _ref20$reverse; var newEvents = deepCopy(events); var sortedCollection = newEvents.filter(function (e) { return e[propertyKey] < 0; }).sort(function (a, b) { if (isUndefinedOrNull(a) && !isUndefinedOrNull(b)) { return -1; } else if (!isUndefinedOrNull(a) && isUndefinedOrNull(b)) { return 1; } else if (isUndefinedOrNull(a) && isUndefinedOrNull(b)) { return 0; } else if (Math.abs(a[propertyKey]) > Math.abs(b[propertyKey])) { return -1; } else if (Math.abs(a[propertyKey]) < Math.abs(b[propertyKey])) { return 1; // eslint-disable-next-line no-else-return } else { return 0; } }); if (reverse) { sortedCollection = sortedCollection.reverse(); } return sortedCollection; }; var sumAllPositiveEventAmounts = function sumAllPositiveEventAmounts(events) { var sum = 0; events.forEach(function (event) { var amount = event.amount; if (!isUndefinedOrNull(amount) && amount > 0) { // routine/reminder types like STANDARD_EVENT_ROUTINE do not require an amount field // we first check to make sure it had an amount field so that it satisfies our required context // however, we are actually only interested in the multi-currency converted amount sum += amount; } }); return sum; }; var sumAllNegativeEventAmounts = function sumAllNegativeEventAmounts(events) { var sum = 0; events.forEach(function (event) { var amount = event.amount; if (!isUndefinedOrNull(amount) && amount < 0) { // routine/reminder types like STANDARD_EVENT_ROUTINE do not require an amount field // we first check to make sure it had an amount field so that it satisfies our required context // however, we are actually only interested in the multi-currency converted amount sum += amount; } }); return sum; }; module.exports = { sortEventsForReports: sortEventsForReports, sortAggregates: sortAggregates, isThisWithinXPercentOfThat: isThisWithinXPercentOfThat, findEventsWithinXPercentOfValue: findEventsWithinXPercentOfValue, filterEventsByKeysAndValues: filterEventsByKeysAndValues, filterEventsByFlowDirection: filterEventsByFlowDirection, findSnapshotsGreaterThanSupport: findSnapshotsGreaterThanSupport, findSnapshotsLessThanResistance: findSnapshotsLessThanResistance, findPositiveSnapshotsGreaterThanSupport: findPositiveSnapshotsGreaterThanSupport, findPositiveSnapshotsLessThanResistance: findPositiveSnapshotsLessThanResistance, findNegativeSnapshotsGreaterThanSupport: findNegativeSnapshotsGreaterThanSupport, findNegativeSnapshotsLessThanResistance: findNegativeSnapshotsLessThanResistance, findGreatestValueSnapshots: findGreatestValueSnapshots, findGreatestPositiveValueSnapshots: findGreatestPositiveValueSnapshots, findGreatestNegativeValueSnapshots: findGreatestNegativeValueSnapshots, findCriticalSnapshots: findCriticalSnapshots, findRulesToRetire: findRulesToRetire, findIrrelevantRules: findIrrelevantRules, findEventsWithProperty: findEventsWithProperty, findEventsByPropertyKeyAndValues: findEventsByPropertyKeyAndValues, findEventsWithPropertyKeyContainingSubstring: findEventsWithPropertyKeyContainingSubstring, sumAllPositiveEventAmounts: sumAllPositiveEventAmounts, sumAllNegativeEventAmounts: sumAllNegativeEventAmounts };