Compare two columns from which one column is not mandatory - excel

I need help for a macro code.
In my case the excel macro checks data in one sheet ("Check_file") for completeness and correctness.
There are mandatory columns in the sheet which have to exist and also not-mandatory columns can exist.
In my example the columns “company” and “fee” are mandatory columns, if they are missing or false the macro will throw an error.
Next to them, the column “gross fee” is not-mandatory and its data should only be checked with the data in column “fee”, if column “gross fee” exists. If it exists, the amount should be the same as in column “fee”. If it doesnt exist, there should be no comparison.
The check for the mandatory columns works fine within a For-Loop and an own Range.
My problem is that I dont know how I can involve the not-mandatory columns into the loop of the mandatory columns…
I tried to define a separate Range for the not-mandatory columns area. But it seems that I cannnot create the connection to the not-mandatory column if it is not set in the mandatory columns loop. But if it is set to the mandatory columns range and the not-mandatory column doesnt exist, an error will be thrown.
Should the exist-check for the not-mandatory columns be placed in a separate Sub or Function? If yes, how can the connection be created to the mandatory check Range?
This is the vba code:
Function Main_Check(ByVal StrFilePath As String) As String
'//Checks all criteria for the correct filling of the template. Marks all fields that are incorrectly
filled in red.
Dim WB As Workbook, WS As Worksheet
Dim i As Long, iNotMand As Long, lEnde As Long, strHeader As String, ii As Long, lColEnde As Long
Dim rngFind As Range, booCheck As Boolean, rngHeader As Range, rngKey As Range, rngUsed As Range,
rngHeaderNotMand As Range, rngFindNotMand As Range, rngKeyGrossFee As Range, rngGrossFee As Range
Dim strKey As String, arrKey As String, strKeyGrossFee As String, strGrossFee As String
On Error GoTo ErrorHandler
If StrFilePath = “” Then GoTo ErrorHandler
With Application
.ScreenUpdating = False
.EnableEvents = False
End With
'//Template is opened
Set WB = Workbooks.Open(StrFilePath)
Set WS = WB.Worksheets(“Check_file”)
With WS
.Cells.EntireColumn.AutoFit
'//Stores the last row and column to be processed
lEnde = .Cells(.UsedRange.SpecialCells(xlCellTypeLastCell).Row + 2, 2).End(xlUp).Row
lColEnde = .UsedRange.SpecialCells(xlCellTypeLastCell).Column
'//Find the beginning of the table
Set rngFind = .Cells.Find(what:=Settings.Cells(Settings.Range("Header_Start").Row + 1, 2).Value,
LookIn:=xlValues, lookat:=xlWhole)
If rngFind Is Nothing Then
booCheck = False
End
End If
.Range(rngFind.Address, .Cells(.UsedRange.SpecialCells(xlCellTypeLastCell).Row,
rngFind.Column)).EntireRow.Hidden = False
lEnde = .Cells(.UsedRange.SpecialCells(xlCellTypeLastCell).Row + 2, 2).End(xlUp).Row
'//booCheck is set to true and on error to false _
Thus, if "True" is passed, the complete file is correct
Set rngUsed = .Range(rngFind.Address, .Cells(lEnde, lColEnde))
booCheck = IsErrorAll(rngUsed)
'//Header Check _
Checks all headers in advance to see if they are present and writes the missing ones in a cell
.Cells(4, 7).Clear
.Cells(4, 8).Clear
For i = Settings.Range("Header_Start").Row + 1 To Settings.Range("Header_Ende").Row - 1
Set rngHeader = .Range(rngFind, .Cells(rngFind.Row, lColEnde)).Find(what:=Settings.Cells(i, 2).Value,
LookIn:=xlValues, lookat:=xlWhole)
If rngHeader Is Nothing Then
booCheck = False
.Cells(4, 7).Value = "The following column labels were not found: "
If .Cells(4, 8).Value = "" Then
.Cells(4, 8).Value = .Cells(4, 8).Value & Settings.Cells(i, 2).Value
Else
.Cells(4, 8).Value = "," & .Cells(4, 8).Value & Settings.Cells(i, 2).Value
End If
.Cells(4, 8).Interior.Color = vbRed
Else
End If
Next i
If booCheck = False Then GoTo Ende
'// Check Not-Mandatory Columns _
Checks in advance whether Not-mandatory columns are available
Set rngFindNotMand = .Cells.Find(what:=Settings.Cells(Settings.Range("NotMand_Start").Row + 1, 2).Value, LookIn:=xlValues, lookat:=xlWhole)
For iNotMand = Settings.Range("NotMand_Start").Row + 1 To Settings.Range("NotMand_Ende").Row - 1
Set rngHeaderNotMand = .Range(rngFindNotMand, .Cells(rngFindNotMand.Row, lColEnde)).Find(what:=Settings.Cells(iNotMand, 2).Value, LookIn:=xlValues, lookat:=xlWhole)
If Not rngHeaderNotMand Is Nothing Then
'//Not-mandatory columns are defined
strKeyGrossFee = "Gross fee"
Set rngKeyGrossFee = Settings.Cells(1, 1).EntireColumn.Find(what:=strKeyGrossFee, LookIn:=xlValues, lookat:=xlWhole)
strGrossFee = Settings.Cells(rngKeyGrossFee.Row, 2).Value
Else
strKeyGrossFee = ""
End If
'//All line items are run through and the individual criteria are checked
For i = rngFind.Row + 1 To lEnde Step 1
'//Company
strKey = "Company"
Set rngKey = Settings.Cells(1, 1).EntireColumn.Find(what:=strKey, LookIn:=xlValues, lookat:=xlWhole)
strHeader = Settings.Cells(rngKey.Row, 2).Value
Set rngHeader = .Range(rngFind, .Cells(rngFind.Row, lColEnde)).Find(what:=strHeader, LookIn:=xlValues, lookat:=xlWhole)
iCoi = rngHeader.Column
If .Cells(i, rngHeader.Column).Value Like "####" Then
.Cells(i, rngHeader.Column).Interior.Pattern = xlNone
Else
.Cells(i, rngHeader.Column).Interior.Color = vbRed
booCheck = False
End If
'//Fee
strKey = "Fee"
Set rngKey = Settings.Cells(1, 1).EntireColumn.Find(what:=strKey, LookIn:=xlValues, lookat:=xlWhole)
strHeader = Settings.Cells(rngKey.Row, 2).Value
Set rngHeader = .Range(rngFind, .Cells(rngFind.Row, lColEnde)).Find(what:=strHeader, LookIn:=xlValues, lookat:=xlWhole)
If .Cells(i, rngHeader.Column).Value Like "*,*" Then
.Cells(i, rngHeader.Column).Interior.Color = vbRed
booCheck = False
Else
.Cells(i, rngHeader.Column).Interior.Pattern = xlNone
End If
'//Gross fee
strKey = "Fee"
Set rngKey = Settings.Cells(1, 1).EntireColumn.Find(what:=strKey, LookIn:=xlValues, lookat:=xlWhole)
strHeader = Settings.Cells(rngKey.Row, 2).Value
Set rngHeader = .Range(rngFind, .Cells(rngFind.Row, lColEnde)).Find(what:=strHeader, LookIn:=xlValues, lookat:=xlWhole)
Set rngFindNotMand = .Cells.Find(what:=Settings.Cells(Settings.Range("NotMand_Start").Row + 1, 2).Value, LookIn:=xlValues, lookat:=xlWhole)
strKeyGrossFee = "Gross fee"
Set rngKeyGrossFee = Settings.Cells(1, 1).EntireColumn.Find(what:=strKeyGrossFee, LookIn:=xlValues, lookat:=xlWhole)
strGrossFee = Settings.Cells(rngKeyGrossFee.Row, 2).Value
Set rngGrossFee = .Range(rngFindNotMand, .Cells(rngFindNotMand.Row, lColEnde)).Find(what:=strGrossFee, LookIn:=xlValues, lookat:=xlWhole)
If .Cells(i, rngGrossFee.Column).Value Is Nothing Then
.Cells(i, rngHeader.Column).Interior.Pattern = xlNone
ElseIf .Cells(i, rngHeader.Column).Value <> .Cells(i, rngGrossFee.Column).Value Then
.Cells(i, rngHeader.Column).Interior.Color = vbRed
booCheck = False
Else
.Cells(i, rngHeader.Column).Interior.Pattern = xlNone
End If
Next i
End With
'//Define results
Ende:
Main_Check = booCheck & “,” & Replace(CStr(rngFind.Address), “$”, “”)
If booCheck = False Then
WS.Cells(7, 7).Value = “Error counter:”
WS.Cells(7, 8).Value = WS.Cells(7, 8).Value + 1
Else
WS.Cells(7, 7).Value = “Check ok”
WS.Cells(7, 8).Value = “”
End If
WB.Close (True)
With Application
.ScreenUpdating = True
.EnableEvents = True
End With
Exit Function
'//If there are other errors, it should exit here and return ERROR
ErrorHandler:
On Error GoTo -1
On Error Resume Next
Main_Check = “ERROR”
WB.Close (True)
With Application
.ScreenUpdating = True
.EnableEvents = True
End With
End Function

I would recommend you refactor your code into 3 parts. 1- read the settings, 2- profile the sheet and then 3- check the data. Debug each step in turn.
Function Main_Check2(ByVal StrFilePath As String) As String
Const CHECK = "Check_file"
Dim wb As Workbook, ws As Worksheet, arHdr
Dim booCheck As Boolean, bExists As Boolean
Dim iColFee As Long, i As Long, msg As String
' check valid filepath
If Dir(StrFilePath) = "" Then
msg = "'" & StrFilePath & "' does not exist"
MsgBox msg, vbCritical, "File not found"
Main_Check2 = msg
Exit Function
End If
' open file and check sheet exists
Set wb = Workbooks.Open(StrFilePath)
bExists = False
For Each ws In wb.Sheets
If ws.Name = CHECK Then
bExists = True
Exit For
End If
Next
If Not bExists Then
msg = "Sheet '" & CHECK & "' not found in " & wb.FullName
MsgBox msg, vbCritical, "Sheet not found"
wb.Close False
Exit Function
End If
' get header details from settings sheet
Call GetSettings(Settings, arHdr)
' check headers, find fee column
booCheck = CheckHeaders(ws, arHdr, iColFee)
'Call DumpArray(arHdr) ' check results so far
' exit if false
If booCheck = False Then
Main_Check2 = booCheck
Exit Function
End If
' check data
With ws
'//All line items are run through and the individual criteria are checked
Dim rngData As Range, sName As String, n As Long
Dim iRow As Long, iCol As Long, lastrow As Long, cell As Range
For i = 1 To UBound(arHdr)
sName = arHdr(i, 1) ' header name
iCol = arHdr(i, 4) ' header column
iRow = arHdr(i, 5) ' header row
n = arHdr(i, 6) ' number of rows
' scan column
If iCol > 0 And n > 0 Then
Set rngData = .Cells(iRow + 1, iCol).Resize(n)
Select Case sName
Case "Company"
For Each cell In rngData
If cell.Value Like "####" Then
cell.Interior.Pattern = xlNone
Else
cell.Interior.Color = vbRed
booCheck = False
End If
Next
Case "Fee"
For Each cell In rngData
If Not cell.Value Like "*,*" Then
cell.Interior.Pattern = xlNone
Else
cell.Interior.Color = vbRed
booCheck = False
End If
Next
Case "Gross Fee"
' optional - skipped if icol = 0
For Each cell In rngData
If Len(cell) = 0 Then
cell.Interior.Pattern = xlNone
ElseIf cell.Value <> .Cells(cell.Row, iColFee).Value Then
cell.Interior.Color = vbRed
booCheck = False
Else
cell.Interior.Pattern = xlNone
End If
Next
End Select
End If
Next
End With
'//Define results
Ende:
If booCheck = False Then
ws.Cells(7, 7).Value = "Error counter:"
ws.Cells(7, 8).Value = ws.Cells(7, 8).Value + 1
Else
ws.Cells(7, 7).Value = "Check ok"""
ws.Cells(7, 8).Value = ""
End If
'wb.Close True
Main_Check2 = booCheck '& "," & Replace(CStr(rngFind.Address), "$", "")
End Function
Function GetSettings(ws, ByRef arHdr) As Boolean
Dim r1 As Long, r2 As Long, r3 As Long, r4 As Long
Dim n As Long, m As Long, i As Long, msg As String
With ws
r1 = .Range("Header_Start").Row
r2 = .Range("Header_Ende").Row
r3 = .Range("NotMand_Start").Row
r4 = .Range("NotMand_Ende").Row
m = r2 - r1 - 1 ' mandatory header
n = r4 - r3 - 1 ' non-mandatory headers
If m < 1 Then
msg = "No mandatory headers on setting"
MsgBox msg, vbExclamation, "Settings Error"
getSettings = False
End If
' size array and fill
ReDim arHdr(1 To n + m, 1 To 6)
For i = 1 To m
arHdr(i, 1) = .Cells(r1 + i, 1)
arHdr(i, 2) = .Cells(r1 + i, 2) ' search term
If Len(arHdr(i, 2)) > 0 Then ' skip blanks
arHdr(i, 3) = True ' mandatory
Else
arHdr(i, 3) = False
End If
Next
For i = 1 To n
arHdr(m + i, 1) = .Cells(r3 + i, 1)
arHdr(m + i, 2) = .Cells(r3 + i, 2)
arHdr(m + i, 3) = False ' optional
Next
End With
getSettings = True
End Function
Function CheckHeaders(ws, ByRef arHdr, ByRef iColFee) As Boolean
'//Header Check
'Checks all headers in advance to see if they are present
'and writes the missing ones in a cell
Dim rngTable As Range, rng As Range
Dim msg As String, sHdr As String, sTableStart As String
Dim i As Long, lastrow As Long, rowHdr As Long
Dim booCheck As Boolean
' search value for column 1 of table
sTableStart = arHdr(1, 2)
With ws
'//Find the beginning of the table
Set rngTable = .Cells.Find(what:=sTableStart, LookIn:=xlValues, lookat:=xlWhole)
If rngTable Is Nothing Then
msg = "Could not find begining of table '" & sTableStart & "'"
MsgBox msg, vbExclamation, "Error"
CheckHeaders = False
Exit Function
Else
rowHdr = rngTable.Row
End If
For i = 1 To UBound(arHdr)
sHdr = Trim(arHdr(i, 2))
If Len(sHdr) > 0 Then ' skip blanks
Set rng = .Rows(rowHdr).Find(what:=sHdr, LookIn:=xlValues, lookat:=xlWhole)
If rng Is Nothing Then
arHdr(i, 4) = 0
Else
' store fee column for later gross fee check
If arHdr(i, 1) = "Fee" Then iColFee = rng.Column
arHdr(i, 4) = rng.Column
arHdr(i, 5) = rng.Row
lastrow = .Cells(.Rows.Count, rng.Column).End(xlUp).Row
arHdr(i, 6) = lastrow - rng.Row - 1 ' data rows
End If
Else
arHdr(i, 4) = 0
End If
Next
' check for mandatory column errors
Dim sep As String
For i = 1 To UBound(arHdr)
If arHdr(i, 3) And arHdr(i, 4) = 0 Then
msg = msg & sep & arHdr(i, 2)
sep = ","
End If
Next
If Len(msg) > 0 Then
.Cells(4, 7) = "The following column labels were not found: "
.Cells(4, 8) = msg
.Cells(4, 8).Interior.Color = vbRed
CheckHeaders = False
'GoTo Ende
Else
.Cells(4, 7).Clear ' G4
.Cells(4, 8).Clear ' H4
CheckHeaders = True
End If
End With
End Function
' dump array to new workbook to debug
Sub DumpArray(ar)
Dim wb As Workbook: Set wb = Workbooks.Add
With wb.Sheets(1)
.Name = "arHdr"
.Range("A1:F1") = Array("Header1", "Header2", "Mandatory", "Column", "Row", "DataRows")
.Range("A2").Resize(UBound(ar), UBound(ar, 2)) = ar
End With
' save - replace existing
Application.DisplayAlerts = False
wb.SaveAs "debug_arHdr.xlsx"
Application.DisplayAlerts = True
'wb.Close
End Sub

Related

How to show results on desired sheet?

The below code is not showing the results on the "All Stock Analysis" sheet.
I tried doing a test after the activation of each worksheet (Range("I1).Interior.Color = vbGreen) and cell I1 turns green on each of the desired worksheets. What other tests can I try? No error msg pops up.
Sub AllStocksAnalysisRefactored()
Dim startTime As Single
Dim endTime As Single
yearValue = InputBox("What year would you like to run the analysis on?")
startTime = Timer
'Format the output sheet on All Stocks Analysis worksheet
Worksheets("All Stock Analysis").Activate
Range("A1").Value = "All Stocks (" + yearValue + ")"
'Create a header row
Cells(3, 1).Value = "Ticker"
Cells(3, 2).Value = "Total Daily Volume"
Cells(3, 3).Value = "Return"
'Initialize array of all tickers
Dim tickers(12) As String
tickers(0) = "AY"
tickers(1) = "CSIQ"
tickers(2) = "DQ"
tickers(3) = "ENPH"
tickers(4) = "FSLR"
tickers(5) = "HASI"
tickers(6) = "JKS"
tickers(7) = "RUN"
tickers(8) = "SEDG"
tickers(9) = "SPWR"
tickers(10) = "TERP"
tickers(11) = "VSLR"
'Activate data worksheet
Worksheets(yearValue).Activate
'Get the number of rows to loop over
RowCount = Cells(Rows.Count, "A").End(xlUp).Row
'1a) Create a ticker Index
Dim tickerIndex As Single
tickerIndex = 0
'1b) Create three output arrays
Dim tickerVolumes(12) As LongLong
Dim tickerstartingPrices(12) As Single
Dim tickerendingPrices(12) As Single
''2a) Create a for loop to initialize the tickerVolumes to zero.
For i = 0 To 11
tickerVolumes(i) = 0
''2b) Loop over all the rows in the spreadsheet.
For j = 2 To RowCount
'3a) Increase volume for current ticker
tickerVolumes(tickerIndex) = tickerVolumes(tickerIndex) + Cells(j, 8).Value
'3b) Check if the current row is the first row with the selected tickerIndex.
'If Then
If Cells(j - 1, 1).Value <> tickers(tickerIndex) And _
Cells(j, 1).Value = tickers(tickerIndex) Then
tickerstartingPrices(tickerIndex) = Cells(j, 6).Value
'End If
End If
'3c) check if the current row is the last row with the selected ticker
'If the next row’s ticker doesn’t match, increase the tickerIndex.
'If Then
If Cells(j + 1, 1).Value <> tickers(tickerIndex) And _
Cells(j, 1).Value = tickers(tickerIndex) Then
tickerendingPrices(tickerIndex) = Cells(j, 6).Value
'3d Increase the tickerIndex.
tickerIndex = tickerIndex + 1
'End If
End If
Next j
Next i
'4) Loop through your arrays to output the Ticker, Total Daily Volume, and Return.
For i = 0 To 11
Worksheets("All Stock Analysis").Activate
Next i
'Formatting
Worksheets("All Stock Analysis").Activate
Range("A3:C3").Font.FontStyle = "Bold"
Range("A3:C3").Borders(xlEdgeBottom).LineStyle = xlContinuous
Range("B4:B15").NumberFormat = "#,##0"
Range("C4:C15").NumberFormat = "0.0%"
Columns("B").AutoFit
dataRowStart = 4
dataRowEnd = 15
For i = dataRowStart To dataRowEnd
If Cells(i, 3) > 0 Then
Cells(i, 3).Interior.Color = vbGreen
Else
Cells(i, 3).Interior.Color = vbRed
End If
Next i
endTime = Timer
MsgBox "This code ran in " & (endTime - startTime) & _
" seconds for the year " & (yearValue)
End Sub
Here is how "All Stock Analysis" sheet will look after running the code:
You only need to scan the data sheet once if you use a dictionary object to convert the ticker ID to an array index number.
Option Explicit
Sub AllStocksAnalysisRefactored()
Const SHT_NAME = "All Stock Analysis"
Dim wb As Workbook, ws As Worksheet, wsYr As Worksheet
Dim cell As Range, yr As String, iRow As Long, iLastRow As Long
Dim t As Single: t = Timer
' choose data worksheet
yr = InputBox("What year would you like to run the analysis on ? ", "Enter Year", Year(Date))
Set wb = ThisWorkbook
On Error Resume Next
Set wsYr = wb.Sheets(yr)
On Error GoTo 0
' check if exists
If wsYr Is Nothing Then
MsgBox "Sheet '" & yr & "' does not exists.", vbCritical, "Error"
Exit Sub
End If
'Initialize array of all tickers
Dim tickerID, tickerData(), i As Integer, n As Integer
Dim dict As Object, sId As String
tickerID = Array("AY", "CSIQ", "DQ", "ENPH", "FSLR", "HASI", _
"JKS", "RUN", "SEDG", "SPWR", "TERP", "VSLR")
n = UBound(tickerID) + 1
ReDim tickerData(1 To n, 1 To 5)
' create dict id to index
Set dict = CreateObject("Scripting.Dictionary")
For i = 1 To n
sId = UCase(Trim(tickerID(i - 1)))
tickerData(i, 1) = sId ' id
tickerData(i, 2) = 0 ' volume
tickerData(i, 3) = 0 ' start price
tickerData(i, 4) = 0 ' finish price
tickerData(i, 5) = 0 ' count
dict.Add sId, i
Next
'Get the number of rows to loop over
iLastRow = wsYr.Cells(Rows.Count, "A").End(xlUp).Row
' Loop over all the rows in the spreadsheet.
' A=ticker, F=Price , H=Volume
For iRow = 2 To iLastRow
sId = UCase(Trim(wsYr.Cells(iRow, "A")))
If dict.exists(sId) Then
i = dict(sId)
' volume
tickerData(i, 2) = tickerData(i, 2) + wsYr.Cells(iRow, "H") ' volume
' start price when count is 0
If tickerData(i, 5) = 0 Then
tickerData(i, 3) = wsYr.Cells(iRow, "F")
End If
' end price
tickerData(i, 4) = wsYr.Cells(iRow, "F")
' count
tickerData(i, 5) = tickerData(i, 5) + 1
End If
Next
'Format the output sheet on All Stocks Analysis worksheet
Set ws = wb.Sheets(SHT_NAME)
ws.Cells.Clear
With ws
.Range("A1").Value2 = "All Stocks (" & yr & ")"
With .Range("A3:E3")
.Value2 = Array("Ticker", "Total Daily Volume", "Start Price", "End Price", "Return")
.Font.FontStyle = "Bold"
.Borders(xlEdgeBottom).LineStyle = xlContinuous
End With
.Range("A4").Resize(n, 4).Value2 = tickerData
.Range("B4:D4").Resize(n).NumberFormat = "#,##0"
.Range("E4").Resize(n).NumberFormat = "0.0%"
.Columns("B").AutoFit
End With
' coloring
For Each cell In ws.Range("E4").Resize(n)
cell.FormulaR1C1 = "=(RC[-1]-RC[-2])/RC[-2]" ' end-start/start
If cell > 0 Then
cell.Interior.Color = vbGreen
Else
cell.Interior.Color = vbRed
End If
Next
ws.Activate
ws.Range("A1").Select
MsgBox "This code ran for (" & yr & ")", vbInformation, Int(Timer - t) & " seconds"
End Sub

Is any way to make this VBA code to find next same value if there are more than one?

I am a beginner on vba, sarching and reading different things about vba I have created a piece of code but doesn't work how I want to. If I search for a specific value the code find it and show on specific textboxes a specific value, but if there are more than one same values (in searching column) I want to make the code go to next one until find every same value, what my actual code doesn't do. Any help on improving this code or any other code that does it I appreciate.
Here is my code,
Private Sub Search_Click()
Dim a As String
Dim b As Double
Dim k As Range
On Error GoTo dontexist:
If Me.TextBox20.Value = "" Or Me.TextBox20.Value = "Number of invoice" Then
Me.Label29.Caption = "Number of invoice"
b = Me.TextBox24.Value
Set k = Sheets("Sheet2").Range("E:E")
r = Application.WorksheetFunction.Match(b, k, 0)
Me.TextBox21.Value = Sheets("Sheet2").Cells(r, 2).Value
Me.TextBox22.Value = Sheets("Sheet2").Cells(r, 8).Value
Me.TextBox23.Value = Sheets("Sheet2").Cells(r, 4).Value
Exit Sub
Else
Me.Label29.Caption = "Sum of invoice"
a = Me.TextBox20.Value
Set k = Sheets("Sheet2").Range("H:H")
r = Application.WorksheetFunction.Match(a, k, 0)
Me.TextBox21.Value = Sheets("Sheet2").Cells(r, 2).Value
Me.TextBox22.Value = Sheets("Sheet2").Cells(r, 5).Value
Me.TextBox23.Value = Sheets("Sheet2").Cells(r, 4).Value
Exit Sub
End If
dontexist:
MsgBox "This record dosn't exist!", vbInformation, "Info!"
End Sub
Add a label to your form to hold the last found row and start the search from there. I have used label30.
Option Explicit
Private Sub Search_Click()
Dim rngSearch As Range, rngFound As Range, sColumn As String
Dim sValue As String, iCount As Long
Dim ws As Worksheet
Set ws = Sheets("Sheet2")
' label to hold row to start search at
If Label30 = "" Then Label30 = "1"
If Len(TextBox24) > 0 Then
' search on number
sValue = TextBox24
sColumn = "E"
Label29 = "Number of invoice"
ElseIf Len(TextBox20) > 0 Then
' search on total
sValue = TextBox20
sColumn = "H"
Label29 = "Sum of invoice"
Else
MsgBox "No search values entered", vbExclamation
Exit Sub
End If
' count number of matches
Set rngSearch = ws.Cells(1, sColumn).EntireColumn
iCount = Application.WorksheetFunction.CountIf(rngSearch, sValue)
If iCount > 0 Then
' continue search from last position
Set rngFound = rngSearch.Find(sValue, _
After:= ws.Range(sColumn & Label30), _
LookIn:=xlValues, _
LookAt:=xlWhole)
If rngFound Is Nothing Then
' not found
Label30 = ""
MsgBox "No more records found"
Else
' is row new
If rngFound.Row > Label30 Then
'MsgBox rngFound.Row
' copy into text boxes
With rngFound.EntireRow
If sColumn = "E" Then
TextBox21 = .Cells(1, 2)
TextBox22 = .Cells(1, 8)
TextBox23 = .Cells(1, 4)
Else
TextBox21 = .Cells(1, 2)
TextBox22 = .Cells(1, 5)
TextBox23 = .Cells(1, 4)
End If
End With
Label30 = rngFound.Row
Else
MsgBox "No more records found", vbExclamation
Label30 = ""
Exit Sub
End If
End If
Else
MsgBox "No records found", vbExclamation
Label30 = ""
End If
End Sub

adding data from userform into table

I created a userform to add data in excel.
The data is been added quite good but the problem is that they are added first in total row then outside the table I have created
here is the code I am using:
Sub Submit_Data()
Dim iRow As Long
If adminpanel.txtRowNumber.Value = "" Then
iRow = student.Range("A" & Rows.Count).End(xlUp).Row + 1
Else
iRow = adminpanel.txtRowNumber.Value
End If
With student.Range("A" & iRow)
.Offset(0, 0).Value = "=Row()-1"
.Offset(0, 1).Value = adminpanel.Studentname.Value
.Offset(0, 2).Value = adminpanel.Class.Value
.Offset(0, 3).Value = adminpanel.School.Value
.Offset(0, 4).Value = adminpanel.Mobile.Value
.Offset(0, 5).Value = adminpanel.Email.Value
.Offset(0, 6).Value = adminpanel.txtImagePath.Value
End With
Call Reset_Form
Application.ScreenUpdating = True
MsgBox "data are done"
End Sub
If you're working with a Table/ListObject then it has a ListRows.Add method which you should use when you need to add a new row. From that row you can get its Range property.
Sub Submit_Data()
Dim iRow As Long, tblRow As Range, lo As ListObject
Set lo = student.ListObjects(1) 'get a reference to your table
If Len(adminpanel.txtRowNumber.Value) = 0 Then
Set tblRow = lo.ListRows.Add.Range '<< add a new row and get its range
Else
'get a reference to the existing row
iRow = CLng(adminpanel.txtRowNumber.Value)
Set tblRow = Application.Intersect(student.Rows(iRow), _
lo.DataBodyRange)
End If
'Fill the row in one operation using an array
tblRow.Value = Array(tblRow.Row - 1, adminpanel.Studentname.Value, _
adminpanel.Class.Value, adminpanel.School.Value, _
adminpanel.Mobile.Value, adminpanel.Email.Value, _
adminpanel.txtImagePath.Value)
Reset_Form
MsgBox "data are done"
End Sub

controlling excel cells and writing it to other cells

I have a excel sheet like at below. I want to find some strings in my excel's third cell. The string is 180 days. When the cell value includes 180 days, I want write previous cells value in next to empty cells like in below picture. I want to write process plan in first cell, operation title in second cell. I wrote this codes but it's not working like what I want.
Sub Button1_Click()
Dim excelRange As Long
Dim i As Long
Dim k As Long
'Dim txt As String
excelRange = ActiveSheet.Cells(1048576, 3).End(xlUp).Row
k = 2
For a = 2 To excelRange
txt = Cells(a, 3)
k = a
If InStr(1, txt, "180 days") > 0 Then
For i = a To 2 Step -1
txt1 = Cells(i, 3)
If InStr(1, txt1, "Oper Title") > 0 Then
Cells(a, 2) = Cells((k + 1), 3)
ElseIf InStr(1, txt1, "Process") > 0 Then
Cells(a, 1) = Cells(k, 3)
Else:
k = k - 1
End If
Next i
End If
Next a
End Sub
Sub test()
Dim excelRange As Range
Dim criteriRange As Range
Dim evaluateRange As Range
Dim c As Range
Dim i As Long
Set excelRange = Range("C1:C" & Cells(1048576, 3).End(xlUp).Row)
For Each cell In excelRange
If UCase(cell.Text) Like "*180 DAY*" Then
If criteriRange Is Nothing Then
Set criteriRange = cell
Else
Set criteriRange = Union(criteriRange, cell)
End If
End If
Next
If Not criteriRange Is Nothing Then
For Each c In criteriRange
For i = c.Row To 1 Step -1
If UCase(Cells(i, 3)) Like "*PROCESS PLAN*" Then
c.Offset(0, -2) = Cells(i, 3)
Exit For
End If
Next
For i = c.Row To 1 Step -1
If UCase(Cells(i, 3)) Like "*OPER TITLE*" Then
c.Offset(0, -1) = Cells(i + 1, 3)
Exit For
End If
Next
Next
End If
End Sub
Instead of looping through a range, your macro will run much faster if you use the Range.Find method.
In your code, you did not check to ensure that all of your sets of Process | Title | 180 Days are complete. I added that to the code below, by making sure that the Process and Title rows were found after the previous 180 day row (or before the 180 day row for the first instance).
In your code, you did not check to see if the cells where you want to output this information are, in fact, empty. If you really want to do that, you can easily modify this code to check these cells before writing to them.
Hopefully, through the comments and the use of meaningful variable names, you will be able to understand what is going on. But you might want to also read through VBA Help for the Range.Find method.
In general, we search down to find the 180 day row, then search up from there to find the associated Process and Title rows.
If a preceding Process or Title row should be before the preceding 180 day row, then we have an incomplete set, output the error message, and terminate the procedure.
If necessary, you could develop procedures to deal with incomplete data sets.
Option Explicit
Sub Info()
Dim searchRng As Range, C As Range, cProcessPlan As Range, cOperTitle As Range
Dim firstAddress As String 'to check when we are done
Dim lastAddress As String 'to check for incomplete data sets
'Where are we looking?
Set searchRng = ThisWorkbook.Worksheets("Sheet1").Columns(3)
With searchRng
Set C = .Find(what:="180 Days", after:=.Cells(1, 1), LookIn:=xlValues, _
lookat:=xlPart, searchorder:=xlByRows, _
searchdirection:=xlNext, MatchCase:=False)
If Not C Is Nothing Then
firstAddress = C.Address
lastAddress = C.Address
Set cOperTitle = .Find(what:="Oper Title", after:=C, searchdirection:=xlPrevious)
Set cProcessPlan = .Find(what:="Process Plan", after:=C, searchdirection:=xlPrevious)
If Not cOperTitle Is Nothing Or Not cProcessPlan Is Nothing Then
'check for full set
If cOperTitle.Row > Range(lastAddress).Row Or cProcessPlan.Row > Range(lastAddress).Row Then
MsgBox "Incomplete Data Set" & vbLf & "Before: " & C.Address
Exit Sub
End If
C.Offset(0, -1) = cOperTitle.Offset(1, 0)
C.Offset(0, -2) = cProcessPlan
Else
MsgBox "Title or Process Plan not found"
Exit Sub
End If
Do
Set C = .Find(what:="180 Days", after:=C, LookIn:=xlValues, _
lookat:=xlPart, searchorder:=xlByRows, _
searchdirection:=xlNext, MatchCase:=False)
If C.Address = firstAddress Then Exit Do
Set cOperTitle = .Find(what:="Oper Title", after:=C, searchdirection:=xlPrevious)
Set cProcessPlan = .Find(what:="Process Plan", after:=C, searchdirection:=xlPrevious)
'check for a full set
If cOperTitle.Row < Range(lastAddress).Row Or cProcessPlan.Row < Range(lastAddress).Row Then
MsgBox "Incomplete Data Set" & vbLf & "Between: " & lastAddress & " and " & C.Address
Exit Sub
End If
C.Offset(0, -1) = cOperTitle.Offset(1, 0)
C.Offset(0, -2) = cProcessPlan
lastAddress = C.Address
Loop
End If
End With
'next stuff
End Sub
Using a variant array is fast.
Sub test()
Dim Ws As Worksheet
Dim rngDB As Range
Dim vDB As Variant
Dim vRow(), vTitle(), vProcess()
Dim i As Long, j As Long, k As Long, m As Long
Set Ws = ActiveSheet
With Ws
Set rngDB = .Range("a1", .Range("c" & Rows.Count).End(xlUp))
End With
vDB = rngDB
For i = 1 To UBound(vDB, 1)
If InStr(vDB(i, 3), "180 days") Then
j = j + 1
ReDim Preserve vRow(1 To j)
vRow(j) = i
ElseIf InStr(vDB(i, 3), "Oper Title") Then
k = k + 1
ReDim Preserve vTitle(1 To k)
vTitle(k) = vDB(i + 1, 3)
ElseIf InStr(vDB(i, 3), "Process") Then
m = m + 1
ReDim Preserve vProcess(1 To m)
vProcess(m) = vDB(i, 3)
End If
Next i
For i = 1 To j
vDB(vRow(i), 1) = vProcess(i)
vDB(vRow(i), 2) = vTitle(i)
Next i
rngDB = vDB
End Sub

VB error "Object variable or With block variable not set (Error 91)"

Option Explicit
Private Sub Worksheet_Activate()
Dim r As Range, rng As Range, snRow As Range, TmRow As Range
Dim x As Integer, ETRow As Long, LTRow As Long
Dim TMName As String
Application.ScreenUpdating = False
ETRow = 10: LTRow = 10
ActiveSheet.Range("C4:AG5,C11:L41").ClearContents
For x = 1 To Sheets.Count
If Sheets(x).Name <> "Summary" Then
With Sheets(Sheets(x).Name)
TMName = Left(Sheets(x).Name, 6)
With .Range("C:C")
Set snRow = .Find("Total Staff (inc Supervisors)", LookIn:=xlValues, LookAt:=xlWhole)
End With
Set rng = .Range("D5", "AH5")
For Each r In rng
If InStr(1, r.Value, "LT") > 0 Then
With Sheets("Summary")
.Cells(5, r.Column - 1) = Sheets(Sheets(x).Name).Cells(snRow.Row, r.Column).Value
With .Range("I9:L9")
Set TmRow = .Find(TMName, LookIn:=xlValues, LookAt:=xlWhole)
End With
.Cells(LTRow, TmRow.Column) = Left(r.Value, InStr(1, r.Value, "LT", vbTextCompare) - 1)
LTRow = LTRow + 1
End With
ElseIf InStr(1, r.Value, "ET") > 0 Then
With Sheets("Summary")
.Cells(4, r.Column - 1) = Sheets(Sheets(x).Name).Cells(snRow.Row, r.Column).Value
With .Range("C9:F9")
Set TmRow = .Find(TMName, LookIn:=xlValues, LookAt:=xlWhole)
End With
.Cells(ETRow, TmRow.Column) = Left(r.Value, InStr(1, r.Value, "ET", vbTextCompare) - 1)
ETRow = ETRow + 1
End With
End If
Next
End With
End If
Next
Application.ScreenUpdating = True
End Sub
It is saying there is an issue with
.Cells(LTRow, TmRow.Column) = Left(r.Value, InStr(1, r.Value, "LT", vbTextCompare) - 1)
and
.Cells(ETRow, TmRow.Column) = Left(r.Value, InStr(1, r.Value, "ET", vbTextCompare) - 1)
This code works on a roster with 4 sheets if the user puts in ET or LT next to the date it then counts if someone is on duty (signified by W)
The code is for summary sheet.
Not sure why as it doesn't work but as soon as I try to change the actual summary sheet by adding an extra row below C5 this happens. Then even if I undo everything, it still occurs.
The problem is that you are assigning value to
.Cells(LTRow, TmRow.Column) and in the line before you have:
Set TmRow = .Find(TMName, LookIn:=xlValues, LookAt:=xlWhole)
Thus, if TmRow is not assigned to a value through the .Find(), TmRow.Column would give this error.
Try to go around like this:
With .Range("I9:L9")
Set TmRow = .Find(TMName, LookIn:=xlValues, LookAt:=xlWhole)
If TmRow Is Nothing Then
MsgBox "TmRow knows nothing"
Stop
End If
End With
Then think of a way to rebuild your code.

Resources