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

1,216 lines (1,096 loc) 86.4 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); 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('./index.js'), isThisWithinXPercentOfThat = _require4.isThisWithinXPercentOfThat, findGreatestValueSnapshots = _require4.findGreatestValueSnapshots, findGreatestPositiveValueSnapshots = _require4.findGreatestPositiveValueSnapshots, findGreatestNegativeValueSnapshots = _require4.findGreatestNegativeValueSnapshots; var _require5 = require('../timeZone'), createTimeZone = _require5.createTimeZone; var _require6 = require('../timeStream'), TimeStream = _require6.TimeStream; var _require7 = require('../constants'), DATE_FORMAT_STRING = _require7.DATE_FORMAT_STRING, ANNUALLY = _require7.ANNUALLY, MONTHLY = _require7.MONTHLY, WEEKLY = _require7.WEEKLY, DAY_CYCLES = _require7.DAY_CYCLES, DATE_SETS = _require7.DATE_SETS, SUMS_AND_AVERAGES = _require7.SUMS_AND_AVERAGES, MEDIANS_AND_MODES = _require7.MEDIANS_AND_MODES, MINIMUMS_AND_MAXIMUMS = _require7.MINIMUMS_AND_MAXIMUMS, GREATEST_VALUES = _require7.GREATEST_VALUES, MONDAY = _require7.MONDAY, POSITIVE = _require7.POSITIVE, NEGATIVE = _require7.NEGATIVE, BOTH = _require7.BOTH, DATE_DELIMITER = _require7.DATE_DELIMITER, AGGREGATE = _require7.AGGREGATE, DEFAULT_SELECTION_LIMIT = _require7.DEFAULT_SELECTION_LIMIT; // TODO: the primary aggregate functions here could most certainly be abstracted and modularized further, thereby decreasing the amount of code in this file. var aggregateGreatestValuesListProcess = function aggregateGreatestValuesListProcess(_ref) { var aggregate = _ref.aggregate, events = _ref.events, propertyKey = _ref.propertyKey, flowDirection = _ref.flowDirection, selectionLimit = _ref.selectionLimit, reverse = _ref.reverse, transientData = _ref.transientData; /* TODO: performance could be increased if we replaced the functions being called below with dedicated functions since we are reusing the snapshot functions, we have to accomodate the object data struture by first creating a value list and then adding back the propertyKey if we created dedicated functions for each switch case with simple array data structures, we could speed this process up also we are using findGreatestPositiveValueSnapshots for least value situations too by reversing the sort (another performance hit) */ var valueList = transientData.dataSet.map(function (obj) { return obj[propertyKey]; }); var set = Array.from(new Set(valueList)); var newList = set.map(function (value) { return (0, _defineProperty2["default"])({}, propertyKey, value); }); var collection = []; switch (flowDirection) { case POSITIVE: collection = findGreatestPositiveValueSnapshots({ events: newList, propertyKey: propertyKey, selectionLimit: selectionLimit, reverse: reverse }); break; case NEGATIVE: collection = findGreatestNegativeValueSnapshots({ events: newList, propertyKey: propertyKey, selectionLimit: selectionLimit, reverse: reverse }); break; case BOTH: collection = findGreatestValueSnapshots({ // TODO: we might not need seaparate functions for these anymore? events: newList, // TODO cont: since we are pre-filtering via flowDirection before we hit this function in createReport propertyKey: propertyKey, selectionLimit: selectionLimit, reverse: reverse }); break; default: collection = findGreatestValueSnapshots({ events: newList, propertyKey: propertyKey, selectionLimit: selectionLimit, reverse: reverse }); break; } var finalCollection = collection.slice(0, !isUndefinedOrNull(selectionLimit) && selectionLimit <= collection.length ? selectionLimit : collection.length); if (reverse) { aggregate.leastValues = finalCollection.map(function (obj) { return obj[propertyKey]; }); } else { aggregate.greatestValues = finalCollection.map(function (obj) { return obj[propertyKey]; }); } }; var aggregateGreatestValuesEventProcess = function aggregateGreatestValuesEventProcess(_ref3) { var aggregate = _ref3.aggregate, event = _ref3.event, date = _ref3.date, propertyKey = _ref3.propertyKey, flowDirection = _ref3.flowDirection, transientData = _ref3.transientData; if (flowDirection === POSITIVE && event[propertyKey] > 0) { // TODO: could refactor this to use filterByFlowDirection or somethig in the main createReport function prior to aggregation for each aggregate rule? aggregate.eventCount++; transientData.dataSet.push((0, _defineProperty2["default"])({}, propertyKey, event[propertyKey])); } else if (flowDirection === NEGATIVE && event[propertyKey] < 0) { aggregate.eventCount++; transientData.dataSet.push((0, _defineProperty2["default"])({}, propertyKey, event[propertyKey])); } else if (flowDirection === BOTH && event[propertyKey] !== 0) { aggregate.eventCount++; transientData.dataSet.push((0, _defineProperty2["default"])({}, propertyKey, event[propertyKey])); } }; var aggregateGreatestValuesInit = function aggregateGreatestValuesInit(_ref4) { var aggregate = _ref4.aggregate, event = _ref4.event, date = _ref4.date, propertyKey = _ref4.propertyKey, flowDirection = _ref4.flowDirection, transientData = _ref4.transientData; transientData.dataSet = []; }; var aggregateSumsAndAvgsEventProcess = function aggregateSumsAndAvgsEventProcess(_ref5) { var aggregate = _ref5.aggregate, event = _ref5.event, date = _ref5.date, propertyKey = _ref5.propertyKey, flowDirection = _ref5.flowDirection, transientData = _ref5.transientData; if (flowDirection === POSITIVE && event[propertyKey] > 0) { // TODO: could refactor this to use filterByFlowDirection or somethig in the main createReport function prior to aggregation for each aggregate rule? aggregate.eventCount++; aggregate.sum += event[propertyKey]; } else if (flowDirection === NEGATIVE && event[propertyKey] < 0) { aggregate.eventCount++; aggregate.sum += Math.abs(event[propertyKey]); } else if (flowDirection === BOTH && event[propertyKey] !== 0) { aggregate.eventCount++; aggregate.sum += event[propertyKey]; } }; var aggregateSumsAndAvgsListProcess = function aggregateSumsAndAvgsListProcess(_ref6) { var aggregate = _ref6.aggregate, events = _ref6.events, propertyKey = _ref6.propertyKey, flowDirection = _ref6.flowDirection, transientData = _ref6.transientData; aggregate.average = aggregate.sum / aggregate.eventCount; }; var aggregateSumsAndAvgsInit = function aggregateSumsAndAvgsInit(_ref7) { var aggregate = _ref7.aggregate, event = _ref7.event, date = _ref7.date, propertyKey = _ref7.propertyKey, flowDirection = _ref7.flowDirection, transientData = _ref7.transientData; aggregate.sum = 0; }; var aggregateMinimumsAndMaximumsEventProcess = function aggregateMinimumsAndMaximumsEventProcess(_ref8) { var aggregate = _ref8.aggregate, event = _ref8.event, date = _ref8.date, propertyKey = _ref8.propertyKey, flowDirection = _ref8.flowDirection, transientData = _ref8.transientData; if (flowDirection === POSITIVE && event[propertyKey] > 0) { aggregate.eventCount++; if (!aggregate.max || event[propertyKey] > aggregate.max) { // TODO: could refactor this to use filterByFlowDirection or somethig in the main createReport function prior to aggregation for each aggregate rule? aggregate.max = event[propertyKey]; } if (!aggregate.min || event[propertyKey] < aggregate.min) { aggregate.min = event[propertyKey]; } } else if (flowDirection === NEGATIVE && event[propertyKey] < 0) { aggregate.eventCount++; if (!aggregate.max || Math.abs(event[propertyKey]) > aggregate.max) { aggregate.max = Math.abs(event[propertyKey]); } if (!aggregate.min || Math.abs(event[propertyKey]) < aggregate.min) { aggregate.min = Math.abs(event[propertyKey]); } } else if (flowDirection === BOTH && event[propertyKey] !== 0) { aggregate.eventCount++; if (!aggregate.max || event[propertyKey] > aggregate.max) { aggregate.max = event[propertyKey]; } if (!aggregate.min || event[propertyKey] < aggregate.min) { aggregate.min = event[propertyKey]; } } }; var aggregateMinimumsAndMaximumsInit = function aggregateMinimumsAndMaximumsInit(_ref9) { var aggregate = _ref9.aggregate, event = _ref9.event, date = _ref9.date, propertyKey = _ref9.propertyKey, flowDirection = _ref9.flowDirection, transientData = _ref9.transientData; }; var findModes = function findModes(set) { var modeMax = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 5; var modes = []; var transientSet = deepCopy(set); var newSet = transientSet.sort(function (a, b) { if (a.frequency < b.frequency) { return 1; } else if (a.frequency > b.frequency) { return -1; // eslint-disable-next-line no-else-return } else { return 0; } }); if (newSet.length > 0) { var maxFrequency = newSet[0].frequency; var thereIsAtLeastOneMode = newSet.some(function (element) { // by commenting out the below block, we now are merely checking that there is a list, we are not concerend if its the only value in the set, and I think we should allow that // return element.frequency < maxFrequency; return true; }); if (thereIsAtLeastOneMode) { var finalSet = newSet.filter(function (element) { return element.frequency === maxFrequency; }); var setLength = modeMax <= finalSet.length ? modeMax : finalSet.length; for (var looper = 0; looper < setLength; looper++) { modes.push(finalSet[looper]); } } else if (newSet.length === 2) { if (newSet[0] === newSet[1]) { modes.push(newSet[0]); modes.push(newSet[1]); } } else { return []; } } var finalModes = modes.map(function (mode) { return mode.value; }); return finalModes; }; var findMedians = function findMedians(set) { var medians = []; var transientSet = deepCopy(set); var newSet = transientSet.sort(function (a, b) { if (a > b) { return 1; } else if (a < b) { return -1; // eslint-disable-next-line no-else-return } else { return 0; } }); var setLength = newSet.length; var midPointIndex; if (setLength > 2) { if (setLength % 2 > 0) { midPointIndex = Math.ceil(setLength / 2); medians.push(newSet[midPointIndex]); } else { midPointIndex = setLength / 2; medians.push(newSet[midPointIndex]); medians.push(newSet[midPointIndex + 1]); } } medians = medians.sort(function (a, b) { if (a > b) { return 1; } else if (a < b) { return -1; // eslint-disable-next-line no-else-return } else { return 0; } }); return medians; }; var pushToMediansAndModesList = function pushToMediansAndModesList(_ref10) { var value = _ref10.value, xPercentRange = _ref10.xPercentRange, transientData = _ref10.transientData; var matchFound = false; var _loop = function _loop(looper) { if (transientData.modeDataSet[looper].value.toString() === value.toString()) { // toString for more precise comparison transientData.modeDataSet[looper].frequency++; matchFound = true; } else if ( // matching on the xPercentRange strategy // check for commonality in either direction and if matched then increment the frequency of both isThisWithinXPercentOfThat({ value: value, xPercentRange: xPercentRange, xPercentTarget: transientData.modeDataSet[looper].value }) || isThisWithinXPercentOfThat({ value: transientData.modeDataSet[looper].value, xPercentRange: xPercentRange, xPercentTarget: value })) { // toString for more precise comparison transientData.modeDataSet[looper].frequency++; var indexOfValue = -1; // eslint-disable-next-line no-loop-func matchFound = transientData.modeDataSet.some(function (element, index) { if (looper !== index) { // only increment if it was already in the list if (element.value.toString() === value.toString()) { indexOfValue = index; if (indexOfValue > 0) { transientData.modeDataSet[indexOfValue].frequency++; return true; } } } }); return "break"; } }; for (var looper = 0; looper < transientData.modeDataSet.length; looper++) { var _ret = _loop(looper); if (_ret === "break") break; } if (!matchFound) { transientData.modeDataSet.push({ value: value, frequency: 1 }); } transientData.medianDataSet.push(value); }; var aggregateMediansAndModesListProcess = function aggregateMediansAndModesListProcess(_ref11) { var aggregate = _ref11.aggregate, events = _ref11.events, propertyKey = _ref11.propertyKey, flowDirection = _ref11.flowDirection, _ref11$modeMax = _ref11.modeMax, modeMax = _ref11$modeMax === void 0 ? 5 : _ref11$modeMax, transientData = _ref11.transientData; var modes = findModes(transientData.modeDataSet, modeMax); var medians = findMedians(transientData.medianDataSet); aggregate.modes = modes; aggregate.medians = medians; }; var aggregateMediansAndModesEventProcess = function aggregateMediansAndModesEventProcess(_ref12) { var aggregate = _ref12.aggregate, event = _ref12.event, date = _ref12.date, propertyKey = _ref12.propertyKey, flowDirection = _ref12.flowDirection, xPercentRange = _ref12.xPercentRange, transientData = _ref12.transientData; if (flowDirection === POSITIVE && event[propertyKey] > 0) { aggregate.eventCount++; pushToMediansAndModesList({ value: event[propertyKey], xPercentRange: xPercentRange, transientData: transientData }); } else if (flowDirection === NEGATIVE && event[propertyKey] < 0) { aggregate.eventCount++; pushToMediansAndModesList({ value: Math.abs(event[propertyKey]), xPercentRange: xPercentRange, transientData: transientData }); } else if (flowDirection === BOTH && event[propertyKey] !== 0) { aggregate.eventCount++; pushToMediansAndModesList({ value: event[propertyKey], xPercentRange: xPercentRange, transientData: transientData }); } }; var aggregateMediansAndModesInit = function aggregateMediansAndModesInit(_ref13) { var aggregate = _ref13.aggregate, event = _ref13.event, date = _ref13.date, propertyKey = _ref13.propertyKey, flowDirection = _ref13.flowDirection, transientData = _ref13.transientData; transientData.modeDataSet = []; transientData.medianDataSet = []; }; /* TODO: due to remove the following variables from the aggregate assignment structs, they can probably be removed from several functions frequency, propertyKey, flowDirection, selectionLimit, modeMax, xPercentRange, dayCycles later I also removed group(name) and type */ var findAnnualAggregates = function findAnnualAggregates(_ref14) { var name = _ref14.name, type = _ref14.type, _ref14$frequency = _ref14.frequency, frequency = _ref14$frequency === void 0 ? ANNUALLY : _ref14$frequency, events = _ref14.events, _ref14$propertyKey = _ref14.propertyKey, propertyKey = _ref14$propertyKey === void 0 ? 'balanceEnding' : _ref14$propertyKey, fiscalYearStart = _ref14.fiscalYearStart, _ref14$flowDirection = _ref14.flowDirection, flowDirection = _ref14$flowDirection === void 0 ? BOTH : _ref14$flowDirection, _ref14$selectionLimit = _ref14.selectionLimit, selectionLimit = _ref14$selectionLimit === void 0 ? DEFAULT_SELECTION_LIMIT : _ref14$selectionLimit, _ref14$reverse = _ref14.reverse, reverse = _ref14$reverse === void 0 ? false : _ref14$reverse, modeMax = _ref14.modeMax, _ref14$xPercentRange = _ref14.xPercentRange, xPercentRange = _ref14$xPercentRange === void 0 ? 0 : _ref14$xPercentRange, _ref14$aggregateInit = _ref14.aggregateInit, aggregateInit = _ref14$aggregateInit === void 0 ? function () {} : _ref14$aggregateInit, _ref14$aggregateEvent = _ref14.aggregateEventProcess, aggregateEventProcess = _ref14$aggregateEvent === void 0 ? function () {} : _ref14$aggregateEvent, _ref14$aggregateListP = _ref14.aggregateListProcess, aggregateListProcess = _ref14$aggregateListP === void 0 ? function () {} : _ref14$aggregateListP, _ref14$transientData = _ref14.transientData, transientData = _ref14$transientData === void 0 ? {} : _ref14$transientData; try { var aggregateDateStart = createTimeZone({ timeZone: events[0].timeZone, timeZoneType: events[0].timeZoneType, dateString: events[0].dateStart }); var aggregateDateEnd = createTimeZone({ timeZone: events[events.length - 1].timeZone, timeZoneType: events[events.length - 1].timeZoneType, dateString: events[events.length - 1].dateStart // an event's dateEnd is definitely not relevant since there is no need to loop over extra days when there are no more new events }); var effectiveDateStartString; if (!fiscalYearStart) { effectiveDateStartString = aggregateDateStart.clone().startOf('year').format(DATE_FORMAT_STRING); } else { var dateStartSplit = events[0].dateStart.split(DATE_DELIMITER); var potentialDateStartString = "".concat(dateStartSplit[0]).concat(DATE_DELIMITER).concat(fiscalYearStart); if (potentialDateStartString > events[0].dateStart) { // we have to make sure that the effectiveDateStartString is less than the dateStart of the first event var oldYearString = potentialDateStartString.split(DATE_DELIMITER)[0]; var oldYearNumber = parseInt(oldYearString, 10); oldYearNumber -= 1; var unpaddedNewYearString = oldYearNumber.toString(); var paddedNewYearString = unpaddedNewYearString.padStart(4, '0'); effectiveDateStartString = paddedNewYearString; } else { effectiveDateStartString = potentialDateStartString; } } var timeStream = new TimeStream({ effectiveDateStartString: effectiveDateStartString, effectiveDateEndString: aggregateDateEnd.format(DATE_FORMAT_STRING), timeStartString: null, timeEndString: null, timeZone: events[0].timeZone, // all of the events should be expected to share the same timeZone timeZoneType: events[0].timeZoneType }); var aggregates = []; var aggregate = { entityType: AGGREGATE, // can be used to categorize if an object is a rule, event, or an aggregate eventCount: 0, dateStart: timeStream.effectiveDateStartString, dateEnd: timeStream.effectiveDateStartString // default value; it will most likely be modified }; aggregateInit({ aggregate: aggregate, event: events[0], date: timeStream.looperDate, propertyKey: propertyKey, flowDirection: flowDirection, transientData: transientData }); var breakLoop = false; var eventLooper = 0; var event; var termDateString = timeStream.looperDate.clone().add(1, 'years').add(-1, 'days').format(DATE_FORMAT_STRING); do { var currentDateInFocus = timeStream.looperDate.format(DATE_FORMAT_STRING); while (eventLooper < events.length && events[eventLooper].dateStart === currentDateInFocus && !isUndefinedOrNull(events[eventLooper][propertyKey])) { event = events[eventLooper]; // begin logic unique to this specific function aggregateEventProcess({ aggregate: aggregate, event: event, date: timeStream.looperDate, propertyKey: propertyKey, flowDirection: flowDirection, xPercentRange: xPercentRange, transientData: transientData }); // end logic unique to this specific function eventLooper++; } // if we hit the termDate with a zero event count then reset the termDate and the aggregate dateStart if (currentDateInFocus === termDateString && aggregate.eventCount === 0) { aggregate.dateStart = currentDateInFocus; aggregate.dateEnd = currentDateInFocus; // default value termDateString = timeStream.looperDate.clone().add(1, 'years').add(-1, 'days').format(DATE_FORMAT_STRING); } if (aggregate.eventCount > 0 && (timeStream.looperDate.format(DATE_FORMAT_STRING) >= termDateString || eventLooper === events.length)) { aggregateListProcess({ aggregate: aggregate, events: events, propertyKey: propertyKey, flowDirection: flowDirection, selectionLimit: selectionLimit, reverse: reverse, modeMax: modeMax, transientData: transientData }); aggregate.dateEnd = timeStream.looperDate.format(DATE_FORMAT_STRING); aggregates.push(aggregate); termDateString = timeStream.looperDate.clone().add(1, 'years').add(-1, 'days').format(DATE_FORMAT_STRING); // FORCE the last day of the month (obviously since months do not all have the same amount of days); if (eventLooper >= events.length) { breakLoop = true; } if (!breakLoop) { aggregate = { entityType: AGGREGATE, // can be used to categorize if an object is a rule, event, or an aggregate eventCount: 0, dateStart: events[eventLooper].dateStart, dateEnd: events[eventLooper].dateStart // default value; it will most likely be modified }; aggregateInit({ aggregate: aggregate, event: events[eventLooper], date: timeStream.looperDate, propertyKey: propertyKey, flowDirection: flowDirection, transientData: transientData }); } } } while (!breakLoop && timeStream.stream1DayForward()); return aggregates; } catch (err) { throw errorDisc({ err: err }); } }; var findAnnualMediansAndModes = function findAnnualMediansAndModes(_ref15) { var name = _ref15.name, _ref15$type = _ref15.type, type = _ref15$type === void 0 ? MEDIANS_AND_MODES : _ref15$type, _ref15$frequency = _ref15.frequency, frequency = _ref15$frequency === void 0 ? ANNUALLY : _ref15$frequency, events = _ref15.events, _ref15$propertyKey = _ref15.propertyKey, propertyKey = _ref15$propertyKey === void 0 ? 'balanceEnding' : _ref15$propertyKey, fiscalYearStart = _ref15.fiscalYearStart, _ref15$flowDirection = _ref15.flowDirection, flowDirection = _ref15$flowDirection === void 0 ? BOTH : _ref15$flowDirection, _ref15$modeMax = _ref15.modeMax, modeMax = _ref15$modeMax === void 0 ? 5 : _ref15$modeMax, _ref15$xPercentRange = _ref15.xPercentRange, xPercentRange = _ref15$xPercentRange === void 0 ? 0 : _ref15$xPercentRange, _ref15$aggregateInit = _ref15.aggregateInit, aggregateInit = _ref15$aggregateInit === void 0 ? function () {} : _ref15$aggregateInit, _ref15$aggregateEvent = _ref15.aggregateEventProcess, aggregateEventProcess = _ref15$aggregateEvent === void 0 ? function () {} : _ref15$aggregateEvent, _ref15$aggregateListP = _ref15.aggregateListProcess, aggregateListProcess = _ref15$aggregateListP === void 0 ? function () {} : _ref15$aggregateListP, _ref15$transientData = _ref15.transientData, transientData = _ref15$transientData === void 0 ? {} : _ref15$transientData; var aggregates = findAnnualAggregates({ name: name, type: type, frequency: frequency, events: events, propertyKey: propertyKey, fiscalYearStart: fiscalYearStart, flowDirection: flowDirection, modeMax: modeMax, xPercentRange: xPercentRange, aggregateInit: aggregateMediansAndModesInit, aggregateEventProcess: aggregateMediansAndModesEventProcess, aggregateListProcess: aggregateMediansAndModesListProcess, transientData: transientData }); return aggregates; }; var findAnnualSumsAndAvgs = function findAnnualSumsAndAvgs(_ref16) { var name = _ref16.name, _ref16$type = _ref16.type, type = _ref16$type === void 0 ? SUMS_AND_AVERAGES : _ref16$type, _ref16$frequency = _ref16.frequency, frequency = _ref16$frequency === void 0 ? ANNUALLY : _ref16$frequency, events = _ref16.events, _ref16$propertyKey = _ref16.propertyKey, propertyKey = _ref16$propertyKey === void 0 ? 'balanceEnding' : _ref16$propertyKey, fiscalYearStart = _ref16.fiscalYearStart, _ref16$flowDirection = _ref16.flowDirection, flowDirection = _ref16$flowDirection === void 0 ? BOTH : _ref16$flowDirection, _ref16$aggregateInit = _ref16.aggregateInit, aggregateInit = _ref16$aggregateInit === void 0 ? function () {} : _ref16$aggregateInit, _ref16$aggregateEvent = _ref16.aggregateEventProcess, aggregateEventProcess = _ref16$aggregateEvent === void 0 ? function () {} : _ref16$aggregateEvent, _ref16$aggregateListP = _ref16.aggregateListProcess, aggregateListProcess = _ref16$aggregateListP === void 0 ? function () {} : _ref16$aggregateListP, _ref16$transientData = _ref16.transientData, transientData = _ref16$transientData === void 0 ? {} : _ref16$transientData; var aggregates = findAnnualAggregates({ name: name, type: type, frequency: frequency, events: events, propertyKey: propertyKey, fiscalYearStart: fiscalYearStart, flowDirection: flowDirection, aggregateInit: aggregateSumsAndAvgsInit, aggregateEventProcess: aggregateSumsAndAvgsEventProcess, aggregateListProcess: aggregateSumsAndAvgsListProcess, transientData: transientData }); return aggregates; }; var findAnnualMinimumsAndMaximums = function findAnnualMinimumsAndMaximums(_ref17) { var name = _ref17.name, _ref17$type = _ref17.type, type = _ref17$type === void 0 ? MINIMUMS_AND_MAXIMUMS : _ref17$type, _ref17$frequency = _ref17.frequency, frequency = _ref17$frequency === void 0 ? ANNUALLY : _ref17$frequency, events = _ref17.events, _ref17$propertyKey = _ref17.propertyKey, propertyKey = _ref17$propertyKey === void 0 ? 'balanceEnding' : _ref17$propertyKey, fiscalYearStart = _ref17.fiscalYearStart, _ref17$flowDirection = _ref17.flowDirection, flowDirection = _ref17$flowDirection === void 0 ? BOTH : _ref17$flowDirection, _ref17$aggregateInit = _ref17.aggregateInit, aggregateInit = _ref17$aggregateInit === void 0 ? function () {} : _ref17$aggregateInit, _ref17$aggregateEvent = _ref17.aggregateEventProcess, aggregateEventProcess = _ref17$aggregateEvent === void 0 ? function () {} : _ref17$aggregateEvent, _ref17$transientData = _ref17.transientData, transientData = _ref17$transientData === void 0 ? {} : _ref17$transientData; var aggregates = findAnnualAggregates({ name: name, type: type, frequency: frequency, events: events, propertyKey: propertyKey, fiscalYearStart: fiscalYearStart, flowDirection: flowDirection, aggregateInit: aggregateMinimumsAndMaximumsInit, aggregateEventProcess: aggregateMinimumsAndMaximumsEventProcess, transientData: transientData }); return aggregates; }; var findAnnualGreatestValues = function findAnnualGreatestValues(_ref18) { var name = _ref18.name, _ref18$type = _ref18.type, type = _ref18$type === void 0 ? GREATEST_VALUES : _ref18$type, _ref18$frequency = _ref18.frequency, frequency = _ref18$frequency === void 0 ? ANNUALLY : _ref18$frequency, events = _ref18.events, _ref18$propertyKey = _ref18.propertyKey, propertyKey = _ref18$propertyKey === void 0 ? 'balanceEnding' : _ref18$propertyKey, fiscalYearStart = _ref18.fiscalYearStart, _ref18$flowDirection = _ref18.flowDirection, flowDirection = _ref18$flowDirection === void 0 ? BOTH : _ref18$flowDirection, _ref18$selectionLimit = _ref18.selectionLimit, selectionLimit = _ref18$selectionLimit === void 0 ? DEFAULT_SELECTION_LIMIT : _ref18$selectionLimit, _ref18$reverse = _ref18.reverse, reverse = _ref18$reverse === void 0 ? false : _ref18$reverse, _ref18$aggregateInit = _ref18.aggregateInit, aggregateInit = _ref18$aggregateInit === void 0 ? function () {} : _ref18$aggregateInit, _ref18$aggregateEvent = _ref18.aggregateEventProcess, aggregateEventProcess = _ref18$aggregateEvent === void 0 ? function () {} : _ref18$aggregateEvent, _ref18$aggregateListP = _ref18.aggregateListProcess, aggregateListProcess = _ref18$aggregateListP === void 0 ? function () {} : _ref18$aggregateListP, _ref18$transientData = _ref18.transientData, transientData = _ref18$transientData === void 0 ? {} : _ref18$transientData; var aggregates = findAnnualAggregates({ name: name, type: type, frequency: frequency, events: events, propertyKey: propertyKey, fiscalYearStart: fiscalYearStart, flowDirection: flowDirection, selectionLimit: selectionLimit, reverse: reverse, aggregateInit: aggregateGreatestValuesInit, aggregateEventProcess: aggregateGreatestValuesEventProcess, aggregateListProcess: aggregateGreatestValuesListProcess, transientData: transientData }); return aggregates; }; var findMonthlyAggregates = function findMonthlyAggregates(_ref19) { var name = _ref19.name, type = _ref19.type, _ref19$frequency = _ref19.frequency, frequency = _ref19$frequency === void 0 ? MONTHLY : _ref19$frequency, events = _ref19.events, _ref19$propertyKey = _ref19.propertyKey, propertyKey = _ref19$propertyKey === void 0 ? 'balanceEnding' : _ref19$propertyKey, _ref19$flowDirection = _ref19.flowDirection, flowDirection = _ref19$flowDirection === void 0 ? BOTH : _ref19$flowDirection, _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, modeMax = _ref19.modeMax, _ref19$xPercentRange = _ref19.xPercentRange, xPercentRange = _ref19$xPercentRange === void 0 ? 0 : _ref19$xPercentRange, _ref19$aggregateInit = _ref19.aggregateInit, aggregateInit = _ref19$aggregateInit === void 0 ? function () {} : _ref19$aggregateInit, _ref19$aggregateEvent = _ref19.aggregateEventProcess, aggregateEventProcess = _ref19$aggregateEvent === void 0 ? function () {} : _ref19$aggregateEvent, _ref19$aggregateListP = _ref19.aggregateListProcess, aggregateListProcess = _ref19$aggregateListP === void 0 ? function () {} : _ref19$aggregateListP, _ref19$transientData = _ref19.transientData, transientData = _ref19$transientData === void 0 ? {} : _ref19$transientData; try { var aggregateDateStart = createTimeZone({ timeZone: events[0].timeZone, timeZoneType: events[0].timeZoneType, dateString: events[0].dateStart }); var aggregateDateEnd = createTimeZone({ timeZone: events[events.length - 1].timeZone, timeZoneType: events[events.length - 1].timeZoneType, dateString: events[events.length - 1].dateStart // an event's dateEnd is definitely not relevant since there is no need to loop over extra days when there are no more new events }); var timeStream = new TimeStream({ effectiveDateStartString: aggregateDateStart.clone().startOf('month').format(DATE_FORMAT_STRING), effectiveDateEndString: aggregateDateEnd.format(DATE_FORMAT_STRING), timeStartString: null, timeEndString: null, timeZone: events[0].timeZone, // all of the events should be expected to share the same timeZone timeZoneType: events[0].timeZoneType }); var aggregates = []; var aggregate = { entityType: AGGREGATE, // can be used to categorize if an object is a rule, event, or an aggregate eventCount: 0, dateStart: timeStream.effectiveDateStartString, dateEnd: timeStream.effectiveDateStartString // default value; it will most likely be modified }; aggregateInit({ aggregate: aggregate, event: events[0], date: timeStream.looperDate, propertyKey: propertyKey, flowDirection: flowDirection, transientData: transientData }); var breakLoop = false; var eventLooper = 0; var event; var termDateString = timeStream.looperDate.clone().endOf('month').format(DATE_FORMAT_STRING); do { var currentDateInFocus = timeStream.looperDate.format(DATE_FORMAT_STRING); while (eventLooper < events.length && events[eventLooper].dateStart === currentDateInFocus && !isUndefinedOrNull(events[eventLooper][propertyKey])) { event = events[eventLooper]; // begin logic unique to this specific function aggregateEventProcess({ aggregate: aggregate, event: event, date: timeStream.looperDate, propertyKey: propertyKey, flowDirection: flowDirection, xPercentRange: xPercentRange, transientData: transientData }); // end logic unique to this specific function eventLooper++; } // if we hit the termDate with a zero event count then reset the termDate and the aggregate dateStart if (currentDateInFocus === termDateString && aggregate.eventCount === 0) { aggregate.dateStart = currentDateInFocus; aggregate.dateEnd = currentDateInFocus; // default value termDateString = timeStream.looperDate.clone().endOf('month').format(DATE_FORMAT_STRING); } if (aggregate.eventCount > 0 && (timeStream.looperDate.format(DATE_FORMAT_STRING) >= termDateString || eventLooper === events.length)) { aggregateListProcess({ aggregate: aggregate, events: events, propertyKey: propertyKey, flowDirection: flowDirection, selectionLimit: selectionLimit, reverse: reverse, modeMax: modeMax, transientData: transientData }); aggregate.dateEnd = timeStream.looperDate.format(DATE_FORMAT_STRING); aggregates.push(aggregate); termDateString = timeStream.looperDate.clone().add(1, 'months').endOf('month').format(DATE_FORMAT_STRING); // FORCE the last day of the month (obviously since months do not all have the same amount of days); if (eventLooper >= events.length) { breakLoop = true; } if (!breakLoop) { aggregate = { entityType: AGGREGATE, // can be used to categorize if an object is a rule, event, or an aggregate eventCount: 0, dateStart: events[eventLooper].dateStart, dateEnd: events[eventLooper].dateStart // default value; it will most likely be modified }; aggregateInit({ aggregate: aggregate, event: events[eventLooper], date: timeStream.looperDate, propertyKey: propertyKey, flowDirection: flowDirection, transientData: transientData }); } } } while (!breakLoop && timeStream.stream1DayForward()); return aggregates; } catch (err) { throw errorDisc({ err: err }); } }; var findMonthlyMediansAndModes = function findMonthlyMediansAndModes(_ref20) { var name = _ref20.name, _ref20$type = _ref20.type, type = _ref20$type === void 0 ? MEDIANS_AND_MODES : _ref20$type, _ref20$frequency = _ref20.frequency, frequency = _ref20$frequency === void 0 ? MONTHLY : _ref20$frequency, events = _ref20.events, _ref20$propertyKey = _ref20.propertyKey, propertyKey = _ref20$propertyKey === void 0 ? 'balanceEnding' : _ref20$propertyKey, _ref20$flowDirection = _ref20.flowDirection, flowDirection = _ref20$flowDirection === void 0 ? BOTH : _ref20$flowDirection, _ref20$modeMax = _ref20.modeMax, modeMax = _ref20$modeMax === void 0 ? 5 : _ref20$modeMax, _ref20$xPercentRange = _ref20.xPercentRange, xPercentRange = _ref20$xPercentRange === void 0 ? 0 : _ref20$xPercentRange, _ref20$aggregateInit = _ref20.aggregateInit, aggregateInit = _ref20$aggregateInit === void 0 ? function () {} : _ref20$aggregateInit, _ref20$aggregateEvent = _ref20.aggregateEventProcess, aggregateEventProcess = _ref20$aggregateEvent === void 0 ? function () {} : _ref20$aggregateEvent, _ref20$aggregateListP = _ref20.aggregateListProcess, aggregateListProcess = _ref20$aggregateListP === void 0 ? function () {} : _ref20$aggregateListP, _ref20$transientData = _ref20.transientData, transientData = _ref20$transientData === void 0 ? {} : _ref20$transientData; var aggregates = findMonthlyAggregates({ name: name, type: type, frequency: frequency, events: events, propertyKey: propertyKey, flowDirection: flowDirection, modeMax: modeMax, xPercentRange: xPercentRange, aggregateInit: aggregateMediansAndModesInit, aggregateEventProcess: aggregateMediansAndModesEventProcess, aggregateListProcess: aggregateMediansAndModesListProcess, transientData: transientData }); return aggregates; }; var findMonthlySumsAndAvgs = function findMonthlySumsAndAvgs(_ref21) { var name = _ref21.name, _ref21$type = _ref21.type, type = _ref21$type === void 0 ? SUMS_AND_AVERAGES : _ref21$type, _ref21$frequency = _ref21.frequency, frequency = _ref21$frequency === void 0 ? MONTHLY : _ref21$frequency, events = _ref21.events, _ref21$propertyKey = _ref21.propertyKey, propertyKey = _ref21$propertyKey === void 0 ? 'balanceEnding' : _ref21$propertyKey, _ref21$flowDirection = _ref21.flowDirection, flowDirection = _ref21$flowDirection === void 0 ? BOTH : _ref21$flowDirection, _ref21$aggregateInit = _ref21.aggregateInit, aggregateInit = _ref21$aggregateInit === void 0 ? function () {} : _ref21$aggregateInit, _ref21$aggregateEvent = _ref21.aggregateEventProcess, aggregateEventProcess = _ref21$aggregateEvent === void 0 ? function () {} : _ref21$aggregateEvent, _ref21$aggregateListP = _ref21.aggregateListProcess, aggregateListProcess = _ref21$aggregateListP === void 0 ? function () {} : _ref21$aggregateListP, _ref21$transientData = _ref21.transientData, transientData = _ref21$transientData === void 0 ? {} : _ref21$transientData; var aggregates = findMonthlyAggregates({ name: name, type: type, frequency: frequency, events: events, propertyKey: propertyKey, flowDirection: flowDirection, aggregateInit: aggregateSumsAndAvgsInit, aggregateEventProcess: aggregateSumsAndAvgsEventProcess, aggregateListProcess: aggregateSumsAndAvgsListProcess, transientData: transientData }); return aggregates; }; var findMonthlyMinimumsAndMaximums = function findMonthlyMinimumsAndMaximums(_ref22) { var name = _ref22.name, _ref22$type = _ref22.type, type = _ref22$type === void 0 ? MINIMUMS_AND_MAXIMUMS : _ref22$type, _ref22$frequency = _ref22.frequency, frequency = _ref22$frequency === void 0 ? MONTHLY : _ref22$frequency, events = _ref22.events, _ref22$propertyKey = _ref22.propertyKey, propertyKey = _ref22$propertyKey === void 0 ? 'balanceEnding' : _ref22$propertyKey, _ref22$flowDirection = _ref22.flowDirection, flowDirection = _ref22$flowDirection === void 0 ? BOTH : _ref22$flowDirection, _ref22$aggregateInit = _ref22.aggregateInit, aggregateInit = _ref22$aggregateInit === void 0 ? function () {} : _ref22$aggregateInit, _ref22$aggregateEvent = _ref22.aggregateEventProcess, aggregateEventProcess = _ref22$aggregateEvent === void 0 ? function () {} : _ref22$aggregateEvent, _ref22$transientData = _ref22.transientData, transientData = _ref22$transientData === void 0 ? {} : _ref22$transientData; var aggregates = findMonthlyAggregates({ name: name, type: type, frequency: frequency, events: events, propertyKey: propertyKey, flowDirection: flowDirection, aggregateInit: aggregateMinimumsAndMaximumsInit, aggregateEventProcess: aggregateMinimumsAndMaximumsEventProcess, transientData: transientData }); return aggregates; }; var findMonthlyGreatestValues = function findMonthlyGreatestValues(_ref23) { var name = _ref23.name, _ref23$type = _ref23.type, type = _ref23$type === void 0 ? GREATEST_VALUES : _ref23$type, _ref23$frequency = _ref23.frequency, frequency = _ref23$frequency === void 0 ? MONTHLY : _ref23$frequency, events = _ref23.events, _ref23$propertyKey = _ref23.propertyKey, propertyKey = _ref23$propertyKey === void 0 ? 'balanceEnding' : _ref23$propertyKey, _ref23$flowDirection = _ref23.flowDirection, flowDirection = _ref23$flowDirection === void 0 ? BOTH : _ref23$flowDirection, _ref23$selectionLimit = _ref23.selectionLimit, selectionLimit = _ref23$selectionLimit === void 0 ? DEFAULT_SELECTION_LIMIT : _ref23$selectionLimit, _ref23$reverse = _ref23.reverse, reverse = _ref23$reverse === void 0 ? false : _ref23$reverse, _ref23$aggregateInit = _ref23.aggregateInit, aggregateInit = _ref23$aggregateInit === void 0 ? function () {} : _ref23$aggregateInit, _ref23$aggregateEvent = _ref23.aggregateEventProcess, aggregateEventProcess = _ref23$aggregateEvent === void 0 ? function () {} : _ref23$aggregateEvent, _ref23$aggregateListP = _ref23.aggregateListProcess, aggregateListProcess = _ref23$aggregateListP === void 0 ? function () {} : _ref23$aggregateListP, _ref23$transientData = _ref23.transientData, transientData = _ref23$transientData === void 0 ? {} : _ref23$transientData; var aggregates = findMonthlyAggregates({ name: name, type: type, frequency: frequency, events: events, propertyKey: propertyKey, flowDirection: flowDirection, selectionLimit: selectionLimit, reverse: reverse, aggregateInit: aggregateGreatestValuesInit, aggregateEventProcess: aggregateGreatestValuesEventProcess, aggregateListProcess: aggregateGreatestValuesListProcess, transientData: transientData }); return aggregates; }; var findWeeklyAggregates = function findWeeklyAggregates(_ref24) { var name = _ref24.name, type = _ref24.type, _ref24$frequency = _ref24.frequency, frequency = _ref24$frequency === void 0 ? WEEKLY : _ref24$frequency, events = _ref24.events, _ref24$propertyKey = _ref24.propertyKey, propertyKey = _ref24$propertyKey === void 0 ? 'balanceEnding' : _ref24$propertyKey, _ref24$flowDirection = _ref24.flowDirection, flowDirection = _ref24$flowDirection === void 0 ? BOTH : _ref24$flowDirection, _ref24$weekdayStart = _ref24.weekdayStart, weekdayStart = _ref24$weekdayStart === void 0 ? MONDAY : _ref24$weekdayStart, _ref24$dayCycles = _ref24.dayCycles, dayCycles = _ref24$dayCycles === void 0 ? 7 : _ref24$dayCycles, _ref24$cycleDateStart = _ref24.cycleDateStart, cycleDateStart = _ref24$cycleDateStart === void 0 ? null : _ref24$cycleDateStart, _ref24$selectionLimit = _ref24.selectionLimit, selectionLimit = _ref24$selectionLimit === void 0 ? DEFAULT_SELECTION_LIMIT : _ref24$selectionLimit, _ref24$reverse = _ref24.reverse, reverse = _ref24$reverse === void 0 ? false : _ref24$reverse, modeMax = _ref24.modeMax, _ref24$xPercentRange = _ref24.xPercentRange, xPercentRange = _ref24$xPercentRange === void 0 ? 0 : _ref24$xPercentRange, _ref24$aggregateInit = _ref24.aggregateInit, aggregateInit = _ref24$aggregateInit === void 0 ? function () {} : _ref24$aggregateInit, _ref24$aggregateEvent = _ref24.aggregateEventProcess, aggregateEventProcess = _ref24$aggregateEvent === void 0 ? function () {} : _ref24$aggregateEvent, _ref24$aggregateListP = _ref24.aggregateListProcess, aggregateListProcess = _ref24$aggregateListP === void 0 ? function () {} : _ref24$aggregateListP, _ref24$transientData = _ref24.transientData, transientData = _ref24$transientData === void 0 ? {} : _ref24$transientData; try { var aggregateDateStart = createTimeZone({ timeZone: events[0].timeZone, timeZoneType: events[0].timeZoneType, dateString: cycleDateStart || events[0].dateStart }); var aggregateDateEnd = createTimeZone({ timeZone: events[events.length - 1].timeZone, timeZoneType: events[events.length - 1].timeZoneType, dateString: events[events.length - 1].dateStart // an event's dateEnd is definitely not relevant since there is no need to loop over extra days when there are no more new events }); var effectiveDateStart = aggregateDateStart; if (!isUndefinedOrNull(weekdayStart)) { if (aggregateDateStart.day() === weekdayStart) { effectiveDateStart = aggregateDateStart; } else { var newDate = aggregateDateStart.clone().add(-1, 'days'); while (newDate.day() !== weekdayStart) { newDate = newDate.clone().add(-1, 'days'); } effectiveDateStart = newDate; } } else { // scenario for findDayCycleAggregates // eslint-disable-next-line no-lonely-if if (aggregateDateStart.format(DATE_FORMAT_STRING) <= events[0].dateStart) { effectiveDateStart = aggregateDateStart; } else { var _newDate = aggregateDateStart.clone().add(-dayCycles, 'days'); while (_newDate.format(DATE_FORMAT_STRING) > events[0].dateStart) { _newDate = _newDate.clone().add(-dayCycles, 'days'); } effectiveDateStart = _newDate; } } var timeStream = new TimeStream({ effectiveDateStartString: effectiveDateStart.format(DATE_FORMAT_STRING), effectiveDateEndString: aggregateDateEnd.format(DATE_FORMAT_STRING), timeStartString: null, timeEndString: null, timeZone: events[0].timeZone, // all of the events should be expected to share the same timeZone timeZoneType: events[0].timeZoneType }); var aggregates = []; var aggregate = { entityType: AGGREGATE, // can be used to categorize if an object is a rule, event, or an aggregate eventCount: 0, dateStart: timeStream.effectiveDateStartString, dateEnd: timeStream.effectiveDateStartString // default value; it will most likely be modified }; aggregateInit({ aggregate: aggregate, event: events[0], date: timeStream.looperDate, propertyKey: propertyKey, flowDirection: flowDirection, transientData: transientData }); var breakLoop = false; var eventLooper = 0; var event; var termDateString = timeStream.looperDate.clone().add(dayCycles - 1, 'days') // if timeStream.looperDate starts the cycle on MONDAY, we only want to calculate our EventProcess through SUNDAY of the same week, inclusively .format(DATE_FORMAT_STRING); do { var currentDateInFocus = timeStream.looperDate.format(DATE_FORMAT_STRING); while (eventLooper < events.length && events[eventLooper].dateStart === currentDateInFocus && !isUndefinedOrNull(events[eventLooper][propertyKey])) { event = events[eventLooper]; // begin logic unique to this specific function aggregateEventProcess({ aggregate: aggregate, event: event, date: timeStream.looperDate, propertyKey: propertyKey, flowDirection: flowDirection, xPercentRange: xPercentRange, transientData: transientData }); // end logic unique to this specific function eventLooper++; } // if we hit the termDate with a zero event count then reset the termDate and the aggregate dateStart if (currentDateInFocus === termDateString && aggregate.eventCount === 0) { aggregate.dateStart = currentDateInFocus; aggregate.dateEnd = currentDateInFocus; // default value termDateString = timeStream.looperDate.clone().add(dayCycles - 1, 'days') // if timeStream.looperDate starts the cycle on MONDAY, we only want to calculate our EventProcess through SUNDAY of the same week, inclusively .format(DATE_FORMAT_STRING); } if (aggregate.eventCount > 0 && (timeStream.looperDate.format(DATE_FORMAT_STRING) >= termDateString || eventLooper === events.length)) { aggregateListProcess({ aggregate: aggregate, events: events, propertyKey: propertyKey, flowDirection: flowDirection, weekdayStart: weekdayStart, dayCycles: dayCycles, cycleDateStart: cycleDateStart,