1956 lines
68 KiB
JavaScript
1956 lines
68 KiB
JavaScript
'use strict';
|
|
|
|
app.controller('teamInfoController', ['$scope', '$http', 'hoursResourcesConverter', 'cellHighlightingService', 'teamInfoService', 'dataSources', 'roundService', '$filter', '$q', '$timeout', function ($scope, $http, hoursResourcesConverter, cellHighlightingService, teamInfoService, dataSources, roundService, $filter, $q, $timeout) {
|
|
var collapsedIcon = 'fa-plus-square',
|
|
nonCollapsedIcon = 'fa-minus-square';
|
|
|
|
var groupByEnum = {
|
|
Category: 'Category',
|
|
Team: 'Team',
|
|
Company: 'Business Unit',
|
|
CostCenter: 'Cost Center'
|
|
};
|
|
|
|
$scope.RowType = {
|
|
NonProjectTime: 1,
|
|
Team: 2,
|
|
ExpenditureCategory: 3,
|
|
Company: 4,
|
|
CostCenter: 5
|
|
};
|
|
$scope.ExpendituresByCostCentersCache = {};
|
|
$scope.Weekendings = [];
|
|
$scope.InternalData = null; //DAL data model
|
|
$scope.IsRedrawRequired = false;
|
|
setupJSEvents();
|
|
|
|
$scope.DisplayMode = { // Page Options
|
|
IsUOMHours: true, // display data in Hours
|
|
GroupBy: groupByEnum.Category, // group capacity by EC by default
|
|
TotalsAs: 1, // Allocated/Capacity
|
|
CapacityView: false, // Planned
|
|
IsAvgMode: false, // Totals average mode
|
|
ShowResources: true, // display bottom part (starting Non-Project Time row until the bottom of the grid)
|
|
DisplayPriorityColumn: false
|
|
};
|
|
|
|
$scope.DisplayData = {}; // UI data model
|
|
$scope.DisplayDataOrder = [];
|
|
$scope.$on('rebindTeamInfo', function (event, data) {
|
|
if (!data)
|
|
return;
|
|
// we must create view model only after all services data will be loaded
|
|
hoursResourcesConverter.load().then(function () {
|
|
$scope.InternalData = {
|
|
Header: data.Header || {}
|
|
};
|
|
initOptions(data.DisplayMode);
|
|
initRows();
|
|
});
|
|
});
|
|
$scope.$on('teaminfo.recreateRows', function (event) {
|
|
initRows();
|
|
});
|
|
$scope.$on('queue.recreateRows', function (event) {
|
|
if (!$scope.IsRedrawRequired) {
|
|
$scope.IsRedrawRequired = true;
|
|
$timeout(function () {
|
|
initRows();
|
|
$scope.IsRedrawRequired = false;
|
|
});
|
|
}
|
|
});
|
|
$scope.$on('groupByChanged', function (event, data) {
|
|
$scope.DisplayMode.GroupBy = data;
|
|
initRows();
|
|
});
|
|
$scope.$on('changeUOMMode', function (event, data) {
|
|
$scope.DisplayMode.IsUOMHours = data;
|
|
zeroRowsData($scope.DisplayData);
|
|
initRowsData();
|
|
});
|
|
$scope.$on('totalsAsChanged', function (event, data) {
|
|
$scope.DisplayMode.TotalsAs = data;
|
|
initRows();
|
|
});
|
|
$scope.$on('capacityViewChanged', function (event, data) {
|
|
$scope.DisplayMode.CapacityView = data;
|
|
zeroRowsData($scope.DisplayData);
|
|
initRows();
|
|
});
|
|
$scope.$on('showResourcesChanged', function (event, data) {
|
|
$scope.DisplayMode.ShowResources = data;
|
|
initRows();
|
|
});
|
|
$scope.$on('resourceValueChanged', function (event, data) {
|
|
if (!data)
|
|
return;
|
|
|
|
resourceValueChanged(data.TeamId, data.ExpenditureCategoryId, data.ResourceId, data.WeekEnding);
|
|
});
|
|
$scope.$on('teamValueChanged', function (event, data) {
|
|
if (!data)
|
|
return;
|
|
|
|
teamValueChanged(data.TeamId, data.ExpenditureCategoryId, data.WeekEnding);
|
|
});
|
|
$scope.$on('resourceNonProjectTimeChanged', function (event, data) {
|
|
if (!data)
|
|
return;
|
|
|
|
// TODO: Rewrite to update the only changed cells
|
|
initRows();
|
|
});
|
|
$scope.toggleParentRow = function (key) {
|
|
$scope.DisplayData[key].IsCollapsed = !$scope.DisplayData[key].IsCollapsed;
|
|
$.each($scope.DisplayData, function (rowKey, row) {
|
|
if (row.Type == $scope.RowType.ExpenditureCategory && row.ParentId == $scope.DisplayData[key].Id) {
|
|
row.Initialized = true;
|
|
row.Show = !$scope.DisplayData[key].IsCollapsed;
|
|
}
|
|
});
|
|
};
|
|
$scope.toggleRow = function (key) {
|
|
$scope.DisplayData[key].IsCollapsed = !$scope.DisplayData[key].IsCollapsed;
|
|
if ($scope.DisplayData[key].Resources) {
|
|
for (var resourceIndex = 0; resourceIndex < $scope.DisplayData[key].Resources.length; resourceIndex++) {
|
|
$scope.DisplayData[key].Resources[resourceIndex].Initialized = true;
|
|
}
|
|
}
|
|
};
|
|
$scope.showTooltip = function (event, cell, rowType) {
|
|
var $target = $(event.currentTarget);
|
|
if ($target.data('bs.tooltip')) {
|
|
$target.attr('data-original-title', getTooltipContent(cell, rowType))
|
|
return;
|
|
}
|
|
var opts = {
|
|
trigger: 'click',
|
|
html: true,
|
|
title: getTooltipContent(cell, rowType)
|
|
};
|
|
$target.tooltip(opts).on('show.bs.tooltip', hideRedundantTooltips);
|
|
$target.tooltip('show');
|
|
};
|
|
|
|
$scope.isAvgMode = function () {
|
|
return $scope.DisplayMode.IsAvgMode && !$scope.DisplayMode.IsUOMHours;
|
|
};
|
|
|
|
// set page options with values received from outer scope
|
|
function initOptions(options) {
|
|
if (!options)
|
|
return;
|
|
|
|
var modes = Object.keys(options);
|
|
if (modes.indexOf('IsUOMHours') >= 0)
|
|
$scope.DisplayMode.IsUOMHours = options.IsUOMHours;
|
|
if (modes.indexOf('GroupBy') >= 0)
|
|
$scope.DisplayMode.GroupBy = options.GroupBy;
|
|
if (modes.indexOf('TotalsAs') >= 0)
|
|
$scope.DisplayMode.TotalsAs = options.TotalsAs;
|
|
if (modes.indexOf('CapacityView') >= 0)
|
|
$scope.DisplayMode.CapacityView = options.CapacityView;
|
|
if (modes.indexOf('IsAvgMode') >= 0)
|
|
$scope.DisplayMode.IsAvgMode = options.IsAvgMode;
|
|
if (modes.indexOf('ShowResources') >= 0)
|
|
$scope.DisplayMode.ShowResources = options.ShowResources;
|
|
if (modes.indexOf('DisplayPriorityColumn') >= 0)
|
|
$scope.DisplayMode.DisplayPriorityColumn = options.DisplayPriorityColumn;
|
|
};
|
|
|
|
//==========Start Convert data model to UI data model methods=============//
|
|
function initNonProjectTimeTotalRow() {
|
|
return {
|
|
Name: "Non-Project Time",
|
|
Type: $scope.RowType.NonProjectTime,
|
|
IsCollapsed: true,
|
|
Categories: {},
|
|
Cells: new Array($scope.InternalData.Header.Weeks.length)
|
|
};
|
|
};
|
|
function initNonProjectTimeRow(name) {
|
|
return {
|
|
Name: name,
|
|
Cells: new Array($scope.InternalData.Header.Weeks.length)
|
|
};
|
|
};
|
|
|
|
function initTopLevelRow(id, name, type) {
|
|
if (!topLevelExists()) {
|
|
return null;
|
|
}
|
|
|
|
return {
|
|
Id: id,
|
|
Name: name,
|
|
Type: type,
|
|
IsCollapsed: true,
|
|
Cells: new Array($scope.InternalData.Header.Weeks.length),
|
|
CSSClass: new Array($scope.InternalData.Header.Weeks.length),
|
|
Total: {
|
|
Value1: 0,
|
|
Value2: 0,
|
|
Allocated: 0,
|
|
Needed: 0,
|
|
Capacity: 0,
|
|
NonProjectTime: 0,
|
|
SuperECAllocations: 0
|
|
},
|
|
};
|
|
};
|
|
function initExpCategoryRow(category, parentId, isTeamCollapsed) {
|
|
return {
|
|
Id: category.Id,
|
|
ParentId: parentId,
|
|
Name: category.Name,
|
|
Type: $scope.RowType.ExpenditureCategory,
|
|
AllowResourceAssignment: category.AllowResourceAssignment,
|
|
IsCollapsed: true,
|
|
Initialized: !isTeamCollapsed,
|
|
Show: !isTeamCollapsed,
|
|
Cells: new Array($scope.InternalData.Header.Weeks.length),
|
|
Resources: [],
|
|
CSSClass: new Array($scope.InternalData.Header.Weeks.length),
|
|
Total: {
|
|
Value1: '-',
|
|
Value2: '-',
|
|
Allocated: 0,
|
|
Needed: 0,
|
|
Capacity: 0,
|
|
NonProjectTime: 0,
|
|
SuperECAllocations: 0
|
|
},
|
|
};
|
|
};
|
|
function initResourceRow(resource, catId) {
|
|
return {
|
|
Id: resource.Id,
|
|
ExpCatId: catId,
|
|
Name: resource.Name,
|
|
Initialized: false,
|
|
Cells: new Array($scope.InternalData.Header.Weeks.length),
|
|
CSSClass: new Array($scope.InternalData.Header.Weeks.length),
|
|
Total: {
|
|
Value1: 0,
|
|
Value2: 0,
|
|
Allocated: 0,
|
|
Needed: 0,
|
|
Capacity: 0,
|
|
NonProjectTime: 0,
|
|
SuperECAllocations: 0
|
|
},
|
|
};
|
|
};
|
|
function initRows() {
|
|
var nptCatsTask = dataSources.getNonProjectTimeCategories();
|
|
var expCatsTask = dataSources.load();
|
|
var companiesTask = dataSources.loadCompanies();
|
|
var costCentersTask = dataSources.loadCostCenters();
|
|
$q.all([nptCatsTask, expCatsTask, companiesTask, costCentersTask])
|
|
.then(function () {
|
|
initRowsCallback();
|
|
})
|
|
.then(null, function () {
|
|
showErrorModal(null, null, '000010');
|
|
});
|
|
};
|
|
function initRowsCallback() {
|
|
if (!$scope.InternalData)
|
|
return;
|
|
|
|
$scope.ExpendituresByCostCentersCache = {};
|
|
|
|
if (!$scope.DisplayMode.ShowResources) {
|
|
$scope.DisplayData = {};
|
|
$scope.DisplayDataOrder = [];
|
|
return;
|
|
}
|
|
|
|
$scope.DisplayData = {
|
|
"NonProjectTime": initNonProjectTimeTotalRow(),
|
|
};
|
|
$scope.DisplayDataOrder = ["NonProjectTime"];
|
|
$scope.Weekendings = getWeekendingFromHeader($scope.InternalData);
|
|
|
|
switch ($scope.DisplayMode.GroupBy) {
|
|
case groupByEnum.CostCenter:
|
|
initRows4GroupByCostCenterMode();
|
|
break;
|
|
default:
|
|
initRows4OtherGroupModes();
|
|
}
|
|
|
|
initRowsData();
|
|
};
|
|
function initRows4OtherGroupModes() {
|
|
var totalsAsModeParsed = parseInt($scope.DisplayMode.TotalsAs);
|
|
var companies = $scope.DisplayMode.GroupBy === groupByEnum.Company ? Object.keys(dataSources.getCompanies()) : [null];
|
|
|
|
for (var companyIndex = 0; companyIndex < companies.length; companyIndex++) {
|
|
var companyId = companies[companyIndex];
|
|
var teams = !!companyId ? teamInfoService.getTeamsByCompanyId(companyId) : teamInfoService.getAll();
|
|
if (teams) {
|
|
for (var teamId in teams) {
|
|
var team = teams[teamId];
|
|
|
|
var topLevelItemId = undefined;
|
|
switch ($scope.DisplayMode.GroupBy) {
|
|
case groupByEnum.Team:
|
|
topLevelItemId = teamId;
|
|
break;
|
|
case groupByEnum.Company:
|
|
topLevelItemId = team.CompanyId;
|
|
break;
|
|
}
|
|
|
|
var topLevelItem = getTopLevelItem(topLevelItemId);
|
|
var topLevelItemKey = null;
|
|
|
|
if (topLevelExists() && topLevelItem) {
|
|
topLevelItemKey = topLevelItem.Key;
|
|
if (!(topLevelItemKey in $scope.DisplayData)) {
|
|
$scope.DisplayData[topLevelItemKey] = initTopLevelRow(topLevelItemKey, topLevelItem.Name, topLevelItem.Type);
|
|
$scope.DisplayDataOrder.push(topLevelItemKey);
|
|
}
|
|
}
|
|
|
|
if (!team.ExpCategories) {
|
|
continue;
|
|
}
|
|
|
|
// sort expCats so general ECs go first, this way we could reallocate resources to already existing general EC rows
|
|
var expCategories = $filter('sortObjectsBy')(team.ExpCategories, 'AllowResourceAssignment', true);
|
|
for (var expCatIndex in expCategories) {
|
|
var ec = expCategories[expCatIndex];
|
|
|
|
if (!isExpenditureVisible(ec))
|
|
// Prevent displaying Super EC, if it doesn't has resources
|
|
continue;
|
|
|
|
// prepare EC row
|
|
var ecKey = getECKey(ec.Id, teamId, team.CompanyId);
|
|
if ((ec.AllowResourceAssignment || (totalsAsModeParsed != 1)) && !$scope.DisplayData[ecKey]) {
|
|
// Dispaly ordinary ECs always
|
|
// Display SuperECs for all modes, except TotalsAsMode=1 (Allocated / Capacity)
|
|
$scope.DisplayData[ecKey] = initExpCategoryRow(ec, topLevelItemKey, (!!topLevelItemKey && $scope.DisplayData[topLevelItemKey]) ? $scope.DisplayData[topLevelItemKey].IsCollapsed : false);
|
|
}
|
|
|
|
// prepare EC's resource rows
|
|
if (!ec.Resources) {
|
|
continue;
|
|
}
|
|
|
|
for (var resourceId in ec.Resources) {
|
|
var resource = teamInfoService.extendResourceModelWithAttributes(ec.Resources[resourceId], teamId);
|
|
var resourceRow = null;
|
|
var originalEcKey = getECKey(resource.OwnExpenditureCategoryId, teamId, team.CompanyId);
|
|
|
|
if ($scope.DisplayData[originalEcKey]) {
|
|
// If EC is visible due to current TotalsAs settings
|
|
for (var idx = 0; idx < $scope.DisplayData[originalEcKey].Resources.length; idx++) {
|
|
if ($scope.DisplayData[originalEcKey].Resources[idx].Id == resourceId) {
|
|
resourceRow = initResourceRow(resource, resource.OwnExpenditureCategoryId);
|
|
break;
|
|
}
|
|
}
|
|
if (!resourceRow) {
|
|
resourceRow = initResourceRow(resource, resource.OwnExpenditureCategoryId);
|
|
$scope.DisplayData[originalEcKey].Resources.push(resourceRow);
|
|
}
|
|
}
|
|
|
|
if (!resource.NonProjectTime) {
|
|
continue;
|
|
}
|
|
|
|
for (var npTimeId in resource.NonProjectTime) {
|
|
var npTimeCategoryId = resource.NonProjectTime[npTimeId].CategoryId;
|
|
if (!$scope.DisplayData["NonProjectTime"].Categories[npTimeCategoryId]) {
|
|
var npTimeCategory = dataSources.getNPTCategory(npTimeCategoryId);
|
|
var npTimeCategoryRow = initNonProjectTimeRow(npTimeCategory);
|
|
$scope.DisplayData["NonProjectTime"].Categories[npTimeCategoryId] = npTimeCategoryRow;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// sort ECs by Name
|
|
var sortedByName = $filter('sortObjectsBy')(team.ExpCategories, 'Name', false);
|
|
for (var i in sortedByName) {
|
|
var ecKey = getECKey(sortedByName[i].Id, teamId, team.CompanyId);
|
|
if ($scope.DisplayData[ecKey] && $.inArray(ecKey, $scope.DisplayDataOrder) == -1) {
|
|
$scope.DisplayDataOrder.push(ecKey);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
function initRows4GroupByCostCenterMode() {
|
|
var totalsAsModeParsed = parseInt($scope.DisplayMode.TotalsAs);
|
|
|
|
$scope.ExpendituresByCostCentersCache = teamInfoService.getExpendituresByCostCentersSummary();
|
|
if (!$scope.ExpendituresByCostCentersCache)
|
|
return;
|
|
|
|
// Get all available cost centers
|
|
var allCostCenters = dataSources.getCostCenters();
|
|
if (!allCostCenters)
|
|
return;
|
|
|
|
// Prepare cache for creating view-model rows
|
|
for (var costCenterId in $scope.ExpendituresByCostCentersCache) {
|
|
var costCenterCached = $scope.ExpendituresByCostCentersCache[costCenterId];
|
|
|
|
if (costCenterCached && (costCenterId in allCostCenters)) {
|
|
// Get Cost Center names
|
|
costCenterCached.Id = costCenterId;
|
|
costCenterCached.Name = allCostCenters[costCenterId].Name;
|
|
|
|
if (costCenterCached.Expenditures) {
|
|
for (var expCatId in costCenterCached.Expenditures) {
|
|
// Get EC Names and other necessary attributes
|
|
var expCat = dataSources.getExpenditureById(expCatId);
|
|
if (expCat) {
|
|
var expCatData = costCenterCached.Expenditures[expCatId];
|
|
expCatData.Id = expCatId;
|
|
expCatData.Name = expCat.ExpenditureCategoryName;
|
|
expCatData.AllowResourceAssignment = expCat.AllowResourceAssignment;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
var costCentersSorted = $filter('sortObjectsBy')($scope.ExpendituresByCostCentersCache, 'Name', false);
|
|
var costCentersCount = costCentersSorted && angular.isArray(costCentersSorted) && costCentersSorted.length ? costCentersSorted.length : 0;
|
|
|
|
for (var ccIndex = 0; ccIndex < costCentersCount; ccIndex++) {
|
|
var costCenter = costCentersSorted[ccIndex];
|
|
|
|
var topLevelItem = getTopLevelItem(costCenter.Id);
|
|
var topLevelItemKey = null;
|
|
|
|
if (topLevelItem) {
|
|
topLevelItemKey = topLevelItem.Key;
|
|
if (!(topLevelItemKey in $scope.DisplayData)) {
|
|
$scope.DisplayData[topLevelItemKey] = initTopLevelRow(topLevelItemKey, topLevelItem.Name, topLevelItem.Type);
|
|
$scope.DisplayDataOrder.push(topLevelItemKey);
|
|
}
|
|
|
|
if (!costCenter.Expenditures) {
|
|
continue;
|
|
}
|
|
|
|
// sort expCats so general ECs go first, this way we could reallocate resources to already existing general EC rows
|
|
var expCatsSorted = $filter('sortObjectsBy')(costCenter.Expenditures, 'AllowResourceAssignment', true);
|
|
var expCatsSortedCount = expCatsSorted && angular.isArray(expCatsSorted) && expCatsSorted.length ? expCatsSorted.length : 0;
|
|
|
|
for (var eIndex = 0; eIndex < expCatsSortedCount; eIndex++) {
|
|
var ec = expCatsSorted[eIndex];
|
|
|
|
if (ec && ec.Teams) {
|
|
var expCatId = expCatsSorted[eIndex].Id;
|
|
var expCatData = teamInfoService.mergeExpenditureInTeams(expCatId, ec.Teams, true);
|
|
|
|
if (!isExpenditureVisible(expCatData)) {
|
|
// Prevent displaying Super EC, if it doesn't has resources
|
|
continue;
|
|
}
|
|
|
|
// prepare EC row
|
|
var ecKey = getECKey(expCatData.Id, null, costCenter.Id);
|
|
if ((expCatData.AllowResourceAssignment || (totalsAsModeParsed != 1)) && !$scope.DisplayData[ecKey]) {
|
|
// Dispaly all ordinary ECs + SuperECs for al modes, except TotalsAsMode=1 (Allocated/Capacity)
|
|
$scope.DisplayData[ecKey] = initExpCategoryRow(expCatData, topLevelItemKey, (!!topLevelItemKey && $scope.DisplayData[topLevelItemKey]) ? $scope.DisplayData[topLevelItemKey].IsCollapsed : false);
|
|
}
|
|
|
|
// prepare EC's resource rows
|
|
var expCatResources = teamInfoService.mergeExpenditureResources(expCatId, ec.Teams, false);
|
|
if (!expCatResources) {
|
|
continue;
|
|
}
|
|
|
|
var expCatResourcesSorted = $filter('sortObjectsBy')(expCatResources, 'SortingKey', false);
|
|
var expCatResourcesSortedCount = expCatResourcesSorted && angular.isArray(expCatResourcesSorted) && expCatResourcesSorted.length ? expCatResourcesSorted.length : 0;
|
|
|
|
for (var rIndex = 0; rIndex < expCatResourcesSortedCount; rIndex++) {
|
|
var resource = expCatResourcesSorted[rIndex];
|
|
var resourceId = resource.Id;
|
|
var resourceRow = null;
|
|
var originalEcKey = getECKey(resource.OwnExpenditureCategoryId, null, costCenter.Id);
|
|
|
|
if ($scope.DisplayData[originalEcKey]) {
|
|
// If EC is visible due to current TotalsAs settings
|
|
for (var idx = 0; idx < $scope.DisplayData[originalEcKey].Resources.length; idx++) {
|
|
if ($scope.DisplayData[originalEcKey].Resources[idx].Id == resourceId) {
|
|
resourceRow = initResourceRow(resource, resource.OwnExpenditureCategoryId);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!resourceRow) {
|
|
resourceRow = initResourceRow(resource, resource.OwnExpenditureCategoryId);
|
|
$scope.DisplayData[originalEcKey].Resources.push(resourceRow);
|
|
}
|
|
}
|
|
|
|
if (!resource.NonProjectTime) {
|
|
continue;
|
|
}
|
|
|
|
for (var npTimeId in resource.NonProjectTime) {
|
|
var npTimeCategoryId = resource.NonProjectTime[npTimeId].CategoryId;
|
|
if (!$scope.DisplayData["NonProjectTime"].Categories[npTimeCategoryId]) {
|
|
var npTimeCategory = dataSources.getNPTCategory(npTimeCategoryId);
|
|
var npTimeCategoryRow = initNonProjectTimeRow(npTimeCategory);
|
|
$scope.DisplayData["NonProjectTime"].Categories[npTimeCategoryId] = npTimeCategoryRow;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// sort ECs by Name
|
|
var sortedByName = $filter('sortObjectsBy')(costCenter.Expenditures, 'Name', false);
|
|
for (var i in sortedByName) {
|
|
var ecKey = getECKey(sortedByName[i].Id, null, costCenter.Id);
|
|
if ($scope.DisplayData[ecKey] && $.inArray(ecKey, $scope.DisplayDataOrder) == -1) {
|
|
$scope.DisplayDataOrder.push(ecKey);
|
|
}
|
|
}
|
|
}
|
|
} // for by Cost Centers
|
|
}
|
|
};
|
|
function zeroRowsData(rowsObject) {
|
|
if (!rowsObject) {
|
|
return;
|
|
}
|
|
|
|
for (var key in rowsObject) {
|
|
var row = rowsObject[key];
|
|
if (!row)
|
|
continue;
|
|
if (row.Cells) {
|
|
row.Cells = new Array($scope.InternalData.Header.Weeks.length);
|
|
}
|
|
if (row.Total) {
|
|
if (angular.isNumber(row.Total.Value1)) {
|
|
row.Total.Value1 = 0;
|
|
}
|
|
if (angular.isNumber(row.Total.Value2)) {
|
|
row.Total.Value2 = 0;
|
|
}
|
|
row.Total.Allocated = 0;
|
|
row.Total.Needed = 0;
|
|
row.Total.Capacity = 0;
|
|
row.Total.NonProjectTime = 0;
|
|
row.Total.SuperECAllocations = 0;
|
|
}
|
|
if (typeof row === 'object') {
|
|
zeroRowsData(row);
|
|
}
|
|
};
|
|
};
|
|
function initRowsData() {
|
|
hideRedundantTooltips();
|
|
if (!$scope.InternalData.Header.Months || !$scope.InternalData.Header.Weeks)
|
|
return;
|
|
|
|
for (var dataKey in $scope.DisplayData) {
|
|
var fillCellFunction = undefined,
|
|
originalRow = $scope.DisplayData[dataKey];
|
|
|
|
switch (originalRow.Type) {
|
|
case $scope.RowType.NonProjectTime:
|
|
fillCellFunction = fillNonProjectTimeCell;
|
|
break;
|
|
case $scope.RowType.Team:
|
|
fillCellFunction = fillTeamCell;
|
|
break;
|
|
case $scope.RowType.ExpenditureCategory:
|
|
fillCellFunction = fillExpCatCell;
|
|
break;
|
|
case $scope.RowType.Company:
|
|
fillCellFunction = fillCompanyCell;
|
|
break;
|
|
case $scope.RowType.CostCenter:
|
|
fillCellFunction = fillCostCenterCell;
|
|
break;
|
|
}
|
|
|
|
for (var mIndex in $scope.InternalData.Header.Months) {
|
|
var month = $scope.InternalData.Header.Months[mIndex];
|
|
if (!month.Childs)
|
|
continue;
|
|
|
|
for (var wIndex in month.Childs) {
|
|
if (fillCellFunction == undefined || typeof fillCellFunction !== 'function') {
|
|
updateTotalRowCell(originalRow, month.Childs[wIndex], month.SelfIndexInWeeks, [0, 0]);
|
|
} else {
|
|
fillCellFunction(month.Childs[wIndex], month.SelfIndexInWeeks, originalRow);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
function fillNonProjectTimeCell(wIndex, mIndex, originalRow) {
|
|
var week = $scope.InternalData.Header.Weeks[wIndex];
|
|
var totalValue = 0;
|
|
var categories = {};
|
|
|
|
var teams = teamInfoService.getAll();
|
|
if (teams) {
|
|
for (var teamId in teams) {
|
|
var team = teams[teamId];
|
|
if (!team || !team.ExpCategories)
|
|
continue;
|
|
|
|
for (var expCatId in team.ExpCategories) {
|
|
var category = team.ExpCategories[expCatId];
|
|
if (!category || !category.Resources || !category.AllowResourceAssignment)
|
|
continue;
|
|
|
|
for (var resourceId in category.Resources) {
|
|
if (!category.Resources[resourceId])
|
|
continue;
|
|
|
|
var resource = teamInfoService.extendResourceModelWithAttributes(category.Resources[resourceId], teamId);
|
|
|
|
if (!resource || !resource.NonProjectTime)
|
|
continue;
|
|
|
|
for (var npTimeId in resource.NonProjectTime) {
|
|
var npTime = resource.NonProjectTime[npTimeId];
|
|
var npTimeCategoryId = npTime.CategoryId;
|
|
var v = npTime.Allocations[week.Milliseconds] || 0;
|
|
if (!$scope.DisplayMode.IsUOMHours) {
|
|
v = hoursResourcesConverter.convertToResources(resource.OwnExpenditureCategoryId, v);
|
|
}
|
|
|
|
if (categories[npTimeCategoryId] == undefined)
|
|
categories[npTimeCategoryId] = 0;
|
|
categories[npTimeCategoryId] += v;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
angular.forEach(categories, function (value, key) {
|
|
totalValue += (value || 0);
|
|
updateSimpleCell($scope.DisplayData["NonProjectTime"].Categories[key], wIndex, mIndex, value || 0);
|
|
});
|
|
|
|
updateSimpleCell(originalRow, wIndex, mIndex, totalValue);
|
|
};
|
|
function fillExpCatCell(wIndex, mIndex, originalRow) {
|
|
if (!originalRow || !originalRow.Id) {
|
|
return;
|
|
}
|
|
|
|
var teamIds = null;
|
|
var values = undefined;
|
|
var week = $scope.InternalData.Header.Weeks[wIndex];
|
|
var expCatId = originalRow.Id;
|
|
|
|
switch ($scope.DisplayMode.GroupBy) {
|
|
case groupByEnum.Team:
|
|
// Expenditures grouped by teams
|
|
teamIds = [originalRow.ParentId];
|
|
break;
|
|
case groupByEnum.Company:
|
|
// Expenditures grouped by companies
|
|
var companyTeams = teamInfoService.getTeamsByCompanyId(originalRow.ParentId);
|
|
if (companyTeams) {
|
|
teamIds = Object.keys(companyTeams);
|
|
}
|
|
break;
|
|
default:
|
|
// Get data by all teams, expenditure belongs to
|
|
teamIds = null;
|
|
}
|
|
|
|
// Get expcenditure cell values with expenditure assigned resources
|
|
values = getExpCatCellValue(expCatId, teamIds, week.Milliseconds, true);
|
|
|
|
values.Value1 = angular.isNumber(values.Value1) ? roundService.roundQuantity(values.Value1) : values.Value1;
|
|
values.Value2 = angular.isNumber(values.Value2) ? roundService.roundQuantity(values.Value2) : values.Value2;
|
|
values.Allocated = roundService.roundQuantity(values.Allocated);
|
|
values.Needed = roundService.roundQuantity(values.Needed);
|
|
values.Capacity = roundService.roundQuantity(values.Capacity);
|
|
values.NonProjectTime = roundService.roundQuantity(values.NonProjectTime);
|
|
values.SuperECAllocations = roundService.roundQuantity(values.SuperECAllocations);
|
|
|
|
updateTotalRowCell(originalRow, wIndex, mIndex, values);
|
|
|
|
if (values.Resources) {
|
|
updateResourcesCell(originalRow, wIndex, mIndex, values.Resources, week.Milliseconds);
|
|
}
|
|
};
|
|
function fillTeamCell(wIndex, mIndex, originalRow) {
|
|
if (!originalRow || !originalRow.Id) {
|
|
return;
|
|
}
|
|
|
|
var week = $scope.InternalData.Header.Weeks[wIndex];
|
|
var result = {
|
|
Value1: 0,
|
|
Value2: 0,
|
|
Allocated: 0,
|
|
Needed: 0,
|
|
Capacity: 0,
|
|
NonProjectTime: 0,
|
|
SuperECAllocations: 0
|
|
};
|
|
|
|
var totalsAsParsed = parseInt($scope.DisplayMode.TotalsAs);
|
|
var teamId = originalRow.Id;
|
|
var teamIds = [teamId];
|
|
var teamExpCats = teamInfoService.getExpenditureCategoriesInTeam(teamId);
|
|
if (!teamExpCats || !angular.isArray(teamExpCats) || !teamExpCats.length) {
|
|
return result;
|
|
}
|
|
|
|
for (var eIndex = 0; eIndex < teamExpCats.length; eIndex++) {
|
|
var expCatId = teamExpCats[eIndex];
|
|
var expCatInfo = dataSources.getExpenditureById(expCatId);
|
|
var isProjectRole = !expCatInfo.AllowResourceAssignment;
|
|
|
|
// In Allocated/Capacity mode perform sum only by ordinary ExpCats, because project role are not displayed
|
|
var takeExpCatInAccount = (totalsAsParsed != 1) || !isProjectRole;
|
|
|
|
if (takeExpCatInAccount) {
|
|
var val = getExpCatCellValue(expCatId, teamIds, week.Milliseconds, false);
|
|
|
|
if (angular.isNumber(val.Value1))
|
|
result.Value1 += val.Value1;
|
|
|
|
if (angular.isNumber(val.Value2))
|
|
result.Value2 += val.Value2;
|
|
|
|
result.Allocated += val.Allocated || 0;
|
|
result.Needed += (!isProjectRole ? (val.Needed || 0) : (val.RemainingNeed || 0));
|
|
result.Capacity += val.Capacity || 0;
|
|
result.NonProjectTime += val.NonProjectTime || 0;
|
|
result.SuperECAllocations += val.SuperECAllocations || 0;
|
|
}
|
|
}
|
|
|
|
result.Value1 = roundService.roundQuantity(result.Value1);
|
|
result.Value2 = roundService.roundQuantity(result.Value2);
|
|
result.Allocated = roundService.roundQuantity(result.Allocated);
|
|
result.Needed = roundService.roundQuantity(result.Needed);
|
|
result.Capacity = roundService.roundQuantity(result.Capacity);
|
|
result.NonProjectTime = roundService.roundQuantity(result.NonProjectTime);
|
|
result.SuperECAllocations = roundService.roundQuantity(result.SuperECAllocations);
|
|
|
|
updateTotalRowCell(originalRow, wIndex, mIndex, result);
|
|
};
|
|
function fillCompanyCell(wIndex, mIndex, originalRow) {
|
|
if (!originalRow || !originalRow.Id) {
|
|
return;
|
|
}
|
|
|
|
var week = $scope.InternalData.Header.Weeks[wIndex];
|
|
var result = {
|
|
Value1: 0,
|
|
Value2: 0,
|
|
Allocated: 0,
|
|
Needed: 0,
|
|
Capacity: 0,
|
|
NonProjectTime: 0,
|
|
SuperECAllocations: 0
|
|
};
|
|
|
|
var totalsAsParsed = parseInt($scope.DisplayMode.TotalsAs);
|
|
var companyId = originalRow.Id;
|
|
var teams = teamInfoService.getTeamsByCompanyId(companyId);
|
|
if (teams) {
|
|
var teamIds = Object.keys(teams);
|
|
var expCatIds = teamInfoService.getExpenditureCategoriesInTeams(teamIds);
|
|
|
|
// Iterate by Categories instead of teams, because we have a ready-to-use method to get total values for
|
|
// a category by a set of teams
|
|
for (var eIndex = 0; eIndex < expCatIds.length; eIndex++) {
|
|
var expCatId = expCatIds[eIndex];
|
|
var expCatInfo = dataSources.getExpenditureById(expCatId);
|
|
var isProjectRole = !expCatInfo.AllowResourceAssignment;
|
|
|
|
// In Allocated/Capacity mode perform sum only by ordinary ExpCats, because project role are not displayed
|
|
var takeExpCatInAccount = (totalsAsParsed != 1) || !isProjectRole;
|
|
|
|
if (takeExpCatInAccount) {
|
|
var val = getExpCatCellValue(expCatId, teamIds, week.Milliseconds, false);
|
|
|
|
if (angular.isNumber(val.Value1))
|
|
result.Value1 += val.Value1;
|
|
|
|
if (angular.isNumber(val.Value2))
|
|
result.Value2 += val.Value2;
|
|
|
|
result.Allocated += val.Allocated || 0;
|
|
result.Needed += (!isProjectRole ? (val.Needed || 0) : (val.RemainingNeed || 0));
|
|
result.Capacity += val.Capacity || 0;
|
|
result.NonProjectTime += val.NonProjectTime || 0;
|
|
result.SuperECAllocations += val.SuperECAllocations || 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
result.Value1 = roundService.roundQuantity(result.Value1);
|
|
result.Value2 = roundService.roundQuantity(result.Value2);
|
|
result.Allocated = roundService.roundQuantity(result.Allocated);
|
|
result.Needed = roundService.roundQuantity(result.Needed);
|
|
result.Capacity = roundService.roundQuantity(result.Capacity);
|
|
result.NonProjectTime = roundService.roundQuantity(result.NonProjectTime);
|
|
result.SuperECAllocations = roundService.roundQuantity(result.SuperECAllocations);
|
|
|
|
updateTotalRowCell(originalRow, wIndex, mIndex, result);
|
|
};
|
|
function fillCostCenterCell(wIndex, mIndex, originalRow) {
|
|
if (!originalRow || !originalRow.Id || !$scope.ExpendituresByCostCentersCache) {
|
|
return;
|
|
}
|
|
|
|
var totalsAsParsed = parseInt($scope.DisplayMode.TotalsAs);
|
|
var costCenterId = originalRow.Id;
|
|
if (!(costCenterId in $scope.ExpendituresByCostCentersCache))
|
|
return;
|
|
|
|
var week = $scope.InternalData.Header.Weeks[wIndex];
|
|
var result = {
|
|
Value1: 0,
|
|
Value2: 0,
|
|
Allocated: 0,
|
|
Needed: 0,
|
|
Capacity: 0,
|
|
NonProjectTime: 0,
|
|
SuperECAllocations: 0
|
|
};
|
|
|
|
var costCenterData = $scope.ExpendituresByCostCentersCache[costCenterId];
|
|
if (costCenterData && costCenterData.Expenditures) {
|
|
for (var expCatId in costCenterData.Expenditures) {
|
|
var expCatInfo = dataSources.getExpenditureById(expCatId);
|
|
var isProjectRole = !expCatInfo.AllowResourceAssignment;
|
|
|
|
// In Allocated/Capacity mode perform sum only by ordinary ExpCats, because project role are not displayed
|
|
var takeExpCatInAccount = (totalsAsParsed != 1) || !isProjectRole;
|
|
|
|
if (takeExpCatInAccount) {
|
|
var expCatItem = costCenterData.Expenditures[expCatId];
|
|
|
|
if (expCatItem && expCatItem.Teams && angular.isArray(expCatItem.Teams) && expCatItem.Teams.length) {
|
|
var val = getExpCatCellValue(expCatId, expCatItem.Teams, week.Milliseconds, false);
|
|
|
|
if (angular.isNumber(val.Value1))
|
|
result.Value1 += val.Value1;
|
|
|
|
if (angular.isNumber(val.Value2))
|
|
result.Value2 += val.Value2;
|
|
|
|
result.Allocated += val.Allocated || 0;
|
|
result.Needed += (!isProjectRole ? (val.Needed || 0) : (val.RemainingNeed || 0));
|
|
result.Capacity += val.Capacity || 0;
|
|
result.NonProjectTime += val.NonProjectTime || 0;
|
|
result.SuperECAllocations += val.SuperECAllocations || 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
result.Value1 = roundService.roundQuantity(result.Value1);
|
|
result.Value2 = roundService.roundQuantity(result.Value2);
|
|
result.Allocated = roundService.roundQuantity(result.Allocated);
|
|
result.Needed = roundService.roundQuantity(result.Needed);
|
|
result.Capacity = roundService.roundQuantity(result.Capacity);
|
|
result.NonProjectTime = roundService.roundQuantity(result.NonProjectTime);
|
|
result.SuperECAllocations = roundService.roundQuantity(result.SuperECAllocations);
|
|
|
|
updateTotalRowCell(originalRow, wIndex, mIndex, result);
|
|
}
|
|
};
|
|
function updateSimpleCell(originalRow, weekIndex, monthIndex, newValue) {
|
|
if (!originalRow || !originalRow.Cells)
|
|
return;
|
|
|
|
if (originalRow.Cells[monthIndex] == undefined)
|
|
originalRow.Cells[monthIndex] = 0;
|
|
|
|
if (originalRow.Cells[weekIndex] == undefined)
|
|
originalRow.Cells[weekIndex] = 0;
|
|
|
|
// we should increase/decrease month total value by delta between new and old values
|
|
var showAverageTotals = $scope.isAvgMode();
|
|
var totalsDelta = 0;
|
|
|
|
if (angular.isNumber(newValue) && !isNaN(newValue))
|
|
var totalsDelta = newValue;
|
|
|
|
if (showAverageTotals) {
|
|
var monthIndexInMonthes = $scope.InternalData.Header.Weeks[weekIndex].ParentIndex;
|
|
var weekCountInThisMonth = $scope.InternalData.Header.Months[monthIndexInMonthes].Childs.length;
|
|
totalsDelta = totalsDelta / weekCountInThisMonth;
|
|
}
|
|
|
|
originalRow.Cells[monthIndex] = roundService.roundQuantity(originalRow.Cells[monthIndex] + totalsDelta - originalRow.Cells[weekIndex]);
|
|
originalRow.Cells[weekIndex] = angular.isNumber(newValue) ? roundService.roundQuantity(newValue) : newValue;
|
|
};
|
|
function updateResourceCell(originalRow, weekIndex, monthIndex, newValues) {
|
|
if (!originalRow || !originalRow.Cells)
|
|
return;
|
|
|
|
var monthIndexInMonthes = $scope.InternalData.Header.Weeks[weekIndex].ParentIndex;
|
|
var weekCountInThisMonth = $scope.InternalData.Header.Months[monthIndexInMonthes].Childs.length;
|
|
var totalWeeks = $scope.InternalData.Header.TotalWeeks;
|
|
|
|
if (originalRow.Cells[monthIndex] == undefined)
|
|
originalRow.Cells[monthIndex] = {
|
|
Value1: 0,
|
|
Value2: 0,
|
|
Allocated: 0,
|
|
Needed: 0,
|
|
Capacity: 0,
|
|
NonProjectTime: 0,
|
|
SuperECAllocations: 0
|
|
};
|
|
|
|
if (originalRow.Cells[weekIndex] == undefined)
|
|
originalRow.Cells[weekIndex] = {
|
|
Value1: 0,
|
|
Value2: 0,
|
|
Allocated: 0,
|
|
Needed: 0,
|
|
Capacity: 0,
|
|
NonProjectTime: 0,
|
|
SuperECAllocations: 0
|
|
};
|
|
|
|
// we should to increase/decrease month total value by delta between new and old values
|
|
var totalsDelta = {};
|
|
totalsDelta.Value1 = newValues.Value1 - originalRow.Cells[weekIndex].Value1;
|
|
totalsDelta.Value2 = newValues.Value2 - originalRow.Cells[weekIndex].Value2;
|
|
totalsDelta.Allocated = newValues.Allocated - originalRow.Cells[weekIndex].Allocated;
|
|
totalsDelta.Needed = newValues.Needed - originalRow.Cells[weekIndex].Needed;
|
|
totalsDelta.Capacity = newValues.Capacity - originalRow.Cells[weekIndex].Capacity;
|
|
totalsDelta.NonProjectTime = newValues.NonProjectTime - originalRow.Cells[weekIndex].NonProjectTime;
|
|
totalsDelta.SuperECAllocations = newValues.SuperECAllocations - originalRow.Cells[weekIndex].SuperECAllocations;
|
|
|
|
var showAverageTotals = $scope.isAvgMode();
|
|
|
|
originalRow.Total.Value1 = roundService.roundQuantity(originalRow.Total.Value1 + (showAverageTotals ? (totalsDelta.Value1 / totalWeeks): totalsDelta.Value1));
|
|
originalRow.Total.Value2 = roundService.roundQuantity(originalRow.Total.Value2 + (showAverageTotals ? (totalsDelta.Value2 / totalWeeks) : totalsDelta.Value2));
|
|
originalRow.Total.Allocated = roundService.roundQuantity(originalRow.Total.Allocated + (showAverageTotals ? (totalsDelta.Allocated / totalWeeks) : totalsDelta.Allocated));
|
|
originalRow.Total.Needed = roundService.roundQuantity(originalRow.Total.Needed + (showAverageTotals ? (totalsDelta.Needed / totalWeeks) : totalsDelta.Needed));
|
|
originalRow.Total.Capacity = roundService.roundQuantity(originalRow.Total.Capacity + (showAverageTotals ? (totalsDelta.Capacity / totalWeeks) : totalsDelta.Capacity));
|
|
originalRow.Total.NonProjectTime = roundService.roundQuantity(originalRow.Total.NonProjectTime + (showAverageTotals ? (totalsDelta.NonProjectTime / totalWeeks): totalsDelta.NonProjectTime));
|
|
originalRow.Total.SuperECAllocations = roundService.roundQuantity(originalRow.Total.SuperECAllocations + (showAverageTotals ? (totalsDelta.SuperECAllocations / totalWeeks): totalsDelta.SuperECAllocations));
|
|
|
|
originalRow.Cells[monthIndex].Value1 = roundService.roundQuantity(originalRow.Cells[monthIndex].Value1 + (showAverageTotals ? (totalsDelta.Value1 / weekCountInThisMonth) : totalsDelta.Value1));
|
|
originalRow.Cells[monthIndex].Value2 = roundService.roundQuantity(originalRow.Cells[monthIndex].Value2 + (showAverageTotals ? (totalsDelta.Value2 / weekCountInThisMonth): totalsDelta.Value2));
|
|
originalRow.Cells[monthIndex].Allocated = roundService.roundQuantity(originalRow.Cells[monthIndex].Allocated + (showAverageTotals ? (totalsDelta.Allocated / weekCountInThisMonth): totalsDelta.Allocated));
|
|
originalRow.Cells[monthIndex].Needed = roundService.roundQuantity(originalRow.Cells[monthIndex].Needed + (showAverageTotals ? (totalsDelta.Needed / weekCountInThisMonth) : totalsDelta.Needed));
|
|
originalRow.Cells[monthIndex].Capacity = roundService.roundQuantity(originalRow.Cells[monthIndex].Capacity + (showAverageTotals ? (totalsDelta.Capacity / weekCountInThisMonth) : totalsDelta.Capacity));
|
|
originalRow.Cells[monthIndex].NonProjectTime = roundService.roundQuantity(originalRow.Cells[monthIndex].NonProjectTime + (showAverageTotals ? (totalsDelta.NonProjectTime / weekCountInThisMonth): totalsDelta.NonProjectTime));
|
|
originalRow.Cells[monthIndex].SuperECAllocations = roundService.roundQuantity(originalRow.Cells[monthIndex].SuperECAllocations + (showAverageTotals ? (totalsDelta.SuperECAllocations / weekCountInThisMonth) : totalsDelta.SuperECAllocations));
|
|
originalRow.Cells[weekIndex] = newValues;
|
|
updateWeeklyCellCssClass(originalRow, weekIndex);
|
|
};
|
|
function updateResourcesCell(originalRow, weekIndex, monthIndex, resources, milliseconds) {
|
|
if (!originalRow || !originalRow.Resources)
|
|
return;
|
|
|
|
for (var resIndex = 0; resIndex < originalRow.Resources.length; resIndex++) {
|
|
var resRow = originalRow.Resources[resIndex];
|
|
var newValues = {
|
|
Value1: 0,
|
|
Value2: 0,
|
|
Allocated: 0,
|
|
Needed: 0,
|
|
Capacity: 0,
|
|
NonProjectTime: 0,
|
|
SuperECAllocations: 0
|
|
};
|
|
for (var i = 0; i < resources.length; i++) {
|
|
if (resRow.Id == resources[i].Id) {
|
|
var resource = resources[i];
|
|
|
|
if (!resourceHasTeam(resource, milliseconds))
|
|
continue;
|
|
|
|
var values = getResourceCellValue(resource, milliseconds, originalRow);
|
|
newValues = concatValues(newValues, values);
|
|
}
|
|
}
|
|
updateResourceCell(resRow, weekIndex, monthIndex, newValues);
|
|
}
|
|
};
|
|
function updateTotalRowCell(originalRow, weekIndex, monthIndex, newValues) {
|
|
if (!originalRow || !originalRow.Cells)
|
|
return;
|
|
|
|
if (originalRow.Cells[monthIndex] == undefined) {
|
|
if ((originalRow.Type == $scope.RowType.ExpenditureCategory) && !originalRow.AllowResourceAssignment)
|
|
originalRow.Cells[monthIndex] = {
|
|
Value1: '-',
|
|
Value2: '-',
|
|
Allocated: 0,
|
|
Needed: 0,
|
|
Capacity: 0,
|
|
NonProjectTime: 0,
|
|
SuperECAllocations: 0
|
|
};
|
|
else
|
|
originalRow.Cells[monthIndex] = {
|
|
Value1: 0,
|
|
Value2: 0,
|
|
Allocated: 0,
|
|
Needed: 0,
|
|
Capacity: 0,
|
|
NonProjectTime: 0,
|
|
SuperECAllocations: 0
|
|
};
|
|
}
|
|
|
|
if (originalRow.Cells[weekIndex] == undefined) {
|
|
if ((originalRow.Type == $scope.RowType.ExpenditureCategory) && !originalRow.AllowResourceAssignment)
|
|
originalRow.Cells[weekIndex] = {
|
|
Value1: '-',
|
|
Value2: '-',
|
|
Allocated: 0,
|
|
Needed: 0,
|
|
Capacity: 0,
|
|
NonProjectTime: 0,
|
|
SuperECAllocations: 0
|
|
};
|
|
else
|
|
originalRow.Cells[weekIndex] = {
|
|
Value1: 0,
|
|
Value2: 0,
|
|
Allocated: 0,
|
|
Needed: 0,
|
|
Capacity: 0,
|
|
NonProjectTime: 0,
|
|
SuperECAllocations: 0
|
|
};
|
|
}
|
|
|
|
var monthIndexInMonthes = $scope.InternalData.Header.Weeks[weekIndex].ParentIndex;
|
|
var weekCountInThisMonth = $scope.InternalData.Header.Months[monthIndexInMonthes].Childs.length;
|
|
var totalWeeks = $scope.InternalData.Header.TotalWeeks;
|
|
var showAverageTotals = $scope.isAvgMode();
|
|
|
|
var totalsDelta = {
|
|
Value1: 0,
|
|
Value2: 0,
|
|
Allocated: 0,
|
|
Needed: 0,
|
|
Capacity: 0,
|
|
NonProjectTime: 0,
|
|
SuperECAllocations: 0
|
|
};
|
|
|
|
var newValue1Analyzed = angular.isNumber(newValues.Value1) ? newValues.Value1 : 0;
|
|
var newValue2Analyzed = angular.isNumber(newValues.Value2) ? newValues.Value2 : 0;
|
|
var oldValue1Analyzed = angular.isNumber(originalRow.Cells[weekIndex].Value1) ? originalRow.Cells[weekIndex].Value1 : 0;
|
|
var oldValue2Analyzed = angular.isNumber(originalRow.Cells[weekIndex].Value2) ? originalRow.Cells[weekIndex].Value2 : 0;
|
|
|
|
totalsDelta.Value1 = newValue1Analyzed - oldValue1Analyzed;
|
|
totalsDelta.Value2 = newValue2Analyzed - oldValue2Analyzed;
|
|
totalsDelta.Allocated = (newValues.Allocated || 0) - (originalRow.Cells[weekIndex].Allocated || 0);
|
|
totalsDelta.Needed = (newValues.Needed || 0) - (originalRow.Cells[weekIndex].Needed || 0);
|
|
totalsDelta.Capacity = (newValues.Capacity || 0) - (originalRow.Cells[weekIndex].Capacity || 0);
|
|
totalsDelta.NonProjectTime = (newValues.NonProjectTime || 0) - (originalRow.Cells[weekIndex].NonProjectTime || 0);
|
|
totalsDelta.SuperECAllocations = (newValues.SuperECAllocations || 0) - (originalRow.Cells[weekIndex].SuperECAllocations || 0);
|
|
|
|
if (angular.isNumber(newValues.Value1)) {
|
|
if (angular.isNumber(originalRow.Cells[monthIndex].Value1))
|
|
originalRow.Cells[monthIndex].Value1 += (showAverageTotals ? (totalsDelta.Value1 / weekCountInThisMonth) : totalsDelta.Value1);
|
|
else
|
|
originalRow.Cells[monthIndex].Value1 = (showAverageTotals ? (totalsDelta.Value1 / weekCountInThisMonth) : totalsDelta.Value1);
|
|
|
|
if (angular.isNumber(originalRow.Total.Value1))
|
|
originalRow.Total.Value1 += (showAverageTotals ? (totalsDelta.Value1 / totalWeeks) : totalsDelta.Value1);
|
|
else
|
|
originalRow.Total.Value1 = (showAverageTotals ? (totalsDelta.Value1 / totalWeeks) : totalsDelta.Value1);
|
|
}
|
|
|
|
if (angular.isNumber(newValues.Value2)) {
|
|
if (angular.isNumber(originalRow.Cells[monthIndex].Value2))
|
|
originalRow.Cells[monthIndex].Value2 += (showAverageTotals ? (totalsDelta.Value2 / weekCountInThisMonth) : totalsDelta.Value2);
|
|
else
|
|
originalRow.Cells[monthIndex].Value2 = (showAverageTotals ? (totalsDelta.Value2 / weekCountInThisMonth) : totalsDelta.Value2);
|
|
|
|
if (angular.isNumber(originalRow.Total.Value2))
|
|
originalRow.Total.Value2 += (showAverageTotals ? (totalsDelta.Value2 / totalWeeks) : totalsDelta.Value2);
|
|
else
|
|
originalRow.Total.Value2 = (showAverageTotals ? (totalsDelta.Value2 / totalWeeks) : totalsDelta.Value2);
|
|
}
|
|
|
|
originalRow.Cells[monthIndex].Allocated += (showAverageTotals ? (totalsDelta.Allocated / weekCountInThisMonth) : totalsDelta.Allocated);
|
|
originalRow.Cells[monthIndex].Needed += (showAverageTotals ? (totalsDelta.Needed / weekCountInThisMonth) : totalsDelta.Needed);
|
|
originalRow.Cells[monthIndex].Capacity += (showAverageTotals ? (totalsDelta.Capacity / weekCountInThisMonth) : totalsDelta.Capacity);
|
|
originalRow.Cells[monthIndex].NonProjectTime += (showAverageTotals ? (totalsDelta.NonProjectTime / weekCountInThisMonth) : totalsDelta.NonProjectTime);
|
|
originalRow.Cells[monthIndex].SuperECAllocations += (showAverageTotals ? (totalsDelta.SuperECAllocations / weekCountInThisMonth) : totalsDelta.SuperECAllocations);
|
|
|
|
originalRow.Total.Allocated += (showAverageTotals ? (totalsDelta.Allocated / totalWeeks) : totalsDelta.Allocated);
|
|
originalRow.Total.Needed += (showAverageTotals ? (totalsDelta.Needed / totalWeeks) : totalsDelta.Needed);
|
|
originalRow.Total.Capacity += (showAverageTotals ? (totalsDelta.Capacity / totalWeeks) : totalsDelta.Capacity);
|
|
originalRow.Total.NonProjectTime += (showAverageTotals ? (totalsDelta.NonProjectTime / totalWeeks) : totalsDelta.NonProjectTime);
|
|
originalRow.Total.SuperECAllocations += (showAverageTotals ? (totalsDelta.SuperECAllocations / totalWeeks) : totalsDelta.SuperECAllocations);
|
|
|
|
originalRow.Cells[weekIndex] = newValues;
|
|
updateWeeklyCellCssClass(originalRow, weekIndex);
|
|
};
|
|
function updateWeeklyCellCssClass(row, weekIndex) {
|
|
// Set cells highlighting
|
|
if ((row.Type == $scope.RowType.ExpenditureCategory) && !row.AllowResourceAssignment) {
|
|
updateProjectRoleCellCssClass(row, weekIndex);
|
|
}
|
|
else {
|
|
switch (parseInt($scope.DisplayMode.TotalsAs)) {
|
|
case 4: //Remaining/Capacity
|
|
updateRemainingCapacityCssClass(row, weekIndex);
|
|
break;
|
|
default:
|
|
cellHighlightingService.setCellCssClasses(row, weekIndex);
|
|
break;
|
|
}
|
|
}
|
|
var monthIndexInMonthes = $scope.InternalData.Header.Weeks[weekIndex].ParentIndex;
|
|
var month = $scope.InternalData.Header.Months[monthIndexInMonthes];
|
|
|
|
updateMonthlyCellCssClass(row, month);
|
|
};
|
|
function updateMonthlyCellCssClass(row, month) {
|
|
if (!row.CSSClass)
|
|
return;
|
|
var isOverAllocated = false,
|
|
isUnderAllocated = false,
|
|
isEquals = true;
|
|
var monthWeeks = month.Childs;
|
|
var monthIndex = month.SelfIndexInWeeks;
|
|
|
|
for (var i in monthWeeks) {
|
|
var cssClasses = row.CSSClass[monthWeeks[i]];
|
|
if (cssClasses == cellHighlightingService.cellOverClass) {
|
|
isOverAllocated = true;
|
|
isEquals = false;
|
|
}
|
|
else if (cssClasses == cellHighlightingService.cellLessClass) {
|
|
isUnderAllocated = true;
|
|
isEquals = false;
|
|
}
|
|
else if (cssClasses != cellHighlightingService.cellEqualClass && cssClasses != '') {
|
|
isEquals = false;
|
|
}
|
|
}
|
|
|
|
if (isOverAllocated)
|
|
row.CSSClass[monthIndex] = cellHighlightingService.cellOverClass;
|
|
else if (isUnderAllocated)
|
|
row.CSSClass[monthIndex] = cellHighlightingService.cellLessClass;
|
|
else if (isEquals)
|
|
row.CSSClass[monthIndex] = cellHighlightingService.cellEqualClass;
|
|
else
|
|
row.CSSClass[monthIndex] = '';
|
|
};
|
|
function updateRemainingCapacityCssClass(row, weekIndex) {
|
|
if (!row || isNaN(weekIndex))
|
|
return;
|
|
|
|
row.CSSClass[weekIndex] = cellHighlightingService.removeHighlightClasses(row.CSSClass[weekIndex]);
|
|
var compareResult = null;
|
|
if (row.Cells[weekIndex].Value1 < 0)
|
|
compareResult = 1;
|
|
else if (row.Cells[weekIndex].Value1 > 0)
|
|
compareResult = -1;
|
|
else if (row.Cells[weekIndex].Value1 == 0)
|
|
if (row.Cells[weekIndex].Capacity == 0)
|
|
compareResult = null; // do not validate zeros
|
|
else
|
|
compareResult = 0;
|
|
var compareResultClass = cellHighlightingService.getCssClass(compareResult);
|
|
var resultCellClasses = cellHighlightingService.addCssClass(row.CSSClass[weekIndex], compareResultClass);
|
|
|
|
row.CSSClass[weekIndex] = resultCellClasses;
|
|
};
|
|
function updateProjectRoleCellCssClass(row, weekIndex) {
|
|
if (!row || isNaN(weekIndex))
|
|
return;
|
|
|
|
row.CSSClass[weekIndex] = cellHighlightingService.removeHighlightClasses(row.CSSClass[weekIndex]);
|
|
var compareResult = null;
|
|
var cell = row.Cells[weekIndex];
|
|
var need = angular.isNumber(cell.Needed) && !isNaN(cell.Needed) ? Number(cell.Needed) : 0;
|
|
var allocated = angular.isNumber(cell.Allocated) && !isNaN(cell.Allocated) ? Number(cell.Allocated) : 0;
|
|
|
|
if ((allocated == 0) && (need == 0))
|
|
// Do not highlight cells with no values
|
|
return;
|
|
|
|
if (allocated > need)
|
|
compareResult = 1;
|
|
|
|
if (allocated < need)
|
|
compareResult = -1;
|
|
|
|
if (allocated == need)
|
|
compareResult = 0;
|
|
|
|
var highlightingClassToAdd = cellHighlightingService.getCssClass(compareResult);
|
|
var resultCellClasses = cellHighlightingService.addCssClass(row.CSSClass[weekIndex], highlightingClassToAdd);
|
|
row.CSSClass[weekIndex] = resultCellClasses;
|
|
};
|
|
function getExpCatCellValue(expCatId, teamIds, we, withResources) {
|
|
if (!expCatId || !we)
|
|
return null;
|
|
|
|
var result = {
|
|
Value1: '-',
|
|
Value2: '-',
|
|
Allocated: 0,
|
|
Needed: 0,
|
|
Capacity: 0,
|
|
NonProjectTime: 0,
|
|
SuperECAllocations: 0
|
|
};
|
|
|
|
// Get teams, the expenditure belongs to (from incoming array or from service)
|
|
var teamsToBrowse = getExpenditureTeams(expCatId, teamIds);
|
|
if (!teamsToBrowse || !angular.isArray(teamsToBrowse) || !teamsToBrowse.length) {
|
|
// No teams for expenditure found
|
|
return result;
|
|
}
|
|
|
|
// Get expenditure type: ordinary EC or project role
|
|
var randomExpenditureTeamId = teamsToBrowse[0];
|
|
var expCatItem = teamInfoService.getExpenditureCategoryInTeam(randomExpenditureTeamId, expCatId);
|
|
if (!expCatItem)
|
|
return result;
|
|
|
|
// Get agregated data for expenditure by specified team list
|
|
var result = expCatItem.AllowResourceAssignment ? getOrdinaryExpCatCellValue(expCatId, teamsToBrowse, we) : getProjectRoleCellValue(expCatId, teamsToBrowse, we);
|
|
if (!result)
|
|
return result;
|
|
|
|
if (withResources) {
|
|
// Get resources for expenditure
|
|
result.Resources = [];
|
|
|
|
for (var tIndex = 0; tIndex < teamsToBrowse.length; tIndex++) {
|
|
var teamId = teamsToBrowse[tIndex];
|
|
var resourceModels = teamInfoService.getResourcesByTeam(teamId, expCatId);
|
|
|
|
if (resourceModels) {
|
|
for (var resourceId in resourceModels) {
|
|
result.Resources.push(resourceModels[resourceId]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
};
|
|
function getOrdinaryExpCatCellValue(expCatId, teamIds, we) {
|
|
// Return value (2 items) for ordinary EC cell. Converted to Resources/hours according to current display settings
|
|
if (!expCatId)
|
|
throw "getOrdinaryExpCatCellValue: 'expCatId' parameter is empty";
|
|
|
|
if (!we)
|
|
throw "getOrdinaryExpCatCellValue: 'we' parameter is empty";
|
|
|
|
var result = {
|
|
Value1: 0,
|
|
Value2: 0,
|
|
Allocated: 0,
|
|
Needed: 0,
|
|
Capacity: 0,
|
|
NonProjectTime: 0,
|
|
SuperECAllocations: 0,
|
|
IsProjectRole: false
|
|
};
|
|
|
|
var val1 = 0;
|
|
var val2 = 0;
|
|
var allocated = 0;
|
|
var needed = 0;
|
|
var capacity = 0;
|
|
var npts = 0;
|
|
var superECAllocs = 0;
|
|
|
|
// Get teams to loop through
|
|
var teamsToBrowse = getExpenditureTeams(expCatId, teamIds);
|
|
if (!teamsToBrowse || !angular.isArray(teamsToBrowse) || !teamsToBrowse.length)
|
|
return result;
|
|
|
|
for (var tIndex = 0; tIndex < teamsToBrowse.length; tIndex++) {
|
|
var teamId = teamsToBrowse[tIndex];
|
|
var expCat = teamInfoService.getExpenditureCategoryInTeam(teamId, expCatId, false);
|
|
|
|
if (expCat) {
|
|
capacity += ($scope.DisplayMode.CapacityView ? (expCat.ActualCapacityValues[we] || 0) : (capacity = expCat.PlannedCapacityValues[we] || 0));
|
|
allocated += (expCat.AllocatedCapacity[we] || 0);
|
|
needed += expCat.NeedCapacity[we] || 0;
|
|
|
|
if (expCat.Resources) {
|
|
// Get NPT allocations and time, allocated to project roles
|
|
for (var resourceId in expCat.Resources) {
|
|
var resourceInExpCat = expCat.Resources[resourceId];
|
|
var resourceModel = dataSources.getResourceById(resourceId);
|
|
var resourceNpt = teamInfoService.getResourceSummaryNptAllocation(resourceInExpCat, we) || 0;
|
|
var resourceSuperECAllocs = teamInfoService.getSuperEcsTotalAllocatedHrs(resourceId, we) || 0;
|
|
|
|
if (!$scope.DisplayMode.IsUOMHours) {
|
|
// Instant convertion of values to resources - get resource's own expenditure
|
|
npts += hoursResourcesConverter.convertToResources(resourceModel.ExpenditureCategoryId, resourceNpt);
|
|
superECAllocs += hoursResourcesConverter.convertToResources(resourceModel.ExpenditureCategoryId, resourceSuperECAllocs);
|
|
}
|
|
else {
|
|
npts += resourceNpt;
|
|
superECAllocs += resourceSuperECAllocs;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!$scope.DisplayMode.IsUOMHours) {
|
|
// Instant convertion of values to resources
|
|
allocated = hoursResourcesConverter.convertToResources(expCatId, allocated);
|
|
needed = hoursResourcesConverter.convertToResources(expCatId, needed);
|
|
capacity = hoursResourcesConverter.convertToResources(expCatId, capacity);
|
|
}
|
|
|
|
switch (parseInt($scope.DisplayMode.TotalsAs)) {
|
|
case 1:
|
|
// Allocated/Capacity mode
|
|
val1 += (allocated + npts + superECAllocs);
|
|
val2 += capacity;
|
|
break;
|
|
case 2:
|
|
// Need/Capacity mode
|
|
val1 = needed;
|
|
val2 = capacity;
|
|
break;
|
|
case 3:
|
|
// Allocated/Need mode
|
|
val1 = allocated + npts + superECAllocs;
|
|
val2 = needed;
|
|
break;
|
|
case 4:
|
|
// Remaining/Capacity mode
|
|
val1 = capacity - needed - superECAllocs;
|
|
val2 = capacity;
|
|
break;
|
|
default:
|
|
throw "Display mode TotalAs (" + $scope.DisplayMode.TotalsAs + ") not supported";
|
|
}
|
|
|
|
result = {
|
|
Value1: roundService.roundQuantity(val1),
|
|
Value2: roundService.roundQuantity(val2),
|
|
Allocated: roundService.roundQuantity(allocated),
|
|
Needed: roundService.roundQuantity(needed),
|
|
Capacity: roundService.roundQuantity(capacity),
|
|
NonProjectTime: roundService.roundQuantity(npts),
|
|
SuperECAllocations: roundService.roundQuantity(superECAllocs),
|
|
IsProjectRole: false
|
|
};
|
|
|
|
return result;
|
|
};
|
|
function getProjectRoleCellValue(expCatId, teamIds, we) {
|
|
if (!expCatId)
|
|
throw "getProjectRoleCellValue: expCatId parameter is empty";
|
|
|
|
if (!we)
|
|
throw "getProjectRoleCellValue: we parameter is empty";
|
|
|
|
var result = {
|
|
Value1: 0,
|
|
Value2: 0,
|
|
Allocated: 0,
|
|
Needed: 0,
|
|
RemainingNeed: 0,
|
|
IsProjectRole: true
|
|
};
|
|
|
|
var val1 = 0;
|
|
var val2 = 0;
|
|
var teamsAllocated = 0;
|
|
// var resourcesAssigned = 0;
|
|
var needed = 0;
|
|
var remainingNeed = 0;
|
|
|
|
// Get teams to loop through
|
|
var teamsToBrowse = getExpenditureTeams(expCatId, teamIds);
|
|
if (!teamsToBrowse || !angular.isArray(teamsToBrowse) || !teamsToBrowse.length)
|
|
return result;
|
|
|
|
var weekendings = [we];
|
|
var expCatNeed = teamInfoService.getExpenditureTotalNeed(expCatId, weekendings);
|
|
var expCatTeamAllocations = teamInfoService.getTeamAllocationsTotalByExpCat(expCatId, teamsToBrowse, weekendings);
|
|
//var expCatResourceAssignments = teamInfoService.getResourceTotalAllocatedHrs(expCatId, teamsToBrowse, null, weekendings);
|
|
|
|
needed = expCatNeed && (we in expCatNeed) ? expCatNeed[we] || 0 : 0;
|
|
teamsAllocated = expCatTeamAllocations && (we in expCatTeamAllocations) ? expCatTeamAllocations[we] || 0 : 0;
|
|
// resourcesAssigned = expCatResourceAssignments && (we in expCatResourceAssignments) ? expCatResourceAssignments[we] || 0 : 0;
|
|
|
|
if (!$scope.DisplayMode.IsUOMHours) {
|
|
// Instant convertion of values to resources
|
|
teamsAllocated = hoursResourcesConverter.convertToResources(expCatId, teamsAllocated);
|
|
needed = hoursResourcesConverter.convertToResources(expCatId, needed);
|
|
}
|
|
remainingNeed = needed - teamsAllocated;
|
|
|
|
// The same calculations for Planned and Actuals display mode
|
|
// For Super EC Allocated = Need always. So, in order to get Need summary value, we calculate Allocated summary
|
|
switch (parseInt($scope.DisplayMode.TotalsAs)) {
|
|
case 1:
|
|
// Allocated/Capacity
|
|
// Project role has no display values in this mode
|
|
val1 = '-';
|
|
val2 = '-';
|
|
break;
|
|
case 2:
|
|
// Need/Capacity
|
|
val1 = remainingNeed;
|
|
val2 = '-';
|
|
break;
|
|
case 3:
|
|
// Allocated/Need
|
|
val1 = teamsAllocated;
|
|
val2 = remainingNeed;
|
|
break;
|
|
case 4:
|
|
// Remaining/Capacity
|
|
val1 = -remainingNeed;
|
|
val2 = '-';
|
|
break;
|
|
default:
|
|
throw "Display mode TotalAs (" + $scope.DisplayMode.TotalsAs + ") not supported";
|
|
}
|
|
|
|
var result = {
|
|
Value1: angular.isNumber(val1) ? roundService.roundQuantity(val1) : val1,
|
|
Value2: angular.isNumber(val2) ? roundService.roundQuantity(val2) : val2,
|
|
Allocated: roundService.roundQuantity(teamsAllocated),
|
|
Needed: roundService.roundQuantity(needed),
|
|
RemainingNeed: roundService.roundQuantity(remainingNeed),
|
|
IsProjectRole: true
|
|
};
|
|
|
|
return result;
|
|
};
|
|
|
|
// returns an object with values to display in Resource row (bottom part)
|
|
function getResourceCellValue(resource, we, expCatRow) {
|
|
var values = {
|
|
Value1: 0,
|
|
Value2: 0,
|
|
Allocated: 0,
|
|
Capacity: 0,
|
|
NonProjectTime: 0,
|
|
SuperECAllocations: 0
|
|
};
|
|
|
|
var summaryAllocated = teamInfoService.getResourceSummaryAllocatedHrs(resource.Id, we);
|
|
values.SuperECAllocations = teamInfoService.getSuperEcsTotalAllocatedHrs(resource.Id, we);
|
|
values.Capacity = resource.TotalCapacity[we] || 0;
|
|
switch (parseInt($scope.DisplayMode.TotalsAs)) {
|
|
case 4:
|
|
// Remaining/Capacity
|
|
values.Value1 = (resource.TotalCapacity[we] || 0) - summaryAllocated;
|
|
if (expCatRow.AllowResourceAssignment) {
|
|
values.NonProjectTime += teamInfoService.getResourceSummaryNptAllocation(resource, we) || 0;
|
|
}
|
|
values.Value1 -= values.NonProjectTime;
|
|
values.Value2 = values.Capacity;
|
|
break;
|
|
default:
|
|
// Allocated/Capacity
|
|
values.Value1 = summaryAllocated || 0;
|
|
if (expCatRow.AllowResourceAssignment) {
|
|
values.NonProjectTime += teamInfoService.getResourceSummaryNptAllocation(resource, we) || 0;
|
|
}
|
|
values.Value1 += values.NonProjectTime;
|
|
values.Value2 = values.Capacity;
|
|
break;
|
|
};
|
|
values.Allocated = summaryAllocated - values.SuperECAllocations;
|
|
|
|
if (!$scope.DisplayMode.IsUOMHours) {
|
|
if (angular.isNumber(values.Value1) && !isNaN(values.Value1)) {
|
|
values.Value1 = hoursResourcesConverter.convertToResources(resource.OwnExpenditureCategoryId, values.Value1);
|
|
}
|
|
|
|
if (angular.isNumber(values.Value2) && !isNaN(values.Value2)) {
|
|
values.Value2 = hoursResourcesConverter.convertToResources(resource.OwnExpenditureCategoryId, values.Value2);
|
|
}
|
|
values.Allocated = hoursResourcesConverter.convertToResources(resource.OwnExpenditureCategoryId, values.Allocated);
|
|
values.Capacity = hoursResourcesConverter.convertToResources(resource.OwnExpenditureCategoryId, values.Capacity);
|
|
values.NonProjectTime = hoursResourcesConverter.convertToResources(resource.OwnExpenditureCategoryId, values.NonProjectTime);
|
|
values.SuperECAllocations = hoursResourcesConverter.convertToResources(resource.OwnExpenditureCategoryId, values.SuperECAllocations);
|
|
}
|
|
values.Value1 = roundService.roundQuantity(values.Value1);
|
|
values.Value2 = roundService.roundQuantity(values.Value2);
|
|
values.Allocated = roundService.roundQuantity(values.Allocated);
|
|
values.Capacity = roundService.roundQuantity(values.Capacity);
|
|
values.NonProjectTime = roundService.roundQuantity(values.NonProjectTime);
|
|
values.SuperECAllocations = roundService.roundQuantity(values.SuperECAllocations);
|
|
|
|
return values;
|
|
};
|
|
|
|
//==========End Convert data model to UI data model methods=============//
|
|
function resourceValueChanged(teamId, expenditureCategoryId, resourceId, weekEnding) {
|
|
if (!teamId || !expenditureCategoryId || !resourceId || !(new Date(weekEnding).getTime()))
|
|
return;
|
|
|
|
var weekIndex = getWeekIndex(weekEnding);
|
|
if (weekIndex < 0)
|
|
return;
|
|
|
|
var team = teamInfoService.getById(teamId);
|
|
if (!team)
|
|
return;
|
|
|
|
var week = $scope.InternalData.Header.Weeks[weekIndex];
|
|
if (!week)
|
|
return;
|
|
|
|
var month = $scope.InternalData.Header.Months[week.ParentIndex];
|
|
if (!month)
|
|
return;
|
|
|
|
var topLevelItemId = undefined;
|
|
switch ($scope.DisplayMode.GroupBy) {
|
|
case groupByEnum.Team:
|
|
topLevelItemId = team.Id;
|
|
break;
|
|
case groupByEnum.Company:
|
|
topLevelItemId = team.CompanyId;
|
|
break;
|
|
case groupByEnum.CostCenter:
|
|
// Get Cost Center for EC
|
|
var expCat = dataSources.getExpenditureById(expenditureCategoryId);
|
|
if (!expCat || !expCat.CreditId) {
|
|
throw "Expenditure Category " +expenditureCategoryId + "not found in dataSources";
|
|
}
|
|
topLevelItemId = expCat.CreditId;
|
|
break;
|
|
}
|
|
|
|
// update exactly changed EC (general or Super EC)
|
|
var ecKey = getECKey(expenditureCategoryId, team.Id, topLevelItemId);
|
|
var originalRow = $scope.DisplayData[ecKey];
|
|
if (originalRow) {
|
|
fillExpCatCell(weekIndex, month.SelfIndexInWeeks, originalRow);
|
|
if (topLevelExists()) {
|
|
var topLevelItem = getTopLevelItem(topLevelItemId);
|
|
|
|
if (topLevelItem && $scope.DisplayData[topLevelItem.Key]) {
|
|
switch (topLevelItem.Type) {
|
|
case $scope.RowType.Team:
|
|
fillTeamCell(weekIndex, month.SelfIndexInWeeks, $scope.DisplayData[topLevelItem.Key]);
|
|
break;
|
|
case $scope.RowType.Company:
|
|
fillCompanyCell(weekIndex, month.SelfIndexInWeeks, $scope.DisplayData[topLevelItem.Key]);
|
|
break;
|
|
case $scope.RowType.CostCenter:
|
|
fillCostCenterCell(weekIndex, month.SelfIndexInWeeks, $scope.DisplayData[topLevelItem.Key]);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
var expCategoryInTeam = team.ExpCategories[expenditureCategoryId];
|
|
if (!expCategoryInTeam) {
|
|
return;
|
|
}
|
|
|
|
// update general EC and resource under it if changed EC is Project role
|
|
if (!expCategoryInTeam.AllowResourceAssignment) {
|
|
if (expCategoryInTeam.Resources) {
|
|
var resource = expCategoryInTeam.Resources[resourceId];
|
|
if (resource) {
|
|
ecKey = getECKey(resource.OwnExpenditureCategoryId, team.Id, topLevelItemId);
|
|
originalRow = $scope.DisplayData[ecKey];
|
|
}
|
|
}
|
|
}
|
|
if (!originalRow) {
|
|
return;
|
|
}
|
|
|
|
fillExpCatCell(weekIndex, month.SelfIndexInWeeks, originalRow);
|
|
};
|
|
function teamValueChanged(teamId, expenditureCategoryId, weekEnding) {
|
|
if (!teamId || !expenditureCategoryId || !(new Date(weekEnding).getTime()))
|
|
return;
|
|
|
|
var weekIndex = getWeekIndex(weekEnding);
|
|
if (weekIndex < 0)
|
|
return;
|
|
|
|
var team = teamInfoService.getById(teamId);
|
|
if (!team)
|
|
return;
|
|
|
|
var week = $scope.InternalData.Header.Weeks[weekIndex];
|
|
if (!week)
|
|
return;
|
|
|
|
var month = $scope.InternalData.Header.Months[week.ParentIndex];
|
|
if (!month)
|
|
return;
|
|
|
|
var topLevelItemId = undefined;
|
|
switch ($scope.DisplayMode.GroupBy) {
|
|
case groupByEnum.Team:
|
|
topLevelItemId = team.Id;
|
|
break;
|
|
case groupByEnum.Company:
|
|
topLevelItemId = team.CompanyId;
|
|
break;
|
|
case groupByEnum.CostCenter:
|
|
// Get Cost Center for EC
|
|
var expCat = dataSources.getExpenditureById(expenditureCategoryId);
|
|
if (!expCat || !expCat.CreditId) {
|
|
throw "Expenditure Category " +expenditureCategoryId + "not found in dataSources";
|
|
}
|
|
topLevelItemId = expCat.CreditId;
|
|
break;
|
|
}
|
|
|
|
// update exactly changed EC (general or Super EC)
|
|
var ecKey = getECKey(expenditureCategoryId, team.Id, topLevelItemId);
|
|
var originalRow = $scope.DisplayData[ecKey];
|
|
if (originalRow) {
|
|
fillExpCatCell(weekIndex, month.SelfIndexInWeeks, originalRow);
|
|
if (topLevelExists()) {
|
|
var topLevelItem = getTopLevelItem(topLevelItemId);
|
|
|
|
if (topLevelItem && $scope.DisplayData[topLevelItem.Key]) {
|
|
switch (topLevelItem.Type) {
|
|
case $scope.RowType.Team:
|
|
fillTeamCell(weekIndex, month.SelfIndexInWeeks, $scope.DisplayData[topLevelItem.Key]);
|
|
break;
|
|
case $scope.RowType.Company:
|
|
fillCompanyCell(weekIndex, month.SelfIndexInWeeks, $scope.DisplayData[topLevelItem.Key]);
|
|
break;
|
|
case $scope.RowType.CostCenter:
|
|
fillCostCenterCell(weekIndex, month.SelfIndexInWeeks, $scope.DisplayData[topLevelItem.Key]);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
function getWeekIndex(weekEnding) {
|
|
var weekIndex = -1;
|
|
for (var i = 0; i < $scope.InternalData.Header.Weeks.length; i++) {
|
|
var week = $scope.InternalData.Header.Weeks[i];
|
|
if (!!week && week.Milliseconds == weekEnding && week.DataType == Header.DataType.Week) {
|
|
weekIndex = i;
|
|
break;
|
|
}
|
|
}
|
|
return weekIndex;
|
|
};
|
|
function isExpenditureVisible(expCat) {
|
|
// Returns TRUE, if EC must be displayed. For ordinary ECs always returns TRUE.
|
|
// For Project Role returns true, if it has at least one resource with non-zero allocations
|
|
if (!expCat)
|
|
return false;
|
|
|
|
var result = false;
|
|
var expCatId = expCat.Id;
|
|
var ACTUALS = 1;
|
|
var PLANNED = 2;
|
|
var TEAM = 4;
|
|
|
|
if (expCat.AllowResourceAssignment) {
|
|
// Ordinary EC. Check it fits current Capcity display mode
|
|
result = (!$scope.DisplayMode.CapacityView && (((expCat.ECScenario & PLANNED) > 0) || ((expCat.ECScenario & TEAM) > 0))) ||
|
|
($scope.DisplayMode.CapacityView && (((expCat.ECScenario & ACTUALS) > 0) || ((expCat.ECScenario & TEAM) > 0)));
|
|
|
|
if (result) {
|
|
// Displayed only, if has planned capacity or actual capacity for calendar period (ticket 2409)
|
|
result = expCatHasPlannedCapacityForPeriod(expCat, $scope.Weekendings) || expCatHasActualCapacityForPeriod(expCat, $scope.Weekendings);
|
|
}
|
|
}
|
|
else {
|
|
// Super EC. Always display it to show remaining need
|
|
result = true;
|
|
}
|
|
|
|
return result;
|
|
};
|
|
function resourceHasTeam(resourceModel, we) {
|
|
if (!resourceModel || !we || !resourceModel.Teams || !resourceModel.Teams.length)
|
|
return false;
|
|
|
|
var result = false;
|
|
|
|
for (var index = 0; index < resourceModel.Teams.length; index++) {
|
|
var teamMembership = resourceModel.Teams[index];
|
|
|
|
result = teamMembership.StartDate && (teamMembership.StartDate < we) && (!teamMembership.EndDate || teamMembership.EndDate >= we);
|
|
|
|
if (result) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
};
|
|
function getTooltipContent(cell, rowType) {
|
|
var tooltip = '';
|
|
var units = ' hours';
|
|
if (!$scope.DisplayMode.IsUOMHours)
|
|
units = ' resources';
|
|
|
|
var displayAllocated, displayNeeded, displayNpt, displayCapacity, displaySuper;
|
|
|
|
if (rowType == 'resource') {
|
|
displayAllocated = displayNpt = displayCapacity = displaySuper = true;
|
|
} else {
|
|
switch (parseInt($scope.DisplayMode.TotalsAs)) {
|
|
case 1:
|
|
//Allocated/Capacity
|
|
displayAllocated = true;
|
|
displayCapacity = displayNpt = displaySuper = (rowType != 'expenditure') || !cell.IsProjectRole;
|
|
displayNeeded = (rowType == 'expenditure') && cell.IsProjectRole;
|
|
break;
|
|
case 2:
|
|
// Need/Capacity
|
|
displayNeeded = true;
|
|
displayCapacity = (rowType != 'expenditure') || !cell.IsProjectRole;
|
|
displayAllocated = (rowType == 'expenditure') && cell.IsProjectRole;
|
|
break;
|
|
case 3:
|
|
//Allocated/Need
|
|
displayAllocated = displayNeeded = true;
|
|
displayNpt = displaySuper = (rowType != 'expenditure') || !cell.IsProjectRole;
|
|
break;
|
|
case 4:
|
|
//Remaining/Capacity
|
|
displayNeeded = true;
|
|
displayCapacity = displaySuper = (rowType != 'expenditure') || !cell.IsProjectRole;
|
|
displayAllocated = (rowType == 'expenditure') && cell.IsProjectRole;
|
|
break;
|
|
}
|
|
}
|
|
if (displayAllocated)
|
|
tooltip += 'Allocated: ' + cell.Allocated + units;
|
|
if (displayNeeded && cell.Needed)
|
|
tooltip += (tooltip.length > 0 ? '<br/>' : '') + 'Need: ' + cell.Needed + units;
|
|
if (displayNpt && cell.NonProjectTime)
|
|
tooltip += (tooltip.length > 0 ? '<br/>' : '') + 'Non-Project Time: ' + cell.NonProjectTime + units;
|
|
if (displaySuper && cell.SuperECAllocations)
|
|
tooltip += (tooltip.length > 0 ? '<br/>' : '') + 'Project Roles: ' + cell.SuperECAllocations + units;
|
|
if (displayCapacity && cell.Capacity)
|
|
tooltip += (tooltip.length > 0 ? '<br/>' : '') + 'Capacity: ' + cell.Capacity + units;
|
|
return tooltip;
|
|
};
|
|
function hideRedundantTooltips(exceptObj) {
|
|
$('.tooltip').not(exceptObj).removeClass('in').hide();
|
|
};
|
|
function setupJSEvents() {
|
|
var scrollTimeout = 0;
|
|
var obj = window;
|
|
$(obj).off('scroll.tt').on('scroll.tt', function () {
|
|
if (scrollTimeout > 0) {
|
|
if ($(obj).data('scroll.tt.timeout')) {
|
|
clearTimeout($(obj).data('scroll.tt.timeout'));
|
|
}
|
|
$(obj).data('scroll.tt.timeout', setTimeout(hideRedundantTooltips, scrollTimeout));
|
|
}
|
|
else
|
|
hideRedundantTooltips();
|
|
});
|
|
$(document).off('click.tt').on('click.tt', function (e) {
|
|
var parents = $(e.target).parents(":data('bs.tooltip')");
|
|
var target;
|
|
if (parents && parents.length && parents.length > 0) {
|
|
target = $(parents[0]).next();
|
|
}
|
|
hideRedundantTooltips(target);
|
|
});
|
|
};
|
|
function concatValues(item1, item2) {
|
|
var result = angular.extend({}, item1);
|
|
if (angular.isNumber(result.Value1)) {
|
|
if (angular.isNumber(item2.Value1)) {
|
|
result.Value1 += item2.Value1;
|
|
}
|
|
}
|
|
else {
|
|
result.Value1 = item2.Value1;
|
|
}
|
|
|
|
if (angular.isNumber(result.Value2)) {
|
|
if (angular.isNumber(item2.Value2)) {
|
|
result.Value2 += item2.Value2;
|
|
}
|
|
}
|
|
else {
|
|
result.Value2 = item2.Value2;
|
|
}
|
|
result.Allocated += item2.Allocated || 0;
|
|
result.Needed += item2.Needed || 0;
|
|
result.Capacity += item2.Capacity || 0;
|
|
result.NonProjectTime += item2.NonProjectTime || 0;
|
|
result.SuperECAllocations += item2.SuperECAllocations || 0;
|
|
return result;
|
|
};
|
|
function topLevelExists() {
|
|
return $scope.DisplayMode.GroupBy === groupByEnum.Team || $scope.DisplayMode.GroupBy === groupByEnum.Company ||
|
|
$scope.DisplayMode.GroupBy === groupByEnum.CostCenter;
|
|
};
|
|
function getTopLevelItem(itemId) {
|
|
if ($scope.DisplayMode.GroupBy === groupByEnum.Team) {
|
|
var team = teamInfoService.getById(itemId);
|
|
var item = {
|
|
Key: itemId,
|
|
Name: team.Name,
|
|
Type: $scope.RowType.Team
|
|
};
|
|
return item;
|
|
}
|
|
|
|
if ($scope.DisplayMode.GroupBy === groupByEnum.Company) {
|
|
var company = dataSources.getCompanyById(itemId);
|
|
var item = {
|
|
Key: itemId,
|
|
Name: company.Name,
|
|
Type: $scope.RowType.Company
|
|
};
|
|
return item;
|
|
}
|
|
|
|
if ($scope.DisplayMode.GroupBy === groupByEnum.CostCenter) {
|
|
var costCenter = dataSources.getCostCenterById(itemId);
|
|
var item = {
|
|
Key: itemId,
|
|
Name: costCenter.Name,
|
|
Type: $scope.RowType.CostCenter
|
|
};
|
|
return item;
|
|
}
|
|
|
|
return null;
|
|
};
|
|
function getECKey(expenditureCategoryId, teamId, rootItemId) {
|
|
if (!topLevelExists()) {
|
|
return expenditureCategoryId;
|
|
}
|
|
|
|
var topLevelItemId = undefined;
|
|
switch ($scope.DisplayMode.GroupBy) {
|
|
case groupByEnum.Team:
|
|
topLevelItemId = teamId;
|
|
break;
|
|
case groupByEnum.Company:
|
|
topLevelItemId = rootItemId; // contains companyId
|
|
break;
|
|
case groupByEnum.CostCenter:
|
|
topLevelItemId = rootItemId; // contains costCenterId
|
|
break;
|
|
}
|
|
|
|
var topLevelItem = getTopLevelItem(topLevelItemId);
|
|
var key = topLevelItem.Key + '-' + expenditureCategoryId;
|
|
|
|
return key;
|
|
};
|
|
function getWeekendingFromHeader(header) {
|
|
if (!header || !header.Header)
|
|
return;
|
|
|
|
var result = [];
|
|
if (header.Header.Weeks) {
|
|
var weeksCount = header.Header.Weeks.length;
|
|
for (var index = 0; index < weeksCount; index++) {
|
|
var week = header.Header.Weeks[index];
|
|
|
|
if ((week.DataType == Header.DataType.Week) && week.Milliseconds) {
|
|
result.push(week.Milliseconds);
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
};
|
|
function expCatHasPlannedCapacityForPeriod(expCatItem, weeks) {
|
|
// Return TRUE, if category has at least one non-zero value for planned capacity in period
|
|
if (!expCatItem || !weeks || !angular.isArray(weeks))
|
|
return false;
|
|
|
|
var result = false;
|
|
|
|
if (!expCatItem.PlannedCapacityValues)
|
|
return result;
|
|
|
|
var weeksCount = weeks.length;
|
|
for (var index = 0; index < weeksCount; index++) {
|
|
var we = weeks[index];
|
|
var value = expCatItem.PlannedCapacityValues[we];
|
|
result = angular.isNumber(value) && (Number(value) > 0);
|
|
|
|
if (result)
|
|
break;
|
|
}
|
|
|
|
return result;
|
|
};
|
|
function expCatHasActualCapacityForPeriod(expCatItem, weeks) {
|
|
// Return TRUE, if category has at least one non-zero value for actual capacity in period
|
|
if (!expCatItem || !weeks || !angular.isArray(weeks))
|
|
return false;
|
|
|
|
var result = false;
|
|
|
|
if (!expCatItem.ActualCapacityValues)
|
|
return result;
|
|
|
|
var weeksCount = weeks.length;
|
|
for (var index = 0; index < weeksCount; index++) {
|
|
var we = weeks[index];
|
|
var value = expCatItem.ActualCapacityValues[we];
|
|
result = angular.isNumber(value) && (Number(value) > 0);
|
|
|
|
if (result)
|
|
break;
|
|
}
|
|
|
|
return result;
|
|
};
|
|
function getExpenditureTeams(expCatId, teamIds) {
|
|
// Returns team IDs, the expenditure exists in. If teams array is specified, doesn't perform any
|
|
// checks, and returns this array back
|
|
var result = null;
|
|
|
|
if (!expCatId)
|
|
return result;
|
|
|
|
if (teamIds && angular.isArray(teamIds)) {
|
|
result = teamIds;
|
|
}
|
|
else {
|
|
// Get teams to loop through
|
|
var teamsForExpCat = teamInfoService.getTeamsByExpenditureCategoryId(expCatId, false);
|
|
if (!teamsForExpCat)
|
|
return result;
|
|
|
|
result = Object.keys(teamsForExpCat);
|
|
}
|
|
|
|
return result;
|
|
};
|
|
}]); |