Using Excel VBA to retrieve data from multiple MS Project Files - excel

I have ran into an automation issue that I cannot seem to figure out.
Currently, I have a worksheet,("Project") that contains data in columns "A"(Project Name) & "B"(Project File Location).
Column "B" contains the string location of each MS Project file.
My VBA macro loops through column "B" and opens each MS Project file and copies a task with the .SelectTaskField method and then copies it back into column "E" of the worksheet.
The first 2 projects loop through without any issues, however, on the 3rd project, I receive the Run-time error '1004': An unexpected error occurred with the method.
I co-worker and I have poured through the code and the MS Project Files to see if there are any differences in the data and we cannot find any differences.
Below is a copy of the code that I have been using.
Just wanted to see if anyone else has had similar issues. I have found that MS Project does not like to be manipulated like Excel or Word.
Any help would be greatly appreciated.
Sub Test()
Dim ws As Worksheet
Set ws = Worksheets("Projects")
Dim lrow As Long
lrow = Range("B" & Rows.Count).End(xlUp).Row
'Turns off updates and alerts
Application.ScreenUpdating = True
Application.DisplayAlerts = True
'Select Daily Field Reports and clear worksheet
ws.Range("E2:E" & lrow).ClearContents
'Opens MS Project
Set objproject = CreateObject("MSProject.Project")
'This keeps MS Project invisible. If you want to see it, change to "True"
objproject.Application.Visible = True
Dim oproject As Range
'This cycles through the range and gathers the data for each project
For Each oproject In Range("B2:B" & lrow)
Set objproject = CreateObject("MSProject.Project")
oproject.Select
objproject.Application.FileOpen Selection
objproject.Application.Visible = True
objproject.Application.SelectTaskField Row:=1, Column:="Percent Complete", RowRelative:=False 'The column name must match. This is the only issue that I have uncovered.
objproject.Application.EditCopy
ws.Select
Dim lastrow As Long
lastrow = ws.Cells(Rows.Count, "E").End(xlUp).Row + 1
Dim Rng As Range
Set Rng = ws.Range("E" & lastrow)
'Rng.PasteSpecial xlPasteFormats
Rng.PasteSpecial xlPasteValues
objproject.Application.Quit
Next oproject
'Turns updates and alerts back on
Application.ScreenUpdating = True
Application.DisplayAlerts = True
'Closes MS Project
objproject.Application.Quit
End Sub

Using the SelectTaskField method presumes the file was saved in a task view and that the column you want is in the table of the view. Better to get the values you need directly from the Task object.
It appears you are looking for the % Complete value from the first task. In that case use this:
objproject.ActiveProject.Tasks(1).PercentComplete
Here's how it could work in your code. I took the liberty of simplifying it a bit:
Sub Test()
Dim ws As Worksheet
Set ws = Worksheets("Projects")
Dim lrow As Long
lrow = Range("B" & Rows.Count).End(xlUp).Row
Application.ScreenUpdating = True
Application.DisplayAlerts = True
ws.Range("E2:E" & lrow).ClearContents
Dim objproject As MSProject.Application
Set objproject = CreateObject("MSProject.Application")
objproject.Application.Visible = True
Dim oproject As Range
For Each oproject In Range("B2:B" & lrow)
objproject.FileOpen Name:=oproject.Value, ReadOnly:=True
oproject.Offset(, 3) = objproject.ActiveProject.Tasks(1).PercentComplete
objproject.FileCloseEx
Next oproject
Application.ScreenUpdating = True
Application.DisplayAlerts = True
objproject.Quit
End Sub
Note that it is more straight-forward to get a reference to the application object rather than a child of that object: CreateObject("MSProject.Application") is preferable to CreateObject("MSProject.Project").

Related

Iteratively break out a data file to a template file and save as a new file for every 5,000 rows

I am trying to break out a data file by 5,000 rows due to limitation with a tool. I have a template file that has multiple sheets (I only have to update data on the first sheet titled 'Service Template', but I need all tabs present on the newly created files). The tool requires the template file to be used so I have to use that file instead of copying the data to a completely new file. I am also attempting to do this on a Mac, but can use virtual machine if absolutely necessary.
The data file and the template file both start on row 2 as both files have headers.
I have the below code that I have been trying to build out but it is still not working and I am stuck.
Data file sheet = 'Sheet1' and Template File Sheet = 'Service Template'
Sub test()
Dim lastRow As Long, myRow As Long, myBook As Workbook
ActiveSheet.Name = "Sheet1"
lastRow = ThisWorkbook.Sheets("Sheet1").Cells(Rows.Count, 1).End(xlUp).Row
For myRow = 1 To lastRow Step 5000
Set myBook = Workbooks.Open("/Users/Downloads/Test/TemplateFile.xlsx")
ThisWorkbook.Sheets("Sheet1").Rows(myRow & ":" & myRow + 4999).EntireRow.Copy myBook.Sheets("Sheet1").Range("A2")
Application.DisplayAlerts = False
myBook.SaveAs Filename:="\Users\Downloads\Test\" & myBook.Name
Application.DisplayAlerts = False
myBook.Close
Next myRow
End Sub
I am looking to transfer 5000 rows (starting row2) from the data file to the template file (starting row2) save as a new file and then keep doing the same process until all of the rows are complete.
Try something like this:
Sub test()
Const BLOCK_SIZE As Long = 5000
Dim wsSrc As Worksheet, myBook As Workbook, rngCopy As Range
Set wsSrc = ActiveSheet 'or some other specific sheet
Set rngCopy = wsSrc.Rows(2).Resize(BLOCK_SIZE)
Do While Application.CountA(rngCopy) > 0 'loop while range has content
With Workbooks.Open("/Users/Downloads/Test/TemplateFile.xlsx")
rngCopy.Copy .Worksheets("Sheet1").Range("A2")
.SaveAs "\Users\Downloads\Test\" & "R" & rngCopy.Row & "_" & .Name
.Close SaveChanges:=True
End With
Set rngCopy = rngCopy.Offset(BLOCK_SIZE) 'next block down
Loop
End Sub

Extremely slow moving of data VBA - Alternatives

I have this working code which copies data in specific columns to a new file.
Sub GetFileCopyData()
Dim Fname As String
Dim SrcWbk As Workbook
Dim DestWbk As Workbook
Set DestWbk = ThisWorkbook
Application.Calculation = xlManual
Application.ScreenUpdating = False
Sheets("Data").UsedRange.ClearContents
Fname = Application.GetOpenFilename(FileFilter:="Excel Files (*.csv*), *.csv*", Title:="Select a File")
If Fname = "False" Then Exit Sub
Set SrcWbk = Workbooks.Open(Fname)
SrcWbk.Sheets(1).Range("A:A").Copy DestWbk.Sheets("Data").Range("A:A")
SrcWbk.Sheets(1).Range("E:E").Copy DestWbk.Sheets("Data").Range("B:B")
SrcWbk.Sheets(1).Range("M:M").Copy DestWbk.Sheets("Data").Range("C:C")
SrcWbk.Sheets(1).Range("AD:AD").Copy DestWbk.Sheets("Data").Range("D:D")
SrcWbk.Sheets(1).Range("AF:AF").Copy DestWbk.Sheets("Data").Range("E:E")
SrcWbk.Sheets(1).Range("DA:DA").Copy DestWbk.Sheets("Data").Range("F:F")
SrcWbk.Sheets(1).Range("AEG:AEG").Copy DestWbk.Sheets("Data").Range("G:G")
SrcWbk.Sheets(1).Range("AEM:AEM").Copy DestWbk.Sheets("Data").Range("H:H")
SrcWbk.Close False
Application.ScreenUpdating = True
Application.Calculation = xlAutomatic
End Sub
This runs extremely slowly. I've already tried turning screen updating off etc. I read that the following is quicker than copying, which is slow.
Range("A1:Z100").value = Range("A101:Z200").value
Can anyone please tell me how to implement this? I tried using this code but it ended up being blank:
SrcWbk.Sheets(1).Range("A:A").Value = DestWbk.Sheets("Data").Range("A:A").Value
If all you're copying is values, rather than copying the entire column, which is very resource intensive (effectively you're copying 1048576 cells) you could try implementing a lastrow statement and only copy the used range of the column. This could drastically reduce your runtime depending on how many values you have. Something amongst the lines of:
Sub copy()
Dim lastr As Long
lastr = Sheet1.Range("A" & Rows.Count).End(xlUp).Row
Sheet2.Range("A1:A" & lastr).Value = Sheet1.Range("A1:A" & lastr).Value
End Sub
To adapt your code you should replace the following line:
SrcWbk.Sheets(1).Range("A:A").Copy DestWbk.Sheets("Data").Range("A:A")
With this:
lastr = SrcWbk.Sheets(1).Range("A" & Rows.Count).End(xlUp).Row
DestWbk.Sheets("Data").Range("A" & lastr).value = SrcWbk.Sheets(1).Range("A" & lastr).value
Please note if your column lengths vary, you should redo the lastr calculation for every column. If all your columns are the same length (All ending on the same row), then using the first calculation for every column will do.
It looks like your requirement is just extracting specific columns from CSV file, then Get & Transform should be the best fit rather than VBA solution.
Yet another option is to use Microsoft Text Driver via ADO in VBA.

VBA: Tickbox - True, creates a sheet. False, deletes the sheet

I am editing an existing template in VBA. This template organises data from a "Raw Data" sheet into a "Day", "Evening" and "Night" sheet. I want to create a sheet called "Weekend", where data from "Raw Data" will be copied to if the date is not a weekday (i know how to do that bit).
However, the amount of data that runs through this template is massive, so to avoid creating a weekend sheet where the user does not need/want one I want to put in a section of code where if a tick box is ticked (True), a sheet called "Weekend" will be created (within the workbook, but inbetween existing sheets - namely inbetween a sheet called "Night" and "Graph - All Data") and when it is unticked (False) this sheet will not exist.
I thought about having this sheet to exist all the time, and to have it hidden when the tick box is unticked, however this means that the data would be still piled into it and in the interest of efficiency I would rather not have it like that.
Here is the Code I am trying to alter
Sub ToggleWindDirection()
Dim i As Long
Application.ScreenUpdating = False
If sheetArr(1) Is Nothing And LastNDRow = Empty Then
DefineLists
End If
Sheets("Raw Data").Unprotect Password:="2260"
For Each sht In sheetArr
sht.Unprotect Password:="2260"
Next
Set chtAllData = ActiveWorkbook.Charts("Graph - All Data")
With Sheets("Raw Data")
If .Range("O15").Value = True Then
'Wind direction is being used
.Range("C17:G17").Font.ColorIndex = xlAutomatic
.Range("D17").Font.ColorIndex = 9
.Range("G17").Font.ColorIndex = 9
.Range("D17").Locked = False
.Range("G17").Locked = False
.Range("F" & FirstNDRow & ":F10000").Interior.Pattern = xlNone
.Range("F" & FirstNDRow & ":F10000").Interior.PatternTintAndShade = 0
.Range("F" & FirstNDRow & ":F10000").Font.ColorIndex = xlAutomatic
Else
'Not using wind direction
.Range("C17:G17").Font.ColorIndex = 16
.Range("D17").Locked = True
.Range("G17").Locked = True
.Range("F" & FirstNDRow & ":F10000").Interior.Pattern = xlSolid
.Range("F" & FirstNDRow & ":F10000").Interior.TintAndShade = -4.99893185216834E-02
.Range("F" & FirstNDRow & ":F10000").Font.ColorIndex = 16
End If
'Addition by lewisthegruffalo 2016
Dim ws As Worksheet
If .Range("O21").Value = True Then
'create the weekend sheet
Set ws = Worksheets.Add(After:=Worksheets("Night"))
ws.Name = "Weekend"
Else
'No Weekend needed
Worksheets("Weekend").Delete
End If
End With
Sheets("Raw Data").Activate
Application.ScreenUpdating = True
End Sub
Any help would be greatly apprichiated.
Kind Regards,
lewisthegruffalo
If you want to create a new sheet using sheets.add then you can utilise the arguments in it to tell it where to put it, the msdn page has a good example at the bottom.
What you have so far? Where do you wanna call it? What checkbox shall be ticked? Please provide some code.
A general answer is this to add a new worksheet:
Dim ws As Worksheet
If Something Then
Set ws = Worksheets.Add(Before:=Worksheets("Graph - All Data"))
ws.Name = "Weekend"
'do anything you want with the ws object
End if

Date for VBA not working in Excel 2011?

Here is my code, I am attempting, which works BTW on a PC, but not on a Mac, to run this code and have an excel sheet created, named, add a tab, change the color of said tabs, change the name of said tabs, and then transpose the data while maintaining the format of the cells and width and height of the cells to the new worksheet.
This works, on a PC.... but when I get onto a Mac, it doesn't.
I go into References, and this is what I see.
I see Ref Edit Control, and Microsoft Scripting Runtime are missing. I disabled both, and the script gives me an error here still:
wbBK2.SaveAs Dir & Application.PathSeparator & "Open Order Report -" & Format(Date, "mm-dd-yyyy") & ".xlsx"
The error happens at the (Date, "mm-dd-yyyy") Specifically the Date section. I can't figure out why this is happening honestly. If someone can peruse this and give me an answer and a solution it be greatly appreciated.
The error I get is an Error '9 Subscript Out Of Range I can't see a reason why this error only shows up on a Mac, and not a PC.
Option Explicit
Sub OpenOrderReportExport()
Dim wsJL As Worksheet 'Jobs List
Dim wsPOT As Worksheet 'PO Tracking
Dim wsTNO As Worksheet 'Tel-Nexx OOR
Dim wsDOO As Worksheet 'Dakota OOR
Dim wbBK1 As Workbook 'Open Order Report
Dim wbBK2 As Workbook 'New Workbook
Dim wsWS1 As Worksheet 'Sheet1
Dim wsWS2 As Worksheet 'Sheet2
Dim wsWS3 As Worksheet 'Sheet3
Dim wsWS4 As Worksheet 'Sheet4
Dim CurrentFile As String, NewFileType As String, NewFile As String, Dir As String, lastrow As Long
Set wsJL = Sheets("Jobs List") 'Jobs List
Set wsPOT = Sheets("PO Tracking") 'PO Tracking
Set wsTNO = Sheets("Tel-Nexx OOR") 'Tel-Nexx OOR
Set wsDOO = Sheets("Dakota OOR") 'Dakota OOR
Set wbBK1 = ThisWorkbook
Set wbBK2 = Workbooks.Add 'New Workbook
Set wsWS1 = wbBK2.Sheets("Sheet1") 'Sheet1
Set wsWS2 = wbBK2.Sheets("Sheet2") 'Sheet2
Set wsWS3 = wbBK2.Sheets("Sheet3") 'Sheet3
Application.ScreenUpdating = False ' Prevents screen refreshing.
CurrentFile = ThisWorkbook.FullName
NewFileType = "Excel Files 2007 (*.xlsx)"
Dir = ThisWorkbook.path & Application.PathSeparator & "Reports"
wbBK2.SaveAs Dir & Application.PathSeparator & "Open Order Report -" & Format(Date, "mm-dd-yyyy") & ".xlsx"
Sheets.Add After:=Sheets(Sheets.Count)
Set wsWS4 = wbBK2.Sheets("Sheet4") 'Sheet4
With wbBK2
Dim Sht As Worksheet
For Each Sht In Worksheets
Sht.Tab.Color = 255
Next
End With
Sheets("Sheet1").Name = "Jobs List"
Sheets("Sheet2").Name = "PO Tracking"
Sheets("Sheet3").Name = "Dakota OOR"
Sheets("Sheet4").Name = "Tel-Nexx OOR"
With wbBK1
'Jobs List Export
lastrow = wsJL.Range("B" & Rows.Count).End(xlUp).Row
wsJL.Range("A2:N2").Copy
wsWS1.Range("A1").PasteSpecial xlPasteAll
wsJL.Range("A3:N" & lastrow).Copy
wsWS1.Range("A2").PasteSpecial xlPasteValuesAndNumberFormats
wsWS1.Range("A2").PasteSpecial xlPasteColumnWidths
wsJL.Range("B3:N" & lastrow).Copy
wsWS1.Range("B2").PasteSpecial xlPasteFormats
wsWS1.Columns("A").Delete
'Tel-Nexx Export
lastrow = wsTNO.Range("B" & Rows.Count).End(xlUp).Row
wsTNO.Range("A2:Q2").Copy
wsWS2.Range("A1").PasteSpecial xlPasteAll
wsTNO.Range("A3:Q" & lastrow).Copy
wsWS2.Range("A2").PasteSpecial xlPasteValuesAndNumberFormats
wsWS2.Range("A2").PasteSpecial xlPasteColumnWidths
wsTNO.Range("B3:Q" & lastrow).Copy
wsWS2.Range("B2").PasteSpecial xlPasteFormats
wsWS2.Columns("A").Delete
'Dakota Export
lastrow = wsDOO.Range("B" & Rows.Count).End(xlUp).Row
wsDOO.Range("A2:O2").Copy
wsWS3.Range("A1").PasteSpecial xlPasteAll
wsDOO.Range("A3:O" & lastrow).Copy
wsWS3.Range("A2").PasteSpecial xlPasteValuesAndNumberFormats
wsWS3.Range("A2").PasteSpecial xlPasteColumnWidths
wsDOO.Range("B3:O" & lastrow).Copy
wsWS3.Range("B2").PasteSpecial xlPasteFormats
wsWS3.Columns("A").Delete
'PO Tracking Export
lastrow = wsPOT.Range("B" & Rows.Count).End(xlUp).Row
wsPOT.Range("A2:K2").Copy
wsWS4.Range("A1").PasteSpecial xlPasteAll
wsPOT.Range("A3:K" & lastrow).Copy
wsWS4.Range("A2").PasteSpecial xlPasteValuesAndNumberFormats
wsWS4.Range("A2").PasteSpecial xlPasteColumnWidths
wsPOT.Range("B3:K" & lastrow).Copy
wsWS4.Range("B2").PasteSpecial xlPasteFormats
wsWS4.Columns("A").Delete
End With
With wsWS1
.Activate
.Range("A1").Select
End With
End Sub
the MISSING references are what are causing the problems. Remove the checkmarks there, and the basic stuff like date will start working again. If those references are critical to the code you are running, you will have to search out the Mac equivalents
More about references - normally resolving "Missing" fixes it, but, from here
What you're describing is typical of corrupted references. This can
be caused by a referenced file being a different version or in a different
location between the machine on which the code was developed, and the
client machines. Our company also tries to keep all the machines configured
the same way, but I've found it's essentially impossible to manage.
Open any code module (or open the Debug Window, using Ctrl-G, provided you
haven't selected the "keep debug window on top" option). Select Tools |
References from the menu bar. Examine all of the selected references.
If any of the selected references have "MISSING:" in front of them, unselect
them, and back out of the dialog. If you really need the reference(s) you
just unselected (you can tell by doing a Compile All Modules), go back in
and reselect them.
If none have "MISSING:", select an additional reference at random, back out
of the dialog, then go back in and unselect the reference you just added. If
that doesn't solve the problem, try to unselect as many of the selected
references as you can (Office may not let you unselect them all), back out
of the dialog, then go back in and reselect the references you just
unselected. (NOTE: write down what the references are before you delete
them, because they'll be in a different order when you go back in)
Yes, disambigulating as VBA.xxxx will work, since Excel no longer has to
look through all of the references.

After document is opened in Excel, copying data from first tab with caveat

I have a workbook that I need to open and then copy the data from the first tab, and then paste it to the original workbook in which the script was created from.
The problem I am running into is that the workbook and the first tab will be named the same thing. So if the workbook name is OpenPOs-100255-08292012.xls the tab is going to be OpenPOs-100255-08292012. Next week though the excel sheet is going to be OpenPOs-200211-12495312.xls which means the tab is going to be OpenPOs-200211-12495312.
With the code I am using right now, is there a way to make it work for this kind of situation? I thought about making it so that the "Sheet 1" becomes the tab of the day? I thought about using `wsPOR.Sheets(wsPOR) but I have a feeling that is going to be coming back as an error. Can someone help please?
Sub Update_TNOOR()
Dim wsTNO As Worksheet
Dim wsTND As Worksheet
Dim wsTNA As Worksheet
Dim wbPOR As Workbook 'New Workbook
Dim wbOOR As Workbook 'ThisWorkbook
Dim lastrow As Long, lastrow2 As Long, fstcell As Long
Dim strFile As String, NewFileType As String, filename As String
Set wsTNO = Sheets("Tel-Nexx OOR")
Set wsTND = Sheets("Tel-Nexx Data")
Set wsTNA = Sheets("Tel-Nexx Archive")
Set wbOOR = ThisWorkbook
With Application
.ScreenUpdating = False
.DisplayAlerts = False
.EnableEvents = False
End With
lastrow = wsTND.Range("A" & Rows.Count).End(xlUp).Row + 1
wsTND.Range("A2:P" & lastrow).Delete
strFile = Application.GetOpenFilename()
NewFileType = "Excel Files 2007 (*.xls)"
Set wbPOR = Application.Workbooks.Open(strFile)
lastrow = wbPOR.Sheets("Sheet1").Range("A" & wbPOR.Sheets("Sheet1").Rows.Count).End(xlUp).Row
wbPOR.Sheets("Sheet1").Range("A4:N" & lastrow).Copy wbOOR.Sheets("Tel-Nexx Data").Range("A2")
wbPOR.Save
wbPOR.Close
End Sub
Based on my comment above, your code, from strFile = Application.GetOpenFilename() to wbPOR.Close becomes:
strFile = Application.GetOpenFilename()
NewFileType = "Excel Files 2007 (*.xls)"
Set wbPOR = Application.Workbooks.Open(strFile)
Dim wsPOR As Worksheet
Set wsPOR = wbPOR.Sheets(Replace(wbPOR.Name, ".xls", ""))
lastrow = wsPOR.Range("A" & wsPOR.Rows.Count).End(xlUp).Row
wsPOR.Range("A4:N" & lastrow).Copy wbOOR.Sheets("Tel-Nexx Data").Range("A2")
wbPOR.Save
wbPOR.Close
use the answers from this question, and substitute that into your sheets() reference
using my answer as an example, your wbPOR.Sheets("Sheet1"). would become
wbPOR.Sheets(left(strFile ,instrrev(strFile ,".")-1)).
which would also have the advantage of working with other extensions should you expand to newer excel versions.

Resources