Excel VBA Billing Userform input data questions - vba

So I'm trying to code for an Excel VBA billing archive/tracker form for my work, and I am very new to excel vba. Any help would be much appreciated. Essentially, I want my userform to input billing date on the bottom table, then take the sum of the parts selected (A1-A19) and input the sum to the nearest, empty cell to the left of the input billing date. If it's the first billing cycle, then I just want to call the dollar amount in the Grand Total Engagement and put that value to the right of the input billing date. Attached are my snippits of the Excel sheet, and userform. Thanks so much community!
Excel Sheet
BillingArchiveUserform
'Creates "LastRow" variable
If Range("D47").Value = "" Then
LastRow = 47
Else
Main.Range("D46").Select
LastRow = Selection.End(xlDown).Row
End If
i = 47
Do While i <= LastRow
If BillingDate.Value = Range("D" & i).Value Then
If CmBBill.Value = "Yes" Then
'Range("D" & i).Offset( 'resume from here and figure it out, but I want to choose the nearest empty cell to the right of date
'Input project specific information (i.e. the sum of the parts that need to be billed)
Else
i = i + 1
End If
Loop
Cells(LastRow + 1, 4).Value = BillingDate.Value 'Inputs Billing Date
'2. Choose column of ProjectToBill based on value in textbox
Main.Range("E5").Select
LastCol = Selection.End(xlToRight).Column

1) You do not need a loop for this
2) You do not have any logic that checks if the userform checkboxes are set to true or false and then what to do if they are true/false.
3) Offset Property will be need to put information in lowe table in next available row. Offset can be sued to adjust left or right based obn your criteria, which will most likely be IF's
4) if first billing cycle you can direct the input to the first location in the lower area.
5) seeing in how your just dumping these into a spreadsheet, having to declare variables from textboxes in userforms is wont be needed. I do mention it however, because if you ever need to manipulate that information in VBA, it may need to be done so. And it looks like that simple math needs ot be done, so if you run into issues that may be the problem.
This should get you started in the right place
Private Sub commandbutton_click()
If CheckBox1.Value = True Then Sheet1.Range("E4").Value = "Wherever you hid the predetermined value"
If Sheet1.Range("d47").Value = vbNullString Then
Sheet1.Range("d47").Value = BillingDateTextBox.Value
Else
Sheet1.Cells(Sheet1.Rows.Count, 1).End(xlUp).Offset(1, 0).Value = BillingDateTextBox.Value
Sheet1.Cells(Sheet1.Rows.Count, 1).End(xlUp).Offset(0, 1).Value = "Wherever you hid the predetermined value"
End If
End Sub

Related

Hide Rows based on Date in Column

I've searched and searched the internet and all of the forums and I've been piecing together code and still can't figure this out. I've tried For loops and For Each loops and still can't get it right. In my sheet, I have all of my dates in Column D. I want to hide rows by month. I want to be able to click a macro button and only show dates in January, or February, or etc.
This is what I currently have:
Sub January()
'
'
'
Dim cell As Range
For Each cell In Range("Date")
If cell.Value = "" Then
cell.EntireRow.Hidden = False
End If
If cell.Value < "1/1/2018" Or cell.Value > "1/31/2018" Then
cell.EntireRow.Hidden = True
End If
Next cell
End Sub
When I run this, it just hides anything that isn't an empty cell. I've cycled between defining cell as a Range and as a Variant and it's the same either way.
ETA:
It is working now and it took help from everybody. I really appreciate it! Here's what I ended with..
Sub January()
'
'
'
Dim cell As Range
For Each cell In Range("Date")
If cell.Value = "" Then
cell.EntireRow.Hidden = False
ElseIf cell.Value < CDate("1/1") Or cell.Value > CDate("1/31") Then
cell.EntireRow.Hidden = True
End If
Next cell
End Sub
I removed the years from the code so that I don't have to change any coding for future years.
Your current setup would qualify all dates as either < or > the respective date comparison.
If you are trying to hide rows for January in this code, then you need to use AND instead of OR
And be sure you use >= & <= to include those first and last dates.
If cell >= "1/1/2018" AND cell <= "1/31/2018" Then
If you are trying to hide rows not January then your < and > are transposed:
If cell < "1/1/2018" OR cell > "1/31/2018" Then
Alternative approach: If you've got Excel 2013 or later, simply add a Table Slicer and filter on a MONTH column generated with =DATE(YEAR([#Date]),MONTH([#Date]),1) as shown below:
Or otherwise use a PivotTable and a Slicer:
To see how easy it is to set up a PivotTable, see VBA to copy data if multiple criteria are met
Ultimately, I believe this is the code you're looking for:
Sub January()
Dim cell As Range
Application.ScreenUpdating = False
For Each cell In Range("Date")
'If date falls on or after January 1, AND on or before January 31, don't hide the row
If cell.Value >= CDate("1/1/2018") And cell.Value <= CDate("1/31/2018") Then
cell.EntireRow.Hidden = False
Else
'If the cell doesn't contain anything or isn't in January, hide the row
cell.EntireRow.Hidden = True
End If
Next cell
Application.ScreenUpdating = True
End Sub
You need to use And logic, not Or logic. Or logic always returns TRUE unless both expressions are false or there is a null involved. Because of this, the code stopped looking at your logical statement once it evaluated to true since every date you had - I'm assuming - fell after January 1, 2018. This in turn caused the rows to hide unexpectedly.
Additionally, I would convert the strings you have into dates using CDate. It helps Excel understand what is going on a bit better and makes your code easier to understand to outsiders. Another good practice to work on is adding comments to code. I think we've all learned the hard way by leaving comments out of code at some point or another.
One last thing: if you're planning to have buttons for each month, consider doing one procedure for all of them and having variables populate the date ranges, potentially using input boxes to get the values from the user. It'll save you a lot of headaches if you ever decide to change things up in the future.
Untested, written on mobile. I am just providing an alternative approach which tries to use MONTH and YEAR. Some may find this approach easier to understand.
Option Explicit
Sub January()
Dim cell As Range
For Each cell In Range("Date")
If cell.Value = "" Then
cell.EntireRow.Hidden = False
Else
cell.EntireRow.Hidden = (Month(cell.Value) = 1) and (year(cell.Value) = 2018)
End if
Next cell
End sub
I will actually go with Slicers and Table.
But if you call VBA your neat solution then I'd say abandon the loop.
Have nothing against it but if Excel already have the functionality, then use it.
It is like a discount or a promotion that we need to take advantage of.
So instead of loop, why not just filter?
Dim lr As Long, r As Range
With Sheet1 '/* sheet where data reside */
.AutoFilterMode = False '/* reset any filtering already applied */
lr = .Range("D" & .Rows.Count).End(xlUp).Row '/* get the target cells */
Set r = .Range("D1:D" & lr) '/* explicitly set target object */
'/* filter without showing the dropdown, see the last argument set to false */
r.AutoFilter 1, ">=2/1/2018", xlAnd, "<=2/28/2018", False
End With
Above is for February of this year, you can tweak it to be dynamic.
You can create separate sub procedure for each month of you can just have a generic one.

Dynamic Named Range with Moving Data Below

I am trying to create a dynamic named range that I can use for a data validation list. I use these all time but in this case I have information that is housed below the range that cannot be counted in the range. Also, I have a macro that insert rows within this range that do need to be counted.
I normally would use something like this if nothing else was in the column: =OFFSET($A$1,0,0,COUNTA($A:$A),1)
I need to start this one down the page a little ways so I used:
=OFFSET($A$24,0,0,COUNTA($A$24:$A24),1)
Notice I have removed the "$" before the last "24" in the formula hoping it would expand accordingly, but that does not seem to be consistent.
Basically, I need the COUNTA range to only include a range of cells that will always be growing and shrinking.
I'm not bad in VBA and am open to a solution that might include looping through a range of cells and stopping once it reaches a cell that's value equals a certain text string (in the case in would be .Value = "Request 1"). But I am a little apprehensive about feeding a form or ActiveX Control, as this has caused me issues in the past with viewing and printing functionality.
I used a the following code to create a range elsewhere in the workbook that I could then easily use to create a dynamic named range:
Sub UpdateEntities()
Dim i As Long, x As Long
i = 24
x = 1
Sheets("Values").Range("AH:AH").ClearContents
Do While Cells(i, 1).Value <> "REQUEST 1"
Cells(i, 1).Select
If ActiveCell.Value <> "" Then
Sheets("Values").Cells(x, 34).Value = ActiveCell.Value
i = i + 1
x = x + 1
Else
i = i + 1
End If
Loop
End Sub

Excel VBA code for MID/Splitting text in cell based on fixed width

I apologize if there is already the same question asked elsewhere with an answer however I have been unable to find it so here I go.
I will also mention that I am a VBA beginner, mostly playing around with codes obtained from other people to get what I want.
I currently have data in Columns A-D, with the information in column C being the important column. Everything else should be ignored.
I have a line of text in cell C1 of sheet1. It is 25 characters long and resembles the following:
4760-000004598700000000000
I have over ~970,000 rows of data and need to pull out the information found within each of these cells into two different cells in another sheet.
I cannot simply use a formula due to the number of records (excel crashes when I try).
If using the mid function for C1, I would enter something like (C1,2,3) and (C1,5,11). (except it would be for each cell in column C)
The leading zeroes between the + or - and the beginning of the first non-zero value are of no consequence but I can fix that part on my own if need be.
Ideally the information would be pulled into an existing sheet that I have prepared, in the A and B columns. (IE:sheet2)
For example, using the text provided above, the sheet would look like:
A|B
760|-0000045987 or -45987
I have looked into array, split and mid codes but I had troubles adapting them to my situation with my limited knowledge of VBA. I am sure there is a way to do this and I would appreciate any help to come up with a solution.
Thank you in advance for your help and please let me know if you need any additional information.
It sounds like what you're after could be achieved by the Text to Columns tool. I'm not sure whether you're trying to include this as a step in an existing macro, or if this is all you want the macro to do, so I'll give you both answers.
If you're just looking to split the text at a specified point, you can use the Text to Columns tool. Highlight the cells you want to modify, then go to the Data tab and select "Text to Columns" from the "Data Tools" group.
In the Text to Columns wizard, select the "Fixed Width" radio button and click Next. On step 2, click in the data preview to add breaks where you want the data to be split - so, in the example you gave above, click between "760" and "-". Click Next again.
On step 3, you can choose the format of each column that will result from the operation. This is useful with the leading zeroes you mentioned - you can set each column to "Text". When you're ready, click Finish, and the data will be split.
You can do the same thing with VBA using a fairly simple bit of code, which can be standalone or integrated into a larger macro.
Sub RunTextToColumns()
Dim rngAll As Range
Set rngAll = Range("A1", "A970000")
rngAll.TextToColumns _
DataType:=xlFixedWidth, _
FieldInfo:=Array(Array(0, 2), Array(3, 2))
With Sheets("Sheet4").Range("A1", "A970000")
.Value = Range("A1", "A970000").Value
.Offset(0, 1).Value = Range("B1", "B970000").Value
End With
End Sub
This takes around a second to run, including the split and copying the data. Of course, the hard-coded references to ranges and worksheets are bad practice, and should be replaced with either variables or constants, but I left it this way for the sake of clarity.
How about this:
Sub GetNumbers()
Dim Cel As Range, Rng As Range, sCode As String
Application.ScreenUpdating = False
Application.DisplayAlerts = False
Set Rng = Sheets("Sheet1").Range("C1:C" & Sheets("Sheet1").Range("C1048576").End(xlUp).Row)
For Each Cel In Rng
Sheets("Sheet2").Cells(Cel.Row, 1).Value = Mid(Cel.Value, 2, 3)
sCode = Mid(Cel.Value, 5, 11)
'Internale loop to get rid of the Zeros, reducing one-by-one
Do Until Mid(sCode, 2, 1) <> "0" And Mid(sCode, 2, 1) <> 0
sCode = Left(sCode, 1) & Right(sCode, Len(sCode) - 2)
Loop
Sheets("Sheet2").Cells(Cel.Row, 2).Value = sCode
Next
Application.ScreenUpdating = True
Application.DisplayAlerts = True
End Sub
I think there's an array formula thing that would do this, but I prefer the brute force approach. There are two ways to fill in the fields, with a procedure or with a function. I've done both, to illustrate them for you. As well, I've purposely used a number of ways of referencing the cells and of separating the text, to illustrate the various ways of achieving your goal.
Sub SetFields()
Dim rowcounter As Long, lastrow As Long
lastrow = ActiveSheet.Cells(Rows.Count, 3).End(xlUp).Row 'get the last row in column "C"
For rowcounter = 1 To lastrow 'for each row in the range of values
'put the left part in column "D"
ActiveSheet.Range("D" & rowcounter) = FieldSplitter(ActiveSheet.Cells(rowcounter, 3).Text, True)
'and the right part in the column two over from colum "C"
ActiveSheet.Cells(rowcounter, 3).Offset(0, 2) = FieldSplitter(ActiveSheet.Cells(rowcounter, 3).Text, False)
Next rowcounter
End Sub
Function FieldSplitter(FieldText As String, boolLeft As Boolean) As String
If boolLeft Then
FieldSplitter = Mid(FieldText, 2, 3) 'one way of getting text from a string
Else
FieldSplitter = Left(Right(FieldText, 16), 5) ' another way
End If
'Another useful function is Split, as in myString = Split (fieldtext, "-")(0) This would return "4760"
End Function

Selecting last row, range, vba

I'm fairly new to vba and have a rather simple problem. Can someone please help:
Instead of selecting the specific cell, I want my vba macro to go to the bottom of the column of interest, skip, and two cells below it do the following:
Range("W72").Select
Selection.NumberFormat = "General"
ActiveCell.FormulaR1C1 = "Null_value"
Range("X72").Select
Selection.NumberFormat = "General"
ActiveCell.FormulaR1C1 = "=R[-2]C[1]-SUM(R[-2]C[-8]:R[-2]C[-6])"
As you can see the code above refers to the specific cells W72 and X72. Currently, the last entry in these columns are in W70 and X70 but next month my dataset will get bigger so W72 and X72 aren't the right locations to do the actions above. How do I correct for this such that my vba code is automatically going to the bottom of W(n):X(n), skips one row and in W(n+2), X(n+2) performs the code above.
Also, my formula above (ActiveCell.FormulaR1C1) also is referring to specific cells, in my case Row 70 several columns to the left, but as you probably tell, this too has the same issue since the row referencing changes each month. I need to get my vba to have the formula pick up the last row of those columns, the columns are P,Q,R.
Thanks for any help you can provide.
Update: Part of my same working project, I would greatly appreciate if anyone can help with this too. Thank you:
Hi All,
I currently have an input box for a variable that changes everymonth:
r_mo = Application.InputBox(prompt:="Enter the reporting month as YYYYMM (Eg:201604). Errors in this entry will result in errors in the results.")
This prompts an input box which one has to manually enter into... However, I want to automate this process and eliminate the need for an input box. Isn't there a now function in vba that will automatically generate today's date.
From a now, or system function all I want to do is extract the year in four digits and the month in two digits.
So for example, if we're in decemeber 2016
Sub asasdas ()
"Now function"
r_mo = YYYYMM ' automatically updated from "now function"
End Sub
I appreciate any help you can give me and thank you so much all.
You can get the last populated row of a given column (W in my example) in VBA with the following code:
Dim ws As Worksheet : Set ws = ThisWorkbook.Worksheets("MySheetNameHere")
lastRow = ws.Cells(ws.Rows.Count, "W").End(xlUp).Row
Naturally, if you then add 2 to lastRow you have the cell you are looking for.
I'd do it like
Sub asdf()
Range("w1048576").End(xlUp).Offset(2, 0).Select 'gets the last row
With Selection
.NumberFormat = "General"
.FormulaR1C1 = "Null_value"
End With
ActiveCell.Offset(, 1).Select
With Selection
.NumberFormat = "General"
.FormulaR1C1 = "=R[-2]C[1]-SUM(R[-2]C[-8]:R[-2]C[-6])"
End With
End Sub
If you want a more detailed answer you're going to have to make a new question but for your second question try this.
Sub Now()
Dim myDate As String
myDate = Date
myDate = Format(myDate, "yyyymm")
Debug.Print myDate '201606 output for June 10th 2016
End Sub

I want to count the number of entries made in a particular column in multiple excel sheets on present day. What could a possible solution?

I am trying to count the number of entries made in a particular column(s) in multiple sheets on present day through VBA
Example of my data:
Here I want to write a code which can help me count the number of RPCs on a daily basis.
You should try to code it yorself before asking for help on here, we help people whos code is not working, but it was a simple bit of coding to do for you.
You will need to change the columns which it is looking down, from the screenshot it is column J but it may be different
Sub CodeForUser()
Dim LastRow As Long
Range("A65000").End(xlUp).Select 'change coloumn if needed
LastRow = ActiveCell.Row
[A1].Select 'change coloumn if needed
Do Until ActiveCell.Row >= LastRow
If ActiveCell.Value = "RPC 1&2" Then ' if there is more than just RPC 1&2 you will need to do is remov the "then" and add "And ActiveCell.Value = "what everelse you want" and then add then to the end
X = X + 1
End If
ActiveCell.Offset(1, 0).Select
Loop
'displays message box showing how many RPCs there are
MsgBox ("Number of RPCs is " & X)
End Sub