Excel VBA writes data to second workbook, but starts opening read-only versions because " _ is already open - vba

I have some VBA script in one Excel Workbook that has three subs that each either read from a second Workbook. Each of the subs uses the following algorithm (simplified to distill the interaction with the second book):
Public Sub EditRemote()
Dim remoteDataSheet As Worksheet
Dim source As String 'Source worksheet name
Dim target As String 'Target worksheet name
Dim path As String
Dim wkbName As String
source = "CountData"
path = ThisWorkbook.Worksheets("Parameters").Range("B2").Value
wkbName = ThisWorkbook.Worksheets("Parameters").Range("A2").Value
target = "CountData"
Application.EnableCancelKey = xlDisabled
Set localDataSheet = ThisWorkbook.Sheets(source)
If Not WorkbookIsOpen(wkbName) Then
Workbooks.Open (path)
End If
Set remoteDataSheet = Workbooks(wkbName).Sheets(source)
remoteDataSheet.Cells(1,1) = localDataSheet.Cells(1,1)
remoteDataSheet.Cells(1,2) = localDataSheet.Cells(1,2)
Workbooks(wkbName).Close SaveChanges:=True
End Sub
Function WorkbookIsOpen(targetWorkbook As String) As Boolean
Dim testBook As Workbook
On Error Resume Next
Set testBook = Workbooks(targetWorkbook)
If Err.Number = 0 Then
WorkbookIsOpen = True
Else:
WorkbookIsOpen = False
End If
End Function
There is also a pivot table in this Workbook that draws its data from the second file though an external data connection as well. The issue that is plaguing me is that it seems that not initially but after a few operations, these subs stop making the edits properly and instead it opens a read only copy of the second Workbook. When I try to open the second workbook manually I get a message saying that the file is already open and is locked for editing. Right now both files are local to my computer and couldn't be opened by anyone else. What am I missing to be sure that I can make the code work as intended?

I made some modification to your code, ran it a few times, and didn't get your "Read-only" message.
In your code the line of declaring localDataSheet is missing, added Dim localDataSheet As Worksheet , also added Dim remoteWb As Workbook for the remote workbook.
(didn't modify your Funtion WorkbookIsOpen code).
Sub EditRemote Code
Option Explicit
Public Sub EditRemote()
Dim remoteDataSheet As Worksheet
Dim localDataSheet As Worksheet
Dim source As String 'Source worksheet name
Dim target As String 'Target worksheet name
Dim path As String
Dim wkbName As String
Dim remoteWb As Workbook
source = "CountData"
path = ThisWorkbook.Worksheets("Parameters").Range("B2").Value
wkbName = ThisWorkbook.Worksheets("Parameters").Range("A2").Value
target = "CountData"
Application.EnableCancelKey = xlDisabled
Set localDataSheet = ThisWorkbook.Sheets(source)
' check if workbbok already open
If Not WorkbookIsOpen(wkbName) Then
Set remoteWb = Workbooks.Open(path)
Else
Set remoteWb = Workbooks(wkbName) ' workbook is open >> set remoteWb accordingly
End If
Set remoteDataSheet = remoteWb.Sheets(source)
remoteDataSheet.Cells(1, 1) = localDataSheet.Cells(1, 1)
remoteDataSheet.Cells(1, 2) = localDataSheet.Cells(1, 2)
Workbooks(wkbName).Close SaveChanges:=True
End Sub
Just to verify the data in your Excel "Parameters" sheet, the screen-shot below shows the data I used for my testing.
Cell A2 contains the "Clean" workbook name.
Cell B2 contains workbbok "full" name - path + "clean" workbook name.

After some further testing to diagnose the issue, I found that there was nothing wrong with the VBA code, but rather the external data connection to the remote Workbook was locking that Workbook every time I refreshed the data in the pivot table that used the external data connection as its source. It isn't unlocking the file when it is done refreshing, and that leaves the file locked until I close the Workbook with the pivot table. Now I just need to solve that problem.

Related

Opening workbook from file and updating link in original workbook

I'm trying to write a macro in VBA, that will open another Workbook using a PathFile specified in a cell (this works), updates link in workbook in which macro is used (doesn't work) and closes the PathFile workbook (works)
This is a code:
Sub UpdateRaw()
Dim CurrWb As Workbook
Dim FilePath As String
Dim book As Excel.Workbook
Set CurrWb = ActiveWorkbook
FilePath = Range("I1").Value
Dim app As New Excel.Application
app.Visible = True 'so we can see whether correct file is being opened
Set book = app.Workbooks.Open(FilePath)
CurrWb.Activate
Worksheets("Raw_vs_Actual").EnableCalculation = False
Worksheets("Raw_vs_Actual").EnableCalculation = True
book.Close SaveChanges:=False
app.Quit
Set app = Nothing
End Sub
Going step by step I found that command CurrWb.Activate doesn't take me back to my original Workfile. My suspicion is that by opening new Excel Application I can't get back to the CurrWb (ActiveWorkbook). Is there a workaround? I need this so my INDIRECT function doesn't return #REF.
I'm using Excel 2010 in case it's important.
I think Set book = app.Workbooks.Open(FilePath) shall be enough, but if not refresh the workbook:
book.RefreshAll
for opened workbook. For the workbook that contains the macro, use
ThisWorkbook.RefreshAll

copy worksheet excel VBA fine tuning. Copy from certain cells, paste in certain cells & worksheet naming,

EDIT: I updated some code and I am getting an now error message now as well. Error is shown below.
I found a piece of code on this site and copies a worksheet to another workbook like I want, however I want to do some fine tuning. I need the source worksheet to copy all the information in the cells from cell "A11" - "J11" until the information in the rows end.
The copied information needs to be posted in cells "A4" - "J4" and down the rows until there is no more information to paste.
When the worksheet is copied it needs to be named a certain name (let's say it needs to be named "Customer Information") however, there will be a current sheet in the destination workbook by the same name. Is there a way to copy it over without adding (1) to the end of the name since there is already a tab with that name.
Here is the code I currently have
Sub UpdateCustomerInformation()
Dim wkbSource As Workbook
Dim wkbDest As Workbook
Dim shttocopy As Worksheet
Dim wbname As String
Dim destSheet As Worksheet
' check if the file is open
Ret = Isworkbookopen("\\showdog\service\\Service_job_PO\Customer Information - Query.xls")
If Ret = False Then
' open file
Set wkbSource = Workbooks.Open("\\showdog\service\\Service_job_PO\Customer Information - Query.xls")
Else
'Just make it active
'Workbooks("C:\stack\file1.xlsx").Activate
Set wkbSource = Workbooks("Customer Information - Query.xls")
End If
' check if the file is open
Ret = Isworkbookopen("\\showdog\service\Service Jobs.xlsm")
If Ret = False Then
' open file
Set wkbDest = Workbooks.Open("\\showdog\service\Service Jobs.xlsm")
Set destSheet = wkbDest.Sheets("Customer Information")
'perform copy
Set shttocopy = wkbSource.Sheets("Report")
shttocopy.Range("A11:J11").End(xlDown).Copy
Error gets thrown here: "object doesn't support this property or method"
wkbDest.Sheets(destSheet.Name).Range("A4:J4").End(xlDown).Paste
I am unsure why though. I thought I had everything correct, but I obviously don't
Application.DisplayAlerts = False
wkbDest.Save
wkbDest.Close
Application.DisplayAlerts = True
'close file
Else
'Just make it active
'Workbooks("C:\stack\file2.xlsx").Activate
Set wkbDest = Workbooks("Service Jobs.xlsm")
Set destSheet = wkbDest.Sheets("Customer Information")
'perform copy
Set shttocopy = wkbSource.Sheets("Report")
shttocopy.Range("A11:J11").End(xlDown).Copy
wkbDest.Sheets(destSheet.Name).Range("A4:J4").End(xlDown).Paste
End If
End Sub
Function Isworkbookopen(filename As String)
Dim ff As Long, ErrNo As Long
Dim wkb As Workbook
Dim nam As String
wbname = filename
On Error Resume Next
ff = FreeFile()
Open filename For Input Lock Read As #ff
Close ff
ErrNo = Err
On Error GoTo 0
Select Case ErrNo
Case 0: Isworkbookopen = False
Case 70: Isworkbookopen = True
Case Else: Error ErrNo
End Select
End Function
I am unsure how to accomplish the tasks mentioned above. Any help would be greatly appreciated!
To copy the whole range form shttocopy (using what #Rgo said and presuming there are no blank cells inside the range in shttocopy) to the bottom of the existing range in destsheet + 1 row (again presuming no blanks in column "A").
With shttocopy
.Range(.Range("A11"), .Range("A11").End(xlDown).End(xlToRight)).Copy _
destsheet.Range("A4").End(xlDown).Offset(1)
End With
This code can be changed.
shttocopy.Range("A11:J11").End(xlDown).Copy
wkbDest.Sheets(destSheet.Name).Range("A4:J4").End(xlDown).Paste
to
shttocopy.Range("A11:J11").Copy destsheet.range("A4")
You do not need to put destSheet.name inside of Sheets()
While the macro recorder will create separate copy/paste instructions, it should be rewritten like above.
End(xlDown) is typically used to locate the next available row for copying and should not be used this way.
If you want to copy one row at a time use End(xlUP) to find the next available row:
lRow = DestSheet.Range("A65536").end(xlUP).row + 1
shttocopy.Range("A1").Copy destsheet.range("A" & lrow)
If you need to identify the bottom right address of the range you are copying from use the following:
dim aRange as range
set aRange = shttocopy.range(Range("A1").address, Cells(shttocopy.usedrange.rows.count, shttocopy.usedrange.columns.count).address)
Shttocopy.arange.copy ...
The Copy on one line and paste method on another often throws errors and it is advisable to replace it. as suggested above.

Excel copy/paste data based on tab names in multiple files

I have a (hopefully) easy situation. I'm seeking to automate this process with a VBA macro.
I have an Excel spreadsheet (let's call this data.xls) that has multiple tabs with the following names (this is just an example):
Sucralose
Cellulose
Dextrose
Each tab simply has a column of data in it.
I want to know if there is a simple way to copy all the tabs of data to another spreadsheet with specific formatting for further operations (let's call this reduction.xls) based on the tab naming.
For example:
I want to copy Column A of tab Sucrose, Dextrose, Cellulose FROM data.xls TO Column F of the same named tabs (already existing) in reduction.xls [Sucrose, Dextrose, Cellulose].
I'm looking for a "true/false" type statement where the column from each tab in data.xls will be pasted into reduction.xls assuming the same exact named tab exists, without any need for interaction from the user.
Code posted below has the following features:
It is prepared for easily handling an arbitrary number of tabs. You have to modify only 3 lines, as indicated: 1) The list of tab names, 2) the name of the source workbook, 3) the name of the target workbook.
It is "protected" against missing tabs in the target workbook.
The structure is likely self-explanatory (although this might be a subjective statement).
.
Sub copy_tab(ByVal wsName As String)
Dim wbnamesrc As String
Dim wbnametrg As String
wbnamesrc = "source.xlsm" ' Change this line
wbnametrg = "Book8" ' Change this line
Dim wbsrc As Workbook
Dim wbtrg As Workbook
Set wbsrc = Workbooks(wbnamesrc)
Set wbtrg = Workbooks(wbnametrg)
If (WorksheetExists(wsName, wbnametrg)) Then
Dim rngsrc As Range
Dim rngtrg As Range
Application.CutCopyMode = False
wbsrc.Worksheets(wsName).Range("A:A").Copy
wbtrg.Worksheets(wsName).Range("A:A").PasteSpecial
End If
End Sub
Sub copy_tabs()
Dim wslist As String
Dim sep As String
wslist = "Sucralose|Cellulose|Dextrose|Sheet1" ' Change this line
sep = "|"
Dim wsnames() As String
wsnames = Split(wslist, sep, -1, vbBinaryCompare)
Dim wsName As String
Dim wsnamev As Variant
For Each wsnamev In wsnames
wsName = CStr(wsnamev)
Call copy_tab(wsName)
Next wsnamev
End Sub
Public Function str_split(str, sep, n) As String
' From http://superuser.com/questions/483419/how-to-split-a-string-based-on-in-ms-excel
' splits on your choice of character and returns the nth element of the split list.
Dim V() As String
V = Split(str, sep)
str_split = V(n - 1)
End Function
' From http://stackoverflow.com/a/11414255/2707864
Public Function WorksheetExists(ByVal wsName As String, ByVal wbName As String) As Boolean
Dim ws As Worksheet
Dim ret As Boolean
ret = False
wsName = UCase(wsName)
For Each ws In Workbooks(wbName).Worksheets
If UCase(ws.Name) = wsName Then
ret = True
Exit For
End If
Next
WorksheetExists = ret
End Function
Personally I would create the VBA in a separate workbook that you can open and execute separately from the other 2 interacting workbooks.
Thus I defined three dimension. wbk = workbook with code in it. wbk1 = the source workbook where you will copy from. wbk2 - the destination workbook where you will paste to.
You will have to edit the file locations as well as the Ranges. Say if you only wanted A1:A100, provided it is the same number of rows each time. If not I suggest increasing the rows far past what you anticipate the row count will be so you make sure you don't miss any.
Go to a new workbook
Hold Alt and press F11 key
Click Insert -> Module
Paste the below code in the window and update file locations and copy/paste range as needed
Press Run Macro (green play button) or hit F5 with your cursor in the code
Sub DataTransfer()
Dim wbk, wbk1, wbk2 As Workbook
'Workbook with VBA in it.
Set wbk = ActiveWorkbook
'Define destination workbook
Set wbk1 = Workbooks.Open("C:\data.xls")
'Define Source workbook
Set wbk2 = Workbooks.Open("C:\reduction.xls")
Call wbk1.Worksheets("Sucralose").Range("A1:A100000").Copy
Call wbk2.Worksheets("Sucralose").Range("F1:F100000").PasteSpecial(xlPasteValues)
Application.CutCopyMode = False
Call wbk1.Worksheets("Cellulose").Range("A1:A100000").Copy
Call wbk2.Worksheets("Cellulose").Range("F1:F100000").PasteSpecial(xlPasteValues)
Application.CutCopyMode = False
Call wbk1.Worksheets("Dextrose").Range("A1:A100000").Copy
Call wbk2.Worksheets("Dextrose").Range("F1:F100000").PasteSpecial(xlPasteValues)
Application.CutCopyMode = False
End Sub

Command Button to modify cell value in unknown name open workbook

So the issue I'm having is we have a schedule program made via excel, that is set to replace all user names and shift times with "####" and where it would normally display names inputs "Contact blah blah for new version." This occured on 1/1/15. For now they can backdate their computer to a date prior to 1/1/15 and once they type a value in to any cell the worksheet runs and all their data re-appears. We have locations across the country that saves the file every two weeks to Wildcardname.xls I'm looking for a way to program a command button that finds the other random name opened workbook, goes to hidden sheet "help" and changes the value of Cell A184 to "01/01/2016" or any date I plug in. Which would remove the "####" issue and replace it with the originally inputed values. The user could then save the file and carry on.
I was browsing through various help boards and found this..prompts a user to select the workbook. This would be the workbook that needs changed.
http://www.excelforum.com/excel-programming-vba-macros/695467-copy-values-from-a-worksheet-to-another-workbook-source-workbook-name-unknown.html
Sub CopyData()
Dim DstRng As Range
Dim DstWkb As Workbook
Dim DstWks As Worksheet
Dim FileFilter As String
Dim Filename As String
Dim SrcRng As Range
Dim SrcWkb As Workbook
Dim SrcWks As Worksheet
Dim SheetName As String
SheetName = "Output Table"
FileFilter = "Excel Workbooks (*.xls), *.xls"
Filename = Application.GetOpenFilename(FileFilter, , "Open Source Workbook")
If Filename = "False" Then
MsgBox "Open Source File Canceled."
Exit Sub
End If
Set SrcWkb = Workbooks.Open(Filename)
Set SrcWks = SrcWkb.Worksheets(SheetName)
Set SrcRng = SrcWks.Range("A2:H20")
FileFilter = "Excel Workbooks (*.xls), *.xls"
Filename = Application.GetOpenFilename(FileFilter, , "Open Destination Workbook")
If Filename = "False" Then
MsgBox "Open Destination File Canceled."
Exit Sub
End If
Set DstWkb = Workbooks.Open(Filename)
Set DstWks = DstWkb.Worksheets(SheetName)
Set DstRng = DstWks.Range("A2:H20")
SrcRng.Copy Destination:=DstRng
End Sub
Can this be modified to accomplish what I want to complete?
I can't post an image yet, so here's a link to a mock up. Before shot of the program on the left, and on the right is what I want it to look like.
http://i528.photobucket.com/albums/dd330/DLN1223/mockup.jpg
Hopefully this description makes since....
Thanks in advance for your help.
This is what I use:
Dim FileToOpen As Variant
Dim WKbook as workbook
FileToOpen = Application.GetOpenFilename("Excel files (*.xlsx),*.xlsx", , "Select Workbook to Open")
If FileToOpen = False Then Exit Sub 'quit on cancel
Set Wkbook = Workbooks.Open(FileToOpen, False, False)
With this, I can the set the value I want, and save changes
Wkbook.Sheets("help").Range("A184")=#1/1/2016#
Wkbook.Close SaveChanges:=True
depending on the filetype, you may need to change Excel files (*.xlsx),*.xlsx to Excel files (*.xls),*.xls

How to create new workbook(blank) in excel and prompt user for name and file location

Suppose I want a create a new excel workbook(blank) using macros and the user should be prompted to enter the file name and location, the new file should not overwrite some existing file with the same name that user has entered
The name of file that user has entered should be saved in some cell of the existing sheet in which I am running the macros
Till now I have been using this
Workbooks.Add
Dim file_name As Variant
ActiveWorkbook.file_name = _
Application.GetSaveAsFilename(FileFilter:="Microsoft Excel file (*.xls), *.xls")
I don't know how to copy the name to the cell
I am getting error message: Run-time error'438' with the above code
I don't know how to fix it, I am a beginner, some help please
Option Explicit
Public Function wbNewWB(rngCelltoWriteTheFilenameTo As Range) As Workbook
Dim wbNew As Workbook
Dim vFilename As Variant
Dim bFileexists As Boolean
Set wbNew = Workbooks.Add
bFileexists = True
Do
vFilename = Application.GetSaveAsFilename(fileFilter:="Microsoft Excel file (*.xls), *.xls")
bFileexists = Dir(vFilename) <> ""
Loop Until vFilename <> False And bFileexists = False
wbNew.SaveAs Filename:=vFilename
rngCelltoWriteTheFilenameTo.Value = vFilename
Set wbNewWB = wbNew
End Function
Sub test()
Dim wbMyWB
Set wbMyWB = wbNewWB(ThisWorkbook.Worksheets(1).Range("A1"))
Debug.Print "Finished"
End Sub
As far as I understood your requirements correctly, this might be a solution: the function wbNewWB adds a new workbook, then asks for a file name and checks whether a file with this file name already exists. If a filename is provided and does not already exist, the workbook is saved under the provided file name and the file name is stored as a value in the cell you determine as an input parameter to the function. Finally the new workbook is provided as the return value of the function.
To illustrate the usage of the function I further added a sub, where the function is called.
To use this code, you should simply add a module to your existing project and copy & paste the code.