Creating Copies from a Master Workbook using VBA - vba

I have two files, one the file in which I want to run the macro in and another external file.
Within the file that the macro is running in (henceforth the "master" file), there is something that looks like this:
The code that I have so far is this:
Sub test()
For i = 1 To 3
If Not Range("B" & i).Value = "X" Then
Range("C2").Value = Range("A" & i).Value
Calculate 'updates the formula
Range("B" & i).Value = "X" 'update the check
Range("D2").Copy 'this is the tricky part - this is what is needed. The formula links needs to be broken so that only the values remain
Range("D2").PasteSpecial xlPasteValues
ActiveWorkbook.SaveCopyAs "C:\Users\n0269777\Desktop\" & Range("A" & i).Value & ".xlsm" 'the problem with SaveCopyAs is that the formula originally is now overwritten.
'thus I need some way to refer back to the 'master' workbook, the one where the formula has not yet been overwritten
End If
Next i
End Sub
What I want to achieve is that the macro will loop through and check to see if a workbook has been created with the names in column A. Then, it will update the value in "C2". Finally, a copy is saved -- and the formula is overwriten to its value, rather than remain a formula. This is the difficulty in that I cannot simply save a copy of the workbook -- the formula would have been overwritten after the run of the macro.
This is what happens in Type3.xlsm after running the macro. As you can see, the value in "D2" is 1, whereas it should be 3.
I have also considered this method:
Sub test2()
For i = 1 To 3
If Not Range("B" & i).Value = "X" Then
Range("C2").Value = Range("A" & i).Value
Calculate 'updates the formula
Range("B" & i).Value = "X" 'update the check
Set wboor = ActiveWorkbook
fileaddress = ActiveWorkbook.Path & "\" & ActiveWorkbook.Name
Range("D2").Copy 'this is the tricky part - this is what is needed. The formula links needs to be broken so that only the values remain
Range("D2").PasteSpecial xlPasteValues
wboor.SaveCopyAs "C:\Users\n0269777\Desktop\" & Range("A" & i).Value & ".xlsm" 'Perhaps I can save a copy first? Then close the workbook, so the formula is preserved
wboor.Close
Workbooks.Open Filename:=fileaddress 'but then, how do I call the original file, and then loop the macro to run again?
End If
Next i
End Sub
Any suggestions/help would be appreciated!

Not sure it was necessary to start a new question, but anyway try this.
Sub test()
Dim wb As Workbook, s As String, i As Long
For i = 1 To 3
If Not Range("B" & i).Value = "X" Then
Range("C2").Value = Range("A" & i).Value
Calculate 'updates the formula
Range("B" & i).Value = "X" 'update the check
s = "C:\Users\n0269777\Desktop\" & Range("A" & i).Value & ".xlsm"
ActiveWorkbook.SaveCopyAs s
Set wb = Workbooks.Open(s)
wb.Sheets(1).UsedRange.Value = wb.Sheets(1).UsedRange.Value
wb.Close True
End If
Next i
End Sub

You could save a copy of the formula, and put it back into the workbook each time. ie:
Option Explicit
Sub test()
With ThisWorkbook.ActiveSheet
Dim formulaText As String
formulaText = .Range("D2").Formula
Dim i As Long
For i = 1 To 3
If Not .Range("B" & i).Value = "X" Then
.Range("C2").Value = Range("A" & i).Value
Calculate 'updates the formula
.Range("B" & i).Value = "X" 'update the check
.Range("D2").Copy
.Range("D2").PasteSpecial xlPasteValues
ActiveWorkbook.SaveCopyAs "C:\Users\n0269777\Desktop\" & Range("A" & i).Value & ".xlsm"
.Range("D2").Formula = formulaText
End If
Next i
End With
End Sub

Related

How to Save Copies from a Master Workbook

The above picture is a simplified example of what I wish to achieve. Essentially, I need a macro that will go through column A, paste into cell C2, and then save a copy of the workbook. Thus, the end result will be "Type1.xlsm", "Type2.xlsm", etc. At the end of each macro run, the link to the external sheet is broken.
The issue that I am running into is that every time I save a copy of the master workbook, there is no way for the macro to go back to the original workbook. I need some way in which I can recursively call the macro.
Here is what I have so far:
Sub test()
For i = 1 To 5
If Not Range("B" & i).Value = "X" Then
Range("C2").Value = Range("A" & i).Value
Calculate 'updates the formula
Range("B" & i).Value = "X" 'update the check
ActiveWorkbook.SaveAs Filename:=Range("A" & i).Value
ActiveWorkbook.Close
'INSERT CODE TO BREAK THE EXTERNAL LINK
'At this point, how do I refer back to the original workbook?
End If
Next i
End Sub
This is what the output should be:
And an example of what "Type4.xlsm" should be:
I think this should be enough to do it. You can create an object variable pointing to the relevant sheet in the master file (may not be the first). The file containing the code can be referred to as ThisWorkbook.
Sub test()
Dim ws As Worksheet
Set ws = ThisWorkbook.Sheets(1)
For i = 1 To 5
If Not ws.Range("B" & i).Value = "X" Then
ws.Range("C2").Value = ws.Range("A" & i).Value
Calculate 'updates the formula
ws.Range("B" & i).Value = "X" 'update the check
ActiveWorkbook.SaveAs Filename:=ws.Range("A" & i).Value
ActiveWorkbook.Close
'At this point, how do I refer back to the original workbook?
End If
Next i
End Sub
Use SaveCopyAs() method
Sub test()
For i = 1 To 5
If Not Range("B" & i).Value = "X" Then
Range("C2").Value = Range("A" & i).Value
Calculate 'updates the formula
Range("B" & i).Value = "X" 'update the check
ActiveWorkbook.SaveCopyAs Range("A" & i).Value
'At this point, your active workbook and worksheet are still the "original" ones
End If
Next i
End Sub

Excel VBA to make hyperlink for active cell

I want to make a link from active cell in workbook 1 which can I use it in workbook 2. I use the following code which assigned to a button:
With ActiveSheet
.Hyperlinks.Add Range("F6"), _
.Parent.FullName & "#'" & .Name & "'!" & "$A$1", TextToDisplay:="link"
End With
This code made a link with full path and I can use it in any workbook but I need some changes which I could to:
Make the active cell hyperlink not cell A1 which specified in code.
The value in the active cell become text to display arg of hyperlink function.
Thanks
PS after Vityata answere: how can i change Range("F6") to activecell adress?
In order to obtain the active cell value and address, change your code the corresponding places with the following:
ActiveCell.Address
ActiveCell.Value
I find it just to close this topic.
Sub Button36_Click()
Dim newRange As Range
Set newRange = Range(ActiveCell, ActiveCell.Offset(numRows, numCols))
With ActiveSheet
.Hyperlinks.Add Anchor:=newRange, _
Address:=.Parent.FullName & "#'" & .Name & "'!" & ActiveCell.Address, TextToDisplay:=ActiveCell.Text
End With
End Sub
try this
Sub add_links_Input_Column()
Dim lRow As Long
Dim ColHead As String
ColHead = InputBox("Enter Column Letter", "Identify Column", [c1].Value)
If ColHead = "" Then Exit Sub
With ActiveSheet
lRow = .Range(ColHead & .Rows.Count).End(xlUp).Row
For Each c In .Range(ColHead & "2:" & ColHead & lRow)
ActiveSheet.Hyperlinks.Add anchor:=c, Address:=c.Value
Next
End With
End Sub

using a variable name in my VBA sumif formula

I have built a code that performs a sumif on another workbook that I open using the get open filename dialogue box. I intend to use this formula daily, and hence the workbook where I intend to obtain the information and the workbook where I intend to paste the results will continue to have varying names basing on the date of the day.
I get a type mismatch on the SUMIF formula. Please help.
Sub flextab()
Dim LastCol As Integer, LastRow As Long
' Get the dimensions
With Sheets("Flex")
LastCol = .Cells(2, .Columns.Count).End(xlToLeft).Column
End With
'Insert a column where required.
Cells(2, LastCol - 1).EntireColumn.Insert
'Insert the date
Cells(2, LastCol - 1).Value = Date - 1
'Insert the balance sheet balances for the day
Dim wb1 As Workbook, wb2 As Workbook
Dim FileName1 As String, FileName2 As String
Dim BalSheet As Worksheet
Dim Ret1
FileName1 = ThisWorkbook.Name
'~~> Get the first File
Ret1 = Application.GetOpenFilename("Excel Files (*.xls*), *.xls*", _
, "Please select the Balance sheet for the day")
If Ret1 = False Then Exit Sub
Set wb2 = Workbooks.Open(Ret1)
FileName2 = wb2.Name
Workbooks(FileName1).Activate
'let's get a reference to the worksheet with the source values
Set BalSheet = Workbooks(FileName2).Worksheets("Sheet1")
With Worksheets("Flex").Range(Cells(5, LastCol - 1), Cells(109, LastCol - 1))
'let's put in our SUMIF formulas
.Formula = "=SUMIF(" & BalSheet.Range("B2:B20000") & "," & Worksheets("Flex").Range("A5") & " , " & BalSheet.Range("n2:n20000") & ")"
'let's convert the formulas into values
.Value = .Value
End With
wb2.Close SaveChanges:=False
Set wb2 = Nothing
Set wb1 = Nothing
End Sub
You are adding ranges to a string, while ranges are objects. Try this:
"=SUMIF(" & BalSheet.Range("B2:B20000").Address & "," & Worksheets("Flex").Range("A5").Value & " , " & BalSheet.Range("n2:n20000").Address & ")"
instead of this:
"=SUMIF(" & BalSheet.Range("B2:B20000") & "," & worksheets("Flex").Range("A5") & " , " & BalSheet.Range("n2:n20000") & ")"
You are working with 2 workbooks, but you are using some worksheets without qualifier.
If you have focus to the wrong workbook this will fail if the workbook that is active does not have a Flex worksheet.
So change this:
Worksheets("Flex").Range(Cells(5, LastCol - 1)
to this:
wbX.Worksheets("Flex").Range(Cells(5, LastCol - 1)
where the X is the correct workbook.

Trying to use Excel VBA to skip blank rows in my copy/paste loop

I have a copy / paste loop for line items in an Excel file that exports data from these line items into an Excel-based form and saves each form by the value in Row B. My issue is that these line items are divided into 3 different tables on the same sheet, each with a different number of line items to be copied. Furthermore, each table is separated by 2 blank rows.
What I need the macro to do for me:
Start at line 17 and copy all line items in the first table until it hits a blank row - this varies from 1 to 600 rows.
Skip to SecondTable and perform the same functions.
Repeat for ThirdTable
Ignore some of the declarations as I deleted a large chunk of code for readability. I figured I would need 3 separate copy/paste loops to accomplish this (I've only included 2 here) and I tried using .Find to reference the start of the second/third tables. The macro runs as normal through the first table, but doesn't stop when it hits a blank row and fails when it tries to save a file based on the value of an empty cell. I believe the issue lies with the EndOne = .Range("B" & .Rows.Count).End(xlUp).Row argument right under With wsSource. Instead of counting only the non-blank rows of the first table, it counts the number of rows through the end of the third table.
Sub CopyToForm()
Dim wbSource As Workbook, wbForm As Workbook
Dim wsSource As Worksheet, wsForm As Worksheet
Dim formpath As String, foldertosavepath As String
Dim EndOne As Long, EndTwo As Long, EndThree As Long, i As Integer
Dim strProcessingFormPath As String
'Dim strCancel As String
'Dim strFilt As String
'Dim intFilterIndex As Integer
'Dim strDialogueFileTitle As String
Dim SecondTable As String
Dim ThirdTable As String
Set wbSource = ThisWorkbook '~~> Write your code in Indication Tool.xls
Set wsSource = wbSource.Sheets("Indication Tool") '~~> Put the source sheet name
With wsSource
'~~> Counts how many rows are in the Indication Tool
EndOne = .Range("B" & .Rows.Count).End(xlUp).Row
If EndOne < 17 Then MsgBox "No data for transfer": Exit Sub
For i = 17 To EndOne
Set wbForm = Workbooks.Open(formpath) '~~> open the form
Set wsForm = wbForm.Sheets("Processing Form") '~~> Declare which worksheet to activate
'~~> Proceed with the copying / pasting of values
.Range("B" & i).Copy wsForm.Range("F7:K7")
.Range("C" & i).Copy: wsForm.Range("D8").PasteSpecial xlPasteValues
.Range("C" & i).Copy: wsForm.Range("D30").PasteSpecial xlPasteValues
.Range("D" & i).Copy: wsForm.Range("H29").PasteSpecial xlPasteValues
.Range("E" & i).Copy: wsForm.Range("E29").PasteSpecial xlPasteValues
.Range("F" & i).Copy: wsForm.Range("D33").PasteSpecial xlPasteValues
.Range("G" & i).Copy: wsForm.Range("K30").PasteSpecial xlPasteValues
.Range("H" & i).Copy: wsForm.Range("P33").PasteSpecial xlPasteValues
.Range("L" & i).Copy: wsForm.Range("H32").PasteSpecial xlPasteValues
.Range("R" & i).Copy: wsForm.Range("D87").PasteSpecial xlPasteValues
'.Range("C5:M5").Copy: wsForm.Range("E102").PasteSpecial xlPasteValues
'~~> Save the form using the value in cell i,B
wbForm.SaveAs .Range("B" & i).Value & ".xls"
wbForm.Close
Set wbForm = Nothing
Set wsForm = Nothing
Next
End With
With wsSource
SecondTable = .Range("B:B").Find("SecondTable").Row
EndTwo = .Range("B" & .Rows.Count).End(xlUp).Row
For i = Second Table + 1 To EndTwo
Set wbForm = Workbooks.Open(formpath) '~~> open the form
Set wsForm = wbForm.Sheets("Processing Form") '~~> Declare which worksheet to activate
'~~> Proceed with the copying / pasting of values
.Range("B" & i).Copy wsForm.Range("F7:K7")
.Range("C" & i).Copy: wsForm.Range("D8").PasteSpecial xlPasteValues
.Range("C" & i).Copy: wsForm.Range("D30").PasteSpecial xlPasteValues
.Range("D" & i).Copy: wsForm.Range("H29").PasteSpecial xlPasteValues
.Range("E" & i).Copy: wsForm.Range("E29").PasteSpecial xlPasteValues
.Range("F" & i).Copy: wsForm.Range("D33").PasteSpecial xlPasteValues
.Range("G" & i).Copy: wsForm.Range("K30").PasteSpecial xlPasteValues
.Range("H" & i).Copy: wsForm.Range("P33").PasteSpecial xlPasteValues
.Range("L" & i).Copy: wsForm.Range("H32").PasteSpecial xlPasteValues
.Range("R" & i).Copy: wsForm.Range("D87").PasteSpecial xlPasteValues
.Range("C5:M5").Copy: wsForm.Range("E102").PasteSpecial xlPasteValues
'~~> Save the form using the cells i,B
wbForm.SaveAs .Range("B" & i).Value & ".xls"
wbForm.Close
Set wbForm = Nothing
Set wsForm = Nothing
Next
End With
End Sub
Am I on the right track with the .Find and a separate copy/paste loop for each table? I realize this is a complex problem and I appreciate any time you take to spend helping me out.
Am I on the right track with the .Find and a separate copy/paste loop for each table?
Not exactly. The code inside those loops is largely the same, so it is a good candidate for subroutine. This will make your code more human-readable, and also makes it easier to maintain since there will only be one place to make revisions, instead of multiple (imagine if you needed to do 10 different iterations, or 1,000 -- you wouldn't possibly write 1,000 different loops to do the same thing!!)
Consider this instead (I observe a few obvious errors which I will correct, but this is not tested). What I have done is to take your several loops, and consolidate them in to a single subroutine. Then we send some information like where the table starts and where it ends, to that subroutine:
Sub CopyStuff(ws as Worksheet, tblStart as Long, tblEnd as Long)
We will send it: wsSource, and the other variables will be used/re-used to determine the start/end of each table. I removed the redundant variables (unless they need to be re-used elsewhere, having two variables EndOne and EndTwo is unnecessary: we can make use of more generic variables like tblStart and tblEnd which we can reassign for subsequent tables.
In this way it is a lot more apparent that we are processing multiple tables in an identical manner. We also have only a single For i = ... loop to manage, should the code require changes in the future. So it is easier to comprehend, and easier to maintain.
Sub CopyToForm()
Dim wbSource As Workbook 'No longer needed in this context: wbForm As Workbook
Dim wsSource As Worksheet 'No longer needed in this context: wsForm As Worksheet
Dim formpath As String, foldertosavepath As String
Dim tblEnd As Long, tblStart As Long, i As Integer
Dim strProcessingFormPath As String
Dim tblStart as Integer: tblStart = 16
Set wbSource = ThisWorkbook '~~> Write your code in Indication Tool.xls
Set wsSource = wbSource.Sheets("Indication Tool") '~~> Put the source sheet name
With wsSource
'~~> Counts how many rows are in the Indication Tool
tblEnd = .Range("B" & .Rows.Count).End(xlUp).Row
If tblEnd < 17 Then GoTo EarlyExit '## I like to use only one exit point from my subroutines/functions
CopyStuff wsSource, tblStart, tblEnd
tblStart = .Range("B:B").Find("SecondTable").Row + 1
tblEnd = .Range("B" & .Rows.Count).End(xlUp).Row
CopyStuff wsSource, tblStart, tblEnd
'And presumably...
tblStart = .Range("B:B").Find("ThirdTable").Row + 1
tblEnd = .Range("B" & .Rows.Count).End(xlUp).Row
CopyStuff wsSource, tblStart, tblEnd
End With
Exit Sub
EarlyExit:
MsgBox "No data for transfer"
End Sub
Private Sub CopyStuff(ws As Worksheet, tblStart as Long, tblEnd as Long)
Dim wbForm as Workbook, wsForm as Worksheet, i As Long
With ws
For i = tblStart to tblEnd
Set wbForm = Workbooks.Open(formpath) '~~> open the form
Set wsForm = wbForm.Sheets("Processing Form") '~~> Declare which worksheet to activate
'~~> Proceed with the copying / pasting of values
.Range("B" & i).Copy wsForm.Range("F7:K7")
.Range("C" & i).Copy: wsForm.Range("D8").PasteSpecial xlPasteValues
.Range("C" & i).Copy: wsForm.Range("D30").PasteSpecial xlPasteValues
.Range("D" & i).Copy: wsForm.Range("H29").PasteSpecial xlPasteValues
.Range("E" & i).Copy: wsForm.Range("E29").PasteSpecial xlPasteValues
.Range("F" & i).Copy: wsForm.Range("D33").PasteSpecial xlPasteValues
.Range("G" & i).Copy: wsForm.Range("K30").PasteSpecial xlPasteValues
.Range("H" & i).Copy: wsForm.Range("P33").PasteSpecial xlPasteValues
.Range("L" & i).Copy: wsForm.Range("H32").PasteSpecial xlPasteValues
.Range("R" & i).Copy: wsForm.Range("D87").PasteSpecial xlPasteValues
'.Range("C5:M5").Copy: wsForm.Range("E102").PasteSpecial xlPasteValues
'~~> Save the form using the value in cell i,B
wbForm.SaveAs .Range("B" & i).Value & ".xls"
wbForm.Close
Set wbForm = Nothing
Set wsForm = Nothing
Next
End With
End Sub

Trying to combine a VBA copy/paste loop with a VBA 'Open File' Dialog Box

I have an Excel macro that copies and pastes line items from a source file to an Excel-based form. It opens a form template and saves each line item as it's own file then loops through the remaining rows. Right now I have a file path built into the code that refers to the form template needed, but I need the user to be able to choose which file they want to use as their template. I have code for both of these processes but I haven't been able to combine them. My example below results in a Compile Error: Variable not defined.
Here's what I have so far:
Option Explicit
Sub CopyToForm()
Dim wbSource As Workbook, wbForm As Workbook
Dim wsSource As Worksheet, wsForm As Worksheet
Dim formpath As String, foldertosavepath As String
Dim lrow As Long, i As Integer
Set wbSource = ThisWorkbook '~~> Write your code in Indication Tool.xls
Set wsSource = wbSource.Sheets("Indication Tool") '~~> Put the source sheet name
'~~> This opens the Processing Form template.
formpath = "C:\File path.xls"
'~~> Prompts user with Open File Dialog Box
strCancel = "N"
strWorkbookNameAndPath = Application.GetOpenFilename _
(FileFilter:=strFilt, _
FilterIndex:=intFilterIndex, _
Title:=strDialogueFileTitle)
'~~> Exits If No File Selected
If strWorkbookNameAndPath = "" Then
MsgBox ("No Filename Selected")
strCancel = "Y"
Exit Sub
ElseIf strWorkbookNameAndPath = "False" Then
MsgBox ("You Clicked The Cancel Button")
strCancel = "Y"
Exit Sub
End If
Workbooks.Open strWorkbookNameAndPath
'~~> This declares path where the Individual forms will be saved.
foldertosavepath = "C:\File path\Forms\"
With wsSource
'~~> Counts how many rows are in the Indication Tool
lrow = .Range("B" & .Rows.Count).End(xlUp).Row
If lrow < 18 Then MsgBox "No data for transfer": Exit Sub
For i = 18 To lrow
Set wbForm = Workbooks.Open(formpath) '~~> open the form
Set wsForm = wbForm.Sheets("Processing Form") '~~> Declare which worksheet to activate
'~~> Proceed with the copying
.Range("B" & i).Copy wsForm.Range("F7:K7")
.Range("C" & i).Copy wsForm.Range("D8")
.Range("C" & i).Copy wsForm.Range("D30")
.Range("D" & i).Copy wsForm.Range("H29")
.Range("E" & i).Copy wsForm.Range("E29")
.Range("F" & i).Copy wsForm.Range("D33")
.Range("G" & i).Copy wsForm.Range("J30:K30")
.Range("H" & i).Copy wsForm.Range("P33")
.Range("I" & i).Copy wsForm.Range("L33:N33")
.Range("L" & i).Copy wsForm.Range("H32")
.Range("R" & i).Copy wsForm.Range("D87")
.Range("C2:F2").Copy wsForm.Range("J101:M101")
.Range("C3:M3").Copy wsForm.Range("E102:O102")
'~~> Save the form using the client name
wbForm.SaveAs foldertosavepath & .Range("B" & i).Value & ".xls"
'~~> These steps are for formatting, as I haven't figured out how paste values only
Set wbForm = Workbooks.Open(formpath)
Cells.Select
Selection.Copy
wsForm.Activate
'~~> This allows the format to be pasted into the updated Form
wsForm.Unprotect
Cells.Select
Selection.PasteSpecial Paste:=xlPasteFormats
wsForm.Protect
ActiveWorkbook.Save
ActiveWorkbook.Close
wbForm.Close ([SaveChanges:=False])
Set wbForm = Nothing
Set wsForm = Nothing
Next
End With
End Sub
When I debug the error, Sub CopyToForm() is highlighted in yellow and strCancel = is selected. Is there a way to set the user-chosen file as the formpath? Thanks in advance for your help, this has been a thorn in my side for quite some time.
Option Explicit is declared, and strCancel isn't declared as a variable.
Add Dim strCancel As String to your code