• Hi All

    Please note that at the Chandoo.org Forums there is Zero Tolerance to Spam

    Post Spam and you Will Be Deleted as a User

    Hui...

  • When starting a new post, to receive a quicker and more targeted answer, Please include a sample file in the initial post.

New workbook not taking cell value from itself to Save / SaveAs

Ashish Bangera

New Member
Hi...I was trying to code Mastersheet that can filter itself, and each filtered entry gets copy / pasted on new work book. But the new workbook not taking cell value from itself when trying to Save / SaveAs. Also unable to loop the filtered entry......Please help!!!
 

Attachments

  • Sample - Copy.xlsm
    41.7 KB · Views: 3
Welcome to the forum!

Looks like you have several issues.

1. No loop to filter by each unique column G unique value.
2. You are copying all cells so filter is redundant.
3. You are attempting to save an xlsm as an xlsx. xlsx files have no macros. When you save as another file type, you need to set the 2nd parameter value for fileformat. If not, it will save but will not open (usually). There are several issues with this.
4. Your code is in ThisWorkbook. Is it meant to run in a Workbook Open, Close, or other event? If not, it should be in a Module.

For (3), there are better ways to do it. Please let me know if you want it saved as xlsm or xlsx. Also let me know what you want done if file exists already.

If you want me to work up a full solution, please respond back. Allow a day or so for me to work on it. I do a few other things so I have to work it in. I have done it many times so it may not take long once I get to it.

Tip: When saving a filename, put it in a string say s. Then, Debug.Print s, at runtime will put the value into VBE Immediate window. Click the VBE View menu to enable it if needed. I usually put it below the Code window. This will tell if you built a proper and legal filename.

IF you are the only one running it, and have the Template folder on your Desktop, the path will be fine. If for you and others, code needs to find the path to the current user's Desktop and check if the Template folder exists and create it. I can code that for you but it just depends on your goals/needs.
 
Last edited:
You may want to add B3 value to fn.

It looks like it is the Pol value. All values are the same. Extra code is needed to get that if each Line filter does not have the same Pol value that you want in B3 I think. Rather than an offset by row, a different method is needed. Of course since I don't know your whole dataset, it may be that each Line may not have the same Pol.

Code:
Sub Main()
  Dim fn As String, p As String
  Dim ws As Worksheet, wt As Worksheet
  Dim cMode As Integer, c As Range, a, e
  'Set and create desktop Template folder/path if needed.
  p = CreateObject("WScript.Shell").SpecialFolders("Desktop") & "\Template\"
  If Dir(p, vbDirectory) = "" Then MkDir p

  'Speed things along a bit...
  Application.ScreenUpdating = False
  Application.EnableEvents = False
  cMode = Application.Calculation
  Application.Calculation = xlCalculationManual

  'Set source worksheet
  Set ws = ActiveSheet
  With ws
    Set c = .Range("G11")
    'Set a = unique values in column G11 (c) and down.
    a = UniqueArrayByDict(.Range(c.Offset(1), _
      .Cells(.Rows.Count, c.Column).End(xlUp)).Value)
    .AutoFilterMode = False
    'Iterate each element in array a for unique values in filters field c.
    For Each e In a
      c.AutoFilter field:=7, Criteria1:=e
      .Range("B3").Value = e
      .UsedRange.SpecialCells(xlCellTypeVisible).Copy
      Set wt = Workbooks.Add(xlWBATWorksheet).Worksheets(1)
      wt.Paste
'???? fn built right?
      fn = p & e & ".xlsx"
      Debug.Print fn
      'Delete xlsx file if it exists.
      If Dir(fn) <> "" Then Kill fn
      wt.SaveAs fn, xlOpenXMLWorkbook
      wt.Parent.Close False
    Next e
'???? Reset B3 value?
    .Range("B3").Value = ""
    .AutoFilterMode = False 'Turn off
  End With
   
  Application.CutCopyMode = False
  Application.ScreenUpdating = True
  Application.EnableEvents = True
  Application.Calculation = cMode
End Sub


'Early Binding method requires Reference: MicroSoft Scripting Runtime, scrrun.dll
Function UniqueArrayByDict(Array1d As Variant, Optional compareMethod As Integer = 0) As Variant
  Dim dic As Object 'Late Binding method - Requires no Reference
  Set dic = CreateObject("Scripting.Dictionary")  'Late or Early Binding method
  'Dim dic As Dictionary     'Early Binding method
  'Set dic = New Dictionary  'Early Binding Method
  Dim e As Variant
  dic.CompareMode = compareMethod
  'BinaryCompare=0
  'TextCompare=1
  'DatabaseCompare=2
  For Each e In Array1d
    If Not dic.Exists(e) Then dic.Add e, Nothing
  Next e
  UniqueArrayByDict = dic.Keys
End Function
 
Dear Sir,


First off, Thank you very much for your time and kind assistance...I have mentioned below what exactly I was trying to do. I hope it’s not too much for you…



##Correction: In B3 (POL) is same as in coloumn "D" (I have shared the revised file for your review, and put the code in Module 1 as suggested by you)



#MyObject 1: I will have different Line in column "G". Each needs to be filtered and copied to a separate new workbook and that new workbook should be named by value of its Line & POL (e.g.: Asiats_UQR) with xlsx fileformat. Also, not even the filtered value should be copied but also the info from A1:B8, hence my below code includes:


Cells.Select
Cells.PasteSpecial xlPasteValues


#MyObject 2 (This can be on Sheet2): Once I get all the separate files by Line names in chosen folder, I would like to email each file as attachment to different customer.


We can have this macro in sheet2 in which I will save comprehensive email distribution list (in below format) of Line name, “To recipient” and “CC recipient”,


A B C
LineName To-Recipient CC-Recipient


So that macro goes thru by each Line name in the folder as to who it needs to mail with CC.

Lastly, In my email body, I will have a table (please see attached Sample email body) which gives a summary basis Size / Status.



###I have written the below action as to what it needs to do once filter is applied but I am lacking on looping thru each filter value in column “G”. If you can make it concise and smooth or write your own set of codes for above Myobjects, will be very very very helpful...



Sub slot_report()


Dim fname As String

Dim Path As String

Dim Newbook As Workbook


Path = "C:\Users\acer\Desktop\Template\"



'ActiveSheet.Range("G11").Offset(1, 0).Value



Application.ScreenUpdating = False

Application.DisplayAlerts = False



With ActiveSheet


.AutoFilterMode = False

.Range("G11").AutoFilter field:=7, Criteria1:="HIKARU"

Cells.Select

Selection.Copy

Workbooks.Add

ActiveSheet.Select

ActiveSheet.Paste

Application.CutCopyMode = False





Terminal = ActiveSheet.Range("B3").Value

fname = ActiveSheet.Range("G11").Offset(1, 0).Value





CheckCompatibility = False

Application.DisplayAlerts = False


Cells.Select

Cells.EntireColumn.AutoFit

Range("A1").Select



ActiveWorkbook.SaveAs filename:=Path & fname & "_" & Terminal & ".xlsx", FileFormat:=xlOpenXMLWorkbook, CreateBackup:=False



ActiveWorkbook.Close







End With



Application.ScreenUpdating = True

Application.DisplayAlerts = True




End Sub
 

Attachments

  • MyMacro.xlsm
    40.7 KB · Views: 2
  • Sample email body.docx
    12.7 KB · Views: 2
Please paste code between code tags. The block above a reply shows the way to type the code tags. Or, you can also click the first icon in 2nd toolbar menu row to paste your code.

I already did 99.9% of Objective #1. Did you even try it? I can add the two parts that I showed in the '?? comments in my code since you explained those in post #8.

FYIW, Select, Selection, Activate, ActiveWindow, and such are seldom needed. Most people learn to use those due to the macro recorder. The main reason you should go beyond the macro recorder is to shorten code but mainly to improve speed. Some tips are here: http://www.tushar-mehta.com/excel/vba/beyond_the_macro_recorder/index.htm

Since you have a 2nd objective that depends on objective #1, you have two choices: (1) Run it inside objective #1 code or (2) Run it after objective #2. (2) will add a bit more time since it has to filter the Line data again, check that the xlsx file exists, and such. Still, it is not that hard to code to do those things again. It would likely not increase time too much unless you have 1,000+ unique Line items.

In objective #2, it looks like you want to embed a table in the body of the email. Since it is not the table that was filtered, I don't know where it comes from. I would guess that it would be filtered but with fewer columns. I can't imagine that you would want a static table.

So, let me know your choice for objective #2. Also workup in the Excel file what you want for the table to include in the body of the email. Meanwhile, I will finish objective #1. I could start objective #2 but it would be best to know your preference/choice first.
 
Yes...Sir! I did try your earlier code and it works like Magic!! I could not ever imagine that much fast... :) One thing you can add is while saving file name it should be Line_POL (ex: Asiats_UQR)

As long as table concerned, you are right! It's not the table that was filtered however a separate table I suppose to mention in my email which is like a summary of the respective Line report that will be attached in mail - It will be a simple summary as to how many Empty / Full with POL / POD / Size (FYI....In my main sheet I only mentioned Size as "20" but in real case there would be three sizes 20/40/45, however this will never be my filter criteria as it always be "Line").

Have uploaded again a sample Table for "Asiats". Apart from the table rest of the email content will remain static for all the customers...

Lastly, please don't code #MyObject2 in #MyObject1, lets have #MyObject2 separate in sheet2 which has all the email addresses and I will trigger the code once #MyObject1 is complete.

FYI...The saved folder "Path" from MyObject1 will change everytime I put new Dataset, So, I hope Myobject2 will take the same path to pick files for attaching on mails. I will manually change the path in every new dataset.

I forgot to mention the subjectLine of email would be "ARRIVAL ADVICE- Cell B1"_" CellA1 "<space>" Cell B2" reference from the main sheet or respective line report (in any case value is same)..

Thank you!!
 

Attachments

  • Sample email body.docx
    12.9 KB · Views: 1
I guess you can workup a table for the email body in another sheet? I will just use a dummy table to show the concept.

For objective 1, this should do.
Code:
Sub Obj1()
  Dim fn As String, p As String
  Dim ws As Worksheet, wt As Worksheet
  Dim cMode As Integer, c As Range, r As Range
  Dim a, e
  'Set and create desktop Template folder/path if needed.
  p = CreateObject("WScript.Shell").SpecialFolders("Desktop") & "\Template\"
  If Dir(p, vbDirectory) = "" Then MkDir p

  'Speed things along a bit...
  Application.ScreenUpdating = False
  Application.EnableEvents = False
  cMode = Application.Calculation
  Application.Calculation = xlCalculationManual

  'Set source worksheet
  Set ws = Worksheets("SLOT MSG")
  With ws
    Set c = .Range("G11")
    'Set a = unique values in column G11 (c) and down.
    a = UniqueArrayByDict(.Range(c.Offset(1), _
      .Cells(.Rows.Count, c.Column).End(xlUp)).Value)
    .AutoFilterMode = False
    'Iterate each element in array a for unique values in filters field c.
    For Each e In a
      c.AutoFilter field:=7, Criteria1:=e
      Set r = .UsedRange.SpecialCells(xlCellTypeVisible)
      r.Copy
      Set wt = Workbooks.Add(xlWBATWorksheet).Worksheets(1)
      wt.Paste
      'Set B3 in both wt and ThisWorkbook
      wt.Range("B3").Formula = "=D12"
      .Range("B3").Value = wt.Range("B3").Value
      'Basefilename=Line_POL.xlsx, e.g. Asiats_UQR.xlsx
      fn = p & e & "_" & .Range("B3").Value & ".xlsx"
      'Delete xlsx file if it exists.
      If Dir(fn) <> "" Then Kill fn
      wt.SaveAs fn, xlOpenXMLWorkbook
      wt.Parent.Close False
    Next e
'???? Reset B3 value?
    .Range("B3").Value = ""
    .AutoFilterMode = False 'Turn off
  End With
 
  Application.CutCopyMode = False
  Application.ScreenUpdating = True
  Application.EnableEvents = True
  Application.Calculation = cMode
End Sub


'Early Binding method requires Reference: MicroSoft Scripting Runtime, scrrun.dll
Function UniqueArrayByDict(Array1d As Variant, Optional compareMethod As Integer = 0) As Variant
  Dim dic As Object 'Late Binding method - Requires no Reference
  Set dic = CreateObject("Scripting.Dictionary")  'Late or Early Binding method
  'Dim dic As Dictionary     'Early Binding method
  'Set dic = New Dictionary  'Early Binding Method
  Dim e As Variant
  dic.CompareMode = compareMethod
  'BinaryCompare=0
  'TextCompare=1
  'DatabaseCompare=2
  For Each e In Array1d
    If Not dic.Exists(e) Then dic.Add e, Nothing
  Next e
  UniqueArrayByDict = dic.Keys
End Function
 
Last edited:
As for the folder, if you want it variable in value, there are several ways to do it. You can store the path (p) into a cell, a custom property, an external file, the registry, etc. Or, hard code it in the Subs or add an input parameter for the Obj1 and Obj2 Subs and pass the value in other Subs.

The subject line has some data with no values since your sheet Slot Msg had none in those cells.

The dummy table, eTable, is in the sheet Word that I added.

I had to add an existing Line value in sheet Sheet2Line so that a test email could Display. I commented a SendDelay feature so when you change Display to Send, you will have a chance to set delivery send date-time. It is handy in case you send and find, oops, I sent this in error.

Code:
Sub Obj2()
  Dim ws As Worksheet, r As Range, c As Range
  Dim p$, fn$, sTo$, sCC$, sSubject$, bPrefix$, bSuffix$
  'Tools > References > Microsoft Outlook xx.0 Object Library
  Dim olApp As Outlook.Application, olMail As Outlook.MailItem
  'Tools > References > Microsoft Word xx.0 Object Library
  Dim Word As Document, wr As Word.Range
 
  'Workbooks to add as email attachment
  p = CreateObject("WScript.Shell").SpecialFolders("Desktop") _
    & "\Template\"
  If Dir(p, vbDirectory) = "" Then Exit Sub
 
  bPrefix = "Dear Customer," & vbCrLf & vbCrLf & _
    "XXXX xXXXXXX X XXXXXXXXXXX XXXXXXXXXXXX XXXX X  XXXXXXXXXXXX XXXXXXXXX" & vbCrLf & _
    "XXXX xXXXXXX X XXXXXXXXXXX XXXXXXXXXXXX XXXX X  XXXXXXXXXXXX XXXXXXXXX" & vbCrLf & _
    "XXXX xXXXXXX X XXXXXXXXXXX XXXXXXXXXXXX XXXX X  XXXXXXXXXXXX XXXXXXXXX" & vbCrLf & _
    "XXXX xXXXXXX X XXXXXXXXXXX XXXXXXXXXXXX XXXX X  XXXXXXXXXXXX XXXXXXXXX" & vbCrLf & _
    "XXXX xXXXXXX X XXXXXXXXXXX XXXXXXXXXXXX XXXX X  XXXXXXXXXXXX XXXXXXXXX" & vbCrLf & _
    vbCrLf & vbCrLf
   
  bSuffix = vbCrLf & vbCrLf & _
    "XXXX xXXXXXX X XXXXXXXXXXX XXXXXXXXXXXX XXXX X  XXXXXXXXXXXX XXXXXXXXX" & vbCrLf & _
    "XXXX xXXXXXX X XXXXXXXXXXX XXXXXXXXXXXX XXXX X  XXXXXXXXXXXX XXXXXXXXX" & vbCrLf & _
    "XXXX xXXXXXX X XXXXXXXXXXX XXXXXXXXXXXX XXXX X  XXXXXXXXXXXX XXXXXXXXX" & vbCrLf & _
    "XXXX xXXXXXX X XXXXXXXXXXX XXXXXXXXXXXX XXXX X  XXXXXXXXXXXX XXXXXXXXX" & vbCrLf & _
    vbCrLf & vbCrLf & vbCrLf & _
    "Regards," & vbCrLf & vbCrLf & "Ashish a.Bangera"
 
  Set olApp = New Outlook.Application
  Set olMail = olApp.CreateItem(olMailItem)
 
  Set ws = ThisWorkbook.Worksheets("Sheet2Line ")
  With ws
    Set r = .Range("A2", .Range("A2").End(xlDown))
    For Each c In r
      fn = Dir(p & c & "_*.xlsx")
      'If no file to attach exists, go to next Line item.
      If fn = "" Then GoTo NextC
      fn = p & fn
     
      sTo = c.Offset(, 1).Value2
      sCC = c.Offset(, 2).Value2
      If sTo = "" And sCC = "" Then GoTo NextC
     
      sSubject = "ARRIVAL ADVICE- " & .[B1] & _
        "_" & .[A1] & " " & .[B2]
      With olMail
        .Importance = olImportanceNormal
        .To = sTo
        .CC = sCC
        .Subject = sSubject
        .Attachments.Add fn
       
        '.GetInspector.Display
        Set Word = .GetInspector.WordEditor
        'Add Prefix to body.
        Word.Content = bPrefix
        'Add table to body.
        [eTable].Copy
        Set wr = Word.Content
        wr.Collapse Direction:=wdCollapseEnd
        wr.Paste
        wr.Collapse Direction:=wdCollapseEnd
        wr.Text = bSuffix
       
        Application.CutCopyMode = False
       
        'https://msdn.microsoft.com/en-us/library/microsoft.office.interop.outlook._mailitem.deferreddeliverytime.aspx?cs-save-lang=1&cs-lang=vb#code-snippet-1
        '.DeferredDeliveryTime = Now + TimeValue("00:10:00")
        .Display
        '.Send
      End With
NextC:
    Next c
  End With
 
  Set olMail = Nothing
  Set olApp = Nothing
End Sub
 

Attachments

  • FilterToNewXLSXs.xlsm
    82.7 KB · Views: 12
Good Morning Sir! Hope you are doing fine....

My apologies for the delayed response...I was so much overwhelmed with official work that I am getting some peaceful time today since I last spoke to you...

I ran all the three set of module and below is my review..

Module1 (Obj1): For the first time it runs fine..but later it gives some DLL error..I was not able to fix it...

In my Mainsheet, I also have the formula: =SUBTOTAL(3,B12:B20) , =SUBTOTAL(9,S12:S20), =SUBTOTAL(9,T12:T20) at the end of respective coloumns, so that I get the respective value after pasting all in each separated files , but for some reason it is not working...

Module2 (mobj2) / Module (moutlook): It is giving me attached error...please check.
 

Attachments

  • Error.jpg
    Error.jpg
    568.8 KB · Views: 3
Hello Kenneth... I have been testing the code and it works superb for me as I did some tweaks in it...

Just want to take this opportunity to say thank you!!..
 
Back
Top