How can I get a cell in a cell and move into another file?
I'm trying something along below lines, like a dizzy cockroach, but unsuccessfull so far:
function moveOrders () {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const orderSheet = ss.getSheetByName('Orders');
const activeSheetName = e.source.getActiveSheet().getName();
const thisRow = e.range.getRow();
const thisCol = e.range.getColumn();
const cellValue = e.range.getValue();
const destinationSheet = ss.getSheetByName('Sheet31');
if (activeSheetName == orderSheet.getName() && thisRow > 5 && thisCol < 13 && cellValue == 'Confirmed') {
const rowValues = orderSheet.getRange(thisRow, 1, 1, 13).getValues();
const image = SpreadsheetApp.newCellImage(rowValues[2]).setAltTextDescription('Clothing Piece').toBuilder().build();
destinationSheet.getRange(destinationSheet+1,1,rowValues.length, rowValues[0].length).setValues(rowValues)
}
This is how I get the value, the image being highlighted in green (3rd element of the array:
It throws the following error:
Parametes null don't correspond to method signatures for SpreadsheetApp.newCellImage
Here's a sample file for tests: https://docs.google.com/spreadsheets/d/1gxhXeNCMZ8MnJds1LM7vGQqibYgfXDYPkA6q6VNHcN0/edit?usp=sharing
I thought that in your situation, this thread might be useful. Ref And, if this is reflected in your script, how about the following modification?
And, from const activeSheetName = e.source.getActiveSheet().getName();, const thisRow = e.range.getRow(); and so on, I guessed that you might want to use your script as the OnEdit trigger.
Modified script:
function onEdit(e) {
const orderSheet = e.source.getSheetByName('Orders');
const activeSheetName = e.source.getActiveSheet().getName();
const thisRow = e.range.getRow();
const thisCol = e.range.getColumn();
const cellValue = e.range.getValue();
const destinationSheet = e.source.getSheetByName('Sheet31');
if (activeSheetName == orderSheet.getName() && thisRow > 5 && thisCol < 13 && cellValue == 'Confirmed') {
const rowValues = orderSheet.getRange(thisRow, 1, 1, 13);
rowValues.copyTo(destinationSheet.getRange(destinationSheet.getLastRow() + 1, 1), { contentsOnly: true });
}
}
In your showing script, destinationSheet+ of destinationSheet+1 is Sheet object. In this case, an error occurs. I guessed that you might use destinationSheet.getLastRow() + 1.
When this script is run, the row is copied from "Orders" to "Sheet31" using copyTo. By this, the row including both the image and the values can be copied.
References:
Related thread.
Moving contents of a cell with app script in Google sheets
copyTo(destination)
Added
About Does this work between files, as well?, when the above script is modified, it becomes as follows. In this case, please install the OnEdit trigger to installedOnEdit.
Sample script:
function installedOnEdit(e) {
const dstSpreadsheetId = "###"; // Please set the destination Spreadsheet ID.
const orderSheet = e.source.getSheetByName('Orders');
const activeSheetName = e.source.getActiveSheet().getName();
const thisRow = e.range.getRow();
const thisCol = e.range.getColumn();
const cellValue = e.range.getValue();
if (activeSheetName == orderSheet.getName() && thisRow > 5 && thisCol < 13 && cellValue == 'Confirmed') {
const dstSS = SpreadsheetApp.openById(dstSpreadsheetId);
const destinationSheet = dstSS.getSheetByName('Sheet31');
const temp = orderSheet.copyTo(dstSS);
const rowValues = temp.getRange(thisRow, 1, 1, 13);
rowValues.copyTo(destinationSheet.getRange(destinationSheet.getLastRow() + 1, 1), { contentsOnly: true });
dstSS.deleteSheet(temp);
}
}
You can just use moveTo(), there's no need for getValues and setValues this will cut and paste both format and value from this range and target range.
Try this code instead:
function onEdit(e) {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const orderSheet = ss.getSheetByName('Orders');
const activeSheetName = e.source.getActiveSheet().getName();
const thisRow = e.range.getRow();
const thisCol = e.range.getColumn();
const cellValue = e.range.getValue();
const destinationSheet = e.source.getSheetByName('Destination');
if (activeSheetName == orderSheet.getName() && thisRow > 5 && thisCol < 13 && cellValue == 'Confirmed') {
const rowValues = orderSheet.getRange(thisRow, 1, 1, 13);
rowValues.moveTo(destinationSheet.getRange(destinationSheet.getLastRow() + 1, 1));
}
}
Let me know if this works!
Reference: https://developers.google.com/apps-script/reference/spreadsheet/range#movetotarget
Related
I have 1 spreadsheet with multiple sheets.
The 1st and the 2nd sheets sometimes have similar data in the rows (duplicates). Also, each sheet has a column (8) with a checkbox.
Task: I need to move one of the duplicates to the 3rd sheet when the checkboxes in both sheets (1st and 2nd) are checked.
Here is the code that moves the row when it's checked in a single sheet.
Can someone help me modify it to complete my task?
function onEdit(e) {
let range = e.range;
let col = range.getColumn();
let row = range.getRow();
let val = range.getValue();
let source = e.source.getActiveSheet();
let ss = SpreadsheetApp.getActiveSpreadsheet();
let sourceSheet = ss.getSheetByName(source.getName());
if (sourceSheet.getName() == 'SHEET1' && col == 8 && val == true){
let data = sourceSheet.getRange(row,1,1,7).getValues();
let targetSheet = ss.getSheetByName('SHEET2');
targetSheet.appendRow(data[0]);
sourceSheet.deleteRow(row);
}
if (sourceSheet.getName() == 'SHEET2' && col == 8 && val == true){
let data = sourceSheet.getRange(row,1,1,7).getValues();
let targetSheet = ss.getSheetByName('SHEET3');
targetSheet.appendRow(data[0]);
sourceSheet.deleteRow(row);
}
}
Sample Spreadsheet
Apply filter()
You can add the filter() method (combined with the JSON.stringify() method) to your script to determine if a row is a duplicate of another from the other sheet. The modified script should somehow look like this:
function onEdit(e) {
var range = e.range;
var col = range.getColumn();
var row = range.getRow();
var val = range.getValue();
var source = e.source.getActiveSheet();
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sourceSheet = ss.getSheetByName(source.getName());
var targetSheet = ss.getSheetByName('Sheet3');
//If Sheet1 checkbox is triggered
if (sourceSheet.getName() == 'Sheet1' && col == 8 && val == true) {
var rowArr = ss.getSheetByName('Sheet1').getRange(row, 1, 1, 8).getValues();
var lastRow2 = ss.getSheetByName('Sheet2').getLastRow();
var sheet2Values = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet2').getRange(1, 1, lastRow2, 8).getValues();
var count = sheet2Values.filter(x => {
return (JSON.stringify(x) === JSON.stringify(rowArr[0]))
});
var otherRow = sheet2Values.map((x, i) => {
if (JSON.stringify(x) === JSON.stringify(rowArr[0])) {
return i;
}
});
if (count.length > 0) {
var otherRow = sheet2Values.map((x, i) => {
if (JSON.stringify(x) === JSON.stringify(rowArr[0])) {
return i;
}
}).filter(y => y);
rowArr[0].pop();
targetSheet.appendRow(rowArr[0]);
sourceSheet.deleteRow(row);
var otherSheet = ss.getSheetByName('Sheet2');
otherSheet.deleteRow(otherRow[0] + 1)
}
}
//If Sheet2 checkbox is triggered
if (sourceSheet.getName() == 'Sheet2' && col == 8 && val == true) {
var rowArr = ss.getSheetByName('Sheet2').getRange(row, 1, 1, 8).getValues();
var lastRow = ss.getSheetByName('Sheet1').getLastRow();
var sheet1Values = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet1').getRange(1, 1, lastRow, 8).getValues();
var count = sheet1Values.filter(x => {
return (JSON.stringify(x) === JSON.stringify(rowArr[0]))
});
if (count.length > 0) {
var otherRow = sheet1Values.map((x, i) => {
if (JSON.stringify(x) === JSON.stringify(rowArr[0])) {
return i;
}
}).filter(y => y);
rowArr[0].pop();
targetSheet.appendRow(rowArr[0]);
sourceSheet.deleteRow(row);
var otherSheet = ss.getSheetByName('Sheet1');
otherSheet.deleteRow(otherRow[0] + 1)
}
}
}
Output
Based on your sample spreadsheet, the modified script should work as shown below:
Please take note that the appendRow() method appends any row to the last non-empty row in the sheet. A row with a checkbox is not considered to be an empty row thus explains the behavior of the output.
Reference
filter()
JSON.stringify()
Would that do what you want ?
function copyDuplicateRows() {
var spreadsheet = SpreadsheetApp.getActive();
var sheet1 = spreadsheet.getSheetByName("Sheet1");
var sheet2 = spreadsheet.getSheetByName("Sheet2");
var sheet3 = spreadsheet.getSheetByName("Sheet3"); // destination sheet
var data1 = sheet1.getDataRange().getValues();
var data2 = sheet2.getDataRange().getValues();
// Use a JavaScript object to keep track of duplicate rows
var seen = {};
// Iterate through data1 and copy unique rows to sheet3
for (var i = 0; i < data1.length; i++) {
var row = data1[i];
var key = row.join(); // create a unique key for each row
if (!seen[key]) {
seen[key] = true;
sheet3.appendRow(row);
}
}
// Iterate through data2 and copy unique rows to sheet3
for (var i = 0; i < data2.length; i++) {
var row = data2[i];
var key = row.join();
if (!seen[key]) {
seen[key] = true;
sheet3.appendRow(row);
}
}
}
I am trying to get Google Sheets show a popup message whenever a cell changes from FALSE to TRUE. I have found a few ways of doing it using onEdit functions. The problem here is that the Cell in question does not really get edited, but rather changes value due to another set of cells meeting certain criteria. That means onEdit functions won't run when the value changes.
I tried this script, but it does nothing:
//popup message
function alertMessageYesNoButton() {
var result = SpreadsheetApp.getUi().alert("Are you sure you want to send an alert about?", SpreadsheetApp.getUi().ButtonSet.YES_NO);
SpreadsheetApp.getActive().toast(result);
}
function sendMessageYesNo(e){
var sheet = SpreadsheetApp.getActive().getSheetByName('Sheet1');
var cellValue = sheet.getRange(4, 17).getValue();
if (cellValue == 'TRUE'){
alertMessageYesNoButton();
}else{}
}
If someone could come up with something that works, I would really appreciate it.
From your following reply,
cell D17 changes from FALSE to TRUE when three other checkboxes are checked using this formula: {"";SUMPRODUCT(B16:B18)=COUNTA(B16:B18)}.
In this case, how about checking the checkboxes of "B16", "B17", "B18"? When this is reflected in your showing script, how about the following modification? I thought that when those checkboxes are manually checked, onEdit trigger can be used.
In this modification, when all checkboxes of "B16:B18" of "Sheet1" are checked, your script of alertMessageYesNoButton() is run. So, when you test this, please check all checkboxes of "B16:B18" of "Sheet1". By this, the script is run.
Pattern 1:
function onEdit(e) {
const sheetName = "Sheet1"; // This is from your script.
const checkBoxRange = "B16:B18"; // This is from your reply.
const { range } = e;
const sheet = range.getSheet();
const r = sheet.getRange(checkBoxRange);
const rowStart = r.getRow();
const rowEnd = rowStart + r.getNumRows() - 1;
const colStart = r.getColumn();
const condition1 = sheet.getSheetName() == sheetName;
const condition2 = range.rowStart >= rowStart && range.rowEnd <= rowEnd && range.columnStart == colStart;
const condition3 = r.getValues().every(([b]) => b === true);
if (!condition1 || !condition2 || !condition3) return;
alertMessageYesNoButton(); // This is your script.
}
Pattern 2:
function onEdit(e) {
const sheetName = "Sheet1"; // This is from your script.
const checkBoxRange = "B16:B18"; // This is from your reply.
const { range } = e;
const sheet = range.getSheet();
const r = sheet.getRange(checkBoxRange);
const rowStart = r.getRow();
const rowEnd = rowStart + r.getNumRows() - 1;
const colStart = r.getColumn();
const condition1 = sheet.getSheetName() == sheetName;
const condition2 = range.rowStart >= rowStart && range.rowEnd <= rowEnd && range.columnStart == colStart;
const condition3 = r.getValues().every(([b]) => b === true);
if (!condition1 || !condition2 || !condition3) return;
// This is your script.
var cellValue = sheet.getRange(4, 17).getValue();
if (cellValue == 'TRUE') {
alertMessageYesNoButton();
}
}
Reference:
Simple Triggers
Added:
From your following reply,
this works very well for the example I laid out, but I tried using several multiple instances of the same script within the same sheet and it always works only in one of them. So let´s say I have for instances of the same situation (B16:B18), (B13:B15), (D13:D15) and (D16:D18). Only when I check the boxes corresponding to the last function (from top to bottom), the message pops up.
From your question and your reply, I proposed an answer as the checkboxes of "B16:B18". But from your reply, when your checkboxes are "B16:B18", "B13:B15", "D13:D15", "D16:D18", how about the following sample script?
Sample script:
function onEdit(e) {
const sheetName = "Sheet1"; // This is from your script.
const checkBoxRanges = ["B16:B18", "B13:B15", "D13:D15", "D16:D18"]; // This is from your reply.
const { range } = e;
const sheet = range.getSheet();
const actSheetName = sheet.getSheetName();
const res = checkBoxRanges.find(checkBoxRange => {
const r = sheet.getRange(checkBoxRange);
const rowStart = r.getRow();
const rowEnd = rowStart + r.getNumRows() - 1;
const colStart = r.getColumn();
const condition1 = actSheetName == sheetName;
const condition2 = range.rowStart >= rowStart && range.rowEnd <= rowEnd && range.columnStart == colStart;
const condition3 = r.getValues().every(([b]) => b === true);
return condition1 && condition2 && condition3;
});
if (!res) return;
Browser.msgBox(`Checked checkboxes of ${res}`); // Here, you can see the checkbox group that was checked.
alertMessageYesNoButton(); // This is your script.
}
In this case, by giving const checkBoxRanges = ["B16:B18", "B13:B15", "D13:D15", "D16:D18"], the script detects the checked checkboxe group, and show the range as a sample.
I have a google sheet with a column full of checkboxes. My goal is to have a row of the selected adjacent columns of data (namedInsured, lineCoverage, etc.) append to a separate sheet when a checkbox is selected and True. This is what I currently have, and it does absolutely nothing.
function onEdit(e) {
const ss = e.range.getSheet();
var active_range = ss.getActiveRange();
var namedInsured = ss.getRange(active_range.getRowIndex(), 4).getValue();
var lineCoverage = ss.getRange(active_range.getRowIndex(), 5).getValue();
var effDate = ss.getRange(active_range.getRowIndex(), 6).getValue();
var assignedTo = ss.getRange(active_range.getRowIndex(), 15).getValue();
var data = [namedInsured, lineCoverage, effDate, assignedTo];
const destinationSheet = ss.getSheetByName("Sheet4");
if (ss.getName() != "Sheet4" && e.range.columnStart == 10 && e.value == "TRUE")
{
e.source.toast("Quote In Process Entered")
destinationSheet.appendRow(data);
}
}
[This is an example of the desired output]
<< If the first and fourth checkboxes were selected one after the other, this is how they should appear on the separate sheet
This works for me
function onEdit(e) {
e.source.toast('Entry')
const sh = e.range.getSheet();
if (sh.getName() == "Sheet0" && e.range.columnStart == 10 && e.value == "TRUE") {
e.source.toast('Flag1');
const [a,b,c] = sh.getRange(e.range.rowStart, 4,1,3).getValues()[0];
var d = sh.getRange(e.range.rowStart, 15).getValue();
var data = [a, b, c, d];
const dsh = e.source.getSheetByName("Sheet1");//modified from original
dsh.appendRow(data);
}
}
Demo:
Script works really well but I have 12 columns and this script only works on A and B.
function onEditTwoWay(e) {
var ss = SpreadsheetApp;
var sheet = ss.getActiveSpreadsheet();
var sheet1 = sheet.getSheetByName("Dep1");
var sheet2 = sheet.getSheetByName("Dep2");
var cell = sheet.getActiveCell();
var value = cell.getValue();
var currentRow = cell.getRow();
var currentColumn = cell.getColumn();
var activeWorksheet = ["Dep1","Dep2"];
var columnEdit = [1,2,12];
if(activeWorksheet.indexOf(ss.getActiveSheet().getName()) > -1 &&
columnEdit.indexOf(currentColumn) > -1 && currentRow > 1) {
sheet1.getRange(currentRow, currentColumn).setValue(value);
sheet2.getRange(currentRow, currentColumn).setValue(value);
};
};
Try this:
function onEdit(e) {
const sh = e.range.getSheet();
var shts = ["Dep1", "Dep2"];
var cols = [1, 2, 12];
if (~shts.indexOf(sh.getName()) && ~cols.indexOf(e.range.columnStart) && e.range.rowStart > 1) {
shts.forEach(name => {
e.source.getSheetByName(name).getRange(e.range.rowStart, e.range.columnStart).setValue(e.value);
});
}
}
All columns need to be entered in columnEdit so changed from
columnEdit = [1,2,12] to columnEdit = [1,2,3,4,5,6,7,8,9,10,11,12]
The objective I'm trying to achieve is to compare all the values in a column and if the value is NOT blank, move the entire row to another sheet, and delete the row from the first sheet. I'm very novice at this, and I know that my code is incomplete to accomplish the objective, but so far this is what I have:
function onEdit(event) {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const row = event.range.getRow();
const col = event.range.getColumn();
const as = event.source.getActiveSheet();
const numColumns = as.getLastColumn();
const targetSheet = ss.getSheetByName("Sheet2");
const target = targetSheet.getRange(targetSheet.getLastRow() + 1, 1);
if(as.getName() == "Form Responses 1" && as.getRange(row,19) == "") {
as.getRange(row, 1, 1, numColumns).moveTo(target);
//as.deleteRow(row);
}
}
function onEdit(e) {
const sh = e.range.getSheet();
if (sh.getName() == "Form Responses 1" && sh.getRange(e.range.rowStart, 19).getValue() != "") {
const tsh = ss.getSheetByName("Sheet2");
const trg = tsh.getRange(tsh.getLastRow() + 1, 1);
sh.getRange(e.range.rowStart, 1, 1, sh.getLastColumn()).copyTo(trg);
}
}
Then you probably can't delete either