Generate invoices in a flash with Excel VBA Invoice … invoices in a flash with Excel VBA Invoice...
Transcript of Generate invoices in a flash with Excel VBA Invoice … invoices in a flash with Excel VBA Invoice...
Generate invoices in a flash with Excel VBA Invoice Generator
This is a Microsoft Excel VBA Invoice project that develops an Invoice Generator that is free to use.
The data is stored in 2 sheets (databases) and filtered to your criteria. The purpose of this project is to
help with your VBA and general Excel skills in basic application development. The template is for
Excel 2010 however this application will run fine if created in Microsoft Excel 2003 /2007.
I will be adding the videos in stages. First look at the Overview video to see if this is something that you may be interested
in. Please feel free to contact me if you have any suggestions or problems through the comments at the end of this blog or
with the contact form in this website.
Part of the data filter system incorporated in the application.
Features of the Excel VBA Invoice Generator
Generates consecutive invoices
Stores all items purchases
Stores all invoice totals
Filters by date
Filters by Customer
Filters by Invoice number
Filters by product category
Shows outstanding amounts
The most important feature is the free template and instructions on how to create the
application.
What you will learn
How to record macros
Using the VBA Editor
VBA variables
Clearing multiple ranges
Moving data without selecting the data
Running macros from buttons
Adding error handling to a procedure
Protecting your workbook with code
Hyperlinking between work sheets
Dynamic named ranges
Advanced filters with multiple variable criteria
Data validation
Cascading data validation
Vlookup formulas
Dealing with the N/A error in Vlookup formulas
Visual Basic for Applications and Excel Formulas
If you want to disable the splash screen temporarily then follow the instructions in this
image.
Double click image to view in larger lightbox
Part 1: Here is the overview video
Watch this first to decide if you want to build the application
http://www.youtube.com/watch?v=wM2z3VOS9CE
This application has been designed by Trevor Easton for training purposes. You are able to use this
for your personal use. The application as is or modified in not permitted for sale in any form. No
warranties are implied or given with this application.
Part 2: In this tutorial I discuss the template and how it is set up.
The invoice being created and sent with 2 verification messages
The steps for modifying the basic template are here. I will show you how the hyperlinks for the
navigation are arranged and why.
If you wish to change the spin button range on the invoice sheet then this is the video to watch.
You may want to watch this tutorial on named ranges. Static Named Ranges
http://www.youtube.com/watch?v=7EIR8IncBFw
You may want to watch this tutorial on dynamic named ranges. Dynamic Named Ranges
To create a named range simply highlight the range or cell and then click in the name box just above
column A. Type the name with no spaces and hit enter.
Here are the static named ranges that I have put into the template that you have downloaded
Accounts =Invoice!$G$9:$N$9
Clients =Customers!$A$1
Interface =Interface!$A$1
MakeInvoice =Invoice!$A$1
Products =Products!$A$1
Summary ='Invoice Summary'!$A$1
TotalSales =Accounts!$A$1
Generate invoices in a flash with Excel VBA Invoice Generator:
Part 3
Part 3: In this tutorial all the dynamic named ranges are added.
http://www.youtube.com/watch?v=N1vGjOsmgV8
Now we have a bit of work with dynamic named ranges for our Invoice application.
What is a dynamic named range and how does it work?
Simply put we want to be able to change things on the fly and still have a program work just as we
want. So how are we going to do that?
Now you might be thinking that’s too complicated for me. Can I just reassure you that I have broken
this down into simple steps that should reduce the degree of difficulty? At any rate why not give it a
try?
Dynamic named ranges are essentially built on the offset function. As you can see from the illustration
below the offset function has five arguments. Let’s discuss them one at a time.
1. The first is a cell reference that is the starting cell for the range
2. The second argument is the number of rows you wish to offset. By offset we mean just simply
move to. For example if we wanted to start our range at F16 then the row number would be 1 if
we wish to start a range at F14 then the road number would be -1.
3. The third argument is the same as the second except that it refers to columns, if we wish to
move one column to the right we would have the number 1 if we wish to move to the left we
would add the number -1.
4. The fourth and fifth are optional arguments that you probably don’t use regularly when using
the offset function but it is these two optional arguments that make add dynamic named range
possible because we will be replacing them with a formula that counts the number in a range. In
the offset function if we wanted to refer to 15 rows then we would add the number 15.
5. The fifth argument is the same as the fourth except that it refers to columns.
What will make this offset formula dynamic is for us to put it into the Name Manager and create a
named range with the offset formula. For our fourth argument where we see the row reference we will
replace it with a COUNT or COUNTA function.
If we are referring to text then we would use the COUNTA function.
When we are referring to numbers we would use the COUNT function.
Have a look at the illustration below and you will see that we have replaced the row height with a
COUNTA function. Simply put, instead of giving an absolute reference here we are allowing the
formula to count how many rows have data in that range and then adding that result to our formula.
Pretty cool, don’t you think.
Here are the dynamic named ranges that need to be added for this application.
Category =OFFSET(Products!$L$6,,,COUNTA(Products!$L:$L))
Category_Full =OFFSET(Products!$D$6,,,COUNTA(Products!$D:$D))
Costumers =OFFSET(Customers!$C$6,,,COUNTA(Customers!$C:$C),6)
Customer_Key =OFFSET(Customers!$C$6,,,COUNTA(Customers!$C:$C))
Invoice_range =OFFSET('Invoice Summary'!$F$7,,,COUNT('Invoice Summary'!$F:$F),11)
Items =OFFSET(Products!$E$6,,,COUNTA(Products!$E:$E),3)
Invoice =OFFSET(Invoice!$G$19,,-3,COUNTA(Invoice!$G$19:$G$49),11)
To get to the name manager go to the formula tab on the ribbon click name manager click new. After
you type in the name that you want as the reference for this new named range and then add the formula
in the box below.
I could have just told you the formula to put in the named range box but I thought it would be far more
valuable if you could see how dynamic named ranges are created and then when you’re working on the
fly in your office you will be able to create dynamic named ranges on the run. Take the time to
experiment with all the arguments and till you are proficient in using dynamic named ranges.
Remember the goal is to learn from this office tutorial.
I hope this is help you understand how a dynamic named range is formed so that you will be able to not
just copy them but create them at the coal face.
Cascading data validation
I would like to say that this is simple but it is not. This formula will take a bit of getting your head
around. I will try to explain it the best I can.
It is important that you sort the Items list before adding the validation. This step was
omitted in the video tutorial.
Here is the formula
Please note if you are using a version prior to 2010 you will need to change Products!$E$5 in the
following formulas to a named range.
=OFFSET(Products!$E$5,MATCH($H19,Category_Full,0),0,COUNTIF(Category_Full,$H19),1)
Here is how it works. Look at the breakdown of the offset function.
OFFSET(reference,rows,cols,[Height],[Width])
Reference = is the first value in the dynamic named range which is E6 on the Products worksheet.
=OFFSET(Products!$E$5,
Row = Is the MATCH function matching the value in the from the dropdown box on the invoice sheet
to the dynamic named range Category_Full
=OFFSET(Products!$E$5,MATCH($H19,Category_Full,
Cols = 0. We are not referencing cols here.
[Height] = This is a row reference to COUNTIF the dynamic named range Catergory _Full has the
value has the value from the dropdown box on the invoice sheet.
=OFFSET(Products!$E$5,MATCH($H19,Category_Full,0),0,COUNTIF(Category_Full,$H19)
[Width] = This is a column reference that moves 1 column to the right.
=OFFSET(Products!$E$5,MATCH($H19,Category_Full,0),0,COUNTIF(Category_Full,$H19),1)
In a nutshell
Our dynamic range for the data validation starts on the Products sheet at E5. We find a ( MATCH)
matches for the Category value on the product sheet dynamic range Category_Full then we do a
count(COUNTIF) for the same range and move one column to the right to select the values.
This is how the cascading validation should look
Click the images to show larger view in a lightbox
Generate invoices in a flash with Excel VBA Invoice Generator:
Part 4
Part 4: In go the formulas.
http://www.youtube.com/watch?v=mVPkulUsRjc
Formulas for the invoice sheet
What is the Excel Vlookup function? The Vlookup function looks up data in a list based on a criteria or
value that you specify. And then give you an adjacent value to the lookup value. The Vlookup function
always references the column on the left. The lookup value that you use must always be located in this
column.
1. The criteria or “ look up value” in this instance it is $I19.
2. The second argument is the data array. In this instance we are using the dynamic named range
“Items”.
3. The third is the column that you want the information to come from in this instance it is column
2.
4. The fourth argument is whether you want an exact match or an approximate match. This
argument can be omitted or you can use False or 0 the exact match or True or 1 for an
approximate match.
Code =IF(ISNA(VLOOKUP($I19,Items,2,0)),"",VLOOKUP($I19,Items,2,0))
Unit Price =IF(ISNA(VLOOKUP($I19,Items,2,0)),"",VLOOKUP($I19,Items,2,0))
Use the IF function to remove “#Value” errors as shown below.
Here is how it works
=IF(logical_test,[value_if_true],[value_if_false])
Total =IF(L19="","",IF(M19="",L19,L19-(L19*M19)))
Amount =IF(K19="","",G19*K19)
Here is the breakdown of the formula
Total =IF(L19="","",IF(M19="",L19,L19-(L19*M19)))
=IF(logical_test,[value_if_true],[value_if_false])
Use the Subtotal function to total the values
This is a fantastic function that lets you choose from multiple functions as you can see from this
illustration.
SubTotal =SUBTOTAL(9,Invoice!L$19:L$49)
Generate invoices in a flash with Excel VBA Invoice Generator:
Part 5
Part 5: Moving the data
In this tutorial the information from the 2 named ranges “Invoice” and “Accounts” is moved to the 2
destination sheets. The after invoice credits will also be added. So we will be adding 2 procedures
Here is the video tutorial
http://www.youtube.com/watch?v=hZuJ4lP9TOY
The breakdown of the macro is as follows. Please add this to the Copy_To module.
Before we begin we need to add a static named range called “ClearInvoice”
Here are the parameters for the static named range.
=Invoice!$G$12,Invoice!$I$12,Invoice!$N$13,Invoice!$N$15,
Invoice!$G$19:$I$49,Invoice!$M$19:$M$49,Invoice!$N$52
Moving the 2 ranges
Most of this code is just message boxes and error handling. Here the part that does the work.
[box style="note"]Set SrcRng1 = Sheet2.Range("Invoice")
SrcRng1.Copy
DstRng.End(xlDown).Offset(1, 0).PasteSpecial xlPasteValues
Set SrcRng2 = Sheet2.Range("Accounts")
SrcRng2.Copy
DstRng2.End(xlDown).Offset(1, 0).PasteSpecial xlPasteValues[/box]
It will work with just this code if you wanted it to. I have added comment to explain what each piece of
code does. The comments have a apostrophe in front of them and should appear in different colour in
the Visual Basic editor.
Comments are in red
Sub CopyCells()
'written by Trevor Easton 21/2/2013
'This code will pick up a multiple ranges and add them to 2 separate sheets without
' selecting the data
'error handler- change Scooby_Doo to whatever you want
On Error GoTo Scooby_Doo:
'unprotect all sheets
'Unprotect_All
'dim variables
Dim DstRng As Range 'destination range
Dim DstRng1 As Range 'destination range
Dim SrcRng1 As Range 'source range
Dim SrcRng2 As Range 'source range
'destination variable
Set DstRng = Sheet5.Range("e5")
Set DstRng2 = Sheet6.Range("e5")
'hold in memory
Application.ScreenUpdating = False
'mandatory fields
If Range("N13") = "" Then
MsgBox "It appears that you have forgotten to add the date"
Exit Sub
ElseIf Range("N14") = "" Then
MsgBox "The invoice number is missing"
Exit Sub
ElseIf Range("G12") = "" Then
MsgBox "Please add the company"
Exit Sub
Else
'give the user a chance to exit here
Select Case MsgBox _
("You are about to finalise this invoice." _
& vbCrLf & "Check everything before you proceed", _
vbYesNo Or vbExclamation, "Are you sure?")
Case vbYes
Case vbNo
Exit Sub
End Select
'copy and paste data without selecting
'first sheet
'source variable
Set SrcRng1 = Sheet2.Range("Invoice")
SrcRng1.Copy
DstRng.End(xlDown).Offset(1, 0).PasteSpecial xlPasteValues
'second sheet
'source variable
Set SrcRng2 = Sheet2.Range("Accounts")
SrcRng2.Copy
DstRng2.End(xlDown).Offset(1, 0).PasteSpecial xlPasteValues
Sheet2.Select
'add invoice number
Range("N14").Value = Range("N14").Value + 1
'empty clipboard
Application.CutCopyMode = False
'confirmation message
MsgBox "Your invoice has been sent to Invoice Summary" _
& vbCrLf & "and the totals have been sent to Accounts"
'clear the invoice
Clear_Invoice
' Protect_All
End If
'error handler
'exit sub on error
Exit Sub
Scooby_Doo:
'message on error
MsgBox " Opps a daisy, something has gone bottoms up"
'reprotect if error occurs
'Protect_All
End Sub
Sub Clear_Invoice()
'clean up invoice
Dim Clr As Range
'set varable for range
Set Clr = Sheet2.Range("ClearInvoice")
'can not use ClearContents because of merged cells so I have set value to ""
Clr.Value = ""
End Sub
Adding the after invoice credits
Most of this code is just message boxes and error handling. Here the part that does the work.
[box style="note"]Set DstRng3 = Sheet6.Range("e5")
Sheet1.Range("R4").Copy
DstRng3.End(xlDown).Offset(1, 0).PasteSpecial xlPasteValues
Sheet1.Range("R5").Copy
DstRng3.End(xlDown).Offset(0, 2).PasteSpecial xlPasteValues
Sheet1.Range("R6").Copy
DstRng3.End(xlDown).Offset(0, 6).PasteSpecial xlPasteValues [/box]
It will work with just this code if you wanted it to. Here is the code to copy the after invoice credits.
Sub Copy_Credit()
On Error GoTo Scooby_Doo:
Application.ScreenUpdating = False
Select Case MsgBox _
("You are about to finalise this credit." _
& vbCrLf & "Check everything before you proceed", _
vbYesNo Or vbExclamation, "Are you sure?")
Case vbYes
Case vbNo
Exit Sub
End Select
If Range("R4") = "" Then
MsgBox "It appear that you have forgotten to add the date"
Exit Sub
ElseIf Range("R5") = "" Then
MsgBox "The customer is missing"
Exit Sub
ElseIf Range("R6") = "" Then
MsgBox "Please add credit amount"
Exit Sub
Else
Set DstRng3 = Sheet6.Range("e5")
Sheet1.Range("R4").Copy
DstRng3.End(xlDown).Offset(1, 0).PasteSpecial xlPasteValues
Sheet1.Range("R5").Copy
DstRng3.End(xlDown).Offset(0, 2).PasteSpecial xlPasteValues
Sheet1.Range("R6").Copy
DstRng3.End(xlDown).Offset(0, 6).PasteSpecial xlPasteValues
Sheet1.Range("R4:R6").Value = ""
MsgBox "Your credit has been added" & vbCrLf & "filter your data to check"
Application.CutCopyMode = False
End If
Exit Sub
Scooby_Doo:
MsgBox "An error occurred in running this code"
End Sub
Generate invoices in a flash with Excel VBA Invoice Generator:
Part 6
Part 6: Filtering Items and Account
Here is the video
http://www.youtube.com/watch?v=e9uCLXiVJXw
Here is a brief breakdown of how the Advanced filter works.
Firstly, where do we locate the advanced filter tab? On the ribbon click the Data tab and then click
Advanced. See illustration below
Dialog box options for advanced filters
1) When you click the advanced button a dialogue box appears giving you the several options let’s
have a look at what those options are. Under the category Action you will notice 2 radio buttons. If you
wish to filter the data set itself or in place then click the top button.
2) If you wish to filter to another location whether that be on the same worksheet or and other
worksheet you would click Copy to another location. It is very important to remember that if you wish
to have the data filtered to another sheet then you must start this process from that sheet.
3) You now have three boxes
i) List range
ii) Criteria range
iii) Copy to
4) To set these three ranges you need to first click inside the box until you see your cursor inside the
box. That tells Microsoft Excel that that is the area you wish to now edit.
5) Enter the range manually or click the red arrow and scroll over the range for that particular
section.
6) If you wish only to filter unique records and this can be a very valuable asset if you are looking
for specific data in large data sets then clicks the button unique records.
The List range.
The list range must include the heading along with all of the data. There should not be any blank rows
in your data. And it is best to surround the data set with blank columns and rows so that Microsoft
Excel can recognise this as a data list.
The Criteria range.
The criteria range should include both the header and that the criteria with no spaces. It is possible to
filter with multiple criteria and with operators such as greater than >/ less than parameters for your
criteria. You can also filter between two sets of criteria such as greater than> /and less than.
Go to the VBA editor ALT+F11 and double click on the filter module then paste in the code below.
Copy and paste all 3 procedures listed below. Advanced / Advanced_Total / Clearme
Here is the part of the code that does the work.
Set area = Sheet5.Range("E6:M1000000")
area.AdvancedFilter Action:=xlFilterCopy _
, CriteriaRange:=Sheet1.Range("G5:K6"), CopyToRange:=Sheet1.Range("G8:O1000000"), _
Unique:=False
Then follow the instructions below
1. Assign the macros
Assign the macro Advanced to the Click Here to Filter button on the interface sheet
Assign the macro Clearme to the Clear Data button on the interface sheet
Right click the shape / Assign Macro / select the macro / OK
2. Add data validation
In cell I6 add add data validation list for Customer_Key
In cell J6 add add data validation list for Category
Go to Data / Data Validation/ List / put the curser in the source box and press the F3 key /select the
Named Range / OK
Understanding the filter criteria
You can use these filters singularly or combined
Filter by date The format must be mm/dd/yyyy
Filter by greater then or less then a date The format must be mm/dd/yyyy use the operators > or < eg > 3/13/2013 means dates greater
then13th march 2013
Filter by invoice Add the invoice number and if you wish use the operators > or <
Filter by Customer Select the customer from the dropdown list
Filter by category
Select the category from the dropdown list.
N.B When you add the category to the filters the accounts filters are excluded as there is not category
in the accounts database.
This is the code to copy and paste. Copy all three procedures together and paste into the filters
module..
Sub Advanced()
'unprotect all sheets
'Unprotect_All
'stop screen flicker
Application.ScreenUpdating = False
'clean up the background
With Sheet1.Range("a9:y1000").Interior
.PatternColorIndex = xlAutomatic
.ThemeColor = xlThemeColorAccent5
.TintAndShade = 0.799981688894314
End With
With Sheet1.Range("a9:y1000")
.Borders.LineStyle = xlNone
.Borders(xlEdgeRight).Weight = xlThin
.ClearContents
End With
'set variable for advanced filter
Set area = Sheet5.Range("E6:M1000000")
'run the filter
area.AdvancedFilter Action:=xlFilterCopy _
, CriteriaRange:=Sheet1.Range("G5:K6"), CopyToRange:=Sheet1.Range("G8:O1000000"), _
Unique:=False
'call the second advanced filter
Advanced_Totals
'reprotect the sheet
'Protect_All
End Sub
Sub Advanced_Totals()
'stop screen flicker
Application.ScreenUpdating = False
'set adavanced filter range
Set area2 = Sheet6.Range("E6:L1000000")
'run the filter
area2.AdvancedFilter Action:=xlFilterCopy _
, CriteriaRange:=Sheet1.Range("G5:K6"), CopyToRange:=Sheet1.Range("Q8:X1000000"), _
Unique:=False
End Sub
Sub Clearme()
'Unprotect_All
'stop screen flicker
Application.ScreenUpdating = False
'clean the sheet
With Sheet1.Range("a9:y100").Interior
.PatternColorIndex = xlAutomatic
.ThemeColor = xlThemeColorAccent5
.TintAndShade = 0.799981688894314
End With
With Sheet1.Range("a9:y100")
.Borders.LineStyle = xlNone
.Borders(xlEdgeRight).Weight = xlThin
.ClearContents
End With
'Protect_All
End Sub
Generate invoices in a flash with Excel VBA Invoice Generator:
Part 7
Part 7: Protecting the document
http://www.youtube.com/watch?v=PoUV49US0Lc
To finalise the project we have a little bit of tidying up to do and we need to protect the application.
Here is a list of things that need to be done.
Minimise the ribbon On the top right hand corner of the active window you will find an arrow that will minimize the ribbon.
Disable the sheet tabs Click on the file tab and choose Options / Advanced / Show Sheet Tabs / clear the box.
Disable the formula bar and the headings for all sheets. Go to each sheet and choose View and deselect the boxes for Headings and Formula bar.
Customize the Userform Hold down the Alt + F11 to open the VBA Editor. In the left panel double click the Userform. Select
the Userform by clicking in any blank part of it. Right click and choose Properties. From the properties
dialog box change the appropriate part to suit your needs. Click on the labels and do the same.
Change the display time of the Userform Select the Userform by clicking in any blank part of it. Right click and choose View code. You will
notice in the “for loop” this expression “100/5”. Change the 5 to the number of seconds that you want
the progress bar to run for. Go to This Workbook and follow the instructions below.
This workbook To set the Userform timer change the number of seconds to the same amount as you set the progress
bar in the Userform.
Remove the apostrophe from in front of “Protect_all” this will allow all sheets to be protected when
the workbook opens.
Protect Module Double click on the protect module to open it.
To change the password put the curser in the “Unprotect_all” procedure and press the F5 key to run the
procedure. This will unprotect all sheets. Before you proceed check that the sheets are unprotected.
Return to the 2 procedures and change “Online” to the password that you desire.
Protect shortcut key I have put in shortcuts to enable you to manually protect and unprotect all of the sheets. To protect all
ALT+SHIFT+P to unprotect all ALT+SHIFT+U.
Protect the advanced filters Double click on the filters module and in the “Advanced” procedure remove the apostrophe from in
front of “Protect_all” and “Unprotect_all” . Do not touch the other procedures.