Google Apps Scripts, onEdit function wont gets no value on edited range - javascript

I'm making a task manager in Google Sheets that when a task is created it sends the task that was just made to a target Sheet, I was able to get it to send the data just fine to the target sheet but my problem lies with what i wanted to do with that edit made on the target sheet. On the target sheet I want it to grab that info that was just inputted and place it in the next available row, but when I test my code It doesn't wanna work correctly.
My Code(for the target sheet):
function onEdit (e) {
var ss = SpreadsheetApp.openById('SheetIdHere');
var sheet = ss.getSheets()[0];
var sheetRows = sheet.getMaxRows();
var openRow = 0;
for (var i =4;i < sheetRows;i++) {
var currentValue = sheet.getRange(i,2).getValue();
if (currentValue == "") {
openRow = i;
break;
}
}
var lastEdit = sheet.getRange(1000, 2, 1, 3);
var lastEditValues = lastEdit.getValues();
var openRange = sheet.getRange(openRow, 2, 1, 3);
var openA1 = openRange.getA1Notation();
sheet.getRange(openA1).setValues(lastEditValues);
}
So far from my tests, the for loop to find the next available row is working fine because when I make another change on the sheet afterward it will grab the edited information fine and input it into the next row, but on the initial onEdit fire it will just grab the values from the previous test at row 1000 and put it into the next row instead of the new values.
I feel like there is a mistiming perhaps on when it grabs the values because if I clear the sheets and send a new task nothing appears on the next available row but I might be mistaken on that feeling.

Related

How can multiple tabs be created from a range using Google Apps Script in a Google Sheet?

I need to create a custom function to create multiple tabs in a Google Sheet based on the values in column A. How do I do this?
This is for tracking the data on particular e-commerce products. The name of them are in the first column of the first tab. Each entry in column A needs to have a tab labeled as the same name.
I don't know how to do this.
The output should be new tabs created labeled by each cell in column A.
Copy and paste this code in the script editor.
function createSheet(sheetIndex, value){
// Check if cell is empty after editing
if (value.oldValue){
value = 'Sheet' + (sheets.length - sheetIndex);
}
var sheets = SpreadsheetApp.getActiveSpreadsheet().getSheets();
// If sheet already exits just change the sheet name
if (sheets[sheetIndex]) {
sheets[sheets.length - sheetIndex].setName(value)
} else {
SpreadsheetApp.getActiveSpreadsheet().insertSheet(value);
}
}
function onEdit(e) {
var sheet = e.source.getActiveSheet();
var sheets = SpreadsheetApp.getActiveSpreadsheet().getSheets();
// Check if the sheet being edited is the first sheet and first column (A)
if (sheet.getSheetId() == sheets[0].getSheetId() && sheet.getActiveCell().getColumn() == 1) {
createSheet(e.range.getRowIndex(), e.value)
}
}
You can extend the code to support deletion; it's a bit tricky though because there is no onDelete event. You can compare the sheets array with the values in the column to determine which sheet delete.
Creating Multiple Sheets with Templates and Position
Open a new Spreadsheet and go to script editor
Load the Code and Save it
Create the sheet named "Sheet Data"
Create the three templates
Run createTrigger()
Close the Spreadsheet
Open the Spreadsheet give it some time and it will create all of the sheets without bothering the templates or the Sheet Data sheet.
The Code:
function onOpen() {
SpreadsheetApp.getUi().createMenu("My Menu")
.addItem('Create onOpen Trigger', 'createTrigger')
.addToUi();
}
//This is where the sheets get created
function startUp() {
var ss=SpreadsheetApp.getActive();
var templateA=["A","B","C"];
templateA.forEach(function(name){ss.getSheetByName(name).hideSheet();});
var sh=ss.getSheetByName("Sheet Data");
var shts=ss.getSheets();
var shtnames=shts.map(function(sh){if(!sh.isSheetHidden()){return sh.getName();}}).filter(function(e){return e});
var hdnshts=shts.map(function(sh){if(sh.isSheetHidden()){return sh.getName();}}).filter(function(e){return e});
var rg=sh.getRange(2,1,sh.getLastRow()-1,sh.getLastColumn());
var vA=rg.getValues();
vA.sort(function(a,b){return a[2]-b[2];});
vA.forEach(function(r){
if(shtnames.indexOf(r[0])==-1 && r[0]!=sh.getName()) {
ss.insertSheet(r[0], r[2], {template:ss.getSheetByName(r[1])});
}
shts=ss.getSheets();
shtnames=shts.map(function(sh){if(!sh.isSheetHidden()){return sh.getName();}}).filter(function(e){return e});
});
var end="is near";
}
function isTrigger(funcName){
var r=false;
if(funcName){
var allTriggers=ScriptApp.getProjectTriggers();
for(var i=0;i<allTriggers.length;i++){
if(funcName==allTriggers[i].getHandlerFunction()){
r=true;
break;
}
}
}
return r;
}
//Installable onOpen trigger
//run this function first
function createTrigger() {
var ss=SpreadsheetApp.getActive();
if(!isTrigger('startUp')) {//This keeps you from creating unnecessary triggers
ScriptApp.newTrigger('startUp').forSpreadsheet(ss).onOpen().create()
}
}
//This is useful for testing
function deleteSheets() {
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName("Sheet Data");
var rg=sh.getRange(2,1,sh.getLastRow()-1,sh.getLastColumn());
var vA=rg.getValues();
vA.forEach(function(r){
ss.deleteSheet(ss.getSheetByName(r[0]));
});
}
The Creation Sheet:
Three Templates:
I just made these templates up as simple examples to assist in the creation of your sheets. I find that non programmers really excel at this process making extremely fancy spreadsheets. For me, I keep them simple.
I tried using this gif to show how the files are create with the spreadsheet opens up the first time. It won't create additional sheets of the same name. But if you add additional names it will create them. It's not capable of deleting sheet names that have been removed, so there's something you can do for yourself.
This function below creates a new sheet every time a cell from column A in your original sheet is edited, only if another sheet with this name does not exist already. To accomplish that, and onEdit trigger is used, as well as the corresponding event object:
function onEdit(e) {
var firstSheetName = "Sheet1"; // Name of your original sheet, change accordingly
var range = e.range; // Edited cell
var ss = e.source; // Edited spreadsheet
var sheetName = range.getSheet().getName(); // Name of edited sheet
var value = range.getValue(); // Value of the edited cell
// Checks whether edited sheet is the original one, and edited column is A:
if(sheetName == firstSheetName && range.getColumn() == 1) {
// Checks whether a sheet with this name exists:
var sheetExists = ss.getSheets().some(function(sheet) {
return sheet.getName() == value;
});
// Create new sheet only if a sheet with this name it doesn't exist:
if(!sheetExists) {
ss.insertSheet(value);
range.activate(); // Go back to original edited cell
}
}
}
See the comments in the shared script to understand what the code is doing, step by step.
In this example, the original sheet is called "Sheet1". Change it accordingly if that's not your case.
I hope this is of any help.

Move Row to Other Sheet Based on Cell Value

I have a sheet with rows I'd like to move to another sheet based on a cell value. I tried following this post's solution (refer below), but I'm having trouble editing the script towards what I want it to do.
I'm doing Check-Ins for an event. I would like to be able to change the value in Column F, populate Column G with the time the status changed, and for the row data to migrate to the Attendee Arrived sheet.
I believe the script already does this, but one has to run it manually. It also takes care of deleting the row data in A (Event) after migrating it to B (Attendee Arrived).
My question is could someone please help me set it up in order for script to run continuously (on edit), and also accomplish all of the above if I missed something?
I don't believe the script will respect the drop down format as it runs so I'm willing to manually type in something. It'd be cool if it could stay that way though - makes it easier for one.
Here's the sheet I'm testing on.
https://docs.google.com/spreadsheets/d/1HrFnV2gFKj1vkw_UpJN4tstHVPK6Y8XhHCIyna9TLJg/edit#gid=1517587380
Here's the solution I tried following. All credit to Jason P and Ritz for this.
Google App Script - Google Spreadsheets Move Row based on cell value efficiently
Thank you D:
function CheckIn() {
// How Many Columns over to copy
var columsCopyCount = 7; // A=1 B=2 C=3 ....
// What Column to Monitor
var columnsToMonitor = 6; // A=1 B=2 C=3 ....
//TARGET SPREAD SHEETS
var target1 = "Attendee Arrived";
//Target Value
var cellvalue = "Attendee Arrived";
//SOURCE SPREAD SHEET
var ss = SpreadsheetApp.openById('1HrFnV2gFKj1vkw_UpJN4tstHVPK6Y8XhHCIyna9TLJg');
var sourceSpreadSheetSheetID = ss.getSheetByName("Event");
var sourceSpreadSheetSheetID1 = ss.getSheetByName(target1);
var data = sourceSpreadSheetSheetID.getRange(2, 1, sourceSpreadSheetSheetID.getLastRow() - 1, sourceSpreadSheetSheetID.getLastColumn()).getValues();
var attendee = [];
for (var i = 0; i < data.length; i++) {
var rValue = data[i][6];
if (rValue == cellvalue) {
attendee.push(data[i]);
} else { //Fail Safe
attendee.push(data[i]);
}
}
if(attendee.length > 0){
sourceSpreadSheetSheetID1.getRange(sourceSpreadSheetSheetID1.getLastRow() + 1,
1, attendee.length, attendee[0].length).setValues(attendee);
}
//Will delete the rows of importdata once the data is copided to other
sheets
sourceSpreadSheetSheetID.deleteRows(2,
sourceSpreadSheetSheetID.getLastRow() - 1);
}
Try this:
I imagine that you already know that you'll need an installable onEdit Trigger and that you can't test a function of this nature by running it without the event object.
function checkIn(e) {
var sh=e.range.getSheet();
if(sh.getName()!="Event") return;
if(e.range.columnStart==6) {
if(e.value=="Attendee Arrived"){
e.range.offset(0,1).setValue(Utilities.formatDate(new Date(), Session.getScriptTimeZone(), "M/d/yyyy HH:mm:ss"));
var row=sh.getRange(e.range.rowStart,1,1,sh.getLastColumn()).getValues()[0];
e.source.getSheetByName("Attendee Arrived").appendRow(row);
sh.deleteRow(e.range.rowStart);
}
}
}

How can i get this Script to run onEdit

I am trying to get this to run automatically, however with the current form it only works if i manually run the code.
I have another code file in this sheet which also uses onEdit, how can I incorporate the same onEdit to this code or make it run whenever column G is selected "Complete" to then change column H to "shipped"
function addValidation() {
// Get the spreadsheet and active sheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
// Get the data as an Array you can iterate and manipulate
var data = sheet.getDataRange().getValues();
var rule = SpreadsheetApp.newDataValidation().requireValueInList(["Complete"]).build();
// Loop the sheet
for(var i=0; i<data.length; i++) {
if(data[i][11] == "Complete") {
sheet.getRange(i+1, 8).clear().setDataValidation(rule);
} {
sheet.getRange(i+1, 8).clearDataValidations().setValue("Shipped");
}
}
}
A couple things:
In your if statement, you're not checking Column G - you're checking column K (11). If you want to check G, you need to change your if to:
if(data[i][6].toString() == "Complete") {
...
}
Your logic is backward and you're missing the else keyword to run the alternative. As is, it will mark 'Shipped' if it doesn't match 'Complete.' Swap the statements to fill the cell values correctly.
Also, string checking in a loop is a little tricky. Cell references as indices are objects, not strings. You need to convert your cell using the .toString() method before you can compare a straight string.
Your whole script should read:
function addValidation() {
// Get the spreadsheet and active sheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
// Get the data as an Array you can iterate and manipulate
var data = sheet.getDataRange().getValues();
// Loop the sheet
for(var i=0; i<data.length; i++) {
// To change which columns you're testing, change the second value.
// ColG = 6, not 11.
// Convert data[i][6] using .toString() to compare the value
if(data[i][6].toString() == "Complete") {
// If it's "Complete," mark Col H as 'Shipped'
sheet.getRange(i+1, 8).setValue("Shipped");
} else {
// If you want something else to happen, add that here.
}
}
}
The last step is to enable a trigger for the script. If you name your function onEdit(), it will run by itself because editing is a simple script. Since it's called addValidation, you need to manually add the onEdit trigger in the UI. Go to Edit > Current Project Triggers and click on "Add Trigger" in the bottom right. Follow the steps in the UI and the script should run itself.

Installable trigger onFormSubmit from non-Google form?

Thanks to everyone for their help in advance.
As a general novice to scripts and Installable Triggers specifically, I have searched all over for the technique of how to do this and haven't found what I need. I will try to break down what I am looking for as best I can.
1.) I am using a form service that is not through Google and I need this email script to trigger when a form is submitted via this service.
Currently I am using a Simple Trigger that runs every 5 minutes which is super annoying because it slows down the whole project and is obviously not very efficient.
2.) I don't know where the 'Trigger' function needs to be placed within the script so I will post the simple script that I am using and hopefully you guys can direct me to where it needs to be placed (and if it even matters?).
Finally, I have already visited all of the links both through Google and Formstack on this issue and have been unable to piece together the result I am looking for. Again, I am a novice at this trigger part so a walk-through is very much appreciated! :-)
Thanks again!
Edit: script
function Email1() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
ss.setActiveSheet(ss.getSheetByName("Sheet3"));
var startRow = 2; // First row of data to process
var numRows = 18; // Number of rows to process
// Fetch the range of cells A2:B19
var dataRange = (ss.getSheetByName('Sheet3')).getRange(startRow, 1, numRows, 19)
// Fetch values for each row in the Range.
var data = dataRange.getValues();
for (var i = 0; i < data.length; ++i) {
var EMAIL_SENT = "EMAIL_SENT";
var row = data[i];
var emailAddress = row[0]; // First column
if(row[0]!="")
var message = row[1]; // Second column
var emailSent = row[2]; // Third column
var clientEmail = row[13]; // Thirteenth column
if (emailSent != EMAIL_SENT) { // Prevents sending duplicates
var subject = "APPOINTMENT SCHEDULED";
MailApp.sendEmail(emailAddress, subject, message, {noReply:true, cc:clientEmail, bcc:
'xxxxx.myemail.com'});
(ss.getSheetByName('Sheet3')).getRange(startRow + i, 3).setValue(EMAIL_SENT);
// Make sure the cell is updated right away in case the script is interrupted
SpreadsheetApp.flush();
}
}
}

How to script the addition of a row in one sheet when the source sheet has a row added?

I have a google form which populates a google spreadsheet (source sheet). I located and customized a script to pull certain columns -in their entirety- from the source sheet into a new tab/sheet. Apparently there has to be the same number of rows in both sheets for the script to run properly. If not, it turns back an error. Every time a new form is submitted, a row is added to the source sheet, putting the two sheets out of sync and breaking the script.
I'd like help figuring out what function I need to add to the existing script (or what changes I can make to it) so when a new row appears in the source sheet (because a form has been submitted), a blank row appears in the target sheet.
Do I need to tweak my script or add a new function?
function importFunction(a) {
var ss = SpreadsheetApp.openById("0ApTaY3v27-UqdElwZTBvanNpaC1UckpxaTJRZS1XNWc");
var sourceSheet = ss.getSheets()[0];
var ss2 = SpreadsheetApp.openById("0ApTaY3v27-UqdElwZTBvanNpaC1UckpxaTJRZS1XNWc");
var targetSheet = ss2.getSheets()[1];
var values1 = sourceSheet.setActiveSelection("C:C").getValues();
var values2 = sourceSheet.setActiveSelection("AD:AD").getValues();
var values3 = sourceSheet.setActiveSelection("D:E").getValues();
var values4 = sourceSheet.setActiveSelection("AE:AE").getValues();
var values5 = sourceSheet.setActiveSelection("F:H").getValues();
var values6 = sourceSheet.setActiveSelection("N:U").getValues();
targetSheet.setActiveSelection("A:A").setValues(values1);
targetSheet.setActiveSelection("B:B").setValues(values2);
targetSheet.setActiveSelection("C:D").setValues(values3);
targetSheet.setActiveSelection("E:E").setValues(values4);
targetSheet.setActiveSelection("F:H").setValues(values5);
targetSheet.setActiveSelection("I:P").setValues(values6);
}
Per the suggestion below, I tried to change script to the following, but I get an error - Cannot find method appendRow(). How do I fix that?
function importFunction(a) {
var ss = SpreadsheetApp.openById("0ApTaY3v27-UqdElwZTBvanNpaC1UckpxaTJRZS1XNWc");
var sourceSheet = ss.getSheets()[0];
var ss2 = SpreadsheetApp.openById("0ApTaY3v27-UqdElwZTBvanNpaC1UckpxaTJRZS1XNWc");
var targetSheet = ss2.getSheets()[1];
var targetMax = targetSheet.getMaxRows();
var values1 = sourceSheet.setActiveSelection("C:C").getValues();
var values2 = sourceSheet.setActiveSelection("AD:AD").getValues();
var values3 = sourceSheet.setActiveSelection("D:E").getValues();
var values4 = sourceSheet.setActiveSelection("AE:AE").getValues();
var values5 = sourceSheet.setActiveSelection("F:H").getValues();
var values6 = sourceSheet.setActiveSelection("N:U").getValues();
if(targetMax == values1.length) {
targetSheet.setActiveSelection("A:A").setValues(values1);
targetSheet.setActiveSelection("B:B").setValues(values2);
targetSheet.setActiveSelection("C:D").setValues(values3);
targetSheet.setActiveSelection("E:E").setValues(values4);
targetSheet.setActiveSelection("F:H").setValues(values5);
targetSheet.setActiveSelection("I:P").setValues(values6);
}
else
targetSheet.appendRow();
}
You could simply check the number of available rows in sheet SS2 before writing the values using getMaxRows() and compare it to the length of your data arrays value1.length, then, depending on this comparison add the rows you need using appendRow() eventually in a for next loop.
EDIT : following your EDIT, I tested a bit further and it appears that appendRow() doesn't work as I thought, it appends Rows at the begining of an empty sheet... so I tried this :
if(targetMax < values1.length) {
var RowsToAdd = values1.length-targetMax
for(nn=0;nn<RowsToAdd;++nn){targetSheet.insertRowAfter(targetMax)}
}
Just place it right after var value6=...
sorry for this little error ;-)

Categories

Resources