I am looking for a solution that will move an entire row's content based on what is in the specified cell when the function is run on a trigger. I can accomplish this using the onEdit(e) within sheets, but when running a "regular" version, nothing happens. No errors.
I frequently use this type of functionality when a cell is edited, but for this specific sheet I need it to work when called (by trigger, manual, etc).
function archive(){
// assumes source data in sheet named Form Responses
// target sheet of move to named ARCHIVE
// Target col is B (3)
// Target value is "TEST" to move to "ARCHIVE"
var sheet = SpreadsheetApp.getActiveSpreadsheet();
var mainSheet = sheet.getSheetByName("Form Responses");
var r = mainSheet.getActiveRange();
if(r.getColumn() == 3 && r.getValue() >= "TEST"){
var row = r.getRow();
var numColumns = mainSheet.getLastColumn();
var targetSheet = sheet.getSheetByName("ARCHIVE");
var target = targetSheet.getRange(targetSheet.getLastRow() + 1, 1);
mainSheet.getRange(row, 1, 1, numColumns).copyTo(target, {contentsOnly:true});
mainSheet.deleteRow(row);
}
}
When ran, nothing happens to the sheet. No error, no logs, etc.
Related
I currently have working code inside of my Google Sheet. The code moves certain rows in the sheet over to another sheet when labeled as "Archive" in a drop down menu.
The problem I have is that when this happens, the entire row gets deleted. I only need the information from column C:O (C2:O) to be archived. Another problem that this creates, when it deletes the row the other rows move up, thus deleting the set amount of rows I have created for input.
I need it to automatically replace the archive rows with another row with all the same conditional formatting and functions so that it does not disrupt the rest of the sheet and there is no need to go in and manually create more rows.
Please help, thank you very much in advance.
Current code used in Google App Scripts is attached below.
function myFunction() {
// moves a row from a sheet to another when a magic value is entered in a column
// adjust the following variables to fit your needs
// see https://productforums.google.com/d/topic/docs/ehoCZjFPBao/discussion
var sheetNameToWatch = 'Campaigns';
var columnNumberToWatch = 6;
// column A = 1, B = 2, etc…
var valueToWatch = 'Archive';
var sheetNameToMoveTheRowTo = 'Archive';
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()).moveTo(targetRange);
sheet.deleteRow(range.getRow());
}
}
You just need to use copyTo() [1] function instead of moveTo() [2] function which is cutting and pasting the source row. Also, you need to remove the deleteRow function if you just want to leave the row cells with same format and functions set. You only would need to change this:
sheet.getRange(range.getRow(), 1, 1, sheet.getLastColumn()).moveTo(targetRange);
sheet.deleteRow(range.getRow());
To this:
sheet.getRange(range.getRow(), 1, 1, sheet.getLastColumn()).copyTo(targetRange);
Moreover, if you just want to copy the values and not the formulas from the cells to the target sheet, use the copyTo function with options [3], like this:
sheet.getRange(range.getRow(), 1, 1, sheet.getLastColumn()).copyTo(targetRange, SpreadsheetApp.CopyPasteType.PASTE_VALUES, false);
[1] https://developers.google.com/apps-script/reference/spreadsheet/range#copytodestination
[2] https://developers.google.com/apps-script/reference/spreadsheet/range#movetotarget
[3] https://developers.google.com/apps-script/reference/spreadsheet/range#copytodestination-copypastetype-transposed
If you want to clear the values without deleting the row, you'll want to change your approach in the following ways:
obtain a Range which includes the data you want to archive
to populate the archive sheet, use copyTo() instead of moveTo()
obtain a Range which includes only the cells you want to clear out
use range.clearContent() to clear that range
Here's a reimplementation of your function. I've extracted the configurable bits into global variables, which makes adjusting them down the line a bit easier to do.
var SOURCE_SHEET_NAME = 'Campaigns';
var TARGET_SHEET_NAME = 'Archive Campaigns';
// The cell value that will trigger the archiving action.
var ARCHIVE_VALUE = 'Archive';
// The column number where you expect the ARCHIVE_VALUE to appear.
var ARCHIVE_COLUMN_NUMBER = 4;
// The starting and ending columns for the range of cells to archive.
var ARCHIVE_START_COLUMN = 1;
var ARCHIVE_END_COLUMN = 15;
// The starting and ending columns for the range of cells to clear.
var CLEAR_START_COLUMN = 1;
var CLEAR_END_COLUMN = 10;
function archiveRow() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var activeCellRange = sheet.getActiveCell();
if (sheet.getName() != SHEET_NAME ||
activeCellRange.getColumn() != ARCHIVE_COLUMN_NUMBER ||
activeCellRange.getValue() != ARCHIVE_VALUE) {
return;
}
var activeCellRow = activeCellRange.getRow();
// Get the range for the data to archive.
var archiveNumColumns = ARCHIVE_END_COLUMN - ARCHIVE_START_COLUMN + 1;
var archiveRowRange = sheet.getRange(activeCellRow, ARCHIVE_START_COLUMN, 1, archiveNumColumns)
// Get the range for the archive destination.
var targetSheet = ss.getSheetByName(TARGET_SHEET_NAME);
var targetRange = targetSheet.getRange(targetSheet.getLastRow() + 1, 1, 1, archiveNumColumns);
// Copy the data to the target range.
archiveRowRange.copyTo(targetRange);
// Get the range for the data to clear.
var clearNumColumns = CLEAR_END_COLUMN - CLEAR_START_COLUMN + 1;
var clearRowRange = sheet.getRange(activeCellRow, CLEAR_START_COLUMN, 1, clearNumColumns);
// Clear the row.
clearRowRange.clearContent();
}
I copied a code from https://www.seerinteractive.com/blog/google-sheets-scripts/ to move rows from one tab to another. I modified the code to trigger it whenever a cell in the specified column was edited. It worked on both manual edit and copy pasted data, but when I tried copy pasting the data on multiple cells in the specified column, it only moves the first cell in the selection and the rest of the cells below doesn't move. Also when I edit the cell too fast, it sometimes doesn't move the row.
I tried using the onChange trigger but I cannot figure out how to make it work.
function onEdit() {
var sheetNameToWatch = "raw";
var columnNumberToWatch = 16;
// column A = 1, B = 2, etc…
var valueToWatch = "1";
var sheetNameToMoveTheRowTo = "data prep";
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()).moveTo(targetRange);
sheet.clearContents(range.getRow());
}
The question has two parts
Regarding getting all the values of a range, use getValues instead of getValue. This will return an array of arrays (2D array).
Regarding the onEdit trigger not being triggered all the time, this is an known limitation of onEdit. It's already reported on the issue tracker an on some questions here.
Related
Failure of calling Google App Script onEdit function many times in a second
OK, I'm fairly new at doing this. I have a google spreadsheet that I populate and then run a script to create forms are created to complete tasks. My end users mark the task as completed. I am wanting entries in the spreadsheet that change to yes AND are Validate tasks to copy to another sheet (Log). I can get the entries to copy if I just have a watcher for yes but am having problems putting the other if statement for the task in there. Here is what I have for a test...
function onChange() {
// moves a row from any sheet to an archive sheet when a magic value is entered in a column
var columnNumberToWatch = /* column D */ 4; // column A = 1, B = 2, etc.
var valueToWatch = "yes";
var sheetNameToMoveTheRowTo = "LOG";
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Sheet2");
var cell = sheet.getActiveCell();
if ( sheet.getName() != sheetNameToMoveTheRowTo && cell.getColumn() == columnNumberToWatch
&& cell.getValue().toLowerCase() == valueToWatch) {
if (cell.getColumn() == 2 && cell.getValue() == "Validate") {
var targetSheet = ss.getSheetByName(sheetNameToMoveTheRowTo);
var targetRange = targetSheet.getRange(targetSheet.getLastRow() + 1, 1);
sheet.getRange(cell.getRow(), 1, 1, sheet.getLastColumn())
.copyTo(targetRange);
}
}
}
It looks like you are trying to watch two separate locations with the same cell variable. You start by initializing cell as the active cell, then check that the column is #4, then in the second if statement you use cell to getColumn 2.
Maybe create a new set of variables to use for the validation check:
var activeRow=cell.getRow();
var valCheck=sheet.getRange(activeRow, 2);
then the if statement changes to:
if (valCheck.getValue() == "Validate") {
I have successfully moved an entire row to my "Archive" sheet, but NOT with the criteria mentioned in the title and I have been messing with it for about two or three weeks. I want to be able to archive an entire row that is 30 days old AND has a value of "Complete". I've been trying to successfully incorporate an onOpen script but any suggestions would be appreciated if there is another way.
Here is the onEdit script that I am working off of and that I've used successfully before (minus the criteria that I want to set in place). I have edited it with onOpen before and didn't have any errors but it just wouldn't work.
The checklist sheet is what I'd be pulling my row from, The "Archive" sheet is my destination for the moving of the row and the column 15 is obviously where I'd find my value "Complete". I can give you access to my test sheet if necessary.
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() == "Checklist" && r.getColumn() == 15 && r.getValue() == "Complete") {
var row = r.getRow();
var numColumns = s.getLastColumn();
var targetSheet = ss.getSheetByName("Archive");
var target = targetSheet.getRange(targetSheet.getLastRow() + 1, 1);
s.getRange(row, 1, 1, numColumns).moveTo(target);
s.deleteRow(row);
}
}
I have created an example spreadsheet here with all the apps script to complete your task; just make a copy to play with it. There is a portion of the spreadsheet on Sheet3 that calculates an index for how many rows need to be archived which is needed for the script, sorry I'm fairly new to this so maybe someone else can edit my script to include it.
The formula I am using to get the index for records that need to be archived is:
=COUNTA(IFERROR(FILTER(Sheet1!A:A,Sheet1!A:A<TODAY()-30,Sheet1!B:B="Complete")))
This script:
On opening the spreadsheet runs the following steps:
Sorts first 2 columns in sheet1 to bring the completed rows to the top.
Uses the index on Sheet3 to move dates older than 30 days to Sheet2.
Erased dates older than 30 days off of Sheet1
Sorts both columns on Sheet1 back in order by date.
Note: Another option to trigger this script is by changing the function name, going to the icon in the menu, adding a new trigger, and setting it to Function > From spreadsheet > On open
function onOpen() {
var s1 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet1');
var s2 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet2');
var s3 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet3');
var sortRange = s1.getRange(1, 1, s1.getMaxRows(), 2);
sortRange.sort([{column: 2, ascending: true}, //first column to sort
{column: 1, ascending: true}]);//second column to sort
var lastDateIndex = s3.getSheetValues(3, 2, 1, 1); //number of rows to move
var range = s1.getRange(1, 1, lastDateIndex, 2)
startRow = range.getRowIndex(),
numRows = range.getNumRows(),
numCols = range.getNumColumns()
var values = range.getValues(),
lastRow = s2.getLastRow();
s2.getRange(lastRow+1,1,lastDateIndex,2).setValues(values);
s1.deleteRows(startRow,numRows);
var sortRange = s1.getRange(1, 1, s1.getMaxRows(), 2);
sortRange.sort([{column: 1, ascending: true}]);
}
I put together this test:
https://docs.google.com/a/strandvinduspuss.no/spreadsheets/d/1WZTYq-WiQq8asXZjHMMN9bsGiQKbPKmlRrsnkAsadU4/edit?usp=sharing
The function: in the List tab, when you change the name of col B, part of the row is copied to the matching sheet.
What I need is to copy them to separate spreadsheets (one for each employer). Is that possible?
My code:
// http://victorwyee.com/gapps/move-row-on-cell-value-google-spreadsheets/
/**
* Moves row of data to another spreadsheet based on criteria in column 6.
* Each spreadsheet can be a "source" spreadsheet, or the "target" spreadsheet.
* Assumes there as many spreadsheets as there are criteria values, e.g.
* if the criteria are "R", "P", and "D", then there are three spreadsheets
* named "R", "P" and "D".
* Assumes that each spreadsheet has the same structure.
*/
function onEdit(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = ss.getActiveSheet();
var r = SpreadsheetApp.getActiveRange();
// Get the row and column of the active cell.
var rowIndex = r.getRowIndex();
var colIndex = r.getColumnIndex();
// Get the number of columns in the active sheet.
var colNumber = s.getLastColumn();
// Move row based on criteria in column 6, and if row is not the header.
if (colIndex == 2 && rowIndex != 1) {
// Get value from column 6, in the active row.
var status = s.getRange(rowIndex, colIndex).getValue();
// Do nothing if criteria value is not actually changed to something else.
if (s.getName() != status) {
// The target sheet is the one with the same name as the criteria value.
var targetSheet = ss.getSheetByName(status);
var target = targetSheet.getRange(targetSheet.getLastRow() +1, 1);
// Set the range and copy
// s.getRange(rowIndex, 10, 1, colNumber).copyTo(target);
targetSheet.insertRowsAfter(targetSheet.getLastRow(), 1);
s.getRange(rowIndex, 1, 1, 16).copyTo(target);
// s.deleteRow(rowIndex);
}
}
}
Have made some changes so the comments are not updated.
You can copy sheets to other Spreadsheets, but (to my knowledge, based off of the documentation and errors I've received) you can not copy a range to a separate spreadsheet. I have a similar use case to you, and my workaround is to move what I want to copy to a separate sheet, and then copy that sheet to the new/other spreadsheet:
var newSpreadsheet = SpreadsheetApp.create("I am a copy");
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Copy Me");
sheet.copyTo(newSpreadsheet);
This wouldn't work in your case if you needed to keep the same sheet/data where you are moving the data to however, since it would be overwritten. An option in that case is to copy the sheet over, and then move whatever data you want, and then delete the excess sheet.
var newSpreadsheet = SpreadsheetApp.create("I am a copy");
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1");
var projectname = SpreadsheetApp.getActiveSpreadsheet();
sheet.copyTo(newSpreadsheet);
var dest = newSpreadsheet.getSheetByName("Copy of Sheet1").getRange("E7");
newSpreadsheet.getSheetByName("Copy of Sheet1").getRange("B1").copyTo(dest);
var notNeeded = newSpreadsheet.getSheetByName("Copy of Sheet1");
newSpreadsheet.deleteSheet(notNeeded);
Hopefully that helps get you moving in the right direction...
I believe copyTo does not work between different spreadsheets. What you can do is use getValue and setValue to copy the information over.
Instead of s.getRange(rowIndex, 1, 1, 16).copyTo(target); try:
var values = s.getRange(rowIndex, 1, 1, 16).getValue();
targetSheet.getRange(target).setValue(values);
Check out the range documentation page for more info, there are explanations of setValue and getValue there.