I have the below script working fine on my "Order sheet" (project name is "Order sheet code" file is "Code.gs")
function onEdit(e) {
var sheet = e.source.getActiveSheet();
var activeCell = sheet.getActiveCell();
var col = activeCell.getColumn();
var row = activeCell.getRow();
if (e.range.getSheet().getSheetName() == 'Order Sheet') {
if (col == 6) { // assuming status is in column 6 (E), adapt if needed
if (col == 6 && sheet.getRange(row, col + 1).isBlank())
sheet.getRange(row, col + 1).setValue(new Date()).setNumberFormat('DDD, dd MMM'); // change the display format to your preference here
}
if (col == 6 && sheet.getRange(row, col).isBlank())
sheet.getRange(row, col + 1).setValue("")
}
}
Now I have a new sheet, "Work Hours" where I want to utilize the same sort of functionality (different column and time instead of date), but even when I copy this script to a new script file (Project "Work Hours code", file name "Code.gs") and change the SheetName (line 7) to 'Work Hours' it doesn't seem to work.
I thought maybe it was because I had 2 projects with the Code.gs file, but changing that to Code2.gs didn't make a difference (changed it back, just in case).
Also, eventually I want this to run even when the sheet is closed, because I need it to record when data comes in from an external source. Is that possible? I presume I'd have the change the variable for "sheet" since it wouldn't be active?
This will work on both sheets:
function onEdit(e) {
var sh= e.range.getSheet()
var shts=['Order Sheet','Work Hours']
if (shts.indexOf(sh.getName()!=-1)) {
//do whatever you want in here
}
}
or if want to do different things on each sheet:
function onEdit(e) {
var sh= e.range.getSheet()
if(sh.getName() == 'Order Sheet') {
}
if(sh.getName() == 'Work Order') {
}
}
Ok, for other newbies like me with this issue:
BE EXTRA VIGILANT... I know this is obvious, but especially with copy/paste: it seems I had an extra { in my original script...
Probably happened when copy-pasting and deleting a few times when I was trying different things.
I also found why I couldn't make #Cooper 's suggestion work: I was working on BOUND SCRIPTS (ie scripts bound to a specific sheet)
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 new to Google App Script and trying to create a queue-like spreadsheet.
The current design is that: in sheet A, there's a column of checkboxs, and if any one of them is checked, the entire row would be moved to the end of another sheet, say sheet B.
The way I calculate the end of the sheet B is as followed:
(I read from a different stackoverflow post)
var Avals = dest.getRange("B:B").getValues();
var Alast = Avals.filter(String).length;
var lastRow = String(Alast + 1);
The issue I'm having here is that when I check two checkboxes quickly one by one, they are mapped to the same lastRow in sheet B, because the second checkbox triggers the onEdit trigger before the first one has moved the entire row to the bottom of the sheet B.
Does anyone know how to solve this issue?
Right now, the workaround is since it's a shared google sheet, I asked my colleagues not to click the checkbox column at the same time.
This seems to be working for what little testing I've done
function onEdit(e) {
LockService.getScriptLock().tryLock(2000)
const sh = e.range.getSheet();
if(sh.getName() == "Sheet0" && e.range.columnStart == 1 && e.range.rowStart > 1 && e.value == "TRUE") {
let tsh = e.source.getSheetByName("Sheet1");
let vs = sh.getRange(e.range.rowStart, 1, 1,sh.getLastColumn()).getValues();
tsh.getRange(tsh.getLastRow() + 1, 1 , vs.length, vs[0].length).setValues(vs);
}
LockService.getScriptLock().releaseLock();
}
My script below works as an independent script - It will add a date and a time in a cell/column opposite the trigger cell/ column. I.e. Any cell in Column A stating TRUE will have in its adjacent Column B cell stating the exact date & time, at any moment the cell in Column A is deemed to be TRUE. The script keeps this as as one single change. If I were to delete TRUE or change it to any other text, the data in column B would remain the same, until I write TRUE again - This will force the cell(s) in Column B to overwrite the previous date/time with the latest date/time the moment the new "TRUE" statement was issued in Column A.
Now, I would like to add another onEdit function where it works on Sheet 2, but the column reference numbers are not 1 (as you will see below, all numbers are 1, whether positive or negative), but will be 2. I do not know how to get the two separate functions to work as when I list them separately, with an identical script structure, only the last script in the sequence works, and the first one does not.
I hope I have made this clear enough; I am not a coder by any means, and I have done my best with tutorials, forums, etc before posting this question. I can learn better by reverse-engineering someone else's work rather than being given the building blocks and told to create something, but I appreciate any education on this so I can learn for the future.
function onEdit(e) {
var aCell = e.source.getActiveCell(), col = aCell.getColumn();
if(col == 1) { //number of column(s) where you have a data
var adjacentCell = aCell.offset(-1,-1);
var newDate = Utilities.formatDate(new Date(),
"GMT+1", "dd/MM/yyyy hh:mm:ss");
adjacentCell.setValue(newDate);
}}
function onEdit(e){
var ss = SpreadsheetApp.getActiveSheet();
//change to be your sheet name so the script only triggers on that sheet
if( ss.getName() == "Sheet1" ) { //checks that we're on the correct sheet
var r = ss.getActiveCell();
if(e.value != "TRUE") return;
e.source.getActiveSheet().getRange(e.range.rowStart,e.range.columnStart+1).setValue(new
Date());
}
}
I don't think that you have specified sufficient conditions for this to work correctly but this is what you have so far:
function onEdit(e) {
const sh = e.range.getSheet();
if(sh.getName() == 'Sheet1' && e.range.columnStart == 1) {
e.range.offset(-1,-1).setValue(Utilities.formatDate(new Date(),"GMT+1", "dd/MM/yyyy hh:mm:ss"));
}
if(sh.getName() == 'Sheet1' && e.value == "TRUE") {
e.range.offset(0,1).setValue(new Date())
}
}
I imagine you will need further debugging to get this to work.
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'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.