I'm a new to Script editor on google spreadsheet and not much of a programmer.
I've been working with google docs for sometime now and it has become an important tool in my daily activity.
What I'm trying to do, is the following:
Seek a whole document (with severall sheet such as "1", "2", "3", and so on, corresponding to the number of days a month can hold) and if the column 7 shows a determined value (in my case, it will be RECEBER), pull all the data in that row and write onto a sheet created for this purpose.
What's happening is that I'm using the the event onEdit to trigger this function. At first sight, it would be ideal, but in my case, I copy a lot of data from other spreadsheets and the paste command does not trigger the onEdit event. Instead, I have to edit the cell manually in order to get that row copied onto the other sheet.
I could run it just once, once tha whole days of the month were filled and there were any changes left to do, but what I really want to do is to make it immediately, as soon as the content is inserted into the spreadsheet.
There's also another problem with my code, it has to be fit and adapted to all the other sheets, as the if clause only performs the full operation if the active sheet equals "1". Anyway, I believe there is a simple solution to this.
Here's the code found on the net that already took me halfway:
function onEdit(event) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = event.source.getActiveSheet();
var r = event.source.getActiveRange();
if(s.getName() == "1" && r.getColumn() == 7 && r.getValue() == "RECEBER") {
var row = r.getRow();
var numColumns = s.getLastColumn();
var targetSheet = ss.getSheetByName("money");
var target = targetSheet.getRange(targetSheet.getLastRow() + 1, 1);
s.getRange(row, 1, 1, numColumns).copyTo(target);
}
}
I'll appreciate all the help you can give.
Thanks in advance.
Diogo Sousa
-- Updated 12Oct --
I've changed the logics on my code, and as patt0 suggested, I run the script from the menu created. I've tried adapting the code, but I believe there's some section wrong. The script runs, but isn't writing anything at all on my target sheet.
here's the code:
function onOpen() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var menuEntries = [ {name: "RECEBER", functionName: "RECEBER"} ];
ss.addMenu("Scripts", menuEntries);
}
function mustBeCopied(sheetName) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheetNumber = parseInt(sheetName);
if (sheetNumber <=31 && sheetNumber >=1)
return true;
return false;
}
function RECEBER() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = ss.getActiveSheet();
var r = ss.getActiveRange();
if(mustBeCopied(s.getName()) && r.getColumn() == 7 && r.getValue() == "RECEBER") {
var row = r.getRow();
var numColumns = s.getLastColumn();
var targetSheet = ss.getSheetByName("money");
var target = targetSheet.getRange(targetSheet.getLastRow() + 1, 1);
s.getRange(row, 1, 1, numColumns).copyTo(target);
}
}
the function Mustbecopied, from what I believe, only sets the range of sheets (1 to 31) eligible;
the function RECEBER will determine if the value on the column 7 will satisfy the condition (RECEBER), so it can retrieve all the row information and move it to the target sheet.
Maybe the trouble is the active sheet thing.. can I us eit in my own advantage and apply the script to the selected sheet?
Also, if I could have both option (whether to apply it to the selected sheet or to the whole document), that wouuld be great and simplify my daily work a lot!
Your question is really multiple questions in one, and I will only deal with the second part, the first part has quite a number of dimensions (how to update summary sheets effectively), which we can discuss with another question maybe.
In order to determine if the data in a particular sheet needs to be copied, you could create a simple function that returns a boolean if the sheet satisfies the condition that it is a number between 1 and 31.
function mustBeCopied(sheetName) {
var sheetNumber = parseInt(sheetName);
if (sheetNumber <=31 && sheetNumber >=1)
return true;
return false;
}
Bear in mind that a sheet with the name "28.7" would satisfy the condition.
The first line of your onEdit() script would then look like this
if(mustBeCopied(s.getName()) && r.getColumn() == 7 && r.getValue() == "RECEBER")
Let me know if this works for you.
--- Oct 14 ---
As far as I can see, the issue that remains, is that when your function was running onEdit, the range (active range) was the cell that was just edited. So the range would be only one cell.
In order for the scrip to run, you would need to highlight each cell to be copied before selecting the menu, which would be tedious to say the least
Further if you have a defined range to be copied, which is the same on each page, we can copy that range every time, or iterate through each row in the column to look for "RECEBER" and get those rows copied.
Further We can try to update your function so that iterate through all the sheets is a many similar to this.
var sheets = SpreadsheetApp.getActiveSpreadsheet().getSheets();
for (var i = 0; i < sheets.length; i::) {
var s = sheets[i];
if (mustBeCopied(s.getName()) {
var range = s.getRange(someRow, 7, someNumOfRows);
for (var j = 1; j <= someNumOfRows; j++) {
if (range.getCell(j,1).getValue() == "RECEBER") {
//YOUR COPY CODE
}
}
}
}
Let me know how this works.
Related
I'm attempting to create a spreadsheet to organise products ordered at my workplace.
When an order is received a team member would add the details to the sheet; when it is collected they'd fill out date and ID then tick the order complete. See Attached
What I want to happen next is that the row containing the complete details from that order is appended to a second page in the sheet and the original row is deleted.
I can't make sense of how to get this to run automatically when the box is checked; so far I have been compiling a script to run from a button press:
function runFiling() {
function moveRows() {
var ss = SpreadsheetApp.getActive();
var osh = ss.getSheetByName('Current');
var dsh = ss.getSheetByName('Collected');
var srg = osh.getDataRange('H2:H');//You might want to specify a more unique range. This just gets all of the data on the sheet
var svA = srg.getValues();
var d=0;//deleted row counter
for(var i=1;i<svA.length;i++) {
if(svA[i][7] =='TRUE') {
dsh.appendRow(svA[i]);//append entire row to Sheet2
osh.deleteRow(i-d+1);//accounts for the difference between length of array and number of remaining row.
d++;
}
}
}
}
However even this fails to Append or Delete anything although no errors are found/returned.
If anyone can suggest a way to fix the above or, preferably, how to make the script work when the box is ticked your help will be most appreciated.
Try it this way using an onEdit(e) function
function onEdit(e) {
const sh = e.range.getSheet();
if (sh.getName() == 'Current' && e.range.columnStart == 7 && e.range.rowStart > 1 && e.value == "TRUE") {
const dsh = ss.getSheetByName('Collected');
const vs = sh.getRange(e.range.rowStart, 1, 1, sh.getLastColumn()).getValues()
dsh.getRange(dsh.getLastRow() + 1, 1, vs.length, vs[0].length).setValues(vs);
sh.deleteRow(e.range.rowStart);
}
}
This will accomplish the task line by line as the check boxes are checked off by the user.
I'm trying to make google sheets automatically copy a row and paste it to the bottom of another tab when Completed is selected from a dropdown in the 8th column. Then I want it to grab the formulas from that row, clear the row, and then replace the formulas. The code works as far as copying and pasting to the other tab but I can't get it to grab the formulas, clear the row and then replace the formulas. I can't even get it to clear the row. The code is correctly executing everything but the last four lines.
This isn't my code. I got it from a website and modified it to fit my sheet. I have a very basic understanding of javascript. I need the original row to remain with just the formulas populated so that it can be reused. I'm using copyTo instead of moveTo because the row is being referenced upstream by another sheet and moveTo causes the formulas on the upstream spreadsheet to auto update to the new location of the data instead of continuing to reference the original row. Thank you for any help.
function myStorage() {
// moves a row from a sheet to another when a value is entered in a column
var sheetNameToWatch = "In Progress";
var columnNumberToWatch = 8;
var valueToWatch = "Completed";
var sheetNameToMoveTheRowTo = "Storage";
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getActiveCell();
if (sheet.getName() == sheetNameToWatch && range.getColumn() == columnNumberToWatch && range.getValue() == valueToWatch) {
var targetSheet = ss.getSheetByName(sheetNameToMoveTheRowTo);
var targetRange = targetSheet.getRange(targetSheet.getLastRow() + 1, 1);
sheet.getRange(range.getRow(), 1, 1, sheet.getLastColumn()).copyTo(targetRange);
// this is where it stops working. I'm not sure how to grab just the row of the active cell and do the following
var clearRange = sheet.getRange(range.getRow());
clearRange.getFormulas();
clearRange.clearContent();
clearRange.setFormulas();
}
}
Try this:
function CallItSomethingElse(e) {
var sh=e.range.getSheet();
if (sh.getName()=="In Progress" && e.range.columnStart==8 && e.value=="Completed") {
var targetSheet = e.source.getSheetByName("Storage");
var targetRange = targetSheet.getRange(targetSheet.getLastRow() + 1, 1);
sh.getRange(e.range.rowStart,1,1,sh.getLastColumn()).copyTo(targetRange);
var clearRange=sh.getRange(e.range.rowStart,1,1,sh.getLastColumn());
var fA=clearRange.getFormulas();//returns a 2d array of formulas
clearRange.clearContent();
clearRange.setFormulas(fA);//you have to add that array back into this line
}
}
Because many people will take this code and try to run it from the editor just to return and tell me that they get the error of cannot find method getSheet of undefined. Let me first suggest that you can't run this code without an event object. The single e is the parameter that the event object will be place into. Without the e the program cannot run. Also unless there is a trigger to create the e the script cannot run. So if you wish to run this from the script editor then you will have to provide the event object. I don't choose to do that so when I debug these things I do it will a live trigger.
I have been working on this for a few days and have turned the internet and these forums upside down, but can't find the answer anywhere. I've learned some scripting along the way, but have determined that what I need to do is way beyond my limits. Any help at all or guidance would be SUPER well received!!
I'm setting up an order sheet system for my and my wife's handmade 'business' (project). Essentially I need to move order 1 (columns a-w) from 'ready to ship" when "done" is entered into column X, onto the next blank row on "sent", discounting the formulas in columns w-z. So when its checking for a blank row, it should only check columns a-w.
This means that once the order info has moved across onto the new sheet, we have extra functionality added in those columns (I hooked it up to send an email apologising, if an order will be late - hope not, but better to be prepared, with our postal service :) )
Sample sheet here.
I originally learned how to write a 'move row if value added in column x - and learned about triggers and how to call the sheets etc.
Then, because column x, y and z have formulas, I either end up deleting them when I move the whole row - not cool. Or I end up (if i move only columns a- w) having the new (moved) row, showing up in row 9 million, because it sees the formulas as not blank rows. Ive tried editing a 'find row based on column' script, that seemed to work okay, but then i tried to put the two together and thats when it blew up.
Can anyone tell me where i'm going wrong here?
function lastRowForColumn(sheet, column){
// Get the last row with data for the whole sheet.
var ss = spreadSheetApp.getActiveSpreadSheet();// this gets you the
active spreadsheet in which you are working
var sheet = ss.getSheetByName('Sent');
var r = event.source.getActiveRange();
var data = sheet.getRange(1, "A", numRows).getValues();
var numRows = sheet.getLastRow();
// Iterate backwards and find first non empty cell
for(var i = data.length - 1 ; i >= 0 ; i--){
if (data[i][0] != null && data[i][0] != ""){
return i + 1;
var ss = spreadSheetApp.getActiveSpreadSheet();// this gets you the
active spreadsheet in which you are working
var sheet = ss.getSheetByName('Sent');
var r = event.source.getActiveRange();
if(s.getName() == "Ready To Send" && r.getColumn() == 25 &&
r.getValue() == "Sent") {
var row = r.getRow();
var numColumns = s.getLastColumn();
var targetSheet = ss.getSheetByName("Sent");
var lastRow = lastRowForColumn();
var target = targetSheet.getRange(targetSheet, i, 1);
s.getRange(row, 1, 1, 10).moveTo(target);
s.deleteRow(row);
}
}
}
}
Try this:
function onEdit(e){
var rg=e.range;
var sh=rg.getSheet();
var name=sh.getName();
var col=rg.columnStart;
if(name!='Sheet To Send'){return;}
if(col==24 && e.value=='done') {
var ss=e.source;
var sheet=ss.getSheetByName('Sent');
var row=rg.rowStart;
var srg=sh.getRange(row,1,1,23);
var data=srg.getValues();
var drg=sheet.getRange(sheet.getLastRow()+1,1,1,23);
drg.setValues(data);
sh.deleteRow(row);
}
}
I think what you want is to use getDisplayValues. That solves both your (formulas aren't blank problem (make sure your formulas display blank when the row is unused) and also the it isn't moving my numbers problem).
Try:
moveMe = [];
rowArray = s.getRange(row,1,1,10).getDisplayValues();
moveMe.push(rowArray);
targetSheet.appendRow(moveMe);
s.deleteRow(row);
You might have to wrestle the appendRow a bit but it is the cleanest. If it doesn't work as is try skipping the push step and doing
targetSheet.appendRow(rowArray);
BETTER (edit):
You need to change first your data row to:
var data = sheet.getRange(1, "A", numRows).getDisplayValues();
and then later you can simply do:
targetSheet.appendRow(data[i]);
s.deleteRow(row);
This function takes an edit event and checks if the values edited was changed to 'Sent'. It then copies that row into the sent sheet in the same spreadsheet, maintaining the values.
function onEdit(e){
if(e.value != 'Sent') return false; //If not changed to 'Sent' then stop.
var range = e.range; //get the range the edit occured in.
if(range.getColumn() != 25) return false;//see if it was column number 25, ie. Column Y
var sheet = range.getSheet();
if(sheet.getName() != 'Ready To Send') return false;
var editedRow = sheet.getRange(range.getRow(), 1, 1, sheet.getMaxColumns()); //Select the row.
var sent = sheet.getParent().getSheetByName('Sent');
sent.appendRow(editedRow.getValues()[0]); //Copy row to Sent sheet.
sheet.deleteRow(range.getRow()); //Deleted the original row
}
I have the following script running on a document (I found on the forum and unfortunately I cannot remember who it was to give credit to but it works great), pulling the whole rows data into a new sheet then deleting the row from its current location;
function onEdit(event)
{
// assumes source data in sheet named Schedule
// target sheet of move to named Invoiced
// test column with "9 - Invoiced" is col 1
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = event.source.getActiveSheet();
var r = event.source.getActiveRange();
if(s.getName() == "AllOpps" && r.getColumn() == 1 && r.getValue() ==
"Outreached")
{
var row = r.getRow();
var numColumns = s.getLastColumn();
var targetSheet = ss.getSheetByName("InitialOutreach");
if(targetSheet.getLastRow()+1 == targetSheet.getMaxRows())
{
targetSheet.insertRowsAfter(targetSheet.getLastRow(), 5);
//inserts 5 rows after last used row
}
var target = targetSheet.getRange(targetSheet.getLastRow()+1, 1);
s.getRange(row, 1, 1, numColumns).moveTo(target);
s.deleteRow(row);
}
}
What I am looking for is an amendment to this and I have no idea how to make it work as my script knowledge is extremely limited, in fact, I am not even sure if it is possible but here goes.
Upon selection of "Outreached" I would like that row to do 2 things, firstly for the whole row to be copied across to the InitialOutreach tab, as it does now, and at the same time column 11,12,13 and 14 to be copied into a completely different document, for ease of use I have added it in the shared sample doc as Mail. This second copy will need to be into the respectively named columns
You can find a sample sheet here. Please, note that AllOpps and Initial Outreach are in the same document and represents the initial transfer while Mail Merge tab will be the second transfer. Once again the second transfer ie. only select columns will be in a completely different document.
https://docs.google.com/spreadsheets/d/1x_NFLXC2doWAgCWcAbxpuAh1vW-IclMZ1prN4loWdDA/edit?usp=sharing.
Once again I am not even sure running 2 different tasks on 1 trigger is even possible.
NOTE: This reply has been modified from previous versions, to deal with various changes and problems as reported by the OP.
The code below should do what you need. Note carefully the changed function name myOnEdit which also appears in the onOpen event, where it is named as an "installable" trigger. See later notes on reason for this.
function onOpen() {
ScriptApp.newTrigger('myOnEdit')
.onEdit()
.create();
}
function myOnEdit(event)
{
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = event.source.getActiveSheet();
var r = event.source.getActiveRange();
if(s.getName() == "AllOpps" && r.getColumn() == 1 && r.getValue() == "Outreached")
{
var row = r.getRow();
var numColumns = s.getLastColumn();
// first copy and paste the "Outreached" row in entrity
var targetSheet = ss.getSheetByName("InitialOutreach");
if(targetSheet.getLastRow()+1 == targetSheet.getMaxRows())
{
targetSheet.insertRowsAfter(targetSheet.getLastRow(), 5);
//inserts 5 rows after last used row
}
var target = targetSheet.getRange(targetSheet.getLastRow()+1, 1);
var values = s.getRange(row, 11, 1, 5).getValues(); // save the values to copy to "mail Merge"
s.getRange(row, 1, 1, numColumns).moveTo(target);
Logger.log(values);
// Copy the required columns to "Mail Merge" spreadsheet"
var mailMergeSS= SpreadsheetApp.openById('--copy your sheet ID here--');
targetSheet = mailMergeSS.getSheetByName("Mail Merge");
if(targetSheet.getLastRow()+1 == targetSheet.getMaxRows())
{
targetSheet.insertRowsAfter(targetSheet.getLastRow(), 5);
//inserts 5 rows after last used row
}
target = targetSheet.getRange(targetSheet.getLastRow()+1,1,1,5);
target.setValues(values);
s.deleteRow(row);
}
}
1) Make the new target spreadsheet for the mail merge lines and make a new tag called "Mail Merge". Make
2) To find the ID of the new sheet open it and look at the URL. It should look something like this:
https://docs.google.com/spreadsheets/d/14NP6TUyj1xLCPjKsPdGS6fiO0Bvzv1u4696LFd3tA7I/edit#gid=0
The ID is the emboldened bit ie 14NP6TUyj1xLCPjKsPdGS6fiO0Bvzv1u4696LFd3tA7I in the example above,. Yours will be different, so copy and paste it so that your code line looks like:
var mailMergeSS= SpreadsheetApp.openById('14NP6TUyj1xLCPjKsPdGS6fiO0Bvzv1u4696LFd3tA7I');
To avoid this error:
SpreadsheetApp.openById([0AjqSnE_p3nFqdDN0LWpFbjFqVDRwNmFGOV91QzZrZc])
[0 seconds] Execution failed: You do not have permission to perform
that action.
.. which arises when you try to open an external sheet, you need to use "Installable Triggers". The appropriate references are here:
www.developers.google.com/apps-script/guides/triggers/installable
developers.google.com/apps-script/guides/triggers/events
Installable triggers can be installed in 2 ways:
In your CopyCols sheet, go to the script editor and select Current
Project Triggers. Add a new trigger with settings as follows: Choose
which function to run: myOnEdit Which runs at deployment:
head Select Event Source: from Spreadsheet Select
event type: onEdit Then save and restart your sheet..
or....In your code add this function lines:
function onOpen() {
ScriptApp.newTrigger('myOnEdit')
.onEdit()
.create();
}
I am having trouble creating a script that can populate multiple sheets from a single main tracker spreadsheet. More specifically, the Main tracker spreadsheet has a column named outcome in which a clients outcome is recorded. I want to be able to simply enter the outcome in the main tracker and have the entry of that value trigger the creation of a new row in a separate sheet that contains only information about that single outcome.
For example, in the master sheet I would entered " Added to waitlist" in the outcome column, which would thereby generate a copy of that record in another sheet with the same file called "Clients added to waitlist". I want to be able to do that for about 5 differentoutcomes.
Currently I have been using the following script for each of my 5 outcomes and 5 tabs to complete this task but I have yet to be successful:
function onEdit(event) {
// assumes source data in sheet named Needed
// target sheet of move to named Acquired
// test column with yes/no is col 4 or D
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = event.source.getActiveSheet();
var r = event.source.getActiveRange();
if(s.getName() == "Main Tracker" && r.getColumn() == 8 && r.getValue() == "Added to Waitlist") {
var row = r.getRow();
var numColumns = s.getLastColumn();
var targetSheet = ss.getSheetByName("Clients Added to Waitlist");
var target = targetSheet.getRange(targetSheet.getLastRow() + 1, 1);
s.getRange(row, 1, 1, numColumns).copyTo(target);
}
}
Whenever, I have more than one of the above script entries , only the very first one entered into the editor works, while the rest simply don't take. If some could help me reconcile this problem that would be great.
You can't use more than one onEdit function in your script. Function names must be unique. You can have multiple conditional statements within the function, though.
if (event.value == "Added to Waitlist") {
// do something
}
if (event.value == "Another thing") {
// do something else
}
Or better yet, use a switch statement:
switch (event.value) {
case "Added to Waitlist":
// do something
break
case "Another thing":
// do something else
break
Note that event.value gives you the value entered on edit; you don't need to call getValue on the active cell to get it.
Similarly, r = event.source.getActiveRange(); could be simply r = event.range;