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.
Related
I'm automating the appointment process at my company and I have been successfully implementing some google scripts with time trigger.
Whenever someone takes an appointment on Calendly it creates at row with several information through Zapier.
I then have a script with several functions that operates on the newly added rows.
One function auto sort the new row based on the date column then two others functions fill two columns with a checkbox(FALSE) and a datavalidation based on a list of choice. All of those functions are time triggered, let's say 30 minutes.
The problem is whenever the trigger happens it automatically checks the checkbox to TRUE for the entire column and to the first choice of the list for the entire datavalidation column.
How can I solve that ?
var SORT_COLUMN_INDEX = 4;
var ASCENDING = true;
var NUMBER_OF_HEADER_ROWS = 1;
var activeSheet;
function autoSort() {
console.log(sheet, activeSheet)
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
var range = sheet.getDataRange();
if (NUMBER_OF_HEADER_ROWS > 0) {
range = range.offset(NUMBER_OF_HEADER_ROWS, 0);
}
range.sort( {
column: SORT_COLUMN_INDEX,
ascending: ASCENDING
} );
}
// Fonction to automatically add data validation in column K
function setDataValidationComing() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
var listOfChoices = ["Coming","Not Coming","Message Left", "Unreachable"]
var validation = SpreadsheetApp.newDataValidation().requireValueInList(listOfChoices).build();;
sheet.getRange("K2").setDataValidation(validation);
var lr = sheet.getLastRow();
var fillDownRange = sheet.getRange(2, 11, lr-1);
sheet.getRange("K2").copyTo(fillDownRange);
}
// Fonction to automatically add checkbox for appointment honored in column L
function setCheckboxCame() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
var validation = SpreadsheetApp.newDataValidation().requireCheckbox().build();
sheet.getRange("L2").setDataValidation(validation);
var lr = sheet.getLastRow();
var fillDownRange = sheet.getRange(2, 12, lr-1);
sheet.getRange("L2").copyTo(fillDownRange);
}
Here is a screenshot of the google sheet. Google sheet screenshot
Thanks for your help, I've just started using Google Script a week ago !
The problem is whenever the trigger happens it automatically checks the checkbox to TRUE for the entire column and to the first choice of the list for the entire datavalidation column.
It is not actually checking it to true for the entire column. It is copying the value of the first row after the header, and applying that value to each checkbox in the column. The first row of data shows 'Coming' so if you run that script, it will apply Coming to all of them. If you change it to 'Not Coming', it would apply 'Not coming' to every row. This is because of this line:
sheet.getRange("K2").copyTo(fillDownRange);
You don't want to copy the value of K2, you want to only copy the validation. So that line should really be:
`sheet.getRange("K2").copyTo(fillDownRange, SpreadsheetApp.CopyPasteType.PASTE_DATA_VALIDATION, false);`
As for the checkbox, that's a little bit trickier, but the same concept applies:
function setCheckboxCame() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
// UPDATED THIS PART
var validation = SpreadsheetApp.newDataValidation().requireCheckbox();
validation.setAllowInvalid(false);
validation.build();
var lr = sheet.getLastRow();
var fillDownRange = sheet.getRange(2, 12, lr-1);
// CHANGE THIS:
//sheet.getRange("L2").copyTo(fillDownRange);
// TO THIS:
fillDownRange.setDataValidation(validation);
}
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 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.
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}]);
}
Im currently using google sheets and a script to move a portion of row when i select "ok" in a data validation column , the problem is that it copies the formulas of each cell and not the displayed value, and ideas, Im not the best at this so any help is HUGE.
/**
* Moves row of data to another spreadsheet based on criteria in column 6 to sheet with same name as the value in column 4.
*/
function onEdit(e) {
// see Sheet event objects docs
// https://developers.google.com/apps-script/guides/triggers/events#google_sheets_events
var ss = e.source;
var s = ss.getActiveSheet();
var r = e.range;
// to let you modify where the action and move columns are in the form responses sheet
var actionCol = 6;
var nameCol = 4;
// 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.
// -1 to drop our action/status column
var colNumber = s.getLastColumn()-1;
// if our action/status col is changed to ok do stuff
if (e.value == "ok" && colIndex == actionCol) {
// get our target sheet name - in this example we are using the priority column
var targetSheet = s.getRange(rowIndex, nameCol).getValue();
// if the sheet exists do more stuff
if (ss.getSheetByName(targetSheet)) {
// set our target sheet and target range
var targetSheet = ss.getSheetByName(targetSheet);
var targetRange = targetSheet.getRange(targetSheet.getLastRow()+1, 1, 1, 6); //6 represents Numer of Columns to Copy
// get our source range/row
var sourceRange = s.getRange(rowIndex, 1, 1, 6); //6 represents Numer of Columns to Copy
// new sheets says: 'Cannot cut from form data. Use copy instead.'
sourceRange.copyTo(targetRange);
// ..but we can still delete the row after
// or you might want to keep but note move e.g. r.setValue("moved");
}
}
}
Add the option to sourceRange.copyTo(targetRange); as described here:
https://developers.google.com/apps-script/reference/spreadsheet/range#copyTo(Range,Object)
Please look at the docs before posting, thanks. Good luck.