If you do not know how loops work, you must learn the basics of Excel VBA. You cannot hope to stitch together bits of code gathered from the internet without some understanding of VBA.
Search for "Excel VBA Tutorial". You will get many hits of which many will be for free online tutorials. These tutorials differ in approach so try a few to see which best matches your learning style. Alternatively, visit a good bookshop or library where you will find a selection of Excel VBA Primers. I suggest a library so you can take a few books home for a try before purchasing your favourite.
There are many holes in your specification. Perhaps you have a complete specification which you have not documented here. If you have a complete specification, please do not add it to your question. For sites like this, you need small focused questions.
Two design questions I have spotted are:
Why fill the Date column with
=TODAY()
? If the archive macro is not run at the end of the day, Excel will have changed the date when the macro is run the next day. Either fill the column with the date value or use the nearest VBA equivalent function which is Now().You imply the user might enter a count for Item A and then enter another count later in the day. The archive sheet is to hold the total of those two counts. How is this handled? You could have two or more rows for Item A. The user could run the archive macro before entering a new value in the Item A row. You could use a Worksheet Change event to automatically archive the value after the user has entered it.
You need to fully specify what the macro is going to do and how it is going to be used before trying to code it. Below I have provided two alternative macros that achieve what I believe is the first step of your requirement: locate valid rows in the data entry worksheet and extract the values ready for achiving.
I suggest you study basic Excel VBA first. That should give you enough knowledge to understand my macros even though the second macro uses non-basic statements. Come back with questions as necessary but please run and try to understand the macros before asking these questions.
Demo1
I created a worksheet "Data Entry" and filled it with data that matches my understanding of your worksheet "Sheet1". Please do not use the default worksheet names because it gets very confusing. Replace my name with whatever you choose.
The macro Demo1 outputs the values from valid rows to the Immediate Window. Writing to the Immediate Window is a convenient way of testing small blocks of code as they are written.
I have documented what the code does but not the VBA statements. Once you know a statement exists, it is usually easy to look it up.
Option Explicit
Sub Demo1()
Dim CountCrnt As Long
Dim DateCrnt As Date
Dim ItemCrnt As String
Dim RowCrnt As Long
Dim RowLast As Long
With Worksheets("Data Entry")
' This sets RowLast to the last used row in column "C" or sets it to 1 if no
' row is used. It is the VBA equivalent of positioning the cursor to the
' bottom of column C and clicking Ctrl+Up
RowLast = .Cells(Rows.Count, "C").End(xlUp).Row
' I have assumed the first data row is 2
For RowCrnt = 2 To RowLast
' I have allowed for column C being empty. I assume such rows are
' to be ignored. I also ignore rows with invalid values in columns
' B or C.
If .Cells(RowCrnt, "C").Value <> "" And _
IsNumeric(.Cells(RowCrnt, "C").Value) And _
IsDate(.Cells(RowCrnt, "B").Value) Then
' Extract the validated values to variables ready for the next stage
' of processing.
ItemCrnt = .Cells(RowCrnt, "A").Value
DateCrnt = .Cells(RowCrnt, "B").Value
CountCrnt = .Cells(RowCrnt, "C").Value
' Output row values to Immediate Window
Debug.Print RowCrnt & " " & ItemCrnt & " " & _
Format(DateCrnt, "dmmmyy") & " " & CountCrnt
End If
Next
End With
End Sub
Demo2
Macro Demo2 achieves the same as macro Demo1 but in a different way.
Demo1 accessed the cells within the worksheet individually. Demo2 copies the entire worksheet to a Variant which can then be accessed as a 2D array. This is much faster that individual cell access and is usually more convenient if you only want the cell values.
Demo1 output values to the Immediate Window. This is very convenient for small volumes of output but early lines will be lost for larger volumes. Demo2 creates a file within the same folder as the workbook and writes the output to that file so nothing will be lost.
Sub Demo2()
Dim CountCrnt As Long
Dim DateCrnt As Date
Dim FileOutNum As Long
Dim ItemCrnt As String
Dim RowCrnt As Long
Dim RowLast As Long
Dim SheetValue As Variant
FileOutNum = FreeFile
Open ActiveWorkbook.Path & "\Demo2.txt" For Output As #FileOutNum
With Worksheets("Data Entry")
' This statement converts Variant SheetValue to an appropriately sized
' two-dimensional array and copies the values from the entire used
' range of the worksheet to it.
SheetValue = .UsedRange.Value
' Standard practice for 2D arrays is to have the first dimension for
' columns and the second for rows. For arrays copied from or to
' worksheets, the first dimension is for rows and the second is for
' columns. This can be confusing but means that array elements are
' accessed as SheetValue(Row, Column) which matches Cells(Row, Column).
' Note that the lower bounds for both dimensions are always one. If the
' range copied from the worksheet starts at Cell A1, row and column
' numbers for the array will match those of the worksheet.
End With
For RowCrnt = 2 To UBound(SheetValue, 1)
' I have allowed for column 3 (= "C") being empty. I assume such rows
' are to be ignored. I also ignore rows with invalid values in columns
' 2 (= "B") or 3.
If SheetValue(RowCrnt, 3) <> "" And _
IsNumeric(SheetValue(RowCrnt, 3)) And _
IsDate(SheetValue(RowCrnt, 2)) Then
ItemCrnt = SheetValue(RowCrnt, 1)
DateCrnt = SheetValue(RowCrnt, 2)
CountCrnt = SheetValue(RowCrnt, 3)
' Output row values to file
Print #FileOutNum, RowCrnt & " " & ItemCrnt & " " & _
Format(DateCrnt, "dmmmyy") & " " & CountCrnt
End If
Next
Close #FileOutNum
End Sub
Edit New section in response to supplementary question.
As you have discovered there is no way of "printing" to a worksheet but it is easy to write to individual cells. I have used a diagnostic worksheet but I normally consider this technique more trouble than it is worth. Output to a file is easier to add and easier to delete and it does not interfere with the code.
The code below is in the correct order but I have added explanations between blocks.
Dim RowDiagCrnt As Long
The above statement is not within a subroutine which makes is a gloabl variable that can be accessed from any routine. If there are several routines that need to output diagnostic information, it is easier to use a global variable for the row number than pass it as a parameter from the parent routine.
I have a system for naming variables, "Row" means this is a row. "Diag" identifies the worksheet". "Crnt" identifies this as the current row number. In Demo1
, I had RowCrnt
because I only had one worksheet. You may not like my system. Fine, develop your own. Having a system means I can look at a macro I wrote years ago and know what all the variables are. This makes maintenance much, much easier.
Sub Demo3()
Dim CountCrnt As Long
Dim DateCrnt As Date
Dim ItemCrnt As String
Dim RowDiagCrnt As Long
Dim RowEntryCrnt As Long
Dim RowEntryLast As Long
Dim ValidRow As Boolean
Dim WkshtDiag As Worksheet
Dim WkshtEntry As Worksheet
I now have two worksheets and I will have to switch between them. I do not like multiple uses of Worksheets("Xxxxx")
because I might change "Xxxxx". A reference avoids multiple uses of the name and is faster.
Set WkshtEntry = Worksheets("Data Entry")
Set WkshtDiag = Worksheets("Diagnostics")
' Delete existing contents of diagnostic worksheet and create header row
With WkshtDiag
.Cells.EntireRow.Delete
.Cells(1, "A").Value = "Row"
.Cells(1, "B").Value = "Item"
.Cells(1, "C").Value = "Date"
.Cells(1, "D").Value = "Count"
End With
RowDiagCrnt = 2
With WkshtEntry
RowEntryLast = .Cells(Rows.Count, "C").End(xlUp).Row
End With
For RowEntryCrnt = 2 To RowEntryLast
I must keep the access to the two worksheet separate if I want to use With
statements. I have used a boolean to handle this problem.
With WkshtEntry
If .Cells(RowEntryCrnt, "C").Value <> "" And _
IsNumeric(.Cells(RowEntryCrnt, "C").Value) And _
IsDate(.Cells(RowEntryCrnt, "B").Value) Then
ItemCrnt = .Cells(RowEntryCrnt, "A").Value
DateCrnt = .Cells(RowEntryCrnt, "B").Value
CountCrnt = .Cells(RowEntryCrnt, "C").Value
ValidRow = True
Else
ValidRow = False
End If
End With
If ValidRow Then
With WkshtDiag
' Output row values to Diagnostic worksheet
.Cells(RowDiagCrnt, "A").Value = RowEntryCrnt
.Cells(RowDiagCrnt, "B").Value = ItemCrnt
With .Cells(RowDiagCrnt, "C")
.Value = DateCrnt
.NumberFormat = "dmmmyy"
End With
.Cells(RowDiagCrnt, "D").Value = CountCrnt
RowDiagCrnt = RowDiagCrnt + 1
End With
End If
Next
' Set columns to appropriate width for contents
With WkshtDiag
.Columns.AutoFit
End With
End Sub
I hope you can see why the reasons for all the changes I made to Demo1
to create Demo3
. Having a second worksheet that is not required for the final solution adds complexity that I normally prefer to avoid.