4182 lines
166 KiB
JavaScript
4182 lines
166 KiB
JavaScript
'use strict';
|
||
|
||
app.controller('mixProjectController', ['$scope', '$rootScope', '$http', '$filter', '$element', '$timeout', '$document', '$compile', 'hoursResourcesConverter', 'teamInfoService', 'dataSources', function ($scope, $rootScope, $http, $filter, $element, $timeout, $document, $compile, hoursResourcesConverter, teamInfoService, dataSources) {
|
||
|
||
var TeamAllocationRow = (function () {
|
||
var TeamAllocationRow = function (isInitialized, show, cells) {
|
||
this.IsInitialized = isInitialized;
|
||
this.Show = show;
|
||
this.Cells = cells;
|
||
};
|
||
|
||
TeamAllocationRow.prototype = {
|
||
|
||
initialize: function () {
|
||
this.IsInitialized = true;
|
||
},
|
||
|
||
toggleShow: function () {
|
||
this.Show = !this.Show;
|
||
}
|
||
|
||
};
|
||
|
||
return TeamAllocationRow;
|
||
})();
|
||
var AllocationMode = {
|
||
AssignRest: 1,
|
||
AssignAll: 2,
|
||
Reset: 3
|
||
};
|
||
|
||
var FormatCells = {
|
||
DecimalCalcQtyPlaces: 6
|
||
};
|
||
|
||
var commonErrorMessage = 'An error occurred while processing your request. Please, try again later.';
|
||
var C_EMPTY_GUID = '00000000-0000-0000-0000-000000000000'; // SA. ENV-1254
|
||
|
||
// SA. ENV-1001. Urls to get data from server
|
||
$scope.dataUrls = {
|
||
getTeamsByExpCatUrl: '/Team/GetTeamsByExpenditureCategory', // SA. ENV-1254
|
||
getTeamsByIdUrl: '/Mix/GetTeamsById', // SA. ENV-1254
|
||
getScenarioDetailsUrl: '/Mix/GetScenarioDetails',
|
||
editScenarioDetailsUrl: '/Mix/EditScenarioDetails',
|
||
getCalendarUrl: '/Mix/LoadCalendar',
|
||
loadMixUrl: '/Mix/LoadMix',
|
||
saveMixUrl: '/Mix/SaveMix',
|
||
deleteMixUrl: '/Mix/DeleteMix'
|
||
};
|
||
|
||
$scope.copier = {
|
||
parentProjectId: null,
|
||
targetProjectId: null,
|
||
scenarioId: null,
|
||
scenarioName: 'copy'
|
||
};
|
||
|
||
$scope.data = {
|
||
Projects2Add: [],
|
||
SelectedFilterItems: [] // SA. ENV-1254. Selected teams and views in the Mix header filter
|
||
};
|
||
|
||
$scope.AddTeamForm = {
|
||
AvailableProjectExpCats2Add: [], // categories to add
|
||
AvailableInTeams: [], // teams in layout to add
|
||
AvailableProjectOutTeams2Add: [], // teams out of view to add
|
||
ProjectId: null // selected project for which we open the modal form
|
||
};
|
||
|
||
$scope.Calendar = initCalendar();
|
||
$scope.projectBars = [];
|
||
// SA. Shows, the page has unsaved mix data, that must be transferred to the server, when filter changed
|
||
$scope.IsDataLoaded = false;
|
||
$scope.DataChanged = false; // SA. ENV-1153
|
||
$scope.clickShift = 0
|
||
$scope.cellWidth = 114;
|
||
|
||
$scope.UnassignedExpendituresProjectsExist = false; // SA. ENV-1210
|
||
$scope.UnscheduledProjectsExist = false; // SA. ENV-1210
|
||
$scope.PageWarningMessage = ""; // SA. ENV-1210
|
||
|
||
|
||
/* Display Mode -------------------------------------------------------------------------------*/
|
||
$scope.DisplayMode = {
|
||
IsViewModeMonth: true, // do not display weeks by default
|
||
IsUOMHours: true, // display data in Hours
|
||
GroupCapacityByTeam: false, // do not group capacity by team
|
||
TotalsAs: 1, //Allocated/Capacity
|
||
CapacityView: false, // Planned
|
||
IsAvgMode: false, // Totals average mode
|
||
UnassignedAllocations: false // SA. ENV-1298
|
||
};
|
||
|
||
$scope.$watch('DisplayMode.IsViewModeMonth', function (newValue, oldValue) {
|
||
if (oldValue != newValue) {
|
||
$scope.setMonthesExpanded(!newValue);
|
||
for (var teamIndex in $scope.Calendar.Teams) {
|
||
var team = $scope.Calendar.Teams[teamIndex];
|
||
refreshTeamProjectCssStyles(team);
|
||
}
|
||
}
|
||
});
|
||
|
||
$scope.$watch('DisplayMode.IsUOMHours', function (newValue, oldValue) {
|
||
if (oldValue != newValue) {
|
||
$scope.recreateView();
|
||
$scope.$broadcast('changeUOMMode', newValue);
|
||
}
|
||
});
|
||
|
||
$scope.$watch('DisplayMode.GroupCapacityByTeam', function (newValue, oldValue) {
|
||
if (oldValue != newValue) {
|
||
$scope.$broadcast('groupCapacityByTeamChanged', newValue);
|
||
}
|
||
});
|
||
|
||
$scope.$watch('DisplayMode.TotalsAs', function (newValue, oldValue) {
|
||
if (oldValue != newValue) {
|
||
$scope.$broadcast('totalsAsChanged', newValue);
|
||
}
|
||
});
|
||
|
||
$scope.$watch('DisplayMode.CapacityView', function (newValue, oldValue) {
|
||
if (oldValue != newValue) {
|
||
$scope.$broadcast('capacityViewChanged', newValue);
|
||
}
|
||
});
|
||
|
||
$scope.setMonthesExpanded = function (expand) {
|
||
if (expand)
|
||
$scope.Calendar.Header.expandMonthes();
|
||
else
|
||
$scope.Calendar.Header.collapseMonthes();
|
||
};
|
||
|
||
function initCalendar() {
|
||
return {
|
||
StartDate: null, // Mix Start Date
|
||
EndDate: null, // Mix End Date
|
||
FiscalCalendarWeekEndings: [], // Mix Calendar Weekendings
|
||
Header: null, // Calendar header. See details on ~/Scripts/Angular/Types/GridHeader.js
|
||
Teams: [], // Displayed in the Mix Calendar Teams
|
||
Projects: {}, // Mix Projects Raw Data
|
||
UnscheduledProjects: [], // List of Mix unscheduled projects (Ids only)
|
||
Queue4UnscheduledProjects: [], // List of Mix queued projects (Ids only)
|
||
ManagedProjects: [], // List of Mix Calendar displayed projects (Ids only)
|
||
UnassignedExpendituresProjects: [], // SA. ENV-1210
|
||
GridLayout: {} // Mix Calendar Layout
|
||
};
|
||
};
|
||
|
||
$scope.toggleMonth = function (monthIndex) {
|
||
$scope.Calendar.Header.toggleMonth(monthIndex);
|
||
for (var teamIndex in $scope.Calendar.Teams) {
|
||
var team = $scope.Calendar.Teams[teamIndex];
|
||
refreshTeamProjectCssStyles(team);
|
||
}
|
||
};
|
||
|
||
$scope.init = function (data) {
|
||
if (!data) {
|
||
return;
|
||
}
|
||
|
||
if (data.showAvgTotals) { // SA. ENV-1030. Average totals mode
|
||
$scope.DisplayMode.IsAvgMode = data.showAvgTotals;
|
||
}
|
||
|
||
if (data.prefs && (data.prefs.length > 0)) {
|
||
var prefs = angular.fromJson(data.prefs);
|
||
|
||
for (var i = 0; i < prefs.length; i++) {
|
||
switch (prefs[i].Key) {
|
||
case "monthWeekMode":
|
||
$scope.DisplayMode.IsViewModeMonth = prefs[i].Value;
|
||
break;
|
||
case "uomMode":
|
||
$scope.DisplayMode.IsUOMHours = prefs[i].Value;
|
||
break;
|
||
case "groupCapacityByTeam":
|
||
$scope.DisplayMode.GroupCapacityByTeam = prefs[i].Value;
|
||
break;
|
||
case "showOption":
|
||
$scope.DisplayMode.TotalsAs = prefs[i].Value;
|
||
break;
|
||
case "capacityView":
|
||
$scope.DisplayMode.CapacityView = prefs[i].Value;
|
||
break;
|
||
case "unassignedAllocations":
|
||
$scope.DisplayMode.UnassignedAllocations = prefs[i].Value;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
};
|
||
|
||
/* Event receivers -------------------------------------------------------------------------------*/
|
||
$scope.$on('filterChanged', function (event, filter, availableTeams, callbackFn) {
|
||
var postData = {};
|
||
postData.Filter = {};
|
||
postData.Filter.Selection = filter;
|
||
|
||
$scope.fixFilterForSave(postData.Filter); // SA. Fix for valid deserialization of the filter on server
|
||
|
||
if ($scope.IsDataLoaded) {
|
||
// The page has data, that must be sent to the server to preserve user changes
|
||
$scope.createSaveDataPackage(postData);
|
||
}
|
||
|
||
var request = getAntiXSRFRequest($scope.dataUrls.getCalendarUrl, postData);
|
||
try {
|
||
$http(request)
|
||
.success(function (data, status, headers, config) {
|
||
try {
|
||
$scope.IsDataLoaded = true;
|
||
// set available teams from header controller
|
||
data.Filter.Variants.AvailableTeamsAndViews = availableTeams;
|
||
$scope.storeMixFilter(data); // SA. ENV-1254
|
||
$scope.rebuildCalendar(data.Calendar);
|
||
|
||
if (callbackFn && (typeof callbackFn === 'function'))
|
||
callbackFn(data);
|
||
|
||
unblockUI();
|
||
} catch (e) {
|
||
unblockUI();
|
||
showErrorModal('Oops!', commonErrorMessage);
|
||
}
|
||
})
|
||
.error(function (data, status, headers, config) {
|
||
unblockUI();
|
||
showErrorModal('Oops!', commonErrorMessage);
|
||
});
|
||
} catch (e) {
|
||
unblockUI();
|
||
showErrorModal('Oops!', commonErrorMessage);
|
||
}
|
||
});
|
||
|
||
$scope.$on('loadMix', function (event, mixId, setFilterCallback) {
|
||
blockUI();
|
||
|
||
var request = getAntiXSRFRequest($scope.dataUrls.loadMixUrl, mixId);
|
||
try {
|
||
$http(request)
|
||
.success(function (data, status, headers, config) {
|
||
try {
|
||
if (!data) {
|
||
unblockUI();
|
||
return;
|
||
}
|
||
|
||
if (setFilterCallback && (typeof setFilterCallback === 'function'))
|
||
setFilterCallback(data);
|
||
|
||
$scope.IsDataLoaded = true;
|
||
$scope.MixId = data.Id;
|
||
$scope.storeMixFilter(data); // SA. ENV-1254
|
||
$scope.rebuildCalendar(data.Calendar);
|
||
|
||
unblockUI();
|
||
} catch (e) {
|
||
unblockUI();
|
||
showErrorModal('Oops!', commonErrorMessage);
|
||
} finally {
|
||
$scope.$parent.$broadcast('dataloaded');
|
||
}
|
||
})
|
||
.error(function (data, status, headers, config) {
|
||
unblockUI();
|
||
showErrorModal('Oops!', commonErrorMessage);
|
||
});
|
||
} catch (e) {
|
||
unblockUI();
|
||
showErrorModal('Oops!', commonErrorMessage);
|
||
}
|
||
});
|
||
$scope.$on('saveChanges', function (event, saveData, successSaveCallBack, failedSaveCallBack) {
|
||
if (!saveData) {
|
||
if (isCallbackValid(failedSaveCallBack)) {
|
||
failedSaveCallBack(commonErrorMessage);
|
||
}
|
||
return;
|
||
}
|
||
|
||
// Get filter values from header controller
|
||
$scope.createSaveDataPackage(saveData);
|
||
|
||
var request = getAntiXSRFRequest($scope.dataUrls.saveMixUrl, saveData);
|
||
try {
|
||
$http(request)
|
||
.success(function (data, status, headers, config) {
|
||
if (isCallbackValid(successSaveCallBack)) {
|
||
successSaveCallBack(data);
|
||
}
|
||
})
|
||
.error(function (data, status, headers, config) {
|
||
if (isCallbackValid(failedSaveCallBack)) {
|
||
failedSaveCallBack(commonErrorMessage);
|
||
}
|
||
});
|
||
|
||
// unblockUI();
|
||
} catch (e) {
|
||
if (isCallbackValid(failedSaveCallBack)) {
|
||
failedSaveCallBack(commonErrorMessage);
|
||
}
|
||
}
|
||
});
|
||
|
||
$scope.$on('deleteMix', function (event, mixId, okCallback, errorCallback) {
|
||
var request = getAntiXSRFRequest($scope.dataUrls.deleteMixUrl, mixId);
|
||
try {
|
||
$http(request)
|
||
.success(function (data, status, headers, config) {
|
||
try {
|
||
if (okCallback && (typeof okCallback === 'function'))
|
||
okCallback(data);
|
||
else {
|
||
unblockUI();
|
||
}
|
||
} catch (err) {
|
||
if (errorCallback && (typeof errorCallback === 'function'))
|
||
errorCallback();
|
||
unblockUI();
|
||
showErrorModal('Oops!', commonErrorMessage);
|
||
}
|
||
})
|
||
.error(function (data, status, headers, config) {
|
||
if (errorCallback && (typeof errorCallback === 'function'))
|
||
errorCallback();
|
||
unblockUI();
|
||
showErrorModal('Oops!', commonErrorMessage);
|
||
});
|
||
} catch (e) {
|
||
if (errorCallback && (typeof errorCallback === 'function'))
|
||
errorCallback();
|
||
unblockUI();
|
||
showErrorModal('Oops!', commonErrorMessage);
|
||
}
|
||
});
|
||
$scope.$on('saveScenarioDetails', function (event, scenario) {
|
||
if (!!scenario && !!scenario.Id && !!scenario.ProjectId && scenario.Expenditures) {
|
||
var project = $scope.Calendar.Projects[scenario.ProjectId];
|
||
if (!!project && !!project.Scenario && project.Scenario.Id === scenario.Id) {
|
||
var rangeChanged = project.Scenario.StartDate != scenario.StartDate || project.Scenario.EndDate != scenario.EndDate;
|
||
if (rangeChanged) {
|
||
project.Scenario.StartDate = scenario.StartDate;
|
||
project.Scenario.EndDate = scenario.EndDate;
|
||
$scope.recreateView();
|
||
}
|
||
refreshProjectData(project, scenario.Expenditures);
|
||
setDataChanged(true);
|
||
}
|
||
}
|
||
$document.trigger('rmo.close-scenario-details-window');
|
||
});
|
||
$scope.$on('cancelScenarioDetails', function (event) {
|
||
$document.trigger('rmo.close-scenario-details-window');
|
||
});
|
||
/* Drag and Drop -------------------------------------------------------------------------------*/
|
||
$scope.dragEnter = function ($dropmodel, $dragmodel, $event) {
|
||
$dropmodel.Team.Drop = {
|
||
Row: $dropmodel.Row
|
||
};
|
||
|
||
if (!$dragmodel) {
|
||
return;
|
||
}
|
||
|
||
var project = $scope.Calendar.Projects[$dragmodel.ProjectId];
|
||
if (!project || !project.Scenario) {
|
||
$dropmodel.Team.Drop.Cells = {};
|
||
$dropmodel.Team.Drop.Cells[$dropmodel.Milliseconds] = true;
|
||
return;
|
||
}
|
||
|
||
var cellIndex = $dragmodel.CellIndex;
|
||
if ($scope.clickShift != 0) {
|
||
cellIndex = getNextVisibleIndex(cellIndex, Math.ceil($scope.clickShift / $scope.cellWidth) - 1);
|
||
//console.log(cellIndex);
|
||
}
|
||
var shiftX = getDatesShift(getStartDate4Dragging($dragmodel.Team, $dragmodel.Row, cellIndex), $dropmodel.Milliseconds);
|
||
|
||
$dropmodel.Team.Drop.Cells = getActiveCells(project.Scenario, shiftX);
|
||
};
|
||
|
||
function getStartDate4Dragging(team, rowIndex, cellIndex) {
|
||
if ($scope.Calendar.Header.Weeks.length <= cellIndex) {
|
||
return null;
|
||
}
|
||
|
||
var week = $scope.Calendar.Header.Weeks[cellIndex];
|
||
if (week.DataType === Header.DataType.Week) {
|
||
return week.Milliseconds; // return week value if cell is week cell, we should calculate date only for month's cells
|
||
}
|
||
|
||
if (!team || !team.Allocations || team.Allocations.length <= rowIndex) {
|
||
return week.Milliseconds; // return default value if allocations collection is incorrect
|
||
}
|
||
|
||
var projectsRow = team.Allocations[rowIndex];
|
||
if (!projectsRow.Cells || projectsRow.Cells.length <= cellIndex) {
|
||
return week.Milliseconds; // return default value if cells collection is incorrect
|
||
}
|
||
|
||
var month = $scope.Calendar.Header.Months[week.ParentIndex];
|
||
if (!month || !month.Childs) {
|
||
return week.Milliseconds;
|
||
}
|
||
|
||
var projectId = projectsRow.Cells[cellIndex].Id,
|
||
milliseconds = week.Milliseconds;
|
||
|
||
for (var i = 0; i < month.Childs.length; i++) {
|
||
if (projectsRow.Cells.length <= month.Childs[i])
|
||
continue;
|
||
|
||
if (projectsRow.Cells[month.Childs[i]].Id == projectId) {
|
||
milliseconds = $scope.Calendar.Header.Weeks[month.Childs[i]].Milliseconds;
|
||
break;
|
||
}
|
||
}
|
||
|
||
return milliseconds;
|
||
}
|
||
|
||
function getActiveCells(scenario, shiftX) {
|
||
if (!scenario)
|
||
return {};
|
||
|
||
var cells = {};
|
||
var startDateMs = getNewDate(getNearestDate(scenario.StartDate), shiftX),
|
||
endDateMs = getNewDate(getNearestDate(scenario.EndDate), shiftX);
|
||
|
||
for (var i = 0; i < $scope.Calendar.Header.Months.length; i++) {
|
||
var month = $scope.Calendar.Header.Months[i];
|
||
|
||
for (var j = 0; j < month.Childs.length; j++) {
|
||
var week = $scope.Calendar.Header.Weeks[month.Childs[j]];
|
||
if (week.Milliseconds >= startDateMs && week.Milliseconds <= endDateMs) {
|
||
cells[week.Milliseconds] = true;
|
||
if (month.IsCollapsed === true) {
|
||
var monthWeek = $scope.Calendar.Header.Weeks[month.SelfIndexInWeeks];
|
||
cells[monthWeek.Milliseconds] = true;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
return cells;
|
||
}
|
||
|
||
function getNextVisibleIndex(startIndex, count) {
|
||
if (count <= 0)
|
||
return startIndex;
|
||
|
||
var countReturn = 0;
|
||
var countLeft = count;
|
||
var visibleCells = 0;
|
||
for (var i = 0; i < $scope.Calendar.Header.Weeks.length; i++) {
|
||
if (startIndex < visibleCells) {
|
||
|
||
countReturn++;
|
||
if ($scope.Calendar.Header.Weeks[i].Show) {
|
||
countLeft--;
|
||
|
||
if (countLeft == 0)
|
||
break;
|
||
}
|
||
|
||
}
|
||
visibleCells++;
|
||
}
|
||
return startIndex + countReturn;
|
||
//} else {
|
||
// for (var i = startIndex; i >= 0; i--) {
|
||
// if ($scope.Calendar.Header.Weeks[i].Show) {
|
||
// countLeft--;
|
||
// countReturn++;
|
||
// }
|
||
// if (countLeft == 0)
|
||
// break;
|
||
// }
|
||
// return startIndex - countReturn;
|
||
//}
|
||
|
||
}
|
||
|
||
function getNextVisibleMilliseconds(msStart, count) {
|
||
if (count <= 0)
|
||
return msStart;
|
||
|
||
var msReturn = null;
|
||
var countLeft = count;
|
||
var visibleCells = 0;
|
||
for (var i = 0; i < $scope.Calendar.Header.Weeks.length; i++) {
|
||
if (msStart < $scope.Calendar.Header.Weeks[i].Milliseconds) {
|
||
|
||
if ($scope.Calendar.Header.Weeks[i].Show) {
|
||
msReturn = $scope.Calendar.Header.Weeks[i].Milliseconds;
|
||
countLeft--;
|
||
if (countLeft == 0)
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
return msReturn;
|
||
}
|
||
|
||
$scope.dragLeave = function ($dropmodel, $dragmodel) {
|
||
if (!$dropmodel || !$dropmodel.Team)
|
||
return;
|
||
|
||
$dropmodel.Team.Drop = {};
|
||
};
|
||
|
||
$scope.drop = function ($dropmodel, $dragmodel) {
|
||
if (!$dragmodel || !$dropmodel)
|
||
return;
|
||
|
||
var placeBeforeSelectedRow = (($dropmodel.Team.Drop.Mod == "after") || ($dropmodel.Team.Drop.Mod == "before")) ? true : false;
|
||
var selectedRow = ($dropmodel.Team.Drop.Mod == "after") ? $dropmodel.Team.Drop.Row + 1 : $dropmodel.Team.Drop.Row;
|
||
$dropmodel.Team.Drop = {};
|
||
|
||
if ($dragmodel.IsUnscheduled === true) {
|
||
var dt = new Date($dropmodel.Milliseconds);
|
||
var dtFormatted = (dt.getMonth() + 1) + "/" + dt.getDate() + "/" + dt.getFullYear();
|
||
//var ed = new Date($dropmodel.Milliseconds + (1000 * 60 * 60 * 24 * 7));
|
||
var projectId = $dragmodel.ProjectId;
|
||
|
||
showCreateScenarioDialog({
|
||
ProjectId: projectId,
|
||
TargetTeam: $dropmodel.Team,
|
||
TargetRow: selectedRow,
|
||
StartDate: dtFormatted, //$dropmodel.Milliseconds,
|
||
EndDate: dtFormatted //StartDate + 7days
|
||
});
|
||
|
||
// SA. ENV-1298
|
||
var projectItem = $scope.getProjectById(projectId);
|
||
if (projectItem && projectItem.Scenario && projectItem.Scenario.Expenditures) {
|
||
createProjectUnassignedExpendituresItems(projectItem, $dropmodel.Team.Id);
|
||
}
|
||
}
|
||
else {
|
||
if ($dragmodel.Team.Id == $dropmodel.Team.Id) {
|
||
// Move project within team block. Need to update project dates in all teams
|
||
var cellIndex = $dragmodel.CellIndex;
|
||
var ms = $dragmodel.Milliseconds;
|
||
if ($scope.clickShift != 0) {
|
||
//cellIndex = getNextVisibleIndex(cellIndex, Math.ceil($scope.clickShift / $scope.cellWidth)-1);
|
||
//$dropmodel.Milliseconds > $dragmodel.Milliseconds);
|
||
//console.log("w:" + Math.ceil($scope.clickShift / $scope.cellWidth) + " 1orig cellIndex:" + $dragmodel.CellIndex + " 1corrected cellIndex:" + cellIndex);
|
||
ms = getNextVisibleMilliseconds($dragmodel.Milliseconds, Math.ceil($scope.clickShift / $scope.cellWidth) - 1);
|
||
}
|
||
var dragStartDate = getStartDate4Dragging($dragmodel.Team, $dragmodel.Row, cellIndex);//$dragmodel.CellIndex);
|
||
|
||
console.log("$dragmodel.Milliseconds:" + $dragmodel.Milliseconds + " ms:" + ms);
|
||
//var tdSelector = "td[label-pos-miliseconds = '" + $dropmodel.Milliseconds + "'][label-pos-row ='" + ($dropmodel.Row) + "']";
|
||
//var labelSelector = "div[label-pos-projectId ='" + $dragmodel.ProjectId + "']";
|
||
|
||
$scope.moveProjectInsideTeam($dragmodel.ProjectId, $dragmodel.Team,
|
||
getDatesShift(ms, $dropmodel.Milliseconds), $dragmodel.Row, selectedRow, placeBeforeSelectedRow);
|
||
}
|
||
else {
|
||
var cellIndex = $dragmodel.CellIndex;
|
||
if ($scope.clickShift != 0) {
|
||
//cellIndex = getNextVisibleIndex(cellIndex, Math.ceil($scope.clickShift / $scope.cellWidth)-1, true); //$dropmodel.Milliseconds > $dragmodel.Milliseconds);
|
||
//console.log("w:"+Math.ceil($scope.clickShift / $scope.cellWidth)+" 2orig cellIndex:" + $dragmodel.CellIndex + " 2corrected cellIndex:" + cellIndex);
|
||
}
|
||
|
||
var dragStartDate = getStartDate4Dragging($dragmodel.Team, $dragmodel.Row, cellIndex); //$dragmodel.CellIndex);
|
||
var datesShift = getDatesShift(dragStartDate, $dropmodel.Milliseconds);
|
||
var projectItem = $scope.getProjectById($dragmodel.ProjectId);
|
||
|
||
if (!isScenarioLoaded(projectItem.Scenario)) {
|
||
// Load scenario details to perform the check
|
||
loadScenarioDetails(projectItem.Scenario.Id, projectItem.Id, function (expenditures) {
|
||
setScenarioExpenditures(projectItem.Scenario, expenditures);
|
||
checkProjectMovementToTeam(projectItem, $dragmodel.Team.Id, $dropmodel.Team.Id,
|
||
datesShift, selectedRow, placeBeforeSelectedRow);
|
||
});
|
||
}
|
||
else {
|
||
checkProjectMovementToTeam(projectItem, $dragmodel.Team.Id, $dropmodel.Team.Id,
|
||
datesShift, selectedRow, placeBeforeSelectedRow)
|
||
}
|
||
}
|
||
}
|
||
|
||
setDataChanged(true); // SA. ENV-1153
|
||
};
|
||
|
||
// SA. ENV-1210. Checks the project can be moved from source to target team with reassignmtnt of ECs
|
||
function checkProjectMovementToTeam(projectItem, sourceTeamId, targetTeamId, datesShift, selectedRow, placeBeforeSelectedRow) {
|
||
// Get categories, that have allocations in source team to be moved to target team
|
||
var sourceTeamAllocatedCategories =
|
||
getAllocatedScenarioExpendituresByTeam(projectItem.Scenario, sourceTeamId);
|
||
|
||
var nonExistingExpCatsInTargetTeam = [];
|
||
|
||
if (sourceTeamAllocatedCategories && (sourceTeamAllocatedCategories.length > 0)) {
|
||
// Get categories, which allocations can't be moved to target team, because
|
||
// these ECs don't exist in target team
|
||
nonExistingExpCatsInTargetTeam = categoriesNotInTeam(sourceTeamAllocatedCategories, targetTeamId);
|
||
}
|
||
|
||
if (nonExistingExpCatsInTargetTeam.length > 0) {
|
||
// We are here, if no EC can be auto reassigned to new team
|
||
bootbox.dialog({
|
||
message: "Expenditures allocated are not available on new team. To continue, press OK",
|
||
buttons: {
|
||
success: {
|
||
label: "OK",
|
||
className: "btn-primary",
|
||
callback: function () {
|
||
$scope.$apply(function () {
|
||
addTeamToProject(projectItem.Id, targetTeamId, datesShift, selectedRow, placeBeforeSelectedRow);
|
||
copyAllocationsFromTeamToTeam(projectItem, sourceTeamId, targetTeamId);
|
||
removeTeamFromProjectInternal(projectItem.Id, sourceTeamId);
|
||
|
||
if ($scope.DisplayMode.UnassignedAllocations)
|
||
tryAllocateUnassignedExpCatsToTeam(projectItem.Id, targetTeamId);
|
||
|
||
showExpCatMovementPageWarning();
|
||
createProjectUnassignedExpendituresItems(projectItem, targetTeamId);
|
||
|
||
// Refresh calendar view
|
||
$scope.recreateView();
|
||
});
|
||
}
|
||
},
|
||
details: {
|
||
label: "Open Scenario Details",
|
||
className: "btn-primary",
|
||
callback: function () {
|
||
$scope.$apply(function () {
|
||
addTeamToProject(projectItem.Id, targetTeamId, datesShift, selectedRow, placeBeforeSelectedRow);
|
||
copyAllocationsFromTeamToTeam(projectItem, sourceTeamId, targetTeamId);
|
||
removeTeamFromProjectInternal(projectItem.Id, sourceTeamId);
|
||
|
||
if ($scope.DisplayMode.UnassignedAllocations)
|
||
tryAllocateUnassignedExpCatsToTeam(projectItem.Id, targetTeamId);
|
||
|
||
showExpCatMovementPageWarning();
|
||
createProjectUnassignedExpendituresItems(projectItem, targetTeamId);
|
||
|
||
// Refresh calendar view
|
||
$scope.recreateView();
|
||
loadScenarioDetailsEditForm(projectItem.Scenario, false);
|
||
});
|
||
}
|
||
},
|
||
cancel: {
|
||
label: "Cancel",
|
||
className: "btn-default",
|
||
callback: function () {
|
||
// Project movement to another team chancelled
|
||
}
|
||
}
|
||
}
|
||
});
|
||
}
|
||
else {
|
||
// We are here, if reassignment of ECs can be performed automatically
|
||
$scope.hidePageWarningMessage();
|
||
addTeamToProject(projectItem.Id, targetTeamId, datesShift, selectedRow, placeBeforeSelectedRow);
|
||
copyAllocationsFromTeamToTeam(projectItem, sourceTeamId, targetTeamId);
|
||
removeTeamFromProjectInternal(projectItem.Id, sourceTeamId);
|
||
|
||
if ($scope.DisplayMode.UnassignedAllocations)
|
||
tryAllocateUnassignedExpCatsToTeam(projectItem.Id, targetTeamId);
|
||
|
||
// Refresh calendar view
|
||
$scope.recreateView();
|
||
}
|
||
}
|
||
|
||
// SA. ENV-1210. Moves project from source to target team, with reassignment of ECs allocations
|
||
function copyAllocationsFromTeamToTeam(projectItem, sourceTeamId, targetTeamId) {
|
||
// Get categories, that have allocations in source team to be moved to target team
|
||
var allocatedExpCats =
|
||
getAllocatedScenarioExpendituresByTeam(projectItem.Scenario, sourceTeamId);
|
||
|
||
if (allocatedExpCats && (allocatedExpCats.length > 0)) {
|
||
var updatedData = angular.copy(projectItem.Scenario.Expenditures);
|
||
copyAllocationsFromTeamToTeamInternal(updatedData, allocatedExpCats, sourceTeamId, targetTeamId);
|
||
refreshProjectData(projectItem, updatedData);
|
||
}
|
||
}
|
||
|
||
// SA. ENV-1210. Shows warning at the top of the Calendar
|
||
function showExpCatMovementPageWarning() {
|
||
$scope.PageWarningMessage =
|
||
"Destination team doesn't contain some necessary expenditure categories for the project. Please check allocations";
|
||
}
|
||
|
||
// SA. ENV-1210. Copies allocations for given exp. categories from source team to target team
|
||
// ExpCatsdata is the property Expenditures of a Scenario object
|
||
function copyAllocationsFromTeamToTeamInternal(ExpCatsdata, expCategories, sourceTeamId, targetTeamId) {
|
||
if (!ExpCatsdata)
|
||
return null;
|
||
|
||
$.each(expCategories, function (index, expCatId) {
|
||
var currentEC = ExpCatsdata[expCatId];
|
||
var srcTeam = currentEC.Teams[sourceTeamId];
|
||
var dstTeam = currentEC.Teams[targetTeamId];
|
||
|
||
if (srcTeam && srcTeam.QuantityValues) {
|
||
// Destination team exists in the Category. Perform copping of allocations
|
||
var weekEndings = Object.keys(srcTeam.QuantityValues);
|
||
|
||
if (dstTeam && dstTeam.QuantityValues) {
|
||
$.each(weekEndings, function (index, we) {
|
||
if (srcTeam.QuantityValues[we] !== undefined) {
|
||
if (dstTeam.QuantityValues[we] === undefined)
|
||
dstTeam.QuantityValues[we] = 0;
|
||
|
||
dstTeam.QuantityValues[we] += srcTeam.QuantityValues[we];
|
||
}
|
||
});
|
||
}
|
||
else {
|
||
// SA. ENV-1254. The Category hasn't the desired destination team. Copy allocations to
|
||
// UnassignedAllocations
|
||
if (!currentEC.UnassignedAllocations)
|
||
currentEC.UnassignedAllocations = {};
|
||
|
||
$.each(weekEndings, function (index, we) {
|
||
if (srcTeam.QuantityValues[we] !== undefined) {
|
||
if (currentEC.UnassignedAllocations[we] === undefined)
|
||
currentEC.UnassignedAllocations[we] = 0;
|
||
|
||
currentEC.UnassignedAllocations[we] += srcTeam.QuantityValues[we];
|
||
}
|
||
});
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
// SA. ENV-1210. Returns expenditures, that are have non-zero allocations
|
||
// within specified team in specified scenario of a project
|
||
function getAllocatedScenarioExpendituresByTeam(scenarioItem, teamId) {
|
||
var scenarioExpenditureKeys = Object.keys(scenarioItem.Expenditures);
|
||
|
||
var allocatedECs = $.grep(scenarioExpenditureKeys, function (key, index) {
|
||
// Check the EC belongs to specified team
|
||
var include = (scenarioItem.Expenditures[key].Teams != null) &&
|
||
(scenarioItem.Expenditures[key].Teams[teamId] !== undefined);
|
||
|
||
if (include) {
|
||
var currentTeam = scenarioItem.Expenditures[key].Teams[teamId];
|
||
|
||
if (currentTeam.QuantityValues) {
|
||
// Check EC has non-zero allocations within specified team
|
||
var weekendings = Object.keys(currentTeam.QuantityValues);
|
||
var nonZeroAllocations = $.grep(weekendings, function (we, index) {
|
||
return currentTeam.QuantityValues[we] > 0;
|
||
});
|
||
|
||
include = nonZeroAllocations.length > 0;
|
||
}
|
||
else {
|
||
// EC has no allocations within the team
|
||
include = false;
|
||
}
|
||
}
|
||
|
||
return include;
|
||
});
|
||
|
||
return allocatedECs;
|
||
}
|
||
|
||
// SA. ENV-1210. Returns those categories from specified list, that not exist in the specified team
|
||
function categoriesNotInTeam(expCategories, teamId) {
|
||
var teamItem = teamInfoService.getById(teamId);
|
||
var teamCategories = teamItem.ExpCategories ? Object.keys(teamItem.ExpCategories) : [];
|
||
|
||
var categoriesOutOfTeam = $.grep(expCategories, function (ecId, index) {
|
||
return teamCategories.indexOf(ecId) < 0;
|
||
});
|
||
|
||
return categoriesOutOfTeam;
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
/* Display Model -------------------------------------------------------------------------------*/
|
||
function calculateTeamHeight(team) {
|
||
if (!team || !team.Allocations || team.Allocations.length <= 0)
|
||
return 1;
|
||
|
||
var height = team.Allocations.length * 32, //52,
|
||
rowsWithBorder = $scope.GridLayout[team.Id].length || 0;
|
||
|
||
return (height - 9 + rowsWithBorder) + 'px'; // 4 (top padding) + 4 (bottom padding) + 1 (bottom-border) = 9
|
||
};
|
||
|
||
function convertToTeamViewModel(teams) {
|
||
var viewModel = [];
|
||
if (!teams)
|
||
return viewModel;
|
||
|
||
for (var i = 0; i < teams.length; i++) {
|
||
viewModel.push({
|
||
Id: teams[i].Id,
|
||
Name: teams[i].Name,
|
||
Allocations: [],
|
||
AllocationCssStyles: []
|
||
});
|
||
}
|
||
|
||
return viewModel;
|
||
};
|
||
|
||
// SA. ENV-1254
|
||
$scope.storeMixFilter = function (data) {
|
||
$scope.data.SelectedFilterItems = [];
|
||
angular.forEach(data.Filter.Selection.TeamsViews, function (item, index) {
|
||
$scope.data.SelectedFilterItems.push(item.Id);
|
||
});
|
||
$scope.data.AvailableTeams = $filter('filter')(data.Filter.Variants.AvailableTeamsAndViews, {Group: {Name: 'Teams'}});
|
||
}
|
||
|
||
$scope.rebuildCalendar = function (data) {
|
||
if (!data) {
|
||
data = {};
|
||
}
|
||
|
||
$scope.Calendar = initCalendar();
|
||
if (!data.WeekEndings || Object.keys(data.WeekEndings).length <= 0)
|
||
return;
|
||
|
||
// init team service with teams collection
|
||
teamInfoService.init(data.Teams);
|
||
|
||
$scope.Calendar.FiscalCalendarWeekEndings = data.FiscalCalendarWeekEndings || [];
|
||
$scope.Calendar.Header = new GridHeader(data.WeekEndings || {}).create();
|
||
$scope.Calendar.StartDate = $scope.Calendar.Header.Weeks[0].Milliseconds;
|
||
$scope.Calendar.EndDate = $scope.Calendar.Header.Weeks[$scope.Calendar.Header.Weeks.length - 2].Milliseconds;
|
||
$scope.Calendar.Teams = convertToTeamViewModel(data.Teams);
|
||
$scope.Calendar.Projects = data.Projects || {};
|
||
$scope.Calendar.UnscheduledProjects = data.UnscheduledProjects || [];
|
||
$scope.Calendar.Queue4UnscheduledProjects = data.QueuedProjects || [];
|
||
$scope.Calendar.UnassignedExpendituresProjects = data.UnassignedExpendituresProjects || []; // SA. ENV-1210
|
||
$scope.Calendar.ManagedProjects = data.ManagedProjects || [];
|
||
$scope.GridLayout = $scope.getLayoutForClient(data.Layout);
|
||
|
||
$scope.prepareManagedToDisplay();
|
||
$scope.prepareUnscheduledToDisplay();
|
||
$scope.prepareQueuedToDisplay();
|
||
$scope.prepareUnassignedEcsProjectsToDisplay(); // SA. ENV-1210
|
||
|
||
$scope.setMonthesExpanded(!$scope.DisplayMode.IsViewModeMonth);
|
||
$scope.recreateView();
|
||
|
||
// need to pass new objects for preventing using single instances of objects inside current and other controllers
|
||
$scope.$broadcast('rebindTeamInfo', {
|
||
Teams: angular.copy(data.Teams || []),
|
||
Vacations: angular.copy(data.Vacations || {}),
|
||
Trainings: angular.copy(data.Trainings || {}),
|
||
Header: angular.copy($scope.Calendar.Header),
|
||
DisplayMode: $scope.DisplayMode
|
||
});
|
||
|
||
// SA. ENV-1159. Init page javascript controls with default values
|
||
initPageControls();
|
||
};
|
||
|
||
$scope.recreateView = function () {
|
||
for (var teamIndex = 0; teamIndex < $scope.Calendar.Teams.length; teamIndex++) {
|
||
var currentTeam = $scope.Calendar.Teams[teamIndex];
|
||
currentTeam.Allocations = [];
|
||
currentTeam.AllocationCssStyles = [];
|
||
|
||
createLayoutForTeam(currentTeam.Id);
|
||
|
||
if (!$scope.GridLayout[currentTeam.Id] || $scope.GridLayout[currentTeam.Id].length < 1) {
|
||
// Draw blank row for empty team
|
||
createDummyViewForTeam(currentTeam);
|
||
} else {
|
||
createProjectsViewForTeam(currentTeam);
|
||
}
|
||
|
||
// prepare CSS styles for each allocation cels
|
||
refreshTeamProjectCssStyles(currentTeam);
|
||
}
|
||
};
|
||
|
||
function refreshTeamProjectCssStyles(currentTeam) {
|
||
for (var i = 0; i < currentTeam.Allocations.length; i++) {
|
||
currentTeam.AllocationCssStyles[i] = '';
|
||
if (i > 0)
|
||
currentTeam.AllocationCssStyles[i] += 'visibility:hidden;';
|
||
else
|
||
currentTeam.AllocationCssStyles[i] += 'height:' + calculateTeamHeight(currentTeam);
|
||
var colSpan = 1;
|
||
var firstCell = null;
|
||
var lastId = null;
|
||
for (var j = 0; j < currentTeam.Allocations[i].Cells.length; j++) {
|
||
var cell = currentTeam.Allocations[i].Cells[j];
|
||
cell.IsSpanned = false;
|
||
cell.colSpan = 1;
|
||
cell.width = 50;
|
||
//cell.Pinned = false;
|
||
|
||
if (!cell.Id) {
|
||
continue;
|
||
}
|
||
|
||
if ($scope.Calendar.Header.Weeks[j].Show) {
|
||
//last cell
|
||
if (cell.IsProjectLastCell) {
|
||
//one cell project
|
||
if (!cell.IsFirstCell) {
|
||
colSpan++;
|
||
cell.IsSpanned = true;
|
||
}
|
||
//if (firstCell && colSpan > 1) {
|
||
// firstCell.colSpan = colSpan;
|
||
// firstCell.width = (colSpan * $scope.cellWidth) - 66;
|
||
// firstCell.OverEnd = cell.OverEnd;
|
||
//}
|
||
} else {
|
||
//first cell
|
||
if (cell.IsFirstCell) {
|
||
firstCell = cell;
|
||
colSpan = 1;
|
||
//cell in a middle
|
||
} else {
|
||
if (firstCell) {
|
||
cell.IsSpanned = true;
|
||
colSpan++;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if (cell.IsProjectLastCell)
|
||
{
|
||
if (firstCell && colSpan > 1) {
|
||
firstCell.colSpan = colSpan;
|
||
firstCell.width = (colSpan * $scope.cellWidth) - 66;
|
||
firstCell.OverEnd = cell.OverEnd;
|
||
}
|
||
}
|
||
if (cell.colSpan == 0)
|
||
cell.colSpan = 1;
|
||
|
||
cell.CssStyle = getProjectCSS($scope.getProjectById(cell.Id));
|
||
//drawBorder(currentTeam, i, j);
|
||
}
|
||
//if (firstCell && colSpan > 1)
|
||
// firstCell.colSpan = colSpan;
|
||
}
|
||
}
|
||
|
||
function createProjectsViewForTeam(team) {
|
||
if (!team || !$scope.GridLayout || !$scope.GridLayout[team.Id])
|
||
return;
|
||
|
||
var currentLayout = $scope.GridLayout[team.Id];
|
||
var projectsMapHelper = [];
|
||
|
||
// Create helper projects layout struct for current team
|
||
for (var rowIndex = 0; rowIndex < currentLayout.length; rowIndex++) {
|
||
var helperRow = [];
|
||
|
||
for (var itemIndex = 0; itemIndex < currentLayout[rowIndex].length; itemIndex++) {
|
||
var projectId = currentLayout[rowIndex][itemIndex].ProjectId;
|
||
var projItem = $scope.getProjectById(projectId);
|
||
helperRow.push(projItem);
|
||
}
|
||
|
||
projectsMapHelper.push(helperRow);
|
||
}
|
||
|
||
for (var rowIndex = 0; rowIndex < projectsMapHelper.length; rowIndex++) {
|
||
var cells = [];
|
||
var weekProjects = [];
|
||
var monthProjects = [];
|
||
|
||
for (var mIndex = 0; mIndex < $scope.Calendar.Header.Months.length; mIndex++) {
|
||
var weekendsCount = $scope.Calendar.Header.Months[mIndex].Childs.length; // SA. ENV-1046
|
||
for (var wIndex = 0; wIndex < weekendsCount; wIndex++) {
|
||
var weekIndex = $scope.Calendar.Header.Months[mIndex].Childs[wIndex];
|
||
var week = $scope.Calendar.Header.Weeks[weekIndex];
|
||
var weekProject = getProjectAtWeekCell(week, projectsMapHelper[rowIndex]);
|
||
|
||
if (!!weekProject) {
|
||
// Create and add project cell
|
||
var isFirstCell = (weekProjects[weekProject.Id] === undefined);
|
||
var isLastCell = week.Milliseconds == $scope.Calendar.EndDate;
|
||
var endDelta = weekProject.Scenario ? Math.floor((week.Milliseconds - weekProject.Scenario.EndDate) / 86400000) : -1;
|
||
var isProjectLastCell = (endDelta >= 0 && endDelta <= 7) || (isLastCell && weekProject.Scenario.EndDate > week.Milliseconds);
|
||
cells.push(createCell(weekProject.Id, weekProject.Name, '', '', isFirstCell,
|
||
(isFirstCell && weekProject.Scenario ? (Math.floor(($scope.Calendar.StartDate - weekProject.Scenario.StartDate) / 86400000) > 7) : false),
|
||
(isLastCell && weekProject.Scenario ? (Math.floor((weekProject.Scenario.EndDate - $scope.Calendar.EndDate) / 86400000) > 0) : false),
|
||
isProjectLastCell, weekProject.Pinned));
|
||
weekProjects[weekProject.Id] = true;
|
||
} else {
|
||
// Create and add blank cell
|
||
cells.push(createBlankCell());
|
||
}
|
||
}
|
||
|
||
var monthProject = getProjectAtMonthCell($scope.Calendar.Header.Months[mIndex], projectsMapHelper[rowIndex]);
|
||
if (!!monthProject) {
|
||
|
||
var isFirstCell = (monthProjects[monthProject.Id] === undefined);
|
||
var isLastCell = $scope.Calendar.Header.Months.length - 1 == mIndex;
|
||
var endDelta = -1;
|
||
var isProjectLastMonthCell = false;
|
||
for (var wIndex = 0; wIndex < weekendsCount; wIndex++) {
|
||
var weekIndex = $scope.Calendar.Header.Months[mIndex].Childs[wIndex];
|
||
var week = $scope.Calendar.Header.Weeks[weekIndex];
|
||
var endDelta = monthProject.Scenario ? Math.floor((week.Milliseconds - monthProject.Scenario.EndDate) / 86400000) : -1;
|
||
isProjectLastMonthCell = (endDelta >= 0 && endDelta <= 7) || (isLastCell && monthProject.Scenario.EndDate > week.Milliseconds);
|
||
if (isProjectLastMonthCell)
|
||
break;
|
||
}
|
||
cells.push(createCell(monthProject.Id, monthProject.Name, '', '', isFirstCell,
|
||
(isFirstCell && monthProject.Scenario ? (Math.floor(($scope.Calendar.StartDate - monthProject.Scenario.StartDate) / 86400000) > 7) : false),
|
||
(isLastCell && monthProject.Scenario ? (Math.floor((monthProject.Scenario.EndDate - $scope.Calendar.EndDate) / 86400000) > 0) : false),
|
||
isProjectLastMonthCell, monthProject.Pinned));
|
||
monthProjects[monthProject.Id] = true;
|
||
}
|
||
else {
|
||
cells.push(createBlankCell());
|
||
}
|
||
}
|
||
|
||
team.Allocations.push((new TeamAllocationRow(true, true, cells)));
|
||
}
|
||
};
|
||
|
||
function createDummyViewForTeam(team) {
|
||
var cells = [];
|
||
var allocationCell;
|
||
|
||
for (var i = 0; i < $scope.Calendar.Header.Weeks.length; i++) {
|
||
allocationCell = $scope.createDummyAllocationCell();
|
||
cells.push(allocationCell);
|
||
}
|
||
|
||
team.Allocations.push((new TeamAllocationRow(true, true, cells)));
|
||
};
|
||
|
||
function createLayoutForTeam(teamId) {
|
||
if (!$scope.GridLayout)
|
||
$scope.GridLayout = {};
|
||
|
||
if (!$scope.GridLayout[teamId])
|
||
$scope.GridLayout[teamId] = [];
|
||
|
||
if ($scope.GridLayout[teamId].length < 1) {
|
||
// Recreate layout via automatic engine
|
||
$scope.GridLayout[teamId] = arrangeProjectsWithFCNR(teamId);
|
||
}
|
||
};
|
||
|
||
// arrange projects using Floor Сeiling No Rotation (FCNR) algorithm
|
||
function arrangeProjectsWithFCNR(teamId) {
|
||
var layout = [],
|
||
layoutFillingRemaining = []; // contains how many empty space exists in the row
|
||
|
||
if (!teamId)
|
||
return layout;
|
||
|
||
if (!$scope.Calendar.ManagedProjects || $scope.Calendar.ManagedProjects.length <= 0)
|
||
return layout;
|
||
|
||
for (var i in $scope.Calendar.ManagedProjects) {
|
||
var currentProject = $scope.Calendar.Projects[$scope.Calendar.ManagedProjects[i].Id];
|
||
if (!currentProject || !currentProject.Scenario || !$scope.projectHasTeam(currentProject, teamId))
|
||
continue;
|
||
|
||
var optimalX = -1,
|
||
optimalY = -1,
|
||
projectCovers = 0, // what period covers current project in selected range
|
||
// min value of remaining empty space if project will be placed at the row;
|
||
// we need to place project in the row in which empty space will be min after project will be placed
|
||
minRemaining = $scope.Calendar.EndDate - $scope.Calendar.StartDate;
|
||
|
||
for (var j in layout) {
|
||
|
||
var firstProject = $scope.Calendar.Projects[layout[j][0].ProjectId];
|
||
var lastProject = $scope.Calendar.Projects[layout[j][layout[j].length - 1].ProjectId];
|
||
|
||
var currentProjectNearestStartDate = getNearestDate(currentProject.Scenario.StartDate),
|
||
currentProjectNearestEndDate = getNearestDate(currentProject.Scenario.EndDate),
|
||
firstProjectNearestStartDate = getNearestDate(firstProject.Scenario.StartDate),
|
||
lastProjectNearestEndDate = getNearestDate(lastProject.Scenario.EndDate);
|
||
|
||
// check if the project can be placed in the begin of the row
|
||
if (currentProjectNearestEndDate < firstProjectNearestStartDate) {
|
||
projectCovers = currentProject.Scenario.EndDate -
|
||
Math.max($scope.Calendar.StartDate, currentProject.Scenario.StartDate);
|
||
|
||
var remaining = layoutFillingRemaining[j] - projectCovers;
|
||
if (remaining < minRemaining) {
|
||
minRemaining = remaining;
|
||
optimalX = j;
|
||
optimalY = 0;
|
||
}
|
||
}
|
||
// check if the project can be placed in the end of the row
|
||
if (currentProjectNearestStartDate > lastProjectNearestEndDate) {
|
||
projectCovers = Math.min($scope.Calendar.EndDate, currentProject.Scenario.EndDate) -
|
||
currentProject.Scenario.StartDate;
|
||
|
||
var remaining = layoutFillingRemaining[j] - projectCovers;
|
||
if (remaining < minRemaining) {
|
||
minRemaining = remaining;
|
||
optimalX = j;
|
||
optimalY = layout[j].length;
|
||
}
|
||
}
|
||
|
||
for (var z = 0; z < layout[j].length - 1; z++) {
|
||
var prevProject = $scope.Calendar.Projects[layout[j][z].ProjectId];
|
||
var nextProject = $scope.Calendar.Projects[layout[j][z + 1].ProjectId];
|
||
|
||
var prevProjectNearestEndDate = getNearestDate(prevProject.Scenario.EndDate),
|
||
nextProjectNearestStartDate = getNearestDate(nextProject.Scenario.StartDate);
|
||
|
||
// check if the project can be placed between 2 neighboring projects in the row
|
||
if (currentProjectNearestStartDate > prevProjectNearestEndDate &&
|
||
currentProjectNearestEndDate < nextProjectNearestStartDate) {
|
||
projectCovers = currentProject.Scenario.EndDate - currentProject.Scenario.StartDate;
|
||
|
||
var remaining = layoutFillingRemaining[j] - projectCovers;
|
||
if (remaining < minRemaining) {
|
||
minRemaining = remaining;
|
||
optimalX = j;
|
||
optimalY = z + 1;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if (optimalX < 0) {
|
||
layout.push([]);
|
||
layout[layout.length - 1].push(createLayoutRow(currentProject.Id));
|
||
|
||
var projectCovers = Math.min(currentProject.Scenario.EndDate, $scope.Calendar.EndDate) -
|
||
Math.max(currentProject.Scenario.StartDate, $scope.Calendar.StartDate);
|
||
|
||
layoutFillingRemaining.push([]);
|
||
layoutFillingRemaining[layoutFillingRemaining.length - 1] = $scope.Calendar.EndDate - $scope.Calendar.StartDate - projectCovers;
|
||
}
|
||
else {
|
||
if (!layout[optimalX])
|
||
layout[optimalX] = [];
|
||
|
||
if (optimalY < 0) {
|
||
layout[optimalX].push(createLayoutRow(currentProject.Id));
|
||
}
|
||
else {
|
||
layout[optimalX].splice(optimalY, 0, createLayoutRow(currentProject.Id));
|
||
}
|
||
|
||
layoutFillingRemaining[optimalX] -= projectCovers;
|
||
}
|
||
}
|
||
|
||
return layout;
|
||
}
|
||
|
||
function getProjectAtWeekCell(week, mapRow) {
|
||
var foundProject = null;
|
||
|
||
if (!mapRow || (mapRow.length < 1))
|
||
return null;
|
||
|
||
for (var index = 0; index < mapRow.length; index++) {
|
||
if (!!mapRow[index].Scenario &&
|
||
(getNearestDate(mapRow[index].Scenario.StartDate) <= week.Milliseconds) &&
|
||
(getNearestDate(mapRow[index].Scenario.EndDate) >= week.Milliseconds)) {
|
||
foundProject = mapRow[index];
|
||
break;
|
||
}
|
||
}
|
||
|
||
return foundProject;
|
||
};
|
||
|
||
function getProjectAtMonthCell(month, mapRow) {
|
||
var foundProject = null;
|
||
|
||
if (!month || !month.Childs || month.Childs.length <= 0 || !mapRow || (mapRow.length < 1))
|
||
return null;
|
||
|
||
for (var wIndex = month.Childs.length - 1; wIndex >= 0; wIndex--) {
|
||
var week = $scope.Calendar.Header.Weeks[month.Childs[wIndex]];
|
||
foundProject = getProjectAtWeekCell(week, mapRow);
|
||
if (!!foundProject)
|
||
break;
|
||
}
|
||
|
||
return foundProject;
|
||
};
|
||
|
||
function addProjectToLayout(projectId, teamId, index, insertBefore) {
|
||
if (!projectId || !teamId || (index < 0))
|
||
return;
|
||
|
||
if (!$scope.GridLayout) {
|
||
$scope.GridLayout = {};
|
||
}
|
||
|
||
if (!$scope.GridLayout[teamId]) {
|
||
$scope.GridLayout[teamId] = [];
|
||
}
|
||
|
||
var currentLayout = $scope.GridLayout[teamId];
|
||
var projectRow = createLayoutRow(projectId);
|
||
|
||
if (index >= currentLayout.length) {
|
||
// New index is out of bounds. Adding project in the end of the map
|
||
currentLayout.push([projectRow]);
|
||
} else {
|
||
// in this case we just put single project to new row
|
||
if (insertBefore) {
|
||
currentLayout.splice(index, 0, [projectRow]);
|
||
}
|
||
else {
|
||
// otherwise put project in existence row
|
||
currentLayout[index].push(projectRow);
|
||
}
|
||
}
|
||
};
|
||
|
||
// SA. ENV-1114
|
||
function removeProjectFromLayout(projectId) {
|
||
if (!projectId || !$scope.GridLayout) {
|
||
return;
|
||
}
|
||
|
||
for (var teamId in $scope.GridLayout) {
|
||
removeProjectFromTeamLayout(projectId, teamId);
|
||
}
|
||
};
|
||
|
||
function removeProjectFromTeamLayout(projectId, teamId) {
|
||
if (!$scope.GridLayout || !$scope.GridLayout[teamId] || ($scope.GridLayout[teamId].length < 1)) {
|
||
return;
|
||
}
|
||
|
||
var found = false;
|
||
var currentLayout = $scope.GridLayout[teamId];
|
||
|
||
for (var rowIndex = 0; rowIndex < currentLayout.length; rowIndex++) {
|
||
for (var itemIndex = 0; itemIndex < currentLayout[rowIndex].length; itemIndex++) {
|
||
if (currentLayout[rowIndex][itemIndex].ProjectId == projectId) {
|
||
currentLayout[rowIndex].splice(itemIndex, 1);
|
||
found = true;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (found) {
|
||
if (currentLayout[rowIndex].length < 1) {
|
||
currentLayout.splice(rowIndex, 1);
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
};
|
||
|
||
$scope.updateProjectInLayout = function (projectId, teamId, sourceRow, targetRow, insertBefore) {
|
||
if (!$scope.GridLayout || !$scope.GridLayout[teamId] || ($scope.GridLayout[teamId].length < 1))
|
||
return;
|
||
|
||
if ((sourceRow === undefined) || (sourceRow < 0) || (targetRow === undefined) || (targetRow < 0) ||
|
||
(sourceRow >= $scope.GridLayout[teamId].length))
|
||
return;
|
||
|
||
if ((sourceRow == targetRow) && !insertBefore)
|
||
// No need to update map
|
||
return;
|
||
|
||
// Remove project from source row
|
||
var currentLayout = $scope.GridLayout[teamId];
|
||
var sourceRowMap = currentLayout[sourceRow];
|
||
var targetRowCorrected = targetRow;
|
||
|
||
for (var index = 0; index < sourceRowMap.length; index++) {
|
||
if (sourceRowMap[index].ProjectId == projectId) {
|
||
sourceRowMap.splice(index, 1);
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (sourceRowMap.length < 1) {
|
||
currentLayout.splice(sourceRow, 1);
|
||
|
||
if (targetRow > sourceRow) {
|
||
targetRowCorrected--;
|
||
}
|
||
}
|
||
|
||
// Add project to team map as new position
|
||
addProjectToLayout(projectId, teamId, targetRowCorrected, insertBefore);
|
||
};
|
||
|
||
function changeProjectRange(project, shiftX, callbackFn) {
|
||
if (!project || !project.Scenario || shiftX == 0) {
|
||
if (!!callbackFn && typeof callbackFn === 'function') {
|
||
callbackFn();
|
||
}
|
||
return;
|
||
}
|
||
|
||
var nearestStartDate = getNearestDate(project.Scenario.StartDate);
|
||
var nearestEndDate = getNearestDate(project.Scenario.EndDate);
|
||
var shiftedStartDate = getNewDate(nearestStartDate, shiftX);
|
||
var shiftedEndDate = getNewDate(nearestEndDate, shiftX);
|
||
|
||
if (!shiftedStartDate) {
|
||
if (!$scope.Calendar.FiscalCalendarWeekEndings || $scope.Calendar.FiscalCalendarWeekEndings.length <= 0) {
|
||
bootbox.alert('Fiscal Calendar is incorrect.');
|
||
} else {
|
||
var startDate = new Date($scope.Calendar.FiscalCalendarWeekEndings[0]);
|
||
var startDateStr = (startDate.getMonth() + 1) + '/' + startDate.getDate() + '/' + startDate.getFullYear();
|
||
bootbox.alert('Financial Calendar starts on ' + startDateStr + '. You cannot move scenario so it exceeds the date range of Financial Calendar.');
|
||
}
|
||
return;
|
||
}
|
||
|
||
if (!shiftedEndDate) {
|
||
if (!$scope.Calendar.FiscalCalendarWeekEndings || $scope.Calendar.FiscalCalendarWeekEndings.length <= 0) {
|
||
bootbox.alert('Fiscal Calendar is incorrect.');
|
||
} else {
|
||
var endDate = new Date($scope.Calendar.FiscalCalendarWeekEndings[$scope.Calendar.FiscalCalendarWeekEndings.length - 1]);
|
||
var endDateStr = (endDate.getMonth() + 1) + '/' + endDate.getDate() + '/' + endDate.getFullYear();
|
||
bootbox.alert('Financial Calendar ends on ' + endDateStr + '. You cannot move scenario so it exceeds the date range of Financial Calendar.');
|
||
}
|
||
return;
|
||
}
|
||
|
||
if (project.Deadline > 0 && shiftedEndDate > project.Deadline) {
|
||
var deadline = new Date(project.Deadline);
|
||
var deadlineStr = (deadline.getMonth() + 1) + '/' + deadline.getDate() + '/' + deadline.getFullYear();
|
||
bootbox.alert('Scenario End Date should not exceed Project Deadline date on ' + deadlineStr);
|
||
return;
|
||
}
|
||
|
||
project.Scenario.StartDate += (shiftedStartDate - nearestStartDate);
|
||
project.Scenario.EndDate += (shiftedEndDate - nearestEndDate);
|
||
|
||
if (!isScenarioLoaded(project.Scenario)) {
|
||
loadScenarioDetails(project.Scenario.Id, project.Id, function (expenditures) {
|
||
setScenarioExpenditures(project.Scenario, expenditures);
|
||
shiftProjectData(project, shiftX, callbackFn);
|
||
});
|
||
}
|
||
else {
|
||
shiftProjectData(project, shiftX, callbackFn);
|
||
}
|
||
}
|
||
|
||
function shiftProjectData(project, shiftX, callbackFn) {
|
||
var shiftedData = shiftScenarioDetails(project.Scenario, shiftX);
|
||
if (!shiftedData) {
|
||
return;
|
||
}
|
||
|
||
refreshProjectData(project, shiftedData);
|
||
if (!!callbackFn && typeof callbackFn === 'function') {
|
||
callbackFn(project);
|
||
}
|
||
}
|
||
|
||
function shiftScenarioDetails(scenario, shiftX) {
|
||
if (!scenario || !scenario.Expenditures || !shiftX) {
|
||
return null;
|
||
}
|
||
|
||
var shiftedData = {};
|
||
for (var expCatId in scenario.Expenditures) {
|
||
var category = scenario.Expenditures[expCatId];
|
||
|
||
shiftedData[expCatId] = {
|
||
Details: {},
|
||
Teams: {}
|
||
};
|
||
if (!!category.Details) {
|
||
shiftedData[expCatId].Details = shiftDetailsArray(category.Details, shiftX);
|
||
}
|
||
|
||
if (!category.Teams) {
|
||
continue;
|
||
}
|
||
|
||
for (var teamId in category.Teams) {
|
||
var team = category.Teams[teamId];
|
||
shiftedData[expCatId].Teams[teamId] = {
|
||
QuantityValues: {},
|
||
Resources: {},
|
||
AllResources: {}
|
||
};
|
||
|
||
if (!team.QuantityValues) {
|
||
continue;
|
||
}
|
||
|
||
shiftedData[expCatId].Teams[teamId].QuantityValues = shiftDetailsArray(team.QuantityValues, shiftX);
|
||
|
||
if (!!team.Resources) {
|
||
for (var resourceId in team.Resources) {
|
||
var resource = team.Resources[resourceId];
|
||
shiftedData[expCatId].Teams[teamId].Resources[resourceId] = {
|
||
QuantityValues: {},
|
||
};
|
||
if (!resource.QuantityValues) {
|
||
continue;
|
||
}
|
||
|
||
shiftedData[expCatId].Teams[teamId].Resources[resourceId].QuantityValues = shiftDetailsArray(resource.QuantityValues, shiftX);
|
||
}
|
||
}
|
||
|
||
if (!!team.AllResources) {
|
||
for (var resourceId in team.AllResources) {
|
||
var resource = team.AllResources[resourceId];
|
||
shiftedData[expCatId].Teams[teamId].AllResources[resourceId] = {
|
||
QuantityValues: {},
|
||
};
|
||
if (!resource.QuantityValues) {
|
||
continue;
|
||
}
|
||
|
||
shiftedData[expCatId].Teams[teamId].AllResources[resourceId].QuantityValues = shiftDetailsArray(resource.QuantityValues, shiftX);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
return shiftedData;
|
||
};
|
||
function copyQuantityValues(expCatId, targetTeam, sourceTeam) {
|
||
if (!targetTeam || !sourceTeam)
|
||
return;
|
||
|
||
compareValuesAndTriggerChanges(targetTeam.QuantityValues, sourceTeam.QuantityValues, targetTeam.Id, expCatId, null);
|
||
targetTeam.QuantityValues = sourceTeam.QuantityValues;
|
||
if (!!sourceTeam.Resources && !!targetTeam.Resources) {
|
||
for (var resourceId in sourceTeam.Resources) {
|
||
// create new resource if it has not added yet
|
||
if (!targetTeam.Resources[resourceId]) {
|
||
targetTeam.Resources[resourceId] = angular.extend({}, sourceTeam.Resources[resourceId], { QuantityValues: {} });
|
||
}
|
||
|
||
compareValuesAndTriggerChanges(targetTeam.Resources[resourceId].QuantityValues, sourceTeam.Resources[resourceId].QuantityValues, targetTeam.Id, expCatId, resourceId);
|
||
targetTeam.Resources[resourceId].QuantityValues = sourceTeam.Resources[resourceId].QuantityValues;
|
||
}
|
||
// delete resources from target team if they have deleted from source team
|
||
for (var resourceId in targetTeam.Resources) {
|
||
if (!sourceTeam.Resources[resourceId] || sourceTeam.Resources[resourceId].Deleted === true) {
|
||
delete targetTeam.Resources[resourceId];
|
||
}
|
||
}
|
||
}
|
||
|
||
if (!!sourceTeam.AllResources && !!targetTeam.AllResources) {
|
||
for (var resourceId in sourceTeam.AllResources) {
|
||
if (!targetTeam.AllResources[resourceId]) {
|
||
targetTeam.AllResources[resourceId] = angular.copy(sourceTeam.AllResources[resourceId]);
|
||
} else {
|
||
targetTeam.AllResources[resourceId].QuantityValues = sourceTeam.AllResources[resourceId].QuantityValues;
|
||
}
|
||
}
|
||
}
|
||
};
|
||
function compareValuesAndTriggerChanges(oldQuantityValues, newQuantityValues, teamId, expCatId, resourceId) {
|
||
if ((!oldQuantityValues && !newQuantityValues) || !teamId || !expCatId)
|
||
return;
|
||
|
||
if (!oldQuantityValues)
|
||
oldQuantityValues = {};
|
||
|
||
if (!newQuantityValues)
|
||
newQuantityValues = {};
|
||
|
||
for (var mIndex = 0; mIndex < $scope.Calendar.Header.Months.length; mIndex++) {
|
||
var month = $scope.Calendar.Header.Months[mIndex];
|
||
for (var wIndex = 0; wIndex < month.Childs.length; wIndex++) {
|
||
var week = $scope.Calendar.Header.Weeks[month.Childs[wIndex]];
|
||
var oldValue = oldQuantityValues[week.Milliseconds] || 0;
|
||
var newValue = newQuantityValues[week.Milliseconds] || 0;
|
||
if (newValue === oldValue)
|
||
continue;
|
||
|
||
if (!resourceId)
|
||
expenditureCategoryValueChanged(teamId, expCatId, month.Childs[wIndex], week.Milliseconds, (newValue - oldValue));
|
||
else
|
||
resourceValueChanged(teamId, expCatId, resourceId, month.Childs[wIndex], week.Milliseconds, (newValue - oldValue));
|
||
}
|
||
}
|
||
};
|
||
|
||
// SA. Set doProjectCleaning = true to remove from project expenditures the teams,
|
||
// which not exist in data.
|
||
function refreshProjectData(project, data, doProjectCleaning) {
|
||
if (!project || !project.Scenario || !data) {
|
||
return;
|
||
}
|
||
|
||
for (var expCatId in data) {
|
||
if (!project.Scenario.Expenditures[expCatId])
|
||
project.Scenario.Expenditures[expCatId] = angular.extend({}, data[expCatId], { Teams: {} });
|
||
|
||
project.Scenario.Expenditures[expCatId].Details = data[expCatId].Details || {};
|
||
|
||
if (!data[expCatId].Teams)
|
||
data[expCatId].Teams = {};
|
||
|
||
// Currently existing teams in DATA and Project
|
||
var currentExpCatDataTeams = Object.keys(data[expCatId].Teams);
|
||
var currentExpCatProjectTeams = Object.keys(project.Scenario.Expenditures[expCatId].Teams);
|
||
// Teams, which exist in DATA, but not exist in Project
|
||
var newExpCatProjectTeams = $.grep(currentExpCatDataTeams, function (teamId, index) {
|
||
return ($.inArray(teamId, currentExpCatProjectTeams) < 0)
|
||
});
|
||
|
||
$.each(newExpCatProjectTeams, function (index, teamId) {
|
||
var emptyTeam = {
|
||
AllResources: {},
|
||
Resources: {},
|
||
QuantityValues: {},
|
||
CapacityQuantityValues: {},
|
||
RestQuantityValues: {}
|
||
};
|
||
project.Scenario.Expenditures[expCatId].Teams[teamId] = angular.extend({}, data[expCatId].Teams[teamId], emptyTeam);
|
||
|
||
var sourceTeam = data[expCatId].Teams[teamId];
|
||
var targetTeam = project.Scenario.Expenditures[expCatId].Teams[teamId];
|
||
copyQuantityValues(expCatId, targetTeam, sourceTeam);
|
||
});
|
||
|
||
$.each(currentExpCatDataTeams, function (index, teamId) {
|
||
var sourceTeam = data[expCatId].Teams[teamId];
|
||
var targetTeam = project.Scenario.Expenditures[expCatId].Teams[teamId];
|
||
copyQuantityValues(expCatId, targetTeam, sourceTeam);
|
||
});
|
||
|
||
if (doProjectCleaning) {
|
||
var absentInDataProjectTeams = $.grep(currentExpCatProjectTeams, function (teamId, index) {
|
||
return ($.inArray(teamId, currentExpCatDataTeams) < 0);
|
||
});
|
||
|
||
$.each(absentInDataProjectTeams, function (index, teamId) {
|
||
// SA. Turn to zero Project team allocations to update bottom part of the Calendar
|
||
var projectTeam = project.Scenario.Expenditures[expCatId].Teams[teamId];
|
||
var teamKiller = createZeroAllocationsTeamFromTeam(projectTeam);
|
||
|
||
copyQuantityValues(expCatId, projectTeam, teamKiller);
|
||
delete project.Scenario.Expenditures[expCatId].Teams[teamId];
|
||
});
|
||
}
|
||
|
||
// SA. ENV-1254. Copy anassigned allocations for current EC from DATA to Project
|
||
project.Scenario.Expenditures[expCatId].UnassignedAllocations = angular.copy(data[expCatId].UnassignedAllocations);
|
||
}
|
||
};
|
||
|
||
// SA. ENV-1210. Return the copy for specified team with all allocations turned to zero
|
||
function createZeroAllocationsTeamFromTeam(team) {
|
||
if (!team)
|
||
return;
|
||
|
||
var teamKiller = angular.copy(team);
|
||
|
||
if (teamKiller.QuantityValues) {
|
||
var weekendings = Object.keys(teamKiller.QuantityValues);
|
||
$.each(weekendings, function (index, we) {
|
||
teamKiller.QuantityValues[we] = 0;
|
||
});
|
||
}
|
||
|
||
if (teamKiller.Resources) {
|
||
var resourceKeys = Object.keys(teamKiller.Resources);
|
||
$.each(resourceKeys, function (index, resId) {
|
||
var currentRes = teamKiller.Resources[resId];
|
||
|
||
if (currentRes.QuantityValues) {
|
||
var weekendings = Object.keys(currentRes.QuantityValues);
|
||
$.each(weekendings, function (index, we) {
|
||
currentRes.QuantityValues[we] = 0;
|
||
});
|
||
}
|
||
});
|
||
}
|
||
|
||
teamKiller.AllResources = {};
|
||
return teamKiller;
|
||
}
|
||
|
||
function triggerValuesChanged4NewScenario(scenario) {
|
||
if (!scenario || !scenario.Expenditures) {
|
||
return;
|
||
}
|
||
|
||
for (var expCatId in scenario.Expenditures) {
|
||
if (!scenario.Expenditures[expCatId].Teams || Object.keys(scenario.Expenditures[expCatId].Teams).length <= 0) {
|
||
continue;
|
||
}
|
||
|
||
for (var teamId in scenario.Expenditures[expCatId].Teams) {
|
||
var sourceTeam = scenario.Expenditures[expCatId].Teams[teamId];
|
||
if (!sourceTeam.QuantityValues)
|
||
continue;
|
||
|
||
compareValuesAndTriggerChanges({}, sourceTeam.QuantityValues, teamId, expCatId);
|
||
if (!sourceTeam.Resources)
|
||
continue;
|
||
|
||
for (var resourceId in sourceTeam.Resources) {
|
||
if (!sourceTeam.Resources[resourceId].QuantityValues)
|
||
continue;
|
||
|
||
compareValuesAndTriggerChanges({}, sourceTeam.Resources[resourceId].QuantityValues, teamId, expCatId, resourceId);
|
||
}
|
||
}
|
||
}
|
||
};
|
||
function shiftDetailsArray(detailsObject, shiftX) {
|
||
if (!detailsObject)
|
||
return;
|
||
|
||
var shiftedTeamQuantities = {};
|
||
for (var weekEnding in detailsObject) {
|
||
var shifted = getNewDate(weekEnding, shiftX);
|
||
if (!shifted) {
|
||
throw 'It is unavailable to shift date from [' + weekEnding + '] on [' + shiftX + '] positions';
|
||
}
|
||
|
||
shiftedTeamQuantities[shifted] = detailsObject[weekEnding];
|
||
}
|
||
|
||
return shiftedTeamQuantities;
|
||
}
|
||
|
||
$scope.moveProjectInsideTeam = function (projectId, team, shiftX, sourceRow, targetRow, insertBefore) {
|
||
if (!projectId)
|
||
return;
|
||
|
||
shiftX = shiftX || 0;
|
||
var project = $scope.getProjectById(projectId);
|
||
|
||
if (!project || !project.Scenario)
|
||
return;
|
||
|
||
changeProjectRange(project, shiftX, function () {
|
||
|
||
var rowToInsertIn = targetRow;
|
||
|
||
// Check for projects intersection within row
|
||
if (!insertBefore && checkProjectsIntersection(project, team.Id, targetRow)) {
|
||
rowToInsertIn++;
|
||
insertBefore = true;
|
||
}
|
||
|
||
if (sourceRow != rowToInsertIn) {
|
||
$scope.updateProjectInLayout(projectId, team.Id, sourceRow, rowToInsertIn, insertBefore);
|
||
}
|
||
project = $scope.getProjectById(projectId);
|
||
|
||
// Move copies of the project in other teams
|
||
synchronizeProjectInLayoutTeamBlocks(project, team.Id);
|
||
$scope.recreateView();
|
||
|
||
});
|
||
};
|
||
|
||
function addTeamToProject(projectId, teamId, shiftX, targetRow, insertBefore, callbackFn) {
|
||
if (!projectId)
|
||
return;
|
||
|
||
shiftX = shiftX || 0;
|
||
var project = $scope.getProjectById(projectId);
|
||
if (!project || !project.Scenario)
|
||
return;
|
||
|
||
if ($scope.projectHasTeam(project, teamId))
|
||
// Project already has the team
|
||
return;
|
||
|
||
// shift project if necessary
|
||
if (shiftX != 0) {
|
||
changeProjectRange(project, shiftX, function () {
|
||
addTeamToProjectInternal(project, teamId, targetRow, insertBefore, shiftX);
|
||
if (!!callbackFn && typeof callbackFn === 'function') {
|
||
callbackFn();
|
||
}
|
||
});
|
||
}
|
||
else {
|
||
if (!isScenarioLoaded(project.Scenario)) {
|
||
loadScenarioDetails(project.Scenario.Id, project.Id, function (expenditures) {
|
||
setScenarioExpenditures(project.Scenario, expenditures);
|
||
addTeamToProjectInternal(project, teamId, targetRow, insertBefore, shiftX);
|
||
if (!!callbackFn && typeof callbackFn === 'function') {
|
||
callbackFn();
|
||
}
|
||
});
|
||
}
|
||
else {
|
||
addTeamToProjectInternal(project, teamId, targetRow, insertBefore, shiftX);
|
||
if (!!callbackFn && typeof callbackFn === 'function') {
|
||
callbackFn();
|
||
}
|
||
}
|
||
}
|
||
};
|
||
|
||
function addTeamToProjectInternal(project, teamId, targetRow, insertBefore, shiftX) {
|
||
if (!project || !project.Teams || !teamId) {
|
||
return;
|
||
}
|
||
|
||
// Add team to project
|
||
project.Teams.push(teamId);
|
||
fillExpenditures4TeamById(project, teamId);
|
||
var rowToInsertIn = targetRow;
|
||
|
||
// Check for projects intersection within row
|
||
if (!insertBefore && checkProjectsIntersection(project, teamId, targetRow)) {
|
||
rowToInsertIn++;
|
||
insertBefore = true;
|
||
}
|
||
|
||
addProjectToLayout(project.Id, teamId, rowToInsertIn, insertBefore);
|
||
|
||
if (shiftX != 0) {
|
||
synchronizeProjectInLayoutTeamBlocks(project, teamId);
|
||
}
|
||
}
|
||
|
||
function synchronizeProjectInLayoutTeamBlocks(project, excludeTeam) {
|
||
if (!project || !project.Teams || !project.Teams.length <= 0)
|
||
return;
|
||
|
||
for (var teamIndex = 0; teamIndex < project.Teams.length; teamIndex++) {
|
||
if (project.Teams[teamIndex] == excludeTeam)
|
||
continue;
|
||
|
||
if (!teamInfoService.isExists(project.Teams[teamIndex]))
|
||
continue;
|
||
|
||
var position = findProjectPositionInLayout(project.Id, $scope.GridLayout[project.Teams[teamIndex]]);
|
||
if (!position)
|
||
continue;
|
||
|
||
var insertBefore = false;
|
||
var newRowIndex = position.RowIndex;
|
||
|
||
// Check for projects intersection within row
|
||
if (checkProjectsIntersection(project, project.Teams[teamIndex], position.RowIndex)) {
|
||
newRowIndex++;
|
||
insertBefore = true;
|
||
}
|
||
|
||
if (position.RowIndex != newRowIndex) {
|
||
$scope.updateProjectInLayout(project.Id, project.Teams[teamIndex], position.RowIndex, newRowIndex, insertBefore);
|
||
}
|
||
}
|
||
}
|
||
|
||
// SA. ENV-1254. Refactored for reusage
|
||
function fillExpenditures4TeamById(project, teamId) {
|
||
if (!project || !project.Scenario || !teamId) {
|
||
return;
|
||
}
|
||
|
||
var team = teamInfoService.getById(teamId);
|
||
if (!team || !team.ExpCategories) {
|
||
return;
|
||
}
|
||
|
||
fillExpenditures4Team(project, team);
|
||
}
|
||
|
||
// SA. ENV-1254. Refactored for reusage
|
||
function fillExpenditures4Team(project, team) {
|
||
if (!project || !project.Scenario) {
|
||
return;
|
||
}
|
||
|
||
if (!team || !team.ExpCategories) {
|
||
return;
|
||
}
|
||
|
||
if (!project.Scenario.Expenditures)
|
||
project.Scenario.Expenditures = {};
|
||
|
||
var startDateIndex = binarySearch($scope.Calendar.FiscalCalendarWeekEndings, project.Scenario.StartDate, 0, $scope.Calendar.FiscalCalendarWeekEndings.length, true);
|
||
var endDateIndex = binarySearch($scope.Calendar.FiscalCalendarWeekEndings, project.Scenario.EndDate, 0, $scope.Calendar.FiscalCalendarWeekEndings.length, true);
|
||
|
||
if (startDateIndex < 0 || endDateIndex < 0 ||
|
||
startDateIndex >= $scope.Calendar.FiscalCalendarWeekEndings.length ||
|
||
endDateIndex >= $scope.Calendar.FiscalCalendarWeekEndings.length) {
|
||
throw 'Invalid start [' + startDateIndex + '] or end [' + endDateIndex + '] indexes';
|
||
}
|
||
|
||
for (var expCatId in project.Scenario.Expenditures) {
|
||
if (!team.ExpCategories[expCatId]) {
|
||
continue;
|
||
}
|
||
|
||
// if scenario already has information for this category and this team we need to skip it
|
||
var ecInScenario = project.Scenario.Expenditures[expCatId];
|
||
if (!!ecInScenario.Teams[team.Id])
|
||
continue;
|
||
|
||
ecInScenario.Teams[team.Id] = {
|
||
Id: team.Id,
|
||
QuantityValues: {},
|
||
AllResources: {},
|
||
CanBeDeleted: false, // SA. 1210
|
||
Changed: false, // SA. 1210
|
||
Collapsed: true, // SA. 1210
|
||
CollapsedClass: "fa-plus-square", // SA. 1210
|
||
IsAccessable: true, // SA. 1210
|
||
Name: team.Name, // SA. 1210
|
||
CapacityQuantityValues: {}, // SA. 1210
|
||
Resources: {}, // SA. 1210
|
||
RestQuantityValues: {} // SA. 1210
|
||
};
|
||
|
||
if (!team.ExpCategories[expCatId].Resources) {
|
||
continue;
|
||
}
|
||
|
||
for (var resourceId in team.ExpCategories[expCatId].Resources) {
|
||
var resource = team.ExpCategories[expCatId].Resources[resourceId];
|
||
ecInScenario.Teams[team.Id].AllResources[resourceId] = {
|
||
Id: resourceId,
|
||
Name: resource.Name,
|
||
QuantityValues: {}
|
||
};
|
||
}
|
||
|
||
for (var i = startDateIndex; i <= endDateIndex; i++) {
|
||
var date = $scope.Calendar.FiscalCalendarWeekEndings[i];
|
||
ecInScenario.Teams[team.Id].QuantityValues[date] = 0;
|
||
// ecInScenario.Teams[teamId].RestQuantityValues[date] = 0; // SA. ENV-1210
|
||
|
||
for (var resourceId in team.ExpCategories[expCatId].Resources) {
|
||
ecInScenario.Teams[team.Id].AllResources[resourceId].QuantityValues[date] = 0;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
function removeTeamFromScenario(project, teamId) {
|
||
if (!project || !project.Scenario || !teamId) {
|
||
return;
|
||
}
|
||
|
||
var team = teamInfoService.getById(teamId);
|
||
if (!team || !team.ExpCategories) {
|
||
return;
|
||
}
|
||
|
||
if (!project.Scenario.Expenditures) {
|
||
return;
|
||
}
|
||
|
||
// SA. ENV-1210. Remove team allocations from deleted categories
|
||
var performProjectRefresh = false;
|
||
var updatedData = angular.copy(project.Scenario.Expenditures);
|
||
var expCatKeys = Object.keys(project.Scenario.Expenditures);
|
||
|
||
$.each(expCatKeys, function (index, expCatId) {
|
||
var currentExpCat = project.Scenario.Expenditures[expCatId];
|
||
|
||
if (currentExpCat.Teams && currentExpCat.Teams[teamId]) {
|
||
delete updatedData[expCatId].Teams[teamId];
|
||
performProjectRefresh = true;
|
||
}
|
||
});
|
||
|
||
if (performProjectRefresh) {
|
||
refreshProjectData(project, updatedData, true);
|
||
}
|
||
}
|
||
|
||
function removeTeamFromProjectInternal(projectId, teamId) {
|
||
var project = $scope.Calendar.Projects[projectId];
|
||
if (!project || !project.Teams)
|
||
return;
|
||
|
||
for (var teamIndex = 0; teamIndex < project.Teams.length; teamIndex++) {
|
||
if (project.Teams[teamIndex] == teamId) {
|
||
project.Teams.splice(teamIndex, 1);
|
||
break;
|
||
}
|
||
}
|
||
|
||
removeTeamFromScenario(project, teamId);
|
||
|
||
if (projectHasNoTeams(projectId)) {
|
||
// SA. ENV-1114. Completelly remove the project from layout (from all teams in the calendar)
|
||
removeProjectFromLayout(project.Id);
|
||
removeProjectFromManaged(project.Id);
|
||
// SA. ENV-1218. Remove items from Projects with Unassigned Expenditures block
|
||
removeProjectFromUnassignedExpenditures(project.Id);
|
||
$scope.pushProjectToUnscheduled(project);
|
||
}
|
||
else {
|
||
// Project has other teams after the given team was removed
|
||
removeProjectFromTeamLayout(projectId, teamId);
|
||
}
|
||
|
||
// SA. ENV-1153
|
||
setDataChanged(true);
|
||
};
|
||
|
||
// SA. ENV-1114. Checks the project has any team, displayed in the calendar.
|
||
// It may be possible, the project has any team, but they are not in the calendar.
|
||
// Treat this as project has no teams
|
||
function projectHasNoTeams(projectId) {
|
||
if (!projectId || (projectId.length < 1))
|
||
return true;
|
||
|
||
var project = $scope.getProjectById(projectId);
|
||
|
||
if (!project || !project.Teams || (project.Teams.length < 1)) {
|
||
// Project has no teams
|
||
return true;
|
||
}
|
||
|
||
var projectHasCalendarTeam = false;
|
||
for (var index = 0; index < $scope.Calendar.Teams.length; index++) {
|
||
var team = $scope.Calendar.Teams[index];
|
||
|
||
if ($scope.projectHasTeam(project, team.Id)) {
|
||
projectHasCalendarTeam = true;
|
||
break;
|
||
}
|
||
}
|
||
|
||
return !projectHasCalendarTeam;
|
||
};
|
||
|
||
function addProjectFromUnscheduled(projectId, targetTeamId, targetRow) {
|
||
if (!projectId || !targetTeamId || targetRow < 0)
|
||
return;
|
||
|
||
var project = $scope.Calendar.Projects[projectId];
|
||
if (!project)
|
||
return;
|
||
|
||
if (!project.Teams)
|
||
throw 'Teams is undefined for project ' + projectId;
|
||
|
||
// we need to link current team with the project if it didn't yet
|
||
if (!$scope.projectHasTeam(project, targetTeamId))
|
||
project.Teams.push(targetTeamId);
|
||
|
||
pushProjectToManaged(project);
|
||
removeProjectFromUnscheduled(project.Id);
|
||
addProjectToLayout(project.Id, targetTeamId, targetRow, true);
|
||
|
||
project.Teams = union(project.Teams, getTeamsInScenario(project.Scenario));
|
||
for (var i = 0; i < project.Teams.length; i++) {
|
||
var teamId = project.Teams[i];
|
||
if (teamId === targetTeamId)
|
||
continue;
|
||
|
||
var layout = $scope.GridLayout[project.Teams[i]];
|
||
if (!layout)
|
||
continue;
|
||
|
||
addProjectToLayout(project.Id, teamId, layout.length, true);
|
||
}
|
||
};
|
||
|
||
function getTeamsInScenario(scenario) {
|
||
if (!scenario || !scenario.Expenditures)
|
||
return [];
|
||
|
||
var teams = [];
|
||
for (var expCatId in scenario.Expenditures) {
|
||
var category = scenario.Expenditures[expCatId];
|
||
if (!category.Teams)
|
||
continue;
|
||
|
||
for (var teamId in category.Teams) {
|
||
if (teams.indexOf(teamId) < 0)
|
||
teams.push(teamId);
|
||
}
|
||
}
|
||
|
||
return teams;
|
||
};
|
||
|
||
function checkProjectsIntersection(project, teamId, rowIndex) {
|
||
if (!project || !teamId || (rowIndex < 0) || !$scope.GridLayout || !$scope.GridLayout[teamId] ||
|
||
(rowIndex >= $scope.GridLayout[teamId].length)) {
|
||
return false;
|
||
}
|
||
|
||
var currentLayout = $scope.GridLayout[teamId];
|
||
var rowMap = currentLayout[rowIndex];
|
||
var projectNearestStartDate = getNearestDate(project.Scenario.StartDate),
|
||
projectNearestEndDate = getNearestDate(project.Scenario.EndDate);
|
||
|
||
for (var itemIndex = 0; itemIndex < rowMap.length; itemIndex++) {
|
||
if (rowMap[itemIndex].ProjectId != project.Id) {
|
||
var checkableProject = $scope.getProjectById(rowMap[itemIndex].ProjectId);
|
||
// return true if dates intersect
|
||
if (projectNearestStartDate <= getNearestDate(checkableProject.Scenario.EndDate) &&
|
||
projectNearestEndDate >= getNearestDate(checkableProject.Scenario.StartDate)) {
|
||
return true;
|
||
}
|
||
}
|
||
}
|
||
return false;
|
||
};
|
||
|
||
$scope.addProjectsFromQueue = function () {
|
||
if ($scope.data.Projects2Add == null || $scope.data.Projects2Add.length < 0) {
|
||
$scope.data.Projects2Add = [];
|
||
return;
|
||
}
|
||
|
||
$.each($scope.data.Projects2Add, function (index, item) {
|
||
var project = $scope.Calendar.Projects[item.Id];
|
||
if (!project)
|
||
return;
|
||
|
||
$scope.moveProjectFromQueue2Unscheduled(project);
|
||
});
|
||
|
||
$scope.data.Projects2Add = [];
|
||
$('#selProjects2Add').select2('val', '');
|
||
setDataChanged(true);
|
||
};
|
||
|
||
$scope.moveProjectFromQueue2Unscheduled = function (project) {
|
||
//validate params
|
||
if (project == null || project.Id == null)
|
||
return;
|
||
|
||
// do not add existing project
|
||
for (var i = 0; i < $scope.Calendar.UnscheduledProjects.length; i++) {
|
||
if ($scope.Calendar.UnscheduledProjects[i].Id == project.Id) {
|
||
return;
|
||
}
|
||
}
|
||
|
||
$scope.pushProjectToUnscheduled(project);
|
||
|
||
// Remove it from queue
|
||
var index2Remove = -1;
|
||
for (var i = 0; i < $scope.Calendar.Queue4UnscheduledProjects.length; i++) {
|
||
if ($scope.Calendar.Queue4UnscheduledProjects[i].Id == project.Id) {
|
||
index2Remove = i;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (index2Remove >= 0)
|
||
$scope.Calendar.Queue4UnscheduledProjects.splice(index2Remove, 1);
|
||
};
|
||
|
||
function removeProjectFromUnscheduled(projectId) {
|
||
if (!projectId || (projectId.length < 1))
|
||
return;
|
||
|
||
var index2Remove = -1;
|
||
for (var i = 0; i < $scope.Calendar.UnscheduledProjects.length; i++) {
|
||
if ($scope.Calendar.UnscheduledProjects[i].Id == projectId) {
|
||
index2Remove = i;
|
||
break;
|
||
}
|
||
}
|
||
if (index2Remove >= 0)
|
||
$scope.Calendar.UnscheduledProjects.splice(index2Remove, 1);
|
||
};
|
||
|
||
function removeProjectFromManaged(projectId) {
|
||
if (!projectId || (projectId.length < 1))
|
||
return;
|
||
|
||
var index2Remove = -1;
|
||
for (var i = 0; i < $scope.Calendar.ManagedProjects.length; i++) {
|
||
if ($scope.Calendar.ManagedProjects[i].Id == projectId) {
|
||
index2Remove = i;
|
||
break;
|
||
}
|
||
}
|
||
if (index2Remove >= 0)
|
||
$scope.Calendar.ManagedProjects.splice(index2Remove, 1);
|
||
};
|
||
|
||
$scope.projectHasTeam = function (project, teamId) {
|
||
return project.Teams.indexOf(teamId) >= 0;
|
||
};
|
||
|
||
$scope.getProjectById = function (projectId) {
|
||
return $scope.Calendar.Projects[projectId];
|
||
};
|
||
|
||
function createCell(id, name, cssClass, cssStyle, isFirstCell, overStart, overEnd, isProjectLastCell, pinned) {
|
||
var cell = {
|
||
Id: id,
|
||
Title: name,
|
||
CssClass: cssClass,
|
||
CssStyle: cssStyle,
|
||
IsFirstCell: isFirstCell,
|
||
OverStart: overStart,
|
||
OverEnd: overEnd,
|
||
IsProjectLastCell: isProjectLastCell,
|
||
Pinned: pinned
|
||
};
|
||
if (isFirstCell) {
|
||
cell.Name = name;
|
||
cell.CssClass += " first-cell";
|
||
}
|
||
return cell;
|
||
}
|
||
|
||
function createBlankCell() {
|
||
return {
|
||
Id: "",
|
||
CssStyle: ''
|
||
};
|
||
};
|
||
|
||
$scope.createDummyAllocationCell = function () {
|
||
var allocationCell = {
|
||
Id: "",
|
||
CssStyle: '',
|
||
IsDummyCell: true
|
||
};
|
||
|
||
return allocationCell;
|
||
};
|
||
|
||
$scope.pushProjectToUnscheduled = function (project) {
|
||
var css = getProjectCSS(project);
|
||
var projData = {
|
||
Id: project.Id,
|
||
Name: project.Name,
|
||
CssStyle: css
|
||
};
|
||
$scope.Calendar.UnscheduledProjects.push(projData);
|
||
};
|
||
|
||
// SA. ENV-1210.
|
||
function isProjectInUnassignedExpenditures(projectId, expCatId) {
|
||
return $scope.Calendar.UnassignedExpendituresProjects.some(function (data) {
|
||
return (data.ProjectId === projectId) && (data.ExpCatId === expCatId);
|
||
});
|
||
};
|
||
|
||
// SA. ENV-1210
|
||
$scope.pushProjectToUnassignedExpenditures = function (project, expCatIdArray, targetTeamId) {
|
||
if (!project || !project.Scenario || !project.Scenario.Expenditures || !expCatIdArray)
|
||
return;
|
||
|
||
$.each(expCatIdArray, function (index, expCatId) {
|
||
if (!isProjectInUnassignedExpenditures(project.Id, expCatId)) {
|
||
// Add project to Unassigned Expenditures Projects block
|
||
var expCatName = "";
|
||
|
||
if (project.Scenario.Expenditures[expCatId]) {
|
||
// Get expenditure categgory name from inside of the project
|
||
expCatName = project.Scenario.Expenditures[expCatId].ExpenditureCategoryName;
|
||
|
||
var css = getProjectCSS(project);
|
||
var projData = {
|
||
ProjectId: project.Id,
|
||
ExpCatId: expCatId,
|
||
TargetTeamId: targetTeamId,
|
||
Name: expCatName + "; " + project.Name,
|
||
CssStyle: css
|
||
};
|
||
$scope.Calendar.UnassignedExpendituresProjects.push(projData);
|
||
}
|
||
}
|
||
else {
|
||
// Update current (target) team in existing record of Unassigned Exp Projects
|
||
updateProjectFromUnassignedExpenditures(project.Id, expCatId, targetTeamId);
|
||
}
|
||
});
|
||
|
||
$scope.UnassignedExpendituresProjectsExist = $scope.Calendar.UnassignedExpendituresProjects.length > 0;
|
||
};
|
||
|
||
// SA. ENV-1254
|
||
function updateProjectFromUnassignedExpenditures(projectId, expCatId, newTargetTeamId) {
|
||
if (!projectId || !expCatId || !$scope.UnassignedExpendituresProjectsExist)
|
||
return;
|
||
|
||
var itemIndex = -1;
|
||
|
||
$.each($scope.Calendar.UnassignedExpendituresProjects, function (index, item) {
|
||
if ((item.ProjectId == projectId) && (item.ExpCatId == expCatId)) {
|
||
itemIndex = index;
|
||
}
|
||
});
|
||
|
||
if (itemIndex >= 0) {
|
||
$scope.Calendar.UnassignedExpendituresProjects[itemIndex].TargetTeamId = newTargetTeamId;
|
||
}
|
||
};
|
||
|
||
// SA. ENV-1254. If expCatId is undefined - removes all items for specified project
|
||
function removeProjectFromUnassignedExpenditures(projectId, expCatId) {
|
||
if (!projectId || !$scope.UnassignedExpendituresProjectsExist)
|
||
return;
|
||
|
||
for (var index = $scope.Calendar.UnassignedExpendituresProjects.length - 1; index >= 0; index--) {
|
||
var item = $scope.Calendar.UnassignedExpendituresProjects[index];
|
||
|
||
if ((item.ProjectId == projectId) && (!expCatId || (item.ExpCatId == expCatId)))
|
||
$scope.Calendar.UnassignedExpendituresProjects.splice(index, 1);
|
||
}
|
||
|
||
$scope.UnassignedExpendituresProjectsExist = $scope.Calendar.UnassignedExpendituresProjects.length > 0;
|
||
};
|
||
|
||
$scope.pushProjectToQueue = function (project, insertAtTheTop) {
|
||
var projData = {
|
||
Id: project.Id,
|
||
Name: project.Name,
|
||
Color: project.Color
|
||
}
|
||
if (insertAtTheTop) {
|
||
$scope.Calendar.Queue4UnscheduledProjects.unshift(projData);
|
||
}
|
||
else {
|
||
$scope.Calendar.Queue4UnscheduledProjects.push(projData);
|
||
}
|
||
};
|
||
|
||
function isProjectInManaged(projectId) {
|
||
return $scope.Calendar.ManagedProjects.some(function (project) {
|
||
return project.Id === projectId;
|
||
});
|
||
};
|
||
|
||
function pushProjectToManaged(project) {
|
||
var projData = {
|
||
Id: project.Id,
|
||
Name: project.Name,
|
||
Teams: project.Teams
|
||
};
|
||
|
||
$scope.Calendar.ManagedProjects.push(projData);
|
||
};
|
||
|
||
$scope.prepareUnscheduledToDisplay = function () {
|
||
if ($scope.Calendar && $scope.Calendar.UnscheduledProjects && $scope.Calendar.UnscheduledProjects.length) {
|
||
for (var index = 0; index < $scope.Calendar.UnscheduledProjects.length; index++) {
|
||
var project = $scope.getProjectById($scope.Calendar.UnscheduledProjects[index]);
|
||
var css = getProjectCSS(project);
|
||
|
||
var projData = {
|
||
Id: project.Id,
|
||
Name: project.Name,
|
||
CssStyle: css
|
||
};
|
||
|
||
$scope.Calendar.UnscheduledProjects[index] = projData;
|
||
}
|
||
}
|
||
|
||
// SA. ENV-1210
|
||
$scope.UnscheduledProjectsExist =
|
||
$scope.Calendar && $scope.Calendar.UnscheduledProjects &&
|
||
($scope.Calendar.UnscheduledProjects.length > 0);
|
||
};
|
||
|
||
$scope.prepareQueuedToDisplay = function () {
|
||
if (!$scope.Calendar || !$scope.Calendar.Queue4UnscheduledProjects)
|
||
return;
|
||
|
||
for (var index = 0; index < $scope.Calendar.Queue4UnscheduledProjects.length; index++) {
|
||
var project = $scope.getProjectById($scope.Calendar.Queue4UnscheduledProjects[index]);
|
||
|
||
var projData = {
|
||
Id: project.Id,
|
||
Name: project.Name,
|
||
Color: project.Color
|
||
};
|
||
|
||
$scope.Calendar.Queue4UnscheduledProjects[index] = projData;
|
||
}
|
||
};
|
||
|
||
// SA. ENV-1210
|
||
$scope.prepareUnassignedEcsProjectsToDisplay = function () {
|
||
if ($scope.Calendar && $scope.Calendar.UnassignedExpendituresProjects &&
|
||
$scope.Calendar.UnassignedExpendituresProjects.length) {
|
||
for (var index = 0; index < $scope.Calendar.UnassignedExpendituresProjects.length; index++) {
|
||
var currentItem = $scope.Calendar.UnassignedExpendituresProjects[index];
|
||
var project = $scope.getProjectById(currentItem.ProjectId);
|
||
var css = getProjectCSS(project);
|
||
|
||
var projData = {
|
||
ProjectId: currentItem.ProjectId,
|
||
ExpCatId: currentItem.ExpCatId,
|
||
TargetTeamId: currentItem.TargetTeamId,
|
||
Name: currentItem.Name,
|
||
CssStyle: css
|
||
};
|
||
|
||
$scope.Calendar.UnassignedExpendituresProjects[index] = projData;
|
||
}
|
||
}
|
||
|
||
$scope.UnassignedExpendituresProjectsExist =
|
||
$scope.Calendar && $scope.Calendar.UnassignedExpendituresProjects &&
|
||
($scope.Calendar.UnassignedExpendituresProjects.length > 0);
|
||
};
|
||
|
||
$scope.prepareManagedToDisplay = function () {
|
||
if (!$scope.Calendar || !$scope.Calendar.ManagedProjects)
|
||
return;
|
||
|
||
for (var index = 0; index < $scope.Calendar.ManagedProjects.length; index++) {
|
||
var project = $scope.getProjectById($scope.Calendar.ManagedProjects[index]);
|
||
|
||
var projData = {
|
||
Id: project.Id,
|
||
Name: project.Name,
|
||
Teams: project.Teams
|
||
};
|
||
|
||
$scope.Calendar.ManagedProjects[index] = projData;
|
||
}
|
||
};
|
||
|
||
// helper functions
|
||
function getProjectCSS(project) {
|
||
var colorRgb = project.ColorRGB || '';
|
||
var css = '';
|
||
|
||
if (colorRgb.length > 0) {
|
||
css += 'background-color: rgba(' + colorRgb + ', 0.8);';
|
||
//css += 'border-right-color: rgba(' + colorRgb + ', 0) !important;';
|
||
css += 'border: 1px solid #a0a0a0 !important;';
|
||
}
|
||
//if (!project.Pinned)
|
||
// css += 'cursor: pointer;';
|
||
return css;
|
||
};
|
||
|
||
//function drawBorder(team, rowIndex, colIndex) {
|
||
|
||
// if (!team || rowIndex < 0 || colIndex < 0)
|
||
// return;
|
||
|
||
// var projectRow = team.Allocations[rowIndex];
|
||
// if (!projectRow || !projectRow.Cells)
|
||
// return;
|
||
|
||
// var projectCell = projectRow.Cells[colIndex];
|
||
// if (!projectCell || !projectCell.Id)
|
||
// return;
|
||
|
||
// var projectNextCell = projectRow.Cells[colIndex + 1];
|
||
// var projectBelowCell = null;
|
||
// if (team.Allocations.length > (rowIndex + 1))
|
||
// projectBelowCell = team.Allocations[rowIndex + 1].Cells[colIndex];
|
||
|
||
// var borderStyle = '1px solid black !important;';
|
||
|
||
// // top border always draws
|
||
// var topBorder = 'border-top: ' + borderStyle,
|
||
// bottomBorder = '', rightBorder = '', leftBorder = '';
|
||
|
||
// // left border draws only if it is first project cell
|
||
// if (projectCell.IsFirstCell === true) {
|
||
// leftBorder = 'border-left: ' + borderStyle;
|
||
// }
|
||
|
||
// // right border draws only if it is last cell of project and next cell doesn't exist or is empty
|
||
// if (projectCell.IsFirstCell === true && (!projectNextCell || !projectNextCell.Id)) { //IsProjectLastCell
|
||
// rightBorder = 'border-right: ' + borderStyle;
|
||
// }
|
||
|
||
// // bottom border draws if below cell doesn't exist or is empty
|
||
// if ((!projectBelowCell || !projectBelowCell.Id)) {
|
||
// bottomBorder = 'border-bottom: ' + borderStyle;
|
||
// }
|
||
|
||
// projectCell.CssStyle += (topBorder + bottomBorder + leftBorder + rightBorder);
|
||
//};
|
||
|
||
function getProjectCssClass(project) {
|
||
var cssClass = 'headcol';
|
||
if ((team.Drop.Row == aIndex) && (!team.Drop.Mod) && team.Drop.Cells[Calendar.Header.Weeks[$index].Milliseconds]) {
|
||
cssClass += ' droppable-active';
|
||
}
|
||
}
|
||
|
||
$scope.onMouseMove = function (e, team) {
|
||
var elementAtMouse = $scope.getElementFromPoint(e.clientX, e.clientY);
|
||
var cellElement = elementAtMouse;
|
||
|
||
if (elementAtMouse.nodeName.toUpperCase() != "TD") {
|
||
var parentTds = $(elementAtMouse).closest("td");
|
||
if (parentTds.length > 0) {
|
||
cellElement = parentTds[0];
|
||
}
|
||
}
|
||
|
||
if (cellElement) {
|
||
var viewportRect = document.body.getBoundingClientRect();
|
||
var elementRect = cellElement.getBoundingClientRect();
|
||
|
||
var elementHeight = elementRect.bottom - elementRect.top;
|
||
var elementAbsTop = elementRect.top - viewportRect.top;
|
||
var elementAbsBottom = elementAbsTop + elementHeight;
|
||
|
||
var elementMouseRelativeTop = e.pageY - elementAbsTop;
|
||
|
||
if ((elementMouseRelativeTop / elementHeight) < 0.4) {
|
||
team.Drop.Mod = "before";
|
||
} else {
|
||
if ((elementMouseRelativeTop / elementHeight) > 0.8) {
|
||
team.Drop.Mod = "after";
|
||
} else {
|
||
team.Drop.Mod = undefined;
|
||
}
|
||
}
|
||
}
|
||
};
|
||
|
||
$scope.getElementFromPoint = function (x, y) {
|
||
var check = false, isRelative = true;
|
||
|
||
if (!document.elementFromPoint) return null;
|
||
|
||
if (!check) {
|
||
var sl;
|
||
if ((sl = $(document).scrollTop()) > 0) {
|
||
isRelative = (document.elementFromPoint(0, sl + $(window).height() - 1) == null);
|
||
} else if ((sl = $(document).scrollLeft()) > 0) {
|
||
isRelative = (document.elementFromPoint(sl + $(window).width() - 1, 0) == null);
|
||
}
|
||
check = (sl > 0);
|
||
}
|
||
|
||
if (!isRelative) {
|
||
x += $(document).scrollLeft();
|
||
y += $(document).scrollTop();
|
||
}
|
||
|
||
return document.elementFromPoint(x, y);
|
||
};
|
||
|
||
/* Mix loading and saving */
|
||
$scope.createSaveDataPackage = function (dataPackage) {
|
||
dataPackage.Calendar = {};
|
||
dataPackage.Calendar.Projects = {};
|
||
dataPackage.Calendar.ManagedProjects = [];
|
||
dataPackage.Calendar.UnscheduledProjects = [];
|
||
dataPackage.Calendar.QueuedProjects = [];
|
||
dataPackage.Calendar.UnassignedExpendituresProjects = []; // SA. ENV-1210
|
||
dataPackage.Calendar.Layout = [];
|
||
dataPackage.Calendar.Teams = [];
|
||
|
||
// Saving managed projects
|
||
for (var pIndex = 0; pIndex < $scope.Calendar.ManagedProjects.length; pIndex++) {
|
||
var currentProject = $scope.Calendar.ManagedProjects[pIndex];
|
||
dataPackage.Calendar.ManagedProjects.push(currentProject.Id);
|
||
}
|
||
|
||
// Saving unscheduled projects
|
||
for (var pIndex = 0; pIndex < $scope.Calendar.UnscheduledProjects.length; pIndex++) {
|
||
var currentProject = $scope.Calendar.UnscheduledProjects[pIndex];
|
||
dataPackage.Calendar.UnscheduledProjects.push(currentProject.Id);
|
||
}
|
||
|
||
// Saving queued projects
|
||
for (var pIndex = 0; pIndex < $scope.Calendar.Queue4UnscheduledProjects.length; pIndex++) {
|
||
var currentProject = $scope.Calendar.Queue4UnscheduledProjects[pIndex];
|
||
dataPackage.Calendar.QueuedProjects.push(currentProject.Id);
|
||
}
|
||
|
||
// SA. ENV-1210. Saving unassigned expenditures projects
|
||
for (var pIndex = 0; pIndex < $scope.Calendar.UnassignedExpendituresProjects.length; pIndex++) {
|
||
var dataItem = $scope.Calendar.UnassignedExpendituresProjects[pIndex];
|
||
var item2push = {
|
||
ProjectId: dataItem.ProjectId,
|
||
ExpCatId: dataItem.ExpCatId,
|
||
SourceTeamId: dataItem.SourceTeamId,
|
||
TargetTeamId: dataItem.TargetTeamId,
|
||
Name: dataItem.Name
|
||
};
|
||
|
||
dataPackage.Calendar.UnassignedExpendituresProjects.push(item2push);
|
||
}
|
||
|
||
// Saving scenario dates and attached teams for Managed projects
|
||
for (var projectId in $scope.Calendar.Projects) {
|
||
dataPackage.Calendar.Projects[projectId] = $scope.getProjectById(projectId);
|
||
}
|
||
|
||
// Saving grid layout
|
||
dataPackage.Calendar.Layout = $scope.getLayoutForServer($scope.GridLayout);
|
||
|
||
// Saving teams
|
||
var teams = teamInfoService.getAll();
|
||
if (!!teams) {
|
||
angular.forEach(teams, function (currentTeam, teamId) {
|
||
var pc = [];
|
||
|
||
for (var expCatId in currentTeam.ExpCategories) {
|
||
var expCatItem = currentTeam.ExpCategories[expCatId];
|
||
|
||
var nr = {
|
||
ExpCatId: expCatItem.Id,
|
||
Values: expCatItem.PlannedCapacityValues
|
||
}
|
||
pc.push(nr);
|
||
}
|
||
|
||
var team = {
|
||
Id: currentTeam.Id,
|
||
Name: currentTeam.Name,
|
||
IsNew: currentTeam.IsNew,
|
||
CompanyId: currentTeam.CompanyId,
|
||
UserId: currentTeam.UserId,
|
||
CostCenterId: currentTeam.CostCenterId,
|
||
ExpCategories: currentTeam.ExpCategories,
|
||
PlannedCapacity: pc
|
||
};
|
||
dataPackage.Calendar.Teams.push(team);
|
||
});
|
||
}
|
||
};
|
||
|
||
$scope.fixFilterForSave = function (filter) {
|
||
if (filter && filter.Selection && filter.Selection.TeamsViews) {
|
||
for (var index = 0; index < filter.Selection.TeamsViews.length; index++) {
|
||
var currentItem = filter.Selection.TeamsViews[index];
|
||
|
||
if (!currentItem.Data || (currentItem.Data == null)) {
|
||
currentItem.Data = [];
|
||
}
|
||
}
|
||
}
|
||
};
|
||
|
||
$scope.getLayoutForClient = function (serverLayout) {
|
||
var clientLayout = {};
|
||
|
||
if (!serverLayout || (serverLayout.length < 1)) {
|
||
return clientLayout;
|
||
}
|
||
|
||
for (var tIndex = 0; tIndex < $scope.Calendar.Teams.length; tIndex++) {
|
||
var teamId = $scope.Calendar.Teams[tIndex].Id;
|
||
clientLayout[teamId] = [];
|
||
|
||
var teamLayoutRecords = $scope.getTeamLayoutRecords(serverLayout, teamId);
|
||
var rowIndex = -1;
|
||
|
||
for (var rIndex = 0; rIndex < teamLayoutRecords.length; rIndex++) {
|
||
var rec = teamLayoutRecords[rIndex];
|
||
var layoutItem = {
|
||
ProjectId: rec.ProjectId
|
||
}
|
||
|
||
if (rec.Row != rowIndex) {
|
||
clientLayout[teamId].push([]);
|
||
rowIndex = rec.Row;
|
||
}
|
||
|
||
clientLayout[teamId][rowIndex].push(layoutItem);
|
||
}
|
||
}
|
||
|
||
return clientLayout;
|
||
};
|
||
|
||
$scope.getTeamLayoutRecords = function (serverLayout, teamId) {
|
||
var teamRecords = [];
|
||
var sortedRecords = [];
|
||
|
||
for (var index = 0; index < serverLayout.length; index++) {
|
||
if (serverLayout[index].TeamId == teamId) {
|
||
teamRecords.push(serverLayout[index]);
|
||
}
|
||
}
|
||
|
||
if (teamRecords.length < 1) {
|
||
return teamRecords;
|
||
}
|
||
|
||
sortedRecords = teamRecords.sort(function (a, b) {
|
||
if ((a.Row < b.Row) || ((a.Row == b.Row) && (a.Index < b.Index))) {
|
||
return -1;
|
||
}
|
||
|
||
if ((a.Row > b.Row) || ((a.Row == b.Row) && (a.Index > b.Index))) {
|
||
return 1;
|
||
}
|
||
|
||
return 0;
|
||
});
|
||
|
||
return sortedRecords;
|
||
};
|
||
|
||
$scope.getLayoutForServer = function (layout) {
|
||
var result = [];
|
||
|
||
for (var teamId in layout) {
|
||
var currentTeam = layout[teamId];
|
||
|
||
for (var rowIndex = 0; rowIndex < currentTeam.length; rowIndex++) {
|
||
for (var projIndex = 0; projIndex < currentTeam[rowIndex].length; projIndex++) {
|
||
var layoutItem = angular.copy(currentTeam[rowIndex][projIndex]);
|
||
layoutItem.Row = rowIndex;
|
||
layoutItem.Index = projIndex;
|
||
layoutItem.TeamId = teamId;
|
||
|
||
result.push(layoutItem);
|
||
}
|
||
}
|
||
}
|
||
|
||
return result;
|
||
};
|
||
|
||
$scope.setStartDnD = function ($event) {
|
||
$scope.clickShift = $event.offsetX;
|
||
}
|
||
|
||
$scope.removeProject = function ($event, teamId, projectId) {
|
||
bootbox.confirm("Are you sure you want to remove this project from the team?", function (result) {
|
||
$scope.$apply(function () {
|
||
if (result) {
|
||
removeTeamFromProjectInternal(projectId, teamId);
|
||
$scope.recreateView();
|
||
|
||
}
|
||
});
|
||
});
|
||
};
|
||
|
||
function loadScenarioDetails(scenarioId, projectId, successCallbackFn) {
|
||
blockUI();
|
||
var requestData = {
|
||
MixId: $scope.MixId,
|
||
ScenarioId: scenarioId
|
||
};
|
||
var request = getAntiXSRFRequest($scope.dataUrls.getScenarioDetailsUrl, requestData);
|
||
try {
|
||
$http(request).success(function (expenditures, status, headers, config) {
|
||
try {
|
||
|
||
if (!expenditures) {
|
||
unblockUI();
|
||
return;
|
||
}
|
||
|
||
if (angular.isFunction(successCallbackFn)) {
|
||
successCallbackFn(expenditures);
|
||
}
|
||
|
||
unblockUI();
|
||
} catch (e) {
|
||
unblockUI();
|
||
showErrorModal('Oops!', commonErrorMessage);
|
||
}
|
||
}).error(function (data, status, headers, config) {
|
||
unblockUI();
|
||
showErrorModal('Oops!', commonErrorMessage);
|
||
});
|
||
} catch (e) {
|
||
unblockUI();
|
||
showErrorModal('Oops!', commonErrorMessage);
|
||
}
|
||
};
|
||
function setScenarioExpenditures(scenario, expenditures) {
|
||
scenario.Expenditures = expenditures || {};
|
||
};
|
||
function createLayoutRow(id) {
|
||
return {
|
||
ProjectId: id
|
||
};
|
||
};
|
||
|
||
/* Events Triggers Start */
|
||
// Fires event after EC cell value has been changed
|
||
function expenditureCategoryValueChanged(teamId, expCatId, weekIndex, weekEndingMs, deltaValue) {
|
||
teamInfoService.changeExpenditureCategoryValue(teamId, expCatId, weekEndingMs, deltaValue);
|
||
$scope.$broadcast('expenditureCategoryValueChanged', {
|
||
TeamId: teamId,
|
||
ExpenditureCategoryId: expCatId,
|
||
WeekIndex: weekIndex
|
||
});
|
||
setDataChanged(true);
|
||
}
|
||
|
||
// Fires event after resource cell value has been changed
|
||
function resourceValueChanged(teamId, expCatId, resourceId, weekIndex, weekEndingMs, deltaValue) {
|
||
teamInfoService.changeResourceValue(teamId, expCatId, resourceId, weekEndingMs, deltaValue);
|
||
$scope.$broadcast('resourceValueChanged', {
|
||
TeamId: teamId,
|
||
ExpenditureCategoryId: expCatId,
|
||
ResourceId: resourceId,
|
||
WeekIndex: weekIndex
|
||
});
|
||
setDataChanged(true);
|
||
}
|
||
/* Events Triggers End */
|
||
|
||
$scope.createScenario = function (model, createScenarioModel) {
|
||
if (!model || !model.Scenario || !model.Calendar || !createScenarioModel || !createScenarioModel.ProjectId || !createScenarioModel.TargetTeam)
|
||
return;
|
||
|
||
// SA. ENV-1159
|
||
var promptToChangeView = (model.Scenario.EndDate < $scope.Calendar.StartDate) ||
|
||
(model.Scenario.StartDate > $scope.Calendar.EndDate);
|
||
|
||
var project = $scope.getProjectById(createScenarioModel.ProjectId);
|
||
project.Scenario = {
|
||
CGSplit: model.Scenario.CGSplit,
|
||
CostSavings: model.Scenario.CostSavings,
|
||
Duration: model.Scenario.Duration,
|
||
EFXSplit: model.Scenario.EFXSplit,
|
||
StartDate: model.Scenario.StartDate,
|
||
EndDate: model.Scenario.EndDate,
|
||
Expenditures: angular.copy(model.Calendar.Expenditures),
|
||
GrossMargin: model.Scenario.GrossMargin,
|
||
GrowthScenario: model.Scenario.GrowthScenario,
|
||
Id: Math.uuid(), // we need to create new GUID for new scenario because mongo will have many allocations and scenarios with ScenarioId == Guid.Empty
|
||
LMMargin: model.Scenario.LMMargin,
|
||
ParentId: model.Scenario.ParentId,
|
||
ProjectedRevenue: model.Scenario.ProjectedRevenue,
|
||
TDDirectCosts: model.Scenario.TDDirectCosts,
|
||
TemplateId: model.Scenario.TemplateId,
|
||
Type: model.Scenario.Type,
|
||
UseLMMargin: model.Scenario.UseLMMargin,
|
||
IsNew: true
|
||
};
|
||
|
||
// SA. ENV-1153.
|
||
setDataChanged(true);
|
||
|
||
if (!promptToChangeView) {
|
||
$scope.$apply(function () {
|
||
// Project fit mix dates and should be displayed in the Calendar
|
||
blockUI();
|
||
try {
|
||
// some interface logic for project displaying
|
||
if (!isProjectInManaged(createScenarioModel.ProjectId)) {
|
||
addProjectFromUnscheduled(createScenarioModel.ProjectId, createScenarioModel.TargetTeam.Id, createScenarioModel.TargetRow || 1);
|
||
}
|
||
$scope.recreateView();
|
||
|
||
triggerValuesChanged4NewScenario(project.Scenario);
|
||
|
||
// we need to add available teams which exists in the project but not exists in the scenario with zero-allocations
|
||
for (var i = 0; i < project.Teams.length; i++) {
|
||
fillExpenditures4TeamById(project, project.Teams[i]);
|
||
}
|
||
}
|
||
catch (e) {
|
||
unblockUI();
|
||
ShowErrorMessage(commonErrorMessage);
|
||
}
|
||
finally {
|
||
unblockUI();
|
||
}
|
||
});
|
||
}
|
||
else {
|
||
// Move the project to Queued section, because now it has a scenario
|
||
removeProjectFromUnscheduled(project.Id);
|
||
$scope.pushProjectToQueue(project, true);
|
||
|
||
// Project dates are out of the mix dates
|
||
var extendedViewDates = getExtendedViewDates(model.Scenario.StartDate, model.Scenario.EndDate,
|
||
$scope.Calendar.StartDate, $scope.Calendar.EndDate);
|
||
var promptText = getChangeViewPrompt(extendedViewDates.StartDate, extendedViewDates.EndDate);
|
||
|
||
$timeout(function () {
|
||
bootbox.dialog({
|
||
message: promptText,
|
||
// title: "Custom title",
|
||
buttons: {
|
||
success: {
|
||
label: "OK",
|
||
className: "btn-success",
|
||
callback: function () {
|
||
// Go expanding current view
|
||
$rootScope.$broadcast("changeDisplayView", extendedViewDates.StartDate, extendedViewDates.EndDate);
|
||
}
|
||
},
|
||
cancel: {
|
||
label: "Keep Current Date Range",
|
||
className: "btn-primary",
|
||
callback: function () {
|
||
// current view not changed
|
||
}
|
||
}
|
||
}
|
||
});
|
||
|
||
});
|
||
}
|
||
};
|
||
|
||
$scope.editPinProject = function (cell, id, teamid, $event, state) {
|
||
cell.Pinned = state;
|
||
var project = $scope.getProjectById(id);
|
||
project.Pinned = state;
|
||
$("#menu_dd_" + id + "_" + teamid).removeClass("open"); //removeClass("in").addClass("collapse"); ////addClass("collapse"); //css({'display': 'none'});
|
||
setDataChanged(true);
|
||
getProjectCSS(project);
|
||
|
||
}
|
||
|
||
$scope.editScenarioDetails = function (projectId, $event, pinned) {
|
||
if (!!$event && angular.isFunction($event.preventDefault))
|
||
$event.preventDefault();
|
||
|
||
if (!projectId)
|
||
return;
|
||
|
||
var project = $scope.getProjectById(projectId);
|
||
if (!project || !project.Scenario)
|
||
return;
|
||
|
||
if (isScenarioLoaded(project.Scenario)) {
|
||
loadScenarioDetailsEditForm(project.Scenario, pinned);
|
||
}
|
||
else {
|
||
loadScenarioDetails(project.Scenario.Id, projectId, function (expenditures) {
|
||
setScenarioExpenditures(project.Scenario, expenditures);
|
||
loadScenarioDetailsEditForm(project.Scenario, pinned);
|
||
});
|
||
}
|
||
};
|
||
function loadScenarioDetailsEditForm(scenario, pinned) {
|
||
if (!scenario)
|
||
return;
|
||
|
||
blockUI();
|
||
|
||
// we should send scenario w/o expenditures and cost savings for reducing a traffic (these properties do not use at this action method)
|
||
var requestData = angular.extend({}, scenario, { Pinned: pinned, Expenditures: null, CostSavings: null });
|
||
var request = getAntiXSRFRequest($scope.dataUrls.editScenarioDetailsUrl, requestData);
|
||
try {
|
||
$http(request).success(function (content, status, headers, config) {
|
||
try {
|
||
if (!content) {
|
||
unblockUI();
|
||
return;
|
||
}
|
||
|
||
// we should copy in the scenario information that does not store in the mix: capacity and rest capacity
|
||
// we need this information for edit scenario details form
|
||
copyToScenarioTeamInformation(scenario);
|
||
|
||
var $html = angular.element('<div class="row" data-section="scenarioCalendar">' + content + '</div>');
|
||
// we should destroy dynamic scopes before DOM elements will be destroyed
|
||
$html.on('$destroy', function (event) {
|
||
var controllers = angular.element(event.currentTarget).find('[ng-controller]');
|
||
for (var i = 0; i < controllers.length; i++) {
|
||
if (angular.isElement(controllers[i])) {
|
||
var scope = angular.element(controllers[i]).scope();
|
||
if (!!scope)
|
||
scope.$destroy();
|
||
}
|
||
}
|
||
});
|
||
var $element = $compile($html)($scope);
|
||
var data = {
|
||
Expenditures: scenario.Expenditures,
|
||
Rates: scenario.Rates
|
||
};
|
||
$scope.$broadcast('changeScenarioDetails', data);
|
||
$document.trigger('rmo.open-scenario-details-window', { html: $element });
|
||
unblockUI();
|
||
} catch (e) {
|
||
unblockUI();
|
||
showErrorModal('Oops!', commonErrorMessage);
|
||
}
|
||
}).error(function (data, status, headers, config) {
|
||
unblockUI();
|
||
showErrorModal('Oops!', commonErrorMessage);
|
||
});
|
||
} catch (e) {
|
||
unblockUI();
|
||
showErrorModal('Oops!', commonErrorMessage);
|
||
}
|
||
};
|
||
|
||
$scope.editScenarioTeams = function (projectId, $event) {
|
||
if (!!$event && angular.isFunction($event.preventDefault))
|
||
$event.preventDefault();
|
||
|
||
if (!projectId)
|
||
return;
|
||
|
||
var project = $scope.getProjectById(projectId);
|
||
if (!project || !project.Scenario)
|
||
return;
|
||
if (isScenarioLoaded(project.Scenario)) {
|
||
fillEditTeamsForm(project);
|
||
}
|
||
else {
|
||
loadScenarioDetails(project.Scenario.Id, projectId, function (expenditures) {
|
||
setScenarioExpenditures(project.Scenario, expenditures);
|
||
fillEditTeamsForm(project);
|
||
});
|
||
}
|
||
};
|
||
$scope.cancelAddExpCatsAndTeams = function() {
|
||
$scope.AddTeamForm.ProjectId = null;
|
||
$scope.AddTeamForm.ProjectExpCats2Add = null;
|
||
$scope.AddTeamForm.ProjectInTeams2Add = null;
|
||
$scope.AddTeamForm.ProjectOutTeams2Add = null;
|
||
$scope.AddTeamForm.AvailableProjectExpCats2Add = [];
|
||
$scope.AddTeamForm.AvailableInTeams = [];
|
||
$scope.AddTeamForm.AvailableProjectOutTeams2Add = [];
|
||
}
|
||
function fillEditTeamsForm(project) {
|
||
$scope.AddTeamForm.ProjectId = project.Id;
|
||
$scope.AddTeamForm.ProjectExpCats2Add = null;
|
||
$scope.AddTeamForm.AvailableProjectExpCats2Add = [];
|
||
// get data source for expenditures dropdown
|
||
var expCats = dataSources.getExpenditures();
|
||
var scenarioExpCatKeys = Object.keys(project.Scenario.Expenditures);
|
||
angular.forEach(expCats, function (item, index) {
|
||
if ($.inArray(item.ExpenditureCategoryId, scenarioExpCatKeys) < 0)
|
||
$scope.AddTeamForm.AvailableProjectExpCats2Add.push({ Id: item.ExpenditureCategoryId, Name: item.ExpenditureCategoryName });
|
||
});
|
||
// get date source for In Teams dropdown
|
||
$scope.AddTeamForm.AvailableInTeams = [];
|
||
var allInTeam = teamInfoService.getAll();
|
||
var inTeamKeys = Object.keys(allInTeam);
|
||
angular.forEach(allInTeam, function (item, index) { // add each selected team to project
|
||
if ($.inArray(item.Id, project.Teams) < 0) // add only if it already does not exist in project
|
||
{
|
||
$scope.AddTeamForm.AvailableInTeams.push(item);
|
||
}
|
||
});
|
||
// get datasource for Out Teams dropdown
|
||
$scope.AddTeamForm.AvailableProjectOutTeams2Add = [];
|
||
angular.forEach($scope.data.AvailableTeams, function (item, index) { // add each selected team to project
|
||
if ($.inArray(item.Id, project.Teams) < 0 && $.inArray(item.Id, inTeamKeys) < 0) // add only if it already does not exist in project or in mix
|
||
{
|
||
$scope.AddTeamForm.AvailableProjectOutTeams2Add.push({ Id: item.Id, Name: item.TVName });
|
||
}
|
||
});
|
||
// notify system that it should open modal form
|
||
$document.trigger('rmo.open-scenario-teams-window');
|
||
}
|
||
$scope.addTeamsAndExpenditures2Project = function(isOpenDetails){
|
||
var project = $scope.getProjectById($scope.AddTeamForm.ProjectId);
|
||
// add expenditures
|
||
$scope.addProjectExpCats(project);
|
||
// add in Teams
|
||
$scope.addTeams2Project(project, function ()
|
||
{
|
||
//clear form
|
||
$scope.cancelAddExpCatsAndTeams();
|
||
if (typeof resetAddExpCatsDataChanged === 'function') {
|
||
resetAddExpCatsDataChanged();
|
||
}
|
||
if (isOpenDetails)
|
||
loadScenarioDetailsEditForm(project.Scenario, false);
|
||
$document.trigger('rmo.close-scenario-teams-window');
|
||
});
|
||
};
|
||
$scope.addProjectExpCats = function (project) {
|
||
var scenarioExpCatKeys = Object.keys(project.Scenario.Expenditures);
|
||
angular.forEach($scope.AddTeamForm.ProjectExpCats2Add, function (item, index) { // add each selected expenditure to project
|
||
if ($.inArray(item.Id, scenarioExpCatKeys) < 0) // add only if it already does nto exist in project
|
||
{
|
||
var expCat = $.extend({}, dataSources.getExpenditureById(item.Id)); // get general expenditure info from common datastore
|
||
if (expCat != null)
|
||
{
|
||
// expand general object with controller specific properties
|
||
expCat.Collapsed = true;
|
||
expCat.CollapsedClass = "fa-plus-square";
|
||
// setup Details array
|
||
var detailsRow = {};
|
||
var counter = 1;
|
||
var startDateIndex = binarySearch($scope.Calendar.FiscalCalendarWeekEndings, project.Scenario.StartDate, 0, $scope.Calendar.FiscalCalendarWeekEndings.length, true);
|
||
var endDateIndex = binarySearch($scope.Calendar.FiscalCalendarWeekEndings, project.Scenario.EndDate, 0, $scope.Calendar.FiscalCalendarWeekEndings.length, true);
|
||
|
||
if (startDateIndex < 0 || endDateIndex < 0 ||
|
||
startDateIndex >= $scope.Calendar.FiscalCalendarWeekEndings.length ||
|
||
endDateIndex >= $scope.Calendar.FiscalCalendarWeekEndings.length) {
|
||
throw 'Invalid start [' + startDateIndex + '] or end [' + endDateIndex + '] indexes';
|
||
}
|
||
|
||
for (var i = startDateIndex; i <= endDateIndex; i++) {
|
||
var millisec = $scope.Calendar.FiscalCalendarWeekEndings[i];
|
||
detailsRow[millisec] = {};
|
||
detailsRow[millisec].ActualsCost = null;
|
||
detailsRow[millisec].ActualsId = null;
|
||
detailsRow[millisec].ActualsQuantity = null;
|
||
detailsRow[millisec].Changed = false;
|
||
detailsRow[millisec].ForecastCost = 0;
|
||
detailsRow[millisec].ForecastId = null;
|
||
detailsRow[millisec].ForecastQuantity = 0;
|
||
detailsRow[millisec].WeekOrdinal = counter++;
|
||
}
|
||
expCat.Details = detailsRow;
|
||
|
||
//setup Teams array
|
||
var teams = {};
|
||
for (var teamIndex = 0; teamIndex < project.Teams.length; teamIndex++)
|
||
{
|
||
var commonTeam = teamInfoService.getById(project.Teams[teamIndex]); // get team from bottom part
|
||
var expCatKeys = Object.keys(commonTeam.ExpCategories);
|
||
for (var expCatIndex=0;expCatIndex < expCatKeys.length;expCatIndex++)
|
||
{
|
||
var expCatKey = expCatKeys[expCatIndex];
|
||
var expCatItem = commonTeam.ExpCategories[expCatKey];
|
||
if (expCatKey == expCat.ExpenditureCategoryId)
|
||
{
|
||
var team = {
|
||
Id: commonTeam.Id,
|
||
Name: commonTeam.Name,
|
||
AllResources: {},
|
||
Resources: {},
|
||
CanBeDeleted: false,
|
||
Changed: false,
|
||
Collapsed: true,
|
||
CollapsedClass: "fa-plus-square",
|
||
IsAccessible: false,
|
||
QuantityValues: {}
|
||
};
|
||
var resKeys = Object.keys(expCatItem.Resources);
|
||
for (var resIndex = 0; resIndex < resKeys.length; resIndex++)
|
||
{
|
||
var resKey = resKeys[resIndex];
|
||
var res = expCatItem.Resources[resKey];
|
||
team.AllResources[resKey] = {
|
||
Id: res.Id,
|
||
Name: res.Name,
|
||
Changed: false,
|
||
Deleted: false,
|
||
IsActiveEmployee: false
|
||
};
|
||
}
|
||
for (var i = startDateIndex; i <= endDateIndex; i++) {
|
||
var millisec = $scope.Calendar.FiscalCalendarWeekEndings[i];
|
||
team.QuantityValues[millisec] = 0;
|
||
}
|
||
teams[team.Id] = team;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
expCat.Teams = teams;
|
||
// add new expenditure record to scenario
|
||
project.Scenario.Expenditures[item.Id] = expCat;
|
||
}
|
||
}
|
||
});
|
||
};
|
||
$scope.addTeams2Project = function (project, fnCallback) {
|
||
angular.forEach($scope.AddTeamForm.ProjectInTeams2Add, function (item, index) { // add each selected team to project
|
||
if ($.inArray(item.Id, project.Teams) < 0) // add only if it already does nto exist in project
|
||
{
|
||
addTeamToProject(project.Id, item.Id, 0, $scope.GridLayout[item.Id].length, true);
|
||
}
|
||
});
|
||
var outTeamIds = [];
|
||
angular.forEach($scope.AddTeamForm.ProjectOutTeams2Add, function (item, index) { // add each selected team to project
|
||
if ($.inArray(item.Id, project.Teams) < 0) // add only if it already does nto exist in project
|
||
{
|
||
outTeamIds.push(item.Id);
|
||
}
|
||
});
|
||
// if no out teams then redraw layout
|
||
if (outTeamIds.length > 0) {
|
||
var scenarioExpCatKeys = Object.keys(project.Scenario.Expenditures);
|
||
loadTeamsData(outTeamIds, function (teams) {
|
||
angular.forEach(teams, function (team, teamIndex) {
|
||
// add new team with empty allocations for each expenditures
|
||
angular.forEach(scenarioExpCatKeys, function (expCatKey, expCatIndex) {
|
||
addTeamToProjectWithStructs(project.Id, expCatKey, team);
|
||
});
|
||
});
|
||
// add new team to filter, apply changes and fill allocations on callback
|
||
bootbox.alert('Selected Teams out of Current View must be added to current Mix. Press OK to reload Mix<br/>' +
|
||
'Unsaved Mix data will be preserved...', function () {
|
||
$scope.$apply(function () {
|
||
var startDateAsDate = new Date($scope.Calendar.StartDate);
|
||
var endDateAsDate = new Date($scope.Calendar.EndDate);
|
||
|
||
var startDateAsText = (startDateAsDate.getMonth() + 1) + '/' + startDateAsDate.getDate() + '/' + startDateAsDate.getFullYear();
|
||
var endDateAsText = (endDateAsDate.getMonth() + 1) + '/' + endDateAsDate.getDate() + '/' + endDateAsDate.getFullYear();
|
||
|
||
var newFilterSelection = $scope.data.SelectedFilterItems;
|
||
for (var outTeamIndex = 0; outTeamIndex < outTeamIds.length; outTeamIndex++) {
|
||
newFilterSelection.push(outTeamIds[outTeamIndex]);
|
||
}
|
||
$rootScope.$broadcast("changeDisplayView", startDateAsText, endDateAsText, newFilterSelection, function ()
|
||
{
|
||
if (typeof (fnCallback) === 'function')
|
||
fnCallback();
|
||
});
|
||
});
|
||
});
|
||
});
|
||
} else {
|
||
$scope.recreateView();
|
||
if (typeof (fnCallback) === 'function')
|
||
fnCallback();
|
||
}
|
||
}
|
||
|
||
function copyToScenarioTeamInformation(scenario) {
|
||
if (!scenario || !scenario.Expenditures)
|
||
return;
|
||
|
||
var teams = teamInfoService.getAll();
|
||
if (!!teams) {
|
||
for (var expCatId in scenario.Expenditures) {
|
||
var expCat = scenario.Expenditures[expCatId];
|
||
if (!expCat || !expCat.Teams)
|
||
continue;
|
||
|
||
for (var teamId in expCat.Teams) {
|
||
var targetTeam = expCat.Teams[teamId];
|
||
var sourceTeam = teams[teamId];
|
||
|
||
copyTeamCapacityInfo(targetTeam, sourceTeam, expCatId);
|
||
}
|
||
}
|
||
}
|
||
};
|
||
function copyTeamCapacityInfo(targetTeam, sourceTeam, expCatId) {
|
||
if (!targetTeam || !sourceTeam || !expCatId || !sourceTeam.ExpCategories)
|
||
return;
|
||
|
||
var currentDate = new Date();
|
||
var currentDateUtc = new Date(currentDate.getUTCFullYear(), currentDate.getUTCMonth(), currentDate.getUTCDate());
|
||
|
||
var expCatInTeam = sourceTeam.ExpCategories[expCatId];
|
||
if (!expCatInTeam)
|
||
return;
|
||
|
||
var plannedCapacityWeeks = Object.keys(expCatInTeam.PlannedCapacityValues || {});
|
||
var actualCapacityWeeks = Object.keys(expCatInTeam.ActualCapacityValues || {});
|
||
var capacityWeeks = union(plannedCapacityWeeks, actualCapacityWeeks);
|
||
|
||
targetTeam.CapacityQuantityValues = {};
|
||
targetTeam.RestQuantityValues = {};
|
||
|
||
for (var i = 0; i < capacityWeeks.length; i++) {
|
||
var weekEnding = new Date(capacityWeeks[i]),
|
||
weekEndingUtc = new Date(weekEnding.getUTCFullYear(), weekEnding.getUTCMonth(), weekEnding.getUTCDate()),
|
||
capacity = 0;
|
||
|
||
if (weekEndingUtc < currentDateUtc)
|
||
capacity = expCatInTeam.ActualCapacityValues[capacityWeeks[i]] || 0;
|
||
else
|
||
capacity = expCatInTeam.PlannedCapacityValues[capacityWeeks[i]] || 0;
|
||
|
||
targetTeam.CapacityQuantityValues[capacityWeeks[i]] = capacity;
|
||
targetTeam.RestQuantityValues[capacityWeeks[i]] = capacity - (expCatInTeam.AllocatedCapacity ? (expCatInTeam.AllocatedCapacity[capacityWeeks[i]] || 0) : 0);
|
||
}
|
||
|
||
if (!expCatInTeam.Resources || Object.keys(expCatInTeam.Resources).length <= 0)
|
||
return;
|
||
|
||
if (!!targetTeam.AllResources) {
|
||
for (var resourceId in targetTeam.AllResources) {
|
||
if (!expCatInTeam.Resources[resourceId])
|
||
continue;
|
||
|
||
var targetResource = targetTeam.AllResources[resourceId];
|
||
var sourceResource = expCatInTeam.Resources[resourceId];
|
||
|
||
copyResourceCapacityInfo(targetResource, sourceResource);
|
||
}
|
||
}
|
||
|
||
if (!!targetTeam.Resources) {
|
||
for (var resourceId in targetTeam.Resources) {
|
||
if (!expCatInTeam.Resources[resourceId])
|
||
continue;
|
||
|
||
var targetResource = targetTeam.Resources[resourceId];
|
||
var sourceResource = expCatInTeam.Resources[resourceId];
|
||
|
||
copyResourceCapacityInfo(targetResource, sourceResource);
|
||
}
|
||
}
|
||
};
|
||
function copyResourceCapacityInfo(targetResource, sourceResource) {
|
||
if (!targetResource || !sourceResource)
|
||
return;
|
||
|
||
targetResource.CapacityQuantityValues = angular.copy(sourceResource.TotalCapacity || {});
|
||
targetResource.RestQuantityValues = {};
|
||
|
||
for (var weekEnding in targetResource.CapacityQuantityValues) {
|
||
var capacity = (targetResource.CapacityQuantityValues[weekEnding] || 0);
|
||
var allocated = !sourceResource.AllocatedCapacity ? 0 : (sourceResource.AllocatedCapacity[weekEnding] || 0);
|
||
|
||
targetResource.RestQuantityValues[weekEnding] = capacity - allocated;
|
||
}
|
||
};
|
||
|
||
// SA. ENV-1159
|
||
function getExtendedViewDates(scenarioStartDate, scenarioEndDate, currentViewStartDate, currentViewEndDate) {
|
||
var startDate = scenarioStartDate < currentViewStartDate ? scenarioStartDate : currentViewStartDate;
|
||
var endDate = scenarioEndDate > currentViewEndDate ? scenarioEndDate : currentViewEndDate;
|
||
|
||
var startDateObj = new Date(startDate);
|
||
var endDateObj = new Date(endDate);
|
||
|
||
var startDateText = (startDateObj.getMonth() + 1) + "/" + startDateObj.getDate() + "/" + startDateObj.getFullYear();
|
||
var endDateText = (endDateObj.getMonth() + 1) + "/" + endDateObj.getDate() + "/" + endDateObj.getFullYear();
|
||
|
||
return {
|
||
StartDate: startDateText,
|
||
EndDate: endDateText
|
||
};
|
||
};
|
||
|
||
// SA. ENV-1159
|
||
function getChangeViewPrompt(startDateText, endDateText) {
|
||
var basicText = "Project has been saved, but it is scheduled outside current view.<br/>" +
|
||
"Would you like to change current view to {0} to get the project displayed?";
|
||
|
||
var finalText = basicText.replace("{0}", "[" + startDateText + " - " + endDateText + "]");
|
||
return finalText;
|
||
};
|
||
|
||
function calcWithPrecision(value) {
|
||
var dec = Math.pow(10, FormatCells.DecimalCalcQtyPlaces);
|
||
return Math.round(value * dec) / dec;
|
||
}
|
||
|
||
function getDatesShift(oldDate, newDate) {
|
||
if (!oldDate || !newDate) {
|
||
return 0;
|
||
}
|
||
|
||
var oldDateIndex = binarySearch($scope.Calendar.FiscalCalendarWeekEndings, oldDate, 0, $scope.Calendar.FiscalCalendarWeekEndings.length, false);
|
||
var newDateIndex = binarySearch($scope.Calendar.FiscalCalendarWeekEndings, newDate, 0, $scope.Calendar.FiscalCalendarWeekEndings.length, false);
|
||
|
||
if (oldDateIndex < 0 || newDateIndex < 0) {
|
||
throw 'Incorrect oldDate [' + oldDate + '] and newDate [' + newDate + '] parameters';
|
||
}
|
||
|
||
return newDateIndex - oldDateIndex;
|
||
}
|
||
|
||
function getNewDate(date, shift) {
|
||
if (!date || !shift) {
|
||
return date;
|
||
}
|
||
|
||
var dateIndex = binarySearch($scope.Calendar.FiscalCalendarWeekEndings, date, 0, $scope.Calendar.FiscalCalendarWeekEndings.length, false);
|
||
if (dateIndex < 0) {
|
||
throw 'Incorrect date [' + date + '] parameter';
|
||
}
|
||
|
||
return $scope.Calendar.FiscalCalendarWeekEndings[dateIndex + shift];
|
||
}
|
||
|
||
function getNearestDate(date) {
|
||
if (!date)
|
||
return;
|
||
|
||
var index = binarySearch($scope.Calendar.FiscalCalendarWeekEndings, date, 0, $scope.Calendar.FiscalCalendarWeekEndings.length, true);
|
||
|
||
if (index < 0 ||
|
||
index >= $scope.Calendar.FiscalCalendarWeekEndings.length ||
|
||
index >= $scope.Calendar.FiscalCalendarWeekEndings.length) {
|
||
return null;
|
||
}
|
||
|
||
return $scope.Calendar.FiscalCalendarWeekEndings[index];
|
||
}
|
||
|
||
function findProjectPositionInLayout(projectId, layout) {
|
||
if (!projectId || !layout || layout.length <= 0) {
|
||
return null;
|
||
}
|
||
|
||
for (var i = 0; i < layout.length; i++) {
|
||
if (!layout[i])
|
||
continue;
|
||
|
||
for (var j = 0; j < layout[i].length; j++) {
|
||
if (layout[i][j].ProjectId == projectId) {
|
||
return {
|
||
RowIndex: i,
|
||
ColIndex: j
|
||
};
|
||
}
|
||
}
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
function findProjectInLayout(teamId, projectId) {
|
||
var currentLayout = $scope.GridLayout[teamId];
|
||
if (!currentLayout) {
|
||
return;
|
||
}
|
||
|
||
var projectPosition = findProjectPositionInLayout(projectId, currentLayout);
|
||
if (!projectPosition) {
|
||
return;
|
||
}
|
||
|
||
return currentLayout[projectPosition.RowIndex][projectPosition.ColIndex];
|
||
}
|
||
|
||
function isCallbackValid(callbackFunc) {
|
||
return (callbackFunc && (callbackFunc != null) && (typeof callbackFunc === 'function'));
|
||
}
|
||
|
||
function isValidNumber(number) {
|
||
if (!number && number != 0)
|
||
return false;
|
||
|
||
if (typeof number === 'number')
|
||
return true;
|
||
|
||
if (typeof number === 'string')
|
||
return number.match(new RegExp(/^\d*\.?\d*$/)) !== null;
|
||
|
||
throw 'number has type [' + (typeof number) + ']';
|
||
}
|
||
|
||
function getWeekends4Item(row, month, itemId) {
|
||
if (!row || !row.Cells || row.Cells.length <= 0 || !itemId || !month || !month.Childs)
|
||
return 0;
|
||
|
||
var count = 0;
|
||
for (var i = 0; i < month.Childs.length; i++) {
|
||
if (row.Cells[month.Childs[i]].Id == itemId)
|
||
count++;
|
||
}
|
||
|
||
return count;
|
||
}
|
||
|
||
// SA. ENV-1153
|
||
function setDataChanged(value) {
|
||
if ($scope.DataChanged != value) {
|
||
$scope.DataChanged = value;
|
||
$rootScope.$broadcast("dataChanged", $scope.DataChanged);
|
||
}
|
||
}
|
||
|
||
// SA. ENV-1159
|
||
function initPageControls() {
|
||
$element.find('#selProjects2Add').select2('val', '');
|
||
$(".projectEdit").on('click', function (e) {
|
||
$('#outdated').show();
|
||
});
|
||
}
|
||
|
||
// SA. ENV-1210. Compares simple arrays by data (ordering doesn't matter)
|
||
function ArraysAreEqual(array1, array2) {
|
||
var len1 = $(array1).not(array2).length;
|
||
var len2 = $(array2).not(array1).length;
|
||
return (len1 == len2) && (len1 == 0);
|
||
}
|
||
|
||
// SA. ENV-1210. Hides warning messageat the top of the Calendar
|
||
$scope.hidePageWarningMessage = function () {
|
||
$scope.PageWarningMessage = "";
|
||
}
|
||
|
||
$scope.copyScenario = function (id, scenId) {
|
||
$scope.copier.parentProjectId = id;
|
||
$scope.copier.scenarioId = scenId;
|
||
$scope.copier.targetProjectId = $scope.Calendar.Projects[Object.keys($scope.Calendar.Projects)[0]];
|
||
$('#copierProjectId').select2().select2('val', $('#copierProjectId option:eq(1)').val());
|
||
var scen = $scope.Calendar.Projects[Object.keys($scope.Calendar.Projects)[0]].InactiveScenarios[scenId];
|
||
if (typeof scen === 'undefined')
|
||
scen = $scope.Calendar.Projects[Object.keys($scope.Calendar.Projects)[0]].Scenario;
|
||
$scope.copier.scenarioName = scen.Name + ' Copy';
|
||
$("#copyScenario").modal('show');
|
||
}
|
||
$scope.copyScenarioConfirmed = function () {
|
||
var scenario = jQuery.extend(true, {}, $scope.Calendar.Projects[$scope.copier.parentProjectId].InactiveScenarios[$scope.copier.scenarioId]);
|
||
if (typeof scenario === 'undefined')
|
||
scenario = $scope.Calendar.Projects[$scope.copier.parentProjectId].Scenario;
|
||
scenario.Name = $scope.copier.scenarioName;
|
||
scenario.Id = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
|
||
var r = crypto.getRandomValues(new Uint8Array(1))[0] % 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
|
||
return v.toString(16);
|
||
});
|
||
$scope.Calendar.Projects[$scope.copier.targetProjectId.Id].Scenario = scenario;
|
||
$.each($scope.Calendar.Projects[$scope.copier.parentProjectId].Teams, function (index, id) {
|
||
var rows = $scope.GridLayout[id];
|
||
if (typeof rows === 'undefined') rows = [0];
|
||
var k = -1, i = 0, len = $scope.Calendar.UnscheduledProjects.length;
|
||
for (; i < len; i++) {
|
||
if ($scope.Calendar.UnscheduledProjects[i].Id == $scope.copier.targetProjectId.Id) {
|
||
k = i;
|
||
}
|
||
}
|
||
if (k >= 0) addProjectFromUnscheduled($scope.copier.targetProjectId.Id, id, rows.length);
|
||
});
|
||
$scope.recreateView();
|
||
|
||
//$("#copyScenario").modal('hide');
|
||
console.log('Copied Successfully');
|
||
}
|
||
|
||
/* SA. ENV-1254. Reassign of Unassigned Expenditure Categories ------------------------------ */
|
||
$scope.frmReassign_ToTeamId = null;
|
||
$scope.frmReassign_ToExpCatId = null;
|
||
$scope.frmReassign_Action = null;
|
||
$scope.frmReassign_ExpCatsInTeam = [];
|
||
$scope.frmReassign_MixTeams = [];
|
||
$scope.frmReassign_ExpCatName = null;
|
||
$scope.frmReassign_OtherEcActionEnabled = true;
|
||
$scope.frmReassign_TryAssignOtherECs = false;
|
||
$scope.frmReassign_CurrentItem = null;
|
||
|
||
// SA. ENV-1254
|
||
$scope.reassignExpCat = function (expCatInfo, callbackFuncName) {
|
||
if (!expCatInfo)
|
||
return;
|
||
|
||
$scope.frmReassign_ExpCatName = '< Unknown >';
|
||
$scope.reassignToTeamId = null;
|
||
$scope.reassignToExpCatId = null;
|
||
$scope.frmReassign_TryAssignOtherECs = false;
|
||
$scope.frmReassign_ExpCatsInTeam = [];
|
||
$scope.frmReassign_MixTeams = [];
|
||
$scope.frmReassign_CurrentItem = expCatInfo;
|
||
$scope.frmReassign_OtherEcActionEnabled = expCatInfo.TargetTeamId && (expCatInfo.TargetTeamId != C_EMPTY_GUID);
|
||
$scope.frmReassign_Action = $scope.frmReassign_OtherEcActionEnabled ? 'otherEC' : 'otherTeam';
|
||
|
||
if (expCatInfo.Name && (expCatInfo.Name.length > 0)) {
|
||
var separatorPos = expCatInfo.Name.indexOf(";");
|
||
|
||
if (separatorPos > 0) {
|
||
$scope.frmReassign_ExpCatName = expCatInfo.Name.substr(0, separatorPos);
|
||
}
|
||
}
|
||
|
||
// Prepare EC list of current team
|
||
if ($scope.frmReassign_OtherEcActionEnabled) {
|
||
var teamItem = teamInfoService.getById(expCatInfo.TargetTeamId);
|
||
|
||
if (teamItem && teamItem.ExpCategories) {
|
||
var expCatsInTeam = Object.keys(teamItem.ExpCategories)
|
||
|
||
$.each(expCatsInTeam, function (index, expCatId) {
|
||
var currentExpCatItem = teamItem.ExpCategories[expCatId];
|
||
var ItemToPush = {
|
||
Id: currentExpCatItem.Id,
|
||
Name: currentExpCatItem.Name
|
||
}
|
||
$scope.frmReassign_ExpCatsInTeam.push(ItemToPush);
|
||
});
|
||
}
|
||
}
|
||
|
||
// Prepare list of teams, which contain specified category
|
||
var sourceExpCatId = $scope.frmReassign_CurrentItem.ExpCatId;
|
||
var projectId = expCatInfo.ProjectId;
|
||
var projectItem = $scope.getProjectById(projectId);
|
||
|
||
loadTeamsByExpCategory(sourceExpCatId, function (teams) {
|
||
if (teams) {
|
||
var thisMixGroup = {
|
||
Ord: 1,
|
||
Name: "Teams in Current Mix"
|
||
};
|
||
|
||
var otherTeamsGroup = {
|
||
Ord: 2,
|
||
Name: "Other Teams"
|
||
};
|
||
|
||
angular.forEach(teams, function (teamName, teamId) {
|
||
var teamNameDisplayed = (projectItem.Teams.indexOf(teamId) < 0) ? teamName : (teamName + ' *');
|
||
|
||
var teamRecord = {
|
||
Id: teamId,
|
||
Name: teamNameDisplayed,
|
||
Group: teamInfoService.isExists(teamId) ? thisMixGroup : otherTeamsGroup
|
||
}
|
||
$scope.frmReassign_MixTeams.push(teamRecord);
|
||
});
|
||
|
||
if (callbackFuncName && (callbackFuncName.length > 0)) {
|
||
var callbackFunc = new Function(callbackFuncName + "('" + $scope.frmReassign_Action + "')");
|
||
callbackFunc();
|
||
}
|
||
}
|
||
})
|
||
}
|
||
|
||
// SA. ENV-1254
|
||
function loadTeamsByExpCategory(expCatId, successCallbackFn) {
|
||
blockUI();
|
||
var requestData = {
|
||
id: expCatId
|
||
};
|
||
var request = getAntiXSRFRequest($scope.dataUrls.getTeamsByExpCatUrl);
|
||
request.data = requestData;
|
||
try {
|
||
$http(request).success(function (teams, status, headers, config) {
|
||
try {
|
||
if (!teams) {
|
||
unblockUI();
|
||
return null;
|
||
}
|
||
|
||
if (!!successCallbackFn && typeof successCallbackFn === 'function') {
|
||
successCallbackFn(teams);
|
||
}
|
||
|
||
unblockUI();
|
||
} catch (e) {
|
||
unblockUI();
|
||
showErrorModal('Oops!', commonErrorMessage);
|
||
}
|
||
}).error(function (data, status, headers, config) {
|
||
unblockUI();
|
||
showErrorModal('Oops!', commonErrorMessage);
|
||
});
|
||
} catch (e) {
|
||
unblockUI();
|
||
showErrorModal('Oops!', commonErrorMessage);
|
||
}
|
||
}
|
||
|
||
// SA. ENV-1254
|
||
$scope.openScenarioDetailsWindow = function () {
|
||
if (!$scope.frmReassign_CurrentItem)
|
||
return;
|
||
|
||
var projectId = $scope.frmReassign_CurrentItem.ProjectId;
|
||
var project = $scope.getProjectById(projectId);
|
||
if (!project || !project.Scenario)
|
||
return;
|
||
|
||
loadScenarioDetailsEditForm(project.Scenario, false);
|
||
}
|
||
|
||
// SA. ENV-1254
|
||
$scope.reassignUnassignedAllocations = function (openScenarioDetails) {
|
||
if (!$scope.frmReassign_CurrentItem)
|
||
return;
|
||
|
||
var projectId = $scope.frmReassign_CurrentItem.ProjectId;
|
||
var projectItem = $scope.getProjectById(projectId);
|
||
|
||
if (!projectItem || !projectItem.Scenario || !projectItem.Scenario.Id)
|
||
return;
|
||
|
||
$scope.hidePageWarningMessage();
|
||
|
||
if (!isScenarioLoaded(projectItem.Scenario)) {
|
||
loadScenarioDetails(projectItem.Scenario.Id, projectId, function (expenditures) {
|
||
setScenarioExpenditures(projectItem.Scenario, expenditures);
|
||
reassignUnassignedAllocationsInternal(openScenarioDetails);
|
||
})
|
||
}
|
||
else {
|
||
reassignUnassignedAllocationsInternal(openScenarioDetails);
|
||
}
|
||
}
|
||
|
||
// SA. ENV-1254
|
||
function reassignUnassignedAllocationsInternal(openScenarioDetails) {
|
||
var projectId = $scope.frmReassign_CurrentItem.ProjectId;
|
||
var sourceExpCatId = $scope.frmReassign_CurrentItem.ExpCatId;
|
||
|
||
switch ($scope.frmReassign_Action) {
|
||
case "remove":
|
||
removeUnassignedAllocations(projectId, sourceExpCatId);
|
||
removeProjectFromUnassignedExpenditures(projectId, sourceExpCatId);
|
||
|
||
if (openScenarioDetails)
|
||
$scope.openScenarioDetailsWindow();
|
||
|
||
break;
|
||
case "otherEC":
|
||
var targetExpCatId = $scope.frmReassign_ToExpCatId;
|
||
var targetTeamId = $scope.frmReassign_CurrentItem.TargetTeamId;
|
||
setUnassignedAllocationsToExpCat(projectId, targetTeamId, sourceExpCatId, targetExpCatId);
|
||
removeProjectFromUnassignedExpenditures(projectId, sourceExpCatId);
|
||
|
||
if (openScenarioDetails)
|
||
$scope.openScenarioDetailsWindow();
|
||
|
||
break;
|
||
case "otherTeam":
|
||
var targetTeamId = $scope.frmReassign_ToTeamId;
|
||
setUnassignedAllocationsToOtherTeam(projectId, sourceExpCatId, targetTeamId, openScenarioDetails);
|
||
$scope.recreateView();
|
||
break;
|
||
}
|
||
}
|
||
|
||
// SA. ENV-1254
|
||
function setUnassignedAllocationsToExpCat(projectId, teamId, sourceExpCatId, targetExpCatId) {
|
||
var project = $scope.getProjectById(projectId);
|
||
var teamItem = teamInfoService.getById(teamId);
|
||
|
||
if (!project || !project.Scenario || !project.Scenario.Expenditures || !teamItem)
|
||
return;
|
||
|
||
var projectExpCats = Object.keys(project.Scenario.Expenditures);
|
||
if ($.inArray(sourceExpCatId, projectExpCats) < 0)
|
||
// Scenario structure is invalid: source EC not found
|
||
return;
|
||
|
||
if ($.inArray(targetExpCatId, projectExpCats) < 0) {
|
||
var teamExpCats = Object.keys(teamItem.ExpCategories);
|
||
|
||
if ($.inArray(targetExpCatId, teamExpCats) >= 0) {
|
||
var teamExpCatTemplate = teamItem.ExpCategories[targetExpCatId];
|
||
var expCatItem = angular.copy(project.Scenario.Expenditures[sourceExpCatId]);
|
||
|
||
expCatItem.ExpenditureCategoryId = teamExpCatTemplate.Id;
|
||
expCatItem.ExpenditureCategoryName = teamExpCatTemplate.Name;
|
||
expCatItem.UOMValue = teamExpCatTemplate.UomValue;
|
||
expCatItem.Teams = {};
|
||
expCatItem.UnassignedAllocations = {};
|
||
|
||
// TODO. SA. Get from EC-template from Team the fallowing values
|
||
// (attributes must be added to Team in Mix.Teams collection):
|
||
// CGEFX, CreditId, GLId, SortOrder, SystemAttributeOne, SystemAttributeTwo, Type, UOMId, UseType
|
||
|
||
project.Scenario.Expenditures[targetExpCatId] = expCatItem;
|
||
}
|
||
}
|
||
|
||
// Check team exists in target EC
|
||
var targetExpCat = project.Scenario.Expenditures[targetExpCatId];
|
||
|
||
if (!targetExpCat.Teams)
|
||
targetExpCat.Teams = {};
|
||
|
||
if (!targetExpCat.Teams[teamId])
|
||
fillExpenditures4TeamById(project, teamId);
|
||
|
||
// Move unassigned allocations in source EC to target Team in target EC
|
||
if (targetExpCat.Teams[teamId]) {
|
||
var updatedData = angular.copy(project.Scenario.Expenditures);
|
||
var sourceExpCat = updatedData[sourceExpCatId];
|
||
targetExpCat = updatedData[targetExpCatId];
|
||
var targetExpCatTeam = targetExpCat.Teams[teamId];
|
||
|
||
angular.forEach(sourceExpCat.UnassignedAllocations, function (allocationItem, we) {
|
||
if (targetExpCatTeam.QuantityValues[we] === undefined)
|
||
targetExpCatTeam.QuantityValues[we] = 0;
|
||
|
||
targetExpCatTeam.QuantityValues[we] += allocationItem;
|
||
});
|
||
|
||
sourceExpCat.UnassignedAllocations = {};
|
||
refreshProjectData(project, updatedData);
|
||
}
|
||
}
|
||
|
||
// SA. ENV-1254
|
||
function loadTeamsData(teamIds, callbackFn) {
|
||
blockUI();
|
||
var requestData = {
|
||
ids: teamIds,
|
||
startDateMs: $scope.Calendar.StartDate,
|
||
endDateMs: $scope.Calendar.EndDate
|
||
};
|
||
var request = getAntiXSRFRequest($scope.dataUrls.getTeamsByIdUrl);
|
||
request.data = requestData;
|
||
try {
|
||
$http(request).success(function (teams, status, headers, config) {
|
||
try {
|
||
if (!teams) {
|
||
unblockUI();
|
||
return null;
|
||
}
|
||
|
||
if (!!callbackFn && typeof callbackFn === 'function') {
|
||
callbackFn(teams);
|
||
}
|
||
|
||
unblockUI();
|
||
} catch (e) {
|
||
unblockUI();
|
||
showErrorModal('Oops!', commonErrorMessage);
|
||
}
|
||
}).error(function (data, status, headers, config) {
|
||
unblockUI();
|
||
showErrorModal('Oops!', commonErrorMessage);
|
||
});
|
||
} catch (e) {
|
||
unblockUI();
|
||
showErrorModal('Oops!', commonErrorMessage);
|
||
}
|
||
}
|
||
|
||
// SA. ENV-1254
|
||
function setUnassignedAllocationsToOtherTeam(projectId, expCatId, targetTeamId, openScenarioDetails) {
|
||
var teamItem = teamInfoService.getById(targetTeamId);
|
||
|
||
if (!teamItem) {
|
||
// Team is not in the Mix
|
||
loadTeamsData([targetTeamId], function (teams) {
|
||
angular.forEach(teams, function (team, teamIndex) {
|
||
// add new team with empty allocations
|
||
addTeamToProjectWithStructs(projectId, expCatId, team);
|
||
});
|
||
// add new team to filter, apply changes and fill allocations on callback
|
||
bootbox.alert('The selected Team must be added to current Mix. Press OK to reload Mix<br/>' +
|
||
'Unsaved Mix data will be preserved...', function () {
|
||
$scope.$apply(function () {
|
||
var startDateAsDate = new Date($scope.Calendar.StartDate);
|
||
var endDateAsDate = new Date($scope.Calendar.EndDate);
|
||
|
||
var startDateAsText =
|
||
(startDateAsDate.getMonth() + 1) + '/' + startDateAsDate.getDate() + '/' + startDateAsDate.getFullYear();
|
||
var endDateAsText =
|
||
(endDateAsDate.getMonth() + 1) + '/' + endDateAsDate.getDate() + '/' + endDateAsDate.getFullYear();
|
||
|
||
var newFilterSelection = $scope.data.SelectedFilterItems;
|
||
newFilterSelection.push(targetTeamId);
|
||
$rootScope.$broadcast("changeDisplayView", startDateAsText, endDateAsText, newFilterSelection,
|
||
function () {
|
||
// add allocations to the new team of the project
|
||
setUnassignedAllocationsToTeam(projectId, expCatId, targetTeamId);
|
||
// remove record from "Project with Unassigned expenditures" block, with specific expenditure category
|
||
removeProjectFromUnassignedExpenditures(projectId, expCatId);
|
||
|
||
// SA. ENV-1298. Try to allocate other unassigned ECs of the project to selected team
|
||
if ($scope.frmReassign_TryAssignOtherECs) {
|
||
tryAllocateUnassignedExpCatsToTeam(projectId, teamId);
|
||
}
|
||
|
||
if (openScenarioDetails) {
|
||
$scope.openScenarioDetailsWindow();
|
||
}
|
||
});
|
||
});
|
||
});
|
||
});
|
||
}
|
||
else {
|
||
// Team exists in the Mix
|
||
var project = $scope.getProjectById(projectId);
|
||
|
||
if (!$scope.projectHasTeam(project, teamItem.Id)) {
|
||
addTeamToProjectWithStructs(projectId, expCatId, teamItem);
|
||
}
|
||
|
||
setUnassignedAllocationsToTeam(projectId, expCatId, teamItem.Id);
|
||
removeProjectFromUnassignedExpenditures(projectId, expCatId);
|
||
|
||
// SA. ENV-1298. Try to allocate other unassigned ECs of the project to selected team
|
||
if ($scope.frmReassign_TryAssignOtherECs) {
|
||
tryAllocateUnassignedExpCatsToTeam(projectId, teamItem.Id);
|
||
}
|
||
|
||
if (openScenarioDetails) {
|
||
$scope.openScenarioDetailsWindow();
|
||
}
|
||
}
|
||
}
|
||
|
||
// SA. ENV-1254
|
||
function addTeamToProjectWithStructs(projectId, expCatId, team) {
|
||
if (!projectId || !expCatId || !team)
|
||
return;
|
||
|
||
var project = $scope.getProjectById(projectId);
|
||
if (!project || !project.Scenario || !project.Scenario.Expenditures)
|
||
return;
|
||
|
||
if (Object.keys(project.Scenario.Expenditures).indexOf(expCatId) < 0)
|
||
// Expenditure not in the Project
|
||
return;
|
||
|
||
if (!project.Scenario.Expenditures[expCatId].Teams)
|
||
project.Scenario.Expenditures[expCatId].Teams = {};
|
||
|
||
// Adding team to project
|
||
addTeamToProject(project.Id, team.Id, 0, 0, true);
|
||
|
||
if (!teamInfoService.isExists(team.Id)) {
|
||
fillExpenditures4Team(project, team);
|
||
}
|
||
}
|
||
|
||
// SA. ENV-1254
|
||
function setUnassignedAllocationsToTeam(projectId, expCatId, teamId) {
|
||
if (!projectId || !expCatId || !teamId)
|
||
return;
|
||
|
||
var project = $scope.getProjectById(projectId);
|
||
if (!project || !project.Scenario || !project.Scenario.Expenditures)
|
||
return;
|
||
|
||
// Move unassigned allocations to target Team in target EC
|
||
var updatedData = angular.copy(project.Scenario.Expenditures);
|
||
var expCat = updatedData[expCatId];
|
||
var expCatTeam = expCat.Teams[teamId];
|
||
|
||
angular.forEach(expCat.UnassignedAllocations, function (allocationItem, we) {
|
||
if (expCatTeam.QuantityValues[we] === undefined)
|
||
expCatTeam.QuantityValues[we] = 0;
|
||
|
||
expCatTeam.QuantityValues[we] += allocationItem;
|
||
});
|
||
|
||
// Clear unassigned data only, because it was just moved
|
||
expCat.UnassignedAllocations = {};
|
||
refreshProjectData(project, updatedData);
|
||
}
|
||
|
||
// SA. ENV-1254
|
||
function removeUnassignedAllocations(projectId, expCatId) {
|
||
var project = $scope.getProjectById(projectId);
|
||
|
||
if (project && project.Scenario && project.Scenario.Expenditures) {
|
||
var expCatKeys = Object.keys(project.Scenario.Expenditures);
|
||
|
||
if ($.inArray(expCatId, expCatKeys) >= 0) {
|
||
delete project.Scenario.Expenditures[expCatId];
|
||
}
|
||
}
|
||
}
|
||
|
||
// SA. ENV-1254
|
||
$scope.IsReassignExpCatActionAvailable = function () {
|
||
var result =
|
||
((($scope.frmReassign_Action == "otherEC") && $scope.frmReassign_ToExpCatId) ||
|
||
(($scope.frmReassign_Action == "otherTeam") && $scope.frmReassign_ToTeamId) ||
|
||
($scope.frmReassign_Action == "remove"));
|
||
return result;
|
||
}
|
||
|
||
// SA. ENV-1298. If expCatsArray not specified, does the action for all found ECs in Unassigned ECs block
|
||
function tryAllocateUnassignedExpCatsToTeam(projectId, teamId, expCatsArray) {
|
||
if (!$scope.UnassignedExpendituresProjectsExist)
|
||
return;
|
||
|
||
var expCats = expCatsArray;
|
||
|
||
if (!expCats) {
|
||
// Get EC list from Unassigned Expenditures block currently existing records
|
||
expCats = [];
|
||
|
||
angular.forEach($scope.Calendar.UnassignedExpendituresProjects, function (rec, index) {
|
||
if (rec.ProjectId == projectId) {
|
||
expCats.push(rec.ExpCatId);
|
||
}
|
||
});
|
||
}
|
||
|
||
for (var index = 0; index < expCats.length; index++) {
|
||
var expCatId = expCats[index];
|
||
tryAllocateSingleUnassignedExpCatToTeam(projectId, teamId, expCatId);
|
||
}
|
||
}
|
||
|
||
// SA. ENV-1298.
|
||
function tryAllocateSingleUnassignedExpCatToTeam(projectId, teamId, expCatId) {
|
||
// Check project has the specified EC and Team
|
||
var project = $scope.getProjectById(projectId);
|
||
if (!project || !project.Scenario || !$scope.projectHasTeam(project, teamId))
|
||
return;
|
||
|
||
if (!project.Scenario.Expenditures || !project.Scenario.Expenditures[expCatId] ||
|
||
!project.Scenario.Expenditures[expCatId].Teams || !project.Scenario.Expenditures[expCatId].Teams[teamId])
|
||
return;
|
||
|
||
setUnassignedAllocationsToExpCat(projectId, teamId, expCatId, expCatId);
|
||
removeProjectFromUnassignedExpenditures(projectId, expCatId);
|
||
}
|
||
|
||
/* SA. ENV-1218. Functions to remove team from Project and redistribute allocations ----------- */
|
||
$scope.removeTeamFromProject = function (projectId, teamId) {
|
||
if (!projectId || !teamId || (projectId == C_EMPTY_GUID) || (teamId == C_EMPTY_GUID))
|
||
return;
|
||
|
||
var project = $scope.getProjectById(projectId);
|
||
|
||
if (!project.Teams || (project.Teams.indexOf(teamId) < 0))
|
||
return;
|
||
|
||
if (!isScenarioLoaded(project.Scenario)) {
|
||
loadScenarioDetails(project.Scenario.Id, projectId, function (expenditures) {
|
||
setScenarioExpenditures(project.Scenario, expenditures);
|
||
distributeTeamAllocationsToOtherTeams(project, teamId);
|
||
$scope.recreateView();
|
||
})
|
||
}
|
||
else {
|
||
distributeTeamAllocationsToOtherTeams(project, teamId);
|
||
$scope.recreateView();
|
||
}
|
||
}
|
||
|
||
function distributeTeamAllocationsToOtherTeams(project, sourceTeamId) {
|
||
if (!project || !sourceTeamId || (sourceTeamId == C_EMPTY_GUID))
|
||
return;
|
||
|
||
if (!project.Teams || (project.Teams.indexOf(sourceTeamId) < 0))
|
||
return;
|
||
|
||
// Create allocations distribution map
|
||
var allocationsDistributionMap = createAllocationsDistributionMap(project, sourceTeamId);
|
||
var movableExpCats = Object.keys(allocationsDistributionMap);
|
||
|
||
if (movableExpCats && (movableExpCats.length > 0)) {
|
||
// Perform redistribution in a copy of Project's expenditure allocations by map
|
||
var projectDataCopy = angular.copy(project.Scenario.Expenditures);
|
||
moveAllocationsByDistributionMap(projectDataCopy, sourceTeamId, allocationsDistributionMap)
|
||
refreshProjectData(project, projectDataCopy, false);
|
||
projectDataCopy = null;
|
||
}
|
||
|
||
removeTeamFromProjectInternal(project.Id, sourceTeamId);
|
||
var recordsCreated = createProjectUnassignedExpendituresItems(project);
|
||
|
||
if (recordsCreated > 0) {
|
||
$timeout(function () {
|
||
bootbox.alert("Allocations for some expenditure categories were not able to be distributed " +
|
||
"to other teams, and became unassigned.<br/>Please, Check 'Projects with Unassigned Expenditures' " +
|
||
"block for details.");
|
||
});
|
||
}
|
||
}
|
||
|
||
// SA. ENV-1218. Creates records in Projects with Unassigned Expenditures block for
|
||
// all project ExpCats, that has unZero values in ExpCat.UnassignedAllocations
|
||
function createProjectUnassignedExpendituresItems(project, setCurrentTeamId) {
|
||
var recordsCreatedCount = 0;
|
||
|
||
if (!project || !project.Scenario || !project.Scenario.Expenditures)
|
||
return recordsCreatedCount;
|
||
|
||
if (isProjectInManaged(project.Id)) {
|
||
var projectExpCats = Object.keys(project.Scenario.Expenditures);
|
||
|
||
if (projectExpCats.length > 0) {
|
||
var unassignedExpCats = $.grep(projectExpCats, function (expCatId, index) {
|
||
var expCatData = project.Scenario.Expenditures[expCatId];
|
||
var hasUnassignedQuantities = false;
|
||
|
||
if (expCatData && expCatData.UnassignedAllocations) {
|
||
angular.forEach(expCatData.UnassignedAllocations, function (value, we) {
|
||
hasUnassignedQuantities = hasUnassignedQuantities || (value > 0);
|
||
});
|
||
}
|
||
|
||
return hasUnassignedQuantities;
|
||
});
|
||
|
||
recordsCreatedCount = unassignedExpCats.length;
|
||
|
||
if (recordsCreatedCount > 0) {
|
||
if (!setCurrentTeamId) {
|
||
if (project.Teams.length == 1)
|
||
// If project in the last has only one team, it will be target team for unassigned allocations
|
||
setCurrentTeamId = project.Teams[0];
|
||
}
|
||
|
||
$scope.pushProjectToUnassignedExpenditures(project, unassignedExpCats, setCurrentTeamId);
|
||
}
|
||
}
|
||
}
|
||
|
||
return recordsCreatedCount;
|
||
}
|
||
|
||
// SA. ENV-1218
|
||
function createAllocationsDistributionMap(project, teamId) {
|
||
var dMap = {};
|
||
if (!project.Scenario || !project.Scenario.Expenditures)
|
||
return dMap;
|
||
|
||
angular.forEach(project.Scenario.Expenditures, function (expCatData, expCatId) {
|
||
if (expCatData && expCatData.Teams) {
|
||
var expCatTeams = Object.keys(expCatData.Teams);
|
||
var otherTeams = [];
|
||
|
||
if (expCatTeams.indexOf(teamId) >= 0) {
|
||
// The Team exists in current EC. Looking for proper redistribution plan
|
||
otherTeams = $.grep(expCatTeams, function (ecTeamId, index) {
|
||
return (ecTeamId != teamId) && (project.Teams.indexOf(ecTeamId) >= 0);
|
||
});
|
||
|
||
dMap[expCatId] = otherTeams;
|
||
}
|
||
}
|
||
});
|
||
|
||
return dMap;
|
||
}
|
||
|
||
// SA. ENV-1218. Moves allocations from source team to specified teams for every EC in the Distribution map
|
||
// ExpCatsdata is the property Expenditures of a Scenario object
|
||
// distributionMap - associative array of ECs. Every element is an array of the target team IDs
|
||
function moveAllocationsByDistributionMap(ExpCatsdata, sourceTeamId, distributionMap) {
|
||
if (!ExpCatsdata || !sourceTeamId || !distributionMap)
|
||
return;
|
||
|
||
angular.forEach(distributionMap, function (targetTeamKeys, expCatId) {
|
||
var currentEC = ExpCatsdata[expCatId];
|
||
var srcTeam = currentEC.Teams ? currentEC.Teams[sourceTeamId] : undefined;
|
||
|
||
if (currentEC && srcTeam && srcTeam.QuantityValues) {
|
||
var weekEndings = Object.keys(srcTeam.QuantityValues);
|
||
var targetTeamsCount = targetTeamKeys && Array.isArray(targetTeamKeys) ? targetTeamKeys.length : 0;
|
||
|
||
if (targetTeamsCount > 0) {
|
||
// Move source team allocations to other teams
|
||
var targetTeams = [];
|
||
angular.forEach(targetTeamKeys, function (targetTeamId, index) {
|
||
var targetTeam = currentEC.Teams[targetTeamId];
|
||
targetTeams.push(targetTeam);
|
||
|
||
if (!targetTeam.QuantityValues)
|
||
targetTeam.QuantityValues = {};
|
||
});
|
||
|
||
angular.forEach(weekEndings, function (we, index) {
|
||
redistributeQuantityValuesByCurve(targetTeams, we, srcTeam.QuantityValues[we]);
|
||
// Reset value, because it was distributed to other teams
|
||
srcTeam.QuantityValues[we] = 0;
|
||
});
|
||
}
|
||
|
||
// Move remaining allocations to UnassignedQuantityValues for EC
|
||
if (!currentEC.UnassignedAllocations)
|
||
currentEC.UnassignedAllocations = {};
|
||
|
||
var remainingNonZeroValues = $.grep(weekEndings, function (we, index) {
|
||
return srcTeam.QuantityValues[we] > 0;
|
||
});
|
||
|
||
if (remainingNonZeroValues.length > 0) {
|
||
angular.forEach(weekEndings, function (we, index) {
|
||
var valueToMove = srcTeam.QuantityValues[we];
|
||
|
||
if (currentEC.UnassignedAllocations[we] === undefined)
|
||
currentEC.UnassignedAllocations[we] = 0;
|
||
|
||
currentEC.UnassignedAllocations[we] += valueToMove;
|
||
srcTeam.QuantityValues[we] = 0;
|
||
});
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
// SA. ENV-1218. Redistributes value for single Weekending to specified teams by curve.
|
||
// If no allocations exist for all specified teams, distribution is performed by equal parts
|
||
function redistributeQuantityValuesByCurve(teams, weekending, valueToDistribute) {
|
||
var multipliers = {};
|
||
var valueSumm = 0;
|
||
|
||
angular.forEach(teams, function (team, index) {
|
||
if (team.QuantityValues && (team.QuantityValues[weekending] !== undefined)) {
|
||
multipliers[team.Id] = team.QuantityValues[weekending];
|
||
valueSumm += multipliers[team.Id];
|
||
}
|
||
else {
|
||
multipliers[team.Id] = 0;
|
||
}
|
||
});
|
||
|
||
if (valueSumm > 0) {
|
||
// Curve exists
|
||
angular.forEach(multipliers, function (value, key) {
|
||
multipliers[key] = value / valueSumm;
|
||
});
|
||
|
||
angular.forEach(teams, function (team, index) {
|
||
if (team.QuantityValues[weekending] === undefined)
|
||
team.QuantityValues[weekending] = 0;
|
||
|
||
team.QuantityValues[weekending] += valueToDistribute * multipliers[team.Id];
|
||
});
|
||
}
|
||
else {
|
||
// No curve, because all teams has Zero values currently allocated
|
||
var teamCount = Object.keys(multipliers).length;
|
||
angular.forEach(teams, function (team, index) {
|
||
if (team.QuantityValues[weekending] === undefined)
|
||
team.QuantityValues[weekending] = 0;
|
||
|
||
if (teamCount > 0) {
|
||
team.QuantityValues[weekending] += valueToDistribute / teamCount;
|
||
}
|
||
});
|
||
}
|
||
}
|
||
|
||
$scope.activateScenario = function (scenId, projId) {
|
||
bootbox.confirm({
|
||
message: "Are you sure you want to use this scenario in the current Mix? Your current scenario will be overwritten.",
|
||
callback: function (result) {
|
||
$scope.$apply(function () {
|
||
if (result) {
|
||
$scope.activateScenarioConfirmed(scenId, projId);
|
||
}
|
||
});
|
||
}
|
||
});
|
||
}
|
||
$scope.activateScenarioConfirmed = function (scenarioId, projectId) {
|
||
var project = $scope.Calendar.Projects[projectId];
|
||
if (!project)
|
||
return;
|
||
|
||
var newScenario = project.InactiveScenarios[scenarioId];
|
||
if (!newScenario)
|
||
return;
|
||
|
||
if (isScenarioLoaded(newScenario)) {
|
||
setActiveScenario(project, newScenario);
|
||
}
|
||
else {
|
||
loadScenarioDetails(newScenario.Id, project.Id, function (expenditures) {
|
||
setScenarioExpenditures(newScenario, expenditures);
|
||
setActiveScenario(project, newScenario);
|
||
});
|
||
}
|
||
};
|
||
function setActiveScenario(project, scenario) {
|
||
if (!project || !project.Scenario || !scenario)
|
||
return;
|
||
|
||
delete project.InactiveScenarios[scenario.Id];
|
||
|
||
var oldScenario = angular.copy(project.Scenario);
|
||
if (isScenarioLoaded(oldScenario)) {
|
||
project.Scenario = angular.extend({}, scenario, { Expenditures: angular.copy(oldScenario.Expenditures) });
|
||
refreshProjectData(project, scenario.Expenditures, true);
|
||
project.InactiveScenarios[oldScenario.Id] = oldScenario;
|
||
|
||
$scope.recreateView();
|
||
}
|
||
else {
|
||
loadScenarioDetails(oldScenario.Id, project.Id, function (expenditures) {
|
||
setScenarioExpenditures(oldScenario, expenditures);
|
||
project.Scenario = angular.extend({}, scenario, { Expenditures: angular.copy(oldScenario.Expenditures) });
|
||
refreshProjectData(project, scenario.Expenditures, true);
|
||
project.InactiveScenarios[oldScenario.Id] = oldScenario;
|
||
|
||
$scope.recreateView();
|
||
});
|
||
}
|
||
};
|
||
function isScenarioLoaded(scenario) {
|
||
// we should return true because otherwise it may be detected as command to load scenario for incorrect project
|
||
if (!scenario)
|
||
return true;
|
||
|
||
// if expenditures do not exist method return false
|
||
return !!scenario.Expenditures && Object.keys(scenario.Expenditures).length > 0;
|
||
};
|
||
}]); |