This is the "largest" VBA project I've worked on, but perhaps not the most complicated. The meat and potatoes of it basically copies and pastes data from 2 source tabs into 1. A large piece of the code was broken out and refactored into sub procedures. Variables such as "DEM_Name" are dummy names. The main sub is called GenerateLFF
. I welcome any feedback where possible but I'm looking for the following:
- Performance: The performance was greatly improved by using the
performance_Opt
sub procedure. Even though I haven't tested it with a large data set, it takes 5-6 seconds to work with 5 records. - Improving the
copy_paste
sub procedure. This is the sub referenced the most, and I'm wondering if there is any other way of writing it to improve performance. - Sub procedures: Initially, I would never break out VBA into sub procedures. Now, I might be over doing it. Does breaking out code into may subs hurt performance?
Public Const DEM_WS As String = "DEM_NAME"
Public Const PTI_WS As String = "PTI_NAME"
Public Const LFF_WS As String = "LFF_NAME"
Public DEM_ERow As Integer
Sub GenerateLFF()
'last row of DEM
DEM_ERow = Sheets(DEM_WS).Range("C4", Sheets(DEM_WS).Range("C4").End(xlDown)).Rows.Count + 3
performance_Opt True
Call format_data_tabs
Call copy_data
Call COPY_CONST
Call CO_BUYER_CONST
Call Edge_Cases
Call Formatting
Call Clear_Contents
performance_Opt False
End Sub
Sub performance_Opt(TurnOn As Boolean)
With Application
.Calculation = IIf(TurnOn, xlCalculationManual, xlCalculationAutomatic)
.ScreenUpdating = Not TurnOn
.DisplayStatusBar = Not TurnOn
.EnableEvents = Not TurnOn
End With
ActiveSheet.DisplayPageBreaks = Not TurnOn
End Sub
Private Function Header_Verification() As Boolean
'Set Wrong as string to contain all wrong columns
Dim Wrong As String
'This is a check to ensure the column headers where we are copying data from are what we mapped them to be
Wrong = ""
Set wsDEM = Sheets("DEM_NAME")
Set wsPTI = Sheets("PTI_NAME")
'*** header verification
If wsDEM.Range("E3") <> "CSC Account Number" Then Wrong = Wrong & "CSC Account Number: DEM E" & vbCrLf
If wsDEM.Range("F3") <> "Last Name" Then Wrong = Wrong & "Last Name: DEM F" & vbCrLf
If wsDEM.Range("G3") <> "First Name" Then Wrong = Wrong & "First Name: DEM G" & vbCrLf
If wsDEM.Range("H3") <> "Loan Date" Then Wrong = Wrong & "Loan Date" & vbCrLf
If wsDEM.Range("K3") <> "APR" Then Wrong = Wrong & "APR: DEM K" & vbCrLf
If wsDEM.Range("M3") <> "Amt Financed" Then Wrong = Wrong & "Amt Financed: DEM M" & vbCrLf
If wsDEM.Range("Q3") <> "Number of Payments" Then Wrong = Wrong & "Number of Payments: DEM Q" & vbCrLf
If wsDEM.Range("R3") <> "Freq" Then Wrong = Wrong & "Freq: DEM R" & vbCrLf
If wsDEM.Range("AL3") <> "Vehicle Year" Then Wrong = Wrong & "Vehicle Year: DEM AL" & vbCrLf
If wsDEM.Range("AM3") <> "Make" Then Wrong = Wrong & "Make: DEM AM" & vbCrLf
If wsDEM.Range("AN3") <> "Model" Then Wrong = Wrong & "Model: DEM AN" & vbCrLf
If wsDEM.Range("AO3") <> "Vin" Then Wrong = Wrong & "Vin: DEM AO" & vbCrLf
If wsDEM.Range("BE3") <> "Co- buyer First Name" Then Wrong = Wrong & "Co- buyer First Name: DEM BC" & vbCrLf
If wsDEM.Range("BF3") <> "Co Buyer Last Name" Then Wrong = Wrong & "Co Buyer Last Name: DEM BD" & vbCrLf
If wsDEM.Range("BG3") <> "CoBuyer Address" Then Wrong = Wrong & "CoBuyer Address: DEM BE" & vbCrLf
If wsDEM.Range("BH3") <> "Co Buyer City" Then Wrong = Wrong & "Co Buyer City" & vbCrLf
If wsDEM.Range("BI3") <> "Co Buyer State" Then Wrong = Wrong & "Co Buyer State: DEM BG" & vbCrLf
If wsDEM.Range("BJ3") <> "Co Buyer Zip" Then Wrong = Wrong & "Co Buyer Zip: DEM BH" & vbCrLf
If wsDEM.Range("BK3") <> "Co Buyer Social" Then Wrong = Wrong & "Co Buyer Social: DEM BI" & vbCrLf
If wsDEM.Range("BL3") <> "Co Buyer DOB" Then Wrong = Wrong & "Co Buyer DOB: DEM BJ" & vbCrLf
If wsDEM.Range("BM3") <> "Schedule 1 PMT Freq" Then Wrong = Wrong & "Schedule 1 PMT Freq: DEM BM" & vbCrLf
If wsDEM.Range("BN3") <> "Schedule 1 Number of PMT's" Then Wrong = Wrong & "Schedule 1 Number of PMT's: DEM BN" & vbCrLf
If wsDEM.Range("BO3") <> "Schedule 1 PMT Amount" Then Wrong = Wrong & "Schedule 1 PMT Amount: DEM BO" & vbCrLf
If wsDEM.Range("BP3") <> "Schedule 1 PMT Start Date" Then Wrong = Wrong & "Schedule 1 PMT Start date: DEM BP" & vbCrLf
If wsDEM.Range("BQ3") <> "Schedule 2 PMT Freq" Then Wrong = Wrong & "Schedule 2 PMT Freq: DEM BQ" & vbCrLf
If wsDEM.Range("BR3") <> "Schedule 2 Number of PMT's" Then Wrong = Wrong & "Schedule 2 Number of PMT's: DEM BR" & vbCrLf
If wsDEM.Range("BS3") <> "Schedule 2 PMT Amount" Then Wrong = Wrong & "Schedule 2 PMT Amount: DEM BS" & vbCrLf
If wsDEM.Range("BT3") <> "Schedule 2 PMT Start Date" Then Wrong = Wrong & "Schedule 2 PMT Start date: DEM BT" & vbCrLf
If wsDEM.Range("BV3") <> "Schedule 3 PMT Start Date" Then Wrong = Wrong & "Schedule 3 PMT Start date: DEM BV" & vbCrLf
'*** header verification ***
If wsPTI.Range("BQ1") <> "Cus Address" Then Wrong = Wrong & "Cus Address: PTI BQ" & vbCrLf
If wsPTI.Range("BR1") <> "City" Then Wrong = Wrong & "City: PTI BR" & vbCrLf
If wsPTI.Range("BS1") <> "State" Then Wrong = Wrong & "State: PTI BS" & vbCrLf
If wsPTI.Range("BT1") <> "Zip Code" Then Wrong = Wrong & "Zip Code: PTI BT" & vbCrLf
If wsPTI.Range("BU1") <> "Phone (Home)" Then Wrong = Wrong & "Phone (Home): PTI BU" & vbCrLf
If wsPTI.Range("BV1") <> "Phone (Cell)" Then Wrong = Wrong & "Phone (Cell): PTI BV" & vbCrLf
If wsPTI.Range("CD1") <> "Employer Phone" Then Wrong = Wrong & "Employer Phone: PTI CD" & vbCrLf
If wsPTI.Range("CF1") <> "Social Security #" Then Wrong = Wrong & "Social Security #: PTI CF" & vbCrLf
If wsPTI.Range("CG1") <> "Date of Birth" Then Wrong = Wrong & "Date of Birth: PTI CG" & vbCrLf
If Wrong <> "" Then
MsgBox "Please check the following columns:" & vbCrLf & Wrong
Header_Verification = False
Else
Header_Verification = True
End If
End Function
Private Sub Filter_Blank(ByVal sheetName As String, ByVal filter_Range As String, ByVal FilterIndex As Integer)
'*** Make sure that the column contains only blanks and loan records, nothing else
Sheets(sheetName).Select
Sheets(sheetName).Range(filter_Range, Range(filter_Range).End(xlToRight)).Select
Selection.AutoFilter Field:=FilterIndex, Criteria1:="<>"
End Sub
Private Sub format_data_tabs()
If Sheets(PTI_WS).Visible <> xlSheetVisible Then Sheets(PTI_WS).Visible = xlSheetVisible
'Insert Loan Feed File tab
Sheets.Add
ActiveSheet.Name = LFF_WS
Call Filter_Blank(DEM_WS, "A3", 13)
Call sort_Asc(DEM_WS, "D3")
Call Filter_Blank(PTI_WS, "B1", 10)
Call sort_Asc(PTI_WS, "B1")
If Header_Verification = False Then End
Call match_hide_records(DEM_NAME, "B2", PTI_NAME, "D4")
End Sub
Private Sub sort_Asc(ByVal sheetName As String, ByVal filter_Range As String)
Dim target_range As Range
Set target_range = Range(filter_Range, Range(filter_Range).End(xlDown))
Sheets(sheetName).Select
With Sheets(sheetName).AutoFilter.Sort
.SortFields.Clear
.SortFields.Add Key:=target_range, Order:=xlAscending
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
End Sub
Private Sub match_hide_records(ByVal broad_list_wbName As String, ByVal broad_list_cell As String, ByVal narrow_list_wbName As String, ByVal narrow_list_cell As String)
Dim broadList As Range
Dim narrowList As Range
Dim broad_wbName As Worksheet
Dim narrow_wbName As Worksheet
Set broad_wbName = Sheets(broad_list_wbName)
Set narrow_wbName = Sheets(narrow_list_wbName)
broad_wbName.Activate
Set broadList = broad_wbName.Range(broad_list_cell, Range(broad_list_cell).End(xlDown))
narrow_wbName.Activate
Set narrowList = narrow_wbName.Range(narrow_list_cell, Range(narrow_list_cell).End(xlDown))
'match and hide
For Each cell In broadList
cell_match = Application.Match(cell.Value, narrowList, 0)
If IsNumeric(cell_match) = False Then
cell.EntireRow.Hidden = True
End If
Next cell
End Sub
Private Sub copy_data()
Call copy_paste(DEM_WS, "E4", "A2")
Call copy_paste(DEM_WS, "F4", "B2")
Call copy_paste(DEM_WS, "G4", "C2")
Call copy_paste(PTI_WS, "BQ2", "F2")
Call copy_paste(PTI_WS, "BR2", "G2")
Call copy_paste(PTI_WS, "BS2", "H2")
Call copy_paste(PTI_WS, "BT2", "I2")
Call copy_paste(PTI_WS, "CG2", "P2")
Call copy_paste(PTI_WS, "CG2", "BN2")
Call copy_paste(DEM_WS, "BL4", "V2")
Call copy_paste(DEM_WS, "BJ4", "AC2")
Call copy_paste(DEM_WS, "BH4", "AE2")
Call copy_paste(DEM_WS, "BE4", "AH2")
Call copy_paste(DEM_WS, "BF4", "AK2")
Call copy_paste(DEM_WS, "BI4", "AN2")
Call copy_paste(DEM_WS, "J4", "FK2")
Call copy_paste(DEM_WS, "AO4", "FN2")
Call copy_paste(DEM_WS, "BP4", "EX2")
Call copy_paste(DEM_WS, "BT4", "EY2")
Call copy_paste(DEM_WS, "BV4", "EZ2")
Call copy_paste(DEM_WS, "BO4", "DZ2")
Call copy_paste(DEM_WS, "BS4", "EA2")
Call copy_paste(DEM_WS, "BU4", "EB2")
Call copy_paste(DEM_WS, "BN4", "EH2")
Call copy_paste(DEM_WS, "BR4", "EI2")
Call copy_paste(DEM_WS, "AN4", "DB2")
Call copy_paste(DEM_WS, "H4", "AW2")
Call copy_paste(DEM_WS, "H4", "DN2")
Call copy_paste(DEM_WS, "M4", "DY2")
Call copy_paste(PTI_WS, "CD2", "Q2")
Call copy_paste(PTI_WS, "BW2", "S2")
Call copy_paste(PTI_WS, "BU2", "BB2")
Call copy_paste(DEM_WS, "K4", "BK2")
Call copy_paste(DEM_WS, "AM4", "CY2")
Call copy_paste(DEM_WS, "AL4", "DC2")
Call copy_paste(DEM_WS, "T4", "BE2")
Call copy_paste(DEM_WS, "R4", "DO2")
Call copy_paste(LFF_WS, "BK2", "FF2")
Call copy_paste(LFF_WS, "DV2", "EP2")
Call copy_paste(LFF_WS, "DV2", "EQ2")
Call copy_paste(DEM_WS, "R4", "CL2")
Call copy_paste(DEM_WS, "T4", "FI2")
Call copy_paste(DEM_WS, "Q4", "FJ2")
Call copy_paste(DEM_WS, "L4", "FS2")
Call copy_paste(DEM_WS, "R4", "DV2")
End Sub
Private Sub copy_paste(ByVal src_sheetname As String, ByVal src_cell As String, ByVal dst_cell As String)
Dim dst_sheetName As Worksheet
Set dst_sheetName = Sheets("Loan Feed File Output")
Dim src_column As String
src_column = Left(src_cell, Len(src_cell) - 1)
Dim target_range As String
target_range = src_cell & ":" & src_column & DEM_ERow
Sheets(src_sheetname).Range(target_range).Copy
dst_sheetName.Range(dst_cell).PasteSpecial Paste:=xlPasteValues
End Sub
Private Sub COPY_CONST()
Call CONST_DATA("J", "US")
Call CONST_DATA("M", "'000000000")
Call CONST_DATA("N", "'0")
Call CONST_DATA("T", "'0")
Call CONST_DATA("U", "'0000000.00")
Call CONST_DATA("AR", "'0000000.00")
Call CONST_DATA("AS", "'0000000.00")
Call CONST_DATA("AT", "'0000000.00")
Call CONST_DATA("AV", "'.00000")
Call CONST_DATA("AZ", "'001")
Call CONST_DATA("BC", "'0000000.00")
Call CONST_DATA("BH", "'000000000.00")
Call CONST_DATA("BI", "'0")
Call CONST_DATA("BJ", "'.00000")
Call CONST_DATA("BO", "'00")
Call CONST_DATA("BP", "'0000000000000")
Call CONST_DATA("BQ", "'0000000000000")
Call CONST_DATA("BV", "'0.0000")
Call CONST_DATA("CA", "'0000000.00")
Call CONST_DATA("CB", "'0000000.00")
Call CONST_DATA("CC", "'0000000.00")
Call CONST_DATA("CE", "A")
Call CONST_DATA("CF", "'1")
Call CONST_DATA("CH", "'0000000.00000000")
Call CONST_DATA("CI", "'.00000")
Call CONST_DATA("CJ", "'000001")
Call CONST_DATA("DD", "'000")
Call CONST_DATA("DK", "'000")
Call CONST_DATA("DA", "'3")
Call CONST_DATA("FG", "8880")
Call CONST_DATA("DI", "U")
Call CONST_DATA("BF", Sheets("Home").Range("B6").Value)
End Sub
Private Sub CONST_DATA(ByVal dst_col As String, ByVal str_value As String)
Dim target_range As String
target_range = dst_col & "2:" & dst_col & (DEM_ERow - 2)
Sheets(LFF_WS).Range(target_range).Value = str_value
End Sub
Private Sub CO_BUYER_CONST()
Sheets(LFF_WS).Select
Dim co_buyer_range As Range
Set co_buyer_range = Sheets(LFF_WS).Range("AH2:AH" & DEM_ERow)
For Each cell In co_buyer_range
If IsEmpty(cell) = False Then
Range("AG" & cell.Row).Value = "US" 'CO_BUYER_COUNTRY
Range("AO" & cell.Row).Value = "'0001" 'COLL
Range("AP" & cell.Row).Value = "'0" 'COMAKER_CE
End If
Next cell
End Sub
Private Sub CSC_AccountNubmer_EdgeCase(ByVal strRange As String)
'Returns 7 digits from the CSC Account number
Dim CSC_Range As Range
Set CSC_Range = Sheets(LFF_WS).Range(strRange & "2:" & strRange & DEM_ERow - 2)
For Each cell In CSC_Range
cell.Value = Mid(cell.Value, 7, 7)
Next cell
End Sub
Private Sub LateChgCode_EdgeCase(ByVal strRange As String)
'Retrieves the state code from the state code list located in tab "Raw 1"
Dim State_Code_Range As Range
Set State_Code_Range = Sheets(LFF_WS).Range(strRange & "2:" & strRange & DEM_ERow - 2)
For Each cell In State_Code_Range
cell.Value = Application.Index(Sheets("Raw 1").Range("Y5:AE56"), Application.Match(cell.Value, Sheets("Raw 1").Range("Y5:Y56"), 0), 7)
Next cell
End Sub
Private Sub Percent_EdgeCase(ByVal strRange As String)
'Checks APR formatting and if not decimal, decimal
Dim APR_Range As Range
Set APR_Range = Sheets(LFF_WS).Range(strRange & "2:" & strRange & DEM_ERow - 2)
For Each cell In APR_Range
If cell.Value < 1 Then cell.Value = cell.Value * 100
cell.NumberFormat = "0.00"
Next cell
End Sub
Private Sub Make_Len_EdgeCase(ByVal strRange As String)
'Returns 8 characters if the Make is greater than 8 characters
Dim Make_Range As Range
Set Make_Range = Sheets(LFF_WS).Range(strRange & "2:" & strRange & DEM_ERow - 2)
For Each cell In Make_Range
If Len(cell.Value) > 8 Then cell.Value = Left(cell.Value, 8)
Next cell
End Sub
Private Sub Year_Len_EdgeCase(ByVal strRange As String)
'Returns the year as YY if the format is YYYY
Dim Year_Range As Range
Set Year_Range = Sheets(LFF_WS).Range(strRange & "2:" & strRange & DEM_ERow - 2)
For Each cell In Year_Range
cell.NumberFormat = "@"
If Len(cell.Value) > 2 Then cell.Value = Right(cell.Value, 2)
Next cell
End Sub
Private Sub Notice_EdgeCase(ByVal strRange As String)
'Returns the correct type based on the value in column DO
Dim Notice_Range As Range
Set Notice_Range = Sheets(LFF_WS).Range(strRange & "2:" & strRange & DEM_ERow - 2)
For Each cell In Notice_Range
If cell.Value = 12 Then
cell.Value = "D"
Else:
cell.Value = "'2"
End If
Next cell
End Sub
Private Sub Freq_Code_EdgeCase(ByVal strRange As String)
Dim Freq As Integer
Dim Freq_Code_Range As Range
Set Freq_Code_Range = Sheets(LFF_WS).Range(strRange & "2:" & strRange & DEM_ERow - 2)
For Each cell In Freq_Code_Range
Freq = cell.Value
Select Case Freq
Case Is = 12
cell.Value = ""
Case Is = 24
cell.Value = "PFR2"
Case Is = 26
cell.Value = "PFR8"
Case Else
cell.Value = "PFR1"
End Select
Next cell
End Sub
Private Sub PMT_Freq_EdgeCase(ByVal strRange As String)
Dim PMT_Freq As Variant 'integer or string
Dim PMT_Freq_Range As Range
Set PMT_Freq_Range = Sheets(LFF_WS).Range(strRange & "2:" & strRange & DEM_ERow - 2)
For Each cell In PMT_Freq_Range
PMT_Freq = cell.Value
Select Case PMT_Freq
Case Is = ""
cell.Value = ""
Case Is = 12
cell.Value = "'3"
Case Is = 24
cell.Value = "'2"
Case Is = 26
cell.Value = "'8"
Case Else
cell.Value = "'1"
End Select
Next cell
End Sub
Private Sub State_Code_EdgeCase(ByVal strRange As String)
'Retrieves the state code from the state code list located in tab "Raw 1"
Dim State_Code_Range As Range
Set State_Code_Range = Sheets(LFF_WS).Range(strRange & "2:" & strRange & DEM_ERow - 2)
For Each cell In State_Code_Range
cell.Value = Application.Index(Sheets("Raw 1").Range("Y5:AD56"), Application.Match(cell.Value, Sheets("Raw 1").Range("Y5:Y56"), 0), 6)
Next cell
End Sub
Private Sub LR_IntDiscount_EdgeCase(ByVal strRange As String)
' there has to be a value to compare the loans to determine if we need this value or not
Dim LR_IntDiscount_Range As Range
Set LR_IntDiscount_Range = Sheets(LFF_WS).Range(strRange & "2:" & strRange & DEM_ERow)
For Each cell In LR_IntDiscount_Range
Select Case cell.Value
Case Is = ""
cell.Value = ""
Case Is = 3
cell.Value = ""
End Select
Next cell
End Sub
Private Sub SSN_EdgeCase(ByVal src_sheetname As String, ByVal src_cell As String, ByVal dst_cell As String)
'copies and values and formatting of SSNs
Dim dst_sheetName As Worksheet
Set dst_sheetName = Sheets("Loan Feed File Output")
Dim src_column As String
src_column = Left(src_cell, Len(src_cell) - 1)
Dim target_range As String
target_range = src_cell & ":" & src_column & DEM_ERow
Sheets(src_sheetname).Range(target_range).Copy
dst_sheetName.Range(dst_cell).PasteSpecial Paste:=xlPasteAllUsingSourceTheme
'returns dummy SSN if less than 9 chars
Dim dst_range As String
dst_range = dst_cell & "2:" & dst_cell & DEM_ERow - 2
For Each cell In Sheets(LFF_WS).Range(dst_range)
If Len(cell.Value) > 1 And Len(cell.Value) < 9 Then cell.Value = "999999999"
Next cell
End Sub
Private Sub Integer_Extraction(ByVal col_Alpha As String)
'Extracts the integers from phone numbers and SSN
Dim strRange As String
strRange = col_Alpha & "2:" & col_Alpha & DEM_ERow
Dim target_range As Range
Set target_range = Sheets(LFF_WS).Range(strRange)
Dim strInt As String
For Each cell In target_range
strInt = ""
For i = 1 To Len(cell.Value)
If Mid(cell.Value, i, 1) >= "0" And Mid(cell.Value, i, 1) <= "9" Then
strInt = strInt + Mid(cell.Value, i, 1)
End If
Next i
cell.Value = strInt
Next cell
End Sub
Private Sub date_format(ByVal col_Alpha As String)
'returns int date as date
Dim strRange As String
strRange = col_Alpha & "2:" & col_Alpha & DEM_ERow
Dim target_range As Range
Set target_range = Sheets(LFF_WS).Range(strRange)
target_range.NumberFormat = "m/d/yyyy"
End Sub
Private Sub Clear_Contents()
Dim target_range As Range
Set target_range = Sheets(LFF_WS).Range("A2:HD" & DEM_ERow - 2)
For Each cell In target_range
If cell.Value = "" Then cell.ClearContents
Next cell
End Sub
Private Sub Edge_Cases()
Call CSC_AccountNubmer_EdgeCase("A")
Call Percent_EdgeCase("FF")
Call Percent_EdgeCase("BK")
Call Year_Len_EdgeCase("DC")
Call Notice_EdgeCase("DO")
Call Freq_Code_EdgeCase("CL")
Call PMT_Freq_EdgeCase("DV")
Call State_Code_EdgeCase("FI")
Call Make_Len_EdgeCase("CY")
Call LateChgCode_EdgeCase("BE")
Call SSN_EdgeCase(PTI_WS, "CF2", "FH2")
Call SSN_EdgeCase(DEM_WS, "BK4", "AB2")
End Sub
Private Sub Formatting()
Call date_format("P")
Call date_format("AW")
Call date_format("BN")
Call date_format("DN")
Call date_format("EX")
Call date_format("V")
Call Integer_Extraction("FH")
Call Integer_Extraction("BB")
Call Integer_Extraction("Q")
End Sub
wsDEM
andwsPTI
aren't declared. The call tomatch_hide_records
doesn't include quotes aroundDEM_NAME
orPTI_NAME
. Insidematch_hide_records
the variablecell
isn't declared nor iscell_match
. Looks likecell
is never declared in any of theFor Each
loops. If you can provide code that's compilable withOption Explicit
Debug>Compile without gettingVariable not defined
I'll take a look at it again. \$\endgroup\$