Print excel to PDF for rows of unique values

Nathanoj

New Member
Joined
Sep 13, 2016
Messages
6
Hi,

My code below currently copy/paste each row from Sheet 1 (detailsSheet) to Sheet 2 (reportSheet) and then print/save it as a PDF on my desktop, and works great!
As a next step (where I would appreciate some help), I would like to copy rows as long as the value in column A is the same. For example if there are 3 rows with the unique value of "Pile123" in column A , I want to copy those three rows to the reportSheet before saving the PDF to the desktop. But I might also have a value with "Pile456" which only consists of 1 row, in which case I only want it to copy the one row before saving. My current code is below:


Sub ExportingandSavingPDF()

'Defining worksheets
Dim detailsSheet As Worksheet
Dim reportSheet As Worksheet
Dim i As Long
Dim LastRow As Long
LastRow = Range("B" & Rows.Count).End(xlUp).Row

'Looping the through each row
For i = 2 To LastRow

Set reportSheet = ActiveWorkbook.Sheets("Contract Form")
Set detailsSheet = ActiveWorkbook.Sheets("New POs")

'Assigning values
SPile = detailsSheet.Cells(i, 2)
SClient = detailsSheet.Cells(i, 1)
SCommodity = detailsSheet.Cells(i, 2)
SOption = detailsSheet.Cells(i, 3)
SQtyMT = detailsSheet.Cells(i, 4)
SPriceMT = detailsSheet.Cells(i, 5)
SWhs = detailsSheet.Cells(i, 6)
SIncoterm = detailsSheet.Cells(i, 8)
SDeliveryCity = detailsSheet.Cells(i, 9)
SPO = detailsSheet.Cells(i, 11)
SDeliveryDate = detailsSheet.Cells(i, 14)
SWhsAddress = detailsSheet.Cells(i, 18)
SClientAddress = detailsSheet.Cells(i, 15)
SClientTownZip = detailsSheet.Cells(i, 16)


'Generating the output
'reportSheet.Cells(19, 1).Value = SPile
'reportSheet.Cells(17, 1).Value = SClient
reportSheet.Cells(17, 1).Value = SCommodity
reportSheet.Cells(17, 5).Value = SOption
reportSheet.Cells(17, 4).Value = SIncoterm
'reportSheet.Cells(1, 1).Value = SWhs
reportSheet.Cells(17, 2).Value = SDeliveryCity
reportSheet.Cells(21, 2).Value = SPriceMT
reportSheet.Cells(17, 6).Value = SPO
reportSheet.Cells(17, 3).Value = SDeliveryDate
'reportSheet.Cells(5, 1).Value = SWhsAddress
reportSheet.Cells(21, 1).Value = SQtyMT
reportSheet.Cells(10, 6).Value = SClient
reportSheet.Cells(11, 6).Value = SClientAddress
reportSheet.Cells(12, 6).Value = SClientTownZip


'Save the PDF file
Worksheets("Contract Form").Range("A1:G28").ExportAsFixedFormat Type:=xlTypePDF, Filename:= _
"C:\Users\JonathanGerafi\Desktop\" & SPO & ".PDF", Quality:=xlQualityStandard, _
IncludeDocProperties:=True, IgnorePrintAreas:=False, _
OpenAfterPublish:=False

With ActiveSheet.PageSetup
.Zoom = False
.Orientation = xlPortrait
.FitToPagesWide = 1
.FitToPagesTall = 1
End With

Next i

End Sub
 

Excel Facts

Formula for Yesterday
Name Manager, New Name. Yesterday =TODAY()-1. OK. Then, use =YESTERDAY in any cell. Tomorrow could be =TODAY()+1.
Hi Nathan,

is the data in Sheets("New POs") sorted? If more than one number where to place the additional data and all data or which should be copied? Clear the data from the report after creating a PDF?

Ciao,
Holger
 
Upvote 0
Hi Nathan,

is the data in Sheets("New POs") sorted? If more than one number where to place the additional data and all data or which should be copied? Clear the data from the report after creating a PDF?

Ciao,
Holger
Hi Nathan, thanks for looking into this. The data is sorted in sheet "New POs". I pull it from another file via a query and plan on sorting it by Pile number. So for example the goal is to copy row 1, 2 and 3 to reportSheet and then save the PDF, then copy row 4 and then save the PDF, and finally copy 5, 6, 7, 8 and 9 and then save the PDF. Hope this is helpful.

RowSpileSQtyMTSdelivery Date
1Pile 12320Jan
2Pile 12320Feb
3Pile 12320Mar
4Pile 45620Jan
5Pile 78620Jan
6Pile 78620Feb
7Pile 78620Mar
8Pile 78620Apr
9Pile 78620May
 
Upvote 0
Hi Nathan,

seems I wasn't clear in what I am asking for. I will try to explain better with a bit of your original code and a restructured code below to find out which Columns shall be repeated for the printout (I assume that e.g. Name, Address, SO only need to be transferred once):

VBA Code:
'...
''original code
'reportSheet.Cells(19, 1).Value = SPile
'reportSheet.Cells(17, 1).Value = SClient
reportSheet.Cells(17, 1).Value = SCommodity
reportSheet.Cells(17, 5).Value = SOption
reportSheet.Cells(17, 4).Value = SIncoterm
'reportSheet.Cells(1, 1).Value = SWhs
reportSheet.Cells(17, 2).Value = SDeliveryCity
reportSheet.Cells(21, 2).Value = SPriceMT
reportSheet.Cells(17, 6).Value = SPO
reportSheet.Cells(17, 3).Value = SDeliveryDate
'reportSheet.Cells(5, 1).Value = SWhsAddress
reportSheet.Cells(21, 1).Value = SQtyMT
reportSheet.Cells(10, 6).Value = SClient
reportSheet.Cells(11, 6).Value = SClientAddress
reportSheet.Cells(12, 6).Value = SClientTownZip


''how and where to add data from details

'Column A
'reportSheet.Cells(1, 1).Value = SWhs
'reportSheet.Cells(5, 1).Value = SWhsAddress
'reportSheet.Cells(17, 1).Value = SClient
reportSheet.Cells(17, 1).Value = SCommodity
'reportSheet.Cells(19, 1).Value = SPile
reportSheet.Cells(21, 1).Value = SQtyMT
'/// there are only three empty rows between these entries,
'/// shall they only be transferred once or more of

'Column B
reportSheet.Cells(17, 2).Value = SDeliveryCity
reportSheet.Cells(21, 2).Value = SPriceMT
'/// there are only three empty rows between these entries,
'/// shall they only be transferred once or more of

'Column C
reportSheet.Cells(17, 3).Value = SDeliveryDate    'repeat
'/// comment indicates that value shall be repeated in next row

'Column D
reportSheet.Cells(17, 4).Value = SIncoterm

'Column E
reportSheet.Cells(17, 5).Value = SOption

'Column F
reportSheet.Cells(10, 6).Value = SClient
reportSheet.Cells(11, 6).Value = SClientAddress
reportSheet.Cells(12, 6).Value = SClientTownZip
reportSheet.Cells(17, 6).Value = SPO

I only put in one comment to indicate that this item (I choose SDeliveryDate) needs to be copied as often as it appears in Details. Is there a maximum number of rows that you want to be included for one PDF or in other words do you know for sure that your mentioned number of 4 rows for one SPile will not be exceeded?

All items without comment would only get transferred once.

Right now my idea to solve your problem is to get the number of SPiles by using a dictionary, applying an AutoFilter to the sheet and work on the range of visible cells in the data range to transfer the information, create the PDF, delete the ranges which have more than one entry, get the next number. But to present a working code I would need to know which areas must betransferred before PDF and cleared thereafter.

Ciao,
Holger
 
Upvote 0
Hi Nathan,

seems I wasn't clear in what I am asking for. I will try to explain better with a bit of your original code and a restructured code below to find out which Columns shall be repeated for the printout (I assume that e.g. Name, Address, SO only need to be transferred once):

VBA Code:
'...
''original code
'reportSheet.Cells(19, 1).Value = SPile
'reportSheet.Cells(17, 1).Value = SClient
reportSheet.Cells(17, 1).Value = SCommodity
reportSheet.Cells(17, 5).Value = SOption
reportSheet.Cells(17, 4).Value = SIncoterm
'reportSheet.Cells(1, 1).Value = SWhs
reportSheet.Cells(17, 2).Value = SDeliveryCity
reportSheet.Cells(21, 2).Value = SPriceMT
reportSheet.Cells(17, 6).Value = SPO
reportSheet.Cells(17, 3).Value = SDeliveryDate
'reportSheet.Cells(5, 1).Value = SWhsAddress
reportSheet.Cells(21, 1).Value = SQtyMT
reportSheet.Cells(10, 6).Value = SClient
reportSheet.Cells(11, 6).Value = SClientAddress
reportSheet.Cells(12, 6).Value = SClientTownZip


''how and where to add data from details

'Column A
'reportSheet.Cells(1, 1).Value = SWhs
'reportSheet.Cells(5, 1).Value = SWhsAddress
'reportSheet.Cells(17, 1).Value = SClient
reportSheet.Cells(17, 1).Value = SCommodity
'reportSheet.Cells(19, 1).Value = SPile
reportSheet.Cells(21, 1).Value = SQtyMT
'/// there are only three empty rows between these entries,
'/// shall they only be transferred once or more of

'Column B
reportSheet.Cells(17, 2).Value = SDeliveryCity
reportSheet.Cells(21, 2).Value = SPriceMT
'/// there are only three empty rows between these entries,
'/// shall they only be transferred once or more of

'Column C
reportSheet.Cells(17, 3).Value = SDeliveryDate    'repeat
'/// comment indicates that value shall be repeated in next row

'Column D
reportSheet.Cells(17, 4).Value = SIncoterm

'Column E
reportSheet.Cells(17, 5).Value = SOption

'Column F
reportSheet.Cells(10, 6).Value = SClient
reportSheet.Cells(11, 6).Value = SClientAddress
reportSheet.Cells(12, 6).Value = SClientTownZip
reportSheet.Cells(17, 6).Value = SPO

I only put in one comment to indicate that this item (I choose SDeliveryDate) needs to be copied as often as it appears in Details. Is there a maximum number of rows that you want to be included for one PDF or in other words do you know for sure that your mentioned number of 4 rows for one SPile will not be exceeded?

All items without comment would only get transferred once.

Right now my idea to solve your problem is to get the number of SPiles by using a dictionary, applying an AutoFilter to the sheet and work on the range of visible cells in the data range to transfer the information, create the PDF, delete the ranges which have more than one entry, get the next number. But to present a working code I would need to know which areas must betransferred before PDF and cleared thereafter.

Ciao,
Holger
Thank you Holger,
In regards to your first question regarding SCommodity, SQtyMT, SDeliveryCity and SPriceMT:

1)
'/// shall they only be transferred once or more of

Yes, for each row they shall only be transferred once, so if the same pile number appears in two rows, then commodity should be transferred twice - once for each row.

2)
"do you know for sure that your mentioned number of 4 rows for one SPile will not be exceeded? 4 rows was just an example but it could be more or less (it will likely be up to 10)

The areas that must be transferred are
reportSheet.Cells(17, 1).Value = SCommodity
reportSheet.Cells(17, 5).Value = SOption
reportSheet.Cells(17, 4).Value = SIncoterm
reportSheet.Cells(17, 2).Value = SDeliveryCity
reportSheet.Cells(21, 2).Value = SPriceMT
reportSheet.Cells(17, 6).Value = SPO
reportSheet.Cells(17, 3).Value = SDeliveryDate
reportSheet.Cells(21, 1).Value = SQtyMT
reportSheet.Cells(10, 6).Value = SClient
reportSheet.Cells(11, 6).Value = SClientAddress
reportSheet.Cells(12, 6).Value = SClientTownZip

Thanks again Holger and hope this is helpful, otherwise please let me know
 
Upvote 0
Hi Nathan,

here we are with my next question as you mention Commodity - you give up the start address to be A17. But QtyMT starts at A21 so depending on how many rows there are in Details you would need to tell me how to handle this:

  1. add all rows to the printout meaning that we would have to insert addtional cells/rows which in my opinion would lead to work with a copy of Report which would be deleted after creating the PDF
  2. limit the number of entries to 3 (leaving one empty cell A20) or max 4 (no gap between Commodity and QtyMT) which would lead to multiple printouts which would have to include numbers to distinguish them

As no further entry is made in Column A QtyMT could take items until A28 as you have set the printrange to be Range("A1:G28"). So opting for 1 would mean to change the printrange as well in order to caption all entries.

I know it's not easy to have me trying to solve your request but the flow chart on how to programm is already in my mind, I just need some more information to handle what might become an issue with the layout you posted in your code.

Ciao,
Holger
 
Upvote 0
Hi Nathan,

what about creating a second report which would take all information for the rows which exceed a given number (say 3) in order to keep the original setup? You would need to pass information about the name of the second report sheet as well as which information should start where and if anything would be changed on the first report give details of these.

Holger
 
Upvote 0
Hi Nathan,

this is how I might handle the given situation. Please read the requirements carefully: I added a new sheet for taking printouts with more than 3 items, and I desribed how the distances for keeping all data and not overwriting it due to other data starting at a given cell. If you have questions or problems adapapting the code feel free to ask:

VBA Code:
Sub MrE1221718_1613C0D()
'https://www.mrexcel.com/board/threads/print-excel-to-pdf-for-rows-of-unique-values.1221718/
'Requirements:
'  - sheet with data in listform ("New POs"), headers in row 1, data starts in row 2
'  - sheet for taking the 3 rows of filtered data for PDF ("Contract Form")
'  - sheet for taking the 3 rows of filtered data and copying the remaining rows on second page
'      starting at row 35 (fix) and relativ at 35 - 3 + numbers of autofiltered data rows ("Contract Form+")
'      you should adjust this sheet to meet your criteria for printout to PDF
'  - "Contract Form+"  at present is limited for taking two pages as printout

  'Defining Variables
  Dim lngCounter          As Long                 'looping the dictionary
  Dim lngLoop             As Long                 'taking the row number of the filtered data to work on
  Dim lngOffset           As Long                 'needed for getting the next row in Printout for data
  Dim lngRowsBetween      As Long                 'needed to keep distance between the blocks of data
  
  Dim rngWork             As Range                'data range for each single PO
  Dim rngArea             As Range                'might be needed if range is not continuous
  Dim rngCell             As Range                'need for looping through each area
  
  Dim objScDir            As Object               'late binding for scripting.dictionary
  
  Dim strPath             As String               'reading in the path to the user and given folder
  Dim strReturn           As String               'for checkiong availabilty of path
  Dim strStamp            As String               'taking date/time stamp to add to PO for saving
  
  Dim wsDetails           As Worksheet            'worksheet with the raw data
  Dim wsReport            As Worksheet            'sheet for taking data for output
  
  'Constants for the sheets we will work with
  Const cstrDETAILS       As String = "New POs"
  Const cstrREPORIG       As String = "Contract Form"
  Const cstrREPALT        As String = "Contract Form+"
  
  'Constants for Ranges used later on
  Const clngColFilter     As Long = 2
  Const clngStartCom      As Long = 32            'starts on Row 17 on sheet 1, 28 rows total, minus number of
                                                  'items on first page 3
  
  Const cstrCliAddFro     As String = "O"
  Const cstrCliAddTo      As String = "F"
  Const cstrClientFro     As String = "A"
  Const cstrClientTo      As String = "F"
  Const cstrCliTwnZipFr   As String = "P"
  Const cstrCliTwnZipTo   As String = "F"
  Const cstrCommodFro     As String = "B"
  Const cstrCommodTo      As String = "A"
  Const cstrDelCityFro    As String = "I"
  Const cstrDelCityTo     As String = "B"
  Const cstrIncotermFro   As String = "H"
  Const cstrIncotermTo    As String = "D"
  Const cstrOptionFro     As String = "C"
  Const cstrOptionTo      As String = "E"
  Const cstrPOFrom        As String = "K"
  Const cstrPOTo          As String = "F"
  Const cstrPriceMTFro    As String = "E"
  Const cstrPriceMTTo     As String = "B"
  Const cstrQtyMTFro      As String = "D"
  Const cstrQtyMTTo       As String = "A"
  
  On Error GoTo err_here
  
  'Check all sheets are present in ActiveWorkbook
  If Not Evaluate("ISREF('" & cstrDETAILS & "'!A1)") Then
    MsgBox "Cannot find sheet '" & cstrDETAILS & "', please check.", vbInformation, "No match for " & cstrDETAILS
    GoTo end_here
  End If
  If Sheets(cstrDETAILS).UsedRange.Address = Range("A1").Address Then
    MsgBox "Cannot find data on sheet '" & cstrDETAILS & "', please check.", vbInformation, "No data found in " & cstrDETAILS
    GoTo end_here
  End If
  If Not Evaluate("ISREF('" & cstrREPORIG & "'!A1)") Then
    MsgBox "Cannot find sheet '" & cstrREPORIG & "', please check.", vbInformation, "No match for " & cstrREPORIG
    GoTo end_here
  End If
  If Not Evaluate("ISREF('" & cstrREPALT & "'!A1)") Then
    MsgBox "Cannot find sheet '" & cstrREPALT & "', please check.", vbInformation, "No match for " & cstrREPALT
    GoTo end_here
  End If
  
  'check we have a valid path to save
  strPath = Environ("Userprofile") & "\Desktop\"
  strReturn = Dir(Left(strPath, Len(strPath) - 1), vbDirectory)
  If strReturn = "" Then
    MsgBox "Error in Path to '" & strPath, vbInformation, "Folder cannot be found"
    GoTo end_here
  End If
  
  'starting work here
  Set wsDetails = ActiveWorkbook.Sheets(cstrDETAILS)
  Set objScDir = CreateObject("scripting.dictionary")

  'set any Autofilter off, build a range towork on for unique POs
  With wsDetails
    .AutoFilterMode = False
    Set rngWork = .Range(.Cells(2, clngColFilter), .Cells(Rows.Count, clngColFilter).End(xlUp))
  End With
  'loop through the range and add value to dictionary, item will only be accdepted if not present
  For Each rngCell In rngWork
    objScDir(rngCell.Value) = Empty
  Next rngCell
  
  With wsDetails
    'loop through the dictionary
    For lngCounter = 0 To objScDir.Count - 1
      'set autoFilter on Column with Key
      .Range("A1").CurrentRegion.AutoFilter field:=clngColFilter, Criteria1:=objScDir.Keys()(lngCounter)
      lngOffset = -1
      Set rngWork = .Range(.Cells(2, clngColFilter), .Cells(Rows.Count, clngColFilter).End(xlUp)).SpecialCells(xlCellTypeVisible)
      lngRowsBetween = rngWork.Cells.Count - 2
      'decide which report should be used
      If rngWork.Cells.Count <= 3 Then
        Set wsReport = ActiveWorkbook.Sheets(cstrREPORIG)
      Else
        Set wsReport = ActiveWorkbook.Sheets(cstrREPALT)
      End If
      For Each rngArea In rngWork
        For Each rngCell In rngArea
          lngOffset = lngOffset + 1
          lngLoop = rngCell.Row
          If lngOffset = 0 Then
            '  SOption
            wsReport.Cells(17, cstrOptionTo).Offset(lngOffset).Value = wsDetails.Cells(lngLoop, cstrOptionFro)
            '  SIncoterm
            wsReport.Cells(17, cstrIncotermTo).Offset(lngOffset).Value = wsDetails.Cells(lngLoop, cstrIncotermFro)
            '  SClient
            wsReport.Cells(10, cstrClientTo).Offset(lngOffset).Value = wsDetails.Cells(lngLoop, cstrClientFro)
            '  SClientAddress
            wsReport.Cells(11, cstrCliAddTo).Offset(lngOffset).Value = wsDetails.Cells(lngLoop, cstrCliAddFro)
            '  SClientTownZip
            wsReport.Cells(12, cstrCliTwnZipTo).Offset(lngOffset).Value = wsDetails.Cells(lngLoop, cstrCliTwnZipFr)
            '  SPO
            wsReport.Cells(17, cstrPOTo).Offset(lngOffset).Value = wsDetails.Cells(lngLoop, cstrPOFrom)
          End If
          
          If lngOffset < 3 Then
            '  SCommodity
            wsReport.Cells(17, cstrCommodTo).Offset(lngOffset).Value = wsDetails.Cells(lngLoop, cstrCommodFro)
            '  SQtyMT
            wsReport.Cells(21, cstrQtyMTTo).Offset(lngOffset).Value = wsDetails.Cells(lngLoop, cstrQtyMTFro)
            '  SDeliveryCity
            wsReport.Cells(17, cstrDelCityTo).Offset(lngOffset).Value = wsDetails.Cells(lngLoop, cstrDelCityFro)
            '  SPriceMT
            wsReport.Cells(21, cstrPriceMTTo).Offset(lngOffset).Value = wsDetails.Cells(lngLoop, cstrPriceMTFro)
          Else
'            'working on the secod page for the prinout
            '  SCommodity
            wsReport.Cells(clngStartCom, cstrCommodTo).Offset(lngOffset).Value = wsDetails.Cells(lngLoop, cstrCommodFro)
            '  SQtyMT
            wsReport.Cells(clngStartCom + lngRowsBetween, cstrQtyMTTo).Offset(lngOffset).Value = wsDetails.Cells(lngLoop, cstrQtyMTFro)
            '  SDeliveryCity
            wsReport.Cells(clngStartCom, cstrDelCityTo).Offset(lngOffset).Value = wsDetails.Cells(lngLoop, cstrDelCityFro)
            '  SPriceMT
            wsReport.Cells(clngStartCom + lngRowsBetween, cstrPriceMTTo).Offset(lngOffset).Value = wsDetails.Cells(lngLoop, cstrPriceMTFro)
          End If
        Next rngCell
      Next rngArea
   
      strStamp = Format(Now(), "_yymmdd_hhmmss")
      'Save the PDF file
      If wsReport.Name = cstrREPORIG Then
        With wsReport.PageSetup
          .Zoom = False
          .Orientation = xlPortrait
          .FitToPagesWide = 1
          .FitToPagesTall = 1
        End With
        wsReport.Range("A1:G28").ExportAsFixedFormat _
              Type:=xlTypePDF, _
              Filename:=strPath & wsReport.Cells(17, 6).Value & strStamp & ".PDF", _
              Quality:=xlQualityStandard, _
              IncludeDocProperties:=True, _
              IgnorePrintAreas:=False, _
              OpenAfterPublish:=False
        wsReport.Cells(17, cstrCommodTo).Resize(4, 1).ClearContents
        wsReport.Cells(21, cstrQtyMTTo).Resize(4, 1).ClearContents
        wsReport.Cells(17, cstrDelCityTo).Resize(4, 1).ClearContents
        wsReport.Cells(21, cstrPriceMTTo).Resize(4, 1).ClearContents
      Else
'/// You would need to adapt your worksheet setup to match by recording a macro and paste the code in here.
'/// Please mind that I use wsReport while the macro recorder should use ActiveSheet
'/// or ActiveWindow.SelectedSheets
'        With wsReport.PageSetup
'          .Zoom = False
'          .Orientation = xlPortrait
'          .FitToPagesWide = 1
'          .FitToPagesTall = 1
'        End With
        wsReport.HPageBreaks.Add Before:=Range("A29")
        wsReport.Range("A1:G56").ExportAsFixedFormat _
              Type:=xlTypePDF, _
              Filename:=strPath & wsReport.Cells(17, 6).Value & strStamp & ".PDF", _
              Quality:=xlQualityStandard, _
              IncludeDocProperties:=True, _
              IgnorePrintAreas:=False, _
              OpenAfterPublish:=False
        wsReport.Cells(17, cstrCommodTo).Resize(4, 1).ClearContents
        wsReport.Cells(21, cstrQtyMTTo).Resize(4, 1).ClearContents
        wsReport.Cells(17, cstrDelCityTo).Resize(4, 1).ClearContents
        wsReport.Cells(21, cstrPriceMTTo).Resize(4, 1).ClearContents
        wsReport.Cells(clngStartCom + 3, cstrCommodTo).Resize(rngWork.Cells.Count - 3, 1).ClearContents
        wsReport.Cells(clngStartCom + 3 + lngRowsBetween, cstrQtyMTTo).Resize(rngWork.Cells.Count - 3, 1).ClearContents
        wsReport.Cells(clngStartCom + 3, cstrDelCityTo).Resize(rngWork.Cells.Count - 3, 1).ClearContents
        wsReport.Cells(clngStartCom + 3 + lngRowsBetween, cstrPriceMTTo).Resize(rngWork.Cells.Count - 3, 1).ClearContents
      End If
    Next lngCounter
  End With

end_here:
  Set wsReport = Nothing
  Set rngWork = Nothing
  Set wsDetails = Nothing
  Set objScDir = Nothing
  Application.ScreenUpdating = True
  Exit Sub

err_here:
  MsgBox "An error occurred, more information in the Immediate Window", , "Sorry..."
  Debug.Print "Error Number: " & Err.Number
  Debug.Print "Error Description: " & Err.Description
  Resume end_here
End Sub

Ciao,
Holger
 
Upvote 0
Hi Nathan,

the code above will allow any number of items of a Pile and so might start overwriting data. I limited the number of items per Pile to 12, started at row 6 for the second page with the headers which will be inserted by code. If the number of items is larger than the number given by constant this will be marked in Details one column to the right of the data with a constant and you will be informed at the end if and how many Pile have been skipped.

As I cannot see your data for Output please adjust the setup for testing the code in order to find which out commands are needed to format the sheet prior to printing. I dragged the Horizontal Page Break to the desired row as the code refused to do so properly.

The Immediate Window showed some information after running the file:
Rich (BB code):
Finished! Duration: 35,5078125 seconds
Number of items: 89
Number of Piles skipped: 21
Total number of rows: 830
You will get more information about the items which were skipped as well as the number of rows. As this is in a string you may print that variable to the Immediate Window instead of being informed by a MessageBox.

VBA Code:
Sub MrE1221718_1613D0B()
'https://www.mrexcel.com/board/threads/print-excel-to-pdf-for-rows-of-unique-values.1221718/
'last updated: 13. Nov. 2022, HaHoBe
'
'Requirements:
'  - sheet with data in listform ("New POs"), headers in row 1, data starts in row 2
'  - sheet for taking the 3 rows of filtered data for PDF ("Contract Form")
'
'  - sheet for taking the 3 rows of filtered data and copying the next rows on second page
'      second page will look different from first page taking only some data from first page
'      using formulas for the first 4 rows as well as the number of page
'      starting at row 6 (fix) as header and relativ from row 11 + data exceeding 3
'      the maximum of rows per data to copy for sheet 2 will be set by a constant to allow a total of
'      12 rows for one Pile as I setup here with clngMaxNumPile.
'      Check for total number of rows will be taken after setting the range to work,
'      if number of rows is larger than the constant for max rows a comment will be set at the far right end
'      to flag those Piles which have not been processed and a counter will be augmented for
'      information on how many Piles were marked ("Contract Form+")
'      you should adjust this sheet to meet your criteria for printout to PDF
'  - "Contract Form+"  at present is limited for taking two pages as printout

  'Defining Variables
  Dim dblEnd              As Double
  Dim dblStart            As Double
  
  Dim lngCounter          As Long                 'looping the dictionary
  Dim lngLoop             As Long                 'taking the row number of the filtered data to work on
  Dim lngOffset           As Long                 'needed for getting the next row in Printout for data
  Dim lngRowsBetween      As Long                 'needed to keep distance between the blocks of data
  Dim lngSecLoop          As Long                 'used for the second page as Counter
  Dim lngSkip             As Long                 'taking the number of Piles that were not processed
  Dim lngWork             As Long                 'number of rows in the AutoFilter data range
  
  Dim rngWork             As Range                'data range for each single PO
  Dim rngArea             As Range                'might be needed if range is not continuous
  Dim rngCell             As Range                'need for looping through each area
  
  Dim objScDir            As Object               'late binding for scripting.dictionary
  
  Dim strInfo             As String               'text to hold item not processed and number of rows
  Dim strPath             As String               'reading in the path to the user and given folder
  Dim strReturn           As String               'for checkiong availabilty of path
  Dim strStamp            As String               'taking date/time stamp to add to PO for saving
  
  Dim wsDetails           As Worksheet            'worksheet with the raw data
  Dim wsReport            As Worksheet            'sheet for taking data for output
  
  'Constants for the sheets we will work with
  Const cstrDETAILS       As String = "New POs"
  Const cstrREPORIG       As String = "Contract Form"
  Const cstrREPALT        As String = "Contract Form+"
  
  'Constants for Ranges and Values used later on
  Const clngColFilter     As Long = 2
  Const clngStartCom      As Long = 34                  'start row for second page as header row
  Const clngMaxNumPile    As Long = 12                  'limiting the number of items per Pile
  Const cstrSKIP          As String = "not processed"   'text to notify
  
  Const cstrCliAddFro     As String = "O"
  Const cstrCliAddTo      As String = "F"
  Const cstrClientFro     As String = "A"
  Const cstrClientTo      As String = "F"
  Const cstrCliTwnZipFr   As String = "P"
  Const cstrCliTwnZipTo   As String = "F"
  Const cstrCommodFro     As String = "B"
  Const cstrCommodTo      As String = "A"
  Const cstrDelCityFro    As String = "I"
  Const cstrDelCityTo     As String = "B"
  Const cstrIncotermFro   As String = "H"
  Const cstrIncotermTo    As String = "D"
  Const cstrOptionFro     As String = "C"
  Const cstrOptionTo      As String = "E"
  Const cstrPOFrom        As String = "K"
  Const cstrPOTo          As String = "F"
  Const cstrPriceMTFro    As String = "E"
  Const cstrPriceMTTo     As String = "B"
  Const cstrQtyMTFro      As String = "D"
  Const cstrQtyMTTo       As String = "A"
  
  On Error GoTo err_here
  
  dblStart = Timer
  'Check all sheets are present in ActiveWorkbook
  If Not Evaluate("ISREF('" & cstrDETAILS & "'!A1)") Then
    MsgBox "Cannot find sheet '" & cstrDETAILS & "', please check.", vbInformation, "No match for " & cstrDETAILS
    GoTo end_here
  End If
  If Sheets(cstrDETAILS).UsedRange.Address = Range("A1").Address Then
    MsgBox "Cannot find data on sheet '" & cstrDETAILS & "', please check.", vbInformation, "No data found in " & cstrDETAILS
    GoTo end_here
  End If
  If Not Evaluate("ISREF('" & cstrREPORIG & "'!A1)") Then
    MsgBox "Cannot find sheet '" & cstrREPORIG & "', please check.", vbInformation, "No match for " & cstrREPORIG
    GoTo end_here
  End If
  If Not Evaluate("ISREF('" & cstrREPALT & "'!A1)") Then
    MsgBox "Cannot find sheet '" & cstrREPALT & "', please check.", vbInformation, "No match for " & cstrREPALT
    GoTo end_here
  End If
  
  'check we have a valid path to save
  strPath = Environ("Userprofile") & "\Desktop\"
  strReturn = Dir(Left(strPath, Len(strPath) - 1), vbDirectory)
  If strReturn = "" Then
    MsgBox "Error in Path to '" & strPath, vbInformation, "Folder cannot be found"
    GoTo end_here
  End If
  
  Application.ScreenUpdating = False
  'starting work here
  Set wsDetails = ActiveWorkbook.Sheets(cstrDETAILS)
  Set objScDir = CreateObject("scripting.dictionary")

  'set any Autofilter off, build a range towork on for unique POs
  With wsDetails
    .AutoFilterMode = False
    'if any remarks about skipped items clear column
    If WorksheetFunction.CountIf(.Columns(.Cells(1, .Columns.Count).End(xlToLeft).Column + 1), cstrSKIP) > 0 Then
      .Columns(.Cells(1, .Columns.Count).End(xlToLeft).Column + 1).ClearContents
    End If
    Set rngWork = .Range(.Cells(2, clngColFilter), .Cells(Rows.Count, clngColFilter).End(xlUp))
  End With
  'loop through the range and add value to dictionary, item will only be accdepted if not present
  For Each rngCell In rngWork
    objScDir(rngCell.Value) = Empty
  Next rngCell
  
  With wsDetails
    'loop through the dictionary
    For lngCounter = 0 To objScDir.Count - 1
      'set autoFilter on Column with Key
      .Range("A1").CurrentRegion.AutoFilter field:=clngColFilter, Criteria1:=objScDir.Keys()(lngCounter)
      Set rngWork = .Range(.Cells(2, clngColFilter), .Cells(Rows.Count, clngColFilter).End(xlUp)).SpecialCells(xlCellTypeVisible)
      'check for continuing or flagging data to show too many items
      lngWork = rngWork.Cells.Count
      If lngWork > clngMaxNumPile Then
        rngWork.Offset(, wsDetails.Cells(1, wsDetails.Columns.Count).End(xlToLeft).Column - 1).Value = cstrSKIP
        strInfo = strInfo & vbCrLf & objScDir.Keys()(lngCounter) & ": " & vbTab & lngWork & " rows"
        lngSkip = lngSkip + 1
        GoTo continue_here
      End If
      lngRowsBetween = lngWork - 1
      lngSecLoop = 0
      lngOffset = -1
      'decide which report should be used
      If rngWork.Cells.Count <= 3 Then
        Set wsReport = ActiveWorkbook.Sheets(cstrREPORIG)
      Else
        Set wsReport = ActiveWorkbook.Sheets(cstrREPALT)
      End If
      For Each rngArea In rngWork
        For Each rngCell In rngArea
          lngOffset = lngOffset + 1
          lngLoop = rngCell.Row
          If lngOffset = 0 Then
            '  SOption
            wsReport.Cells(17, cstrOptionTo).Offset(lngOffset).Value = wsDetails.Cells(lngLoop, cstrOptionFro)
            '  SIncoterm
            wsReport.Cells(17, cstrIncotermTo).Offset(lngOffset).Value = wsDetails.Cells(lngLoop, cstrIncotermFro)
            '  SClient
            wsReport.Cells(10, cstrClientTo).Offset(lngOffset).Value = wsDetails.Cells(lngLoop, cstrClientFro)
            '  SClientAddress
            wsReport.Cells(11, cstrCliAddTo).Offset(lngOffset).Value = wsDetails.Cells(lngLoop, cstrCliAddFro)
            '  SClientTownZip
            wsReport.Cells(12, cstrCliTwnZipTo).Offset(lngOffset).Value = wsDetails.Cells(lngLoop, cstrCliTwnZipFr)
            '  SPO
            wsReport.Cells(17, cstrPOTo).Offset(lngOffset).Value = wsDetails.Cells(lngLoop, cstrPOFrom)
          End If
          
          If lngOffset < 3 Then
            '  SCommodity
            wsReport.Cells(17, cstrCommodTo).Offset(lngOffset).Value = wsDetails.Cells(lngLoop, cstrCommodFro)
            '  SQtyMT
            wsReport.Cells(21, cstrQtyMTTo).Offset(lngOffset).Value = wsDetails.Cells(lngLoop, cstrQtyMTFro)
            '  SDeliveryCity
            wsReport.Cells(17, cstrDelCityTo).Offset(lngOffset).Value = wsDetails.Cells(lngLoop, cstrDelCityFro)
            '  SPriceMT
            wsReport.Cells(21, cstrPriceMTTo).Offset(lngOffset).Value = wsDetails.Cells(lngLoop, cstrPriceMTFro)
          Else
'           'working on the second page for the printout
            lngSecLoop = lngSecLoop + 1
            'Inserting the headers, Commodity and DeliverCity may remain but the other 2 are vaiable
            If lngSecLoop = 1 Then
              wsReport.Cells(clngStartCom, cstrCommodTo).Value = "Commodity"
              wsReport.Cells(clngStartCom + lngRowsBetween, cstrQtyMTTo).Value = "QtyMT"
              wsReport.Cells(clngStartCom, cstrDelCityTo).Value = "DeliveryCity"
              wsReport.Cells(clngStartCom + lngRowsBetween, cstrPriceMTTo).Value = "PriceMT"
            End If
            '  SCommodity
            wsReport.Cells(clngStartCom, cstrCommodTo).Offset(lngSecLoop).Value = wsDetails.Cells(lngLoop, cstrCommodFro)
            '  SQtyMT
            wsReport.Cells(clngStartCom + lngRowsBetween, cstrQtyMTTo).Offset(lngSecLoop).Value = wsDetails.Cells(lngLoop, cstrQtyMTFro)
            '  SDeliveryCity
            wsReport.Cells(clngStartCom, cstrDelCityTo).Offset(lngSecLoop).Value = wsDetails.Cells(lngLoop, cstrDelCityFro)
            '  SPriceMT
            wsReport.Cells(clngStartCom + lngRowsBetween, cstrPriceMTTo).Offset(lngSecLoop).Value = wsDetails.Cells(lngLoop, cstrPriceMTFro)
          End If
        Next rngCell
      Next rngArea
   
      strStamp = Format(Now(), "_yymmdd_hhmmss")
      'Save the PDF file
      If wsReport.Name = cstrREPORIG Then
        With wsReport.PageSetup
          .Zoom = False
          .Orientation = xlPortrait
          .FitToPagesWide = 1
          .FitToPagesTall = 1
        End With
        wsReport.Range("A1:G28").ExportAsFixedFormat _
              Type:=xlTypePDF, _
              Filename:=strPath & wsReport.Cells(17, 6).Value & strStamp & ".PDF", _
              Quality:=xlQualityStandard, _
              IncludeDocProperties:=True, _
              IgnorePrintAreas:=False, _
              OpenAfterPublish:=False
        wsReport.Cells(17, cstrCommodTo).Resize(4, 1).ClearContents
        wsReport.Cells(21, cstrQtyMTTo).Resize(4, 1).ClearContents
        wsReport.Cells(17, cstrDelCityTo).Resize(4, 1).ClearContents
        wsReport.Cells(21, cstrPriceMTTo).Resize(4, 1).ClearContents
      Else
'/// You would need to adapt your worksheet setup to match by recording a macro and paste the code in here.
'/// Please mind that I use wsReport while the macro recorder should use ActiveSheet
'/// or ActiveWindow.SelectedSheets
'        With wsReport.PageSetup
'          .Zoom = False
'          .Orientation = xlPortrait
'          .FitToPagesWide = 1
'          .FitToPagesTall = 1
'        End With
        wsReport.HPageBreaks.Add Before:=Range("A29")
        wsReport.Range("A1:G56").ExportAsFixedFormat _
              Type:=xlTypePDF, _
              Filename:=strPath & wsReport.Cells(17, 6).Value & strStamp & ".PDF", _
              Quality:=xlQualityStandard, _
              IncludeDocProperties:=True, _
              IgnorePrintAreas:=False, _
              OpenAfterPublish:=False
        wsReport.Cells(17, cstrCommodTo).Resize(3, 1).ClearContents
        wsReport.Cells(21, cstrQtyMTTo).Resize(3, 1).ClearContents
        wsReport.Cells(17, cstrDelCityTo).Resize(3, 1).ClearContents
        wsReport.Cells(21, cstrPriceMTTo).Resize(3, 1).ClearContents
        'deleting the range on the second page starting at row 6 and cstrCommodTo ("A") up to the last entry in cstrDelCityTo ("B")
        'this includes deleting the headers
        wsReport.Range(wsReport.Cells(clngStartCom, cstrCommodTo), wsReport.Cells(wsReport.Rows.Count, cstrDelCityTo).End(xlUp)).ClearContents
      End If
continue_here:
    Next lngCounter
  End With

end_here:
  wsDetails.AutoFilterMode = False
  dblEnd = Timer
  Debug.Print "Finished! Duration: " & dblEnd - dblStart & " seconds"
  Debug.Print "Number of items: " & objScDir.Count
  Debug.Print "Number of Piles skipped: " & lngSkip
  Debug.Print "Total number of rows: " & wsDetails.Cells(wsDetails.Rows.Count, clngColFilter).End(xlUp).Row - 1
  If lngSkip > 0 Then
    MsgBox lngSkip & " Pile(s) showed a number of more than " & clngMaxNumPile & "." & vbCrLf & _
        IIf(lngSkip > 2, "Please consider to setup another page or more pages for printout.", "") & _
        vbCrLf & vbCrLf & "Information on items and max number:" & strInfo, _
        vbInformation, "Some PILES were not processed"
  End If
  Set wsReport = Nothing
  Set rngWork = Nothing
  Set wsDetails = Nothing
  Set objScDir = Nothing
  Application.ScreenUpdating = True
  Exit Sub

err_here:
  MsgBox "An error occurred, more information in the Immediate Window", , "Sorry..."
  Debug.Print "Error Number: " & Err.Number
  Debug.Print "Error Description: " & Err.Description
  Resume end_here
End Sub

Holger
 
Upvote 0
Hi Nathan,

have you been able to test the code by now? I know I had to do a lot of guessing there and might have taken a wrong turn. If so feel free to tell what should be altered. Or have you found a workaround by yourself?

Ciao,
Holger
 
Upvote 0

Forum statistics

Threads
1,223,886
Messages
6,175,196
Members
452,616
Latest member
intern444

We've detected that you are using an adblocker.

We have a great community of people providing Excel help here, but the hosting costs are enormous. You can help keep this site running by allowing ads on MrExcel.com.
Allow Ads at MrExcel

Which adblocker are you using?

Disable AdBlock

Follow these easy steps to disable AdBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the icon in the browser’s toolbar.
2)Click on the "Pause on this site" option.
Go back

Disable AdBlock Plus

Follow these easy steps to disable AdBlock Plus

1)Click on the icon in the browser’s toolbar.
2)Click on the toggle to disable it for "mrexcel.com".
Go back

Disable uBlock Origin

Follow these easy steps to disable uBlock Origin

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back

Disable uBlock

Follow these easy steps to disable uBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back
Back
Top