Part one is a review from last week on how to complete the first step of every project
Part 1 - Housekeeping / Overhead
Project and Form Property Standards
Save the Project in your NEW Project directory
Did you remember the comment block and Option Explicit?
'
' Name
' Class and Meeting Time
' Brief Description of this Program
'
Option Explicit
Part 2 - Building the Example Application
This program should simulate the use of a cash register processing an order at most any retail establishment. Enter the quantity and cost of each item and click on the "Enter" button or press the "Enter" key (remember what will make this happen). The program must calculate the cost for that item, and accumulate that amount into the order total. The program must also maintain the cash drawer balance after each order is complete.
Build the User Interface
Use the Format Menu Option to correctly size and align the controls on the form. Make sure that the tab indexes are set correctly. How about the keyboard interface for the two text boxes and the three command buttons?
Text Boxes: txtQty txtCost |
Command Buttons: cmdEnter cmdCheckOut cmdExit |
|
Labels: Most are for user interface information and will keep the default name but a few will display information calculated by the program and will need meaningful names and the Border Style property changed to Fixed Single. Make the Captions blank. The values you see in the Date and Register Balance controls are set in the program. |
||
lblItemTotal lblAccumCount lblAccumTotal |
lblTax lblDue lblBalance |
Also: lblDate at the top |
Done with the user interface?
Are the controls all sized properly, tabs are set the way you want them, and keyboard interface configured? Good! One more change:
Select all of the labels that will be addressed by the program (the ones that DON'T use the default control names) and change the alignment property to Right Justify. Don't change them individually, that would be cheating.
Run the program: Click on Run | Start With Full Compile
Test the Tab Sequence and the Keyboard Interface one more time.
You have completed the user interface
Build the Event Handlers
The program must accept user input to process a transaction. The input is the number of items and the cost per item. When the "Enter" button is pressed, the transaction amount is calculated along with a calculated tax amount and the order total. The program can process an unlimited number of transactions. When the order is complete, the user can "Check Out", which will finish the current order by "rolling" the amount due into the register balance and then preparing for the next order by clearing the order total accumulators.
A number of Module Level variables are required to hold the accumulated totals
Dim m_intOrderItems As Integer | Accumulated Items |
Dim m_sngOrderTotal As Single | Accumulated Total |
Dim m_sngRegisterBalance As Single | Running total |
Constants are often defined as Module Level as they are easier to locate
Const m_sngTaxRate As Single = 0.085
Const m_sngStartingBalance As Single = 500
When the user clicks on the Enter button, the program must perform the simple total calculation on the converted input, and then convert the calculated to to the proper format before this result is displayed in the form.
For cmdEnter_Click()
' Local Variables
Dim intQty As Integer | Converted value from Text Box |
Dim sngCost As Single | Converted value from Text Box |
Dim sngTotal As Single | Calculated total for this transaction |
Dim sngTax As Single | Calculated total for total tax |
Dim sngDue As Single | Calculated total for this order |
' Convert input
intQty = Val(txtQty.Text)
sngCost = Val(txtCost.Text)
' Calculate transaction amount
sngTotal = intQty * sngCost
' Format the results
lblItemTotal.Caption = FormatCurrency(sngTotal, 2)
The program must also maintain some higher level totals like the number of items in the order, the tax and the order total. The result must be properly formatted for display.
' Maintain Order Totals
m_intOrderItems = m_intOrderItems + intQty
m_sngOrderTotal = m_sngOrderTotal + sngTotal
' Tax and Totals
sngTax = m_sngOrderTotal * m_sngTaxRate
' What's this ???
sngTax = sngTax * 1000 \ 10
See the rounding explanation below
sngTax = sngTax / 100
sngDue = m_sngOrderTotal + sngTax
' Format the results
lblAccumCount.Caption = FormatNumber(m_intOrderItems, 0)
lblAccumTotal.Caption = FormatCurrency(m_sngOrderTotal, 2)
lblTax.Caption = FormatCurrency(sngTax, 2)
lblDue.Caption = FormatCurrency(sngDue, 2)
When you divide a floating point number (Type Single or Double in VB), especially when you need a precision to the penny, steps must be taken to round to the penny (or hundredths). The floating point result is much more precise.
Example:
Tax on $0.50 at 8.5% is 4.25 cents or $.0425. Since you can't charge a quarter of a penny
(not yet anyway) we need to take steps to keep the accumulated totals accurate to the
penny.
From the code above:
sngTax = sngTax * 1000 \ 10 sngTax = sngTax / 100 |
.0425 * 1000 = 42.5 \ 10 = 4 (integer division) 4 / 100 = .04 or 4 cents and not 4.25 cents |
This type of manual adjustment is necessary in order to maintain accurate totals.
This completes the processing for the current item, but you must now prepare the form for the next transaction by clearing the input controls
' Clear the contents and reposition cursor
txtQty.Text = ""
txtCost.Text = ""
txtQty.SetFocus
To complete the order and "check out" the customer, that is collect the payment, use this logic:
For cmdCheckOut_Click()
Two methods of calculating the Amount Due
Dim sngDue As Single
'
' This works but rounding is not acceptable
'
' Calculate the amount due and add it to the register balance
sngDue = m_sngOrderTotal + (m_sngOrderTotal * m_sngTaxRate)
'
' This works better
'
Dim sngTax As Single
sngTax = m_sngOrderTotal * m_sngTaxRate
sngTax = sngTax * 1000 \ 10
sngTax = sngTax / 100
sngDue = m_sngOrderTotal + sngTax
m_sngRegisterBalance = m_sngRegisterBalance + sngDue
lblBalance.Caption = FormatCurrency(m_sngRegisterBalance, 2)
Use the intermediate variable and perform the rounding. Breaking up the steps often
make the process clearer.
This completes the processing for the current order, but you must now prepare the form for the next order by clearing the accumulated totals and the input controls
' Clear the order accumulators
m_intOrderItems = 0
m_sngOrderTotal = 0
' Clear the form controls
txtQty.Text = ""
txtCost.Text = ""
txtQty.SetFocus
lblAccumCount.Caption = ""
lblAccumTotal.Caption = ""
lblItemTotal.Caption = ""
lblTax.Caption = ""
lblDue.Caption = ""
You are ready to process the next customer and a brand new order, so all the form controls are clear except for the drawer total.
Test the program using test data for which you know what the results should be.
Just because you get results does not mean that the results are correct
One piece of the problem is still missing.
There are three labels that contain values and at this point there is no explanation on how this information was placed on the form. In general, this work would be performed as part of an initialization sequence when the program starts. VB is no different and provides an event to perform this step. This initialization event takes place when the program (or more specifically the form) is loaded. The event is Form_Load
Display the date and time by using VB's Date and Time references. There are a number of format options but this one uses the General format. Use VB's format functions and the defined constants to display the Sales Tax rate and Drawer Totals.
For Form_Load()
' Some initialization
' This could be added to cmd_Enter_Click to
' update the time after every transaction
lblDate = FormatDateTime(Date, vbGeneralDate) & " - " & _
FormatDateTime(Time, vbGeneralDate)
m_sngRegisterBalance = m_sngStartingBalance
lblTaxRate.Caption = FormatPercent(m_sngTaxRate, 2)
lblBalance.Caption = FormatCurrency(m_sngRegisterBalance, 2)
VB will initialize all numeric variables to zero, but it's a good programming practice to initialize the Module Level accumulators as part of this event.
Finish up the testing and be sure to save the project.
A couple of thoughts before this example is complete.
Accumulating totals correctly and eliminating the errors related to rounding will give your program a bit more credibility that if it appears to behave inconsistently because totals are off by a penny.
Remember the scope or the lifespan of the variables you define. A local variable is only "alive" for the duration of the event handler in which is is defined. Variables that maintain counts and totals must be Module Level Variables in order to survive outside of the event handlers.
If you need to perform any initialization of variables, this should be done in the Form Load event.