I have a workbook that I am working on for our company and need a little help on this one. Normally I can figure things out pretty easy. In my worksheet there are several tabs the ones I am working with now are NEW, In Progress, On Hold, Scheduled and Complete. Permits are added to the New tab Via google form input, what I would like to happen that when the status changes in Column K is that it goes to the corresponding tab. I had this all set up in Excel but we are no longer allowed to use Excel at work. TK's Daily Production Any help would greatly be appreciated
In sheet, click Tools > Script Editor.
Paste below code. This code assumes Column K has header 'status' and this column values have corresponding tabs = sheets in Spreadsheet. As an example, if Column K has value 'NEW', there must be a tab named 'NEW'. If you edit value in this column, say from 'NEW' to 'Complete', that row will be copied to 'Complete' tab and optionally deleted from 'NEW' tab if corresponding code is activated.
// onEdit trigger
function onEdit() {
moveToTab();
}
function moveToTab() {
// active SS
var ss = SpreadsheetApp.getActiveSpreadsheet();
// active cell/range
var range = ss.getActiveRange();
// active column
var col = range.getColumn();
Logger.log(col);
// active sheet
var aSheet = ss.getActiveSheet();
// get active column header
var header = aSheet.getRange(1,col,1,1).getValue();
Logger.log(header);
// here i assume, Column K has a header 'status'
// change 'status' string to what you have as Column K header
// if column is not 'status', do nothing
if (header.toLowerCase() !== 'status') return;
// get value
var target = range.getValue();
// trim and check there is a value
if (target.toString().trim() === '') return;
// do nothing if same sheet
if (target.toString().toLowerCase() === aSheet.getName().toLowerCase()) return;
// try {
// try move
// target sheet based on Column K value
var sheets = ss.getSheets();
var tSheet = sheets.filter(function(sheet) {
if (sheet.getName().toLowerCase() == target.toLowerCase()) return true;
else return false;
})[0];
if (!tSheet) return;
// active cell row
var row = range.getRow();
// get active row values
var values = aSheet.getRange(row, 1, 1, aSheet.getLastColumn()).getValues();
// append values to target row
tSheet.appendRow(values[0]);
// optionally delete row from old sheet
// un-comment if required
aSheet.deleteRow(row);
// } catch(e) {}
}
Related
Is it possible for my Google sheet to automatically append a new formatted row to the end of a column when it fills up?
I would like to avoid having to manually create, say, 1000 pre-formatted empty rows in my sheet.
This is possible using Google Apps Script onEdit() trigger.
Try:
function onEdit(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var range = sheet.getRange(1, 1, sheet.getLastRow(), 3); //Range is set from COL A - COL C up to last row with data
var lastRow = sheet.getLastRow();
var val = range.getValues();
var lastVal = val[val.length - 1];
//Check if the edit happened within Columns A-C
var range = e.range
var col = range.getColumn();
//Set sheetname and cell range where the options are coming from
var options = ss.getSheetByName("Names").getRange("A2:A")
var dropdownCell1 = sheet.getRange(lastRow + 1, 1);
var dropdownCell2 = sheet.getRange(lastRow + 1, 2);
if (col>= 1 && col <= 3 && !lastVal.includes('')) { //checks if the last row of data is filled
//Sets the data validation rule to the cells
var rule = SpreadsheetApp.newDataValidation().requireValueInRange(options)
dropdownCell1.setDataValidation(rule)
dropdownCell2.setDataValidation(rule)
}
}
Result:
Explanation:
This uses onEdit() trigger which runs every time you make edit to a cell value. This then checks if the edit happens within Columns A-C and checks if the last row from Columns A-C are filled. It then adds the data validation rule to the cells. The dropdown comes from a sheet called 'Names' and the options are listed there.
I just did not add a code to reverse it if a data is deleted. You may edit the range if you are using different columns. Hope this helps!
Format the sheet down to the last row, as tested here in this GIF using Sequence(20), Sequence(2000).
Suppose I have a Google Sheet where columns A:G are dropdown lists.
I would like to add an option to lock choices in columns A:G using a checkbox in column H, where checking the box in this column will lock columns A:G so they can not be edited (unless the checkbox in column H is de-selected).
Is it possible to do this on Google Sheets?
I have created the following script that is based on a previous script I found in a response here in StackOverflow a long time ago.
function onEdit(e)
{
var editRange = {
top : 1,
bottom : 1000,
left : 2,
right : 2
};
var cell = e.range;
// Exit if we're out of range
var thisRow = cell.getRow();
if (thisRow < editRange.top || thisRow > editRange.bottom) return;
var thisCol = cell.getColumn();
if (thisCol < editRange.left || thisCol > editRange.right) return;
var val1 = e.range.getRow();
var val2 = e.range.getColumn();
var validation = SpreadsheetApp.getActive().getSheetByName("Testing Sheet").getRange(val1, val2-1);
if(cell.isChecked()){
var rule = SpreadsheetApp.newDataValidation().requireValueInList(['Red', 'Blue']).build();
}
else{
var rule = SpreadsheetApp.newDataValidation().requireValueInList(['Black', 'White']).build();
}
validation.setDataValidation(rule);
}
I modified the range at the beginning so it only recognizes the change in column B, but you can just change it to 8 for column H. Since the script uses an onChange trigger, what you need to validate first is if the edit was made in the range where the checkboxes are, then I set the range of the cell that I was going to change based on the value from the checkbox and created the data validation rule with the expected values depending on the conditional.
In your case you can just add the rest of the ranges for the other columns with more validation variables and change the column parameter to -2, -3, or -4 depending on the cells you will be editing for example, and then create more validation rules according to the data you need in the dropdowns.
Here is an example of how this would work:
References:
onEdit()
Data Validation
I have two sheets. On one sheet (Sheet1), I have a column 'A' which has 5 fixed values, and a column 'B' which records the timestamp value in 'A' change. The other sheet (Sheet2) is meant to record all the changes in the first sheet in one day.
I use this simple query in the recording sheet:
=QUERY(Sheet1!A$1:X,"select * where C>="& Sheet2!D1)
with Sheet1!C has timestamps and Sheet2!D1 is the timestamp of 12:00 AM today
The problem is when I change the value of a row in C columns more than one time, instead of creating a new row in Sheet2 it change the value of that row in Sheet2 into new values.
So how do I change my code to get my desire results?
EDIT 2: here is my new code, but it doesn't help.
function importdata(x) {
// var row = x.range.getRow();
// var col = x.range.getColumn();
var addedsheet=SpreadsheetApp.getActive().getSheetByName("Change"); // Sheet where I want to keep the record of the change
var original_sheet = SpreadsheetApp.getActive().getSheetByName("Master"); //sheet where the change is happended
var compared_value = addedsheet.getRange(1,4).getValue(); // Cell D1 of sheet "Change", which has timestamp of today
var insert_area = original_sheet.getRange("A2:X").getValues() // area to get value from "Master" sheet to put into "Change"
var compared_area = original_sheet.getRange("C2:C").getValues(); // area where has timestamp
if (compared_area >= compared_value){
addedsheet.values.append([insert_area])}
} //if timestamp of one row from Master is greater than the value at Change!D1 =>append that row at the end (this is what I'm trying to do)
EDIT 3: I fixed the above code by append[insert_area][0] instead of [insert_area]
But then I have a new problem: there will a chance that a row in sheet 1 will be overwrited in sheet 2. I try something like this, but it returns nothing on the sheet.
function for_each_row(){
var addedsheet=SpreadsheetApp.getActive().getSheetByName("Change"); // Sheet where I want to keep the record of the change
var original_sheet = SpreadsheetApp.getActive().getSheetByName("Master"); //sheet where the change is happended
var compared_value = addedsheet.getRange(1,4).getValue(); // Cell D1 of sheet "Change", which has timestamp of today
var number_of_row_2 = addedsheet.getLastRow;
var number_of_row_1 = original_sheet.getLastRow();
for (var i=2; i<number_of_row_1 +1; i++){
var compared_stamp = original_sheet.getRange("C"+i).getValues();
var insert_values = (original_sheet.getRange(i,1,1,24).getValues())
if (compared_stamp > compared_value){
var insert_values = (original_sheet.getRange(i,1,1,24).getValues());
for (var j = 2; j<number_of_row_2 +1; j++){
var value_from_sheet = addedsheet.getRange(j,1,1,24).getValues();
if (insert_values ===value_from_sheet){
return
}
else(
addedsheet.appendRow(insert_values[0]))
}
}
}
}
My thought is if a row satisfies the 1st condition then the value will be check in sheet 2. If sheet 2 didn't have that row then append that row.
Issue:
If I understand you correctly, you want to do the following:
If sheet Master is edited, iterate through all rows in this sheet (excluding headers), and for each row, check the following:
Column C has a higher value than cell D1 in sheet Change.
This row, with these exact values, does not exist in Change.
If these conditions are meet, append the row to Change.
Solution:
Use filter and some to filter out rows that don't match your two conditions, and use setValues to write the resulting rows to your other sheet.
Code snippet:
function onEdit(e) {
var editedSheet = e ? e.range.getSheet() : SpreadsheetApp.getActiveSheet();
if (editedSheet.getName() === "Master") {
var addedSheet = SpreadsheetApp.getActive().getSheetByName("Change");
var compared_value = addedSheet.getRange(1,4).getValue();
var newData = editedSheet.getRange("A2:X" + editedSheet.getLastRow()).getValues();
var currentData = addedSheet.getRange("A2:X" + addedSheet.getLastRow()).getValues();
var filteredData = newData.filter(row => row[2] >= compared_value)
.filter(row => !currentData.some(currentRow => JSON.stringify(currentRow) === JSON.stringify(row)));
addedSheet.getRange(addedSheet.getLastRow()+1,1,filteredData.length,filteredData[0].length).setValues(filteredData);
}
}
Note:
An easy way to check if two rows have the same values is using JSON.stringify(row).
I'm assuming the timestamps are not Dates. If they are, you should compare them using getTime(). So you should change the corresponding code to newData.filter(row => row[2].getTime() >= compared_value.getTime()).
I'm looking to see if there is a way to delete cell A2, and then have google sheets automatically delete cells E2,G2,H2,I2. I would want this for each row for 50 rows. What is happening right now is if I delete A2, google sheets automatically pushes the next oldest data. however, it then pushes down cell E2,G2,H2,I2, because it didnt give me enough time to delete them. Help is appreciated.
https://docs.google.com/spreadsheets/d/10eK6tyHVIX6JyFChXYvtS6JmYyUnE-Ib0pClSnouBoU/edit?usp=sharing
By using a simple comparison inside your trigger you can delete the data
Here's my approach by using the code you provided inside the Sheet:
function onEdit(e){
script1(e);
script2(e);
}
function script1(e){
var row = e.range.getRow();
var col = e.range.getColumn();
if(col === 1 && row > 1 && e.source.getActiveSheet().getName() === "Kanban"){
if (e.range.getValue() === '') {
e.source.getActiveSheet().getRange(`E${row}`).setValue('');
e.source.getActiveSheet().getRange(`H${row}`).setValue('');
} else {
e.source.getActiveSheet().getRange(row,8).setValue(new Date());
}
}
}
function script2(e){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Kanban");
var range = sheet.getRange("A2:I");
range.sort([{column: 8, ascending: true}]);
}
Where if (e.range.getValue() === '') and .setValue(''); will "delete" the data when it comes to first column and it's been deleted, otherwise it'll change the Timestamp cell.
Reference
Event Object
Ok, complete newb here...I've searched and found various 'copy row to another sheet' scripts but can only get one to work. Here is my bodged version below. What I am trying to do is have a front sheet that displays the rows from sheet 1 where one of three criteria is met i.e column N has a status of 'Edit', 'SB Edit' or 'Amend Edit'. The below does do this but it doesn't loop and each time I run it, it repeats the action and I get duplicates of all the rows on the destination sheet.
Essentially, I want it running such that any time the status of column N for a particular row is changed to one of these three values, it shows up on Sheet 2. So Sheet 2 is a 'live' display of all rows in sheet 1 where column N equals the specified value
Any help much appreciated...
function copyrange() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Sheet 1'); //source sheet
var testrange = sheet.getRange('N:N');
var testvalue = (testrange.getValues());
var csh = ss.getSheetByName('Sheet 2'); //destination sheet
var data = [];
var j =[];
//Condition check in N:N; If true copy the same row to data array
for (i=0; i<testvalue.length;i++) {
if ( testvalue[i] == 'Edit') {
data.push.apply(data,sheet.getRange(i+1,1,1,15).getValues());
//Copy matched ROW numbers to j
j.push(i);
}
}
//Copy data array to destination sheet
csh.getRange(csh.getLastRow()+1,1,data.length,data[0].length).setValues(data);
for (i=0; i<testvalue.length;i++) {
if ( testvalue[i] == 'Amend Edit') {
data.push.apply(data,sheet.getRange(i+1,1,1,15).getValues());
//Copy matched ROW numbers to j
j.push(i);
}
}
//Copy data array to destination sheet
csh.getRange(csh.getLastRow()+1,1,data.length,data[0].length).setValues(data);
for (i=0; i<testvalue.length;i++) {
if ( testvalue[i] == 'SB Edit') {
data.push.apply(data,sheet.getRange(i+1,1,1,15).getValues());
//Copy matched ROW numbers to j
j.push(i);
}
}
//Copy data array to destination sheet
csh.getRange(csh.getLastRow()+1,1,data.length,data[0].length).setValues(data);
//Delete matched rows in source sheet
//for (i=0;i<j.length;i++){
//var k = j[i]+1;
//sheet.deleteRow(k);
//Alter j to account for deleted rows
//if (!(i == j.length-1)) {
//j[i+1] = j[i+1]-i-1;
}
If you want that copy to happen when the column N is modified, it's probably best to set a trigger, like onEdit. That will give you an object with data about the event that happened, including the cell that was changed and its value.
You can then use such data to get the whole row and copy it to the other sheet, like this:
function onEdit(e) {
const statuses = ['Edit', 'SB Edit', 'Amend Edit'];
// Check if the modification happened on column N (14th column) and if the new value is one of the desired statuses
if (e.range.getColumn() === 14 && statuses.includes(e.value)) {
// Get sheets
const spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
const sheet1 = spreadsheet.getSheetByName('Sheet1');
const sheet2 = spreadsheet.getSheetByName('Sheet2');
// Get the whole row of the modified cell
const modifiedRow = sheet1.getRange(e.range.getRow(), 1, 1, sheet1.getLastColumn()).getValues();
// Append modified row to the end of Sheet2
sheet2.appendRow(modifiedRow.flat());
}
}
If you want to avoid duplicated rows in Sheet2, then you need to have a way to check whether a particular row was added to Sheet2 before.
For example, if column A has a unique identifier (ID) for rows, you can search for that ID in Sheet2 before appending a new row. If it exists, then you might want to replace it with the new values or just do nothing. If it doesn't exist, then you append the row to the end:
function onEdit(e) {
// Check if the modification happened on column N (14th column) and if the new value is one of the desired statuses
if (e.range.getColumn() === 14) {
// Specify update statuses
const addStatuses = ['Edit', 'SB Edit', 'Amend Edit'];
const removeStatuses = ['On Hold', 'Version Returned'];
// Get sheets
const spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
const sheet1 = spreadsheet.getSheetByName('Sheet1');
const sheet2 = spreadsheet.getSheetByName('Sheet2');
// Get the whole row of the modified cell
const modifiedRow = sheet1.getRange(e.range.getRow(), 1, 1, sheet1.getLastColumn()).getValues();
// Assume that ID is in column A (first column)
const modifiedRowID = modifiedRow[0][0];
// Get values from Sheet2
const columnValues = sheet2.getDataRange().getValues();
// Check if Sheet2 already has row based on the value of column A and retrieve its index
let rowIndex = -1;
for (let i = 0; i < columnValues.length; i++) {
if (columnValues[i][0] == modifiedRowID) {
rowIndex = i + 1; // JavaScript arrays indices start from 0, but Sheets row indices start from 1
break;
}
}
// If row should be added/removed to/from Sheet2
if (addStatuses.includes(e.value)) {
if (rowIndex == -1) {
// Row not found in Sheet2, so add it
sheet2.appendRow(modifiedRow.flat());
} else {
// Optional: Row found in Sheet2, so replace it with current values
sheet2.getRange(rowIndex, 1, 1, modifiedRow[0].length).setValues(modifiedRow);
}
} else if (removeStatuses.includes(e.value) && rowIndex > -1) {
// Remove row from Sheet2
sheet2.deleteRow(rowIndex);
}
}
}