/* =========================================================== * CapacityPlanning.js v0.0.1 * =========================================================== * Copyright 2015 Prevu * ========================================================== */ (function ($) { "use strict"; // jshint ;_; /* CapacityPlanning CLASS DEFINITION * ====================== */ var CapacityPlanning = function (element, options) { this.init(element, options); }; CapacityPlanning.prototype = { constructor: CapacityPlanning, init: function (element, options) { var plugin = this; this.options = options; this.$container = $(element); this.$dataset = {}; this.$dataset.data = []; this.$dataLoaded = false; this.$controls = {}; this.$maxDate = this.getUtcFromText("1/1/2100"); this.$allCatgoriesIndex = null; if (!this.options.teamId) { throw "Team Id must be set"; } // Init filter elements: datepickers and select2 this.$container.find('[data-capacity-planning="filterDates"]').datepicker(this.options.datePickerOptions); this.$controls.categoriesFilter = this.$container.find('[data-capacity-planning="filterCategories"]').select2(); // Attach event receivers to controls this.$controls.isPermanentFilter = this.$container.find('[data-capacity-planning="filterIsPermanent"]'); this.$controls.isPermanentFilter.on("click", function () { plugin.switchPermanent(this); }); this.$container.find('[data-capacity-planning="filterIncreaseCapacity"]').on("click", function () { plugin.increaseCapacity(); }); this.$container.find('[data-capacity-planning="filterDecreaseCapacity"]').on("click", function () { plugin.decreaseCapacity(); }); this.$container.find('[data-capacity-planning="filterResetCapacity"]').on("click", function () { plugin.resetCapacity(); }); this.$container.find('[data-capacity-planning="filterResetEntireCapacity"]').on("click", function () { plugin.resetEntireCapacity(this); }); this.$container.find('[data-capacity-planning="formDecreaseSubmitBtn"]').on("click", function () { plugin.fillDecreaseSubmit(this); }); // Caching other controls this.$controls.dataTable = this.$container.find('[data-capacity-planning="dataTable"]'); this.$controls.endDateFilter = this.$container.find('[data-capacity-planning="filterEndDate"]'); this.$controls.startDateFilter = this.$container.find('[data-capacity-planning="filterStartDate"]'); this.$controls.amountFilter = this.$container.find('[data-capacity-planning="filterAmount"]'); // Public initial ecpenditure categories list this.initCategoriesFilter(); this.createCategoriesIndex(); if (this.options.data) { this.setData(this.options.data); } if (this.options.initCallback && (typeof this.options.initCallback === 'function')) { this.options.initCallback(this.$dataset); } }, initCategoriesFilter: function () { var expCatDropDown = this.$controls.categoriesFilter; expCatDropDown.empty(); var selInPlanHtml = $('').prop('label', "Categories in Plan").appendTo(expCatDropDown); var selAddHtml = $('').prop('label', "Add Category").appendTo(expCatDropDown); if (!this.options.expenditureCategories || (this.options.expenditureCategories.length < 1)) return; $.each(this.options.expenditureCategories, function () { $("").val(this.Id).text(this.Name).appendTo(this.InPlan == true ? selInPlanHtml : selAddHtml); }); expCatDropDown.select2({ allowClear: true }); }, switchPermanent: function (control) { var isChecked = this.$controls.isPermanentFilter.prop('checked'); if (isChecked) { this.$controls.endDateFilter.css('visibility', 'hidden'); this.$container.find('.enddatespan').hide(); } else { this.$controls.endDateFilter.css('visibility', 'visible'); this.$container.find('.enddatespan').show(); } }, createCategoriesIndex: function () { this.$allCatgoriesIndex = []; var currentCat; for (var index = 0; index < this.options.expenditureCategories.length; index++) { currentCat = this.options.expenditureCategories[index]; this.$allCatgoriesIndex[currentCat.Id] = currentCat; } }, getCategoryById: function (id) { return this.$allCatgoriesIndex[id]; }, categoryExists: function (catId) { var catItem = this.getCategoryById(catId); return (catItem !== undefined); }, increaseCapacity: function () { if (!this.isFormValid()) return; var startDateAsText = this.$controls.startDateFilter.val(); var endDateAsText = this.$controls.endDateFilter.val(); var expCat = this.$controls.categoriesFilter.val(); var amount = this.$controls.amountFilter.val(); var isPermanent = this.$controls.isPermanentFilter.prop('checked'); var startDate = this.getUtcFromText(startDateAsText); var endDate = this.getUtcFromText(endDateAsText); if (isPermanent) endDate = null; this.changeCapacity(startDate, endDate, expCat, amount); this.renderTable(); }, decreaseCapacity: function () { if (!this.isFormValid()) return; var startDateAsText = this.$controls.startDateFilter.val(); var endDateAsText = this.$controls.endDateFilter.val(); var expCat = this.$controls.categoriesFilter.val(); var amount = this.$controls.amountFilter.val(); var isPermanent = this.$controls.isPermanentFilter.prop('checked'); var startDate = this.getUtcFromText(startDateAsText); var endDate = this.getUtcFromText(endDateAsText); if (isPermanent) endDate = null; this.changeCapacity(startDate, endDate, expCat, -amount); this.renderTable(); }, changeCapacity: function (startDate, endDate, expCat, amount) { var positions = null; var startDateUnix = this.getUnixDateFromUtc(startDate); var endDateUnix = this.getUnixDateFromUtc(endDate); if (this.$dataset.data && (this.$dataset.data.length > 0)) { for (var catIndex = 0; catIndex < this.$dataset.data.length; catIndex++) { if (this.$dataset.data[catIndex].Id == expCat) { positions = this.$dataset.data[catIndex].Positions; break; } } } if (!positions) { // Category not found var expCatItem = this.getCategoryById(expCat); if (expCatItem) { var newCatItem = { Id: expCatItem.Id, Name: expCatItem.Name, InPlan: expCatItem.InPlan, Positions: [] }; this.$dataset.data.push(newCatItem); positions = newCatItem.Positions; } else throw "Expenditure category was not found in initial list of categories"; } if (amount > 0) { // Perform increase capacity for (var index = 0; index < amount; index++) { var newItem = { Need: 1, StartDate: startDateUnix, EndDate: endDateUnix } positions.push(newItem); } } else { // Perform decrease capacity // try to find the exact suitable rows var remainingAmount = amount; var endDateExact = endDate ? endDate : this.$maxDate; for (var index = 0; index < Math.abs(amount) ; index++) { for (var posIndex = positions.length - 1; posIndex >= 0; posIndex--) { if ((positions[posIndex].StartDateEx == startDate) && (positions[posIndex].EndDateEx == endDateExact) && (positions[posIndex].Need == 1)) { positions.splice(posIndex, 1); remainingAmount++; // is negative value break; } } } if (Math.abs(remainingAmount) > 0) { // looking for partial rows to decrease var removablePositions = []; for (var index = 0; index < Math.abs(remainingAmount) ; index++) { var posItem = { StartDate: startDateUnix, EndDate: endDateUnix, StartDateEx: startDate, EndDateEx: endDateExact }; removablePositions.push(posItem); } var remIndex = 0; while (remIndex < removablePositions.length) { var currRemovablePos = removablePositions[remIndex]; for (var posIndex = positions.length - 1; posIndex >= 0; posIndex--) { if ((currRemovablePos.StartDateEx <= positions[posIndex].StartDateEx) && (currRemovablePos.EndDateEx > positions[posIndex].StartDateEx) && (currRemovablePos.EndDateEx < positions[posIndex].EndDateEx)) { var tmpStartDate = positions[posIndex].StartDate; var tmpStartDateEx = positions[posIndex].StartDateEx; positions[posIndex].StartDate = currRemovablePos.EndDate; positions[posIndex].StartDateEx = currRemovablePos.EndDateEx; currRemovablePos.EndDate = tmpStartDate; currRemovablePos.EndDateEx = tmpStartDateEx; if (currRemovablePos.StartDateEx == currRemovablePos.EndDateEx) { removablePositions.splice(remIndex, 1); remIndex = -1; break; } } else { if ((currRemovablePos.StartDateEx > positions[posIndex].StartDateEx) && (currRemovablePos.StartDateEx < positions[posIndex].EndDateEx) && (currRemovablePos.EndDateEx >= positions[posIndex].EndDateEx)) { var tmpEndDate = positions[posIndex].EndDate; var tmpEndDateEx = positions[posIndex].EndDateEx; positions[posIndex].EndDate = currRemovablePos.StartDate; positions[posIndex].EndDateEx = currRemovablePos.StartDateEx; currRemovablePos.StartDate = tmpEndDate; currRemovablePos.StartDateEx = tmpEndDateEx; if (currRemovablePos.StartDateEx == currRemovablePos.EndDateEx) { removablePositions.splice(remIndex, 1); remIndex = -1; break; } } else { if ((currRemovablePos.StartDateEx <= positions[posIndex].StartDateEx) && (currRemovablePos.EndDateEx >= positions[posIndex].EndDateEx)) { if (positions[posIndex].EndDateEx != currRemovablePos.EndDateEx) { var newPosItem = { StartDate: positions[posIndex].EndDate, EndDate: currRemovablePos.EndDate, StartDateEx: positions[posIndex].EndDateEx, EndDateEx: currRemovablePos.EndDateEx }; removablePositions.push(newPosItem); } currRemovablePos.EndDate = positions[posIndex].StartDate; currRemovablePos.EndDateEx = positions[posIndex].StartDateEx; positions.splice(posIndex, 1); if (currRemovablePos.StartDateEx == currRemovablePos.EndDateEx) { removablePositions.splice(remIndex, 1); remIndex = -1; break; } } else { if ((currRemovablePos.StartDateEx > positions[posIndex].StartDateEx) && (currRemovablePos.EndDateEx < positions[posIndex].EndDateEx)) { var tmpEndDate = positions[posIndex].EndDate; var tmpEndDateEx = positions[posIndex].EndDateEx; positions[posIndex].EndDate = currRemovablePos.StartDate; positions[posIndex].EndDateEx = currRemovablePos.StartDateEx; var newPos = { Need: positions[posIndex].Need, StartDate: currRemovablePos.EndDate, EndDate: tmpEndDate, StartDateEx: currRemovablePos.EndDateEx, EndDateEx: tmpEndDateEx } positions.push(newPos); removablePositions.splice(remIndex, 1); remIndex = -1; break; } } } } } remIndex++; } } } }, deleteRecord: function (element) { var itemId = $(element).attr('data-capacity-planning'); for (var catIndex = this.$dataset.data.length - 1; catIndex >= 0; catIndex--) { var positions = this.$dataset.data[catIndex].Positions; if (positions != null) { for (var posIndex = positions.length - 1; posIndex >= 0; posIndex--) { if (positions[posIndex].Id == itemId) { positions.splice(posIndex, 1); break; } } } } this.renderTable(); }, resetCapacity: function () { var startDateAsText = this.$controls.startDateFilter.val(); var endDateAsText = this.$controls.endDateFilter.val(); var expCat = this.$controls.categoriesFilter.val(); var isPermanent = this.$controls.isPermanentFilter.prop('checked'); var startDate = this.getUtcFromText(startDateAsText); var endDate = this.getUtcFromText(endDateAsText); if (isPermanent) endDate = null; var decreaseBoundValue = this.getPositionsCount() + 1; this.changeCapacity(startDate, endDate, expCat, -decreaseBoundValue); this.renderTable(); }, resetEntireCapacity: function (control) { var plugin = this; bootbox.dialog({ title: "Reset Planned Capacity", message: '
Are you sure you want to reset Planned Capacity and make it equal to Actual Capacity?
' + '