Google Sheets - Script to Change Currency Formatting Based On Dropdown Selection ($ + €) - google-apps-script

I have a Sheet with two tabs — Tab 1 is a quote/invoice and Tab 2 is a list of clients. C3 on Tab 1 contains a dropdown list which pulls from Tab 2, Clients 1-4 use USD ($) but Client 5 uses EURO (€). Tab 1 also contains a costs column in I and a feeder column in H.
My goal is to use a script to change the currency formatting in column I based on what's selected in C3. When Client 5 is selected all costs in I need to contain a '€' prefix, and when anyone else is selected they need to contain a '$' prefix.
In the following code 'H' is a feeder cell where 'EURO' or 'literally anything else' can be entered to change the currency format in 'I' between '€' to '$':
function onEdit(e){
var sheetName = "Main Sheet";
var currencyCol = 8; //column H
var amountCol = 9; //column I
var defaultFormat = "[$$]#,##0.00";
var currencyFormat = {"USD":"[$$]#,##0.00",
"EURO":"[$€]#,##0.00"};
var r = e.range;
if(e.source.getSheetName()==sheetName && r.getColumn() == currencyCol){ // This assumes I want to manually change H, I'd rather have it automatically change between EURO (including € in column I) when client 5 is selected, and USD (including $ in column I) when clients 1-4 are selected
var uf = currencyFormat[r.getValue()];
uf = uf?uf:defaultFormat;
r.offset(0,amountCol-currencyCol).setNumberFormat(uf);
}
}
It works, but I have to manually type 'EURO' or 'anything else' in 'H' line-by-line to change the currencies for 'I' - but I need it to change automatically based on the selection in C3.
I tried using =if(C3="Client 5", "EURO", "") in Column H which works the first time Client 5 is selected in the dropdown, but doesn't reset 'I' to '$' once changed and needs to be manually typed in before working again.
I also tried 2 Macros which manually input 'EURO' and 'USD' into column H and tried to run them with a script to trigger when Client 5 is selected. It worked in 'H', but 'I' didn't change when the words appeared I think the issue is that the code isn't/can't trigger outside of manual data entry.
Any help would be great, I'm a beginner and I can't wrap my head around it. Feel free to message me or ask for a link to a test sheet.
Also I attached a screenshot of what tab 1 & 2 look like. I manually changed EURO to USD on Tab 1 to set that currency in column I, ideally that change can be based off C3Spreadsheet example

I believe your goal is as follows.
When a dropdown list of "C3" of "Tab1" sheet is changed, you want to change the number format of the cells "I8:I".
You want to retrieve the values of the dropdown list and the corresponding number formats from the cells "B4:C8" of "Tab2".
In this case, how about the following modification?
Modified script:
function onEdit(e) {
var sheetName1 = "Tab1"; // Please set the sheet name of "Tab1".
var sheetName2 = "Tab2"; // Please set the sheet name of "Tab2".
var r = e.range;
if (e.source.getSheetName() == sheetName1 && r.getA1Notation() == "C3") {
var defaultFormat = "[$$]#,##0.00";
var currencyFormat = { "DOLLAR": "[$$]#,##0.00", "EURO": "[$€]#,##0.00" };
var obj = e.source.getSheetByName(sheetName2).getRange("B2:C8").getValues().reduce((o, [b, c]) => (o[b] = c, o), {});
var sheet = r.getSheet();
sheet.getRange("I8:I" + sheet.getLastRow()).setNumberFormat(currencyFormat[obj[e.value]] || defaultFormat);
}
}
Note:
In this modified script, when the dropdown list of "C3" of "Tab1" sheet is changed, the number format of cells "I8:I" is changed by the values of "B4:C8" of "Tab2". In this case, the column "H" is not used. Please be careful about this.
In your script, "USD":"[$$]#,##0.00" and "EURO":"[$€]#,##0.00" are used. But, in your image, "DOLLAR" and "EURO" are used. In this modification, "DOLLAR" and "EURO" are used. Please be careful about this.
Reference:
reduce()

Related

Replace text with hyperlinked version onEdit

I have a google sheet with a list of people's initials in range A4:A.
These initials are each hyperlinked to a different part of my sheet.
Column MM contains many cells that all have a data validation dropdown list referencing the initials list in A4:A
I need a script that triggers on edit so that when a user selects initials in the dropdown list in column MM, the script replaces it with the hyperlinked version from column A
My scripting knowledge is rudimentary. Normally I'd do a replace something like the below but I'm not sure how to implement the search so it takes the value from column MM and searches for it in columnA (or even if this would take the hyperlink function along with it.
`function replaceText(){
var oldText = "AB";
var newText = "AB hyperlinked";
var sheet = SpreadsheetApp.getActive().getSheetByName('SHOTS');
sheet.getRange("MM1:MM" + sheet.getLastRow()).createTextFinder(oldText).replaceAllWith(newText);
}`
Thanks in advance for any help given
If you have the chance of having an additional column, you can do a workaround without script with FILTER:
=FILTER(A2:A,A2:A=MM1)
Or if you want it as an array for many rows
=BYROW (MM1:MM,LAMBDA(each,IF(each="","",FILTER(A2:A,A2:A=each))))
Check in this image how when I chose value "b" it returns the hyperlinked value in the coloured column
OPTION B with SCRIPT:
Please check if MM is column 351, if not change that number. Run it once from the console (it will give an error, but it's just for approving the permissions), then go to your sheet and start trying with your dropdown:
function onEdit(e) {
var ecolumn = e.range.getColumn()
if (ecolumn == 351){
var sheet = SpreadsheetApp.getActive()
var range = sheet.getActiveRange();
var value = range.getValue()
var lookuprange = sheet.getRange("A:A").getValues().map(n => n[0])
var index = lookuprange.indexOf(value)+1
if (index != 0){
var link = sheet.getRange("A"+index).getRichTextValue().getLinkUrl()
var richValue = SpreadsheetApp.newRichTextValue()
.setText(value)
.setLinkUrl(link)
.build();
range.setRichTextValue(richValue);}}
}
REFERENCE: Apps Script: how to get hyperlink from a cell where there is no formula

Currency format in google apps script

I have created some set of rows for a requirement gathering document in which for a range I have set numberformat for USD as
ex_range.setNumberFormat("[$$]#");
upon execution, in the range when the user enters the value, the formatting rounds it to a whole number.
I tried
ex_range.setNumberFormat("[$$]#,##0.0000");
and for the above, when the user enters a value say e.g. 34.56 I get $34.5600
I want the user to enter the value and whatever decimal points they enter to not round off or add extra zeros.
If the user enters 50.67, I should get $50.67 and if 45.5670 is entered I should get $45.5670
Please help in how to set the format to USD for the range and no round up happens to the values.
I am having the user fill value in the range K43:P57, they should be able to enter any value with any decimal number, the value should not get rounded off.
and the script should read the range K43:P57and identify the value with highest decimal numbers and populate in G43
With an onEdit(e) function you can set the number format for the edited cell. This will override the column formatting. Be aware that the 0 at the end of 5.989830 will be "cut off" before sending it to the script. So the value of n inside the script below will be 5.98983 like #Tanaike is mentioning in the comments. Nevertheless i hope this will help:
EDIT: If the original column formatting is Plain text the solution below will work with tailing zero's
Extensions -> Apps script.
Paste the code.
Change the sheetname and column number to the values where theformatting needs to happen.
Save an quit.
Test it out.
function onEdit(e) {
//Settings
const sheetname = "BB";
//Rest of script
const sheet = e.source.getActiveSheet();
const range = e.range;
const n = e.value;
if (isNaN(n)) return;
if (sheet.getName() !== sheetname) return;
const column = range.getColumn();
if (column < 11) return;
if (column > 16) return;
const row = range.getRow();
if (row < 43) return;
if (row > 57) return;
const length = n.toString().split(".")[1].length;
const zeros = new Array(length).fill("0").join("");
const format = "#,##0." + zeros;
range.setNumberFormat(format);
}

Google Apps Script - How to get clipboard column number

Goal: When a user pastes a value to a cell, I want the script to check whether or not the copied value is from the same column or not.
Problem: Oftentimes, the user copies a cell from a different column and pastes in a cell that's under another column. This causes some problems for this particular sheet, so I want to be able to have the script check if the clipboard’s copied value's column number (or letter) is the same with the column of where the user is trying to paste it.
Example:
Scenario A: User copied a cell from A1 and attempts to paste it to B2 (either by mistake or intentionally)
If statement check: Check if the user’s clipboard copied value’s column number (or letter) is equal to 2 (if by letter: B).
Result: Since the copied column number is 1 and editing column number is 2, the script will show an error message to refuse the editing.
Scenario B: User copied a cell from A1 and attempts to paste it to A2
If statement check: Check if the user’s clipboard copied value’s column number (or letter) is equal to 1 (if by letter: A).
Result: Since the copied column number is 1 and editing column number is also 1, the script will allow the user to pates the value to A2.
Here's my attempt: I thought it would make sense to use the onEdit function to compare
function onEdit(e) {
const range = e.range;
const source = e.source;
const sheetName = source.getActiveSheet().getName();
const row = range.getRow();
const column = range.getColumn();
const editedRangeValue = range.getValue();
// Call the copyPrevention function
copyPrevention(sheetName, range, row, column, editedRangeValue);
}
function copyPrevention(sheetName, range, row, column, editedRangeValue) {
const headerRowNum = 12;
if (sheetName == 'Status' && row > headerRowNum && editedRangeValue != '') {
// row variable: This is to ensure the editing will only apply if the user tries to edit after row 12 (headerRow).
if (column == "") { // The "" is just a placeholder. If this is even possible, I would like to compare user's editing column number & their clipboard column number. And if the column numbers are the same, then I'd like the script to continue the execution without throwing any errors.
range.setValue(editedRangeValue);
} else if (column != "") { // The "" is just a placeholder. I would like to compare user's editing column number & their clipboard column number. And if the column numbers are not the same, then I'd like the script to throw an error and not let the user edit the cell.
const statusSheetUi = SpreadsheetApp.getUi();
statusSheetUi.alert("You're pasting in the wrong column!");
}
}
}
I have a feeling that this isn't possible, however wanted to check.
Thank you!

Running a formula in the background of a cell on Sheets

I am trying to make a sheet where I can type a time (duration) into a cell without colons or decimals and it show up in the same cell with the colons and decimals (example: input "10342" and the cell reads "1:03.42"). The formula I have that works in another cell is:
=ARRAYFORMULA(TEXT(AVERAGE(VALUE(IF(D3<>"", TEXT(
IF(IFERROR( LEFT(D3, LEN(D3)-6))="", 0, LEFT(D3, LEN(D3)-6))&":"&
IF(IFERROR(RIGHT(LEFT(D3, LEN(D3)-4), 2))="", "00", RIGHT(LEFT(D3, LEN(D3)-4), 2))&":"&
IF(IFERROR(RIGHT(LEFT(D3, LEN(D3)-2), 2))="", "00", RIGHT(LEFT(D3, LEN(D3)-2), 2))&"."&
IF(LEN(D3)>1, RIGHT(D3, 2), "0"&D3), "[h]:mm:ss.00"), ))), "[h]:mm:ss.00"))
I have tried conditional formatting and I'm not the greatest with macros. Is there anyway that this would be possible?
You clearly need an onEdit function. You have to open the script editor within your spreadsheet file and copy paste the code snippet I provide here. With the following code, every time you edit a cell in column A of "Sheet1" you get back the desired format of your input to the exact same cell. If the value you enter is: 10342 it will become: 1:03.42 . If the value you enter is 130342 you get back 13:03.42 . If you want to edit cells only after row 1 (in case you have a header) you can add in the if condition statement the condition : row >1.
function onEdit(e) {
var row = e.range.getRow();
var col = e.range.getColumn();
if (col === 1 && e.source.getActiveSheet().getName() === "Sheet1"){
var valu = e.source.getActiveSheet().getRange(row,1).getValue().toString()
if (valu.length == 5){
var result = valu.slice(0,1)+":"+valu.slice(1,3)+"."+valu.slice(-2);
}
else if ( valu.length==6) {
var result = valu.slice(0,2)+":"+valu.slice(2,4)+"."+valu.slice(-2);
}
e.source.getActiveSheet().getRange(row,1).setValue(result);
}
}
Don't forget to modify the name of the sheet (in my case "Sheet1") as well as the column you want to work with. As you can see my solution uses col===1, e.source.getActiveSheet().getRange(row,1).setValue(result) and var valu = e.source.getActiveSheet().getRange(row,1).getValue().toString(). Number 1 corresponds to column A. If you want to work with column D, for example, you need to replace 1 with 4 for all these formulas. Let me know in the comments if you have any questions.

If Checkbox is checked, use a script to clear specified cells in row and clear checkbox after script is run

I'm trying to get a checkbox to run a script on the row it is in. It needs to loop through all rows in the specified sheet.
If a checkbox is checked in Column F, I want to schedule a script to clear columns B-F.
B is a checkbox that should be set to FALSE
C is a data validated input
D is data validated input
E is a formula that relies on data in C and D. The formula should remain.
F being true is the triggering checkbox
If F is checked, then clear contents in B,C,D, F (E shouldnt be cleared since it's a formula)
I've tried modifying scripts from here and other links without success:
Reset checkboxes to false
Using a script to generate docs from rows in Google Sheet if checkbox is checked
Google Sheets Script to Hide Row if Checkbox Checked
function try_It(){
deleteRow(5); //// choose col = 2 for column C, 5 for F
}
function deleteRowContents (col){ // col is the index of the column to check for checkbox being true
var sh = SpreadsheetApp.getActiveSheet();
var data = sh.getDataRange().getValues();
var targetData = new Array();
for(n=0;n<data.length;++n){
if(data[n][col]!="TRUE" && data[n][col]!="FALSE"){ targetData.push(data[n])};
}
Logger.log(targetData);
sh.getDataRange().clear();
sh.getRange(1,1,targetData.length,targetData[0].length).setValues(targetData);
}
Expected results:
If Column F is checked, clear B:F in same rows.(I know this script above is not even close to being complete).
Actual results:
This deletes all the formatting on the entire sheet.
It doesn't clear range properly even in the column F checkbox.
You want to clear the values of the column "B" to "D" and the column "F" when the checkbox of the column "F" is true.
In this case, you want to change the checkbox to false.
You don't want to clear the cell format.
If my understanding is correct, how about this modification? Please think of this as just one of several solutions. The flow of the modified script is as follows.
Create the range list for deleting the columns from the retrieved values.
Clear the content of the range list.
Modified script:
function deleteRowContents (col){ // col is the index of the column to check for checkbox being true
var col = 6; // If the column "F" is 6, please set 6.
var sh = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet4"); // Modified
var data = sh.getDataRange().getValues();
// Below script was modified.
var deleteRanges = data.reduce(function(ar, e, i) {
if (e[col - 1] === true) { // Modified
return ar.concat(["B" + (i + 1) + ":D" + (i + 1), "F" + (i + 1)]);
}
return ar;
}, []);
if (deleteRanges.length > 0) { // or if (deleteRanges.length) { // Added
sh.getRangeList(deleteRanges).clearContent();
}
}
References:
Class RangeList
clearContent()
If I misunderstood your question and this was not the result you want, I apologize.