deleteRow based off cell edit on another sheet of same spreadsheet - google-apps-script

WHAT I HAVE One google spreadsheet named "Script Test" with two sheets named "delete" and "non delete".
WHAT I NEED If a row in Col B on "non delete" is changed to 'DELETE' via the drop down menu, the row with the same Buy Number on "delete" will be deleted.
WHAT I HAVE TRIED
What has worked = By researching on stack I found an onEdit function that deletes a row on "delete" based on if a cell has a specific value. In this case, that value is 'DELETE'. The problem with this is, I can only get it to work if that cell is on the sheet "delete" rather than the sheet "non delete". If I'm working off of "non delete" and need to go back to "delete" to delete a row of information, I can just right click on the row number and manually delete it. So, this script isn't necessarily saving me time.
This script is as follows:
function onEdit(e) {
try {
var ss = e.source;
var s = ss.getActiveSheet();
if (s.getName() == 'delete' &&
e.range.columnStart == 1 && e.range.columnEnd == 1 && // only look at edits happening in col A which is 1
e.range.rowStart == e.range.rowEnd ) { // only look at single row edits which will equal a single cell
checkCellValue(e);
}
} catch (error) { Logger.log(error); }
};
function checkCellValue(e) {
if (e.value == 'DELETE') {
e.source.getActiveSheet().deleteRow(e.range.rowStart);
}
}
What has not worked = I fiddled with the script a bit to have it read Col F on "delete" and in Col F I have an Index Match of Col B in "non delete". However, this does not delete the row on "delete" when Col F changes to 'DELETE'. Now I'm not 100% on this but I can pretty much deduce that this is happening because Col F isn't being "edited", rather the formula inside of it is "updating". I've also tried fiddling with other scripts that I found on stack but none seem to have gotten me as close as the script above.
THINGS TO THINK ABOUT
First of all, thanks for any help you guys can give me. Just before posting this question, I came across a filter function that I think may be a direction to head in if I'm right about the Index Match. I found one function that hides rows based on a filter but I would need the row to be deleted so I'm assuming that is as simple as switching hideRows with deleteRows. I've tried adding screenshots of what I need done but I don't have enough reputation. I can and will add a link to a copy of the spreadsheet if that helps. Once again, thanks for any tips or guidance.
Copy of Script Test

Use the getSheetByName() method to get the delete sheet.
function checkCellValue(argRowToDelete) {
if (e.value == 'DELETE') {
var toDeltSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("delete");
toDeltSheet.deleteRow(argRowToDelete);
}
}
If you want a separate function just for deleting the row, maybe do the check for the 'DELETE' text in the main function, and pass the row number to the delete function:
I've tested the following code, and it works. When the drop down list is used to select 'DELETE' in the 'non delete' sheet, it deletes the corresponding row in the 'delete' sheet.
I made multiple changes to the code. Even though this code deletes a row in a sheet different from where the edit is taking place, there is still a potential problem. Once a row in the 'delete' sheet is deleted, the rows will shift. If you start deleting rows at the top or middle, then every row below the deleted row is no longer synced with the rows in the 'delete' sheet.
So, this answers your question, but now you have yet another problem.
function onEdit(e) {
try {
var ss = e.source;
var s = ss.getActiveSheet();
var colStart = e.range.columnStart;
var colEnd = e.range.columnEnd;
Logger.log('colStart: ' + colStart);
Logger.log('colEnd: ' + colEnd);
var thisRow = e.range.getRow();
Logger.log('s: ' + s.getName());
//Avoid looking at multi column edits. If column start and column end is same column,
//then not a multi column edit
var editedFromNonDelete = (s.getName() === 'non delete');
Logger.log('editedFromNonDelete: ' + editedFromNonDelete);
var editedFromColB = (colEnd === 2) && (colStart === colEnd);
// only look at edits happening in col B
if (editedFromNonDelete && editedFromColB) {
Logger.log('e.value: ' + e.value);
if (e.value === 'DELETE') {
fncDeleteRow(thisRow);
};
}
} catch (error) {
Logger.log(error);
}
};
function fncDeleteRow(argRowToDelete) {
var toDeltSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("delete");
toDeltSheet.deleteRow(argRowToDelete);
};

After testing out the filter function for a couple minutes, I've pretty much got it to do what I needed. Thanks anyways!

Related

In Google Sheets onedit function, abort function if variable cell value is not blank

I have a search page driven by checkboxes and an onEdit function in a Google Sheet. It allows users to search a dataset, select a single row (record) from the search results, and append a user code if assuming responsibility for that record. It includes a dialog box asking them if they wish to proceed before the function executes. If yes, it proceeds and resets. If no, it aborts and waits for a new command.
The data setup is:
Col A
Col B
Col C
Col D
Col E
Chkbox
Name
Age
Place
User
The user code data enters in column E of the dataset. What I need is for the onEdit function to abort if another user has already assumed responsibility for that record. In other words, say User A searches and selects a record for "Sarah Stout," and clicks the checkbox to code that as "USER A" to take responsibility for Ms. Stout (and USER A will appear for anyone else's subsequent searches). However, User B has already done the same thing, so in column E, it already says USER B. I want the function to abort so that User A override that from the search page.
I tried:
if (NOT(ISBLANK($E$6))) return;
because the first search result would appear in row 6 (but the first row with a checkbox), but that didn't work. I just don't understand the syntax as well in this, so I don't know how to specify the column. Here's the onEdit code
function onEdit(e) {
if (e.range.columnStart != 1) return;
if (e.value != 'TRUE') return;
var sheet = SpreadsheetApp.getActive();
var ignored_sheets = ['Sheet2','Sheet3','ReportOutput'];
if (ignored_sheets.includes(sheet.getName())) return;
var row_index = e.range.rowStart;
var row = sheet.getRange('B' + row_index + ':D' + row_index).getDisplayValues().flat();
if (row[1] == '') return;
var result = SpreadsheetApp.getUi()
.alert("Do you wish to mark this record?", SpreadsheetApp.getUi().ButtonSet.OK_CANCEL);
if (result != SpreadsheetApp.getUi().Button.OK) {
sheet.toast("Request canceled.");
sheet.getActiveCell().setValue(false);
return;}
sheet.toast("Request initiated.");
sheet.getActiveCell().setValue(false);
sheet.getRange('B3').clearContent();
sheet.getSheetByName('ReportOutput').appendRow(row);
sheet.getSheetByName('Sheet1').setActiveCell('B3');
}
Does anyone have any ideas on how to make this work?
Where am I going wrong?
I know I'll need an IF statement, and I know I'll need something like this if they check the box when they shouldn't:
{sheet.toast("Unable to overwrite data.");
sheet.getActiveCell().setValue(false);
return;}
Suggestion
If I've understood your question clearly, you want your onEdit(e) function to abort running if the value on column E, based on the selected row, already contains a user code.
Sample Code:
You need to add these lines of code after the var sheet = SpreadsheetApp.getActive(); line.
//If the selected row on column E is not blank
if(e.range.getSheet().getRange(e.range.getRow(),5).getValue().length > 0){
sheet.toast("Process aborted.\ \""+e.range.getSheet().getRange(e.range.getRow(),5).getValue()+"\" has already taken responsibility for \""+e.range.getSheet().getRange(e.range.getRow(),2).getValue()+"\"");
sheet.getActiveCell().setValue(false);
return;
};
Sample Demonstration
Sample Sheet:
Result:
Say "User A" checks the box for Sarah Stout but the column E for Sarah already contains USER B
After a split second

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();
}
}

Shared Spreadsheet with two instances of onEdit, deleting wrong row

I'm trying to create a lead management format using Google Sheets & Apps Script.
The apps script is checking whether the value in column M of sheet Propsect or Interested has changed and depending on the value, moving the row to the respective sheet (Interested, Postponed, Lost, or Booked)
The spreadsheet is shared with my team who'll make changes and with multiple users editing at a time.
Now, the problem is that, as soon as two onEdits are triggered, and if both require rows to be moved, the first instance runs properly but the second one removes the wrong row.
Eg: In sheet Prospect, Row 2 & Row 3 have status changed to Lost & Postponed at the same time. Now, Lost gets triggered properly, however, the Postponed instance deletes the 4th row (now the 3rd row, as row 2 was removed before).
I have tried to add in lockservice to the code so that only one instance is running but that doesn't seem to solve the problem as the event object is still considering the un-updated row number.
Even tried adding flush() at the start & end of the code but didn't work either.
You can access the spreadsheet here.
My code is as follows:
function Master(e) {
var lock = LockService.getScriptLock();
var SS = e.source;
var Sheet = e.source.getActiveSheet();
var Range = e.range;
if(Sheet.getName() == "Prospect" && Range.getColumn() == "13" || Sheet.getName() == "Interested" && Range.getColumn() == "13"){
moveRows(SS,Sheet,Range);
}
lock.releaseLock();
}
function moveRows(SS,Sheet,Range) {
var val1 = Sheet.getRange(Range.getRow(),1,1,10).getDisplayValues();
val1 = String(val1).split(",");
var tar_sheet = SpreadsheetApp.getActive().getSheetByName(Range.getValue());
var row = tar_sheet.getRange(tar_sheet.getLastRow()+1,1,1,val1.length).setValues([val1]);
Sheet.deleteRow(Range.getRow());
}
}
Is there any way for the second onEdit to run only after the first has completed execution? I guess, if that could happen, the problem would be solved?
I Hope I have been able to convey my question properly.
Issue:
Event object e passed to a onEdit(e) is not altered, when two or more edits are done at the same time and the first edit alters the next edit's row number- making e.range.rowStart of the second+ edit unreliable at the time of it's execution.
Possible Solutions:
Do not delete the rows immediately. Mark them for deletion(save the range string in properties service) and delete them later(time trigger), when document is not in use.
Alternatively, Add code guards: Check range.getValue()===e.value. If they're equal, continue to moveRows else keep offseting the range by -1 row until they're both equal.
References:
PropertiesService
Range#offset
I guess you should trigger only one function based on user interaction and then inside that perform conditional operations.
Something like this:
function onEdit(event_object) {
var sheet = event_object.range.getSheet();
var row = event_object.range.getRow();
var column = event_object.range.getColumn();
if (sheet.getName() == "Sheet1") {
// perform operations when Sheet1 is edited
} else if (sheet.getName() == "Sheet2") {
// perform operations when Sheet2 is edited
}
}
Reference :
https://developers.google.com/apps-script/reference/spreadsheet/range
https://developers.google.com/apps-script/guides/triggers/events#edit

delete entire row if any cell is empty

Just like the title says. I found a solution if one column is blank, but not if any are blank.
I crafted this with the help of another post, though I don't have a clue re: google scripts.
function onEdit(e) {
//Logger.log(JSON.stringify(e));
//{"source":{},"range":{"rowStart":1,"rowEnd":1,"columnEnd":1,"columnStart":1},"value":"1","user":{"email":"","nickname":""},"authMode":{}}
try {
var ss = e.source; // Just pull the spreadsheet object from the one already being passed to onEdit
var s = ss.getActiveSheet();
// Conditions are by sheet and a single cell in a certain column
if (s.getName() == 'Sheet1' && // change to your own
e.range.columnStart == 1 && e.range.columnEnd == 99 && // only look at edits happening in col C which is 3
e.range.rowStart == e.range.rowEnd ) { // only look at single row edits which will equal a single cell
checkCellValue(e);
}
} catch (error) { Logger.log(error); }
};
function checkCellValue(e) {
if ( !e.value || e.value == 0) { // Delete if value is zero or empty
e.source.getActiveSheet().deleteRow(e.range.rowStart);
}
}
problem is, I have no idea how to actually "use" it
Demo of the setup.
e.range.columnStart and e.range.columnEnd need to be equal to each other in this case
In the spreadsheet that you'd like to use this in, click on Script Editor in the Tools menu. Select Blank Project in the dialog that comes up, and then replace the code in the window with the code you've posted above. Save and name your project, then click Current Project's Triggers in the Resources menu. Click the link to add a new trigger. You'll see a series of drop down boxes, select the function (onEdit()), From Spreadsheet (the event source), and "On Edit" (the event). You have the option to set failure notifications. Click save to save the trigger. You'll be prompted to provide authorization, once you do, the script should run as expected.