5730 lines
305 KiB
JavaScript
5730 lines
305 KiB
JavaScript
'use strict';
|
|
|
|
uiDirectives.factory('activityCalendarUIService', ['activityCalendarService', 'dataSources', 'scenarioDetailsService', 'teamInfoService', 'hoursResourcesConverter', 'roundService', 'calculateDistributionService', 'cellHighlightingService', '$filter', '$timeout', '$q', '$http', '$templateCache', function (activityCalendarService, dataSources, scenarioDetailsService, teamInfoService, hoursResourcesConverter, roundService, calculateDistributionService, cellHighlightingService, $filter, $timeout, $q, $http, $templateCache) {
|
|
var resolveRange = function (header, startDate, endDate) {
|
|
if (!header || !header.getFirstWeek || !header.getLastWeek || !startDate || !endDate) {
|
|
return;
|
|
}
|
|
|
|
var firstWeek = header.getFirstWeek();
|
|
var lastWeek = header.getLastWeek();
|
|
if (!firstWeek || !lastWeek) {
|
|
return;
|
|
}
|
|
|
|
var range = {
|
|
startDate: Math.max(startDate, firstWeek.Milliseconds),
|
|
endDate: Math.min(endDate, lastWeek.Milliseconds)
|
|
};
|
|
|
|
return range;
|
|
};
|
|
var checkAnyTeamEditable = function (teams) {
|
|
if (!teams || !teams.length) {
|
|
return false;
|
|
}
|
|
|
|
for (var i = 0; i < teams.length; i++) {
|
|
if (!teams[i].ReadOnly) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
};
|
|
var checkAllTeamsEditable = function (teams) {
|
|
if (!teams || !teams.length) {
|
|
return false;
|
|
}
|
|
|
|
for (var i = 0; i < teams.length; i++) {
|
|
if (teams[i].ReadOnly === true) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
};
|
|
var service = {
|
|
allocationMode: {
|
|
teamAllocation: 1, // Team Allocations
|
|
needAllocation: 2, // Project Need (ScenarioDetail)
|
|
remainingNeedAllocation: 3, // Project Need minus Team Allocations
|
|
unassignedNeedAllocation: 4, // Project Need minus Resource Allocations
|
|
remainingTeamAllocation: 5, // Team Allocations minus Resource Allocations
|
|
|
|
defaultHoursCollectionName: 'QuantityHoursValues',
|
|
defaultResourcesCollectionName: 'QuantityResourcesValues',
|
|
defaultHoursTotalName: 'TotalHoursValue',
|
|
defaultResourcesTotalName: 'TotalResourcesValue',
|
|
},
|
|
filterEntityType: {
|
|
team: 1,
|
|
view: 2,
|
|
company: 3,
|
|
resource: 4,
|
|
report: 5,
|
|
},
|
|
viewRowTemplates: {
|
|
projectRowTemplate: '/Content/templates/ActivityCalendar/projectRow.html',
|
|
projectRowNumbersTemplate: '/Content/templates/ActivityCalendar/projectNumbersRow.html',
|
|
expCatRowTemplate: '/Content/templates/ActivityCalendar/expCatRow.html',
|
|
expCatRowNumbersTemplate: '/Content/templates/ActivityCalendar/expCatNumbersRow.html',
|
|
resourceRowTemplate: '/Content/templates/ActivityCalendar/resourceRow.html',
|
|
resourceRowNumbersTemplate: '/Content/templates/ActivityCalendar/resourceNumbersRow.html',
|
|
expCatUnallocatedRowTemplate: '/Content/templates/ActivityCalendar/expCatUnallocatedRow.html',
|
|
expCatUnallocatedRowNumbersTemplate: '/Content/templates/ActivityCalendar/expCatUnallocatedNumbersRow.html',
|
|
teamRowTemplate: '/Content/templates/ActivityCalendar/teamRow.html',
|
|
teamRowNumbersTemplate: '/Content/templates/ActivityCalendar/teamNumbersRow.html',
|
|
teamUnallocatedRowTemplate: '/Content/templates/ActivityCalendar/teamUnallocatedRow.html',
|
|
teamUnallocatedRowNumbersTemplate: '/Content/templates/ActivityCalendar/teamUnallocatedNumbersRow.html',
|
|
resourceGbrRowTemplate: '/Content/templates/ActivityCalendar/resourceGbrRow.html',
|
|
resourceGbrRowNumbersTemplate: '/Content/templates/ActivityCalendar/resourceGbrNumbersRow.html',
|
|
projectGbrRowTemplate: '/Content/templates/ActivityCalendar/projectGbrRow.html',
|
|
projectGbrRowNumbersTemplate: '/Content/templates/ActivityCalendar/projectGbrNumbersRow.html',
|
|
projectRcRowTemplate: '/Content/templates/ActivityCalendar/projectRcRow.html',
|
|
projectRcRowNumbersTemplate: '/Content/templates/ActivityCalendar/projectRcNumbersRow.html',
|
|
projectActualsRowTemplate: '/Content/templates/ActivityCalendar/projectActualsRow.html',
|
|
projectActualsRowNumbersTemplate: '/Content/templates/ActivityCalendar/projectActualsNumbersRow.html',
|
|
projectSupplyDemandReportRowTemplate: '/Content/templates/SupplyDemandReport/projectRow.html',
|
|
projectSupplyDemandReportRowNumbersTemplate: '/Content/templates/SupplyDemandReport/projectNumbersRow.html',
|
|
expCatSupplyDemandReportRowTemplate: '/Content/templates/SupplyDemandReport/expCatRow.html',
|
|
expCatSupplyDemandReportRowNumbersTemplate: '/Content/templates/SupplyDemandReport/expCatNumbersRow.html',
|
|
},
|
|
cacheTemplates: function () {
|
|
var promises = [];
|
|
angular.forEach(this.viewRowTemplates, function (templateUrl, key) {
|
|
console.debug("cacheTemplates "+ templateUrl + " query"); // DEBUG
|
|
console.time("cacheTemplates " + templateUrl); // DEBUG
|
|
promises.push(dataSources.loadStaticFile(templateUrl + '?v=' + new Date().getTime())
|
|
.then(function (response) {
|
|
console.timeEnd("cacheTemplates " + templateUrl); // DEBUG
|
|
$templateCache.put(templateUrl, response.data);
|
|
}, function (response) {
|
|
throw 'An error occurred while loading template ' + templateUrl;
|
|
}));
|
|
});
|
|
return promises;
|
|
},
|
|
getUtcNow: function () {
|
|
var nowDate = new Date();
|
|
var year = nowDate.getUTCFullYear();
|
|
var month = nowDate.getUTCMonth();
|
|
var day = nowDate.getUTCDate();
|
|
var hours = nowDate.getUTCHours();
|
|
var minutes = nowDate.getUTCMinutes();
|
|
var utcNowMs = Date.UTC(year, month, day, hours, minutes);
|
|
return utcNowMs;
|
|
},
|
|
getAllocationMode: function (viewModelRow, customAllocationMode) {
|
|
// Calculates allocation mode for Row
|
|
// If no values are set in the Row as well as no custom value specified, returns default value (team allocations)
|
|
var result = viewModelRow && viewModelRow.AllocationMode && angular.isNumber(viewModelRow.AllocationMode) ?
|
|
Number(viewModelRow.AllocationMode) :
|
|
(angular.isNumber(customAllocationMode) ? Number(customAllocationMode) : this.allocationMode.teamAllocation);
|
|
return result;
|
|
},
|
|
getCollectionNames4AllocationMode: function (allocationMode) {
|
|
switch (allocationMode) {
|
|
case this.allocationMode.needAllocation:
|
|
return {
|
|
hoursCollectionName: 'NeedHoursValues',
|
|
hoursTotal: 'TotalNeedHoursValue',
|
|
resourcesCollectionName: 'NeedResourcesValues',
|
|
resourceTotal: 'TotalNeedResourcesValue'
|
|
};
|
|
case this.allocationMode.remainingNeedAllocation:
|
|
return {
|
|
hoursCollectionName: 'RemainingNeedHoursValues',
|
|
hoursTotal: 'TotalRemainingNeedHoursValue',
|
|
resourcesCollectionName: 'RemainingNeedResourcesValues',
|
|
resourceTotal: 'TotalRemainingNeedResourcesValue'
|
|
};
|
|
case this.allocationMode.unassignedNeedAllocation:
|
|
return {
|
|
hoursCollectionName: 'UnassignedNeedHoursValues',
|
|
hoursTotal: 'TotalUnassignedNeedHoursValue',
|
|
resourcesCollectionName: 'UnassignedNeedResourcesValues',
|
|
resourceTotal: 'TotalUnassignedNeedResourcesValue'
|
|
};
|
|
case this.allocationMode.remainingTeamAllocation:
|
|
return {
|
|
hoursCollectionName: 'RemainingTAHoursValues',
|
|
hoursTotal: 'TotalRemainingTAHoursValue',
|
|
resourcesCollectionName: 'RemainingTAResourcesValues',
|
|
resourceTotal: 'TotalRemainingTAResourcesValue'
|
|
};
|
|
default:
|
|
return {
|
|
hoursCollectionName: this.allocationMode.defaultHoursCollectionName,
|
|
hoursTotal: this.allocationMode.defaultHoursTotalName,
|
|
resourcesCollectionName: this.allocationMode.defaultResourcesCollectionName,
|
|
resourceTotal: this.allocationMode.defaultResourcesTotalName
|
|
};
|
|
}
|
|
},
|
|
getTeamAllocations4Display: function (filter, displayMode, header, calendarDate, projectId, expCatId, teamId) {
|
|
var result = [];
|
|
if (!filter || !displayMode || !header || !calendarDate || !projectId || !expCatId)
|
|
return result;
|
|
|
|
var project = activityCalendarService.getProjectById(filter, projectId);
|
|
if (!project || !project.ActiveScenario || !project.ActiveScenario.Id || !project.Teams)
|
|
return result;
|
|
|
|
var scenarioId = project.ActiveScenario.Id;
|
|
var teamAllocations = {};
|
|
var teams = [];
|
|
var weekendings = [];
|
|
|
|
if (calendarDate.DataType == Header.DataType.Week) {
|
|
// Get allocations for a single weekending
|
|
weekendings.push(calendarDate.Milliseconds);
|
|
}
|
|
|
|
if (calendarDate.DataType == Header.DataType.Month) {
|
|
// Get summary allocations for month
|
|
weekendings = header.getWeeksInMonth(calendarDate.ParentIndex);
|
|
}
|
|
|
|
if (!weekendings || (weekendings.length < 1))
|
|
return result;
|
|
|
|
if (teamId) {
|
|
// Get data for single team
|
|
teams.push(teamId);
|
|
}
|
|
else {
|
|
// Get data for all project teams
|
|
teams = Object.keys(project.Teams);
|
|
}
|
|
|
|
var valuesCount = weekendings.length;
|
|
for (var tIndex = 0; tIndex < teams.length; tIndex++) {
|
|
var currentTeamId = teams[tIndex];
|
|
var currentTeam = teamInfoService.getById(currentTeamId);
|
|
var needValue = undefined;
|
|
|
|
for (var wIndex = 0; wIndex < weekendings.length; wIndex++) {
|
|
var weekending = weekendings[wIndex];
|
|
var allocatedHours = activityCalendarService.getTeamAllocations4Scenario4Week(filter, scenarioId, [currentTeamId], expCatId, weekending);
|
|
|
|
if (angular.isNumber(allocatedHours)) {
|
|
needValue = (needValue || 0) + allocatedHours;
|
|
}
|
|
}
|
|
|
|
if (angular.isNumber(needValue)) {
|
|
if (displayMode.ShowAvgTotals && !displayMode.IsUOMHours && (valuesCount > 1)) {
|
|
needValue = needValue / valuesCount;
|
|
}
|
|
|
|
if (displayMode.IsUOMHours) {
|
|
needValue = roundService.roundQuantity(needValue);
|
|
}
|
|
else {
|
|
needValue = hoursResourcesConverter.convertToResources(expCatId, needValue);
|
|
needValue = roundService.roundQuantity(needValue);
|
|
}
|
|
|
|
var teamInfo = {
|
|
Id: currentTeamId,
|
|
Name: currentTeam.Name,
|
|
Need: needValue
|
|
};
|
|
|
|
teamAllocations[currentTeamId] = teamInfo;
|
|
}
|
|
}
|
|
|
|
var keys = Object.keys(teamAllocations);
|
|
if (keys.length > 0) {
|
|
result = $filter('sortObjectsBy')(teamAllocations, 'Name', false);
|
|
}
|
|
|
|
return result;
|
|
},
|
|
getExpCatUnassignedNeed: function (filter, displayMode, header, calendarDate, projectId, scenarioId, expCatIds, withResourceAllocations) {
|
|
if (!filter || !displayMode || !header || !calendarDate || !projectId || !scenarioId) {
|
|
return null;
|
|
}
|
|
|
|
var weekendings = [];
|
|
var result = {
|
|
Need: 0,
|
|
AllocatedToTeamsInView: 0,
|
|
AllocatedToTeamsOutOfView: 0,
|
|
ResourceAllocations: 0,
|
|
Underallocation: 0
|
|
};
|
|
|
|
if (withResourceAllocations) {
|
|
result.ResourceAllocations = 0;
|
|
}
|
|
|
|
if (calendarDate.DataType == Header.DataType.Week) {
|
|
// Get allocations for a single weekending
|
|
weekendings.push(calendarDate.Milliseconds);
|
|
}
|
|
if (calendarDate.DataType == Header.DataType.Month) {
|
|
// Get summary allocations for month
|
|
weekendings = header.getWeeksInMonth(calendarDate.ParentIndex);
|
|
}
|
|
var weeksCount = weekendings.length;
|
|
var useAverageValue = (calendarDate.DataType == Header.DataType.Month) && displayMode.ShowAvgTotals &&
|
|
!displayMode.IsUOMHours && (weeksCount > 0);
|
|
|
|
var needAllocations = activityCalendarService.getNeedAllocations4Scenario(filter, scenarioId);
|
|
var unassignedNeedAllocations = activityCalendarService.getUnassignedNeedAllocations4Scenario(filter, scenarioId);
|
|
var restTeamAllocations = activityCalendarService.getRestTeamAllocations4Scenario(filter, scenarioId);
|
|
|
|
if (!needAllocations)
|
|
return result;
|
|
|
|
var expCats = expCats = (expCatIds && angular.isArray(expCatIds)) ? expCatIds :
|
|
(needAllocations ? Object.keys(needAllocations) : []);
|
|
|
|
// Project need calculation (and resource allocations)
|
|
for (var ecIndex = 0; ecIndex < expCats.length; ecIndex++) {
|
|
var expCatId = expCats[ecIndex];
|
|
var expCatNeed = 0;
|
|
var unassignedNeed = 0;
|
|
|
|
if (expCatId in needAllocations) {
|
|
var expCatTeamAllocations = this.getTeamAllocations4Display(filter, displayMode, header, calendarDate, projectId, expCatId);
|
|
var expCatNeedData = needAllocations[expCatId];
|
|
|
|
if (expCatNeedData && expCatNeedData.Allocations) {
|
|
for (var wIndex = 0; wIndex < weekendings.length; wIndex++) {
|
|
var we = weekendings[wIndex];
|
|
if ((we in expCatNeedData.Allocations) && angular.isNumber(expCatNeedData.Allocations[we])) {
|
|
if (displayMode.IsUOMHours) {
|
|
expCatNeed += expCatNeedData.Allocations[we];
|
|
}
|
|
else {
|
|
expCatNeed += hoursResourcesConverter.convertToResources(expCatId, expCatNeedData.Allocations[we]);
|
|
}
|
|
|
|
if (withResourceAllocations && unassignedNeedAllocations && (expCatId in unassignedNeedAllocations) && unassignedNeedAllocations[expCatId].Allocations) {
|
|
// Calculate resource allocations sum as Project Need minus Unassigned need, because some resource
|
|
// allocations may be out of current view, and not present in data model
|
|
var unassignedData = unassignedNeedAllocations[expCatId].Allocations;
|
|
|
|
if ((we in unassignedData) && angular.isNumber(unassignedData[we])) {
|
|
if (displayMode.IsUOMHours) {
|
|
unassignedNeed += unassignedData[we];
|
|
}
|
|
else {
|
|
unassignedNeed += hoursResourcesConverter.convertToResources(expCatId, unassignedData[we]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (useAverageValue) {
|
|
expCatNeed = expCatNeed / weeksCount;
|
|
|
|
if (withResourceAllocations) {
|
|
unassignedNeed = unassignedNeed / weeksCount;
|
|
}
|
|
}
|
|
}
|
|
|
|
result.Need += roundService.roundQuantity(expCatNeed);
|
|
if (withResourceAllocations) {
|
|
result.ResourceAllocations += roundService.roundQuantity(expCatNeed >= unassignedNeed ? expCatNeed - unassignedNeed : 0);
|
|
}
|
|
|
|
// Team Allocations in current view calculation
|
|
if (expCatTeamAllocations && (expCatTeamAllocations.length > 0)) {
|
|
// Allocated to team in current view
|
|
for (var index = 0; index < expCatTeamAllocations.length; index++) {
|
|
var teamInfo = expCatTeamAllocations[index];
|
|
result.AllocatedToTeamsInView += roundService.roundQuantity(teamInfo.Need);
|
|
}
|
|
}
|
|
|
|
// Rest team Allocations calculation (for teams out of current view)
|
|
if (restTeamAllocations && (expCatId in restTeamAllocations)) {
|
|
var teamAllocationsOutOfView = 0;
|
|
var expCatTeamsAllocated = restTeamAllocations[expCatId];
|
|
|
|
if (expCatTeamsAllocated && expCatTeamsAllocated.Allocations) {
|
|
for (var wIndex = 0; wIndex < weekendings.length; wIndex++) {
|
|
// Allocations to teams out of current view
|
|
var we = weekendings[wIndex];
|
|
|
|
if ((we in expCatTeamsAllocated.Allocations) && angular.isNumber(expCatTeamsAllocated.Allocations[we])) {
|
|
if (displayMode.IsUOMHours) {
|
|
teamAllocationsOutOfView += expCatTeamsAllocated.Allocations[we];
|
|
}
|
|
else {
|
|
teamAllocationsOutOfView += hoursResourcesConverter.convertToResources(expCatId, expCatTeamsAllocated.Allocations[we]);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (useAverageValue) {
|
|
// Get average value
|
|
teamAllocationsOutOfView = teamAllocationsOutOfView / weeksCount;
|
|
}
|
|
result.AllocatedToTeamsOutOfView += roundService.roundQuantity(teamAllocationsOutOfView);
|
|
}
|
|
}
|
|
}
|
|
|
|
var allocatedToTeamsSummary = result.AllocatedToTeamsInView + result.AllocatedToTeamsOutOfView;
|
|
result.Underallocation = roundService.roundQuantity(result.Need - allocatedToTeamsSummary);
|
|
|
|
result.Need = roundService.roundQuantity(result.Need);
|
|
result.AllocatedToTeamsInView = roundService.roundQuantity(result.AllocatedToTeamsInView);
|
|
result.AllocatedToTeamsOutOfView = roundService.roundQuantity(result.AllocatedToTeamsOutOfView);
|
|
|
|
if (withResourceAllocations) {
|
|
result.ResourceAllocations = roundService.roundQuantity(result.ResourceAllocations);
|
|
}
|
|
|
|
return result;
|
|
},
|
|
getExpCatRemainingTeamAllocations: function (filter, displayMode, header, calendarDate, projectId, allocationMode, expCatIds, teamIds) {
|
|
if (!filter || !displayMode || !header || !calendarDate || !projectId || !allocationMode) {
|
|
return null;
|
|
}
|
|
|
|
var projectRawData = activityCalendarService.getProjectById(filter, projectId);
|
|
if (!projectRawData || !projectRawData.ActiveScenario || !projectRawData.ActiveScenario.Id)
|
|
return null;
|
|
|
|
var resourceAllocations = 0;
|
|
var weekendings = [];
|
|
var result = {
|
|
TeamAllocations: 0,
|
|
ResourceAllocations: 0
|
|
};
|
|
|
|
if (calendarDate.DataType == Header.DataType.Week) {
|
|
// Get allocations for a single weekending
|
|
weekendings.push(calendarDate.Milliseconds);
|
|
}
|
|
|
|
if (calendarDate.DataType == Header.DataType.Month) {
|
|
// Get summary allocations for month
|
|
weekendings = header.getWeeksInMonth(calendarDate.ParentIndex);
|
|
}
|
|
var weeksCount = weekendings.length;
|
|
var useAverageValue = (calendarDate.DataType == Header.DataType.Month) && displayMode.ShowAvgTotals &&
|
|
!displayMode.IsUOMHours && (weeksCount > 0);
|
|
|
|
// Filtering of ECs by teams is not performed for No Team block (even if filer for AC is set to Team or View)
|
|
var isFilteredByTeamOrView = this.isFilteredByTeams(filter) && (allocationMode != this.allocationMode.remainingNeedAllocation);
|
|
var expCats = expCatIds && angular.isArray(expCatIds) ? expCatIds : null;
|
|
var teams = teamIds && angular.isArray(teamIds) ? teamIds : null;
|
|
var projectExpCatsData = this.getExpenditures4Project(projectRawData, teams, expCats, header, filter, false, isFilteredByTeamOrView, true);
|
|
|
|
// Calculate resource allocations sum
|
|
if (projectExpCatsData) {
|
|
var expCats = Object.keys(projectExpCatsData);
|
|
|
|
if (!expCats || !angular.isArray(expCats) || !expCats.length) {
|
|
return result;
|
|
}
|
|
|
|
for (var ecIndex = 0; ecIndex < expCats.length; ecIndex++) {
|
|
var expCatId = expCats[ecIndex];
|
|
|
|
if (projectExpCatsData[expCatId] && projectExpCatsData[expCatId].Teams) {
|
|
var teams = projectExpCatsData[expCatId].Teams;
|
|
|
|
for (var teamId in teams) {
|
|
var team = teams[teamId];
|
|
var expCatResourceAllocations = 0;
|
|
var expCatTeamAllocations = 0;
|
|
|
|
for (var wIndex = 0; wIndex < weekendings.length; wIndex++) {
|
|
var we = weekendings[wIndex];
|
|
|
|
if (team.Allocations && (we in team.Allocations) && angular.isNumber(team.Allocations[we])) {
|
|
if (displayMode.IsUOMHours) {
|
|
expCatTeamAllocations += team.Allocations[we];
|
|
}
|
|
else {
|
|
expCatTeamAllocations += hoursResourcesConverter.convertToResources(expCatId, team.Allocations[we]);
|
|
}
|
|
}
|
|
|
|
if (team.Resources) {
|
|
for (var resourceId in team.Resources) {
|
|
var resource = team.Resources[resourceId];
|
|
|
|
if (resource && resource.Allocations) {
|
|
if ((we in resource.Allocations) && angular.isNumber(resource.Allocations[we])) {
|
|
if (displayMode.IsUOMHours) {
|
|
expCatResourceAllocations += resource.Allocations[we];
|
|
}
|
|
else {
|
|
expCatResourceAllocations += hoursResourcesConverter.convertToResources(expCatId, resource.Allocations[we]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (useAverageValue) {
|
|
expCatTeamAllocations = expCatTeamAllocations / weeksCount;
|
|
expCatResourceAllocations = expCatResourceAllocations / weeksCount;
|
|
}
|
|
|
|
result.TeamAllocations += roundService.roundQuantity(expCatTeamAllocations);
|
|
result.ResourceAllocations += roundService.roundQuantity(expCatResourceAllocations);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
result.TeamAllocations = roundService.roundQuantity(result.TeamAllocations);
|
|
result.ResourceAllocations = roundService.roundQuantity(result.ResourceAllocations);
|
|
return result;
|
|
},
|
|
toggleRow: function (uiRow, dataModel, newValue) {
|
|
newValue = newValue || !uiRow.Collapsed;
|
|
|
|
uiRow.Collapsed = dataModel.Collapsed = newValue;
|
|
if (uiRow.Children) {
|
|
var that = this;
|
|
angular.forEach(uiRow.Children, function (child) {
|
|
that.showRow(child, !uiRow.Collapsed);
|
|
});
|
|
}
|
|
|
|
if (uiRow.NonProjectTime) {
|
|
this.showRow(uiRow.NonProjectTime, !uiRow.Collapsed);
|
|
}
|
|
},
|
|
showRow: function (uiRow, isDisplay) {
|
|
uiRow.Show = isDisplay;
|
|
uiRow.Initialized = uiRow.Initialized || uiRow.Show;
|
|
|
|
if (uiRow.Children) {
|
|
var that = this;
|
|
angular.forEach(uiRow.Children, function (child) {
|
|
that.showRow(child, !uiRow.Collapsed && isDisplay);
|
|
});
|
|
}
|
|
},
|
|
toggleGridSource: function (rows, isUOMHours, allocationMode) {
|
|
if (!rows || !rows.length) {
|
|
return;
|
|
}
|
|
|
|
for (var i = 0; i < rows.length; i++) {
|
|
var collectionNames = this.getCollectionNames4AllocationMode(rows[i].AllocationMode || allocationMode);
|
|
if (isUOMHours) {
|
|
rows[i].TotalValue = (collectionNames.hoursTotal in rows[i] ? rows[i][collectionNames.hoursTotal] : rows[i][this.allocationMode.defaultHoursTotalName]);
|
|
rows[i].Cells = angular.copy(collectionNames.hoursCollectionName in rows[i] ? rows[i][collectionNames.hoursCollectionName] : rows[i][this.allocationMode.defaultHoursCollectionName]);
|
|
} else {
|
|
rows[i].TotalValue = (collectionNames.resourceTotal in rows[i] ? rows[i][collectionNames.resourceTotal] : rows[i][this.allocationMode.defaultResourcesTotalName]);
|
|
rows[i].Cells = angular.copy(collectionNames.resourcesCollectionName in rows[i] ? rows[i][collectionNames.resourcesCollectionName] : rows[i][this.allocationMode.defaultResourcesCollectionName]);
|
|
}
|
|
|
|
if (rows[i].Children && rows[i].Children.length) {
|
|
this.toggleGridSource(rows[i].Children, isUOMHours, allocationMode);
|
|
}
|
|
}
|
|
},
|
|
toggleBarsValuesMode: function (rows, isBarMode) {
|
|
if (!rows || !rows.length) {
|
|
return;
|
|
}
|
|
|
|
for (var i = 0; i < rows.length; i++) {
|
|
var row = rows[i];
|
|
if (row && row.Cells && row.CSSStyle && row.CSSClass) {
|
|
for (var j = 0; j < row.Cells.length; j++) {
|
|
var cell = row.Cells[j];
|
|
if (cell === undefined) {
|
|
continue;
|
|
}
|
|
|
|
row.CSSClass[j] = (row.CSSClass[j] || '').replace(/ac-colors-mode-cell/g, '');
|
|
|
|
if (isBarMode && row.Color) {
|
|
row.CSSClass[j] += " ac-colors-mode-cell";
|
|
row.CSSStyle[j] = "border-bottom: 1px solid " + row.Color + " !important;border-right-color: " + row.Color + " !important;background-color: " + row.Color + ";";
|
|
}
|
|
else {
|
|
row.CSSStyle[j] = null;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
toggleSortBy: function (rows, sortBy, sortOrderAsc) {
|
|
if (!rows || !rows.length) {
|
|
return rows;
|
|
}
|
|
// sort project parts the same way as top level projects
|
|
var that = this;
|
|
angular.forEach(rows, function (row) {
|
|
if (row.RowType == 'Project' && row.Children && row.Children.length > 0)
|
|
row.Children = that.toggleSortBy(row.Children, sortBy, sortOrderAsc);
|
|
});
|
|
return $filter('orderBy')(rows, sortBy, !sortOrderAsc);
|
|
},
|
|
toggleParts: function (rows, showParts, masterProjectsLevel, customMasterProjectTemplate) {
|
|
if (!rows || !rows.length) {
|
|
return rows;
|
|
}
|
|
var result = [];
|
|
var masterProjects = {};
|
|
var that = this;
|
|
var projectsRootLevel = masterProjectsLevel && angular.isNumber(masterProjectsLevel) && !isNaN(masterProjectsLevel) ? masterProjectsLevel : 1;
|
|
|
|
// arrange rows according to showParts argument's value
|
|
angular.forEach(rows, function (item) {
|
|
if (showParts) // do not group by master project
|
|
{
|
|
if (item.IsMaster) {
|
|
// if this is a master project then we should expand all child rows
|
|
if (item.Children)
|
|
for (var i = 0; i < item.Children.length; i++) {
|
|
var child = item.Children[i];
|
|
child.Name = child.getFullName();
|
|
result.push(child);
|
|
}
|
|
}
|
|
else {
|
|
result.push(item);
|
|
item.Name = item.getFullName();
|
|
}
|
|
}
|
|
else {
|
|
if (item.IsMaster)
|
|
result.push(item);
|
|
else {
|
|
if (item.ParentProjectId && item.ParentProjectId != item.Id) {
|
|
var parent = masterProjects[item.ParentProjectId];
|
|
if (!parent) {
|
|
parent = that.createViewModel4Project({
|
|
ParentProjectId: item.ParentProjectId,
|
|
ProjectId: item.ParentProjectId,
|
|
Name: item.ParentName,
|
|
ReadOnly: true,
|
|
Color: item.Color,
|
|
ActiveScenario: {},
|
|
IsMaster: true
|
|
});
|
|
parent.Level = projectsRootLevel;
|
|
|
|
if (customMasterProjectTemplate && parent.Templates) {
|
|
if (customMasterProjectTemplate.Main) {
|
|
parent.Templates.Main = customMasterProjectTemplate.Main;
|
|
}
|
|
if (customMasterProjectTemplate.Numbers) {
|
|
parent.Templates.Numbers = customMasterProjectTemplate.Numbers;
|
|
}
|
|
}
|
|
|
|
masterProjects[item.ParentProjectId] = parent;
|
|
result.push(parent);
|
|
}
|
|
parent.addPart(item);
|
|
|
|
if (item.AllocationMode && angular.isNumber(item.AllocationMode)) {
|
|
// Copy allocation mode from part to it's parent project
|
|
parent.AllocationMode = item.AllocationMode;
|
|
}
|
|
}
|
|
else
|
|
result.push(item);
|
|
}
|
|
}
|
|
});
|
|
|
|
// collapse all top level rows
|
|
angular.forEach(result, function (item) {
|
|
that.setLevel(item, projectsRootLevel);
|
|
|
|
if (projectsRootLevel == 1) {
|
|
item.Initialized = true;
|
|
item.Show = true;
|
|
that.toggleRow(item, {}, true);
|
|
}
|
|
});
|
|
return result;
|
|
},
|
|
setLevel: function (row, level) {
|
|
row.Level = level;
|
|
if (row.Children)
|
|
for (var i = 0; i < row.Children.length; i++)
|
|
this.setLevel(row.Children[i], level + 1);
|
|
|
|
},
|
|
toggleStatus: function (btn, projectId, scenario) {
|
|
var $deferrer = $q.defer();
|
|
var dateRange = dataSources.getDateRange();
|
|
$(btn).scenarioStatusToggle({
|
|
scenario: {
|
|
scenarioId: scenario.Id,
|
|
projectId: projectId,
|
|
startDate: scenario.StartDate ? DateTimeConverter.msFormatAsUtcString(scenario.StartDate) : null,
|
|
endDate: scenario.EndDate ? DateTimeConverter.msFormatAsUtcString(scenario.EndDate) : null,
|
|
},
|
|
minStartDate: dateRange.MinDate,
|
|
maxEndDate: dateRange.MaxDate,
|
|
runType: 'OnDemand',
|
|
success: function (args) {
|
|
// emulate document click to hide menus and popovers
|
|
$(document).trigger('click.pm');
|
|
args[0].resolve(true);
|
|
},
|
|
cancel: function (args) {
|
|
args[0].resolve(false);
|
|
},
|
|
error: function (args) {
|
|
args[0].reject();
|
|
},
|
|
});
|
|
$(btn).scenarioStatusToggle('toggleStatus', [$deferrer]);
|
|
|
|
return $deferrer.promise;
|
|
},
|
|
openCreateScenarioWz: function (projectId, scenarioWizContainerId, scenarioWizModalId) {
|
|
scenarioWizContainerId = scenarioWizContainerId || 'reloadForm';
|
|
scenarioWizModalId = scenarioWizModalId || 'createScenario';
|
|
var url = "/Scenarios/LoadScenario?Id=" + projectId;
|
|
$('#' + scenarioWizContainerId).load(url, function () {
|
|
if (typeof initScenario === 'function')
|
|
initScenario();
|
|
$('#' + scenarioWizModalId).modal('show');
|
|
});
|
|
},
|
|
applyAvgMode: function (rows, header) {
|
|
if (!rows || !header) {
|
|
return;
|
|
}
|
|
|
|
if (!header.Months || !header.Weeks) {
|
|
return;
|
|
}
|
|
|
|
for (var i = 0; i < rows.length; i++) {
|
|
for (var j = 0; j < header.Months.length; j++) {
|
|
var month = header.Months[j];
|
|
var monthLength = month.Childs.length;
|
|
if (monthLength <= 0) {
|
|
continue;
|
|
}
|
|
if (!rows[i].Cells[month.SelfIndexInWeeks]) {
|
|
continue;
|
|
}
|
|
rows[i].Cells[month.SelfIndexInWeeks] = roundService.roundQuantity(rows[i].Cells[month.SelfIndexInWeeks] / monthLength);
|
|
}
|
|
rows[i].TotalValue = roundService.roundQuantity(rows[i].TotalValue / rows[i].VisibleCellsCount);
|
|
if (rows[i].Children) {
|
|
this.applyAvgMode(rows[i].Children, header);
|
|
}
|
|
}
|
|
},
|
|
createViewModel4Team: function (teamData, filter) {
|
|
if (!teamData) {
|
|
return null;
|
|
}
|
|
|
|
var projectsViewModel = this.createViewModel4Projects(teamData.Projects, filter);
|
|
|
|
if (projectsViewModel && angular.isArray(projectsViewModel) && projectsViewModel.length) {
|
|
for (var index = 0; index < projectsViewModel.length; index++) {
|
|
projectsViewModel[index].ParentTeamId = teamData.Id;
|
|
}
|
|
}
|
|
|
|
var model = {
|
|
Id: teamData.Id,
|
|
Name: teamData.Name || 'empty',
|
|
VisibleCellsCount: 0, // equals to the number of weeks in the calendar
|
|
TotalHoursValue: 0, // total value in hours
|
|
TotalResourcesValue: 0, // total value in resources
|
|
TotalValue: 0, // total value for view, it depends on selected display mode (hours, resources)
|
|
QuantityHoursValues: [], // week/month values in hours
|
|
QuantityResourcesValues: [], // week/month values in resources
|
|
Cells: [], // week/month values for view, it depends on selected display mode (hours, resources)
|
|
Children: projectsViewModel
|
|
};
|
|
|
|
return model;
|
|
},
|
|
createViewModel4Company: function (companyData, filter) {
|
|
if (!companyData) {
|
|
return null;
|
|
}
|
|
var addECTeams = filter.EntityType == this.filterEntityType.view;
|
|
var projectsViewModel = this.createViewModel4Projects(companyData.Projects, filter, addECTeams);
|
|
var model = {
|
|
Id: companyData.Id,
|
|
Name: companyData.Name || 'empty',
|
|
VisibleCellsCount: 0, // equals to the number of weeks in the calendar
|
|
TotalHoursValue: 0, // total value in hours
|
|
TotalResourcesValue: 0, // total value in resources
|
|
TotalValue: 0, // total value for view, it depends on selected display mode (hours, resources)
|
|
QuantityHoursValues: [], // week/month values in hours
|
|
QuantityResourcesValues: [], // week/month values in resources
|
|
NeedHoursValues: [], // week/month need values in hours
|
|
NeedResourcesValues: [], // week/month need values in resources
|
|
TotalNeedHoursValue: 0, // total value in hours
|
|
TotalNeedResourcesValue: 0, // total value in resources
|
|
Cells: [], // week/month values for view, it depends on selected display mode (hours, resources)
|
|
Children: projectsViewModel
|
|
};
|
|
|
|
return model;
|
|
},
|
|
createViewModel4Client: function (clientData, filter) {
|
|
if (!clientData) {
|
|
return null;
|
|
}
|
|
var addECTeams = filter.EntityType == this.filterEntityType.view;
|
|
var projectsViewModel = this.createViewModel4Projects(clientData.Projects, filter, addECTeams);
|
|
var model = {
|
|
Id: clientData.Id,
|
|
Name: clientData.Name || 'empty',
|
|
VisibleCellsCount: 0, // equals to the number of weeks in the calendar
|
|
TotalHoursValue: 0, // total value in hours
|
|
TotalResourcesValue: 0, // total value in resources
|
|
TotalValue: 0, // total value for view, it depends on selected display mode (hours, resources)
|
|
QuantityHoursValues: [], // week/month values in hours
|
|
QuantityResourcesValues: [], // week/month values in resources
|
|
NeedHoursValues: [], // week/month need values in hours
|
|
NeedResourcesValues: [], // week/month need values in resources
|
|
TotalNeedHoursValue: 0, // total value in hours
|
|
TotalNeedResourcesValue: 0, // total value in resources
|
|
Cells: [], // week/month values for view, it depends on selected display mode (hours, resources)
|
|
Children: projectsViewModel
|
|
};
|
|
|
|
return model;
|
|
},
|
|
createViewModel4CostCenter: function (costCenterData, filter) {
|
|
if (!costCenterData) {
|
|
return null;
|
|
}
|
|
var addECTeams = filter.EntityType == this.filterEntityType.view;
|
|
var projectsViewModel = this.createViewModel4Projects(costCenterData.Projects, filter, addECTeams);
|
|
|
|
if (projectsViewModel && angular.isArray(projectsViewModel)) {
|
|
for (var index = 0; index < projectsViewModel.length; index++) {
|
|
if (projectsViewModel[index]) {
|
|
projectsViewModel[index].ParentCostCenterId = costCenterData.Id;
|
|
}
|
|
}
|
|
}
|
|
|
|
var model = {
|
|
Id: costCenterData.Id,
|
|
Name: costCenterData.Name || 'empty',
|
|
VisibleCellsCount: 0, // equals to the number of weeks in the calendar
|
|
TotalHoursValue: 0, // total value in hours
|
|
TotalResourcesValue: 0, // total value in resources
|
|
TotalValue: 0, // total value for view, it depends on selected display mode (hours, resources)
|
|
QuantityHoursValues: [], // week/month values in hours
|
|
QuantityResourcesValues: [], // week/month values in resources
|
|
NeedHoursValues: [], // week/month need values in hours
|
|
NeedResourcesValues: [], // week/month need values in resources
|
|
TotalNeedHoursValue: 0, // total value in hours
|
|
TotalNeedResourcesValue: 0, // total value in resources
|
|
Cells: [], // week/month values for view, it depends on selected display mode (hours, resources)
|
|
Children: projectsViewModel
|
|
};
|
|
|
|
return model;
|
|
},
|
|
createViewModel4Projects: function (projects, filter, addECTeams, customRowTemplates) {
|
|
if (!projects) {
|
|
return null;
|
|
}
|
|
|
|
var result = [];
|
|
for (var projectId in projects) {
|
|
var projectItem = projects[projectId];
|
|
if (projectItem.Project && projectItem.Expenditures) {
|
|
var projectViewModel = this.createViewModel4Project(projectItem.Project, projectItem.Expenditures, filter, addECTeams, customRowTemplates);
|
|
if (projectViewModel) {
|
|
result.push(projectViewModel);
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
},
|
|
createViewModel4Project: function (project, expenditures, filter, addECTeams, customRowTemplates) {//todo: test all usages
|
|
// project - project data from data layer
|
|
// expenditures - data from data layer for each expenditure of the project. Data is grouped by team if addECTeams is true,
|
|
// otherwise data is sum for all project team's data of each category row
|
|
// filter - represents filtering info entered by user
|
|
// addECTeams - indicates whether to add team rows under EC rows or not
|
|
if (!project || !project.ActiveScenario) {
|
|
return null;
|
|
}
|
|
|
|
var expendituresModel = this.createViewModel4ExpCats(expenditures, addECTeams, customRowTemplates);
|
|
var editUrl;
|
|
if (project.ParentProjectId) {
|
|
editUrl = '/Project/Edit/' + project.ParentProjectId;
|
|
if (project.PartId)
|
|
editUrl += '?partId=' + project.PartId;
|
|
}
|
|
|
|
var templates = angular.extend({}, this.viewRowTemplates, customRowTemplates || {});
|
|
var projectViewModel = {
|
|
Id: project.ProjectId,
|
|
ParentProjectId: project.ParentProjectId,
|
|
ParentName: project.ParentName,
|
|
IsMaster: project.IsMaster,
|
|
PartName: project.Name || 'empty',
|
|
Name: project.Name || 'empty',
|
|
AllocationMode: project.AllocationMode || this.allocationMode.teamAllocation,
|
|
Color: project.Color,
|
|
Type: project.TypeName,
|
|
Date: project.DeadlineMs,
|
|
Priority: project.Priority,
|
|
ReadOnly: project.ReadOnly,
|
|
DisableResourceEdit: project.ReadOnly || this.isFilteredByCompany(filter),
|
|
VisibleCellsCount: 0, // equals to the scenario duration in the selected period, number of scenario cells filled with values
|
|
VisibleCellWeekendings: {}, // represents array of weekendings in ms for single project or part and union of part's weeks for master project
|
|
ActiveScenario: {
|
|
Id: project.ActiveScenario.Id,
|
|
Name: project.ActiveScenario.Name,
|
|
IsBottomUp: project.ActiveScenario.IsBottomUp,
|
|
StartDate: project.ActiveScenario.StartDate,
|
|
EndDate: project.ActiveScenario.EndDate
|
|
},
|
|
InactiveScenarios: project.InactiveScenarios,
|
|
RowType: 'Project',
|
|
Templates: {
|
|
Main: templates.projectRowTemplate,
|
|
Numbers: templates.projectRowNumbersTemplate
|
|
},
|
|
EditUrl: editUrl,
|
|
Collapsed: true,
|
|
TotalHoursValue: 0, // total value in hours
|
|
TotalResourcesValue: 0, // total value in resources
|
|
TotalValue: 0, // total value for view, it depends on selected display mode (hours, resources)
|
|
QuantityHoursValues: [], // week/month values in hours
|
|
QuantityResourcesValues: [], // week/month values in resources
|
|
NeedHoursValues: [], // week/month need values in hours
|
|
NeedResourcesValues: [], // week/month need values in resources
|
|
TotalNeedHoursValue: 0, // total value in hours
|
|
TotalNeedResourcesValue: 0, // total value in resources
|
|
RemainingNeedHoursValues: [], // week/month need values in hours
|
|
RemainingNeedResourcesValues: [], // week/month need values in resources
|
|
RemainingTAHoursValues: [], // week/month remaining team allocation values in hours
|
|
RemainingTAResourcesValues: [], // week/month remaining team allocation values in resources
|
|
TotalRemainingNeedHoursValue: 0, // total value in hours
|
|
TotalRemainingNeedResourcesValue: 0, // total value in resources
|
|
TotalRemainingTAHoursValue: 0, // total value in hours
|
|
TotalRemainingTAResourcesValue: 0, // total value in resources
|
|
UnassignedNeedHoursValues: [], // week/month need values in hours
|
|
UnassignedNeedResourcesValues: [], // week/month need values in resources
|
|
TotalUnassignedNeedHoursValue: 0, // total value in hours
|
|
TotalUnassignedNeedResourcesValue: 0, // total value in resources
|
|
Cells: [], // week/month values for view, it depends on selected display mode (hours, resources)
|
|
CSSStyle: [],
|
|
CSSClass: [],
|
|
Children: expendituresModel, // children expenditure rows for the project
|
|
calculateResourceTotalOnRange: function (weeks) {
|
|
return calculateDistributionService.calculateSumOnRange(this.Cells, weeks);
|
|
},
|
|
addPart: function (part) {
|
|
part.Name = part.getSelfName();
|
|
this.TotalHoursValue = roundService.roundQuantity(this.TotalHoursValue + part.TotalHoursValue);
|
|
this.TotalResourcesValue = roundService.roundQuantity(this.TotalResourcesValue + part.TotalResourcesValue);
|
|
this.TotalValue = roundService.roundQuantity(this.TotalValue + part.TotalValue);
|
|
this.QuantityHoursValues = calculateDistributionService.mergeAllocations(this.QuantityHoursValues, part.QuantityHoursValues);
|
|
this.QuantityResourcesValues = calculateDistributionService.mergeAllocations(this.QuantityResourcesValues, part.QuantityResourcesValues);
|
|
this.NeedHoursValues = calculateDistributionService.mergeAllocations(this.NeedHoursValues, part.NeedHoursValues);
|
|
this.NeedResourcesValues = calculateDistributionService.mergeAllocations(this.NeedResourcesValues, part.NeedResourcesValues);
|
|
this.TotalNeedHoursValue = roundService.roundQuantity(this.TotalNeedHoursValue + part.TotalNeedHoursValue);
|
|
this.TotalNeedResourcesValue = roundService.roundQuantity(this.TotalNeedResourcesValue + part.TotalNeedResourcesValue);
|
|
this.RemainingNeedHoursValues = calculateDistributionService.mergeAllocations(this.RemainingNeedHoursValues, part.RemainingNeedHoursValues);
|
|
this.RemainingNeedResourcesValues = calculateDistributionService.mergeAllocations(this.RemainingNeedResourcesValues, part.RemainingNeedResourcesValues);
|
|
this.RemainingTAHoursValues = calculateDistributionService.mergeAllocations(this.RemainingTAHoursValues, part.RemainingTAHoursValues);
|
|
this.RemainingTAResourcesValues = calculateDistributionService.mergeAllocations(this.RemainingTAResourcesValues, part.RemainingTAResourcesValues);
|
|
this.TotalRemainingNeedHoursValue = roundService.roundQuantity(this.TotalRemainingNeedHoursValue + part.TotalRemainingNeedHoursValue);
|
|
this.TotalRemainingNeedResourcesValue = roundService.roundQuantity(this.TotalRemainingNeedResourcesValue + part.TotalRemainingNeedResourcesValue);
|
|
this.TotalRemainingTAHoursValue = roundService.roundQuantity(this.TotalRemainingTAHoursValue + part.TotalRemainingTAHoursValue);
|
|
this.TotalRemainingTAResourcesValue = roundService.roundQuantity(this.TotalRemainingTAResourcesValue + part.TotalRemainingTAResourcesValue);
|
|
this.UnassignedNeedHoursValues = calculateDistributionService.mergeAllocations(this.UnassignedNeedHoursValues, part.UnassignedNeedHoursValues);
|
|
this.UnassignedNeedResourcesValues = calculateDistributionService.mergeAllocations(this.UnassignedNeedResourcesValues, part.UnassignedNeedResourcesValues);
|
|
this.TotalUnassignedNeedHoursValue = roundService.roundQuantity(this.TotalUnassignedNeedHoursValue + part.TotalUnassignedNeedHoursValue);
|
|
this.TotalUnassignedNeedResourcesValue = roundService.roundQuantity(this.TotalUnassignedNeedResourcesValue + part.TotalUnassignedNeedResourcesValue);
|
|
this.VisibleCellWeekendings = calculateDistributionService.mergeAllocations(this.VisibleCellWeekendings, part.VisibleCellWeekendings);
|
|
this.VisibleCellsCount = Object.keys(this.VisibleCellWeekendings).length;
|
|
|
|
if ((this.Level !== undefined) && angular.isNumber(this.Level) && !isNaN(this.Level)) {
|
|
part.Level = this.Level + 1;
|
|
}
|
|
|
|
if (!this.Children)
|
|
this.Children = [];
|
|
this.Children.push(part);
|
|
},
|
|
getSelfName: function () {
|
|
return this.PartName;
|
|
},
|
|
getFullName: function () {
|
|
var displayName;
|
|
if (this.PartName)
|
|
displayName = this.PartName;
|
|
if (this.ParentName) {
|
|
if (displayName.length > 0)
|
|
displayName += ': ';
|
|
displayName += this.ParentName;
|
|
}
|
|
return displayName;
|
|
}
|
|
};
|
|
|
|
return projectViewModel;
|
|
},
|
|
createViewModel4ExpCats: function (expenditures, addECTeams, customRowTemplates) {//todo: test all usages
|
|
if (!expenditures || Object.keys(expenditures).length <= 0) {
|
|
return null;
|
|
}
|
|
|
|
var rows = [];
|
|
|
|
for (var categoryId in expenditures) {
|
|
var category = expenditures[categoryId];
|
|
var itemViewModel = this.createViewModel4ExpCat(category, addECTeams, customRowTemplates);
|
|
if (itemViewModel) {
|
|
rows.push(itemViewModel);
|
|
}
|
|
};
|
|
|
|
return rows;
|
|
},
|
|
createViewModel4ExpCat: function (exp, addECTeams, customRowTemplates) {//todo: test all usages
|
|
if (!exp) {
|
|
return null;
|
|
}
|
|
|
|
var resources = addECTeams ? null : this.createViewModel4Resources(exp.Resources);
|
|
var availableResources = addECTeams ? null : this.createViewModel4AvailableResources(exp.AvailableResources);
|
|
var ecTeams = null;
|
|
|
|
var templates = angular.extend({}, this.viewRowTemplates, customRowTemplates || {});
|
|
if (addECTeams) {
|
|
ecTeams = this.createViewModel4ECTeams(exp.Teams);
|
|
for (var teamId in ecTeams) {
|
|
ecTeams[teamId].Templates.Main = templates.teamRowTemplate;
|
|
ecTeams[teamId].Templates.Numbers = templates.teamRowNumbersTemplate;
|
|
}
|
|
}
|
|
var model = {
|
|
Id: exp.Id,
|
|
Name: exp.Name || 'empty',
|
|
AllocationMode: exp.AllocationMode || this.allocationMode.teamAllocation,
|
|
RowType: 'ExpCat',
|
|
Templates: {
|
|
Main: templates.expCatRowTemplate,
|
|
Numbers: templates.expCatRowNumbersTemplate
|
|
},
|
|
AllowResourceAssignment: exp.AllowResourceAssignment,
|
|
Collapsed: exp.Collapsed || true,
|
|
VisibleCellsCount: 0, // equals to the scenario duration in the selected period, number of scenario cells filled with values
|
|
TotalHoursValue: 0, // total value in hours
|
|
TotalResourcesValue: 0, // total value in resources
|
|
TotalValue: 0, // total value for view, it depends on selected display mode (hours, resources)
|
|
QuantityHoursValues: [], // week/month values in hours
|
|
QuantityResourcesValues: [], // week/month values in resources
|
|
NeedHoursValues: [], // week/month need values in hours
|
|
NeedResourcesValues: [], // week/month need values in resources
|
|
TotalNeedHoursValue: 0, // total value in hours
|
|
TotalNeedResourcesValue: 0, // total value in resources
|
|
RemainingNeedHoursValues: [], // week/month need values in hours
|
|
RemainingNeedResourcesValues: [], // week/month need values in resources
|
|
TotalRemainingNeedHoursValue: 0, // total value in hours
|
|
TotalRemainingNeedResourcesValue: 0, // total value in resources
|
|
RemainingTAHoursValues: [], // week/month need values in hours
|
|
RemainingTAResourcesValues: [], // week/month need values in resources
|
|
TotalRemainingTAHoursValue: 0, // total value in hours
|
|
TotalRemainingTAResourcesValue: 0, // total value in resources
|
|
UnassignedNeedHoursValues: [], // week/month need values in hours
|
|
UnassignedNeedResourcesValues: [], // week/month need values in resources
|
|
TotalUnassignedNeedHoursValue: 0, // total value in hours
|
|
TotalUnassignedNeedResourcesValue: 0, // total value in resources
|
|
RemainingCapacityValues: [], // team allocation - resource allocation in hours
|
|
Cells: [], // week/month values for view, it depends on selected display mode (hours, resources)
|
|
CSSClass: [],
|
|
Children: addECTeams ? ecTeams : resources, // children resource rows for the expenditure
|
|
AvailableResources: addECTeams ? null : availableResources
|
|
};
|
|
return model;
|
|
},
|
|
createViewModel4ECTeams: function (teams) {
|
|
if (!teams || Object.keys(teams).length <= 0) {
|
|
return null;
|
|
}
|
|
|
|
var rows = [];
|
|
for (var teamId in teams) {
|
|
var team = teams[teamId];
|
|
var itemViewModel = this.createViewModel4ECTeam(team);
|
|
if (itemViewModel) {
|
|
rows.push(itemViewModel);
|
|
}
|
|
};
|
|
|
|
return rows;
|
|
},
|
|
createViewModel4ECTeam: function (ecTeamData) {
|
|
if (!ecTeamData) {
|
|
return null;
|
|
}
|
|
|
|
var resources = this.createViewModel4Resources(ecTeamData.Resources);
|
|
var availableResources = this.createViewModel4AvailableResources(ecTeamData.AvailableResources);
|
|
var basicModel = {
|
|
Id: ecTeamData.Id,
|
|
Name: ecTeamData.Name || 'empty',
|
|
AllocationMode: ecTeamData.AllocationMode || this.allocationMode.teamAllocation,
|
|
ExpenditureCategoryId: ecTeamData.ExpenditureCategoryId,
|
|
Templates: {
|
|
Main: this.viewRowTemplates.teamUnallocatedRowTemplate,
|
|
Numbers: this.viewRowTemplates.teamUnallocatedRowNumbersTemplate
|
|
},
|
|
Initialized: ecTeamData.Initialized,
|
|
Show: ecTeamData.Show,
|
|
Level: ecTeamData.Level,
|
|
RowType: 'Team',
|
|
Collapsed: true,
|
|
VisibleCellsCount: 0, // equals to the scenario duration in the selected period, number of scenario cells filled with values
|
|
TotalHoursValue: 0, // total value in hours
|
|
TotalResourcesValue: 0, // total value in resources
|
|
TotalValue: 0, // total value for view, it depends on selected display mode (hours, resources)
|
|
QuantityHoursValues: [], // week/month values in hours
|
|
QuantityResourcesValues: [], // week/month values in resources
|
|
NeedHoursValues: [], // week/month need values in hours
|
|
NeedResourcesValues: [], // week/month need values in resources
|
|
TotalNeedHoursValue: 0, // total value in hours
|
|
TotalNeedResourcesValue: 0, // total value in resources
|
|
RemainingNeedHoursValues: [], // week/month need values in hours
|
|
RemainingNeedResourcesValues: [], // week/month need values in resources
|
|
TotalRemainingNeedHoursValue: 0, // total value in hours
|
|
TotalRemainingNeedResourcesValue: 0, // total value in resources
|
|
RemainingTAHoursValues: [], // week/month need values in hours
|
|
RemainingTAResourcesValues: [], // week/month need values in resources
|
|
TotalRemainingTAHoursValue: 0, // total value in hours
|
|
TotalRemainingTAResourcesValue: 0, // total value in resources
|
|
UnassignedNeedHoursValues: [], // week/month need values in hours
|
|
UnassignedNeedResourcesValues: [], // week/month need values in resources
|
|
TotalUnassignedNeedHoursValue: 0, // total value in hours
|
|
TotalUnassignedNeedResourcesValue: 0, // total value in resources
|
|
RemainingCapacityValues: [], // team allocation - resource allocation in hours
|
|
Cells: [], // week/month values for view, it depends on selected display mode (hours, resources)
|
|
CSSClass: [],
|
|
Children: resources, // children resource rows for the expenditure
|
|
AvailableResources: availableResources,
|
|
calculateResourceTotalOnRange: function (weeks) {
|
|
return calculateDistributionService.calculateSumOnRange(this.Cells, weeks);
|
|
},
|
|
};
|
|
var basicEditableModel = this.createViewModelEditableBasic();
|
|
var model = angular.extend({}, basicModel, basicEditableModel);
|
|
return model;
|
|
},
|
|
createViewModel4Resources: function (resources) {
|
|
if (!resources || !Object.keys(resources).length) {
|
|
return null;
|
|
}
|
|
|
|
var rows = [];
|
|
|
|
for (var resourceId in resources) {
|
|
var resource = resources[resourceId];
|
|
var itemViewModel = this.createViewModel4Resource(resource);
|
|
if (itemViewModel) {
|
|
rows.push(itemViewModel);
|
|
}
|
|
};
|
|
|
|
return rows;
|
|
},
|
|
createViewModel4ResourceBasic: function (resource) {
|
|
if (!resource) {
|
|
return;
|
|
}
|
|
|
|
var model = {
|
|
Id: resource.Id,
|
|
Name: resource.Name || 'empty',
|
|
Templates: {
|
|
Main: this.viewRowTemplates.resourceRowTemplate,
|
|
Numbers: this.viewRowTemplates.resourceRowNumbersTemplate
|
|
},
|
|
Collapsed: true,
|
|
VisibleCellsCount: 0, // equals to the scenario duration in the selected period, number of scenario cells filled with values
|
|
VisibleCellWeekendings: {}, // represents an array of weekendings in ms for the row
|
|
TotalHoursValue: 0, // total value in hours
|
|
TotalResourcesValue: 0, // total value in resources
|
|
TotalValue: 0, // total value for view, it depends on selected display mode (hours, resources)
|
|
QuantityHoursValues: [], // week/month values in hours
|
|
QuantityResourcesValues: [], // week/month values in resources
|
|
Cells: [], // week/month values for view, it depends on selected display mode (hours, resources)
|
|
CSSClass: [],
|
|
calculateResourceTotalOnRange: function (weeks) {
|
|
return calculateDistributionService.calculateSumOnRange(this.Cells, weeks);
|
|
},
|
|
};
|
|
|
|
return model;
|
|
},
|
|
createViewModelEditableBasic: function () {
|
|
var model = {
|
|
IsEditable: false,
|
|
CanBeRemoved: true,
|
|
EditableForecastWeeks: [], // array with editable weeks for resource forecast values
|
|
|
|
sortWeeks: function (weeks) {
|
|
var result = {
|
|
Editable: [],
|
|
ReadOnly: [],
|
|
};
|
|
|
|
if (!weeks || !weeks.length) {
|
|
return result;
|
|
}
|
|
|
|
if (!this.EditableForecastWeeks || !this.EditableForecastWeeks.length) {
|
|
return result;
|
|
}
|
|
|
|
for (var i = 0; i < weeks.length; i++) {
|
|
if (this.EditableForecastWeeks[weeks[i]]) {
|
|
result.Editable.push(weeks[i]);
|
|
} else {
|
|
result.ReadOnly.push(weeks[i]);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
},
|
|
};
|
|
|
|
return model;
|
|
},
|
|
createViewModel4Resource: function (resource) {
|
|
if (!resource) {
|
|
return null;
|
|
}
|
|
|
|
var basicModel = this.createViewModel4ResourceBasic(resource);
|
|
var basicEditableModel = this.createViewModelEditableBasic();
|
|
var model = angular.extend({}, basicModel, basicEditableModel, {
|
|
Teams: [],
|
|
RowType: 'Resource'
|
|
});
|
|
|
|
return model;
|
|
},
|
|
createViewModel4AvailableResources: function (resources) {
|
|
if (!resources || !Object.keys(resources).length) {
|
|
return null;
|
|
}
|
|
|
|
var items = {};
|
|
|
|
for (var resourceId in resources) {
|
|
var resource = resources[resourceId];
|
|
var model = this.createViewModel4AvailableResource(resource);
|
|
if (model) {
|
|
items[resourceId] = model;
|
|
}
|
|
};
|
|
|
|
return items;
|
|
},
|
|
createViewModel4AvailableResource: function (resource) {
|
|
if (!resource) {
|
|
return null;
|
|
}
|
|
|
|
var model = {
|
|
id: resource.Id,
|
|
name: resource.Name,
|
|
teams: resource.Teams,
|
|
startDate: resource.StartDate,
|
|
endDate: resource.EndDate,
|
|
minAvailability: resource.MinAvailability,
|
|
maxAvailability: resource.MaxAvailability,
|
|
avgAvailability: resource.AvgAvailability,
|
|
};
|
|
|
|
return model;
|
|
},
|
|
createViewModel4TotalRow: function (name) {
|
|
var model = {
|
|
Name: name,
|
|
Collapsed: false,
|
|
VisibleCellsCount: 0,
|
|
TotalHoursValue: 0, // total value in hours
|
|
TotalResourcesValue: 0, // total value in resources
|
|
TotalValue: 0, // total value for view, it depends on selected display mode (hours, resources)
|
|
QuantityHoursValues: [], // week/month values in hours
|
|
QuantityResourcesValues: [], // week/month values in resources
|
|
Cells: [], // week/month values for view, it depends on selected display mode (hours, resources)
|
|
CSSStyle: [],
|
|
CSSClass: [],
|
|
Children: [],
|
|
};
|
|
|
|
return model;
|
|
},
|
|
createViewModel4NptTotalRow: function (name, data) {
|
|
var model = {
|
|
Name: name,
|
|
RowType: "NPT",
|
|
Collapsed: true,
|
|
VisibleCellsCount: 0,
|
|
TotalHoursValue: 0, // total value in hours
|
|
TotalResourcesValue: 0, // total value in resources
|
|
TotalValue: 0, // total value for view, it depends on selected display mode (hours, resources)
|
|
QuantityHoursValues: [], // week/month values in hours
|
|
QuantityResourcesValues: [], // week/month values in resources
|
|
Cells: [], // week/month values for view, it depends on selected display mode (hours, resources)
|
|
CSSStyle: [],
|
|
CSSClass: []
|
|
};
|
|
|
|
var nptCategoryModels = this.createViewModel4NptCategories(data);
|
|
if (nptCategoryModels && (nptCategoryModels.length > 0))
|
|
model.Children = nptCategoryModels;
|
|
|
|
return model;
|
|
},
|
|
createViewModel4NptCategories: function (nptCategoriesData) {
|
|
if (!nptCategoriesData) {
|
|
return null;
|
|
}
|
|
|
|
var result = [];
|
|
var nptCategoriesDataSorted = $filter('sortObjectsBy')(nptCategoriesData, 'Name', false);
|
|
|
|
for (var index = 0; index < nptCategoriesDataSorted.length; index++) {
|
|
var nptCategoryItem = nptCategoriesDataSorted[index];
|
|
if (nptCategoryItem.NonProjectTime) {
|
|
var nptCategoryViewModel = this.createViewModel4NptCategory(nptCategoryItem);
|
|
if (nptCategoryViewModel) {
|
|
result.push(nptCategoryViewModel);
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
},
|
|
createViewModel4NptCategory: function (nptCategoryData) {
|
|
if (!nptCategoryData || !nptCategoryData.NonProjectTime || (nptCategoryData.NonProjectTime.length < 1))
|
|
return null;
|
|
|
|
var categoryViewModel = {
|
|
Id: nptCategoryData.Id,
|
|
Name: nptCategoryData.Name || 'empty',
|
|
VisibleCellsCount: 0,
|
|
RowType: 'NonProjectTimeCategory',
|
|
Collapsed: nptCategoryData.Collapsed || true,
|
|
TotalHoursValue: 0, // total value in hours
|
|
TotalResourcesValue: 0, // total value in resources
|
|
TotalValue: 0, // total value for view, it depends on selected display mode (hours, resources)
|
|
QuantityHoursValues: [], // week/month values in hours
|
|
QuantityResourcesValues: [], // week/month values in resources
|
|
Cells: [], // week/month values for view, it depends on selected display mode (hours, resources)
|
|
CSSStyle: [],
|
|
CSSClass: []
|
|
};
|
|
|
|
categoryViewModel.Children = this.createViewModel4NptItems(nptCategoryData.NonProjectTime);
|
|
return categoryViewModel;
|
|
},
|
|
createViewModel4NptItems: function (nptItemsData) {
|
|
if (!nptItemsData) {
|
|
return null;
|
|
}
|
|
|
|
var result = [];
|
|
var nptItemsDataSorted = $filter('sortObjectsBy')(nptItemsData, 'Name', false);
|
|
|
|
for (var index = 0; index < nptItemsDataSorted.length; index++) {
|
|
var nptItem = nptItemsDataSorted[index];
|
|
|
|
if (nptItem.Resources) {
|
|
var nptItemViewModel = this.createViewModel4NptItem(nptItem);
|
|
if (nptItemViewModel) {
|
|
result.push(nptItemViewModel);
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
},
|
|
createViewModel4NptItem: function (nptItemData) {
|
|
if (!nptItemData || !nptItemData.Resources || (nptItemData.Resources.length < 1)) {
|
|
return null;
|
|
}
|
|
|
|
var nptViewModel = {
|
|
Id: nptItemData.Id,
|
|
Name: nptItemData.isTeamWide ? (nptItemData.Name + ' (team-wide)') : nptItemData.Name,
|
|
IsTeamWide: nptItemData.isTeamWide,
|
|
IsReadOnly: false,
|
|
VisibleCellsCount: 0,
|
|
RowType: 'NonProjectTime',
|
|
Collapsed: nptItemData.Collapsed || true,
|
|
TotalHoursValue: 0, // total value in hours
|
|
TotalResourcesValue: 0, // total value in resources
|
|
TotalValue: 0, // total value for view, it depends on selected display mode (hours, resources)
|
|
QuantityHoursValues: [], // week/month values in hours
|
|
QuantityResourcesValues: [], // week/month values in resources
|
|
EditableNptWeeks: [], // week/month values are editable
|
|
Resources: [], // Resources with ECs in team-wide NPT. For resource-level NPT is empty
|
|
Cells: [], // week/month values for view, it depends on selected display mode (hours, resources)
|
|
CSSStyle: [],
|
|
CSSClass: [],
|
|
|
|
sortWeeks: function (weeks) {
|
|
var result = {
|
|
Editable: [],
|
|
ReadOnly: [],
|
|
};
|
|
|
|
if (!weeks || !weeks.length) {
|
|
return result;
|
|
}
|
|
|
|
if (!this.EditableNptWeeks || !this.EditableNptWeeks.length) {
|
|
return result;
|
|
}
|
|
|
|
for (var i = 0; i < weeks.length; i++) {
|
|
if (this.EditableNptWeeks[weeks[i]]) {
|
|
result.Editable.push(weeks[i]);
|
|
} else {
|
|
result.ReadOnly.push(weeks[i]);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
},
|
|
calculateResourceTotalOnRange: function (weeks) {
|
|
return calculateDistributionService.calculateSumOnRange(this.Cells, weeks);
|
|
},
|
|
|
|
};
|
|
|
|
if (!nptItemData.isTeamWide) {
|
|
// Resource-level NPT drops down to it's resources list
|
|
nptViewModel.Children = this.createViewModel4NptResources(nptItemData.Resources);
|
|
}
|
|
else {
|
|
// We store resources in team-wide NPT
|
|
for (var resourceId in nptItemData.Resources) {
|
|
var resourceInfo = {
|
|
Id: resourceId,
|
|
ExpenditureCategoryId: nptItemData.Resources[resourceId].ExpenditureCategoryId
|
|
}
|
|
nptViewModel.Resources.push(resourceInfo);
|
|
}
|
|
}
|
|
|
|
return nptViewModel;
|
|
},
|
|
createViewModel4NptResources: function (nptResourcesData) {
|
|
if (!nptResourcesData) {
|
|
return null;
|
|
}
|
|
|
|
var result = [];
|
|
var nptResourcesDataSorted = $filter('sortObjectsBy')(nptResourcesData, 'SortingKey', false);
|
|
|
|
for (var index = 0; index < nptResourcesDataSorted.length; index++) {
|
|
var resourceData = nptResourcesDataSorted[index];
|
|
var resourceViewModel = this.createViewModel4NptResource(resourceData);
|
|
|
|
if (resourceViewModel) {
|
|
result.push(resourceViewModel);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
},
|
|
createViewModel4NptResource: function (resourceData) {
|
|
if (!resourceData) {
|
|
return null;
|
|
}
|
|
|
|
var resourceViewModel = {
|
|
Id: resourceData.Id,
|
|
Name: resourceData.Name || 'empty',
|
|
ExpenditureCategoryId: resourceData.ExpenditureCategoryId,
|
|
VisibleCellsCount: 0,
|
|
IsReadOnly: false,
|
|
RowType: 'NonProjectTimeResource',
|
|
Collapsed: resourceData.Collapsed || true,
|
|
TotalHoursValue: 0, // total value in hours
|
|
TotalResourcesValue: 0, // total value in resources
|
|
TotalValue: 0, // total value for view, it depends on selected display mode (hours, resources)
|
|
QuantityHoursValues: [], // week/month values in hours
|
|
QuantityResourcesValues: [], // week/month values in resources
|
|
EditableNptWeeks: [], // // week/month values are editable
|
|
Cells: [], // week/month values for view, it depends on selected display mode (hours, resources)
|
|
CSSStyle: [],
|
|
CSSClass: [],
|
|
|
|
sortWeeks: function (weeks) {
|
|
var result = {
|
|
Editable: [],
|
|
ReadOnly: [],
|
|
};
|
|
|
|
if (!weeks || !weeks.length) {
|
|
return result;
|
|
}
|
|
|
|
if (!this.EditableNptWeeks || !this.EditableNptWeeks.length) {
|
|
return result;
|
|
}
|
|
|
|
for (var i = 0; i < weeks.length; i++) {
|
|
if (this.EditableNptWeeks[weeks[i]]) {
|
|
result.Editable.push(weeks[i]);
|
|
} else {
|
|
result.ReadOnly.push(weeks[i]);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
},
|
|
calculateResourceTotalOnRange: function (weeks) {
|
|
return calculateDistributionService.calculateSumOnRange(this.Cells, weeks);
|
|
},
|
|
};
|
|
|
|
return resourceViewModel;
|
|
},
|
|
createViewModel4AvailableTeams: function (availableTeams) {
|
|
var availableTeamsViewModel = [];
|
|
if (!availableTeams || !Object.keys(availableTeams).length) {
|
|
return availableTeamsViewModel;
|
|
}
|
|
|
|
for (var teamId in availableTeams) {
|
|
var availableTeamViewModel = this.createViewModel4AvailableTeam(availableTeams[teamId]);
|
|
if (availableTeamViewModel) {
|
|
availableTeamsViewModel.push(availableTeamViewModel);
|
|
}
|
|
}
|
|
|
|
return availableTeamsViewModel;
|
|
},
|
|
createViewModel4AvailableTeam: function (availableTeam) {
|
|
if (!availableTeam) {
|
|
return null;
|
|
}
|
|
|
|
var availableTeamViewModel = {
|
|
Id: availableTeam.Id,
|
|
Name: availableTeam.Name,
|
|
};
|
|
|
|
return availableTeamViewModel;
|
|
},
|
|
fillProjectRowWithData: function (rollupRow, projectRow, expenditures, header, filter, addECTeams) {
|
|
if (!projectRow || !projectRow.ActiveScenario) {
|
|
return;
|
|
}
|
|
|
|
if (!header || !header.Months || !header.Weeks || !filter) {
|
|
return;
|
|
}
|
|
|
|
var weeksCount = 0;
|
|
if (!projectRow.Children || !expenditures || !Object.keys(expenditures).length) {
|
|
this.fillExpenditureCategoryRowWithData(header, rollupRow, projectRow, null, null, projectRow.ActiveScenario.StartDate, projectRow.ActiveScenario.EndDate);
|
|
} else {
|
|
for (var ecIndex = 0; ecIndex < projectRow.Children.length; ecIndex++) {
|
|
var ecRow = projectRow.Children[ecIndex];
|
|
var category = expenditures[ecRow.Id];
|
|
if (category) {
|
|
this.fillExpenditureCategoryRowWithData(header, rollupRow, projectRow, ecRow, category, projectRow.ActiveScenario.StartDate, projectRow.ActiveScenario.EndDate);
|
|
if (addECTeams) {
|
|
if (ecRow.Children && category.Teams) {
|
|
for (var teamKey in ecRow.Children) {
|
|
var teamRow = ecRow.Children[teamKey];
|
|
var team = category.Teams[teamRow.Id];
|
|
|
|
this.fillECTeamRowWithData(header, teamRow, ecRow, team, projectRow.Id, projectRow.ActiveScenario.StartDate, projectRow.ActiveScenario.EndDate, projectRow.ReadOnly);
|
|
if (teamRow.Children && team.Resources) {
|
|
for (var resourceIndex = 0; resourceIndex < teamRow.Children.length; resourceIndex++) {
|
|
var resourceRow = teamRow.Children[resourceIndex];
|
|
var resource = team.Resources[resourceRow.Id];
|
|
this.fillResourceRowWithData(header, teamRow, teamRow.ExpenditureCategoryId, resourceRow.Id, resourceRow, resource, null, projectRow.ActiveScenario.StartDate, projectRow.ActiveScenario.EndDate, false, projectRow.DisableResourceEdit);
|
|
};
|
|
}
|
|
};
|
|
}
|
|
} else {
|
|
if (ecRow.Children && category.Resources) {
|
|
for (var resourceIndex = 0; resourceIndex < ecRow.Children.length; resourceIndex++) {
|
|
var resourceRow = ecRow.Children[resourceIndex];
|
|
var resource = category.Resources[resourceRow.Id];
|
|
|
|
this.fillResourceRowWithData(header, ecRow, ecRow.Id, resourceRow.Id, resourceRow, resource, null, projectRow.ActiveScenario.StartDate, projectRow.ActiveScenario.EndDate, false, projectRow.DisableResourceEdit);
|
|
};
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (rollupRow) {
|
|
rollupRow.VisibleCellsCount = header.TotalWeeks;
|
|
}
|
|
},
|
|
// rollupRow - team (for group by team mode) or company (for group by company mode) row
|
|
fillExpenditureCategoryRowWithData: function (header, rollupRow, projectRow, expenditureCategoryRow, categoryRowData, startDate, endDate) {
|
|
if (!header) {
|
|
return;
|
|
}
|
|
|
|
if (!rollupRow && !projectRow && !expenditureCategoryRow) {
|
|
return;
|
|
}
|
|
|
|
var projectWeeksCount = 0;
|
|
var projectWeeks = {};
|
|
for (var monthIndex = 0; monthIndex < header.Months.length; monthIndex++) {
|
|
var month = header.Months[monthIndex];
|
|
for (var mcIndex = 0; mcIndex < month.Childs.length; mcIndex++) {
|
|
var weekIndex = month.Childs[mcIndex];
|
|
var week = header.Weeks[weekIndex];
|
|
if ((!!startDate && week.Milliseconds < startDate) ||
|
|
(!!endDate && week.Milliseconds > endDate)) {
|
|
continue;
|
|
}
|
|
|
|
projectWeeksCount += 1;
|
|
projectWeeks[week.Milliseconds] = 0;
|
|
var hoursValue = (categoryRowData && categoryRowData.Allocations) ? categoryRowData.Allocations[week.Milliseconds] || 0 : 0;
|
|
var resourcesValue = hoursValue === 0 ? 0 : hoursResourcesConverter.convertToResources(categoryRowData.Id, hoursValue);
|
|
var needHoursValue = (categoryRowData && categoryRowData.NeedAllocations) ? categoryRowData.NeedAllocations[week.Milliseconds] || 0 : 0;
|
|
var needResourcesValue = needHoursValue === 0 ? 0 : hoursResourcesConverter.convertToResources(categoryRowData.Id, needHoursValue);
|
|
var remainingNeedHoursValue = (categoryRowData && categoryRowData.RemainingNeed) ? Math.max(categoryRowData.RemainingNeed[week.Milliseconds] || 0, 0) : 0;
|
|
var remainingNeedResourcesValue = remainingNeedHoursValue === 0 ? 0 : hoursResourcesConverter.convertToResources(categoryRowData.Id, remainingNeedHoursValue);
|
|
var remainingTAHoursValue = (categoryRowData && categoryRowData.RemainingTeamAllocations) ? Math.max(categoryRowData.RemainingTeamAllocations[week.Milliseconds] || 0, 0) : 0;
|
|
var remainingTAResourcesValue = remainingTAHoursValue === 0 ? 0 : hoursResourcesConverter.convertToResources(categoryRowData.Id, remainingTAHoursValue);
|
|
var unassignedNeedHoursValue = (categoryRowData && categoryRowData.UnassignedNeed) ? categoryRowData.UnassignedNeed[week.Milliseconds] || 0 : 0;
|
|
var unassignedNeedResourcesValue = unassignedNeedHoursValue === 0 ? 0 : hoursResourcesConverter.convertToResources(categoryRowData.Id, unassignedNeedHoursValue);
|
|
|
|
if (expenditureCategoryRow) {
|
|
// week values
|
|
expenditureCategoryRow.QuantityHoursValues[weekIndex] = hoursValue;
|
|
expenditureCategoryRow.RemainingCapacityValues[weekIndex] = hoursValue;
|
|
expenditureCategoryRow.QuantityResourcesValues[weekIndex] = resourcesValue;
|
|
expenditureCategoryRow.NeedHoursValues[weekIndex] = needHoursValue;
|
|
expenditureCategoryRow.NeedResourcesValues[weekIndex] = needResourcesValue;
|
|
expenditureCategoryRow.RemainingNeedHoursValues[weekIndex] = remainingNeedHoursValue;
|
|
expenditureCategoryRow.RemainingNeedResourcesValues[weekIndex] = remainingNeedResourcesValue;
|
|
expenditureCategoryRow.RemainingTAHoursValues[weekIndex] = remainingTAHoursValue;
|
|
expenditureCategoryRow.RemainingTAResourcesValues[weekIndex] = remainingTAResourcesValue;
|
|
expenditureCategoryRow.UnassignedNeedHoursValues[weekIndex] = unassignedNeedHoursValue;
|
|
expenditureCategoryRow.UnassignedNeedResourcesValues[weekIndex] = unassignedNeedResourcesValue;
|
|
|
|
// month values
|
|
expenditureCategoryRow.QuantityHoursValues[month.SelfIndexInWeeks] = (expenditureCategoryRow.QuantityHoursValues[month.SelfIndexInWeeks] || 0) + hoursValue;
|
|
expenditureCategoryRow.RemainingCapacityValues[month.SelfIndexInWeeks] = (expenditureCategoryRow.RemainingCapacityValues[month.SelfIndexInWeeks] || 0) + hoursValue;
|
|
expenditureCategoryRow.QuantityResourcesValues[month.SelfIndexInWeeks] = (expenditureCategoryRow.QuantityResourcesValues[month.SelfIndexInWeeks] || 0) + resourcesValue;
|
|
expenditureCategoryRow.NeedHoursValues[month.SelfIndexInWeeks] = (expenditureCategoryRow.NeedHoursValues[month.SelfIndexInWeeks] || 0) + needHoursValue;
|
|
expenditureCategoryRow.NeedResourcesValues[month.SelfIndexInWeeks] = (expenditureCategoryRow.NeedResourcesValues[month.SelfIndexInWeeks] || 0) + needResourcesValue;
|
|
expenditureCategoryRow.RemainingNeedHoursValues[month.SelfIndexInWeeks] = (expenditureCategoryRow.RemainingNeedHoursValues[month.SelfIndexInWeeks] || 0) + remainingNeedHoursValue;
|
|
expenditureCategoryRow.RemainingNeedResourcesValues[month.SelfIndexInWeeks] = (expenditureCategoryRow.RemainingNeedResourcesValues[month.SelfIndexInWeeks] || 0) + remainingNeedResourcesValue;
|
|
expenditureCategoryRow.RemainingTAHoursValues[month.SelfIndexInWeeks] = (expenditureCategoryRow.RemainingTAHoursValues[month.SelfIndexInWeeks] || 0) + remainingTAHoursValue;
|
|
expenditureCategoryRow.RemainingTAResourcesValues[month.SelfIndexInWeeks] = (expenditureCategoryRow.RemainingTAResourcesValues[month.SelfIndexInWeeks] || 0) + remainingTAResourcesValue;
|
|
expenditureCategoryRow.UnassignedNeedHoursValues[month.SelfIndexInWeeks] = (expenditureCategoryRow.UnassignedNeedHoursValues[month.SelfIndexInWeeks] || 0) + unassignedNeedHoursValue;
|
|
expenditureCategoryRow.UnassignedNeedResourcesValues[month.SelfIndexInWeeks] = (expenditureCategoryRow.UnassignedNeedResourcesValues[month.SelfIndexInWeeks] || 0) + unassignedNeedResourcesValue;
|
|
|
|
// grand total values
|
|
expenditureCategoryRow.TotalHoursValue += hoursValue;
|
|
expenditureCategoryRow.TotalResourcesValue += resourcesValue;
|
|
expenditureCategoryRow.TotalNeedHoursValue += needHoursValue;
|
|
expenditureCategoryRow.TotalNeedResourcesValue += needResourcesValue;
|
|
expenditureCategoryRow.TotalRemainingNeedHoursValue += remainingNeedHoursValue;
|
|
expenditureCategoryRow.TotalRemainingNeedResourcesValue += remainingNeedResourcesValue;
|
|
expenditureCategoryRow.TotalRemainingTAHoursValue += remainingTAHoursValue;
|
|
expenditureCategoryRow.TotalRemainingTAResourcesValue += remainingTAResourcesValue;
|
|
expenditureCategoryRow.TotalUnassignedNeedHoursValue += unassignedNeedHoursValue;
|
|
expenditureCategoryRow.TotalUnassignedNeedResourcesValue += unassignedNeedResourcesValue;
|
|
}
|
|
|
|
if (projectRow) {
|
|
// week values
|
|
projectRow.QuantityHoursValues[weekIndex] = (projectRow.QuantityHoursValues[weekIndex] || 0) + hoursValue;
|
|
projectRow.QuantityResourcesValues[weekIndex] = (projectRow.QuantityResourcesValues[weekIndex] || 0) + resourcesValue;
|
|
projectRow.NeedHoursValues[weekIndex] = (projectRow.NeedHoursValues[weekIndex] || 0) + needHoursValue;
|
|
projectRow.NeedResourcesValues[weekIndex] = (projectRow.NeedResourcesValues[weekIndex] || 0) + needResourcesValue;
|
|
projectRow.RemainingNeedHoursValues[weekIndex] = (projectRow.RemainingNeedHoursValues[weekIndex] || 0) + remainingNeedHoursValue;
|
|
projectRow.RemainingNeedResourcesValues[weekIndex] = (projectRow.RemainingNeedResourcesValues[weekIndex] || 0) + remainingNeedResourcesValue;
|
|
projectRow.RemainingTAHoursValues[weekIndex] = (projectRow.RemainingTAHoursValues[weekIndex] || 0) + remainingTAHoursValue;
|
|
projectRow.RemainingTAResourcesValues[weekIndex] = (projectRow.RemainingTAResourcesValues[weekIndex] || 0) + remainingTAResourcesValue;
|
|
projectRow.UnassignedNeedHoursValues[weekIndex] = (projectRow.UnassignedNeedHoursValues[weekIndex] || 0) + unassignedNeedHoursValue;
|
|
projectRow.UnassignedNeedResourcesValues[weekIndex] = (projectRow.UnassignedNeedResourcesValues[weekIndex] || 0) + unassignedNeedResourcesValue;
|
|
|
|
// month values
|
|
projectRow.QuantityHoursValues[month.SelfIndexInWeeks] = (projectRow.QuantityHoursValues[month.SelfIndexInWeeks] || 0) + hoursValue;
|
|
projectRow.QuantityResourcesValues[month.SelfIndexInWeeks] = (projectRow.QuantityResourcesValues[month.SelfIndexInWeeks] || 0) + resourcesValue;
|
|
projectRow.NeedHoursValues[month.SelfIndexInWeeks] = (projectRow.NeedHoursValues[month.SelfIndexInWeeks] || 0) + needHoursValue;
|
|
projectRow.NeedResourcesValues[month.SelfIndexInWeeks] = (projectRow.NeedResourcesValues[month.SelfIndexInWeeks] || 0) + needResourcesValue;
|
|
projectRow.RemainingNeedHoursValues[month.SelfIndexInWeeks] = (projectRow.RemainingNeedHoursValues[month.SelfIndexInWeeks] || 0) + remainingNeedHoursValue;
|
|
projectRow.RemainingNeedResourcesValues[month.SelfIndexInWeeks] = (projectRow.RemainingNeedResourcesValues[month.SelfIndexInWeeks] || 0) + remainingNeedResourcesValue;
|
|
projectRow.RemainingTAHoursValues[month.SelfIndexInWeeks] = (projectRow.RemainingTAHoursValues[month.SelfIndexInWeeks] || 0) + remainingTAHoursValue;
|
|
projectRow.RemainingTAResourcesValues[month.SelfIndexInWeeks] = (projectRow.RemainingTAResourcesValues[month.SelfIndexInWeeks] || 0) + remainingTAResourcesValue;
|
|
projectRow.UnassignedNeedHoursValues[month.SelfIndexInWeeks] = (projectRow.UnassignedNeedHoursValues[month.SelfIndexInWeeks] || 0) + unassignedNeedHoursValue;
|
|
projectRow.UnassignedNeedResourcesValues[month.SelfIndexInWeeks] = (projectRow.UnassignedNeedResourcesValues[month.SelfIndexInWeeks] || 0) + unassignedNeedResourcesValue;
|
|
|
|
// grand total values
|
|
projectRow.TotalHoursValue += hoursValue;
|
|
projectRow.TotalResourcesValue += resourcesValue;
|
|
projectRow.TotalNeedHoursValue += needHoursValue;
|
|
projectRow.TotalNeedResourcesValue += needResourcesValue;
|
|
projectRow.TotalRemainingNeedHoursValue += remainingNeedHoursValue;
|
|
projectRow.TotalRemainingNeedResourcesValue += remainingNeedResourcesValue;
|
|
projectRow.TotalRemainingTAHoursValue += remainingTAHoursValue;
|
|
projectRow.TotalRemainingTAResourcesValue += remainingTAResourcesValue;
|
|
projectRow.TotalUnassignedNeedHoursValue += unassignedNeedHoursValue;
|
|
projectRow.TotalUnassignedNeedResourcesValue += unassignedNeedResourcesValue;
|
|
}
|
|
|
|
if (rollupRow) {
|
|
rollupRow.QuantityHoursValues[weekIndex] = (rollupRow.QuantityHoursValues[weekIndex] || 0) + hoursValue;
|
|
rollupRow.QuantityResourcesValues[weekIndex] = (rollupRow.QuantityResourcesValues[weekIndex] || 0) + resourcesValue;
|
|
rollupRow.QuantityHoursValues[month.SelfIndexInWeeks] = (rollupRow.QuantityHoursValues[month.SelfIndexInWeeks] || 0) + hoursValue;
|
|
rollupRow.QuantityResourcesValues[month.SelfIndexInWeeks] = (rollupRow.QuantityResourcesValues[month.SelfIndexInWeeks] || 0) + resourcesValue;
|
|
rollupRow.TotalHoursValue += hoursValue;
|
|
rollupRow.TotalResourcesValue += resourcesValue;
|
|
|
|
// for project and company we have these collections, but for team we do not
|
|
if (rollupRow.NeedHoursValues) {
|
|
rollupRow.NeedHoursValues[weekIndex] = (rollupRow.NeedHoursValues[weekIndex] || 0) + needHoursValue;
|
|
rollupRow.NeedHoursValues[month.SelfIndexInWeeks] = (rollupRow.NeedHoursValues[month.SelfIndexInWeeks] || 0) + needHoursValue;
|
|
rollupRow.TotalNeedHoursValue += needHoursValue;
|
|
}
|
|
if (rollupRow.NeedResourcesValues) {
|
|
rollupRow.NeedResourcesValues[weekIndex] = (rollupRow.NeedResourcesValues[weekIndex] || 0) + needResourcesValue;
|
|
rollupRow.NeedResourcesValues[month.SelfIndexInWeeks] = (rollupRow.NeedResourcesValues[month.SelfIndexInWeeks] || 0) + needResourcesValue;
|
|
rollupRow.TotalNeedResourcesValue += needResourcesValue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (expenditureCategoryRow) {
|
|
expenditureCategoryRow.VisibleCellsCount = projectWeeksCount;
|
|
}
|
|
if (projectRow) {
|
|
projectRow.VisibleCellsCount = projectWeeksCount;
|
|
projectRow.VisibleCellWeekendings = projectWeeks;
|
|
}
|
|
},
|
|
// To-Do: ENV-2254. review ability to join fillECTeamRowWithData and fillExpenditureCategoryRowWithData as they fill almost the same data
|
|
// 1) improve rollup rows handling
|
|
// 2) move resource filling out of method
|
|
// 3) keep collections filling and rounding from fillECTeamRowWithData method
|
|
fillECTeamRowWithData: function (header, row, expCatRow, rowData, projectId, startDate, endDate, projectReadOnly) {
|
|
if (!header) {
|
|
return;
|
|
}
|
|
if (!row)
|
|
throw 'row argument of fillECTeamRowWithData function is required';
|
|
if (!row.ExpenditureCategoryId)
|
|
throw 'ExpenditureCategoryId is not set for the EC\'s team row';
|
|
if (!row.Id)
|
|
throw 'TeamRow.Id is not set for the EC\'s team row';
|
|
|
|
// If we must set weeks in the past read-only, we get the first week, from which the values start to be editable
|
|
var rowIsEditable = false;
|
|
|
|
var projectWeeksCount = 0;
|
|
var ecId = row.ExpenditureCategoryId;
|
|
var teamId = row.Id;
|
|
for (var monthIndex = 0; monthIndex < header.Months.length; monthIndex++) {
|
|
var month = header.Months[monthIndex];
|
|
var monthEditable = true;
|
|
for (var mcIndex = 0; mcIndex < month.Childs.length; mcIndex++) {
|
|
var weekIndex = month.Childs[mcIndex];
|
|
var week = header.Weeks[weekIndex];
|
|
var weekEditable = true;
|
|
|
|
if ((!!startDate && week.Milliseconds < startDate) ||
|
|
(!!endDate && week.Milliseconds > endDate)) {
|
|
// if scenario does not cover each week of the month we shouldn't give ability to edit this month
|
|
monthEditable = false;
|
|
continue;
|
|
}
|
|
|
|
projectWeeksCount += 1;
|
|
|
|
var hoursValue = (rowData && rowData.Allocations) ? rowData.Allocations[week.Milliseconds] || 0 : 0;
|
|
var resourcesValue = hoursValue === 0 ? 0 : hoursResourcesConverter.convertToResources(ecId, hoursValue);
|
|
var needHoursValue = (rowData && rowData.NeedAllocations) ? rowData.NeedAllocations[week.Milliseconds] || 0 : 0;
|
|
var needResourcesValue = needHoursValue === 0 ? 0 : hoursResourcesConverter.convertToResources(ecId, needHoursValue);
|
|
var remainingNeedHoursValue = (rowData && rowData.RemainingNeed) ? Math.max(rowData.RemainingNeed[week.Milliseconds] || 0, 0) : 0;
|
|
var remainingNeedResourcesValue = remainingNeedHoursValue === 0 ? 0 : hoursResourcesConverter.convertToResources(ecId, remainingNeedHoursValue);
|
|
var remainingTAHoursValue = (rowData && rowData.RemainingTeamAllocations) ? Math.max(rowData.RemainingTeamAllocations[week.Milliseconds] || 0, 0) : 0;
|
|
var remainingTAResourcesValue = remainingTAHoursValue === 0 ? 0 : hoursResourcesConverter.convertToResources(ecId, remainingTAHoursValue);
|
|
|
|
if (row) {
|
|
// week values
|
|
row.QuantityHoursValues[weekIndex] = roundService.roundQuantity(hoursValue);
|
|
row.QuantityResourcesValues[weekIndex] = roundService.roundQuantity(resourcesValue);
|
|
row.RemainingCapacityValues[weekIndex] = roundService.roundQuantity(hoursValue); // To-Do: remove me, take remaining/all should deal with DAL method(s)
|
|
row.NeedHoursValues[weekIndex] = roundService.roundQuantity(needHoursValue);
|
|
row.NeedResourcesValues[weekIndex] = roundService.roundQuantity(needResourcesValue);
|
|
row.RemainingNeedHoursValues[weekIndex] = roundService.roundQuantity(remainingNeedHoursValue);
|
|
row.RemainingNeedResourcesValues[weekIndex] = roundService.roundQuantity(remainingNeedResourcesValue);
|
|
row.RemainingTAHoursValues[weekIndex] = roundService.roundQuantity(remainingTAHoursValue);
|
|
row.RemainingTAResourcesValues[weekIndex] = roundService.roundQuantity(remainingTAResourcesValue);
|
|
|
|
// month values
|
|
row.QuantityHoursValues[month.SelfIndexInWeeks] = (row.QuantityHoursValues[month.SelfIndexInWeeks] || 0) + hoursValue;
|
|
row.RemainingCapacityValues[month.SelfIndexInWeeks] = (row.RemainingCapacityValues[month.SelfIndexInWeeks] || 0) + hoursValue;
|
|
row.NeedHoursValues[month.SelfIndexInWeeks] = (row.NeedHoursValues[month.SelfIndexInWeeks] || 0) + needHoursValue;
|
|
row.RemainingNeedHoursValues[month.SelfIndexInWeeks] = (row.RemainingNeedHoursValues[month.SelfIndexInWeeks] || 0) + remainingNeedHoursValue;
|
|
row.RemainingTAHoursValues[month.SelfIndexInWeeks] = (row.RemainingTAHoursValues[month.SelfIndexInWeeks] || 0) + remainingTAHoursValue;
|
|
|
|
// grand total values
|
|
row.TotalHoursValue += hoursValue;
|
|
row.TotalNeedHoursValue += needHoursValue;
|
|
row.TotalRemainingNeedHoursValue += remainingNeedHoursValue;
|
|
row.TotalRemainingTAHoursValue += remainingTAHoursValue;
|
|
}
|
|
|
|
// if user has only read permissions on project we should set all cells to non-editable state
|
|
if (projectReadOnly === true) {
|
|
weekEditable = false;
|
|
}
|
|
// if team is not editable for any week of the month we shouldn't give ability to edit this month
|
|
monthEditable = monthEditable && weekEditable;
|
|
// if team is editable for any week of the scenario we should give ability to edit grand total cell for this row
|
|
rowIsEditable = rowIsEditable || weekEditable;
|
|
row.EditableForecastWeeks[weekIndex] = weekEditable;
|
|
|
|
// To-Do: ENV-2249. implement set week styles method for teams or reuse any of existing methods
|
|
//this.updateResourceWeekStyles(teamId, row, week.Milliseconds, weekIndex);
|
|
}
|
|
// round calculated month values to avoid rounding issues
|
|
if (!isNaN(row.QuantityHoursValues[month.SelfIndexInWeeks])) {
|
|
row.QuantityHoursValues[month.SelfIndexInWeeks] = roundService.roundQuantity(row.QuantityHoursValues[month.SelfIndexInWeeks] || 0);
|
|
row.QuantityResourcesValues[month.SelfIndexInWeeks] = roundService.roundQuantity(hoursResourcesConverter.convertToResources(ecId, row.QuantityHoursValues[month.SelfIndexInWeeks] || 0));
|
|
}
|
|
if (!isNaN(row.RemainingCapacityValues[month.SelfIndexInWeeks]))
|
|
row.RemainingCapacityValues[month.SelfIndexInWeeks] = roundService.roundQuantity(row.RemainingCapacityValues[month.SelfIndexInWeeks] || 0);
|
|
if (!isNaN(row.NeedHoursValues[month.SelfIndexInWeeks])) {
|
|
row.NeedHoursValues[month.SelfIndexInWeeks] = roundService.roundQuantity(row.NeedHoursValues[month.SelfIndexInWeeks] || 0);
|
|
row.NeedResourcesValues[month.SelfIndexInWeeks] = roundService.roundQuantity(hoursResourcesConverter.convertToResources(ecId, row.NeedHoursValues[month.SelfIndexInWeeks] || 0));
|
|
}
|
|
if (!isNaN(row.RemainingNeedHoursValues[month.SelfIndexInWeeks])) {
|
|
row.RemainingNeedHoursValues[month.SelfIndexInWeeks] = roundService.roundQuantity(row.RemainingNeedHoursValues[month.SelfIndexInWeeks] || 0);
|
|
row.RemainingNeedResourcesValues[month.SelfIndexInWeeks] = roundService.roundQuantity(hoursResourcesConverter.convertToResources(ecId, row.RemainingNeedHoursValues[month.SelfIndexInWeeks] || 0));
|
|
}
|
|
if (!isNaN(row.RemainingTAHoursValues[month.SelfIndexInWeeks])) {
|
|
row.RemainingTAHoursValues[month.SelfIndexInWeeks] = roundService.roundQuantity(row.RemainingTAHoursValues[month.SelfIndexInWeeks] || 0);
|
|
row.RemainingTAResourcesValues[month.SelfIndexInWeeks] = roundService.roundQuantity(hoursResourcesConverter.convertToResources(ecId, row.RemainingTAHoursValues[month.SelfIndexInWeeks] || 0));
|
|
}
|
|
row.EditableForecastWeeks[month.SelfIndexInWeeks] = monthEditable;
|
|
this.updateMonthStyles(row, month.SelfIndexInWeeks, month.Childs);
|
|
}
|
|
|
|
// round calculated grand total values to avoid rounding issues
|
|
row.TotalHoursValue = roundService.roundQuantity(row.TotalHoursValue);
|
|
row.TotalResourcesValue = roundService.roundQuantity(hoursResourcesConverter.convertToResources(ecId, row.TotalHoursValue));
|
|
row.TotalNeedHoursValue = roundService.roundQuantity(row.TotalNeedHoursValue);
|
|
row.TotalNeedResourcesValue = roundService.roundQuantity(hoursResourcesConverter.convertToResources(ecId, row.TotalNeedHoursValue));
|
|
row.TotalRemainingNeedHoursValue = roundService.roundQuantity(row.TotalRemainingNeedHoursValue);
|
|
row.TotalRemainingNeedResourcesValue = roundService.roundQuantity(hoursResourcesConverter.convertToResources(ecId, row.TotalRemainingNeedHoursValue));
|
|
row.TotalRemainingTAHoursValue = roundService.roundQuantity(row.TotalRemainingTAHoursValue);
|
|
row.TotalRemainingTAResourcesValue = roundService.roundQuantity(hoursResourcesConverter.convertToResources(ecId, row.TotalRemainingTAHoursValue));
|
|
|
|
row.VisibleCellsCount = projectWeeksCount;
|
|
row.CanBeRemoved = !projectReadOnly;
|
|
row.IsEditable = rowIsEditable;
|
|
},
|
|
fillResourceRowWithData: function (header, parentRow, expCatId, resourceId, resourceRow, resourceRowData, rollupToRows, startDate, endDate, readonlyToCurrentDate, disableResourceEdit) {
|
|
if (!header || !resourceId || !resourceRow) {
|
|
return;
|
|
}
|
|
|
|
// If we must set weeks in the past read-only, we get the first week, from which the values start to be editable
|
|
var firstEditableWeek = readonlyToCurrentDate ? header.getNextWeekByDate(this.getUtcNow()) : null;
|
|
var rowIsEditable = false;
|
|
var performRollup = rollupToRows && rollupToRows.length;
|
|
|
|
resourceRow.Teams = resourceRowData ? resourceRowData.Teams || [] : [];
|
|
|
|
for (var monthIndex = 0; monthIndex < header.Months.length; monthIndex++) {
|
|
var month = header.Months[monthIndex];
|
|
var monthEditable = true;
|
|
|
|
for (var mcIndex = 0; mcIndex < month.Childs.length; mcIndex++) {
|
|
var weekIndex = month.Childs[mcIndex];
|
|
var week = header.Weeks[weekIndex];
|
|
var weekEditable = true;
|
|
|
|
if ((!!startDate && week.Milliseconds < startDate) ||
|
|
(!!endDate && week.Milliseconds > endDate)) {
|
|
// if scenario does not cover each week of the month we shouldn't give ability to edit this month
|
|
monthEditable = false;
|
|
continue;
|
|
}
|
|
|
|
var resHoursValue = (resourceRowData && resourceRowData.Allocations) ? resourceRowData.Allocations[week.Milliseconds] || 0 : 0;
|
|
var resResourcesValue = resHoursValue === 0 ? 0 : hoursResourcesConverter.convertToResources(expCatId, resHoursValue);
|
|
|
|
resourceRow.VisibleCellsCount += 1;
|
|
resourceRow.VisibleCellWeekendings[week.Milliseconds] = 0;
|
|
resourceRow.QuantityHoursValues[weekIndex] = roundService.roundQuantity(resHoursValue);
|
|
resourceRow.QuantityResourcesValues[weekIndex] = roundService.roundQuantity(resResourcesValue);
|
|
resourceRow.QuantityHoursValues[month.SelfIndexInWeeks] = (resourceRow.QuantityHoursValues[month.SelfIndexInWeeks] || 0) + resHoursValue;
|
|
resourceRow.QuantityResourcesValues[month.SelfIndexInWeeks] = (resourceRow.QuantityResourcesValues[month.SelfIndexInWeeks] || 0) + resResourcesValue;
|
|
resourceRow.TotalHoursValue += resHoursValue;
|
|
resourceRow.TotalResourcesValue += resResourcesValue;
|
|
|
|
// we need to roll up current resource values upto some total rows
|
|
if (performRollup) {
|
|
for (var rollupRowIndex = 0; rollupRowIndex < rollupToRows.length; rollupRowIndex++) {
|
|
var rollupRow = rollupToRows[rollupRowIndex];
|
|
if (rollupRow) {
|
|
rollupRow.QuantityHoursValues[weekIndex] = (rollupRow.QuantityHoursValues[weekIndex] || 0) + resHoursValue;
|
|
rollupRow.QuantityResourcesValues[weekIndex] = (rollupRow.QuantityResourcesValues[weekIndex] || 0) + resResourcesValue;
|
|
rollupRow.QuantityHoursValues[month.SelfIndexInWeeks] = (rollupRow.QuantityHoursValues[month.SelfIndexInWeeks] || 0) + resHoursValue;
|
|
rollupRow.QuantityResourcesValues[month.SelfIndexInWeeks] = (rollupRow.QuantityResourcesValues[month.SelfIndexInWeeks] || 0) + resResourcesValue;
|
|
rollupRow.TotalHoursValue += resHoursValue;
|
|
rollupRow.TotalResourcesValue += resResourcesValue;
|
|
}
|
|
}
|
|
}
|
|
// to-do: remove me, validation should use activityCalendarService to get data for validation
|
|
if (parentRow && parentRow.RemainingCapacityValues) {
|
|
parentRow.RemainingCapacityValues[weekIndex] = roundService.roundQuantity((parentRow.RemainingCapacityValues[weekIndex] || 0) - resHoursValue);
|
|
parentRow.RemainingCapacityValues[month.SelfIndexInWeeks] = roundService.roundQuantity((parentRow.RemainingCapacityValues[month.SelfIndexInWeeks] || 0) - resHoursValue);
|
|
}
|
|
|
|
// Check if current resource is a member of any team in the list
|
|
var availableTeam = this.resolveResourceAvailableTeam(week.Milliseconds, resourceRow.Teams);
|
|
if (!availableTeam || availableTeam.ReadOnly) {
|
|
// Resource doesn't have available team at this week or team is read only. Cell should be read-only
|
|
weekEditable = false;
|
|
}
|
|
|
|
if (weekEditable && readonlyToCurrentDate && firstEditableWeek) {
|
|
// Set cells read-only for cells in the past
|
|
weekEditable = (header.compareWeeks(week, firstEditableWeek) > 0);
|
|
}
|
|
|
|
// if user has only read permissions on project we should set all cells to non-editable state
|
|
if (disableResourceEdit === true) {
|
|
weekEditable = false;
|
|
}
|
|
|
|
// if resource is not available for any week of the month we shouldn't give ability to edit this month
|
|
monthEditable = monthEditable && weekEditable;
|
|
// if resource is available for any week of the scenario we should give ability to edit grand total cell for this row
|
|
rowIsEditable = rowIsEditable || weekEditable;
|
|
resourceRow.EditableForecastWeeks[weekIndex] = weekEditable;
|
|
|
|
this.updateResourceWeekStyles(resourceId, resourceRow, week.Milliseconds, weekIndex);
|
|
}
|
|
|
|
resourceRow.EditableForecastWeeks[month.SelfIndexInWeeks] = monthEditable;
|
|
if (resourceRow.QuantityHoursValues[month.SelfIndexInWeeks]) {
|
|
resourceRow.QuantityHoursValues[month.SelfIndexInWeeks] = roundService.roundQuantity(resourceRow.QuantityHoursValues[month.SelfIndexInWeeks]);
|
|
}
|
|
if (resourceRow.QuantityResourcesValues[month.SelfIndexInWeeks]) {
|
|
resourceRow.QuantityResourcesValues[month.SelfIndexInWeeks] = roundService.roundQuantity(resourceRow.QuantityResourcesValues[month.SelfIndexInWeeks]);
|
|
}
|
|
|
|
this.updateMonthStyles(resourceRow, month.SelfIndexInWeeks, month.Childs);
|
|
}
|
|
|
|
resourceRow.CanBeRemoved = !disableResourceEdit && checkAllTeamsEditable(resourceRow.Teams);
|
|
resourceRow.IsEditable = rowIsEditable;
|
|
resourceRow.TotalHoursValue = roundService.roundQuantity(resourceRow.TotalHoursValue);
|
|
resourceRow.TotalResourcesValue = roundService.roundQuantity(resourceRow.TotalResourcesValue);
|
|
},
|
|
fillResourceActualsRowWithData: function (header, actualsRow, actualsData, startDate, endDate, disableResourceEdit) {
|
|
if (!header || !actualsRow || !actualsRow.ExpenditureCategory || !actualsRow.ExpenditureCategory.Id) {
|
|
return;
|
|
}
|
|
|
|
// If we must set weeks in the past read-only, we get the first week, from which the values start to be editable
|
|
var firstReadOnlyWeek = header.getNextWeekByDate(this.getUtcNow());
|
|
var rowIsEditable = false;
|
|
|
|
actualsRow.Teams = actualsData ? actualsData.Teams || [] : [];
|
|
|
|
for (var monthIndex = 0; monthIndex < header.Months.length; monthIndex++) {
|
|
var month = header.Months[monthIndex];
|
|
var monthEditable = true;
|
|
|
|
for (var mcIndex = 0; mcIndex < month.Childs.length; mcIndex++) {
|
|
var weekIndex = month.Childs[mcIndex];
|
|
var week = header.Weeks[weekIndex];
|
|
var weekEditable = true;
|
|
|
|
if ((!!startDate && week.Milliseconds < startDate) ||
|
|
(!!endDate && week.Milliseconds > endDate)) {
|
|
// if scenario does not cover each week of the month we shouldn't give ability to edit this month
|
|
monthEditable = false;
|
|
continue;
|
|
}
|
|
|
|
var resHoursValue = (actualsData && actualsData.Actuals) ? actualsData.Actuals[week.Milliseconds] || 0 : 0;
|
|
var resResourcesValue = resHoursValue === 0 ? 0 :
|
|
hoursResourcesConverter.convertToResources(actualsRow.ExpenditureCategory.Id, resHoursValue);
|
|
|
|
actualsRow.VisibleCellsCount += 1;
|
|
actualsRow.QuantityHoursValues[weekIndex] = roundService.roundQuantity(resHoursValue);
|
|
actualsRow.QuantityResourcesValues[weekIndex] = roundService.roundQuantity(resResourcesValue);
|
|
actualsRow.QuantityHoursValues[month.SelfIndexInWeeks] = (actualsRow.QuantityHoursValues[month.SelfIndexInWeeks] || 0) + resHoursValue;
|
|
actualsRow.QuantityResourcesValues[month.SelfIndexInWeeks] = (actualsRow.QuantityResourcesValues[month.SelfIndexInWeeks] || 0) + resResourcesValue;
|
|
actualsRow.TotalHoursValue += resHoursValue;
|
|
actualsRow.TotalResourcesValue += resResourcesValue;
|
|
|
|
// check if current week is editable
|
|
var availableTeam = this.resolveResourceAvailableTeam(week.Milliseconds, actualsRow.Teams);
|
|
if (!availableTeam || availableTeam.ReadOnly) {
|
|
// Resource doesn't has available team at this week. Cell should be read-only
|
|
weekEditable = false;
|
|
}
|
|
|
|
if (weekEditable && firstReadOnlyWeek) {
|
|
// Set cells read-only for cells in the past
|
|
weekEditable = (header.compareWeeks(week, firstReadOnlyWeek) <= 0);
|
|
}
|
|
|
|
if (disableResourceEdit === true) {
|
|
weekEditable = false;
|
|
}
|
|
|
|
// if resource is not available for any week of the month we shouldn't give ability to edit this month
|
|
monthEditable = monthEditable && weekEditable;
|
|
// if resource is available for any week of the scenario we should give ability to edit grand total cell for this row
|
|
rowIsEditable = rowIsEditable || weekEditable;
|
|
actualsRow.EditableForecastWeeks[weekIndex] = weekEditable;
|
|
}
|
|
|
|
actualsRow.EditableForecastWeeks[month.SelfIndexInWeeks] = monthEditable;
|
|
if (actualsRow.QuantityHoursValues[month.SelfIndexInWeeks]) {
|
|
actualsRow.QuantityHoursValues[month.SelfIndexInWeeks] = roundService.roundQuantity(actualsRow.QuantityHoursValues[month.SelfIndexInWeeks]);
|
|
}
|
|
if (actualsRow.QuantityResourcesValues[month.SelfIndexInWeeks]) {
|
|
actualsRow.QuantityResourcesValues[month.SelfIndexInWeeks] = roundService.roundQuantity(actualsRow.QuantityResourcesValues[month.SelfIndexInWeeks]);
|
|
}
|
|
}
|
|
|
|
actualsRow.IsEditable = rowIsEditable;
|
|
actualsRow.TotalHoursValue = roundService.roundQuantity(actualsRow.TotalHoursValue);
|
|
actualsRow.TotalResourcesValue = roundService.roundQuantity(actualsRow.TotalResourcesValue);
|
|
},
|
|
fillTotalRowData: function (projectRows, totalRow, header, allocationMode/*to-do: remove allocationMode argument*/) {
|
|
if (!totalRow || !projectRows) {
|
|
return;
|
|
}
|
|
|
|
if (!header || !header.Months || !header.Weeks) {
|
|
return;
|
|
}
|
|
|
|
totalRow.TotalHoursValue = 0;
|
|
totalRow.TotalResourcesValue = 0;
|
|
totalRow.VisibleCellsCount = header.TotalWeeks;
|
|
for (var monthIndex = 0; monthIndex < header.Months.length; monthIndex++) {
|
|
var month = header.Months[monthIndex];
|
|
|
|
totalRow.QuantityHoursValues[month.SelfIndexInWeeks] = 0;
|
|
totalRow.QuantityResourcesValues[month.SelfIndexInWeeks] = 0;
|
|
for (var mcIndex = 0; mcIndex < month.Childs.length; mcIndex++) {
|
|
var weekIndex = month.Childs[mcIndex];
|
|
var week = header.Weeks[weekIndex];
|
|
|
|
totalRow.QuantityHoursValues[weekIndex] = 0;
|
|
totalRow.QuantityResourcesValues[weekIndex] = 0;
|
|
|
|
|
|
for (var childIndex = 0; childIndex < projectRows.length; childIndex++) {
|
|
var projectRow = projectRows[childIndex];
|
|
var collectionNames = this.getCollectionNames4AllocationMode(projectRow.AllocationMode || allocationMode);
|
|
var hoursValue = (collectionNames.hoursCollectionName in projectRow) ? (projectRow[collectionNames.hoursCollectionName][weekIndex] || 0) : (projectRow[this.allocationMode.defaultHoursCollectionName][weekIndex] || 0);
|
|
var resourcesValue = (collectionNames.resourcesCollectionName in projectRow) ? (projectRow[collectionNames.resourcesCollectionName][weekIndex] || 0) : (projectRow[this.allocationMode.defaultResourcesCollectionName][weekIndex] || 0);
|
|
|
|
totalRow.QuantityHoursValues[weekIndex] = (totalRow.QuantityHoursValues[weekIndex] || 0) + hoursValue;
|
|
totalRow.QuantityResourcesValues[weekIndex] = (totalRow.QuantityResourcesValues[weekIndex] || 0) + resourcesValue;
|
|
totalRow.QuantityHoursValues[month.SelfIndexInWeeks] = (totalRow.QuantityHoursValues[month.SelfIndexInWeeks] || 0) + hoursValue;
|
|
totalRow.QuantityResourcesValues[month.SelfIndexInWeeks] = (totalRow.QuantityResourcesValues[month.SelfIndexInWeeks] || 0) + resourcesValue;
|
|
totalRow.TotalHoursValue += hoursValue;
|
|
totalRow.TotalResourcesValue += resourcesValue;
|
|
}
|
|
|
|
totalRow.QuantityHoursValues[weekIndex] = roundService.roundQuantity(totalRow.QuantityHoursValues[weekIndex]);
|
|
totalRow.QuantityResourcesValues[weekIndex] = roundService.roundQuantity(totalRow.QuantityResourcesValues[weekIndex]);
|
|
}
|
|
totalRow.QuantityHoursValues[month.SelfIndexInWeeks] = roundService.roundQuantity(totalRow.QuantityHoursValues[month.SelfIndexInWeeks]);
|
|
totalRow.QuantityResourcesValues[month.SelfIndexInWeeks] = roundService.roundQuantity(totalRow.QuantityResourcesValues[month.SelfIndexInWeeks]);
|
|
}
|
|
totalRow.TotalHoursValue = roundService.roundQuantity(totalRow.TotalHoursValue);
|
|
totalRow.TotalResourcesValue = roundService.roundQuantity(totalRow.TotalResourcesValue);
|
|
},
|
|
fillRemainingCapacityRowData: function (totalRow, capacityRow, header, isCapacityTypeActuals) {
|
|
if (!totalRow || !capacityRow) {
|
|
return;
|
|
}
|
|
|
|
if (!header || !header.Months || !header.Weeks) {
|
|
return;
|
|
}
|
|
|
|
capacityRow.TotalHoursValue = 0;
|
|
capacityRow.TotalResourcesValue = 0;
|
|
capacityRow.VisibleCellsCount = header.TotalWeeks;
|
|
for (var monthIndex = 0; monthIndex < header.Months.length; monthIndex++) {
|
|
var month = header.Months[monthIndex];
|
|
|
|
capacityRow.QuantityHoursValues[month.SelfIndexInWeeks] = 0;
|
|
capacityRow.QuantityResourcesValues[month.SelfIndexInWeeks] = 0;
|
|
for (var mcIndex = 0; mcIndex < month.Childs.length; mcIndex++) {
|
|
var weekIndex = month.Childs[mcIndex];
|
|
var week = header.Weeks[weekIndex];
|
|
|
|
capacityRow.QuantityHoursValues[weekIndex] = 0;
|
|
capacityRow.QuantityResourcesValues[weekIndex] = 0;
|
|
|
|
var hoursTotalValue = totalRow.QuantityHoursValues[weekIndex] || 0;
|
|
var resourcesTotalValue = totalRow.QuantityResourcesValues[weekIndex] || 0;
|
|
var hoursCapacityValue = 0;
|
|
var resourcesCapacityValue = 0;
|
|
angular.forEach(teamInfoService.getAll(), function (team, teamKey) {
|
|
for (var catKey in team.ExpCategories) {
|
|
var teamExpCat = team.ExpCategories[catKey];
|
|
var ecCapHours = 0;
|
|
var ecCapResources = 0;
|
|
if (teamExpCat.AllowResourceAssignment) {
|
|
// Calculations for ordinary EC
|
|
var ecCapHours = isCapacityTypeActuals ? teamExpCat.ActualCapacityValues[week.Milliseconds] || 0 : teamExpCat.PlannedCapacityValues[week.Milliseconds] || 0;
|
|
if (!angular.isNumber(ecCapHours) || isNaN(ecCapHours)) {
|
|
ecCapHours = 0;
|
|
}
|
|
ecCapResources = hoursResourcesConverter.convertToResources(catKey, ecCapHours);
|
|
}
|
|
else {
|
|
// Super EC does not increase team capacity
|
|
}
|
|
hoursCapacityValue += ecCapHours;
|
|
resourcesCapacityValue += ecCapResources;
|
|
}
|
|
});
|
|
var hoursToAdd = hoursCapacityValue - hoursTotalValue;
|
|
var resourcesToAdd = resourcesCapacityValue - resourcesTotalValue;
|
|
|
|
capacityRow.QuantityHoursValues[weekIndex] = roundService.roundQuantity(capacityRow.QuantityHoursValues[weekIndex] + hoursToAdd);
|
|
capacityRow.QuantityResourcesValues[weekIndex] = roundService.roundQuantity(capacityRow.QuantityResourcesValues[weekIndex] + resourcesToAdd);
|
|
capacityRow.QuantityHoursValues[month.SelfIndexInWeeks] = capacityRow.QuantityHoursValues[month.SelfIndexInWeeks] + hoursToAdd;
|
|
capacityRow.QuantityResourcesValues[month.SelfIndexInWeeks] = capacityRow.QuantityResourcesValues[month.SelfIndexInWeeks] + resourcesToAdd;
|
|
capacityRow.TotalHoursValue += hoursToAdd;
|
|
capacityRow.TotalResourcesValue += resourcesToAdd;
|
|
}
|
|
capacityRow.QuantityHoursValues[month.SelfIndexInWeeks] = roundService.roundQuantity(capacityRow.QuantityHoursValues[month.SelfIndexInWeeks]);
|
|
capacityRow.QuantityResourcesValues[month.SelfIndexInWeeks] = roundService.roundQuantity(capacityRow.QuantityResourcesValues[month.SelfIndexInWeeks]);
|
|
}
|
|
capacityRow.TotalHoursValue = roundService.roundQuantity(capacityRow.TotalHoursValue);
|
|
capacityRow.TotalResourcesValue = roundService.roundQuantity(capacityRow.TotalResourcesValue);
|
|
},
|
|
fillResourceRemainingCapacityRowData: function (totalRow, capacityRow, resourceId, header) {
|
|
if (!totalRow || !capacityRow || !resourceId) {
|
|
return;
|
|
}
|
|
|
|
if (!header || !header.Months || !header.Weeks) {
|
|
return;
|
|
}
|
|
|
|
var teams = teamInfoService.getAll();
|
|
var teamKeys = Object.keys(teams);
|
|
|
|
capacityRow.TotalHoursValue = 0;
|
|
capacityRow.TotalResourcesValue = 0;
|
|
capacityRow.VisibleCellsCount = header.TotalWeeks;
|
|
for (var monthIndex = 0; monthIndex < header.Months.length; monthIndex++) {
|
|
var month = header.Months[monthIndex];
|
|
|
|
capacityRow.QuantityHoursValues[month.SelfIndexInWeeks] = 0;
|
|
capacityRow.QuantityResourcesValues[month.SelfIndexInWeeks] = 0;
|
|
for (var mcIndex = 0; mcIndex < month.Childs.length; mcIndex++) {
|
|
var weekIndex = month.Childs[mcIndex];
|
|
var week = header.Weeks[weekIndex];
|
|
|
|
capacityRow.QuantityHoursValues[weekIndex] = 0;
|
|
capacityRow.QuantityResourcesValues[weekIndex] = 0;
|
|
|
|
var hoursTotalValue = totalRow.QuantityHoursValues[weekIndex] || 0;
|
|
var resourcesTotalValue = totalRow.QuantityResourcesValues[weekIndex] || 0;
|
|
var hoursCapacityValue = 0;
|
|
var resourcesCapacityValue = 0;
|
|
|
|
for (var tIndex = 0; tIndex < teamKeys.length; tIndex++) {
|
|
var team = teams[teamKeys[tIndex]];
|
|
|
|
for (var catKey in team.ExpCategories) {
|
|
var teamExpCat = team.ExpCategories[catKey];
|
|
|
|
if (teamExpCat.Resources && (resourceId in teamExpCat.Resources)) {
|
|
var resource = teamExpCat.Resources[resourceId];
|
|
|
|
if (resource && teamExpCat.AllowResourceAssignment) {
|
|
var resCapHours = 0;
|
|
var resCapResources = 0;
|
|
|
|
// Calculations for ordinary EC
|
|
if (resource.TotalCapacity && resource.TotalCapacity[week.Milliseconds]) {
|
|
var resCapHours = resource.TotalCapacity[week.Milliseconds];
|
|
if (!angular.isNumber(resCapHours) || isNaN(resCapResources)) {
|
|
resCapHours = 0;
|
|
}
|
|
resCapResources = hoursResourcesConverter.convertToResources(catKey, resCapHours);
|
|
}
|
|
|
|
hoursCapacityValue += resCapHours;
|
|
resourcesCapacityValue += resCapResources;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
var hoursToAdd = hoursCapacityValue - hoursTotalValue;
|
|
var resourcesToAdd = resourcesCapacityValue - resourcesTotalValue;
|
|
|
|
capacityRow.QuantityHoursValues[weekIndex] = roundService.roundQuantity(capacityRow.QuantityHoursValues[weekIndex] + hoursToAdd);
|
|
capacityRow.QuantityResourcesValues[weekIndex] = roundService.roundQuantity(capacityRow.QuantityResourcesValues[weekIndex] + resourcesToAdd);
|
|
capacityRow.QuantityHoursValues[month.SelfIndexInWeeks] = capacityRow.QuantityHoursValues[month.SelfIndexInWeeks] + hoursToAdd;
|
|
capacityRow.QuantityResourcesValues[month.SelfIndexInWeeks] = capacityRow.QuantityResourcesValues[month.SelfIndexInWeeks] + resourcesToAdd;
|
|
capacityRow.TotalHoursValue += hoursToAdd;
|
|
capacityRow.TotalResourcesValue += resourcesToAdd;
|
|
}
|
|
capacityRow.QuantityHoursValues[month.SelfIndexInWeeks] = roundService.roundQuantity(capacityRow.QuantityHoursValues[month.SelfIndexInWeeks]);
|
|
capacityRow.QuantityResourcesValues[month.SelfIndexInWeeks] = roundService.roundQuantity(capacityRow.QuantityResourcesValues[month.SelfIndexInWeeks]);
|
|
}
|
|
capacityRow.TotalHoursValue = roundService.roundQuantity(capacityRow.TotalHoursValue);
|
|
capacityRow.TotalResourcesValue = roundService.roundQuantity(capacityRow.TotalResourcesValue);
|
|
},
|
|
fillTotalNptRowData: function (totalNptRow, nptDataModel, header, rollupToRows) {
|
|
if (!totalNptRow) {
|
|
return;
|
|
}
|
|
|
|
if (!header || !header.Months || !header.Weeks) {
|
|
return;
|
|
}
|
|
|
|
totalNptRow.TotalHoursValue = 0;
|
|
totalNptRow.TotalResourcesValue = 0;
|
|
totalNptRow.VisibleCellsCount = header.TotalWeeks;
|
|
|
|
if (totalNptRow.Children && nptDataModel) {
|
|
// Fill NPT total row data during filling of NPT categories rows
|
|
for (var index = 0; index < totalNptRow.Children.length; index++) {
|
|
var nptCatRow = totalNptRow.Children[index];
|
|
var nptCatData = nptDataModel[nptCatRow.Id];
|
|
this.fillNptCategoryRowData(nptCatRow, totalNptRow, nptCatData, header, rollupToRows);
|
|
};
|
|
|
|
totalNptRow.TotalHoursValue = roundService.roundQuantity(totalNptRow.TotalHoursValue);
|
|
totalNptRow.TotalResourcesValue = roundService.roundQuantity(totalNptRow.TotalResourcesValue);
|
|
}
|
|
else {
|
|
// Fill NPT total row with zero values, as NPT data is empty
|
|
for (var monthIndex = 0; monthIndex < header.Months.length; monthIndex++) {
|
|
var month = header.Months[monthIndex];
|
|
|
|
totalNptRow.QuantityHoursValues[month.SelfIndexInWeeks] = 0;
|
|
totalNptRow.QuantityResourcesValues[month.SelfIndexInWeeks] = 0;
|
|
|
|
for (var mcIndex = 0; mcIndex < month.Childs.length; mcIndex++) {
|
|
var weekIndex = month.Childs[mcIndex];
|
|
var week = header.Weeks[weekIndex];
|
|
|
|
totalNptRow.QuantityHoursValues[weekIndex] = 0;
|
|
totalNptRow.QuantityResourcesValues[weekIndex] = 0;
|
|
}
|
|
}
|
|
}
|
|
},
|
|
fillNptCategoryRowData: function (nptCatRow, totalNptRow, nptCatData, header, rollupToRows) {
|
|
if (!nptCatRow || !totalNptRow) {
|
|
return;
|
|
}
|
|
|
|
if (!header || !header.Months || !header.Weeks) {
|
|
return;
|
|
}
|
|
|
|
nptCatRow.VisibleCellsCount = header.TotalWeeks;
|
|
var readonlyToDate = this.getUtcNow();
|
|
|
|
if (nptCatRow.Children && nptCatData) {
|
|
for (var index = 0; index < nptCatRow.Children.length; index++) {
|
|
var nptItemRow = nptCatRow.Children[index];
|
|
var nptItemData = nptCatData.NonProjectTime[nptItemRow.Id];
|
|
this.fillNptItemRowData(nptItemRow, nptCatRow, totalNptRow, nptItemData, readonlyToDate, header, rollupToRows);
|
|
};
|
|
}
|
|
},
|
|
fillNptItemRowData: function (nptItemRow, nptCatRow, totalNptRow, nptItemData, readonlyToDate, header, rollupToRows) {
|
|
if (!nptItemRow || !nptCatRow || !totalNptRow) {
|
|
return;
|
|
}
|
|
|
|
if (!header || !header.Months || !header.Weeks) {
|
|
return;
|
|
}
|
|
|
|
nptItemRow.VisibleCellsCount = header.TotalWeeks;
|
|
|
|
if (nptItemData && nptItemData.Resources) {
|
|
if (!nptItemData.isTeamWide) {
|
|
// Fill rows with data for resource-level NPT
|
|
if (nptItemRow.Children && nptItemRow.Children.length) {
|
|
for (var index = 0; index < nptItemRow.Children.length; index++) {
|
|
var nptResourceRow = nptItemRow.Children[index];
|
|
var nptResourceData = nptItemData.Resources[nptResourceRow.Id];
|
|
this.fillNptResourceRowData(nptResourceRow, nptItemRow, nptCatRow, totalNptRow, nptItemData, nptResourceData, readonlyToDate, header, rollupToRows);
|
|
};
|
|
}
|
|
}
|
|
else {
|
|
// Fill with data for team-level NPT
|
|
for (var resourceId in nptItemData.Resources) {
|
|
var nptResourceData = nptItemData.Resources[resourceId];
|
|
this.fillNptResourceRowData(null, nptItemRow, nptCatRow, totalNptRow, nptItemData, nptResourceData, readonlyToDate, header, rollupToRows);
|
|
};
|
|
}
|
|
}
|
|
},
|
|
fillNptResourceRowData: function (nptResourceRow, nptItemRow, nptCatRow, totalNptRow, nptItemData, nptResourceData, readonlyToDate, header, rollupToRows) {
|
|
if (!header) {
|
|
return;
|
|
}
|
|
|
|
if (!nptItemRow || !nptCatRow || !totalNptRow || !nptItemData || !nptResourceData) {
|
|
return;
|
|
}
|
|
|
|
var performRollup = rollupToRows && (rollupToRows.length > 0);
|
|
|
|
if (nptResourceRow) {
|
|
nptResourceRow.VisibleCellsCount = header.TotalWeeks;
|
|
}
|
|
|
|
for (var monthIndex = 0; monthIndex < header.Months.length; monthIndex++) {
|
|
var month = header.Months[monthIndex];
|
|
|
|
if (nptResourceRow) {
|
|
nptResourceRow.EditableNptWeeks[month.SelfIndexInWeeks] = true;
|
|
}
|
|
|
|
if (nptItemRow && nptItemRow.IsTeamWide) {
|
|
nptItemRow.EditableNptWeeks[month.SelfIndexInWeeks] = true;
|
|
}
|
|
|
|
for (var mcIndex = 0; mcIndex < month.Childs.length; mcIndex++) {
|
|
var weekIndex = month.Childs[mcIndex];
|
|
var week = header.Weeks[weekIndex];
|
|
|
|
var resourceExpCatId = nptResourceData.ExpenditureCategoryId;
|
|
var hoursValue = (nptResourceData && nptResourceData.Allocations && nptResourceData.Allocations[week.Milliseconds])
|
|
? nptResourceData.Allocations[week.Milliseconds] || 0 : 0;
|
|
var resourcesValue = (resourceExpCatId && hoursValue != 0) ? hoursResourcesConverter.convertToResources(resourceExpCatId, hoursValue) : 0;
|
|
var isWeekEditable = (hoursValue != 0) && (week.Milliseconds > readonlyToDate);
|
|
|
|
if (nptResourceRow) {
|
|
nptResourceRow.QuantityHoursValues[weekIndex] = hoursValue;
|
|
nptResourceRow.QuantityResourcesValues[weekIndex] = resourcesValue;
|
|
|
|
nptResourceRow.QuantityHoursValues[month.SelfIndexInWeeks] = (nptResourceRow.QuantityHoursValues[month.SelfIndexInWeeks] || 0) + hoursValue;
|
|
nptResourceRow.QuantityResourcesValues[month.SelfIndexInWeeks] = (nptResourceRow.QuantityResourcesValues[month.SelfIndexInWeeks] || 0) + resourcesValue;
|
|
|
|
nptResourceRow.TotalHoursValue += hoursValue;
|
|
nptResourceRow.TotalResourcesValue += resourcesValue;
|
|
|
|
// Set week / month cells to be editable or read-only
|
|
nptResourceRow.EditableNptWeeks[weekIndex] = isWeekEditable && !nptResourceData.isReadOnly;
|
|
nptResourceRow.EditableNptWeeks[month.SelfIndexInWeeks] =
|
|
nptResourceRow.EditableNptWeeks[month.SelfIndexInWeeks] && nptResourceRow.EditableNptWeeks[weekIndex];
|
|
}
|
|
|
|
if (nptItemRow) {
|
|
nptItemRow.QuantityHoursValues[weekIndex] = (nptItemRow.QuantityHoursValues[weekIndex] || 0) + hoursValue;
|
|
nptItemRow.QuantityResourcesValues[weekIndex] = (nptItemRow.QuantityResourcesValues[weekIndex] || 0) + resourcesValue;
|
|
nptItemRow.QuantityHoursValues[month.SelfIndexInWeeks] = (nptItemRow.QuantityHoursValues[month.SelfIndexInWeeks] || 0) + hoursValue;
|
|
nptItemRow.QuantityResourcesValues[month.SelfIndexInWeeks] = (nptItemRow.QuantityResourcesValues[month.SelfIndexInWeeks] || 0) + resourcesValue;
|
|
nptItemRow.TotalHoursValue += hoursValue;
|
|
nptItemRow.TotalResourcesValue += resourcesValue;
|
|
|
|
// Set week / month cells to be editable or read-only
|
|
// nptItemRow.EditableNptWeeks[weekIndex] = isWeekEditable && nptItemRow.IsTeamWide && !nptItemData.isReadOnly;
|
|
nptItemRow.EditableNptWeeks[weekIndex] = isWeekEditable && !nptItemData.isReadOnly;
|
|
nptItemRow.EditableNptWeeks[month.SelfIndexInWeeks] =
|
|
nptItemRow.EditableNptWeeks[month.SelfIndexInWeeks] && nptItemRow.EditableNptWeeks[weekIndex];
|
|
}
|
|
|
|
if (nptCatRow) {
|
|
nptCatRow.QuantityHoursValues[weekIndex] = (nptCatRow.QuantityHoursValues[weekIndex] || 0) + hoursValue;
|
|
nptCatRow.QuantityResourcesValues[weekIndex] = (nptCatRow.QuantityResourcesValues[weekIndex] || 0) + resourcesValue;
|
|
nptCatRow.QuantityHoursValues[month.SelfIndexInWeeks] = (nptCatRow.QuantityHoursValues[month.SelfIndexInWeeks] || 0) + hoursValue;
|
|
nptCatRow.QuantityResourcesValues[month.SelfIndexInWeeks] = (nptCatRow.QuantityResourcesValues[month.SelfIndexInWeeks] || 0) + resourcesValue;
|
|
nptCatRow.TotalHoursValue += hoursValue;
|
|
nptCatRow.TotalResourcesValue += resourcesValue;
|
|
}
|
|
|
|
if (totalNptRow) {
|
|
totalNptRow.QuantityHoursValues[weekIndex] = (totalNptRow.QuantityHoursValues[weekIndex] || 0) + hoursValue;
|
|
totalNptRow.QuantityResourcesValues[weekIndex] = (totalNptRow.QuantityResourcesValues[weekIndex] || 0) + resourcesValue;
|
|
totalNptRow.QuantityHoursValues[month.SelfIndexInWeeks] = (totalNptRow.QuantityHoursValues[month.SelfIndexInWeeks] || 0) + hoursValue;
|
|
totalNptRow.QuantityResourcesValues[month.SelfIndexInWeeks] = (totalNptRow.QuantityResourcesValues[month.SelfIndexInWeeks] || 0) + resourcesValue;
|
|
totalNptRow.TotalHoursValue += hoursValue;
|
|
totalNptRow.TotalResourcesValue += resourcesValue;
|
|
}
|
|
|
|
if (performRollup) {
|
|
for (var rollupRowIndex = 0; rollupRowIndex < rollupToRows.length; rollupRowIndex++) {
|
|
var rollupRow = rollupToRows[rollupRowIndex];
|
|
if (rollupRow) {
|
|
rollupRow.QuantityHoursValues[weekIndex] = (rollupRow.QuantityHoursValues[weekIndex] || 0) + hoursValue;
|
|
rollupRow.QuantityResourcesValues[weekIndex] = (rollupRow.QuantityResourcesValues[weekIndex] || 0) + resourcesValue;
|
|
rollupRow.QuantityHoursValues[month.SelfIndexInWeeks] = (rollupRow.QuantityHoursValues[month.SelfIndexInWeeks] || 0) + hoursValue;
|
|
rollupRow.QuantityResourcesValues[month.SelfIndexInWeeks] = (rollupRow.QuantityResourcesValues[month.SelfIndexInWeeks] || 0) + resourcesValue;
|
|
rollupRow.TotalHoursValue += hoursValue;
|
|
rollupRow.TotalResourcesValue += resourcesValue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
groupNptDataByCategory: function (resourceKey, resourceData, targetData) {
|
|
if (!resourceData || !resourceData.NonProjectTime)
|
|
return targetData;
|
|
|
|
for (var nptId in resourceData.NonProjectTime) {
|
|
var sourceNpt = resourceData.NonProjectTime[nptId];
|
|
if (!sourceNpt)
|
|
continue;
|
|
|
|
var targetCat = targetData[sourceNpt.CategoryId];
|
|
if (!targetCat)
|
|
targetCat = {};
|
|
|
|
var targetNpt = targetCat[nptId];
|
|
if (!targetNpt)
|
|
targetNpt = {
|
|
Id: sourceNpt.Id,
|
|
Name: sourceNpt.Name,
|
|
isTeamWide: sourceNpt.isTeamWide,
|
|
isReadOnly: sourceNpt.isReadOnly,
|
|
CategoryId: sourceNpt.CategoryId
|
|
};
|
|
|
|
if (!targetNpt.Resources)
|
|
targetNpt.Resources = {};
|
|
|
|
if (!targetNpt.Resources[resourceKey])
|
|
targetNpt.Resources[resourceKey] = {
|
|
Id: resourceKey,
|
|
Name: resourceData.Name,
|
|
isReadOnly: sourceNpt.isReadOnly,
|
|
SortingKey: resourceData.SortingKey,
|
|
ExpenditureCategoryId: resourceData.OwnExpenditureCategoryId,
|
|
Allocations: {}
|
|
};
|
|
|
|
if (!sourceNpt.Allocations)
|
|
continue
|
|
|
|
for (var week in sourceNpt.Allocations) {
|
|
targetNpt.Resources[resourceKey].Allocations[week] = (targetNpt.Resources[resourceKey].Allocations[week] || 0) + (sourceNpt.Allocations[week] || 0);
|
|
}
|
|
|
|
targetCat[nptId] = targetNpt;
|
|
targetData[sourceNpt.CategoryId] = targetCat;
|
|
}
|
|
return targetData;
|
|
},
|
|
getNonProjectTimeDataModel: function (nonProjectTimeCategoryCache, groupByMode) {
|
|
if (!nonProjectTimeCategoryCache || (groupByMode === undefined) || (groupByMode == ""))
|
|
return null;
|
|
|
|
if (!angular.isNumber(Number(groupByMode)))
|
|
return;
|
|
|
|
var groupMode = Number(groupByMode);
|
|
var teams = teamInfoService.getAll();
|
|
if (!teams)
|
|
return null;
|
|
|
|
var nonProjectTimeCategories = nonProjectTimeCategoryCache;
|
|
var nonProjectTime = {};
|
|
var result = {};
|
|
|
|
var nptCatKeys = Object.keys(nonProjectTimeCategories);
|
|
if (nptCatKeys.length < 1)
|
|
// NPT data found in data source dataModel
|
|
return result;
|
|
|
|
// gather all npt allocations from resources and group them by NPT Category
|
|
for (var teamKey in teams) {
|
|
var teamData = teams[teamKey];
|
|
if (!teamData || !teamData.ExpCategories)
|
|
continue;
|
|
|
|
for (var expCatKey in teamData.ExpCategories) {
|
|
var expCatData = teamData.ExpCategories[expCatKey];
|
|
if (!expCatData || !expCatData.Resources || !expCatData.AllowResourceAssignment)
|
|
continue;
|
|
|
|
for (var resKey in expCatData.Resources) {
|
|
var resData = teamInfoService.extendResourceModelWithAttributes(expCatData.Resources[resKey]);
|
|
if (!resData || !resData.NonProjectTime || (Object.keys(resData.NonProjectTime).length < 1))
|
|
continue;
|
|
|
|
var groupingKey = null;
|
|
switch (groupMode) {
|
|
case 0: groupingKey = resKey; break; // Single group NPTs by current resource
|
|
case 1: groupingKey = teamData.Id; break; // Group NPTs by Teams
|
|
case 2: groupingKey = C_EMPTY_GUID; break; // Single virtual group on Guid.Empty key
|
|
case 3: groupingKey = resKey; break; // Group NPTs by Resources
|
|
case 4: groupingKey = teamData.CompanyId; break; // Group NPTs by Companies
|
|
case 5: groupingKey = C_EMPTY_GUID; break; // Single virtual group by Guid.Empty key for group by client mode
|
|
case 6: groupingKey = C_EMPTY_GUID; break; // Single virtual group by Guid.Empty key for group by cost center mode
|
|
}
|
|
|
|
if (groupingKey) {
|
|
if (!nonProjectTime[groupingKey])
|
|
nonProjectTime[groupingKey] = {};
|
|
|
|
nonProjectTime[groupingKey] = this.groupNptDataByCategory(resKey, resData, nonProjectTime[groupingKey]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// build UI dataModel rows
|
|
for (var groupId in nonProjectTime) {
|
|
var nonProjectTimeForGroup = nonProjectTime[groupId];
|
|
|
|
for (var nptCatId in nonProjectTimeCategories) {
|
|
var nptCatName = nonProjectTimeCategories[nptCatId];
|
|
var nptCatResultModel = {
|
|
Id: nptCatId,
|
|
Name: nptCatName,
|
|
NonProjectTime: {}
|
|
}
|
|
|
|
// Get NPT-items in current category
|
|
var nptResourcesAsArray = {};
|
|
var nptItems = nonProjectTimeForGroup[nptCatId];
|
|
if (!nptItems)
|
|
continue;
|
|
|
|
for (var nptItemId in nptItems) {
|
|
var nptItem = angular.copy(nptItems[nptItemId]);
|
|
nptCatResultModel.NonProjectTime[nptItemId] = nptItem;
|
|
}
|
|
|
|
if (!result[groupId])
|
|
result[groupId] = {};
|
|
|
|
result[groupId][nptCatId] = nptCatResultModel;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
},
|
|
getNonProjectTimeItemsByCategory: function (nonProjectTime, categoryId) {
|
|
if (!categoryId || !nonProjectTime)
|
|
return null;
|
|
|
|
var result = {};
|
|
|
|
for (var nptId in nonProjectTime) {
|
|
var nptItem = nonProjectTime[nptId];
|
|
|
|
if (nptItem.CategoryId && (nptItem.CategoryId == categoryId))
|
|
result[nptId] = nptItem;
|
|
}
|
|
|
|
return result;
|
|
},
|
|
getExpenditures4Project: function (project, teamIds, ecs, header, filter, includeProjectNeed, filterExpendituresByTeams, groupDataByTeams, includeWithoutAllocation) {//todo: test all usage
|
|
// groupDataByTeams - indicates whether to put team rows under EC rows or not
|
|
var expenditures = {};
|
|
|
|
if (!project || !project.ActiveScenario || !filter) {
|
|
return expenditures;
|
|
}
|
|
|
|
var scenarioId = project.ActiveScenario.Id;
|
|
var startDate = project.ActiveScenario.StartDate;
|
|
var endDate = project.ActiveScenario.StartDate;
|
|
var allocationMode = project.AllocationMode || this.allocationMode.teamAllocation;
|
|
|
|
if (!scenarioId || !startDate || !endDate) {
|
|
return expenditures;
|
|
}
|
|
|
|
if (!header || !header.getWeekEndingsRange) {
|
|
return expenditures;
|
|
}
|
|
|
|
var projectTeams = Object.keys(project.Teams || {});
|
|
|
|
// which teams are here, only AC or all project? Answ: AC displayed only
|
|
teamIds = (teamIds && teamIds.length) ? teamIds : projectTeams;
|
|
var ta4Project = activityCalendarService.getTeamAllocations4Scenario(filter, teamIds, scenarioId) || {};
|
|
var na4Project = includeProjectNeed ? activityCalendarService.getNeedAllocations4Scenario(filter, scenarioId) || {} : {};
|
|
// remaining need should always be calculated according to all project teams that are available in the calendar
|
|
var rna4Project = includeProjectNeed ? activityCalendarService.getRemainingNeedAllocations4Scenario(filter, projectTeams, scenarioId) || {} : {};
|
|
var un4Project = includeProjectNeed ? activityCalendarService.getUnassignedNeedAllocations4Scenario(filter, scenarioId) || {} : {};
|
|
var rta4Project = includeProjectNeed ? activityCalendarService.getRemainingTeamAllocations4Scenario(filter, teamIds, project.ProjectId, scenarioId) || {} : {};
|
|
// TODO: when will remove redundant data based on allocationMode verify that list of ecs is not missing any category because of absence of related data
|
|
|
|
var requiredECs = ecs || union(Object.keys(ta4Project), Object.keys(na4Project));
|
|
var filteredExpCats = requiredECs;
|
|
|
|
if (filterExpendituresByTeams) {
|
|
// Apply filtering of Expenditures by teams and existance of team allocations
|
|
filteredExpCats = activityCalendarService.filterCategoriesVsTeams(requiredECs, teamIds);
|
|
|
|
var alwaysVisibleExpCats = activityCalendarService.getAlwaysVisibleProjectRolesByClientSideFilter();
|
|
filteredExpCats = this.getCategoriesWithNonZeroTeamAllocations(filteredExpCats, ta4Project, alwaysVisibleExpCats, includeWithoutAllocation);
|
|
}
|
|
|
|
for (var ecIndex = 0; ecIndex < filteredExpCats.length; ecIndex++) {
|
|
var categoryId = filteredExpCats[ecIndex];
|
|
if (categoryId in expenditures) { // just additional check for case when requiredECs is not a distinct array for some reasons
|
|
continue;
|
|
}
|
|
|
|
var sourceCategory = dataSources.getExpenditureById(categoryId);
|
|
if (!sourceCategory) {
|
|
throw 'Expenditure category with id = ' + categoryId + ' does not exist';
|
|
}
|
|
var categoryTeams = (categoryId in ta4Project) ? angular.copy(ta4Project[categoryId].Teams || null) : null;
|
|
if (groupDataByTeams) {
|
|
// allocation mode on team row cannot be project need or unassigned project need
|
|
var teamAllocationMode = (allocationMode == this.allocationMode.needAllocation || allocationMode == this.allocationMode.remainingNeedAllocation) ?
|
|
this.allocationMode.teamAllocation : allocationMode;
|
|
if (categoryTeams)
|
|
for (var teamId in categoryTeams) {
|
|
var currentTeam = teamInfoService.getById(teamId);
|
|
categoryTeams[teamId].AllocationMode = teamAllocationMode;
|
|
categoryTeams[teamId].Resources = this.getAssignedResources4Project(filter, project, categoryId, [teamId]);
|
|
categoryTeams[teamId].AvailableResources = this.getAvailableResources4Project(filter, header, project, categoryId, [teamId]);
|
|
categoryTeams[teamId].Name = currentTeam.Name;
|
|
categoryTeams[teamId].Id = currentTeam.Id;
|
|
categoryTeams[teamId].ExpenditureCategoryId = categoryId;
|
|
if (rta4Project && (categoryId in rta4Project) && rta4Project[categoryId].Teams && (teamId in rta4Project[categoryId].Teams))
|
|
categoryTeams[teamId].RemainingTeamAllocations = angular.copy(rta4Project[categoryId].Teams[teamId].Allocations || {});
|
|
else
|
|
categoryTeams[teamId].RemainingTeamAllocations = {};
|
|
}
|
|
}
|
|
|
|
expenditures[categoryId] = {
|
|
Id: categoryId,
|
|
Name: sourceCategory.ExpenditureCategoryName,
|
|
AllocationMode: allocationMode,
|
|
AllowResourceAssignment: sourceCategory.AllowResourceAssignment,
|
|
Allocations: (categoryId in ta4Project) ? angular.copy(ta4Project[categoryId].Allocations || {}) : {},
|
|
NeedAllocations: (categoryId in na4Project) ? angular.copy(na4Project[categoryId].Allocations || {}) : {},
|
|
RemainingNeed: (categoryId in rna4Project) ? angular.copy(rna4Project[categoryId].Allocations || {}) : {},
|
|
UnassignedNeed: (categoryId in un4Project) ?
|
|
angular.copy(un4Project && un4Project[categoryId] && un4Project[categoryId].Allocations ? un4Project[categoryId].Allocations : {}) : {},
|
|
RemainingTeamAllocations: (categoryId in rta4Project) ? angular.copy(rta4Project[categoryId].Allocations || {}) : {},
|
|
Teams: categoryTeams
|
|
};
|
|
}
|
|
|
|
return expenditures;
|
|
},
|
|
getExpendituresWithResources4Project: function (project, teamIds, ecs, header, filter, includeProjectNeed, filterCategories, groupDataByTeams, includeWithoutAllocation) {//todo: test all usage
|
|
//console.log(groupDataByTeams,includeWithoutAllocation);
|
|
// groupDataByTeams - indicates whether to put team rows under EC rows or not
|
|
var expenditures = this.getExpenditures4Project(project, teamIds, ecs, header, filter, includeProjectNeed, filterCategories, groupDataByTeams, includeWithoutAllocation) || {};
|
|
|
|
if (!groupDataByTeams) // do not put resources under EC row, because there will be team rows
|
|
{
|
|
for (var categoryId in expenditures) {
|
|
expenditures[categoryId].Resources = this.getAssignedResources4Project(filter, project, categoryId, teamIds);
|
|
expenditures[categoryId].AvailableResources = this.getAvailableResources4Project(filter, header, project, categoryId, teamIds);
|
|
}
|
|
}
|
|
|
|
return expenditures;
|
|
},
|
|
getAssignedResources4Project: function (filter, project, categoryId, teamIds, resourceId) {
|
|
if (!filter || !project || !project.ActiveScenario || !project.Teams || !categoryId) {
|
|
return null;
|
|
}
|
|
|
|
var result = {};
|
|
|
|
var targetTeamIds = (teamIds && teamIds.length) ? teamIds : Object.keys(project.Teams);
|
|
if (targetTeamIds.length <= 0) {
|
|
return null;
|
|
}
|
|
|
|
var isSuperEC = dataSources.isSuperEC(categoryId);
|
|
var requestedResourceIds = resourceId ? [resourceId] : (activityCalendarService.getResourcesByProject(filter, project.ProjectId, categoryId, targetTeamIds) || []);
|
|
for (var teamIndex = 0; teamIndex < targetTeamIds.length; teamIndex++) {
|
|
var teamId = targetTeamIds[teamIndex];
|
|
|
|
// if requested category is super one we need to get all resources that exist in the team
|
|
var resources = teamInfoService.getResourcesByTeam(teamId, (isSuperEC ? null : categoryId));
|
|
if (!resources || !Object.keys(resources).length) {
|
|
continue;
|
|
}
|
|
|
|
for (var resourceIndex = 0; resourceIndex < requestedResourceIds.length; resourceIndex++) {
|
|
var processingResourceId = requestedResourceIds[resourceIndex];
|
|
var isResourceAssigned = activityCalendarService.checkResourceIsAssignedToProject(filter, project.ProjectId, categoryId, targetTeamIds, processingResourceId);
|
|
if (!isResourceAssigned) {
|
|
continue;
|
|
}
|
|
|
|
var resource = resources[processingResourceId];
|
|
if (resource) {
|
|
if (!result[resource.Id]) {
|
|
result[resource.Id] = {
|
|
Id: resource.Id,
|
|
Name: resource.Name,
|
|
Teams: [],
|
|
Allocations: {},
|
|
};
|
|
}
|
|
|
|
var savedResource = result[resource.Id];
|
|
|
|
for (var mIndex = 0; mIndex < resource.Teams.length; mIndex++) {
|
|
var membershipInfo = resource.Teams[mIndex];
|
|
|
|
if (membershipInfo.TeamId == teamId) {
|
|
savedResource.Teams.push({
|
|
TeamId: teamId,
|
|
ReadOnly: membershipInfo.ReadOnly,
|
|
StartDate: membershipInfo.StartDate,
|
|
EndDate: membershipInfo.EndDate
|
|
});
|
|
|
|
savedResource.StartDate = Math.min(savedResource.StartDate || membershipInfo.StartDate, membershipInfo.StartDate);
|
|
savedResource.EndDate = membershipInfo.EndDate ? Math.max(savedResource.EndDate || membershipInfo.EndDate, membershipInfo.EndDate) : null;
|
|
}
|
|
}
|
|
|
|
var resourceAllocations = activityCalendarService.getResourceAllocations4Scenario(filter, resource.Id, project.ActiveScenario.Id, teamId, categoryId);
|
|
if (resourceAllocations) {
|
|
angular.forEach(resourceAllocations, function (item, key) {
|
|
savedResource.Allocations[key] = item;
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
},
|
|
getAvailableResources4Project: function (filter, header, project, categoryId, teamIds, resourceId) {
|
|
if (!filter || !header || !project || !project.ActiveScenario || !project.Teams || !categoryId) {
|
|
return null;
|
|
}
|
|
|
|
var availableResources = {};
|
|
var result = {};
|
|
|
|
var targetTeamIds = (teamIds && teamIds.length) ? teamIds : Object.keys(project.Teams);
|
|
if (targetTeamIds.length <= 0) {
|
|
return null;
|
|
}
|
|
|
|
var isSuperEC = dataSources.isSuperEC(categoryId);
|
|
var scenarioRange = header.getWeekEndingsRange(project.ActiveScenario.StartDate, project.ActiveScenario.EndDate);
|
|
|
|
for (var teamIndex = 0; teamIndex < targetTeamIds.length; teamIndex++) {
|
|
var teamId = targetTeamIds[teamIndex];
|
|
|
|
// if requested category is super one we need to get all resources that exist in the team
|
|
var resources = teamInfoService.getResourcesWithAllocationsByTeam(teamId, (isSuperEC ? null : categoryId), (resourceId ? [resourceId] : null));
|
|
if (!resources || !Object.keys(resources).length) {
|
|
continue;
|
|
}
|
|
|
|
var requestedResourceIds = resourceId ? [resourceId] : Object.keys(resources);
|
|
for (var resourceIndex = 0; resourceIndex < requestedResourceIds.length; resourceIndex++) {
|
|
var processingResourceId = requestedResourceIds[resourceIndex];
|
|
var isResourceAssigned = activityCalendarService.checkResourceIsAssignedToProject(filter, project.ProjectId, categoryId, targetTeamIds, processingResourceId);
|
|
if (isResourceAssigned) {
|
|
continue;
|
|
}
|
|
|
|
var resource = resources[processingResourceId];
|
|
if (resource && checkAnyTeamEditable(resource.Teams)) {
|
|
if (!availableResources[resource.Id]) {
|
|
availableResources[resource.Id] = {
|
|
Id: resource.Id,
|
|
Name: resource.Name,
|
|
OwnExpenditureCategoryId: resource.OwnExpenditureCategoryId,
|
|
Teams: [],
|
|
AllocatedCapacity: {},
|
|
TotalCapacity: {}
|
|
};
|
|
}
|
|
|
|
var savedResource = availableResources[resource.Id];
|
|
|
|
for (var mIndex = 0; mIndex < resource.Teams.length; mIndex++) {
|
|
var teamMembership = resource.Teams[mIndex];
|
|
if (teamMembership && (teamMembership.TeamId == teamId)) {
|
|
savedResource.Teams.push({
|
|
TeamId: teamId,
|
|
ReadOnly: teamMembership.ReadOnly,
|
|
StartDate: teamMembership.StartDate,
|
|
EndDate: teamMembership.EndDate
|
|
});
|
|
|
|
savedResource.StartDate = Math.min(savedResource.StartDate || teamMembership.StartDate, teamMembership.StartDate);
|
|
savedResource.EndDate = teamMembership.EndDate ? Math.max(savedResource.EndDate || teamMembership.EndDate, teamMembership.EndDate) : null;
|
|
}
|
|
}
|
|
|
|
if (resource.AllocatedCapacity) {
|
|
angular.forEach(resource.AllocatedCapacity, function (item, key) {
|
|
savedResource.AllocatedCapacity[key] = item;
|
|
});
|
|
}
|
|
if (resource.TotalCapacity) {
|
|
angular.forEach(resource.TotalCapacity, function (item, key) {
|
|
savedResource.TotalCapacity[key] = item;
|
|
});
|
|
}
|
|
if (resource.NonProjectTime) {
|
|
savedResource.NonProjectTime = resource.NonProjectTime;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (Object.keys(availableResources).length) {
|
|
for (var resourceId in availableResources) {
|
|
var resource = availableResources[resourceId];
|
|
if (resource) {
|
|
var sourceCategory = dataSources.getExpenditureById(resource.OwnExpenditureCategoryId);
|
|
scenarioDetailsService.recalculateResourceAvailability(resource, scenarioRange);
|
|
|
|
if (resource.IsVisible) {
|
|
result[resourceId] = {
|
|
Id: resourceId,
|
|
Name: resource.Name,
|
|
Teams: resource.Teams,
|
|
StartDate: resource.StartDate,
|
|
EndDate: resource.EndDate,
|
|
MinAvailability: resource.MinAvailability,
|
|
MaxAvailability: resource.MaxAvailability,
|
|
AvgAvailability: resource.AvgAvailability,
|
|
};
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
},
|
|
getTeamRowsInProjectUnassigned: function (projectRow, teamId) {
|
|
if (!projectRow || !projectRow.Children || !teamId)
|
|
return null;
|
|
|
|
var foundTeamRows = {};
|
|
|
|
for (var expCatIndex = 0; expCatIndex < projectRow.Children.length; expCatIndex++) {
|
|
var expCatRow = projectRow.Children[expCatIndex];
|
|
|
|
if (expCatRow && expCatRow.Children && expCatRow.Children.length) {
|
|
for (var teamIndex = 0; teamIndex < expCatRow.Children.length; teamIndex++) {
|
|
var teamRow = expCatRow.Children[teamIndex];
|
|
|
|
if ((teamRow.Id == teamId) && expCatRow.Id) {
|
|
foundTeamRows[expCatRow.Id] = {
|
|
ExpCatRow: expCatRow,
|
|
TeamRow: teamRow,
|
|
TeamIndex: teamIndex
|
|
};
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return foundTeamRows;
|
|
},
|
|
formatPeopleResourceOption: function (element) {
|
|
if (!element) {
|
|
return null;
|
|
}
|
|
|
|
var $optionScope = angular.element(element).scope();
|
|
/* $optionScope.resource property is declared in the expression in the view: (resourceId, resource) in row.AvailableResources track by $index */
|
|
if ($optionScope && $optionScope.resource) {
|
|
return scenarioDetailsService.getAssignableResourceOptionHtml($optionScope.resource);
|
|
}
|
|
|
|
return null;
|
|
},
|
|
assignResource: function (filter, header, projectId, sourceExpCatRow, resourceId, rowToAddAssignmentRowTo) {
|
|
if (!filter || !resourceId) {
|
|
return;
|
|
}
|
|
|
|
if (!sourceExpCatRow || !rowToAddAssignmentRowTo || !rowToAddAssignmentRowTo.AvailableResources || !(resourceId in rowToAddAssignmentRowTo.AvailableResources)) {
|
|
return;
|
|
}
|
|
|
|
var resource = rowToAddAssignmentRowTo.AvailableResources[resourceId];
|
|
if (!resource || !resource.teams || !resource.teams.length) {
|
|
return;
|
|
}
|
|
|
|
var project = activityCalendarService.getProjectById(filter, projectId);
|
|
if (!project || !project.ActiveScenario) {
|
|
return;
|
|
}
|
|
|
|
var range = resolveRange(header, project.ActiveScenario.StartDate, project.ActiveScenario.EndDate);
|
|
if (!range || !range.startDate || !range.endDate) {
|
|
return;
|
|
}
|
|
|
|
// assign resource from all teams that fit to the visible scenario period
|
|
var assignedTeams = [];
|
|
for (var i = 0; i < resource.teams.length; i++) {
|
|
var team = resource.teams[i];
|
|
if (team.StartDate <= range.endDate && (!team.EndDate || team.EndDate >= range.startDate)) {
|
|
activityCalendarService.assignResource(filter, project.ProjectId, sourceExpCatRow.Id, team.TeamId, resource.id);
|
|
assignedTeams.push(team.TeamId);
|
|
}
|
|
}
|
|
|
|
// init resource values with zero
|
|
var scenarioRange = header.getWeekEndingsRange(project.ActiveScenario.StartDate, project.ActiveScenario.EndDate);
|
|
for (var i = 0; i < scenarioRange.length; i++) {
|
|
var availableTeam = this.resolveResourceAvailableTeam(scenarioRange[i], resource.teams);
|
|
if (availableTeam) {
|
|
activityCalendarService.changeResourceValue(filter, project.ProjectId, sourceExpCatRow.Id, availableTeam.TeamId, resource.id, scenarioRange[i], 0, false);
|
|
}
|
|
}
|
|
|
|
// Drop off pre-filtered data cache due to recent data changes
|
|
activityCalendarService.invalidatePreFilteredCache(filter);
|
|
|
|
var rowForAssignmentCreated = false;
|
|
var assignedResources = this.getAssignedResources4Project(filter, project, sourceExpCatRow.Id, assignedTeams, resource.id);
|
|
if (assignedResources && (resource.id in assignedResources)) {
|
|
// Create viewModel as Resource Row and fill it with data to display
|
|
var assignedResource = assignedResources[resource.id];
|
|
var assignedResourceModel = this.createViewModel4Resource(assignedResource);
|
|
if (assignedResourceModel) {
|
|
this.fillResourceRowWithData(header, rowToAddAssignmentRowTo, sourceExpCatRow.Id, resource.id, assignedResourceModel, assignedResource, null, range.startDate, range.endDate);
|
|
// for new resource it is not need to pass real hours/resources mode, because it values are filled with 0
|
|
this.toggleGridSource([assignedResourceModel], null);
|
|
|
|
assignedResourceModel.Initialized = true;
|
|
assignedResourceModel.Show = true;
|
|
assignedResourceModel.Level = rowToAddAssignmentRowTo.Level + 1;
|
|
|
|
if (!rowToAddAssignmentRowTo.Children) {
|
|
rowToAddAssignmentRowTo.Children = [];
|
|
}
|
|
|
|
rowToAddAssignmentRowTo.Children.push(assignedResourceModel);
|
|
rowForAssignmentCreated = true;
|
|
}
|
|
}
|
|
|
|
if (rowForAssignmentCreated) {
|
|
// Remove assigned resource from available resources list at the source EC row
|
|
rowToAddAssignmentRowTo.ResourceToAssignId = null;
|
|
delete rowToAddAssignmentRowTo.AvailableResources[resource.id];
|
|
|
|
// set collection to null if it is empty to hide row with control for resource assignment
|
|
if (!Object.keys(rowToAddAssignmentRowTo.AvailableResources).length) {
|
|
rowToAddAssignmentRowTo.AvailableResources = null;
|
|
}
|
|
}
|
|
},
|
|
assignTeam: function (filter, header, projectId, teamId, extendedModel) {
|
|
if (!filter || !header || !projectId || !teamId) {
|
|
return;
|
|
}
|
|
|
|
var project = activityCalendarService.getProjectById(filter, projectId);
|
|
if (!project || !project.ActiveScenario) {
|
|
return;
|
|
}
|
|
|
|
var weekEndings = header.getWeekEndingsRange(project.ActiveScenario.StartDate, project.ActiveScenario.EndDate);
|
|
if (!weekEndings || !weekEndings.length) {
|
|
return;
|
|
}
|
|
|
|
activityCalendarService.assignTeam(filter, projectId, teamId, extendedModel);
|
|
activityCalendarService.createTeamAllocations(filter, projectId, teamId, weekEndings);
|
|
|
|
// Drop off pre-filtered data cache due to recent data changes
|
|
activityCalendarService.invalidatePreFilteredCache(filter);
|
|
},
|
|
checkResourceValue: function (filter, header, projectId, parentRow, resourceId, resRow, isUOMHours, isAvgMode, $index, $data, isActuals) {
|
|
if (!filter || !header || !projectId || !parentRow || !resourceId || !resRow || !resRow.Cells) {
|
|
return 'Error has occurred. Try again later.';
|
|
}
|
|
|
|
var newValue = roundService.roundQuantity($data);
|
|
if (isNaN(newValue))
|
|
newValue = 0;
|
|
|
|
if (newValue < 0) {
|
|
return "Value should not be less than zero";
|
|
}
|
|
|
|
if (newValue == resRow.Cells[$index])
|
|
return false;
|
|
|
|
var week = header.Weeks[$index];
|
|
if (!week) {
|
|
console.error('Incorrect week index: ' + $index);
|
|
return false;
|
|
}
|
|
|
|
var changedCells = [];
|
|
if (week.DataType == Header.DataType.Month) {
|
|
changedCells = this.changeResourceMonthValue(filter, header, resourceId, resRow, projectId, parentRow, isUOMHours, isAvgMode, week.ParentIndex, newValue, isActuals);
|
|
}
|
|
else {
|
|
var changedCell = this.changeResourceWeekValue(filter, header, resourceId, resRow, projectId, parentRow, isUOMHours, isAvgMode, $index, newValue, isActuals);
|
|
if (changedCell) {
|
|
changedCells.push(changedCell);
|
|
}
|
|
}
|
|
|
|
// Drop off pre-filtered data cache due to recent data changes
|
|
activityCalendarService.invalidatePreFilteredCache(filter);
|
|
return changedCells;
|
|
},
|
|
checkTeamValue: function (filter, header, projectId, expCatRow, teamId, teamRow, isUOMHours, isAvgMode, $index, $data) {
|
|
if (!filter || !header || !projectId || !expCatRow || !teamId || !teamRow || !teamRow.Cells) {
|
|
return 'Error has occurred. Try again later.';
|
|
}
|
|
|
|
var newValue = roundService.roundQuantity($data);
|
|
if (isNaN(newValue))
|
|
newValue = 0;
|
|
|
|
if (newValue < 0) {
|
|
return "Value should not be less than zero";
|
|
}
|
|
|
|
if (newValue == teamRow.Cells[$index])
|
|
return false;
|
|
|
|
var week = header.Weeks[$index];
|
|
if (!week) {
|
|
console.error('Incorrect week index: ' + $index);
|
|
return false;
|
|
}
|
|
|
|
var changedCells = [];
|
|
if (week.DataType == Header.DataType.Month) {
|
|
changedCells = this.changeTeamMonthValue(filter, header, teamId, teamRow, projectId, expCatRow, isUOMHours, isAvgMode, week.ParentIndex, newValue);
|
|
}
|
|
else {
|
|
var changedCell = this.changeTeamWeekValue(filter, header, teamId, teamRow, projectId, expCatRow, isUOMHours, isAvgMode, $index, newValue);
|
|
if (changedCell) {
|
|
changedCells.push(changedCell);
|
|
}
|
|
}
|
|
|
|
// Drop off pre-filtered data cache due to recent data changes
|
|
activityCalendarService.invalidatePreFilteredCache(filter);
|
|
return changedCells;
|
|
},
|
|
checkResourceGrandTotalValue: function (filter, header, projectId, startDate, endDate, parentRow, resourceId, resRow, isUOMHours, isAvgMode, $data, isActuals) {
|
|
if (!filter || !header || !projectId || !startDate || !endDate || !parentRow || !resourceId || !resRow) {
|
|
return 'Error has occurred. Try again later.';
|
|
}
|
|
|
|
var newValue = roundService.roundQuantity($data);
|
|
if (isNaN(newValue))
|
|
newValue = 0;
|
|
|
|
if (newValue < 0) {
|
|
return "Value should not be less than zero";
|
|
}
|
|
|
|
if (newValue == resRow.TotalValue)
|
|
return false;
|
|
|
|
var result = this.changeResourceGrandTotalValue(filter, header, resourceId, resRow, projectId, startDate, endDate, parentRow, isUOMHours, isAvgMode, newValue, isActuals);
|
|
|
|
// Drop off pre-filtered data cache due to recent data changes
|
|
activityCalendarService.invalidatePreFilteredCache(filter);
|
|
return result;
|
|
},
|
|
checkTeamGrandTotalValue: function (filter, header, projectId, startDate, endDate, expCatRow, teamId, teamRow, isUOMHours, isAvgMode, $data) {
|
|
if (!filter || !header || !projectId || !startDate || !endDate || !expCatRow || !teamId || !teamRow) {
|
|
return 'Error has occurred. Try again later.';
|
|
}
|
|
|
|
var newValue = roundService.roundQuantity($data);
|
|
if (isNaN(newValue))
|
|
newValue = 0;
|
|
|
|
if (newValue < 0) {
|
|
return "Value should not be less than zero";
|
|
}
|
|
|
|
if (newValue == teamRow.TotalValue)
|
|
return false;
|
|
|
|
var result = this.changeTeamGrandTotalValue(filter, header, teamId, teamRow, projectId, startDate, endDate, expCatRow, isUOMHours, isAvgMode, newValue);
|
|
|
|
// Drop off pre-filtered data cache due to recent data changes
|
|
activityCalendarService.invalidatePreFilteredCache(filter);
|
|
return result;
|
|
},
|
|
checkNptResourceValue: function (filter, header, remCapacityRow, rollupToRows, nptCatRow, nptItemRow, nptResourceRow, isUOMHours, isAvgMode, $index, $data) {
|
|
if (!filter || !header || !nptCatRow || !nptItemRow || !nptResourceRow || !nptResourceRow.Cells) {
|
|
return 'Error has occurred. Try again later.';
|
|
}
|
|
|
|
var newValue = roundService.roundQuantity($data);
|
|
if (isNaN(newValue))
|
|
newValue = 0;
|
|
|
|
if (newValue < 0) {
|
|
return "Value should not be less than zero";
|
|
}
|
|
|
|
if (newValue == nptResourceRow.Cells[$index])
|
|
return false;
|
|
|
|
var week = header.Weeks[$index];
|
|
if (!week) {
|
|
console.error('Incorrect week index: ' + $index);
|
|
return false;
|
|
}
|
|
|
|
var changedCells = [];
|
|
if (week.DataType == Header.DataType.Month) {
|
|
changedCells = this.changeNptResourceMonthValue(filter, header, remCapacityRow, rollupToRows, nptCatRow, nptItemRow, nptResourceRow, isUOMHours, isAvgMode, week.ParentIndex, newValue);
|
|
}
|
|
else {
|
|
var changedCell = this.changeNptResourceWeekValue(filter, header, remCapacityRow, rollupToRows, nptCatRow, nptItemRow, nptResourceRow, isUOMHours, isAvgMode, $index, newValue);
|
|
if (changedCell) {
|
|
changedCells.push(changedCell);
|
|
}
|
|
}
|
|
|
|
// Drop off pre-filtered data cache due to recent data changes
|
|
activityCalendarService.invalidatePreFilteredCache(filter);
|
|
return changedCells;
|
|
},
|
|
checkTeamWideNptValue: function (filter, header, remCapacityRow, rollupToRows, nptCatRow, nptItemRow, isUOMHours, isAvgMode, $index, $data) {
|
|
if (!filter || !header || !nptCatRow || !nptItemRow || !nptItemRow.Cells) {
|
|
return 'Error has occurred. Try again later.';
|
|
}
|
|
|
|
var newValue = roundService.roundQuantity($data);
|
|
if (isNaN(newValue))
|
|
newValue = 0;
|
|
|
|
if (newValue < 0) {
|
|
return "Value should not be less than zero";
|
|
}
|
|
|
|
if (newValue == nptItemRow.Cells[$index])
|
|
return false;
|
|
|
|
var week = header.Weeks[$index];
|
|
if (!week) {
|
|
console.error('Incorrect week index: ' + $index);
|
|
return false;
|
|
}
|
|
|
|
var changedCells = [];
|
|
if (week.DataType == Header.DataType.Month) {
|
|
changedCells = this.changeTeamWideNptMonthValue(filter, header, remCapacityRow, rollupToRows, nptCatRow, nptItemRow, isUOMHours, isAvgMode, week.ParentIndex, newValue);
|
|
}
|
|
else {
|
|
var changedCell = this.changeTeamWideNptWeekValue(filter, header, remCapacityRow, rollupToRows, nptCatRow, nptItemRow, isUOMHours, isAvgMode, $index, newValue);
|
|
if (changedCell) {
|
|
changedCells.push(changedCell);
|
|
}
|
|
}
|
|
|
|
// Drop off pre-filtered data cache due to recent data changes
|
|
activityCalendarService.invalidatePreFilteredCache(filter);
|
|
return changedCells;
|
|
},
|
|
takeRemainingCapacity: function (filter, header, projectId, startDate, endDate, parentRow, resourceId, resRow, isUOMHours, isAvgMode) {
|
|
if (!filter || !header || !projectId || !startDate || !endDate || !parentRow || !resourceId || !resRow) {
|
|
return;
|
|
}
|
|
|
|
var result = this.takeCapacity(filter, header, projectId, startDate, endDate, parentRow, resourceId, resRow, isUOMHours, isAvgMode, true);
|
|
return result;
|
|
},
|
|
takeFullCapacity: function (filter, header, projectId, startDate, endDate, parentRow, resourceId, resRow, isUOMHours, isAvgMode) {
|
|
if (!filter || !header || !projectId || !startDate || !endDate || !parentRow || !resourceId || !resRow) {
|
|
return;
|
|
}
|
|
|
|
var result = this.takeCapacity(filter, header, projectId, startDate, endDate, parentRow, resourceId, resRow, isUOMHours, isAvgMode, false);
|
|
return result;
|
|
},
|
|
takeCapacity: function (filter, header, projectId, startDate, endDate, parentRow, resourceId, resRow, isUOMHours, isAvgMode, isRemaining) {
|
|
if (!filter || !header || !projectId || !startDate || !endDate || !parentRow || !resourceId || !resRow || !resRow.Teams) {
|
|
return;
|
|
}
|
|
|
|
var weeks = header.getWeeks(startDate, endDate);
|
|
if (!weeks || !Object.keys(weeks).length) {
|
|
return;
|
|
}
|
|
//to-do: replace parentRow argument with expCatId and get remaining capacity directly from envisageService
|
|
var expCatId = parentRow.ExpenditureCategoryId || parentRow.Id;
|
|
var resourceTeams = resRow.Teams.map(function (team) { return team.TeamId });
|
|
var resourceSummary = teamInfoService.getResourceSummary(resourceTeams, expCatId, resourceId);
|
|
if (!resourceSummary) {
|
|
return;
|
|
}
|
|
|
|
var changedCells = [];
|
|
for (var weekIndexKey in weeks) {
|
|
var weekIndex = parseInt(weekIndexKey);
|
|
var restCapacity = parentRow.RemainingCapacityValues[weekIndex] || 0;
|
|
var assignedCapacity = resRow.QuantityHoursValues[weekIndex] || 0;
|
|
|
|
// we need to consider resource allocation in the current scenario
|
|
var availableCapacity = (resourceSummary.TotalCapacity ? roundService.roundQuantity(resourceSummary.TotalCapacity[weeks[weekIndex]] || 0) : 0) - assignedCapacity;
|
|
if (isRemaining) {
|
|
var allocatedCapacityInOtherScenarios = (resourceSummary.AllocatedCapacity ? roundService.roundQuantity(resourceSummary.AllocatedCapacity[weeks[weekIndex]] || 0) : 0) - assignedCapacity;
|
|
var assignedNpt = roundService.roundQuantity(teamInfoService.getResourceSummaryNptAllocation(resourceSummary, weeks[weekIndex]));
|
|
availableCapacity = roundService.roundQuantity(availableCapacity - allocatedCapacityInOtherScenarios - assignedNpt);
|
|
}
|
|
|
|
var capacityToAssign = Math.min(Math.max(restCapacity, 0), Math.max(availableCapacity, 0));
|
|
var newValue = capacityToAssign + assignedCapacity;
|
|
|
|
if (!isUOMHours) {
|
|
newValue = hoursResourcesConverter.convertToResources(expCatId, newValue);
|
|
}
|
|
|
|
var changedCell = this.changeResourceWeekValue(filter, header, resourceId, resRow, projectId, parentRow, isUOMHours, isAvgMode, weekIndex, newValue, false);
|
|
if (changedCell) {
|
|
changedCells.push(changedCell);
|
|
}
|
|
}
|
|
|
|
// Drop off pre-filtered data cache due to recent data changes
|
|
activityCalendarService.invalidatePreFilteredCache(filter);
|
|
return changedCells;
|
|
},
|
|
takeTeamFullCapacity: function (filter, header, projectId, startDate, endDate, expCatRow, teamRow, isUOMHours, isAvgMode) {
|
|
if (!filter || !header || !projectId || !startDate || !endDate || !expCatRow || !teamRow) {
|
|
return;
|
|
}
|
|
|
|
var result = this.takeTeamCapacity(filter, header, projectId, startDate, endDate, expCatRow, teamRow, isUOMHours, isAvgMode, false);
|
|
return result;
|
|
},
|
|
takeTeamRemainingCapacity: function (filter, header, projectId, startDate, endDate, expCatRow, teamRow, isUOMHours, isAvgMode) {
|
|
if (!filter || !header || !projectId || !startDate || !endDate || !expCatRow || !teamRow) {
|
|
return;
|
|
}
|
|
|
|
return this.takeTeamCapacity(filter, header, projectId, startDate, endDate, expCatRow, teamRow, isUOMHours, isAvgMode, true);
|
|
},
|
|
takeTeamCapacity: function (filter, header, projectId, startDate, endDate, expCatRow, teamRow, isUOMHours, isAvgMode, isRemaining) {
|
|
if (!filter || !header || !projectId || !startDate || !endDate || !expCatRow || !teamRow) {
|
|
return;
|
|
}
|
|
|
|
var project = activityCalendarService.getProjectById(filter, projectId);
|
|
if (!project || !project.ActiveScenario || !project.ActiveScenario.Id) {
|
|
return;
|
|
}
|
|
var scenarioId = project.ActiveScenario.Id;
|
|
|
|
var weeks = header.getWeeks(startDate, endDate);
|
|
if (!weeks || !Object.keys(weeks).length) {
|
|
return;
|
|
}
|
|
|
|
var weekendingsMs =[];
|
|
for (var wKeyAsIndex in weeks) {
|
|
weekendingsMs.push(weeks[parseInt(wKeyAsIndex)]);
|
|
}
|
|
|
|
var teamId = teamRow.Id;
|
|
var originalExpCatId = expCatRow.Id;
|
|
|
|
// Check the EC is a project role
|
|
var expCatInfo = dataSources.getExpenditureById(originalExpCatId);
|
|
if (!expCatInfo)
|
|
return;
|
|
|
|
var isProjectRole = !expCatInfo.AllowResourceAssignment;
|
|
var expCatInTeam = null;
|
|
|
|
if (!isProjectRole) {
|
|
// For ordinary EC we need to get it's info in current team context to figure up it's capacity
|
|
expCatInTeam = teamInfoService.getExpenditureCategoryInTeam(teamId, originalExpCatId, false);
|
|
if (!expCatInTeam) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Get team allocations for current category (if it is an ordinary category) or for all categories (if it is a project role).
|
|
// Do it for all scenarios
|
|
var expCatId4Allocations = !isProjectRole ? originalExpCatId : undefined;
|
|
var expCatTeamAllocations = activityCalendarService.getTeamAllocationsSummary4Week(filter, [teamId], expCatId4Allocations, weekendingsMs, true);
|
|
|
|
var teamNpt = {};
|
|
if (isRemaining) {
|
|
teamNpt = teamInfoService.getTeamSummaryNptAllocations(teamId, expCatId4Allocations);
|
|
}
|
|
|
|
var nowUtc = DateTimeConverter.getUtcNowMs();
|
|
var changedCells = [];
|
|
|
|
for (var weekIndexKey in weeks) {
|
|
var weekIndex = parseInt(weekIndexKey);
|
|
var weekendingMs = weeks[weekIndex];
|
|
|
|
// Get remaining need for current category (we should try to assign it by maximum)
|
|
var capacityForWe = 0;
|
|
var restCapacity = expCatRow.RemainingCapacityValues[weekIndex] || 0;
|
|
// Get total current team allocations for current scenario for current category plus all project roles
|
|
var allocatedInCurrentScenarioTotal = activityCalendarService.getTeamAllocations4Scenario4Week(filter, scenarioId, [teamId], expCatId4Allocations, weekendingMs, true);
|
|
// Get total current team allocations for current scenario for current category only
|
|
var allocatedInScenarioToOriginalExpCat = activityCalendarService.getTeamAllocations4Scenario4Week(filter, scenarioId, [teamId], originalExpCatId, weekendingMs, false);
|
|
|
|
if (isProjectRole) {
|
|
// For dates in the past we take actuals capacity values, for future - planned
|
|
if (weekendingMs < nowUtc)
|
|
capacityForWe = teamInfoService.getTeamTotalCapacity(teamId, false, weekendingMs);
|
|
else
|
|
capacityForWe = teamInfoService.getTeamTotalCapacity(teamId, true, weekendingMs);
|
|
}
|
|
else {
|
|
if (weekendingMs < nowUtc)
|
|
capacityForWe = expCatInTeam.ActualCapacityValues && (weekendingMs in expCatInTeam.ActualCapacityValues) && angular.isNumber(expCatInTeam.ActualCapacityValues[weekendingMs])
|
|
? (expCatInTeam.ActualCapacityValues[weekendingMs] || 0) : 0;
|
|
else
|
|
capacityForWe = expCatInTeam.PlannedCapacityValues && (weekendingMs in expCatInTeam.PlannedCapacityValues) && angular.isNumber(expCatInTeam.PlannedCapacityValues[weekendingMs])
|
|
? (expCatInTeam.PlannedCapacityValues[weekendingMs] || 0) : 0;
|
|
}
|
|
|
|
var availableCapacity = capacityForWe - allocatedInCurrentScenarioTotal;
|
|
|
|
if (isRemaining) {
|
|
var allocationsInAllScenarios = 0;
|
|
|
|
if (expCatTeamAllocations && expCatTeamAllocations[teamId] && (weekendingMs in expCatTeamAllocations[teamId]) &&
|
|
angular.isNumber(expCatTeamAllocations[teamId][weekendingMs])) {
|
|
allocationsInAllScenarios = Number(expCatTeamAllocations[teamId][weekendingMs]);
|
|
}
|
|
|
|
var allocatedCapacityInOtherScenarios = allocationsInAllScenarios - allocatedInCurrentScenarioTotal;
|
|
var assignedNpt = teamNpt && teamNpt[weeks[weekIndex]] ? teamNpt[weekendingMs] : 0;
|
|
availableCapacity = availableCapacity - allocatedCapacityInOtherScenarios - assignedNpt;
|
|
}
|
|
|
|
var capacityToAssign = Math.min(Math.max(restCapacity, 0), Math.max(availableCapacity, 0));
|
|
var newValue = capacityToAssign + allocatedInScenarioToOriginalExpCat;
|
|
|
|
if (!isUOMHours) {
|
|
newValue = hoursResourcesConverter.convertToResources(originalExpCatId, newValue);
|
|
}
|
|
newValue = roundService.roundQuantity(newValue);
|
|
|
|
var changedCell = this.changeTeamWeekValue(filter, header, teamId, teamRow, projectId, expCatRow, isUOMHours, isAvgMode, weekIndex, newValue, false);
|
|
if (changedCell) {
|
|
changedCells.push(changedCell);
|
|
}
|
|
}
|
|
|
|
// Drop off pre-filtered data cache due to recent data changes
|
|
activityCalendarService.invalidatePreFilteredCache(filter);
|
|
return changedCells;
|
|
},
|
|
zeroTeam: function (filter, header, projectId, startDate, endDate, expCatRow, teamRow, isUOMHours, isAvgMode) {
|
|
if (!filter || !header || !projectId || !startDate || !endDate || !expCatRow || !teamRow) {
|
|
return 'Error has occurred. Try again later.';
|
|
}
|
|
|
|
var result = this.changeTeamGrandTotalValue(filter, header, teamRow.Id, teamRow, projectId, startDate, endDate, expCatRow, isUOMHours, isAvgMode, 0);
|
|
|
|
// Drop off pre-filtered data cache due to recent data changes
|
|
activityCalendarService.invalidatePreFilteredCache(filter);
|
|
return result;
|
|
},
|
|
zeroResource: function (filter, header, projectId, startDate, endDate, parentRow, resourceId, resRow, isUOMHours, isAvgMode) {
|
|
if (!filter || !header || !projectId || !startDate || !endDate || !parentRow || !resourceId || !resRow) {
|
|
return 'Error has occurred. Try again later.';
|
|
}
|
|
|
|
var result = this.changeResourceGrandTotalValue(filter, header, resourceId, resRow, projectId, startDate, endDate, parentRow, isUOMHours, isAvgMode, 0, false);
|
|
|
|
// Drop off pre-filtered data cache due to recent data changes
|
|
activityCalendarService.invalidatePreFilteredCache(filter);
|
|
return result;
|
|
},
|
|
cleanAndRemoveTeam: function (filter, header, projectRow, teamId, startDate, endDate, isUOMHours, isAvgMode) {
|
|
if (!filter || !header || !projectRow || !projectRow.Children || !teamId || !startDate || !endDate) {
|
|
return;
|
|
}
|
|
|
|
// Get team rows inside project row
|
|
var changedCells = {};
|
|
var foundTeamRows = this.getTeamRowsInProjectUnassigned(projectRow, teamId);
|
|
|
|
for (var expCatId in foundTeamRows) {
|
|
var currentTeamRow = foundTeamRows[expCatId].TeamRow;
|
|
var currentExpCatRow = foundTeamRows[expCatId].ExpCatRow;
|
|
var project = activityCalendarService.getProjectById(filter, projectRow.Id);
|
|
|
|
var changedCells4ExpCat = [];
|
|
if (project && project.Teams && project.Teams[teamId] && ((project.Teams[teamId].Type || activityCalendarService.TeamType.Saved) !== activityCalendarService.TeamType.SavedPending)) {
|
|
changedCells4ExpCat = this.changeTeamGrandTotalValue(filter, header, currentTeamRow.Id, currentTeamRow,
|
|
projectRow.Id, startDate, endDate, currentExpCatRow, isUOMHours, isAvgMode, 0);
|
|
}
|
|
else {
|
|
var originalAllocations = activityCalendarService.getTeamAllocationsBackup(filter, project.ActiveScenario.Id, teamId, expCatId);
|
|
if (originalAllocations) {
|
|
var weeks = header.getWeeks(startDate, endDate);
|
|
for (var weekIndex in weeks) {
|
|
if (weeks[weekIndex] in originalAllocations) {
|
|
var originalValue = originalAllocations[weeks[weekIndex]] || 0;
|
|
var cellValue = isUOMHours ? originalValue : hoursResourcesConverter.convertToResources(expCatId, originalValue);
|
|
var changedCell = this.changeTeamWeekValue(filter, header, teamId, currentTeamRow, projectRow.Id, currentExpCatRow, isUOMHours, isAvgMode, weekIndex, cellValue || 0);
|
|
if (changedCell) {
|
|
changedCells4ExpCat.push(changedCell);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
changedCells[expCatId] = {
|
|
ExpCatRow: currentExpCatRow,
|
|
ChangedCells: changedCells4ExpCat
|
|
};
|
|
}
|
|
|
|
this.removeTeam(filter, header, projectRow, teamId);
|
|
return changedCells;
|
|
},
|
|
removeTeam: function (filter, header, projectRow, teamId) {
|
|
if (!filter || !header || !projectRow || !teamId) {
|
|
return;
|
|
}
|
|
|
|
var foundTeamRows = this.getTeamRowsInProjectUnassigned(projectRow, teamId);
|
|
for (var expCatId in foundTeamRows) {
|
|
var teamRow = foundTeamRows[expCatId].TeamRow;
|
|
var expCatRow = foundTeamRows[expCatId].ExpCatRow;
|
|
var teamIndexInExpCatRow = foundTeamRows[expCatId].TeamIndex;
|
|
|
|
if (teamRow.Children && teamRow.Children.length) {
|
|
// Team row has some child assigned resources. Remove them
|
|
// Make a copy for ECrow, because removeResource method changes it
|
|
var expCatRowCopy = angular.copy(expCatRow);
|
|
|
|
for (var resourceIndex = teamRow.Children.length - 1; resourceIndex >= 0; resourceIndex--) {
|
|
var resourceRow = teamRow.Children[resourceIndex];
|
|
this.removeResource(filter, header, projectRow.Id, expCatId, expCatRowCopy, resourceRow.Id, resourceRow, resourceIndex);
|
|
}
|
|
teamRow.Children = [];
|
|
}
|
|
|
|
if (expCatRow.Children && (expCatRow.Children.length > teamIndexInExpCatRow)) {
|
|
expCatRow.Children.splice(teamIndexInExpCatRow, 1);
|
|
}
|
|
}
|
|
|
|
activityCalendarService.removeTeam(filter, projectRow.Id, teamId)
|
|
|
|
// Drop off pre-filtered data cache due to recent data changes
|
|
activityCalendarService.invalidatePreFilteredCache(filter);
|
|
},
|
|
cleanAndRemoveResource: function (filter, header, projectId, startDate, endDate, expCatId, parentRow, resourceId, resRow, isUOMHours, isAvgMode, $index) {
|
|
if (!filter || !header || !projectId || !startDate || !endDate || !expCatId || !parentRow || !resourceId || !resRow) {
|
|
return;
|
|
}
|
|
var changedCells = this.changeResourceGrandTotalValue(filter, header, resourceId, resRow, projectId, startDate, endDate, parentRow, isUOMHours, isAvgMode, 0, false);
|
|
this.removeResource(filter, header, projectId, expCatId, parentRow, resourceId, resRow, $index);
|
|
|
|
return changedCells;
|
|
},
|
|
removeResource: function (filter, header, projectId, expCatId, parentRow, resourceId, resRow, $index) {
|
|
if (!filter || !projectId || !expCatId || !parentRow || !resourceId) {
|
|
return;
|
|
}
|
|
|
|
if (!resRow || !resRow.Teams || !resRow.Teams.length) {
|
|
return;
|
|
}
|
|
|
|
var project = activityCalendarService.getProjectById(filter, projectId);
|
|
if (!project || !project.ActiveScenario) {
|
|
return;
|
|
}
|
|
|
|
var range = resolveRange(header, project.ActiveScenario.StartDate, project.ActiveScenario.EndDate);
|
|
if (!range || !range.startDate || !range.endDate) {
|
|
return;
|
|
}
|
|
|
|
// remove resource from all teams (we do not need to analyze period where team fits)
|
|
var assignedTeams = [];
|
|
for (var i = 0; i < resRow.Teams.length; i++) {
|
|
var team = resRow.Teams[i];
|
|
if (team && team.TeamId) {
|
|
activityCalendarService.removeResource(filter, project.ProjectId, expCatId, team.TeamId, resourceId);
|
|
assignedTeams.push(team.TeamId);
|
|
}
|
|
}
|
|
|
|
// Drop off pre-filtered data cache due to recent data changes
|
|
activityCalendarService.invalidatePreFilteredCache(filter);
|
|
|
|
var availableResources = this.getAvailableResources4Project(filter, header, project, expCatId, assignedTeams, resourceId);
|
|
if (availableResources && (resourceId in availableResources)) {
|
|
var availableResourceModel = this.createViewModel4AvailableResource(availableResources[resourceId]);
|
|
if (availableResourceModel) {
|
|
if (!parentRow.AvailableResources) {
|
|
parentRow.AvailableResources = {};
|
|
}
|
|
parentRow.AvailableResources[availableResourceModel.id] = availableResourceModel;
|
|
}
|
|
}
|
|
if (parentRow.Children && parentRow.Children.length) {
|
|
parentRow.Children.splice($index, 1);
|
|
}
|
|
},
|
|
//todo: replace expCatRow with expCatId
|
|
changeResourceWeekValue: function (filter, header, resourceId, resRow, projectId, parentRow, isUOMHours, isAvgMode, $index, newValue, isActuals) {
|
|
if (!filter || !header || !resourceId || !resRow || !projectId || !parentRow) {
|
|
return null;
|
|
}
|
|
|
|
var week = header.Weeks[$index];
|
|
if (!week || week.DataType != Header.DataType.Week) {
|
|
return null;
|
|
}
|
|
|
|
if (isNaN(parseFloat(newValue))) {
|
|
newValue = 0;
|
|
}
|
|
|
|
var team = this.resolveResourceAvailableTeam(week.Milliseconds, resRow.Teams);
|
|
if (!team || !team.TeamId) {
|
|
return null;
|
|
}
|
|
var expCatId = parentRow.ExpenditureCategoryId || parentRow.Id;
|
|
var hoursValue = roundService.roundQuantity(isUOMHours ? newValue : hoursResourcesConverter.convertToHours(expCatId, newValue));
|
|
var deltaHoursValue = roundService.roundQuantity(hoursValue - (resRow.QuantityHoursValues[$index] || 0));
|
|
var changedCell = {
|
|
TeamId: team.TeamId,
|
|
ExpenditureCategoryId: expCatId,
|
|
ResourceId: resourceId,
|
|
WeekEnding: week.Milliseconds,
|
|
WeekIndex: $index,
|
|
MonthIndex: week.ParentIndex,
|
|
DeltaHoursValue: deltaHoursValue,
|
|
};
|
|
|
|
this.updateResourceValueInViewModel(header, resRow, parentRow, isUOMHours, isAvgMode, deltaHoursValue, $index);
|
|
activityCalendarService.changeResourceValue(filter, projectId, changedCell.ExpenditureCategoryId, changedCell.TeamId, changedCell.ResourceId, changedCell.WeekEnding, hoursValue, isActuals);
|
|
|
|
return changedCell;
|
|
},
|
|
changeTeamWeekValue: function (filter, header, teamId, teamRow, projectId, expCatRow, isUOMHours, isAvgMode, $index, newValue) {
|
|
if (!filter || !header || !teamId || !teamRow || !projectId || !expCatRow) {
|
|
return null;
|
|
}
|
|
|
|
var week = header.Weeks[$index];
|
|
if (!week || week.DataType != Header.DataType.Week) {
|
|
return null;
|
|
}
|
|
|
|
if (isNaN(parseFloat(newValue))) {
|
|
newValue = 0;
|
|
}
|
|
|
|
// Get categories count in team (before changes) to determine, if category was added to team
|
|
var expCatsInTeamCountPrev = teamInfoService.getExpenditureCategoriesInTeamCount(teamId, true);
|
|
|
|
var hoursValue = roundService.roundQuantity(isUOMHours ? newValue : hoursResourcesConverter.convertToHours(expCatRow.Id, newValue));
|
|
var oldValue = (teamRow.QuantityHoursValues[$index] || 0);
|
|
var deltaHoursValue = roundService.roundQuantity(hoursValue - (teamRow.QuantityHoursValues[$index] || 0));
|
|
var changedCell = {
|
|
TeamId: teamId,
|
|
ExpenditureCategoryId: expCatRow.Id,
|
|
WeekEnding: week.Milliseconds,
|
|
WeekIndex: $index,
|
|
MonthIndex: week.ParentIndex,
|
|
DeltaHoursValue: deltaHoursValue,
|
|
OriginalValue: oldValue
|
|
};
|
|
|
|
this.updateTeamValueInViewModel(header, teamRow, expCatRow, isUOMHours, isAvgMode, deltaHoursValue, $index);
|
|
activityCalendarService.changeTeamValue(filter, projectId, changedCell.ExpenditureCategoryId, changedCell.TeamId, changedCell.WeekEnding, hoursValue);
|
|
|
|
// Get categories count in team (after changes)
|
|
var expCatsInTeamCountAfter = teamInfoService.getExpenditureCategoriesInTeamCount(teamId, true);
|
|
if (expCatsInTeamCountPrev != expCatsInTeamCountAfter) {
|
|
changedCell.Added = true;
|
|
}
|
|
|
|
return changedCell;
|
|
},
|
|
changeResourceMonthValue: function (filter, header, resourceId, resRow, projectId, parentRow, isUOMHours, isAvgMode, monthIndex, newValue, isActuals) {
|
|
if (!filter || !header || !resourceId || !resRow || !projectId || !parentRow) {
|
|
return null;
|
|
}
|
|
|
|
var month = header.Months[monthIndex];
|
|
if (!month || !month.Childs) {
|
|
return null;
|
|
}
|
|
|
|
newValue = parseFloat(newValue) || 0;
|
|
if (isAvgMode) {
|
|
newValue *= month.Childs.length;
|
|
}
|
|
|
|
return this.alignTotalValue(filter, header, resourceId, resRow, projectId, parentRow, isUOMHours, isAvgMode, month.Childs, newValue, isActuals, this.changeResourceWeekValue);
|
|
},
|
|
changeTeamMonthValue: function (filter, header, teamId, teamRow, projectId, expCatRow, isUOMHours, isAvgMode, monthIndex, newValue) {
|
|
if (!filter || !header || !teamId || !teamRow || !projectId || !expCatRow) {
|
|
return null;
|
|
}
|
|
|
|
var month = header.Months[monthIndex];
|
|
if (!month || !month.Childs) {
|
|
return null;
|
|
}
|
|
|
|
newValue = parseFloat(newValue) || 0;
|
|
if (isAvgMode) {
|
|
newValue *= month.Childs.length;
|
|
}
|
|
|
|
return this.alignTotalValue(filter, header, teamId, teamRow, projectId, expCatRow, isUOMHours, isAvgMode, month.Childs, newValue, null, this.changeTeamWeekValue);
|
|
},
|
|
changeNptResourceWeekValue: function (filter, header, remCapacityRow, rollupToRows, nptCatRow, nptItemRow, nptResourceRow, isUOMHours, isAvgMode, $index, newValue) {
|
|
if (!filter || !header || !nptCatRow || !nptItemRow || !nptResourceRow) {
|
|
return null;
|
|
}
|
|
|
|
var week = header.Weeks[$index];
|
|
if (!week || week.DataType != Header.DataType.Week) {
|
|
return null;
|
|
}
|
|
|
|
if (isNaN(parseFloat(newValue))) {
|
|
newValue = 0;
|
|
}
|
|
|
|
var hoursValue = roundService.roundQuantity(isUOMHours ? newValue : hoursResourcesConverter.convertToHours(nptResourceRow.ExpenditureCategoryId, newValue));
|
|
var deltaHoursValue = roundService.roundQuantity(hoursValue - (nptResourceRow.QuantityHoursValues[$index] || 0));
|
|
var changedCell = {
|
|
NonProjectTimeCategoryId: nptCatRow.Id,
|
|
NonProjectTimeId: nptItemRow.Id,
|
|
ResourceId: nptResourceRow.Id,
|
|
WeekEnding: week.Milliseconds,
|
|
};
|
|
|
|
activityCalendarService.changeNptResourceValue(filter, nptItemRow.Id, nptResourceRow.Id, nptResourceRow.ExpenditureCategoryId, week.Milliseconds, hoursValue);
|
|
this.updateNptResourceValueInViewModel(header, nptResourceRow, rollupToRows, remCapacityRow, isUOMHours, isAvgMode, deltaHoursValue, $index);
|
|
|
|
return changedCell;
|
|
},
|
|
changeNptResourceMonthValue: function (filter, header, remCapacityRow, rollupToRows, nptCatRow, nptItemRow, nptResourceRow, isUOMHours, isAvgMode, monthIndex, newValue) {
|
|
if (!filter || !header || !nptCatRow || !nptItemRow || !nptResourceRow) {
|
|
return null;
|
|
}
|
|
|
|
var month = header.Months[monthIndex];
|
|
if (!month || !month.Childs) {
|
|
return null;
|
|
}
|
|
|
|
newValue = parseFloat(newValue) || 0;
|
|
if (isAvgMode) {
|
|
newValue *= month.Childs.length;
|
|
}
|
|
|
|
return this.alignNptResourceTotalValue(filter, header, remCapacityRow, rollupToRows, nptCatRow, nptItemRow, nptResourceRow, isUOMHours, isAvgMode, month.Childs, newValue);
|
|
},
|
|
changeTeamWideNptWeekValue: function (filter, header, remCapacityRow, rollupToRows, nptCatRow, nptItemRow, isUOMHours, isAvgMode, $index, newValue) {
|
|
if (!filter || !header || !nptCatRow || !nptItemRow || !nptItemRow.Resources ||
|
|
(nptItemRow.Resources.length < 1)) {
|
|
return null;
|
|
}
|
|
|
|
var week = header.Weeks[$index];
|
|
if (!week || week.DataType != Header.DataType.Week) {
|
|
return null;
|
|
}
|
|
|
|
if (isNaN(parseFloat(newValue))) {
|
|
newValue = 0;
|
|
}
|
|
|
|
// Get current allocations for NPT-resources and calculate total delta in hours and resources
|
|
var currentAllocations = activityCalendarService.getNonProjectTimeResourceAllocations(nptItemRow.Id, week.Milliseconds);
|
|
var requiredResources = nptItemRow.Resources.map(function (e) { return e.Id; });
|
|
var resourcesMissed = !Object.keysInObject(requiredResources, currentAllocations);
|
|
if (resourcesMissed) {
|
|
throw "Unable to update team-wide non-project time allocations: resource not found";
|
|
}
|
|
|
|
var keys = Object.keys(currentAllocations);
|
|
var values = Object.values(currentAllocations);
|
|
var alignedValues = calculateDistributionService.alignValues(values, null, newValue);
|
|
|
|
var deltaHoursTotal = 0;
|
|
var deltaResourcesTotal = 0;
|
|
|
|
for (var index = 0; index < nptItemRow.Resources.length; index++) {
|
|
var resource = nptItemRow.Resources[index];
|
|
var resourceValueIndex = keys.indexOf(resource.Id);
|
|
var resourceValue = alignedValues[resourceValueIndex];
|
|
var oldValue = currentAllocations[resource.Id] || 0;
|
|
var hoursValue = roundService.roundQuantity(isUOMHours ? resourceValue : hoursResourcesConverter.convertToHours(resource.ExpenditureCategoryId, resourceValue));
|
|
var deltaHours = roundService.roundQuantity(hoursValue - oldValue);
|
|
var deltaResources = roundService.roundQuantity(hoursResourcesConverter.convertToResources(resource.ExpenditureCategoryId, deltaHours));
|
|
|
|
deltaHoursTotal += deltaHours;
|
|
deltaResourcesTotal += deltaResources;
|
|
|
|
activityCalendarService.changeTeamWideNptValue(filter, nptItemRow.Id, resource.Id, resource.ExpenditureCategoryId, week.Milliseconds, hoursValue);
|
|
}
|
|
|
|
// final rounding of total values
|
|
deltaHoursTotal = roundService.roundQuantity(deltaHoursTotal);
|
|
deltaResourcesTotal = roundService.roundQuantity(deltaResourcesTotal);
|
|
|
|
this.updateTeamWideNptValuesInViewModel(header, remCapacityRow, rollupToRows, deltaHoursTotal, deltaResourcesTotal, isUOMHours, isAvgMode, $index);
|
|
|
|
var changedCell = {
|
|
NonProjectTimeCategoryId: nptCatRow.Id,
|
|
NonProjectTimeId: nptItemRow.Id,
|
|
WeekEnding: week.Milliseconds,
|
|
};
|
|
return changedCell;
|
|
},
|
|
changeTeamWideNptMonthValue: function (filter, header, remCapacityRow, rollupToRows, nptCatRow, nptItemRow, isUOMHours, isAvgMode, monthIndex, newValue) {
|
|
if (!filter || !header || !nptCatRow || !nptItemRow) {
|
|
return null;
|
|
}
|
|
|
|
var month = header.Months[monthIndex];
|
|
if (!month || !month.Childs) {
|
|
return null;
|
|
}
|
|
|
|
newValue = parseFloat(newValue) || 0;
|
|
if (isAvgMode) {
|
|
newValue *= month.Childs.length;
|
|
}
|
|
|
|
return this.alignTeamWideNptTotalValue(filter, header, remCapacityRow, rollupToRows, nptCatRow, nptItemRow, isUOMHours, isAvgMode, month.Childs, newValue);
|
|
},
|
|
// todo: replace expCatRow with expCatId and do not update RemainingCapacity in expCatRow because it should be calculated on the fly from DAL
|
|
changeResourceGrandTotalValue: function (filter, header, resourceId, resRow, projectId, startDate, endDate, parentRow, isUOMHours, isAvgMode, newValue, isActuals) {
|
|
if (!filter || !header || !resourceId || !resRow || !projectId || !parentRow) {
|
|
return;
|
|
}
|
|
|
|
var weeks = header.getWeeks(startDate, endDate);
|
|
if (!weeks) {
|
|
return;
|
|
}
|
|
var weekIndexes = Object.keys(weeks);
|
|
|
|
newValue = parseFloat(newValue) || 0;
|
|
if (isAvgMode) {
|
|
newValue *= weekIndexes.length;
|
|
}
|
|
|
|
return this.alignTotalValue(filter, header, resourceId, resRow, projectId, parentRow, isUOMHours, isAvgMode, weekIndexes, newValue, isActuals, this.changeResourceWeekValue);
|
|
},
|
|
changeTeamGrandTotalValue: function (filter, header, teamId, teamRow, projectId, startDate, endDate, expCatRow, isUOMHours, isAvgMode, newValue) {
|
|
if (!filter || !header || !teamId || !teamRow || !projectId || !expCatRow) {
|
|
return;
|
|
}
|
|
|
|
var weeks = header.getWeeks(startDate, endDate);
|
|
if (!weeks) {
|
|
return;
|
|
}
|
|
var weekIndexes = Object.keys(weeks);
|
|
|
|
newValue = parseFloat(newValue) || 0;
|
|
if (isAvgMode) {
|
|
newValue *= weekIndexes.length;
|
|
}
|
|
|
|
return this.alignTotalValue(filter, header, teamId, teamRow, projectId, expCatRow, isUOMHours, isAvgMode, weekIndexes, newValue, null, this.changeTeamWeekValue);
|
|
},
|
|
/// changeWeekHandler - service method to handle weekly change (changeResourceWeekValue or changeTeamWeekValue)
|
|
alignResourceTotalValue: function (filter, header, resourceId, resRow, projectId, expCatRow, isUOMHours, isAvgMode, weeks, newValue, isActuals) {
|
|
if (!filter || !header || !resourceId || !resRow || !projectId || !expCatRow || !weeks || !weeks.length) {
|
|
return null;
|
|
}
|
|
|
|
var sortedWeeks = resRow.sortWeeks(weeks);
|
|
if (!sortedWeeks || !sortedWeeks.Editable || !sortedWeeks.Editable.length) {
|
|
return null;
|
|
}
|
|
|
|
var readOnlyTotal = resRow.calculateResourceTotalOnRange(sortedWeeks.ReadOnly);
|
|
var newTotal = roundService.roundQuantity(Math.max((parseFloat(newValue) || 0) - readOnlyTotal, 0));
|
|
|
|
var changedCells = [];
|
|
var alignedValues = calculateDistributionService.alignValues(resRow.Cells, sortedWeeks.Editable, newTotal);
|
|
if (alignedValues) {
|
|
for (var key in alignedValues) {
|
|
var weekIndex = parseInt(key);
|
|
var weekValue = alignedValues[weekIndex] || 0;
|
|
// to-do: change expCatRow to expCatId
|
|
var changedCell = this.changeResourceWeekValue(filter, header, resourceId, resRow, projectId, expCatRow, isUOMHours, isAvgMode, weekIndex, weekValue, isActuals);
|
|
if (changedCell) {
|
|
changedCells.push(changedCell);
|
|
}
|
|
}
|
|
}
|
|
|
|
return changedCells;
|
|
},
|
|
alignTotalValue: function (filter, header, rowId, row, projectId, parentRow, isUOMHours, isAvgMode, weeks, newValue, isActuals, changeWeekHandler) {
|
|
if (!filter || !header || !rowId || !row || !projectId || !parentRow || !weeks || !weeks.length || 'function' !== typeof (changeWeekHandler)) {
|
|
return null;
|
|
}
|
|
|
|
var sortedWeeks = row.sortWeeks(weeks);
|
|
if (!sortedWeeks || !sortedWeeks.Editable || !sortedWeeks.Editable.length) {
|
|
return null;
|
|
}
|
|
|
|
var readOnlyTotal = row.calculateResourceTotalOnRange(sortedWeeks.ReadOnly);
|
|
var newTotal = roundService.roundQuantity(Math.max((parseFloat(newValue) || 0) - readOnlyTotal, 0));
|
|
|
|
var changedCells = [];
|
|
var alignedValues = calculateDistributionService.alignValues(row.Cells, sortedWeeks.Editable, newTotal);
|
|
if (alignedValues) {
|
|
for (var key in alignedValues) {
|
|
var weekIndex = parseInt(key);
|
|
var weekValue = alignedValues[weekIndex] || 0;
|
|
|
|
var changedCell = changeWeekHandler.apply(this, [filter, header, rowId, row, projectId, parentRow, isUOMHours, isAvgMode, weekIndex, weekValue, isActuals]);
|
|
if (changedCell) {
|
|
changedCells.push(changedCell);
|
|
}
|
|
}
|
|
}
|
|
|
|
return changedCells;
|
|
},
|
|
alignNptResourceTotalValue: function (filter, header, remCapacityRow, rollupToRows, nptCatRow, nptItemRow, nptResourceRow, isUOMHours, isAvgMode, weeks, newValue) {
|
|
if (!filter || !header || !nptCatRow || !nptItemRow || !nptResourceRow || !weeks || !weeks.length) {
|
|
return null;
|
|
}
|
|
|
|
var sortedWeeks = nptResourceRow.sortWeeks(weeks);
|
|
if (!sortedWeeks || !sortedWeeks.Editable || !sortedWeeks.Editable.length) {
|
|
return null;
|
|
}
|
|
|
|
var readOnlyTotal = nptResourceRow.calculateResourceTotalOnRange(sortedWeeks.ReadOnly);
|
|
var newTotal = roundService.roundQuantity(Math.max((parseFloat(newValue) || 0) - readOnlyTotal, 0));
|
|
|
|
var changedCells = [];
|
|
var alignedValues = calculateDistributionService.alignValues(nptResourceRow.Cells, sortedWeeks.Editable, newTotal);
|
|
if (alignedValues) {
|
|
for (var key in alignedValues) {
|
|
var weekIndex = parseInt(key);
|
|
var weekValue = alignedValues[weekIndex] || 0;
|
|
|
|
var changedCell = this.changeNptResourceWeekValue(filter, header, remCapacityRow, rollupToRows, nptCatRow, nptItemRow, nptResourceRow, isUOMHours, isAvgMode, weekIndex, weekValue);
|
|
if (changedCell) {
|
|
changedCells.push(changedCell);
|
|
}
|
|
}
|
|
}
|
|
|
|
return changedCells;
|
|
},
|
|
alignTeamWideNptTotalValue: function (filter, header, remCapacityRow, rollupToRows, nptCatRow, nptItemRow, isUOMHours, isAvgMode, weeks, newValue) {
|
|
if (!filter || !header || !nptCatRow || !nptItemRow || !weeks || !weeks.length) {
|
|
return null;
|
|
}
|
|
|
|
var sortedWeeks = nptItemRow.sortWeeks(weeks);
|
|
if (!sortedWeeks || !sortedWeeks.Editable || !sortedWeeks.Editable.length) {
|
|
return null;
|
|
}
|
|
|
|
var readOnlyTotal = nptItemRow.calculateResourceTotalOnRange(sortedWeeks.ReadOnly);
|
|
var newTotal = roundService.roundQuantity(Math.max((parseFloat(newValue) || 0) - readOnlyTotal, 0));
|
|
|
|
var changedCells = [];
|
|
var alignedValues = calculateDistributionService.alignValues(nptItemRow.Cells, sortedWeeks.Editable, newTotal);
|
|
if (alignedValues) {
|
|
for (var key in alignedValues) {
|
|
var weekIndex = parseInt(key);
|
|
var weekValue = alignedValues[weekIndex] || 0;
|
|
|
|
var changedCell = this.changeTeamWideNptWeekValue(filter, header, remCapacityRow, rollupToRows, nptCatRow, nptItemRow, isUOMHours, isAvgMode, weekIndex, weekValue);
|
|
if (changedCell) {
|
|
changedCells.push(changedCell);
|
|
}
|
|
}
|
|
}
|
|
|
|
return changedCells;
|
|
},
|
|
// todo: replace expCatRow with expCatId
|
|
updateResourceValueInViewModel: function (header, resRow, parentRow, isUOMHours, isAvgMode, deltaHours, colIndex) {
|
|
if (!header || !resRow || !parentRow) {
|
|
return;
|
|
}
|
|
|
|
if (isNaN(colIndex = parseInt(colIndex)) || colIndex < 0) {
|
|
return;
|
|
}
|
|
|
|
var week = header.Weeks[colIndex],
|
|
month = header.Months[week.ParentIndex],
|
|
monthIndex = month.SelfIndexInWeeks,
|
|
monthWeeksCount = month.Childs.length;
|
|
|
|
// Caution: we should round result of operation because of known javascript math problem: http://www.webdeveloper.com/forum/showthread.php?92612-Basic-(-)-Javascript-math-precision-problem
|
|
// Example: 4.615385 + 6.923077 + 6.923077 + 6.923077 + 4.615384 = 29.999999999999996, but should be 30
|
|
var expCatId = parentRow.ExpenditureCategoryId || parentRow.Id;
|
|
resRow.QuantityHoursValues[colIndex] = roundService.roundQuantity(resRow.QuantityHoursValues[colIndex] + (deltaHours || 0));
|
|
resRow.QuantityHoursValues[monthIndex] = roundService.roundQuantity(resRow.QuantityHoursValues[monthIndex] + (deltaHours || 0));
|
|
resRow.TotalHoursValue = roundService.roundQuantity(resRow.TotalHoursValue + (deltaHours || 0));
|
|
resRow.QuantityResourcesValues[colIndex] = roundService.roundQuantity(hoursResourcesConverter.convertToResources(expCatId, resRow.QuantityHoursValues[colIndex]));
|
|
resRow.QuantityResourcesValues[monthIndex] = roundService.roundQuantity(hoursResourcesConverter.convertToResources(expCatId, resRow.QuantityHoursValues[monthIndex]));
|
|
resRow.TotalResourcesValue = roundService.roundQuantity(hoursResourcesConverter.convertToResources(expCatId, resRow.TotalHoursValue));
|
|
|
|
if (parentRow.RemainingCapacityValues) {
|
|
parentRow.RemainingCapacityValues[colIndex] = roundService.roundQuantity(parentRow.RemainingCapacityValues[colIndex] - (deltaHours || 0));
|
|
parentRow.RemainingCapacityValues[monthIndex] = roundService.roundQuantity(parentRow.RemainingCapacityValues[monthIndex] - (deltaHours || 0));
|
|
}
|
|
|
|
if (isUOMHours) {
|
|
resRow.Cells[colIndex] = resRow.QuantityHoursValues[colIndex];
|
|
resRow.Cells[monthIndex] = resRow.QuantityHoursValues[monthIndex];
|
|
resRow.TotalValue = resRow.TotalHoursValue;
|
|
}
|
|
else {
|
|
resRow.Cells[colIndex] = resRow.QuantityResourcesValues[colIndex];
|
|
resRow.Cells[monthIndex] = isAvgMode ? roundService.roundQuantity(resRow.QuantityResourcesValues[monthIndex] / monthWeeksCount) : resRow.QuantityResourcesValues[monthIndex];
|
|
resRow.TotalValue = isAvgMode ? roundService.roundQuantity(resRow.TotalResourcesValue / resRow.VisibleCellsCount) : resRow.TotalResourcesValue;
|
|
}
|
|
},
|
|
updateTeamValueInViewModel: function (header, teamRow, expCatRow, isUOMHours, isAvgMode, deltaHours, colIndex) {
|
|
if (!header || !teamRow || !expCatRow) {
|
|
return;
|
|
}
|
|
|
|
if (isNaN(colIndex = parseInt(colIndex)) || colIndex < 0) {
|
|
return;
|
|
}
|
|
|
|
var week = header.Weeks[colIndex],
|
|
month = header.Months[week.ParentIndex],
|
|
monthIndex = month.SelfIndexInWeeks,
|
|
monthWeeksCount = month.Childs.length;
|
|
|
|
teamRow.QuantityHoursValues[colIndex] = roundService.roundQuantity(teamRow.QuantityHoursValues[colIndex] + (deltaHours || 0));
|
|
teamRow.QuantityHoursValues[monthIndex] = roundService.roundQuantity(teamRow.QuantityHoursValues[monthIndex] + (deltaHours || 0));
|
|
teamRow.TotalHoursValue = roundService.roundQuantity(teamRow.TotalHoursValue + (deltaHours || 0));
|
|
teamRow.QuantityResourcesValues[colIndex] = roundService.roundQuantity(hoursResourcesConverter.convertToResources(expCatRow.Id, teamRow.QuantityHoursValues[colIndex]));
|
|
teamRow.QuantityResourcesValues[monthIndex] = roundService.roundQuantity(hoursResourcesConverter.convertToResources(expCatRow.Id, teamRow.QuantityHoursValues[monthIndex]));
|
|
teamRow.TotalResourcesValue = roundService.roundQuantity(hoursResourcesConverter.convertToResources(expCatRow.Id, teamRow.TotalHoursValue));
|
|
|
|
if (expCatRow.RemainingCapacityValues) {
|
|
expCatRow.RemainingCapacityValues[colIndex] = roundService.roundQuantity(expCatRow.RemainingCapacityValues[colIndex] - (deltaHours || 0));
|
|
expCatRow.RemainingCapacityValues[monthIndex] = roundService.roundQuantity(expCatRow.RemainingCapacityValues[monthIndex] - (deltaHours || 0));
|
|
}
|
|
|
|
if (isUOMHours) {
|
|
teamRow.Cells[colIndex] = teamRow.QuantityHoursValues[colIndex];
|
|
teamRow.Cells[monthIndex] = teamRow.QuantityHoursValues[monthIndex];
|
|
teamRow.TotalValue = teamRow.TotalHoursValue;
|
|
}
|
|
else {
|
|
teamRow.Cells[colIndex] = teamRow.QuantityResourcesValues[colIndex];
|
|
teamRow.Cells[monthIndex] = isAvgMode ? roundService.roundQuantity(teamRow.QuantityResourcesValues[monthIndex] / monthWeeksCount) : teamRow.QuantityResourcesValues[monthIndex];
|
|
teamRow.TotalValue = isAvgMode ? roundService.roundQuantity(teamRow.TotalResourcesValue / teamRow.VisibleCellsCount) : teamRow.TotalResourcesValue;
|
|
}
|
|
},
|
|
updateNptResourceValueInViewModel: function (header, nptResourceRow, rollupToRows, remCapacityRow, isUOMHours, isAvgMode, deltaHours, colIndex) {
|
|
if (!header || !nptResourceRow) {
|
|
return;
|
|
}
|
|
|
|
if (isNaN(colIndex = parseInt(colIndex)) || colIndex < 0) {
|
|
return;
|
|
}
|
|
|
|
var deltaResources = hoursResourcesConverter.convertToResources(nptResourceRow.ExpenditureCategoryId, deltaHours) || 0;
|
|
var week = header.Weeks[colIndex],
|
|
month = header.Months[week.ParentIndex],
|
|
monthIndex = month.SelfIndexInWeeks,
|
|
monthWeeksCount = month.Childs.length;
|
|
|
|
// Caution: we should round result of operation because of known javascript math problem: http://www.webdeveloper.com/forum/showthread.php?92612-Basic-(-)-Javascript-math-precision-problem
|
|
// Example: 4.615385 + 6.923077 + 6.923077 + 6.923077 + 4.615384 = 29.999999999999996, but should be 30
|
|
|
|
// Update of NPT resource row
|
|
nptResourceRow.QuantityHoursValues[colIndex] = roundService.roundQuantity(nptResourceRow.QuantityHoursValues[colIndex] + (deltaHours || 0));
|
|
nptResourceRow.QuantityHoursValues[monthIndex] = roundService.roundQuantity(nptResourceRow.QuantityHoursValues[monthIndex] + (deltaHours || 0));
|
|
nptResourceRow.TotalHoursValue = roundService.roundQuantity(nptResourceRow.TotalHoursValue + (deltaHours || 0));
|
|
nptResourceRow.QuantityResourcesValues[colIndex] = roundService.roundQuantity(nptResourceRow.QuantityResourcesValues[colIndex] + deltaResources);
|
|
nptResourceRow.QuantityResourcesValues[monthIndex] = roundService.roundQuantity(nptResourceRow.QuantityResourcesValues[monthIndex] + deltaResources);
|
|
nptResourceRow.TotalResourcesValue = roundService.roundQuantity(nptResourceRow.TotalResourcesValue + deltaResources);
|
|
|
|
if (rollupToRows && rollupToRows.length) {
|
|
for (var index = 0; index < rollupToRows.length; index++) {
|
|
var rollupRow = rollupToRows[index];
|
|
rollupRow.QuantityHoursValues[colIndex] = roundService.roundQuantity(rollupRow.QuantityHoursValues[colIndex] + (deltaHours || 0));
|
|
rollupRow.QuantityHoursValues[monthIndex] = roundService.roundQuantity(rollupRow.QuantityHoursValues[monthIndex] + (deltaHours || 0));
|
|
rollupRow.TotalHoursValue = roundService.roundQuantity(rollupRow.TotalHoursValue + (deltaHours || 0));
|
|
rollupRow.QuantityResourcesValues[colIndex] = roundService.roundQuantity(rollupRow.QuantityResourcesValues[colIndex] + deltaResources);
|
|
rollupRow.QuantityResourcesValues[monthIndex] = roundService.roundQuantity(rollupRow.QuantityResourcesValues[monthIndex] + deltaResources);
|
|
rollupRow.TotalResourcesValue = roundService.roundQuantity(rollupRow.TotalResourcesValue + deltaResources);
|
|
}
|
|
}
|
|
|
|
if (remCapacityRow) {
|
|
// Update of Remaining Capacity row
|
|
remCapacityRow.QuantityHoursValues[colIndex] = roundService.roundQuantity(remCapacityRow.QuantityHoursValues[colIndex] - (deltaHours || 0));
|
|
remCapacityRow.QuantityHoursValues[monthIndex] = roundService.roundQuantity(remCapacityRow.QuantityHoursValues[monthIndex] - (deltaHours || 0));
|
|
remCapacityRow.TotalHoursValue = roundService.roundQuantity(remCapacityRow.TotalHoursValue - (deltaHours || 0));
|
|
remCapacityRow.QuantityResourcesValues[colIndex] = roundService.roundQuantity(remCapacityRow.QuantityResourcesValues[colIndex] - deltaResources);
|
|
remCapacityRow.QuantityResourcesValues[monthIndex] = roundService.roundQuantity(remCapacityRow.QuantityResourcesValues[monthIndex] - deltaResources);
|
|
remCapacityRow.TotalResourcesValue = roundService.roundQuantity(remCapacityRow.TotalResourcesValue - deltaResources);
|
|
}
|
|
|
|
if (isUOMHours) {
|
|
nptResourceRow.Cells[colIndex] = nptResourceRow.QuantityHoursValues[colIndex];
|
|
nptResourceRow.Cells[monthIndex] = nptResourceRow.QuantityHoursValues[monthIndex];
|
|
nptResourceRow.TotalValue = nptResourceRow.TotalHoursValue;
|
|
|
|
if (rollupToRows && rollupToRows.length) {
|
|
for (var index = 0; index < rollupToRows.length; index++) {
|
|
var rollupRow = rollupToRows[index];
|
|
rollupRow.Cells[colIndex] = rollupRow.QuantityHoursValues[colIndex];
|
|
rollupRow.Cells[monthIndex] = rollupRow.QuantityHoursValues[monthIndex];
|
|
rollupRow.TotalValue = rollupRow.TotalHoursValue;
|
|
}
|
|
}
|
|
|
|
if (remCapacityRow) {
|
|
remCapacityRow.Cells[colIndex] = remCapacityRow.QuantityHoursValues[colIndex];
|
|
remCapacityRow.Cells[monthIndex] = remCapacityRow.QuantityHoursValues[monthIndex];
|
|
remCapacityRow.TotalValue = remCapacityRow.TotalHoursValue;
|
|
}
|
|
}
|
|
else {
|
|
nptResourceRow.Cells[colIndex] = nptResourceRow.QuantityResourcesValues[colIndex];
|
|
nptResourceRow.Cells[monthIndex] = isAvgMode ? roundService.roundQuantity(nptResourceRow.QuantityResourcesValues[monthIndex] / monthWeeksCount) : nptResourceRow.QuantityResourcesValues[monthIndex];
|
|
nptResourceRow.TotalValue = isAvgMode ? roundService.roundQuantity(nptResourceRow.TotalResourcesValue / nptResourceRow.VisibleCellsCount) : nptResourceRow.TotalResourcesValue;
|
|
|
|
if (rollupToRows && rollupToRows.length) {
|
|
for (var index = 0; index < rollupToRows.length; index++) {
|
|
var rollupRow = rollupToRows[index];
|
|
rollupRow.Cells[colIndex] = rollupRow.QuantityResourcesValues[colIndex];
|
|
rollupRow.Cells[monthIndex] = isAvgMode ? roundService.roundQuantity(rollupRow.QuantityResourcesValues[monthIndex] / monthWeeksCount) : rollupRow.QuantityResourcesValues[monthIndex];
|
|
rollupRow.TotalValue = isAvgMode ? roundService.roundQuantity(rollupRow.TotalResourcesValue / rollupRow.VisibleCellsCount) : rollupRow.TotalResourcesValue;
|
|
}
|
|
}
|
|
|
|
if (remCapacityRow) {
|
|
remCapacityRow.Cells[colIndex] = remCapacityRow.QuantityResourcesValues[colIndex];
|
|
remCapacityRow.Cells[monthIndex] = isAvgMode ? roundService.roundQuantity(remCapacityRow.QuantityResourcesValues[monthIndex] / monthWeeksCount) : remCapacityRow.QuantityResourcesValues[monthIndex];
|
|
remCapacityRow.TotalValue = isAvgMode ? roundService.roundQuantity(remCapacityRow.TotalResourcesValue / remCapacityRow.VisibleCellsCount) : remCapacityRow.TotalResourcesValue;
|
|
}
|
|
}
|
|
},
|
|
updateTeamWideNptValuesInViewModel: function (header, remCapacityRow, rowsToUpdate, deltaHours, deltaResources, isUOMHours, isAvgMode, colIndex) {
|
|
if (!header || !rowsToUpdate || !rowsToUpdate.length) {
|
|
return;
|
|
}
|
|
|
|
if (isNaN(colIndex = parseInt(colIndex)) || colIndex < 0) {
|
|
return;
|
|
}
|
|
|
|
var week = header.Weeks[colIndex],
|
|
month = header.Months[week.ParentIndex],
|
|
monthIndex = month.SelfIndexInWeeks,
|
|
monthWeeksCount = month.Childs.length;
|
|
|
|
// Caution: we should round result of operation because of known javascript math problem: http://www.webdeveloper.com/forum/showthread.php?92612-Basic-(-)-Javascript-math-precision-problem
|
|
// Example: 4.615385 + 6.923077 + 6.923077 + 6.923077 + 4.615384 = 29.999999999999996, but should be 30
|
|
|
|
for (var index = 0; index < rowsToUpdate.length; index++) {
|
|
var row = rowsToUpdate[index];
|
|
row.QuantityHoursValues[colIndex] = roundService.roundQuantity(row.QuantityHoursValues[colIndex] + (deltaHours || 0));
|
|
row.QuantityHoursValues[monthIndex] = roundService.roundQuantity(row.QuantityHoursValues[monthIndex] + (deltaHours || 0));
|
|
row.TotalHoursValue = roundService.roundQuantity(row.TotalHoursValue + (deltaHours || 0));
|
|
row.QuantityResourcesValues[colIndex] = roundService.roundQuantity(row.QuantityResourcesValues[colIndex] + deltaResources);
|
|
row.QuantityResourcesValues[monthIndex] = roundService.roundQuantity(row.QuantityResourcesValues[monthIndex] + deltaResources);
|
|
row.TotalResourcesValue = roundService.roundQuantity(row.TotalResourcesValue + deltaResources);
|
|
}
|
|
|
|
if (remCapacityRow) {
|
|
// Update of Remaining Capacity row
|
|
remCapacityRow.QuantityHoursValues[colIndex] = roundService.roundQuantity(remCapacityRow.QuantityHoursValues[colIndex] - (deltaHours || 0));
|
|
remCapacityRow.QuantityHoursValues[monthIndex] = roundService.roundQuantity(remCapacityRow.QuantityHoursValues[monthIndex] - (deltaHours || 0));
|
|
remCapacityRow.TotalHoursValue = roundService.roundQuantity(remCapacityRow.TotalHoursValue - (deltaHours || 0));
|
|
remCapacityRow.QuantityResourcesValues[colIndex] = roundService.roundQuantity(remCapacityRow.QuantityResourcesValues[colIndex] - deltaResources);
|
|
remCapacityRow.QuantityResourcesValues[monthIndex] = roundService.roundQuantity(remCapacityRow.QuantityResourcesValues[monthIndex] - deltaResources);
|
|
remCapacityRow.TotalResourcesValue = roundService.roundQuantity(remCapacityRow.TotalResourcesValue - deltaResources);
|
|
}
|
|
|
|
if (isUOMHours) {
|
|
for (var index = 0; index < rowsToUpdate.length; index++) {
|
|
var row = rowsToUpdate[index];
|
|
row.Cells[colIndex] = row.QuantityHoursValues[colIndex];
|
|
row.Cells[monthIndex] = row.QuantityHoursValues[monthIndex];
|
|
row.TotalValue = row.TotalHoursValue;
|
|
}
|
|
|
|
if (remCapacityRow) {
|
|
remCapacityRow.Cells[colIndex] = remCapacityRow.QuantityHoursValues[colIndex];
|
|
remCapacityRow.Cells[monthIndex] = remCapacityRow.QuantityHoursValues[monthIndex];
|
|
remCapacityRow.TotalValue = remCapacityRow.TotalHoursValue;
|
|
}
|
|
}
|
|
else {
|
|
for (var index = 0; index < rowsToUpdate.length; index++) {
|
|
var row = rowsToUpdate[index];
|
|
row.Cells[colIndex] = row.QuantityResourcesValues[colIndex];
|
|
row.Cells[monthIndex] = isAvgMode ? roundService.roundQuantity(row.QuantityResourcesValues[monthIndex] / monthWeeksCount) : row.QuantityResourcesValues[monthIndex];
|
|
row.TotalValue = isAvgMode ? roundService.roundQuantity(row.TotalResourcesValue / row.VisibleCellsCount) : row.TotalResourcesValue;
|
|
}
|
|
|
|
if (remCapacityRow) {
|
|
remCapacityRow.Cells[colIndex] = remCapacityRow.QuantityResourcesValues[colIndex];
|
|
remCapacityRow.Cells[monthIndex] = isAvgMode ? roundService.roundQuantity(remCapacityRow.QuantityResourcesValues[monthIndex] / monthWeeksCount) : remCapacityRow.QuantityResourcesValues[monthIndex];
|
|
remCapacityRow.TotalValue = isAvgMode ? roundService.roundQuantity(remCapacityRow.TotalResourcesValue / remCapacityRow.VisibleCellsCount) : remCapacityRow.TotalResourcesValue;
|
|
}
|
|
}
|
|
},
|
|
recalculateResourceAvailability: function (filter, header, projectRows, resourceId) {
|
|
if (!filter || !header || !projectRows || !projectRows.length || !resourceId) {
|
|
return;
|
|
}
|
|
|
|
for (var projectIndex = 0; projectIndex < projectRows.length; projectIndex++) {
|
|
var projectRow = projectRows[projectIndex];
|
|
if (projectRow.Children && projectRow.Children.length) {
|
|
for (var ecIndex = 0; ecIndex < projectRow.Children.length; ecIndex++) {
|
|
var ecRow = projectRow.Children[ecIndex];
|
|
if (ecRow.AvailableResources && ecRow.AvailableResources[resourceId]) {
|
|
var project = activityCalendarService.getProjectById(filter, projectRow.Id);
|
|
if (project && project.ActiveScenario) {
|
|
var availableResourceRow = ecRow.AvailableResources[resourceId];
|
|
var availableResourceTeams = availableResourceRow.teams ? availableResourceRow.teams.map(function (team) { return team.TeamId; }) : [];
|
|
var availableResourceData = this.getAvailableResources4Project(filter, header, project, ecRow.Id, availableResourceTeams, resourceId);
|
|
if (availableResourceData && availableResourceData[resourceId]) {
|
|
var resource = availableResourceData[resourceId];
|
|
|
|
availableResourceRow.minAvailability = resource.MinAvailability;
|
|
availableResourceRow.maxAvailability = resource.MaxAvailability;
|
|
availableResourceRow.avgAvailability = resource.AvgAvailability;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
/* Highlighting methods */
|
|
updateResourceWeekStyles: function (resourceId, resourceRow, weekEndingMs, weekIndex) {
|
|
if (!resourceId || !resourceRow || !resourceRow.CSSClass || !weekEndingMs) {
|
|
return;
|
|
}
|
|
|
|
resourceRow.CSSClass[weekIndex] = cellHighlightingService.removeHighlightClasses(resourceRow.CSSClass[weekIndex]);
|
|
var resourceIsOverAllocated = activityCalendarService.isResourceOverAllocated(resourceId, weekEndingMs);
|
|
// set overallocated class only if current cell contains a positive value
|
|
if (resourceIsOverAllocated === true && resourceRow.QuantityHoursValues && (resourceRow.QuantityHoursValues[weekIndex] || 0) > 0) {
|
|
resourceRow.CSSClass[weekIndex] = resourceRow.CSSClass[weekIndex] + ' ' + cellHighlightingService.cellOverClass;
|
|
}
|
|
},
|
|
updateResourceStyles: function (header, resourceId, resourceRows, data) {
|
|
if (!header || !resourceId || !resourceRows || !resourceRows.length || typeof data !== 'object' || !angular.isArray(data) || !data.length) {
|
|
return;
|
|
}
|
|
|
|
for (var rIndex = 0; rIndex < resourceRows.length; rIndex++) {
|
|
var resourceRow = resourceRows[rIndex];
|
|
var monthsToUpdate = [];
|
|
|
|
for (var i = 0; i < data.length; i++) {
|
|
var monthIndex = data[i].MonthIndex;
|
|
var weekIndex = data[i].WeekIndex;
|
|
var weekEndingMs = data[i].WeekEnding;
|
|
|
|
this.updateResourceWeekStyles(resourceId, resourceRow, weekEndingMs, weekIndex);
|
|
|
|
if (monthsToUpdate.indexOf(monthIndex) < 0) {
|
|
monthsToUpdate.push(monthIndex);
|
|
}
|
|
}
|
|
|
|
for (var mIndex = 0; mIndex < monthsToUpdate.length; mIndex++) {
|
|
var month = header.Months[monthsToUpdate[mIndex]];
|
|
this.updateMonthStyles(resourceRow, month.SelfIndexInWeeks, month.Childs);
|
|
}
|
|
}
|
|
},
|
|
updateResourceStylesOnEntireRange: function (header, resourceId, resourceRow) {
|
|
if (!header || !resourceId || !resourceRow || !resourceRow.QuantityHoursValues) {
|
|
return;
|
|
}
|
|
|
|
for (var monthIndex = 0; monthIndex < header.Months.length; monthIndex++) {
|
|
var month = header.Months[monthIndex];
|
|
for (var mcIndex = 0; mcIndex < month.Childs.length; mcIndex++) {
|
|
var weekIndex = month.Childs[mcIndex];
|
|
var week = header.Weeks[weekIndex];
|
|
if (resourceRow.QuantityHoursValues[weekIndex] == undefined) {
|
|
continue;
|
|
}
|
|
|
|
this.updateResourceWeekStyles(resourceId, resourceRow, week.Milliseconds, weekIndex);
|
|
}
|
|
|
|
this.updateMonthStyles(resourceRow, month.SelfIndexInWeeks, month.Childs);
|
|
}
|
|
},
|
|
getProjectWeekendingRange: function (header, projectRow) {
|
|
if (!header || !projectRow)
|
|
return null;
|
|
|
|
var result = {
|
|
Weeks: {},
|
|
Months: {}
|
|
};
|
|
|
|
for (var monthIndex = 0; monthIndex < header.Months.length; monthIndex++) {
|
|
var month = header.Months[monthIndex];
|
|
var monthWeekIncluded = false;
|
|
|
|
for (var mcIndex = 0; mcIndex < month.Childs.length; mcIndex++) {
|
|
var weekIndex = month.Childs[mcIndex];
|
|
var week = header.Weeks[weekIndex];
|
|
var weekInScenarioRange = false;
|
|
|
|
if (projectRow.IsMaster) {
|
|
if (projectRow.Children && angular.isArray(projectRow.Children)) {
|
|
for (var pIndex = 0; pIndex < projectRow.Children.length; pIndex++) {
|
|
var partRow = projectRow.Children[pIndex];
|
|
|
|
if (partRow && partRow.ActiveScenario && !!partRow.ActiveScenario.StartDate && !!partRow.ActiveScenario.EndDate) {
|
|
if ((week.Milliseconds >= partRow.ActiveScenario.StartDate) &&
|
|
(week.Milliseconds <= partRow.ActiveScenario.EndDate)) {
|
|
// Week is inside scenario range
|
|
weekInScenarioRange = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if (!!projectRow.ActiveScenario.StartDate && !!projectRow.ActiveScenario.EndDate) {
|
|
if ((week.Milliseconds >= projectRow.ActiveScenario.StartDate) &&
|
|
(week.Milliseconds <= projectRow.ActiveScenario.EndDate)) {
|
|
// Week is inside scenario range
|
|
weekInScenarioRange = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (weekInScenarioRange) {
|
|
result.Weeks[week.Milliseconds] = weekIndex;
|
|
monthWeekIncluded = true;
|
|
// usage: this.updateProjectWeekStyles(projectRow, week.Milliseconds, weekIndex);
|
|
}
|
|
}
|
|
|
|
if (monthWeekIncluded) {
|
|
result.Months[month.SelfIndexInWeeks] = month.Childs;
|
|
// usage: this.updateMonthStyles(projectRow, month.SelfIndexInWeeks, month.Childs);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
},
|
|
getChangesDataWeekendingRange: function (header, data) {
|
|
if (!header || (typeof data !== 'object') || !angular.isArray(data) || !data.length) {
|
|
return null;
|
|
}
|
|
|
|
var result = {
|
|
Weeks: {},
|
|
Months: {}
|
|
};
|
|
|
|
var prevMonthIndex = data[0].MonthIndex;
|
|
for (var i = 0; i < data.length; i++) {
|
|
var monthIndex = data[i].MonthIndex;
|
|
var weekIndex = data[i].WeekIndex;
|
|
var weekEndingMs = data[i].WeekEnding;
|
|
|
|
result.Weeks[weekEndingMs] = weekIndex;
|
|
|
|
if (i === (data.length - 1) || prevMonthIndex !== monthIndex) {
|
|
var month = header.Months[prevMonthIndex];
|
|
|
|
result.Months[month.SelfIndexInWeeks] = month.Childs;
|
|
prevMonthIndex = monthIndex;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
},
|
|
getAgregates4HighlightingNeedVsTa: function (projectsData, weekendings, includeRestTeamAllocations, agregateToMasterProject) {
|
|
// Agregates to validate Project Need vs Team Allocations
|
|
if (!projectsData || !weekendings || !angular.isArray(weekendings))
|
|
return null;
|
|
|
|
var result = {};
|
|
var multipleProjects = Object.keys(projectsData).length > 1;
|
|
|
|
for (var wIndex = 0; wIndex < weekendings.length; wIndex++) {
|
|
var we = weekendings[wIndex];
|
|
var masterProjectResult = {
|
|
Level: 'Master',
|
|
TargetValue: 0,
|
|
CalculatedValue: 0,
|
|
Children: {}
|
|
};
|
|
|
|
for (var projectId in projectsData) {
|
|
var projectData = projectsData[projectId];
|
|
var prjResult = {
|
|
Level: 'Project',
|
|
TargetValue: 0,
|
|
CalculatedValue: 0,
|
|
Children: {}
|
|
};
|
|
|
|
if (agregateToMasterProject || multipleProjects) {
|
|
// Need to get agregated data to master project level
|
|
masterProjectResult.Children[projectId] = prjResult;
|
|
}
|
|
|
|
for (var expCatId in projectData.TeamsInView) {
|
|
var expCat = projectData.TeamsInView[expCatId];
|
|
|
|
if (expCat && expCat.NeedAllocations) {
|
|
var expCatResult = {
|
|
Level: 'Category',
|
|
TargetValue: roundService.roundQuantity(expCat.NeedAllocations[we] || 0),
|
|
CalculatedValue: 0
|
|
};
|
|
|
|
if (includeRestTeamAllocations && projectData.TeamsOutOfView && (expCatId in projectData.TeamsOutOfView)) {
|
|
var expCatOutOfViewData = projectData.TeamsOutOfView[expCatId];
|
|
|
|
if (expCatOutOfViewData && expCatOutOfViewData.Allocations && (we in expCatOutOfViewData.Allocations)) {
|
|
// For No Team block (Group by Team mode) it's necessary to take rest teams allocations in calculation also
|
|
expCatResult.CalculatedValue = angular.isNumber(expCatOutOfViewData.Allocations[we]) ? Number(expCatOutOfViewData.Allocations[we]) : 0;
|
|
}
|
|
}
|
|
|
|
prjResult.Children[expCatId] = expCatResult;
|
|
prjResult.TargetValue += expCatResult.TargetValue;
|
|
|
|
if (expCat.Teams) {
|
|
for (var teamId in expCat.Teams) {
|
|
var team = expCat.Teams[teamId];
|
|
|
|
if (team && team.Allocations) {
|
|
expCatResult.CalculatedValue += team.Allocations[we] || 0;
|
|
}
|
|
}
|
|
}
|
|
expCatResult.CalculatedValue = roundService.roundQuantity(expCatResult.CalculatedValue);
|
|
prjResult.CalculatedValue += expCatResult.CalculatedValue;
|
|
}
|
|
}
|
|
prjResult.TargetValue = roundService.roundQuantity(prjResult.TargetValue);
|
|
prjResult.CalculatedValue = roundService.roundQuantity(prjResult.CalculatedValue);
|
|
|
|
if (agregateToMasterProject || multipleProjects) {
|
|
// Need to get agregated data to master project level
|
|
masterProjectResult.TargetValue += prjResult.TargetValue;
|
|
masterProjectResult.CalculatedValue += prjResult.CalculatedValue;
|
|
masterProjectResult.Children[projectId] = prjResult;
|
|
}
|
|
else {
|
|
// No master project level is needed. Simple project well be on the top of agregated data in result
|
|
result[we] = prjResult;
|
|
}
|
|
}
|
|
|
|
if (agregateToMasterProject || multipleProjects) {
|
|
// Need to get agregated data to master project level. Master project will be on the top of agregated data result
|
|
masterProjectResult.TargetValue = roundService.roundQuantity(masterProjectResult.TargetValue);
|
|
masterProjectResult.CalculatedValue = roundService.roundQuantity(masterProjectResult.CalculatedValue);
|
|
result[we] = masterProjectResult;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
},
|
|
getAgregates4HighlightingTaVsRa: function (projectsData, weekendings, agregateToMasterProject) {
|
|
// Agregates to validate Team allocations vs Resource allocations
|
|
if (!projectsData || !weekendings || !angular.isArray(weekendings))
|
|
return null;
|
|
|
|
var result = {};
|
|
var multipleProjects = Object.keys(projectsData).length > 1;
|
|
|
|
for (var wIndex = 0; wIndex < weekendings.length; wIndex++) {
|
|
var we = weekendings[wIndex];
|
|
var masterProjectResult = {
|
|
Level: 'Master',
|
|
TargetValue: 0,
|
|
CalculatedValue: 0,
|
|
Children: {}
|
|
};
|
|
|
|
for (var projectId in projectsData) {
|
|
var projectData = projectsData[projectId];
|
|
var prjResult = {
|
|
Level: 'Project',
|
|
TargetValue: 0,
|
|
CalculatedValue: 0,
|
|
Children: {}
|
|
};
|
|
|
|
if (agregateToMasterProject || multipleProjects) {
|
|
// Need to get agregated data to master project level
|
|
masterProjectResult.Children[projectId] = prjResult;
|
|
}
|
|
|
|
for (var expCatId in projectData.TeamsInView) {
|
|
var expCat = projectData.TeamsInView[expCatId];
|
|
|
|
if (expCat && expCat.Allocations) {
|
|
var expCatResult = {
|
|
Level: 'Category',
|
|
TargetValue: 0,
|
|
CalculatedValue: 0,
|
|
Children: {}
|
|
};
|
|
prjResult.Children[expCatId] = expCatResult;
|
|
|
|
if (expCat.Teams) {
|
|
for (var teamId in expCat.Teams) {
|
|
var team = expCat.Teams[teamId];
|
|
|
|
if (team && team.Allocations) {
|
|
// for team allocations display mode
|
|
var teamResult = {
|
|
Level: 'Team',
|
|
TargetValue: team.Allocations[we] || 0,
|
|
CalculatedValue: 0
|
|
};
|
|
expCatResult.Children[teamId] = teamResult;
|
|
|
|
if (team.Resources) {
|
|
for (var resourceId in team.Resources) {
|
|
var resource = team.Resources[resourceId];
|
|
|
|
if (resource && resource.Allocations) {
|
|
teamResult.CalculatedValue += resource.Allocations[we] || 0;
|
|
};
|
|
}
|
|
teamResult.CalculatedValue = roundService.roundQuantity(teamResult.CalculatedValue);
|
|
}
|
|
expCatResult.CalculatedValue += teamResult.CalculatedValue;
|
|
expCatResult.TargetValue += teamResult.TargetValue;
|
|
}
|
|
}
|
|
}
|
|
expCatResult.CalculatedValue = roundService.roundQuantity(expCatResult.CalculatedValue);
|
|
expCatResult.TargetValue = roundService.roundQuantity(expCatResult.TargetValue);
|
|
|
|
prjResult.CalculatedValue += expCatResult.CalculatedValue;
|
|
prjResult.TargetValue += expCatResult.TargetValue;
|
|
}
|
|
}
|
|
prjResult.TargetValue = roundService.roundQuantity(prjResult.TargetValue);
|
|
prjResult.CalculatedValue = roundService.roundQuantity(prjResult.CalculatedValue);
|
|
|
|
if (agregateToMasterProject || multipleProjects) {
|
|
// Need to get agregated data to master project level
|
|
masterProjectResult.TargetValue += prjResult.TargetValue;
|
|
masterProjectResult.CalculatedValue += prjResult.CalculatedValue;
|
|
masterProjectResult.Children[projectId] = prjResult;
|
|
}
|
|
else {
|
|
// No master project level is needed. Simple project well be on the top of agregated data in result
|
|
result[we] = prjResult;
|
|
}
|
|
}
|
|
|
|
if (agregateToMasterProject || multipleProjects) {
|
|
// Need to get agregated data to master project level. Master project will be on the top of agregated data result
|
|
masterProjectResult.TargetValue = roundService.roundQuantity(masterProjectResult.TargetValue);
|
|
masterProjectResult.CalculatedValue = roundService.roundQuantity(masterProjectResult.CalculatedValue);
|
|
result[we] = masterProjectResult;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
},
|
|
getAgregates4HighlightingUneedVsRa: function (projectsData, weekendings, agregateToMasterProject) {
|
|
// Agregates to validate Unassigned need vs resource allocations
|
|
// These agregates are necessary for Group By Business Unit & Filter by Business Unit.
|
|
// Precalculated Unassigned need is in expCat.RemainingNeed collections. Take it as is.
|
|
// Resources can not be assigned in group by Business Unit mode, so resource allocations are always 0
|
|
if (!projectsData || !weekendings || !angular.isArray(weekendings))
|
|
return null;
|
|
|
|
var result = {};
|
|
var multipleProjects = Object.keys(projectsData).length > 1;
|
|
|
|
for (var wIndex = 0; wIndex < weekendings.length; wIndex++) {
|
|
var we = weekendings[wIndex];
|
|
var masterProjectResult = {
|
|
Level: 'Master',
|
|
TargetValue: 0,
|
|
CalculatedValue: 0,
|
|
Children: {}
|
|
};
|
|
|
|
for (var projectId in projectsData) {
|
|
var projectData = projectsData[projectId];
|
|
var prjResult = {
|
|
Level: 'Project',
|
|
TargetValue: 0,
|
|
CalculatedValue: 0,
|
|
Children: {}
|
|
};
|
|
|
|
if (agregateToMasterProject || multipleProjects) {
|
|
// Need to get agregated data to master project level
|
|
masterProjectResult.Children[projectId] = prjResult;
|
|
}
|
|
|
|
for (var expCatId in projectData.TeamsInView) {
|
|
var expCat = projectData.TeamsInView[expCatId];
|
|
|
|
if (expCat && expCat.RemainingNeed) {
|
|
var expCatResult = {
|
|
Level: 'Category',
|
|
TargetValue: roundService.roundQuantity(expCat.UnassignedNeed[we] || 0),
|
|
CalculatedValue: 0
|
|
};
|
|
|
|
prjResult.Children[expCatId] = expCatResult;
|
|
prjResult.TargetValue += expCatResult.TargetValue;
|
|
}
|
|
}
|
|
prjResult.TargetValue = roundService.roundQuantity(prjResult.TargetValue);
|
|
|
|
if (agregateToMasterProject || multipleProjects) {
|
|
// Need to get agregated data to master project level
|
|
masterProjectResult.TargetValue += prjResult.TargetValue;
|
|
masterProjectResult.CalculatedValue += prjResult.CalculatedValue;
|
|
masterProjectResult.Children[projectId] = prjResult;
|
|
}
|
|
else {
|
|
// No master project level is needed. Simple project well be on the top of agregated data in result
|
|
result[we] = prjResult;
|
|
}
|
|
}
|
|
|
|
if (agregateToMasterProject || multipleProjects) {
|
|
// Need to get agregated data to master project level. Master project will be on the top of agregated data result
|
|
masterProjectResult.TargetValue = roundService.roundQuantity(masterProjectResult.TargetValue);
|
|
masterProjectResult.CalculatedValue = roundService.roundQuantity(masterProjectResult.CalculatedValue);
|
|
result[we] = masterProjectResult;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
},
|
|
getProjectDataForHighlighting: function (header, filter, projectIds, allocationMode, expCatsFilter, teamsFilter) {
|
|
if (!header || !filter || !projectIds || !angular.isArray(projectIds) || !allocationMode)
|
|
return null;
|
|
|
|
var result = {};
|
|
// Filtering of ECs by teams is not performed for No Team block (even if filer for AC is set to Team or View)
|
|
var isFilteredByTeamOrView = this.isFilteredByTeams(filter) && (allocationMode != this.allocationMode.remainingNeedAllocation);
|
|
var includeProjectNeed =
|
|
(allocationMode == this.allocationMode.needAllocation) ||
|
|
(allocationMode == this.allocationMode.remainingNeedAllocation) ||
|
|
(allocationMode == this.allocationMode.unassignedNeedAllocation);
|
|
var excludeResourceAllocations = !includeProjectNeed;
|
|
|
|
for (var pIndex = 0; pIndex < projectIds.length; pIndex++) {
|
|
var projectId = projectIds[pIndex];
|
|
var projectRawData = activityCalendarService.getProjectById(filter, projectId);
|
|
|
|
if (!projectRawData || !projectRawData.ActiveScenario || !projectRawData.ActiveScenario.Id)
|
|
return null;
|
|
|
|
var projectData = {
|
|
TeamsInView: null
|
|
};
|
|
var projectExpCatsData = this.getExpenditures4Project(projectRawData, null, null, header, filter, includeProjectNeed, isFilteredByTeamOrView, excludeResourceAllocations);
|
|
|
|
if ((!expCatsFilter || !angular.isArray(expCatsFilter) || !expCatsFilter.length) &&
|
|
(!teamsFilter || !angular.isArray(teamsFilter) || !teamsFilter.length)) {
|
|
// No filtering applied
|
|
projectData.TeamsInView = projectExpCatsData;
|
|
}
|
|
else {
|
|
// Apply filtering by ECs and teams
|
|
var filteringResult = {};
|
|
for (var expCatId in projectExpCatsData) {
|
|
var currentExpCatData = angular.copy(projectExpCatsData[expCatId]);
|
|
|
|
if (expCatsFilter && angular.isArray(expCatsFilter) && expCatsFilter.length) {
|
|
if (expCatsFilter.indexOf(expCatId) < 0) {
|
|
// EC not passed through filter
|
|
currentExpCatData = null;
|
|
}
|
|
}
|
|
|
|
if (currentExpCatData && currentExpCatData.Teams && teamsFilter && angular.isArray(teamsFilter) && teamsFilter.length) {
|
|
var filteredTeams4ExpCat = {};
|
|
for (var teamId in currentExpCatData.Teams) {
|
|
if (teamsFilter.indexOf(teamId) >= 0) {
|
|
// Team passed through filter
|
|
filteredTeams4ExpCat[teamId] = currentExpCatData.Teams[teamId];
|
|
}
|
|
}
|
|
|
|
if (Object.keys(filteredTeams4ExpCat).length) {
|
|
currentExpCatData.Teams = filteredTeams4ExpCat;
|
|
}
|
|
else {
|
|
// Contains no teams
|
|
currentExpCatData = null;
|
|
}
|
|
}
|
|
|
|
if (currentExpCatData) {
|
|
filteringResult[expCatId] = currentExpCatData;
|
|
}
|
|
}
|
|
|
|
if (Object.keys(filteringResult).length) {
|
|
projectData.TeamsInView = filteringResult;
|
|
}
|
|
}
|
|
|
|
if (allocationMode == this.allocationMode.remainingNeedAllocation) {
|
|
// Get also team allocations for not visible in currect calendar view teams
|
|
projectData.TeamsOutOfView = activityCalendarService.getRestTeamAllocations4Scenario(filter, projectRawData.ActiveScenario.Id);
|
|
}
|
|
|
|
result[projectId] = projectData;
|
|
}
|
|
|
|
return result;
|
|
},
|
|
getNonHighlightableProjectRows: function (allocationMode) {
|
|
// Row is not highlightable withing project highlighting engine, because not present in
|
|
// a particular view, or is editable and is highlighted via edit validation engine
|
|
var result = {};
|
|
if (!allocationMode)
|
|
return result;
|
|
|
|
switch (allocationMode) {
|
|
case this.allocationMode.needAllocation:
|
|
case this.allocationMode.remainingNeedAllocation:
|
|
result['Team'] = true;
|
|
break;
|
|
|
|
case this.allocationMode.teamAllocation:
|
|
case this.allocationMode.remainingTeamAllocation:
|
|
result['Resource'] = true;
|
|
break;
|
|
}
|
|
|
|
// this.allocationMode.unassignedNeedAllocation ??
|
|
return result;
|
|
},
|
|
updateProjectStyles: function (header, filter, projectRows, data, expendituresFilter, teamsFilter) {
|
|
// Call this method with empty <data> param to update styles on entire project range
|
|
if (!header || !filter || !projectRows || !angular.isArray(projectRows) || !projectRows.length) {
|
|
return;
|
|
}
|
|
|
|
var isMasterProject = false;
|
|
for (var pIndex = 0; pIndex < projectRows.length; pIndex++) {
|
|
var projectRow = projectRows[pIndex];
|
|
|
|
// Get weeks and months to update styles for
|
|
var calendarDatesToUpdate = (typeof data === 'object' && angular.isArray(data) && data.length) ?
|
|
this.getChangesDataWeekendingRange(header, data) : // Queue style updates to changed weeks only
|
|
this.getProjectWeekendingRange(header, projectRow); // Queue style updates for entire project range
|
|
var weekendings = Object.keys(calendarDatesToUpdate.Weeks);
|
|
|
|
// Get project Ids (part ids list for multipart project)
|
|
var projectIds = [];
|
|
if (projectRow.IsMaster) {
|
|
for (var partIndex = 0; partIndex < projectRow.Children.length; partIndex++) {
|
|
var partRow = projectRow.Children[partIndex];
|
|
if (partRow && partRow.Id) {
|
|
projectIds.push(partRow.Id);
|
|
}
|
|
}
|
|
isMasterProject = true;
|
|
}
|
|
else {
|
|
// Simple project (non-multipart)
|
|
projectIds.push(projectRow.Id);
|
|
isMasterProject = false;
|
|
}
|
|
|
|
// Get allocation mode and rowtypes, which don't participate in highlighting
|
|
var allocationModeCalculated = this.getAllocationMode(projectRow);
|
|
var unhighlightableRowTypes = this.getNonHighlightableProjectRows(allocationModeCalculated);
|
|
|
|
var projectsData = this.getProjectDataForHighlighting(header, filter, projectIds, allocationModeCalculated, expendituresFilter, teamsFilter);
|
|
var projectsDataAgregated = null;
|
|
|
|
switch (allocationModeCalculated) {
|
|
case this.allocationMode.needAllocation:
|
|
case this.allocationMode.remainingNeedAllocation: {
|
|
var includeRestTeamAllocations = (allocationModeCalculated == this.allocationMode.remainingNeedAllocation);
|
|
projectsDataAgregated = this.getAgregates4HighlightingNeedVsTa(projectsData, weekendings, includeRestTeamAllocations, isMasterProject);
|
|
break;
|
|
}
|
|
case this.allocationMode.teamAllocation: {
|
|
projectsDataAgregated = this.getAgregates4HighlightingTaVsRa(projectsData, weekendings, isMasterProject);
|
|
break;
|
|
}
|
|
case this.allocationMode.remainingTeamAllocation: {
|
|
projectsDataAgregated = this.getAgregates4HighlightingTaVsRa(projectsData, weekendings, isMasterProject);
|
|
break;
|
|
}
|
|
case this.allocationMode.unassignedNeedAllocation: {
|
|
// For AC filtering by Business Unit validation. Unassigned block. Validation of Unassigned project need vs Resource allocations
|
|
projectsDataAgregated = this.getAgregates4HighlightingUneedVsRa(projectsData, weekendings, isMasterProject);
|
|
break;
|
|
}
|
|
default: {
|
|
console.log("activityCalendarUIService.updateProjectStyles: Unknown Allocation mode found");
|
|
}
|
|
}
|
|
|
|
if (projectsDataAgregated) {
|
|
// Update highlighting for week cells
|
|
for (var weekEndingMs in calendarDatesToUpdate.Weeks) {
|
|
var weekIndex = calendarDatesToUpdate.Weeks[weekEndingMs];
|
|
var projectAgregatesWe = projectsDataAgregated[weekEndingMs];
|
|
this.updateProjectWeekStyles(projectRow, weekIndex, projectAgregatesWe, unhighlightableRowTypes);
|
|
}
|
|
|
|
for (var monthIndex in calendarDatesToUpdate.Months) {
|
|
var weekIndexes = calendarDatesToUpdate.Months[monthIndex];
|
|
this.updateMonthStyles(projectRow, monthIndex, weekIndexes, unhighlightableRowTypes);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
updateProjectWeekStyles: function (row, weekIndex, data, unhighlightableRowTypes) {
|
|
if (!row || !row.CSSClass || !data) {
|
|
return;
|
|
}
|
|
|
|
var filterByRowTypes = unhighlightableRowTypes && (Object.keys(unhighlightableRowTypes).length > 0);
|
|
|
|
if (!filterByRowTypes || (row.RowType && !(row.RowType in unhighlightableRowTypes))) {
|
|
row.CSSClass[weekIndex] = cellHighlightingService.getWithValidationCssClass(row.CSSClass[weekIndex], data.CalculatedValue || 0, data.TargetValue || 0);
|
|
}
|
|
|
|
if (row.Children && row.Children.length && data.Children) {
|
|
for (var index = 0; index < row.Children.length; index++) {
|
|
var childRow = row.Children[index];
|
|
|
|
if (childRow.Id) {
|
|
var childData = data.Children[childRow.Id];
|
|
|
|
if (childData) {
|
|
if (!filterByRowTypes || (row.RowType && !(row.RowType in unhighlightableRowTypes))) {
|
|
this.updateProjectWeekStyles(childRow, weekIndex, childData, unhighlightableRowTypes);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
updateMonthStyles: function (row, monthIndexInWeeks, weekIndexes, unhighlightableRowTypes) {
|
|
if (!row || !row.CSSClass || !weekIndexes || !weekIndexes.length) {
|
|
return;
|
|
}
|
|
|
|
var filterByRowTypes = unhighlightableRowTypes && (Object.keys(unhighlightableRowTypes).length > 0);
|
|
row.CSSClass[monthIndexInWeeks] = cellHighlightingService.removeHighlightClasses(row.CSSClass[monthIndexInWeeks]) || '';
|
|
|
|
var hasOverAllocated = false;
|
|
var hasUnderAllocated = false;
|
|
var cellEqualCount = 0;
|
|
var monthExistingCells = 0;
|
|
|
|
if (!filterByRowTypes || (row.RowType && !(row.RowType in unhighlightableRowTypes))) {
|
|
for (var i = 0; i < weekIndexes.length; i++) {
|
|
if ((row.CSSClass[weekIndexes[i]] || '').indexOf(cellHighlightingService.cellOverClass) >= 0) {
|
|
hasOverAllocated = true;
|
|
}
|
|
else if ((row.CSSClass[weekIndexes[i]] || '').indexOf(cellHighlightingService.cellLessClass) >= 0) {
|
|
hasUnderAllocated = true;
|
|
}
|
|
else if ((row.CSSClass[weekIndexes[i]] || '').indexOf(cellHighlightingService.cellEqualClass) >= 0 ||
|
|
row.QuantityHoursValues[weekIndexes[i]] == 0) // identify zero cell as equal to mark monthly cell equal even if some(not all) of cells are zero
|
|
{
|
|
cellEqualCount++;
|
|
}
|
|
if (Object.keys(row.CSSClass).indexOf(weekIndexes[i].toString()) >= 0)
|
|
monthExistingCells++;
|
|
}
|
|
|
|
if (hasOverAllocated) { // if any cell is colored with red we should color month cell with red as well
|
|
row.CSSClass[monthIndexInWeeks] = (row.CSSClass[monthIndexInWeeks] || '') + ' ' + cellHighlightingService.cellOverClass;
|
|
}
|
|
else if (hasUnderAllocated) {// if there is no cell colored with red and exist at least one cell that is colored with yellow we should color month cell with yellow as well
|
|
row.CSSClass[monthIndexInWeeks] = (row.CSSClass[monthIndexInWeeks] || '') + ' ' + cellHighlightingService.cellLessClass;
|
|
}
|
|
else if (cellEqualCount === monthExistingCells && row.QuantityHoursValues[monthIndexInWeeks] > 0) {
|
|
// in some cases weekly cells can be without highlighting and we must color month cell with green only when all cells are colored with green or zero
|
|
// and monthly value is greater than zero
|
|
row.CSSClass[monthIndexInWeeks] = (row.CSSClass[monthIndexInWeeks] || '') + ' ' + cellHighlightingService.cellEqualClass;
|
|
}
|
|
}
|
|
|
|
if (row.Children && row.Children.length) {
|
|
for (var cIndex = 0; cIndex < row.Children.length; cIndex++) {
|
|
var childRow = row.Children[cIndex];
|
|
if (!filterByRowTypes || (childRow.RowType && !(childRow.RowType in unhighlightableRowTypes))) {
|
|
this.updateMonthStyles(childRow, monthIndexInWeeks, weekIndexes, unhighlightableRowTypes);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
setTeamWideNptItemsReadOnly: function (nonProjectTimeDataCache) {
|
|
if (!nonProjectTimeDataCache)
|
|
return;
|
|
|
|
for (var groupKey in nonProjectTimeDataCache) {
|
|
var nptGroup = nonProjectTimeDataCache[groupKey];
|
|
|
|
if (nptGroup) {
|
|
for (var nptCatId in nptGroup) {
|
|
var nptCatItem = nptGroup[nptCatId];
|
|
|
|
if (nptCatItem && nptCatItem.NonProjectTime) {
|
|
for (var nptItemId in nptCatItem.NonProjectTime) {
|
|
var nptItem = nptCatItem.NonProjectTime[nptItemId];
|
|
|
|
if (nptItem && nptItem.isTeamWide)
|
|
nptItem.isReadOnly = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
castDisplayModeIntoTeamInfoMode: function (displayMode) {
|
|
if (!displayMode) {
|
|
return null;
|
|
}
|
|
|
|
var castGroupBy = function () {
|
|
if (!displayMode.hasOwnProperty('GroupBy')) {
|
|
return 'Category';
|
|
}
|
|
|
|
switch (displayMode.GroupBy.Value) {
|
|
case '1': // Group by Team AC mode
|
|
case '5': // Supply and Demand report
|
|
return 'Team';
|
|
case '4': return 'Business Unit';
|
|
case '6': return 'Cost Center';
|
|
default: return 'Category';
|
|
}
|
|
};
|
|
|
|
var tiDisplayMode = {};
|
|
|
|
tiDisplayMode.IsUOMHours = displayMode.IsUOMHours;
|
|
tiDisplayMode.IsAvgMode = displayMode.ShowAvgTotals;
|
|
tiDisplayMode.GroupBy = castGroupBy();
|
|
if (displayMode.hasOwnProperty('CapacityMode') >= 0)
|
|
tiDisplayMode.TotalsAs = displayMode.CapacityMode.Value;
|
|
if (displayMode.hasOwnProperty('IsCapacityModeActuals') >= 0)
|
|
tiDisplayMode.CapacityView = displayMode.IsCapacityModeActuals.Value;
|
|
if (displayMode.hasOwnProperty('ShowLower') >= 0)
|
|
tiDisplayMode.ShowResources = displayMode.ShowLower.Value;
|
|
|
|
return tiDisplayMode;
|
|
},
|
|
watchKeyInput: function (t) {
|
|
$timeout(function () {
|
|
if (t.$editable.inputEl.select)
|
|
t.$editable.inputEl.select();
|
|
else if (t.$editable.inputEl.setSelectionRange)
|
|
t.$editable.inputEl.setSelectionRange(0, t.$editable.inputEl.val().length);
|
|
}, 3);
|
|
|
|
t.$editable.inputEl.on('keydown', function (e) {
|
|
if (e.which == 9) { //when tab key is pressed
|
|
e.preventDefault();
|
|
var tab2Cell;
|
|
if (e.shiftKey) { // when shift + tab use with 'onblur' set to 'submit' for automatic submission find the parent of the editable before this one in the markup grab the editable and display it
|
|
tab2Cell = $(this).parentsUntil('table.main-table').prevAll(":has(.editable:visible):first").find(".editable:visible:last");
|
|
t.$form.$submit();
|
|
$timeout(function () {
|
|
tab2Cell.click();
|
|
}, 0);
|
|
} else { // when just tab use with 'onblur' set to 'submit' for automatic submission find the parent of the editable after this one in the markup grab the editable and display it
|
|
tab2Cell = $(this).parentsUntil('table.main-table').nextAll(":has(.editable:visible):first").find(".editable:visible:first");
|
|
t.$form.$submit();
|
|
$timeout(function () {
|
|
tab2Cell.click();
|
|
}, 0);
|
|
}
|
|
}
|
|
});
|
|
},
|
|
onTxtBlur: function (t) {
|
|
t.$form.$submit();
|
|
},
|
|
rollupRemainingRow: function (rowObject, deltaHoursValue, deltaResourcesValue, allowNegative, weekCellIndex, monthCellIndex) {
|
|
if (deltaHoursValue == 0)
|
|
return [0, 0];
|
|
var row = rowObject.Row;
|
|
var deltaHours = (deltaHoursValue || 0);
|
|
var deltaResources = (deltaResourcesValue || 0);
|
|
// rollup delta value to all nested rows and override incoming delta to avoid negative numbers if necessary
|
|
if (rowObject.Children && rowObject.Children.length > 0) {
|
|
deltaHours = 0;
|
|
deltaResources = 0;
|
|
|
|
for (var i = 0; i < rowObject.Children.length; i++) {
|
|
var res = this.rollupRemainingRow(rowObject.Children[i], (deltaHoursValue || 0), (deltaResourcesValue || 0), allowNegative, weekCellIndex, monthCellIndex);
|
|
deltaHours += res[0];
|
|
deltaResources += res[1];
|
|
}
|
|
}
|
|
var collectionNames = this.getRowCollectionNames(rowObject);
|
|
// if negative numbers are not allowed then we should reset value to zero
|
|
if (!allowNegative && ((row[collectionNames.hoursCollectionName][weekCellIndex] || 0) + deltaHours) < 0) {
|
|
deltaHours = -(row[collectionNames.hoursCollectionName][weekCellIndex] || 0);
|
|
deltaResources = -(row[collectionNames.resourcesCollectionName][weekCellIndex] || 0);
|
|
}
|
|
|
|
// add delta value to values from all collections
|
|
row[collectionNames.hoursCollectionName][weekCellIndex] = roundService.roundQuantity((row[collectionNames.hoursCollectionName][weekCellIndex] || 0) + deltaHours);
|
|
row[collectionNames.hoursCollectionName][monthCellIndex] = roundService.roundQuantity((row[collectionNames.hoursCollectionName][monthCellIndex] || 0) + deltaHours);
|
|
row[collectionNames.hoursTotal] = roundService.roundQuantity((row[collectionNames.hoursTotal] || 0) + deltaHours);
|
|
row[collectionNames.resourcesCollectionName][weekCellIndex] = roundService.roundQuantity((row[collectionNames.resourcesCollectionName][weekCellIndex] || 0) + deltaResources);
|
|
row[collectionNames.resourcesCollectionName][monthCellIndex] = roundService.roundQuantity((row[collectionNames.resourcesCollectionName][monthCellIndex] || 0) + deltaResources);
|
|
row[collectionNames.resourceTotal] = roundService.roundQuantity((row[collectionNames.resourceTotal] || 0) + deltaResources);
|
|
// return adjusted delta values
|
|
return [deltaHours, deltaResources];
|
|
},
|
|
getRowCollectionNames: function (rowObject) {//todo: check all calls alloc mode source changed
|
|
var row = rowObject.Row;
|
|
var collectionNames = this.getCollectionNames4AllocationMode(row.AllocationMode);
|
|
var names = {
|
|
hoursCollectionName: (collectionNames.hoursCollectionName in row ? collectionNames.hoursCollectionName : this.allocationMode.defaultHoursCollectionName),
|
|
hoursTotal: (collectionNames.hoursTotal in row ? collectionNames.hoursTotal : this.allocationMode.defaultHoursTotalName),
|
|
resourcesCollectionName: (collectionNames.resourcesCollectionName in row ? collectionNames.resourcesCollectionName : this.allocationMode.defaultResourcesCollectionName),
|
|
resourceTotal: (collectionNames.resourceTotal in row ? collectionNames.resourceTotal : this.allocationMode.defaultResourcesTotalName)
|
|
};
|
|
return names;
|
|
},
|
|
// returns true if AC filtered ty team or view, otherwise returns false
|
|
isFilteredByTeams: function (filter) {
|
|
var result = (filter.EntityType == this.filterEntityType.team) || (filter.EntityType == this.filterEntityType.view);
|
|
return result;
|
|
},
|
|
// returns true if AC filtered ty company, otherwise returns false
|
|
isFilteredByCompany: function (filter) {
|
|
if (!filter)
|
|
return false;
|
|
var result = filter.EntityType == this.filterEntityType.company;
|
|
return result;
|
|
},
|
|
getCategoriesWithNonZeroTeamAllocations: function (expCats, projectTeamAllocations, alwaysVisibleExpCats, includeWithoutAllocation) {
|
|
if (!expCats || !expCats.length)
|
|
return expCats;
|
|
|
|
if (!projectTeamAllocations)
|
|
return [];
|
|
|
|
var result = [];
|
|
for (var index = 0; index < expCats.length; index++) {
|
|
var expCatId = expCats[index];
|
|
var isProjectRole = dataSources.isSuperEC(expCatId);
|
|
|
|
if (!isProjectRole || (isProjectRole && (!alwaysVisibleExpCats || !angular.isArray(alwaysVisibleExpCats) || (alwaysVisibleExpCats.indexOf(expCatId) < 0)))) {
|
|
// Always perform filtering for ordinary ECs.
|
|
// Perform filtering for a project role, only if it is not in Always Visible list
|
|
if (expCatId in projectTeamAllocations) {
|
|
var expCatData = projectTeamAllocations[expCatId];
|
|
|
|
if (includeWithoutAllocation || (expCatData && expCatData.Allocations && !calculateDistributionService.isEmptyAllocations(expCatData.Allocations, false))) {
|
|
result.push(expCatId);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
// Project Roles is in Always Visible list
|
|
result.push(expCatId);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
},
|
|
// sets AllocationMode property to provided value for the specified row and all children levels
|
|
setAllocationMode: function (row, allocationMode, isOverwrite) {
|
|
// row - an object which allocation mode should be set
|
|
// allocationMode - value that will be set for the specified row and all it's descendants
|
|
// isOverwrite - indicates whether to overwrite row.AllocationMode if it is already set or not
|
|
if (!row || !allocationMode) {
|
|
return;
|
|
}
|
|
row.AllocationMode = isOverwrite ? allocationMode : row.AllocationMode || allocationMode;
|
|
if (row.Children && row.Children.length) {
|
|
var that = this;
|
|
angular.forEach(row.Children, function (child) {
|
|
that.setAllocationMode(child, allocationMode);
|
|
})
|
|
}
|
|
},
|
|
getProjectRows: function (collection, projectId) {
|
|
if (!projectId || !collection || !angular.isArray(collection))
|
|
return null;
|
|
|
|
var result = {
|
|
masterProjectRow: null,
|
|
projectRow: null
|
|
};
|
|
|
|
for (var mIndex = 0; mIndex < collection.length; mIndex++) {
|
|
var row = collection[mIndex];
|
|
|
|
if (row) {
|
|
if (row.IsMaster) {
|
|
if (row.Children) {
|
|
for (var pIndex = 0; pIndex < row.Children.length; pIndex++) {
|
|
var projectRow = row.Children[pIndex];
|
|
|
|
if (projectRow && projectRow.Id && (projectRow.Id == projectId)) {
|
|
result.masterProjectRow = row;
|
|
result.projectRow = projectRow;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if (row.Id && (row.Id == projectId)) {
|
|
result.projectRow = row;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (result.projectRow) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
},
|
|
getResourceRows: function (collection, resourceId) {
|
|
if (!resourceId || !collection || !angular.isArray(collection))
|
|
return null;
|
|
|
|
var result = [];
|
|
|
|
for (var index = 0; index < collection.length; index++) {
|
|
var row = collection[index];
|
|
|
|
if (row) {
|
|
if (row.RowType && (row.RowType == 'Resource') && (row.Id == resourceId)) {
|
|
result.push(row);
|
|
break;
|
|
}
|
|
|
|
if ((!row.RowType || (row.RowType != 'Resource')) && row.Children) {
|
|
var foundRows = this.getResourceRows(row.Children, resourceId);
|
|
|
|
if (foundRows && foundRows.length) {
|
|
result = result.concat(foundRows);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
},
|
|
findRowInCollection: function (collection, id, filter) {
|
|
if (!id || !collection || !angular.isArray(collection) || !collection.length)
|
|
return null;
|
|
|
|
var result = null;
|
|
for (var index = 0; index < collection.length; index++) {
|
|
var row = collection[index];
|
|
|
|
if (row && row.Id && row.Id == id) {
|
|
var fullMatch = true;
|
|
|
|
if (filter) {
|
|
var properties = Object.keys(filter);
|
|
|
|
for (var propIndex = 0; propIndex < properties.length; propIndex++) {
|
|
var propName = properties[propIndex];
|
|
|
|
if (row[propName] !== filter[propName]) {
|
|
fullMatch = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (fullMatch) {
|
|
result = row;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
},
|
|
resolveResourceAvailableTeam: function (weekEndingMs, teams) {
|
|
if (!weekEndingMs || !teams || !teams.length)
|
|
return null;
|
|
for (var i = 0; i < teams.length; i++) {
|
|
var team = teams[i];
|
|
if ((!!team.StartDate && team.StartDate <= weekEndingMs) && (!team.EndDate || team.EndDate >= weekEndingMs)) {
|
|
return team;
|
|
}
|
|
}
|
|
return null;
|
|
},
|
|
|
|
// ----------------- No Team section methods -------------------------
|
|
// removes empty rows from projectExpCats collection and children
|
|
// in other words: let's hide it if there is nothing to assign
|
|
removeEmptyUnassignedExpenditures: function (projectId, projectExpCats, filter) {
|
|
if (!projectExpCats)
|
|
return;
|
|
|
|
var result = {};
|
|
var ec2TeamsExisting = {};
|
|
|
|
var expCatsInPendingTeams = activityCalendarService.getExpendituresExistInPendingTeams(filter, projectId);
|
|
|
|
for (var expCatId in projectExpCats) {
|
|
// check if there is any team that contains category
|
|
if (ec2TeamsExisting[expCatId] == undefined) {
|
|
ec2TeamsExisting[expCatId] = teamInfoService.teamsWithExpenditureCategoryExist(expCatId);
|
|
}
|
|
|
|
var expCatItem = projectExpCats[expCatId];
|
|
if (expCatItem && ec2TeamsExisting[expCatId] &&
|
|
((expCatsInPendingTeams && (expCatId in expCatsInPendingTeams) && expCatsInPendingTeams[expCatId]) ||
|
|
!calculateDistributionService.isEmptyAllocations(expCatItem.RemainingNeed))) {
|
|
// Display EC, if it has realated teams and (it has recently assigned teams or has unallocated need)
|
|
result[expCatId] = expCatItem;
|
|
}
|
|
}
|
|
|
|
if (Object.keys(result).length < 1) {
|
|
// No EC with Remaining Need was found
|
|
result = undefined;
|
|
}
|
|
|
|
return result;
|
|
},
|
|
getTooltipEcUnassignedContent: function (opts, displayMode, filter, header, noTeamRows) {
|
|
var tooltip = 'No data available';
|
|
var units = displayMode.IsUOMHours ? ' hours' : ' resources';
|
|
|
|
if (!opts || !opts.header || !opts.projectId || !opts.expCatId)
|
|
return tooltip;
|
|
|
|
var headerText = opts.header;
|
|
var projectId = opts.projectId;
|
|
var expCatId = opts.expCatId;
|
|
|
|
var rows = this.findNoTeamRows(noTeamRows, projectId, expCatId);
|
|
if (!rows || !rows.projectRow || !rows.expCatRow)
|
|
return tooltip;
|
|
|
|
var projectRow = rows.projectRow;
|
|
var expCatRow = rows.expCatRow;
|
|
var scenarioId = projectRow.ActiveScenario ? projectRow.ActiveScenario.Id : null;
|
|
|
|
if (!projectId || !scenarioId) {
|
|
return tooltip;
|
|
}
|
|
|
|
var expCatInfo = this.getExpCatUnassignedNeed(filter, displayMode,
|
|
header, headerText, projectId, scenarioId, [expCatRow.Id], false);
|
|
|
|
if (expCatInfo) {
|
|
tooltip =
|
|
'Category Need: ' + String(expCatInfo.Need || 0) + units + '<br/>' +
|
|
'Allocated to Teams in View: ' + String(expCatInfo.AllocatedToTeamsInView || 0) + units + '<br/>' +
|
|
'Allocated to Teams out of View: ' + String(expCatInfo.AllocatedToTeamsOutOfView || 0) + units;
|
|
|
|
if (expCatInfo.Underallocation) {
|
|
if (expCatInfo.Underallocation > 0) {
|
|
tooltip += ('<br/>Underallocation: ' + String(expCatInfo.Underallocation) + units);
|
|
}
|
|
else {
|
|
if (expCatInfo.Underallocation < 0) {
|
|
tooltip += ('<br/>Overallocation: ' + String(-expCatInfo.Underallocation) + units);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return tooltip;
|
|
},
|
|
getTooltipProjectUnassignedContent: function (opts, displayMode, filter, header, noTeamRows) {
|
|
var tooltip = 'No data available';
|
|
var units = displayMode.IsUOMHours ? ' hours' : ' resources';
|
|
|
|
if (!opts || !opts.header || !opts.projectId)
|
|
return tooltip;
|
|
|
|
var headerText = opts.header;
|
|
var projectId = opts.projectId;
|
|
|
|
var projectNeed = 0;
|
|
var projectAllocatedInView = 0;
|
|
var projectAllocatedOutOfView = 0;
|
|
var projectRowsInfo = this.getProjectRows(noTeamRows, projectId);
|
|
var projectRow = projectRowsInfo && projectRowsInfo.projectRow ? projectRowsInfo.projectRow : null;
|
|
|
|
if (!projectRow)
|
|
return tooltip;
|
|
|
|
var scenarioId = projectRow.ActiveScenario ? projectRow.ActiveScenario.Id : null;
|
|
if (!scenarioId) {
|
|
return tooltip;
|
|
}
|
|
|
|
if (projectRow.Children && projectRow.Children.length) {
|
|
for (var ecIndex = 0; ecIndex < projectRow.Children.length; ecIndex++) {
|
|
var expCatRow = projectRow.Children[ecIndex];
|
|
|
|
if (expCatRow) {
|
|
var expCatSummaryInfo = this.getExpCatUnassignedNeed(filter, displayMode,
|
|
header, headerText, projectId, scenarioId, [expCatRow.Id], false);
|
|
|
|
if (expCatSummaryInfo) {
|
|
projectNeed += (expCatSummaryInfo.Need || 0);
|
|
projectAllocatedInView += (expCatSummaryInfo.AllocatedToTeamsInView || 0);
|
|
projectAllocatedOutOfView += (expCatSummaryInfo.AllocatedToTeamsOutOfView || 0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
projectNeed = Math.roundQuantity(projectNeed);
|
|
projectAllocatedInView = Math.roundQuantity(projectAllocatedInView);
|
|
projectAllocatedOutOfView = Math.roundQuantity(projectAllocatedOutOfView);
|
|
|
|
tooltip =
|
|
'Project Need: ' + String(projectNeed) + units + '<br/>' +
|
|
'Allocated to Teams in View: ' + String(projectAllocatedInView) + units + '<br/>' +
|
|
'Allocated to Teams out of View: ' + String(projectAllocatedOutOfView) + units;
|
|
|
|
return tooltip;
|
|
},
|
|
findNoTeamRows: function (rows, projectId, expCatId) {
|
|
if (!projectId || !expCatId || !rows || !rows.length) {
|
|
return null;
|
|
}
|
|
|
|
var result = {
|
|
masterProjectRow: null,
|
|
projectRow: null,
|
|
expCatRow: null
|
|
};
|
|
|
|
// Find project rows (master & part)
|
|
var projectRowsInfo = this.getProjectRows(rows, projectId);
|
|
result.masterProjectRow = projectRowsInfo && projectRowsInfo.masterProjectRow ? projectRowsInfo.masterProjectRow : null;
|
|
result.projectRow = projectRowsInfo && projectRowsInfo.projectRow ? projectRowsInfo.projectRow : null;
|
|
|
|
if (result.projectRow && result.projectRow.Children) {
|
|
result.expCatRow = this.findRowInCollection(result.projectRow.Children, expCatId);
|
|
}
|
|
|
|
return result;
|
|
},
|
|
getNoTeamRowsToUpdate: function (noTeamRow, projectId, expCatId) {
|
|
if (!projectId || !expCatId || !noTeamRow || !noTeamRow.Children)
|
|
return [];
|
|
|
|
// Get corresponding row for unassigned project
|
|
var result = [];
|
|
var foundRowsToRollup = this.findNoTeamRows(noTeamRow.Children, projectId, expCatId);
|
|
var result = [];
|
|
var masterProjectRow = null;
|
|
|
|
if (foundRowsToRollup) {
|
|
if (foundRowsToRollup.expCatRow) {
|
|
var expCatRow = {
|
|
Row: foundRowsToRollup.expCatRow,
|
|
Children: [],
|
|
};
|
|
|
|
if (foundRowsToRollup.masterProjectRow) {
|
|
masterProjectRow = {
|
|
Row: foundRowsToRollup.masterProjectRow,
|
|
Children: [],
|
|
};
|
|
}
|
|
|
|
if (foundRowsToRollup.projectRow) {
|
|
var projectRow = {
|
|
Row: foundRowsToRollup.projectRow,
|
|
Children: [],
|
|
};
|
|
|
|
var unassignedRow = {
|
|
Row: noTeamRow,
|
|
Children: []
|
|
};
|
|
|
|
projectRow.Children.push(expCatRow);
|
|
if (masterProjectRow) {
|
|
masterProjectRow.Children.push(projectRow)
|
|
unassignedRow.Children.push(masterProjectRow);
|
|
}
|
|
else {
|
|
unassignedRow.Children.push(projectRow);
|
|
}
|
|
|
|
result.push(unassignedRow);
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
},
|
|
getAvailableTeams: function (filter, projectId, expenditureCategoryId) {
|
|
if (!projectId || !expenditureCategoryId) {
|
|
return null;
|
|
}
|
|
|
|
var availableTeams = activityCalendarService.getTeamsAvailable4Assign(filter, projectId, expenditureCategoryId);
|
|
var availableTeamsViewModel = this.createViewModel4AvailableTeams(availableTeams);
|
|
if (!availableTeamsViewModel || !Object.keys(availableTeamsViewModel).length) {
|
|
return null;
|
|
}
|
|
|
|
return $filter('sortObjectsBy')(availableTeamsViewModel, 'Name', false);
|
|
},
|
|
refreshProjectAvailableTeams: function (filter, projectRow) {
|
|
// we need to refresh list with available teams for each expenditure category under certain project
|
|
if (projectRow.Children) {
|
|
for (var ecIndex = 0; ecIndex < projectRow.Children.length; ecIndex++) {
|
|
var ecRow = projectRow.Children[ecIndex];
|
|
if (ecRow) {
|
|
ecRow.AvailableTeams = this.getAvailableTeams(filter, projectRow.Id, ecRow.Id);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
setNoTeamRowTemplates: function (expCatRow) {
|
|
if (!expCatRow)
|
|
return;
|
|
|
|
expCatRow.Templates = {
|
|
Main: this.viewRowTemplates.expCatUnallocatedRowTemplate,
|
|
Numbers: this.viewRowTemplates.expCatUnallocatedRowNumbersTemplate
|
|
};
|
|
|
|
if (expCatRow.Children) {
|
|
for (var tIndex = 0; tIndex < expCatRow.Children.length; tIndex) {
|
|
var teamRow = expCatRow.Children[tIndex];
|
|
teamRow.Templates = {
|
|
Main: this.viewRowTemplates.teamUnallocatedRowTemplate,
|
|
Numbers: this.viewRowTemplates.teamUnallocatedRowNumbersTemplate
|
|
};
|
|
}
|
|
}
|
|
},
|
|
createAndFillNewEcTeamRow: function (projectRow, expCatRow, teamId, teamInfo, header, displayMode, filter) {
|
|
// create a UI row model and fill it with data
|
|
var ecTeamData = this.getDataModel4NewTeam(teamId, projectRow.Id, expCatRow.Id, filter, header);
|
|
ecTeamData = angular.extend({}, ecTeamData, {
|
|
Id: teamInfo.Id,
|
|
ExpenditureCategoryId: ecTeamData.Id,
|
|
Name: teamInfo.Name,
|
|
Initialized: expCatRow.Initialized,
|
|
Show: expCatRow.Show,
|
|
Level: expCatRow.Level + 1
|
|
});
|
|
var newTeamRow = this.createViewModel4ECTeam(ecTeamData);
|
|
this.fillECTeamRowWithData(header, newTeamRow, expCatRow, ecTeamData, projectRow.Id, projectRow.ActiveScenario.StartDate,
|
|
projectRow.ActiveScenario.EndDate, projectRow.ReadOnly);
|
|
this.toggleGridSource([newTeamRow], displayMode.IsUOMHours);
|
|
if (displayMode.IsAvgMode()) {
|
|
this.applyAvgMode([newTeamRow], header);
|
|
}
|
|
|
|
// Remove highlighting classes for new team row (they are automatically set in toggleGridSource
|
|
if (newTeamRow && newTeamRow.CSSClass && angular.isArray(newTeamRow.CSSClass)) {
|
|
for (var index = 0; index < newTeamRow.CSSClass.length; index++) {
|
|
newTeamRow.CSSClass[index] = '';
|
|
}
|
|
}
|
|
|
|
// add new team row to EC row's children and resort it
|
|
if (!expCatRow.Children)
|
|
expCatRow.Children = [];
|
|
expCatRow.Children.push(newTeamRow);
|
|
expCatRow.Children = this.toggleSortBy(expCatRow.Children, displayMode.SortBy, displayMode.SortOrder);
|
|
},
|
|
getDataModel4NewTeam: function (teamId, projectId, expCatId, filter, header) {
|
|
if (!teamId || !projectId || !expCatId) {
|
|
return null;
|
|
}
|
|
|
|
var project = activityCalendarService.getProjectById(filter, projectId);
|
|
if (!project || !project.Teams || !(teamId in project.Teams)) {
|
|
return null;
|
|
}
|
|
|
|
var expCats = this.getExpendituresWithResources4Project(project, [teamId], [expCatId], header, filter, true, false);
|
|
if (expCats && expCats[expCatId])
|
|
return expCats[expCatId];
|
|
else
|
|
return null;
|
|
},
|
|
refreshNoTeamProjectStyles: function (filter, header, noTeamRows, projectRow, data) {
|
|
if (!projectRow || typeof data !== 'object' || !angular.isArray(data)) {
|
|
return;
|
|
}
|
|
var rowToUpdateStylesIn = projectRow;
|
|
if (projectRow.Level && (projectRow.Level > 1)) {
|
|
// For master project perform style updates for entire master project
|
|
var projectRowsInfo = this.getProjectRows(noTeamRows, projectRow.Id);
|
|
if (projectRowsInfo && projectRowsInfo.masterProjectRow) {
|
|
rowToUpdateStylesIn = projectRowsInfo.masterProjectRow;
|
|
}
|
|
}
|
|
this.updateProjectStyles(header, filter, [rowToUpdateStylesIn], data);
|
|
},
|
|
updateTotalRows: function (changedCells, teamRow, expCatRow, projectId, noTeamRow, assignedRows, header, filter, displayMode) {
|
|
if (typeof changedCells !== 'object' || !angular.isArray(changedCells) || !expCatRow || !projectId || !teamRow) {
|
|
return;
|
|
}
|
|
var expenditureCategoryId = expCatRow.Id;
|
|
// gather rows to rollup changes
|
|
var noTeamRowsToUpdate = this.getNoTeamRowsToUpdate(noTeamRow, projectId, expenditureCategoryId) ||[];
|
|
var assignedRowsToUpdate = this.getAssignedRowsToUpdate(assignedRows, teamRow.Id, projectId, expenditureCategoryId) || [];
|
|
if ((assignedRowsToUpdate.length <= 0) && (noTeamRowsToUpdate.length <= 0)) {
|
|
return;
|
|
}
|
|
|
|
var header = header;
|
|
var isUOMHours = displayMode.IsUOMHours;
|
|
var isAvgMode = displayMode.IsAvgMode();
|
|
var project = activityCalendarService.getProjectById(filter, projectId);
|
|
var weekEndings = changedCells.map(function (cell) { return cell.WeekEnding; });
|
|
var remainingNeed = activityCalendarService.getRemainingNeedAllocations4Scenario(filter, Object.keys(project.Teams),
|
|
project.ActiveScenario.Id, [expenditureCategoryId], weekEndings) || {};
|
|
var expCatCollectionNames = this.getCollectionNames4AllocationMode(expCatRow.AllocationMode);
|
|
|
|
for (var i = 0; i < changedCells.length; i++) {
|
|
var cell = changedCells[i];
|
|
var month = header.Months[cell.MonthIndex];
|
|
var monthWeeksCount = month.Childs.length;
|
|
|
|
// decrease values in related rows by provided delta value
|
|
var oldRemainingNeedValue = expCatRow[expCatCollectionNames.hoursCollectionName][cell.WeekIndex] || 0;
|
|
// get delta value for exp cat row
|
|
var newRemainingNeedValue = ((remainingNeed[expenditureCategoryId] || {}).Allocations || {})[cell.WeekEnding] || 0;
|
|
var rollupRemainingNeedDeltaHours = oldRemainingNeedValue - newRemainingNeedValue;
|
|
var rollupRemainingNeedDeltaResources = hoursResourcesConverter.convertToResources(expenditureCategoryId, rollupRemainingNeedDeltaHours);
|
|
// team allocations delta values
|
|
var rollupTeamAllocationDeltaHours = cell.DeltaHoursValue || 0;
|
|
var rollupTeamAllocationDeltaResources = hoursResourcesConverter.convertToResources(expenditureCategoryId, rollupTeamAllocationDeltaHours);
|
|
|
|
// rollup rows under no team section
|
|
for (var rowIndex = 0; rowIndex < noTeamRowsToUpdate.length; rowIndex++) {
|
|
var row = noTeamRowsToUpdate[rowIndex];
|
|
this.rollupRemainingRow(row, -(rollupRemainingNeedDeltaHours || 0), -(rollupRemainingNeedDeltaResources || 0), false, cell.WeekIndex, month.SelfIndexInWeeks);
|
|
this.setCellsValues4Row(row, cell.WeekIndex, month.SelfIndexInWeeks, isUOMHours, isAvgMode, monthWeeksCount);
|
|
}
|
|
|
|
// rollup already assigned rows in the top part
|
|
for (var rowIndex = 0; rowIndex < assignedRowsToUpdate.length; rowIndex++) {
|
|
var rollupValues = this.rollupRemainingRow(assignedRowsToUpdate[rowIndex], (rollupTeamAllocationDeltaHours || 0), (rollupTeamAllocationDeltaResources || 0), false, cell.WeekIndex, month.SelfIndexInWeeks);
|
|
this.setCellsValues4Row(assignedRowsToUpdate[rowIndex], cell.WeekIndex, month.SelfIndexInWeeks, isUOMHours, isAvgMode, monthWeeksCount);
|
|
var assignedProjectItems = assignedRowsToUpdate[rowIndex].Children;
|
|
|
|
if (assignedProjectItems && angular.isArray(assignedProjectItems)) {
|
|
for (var projectIndex = 0; projectIndex < assignedProjectItems.length; projectIndex++) {
|
|
// refresh original project styles
|
|
var assignedProjectItem = assignedProjectItems[projectIndex];
|
|
var assignedProjectRow = assignedProjectItem.Row;
|
|
|
|
if (assignedProjectItem.updateCellStyles) {
|
|
this.refreshProjectStyles(assignedRows, header, filter, assignedProjectRow, changedCells);
|
|
}
|
|
|
|
// change remaining capacity value for original category in the top part
|
|
if (assignedProjectItem.Children && angular.isArray(assignedProjectItem.Children)) {
|
|
for (var ecIndex = 0; ecIndex < assignedProjectItem.Children.length; ecIndex++) {
|
|
var assignedECRow = assignedProjectItem.Children[ecIndex].Row;
|
|
if (assignedECRow.RemainingCapacityValues) {
|
|
assignedECRow.RemainingCapacityValues[cell.WeekIndex] += rollupValues[0];
|
|
assignedECRow.RemainingCapacityValues[month.SelfIndexInWeeks] += rollupValues[0];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
getAssignedRowsToUpdate: function (assignedRows, teamId, projectId, expCatId) {
|
|
if (!teamId || !projectId || !expCatId || !assignedRows || !assignedRows.length) {
|
|
return [];
|
|
}
|
|
|
|
var foundRowsToRollup = this.findAssignedRows(assignedRows, teamId, projectId, expCatId);
|
|
var result = [];
|
|
var masterProjectRow = null;
|
|
|
|
if (foundRowsToRollup) {
|
|
if (foundRowsToRollup.expCatRow) {
|
|
var expCatRow = {
|
|
Row: foundRowsToRollup.expCatRow,
|
|
Children: [],
|
|
};
|
|
|
|
if (foundRowsToRollup.masterProjectRow) {
|
|
masterProjectRow = {
|
|
updateCellStyles: false, // No cell highlighting update needed (master project rows have no highlighting)
|
|
Row: foundRowsToRollup.masterProjectRow,
|
|
Children: [],
|
|
};
|
|
}
|
|
|
|
if (foundRowsToRollup.projectRow) {
|
|
var projectRow = {
|
|
updateCellStyles: true, // Cell highlighting update needed
|
|
Row: foundRowsToRollup.projectRow,
|
|
Children: [],
|
|
};
|
|
|
|
if (foundRowsToRollup.teamRow) {
|
|
var teamRow = {
|
|
Row: foundRowsToRollup.teamRow,
|
|
Children: [],
|
|
};
|
|
|
|
projectRow.Children.push(expCatRow);
|
|
if (masterProjectRow) {
|
|
masterProjectRow.Children.push(projectRow)
|
|
teamRow.Children.push(masterProjectRow);
|
|
}
|
|
else {
|
|
teamRow.Children.push(projectRow);
|
|
}
|
|
result.push(teamRow);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
},
|
|
findAssignedRows: function (assignedRows, teamId, projectId, expCatId) {
|
|
if (!teamId || !projectId || !expCatId || !assignedRows || !assignedRows.length)
|
|
return null;
|
|
|
|
var result = {
|
|
teamRow: null,
|
|
masterProjectRow: null,
|
|
projectRow: null,
|
|
expCatRow: null
|
|
};
|
|
|
|
if (assignedRows && angular.isArray(assignedRows)) {
|
|
// Get project rows (master & part)
|
|
var projectRowsInfo = this.getProjectRows(assignedRows, projectId);
|
|
result.masterProjectRow = projectRowsInfo && projectRowsInfo.masterProjectRow ? projectRowsInfo.masterProjectRow : null;
|
|
result.projectRow = projectRowsInfo && projectRowsInfo.projectRow ? projectRowsInfo.projectRow : null;
|
|
|
|
if (result.projectRow && result.projectRow.Children) {
|
|
result.expCatRow = this.findRowInCollection(result.projectRow.Children, expCatId);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
},
|
|
// set Cells values based on new values and apply AVG mode if necessary
|
|
setCellsValues4Row: function (rowObject, weekCellIndex, monthCellIndex, isUOMHours, isAvgMode, monthWeeksCount) {
|
|
var row = rowObject.Row;
|
|
var collectionNames = this.getRowCollectionNames(rowObject);
|
|
// recursively set Cells properties for al nested rows
|
|
if (rowObject.Children && rowObject.Children.length > 0) {
|
|
for (var i = 0; i < rowObject.Children.length; i++) {
|
|
this.setCellsValues4Row(rowObject.Children[i], weekCellIndex, monthCellIndex, isUOMHours, isAvgMode, monthWeeksCount);
|
|
}
|
|
}
|
|
if (isUOMHours) {
|
|
row.Cells[weekCellIndex] = row[collectionNames.hoursCollectionName][weekCellIndex];
|
|
row.Cells[monthCellIndex] = row[collectionNames.hoursCollectionName][monthCellIndex];
|
|
row.TotalValue = row[collectionNames.hoursTotal];
|
|
}
|
|
else {
|
|
row.Cells[weekCellIndex] = row[collectionNames.resourcesCollectionName][weekCellIndex];
|
|
row.Cells[monthCellIndex] = isAvgMode ?
|
|
monthWeeksCount == 0 ? 0 : Math.roundQuantity(row[collectionNames.resourcesCollectionName][monthCellIndex] / monthWeeksCount) :
|
|
row[collectionNames.resourcesCollectionName][monthCellIndex];
|
|
row.TotalValue = isAvgMode ?
|
|
row.VisibleCellsCount == 0 ? 0 : Math.roundQuantity(row[collectionNames.resourceTotal] / row.VisibleCellsCount) :
|
|
row[collectionNames.resourceTotal];
|
|
}
|
|
},
|
|
refreshProjectStyles: function (rows, header, filter, projectRow, data) {
|
|
if (!projectRow || typeof data !== 'object' || !angular.isArray(data)) {
|
|
return;
|
|
}
|
|
|
|
var rowToUpdateStylesIn = projectRow;
|
|
if (projectRow.Level && (projectRow.Level > 1)) {
|
|
// For master project perform style updates for entire master project
|
|
var projectRowsInfo = this.getProjectRows(rows, projectRow.Id);
|
|
|
|
if (projectRowsInfo && projectRowsInfo.masterProjectRow) {
|
|
rowToUpdateStylesIn = projectRowsInfo.masterProjectRow;
|
|
}
|
|
}
|
|
|
|
this.updateProjectStyles(header, filter, [rowToUpdateStylesIn], data);
|
|
},
|
|
categoryRowsWereAdded: function(changedCellsData) {
|
|
if (!changedCellsData || !angular.isArray(changedCellsData))
|
|
return false;
|
|
|
|
var result = false;
|
|
for (var index = 0; index < changedCellsData.length; index++) {
|
|
if (changedCellsData[index] && changedCellsData[index].Added) {
|
|
result = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
}
|
|
|
|
return service;
|
|
}]);
|
|
uiDirectives.directive("gridRow", ['$templateCache', '$compile', function ($templateCache, $compile) {
|
|
var rowCounter = 0;
|
|
var linkedCounter = 0;
|
|
var directiveDefinitionObject = {
|
|
scope: {
|
|
'InitRow': '=gridRowInit' // indicates whether to render the row or not
|
|
},
|
|
link: function (scope, element, attrs, ctrl, transclude) {
|
|
//console.log('scope.InitRow: ' + scope.InitRow + '|' + attrs.gridRowInit);
|
|
// function which gets template content, compiles it and attaches it to the DOM
|
|
var buildNodes = function (value) {
|
|
if (value) {
|
|
linkedCounter = linkedCounter + 1;
|
|
// console.log('gridRow linked: ' + linkedCounter);
|
|
var tmpl = angular.element($templateCache.get(attrs.templateurl));
|
|
$newNodes = $compile(tmpl)(scope.$parent);
|
|
//element.replaceWith(element, $newNodes);
|
|
element.after($newNodes);
|
|
}
|
|
};
|
|
// function which destroys the DOM created by directive
|
|
var cleanup = function () {
|
|
angular.element($newNodes).remove();
|
|
};
|
|
|
|
var $newNodes;
|
|
// postpone building the DOM until gridRowInit=true if gridRowInit attribute is set
|
|
if (attrs.gridRowInit) {
|
|
scope.$watch('InitRow', function (newValue, oldValue) {
|
|
//console.log('watch fired with value: ' + newValue);
|
|
if ($newNodes === undefined && newValue === true)
|
|
buildNodes(true); // compile and attach DOM only if gridRowInit attribute expression is set to true
|
|
});
|
|
} else { // else (gridRowInit attribute is not set)
|
|
buildNodes(true);
|
|
}
|
|
// hide original tr
|
|
element.addClass('ng-hide');
|
|
|
|
scope.$on('$destroy', function (event) {
|
|
cleanup();
|
|
});
|
|
rowCounter = rowCounter + 1;
|
|
// console.log('gridRow link ' + rowCounter);
|
|
//console.log(scope.$parent);
|
|
}
|
|
};
|
|
return directiveDefinitionObject;
|
|
}]); |