I'm trying to develop a simple code which combines multiple small docx(or rtf) into one docx.
document creation should be based on the following:
1. I have in column A a list of names of the small docs
2. in Column B is one of 2 entries (yes/no)
ex:
A B
doc1 yes
doc2 no
doc3 yes
doc4 yes
doc5 no
3. I have already supplied the location of the small docs in a cell in the sheet
4. also supplied the place where the new (merged) doc would be placed
below is a sample of the code
Application.ScreenUpdating = False
strRandom = Replace(Replace(Replace(Now, ":", ""), "/", ""), " ", "")
MergeFileName = "Merger" & strRandom & ".doc"
MergeFolder = mainworkbook.Sheets("Main").Range("L10").Value
Set objWord = CreateObject("Word.Application")
Set objDoc = objWord.Documents.Add
objWord.Visible = True
Set objSelection = objWord.Selection
'objSelection.TypeText ("Saving this file after this text")
objDoc.SaveAs (MergeFolder & MergeFileName)
For i = 1 To NoOfFiles
If Range("B" & i).Value = "Yes" Then
Set objTempWord = CreateObject("Word.Application")
Set tempDoc = objWord.Documents.Open(Folderpath & "\" & Range("A" & i).Value)
'Set wb = Documents.Open(MyPath & "\" & MyName)
Set objTempSelection = objTempWord.Selection
'objTempSelection.WholeStory
'Selection.Copy
tempDoc.Range.Select
tempDoc.Range.Copy
'Windows(1).Activate
'Selection.EndKey Unit:=wdLine
'objSelection.TypeParagraph
objSelection.PasteSpecial xlPasteAll
.InsertBreak wdPageBreak
tempDoc.Close
End If
Next
objDoc.Save
Application.ScreenUpdating = True
mainworkbook.Sheets("Main").Activate
MsgBox "Completed...Merge File is saved at " & MergeFolder & MergeFileName
FetchFileClicked = False
End Sub
the problem with this code is that it never kills the temp doc opened, so I have 10 docs to be merged I'll end up with 10 WINWORD process and no MSWord windows.
is there a way to fix that problem.
I have heard that if I convert all small docs to .rtf I can parse the file without the need to open it.
I am pulling this code from a userform I use to populate documents from a collection of templates, so my apologies if this doesn't work exactly as I will describe:
Sub Insert_File_From_Location()
CreateObject (Word.Application.Documents.Add)
If ComboBox1.Value = "blah" Then
Selection.InsertFile FileName:="C:\blah.docx"
Else:
End If
End Sub
I took out all of the else if statements to make it look simpler.
A possibility is, taking the above code and manipulating to let your Column B cells define the ComboBox1.Value (yes/no entries). You then would have the Selection.InsertFile FileName:= direct to the location defined in the adjacent cell in Column A. This would need to be a dynamic reference utilizing a loop through the last row.
What I don't have going on is auto-saving the merged document because I have to, typically, manipulate the contents and remove sections that are standard for some templates in my collection.
Hopefully that helps, Karim! When working with just Word docs (.doc or .docx) I do have processes show up in my task manager, but they go away when the insert is completed, leaving me with one Word process for the opened document.
After some serious troubleshooting I finally got it to work, and here the code below.
Application.ScreenUpdating = False
strRandom = Replace(Replace(Replace(Now, ":", ""), "/", ""), " ", "")
MergeFileName = "Merger" & strRandom & ".doc"
MergeFolder = mainworkbook.Sheets("Main").Range("L10").Value
Set objWord = CreateObject("Word.Application")
Set appWord = GetObject(, "Word.Application")
Set objDoc = objWord.Documents.Add
objWord.Visible = True
Set objSelection = objWord.Selection
objDoc.SaveAs (MergeFolder & MergeFileName)
For i = 1 To NoOfFiles
If Range("B" & i).Value = "Yes" Then
myName = (Folderpath & "\" & Range("A" & i).Value)
With appWord.Selection
.InsertFile Filename:=myName
End With
With objWord.Selection
.Collapse Direction:=wdCollapseEnd
.InsertBreak Type:=7
End With
End If
Next
objDoc.Save
Application.ScreenUpdating = True
mainworkbook.Sheets("Main").Activate
MsgBox "Completed...Merge File is saved at " & MergeFolder & MergeFileName
PS: thanks Cyril for the tip it was the key to resolve that pain of mine
Let’s try something like combining multiple Word documents; you need to copy all the contents of each one, and paste everything into one consolidated Word doc. That could take a very long time, especially if there are many files in the folder. Simply run the script below and the code will do all the work for you.
Sub MergeAllWordDocs1()
Dim i As Long
Dim MyName As String, MyPath As String
Application.ScreenUpdating = False
Documents.Add
MyPath = "C:\Users\your_path_here\" ' <= change this as necessary
MyName = Dir$(MyPath & "*.do*") ' not *.* if you just want doc files
Do While MyName <> ""
If InStr(MyName, "~") = 0 Then
Selection.InsertFile _
FileName:="""" & MyPath & MyName & """",
ConfirmConversions:=False, Link:=False,
Attachment:=False
Selection.InsertBreak Type:=wdPageBreak
End If
MyName = Dir() ' gets the next doc file in the directory
Loop
End Sub
Related
I have a spreadsheet that I want to open two Word documents, do a bunch of stuff and then add all the contents of one to the end of the other. I've managed to figure out how to do the bunch of stuff, but for the life of me I can't seem to copy the contents of one to end of the other (which I thought would be the easiest part).
My code:
Sub Redate_OUT()
Application.ScreenUpdating = False
Application.DisplayAlerts = False
Application.DisplayStatusBar = True
Dim sh As Worksheet
Set sh = ThisWorkbook.Sheets("Settings")
Dim pdf_path As String
Dim word_path As String
Dim Updated_path As String
pdf_path = sh.Range("E4").Value
Updated_path = sh.Range("E5").Value
word_path = "\\filestorage\cie\Operations\Results Team\Enquiries About Results\1.Series Folders\June 2022\4. Letters\OUT\Redater\Temporary Folder (Word)"
Dim fso As New FileSystemObject
Dim fo As Folder
Dim f As File
Dim MonthNo As Long
Dim MonthType As String
Dim Check As Boolean
Set fo = fso.GetFolder(pdf_path)
Dim wa As Object
Dim doc As Object
Dim FinalWording As Object
Set wa = CreateObject("word.application")
wa.Visible = True
Dim file_Count As Integer
Set FinalWording = wa.Documents.Open("\\filestorage\cie\Operations\Results Team\Enquiries About Results\1.Series Folders\June 2022\4. Letters\OUT\Redater\Final Wording.docx")
For Each f In fo.Files
Application.StatusBar = "Converting - " & file_Count + 1 & "/" & fo.Files.Count
Set doc = wa.Documents.Open(f.Path)
doc.SaveAs2 (word_path & "\" & Replace(f.Name, ".pdf", ".docx"))
doc.Close False
Set doc = wa.Documents.Open(word_path & "\" & Replace(f.Name, ".pdf", ".docx"))
For MonthNo = 12 To 1 Step -1
MonthType = MonthName(MonthNo)
With doc.Content.Find
.Text = "?? " & MonthType & " 2022"
.Replacement.Text = Format(Date, "dd") & " " & MonthName(Format(Date, "mm")) & " " & Format(Date, "yyyy")
.MatchWildcards = True
.MatchWholeWord = True
.Execute Replace:=wdReplaceAll
If .Found = True Then
Check = True
GoTo Done
End If
End With
Next
Done:
If Check = True Then
With doc.Content.Find
.Text = "If you believe we have not arrived at this outcome properly, * Enquiry About Results Team"
.Replacement.Text = ""
.MatchWildcards = True
.MatchWholeWord = True
.Execute Replace:=wdReplaceAll
End With
FinalWording.Content.WholeStory 'Select whole document
Selection.Copy 'Copy your selection
Documents(doc.Name).Activate 'Activate the other document
Selection.EndKey wdStory 'Move to end of document
Selection.PasteAndFormat wdPasteDefault 'Pastes in the content
doc.ExportAsFixedFormat OutputFileName:=Updated_path & "/" & f.Name, ExportFormat:=wdExportFormatPDF, Range:=2
doc.Close
End If
file_Count = file_Count + 1
Next
FinalWording.Close False
wa.Quit
Kill word_path & "\" & "*.docx"
MsgBox "All OUT Letters have been updated", vbInformation
Application.StatusBar = ""
End Sub
My main difficulty is with:
FinalWording.Content.WholeStory 'Select whole document
Selection.Copy 'Copy your selection
Documents(doc.Name).Activate 'Activate the other document
Selection.EndKey wdStory 'Move to end of document
Selection.PasteAndFormat wdPasteDefault 'Pastes in the content
I get an error (Object does not support this property or method) with:
Selection.EndKey wdStory 'Move to end of document
I'm also not convinced that the contents of the FinalWording document is actually being copied, as when I try to paste this manually after that line of code has run, nothing happens.
On a side note, after the PDF is saved as a word document, I've been closing this and opening again to have a variable to use (doc). As I don't need to save the word document, if there's an easier way of doing this without having to close and open it, I would greatly appreciate that.
Many thanks.
You can replace this entire block:
FinalWording.Content.WholeStory 'Select whole document
Selection.Copy 'Copy your selection
Documents(doc.Name).Activate 'Activate the other document
Selection.EndKey wdStory 'Move to end of document
Selection.PasteAndFormat wdPasteDefault 'Pastes in the content
With:
doc.Characters.Last.FormattedText = FinalWording.Content.FormattedText
First I want to say I am extremely new to utilizing VBA to make my excel sheets more efficient.
I started a few months back and mainly generate code by piecing together what I find online then edit to meet my specific needs.
What Current Code Does:
What I have created allows me to perform a multiple document mailmerge from excel to merge records from my datasoure (Project Information) with the click of a button. Before performing the merge, the user identifies 5 conditions;
Zoning (ex. R20; located in cell C8)
Easement Type (ex. TE; located in cell F8)
The Template to use from the previously uploaded template list (located in cell J8)
The Area of the Lot (located in cell P8)
If it is a just compensation Report ("yes" or "no" located in cell C11)
The criteria above identifies the record numbers that match the specified criteria to create individual mailmerge documents for each record and saves in the corresponding property file which is associated with the record number. The sheet that is generating mailmerge ("report Creation") is different from the datasource and maintains records of when the mailmerge was performed and what template was used. This sheet also contains the list of records and is the search range for the criteria (record start on line 39 so +37 is used to match "Report Creation" row).The code also contains a loading bar that appears when the merging is being performed and shows percentage complete (percentage is not correct but used more to show user merge is in progress).
My Question:
What I am now trying to adjust is when the mailmerge is performed I still want the individual documents but I want to maintain the link between the new document and the datasource. That way I can always update the word document if any changes occur. It currently merges to a word document that no longer contains any mailmerge field and is as if I finalized a merge.
I am assuming this is a minor change after the .opendatasource but cant pinpoint what to change.
My Code may be a bit messy and could definitely use some cleanup but it gets the job done. See below.
Current Code:
Sub RunMerge()
Dim StrMMSrc As String, StrMMDoc As String, StrMMDocName As String, StrName As String, dataname As String
Dim i As Long
Dim Load As Integer
Dim wdApp As New Word.Application
Dim wdDoc As Word.Document
Dim ReportNum, AddressName, SaveLoc, NewFile, fpath, subfldr, DateCr As String
Dim ExpTemp, ExTempDate, ExpReview, ExpRevDate As Range
Dim ExpRow, CustCol, lastRow, StrMMDocRow, ExportedDoc, LotSizeSM, LotSizeLG, ActualLS, symbpos As Long
Dim FileName, Zoning, Ease, LotSizeRNG, Ztype, Etype As String
On Error GoTo errhandler
'Turn off at the start
TurnOffFunctionality
wdApp.DisplayAlerts = wdAlertsNone
Set wsreports = ThisWorkbook.Worksheets("Report Creation")
Set wsinfo = ThisWorkbook.Worksheets("Project Information")
Set wsdetails = ThisWorkbook.Worksheets("Project Details")
StrMMSrc = ThisWorkbook.fullname
lastRow = wsinfo.Columns("A").Find("*", SearchDirection:=xlPrevious, SearchOrder:=xlByRows, LookIn:=xlValues).row
dataname = wsinfo.Name
'set folder path for saving documents
fpath = ThisWorkbook.Sheets("Project Details").Range("E30").Value
subfldr = wsdetails.Range("F34").Value
'date exported
DateCr = Format(Date, "mm-dd-yyyy")
ExportedDoc = 0
With wsreports
' set range criteria
LotSizeRNG = .Range("P8").Value
symbpos = InStr(1, LotSizeRNG, "<>")
LotSizeSM = CInt(Left(LotSizeRNG, symbpos - 1))
LotSizeLG = CInt(Mid(LotSizeRNG, symbpos + 2))
If LotSizeLG = "" Then LotSizeLG = 100000000
If wsreports.Range("J8").Value = Empty Then
MsgBox "Please Select A Template From The Dropdown List to Export"
wsreports.Range("J8").Select
GoTo errhandler
End If
StrMMDocRow = .Application.Match(Range("J8").Value, .Range("C1:C34"), 0) 'Set Template Row
StrMMDocName = .Range("J8").Value 'set template name
Zoning = .Range("C8").Value 'set Zoning Criteria
Ease = .Range("F8").Value 'Set Easement Criteria
StrMMDoc = .Range("AB" & StrMMDocRow).Value 'Word Document Filename
End With
wdApp.Visible = False
Set wdDoc = wdApp.Documents.Open(FileName:=StrMMDoc, AddToRecentFiles:=False)
With wdDoc
With .MailMerge
.MainDocumentType = wdFormLetters
.OpenDataSource Name:=StrMMSrc, AddToRecentFiles:=False, LinkToSource:=False,
ConfirmConversions:=False, _
ReadOnly:=True, Format:=wdOpenFormatAuto, Connection:="Provider=Microsoft.ACE.OLEDB.12.0;" & _
"User ID=Admin;Data Source=" & StrMMSrc & ";Mode=Read;Extended Properties=""HDR=YES;IMEX=1;"";", _
SQLStatement:="SELECT * FROM `Project Information$`", SQLStatement1:="", SubType:=wdMergeSubTypeAccess
UserFormLoad.Show
For i = 2 To lastRow
Ztype = wsreports.Range("D" & i + 37).Value
Etype = wsreports.Range("F" & i + 37).Value
ActualLS = wsreports.Range("E" & i + 37).Value
'Check the row for matching zone and easement cristeria
If wsreports.Range("C11").Value = "No" And StrMMDocName <> wsreports.Range("H" & i + 37).Value _
And Ztype = Zoning And ActualLS >= LotSizeSM And ActualLS <= LotSizeLG And Etype = Ease Then
ExportedDoc = ExportedDoc + 1
'set newfile location
ReportNum = wsreports.Range("B" & i + 37).Value
AddressName = wsreports.Range("C" & i + 37).Value
SaveLoc = fpath & "\#" & ReportNum & "_" & AddressName & "\" & subfldr
'generate new file name with date
NewFile = SaveLoc & "\" & AddressName & "_Draft Report_" & DateCr & ".docx"
.Destination = wdSendToNewDocument
.SuppressBlankLines = True
With .DataSource
.FirstRecord = i - 1
.LastRecord = i - 1
.ActiveRecord = i - 1
StrName = NewFile
End With
.Execute Pause:=False
wsreports.Range("I" & i + 37).Value = StrMMDocName
wsreports.Range("L" & i + 37).Value = DateCr
With wdApp.ActiveDocument
.SaveAs FileName:=StrName, FileFormat:=wdFormatXMLDocument, AddToRecentFiles:=False
' and/or:
'.SaveAs Filename:=StrMMPath & StrName & ".pdf", FileFormat:=wdFormatPDF, AddToRecentFiles:=False
.Close savechanges:=False
End With
Dim r As Integer
r = i
Load = Application.WorksheetFunction.RoundDown((r + 1) / (lastRow) * 100, 0)
DoEvents
UserFormLoad.LoadBar.Width = Load / 100 * 222
UserFormLoad.LabelProg.Caption = Load & "%"
End If
Next i
Unload UserFormLoad
.MainDocumentType = wdNotAMergeDocument
End With
.Close savechanges:=False
End With
If ExportedDoc = 0 Then
MsgBox "No Properties Matched The Criteria Specified. Use The Table To Verify The Easement and Zoning Have Properties Meeting Criteria.", vbOKOnly, "No Matches Found"
Else
MsgBox "The Property Draft Reports Were Exported Successfully. Please Check Project Property" & subfldr & " Folder for Word Document.", vbOKOnly, "Export Successfull"
End If
'cleanup if error
errhandler:
TurnOnFunctionality
wdApp.DisplayAlerts = wdAlertsAll
Set wdDoc = Nothing
Set wdApp = Nothing
End Sub
You cannot use mailmerge for what you want to achieve. You would need to use LINK fields instead of MERGEFIELDs and update the LINK field row references for each output document.
An alternative approach would be to re-run the mailmerge for just the record(s) you want to update.
Im trying to generate offer letters based on details provide and mail merge it. But i want my output in PDF Format instead of word.
Since it exports the file in word, i want that the final output that is generated is a PDF. But whenever i am trying i am facing with the same error.
Im getting System Error &H80004005 Unspecified Error.
Sub cmdAgree_Click()
Application.DisplayAlerts = False
Application.ScreenUpdating = False
Application.ReferenceStyle = xlA1
' Sheets("DATA").Select
' ActiveSheet.Range("A1").Select
' Selection.End(xlDown).Select
' row_ref = Selection.Row
'
' Sheets("Mail Merge").Range("D4").Value = row_ref
Sheets("Mail Merge").Select
frst_rw = Sheets("Mail Merge").Range("D6").Value
lst_rw = Sheets("Mail Merge").Range("D7").Value
' ActiveWorkbook.Save
'Loop to check if the start row is greater than the last actioned row
If frst_rw = 1 Then
MsgBox "Start row can't be 1. Please check and update to proceed!", vbCritical
Exit Sub
End If
If Sheets("Data").Range("A" & frst_rw).Value = "" Then
MsgBox "No Data to work upon. Please check the reference row used!!!"
Exit Sub
End If
' If frst_rw <= Sheets("Mail Merge").Range("D5").Value And Sheets("Mail Merge").Range("D5").Value <> "" Then
' MsgBox "Start from Row: Cant be less than last actioned row of data in the DATA tab." & vbNewLine _
' & "Please check and update to proceed!", vbCritical
' Exit Sub
' End If
'Loop to check if the last row to generate is greater than the total rows of data
' If lst_rw > Sheets("Mail Merge").Range("D4").Value Then
' MsgBox "End at Row: Cant be greater than total data rows in the DATA tab." & vbNewLine _
' & "Please check and update to proceed!", vbCritical
' Exit Sub
' Else
'Update the last actioned row for future reference
Sheets("Mail Merge").Range("D5").Value = Sheets("Mail Merge").Range("D7").Value
' End If
'Loop though the start row and end row to generate the word documents for different candidates
Dim wd As Object
Dim wdocSource As Object
Dim strWorkbookName As String
On Error Resume Next
'agreement_folder = ThisWorkbook.Path & "\Agreement Template\"
For x = frst_rw - 1 To lst_rw - 1
' For x = frst_rw To lst_rw
'This if condition tackles the choice of group company basis which the template gets selected
If Sheets("DATA").Range("AS" & x + 1).Value = "APPLE" Then
agreement_folder = ThisWorkbook.Path & "\Agreement Template - APPLE\"
ElseIf Sheets("DATA").Range("AS" & x + 1).Value = "BANANA" Then
agreement_folder = ThisWorkbook.Path & "\Agreement Template - BANANA\"
ElseIf Sheets("DATA").Range("AS" & x + 1).Value = "CHERRY" Then
agreement_folder = ThisWorkbook.Path & "\Agreement Template - CHERRY\"
End If
Set wd = GetObject(, "Word.Application")
If wd Is Nothing Then
Set wd = CreateObject("Word.Application")
End If
On Error GoTo 0
Set wdocSource = wd.Documents.Open(agreement_folder & Sheets("DATA").Range("AL" & x + 1).Value)
'Set wdocSource = wd.Documents.Open(agreement_folder & Sheets("DATA").Range("AL" & x).Value)
strWorkbookName = ThisWorkbook.Path & "\" & ThisWorkbook.Name
wdocSource.MailMerge.MainDocumentType = wdFormLetters
wdocSource.MailMerge.OpenDataSource _
Name:=strWorkbookName, _
AddToRecentFiles:=False, _
Revert:=False, _
Format:=wdOpenFormatAuto, _
Connection:="Data Source=" & strWorkbookName & ";Mode=Read", _
SQLStatement:="SELECT * FROM `DATA$`"
With wdocSource.MailMerge
.Destination = wdSendToNewDocument
.SuppressBlankLines = True
With .DataSource
.FirstRecord = x
.LastRecord = x
End With
.Execute Pause:=False
End With
Dim PathToSave As String
PathToSave = ThisWorkbook.Path & "\" & "pdf" & "\" & Sheets("DATA").Range("B2").Value & ".pdf"
If Dir(PathToSave, 0) <> vbNullString Then
With wd.FileDialog(FileDialogType:=msoFileDialogSaveAs)
If .Show = True Then
PathToSave = .SelectedItems(1)
End If
End With
End If
wd.ActiveDocument.ExportAsFixedFormat PathToSave, 17 'The constant for wdExportFormatPDF
'Sheets("Mail Merge").Select
wd.Visible = True
wdocSource.Close savechanges:=False
wd.ActiveDocument.Close savechanges:=False
Set wdocSource = Nothing
Set wd = Nothing
Next x
Sheets("Mail Merge").Range("D6").ClearContents
Sheets("Mail Merge").Range("D7").ClearContents
MsgBox "All necessary Documents created and are open for your review. Please save and send!", vbCritical
End Sub
Your code is non-trivial, so I'm not going to try to get it setup and working on my side. Instead, I'd suggest adding a Watch Window, and check the results. That should help you isolate the issue and quickly resolve it.
https://www.techonthenet.com/excel/macros/add_watch2016.php
Although error messages are sometimes misleading, it really should help you figure it out, or get close enough to post back with very specific information about what's going on there.
I am trying to write some VBA code in excel to automate my task of building 100+ .pdf word documents, each following the set template. I originally copied a code from a youtube tutorial showing how to build automated emails from a spreadsheet, and I felt my application was similar enough.
I can get the text replacements to occur as they should. My primary issue is getting images inserted where they need to be. I've attempted using a bookmark and replace code with no luck. I think my issue lies in my variables not having the correct value between the various subs, although that is only my uneducated best guess.
My next issue is creating a code to pull text from an existing document and paste into a new document. I'll be honest, I have been so stuck on the image issue that I haven't even looked into this yet.
I may be going about this task in an inefficient way, however, if someone may be able to spot the fault in my code, it would be greatly appreciated. I have pasted my existing code below. Hopefully it isn't too bad.
Option Explicit
Dim CustRow, CustCol, LastRow, TemplRow, j As Long
Dim DocLoc, TagName, TagValue, TemplName, FileName As String
Dim CurDt, LastAppDt As Date
Dim WordDoc, WordApp As Object
Dim WordContent As Word.Range
Sub CreateWordDocuments()
With Sheet1
If .Range("B3").Value = Empty Then
MsgBox "Please select a correct template from the drop down list"
.Range("G3").Select
Exit Sub
End If
TemplRow = .Range("B3").Value 'Set Template Row
TemplName = .Range("G3").Value 'Set Template Name
DocLoc = Sheet2.Range("F" & TemplRow).Value 'Word Document Filename
'Open Word Template
On Error Resume Next 'If Word is already running
Set WordApp = GetObject("Word.Application")
If Err.Number <> 0 Then
'Launch a new instance of Word
Err.Clear
'On Error GoTo Error_Handler
Set WordApp = CreateObject("Word.Application")
WordApp.Visible = True 'Make the application visible to the user
End If
LastRow = .Range("E9999").End(xlUp).Row 'Determine Last Row in Table
For CustRow = 8 To LastRow
Set WordDoc = WordApp.Documents.Open(FileName:=DocLoc, ReadOnly:=False) 'Open Template
For CustCol = 5 To 10 'Move Through 6 Columns
TagName = .Cells(7, CustCol).Value 'Tag Name
TagValue = .Cells(CustRow, CustCol).Value 'Tag Value
With WordDoc.Content.Find
.Text = TagName
.Replacement.Text = TagValue
.Wrap = wdFindContinue
.Execute Replace:=wdReplaceAll 'Find & Replace all instances
End With
Next CustCol
Call InsertScreenshots
If .Range("I3").Value = "PDF" Then
FileName = ThisWorkbook.Path & "\" & .Range("E" & CustRow).Value & "_" & .Range("G" & CustRow).Value & ".pdf" 'Create full filename & Path with current workbook location, Category_Model
WordDoc.ExportAsFixedFormat OutputFileName:=FileName, ExportFormat:=wdExportFormatPDF
WordDoc.Close False
Else: 'If Word
FileName = ThisWorkbook.Path & "\" & .Range("E" & CustRow).Value & "_" & .Range("G" & CustRow).Value & ".docx"
WordDoc.SaveAs FileName
End If
.Range("O" & CustRow).Value = TemplName 'Template Name
.Range("P" & CustRow).Value = Now
Next CustRow
End With
End Sub
Sub FillABookmark(bookmarkname As String, imagepath As String)
Dim objWord As Object
Dim objDoc As Object
With Sheet1
On Error Resume Next
Set objWord = GetObject(, "Word.Application")
If objWord Is Nothing Then
Set objWord = CreateObject("Word.Application")
objWord.Visible = True
objWord.Documents.Open "DocLoc"
End If
Set objDoc = objWord.ActiveDocument
With objDoc
.Bookmarks(bookmarkname).Select
.Shapes.AddPicture FileName:=imagepath
End With
End With
End Sub
Sub InsertScreenshots()
With Sheet1
For CustCol = 11 To 14 'Move Through 4 Columns
TagName = .Cells(7, CustCol).Value 'Tag Name
TagValue = .Cells(CustRow, CustCol).Value 'Tag Value
Call FillABookmark("TagName", "TagValue")
Next CustCol
End With
End Sub
There is a lot going on here and a lot of issues.
Key Points
Learn the value of proper indenting
Dim all variables, otherwise they will be Variants
Early Binding is easier to debug. Use explicit types rather than Object
Don't use Module scoped Variables unless you have a good reason
CodeNames can be useful, but give them meaningful names
Correct test for Empty is IsEmpty
GetObject ClassID is the 2nd parameter. I needed to use Word.Application.16, your installation may vary
Reset your error handling after using On Error Resume Next as soon as you can (this likely was hiding errors from you)
When using EndUp to find the last used row, search from the bottom of the sheet
Simplified the calling of your InsertScreenshots code
You already had a Word app and open doc, don't open it again
Simplified the Insert of image, avoid use of Select
Note: without a sample of your workbook and word doc I can't be sure there aren't other issues, you will need to continue the debug.
See inline comments on changes marked with ~~
Refactored code
Option Explicit
Sub CreateWordDocuments()
'~~ Don't use module scoped variables
'~~ declare all variable types, else they are Variants
Dim CustRow As Long, CustCol As Long, LastRow As Long, TemplRow As Long, j As Long
Dim DocLoc As String, TagName As String, TagValue As String, TemplName As String, FileName As String
Dim CurDt As Date, LastAppDt As Date
'~~ to make debugging easier, use Early Binding (add reference to Microsoft Word), to get Intellisence help. If you need late binding, change back later
Dim WordDoc As Word.Document, WordApp As Word.Application ' Object
Dim WordContent As Word.Range '~~ this suggests you are already using Early Binding!
With Sheet1 '~~ If you are going to use CodeNames, give the sheet a meaningful name (edit it in the Properties window)
If IsEmpty(.Range("B3").Value) Then '~~ correct test for Empty
MsgBox "Please select a correct template from the drop down list"
.Range("G3").Select '~~ will only work if Sheet1 is active
Exit Sub
End If
TemplRow = .Range("B3").Value 'Set Template Row
TemplName = .Range("G3").Value 'Set Template Name
DocLoc = Sheet2.Range("F" & TemplRow).Value 'Word Document Filename
'Open Word Template
On Error Resume Next 'If Word is already running
Set WordApp = GetObject(, "Word.Application.16") '~~ correct format for Office365 - YMMV
If Err.Number <> 0 Then
Err.Clear
On Error GoTo 0 '~~ reset error handling
'Launch a new instance of Word
Set WordApp = New Word.Application ' CreateObject("Word.Application")
WordApp.Visible = True 'Make the application visible to the user
End If
On Error GoTo 0 '~~ reset error handling
WordApp.Visible = True
LastRow = .Cells(.Rows.Count, 5).End(xlUp).Row '~~ use real last row 'Determine Last Row in Table
For CustRow = 8 To LastRow
Set WordDoc = WordApp.Documents.Open(FileName:=DocLoc, ReadOnly:=False) 'Open Template
For CustCol = 5 To 10 'Move Through 6 Columns
TagName = .Cells(7, CustCol).Value 'Tag Name
TagValue = .Cells(CustRow, CustCol).Value 'Tag Value
With WordDoc.Content.Find
.Text = TagName
.Replacement.Text = TagValue
.Wrap = wdFindContinue
.Execute Replace:=wdReplaceAll 'Find & Replace all instances
End With
Next CustCol
For CustCol = 11 To 14 'Move Through 4 Columns ~~ do it here, it's cleaner and easier to reference the Row
TagName = .Cells(7, CustCol).Value '~~ Bookmark Name
TagValue = .Cells(CustRow, CustCol).Value '~~ Image path and name
FillABookmark TagName, TagValue, WordDoc '~~ call to insert each image
Next
If .Range("I3").Value = "PDF" Then
FileName = ThisWorkbook.Path & "\" & .Range("E" & CustRow).Value & "_" & .Range("G" & CustRow).Value & ".pdf" 'Create full filename & Path with current workbook location, Category_Model
WordDoc.ExportAsFixedFormat OutputFileName:=FileName, ExportFormat:=wdExportFormatPDF
WordDoc.Close False
Else '~~ don't need the :
FileName = ThisWorkbook.Path & "\" & .Range("E" & CustRow).Value & "_" & .Range("G" & CustRow).Value & ".docx"
WordDoc.SaveAs FileName
End If
.Range("O" & CustRow).Value = TemplName 'Template Name
.Range("P" & CustRow).Value = Now
Next CustRow
End With
End Sub
Sub FillABookmark(bookmarkname As String, imagepath As String, objDoc As Word.Document)
'~~ Use passed Parameter for Doc
'~~ Don't need select
objDoc.Bookmarks(bookmarkname).Range _
.InlineShapes.AddPicture FileName:=imagepath
End Sub
I'm trying to do something that sounds incredibly simple but I can't figure out how to fit it into existing VBA code. The code below cycles through a pivot table 1 item at a time and copies that pivot table data out to a new workbook and emails to the staff member
All i need to add in is for it to copy (just values and formatting) a 13x2 table in the range E15:S16 on the same sheet as the pivot table, into the new workbook in the tab I've named "Monthly Forecast". with the loops etc i'm not sure how to get this into the code so it copies the pivot data and then the monthly forecast into the separate tab
Hope that makes sense, any help would be wonderful :)
Option Explicit
Sub PivotSurvItems()
Dim i As Integer
Dim sItem As String
Dim sName As String
Dim sEmail As String
Dim OutApp As Object
Dim OutMail As Object
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
Application.DisplayAlerts = False
With ActiveSheet.PivotTables("PivotTable1")
.PivotCache.MissingItemsLimit = xlMissingItemsNone
With .PivotFields("Staff")
'---hide all items except item 1
.PivotItems(1).Visible = True
For i = 2 To .PivotItems.Count
.PivotItems(i).Visible = False
Next
For i = 1 To .PivotItems.Count
.PivotItems(i).Visible = True
If i <> 1 Then .PivotItems(i - 1).Visible = False
sItem = .PivotItems(i)
ActiveSheet.PivotTables("PivotTable1").PivotSelect "", xlDataAndLabel, True
Selection.Copy
Workbooks.Add
With ActiveWorkbook
.Sheets(1).Cells(1).PasteSpecial _
Paste:=xlPasteValuesAndNumberFormats
Worksheets("Sheet1").Columns("A:R").AutoFit
ActiveSheet.Range("A2").AutoFilter
sName = Range("C" & 2)
sEmail = Range("N" & 2)
Columns(1).EntireColumn.Delete
Columns(2).EntireColumn.Delete
Columns(2).EntireColumn.Delete
Columns(2).EntireColumn.Delete
Columns(10).EntireColumn.Delete
ActiveSheet.Name = "FCW"
Sheets.Add(After:=Sheets(Sheets.Count)).Name = "Monthly Forecast"
Worksheets("FCW").Activate
'create folder
On Error Resume Next
MkDir "C:\Temp\FCW" & "\" & sName
On Error GoTo 0
.SaveAs "C:\Temp\FCW" & "\" & sName & "\" & sItem & " " & Format(Now(), "DD-MM-YYYY") & ".xlsx", _
FileFormat:=xlOpenXMLWorkbook
Set OutApp = CreateObject("Outlook.Application")
Set OutMail = OutApp.CreateItem(0)
On Error Resume Next
With OutMail
.To = sEmail
.CC = ""
.BCC = ""
.Subject = "Planning Spreadsheet"
.Attachments.Add ActiveWorkbook.FullName
.Send
End With
On Error GoTo 0
Set OutMail = Nothing
Set OutApp = Nothing
.Close
End With
Next i
End With
End With
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
Application.DisplayAlerts = True
End Sub
Instead of changing visibility and cycling through all the items in the pivot table, assign the values to a 'table' (a range) and pass it to where you want it to go (it's much faster than using Excel's .copy and .PasteSpecial in VBA.
Also, I suggest that you copy all the data to an 'outputs' worksheet in the same workbook. When all the data has been copied, export that specific outputs worksheet into a new workbook. This way you avoid copying and pasting data between two different workbooks which can be prone to errors.
In your code, I would remove everything from the item cycling down until the Temp folder creation and replace it with something like the following:
'Copy values
Set rStartCell = ActiveSheet.Range("A1") 'Specify the top-left corner cell of the data you wish to copy
Set rTable_1 = ActiveSheet.Range(rStartCell, ActiveSheet.Range("Z" & rStartCell.End(xlDown).Row)) 'Change the Z column to the last column of the data you wish to copy. You can automate this by using something like Range(A1).end(xltoright).columns.count formula to grab the number of columns.
Debug.Print "rTable_1: " & rTable_1.Address & " -> " & rTable_1.Rows.Count & " x " & rTable_1.Columns.Count 'good to test exactly what you're copying
'Paste Values
Set rStartCell = Outputs.Range("A1") 'Change A1 to the cell of where you want to paste on the Outputs worksheet in your original workbook.
Set rTable_2 = Outputs.Range(rStartCell, rStartCell.Offset(rTable_1.Rows.Count - 1, rTable_1.Columns.Count - 1))
Debug.Print "rTable_2: " & rTable_2.Address & " -> " & rTable_2.Rows.Count & " x " & rTable_2.Columns.Count
rTable_2.Value = rTable_1.Value
rTable_1.Copy
rTable_2.PasteSpecial Paste:=xlPasteFormats 'to copy/paste those formats you need
'Copy Worksheet and open it in a new workbook
ThisWorkbook.Sheets("NAME OF OUTPUTS SHEET").Copy 'Using ThisWorkbook to point to the workbook holding this code.
ActiveSheet.Name = "FCW"
You can use this method to copy/paste that other table mentioned as well.