Save new workbook with data from multiple sheets - excel

I am fairly new at using VBA to manipulate data in Excel. I am trying to build about 800 bills of materials from data I have extracted from an old system we are replacing.
I have an Excel workbook with multiple sheets with a table on each sheet. What I need to do is work through every "ParentID" in one sheet and save each unique "Parent ID" and "ChildID" to a new workbook titled with the ParentID. Then lookup each unique "ChildID" in another worksheet and save data from this worksheet to the new workbook for each unique "ChildID".
I found the below VBA code and have been working on changing things to work for me as I think this will get me at least part way there but I am having trouble getting all of the Template values replaced with my values due to lack of knowlege on syntax and VBA code.
If someone could help me identify what I need to replace to get at least this code working I think I would be well on my way.
Thanks!
Option Explicit
Sub ExportData()
'Declare variables
Dim ArrayItem As Long
Dim tblUsedIn As Worksheet
Dim ArrayOfUniqueValues As Variant
Dim SavePath As String
Dim ColumnHeadingInt As Long
Dim ColumnHeadingStr As String
Dim rng As Range
'Set the worksheet to
Set tblUsedIn = Sheets("tblUsedIn")
'Set the save path for the files created
SavePath = Range("File Save Location")
'Set variables for the column we want to separate data based on
ColumnHeadingInt = WorksheetFunction.Match(Range("ParentID").Value, Range("Data[#Headers]"), 0)
ColumnHeadingStr = "Data[[#All],[" & Range("ParentID").Value & "]]"
'Turn off screen updating to save runtime
Application.ScreenUpdating = False
'Create a temporary list of unique values from the column we want to
'separate our data based on
Range(ColumnHeadingStr & "").AdvancedFilter Action:=xlFilterCopy, _
CopyToRange:=Range("UniqueValues"), Unique:=True
'Sort our temporary list of unique values
tblUsedIn.Range("UniqueValues").EntireColumn.Sort Key1:=tblUsedIn.Range("UniqueValues").Offset(1, 0), _
Order1:=xlAscending, Header:=xlYes, OrderCustom:=1, MatchCase:=False, _
Orientation:=xlTopToBottom, DataOption1:=xlSortNormal
'Add unique field values into an array
'ArrayOfUniqueValues = Application.WorksheetFunction.Transpose(tblUsedIn.Range("IV2:IV" & RotblUsedIn.Count).SpecialCells(xlCellTypeConstants))
ArrayOfUniqueValues = Application.WorksheetFunction.Transpose(tblUsedIn.Range("UniqueValues").EntireColumn.SpecialCells(xlCellTypeConstants))
'Delete the temporary values
tblUsedIn.Range("UniqueValues").EntireColumn.Clear
'Loop through our array of unique field values, copy paste into new workbooks and save
For ArrayItem = 1 To UBound(ArrayOfUniqueValues)
tblUsedIn.ListObjects("Data").Range.AutoFilter Field:=ColumnHeadingInt, Criteria1:=ArrayOfUniqueValues(ArrayItem)
tblUsedIn.Range("Data[#All]").SpecialCells(xlCellTypeVisible).Copy
Workbooks.Add
Range("A1").PasteSpecial xlPasteAll
ActiveWorkbook.SaveAs SavePath & ArrayOfUniqueValues(ArrayItem) & Format(Now(), " YYYY-MM-DD hhmmss") & ".xlsx", 51
ActiveWorkbook.Close False
tblUsedIn.ListObjects("Data").Range.AutoFilter Field:=ColumnHeadingInt
Next ArrayItem
tblUsedIn.AutoFilterMode = False
MsgBox "Finished exporting!"
Application.ScreenUpdating = True
End Sub

Related

VBA keeps pasting number stores as texts

I'd like VBA to stop copy pasting my top headers with the green ribbon stating "numbers are stores as texts". Is there any way to get around it? I'm trying to copy and paste financial data from another excel sheet onto my main sheet. Here's the Code. Thank you in advance.
Sub God()
'Delete all data from the ranges
ThisWorkbook.Worksheets("10k I").Cells.Clear
ThisWorkbook.Worksheets("10k B").Cells.Clear
ThisWorkbook.Worksheets("10k C").Cells.Clear
ThisWorkbook.Worksheets("10q I").Cells.Clear
ThisWorkbook.Worksheets("10q B").Cells.Clear
ThisWorkbook.Worksheets("10q C").Cells.Clear
'Open workbook Income statement
Workbooks.Open "/Users/krabbysponge/Downloads/1.xls"
Workbooks.Open "/Users/krabbysponge/Downloads/2.xls"
Workbooks.Open "/Users/krabbysponge/Downloads/3.xls"
Workbooks.Open "/Users/krabbysponge/Downloads/4.xls"
Workbooks.Open "/Users/krabbysponge/Downloads/5.xls"
Workbooks.Open "/Users/krabbysponge/Downloads/6.xls"
'Copy range to another workbook
Workbooks("1.xls").Worksheets(1).Range("A1:M150").Copy _
ThisWorkbook.Worksheets("10k I").Range("A1")
Workbooks("2.xls").Worksheets(1).Range("A1:M150").Copy _
ThisWorkbook.Worksheets("10k B").Range("A1")
Workbooks("3.xls").Worksheets(1).Range("A1:M150").Copy _
ThisWorkbook.Worksheets("10k C").Range("A1")
Workbooks("4.xls").Worksheets(1).Range("A1:M150").Copy _
ThisWorkbook.Worksheets("10q I").Range("A1")
Workbooks("5.xls").Worksheets(1).Range("A1:M150").Copy _
ThisWorkbook.Worksheets("10q B").Range("A1")
Workbooks("6.xls").Worksheets(1).Range("A1:M150").Copy _
ThisWorkbook.Worksheets("10q C").Range("A1")
'Close workbook Income statement
Workbooks("1.xls").Close SaveChanges:=True
Workbooks("2.xls").Close SaveChanges:=True
Workbooks("3.xls").Close SaveChanges:=True
Workbooks("4.xls").Close SaveChanges:=True
Workbooks("5.xls").Close SaveChanges:=True
Workbooks("6.xls").Close SaveChanges:=True
End Sub
You don't need to copy - but can write the value direct to the target sheet.
This has two advantages:
you don't use the clipboard
therefore it is much faster
According to "Don't repeat yourself" (DRY) you can put everything in a for-loop - and configuring your files in an array. In case there are changes to this setting it is much easier to adapt the code.
Public Sub copyData()
Dim arrConfig(1, 2) As String
'Target sheetname | Source filename
arrConfig(0, 0) = "10k I": arrConfig(1, 0) = "1.xls"
arrConfig(0, 1) = "10k B": arrConfig(1, 1) = "2.xls"
arrConfig(0, 2) = "10k C": arrConfig(1, 2) = "3.xls"
'...
'arrconfig(0,6) = ...
Const pathDownloads As String = "/Users/krabbysponge/Downloads/"
Const AddressToCopy As String = "A1:M150"
Dim i As Long, wbSource As Workbook, wsSource As Worksheet, wsTarget As Worksheet
For i = 0 To UBound(arrConfig, 2)
Set wsTarget = ThisWorkbook.Worksheets(arrConfig(0, i))
wsTarget.Cells.Clear
Set wbSource = Workbooks.Open(pathDownloads & arrConfig(1, i))
Set wsSource = wbSource.Worksheets(1)
'This is the part where the data are written from one range to another (values only without formatting)
wsTarget.Range(AddressToCopy).Value = wsSource.Range(AddressToCopy).Value
wbSource.Close savechanges:=True
Next
End Sub

Extract specific columns into multiple .CSVs, error in code

I have an excel file where each column has varying products codes, descriptions, unit of measure, and 5 different prices (one for each distributor) in this order.
Workbook as it is:
I need to make 5 distinct .csv files, named after each distributor, each with code, discount (normally 0), one of the prices, MU and description, in this order.
What one of the CSVs should look like:
I can move columns around alright, the issues is that I can't seem to find a way to save the CSVs.
The code that I have (not mine) stops as it seems to be "unable to access the .csv" as it tries to save it.
Sub FornitoriToCSV()
Const FLDR = "C:\Users\HER-XV\Desktop" 'where to save files
Dim rng As Range, wb As Workbook, i As Long, rws As Long
Set rng = ActiveSheet.Range("A1").CurrentRegion 'data table
rws = rng.Rows.Count 'how many rows of data?
For i = 4 To rng.Columns.Count 'loop for each client column (starting at col4)
Set wb = Workbooks.Add 'add workbook
'copy data to workbook
With wb.Sheets(1)
.Range("A1").Resize(rws).Value = rng.Columns(1).Value
.Range("B1").Value = "Discount"
.Range("B2").Resize(rws - 1).Value = 0
.Range("C1").Resize(rws).Value = rng.Columns(i).Value 'client data
.Range("D1").Resize(rws).Value = rng.Columns(3).Value
.Range("E1").Resize(rws).Value = rng.Columns(2).Value
End With
'save the file using the client name
wb.SaveAs Filename:=FLDR & rng.Cells(1, i).Value & ".csv", _
FileFormat:=xlCSVUTF8, CreateBackup:=False
wb.Close False
Next i
End Sub
Any help would be much appreciated!

Problems with saving excel file after importing data via macro

I want to insert data from source files in my excel model by opening the files and copying and pasting the values. I am just updating the values and not inserting formulas, formats or images etc.
The macro works fine and the inputs are pasted in my excel model. The last command is: ActiveWorkbook.Save
However, sometimes the macro cannot save the file (and I cannot see a regularity here - sometimes it works, sometimes it doesn't) and it displays the error message: "Errors were detected while saving. Microsoft Excel may be able to save the file by removing or repairing some features. To make the repairs in a new file, click continue. To cancel saving the file, click cancel."
Does anyone have an idea on how to fix this error? I am also posting the full code below. Thanks a lot in advance!
' Definitions
Dim i As Integer
Dim mapping_sheet, Worksheet_MVP, Dateiname_Input, Name_Worksheet_Input, Pfad_Input, Pfad_Datei, Zelle, Text As String
' Workbooks
Dim MVP, Auszug As Workbook
Pfad_Input = ActiveSheet.Range("B7").Value
Set MVP = ActiveWorkbook
Sheets("Automatisierung Datenupdate").Activate
Workbooks(MVP.Name).Application.Calculation = xlCalculationManual
Workbooks(MVP.Name).Application.CalculateBeforeSave = False
' 1. Updating Macro
' Copy Pasting Data
If ActiveSheet.Range("E11").Value = "Ja" Then
Dateiname_Input = ActiveSheet.Range("M11").Value
Name_Worksheet_Input = ActiveSheet.Range("D11").Value
Worksheet_MVP = ActiveSheet.Range("B11").Value
Pfad_Datei = Pfad_Input & "\" & Dateiname_Input
Sheets(Worksheet_MVP).Activate
Range("B6:ZZ300").Select
Selection.ClearContents
Set Auszug = Workbooks.Open(Filename:=Pfad_Datei)
Workbooks(Auszug.Name).Activate
Sheets(Name_Worksheet_Input).Activate
Range("A4:ZY298").Select
Selection.Copy
Workbooks(MVP.Name).Activate
Sheets(Worksheet_MVP).Activate
Range("B6").Select
Selection.PasteSpecial Paste:=xlPasteValues
' Close and Save
Workbooks(Auszug.Name).Activate
ActiveSheet.Range("A1").Copy
Workbooks(Auszug.Name).Close savechanges:=False
Workbooks(MVP.Name).Activate
Sheets("Automatisierung Datenupdate").Activate
Range("M11").Select
Selection.Copy
Range("C11").Select
Selection.PasteSpecial Paste:=xlPasteValues
End If
' Save
Sheets("Automatisierung Datenupdate").Activate
Application.ScreenUpdating = True
ActiveWorkbook.Save
End Sub
You mix up the concepts in your code which probably leads to the unexpected/irregular errors.
If you don't specify a data type (or object type) when you dim, the variable is Variant by default. e.g. "Dim MVP" is the same as "Dim MVP as variant"
You do assign your workbooks/worksheets to a variable but don't use the magic. Once set you can just refer to the workbook by referencing the varName.
Although variables are very powerfull, when you just want to use cell values it's better to store these in memory (e.g. an array)
Hereunder an alternative approach, only using the named workbooks/worksheets and minimising the interactions with the sheet by using arrays:
Sub ceci()
'dim vars to specific datatype
Dim wb As Workbook, sh As Worksheet, arr
Set wb = ThisWorkbook
Set sh = wb.Sheets("Automatisierung Datenupdate")
'To minimize the interactions with the sheet we store the data in memory, an array
'here we can access each cell by referencing our array(<rowCounter>, <columnCounter>
'e.g. arr(j,i) => if j = 1 and i = 1 we'll have the values of Cell A1
'we can dump these values anywhere in the activesheet, other sheet, other workbook, ..
Dim sh2 As Worksheet, wb2 As Workbook
arr = sh.Range("A1").CurrentRegion.Value2 'assuming you have data as of A1 we store all in the array, you can fine tune if needed though
If arr(11, 5) = "ja" Then 'E11
'source wb
Set sh2 = wb.Sheets(arr(11, 2)) 'b11
sh2.Range("B6:ZZ300").ClearContents
'wb2 - by specifically naming the workbooks and sheets we avoid unexpected errors
Dim sh3 As Worksheet, arr2, Pfad_Datei As String: Pfad_Datei = wb.Path & "\" & arr(11, 13) 'arr(7, 2) & "\" & arr(11, 13) 'b7 & m11
Set wb2 = Workbooks.Open(Filename:=Pfad_Datei)
Set sh3 = wb2.Sheets(arr(11, 4)) 'd11
arr2 = sh3.Range("A4:ZY298").Value2
sh2.Range(sh2.Cells(6, 2), sh2.Cells(UBound(arr2), UBound(arr2, 2))).Value2 = arr2 'dumb to sheet
'wb1
sh.Range("c11").Value = arr(11, 13) 'm11
End If
wb.Save
End Sub

VBA: How to choose files from a folder based on a precondition loop in same sub?

I am very new to VBA and I am trying to automate a dashboard process for my team. The dashboard takes two raw data files, I call these raw data and OP. I created a sheet with macro where I paste the raw data and run the macro by help of button. It filters data based on column 'Market', and there are some 14 markets, start the loop and repeat for all 14. So raw data is one big file with all markets and OP are 14 specific to markets, these are too heavy files.
What I am trying to do is to pick the OP file from the folder for same market as that which the loop picking in raw data file. So trying to get 14 output files for each market.
I looked everywhere no solution. Any help will be a great help.
'Initialize Values
Dim wb_RawData As String
Dim wb_MasterData As String
Dim wb_Control As String
Dim wb_OP As String
Dim Tempsheet As String
Dim MarketArray As Variant
Dim MarketColumn As Integer
Dim Lastrow As Long
Dim ArrayLength As Integer
Dim Workbook As String
Dim StartTime As Double
Dim EndTime As Double
'Loop start time
StartTime = Now
wb_RawData = "RawData"
wb_MasterData = "MasterData"
wb_Control = "Control"
wb_OP = "OP"
MarketColumn = 5
Workbook = Sheets(wb_MasterData).Cells(22, 4)
Sheets(wb_RawData).Activate
Sheets(wb_RawData).Cells(104, MarketColumn).Select
Selection.End(xlUp).Select
Lastrow = ActiveCell.row
Sheets(wb_RawData).Range(Cells(2, MarketColumn), Cells(Lastrow, MarketColumn)).Select
If Not Selection Is Nothing Then
For Each Cell In Selection
If (Cell <> "") And (InStr(Temp_Value, Cell) = 0) Then
Temp_Value = Temp_Vaue & Cell & "|"
End If
Next Cell
End If
If Len(Temp_Value) > 0 Then Temp_Value = left(Temp_Value, Len(Temp_Value) - 1)
MarketArray = Split(Temp_Value, "|")
'Loop through every MarketCode
ArrayLength = UBound(MarketArray)
Application.ScreenUpdating = False
And then it copy and past the raw data into raw data sheet.
The below code is manually picking the OP file, and copy pasting in the main dashboard. But I want to automate this process.
'opening the raw data order profile dashboard
Workbooks.Open Filename:=Path & FIE_OP_RawData
'selecting the section
Range("A1:I1").Select
Range(Selection, Selection.End(xlToRight)).Select
Range(Selection, Selection.End(xlDown)).Select
Selection.Copy
'pasting in new dashboard
Windows(FIE_Model_Dashboard).Activate
Sheets(OrderProfiles_Sheet_NewDashboard).Select
Range("a1").Select
ActiveSheet.Paste
'Save Dashboard
ChDir _
Location_folder
ActiveWorkbook.SaveAs Filename:= _
Location_folder & "Dashboard - Market " & MarketArray(i) & " - " &
Run_Period & ".xlsb" _
, FileFormat:=xlExcel12, CreateBackup:=False
'Close Dashboard
ActiveWindow.Close
I wish to select same market specific OP file from folder that the loop picked from the raw data.

Copying base data sheet along with selected sheets from source workbook to new workbook

I am looking at building a master workbook which receives a monthly dump of data for all Cost Centres which will then populate a large number of worksheets within the workbook, and which then need to be split off and sent out to service heads. A service head will receive a selection of worksheets based on the first 4 characters of the sheet name (although this may change in due course).
eg 1234x, 1234y, 5678a, 5678b will produce two new workbooks named 1234 and 5678 with two sheets in each.
I have cobbled some code from various forum to create a macro that will work through a hard coded array defining the service head 4 character codes and create a series of new workbooks. And which seems to work.
However.. I also need to include the main data dump sheet within the source file (called "data") with the the array of files being copied over so that the links remain with the data sheet being copied over. If I write a line to copy over the data sheet separately, the new workbook still refers back to the source file, which service heads do not have access to.
So main question is: how can I add the "data" tab into the Sheets(CopyNames).Copy code so it is copied over with all the other files in the array at the same to keep the links intact?
Second question is if I decide it is the first two characters of the worksheet define the sheets that relate to a service head, how do I tweak the split/mid line of code - I've trialled around but am getting tied up in knots!
Any other tips to make the code more elegant much appreciated (there may be quite a long list of service head codes and I am sure there is a better way of creating a list for the routine to loop through)
Sub Copy_Sheets()
Dim strNames As String, strWSName As String
Dim arrNames, CopyNames
Dim wbAct As Workbook
Dim i As Long
Dim arrlist As Object
Set arrlist = CreateObject("system.collections.arraylist")
arrlist.Add "1234"
arrlist.Add "5678"
Set wbAct = ActiveWorkbook
For Each Item In arrlist
For i = 1 To Sheets.Count
strNames = strNames & "," & Sheets(i).Name
Next i
arrNames = Split(Mid(strNames, 2), ",")
'strWSName =("1234")
strWSName = Item
Application.ScreenUpdating = False
CopyNames = Filter(arrNames, strWSName, True, vbTextCompare)
If UBound(CopyNames) > -1 Then
Sheets(CopyNames).Copy
ActiveWorkbook.SaveAs Filename:=strWSName & " " & Format(Now, "dd-mmm-yy h-mm-ss")
ActiveWorkbook.Close
wbAct.Activate
Else
MsgBox "No sheets found: " & strWSName
End If
Next Item
Application.ScreenUpdating = True
End Sub
Option Explicit
Sub CopySheets()
With ThisWorkbook
Dim SheetIndex As Long
Dim ValidSheetNames() As String
ReDim ValidSheetNames(1 To .Worksheets.Count)
' Build a 1 dimensional array called ValidSheetNames, which contains every sheet in the master workbook other than DEDICATEDSHEET. '
Dim ws As Worksheet
For Each ws In .Worksheets
If ws.Name <> "DEDICATEDSHEET" Then
SheetIndex = SheetIndex + 1
ValidSheetNames(SheetIndex) = ws.Name
End If
Next ws
ReDim Preserve ValidSheetNames(1 To SheetIndex)
' Read all ServiceCodes into a 1-dimensional array '
Dim ServiceHeadCodes As Variant
ServiceHeadCodes = Application.Transpose(.Worksheets("DEDICATEDSHEET").Range("CCLIST[CC]").Value2)
Dim CodeIndex As Long
' Now loop through each ServiceHeadCode '
For CodeIndex = LBound(ServiceHeadCodes) To UBound(ServiceHeadCodes)
' Put all sheet names which contain the current ServiceHeadCode into an array called SheetsToCopy '
Dim SheetsToCopy() As String
SheetsToCopy = Filter(ValidSheetNames, ServiceHeadCodes(CodeIndex), True, vbTextCompare)
' Check if SheetToCopy now contains any sheet names at all. '
If UBound(SheetsToCopy) > -1 Then
' Add the name of the Data sheet to the end of the array '
ReDim Preserve SheetsToCopy(LBound(SheetsToCopy) To (UBound(SheetsToCopy) + 1))
SheetsToCopy(UBound(SheetsToCopy)) = "Data"
Dim OutputWorkbook As Workbook
Set OutputWorkbook = Application.Workbooks.Add
' Copy all sheets which are in SheetToCopy array to newly created OutputWorkbook '
.Worksheets(SheetsToCopy).Copy OutputWorkbook.Worksheets(1)
' Delete the default Sheet1, which should be at the end as copied sheets were inserted before it. '
' But suppress the Are you sure you want to delete this sheet.. message. '
Application.DisplayAlerts = False
OutputWorkbook.Worksheets(OutputWorkbook.Worksheets.Count).Delete
Application.DisplayAlerts = True
' Re-enable alerts, as we want to see any other dialogue boxes/messages
' Not providing a full directory path below means OutputWorkbook will be saved wherever Thisworkbook is saved.'
OutputWorkbook.SaveAs Filename:=ServiceHeadCodes(CodeIndex) & " " & Format(Now, "dd-mmm-yy h-mm-ss") & ".xlsx", FileFormat:=51
OutputWorkbook.Close
Else
MsgBox "No sheets found: " & ServiceHeadCodes(CodeIndex)
End If
Next CodeIndex
End With
End Sub
Untested and written on mobile, sorry for bad formatting.
This approach proposes that you store all service head codes in a 1-column Excel table on a dedicated sheet that is referred to via Excel table nomenclature (which might be easier than ArrayList.Add for each new service head code).
I assume code is stored in master workbook ('thisworkbook'), which might not be true.
You could modify the serviceheadcodes table directly on the spreadsheet itself, if you later decide that SheetsToCopy will be determined by first 2, 3 or X characters -- or you could modify array itself with left$() function.
Hope it works or gives you some ideas.
Edit: This is my sheet and table layout (which I assume matches yours).
And this is what the code above gives me on my computer.

Resources