'use strict'; /* Controllers */ var controllers = angular.module('controllers', ["xeditable"]); controllers.directive('resizeFreez', function($timeout) { return { restrict: 'A', link: function(scope, elem, attrs) { if (scope.$last) { $timeout(function() { //resizeFreez(); scope.resizeFreezAng(); $("#loader").hide(); unblockUI(); }, 0); } } }; }); controllers.controller('scenarioDetailsCalendarController', ['$scope', '$http', '$location', '$timeout', function ($scope, $http, $location, $timeout) { $scope.CollapsedIcon = 'fa-plus-square'; $scope.NonCollapsedIcon = 'fa-minus-square'; $scope.id = $location.absUrl().substr($location.absUrl().lastIndexOf('ls/') + 3, 36); $scope.calendarFilters = { CategoryType: null, LaborMaterials: null, IncomeType: null, GLAccount: null, CreditDepartment: null, SelectedExpCats: null, IsTableModeQuantity: true, IsViewModeMonth: true, IsUOMHours: null, ScenarioId: $scope.id }; $scope.typeOptions = [ { name: 'Quantity', value: 'Quantity' }, { name: 'Cost', value: 'Cost' } ]; $scope.reallocator = { SourceExpCat: null, TargetExpCat: null, SourceWeekEnding: null, TargetWeekEnding: null, Percentage: 100, ReallocateBy: $scope.typeOptions[0].value, CurveType: 'source', OtherCurve: null, }; $scope.pushpuller = { weeks: 1, push: true, resource: null, startdate: null }; $scope.quickPushPull = function (push, weeks) { for (var i = 1; i < $scope.data.ScenarioCalendar.length; i++) { if ($scope.data.ScenarioCalendar[i].Checked) $scope.pushPullExpCat($scope.data.ScenarioCalendar[i], i, weeks, push); } }; $scope.editTotal = { ExpCat: null, SeatsCost: $scope.typeOptions[0].value, CurrentTotal: null, NewTotal: null, Curve: 'source', OtherCurve: null, }; $scope.formatCells = { ExpCats: [], SelectedExpCats: null, StartDate: null, EndDate: null, DecimalPlaces: 2, DecimalCalcCostPlaces: 4, DecimalCalcQtyPlaces: 6 }; $scope.categoryTypes = []; $http.get('/Utils/GetExpentitureCategoryTypes').success(function(data) { $scope.categoryTypes = data; }).error(function(data, status, headers, config) { console.log("an error occurred while loading category types"); }); $scope.CGEFX = []; $http.get('/Utils/GetCGEFX').success(function (data) { $scope.CGEFX = data; }); $scope.incomeTypes = []; $http.get('/Utils/GetIncomeTypes').success(function (data) { $scope.incomeTypes = data; }); $scope.glAccounts = []; $http.get('/Utils/GetGLAccounts').success(function (data) { $scope.glAccounts = data; }); $scope.creditDepartments = []; $http.get('/Utils/GetCreditDepartments').success(function (data) { $scope.creditDepartments = data; }); $scope.availableExpenditures = []; $http.get('/Scenarios/GetScenarioAvailableExpCategories?id=' + $scope.id).success(function (data) { $scope.availableExpenditures = data; if ($scope.availableExpenditures != null && $scope.availableExpenditures.length > 0) { $scope.reallocator.SourceExpCat = $scope.availableExpenditures[0].Id; $scope.reallocator.TargetExpCat = $scope.availableExpenditures.length > 1 ? $scope.availableExpenditures[1].Id : null; $scope.reallocator.OtherCurve = $scope.availableExpenditures[0].Id; $scope.editTotal = { ExpCat: $scope.availableExpenditures[0].Id, OtherCurve: $scope.availableExpenditures[0].Id, SeatsCost: $scope.typeOptions[0].value, Curve: 'source' }; } }); $scope.rates = []; $http.post('/Scenarios/GetRates', { scenarioId: $scope.id, uomMode: $scope.calendarFilters.IsUOMHours }).success(function (responseData) { $scope.rates = responseData; }).error(function (data, status, headers, config) { console.log("an error occurred while loading rates"); }); $scope.data = null; $scope.data2Update = { ScenarioId: $scope.id, ChangedExpCats: [] }; function getExpCatById(ExpCatId) { for (var i = 0; i < $scope.data.ScenarioCalendar.length; i++) { if ($scope.data.ScenarioCalendar[i].ExpCatId == ExpCatId) { return $scope.data.ScenarioCalendar[i]; } } return null; } function getResourceById(resId) { for (var i = 0; i < $scope.data.AllResources.length; i++) { if ($scope.data.AllResources[i].Id == resId) { return $scope.data.AllResources[i]; } } return null; } function clearResource(expCat, resource) { if (resource != null) { resource.GrandTotalQuantity = 0; var scenarioCalendarRow = null; var i; for (i = 0; i < $scope.data.ScenarioCalendar.length; i++) { if ($scope.data.ScenarioCalendar[i].ExpCatId === expCat.ExpCatId) { scenarioCalendarRow = $scope.data.ScenarioCalendar[i]; break; } } for (i = 0; i < $scope.data.Headers.length; i++) { expCat.RestQuantity[i] += resource.QuantityValues[i]; resource.QuantityValues[i] = 0; applyCellChange(expCat.ExpCatId, $scope.data.Headers[i].Milliseconds, scenarioCalendarRow.ScenarioDetailIds[i], resource.QuantityValues[i], 0, resource.Id, false, false); } } } function updateTotals(resource, colIndex, newQuantity, oldQuantity, newCost, oldCost) { for (var i = colIndex + 1; i < $scope.data.Headers.length; i++) { if (!$scope.data.Headers[i].IsMonth) continue; //update month cell resource.QuantityValues[i] += (newQuantity - oldQuantity); resource.GrandTotalQuantity[i] += (newQuantity - oldQuantity); break; } } function allocateResource(resource, expCat, create, allocateRest, reset) { if (resource != null) { resource.GrandTotalQuantity = 0; resource.GrandTotalCost = 0; var scenarioCalendarRow = null; var i; for (i = 0; i < $scope.data.ScenarioCalendar.length; i++) { if ($scope.data.ScenarioCalendar[i].ExpCatId === expCat.ExpCatId) { scenarioCalendarRow = $scope.data.ScenarioCalendar[i]; break; } } for (i = 0; i < $scope.data.Headers.length; i++) { var quantity = create || reset ? 0 : allocateRest ? resource.QuantityValues[i] + expCat.RestQuantity[i] : expCat.QuantityValues[i]; if (create) resource.QuantityValues[i] = 0; expCat.RestQuantity[i] -= quantity - resource.QuantityValues[i]; updateTotals(resource, i, quantity, resource.QuantityValues[i], 0, 0); resource.QuantityValues[i] = quantity; if (!$scope.data.Headers[i].IsMonth) { resource.GrandTotalQuantity += quantity; applyCellChange(expCat.ExpCatId, $scope.data.Headers[i].Milliseconds, scenarioCalendarRow.ScenarioDetailIds[i], resource.QuantityValues[i], resource.CostValues[i], resource.Id, create, false); } } } } $scope.getCalendar = function () { if (changesMade) if (!confirm("Changes have been made. Reloading will cause unsaved data loss.")) { return; } blockUI(); $scope.data = null; var postData = JSON.parse(JSON.stringify($scope.calendarFilters)); $http.post('/Scenarios/LoadJsonScenarioCalendar', postData). success(function (data, status, headers, config) { if (data.Headers.length < 1) { unblockUI(); return; } $scope.data = data; $scope.calendarFilters.IsUOMHours = data.IsUOMHours; $scope.switchViewMode($scope.calendarFilters.IsViewModeMonth); $scope.data2Update = { ScenarioId: $scope.id, ChangedExpCats: [] }; var dec = Math.pow(10, parseInt ($scope.formatCells.DecimalPlaces)); $scope.reallocator.SourceWeekEnding = $scope.data.Headers[0].Title; $scope.reallocator.TargetWeekEnding = $scope.data.Headers[$scope.data.Headers.length - 2].Title; $scope.editTotal.CurrentTotal = Math.round($scope.data.ScenarioCalendar[1].GrandTotalQuantity * dec) / dec; var dt = new Date($scope.data.Headers[0].Milliseconds); $scope.formatCells.StartDate = (dt.getMonth() + 1) + "/" + dt.getDate() + "/" + dt.getFullYear(); if ($scope.data.Headers.length > 3) dt = new Date($scope.data.Headers[$scope.data.Headers.length - 2].Milliseconds); else dt = new Date($scope.data.Headers[$scope.data.Headers.length - 2].Milliseconds); $scope.formatCells.EndDate = (dt.getMonth() + 1) + "/" + dt.getDate() + "/" + dt.getFullYear(); $scope.getResourceData(); $scope.prefillAllocateResource(); $scope.checkedExpenditures = []; applyChecked(); $('#btnSaveChanges').attr('disabled', true); unblockUI(); }). error(function(data, status, headers, config) { // called asynchronously if an error occurs // or server returns response with an error status. console.log("an error occurred while loading calendar"); unblockUI(); }); }; $scope.getRates = function() { $http.post('/Scenarios/GetRates', { scenarioId: $scope.id, uomMode: $scope.calendarFilters.IsUOMHours }).success(function (data) { $scope.rates = data; }).error(function (data, status, headers, config) { console.log("an error occurred while loading rates"); }); }; $scope.onPercentageChange = function(val) { $scope.reallocator.Percentage = val; }; $scope.checkEditable = function(useType) { if (3 === useType) { return false; } return true; }; $scope.checkValue = function (data, rowId, colIndex) { var newValue = parseFloat(data); if (isNaN(newValue)) newValue = 0; if (newValue < 0) { return "Value should not be less than zero"; } var isMonth = $scope.data.Headers[colIndex].IsMonth; for (var i = 0; i < $scope.data.ScenarioCalendar.length; i++) { if ($scope.data.ScenarioCalendar[i].ExpCatId === rowId) { if (isMonth) { var ec = $scope.data.ScenarioCalendar[i]; var ec_val = $scope.calendarFilters.IsTableModeQuantity ? ec.QuantityValues[colIndex] : ec.CostValues[colIndex]; var weeks = 0; for (var j = colIndex - 1; j >= 0 && !$scope.data.Headers[j].IsMonth; j--) { weeks++; } var coef = ec_val > 0 ? newValue / ec_val : 1; for (var j = colIndex - 1; j >= 0 && !$scope.data.Headers[j].IsMonth; j--) { var val = ec_val > 0 ? ($scope.calendarFilters.IsTableModeQuantity ? ec.QuantityValues[j] : ec.CostValues[j]) * coef : newValue/ weeks; if ($scope.calendarFilters.IsTableModeQuantity) { updateQuantityOrCostCell(rowId, i, j, val, null, true); } else { updateQuantityOrCostCell(rowId, i, j, null, val, false); } } } //} else { if ($scope.calendarFilters.IsTableModeQuantity) { updateQuantityOrCostCell(rowId, i, colIndex, newValue, null, true); } else { updateQuantityOrCostCell(rowId, i, colIndex, null, newValue, false); } //} break; } } //required to be false by xeditable grid return false; }; $scope.watchKeyInput = function(t) { $timeout(function() { if (t.$editable.inputEl.select) t.$editable.inputEl.select(); else if (t.$editable.inputEl.setSelectionRange) t.$editable.inputEl.setSelectionRange(0, t.$editable.inputEl.val().length); }, 3); t.$editable.inputEl.on('keydown', function (e) { if (e.which == 9) { //when tab key is pressed e.preventDefault(); var tab2Cell; if (e.shiftKey) {// when shift + tab use with 'onblur' set to 'submit' for automatic submission find the parent of the editable before this one in the markup grab the editable and display it tab2Cell = $(this).parentsUntil('table#table').prevAll(":has(.editable:visible):first").find(".editable:visible:last"); t.$form.$submit(); tab2Cell.click(); } else {// when just tab use with 'onblur' set to 'submit' for automatic submission find the parent of the editable after this one in the markup grab the editable and display it tab2Cell = $(this).parentsUntil('table#table').nextAll(":has(.editable:visible):first").find(".editable:visible:first"); t.$form.$submit(); tab2Cell.click(); } } }); }; $scope.onTxtBlur = function(txt) { txt.$form.$submit(); }; function updateQuantityOrCostCell(expCatId, rowIndex, colIndex, newQuantity, newCost, isUpdateQuantity) { /* isUpdateQuantity - indicates whether to update by quantity or by cost. * true - Quantity will be updated from newQuantity, Cost will be recalculated as newQuantity * rate * false - Cost will be updated from newCost, Quantity will be recalculated as newCost / rate * null - Quantity will be updated from newQuantity, Cost will be updated from newCost */ onDataChanged(); var dec = Math.pow(10, true === isUpdateQuantity ? parseInt($scope.formatCells.DecimalCalcQtyPlaces) : parseInt($scope.formatCells.DecimalCalcCostPlaces)); var oldQuantity = $scope.data.ScenarioCalendar[rowIndex].QuantityValues[colIndex]; var oldCost = $scope.data.ScenarioCalendar[rowIndex].CostValues[colIndex]; if (true === isUpdateQuantity) { // update quantity, recalculate cost based on exp cat rate if (oldQuantity === newQuantity || newQuantity < 0) return; newCost = Math.round(newQuantity * $scope.getRate(expCatId, $scope.data.Headers[colIndex].Milliseconds) * dec) / dec; } else if (false === isUpdateQuantity) { // update cost, recalculate quantity based on exp cat rate if (oldCost === newCost || newCost < 0) return; newQuantity = Math.round(newCost / $scope.getRate(expCatId, $scope.data.Headers[colIndex].Milliseconds) * dec) / dec; } // update modified cell $scope.data.ScenarioCalendar[rowIndex].RestQuantity[colIndex] += newQuantity - $scope.data.ScenarioCalendar[rowIndex].QuantityValues[colIndex]; $scope.data.ScenarioCalendar[rowIndex].CostValues[colIndex] = newCost; $scope.data.ScenarioCalendar[rowIndex].QuantityValues[colIndex] = newQuantity; // update row grand totals $scope.data.ScenarioCalendar[rowIndex].GrandTotalCost = calcWithSamePresicion($scope.data.ScenarioCalendar[rowIndex].GrandTotalCost, (newCost - oldCost), true); $scope.data.ScenarioCalendar[rowIndex].GrandTotalQuantity = calcWithSamePresicion($scope.data.ScenarioCalendar[rowIndex].GrandTotalQuantity, (newQuantity - oldQuantity), false); // update column totals $scope.data.ScenarioCalendar[0].CostValues[colIndex] += (newCost - oldCost); $scope.data.ScenarioCalendar[0].QuantityValues[colIndex] += (newQuantity - oldQuantity); // update table totals $scope.data.ScenarioCalendar[0].GrandTotalCost += (newCost - oldCost); $scope.data.ScenarioCalendar[0].GrandTotalQuantity += (newQuantity - oldQuantity); // update month cell and month total cell for (var i = colIndex + 1; i < $scope.data.Headers.length; i++) { if (!$scope.data.Headers[i].IsMonth) continue; //update month cell $scope.data.ScenarioCalendar[rowIndex].QuantityValues[i] = calcWithSamePresicion($scope.data.ScenarioCalendar[rowIndex].QuantityValues[i], (newQuantity - oldQuantity), false); $scope.data.ScenarioCalendar[rowIndex].CostValues[i] = calcWithSamePresicion($scope.data.ScenarioCalendar[rowIndex].CostValues[i], (newCost - oldCost), true); // update month column totals $scope.data.ScenarioCalendar[0].CostValues[i]= calcWithSamePresicion($scope.data.ScenarioCalendar[0].CostValues[i], (newCost - oldCost), true); $scope.data.ScenarioCalendar[0].QuantityValues[i]= calcWithSamePresicion($scope.data.ScenarioCalendar[0].QuantityValues[i], (newQuantity - oldQuantity), false); break; } applyCellChange(expCatId, $scope.data.Headers[colIndex].Milliseconds, $scope.data.ScenarioCalendar[rowIndex].ScenarioDetailIds[colIndex], $scope.data.ScenarioCalendar[rowIndex].QuantityValues[colIndex], $scope.data.ScenarioCalendar[rowIndex].CostValues[colIndex], null, false, false); } function applyCellChange(rowId, cellMilliseconds, cellId, cellQuantity, cellCost, resId, isAdded, isRemoved) { var catIndex = -1; for (var ix = 0; ix < $scope.data2Update.ChangedExpCats.length; ix++) { if ($scope.data2Update.ChangedExpCats[ix].Id == rowId) { catIndex = ix; break; } } if (catIndex == -1) { $scope.data2Update.ChangedExpCats[$scope.data2Update.ChangedExpCats.length] = { Id: rowId, Values: [], Resources: [] }; catIndex = $scope.data2Update.ChangedExpCats.length - 1; } if (resId == null) { var dateIndex = -1; for (var dx = 0; dx < $scope.data2Update.ChangedExpCats[catIndex].Values.length; dx++) { if ($scope.data2Update.ChangedExpCats[catIndex].Values[dx].Id == cellId) { dateIndex = dx; break; } } if (dateIndex == -1) { $scope.data2Update.ChangedExpCats[catIndex].Values[$scope.data2Update.ChangedExpCats[catIndex].Values.length] = { Id: cellId, Milliseconds: cellMilliseconds, Values: [] }; dateIndex = $scope.data2Update.ChangedExpCats[catIndex].Values.length - 1; } $scope.data2Update.ChangedExpCats[catIndex].Values[dateIndex].Cost = cellCost; $scope.data2Update.ChangedExpCats[catIndex].Values[dateIndex].Quantity = cellQuantity; } else { var dateIndex = -1; for (var dx = 0; dx < $scope.data2Update.ChangedExpCats[catIndex].Resources.length; dx++) { if ($scope.data2Update.ChangedExpCats[catIndex].Resources[dx].Id == resId) { dateIndex = dx; break; } } if (dateIndex == -1) { $scope.data2Update.ChangedExpCats[catIndex].Resources[$scope.data2Update.ChangedExpCats[catIndex].Resources.length] = { Id: resId, Values: [], IsAdded: isAdded, IsRemoved: isRemoved }; dateIndex = $scope.data2Update.ChangedExpCats[catIndex].Resources.length - 1; if (isAdded || isRemoved) return; } else { if (isRemoved){ if ($scope.data2Update.ChangedExpCats[catIndex].Resources[dateIndex].IsAdded) { $scope.data2Update.ChangedExpCats[catIndex].Resources.splice(dateIndex, 1); } return; } } var dateIndex1 = -1; for (var dx = 0; dx < $scope.data2Update.ChangedExpCats[catIndex].Resources[dateIndex].Values.length; dx++) { if ($scope.data2Update.ChangedExpCats[catIndex].Resources[dateIndex].Values[dx].Id == cellId) { dateIndex1 = dx; break; } } if (dateIndex1 == -1) { $scope.data2Update.ChangedExpCats[catIndex].Resources[dateIndex].Values[$scope.data2Update.ChangedExpCats[catIndex].Resources[dateIndex].Values.length] = { Id: cellId, Milliseconds: cellMilliseconds, Values: [] }; dateIndex1 = $scope.data2Update.ChangedExpCats[catIndex].Resources[dateIndex].Values.length - 1; } $scope.data2Update.ChangedExpCats[catIndex].Resources[dateIndex].Values[dateIndex1].Quantity = cellQuantity; } $('#btnSaveChanges').removeAttr('disabled'); } function calcWithSamePresicion(val1, val2, isCost) { var dec = Math.pow(10, parseInt(isCost ? $scope.formatCells.DecimalCalcCostPlaces : $scope.formatCells.DecimalCalcQtyPlaces)); return Math.round(val1 * dec) / dec + Math.round(val2 * dec)/dec; }; $scope.saveChanges = function () { blockUI(); onDataSaved(); var postData = JSON.parse(JSON.stringify($scope.data2Update)); postData.ScenarioFilters = $scope.calendarFilters; $http.post('/Scenarios/SaveChanges', postData). success(function (data, status, headers, config) { //$scope.data = data; $scope.data2Update = { ScenarioId: $scope.id, ChangedExpCats: [] }; //alert("data saved successfully"); var tab = $("#uidemo-tabs-default-demo li.active a").attr("href"); var retUrl = window.location.href; if (retUrl.indexOf("&tab=") > 0) { retUrl = retUrl.substr(0, retUrl.indexOf("&tab=")); } if (retUrl.indexOf("?") > 0) { window.location.href = retUrl + "&tab=" + tab.replace("#", ""); } else { window.location.href = retUrl + "?tab=" + tab.replace("#", ""); } }). error(function (data, status, headers, config) { console.log("an error occurred while saving calendar changes"); unblockUI(); }); }; $scope.getRate = function (expCatId, date) { if ($scope.rates && $scope.rates.length > 0) { for (var i = 0; i < $scope.rates.length; i++) { if ($scope.rates[i].expCatId === expCatId) { if ($scope.rates[i].rateValues && $scope.rates[i].rateValues.length > 0) { for (var rIndex = 0; rIndex < $scope.rates[i].rateValues.length; rIndex++) { if (date <= $scope.rates[i].rateValues[rIndex].weekEndDate) return $scope.rates[i].rateValues[rIndex].rateValue; } return $scope.rates[i].rateValues[$scope.rates[i].rateValues.length - 1].rateValue; } break; } } } return 0; }; $scope.graphData = []; $scope.maxGraphValue = 0; $scope.showGraph = function() { // prepare data $scope.maxGraphValue = 0; $scope.graphData = []; var startDate = new Date($scope.data.StartDate.replace('/Date(', '').replace(')/', '') * 1); var endDate = new Date($scope.data.EndDate.replace('/Date(', '').replace(')/', '') * 1); var days = (endDate - startDate) / (1000 * 60 * 60 * 24); var i; if (days <= 400) { for (i = 1; i < $scope.data.ScenarioCalendar.length; i++) { // skip this row if it is not checked if (!$scope.data.ScenarioCalendar[i].Checked) continue; var expCatData = new Array(); for (var colIndex = 0; colIndex < $scope.data.Headers.length; colIndex++) { if ($scope.data.Headers[colIndex].IsMonth) continue; expCatData[expCatData.length] = [$scope.data.Headers[colIndex].Milliseconds, $scope.data.ScenarioCalendar[i].QuantityValues[colIndex]]; if (true !== $scope.calendarFilters.IsTableModeQuantity) { expCatData[expCatData.length - 1][1] = $scope.data.ScenarioCalendar[i].CostValues[colIndex]; } if (expCatData[expCatData.length - 1][1] > $scope.maxGraphValue) $scope.maxGraphValue = expCatData[expCatData.length - 1][1]; expCatData[expCatData.length - 1][1] = expCatData[expCatData.length - 1][1].toFixed(2); } $scope.graphData[$scope.graphData.length] = { label: $scope.data.ScenarioCalendar[i].ExpCatName, data: expCatData, lines: { show: true, fill: true, steps: false }, filledPoints: true, stack: true }; } } else { $.get('/Scenarios/GetMonthEndDates', { 'StartDate': startDate.getMonth() + 1 + '/' + startDate.getDate() + '/' + startDate.getFullYear(), 'EndDate': endDate.getMonth() + 1 + '/' + endDate.getDate() + '/' + endDate.getFullYear() }).done(function(data) { var dates = data; for (i = 1; i < $scope.data.ScenarioCalendar.length; i++) { // skip this row if it is not checked if (!$scope.data.ScenarioCalendar[i].Checked) continue; var expCatData = new Array(); var currentIndex = 0; for (var colIndex = 0; colIndex < $scope.data.Headers.length; colIndex++) { if ($scope.data.Headers[colIndex].IsMonth) continue; for (var index = 0; index < dates.length; index++) { if (dates[index] == $scope.data.Headers[colIndex].Milliseconds) { expCatData[currentIndex] = [$scope.data.Headers[colIndex].Milliseconds, $scope.data.ScenarioCalendar[i].QuantityValues[colIndex]]; if (true !== $scope.calendarFilters.IsTableModeQuantity) { expCatData[currentIndex][1] = $scope.data.ScenarioCalendar[i].CostValues[colIndex]; } if (expCatData[currentIndex][1] > $scope.maxGraphValue) $scope.maxGraphValue = expCatData[currentIndex][1]; expCatData[currentIndex][1] = expCatData[currentIndex][1].toFixed(2); currentIndex++; } } } $scope.graphData[$scope.graphData.length] = { label: $scope.data.ScenarioCalendar[i].ExpCatName, data: expCatData, lines: { show: true, fill: true, steps: false }, filledPoints: true, stack: true }; } }); } // show graph $('#DetailsGraphContainer').html('
'); $("#modalGraph").modal("show"); setTimeout($scope.initGraph, 500); }; $scope.initGraph = function() { // Init Chart $('#divDetailsGraph').pixelPlot($scope.graphData, { //var plot = $.plot($('#jq-flot-graph2'), graphData, { series: { points: { show: false }, lines: { show: true, } }, xaxis: { mode: "time", tickSize: [1, "month"], tickLength: 0, axisLabel: "time", axisLabelUseCanvas: true, axisLabelFontSizePixels: 12, axisLabelFontFamily: 'Verdana, Arial', axisLabelPadding: 10 }, yaxis: { //tickSize: $scope.maxGraphValue / 5 } }, { height: 405, width: 405, tooltipText: "y + ' units at ' + (new Date(x)).toString()" }); }; $scope.checkedExpenditures = []; $scope.collectchecked = function(obj) { $scope.checkedExpenditures = []; if ($scope.data == null || $scope.data.ScenarioCalendar == null || obj == null) return; for (var i = 1; i < $scope.data.ScenarioCalendar.length; i++) { if (($scope.data.ScenarioCalendar[i].Checked && obj.ExpCatId != $scope.data.ScenarioCalendar[i].ExpCatId) || (!obj.Checked && obj.ExpCatId == $scope.data.ScenarioCalendar[i].ExpCatId)) $scope.checkedExpenditures.push($scope.data.ScenarioCalendar[i]); } applyChecked(); }; $scope.checkAll = function () { $scope.checkedExpenditures = []; var checked = !$scope.data.ScenarioCalendar[0].Checked; if ($scope.data == null || $scope.data.ScenarioCalendar == null) return; for (var i = 1; i < $scope.data.ScenarioCalendar.length; i++) { $scope.data.ScenarioCalendar[i].Checked = checked; if (checked) $scope.checkedExpenditures.push($scope.data.ScenarioCalendar[i]); } applyChecked(); }; function applyChecked() { if ($scope.checkedExpenditures.length > 0) { $('#btnPushPull').removeAttr('disabled'); $('#btnFormatCells').removeAttr('disabled'); $('#btnShowGraph').removeAttr('disabled'); } else { $('#btnPushPull').attr('disabled', true); $('#btnFormatCells').attr('disabled', true); $('#btnShowGraph').attr('disabled', true); } $scope.data.ScenarioCalendar[0].Checked = ($scope.checkedExpenditures.length == ($scope.data.ScenarioCalendar.length - 1)); } $scope.resizeFreezAng = function() { setTimeout(function() { resizeFreez(); }, 0); }; $scope.takeAll = function (resId, expCatId) { if (confirm("Are you sure you want to re-assign category quantity and assign everything to this resource?")) { var expCat = getExpCatById(expCatId); var resource = null; for (var i = 0; i < expCat.Resources.length; i++) { if (expCat.Resources[i].Id == resId) { resource = expCat.Resources[i]; }else{ clearResource(expCat, expCat.Resources[i]); } } allocateResource(resource, expCat, false, false, false); } }; $scope.takeRemaining = function (resId, expCatId) { var expCat = getExpCatById(expCatId); if (expCat != null) { for (var i = 0; i < expCat.Resources.length; i++) { if (expCat.Resources[i].Id == resId) { allocateResource(expCat.Resources[i], expCat, false, true, false); return; } } } }; $scope.zeroResource = function (resId, expCatId) { if (confirm("Are you sure you want fill this resource quantities with 0s?")) { var expCat = getExpCatById(expCatId); for (var i = 0; i < expCat.Resources.length; i++) { if (expCat.Resources[i].Id == resId) { allocateResource(expCat.Resources[i], expCat, false, false, true); return; } } } }; $scope.assignResource = function (resId, expCatId, $event) { if (resId == null || expCatId == null) return; var resource = getResourceById(resId); if (resource != null) { var expCat = getExpCatById(expCatId); if (expCat != null) { for (var j = 0; j < expCat.Resources.length; j++) { if (expCat.Resources[j].Id == resource.Id) { alert("The " + resource.Name + " has been assigned to the " + expCat.Name); break; } } resource = { 'Id': resource.Id, 'Name': resource.Name, 'QuantityValues': new Array($scope.data.Headers.length), 'CostValues': new Array($scope.data.Headers.length) }; } allocateResource(resource, expCat, true, false, false); expCat.Resources.push(resource); var tr = $($event.target).parentsUntil('table').last().parent().find('thead tr:first'); console.log("tr: " + $(tr).html()); var thName = tr.find('th:eq(1)'); console.log("thName: " + $(thName).html()); resource.width = thName.width() + 'px'; var thTot = tr.find('th:eq(2)'); console.log("thTot: " + $(thTot).html()); resource.left = thTot.css('left'); $scope.resizeFreezAng(); } }; $scope.removeResource = function (resId, expCatId) { if (confirm("Are you sure you want to remove this resource?")) { var expCat = getExpCatById(expCatId); for (var i = 0; i < expCat.Resources.length; i++) { if (expCat.Resources[i].Id == resId) { expCat.Resources.splice(i, 1); applyCellChange(expCatId, 0, 0, 0, 0, resId, false, true); $scope.resizeFreezAng(); return; } } } }; $scope.checkResourceValue = function (data, expCatId, resId, colIndex) { var newValue = parseFloat(data); if (newValue < 0) { return "Value should not be less than zero"; } var isMonth = $scope.data.Headers[colIndex].IsMonth; var expCat = getExpCatById(expCatId); var resource = null; for (var i = 0; i < expCat.Resources.length; i++) { if (expCat.Resources[i].Id == resId) { resource = expCat.Resources[i]; break; } } var scenarioCalendarRow = null; for (var i = 0; i < $scope.data.ScenarioCalendar.length; i++) { if ($scope.data.ScenarioCalendar[i].ExpCatId === expCatId) { scenarioCalendarRow = $scope.data.ScenarioCalendar[i]; break; } } if (expCat != null && resource != null) { if ($scope.calendarFilters.IsTableModeQuantity) { if (isMonth) { var ec_val = resource.QuantityValues[colIndex]; var weeks = 0; for (var j = colIndex - 1; j >= 0 && !$scope.data.Headers[j].IsMonth; j--) { weeks++; } var coef = ec_val > 0 ? newValue / ec_val : 1; for (var j = colIndex - 1; j >= 0 && !$scope.data.Headers[j].IsMonth; j--) { var val = ec_val > 0 ? resource.QuantityValues[j] * coef : newValue / weeks; resource.GrandTotalQuantity += val - resource.QuantityValues[j]; updateTotals(resource, j, val, resource.QuantityValues[j], 0, 0); expCat.RestQuantity[j] -= val - resource.QuantityValues[j]; resource.QuantityValues[j] = val; applyCellChange(expCatId, $scope.data.Headers[j].Milliseconds, scenarioCalendarRow.ScenarioDetailIds[j], resource.QuantityValues[j], 0, resId, false, false); } } else { resource.GrandTotalQuantity += newValue - resource.QuantityValues[colIndex]; updateTotals(resource, colIndex, newValue, resource.QuantityValues[colIndex], 0, 0); expCat.RestQuantity[colIndex] -= newValue - resource.QuantityValues[colIndex]; resource.QuantityValues[colIndex] = newValue; applyCellChange(expCatId, $scope.data.Headers[colIndex].Milliseconds, scenarioCalendarRow.ScenarioDetailIds[colIndex], resource.QuantityValues[colIndex], 0, resId, false, false); } } } //required to be false by xeditable grid return false; }; $scope.getExpCatResources = function(expCat) { var resources = []; for (var i = 0; i < $scope.data.AllResources.length; i++) { if ($scope.data.AllResources[i].ExpedentureCategoryId == expCat.ExpCatId && $scope.data.AllResources[i].IsActiveEmployee) { var isFound = false; for (var j = 0; j < expCat.Resources.length; j++) { if (expCat.Resources[j].Id == $scope.data.AllResources[i].Id) { isFound = true; break; } } if (!isFound) resources.push($scope.data.AllResources[i]); } } return resources; }; $scope.switchViewMode = function (value) { var newValue = value != null ? value : !$scope.calendarFilters.IsViewModeMonth; $scope.calendarFilters.IsViewModeMonth = newValue; for (var i = 0; i < $scope.data.Headers.length; i++) { if ($scope.data.Headers[i].IsMonth) { $scope.data.Headers[i].Collapsed = newValue; $scope.data.Headers[i].Show = newValue; $scope.data.Headers[i].CollapsedClass = newValue ? $scope.CollapsedIcon : $scope.NonCollapsedIcon; } else { $scope.data.Headers[i].Collapsed = newValue; $scope.data.Headers[i].Show = !newValue; } } $scope.resizeFreezAng(); }; $scope.switchUOMMode = function (value) { if (autoclick) { autoclick = false; return; } if (changesMade) if (!confirm("Changes have been made. Changing UOM will cause unsaved data loss.")) { autoclick = true; window.setTimeout(toggleUomMode, 10); return; } onDataSaved(); var newValue = value != null ? value : !$scope.calendarFilters.IsUOMHours; $scope.calendarFilters.IsUOMHours = newValue; $scope.getCalendar(); $scope.getRates(); }; $scope.switchTableMode = function (value) { var newValue = value != null ? value : !$scope.calendarFilters.IsTableModeQuantity; $scope.calendarFilters.IsTableModeQuantity = newValue; }; $scope.reallocateResource = function () { var sourceQuantity = 0; var sourceCost = 0; if ($scope.reallocator.Percentage == 0) { alert('Percentage is 0, nothing to reallocate.'); return; } var sourceRowIndex, targetRowIndex, curveRowIndex, sourceColIndex, targetColIndex; var i; for (i = 0; i < $scope.data.ScenarioCalendar.length; i++) { if ($scope.data.ScenarioCalendar[i].ExpCatId === $scope.reallocator.SourceExpCat) sourceRowIndex = i; if ($scope.data.ScenarioCalendar[i].ExpCatId === $scope.reallocator.TargetExpCat) targetRowIndex = i; if ($scope.reallocator.CurveType == 'other' && $scope.data.ScenarioCalendar[i].ExpCatId === $scope.reallocator.OtherCurve) curveRowIndex = i; } if (sourceRowIndex == targetRowIndex) { alert('Cant reallocate within same expenditure category.'); return; } for (i = 0; i < $scope.data.Headers.length; i++) { if ($scope.data.Headers[i].IsMonth) continue; if ($scope.data.Headers[i].Title === $scope.reallocator.SourceWeekEnding) sourceColIndex = i; if ($scope.data.Headers[i].Title === $scope.reallocator.TargetWeekEnding) targetColIndex = i; } if (sourceColIndex == null || targetColIndex == null) { alert('Please check selected dates to match valid week endings.'); return; } else if (targetColIndex < sourceColIndex) { alert('Please select To week ending date later than From week ending date.'); return; } var sourceSumCost = 0, sourceSumQuantity = 0, targetSumCost = 0, targetSumQuantity = 0, otherSumCost = 0, otherSumQuantity = 0; var percentageMult = $scope.reallocator.Percentage / 100; for (i = sourceColIndex; i <= targetColIndex ; i++) { if ($scope.data.Headers[i].IsMonth) continue; sourceSumQuantity += $scope.data.ScenarioCalendar[sourceRowIndex].QuantityValues[i]; sourceSumCost += $scope.data.ScenarioCalendar[sourceRowIndex].CostValues[i]; targetSumQuantity += $scope.data.ScenarioCalendar[targetRowIndex].QuantityValues[i]; targetSumCost += $scope.data.ScenarioCalendar[targetRowIndex].CostValues[i]; if ($scope.reallocator.CurveType == 'other') { otherSumQuantity += $scope.data.ScenarioCalendar[curveRowIndex].QuantityValues[i]; otherSumCost += $scope.data.ScenarioCalendar[curveRowIndex].CostValues[i]; } } if ((sourceSumQuantity == 0 || sourceSumCost == 0)) { alert('Source is zero, nothing to reallocate.'); return; } if ((targetSumQuantity == 0 || targetSumCost == 0) && $scope.reallocator.CurveType == 'target') { alert('Target curve is zero. Please select a different curve.'); return; } if ((otherSumQuantity == 0 || otherSumCost == 0) && $scope.reallocator.CurveType == 'other') { alert('Different curve is zero. Please select a another curve.'); return; } var quantity2Move = 0; var cost2Move = 0; for (i = sourceColIndex; i <= targetColIndex ; i++) { if ($scope.data.Headers[i].IsMonth) continue; if ($scope.reallocator.CurveType == 'source') { sourceQuantity = $scope.data.ScenarioCalendar[sourceRowIndex].QuantityValues[i]; sourceCost = $scope.data.ScenarioCalendar[sourceRowIndex].CostValues[i]; // update column values in model if ($scope.reallocator.ReallocateBy == 'Quantity') { updateQuantityOrCostCell($scope.reallocator.SourceExpCat, sourceRowIndex, i, sourceQuantity * (1 - percentageMult), sourceCost * (1 - percentageMult), null); updateQuantityOrCostCell($scope.reallocator.TargetExpCat, targetRowIndex, i, $scope.data.ScenarioCalendar[targetRowIndex].QuantityValues[i] + sourceQuantity * percentageMult, null, true); } else { updateQuantityOrCostCell($scope.reallocator.SourceExpCat, sourceRowIndex, i, sourceQuantity * (1 - percentageMult), sourceCost * (1 - percentageMult), null); updateQuantityOrCostCell($scope.reallocator.TargetExpCat, targetRowIndex, i, null, $scope.data.ScenarioCalendar[targetRowIndex].CostValues[i] + sourceCost * percentageMult, false); } } else if ($scope.reallocator.CurveType == 'target') { sourceQuantity = $scope.data.ScenarioCalendar[sourceRowIndex].QuantityValues[i]; sourceCost = $scope.data.ScenarioCalendar[sourceRowIndex].CostValues[i]; quantity2Move = (percentageMult * sourceSumQuantity) * ($scope.data.ScenarioCalendar[targetRowIndex].QuantityValues[i] / targetSumQuantity); cost2Move = (percentageMult * sourceSumCost) * ($scope.data.ScenarioCalendar[targetRowIndex].CostValues[i] / targetSumCost); // update column values in model if ($scope.reallocator.ReallocateBy == 'Quantity') { // we cannot reallocate more than source quantity if (quantity2Move > $scope.data.ScenarioCalendar[sourceRowIndex].QuantityValues[i]) quantity2Move = $scope.data.ScenarioCalendar[sourceRowIndex].QuantityValues[i]; if (quantity2Move > 0) { updateQuantityOrCostCell($scope.reallocator.SourceExpCat, sourceRowIndex, i, sourceQuantity * (1 - percentageMult), null, true); updateQuantityOrCostCell($scope.reallocator.TargetExpCat, targetRowIndex, i, $scope.data.ScenarioCalendar[targetRowIndex].QuantityValues[i] + quantity2Move, null, true); } } else { // we cannot reallocate more than source quantity if (cost2Move > $scope.data.ScenarioCalendar[sourceRowIndex].CostValues[i]) cost2Move = $scope.data.ScenarioCalendar[sourceRowIndex].CostValues[i]; if (cost2Move > 0) { updateQuantityOrCostCell($scope.reallocator.SourceExpCat, sourceRowIndex, i, null, sourceCost * (1 - percentageMult), false); updateQuantityOrCostCell($scope.reallocator.TargetExpCat, targetRowIndex, i, null, $scope.data.ScenarioCalendar[targetRowIndex].CostValues[i] + cost2Move, false); } } } else if ($scope.reallocator.CurveType == 'other') { sourceQuantity = $scope.data.ScenarioCalendar[sourceRowIndex].QuantityValues[i]; sourceCost = $scope.data.ScenarioCalendar[sourceRowIndex].CostValues[i]; quantity2Move = (percentageMult * sourceSumQuantity) * ($scope.data.ScenarioCalendar[curveRowIndex].QuantityValues[i] / otherSumQuantity); cost2Move = (percentageMult * sourceSumCost) * ($scope.data.ScenarioCalendar[curveRowIndex].CostValues[i] / otherSumCost); // update column values in model if ($scope.reallocator.ReallocateBy == 'Quantity') { // we cannot reallocate more than source quantity if (quantity2Move > $scope.data.ScenarioCalendar[sourceRowIndex].QuantityValues[i]) quantity2Move = $scope.data.ScenarioCalendar[sourceRowIndex].QuantityValues[i]; if (quantity2Move > 0) { updateQuantityOrCostCell($scope.reallocator.SourceExpCat, sourceRowIndex, i, sourceQuantity * (1 - percentageMult), null, true); updateQuantityOrCostCell($scope.reallocator.TargetExpCat, targetRowIndex, i, $scope.data.ScenarioCalendar[targetRowIndex].QuantityValues[i] + quantity2Move, null, true); } } else { // we cannot reallocate more than source quantity if (cost2Move > $scope.data.ScenarioCalendar[sourceRowIndex].CostValues[i]) cost2Move = $scope.data.ScenarioCalendar[sourceRowIndex].CostValues[i]; if (cost2Move > 0) { updateQuantityOrCostCell($scope.reallocator.SourceExpCat, sourceRowIndex, i, null, sourceCost * (1 - percentageMult), false); updateQuantityOrCostCell($scope.reallocator.TargetExpCat, targetRowIndex, i, null, $scope.data.ScenarioCalendar[targetRowIndex].CostValues[i] + cost2Move, false); } } } } $("#reallocator").modal("hide"); }; $scope.pushPull = function () { for (var i = 1; i < $scope.data.ScenarioCalendar.length; i++) { if($scope.data.ScenarioCalendar[i].Checked) $scope.pushPullExpCat($scope.data.ScenarioCalendar[i], i, $scope.pushpuller.weeks, $scope.pushpuller.push); } $("#push_pull").modal("hide"); }; $scope.pushPullExpCat = function (data, sourceRowIndex, weeks, push) { var oldQuantity = 0; var oldCost = 0; var i; var j; var handledWeeks = 0; var leftMargin; // index of the cell next to the last modified cell from the left side of the table. E.g. if we push 2 weeks, then it will be [2], or [3] if there is only one week in the first month var rightMargin; // index of the cell previous to the first modified cell from the right side of the table. E.g. if we pull 2 weeks, then it will be [length-3], or [length-4] if there is only one week in the last month for (i = 0; i < $scope.data.Headers.length; i++) { if ($scope.data.Headers[i].IsMonth) continue; handledWeeks++; if (handledWeeks == weeks) { leftMargin = i + 1; break; } } handledWeeks = 0; for (i = $scope.data.Headers.length-1; i >= 0; i--) { if ($scope.data.Headers[i].IsMonth) continue; if (i < leftMargin) { alert('Error occurred while calculating right margin of required changes.'); return; } handledWeeks++; if (handledWeeks == weeks) { rightMargin = i - 1; break; } } if (push) { for (i = $scope.data.Headers.length - 1; i >= 0; i--) { if ($scope.data.Headers[i].IsMonth) continue; oldQuantity = 0; oldCost = 0; if (i < leftMargin) { if ($scope.calendarFilters.IsTableModeQuantity) { updateQuantityOrCostCell(data.ExpCatId, sourceRowIndex, i, 0, null, true); } else { updateQuantityOrCostCell(data.ExpCatId, sourceRowIndex, i, null, 0, false); } } else { //copy data from [i+weeks] cell handledWeeks = 0; for (j = i - 1; j >= 0; j--) { if ($scope.data.Headers[j].IsMonth) continue; handledWeeks++; if (handledWeeks == weeks) { oldQuantity = data.QuantityValues[j]; oldCost = data.CostValues[j]; break; } } if ($scope.calendarFilters.IsTableModeQuantity) { updateQuantityOrCostCell(data.ExpCatId, sourceRowIndex, i, oldQuantity, null, true); } else { updateQuantityOrCostCell(data.ExpCatId, sourceRowIndex, i, null, oldCost, false); } } } } else { for (i = 0; i < $scope.data.Headers.length; i++) { if ($scope.data.Headers[i].IsMonth) continue; oldQuantity = 0; oldCost = 0; if (i <= rightMargin) { //copy data from [i+weeks] cell handledWeeks = 0; for (j = i + 1; j < $scope.data.Headers.length; j++) { if ($scope.data.Headers[j].IsMonth) continue; handledWeeks++; if (handledWeeks == weeks) { oldQuantity = data.QuantityValues[j]; oldCost = data.CostValues[j]; break; } } if ($scope.calendarFilters.IsTableModeQuantity) { updateQuantityOrCostCell(data.ExpCatId, sourceRowIndex, i, oldQuantity, null, true); } else { updateQuantityOrCostCell(data.ExpCatId, sourceRowIndex, i, null, oldCost, false); } } else if (i > rightMargin) { if ($scope.calendarFilters.IsTableModeQuantity) { updateQuantityOrCostCell(data.ExpCatId, sourceRowIndex, i, 0, null, true); } else { updateQuantityOrCostCell(data.ExpCatId, sourceRowIndex, i, null, 0, false); } } } } }; $scope.editTotalFunc = function () { var oldQuantity = 0; var oldCost = 0; if ($scope.editTotal.CurrentTotal == $scope.editTotal.NewTotal && $scope.editTotal.Curve != 'other') { $("#editTotal").modal("hide"); return; } var changeMult = $scope.editTotal.NewTotal / $scope.editTotal.CurrentTotal; var sourceRowIndex = null; var curveRowIndex = null; var i; for (i = 0; i < $scope.data.ScenarioCalendar.length; i++) { if ($scope.data.ScenarioCalendar[i].ExpCatId === $scope.editTotal.ExpCat) sourceRowIndex = i; if ($scope.data.ScenarioCalendar[i].ExpCatId === $scope.editTotal.OtherCurve) curveRowIndex = i; } if (null === sourceRowIndex) { alert('Cannot find expenditure category to update.'); return; } if (null === curveRowIndex) { alert('Cannot find expenditure category to use as a curve.'); return; } var sourceSumCost = 0, sourceSumQuantity = 0, curveSumCost = 0, curveSumQuantity = 0; for (i = 0; i < $scope.data.Headers.length ; i++) { if ($scope.data.Headers[i].IsMonth) continue; sourceSumQuantity += $scope.data.ScenarioCalendar[sourceRowIndex].QuantityValues[i]; sourceSumCost += $scope.data.ScenarioCalendar[sourceRowIndex].CostValues[i]; curveSumQuantity += $scope.data.ScenarioCalendar[curveRowIndex].QuantityValues[i]; curveSumCost += $scope.data.ScenarioCalendar[curveRowIndex].CostValues[i]; } if (curveSumQuantity == 0 || sourceSumQuantity == 0) { changeMult = $scope.editTotal.NewTotal; } //console.log("sourceSumQuantity: " + sourceSumQuantity + "; curveSumQuantity: " + curveSumQuantity + "; changeMult: " + changeMult); var numberOfWeeks = 0; for (i = 0; i < $scope.data.Headers.length; i++) { if ($scope.data.Headers[i].IsMonth) continue; numberOfWeeks++; } if (curveSumQuantity > 0) for (i = 0; i < $scope.data.Headers.length; i++) { if ($scope.data.Headers[i].IsMonth) continue; oldQuantity = $scope.data.ScenarioCalendar[sourceRowIndex].QuantityValues[i]; oldCost = $scope.data.ScenarioCalendar[sourceRowIndex].CostValues[i]; if ($scope.editTotal.Curve == 'other') { var targetQuantity = $scope.data.ScenarioCalendar[curveRowIndex].QuantityValues[i]; var targetCost = $scope.data.ScenarioCalendar[curveRowIndex].CostValues[i]; if (sourceSumQuantity != 0) updateQuantityOrCostCell($scope.editTotal.ExpCat, sourceRowIndex, i, Math.abs(sourceSumQuantity * (targetQuantity / curveSumQuantity) * changeMult), Math.abs(sourceSumCost * (targetCost / curveSumCost) * changeMult), null); else updateQuantityOrCostCell($scope.editTotal.ExpCat, sourceRowIndex, i, Math.abs((targetQuantity / curveSumQuantity) * changeMult), Math.abs(sourceSumCost * (targetCost / curveSumCost) * changeMult), null); } else updateQuantityOrCostCell($scope.editTotal.ExpCat, sourceRowIndex, i, Math.abs(oldQuantity * changeMult), Math.abs(oldCost * changeMult), null); } else { for (i = 0; i < $scope.data.Headers.length; i++) { if ($scope.data.Headers[i].IsMonth) continue; updateQuantityOrCostCell($scope.editTotal.ExpCat, sourceRowIndex, i, Math.abs(changeMult / numberOfWeeks), Math.abs(changeMult * $scope.getRate($scope.editTotal.ExpCat, $scope.data.Headers[i].Milliseconds) / numberOfWeeks), null); } } $scope.editTotal.CurrentTotal = $scope.editTotal.NewTotal; $("#editTotal").modal("hide"); }; $scope.recalcTotal = function () { var dec = Math.pow(10, parseInt($scope.formatCells.DecimalPlaces)); for (var i = 0; i < $scope.data.ScenarioCalendar.length; i++) { if ($scope.data.ScenarioCalendar[i].ExpCatId === $scope.editTotal.ExpCat) { if ($scope.editTotal.SeatsCost == 'Quantity') $scope.editTotal.CurrentTotal = Math.round($scope.data.ScenarioCalendar[i].GrandTotalQuantity* dec) / dec; else $scope.editTotal.CurrentTotal = Math.round($scope.data.ScenarioCalendar[i].GrandTotalCost*dec)/dec; break; } } }; $scope.isOtherSelectedReallocator = function () { return $scope.reallocator.CurveType == 'other'; }; $scope.isOtherSelectedTotal = function () { return $scope.editTotal.Curve == 'other'; }; $scope.prefillFormatCells = function() { $scope.formatCells.ExpCats = []; for (var i = 1; i < $scope.data.ScenarioCalendar.length; i++) { // skip this row if it is not checked if (!$scope.data.ScenarioCalendar[i].Checked) continue; $scope.formatCells.ExpCats[$scope.formatCells.ExpCats.length] = { Id: $scope.data.ScenarioCalendar[i].ExpCatId, Name: $scope.data.ScenarioCalendar[i].ExpCatName }; } if ($scope.formatCells.ExpCats.length > 0) $scope.formatCells.SelectedExpCats = $scope.formatCells.ExpCats[0].Id; }; $scope.submitFormatCells = function () { if ($scope.data.ScenarioType === 9) return false; if (isNaN($scope.formatCells.DecimalPlaces)) { alert("Decimal Places is not set."); return false; } var colIndexes2Update = []; // gather column indexes which we're going to update var dtStart = $scope.formatCells.StartDate.split('/'); var msStart = new Date(dtStart[2], dtStart[0] - 1, dtStart[1]).getTime(); var dtEnd = $scope.formatCells.EndDate.split('/'); var msEnd = new Date(dtEnd[2], dtEnd[0] - 1, dtEnd[1]).getTime(); for (var headerIndex = 0; headerIndex < $scope.data.Headers.length; headerIndex++) { if ($scope.data.Headers[headerIndex].IsMonth) continue; if ($scope.data.Headers[headerIndex].Milliseconds >= msStart && $scope.data.Headers[headerIndex].Milliseconds <= msEnd && $scope.data.Headers[headerIndex].Milliseconds > $scope.data.ActualsEndDateMs) { colIndexes2Update[colIndexes2Update.length] = headerIndex; } } var oldQuantity = 0; var newQuantity = 0; var newCost = 0; var dec = Math.pow(10, parseInt ($scope.formatCells.DecimalPlaces)); for (var rowIndex = 0; rowIndex < $scope.data.ScenarioCalendar.length; rowIndex++) { for (var catIndex = 0; catIndex < $scope.formatCells.ExpCats.length; catIndex++) { if ($scope.formatCells.ExpCats[catIndex].Id === $scope.data.ScenarioCalendar[rowIndex].ExpCatId) { for (var colIndex = 0; colIndex < colIndexes2Update.length; colIndex++) { oldQuantity = $scope.data.ScenarioCalendar[rowIndex].QuantityValues[colIndexes2Update[colIndex]]; newQuantity = Math.round(oldQuantity * dec) / dec; newCost = Math.round(newQuantity * $scope.getRate($scope.formatCells.ExpCats[catIndex].Id, $scope.data.Headers[colIndexes2Update[colIndex]].Milliseconds) * dec) / dec; if (oldQuantity === newQuantity) continue; updateQuantityOrCostCell($scope.formatCells.ExpCats[catIndex].Id, rowIndex, colIndexes2Update[colIndex], newQuantity, newCost, null); } break; } } } $("#modalFormatCells").modal("hide"); return true; }; $scope.onMonthHeaderClick = function (header) { header.Collapsed = !header.Collapsed; header.Show = !header.Show; header.CollapsedClass = header.Collapsed ? $scope.CollapsedIcon : $scope.NonCollapsedIcon; if (header.Weeks && header.Weeks.length > 0) for (var i = 0; i < header.Weeks.length; i++) { $scope.data.Headers[header.Weeks[i]].Collapsed = header.Collapsed; $scope.data.Headers[header.Weeks[i]].Show = !header.Collapsed; } $scope.resizeFreezAng(); }; $scope.onExpCatClick = function (row) { row.Collapsed = !row.Collapsed; //row.Show = !row.Show; row.CollapsedClass = row.Collapsed ? $scope.CollapsedIcon : $scope.NonCollapsedIcon; }; $scope.prefillAllocateResource = function () { $scope.allocateResource = new Object(); }; $scope.getResourceData = function () { var url = '/Scenarios/GetScenarioResources'; $.post(url, { 'scenarioId': $scope.data.ScenarioId }, function(data) { if (data.Status == 'Ok') { $scope.allocateResource.allResourceList = data.Data; $scope.allocateResource.TeamsList = new Array(); for (var i = 0; i < data.Data.length; i++) { var item = data.Data[i]; if (item.Team != null) { for (var t = 0; t < item.Team.length; t++) { var team = item.Team[t]; if ($scope.allocateResource.TeamsList.length == 0) $scope.allocateResource.TeamsList.push(team); var inArray = false; for (var index = 0; index < $scope.allocateResource.TeamsList.length; index++) { if ($scope.allocateResource.TeamsList[index].Id == team.Id) inArray = true; } if (!inArray) $scope.allocateResource.TeamsList.push(team); } } } } else if (data.Status == 'Error') { console.error(data.Msg); } }); }; $scope.getResourcesByTeam = function() { $scope.allocateResource.ResourceList = new Array(); var teamId = $scope.allocateResource.SelectedTeam; for (var i = 0; i < $scope.allocateResource.allResourceList.length; i++) { var res = $scope.allocateResource.allResourceList[i]; var inTeam = false; var j; for (j = 0; j < res.Team.length; j++) { var team = res.Team[j]; if (team.Id == teamId) inTeam = true; } if (inTeam) { var inList = false; for (j = 0; j < $scope.allocateResource.ResourceList.length; j++) { var r = $scope.allocateResource.ResourceList[j]; if (r.Id == res.Id) inList = true; } if (!inList) $scope.allocateResource.ResourceList.push(res); } } return 0; }; $scope.fillTeamsByResources = function() { $scope.allocateResource.TeamsList = new Array(); for (var i = 0; i < $scope.allocateResource.ResourceList.length; i++) { var res = $scope.allocateResource.ResourceList[i]; if($scope.allocateResource.TeamsList.indexOf(res.Team) == -1) { $scope.allocateResource.TeamsList.push(res.Team); } } }; }]); controllers.controller('teamDetailsCalendarController', ['$scope', '$http', '$location', '$timeout', function ($scope, $http, $location, $timeout) { $scope.CollapsedIcon = 'fa-plus-square'; $scope.NonCollapsedIcon = 'fa-minus-square'; $scope.availableExpenditures = []; $scope.rates = []; $scope.id; $scope.teamId; $scope.calendarFilters = { IsTableModeQuantity: true, IsViewModeMonth: true, ScenarioId: $scope.id, IsUOMHours: false, }; $scope.typeOptions = [ { name: 'Quantity', value: 'Quantity' }, { name: 'Cost', value: 'Cost' } ]; $scope.reallocator = { SourceExpCat: null, TargetExpCat: null, SourceWeekEnding: null, TargetWeekEnding: null, Percentage: 100, ReallocateBy: $scope.typeOptions[0].value, CurveType: 'source', OtherCurve: null, }; $scope.pushpuller = { weeks: 1, push: true, resource: null, startdate: null }; $scope.quickPushPull = function (push, weeks) { for (var i = 1; i < $scope.data.ScenarioCalendar.length; i++) { if ($scope.data.ScenarioCalendar[i].Checked) $scope.pushPullExpCat($scope.data.ScenarioCalendar[i], i, weeks, push); } }; $scope.editTotal = { ExpCat: null, SeatsCost: $scope.typeOptions[0].value, CurrentTotal: null, NewTotal: null, Curve: 'source', OtherCurve: null, }; $scope.formatCells = { ExpCats: [], SelectedExpCats: null, StartDate: null, EndDate: null, DecimalPlaces: 2 }; $scope.data = null; $scope.data2Update = { ScenarioId: $scope.id, ChangedExpCats: [] }; $scope.teamChanged = function() { $http.get('/Scenarios/GetTeamCapacityScenarioId?teamId=' + $scope.teamId).success(function(scenarioId) { $scope.id = scenarioId.replace(/"/g, ''); $scope.calendarFilters.ScenarioId = $scope.id; $http.get('/Scenarios/GetScenarioAvailableExpCategories?id=' + $scope.id).success(function(expData) { $scope.availableExpenditures = expData; if ($scope.availableExpenditures != null && $scope.availableExpenditures.length > 0) { $scope.reallocator.SourceExpCat = $scope.availableExpenditures[0].Id; $scope.reallocator.TargetExpCat = $scope.availableExpenditures.length > 1 ? $scope.availableExpenditures[1].Id : null; $scope.reallocator.OtherCurve = $scope.availableExpenditures[0].Id; $scope.editTotal = { ExpCat: $scope.availableExpenditures[0].Id, OtherCurve: $scope.availableExpenditures[0].Id, SeatsCost: $scope.typeOptions[0].value, Curve: 'source' }; } }); $scope.getRates(); $scope.getCalendar(); }); }; $scope.getRates = function () { $http.post('/Scenarios/GetRates', { scenarioId: $scope.id, uomMode: $scope.calendarFilters.IsUOMHours }).success(function (data) { $scope.rates = data; }).error(function (data, status, headers, config) { console.log("an error occurred while loading rates"); }); }; $scope.switchUOMMode = function (value) { var newValue = value != null ? value : !$scope.calendarFilters.IsUOMHours; $scope.calendarFilters.IsUOMHours = newValue; $scope.getCalendar(); $scope.getRates(); }; function getExpCatById(ExpCatId) { for (var i = 0; i < $scope.data.ScenarioCalendar.length; i++) { if ($scope.data.ScenarioCalendar[i].ExpCatId == ExpCatId) { return $scope.data.ScenarioCalendar[i]; } } return null; } function getResourceById(resId) { for (var i = 0; i < $scope.data.AllResources.length; i++) { if ($scope.data.AllResources[i].Id == resId) { return $scope.data.AllResources[i]; } } return null; } function clearResource(expCat, resource) { if (resource != null) { resource.GrandTotalQuantity = 0; var scenarioCalendarRow = null; for (var i = 0; i < $scope.data.ScenarioCalendar.length; i++) { if ($scope.data.ScenarioCalendar[i].ExpCatId === expCat.ExpCatId) { scenarioCalendarRow = $scope.data.ScenarioCalendar[i]; break; } } for (var i = 0; i < $scope.data.Headers.length; i++) { expCat.RestQuantity[i] += resource.QuantityValues[i]; resource.QuantityValues[i] = 0; applyCellChange(expCat.ExpCatId, $scope.data.Headers[i].Milliseconds, scenarioCalendarRow.ScenarioDetailIds[i], resource.QuantityValues[i], 0, resource.Id, false, false); } } } function updateTotals(resource, colIndex, newQuantity, oldQuantity, newCost, oldCost) { for (var i = colIndex + 1; i < $scope.data.Headers.length; i++) { if (!$scope.data.Headers[i].IsMonth) continue; //update month cell resource.QuantityValues[i] += (newQuantity - oldQuantity); resource.GrandTotalQuantity[i] += (newQuantity - oldQuantity); break; } } function allocateResource(resource, expCat, create, allocateRest, reset) { if (resource != null) { resource.GrandTotalQuantity = 0; resource.GrandTotalCost = 0; var scenarioCalendarRow = null; var i; for (i = 0; i < $scope.data.ScenarioCalendar.length; i++) { if ($scope.data.ScenarioCalendar[i].ExpCatId === expCat.ExpCatId) { scenarioCalendarRow = $scope.data.ScenarioCalendar[i]; break; } } for (i = 0; i < $scope.data.Headers.length; i++) { var quantity = create || reset ? 0 : allocateRest ? resource.QuantityValues[i] + expCat.RestQuantity[i] : expCat.QuantityValues[i]; if (create) resource.QuantityValues[i] = 0; expCat.RestQuantity[i] -= quantity - resource.QuantityValues[i]; updateTotals(resource, i, quantity, resource.QuantityValues[i], 0, 0); resource.QuantityValues[i] = quantity; if (!$scope.data.Headers[i].IsMonth) { resource.GrandTotalQuantity += quantity; applyCellChange(expCat.ExpCatId, $scope.data.Headers[i].Milliseconds, scenarioCalendarRow.ScenarioDetailIds[i], resource.QuantityValues[i], resource.CostValues[i], resource.Id, create, false); } } } } $scope.getCalendar = function () { if ($scope.id === undefined || $scope.id == "" || $scope.id == "0") return; blockUI(); $scope.data = null; var postData = JSON.parse(JSON.stringify($scope.calendarFilters)); $http.post('/Scenarios/LoadJsonScenarioCalendar', postData). success(function (data, status, headers, config) { if (data.Headers.length < 1) { unblockUI(); return; } $scope.data = data; $scope.calendarFilters.IsUOMHours = data.IsUOMHours; $scope.switchViewMode($scope.calendarFilters.IsViewModeMonth); $scope.data2Update = { ScenarioId: $scope.id, ChangedExpCats: [] }; $scope.reallocator.SourceWeekEnding = $scope.data.Headers[0].Title; $scope.reallocator.TargetWeekEnding = $scope.data.Headers[$scope.data.Headers.length - 2].Title; $scope.editTotal.CurrentTotal = $scope.data.ScenarioCalendar[1].GrandTotalQuantity; var dt = new Date($scope.data.Headers[0].Milliseconds); $scope.formatCells.StartDate = (dt.getMonth() + 1) + "/" + dt.getDate() + "/" + dt.getFullYear(); if ($scope.data.Headers.length > 3) dt = new Date($scope.data.Headers[$scope.data.Headers.length - 2].Milliseconds); else dt = new Date($scope.data.Headers[$scope.data.Headers.length - 2].Milliseconds); $scope.formatCells.EndDate = (dt.getMonth() + 1) + "/" + dt.getDate() + "/" + dt.getFullYear(); $scope.getResourceData(); $scope.prefillAllocateResource(); unblockUI(); }). error(function (data, status, headers, config) { // called asynchronously if an error occurs // or server returns response with an error status. console.log("an error occurred while loading calendar"); unblockUI(); }); }; $scope.onPercentageChange = function (val) { $scope.reallocator.Percentage = val; }; $scope.checkEditable = function (useType) { if (3 === useType) { return false; } return true; }; $scope.checkValue = function (data, rowId, colIndex) { var newValue = parseFloat(data); if (isNaN(newValue)) newValue = 0; if (newValue < 0) { return "Value should not be less than zero"; } for (var i = 0; i < $scope.data.ScenarioCalendar.length; i++) { if ($scope.data.ScenarioCalendar[i].ExpCatId === rowId) { if ($scope.calendarFilters.IsTableModeQuantity) { updateQuantityOrCostCell(rowId, i, colIndex, newValue, null, true); } else { updateQuantityOrCostCell(rowId, i, colIndex, null, newValue, false); } break; } } //required to be false by xeditable grid return false; }; $scope.watchKeyInput = function (t) { $timeout(function () { if (t.$editable.inputEl.select) t.$editable.inputEl.select(); else if (t.$editable.inputEl.setSelectionRange) t.$editable.inputEl.setSelectionRange(0, t.$editable.inputEl.val().length); }, 3); t.$editable.inputEl.on('keydown', function (e) { if (e.which == 9) { //when tab key is pressed e.preventDefault(); var tab2Cell; if (e.shiftKey) {// when shift + tab use with 'onblur' set to 'submit' for automatic submission find the parent of the editable before this one in the markup grab the editable and display it tab2Cell = $(this).parentsUntil('table#table').prevAll(":has(.editable:visible):first").find(".editable:visible:last"); t.$form.$submit(); tab2Cell.click(); } else {// when just tab use with 'onblur' set to 'submit' for automatic submission find the parent of the editable after this one in the markup grab the editable and display it tab2Cell = $(this).parentsUntil('table#table').nextAll(":has(.editable:visible):first").find(".editable:visible:first"); t.$form.$submit(); tab2Cell.click(); } } }); }; $scope.onTxtBlur = function (txt) { txt.$form.$submit(); }; function updateQuantityOrCostCell(expCatId, rowIndex, colIndex, newQuantity, newCost, isUpdateQuantity) { /* isUpdateQuantity - indicates whether to update by quantity or by cost. * true - Quantity will be updated from newQuantity, Cost will be recalculated as newQuantity * rate * false - Cost will be updated from newCost, Quantity will be recalculated as newCost / rate * null - Quantity will be updated from newQuantity, Cost will be updated from newCost */ var oldQuantity = $scope.data.ScenarioCalendar[rowIndex].QuantityValues[colIndex]; var oldCost = $scope.data.ScenarioCalendar[rowIndex].CostValues[colIndex]; if (true === isUpdateQuantity) { // update quantity, recalculate cost based on exp cat rate if (oldQuantity === newQuantity || newQuantity < 0) return; newCost = Math.round(newQuantity * $scope.getRate(expCatId, $scope.data.Headers[colIndex].Milliseconds) * 10000) / 10000; } else if (false === isUpdateQuantity) { // update cost, recalculate quantity based on exp cat rate if (oldCost === newCost || newCost < 0) return; newQuantity = Math.round(newCost / $scope.getRate(expCatId, $scope.data.Headers[colIndex].Milliseconds) * 10000) / 10000; } // update modified cell $scope.data.ScenarioCalendar[rowIndex].RestQuantity[colIndex] += newQuantity - $scope.data.ScenarioCalendar[rowIndex].QuantityValues[colIndex]; $scope.data.ScenarioCalendar[rowIndex].CostValues[colIndex] = newCost; $scope.data.ScenarioCalendar[rowIndex].QuantityValues[colIndex] = newQuantity; // update row grand totals $scope.data.ScenarioCalendar[rowIndex].GrandTotalCost += (newCost - oldCost); $scope.data.ScenarioCalendar[rowIndex].GrandTotalQuantity += (newQuantity - oldQuantity); // update column totals $scope.data.ScenarioCalendar[0].CostValues[colIndex] += (newCost - oldCost); $scope.data.ScenarioCalendar[0].QuantityValues[colIndex] += (newQuantity - oldQuantity); // update table totals $scope.data.ScenarioCalendar[0].GrandTotalCost += (newCost - oldCost); $scope.data.ScenarioCalendar[0].GrandTotalQuantity += (newQuantity - oldQuantity); // update month cell and month total cell for (var i = colIndex + 1; i < $scope.data.Headers.length; i++) { if (!$scope.data.Headers[i].IsMonth) continue; //update month cell $scope.data.ScenarioCalendar[rowIndex].QuantityValues[i] += (newQuantity - oldQuantity); $scope.data.ScenarioCalendar[rowIndex].CostValues[i] += (newCost - oldCost); // update month column totals $scope.data.ScenarioCalendar[0].CostValues[i] += (newCost - oldCost); $scope.data.ScenarioCalendar[0].QuantityValues[i] += (newQuantity - oldQuantity); break; } applyCellChange(expCatId, $scope.data.Headers[colIndex].Milliseconds, $scope.data.ScenarioCalendar[rowIndex].ScenarioDetailIds[colIndex], $scope.data.ScenarioCalendar[rowIndex].QuantityValues[colIndex], $scope.data.ScenarioCalendar[rowIndex].CostValues[colIndex], null, false, false); } function applyCellChange(rowId, cellMilliseconds, cellId, cellQuantity, cellCost, resId, isAdded, isRemoved) { var catIndex = -1; for (var ix = 0; ix < $scope.data2Update.ChangedExpCats.length; ix++) { if ($scope.data2Update.ChangedExpCats[ix].Id == rowId) { catIndex = ix; break; } } if (catIndex == -1) { $scope.data2Update.ChangedExpCats[$scope.data2Update.ChangedExpCats.length] = { Id: rowId, Values: [], Resources: [] }; catIndex = $scope.data2Update.ChangedExpCats.length - 1; } if (resId == null) { var dateIndex = -1; for (var dx = 0; dx < $scope.data2Update.ChangedExpCats[catIndex].Values.length; dx++) { if ($scope.data2Update.ChangedExpCats[catIndex].Values[dx].Id == cellId) { dateIndex = dx; break; } } if (dateIndex == -1) { $scope.data2Update.ChangedExpCats[catIndex].Values[$scope.data2Update.ChangedExpCats[catIndex].Values.length] = { Id: cellId, Milliseconds: cellMilliseconds, Values: [] }; dateIndex = $scope.data2Update.ChangedExpCats[catIndex].Values.length - 1; } $scope.data2Update.ChangedExpCats[catIndex].Values[dateIndex].Cost = cellCost; $scope.data2Update.ChangedExpCats[catIndex].Values[dateIndex].Quantity = cellQuantity; } else { var dateIndex = -1; for (var dx = 0; dx < $scope.data2Update.ChangedExpCats[catIndex].Resources.length; dx++) { if ($scope.data2Update.ChangedExpCats[catIndex].Resources[dx].Id == resId) { dateIndex = dx; break; } } if (dateIndex == -1) { $scope.data2Update.ChangedExpCats[catIndex].Resources[$scope.data2Update.ChangedExpCats[catIndex].Resources.length] = { Id: resId, Values: [], IsAdded: isAdded, IsRemoved: isRemoved }; dateIndex = $scope.data2Update.ChangedExpCats[catIndex].Resources.length - 1; if (isAdded || isRemoved) return; } else { if (isRemoved) { if ($scope.data2Update.ChangedExpCats[catIndex].Resources[dateIndex].IsAdded) { $scope.data2Update.ChangedExpCats[catIndex].Resources.splice(dateIndex, 1); } return; } } var dateIndex1 = -1; for (var dx = 0; dx < $scope.data2Update.ChangedExpCats[catIndex].Resources[dateIndex].Values.length; dx++) { if ($scope.data2Update.ChangedExpCats[catIndex].Resources[dateIndex].Values[dx].Id == cellId) { dateIndex1 = dx; break; } } if (dateIndex1 == -1) { $scope.data2Update.ChangedExpCats[catIndex].Resources[dateIndex].Values[$scope.data2Update.ChangedExpCats[catIndex].Resources[dateIndex].Values.length] = { Id: cellId, Milliseconds: cellMilliseconds, Values: [] }; dateIndex1 = $scope.data2Update.ChangedExpCats[catIndex].Resources[dateIndex].Values.length - 1; } //$scope.data2Update.ChangedExpCats[catIndex].Resources[dateIndex].Values[dateIndex1].Cost = cellCost; $scope.data2Update.ChangedExpCats[catIndex].Resources[dateIndex].Values[dateIndex1].Quantity = cellQuantity; } } $scope.saveChanges = function () { blockUI(); var postData = JSON.parse(JSON.stringify($scope.data2Update)); postData.ScenarioFilters = $scope.calendarFilters; $http.post('/Scenarios/SaveChanges', postData). success(function (data, status, headers, config) { //$scope.data = data; $scope.data2Update = { ScenarioId: $scope.id, ChangedExpCats: [] }; InitGraph($scope.teamId); if (typeof LoadGraphData === 'function') LoadGraphData(); unblockUI(); //var tab = $("#uidemo-tabs-default-demo li.active a").attr("href"); //var retUrl = window.location.href; //if (retUrl.indexOf("&tab=") > 0) { // retUrl = retUrl.substr(0, retUrl.indexOf("&tab=")); //} //window.location.href = retUrl + "&tab=" + tab.replace("#", ""); onDataSaved(); }). error(function (data, status, headers, config) { console.log("an error occurred while saving calendar changes"); unblockUI(); }); }; $scope.getRate = function (expCatId, date) { if ($scope.rates && $scope.rates.length > 0) { for (var i = 0; i < $scope.rates.length; i++) { if ($scope.rates[i].expCatId === expCatId) { if ($scope.rates[i].rateValues && $scope.rates[i].rateValues.length > 0) { for (var rIndex = 0; rIndex < $scope.rates[i].rateValues.length; rIndex++) { if (date <= $scope.rates[i].rateValues[rIndex].weekEndDate) return $scope.rates[i].rateValues[rIndex].rateValue; } return $scope.rates[i].rateValues[$scope.rates[i].rateValues.length - 1].rateValue; } break; } } } return 0; }; $scope.graphData = []; $scope.maxGraphValue = 0; $scope.showGraph = function () { // prepare data $scope.maxGraphValue = 0; $scope.graphData = []; var startDate = new Date($scope.data.StartDate.replace('/Date(', '').replace(')/', '') * 1); var endDate = new Date($scope.data.EndDate.replace('/Date(', '').replace(')/', '') * 1); var days = (endDate - startDate) / (1000 * 60 * 60 * 24); var i; if (days <= 400) { for (i = 1; i < $scope.data.ScenarioCalendar.length; i++) { // skip this row if it is not checked if (!$scope.data.ScenarioCalendar[i].Checked) continue; var expCatData = new Array(); for (var colIndex = 0; colIndex < $scope.data.Headers.length; colIndex++) { if ($scope.data.Headers[colIndex].IsMonth) continue; expCatData[expCatData.length] = [$scope.data.Headers[colIndex].Milliseconds, $scope.data.ScenarioCalendar[i].QuantityValues[colIndex]]; if (true !== $scope.calendarFilters.IsTableModeQuantity) { expCatData[expCatData.length - 1][1] = $scope.data.ScenarioCalendar[i].CostValues[colIndex]; } if (expCatData[expCatData.length - 1][1] > $scope.maxGraphValue) $scope.maxGraphValue = expCatData[expCatData.length - 1][1]; expCatData[expCatData.length - 1][1] = expCatData[expCatData.length - 1][1].toFixed(2); } $scope.graphData[$scope.graphData.length] = { label: $scope.data.ScenarioCalendar[i].ExpCatName, data: expCatData, lines: { show: true, fill: true, steps: false }, filledPoints: true, stack: true }; } } else { $.get('/Scenarios/GetMonthEndDates', { 'StartDate': startDate.getMonth() + 1 + '/' + startDate.getDate() + '/' + startDate.getFullYear(), 'EndDate': endDate.getMonth() + 1 + '/' + endDate.getDate() + '/' + endDate.getFullYear() }).done(function (data) { var dates = data; for (i = 1; i < $scope.data.ScenarioCalendar.length; i++) { // skip this row if it is not checked if (!$scope.data.ScenarioCalendar[i].Checked) continue; var expCatData = new Array(); var currentIndex = 0; for (var colIndex = 0; colIndex < $scope.data.Headers.length; colIndex++) { if ($scope.data.Headers[colIndex].IsMonth) continue; for (var index = 0; index < dates.length; index++) { if (dates[index] == $scope.data.Headers[colIndex].Milliseconds) { expCatData[currentIndex] = [$scope.data.Headers[colIndex].Milliseconds, $scope.data.ScenarioCalendar[i].QuantityValues[colIndex]]; if (true !== $scope.calendarFilters.IsTableModeQuantity) { expCatData[currentIndex][1] = $scope.data.ScenarioCalendar[i].CostValues[colIndex]; } if (expCatData[currentIndex][1] > $scope.maxGraphValue) $scope.maxGraphValue = expCatData[currentIndex][1]; expCatData[currentIndex][1] = expCatData[currentIndex][1].toFixed(2); currentIndex++; } } } $scope.graphData[$scope.graphData.length] = { label: $scope.data.ScenarioCalendar[i].ExpCatName, data: expCatData, lines: { show: true, fill: true, steps: false }, filledPoints: true, stack: true }; } }); } // show graph $('#DetailsGraphContainer').html('
'); $("#modalGraph").modal("show"); setTimeout($scope.initGraph, 500); }; $scope.initGraph = function () { // Init Chart $('#divDetailsGraph').pixelPlot($scope.graphData, { //var plot = $.plot($('#jq-flot-graph2'), graphData, { series: { points: { show: false }, lines: { show: true, } }, xaxis: { mode: "time", tickSize: [1, "month"], tickLength: 0, axisLabel: "time", axisLabelUseCanvas: true, axisLabelFontSizePixels: 12, axisLabelFontFamily: 'Verdana, Arial', axisLabelPadding: 10 }, yaxis: { //tickSize: $scope.maxGraphValue / 5 } }, { height: 405, width: 405, tooltipText: "y + ' units at ' + (new Date(x)).toString()" }); }; $scope.anyChecked = function () { if ($scope.data == null || $scope.data.ScenarioCalendar == null) return false; for (var i = 1; i < $scope.data.ScenarioCalendar.length; i++) { if ($scope.data.ScenarioCalendar[i].Checked) return true; } return false; }; $scope.checkedExpenditures = []; $scope.collectchecked = function (obj) { $scope.checkedExpenditures = []; if ($scope.data == null || $scope.data.ScenarioCalendar == null) return; for (var i = 1; i < $scope.data.ScenarioCalendar.length; i++) { if ($scope.data.ScenarioCalendar[i].Checked || (obj != null && !obj.Checked && obj.ExpCatId == $scope.data.ScenarioCalendar[i].ExpCatId)) $scope.checkedExpenditures.push($scope.data.ScenarioCalendar[i]); } }; $scope.resizeFreezAng = function () { setTimeout(function () { resizeFreez(); }, 0); }; $scope.takeAll = function (resId, expCatId) { if (confirm("Are you sure you want to re-assign category quantity and assign everything to this resource?")) { var expCat = getExpCatById(expCatId); var resource = null; for (var i = 0; i < expCat.Resources.length; i++) { if (expCat.Resources[i].Id == resId) { resource = expCat.Resources[i]; } else { clearResource(expCat, expCat.Resources[i]); } } allocateResource(resource, expCat, false, false, false); } }; $scope.takeRemaining = function (resId, expCatId) { var expCat = getExpCatById(expCatId); if (expCat != null) { for (var i = 0; i < expCat.Resources.length; i++) { if (expCat.Resources[i].Id == resId) { allocateResource(expCat.Resources[i], expCat, false, true, false); return; } } } }; $scope.zeroResource = function (resId, expCatId) { if (confirm("Are you sure you want fill this resource quantities with 0s?")) { var expCat = getExpCatById(expCatId); for (var i = 0; i < expCat.Resources.length; i++) { if (expCat.Resources[i].Id == resId) { allocateResource(expCat.Resources[i], expCat, false, false, true); return; } } } }; $scope.assignResource = function (resId, expCatId, $event) { if (resId == null || expCatId == null) return; var resource = getResourceById(resId); if (resource != null) { var expCat = getExpCatById(expCatId); if (expCat != null) { for (var j = 0; j < expCat.Resources.length; j++) { if (expCat.Resources[j].Id == resource.Id) { alert("The " + resource.Name + " has been assigned to the " + expCat.Name); break; } } resource = { 'Id': resource.Id, 'Name': resource.Name, 'QuantityValues': new Array($scope.data.Headers.length), 'CostValues': new Array($scope.data.Headers.length) }; } allocateResource(resource, expCat, true, false, false); expCat.Resources.push(resource); var tr = $($event.target).parentsUntil('table').last().parent().find('thead tr:first'); console.log("tr: " + $(tr).html()); var thName = tr.find('th:eq(1)'); console.log("thName: " + $(thName).html()); resource.width = thName.width() + 'px'; var thTot = tr.find('th:eq(2)'); console.log("thTot: " + $(thTot).html()); resource.left = thTot.css('left'); $scope.resizeFreezAng(); } }; $scope.removeResource = function (resId, expCatId) { if (confirm("Are you sure you want to remove this resource?")) { var expCat = getExpCatById(expCatId); for (var i = 0; i < expCat.Resources.length; i++) { if (expCat.Resources[i].Id == resId) { expCat.Resources.splice(i, 1); applyCellChange(expCatId, 0, 0, 0, 0, resId, false, true); $scope.resizeFreezAng(); return; } } } }; $scope.checkResourceValue = function (data, expCatId, resId, colIndex) { var newValue = parseFloat(data); if (newValue < 0) { return "Value should not be less than zero"; } var expCat = getExpCatById(expCatId); var resource = null; for (var i = 0; i < expCat.Resources.length; i++) { if (expCat.Resources[i].Id == resId) { resource = expCat.Resources[i]; break; } } var scenarioCalendarRow = null; for (var i = 0; i < $scope.data.ScenarioCalendar.length; i++) { if ($scope.data.ScenarioCalendar[i].ExpCatId === expCatId) { scenarioCalendarRow = $scope.data.ScenarioCalendar[i]; break; } } if (expCat != null && resource != null) { if ($scope.calendarFilters.IsTableModeQuantity) { resource.GrandTotalQuantity += newValue - resource.QuantityValues[colIndex]; updateTotals(resource, colIndex, newValue, resource.QuantityValues[colIndex], 0, 0); expCat.RestQuantity[colIndex] -= newValue - resource.QuantityValues[colIndex]; resource.QuantityValues[colIndex] = newValue; } applyCellChange(expCatId, $scope.data.Headers[colIndex].Milliseconds, scenarioCalendarRow.ScenarioDetailIds[colIndex], resource.QuantityValues[colIndex], 0, resId, false, false); } //required to be false by xeditable grid return false; }; $scope.getExpCatResources = function (expCat) { var resources = []; for (var i = 0; i < $scope.data.AllResources.length; i++) { if ($scope.data.AllResources[i].ExpedentureCategoryId == expCat.ExpCatId && $scope.data.AllResources[i] && $scope.data.AllResources[i].IsActiveEmployee) { var isFound = false; for (var j = 0; j < expCat.Resources.length; j++) { if (expCat.Resources[j].Id == $scope.data.AllResources[i].Id) { isFound = true; break; } } if (!isFound) resources.push($scope.data.AllResources[i]); } } return resources; }; $scope.checkAll = function () { $scope.checkedExpenditures = []; var checked = !$scope.data.ScenarioCalendar[0].Checked; if ($scope.data == null || $scope.data.ScenarioCalendar == null) return; for (var i = 1; i < $scope.data.ScenarioCalendar.length; i++) { $scope.data.ScenarioCalendar[i].Checked = checked; if (checked) $scope.checkedExpenditures.push($scope.data.ScenarioCalendar[i]); } }; $scope.switchViewMode = function (value) { var newValue = value != null ? value : !$scope.calendarFilters.IsViewModeMonth; for (var i = 0; i < $scope.data.Headers.length; i++) { if ($scope.data.Headers[i].IsMonth) { $scope.data.Headers[i].Collapsed = newValue; $scope.data.Headers[i].Show = newValue; $scope.data.Headers[i].CollapsedClass = newValue ? $scope.CollapsedIcon : $scope.NonCollapsedIcon; } else { $scope.data.Headers[i].Collapsed = newValue; $scope.data.Headers[i].Show = !newValue; } } $scope.resizeFreezAng(); }; $scope.anyUpdated = function () { return ($scope.data2Update && $scope.data2Update.ChangedExpCats && $scope.data2Update.ChangedExpCats.length > 0); }; $scope.reallocateResource = function () { var sourceQuantity = 0; var sourceCost = 0; if ($scope.reallocator.Percentage == 0) { alert('Percentage is 0, nothing to reallocate.'); return; } var sourceRowIndex, targetRowIndex, curveRowIndex, sourceColIndex, targetColIndex; var i; for (i = 0; i < $scope.data.ScenarioCalendar.length; i++) { if ($scope.data.ScenarioCalendar[i].ExpCatId === $scope.reallocator.SourceExpCat) sourceRowIndex = i; if ($scope.data.ScenarioCalendar[i].ExpCatId === $scope.reallocator.TargetExpCat) targetRowIndex = i; if ($scope.reallocator.CurveType == 'other' && $scope.data.ScenarioCalendar[i].ExpCatId === $scope.reallocator.OtherCurve) curveRowIndex = i; } if (sourceRowIndex == targetRowIndex) { alert('Cant reallocate within same expenditure category.'); return; } for (i = 0; i < $scope.data.Headers.length; i++) { if ($scope.data.Headers[i].IsMonth) continue; if ($scope.data.Headers[i].Title === $scope.reallocator.SourceWeekEnding) sourceColIndex = i; if ($scope.data.Headers[i].Title === $scope.reallocator.TargetWeekEnding) targetColIndex = i; } if (sourceColIndex == null || targetColIndex == null) { alert('Please check selected dates to match valid week endings.'); return; } else if (targetColIndex < sourceColIndex) { alert('Please select To week ending date later than From week ending date.'); return; } var sourceSumCost = 0, sourceSumQuantity = 0, targetSumCost = 0, targetSumQuantity = 0, otherSumCost = 0, otherSumQuantity = 0; var percentageMult = $scope.reallocator.Percentage / 100; for (i = sourceColIndex; i <= targetColIndex ; i++) { if ($scope.data.Headers[i].IsMonth) continue; sourceSumQuantity += $scope.data.ScenarioCalendar[sourceRowIndex].QuantityValues[i]; sourceSumCost += $scope.data.ScenarioCalendar[sourceRowIndex].CostValues[i]; targetSumQuantity += $scope.data.ScenarioCalendar[targetRowIndex].QuantityValues[i]; targetSumCost += $scope.data.ScenarioCalendar[targetRowIndex].CostValues[i]; if ($scope.reallocator.CurveType == 'other') { otherSumQuantity += $scope.data.ScenarioCalendar[curveRowIndex].QuantityValues[i]; otherSumCost += $scope.data.ScenarioCalendar[curveRowIndex].CostValues[i]; } } if ((sourceSumQuantity == 0 || sourceSumCost == 0)) { alert('Source is zero, nothing to reallocate.'); return; } if ((targetSumQuantity == 0 || targetSumCost == 0) && $scope.reallocator.CurveType == 'target') { alert('Target curve is zero. Please select a different curve.'); return; } if ((otherSumQuantity == 0 || otherSumCost == 0) && $scope.reallocator.CurveType == 'other') { alert('Different curve is zero. Please select a another curve.'); return; } var quantity2Move = 0; var cost2Move = 0; for (i = sourceColIndex; i <= targetColIndex ; i++) { if ($scope.data.Headers[i].IsMonth) continue; if ($scope.reallocator.CurveType == 'source') { sourceQuantity = $scope.data.ScenarioCalendar[sourceRowIndex].QuantityValues[i]; sourceCost = $scope.data.ScenarioCalendar[sourceRowIndex].CostValues[i]; // update column values in model if ($scope.reallocator.ReallocateBy == 'Quantity') { updateQuantityOrCostCell($scope.reallocator.SourceExpCat, sourceRowIndex, i, sourceQuantity * (1 - percentageMult), sourceCost * (1 - percentageMult), null); updateQuantityOrCostCell($scope.reallocator.TargetExpCat, targetRowIndex, i, $scope.data.ScenarioCalendar[targetRowIndex].QuantityValues[i] + sourceQuantity * percentageMult, null, true); } else { updateQuantityOrCostCell($scope.reallocator.SourceExpCat, sourceRowIndex, i, sourceQuantity * (1 - percentageMult), sourceCost * (1 - percentageMult), null); updateQuantityOrCostCell($scope.reallocator.TargetExpCat, targetRowIndex, i, null, $scope.data.ScenarioCalendar[targetRowIndex].CostValues[i] + sourceCost * percentageMult, false); } } else if ($scope.reallocator.CurveType == 'target') { sourceQuantity = $scope.data.ScenarioCalendar[sourceRowIndex].QuantityValues[i]; sourceCost = $scope.data.ScenarioCalendar[sourceRowIndex].CostValues[i]; quantity2Move = (percentageMult * sourceSumQuantity) * ($scope.data.ScenarioCalendar[targetRowIndex].QuantityValues[i] / targetSumQuantity); cost2Move = (percentageMult * sourceSumCost) * ($scope.data.ScenarioCalendar[targetRowIndex].CostValues[i] / targetSumCost); // update column values in model if ($scope.reallocator.ReallocateBy == 'Quantity') { // we cannot reallocate more than source quantity if (quantity2Move > $scope.data.ScenarioCalendar[sourceRowIndex].QuantityValues[i]) quantity2Move = $scope.data.ScenarioCalendar[sourceRowIndex].QuantityValues[i]; if (quantity2Move > 0) { updateQuantityOrCostCell($scope.reallocator.SourceExpCat, sourceRowIndex, i, sourceQuantity - quantity2Move, null, true); updateQuantityOrCostCell($scope.reallocator.TargetExpCat, targetRowIndex, i, $scope.data.ScenarioCalendar[targetRowIndex].QuantityValues[i] + quantity2Move, null, true); } } else { // we cannot reallocate more than source quantity if (cost2Move > $scope.data.ScenarioCalendar[sourceRowIndex].CostValues[i]) cost2Move = $scope.data.ScenarioCalendar[sourceRowIndex].CostValues[i]; if (cost2Move > 0) { updateQuantityOrCostCell($scope.reallocator.SourceExpCat, sourceRowIndex, i, null, sourceCost - cost2Move, false); updateQuantityOrCostCell($scope.reallocator.TargetExpCat, targetRowIndex, i, null, $scope.data.ScenarioCalendar[targetRowIndex].CostValues[i] + cost2Move, false); } } } else if ($scope.reallocator.CurveType == 'other') { sourceQuantity = $scope.data.ScenarioCalendar[sourceRowIndex].QuantityValues[i]; sourceCost = $scope.data.ScenarioCalendar[sourceRowIndex].CostValues[i]; quantity2Move = (percentageMult * sourceSumQuantity) * ($scope.data.ScenarioCalendar[curveRowIndex].QuantityValues[i] / otherSumQuantity); cost2Move = (percentageMult * sourceSumCost) * ($scope.data.ScenarioCalendar[curveRowIndex].CostValues[i] / otherSumCost); // update column values in model if ($scope.reallocator.ReallocateBy == 'Quantity') { // we cannot reallocate more than source quantity if (quantity2Move > $scope.data.ScenarioCalendar[sourceRowIndex].QuantityValues[i]) quantity2Move = $scope.data.ScenarioCalendar[sourceRowIndex].QuantityValues[i]; if (quantity2Move > 0) { updateQuantityOrCostCell($scope.reallocator.SourceExpCat, sourceRowIndex, i, sourceQuantity - quantity2Move, null, true); updateQuantityOrCostCell($scope.reallocator.TargetExpCat, targetRowIndex, i, $scope.data.ScenarioCalendar[targetRowIndex].QuantityValues[i] + quantity2Move, null, true); } } else { // we cannot reallocate more than source quantity if (cost2Move > $scope.data.ScenarioCalendar[sourceRowIndex].CostValues[i]) cost2Move = $scope.data.ScenarioCalendar[sourceRowIndex].CostValues[i]; if (cost2Move > 0) { updateQuantityOrCostCell($scope.reallocator.SourceExpCat, sourceRowIndex, i, null, sourceCost - cost2Move, false); updateQuantityOrCostCell($scope.reallocator.TargetExpCat, targetRowIndex, i, null, $scope.data.ScenarioCalendar[targetRowIndex].CostValues[i] + cost2Move, false); } } } } $("#reallocator").modal("hide"); }; $scope.pushPull = function () { for (var i = 1; i < $scope.data.ScenarioCalendar.length; i++) { if ($scope.data.ScenarioCalendar[i].Checked) $scope.pushPullExpCat($scope.data.ScenarioCalendar[i], i, $scope.pushpuller.weeks, $scope.pushpuller.push); } $("#push_pull").modal("hide"); }; $scope.pushPullExpCat = function (data, sourceRowIndex, weeks, push) { var oldQuantity = 0; var oldCost = 0; var i; var j; var handledWeeks = 0; var leftMargin; // index of the cell next to the last modified cell from the left side of the table. E.g. if we push 2 weeks, then it will be [2], or [3] if there is only one week in the first month var rightMargin; // index of the cell previous to the first modified cell from the right side of the table. E.g. if we pull 2 weeks, then it will be [length-3], or [length-4] if there is only one week in the last month for (i = 0; i < $scope.data.Headers.length; i++) { if ($scope.data.Headers[i].IsMonth) continue; handledWeeks++; if (handledWeeks == weeks) { leftMargin = i + 1; break; } } handledWeeks = 0; for (i = $scope.data.Headers.length - 1; i >= 0; i--) { if ($scope.data.Headers[i].IsMonth) continue; if (i < leftMargin) { alert('Error occurred while calculating right margin of required changes.'); return; } handledWeeks++; if (handledWeeks == weeks) { rightMargin = i - 1; break; } } if (push) { for (i = $scope.data.Headers.length - 1; i >= 0; i--) { if ($scope.data.Headers[i].IsMonth) continue; oldQuantity = 0; oldCost = 0; if (i < leftMargin) { if ($scope.calendarFilters.IsTableModeQuantity) { updateQuantityOrCostCell(data.ExpCatId, sourceRowIndex, i, 0, null, true); } else { updateQuantityOrCostCell(data.ExpCatId, sourceRowIndex, i, null, 0, false); } } else { //copy data from [i+weeks] cell handledWeeks = 0; for (j = i - 1; j >= 0; j--) { if ($scope.data.Headers[j].IsMonth) continue; handledWeeks++; if (handledWeeks == weeks) { oldQuantity = data.QuantityValues[j]; oldCost = data.CostValues[j]; break; } } if ($scope.calendarFilters.IsTableModeQuantity) { updateQuantityOrCostCell(data.ExpCatId, sourceRowIndex, i, oldQuantity, null, true); } else { updateQuantityOrCostCell(data.ExpCatId, sourceRowIndex, i, null, oldCost, false); } } } } else { for (i = 0; i < $scope.data.Headers.length; i++) { if ($scope.data.Headers[i].IsMonth) continue; oldQuantity = 0; oldCost = 0; if (i <= rightMargin) { //copy data from [i+weeks] cell handledWeeks = 0; for (j = i + 1; j < $scope.data.Headers.length; j++) { if ($scope.data.Headers[j].IsMonth) continue; handledWeeks++; if (handledWeeks == weeks) { oldQuantity = data.QuantityValues[j]; oldCost = data.CostValues[j]; break; } } if ($scope.calendarFilters.IsTableModeQuantity) { updateQuantityOrCostCell(data.ExpCatId, sourceRowIndex, i, oldQuantity, null, true); } else { updateQuantityOrCostCell(data.ExpCatId, sourceRowIndex, i, null, oldCost, false); } } else if (i > rightMargin) { if ($scope.calendarFilters.IsTableModeQuantity) { updateQuantityOrCostCell(data.ExpCatId, sourceRowIndex, i, 0, null, true); } else { updateQuantityOrCostCell(data.ExpCatId, sourceRowIndex, i, null, 0, false); } } } } }; $scope.editTotalFunc = function () { var oldQuantity = 0; var oldCost = 0; if ($scope.editTotal.CurrentTotal == $scope.editTotal.NewTotal) { $("#editTotal").modal("hide"); return; } var changeMult = $scope.editTotal.NewTotal / $scope.editTotal.CurrentTotal; var sourceRowIndex = null; var curveRowIndex = null; var i; for (i = 0; i < $scope.data.ScenarioCalendar.length; i++) { if ($scope.data.ScenarioCalendar[i].ExpCatId === $scope.editTotal.ExpCat) sourceRowIndex = i; if ($scope.data.ScenarioCalendar[i].ExpCatId === $scope.editTotal.OtherCurve) curveRowIndex = i; } if (null === sourceRowIndex) { alert('Cannot find expenditure category to update.'); return; } if (null === curveRowIndex) { alert('Cannot find expenditure category to use as a curve.'); return; } var sourceSumCost = 0, sourceSumQuantity = 0, curveSumCost = 0, curveSumQuantity = 0; for (i = 0; i < $scope.data.Headers.length ; i++) { if ($scope.data.Headers[i].IsMonth) continue; sourceSumQuantity += $scope.data.ScenarioCalendar[sourceRowIndex].QuantityValues[i]; sourceSumCost += $scope.data.ScenarioCalendar[sourceRowIndex].CostValues[i]; curveSumQuantity += $scope.data.ScenarioCalendar[curveRowIndex].QuantityValues[i]; curveSumCost += $scope.data.ScenarioCalendar[curveRowIndex].CostValues[i]; } if (curveSumQuantity == 0) { changeMult = $scope.editTotal.NewTotal; } var numberOfWeeks = 0; for (i = 0; i < $scope.data.Headers.length; i++) { if ($scope.data.Headers[i].IsMonth) continue; numberOfWeeks++; } if (curveSumQuantity == 0 && $scope.editTotal.Curve == 'other') { alert('Curve is zero. Please select a different curve.'); return; } if (curveSumQuantity > 0) for (i = 0; i < $scope.data.Headers.length; i++) { if ($scope.data.Headers[i].IsMonth) continue; oldQuantity = $scope.data.ScenarioCalendar[sourceRowIndex].QuantityValues[i]; oldCost = $scope.data.ScenarioCalendar[sourceRowIndex].CostValues[i]; updateQuantityOrCostCell($scope.editTotal.ExpCat, sourceRowIndex, i, oldQuantity * changeMult, oldCost * changeMult, null); } else for (i = 0; i < $scope.data.Headers.length; i++) { if ($scope.data.Headers[i].IsMonth) continue; updateQuantityOrCostCell($scope.editTotal.ExpCat, sourceRowIndex, i, changeMult / numberOfWeeks, changeMult * $scope.getRate($scope.editTotal.ExpCat, $scope.data.Headers[i].Milliseconds) / numberOfWeeks, null); } $("#editTotal").modal("hide"); }; $scope.recalcTotal = function () { for (var i = 0; i < $scope.data.ScenarioCalendar.length; i++) { if ($scope.data.ScenarioCalendar[i].ExpCatId === $scope.editTotal.ExpCat) { if ($scope.editTotal.SeatsCost == 'Quantity') $scope.editTotal.CurrentTotal = $scope.data.ScenarioCalendar[i].GrandTotalQuantity; else $scope.editTotal.CurrentTotal = $scope.data.ScenarioCalendar[i].GrandTotalCost; break; } } }; $scope.isOtherSelectedReallocator = function () { return $scope.reallocator.CurveType == 'other'; }; $scope.isOtherSelectedTotal = function () { return $scope.editTotal.Curve == 'other'; }; $scope.prefillFormatCells = function () { $scope.formatCells.ExpCats = []; for (var i = 1; i < $scope.data.ScenarioCalendar.length; i++) { // skip this row if it is not checked if (!$scope.data.ScenarioCalendar[i].Checked) continue; $scope.formatCells.ExpCats[$scope.formatCells.ExpCats.length] = { Id: $scope.data.ScenarioCalendar[i].ExpCatId, Name: $scope.data.ScenarioCalendar[i].ExpCatName }; } if ($scope.formatCells.ExpCats.length > 0) $scope.formatCells.SelectedExpCats = $scope.formatCells.ExpCats[0].Id; }; $scope.submitFormatCells = function () { if ($scope.data.ScenarioType === 9) return false; if (isNaN($scope.formatCells.DecimalPlaces)) { alert("Decimal Places is not set."); return false; } var colIndexes2Update = []; // gather column indexes which we're going to update var dtStart = $scope.formatCells.StartDate.split('/'); var msStart = new Date(dtStart[2], dtStart[0] - 1, dtStart[1]).getTime(); var dtEnd = $scope.formatCells.EndDate.split('/'); var msEnd = new Date(dtEnd[2], dtEnd[0] - 1, dtEnd[1]).getTime(); for (var headerIndex = 0; headerIndex < $scope.data.Headers.length; headerIndex++) { if ($scope.data.Headers[headerIndex].IsMonth) continue; if ($scope.data.Headers[headerIndex].Milliseconds >= msStart && $scope.data.Headers[headerIndex].Milliseconds <= msEnd && $scope.data.Headers[headerIndex].Milliseconds > $scope.data.ActualsEndDateMs) { colIndexes2Update[colIndexes2Update.length] = headerIndex; } } var oldQuantity = 0; var newQuantity = 0; var newCost = 0; var dec = Math.pow(10, parseInt($scope.formatCells.DecimalPlaces)); for (var rowIndex = 0; rowIndex < $scope.data.ScenarioCalendar.length; rowIndex++) { for (var catIndex = 0; catIndex < $scope.formatCells.ExpCats.length; catIndex++) { if ($scope.formatCells.ExpCats[catIndex].Id === $scope.data.ScenarioCalendar[rowIndex].ExpCatId) { for (var colIndex = 0; colIndex < colIndexes2Update.length; colIndex++) { oldQuantity = $scope.data.ScenarioCalendar[rowIndex].QuantityValues[colIndexes2Update[colIndex]]; newQuantity = Math.round(oldQuantity * dec) / dec; newCost = Math.round(newQuantity * $scope.getRate($scope.formatCells.ExpCats[catIndex].Id, $scope.data.Headers[colIndexes2Update[colIndex]].Milliseconds) * dec) / dec; if (oldQuantity === newQuantity) continue; updateQuantityOrCostCell($scope.formatCells.ExpCats[catIndex].Id, rowIndex, colIndexes2Update[colIndex], newQuantity, newCost, null); } break; } } } $("#modalFormatCells").modal("hide"); return true; }; $scope.onMonthHeaderClick = function (header) { header.Collapsed = !header.Collapsed; header.Show = !header.Show; header.CollapsedClass = header.Collapsed ? $scope.CollapsedIcon : $scope.NonCollapsedIcon; if (header.Weeks && header.Weeks.length > 0) for (var i = 0; i < header.Weeks.length; i++) { $scope.data.Headers[header.Weeks[i]].Collapsed = header.Collapsed; $scope.data.Headers[header.Weeks[i]].Show = !header.Collapsed; } $scope.resizeFreezAng(); }; $scope.onExpCatClick = function (row) { row.Collapsed = !row.Collapsed; //row.Show = !row.Show; row.CollapsedClass = row.Collapsed ? $scope.CollapsedIcon : $scope.NonCollapsedIcon; }; $scope.prefillAllocateResource = function () { $scope.allocateResource = new Object(); }; $scope.getResourceData = function () { var url = '/Scenarios/GetTeamScenarioResources'; $.post(url, { 'scenarioId': $scope.data.ScenarioId }, function (data) { if (data.Status == 'Ok') { $scope.allocateResource.allResourceList = data.Data; $scope.allocateResource.TeamsList = new Array(); for (var i = 0; i < data.Data.length; i++) { var item = data.Data[i]; if (item.Team != null) { for (var t = 0; t < item.Team.length; t++) { var team = item.Team[t]; if ($scope.allocateResource.TeamsList.length == 0) $scope.allocateResource.TeamsList.push(team); var inArray = false; for (var index = 0; index < $scope.allocateResource.TeamsList.length; index++) { if ($scope.allocateResource.TeamsList[index].Id == team.Id) inArray = true; } if (!inArray) $scope.allocateResource.TeamsList.push(team); } } } } else if (data.Status == 'Error') { console.error(data.Msg); } }); }; $scope.getResourcesByTeam = function () { $scope.allocateResource.ResourceList = new Array(); var teamId = $scope.allocateResource.SelectedTeam; for (var i = 0; i < $scope.allocateResource.allResourceList.length; i++) { var res = $scope.allocateResource.allResourceList[i]; var inTeam = false; var j; for (j = 0; j < res.Team.length; j++) { var team = res.Team[j]; if (team.Id == teamId) inTeam = true; } if (inTeam) { var inList = false; for (j = 0; j < $scope.allocateResource.ResourceList.length; j++) { var r = $scope.allocateResource.ResourceList[j]; if (r.Id == res.Id) inList = true; } if (!inList) $scope.allocateResource.ResourceList.push(res); } } return 0; }; $scope.fillTeamsByResources = function () { $scope.allocateResource.TeamsList = new Array(); for (var i = 0; i < $scope.allocateResource.ResourceList.length; i++) { var res = $scope.allocateResource.ResourceList[i]; if ($scope.allocateResource.TeamsList.indexOf(res.Team) == -1) { $scope.allocateResource.TeamsList.push(res.Team); } } }; }]); controllers.controller('capacityManagementController', ['$scope', '$http', '$location', '$timeout', function ($scope, $http, $location, $timeout) { $scope.CollapsedIcon = 'fa-plus-square'; $scope.NonCollapsedIcon = 'fa-minus-square'; $scope.ScenarioCollapsedIcon = 'fa-plus-square-1'; $scope.ScenarioNonCollapsedIcon = 'fa-minus-square-1'; $scope.ExpCatCollapsedIcon = 'fa-plus-square-2'; $scope.ExpCatNonCollapsedIcon = 'fa-minus-square-2'; $scope.id = $location.absUrl().substr($location.absUrl().lastIndexOf('ls/') + 3, 36); $scope.calendarFilters = { CompanyId: null, ViewId: null, TeamId: null, IsUOMHours: null, ShowUpper: true, ShowLower: true, IsBarMode: true, ShowCapacity: 1, //"Total Allocated/Total Capacity" GroupByTeam: true, IsViewModeMonth: true }; $scope.SpreadType = { Notspecified: -1, Equal: 0, Less: 1, Over: 2 }; $scope.companies = []; $http.post('/CapacityManagement/GetCompanies').success(function (data) { $scope.companies = data; }).error(function (data, status, headers, config) { console.log("an error occurred while loading rates"); }); $scope.data = null; $scope.data2Update = { ChangedExpCats: [] }; $scope.grandtotalrow = null; function getExpCatById(scenarioId, ExpCatId, teamId) { for (var i = 0; i < $scope.data.Calendar.length; i++) { if ($scope.data.Calendar[i].ExpCatId != null && $scope.data.Calendar[i].ScenarioId == scenarioId && $scope.data.Calendar[i].ExpCatId == ExpCatId && (!$scope.calendarFilters.GroupByTeam || $scope.data.Calendar[i].TeamId == teamId)) { return $scope.data.Calendar[i]; } } return null; } function getRowByType(type, expCatId) { for (var i = 0; i < $scope.data.Calendar.length; i++) { if ($scope.data.Calendar[i].RowType == type) { if (expCatId != null) { if ($scope.data.Calendar[i].ExpCatId == expCatId) return $scope.data.Calendar[i]; //return expCat by type+expCatId } else return $scope.data.Calendar[i]; //return expCat by type } } return null; } function getResourceById(resId) { for (var i = 0; i < $scope.data.AllResources.length; i++) { if ($scope.data.AllResources[i].Id == resId) { return $scope.data.AllResources[i]; } } return null; } function getResourceByExpCat(expCat, resId) { if (expCat != null) { for (var i = 0; i < expCat.Resources.length; i++) { if (expCat.Resources[i].Id == resId) { return expCat.Resources[i]; } } } return null; } function setChildrenCollapsed(row, internal, parentCollapsed) { var isProject = row.ProjectId != null && row.ExpCatId == null; var isExpCat = row.ExpCatId != null; for (var i = 0; i < $scope.data.Calendar.length; i++) { var currRow = $scope.data.Calendar[i]; if (isProject && currRow.ExpCatId != null && currRow.ProjectId == row.ProjectId && (!$scope.calendarFilters.GroupByTeam || currRow.TeamId == row.TeamId)) { var expCat = currRow; if (internal) { expCat.IsParentCollapsed = parentCollapsed; setChildrenCollapsed(expCat, true, parentCollapsed); } else { expCat.IsParentCollapsed = row.ProjectCollapsed; setChildrenCollapsed(expCat, true, row.ProjectCollapsed); } } else if (isExpCat && currRow.ExpCatId == row.ExpCatId && currRow.ScenarioId == row.ScenarioId && (!$scope.calendarFilters.GroupByTeam || currRow.TeamId == row.TeamId)) { var resource = currRow; if (internal) { resource.IsParentCollapsed = parentCollapsed; } } } } function clearResource(scenarioId, expCatId, resource) { if (resource != null) { resource.GrandTotalQuantity = 0; for (var i = 0; i < $scope.data.Headers.length; i++) { changeResourceValue(0, scenarioId, expCatId, resource.Id, i); } } } function updateTotals(expCat, expCatTotal, resourceTotal, resource, colIndex, newQuantity, oldQuantity, newCost, oldCost) { for (var i = colIndex + 1; i < $scope.data.Headers.length; i++) { if (!$scope.data.Headers[i].IsMonth) continue; //update month cell resource.QuantityValues[i] += (newQuantity - oldQuantity); resource.GrandTotalQuantity[i] += (newQuantity - oldQuantity); if (resourceTotal != null) { resourceTotal.QuantityValues[i] += (newQuantity - oldQuantity); expCatTotal.QuantityValues[i] += (newQuantity - oldQuantity); } recalcSpreading(expCat, expCatTotal, resourceTotal, i); updateCSSClass(expCat, resource, colIndex); break; } } function allocateResource(resource, expCat, create, allocateRest, reset) { if (resource != null) { resource.GrandTotalQuantity = 0; resource.GrandTotalCost = 0; var scenarioCalendarRow = null; var i; for (i = 0; i < $scope.data.Calendar.length; i++) { if ($scope.data.Calendar[i].ExpCatId == expCat.ExpCatId && $scope.data.Calendar[i].ScenarioId == expCat.ScenarioId) { scenarioCalendarRow = $scope.data.Calendar[i]; break; } } for (i = 0; i < $scope.data.Headers.length; i++) { var quantity = create || reset ? 0 : allocateRest ? resource.QuantityValues[i] + expCat.RestQuantity[i] : expCat.QuantityValues[i]; if (create) resource.QuantityValues[i] = 0; if (!$scope.data.Headers[i].IsMonth) { changeResourceValue(quantity, expCat.ScenarioId, expCat.ExpCatId, resource.Id, resource.TeamId, i); resource.GrandTotalQuantity += quantity; } } } } function recalcSpreading(expCat, expCatTotal, resourceTotal, colIndex) { var i; if (expCat != null) { var scenarioCalendarRow = null; for (i = 0; i < $scope.data.Calendar.length; i++) { if ($scope.data.Calendar[i].ProjectId == expCat.ProjectId && $scope.data.Calendar[i].ExpCatId == null && $scope.data.Calendar[i].ScenarioId == expCat.ScenarioId) { scenarioCalendarRow = $scope.data.Calendar[i]; break; } } var resTotal = 0; for (i = 0; i < expCat.Resources.length; i++) { resTotal += expCat.Resources[i].QuantityValues[colIndex]; } resTotal = Math.round(resTotal * 1000000) / 1000000; var compareRes = compareValues(resTotal, expCat.QuantityValues[colIndex]); expCat.SpreadVal[colIndex] = compareRes > 0 ? $scope.SpreadType.Over : compareRes < 0 ? $scope.SpreadType.Less : compareRes == 0 ? $scope.SpreadType.Equal : $scope.SpreadType.Notspecified; updateCSSClass(expCat, null, colIndex); var projectClass = $scope.SpreadType.Equal; for (i = 0; i < $scope.data.Calendar.length; i++) { if ($scope.data.Calendar[i].ProjectId == expCat.ProjectId && $scope.data.Calendar[i].ExpCatId != null && $scope.data.Calendar[i].ScenarioId == expCat.ScenarioId) { if ($scope.data.Calendar[i].SpreadVal[colIndex] == $scope.SpreadType.Over) { projectClass = $scope.SpreadType.Over; break; } else if ($scope.data.Calendar[i].SpreadVal[colIndex] == $scope.SpreadType.Less) { projectClass = $scope.SpreadType.Less; } } } scenarioCalendarRow.SpreadVal[colIndex] = projectClass; updateCSSClass(scenarioCalendarRow, null, colIndex); } //calc exp cat total row if (expCatTotal != null) { //var expCatSpread = $scope.SpreadType.Equal; for (i = 0; i < expCatTotal.Resources.length; i++) { var res = expCatTotal.Resources[i]; var resValue = res.QuantityTotalResValue[colIndex]; //expCatTotal.QuantityTotalResValue[colIndex]; var compareRes = compareValues(res.QuantityValues[colIndex], resValue); res.SpreadVal[colIndex] = compareRes > 0 ? $scope.SpreadType.Over : compareRes < 0 ? $scope.SpreadType.Less : compareRes == 0 ? $scope.SpreadType.Equal : $scope.SpreadType.Notspecified; updateCSSClass(expCatTotal, res, colIndex); //if (res.SpreadVal[colIndex] == $scope.SpreadType.Over) { // expCatSpread = $scope.SpreadType.Over; // break; //} else if (res.SpreadVal[colIndex] == $scope.SpreadType.Less) { // expCatSpread = $scope.SpreadType.Less; //} } //var resValue = expCatTotal.QuantityTotalResValue[colIndex]; //expCatTotal.SpreadVal[colIndex] = expCatTotal.QuantityValues[colIndex] > resValue ? $scope.SpreadType.Over : // expCatTotal.QuantityValues[colIndex] < resValue ? $scope.SpreadType.Less : // expCatTotal.QuantityValues[colIndex] == resValue ? $scope.SpreadType.Equal : $scope.SpreadType.Notspecified; //var expCatSpread = $scope.SpreadType.Equal; //if (expCatTotal.SpreadVal[colIndex] == $scope.SpreadType.Over) { // expCatSpread = $scope.SpreadType.Over; //} else if (expCatTotal.SpreadVal[colIndex] == $scope.SpreadType.Less) { // expCatSpread = $scope.SpreadType.Less; //} var resValue = expCatTotal.QuantityTotalResValue[colIndex]; var compareRes = compareValues(expCatTotal.QuantityValues[colIndex], resValue); expCatTotal.SpreadVal[colIndex] = compareRes > 0 ? $scope.SpreadType.Over : compareRes < 0 ? $scope.SpreadType.Less : compareRes == 0 ? $scope.SpreadType.Equal : $scope.SpreadType.Notspecified; var expCatSpread = $scope.SpreadType.Equal; if (expCatTotal.SpreadVal[colIndex] == $scope.SpreadType.Over) { expCatSpread = $scope.SpreadType.Over; } else if (expCatTotal.SpreadVal[colIndex] == $scope.SpreadType.Less) { expCatSpread = $scope.SpreadType.Less; } expCatTotal.SpreadVal[colIndex] = expCatSpread; updateCSSClass(expCatTotal, null, colIndex); } }; function compareValues(val1, val2) { if (!isNaN(parseFloat(val1)) && !isNaN(parseFloat(val2))) { var val1_ = Math.round(parseFloat(val1) * 1000) / 1000; var val2_ = Math.round(parseFloat(val2) * 1000) / 1000; if ((-0.005 > (val1_ - val2_))) return -1; else if ((0.005 < (val1_ - val2_))) return 1; else return 0; } } function updateCSSClass(row, resRow, colIndex) { if (resRow == null) { // updating project/scenario/category row if (row == null || colIndex == null || row.SpreadVal == null || row.SpreadVal.length <= colIndex) return; if (!row.ReadOnly[colIndex]) { if (row.SpreadVal[colIndex] == $scope.SpreadType.Equal) row.CSSClass[colIndex] = 'cellequal'; else if (row.SpreadVal[colIndex] == $scope.SpreadType.Over) row.CSSClass[colIndex] = 'cellover'; else if (row.SpreadVal[colIndex] == $scope.SpreadType.Less) row.CSSClass[colIndex] = 'cellless'; if (row.RowType == 0) { if (row.Color != null && row.Color.length > 0 && $scope.calendarFilters.IsBarMode) row.BarStyle[colIndex] = "background-color: " + row.Color +";border-right-color: " + row.Color + " !important;"; //"{'background-color': '"+ row.Color +"','border-right-color': '"+ row.Color +" !important'}"; //{"background-color": "' + row.Color + '","border-right-color": "' + row.Color + ' !important;"}'; //row.BarStyle[colIndex] = 'background-color: ' + row.Color + ';border-right-color: ' + row.Color + ' !important;'; else row.BarStyle[colIndex] = null; } } //TODO: assign more classes if needed } else { // updating resource row if (row == null || colIndex == null || row.ReadOnly[colIndex]) return; if (row.RowType == 1) { // top part of the grid //if(!$scope.data.Headers[colIndex].IsMonth && resRow.QuantityValues[colIndex] > resRow.CapacityQuantityValues[colIndex]) // resRow.CSSClass[colIndex] = 'cellover'; } else { // bottom part of the grid if (resRow.SpreadVal == null || resRow.SpreadVal.length <= colIndex) return; if (resRow.SpreadVal[colIndex] == $scope.SpreadType.Equal) resRow.CSSClass[colIndex] = 'cellequal'; else if (resRow.SpreadVal[colIndex] == $scope.SpreadType.Over) resRow.CSSClass[colIndex] = 'cellover'; else if (resRow.SpreadVal[colIndex] == $scope.SpreadType.Less) resRow.CSSClass[colIndex] = 'cellless'; } //TODO: assign more classes if needed } } function changeResourceValue(newValue, scenarioId, expCatId, resId, teamId, colIndex) { var expCat = getExpCatById(scenarioId, expCatId, teamId); var expCatTotal = getExpCatById(null, expCatId, null); var resource = getResourceByExpCat(expCat, resId); var resourceTotal = getResourceByExpCat(expCatTotal, resId);; if (expCat != null && resource != null) { resource.GrandTotalQuantity += (newValue - resource.QuantityValues[colIndex]); updateTotals(expCat, expCatTotal, resourceTotal, resource, colIndex, newValue, resource.QuantityValues[colIndex], 0, 0); expCat.RestQuantity[colIndex] -= newValue - resource.QuantityValues[colIndex]; if (expCatTotal != null && resourceTotal != null) { resourceTotal.QuantityValues[colIndex] += (newValue - resource.QuantityValues[colIndex]); expCatTotal.QuantityValues[colIndex] += (newValue - resource.QuantityValues[colIndex]); } resource.QuantityValues[colIndex] = newValue; applyCellChange(scenarioId, expCatId, $scope.data.Headers[colIndex].Milliseconds, expCat.DetailIds[colIndex], newValue, 0, resId, false, false); } recalcSpreading(expCat, expCatTotal, resourceTotal, colIndex); updateCSSClass(expCat, resource, colIndex); } function applyCellChange(scenarioId, rowId, cellMilliseconds, cellId, cellQuantity, cellCost, resId, isAdded, isRemoved) { var catIndex = -1; for (var ix = 0; ix < $scope.data2Update.ChangedExpCats.length; ix++) { if ($scope.data2Update.ChangedExpCats[ix].Id == rowId && $scope.data2Update.ChangedExpCats[ix].ScenarioId == scenarioId) { catIndex = ix; break; } } if (catIndex == -1) { $scope.data2Update.ChangedExpCats[$scope.data2Update.ChangedExpCats.length] = { ScenarioId: scenarioId, Id: rowId, Values: [], Resources: [] }; catIndex = $scope.data2Update.ChangedExpCats.length - 1; } if (resId == null) { var dateIndex = -1; for (var dx = 0; dx < $scope.data2Update.ChangedExpCats[catIndex].Values.length; dx++) { if ($scope.data2Update.ChangedExpCats[catIndex].Values[dx].Id == cellId) { dateIndex = dx; break; } } if (dateIndex == -1) { $scope.data2Update.ChangedExpCats[catIndex].Values[$scope.data2Update.ChangedExpCats[catIndex].Values.length] = { Id: cellId, Milliseconds: cellMilliseconds, Values: [] }; dateIndex = $scope.data2Update.ChangedExpCats[catIndex].Values.length - 1; } //$scope.data2Update.ChangedExpCats[catIndex].Values[dateIndex].Cost = cellCost; $scope.data2Update.ChangedExpCats[catIndex].Values[dateIndex].Quantity = cellQuantity; } else { var dateIndex = -1; for (var dx = 0; dx < $scope.data2Update.ChangedExpCats[catIndex].Resources.length; dx++) { if ($scope.data2Update.ChangedExpCats[catIndex].Resources[dx].Id == resId) { dateIndex = dx; break; } } if (dateIndex == -1) { $scope.data2Update.ChangedExpCats[catIndex].Resources[$scope.data2Update.ChangedExpCats[catIndex].Resources.length] = { Id: resId, Values: [], IsAdded: isAdded, IsRemoved: isRemoved }; dateIndex = $scope.data2Update.ChangedExpCats[catIndex].Resources.length - 1; if (isAdded || isRemoved) return; } else { if (isRemoved) { if ($scope.data2Update.ChangedExpCats[catIndex].Resources[dateIndex].IsAdded) { $scope.data2Update.ChangedExpCats[catIndex].Resources.splice(dateIndex, 1); } return; } } var dateIndex1 = -1; for (var dx = 0; dx < $scope.data2Update.ChangedExpCats[catIndex].Resources[dateIndex].Values.length; dx++) { if ($scope.data2Update.ChangedExpCats[catIndex].Resources[dateIndex].Values[dx].Milliseconds == cellMilliseconds) { dateIndex1 = dx; break; } } if (dateIndex1 == -1) { $scope.data2Update.ChangedExpCats[catIndex].Resources[dateIndex].Values[$scope.data2Update.ChangedExpCats[catIndex].Resources[dateIndex].Values.length] = { Id: cellId, Milliseconds: cellMilliseconds, Values: [] }; dateIndex1 = $scope.data2Update.ChangedExpCats[catIndex].Resources[dateIndex].Values.length - 1; } //$scope.data2Update.ChangedExpCats[catIndex].Resources[dateIndex].Values[dateIndex1].Cost = cellCost; $scope.data2Update.ChangedExpCats[catIndex].Resources[dateIndex].Values[dateIndex1].Quantity = cellQuantity; } } function RefreshTotalSpreadVal(expCatTotal) { if (expCatTotal.RowType == 7) { for (var colIndex = 0; colIndex < $scope.data.Headers.length; colIndex++) { var resValue = expCatTotal.QuantityTotalResValue[colIndex]; var total = 0; var allocated = 0; if ($scope.calendarFilters.ShowCapacity == 1) { allocated = expCatTotal.QuantityValues[colIndex]; total = expCatTotal.QuantityTotalResValue[colIndex]; } else if ($scope.calendarFilters.ShowCapacity == 2) { allocated = expCatTotal.QuantityExpCatTotalValue[colIndex]; total = expCatTotal.QuantityTotalResValue[colIndex]; } else { allocated = expCatTotal.QuantityValues[colIndex]; total = expCatTotal.QuantityExpCatTotalValue[colIndex]; } var compareRes = compareValues(allocated, total); expCatTotal.SpreadVal[colIndex] = compareRes > 0 ? $scope.SpreadType.Over : compareRes < 0 ? $scope.SpreadType.Less : compareRes == 0 ? $scope.SpreadType.Equal : $scope.SpreadType.Notspecified; var expCatSpread = $scope.SpreadType.Equal; if (expCatTotal.SpreadVal[colIndex] == $scope.SpreadType.Over) { expCatSpread = $scope.SpreadType.Over; } else if (expCatTotal.SpreadVal[colIndex] == $scope.SpreadType.Less) { expCatSpread = $scope.SpreadType.Less; } expCatTotal.SpreadVal[colIndex] = expCatSpread; updateCSSClass(expCatTotal, null, colIndex); } } //updateCSSClass(expCatTotal, null, colIndex); } $scope.getCalendar = function () { $("#loader").hide(); if ($scope.calendarFilters.CompanyId == null && $scope.calendarFilters.TeamId == null && $scope.calendarFilters.ViewId == null) { return; } blockUI(); $scope.data = null; $("#loader").show(); var postData = JSON.parse(JSON.stringify($scope.calendarFilters)); $http.post('/CapacityManagement/LoadJsonCalendar', postData). success(function (data, status, headers, config) { if (data == null) $("#loader").hide(); if (data != null && data.Headers == null || data.Headers.length < 1) { $("#loader").hide(); unblockUI(); return; } if (data.Calendar.length == 5) { $("#loader").hide(); resizeFreezCapacity(); } $scope.data = data; $scope.calendarFilters.IsUOMHours = data.IsUOMHours; $scope.switchViewMode($scope.calendarFilters.IsViewModeMonth); for (var i = 0; i < $scope.data.Calendar.length; i++) { if ($scope.data.Calendar[i].IsTotal && $scope.data.Calendar[i].RowType == 5) { $scope.grandtotalrow = $scope.data.Calendar[i]; //break; } $scope.data.Calendar[i].IsUpperHidden = !$scope.calendarFilters.ShowUpper; $scope.data.Calendar[i].IsLowerHidden = !$scope.calendarFilters.ShowLower; } // initial setup css classes for (var rowIndex = 0; rowIndex < $scope.data.Calendar.length; rowIndex++) { if ($scope.data.Calendar[rowIndex].CSSClass == null) $scope.data.Calendar[rowIndex].CSSClass = new Array($scope.data.Headers.length); if ($scope.data.Calendar[rowIndex].BarStyle == null) $scope.data.Calendar[rowIndex].BarStyle = new Array($scope.data.Headers.length); RefreshTotalSpreadVal($scope.data.Calendar[rowIndex]); for (var colIndex = 0; colIndex < $scope.data.Headers.length; colIndex++) { //if ($scope.data.Calendar[rowIndex].RowType == 7) // buttom row //{ //var row = $scope.data.Calendar[rowIndex]; //var total = 0; //var allocated = 0; //if ($scope.calendarFilters.ShowCapacity == 1) //{ // allocated = row.QuantityValues[colIndex]; // total = row.QuantityTotalResValue[colIndex]; //} //else if ($scope.calendarFilters.ShowCapacity == 2) //{ // allocated = row.QuantityExpCatTotalValue[colIndex]; // total = row.QuantityTotalResValue[colIndex]; //} else { // allocated = row.QuantityValues[colIndex]; // total = row.QuantityExpCatTotalValue[colIndex]; //} //row.SpreadVal[colIndex] = allocated > total ? $scope.SpreadType.Over : // allocated < total ? $scope.SpreadType.Less : // allocated == total ? $scope.SpreadType.Equal : $scope.SpreadType.Notspecified; //} if ($scope.data.Calendar[rowIndex].RowType != 7) updateCSSClass($scope.data.Calendar[rowIndex], null, colIndex); if ($scope.data.Calendar[rowIndex].Resources != null && $scope.data.Calendar[rowIndex].Resources.length > 0) { for (var resIndex = 0; resIndex < $scope.data.Calendar[rowIndex].Resources.length; resIndex++) { if ($scope.data.Calendar[rowIndex].Resources[resIndex].CSSClass == null) $scope.data.Calendar[rowIndex].Resources[resIndex].CSSClass = new Array($scope.data.Headers.length); updateCSSClass($scope.data.Calendar[rowIndex], $scope.data.Calendar[rowIndex].Resources[resIndex], colIndex); } } } } unblockUI(); }). error(function (data, status, headers, config) { // called asynchronously if an error occurs // or server returns response with an error status. console.log("an error occurred while loading calendar"); $("#loader").hide(); unblockUI(); }); }; $scope.checkEditable = function (useType) { if (3 === useType) { return false; } return true; }; $scope.watchKeyInput = function (t) { $timeout(function () { if (t.$editable.inputEl.select) t.$editable.inputEl.select(); else if (t.$editable.inputEl.setSelectionRange) t.$editable.inputEl.setSelectionRange(0, t.$editable.inputEl.val().length); }, 3); t.$editable.inputEl.on('keydown', function (e) { if (e.which == 9) { //when tab key is pressed e.preventDefault(); var tab2Cell; if (e.shiftKey) {// when shift + tab use with 'onblur' set to 'submit' for automatic submission find the parent of the editable before this one in the markup grab the editable and display it tab2Cell = $(this).parentsUntil('table#table').prevAll(":has(.editable:visible):first").find(".editable:visible:last"); t.$form.$submit(); tab2Cell.click(); } else {// when just tab use with 'onblur' set to 'submit' for automatic submission find the parent of the editable after this one in the markup grab the editable and display it tab2Cell = $(this).parentsUntil('table#table').nextAll(":has(.editable:visible):first").find(".editable:visible:first"); t.$form.$submit(); tab2Cell.click(); } } }); }; $scope.onTxtBlur = function (txt) { txt.$form.$submit(); }; $scope.saveChanges = function () { blockUI(); var postData = JSON.parse(JSON.stringify($scope.data2Update)); postData.ScenarioFilters = $scope.calendarFilters; $http.post('/CapacityManagement/SaveChanges', postData). success(function (data, status, headers, config) { $scope.data2Update = { ScenarioId: $scope.id, ChangedExpCats: [] }; alert("data saved successfully"); //var tab = $("#uidemo-tabs-default-demo li.active a").attr("href"); //var retUrl = window.location.href; //if (retUrl.indexOf("&tab=") > 0) { // retUrl = retUrl.substr(0, retUrl.indexOf("&tab=")); //} //window.location.href = retUrl; // + "&tab=" + tab.replace("#", ""); $scope.getCalendar(); }). error(function (data, status, headers, config) { console.log("an error occurred while saving calendar changes"); unblockUI(); }); }; $scope.resizeFreezAng = function () { setTimeout(function () { resizeFreezCapacity(); }, 0); }; $scope.takeAll = function (scenarioId, resId, expCatId, teamId) { if (confirm("Are you sure you want to re-assign category quantity and assign everything to this resource?")) { var expCat = getExpCatById(scenarioId, expCatId, teamId); var resource = null; for (var i = 0; i < expCat.Resources.length; i++) { if (expCat.Resources[i].Id == resId) { resource = expCat.Resources[i]; } else { clearResource(expCat.ScenarioId, expCat.ExpCatId, expCat.Resources[i]); } } var create = false; var allocateRest = false; var reset = false; allocateResource(resource, expCat, create, allocateRest, reset); //false, false, false); } }; $scope.takeRemaining = function (scenarioId, resId, expCatId, teamId) { var expCat = getExpCatById(scenarioId, expCatId, teamId); if (expCat != null) { var create = false; var allocateRest = true; var reset = false; allocateResource(getResourceByExpCat(expCat, resId), expCat, create, allocateRest, reset); //false, true, false); } }; $scope.zeroResource = function (scenarioId, resId, expCatId, teamId) { if (confirm("Are you sure you want fill this resource quantities with 0s?")) { var expCat = getExpCatById(scenarioId, expCatId, teamId); var create = false; var allocateRest = false; var reset = true; allocateResource(getResourceByExpCat(expCat, resId), expCat, create, allocateRest, reset); //false, false, true); } }; $scope.assignResource = function (resId, scenarioId, expCatId, teamId, $event) { if (resId == null || expCatId == null || teamId == null) return; var resource = getResourceById(resId); if (resource != null) { var expCat = getExpCatById(scenarioId, expCatId, teamId); var expCatTotal = getExpCatById(null, expCatId, null); var rowCapacity = getRowByType(6); var rowVacation = getRowByType(2); var rowTraining = getRowByType(3); //Check the same resource assignment if (getResourceByExpCat(expCat, resource.Id) != null) { alert("The " + resource.Name + " has been assigned to the " + expCat.Name); return; } //Add exp cat resource //todo: just added resources are not validated against capacity values var newResource = { 'Id': resource.Id, 'Name': resource.Name, 'QuantityValues': new Array($scope.data.Headers.length), 'SpreadVal': new Array($scope.data.Headers.length), 'CSSClass': new Array($scope.data.Headers.length), 'CapacityQuantityValues': new Array($scope.data.Headers.length), 'TeamId': resource.TeamId }; var i; for (i = 0; i < newResource.QuantityValues.length; i++) { if (isNaN(newResource.QuantityValues[i])) newResource.QuantityValues[i] = 0; } expCat.Resources.push(newResource); if (expCatTotal != null) { var resourceTotal = getResourceByExpCat(expCatTotal, resource.Id); var colIndex; if (resourceTotal == null) { //Add total exp cat resource resourceTotal = { 'Id': resource.Id, 'Name': resource.Name, 'QuantityValues': new Array($scope.data.Headers.length), 'SpreadVal': new Array($scope.data.Headers.length), 'CSSClass': new Array($scope.data.Headers.length) }; for (i = 0; i < resourceTotal.QuantityValues.length; i++) { if (isNaN(resourceTotal.QuantityValues[i])) resourceTotal.QuantityValues[i] = 0; } expCatTotal.QuantityTotalResValue = expCatTotal.QuantityResValue * (expCatTotal.Resources.length + 1); expCatTotal.Resources.push(resourceTotal); for (colIndex = 0; colIndex < $scope.data.Headers.length; colIndex++) { if (rowCapacity != null) { rowCapacity.QuantityValues[colIndex] += expCatTotal.QuantityResValue; } } } for (colIndex = 0; colIndex < $scope.data.Headers.length; colIndex++) { if (rowVacation != null) { rowVacation.QuantityValues[colIndex] += resource.VacationQuantityValues[colIndex]; } if (rowTraining != null) { rowTraining.QuantityValues[colIndex] += resource.TrainingQuantityValues[colIndex]; } newResource.CapacityQuantityValues[colIndex] = expCatTotal.QuantityResValue - resource.VacationQuantityValues[colIndex] - resource.TrainingQuantityValues[colIndex]; } } var create = true; var allocateRest = false; var reset = false; allocateResource(newResource, expCat, create, allocateRest, reset); //true, false, false); expCatTotal.EmptyScenario = expCatTotal.Resources.length == 0; var tr = $($event.target).parentsUntil('table').last().parent().find('thead tr:first'); console.log("tr: " + $(tr).html()); var thName = tr.find('th:eq(0)'); console.log("thName: " + $(thName).html()); newResource.width = thName.width() + 'px'; $scope.resizeFreezAng(); } }; $scope.removeResource = function (scenarioId, resId, expCatId, teamId) { if (confirm("Are you sure you want to remove this resource?")) { var expCat = getExpCatById(scenarioId, expCatId, teamId); var expCatTotal = getExpCatById(null, expCatId, null); var rowVacation = getRowByType(2); var rowTraining = getRowByType(3); var resource = getResourceById(resId); for (var i = 0; i < expCat.Resources.length; i++) { if (expCat.Resources[i].Id == resId) { for (var k = 0; k < expCat.QuantityValues.length; k++) { //expCat.RestQuantity[k] += expCat.Resources[i].QuantityValues[k]; changeResourceValue(0, expCat.ScenarioId, expCat.ExpCatId, expCat.Resources[i].Id, k); } expCat.Resources.splice(i, 1); applyCellChange(scenarioId, expCatId, 0, 0, 0, 0, resId, false, true); expCatTotal.EmptyScenario = expCatTotal.Resources.length == 0; for (var colIndex = 0; colIndex < $scope.data.Headers.length; colIndex++) { if (rowVacation != null) { rowVacation.QuantityValues[colIndex] -= resource.VacationQuantityValues[colIndex]; if (rowVacation.QuantityValues[colIndex] < 0) rowVacation.QuantityValues[colIndex] = 0; } if (rowTraining != null) { rowTraining.QuantityValues[colIndex] -= resource.TrainingQuantityValues[colIndex]; if (rowTraining.QuantityValues[colIndex] < 0) rowTraining.QuantityValues[colIndex] = 0; } } $scope.resizeFreezAng(); return; } } } }; $scope.checkResourceValue = function (data, scenarioId, expCatId, resId, teamId, colIndex) { var newValue = parseFloat(data); if (newValue < 0) { return "Value should not be less than zero"; } changeResourceValue(newValue, scenarioId, expCatId, resId, teamId, colIndex); return false; }; $scope.getExpCatResources = function (expCat) { var resources = []; for (var i = 0; i < $scope.data.AllResources.length; i++) { if ($scope.data.AllResources[i].ExpedentureCategoryId == expCat.ExpCatId && $scope.data.AllResources[i].IsActiveEmployee) { var isProjectFound = false; for (var j = 0; j < $scope.data.AllResources[i].ProjectIds.length; j++) { if ($scope.data.AllResources[i].ProjectIds[j] == expCat.ProjectId && (!$scope.calendarFilters.GroupByTeam || $scope.data.AllResources[i].TeamId == expCat.TeamId)) { isProjectFound = true; break; } } if (isProjectFound) { var isFound = false; for (var j = 0; j < expCat.Resources.length; j++) { if (expCat.Resources[j].Id == $scope.data.AllResources[i].Id && (!$scope.calendarFilters.GroupByTeam || expCat.Resources[j].TeamId == $scope.data.AllResources[i].TeamId)) { isFound = true; break; } } if (!isFound) { for (var j = 0; j < resources.length; j++) { if (resources[j].Id == $scope.data.AllResources[i].Id && (!$scope.calendarFilters.GroupByTeam || resources[j].TeamId == $scope.data.AllResources[i].TeamId)) { isFound = true; break; } } } if (!isFound && $scope.data.AllResources[i].AssignedToTeam) resources.push($scope.data.AllResources[i]); } } } return resources; } $scope.isTakeAllDisabled = function (expCat, res) { for (var j = 0; j < expCat.Resources.length; j++) { if (expCat.Resources[j].ReadOnly) { return true; } } return false; }; $scope.hideCalendarPart = function (upper) { if(upper) $scope.calendarFilters.ShowUpper = !$scope.calendarFilters.ShowUpper else $scope.calendarFilters.ShowLower = !$scope.calendarFilters.ShowLower; if (upper && !$scope.calendarFilters.ShowUpper) $("#showButtomMode").switcher('disable'); else $("#showButtomMode").switcher('enable'); if (!upper && !$scope.calendarFilters.ShowLower) $("#showTopMode").switcher('disable'); else $("#showTopMode").switcher('enable'); if ($scope.data != null) { for (var i = 0; i < $scope.data.Calendar.length; i++) { $scope.data.Calendar[i].IsLowerHidden = !$scope.calendarFilters.ShowLower; $scope.data.Calendar[i].IsUpperHidden = !$scope.calendarFilters.ShowUpper; } } onCMPreferencesItemClick($("#showButtomMode")); onCMPreferencesItemClick($("#showTopMode")); }; $scope.switchUOMMode = function (value) { var newValue = value != null ? value : !$scope.calendarFilters.IsUOMHours; $scope.calendarFilters.IsUOMHours = newValue; $scope.getCalendar(); }; $scope.switchGroupByTeam = function (value) { var newValue = value != null ? value : !$scope.calendarFilters.GroupByTeam; $scope.calendarFilters.GroupByTeam = newValue; onCMPreferencesItemClick($("#groupTeam")); $scope.getCalendar(); }; $scope.switchViewMode = function (value) { var newValue = value != null ? value : !$scope.calendarFilters.IsViewModeMonth; $scope.calendarFilters.IsViewModeMonth = newValue; for (var i = 0; i < $scope.data.Headers.length; i++) { if ($scope.data.Headers[i].IsMonth) { $scope.data.Headers[i].Collapsed = newValue; $scope.data.Headers[i].Show = newValue; $scope.data.Headers[i].CollapsedClass = newValue ? $scope.CollapsedIcon : $scope.NonCollapsedIcon; } else { $scope.data.Headers[i].Collapsed = newValue; $scope.data.Headers[i].Show = !newValue; } } $scope.resizeFreezAng(); onCMPreferencesItemClick($("#defaultView")); }; $scope.switchBarMode = function () { var newValue = !$scope.calendarFilters.IsBarMode; $scope.calendarFilters.IsBarMode = newValue; // update project rows CSS for (var rowIndex = 0; rowIndex < $scope.data.Calendar.length; rowIndex++) { if ($scope.data.Calendar[rowIndex].RowType == 0) for (var colIndex = 0; colIndex < $scope.data.Headers.length; colIndex++) { updateCSSClass($scope.data.Calendar[rowIndex], null, colIndex); } } onCMPreferencesItemClick($("#capBarMode")); }; $scope.changeCapacity = function (value) { $scope.calendarFilters.ShowCapacity = value; for (var i = 0; i < $scope.data.Calendar.length; i++) { if ($scope.data.Calendar[i].RowType == 7) { var expCatTotal = $scope.data.Calendar[i]; RefreshTotalSpreadVal(expCatTotal); } } onCMPreferencesItemClick($("#selectOptions")); }; $scope.anyUpdated = function () { return ($scope.data2Update && $scope.data2Update.ChangedExpCats && $scope.data2Update.ChangedExpCats.length > 0); }; $scope.onMonthHeaderClick = function (header) { header.Collapsed = !header.Collapsed; header.Show = !header.Show; header.CollapsedClass = header.Collapsed ? $scope.CollapsedIcon : $scope.NonCollapsedIcon; if (header.Weeks && header.Weeks.length > 0) for (var i = 0; i < header.Weeks.length; i++) { $scope.data.Headers[header.Weeks[i]].Collapsed = header.Collapsed; $scope.data.Headers[header.Weeks[i]].Show = !header.Collapsed; } for (var i = 0; i < $scope.data.YearHeaders.length; i++) { if ($scope.data.YearHeaders[i].Title == header.Year) { if (header.Collapsed) { $scope.data.YearHeaders[i].SpanCount -= header.Weeks.length - 1; } else { $scope.data.YearHeaders[i].SpanCount += header.Weeks.length - 1; } break; } } $scope.resizeFreezAng(); }; $scope.onParentNodeClick = function (row) { if (row.ProjectId != null && row.ExpCatId == null) { row.ProjectCollapsed = !row.ProjectCollapsed; row.CollapsedClass = row.ProjectCollapsed ? $scope.CollapsedIcon : $scope.NonCollapsedIcon; } else if(row.ExpCatId != null){ row.ExpCatCollapsed = !row.ExpCatCollapsed; if(row.ScenarioId != null) row.CollapsedClass = row.ExpCatCollapsed ? $scope.ExpCatCollapsedIcon : $scope.ExpCatNonCollapsedIcon; else row.CollapsedClass = row.ExpCatCollapsed ? $scope.CollapsedIcon : $scope.NonCollapsedIcon; } //else if (row.ExpCatId == null) { // row.ScenarioCollapsed = !row.ScenarioCollapsed; // row.CollapsedClass = row.ScenarioCollapsed ? $scope.ScenarioCollapsedIcon : $scope.ScenarioNonCollapsedIcon; //} setChildrenCollapsed(row, false, row.IsParentCollapsed); }; $scope.CheckLock = function ($event, tableId, fieldId, url) { //TODO: uncomment when name column UI will be fixed if (CheckLock(event.target.id, tableId, fieldId)) document.location.href = url + '/' + fieldId; }; }]); controllers.controller('costSavingController', ['$scope', '$http', '$location', '$timeout', function ($scope, $http, $location, $timeout) { $scope.CollapsedIcon = 'fa-plus-square'; $scope.NonCollapsedIcon = 'fa-minus-square'; $scope.Dec = Math.pow(10, 3);//precision decimals $scope.data = null; //$scope.id = $location.absUrl().substr($location.absUrl().lastIndexOf('ls/') + 3, 36); $scope.filters = { CollapsedClass: $scope.CollapsedIcon, Show: false, Months: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], StartDate: null, EndDate: null, CostSavings: null, IsCostChanging: false }; $scope.$watch("filters.StartDate", function (newValue, oldValue) { if (newValue != oldValue) { $scope.getYears(); } }); $scope.$watch("filters.EndDate", function (newValue, oldValue) { if (newValue != oldValue) { $scope.getYears(); } }); $scope.$watch("filters.CostSavings", function (newValue, oldValue) { if (newValue != oldValue && !$scope.filters.IsCostChanging) { $scope.getYears(); } $scope.filters.IsCostChanging = false; }); $scope.setStartDate = function(value) { $scope.filters.StartDate = value; $scope.getYears(); }; $scope.getYears = function() { $scope.data = null; if ($scope.filters.StartDate == null || $scope.filters.StartDate.length == 0 || $scope.filters.EndDate == null || $scope.filters.EndDate.length == 0 || $scope.filters.CostSavings == null || $scope.filters.CostSavings.length == 0) { return; } var sd = new Date($scope.filters.StartDate); var ed = new Date($scope.filters.EndDate); var cs = parseFloat($scope.filters.CostSavings); if (isNaN(sd) || isNaN(ed) || isNaN(cs) || cs <= 0) { return; } blockUI(); if (ed - sd > 3155760000000) ed = new Date(sd + 3155760000000); var sdYear = sd.getFullYear(); var edYear = ed.getFullYear(); var data = new Array(); var startMonth = sd.getMonth(); var endMonth = ed.getMonth(); var monthNum = 0; for (var yearIndex = 0; yearIndex <= edYear - sdYear; yearIndex++) { var arrYear = new Array(); arrYear.push(0); for (var monthIndex = 0; monthIndex < 12; monthIndex++) { if (yearIndex == 0 && monthIndex < startMonth) {// before start date arrYear.push(null); } else if (yearIndex == (edYear - sdYear) && monthIndex > endMonth) {// after end date arrYear.push(null); } else {// inside date range arrYear.push(0); monthNum++; } } data.push({Year: sdYear+yearIndex, Costs: arrYear}); } var dec = Math.pow(10, 3);//precision decimals var itemCost = Math.round(cs * dec / monthNum) / dec; for (var i = 0; i < data.length; i++) { for (var j = 1; j < data[i].Costs.length; j++) { if (data[i].Costs[j] != null) { data[i].Costs[j] = itemCost; data[i].Costs[0] += itemCost; } } data[i].Costs[0] = Math.round(data[i].Costs[0] * dec) / dec; } $scope.data = data; unblockUI(); }; $scope.onMonthHeaderClick = function () { $scope.filters.Show = !$scope.filters.Show; $scope.filters.CollapsedClass = $scope.filters.Show ? $scope.NonCollapsedIcon : $scope.CollapsedIcon; }; $scope.onTxtBlur = function (txt) { txt.$form.$submit(); }; $scope.watchKeyInput = function (t) { //TODO: костыль, проблема во вложенных формах // попробовать это https://github.com/vitalets/x-editable/issues/384 try { //$.validator(t.$editable.editorEl); t.$editable.editorEl.validate(); } catch (ex) { } $timeout(function () { if (t.$editable.inputEl.select) t.$editable.inputEl.select(); else if (t.$editable.inputEl.setSelectionRange) t.$editable.inputEl.setSelectionRange(0, t.$editable.inputEl.val().length); }, 3); t.$editable.inputEl.on('keydown', function (e) { if (e.which == 9) { //when tab key is pressed e.preventDefault(); var tab2Cell; if (e.shiftKey) {// when shift + tab use with 'onblur' set to 'submit' for automatic submission find the parent of the editable before this one in the markup grab the editable and display it tab2Cell = $(this).parentsUntil('table#costSaving-table').prevAll(":has(.editable:visible):first").find(".editable:visible:last"); t.$form.$submit(); tab2Cell.click(); } else {// when just tab use with 'onblur' set to 'submit' for automatic submission find the parent of the editable after this one in the markup grab the editable and display it tab2Cell = $(this).parentsUntil('table#costSaving-table').nextAll(":has(.editable:visible):first").find(".editable:visible:first"); t.$form.$submit(); tab2Cell.click(); } } }); }; $scope.checkValue = function (data, yearIndex, colIndex) { var newValue = parseFloat(data); if (isNaN(newValue)) newValue = 0; if (newValue < 0) { return "Value should not be less than zero"; } if (colIndex == 0) { $scope.data[yearIndex].Costs[0] = Math.round(newValue * $scope.Dec) / $scope.Dec; var num = 0; var j; for (j = 1; j <= 12; j++) { if ($scope.data[yearIndex].Costs[j] != null) num++; } var val = Math.round(newValue * $scope.Dec / num) / $scope.Dec; for (j = 1; j <= 12; j++) { if ($scope.data[yearIndex].Costs[j] != null) $scope.data[yearIndex].Costs[j] = val; } } else { var oldValue = $scope.data[yearIndex].Costs[colIndex]; $scope.data[yearIndex].Costs[colIndex] = Math.round(newValue * $scope.Dec) / $scope.Dec; $scope.data[yearIndex].Costs[0] += Math.round((newValue - oldValue) * $scope.Dec) / $scope.Dec; if ($scope.data[yearIndex].Costs[0] < 0) $scope.data[yearIndex].Costs[0] = 0; } var sum = 0; for (var i = 0; i < $scope.data.length; i++) { sum += $scope.data[i].Costs[0]; } sum = Math.round(sum * 100) / 100; $scope.filters.IsCostChanging = true; $scope.filters.CostSavings = sum; //required to be false by xeditable grid return false; }; }]);