Notifications
Clear all

Consolidar Planilhas - Ajustar Código

5 Posts
2 Usuários
0 Reactions
995 Visualizações
(@doncelio)
Posts: 4
New Member
Topic starter
 

Preciso consolidar 3 arquivos .XLSM.
Cada arquivo tem 3 planilhas, MAS, preciso pegar apenas a Primeira planilha.
Todos os arquivos possuem a mesma estrutura.
A partir do segundo arquivo não pode vir o cabeçalho, ou seja, a primeira linha não deve ser importada.

O código abaixo funciona bem e faz quase todo o processo. Porém, ele importa todas as planilhas de cada arquivo e ainda também importa a primeira linha de todas planilhas.

Desde já agradeço aos que puderem ajudar.

Célio

Public Sub ConsolidateSheets()
    Dim Wb1 As Workbook
    Dim Wb2 As Workbook
    Dim ws1 As Worksheet
    Dim ws2 As Worksheet
    Dim ws3 As Worksheet
    Dim rng1 As Range
    Dim rng2 As Range
    Dim rng3 As Range
    Dim rngArea As Range
    Dim lrowSpace As Long
    Dim lSht As Long
    Dim lngCalc As Long
    Dim lngRow As Long
    Dim lngCol As Long
    Dim X()
    Dim bProcessFolder As Boolean
    Dim bNewSheet As Boolean

    Dim StrPrefix
    Dim strFileName As String
    Dim strFolderName As String

    'Declaração Variant necessária para o objeto Shell para usar o diretório padrão
    Dim strDefaultFolder As Variant


 bProcessFolder = (MsgBox("Processa um único diretório (SIM)," & vbNewLine & "or single file (NÃO)", vbYesNo, "Escopo da Aplicação: Diretório ou Arquivo Simples") = vbYes)
    bNewSheet = (MsgBox("Extrai todos os dados para uma única planilha (SIM)," & vbNewLine & "Ou um arquivo destino para cada planilha de origem (NÃO)", vbYesNo, "Formato de saída: Planilha única ou planilha por planilha") = vbYes)
    If Not bProcessFolder Then
        If Not bNewSheet Then
            MsgBox "Não há muito sentido em criar uma réplica exata de seu arquivo de origem :)"
            Exit Sub
        End If
    End If

    'Coloque o diretório padrão aqui se necessário
    strDefaultFolder = "\tcerj96TCERJ_TSUM1 - CONTROLE CONTÍNUO - CICLO 1CONTROLE PROCESSOS POR CCM"

    'Se o usuário estiver copiando todas as planilhas para uma planilha única então a linha de espaçamento
    'para distinguir entre diferentes planihas pode ser colocada aqui
    lrowSpace = 0

    If bProcessFolder Then
        strFolderName = BrowseForFolder(strDefaultFolder)
        'Procura por xls, xlsx, xlsm files
        strFileName = Dir(strFolderName & "*.xls*")
    Else
        strFileName = Application _
                      .GetOpenFilename("Selecione os arquivos (*.xls*), *.xls*")
    End If

    Set Wb1 = Workbooks.Add(1)
    Set ws1 = Wb1.Sheets(1)
    If Not bNewSheet Then ws1.Range("A1:K1") = Array("workbook name", "worksheet count")

    'Desliga Atualização de tela, Eventos, Alertas e colocar o modo de cálculo para manual
    With Application
        .DisplayAlerts = False
        .EnableEvents = False
        .ScreenUpdating = False
        lngCalc = .Calculation
        .Calculation = xlCalculationManual
    End With

    'Coloca o caminha fora do loop
    StrPrefix = strFolderName & IIf(bProcessFolder, "", vbNullString)

    Do While Len(strFileName) > 0
        'Providencia o status do progresso ao usuário
        Application.StatusBar = Left("Processing " & strFolderName & "" & strFileName, 255)
        'Abre cada pasta de trabalho no diretório de interesse
        Set Wb2 = Workbooks.Open(StrPrefix & strFileName)
        If Not bNewSheet Then
            'Adiciona um resumo à primeira planilha
            ws1.Cells(Rows.Count, "A").End(xlUp).Offset(1, 0) = Wb2.Name
            ws1.Cells(Rows.Count, "A").End(xlUp).Offset(0, 1) = Wb2.Sheets.Count
        End If
        For Each ws2 In Wb2.Sheets
        'If ws2.Name = "Processos" Then
            If bNewSheet Then
                'Todos os dados para uma única planilha
                'Pula a importação dos dados da planilha se a planilha de origem estiver vazia
                Set rng2 = ws2.Cells.Find("*", ws2.[a1], xlValues, , xlByRows, xlPrevious)

                If Not rng2 Is Nothing Then
                    Set rng1 = ws1.Cells.Find("*", ws1.[a1], xlValues, , xlByRows, xlPrevious)
                    'Encontra a primeira linha em branco na planilha destino
                    If Not rng1 Is Nothing Then
                        Set rng3 = ws2.Range(ws2.UsedRange.Cells(1), ws2.Cells(rng2.Row, "A"))
                        'Certifica que a área da linha na planilha destino não será excedida
                        If rng3.Rows.Count + rng1.Row < Rows.Count Then
                            'Copia os dados do intervalo usado de cada planilha de origem para a primeira linha em branco
                            ' da planilha destino, usando o endereço inicial da coluna da planilha de origem sendo copiada
                            ws2.UsedRange.Copy ws1.Cells(rng1.Row + 1 + lrowSpace, ws2.UsedRange.Cells(1).Column)
                        Else
                            MsgBox "Tamanho da planilha consolidada excedido. Processo interrompido" & vbNewLine & _
                                   "sheet: " & ws2.Name & vbNewLine & "of" & vbNewLine & "workbook: " & Wb2.Name
                            Wb2.Close False
                            Exit Do
                        End If
                        'Colore a primeira qualquer linha de espaçamento
                        If lrowSpace <> 0 Then ws1.Rows(rng1.Row + 1).Interior.Color = vbGreen
                    Else
                        'Planilha destino está vazia então copia para a primeira linha
                        ws2.UsedRange.Copy ws1.Cells(1, ws2.UsedRange.Cells(1).Column)
                    End If
                End If
            Else
                'Nova planilha para cada planilha de origem
                ws2.Copy after:=Wb1.Sheets(Wb1.Sheets.Count)
                'Remove qualquer link na planilha destino
                'With Wb1.Sheets(Wb1.Sheets.Count).Cells
                    '.Copy
                    '.PasteSpecial xlPasteValues
                'End With
                On Error Resume Next
                Wb1.Sheets(Wb1.Sheets.Count).Name = ws2.Name
                'Nome da planilha já existe na pasta de trabalho destino
                If Err.Number <> 0 Then
                    'Adiciona um número ao nome da planilha até obter um nome único
                    Do
                        lSht = lSht + 1
                        Set ws3 = Wb1.Sheets(ws2.Name & " " & lSht)
                    Loop While Not ws3 Is Nothing
                    lSht = 0
                End If
                On Error GoTo 0
            End If
        'End If
        Next ws2
        'Fecha a pasta de trabalho aberta
        Wb2.Close False
        'Checa se necessário forçar uma saída do laço DO se processando um arquivo único
        If bProcessFolder = False Then Exit Do
        strFileName = Dir
    Loop

    'Remove qualquer link se o usuário usou uma planilha destino
    If bNewSheet Then
        With ws1.UsedRange
            .Copy
            .Cells(1).PasteSpecial xlPasteValuesAndNumberFormats
            .Cells(1).Activate
            ws1.Range("A1:k1").Font.Bold = True
            ws1.Columns.AutoFit
        End With
    Else
        'Formata a planilha resumo se o usuário criou planilhas separadas
        ws1.Activate
        ws1.Range("A1:k1").Font.Bold = True
        ws1.Columns.AutoFit
    End If

    With Application
        .CutCopyMode = False
        .DisplayAlerts = True
        .EnableEvents = True
        .ScreenUpdating = True
        .Calculation = lngCalc
        .StatusBar = vbNullString
    End With
End Sub



Function BrowseForFolder(Optional OpenAt As Variant) As Variant
'From Ken Puls as used in his vbaexpress.com article
'http://www.vbaexpress.com/kb/getarticle.php?kb_id=284

    Dim ShellApp As Object
    'Cria uma janela de navegador de arquivo no diretório padrão
    Set ShellApp = CreateObject("Shell.Application"). _
                   BrowseForFolder(0, "Please choose a folder", 0, OpenAt)

    'Coloca o diretório para o padrão selecionado.  (Em caso de erro cancela)
    On Error Resume Next
    BrowseForFolder = ShellApp.self.Path
    On Error GoTo 0

    'Destroi a aplicação Shell
    Set ShellApp = Nothing

    'Checa por entradas inválidas e não entradas e envia para o Manipulador de Erro se encontrado
    
    'Seleções válidas pode começar com L: (Em que L é a letra) ou
    '\ (com em \nomedoservidornomearquivo.  Todos os outros serão inválidos
    Select Case Mid(BrowseForFolder, 2, 1)
    Case Is = ":"
        If Left(BrowseForFolder, 1) = ":" Then GoTo Invalid
    Case Is = ""
        If Not Left(BrowseForFolder, 1) = "" Then GoTo Invalid
    Case Else

        GoTo Invalid
    End Select

    Exit Function

Invalid:
    'Se foi determinado que a seleção é inválida, coloque pra Falso
    BrowseForFolder = False
End Function
 
Postado : 01/07/2016 12:06 pm
(@mprudencio)
Posts: 2749
Famed Member
 

Disponibilize os arquivos com alguns dados

Marcelo Prudencio
Microsoft Excel Brasil no Facebook

"Começar já é a metade do caminho."
Autor Desconhecido

Simplifica que simples fica.
Nicole Tomazella.

"O Simples é Sempre Melhor Que o Complicado"
Jorge Paulo Lemann.

 
Postado : 01/07/2016 2:19 pm
(@doncelio)
Posts: 4
New Member
Topic starter
 

MPrudencio, obrigado pela atenção.

Segue uma pasta compacta com os arquivos.

Obs.: os dados nos arquivos são fictícios, porém, a estrutura é a que será utilizada.

Obrigado.

 
Postado : 04/07/2016 7:05 am
(@doncelio)
Posts: 4
New Member
Topic starter
 

Atribuindo o crédito devido.

O código original foi criado por Dave Brett e encontra-se no seguinte link: https://www.experts-exchange.com/articles/2804/Collating-worksheets-from-one-or-more-workbooks-into-a-summary-file.html

Nos comentários do link acima o autor responde uma dúvida de um usuário e oferece uma solução que deve também atender ao que preciso, pois agora consigo importar apenas uma planilha independentemente do número de planilhas existentes na pasta de trabalho. Agora falta apenas remover as linhas de cabeçalhos a partir da segunda planilha copiada, em tese o autor também postou uma solução para isso, porém, implementei e não funcionou. Continuarei a tentar, mas se alguém souber resolver avise, por favor.

Obrigado.

Segue o código original:

Public Sub ConsolidateSheets()
    Dim Wb1 As Workbook
    Dim Wb2 As Workbook
    Dim ws1 As Worksheet
    Dim ws2 As Worksheet
    Dim ws3 As Worksheet
    Dim rng1 As Range
    Dim rng2 As Range
    Dim rng3 As Range
    Dim rngArea As Range
    Dim lrowSpace As Long
    Dim lSht As Long
    Dim lngCalc As Long
    Dim lngRow As Long
    Dim lngCol As Long
    Dim X()
    Dim bProcessFolder As Boolean
    Dim bNewSheet As Boolean

    Dim StrPrefix
    Dim strFileName As String
    Dim strFolderName As String

    'variant declaration needed for the Shell object to use a default directory
    Dim strDefaultFolder As Variant


 bProcessFolder = (MsgBox("Process a single folder (Yes)," & vbNewLine & "or single file (No)", vbYesNo, "Application Scope: Folder or single File") = vbYes)
    bNewSheet = (MsgBox("Extract all data to a single sheet (Yes)," & vbNewLine & "or a target file sheet for each source sheet (No)", vbYesNo, "Output Format: Single sheet or sheet by sheet collection") = vbYes)
    If Not bProcessFolder Then
        If Not bNewSheet Then
            MsgBox "There isn't much point creating a exact replica of your source file :)"
            Exit Sub
        End If
    End If

    'set default directory here if needed
    strDefaultFolder = "C:temp"

    'If the user is collating all the sheets to a single target sheet then the row spacing
    'to distinguish between different sheets can be set here
    lrowSpace = 1

    If bProcessFolder Then
        strFolderName = BrowseForFolder(strDefaultFolder)
        'Look for xls, xlsx, xlsm files
        strFileName = Dir(strFolderName & "*.xls*")
    Else
        strFileName = Application _
                      .GetOpenFilename("Select file to process (*.xls*), *.xls*")
    End If

    Set Wb1 = Workbooks.Add(1)
    Set ws1 = Wb1.Sheets(1)
    If Not bNewSheet Then ws1.Range("A1:B1") = Array("workbook name", "worksheet count")

    'Turn off screenupdating, events, alerts and set calculation to manual
    With Application
        .DisplayAlerts = False
        .EnableEvents = False
        .ScreenUpdating = False
        lngCalc = .Calculation
        .Calculation = xlCalculationManual
    End With

    'set path outside the loop
    StrPrefix = strFolderName & IIf(bProcessFolder, "", vbNullString)

    Do While Len(strFileName) > 0
        'Provide progress status to user
        Application.StatusBar = Left("Processing " & strFolderName & "" & strFileName, 255)
        'Open each workbook in the folder of interest
        Set Wb2 = Workbooks.Open(StrPrefix & strFileName)
        If Not bNewSheet Then
            'add summary details to first sheet
            ws1.Cells(Rows.Count, "A").End(xlUp).Offset(1, 0) = Wb2.Name
            ws1.Cells(Rows.Count, "A").End(xlUp).Offset(0, 1) = Wb2.Sheets.Count
        End If
        For Each ws2 In Wb2.Sheets
            If bNewSheet Then
                'All data to a single sheet
                'Skip importing target sheet data if the source sheet is blank
                Set rng2 = ws2.Cells.Find("*", ws2.[a1], xlValues, , xlByRows, xlPrevious)

                If Not rng2 Is Nothing Then
                    Set rng1 = ws1.Cells.Find("*", ws1.[a1], xlValues, , xlByRows, xlPrevious)
                    'Find the first blank row on the target sheet
                    If Not rng1 Is Nothing Then
                        Set rng3 = ws2.Range(ws2.UsedRange.Cells(1), ws2.Cells(rng2.Row, "A"))
                        'Ensure that the row area in the target sheet won't be exceeded
                        If rng3.Rows.Count + rng1.Row < Rows.Count Then
                            'Copy the data from the used range of each source sheet to the first blank row
                            'of the target sheet, using the starting column address from the source sheet being copied
                            ws2.UsedRange.Copy ws1.Cells(rng1.Row + 1 + lrowSpace, ws2.UsedRange.Cells(1).Column)
                        Else
                            MsgBox "Summary sheet size exceeded. Process stopped on " & vbNewLine & _
                                   "sheet: " & ws2.Name & vbNewLine & "of" & vbNewLine & "workbook: " & Wb2.Name
                            Wb2.Close False
                            Exit Do
                        End If
                        'colour the first of any spacer rows
                        If lrowSpace <> 0 Then ws1.Rows(rng1.Row + 1).Interior.Color = vbGreen
                    Else
                        'target sheet is empty so copy to first row
                        ws2.UsedRange.Copy ws1.Cells(1, ws2.UsedRange.Cells(1).Column)
                    End If
                End If
            Else
                'new target sheet for each source sheet
                ws2.Copy after:=Wb1.Sheets(Wb1.Sheets.Count)
                'Remove any links in our target sheet
                With Wb1.Sheets(Wb1.Sheets.Count).Cells
                    .Copy
                    .PasteSpecial xlPasteValues
                End With
                On Error Resume Next
                Wb1.Sheets(Wb1.Sheets.Count).Name = ws2.Name
                'sheet name already exists in target workbook
                If Err.Number <> 0 Then
                    'Add a number to the sheet name till a unique name is derived
                    Do
                        lSht = lSht + 1
                        Set ws3 = Wb1.Sheets(ws2.Name & " " & lSht)
                    Loop While Not ws3 Is Nothing
                    lSht = 0
                End If
                On Error GoTo 0
            End If
        Next ws2
        'Close the opened workbook
        Wb2.Close False
        'Check whether to force a DO loop exit if processing a single file
        If bProcessFolder = False Then Exit Do
        strFileName = Dir
    Loop

    'Remove any links if the user has used a target sheet
    If bNewSheet Then
        With ws1.UsedRange
            .Copy
            .Cells(1).PasteSpecial xlPasteValues
            .Cells(1).Activate
        End With
    Else
        'Format the summary sheet if the user has created separate target sheets
        ws1.Activate
        ws1.Range("A1:B1").Font.Bold = True
        ws1.Columns.AutoFit
    End If

    With Application
        .CutCopyMode = False
        .DisplayAlerts = True
        .EnableEvents = True
        .ScreenUpdating = True
        .Calculation = lngCalc
        .StatusBar = vbNullString
    End With
End Sub


Function BrowseForFolder(Optional OpenAt As Variant) As Variant
'From Ken Puls as used in his vbaexpress.com article
'http://www.vbaexpress.com/kb/getarticle.php?kb_id=284

    Dim ShellApp As Object
    'Create a file browser window at the default folder
    Set ShellApp = CreateObject("Shell.Application"). _
                   BrowseForFolder(0, "Please choose a folder", 0, OpenAt)

    'Set the folder to that selected.  (On error in case cancelled)
    On Error Resume Next
    BrowseForFolder = ShellApp.self.Path
    On Error GoTo 0

    'Destroy the Shell Application
    Set ShellApp = Nothing

    'Check for invalid or non-entries and send to the Invalid error
    'handler if found
    'Valid selections can begin L: (where L is a letter) or
    '\ (as in \servernamesharename.  All others are invalid
    Select Case Mid(BrowseForFolder, 2, 1)
    Case Is = ":"
        If Left(BrowseForFolder, 1) = ":" Then GoTo Invalid
    Case Is = ""
        If Not Left(BrowseForFolder, 1) = "" Then GoTo Invalid
    Case Else
        GoTo Invalid
    End Select

    Exit Function

Invalid:
    'If it was determined that the selection was invalid, set to False
    BrowseForFolder = False
End Function
 
Postado : 05/07/2016 7:25 am
(@doncelio)
Posts: 4
New Member
Topic starter
 

Resovido.

A sugestão do autor do código não funcionou para mim, então fiz a alteração abaixo.

......................................
Simplesmente troquei a linha:

ws2.UsedRange.Copy ws1.Cells(rng1.Row + 1 + lrowSpace, ws2.UsedRange.Cells(1).Column)

Pela linha:

ws2.UsedRange.Offset(1, 0).Copy ws1.Cells(rng1.Row + 1 + lrowSpace, ws2.UsedRange.Cells(1).Column)

......................................

Única coisa a ser feita era redimensionar a cópia: .Offset(1, 0)

Vou deixar aberto mais um dia caso alguém queira se manifestar, depois já poderá ficar como resolvido.

 
Postado : 05/07/2016 12:48 pm