Try Iron Speed Designer here.
|
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.
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 End Class
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…
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
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
...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"))
["strContainer"];
System.Web.HttpContext.Current.Session("strAmountGrandTotal") = Nothing
End If
MyBase.OnPreRender(e)
End Sub
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.
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.