Name
global.PercentCompleteRollupHandler
Description
No description available
Script
var PercentCompleteRollupHandler = Class.create();
PercentCompleteRollupHandler.prototype = {
initialize: function() {
},
handle: function(childRecord, parentTable, parentNavigator, rollupColumn, isAverage) {
PPMDebug.log('PercentCompleteRollupHandler.handle: ' + parentTable + " | " + parentNavigator + " | " + rollupColumn);
var rollupStructureLoader = new RollupStructureLoader();
var parentSysId = childRecord.getValue(parentNavigator);
var childRecordTable = childRecord.instanceOf("rm_story")? childRecord.getValue("sys_class_name"): "planned_task";
if(childRecord && childRecord.operation() == "delete") {
this.updateParent(parentTable, parentSysId, parentNavigator, childRecordTable, childRecord.getValue("sys_id"));
} else {
this.updateParent(parentTable, parentSysId, parentNavigator, childRecordTable);
}
},
updateParent: function(parentTable, parentSysId, parentNavigator, childTableName, excludeSysId) {
PPMDebug.log('PercentCompleteRollupHandler.updateParent: ' + parentTable + " | " + parentSysId + " | " + parentNavigator +
" | " + childTableName + " | " + excludeSysId);
//Calculate durations from sibling tasks
var parent = new GlideRecord(parentTable);
if (parent.get(parentSysId)) {
var sibling = new GlideRecord(childTableName);
sibling.addQuery(parentNavigator, parentSysId);
sibling.addNullQuery('orig_sys_id');
var lookupTable = parentTable;
if(parent.instanceOf('pm_project') || parent.instanceOf('pm_project_task'))
lookupTable = parent.instanceOf('pm_project') ? 'pm_project' : 'pm_project_task';
var ptRollupApi = new PlannedTaskRollupApi();
var sysClassExclusions = ptRollupApi.getRollupExclusions(lookupTable,'percent_complete');
if(sysClassExclusions.length >0 )
sibling.addQuery('sys_class_name', 'NOT IN', sysClassExclusions.join(','));
if( excludeSysId ) {
sibling.addQuery("sys_id", "!=", excludeSysId);
}
sibling.query();
this.rollupFromChildrenAndUpdateParent(sibling, parent, parentTable, parentSysId, parentNavigator, childTableName);
}
},
rollupFromChildrenAndUpdateParent: function(childRecords, parentRecord, parentTable, parentSysId, parentNavigator, childTableName) {
PPMDebug.log('PercentCompleteRollupHandler.rollupFromChildrenAndUpdateParent: ' + parentRecord.getValue("short_description") +
" | Child Record Count -> " + childRecords.getRowCount() + " - " + childRecords.getEncodedQuery());
var totalDur = 0;
var workedDuration = 0;
var percentCalc = 0;
var totalRecords = childRecords.getRowCount();
var schedule;
var durationCalculator = new DurationCalculator();
var hoursPerDayBasedOnSchedule;
var duration;
if (totalRecords == 0) {
parentRecord.percent_complete = percentCalc;
parentRecord.update();
} else {
var mileStones = 0;
var completedMileStones = 0;
while (childRecords.next()) {
if(!schedule && childRecords.top_task)
schedule = childRecords.top_task.schedule;
if (schedule) {
var ptGlobalApi = new PTGlobalAPI();
if(JSUtil.nil(hoursPerDayBasedOnSchedule))
hoursPerDayBasedOnSchedule = ptGlobalApi.getHoursPerDay(childRecords.start_date, schedule, childRecords.getUniqueValue());
//Duration value is in format d HH:mm:ss, to get hours,minutes,values we are splitting on space. if duration only has hours it will give us HH:mm:ss
// durationCalculator._timeToSeconds will calculate seconds if we give value in the format HH:mm:ss
var durationArray = childRecords.duration.getGlideObject().getDurationValue().split(' ');
var durValueIn_HH_mm_ss_format = durationArray.length == 2 ? childRecords.duration.getGlideObject().getDurationValue().split(' ')[1] : childRecords.duration.getGlideObject().getDurationValue();
duration = hoursPerDayBasedOnSchedule * parseInt(childRecords.duration.getGlideObject().getDayPart()) * 60 * 60 + durationCalculator._timeToSeconds(durValueIn_HH_mm_ss_format);
} else {
duration = childRecords.duration.getGlideObject().getNumericValue() / 1000;
}
totalDur += duration;
if (JSUtil.notNil(childRecords.getValue("percent_complete")) && childRecords.getValue("percent_complete") != "0") {
workedDuration += duration * (childRecords.percent_complete / 100);
}
if (childRecords.milestone == true) {
mileStones++;
if (JSUtil.notNil(childRecords.getValue("percent_complete")) && childRecords.getValue("percent_complete") != "0") {
if ((childRecords.percent_complete / 100) === 1) {
completedMileStones++;
}
}
}
}
percentCalc = (workedDuration / totalDur) * 100;
/* case 1: when project contains all milestones
then project %complete will be 100% ONLY if all the milestones are 100% complete.
*/
if (totalRecords === mileStones) {
//even if one is not completed then its 0%
if (completedMileStones == mileStones) {
percentCalc = 100;
} else {
percentCalc = 0;
}
} else {
/* case 2: when project has milestones and regular tasks
Project %complete = 100% ONLY when all the milestones & regular tasks are 100% complete.
*/
if (!isNaN(percentCalc) && parseInt(percentCalc) === 100 && completedMileStones != mileStones) {
percentCalc = 99;
}
}
if (!isNaN(percentCalc) && parentRecord.percent_complete != percentCalc) {
parentRecord.percent_complete = percentCalc;
PPMDebug.log('PercentCompleteRollupHandler.rollupFromChildrenAndUpdateParent - Updating Parent Percent Complete -> ' +
parentRecord.getValue("short_description") + " - " + percentCalc);
parentRecord.update();
} else {
/* parent percent complete and percentCalc are same, but there will be case where its parent
percent complete will be different. this else condition handles this scenerio
ex:
Project
T1 - Closed Complete - 100 percent - 20 days
T2 - Pending - 0% - 2 days
T21 - Pending - 0% - 1 day
T22 - Pending - 0% - 1 day
for above project structure, if T22 duration is updated to 20 days, T2 duration will get updated to 20 days and this will have impact of Project percent complete, though there is no change in T2 percent complete
*/
if (parentRecord.parent) {
var newParent = parentRecord.parent.getRefRecord();
if (newParent.instanceOf('pm_project') || newParent.instanceOf('pm_project_task'))
parentTable = newParent.instanceOf('pm_project') ? 'pm_project' : 'pm_project_task';
this.updateParent(parentTable, newParent.getValue('sys_id'), parentNavigator, childTableName)
}
}
}
},
type: 'PercentCompleteRollupHandler'
};
Sys ID
1e6e73619f001200598a5bb0657fcfc2