I have a budgeting worksheet that contains multiple sheets and formulas. A user can enter his data into the ledger, and various financial scenarios are automatically calculated. The ledger contains some cells that contain formulas, and may be over-written by the user. That is fine since the worksheet is projecting month per month, and must be updated with actual entries to keep the projections accurate.
Occasionally I update/upgrade the budgeting worksheet, and want the user to be able to export his data from the old worksheet and import it to the new worksheet. I have created a macro that exports the data for certain ranges. I have also created a macro that imports the data range to the new worksheet.
However, my problem is that the macro to export also converts predetermined calculations from their original formula (=A1+B2, for example) to whatever the result is for that formula ($1,200, for example). This causes problems for future projections in other data ranges since the formula is now replaced with a static number that cannot change based upon other monthly deposits/withdrawals.
I have tried exporting the data minus any cells that contain formulas but have been unsuccessful. I have attached my working export code (as I have many sheets and ranges, I have only posted the minimum to show what I have working). I have also attached the code I used for ignoring cells with formulas (inspired by this post Excel VBA Copy / Paste macro: Ignore cells with Formulas).
Any help is greatly appreciated. As is surely obvious, I am new to VBA and know next to nothing about it!
WORKING EXPORT CODE:
Sub GenerateData()
Dim strFile As String
'New workbook with 3 sheets
Workbooks.Add xlWBATWorksheet
ActiveSheet.Name = "Financial Info"
Sheets.Add(After:=Sheets(1)).Name = "HELOC"
Sheets.Add(After:=Sheets(2)).Name = "Accelerated Mortgage"
Sheets.Add(After:=Sheets(3)).Name = "Accelerated 2nd Loan"
ActiveWorkbook.Sheets("Financial Info").Range("G6:G8").Value = ThisWorkbook.Sheets("Financial Info").Range("G6:G8").Value
ActiveWorkbook.Sheets("Financial Info").Range("G11:G13").Value = ThisWorkbook.Sheets("Financial Info").Range("G11:G13").Value
ActiveWorkbook.Sheets("HELOC").Range("D13:F74").Value = ThisWorkbook.Sheets("HELOC").Range("D13:F74").Value
ActiveWorkbook.Sheets("HELOC").Range("D86:F147").Value = ThisWorkbook.Sheets("HELOC").Range("D86:F147").Value
End Sub
NON-WORKING:IGNORE CELLS WITH FORMULA
Sub example()
Dim source As Range
Dim target As Range
Set source = ActiveWorkbook.Sheets("HELOC").Range("D13:F877")
Set target = ThisWorkbook.Sheets("HELOC").Range("D13:F877")
copy_non_formulas source:=source, target:=target
copy_non_formulas source:=Range("D13:F74"), target:=Range("D13:F74")
copy_non_formulas source:=Range("D86:F147"), target:=Range("D86:F147")
End Sub
Public Sub copy_non_formulas(source As Range, target As Range)
'Assumes that all formulas start with '=' and all non formulas do not
Dim i As Long
Dim j As Long
Dim c As Range
For i = 1 To source.Rows.Count
For j = 1 To source.Columns.Count
Set c = source(RowIndex:=i, ColumnIndex:=j)
If Left(c.Formula, 1) <> "=" Then
target(RowIndex:=i, ColumnIndex:=j).Value = c.Value
End If
Next j
Next i
End Sub
Here is a simple subroutine which moves only constant values from Sheet1 to a new workbook.
It should be simple to edit this to your requirements, but let me know if you have any problems.
Sub CopyWithoutFormulas()
Dim newWorkbook As Workbook
Set newWorkbook = Workbooks.Add
Dim formulas As Range
On Error Resume Next
Set formulas = Sheet1.UsedRange.SpecialCells(xlCellTypeFormulas)
On Error GoTo 0
If formulas Is Nothing Then
'no formulas so move all values across in one batch
newWorkbook.Worksheets(1).Range(Sheet1.UsedRange.Address).Value = Sheet1.UsedRange.Value
Else
'formulas found so only move constants across
Dim r As Range
For Each r In Sheet1.UsedRange
If Intersect(r, formulas) Is Nothing Then
newWorkbook.Worksheets(1).Range(r.Address).Value = r.Value
End If
Next
End If
End Sub
I tried your code and it seems to work just fine. How does it fail, on what data?
You are in fact copying values from a range and pasting them to the same range, so there is no way of knowing whether it worked. Try to update these lines, set source and target to different ranges. For example, instead
copy_non_formulas source:=Range("D13:F74"), target:=Range("D13:F74")
copy_non_formulas source:=Range("D86:F147"), target:=Range("D86:F147")
you could try
copy_non_formulas source:=Sheets(1).Range("D13:F74"), target:=Sheets(2).Range("D13:F74")
copy_non_formulas source:=Sheets(1).Range("D86:F147"), target:=Sheets(2).Range("D86:F147")
I have tried several suggestions from this site, as well others, but since I am not really sure how to write code in the first place, I am getting nowhere. For the record, the code I posted as "working" I tweaked from an online source and it does exactly what I want...except it copies cells with formulas. The "working" code creates a new workbook with the same tabs, and exports the data to the same respective cells. Now if only I can edit my existing "working" code to eliminate the copying of cells with formulas? For reference here is the full "working" code minus all the sheets/data ranges.
Sub GenerateData()
Dim strFile As String
'New workbook with 3 sheets
Workbooks.Add xlWBATWorksheet
ActiveSheet.Name = "Financial Info"
Sheets.Add(After:=Sheets(1)).Name = "HELOC"
Sheets.Add(After:=Sheets(2)).Name = "Accelerated Mortgage"
Sheets.Add(After:=Sheets(3)).Name = "Accelerated 2nd Loan"
ActiveWorkbook.Sheets("Financial Info").Range("G6:G8").Value = ThisWorkbook.Sheets("Financial Info").Range("G6:G8").Value
ActiveWorkbook.Sheets("Financial Info").Range("G11:G13").Value = ThisWorkbook.Sheets("Financial Info").Range("G11:G13").Value
ActiveWorkbook.Sheets("HELOC").Range("D13:F74").Value = ThisWorkbook.Sheets("HELOC").Range("D13:F74").Value
ActiveWorkbook.Sheets("HELOC").Range("D86:F147").Value = ThisWorkbook.Sheets("HELOC").Range("D86:F147").Value
ActiveWorkbook.SaveAs "Exported Data.xlsx"
End Sub
Related
I have a spreadsheet that operators input data in, with the A column being the date, and the data is input by row. The A column is a formula that adds +1 to the date in the previous cell, going all the way down recursively to auto-populate the date as the sheet is filled out.
I have to have a report printed out at the end of every day, and I am trying to use VBA to filter the rows out by a date that the operator inputs on another sheet in cell B2. I need the macro to grab that date value, and pass it as a variable to the filter in order to pull the 12 rows of that date and paste it into a new sheet. Unfortunately, the value it pulls is not being passed, and when I put a MsgBox command in there, it shows it's pulling 12:00 AM and not a date. When using the Date variable, it also breaks the filter on the bottom macro below (trying 2 different versions just to get this working).
I'm not good with VBA, so my macros were pulled from example websites and I tailored them to what I need.
This is one macro I have tried:
Sub For_RangeCopy()
Dim rDate As Date
Dim rSheet As Worksheet
Set rSheet = ThisWorkbook.Worksheets("EOS")
rDate = CDate(rSheet.Range("B2").Value)
MsgBox (rDate)
' Get the worksheets
Dim shRead As Worksheet
Set shRead = ThisWorkbook.Worksheets("Bi-Hourly Report")
Dim shWrite As Worksheet
Set shWrite = ThisWorkbook.Worksheets("Report")
' Get the range
Dim rg As Range
Set rg = shRead.Range("A1").CurrentRegion
With shWrite
' Clear the data in output worksheet
.Cells.ClearContents
' Set the cell formats
'.Columns(1).NumberFormat = "dd/mm/yyyy"
'.Columns(3).NumberFormat = "$#,##0;[Red]$#,##0"
'.Columns(4).NumberFormat = "0"
'.Columns(5).NumberFormat = "$#,##0;[Red]$#,##0"
End With
' Read through the data
Dim i As Long, row As Long
row = 1
For i = 1 To rg.Rows.Count
If rg.Cells(i, 1).Value2 = rDate Or i = 1 Then
' Copy using Range.Copy
rg.Rows(i).Copy
shWrite.Range("A" & row).PasteSpecial xlPasteValues
' move to the next output row
row = row + 1
End If
Next i
End Sub
And here is another Macro I have tried to use. This one actually gives me the 3 header rows which I don't need, but I don't mind, this paste is a reference for the report layout anyway, so the operators won't see this sheet. But this macro does give me the first block of the date range: 1/1/2023. I do know that the "rgCriteria As String" is likely incorrect, but that is how I get anything useful from this macro. If I change that rgCriteria to a Date, it breaks the rgData.AdvancedFilter command, and I haven't learned enough VBA to know why. And my boss wants this done today, although here I am posting here, thus it's not getting done today.
Sub AdvancedFilterExample()
' Get the worksheets
Dim rSheet As Worksheet
Set rSheet = ThisWorkbook.Worksheets("EOS")
Dim shRead As Worksheet, shWrite As Worksheet
Set shRead = ThisWorkbook.Worksheets("Bi-Hourly Report")
Set shWrite = ThisWorkbook.Worksheets("Report")
' Clear any existing data
shWrite.Cells.Clear
' Remove the any existing filters
If shRead.FilterMode = True Then
shRead.ShowAllData
End If
' Get the source data range
Dim rgData As Range, rgCriteria As String
Set rgData = shRead.Range("A1").CurrentRegion
' IMPORTANT: Do not have any blank rows in the criteria range
'Set rgCriteria = rSheet.Range("B2")
rgCriteria = rSheet.Range("B2").Value
MsgBox (rgCriteria)
' Apply the filter
rgData.AdvancedFilter Action:=xlFilterCopy, CriteriaRange:=rgCriteria _
, CopyToRange:=shWrite.Range("A1")
End Sub
I don't know which method of filtering and pasting is best for my situation, but I do know that the faster is better. I'm copying entire rows, and it needs to be efficient because this log contains a lot of data. I only need one of these macros to work, but I will be heavily modifying them and chaining them together with about 5 other filter/copy/paste sequences to follow, along with printOut commands after that, and finalized by clearing the sheets it pastes to, and then re-enabling all the functionality of the sheet (calculations, displaystatusbar, events, and screenupdating) all to make it quicker while the macro is running. All of these reports will be run using the macro with a button click.
Any thoughts or suggestions would be greatly appreciated. I've been struggling with this for a couple of weeks now. I'm at a loss and turning to the community that has helped me with a TON of questions over the past 20 or so years just by a Google search!
Other information:
I'm using Office 365 on a Windows 10/11 machine. The headers of the sheet it filters does contain merged cells as the header is rows 1-3, there is a lot of data in this sheet that grows through the year. 12 rows per day for an entire year. These macros are written in a Module aptly named "Module 1" if that helps. I do have this workbook, and the original log saved on OneDrive that can be shared.
When using Advanced Filter your criteria range should have headers which match your data table.
Sub AdvancedFilterExample()
Dim rSheet As Worksheet, shRead As Worksheet, shWrite As Worksheet
Dim rgData As Range, rgCriteria As Range
Set rSheet = ThisWorkbook.Worksheets("EOS")
Set shRead = ThisWorkbook.Worksheets("Bi-Hourly Report")
Set shWrite = ThisWorkbook.Worksheets("Report")
Set rgData = shRead.Range("A1").CurrentRegion 'source data range
'## criteria range needs to include a matching date header...
Set rgCriteria = rSheet.Range("B3:B4") 'eg. "Date" in B3, date value in B4
shWrite.Cells.Clear ' Clear any existing data
If shRead.FilterMode = True Then shRead.ShowAllData ' Remove the any existing filters
rgData.AdvancedFilter Action:=xlFilterCopy, _
CriteriaRange:=rgCriteria, _
CopyToRange:=shWrite.Range("A1")
End Sub
I have a template file, whereby multiple copies of the template (5 to 20) are completed, then consolidated into one master file. This file contains multiple sheets (18) and hundreds of entries in each sheet.
Not every cell is a number requiring consolidation, i.e there are titles and dates etc which are repeated, with no clear structure. Re-organising the template is not an option. The best way is to calculate only cells with a "Currency" type.
Here is the code I have so far. I have pieced it together from a number of searches, but will admit that I don't fully understand vba so I know I'm missing something obvious.
To make this a bit simpler, I have closed everything else and opened the target files manually before running this, then called upon the open excel files. Improving this bit of the code so that I can just put the target files into a sub folder and then use that path would be nice but not necessary.
I do not need to change the cell colour at the end, but I just did that as a quick visual reference to see whether it had ran.
The script in this format does not throw up any errors, but simply runs without actually calculating and populating the total value into each cell. I would guess that my approach of trying to extract the sheet and cell reference for every loop is wrong, but I cannot find the answer anywhere, so any help would be greatly appreciated.
totalValue = totalValue + Worksheets(sht).Cells(R, C)
is completely the wrong syntax, and the crux of the issue, but I simply don't know how to fix this within the context of the two existing For Each loops.
Sub ConsolidatedFigures()
Application.ScreenUpdating = False
'Declare variable types
Dim ws As Worksheet
Dim totalValue As Double
Dim C As Integer
Dim R As Integer
Dim wb As Workbook
Dim sht As String
Dim myRange As Range
Dim cell As Range
'Start to loop through each sheet in the workbook
For Each ws In ThisWorkbook.Worksheets
'get the current sheet name to later search for the same sheet in other workbooks
sht = ws.Name
'set the range in the current sheet
Set myRange = ws.UsedRange
'Now loop though every cell in the range
For Each cell In myRange
'Only target cells with an "currency" style so we're not trying to sum text
If cell.Style = "Currency" Then
'get the current cell references to later search for the same cell in other workbooks
C = cell.Column
R = cell.Row
'Within the current cell, go through open workbooks (except this one) to create a total of all the corresponding cells
For Each wb In Application.Workbooks
'the target workbooks will be open already, and nothing else. Disregard this open workbook and personal book
If wb.Name <> "PERSONAL.xlsb" AND wb.Name <> "consolidation.xlsm" Then
totalValue = totalValue + Worksheets(sht).Cells(R, C)
End If
Next wb
'populate the cell with the total of the corresponding cells
cell.Value = totalValue
'change cell colour to show the above step worked
cell.Interior.ColorIndex = 5
End If
'move on to next cell in worksheet
Next cell
'move on to next sheet in the workbook
Next ws
Application.ScreenUpdating = True
End Sub
I have created a tool in excel which can take two spreadsheets, and copy the content from one to another when we do an update on the sheet itself.
The tool is purely designed to be a copy / paste tool, the current code will copy and paste values from sheet to sheet.
I have to include logic into the tool to skip cells with formulas, if the tool copies and pastes the formulas which are currently in the source sheet to the target sheet, they no longer match and throw #REF errors. Any suggestions on how to put a for loop in here or something similar to allow it to check and ignore cells with formulas? I need it only to copy / paste cells with numbers or values.
Sub CopyWorkbook()
Dim wb1 As Workbook, wb2 As Workbook
wb1.Sheets("Capex").Range("H1124:AT1173").Copy
wb2.Sheets("Capex").Range("H529:AT578").PasteSpecial Paste:=xlPasteAll
wb1.Sheets("Capex").Range("H1275:AT1284").Copy
wb2.Sheets("Capex").Range("H580:AT589").PasteSpecial Paste:=xlPasteAll
Rather than loop cell by cell you can use SpecialCells to identify the formulae
There are two options
Copy only the Constant cells to the destination
Remove any Formula cells from the destination
If your formulae occur in a single contigious block then (1) works easily, else this will result in a number of areas needing to copied over. So (2) is preferable
Your first range can be covered as so.
Dim rng1 As Range
Set rng1 = Range("H1124:AT1173")
rng1.Copy [h1275]
On Error Resume Next
[h1275].Resize(rng1.Rows.Count, rng1.Columns.Count).SpecialCells(xlFormulas) = vbNullString
On Error GoTo 0
If you really want to skip cells containing formulas, you could use this example as a start.
The code assumes that only formulas start with an equals sign.
Edit: expanding the example with the ranges in the question.
Sub example()
Dim source As Range
Dim target As Range
Set source = ActiveSheet.Range("A1:B6")
Set target = ActiveSheet.Range("D1:E6")
copy_non_formulas source:=source, target:=target
'Extended example using the ranges posted in the question
'For the sake of formatting, I omitted the fully qualified
'range names.
copy_non_formulas source:=Range("H1124:AT1173"), target:=Range("H529:AT578")
copy_non_formulas source:=Range("H1275:AT1284"), target:=Range("H580:AT589")
End Sub
Public Sub copy_non_formulas(source As Range, target As Range)
'Assumes that all formulas start with '=' and all non formulas do not
Dim i As Long
Dim j As Long
Dim c As Range
For i = 1 To source.Rows.Count
For j = 1 To source.Columns.Count
Set c = source(RowIndex:=i, ColumnIndex:=j)
If Left(c.Formula, 1) <> "=" Then
target(RowIndex:=i, ColumnIndex:=j).Value = c.Value
End If
Next j
Next i
End Sub
Can't you just use Paste:=xlPasteValues for all cells? That way no formulas get copied to the target sheet.
Currently I have spreadsheets coming in that are formatted incorrectly. Our client sent out to his suppliers an old spreadsheet where columns are laid out differently than what they are currently setup as. Normally we would tell them to correct it, but some of these spreadsheets have over 220k rows and 33 columns. They're updating it for the future, but asking them to have their clients redo their tables is a no-go. I've written a script that will copy a column, and place it into the corresponding static column in another workbook. This works okay but I feel there is more that could be done.
Name of open workbook copying from varies.
Name of workbook copied to: C:\User\(Name)\UCOR\Catalogs\PSX-Toolset v1.503-EN.xls
What I would like is help writing a macro that will do the following from open workbook:
1.) Select an entire column minus Row 1 to the first blank row. - This goes from B to AH
2.) Paste that column into PSX-Toolset workbook, worksheet name "Item Data" - Static Assigned Columns
3.) Perform a Save As on PSX-Toolset as (Catalog-PSX-<Workbook Copied From>.xls)
Lastly, I'd like to know if it's possible to do the above, but mapping heading cells. Unfortunately the cell names are not identical.
Untested:
Sub MapAndCopyColumns()
Dim i As Integer, rng As Range
Dim shtSrc As Worksheet, wbDest As Workbook
Dim shtDest As Worksheet
Dim iNew
Set shtSrc = ActiveSheet
Set wbDest = Workbooks.Open("C:\User\(Name)\UCOR\Catalogs\PSX-Toolset v1.503-EN.xls")
Set shtDest = wbDest.Sheets("Item Data")
For i = 2 To 34
Set rng = shtSrc.Cells(2, i)
If rng.Value <> "" Then
If rng.Offset(1, 0).Value <> "" Then
Set rng = Range(rng, rng.End(xlDown))
End If
'map old position >> new position
' mapping table has 2 columns of numbers: "old" and "new"
iNew = Application.VLookup(i, _
ThisWorkbook.Sheets("Mapping").Range("A2:B40"), 2, False)
If Not IsError(iNew) Then
'copy if the column has an entry in the mapping table
rng.Copy shtDest.Cells(2, iNew)
End If
End If
Next i
wbDest.SaveAs "C:\wheretosaveto\Catalog-PSX-" & shtSrc.Parent.Name
End Sub
How I learned most of my vba is through 'record macro'. You start recording, do what you want to do yourself, stop recording and then look at the generated code.
Usually you can improve the code by eliminating a lot of redundant lines, but it should at least expose all the commands you need to complete your goal.
I'm tyring to look for a way to return a range of cells with just the lookup function. Is this really possible?
Basically like this:
=LOOKUP([MasterBook.xlsm]Sheet3!$A:$A,?)
I just want the function to look in the main workbook through all of Column A and return all cells for Column A that have something in them.
EDIT for poster below:
Sure. I have two workbooks; one workbook is essentially a local product that has a "master" sheet on top and then individual worksheets following it that have all of their information extracted from the master sheet. The second workbook is a local copy of a product that I send to a non-local entity higher up the food chain. Basically I need to pull information from the individual sheets in my first workbook and put it in the appropriate columns in the second workbook. I have a macro that gets the info from my sheets in the one workbook over to the other, but the second workbook is formatted differently. I was looking for a way to use a formula if possible.
The macro I am referring to is:
Sub CopyTest()
Dim sourceColumn As Range, targetColumn As Range
Set sourceColumn = Workbooks("Local Workbook.xlsm").Worksheets("Sheet3").Columns("A")
Set targetColumn = Workbooks("Nonlocal Workbook.xlsm").Worksheets("Sheet1").Columns("A")
sourceColumn.Copy Destination:=targetColumn
End Sub
All this does is pull the specified column from one sheet and put it in the column on the second book; but it just pastes it starting in the first block. Since the non-local book is formatted differently, the column I need to transfer to doesn't start until Row 9. shrug I have ideas abuot what I'm trying to do with this, but my ideas tend to exceed my technical ability (which occasionally makes it difficult to explain). :)
Depending on how different your workbooks are formatted. Here is two way to handle this:
Adapt your macro
Instead of copying the whole column, you can copy paste, only the values you want to.
Here is an example:
Sub CopyTest()
Dim rSource As Range, rTarget As Range
Dim lEnd As Long
lEnd = Range("A65536").End(xlUp).Row
Set rSource = Workbooks("Local Workbook.xlsm").Worksheets("Sheet3").Range("A1:A" & lEnd)
Set rTarget = Workbooks("Nonlocal Workbook.xlsm").Worksheets("Sheet1").Range("A9")
rSource.Copy Destination:=rTarget
End Sub
Use a formula
If your data are not in the same order, you'd better use a VLOOKUP formula.
See how it works.
Don't hesitate to post another question with what you've built for some help. Please give as much details as possible so we could help you the best way.
[EDIT] Another try following the comments
Option Explicit
Dim wTarget As Workbook
Sub mainCopy()
Dim bGo As Boolean
bGo = True
'Add a new workbook to copy the data - do you want the user to select one?
Set wTarget = Application.Workbooks.Add()
Do While bGo
CopyTest
bGo = MsgBox("Do you want to import data from another workbook?", vbYesNo, "Continue?")
Loop
End Sub
Sub CopyTest()
Dim rSource As Range, rTarget As Range
Dim lEnd As Long, lCol As Long
Dim ws As Worksheet
Dim vFile As Variant
vFile = Application.GetOpenFilename("Excel-files,*.xls", _
1, "Select One File To Open", , False)
'if the user didn't select a file, exit sub
If TypeName(vFile) = "Boolean" Then Exit Sub
Workbooks.Open vFile
For Each ws In ActiveWorkbook.Worksheets
'do you need to copy the columns separately?
' For lCol = 1 To 10
'find the last cell of the 10th column
lEnd = ws.Cells(65536, 10).End(xlUp).Row
Set rSource = ws.Range("A1:J" & lEnd)
'How can we define the target worksheet?
Set rTarget = wTarget.Worksheets("Sheet1").Range("A9")
rSource.Copy Destination:=rTarget
' Next lCol
Next ws
End Sub