Issue with VBA script running between .xlsm and .xlsb formats - excel

Problem:
Because the .xlsb (herein referred to as TheirFile.xlsb) is downloaded from a third party twice a week, the VBA script must be held by the .xlsm (MyFile.xlsm).
The process is simple:
Focus TheirFile.xlsb
Apply some filters on the data (headers at row 3)
Select the cell AW2 and copy the value (it contains a formula, this may be where the issue occurs)
Focus MyFile.xlsm
Select the cell J28 and paste with ...pastespecial xlPasteAll
However upon checking the code and hitting run, I get nothing in the cell.
Attempts:
Most of them are almost identical, and with the way I (attempt to) bug fix I honestly don't recall all of them.
I used variations on
Workbooks("TheirFile.xlsb").Activate
Range("AW2").Copy
to copy it without error, and
Workbooks("MyFile.xlsm").Activate
Range("j28").Select
ActiveCell.PasteSpecial xlPasteAll
to paste. Something here is failing without returning an error.
I have tried several ways of copying, even a clunky
Range("AW2").Select
x = Selection.Value
Workbooks("MyFile.xlsm").Activate
Range("j28").Select
Selection.Value = x
Which of course didn't work.
Current code and additional info:
Currently I have this in place
Option Explicit
Sub MyMacro() 'indentations for readability
Workbooks("TheirFile.xlsb").Activate
If ActiveSheet.FilterMode Then
ActiveSheet.ShowAllData
End If
'various filters
Dim x As Integer
Workbooks("TheirFile.xlsb").Activate
Range("AW2").Copy
Workbooks("MyFile.xlsm").Activate
Range("j28").Select
ActiveCell.PasteSpecial xlPasteAll
End Sub
The cell AW2 has a =SUBTOTAL(9,cell:cell) formula in it.
The cell j28 is a merged cell.
Questions:
Is there a quick fix for my code?
Would it be more effective to simply perform the subtotal again inside of VBA to avoid the issue?

FIXED
sheets("sheet1").range("AW2").copy
workbooks("MyFile.xlsm").activate
Range("j28").select
activecell.pastespecial xlPasteValues
range("").select appears not to work if the range is in a binary workbook but the macro is in a macro-enabled workbook.

You can/should avoid any uneccessary select/active (also copy/paste is not needed to just transfer values)
Option Explicit
Sub MyMacro()
Dim wsSrc as worksheet
Set wsSrc = workbooks("TheirFile.xlsb").worksheets(1) 'or ws name etc
If wsSrc.FilterMode Then
wsSrc.ShowAllData
End If
'various filters 'USE wsSrc not activesheet
'ThisWorkbook is the wb where your code is running...
ThisWorkbook.worksheets("sheetNameHere").range("J28").value = _
wsSrc.range("AW2").value
End Sub

Related

Excel show value as it was on save instead of #NAME if formula is unknown

We are using accounting software that has an Excel plugin that allows us to fetch data from their database. The way they do this is by adding special formulas that we can use in Excel, for example:
Excel with special formulas:
The problem now is that, when opening an Excel sheet that was prepared like this on another device that does not have this plugin installed, the values are no longer readable: Excel doesn't recognize the formula and thus shows a #NAME error.
As a workaround currently we save every Excel we create like this twice: once with the formulas and once where we pasted all sheets "as values". Of course this is a cumbersome process.
What I was wondering:
Is there any way to teach Excel to show the values as they were when the sheet was last saved? For example, if you make a data connection to some database, and somebody else later opens the file that has no access to the database, Excel can also show the values as they were upon the last connection; just this doesn't seem to work with formulas then?
If this is not possible, is there some easy VBA that we could link to a button in the ribbon, that saves a copy of the current workbook (with all sheets) with values-only, so without the formulas? Then it's still a manual process to click that button but at least we don't have to copy-paste as values sheet per sheet anymore...
Thanks for your advice!
Not sure if this would work with user defined functions.... possible disable "Update links to other documents" in the Advanced options. Pretty certain it wouldn't work.
For your second question I can think of two ways to do it.
Either go through each sheet, copy all the cells and paste values back:
Sub PlaceValues()
Dim wrkBk As Workbook
Set wrkBk = ThisWorkbook 'This is the file containing the code.
'You could change it to look at some other open workbook.
Dim wrksht As Worksheet
For Each wrksht In wrkBk.Worksheets
With wrksht
.Cells.Copy
.Cells.PasteSpecial Paste:=xlPasteValues
End With
Next wrksht
End Sub
A more targeted approach would be to replace the specified formula.
One downfall here would be replacing SUM formula would also replace SUMIF as it contains the text SUM.
Public Sub Test()
ReplaceFormula "FIN_G_EN"
ReplaceFormula "SUM"
End Sub
Public Sub ReplaceFormula(FormulaText As String)
Dim wrkBk As Workbook
Set wrkBk = ThisWorkbook
Dim wrksht As Worksheet
For Each wrksht In wrkBk.Worksheets
Dim rFound As Range
With wrksht.Cells
Set rFound = .Find(What:=FormulaText, _
LookIn:=xlFormulas, _
LookAt:=xlPart, _
MatchCase:=True)
If Not rFound Is Nothing Then
Do
rFound.Value = rFound.Value
Set rFound = .FindNext(rFound)
Loop While Not rFound Is Nothing
End If
End With
Next wrksht
End Sub

Vba code works at times, and at times fails miserably

I have written a pretty elaborate code to automate 2 hours of work into less than 1 min. It works, on most days, somedays like today, the code wont work, or parts of the code wont work.
Its the most obviously simple parts of the code that doesn't work.
This is frustrating me.
To get the code to work, what I have to do would be to restart the system.
Please understand, I dont change the code at all. either before or after the error happens.
the is the code, where the error happens.
Range("Table1_2[[#Headers],[Bag No]:[Batch Making]]").Select
Range(Selection, Selection.End(xlDown)).Select
Selection.Copy
On Error Resume Next
Application.DisplayAlerts = False
Worksheets("Batch Making").Delete
Sheets.Add(After:=Sheets("Sheet1")).Name = "Batch Making"
Range("A1").Select
ActiveSheet.Paste
Cells.Select
Cells.EntireColumn.AutoFit
Today the error was that it would not paste what the code had selected.
please note:
It selected the set of rows and columns
It created the new sheet and selected the first cell
It tried pasting the value also, but nothing happened.
After restarting the system, code worked like a dream.
Why does this happen?? any clue ??
EDIT: Biggest issue is replicating the error, as I mentioned on some days, the code will crash, else it will run smoothly.
Every day new data is fed to the program, and its cleaned to ensure that only what the program can take is given to it and this involves removing #N/A's, #VALUE's and #Ref (this was done today also, I double checked the data after the crash)
Yet at times it fails.
I'll remove the Error Handlers and have separate code to check for availability of sheet, incase the error pop's up again, then I'll update here.
You can try below code. Using select is not the best idea in 99% of time. Also, when referencing a cell it is always good to precisely tell VBA which worksheet to use.
I would suggest to replace on error resume next clause - it disables all errors in your code and you will not be notified if something went wrong.In this case you can write a bit more code to check whether the specific worksheet exist and then remove/add it.
Sub copytable()
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets("worksheet_with_table_you_want_to_copy")
On Error Resume Next
Application.DisplayAlerts = False
ThisWorkbook.Worksheets("Batch Making").Delete
ThisWorkbook.Sheets.Add(After:=Sheets("Sheet1")).Name = "Batch Making"
Application.DisplayAlerts = True
ws.ListObjects(1).Range.Copy
With ThisWorkbook.Worksheets("Batch Making")
.Range("a1").PasteSpecial xlPasteAll
.Cells.EntireColumn.AutoFit
End With
End Sub
edit: Code without on error but with check wheather worksheet "Batch Making" exists and delete it if it's true
Sub copytable()
Dim wsTable As Worksheet
Set wsTable = ThisWorkbook.Worksheets("worksheet_with_table_you_want_to_copy")
Dim ws As Worksheet
Application.DisplayAlerts = False
For Each ws In ThisWorkbook.Worksheets
If ws.Name = "Batch Making" Then ThisWorkbook.Worksheets("Batch Making").Delete
Next ws
Application.DisplayAlerts = True
ThisWorkbook.Sheets.Add(After:=Sheets("Sheet1")).Name = "Batch Making"
ws.ListObjects(1).Range.Copy
With ThisWorkbook.Worksheets("Batch Making")
.Range("a1").PasteSpecial xlPasteAll
.Cells.EntireColumn.AutoFit
End With
End Sub
Unfortunately, you don't say what your code is supposed to do but it seems that you have a template and try to create a new worksheet with a table from this template.
The problem is that the source isn't qualified. Your code copies a "Table_2" from an unspecified source sheet, then deletes the "Batch Making" tab, creates a new sheet by that name and pastes the clipboard to this new sheet. The process is slightly illogical because if the source is a sheet by the name of "Batch Making" then it takes a lot of confidence to delete the original before the copy is safely in place. But I think you have a template. Therefore there is no danger of losing data but the question arises why you make the copy before you need it. Either way, if the copy on the clipboard is lost while deleting and inserting sheets "nothing will happen", as you say.
But what should happen? I presume that you just get a fresh copy of the template, and that means that you retain the structure of the table. Therefore my code below takes a radically different approach. It doesn't delete a sheet (with the table in it) and doesn't create a new sheet. Instead it simply deletes the content of the table in the existing sheet.
Sub Snippet()
' 295
Const WsName As String = "Batch Making"
Dim Ws As Worksheet ' Sheets(WsName)
Dim Tbl As ListObject
On Error Resume Next
Set Ws = Worksheets(WsName)
If Err Then
MsgBox "Sorry, there is no tab """ & WsName & """ in this workbook.", _
vbInformation, "Missing worksheet"
Else
Set Tbl = Ws.ListObjects(1)
If Err Then
MsgBox "Sorry, there is no table on the specified worksheet.", _
vbInformation, "Missing table"
Else
Tbl.DataBodyRange.ClearContents
End If
End If
End Sub
This is done by a single line of code, to wit, Tbl.DataBodyRange.ClearContents. You could take more or different action at that point. For example, you could delete unwanted rows or add default cell content. If there is a lot of such default you might get those data (or formulas) from a template. Otherwise just add it to the code. Headers and totals aren't deleted. The table's structure remains unchanged.
Before that, in the above code, the sheet is qualified and the table. In place of the error messages you could insert code to create the tab and/or create the table.
Please observe the name of the worksheet at the top of the code. You can change that string to any other name. The code doesn't specify the name of the table. Instead, it presumes that there is only one table on that sheet and clears that one.

Copying data from one sheet to another sheet using paste and insert rows

So I'm new to Excel VBA and I'm given a project that requires to copy data from sheet 1 to new workbook. The problems are with the copy, paste and insert new rows. (There are two subroutines but the second subroutine is about the same and it's required, please forgive), Thanks.
Sub CopyInfo()
On Error GoTo Err_Execute
wb1.Sheets("dataform").Range("L2:N2").Copy
wb2.Sheets(shtname).Range("A2:C27").Rows("1:1").Insert Shift:=xlDown
wb2.Sheets(shtname).Range("A2:C28").PasteSpecial (xlPasteValues)
wb1.Sheets("dataform").Range("B2:B28").Copy
wb2.Sheets(shtname).Range("D2").Insert Shift:=xlDown
wb1.Sheets("dataform").Range("D2:D28").Copy
wb2.Sheets(shtname).Range("F2").Insert Shift:=xlDown
update
wb1.Sheets("Setlist").Range("G2").Copy
wb2.Sheets(shtname).Range("E2:E27").Rows("1:27").Insert Shift:=xlDown
wb2.Sheets(shtname).Range("E2:E28").Paste1Special
wb2.Sheets(shtname).Columns().AutoFit
Err_Execute:
If Err.Number = 0 Then MsgBox "All have been copied!" Else _
MsgBox Err.Description
End Sub
Sub update()
wb1.Sheets("dataform").Range("D2:D28").Copy
wb1.Sheets("dataform").Range("E2:E28").PasteSpecial
wb1.Sheets("dataform").Range("F2:F28").PasteSpecial
wb1.Sheets("dataform").Range("F2:F28").Copy
wb2.Sheets(shtname).Range("G2").Insert Shift:=xlDown
End Sub
The program requires from current workbook sheet 1 copies to another workbook. The first workbook sheets 1 will have new rows add-in and the code automatically update it. As currently if current workbook sheet 1 add new rows, I have to manually update the code.
Ok so there's several steps to get to your desired result.
I'm assuming your 2nd workbook is not open yet so you need to open it in VBA first before being able to copy your ranges. There are plenty of answers on SO and google that show you how to do that.
Since you will be adding rows to the original worksheet you need a way to include this in your code. There's a few options on how to do that.
You could create a table and use Dim As ListObject to declare and then set it in VBA. That way when new rows are added to the table they will be automatically included. A second way would be to use a dynamic named range (see name Manager under formulas tab), that includes new rows when they are added.
Possibly the most important part is making the code easier by declaring your variables and objects.
Currently you write wb1.Sheets("dataform") constantly. Instead use Dim DataForm as Worksheet and Set Dataform = wb1.Sheets("dataform"). This way when use adress your sheet you can just write Dataform.Range("A1:A1"). See below for an example.
Dim Source As Workbook
Set Source = ThisWorkbook
Dim Dataform As Worksheet
Set Dataform = Source.Sheets("Dataform")
Dataform.Range("L2:N2").Copy 'See how you can just adress the sheet by name
Now if you were to also declare you ranges as tables/named ranges this would make the code even easier to process. Since you're also always adding a row during each operation a much more efficient way would be to use a for each loop to go through all your named ranges/tables, copy them, insert a new row into the destination sheet and then paste.
Hope this was some inspiration
Sub CopyInfo()
On Error GoTo Err_Execute
dataform.Range("L2:N2").Copy
wb2.Sheets(shtname).Range("A2").Rows("1:27").Insert Shift:=xlDown
wb2.Sheets(shtname).Range("A2:C28").PasteSpecial (xlPasteValues)
dataform.Range("LocationName").Copy
wb2.Sheets(shtname).Range("D2").Insert Shift:=xlDown
dataform.Range("Reading").Copy
wb2.Sheets(shtname).Range("F2").Insert Shift:=xlDown
update
wb1.Sheets("Setlist").Range("D2").Copy
wb2.Sheets(shtname).Range("E2").Rows("1:27").Insert Shift:=xlDown
wb2.Sheets(shtname).Columns().AutoFit
Err_Execute:
If Err.Number = 0 Then MsgBox "All have been copied!" Else _
MsgBox Err.Description
End Sub
Sub update()
dataform.Range("Reading").Copy
dataform.Range("PreviousReading, Usage").PasteSpecial
dataform.Range("Usage").Copy
wb2.Sheets(shtname).Range("G2").Insert Shift:=xlDown
End Sub
(There are more codes but I just add half of it, sorry for the confusion. If there's useful links that could help. Much appreciated.)

Copy formatting to macro

I have a workbook that the workbook formatting is changed regularly, however once changed (maybe weekly or monthly) then going forward until it is changed again a macro needs to replicate that format. Changing the VBA to account for the new formatting each time is very time consuming. Is it possible to format a workbook and then copy the formatting easily to VBA (after the fact not like a macro record) for future use?
In the past I have since used a hidden sheet within the workbook where the macro runs and I essentially copy/paste that into the sheet I am working with. This works but has the downside of when making changes I first need to copy data over to the "template" sheet to ensure everything is correctly aligned with new data.
Possibly some kind of macro that iterates through all cells of a range and outputs to the immediate window the VBA code needed to re-create the formatting?
Basically any ideas will help :)
There are so many formatting options that simply storing them as separate options will take far more space than just a duplicate template sheet. Just run the first code to update your template, and the second to copy it back:
option Explicit
Const TemplatesheetName = "mytemplate"
Sub CopyFormatting
dim ws as worksheet
dim source as worksheet
set source = activesheet
for each ws in worksheets
if ws.name = templatesheetname then
exit for
end if
next ws
if ws is nothing then
set ws = worksheets.add
ws.name = templatesheetname
end if
ws.usedrange.clearformats
source.usedrange.copy
ws.range("a1").pastespecial xlpasteformats
ws.visible = xlveryhidden
end sub
Sub BringBackFormats
dim ws as worksheet
for each ws in worksheets
if ws.name = templatesheetname then
exit for
end if
next ws
if ws is nothing then
msgbox "No template found",vbokonly,"Unabl;e to run"
else
ws.cells.copy
activesheet.range("a1").pastespecial xlpasteformats
end if
exit sub
(written on my phone, can't check the code, there may be typos)

Run-error 1004 -- ActiveSheet.Paste

I've recorded this macro, it keeps bugging on the ActiveSheet.Paste. Can someone help me please?
thank you
Sub Macro13()
'
' Macro13 Macro
'
'
ActiveSheet.Range("$A$3:$L$10001").AutoFilter Field:=6, Criteria1:="O/S"
Columns("A:E").Select
Range("A2").Activate
Selection.Copy
ActiveSheet.Range("$A$3:$L$10001").AutoFilter Field:=6
Workbooks.Add
ActiveSheet.Paste
Cells.Select
Cells.EntireColumn.AutoFit
Rows("1:1").Select
Selection.RowHeight = 89.25
Range("G7").Select
Windows("Test.xlsm").Activate
End Sub
First of all I recommend to read How to avoid using Select in Excel VBA which is absolutely necessary to follow strictly if you are a beginner. This makes your code a lot faster and cleaner.
Yes I know this is a recorded macro, and yes they always have a lot of .Select. This is why you should always re-write your recordings to get a good clean and reliable code. But recordings are good to have something to start with.
Then it is better to specify a worksheet by its name and avoid ActiveSheet where ever you can.
Also always specify a worksheet for every Range() or Cells() etc. Otherwise Excel guesses which worksheet you mean and Excel might guess something different from your guess and then fails.
So the code you tried would better look like this …
Option Explicit
Public Sub DoMyStuff()
Dim wsSrc As Worksheet
Set wsSrc = ActiveSheet 'better specify sheet by name: Set wsSrc = Worksheets("SheetName")
Dim wsNew As Worksheet
Set wsNew = Workbooks.Add().Worksheets(1) 'get first worksheet of a new added workbook
wsSrc.Range("$A$3:$L$10001").AutoFilter Field:=6, Criteria1:="O/S"
wsSrc.Columns("A:E").Copy wsNew.Range("A1") 'copy from source sheet directly into the new sheet
wsNew.Cells.EntireColumn.AutoFit
wsNew.Rows("1:1").RowHeight = 89.25
End Sub

Resources