Introduction
This article focuses on the visibility of internal supply chain management process. It is a sequel to internal supply chain visibility. In this article, the objective is materialized using MSChart and hence provides assessment apparatus. The solution comprises Bar, Line, Area, Pie, Pareto, and Kagi charts. These are further compartmentalized with respect to their respective attributes.
Reports are categorized into segregated, cumulative, and change in rates reports. Segregated reports provide a context of a particular item or goods with this context, another sub context resides to analyze that item or material. Cumulative reports provide a context of all items, or it slides item context to the side.
Segregated and cumulative reports are further classified to segregated cost, segregated quantity, cumulative cost, and cumulative quantity.
Outer working
Add a System.Web.UI.DataVisualization.Charting
reference into the project, and open up your Web.config and add the following lines:
In system.web/httpHandlers
, add this:
<httpHandlers>
<add path="ChartImg.axd" verb="GET,HEAD"
type="System.Web.UI.DataVisualization.Charting.ChartHttpHandler,
System.Web.DataVisualization, Version=3.5.0.0, Culture=neutral,
PublicKeyToken=31bf3856ad364e35"
validate="false"/>
</httpHandlers>
I need to generate more than 200 reports in the same page, so I cannot manage to make each report on each page. The technique is weird but very effective. It effectively creates a division between the presentation and business layers. The code for working of controls is shown below. This code only manages the ISCMS.Main.Chart
object, and provides it with a parameter, or initializes the attributes of this object that defines the chart type, period, and style. All the attributes that are needed to generate reports are set over here.
<script runat="server">
ISCMS.Main.Chart chy = new ISCMS.Main.Chart();
bool IsAllowed = false;
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
SetRadio();
}
}
void SetRadio()
{
if (radProcurement.Checked)
chy._plantSegment = ISCMS.Main.PlantSegregation.PlantSegment.Procurement;
if (radWarehouse.Checked)
chy._plantSegment = ISCMS.Main.PlantSegregation.PlantSegment.Warehouse;
if (radSegregatedCost.Checked)
{
chy._chartBy = ISCMS.Main.PlantSegregation.ChartBy.Segregated_Cost;
chy._is_Segregation = true;
combGoods.Enabled = true;
}
if (radSegregatedQuantity.Checked)
{
chy._chartBy = ISCMS.Main.PlantSegregation.ChartBy.Segregated_Quantity;
chy._is_Segregation = true;
combGoods.Enabled = true;
}
if (radCumulativeCost.Checked)
{
chy._chartBy = ISCMS.Main.PlantSegregation.ChartBy.Cumulative_Cost;
chy._is_Segregation = false;
combGoods.Enabled = false;
}
if (radCumulativeQuantity.Checked)
{
chy._chartBy = ISCMS.Main.PlantSegregation.ChartBy.Cumulative_Quantity;
chy._is_Segregation = false;
combGoods.Enabled = false;
}
if (radChangeRate.Checked)
{
chy._chartBy = ISCMS.Main.PlantSegregation.ChartBy.Rate;
chy._is_Segregation = false;
combGoods.Enabled = true;
}
string chartByFilter = "";
string goodsFilter = "";
chy.InitializeFilter(out chartByFilter, out goodsFilter);
if (!IsPostBack || IsAllowed)
{
combReports.DataSource =
((nodeDataset)Application["nodeDS"]).Nodes.Select(chartByFilter);
combReports.DataTextField = "SelectedNode";
combReports.DataValueField = "Execute";
combReports.DataBind();
System.Data.DataRow[] rows = ((System.Data.DataSet)
Application["Parameters"]).Tables["Goods"].Select(goodsFilter);
ListItem li = new ListItem();
for (int i = 0; i < rows.Length; i++)
{
li = new ListItem();
li.Text = rows[i]["Name"].ToString();
li.Value = rows[i]["Goods_ID"].ToString();
combGoods.Items.Add(li);
}
}
UpdateChart();
}
void UpdateChart()
{
chy._goods_ID = Convert.ToInt32(combGoods.SelectedValue);
chy._goods_Name = combGoods.SelectedItem.Text;
chy._chartName = combReports.SelectedValue.ToString();
chy._charts = ISCMS.Main.PlantSegregation.Charts.Bar;
combMonth.Enabled = false;
if (combPeriod.Text == "Monthly")
combMonth.Enabled = true;
chy.UpdateChartPeriod(combPeriod.Text, combMonth.Text,
combMonth.SelectedValue, combYear.Text);
}
protected void radProcurement_CheckedChanged(object sender, EventArgs e)
{
IsAllowed = true;
SetRadio();
}
protected void combReports_SelectedIndexChanged(object sender, EventArgs e)
{
IsAllowed = false;
SetRadio();
}
</script>
After initialization of the ISCMS.Main.Chart
object, the chart is all set to be rendered, as ISCMS.Main.Chart
is itself a helper class to generate chart objects as well as a repository for its attributes. By calling the GetChart()
method, the System.Web.UI.DataVisualization.Charting.Chart
object will be returned, which is ready to be rendered, because all the chart generation steps are performed in the GetChart()
method.
<table style="width: 980px" >
<tr>
<td >
<%
System.Web.UI.DataVisualization.Charting.Chart Chart1 =
new System.Web.UI.DataVisualization.Charting.Chart();
Chart1 = chy.GetChart();
Chart1.Page = this;
HtmlTextWriter writer = new HtmlTextWriter(Page.Response.Output);
Chart1.RenderControl(writer);
%>
</td>
</tr>
</table>
Inner Working
Chart Class
The Chart
control provides a canvas for a chart report. It acts as a container for chartArea
. Hence, it can be seen as a framework upon which a chart will be masked. The SetChart()
method is used in the solution to set the attributes of the chart. It assigns the title and sets the legends attributes for the report.
System.Web.UI.DataVisualization.Charting.Chart SetChart()
{
System.Web.UI.DataVisualization.Charting.Chart chartTemp =
new System.Web.UI.DataVisualization.Charting.Chart();
chartTemp.BackColor = System.Drawing.Color.Gainsboro;
chartTemp.BackGradientStyle =
System.Web.UI.DataVisualization.Charting.GradientStyle.TopBottom;
chartTemp.BackSecondaryColor = System.Drawing.Color.Silver;
chartTemp.BorderlineColor = System.Drawing.Color.Silver;
chartTemp.BorderlineDashStyle =
System.Web.UI.DataVisualization.Charting.ChartDashStyle.Solid;
chartTemp.BorderlineWidth = 3;
chartTemp.BorderSkin.SkinStyle =
System.Web.UI.DataVisualization.Charting.BorderSkinStyle.None;
if (_charts == ISCMS.Main.PlantSegregation.Charts.Pie)
{
chartTemp.ChartAreas.Add(SetPieChartArea());
}
else
{
chartTemp.ChartAreas.Add(SetChartArea());
}
chartTemp.Legends.Add(SetLegend());
chartTemp.TabIndex = 0;
return chartTemp;
}
ChartArea Class
ChartArea
is a content of the chart class. It is added to the chart class as collections. It provides intrinsic visualization of a chart report other than data visualization. Data visualization is done by Series
which will be discussed later. The SetChartArea()
method sets the attributes of the chart area which define the style of the report to be generated what, and the x-axis and y-axis.
ChartArea SetChartArea()
{
System.Web.UI.DataVisualization.Charting.ChartArea chartArea1 =
new System.Web.UI.DataVisualization.Charting.ChartArea();
chartArea1.Area3DStyle.Inclination = 15;
chartArea1.Area3DStyle.IsClustered = true;
chartArea1.Area3DStyle.IsRightAngleAxes = false;
chartArea1.Area3DStyle.LightStyle =
System.Web.UI.DataVisualization.Charting.LightStyle.Realistic;
chartArea1.Area3DStyle.Perspective = 7;
chartArea1.Area3DStyle.Rotation = 20;
chartArea1.Area3DStyle.WallWidth = 10;
chartArea1.AxisX.LabelStyle.Font = new System.Drawing.Font(
"Trebuchet MS", 8.25F, System.Drawing.FontStyle.Bold);
chartArea1.AxisX.LineColor = System.Drawing.Color.Teal;
chartArea1.AxisX.MajorGrid.LineColor = System.Drawing.Color.Teal;
chartArea1.AxisY.LabelStyle.Font = new System.Drawing.Font(
"Trebuchet MS", 8.25F, System.Drawing.FontStyle.Bold);
chartArea1.AxisY.LineColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))),
((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
chartArea1.AxisY.MajorGrid.LineColor = System.Drawing.Color.Teal;
chartArea1.BackColor = System.Drawing.Color.Gainsboro;
chartArea1.BackGradientStyle =
System.Web.UI.DataVisualization.Charting.GradientStyle.TopBottom;
chartArea1.BackSecondaryColor = System.Drawing.Color.Silver;
chartArea1.BorderColor = System.Drawing.Color.Teal;
chartArea1.BorderDashStyle =
System.Web.UI.DataVisualization.Charting.ChartDashStyle.Solid;
chartArea1.Name = "Default";
chartArea1.ShadowColor = System.Drawing.Color.Transparent;
return chartArea1;
}
Series Class
The Series
class resides in the System.Web.UI.DataVisualization.Charting
namespace. The Series
class objects are contents of the ChartArea
class. They are added to the ChartArea
object as a collection. Hence we can have multiple types of charts within the same chartArea
for comparative reports. The Series
class object is a collection of DataPoint
objects. DataPoint
can be collected by the series.Points.AddXY(ts.Days, amount)
method. The first parameter of the addXY( , )
method defines the x-coordinate of a point, and the y-coordinate is collected from the second parameter. When the series type is set as SeriesChartType.Line
, the DataPoint
will act as a point which generates a line by joining all the DataPoint
s in the collections. If it is set as SeriesChartType.Column
, then the y-coordinate will be the height of the vertical bar, and its position on the base is decided by the x-coordinate. The same is the case with the Kage chart as well.
Pareto Chart
Pareto is a combinational chart. It’s a combination of bar and line charts. The bar chart shows the product or the material along the x –axis and the amount or quantity along the y-axis. In the same chart, a line chart shows the cumulative sum of the preceding material. InitializeParetoChart()
and ParetoChartTypeLoad()
are the fundamental methods for the generation of this chart.
private void InitializeParetoChart()
{
this.chart = SetChart();
this.chart.ChartAreas[0].AxisX.Interval = 1;
this.chart.ChartAreas[0].AxisX.IntervalType =
System.Web.UI.DataVisualization.Charting.DateTimeIntervalType.Days;
this.chart.ChartAreas[0].AxisX.IntervalOffset = 1;
this.chart.ChartAreas[0].AxisX.IntervalOffsetType =
System.Web.UI.DataVisualization.Charting.DateTimeIntervalType.Days;
this.chart.ChartAreas[0].AxisX.LabelAutoFitStyle |=
System.Web.UI.DataVisualization.Charting.LabelAutoFitStyles.LabelsAngleStep90;
this.chart.ChartAreas[0].AxisY2.IsLabelAutoFit = false;
ParetoChartTypeLoad();
}
void MakeParetoChart(System.Web.UI.DataVisualization.Charting.Chart chart,
string srcSeriesName, string destSeriesName)
{
string strChartArea = chart.Series[srcSeriesName].ChartArea;
chart.Series[srcSeriesName].ChartType =
System.Web.UI.DataVisualization.Charting.SeriesChartType.Column;
chart.DataManipulator.Sort(
System.Web.UI.DataVisualization.Charting.PointSortOrder.Descending,
srcSeriesName);
double total = 0.0;
foreach (System.Web.UI.DataVisualization.Charting.DataPoint pt
in chart.Series[srcSeriesName].Points)
total += pt.YValues[0];
chart.ChartAreas[strChartArea].AxisY.Maximum = total;
System.Web.UI.DataVisualization.Charting.Series destSeries =
new System.Web.UI.DataVisualization.Charting.Series(destSeriesName);
chart.Series.Add(destSeries);
destSeries.ChartType =
System.Web.UI.DataVisualization.Charting.SeriesChartType.Line;
destSeries.BorderWidth = 3;
destSeries.ChartArea = chart.Series[srcSeriesName].ChartArea;
destSeries.YAxisType =
System.Web.UI.DataVisualization.Charting.AxisType.Secondary;
chart.ChartAreas[strChartArea].AxisY2.Maximum = 100;
chart.ChartAreas[strChartArea].AxisY2.LabelStyle.Format = "P00";
chart.ChartAreas[strChartArea].AxisX.LabelStyle.IsEndLabelVisible = false;
double percentage = 0.0;
foreach (System.Web.UI.DataVisualization.Charting.DataPoint pt
in chart.Series[srcSeriesName].Points)
{
percentage += (pt.YValues[0] / total * 100.0);
destSeries.Points.Add((Math.Round(percentage, 2)));
}
}
private void ParetoChartTypeLoad()
{
ISCMS.Main.PlantSegregation pseg =
new ISCMS.Main.PlantSegregation
(_plantSegment, _charts, _chartBy, _is_Segregation, this);
rows = pseg.Process();
this.chart.Titles.Add(SetTitle());
System.Web.UI.DataVisualization.Charting.Series series =
new System.Web.UI.DataVisualization.Charting.Series();
series.BorderColor = System.Drawing.Color.FromArgb(((int)(((byte)(180)))),
((int)(((byte)(26)))), ((int)(((byte)(59)))), ((int)(((byte)(105)))));
series.ChartArea = "Default";
series.Legend = "Default";
series.Name = "Default";
this.chart.Series.Add(series);
chart.Series["Default"].Points.Clear();
int amount = 0;
for (int i = 0; i < rows.Length; i++) {
amount = pseg.ProcessItem(i);
if (amount == 0)
continue;
chart.Series["Default"].Points.AddXY(
rows[i]["Name"].ToString(), amount);
}
if (chart.Series.Count > 1)
chart.Series.RemoveAt(1);
MakeParetoChart(chart, "Default", "Pareto");
chart.Series["Pareto"].ChartType =
System.Web.UI.DataVisualization.Charting.SeriesChartType.Line;
chart.Series["Pareto"].Color = Color.FromArgb(252, 180, 65);
chart.Series["Pareto"].IsValueShownAsLabel = false;
chart.Series["Pareto"].MarkerColor = Color.Red;
chart.Series["Pareto"].MarkerStyle =
System.Web.UI.DataVisualization.Charting.MarkerStyle.None;
chart.Series["Pareto"].MarkerBorderColor = Color.MidnightBlue;
chart.Series["Pareto"].MarkerSize = 8;
chart.Series["Pareto"].LabelFormat = "0.#";
}
Line Chart
This chart is used to represent a perpetual inventory with respect to cost and quantity. The main methods to generate this chart are InitializeLineCurvesChart()
and LineCurvesChartTypeLoad()
.
#region Line Chart
private void InitializeLineCurvesChart()
{
chart = SetChart();
chart = LineCurvesChartTypeLoad(chart);
}
private System.Web.UI.DataVisualization.Charting.Chart LineCurvesChartTypeLoad(
System.Web.UI.DataVisualization.Charting.Chart chart)
{
ISCMS.Main.PlantSegregation pseg =
new ISCMS.Main.PlantSegregation
(_plantSegment, _charts, _chartBy, _is_Segregation, this);
rows = pseg.Process();
chart.Titles.Add(SetTitle());
System.Web.UI.DataVisualization.Charting.Series series =
new System.Web.UI.DataVisualization.Charting.Series();
series.BorderColor = System.Drawing.Color.Transparent;
series.BorderWidth = 3;
series.ChartArea = "Default";
series.ChartType = System.Web.UI.DataVisualization.Charting.SeriesChartType.Area;
series.Color = _serial_Color;
series.Legend = "Default";
series.MarkerSize = 4;
series.MarkerStyle = System.Web.UI.DataVisualization.Charting.MarkerStyle.Circle;
series.Name = "series";
series.ShadowColor = System.Drawing.Color.Gainsboro;
series.ShadowOffset = 2;
series.XValueType = System.Web.UI.DataVisualization.Charting.ChartValueType.Date;
series.YValueType = System.Web.UI.DataVisualization.Charting.ChartValueType.Double;
series.BorderColor = System.Drawing.Color.Transparent;
TimeSpan ts = DateTime.Now - new DateTime(1900, 1, 1);
double days = ts.Days;
double days2 = days;
int amount = 0;
int perviousAmount = 0;
for (int pointIndex = 0; pointIndex < rows.Length; pointIndex++)
{
amount = pseg.ProcessItem(pointIndex);
ts = DateTime.Parse(rows[pointIndex]["Date"].ToString()) - new DateTime(1900, 1, 1);
if (perviousAmount == 0 && pointIndex == 0)
{
series.Points.AddXY(ts.Days, amount);
perviousAmount = amount;
continue;
}
if (pointIndex == (rows.Length - 1))
{
series.Points.AddXY(ts.Days, amount);
continue;
}
if (perviousAmount != amount)
{
series.Points.AddXY(ts.Days, amount);
perviousAmount = amount;
continue;
}
}
series.ChartType = System.Web.UI.DataVisualization.Charting.SeriesChartType.Line;
series.IsValueShownAsLabel = true;
chart.ChartAreas["Default"].Area3DStyle.Enable3D = true;
chart.Series.Add(series);
return chart;
}
#endregion
Pie Chart
It is a cumulative chart which can be generated by calling the InitializePieCollection()
method. Its code is as given below:
private void InitializePieCollection()
{
this.chart = SetChart();
this.chart.Legends[0].Enabled = true;
this.chart.Legends[0].IsEquallySpacedItems = true;
PieChartTypeLoad();
}
private void PieChartTypeLoad()
{
ISCMS.Main.PlantSegregation pseg =
new ISCMS.Main.PlantSegregation
(_plantSegment, _charts, _chartBy, _is_Segregation, this);
rows = pseg.Process();
this.chart.Titles.Add(SetTitle());
System.Web.UI.DataVisualization.Charting.Series series =
new System.Web.UI.DataVisualization.Charting.Series();
series.BorderColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))),
((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
series.ChartArea = "Area1";
series.ChartType =
System.Web.UI.DataVisualization.Charting.SeriesChartType.Pie;
series.Color = System.Drawing.Color.FromArgb(((int)(((byte)(180)))),
((int)(((byte)(65)))), ((int)(((byte)(140)))), ((int)(((byte)(240)))));
series.CustomProperties = "DoughnutRadius=15, PieDrawingStyle=Concave,
CollectedLabel=Other, MinimumRelative" +
"PieSize=10";
series.Font = new System.Drawing.Font("Trebuchet MS", 8.25F,
System.Drawing.FontStyle.Bold);
series.Label = "#PERCENT{P1}";
series.Legend = "Default";
series.MarkerStyle =
System.Web.UI.DataVisualization.Charting.MarkerStyle.Circle;
series.Name = "Default";
series.XValueType =
System.Web.UI.DataVisualization.Charting.ChartValueType.Double;
series.YValueType =
System.Web.UI.DataVisualization.Charting.ChartValueType.Double;
System.Web.UI.DataVisualization.Charting.DataPoint dataPoint1 =
new System.Web.UI.DataVisualization.Charting.DataPoint(0, 0);
int amount = 0;
for (int i = 0; i < rows.Length; i++)
{
amount = pseg.ProcessItem(i);
dataPoint1 = new System.Web.UI.DataVisualization.Charting.DataPoint(0,
amount);
dataPoint1.CustomProperties = "OriginalPointIndex=" + i.ToString();
dataPoint1.IsValueShownAsLabel = false;
dataPoint1.LegendText = rows[i]["Name"].ToString();
dataPoint1.Label = rows[i]["Name"].ToString();
series.Points.Add(dataPoint1);
}
this.chart.Series.Add(series);
chart.Series[0].Font = new Font("Trebuchet MS", 8, FontStyle.Bold);
chart.Series[0]["CollectedToolTip"] = "Other";
chart.Series["Default"]["PieLabelStyle"] = "Outside";
chart.Series["Default"]["CollectedThreshold"] = "8";
if (chart.Series.Count > 1)
{
chart.Series.RemoveAt(1);
chart.ChartAreas.RemoveAt(1);
chart.ChartAreas["Default"].Position.Auto = true;
}
}
Bar Chart
The code for the bar chart is shown here:
private void InitializeBarColumn()
{
this.chart = SetChart();
BarColumnChartTypeLoad();
}
private void BarColumnChartTypeLoad()
{
ISCMS.Main.PlantSegregation pseg =
new ISCMS.Main.PlantSegregation
(_plantSegment, _charts, _chartBy, _is_Segregation, this);
rows = pseg.Process();
this.chart.Titles.Add(SetTitle());
System.Web.UI.DataVisualization.Charting.Series series =
new System.Web.UI.DataVisualization.Charting.Series();
series.ChartArea = "Default";
series.Legend = "Default";
series.Name = "series";
series.XValueType =
System.Web.UI.DataVisualization.Charting.ChartValueType.DateTime;
series.ChartType =
System.Web.UI.DataVisualization.Charting.SeriesChartType.Column;
this.chart.Series.Clear();
this.chart.Series.Add(series);
TimeSpan ts = DateTime.Now - new DateTime(1900, 1, 1);
double days = ts.Days;
double days2 = days;
int amount = 0;
for (int pointIndex = 0; pointIndex < rows.Length; pointIndex++)
{
amount = pseg.ProcessItem(pointIndex);
ts = DateTime.Parse(rows[pointIndex]["Date"].ToString()) -
new DateTime(1900, 1, 1);
series.Points.AddXY(ts.Days, amount);
}
series.Color = _serial_Color;
series.BorderColor = System.Drawing.Color.Transparent;
chart.ChartAreas["Default"].Area3DStyle.Enable3D = true;
chart.ChartAreas[0].AxisX.LabelStyle.IntervalOffset = 1;
chart.ChartAreas[0].AxisX.LabelStyle.IntervalOffsetType =
System.Web.UI.DataVisualization.Charting.DateTimeIntervalType.Days;
chart.ChartAreas[0].AxisX.LabelStyle.Interval = 2;
chart.ChartAreas[0].AxisX.LabelStyle.IntervalType =
System.Web.UI.DataVisualization.Charting.DateTimeIntervalType.Days;
}
Kagi Chart
Kagi chart is used to monitor material or finished goods rate over a period of time. This chart can be established by the following code:
private void InitializeKagiChart()
{
this.chart = SetChart();
KagiChartTypeLoad();
}
private void KagiChartTypeLoad()
{
ISCMS.Main.PlantSegregation pseg =
new ISCMS.Main.PlantSegregation
(_plantSegment, _charts, _chartBy, _is_Segregation, this);
rows = pseg.Process();
DataSet ds = new DataSet();
ds.Merge(rows);
System.Web.UI.DataVisualization.Charting.Series series =
new System.Web.UI.DataVisualization.Charting.Series();
series.BorderColor = System.Drawing.Color.Teal;
series.BorderWidth = 3;
series.ChartArea = "Default";
series.ChartType =
System.Web.UI.DataVisualization.Charting.SeriesChartType.Kagi;
series.Color = System.Drawing.Color.Tomato;
series.CustomProperties = "PriceUpColor=SkyBlue";
series.IsXValueIndexed = true;
series.Legend = "Default";
series.Name = "Default";
series.ShadowColor = System.Drawing.Color.Gainsboro;
series.ShadowOffset = 2;
series.XValueType =
System.Web.UI.DataVisualization.Charting.ChartValueType.Date;
series.YValueType =
System.Web.UI.DataVisualization.Charting.ChartValueType.Double;
this.chart.Titles.Add(SetTitle());
series.Points.Clear();
double bufRate = 0;
TimeSpan ts;
for (int pointIndex = 0; pointIndex < rows.Length; pointIndex++)
{
ts = DateTime.Parse(rows[pointIndex]["Date"].ToString()) -
new DateTime(1900, 1, 1);
if (ds.Tables[0].Select(" Date = #" +
DateTime.Parse(
rows[pointIndex]["Date"].ToString()).ToShortDateString() +
"#").Length > 0) {
series.Points.AddXY(ts.Days, Convert.ToDouble(rows[pointIndex]["Unit_Cost"]));
bufRate = Convert.ToDouble(rows[pointIndex]["Unit_Cost"]);
}
else
series.Points.AddXY(ts.Days, bufRate);
}
chart.ChartAreas["Default"].Area3DStyle.Enable3D = true;
this.chart.Series.Add(series);
}