//@include lib_Class.js /** * Helper functions for contracts * * @author MW, ELO Digital Office GmbH * @version 1.0 * * @elowf * * @requires sol.common.DateUtils */ sol.define("sol.contract.DurationUtils", { singleton: true, /** * Returns the "end of" date * @param {Date} date Date * @param {String} unit Unit * @returns {Date} End of date */ getEndOfDate: function (date, unit) { var momentDate; momentDate = moment(date.getTime()); return momentDate.endOf(unit).startOf("day").toDate(); }, allUnits: ["y", "hy", "Q", "M", "w", "d"], /** * Returns the smallest duration unit * @param {Array} unitStrings Units strings * @return {String} */ getSmallestUnitString: function (unitStrings) { var me = this, unit, smallestUnit, unitString, i, j, u = 0; if (!unitStrings) { throw "Units strings are empty"; } if (unitStrings.length == 1) { return unitStrings[0]; } for (i = 0; i < unitStrings.length; i++) { unit = me.getKwlKey(unitStrings[i]); for (j = u; j < me.allUnits.length; j++) { if (unit == me.allUnits[j]) { u = j; } } } smallestUnit = me.allUnits[u]; for (i = 0; i < unitStrings.length; i++) { unitString = unitStrings[i]; unit = me.getKwlKey(unitString); if (smallestUnit == unit) { return unitString; } } }, /** * Calculates a duration * @param {String} startDateIso Start date * @param {String} endDateIso End date * @param {String} unit * @return {Number} */ calcDuration: function (startDateIso, endDateIso, unit) { var me = this, startDate, endDate, number; unit = me.getKwlKey(unit); if (!unit || (unit == "O")) { return 1; } if (!startDateIso || !endDateIso) { return ""; } startDate = sol.common.DateUtils.isoToDate(String(startDateIso)); endDate = sol.common.DateUtils.isoToDate(String(endDateIso)); number = sol.common.DateUtils.diff(startDate, endDate, unit, { roundUp: true }); return number; }, /** * Returns the keyword list key * @param {String} str String * @param {String} separator Separator * @return {String} */ getKwlKey: function (str, separator) { var separatorPos; if (!str) { return ""; } str = String(str); separator = separator || "-"; separatorPos = str.indexOf(separator); if (separatorPos < 0) { return str; } return str.substring(0, separatorPos).trim(); }, /** * Calculates the row total * @param {String} startDateIso Start date * @param {String} endDateIso End date * @param {Decimal} singleAmount Single amount * @param {String} unit Unit * @return {Decimal} */ calcRowTotal: function (startDateIso, endDateIso, singleAmount, unit) { var me = this, count, rowTotal; count = me.calcDuration(startDateIso, endDateIso, unit); if (count == "") { return new Decimal(0); } if (typeof singleAmount == "number") { singleAmount = new Decimal(singleAmount); } rowTotal = singleAmount.times(count); return rowTotal; }, /** * Returns the base currency * @return {String} Base currency */ getBaseCurrency: function () { return "EUR"; }, /** * Calculates the local currency amount * @param {Number} amount Amount * @param {Number} exchangeRate Exchange rate * @return {Number} */ calcLocalCurrencyAmount: function (amount, exchangeRate) { var localCurrencyAmount; localCurrencyAmount = amount / exchangeRate; return localCurrencyAmount; }, /** * Calculates contract data * @param {Object} tplSord Template Sord * @param {Object} updates Template Sord * @return {Object} updates Updates */ calcContract: function (tplSord, updates) { var me = this, additionalShiftCounter = 0, permanent, specialTerminationRight, terminationDate; if (!tplSord) { throw "Template Sord is empty"; } permanent = (tplSord.mapKeys.DURATION_TYPE == "sol.contract.form.permanent"); if (permanent) { do { me.calcNextPossibleContractEnd(tplSord, updates, additionalShiftCounter); terminationDate = me.calcTerminationDateFromNow(tplSord, updates); additionalShiftCounter++; } while (terminationDate && terminationDate.isBefore(moment().startOf("d")) && (additionalShiftCounter <= 10)); } else { me.calcContractEnd(tplSord, updates); specialTerminationRight = (tplSord.mapKeys.SPECIAL_TERMINATION_RIGHT == "1"); if (specialTerminationRight) { me.calcNextPossibleContractEnd(tplSord, updates, 0); me.calcTerminationDateFromNow(tplSord, updates); } else { me.calcTerminationDateFixed(tplSord, updates); } } me.calcReminderDate(tplSord, updates); return updates; }, /** * @private * Converts an ISO date to a moment date * @param {String} isoDate ISO date * @return {Object} Moment date */ isoToMoment: function (isoDate) { var mom; if (!isoDate || (isoDate.length < 8)) { return; } isoDate = isoDate.substr(0, 8); mom = moment(isoDate, "YYYYMMDD"); return mom; }, /** * @private * Converts a moment date to an ISO date * @param {Object} mom Moment date * @return {String} ISO date */ momentToIso: function (mom) { var isoDate; if (!mom) { return ""; } isoDate = mom.format("YYYYMMDD") + "000000"; return isoDate; }, /** * @private * Clears the contract end date * @param {Object} tplSord Tempate Sord */ clearContractEnd: function (tplSord) { if (!tplSord.objKeys.CONTRACT_START) { tplSord.objKeys.CONTRACT_END = ""; } }, /** * @private * @param {Object} tplSord Tempate Sord * @param {Object} updates Updates * @param {Number} additionalShiftCounter Additional shift counter */ calcNextPossibleContractEnd: function (tplSord, updates, additionalShiftCounter) { var me = this, nextPossibleContractEndIso = "", nextPossibleContractEnd, noticePeriodEnd, now, noticePeriod, noticePeriodNumber, noticePeriodUnit, contractEnd, contractStart, startDate, contractMinTermNumber, contractMinTermUnit, contractMinTerm, contractMinEnd, terminationPoint, contractEndIso; contractEndIso = tplSord.objKeys.CONTRACT_END; if (contractEndIso && (tplSord.mapKeys.DURATION_TYPE == "sol.contract.form.permanent")) { me.addUpdate(tplSord, updates, { type: "MAP", key: "NEXT_POSSIBLE_CONTRACT_END", value: "" }); return; } contractEnd = me.isoToMoment(contractEndIso); if (contractEnd) { contractEnd = contractEnd.endOf("d"); } now = moment(); noticePeriodNumber = tplSord.mapKeys.NOTICE_PERIOD || 0; noticePeriodUnit = me.getKwlKey(tplSord.mapKeys.NOTICE_PERIOD_UNIT); contractStart = me.isoToMoment(tplSord.objKeys.CONTRACT_START); if (!contractStart) { return; } contractMinTermNumber = tplSord.mapKeys.CONTRACT_MIN_TERM; contractMinTermUnit = me.getKwlKey(tplSord.mapKeys.CONTRACT_MIN_TERM_UNIT); contractMinTerm = sol.common.DateUtils.prepareDuration(contractMinTermNumber, contractMinTermUnit); startDate = (contractStart.isAfter(now.clone().add(contractMinTerm))) ? contractStart : now; noticePeriod = sol.common.DateUtils.prepareDuration(noticePeriodNumber, noticePeriodUnit); noticePeriodEnd = startDate.clone().add(noticePeriod); if (contractMinTerm) { contractMinEnd = contractStart.add(contractMinTerm); } terminationPoint = me.getKwlKey(tplSord.mapKeys.TERMINATION_POINT); nextPossibleContractEnd = noticePeriodEnd; if (contractMinEnd && contractMinEnd.isAfter(noticePeriodEnd)) { nextPossibleContractEnd = contractMinEnd; } terminationPoint = terminationPoint || "d"; nextPossibleContractEnd = nextPossibleContractEnd.add(additionalShiftCounter, terminationPoint); nextPossibleContractEnd = nextPossibleContractEnd.add(-1, "d"); nextPossibleContractEnd = sol.common.DateUtils.endOf(nextPossibleContractEnd, terminationPoint); nextPossibleContractEndIso = me.momentToIso(nextPossibleContractEnd); if ((tplSord.mapKeys.DURATION_TYPE == "sol.contract.form.fixedTerm") && (tplSord.mapKeys.SPECIAL_TERMINATION_RIGHT == "1")) { if (nextPossibleContractEndIso && nextPossibleContractEnd.isAfter(contractEnd)) { nextPossibleContractEndIso = ""; } } if (nextPossibleContractEnd.isSame(now) || nextPossibleContractEnd.isBefore(now) || !noticePeriod) { nextPossibleContractEndIso = ""; } me.addUpdate(tplSord, updates, { type: "MAP", key: "NEXT_POSSIBLE_CONTRACT_END", value: nextPossibleContractEndIso }); }, /** * @private * Add update * @param {Object} tplSord Template Sord * @param {Array} updates Updates array * @param {Object} update Update */ addUpdate: function (tplSord, updates, update) { if (update.type == "GRP") { tplSord.objKeys[update.key] = update.value; updates.objKeys[update.key] = update.value; } else { tplSord.mapKeys[update.key] = update.value; updates.mapKeys[update.key] = update.value; } }, /** * @private * @param {Object} tplSord Tempate Sord * @param {Object} updates Updates * @param {Number} additionalShiftCounter Additional shift counter */ calcContractEnd: function (tplSord, updates, additionalShiftCounter) { var me = this, contractStart, contractDuration, contractEnd, contractEndIso, unitKey, extensionFlag, extensionInterval, extensionIntervalUnit, extensionStatusValues, contractStatus, extension, noticePeriod, nextPossibleTermination, nextPossibleTerminationStartOfDay, nowMoment; contractStart = me.isoToMoment(tplSord.objKeys.CONTRACT_START); if (!contractStart) { return; } contractDuration = sol.common.DateUtils.prepareDuration(tplSord.mapKeys.CONTRACT_DURATION, tplSord.mapKeys.CONTRACT_DURATION_UNIT); contractEnd = contractStart.clone().add(contractDuration); extensionInterval = tplSord.mapKeys.EXTENSION_INTERVAL; extensionIntervalUnit = tplSord.mapKeys.EXTENSION_INTERVAL_UNIT; extensionFlag = (tplSord.mapKeys.EXTENSION_FLAG == "1"); contractStatus = me.getKwlKey(tplSord.objKeys.CONTRACT_STATUS); extensionStatusValues = ["D", "I", "A", "N", "S"]; if (extensionFlag && (+extensionInterval > 0) && extensionIntervalUnit && (extensionStatusValues.indexOf(contractStatus) > -1)) { extension = sol.common.DateUtils.prepareDuration(extensionInterval, extensionIntervalUnit); noticePeriod = sol.common.DateUtils.prepareDuration(tplSord.mapKeys.NOTICE_PERIOD, tplSord.mapKeys.NOTICE_PERIOD_UNIT); if (noticePeriod) { nextPossibleTermination = contractEnd.clone().subtract(noticePeriod); } else { nextPossibleTermination = contractEnd.clone(); } nextPossibleTerminationStartOfDay = nextPossibleTermination.clone().startOf("d"); nowMoment = moment(); while (nextPossibleTerminationStartOfDay.isBefore(nowMoment)) { contractEnd.add(extension); if (noticePeriod) { nextPossibleTermination = contractEnd.clone().subtract(noticePeriod); } else { nextPossibleTermination = contractEnd.clone(); } nextPossibleTerminationStartOfDay = nextPossibleTermination.clone().startOf("d"); } me.logger.debug(["ExtendContract: newContractEnd={0}]", contractEnd.toString()]); } contractEndIso = me.momentToIso(contractEnd); if (contractDuration) { unitKey = me.getKwlKey(tplSord.mapKeys.CONTRACT_DURATION_UNIT); if ((contractStart.date() == contractEnd.date()) || !unitKey || (unitKey == "d")) { contractEnd = contractEnd.add(-1, "d"); } } else { if (tplSord.objKeys.CONTRACT_END) { contractEnd = me.isoToMoment(tplSord.objKeys.CONTRACT_END); } else { return; } } contractEndIso = me.momentToIso(contractEnd); me.addUpdate(tplSord, updates, { type: "MAP", key: "NEXT_POSSIBLE_CONTRACT_END", value: "" }); me.addUpdate(tplSord, updates, { type: "GRP", key: "CONTRACT_END", value: contractEndIso }); }, /** * @private * @param {Object} tplSord Tempate Sord * @param {Object} updates Updates * @return {Moment} Next possible termination date */ calcTerminationDateFromNow: function (tplSord, updates) { var me = this, nextPossibleContractEnd, noticePeriod, nextPossibleTermination, nextPossibleTerminationIso; nextPossibleContractEnd = me.isoToMoment(tplSord.mapKeys.NEXT_POSSIBLE_CONTRACT_END); if (!nextPossibleContractEnd) { me.addUpdate(tplSord, updates, { type: "GRP", key: "NEXT_POSSIBLE_TERMINATION", value: "" }); return; } noticePeriod = sol.common.DateUtils.prepareDuration(tplSord.mapKeys.NOTICE_PERIOD, tplSord.mapKeys.NOTICE_PERIOD_UNIT); if (!noticePeriod) { return; } nextPossibleTermination = nextPossibleContractEnd.clone().subtract(noticePeriod); nextPossibleTermination = me.adjustToRealMonthEnd(nextPossibleTermination, nextPossibleContractEnd, [tplSord.mapKeys.NOTICE_PERIOD_UNIT, tplSord.mapKeys.TERMINATION_POINT]); nextPossibleTerminationIso = me.momentToIso(nextPossibleTermination); if (nextPossibleTermination.isBefore(moment().startOf("d"))) { nextPossibleTerminationIso = ""; } me.addUpdate(tplSord, updates, { type: "GRP", key: "NEXT_POSSIBLE_TERMINATION", value: nextPossibleTerminationIso }); return nextPossibleTermination; }, /** * @private * @param {Object} tplSord Template Sord * @param {Object} updates Updates * @return {Moment} Next possible termination date */ calcTerminationDateFixed: function (tplSord, updates) { var me = this, nextPossibleTerminationIso = "", contractEnd, noticePeriod, nextPossibleTermination; contractEnd = me.isoToMoment(tplSord.objKeys.CONTRACT_END); if (!contractEnd) { return; } contractEnd = me.isoToMoment(tplSord.objKeys.CONTRACT_END); noticePeriod = sol.common.DateUtils.prepareDuration(tplSord.mapKeys.NOTICE_PERIOD, tplSord.mapKeys.NOTICE_PERIOD_UNIT); if (noticePeriod) { nextPossibleTermination = contractEnd.clone().subtract(noticePeriod); if (nextPossibleTermination.isAfter(moment().startOf("d"))) { nextPossibleTermination = me.adjustToRealMonthEnd(nextPossibleTermination, contractEnd, [tplSord.mapKeys.NOTICE_PERIOD_UNIT, tplSord.mapKeys.TERMINATION_POINT]); nextPossibleTerminationIso = me.momentToIso(nextPossibleTermination); } } me.addUpdate(tplSord, updates, { type: "GRP", key: "NEXT_POSSIBLE_TERMINATION", value: nextPossibleTerminationIso }); }, /** * @private * @param {Moment} adjustMoment Adjust moment * @param {Moment} indicatorMoment Indicator moment * @param {String} indicatorUnits Indicator units */ adjustToRealMonthEnd: function (adjustMoment, indicatorMoment, indicatorUnitStrings) { var me = this, endOfMonthUnits, i, indicatorUnitString, indicatorUnitKey; endOfMonthUnits = ["y", "hy", "Q", "M"]; if (!adjustMoment || !indicatorMoment || !indicatorUnitStrings || (indicatorUnitStrings.length == 0)) { return adjustMoment; } if (!sol.common.DateUtils.isLastDayOfMonth(indicatorMoment, { toleranceDays: 1 })) { return adjustMoment; } for (i = 0; i < indicatorUnitStrings.length; i++) { indicatorUnitString = indicatorUnitStrings[i]; indicatorUnitKey = me.getKwlKey(indicatorUnitString); if (endOfMonthUnits.indexOf(indicatorUnitKey) < 0) { return adjustMoment; } } adjustMoment = adjustMoment.clone().endOf("M"); return adjustMoment; }, /** * @private * @param {Object} tplSord Tempate Sord * @param {Object} updates Updates */ calcReminderDate: function (tplSord, updates) { var me = this, startDateIso, startDate, reminderDate, reminderDateIso, reminderPeriod; startDateIso = tplSord.objKeys.NEXT_POSSIBLE_TERMINATION || tplSord.mapKeys.NEXT_POSSIBLE_CONTRACT_END || tplSord.objKeys.CONTRACT_END; startDate = me.isoToMoment(startDateIso); if (!startDate) { return; } reminderPeriod = sol.common.DateUtils.prepareDuration(tplSord.mapKeys.REMINDER_PERIOD, tplSord.mapKeys.REMINDER_PERIOD_UNIT); if (!reminderPeriod) { return; } reminderDate = startDate.subtract(reminderPeriod); reminderDateIso = me.momentToIso(reminderDate); me.addUpdate(tplSord, updates, { type: "GRP", key: "NEXT_REMINDER_DATE", value: reminderDateIso }); } });