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

3054 lines
150 KiB
JavaScript

'use strict';
app
.directive('optionClassExpr', function ($compile, $parse) {
var NG_OPTIONS_REGEXP = /^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+group\s+by\s+([\s\S]+?))?\s+for\s+(?:([\$\w][\$\w]*)|(?:\(\s*([\$\w][\$\w]*)\s*,\s*([\$\w][\$\w]*)\s*\)))\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?$/;
return {
restrict: 'A',
link: function optionClassExprPostLink(scope, elem, attrs) {
var optionsExp = attrs.ngOptions;
if (!optionsExp) return;
var match = optionsExp.match(NG_OPTIONS_REGEXP);
if (!match) return;
var values = match[7];
scope.$watchCollection(function () {
return elem.children();
}, function (newValue) {
angular.forEach(newValue, function (child, index) {
var child = angular.element(child);
var val = child.val();
if (val) {
child.attr('ng-class', values + '[' + (index - 1) + '].' + attrs.optionClassExpr);
$compile(child)(scope);
}
});
});
}
};
})
.directive('selectedSourceChanged', ['$timeout', function ($timeout) {
return {
link: function ($scope, element, attrs) {
$scope.$on('selectedSourceChanged', function () {
$timeout(function () { // You might need this timeout to be sure its run after DOM render.
element.trigger("change");
}, 0, false);
})
}
};
}])
.controller('capacityManagementController', ['$scope', '$http', '$location', '$timeout', '$window', 'cellHighlightingService', 'scenarioDetailsService', 'dataSources', '$document', function ($scope, $http, $location, $timeout, $window, cellHighlightingService, scenarioDetailsService, dataSources, $document) {
$scope.RowType = {
Project: 0,
ProjectExpenditureCategory: 1,
Total: 2,
Capacity: 3,
BottomCategory: 4,
Team: 5,
CurrentlyAssigned: 6,
NonProjectTimeTotal: 7,
Actual: 8,
NonProjectTimeCategory: 9,
NonProjectTime: 10
}
$scope.CollapsedIcon = 'fa-plus-square';
$scope.NonCollapsedIcon = 'fa-minus-square';
$scope.ScenarioCollapsedIcon = 'fa-plus-square-1';
$scope.ScenarioNonCollapsedIcon = 'fa-minus-square-1';
$scope.ExpCatCollapsedIcon = 'fa-plus-square-2';
$scope.ExpCatNonCollapsedIcon = 'fa-minus-square-2';
$scope.id = $location.absUrl().substr($location.absUrl().lastIndexOf('ls/') + 3, 36);
//$scope.data = {
// VisibleCellCount: 1
//};
$scope.dataSection = "";
$scope.pageTitle = ""; // SA. ENV-905
$scope.preferences = [];
$scope.isOverAllocated = false;
$scope.isMatchChecked = false;
$scope.saveType = "0"; // save scenarios with preserving their current types (BU or TD)
$scope.SaveAs = 0;
$scope.isActiveScenario = true;
$scope.ScenarioName = "";
$scope.dateError = false;
$scope.isReadOnly = false;
var scrollWidthHeight = $.getScrollBarWidthHeight();
$scope.calendarViewSettings = {
headerWidth: 70,
tableHeightMin: 450,
scrollWidth: scrollWidthHeight[0],
scrollHeight: scrollWidthHeight[1]
};
$scope.calendarFilters = {
CompanyId: null,
StartDate: null,
EndDate: null,
ViewId: null,
TeamId: null,
ResourceId: null,
IsUOMHours: null,
PreferredTotalsDisplaying: null,
ShowUpper: true,
ShowLower: true,
IsBarMode: true,
ShowCapacity: 1, //"Total Allocated/Total Capacity"
GroupByTeam: true,
IsViewModeMonth: true,
ShowAvgTotals: true,
IsCapacityModeActuals: true,
sortOrder: false,
sortBy: 'Name'
};
// SA. ENV-799
$scope.CalendarFilterMode = {
IsCompany: true,
IsView: false,
IsTeam: false,
IsResource: false,
FilteredEntityTitle: "Company",
SelectedItemId: null,
}
$scope.SpreadType = {
Notspecified: -1,
Equal: 0,
Less: 1,
Over: 2
};
$scope.availableFilterOptions = {};
$scope.data = null;
$scope.data2Update = {
IsOverAllocated: false,
IsMatchChecked: false,
SaveType: "0",
SaveAs: 0,
IsActiveScenario: false,
ScenarioName: "",
ChangedExpCats: [],
NonProjectTimeCats: [],
ChangedSupperExpCatProjects: []
};
$scope.grandtotalrow = null;
$scope.$watch("saveType", function (newValue, oldValue) {
if (newValue != oldValue) {
if (newValue == "1") {
$scope.isMatchChecked = true;
}
}
});
function getExpCatById(scenarioId, ExpCatId, teamId) {
for (var i = 0; i < $scope.data.Calendar.length; i++) {
if ($scope.data.Calendar[i].ExpCatId != null
&& $scope.data.Calendar[i].ScenarioId == scenarioId && $scope.data.Calendar[i].ExpCatId == ExpCatId &&
(!$scope.calendarFilters.GroupByTeam || $scope.CalendarFilterMode.IsTeam || $scope.data.Calendar[i].TeamId == teamId)) {
return $scope.data.Calendar[i];
}
}
return null;
}
function getNonProjectTimeCatById(nonProjectTimeCategoryId) {
for (var i = 0; i < $scope.data.Calendar.length; i++) {
if ($scope.data.Calendar[i].RowType == $scope.RowType.NonProjectTimeTotal) {
if (nonProjectTimeCategoryId == null) {
return $scope.data.Calendar[i];
} else {
for (var j in $scope.data.Calendar[i].Categories) {
var category = $scope.data.Calendar[i].Categories[j];
if (category.Id == nonProjectTimeCategoryId) {
return category;
}
}
}
}
}
return null;
}
function getNonProjectTimeById(Id) {
for (var i = 0; i < $scope.data.Calendar.length; i++) {
if ($scope.data.Calendar[i].RowType == $scope.RowType.NonProjectTimeTotal) {
for (var j in $scope.data.Calendar[i].Categories) {
var category = $scope.data.Calendar[i].Categories[j];
if (category.Id == Id) {
return category;
}
}
}
}
return null;
}
function getResourceByNonProjectTime(category, resId) {
for (var i = 0; i < category.Resources.length; i++) {
if (category.Resources[i].Id == resId) {
return category.Resources[i];
}
}
return null;
}
// this method returns first project row, but not related for any team
// just for now it is using only for getting start/end date
function getProjectById(projectId) {
return getProjectById(projectId, null);
}
function getProjectById(projectId, teamId) {
for (var i = 0; i < $scope.data.Calendar.length; i++) {
if ($scope.data.Calendar[i].ExpCatId == null && $scope.data.Calendar[i].ProjectId == projectId && (teamId == null || teamId == $scope.data.Calendar[i].TeamId)) {
return $scope.data.Calendar[i];
}
}
return null;
}
function isProjectHaveSupperExpCat(projectId) {
for (var i = 0; i < $scope.data.Calendar.length; i++) {
if ($scope.data.Calendar[i].ExpCatId != null && $scope.data.Calendar[i].ProjectId == projectId && $scope.data.Calendar[i].AllowResourceAssignment == false) {
return true;
}
}
return false;
}
function getResourceById(resId) {
for (var i = 0; i < $scope.data.AllResources.length; i++) {
if ($scope.data.AllResources[i].Id == resId) {
return $scope.data.AllResources[i];
}
}
return null;
}
function getResourceByExpCat(expCat, resId) {
if (expCat != null) {
for (var i = 0; i < expCat.Resources.length; i++) {
if (expCat.Resources[i].Id == resId) {
return expCat.Resources[i];
}
}
}
return null;
}
function getECsThatContainResource(resourceId) {
if (!resourceId)
return;
var result = [];
for (var i = 0; i < $scope.data.Calendar.length; i++) {
var expCatTotal = $scope.data.Calendar[i];
if (expCatTotal.RowType != $scope.RowType.BottomCategory)
continue;
var resource = getResourceByExpCat(expCatTotal, resourceId);
if (resource) {
result.push(expCatTotal);
}
}
return result;
};
function getResourceActualByExpCat(expCat, resId) {
if (expCat != null) {
for (var i = 0; i < expCat.Resources.length; i++) {
if (expCat.Resources[i].Id == resId) {
return expCat.Resources[i];
}
}
}
return null;
}
function setChildrenCollapsed(row, internal, parentCollapsed) {
var isProject = row.ProjectId != null && row.ExpCatId == null;
var isExpCat = row.ExpCatId != null;
for (var i = 0; i < $scope.data.Calendar.length; i++) {
var currRow = $scope.data.Calendar[i];
if (isProject && currRow.ExpCatId != null && currRow.ProjectId == row.ProjectId &&
(!$scope.calendarFilters.GroupByTeam || currRow.TeamId == row.TeamId)) {
var expCat = currRow;
if (internal) {
expCat.IsParentCollapsed = parentCollapsed;
setChildrenCollapsed(expCat, true, parentCollapsed);
} else {
expCat.IsParentCollapsed = row.ProjectCollapsed;
setChildrenCollapsed(expCat, true, row.ProjectCollapsed);
}
} else if (isExpCat && currRow.ExpCatId == row.ExpCatId && currRow.ScenarioId == row.ScenarioId &&
(!$scope.calendarFilters.GroupByTeam || currRow.TeamId == row.TeamId)) {
var resource = currRow;
if (internal) {
resource.IsParentCollapsed = parentCollapsed;
}
}
}
}
function clearResource(scenarioId, expCatId, resource, teamId) {
if (resource != null) {
for (var i = 0; i < $scope.data.Headers.length; i++) {
changeResourceValue(0, scenarioId, expCatId, resource.Id, teamId, i, false);
}
}
}
function updateTotals(expCat, expCatTotal, resourceTotal, resource, colIndex, newQuantity, oldQuantity, newCost, oldCost) {
var monthStartIndex = colIndex;
var monthEndIndex = colIndex;
var weeks = 1;
var project = getProjectById(expCat.ProjectId);
var resourceEndDateWeekending = getNearestWeekending(resource.EndDate);
var projectEndDateWeekending = getNearestWeekending(project.EndDate);
var avg = ($scope.calendarFilters.ShowAvgTotals && !$scope.calendarFilters.IsUOMHours);
//Get current month start/end weeks index and weeks count
var montIndex = 0;
for (var i = colIndex + 1; i < $scope.data.Headers.length; i++) {
if ($scope.data.Headers[i].IsMonth) {
monthEndIndex = i;
montIndex = $scope.data.Headers[i].MonthIndex;
weeks = $scope.data.Headers[i].Weeks.length;
break;
}
}
for (var i = 0; i < monthEndIndex; i++) {
var currWeekending = $scope.data.Headers[i].Milliseconds;
if (montIndex == $scope.data.Headers[i].MonthIndex && currWeekending >= resource.StartDate &&
currWeekending <= resourceEndDateWeekending &&
currWeekending >= project.StartDate && currWeekending <= projectEndDateWeekending) {
if (!$scope.data.Headers[i].IsMonth) {
monthStartIndex = i;
break;
}
}
}
//Get grand total for all months and months count
var firstWeekIndex = -1;
var lastWeekIndex = -1;
for (var i = 0; i < $scope.data.Headers.length; i++) {
var currWeekending = $scope.data.Headers[i].Milliseconds;
if ((currWeekending >= resource.StartDate && currWeekending <= resourceEndDateWeekending &&
currWeekending >= project.StartDate && currWeekending <= projectEndDateWeekending)) {
if (!$scope.data.Headers[i].IsMonth && firstWeekIndex < 0) {
firstWeekIndex = i;
}
lastWeekIndex = i;
}
}
var totalWeeksQuantity = 0;
var totalWeeks = 0;
//In case when resource hire dates are later then project dates
if (firstWeekIndex > -1 && lastWeekIndex > -1) {
for (var i = firstWeekIndex; i <= lastWeekIndex; i++) {
if (!$scope.data.Headers[i].IsMonth) {
totalWeeksQuantity += (i == colIndex) ? newQuantity : resource.QuantityValues[i];
totalWeeks++;
}
}
}
//Calculate grand total
if (avg) {
resource.GrandTotalQuantity = Math.round(totalWeeksQuantity / totalWeeks * 1000) / 1000;
} else {
resource.GrandTotalQuantity = totalWeeksQuantity;
}
oldQuantity = resource.QuantityValues[monthEndIndex];
//Get current month total
var monthTotal = 0;
for (var i = monthStartIndex; i < monthEndIndex; i++) {
monthTotal += (colIndex == i) ? newQuantity : resource.QuantityValues[i];
}
//Calculate current month total
if (avg) {
newQuantity = Math.round(monthTotal / weeks * 1000) / 1000;
} else {
newQuantity = monthTotal;
}
//Change quantity for month
//resource.QuantityValues[monthEndIndex] = newQuantity;
if (resourceTotal != null) {
if (avg) {
recalcButtomPartRows(resourceTotal, expCatTotal, monthEndIndex);
} else {
if (expCat.ExpCatId === expCatTotal.ExpCatId) {
resourceTotal.QuantityValues[monthEndIndex] += (newQuantity - oldQuantity);
resourceTotal.GrandTotalQuantity += (newQuantity - oldQuantity);
expCatTotal.QuantityTotalAllocatedValue[monthEndIndex] += (newQuantity - oldQuantity);
}
resourceTotal.AllocatedQuantityValues[monthEndIndex] += (newQuantity - oldQuantity);
}
}
return {
MonthEndIndex: monthEndIndex,
NewQuantity: newQuantity
};
}
function updateNonProjectTimeTotals(nonProjectTimeCategory, nonProjectTimeTotal, nonProjectTime, resource, colIndex, newQuantity, oldQuantity) {
var monthStartIndex = -1;
var mounthWeeks = 1;
var monthColIndex = 0;
var categoryMonthTotal = 0;
var monthNonProjectTimeTotalTotal = 0;
var monthNonProjectTimeTotal = 0;
var categoryMonth = 0;
var month = 0;
var monthNonProjectTime = 0;
var firstWeekIndex = -1;
var lastWeekIndex = -1;
var totalWeeksQuantity = 0;
var totalWeeks = 0;
var monthTotal = 0;
var monthIndex = $scope.data.Headers[colIndex].MonthIndex;
var avg = ($scope.calendarFilters.ShowAvgTotals && !$scope.calendarFilters.IsUOMHours);
var delta = 0;
if (nonProjectTime.IsTeamMode)
delta = newQuantity - oldQuantity;
else
delta = newQuantity - oldQuantity;
//Change NPT, NPT Total and NPT Category values
nonProjectTimeCategory.QuantityValues[colIndex] += delta;
nonProjectTimeTotal.QuantityValues[colIndex] += delta;
nonProjectTime.QuantityValues[colIndex] += delta;
//Get grand total for all months and months count
for (var i = 0; i < $scope.data.Headers.length; i++) {
if ($scope.data.Headers[i].IsMonth && monthIndex == $scope.data.Headers[i].MonthIndex) {
//monthEndIndex = i;
//montIndex = $scope.data.Headers[i].MonthIndex;
mounthWeeks = $scope.data.Headers[i].Weeks.length;
monthColIndex = i;
//break;
}
if (!$scope.data.Headers[i].IsMonth && monthIndex == $scope.data.Headers[i].MonthIndex && monthStartIndex == -1) {
monthStartIndex = i;
//break;
}
if (!$scope.data.Headers[i].IsMonth) {
if (firstWeekIndex == -1) {
firstWeekIndex = i;
}
lastWeekIndex = i;
}
}
monthStartIndex = monthStartIndex == -1 ? colIndex : monthStartIndex;
//In case when resource hire dates are later then project dates
if (firstWeekIndex > -1 && lastWeekIndex > -1) {
for (var i = firstWeekIndex; i <= lastWeekIndex; i++) {
if (!$scope.data.Headers[i].IsMonth) {
if (!nonProjectTime.IsTeamMode)
totalWeeksQuantity += (i == colIndex) ? newQuantity : resource.QuantityValues[i];
else
totalWeeksQuantity += (i == colIndex) ? newQuantity : nonProjectTime.QuantityValues[i];
totalWeeks++;
}
}
}
//Calculate resource grand total
if (!nonProjectTime.IsTeamMode)
resource.GrandTotalQuantity = avg ? Math.round(totalWeeksQuantity / totalWeeks * 1000) / 1000 : totalWeeksQuantity;
else
nonProjectTime.GrandTotalQuantity = avg ? Math.round(totalWeeksQuantity / totalWeeks * 1000) / 1000 : totalWeeksQuantity;
//Get current resource month total
for (var i = monthStartIndex; i < monthColIndex; i++) {
if (!nonProjectTime.IsTeamMode)
monthTotal += (colIndex == i) ? newQuantity : resource.QuantityValues[i];
else
monthTotal += (colIndex == i) ? newQuantity : nonProjectTime.QuantityValues[i];
}
//Calculate current month total
var newMonthQuantity = avg ? Math.round(monthTotal / mounthWeeks * 1000) / 1000 : monthTotal;
//Change quantity for month
if (!nonProjectTime.IsTeamMode)
resource.QuantityValues[monthColIndex] = newMonthQuantity;
else
nonProjectTime.QuantityValues[monthColIndex] = newMonthQuantity;
//Agregate NPT, NPT Total and NPT Category month and grandtotal values
for (var i = 0; i < $scope.data.Headers.length; i++) {
if (!$scope.data.Headers[i].IsMonth) {
if (monthIndex == $scope.data.Headers[i].MonthIndex) {
categoryMonth += nonProjectTimeCategory.QuantityValues[i];
month += nonProjectTimeTotal.QuantityValues[i];
monthNonProjectTime += nonProjectTime.QuantityValues[i];
}
categoryMonthTotal += nonProjectTimeCategory.QuantityValues[i];
monthNonProjectTimeTotalTotal += nonProjectTimeTotal.QuantityValues[i];
monthNonProjectTimeTotal += nonProjectTime.QuantityValues[i];
}
}
//Change NPT, NPT Total and NPT Category month values
nonProjectTimeCategory.QuantityValues[monthColIndex] = avg ? categoryMonth / mounthWeeks : categoryMonth;
nonProjectTimeTotal.QuantityValues[monthColIndex] = avg ? month / mounthWeeks : month;
nonProjectTime.QuantityValues[monthColIndex] = avg ? monthNonProjectTime / mounthWeeks : monthNonProjectTime;
//Change NPT, NPT Total and NPT Category grandtotal values
nonProjectTimeCategory.GrandTotalQuantity = avg ? categoryMonthTotal / totalWeeks : categoryMonthTotal;
nonProjectTimeTotal.GrandTotalQuantity = avg ? monthNonProjectTimeTotalTotal / totalWeeks : monthNonProjectTimeTotalTotal;
nonProjectTime.GrandTotalQuantity = avg ? monthNonProjectTimeTotal / totalWeeks : monthNonProjectTimeTotal;
}
function updateActualTotals(expCat, expCatTotal, actualTotal, resource, colIndex, newQuantity, oldQuantity) {
var monthStartIndex = colIndex;
var monthEndIndex = colIndex;
var weeks = 1;
var project = getProjectById(expCat.ProjectId);
var resourceEndDateWeekending = getNearestWeekending(resource.EndDate);
var projectEndDateWeekending = getNearestWeekending(project.EndDate);
var avg = ($scope.calendarFilters.ShowAvgTotals && !$scope.calendarFilters.IsUOMHours);
//Get current month start/end weeks index and weeks count
var montIndex = 0;
for (var i = colIndex + 1; i < $scope.data.Headers.length; i++) {
if ($scope.data.Headers[i].IsMonth) {
monthEndIndex = i;
montIndex = $scope.data.Headers[i].MonthIndex;
weeks = $scope.data.Headers[i].Weeks.length;
break;
}
}
for (var i = 0; i < monthEndIndex; i++) {
var currWeekending = $scope.data.Headers[i].Milliseconds;
if (montIndex == $scope.data.Headers[i].MonthIndex && currWeekending >= resource.StartDate &&
currWeekending <= resourceEndDateWeekending &&
currWeekending >= project.StartDate && currWeekending <= projectEndDateWeekending) {
if (!$scope.data.Headers[i].IsMonth) {
monthStartIndex = i;
break;
}
}
}
//Get grand total for all months and months count
var firstWeekIndex = -1;
var lastWeekIndex = -1;
for (var i = 0; i < $scope.data.Headers.length; i++) {
var currWeekending = $scope.data.Headers[i].Milliseconds;
if ((currWeekending >= resource.StartDate && currWeekending <= resourceEndDateWeekending &&
currWeekending >= project.StartDate && currWeekending <= projectEndDateWeekending)) {
if (!$scope.data.Headers[i].IsMonth && firstWeekIndex < 0) {
firstWeekIndex = i;
}
lastWeekIndex = i;
}
}
var totalWeeksQuantity = 0;
var totalWeeks = 0;
//In case when resource hire dates are later then project dates
if (firstWeekIndex > -1 && lastWeekIndex > -1) {
for (var i = firstWeekIndex; i <= lastWeekIndex; i++) {
if (!$scope.data.Headers[i].IsMonth) {
totalWeeksQuantity += (i == colIndex) ? newQuantity : resource.ActualQuantityValues[i];
totalWeeks++;
}
}
}
//Calculate grand total
if (avg) {
resource.GrandActualTotalQuantity = Math.round(totalWeeksQuantity / totalWeeks * 1000) / 1000;
} else {
resource.GrandActualTotalQuantity = totalWeeksQuantity;
}
oldQuantity = resource.ActualQuantityValues[monthEndIndex];
//Get current month total
var monthTotal = 0;
for (var i = monthStartIndex; i < monthEndIndex; i++) {
monthTotal += (colIndex == i) ? newQuantity : resource.ActualQuantityValues[i];
}
//Calculate current month total
if (avg) {
newQuantity = Math.round(monthTotal / weeks * 1000) / 1000;
} else {
newQuantity = monthTotal;
}
//Change quantity for month
resource.ActualQuantityValues[monthEndIndex] = newQuantity;
}
function recalcButtomPartRows(resourceTotal, expCatTotal, monthEndIndex) {
//recalc AVG for month
var monthIndex = $scope.data.Headers[monthEndIndex].MonthIndex;
var resourceValue = 0,
resourceAllocatedValue = 0,
allocatedValue = 0;
for (var i = 0; i < monthEndIndex; i++) {
if ($scope.data.Headers[i].MonthIndex == monthIndex) {
resourceValue += resourceTotal.QuantityValues[i];
resourceAllocatedValue += resourceTotal.AllocatedQuantityValues[i];
allocatedValue += expCatTotal.QuantityTotalAllocatedValue[i];
}
}
resourceTotal.QuantityValues[monthEndIndex] = resourceValue / $scope.data.Headers[monthEndIndex].Weeks.length;
resourceTotal.AllocatedQuantityValues[monthEndIndex] = resourceAllocatedValue / $scope.data.Headers[monthEndIndex].Weeks.length;
expCatTotal.QuantityTotalAllocatedValue[monthEndIndex] = allocatedValue / $scope.data.Headers[monthEndIndex].Weeks.length;
//recalc AVG for resource total
resourceValue = 0
var weeksCount = 0;
for (var i = 0; i < $scope.data.Headers.length; i++) {
if (!$scope.data.Headers[i].IsMonth) {
resourceValue += resourceTotal.QuantityValues[i];
weeksCount++;
}
}
resourceTotal.GrandTotalQuantity = resourceValue / weeksCount;
//recalc AVG for expcat total
}
function allocateResource(resource, expCat, create, allocateRest, reset) {
if (resource != null) {
var scenarioCalendarRow = null;
var i;
for (i = 0; i < $scope.data.Calendar.length; i++) {
if ($scope.data.Calendar[i].RowType == $scope.RowType.Project && $scope.data.Calendar[i].ScenarioId == expCat.ScenarioId) {
scenarioCalendarRow = $scope.data.Calendar[i];
break;
}
}
// TODO: ENV-1649. Seems like resource.Teams contains all teams resource have been ever assigned to
// and expCat.Teams contains all teams from calendar, so we cannot get one team for entire calendar
// we should get it separately in a headers for loop
var team = null;
for (i = 0; i < expCat.Teams.length; i++) {
if (findTeamInResourceTeams(resource.Teams, expCat.Teams[i].Id)) {
team = expCat.Teams[i];
break;
}
}
var res = getResourceById(resource.Id);
for (i = 0; i < $scope.data.Headers.length; i++) {
var cellReadonly = resource.ReadOnlyWeeks[i] || scenarioCalendarRow.ReadOnly[i];
if ($scope.data.Headers[i].IsMonth || (!reset && cellReadonly))
continue;
var quantity = 0;
if (create || reset) //Reset
{
if (create)
resource.QuantityValues[i] = 0;
if (reset && cellReadonly && resource.QuantityValues[i] == 0) // reset readonly cells only if any data exists in it
continue;
quantity = 0;
} else if (allocateRest) { //AssignRest
if (team != null) {
var needToAssign = Math.max(team.QuantityValues[i] - team.AllocatedByResources[i], 0);
} else {
var needToAssign = 0;
}
var canBeAssigned = Math.max(res.CapacityQuantityValues[i] - res.AllocatedQuantityValues[i], 0);
quantity = Math.min(needToAssign, canBeAssigned);
if (quantity == 0) {
continue;
}
quantity += resource.QuantityValues[i];
} else { //AssignAll
if (team != null) {
var needToAssign = Math.max(team.QuantityValues[i] - team.AllocatedByResources[i] + resource.QuantityValues[i], 0);
} else {
var needToAssign = 0;
}
var canBeAssigned = Math.max(res.CapacityQuantityValues[i], 0);
quantity = Math.min(needToAssign, canBeAssigned);
if (quantity == 0) {
continue;
}
}
changeResourceValue(quantity, expCat.ScenarioId, expCat.ExpCatId, resource.Id, expCat.TeamId, i, false);
}
}
}
function recalcSpreading(expCat, expCatTotal, resourceTotal, colIndex) {
var i;
if (expCat != null) {
var scenarioCalendarRow = getProjectById(expCat.ProjectId, expCat.TeamId);
var resTotal = 0;
for (i = 0; i < expCat.Resources.length; i++) {
resTotal += expCat.Resources[i].QuantityValues[colIndex];
}
//Validate team allocation upper
var compareRes = cellHighlightingService.compare(resTotal, expCat.QuantityValues[colIndex]);
expCat.SpreadVal[colIndex] = compareRes == null ? $scope.SpreadType.Notspecified :
compareRes < 0 ? $scope.SpreadType.Less :
compareRes == 0 ? $scope.SpreadType.Equal : $scope.SpreadType.Over;
updateCSSClass(expCat, null, colIndex);
var projectClass = $scope.SpreadType.Notspecified;
for (i = 0; i < $scope.data.Calendar.length; i++) {
if ($scope.data.Calendar[i].ProjectId == expCat.ProjectId && $scope.data.Calendar[i].ExpCatId != null
&& $scope.data.Calendar[i].ScenarioId == expCat.ScenarioId
&& (!$scope.calendarFilters.GroupByTeam || $scope.data.Calendar[i].TeamId == expCat.TeamId)) {
if ($scope.data.Calendar[i].SpreadVal[colIndex] == $scope.SpreadType.Over) {
projectClass = $scope.SpreadType.Over;
break;
} else if ($scope.data.Calendar[i].SpreadVal[colIndex] == $scope.SpreadType.Less) {
projectClass = $scope.SpreadType.Less;
}
}
}
//Validate SD allocation
scenarioCalendarRow.SpreadVal[colIndex] = projectClass;
updateCSSClass(scenarioCalendarRow, null, colIndex);
}
var isMonth = $scope.data.Headers[colIndex].IsMonth;
var startIndex = colIndex;
if (isMonth)
{
for (var mIndex = colIndex - 1; mIndex >= 0; mIndex--)
{
if ($scope.data.Headers[mIndex].IsMonth)
break;
startIndex--;
}
}
//calc exp cat total row
if (expCatTotal != null) {
expCatTotal.SpreadVal[colIndex] = $scope.SpreadType.Notspecified;
//Validate resource allocation bottom
for (i = 0; i < expCatTotal.Resources.length; i++) {
var res = expCatTotal.Resources[i];
if (isMonth) {
var resMonthSpreadVal = $scope.SpreadType.Notspecified;
for (var mIndex = startIndex; mIndex < colIndex; mIndex++) {
if (res.SpreadVal[mIndex] == $scope.SpreadType.Over)
resMonthSpreadVal = res.SpreadVal[mIndex];
else if (resMonthSpreadVal != $scope.SpreadType.Over && resMonthSpreadVal != $scope.SpreadType.Less && res.SpreadVal[mIndex] == $scope.SpreadType.Less)
resMonthSpreadVal = res.SpreadVal[mIndex];
else if (resMonthSpreadVal != $scope.SpreadType.Over && resMonthSpreadVal != $scope.SpreadType.Less)
resMonthSpreadVal = res.SpreadVal[mIndex];
}
res.SpreadVal[colIndex] = resMonthSpreadVal;
updateCSSClass(expCatTotal, res, colIndex);
} else {
var resValue = res.QuantityTotalResValue[colIndex];
var compareRes = cellHighlightingService.compare(res.QuantityValues[colIndex], resValue);
res.SpreadVal[colIndex] = compareRes == null ? $scope.SpreadType.Notspecified :
compareRes == 0 ? $scope.SpreadType.Equal :
compareRes < 0 ? $scope.SpreadType.Less : $scope.SpreadType.Over;
updateCSSClass(expCatTotal, res, colIndex);
setParentCSSClass(colIndex, expCatTotal, res, null);
updateCSSClass(expCatTotal, null, colIndex);
}
}
}
if (!expCatTotal.IsSuperEC) {
for (i = 0; i < expCatTotal.Resources.length; i++) {
var resBottom = expCatTotal.Resources[i];
for (var k = 0; k < $scope.data.Calendar.length; k++) {
if ($scope.data.Calendar[k].ProjectId != null && $scope.data.Calendar[k].ExpCatId != null && $scope.data.Calendar[k].ScenarioId != null) {
var nextExpCat = $scope.data.Calendar[k];
var project = getProjectById(nextExpCat.ProjectId, nextExpCat.TeamId);
if (nextExpCat != null && nextExpCat.Resources != null) {
for (var j = 0; j < nextExpCat.Resources.length; j++) {
var resUpper = nextExpCat.Resources[j];
if (resUpper.Id == resBottom.Id && resUpper.QuantityValues[colIndex] > 0) {
//Validate resource allocation upper when capacity exceeded
//Upper resource allocation is red when exceeds capacity or not painted
resUpper.SpreadVal[colIndex] = expCatTotal.SpreadVal[colIndex] == $scope.SpreadType.Over ? $scope.SpreadType.Over :
$scope.SpreadType.Notspecified;
updateCSSClass(nextExpCat, resUpper, colIndex);
setParentCSSClass(colIndex, nextExpCat, resUpper, project);
updateCSSClass(nextExpCat, null, colIndex);
updateCSSClass(project, null, colIndex);
//Validate resource allocation upper when resources allocation sum exceeded team allocation when resource capacity is not over allocated
if (resUpper.SpreadVal[colIndex] != $scope.SpreadType.Over) {
if (nextExpCat != null && nextExpCat.Resources != null) {
var teamValue1 = nextExpCat.QuantityValues[colIndex];
var resValue1Sum = 0;
for (var l = 0; l < nextExpCat.Resources.length; l++) {
var res1 = nextExpCat.Resources[l];
resValue1Sum += res1.QuantityValues[colIndex];
}
compareRes = cellHighlightingService.compare(resValue1Sum, teamValue1);
for (var l = 0; l < nextExpCat.Resources.length; l++) {
var res1 = nextExpCat.Resources[l];
res1.SpreadVal[colIndex] = compareRes == null || compareRes < 0 ? $scope.SpreadType.Notspecified :
compareRes == 0 ? $scope.SpreadType.Equal : $scope.SpreadType.Over;
updateCSSClass(nextExpCat, res1, colIndex);
setParentCSSClass(colIndex, nextExpCat, res1, project);
updateCSSClass(nextExpCat, null, colIndex);
updateCSSClass(project, null, colIndex);
}
}
}
}
}
}
}
}
}
}
}
function setParentCSSClass(colIndex, expCat, res, project)
{
if (expCat.SpreadVal[colIndex] != $scope.SpreadType.Over) {
if (expCat.SpreadVal[colIndex] != $scope.SpreadType.Less) {
if (res.SpreadVal[colIndex] != $scope.SpreadType.Notspecified) {
expCat.SpreadVal[colIndex] = res.SpreadVal[colIndex];
if(project != null)
project.SpreadVal[colIndex] = expCat.SpreadVal[colIndex];
}
}
else {
if (res.SpreadVal[colIndex] == $scope.SpreadType.Over) {
expCat.SpreadVal[colIndex] = res.SpreadVal[colIndex];
if (project != null)
project.SpreadVal[colIndex] = expCat.SpreadVal[colIndex];
}
}
}
}
function updateCSSClass(row, resRow, colIndex) {
if (resRow == null) { // updating project/scenario/category row
if (row == null || colIndex == null || row.SpreadVal == null || row.SpreadVal.length <= colIndex)
return;
row.CSSClass[colIndex] = (row.CSSClass[colIndex] || '').replace(/cellover/g, '').replace(/cellless/g, '').replace(/cellequal/g, '');;
if (!row.ReadOnly[colIndex]) {
if (row.SpreadVal[colIndex] == $scope.SpreadType.Equal)
row.CSSClass[colIndex] = 'cellequal';
else if (row.SpreadVal[colIndex] == $scope.SpreadType.Over)
row.CSSClass[colIndex] = 'cellover';
else if (row.SpreadVal[colIndex] == $scope.SpreadType.Less)
row.CSSClass[colIndex] = 'cellless';
if (row.RowType == $scope.RowType.Project) {
if (row.Color != null && row.Color.length > 0 && $scope.calendarFilters.IsBarMode) {
row.BarStyle[colIndex] = "border-bottom: 1px solid " + row.Color + " !important;border-right-color: " + row.Color + " !important;background-color: " + row.Color + ";";
} else {
row.BarStyle[colIndex] = null;
}
}
}
if (row.ProjectId == null && row.ExpCatId != null && row.RowType == $scope.RowType.BottomCategory)
row.CSSClass[colIndex] += ' capacity-total';
// assign more classes if needed
} else { // updating resource row
if (row == null || colIndex == null || row.ReadOnly[colIndex])
return;
if (resRow.SpreadVal == null || resRow.SpreadVal.length <= colIndex)
return;
resRow.CSSClass[colIndex] = (resRow.CSSClass[colIndex] || '').replace(/cellover/g, '').replace(/cellless/g, '').replace(/cellequal/g, '');;
if (resRow.SpreadVal[colIndex] == $scope.SpreadType.Equal)
resRow.CSSClass[colIndex] = 'cellequal';
else if (resRow.SpreadVal[colIndex] == $scope.SpreadType.Over)
resRow.CSSClass[colIndex] = 'cellover';
else if (resRow.SpreadVal[colIndex] == $scope.SpreadType.Less)
resRow.CSSClass[colIndex] = 'cellless';
// assign more classes if needed
}
setOverAllocated();
}
function setOverAllocated() {
var isOverAllocated = false;
for (var i = 0; i < $scope.data.Calendar.length; i++) {
if ($scope.data.Calendar[i].ExpCatId != null && $scope.data.Calendar[i].ScenarioId != null) {
for (var j = 0; j < $scope.data.Calendar[i].SpreadVal.length; j++) {
if ($scope.data.Calendar[i].SpreadVal[j] == $scope.SpreadType.Over && isInEdited($scope.data.Calendar[i].ExpCatId, $scope.data.Calendar[i].ScenarioId, $scope.data.Headers[j].Milliseconds)) {
isOverAllocated = true;
break;
}
}
}
if (isOverAllocated)
break;
}
$scope.isOverAllocated = isOverAllocated;
}
function isInEdited(expCatId, scenarioId, milliseconds) {
for (var ix = 0; ix < $scope.data2Update.ChangedExpCats.length; ix++) {
if ($scope.data2Update.ChangedExpCats[ix].Id == expCatId && $scope.data2Update.ChangedExpCats[ix].ScenarioId == scenarioId) {
for (var res in $scope.data2Update.ChangedExpCats[ix].Resources) {
var resource = $scope.data2Update.ChangedExpCats[ix].Resources[res];
for (var val in resource.Values) {
if (resource.Values[val].Milliseconds == milliseconds)
return true;
}
}
}
}
return false;
}
function recalcRest(expCat, colIndex) {
var sum = 0;
for (var i = 0; i < expCat.Resources.length; i++) {
sum += expCat.Resources[i].QuantityValues[colIndex];
}
if (expCat.QuantityValues[colIndex] < sum)
expCat.RestQuantity[colIndex] = 0;
else
expCat.RestQuantity[colIndex] = expCat.QuantityValues[colIndex] - sum;
}
function changeResourceValue(newValue, scenarioId, expCatId, resId, teamId, colIndex, isRemoved) {
var expCat = getExpCatById(scenarioId, expCatId, teamId);
var expCatTotal = getExpCatById(null, expCatId, null);
var resource = getResourceByExpCat(expCat, resId);
var resourceTotal = getResourceByExpCat(expCatTotal, resId);
var header = $scope.data.Headers[colIndex];
if (expCat != null && resource != null) {
var team = null;
// adjust team allocations
for (var i = 0; i < expCat.Teams.length; i++) {
var resTeam = findTeamInResourceTeams(resource.Teams, expCat.Teams[i].Id);
if (resTeam == null || header.Milliseconds < resTeam.TeamStartDate || (resTeam.TeamEndDate != null && header.Milliseconds > resTeam.TeamEndDate))
continue;
team = expCat.Teams[i];
team.AllocatedByResources[colIndex] += (newValue - resource.QuantityValues[colIndex]);
}
var res = getResourceById(resource.Id);
res.AllocatedQuantityValues[colIndex] += (newValue - resource.QuantityValues[colIndex]);
if (expCatTotal && resourceTotal) {
expCatTotal.QuantityTotalAllocatedValue[colIndex] += (newValue - resource.QuantityValues[colIndex]);
expCatTotal.QuantityValues[colIndex] += (newValue - resource.QuantityValues[colIndex]);
resourceTotal.QuantityValues[colIndex] += (newValue - resource.QuantityValues[colIndex]);
}
var totalUpdateResult = {};
var totalECsWithResource = getECsThatContainResource(resId);
if (totalECsWithResource) {
for (var i = 0; i < totalECsWithResource.length; i++) {
var totalEC = totalECsWithResource[i];
var totalResource = getResourceByExpCat(totalEC, resId);
if (totalResource) {
totalResource.AllocatedQuantityValues[colIndex] += (newValue - resource.QuantityValues[colIndex]);
}
totalUpdateResult = updateTotals(expCat, totalEC, totalResource, resource, colIndex, newValue, resource.QuantityValues[colIndex], 0, 0);
}
}
resource.QuantityValues[colIndex] = newValue;
if (totalUpdateResult) {
resource.QuantityValues[totalUpdateResult.MonthEndIndex] = totalUpdateResult.NewQuantity;
//recalcSpreading(expCat, expCatTotal, resourceTotal, totalUpdateResult.MonthEndIndex);
//updateCSSClass(expCat, resource, totalUpdateResult.MonthEndIndex);
}
recalcRest(expCat, colIndex);
applyCellChange(scenarioId, expCatId, $scope.data.Headers[colIndex].Milliseconds, expCat.DetailIds[colIndex],
newValue, 0, resId, false, isRemoved, false, false, teamId);
}
//Recalc spreading for current cell
recalcSpreading(expCat, expCatTotal, resourceTotal, colIndex);
updateCSSClass(expCat, resource, colIndex);
//Recalc spreading for month cell
recalcSpreading(expCat, expCatTotal, resourceTotal, totalUpdateResult.MonthEndIndex);
updateCSSClass(expCat, resource, totalUpdateResult.MonthEndIndex);
recalcBottomPartTotals();
refreshTotalSpreadVal(expCatTotal);
}
function changeActualValue(newValue, scenarioId, expCatId, resId, teamId, colIndex, isRemoved) {
var expCat = getExpCatById(scenarioId, expCatId, teamId);
var expCatTotal = getExpCatById(null, expCatId, null);
var actual = getResourceActualByExpCat(expCat, resId);
var actualTotal = getResourceByExpCat(expCatTotal, resId);
var header = $scope.data.Headers[colIndex];
if (expCat != null && actual != null) {
updateActualTotals(expCat, expCatTotal, actualTotal, actual, colIndex, newValue, actual.ActualQuantityValues[colIndex]);
actual.ActualQuantityValues[colIndex] = newValue;
applyCellChange(scenarioId, expCatId, $scope.data.Headers[colIndex].Milliseconds, expCat.DetailIds[colIndex],
newValue, 0, resId, false, isRemoved, true, false, teamId);
}
}
function changeNonProjectTimeValue(newValue, nonProjectTimeId, nonProjectTimeCategoryId, resId, colIndex) {
var category = getNonProjectTimeCatById(nonProjectTimeCategoryId);
var nonProjectTimeTotal = getNonProjectTimeCatById(null);
var nonProjectTime = getNonProjectTimeById(nonProjectTimeId);
var resource = getResourceByNonProjectTime(nonProjectTime, resId);
var header = $scope.data.Headers[colIndex];
if (category != null) {
if (nonProjectTime.IsTeamMode) { // update Team-level NPT cell
updateNonProjectTimeTotals(category, nonProjectTimeTotal, nonProjectTime, null, colIndex, newValue, nonProjectTime.QuantityValues[colIndex]);
}
else if (resource != null && resource.QuantityValues[colIndex] != null) { // update resource-level NPT cell
updateNonProjectTimeTotals(category, nonProjectTimeTotal, nonProjectTime, resource, colIndex, newValue, resource.QuantityValues[colIndex]);
resource.QuantityValues[colIndex] = newValue;
}
applyCellChange(null, nonProjectTimeId, $scope.data.Headers[colIndex].Milliseconds, null,
newValue, 0, resId, false, false, false, true);
}
}
function applyResourceCellChange(catIndex, cellMilliseconds, cellId, cellQuantity, cellCost, resId, isAdded, isRemoved, isActual) {
var resIndex = -1;
for (var dx = 0; dx < $scope.data2Update.ChangedExpCats[catIndex].Resources.length; dx++) {
if ($scope.data2Update.ChangedExpCats[catIndex].Resources[dx].Id == resId) {
resIndex = dx;
break;
}
}
if (resIndex == -1) {
$scope.data2Update.ChangedExpCats[catIndex].Resources[$scope.data2Update.ChangedExpCats[catIndex].Resources.length] = {
Id: resId,
Values: [],
Actuals: [],
IsAdded: isAdded,
IsRemoved: isRemoved
};
resIndex = $scope.data2Update.ChangedExpCats[catIndex].Resources.length - 1;
if (isAdded || isRemoved)
return;
} else {
$scope.data2Update.ChangedExpCats[catIndex].Resources[resIndex].IsRemoved = isRemoved;
if (isRemoved) {
if ($scope.data2Update.ChangedExpCats[catIndex].Resources[resIndex].IsAdded) {
$scope.data2Update.ChangedExpCats[catIndex].Resources.splice(resIndex, 1);
}
return;
}
}
var dateIndex = -1;
if (isActual) {
for (var dx = 0; dx < $scope.data2Update.ChangedExpCats[catIndex].Resources[resIndex].Actuals.length; dx++) {
if ($scope.data2Update.ChangedExpCats[catIndex].Resources[resIndex].Actuals[dx].Milliseconds == cellMilliseconds) {
dateIndex = dx;
break;
}
}
if (dateIndex == -1) {
$scope.data2Update.ChangedExpCats[catIndex].Resources[resIndex].Actuals[$scope.data2Update.ChangedExpCats[catIndex].Resources[resIndex].Actuals.length] = {
Id: cellId,
Milliseconds: cellMilliseconds,
Values: []
};
dateIndex = $scope.data2Update.ChangedExpCats[catIndex].Resources[resIndex].Actuals.length - 1;
}
$scope.data2Update.ChangedExpCats[catIndex].Resources[resIndex].Actuals[dateIndex].Quantity = cellQuantity;
} else {
for (var dx = 0; dx < $scope.data2Update.ChangedExpCats[catIndex].Resources[resIndex].Values.length; dx++) {
if ($scope.data2Update.ChangedExpCats[catIndex].Resources[resIndex].Values[dx].Milliseconds == cellMilliseconds) {
dateIndex = dx;
break;
}
}
if (dateIndex == -1) {
$scope.data2Update.ChangedExpCats[catIndex].Resources[resIndex].Values[$scope.data2Update.ChangedExpCats[catIndex].Resources[resIndex].Values.length] = {
Id: cellId,
Milliseconds: cellMilliseconds,
Values: []
};
dateIndex = $scope.data2Update.ChangedExpCats[catIndex].Resources[resIndex].Values.length - 1;
}
//$scope.data2Update.ChangedExpCats[catIndex].Resources[resIndex].Values[dateIndex].Cost = cellCost;
$scope.data2Update.ChangedExpCats[catIndex].Resources[resIndex].Values[dateIndex].Quantity = cellQuantity;
}
}
function applyNPTCellChange(catIndex, cellMilliseconds, cellId, cellQuantity, cellCost, resId, isAdded, isRemoved) {
var resIndex = -1;
var dateIndex = -1;
if (resId != null) {
for (var dx = 0; dx < $scope.data2Update.NonProjectTimeCats[catIndex].Resources.length; dx++) {
if ($scope.data2Update.NonProjectTimeCats[catIndex].Resources[dx].Id == resId) {
resIndex = dx;
break;
}
}
if (resIndex == -1) {
$scope.data2Update.NonProjectTimeCats[catIndex].Resources[$scope.data2Update.NonProjectTimeCats[catIndex].Resources.length] = {
Id: resId,
Values: [],
Actuals: [],
IsAdded: isAdded,
IsRemoved: isRemoved
};
resIndex = $scope.data2Update.NonProjectTimeCats[catIndex].Resources.length - 1;
} else {
$scope.data2Update.NonProjectTimeCats[catIndex].Resources[resIndex].IsRemoved = isRemoved;
if (isRemoved) {
if ($scope.data2Update.NonProjectTimeCats[catIndex].Resources[resIndex].IsAdded) {
$scope.data2Update.NonProjectTimeCats[catIndex].Resources.splice(resIndex, 1);
}
return;
}
}
}
if (resIndex != -1) // update cell value for resource-level NPT
{
for (var dx = 0; dx < $scope.data2Update.NonProjectTimeCats[catIndex].Resources[resIndex].Values.length; dx++) {
if ($scope.data2Update.NonProjectTimeCats[catIndex].Resources[resIndex].Values[dx].Milliseconds == cellMilliseconds) {
dateIndex = dx;
break;
}
}
if (dateIndex == -1) {
$scope.data2Update.NonProjectTimeCats[catIndex].Resources[resIndex].Values[$scope.data2Update.NonProjectTimeCats[catIndex].Resources[resIndex].Values.length] = {
Id: cellId,
Milliseconds: cellMilliseconds,
Values: []
};
dateIndex = $scope.data2Update.NonProjectTimeCats[catIndex].Resources[resIndex].Values.length - 1;
}
$scope.data2Update.NonProjectTimeCats[catIndex].Resources[resIndex].Values[dateIndex].Quantity = cellQuantity;
}
else // update cell value for team-level NPT
{
for (var dx = 0; dx < $scope.data2Update.NonProjectTimeCats[catIndex].Values.length; dx++) {
if ($scope.data2Update.NonProjectTimeCats[catIndex].Values[dx].Milliseconds == cellMilliseconds) {
dateIndex = dx;
break;
}
}
if (dateIndex == -1) {
$scope.data2Update.NonProjectTimeCats[catIndex].Values[$scope.data2Update.NonProjectTimeCats[catIndex].Values.length] = {
Id: cellId,
Milliseconds: cellMilliseconds,
Values: []
};
dateIndex = $scope.data2Update.NonProjectTimeCats[catIndex].Values.length - 1;
}
var perTeamQuantity = cellQuantity;
var nonProjectTime = getNonProjectTimeById($scope.data2Update.NonProjectTimeCats[catIndex].Id);
if (nonProjectTime != null && nonProjectTime.Resources != null && nonProjectTime.Resources.length != 0)
perTeamQuantity = perTeamQuantity / nonProjectTime.Resources.length;
$scope.data2Update.NonProjectTimeCats[catIndex].Values[dateIndex].Quantity = perTeamQuantity;
}
}
function applyExpCatCellChange(catIndex, cellMilliseconds, cellId, cellQuantity, cellCost) {
var dateIndex = -1;
for (var dx = 0; dx < $scope.data2Update.ChangedExpCats[catIndex].Values.length; dx++) {
if ($scope.data2Update.ChangedExpCats[catIndex].Values[dx].Id == cellId) {
dateIndex = dx;
break;
}
}
if (dateIndex == -1) {
$scope.data2Update.ChangedExpCats[catIndex].Values[$scope.data2Update.ChangedExpCats[catIndex].Values.length] = {
Id: cellId,
Milliseconds: cellMilliseconds,
Values: []
};
dateIndex = $scope.data2Update.ChangedExpCats[catIndex].Values.length - 1;
}
$scope.data2Update.ChangedExpCats[catIndex].Values[dateIndex].Quantity = cellQuantity;
}
function applyCellChange(scenarioId, rowId, cellMilliseconds, cellId, cellQuantity, cellCost, resId, isAdded, isRemoved, isActual, isNonProjectTime, teamId) {
var catIndex = -1;
if (isNonProjectTime) {
for (var ix = 0; ix < $scope.data2Update.NonProjectTimeCats.length; ix++) {
if ($scope.data2Update.NonProjectTimeCats[ix].Id == rowId) {
catIndex = ix;
break;
}
}
if (catIndex == -1) {
$scope.data2Update.NonProjectTimeCats[$scope.data2Update.NonProjectTimeCats.length] = {
ScenarioId: scenarioId,
Id: rowId,
Values: [],
Resources: []
};
catIndex = $scope.data2Update.NonProjectTimeCats.length - 1;
}
} else {
for (var ix = 0; ix < $scope.data2Update.ChangedExpCats.length; ix++) {
if ($scope.data2Update.ChangedExpCats[ix].Id == rowId && $scope.data2Update.ChangedExpCats[ix].ScenarioId == scenarioId) {
catIndex = ix;
break;
}
}
if (catIndex == -1) {
var expCat = getExpCatById(scenarioId, rowId, teamId);
$scope.data2Update.ChangedExpCats[$scope.data2Update.ChangedExpCats.length] = {
ScenarioId: scenarioId,
Id: rowId,
Values: [],
Resources: [],
AllowResourceAssignment: expCat.AllowResourceAssignment
};
catIndex = $scope.data2Update.ChangedExpCats.length - 1;
if (isProjectHaveSupperExpCat(expCat.ProjectId)) {
var project = getProjectById(expCat.ProjectId);
var itemInArray = false;
for (var i = 0; i < $scope.data2Update.ChangedSupperExpCatProjects.length; i++) {
if (expCat.ProjectId == $scope.data2Update.ChangedSupperExpCatProjects[i].ProjectId) {
itemInArray = true;
break;
}
}
if (!itemInArray)
$scope.data2Update.ChangedSupperExpCatProjects[$scope.data2Update.ChangedSupperExpCatProjects.length] = {
ProjectId: expCat.ProjectId,
Name: project.Name
}
}
}
}
if (isNonProjectTime) {
applyNPTCellChange(catIndex, cellMilliseconds, cellId, cellQuantity, cellCost, resId, isAdded, isRemoved);
} else {
if (resId == null) {
applyExpCatCellChange(catIndex, cellMilliseconds, cellId, cellQuantity, cellCost);
} else {
applyResourceCellChange(catIndex, cellMilliseconds, cellId, cellQuantity, cellCost, resId, isAdded, isRemoved, isActual);
}
}
}
function refreshTotalSpreadVal(expCatTotal) {
if (expCatTotal.RowType == $scope.RowType.BottomCategory) {
for (var colIndex = 0; colIndex < $scope.data.Headers.length; colIndex++) {
if(expCatTotal.IsSuperEC)
{
expCatTotal.SpreadVal[colIndex] = $scope.SpreadType.Equal;
} else {
var resValue = expCatTotal.QuantityTotalResValue[colIndex];
var total = 0;
var allocated = 0;
if ($scope.calendarFilters.ShowCapacity == 1) { // Allocated/Capacity
allocated = expCatTotal.QuantityTotalAllocatedValue[colIndex];
total = expCatTotal.QuantityTotalResValue[colIndex];
} else if ($scope.calendarFilters.ShowCapacity == 2) { // Need/Capacity
allocated = expCatTotal.QuantityValues[colIndex];
total = expCatTotal.QuantityTotalResValue[colIndex];
} else if ($scope.calendarFilters.ShowCapacity == 3) { // Allocated/Need
allocated = expCatTotal.QuantityTotalAllocatedValue[colIndex];
total = expCatTotal.QuantityValues[colIndex];
} else if ($scope.calendarFilters.ShowCapacity == 4) { // Remaining/Capacity, display similar to Allocated/Capacity
allocated = expCatTotal.QuantityValues[colIndex];
total = expCatTotal.QuantityTotalResValue[colIndex];
}
var compareRes = cellHighlightingService.compare(allocated, total);
expCatTotal.SpreadVal[colIndex] = compareRes > 0 ? $scope.SpreadType.Over :
compareRes < 0 ? $scope.SpreadType.Less :
compareRes == 0 ? $scope.SpreadType.Equal : $scope.SpreadType.Notspecified;
//var ecClass = $scope.SpreadType.Notspecified;
//for (var j in expCatTotal.Resources)
//{
// if (ecClass != $scope.SpreadType.Over) {
// if (ecClass != $scope.SpreadType.Less) {
// if (expCatTotal.Resources[j].SpreadVal[colIndex] != $scope.SpreadType.Notspecified) {
// ecClass = expCatTotal.Resources[j].SpreadVal[colIndex];
// }
// } else {
// if (expCatTotal.Resources[j].SpreadVal[colIndex] == $scope.SpreadType.Over) {
// ecClass = expCatTotal.Resources[j].SpreadVal[colIndex] ;
// }
// }
// }
//}
//expCatTotal.SpreadVal[colIndex] = ecClass;
}
// this code sets Equal by default. It means that 2 zeros will be displayed as green
// so I decided to remove it to keep zeros without highligthing
//var expCatSpread = $scope.SpreadType.Equal;
//if (expCatTotal.SpreadVal[colIndex] == $scope.SpreadType.Over) {
// expCatSpread = $scope.SpreadType.Over;
//} else if (expCatTotal.SpreadVal[colIndex] == $scope.SpreadType.Less) {
// expCatSpread = $scope.SpreadType.Less;
//}
//expCatTotal.SpreadVal[colIndex] = expCatSpread;
updateCSSClass(expCatTotal, null, colIndex);
}
}
//updateCSSClass(expCatTotal, null, colIndex);
}
function initCmMenuItems() {
var menu = $('#' + $scope.calendarFilters.MenuId);
menu.html("");
$.each($("#menuCalendarOptionsPrint li"), function (i, o) {
$(o).appendTo(menu);
});
$.each($("#menuCalendarOptions li"), function (i, o) {
$(o).clone(true).appendTo(menu).change(function () {
if (typeof onCMPreferencesItemClick === 'function')
onCMPreferencesItemClick(menu);
});
});
// SA. ENV-815
$("#menuCalendarOptions").find('*[data-key]').removeAttr('data-key');
}
$scope.init = function (initData) {
$scope.calendarFilters.ShowUpper = initData.ShowUpper;
$scope.calendarFilters.ShowLower = initData.ShowLower;
$scope.calendarFilters.IsUOMHours = initData.IsUOMHours;
$scope.calendarFilters.PreferredTotalsDisplaying = initData.PreferredTotalsDisplaying;
$scope.calendarFilters.IsBarMode = initData.IsBarMode;
$scope.calendarFilters.IsViewModeMonth = initData.IsViewModeMonth;
$scope.calendarFilters.IsCapacityModeActuals = initData.IsCapacityModeActuals;
$scope.calendarFilters.GroupByTeam = initData.GroupByTeam;
$scope.calendarFilters.ShowCapacity = initData.ShowCapacity;
$scope.calendarFilters.StartDate = initData.StartDate;
$scope.calendarFilters.EndDate = initData.EndDate;
$scope.calendarFilters.ModelType = initData.ModelType;
$scope.calendarFilters.MenuId = initData.MenuId;
$scope.dataSection = initData.DataSection;
$scope.pageTitle = initData.PageTitle; // SA. ENV-905
$scope.availableFilterOptions = initData.FilterOptions; // SA. ENV-799
initCmMenuItems();
var pagePrefArray;
if (initData.PagePreferences && initData.PagePreferences.length > 0) {
// SA. ENV-799
var filterModeInit = "";
var filterSelectedItemId = null;
pagePrefArray = JSON.parse(initData.PagePreferences);
$scope.preferences = pagePrefArray;
for (var i = 0; i < pagePrefArray.length; i++) {
switch (pagePrefArray[i].Key) {
case "showChart":
$scope.calendarFilters.ShowUpper = pagePrefArray[i].Value || (initData.ResourceId && (initData.ResourceId.length > 0));
break;
case "showCriteria":
$scope.calendarFilters.ShowLower = pagePrefArray[i].Value && !(initData.ResourceId && (initData.ResourceId.length > 0));
break;
case "showOption":
$scope.calendarFilters.ShowCapacity = pagePrefArray[i].Value != null ? pagePrefArray[i].Value : 1; //"Total Allocated/Total Need";
break;
case "uomMode":
$scope.calendarFilters.IsUOMHours = pagePrefArray[i].Value;
break;
case "capBarMode":
$scope.calendarFilters.IsBarMode = pagePrefArray[i].Value;
break;
case "defaultView":
$scope.calendarFilters.IsViewModeMonth = pagePrefArray[i].Value;
break;
case "groupByTeam":
if ($scope.calendarFilters.ModelType != 'TeamboardModel')
$scope.calendarFilters.GroupByTeam = pagePrefArray[i].Value;
else
$scope.calendarFilters.GroupByTeam = false;
break;
case "capacityView":
$scope.calendarFilters.IsCapacityModeActuals = pagePrefArray[i].Value;
break;
case "capacityFilterStartDate":
$scope.calendarFilters.StartDate = pagePrefArray[i].Value;
break;
case "capacityFilterEndDate":
$scope.calendarFilters.EndDate = pagePrefArray[i].Value;
break;
case "filterCompanyList":
if ((pagePrefArray[i].Value) && (filterModeInit.length < 1))
filterModeInit = "Company";
break;
case "filterViewList":
if ((pagePrefArray[i].Value) && (filterModeInit.length < 1))
filterModeInit = "View";
break;
case "filterTeamList":
if ((pagePrefArray[i].Value) && (filterModeInit.length < 1))
filterModeInit = "Team";
break;
case "filteredCompany":
filterSelectedItemId = pagePrefArray[i].Value;
break;
case "sortBy":
$scope.calendarFilters.sortBy = pagePrefArray[i].Value;
break;
case "sortOrder":
$scope.calendarFilters.sortOrder = pagePrefArray[i].Value;
break;
default:
console.log("Restore page preferences (Activity calendar): data key not found in parsing loaded preferences (datakey=" +
pagePrefArray[i].Key + ", dataSection=" + $scope.dataSection + ")");
}
}
}
pagePrefArray = [];
pagePrefArray.push({ Key: "showChart", Value: $scope.calendarFilters.ShowUpper });
pagePrefArray.push({ Key: "showCriteria", Value: $scope.calendarFilters.ShowLower });
pagePrefArray.push({ Key: "sortBy", Value: $scope.calendarFilters.sortBy });
pagePrefArray.push({ Key: "sortOrder", Value: $scope.calendarFilters.sortOrder });
pagePrefArray.push({ Key: "showOption", Value: $scope.calendarFilters.ShowCapacity });
pagePrefArray.push({ Key: "uomMode", Value: $scope.calendarFilters.IsUOMHours });
pagePrefArray.push({ Key: "capBarMode", Value: $scope.calendarFilters.IsBarMode });
pagePrefArray.push({ Key: "defaultView", Value: $scope.calendarFilters.IsViewModeMonth });
pagePrefArray.push({ Key: "groupByTeam", Value: $scope.calendarFilters.GroupByTeam });
pagePrefArray.push({ Key: "capacityView", Value: $scope.calendarFilters.IsCapacityModeActuals });
pagePrefArray.push({ Key: "capacityFilterStartDate", Value: $scope.calendarFilters.StartDate });
pagePrefArray.push({ Key: "capacityFilterEndDate", Value: $scope.calendarFilters.EndDate });
// pagePrefArray.push({ Key: "filteredCompany", Value: $scope.calendarFilters.CompanyId });
restorePreferences($scope.dataSection, pagePrefArray);
$('#' + $scope.calendarFilters.MenuId).click(function (event) {
event.stopPropagation();
});
// SA. ENV-799
if ($scope.IsEmpty(initData.CompanyId) && $scope.IsEmpty(initData.ViewId) &&
$scope.IsEmpty(initData.TeamId) && $scope.IsEmpty(initData.ResourceId)) {
if (filterModeInit == "Company")
initData.CompanyId = filterSelectedItemId;
if (filterModeInit == "View")
initData.ViewId = filterSelectedItemId;
if (filterModeInit == "Team")
initData.TeamId = filterSelectedItemId;
if (filterModeInit == "Resource")
initData.ResourceId = filterSelectedItemId;
}
var filterAttached = false;
// if ($scope.calendarFilters.ModelType && ($scope.calendarFilters.ModelType == "EnVisage.Models.CapacityDetailsModel")) {
if (initData.CompanyId && (initData.CompanyId.length > 0)) {
$scope.CalendarFilterMode.SelectedItemId = initData.CompanyId;
$scope.switchCompanyFilterMode(true);
filterAttached = true;
}
if (initData.ViewId && (initData.ViewId.length > 0)) {
$scope.CalendarFilterMode.SelectedItemId = initData.ViewId;
$scope.switchViewFilterMode(true);
filterAttached = true;
}
if (initData.TeamId && (initData.TeamId.length > 0)) {
$scope.CalendarFilterMode.SelectedItemId = initData.TeamId;
$scope.switchTeamFilterMode(true);
filterAttached = true;
}
if (initData.ResourceId && (initData.ResourceId.length > 0)) {
$scope.CalendarFilterMode.SelectedItemId = initData.ResourceId;
$scope.switchResourceFilterMode(true);
filterAttached = true;
}
if (!filterAttached) {
$scope.switchCompanyFilterMode();
}
// }
// SA. ENV-799
$scope.loadCalendarData();
};
$scope.IsEmpty = function (value) {
return !value || (value == null) || (value == "");
}
$scope.getCalendar = function () {
$("#loader").hide();
if ($scope.IsEmpty($scope.calendarFilters.CompanyId) && $scope.IsEmpty($scope.calendarFilters.TeamId) &&
$scope.IsEmpty($scope.calendarFilters.ViewId) && $scope.IsEmpty($scope.calendarFilters.ResourceId)) {
return;
}
blockUI();
$scope.data = null;
$("#loader").show();
var postData = JSON.parse(JSON.stringify($scope.calendarFilters));
$http.post('/CapacityManagement/LoadJsonCalendar', postData).
success(function (data, status, headers, config) {
if (data == null)
$("#loader").hide();
if (data != null && data.Headers == null || data.Headers.length < 1) {
$("#loader").hide();
unblockUI();
return;
}
$scope.data = data;
$scope.calendarFilters.ShowAvgTotals = data.PreferredTotalsDisplaying;
$scope.calendarFilters.IsUOMHours = data.IsUOMHours;
swithViewMode();
for (var i = 0; i < $scope.data.Calendar.length; i++) {
var row = $scope.data.Calendar[i];
row.IsUpperHidden = !$scope.calendarFilters.ShowUpper;
row.IsLowerHidden = !$scope.calendarFilters.ShowLower;
if (row.RowType != $scope.RowType.Team && row.ScenarioId && row.ExpCatId) {
row.AvailableResources = getExpCatResources(row, row.TeamId);
}
if ($scope.calendarFilters.ResourceId != null && row.RowType == $scope.RowType.Project) {
$scope.isReadOnly = !row.IsEdited;
}
}
// initial setup css classes
for (var rowIndex = 0; rowIndex < $scope.data.Calendar.length; rowIndex++) {
if ($scope.data.Calendar[rowIndex].CSSClass == null)
$scope.data.Calendar[rowIndex].CSSClass = new Array($scope.data.Headers.length);
if ($scope.data.Calendar[rowIndex].BarStyle == null)
$scope.data.Calendar[rowIndex].BarStyle = new Array($scope.data.Headers.length);
refreshTotalSpreadVal($scope.data.Calendar[rowIndex]);
for (var colIndex = 0; colIndex < $scope.data.Headers.length; colIndex++) {
if ($scope.data.Calendar[rowIndex].RowType != $scope.RowType.BottomCategory)
updateCSSClass($scope.data.Calendar[rowIndex], null, colIndex);
if ($scope.data.Calendar[rowIndex].Resources != null && $scope.data.Calendar[rowIndex].Resources.length > 0) {
for (var resIndex = 0; resIndex < $scope.data.Calendar[rowIndex].Resources.length; resIndex++) {
if ($scope.data.Calendar[rowIndex].Resources[resIndex].CSSClass == null)
$scope.data.Calendar[rowIndex].Resources[resIndex].CSSClass = new Array($scope.data.Headers.length);
updateCSSClass($scope.data.Calendar[rowIndex], $scope.data.Calendar[rowIndex].Resources[resIndex], colIndex);
}
}
}
}
for (i = 0; i < $scope.data.Calendar.length; i++) {
if ($scope.data.Calendar[i].IsTotals && $scope.data.Calendar[i].RowType == $scope.RowType.Total) {
$scope.grandtotalrow = $scope.data.Calendar[i];
break;
}
}
recalcBottomPartTotals();
$("#loader").hide();
$timeout(function () {
//To-Do: Replace it with ngSelect2 directive
angular.element('[ng-model="row.ResourceToAssignId"]').select2({
allowClear: true,
dropdownAutoWidth: true,
placeholder: 'Select a person',
dropdownCss: { 'font-size': '9pt' },
minimumResultsForSearch: 5,
formatResult: $scope.formatPeopleResourceOption
});
//To-Do: Remove
if ($scope.CalendarFilterMode.SelectedItemId)
$('[name="selFilterElement"]').select2('val', $scope.CalendarFilterMode.SelectedItemId);
$scope.onResize();
});
unblockUI();
}).
error(function (data, status, headers, config) {
// called asynchronously if an error occurs
// or server returns response with an error status.
var errorMessage = 'An error occurred while loading calendar. ErrorCode = 000003';
showErrorModal('Oops!', errorMessage);
$("#loader").hide();
unblockUI();
});
};
$scope.checkEditable = function (useType) {
if (3 === useType) {
return false;
}
return true;
};
$scope.watchKeyInput = function (t) {
$timeout(function () {
if (t.$editable.inputEl.select)
t.$editable.inputEl.select();
else if (t.$editable.inputEl.setSelectionRange)
t.$editable.inputEl.setSelectionRange(0, t.$editable.inputEl.val().length);
}, 3);
t.$editable.inputEl.on('keydown', function (e) {
if (e.which == 9) { //when tab key is pressed
e.preventDefault();
var tab2Cell;
if (e.shiftKey) { // when shift + tab use with 'onblur' set to 'submit' for automatic submission find the parent of the editable before this one in the markup grab the editable and display it
tab2Cell = $(this).parentsUntil('table#table').prevAll(":has(.editable:visible):first").find(".editable:visible:last");
t.$form.$submit();
tab2Cell.click();
} else { // when just tab use with 'onblur' set to 'submit' for automatic submission find the parent of the editable after this one in the markup grab the editable and display it
tab2Cell = $(this).parentsUntil('table#table').nextAll(":has(.editable:visible):first").find(".editable:visible:first");
t.$form.$submit();
tab2Cell.click();
}
}
});
};
$scope.onTxtBlur = function (txt) {
txt.$form.$submit();
};
$scope.saveChanges = function () {
// if user saves as new and selected TD or BU type for new scenarios
if ($scope.SaveAs == "1" && $scope.saveType != "0") {
var message = "Your new Scenario will be created for the full duration as the current Active Scenario.";
if ($scope.saveType == "1")
message = "Your new Scenario will be created for the full duration as the current Active Scenario. <b>Please note:</b> Only current resource-level allocations will be saved as project estimates.";
bootbox.confirm({
message: message,
callback: function (result) {
$scope.$apply(function () {
if (result) {
saveChangesCallback();
}
});
}
});
}
else {
saveChangesCallback();
}
};
function saveChangesCallback() {
blockUI();
$scope.data2Update.IsOverAllocated = $scope.isOverAllocated,
$scope.data2Update.IsMatchChecked = $scope.isMatchChecked;
$scope.data2Update.SaveType = $scope.saveType;
$scope.data2Update.SaveAs = $scope.SaveAs;
$scope.data2Update.IsActiveScenario = $scope.isActiveScenario;
$scope.data2Update.ScenarioName = $scope.ScenarioName;
$scope.data2Update.ScenarioFilters = $scope.calendarFilters;
$http.post('/CapacityManagement/SaveChanges', $scope.data2Update)
.success(function (data, status, headers, config) {
$scope.data2Update = {
ScenarioId: $scope.id,
ChangedExpCats: [],
NonProjectTimeCats: [],
ChangedSupperExpCatProjects: []
};
$scope.getCalendar();
$document.trigger('calendar.scenario-saved');
unblockUI();
}).error(function (data, status, headers, config) {
var errorMessage = 'An error occurred while saving calendar changes. ErrorCode = 000001';
showErrorModal('Oops!', errorMessage);
console.error(errorMessage);
unblockUI();
});
};
$scope.takeAll = function (scenarioId, resId, expCatId, teamId) {
var expCat = getExpCatById(scenarioId, expCatId, teamId);
if (expCat != null) {
var create = false;
var allocateRest = false;
var reset = false;
allocateResource(getResourceByExpCat(expCat, resId), expCat, create, allocateRest, reset);
// we should recalculate availability of changed resource for entire calendar
recalculateAvailability4ResourceInEntireCalendar(resId);
}
};
$scope.takeRemaining = function (scenarioId, resId, expCatId, teamId) {
var expCat = getExpCatById(scenarioId, expCatId, teamId);
if (expCat != null) {
var create = false;
var allocateRest = true;
var reset = false;
allocateResource(getResourceByExpCat(expCat, resId), expCat, create, allocateRest, reset);
// we should recalculate availability of changed resource for entire calendar
recalculateAvailability4ResourceInEntireCalendar(resId);
}
};
$scope.zeroResource = function (scenarioId, resId, expCatId, teamId) {
bootbox.confirm({
message: "Are you sure you want to fill this resource quantities with 0s?",
callback: function (result) {
$scope.$apply(function () {
if (result) {
var expCat = getExpCatById(scenarioId, expCatId, teamId);
var create = false;
var allocateRest = false;
var reset = true;
allocateResource(getResourceByExpCat(expCat, resId), expCat, create, allocateRest, reset);
// we should recalculate availability of changed resource for entire calendar
recalculateAvailability4ResourceInEntireCalendar(resId);
}
});
},
className: "bootbox-sm"
});
};
$scope.assignResource = function (row, $event) {
if (!row || !row.ResourceToAssignId || !row.ExpCatId || !row.TeamId)
return;
var resource = getResourceById(row.ResourceToAssignId);
if (resource != null) {
var project = getProjectById(row.ProjectId);
var expCat = getExpCatById(row.ScenarioId, row.ExpCatId, row.TeamId);
var expCatTotal = getExpCatById(null, row.ExpCatId, null);
//Check the same resource assignment
if (getResourceByExpCat(expCat, resource.Id) != null) {
alert("The " + resource.Name + " has been assigned to the " + expCat.Name);
return;
}
//Add exp cat resource
//todo: just added resources are not validated against capacity values
var newResource = {
'Id': resource.Id,
'Name': resource.Name,
'QuantityValues': new Array($scope.data.Headers.length),
'SpreadVal': new Array($scope.data.Headers.length),
'CSSClass': new Array($scope.data.Headers.length),
'CapacityQuantityValues': new Array($scope.data.Headers.length),
'AllocatedQuantityValues': new Array($scope.data.Headers.length),
'ReadOnlyWeeks': new Array($scope.data.Headers.length),
'ReadOnlyWeeksActuals': new Array($scope.data.Headers.length),
'Teams': resource.Teams,
'StartDate': resource.StartDate,
'EndDate': resource.EndDate,
'GrandTotalReadOnly': false,
'GrandTotalQuantity': 0
};
var i;
for (i = 0; i < newResource.QuantityValues.length; i++) {
if (isNaN(newResource.QuantityValues[i]))
newResource.QuantityValues[i] = 0;
}
expCat.Resources.push(newResource);
if (expCatTotal != null) {
var resourceTotal = getResourceByExpCat(expCatTotal, resource.Id);
var colIndex;
if (resourceTotal == null) {
//Add total exp cat resource
resourceTotal = {
'Id': resource.Id,
'Name': resource.Name,
'QuantityValues': new Array($scope.data.Headers.length),
'SpreadVal': new Array($scope.data.Headers.length),
'CSSClass': new Array($scope.data.Headers.length),
'Teams': resource.Teams,
'QuantityTotalResValue': new Array($scope.data.Headers.length),
'AllocatedQuantityValues': new Array($scope.data.Headers.length)
};
for (i = 0; i < resourceTotal.QuantityValues.length; i++) {
if (isNaN(resourceTotal.QuantityValues[i]))
resourceTotal.QuantityValues[i] = 0;
}
expCatTotal.QuantityTotalResValue = expCatTotal.QuantityResValue * (expCatTotal.Resources.length + 1);
expCatTotal.Resources.push(resourceTotal);
}
var grandTotalReadOnly = true;
var resourceMonthReadOnly = false;
for (colIndex = 0; colIndex < $scope.data.Headers.length; colIndex++) {
if (!$scope.data.Headers[colIndex].IsMonth) {
newResource.ReadOnlyWeeks[colIndex] = resource.ReadOnlyWeeks[colIndex] || project.ReadOnly[colIndex];
newResource.ReadOnlyWeeksActuals[colIndex] = resource.ReadOnlyWeeksActuals[colIndex] || project.ReadOnly[colIndex];
if (!newResource.ReadOnlyWeeks[colIndex]) {
var groupByTeamMode = $scope.calendarFilters.GroupByTeam && isGuidEmpty($scope.calendarFilters.TeamId) && isGuidEmpty($scope.calendarFilters.ResourceId);
if (groupByTeamMode) {
var resTeamItem = findTeamInResourceTeams(newResource.Teams, expCat.TeamId);
if (resTeamItem) {
newResource.ReadOnlyWeeks[colIndex] = resTeamItem.TeamStartDate > $scope.data.Headers[colIndex].Milliseconds ||
$scope.data.Headers[colIndex].Milliseconds > (resTeamItem.TeamEndDate || Number.MAX_VALUE);
newResource.ReadOnlyWeeksActuals[colIndex] |= newResource.ReadOnlyWeeks[colIndex];
newResource.StartDate = resTeamItem.TeamStartDate;
newResource.EndDate = resTeamItem.TeamEndDate || newResource.EndDate;
}
} else {
var rdnly = true;
for (var i = 0; i < (newResource.Teams || []).length; i++) {
var rt = newResource.Teams[i];
if (rt.TeamStartDate < $scope.data.Headers[colIndex].Milliseconds &&
$scope.data.Headers[colIndex].Milliseconds <= (rt.TeamEndDate || Number.MAX_VALUE)) {
rdnly = false;
break;
}
}
newResource.ReadOnlyWeeks[colIndex] = rdnly;
newResource.ReadOnlyWeeksActuals[colIndex] |= rdnly;
}
}
resourceMonthReadOnly |= newResource.ReadOnlyWeeks[colIndex];
grandTotalReadOnly &= newResource.ReadOnlyWeeks[colIndex];
} else {
newResource.ReadOnlyWeeks[colIndex] = resourceMonthReadOnly;
resourceMonthReadOnly = false;
}
}
newResource.GrandTotalReadOnly = grandTotalReadOnly;
}
var create = true;
var allocateRest = false;
var reset = false;
allocateResource(newResource, expCat, create, allocateRest, reset); //true, false, false);
expCatTotal.EmptyScenario = expCatTotal.Resources.length == 0;
row.AvailableResources = getExpCatResources(row, row.TeamId);
//refreshAssignResourceSelect(row.TeamId, row.ProjectId, row.ExpCatId);
resizeCalendar();
}
row.ResourceToAssignId = null;
};
$scope.removeResource = function (scenarioId, resId, expCatId, teamId) {
bootbox.confirm({
message: "Are you sure you want to remove this resource?",
callback: function (result) {
$scope.$apply(function () {
if (result) {
var expCat = getExpCatById(scenarioId, expCatId, teamId);
var create = false;
var allocateRest = false;
var reset = true;
allocateResource(getResourceByExpCat(expCat, resId), expCat, create, allocateRest, reset);
// we should recalculate availability of changed resource for entire calendar
recalculateAvailability4ResourceInEntireCalendar(resId);
//var expCat = getExpCatById(scenarioId, expCatId, teamId);
var expCatTotal = getExpCatById(null, expCatId, null);
for (var i = 0; i < expCat.Resources.length; i++) {
if (expCat.Resources[i].Id == resId) {
for (var k = 0; k < expCat.QuantityValues.length; k++) {
changeResourceValue(0, expCat.ScenarioId, expCat.ExpCatId, expCat.Resources[i].Id, teamId, k, true);
}
expCat.Resources.splice(i, 1);
applyCellChange(scenarioId, expCatId, 0, 0, 0, 0, resId, false, true, false, false);
expCatTotal.EmptyScenario = expCatTotal.Resources.length == 0;
expCat.AvailableResources = getExpCatResources(expCat, teamId);
//refreshAssignResourceSelect(expCat.TeamId, expCat.ProjectId, expCat.ExpCatId, teamId);
return;
}
}
resizeCalendar();
}
});
},
className: "bootbox-sm"
});
};
function getNearestWeekending(date) {
for (var i = 0; i < $scope.data.Headers.length; i++) {
if ($scope.data.Headers[i].Milliseconds >= date)
return $scope.data.Headers[i].Milliseconds;
}
return date;
}
$scope.checkActualValue = function (data, scenarioId, expCatId, resId, teamId, colIndex) {
var newValue = parseFloat(data);
if (isNaN(newValue))
newValue = 0;
if (newValue < 0) {
return "Value should not be less than zero";
}
//alert("That is not implemented yet!")
//return false;
var expCat = getExpCatById(scenarioId, expCatId, teamId);
//var resource = getResourceByExpCat(expCat, resId);
var actual = getResourceActualByExpCat(expCat, resId);
var avg = ($scope.calendarFilters.ShowAvgTotals && !$scope.calendarFilters.IsUOMHours);
var project = getProjectById(expCat.ProjectId);
var resourceEndDateWeekending = getNearestWeekending(actual.EndDate);
var projectEndDateWeekending = getNearestWeekending(project.EndDate);
//Edit month or week cell
if (colIndex >= 0) {
if ($scope.data.Headers[colIndex].IsMonth) {
var oldValue = actual.QuantityValues[colIndex];
var weeks = 0;
var coef = avg ? (newValue / (oldValue || 1)) : (oldValue > 0 ? newValue / oldValue : 1);
//Get weekendings in entire month
for (var j = colIndex - 1; j >= 0 && !$scope.data.Headers[j].IsMonth; j--) {
var currWeekending = $scope.data.Headers[j].Milliseconds;
if (currWeekending >= actual.StartDate && currWeekending >= project.StartDate &&
currWeekending <= resourceEndDateWeekending && currWeekending <= projectEndDateWeekending && !actual.ReadOnlyWeeksActuals[j]) {
weeks++;
}
}
var totalWeeks = $scope.data.Headers[colIndex].Weeks.length;
for (var j = colIndex - 1; j >= 0 && !$scope.data.Headers[j].IsMonth; j--) {
var currWeekending = $scope.data.Headers[j].Milliseconds;
if (currWeekending >= actual.StartDate && currWeekending >= project.StartDate &&
currWeekending <= resourceEndDateWeekending && currWeekending <= projectEndDateWeekending && !actual.ReadOnlyWeeksActuals[j]) {
var val = avg ? (oldValue > 0 ? actual.QuantityValues[j] * coef : newValue / weeks * totalWeeks)
: (oldValue > 0 ? actual.QuantityValues[j] * coef : newValue / weeks);
changeActualValue(val, scenarioId, expCatId, resId, teamId, j, false);
}
}
} else {
changeActualValue(newValue, scenarioId, expCatId, resId, teamId, colIndex, false);
}
//Edit total cell
} else {
var weeks = 0;
var part1Val = 0;
var part2Val = 0;
var part1Week = 0;
var part2Week = 0;
for (var j = 0; j < $scope.data.Headers.length; j++) {
var currWeekending = $scope.data.Headers[j].Milliseconds;
if (currWeekending >= actual.StartDate && currWeekending >= project.StartDate &&
currWeekending <= resourceEndDateWeekending && currWeekending <= projectEndDateWeekending) {
if (!$scope.data.Headers[j].IsMonth) {
if (actual.ReadOnlyWeeksActuals[j]) {
part1Val += actual.ActualQuantityValues[j];
part1Week++;
} else {
part2Val += actual.ActualQuantityValues[j];
part2Week++;
weeks += 1;
}
}
}
}
var newWeekValue = newValue / part2Week;
//Calc avg for non r/o cells
var coef = (actual.GrandActualTotalQuantity > 0 ? newValue / actual.GrandActualTotalQuantity : -1);
var newTotal = part1Val + part2Val;
if (coef > 0)
coef = (coef * newTotal - part1Val) / part2Val;
else
newValue = (newValue * (part1Week + part2Week)) / part2Week;
for (var j = 0; j < $scope.data.Headers.length; j++) {
var currWeekending = $scope.data.Headers[j].Milliseconds;
if (currWeekending >= actual.StartDate && currWeekending >= project.StartDate &&
currWeekending <= resourceEndDateWeekending && currWeekending <= projectEndDateWeekending &&
!$scope.data.Headers[j].IsMonth && !actual.ReadOnlyWeeksActuals[j]) {
var oldValue = actual.ActualQuantityValues[j];
$scope.checkActualValue(avg ? (coef > 0 ? oldValue * coef : newValue) :
coef > 0 ? oldValue * coef : newWeekValue,
scenarioId, expCatId, resId, teamId, j);
}
}
}
// we should recalculate availability of changed resource for entire calendar
//recalculateAvailability4ResourceInEntireCalendar(resId);
return false;
};
$scope.checkNonProjectTimeValue = function (data, nptId, catId, resId, colIndex) {
var newValue = parseFloat(data);
if (isNaN(newValue))
newValue = 0;
if (newValue < 0) {
return "Value should not be less than zero";
}
var category = getNonProjectTimeById(nptId);
var resource = getResourceByNonProjectTime(category, resId);
var avg = ($scope.calendarFilters.ShowAvgTotals && !$scope.calendarFilters.IsUOMHours);
if (colIndex >= 0) {
if ($scope.data.Headers[colIndex].IsMonth) {
var oldValue = category.IsTeamMode ? category.QuantityValues[colIndex] : resource.QuantityValues[colIndex];
if (oldValue == null)
return;
var weeks = $scope.data.Headers[colIndex].Weeks.length;
var coef = avg ? (newValue / (oldValue != 0 ? oldValue : 1 || 1)) : (oldValue > 0 ? newValue / (oldValue != 0 ? oldValue : 1) : 1);
var totalWeeks = $scope.data.Headers[colIndex].Weeks.length;
for (var j = colIndex - 1; j >= 0 && !$scope.data.Headers[j].IsMonth; j--) {
var val = 0;
if (category.IsTeamMode) {
val = avg ? (oldValue > 0 ? category.QuantityValues[j] * coef : newValue / ((weeks * totalWeeks) != 0 ? weeks * totalWeeks : 1))
: (oldValue > 0 ? category.QuantityValues[j] * coef : newValue / (weeks == 0 ? 1 : weeks));
} else {
val = avg ? (oldValue > 0 ? resource.QuantityValues[j] * coef : newValue / ((weeks * totalWeeks) != 0 ? weeks * totalWeeks : 1))
: (oldValue > 0 ? resource.QuantityValues[j] * coef : newValue / (weeks == 0 ? 1 : weeks));
}
changeNonProjectTimeValue(val, nptId, catId, resId, j);
}
} else {
changeNonProjectTimeValue(newValue, nptId, catId, resId, colIndex);
}
//Edit total cell
} else {
var weeks = 0;
var readOnlyRowSum = 0;
var editableRowSum = 0;
var readonlyWeekCount = 0;
var editableWeekCount = 0;
for (var j = 0; j < $scope.data.Headers.length; j++) {
if (!$scope.data.Headers[j].IsMonth) {
if (!category.IsTeamMode) {
if (resource.ReadOnlyWeeks[j]) {
readOnlyRowSum += (resource.QuantityValues[j] == null ? 0 : resource.QuantityValues[j]);
readonlyWeekCount++;
} else {
editableRowSum += (resource.QuantityValues[j] == null ? 0 : resource.QuantityValues[j]);
editableWeekCount++;
weeks += 1;
}
}
else {
if (category.ReadOnly[j]) {
readOnlyRowSum += (category.QuantityValues[j] == null ? 0 : category.QuantityValues[j]);
readonlyWeekCount++;
} else {
editableRowSum += (category.QuantityValues[j] == null ? 0 : category.QuantityValues[j]);
editableWeekCount++;
weeks += 1;
}
}
}
}
var newWeekValue = newValue / editableWeekCount;
//Calc avg for non r/o cells
var coef = -1;
if (!category.IsTeamMode)
coef = (resource.GrandTotalQuantity > 0 ? newValue / resource.GrandTotalQuantity : -1);
else
coef = (category.GrandTotalQuantity > 0 ? newValue / category.GrandTotalQuantity : -1);
var newTotal = readOnlyRowSum + editableRowSum;
if (coef > 0)
coef = (coef * newTotal - readOnlyRowSum) / editableRowSum;
else
newValue = (newValue * (readonlyWeekCount + editableWeekCount)) / editableWeekCount;
for (var j = 0; j < $scope.data.Headers.length; j++) {
if (!$scope.data.Headers[j].IsMonth) {
if (category.IsTeamMode && category.ReadOnly[j])
continue;
if (!category.IsTeamMode && resource.ReadOnlyWeeks[j])
continue;
var oldValue = 0;
if (category.IsTeamMode)
oldValue = category.QuantityValues[j];
else
oldValue = resource.QuantityValues[j];
$scope.checkNonProjectTimeValue(avg ? (coef > 0 ? oldValue * coef : newValue) :
coef > 0 ? oldValue * coef : newWeekValue,
nptId, catId, resId, j);
}
}
}
return false;
}
$scope.checkResourceValue = function (data, scenarioId, expCatId, resId, teamId, colIndex) {
var newValue = parseFloat(data);
if (isNaN(newValue))
newValue = 0;
if (newValue < 0) {
return "Value should not be less than zero";
}
var expCat = getExpCatById(scenarioId, expCatId, teamId);
var resource = getResourceByExpCat(expCat, resId);
var avg = ($scope.calendarFilters.ShowAvgTotals && !$scope.calendarFilters.IsUOMHours);
var project = getProjectById(expCat.ProjectId);
var resourceEndDateWeekending = getNearestWeekending(resource.EndDate);
var projectEndDateWeekending = getNearestWeekending(project.EndDate);
//Edit month or week cell
if (colIndex >= 0) {
if ($scope.data.Headers[colIndex].IsMonth) {
var oldValue = resource.QuantityValues[colIndex];
var weeks = 0;
var coef = avg ? (newValue / (oldValue || 1)) : (oldValue > 0 ? newValue / oldValue : 1);
//Get weekendings in entire month
for (var j = colIndex - 1; j >= 0 && !$scope.data.Headers[j].IsMonth; j--) {
var currWeekending = $scope.data.Headers[j].Milliseconds;
if (currWeekending >= resource.StartDate && currWeekending >= project.StartDate &&
currWeekending <= resourceEndDateWeekending && currWeekending <= projectEndDateWeekending && !resource.ReadOnlyWeeks[j]) {
weeks++;
}
}
var totalWeeks = $scope.data.Headers[colIndex].Weeks.length;
for (var j = colIndex - 1; j >= 0 && !$scope.data.Headers[j].IsMonth; j--) {
var currWeekending = $scope.data.Headers[j].Milliseconds;
if (currWeekending >= resource.StartDate && currWeekending >= project.StartDate &&
currWeekending <= resourceEndDateWeekending && currWeekending <= projectEndDateWeekending && !resource.ReadOnlyWeeks[j]) {
var val = avg ? (oldValue > 0 ? resource.QuantityValues[j] * coef : newValue / weeks * totalWeeks)
: (oldValue > 0 ? resource.QuantityValues[j] * coef : newValue / weeks);
changeResourceValue(val, scenarioId, expCatId, resId, teamId, j, false);
}
}
} else {
changeResourceValue(newValue, scenarioId, expCatId, resId, teamId, colIndex, false);
}
//Edit total cell
} else {
var weeks = 0;
var part1Val = 0;
var part2Val = 0;
var part1Week = 0;
var part2Week = 0;
for (var j = 0; j < $scope.data.Headers.length; j++) {
var currWeekending = $scope.data.Headers[j].Milliseconds;
if (currWeekending >= resource.StartDate && currWeekending >= project.StartDate &&
currWeekending <= resourceEndDateWeekending && currWeekending <= projectEndDateWeekending) {
if (!$scope.data.Headers[j].IsMonth) {
if (resource.ReadOnlyWeeks[j]) {
part1Val += resource.QuantityValues[j];
part1Week++;
} else {
part2Val += resource.QuantityValues[j];
part2Week++;
weeks += 1;
}
}
}
}
var newWeekValue = newValue / part2Week;
//Calc avg for non r/o cells
var coef = (resource.GrandTotalQuantity > 0 ? newValue / resource.GrandTotalQuantity : -1);
var newTotal = part1Val + part2Val;
if (coef > 0)
coef = (coef * newTotal - part1Val) / part2Val;
else
newValue = (newValue * (part1Week + part2Week)) / part2Week;
for (var j = 0; j < $scope.data.Headers.length; j++) {
var currWeekending = $scope.data.Headers[j].Milliseconds;
if (currWeekending >= resource.StartDate && currWeekending >= project.StartDate &&
currWeekending <= resourceEndDateWeekending && currWeekending <= projectEndDateWeekending &&
!$scope.data.Headers[j].IsMonth && !resource.ReadOnlyWeeks[j]) {
var oldValue = resource.QuantityValues[j];
$scope.checkResourceValue(avg ? (coef > 0 ? oldValue * coef : newValue) :
coef > 0 ? oldValue * coef : newWeekValue,
scenarioId, expCatId, resId, teamId, j);
}
}
}
// we should recalculate availability of changed resource for entire calendar
recalculateAvailability4ResourceInEntireCalendar(resId);
return false;
};
function getExpCatResources(expCat, teamId) {
var resources = [];
for (var i = 0; i < $scope.data.AllResources.length; i++) {
var resource = $scope.data.AllResources[i];
var resourceMatch = resource.ExpedentureCategoryId == expCat.ExpCatId && resource.IsActiveEmployee; // condition for general EC
if (expCat.AllowResourceAssignment == false) {
resourceMatch = resource.IsActiveEmployee; // condition for Super EC
}
if (resourceMatch) {
var isProjectFound = false;
for (var j = 0; j < resource.ProjectIds.length; j++) {
if (resource.ProjectIds[j] == expCat.ProjectId &&
(!$scope.calendarFilters.GroupByTeam || $scope.CalendarFilterMode.IsTeam || findTeamInResourceTeams(resource.Teams, expCat.TeamId))) {
isProjectFound = true;
break;
}
}
if (isProjectFound) {
var isFound = false;
if (expCat.Resources != null) {
for (var j = 0; j < expCat.Resources.length; j++) {
if (expCat.Resources[j].Id == resource.Id &&
(!$scope.calendarFilters.GroupByTeam || teamsAnyOf(expCat.Resources[j].Teams, resource.Teams))) {
isFound = true;
break;
}
}
}
if (!isFound) {
for (var j = 0; j < resources.length; j++) {
if (resources[j].Id == resource.Id &&
(!$scope.calendarFilters.GroupByTeam || teamsAnyOf(resources[j].Teams, resource.Teams))) {
isFound = true;
break;
}
}
}
if (!isFound && resource.AssignedToTeam) {
if ($scope.calendarFilters.ResourceId == null || resource.Id == $scope.calendarFilters.ResourceId) {
resources.push(resource);
}
}
}
}
}
/* Calculation of availability for resources */
return getResources4Assign(resources, expCat.ProjectId, teamId);
};
function getResources4Assign(resources, projectId, teamId) {
var resources4Assign = {};
if (!resources || resources.length <= 0 || !projectId)
return null;
var project = getProjectById(projectId);
if (project) {
var startDate = project.StartDate,
endDate = getNearestWeekending(project.EndDate);
for (var i = 0; i < resources.length; i++) {
var resource4Assign = getResource4Assign(resources[i], startDate, endDate, teamId);
if (resource4Assign)
resources4Assign[resource4Assign.id] = resource4Assign;
}
}
return Object.keys(resources4Assign).length > 0 ? resources4Assign : null;
};
function getResource4Assign(resource, startDate, endDate, teamId) {
if (!resource || (startDate || 0) <= 0 || (endDate || 0) <= 0)
return;
var returnFlag = false;
for (var count = 0; count < resource.Teams.length; count++) {
if (resource.Teams[count].TeamStartDate > endDate || (resource.Teams[count].TeamEndDate || Number.MAX_VALUE) < startDate) {
returnFlag = false || returnFlag;
} else {
returnFlag = true;
}
}
if (!returnFlag) {
return null;
}
var resource4Assign = {
id: resource.Id,
name: resource.Name,
minAvailability: Number.MAX_VALUE,
maxAvailability: -Number.MAX_VALUE,
avgAvailability: 0
}
var expCats = dataSources.getExpenditures();
var uomValue = 0;
if (expCats != null) {
var expCat = expCats[resource.ExpedentureCategoryId];
if (expCat != null)
uomValue = expCat.UOMValue || 0;
// UOMValue always in hours and we should convert it in resources depending on activity calendar mode
if (!$scope.calendarFilters.IsUOMHours && uomValue > 0)
uomValue /= uomValue;
}
var today = new Date();
today = new Date(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
var UTCmilliseconds = today.getTime() - today.getTimezoneOffset() * 60 * 1000;
var weeks = 0;
for (var i = 0; i < $scope.data.Headers.length; i++) {
var header = $scope.data.Headers[i];
if (!header || header.IsMonth || header.Milliseconds < startDate || header.Milliseconds > endDate)
continue;
if (UTCmilliseconds > header.Milliseconds + 86400000 - 1)
continue;
var availability = 0;
if (resource.ReadOnlyWeeks[i])
availability = 0;
else {
var isTeamFound = false;
for (var teamIndex = 0; teamIndex < resource.Teams.length; teamIndex++) {
if (resource.Teams[teamIndex].TeamStartDate <= header.Milliseconds && (resource.Teams[teamIndex].TeamEndDate || Number.MAX_VALUE) >= header.Milliseconds) {
if (!isGuidEmpty(teamId))
{
if (resource.Teams[teamIndex].TeamId == teamId)
{
isTeamFound = true;
break;
}
} else {
isTeamFound = true;
break;
}
}
}
if (!isTeamFound)
{
availability = 0;
}
else {
var remainingCapacity = uomValue - resource.AllocatedQuantityValues[i];
if (resource.NonProjectTime) {
for (var npCategoryId in resource.NonProjectTime)
remainingCapacity -= resource.NonProjectTime[npCategoryId][i] || 0;
}
availability = Math.round(100 * (uomValue > 0 ? (remainingCapacity / uomValue) : 0));
}
}
if (resource4Assign.minAvailability > availability)
resource4Assign.minAvailability = availability;
if (resource4Assign.maxAvailability < availability)
resource4Assign.maxAvailability = availability;
resource4Assign.avgAvailability += availability;
weeks++;
}
if (weeks <= 0)
resource4Assign.avgAvailability = 0;
else
resource4Assign.avgAvailability = Math.round(resource4Assign.avgAvailability / weeks);
return resource4Assign;
};
function recalculateAvailability4ResourceInEntireCalendar(resourceId) {
if (!resourceId)
return;
var resource = getResourceById(resourceId);
if (!resource)
return;
for (var i = 0; i < $scope.data.Calendar.length; i++) {
var row = $scope.data.Calendar[i];
if (!row || !row.AvailableResources || !row.AvailableResources[resourceId])
continue;
var project = getProjectById(row.ProjectId);
if (!project)
continue;
var startDate = project.StartDate,
endDate = getNearestWeekending(project.EndDate);
row.AvailableResources[resourceId] = getResource4Assign(resource, startDate, endDate, row.TeamId);
}
};
$scope.isTakeAllDisabled = function (expCat, res) {
for (var j = 0; j < expCat.Resources.length; j++) {
if (expCat.Resources[j].ReadOnly) {
return true;
}
}
return false;
};
$scope.hideCalendarPart = function (upper) {
if (upper)
$scope.calendarFilters.ShowUpper = !$scope.calendarFilters.ShowUpper;
else
$scope.calendarFilters.ShowLower = !$scope.calendarFilters.ShowLower;
if (upper && !$scope.calendarFilters.ShowUpper)
$("#showButtomMode").switcher('disable');
else
$("#showButtomMode").switcher('enable');
if (!upper && !$scope.calendarFilters.ShowLower)
$("#showTopMode").switcher('disable');
else
$("#showTopMode").switcher('enable');
if ($scope.data != null) {
for (var i = 0; i < $scope.data.Calendar.length; i++) {
$scope.data.Calendar[i].IsLowerHidden = !$scope.calendarFilters.ShowLower;
$scope.data.Calendar[i].IsUpperHidden = !$scope.calendarFilters.ShowUpper;
}
}
// force digest cycle as this method could being called from JS
$scope.$digest();
};
$scope.switchUOMMode = function (value) {
var newValue = value != null ? value : !$scope.calendarFilters.IsUOMHours;
$scope.calendarFilters.IsUOMHours = newValue;
$scope.getCalendar();
};
$scope.switchGroupByTeam = function (value) {
var newValue = value != null ? value : !$scope.calendarFilters.GroupByTeam;
$scope.calendarFilters.GroupByTeam = newValue;
$scope.getCalendar();
};
$scope.switchCapacityVew = function (value) {
var newValue = value != null ? value : !$scope.calendarFilters.IsCapacityModeActuals;
$scope.calendarFilters.IsCapacityModeActuals = newValue;
$scope.getCalendar();
};
$scope.switchViewMode = function (value) {
var newValue = value != null ? value : !$scope.calendarFilters.IsViewModeMonth;
$scope.calendarFilters.IsViewModeMonth = newValue;
swithViewMode();
// force digest cycle as this method could being called from JS
$scope.$digest();
resizeCalendar();
};
function swithViewMode() {
if (!$scope.data)
return;
var newValue = $scope.calendarFilters.IsViewModeMonth;
var j;
for (j = 0; j < $scope.data.YearHeaders.length; j++) {
$scope.data.YearHeaders[j].SpanCount = 0;
}
for (var i = 0; i < $scope.data.Headers.length; i++) {
var header = $scope.data.Headers[i];
if (header.IsMonth) {
header.Collapsed = newValue;
header.Show = newValue;
header.CollapsedClass = newValue ? $scope.CollapsedIcon : $scope.NonCollapsedIcon;
for (j = 0; j < $scope.data.YearHeaders.length; j++) {
if ($scope.data.YearHeaders[j].Title == header.Year) {
if (newValue) {
$scope.data.YearHeaders[j].SpanCount++; // -= header.Weeks.length - 1;
} else {
$scope.data.YearHeaders[j].SpanCount += header.Weeks.length;
}
//break;
}
}
} else {
header.Collapsed = newValue;
header.Show = !newValue;
}
header.Initialized = header.Initialized || header.Show;
}
getVisibleCellCount();
}
function getVisibleCellCount() {
var count = 0;
for (var i = 0; i < $scope.data.Headers.length; i++) {
var header = $scope.data.Headers[i];
if (header.Show)
count++;
}
$scope.data.VisibleCellCount = count;
}
$scope.switchBarMode = function () {
var newValue = !$scope.calendarFilters.IsBarMode;
$scope.calendarFilters.IsBarMode = newValue;
if (!$scope.data)
return;
// update project rows CSS
for (var rowIndex = 0; rowIndex < $scope.data.Calendar.length; rowIndex++) {
if ($scope.data.Calendar[rowIndex].RowType == $scope.RowType.Project)
for (var colIndex = 0; colIndex < $scope.data.Headers.length; colIndex++) {
updateCSSClass($scope.data.Calendar[rowIndex], null, colIndex);
}
}
// force digest cycle as this method could being called from JS
$scope.$digest();
};
$scope.changeCapacity = function (value) {
$scope.calendarFilters.ShowCapacity = value;
if (!$scope.data)
return;
for (var i = 0; i < $scope.data.Calendar.length; i++) {
if ($scope.data.Calendar[i].RowType == $scope.RowType.BottomCategory) {
var expCatTotal = $scope.data.Calendar[i];
refreshTotalSpreadVal(expCatTotal);
}
}
recalcBottomPartTotals();
};
$scope.changeSortBy = function (value) {
$scope.calendarFilters.sortBy = value;
$scope.getCalendar();
};
$scope.switchSortOrder = function () {
var newValue = !$scope.calendarFilters.sortOrder;
$scope.calendarFilters.sortOrder = newValue;
$scope.getCalendar();
};
$scope.onMonthHeaderClick = function (header) {
header.Collapsed = !header.Collapsed;
header.Show = !header.Show;
header.CollapsedClass = header.Collapsed ? $scope.CollapsedIcon : $scope.NonCollapsedIcon;
header.Initialized = header.Initialized || header.Show;
var i;
if (header.Weeks && header.Weeks.length > 0)
for (i = 0; i < header.Weeks.length; i++) {
$scope.data.Headers[header.Weeks[i]].Collapsed = header.Collapsed;
$scope.data.Headers[header.Weeks[i]].Show = !header.Collapsed;
$scope.data.Headers[header.Weeks[i]].Initialized = $scope.data.Headers[header.Weeks[i]].Initialized || $scope.data.Headers[header.Weeks[i]].Show;
}
for (i = 0; i < $scope.data.YearHeaders.length; i++) {
if ($scope.data.YearHeaders[i].Title == header.Year) {
if (header.Collapsed) {
$scope.data.YearHeaders[i].SpanCount -= header.Weeks.length - 1;
} else {
$scope.data.YearHeaders[i].SpanCount += header.Weeks.length - 1;
}
break;
}
}
getVisibleCellCount();
resizeCalendar();
};
$scope.anyUpdated = function () {
return ($scope.data2Update && (($scope.data2Update.ChangedExpCats && $scope.data2Update.ChangedExpCats.length > 0) ||
($scope.data2Update.NonProjectTimeCats && $scope.data2Update.NonProjectTimeCats.length > 0)));
};
$scope.onParentNodeClick = function (row, $event) {
if ($($event.target).parents('.scenario-name').length > 0 || $($event.target).parents('.menuGroup').length > 0)
return;
if (row.ProjectId != null && row.ExpCatId == null) {
row.ProjectCollapsed = !row.ProjectCollapsed;
row.CollapsedClass = row.ProjectCollapsed ? $scope.CollapsedIcon : $scope.NonCollapsedIcon;
} else if (row.ExpCatId != null) {
row.ExpCatCollapsed = !row.ExpCatCollapsed;
if (row.ScenarioId != null)
row.CollapsedClass = row.ExpCatCollapsed ? $scope.ExpCatCollapsedIcon : $scope.ExpCatNonCollapsedIcon;
else
row.CollapsedClass = row.ExpCatCollapsed ? $scope.CollapsedIcon : $scope.NonCollapsedIcon;
}
setChildrenCollapsed(row, false, row.IsParentCollapsed);
resizeCalendar();
};
$scope.toggleNonProjectTotalRow = function (row) {
if (!row ||
(row.RowType != $scope.RowType.NonProjectTimeTotal && row.RowType != $scope.RowType.NonProjectTimeCategory && row.RowType != $scope.RowType.NonProjectTime) ||
(row.RowType == $scope.RowType.NonProjectTime && row.IsTeamMode))
return;
row.IsCollapsed = !row.IsCollapsed;
row.CollapsedClass = row.IsCollapsed ? $scope.CollapsedIcon : $scope.NonCollapsedIcon;
if (row.RowType == $scope.RowType.NonProjectTimeCategory) {
for (var i = 0; i < $scope.data.Calendar.length; i++) {
if ($scope.data.Calendar[i].RowType == $scope.RowType.NonProjectTimeTotal) {
var parent = $scope.data.Calendar[i];
for (var j = 0; j < parent.Categories.length; j++) {
var category = parent.Categories[j];
if (category.ParentId == row.Id)
category.IsParentCollapsed = row.IsCollapsed;
}
}
}
}
resizeCalendar();
};
$scope.toggleTotalRow = function (row) {
if (!row || (row.RowType != $scope.RowType.Total))
return;
row.IsCollapsed = !row.IsCollapsed;
row.CollapsedClass = row.IsCollapsed ? $scope.CollapsedIcon : $scope.NonCollapsedIcon;
};
$scope.CheckLock = function ($event, tableId, fieldId, url) {
if (CheckLock(getEventTargetId($event), tableId, fieldId)) {
// SA. ENV-905. Added backurl to navigation link
var currentUrl = document.location.pathname + document.location.search;
var backUrl = "backUrl=" + encodeURIComponent(currentUrl);
var backName = "";
var navigateToUrl = url + '/' + fieldId + "?" + backUrl;
if ($scope.pageTitle && ($scope.pageTitle.length > 0))
// SA. The title of the page to return. Used to display in scenario form button
backName = "&backName=" + encodeURIComponent($scope.pageTitle);
var navigateToUrl = url + '/' + fieldId + "?" + backUrl + backName;
document.location.href = navigateToUrl;
}
};
$scope.ToggleStatus = function ($event, tableId, fieldId) {
if (CheckLock(getEventTargetId($event), tableId, fieldId)) {
if (fieldId == null || fieldId == "")
return "";
var url = "/ForecastDashboard/CheckIfActive/";
var request = {
'scenarioId': fieldId
};
$.get(url, request, function (data) {
if (data == null || data == "") {
$scope.ToggleStatusConfirmed(fieldId);
return '';
} else {
bootbox.confirm({
message: "There is an active scenario for this project already. Are you sure you want to activate this scenario instead of active one?",
callback: function (result) {
$scope.$apply(function () {
if (result) {
$scope.ToggleStatusConfirmed(fieldId);
}
});
},
className: "bootbox-sm"
});
}
});
}
};
$scope.ToggleStatusConfirmed = function (scenarioId) {
if (scenarioId == null || scenarioId == "")
return "";
var url = "/ForecastDashboard/ToggleStatus/";
var request = {
'scenarioId': scenarioId
};
$.get(url, request, function (data) {
$scope.getCalendar();
});
return '';
};
$scope.CalcRemainingCapacity = function (capacity, index) {
return (capacity - $scope.grandtotalrow.QuantityValues[index]);
};
$scope.CalcRemainingCapacityTotal = function (capacityTotal) {
return (capacityTotal - $scope.grandtotalrow.GrandTotalQuantity);
};
function recalcBottomPartTotals() {
//Skip totals calculating for bottom part of the people resource calendar
if ($scope.calendarFilters.ResourceId != null)
return;
var avg = ($scope.calendarFilters.ShowAvgTotals && !$scope.calendarFilters.IsUOMHours);
for (var i in $scope.data.Calendar) {
var expCatTotal = $scope.data.Calendar[i];
if (expCatTotal.RowType == $scope.RowType.BottomCategory) {
var allocated = 0;
var weeksCount = 0;
for (var colIndex = 0; colIndex < $scope.data.Headers.length; colIndex++) {
if (colIndex == 0)
expCatTotal.GrandTotalQuantity = 0;
if (!$scope.data.Headers[colIndex].IsMonth) {
switch ($scope.calendarFilters.ShowCapacity) {
case "1": // Allocated/Capacity
expCatTotal.GrandTotalQuantity += expCatTotal.QuantityTotalAllocatedValue[colIndex];
break;
case "2": // Need/Capacity
expCatTotal.GrandTotalQuantity += expCatTotal.QuantityValues[colIndex];
break;
case "3": // Allocated/Need
expCatTotal.GrandTotalQuantity += expCatTotal.QuantityTotalAllocatedValue[colIndex];
break;
case "4": // Remaining/Capacity, display similar to Allocated/Capacity
expCatTotal.GrandTotalQuantity += (expCatTotal.QuantityTotalResValue[colIndex] - expCatTotal.QuantityValues[colIndex]);
break;
}
for (var j in expCatTotal.Resources) {
var resourceTotal = expCatTotal.Resources[j];
if (colIndex == 0)
resourceTotal.GrandTotalQuantity = 0;
switch ($scope.calendarFilters.ShowCapacity) {
case "1": // Allocated/Capacity
resourceTotal.GrandTotalQuantity += resourceTotal.QuantityValues[colIndex];
break;
case "2": // Need/Capacity
resourceTotal.GrandTotalQuantity += resourceTotal.QuantityValues[colIndex];
break;
case "3": // Allocated/Need
resourceTotal.GrandTotalQuantity += resourceTotal.QuantityValues[colIndex];
break;
case "4": // Remaining/Capacity, display similar to Allocated/Capacity
resourceTotal.GrandTotalQuantity += (resourceTotal.QuantityTotalResValue[colIndex] - resourceTotal.AllocatedQuantityValues[colIndex]);
break;
}
}
weeksCount++;
}
}
if (avg) {
expCatTotal.GrandTotalQuantity = expCatTotal.GrandTotalQuantity / weeksCount;
for (var j in expCatTotal.Resources) {
var resourceTotal = expCatTotal.Resources[j];
resourceTotal.GrandTotalQuantity = resourceTotal.GrandTotalQuantity / weeksCount;
}
}
}
}
}
$scope.CopyScenario = function ($event, scenarioId) {
if (CheckLock(getEventTargetId($event), 'Scenario', scenarioId)) {
if (scenarioId == null || scenarioId == "")
return "";
var url = "/Scenarios/Details/" + scenarioId + '?ptab=copy';
var backUrl = '&backUrl=' + encodeURIComponent('/CapacityManagement');
if (!!$scope.calendarFilters.TeamId)
backUrl = '&backUrl=' + encodeURIComponent('/Team/Board?ptab=calendar');
else if (!!$scope.calendarFilters.ViewId)
backUrl = '&backUrl=' + encodeURIComponent('/View/Board?ptab=calendar');
document.location.href = url; //+backUrl;
}
};
$scope.AddScenario = function ($event, projectId) {
if (typeof loadScenario === 'function')
loadScenario(projectId);
};
function getEventTargetId($event) {
if (!$event.target.id || $event.target.id === '')
return $event.target.parentElement.id;
return $event.target.id;
}
$scope.SelectedFilterElementChanged = function () {
if ($scope.CalendarFilterMode.SelectedItemId && ($scope.CalendarFilterMode.SelectedItemId.length > 0)) {
if ($scope.dataSection && ($scope.dataSection.length > 0)) {
var prefs = collectPreferences($scope.dataSection);
prefs.push({
Key: "filteredCompany",
Value: $scope.CalendarFilterMode.SelectedItemId
});
saveUserPagePreferences(prefs, $scope.dataSection);
}
}
// SA. ENV-799
$scope.loadCalendarData();
// ENV-539. For some reason select2 jQuery control loose selected value just after $scope.digest()
// so we need to set value again on next JS iteration.
$timeout(function () {
$('[name=selFilterElement]').select2("val", $scope.CalendarFilterMode.SelectedItemId);
}, 0, false);
}
$scope.loadCalendarData = function () {
// SA. ENV-799. Begin
if (!$('#filterForm').valid()) return;
$scope.calendarFilters.CompanyId = null;
$scope.calendarFilters.ViewId = null;
$scope.calendarFilters.TeamId = null;
$scope.calendarFilters.ResourceId = null;
if ($scope.CalendarFilterMode.IsCompany)
$scope.calendarFilters.CompanyId = $scope.CalendarFilterMode.SelectedItemId;
if ($scope.CalendarFilterMode.IsView)
$scope.calendarFilters.ViewId = $scope.CalendarFilterMode.SelectedItemId;
if ($scope.CalendarFilterMode.IsTeam)
$scope.calendarFilters.TeamId = $scope.CalendarFilterMode.SelectedItemId;
if ($scope.CalendarFilterMode.IsResource)
$scope.calendarFilters.ResourceId = $scope.CalendarFilterMode.SelectedItemId;
// SA. ENV-799. End
$scope.getCalendar();
}
// SA. ENV-799. Begin
$scope.switchCompanyFilterMode = function (preserverSelection) {
if (!$scope.CalendarFilterMode.IsCompany)
$scope.CalendarFilterMode.IsCompany = true;
$scope.CalendarFilterMode.IsView = false;
$scope.CalendarFilterMode.IsTeam = false;
$scope.CalendarFilterMode.IsResource = false;
$scope.CalendarFilterMode.FilteredEntityTitle = "Company";
if (($scope.availableFilterOptions != null) && ($scope.availableFilterOptions.Companies != null)) {
$scope.displayedOptions = $scope.availableFilterOptions.Companies;
for (var i = 0; i < $scope.displayedOptions.length; i++) {
var option = $scope.displayedOptions[i];
option.CSSClass = 'ddl-level-item';
if (option.Group && option.Group.Name && option.Group.Name.length > 0) {
option.CSSClass += ' pad-left';
}
}
}
if (!preserverSelection) {
$scope.CalendarFilterMode.SelectedItemId = null;
$scope.$broadcast('selectedSourceChanged');
}
};
$scope.switchViewFilterMode = function (preserverSelection) {
if (!$scope.CalendarFilterMode.IsView)
$scope.CalendarFilterMode.IsView = true;
$scope.CalendarFilterMode.IsCompany = false;
$scope.CalendarFilterMode.IsTeam = false;
$scope.CalendarFilterMode.IsResource = false;
$scope.CalendarFilterMode.FilteredEntityTitle = "View";
if (($scope.availableFilterOptions != null) && ($scope.availableFilterOptions.Views != null))
$scope.displayedOptions = $scope.availableFilterOptions.Views;
if (!preserverSelection) {
$scope.CalendarFilterMode.SelectedItemId = null;
$scope.$broadcast('selectedSourceChanged');
}
};
$scope.switchTeamFilterMode = function (preserverSelection) {
if (!$scope.CalendarFilterMode.IsTeam)
$scope.CalendarFilterMode.IsTeam = true;
$scope.CalendarFilterMode.IsCompany = false;
$scope.CalendarFilterMode.IsView = false;
$scope.CalendarFilterMode.IsResource = false;
$scope.CalendarFilterMode.FilteredEntityTitle = "Team";
if (($scope.availableFilterOptions != null) && ($scope.availableFilterOptions.Teams != null))
$scope.displayedOptions = $scope.availableFilterOptions.Teams;
if (!preserverSelection) {
$scope.CalendarFilterMode.SelectedItemId = null;
$scope.$broadcast('selectedSourceChanged');
}
};
$scope.switchResourceFilterMode = function (preserverSelection) {
if (!$scope.CalendarFilterMode.IsResource)
$scope.CalendarFilterMode.IsResource = true;
$scope.CalendarFilterMode.IsCompany = false;
$scope.CalendarFilterMode.IsView = false;
$scope.CalendarFilterMode.IsTeam = false;
$scope.CalendarFilterMode.FilteredEntityTitle = "Resource";
$scope.IsCapacityModeActuals = true; // SA. ENV-886. Individual resource calendar shows actuals only
if (!preserverSelection) {
$scope.CalendarFilterMode.SelectedItemId = null;
$scope.$broadcast('selectedSourceChanged');
}
};
$scope.scrollHeader = function (evt) {
//if (flag) {
// return;
//}
var e = evt ? evt : window.event;
var t = e.target ? e.target : e.srcElement;
if (t.nodeType == 3) {
t = t.parentNode;
}
var tid = t.id.replace(':scroller', '');
var fh = ge$(tid + ':scroller:fx');
var sd = ge$(tid + ':scroller');
fh.style.left = (0 - sd.scrollLeft) + 'px';
var cf = ge$(tid + '_CFB');
if (cf) {
cf.style.marginTop = (0 - (sd.scrollTop)) + 'px';
}
};
function ge$(d) { return document.getElementById(d); }
$scope.clearWidth = function () {
$scope.monthCol = [];
$scope.weekCol = [];
}
$scope.onResize = function () {
if ($scope.data) {
//resize conteiner height
var isAC = angular.element(".ac").length > 0,
firstCellWidth = angular.element("div[id*='fxcol']").width(),
capacityTable = angular.element("#capacity-table"),
capacityTableHeight = capacityTable.height() || 0,
capacityTableWidth = capacityTable.width() || 0,
browserNonIE = ["ie", "ie1"].indexOf(checkBrowser()) < 0, //IE places scrollbars above the content and does not need additional space for scrollbar
heightContainer = angular.element(".height-container"),
heightContainerHeight = heightContainer.height(),
widthContainer = angular.element(".width-container");
var noVScroll = ($(".ac-no-v-scroll").length > 0);
if (noVScroll || (heightContainerHeight <= $scope.calendarViewSettings.tableHeightMin && capacityTableHeight > 0)) {
heightContainerHeight = capacityTableHeight + $scope.calendarViewSettings.scrollHeight;
heightContainer.height(heightContainerHeight);
}
//resize conteiner width
var vertScrollShown = capacityTableHeight > heightContainerHeight;
if (isAC) {
widthContainer.width(Math.min(($scope.calendarViewSettings.headerWidth * $scope.data.VisibleCellCount) + (browserNonIE && vertScrollShown ? $scope.calendarViewSettings.scrollWidth : 0) + 1,
$("#controller1").outerWidth() - firstCellWidth));
} else {
widthContainer.width(Math.min($scope.calendarViewSettings.headerWidth * $scope.data.VisibleCellCount + (browserNonIE && vertScrollShown ? $scope.calendarViewSettings.scrollWidth : 0),
$("#controller1").outerWidth() - firstCellWidth - 1));
}
//resize conteiner height again when horizont scroll bar appears
if (noVScroll && browserNonIE) {
var heightContainerWidth = heightContainer.width();
var horizontScrollShown = capacityTableWidth > heightContainerWidth;
if (horizontScrollShown) {
heightContainerHeight += $scope.calendarViewSettings.scrollHeight;
heightContainer.height(heightContainerHeight);
}
}
}
};
function refreshSelect2(obj) {
$timeout(function () { // You might need this timeout to be sure its run after DOM render.
obj.trigger("change");
}, 0, false);
};
//function refreshAssignResourceSelect(teamId, projectId, expCatId) {
// var control = angular.element('#assign-resource-select-' + teamId + '-' + projectId + '-' + expCatId);
// control.select2('val', '');
// refreshSelect2(control);
//};
$scope.formatPeopleResourceOption = function (result, container, query, escapeMarkup) {
var $optionScope = angular.element(result.element).scope();
/* $optionScope.resource property is declared in the expression in the view: (resourceId, resource) in row.AvailableResources track by $index */
if ($optionScope && $optionScope.resource) {
return scenarioDetailsService.getAssignableResourceOptionHtml($optionScope.resource);
}
};
function findTeamInResourceTeams(teams, teamId) {
if (teams == null || teams.length == null || teams.length <= 0 || teamId == null)
return null;
for (var i = 0; i < teams.length; i++) {
if (teams[i].TeamId == teamId)
return teams[i];
}
return null;
}
function teamsAnyOf(existingTeams, teams2Search) {
if (existingTeams == null || existingTeams.length == null || existingTeams.length <= 0 ||
teams2Search == null || teams2Search.length == null || teams2Search.length <= 0)
return false;
for (var i = 0; i < existingTeams.length; i++) {
for (var j = 0; j < teams2Search.length; j++) {
if (existingTeams[i].TeamId == teams2Search[j].TeamId)
return true;
}
}
return false;
};
function resizeCalendar() {
$timeout(function () {
$scope.onResize();
});
};
}])
.directive('scrollHeader', function () {
return {
restrict: 'A',
link: function (scope, elem, attr, ctrl) {
elem.bind('scroll', function (e) {
scope.scrollHeader(e);
});
}
};
});