Spreadsheets, function for getting the currency unit of a cell - spreadsheet

Not really an SO question but I don't want to play "guess the password" with Yahoo Answers, and there's no other place.
I have some cells in a column called "cost" and they are entered in a few different currencies, I'd like to have a column where the values in the "cost" columns are represented in my local currency.
It'd be easy enough if I could use if statements, if(???=="$",cell*$[cell with conversion rate],[nested if statements]) but I'm not sure what the ??? is, I've been searching but without knowing what I'm looking for it has been fruitless.

If the values in the column are entered as text, this should be easily solvable using =LEFT(): if the string in the cell is "$100,000", you could use =LEFT([Cell], 1) with an =IF() condition to check for the currency. If the values are entered as numbers, but formatted as Currency, you might want to create a user-defined function in VBA that checks what the cell's NumberFormat is, and returns a currency value depending on the input.
EDIT: A sketch of a VBA solution might look something like this:
Function CurrencyFormat(rng As Range) As String
If InStr(1, rng.NumberFormat, "€") Then
CurrencyFormat = "EUR"
ElseIf InStr(1, rng.NumberFormat, "£") Then
CurrencyFormat = "GBP"
ElseIf InStr(1, rng.NumberFormat, "¥") Then
CurrencyFormat = "JPY"
Else
CurrencyFormat = "USD"
End If
End Function
You'd need to extend it to accomodate the currencies that appear in your dataset. You can use the formula's output as a key to search for the relevant FX rate in a separate table (alternatively, if you'll only ever use one FX rate in the spreadsheet, you can change the formula output so that it already returns the spot rate).

Related

Formula in worksheet and VBA works differently

Cell A1 contains the number 25, which is right-aligned, implying it's a number, not text.
D1 contains the formula:
="" & A1
The 25 in D1 is left-aligned, implying it's text. That is confirmed by the following formula, which returns 1:
=IF(D1="25",1,0)
The following VBA code puts 25 in F1 but the 25 is right-aligned and the IF test returns 0:
Range("F1") = "" & Range("A1")
Any explanation for the discrepancy?
E1 contains the following formula which gives a left-aligned 25 and the IF test returns 1:
TEXT(A1,"0")
However, the following code gives a right-aligned 25 and the IF test returns 0:
Range("F1") = Application.WorksheetFunction.Text(Range("A1"), "0")
Not that I have to use the TEXT function. I just wonder why it works differently than when in a worksheet.
Any rule that tells when or what worksheet functions won't work in VBA code, or more precisely, will give different results than when in worksheet?
When a data is written by vba into a cell, an internal type conversion function is called if required, that is if the data type is different from the cell's numberformat property.
You dont want that conversion function to be called.
To avoid this conversion function to be called, choose the proper Numberformat property for the cell before writing the data.
Range("b4").NumberFormat = "#"
Range("b4") = Application.WorksheetFunction.Text(Range("A1"), "0")
You simply get the wrong idea of what is a number in Excel.
in general ALL input is a string. Also writing "25" in a cell.
However: If possible, Excel will convert all inputs to a numerical value if possible. Also for dates and times.
To prevent this, you simply insert a ' in front of your "text" in the cell.
The confusing part for you is the different behavior for formulas.
A formula will always output a "result" AND the "data type".
So =1+1 will be numeric as the last action was math.
=Left(1+1,1) will be text as the last action was text-based.
For =A1 it will simply copy the type. If there is a formula, then this will be the same. But if there is a "direct input" it will always try to convert to numerical and only be text if it can't be converted or if it starts with a leading ' (A1 itself does this already).
As a result: If there is a plain 25 in the cell, it will always be "numerical" no matter "how" you input the 25.
For newer Excel there is only one exception: if the cell formatting is text prior to entering a number, it will be treated as text (no converting). This does not apply if you change the formatting later.
Simple test:
enter 25 in A1 (formatting general)
enter =ISNUMBER(A1) in A2 (will be TRUE)
set formatting for A1 to "text" (A2 will still be TRUE)
enter 25 in A1 (now A2 will become FALSE)
This may fail (Excel confuses itself sometimes here). Try it with a new sheet. ;)
Hopefully you understood the fault in your logic ;)
The cell alignment says nothing about the cell's contents. Forget about anything being "implied" by it. When you start on a virgin worksheet the format for all cells is "General" which means that Excel will decide the format of what you enter. If you enter a number the format will be "Number". If you enter what looks like a date to Excel the format will be "Date", and for most other things the format will be "Text".
So, if you enter " 25" in a cell formatted as "General" Excel will recognise this to be a number despite the leading spaces, read it is numeric, and format the cell to the right. This will happen regardless of whether you made the entry by hand or used VBA. You can then proceed to format the alignment as you wish.
However, if you enter the number 25 in a cell formatted as Text Excel will recognise the number as text and display it formatted to the left (unless you expressly formatted the horizontal alignment to the right).
The best way to deal with any problems you might encounter in this regard, set the NumberFormat and HorizontalAlignment properties for the cells that you want to write to. You can do that both manually or using VBA.
Worksheet function when used in the worksheet behaves / works the same way as when used in VBA. Consider below code:
Note: Range("B1") contains a numeric value 25
Dim r As Range, v As Variant
Dim wf As WorksheetFunction: Set wf = Application.WorksheetFunction
With Sheet1
Set r = .Range("B1")
v = r.Value2
v = wf.Text(r.Value2, "0")
End With
Now using the local window, let us check the data type of variant v.
SC1: All variables un-initialized
You can see, at the start that all variables have no value and the variant type v is empty.
SC2: Variables initialized and v assigned a value
After executing lines up to v = r.value2, all variable types were confirmed (e.g. Range/Range etc.) and variant v is now Variant/Double.
SC3: Re-assign a value on v but using worksheet function Text
Executing the last line which uses the worksheet function Text, variant v type becomes Variant/String. I think this confirms that the function Text works as expected converting the numeric 25 into a string type.
As for the behavior of passing VBA generated value to worksheet, it is covered by Docmarti's post above.

UDF to return formatted number

Sometimes I like to display numbers along with text in the same cell. To do this, I custom format the cell to something like 0.00" test", e.g. a cell A1 with formula =PI(), but formatted with custom format 0.00" test" would return a displayed result of 3.14 test.
Crucially, the value of the cell is unchanged by the formatting - you can still do =A1 * 3 in another cell and get the result - since the value of A1 is still Pi, only it's display has changed.
For a UDF that returns a numerical value (in my case, Long, but it could be any number), is there a way of returning a cell such that it is displayed 1 way, but it's actual value (.Value2 in VBA I believe) is a number, not text.
I've tried Format( in VBA, but it returns a text string. I would just format my cell how I want it manually, but as you can see from the below code, the formatting is dependent on intermediate results (I'm trying to return the value formatted with the time the calculation took).
UDF_RESULT = Format(valueResult.Length, IIf(tElapsed < timeout, "0" & " """ & Round(tElapsed, 2) & "s""", "0"))
This would be easy to do with a Macro, but within a UDF it's harder. I could declare all of the intermediate values at a module level, then a Worksheet_Calculate() macro can access those values and apply the custom formatting, but a UDF-wrapped approach would be much better.
No.
You're confusing a cell's value with its number format. A UDF can compute a value, and the cell is free to format that value as needed.
So if a UDF returns a number, the cell's value is the result of that function - a number.
Just format the cell as needed. A function doesn't format anything.

How to properly read a single Excel cell

I've been looking up in Google in these past days but I still can't find a good answer to this one.
Currently, this is how I do it:
For Each cell In ws.Range(fromCol, toCol)
If IsNothing(cell.Value) Then Exit For
valueList.Push(cell.Value.ToString())
Next
But when it reads a cell whose assumed data type is Time, it returns a Double value. I try to parse that value but it's not the same as expected.
How can I properly read a single Excel cell with an assumed type of Time?
As per the comment suggesting the article,
.Text is a bad idea in the sense that it will give you just the displayed string in that cell. If you use TypeName on the cell's content, for example, it will always return string regardless of any datatypes the content might be. However, if you use .Value, it will return the actual datatype name of the content of that cell.
This can prove useful to you if you're using TypeName in a comparison for instance. It saves you using conversion functions.
Take this example:
Sub Test()
Range("A1") = "True"
Debug.print(TypeName(Range("A1").Value))
Debug.print(TypeName(Range("A1").Text))
End Sub
This Output is:
Boolean
String

Copy cells if specific text is found

I seem to have a problem and currently have not found a solution to it, which is why I address this question to you:
Each day I have a list of invoices and orders coming from different suppliers, and the orders are based on part numbers and types.
This list is imported as text and then goes through a macro I made, to arrange everything in cells.
I also need to go through some steps to format this list based on the type of order (ex: windshield, carpets, wheels, etc ). what I usually do is to filter everything and select the order type that I am interested, and then copy on the same row cells with text and formulas from another worksheet, named "template", which is a list of conditions .
Since it varies from day to day, it may not necessarily contain all part types, which is I couldn't use a macro, and I have to continue by hand, and sometimes the list exceeds 200-300 lines.
To give you an example, if E2 has "windshield" I copy in M2 to Q2 a selection of cells from "Template" (M2 to Q2), if "carpets" I copy M3 to Q3, and so on. the list of conditions is around 15 - 20 rows, and sometimes 2 conditions may apply (if order exceeds $5000 I mark it red, if overdue I bold everything, etc) but mainly I copy based on text in coll E.
If this could be copied into a macro, I would really appreciate it, as I need to take some steps every time, like auto-fit, copy header, format the amounts as number (from text), change text color based on order type, etc, and this too takes time.
I hope this information is enough to make an idea about this, and if not, I could post an example of the list I have to work with.
Many thanks in advance for your support
Use Application.Worksheetfunction.Match to find in which row in Template the to-be-copied cells can be found, then copy range M-Q for this row and paste in your file
You are asking too much in one question to get help here. We are best at single issue questions. The text and code below is intended you give you some ideas. If your code does not work, post the relevant part here and explain the difference between what it does and what you want it to do.
The problems you mention do not sound difficult. I would expect basic VBA to be enough to get you started. Are you looking for bits of relevant code without learning VBA. If you are, this is a big mistake. Search the web for "Excel VBA tutorial" or visit a large library and review their Excel VBA Primers. There are many tutorials and books to choose from so select one that is right for you. The time spent learning the basics will quickly repay itself.
Dim RowCrnt As Long
Dim RowLast As Long
With Worksheets("xxxx")
RowLast = .Cells(Rows.Count,"E").End(xlUp).Row
For RowCrnt = 2 to RowLast
' Code to process each row goes here
Next
End With
The above is probably the structure of your code. The For loop will examine each row in turn which will allow you to take relevant actions.
I have used "E" as a column letter because your question suggests column "E" is the most important. However, code that references columns in this way can be very confusing. Worse, if the column positions change, you will have to work carefully through your code changing the column letters. Better to have some statements at the top like this:
Const ColDate As String = "A"
Const ColAmtInv As string = "B"
Const ColAmtPaid As string = "C"
Const ColProdType As String = "E"
With these constants every reference to a column uses a name not a letter. The code is easier to read and, if a column moves, one change to the constant statement will fix the problem.
The Select Case statement is useful:
Select Case .Cells(RowCrnt, ColProdType).Value
Case "carpets"
' code for carpets
Case "windshield"
' code for carpets
Case Else
' Unknown product type
.Cells(RowCrnt, ColProdType).Font.Color = RGB(255, 0, 0)
End Select
You can have a string of If statements so multiple actions can occur on a single row:
If DateAdd("m", 1, .Cells(RowCrnt,ColDate).Value) < Now() And _
.Cells(RowCrnt,ColAmtInv).Value) > .Cells(RowCrnt,ColAmtPaid).Value Then
' Invoice overdue
.Rows(RowCrnt).Font.Bold = True
End If
If .Cells(RowCrnt,ColAmtInv).Value) > 5000 Then
' Large invoice
.Rows(RowCrnt).Font.Color = RGB(255, 0, 0)
End If
You do not have to write the entire macro in one go. If most action is because of the product type then try that first.
Hope this helps get you started.

Formatting Values in cells, and sending the formatted values to Powerpoint

I have this code that aggregates multiple workbooks into a preview file where all of the data is clearly laid out in tables and charts. Then those tables and charts are pasted in a PowerPoint presentation.
I am having issues getting the right formatting to work when the values in the tables are copied over to the PowerPoint slides. One of the source worksheets outputs values like 847389.240398230423, and when you try to put that in PowerPoint, you get a value that takes up too many lines and looks like 2.240398230423E-02. That kind of formatting does not look great in any meeting.
I wrote some code that will edit the code while the values are in the Preview workbook
Sheets("Output").Select
'Select1
Range("C10:H15").Select
Selection.NumberFormat = "0.000"
'Select2
Range("C17:H17").Select
Selection.NumberFormat = "0.000"
'Select3
Range("C18:H19").Select
Selection.NumberFormat = "0.00"
'Select4
Range("C20:H21").Select
Selection.NumberFormat = "0.00"
This code works perfectly for the data that I need and shows up like 0.487, but the actual values in the cells are stuff like 0.487682742490, and I think that is what the code is recognizing when it comes time to update the PowerPoint presentation.
Is there a way that you can write some code that will only take the first 2 or 3 digits after the decimal place and ignore the other non-significant figures?
There are a few ways to achieve this. One way is to force the formatted/visible data as the actual value.
For example, let's say cell A1 contains the value 1.234. Using this code:
ActiveSheet.Range("A1").NumberFormat = "0.0"
ActiveSheet.Range("A2").Value = ActiveSheet.Range("A1").Value
ActiveSheet.Range("A3").Value = ActiveSheet.Range("A1").Text
The result is like so:
Cell A1 contains the value 1.234, but displays as 1.2
Cell A2 contains the value 1.234, and displays as 1.234
Cell A3 contains the value 1.2, and displays as 1.2
The .Text property of a cell is essentially what you see in the cell. Once you convert your values in this way, transferring to PP should work as intended.
Alternatively (though not preferred because it is somewhat more cumbersome to use and requires strings, rather than just sticking to values) you can use something like this:
Dim s As String
Dim dec As Integer
dec = 2
s = CStr(ActiveSheet.Range("A1").Value)
ActiveSheet.Range("A3").Value = CDbl(Left(s, InStr(s, ".")) & Left(Right(s, Len(s) - InStr(s, ".")), dec))
In the above, you can extend the number of decimal places by increasing the value of dec. However, without further modification 1.000 will come out as 1 (removes possibly significant 0's).
If you use this method, you can either retain a numeric value (using CDbl()) or keep it as a string if you do not need to perform any arithmetic (i.e. just put into PP).