My spreadsheet is used to manage tasks. I add new tasks by running a small macro, and currently the numbering is simply 1, 2, 3, 4..., generated by the following code:
Cells(ActiveSheet.Rows(9).Row, 1).Value = Cells(ActiveSheet.Rows(10).Row, 1).Value + 1
I would like, using VBA, to evolve this by adding a prefix to the number that represents the Year the task was initiated. Furthermore, the numbering should re-start at 1 for the first entry of each year. I.e
15-1, 15-2, 15-3, 15-4....16-1, 16-2, 16-3...
Any ideas for a simple code that could achieve this?
Here is a very basic example. Amend it to suit your needs. You can also create a procedure and pass the row number to which you want the auto numbering to happen as shown at the end of this post.
Sub Sample()
Dim rng As Range
Dim prev As Range
Dim rw As Long
rw = 9 '<~~ Change this to the relevant row
Set rng = ThisWorkbook.Sheets("Sheet1").Cells(rw, 1)
On Error Resume Next
Set prev = rng.Offset(-1)
On Error GoTo 0
'~~> Check if there is one row above
If Not prev Is Nothing Then
'~~> Match the year
If Left(rng.Offset(-1), 2) <> Format(Date, "yy") Then
'~~> Restart numbering
rng.Value = Format(Date, "yy") & "-" & 1
Else
'~~> Increment numbering. Split will extract the number
rng.Value = Format(Date, "yy") & "-" & Val(Split(rng.Value, "-")(1)) + 1
End If
Else
'~~> Restart numbering
rng.Value = Format(Date, "yy") & "-" & 1
End If
End Sub
Screenshot
Edit:
Using it as a procedure where you can pass arguments.
Sub Sample()
Dim r As Long
r = 9 '<~~ Chnage this to the relevant row
AllocateID r
End Sub
Sub AllocateID(rw As Long)
Dim rng As Range
Dim prev As Range
Set rng = Cells(rw, 1)
On Error Resume Next
Set prev = rng.Offset(-1)
On Error GoTo 0
'~~> Check if there is one row above
If Not prev Is Nothing Then
'~~> Match the year
If Left(rng.Offset(-1), 2) <> Format(Date, "yy") Then
'~~> Restart numbering
rng.Value = Format(Date, "yy") & "-" & 1
Else
'~~> Increment numbering. Split will extract the number
rng.Value = Format(Date, "yy") & "-" & Val(Split(rng.Value, "-")(1)) + 1
End If
Else
'~~> Restart numbering
rng.Value = Format(Date, "yy") & "-" & 1
End If
End Sub
How about this:
Sub Test()
Dim i As Integer
Dim startYear As Integer
priorYear = 2014
With ActiveSheet
For i = 1 To 100
.Cells(i, 1) = CStr(priorYear + WorksheetFunction.RoundUp(i / 12, 0) & "-" & ((i + 1) Mod 12))
Next i
End With
End Sub
sure just do that:
Cells(ActiveSheet.Rows(9).Row, 1).Value = format(date,"yy") & "-" & _
Cells(ActiveSheet.Rows(10).Row, 1).Value + 1
Related
.
The following macro (CollectProjectItems) functions as designed. Applying the same logic, with a change in the Range, in the macro (CollectContractorItems) does not function as desired.
I am presuming the error is something I've overlooked and of course ... for the life of me ... I cannot identify my error.
Need a fresh set of eyes.
Thank you ahead of time.
Sub UpdateCharts()
CollectProjectItems
CollectContractorItems
End Sub
Sub CollectProjectItems()
On Error Resume Next
MyDate = Format(Date, "mmm") & "-" & Right(Year(Date), 2)
For Each cl In Range("A3", Range("A" & Rows.Count).End(xlUp))
wproj = Application.Match(cl.Value, Columns(10), 0)
If IsNumeric(wproj) Then
MyMonth = Application.Match(MyDate, Rows(wproj + 1), 0)
Cells(wproj + 2, MyMonth) = cl.Offset(, 1)
Cells(wproj + 3, MyMonth) = cl.Offset(, 2)
End If
Next
End Sub
Sub CollectContractorItems()
On Error Resume Next
MyDate = Format(Date, "mmm") & "-" & Right(Year(Date), 2)
For Each cl In Range("E3", Range("E" & Rows.Count).End(xlUp))
wproj = Application.Match(cl.Value, Columns(25), 0)
If IsNumeric(wproj) Then
MyMonth = Application.Match(MyDate, Rows(wproj + 1), 0)
Cells(wproj + 2, MyMonth) = cl.Offset(, 1)
Cells(wproj + 3, MyMonth) = cl.Offset(, 2)
End If
Next
End Sub
The second macro does not complete the required edits in Col AG. It duplicates the same edit as the first macro for Col R.
I don't understand how to change the second macro so it affects edits in Cols Z:AK .
???
Download example workbook : Macro Error
Like this:
Sub CollectContractorItems()
Const COL_CONTRACTORS As Long = 25
Dim MyDate As String, cl As Range, ws As Worksheet, wproj, MyMonth
Dim rngDates As Range, dtCol As Long
Set ws = ActiveSheet 'or some specific sheet
MyDate = Format(Date, "mmm") & "-" & Right(Year(Date), 2)
For Each cl In ws.Range("E3:E" & ws.Cells(ws.Rows.Count, "E").End(xlUp).Row).Cells
wproj = Application.Match(cl.Value, ws.Columns(COL_CONTRACTORS), 0)
If Not IsError(wproj) Then
'get the range with dates
Set rngDates = ws.Cells(wproj, COL_CONTRACTORS).Offset(1, 1).Resize(1, 12)
MyMonth = Application.Match(MyDate, rngDates, 0) 'search only in the specific range
If Not IsError(MyMonth) Then
dtCol = rngDates.Cells(MyMonth).Column 'get the column number
ws.Cells(wproj + 2, dtCol) = cl.Offset(, 1)
ws.Cells(wproj + 3, dtCol) = cl.Offset(, 2)
End If
End If
Next
End Sub
I have an invoice from a service provider that I need to format so I can use the data in Excel. But, the formatting is not consistent.
There are three (3) columns:
ID
Description
Amount
Many ID#s on the invoice have a one line (row) description.
But just as many have 2-11 lines (rows) of description.
The ID# is only listed once with each set of description lines.
Up to this point, I have used Excel Formulas. But, all my formulas is making things go very slow.
VBA would be way faster.
What I have done is created an index system looking for new ID#s.
Then I have created a cascading concatenate formula based on the given index system.
The amount has been easy to pull out using a LEFT formula, since the amount lists USD.
I then have a second sheet that does a VLOOKUP off of the first sheet to pull the ID's, final concatenated descriptions, and Amounts.
Our last invoice had 17,427 lines of data with only 1,717 ID#s.
Here is an example of what I am working with:
I want it to look like this:
one of the possible solutions below:
'assume that Id in column `A`, Description in column `B`, Amount in `C` and header in row 1
Sub somecode()
Dim wb As Workbook: Set wb = ActiveWorkbook
Dim sh As Worksheet: Set sh = wb.ActiveSheet
Dim lastRow&: lastRow = sh.Cells(Rows.Count, "B").End(xlUp).Row
Dim idColumn As Range: Set idColumn = sh.Range("A1:A" & lastRow)
Dim dic As Object: Set dic = CreateObject("Scripting.Dictionary")
Dim cl As Range, keyID, valueDescription$, valueAmount$
For Each cl In idColumn
If cl.Value <> "" And Not dic.exists(cl.Value) Then
dic.Add cl.Value, sh.Cells(cl.Row, "B").Value & "|" & sh.Cells(cl.Row, "C").Value
keyID = cl.Value
valueDescription = sh.Cells(cl.Row, "B").Value
valueAmount = sh.Cells(cl.Row, "C").Value
ElseIf cl.Value = "" Then
valueDescription = valueDescription & " " & sh.Cells(cl.Row, "B").Value
dic(keyID) = valueDescription & "|" & valueAmount
End If
Next cl
Set sh = wb.Sheets.Add: sh.Name = "Result " & Date & " " & Replace(Time(), ":", "-")
Dim dkey, xRow&: xRow = 1
For Each dkey In dic
sh.Cells(xRow, "A").Value = dkey
sh.Cells(xRow, "B").Value = Split(dic(dkey), "|")(0)
sh.Cells(xRow, "C").Value = Split(dic(dkey), "|")(1)
xRow = xRow + 1
Next dkey
sh.Columns("A:C").AutoFit
End Sub
test:
I wrote code for you to do this job. Please install it in a standard code module. That is one that you have to insert. None of the existing is suitable.
Option Explicit
Enum Nws ' Worksheet setup (set values as required)
NwsFirstDataRow = 2
NwsNumColumns = 8 ' total number of columns in the sheet
NwsID = 1 ' Columns: 1 = column A
NwsDesc ' undefined = previous + 1
NwsAmt = 5 ' 5 = column E
End Enum
Sub MergeRows()
' Variatus #STO 24 Jan 2020
Dim Wb As Workbook
Dim Ws As Worksheet
Dim Rng As Range
Dim RowArr As Variant
Dim Desc As String, Amt As Double
Dim Tmp As Variant
Dim R As Long
' define workbook and worksheet as required
Set Wb = ActiveWorkbook ' this need not be ThisWorkbook
Set Ws = Wb.Worksheets("Invoice") ' change as appropriate
Application.ScreenUpdating = False
With Ws
R = .Cells(.Rows.Count, NwsDesc).End(xlUp).Row
For R = R To NwsFirstDataRow Step -1
If (R Mod 25) = 3 Then 'NwsFirstDataRow Then
Application.StatusBar = "Another " & R & " rows to process."
End If
Tmp = Trim(.Cells(R, NwsID).Value)
If Len(Tmp) Then
Set Rng = Range(.Cells(R, 1), .Cells(R, NwsNumColumns))
RowArr = Rng.Value
RowArr(1, NwsAmt) = TextToAmount(RowArr(1, NwsAmt))
If Len(Desc) Then
' if you want a comma instead of a line break
' replace Chr(10) with "," in the next line:-
RowArr(1, NwsDesc) = RowArr(1, NwsDesc) & Chr(10) & Desc
RowArr(1, NwsAmt) = RowArr(1, NwsAmt) + Amt
Desc = ""
Amt = 0
End If
With Rng
.Value = RowArr
.Cells.VerticalAlignment = xlTop
.Cells(NwsAmt).NumberFormat = "$#,##0.00"
End With
.Rows(R).AutoFit
Else
Tmp = Trim(.Cells(R, NwsDesc).Value)
If Len(Desc) Then Desc = Chr(10) & Desc
Desc = Tmp & Desc
Tmp = TextToAmount(.Cells(R, NwsAmt).Value)
If Tmp Then Amt = Amt + Tmp
.Rows(R).EntireRow.Delete
End If
Next R
End With
With Application
.ScreenUpdating = True
.StatusBar = "Done"
End With
End Sub
Private Function TextToAmount(ByVal Amt As Variant) As Double
Dim Tmp As Variant
Tmp = Trim(Amt)
If Len(Tmp) Then Tmp = Mid(Tmp, InStr(Tmp, "$") + 1)
TextToAmount = Val(Tmp)
End Function
Before you can run it you need to set the enumerations at the top to tell the code where your data and columns are. Toward the same end, please set the variables for workbook (Wb) and worksheet (Ws) in the procedure itself.
Note that the code adds the price, if any, in the rows that are deleted to the amount set against the remaining item.
Finally, you will see that I programmed the different rows to become lines in a single cell. That isn't what you asked for. If you want the items separate by commas look for the remark in the code where you can change this.
so i have Sheet1 that is use to contain the list of my inventory data. what i want to do is in another sheet(Sheet2). i can search my Sheet1 data and display the data there ( for example when i type cheetos, only the cheetos item got display ). Help me guys, using VBA is okay or other method is also fine.
If your results don't have to be on a different sheet, you could just convert your data to a Table. Select Cells A1:D8 and click on Insert -> Table. Make sure "My table has headers" is clicked and voila!
Once formatted as a table, you can filter Product ID however you need.
If you do need to show these results in another sheet, VBA would be my go-to solution. Maybe something like this:
Public Sub FilterResults()
Dim findText As String
Dim lastRow As Long
Dim foundRow As Long
Dim i As Long
'If there's nothing to search for, then just stop the sub
findText = LCase(Worksheets("Sheet2").Range("D4"))
If findText = "" Then Exit Sub
'Clear any old search results
lastRow = Worksheets("Sheet2").Cells(Rows.Count, 4).End(xlUp).Row
If lastRow > 5 Then
For i = 6 To lastRow
Worksheets("Sheet2").Range("C" & i).ClearContents
Worksheets("Sheet2").Range("D" & i).ClearContents
Worksheets("Sheet2").Range("E" & i).ClearContents
Worksheets("Sheet2").Range("F" & i).ClearContents
Next i
End If
'Start looking for new results
lastRow = Worksheets("Sheet1").Cells(Rows.Count, 1).End(xlUp).Row
foundRow = 6
For i = 2 To lastRow
If InStr(1, LCase(Worksheets("Sheet1").Range("B" & i)), findText) <> 0 Then
Worksheets("Sheet2").Range("C" & foundRow) = Worksheets("Sheet1").Range("A" & i)
Worksheets("Sheet2").Range("D" & foundRow) = Worksheets("Sheet1").Range("B" & i)
Worksheets("Sheet2").Range("E" & foundRow) = Worksheets("Sheet1").Range("C" & i)
Worksheets("Sheet2").Range("F" & foundRow) = Worksheets("Sheet1").Range("D" & i)
foundRow = foundRow + 1
End If
Next i
'If no results were found, then open a pop-up that notifies the user
If foundRow = 6 Then MsgBox "No Results Found", vbCritical + vbOKOnly
End Sub
I would recommend avoiding VBA for this process as it can be done easily with excel's functions. If you would like to do it via VBA one could just loop through the list of products and find a key word, adding it to an array if the "Cheetos" is contained in the specific cell value using a wildcard like so:
This could be modified to run upon the change of the D4 cell if needed, and of course some modifications could be done to ensure that formatting etc can be done to your liking.
Sub test()
Dim wb As Workbook
Dim rng As Range, cell As Range
Dim s_key As String, s_find() As String
Dim i As Long
Set wb = Application.ThisWorkbook
Set rng = wb.Sheets("Sheet1").Range("B2:B8")
s_key = wb.Sheets("Sheet2").Range("D4").Value
wb.sheets("Sheet2").Range("C6:F9999").clearcontents
i = 0
For Each cell In rng
If cell.Value Like "*" & s_key & "*" Then
ReDim Preserve s_find(3, i)
s_find(0, i) = cell.Offset(0, -1).Value
s_find(1, i) = cell.Value
s_find(2, i) = cell.Offset(0, 1).Value
s_find(3, i) = cell.Offset(0, 2).Value
i = i + 1
End If
Next cell
wb.Sheets("Sheet2").Range("C6:F" & 5 + i).Value = Application.WorksheetFunction.Transpose(s_find)
End Sub
I need to create a VBA script in excel that chanages an order number from having "CD" at the front to "CD" at the end so from "CD00001" to "00001CD"
Any help would be awesome. all of the order numbers are in Column B and start at row 5. please help.
What i have so far:
Private Sub OrderNumber_Click()
Dim Val As String
Dim EndC As Integer
EndC = Worksheets("Raw Data Upload").Range("A1048576").End(xlUp).Row
For i = 5 To EndC
Val = Right("B" & i, Len("B" & i) - 2) & Left("B" & i, 2)
Range("B" & i).Value = Val
Next
End Sub
This replaces the order numbers with B5, B6 and so on but if i put this function into Excel itself it works fine.
Like this? DO you want it in column B?
Option Explicit
Private Sub OrderNumber_Click()
Dim i As Long
Dim val As String
Dim EndC As Long
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets("Raw Data Upload")
EndC = ws.Range("A1048576").End(xlUp).Row
For i = 5 To EndC
val = ws.Cells(i, "A")
Range("B" & i).Value = Mid$(val, 3, Len(val) - 2) & Left$(val, 2)
Next i
End Sub
dim beginStr, endStr, originalStr, outputStr as string
dim rng as range
'put the below into a loop, assigning a rng to the desired cell each time
originalStr = rng.value ' Change to chosen range
beginStr = left(originalStr,2)
endStr = right(originalStr, len(originalStr) - 2)
outputStr = endStr + beginStr
Range("B" & i).Value = outputStr
I haven't got a copy of Excel to test this on but it should work.
Simply use:
Right(Range("B" & i), Len(Range("B" & i)) - 2) & Left(Range("B" & i), 2)
An alternative is to set up the cell as a Range():
Sub t()
Dim cel As Range
Dim endC As Long
endC = Worksheets("Raw Data Upload").Range("A1048576").End(xlUp).Row
For i = 5 To endC
Set cel = Range("B" & i)
myVal = Right(cel, Len(cel) - 2) & Left(cel, 2)
Range("B" & i).Value = myVal
Next
End Sub
Currently, when you do Right("B" & i, Len("B" & i) - 2) & Left("B" & i, 2), for row 5, this becomes Right("B5", Len("B5") - 2) & Left("B5", 2) then this evaluates to simply:
Right("B5",0) & Left("B5",2), which is
[nothing] & B5, finally becoming
B5
Note the lack of using B5as a range. Instead it's being treated as a string.
(Also, I'm assuming this is to be run on the ActiveSheet. If not, please add the worksheet before the range, i.e. Worksheets("Raw Data Upload").Range("B" & i)...)
Try this
Private Sub OrderNumber_Click()
Dim cell As Range
With Worksheets("Raw Data Upload")
For Each cell in .Range("B5", .Cells(.Rows.Count, 2).End(xlUp))
cell.Value = Right(cell.Value, Len(cell.Value) - 2) & Left(cell.Value, 2)
Next
End With
End Sub
I have this macro that I use to sum total entries by date. The macro can be run two or three times per day which causes a problem as it wants to sum by date. When I run it and it places the total in the last cell in column E, I want that total number to remain the next time I run the macro, but right now it erases the last entry and sums the new total plus the previous total. How can this be changed? I hope this makes sense the way I am explaining it.
Sub Sum_TodaysDate()
Dim sh As Worksheet
Set sh = ActiveSheet
Dim LastRow As Long, iCount As Long
Dim icell As Range
Dim dSplit As Variant
Dim dIndex As Date
LastRow = sh.Range("D" & Rows.Count).End(xlUp).Row
iCount = 0
For Each icell In sh.Range("D2:D" & LastRow)
dSplit = Split(icell.Value, " ")
dIndex = Format(dSplit(0), "mm/dd/yyyy")
If dIndex = Date Then
iCount = iCount + 1
icell.Offset(0, 1).Value = "|"
End If
Next icell
sh.Range("E" & LastRow).Value = iCount
sh.Range("E" & LastRow).Font.Color = vbRed
Application.ScreenUpdating = True
ThisWorkbook.Save
End Sub
If you wish to leave anything that is currently in column E untouched by the macro, so that previous counts remain in place, change the line that says:
icell.Offset(0, 1).Value = "|"
to
If IsEmpty(icell.Offset(0, 1)) Then
icell.Offset(0, 1).Value = "|"
Endif
If you want the new count to only be the count of records added since the previous time the macro was run (and the data is in date order), this could be modified to be:
If IsEmpty(icell.Offset(0, 1)) Then
icell.Offset(0, 1).Value = "|"
Else
iCount = 0
Endif