Introduction
Internal supply Chain, visibility via 200 plus 3D Chart Reports Part II
This article focuses on the visibility of internal supply chain management process. Its primary objective is to visualize activity and event spawn during work flow and offers panoramic view of upstream and downstream activities. It constitutes inbound, outbound and production operations, and can be segmented into Raw material, work in process and finished goods with their intrinsic operations.
Visibility
In supply chain management information’s visibility is pivotal. System provided context based perpetual inventory by date and material, and history of perpetual inventory as well. Canvas legend provides spectrum to distinguish and visualizes perpetual inventory of refined context (filtered). To elaborate view of perpetual inventory material can be extracted by hovering mouse over the concerned bar.
Inbound Operation Events
Raw material inbound events are inspected by checking inbound Event check box. Hence material events are extracted associated with refined context (filtered). Detail view is obtained by hovering mouse over concerned event bar.
Material Inbound and Production Operation Transition
Panoramic view material inbound and operation transition has two preliminary paths or stages. First one starts from in transit, it corresponds to the situation where material purchase order has been dispatched and is waiting for the material to be delivered. When in transit material is received, it is recorded in material inventory. When in stock inventory is contaminated in any way, then inbound material dispose or waste operation is performed which transforms the material to waste state. Material operation transition convolutes with production operation. There are two operation paths, both of them are initiated by invoking a job. When job status is set to Pending material stock is reserved for the job and is dispatched for production when job status changes to current. It can be set to current Job when job is going to be in manufacturing phase directly. At completion of manufacturing process of the job, status will be set to Completed and produced finished goods will proceed towards QA department and further into finished goods inventory.
Work in Progress Production Operations
Work in progress is a middle layer between raw material and finished goods operational layer thus it acted as buffer between them binding rather then joining them together by Job specification. Job specification constitutes Finished goods that needed to be produced during production operation and Raw material consumed to produced goods, Job adheres two status, the first one is pending job which signifies material stock has been reserved but is not dispatched to manufacturing phase, thus there is no Physical transition in material stock. This delay may be due to unavailability of material stock at that particular instance of establishment of job.
Visibility
Jobs and their diverse phase can be viewed at real time, according to date refine context. The job status is represented by spectrum, which is associated with pending, manufacturing and QA Phase. In work in progress view, each color corresponds to respective event. Each event detail is viewed by hovering mouse over the concerned event. This establishes jobs panoramic view.
Production Operation Events
Change in status of job generates production events represented by color spectrum which are associated with pending, manufacturing and QA phase. Each spectrum color corresponds to event as well, besides job status. Each event detail can be viewed by hovering mouse over the concerned event bar.
Transition of Production Operation
Operation transition convolutes with production operation. There are two operation paths, both of them are initiated by invoking a job. When job status is set to Pending Job, it shows job will be executed at later time. It can be set to current Job when job is going to be in manufacturing phase. At completion of manufacturing process of the job, status will be set to Completed and produced finished goods will proceed towards QA department and further into finished goods inventory.
Finished Goods Outbound and Production Operations
The system contours internal work flow and finished goods. Finished goods are value added inventory, which consume non-value added product (raw material). Operations performed by this segment alter material’s perpetual inventory.
Visibility
In supply chain management information’s visibility is pivotal. It provides context perpetual inventory by date and goods, and history of perpetual inventory as well. Canvas legend provides spectrum to distinguish and visualizes perpetual inventory of that refined context. To elaborate view of perpetual inventory goods can be extracted by hovering mouse over the concerned bar.
Outbound Operation Events
Finished goods outbound events are inspected by checking Event check box. Hence material events are extracted associated with refined context. Detailed view of it is obtained by hovering mouse over the concerned event bar.
Finished Goods Outbound and Production Operation Transition
Panoramic view finished goods outbound and operation transition has two preliminary paths or stages. First one starts from Pending Phase, it corresponds to the situation where material is reserved for production, it is also called pending job. When material is received and placed in material inventory, at appropriate time job is initiated which leads to subsequent stage that is manufacturing phase. After completion of the job, finished product is produced which is transit into finished goods inventory. When in stock inventory is contaminated in any way, then outbound finished goods dispose or waste operation is performed which transforms the material to waste.
Inner Working
Bars.cs class is the bread and butter of this application. It is derived from System.Windows.Forms.Control
class. It represents work process in respect of time, Event in respect of date and quantity and perpetual inventory in respect of quantity and date. Events which contribute to visualization are OnPaintBackground
, Bar_MouseMove
, OnMouseClick
and Bar_MouseLeave
methods.
OnPaintBackground
This method draws bar user interface. It performs its functionality in step and calls MakeScale
, IdentifyBarContext
, GenerateCurrentContextBarResutset
and GenerateBars
respectively. It consumes barArrayList
to draw bars on bar frame which is populated at GenerateCurrentContextBarResutset
method call.
string filter = "";
Bar[] barArray;
ArrayList barArrayList;
float temp = 0;
private static Mutex mut = new Mutex();
System.Data.DataRow[] rows;
protected override void OnPaintBackground(PaintEventArgs pevent)
{
mut.WaitOne();
pevent.Graphics.SmoothingMode =Systtem.Drawing.Drawing2D.SmoothingMode.AntiAlias;
Rectangle translateRect = this.Bounds;
PaintEventArgs pe = new PaintEventArgs(pevent.Graphics, translateRect);
this.InvokePaintBackground(this.Parent, pe);
MakeScale(pevent);
barArrayList = new ArrayList();
temp = (m_ValueDifference * 100) * m_ValuePerPixel;
if ( Form1.data != null)
{
System.Windows.Forms.Control cont =
this.Parent.Controls.Find("Tag", true)[0];
cont.BackColor = System.Drawing.SystemColors.Control;
cont.ForeColor = Color.Black;
if (!IdentifyBarContext(cont))
return;
GenerateCurrentContextBarResutset(cont);
}
else
return;
GenerateBars(pevent);
mut.ReleaseMutex();
}
MakeScale
OnPaintBackground
method MakeScale
method. This method calibrates scale on bar frame according to current selected dimension. Its purpose is to enhance scale reading on bar frame.
protected void MakeScale(PaintEventArgs pe)
{
FindFontBounds();
Bitmap Bitmap= new Bitmap(Width, Height, pe.Graphics);
Graphics ggr = Graphics.FromImage(Bitmap);
ggr.FillRectangle(new SolidBrush(System.Drawing.Color.Transparent),
ientRectangle);
ggr.SmoothingMode = SmoothingMode.HighQuality;
ggr.PixelOffsetMode = PixelOffsetMode.HighQuality;
GraphicsPath gp = new GraphicsPath();
ggr.SetClip(ClientRectangle);
String valueText = "";
SizeF boundingBox;
Int32 counter1 = 0;
Int32 scaleValueUnit = 0;
Int32 scaleTemValueUnit = 0;
Int32 scaleUnit = 0;
while (scaleUnit <= 720)
{
ggr.DrawLine(new Pen(System.Drawing.Color.Gray, 2),
(Single)scaleValueUnit,
(Single)0,
(Single)scaleValueUnit,
(Single)35);
valueText = "";
boundingBox = ggr.MeasureString(valueText,
Font, -1, StringFormat.GenericTypographic);
scaleUnit++;
scaleTemValueUnit = Convert.ToInt32(scaleTemValueUnit +
_ValuePerPixel * 10));
scaleValueUnit = Convert.ToInt32(scaleValueUnit + (100 * m_ValuePerPixel));
counter1++;
}
pe.Graphics.DrawImageUnscaled(Bitmap, 0, 0);
pe.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
pe.Graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
}
IdentifyBarContext
After calibrating bar frame IdentifyBarContext
method is called. This method identifies context of the bar for which bars need to be drawn. MainForm.data
is populated by MainForm
, but it contain records of MainForm
context and is not appropriate for bar context. This method filters down the record for current bar context.
bool IdentifyBarContext(System.Windows.Forms.Control cont)
{
switch (perspective_ID)
{
case 0:
if (option_ID == 0)
filter = " Goods_ID =" + iD;
if (option_ID == 1)
filter = " Date = #" + cont.Text + "#";
rows = MainForm.data.Tables["GoodsCurrent"].Select(filter);
break;
case 1:
filter = " Job_ID = " + iD;
rows = MainForm.data.Tables["JobByDate"].Select(filter);
break;
case 2:
if (option_ID == 0)
filter = " Goods_ID =" + iD;
if (option_ID == 1)
filter = " Date = #" + cont.Text + "#";
rows = MainForm.data.Tables["GoodsCurrent"].Select(filter);
break;
}
return true;
}
GenerateCurrentContextBarResutset
In subsequent step, GenerateCurrentContextBarResutset
method is called. It generates resultset
corresponding to refined context. It assigns color to each bar’s state, phase or transition.
Non-Event Raw Material resultsets by Date
Generation of raw material and job resultset
by date will be discussed. In raw material, there are two more scenarios one is non-Event (perpetual inventory) and Event scenario. It quantifies the inventory by allocating length and location of concerned bar.
#region Generate non Event raw material resultsets by date
if (!is_Event)
{
if (rows.Length == 0)
{
DataAccess.Access ac = new InventorySys.DataAccess.Access();
rows = ac.RawMaterialSingleCurrent( iD
, this.Parent.Controls.Find("Date", true)[0].Text).Tables[0].Select();
}
barArray = new Bar[5];
barArray[0].initial_Point = 0;
barArray[0].ID = Convert.ToInt32(rows[0]["Goods_ID"]);
if (((Convert.ToInt32(rows[0]["Incomming_Current"])) - temp) > 3)
{
barArray[0].final_Point =
(Convert.ToInt32(rows[0]["Incomming_Current"]) - Convert.ToInt32
(rows[0]["Outgoing_Finished"]) -
Convert.ToInt32(rows[0]["Outgoing_Expected"]) –
Convert.ToInt32(rows[0]["Outgoing_Waste"] ) -
Convert.ToInt32(rows[0]["Outgoing_InProcess"])
- temp )* m_ValuePerPixel ;
barArray[0].color = Color.Green;
if( barArray[0].final_Point > 1 )
barArrayList.Add(barArray[0]);
}
else
{
barArray[0].final_Point = 0;
}
barArray[1].initial_Point = barArray[0].final_Point++;
barArray[1].ID = Convert.ToInt32(rows[0]["Goods_ID"]);
if (((Convert.ToInt32(rows[0]["Incomming_Expected"])) - temp) > 3)
{
barArray[1].final_Point = barArray[1].initial_Point +
((Convert.ToInt32(rows[0]["Incomming_Expected"])
- temp) * m_ValuePerPixel);
barArray[1].color = Color.Orange;
if (barArray[1].final_Point > 1)
barArrayList.Add(barArray[1]);
}
else
{
barArray[1].final_Point = barArray[1].initial_Point;
}
barArray[2].initial_Point = barArray[1].final_Point++;
barArray[2].ID = Convert.ToInt32(rows[0]["Goods_ID"]);
if (((Convert.ToInt32(rows[0]["Outgoing_Expected"])) - temp) > 3)
{
barArray[2].final_Point = barArray[2].initial_Point +
((Convert.ToInt32(rows[0]["Outgoing_Expected"])
- temp) * m_ValuePerPixel);
barArray[2].color = Color.DarkBlue;
if (barArray[2].final_Point > 1)
barArrayList.Add(barArray[2]);
}
else
{
barArray[2].final_Point = barArray[2].initial_Point;
}
barArray[3].initial_Point = barArray[2].final_Point++;
barArray[3].ID = Convert.ToInt32(rows[0]["Goods_ID"]);
if (((Convert.ToInt32(rows[0]["Outgoing_InProcess"])) - temp) > 3)
{
barArray[3].final_Point = barArray[3].initial_Point
+ (Convert.ToInt32(rows[0]["Outgoing_InProcess"])
- temp) * m_ValuePerPixel;;
barArray[3].color = Color.Yellow;
if (barArray[3].final_Point > 1)
{
barArrayList.Add(barArray[3]);
}
}
}
#endregion
Event Raw Material resultsets by Date
In event scenario, all transition physical or non-physical are presented in palettes, and its quantity is represented by bar’s length.
#region Generate Event raw material resultsets by date
if (is_Event)
{
barArray = new Bar[rows.Length];
float temp_Initial_Point = 0;
for (int i = 0; i < rows.Length; i++)
{
barArray[i].final_Point = temp_Initial_Point +
(Convert.ToInt32(rows[i]["Unit"]) - temp ) * m_ValuePerPixel;;;
barArray[i].initial_Point = temp_Initial_Point - temp;
temp_Initial_Point = barArray[i].final_Point + temp;
barArray[i].ID = Convert.ToInt32(rows[i]["Transaction_ID"]);
if (Convert.ToInt32(rows[i]["Transaction_Type_ID"]) == 1 &&
Convert.ToInt32(rows[i]["Transaction_Status_ID"]) == 2)
{
barArray[i].color = Color.Orange;
}
if (Convert.ToInt32(rows[i]["Transaction_Type_ID"]) == 1 &&
Convert.ToInt32(rows[i]["Transaction_Status_ID"]) == 1)
{
barArray[i].color = Color.Green;
}
if (Convert.ToInt32(rows[i]["Transaction_Type_ID"]) == 2 &&
Convert.ToInt32(rows[i]["Transaction_Status_ID"]) ==2 )
{
barArray[i].color = Color.DarkBlue;
}
if (Convert.ToInt32(rows[i]["Transaction_Type_ID"]) == 2 &&
Convert.ToInt32(rows[i]["Transaction_Status_ID"]) == 3)
{
barArray[i].color = Color.Yellow;
}
if (Convert.ToInt32(rows[i]["Transaction_Type_ID"]) == 2 &&
Convert.ToInt32(rows[i]["Transaction_Status_ID"]) == 5)
{
barArray[i].color = Color.Black;
}
if (Convert.ToInt32(rows[i]["Transaction_Type_ID"]) == 2 &&
Convert.ToInt32(rows[i]["Transaction_Status_ID"]) == 6)
{
barArray[i].color = Color.Red;
}
if (barArray[i].final_Point > 1)
barArrayList.Add(barArray[i]);
}
}
#endregion
Job Event resultset by Date
In this event based scenario, status is presented in colors, and time which it consumes is represented by bar’s length and location on the bar frame.
#region Generate job resultsets by date
TimeSpan ts = new TimeSpan();
ts = DateTime.Parse(rows[0]["Start_Date_Time"].ToString()) -
DateTime.Parse(Bars.m_Start_Date);
Bar[] barArrayFirst = new Bar[1];
barArrayFirst[0].initial_Point = (ts.Days + 1) * (100 * m_ValuePerPixel) +
(ts.Hours * Convert.ToSingle(2.5 * m_ValuePerPixel));
ts = DateTime.Parse(rows[0]["End_Date_Time"].ToString()) -
DateTime.Parse(Bars.m_Start_Date);
barArrayFirst[0].final_Point = (ts.Days + 1) * (100 * m_ValuePerPixel) +
(ts.Hours * Convert.ToSingle(2.5 * m_ValuePerPixel)); ;
barArrayFirst[0].ID = Convert.ToInt32(rows[0]["Job_ID"]);
barArrayFirst[0].Job_Transaction_ID = 0;
barArrayFirst[0].Is_Final_Job_Status = true;
if (Convert.ToInt32(rows[0]["Status_ID"]) == 2)
{
cont.BackColor = Color.Orange;
cont.ForeColor = Color.Black;
barArrayFirst[0].color = Color.Orange;
}
if (Convert.ToInt32(rows[0]["Status_ID"]) == 3)
{
cont.BackColor = Color.Green;
cont.ForeColor = Color.White;
barArrayFirst[0].color = Color.Black;
}
if (Convert.ToInt32(rows[0]["Status_ID"]) == 5)
{
cont.BackColor = Color.Yellow;
cont.ForeColor = Color.Black;
barArrayFirst[0].color = Color.DimGray;
}
if (Convert.ToInt32(rows[0]["Status_ID"]) == 6)
{
cont.BackColor = Color.Red;
cont.ForeColor = Color.White;
barArrayFirst[0].color = Color.Red;
}
if (barArrayFirst[0].final_Point > 1)
barArrayList.Add(barArrayFirst[0]);
rows = Form1.data.Tables["Job_TransactionByDate"].Select
( "Job_ID = " + rows[0]["Job_ID"].ToString() );
barArray = new Bar[rows.Length];
for (int i = 0; i < rows.Length; i++)
{
ts = DateTime.Parse(rows[i]["Start_Date_Time"].ToString())
- DateTime.Parse(Bars.m_Start_Date);
barArray[i].initial_Point = ( (ts.Days + 1) * (100 * m_ValuePerPixel))
+ (ts.Hours * Convert.ToSingle(2.5 * m_ValuePerPixel));
ts = DateTime.Parse(rows[i]["End_Date_Time"].ToString())
- DateTime.Parse(Bars.m_Start_Date);
barArray[i].final_Point = ((ts.Days + 1) * (100 * m_ValuePerPixel))
+ (ts.Hours * Convert.ToSingle(2.5 * m_ValuePerPixel));
barArray[i].ID = Convert.ToInt32(rows[i]["Job_ID"]);
barArray[i].Job_Transaction_ID =
Convert.ToInt32(rows[i]["Job_Transaction_ID"]);
barArray[i].Is_Final_Job_Status = false;
if (Convert.ToInt32(rows[i]["Status_ID"]) == 2)
{
barArray[i].color = Color.DarkBlue;
}
if (Convert.ToInt32(rows[i]["Status_ID"]) == 3)
{
barArray[i].color = Color.Black;
}
if (Convert.ToInt32(rows[i]["Status_ID"]) == 5)
{
barArray[i].color = Color.DimGray;
}
if (Convert.ToInt32(rows[i]["Status_ID"]) == 6)
{
barArray[i].color = Color.Red;
}
if (barArray[i].final_Point > 1)
barArrayList.Add(barArray[i]);
}
#endregion
GenerateBars
Method consumes generated resultset
and puts them in visual form by drawing them. The actual drawing take place here.
void GenerateBars(PaintEventArgs pevent)
{
int generatedBarCount = 0;
System.Drawing.Drawing2D.GraphicsPath path = new GraphicsPath();
Pen drawingPen = new Pen(Color.Gray);
Bar barStruct;
Color shadeColor, fillColor;
int bar = 0;
Rectangle r = new Rectangle();
while (generatedBarCount < barArrayList.Count)
{
if (generatedBarCount >= barArrayList.Count)
goto Finish1;
barStruct = (Bar)barArrayList[generatedBarCount];
fillColor = barStruct.color;
shadeColor = barStruct.color;
bar = Convert.ToInt32(barStruct.final_Point) -
vert.ToInt32(barStruct.initial_Point);
r = new Rectangle(0, 0, bar, 18);
r.X = Convert.ToInt32(barStruct.initial_Point);
r.Y += 4;
i = i + bar;
path = new GraphicsPath();
path.AddRectangle(r);
PathGradientBrush brush = new PathGradientBrush(path);
brush.WrapMode = WrapMode.TileFlipX;
brush.SurroundColors = new Color[] { shadeColor };
brush.CenterColor = Color.White;
pevent.Graphics.FillPath(brush, path);
pevent.Graphics.DrawPath(drawingPen, path);
generatedBarCount++;
brush.Dispose();
}
Finish1:
drawingPen.Dispose();
path.Dispose();
}
Bar_MouseMove
When mouse moves over a bar frame, this event method is called. On this method, mouse coordinates are extracted to find bar on which mouse is hovering if it is not on generated bar it will display information of bar context. When mouse is over generated bar then respective popup dialog will appear which contains information about the particular event, job or perpetual inventory, e.g. if mouse hover on event bar then popup will display that particular event information.
private void Bar_MouseMove(object sender, MouseEventArgs e)
{
if (barArrayList == null)
return;
if (!Is_HoverEnable)
return;
System.Drawing.Point po = this.FindForm().PointToClient
stem.Windows.Forms.Control.MousePosition);
System.Drawing.Point po2 = this.PointToClient
stem.Windows.Forms.Control.MousePosition);
if (po.X == poLast.X && po.Y == poLast.Y)
return;
poLast = po;
if (aForm != null)
aForm.Dispose();
float m_X = po2.X;
Bar barStruct;
int DisplayShift = Convert.ToInt32(temp / m_ValuePerPixel * 0.5);
#region Traversing current bar
for (int i = 0; i < barArrayList.Count; i++)
{
barStruct = (Bar)barArrayList[i];
if (m_X >= barStruct.initial_Point && m_X <= barStruct.final_Point)
{
if (perspective_ID == 0)
{
if (!is_Event)
{
aForm = new GoodsBalloon(filter,
barStruct.color, rows);
}
else
{
aForm = new ActivityBalloon(barStruct.ID);
}
}
if (perspective_ID == 1)
{
aForm = new JobBalloon(barStruct.ID,
truct.Job_Transaction_ID);
}
if (perspective_ID == 2)
{
if (!is_Event)
{
aForm = new GoodsBalloon(filter,
barStruct.color, rows);
}
else
{
aForm = new ActivityBalloon(barStruct.ID);
}
}
aForm.setBalloonPosition(this.FindForm(), po);
aForm.Show();
this.Focus();
return;
}
}
#endregion
string tag = "";
string name = "";
switch (perspective_ID)
{
case 0:
tag = "Raw Material";
name = this.Parent.Controls.Find("Tag", true)[0].Text;
break;
case 1:
tag = "Job ID";
name = this.Parent.Controls.Find("Tag", true)[0].Text;
break;
case 2:
tag = "Finished Goods";
name = this.Parent.Controls.Find("Tag", true)[0].Text;
break;
}
aForm = new VacantBalloon(tag, name);
aForm.setBalloonPosition(this.FindForm(), po);
aForm.Show();
this.Focus();
}
OnMouseClick
This event is called when status of job is needed to be changed. It can be called by clicking the concerned bar against which popup dialog will prompt for input.
protected override void OnMouseClick(MouseEventArgs e)
{
float m_X = e.X;
Bar barStruct;
if (perspective_ID == 2 || perspective_ID == 1)
{
return;
}
for (int i = 0; i < barArrayList.Count; i++)
{
barStruct = (Bar)barArrayList[i];
if (m_X >= barStruct.initial_Point && m_X <= barStruct.final_Point)
{
# region
if (!Is_HoverEnable)
return;
if (aForm != null)
aForm.Dispose();
if (aFormConcreate != null)
aFormConcreate.Dispose();
System.Drawing.Point po = this.FindForm().PointToClient
(System.Windows.Forms.Control.MousePosition);
System.Drawing.Point poi = po;
if (perspective_ID == 1 && barStruct.Is_Final_Job_Status)
Control.JobStatusBalloon.ShowBox(barStruct, poi);
#endregion
}
}
base.OnMouseClick(e);
}
Bar_MouseLeave
Event is called when mouse leaves current bar frame, it will dispose of current popup dialog.
public void Bar_MouseLeave(object sender, EventArgs e)
{
if (aForm != null)
aForm.Dispose();
}