First time poster here. I've been reading tutorials/guides all day and made a lot of strides, but am having a tough time figuring out how to write a macro that does what I want to do.
I get around 100 time sheets per week that are then copied and imported into an accounting software. The sheets are all based off of a template, are in separate workbooks, and have a worksheet titled "Pre Import Time Card" within them. I copy the values from each book's pre import worksheet into a new file and upload them to our accounting software as a batch.
I want to have a macro open each file automatically, copy the range A1:I151 on each workbook, and then paste the values into a new worksheet. Because of the import templates design, this inevitably leads to many blank rows within the specified range. I would like to delete any blank rows as a final step.
UPDATE: I HAVE COPIED THE CODE TO REFLECT WHAT I CURRENTLY HAVE.Also a list of new problems is below.
pasting to next unused row is not working
I need to figure out how to kill the old file / not have it enter the same file twice.
I would like to suppress the "Privacy warning on VBA / Active X controls" dialog that comes up at each save.
It's not currently copying correctly. I'm getting a bug at the rDest.Resize line.
Object variable or With Block Variable not set.
I had it running when using file names in an array, but decided that was unnecessary and to use a For.. Each loop.
Sub CopySourceValuesToDestination()
Dim wbDest As Workbook
Dim wbSource As Workbook
Dim sDestPath As String
Dim sSourcePath As String
Dim aFile As String
Dim shDest As Worksheet
Dim rDest As Range
Dim i As Long
Dim TSSize As Object
Dim objFso As Object 'New FileSystemObject
Dim objFolder As Object 'Folder
Dim objFile As Object 'File
Set objFso = CreateObject("Scripting.FileSystemObject")
sDestPath = "Z:\Dropbox\My Documents\TimeSheets\Processed\"
sSourcePath = "Z:\Dropbox\My Documents\TimeSheets\Copying\"
'Open the destination workbook at put the destination sheet in a variable
Set wbDest = Workbooks.Open(sDestPath & "Destination.xlsm")
Set shDest = wbDest.Sheets(1)
Set objFolder = objFso.GetFolder(sSourcePath)
For Each objFile In objFolder.Files
aFile = objFile.Name
Set objWb = Workbooks.Open(sSourcePath & aFile)
'find the next cell in col A
Set rDest = shDest.Cells(xlLastRow + 1, 1)
'write the values from source into destination
TSSize = wbSource.Sheets(4).Range("A1").End(xlDown).Row
rDest.Resize(TSSize, 9).Value = wbSource.Sheets(4).Range("A1:I" & TSSize).Value
wbSource.Close False
wbDest.SaveAs sDestPath & "Destination.xlsm"
wbDest.Close
Kill sSourcePath & wbSource
Next
End Sub
Function xlLastRow(Optional WorksheetName As String) As Long
' find the last populated row in a worksheet
If WorksheetName = vbNullString Then
WorksheetName = ActiveSheet.Name
End If
With Worksheets(1)
xlLastRow = .Cells.Find("*", .Cells(1), xlFormulas, _
xlWhole, xlByRows, xlPrevious).Row
End With
End Function
Provded your datarange in the time sheet is continuous you can replace
rDest.Resize(151,9).Value = wbSource.Sheets(1).Range("A1:I151").Value
with
var for storing
dim TSsize as long
TSsize = wbSource.Sheets(1).Range("A1").end(xlDown).Row
rDest.Resize(TSsize,9).Value = wbSource.Sheets(1).Range("A1:I" & TSsize).Value
This is prevent the empty rows from getting into your sheet in the first place.
If the each time sheet is not a continuous range you can interate through the rows looking for empty rows and deleting them. Let me know if that is the case and i will update my answer.
Related
I'm writing a macro to loop thru some excel-files, in a specific directory, to update some values in each file. The value to be updated is located on a worksheet which name is the same in every file. The worksheet I try to update is protected in every file. My problem is when I update those files (I only have to unprotect it) and save my changes, all cells (in entire workbook) that I have formatted as 'currency' or 'accounting' and which contain a formula will be formatted as 'Custom'. Can anyone help me out with this problem?
EDIT: If I stop my macro before closing the file (opened by the macro) and then save the updated file manually, all my formatted cells have the correct format when I open it again.
This is my simplest code I have tried:
Sub moveBill()
Dim ws As Worksheet
Dim rng As Range
Dim objFs As Object
Dim objFolder As Object
Dim file As Object
Dim folder As String
Dim fileCount As Integer
Dim pw As String
pw = "1234"
folder = "C:\Excel-test"
Set objFs = CreateObject("Scripting.FileSystemObject")
Set objFolder = objFs.GetFolder(folder)
fileCount = 0
' Loop through all the files in the folder.
For Each file In objFolder.Files
If Left(file.Name, 12) <> "FINAL REPORT" Then
Dim src As Workbook ' The source workbook.
Set src = Workbooks.Open(file.Path, True)
Set ws = src.Worksheets("Fakturaunderlag")
Set rng = ws.Range("E1")
fileCount = fileCount + 1
ws.Unprotect pw
rng.value = 1
MsgBox "Go to next file"
src.Close (True)
Set src = Nothing
End If
Next
MsgBox fileCount & " files have been updated"
End Sub
This is before I run my code:
This is after I run my code:
This is the 'Custom' formatting that appears after update om my files:
I have two Workbooks. I need to take a String from WB1 (I iterate through Column C in WB1, not every cell contains a String, but when a cell contains a string this is the one I want to copy), find it in WB2 and replace it with another String from WB1 (in the same row, but column A). Here is what I have so far:
' Checks if a given File is already open
Public Function FileInUse(sFileName) As Boolean
On Error Resume Next
Open sFileName For Binary Access Read Lock Read As #1
Close #1
FileInUse = IIf(Err.Number > 0, True, False)
On Error GoTo 0
End Function
Sub copyPaste()
Dim destWB As Workbook
Dim destSH As Worksheet
Dim fileName As String
Dim curCell As Range
Dim oldName As Range
Dim result As Range
' turn off screen refresh, recalculate formula to speed up sub
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
' For i = 2 To Rows.Count
For i = 2 To 5
fileName = "C:\Users...\" & Workbooks("Ressources calculation.xlsm").Worksheets("Tests costs").Cells(i, 2)
If Not FileInUse(fileName) Then
Set destWB = Workbooks.Open(fileName)
Set destSH = destWB.Sheets("Qualification Matrix")
destSH.Activate
End If
Set curCell = Workbooks("Ressources calculation.xlsm").Sheets("Tests costs").Cells(i, 3)
Set oldName = Workbooks("Ressources calculation.xlsm").Sheets("Tests costs").Cells(i, 1)
If Not IsEmpty(curCell) Then
curCell.Copy
Set result = destWB.Sheets("Qualification Matrix").Cells.Find(What:=oldName.Text, LookIn:=xlFormulas, LookAt:=xlWhole, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:=True, MatchByte:=True)
If Not result Is Nothing Then
result.PasteSpecial
End If
End If
Next i
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
End Sub
I have added a MsgBox in the "If Not result" clause which never triggers, so I guess it is not finding the cell. It seems to extract the strings I need to use (in curCell and oldName) fine though (checked also with MsgBox). The cells in which it should search and replace are merged cells, if that makes a difference. I also tried out different values for Cells.Find (leaving all optional parameters, tried all possibilities for lookIn and lookat, MatchByte, tried oldName.Value instead).
This is the first time I'm doing something with Excel Macros/VBA, the last few hours were spend with a lot of trial and error without any result. So I'm sure what I have so far is far from optimal, but I hope that someone can help me with it.
Edit: I narrowed it down a bit. I now activate destSH right before Cells.Find and tried just using a hardcoded example String as a parameter, which works. So I guess the problem is not the find statement but how I try to extract the information I'm looking for with find.
Edit2: As requested, here is a short example walkthrough:
I have a Workbook called "Ressources calculation.xlsm" with three Columns: Current name, File name, New name. Row 4 looks like this:
Misspelledd [File name].xlsx Misspelled
Not every Cell in Column C is filled out. What I'm trying to do is: Iterate through every cell in Column C, if it is not empty copy the string which is in the same row but in Column A, look for it in the file which is noted in Column B and replace it with the right name written under Column C.
Here is a picture of the cell in the destination Workbook which should be found and the text replaced as explained above. It is a merged cell, stretching over rows 2-5.
Edit 3: I finally found out what the problem was. There were "invisible" line breaks at the end of some cells (not really invisible, but you don't easily see them since there are no characters coming after). If this is not the case, the code works.
Try something like this (added some debug.print for troubleshooting)
Sub copyPaste()
Dim destWB As Workbook
Dim destSH As Worksheet
Dim fileName As String
Dim curName, oldName
Dim result As Range
Dim wbRes As Workbook, wsTests As Worksheet
Set wbRes = Workbooks("Ressources calculation.xlsm") 'ThisWorkbook ?
Set wsTests = wbRes.Worksheets("Tests costs")
For i = 2 To 5
fileName = "C:\Users...\" & wsTests.Cells(i, 2)
If Not FileInUse(fileName) Then
Set destWB = Workbooks.Open(fileName)
Set destSH = destWB.Sheets("Qualification Matrix")
curName = Trim(wsTests.Cells(i, 3).Value) '<< always worth adding Trim()...
oldName = Trim(wsTests.Cells(i, 1).Value)
If Len(curName) > 0 Then
Debug.Print "Looking for: '" & oldName & _
"' on sheet '" & destSH.Name & "' in " & _
destWB.FullName
Set result = destSH.UsedRange.Find(What:=oldName, _
LookIn:=xlValues, _
LookAt:=xlWhole)
If Not result Is Nothing Then
Debug.Print "...found"
result.Value = curName
Else
Debug.Print "... not found"
End If
End If
End If 'file not in use
Next i
End Sub
I'm trying to:
Copy data (columns A and B) from one workbook (data.xlsx).
Paste into a new workbook (as values).
Save as CSV with a filename taken from column A in a third workbook (URLs.xlsx).
Process to repeat, taking the same data (which is randomised every time it is pasted) from data.xlsx and pasted into a new CSV - there are 200 rows in URLs.xlsx and so we should end up with 200 files.
I've read lots of topics, here are two I found:
Excel VBA Copy a Range into a New Workbook
https://www.excelcampus.com/vba/copy-paste-another-workbook/
What I've tried
Copying code and replacing the relevant components from various different articles across the web. Some of them work, but when I add the missing bits, I run into errors I don't understand.
Well here is an example avoiding copy pasting in new workbooks:
Expected input like:
Data.xlsx range A1:B200 with RANDBETWEEN() function:
URLs.xlsx range A1:A200 with some URL like so:
Run this code (will take approximately 1 second on my machine, tested with timer):
Dim wbData As Workbook, WBurls As Workbook
Dim CSVFileDir As String, CSVVal As String
Dim A As Long, X As Long, Y As Long, Z As Long
Option Explicit
Sub Transfer2CSV()
Set wbData = Workbooks("data.xlsx") 'Make sure it is open upon running macro
Set WBurls = Workbooks("URLs.xlsx") 'Make sure it is open upon running macro
For X = 1 To 200 'Looping through the 200 rows of WBurls
CSVFileDir = "C:\YourDrive\" & WBurls.Sheets(1).Cells(X, 1).Value & ".csv"
CSVVal = ""
A = FreeFile
Open CSVFileDir For Output As #A
With wbData.Sheets(1).Range("A1:B200") ' or whichever range you using here
.Calculate 'Randomize your range again
For Y = 1 To 200 'or however many rows you have in column A and B.
For Z = 1 To 2
CSVVal = CSVVal & .Cells(Y, Z).Value & ","
Next Z
Print #A, Left(CSVVal, Len(CSVVal) - 2)
CSVVal = ""
Next Y
End With
Close #A
Next X
End Sub
Output:
With each file looking like:
This should work. Make sure your data and URLS workbooks are open.
Sub Macro1()
Dim wsData As Worksheet, wsUrl As Worksheet, wbNew as Workbook
Dim CSVDir as String, rngU As Range
Set wsData = Workbooks("data.xlsx").Worksheets(1)
Set wsUrl = Workbooks("URLs.xlsx").Worksheets(1)
Set rngU = wsUrl.Range("A1", wsUrl.Range("A" & wsUrl.Rows.Count).End(xlUp))
CSVDir = "C:\Users\thomas.mcerlean\Desktop\Work\" 'you gave this as your dir
Set wbNew = Workbooks.Add
For Each cell In rngU
wsData.Range("A1", wsData.Range("B" & wsData.Rows.Count).End(xlUp)).Copy Destination:= wbNew.Worksheets(1).Range("A1")
wbNew.SaveAs Filename:= CSVDir & cell.Value & ".csv", FileFormat:=xlCSV
Next cell
wbNew.Close SaveChanges:=False
End Sub
I have code that open text files and imports data from them. It was working one minute, then i passed it to the guy i was writing it for and he got Run-time error 1004: 'Sorry we couldn't find [path], is it possible it was moved, renamed or deleted?'
Here's my code:
Sub OpenTxtFiles()
Dim ws As Worksheet
Dim rng As Range
Dim cell As Range
Dim xFolder As String
Dim txtFname As String
Dim cellAddr As String
Dim lastRow As Long
Dim userClass As Integer
xFolder = Application.ThisWorkbook.Path
Set rng = Range("E1:I1")
Application.ScreenUpdating = False
For Each cell In rng
txtFname = cell.Value 'sets the cell value as txtFname variable
'MsgBox txtFname
cellAddr = cell.Address 'sets the cell address as cellAddr variable
Workbooks.OpenText Filename:=xFolder & "\" & txtFname, DataType:=xlDelimited, comma:=True
ThisWorkbook is saved in the same folder, hence the xFolder = Application.ThisWorkbook.Path, so it should work, and if i put MsgBox xFolder right after that line, it displays the correct path. I'm very confused. Especially since it was working before i sent it to my colleague.
I tried also replacing the Application.ThisWorkbook.Path with a function that asks the user to choose the folder path, but the same error is given.
Can anyone help me understand whats going wrong and how to fix it?
It was an embarrassingly simple thing i forgot:
I setup a separate sheet for control with a button assigned to the macro, so when the path was searching the range to Set rng variable, it found nothing on the control sheet and returned the error.
Instead of:
Set rng = Range("E1:I1")
I needed:
Set rng = Sheets("Sheet1").Range("E1:I1")
I have the following code which produces a list of excel file paths and email addresses contained in those workbooks.
Code:
Option Explicit
Sub SO()
'clear the existing list here -- not implemented
'...
Range("G17:G" & ActiveSheet.Cells.SpecialCells(xlCellTypeLastCell).Row).ClearContents
Range("V17:V" & ActiveSheet.Cells.SpecialCells(xlCellTypeLastCell).Row).ClearContents
Range("AD17:AD" & ActiveSheet.Cells.SpecialCells(xlCellTypeLastCell).Row).ClearContents
Dim pathsEmails As New Dictionary
Dim app As New Excel.Application
Dim fso As New FileSystemObject
Dim weekFolder As Folder
'replace 1 with either the name or the index of the worksheet which holds the week folder path
'replace B4 with the address of the cell which holds the week folder path
Set weekFolder = fso.GetFolder(Worksheets(1).Range("I8").Value)
Dim supplierFolder As Folder, fle As file
For Each supplierFolder In weekFolder.SubFolders
For Each fle In supplierFolder.files
'test whether this is an Excel file
If fle.Type Like "*Excel*" Then
'open the workbook, read and save the email, and close the workbook
Dim book As Workbook
On Error Resume Next
Set book = app.Workbooks.Open(fle.path, , True)
pathsEmails(fle.path) = book.Worksheets(1).Range("C15").Value
book.Close False
End If
Next
Next
app.Quit
'copy the paths and emails to the worksheet
'(as above) replace 1 with either the name or the index of the worksheet which holds the week folder path
'paths are pasted in starting at cell B6, downwards
'emails are pasted in starting at cell C6, downwards
Worksheets(1).Range("G17").Resize(UBound(pathsEmails.Keys) + 1, 1).Value = WorksheetFunction.Transpose(pathsEmails.Keys)
Worksheets(1).Range("V17").Resize(UBound(pathsEmails.Items) + 1, 1).Value = WorksheetFunction.Transpose(pathsEmails.Items)
'Clear empty cells
On Error Resume Next
Range("V17:V" & ActiveSheet.Cells.SpecialCells(xlCellTypeLastCell).Row).SpecialCells(xlBlanks).EntireRow.Delete
End Sub
This produces a result like so:
G:\folder1\file.xls email#email.com
How can i trim my file path to produce just the following:
file.xls email#email.com
I have tried
replace(pathsEmails(fle.path), "G:\folder1\" , "")
But this doesn't work. Please can someone show me where i am going wrong?
Edit:
Sometimes i have more than one email address in the cell C15.
email#email.com / tom#email.com
So this causes the emails in the workbook to be listed like so:
email#email.com / tom#email.com
Is there anyway i can replace the / and replace it with a , (to make it email friendly)
Using files' names as keys, you should have the desired output :
(if not, try : pathsEmails(Replace(fle.Path,weekFolder.Path,vbNullString)) = book.Worksheets(1).Range("C15").Value)
Option Explicit
Sub SO()
'clear the existing list here -- not implemented
'...
Dim wS As Worksheet
Dim LastRow As Long
Dim i as Long
Set wS = ThisWorkbook.ActiveSheet
With wS
LastRow = .Range("G" & .Rows.Count).End(xlUp).Row
.Range("G17:G" & LastRow).ClearContents
.Range("V17:V" & LastRow).ClearContents
.Range("AD17:AD" & LastRow).ClearContents
End With
Dim pathsEmails As New Dictionary
Dim app As New Excel.Application
Dim fso As New FileSystemObject
Dim weekFolder As Folder
Dim supplierFolder As Folder
Dim fle As File
'replace 1 with either the name or the index of the worksheet which holds the week folder path
'replace B4 with the address of the cell which holds the week folder path
Set weekFolder = fso.GetFolder(wS.Range("I8").Value)
For Each supplierFolder In weekFolder.SubFolders
For Each fle In supplierFolder.Files
'test whether this is an Excel file
If fle.Type Like "*Excel*" Then
'open the workbook, read and save the email, and close the workbook
Dim book As Workbook
On Error Resume Next
Set book = app.Workbooks.Open(fle.Path, , True)
pathsEmails(fle.Name) = book.Worksheets(1).Range("C15").Value
book.Close False
End If
Next fle
Next supplierFolder
app.Quit
'copy the paths and emails to the worksheet
'(as above) replace 1 with either the name or the index of the worksheet which holds the week folder path
'paths are pasted in starting at cell B6, downwards
'emails are pasted in starting at cell C6, downwards
With wS
.Range("G17").Resize(UBound(pathsEmails.Keys) + 1, 1).Value = WorksheetFunction.Transpose(pathsEmails.Keys)
.Range("V17").Resize(UBound(pathsEmails.Items) + 1, 1).Value = WorksheetFunction.Transpose(pathsEmails.Items)
'Clear empty cells
On Error Resume Next
LastRow = .Range("G" & .Rows.Count).End(xlUp).Row
For i = 17 To LastRow
.Range("V" & i)=Replace(.Range("V" & i),"/",",")
Next i
.Range("V17:V" & LastRow).SpecialCells(xlBlanks).EntireRow.Delete
End With
End Sub
Why not use something like mid(fle.path,11,len(fle.path) - 11)?