VBA: Multiplying by a fraction within a loop - vba

My code works below works but I'd like to add one function. I've got a large data sheet with each line repeated three times. Within each set of three I've added a month twice. The purpose is to smooth out forecasted sales into one month and two months beyond the estimated shipping date. Now I'd like to multiply my the values in column E by factors into column F. The original line in each set of three will =50%*E:E in column F, the second line will have =30%*E:E in column F, and the third line will have =20%*E:E in Column F. This process should be repeated continually for every set of three lines. Problem: My current code does give me the correct value, however the values are two cells lower than they need to be. Thanks for any help in advance! My current code is below:
Public Sub DateAdd()
Dim r As Long
Dim l As Long
Dim Quant As Long
Dim dttTemp As Date
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets("SalesForce Projects")
Application.ScreenUpdating = False
For r = ws.Range("A" & ws.Rows.Count).End(xlUp).Row To 1 Step -1
With ws.Cells(r, 1).EntireRow
.Copy
.Resize(2).Offset(1, 0).Insert Shift:=xlDown
End With
dttTemp = ws.Cells(r, "S").Value
ws.Cells(r + 1, "S").Value = DateSerial(Year(dttTemp), Month(dttTemp) + 1,
Day(dttTemp))
ws.Cells(r + 2, "S").Value = DateSerial(Year(dttTemp), Month(dttTemp) + 2,
Day(dttTemp))
Next r
Application.ScreenUpdating = True
' This is where my code is bad
For l = ws.Range("A" & ws.Rows.Count).End(xlUp).Row To 1 Step -3
Quant = ws.Cells(l, "E").Value
ws.Cells(l, "F").Value = Cells(l, "E") * 0.5
ws.Cells(l + 1, "F").Value = Cells(l, "E") * 0.3
ws.Cells(l + 2, "F").Value = Cells(l, "E") * 0.2
Next l
End Sub

Why not do it in the first loop like below?
Public Sub DateAdd()
Dim r As Long
Dim l As Long
Dim Quant As Long
Dim dttTemp As Date
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets("SalesForce Projects")
Application.ScreenUpdating = False
For r = ws.Range("A" & ws.Rows.Count).End(xlUp).Row To 1 Step -1
With ws.Cells(r, 1).EntireRow
.Copy
.Resize(2).Offset(1, 0).Insert Shift:=xlDown
End With
dttTemp = ws.Cells(r, "S").Value
ws.Cells(r, "F").Value = Cells(r, "E") * 0.5 '\\ First line
ws.Cells(r + 1, "S").Value = DateSerial(Year(dttTemp), Month(dttTemp) + 1, Day(dttTemp))
ws.Cells(r + 1, "F").Value = Cells(r, "E") * 0.3 '\\ Second line
ws.Cells(r + 2, "S").Value = DateSerial(Year(dttTemp), Month(dttTemp) + 2, Day(dttTemp))
ws.Cells(r + 2, "F").Value = Cells(r, "E") * 0.2 '\\ Third line
Next r
Application.ScreenUpdating = True
End Sub

When you say "50% of the value in column E," you could mean the entire value of all 90 in column E or just the value of the three cells in a set. If you mean the first then (I assume that row 1 is headings, so your values start at row 2.) In cell F2 enter the formula
=Sum(E:E)*.5
In cell F3 enter
=SUM(E:E)*.3
In cell F4 enter
=SUM(E:E)*.2
If you mean the other option then enter:
In F2 =Sum(E2:E4)*.5
in F3 =Sum(E2:E4)*.3
In F4 =Sum(E2:e4)*.2
Now select f2:f4. Place your mouse on the bottom right corner and you should see the cursor change to a small black cross. Double-click and the formulas will be replicated down the sheet. If you have more than one sheet to fill then control click on the tab names before starting this process to similtainoiusly copy to all selected sheets.

In this case there is no need to loop backwards. Update the For statement to the following:
For l = 1 to ws.Range("A" & ws.Rows.Count).End(xlUp).Row Step 3
Then it should produce desired results.

Related

Split multiple columns into rows by a delimiter

I have two columns which have delimiters. Both columns will have the same number of delimiters. e.g a;b;c in column A and d;e;f in column B. In some columns there might not be any which is fine.
I want to be able split these columns into the exact number of rows and copy data from other columns as well. Therefore, the example above would have a total of 3 rows and result as below:
Col A Col B
a d
b e
c f
I have found the code below which i modified and works for a specified column but I want to apply this to multiple columns if possible.
Option Explicit
Sub splitcells()
Dim InxSplit As Long
Dim SplitCell() As String
Dim RowCrnt As Long
With Worksheets("Sheet1")
RowCrnt = 1
Do While True
If .Cells(RowCrnt, "L").Value = "" Then
Exit Do
End If
SplitCell = Split(.Cells(RowCrnt, "L").Value, "*")
If UBound(SplitCell) > 0 Then
.Cells(RowCrnt, "L").Value = SplitCell(0)
For InxSplit = 1 To UBound(SplitCell)
RowCrnt = RowCrnt + 1
.Rows(RowCrnt).EntireRow.Insert
.Cells(RowCrnt, "L").Value = SplitCell(InxSplit)
.Cells(RowCrnt, "A").Value = .Cells(RowCrnt - 1, "A").Value
.Cells(RowCrnt, "B").Value = .Cells(RowCrnt - 1, "B").Value
.Cells(RowCrnt, "C").Value = .Cells(RowCrnt - 1, "C").Value
.Cells(RowCrnt, "D").Value = .Cells(RowCrnt - 1, "D").Value
.Cells(RowCrnt, "E").Value = .Cells(RowCrnt - 1, "E").Value
.Cells(RowCrnt, "F").Value = .Cells(RowCrnt - 1, "F").Value
.Cells(RowCrnt, "G").Value = .Cells(RowCrnt - 1, "G").Value
.Cells(RowCrnt, "H").Value = .Cells(RowCrnt - 1, "H").Value
.Cells(RowCrnt, "I").Value = .Cells(RowCrnt - 1, "I").Value
.Cells(RowCrnt, "J").Value = .Cells(RowCrnt - 1, "J").Value
.Cells(RowCrnt, "K").Value = .Cells(RowCrnt - 1, "K").Value
Next
End If
RowCrnt = RowCrnt + 1
Loop
End With
End Sub
Is this possible? Any help is greatly appreciated.
Hello, took me a while, but I actually found a pretty fascinating / useful little procedure in this, so i was playing around a bit.
I've created a little procedure which, where you can specify from which column you want to take the data, and into which column you want to paste the data. With the following invocation:
The parse_column procedure is coded with the following way:
' parses all the values into an array
Private Sub parse_column(columnID As Integer, toColumn As Integer)
Dim totalstring As String
Dim lastrow As Integer
Dim ws As Worksheet: Set ws = Sheets("Sheet1") 'change to whatever sheet you are working with
Dim startingrow As Integer
startingrow = 2 'change to whatever row you want the procedure to start from _
(i skipped first row, because it acts as a header)
With ws
lastrow = .Cells(.Rows.Count, columnID).End(xlUp).Row
End With
Dim columnrange As Range: Set columnrange = Range(Cells(startingrow, columnID), Cells(lastrow, columnID))
For Each Rng In columnrange
totalstring = totalstring + Trim(Rng) ' we'll concatenate all the column values into a one string _
(if you wish to take spaces into accoumt, don't use trim)
Next Rng
Dim buffer() As String
ReDim buffer(Len(totalstring) - 1) '(avoid indexation by 0)
For i = 1 To Len(totalstring)
buffer(i - 1) = Mid(totalstring, i, 1) 'we fill in buffer with values
Next i
' we paste values to specified column
For i = (LBound(buffer)) To UBound(buffer)
ws.Cells((i + startingrow), toColumn).Value2 = buffer(i)
Next i
End Sub
So for example, if you wanted to parse all data from Column 1 (A) to Column 4 (D) you would invoke it in your procedure the following way
Private Sub splitcells()
Call parse_column(1, 4)
End Sub
Beauty of this all is, you can simply loop this for all the columns you have in your sheet by a simple static for loop increment. So for example, if we had 3 columns:
Let's presume we have the following data:
^ Note how Column C doesn't even have to be limited to 3 characters
We could use a simple for loop to loop through all 3 columns and paste them to the 4th next column to the right.
Private Sub splitcells()
Dim i As Integer
For i = 1 To 3
Call parse_column(i, (i + 4))
Next i
End Sub
Would produce the following result:

VBA: Using the DateAdd Function

I have a spreadsheet of with identical rows repeated in sequences of three. For example below A represents a row of information. In column S are dates. I'd like to change the date in column S in the second row of each sequence to one month after the original date and the third row of each sequence to two months after the original date and do continue this for the entire spreadsheet using a loop and the DateAdd function.
Here is the code I have now, but the I'm getting a compile error on the DateAdd function (this is my first time using it):
Sub DateChange()
Dim r As Long
Application.ScreenUpdating = False
For r = Range("A" & Rows.Count).End(xlUp).Row To 1 Step -1
With Cells(r, 1).EntireRow
.Copy
.Resize(2).Offset(1, 0).Insert Shift:=xlDown
End With
Cells(r + 1, "S").Value = DateAdd("m", 1, Cells(r, "S"))
Cells(r + 2, "S").Value = DateAdd("m", 2, Cells(r, "S"))
Next r
Application.ScreenUpdating = True
End Sub
If you experience difficulties using the DataAdd function then you can bypass it like this:
Option Explicit
Sub DateChange()
Dim r As Long
Dim dttTemp As Date
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets("Sheet1")
Application.ScreenUpdating = False
For r = ws.Range("A" & ws.Rows.Count).End(xlUp).Row To 1 Step -1
With ws.Cells(r, 1).EntireRow
.Copy
.Resize(2).Offset(1, 0).Insert Shift:=xlDown
End With
dttTemp = ws.Cells(r, "S").Value
ws.Cells(r + 1, "S").Value = DateSerial(Year(dttTemp), Month(dttTemp) + 1, Day(dttTemp))
ws.Cells(r + 2, "S").Value = DateSerial(Year(dttTemp), Month(dttTemp) + 2, Day(dttTemp))
Next r
Application.ScreenUpdating = True
End Sub
Note, that I am also fully qualifying all references (for good coding practice and to avoid errors). This means I am referring to ThisWorkbook.Worksheets("Sheet1").Cells(r, "S").Value instead of just Cells(r, "S") (as you did). Like this VBA knows in which Excel file to look and at which sheet to look at when searching for cell S3 (for example). Also, I am telling VBA that I am interested in that cell's .Value and not .Value2 or .Formula or anything else.
Let me know if the above helped.

VBA, Excel - Compare rows for multiple conditions, copy and add up result in new sheet

In my Excel worksheet I have several values I need to compare and sum up in case defined criteria match.
The worksheet contains these information:
Name(A), Date(B), Hours worked(C), other information(D-H).
Via VBA I want to check if Hours worked exceeds the value "10". If it does then the code needs to compare if the Name in the previous row equals the Name in the current AND the Date of both rows equal each other.
If all these conditions are true the Hours worked should be summed up and the result should be copied to worksheet 2. Also the needed information like Name, Date and other information should be copied.
For now I tried this:
Sub check_Click()
Dim s1 As Worksheet, s2 As Worksheet
Dim N As Long, i As Long, p As Long
Set s1 = Sheets(1)
Set s2 = Sheets(2)
N = s1.Cells(s1.Rows.Count, "C").End(xlUp).Row
p = 1
For i = 1 To N
If IsNumeric(s1.Range("C" & i)) And s1.Cells(i, "C").Value < 10 Then
Next i
ElseIf s1.Cells(i, "B").Value = s1.Cells(i - 1, "B").Value And s1.Cells(i, "A").Value = s1.Cells(i - 1, "A").Value Then
s1.Range(Cells(i, "A"), Cells(i, "C")).Copy s2.Cells(p + 5, 1)
End If
End Sub
As you might see the code isn't working - unfortunate.
I hope someone can light my way.
The trickiest part is to compare the previous row and sum up the hours.
Thanks in advance
The code is not proper. Next i cannot be used inside If ... Then.
Because of lack continue in VBA you have to change condition also (or use Goto, but this is not my preferred solution):
Sub check_Click()
Dim s1 As Worksheet, s2 As Worksheet
Dim N As Long, i As Long, p As Long
Set s1 = Sheets(1)
Set s2 = Sheets(2)
N = s1.Cells(s1.Rows.Count, "C").End(xlUp).Row
p = 1
For i = 1 To N
If IsNumeric(s1.Range("C" & i)) And s1.Cells(i, "C").Value >= 10 Then
If s1.Cells(i, "B").Value = s1.Cells(i - 1, "B").Value And s1.Cells(i, "A").Value = s1.Cells(i - 1, "A").Value Then
s1.Range(Cells(i, "A"), Cells(i, "C")).Copy s2.Cells(p + 5, 1)
End If
End If
Next i
End Sub
EDIT:
Because values are compared with previous row, for loop neds to start from 2.
Sub check_Click()
Dim s1 As Worksheet, s2 As Worksheet
Dim N As Long, i As Long, p As Long
Set s1 = Sheets(1)
Set s2 = Sheets(2)
N = s1.Cells(s1.Rows.Count, "C").End(xlUp).Row
p = 1
For i = 2 To N ' Iterate from second row
If IsNumeric(s1.Range("C" & i)) And s1.Cells(i, "C").Value >= 10 Then
If s1.Cells(i, "B").Value = s1.Cells(i - 1, "B").Value And s1.Cells(i, "A").Value = s1.Cells(i - 1, "A").Value Then
s1.Range(Cells(i, "A"), Cells(i, "C")).Copy s2.Cells(p + 5, 1)
End If
End If
Next i
End Sub
Your Next i is in a wrong place. It should be after all the If statements.
I think comparing the values is done correctly.
If you have trouble copying hours summed just copy the entire row to sheet2 first and then separately update the hours worked cell with something like this:
Worksheets("sheet2").Cells(i,3).Value = Cells(i,3).Value + Cells(i-1,4).Value
Of course replace with the correct cell coordinates.

Excel VBA: Add data to cell after last used row

The pic shows what happens with my code.
I have a user form and I add the labels of the user form into the selected worksheet. And this is what I tried. Now the problem is why is it that there is one cell not on the same row as the others?
Dim c As Control
For Each c In Me.Controls
If TypeName(c) = "Label" Then
With ActiveSheet
i = i + 1
Dim lastRow As Long
lastRow = .Cells(.Rows.Count, "A").End(xlUp).Row + 1
If c <> "Chapter" Then
.Range(Cells(1, 1), Cells(1, i)).Name = "Chapter1"
.Range("Chapter1").Merge
.Range("Chapter1").Value = "Chapter 1"
.Range("Chapter1").HorizontalAlignment = xlCenter
.Cells(lastRow, i).Value = c.Caption
End If
End With
End If
Next
The problem is that the first time you do .Cells(.Rows.Count, 1).End(xlUp).Row there will not be anything yet in A2, so lastRow will be 1. But once you put the value "No." in that cell the next time you execute that code (with i being 2), A2 will be filled, so now .Cells(.Rows.Count, 1).End(xlUp).Row will return 2, giving you the effect you get: all other values end up one row lower.
There are several way to solve this, but here is one way. Add + IIf(i = 1, 1, 0) the assignment of lastRow:
lastRow = .Cells(.Rows.Count, 1).End(xlUp).Row + IIf(i = 1, 1, 0)

vba code not taking correct value of a cell from file

This is my code:
Dim RowLast As Long
Dim sunmLast As Long
Dim tempLast As Long
Dim filterCriteria As String
Dim perporig As Workbook
Dim x As String
tempLast = ThisWorkbook.Sheets("combine BOMs").Cells(Rows.Count, "E").End(xlUp).Row
Range("D5:G" & tempLast).ClearContents
Range("G5:G" & tempLast).Interior.ColorIndex = 0
tempLast = ThisWorkbook.Sheets("combine BOMs").Cells(Rows.Count, "A").End(xlUp).Row
Range("A5:A" & tempLast).ClearContents
tempLast = ThisWorkbook.Sheets("combine BOMs").Cells(Rows.Count, "B").End(xlUp).Row
'Perpetual
Set perporig = Workbooks.Open("\\Etnfps02\vol1\DATA\Inventory\Daily tracking\perpetual.xlsx", UpdateLinks:=False, ReadOnly:=True)
RowLast = perporig.Sheets("perpetual").Cells(Rows.Count, "A").End(xlUp).Row
perporig.Sheets("perpetual").Cells(3, 1) = "Part Number"
For i = 5 To tempLast
Cells(i, 1) = i - 4
perporig.Sheets("perpetual").AutoFilterMode = False
filterCriteria = ThisWorkbook.Sheets("combine BOMs").Range("B" & i).Value
perporig.Sheets("perpetual").Range("A3:J" & RowLast).AutoFilter Field:=1, Criteria1:=filterCriteria
Counter = perporig.Sheets("perpetual").Cells(RowLast + 1, 1).End(xlUp).Row
If Counter = 3 Then
Cells(i, 5).Value = "Not on perpetual"
Else
ThisWorkbook.Sheets("combine BOMs").Cells(i, 5).Value = WorksheetFunction.Sum(perporig.Sheets("perpetual").Range("H4:H" & RowLast).SpecialCells(xlCellTypeVisible))
x = perporig.Sheets("perpetual").Cells(Cells(RowLast + 1, 1).End(xlUp).Row, 4).Value
MsgBox x, vbOKOnly, perporig.Sheets("perpetual").Cells(RowLast + 1, 1).End(xlUp).Row
ThisWorkbook.Sheets("combine BOMs").Cells(i, 4).Value = x
End If
perporig.Sheets("perpetual").AutoFilterMode = False
Next
perporig.Close savechanges:=False
This is the file from which I am clicking my button (or ThisWorkbook)
This is the perpetual file when it is running on the last row of data:
Notice the difference in D9280: it shows stocking type as "P" in the perpetual file, but "B" in my final result, which comes up in cell D12 in ThisWorkbook. To debug, I created a Msgbox prompt for everytime it gets that value for all rows. For every other row, it gives the correct value ("P"), but for this one, msgbox shows "B". The title of the msgbox is the row number, which shows it is taking the correct row whilr getting the value, just that I don't know why it is taking wrong value. I have tried for different data sources, it seems to be coming up with "B" in wrong places every so often.
In the code, just above the line, I have the line to get the on hand quantity, which it does take correctly (I used xltypevisible to paste values for this field, but that is only because I wanted a sum of the results and this was the only way I knew). It's only this stocking type column which shows wrong values randomly.
Any ideas?
Thanks!
1)
Cells(i, 1) = i - 4
as it is written , it refers to perporig.Cells(i, 1)
is this what you want?
2)
perporig.Sheets("perpetual").Range("A3:J" & RowLast).AutoFilter Field:=1, Criteria1:=filterCriteria
would filter from row 3, while you have headers in row 4 and data from row 5 downwards
change it to
perporig.Sheets("perpetual").Range("A4:J" & RowLast).AutoFilter Field:=1, Criteria1:=filterCriteria
3)
what do you think is Counter doing? Not certainly count visible rows only
Credits to findwindow, I found the answer. The .cells(cells()) part didn't have the correct sheet reference for the inner cells():
Instead of
x = perporig.Sheets("perpetual").Cells(Cells(RowLast + 1, 1).End(xlUp).Row, 4).Value
MsgBox x, vbOKOnly, perporig.Sheets("perpetual").Cells(RowLast + 1, 1).End(xlUp).Row
I used this:
With perporig.Sheets("perpetual")
x = .Cells(.Cells(RowLast + 1, 1).End(xlUp).Row, 4).Value
MsgBox x, vbOKOnly, .Cells(RowLast + 1, 1).End(xlUp).Row
End With
And it worked.
Thanks for your help!