How to use Chart component in Acumatica

Hello everybody,

today I want to share one more trick on how to chart in Acumatica. Imagine following case: you have some information about your sales orders, and want somehow to visualize it wiht nice picture. 

How to do it? You can use charting component of windows. Here I've described building elements of charting library. Charting library is located at System.Drawing library.

Main purpose of this article is to demonstrate how to use Charting library without going deep about image management. 

Basically code shows how to load filtered information about sales order add data points, generate dynamic picture and show it on the form.

Firstly let me show to you picture part:

<%@ Page Language="C#" MasterPageFile="~/MasterPages/FormDetail.master" AutoEventWireup="true" ValidateRequest="false" CodeFile="AV302000.aspx.cs" 
    Inherits="Page_AV302000" Title="Untitled Page" %>
<%@ MasterType VirtualPath="~/MasterPages/FormView.master" %>
<asp:Content ID="cont1" ContentPlaceHolderID="phDS" Runat="Server">
    <px:PXDataSource ID="ds" runat="server" Visible="True" Width="100%" TypeName="Avocados.Library.AvocadosTrain" 
                     SuspendUnloading="False"
                     PrimaryView="AvocadosTrainingFilter">
        <CallbackCommands>
        </CallbackCommands>
    </px:PXDataSource>
</asp:Content>
 
<asp:Content ID="cont2" ContentPlaceHolderID="phF" runat="Server">
<px:PXFormView ID="form" runat="server" DataSourceID="ds" Style="z-index100" Width="100%" DataMember="AvocadosTrainingFilter" 
    Caption="Configure training"
               NoteIndicator="True" FilesIndicator="True" LinkIndicator="True" 
               ActivityIndicator="True" ActivityField="NoteActivity" DefaultControlID="edFrom" NotifyIndicator="True"
               TabIndex="14900">
        <CallbackCommands>
            <Save PostData="Self" />
        </CallbackCommands>
    <Template>
        <px:PXDateTimeEdit ID="edFrom" runat="server" DataField="From" CommitChanges="True" AutoRefresh="True">
        </px:PXDateTimeEdit>
        <px:PXLayoutRule runat="server" StartColumn="True">
        </px:PXLayoutRule>
        <px:PXDateTimeEdit ID="edTo" runat="server" DataField="To" CommitChanges="True" AutoRefresh="True">
        </px:PXDateTimeEdit>
        <px:PXLayoutRule runat="server" StartColumn="True">
        </px:PXLayoutRule>
        <px:PXSelector ID="edRegion" runat="server" DataField="Region" CommitChanges="True" AutoRefresh="True" Width="100px">
        </px:PXSelector>
        <px:PXLayoutRule runat="server" StartColumn="True">
        </px:PXLayoutRule>
        <px:PXSelector ID="edType" runat="server" DataField="Type" CommitChanges="True" AutoRefresh="True" Width="100px">
        </px:PXSelector>
    </Template>
    </px:PXFormView>
</asp:Content>
 
<asp:Content ID="cont3" ContentPlaceHolderID="phG" Runat="Server">
    <px:PXFormView ID="frm2" runat="server" DataSourceID="ds" DataMember="Images" TabIndex="100">
        <Template>
            <px:PXLayoutRule runat="server" StartRow="True"/>
            <px:PXTextEdit ID="edName" runat="server" DataField="Name" Enabled="false"></px:PXTextEdit>
            <px:PXImageView ID="edImage" runat="server" DataField="Name">
            </px:PXImageView>
        </Template>
    </px:PXFormView>
</asp:Content>

Below goes a code of graph, which I've used for visualization of Sales order.

[Serializable]
public class AvocadosTrain : PXGraph<AvocadosTrain>
{
    public PXFilter<AvocadosTrainingFilter> AvocadosTrainingFilter;
 
    public PXSelect<SOOrderWhere<SOOrderExt.regionIsNotNull>> AvocadosSalesOrders;
 
    public PXSelect<UploadFile> Images;
 
    public override bool IsDirty => false;
 
    protected virtual void AvocadosTrainingFilter_From_FieldDefaulting(PXCache sender, PXFieldDefaultingEventArgs e)
    {
        AvocadosTrainingFilter filter = (AvocadosTrainingFilter)e.Row;
        if (filter == null)
            return;
        var first = PXSelect<SOOrderWhere<SOOrderExt.regionIsNotNull>, OrderBy<Asc<SOOrder.orderDate>>>.SelectWindowed(this, 0, 1).First();
        e.NewValue = first.GetItem<SOOrder>().OrderDate;
    }
 
 
    protected virtual IEnumerable images()
    {
        var img =
            PXSelect<UploadFileWhere<UploadFile.fileIDEqual<Required<UploadFile.fileID>>>>.Select(thisGuid.Parse(FileID));
        return img;
    }
 
 
    protected virtual void AvocadosTrainingFilter_To_FieldDefaulting(PXCache sender, PXFieldDefaultingEventArgs e)
    {
        AvocadosTrainingFilter filter = (AvocadosTrainingFilter)e.Row;
        if (filter == null)
            return;
        var last = PXSelect<SOOrderWhere<SOOrderExt.regionIsNotNull>, OrderBy<Desc<SOOrder.orderDate>>>.SelectWindowed(this, 0, 1).First();
        e.NewValue = last.GetItem<SOOrder>().OrderDate;
    }
 
    public PXAction<AvocadosTrainingFilter> ShowSelectedRegion;
 
    private string FileID = "ff157a77-a312-4df2-badf-495dfe08a117";
 
    [PXButton]
    [PXUIField(DisplayName = "Visualise current region")]
    protected virtual void showSelectedRegion()
    {
        var avocaosInfo =
            PXSelect<SOOrderWhere<SOOrderExt.regionEqual<Required<SOOrderExt.region>>, 
                And<SOOrderExt.typeEqual<Required<SOOrderExt.type>>>>>.Select(this,
                    AvocadosTrainingFilter.Current.Region, AvocadosTrainingFilter.Current.Type);
 
        var orders = avocaosInfo.Select(a => a.GetItem<SOOrder>()).ToList();
        var ordersExts = orders.Select(a => a.GetExtension<SOOrderExt>()).ToList();
 
        var qtys = orders.OrderBy(a => a.OrderDate).Select(a => a.OrderQty).ToList();
 
        var chart = new Chart();
        chart.Titles.Add(AvocadosTrainingFilter.Current.Region + " " + AvocadosTrainingFilter.Current.Type);
        chart.Size = new Size(600 * 2, 250 * 2);
 
        var chartArea = new ChartArea();
        
        chartArea.AxisX.MajorGrid.LineColor = Color.LightGray;
        chartArea.AxisY.MajorGrid.LineColor = Color.LightGray;
        chartArea.AxisX.LabelStyle.Font = new Font("Consolas", 8);
        chartArea.AxisY.LabelStyle.Font = new Font("Consolas", 8);
        chart.ChartAreas.Add(chartArea);
 
        chart.Series.Clear();
 
        var series = new Series();
        series.Name = "Series1";
        
        series.XValueType = ChartValueType.Double;
        series.YValueType = ChartValueType.Double;
        chart.Series.Add(series);
 
        // bind the datapoints
        //chart.Series["Series1"].Points.DataBindXY(xVals, qtys);
        for (int r = 0; r < qtys.Count; r++)
        {
            double value = (double) qtys[r].Value;
            chart.Series["Series1"].Points.AddY(value);
        }
 
        series.ChartType = SeriesChartType.FastLine;
 
        // draw!
        chart.Invalidate();
 
        var memoryStream = new MemoryStream();
        chart.SaveImage(memoryStream, ChartImageFormat.Png);
 
        var buffer = memoryStream.ToArray();
 
        var uploadedImage = PXSelectJoin<UploadFile,
            InnerJoin<UploadFileRevisionOn<UploadFile.fileIDEqual<UploadFileRevision.fileID>>>,
            Where<UploadFile.fileIDEqual<Required<UploadFile.fileID>>>>.Select(thisGuid.Parse(FileID));
 
        var uploadFileRevision = new UploadFileRevision();
        uploadFileRevision.CreatedByID = PXAccess.GetUserID();
        uploadFileRevision.BlobData = buffer;
        uploadFileRevision.FileID = Guid.Parse(FileID);
        uploadFileRevision.FileRevisionID = 1;
        uploadFileRevision.CreatedDateTime = DateTime.Now;
 
        if (uploadedImage.FirstOrDefault() == null)
        {
            //create
            var uploadFile = new UploadFile();
            
            uploadFile.FileID = Guid.Parse(FileID);
            uploadFile.Name = "visualization.png";
            uploadFile.CreatedByID = PXAccess.GetUserID();
            uploadFile.CreatedDateTime = DateTime.Now;
            uploadFile.Versioned = false;
            
 
            PXDatabase.Insert<UploadFile>(
                new PXDataFieldAssign<UploadFile.fileID>(uploadFile.FileID),
                new PXDataFieldAssign<UploadFile.lastRevisionID>(1),
                new PXDataFieldAssign<UploadFile.name>(uploadFile.Name),
                new PXDataFieldAssign<UploadFile.createdByID>(uploadFile.CreatedByID),
                new PXDataFieldAssign<UploadFile.createdDateTime>(uploadFile.CreatedDateTime),
                new PXDataFieldAssign<UploadFile.versioned>(uploadFile.Versioned));
 
            PXDatabase.Insert<UploadFileRevision>(
                new PXDataFieldAssign<UploadFileRevision.fileID>(uploadFileRevision.FileID),
                new PXDataFieldAssign<UploadFileRevision.fileRevisionID>(1),
                new PXDataFieldAssign<UploadFileRevision.size>(buffer.Length / 1024),
                new PXDataFieldAssign<UploadFileRevision.createdByID>(uploadFileRevision.CreatedByID),
                new PXDataFieldAssign<UploadFileRevision.createdDateTime>(uploadFileRevision.CreatedDateTime),
                new PXDataFieldAssign("Data", uploadFileRevision.BlobData));
        }
        else
        {
            //update
            PXDatabase.Update<UploadFileRevision>(
                new PXDataFieldAssign("Data", uploadFileRevision.BlobData),
                new PXDataFieldRestrict<UploadFileRevision.fileID>(uploadFileRevision.FileID),
                new PXDataFieldAssign<UploadFileRevision.size>(buffer.Length / 1024),
                new PXDataFieldRestrict<UploadFileRevision.fileRevisionID>(uploadFileRevision.FileRevisionID)
                );
        }
 
        Images.Current = Images.Search<UploadFile.fileID>(uploadFileRevision.FileID);
        Images.Current.Data = buffer;
        throw new PXRedirectRequiredException(this"RandomImage");
        //var fr = PXSelect<UploadFileRevision, Where>
    }
 
}

Also code of SOOrderExt:

public class SOOrderExtPXCacheExtension<SOOrder>
{
    #region Region
    public abstract class region : IBqlField
    {
    }
    [PXDBString(255)]
    [PXUIField(DisplayName = "Region", Visibility = PXUIVisibility.Visible)]
    public virtual string Region { getset; }
    #endregion
 
 
    #region type
    public abstract class type : IBqlField
    {
    }
    [PXDBString(255)]
    [PXUIField(DisplayName = "Type", Visibility = PXUIVisibility.Visible)]
    public virtual string Type { getset; }
    #endregion
}

And code of filter:

[Serializable]
public class AvocadosTrainingFilter : IBqlTable
{
    #region from
    public abstract class from : IBqlField
    {
    }
    [PXDBDate]
    [PXUIField(DisplayName = "From", Visibility = PXUIVisibility.Visible)]
    public virtual DateTime? From { getset; }
    #endregion
 
    #region to
    public abstract class to : IBqlField
    {
    }
    [PXDBDate]
    [PXUIField(DisplayName = "To", Visibility = PXUIVisibility.Visible)]
    public virtual DateTime? To { getset; }
    #endregion
 
    #region Type
    public abstract class type : IBqlField
    {
    }
    [PXDBString(255)]
    [PXUIField(DisplayName = "Type", Visibility = PXUIVisibility.Visible)]
    [PXSelector(typeof(Search4<SOOrderExt.typeAggregate<GroupBy<SOOrderExt.type>>>), typeof(SOOrderExt.type))]
    public virtual string Type { getset; }
    #endregion
 
    #region Region
    public abstract class region : IBqlField
    {
    }
    [PXDBString(255)]
    [PXUIField(DisplayName = "Region", Visibility = PXUIVisibility.Visible)]
    [PXSelector(typeof(Search4<SOOrderExt.regionAggregate<GroupBy<SOOrderExt.region>>>), typeof(SOOrderExt.region))]
    public virtual string Region { getset; }
    #endregion
}

Finally, I was able to get something like this:

No Comments

Add a Comment