Selected cell does not update when using button linked to script - google-apps-script

I have a sheet for users to enter a cash-count and their initials then hit a button which runs a script and stores these inputs in a table.
The problem is, if the user types the information (e.g. their initials) and hits the button without first pressing Return or selecting another cell, the information is not saved.
I've seen a similar post:
How to force flush a user's input?
and tried the solutions, some don't work and none are really what I'm looking for. I know that I could use a Checkbox instead of a button but I'd like to find a way do it without resorting to that. The table is a cash-count for a till so not all cells require a value (there may be no $100 notes for example) and I won't know which was the last cell edited.
[The Data][1]
[1]: https://i.stack.imgur.com/mdKXk.png
The Code:
function Accept() {
var ss = SpreadsheetApp.getActive();
var sheet = ss.getActiveSheet();
var db = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("DATA");
var vals = db.getRange("B:B").getValues();
var last = vals.filter(String).length;
var ui = SpreadsheetApp.getUi();
var user = ui.prompt("Notes:");
var values = [[
sheet.getRange("I7").getValue(), //Date/Time
sheet.getRange("N16").getValue(), //Amount
sheet.getRange("E17").getValue(), //Initials
user.getResponseText(), //Notes
]];
db.getRange(last+1,2,1,4).setValues(values);
}

One solution is to utilize a checkbox as the Accept button, and use an onEdit(e) simple trigger to run your Accept() function.
See the checkboxButtons_ script for sample code.

Related

Trying to limit an existing script to a specific sheet

My first issue is this, I have an items log sheet where I want to add and manage individual unique items in our inventory. I created a data validation dependent dropdown list for a main category and found out how to build a script to dynamically create a secondary category dropdown list based on the selected main category.
For Example:
If cell B2 (Main Category) is set to Carabiner (based on data validation range on another sheet) THEN cell C2 (secondary Category) will dynamically create a dropdown list relative to the Carabiner main category (i.e. locking, non-locking)
That is simple enough if you only have one row to create the dropdown lists, but I wanted to be able to pick from a secondary category list in each row dependent on which was picked in the main category cell.
I found a video of a script that did just that and got it working just fine.
Now the problem is that the script runs data validation on every other sheet. How can I limit the script to only run on a specific sheet?
Here is the script:
function onEdit() {
// this line just refers to the current file var start = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet(); var current = start.getActiveCell()
// var to refer to the worksheets -lists is where the data validation range will come from, and main is where we want to use that data validation range var list = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Indirect_Categ_Ranges") var main = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Items_Log");
// has the user selected a category? Refers to the column number in the Items_Log sheet where the user has picked the main category
if (current.getColumn()==2)
{
//to copy the selected sub-category -the 2,1 is the row and column on the Indirect_Categ_Ranges sheet where this script will dynamically update the main category picked to define what the indirect function will display in the next column
var choice = current.getValue()
list.getRange(2,1).setValue(choice)
//clear any validation -the 2,3,1000 looks to start clearing validation at row 2, column 3 and down for up to 1000 entries
main.getRange(2,3,5000).clearDataValidations()
// create the rule - var_point defines the offset number of rows and columns to shift to initiate the dynamic dependent dropdown list, the var_items defines where the to look for the range to build the dropdown list
var point = current.offset(0,1)
var items = list.getRange(2,2,50)
var rule = SpreadsheetApp.newDataValidation().requireValueInRange(items,true).build();
point.setDataValidation(rule)
}
}
Also, is there a way to have the clear validations to run no matter how many rows there are?
The function will run anytime there's an edit and there's nothing you can do to stop that. You can, instead, terminate execution preemptively if it's not the sheet you care about.
The event object tells you which range was edited. You can get that range's sheet to know which sheet was edited. If the name matches, then execute the other stuff.
function onEdit(e) {
if (e.range.getSheet().getName() === 'Items_Log') {
// Data validation
}
}
It's not great practice to use .getActiveRange() or .getActiveSheet() when you want what was actually edited because there is a chance, however small, that the edited range may differ from the active range at the time of function execution.
Explanation:
You need to take advantage of the event object.
That object contains relevant information to the edits you make.
For example:
e.source is equivalent to SpreadsheetApp.getActive()
e.range is equivalent to .getActiveCell().
To run the code only for a particular sheet, in this case Items_Log, add a condition to check if the name of the active sheet matches that name:
if (current.getColumn()==2 && start.getName()=="Items_Log")
where start is the active sheet:
var start = e.source.getActiveSheet();
Solution:
function onEdit(e) {
var start = e.source.getActiveSheet();
var current = e.range;
var list = e.source.getSheetByName("Indirect_Categ_Ranges")
var main = e.source.getSheetByName("Items_Log");
if (current.getColumn()==2 && start.getName()=="Items_Log")
{
var choice = current.getValue()
list.getRange(2,1).setValue(choice)
main.getRange(2,3,5000).clearDataValidations()
var point = current.offset(0,1)
var items = list.getRange(2,2,50)
var rule = SpreadsheetApp.newDataValidation().requireValueInRange(items,true).build();
point.setDataValidation(rule)
}
}
Thanks for the help. after looking at your suggestions and trying a couple things, I found that simply adding the code:
&& start.getName()=="Items_Log")
To the end of the line:
if (current....
Worked and solved the issue.

Google Sheets as Database

I'm trying to enter some data on a sheet in google sheets create a button that submits data onto another sheet. The other sheet will be like my database.
I have little to no JS experience. I'm able to create the button and link it my script but after that, I'm lost. This code worked well to get data to my database sheet. The problem is that the data stays on the same row and when I run the script the old data is erased.
function transfer() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var source = ss.getSheets()[0];
var destination = ss.getSheets()[1];
var range1 = source.getRange("B2");
range2.copyValuesToRange(destination,3,3,2,2);
range2.clearContent();
var range2 = source.getRange("C2");
range2.copyValuesToRange(destination,4,4,2,2);
range2.clearContent();
var range3 = source.getRange("D2");
range3.copyValuesToRange(destination,5,5,2,2);
range3.clearContent();
var range4 = source.getRange("C2");
range4.copyValuesToRange(destination,4,4,2,2);
range4.clearContent();
}
The other issue is I don't want the cells to be empty so I tried to set an alert.
var range1 = source.getRange("A2");
range1.copyValuesToRange(destination,2,2,2,2);
if (range1 ==!"");
Browser.msgBox("Please Enter A Date");
It prompted the msg box but still copied the data over.
Last I would like range1 to be like a unique ID. So if I put a value in A2 on my source sheet then it will auto-fill the other cells.
Here's the link if that helps.
https://docs.google.com/spreadsheets/d/1Zi7Oc0f5AlxcRRoFMM1Q1VkkM1Co6Yo8yYBY1TvkQMc/edit?usp=sharing
I recommend that you spend more time with the documentation. Personally, I've never considered putting buttons directly on the spreadsheet. I'd rather use the menu or a dialog or a sidebar. Also you generally pick up a lot of performance to replace the use of getValue() with getValues() where appropriate.
But here's another way to approach the problem your working on. Perhaps it will give something to think about. Have fun. Your buttons do look nice though. Very nice work.
function onOpen(){//This will put a post button on a menu
SpreadsheetApp.getUi().createMenu('My Menu')
.addItem('Post Data', 'postData')
.addToUi();
}
function postData() {//this will append data fromm 'DataEntry' to 'DB' with a timestamp spliced into it.
var ss=SpreadsheetApp.getActive();
var sh1=ss.getSheetByName('DataEntry');
var sh2=ss.getSheetByName('DB');
var rg1=sh1.getRange(sh1.getLastRow(),1,1,sh1.getLastColumn());
var vA=rg1.getValues();
vA[0].splice(0,0,Utilities.formatDate(new Date(),Session.getScriptTimeZone(),"E MMM dd, yyyy HH:mm:ss"));
sh2.appendRow(vA[0]);
}
This is what my DateEntry tab looks like:
This is what my DB tab looks like:
The post just adds the last row in DataEntry to the row after the last row in DB.

How to retain original range when updating data validation rule with google apps script

I am trying to write a google apps script that updates an existing data validation rule to toggle the "Show dropdown list in cell" option. I have it mostly working, it does change the "Show dropdown list in cell" option, however it is not retaining the original criteria range, which is on a different sheet. It appears to be changing the range to use the sheet the validation rule was from instead of the other sheet. The original data validation requires a value in the range Sheet2!A2:A, but when I run the below script, it changes to require a value in the range Base!A2:A instead.
function turnOffDropDown() {
var baseSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Base");
var templateRange = baseSheet.getRange("B2");
var rule = templateRange.getDataValidation();
var criteria = rule.getCriteriaType();
var args = rule.getCriteriaValues();
args[1] = false;
var newRule = rule.copy().withCriteria(criteria, args).build();
templateRange.setDataValidation(newRule);
}
Am I doing something wrong that it isn't retaining the original range, is there a way to specify the range I want on Sheet2, or is there a different way to update the "Show dropdown list in cell" option?
This seems like a bug and should probably be reported here. I've tried your approach as well as explicitly defining the range, but it consistently replaces the criteria sheet with the sheet in which the validation is being applied, while maintaining the specified cells. (E.g., the validation is being applied to Worksheet!C1, so CriteriaValues!A1:10 becomes Worksheet!A1:10.)
As a workaround, given that you're likely not changing the criteria type, you can try creating a new validation instead using requireValueInRange(). I've modified your code to create a "Toggle Dropdown" option. This does maintain the correct range.
function toggleDropDown() {
var baseSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Base");
var templateRange = baseSheet.getRange("B1");
var rule = templateRange.getDataValidation();
var args = rule.getCriteriaValues();
rule = SpreadsheetApp.newDataValidation().requireValueInRange(args[0], !args[1]).build();
templateRange.setDataValidation(rule);
}

How to create a button which clears data in spreadsheets

Currently I have a google spreadsheet document in which users are inputting data based on conditional formatting the cells are changing colors. I would like to create a button with which I can easy restore all the cells into their first state.
I have this script at the moment:
function clearRange() {
var sheet = SpreadsheetApp.getActive().getSheetByName('Sheet1');
sheet.getRange('B13:B30').clearContent();
}
Which works like a charm but the thing is that I have default data in the cells and when this scripts get applied it clears the default data.
Any ideas how to make such a script which will clear only the inputted data by the user.
It's not the solution you want but apart from what Scott suggested, which is the most straightforward approach, you could make a copy of your sheet in the unedited state. In this copy have a script which will just copy and paste the info into the sheet that the user fills in.
Then if you make changes you just need to update the template sheet and not the script.
This is a very basic script but should you'll get the idea.
function myFunction() {
var app = SpreadsheetApp;
var tempSheet = app.getActiveSpreadsheet().getSheetByName('Sheet1');
var inputSheet = app.openById('-----ID OF INPUT SHEET----').getSheetByName('Sheet1');
var tempData = tempSheet.getDataRange().getValues();
inputSheet.getRange(1, 1, tempData.length,
tempData[1].length).setValues(tempData);
}

Google Spreadsheet replacing a value by another value upon edit by user

I have just started to find out about Google Spreadsheet Scripts, but I can't figure out how I go about doing this:
I am looking for a script that replaces just one specific cell content value ("icecream") for another value ("donut") upon editting/adding.
So far I have found this piece of script on Stackoverflow, but this only works on a cell (not the value in the cell ie. "icecream").
And it only works on me clicking play (I want it to do it automatically, everytime a user enters the specific value into a box. So, if a user enters "icecream" into a cell, it needs to automatically be replaced with "donut"). It doesn't matter which cell the value "icecream" is in, just change the word "icecream" to "donut" automatically for the entire document, everytime someone types in the word "icecream".
Can someone help me?
function doTest() {
SpreadsheetApp.getActiveSheet().getRange('E3').setValue('donut');
}
This worked for me. Let me know if it works for you.
function onEdit(event) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
var active = sheet.getActiveCell();
var cell = active.getValue();
//if cell's value is equal to icecream then set the active's value to donut
if(cell === "icecream"){
active.setValue("donut");
};
};