EnVisageOnline/Main/Source/EnVisage/Views/SingleResQtiesOrCostByTime/Index.cshtml

561 lines
26 KiB
Plaintext

@model EnVisage.Models.SingleResQtiesOrCostByTimeModel
@using EnVisage.Code;
@{
ViewBag.Title = "Single Resource Quantities or Cost by Time";
var isUOMHoursChecked = "checked";
if (ViewBag.IsUOMHours != null)
{
isUOMHoursChecked = ViewBag.IsUOMHours ? "checked" : string.Empty;
}
}
@section scripts
{
<script>
function saveReportPagePreferences() {
var prefs = collectPreferences('reportPage');
saveUserPagePreferences(prefs, 'reportPage');
}
function loadReportPagePreferences() {
restorePreferences('reportPage', loadUserPagePreferences('reportPage'));
}
var datePickerOptions = {
format: 'm/d/yyyy',
autoclose: true,
startDate: '@(Constants.MIN_SELECTABLE_DATE)', // SA. ENV-1235
endDate: '@(Constants.MAX_SELECTABLE_DATE)' // SA. ENV-1235
};
init.push(function () {
init_form();
});
function init_form() {
@foreach (var expCat in Model.SelectedExpenditureItems.TrimStart('[').TrimEnd(']').Split(','))
{
@:document.getElementById("expCatCheckedId_@expCat.TrimStart('"').TrimEnd('"')").checked = true;
}
//set page preferences' key to StartDate and EndDate controls manually as it does not work automatically
$('input[type=text]#@Html.IdFor(m => m.StartDate)').attr("data-key", "filterStartDate");
$('input[type=text]#@Html.IdFor(m => m.EndDate)').attr("data-key", "filterEndDate");
var currGroup = $('#@Html.IdFor(m => m.GroupId)').val();
var currType = $('#@Html.IdFor(m => m.SelectedScenarioType)').val();
loadReportPagePreferences();
updateHidden('@Html.IdFor(m => m.StartDate)', $('input[type=text]#@Html.IdFor(m => m.StartDate)').val());
updateHidden('@Html.IdFor(m => m.EndDate)', $('input[type=text]#@Html.IdFor(m => m.EndDate)').val());
updateHidden('@Html.IdFor(m => m.SeatsCosts)', $('select#@Html.IdFor(m => m.SeatsCosts)').val());
updateHidden('@Html.IdFor(m => m.SystemAttribute1)', $('select#@Html.IdFor(m => m.SystemAttribute1)').val());
updateHidden('@Html.IdFor(m => m.SystemAttribute2)', $('select#@Html.IdFor(m => m.SystemAttribute1)').val());
saveSelectedScenarios();
saveSelectedExpsCats();
$(".datepicker").datepicker(datePickerOptions)
.change(function (ev) {
updateHidden(ev.target.id, $(ev.target).val());
});
$('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
InitGraph(e.target.hash);
});
var options2 = {
orientation: $('body').hasClass('right-to-left') ? "auto right" : 'auto auto',
startDate: '@Constants.MIN_SELECTABLE_DATE', // SA. ENV-1235. Disable incorrect dates input
endDate: '@Constants.MAX_SELECTABLE_DATE' // SA. ENV-1235. Disable incorrect dates input
};
$('#bs-datepicker-range').datepicker(options2);
$('#filterStartDate').addClass('form-control');
$('#filterEndDate').addClass('form-control');
$('#filterStartDate').removeClass('text-box');
$('#filterEndDate').removeClass('text-box');
$('#filterStartDate').removeClass('single-line');
$('#filterEndDate').removeClass('single-line');
$('#uomMode').switcher({
on_state_content: '# Hours',
off_state_content: '# Resources'
});
$('#uomMode').parent().css("width", "100px");
$.each($('#expendituresContainer table#uom tbody tr'), function (i, o) {
if ($(o).has('input[type=checkbox]:checked')) {
$(o).removeClass('unchecked');
} else {
$(o).addClass('unchecked');
}
});
if ($('#@Html.IdFor(m => m.GroupId)').val() != currGroup || $('#@Html.IdFor(m => m.SelectedScenarioType)').val() != currType)
$('#filterForm').submit();
InitGraph($("ul#tabs li.active a").attr('href'));
}
function onFailure(xhr) {
$('#efilter').html(xhr.responseText);
}
function onSuccess(data) {
$('.validation-summary-errors').html('');
$('.input-validation-error').removeClass('input-validation-error');
$('.field-validation-error').remove();
//document.location.href = document.location.href;
init_form();
}
function InitGraph(hash) {
blockUI();
var __siteRoot = '@HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Authority)@Url.Content("~/")';
var url = __siteRoot + "SingleResQtiesOrCostByTime/GetGraphData";
var selectedScenarios = $("#@Html.IdFor(x => x.SelectedScenarios)").val();
$.ajax({
type: "POST",
contentType: "application/json; charset=utf-8",
url: url,
data: JSON.stringify({
startDate: $("#@Html.IdFor(x => x.StartDate)").val(),
endDate: $("#@Html.IdFor(x => x.EndDate)").val(),
cost: $("#@Html.IdFor(x => x.SeatsCosts)").val() == "1" || $("#@Html.IdFor(x => x.SeatsCosts)").val() == "Cost",
selectedScenarios: JSON.parse(selectedScenarios.length == 0 ? '[]' : selectedScenarios),
selectedExpenditureItems: JSON.parse($("#@Html.IdFor(x => x.SelectedExpenditureItems)").val()),
graphType: ($("ul#tabs li.active a").attr('href') == '#general' ? "0" : "1"),
type: $("#@Html.IdFor(x => x.SelectedScenarioType)").val(),
groupId: $("#@Html.IdFor(x => x.GroupId)").val(),
SystemAttributeOne: $("#@Html.IdFor(x => x.SystemAttribute1)").val(),
SystemAttributeTwo: $("#@Html.IdFor(x => x.SystemAttribute2)").val(),
isUOMHours: $('#uomMode').prop('checked')
}),
dataType: "json",
success: function (data) {
var maxValue = 0;
var graphData = new Array();
for (var i = 0; i < data.SingleResQtiesOrCostByTime.length; i++) {
var expCatData = new Array();
for (var colIndex = 0; colIndex < data.FiscalCalendarRecordHeaders.length; colIndex++) {
var val = 0;
var projectName = data.SingleResQtiesOrCostByTime[i].ProjectName;
if ($("#@Html.IdFor(x => x.SeatsCosts)").val() == "1" || $("#@Html.IdFor(x => x.SeatsCosts)").val() == "Cost")
val = data.SingleResQtiesOrCostByTime[i].CostValues[colIndex];
else
val = data.SingleResQtiesOrCostByTime[i].QuantityValues[colIndex];
if (val == undefined)
val = 0;
//val = Math.round(val * 100) / 100;
expCatData[colIndex] = [data.FiscalCalendarRecordMilliseconds[colIndex], val, projectName];
if (expCatData[colIndex][1] > maxValue)
maxValue = expCatData[colIndex][1];
}
if ("TeamPlannedCapacity" == data.SingleResQtiesOrCostByTime[i].ScenarioType || "TeamActualCapacity" == data.SingleResQtiesOrCostByTime[i].ScenarioType) {
graphData[i] = {
label: data.SingleResQtiesOrCostByTime[i].ScenarioDisplayName,
data: expCatData,
lines: { show: true, fill: false, steps: false },
filledPoints: true,
stack: false,
probability: data.SingleResQtiesOrCostByTime[i].Probability
};
} else {
graphData[i] = {
label: data.SingleResQtiesOrCostByTime[i].ScenarioName,
data: expCatData,
lines: { show: true, fill: true, steps: false },
filledPoints: true,
stack: true,
probability: data.SingleResQtiesOrCostByTime[i].Probability
};
}
}
draw(hash, graphData, maxValue);
drawTable(hash, graphData);
unblockUI();
},
error: function (msg) {
unblockUI();
alert(msg.responseText);
}
});
}
function drawTable(hash, graphData) {
var table = (hash == '#general' ? '#SingleResQtiesOrCostByTimeTable' : '#SingleResQtiesOrCostByTimeTableProbability');
$(table + " > thead").html("");
$(table + " > tbody").html("");
if (graphData.length > 0) {
var header_html = "<tr><th style='padding-left:200px'></th><th style='padding: 0 0 0 100px'></th>";
for (var i = 0; i < graphData[0].data.length; i++) {
var formattedDate = new Date(graphData[0].data[i][0]);
var d = formattedDate.getDate();
var m = formattedDate.getMonth();
m += 1; // JavaScript months are 0-11
var y = formattedDate.getFullYear();
header_html += "<th>" + (m.toString().length == 1 ? "0" + m.toString() : m.toString()) + "/" + (d.toString().length == 1 ? "0" + d.toString() : d.toString()) + "/" + y + "</th>";
}
header_html += "</tr>";
$(table + " > thead:last").append(header_html);
var scenario_total = [0];
//Build scenario rows
for (var i = 0; i < graphData.length - 4; i++) {
var total_row = 0;
var projectName = graphData[i].data.length > 0 && graphData[i].data[0][2] != null ? graphData[i].data[0][2] : "";
var row_html = "<tr>";
row_html += "<td>" + (projectName.length > 0 ? projectName : "") + " (" + graphData[i].label + ") " + (hash == '#general' ? '' : ' - ' + graphData[i].probability + '%') + "</td>";
row_html += "<td id='total" + i + "'></td>";
for (var j = 0; j < graphData[i].data.length; j++) {
//Write grand total in first item
var val = graphData[i].data[j][1];
if (i == 0) {
scenario_total.push(val);
} else {
scenario_total[j + 1] += val;
}
scenario_total[0] += val;
total_row += val;
row_html += "<td>" + displayCost(val) + "</td>";
}
row_html += "</tr>";
$(table + " > tbody:last").append(row_html);
$(table).find("#total" + i).text(displayCost(total_row));
}
//if (graphData.length - 5 == i) {
var row_html = "<tr style=\"font-weight:bold;\">";
row_html += "<td>Scenario Totals</td>";
row_html += "<td>" + displayCost(scenario_total[0]) + "</td>";
for (var j = 0; j < graphData[i].data.length; j++) {
row_html += "<td>" + displayCost(scenario_total[j + 1]) + "</td>";
}
row_html += "</tr>";
$(table + " > tbody:last").append(row_html);
row_html = "<tr>";
row_html += "<td></td>";
row_html += "<td></td>";
for (var j = 0; j < graphData[i].data.length; j++) {
row_html += "<td></td>";
}
row_html += "</tr>";
$(table + " > tbody:last").append(row_html);
var total = [0];
var facility_total = [0];
var onLeave_total = [0];
//last 4 are:Actual Capacity, Planned Capacity, Vacations, Trainings
//!!!!!
//!!!!! we do not track actual capacity - it makes sense to compare with planned capacity so we omit row graphData.length - 4
//!!!!!
//TODO: change the algorighm to not use magic numbers and hardocded placement
for (var i = graphData.length - 3; i < graphData.length; i++) {
var total_row = 0;
var projectName = graphData[i].data.length > 0 && graphData[i].data[0][2] != null ? graphData[i].data[0][2] : "";
var row_html = "<tr><td>" + graphData[i].label + "</td>" +
"<td id='total" + i + "'></td>";
for (var j = 0; j < graphData[i].data.length; j++) {
var val = graphData[i].data[j][1];
if (i == (graphData.length - 3)) {
total.push(0);
facility_total.push(0);
onLeave_total.push(0);
}
if (graphData[i].label == "Planned Capacity") {
facility_total[j + 1] += parseFloat(val);
onLeave_total[j + 1] += 0;
total[0] += parseFloat(val);
total[j + 1] += parseFloat(val);
} else {
facility_total[j + 1] += 0;
onLeave_total[j + 1] += parseFloat(val);
total[0] -= parseFloat(val);
total[j + 1] -= parseFloat(val);
}
total_row += parseFloat(val);
row_html += "<td>" + displayCost(val) + "</td>";
}
row_html += "</tr>";
$(table + " > tbody:last").append(row_html);
$(table).find("#total" + i).text(displayCost(total_row));
}
var row_html = "<tr style=\"font-weight:bold;\">" +
"<td>Availability Totals</td>";
for (var j = 0; j < total.length; j++) {
row_html += "<td>" + displayCost(total[j]) + "</td>";
}
row_html += "</tr>";
$(table + " > tbody:last").append(row_html);
row_html = "<tr>" +
"<td></td>";
for (var j = 0; j < total.length; j++) {
row_html += "<td></td>";
}
row_html += "</tr>";
$(table + " > tbody:last").append(row_html);
row_html = "<tr style=\"font-weight:bold;\">";
row_html += "<td>Crew To Hire(OH)</td>";
row_html += "<td id='OH'></td>";
total_row = 0;
for (var j = 1; j < scenario_total.length; j++) {
val = (onLeave_total[j] + scenario_total[j] - facility_total[j]);
row_html += "<td>" + displayCost(val) + "</td>";
total_row += val;
}
row_html += "</tr>";
$(table + " > tbody:last").append(row_html);
$(table).find("#OH").text(displayCost(total_row));
}
}
function displayCost(val) {
if (val != undefined) {
if ($("#@Html.IdFor(x => x.SeatsCosts)").val() == "1" || $("#@Html.IdFor(x => x.SeatsCosts)").val() == "Cost") {
return (Math.round(val * 100) / 100).toString();
} else {
return (Math.round(val * 100) / 100).toString();
}
} else {
return 0;
}
}
function draw(hash, graphData, maxValue) {
// Init Chart
var checkedData = new Array();
var localPalette = _bigColorPalette.slice(0);
for (var i = 0; i < graphData.length; i++) {
//var chk = document.getElementById('expCatCheckedId_' + (i + 1));
//if (chk.checked)
graphData[i].color = localPalette.shift();
checkedData[checkedData.length] = graphData[i];
}
$('#div' + (hash == '#general' ? 'WhatIfGraph' : 'WhatIfGraphProbability') + "Container").
html("<div id='div" + (hash == '#general' ? 'WhatIfGraph' : 'WhatIfGraphProbability') + "'></div>");
var div = $('#div' + (hash == '#general' ? 'WhatIfGraph' : 'WhatIfGraphProbability'));
div.pixelPlot(checkedData, {
series: {
points: {
show: false
},
lines: {
show: true,
fill: true,
steps: false
}
},
xaxis: {
mode: "time",
tickSize: [1, "month"],
tickLength: 0
}
}, {
height: 405,
tooltipText: ($("#@Html.IdFor(x => x.SeatsCosts)").val() == "0" || $("#@Html.IdFor(x => x.SeatsCosts)").val() == "Seats" ?
"y + ' at '" : "'$'+ y + ' at '") + " + (new Date(x)).toDateString()"
});
}
function updateHidden(name, val) {
$("input[name='" + name + "']").val(val);
}
function saveSelectedScenarios() {
var selectedScenarioList = $("#@Html.IdFor(x => x.SelectedScenarios)");
var res = "";
$.each($("input[type='checkbox']"), function (i, o) {
if (o.checked && $(o).attr('scenario') != null) {
res += ",\"" + $(o).attr('scenario') + "\"";
$(o).parent().parent().removeClass('unchecked');
} else if ($(o).attr('scenario') != null) {
$(o).parent().parent().addClass('unchecked');
}
});
if (res.length > 0)
selectedScenarioList.val("[" + res.substr(1, res.length - 1) + "]");
else
selectedScenarioList.val("");
saveReportPagePreferences();
return true;
}
function saveSelectedExpsCats() {
var SelectedExpenditureItems = $("#@Html.IdFor(x => x.SelectedExpenditureItems)");
var res = "";
$.each($("input[type='checkbox']"), function (i, o) {
if (o.checked && o.id.substr(0, 16) == 'expCatCheckedId_') {
res += ",\"" + $(o).val() + "\"";
$(o).parent().parent().removeClass('unchecked');
} else if (o.id.substr(0, 16) == 'expCatCheckedId_') {
$(o).parent().parent().addClass('unchecked');
}
});
if (res.length > 0)
SelectedExpenditureItems.val("[" + res.substr(1, res.length - 1) + "]");
else
SelectedExpenditureItems.val("");
saveReportPagePreferences();
return true;
}
function refreshGraph() {
saveSelectedScenarios();
saveSelectedExpsCats();
var selectedScenarios = $("#@Html.IdFor(x => x.SelectedScenarios)").val();
var selectedCategories = $("#@Html.IdFor(x => x.SelectedExpenditureItems)").val();
if (selectedScenarios.length == 0 || selectedScenarios == "[]")
alert("Please select at least one Project/Scenario.");
else if (selectedCategories.length == 0 || selectedCategories == "[]")
alert("Please select at least one Expenditure Category.");
else
InitGraph($("ul#tabs li.active a").attr('href'));
}
</script>
}
@section pagemenu
{
<ul class="nav navbar-nav navbar-right" data-section="reportPage">
<li class="dropdown">
<a href="#" class="dropdown-toggle user-menu" data-toggle="dropdown">
<i class="fa fa-bars"></i><span>Page Options</span>&nbsp;<i class="fa fa-caret-down"></i>
</a>
<ul class="dropdown-menu dropdown-menu-right" id="visibilitydropdown">
<li><a onclick="_printDoc();$(this).parent().parent().parent().removeClass('open');"><i class="dropdown-icon fa fa-print"></i> Print Page</a></li>
<li class="divider"></li>
<li class="padding-xs-hr"><label><span class="switcherLbl">Quantity as</span><input type="checkbox" @isUOMHoursChecked data-key="uomMode" name="uomMode" id="uomMode" class="switcher px" onclick="saveReportPagePreferences(); refreshGraph();"/></label></li>
</ul>
</li>
</ul>
}
<div id="srqBody" data-section="reportPage">
<div id="filter">
@Html.Partial("_filter", Model)
</div>
<div class="panel">
<div class="panel-body">
<div class="row">
<div class="col-sm-2">
@Html.LabelFor(model => model.SeatsCosts, new {@class = "control-label"})
<div class="form-group no-margin-hr">
@Html.DropDownListFor(model => model.SeatsCosts, Utils.GetListboxItems<SeatsCostsType>().OrderBy(x=>x.Text),
new {@class = "form-control disabled", onchange = "updateHidden(this.name, $(this).val());", data_key = "filterSeatsCosts" })
</div>
</div>
<div class="col-sm-2">
@Html.LabelFor(model => model.StartDate, new {@class = "control-label"})
@Html.EditorFor(model => model.StartDate, new { data_key = "filterStartDate" })
@Html.ValidationMessageFor(model => model.StartDate)
</div>
<div class="col-sm-2">
@Html.LabelFor(model => model.EndDate, new {@class = "control-label"})
@Html.EditorFor(model => model.EndDate, new { data_key = "filterEndDate" })
@Html.ValidationMessageFor(model => model.EndDate)
</div>
<div class="col-sm-2">
@Html.LabelFor(model => model.SystemAttribute1, new {@class = "control-label"})
<div class="form-group no-margin-hr">
@Html.DropDownListFor(model => model.SystemAttribute1, Utils.GetSystemAttributeOne(),
new {@class = "form-control disabled", onchange = "updateHidden(this.name, $(this).val());", data_key = "filterSysAttribute1" })
</div>
</div>
<div class="col-sm-2">
@Html.LabelFor(model => model.SystemAttribute2, new {@class = "control-label"})
<div class="form-group no-margin-hr">
@Html.DropDownListFor(model => model.SystemAttribute2, Utils.GetSystemAttributeTwo(),
new {@class = "form-control disabled", onchange = "updateHidden(this.name, $(this).val());", data_key = "filterSysAttribute2" })
</div>
</div>
</div>
</div>
<div class="panel-footer text-right">
<button type="button" class="btn btn-primary" onclick=" saveReportPagePreferences(); refreshGraph()">Refresh</button>
</div>
</div>
<ul id="tabs" class="nav nav-tabs">
<li class="active">
<a href="#general" data-toggle="tab">Crew Graph by Scenario (100%)<span class="badge badge-primary"></span></a>
</li>
<li>
<a href="#probability" data-toggle="tab">Crew Graph by Scenario (Use Probability)<span class="badge badge-primary"></span></a>
</li>
</ul>
<div class="tab-content tab-content-bordered">
<div class="tab-pane fade in active" id="general">
<div class="panel">
<div class="panel-body">
<div class="graph-container" style="margin-bottom: 20px;" id="divWhatIfGraphContainer">
<div id="divWhatIfGraph"></div>
</div>
<div class="table-light table-responsive" style="width: 100%; overflow-x: auto;">
<table cellpadding="0" cellspacing="0" border="0" class="table table-striped table-bordered" id="SingleResQtiesOrCostByTimeTable">
<thead>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
</div>
</div>
<div class="tab-pane fade" id="probability">
<div class="panel">
<div class="panel-body">
<div class="graph-container" style="margin-bottom: 20px;" id="divWhatIfGraphProbabilityContainer">
<div id="divWhatIfGraphProbability"></div>
</div>
<div class="table-light table-responsive" style="width: 100%; overflow-x: auto;">
<table cellpadding="0" cellspacing="0" border="0" class="table table-striped table-bordered" id="SingleResQtiesOrCostByTimeTableProbability">
<thead>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>