Add line and drag down certain content - vba

I have a document which is used to list staff requests. I want a button to add a new row (new staff request) and insert certain things into columns automatically. So I want it to generate the next ref# (XX-01/XX-02 etc.) in one column and also add in the date the request was raised.
Is this possible?
Copy (not by OP) of comment:
The current code I use is:
Private Sub CommandButton1_Click()
Sheets("Sheet2").Range("A11").Select
ActiveCell.EntireRow.Insert Shift:=xlUp
Sheets("Sheet2").Range("A11:AA11").Select
Selection.Borders.Weight = xlThin
Range("A11:AA11").Interior.Color = RGB(255, 255, 255)
End Sub
This adds a new row and formats it as required. However even if I record a new macro to drag down the required fields (ref# etc) the next time I add a row it's added above the previously added row (as row 11 becomes 12 and so will always be added above).

In view of the first comment above (etc) despite the tags you might like to consider non-VBA alternatives. Perhaps enter into a Table (Insert > Tables - Table), say with ="XX-"&ROW()-1 in the Request# column (which should automatically adjust when dragged down) and use Ctrl+; for entry of the date.

Related

Need help placing table at beginning of page

I am placing tables into a Word document. The number of rows in a table may vary but the row height is fixed so I know the maximum number of rows that can fit on a single page. When I reach that max number, I want to add a new page to the document and then insert a new table on it. Sounds simple but I'm running into all sorts of weird results.
Result #1: I have tried using both "Selection.InsertNewPage" and "Selection.InsertBreak(wdPageBreak)". When I do this, 2 pages are added instead of 1.
NumberOfPages = SectionObject.Range.Information(wdNumberOfPagesInDocument)
TableLocation = TableObject.Range 'get the range object of the current table
TableLocation.Collapse(WdCollapseDirection.wdCollapseEnd) 'go to end of table
TableLocation.Select()
WordDocument.Application.Selection.InsertBreak(WdBreakType.wdPageBreak) 'adds 2 pages instead of 1
NumberOfPagesNew = SectionObject.Range.Information(wdNumberOfPagesInDocument)
To try to make this work I wrote this code (it doesn't work)
If NumberOfPagesNew > NumberOfPages + 1 Then
TableLocation = WordDocument.Range.GoTo(wdGoToPage, wdGoToAbsolute, NumberOfPagesNew) 'go to last page of doc
TableLocation.Delete() 'delete that page
TableLocation = WordDocument.Range.GoTo(wdGoToPage, wdGoToAbsolute, NumberOfPagesNew - 1) 'go to new last page
TableLocation.Collapse(WdCollapseDirection.wdCollapseStart) 'move cursor to start of page
End If
'I add a new table using this code
TableObject = WordDocument.Tables.Add(TableLocation, NumberOfRowsNeeded - 1, 5)
But instead of placing my table on the last page, another page gets added and my table is placed on it. The end result is a page with a table, a blank page, and then another page with a table.
Result #2: Another thing I tried was moving my cursor to the end of the table, adding a line break and then placing my table after that. This got around my problem of adding 2 pages instead of 1. However, it adds 2 lines onto the new page instead of 1 which throws off my row counting code. Below is the code I used.
TableLocation = TableObject.Range 'get range object of current table
TableLocation.Collapse(wdCollapseEnd) 'go to end of table
TableLocation.Select()
WordDocument.Application.Selection.InsertBreak(wdLineBreak) 'should add 1 line
TableLocation = WordDocument.Application.Selection.GoToNext(wdGoToLine)
TableLocation.Collapse(WdCollapseDirection.wdCollapseStart)
'I add my next table using this code.
TableObject = WordDocument.Tables.Add(TableLocation, NumberOfRowsNeeded - 1, 5)
This code places my table one line lower than it should.
I don't do a lot of Word coding and I know very little about the intricacies of the selection object so I am sure that is where my problem lies. If someone could show me a good way of accomplishing what I want to do, I would appreciate it.
Thanks in advance,
Darren
Try the following code. Make sure that the number of rows of the table allows it to fit on one page. If this does not work, try with one fewer row in the table, Word might need some extra spaces before / after the table depending on the active format.
Selection.EndKey Unit:=wdStory
ActiveDocument.Tables.Add Selection.Range, 5, 5
Selection.EndKey Unit:=wdStory
Selection.InsertBreak WdBreakType.wdPageBreak
Selection.EndKey Unit:=wdStory
ActiveDocument.Tables.Add Selection.Range, 5, 5
' ...
Of course, this is just to show you an approach that will work and avoids the difficulties that you experienced. You need to adapt the code to your specific task.

Search xlsx for a value, if found replace with different value

So I'm either being too specific, or not searching well enough, because I can't find anything that answers my question. So I came here for help. Here is the situation:
I have an excel sheet, let's call it "CustomerCodeReference", that has a column (A) of Customer Codes (I.E. A2001, A2002, B3900, Q2838, etc, these are NOT necessarily in order) About 3000 of them, and in the next column over (B), I have the group that code represents (I.E. Accounts Primary, Accounts Secondary, Admin Group, User Group, just different names and etc.)
Now, from our company server I can export a spreadsheet of reports from customers, but the problem is, they are labelled by customer code, + a report serial number. The sheet exports as several columns, but one of the columns (G) contains the Customer code and serial number, and each row is a report, sometimes hundreds depending on the date range set. So keeping with the example, let's say it's a report from "Accounts Primary" It's labelled A2001234567 (where everything after the customer code of 'A2001' is the report serial number) sometimes, the report may be from several customers, so that column may have more than one code+SN in it per row.
Given that I have thousands of these codes and groups, is there some macro I can create that every time I export the spreadsheet of reports, I can maybe copy over the "CustomerCodeReference" sheet, and have it automatically search the column of customer codes and SNs, then either replace the code with the actual name, or place the actual name in another (empty) row further back. So I can basically easily reference whose report it is without having to look up the code each time?
I realize I will need to do this in VBA, as there is no formula I can think of that will work.
I have some pro's I think going for me:
-I already have the Master code list, so even though there are thousands of codes, they are all listed in Column A, and the actual name of group they reference is in column B.
-The codes are consistent, a letter, followed by 4 numbers, so always 5 characters long.
-When pulling the report, it always names the worksheet "Customer Reports" so it's easy to reference
These are constants. So I need the actual customer name to either replace the code (while leaving the serial number intact) or if easier, add the actual name to the next empty column on the same row. I also might need to share this with coworkers, so basically just send them the "CustomerCodeReference" sheet and when they add it to all their pulled spreadsheets, it does the same thing. (Macros will be enabled, so no worries there)
Is this too complicated an idea? or can I pull it off? Thanks in advance for the help!
EDIT: I apologize, I complete forgot to attach any sort of code. Here is what I have come up with in VBA, but not sure if I am on the right track as it does not complete the replacement, and I can't quite get it to add values in next available empty cell.
Sub replaceStringInCell()
'declaring my sheet I want to change change customer codes in
Dim CustomerCodes As Range
'declaring strings I will be replacing and with what I will be replacing them
Dim ReportNumbers As Range
Dim CustomerNames As Range
'identifying column I am working to replace, also trying to shoot for next empty column
Set CustomerCodes = PulledReports.Worksheets("Customer Reports").Range("G:G")
'specifying my strings
ReportNumbers = PulledReports.Worksheets("Customer Reports").Range("G:G")
myReplacementString = PulledReports.Worksheets("Customer Code Reference").Range("A:A")
'replace string in cell
CustomerCodes.Value = Replace(Expression:=CustomerCodes.Value, Find:=ReportNumbers, Replace:=CustomerNames)
End Sub
This should do the trick:
Sub stack_overflow()
Dim cust As Worksheet
Dim ref As Worksheet
Set cust = ActiveWorkbook.Worksheets("Customer Reports")
Set ref = ActiveWorkbook.Worksheets("Customer Code Reference")
'Finding next empty column
Dim column As Integer
column = cust.UsedRange.Columns.column + 1
'Filling this columns
For Each cell In cust.Range("G2:G" & cust.Cells(Rows.Count, "G").End(xlUp).Row)
cust.Cells(cell.Row, column).Value = Application.WorksheetFunction.VLookup(Left(cell.Value, 5), _
ref.Range("A2:B" & ref.Cells(Rows.Count, "B").End(xlUp).Row), 2, False)
Next cell
End Sub

VBA Hide Row in table if Specified Column is Empty

I am trying to add to a macro I have that will hide every row that has no text in a column named Authorization. Please see the code I have below, I thought this may be on the right track but it does not hide any rows.
Cells.EntireRow.Hidden = False
For Each cell In Range("Authorization").End(xlUp)
If cell = "" And cell.Offset(1, 0) = "" Then cell.EntireRow.Hidden = True
Next cell
Edited to add how to define a dynamic named range
It is the fact that you have set the whole column to the name "Authorisation" that I think makes your code freeze, because the whole column is 1 million rows (if you have 2007 or above), and the code will still check even blank rows, so its doing it 1 million times. 1 Option is to rather than set it to the whole column, you could use a "Dynamic Named Range" which will expand and grow as data is added. There are several different formulas to do this, but based on the fact your data may contain blanks, this version of the formula will expand down to the last populated row in the column. My example uses colum A as you havent specified what column you are using, so change A to suit your needs.
You need to open the Names manager, from the Formulas tab
From the dialog box, find your "Authorisation" name.
Select it and you should see its current formula at the bottom of the dialog box, replace that with the following formula:
=OFFSET(Sheet1!$A$3,0,0,MATCH("*",Sheet1!$A:$A,-1)-2,1)
In the above formula:
Sheet1 is my sheet, replace it with yours a needed
$A$3 is the starting row of the name, so based on your comments, have set this as column A row 3
0,0, Are defaults you should not need to change
$A$A$ is the column it is counting values, so change as required
-1 is a default, leave as is
-2 is subtracting 2 from the count because we are starting on row 3, so if you change the starting row, change this
the last 1, defined how many columns your named range covers, in your example it is just 1, so this should not need changing.
Once you have defined the name in this way, the code below should work a lot quicker as it will only loop through down to the last row of entered data. There is one possible issue I can see with this and that is if the very last cell in column A is blank, but the rest of the row isn't, this will miss out the last row. I could fix this by using a different column to count what constitutes the last row, but need to know whicj column would always have a value in it.
< Original answer and code>
not sure you code matches the description of what you want it to do, namely you seem to be trying to check the row beneath the current cell as well, is this what you really wanted? Anyhow your syntax is slightly wrong. I have written and tested this and it works, I have swapped your offset around so my code is checking the cell in the named range "Authorisation" and then also checking the cell to the right. Amend to suit your needs
Sub test()
Dim c As Range
For Each c In Range("Authorisation").Cells
If c.Value = "" And c.Offset(0, 1).Value = "" Then c.EntireRow.Hidden = True
Next c
End Sub

Keeping column formulas in a table header

Is it possible to have the formulas that I need applied on columns be saved or applied to a column header or some kind of metadata so that as and when I add new rows to my Excel table the Formulas get applied to the columns?
Scenarion:
I am creating a template Table, which will have no rows at first.
On a separate sheet (or same sheet for that matter) once the user selects the number of rows to be generated in the table, I dynamically add rows to the table using VBA.
The idea is I may not have any rows in the table at beginning OR user may have deleted rows manually.
When I programmatically add new rows, I want the Formulas applied on the cells as well. Most of the formulas I am using are either of the three types:
Structured table reference, Excel functions like SUM, AVERAGE etc and custom function names.
Updated:
Here is what I have tried:
1> tried applying the formula to the header itself.
Result: The header it self changes with #REF! error. I think the behavior is correct. So it's a no-go option.
2> Tried creating one row and apply the formula to the row. That works, but the problem is, I do not want a dummy row to begin with.
3> Using VBA code to add row to the table using
ActiveWorkbook.Worksheets("Sheet3").ListObjects("Table2").ListRows.Add AlwaysInsert:=True
inside a for loop.
The new rows retain the visual style sheets, but does not seem to retain the formulas. Just blank cells.
Could the fomrmulas be in header cell commnets?
And then with VBA add the formula for the current row:
Sub test()
Dim headerCells As Range
Set headerCells = Range("B2:E2")
OnNewRow 3, headerCells
End Sub
Sub OnNewRow(newRow As Integer, headerCells As Range)
Dim headerCell As Range, targetCell As Range, formulaFromComment As String
For Each headerCell In headerCells
formulaFromComment = GetFormulaFromComment(headerCell)
If (formulaFromComment = "") Then _
GoTo NextHeaderCell
Set targetCell = Intersect(headerCells.Worksheet.Rows(newRow), _
headerCell.EntireColumn)
AddFormula newRow, targetCell, formulaFromComment
NextHeaderCell:
Next
End Sub
Sub AddFormula( _
newRow As Integer, _
targetCell As Range, _
formula As String)
formula = Replace(formula, "{ROW}", newRow)
targetCell.formula = formula
End Sub
Function GetFormulaFromComment(headerCells As Range) As String
' TODO
GetFormulaFromComment = "=SUM($C${ROW}:$E${ROW})"
End Function
Just use tables.
If you highlight cells and choose Insert Table from the ribbon, it doesn't just give you formatting and filters. It also, if you build them the right way, stores column formulas once per column instead of once per cell. Also, the formulas are more readable!
For formulas, you can't use cell addresses if you want it to be a single column formula unless they are absolute. (E.g. $A$1, not A1.) Instead, you use [ColumnTitle] for the entire column (where "ColumnTitle" is the actual title of that column) and [#ColumnTitle] for the column value in the same row. So if "Cost" was the title of column B, "RunningTotal" was the title of column C and your formula for C6 was therefore =B6+C5, you'd instead use a formula of =[#Cost]+OFFSET([#RunningTotal],-1,0)] which is longer but much easier to read/maintain/debug, and if you change a column title then the formulas change too! No VBA required. Given this, plus being able change columns for the entire columns at once, plus being able to refer to other columns in other tables without worrying about cell addresses (e.g. MAX(Table1[Cost])), plus being able to style the tables so easily, plus the integration with Power-Query, and VBA support. (See learn.microsoft.com.) Whether VBA or otherwise, add a row to your table and the columns with a single column formulas will automatically carry over into the new row.
Not sure about Table templates or VBA but perhaps there is another option by using =ARRAYFORMULA()
For example, say you had a header row and 3 columns and wanted your last column to be the product of the first two. In cell C2 you could enter the following:
=ARRAYFORMULA(A2:A*B2:B)
This has three benefits:
Skips the first row completely
Effectively applies the formula to every row which is useful if you later decide to insert a row (your question)
Only one location to modify the formula for every single row
Although, it may not be immediately obvious where how/where the cells are being calculated. (hint: ctrl+~ may help)

Monitor Row for Changes ("Last Updated" Column)

I need a way to monitor a row in excel and update a cell at the end of the row for today's date if anything in the row changes.
For Example:
I have a spreadsheet where projects are listed per row. I have a column that lists the "last updated" date, I need that date to update whenever any content in that entire row changes.
Columns A:F are the content
Column G is the "Last Updated" cell.
I hope someone can help.
As your tag suggests excel-vba, I would assume VBA is also an option :
1st you need to see if the current changed row or range is inside your acceptable range ($A:$F), so we use Intersect method.
If yes, in the same sheet, same row but column 'G' enter todays date.
Here is the vba code:
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Application.Intersect(Range("$A:$F"), Target) Is Nothing Then
Range("G" & Target.Row).Value = Date
End If
End Sub
Enter this code inside the same sheet module (in vba editor) where you would want to implement the functionality. Thats it... :)
Edit: This dosen't help much without protection because any one can again change the last edited date. But, thats a totally different question.