EnVisageOnline/Main/Source/EnVisage/Scripts/Angular/Controllers/teamInfoController.js

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;
};
}]);