VBA для фильтрации и отправки электронной почты - PullRequest
0 голосов
/ 08 октября 2018

Я пытаюсь автоматизировать процесс электронной почты, который мы отправляем различным держателям стека.

Я хотел отфильтровать столбец D на основе балансовой единицы и отослать электронное письмо людям, указанным в столбце O (электронное письмо не должно дублироваться), а также необходимо включить CC (без дубликатов)

enter image description here

Ниже представлен VBA, который пытается, но не может включить TO и CC.

Sub Send_Row_Or_Rows_2()

    Dim OutApp As Object
    Dim OutMail As Object
    Dim rng As Range
    Dim Ash As Worksheet
    Dim Cws As Worksheet
    Dim Rcount As Long
    Dim Rnum As Long
    Dim FilterRange As Range
    Dim FieldNum As Integer
    Dim StrBody As String
    Dim StrBody2 As String
    Dim FileToAttach As String
    Dim RngTo As Range

    Set RngTo = Ash.Columns("H").Offset(1, 0).SpecialCells(xlCellTypeVisible)

    StrBody = "<BODY style=font-size:11pt;font-family:Calibri>Dear Approver,<p>Please be informed that below invoices are waiting for your approval in BasWare for more than 10 days.  Please check them and take action accordingly as soon as possible.</BODY>"

    'On Error GoTo cleanup
    Set OutApp = CreateObject("Outlook.Application")

    With Application
        .EnableEvents = False
        .ScreenUpdating = False
    End With

    'Set filter sheet, you can also use Sheets("MySheet")
    Set Ash = Worksheets("rawdata")

    'Set filter range and filter column (column with e-mail addresses)
    Set FilterRange = Ash.Range("A4:M" & Ash.Rows.Count)
    FieldNum = 4                                 'Filter column = D because the filter range start in column A

    'Add a worksheet for the unique list and copy the unique list in A1
    Set Cws = Worksheets.Add
    FilterRange.Columns(FieldNum).AdvancedFilter _
        Action:=xlFilterCopy, _
        CopyToRange:=Cws.Range("A1"), _
        CriteriaRange:="", Unique:=True

    'Count of the unique values + the header cell
    Rcount = Application.WorksheetFunction.CountA(Cws.Columns(1))

    'If there are unique values start the loop
    If Rcount >= 2 Then
        For Rnum = 2 To Rcount

            'Filter the FilterRange on the FieldNum column
            FilterRange.AutoFilter Field:=FieldNum, _
                                   Criteria1:=Cws.Cells(Rnum, 1).Value
            'If the unique value is a mail addres create a mail
            If Cws.Cells(Rnum, 1).Value Like "?*?*?*" Then

                With Ash.AutoFilter.Range
                    On Error Resume Next
                    Set rng = .SpecialCells(xlCellTypeVisible)
                    On Error GoTo 0
                End With

                Set OutMail = OutApp.CreateItem(0)

                On Error Resume Next
                With OutMail
                    .To = Ash.Cells(Rnum, 15).Value
                    .SentOnBehalfOfName = "CDM_Basware_Administration@esab.com"
                    .CC = sCC
                    .Subject = "Reminder - Pending Invoices - More than 10 days"
                    .HTMLBody = StrBody & RangetoHTML(rng) & signature
                    FileToAttach = "C:\Users\gyousz\Desktop\Weekly_Customer CL3 and PT Control file_May_2018"
                    .Display
                End With

                On Error GoTo 0

                Set OutMail = Nothing
            End If

            'Close AutoFilter
            Ash.AutoFilterMode = False

        Next Rnum
    End If

    With Application
        .EnableEvents = True
        .ScreenUpdating = True
    End With

End Sub

Function RangetoHTML(rng As Range)

    Dim fso As Object
    Dim ts As Object
    Dim TempFile As String
    Dim TempWB As Workbook

    TempFile = Environ$("temp") & "/" & Format(Now, "dd-mm-yy h-mm-ss") & ".htm"

    'Copy the range and create a new workbook to paste the data in
    rng.Copy
    Set TempWB = Workbooks.Add(1)
    With TempWB.Sheets(1)
        .Cells(1).PasteSpecial Paste:=8
        .Cells(1).PasteSpecial xlPasteValues, , False, False
        .Cells(1).PasteSpecial xlPasteFormats, , False, False
        .Cells(1).Select
        Application.CutCopyMode = False
        On Error Resume Next
        .DrawingObjects.Visible = True
        .DrawingObjects.Delete
        On Error GoTo 0
    End With

    'Publish the sheet to a htm file
    With TempWB.PublishObjects.Add( _
         SourceType:=xlSourceRange, _
         Filename:=TempFile, _
         Sheet:=TempWB.Sheets(1).Name, _
         Source:=TempWB.Sheets(1).UsedRange.Address, _
         HtmlType:=xlHtmlStatic)
        .Publish (True)
    End With

    'Read all data from the htm file into RangetoHTML
    Set fso = CreateObject("Scripting.FileSystemObject")
    Set ts = fso.GetFile(TempFile).OpenAsTextStream(1, -2)
    RangetoHTML = ts.ReadAll
    ts.Close
    RangetoHTML = Replace(RangetoHTML, "align=center x:publishsource=", _
                          "align=left x:publishsource=")

    'Close TempWB
    TempWB.Close savechanges:=False

    'Delete the htm file we used in this function
    Kill TempFile
    Set ts = Nothing
    Set fso = Nothing
    Set TempWB = Nothing

End Function

Ответы [ 3 ]

0 голосов
/ 16 октября 2018

Я решу проблему создания уникальных emailTO и emailCC из листа Cws.Для этого я предлагаю вам использовать словари.

Добавить ссылку на «Microsoft Scripting Runtime» согласно скриншоту.enter image description here

Также дано улучшение и предложение о том, как прикрепить файл.

Sub Send_Row_Or_Rows_2()

    Dim OutApp As Object
    Dim OutMail As Object
    Dim rng As Range
    Dim Ash As Worksheet
    Dim Cws As Worksheet
    Dim Rcount As Long
    Dim Rnum As Long
    Dim FilterRange As Range
    Dim FieldNum As Integer
    Dim StrBody As String
    Dim StrBody2 As String
    Dim FileToAttach As String
    Dim RngTo As Range

    Set RngTo = Ash.Columns("H").Offset(1, 0).SpecialCells(xlCellTypeVisible)

    StrBody = "<BODY style=font-size:11pt;font-family:Calibri>Dear Approver,<p>Please be informed that below invoices are waiting for your approval in BasWare for more than 10 days.  Please check them and take action accordingly as soon as possible.</BODY>"

    'On Error GoTo cleanup
    Set OutApp = CreateObject("Outlook.Application")

    With Application
        .EnableEvents = False
        .ScreenUpdating = False
    End With

    'Set filter sheet, you can also use Sheets("MySheet")
    Set Ash = Worksheets("rawdata")

    'Set filter range and filter column (column with e-mail addresses)
    Set FilterRange = Ash.Range("A4:M" & Ash.Rows.Count)
    FieldNum = 4                                 'Filter column = D because the filter range start in column A

    'Add a worksheet for the unique list and copy the unique list in A1
    Set Cws = Worksheets.Add
    FilterRange.Columns(FieldNum).AdvancedFilter _
        Action:=xlFilterCopy, _
        CopyToRange:=Cws.Range("A1"), _
        CriteriaRange:="", Unique:=True

    'Count of the unique values + the header cell
    Rcount = Application.WorksheetFunction.CountA(Cws.Columns(1))

    'If there are unique values start the loop
    If Rcount >= 2 Then

        'find unique emails for TO as CC
        Dim dictTO As New Dictionary
        Dim dictCC As New Dictionary
        Dim emailTO As String
        Dim emailCC As String

        For Rnum = 2 To Rcount
            emailTO = Trim(UCase(Cws.Range("O" & Rnum).Value))
            emailCC = Trim(UCase(Cws.Range("P" & Rnum).Value))
            If Not (emailTO = "") Then
                If Not dictTO.Exists(emailTO) Then
                    Call dictTO.Add(emailTO, emailTO)
                End If
            End If
            If Not (emailCC = "") Then
                If Not dictCC.Exists(emailCC) Then
                    Call dictCC.Add(emailCC, emailCC)
                End If
            End If
        Next Rnum

        'remove CC emails that are in To dict
        For Rnum = 1 To dictTO.Count
            If dictCC.Exists(dictTO.Item(Rnum)) Then
                dictCC.Remove (dictTO.Item(Rnum))
            End If
        Next

        emailTO = ""
        emailCC = ""

        'Generate To Addresses
        For Rnum = 1 To dictTO.Count
            emailTO = emailTO & dictTO.Item(Rnum) & ","
        Next

        'Generate CC Addresses
        For Rnum = 1 To dictTO.Count
            emailCC = emailCC & dictCC.Item(Rnum) & ","
        Next

        With Ash.AutoFilter.Range
            On Error Resume Next
            Set rng = .SpecialCells(xlCellTypeVisible)
            On Error GoTo 0
        End With

        Set OutMail = OutApp.CreateItem(0)
        FileToAttach = "C:\Users\gyousz\Desktop\Weekly_Customer CL3 and PT Control file_May_2018"
        'fixed file being attached everytime - maybe saved a copy of Cws sheet and attach the workbook

        On Error Resume Next
        Dim fso As New FileSystemObject
        With OutMail
            .To = emailTO
            .SentOnBehalfOfName = "CDM_Basware_Administration@esab.com"
            .CC = emailCC
            .Subject = "Reminder - Pending Invoices - More than 10 days"
            .HTMLBody = StrBody & RangetoHTML(rng) & Signature
            If (fso.FileExists(File)) Then 'checking if file exists
                .Attachments.Add FileToAttach 'corrected how to add an attachment
            End If
            .Display
        End With

        On Error GoTo 0

        Set OutMail = Nothing


        'Close AutoFilter
        Ash.AutoFilterMode = False


    End If

    With Application
        .EnableEvents = True
        .ScreenUpdating = True
    End With

End Sub

Function RangetoHTML(rng As Range)

    Dim fso As Object
    Dim ts As Object
    Dim TempFile As String
    Dim TempWB As Workbook

    TempFile = Environ$("temp") & "/" & Format(Now, "dd-mm-yy h-mm-ss") & ".htm"

    'Copy the range and create a new workbook to paste the data in
    rng.Copy
    Set TempWB = Workbooks.Add(1)
    With TempWB.Sheets(1)
        .Cells(1).PasteSpecial Paste:=8
        .Cells(1).PasteSpecial xlPasteValues, , False, False
        .Cells(1).PasteSpecial xlPasteFormats, , False, False
        .Cells(1).Select
        Application.CutCopyMode = False
        On Error Resume Next
        .DrawingObjects.Visible = True
        .DrawingObjects.Delete
        On Error GoTo 0
    End With

    'Publish the sheet to a htm file
    With TempWB.PublishObjects.Add( _
         SourceType:=xlSourceRange, _
         Filename:=TempFile, _
         Sheet:=TempWB.Sheets(1).Name, _
         Source:=TempWB.Sheets(1).UsedRange.Address, _
         HtmlType:=xlHtmlStatic)
        .Publish (True)
    End With

    'Read all data from the htm file into RangetoHTML
    Set fso = CreateObject("Scripting.FileSystemObject")
    Set ts = fso.GetFile(TempFile).OpenAsTextStream(1, -2)
    RangetoHTML = ts.ReadAll
    ts.Close
    RangetoHTML = Replace(RangetoHTML, "align=center x:publishsource=", _
                          "align=left x:publishsource=")

    'Close TempWB
    TempWB.Close savechanges:=False

    'Delete the htm file we used in this function
    Kill TempFile
    Set ts = Nothing
    Set fso = Nothing
    Set TempWB = Nothing

End Function

Удачи

0 голосов
/ 17 октября 2018

Пожалуйста, разделите ваши коды на отдельные функции:

  • Один для получения получателей
  • Один для отправки электронной почты

Я заново создал вашу рабочую книгу.Приведенный ниже код будет выполнять ff:

  • Сначала получить все коды компании
  • Фильтровать список по балансовым единицам
  • Получить список TO и CC
  • Отправитьemail

Единственная оставленная здесь модификация - создание другой функции для отправки электронной почты (и передачи переменных).

    Sub Send_Row_Or_Rows_2()
        With Application
            .EnableEvents = False
            .ScreenUpdating = False
        End With

        On Error GoTo ErrorHandler

        ' Initialization
        ' ==================================================
        Dim shtRec As Worksheet: Set shtRec = ThisWorkbook.Sheets("rawdata")
        Dim intLastRow As Long, intLastCol As Long ' for end cell
        Dim i As Long, j As Long, k As Long, rngCell As Range ' for loops
        Dim rngFilter As Range ' filter range
        Dim strEmailTO As String, strEmailCC As String ' recipients

        Dim arrCoCd() As String ' company codes
        Dim arrEmailTO() As String ' TO recipients
        Dim arrEmailCC() As String ' CC recipients

        Dim arrEmailRec() As String, strEmailRec As String ' temporary variables

        ' Get Recipient header column indexes
        Dim intRowHead As Integer: intRowHead = 4 ' header row
        Dim intColCoCd As Integer: intColCoCd = 1 ' company code column
        Dim intColTo   As Integer:   intColTo = 3 ' TO column
        Dim intColCc   As Integer:   intColCc = 4 ' CC column

        ' Filter Recipients by Company Code
        ' ==================================================
        With shtRec
            ' Remove filter
            If Not .AutoFilter Is Nothing Then .AutoFilterMode = False

            ' Get end cell
            With .Cells.SpecialCells(xlCellTypeLastCell)
                intLastRow = .Row
                intLastCol = .Column
            End With

            ' Add filter
            Set rngFilter = .Range(Cells(intRowHead, 1), Cells(intLastRow, intLastCol))
            rngFilter.AutoFilter

            ' Get list of company codes
            ' =========================
            ReDim arrCoCd(1 To intLastRow)
            For i = (intRowHead + 1) To intLastRow ' exclude header
                With .Cells(i, intColCoCd)
                    If .Value <> vbNullString Then
                        k = k + 1
                        arrCoCd(k) = VBA.Trim(.Value)
                    End If
                End With
            Next i

            ' Reset variable
            k = 0

            ' Get unique values
            ' =========================
            arrCoCd = FnStrUniqueArray(arrCoCd)

            ' Filter by Company Code
            For i = LBound(arrCoCd) To UBound(arrCoCd)
                If arrCoCd(i) <> vbNullString Then
                    rngFilter.AutoFilter Field:=intColCoCd, Criteria1:="=" & arrCoCd(i)
                    While Not Application.CalculationState = xlDone: DoEvents: Wend

                    ' Get list only if with results
                    If .AutoFilter.Range.Columns(intColCoCd).SpecialCells(xlCellTypeVisible).Cells.Count > 1 Then
                        Dim strRng As String

                        ' Get TO list
                        ' =========================
                        ' Loop each visible cell in TO column
                        k = 0
                        strRng = .AutoFilter.Range.Offset(1, 0).Resize(.AutoFilter.Range.Rows.Count - 1).SpecialCells(xlCellTypeVisible).Columns(intColTo).Address(False, False)

                        For Each rngCell In .Range(strRng)
                            ' Remove spaces
                            strEmailRec = VBA.Trim(VBA.Replace(rngCell.Value, " ", ""))

                            ' Get email addresses
                            arrEmailRec = VBA.Split(strEmailRec, ";")

                            ' Add email addresses to list
                            If k = 0 Then k = k + 1 Else k = UBound(arrEmailTO) + 1
                            ReDim Preserve arrEmailTO(1 To k)

                            For j = LBound(arrEmailRec) To UBound(arrEmailRec)
                                arrEmailTO(k) = arrEmailRec(j)
                            Next j

                            ' Remove duplicates in list
                            arrEmailTO = FnStrUniqueArray(arrEmailTO)

                            ' Reset variables
                            strEmailRec = vbNullString
                            Erase arrEmailRec
                        Next rngCell

                        ' Get CC list
                        ' =========================
                        ' Loop each visible cell in CC column
                        k = 0
                        strRng = .AutoFilter.Range.Offset(1, 0).Resize(.AutoFilter.Range.Rows.Count - 1).SpecialCells(xlCellTypeVisible).Columns(intColCc).Address(False, False)

                        For Each rngCell In .Range(strRng)
                            ' Remove spaces
                            strEmailRec = VBA.Trim(VBA.Replace(rngCell.Value, " ", ""))

                            ' Get email addresses
                            arrEmailRec = VBA.Split(strEmailRec, ";")

                            ' Add email addresses to list
                            If k = 0 Then k = k + 1 Else k = UBound(arrEmailCC) + 1
                            ReDim Preserve arrEmailCC(1 To k)

                            For j = LBound(arrEmailRec) To UBound(arrEmailRec)
                                arrEmailCC(k) = arrEmailRec(j)
                            Next j

                            ' Remove duplicates in list
                            arrEmailCC = FnStrUniqueArray(arrEmailCC)

                            ' Reset variables
                            strEmailRec = vbNullString
                            Erase arrEmailRec
                        Next rngCell
                    End If

                    ' Join recipients list
                    strEmailTO = VBA.Join(arrEmailTO, ";")
                    strEmailCC = VBA.Join(arrEmailCC, ";")

                    ' Send email
                    ' <your code to send email passing variables - strEmailTO, strEmailCC, ...>

                    ' Reset variables
                    Erase arrEmailTO
                    Erase arrEmailCC
                End If
            Next i

        End With

    ErrorHandler:

        With Application
            .EnableEvents = True
            .ScreenUpdating = True
        End With

    End Sub

Вот код для удаления дубликатов в массивах.Справка: vba получить уникальные значения из массива

Function FnStrUniqueArray(aTmpArray() As String)
    Dim ctr As Long, cTmpCollection As New Collection, cTmpCollect

    For Each cTmpCollect In aTmpArray
       cTmpCollection.Add cTmpCollect, cTmpCollect
    Next

    ' convert collection to array
    ReDim aTmpArray(1 To cTmpCollection.Count)
    For ctr = 1 To cTmpCollection.Count
        aTmpArray(ctr) = cTmpCollection(ctr)
    Next ctr

    Set cTmpCollection = Nothing
    FnStrUniqueArray = aTmpArray
End Function
0 голосов
/ 11 октября 2018

Полагаю, я хотел бы знать, как выглядят ваши результаты сейчас, но вы могли бы сделать следующее - вам нужно отсортировать лист по компании

DIM TheToList, TheCCList, CurrRow


CurrRow = 1

Do until --end of the sheet is reached ---
TheToList = ""
TheCCList = ""

if cells(CurrRow, 4) = cells(CurrRow-1,4) then    ' same company
  ' I was wrong >>> if instr(1,TheCCList,cells(CurrRow,15)) = 0   then ' diff TO
  if instr(1,TheToList,cells(CurrRow,15)) = 0   then ' diff TO
        TheToList = TheToList & cells(CurrRow,15) & "; "
    end if
    if instr(1,TheCCList,cells(CurrRow,16)) = 0   then ' diff CC
        TheCCList = TheCCList & cells(CurrRow,16) & "; "
    end if
else
    if CurrRow <> 1 then  
         ' do your output here because the company has changed
         ' probably call a subroutine because you will need it at the end too 
    end if
    TheToList = ""
    TheCCList = ""
end if
CurrRow = CurrRow + 1

Loop

' call your output subroutine one more time
...