Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Double-Entry Accounting in an Iron Speed Designer Application

4 Nov 2008 0  
Les Cardwell demonstrates how to build a double-entry accounting application using the latest code generation technology, Iron Speed Designer.

Try Iron Speed Designer here.

CustomerProfileLesCardwell.gif

You must validate that a user has entered an equal amount of debits to credits to ensure all entries have accounted for money flowing in and out of each account.
- Les Cardwell, President of White Box, Inc.

February 12, 2007
Iron Speed Designer V4.X

Double-Entry Accounting in an Iron Speed Designer Application

There are a few business rules that apply to all Ledgers in a double-entry accounting application, regardless of whether they are Accounts Receivable, Accounts Payable, General Journals, or any one of a dozen Ledgers. The most important is to validate that the user has entered an equal amount of debits to credits to ensure all entries have accounted for money flowing in and out of each account. This is ideally done at run-time during data-entry before the record can actually be saved. If accomplished, there is no need to run reports to find un-posted entries, out-of-balance entries, or to even run a trial balance at the end of the accounting period (hypothetically) because we have enforced constraints to address these potential data-correctness errors at the point of entry.

Try Iron Speed Designer here.

Figure1.jpg

Validating an unbalanced transaction.

This is accomplished by overriding the SaveData() method in the customizable area of the <form>TableControl: Base<subform>TableControl class, located in:

.NET Framework 1.1:

...\<Table Folder>\Add<Table Name>Page.aspx.cs or .vb

.NET Framework 2.0 / 3.0:

...\App_Code\<Table Folder>\Add<Table Name>Page.Controls.cs or .vb

The override method itself simply loops through the subform array, totals the values, then either allows the record to be saved, or tosses an exception which alerts the user with the above message.

C#:

public class GJDistributionsTableControl : BaseGJDistributionsTableControl
{

    public override void SaveData()
    {
        decimal amountToDistribute = 0;
        GJDistributionsTableControlRow[] oTable = this.GetRecordControls();
        DbUtils.StartTransaction();
        foreach (GJDistributionsTableControlRow oRow in oTable)
        {
            amountToDistribute = amountToDistribute + Convert.ToDecimal(oRow.Amount.Text);
        }

        if (amountToDistribute.Equals(0))
        {
            DbUtils.CommitTransaction();
            base.SaveData();
        }
        else
        {
            throw new Exception("Distribution Amounts are not equal.");
        }
    }

Visual Basic .NET:

Public Class GJDistributionsTableControl
    Inherits BaseGJDistributionsTableControl
 
    Public Overrides Sub SaveData()
        Dim amountToDistribute As Decimal = 0
        Dim oTable As GJDistributionsTableControlRow() = Me.GetRecordControls()
        DbUtils.StartTransaction()
        Dim oRow As GJDistributionsTableControlRow
        For Each oRow In oTable
           amountToDistribute = amountToDistribute + Convert.ToDecimal(oRow.Amount.Text)
        Next oRow
 
        If amountToDistribute.Equals(0) Then
           DbUtils.CommitTransaction()
           MyBase.SaveData()
        Else
           Throw New Exception("Distribution Amounts are not equal.")
        End If
    End Sub 'SaveData
End Class 'GJDistributionsTableControl

Try Iron Speed Designer here.

We also want to assist the user in determining how much remains to be distributed in an entry, because long entries can become confusing and wearisome when we have to keep track of the net amounts entered either manually or by using a secondary tool while performing the data-entry function. Ideally, this is handled in the user interface screen allowing the user to gracefully adapt to the changing information, and inform them when the data entered may be incorrect. In this case, we display the amount to distribute in the distribution table entry screen, most naturally at the bottom of the Amount column per the screen shot below…

Figure2.JPG

Simplifying the user entry process with dynamic updates.

This is accomplished by overriding the AddButton_Click() method, also in the customizable area of the TableControl class, located in:

.NET Framework 1.1:

...\<Table Folder>\Add<Table Name>Page.aspx.cs or .vb

.NET Framework 2.0 / 3.0:

...\App_Code\<Table Folder>\Add<Table Name>Page.Controls.cs or .vb

This click handler utilizes the same method for aggregating the Amounts entered.

C#:

public override void GJDistributionsAddButton_Click(object sender, EventArgs args)
{
    try
    {
        DbUtils.StartTransaction();

        decimal amountGrandTotal = 0.00m;
        GJDistributionsTableControlRow[] oTable2 = this.GetRecordControls();
        foreach (GJDistributionsTableControlRow oRow2 in oTable2)
        {
            amountGrandTotal = amountGrandTotal + 
                Convert.ToDecimal(oRow2.Amount.Text);
        }

        if (amountGrandTotal.Equals(0))
        {
            ((Label)(this.Page.FindControl("AmountGrandTotal"))).Text = "0.00";
        }
        else
        {
            ((Label)(this.Page.FindControl("AmountGrandTotal"))).Text = 
                Convert.ToString(amountGrandTotal);
        }

        this.AddNewRecord = 1;
        this.DataChanged = true;
        this.Page.CommitTransaction(sender);
    }
    catch (Exception ex)
    {
        this.Page.RollBackTransaction(sender);

        BaseClasses.Utils.MiscUtils.RegisterJScriptAlert(this, "BUTTON_CLICK_MESSAGE",
            ex.Message);
        this.Page.ErrorOnPage = true;
    }
    finally
    {
        DbUtils.EndTransaction();
    }
}

Visual Basic .NET:

Public Overrides Sub GJDistributionsAddButton_Click(sender As Object, args As EventArgs)
    Try
        DbUtils.StartTransaction()
 
        Dim amountGrandTotal As Decimal = 0D
        Dim oTable2 As GJDistributionsTableControlRow() = Me.GetRecordControls()
        Dim oRow2 As GJDistributionsTableControlRow
        For Each oRow2 In oTable2
           amountGrandTotal = amountGrandTotal + Convert.ToDecimal(oRow2.Amount.Text)
        Next oRow2
 
        If amountGrandTotal.Equals(0) Then
           CType(Me.Page.FindControl("AmountGrandTotal"), Label).Text = "0.00"
        Else
           CType(Me.Page.FindControl("AmountGrandTotal"), Label).Text =
           Convert.ToString(amountGrandTotal)
        End If
 
        Me.AddNewRecord = 1
        Me.DataChanged = True
        Me.Page.CommitTransaction(sender)
    Catch ex As Exception
        Me.Page.RollBackTransaction(sender)
 
        BaseClasses.Utils.MiscUtils.RegisterJScriptAlert(Me, "BUTTON_CLICK_MESSAGE",
        ex.Message)
        Me.Page.ErrorOnPage = True
    Finally
        DbUtils.EndTransaction()
    End Try
End Sub 'GJDistributionsAddButton_Click

Try Iron Speed Designer here.

Note that there currently exists an issue that prevents displaying the Amount column as 'money' ($100.00) and creates a string conversion error when executed, so the format has to be changed to a custom format of 0.00 in the Iron Speed Designer in the Amount properties display attributes.

If we then want to take it one step further, we can auto-enter the AmountToDistribute (* -1) for the user by saving the inverse value to a session variable in the 'AddButton_Click' method...

    amountGrandTotal = amountGrandTotal * -1;
    System.Web.HttpContext.Current.Session["strAmountGrandTotal"] = Convert.ToString(
        amountGrandTotal);

...so that the entire class becomes...

C#:

public override void GJDistributionsAddButton_Click(object sender, EventArgs args)
{
    try
    {
        DbUtils.StartTransaction();

        decimal amountGrandTotal = 0.00m;
        GJDistributionsTableControlRow[] oTable2 = this.GetRecordControls();
        foreach (GJDistributionsTableControlRow oRow2 in oTable2)
        {
            amountGrandTotal = amountGrandTotal +
                Convert.ToDecimal(oRow2.Amount.Text);
        }

        if (amountGrandTotal.Equals(0))
        {
            ((Label)(this.Page.FindControl("AmountGrandTotal"))).Text = "0.00";
        }
        else
        {
            ((Label)(this.Page.FindControl("AmountGrandTotal"))).Text = 
                Convert.ToString(amountGrandTotal);
        }

        amountGrandTotal = amountGrandTotal * -1;
        System.Web.HttpContext.Current.Session["strAmountGrandTotal"] = 
            Convert.ToString(amountGrandTotal);

        this.AddNewRecord = 1;
        this.DataChanged = true;
        this.Page.CommitTransaction(sender);
    }
    catch (Exception ex)
    {
        this.Page.RollBackTransaction(sender);

        BaseClasses.Utils.MiscUtils.RegisterJScriptAlert(this, "BUTTON_CLICK_MESSAGE",
            ex.Message);
        this.Page.ErrorOnPage = true;
    }
    finally
    {
        DbUtils.EndTransaction();
    }
}

Visual Basic .NET :

Public Overrides Sub GJDistributionsAddButton_Click(sender As Object, args As EventArgs)
    Try
        DbUtils.StartTransaction()
 
        Dim amountGrandTotal As Decimal = 0D
        Dim oTable2 As GJDistributionsTableControlRow() = Me.GetRecordControls()
        Dim oRow2 As GJDistributionsTableControlRow
        For Each oRow2 In oTable2
           amountGrandTotal = amountGrandTotal + Convert.ToDecimal(oRow2.Amount.Text)
        Next oRow2
 
        If amountGrandTotal.Equals(0) Then
           CType(Me.Page.FindControl("AmountGrandTotal"), Label).Text = "0.00"
        Else
           CType(Me.Page.FindControl("AmountGrandTotal"), Label).Text =
           Convert.ToString(amountGrandTotal)
        End If
 
              amountGrandTotal = amountGrandTotal * -1;
              Me.Page.Session["strAmountGrandTotal"] = Convert.ToString(amountGrandTotal);
 
        Me.AddNewRecord = 1
        Me.DataChanged = True
        Me.Page.CommitTransaction(sender)
    Catch ex As Exception
        Me.Page.RollBackTransaction(sender)
 
        BaseClasses.Utils.MiscUtils.RegisterJScriptAlert(Me, "BUTTON_CLICK_MESSAGE",
        ex.Message)
        Me.Page.ErrorOnPage = True
    Finally
        DbUtils.EndTransaction()
    End Try
End Sub 'GJDistributionsAddButton_Click

...and adding a PreRender event to the ‘public class ARDistributionsTableControlRow : BaseARDistributionsTableControlRow’class...

C#:

public class ARDistributionsTableControlRow : BaseARDistributionsTableControlRow
{
    protected override void OnPreRender(System.EventArgs e)
    {
        if (System.Web.HttpContext.Current.Session["strAmountGrandTotal"] != null)
        {
            this.Amount1.Text = (string)System.Web.HttpContext.Current.Session
                ["strAmountGrandTotal"];

            System.Web.HttpContext.Current.Session["strAmountGrandTotal"] = null;
        }
        base.OnPreRender(e);
    }
}

Visual Basic .NET:

Protected Overrides Sub OnPreRender(e As System.EventArgs)
    If Not (System.Web.HttpContext.Current.Session("strAmountGrandTotal") Is Nothing) Then
        Me.Amount1.Text = CStr(System.Web.HttpContext.Current.Session
           ("strAmountGrandTotal"))
           'this.Container.SelectedValue = (string)System.Web.HttpContext.Current.Session
           ["strContainer"];
           System.Web.HttpContext.Current.Session("strAmountGrandTotal") = Nothing
        End If
    MyBase.OnPreRender(e)
End Sub 'OnPreRender

Try Iron Speed Designer here.

When we then click the Add button, not only does it now give us the Amount to Distribute at the bottom of the page, but it also auto-inserts that amount into the next line-item to further enhance the end-user experience.

Figure3.JPG

Clicking the Add button automatically inserts the amount into the next line-item.

Try Iron Speed Designer here.

Read more on Iron Speed: Integrating Help in your Iron Speed Designer Application.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here