Checkboxes as counters in multiple columns and sheets (but not al) - google-apps-script

Here is the sample sheet
What I need to do is make the checkboxes in the camera and check-in columns (green) work as a counter. When you click on checkbox, it should add one to the total of the column next to it. The trouble is that this has to work in all the sheets, but not in all the columns (I use checkboxes for other things in other columns). And the columns where that kind of data rests isnt exactly the same for all sheets.
How do I make it so that the ADD + 1 button works in a sheets, but only in the columns that I want?
Thanks for the help. Feel free to edit sheet and put code directly on it if you are willing to help.

You can do something like this:
function onEdit(e) {
const sh = e.range.getSheet();
const shn = ["Sheet0","Sheet1","Sheet2"];//you pick the sheets
const cols = [1,5,1];//you pick the columns
const idx = shn.indexOf(sh.getName());
if(~idx && e.range.columnStart == cols[idx] && e.value == "TRUE") {
e.range.offset(0,1).setValue(e.range.offset(0,1).getValue() + 1);
e.range.setValue("FALSE");//Reset the checkbox back to false so it behaves more like a switch
}
}

Related

Add note based on value of another cell

For all I researched, I know there is not an integrated funcion that can do this, but I know it can be done with scripts. I want a script to create note in one specific cell based on another specific cell value.
example in the sheet I'll share
https://docs.google.com/spreadsheets/d/1I7ictcN_tEQyZXFljAVFm1VqZzafIezF1xR5AYW5pVc/edit?usp=sharing
I want the note on D12 to reflect any changes in the value of A8 for example.
Best case would be, if I could use this script as a function calling whatever cell I wanted.
I saw some examples using onEdit as trigger but I don't know if that would work because the cell in question wouldnt even be touched.
I also saw some exemples using get range to target the cells but then I would have to make a new script for each iteration.
Recording User Edits in A8 in the Notes of D12
function onEdit(e) {
const sh = e.range.getSheet();
if(sh.getName() == "Sheet name" && e.range.columnStart == 1 && e.range.rowStart == 8) {
sh.getRange(12,4).setNote(sh.getRange(12,4).getNote() + '\n' + e.value);
}
}

Sort and Move Data Between Tabs Based On Verification Step

Evening everyone!
Below I have the link to my test sheet
https://docs.google.com/spreadsheets/d/12I9u5XoYBQ7qeaTtqBRjEaz8YpLK04DgHlLnSuw4IDo/edit#gid=0
My goal, works between two tabs on the same sheet. One tab, "Reference" has a list of sorted data with a unique shipment reference attached. In an offset column, I have the unique shipments populated, with a checkbox next to each unique shipment label.
image description
Is there a way via with a menu add on or activated cell script, to move all items from columns A:E, that match the selected the checkbox next to a specific shipment reference, onto the second tab labeled "Processing"? I would vision that once items are removed to be transferred from Tab 1 to 2, that the rows will all shift up to avoid blank space, and items being added to Tab 2 will add to the next open row of the doc.
Please let me know if this makes sense and if anyone has any ideas, thank you!
Below is what we had so far:
function Transfer() {
var sh = SpreadsheetApp.getActive();
if(sh.getName() == "Reference" && e.range.columnStart == 8 && e.range.rowStart > 1 && e.value == "TRUE") {
let rg = sh.getRange(e.range.rowStart,1,1,5)
let vs = rg.getValues();
const osh = e.source.getSheetByName("Processing");
osh.getRange(osh.getLastRow() + 1,1,1,5).setValues(vs);
rg.deleteCells(SpreadsheetApp.Dimension.ROWS);
e.range.setValue("FALSE");
}
}

Hide rows based on multiple checkbox values

The project I am working on is to calculate costs of remaining sets in a mobile game. It has a spreadsheet with a list of all the sets, and checkboxes for all 5 pieces, Columns B:F. I want to include the option to hide all sets that are completed, so all Checkboxes are checked. This is done via another Checkbox, H16.
I have modified the Checkbox values to use Yes and No.
I have never used Google Apps Script before, and am very new to coding in general. I think what I need is, to use onEdit, then every time a cell is edited, check if H16 is TRUE, then scan through each row to check the B:F values. If all are true, hide that row. I don't know the best way to type that out, though.
Bonus points, I also want to include a reset checkbox, so when checked, set all values in B:F to false, and show the rows.
Here is a link to the spreadsheet
EDIT: My current GAS code, which isn't much because I don't know what I am doing:
function onEdit(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
var maxSheet = 100;
if(H16 == true)
{
for(i, i<=maxSheet, i = i + 1) {
}
} else {
sheet.showRows(1, maxSheet);
}
}
Hiding rows when all five columns are true
This may not be exactly what you wish but I think it's close. I did not use yes and no values because it's easier for me to leave it true false but you can change that. I'm using Sheet0 and you can change that as well. I used less rows so you can also change that. But the basic idea is that when H16 is clicked it hides rows that have all five columns checked.
Code:
function onEdit(e) {
e.source.toast('entry');//debug
const sh = e.range.getSheet();
const sr = 2;//data start row
const lr = 15;//last row of data
sh.getRange('K1').setValue(JSON.stringify(e));//debug
if(sh.getName() == "Sheet0" && e.range.columnStart == 8 && e.range.rowStart == 16 & e.value == "TRUE" ) {
e.source.toast('past if');//debug
e.range.setValue("FALSE");
let vs = sh.getRange(sr,2,lr - sr + 1, 5).getValues();
vs.forEach((r,i) => {
if(r[0] && r[1] && r[2] && r[3] && r[4]) {
e.source.toast(`Row: ${sr + i}`);//debug
sh.hideRows(sr + i);
}
});
}
}
Image of Sheet0:
I use K1 to provide me with the event object while I debug the script. And I also use the e.source.toast in several location to get an idea of what is going on.
Animation:
an incomplete description of the event object
You can get a better understanding of the event object by using the JSON.stringify code as show in my example.
Most new people want to run the code from the script editor so I'll tell upfront that unless you provide the event object to populate the e then it's not going to work. Just copy and past it and get you unique stuff set like sheet name and data space and then proceed to edit the page and figure out how it works.

How do I correctly combine 2 script, so both are onEdit? [duplicate]

This question already has an answer here:
Sheets Filter Script across multiple tabs displaying blank rows on master sheet, and also causing other scripts to stop functioning
(1 answer)
Closed 2 years ago.
I know I've seen very similar questions asked, but none I've found so far fit where I'm stuck.
I originally had 3 scripts I wanted to combine. I attempted to combine 1 (and this has some weird issues, which I'll get to below***), and now have 2 scripts that I'd like to both be triggered by onEdit, but I can't seem to combine them correctly. The reason I assume is likely due to how the 2 scripts differ in their writing (which is also why a lot of the solutions I've found so far haven't solved it for me).
Here are the 2 scripts:
This one creates a functional checkbox based on criteria
function onEdit(e) {
if (e.range.getColumn() == 2) {
var sheet = e.source.getActiveSheet();
if (e.value === "Tech Note" ||
e.value === "Intake Process")
sheet.getRange(e.range.getRow(),3).insertCheckboxes();
else
sheet.getRange(e.range.getRow(),3).removeCheckboxes();
}
}
This one creates a master sheet of open action items from all the tabs
***This one got weird when I attempted to combine what it did for the "Tech" and "Intake" columns of "Open Action Items" tab, A3 & E3. It sorts some of the info at the top (rows 3-5), but then the info from "E03-O" sheet at the bottom (rows 51-55).
I did what I could to eliminate the empty rows, but wasn't able to get that down either. My work around was going to be sorting, but now this happened...
function myfunction() {
//set variable
const masterSheet = "Open Action Items";
const mastersheetFormulaCell1 = "A3";
const checkRange2 = "$J3:$J"
const ss = SpreadsheetApp.getActiveSpreadsheet();
let formulaArray1 = filteredListOfSheets.map(s => `iferror(FILTER('${s.getSheetName()}'!${dataRange1}, NOT(ISBLANK('${s.getSheetName()}'!${checkRange1}))),{"","",""})`);
let formulaText1 = "=Sort({" + formulaArray1.join(";") + "},2, false)";
ss.getSheetByName(masterSheet).getRange(mastersheetFormulaCell1).setFormula(formulaText1);
}
Here's an example sheet
Any help or guidance much appreciated!
Why not just call myfunction(); in onEdit(e)
It helps to keep code organized if you write short functions and include them in the function you need them called.
function onEdit(e) {
if (e.range.getColumn() == 2) {
myfunction();
var sheet = e.source.getActiveSheet();
if (e.value === "Tech Note" ||
e.value === "Intake Process")
sheet.getRange(e.range.getRow(),3).insertCheckboxes();
else
sheet.getRange(e.range.getRow(),3).removeCheckboxes();
}
}

Cleaning up and trying to consolidate an on edit code

This has a lot of what I assume is redundant code. I While I am making my way to understanding more of how this works, I am a beginner at crafting these from piecemealing what I have read through to have this semi workable document here:
https://docs.google.com/spreadsheets/d/1FqZNZX3zGRscG6teizjEFjmJMLKQmMLRCi6BmUL2T34/edit?usp=sharing
the Master sheet combines the other sheets into itself and automatically updates based off of the entries in the other tabs.
Question 1:
var s = SpreadsheetApp.getActiveSheet();
var r = s.getActiveCell();
var nextCell = r.offset(0,-1 );
//Movement SECTION
if( s.getName() == "Movement" )
{
if( r.getColumn() == 2 )
{nextCell.setValue("Spacing");}
}
//Defense SECTION
if( s.getName() == "Defensive" )
{
if( r.getColumn() == 2 ) {
nextCell.setValue("Defense");}
}
//Offense SECTION
if( s.getName() == "Offensive" )
{
if( r.getColumn() == 2 )
{nextCell.setValue("Offense");}
}
//erases category if the exercise is blank
if(s.getName() == "Movement" | s.getName() == "Defensive" | s.getName() == "Offensive")
{
if( r.getColumn() == 2 )
{if (r.isBlank() | r == "")
{nextCell.clearContent()
}
}
}
Is there a cleaner way to combine all of these sections? the purpose is that on edit, the first column will reflect a category depending on the tab. I currently plan to have Column A locked and hidden, so it's less of an eyesore in the actual tab.
(Bonus Question: is there a way to automatically add the category to the query on the master sheet? [the cell is B4])
Question 2:
Is it faster to use protected ranges, or would it be better to implement a script to prevent changes to certain ranges and push a window to explain what to do?
I am going to go after your 'bonus points' question below, but first:
I use JSON in some of my functions like this to both simplify the logic and make it more readable. Also, I switched the logic so it checks for a blank cell first so it doesn't unnecessarily perform a write before it deletes the content
function onEdit(e){
//checkAll(e)
var s = SpreadsheetApp.getActiveSheet();
var r = s.getActiveCell();
var nextCell = r.offset(0,-1 );
var sheetname = s.getName();
var column = r.getColumn();
var check =
{
Movement: "Spacing",
Defensive: "Defense",
Offensive: "Offense" //if you add more sheets just add them here
}
if(r.isBlank() && check[sheetname] && column == 2) { // !r.getValue() is not necessary, I believe.
nextCell.clearContent()
} else if(check[sheetname] && column == 2) {
nextCell.setValue(check[sheetname])
}
}
Next, before the above will work you are going to have to get into your script and edit/isolate your onEdit(e) function. At the moment it wraps around another function and then everything in the 'Location.gs' file is not wrapped inside a function at all and was throwing errors. I didn't try to error check that part or figure out what it was doing.
Now, lastly, going after your 'for bonus points' question, a way that you can accomplish this that only uses google sheets, some duct tape and super glue, and no google scripts:
EDIT:
Okay, so I played around with an even easier way that doesn't involve any of the duct tape & super glue. Simply replace the formula in your query with the following nested query:
=query({{query(Movement!B3:C,"select 'Space',B,C where B != ''")};{query(Defensive!B3:C,"select 'Defensive',B,C where B !=''")};{query(Offensive!B3:C,"select 'Offensive',B,C where B !=''")}},"select * where Col2 !=''")
This is short, simple, and doesn't involve having any hidden sheets at all and I think it's relatively easy to follow. Performance wise this should be pretty zippy too.
THIS ANSWER BELOW WORKS (but isn't nearly as elegant)
Create a new sheet in this workbook and in column A from row 1 to row 100 (or however many rows you think you might have as a maximum), put "Offensive" in every row., in column B put "Defensive" in every row, and in column C, put "Space" in every row. I've got an example here: copied example sheet in the 'Labels' sheet.
Next, in the cell where you have your query, put the following (for reference, the above sheet I called 'Labels':
=query({{array_constrain(Labels!A1:A100,counta(Movement!B3:B),1),filter(Movement!B3:C,Movement!B3:B<>"")};{array_constrain(Labels!B1:B100,counta(Offensive!B3:B),1),filter(Offensive!B3:C,Offensive!B3:B<>"")};{array_constrain(Labels!C1:C100,counta(Defensive!B3:B),1),filter(Defensive!B3:C,Defensive!B3:B<>"")}},"select *")
This should work for what you want to do without having to have a column for the sheet name. The example in the sheet referenced above is a working example.
I think this is it:
function onEdit(e) {
const sh=e.range.getSheet();
const shts=["Movement","Defensive","Offensive"];
const vals=["Spacing","Defense","Offense"];
const idx=shts.indexOf(sh.getName());
if(idx!=-1 && e.range.columnStart==2 && e.range.getValue()!='') e.range.offset(0,-1).setValue(vals[idx]);
if(idx!=-1 && e.range.columnStart==2 && e.range.getValue()=='') e.range.offset(0,-1).setValue('');
}