You are on page 1of 12

MBA 614 – Final Project Report

Jeremy Heckaman
Executive Summary
Viking Pest and Plant, LLC is a pest control company that offers additional services, such as plant and
tree disease diagnosis and treatment, tree pruning and trimming and landscape consulting. The
company is owned and operated by my brother, primarily in the St. George, Utah metropolitan area. He
principally operates the business out of an office in his home, where he keeps files and customer
information, along with any other company documents and information. Customers will sign a contract
which stipulates the services requested and the frequency of such services. Based on this information,
services are scheduled and payment is collected at that time. Given the limited office space, and
proximity to small children, my brother experiences some difficulty keeping his records organized and
secured from potential destruction. In addition, scheduling out services cost effectively can be a time
consuming and error prone process. Currently he needs to sort through a stack of paper contracts and
write down a paper schedule for all customers. This process needs to be repeated periodically
consuming unnecessary labor hours. The manual process is expected to become unmanageable as his
customer base grows beyond about 250 records. Automation of these tasks will reduce labor needs and
enhance his ability to track his customer activity and service requirements.

For my project, I designed a data base in Excel, using primarily VBA forms, which will allow my brother to
input customer data, link multiple contracts to a customer record and link multiple service call histories
to those contracts. All of this information is input through user friendly forms, and can be recalled and
edited. In addition, the data base has a scheduling function that allows my brother to see upcoming
services for the next week or month. This allows for safe and secure storage of all files in a safe, central
location with easier and more accurate record changes to that information. In addition, it reduces
scheduling time and difficulties because my brother only needs to plan a few weeks in advance, instead
of planning out many months in advance or periodically reviewing all contracts. This program will also
allow my brother to accurately record and report regulatory data, e.g., pesticide usage, required by
State and Federal agencies, e.g., The EPA. Lastly, it provides a platform for additional functionality that I
plan to program in the future, further increasing its value to my brother’s company.

Implementation
Phase I – Design

To begin my project, I first had to review my brother’s business model and determine what data would
be recorded. I also had to determine what functions the program would need to support, not only for
the scope of this project, but for the future as well. Since this project will potentially store all of my

Page | 2
brother’s files in the future, it needs to be robust enough to allow for more features. I had to work with
him to define several features and then prioritize them so that the initial program would be useful and
allow addition of new features and reporting over time.

After determining that the customer information, contract information, and service call information
were most important, I designed the database, along with all the primary and foreign keys. The data
base structure is simple, with multiple service calls attached to one contract, and multiple contracts
attached to one customer. I linked the tables through a simple key system. Each customer has a unique
number. Each contract has a unique number and the associated customer number. Lastly, each service
record has a number and the associated contract number. The data base relationships are outlined
below in figure 1.

Figure 1
Relational Data Base
table diagram

Next step was designing the forms that would be necessary for data entry and editing, as well as
queries. Programming earlier assignments with one or two forms was simple. Having multiple forms
proved difficult, and I quickly found myself bogged down in determining the best way to move through
the forms and sub-forms. I found programming user friendly alerts and controls to be difficult as well,
since you can never quite know the proper balance of information. On one hand, you tell the user too
little and they do something devastating, on the other, you bombard them with information about
unlikely scenarios, and they stop reading alerts altogether.

Upon opening the file, the main menu (figure 2 below) automatically appears, and the first spreadsheet
opens as a background. The user can choose to add a new customer (figure 3), or edit a customer record
(figure 4). Upon adding a new customer, the user is prompted if they would like to add a contract to the
customer, which takes them to an add contract form (figure 5). This is different from the add contract
form accessible through the main menu (figure 6) because the user’s information is automatically
loaded into the top label. In the other form, the user must select the customer record to attach a
contract to. There is also a form for editing contracts (figure 7) which requires the user to select a
customer record first, and then select the desired contract associated with that customer. The last form

Page | 3
is the schedule form (figure 8) that allows the user to view the upcoming service dates for the next
week, or four weeks.
Figure 2
Main Menu

Figure 3
Add New Customer

Page | 4
Figure 4
Edit Customer (Existing Customer)

Figure 5
Add Contract (New Customer)

*New customer’s data


automatically loads here*

Page | 5
Figure 6
Add Contract (Existing Customer)

Figure 7
Edit Contract (Existing Contract)

Page | 6
Figure 8
Appointment Schedule

The data entered or edited is then written to the tables. The customer table (figure 9) and contract table
(figure 10) are shown below.
Figure 9
Customer Table

Figure 10
Contract Table

Phase II – Programming

Next, I started programming the forms. I found writing data from the forms to the data base is fairly
easy to understand and perform. I struggled, however, with linking the data in one table to another via
the table keys. I show the following example as demonstration of my solution. This code is used in the
edit contract form (figure 7 above). The user first selects an existing customer record in the top combo

Page | 7
box. Then this code runs, filling an array with information regarding the existing contracts associated
with the selected customer. The array is subsequently loaded into the second combo box.

Private Sub cmbSelectCust_Change()

If cmbSelectCust.Value = "" Then


ClearForm
Exit Sub
End If

'this selects the customer number from the selection on the combo box
AddCustomerNumber = cmbSelectCust.ListIndex + 1

If AddCustomerNumber < 1 Then


Exit Sub
End If

'this loads the contract array based on the customer selected


Dim R As Integer
Dim X As Integer
Dim NN As Integer
Dim conInfo()

'prevents an error in the event that there are no contracts saved


If Sheets("ContractTable").Range("a2").Value = "" Then
cmbSelectCon = "There are no contracts for any customers listed"
Exit Sub
End If
(1)
'fills the customers() array with the customer numbers in the ContractTable and the associated row number
R = Range(Sheets("ContractTable").Range("a2"), Sheets("ContractTable").Range("a2").End(xlDown)).Count

ReDim Customers(1 To R, 1 To 2)
For X = 1 To R
Customers(X, 1) = Sheets("ContractTable").Range("a" & X + 1).Value
Customers(X, 2) = X + 1
Next

NN = 1
N=0
(2)
'counts the number of customer matches
For X = 1 To R
If Customers(X, 1) = AddCustomerNumber Then
N=N+1
End If
Next
(3)
If N < 1 Then
'if there are no contracts under the customer's record
cmbSelectCon = "No contracts are saved for this customer"
cmbSelectCon.Clear
Else
cmbSelectCon = "Select a contract"
'loads the contract's row location into the contracts() array
ReDim contraCts(1 To N)
For X = 1 To R
If Customers(X, 1) = AddCustomerNumber Then
contraCts(NN) = Customers(X, 2)
NN = NN + 1
End If
Next

Page | 8
(4)
'loads the contract information into an array off of the row information
ReDim conInfo(1 To N)
For X = 1 To N
conInfo(X) = "Con Type = " & Sheets("ContractTable").Range("i" & contraCts(X)).Value & "; Serv Type = " _
& Sheets("ContractTable").Range("j" & contraCts(X)).Value & "; Frequency = " & _
Sheets("ContractTable").Range("k" & contraCts(X)).Value & "; Price = " & _
Sheets("ContractTable").Range("l" & contraCts(X)).Value & "; Initial Service = " & _
Sheets("ContractTable").Range("m" & contraCts(X)).Value
Next
(5)
'loads the info array into the combo box
cmbSelectCon.lisT = conInfo
End If

End Sub

The code is redundant in searching for matches, but effective. Basically what happens is it (1) loads the
customer key for each contract into an array, and then (2) goes through that array counting matches
with the selected customer’s key. After determining the number of matches, it redims an array, which
then (3) loads the row number of each contract with a match. Lastly, it (4) loads an array with all of the
desired contract information, which is then (5) loaded into the second combo box. The user can then
select a contract from the second combo box to modify.

I also decided that for some input fields it would be good to ensure that data was uniform and
complete. I put controls on the phone numbers, the state, and the zip code inputs. The various controls
check for proper entries (length, numbers or symbols, etc.) and then format the entry into a desired
standardized format. If an incorrect input is entered, the user is prompted to re-enter the information.
This is another area that proved to be difficult. Once you find an error you need to inform the user and
then prevent the tab from progressing so that they user remains in the current text box. This presented
a challenge as well. By using the ‘exit’ event, you can prevent the cursor from exiting the box if there is
incorrect data, but what if the customer just wants to leave the item blank (e.g., the customer doesn’t
have a cell phone)? To solve this, I simply inserted an if statement that allowed the user to exit if the
field was blank. Below is the code for the phone number and zip code checks.
Function CleanPhone(theInput As String) As String
'checks phone numbers for validity, and formats them as (###)###-#### regardless of their current format
Dim oneChar As String
Dim X As Integer
Dim phoneNumber As String

For X = 1 To Len(theInput)
oneChar = Mid(theInput, X, 1)
If IsNumeric(oneChar) Then
phoneNumber = phoneNumber & oneChar
End If
Next

If Left(phoneNumber, 1) = 1 Then
phoneNumber = Right(phoneNumber, Len(phoneNumber) - 1)
End If

If Len(phoneNumber) <> 10 Then


MsgBox ("This is not a valid phone number. Please enter a 10 digit phone number")
CleanPhone = "BAD"
Else
CleanPhone = "(" & Left(phoneNumber, 3) & ")" & Mid(phoneNumber, 4, 3) & "-" & Right(phoneNumber, 4)
End If

Page | 9
End Function

Function CheckZip(ZiP As String) As Boolean


'checks zip codes for characters other than numbers and length only...
Dim X As Integer
Dim oneChar As String
Dim N As Integer
N=0

If Len(ZiP) = 5 Then
For X = 1 To 5
oneChar = Mid(ZiP, X, 1)
If IsNumeric(oneChar) Then
N=N+1
End If
Next
If N <> 5 Then
CheckZip = False
MsgBox ("That is not a valid zip code. Please enter a five digit zip code.")
Else
CheckZip = True
End If
Else
MsgBox ("That is not a valid zip code. Please enter a five digit zip code.")
CheckZip = False
End If

End Function

One of the most difficult parts of this project was reporting the service schedule by forecasting future
service dates based on the initial service date and the service frequency listed on the contract. Contract
service frequency can be quarterly, monthly, annually, etc. To program the Appointment Scheduler, I
first needed to make a function that would estimate the next scheduled service date. This function was
fairly difficult to create and had a few bugs to work out. I needed to scan the contracts and project the
next service date using last service date, frequency and the current date. However, in the end, it is
probably my favorite piece of code that I created (shown below). The date calculation information was
very helpful in finishing this routine.

(1)
Function NextServ(InitialServe As Date, Frequency As String) As Date
'function to find the next scheduled service date

Dim NewDate As Date


(2) If Frequency = Sheets("comboarrays").Range("d2").Value Then 'every other week
(3) If Date > InitialServe Then
(5) NewDate = DateAdd("ww", 2, InitialServe)
If Date > NewDate Then
(6) Do Until NewDate > Date
NewDate = DateAdd("ww", 2, NewDate)
Loop
End If
(7) NextServ = NewDate
Else
(4) NextServ = InitialServe
End If
End If

Page | 10
I only include the code for the every other week frequency option, since the other options are the exact
same logic with a different time period. This code is (1) sent the initial service date and the frequency
from the data base record for a given contract. It then (2) takes the frequency and determines which
value it is (e.g., monthly, annually, etc). It (3) next checks to see if the initial service date has already
passed. (4)If not, the initial service date is given as the next service date. (5) If the initial service date has
already passed, however, the function adds the correct time interval to the initial service date. (6) If this
new date is still older than the current date, it continues to add the given interval until the date is past
the current date. (7) This date is then given as the next estimated service date.

This data is subsequently used to identify contracts whose service dates are coming up within the
selected time interval. This will help my brother to access upcoming service calls at the touch of a
button, instead of having to thumb through multiple paper contracts and calculate out future services.

What I Learned
This project provided many learning opportunities, both expected and unexpected, both enjoyable and
not so enjoyable. I learned that programming becomes increasingly difficult as the number of
interrelated sub procedures and functions increase. Changes in one area can have unexpected effects in
another, and are very difficult to track down.

As previously mentioned, I also learned that programming user friendliness is very difficult to do. The
user friendliness of a program seems to be inversely related to a programmer’s suffering. Features that
are attempted with good intentions seem to wreak havoc on a large program for the reasons mentioned
in the previous paragraph. In addition, some very useful items can take up large amounts of
programming and testing time, while not providing much obvious benefit in return. A significant portion
of the time I put into this project was spent trying to figure out small controls and intricate features that,
in the end, appear small and insignificant to the program functionality as a whole, but needed to be
completed.

This leads into the difficulties of testing a program as it is developed and in the final state. Many
problems continually came up as the program was completed piece by piece. Many of these problems
went unnoticed as they occurred in forms or sub procedures that I had previously tested and assumed
that they were complete. For this reason, I had to spend a lot of time going back through the database
and using the various forms in different ways to test where errors and or malfunctions could possibly
occur. To help expedite this process, I even sent my file to my dad, who is a partner in the business, so
that he could be an extra test user for the forms and program as well. I found that the user interface was
quickly created but very time consuming to test and fix, where some more complex functions, such as
search routines, were tougher to figure out but faster to test and correct.

Lastly, a technical code item I learned was how to work with dates. There were many issues with the
dates being loaded in the proper format. Some functions require that they be processed as a date, and
others can take it as a string. Sometimes I needed to use the dates as different formats, so this made it
difficult when I needed to process the dates as both a date and a string in the same procedures. I also
learned about the ‘dateadd’ function (taught to me by Professor Allen), which was critical in
programming the service Appointment Scheduler function discussed above.

Page | 11
Conclusion
In conclusion, this project was a great opportunity for learning. I had wanted to program something for
my brother for some time now, hoping to help him stay more organized and access information more
easily. This project was a perfect opportunity to do so. Through this project I was able to program a data
base that is capable of storing customer and contract records for my brother, as well as helping him
schedule out his work weeks. In addition, the foundation is set for future functionality, such as an AR
aging report, automated emailing lists, or revenue projects, among other things.

I also learned about the difficulties and potential pitfalls that increase exponentially with increased
project scope. If I were able to do the project over again, I would have spent a lot more time planning
out the structure and framework logic that the data base would run on before writing one line of code.
As it is, I spent a lot more time reworking and editing the project than a more efficient programmer
would have likely required. However, it was a great learning experience, and will help me to tackle
future projects more efficiently.

Page | 12

You might also like