1558 lines
65 KiB
JavaScript
1558 lines
65 KiB
JavaScript
'use strict';
|
|
app
|
|
.controller('activityCalendarByTeamController', ['$scope', '$q', 'activityCalendarService', 'teamInfoService', 'activityCalendarUIService', 'dataSources', 'hoursResourcesConverter', '$filter', 'roundService', 'calculateDistributionService', function ($scope, $q, activityCalendarService, teamInfoService, activityCalendarUIService, dataSources, hoursResourcesConverter, $filter, roundService, calculateDistributionService) {
|
|
var commonErrorMessage = 'An error occurred while processing your request. Please, try again later.';
|
|
var calendarDataUrl = '/CapacityManagement/GetActivityCalendar';
|
|
var projectExpendituresCache = {}; // cache for aggregated ECs by project for "group by teams" mode
|
|
var nonProjectTimeCache = {}; // cache for non-project time data
|
|
var nonProjectTimeCategoryCache = {}; // cache for non-project time categories data
|
|
|
|
$scope.ViewModel = {
|
|
DisplayMode: null,
|
|
Header: null,
|
|
Rows: null,
|
|
NoTeamRow: null,
|
|
TotalRow: null,
|
|
RemainingCapacityRow: null,
|
|
NonProjectTimeTotalRows: null,
|
|
DataLoaded: false
|
|
};
|
|
$scope.Filter = null;
|
|
|
|
/* Event listeners */
|
|
$scope.$on('ac.rebuild-calendar', rebuildCalendarHandler);
|
|
$scope.$on('ac.options-changed', optionsChangedHandler);
|
|
|
|
/* Methods for interaction with view */
|
|
$scope.init = function (filter, displayMode) {
|
|
rebuildCalendarHandler(null, filter, displayMode);
|
|
};
|
|
$scope.toggleMonth = function (monthIndex) {
|
|
$scope.ViewModel.Header.toggleMonth(monthIndex);
|
|
};
|
|
$scope.toggleRow = function (uiRow, $event) {
|
|
hideRedundantPopovers();
|
|
|
|
if ($($event.target).parents('.menuGroup').length > 0)
|
|
return;
|
|
|
|
// to-do: we should keep collapsed state in data-model items between possible future postbacks
|
|
var dataModel = activityCalendarService.dataItemStub || {};
|
|
activityCalendarUIService.toggleRow(uiRow, dataModel);
|
|
};
|
|
$scope.ToggleStatus = function ($event, projectId, scenario) {
|
|
var btn = $($event.target).closest('.popover-warning');
|
|
if (btn.length == 1) {
|
|
activityCalendarUIService.toggleStatus(btn, projectId, scenario)
|
|
.then(function (result) {
|
|
if (!!result)
|
|
rebuildCalendarHandler($event, $scope.Filter, $scope.ViewModel.DisplayMode, true);
|
|
})
|
|
.then(null, function () {
|
|
showErrorModal(null, 'An error occurred while toggling scenario status', '000009');
|
|
});
|
|
}
|
|
};
|
|
$scope.AddScenario = function ($event, projectId) {
|
|
activityCalendarUIService.openCreateScenarioWz(projectId);
|
|
};
|
|
$scope.formatPeopleResourceOption = function (result, container, query, escapeMarkup) {
|
|
return activityCalendarUIService.formatPeopleResourceOption(result.element);
|
|
};
|
|
$scope.assignResource = function (projectId, expCatRow, resourceId, teamId) {
|
|
if (!projectId || !expCatRow || !resourceId) {
|
|
return;
|
|
}
|
|
var rowToAddResource = expCatRow;
|
|
if (teamId) {
|
|
for (var teamKey in expCatRow.Children) {
|
|
if (teamId != expCatRow.Children[teamKey].Id)
|
|
continue;
|
|
rowToAddResource = expCatRow.Children[teamKey];
|
|
}
|
|
}
|
|
|
|
activityCalendarUIService.assignResource($scope.Filter, $scope.ViewModel.Header, projectId, expCatRow, resourceId, rowToAddResource);
|
|
dataChanged();
|
|
};
|
|
$scope.assignTeam = function (projectRow, expCatRow) {
|
|
if (!projectRow || !expCatRow || !expCatRow.TeamToAssignId) {
|
|
return;
|
|
}
|
|
// prepare extendModel to keep new added team info
|
|
var extendedModel = {
|
|
Expenditures2Display: {}
|
|
};
|
|
if (projectRow.Children) {
|
|
for (var ecIndex = 0; ecIndex < projectRow.Children.length; ecIndex++) {
|
|
var ecRow = projectRow.Children[ecIndex];
|
|
if (ecRow && ecRow.AvailableTeams) {
|
|
for (var tIndex = 0; tIndex < ecRow.AvailableTeams.length; tIndex++) {
|
|
if (ecRow.AvailableTeams[tIndex].Id == expCatRow.TeamToAssignId) {
|
|
extendedModel.Expenditures2Display[ecRow.Id] = ecRow.Id;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// assign new team to DAL model
|
|
activityCalendarUIService.assignTeam($scope.Filter, $scope.ViewModel.Header, projectRow.Id, expCatRow.TeamToAssignId, extendedModel);
|
|
// add new team row to each EC of the project if it is available for assignment
|
|
if (projectRow.Children) {
|
|
for (var ecIndex = 0; ecIndex < projectRow.Children.length; ecIndex++) {
|
|
var ecRow = projectRow.Children[ecIndex];
|
|
if (ecRow && ecRow.AvailableTeams) {
|
|
for (var tIndex = 0; tIndex < ecRow.AvailableTeams.length; tIndex++) {
|
|
if (ecRow.AvailableTeams[tIndex].Id == expCatRow.TeamToAssignId) {
|
|
createAndFillNewEcTeamRow(projectRow, ecRow, expCatRow.TeamToAssignId);
|
|
// expand ec row
|
|
if (ecRow.Collapsed)
|
|
activityCalendarUIService.toggleRow(ecRow, {}, false);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
refreshProjectAvailableTeams(projectRow);
|
|
expCatRow.TeamToAssignId = null;
|
|
dataChanged();
|
|
};
|
|
function refreshProjectAvailableTeams(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 = getAvailableTeams(projectRow.Id, ecRow.Id);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
$scope.checkResourceValue = function (projectRow, expCatRow, resRow, $index, $data) {
|
|
if (!projectRow || !expCatRow || !resRow) {
|
|
return;
|
|
}
|
|
|
|
var filter = $scope.Filter,
|
|
header = $scope.ViewModel.Header,
|
|
isUOMHours = $scope.ViewModel.DisplayMode.IsUOMHours,
|
|
isAvgMode = $scope.ViewModel.DisplayMode.IsAvgMode(),
|
|
parentRow = expCatRow || {};
|
|
|
|
|
|
var result = activityCalendarUIService.checkResourceValue(filter, header, projectRow.Id, parentRow, resRow.Id, resRow, isUOMHours, isAvgMode, $index, $data, false);
|
|
if (typeof result === 'object' && angular.isArray(result) && result.length > 0) {
|
|
triggerResourceChanged(result);
|
|
recalculateResourceAvailability(resRow.Id);
|
|
refreshResourceStyles(resRow.Id, result);
|
|
refreshProjectStyles(projectRow, result);
|
|
dataChanged();
|
|
|
|
return false;
|
|
}
|
|
|
|
return result;
|
|
};
|
|
$scope.checkTeamValueUnassigned = function (projectRow, expCatRow, teamRow, $index, $data) {
|
|
if (!projectRow || !expCatRow || !teamRow) {
|
|
return;
|
|
}
|
|
|
|
var filter = $scope.Filter,
|
|
header = $scope.ViewModel.Header,
|
|
isUOMHours = $scope.ViewModel.DisplayMode.IsUOMHours,
|
|
isAvgMode = $scope.ViewModel.DisplayMode.IsAvgMode(),
|
|
startDate = projectRow.ActiveScenario.StartDate,
|
|
endDate = projectRow.ActiveScenario.EndDate;
|
|
|
|
var result = activityCalendarUIService.checkTeamValue(filter, header, projectRow.Id, expCatRow, teamRow.Id, teamRow, isUOMHours, isAvgMode, $index, $data);
|
|
onChangeTeamDataUnassigned(filter, header, projectRow, expCatRow, teamRow, startDate, endDate, isUOMHours, isAvgMode, result);
|
|
return false;
|
|
};
|
|
$scope.checkResourceGrandTotalValue = function (projectRow, expCatRow, resRow, $data) {
|
|
if (!projectRow || !projectRow.ActiveScenario || !expCatRow || !resRow) {
|
|
return;
|
|
}
|
|
|
|
var filter = $scope.Filter,
|
|
header = $scope.ViewModel.Header,
|
|
isUOMHours = $scope.ViewModel.DisplayMode.IsUOMHours,
|
|
isAvgMode = $scope.ViewModel.DisplayMode.IsAvgMode(),
|
|
parentRow = expCatRow || {};
|
|
|
|
|
|
var result = activityCalendarUIService.checkResourceGrandTotalValue(filter, header, projectRow.Id, projectRow.ActiveScenario.StartDate, projectRow.ActiveScenario.EndDate,
|
|
parentRow, resRow.Id, resRow, isUOMHours, isAvgMode, $data, false);
|
|
if (typeof result === 'object' && angular.isArray(result) && result.length > 0) {
|
|
triggerResourceChanged(result);
|
|
recalculateResourceAvailability(resRow.Id);
|
|
refreshResourceStyles(resRow.Id, result);
|
|
refreshProjectStyles(projectRow, result);
|
|
dataChanged();
|
|
|
|
return false;
|
|
}
|
|
|
|
return result;
|
|
};
|
|
$scope.checkTeamGrandTotalValueUnassigned = function (projectRow, expCatRow, teamRow, $data) {
|
|
if (!projectRow || !projectRow.ActiveScenario || !expCatRow || !teamRow) {
|
|
return;
|
|
}
|
|
|
|
var filter = $scope.Filter,
|
|
header = $scope.ViewModel.Header,
|
|
isUOMHours = $scope.ViewModel.DisplayMode.IsUOMHours,
|
|
isAvgMode = $scope.ViewModel.DisplayMode.IsAvgMode(),
|
|
startDate = projectRow.ActiveScenario.StartDate,
|
|
endDate = projectRow.ActiveScenario.EndDate;
|
|
|
|
var result = activityCalendarUIService.checkTeamGrandTotalValue(filter, header, projectRow.Id, startDate, endDate, expCatRow, teamRow.Id, teamRow, isUOMHours, isAvgMode, $data);
|
|
onChangeTeamDataUnassigned(filter, header, projectRow, expCatRow, teamRow, startDate, endDate, isUOMHours, isAvgMode, result);
|
|
return false;
|
|
};
|
|
$scope.takeRemaining = function (projectRow, expCatRow, resRow) {
|
|
if (!projectRow || !projectRow.ActiveScenario || !expCatRow || !resRow) {
|
|
return;
|
|
}
|
|
|
|
var filter = $scope.Filter,
|
|
header = $scope.ViewModel.Header,
|
|
isUOMHours = $scope.ViewModel.DisplayMode.IsUOMHours,
|
|
isAvgMode = $scope.ViewModel.DisplayMode.IsAvgMode(),
|
|
parentRow = expCatRow || {};
|
|
|
|
|
|
var result = activityCalendarUIService.takeRemainingCapacity(filter, header, projectRow.Id, projectRow.ActiveScenario.StartDate, projectRow.ActiveScenario.EndDate,
|
|
parentRow, resRow.Id, resRow, isUOMHours, isAvgMode);
|
|
if (typeof result === 'object' && angular.isArray(result) && result.length > 0) {
|
|
triggerResourceChanged(result);
|
|
recalculateResourceAvailability(resRow.Id);
|
|
refreshResourceStyles(resRow.Id, result);
|
|
refreshProjectStyles(projectRow, result);
|
|
dataChanged();
|
|
}
|
|
};
|
|
$scope.takeAll = function (projectRow, expCatRow, resRow) {
|
|
if (!projectRow || !projectRow.ActiveScenario || !expCatRow || !resRow) {
|
|
return;
|
|
}
|
|
|
|
var filter = $scope.Filter,
|
|
header = $scope.ViewModel.Header,
|
|
isUOMHours = $scope.ViewModel.DisplayMode.IsUOMHours,
|
|
isAvgMode = $scope.ViewModel.DisplayMode.IsAvgMode(),
|
|
parentRow = expCatRow || {};
|
|
|
|
|
|
var result = activityCalendarUIService.takeFullCapacity(filter, header, projectRow.Id, projectRow.ActiveScenario.StartDate, projectRow.ActiveScenario.EndDate,
|
|
parentRow, resRow.Id, resRow, isUOMHours, isAvgMode);
|
|
if (typeof result === 'object' && angular.isArray(result) && result.length > 0) {
|
|
triggerResourceChanged(result);
|
|
recalculateResourceAvailability(resRow.Id);
|
|
refreshResourceStyles(resRow.Id, result);
|
|
refreshProjectStyles(projectRow, result);
|
|
dataChanged();
|
|
}
|
|
};
|
|
$scope.zeroResource = function (projectRow, expCatRow, resRow) {
|
|
if (!projectRow || !projectRow.ActiveScenario || !expCatRow || !resRow) {
|
|
return;
|
|
}
|
|
|
|
bootbox.confirm({
|
|
message: "Are you sure you want to fill this resource quantities with 0s?",
|
|
callback: function (result) {
|
|
$scope.$apply(function () {
|
|
if (result) {
|
|
var filter = $scope.Filter,
|
|
header = $scope.ViewModel.Header,
|
|
isUOMHours = $scope.ViewModel.DisplayMode.IsUOMHours,
|
|
isAvgMode = $scope.ViewModel.DisplayMode.IsAvgMode(),
|
|
parentRow = expCatRow || {};
|
|
|
|
|
|
var changedCells = activityCalendarUIService.zeroResource(filter, header, projectRow.Id, projectRow.ActiveScenario.StartDate, projectRow.ActiveScenario.EndDate,
|
|
parentRow, resRow.Id, resRow, isUOMHours, isAvgMode);
|
|
if (typeof changedCells === 'object' && angular.isArray(changedCells) && changedCells.length > 0) {
|
|
triggerResourceChanged(changedCells);
|
|
recalculateResourceAvailability(resRow.Id);
|
|
refreshResourceStyles(resRow.Id, changedCells);
|
|
refreshProjectStyles(projectRow, changedCells);
|
|
dataChanged();
|
|
}
|
|
}
|
|
});
|
|
},
|
|
className: "bootbox-sm"
|
|
});
|
|
};
|
|
$scope.removeResource = function (projectRow, expCatRow, teamRow, resRow, $index) {
|
|
if (!projectRow || !projectRow.ActiveScenario || !expCatRow || !resRow) {
|
|
return;
|
|
}
|
|
|
|
bootbox.confirm({
|
|
message: "Are you sure you want to remove this resource?",
|
|
callback: function (result) {
|
|
$scope.$apply(function () {
|
|
if (result) {
|
|
var filter = $scope.Filter,
|
|
header = $scope.ViewModel.Header,
|
|
isUOMHours = $scope.ViewModel.DisplayMode.IsUOMHours,
|
|
isAvgMode = $scope.ViewModel.DisplayMode.IsAvgMode(),
|
|
expCatId = (teamRow && teamRow.ExpenditureCategoryId) || expCatRow.Id,
|
|
parentRow = expCatRow;
|
|
|
|
var changedCells = activityCalendarUIService.cleanAndRemoveResource(filter, header, projectRow.Id, projectRow.ActiveScenario.StartDate,
|
|
projectRow.ActiveScenario.EndDate, expCatId, parentRow, resRow.Id, resRow, isUOMHours, isAvgMode, $index);
|
|
if (typeof changedCells === 'object' && angular.isArray(changedCells) && changedCells.length > 0) {
|
|
triggerResourceChanged(changedCells);
|
|
recalculateResourceAvailability(resRow.Id);
|
|
refreshResourceStyles(resRow.Id, changedCells);
|
|
refreshProjectStyles(projectRow, changedCells);
|
|
dataChanged();
|
|
}
|
|
}
|
|
});
|
|
},
|
|
className: "bootbox-sm"
|
|
});
|
|
};
|
|
$scope.checkNptResourceValue = function (teamRow, nptTotalRow, nptCatRow, nptItemRow, nptResourceRow, $index, $data) {
|
|
if (!teamRow || !nptTotalRow || !nptCatRow || !nptItemRow || !nptResourceRow)
|
|
return;
|
|
|
|
var filter = $scope.Filter,
|
|
header = $scope.ViewModel.Header,
|
|
isUOMHours = $scope.ViewModel.DisplayMode.IsUOMHours,
|
|
isAvgMode = $scope.ViewModel.DisplayMode.IsAvgMode();
|
|
|
|
var rollupRows = [];
|
|
rollupRows.push(nptTotalRow);
|
|
rollupRows.push(teamRow);
|
|
rollupRows.push(nptCatRow);
|
|
rollupRows.push(nptItemRow);
|
|
rollupRows.push($scope.ViewModel.TotalRow);
|
|
|
|
var result = activityCalendarUIService.checkNptResourceValue(filter, header, $scope.ViewModel.RemainingCapacityRow,
|
|
rollupRows, nptCatRow, nptItemRow, nptResourceRow, isUOMHours, isAvgMode, $index, $data);
|
|
|
|
// if result is not false it is incorrect one and we do not need to recalculate availability in this case
|
|
if (typeof result === 'object' && angular.isArray(result) && result.length > 0) {
|
|
triggerNptResourceChanged(result);
|
|
recalculateResourceAvailability(nptResourceRow.Id);
|
|
dataChanged();
|
|
return false;
|
|
}
|
|
|
|
return result;
|
|
};
|
|
$scope.checkTeamWideNptValue = function (teamRow, nptTotalRow, nptCatRow, nptItemRow, $index, $data) {
|
|
if (!teamRow || !nptTotalRow || !nptCatRow || !nptItemRow)
|
|
return;
|
|
|
|
var filter = $scope.Filter,
|
|
header = $scope.ViewModel.Header,
|
|
isUOMHours = $scope.ViewModel.DisplayMode.IsUOMHours,
|
|
isAvgMode = $scope.ViewModel.DisplayMode.IsAvgMode();
|
|
|
|
var rollupRows = [];
|
|
rollupRows.push(nptTotalRow);
|
|
rollupRows.push(companyRow);
|
|
rollupRows.push(nptCatRow);
|
|
rollupRows.push(nptItemRow);
|
|
rollupRows.push($scope.ViewModel.TotalRow);
|
|
|
|
var result = activityCalendarUIService.checkTeamWideNptValue(filter, header, $scope.ViewModel.RemainingCapacityRow,
|
|
rollupRows, nptCatRow, nptItemRow, isUOMHours, isAvgMode, $index, $data);
|
|
|
|
// if result is not false it is incorrect one and we do not need to recalculate availability in this case
|
|
if (typeof result === 'object' && angular.isArray(result) && result.length > 0) {
|
|
triggerNptResourceChanged(result);
|
|
|
|
// Recalculate resource availability for all related resources
|
|
for (var i = 0; i < result.length; i++) {
|
|
recalculateResourceAvailability(result[i].ResourceId);
|
|
}
|
|
|
|
dataChanged();
|
|
return false;
|
|
}
|
|
|
|
return result;
|
|
};
|
|
$scope.takeTeamRemainingUnassigned = function (projectRow, expCatRow, teamRow) {
|
|
if (!projectRow || !projectRow.ActiveScenario || !expCatRow || !teamRow) {
|
|
return;
|
|
}
|
|
|
|
var filter = $scope.Filter,
|
|
header = $scope.ViewModel.Header,
|
|
isUOMHours = $scope.ViewModel.DisplayMode.IsUOMHours,
|
|
isAvgMode = $scope.ViewModel.DisplayMode.IsAvgMode(),
|
|
startDate = projectRow.ActiveScenario.StartDate,
|
|
endDate = projectRow.ActiveScenario.EndDate;
|
|
|
|
var result = activityCalendarUIService.takeTeamRemainingCapacity(filter, header, projectRow.Id, startDate, endDate,
|
|
expCatRow, teamRow, isUOMHours, isAvgMode);
|
|
onChangeTeamDataUnassigned(filter, header, projectRow, expCatRow, teamRow, startDate, endDate, isUOMHours, isAvgMode, result);
|
|
};
|
|
$scope.takeTeamAllUnassigned = function (projectRow, expCatRow, teamRow) {
|
|
if (!projectRow || !projectRow.ActiveScenario || !expCatRow || !teamRow)
|
|
return;
|
|
|
|
var filter = $scope.Filter,
|
|
header = $scope.ViewModel.Header,
|
|
isUOMHours = $scope.ViewModel.DisplayMode.IsUOMHours,
|
|
isAvgMode = $scope.ViewModel.DisplayMode.IsAvgMode(),
|
|
startDate = projectRow.ActiveScenario.StartDate,
|
|
endDate = projectRow.ActiveScenario.EndDate;
|
|
|
|
var result = activityCalendarUIService.takeTeamFullCapacity(filter, header, projectRow.Id, startDate, endDate,
|
|
expCatRow, teamRow, isUOMHours, isAvgMode);
|
|
onChangeTeamDataUnassigned(filter, header, projectRow, expCatRow, teamRow, startDate, endDate, isUOMHours, isAvgMode, result);
|
|
};
|
|
$scope.zeroTeamUnassigned = function (projectRow, expCatRow, teamRow) {
|
|
if (!projectRow || !projectRow.ActiveScenario || !expCatRow || !teamRow) {
|
|
return;
|
|
}
|
|
|
|
bootbox.confirm({
|
|
message: "Are you sure you want to fill this Team allocations with 0s?",
|
|
callback: function (result) {
|
|
$scope.$apply(function () {
|
|
if (result) {
|
|
var filter = $scope.Filter,
|
|
header = $scope.ViewModel.Header,
|
|
isUOMHours = $scope.ViewModel.DisplayMode.IsUOMHours,
|
|
isAvgMode = $scope.ViewModel.DisplayMode.IsAvgMode(),
|
|
startDate = projectRow.ActiveScenario.StartDate,
|
|
endDate = projectRow.ActiveScenario.EndDate;
|
|
|
|
var changedCells = activityCalendarUIService.zeroTeam(filter, header, projectRow.Id, startDate,
|
|
endDate, expCatRow, teamRow, isUOMHours, isAvgMode);
|
|
onChangeTeamDataUnassigned(filter, header, projectRow, expCatRow, teamRow, startDate, endDate, isUOMHours, isAvgMode, changedCells);
|
|
}
|
|
});
|
|
},
|
|
className: "bootbox-sm"
|
|
});
|
|
};
|
|
$scope.removeTeamUnassigned = function (prjRow, expCatRow, teamRow, $index) {
|
|
if (!prjRow || !prjRow.ActiveScenario || !expCatRow || !teamRow) {
|
|
return;
|
|
}
|
|
|
|
bootbox.confirm({
|
|
message: "Are you sure you want to remove this team?",
|
|
callback: function (result) {
|
|
$scope.$apply(function () {
|
|
if (result) {
|
|
var filter = $scope.Filter,
|
|
header = $scope.ViewModel.Header,
|
|
isUOMHours = $scope.ViewModel.DisplayMode.IsUOMHours,
|
|
isAvgMode = $scope.ViewModel.DisplayMode.IsAvgMode(),
|
|
teamId = teamRow.Id,
|
|
startDate = prjRow.ActiveScenario.StartDate,
|
|
endDate = prjRow.ActiveScenario.EndDate;
|
|
|
|
var changedCellsByExpCats = activityCalendarUIService.cleanAndRemoveTeam(filter, header, prjRow, teamId, startDate,
|
|
endDate, isUOMHours, isAvgMode);
|
|
|
|
if ((typeof changedCellsByExpCats === 'object') && (Object.keys(changedCellsByExpCats).length > 0)) {
|
|
for (var expCatId in changedCellsByExpCats) {
|
|
var currentExpCatRow = changedCellsByExpCats[expCatId].ExpCatRow;
|
|
var changedCells = changedCellsByExpCats[expCatId].ChangedCells;
|
|
|
|
onChangeTeamDataUnassigned(filter, header, prjRow, currentExpCatRow, teamRow, startDate, endDate, isUOMHours, isAvgMode, changedCells);
|
|
}
|
|
}
|
|
|
|
refreshProjectAvailableTeams(prjRow);
|
|
}
|
|
});
|
|
},
|
|
className: "bootbox-sm"
|
|
});
|
|
};
|
|
$scope.watchKeyInput = function (t) {
|
|
activityCalendarUIService.watchKeyInput(t);
|
|
};
|
|
$scope.onTxtBlur = function (t) {
|
|
activityCalendarUIService.onTxtBlur(t);
|
|
};
|
|
|
|
/* Event handlers */
|
|
function rebuildCalendarHandler(event, filter, displayMode, forceDataReload, successCallback) {
|
|
// if filter is changed we need to invalidate cache
|
|
if ($scope.Filter && !activityCalendarService.cacheKeysEqual($scope.Filter, filter)) {
|
|
activityCalendarService.invalidateCache($scope.Filter);
|
|
}
|
|
|
|
$scope.ViewModel.DataLoaded = false;
|
|
$scope.ViewModel.DisplayMode = angular.copy(displayMode || {});
|
|
$scope.Filter = angular.copy(filter || {});
|
|
|
|
blockUI();
|
|
|
|
hoursResourcesConverter.load(forceDataReload).then(function () {
|
|
var loadCalendarTask = activityCalendarService.getActivityCalendar(calendarDataUrl, filter, forceDataReload);
|
|
var nptCategories = dataSources.getNonProjectTimeCategories();
|
|
var cachedTemplates = activityCalendarUIService.cacheTemplates();
|
|
var dateRangeTask = dataSources.loadAvailableDateRange();
|
|
var allTasks = [loadCalendarTask, nptCategories, dateRangeTask].concat(cachedTemplates);
|
|
|
|
$q.all(allTasks)
|
|
.then(function (asyncResults) {
|
|
var calendar = asyncResults[0];
|
|
if (calendar.Teams) {
|
|
var teamIds = Object.keys(calendar.Teams);
|
|
var promise = dataSources.loadResourcesByTeamIds(teamIds)
|
|
.then(function (result) {
|
|
return asyncResults;
|
|
});
|
|
return promise;
|
|
} else {
|
|
return asyncResults;
|
|
}
|
|
})
|
|
.then(function (asyncResults) {
|
|
var calendar = asyncResults[0];
|
|
nonProjectTimeCategoryCache = asyncResults[1];
|
|
|
|
createViewModel(calendar);
|
|
fillViewModelWithData(calendar);
|
|
initBottomPart();
|
|
toggleParts();
|
|
toggleSortBy();
|
|
toggleHoursResourcesMode();
|
|
toggleBarsValuesMode();
|
|
toggleMonths();
|
|
$scope.$emit('ac.workflowactionsloaded');
|
|
unblockUI();
|
|
|
|
if (typeof successCallback === 'function') {
|
|
successCallback(calendar);
|
|
}
|
|
|
|
if (angular.isObject(calendar))
|
|
$scope.ViewModel.DataLoaded = true;
|
|
})
|
|
.then(null, function () {
|
|
unblockUI();
|
|
showErrorModal('Oops!', commonErrorMessage);
|
|
});
|
|
}).then(null, function () {
|
|
unblockUI();
|
|
showErrorModal('Oops!', commonErrorMessage);
|
|
});
|
|
};
|
|
function optionsChangedHandler(event, displayMode, key, newValue) {
|
|
$scope.ViewModel.DisplayMode = displayMode || {};
|
|
switch (key) {
|
|
case 'uomMode':
|
|
toggleHoursResourcesMode();
|
|
$scope.$broadcast('changeUOMMode', newValue);
|
|
break;
|
|
case 'capBarMode':
|
|
toggleBarsValuesMode();
|
|
break;
|
|
case 'defaultView':
|
|
toggleMonths();
|
|
break;
|
|
case 'sortBy':
|
|
case 'sortOrder':
|
|
toggleSortBy();
|
|
break;
|
|
case 'showParts':
|
|
toggleParts();
|
|
toggleSortBy();
|
|
toggleHoursResourcesMode();
|
|
break;
|
|
case 'capacityView':
|
|
toggleCapacityType();
|
|
$scope.$broadcast('capacityViewChanged', newValue);
|
|
break;
|
|
default:
|
|
break;
|
|
};
|
|
};
|
|
function dataChanged() {
|
|
$scope.$emit('ac.calendarDataChanged');
|
|
};
|
|
function onChangeTeamDataUnassigned(filter, header, projectRow, expCatRow, teamRow, startDate, endDate, isUOMHours, isAvgMode, changedCells) {
|
|
if (typeof changedCells === 'object' && angular.isArray(changedCells) && changedCells.length > 0) {
|
|
// Find out, if some categories where added to teams at bottom part
|
|
var needRecreateRows = activityCalendarUIService.categoryRowsWereAdded(changedCells);
|
|
|
|
if (needRecreateRows)
|
|
// Categories where added, we need to redraw all rows in bottom part
|
|
refreshBottomPart();
|
|
else
|
|
triggerTeamChanged(changedCells);
|
|
|
|
updateTotalRows(changedCells, teamRow, expCatRow, projectRow.Id);
|
|
refreshNoTeamProjectStyles(projectRow, changedCells);
|
|
|
|
dataChanged();
|
|
}
|
|
};
|
|
function updateTotalRows(changedCells, teamRow, expCatRow, projectId) {
|
|
if (typeof changedCells !== 'object' || !angular.isArray(changedCells) || !expCatRow || !projectId || !teamRow) {
|
|
return;
|
|
}
|
|
var expenditureCategoryId = expCatRow.Id;
|
|
// gather rows to rollup changes
|
|
var unassignedRowsToUpdate = getUnassignedRowsToUpdate(projectId, expenditureCategoryId) || [];
|
|
var assignedRowsToUpdate = getAssignedRowsToUpdate(teamRow.Id, projectId, expenditureCategoryId) || [];
|
|
if ((assignedRowsToUpdate.length <= 0) && (unassignedRowsToUpdate.length <= 0)) {
|
|
return;
|
|
}
|
|
|
|
var header = $scope.ViewModel.Header;
|
|
var isUOMHours = $scope.ViewModel.DisplayMode.IsUOMHours;
|
|
var isAvgMode = $scope.ViewModel.DisplayMode.IsAvgMode();
|
|
var project = activityCalendarService.getProjectById($scope.Filter, projectId);
|
|
var weekEndings = changedCells.map(function (cell) { return cell.WeekEnding; });
|
|
var remainingNeed = activityCalendarService.getRemainingNeedAllocations4Scenario($scope.Filter, Object.keys(project.Teams), project.ActiveScenario.Id, [expenditureCategoryId], weekEndings) || {};
|
|
var expCatCollectionNames = activityCalendarUIService.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 < unassignedRowsToUpdate.length; rowIndex++) {
|
|
var row = unassignedRowsToUpdate[rowIndex];
|
|
activityCalendarUIService.rollupRemainingRow(row, -(rollupRemainingNeedDeltaHours || 0), -(rollupRemainingNeedDeltaResources || 0), false, cell.WeekIndex, month.SelfIndexInWeeks);
|
|
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 = activityCalendarUIService.rollupRemainingRow(assignedRowsToUpdate[rowIndex], (rollupTeamAllocationDeltaHours || 0), (rollupTeamAllocationDeltaResources || 0), false, cell.WeekIndex, month.SelfIndexInWeeks);
|
|
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) {
|
|
refreshProjectStyles(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];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
function getUnassignedRowsToUpdate(projectId, expCatId) {
|
|
if (!projectId || !expCatId || !$scope.ViewModel.NoTeamRow || !$scope.ViewModel.NoTeamRow.Children)
|
|
return [];
|
|
|
|
// Get corresponding row for unassigned project
|
|
var result = [];
|
|
var foundRowsToRollup = findUnassignedRows(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: $scope.ViewModel.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;
|
|
};
|
|
function getAssignedRowsToUpdate(teamId, projectId, expCatId) {
|
|
if (!teamId || !projectId || !expCatId || !$scope.ViewModel.Rows || !$scope.ViewModel.Rows.length) {
|
|
return [];
|
|
}
|
|
|
|
var foundRowsToRollup = findAssignedRows(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;
|
|
};
|
|
|
|
function findAssignedRows(teamId, projectId, expCatId) {
|
|
if (!teamId || !projectId || !expCatId || !$scope.ViewModel.Rows || !$scope.ViewModel.Rows.length)
|
|
return null;
|
|
|
|
var result = {
|
|
teamRow: null,
|
|
masterProjectRow: null,
|
|
projectRow: null,
|
|
expCatRow: null
|
|
};
|
|
|
|
result.teamRow = activityCalendarUIService.findRowInCollection($scope.ViewModel.Rows, teamId);
|
|
|
|
if (result.teamRow && angular.isArray(result.teamRow.Children)) {
|
|
// Get project rows (master & part)
|
|
var projectRowsInfo = activityCalendarUIService.getProjectRows(result.teamRow.Children, 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 = activityCalendarUIService.findRowInCollection(result.projectRow.Children, expCatId);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
};
|
|
function findUnassignedRows(projectId, expCatId) {
|
|
if (!projectId || !expCatId || !$scope.ViewModel.NoTeamRow || !$scope.ViewModel.NoTeamRow.Children ||
|
|
!$scope.ViewModel.NoTeamRow.Children.length) {
|
|
return null;
|
|
}
|
|
|
|
var result = {
|
|
masterProjectRow: null,
|
|
projectRow: null,
|
|
expCatRow: null
|
|
};
|
|
|
|
// Find project rows (master & part)
|
|
var projectRowsInfo = activityCalendarUIService.getProjectRows($scope.ViewModel.NoTeamRow.Children, 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 = activityCalendarUIService.findRowInCollection(result.projectRow.Children, expCatId);
|
|
}
|
|
|
|
return result;
|
|
};
|
|
// set Cells values based on new values and apply AVG mode if necessary
|
|
function setCellsValues4Row(rowObject, weekCellIndex, monthCellIndex, isUOMHours, isAvgMode, monthWeeksCount) {
|
|
var row = rowObject.Row;
|
|
var collectionNames = activityCalendarUIService.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++) {
|
|
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 : roundService.roundQuantity(row[collectionNames.resourcesCollectionName][monthCellIndex] / monthWeeksCount) :
|
|
row[collectionNames.resourcesCollectionName][monthCellIndex];
|
|
row.TotalValue = isAvgMode ?
|
|
row.VisibleCellsCount == 0 ? 0 : roundService.roundQuantity(row[collectionNames.resourceTotal] / row.VisibleCellsCount) :
|
|
row[collectionNames.resourceTotal];
|
|
}
|
|
}
|
|
|
|
/* Methods for preparing data */
|
|
function removeEmptyUnassignedExpenditures(projectId, projectExpCatsCache) {
|
|
if (!projectExpCatsCache)
|
|
return;
|
|
|
|
var result = {};
|
|
var ec2TeamsExisting = {};
|
|
|
|
var expCatsInPendingTeams = activityCalendarService.getExpendituresExistInPendingTeams($scope.Filter, projectId);
|
|
|
|
for (var expCatId in projectExpCatsCache) {
|
|
// check if there is any team that contains category
|
|
if (ec2TeamsExisting[expCatId] == undefined) {
|
|
ec2TeamsExisting[expCatId] = teamInfoService.teamsWithExpenditureCategoryExist(expCatId);
|
|
}
|
|
|
|
var expCatItem = projectExpCatsCache[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;
|
|
};
|
|
function getDataModel(projects) {
|
|
if (!projects) {
|
|
return null;
|
|
}
|
|
|
|
var filterExpendituresByTeams = activityCalendarUIService.isFilteredByTeams($scope.Filter);
|
|
var result = {
|
|
NoTeam: {
|
|
Id: '',
|
|
Name: 'No Team',
|
|
Projects: {}
|
|
}
|
|
};
|
|
for (var projectId in projects) {
|
|
var project = projects[projectId];
|
|
if (project.Teams && Object.keys(project.Teams).length) {
|
|
for (var teamId in project.Teams) {
|
|
// skip newly assigned teams
|
|
var type = project.Teams[teamId].Type || activityCalendarService.TeamType.Saved;
|
|
if (type === activityCalendarService.TeamType.Pending) {
|
|
continue;
|
|
}
|
|
if (!(teamId in result)) {
|
|
var sourceTeam = teamInfoService.getById(teamId);
|
|
if (sourceTeam) {
|
|
result[teamId] = {
|
|
Id: teamId,
|
|
Name: sourceTeam.Name,
|
|
Projects: {}
|
|
};
|
|
}
|
|
}
|
|
|
|
// if for some reasons teamInfoService.getById got null
|
|
if (!(teamId in result)) {
|
|
console.error('There is no team with id = ' + teamId);
|
|
continue;
|
|
}
|
|
|
|
var projectExpenditures = activityCalendarUIService.getExpendituresWithResources4Project(project, [teamId], null, $scope.ViewModel.Header, $scope.Filter, false, filterExpendituresByTeams);
|
|
if (projectExpenditures && Object.keys(projectExpenditures).length) {
|
|
result[teamId].Projects[projectId] = {
|
|
Project: project,
|
|
Expenditures: projectExpenditures
|
|
};
|
|
}
|
|
}
|
|
}
|
|
var noTeamProject = angular.extend({}, project, { AllocationMode: activityCalendarUIService.allocationMode.remainingNeedAllocation });
|
|
var projectNoTeamsExpenditures = activityCalendarUIService.getExpenditures4Project(noTeamProject, null, null,
|
|
$scope.ViewModel.Header, $scope.Filter, true, false);
|
|
var ecKeys = Object.keys(projectNoTeamsExpenditures);
|
|
for (var i = 0; i < ecKeys.length; i++) {
|
|
if (!projectNoTeamsExpenditures[ecKeys[i]].AllowResourceAssignment && ($.inArray(ecKeys[i], $scope.Filter.ProjectRoles || []) < 0))
|
|
delete projectNoTeamsExpenditures[ecKeys[i]];
|
|
}
|
|
var projectNoTeamsExpendituresNonEmpty = removeEmptyUnassignedExpenditures(projectId, projectNoTeamsExpenditures);
|
|
if (projectNoTeamsExpendituresNonEmpty && (Object.keys(projectNoTeamsExpendituresNonEmpty).length > 0)) {
|
|
result['NoTeam'].Projects[projectId] = {
|
|
Project: noTeamProject,
|
|
Expenditures: projectNoTeamsExpendituresNonEmpty
|
|
};
|
|
}
|
|
}
|
|
|
|
// Add teams without projects to display NPT
|
|
var teams = teamInfoService.getAll();
|
|
if (teams) {
|
|
for (var teamId in teams) {
|
|
if (!(teamId in result)) {
|
|
var sourceTeam = teamInfoService.getById(teamId);
|
|
if (sourceTeam) {
|
|
result[teamId] = {
|
|
Id: teamId,
|
|
Name: sourceTeam.Name,
|
|
Projects: {}
|
|
};
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
return result;
|
|
};
|
|
function getDataModel4NewTeam(teamId, projectId, expCatId) {
|
|
if (!teamId || !projectId || !expCatId) {
|
|
return null;
|
|
}
|
|
|
|
var project = activityCalendarService.getProjectById($scope.Filter, projectId);
|
|
if (!project || !project.Teams || !(teamId in project.Teams)) {
|
|
return null;
|
|
}
|
|
|
|
var expCats = activityCalendarUIService.getExpendituresWithResources4Project(project, [teamId], [expCatId], $scope.ViewModel.Header, $scope.Filter, true, false);
|
|
if (expCats && expCats[expCatId])
|
|
return expCats[expCatId];
|
|
else
|
|
return null;
|
|
};
|
|
|
|
/* Methods for creating view models */
|
|
function createViewModel(model) {
|
|
createHeaderViewModel(model.WeekEndings);
|
|
createRowsViewModel(model);
|
|
};
|
|
function createHeaderViewModel(weekEndings) {
|
|
$scope.ViewModel.Header = (new GridHeader(weekEndings || {}).create());
|
|
};
|
|
function createRowsViewModel(model) {
|
|
$scope.ViewModel.Rows = [];
|
|
$scope.ViewModel.NonProjectTimeTotalRows = [];
|
|
|
|
if (!model || !model.Projects) {
|
|
return;
|
|
}
|
|
projectExpendituresCache = getDataModel(model.Projects);
|
|
nonProjectTimeCache = activityCalendarUIService.getNonProjectTimeDataModel(nonProjectTimeCategoryCache, $scope.DisplayMode.GroupBy.Value);
|
|
|
|
// Set Team-wide npts read-only for current view, because they displayed partially
|
|
activityCalendarUIService.setTeamWideNptItemsReadOnly(nonProjectTimeCache);
|
|
|
|
if (projectExpendituresCache && Object.keys(projectExpendituresCache).length) {
|
|
// sort teams by name
|
|
var sortedTeams = $filter('sortObjectsBy')(projectExpendituresCache, 'Name', false);
|
|
for (var i = 0; i < sortedTeams.length; i++) {
|
|
var teamData = sortedTeams[i];
|
|
if (!teamData.Id) { // No Team section
|
|
continue;
|
|
}
|
|
var teamViewModel = activityCalendarUIService.createViewModel4Team(teamData);
|
|
if (teamViewModel) {
|
|
setTooltips4TopPartRows(teamViewModel);
|
|
|
|
var teamNptData = (nonProjectTimeCache && (teamData.Id in nonProjectTimeCache)) ?
|
|
nonProjectTimeCache[teamData.Id] : null;
|
|
|
|
var teamNptModel = activityCalendarUIService.createViewModel4NptTotalRow('Non-Project Time', teamNptData);
|
|
teamViewModel.NonProjectTime = teamNptModel;
|
|
$scope.ViewModel.NonProjectTimeTotalRows.push(teamNptModel);
|
|
$scope.ViewModel.Rows.push(teamViewModel);
|
|
}
|
|
}
|
|
|
|
if ('NoTeam' in projectExpendituresCache && projectExpendituresCache['NoTeam'].Projects) {
|
|
$scope.ViewModel.NoTeamRow = activityCalendarUIService.createViewModel4TotalRow('No Team');
|
|
$scope.ViewModel.NoTeamRow.AllocationMode = activityCalendarUIService.allocationMode.remainingNeedAllocation;
|
|
$scope.ViewModel.NoTeamRow.Children = activityCalendarUIService.createViewModel4Projects(projectExpendituresCache['NoTeam'].Projects);
|
|
|
|
for (var pIndex = 0; pIndex < $scope.ViewModel.NoTeamRow.Children.length; pIndex++) {
|
|
var projectRow = $scope.ViewModel.NoTeamRow.Children[pIndex];
|
|
projectRow.getTooltipContent = $scope.getTooltipProjectUnassignedContent;
|
|
|
|
if (projectRow.Children) {
|
|
for (var ecIndex = 0; ecIndex < projectRow.Children.length; ecIndex++) {
|
|
var ecRow = projectRow.Children[ecIndex];
|
|
if (ecRow) {
|
|
ecRow.getTooltipContent = $scope.getTooltipEcUnassignedContent;
|
|
ecRow.AvailableTeams = getAvailableTeams(projectRow.Id, ecRow.Id);
|
|
setCustomRowTemplates(ecRow);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Create total rows
|
|
createTotalRowModels();
|
|
};
|
|
function setCustomRowTemplates(expCatRow) {
|
|
if (!expCatRow)
|
|
return;
|
|
|
|
expCatRow.Templates = {
|
|
Main: activityCalendarUIService.viewRowTemplates.expCatUnallocatedRowTemplate,
|
|
Numbers: activityCalendarUIService.viewRowTemplates.expCatUnallocatedRowNumbersTemplate
|
|
};
|
|
|
|
if (expCatRow.Children) {
|
|
for (var tIndex = 0; tIndex < expCatRow.Children.length; tIndex) {
|
|
var teamRow = expCatRow.Children[tIndex];
|
|
teamRow.Templates = {
|
|
Main: activityCalendarUIService.viewRowTemplates.teamUnallocatedRowTemplate,
|
|
Numbers: activityCalendarUIService.viewRowTemplates.teamUnallocatedRowNumbersTemplate
|
|
};
|
|
}
|
|
}
|
|
};
|
|
function setTooltips4TopPartRows(teamRow) {
|
|
if (!teamRow || !teamRow.Children)
|
|
return;
|
|
|
|
for (var pIndex = 0; pIndex < teamRow.Children.length; pIndex++) {
|
|
var projectRow = teamRow.Children[pIndex];
|
|
|
|
if (projectRow && projectRow.Children) {
|
|
for (var ecIndex = 0; ecIndex < projectRow.Children.length; ecIndex++) {
|
|
var expCatRow = projectRow.Children[ecIndex];
|
|
|
|
if (expCatRow) {
|
|
expCatRow.getTooltipContent = $scope.getTooltipExpCatTopContent;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
function createTotalRowModels() {
|
|
$scope.ViewModel.TotalRow = activityCalendarUIService.createViewModel4TotalRow('Total');
|
|
$scope.ViewModel.RemainingCapacityRow = activityCalendarUIService.createViewModel4TotalRow('Remaining Capacity');
|
|
};
|
|
function getTotalRows() {
|
|
var rows = [];
|
|
|
|
if ($scope.ViewModel.TotalRow) {
|
|
rows.push($scope.ViewModel.TotalRow);
|
|
}
|
|
if ($scope.ViewModel.RemainingCapacityRow) {
|
|
rows.push($scope.ViewModel.RemainingCapacityRow);
|
|
}
|
|
|
|
return rows;
|
|
};
|
|
function getResourceRows(resourceId) {
|
|
var result = activityCalendarUIService.getResourceRows($scope.ViewModel.Rows, resourceId);
|
|
return result;
|
|
};
|
|
function getAvailableTeams(projectId, expenditureCategoryId) {
|
|
if (!projectId || !expenditureCategoryId) {
|
|
return null;
|
|
}
|
|
|
|
var availableTeams = activityCalendarService.getTeamsAvailable4Assign($scope.Filter, projectId, expenditureCategoryId);
|
|
var availableTeamsViewModel = activityCalendarUIService.createViewModel4AvailableTeams(availableTeams);
|
|
if (!availableTeamsViewModel || !Object.keys(availableTeamsViewModel).length) {
|
|
return null;
|
|
}
|
|
|
|
return $filter('sortObjectsBy')(availableTeamsViewModel, 'Name', false);
|
|
};
|
|
|
|
/* Root methods for creating view models and filling them with data */
|
|
function fillViewModelWithData(model) {
|
|
if (!$scope.ViewModel.Rows) {
|
|
return;
|
|
}
|
|
|
|
for (var i = 0; i < $scope.ViewModel.Rows.length; i++) {
|
|
var teamRow = $scope.ViewModel.Rows[i];
|
|
var projectExpenditures = projectExpendituresCache[teamRow.Id] || {};
|
|
|
|
for (var j = 0; j < teamRow.Children.length; j++) {
|
|
var projectRow = teamRow.Children[j];
|
|
var projectData = (projectExpenditures.Projects || {})[projectRow.Id] || {};
|
|
activityCalendarUIService.fillProjectRowWithData(teamRow, projectRow, projectData.Expenditures, $scope.ViewModel.Header, $scope.Filter);
|
|
}
|
|
|
|
if (teamRow.NonProjectTime) {
|
|
var teamNptData = (teamRow.Id in nonProjectTimeCache) ? nonProjectTimeCache[teamRow.Id] : null;
|
|
activityCalendarUIService.fillTotalNptRowData(teamRow.NonProjectTime, teamNptData, $scope.ViewModel.Header, [teamRow]);
|
|
}
|
|
}
|
|
|
|
if ($scope.ViewModel.NoTeamRow && $scope.ViewModel.NoTeamRow.Children && $scope.ViewModel.NoTeamRow.Children.length) {
|
|
var projectExpenditures = projectExpendituresCache['NoTeam'] || {};
|
|
|
|
for (var j = 0; j < $scope.ViewModel.NoTeamRow.Children.length; j++) {
|
|
var project = $scope.ViewModel.NoTeamRow.Children[j];
|
|
var projectData = (projectExpenditures.Projects || {})[project.Id] || {};
|
|
|
|
activityCalendarUIService.fillProjectRowWithData(null, project, projectData.Expenditures, $scope.ViewModel.Header, $scope.Filter);
|
|
if (project.Children) {
|
|
for (var ecIndex = 0; ecIndex < project.Children.length; ecIndex++) {
|
|
var ecRow = project.Children[ecIndex];
|
|
ecRow.RemainingCapacityValues = angular.extend([], ecRow.RemainingNeedHoursValues);
|
|
}
|
|
}
|
|
// add additional ec-team rows for recently added teams
|
|
if (projectData && projectData.Project && projectData.Project.Teams) {
|
|
angular.forEach(projectData.Project.Teams, function (pTeamCache, teamKey) {
|
|
// we should care only about pending (newly assigned)/saved pending (assigned multiple times) teams
|
|
var type = pTeamCache.Type || activityCalendarService.TeamType.Saved;
|
|
if (type !== activityCalendarService.TeamType.Saved && pTeamCache.Expenditures2Display && project.Children) {
|
|
for (var ecIndex = 0; ecIndex < project.Children.length; ecIndex++) {
|
|
var ecRow = project.Children[ecIndex];
|
|
if (ecRow.Id in pTeamCache.Expenditures2Display) {
|
|
createAndFillNewEcTeamRow(project, ecRow, teamKey);
|
|
// expand project row
|
|
if (project.Collapsed)
|
|
activityCalendarUIService.toggleRow(project, {}, false);
|
|
// expand ec row
|
|
if (ecRow.Collapsed)
|
|
activityCalendarUIService.toggleRow(ecRow, {}, false);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
refreshProjectAvailableTeams(project);
|
|
}
|
|
|
|
activityCalendarUIService.fillTotalRowData($scope.ViewModel.NoTeamRow.Children, $scope.ViewModel.NoTeamRow, $scope.ViewModel.Header);
|
|
}
|
|
|
|
// Clear NPT caches. Caches not need more
|
|
nonProjectTimeCache = {};
|
|
nonProjectTimeCategoryCache = {};
|
|
|
|
// Fill total rows with data
|
|
fillTotalRowsWithData();
|
|
};
|
|
function fillTotalRowsWithData() {
|
|
var rowsForTotal = ($scope.ViewModel.Rows) ? angular.copy($scope.ViewModel.Rows) : [];
|
|
if ($scope.ViewModel.NoTeamRow) {
|
|
rowsForTotal = rowsForTotal.concat(angular.copy($scope.ViewModel.NoTeamRow));
|
|
}
|
|
|
|
activityCalendarUIService.fillTotalRowData(rowsForTotal, $scope.ViewModel.TotalRow, $scope.ViewModel.Header);
|
|
activityCalendarUIService.fillRemainingCapacityRowData($scope.ViewModel.TotalRow, $scope.ViewModel.RemainingCapacityRow, $scope.ViewModel.Header, $scope.ViewModel.DisplayMode.IsCapacityModeActuals.Value);
|
|
};
|
|
function createAndFillNewEcTeamRow(projectRow, expCatRow, teamId) {
|
|
// create a UI row model and fill it with data
|
|
var team = teamInfoService.getById(teamId);
|
|
var ecTeamData = getDataModel4NewTeam(teamId, projectRow.Id, expCatRow.Id);
|
|
ecTeamData = angular.extend({}, ecTeamData, {
|
|
Id: team.Id,
|
|
ExpenditureCategoryId: ecTeamData.Id,
|
|
Name: team.Name,
|
|
Initialized: expCatRow.Initialized,
|
|
Show: expCatRow.Show,
|
|
Level: expCatRow.Level + 1
|
|
});
|
|
var newTeamRow = activityCalendarUIService.createViewModel4ECTeam(ecTeamData);
|
|
activityCalendarUIService.fillECTeamRowWithData($scope.ViewModel.Header, newTeamRow, expCatRow, ecTeamData, projectRow.Id, projectRow.ActiveScenario.StartDate, projectRow.ActiveScenario.EndDate, projectRow.ReadOnly);
|
|
activityCalendarUIService.toggleGridSource([newTeamRow], $scope.ViewModel.DisplayMode.IsUOMHours);
|
|
if ($scope.ViewModel.DisplayMode.IsAvgMode()) {
|
|
activityCalendarUIService.applyAvgMode([newTeamRow], $scope.ViewModel.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 = activityCalendarUIService.toggleSortBy(expCatRow.Children, $scope.ViewModel.DisplayMode.SortBy, $scope.ViewModel.DisplayMode.SortOrder);
|
|
};
|
|
|
|
/* Methods for toggling display modes */
|
|
function toggleHoursResourcesMode() {
|
|
var totalRows = getTotalRows();
|
|
activityCalendarUIService.toggleGridSource($scope.ViewModel.NonProjectTimeTotalRows, $scope.ViewModel.DisplayMode.IsUOMHours);
|
|
activityCalendarUIService.toggleGridSource($scope.ViewModel.Rows, $scope.ViewModel.DisplayMode.IsUOMHours);
|
|
|
|
if ($scope.ViewModel.NoTeamRow) {
|
|
activityCalendarUIService.toggleGridSource([$scope.ViewModel.NoTeamRow], $scope.ViewModel.DisplayMode.IsUOMHours);
|
|
}
|
|
activityCalendarUIService.toggleGridSource(totalRows, $scope.ViewModel.DisplayMode.IsUOMHours);
|
|
|
|
if ($scope.ViewModel.DisplayMode.IsAvgMode()) {
|
|
activityCalendarUIService.applyAvgMode($scope.ViewModel.NonProjectTimeTotalRows, $scope.ViewModel.Header);
|
|
activityCalendarUIService.applyAvgMode($scope.ViewModel.Rows, $scope.ViewModel.Header);
|
|
|
|
if ($scope.ViewModel.NoTeamRow) {
|
|
activityCalendarUIService.applyAvgMode([$scope.ViewModel.NoTeamRow], $scope.ViewModel.Header);
|
|
}
|
|
activityCalendarUIService.applyAvgMode(totalRows, $scope.ViewModel.Header);
|
|
}
|
|
};
|
|
function toggleBarsValuesMode() {
|
|
if (!$scope.ViewModel.Rows || !$scope.ViewModel.Rows.length) {
|
|
return;
|
|
}
|
|
|
|
for (var i = 0; i < $scope.ViewModel.Rows.length; i++) {
|
|
var projects = $scope.ViewModel.Rows[i].Children;
|
|
if (projects) {
|
|
activityCalendarUIService.toggleBarsValuesMode(projects, $scope.ViewModel.DisplayMode.IsBarMode);
|
|
}
|
|
}
|
|
|
|
if ($scope.ViewModel.NoTeamRow && $scope.ViewModel.NoTeamRow.Children) {
|
|
activityCalendarUIService.toggleBarsValuesMode($scope.ViewModel.NoTeamRow.Children, $scope.ViewModel.DisplayMode.IsBarMode);
|
|
}
|
|
};
|
|
function toggleMonths() {
|
|
if ($scope.ViewModel.DisplayMode.IsViewModeMonth) {
|
|
$scope.ViewModel.Header.collapseMonthes();
|
|
}
|
|
else {
|
|
$scope.ViewModel.Header.expandMonthes();
|
|
}
|
|
};
|
|
function toggleParts() {
|
|
if (!$scope.ViewModel || !$scope.ViewModel.Rows)
|
|
return;
|
|
|
|
for (var i = 0; i < $scope.ViewModel.Rows.length; i++) {
|
|
var team = $scope.ViewModel.Rows[i];
|
|
if (team.Children && team.Children.length > 0) {
|
|
team.Children = activityCalendarUIService.toggleParts(team.Children, $scope.ViewModel.DisplayMode.ShowParts.Value);
|
|
activityCalendarUIService.updateProjectStyles($scope.ViewModel.Header, $scope.Filter, team.Children, null, null, [team.Id]);
|
|
}
|
|
}
|
|
|
|
if ($scope.ViewModel.NoTeamRow && $scope.ViewModel.NoTeamRow.Children && $scope.ViewModel.NoTeamRow.Children.length) {
|
|
$scope.ViewModel.NoTeamRow.Children = activityCalendarUIService.toggleParts($scope.ViewModel.NoTeamRow.Children, $scope.ViewModel.DisplayMode.ShowParts.Value);
|
|
activityCalendarUIService.updateProjectStyles($scope.ViewModel.Header, $scope.Filter, $scope.ViewModel.NoTeamRow.Children);
|
|
}
|
|
};
|
|
function toggleSortBy() {
|
|
if (!$scope.ViewModel || !$scope.ViewModel.Rows)
|
|
return;
|
|
|
|
for (var i = 0; i < $scope.ViewModel.Rows.length; i++) {
|
|
var team = $scope.ViewModel.Rows[i];
|
|
if (team.Children && team.Children.length > 0) {
|
|
team.Children = activityCalendarUIService.toggleSortBy(team.Children, $scope.ViewModel.DisplayMode.SortBy, $scope.ViewModel.DisplayMode.SortOrder);
|
|
}
|
|
}
|
|
|
|
if ($scope.ViewModel.NoTeamRow && $scope.ViewModel.NoTeamRow.Children && $scope.ViewModel.NoTeamRow.Children.length) {
|
|
$scope.ViewModel.NoTeamRow.Children = activityCalendarUIService.toggleSortBy($scope.ViewModel.NoTeamRow.Children, $scope.ViewModel.DisplayMode.SortBy, $scope.ViewModel.DisplayMode.SortOrder);
|
|
}
|
|
};
|
|
function toggleCapacityType() {
|
|
// reset total row values
|
|
createTotalRowModels();
|
|
// recalculate total row UI data
|
|
fillTotalRowsWithData();
|
|
toggleHoursResourcesMode();
|
|
};
|
|
function recalculateResourceAvailability(resourceId) {
|
|
if (!resourceId || !$scope.ViewModel.Rows) {
|
|
return;
|
|
}
|
|
|
|
for (var teamIndex = 0; teamIndex < $scope.ViewModel.Rows.length; teamIndex++) {
|
|
var teamRow = $scope.ViewModel.Rows[teamIndex];
|
|
if (teamRow && teamRow.Children) {
|
|
activityCalendarUIService.recalculateResourceAvailability($scope.Filter, $scope.ViewModel.Header, teamRow.Children, resourceId);
|
|
}
|
|
}
|
|
};
|
|
function initBottomPart() {
|
|
// need to pass new objects for preventing using single instances of objects inside current and other controllers
|
|
$scope.$broadcast('rebindTeamInfo', {
|
|
Header: $scope.ViewModel.Header,
|
|
DisplayMode: activityCalendarUIService.castDisplayModeIntoTeamInfoMode($scope.ViewModel.DisplayMode),
|
|
});
|
|
};
|
|
function refreshBottomPart() {
|
|
$scope.$broadcast('teaminfo.recreateRows');
|
|
};
|
|
function triggerResourceChanged(data) {
|
|
if (typeof data !== 'object' || !angular.isArray(data)) {
|
|
return;
|
|
}
|
|
|
|
for (var i = 0; i < data.length; i++) {
|
|
$scope.$broadcast('resourceValueChanged', data[i]);
|
|
}
|
|
};
|
|
function triggerTeamChanged(data) {
|
|
if (typeof data !== 'object' || !angular.isArray(data)) {
|
|
return;
|
|
}
|
|
|
|
for (var i = 0; i < data.length; i++) {
|
|
$scope.$broadcast('teamValueChanged', data[i]);
|
|
}
|
|
};
|
|
function triggerNptResourceChanged(data) {
|
|
if (typeof data !== 'object' || !angular.isArray(data)) {
|
|
return;
|
|
}
|
|
|
|
for (var i = 0; i < data.length; i++) {
|
|
$scope.$broadcast('resourceNonProjectTimeChanged', data[i]);
|
|
}
|
|
};
|
|
function refreshResourceStyles(resourceId, data) {
|
|
if (!resourceId || typeof data !== 'object' || !angular.isArray(data)) {
|
|
return;
|
|
}
|
|
|
|
var resourceRows = getResourceRows(resourceId);
|
|
if (!resourceRows || !resourceRows.length) {
|
|
return;
|
|
}
|
|
|
|
activityCalendarUIService.updateResourceStyles($scope.ViewModel.Header, resourceId, resourceRows, data);
|
|
};
|
|
function refreshProjectStyles(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
|
|
for (var i = 0; i < $scope.ViewModel.Rows.length; i++) {
|
|
var team = $scope.ViewModel.Rows[i];
|
|
if (team.Children && team.Children.length) {
|
|
var projectRowsInfo = activityCalendarUIService.getProjectRows(team.Children, projectRow.Id);
|
|
|
|
if (projectRowsInfo && projectRowsInfo.masterProjectRow && projectRowsInfo.projectRow &&
|
|
(projectRowsInfo.projectRow.ParentTeamId == projectRow.ParentTeamId)) {
|
|
rowToUpdateStylesIn = projectRowsInfo.masterProjectRow;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
activityCalendarUIService.updateProjectStyles($scope.ViewModel.Header, $scope.Filter, [rowToUpdateStylesIn], data, null, [projectRow.ParentTeamId]);
|
|
};
|
|
function refreshNoTeamProjectStyles(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 = activityCalendarUIService.getProjectRows($scope.ViewModel.NoTeamRow.Children, projectRow.Id);
|
|
|
|
if (projectRowsInfo && projectRowsInfo.masterProjectRow) {
|
|
rowToUpdateStylesIn = projectRowsInfo.masterProjectRow;
|
|
}
|
|
}
|
|
|
|
activityCalendarUIService.updateProjectStyles($scope.ViewModel.Header, $scope.Filter, [rowToUpdateStylesIn], data);
|
|
};
|
|
|
|
/* ===== ToolTips display ==== */
|
|
$scope.getTooltipExpCatTopContent = function (opts) {
|
|
var tooltip = 'No data available';
|
|
var units = ' hours';
|
|
|
|
if (!$scope.DisplayMode.IsUOMHours)
|
|
units = ' resources';
|
|
|
|
if (!opts || !opts.header || !opts.projectId || !opts.expCatId)
|
|
return tooltip;
|
|
|
|
var header = opts.header;
|
|
var projectId = opts.projectId;
|
|
var expCatId = opts.expCatId;
|
|
var teamId = opts.teamId;
|
|
|
|
var teamAllocations = activityCalendarUIService.getTeamAllocations4Display($scope.Filter,
|
|
$scope.ViewModel.DisplayMode, $scope.ViewModel.Header, header, projectId, expCatId, teamId);
|
|
|
|
if (teamAllocations && (teamAllocations.length > 0)) {
|
|
tooltip = 'Allocated to Teams:';
|
|
|
|
for (var index = 0; index < teamAllocations.length; index++) {
|
|
var teamInfo = teamAllocations[index];
|
|
tooltip += ('<br/>' + teamInfo.Name + ': ' + teamInfo.Need + units);
|
|
}
|
|
}
|
|
else {
|
|
tooltip = 'No Team Allocations present';
|
|
}
|
|
|
|
return tooltip;
|
|
};
|
|
$scope.getTooltipEcUnassignedContent = function (opts) {
|
|
var tooltip = 'No data available';
|
|
var units = $scope.DisplayMode.IsUOMHours ? ' hours' : ' resources';
|
|
|
|
if (!opts || !opts.header || !opts.projectId || !opts.expCatId)
|
|
return tooltip;
|
|
|
|
var header = opts.header;
|
|
var projectId = opts.projectId;
|
|
var expCatId = opts.expCatId;
|
|
|
|
var rows = findUnassignedRows(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 = activityCalendarUIService.getExpCatUnassignedNeed($scope.Filter, $scope.ViewModel.DisplayMode,
|
|
$scope.ViewModel.Header, header, 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;
|
|
};
|
|
$scope.getTooltipProjectUnassignedContent = function (opts) {
|
|
var tooltip = 'No data available';
|
|
var units = $scope.DisplayMode.IsUOMHours ? ' hours' : ' resources';
|
|
|
|
if (!opts || !opts.header || !opts.projectId)
|
|
return tooltip;
|
|
|
|
var header = opts.header;
|
|
var projectId = opts.projectId;
|
|
|
|
var projectNeed = 0;
|
|
var projectAllocatedInView = 0;
|
|
var projectAllocatedOutOfView = 0;
|
|
var projectRowsInfo = activityCalendarUIService.getProjectRows($scope.ViewModel.NoTeamRow.Children, 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 = activityCalendarUIService.getExpCatUnassignedNeed($scope.Filter, $scope.ViewModel.DisplayMode,
|
|
$scope.ViewModel.Header, header, projectId, scenarioId, [expCatRow.Id], false);
|
|
|
|
if (expCatSummaryInfo) {
|
|
projectNeed += (expCatSummaryInfo.Need || 0);
|
|
projectAllocatedInView += (expCatSummaryInfo.AllocatedToTeamsInView || 0);
|
|
projectAllocatedOutOfView += (expCatSummaryInfo.AllocatedToTeamsOutOfView || 0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
projectNeed = roundService.roundQuantity(projectNeed);
|
|
projectAllocatedInView = roundService.roundQuantity(projectAllocatedInView);
|
|
projectAllocatedOutOfView = roundService.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;
|
|
};
|
|
}]); |