I have been able to get quite far on my project by using a lot of the solutions posted on here. Unfortunately, I simply cannot workout the bugs and could really use some help. What I am trying to accomplish is as follows:
-"CNC WORK ORDER REQUEST - V2.XLSM" is the source workbook where a department will input their requests for any sort of CNC work. I have formatted it so the output is consistent. A whole department of people will have access to it on their local computers.
-"MASTER SCHEDULE.XLSX" is the target workbook where I would like a particular range of cells copied to, on the next available row of cells in this workbook. This workbook is for upper management to have visibility on a "master schedule" that is created by the submissions of the CNC WORK ORDER REQUEST workbook.
I do feel that the WORKORDER REQUEST is fine, but I am having issue with copying to the next open row of cells on the MASTER SCHEDULE. I cannot get all of the cells to copy to the next open row.
The code I have is as follows:
Sub Auto_Open()
MsgBox "Welcome, please enter information in *ALL* subsquent prompts as instructed."
Dim Customer
Customer = InputBox("Enter customer name")
Range("e1") = Customer
Dim Job
Job = InputBox("Enter Job#")
Range("e2") = Job
Dim JobName
JobName = InputBox("Enter job name / description")
Range("e3") = JobName
Dim TodaysDate
TodaysDate = InputBox("Enter today's date")
Range("E4") = TodaysDate
Dim Manager
Manager = InputBox("Enter your Intials")
Range("e5") = Manager
MsgBox "Before entering which machine will run this part, you must get this information from Ronnie", vbOKOnly
Dim Machine
Machine = InputBox("Enter which machine this will be run on. Options are: Thermwood -or- MultiCam")
Range("b9") = Machine
Dim Part
Part = InputBox("Enter part name as per drawing")
Range("B10") = Part
Dim Qty
Qty = InputBox("Enter Qty needed including overs")
Range("B11") = Qty
Dim Material
Material = InputBox("Enter material type with details")
Range("B12") = Material
Dim Thickness
Thickness = InputBox("Enter material thickness")
Range("b13") = Thickness
Dim EdgeFinish
EdgeFinish = InputBox("Enter edge finish for this part - MILL FINISH, FLAME POLISH, DIAMOND POLISH, DIAMOND AND BUFF")
Range("B14") = EdgeFinish
Dim MaterialInHouse
Material = InputBox("If material is in house, type YES, if not, enter the ETA")
Range("B15") = MaterialInHouse
Dim AddtionalProcessing
AddtionalProcessing = InputBox("Please note any subsequent processing required")
Range("B16") = AddtionalProcessing
Dim NeedBy
NeedBy = InputBox("Enter required completion date for parts")
Range("B17") = NeedBy
Dim AddtionalNotes
AddtionalNotes = InputBox("Enter any additional notes, if required")
Range("B18") = AddtionalNotes
'TESTING - TO COPY ABOVE VALUES TO MASTER SCHEDULE'
'Dim wbk As Workbook
'Dim strFirstFile As String
'Dim strSecondFile As String
'strFirstFile = "CNC WORK ORDER REQUEST - V2.XLSM"
'strSecondFile = "C:\Users\Mike\Desktop\MASTER SCHEDULE.XLSX" 'CHANGE TO TARGET FILE ONCE DETERMINED
'CUSTOMER COPY:
'Set wbk = Workbooks(strFirstFile)
'With wbk.Sheets("sheet1")
'.Range("E1").Copy
'End With
'Set wbk = Workbooks.Open(strSecondFile)
'With wbk.Sheets("sheet1").Range("b" & Rows.Count).End(xlUp).Offset(1, 0).PasteSpecial(xlPasteValues)
'End With
'JOB# COPY:
'Set wbk = Workbooks(strFirstFile)
'With wbk.Sheets("sheet1")
'.Range("E2").Copy
'End With
'Set wbk = Workbooks(strSecondFile)
'With wbk.Sheets("sheet1").Range("C" & Rows.Count).End(xlUp).Offset(1, 0).PasteSpecial
'End With
'wbk.Save
'wbk.Close
'END TESTING
MsgBox "Please print and submit to Ronnie.", vbOKOnly 'ADD COMMENT BELOW BACK UP HERE ONCE SCHEDULE COMPLETE
'Workorder has already been uploaded to schedule",
End Sub
Sorry if I did not articulate myself well enough with what I'm trying to accomplish. I tried posting images, but was not allowed. I am really looking forward to solving this as it will help organize the company I am working with. Thanks in advance!
Kind regards,
Mike Q
Without seeing the sheets it's impossible for me to know where to paste to, but it looks like your problem is your only copying one cell at a time so that's all that will copy. You need to copy the entire range and then if you paste it to the first open cell in your destination sheet it will paste everything.
You will probably still need to tweak this a bit but hopefully it will give you enough to work with:
'TESTING - TO COPY ABOVE VALUES TO MASTER SCHEDULE'
Dim wbk As Workbook
Dim strFirstFile As String
Dim strSecondFile As String
strFirstFile = "CNC WORK ORDER REQUEST - V2.XLSM"
strSecondFile = "C:\Users\Mike\Desktop\MASTER SCHEDULE.XLSX"
'CUSTOMER Copy:
Set wbk = Workbooks(strFirstFile)
wbk.Sheets("sheet1").Range("E1:E5").Copy
Set wbk = Workbooks.Open(strSecondFile)
wbk.Sheets("sheet1").Range("B1").End(xlDown).Offset(1, 0).PasteSpecial (xlPasteValues)
'JOB# Copy:
Set wbk = Workbooks(strFirstFile)
wbk.Sheets("sheet1").Range("B9:B18").Copy
Set wbk = Workbooks(strSecondFile)
wbk.Sheets("sheet1").Range("C1").End(xlDown).Offset(1, 0).PasteSpecial
wbk.Save
wbk.Close
'END TESTING
Just an FYI, if you're looking for the user to input a lot of data check into making a userform. They're not hard to make and will be much easier for your users to input a bunch of data. It also gives you some control over validating it.
Related
I have code to extract all email attachments from specific email folder.
I want to change to extract email attachments starting from a date which I enter in a dialog box. I want to extract email attachments from emails received in the last seven days.
Sub Extract_emails()
Dim OlApp As Object
Dim OlMail As Object
Dim OlItems As Object
Dim Olfolder As Object
Dim J As Integer
Dim strFolder As String
Set OlApp = GetObject(, "Outlook.Application")
If Err.Number = 429 Then
Set OlApp = CreateObject("Outlook.Application")
End If
strFolder = ThisWorkbook.Path & "\Extract"
Set Olfolder = OlApp.getnamespace("MAPI").Folders("MyEmailAddress").Folders("Inbox")
Set OlItems = Olfolder.Items
For Each OlMail In OlItems
If OlMail.Attachments.Count > 0 Then
For J = 1 To OlMail.Attachments.Count
OlMail.Attachments.Item(J).SaveAsFile strFolder & "\" & OlMail.Attachments.Item(J).Filename
Next J
End If
Set OlApp = Nothing
Set OlMail = Nothing
Set OlItems = Nothing
Set Olfolder = Nothing
Next
MsgBox ("Done")
End Sub
I need to extract only xlsx attachments (vendor sends Excel and pdf documents) and to save them in folder. After I need to open saved Excel file and to copy data in base and to close saved xlsx. I don't know name of xlsx file (usually it is our company name and some numbers) but every report has sheets "shipped" from which I copy data in base. No one reads these emails that's why I tried with unread emails.
Code which works with F8 but not with F5.
Set OlApp = GetObject(, "Outlook.Application")
If Err.Number = 429 Then
Set OlApp = CreateObject("Outlook.Application")
End If
strFolder = ThisWorkbook.Path & "\Extract"
Set Olfolder = OlApp.getnamespace("MAPI").Folders("Freight.Invoice#omega.com").Folders("Inbox")
Set OlItems = Olfolder.Items
For Each OlMail In OlItems
If OlMail.UnRead = True Then
If OlMail.Attachments.Count > 0 Then
For J = 1 To OlMail.Attachments.Count
FilePath = strFolder & "\" & OlMail.Attachments.Item(J).FileName
OlMail.Attachments.Item(J).SaveAsFile FilePath
If Right(FilePath, 4) = "xlsx" Then
runit FilePath
For I = 1 To Worksheets.Count
If Worksheets(I).Name = "Shipped" Then
Worksheets("Shipped").Activate
Set wsCopy = Worksheets("Shipped")
Set wsDest = Workbooks("Extract
emails.xlsm").Worksheets("DATA")
lCopyLastRow = wsCopy.Cells(wsCopy.Rows.Count,
"B").End(xlUp).Row
lDestLastRow = wsDest.Cells(wsDest.Rows.Count,
"B").End(xlUp).Offset(1).Row
wsCopy.Range("B4:K" & lCopyLastRow).Copy _
wsDest.Range("B" & lDestLastRow)
Worksheets("Shipped").Activate
ActiveWorkbook.Close savechanges:=False
End If
Next
End If
Next J
End If
End If
Next
For Each OlMail In OlItems
If OlMail.UnRead = True Then
OlMail.UnRead = False
DoEvents
OlMail.Save
End If
Set OlApp = Nothing
Set OlMail = Nothing
Set OlItems = Nothing
Set Olfolder = Nothing
Next
MsgBox ("Done")
End Sub
Sub runit(FilePath As String)
Dim Shex As Object
Dim wsCopy As Worksheet
Dim wsDest As Worksheet
Dim lCopyLastRow As Long
Dim lDestLastRow As Long
Set Shex = CreateObject("Shell.Application")
Shex.Open (FilePath)
End Sub
This is a tutorial rather than a direct answer to your question. I cover everything you need to know. I believe you will find this approach more useful than “a run this code and it will work” answer. I hope I have explained everything adequately. Come back with questions if necessary.
You need to compare an email’s ReceivedTime against the oldest required date. You say you intend to enter the oldest required date and you also say you want the last seven days. There may be an alternative. Type the following commands (except the comments) in you Immediate Window.
? now() The current date and time
? datevalue(now()) The current date
? dateadd("d",-7,now()) Seven days before now
? dateadd("d",-7,datevalue(now())) Seven days ago
? dateadd("ww",-1,datevalue(now())) One week ago
Do any of these expressions give you the date you want? In DateAdd, “d” and “ww” are intervals with “d” meaning days and “ww” meaning weeks. There are other values such as “w” meaning weekdays. Experiment if one of these expressions gives you almost what you want.
Other possibilities include setting a category or a custom property when the attachments are saved.
If you have not done so already, open your workbook and the VBA Editor. Click [Tools] then [References…]. Is “Microsoft Outlook nn.n Object Library” near the top of the list and ticked? Note: “nn.n” depends on the version of Office you are using. If this library is not listed and ticked, scroll down until you find it and click the little box to tick it. This gives your workbook access to Outlook data items so you do not have to specify so many Objects.
Now create a new module and copy the code below to it. If you run macro Demo(), you will get output like this:
Oldest additions to Inbox
[14/12/2019 18:21:21] [28/12/2019 05:05:00] [08/01/2020 18:37:09] [28/03/2019 16:16:12] [21/03/2019 14:00:08]
[14/06/2018 21:02:34] [03/02/2020 09:29:38] [06/03/2020 17:03:50] [11/03/2020 13:43:33] [12/03/2020 00:07:53]
[13/03/2020 08:46:58] [13/03/2020 17:31:23] [14/03/2020 03:42:53] [14/03/2020 08:07:35] [14/03/2020 08:58:11]
[15/03/2020 19:43:16] [16/03/2020 16:48:40] [16/03/2020 20:39:58] [17/03/2020 11:14:29] [18/03/2020 01:43:37]
Newest additions to Inbox
[18/03/2020 01:43:37] [17/03/2020 11:14:29] [16/03/2020 20:39:58] [16/03/2020 16:48:40] [15/03/2020 19:43:16]
[14/03/2020 08:58:11] [14/03/2020 08:07:35] [14/03/2020 03:42:53] [13/03/2020 17:31:23] [13/03/2020 08:46:58]
[12/03/2020 00:07:53] [11/03/2020 13:43:33] [06/03/2020 17:03:50] [03/02/2020 09:29:38] [14/06/2018 21:02:34]
[21/03/2019 14:00:08] [28/03/2019 16:16:12] [08/01/2020 18:37:09] [28/12/2019 05:05:00] [14/12/2019 18:21:21]
Newest emails in Inbox
[20/03/2020 12:16:47] [20/03/2020 00:00:14] [19/03/2020 17:51:21] [19/03/2020 17:06:38] [19/03/2020 10:19:36]
[18/03/2020 16:21:25] [18/03/2020 01:43:37] [17/03/2020 11:14:29] [16/03/2020 20:39:58] [16/03/2020 16:48:40]
[15/03/2020 19:43:16] [14/03/2020 08:58:11] [14/03/2020 08:07:35] [14/03/2020 03:42:53] [13/03/2020 17:31:23]
[13/03/2020 08:46:58] [12/03/2020 00:07:53] [11/03/2020 13:43:33] [06/03/2020 17:03:50] [03/02/2020 09:29:38]
Oldest emails in Inbox
[14/06/2018 21:02:34] [21/03/2019 14:00:08] [28/03/2019 16:16:12] [14/12/2019 18:21:21] [28/12/2019 05:05:00]
[08/01/2020 18:37:09] [03/02/2020 09:29:38] [06/03/2020 17:03:50] [11/03/2020 13:43:33] [12/03/2020 00:07:53]
[13/03/2020 08:46:58] [13/03/2020 17:31:23] [14/03/2020 03:42:53] [14/03/2020 08:07:35] [14/03/2020 08:58:11]
[15/03/2020 19:43:16] [16/03/2020 16:48:40] [16/03/2020 20:39:58] [17/03/2020 11:14:29] [18/03/2020 01:43:37]
Things to note:
I have Dim OutApp As New Outlook.Application. The “New” says create the reference rather than just create a data item for a reference. This means I do not need GetObject or CreateObject. Outlook will only allow one occurrence of itself at a time so my “New” or your CreateObject will reference an existing occurrence or create a new one as necessary. I also have OutApp.Quit at the end. This closes Outlook whether or not it was already open. I don’t use Outlook while using Excel workbooks to access Outlook, so I want Outlook to be closed. If you care, use your Get or Create code but record which was successful, so you know if Quit is needed.
I have named my data item OutApp instead of olApp. Outlook uses the prefix “ol” for its constants, so I avoid this prefix in case my name matches one of Outlook’s.
I have used Session instead of GetNamespace("MAPI"). They are just different ways of achieving the same effect.
ItemsInbox is a “Collection”; what other languages call a “List”. A collection is like an array except you can add new entries before any existing entries, in the middle or after any existing entries. Any existing entries can be removed.
Outlook adds new emails at the end of the collection. So, if you read from first to last, the first email is the one that has been in Inbox longest first. If you read from last to first, the first email is the one that was added to Inbox most recently. This suggests that you can read from last to first and see the most recent emails first and you can stop when you reach an out-of-range email. However, if you move an old email from Inbox to another folder then move it back, it will not be returned to its old position; instead it will be added to the end.
In the macro below, I first list the ReceivedTime of twenty emails from first to last then from last to first. You may see that some are out of sequence.
I then list ReceivedTime of twenty emails after sorting by ReceivedTime in descending then ascending sequence.
Study the four blocks of dates. In particular, note the different sequences. I believe the code behind the third block of dates will be the most suitable for you.
I think I have covered everything but, as I said, come back will questions if necessary and I will repair any deficiencies.
Option Explicit
' Needs reference to "Microsoft Outlook n.nn Object Library"
' where n.nn depends on the version of Outlook you are using.
Sub Demo()
Dim FldrInbox As Outlook.Folder
Dim InxICrnt As Long
Dim InxIMax As Long
Dim ItemsInbox As Outlook.Items
Dim NumOnLine As Long
Dim OutApp As New Outlook.Application
Set FldrInbox = OutApp.Session.Folders("a.j.dallimore#xxxxxxx.com").Folders("Inbox")
Set ItemsInbox = FldrInbox.Items
If ItemsInbox.Count > 20 Then
InxIMax = 20
Else
InxIMax = ItemsInbox.Count
End If
Debug.Print "Oldest additions to Inbox"
NumOnLine = 0
For InxICrnt = 1 To InxIMax
Debug.Print " [" & ItemsInbox(InxICrnt).ReceivedTime & "]";
NumOnLine = NumOnLine + 1
If NumOnLine = 5 Then
Debug.Print
NumOnLine = 0
End If
Next
Debug.Print
Debug.Print "Newest additions to Inbox"
NumOnLine = 0
For InxICrnt = InxIMax To 1 Step -1
Debug.Print " [" & ItemsInbox(InxICrnt).ReceivedTime & "]";
NumOnLine = NumOnLine + 1
If NumOnLine = 5 Then
Debug.Print
NumOnLine = 0
End If
Next
Debug.Print
ItemsInbox.Sort "ReceivedTime", True
Debug.Print "Newest emails in Inbox"
NumOnLine = 0
For InxICrnt = 1 To InxIMax
Debug.Print " [" & ItemsInbox(InxICrnt).ReceivedTime & "]";
NumOnLine = NumOnLine + 1
If NumOnLine = 5 Then
Debug.Print
NumOnLine = 0
End If
Next
Debug.Print
ItemsInbox.Sort "ReceivedTime", False
Debug.Print "Oldest emails in Inbox"
NumOnLine = 0
For InxICrnt = 1 To InxIMax
Debug.Print " [" & ItemsInbox(InxICrnt).ReceivedTime & "]";
NumOnLine = NumOnLine + 1
If NumOnLine = 5 Then
Debug.Print
NumOnLine = 0
End If
Next
Debug.Print
Set ItemsInbox = Nothing
OutApp.Quit
Set OutApp = Nothing
End Sub
Revised requirement
Every week or so, you receive an email from a vendor containing an invoice in both PDF and XLSX formats. An Outlook rule recognises that email and moves it to a dedicated folder. Your team is not interested in the PDF version. The XLSX workbook does not have a consistent name. However, it consistently contains a worksheet “Shipped” that contains data that would be useful to your team. At present, you will not attempt to process that data by macro but you would like it consolidated into your own workbook so it can be viewed conveniently by the team. At present, the desired format is:
Columns B to K of row 4+ of worksheet “Shipped” for week starting 1Mar20
: : : : :
Columns B to K of row 4+ of worksheet “Shipped” for week starting 8Mar20
: : : : :
Columns B to K of row 4+ of worksheet “Shipped” for week starting 15Mar20
: : : : :
Reviewed ideas on achieving requirement
If you had asked a few months ago, I would have suggested linking the macro to the rule with “Run a script”. Microsoft has decided that “Run a script” is dangerous and it is no longer available by default. There is online help which explains how to make “Run a script” available but I suggest you wait until you are more experienced before attempting this.
I would suggest a revised format for the consolidated data:
Data from email received 2Mar20 9:10
Entire contents of worksheet “Shipped”
Data from email received 9Mar20 9:30
Entire contents of worksheet “Shipped”
Data from email received 16Mar20 9:20
Entire contents of worksheet “Shipped”
The heading rows mean there is no possible confusion about where one week’s data ends and another starts. Including the heading rows from the worksheet and all columns means that if they add another column it will still be included in your consolidation and you will have a warning if they change the sequence.
The macro does not have to be in the same workbook as the data. I usually keep the macro and the data separate for this type of task. The data is updated regularly, but the macro is only updated occasionally. For example, I download my bank statements every month and merge them into a continuous statement running back years. I only change the macro when they change the format of the download.
You do not need code that recognises the email by, for example, testing the UnRead property because the email of interest will be the latest in the dedicated folder. There is a possibility that you will call the macro before the new email has arrived, so the macro looks at last week’s email. If it checks the latest header within the consolidated worksheet, it will know it has an old workbook and can exit without making changes.
The following is my suggestion. Do not worry if you do not know how to achieve some of my ideas because I do know how to.
You have two workbooks with names like “Consolidation Macros V02.xlsm” and “Consolidated Data V25.xlsx”. Whenever a new invoice arrives, you open the latest consolidation macros workbook and start the consolidate macro. It is possible to start macros automatically when a workbook is opened but I suggest we leave that for the moment. The macro opens the latest data workbook and notes the date of the most recent addition. It accesses Outlook, finds the latest invoice email and checks its date against the date of the most recent addition. Unless the date of the latest invoice email is later that the latest addition, the macro terminates. If the date is satisfactory, the macro finds the XLSX attachment and saves it to disc. It opens that workbook, checks for worksheet “Shipped” and adds its contents to the bottom of worksheet “Shipped” within the latest consolidated data worksheet and saves the workbook with the next version number.
You will have noticed that I have a version number for each workbook. During my working life I saw too many disasters because people did not save a new version whenever they updated a file. I can drop the version numbers if you do not want them.
Do you think the above matches your requirement?
I have finished testing the system I proposed in my original answer. It is not exactly the same, for reasons I will explain later, but it matches in all important details. I am posting it as new answer so there is no confusion.
To test it, I created some workbooks which I named Test1, Test2, Test3 and so on. Within each workbook I created a worksheet “Shipped”. Each of these worksheets had a different number of rows and columns. Each cell contained “T-R-C” where T was the test number, R was the row and C was the column. These values made it very easy to check that data was copied correctly from the attachments to the consolidated worksheet. After deleting most of the rows so the structure was visible, the result of consolidation was:
You can see that my code can combine all the rows and all the columns from as many emails as required. My emails are not a week apart but that is not important.
My recommendation is that you try my macro as it is. You can then discuss the appearance with your colleagues, and we can then discuss how to change my macro to match your exact requirements.
Create a new disc folder and within it create two new workbooks: one ordinary (xlsx) and one macro-enabled (xlsm).
Name the ordinary workbook “Consolidated Data.xlsx”. Within it, rename the default worksheet as “Shipped”.
The name of the macro-enabled workbook is unimportant as is the name of the worksheet. Within the VBA Editor, create three modules and name then "LibExcel", "LibOutlook" and "ModConsolidate". Naming modules is not essential but dividing macros up by purpose and naming modules for those purposes makes life much easier.
I will tell you to move the code below to one of these three modules.
Module "ModConsolidate" is for code I have written specifically for your requirement. Module "LibExcel" is for code from my library of Excel related routines. Module "LibOutlook" is for code from my library of Outlook related routines.
When I end a project, I look through it to see if there is any code I might wish to use again. If there is, I extract it and save it in "PERSONAL.XLSB" which I use as my library. Any macro saved in this workbook is available to all other workbooks. Don’t bother today but when you have some spare time look up how to create "PERSONAL.XLSB". When you have created it, move modules "LibExcel" and "LibOutlook" to it. In "LibExcel", I have routines to find the last used row and column of a worksheet and to check is a named worksheet exists. In "LibOutlook" I have routines for opening and closing an instance of Outlook from Excel.
When I start a project, I look through my library for routines that might be appropriate. If necessary, a routine will be enhanced to provide functionality that I had not needed before. The result is I have a library of useful functions that get more powerful, and larger, as I complete each project.
I said I would have version numbers on the workbook I created for you. Unfortunately, the macros that handle this and related functionality are too large to post to Stack Overflow.
This code should go in LibExcel:
' Routines useful with Excel
Option Explicit
Public Sub FindLastRowCol(ByRef Wsht As Worksheet, ByRef RowLast As Long, _
ByRef ColLast As Long)
' Sets RowLast and ColLast to the last row and column with a value
' in worksheet Wsht
' The motivation for coding this routine was the discovery that Find by
' previous row found a cell formatted as Merge and Center but Find by
' previous column did not.
' I had known the Find would miss merged cells but this was new to me.
' Dec16 Coded
' 31Dec16 Corrected handling of UserRange
' 15Feb17 SpecialCells was giving a higher row number than Find for
' no reason I could determine. Added code to check for a
' value on rows and columns above those returned by Find
' 25Jun17 Found column with value about that found by Find
Dim ColCrnt As Long
Dim ColLastFind As Long
Dim ColLastOther As Long
Dim ColLastTemp As Long
Dim ColLeft As Long
Dim ColRight As Long
Dim Rng As Range
Dim RowIncludesMerged As Boolean
Dim RowBot As Long
Dim RowCrnt As Long
Dim RowLastFind As Long
Dim RowLastOther As Long
Dim RowLastTemp As Long
Dim RowTop As Long
With Wsht
Set Rng = .Cells.Find("*", .Range("A1"), xlFormulas, , xlByRows, xlPrevious)
If Rng Is Nothing Then
RowLastFind = 0
ColLastFind = 0
Else
RowLastFind = Rng.Row
ColLastFind = Rng.Column
End If
Set Rng = .Cells.Find("*", .Range("A1"), xlValues, , xlByColumns, xlPrevious)
If Rng Is Nothing Then
Else
If RowLastFind < Rng.Row Then
RowLastFind = Rng.Row
End If
If ColLastFind < Rng.Column Then
ColLastFind = Rng.Column
End If
End If
Set Rng = .Range("A1").SpecialCells(xlCellTypeLastCell)
If Rng Is Nothing Then
RowLastOther = 0
ColLastOther = 0
Else
RowLastOther = Rng.Row
ColLastOther = Rng.Column
End If
Set Rng = .UsedRange
If Rng Is Nothing Then
Else
If RowLastOther < Rng.Row + Rng.Rows.Count - 1 Then
RowLastOther = Rng.Row + Rng.Rows.Count - 1
End If
If ColLastOther < Rng.Column + Rng.Columns.Count - 1 Then
ColLastOther = Rng.Column + Rng.Columns.Count - 1
End If
End If
If RowLastFind < RowLastOther Then
' Higher row found by SpecialCells or UserRange
Do While RowLastOther > RowLastFind
ColLastTemp = .Cells(RowLastOther, .Columns.Count).End(xlToLeft).Column
If ColLastTemp > 1 Or .Cells(RowLastOther, 1).Value <> "" Then
Debug.Assert False
' Is this possible
' Row after RowLastFind has value
RowLastFind = RowLastOther
Exit Do
End If
RowLastOther = RowLastOther - 1
Loop
ElseIf RowLastFind > RowLastOther Then
Debug.Assert False
' Is this possible
End If
RowLast = RowLastFind
If ColLastFind < ColLastOther Then
' Higher column found by SpecialCells or UserRange
Do While ColLastOther > ColLastFind
RowLastTemp = .Cells(.Rows.Count, ColLastOther).End(xlUp).Row
If RowLastTemp > 1 Or .Cells(1, ColLastOther).Value <> "" Then
'Debug.Assert False
' Column after ColLastFind has value
' Possible causes:
' * Find does not recognise merged cells
' ' Find does not examine hidden cells
ColLastFind = ColLastOther
Exit Do
End If
ColLastOther = ColLastOther - 1
Loop
ElseIf ColLastFind > ColLastOther Then
Debug.Assert False
' Is this possible
End If
ColLast = ColLastFind
End With
End Sub
Public Function WshtExists(ByRef Wbk As Workbook, ByVal WshtName As String) As Boolean
' Returns True if Worksheet WshtName exists within
' * if Wbk Is Nothing the workbook containing the macros
' * else workbook Wbk
' 21Aug16 Coded by Tony Dallimore
' 14Feb17 Coded alternative routine that cycled through the existing worksheets
' matching their names against WshtName to check if use of "On Error Resume Next"
' was the faster option. I needed to call the routines 6,000,000 times each to
' get an adequate duration for comparison. This version took 33 seconds while
' the alternative took 75 seconds.
' 21Feb20 Added "As Boolean" to declaration. Do not understand how routine worked
' without it.
Dim WbkLocal As Workbook
Dim Wsht As Worksheet
If Wbk Is Nothing Then
Set WbkLocal = ThisWorkbook
Else
Set WbkLocal = Wbk
End If
Err.Clear
On Error Resume Next
Set Wsht = WbkLocal.Worksheets(WshtName)
On Error GoTo 0
If Wsht Is Nothing Then
WshtExists = False
Else
WshtExists = True
End If
End Function
This code should go in LibOutlook
' Routines useful with Outlook.
Option Explicit
Public Sub OutAppClose(ByRef OutApp As Outlook.Application, ByVal Created As Boolean)
' If Created is True, quit the current instance if Outlook.
If Created Then
OutApp.Quit
End If
Set OutApp = Nothing
End Sub
Public Function OutAppGetCreate(ByRef Created As Boolean) As Outlook.Application
' Return a reference to the Outlook Application.
' Set Created to True if the reference is to a new application and to
' False if the reference is to an existing application.
' If Nothing is returned, the routine has been unable to get or create a reference.
' Only one instance of Outlook can be running. CreateObject("Outlook.Application")
' will return a reference to the existing instance if one is already running or
' will start a new instance if one is not running. The disadvantage of using
' CreateObject, is the caller does not know if Outlook was running so does not know
' whether or not to quit Outlook when it has finished using Outlook. By setting
' Created, this routine allows the caller to only quit if this is appropriate.
Set OutAppGetCreate = Nothing
On Error Resume Next
Set OutAppGetCreate = GetObject(, "Outlook.Application")
On Error GoTo 0
If OutAppGetCreate Is Nothing Then
On Error Resume Next
Set OutAppGetCreate = CreateObject("Outlook.Application")
On Error GoTo 0
If OutAppGetCreate Is Nothing Then
Call MsgBox("I am unable to access Outlook", vbOKOnly)
Exit Function
End If
Created = True
Else
Created = False
End If
End Function
This code should go in ModConsolidate:
Option Explicit
' * Need reference to "Microsoft Outlook nn.n Object Library"
' where nn.n depends on the version of Office being used.
' * Needs reference to "Microsoft Scripting Runtime"
Const HeaderForData As String = "Data from email received"
Const WbkConName As String = "Consolidated Data.xlsx"
Const WshtName As String = "Shipped" ' Also used for name of workbooks
Sub ConsolidateDataFromShippedWshts() ()
' Outlook used "ol" as a prefix for its constants. I do not use the same
' prefix to avoid a clash.
Dim OutApp As Outlook.Application
Dim OutAppCreated As Boolean
Dim ColConLast As Long ' Last column of worksheet "Shipped" in consolidated workbook
Dim ColSrcLast As Long ' Last column of worksheet "Shipped" in source workbook
Dim DateLatestExisting As Date ' Date of last block of data in consolidated workbook
Dim DateStr As String ' Date extracted from header row
Dim FldrShipped As Outlook.Folder ' Outlook Folder containing source emails
Dim InxA As Long ' Index into attachments
Dim InxI As Long ' Index into mail items
Dim InxW As Long ' Into into WbkSrcNames
Dim ItemsShipped As Items ' Items in source folder
Dim Path As String ' Disc folder containing workbooks
Dim Rng As Range ' Various uses
Dim RowConCrnt As Long ' Current row of worksheet "Shipped" in consolidated workbook
Dim RowConLast As Long ' Last row of worksheet "Shipped" in consolidated workbook
Dim RowSrcLast As Long ' Last row of worksheet "Shipped" in source workbook
Dim WbkCon As Workbook ' Consolidated workbook
Dim WbkMacros As Workbook ' This workbook
Dim WbkSrc As Workbook ' Workbook extracted from email
Dim WbkSrcName As String ' Name of workbook extracted from email
Dim WbkSrcNameDates As Collection ' Collection of the names and dates of workbooks extracted from emails
Dim WshtCon As Worksheet ' Worksheet "Shipped" in consolidated workbook
Dim WshtSrc As Worksheet ' Worksheet "Shipped" in source workbook
Application.ScreenUpdating = False
Set WbkMacros = ThisWorkbook
Path = WbkMacros.Path
' ### Change if you want a different name for consolidated workbook
Set WbkCon = Workbooks.Open(Path & "\" & WbkConName)
Set WshtCon = WbkCon.Worksheets(WshtName)
' Find last used row of consolidated worksheet
Call FindLastRowCol(WshtCon, RowConLast, ColConLast)
If RowConLast = 0 Then
' No data added yet
DateLatestExisting = 0
Else
' Search up for header for last block of data added
With WshtCon
Set Rng = .Columns(1).Find( _
What:=HeaderForData, After:=.Cells(RowConLast + 1, 1), _
LookIn:=xlValues, LookAt:=xlPart, _
SearchOrder:=xlByRows, SearchDirection:=xlPrevious, _
MatchCase:=False, SearchFormat:=False)
If Rng Is Nothing Then
Debug.Assert False
' It should not be possible to be here. Either the worksheet is empty
' and RowColLast = 0 or one or more blocks of data, each with a header,
' have been added. It appears the worksheet is not as it should be.
DateLatestExisting = 0
Else
DateStr = Mid$(.Cells(Rng.Row, 1).Value, Len(HeaderForData) + 2)
If IsDate(DateStr) Then
DateLatestExisting = DateValue(DateStr) + TimeValue(DateStr)
Else
Debug.Assert False
' It should not be possible to be here. The text after HeaderForData
' should be a valid date. It appears the worksheet is not as it should be.
DateLatestExisting = 0
End If
End If
End With
End If
Set OutApp = OutAppGetCreate(OutAppCreated)
If OutApp Is Nothing Then
' OutAppGetCreated() failed. The user has already been told.
Exit Sub
End If
' ### Change to access folder where you store these emails
Set FldrShipped = OutApp.Session.Folders("MyName#MyIsp").Folders("Test")
' Create list of items in folder sorted by ReceivedTime
Set ItemsShipped = FldrShipped.Items
ItemsShipped.Sort "ReceivedTime", True
Set WbkSrcNameDates = New Collection
' Read items, newest first, until reach an item at or before DateLatestExisting
' Save xlsx attachment, if any, and record names in WbkSrcNames
For InxI = 1 To ItemsShipped.Count
If TypeName(ItemsShipped(InxI)) = "MailItem" Then
If ItemsShipped(InxI).ReceivedTime <= DateLatestExisting Then
' No more unprocessed emails
Exit For
End If
' Save Xlsx attachment, if any
For InxA = 1 To ItemsShipped(InxI).Attachments.Count
If LCase(Right$(ItemsShipped(InxI).Attachments(InxA).FileName, 5)) = ".xlsx" Then
' Have found required attachment. Save with name based on date received
WbkSrcName = WshtName & " " & Format(ItemsShipped(InxI).ReceivedTime, "yymmdd hhmmss") & ".xlsx"
ItemsShipped(InxI).Attachments(InxA).SaveAsFile Path & "\" & WbkSrcName
WbkSrcNameDates.Add VBA.Array(WbkSrcName, ItemsShipped(InxI).ReceivedTime)
Exit For
End If
Next
End If
Next
Call OutAppClose(OutApp, OutAppCreated)
If WbkSrcNameDates.Count = 0 Then
' No new emails with xlsx attachments
WbkCon.Close SaveChanges:=False
Call MsgBox("No new emails containing an xlsx attachment", vbOKOnly)
Set WshtCon = Nothing
Set WbkCon = Nothing
Set WbkMacros = Nothing
Exit Sub
End If
' WbkSrcNameDates contains the names and received dates of the new workbooks
' with the newest first.
' Extract names in reverse order (oldest first) and add contents of worksheet
' "Shipped" to bottom of worksheet "Shipped" of consolidated workbook
For InxW = WbkSrcNameDates.Count To 1 Step -1
Set WbkSrc = Workbooks.Open(Path & "\" & WbkSrcNameDates(InxW)(0))
If WshtExists(WbkSrc, WshtName) Then
' Worksheet "Shipped" exists
Set WshtSrc = WbkSrc.Worksheets(WshtName)
Call FindLastRowCol(WshtSrc, RowSrcLast, ColSrcLast)
RowConCrnt = RowConLast + 1 ' Advance to first free row
With WshtCon.Cells(RowConCrnt, 1)
.Value = HeaderForData & " " & Format(WbkSrcNameDates(InxW)(1), "d-mmm-yy h:mm:ss")
.Font.Bold = True
End With
RowConCrnt = RowConCrnt + 1
With WshtSrc
.Range(.Cells(1, 1), .Cells(RowSrcLast, ColSrcLast)).Copy _
Destination:=WshtCon.Cells(RowConCrnt, 1)
End With
RowConLast = RowConCrnt + RowSrcLast - 1
End If
WbkSrc.Close SaveChanges:=False
Next
' Position cursor to header for latest data
Application.ScreenUpdating = True
WshtCon.Activate
WshtCon.Cells(RowConLast - RowSrcLast, 1).Select
Application.Goto ActiveCell, True
WbkCon.Close SaveChanges:=True
Set WshtCon = Nothing
Set WbkCon = Nothing
Set WbkMacros = Nothing
End Sub
At the top of ModConsolidate, it says it needs references to "Microsoft Outlook nn.n Object Library", where nn.n depends on the version of Office being used, and "Microsoft Scripting Runtime". If you are unsure what that means, ask and I will add an explanation.
Line 173 of ModConsolidate is Set FldrShipped = OutApp.Session.Folders("MyName#MyIsp").Folders("Test"). This references the Outlook folder in which I placed the test emails. Replace my Outlook folder with the one holding these emails on your system. Place as many of these emails as you have in that folder.
Run macro ConsolidateDataFromShippedWshts(). This macro will:
Open workbook “Consolidated Data.xlsx”
Check worksheet “Shipped” and find that it is empty.
Open Outlook if not already open.
Access the Outlook folder and extract the workbook from every email because worksheet “Shipped” is empty. Workbooks will be saved with the name “Shipped yymmdd hhmmss.xlsx”. If worksheet “Shipped” had not been empty, it would only have extracted workbooks from the newer emails.
Close Outlook if it was not open.
Open each of the new workbooks in turn and add the contents of their worksheet “Shipped” to worksheet “Shipped” within “Consolidated Data.xlsx”.
I have tested macro ConsolidateDataFromShippedWshts() thoroughly but only with my fake workbooks and emails. It should work properly unless I have misunderstood the nature of your workbooks and emails. If something goes wrong, describe the problem to me and I will try to diagnose the cause.
If everything works as expected. Review “Consolidated Data.xlsx” and discuss it with your colleagues. While you are doing that, I will start adding more information about my macro to this answer.
"... to extract email attachments starting from date which I enter in dialog box (I want to extract email attachments just for emails which I received in last seven day not the whole folder)."
Option Explicit
Sub Extract_attachments_recent_emails()
' code for Excel
Dim olApp As Object
Dim olMail As Object
Dim olItems As Object
Dim olfolder As Object
Dim J As Long
Dim strFolder As String
Dim ageDays As Long
Dim strFilter As String
Dim resItems As Object
Set olApp = GetObject(, "Outlook.Application")
If Err.Number = 429 Then
Set olApp = CreateObject("Outlook.Application")
End If
strFolder = ThisWorkbook.Path & "\Extract"
Set Olfolder = olApp.GetNamespace("MAPI").Folders("MyEmailAddress").Folders("Inbox")
Set olItems = olfolder.items
' save time with hardcoded number
'ageDays = 7
' be flexible with InputBox
ageDays = InputBox("ageDays", "Input age of oldest mail in days", "7")
strFilter = "[ReceivedTime]>'" & Format(Date - ageDays, "DDDDD HH:NN") & "'"
Set resItems = olItems.Restrict(strFilter)
For Each olMail In resItems
If olMail.Attachments.Count > 0 Then
For J = 1 To olMail.Attachments.Count
OlMail.Attachments.Item(J).SaveAsFile strFolder & "\" & OlMail.Attachments.Item(J).Filename
Next J
End If
Set olMail = Nothing
Next
MsgBox ("Done")
End Sub
Im going to start by telling what my intention was with this code
In my job we have to open every sales order that will be sent in that day and check for the itens to be shipped manually.
Since its very time consuming i tought in creating a worksheet that it will look for the itens in every sales order and copy/paste in my master so i can know what i need to get.
However to my sheet works I had to make a few changes in the Sales order, but now I want to create a error check, that if the file that it was open was an older SO it will tell me its order number so later i can check it.
Also i want to check if by some reason nothing was found in that SO.
Now ill explain what my code does (I have a little knowledge in coding and in excel vba, so please dont judge my ugly script)
Using the value of a cell in a range, it will open the folder and file that matches it's value, then will look for a specific range and for a specific cell value, in this case "Perfil", if this value is found it will copy some cells.
After looking for that file it will open another one and do the same.
However if "Perfil" is not found it wont copy and paste anything and it will just go to the next file.
Public Sub test()
On Error GoTo Errormsg
Dim wbk As Workbook
Dim Fonte As Workbook
Dim Dest As Workbook
Dim Filename As String
Dim FolderName As String
Dim Arquivo As String
Dim Path As String
Dim celula As Range
Dim cll As Range
Dim Inicio As Range
Dim Fim As Range
Dim OffInicio As Range
Dim OffFim As Range
Dim busca As Range
Application.ScreenUpdating = False
Set Dest = Workbooks("testee.xlsm")
Path = 'My file path
lrow = Sheets(1).Range("A" & Sheets(1).Rows.Count).End(xlUp).Row
For Each celula In Dest.Worksheets(1).Range("A3:A" & lrow)
Dest.Sheets(1).Activate
Pedido = Cells(celula.Row, 1)
FolderName = Pedido & "*"
Arquivo = "\" & Pedido
Folder = Dir(Path & FolderName, vbDirectory)
Filename = Dir(Path & Folder & Arquivo & "*.xlsx")
Set wbk = Workbooks.Open(Path & Folder & "\" & Filename, 0)
Set Fonte = Workbooks(Filename)
Fonte.Activate
Set Inicio = Fonte.Worksheets(1).Cells.Find(what:="MODO DE FIXAÇÃO DO PRODUTO")
Set Fim = Fonte.Worksheets(1).Cells.Find(what:="OBSERVAÇÕES")
Set OffInicio = Inicio.Offset(1, 0)
Set OffFim = Fim.Offset(-1, 1)
Set busca = Range(OffInicio, OffFim).Columns(5)
Set check = Range(OffInicio, OffFim).Columns(9)
Range(OffInicio, OffFim).Columns(5).Select
Set busca = Selection
For Each cl In busca
tipo = Cells(cl.Row, 5).Value
If tipo = "Perfil" Then
tamanho = Cells(cl.Row, 6).Value
expessura = Cells(cl.Row, 11).Value
cor = Cells(cl.Row, 12).Value
lrow2 = Dest.Sheets(2).Range("D" & Dest.Sheets(2).Rows.Count).End(xlUp).Row
linha = lrow2 + 1
Dest.Sheets(2).Range("D" & linha).Value = Pedido
Dest.Sheets(2).Range("E" & linha).Value = tamanho
Dest.Sheets(2).Range("H" & linha).Value = cor
End If
Next cl
End If
Next celula
Errormsg:
lrow2 = Dest.Sheets(2).Range("D" & Dest.Sheets(2).Rows.Count).End(xlUp).Row
linha = lrow2 + 1
Dest.Sheets(2).Range("D" & linha).Value = Pedido
Dest.Sheets(2).Range("E" & linha).Value = "Pedido com modelo Antigo"
End Sub
I want to know the files that no data has been copied, so I can check manually and see why it wasnt.
To do that i tought in checking if in that file any data has been copied and pasted in my master sheet, if nothing was done it will send a message in a cell telling its number so i can check it later.
Now is my question:
I dont know if is possible to check if anything was pasted from that file, in case is possible, how i do that?
I cant just check if "Perfil" exists because for my sheet works I had to change a few things in the sheets that had the data I needed, and "perfil"is not something that the older version of it had.
Also in my new version "Perfil"is not the only value that the column can have so i cant just check if perfil is not found there.
There are a few ways you can check if anything has changed in the workbook. I'd suggest this method:
In any (new or existing) standard module, add a public variable declaration at or near the top of the module:
Public wksChanged As Boolean
For each worksheet that you want to monitor for changes, open the Worksheet's module by right-clicking the worksheet's tab and clicking View Code:
...and then add this procedure (to each applicable worksheet module):
Private Sub Worksheet_Change(ByVal Target As Range)
wksChanged = True
End Sub
wksChanged will default to False when the workbook is first opened, and will change to True when any cell is changed. You can "reset" it at any time with:
wksChanged = False
I have form on my website which gets emailed when the customer completes it, then looks like this:-
You got mail from Mr Kelley McIntyre.
Here is the form data:
First Name : Mr XXXXX
Last Name : XXXXXX
Company Name : Army
Email Address : XXXX#hotmail.co.uk
Telephone/Mobile No : 0123456789
Date of Event : 14/12/2013
Number of Guests : 80
Budget : 6500-7000
Type of Event : Other
Catering Required : Yes
Drinks and Entertainment Requirements : christmas meal, welcome drink, wine at table
British Army Warrant Officers & Sergeants plus wives and partners
How Did You Hear About Us? : Google
As you can see its fairly simple form, however I need to export this data into Excel every time I get one of these emails, so I can keep a record of all the enquiries we get.
Can someone help?
I know how to do a Macro, but if its VBA, then I'm lost, so its needs to be in idiot format if possible!
You can start with writing a macro to process an mail item. And setup Outlook Rule to pickup this type of email from Subject/Account then run the macro. Change sExcelFile, sRecordSheet, iC as you see fit. I have made assumptions.
This Code below is for Outlook, please note you need a running Outlook all the time to have this automation. It should get you started half way. Note you need "Microsoft Excel x.0 Object Library" in your References.
Public Sub Rules_WebSiteFormRecord(oMail As MailItem)
Const sExcelFile As String = "C:\Test\Record.xlsx"
Const sRecordSheet As String = "Record" ' Worksheet name
Dim oExcel As Excel.Application, oWB As Excel.Workbook, oWS As Excel.worksheet
Dim arrTxt As Variant, oLine As Variant, iR As Long, iC As Long, bWrite As Boolean
Set oExcel = CreateObject("excel.application")
Set oWB = oExcel.Workbooks.Open(FileName:=sExcelFile)
Set oWS = oWB.Worksheets(sRecordSheet)
' Make Excel visible for Debug purpose:
oExcel.Visible = True
' Find next row of Last used row in Excel worksheet
iR = oWS.Cells(Rows.Count, 1).End(xlUp).Row + 1
' Process email body and store it into columns of worksheet "sRecordSheet"
'Debug.Print oMail.Body
' Store received time of email in Column A
oWS.Cells(iR, 1).Value = oMail.ReceivedTime
' Split the email body into lines then process each
arrTxt = Split(oMail.Body, vbCrLf)
For Each oLine In arrTxt
bWrite = False
' store data according to text in line
If InStr(1, oLine, "First Name", vbTextCompare) Then
iC = 2 ' Column of First Name
bWrite = True
ElseIf InStr(1, oLine, "Last Name", vbTextCompare) Then
iC = 3 ' Column of First Name
bWrite = True
' Add the rest of the fields...
End If
If bWrite Then
oWS.Cells(iR, iC).Value = Split(oLine, ":")(1)
iR = iR + 1
End If
Next
Set oWS = Nothing
' Close the workbook with saving changes
oWB.Close True
Set oWB = Nothing
Set oExcel = Nothing
' mark it as Read if no error occurred
If Err.Number = 0 Then
oMail.UnRead = False
Else
MsgBox "ERR(" & Err.Number & ":" & Err.Description & ") while processing " & oMail.Subject
Err.Clear
End If
End Sub
All right, this is my second attempt at a code, and the second VBA macro project I've been assigned to work on. I've been working to learn VBA as my first coding language for the last week and a half, so I apologize for silly mistakes. That said, straight to business. Here's what I put together for a word document macro:
Sub MacroToUpdateWordDocs()
'the following code gets and sets a open file command bar for word documents
Dim Filter, Caption, SelectedFile As String
Dim Finalrow As String
Dim FinalrowName As String
Filter = "xlsx Files (*.xlsx),*.xlsx"
Caption = "Please Select A .xlsx File, " & TheUser
SelectedFile = Application.GetOpenFilename(Filter, , Caption)
'check if value is blank if it is exit
Finalrow = Cells(Rows.Count, 1).End(xlUp).Row
FinalrowName = Finalrow + 1
If (Trim(SelectedFile) = "") Then
Exit Sub
Else
'setting up the inital word application object
Set auditmaster = CreateObject("excel.sheet")
'opening the document that is defined in the open file dialog
auditmaster.Application.Workbooks.Open (SelectedFile)
'ability to change wether it needs to burn cycles updating the UI
auditmaster.Visible = False
'declare excel sheet
Dim wdoc As Document
'set active sheet
Set wdoc = Application.ActiveDocument
Dim i As Integer
Dim u As Integer
Dim ColumnAOldAddy As String
Dim ColumnCNewAddy As String
u = 1
i = 1
'MsgBox (wordapp.ActiveDocument.Hyperlinks.Count)
'Sets up a loop to go through the Excel Audit file rows.
For i = 1 To auditmaster.ActiveSheet.Rows.Count
'Identifies ColumnAOldAddy and ColumnCNewAddy as columns A and C for each row i. Column A is the current hyperlink.address, C is the updated one.
ColumnAOldAddy = auditmaster.Cells(i, 1)
ColumnCNewAddy = auditmaster.Cells(i, 3)
'If C has a new hyperlink in it, then scan the hyperlinks in wdoc for a match to A, and replace it with C
If ColumnCNewAddy = Not Nothing Then
For u = 1 To doc.Hyperlinks.Count
'If the hyperlink matches.
If doc.Hyperlinks(u).Address = ColumnAOldAddy Then
'Change the links address.
doc.Hyperlinks(u).Address = ColumnCNewAddy
End If
'check the next hyperlink in wdoc
Next
End If
'makes sure the macro doesn't run on into infinity.
If i = Finalrow + 1 Then GoTo Donenow
'Cycles to the next row in the auditmaster workbook.
Next
Donenow:
'Now that we've gone through the auditmaster file, we close it.
auditmaster.ActiveSheet.Close SaveChanges:=wdDoNotSaveChanges
auditmaster.Quit SaveChanges:=wdDoNotSaveChanges
Set auditmaster = Nothing
End If
End Sub
So, this code is suppose to take a hyperlink audit file created by my first macro (The last bugs fixed and functioning wonderfully thanks to the Stack Overflow community!). The audit file has 3 columns and a row for each hyperlink it found in the target .docx: A = hyperlink address, B = Hyperlink displaytext, and C = the new Hyperlink address
When the code runs from the .docx file to be updated, it allows the user to choose the audit file. From there, it goes row by row to check if an updated hyperlink address has been written into the C column by the older audited address/display name, then searches the .docx file for the old hyperlink address and replaces it with the new hyperlink address. At that point, it finishes searching the document then moves on to the next row in the audit excel file.
My problem is that much of this code is copy/pasted out of code from an excel macro. I have been having a hell of a time figuring out how translate that code into something that identifies/references the word/excel documents appropriately. I'm hoping someone with more experience can take a peek at this macro and let me know where I've completely buggered up. It keeps giving me "Method or data member not found" errors all over the place currently, primarily concerning where I attempt to reference the audit excel file. I'm pretty sure that this is a relatively easy fix, but I don't have the vocabulary to figure out how to Google the answer!
Compiled OK, but not tested:
Sub MacroToUpdateWordDocs()
Dim Filter, Caption, SelectedFile As String
Dim Finalrow As String
Dim appXL As Object
Dim oWB As Object
Dim oSht As Object
Dim wdoc As Document
Dim ColumnAOldAddy As String
Dim ColumnCNewAddy As String
Dim i As Long
Dim h As Word.Hyperlink
Dim TheUser As String
Filter = "xlsx Files (*.xlsx),*.xlsx"
Caption = "Please Select A .xlsx File, " & TheUser
Set appXL = CreateObject("excel.application")
appXL.Visible = True
SelectedFile = appXL.GetOpenFilename(Filter, , Caption)
appXL.Visible = False
If Trim(SelectedFile) = "" Then
appXL.Quit
Exit Sub
Else
Set oWB = appXL.Workbooks.Open(SelectedFile)
Set oSht = oWB.worksheets(1)
Finalrow = oSht.Cells(oSht.Rows.Count, 1).End(-4162).Row '-4162=xlUp
End If
Set wdoc = Application.ActiveDocument
For i = 1 To Finalrow
ColumnAOldAddy = oSht.Cells(i, 1).Value
ColumnCNewAddy = oSht.Cells(i, 3).Value
If ColumnCNewAddy <> ColumnAOldAddy Then
For Each h In wdoc.Hyperlinks
If h.Address = ColumnAOldAddy Then
h.Address = ColumnCNewAddy
End If
Next h
End If
Next i
oWB.Close False
appXL.Quit
End Sub
I already have a macro that creates sheets and some other stuff. After a sheet has been created do I want to call another macro that copies data from a second excel (its open) to first and active excel file.
First I want to copy to headers, but I cant get that to work - keep getting errors.
Sub CopyData(sheetName as String)
Dim File as String, SheetData as String
File = "my file.xls"
SheetData = "name of sheet where data is"
# Copy headers to sheetName in main file
Workbooks(File).Worksheets(SheetData).Range("A1").Select # fails here: Method Select for class Range failed
Workbooks(File).Worksheets(SheetData).Range(Selection, Selection.End(xlToRight)).Select
Workbooks(File).Worksheets(SheetData).Selection.Copy ActiveWorkbook.Sheets(sheetName).Cells(1, 1)
End Sub
What is wrong ?
I really want to avoid having to make "my file.xls" active.
Edit: I had to give it up and copy the SheetData to target file as a new sheet, before it could work.
Find and select multiple rows
Two years later (Found this on Google, so for anyone else)... As has been mentioned above, you don't need to select anything. These three lines:
Workbooks(File).Worksheets(SheetData).Range("A1").Select
Workbooks(File).Worksheets(SheetData).Range(Selection, Selection.End(xlToRight)).Select
Workbooks(File).Worksheets(SheetData).Selection.Copy ActiveWorkbook.Sheets(sheetName).Cells(1, 1)
Can be replaced with
Workbooks(File).Worksheets(SheetData).Range(Workbooks(File).Worksheets(SheetData). _
Range("A1"), Workbooks(File).Worksheets(SheetData).Range("A1").End(xlToRight)).Copy _
Destination:=ActiveWorkbook.Sheets(sheetName).Cells(1, 1)
This should get around the select error.
Best practice is to open the source file (with a false visible status if you don't want to be bother) read your data and then we close it.
A working and clean code is avalaible on the link below :
http://vba-useful.blogspot.fr/2013/12/how-do-i-retrieve-data-from-another.html
Would you be happy to make "my file.xls" active if it didn't affect the screen? Turning off screen updating is the way to achieve this, it also has performance improvements (significant if you are doing looping while switching around worksheets / workbooks).
The command to do this is:
Application.ScreenUpdating = False
Don't forget to turn it back to True when your macros is finished.
I don't think you need to select anything at all. I opened two blank workbooks Book1 and Book2, put the value "A" in Range("A1") of Sheet1 in Book2, and submitted the following code in the immediate window -
Workbooks(2).Worksheets(1).Range("A1").Copy Workbooks(1).Worksheets(1).Range("A1")
The Range("A1") in Sheet1 of Book1 now contains "A".
Also, given the fact that in your code you are trying to copy from the ActiveWorkbook to "myfile.xls", the order seems to be reversed as the Copy method should be applied to a range in the ActiveWorkbook, and the destination (argument to the Copy function) should be the appropriate range in "myfile.xls".
I was in need of copying the data from one workbook to another using VBA. The requirement was as mentioned below 1. On pressing an Active X button open the dialogue to select the file from which the data needs to be copied. 2. On clicking OK the value should get copied from a cell / range to currently working workbook.
I did not want to use the open function because it opens the workbook which will be annoying
Below is the code that I wrote in the VBA. Any improvement or new alternative is welcome.
Code: Here I am copying the A1:C4 content from a workbook to the A1:C4 of current workbook
Private Sub CommandButton1_Click()
Dim BackUp As String
Dim cellCollection As New Collection
Dim strSourceSheetName As String
Dim strDestinationSheetName As String
strSourceSheetName = "Sheet1" 'Mention the Source Sheet Name of Source Workbook
strDestinationSheetName = "Sheet2" 'Mention the Destination Sheet Name of Destination Workbook
Set cellCollection = GetCellsFromRange("A1:C4") 'Mention the Range you want to copy data from Source Workbook
With Application.FileDialog(msoFileDialogOpen)
.AllowMultiSelect = False
.Show
'.Filters.Add "Macro Enabled Xl", "*.xlsm;", 1
For intWorkBookCount = 1 To .SelectedItems.Count
Dim strWorkBookName As String
strWorkBookName = .SelectedItems(intWorkBookCount)
For cellCount = 1 To cellCollection.Count
On Error GoTo ErrorHandler
BackUp = Sheets(strDestinationSheetName).Range(cellCollection.Item(cellCount))
Sheets(strDestinationSheetName).Range(cellCollection.Item(cellCount)) = GetData(strWorkBookName, strSourceSheetName, cellCollection.Item(cellCount))
Dim strTempValue As String
strTempValue = Sheets(strDestinationSheetName).Range(cellCollection.Item(cellCount)).Value
If (strTempValue = "0") Then
strTempValue = BackUp
End If
Sheets(strDestinationSheetName).Range(cellCollection.Item(cellCount)) = strTempValue
ErrorHandler:
If (Err.Number <> 0) Then
Sheets(strDestinationSheetName).Range(cellCollection.Item(cellCount)) = BackUp
Exit For
End If
Next cellCount
Next intWorkBookCount
End With
End Sub
Function GetCellsFromRange(RangeInScope As String) As Collection
Dim startCell As String
Dim endCell As String
Dim intStartColumn As Integer
Dim intEndColumn As Integer
Dim intStartRow As Integer
Dim intEndRow As Integer
Dim coll As New Collection
startCell = Left(RangeInScope, InStr(RangeInScope, ":") - 1)
endCell = Right(RangeInScope, Len(RangeInScope) - InStr(RangeInScope, ":"))
intStartColumn = Range(startCell).Column
intEndColumn = Range(endCell).Column
intStartRow = Range(startCell).Row
intEndRow = Range(endCell).Row
For lngColumnCount = intStartColumn To intEndColumn
For lngRowCount = intStartRow To intEndRow
coll.Add (Cells(lngRowCount, lngColumnCount).Address(RowAbsolute:=False, ColumnAbsolute:=False))
Next lngRowCount
Next lngColumnCount
Set GetCellsFromRange = coll
End Function
Function GetData(FileFullPath As String, SheetName As String, CellInScope As String) As String
Dim Path As String
Dim FileName As String
Dim strFinalValue As String
Dim doesSheetExist As Boolean
Path = FileFullPath
Path = StrReverse(Path)
FileName = StrReverse(Left(Path, InStr(Path, "\") - 1))
Path = StrReverse(Right(Path, Len(Path) - InStr(Path, "\") + 1))
strFinalValue = "='" & Path & "[" & FileName & "]" & SheetName & "'!" & CellInScope
GetData = strFinalValue
End Function