Copy cells to second sheet based on cell value using a script - google-apps-script

We use google sheets for our invoice system. Once we pull the order, we fill in the invoice and anything that we do not have we backorder. We type BO in column I. In excel, we used a button that we could click to copy those cells to our 2nd sheet. We need cells A & B to copy to sheet 2 (which is an exact copy of sheet 1) if column I says BO. Here's what I have so far. This almost works... It deletes everything above the rows with data though and copies the data even if column I doesn't have BO.
I need it to just copy Column A & B if column I says BO to sheet 2 (which is a duplicated of sheet 1) I'm sure there's a simple way, but I can't seem to figure it out.
function Backorder() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Carolina Fireworks Order Form");
var columnToSearch = 9; // I column
Logger.log(sheet.getLastRow());
var range =sheet.getRange(1,columnToSearch,sheet.getLastRow(),columnToSearch);
var destination = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Back Order");
var values = range.getValues();
//Destination
var numberOfColumns = 12;
var numberOfRows = 223 ;
var startColumn = 1;
var startRow = 12;
// equivalent to destination.getRange('A1:L223');
var destinationRange= destination.getRange(startRow, startColumn,numberOfRows,numberOfColumns);
var count = '0';
for(var i=0;i<values.length;i++){
Logger.log("i is now: " +i);
Logger.log("Current row value: " +values[i][0])
if(values[i][0] == 'BO'){
Logger.log("*** Value found in column: " +values[i][0] +"**** \n Count: " +i);
count++;
var rangeToCopy = sheet.getRange(i, 1,numberOfRows,numberOfColumns);
rangeToCopy.copyTo(destinationRange);
}
}
}

SOLUTION
**Updated **
The updated script below will loop through the sourceSheet on every row and it specifically checks if every row on column I says "BO".
If there's a match on that specific row of column I, the row # from the loop will be added to rangeToCopy variable (e.g. range "A(row #):B(row #)" in A1 notation). Then, the rangeToCopy will be copied to the destination in the same exact range of rangeToCopy, given that the destination is the same exact copy of sourceSheet.
function backOrder(){
var sheet = SpreadsheetApp.getActive();
var sourceSheet = sheet.getSheetByName("Carolina Fireworks Order Form");
var destination = sheet.getSheetByName("Back Order");
var lastRow = sourceSheet.getDataRange().getLastRow();
//LOOP THROUGH SOURCE SHEET
for(var row=1; row<=lastRow; row++){
//CHECK FOR "BO" ON EVERY ROWS OF COL I ON SOURCESHEET
if(sourceSheet.getRange(row,9).getValue() == "BO"){
Logger.log("CELL I"+row+" has \"BO\""+"\n=================\n"+"RANGE TO COPY:\n"+ "\"A"+row+":B"+row+"\"");
var rangeToCopy = "A"+row+":B"+row;
sourceSheet.getRange(rangeToCopy).copyTo(destination.getRange(rangeToCopy));
sourceSheet.getRange(row,9).setValue("- BO").setHorizontalAlignment("right");
}
}
}
RESULT
Sample filled out Carolina Fireworks Order Form sheet:
The Back Order sheet after running the script:
Execution logs result for review:

Related

Add date and text at the last row after a copy paste

I'm breaking my head here trying to figure it out a way to achieve this in a simples way.
I got working a code that copies a range from a sheet and paste in another at the last row.
function copyInfo() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var copySheet = ss.getSheetByName("Review");
var pasteSheet = ss.getSheetByName("Log");
// get source range
var source = copySheet.getRange(3,2,15,5);
// get destination range
var destination = pasteSheet.getRange(pasteSheet.getLastRow()+1,3,15,5);
// copy values to destination range
source.copyTo(destination, SpreadsheetApp.CopyPasteType.PASTE_VALUES, false);
}
The problem is, at the destination sheet, I still have columns A and B empty. By each row in the source (varies from time to time) copied, I need to place at column A todays date and column B the text "Review".
Like: Date (column A) | Revview (column B) | Pasted Data from Source (column C and other)
Do I need to write a completely new code to check for empty or can I implement both solutions into this code?
The code should fill the two cells before your data. It also caters for the setup where you have three areas in the same sheet.
Each copyInfoXd_button function will be assigned to your button image.
function copyInfo1d_button() {
copyInfo(2, "B1:B");
}
function copyInfo7d_button() {
copyInfo(9, "I1:I");
}
function copyInfo30d_button() {
copyInfo(16, "P1:P");
}
function copyInfo(startingCol, subjectCol)
{
var ss = SpreadsheetApp.getActiveSpreadsheet();
var copySheet = ss.getSheetByName("Review");
var pasteSheet = ss.getSheetByName("Log");
const sourceCols = 4; //actual number of columns to copy, not including the empty column H
const destCols = 6; //actual number of columns to write to, from DATE to OBS
//find the last row from a colum
var avals = copySheet.getRange(subjectCol).getValues();
var lastRow = avals.filter(String).length;
var rowsToCopy = lastRow - 2; //skip the first 2 rows as they are headers
// get source range
var source = copySheet.getRange(3,startingCol,rowsToCopy,sourceCols); // row, column, number of rows and number of columns
// get destination range
var destination = pasteSheet.getRange(pasteSheet.getLastRow()+1,1,rowsToCopy,destCols);
var data = [];
var sourceValues = source.getValues();
//construct new data for the destination range
for (var row in sourceValues) {
var destRow = [Utilities.formatDate(new Date(), ss.getSpreadsheetTimeZone(), 'MMMM dd, yyyy'), 'Review'];
destRow = destRow.concat(sourceValues[row]);
data.push(destRow);
}
destination.setValues(data);
}
I made it! I'm so proud! Thank you all for the help!
I'm beginning to do stuff like this!
After the Charles answer I looked for a way to add to each row. However, I know the code might not be too elegant, and I couldn't figured it out a way of doing using script itself, so I added a helper cell inside the sheet in H1 and H2.
Here's the solution:
function copyInfo24() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var copySheet = ss.getSheetByName("Review");
var pasteSheet = ss.getSheetByName("Log");
// get source range
var source = copySheet.getRange(4,2,99,5);
// get destination range
var destination = pasteSheet.getRange(pasteSheet.getLastRow()+1,3,99,5);
// copy values to destination range
source.copyTo(destination, SpreadsheetApp.CopyPasteType.PASTE_VALUES, false);
// Sets date at Column A and "Review" at Column B.
var data = [];
for(var i =0; i < copySheet.getRange("H1").getValue(); i++) {
data.push([Utilities.formatDate(new Date(), ss.getSpreadsheetTimeZone(), 'MMMM dd, yyyy'), 'Review']);
}
reviewRange = pasteSheet.getRange(pasteSheet.getLastRow()-copySheet.getRange("H2").getValue(), 1, copySheet.getRange("H1").getValue(), 2)
reviewRange.setValues(data); //set the date and 'Review' data
}

Getting Specific Rows from Another Google Sheet via App Script Based on Multiple Cell Values

I would like to fetch data from another google sheet, then retrieve all columns(been successful with this one). As for the rows, I would only like to retrieve all rows with these values, "Mrm1, Mrm2, Mrm3" from Measurement Column.
For example, I have these on another sheet,Original Data
And on another sheet, I want the result to be like this: Expected Retrieved Data
So far, I have this, but I'm not sure how to apply what I need on the rows. This script is retrieving everything.
function Country A() {
var sss = SpreadsheetApp.openById("19-idD8PWuNxnvzRVqBlKhju2RkKxOe8nUQaf9ZMAcgk"); // Source spreadsheet - replace with actual key
var ss = sss.getSheetByName('Compiled'); // Source sheet - change to actual name
var numRows = ss.getLastRow() // get last row with data
var numColumn = ss.getLastColumn(); // get last column with data
var srange = ss.getRange(2,1,numRows,numColumn); // Source range - change to actual range
var values = srange.getDisplayValues();
var lss = SpreadsheetApp.getActiveSpreadsheet(); // Local spreadsheet - the one this script is in
var targetnumRows = lss.getSheetByName('Country A').getMaxRows();
var targetnumcolumns = lss.getSheetByName('Country A').getMaxColumns();
var ls = lss.getSheetByName('Country A').getRange(2,1,numRows,numColumn); // Local sheet - change to actual name and Local range - change to actual range
var TargetRange = lss.getSheetByName('Country A').getRange(2,1,targetnumRows,targetnumcolumns); // Local sheet - change to actual name and Local range - change to actual range
TargetRange.clear() //Clear Sheet Content
ls.setValues(values); // Copy from source sheet to local sheet
var sheet = lss.getSheetByName('Country A');
var maxColumns = sheet.getMaxColumns();
var lastColumn = sheet.getLastColumn();
var maxRows = sheet.getMaxRows();
var lastRow = sheet.getLastRow();
if (maxColumns-lastColumn != 0){sheet.deleteColumns(lastColumn+1, maxColumns-lastColumn)};
if (maxRows-lastRow != 0){sheet.deleteRows(lastRow+1, maxRows-lastRow)};
var TargetHour = lss.getSheetByName('Country A').getRange(1,1).setValue(Utilities.formatDate(new Date(), "CST", "MM-dd-yyyy HH:mm:ss"));
}
Please help me. Thank you.
The script stores the values in a 2D array. You have to iterate through the elements and delete the elements where column E (Col number 4) (A=0, B=1,...E=4) is not equal to "Mrm1, Mrm2, Mrm3".
Then you paste the modified array in the second sheet.
Here is an example. You need to adapt it to your code.
function copyData() {
var ss = SpreadsheetApp.openById("YOUR-ID");
// Stores data as a 2D Array
var data = ss.getSheetByName("Sheet1").getRange("A1:M10").getValues();
// Remove rows where column E not equals "Mrm1, Mrm2, Mrm3"
for (var i = 0; i < data.length; i++) {
if (data[i][4] !== "Mrm1" && data[i][4] !== "Mrm2" && data[i][4] !== "Mrm3") {
data.splice(i,1);
i--;
}
}
// Paste data in sheet 2
var pasteRange = "A1:M" + (data.length);
ss.getSheetByName("Sheet2").getRange(pasteRange).setValues(data);
}

Time trigger to cut & paste row when cell equals N/A

I have a spreadsheet with the following 2 sheets:
Sheet1 "Induction Checklist" is a list of current staff (around 400) from A3:M - Row 2 is the header row.
Sheet2 "Induction Checklist - Staff Left" is a duplicate of Sheet1 but a list of previous staff from A3:M - Row 2 has the same headers as Sheet1
Column C is the Staff ID (number) in both sheets.
Column C of Sheet1 has an ImportRange/Vlookup formula to an entirely different spreadsheet (Source Sheet) to find the Staff ID. When the employee is removed from the Source Sheet, the formula can't find the employee so the value in column C of Sheet1 changes to "N/A".
I want to create a time-based trigger to find the "N/A" values in column C of Sheet1, then cut and paste only those rows to Sheet2 and delete the same rows from Sheet1. If "N/A" is not found, do nothing.
This is a sample of my spreadsheet:
Sample Spreadsheet
I have the following script but can't get it to work. I would really appreciate some help.
function removeOldStaff() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Induction Checklist");
var range = sheet.getRange(1, 1, sheet.getLastRow(), sheet.getLastColumn());
var column = 3
var errorColumn = range.getColumn();
var errorRow = range.getRow();
var error = range.getValue();
var value = "N/A"
if(error == value && errorColumn == column && errorRow > 3 && sheet.getName() == sheetName) {
var numCols = sheet.getLastColumn();
var row = sheet.getRange(errorRow, 1, 1, numCols).getValues();
var destinationSheet = ss.getSheetByName("Induction Checklist - Staff Left");
// Get first empty row:
var emptyRow = destinationSheet.getLastRow() + 1;
// Copy values from 'Induction Checklist'
destinationSheet.getRange(emptyRow, 1, 1, numCols).setValues(row);
sheet.deleteRow(errorRow);
}
}
Issue:
You are not iterating through the different rows in your range. For example, range.getValue() will only return the value of the top-left cell in the range, in your case A1. That's hardly what you want to look for. Similarly, functions like getColumn() or getRow() don't make sense in this context.
Solution:
Use getValues() to get all the values in your range in a 2D array, and iterate through that (for example, with for).
For each row, check the value in column C.
If the value is #N/A, push the full row of data to and array with all the rows to copy (dataToCopy in the sample below), and delete the row from the source sheet (you should loop in reverse order because otherwise deleting the rows would mess with the row indexes).
Copy dataToCopy to the target sheet via setValues
Code sample:
function removeOldStaff() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Induction Checklist");
var destinationSheet = ss.getSheetByName("Induction Checklist - Staff Left");
var firstRow = 3;
var column = 3;
var value = "#N/A";
var values = sheet.getRange(firstRow, 1, sheet.getLastRow() - firstRow + 1, sheet.getLastColumn()).getValues();
var dataToCopy = [];
for (var i = values.length - 1; i >= 0; i--) {
var row = values[i];
var errorValue = row[column - 1];
if (errorValue == value) {
dataToCopy.push(row);
sheet.deleteRow(firstRow + i);
}
}
// Get first empty row:
var emptyRow = destinationSheet.getLastRow() + 1;
// Copy values from 'Induction Checklist'
destinationSheet.getRange(emptyRow, 1, dataToCopy.length, dataToCopy[0].length).setValues(dataToCopy);
}

Google Script: copy values from one sheet to another determining the range

I have two sheets with unique IDs in one column. I need to make sure whenever a new ID is added in one sheet (Sheet1), it is copied to the last empty row in the other sheet (Sheet2). The IMPORTRANGE won't work as being dynamic any static information added in the other sheet would be irrelevant to the respective ID.
In another thread [1], I got help developing this script that will do exactly that. However, this script will insert the data in the other sheet in Column A. How can I modify the script so I can decide the specific range where I want the script to insert the data (in Sheet 2).
Update: I created this spreadsheet as an example. I'm trying to ensure that the script does the calculation (ie. adding vlaues that are not duplicated in the first available empty row), starting in cel C10 in "Sheet2". It would be also great if I can somehow change that range if I need to: https://docs.google.com/spreadsheets/d/1abESAXFrOHoqRQRNqQGfmAFxlbD10wlprAf1tca4y7o/edit#gid=132361488
Thanks!
function updateSheet() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sourceSheet = "Sheet1";
var destinationSheet = "Sheet2";
var source_sheet = ss.getSheetByName(sourceSheet);
var target_sheet = ss.getSheetByName(destinationSheet);
var lastCol = target_sheet.getLastColumn();
var lastRow = target_sheet.getLastRow();
if (lastRow > 1) { // <--- Added
var r = target_sheet.getRange(2,1, lastRow - 1, lastCol);
r.sort([{column: 1, ascending: true}]);
}
_updateSpreadsheet(source_sheet, target_sheet);
}
function _updateSpreadsheet(source_sheet, target_sheet) {
var last_row = target_sheet.getLastRow();
var source_data = source_sheet.getRange("A4:A" + source_sheet.getLastRow()).getValues(); // <--- Modified
var target_data = target_sheet.getDataRange().getValues();
var resultArray = [];
for (var n = 0 ; n < source_data.length ; n++) { // <--- Modified
var keep = true;
for(var p in target_data) {
if (source_data[n][0] == target_data[p][0]) {
keep = false; break;
}
}
var columnsToKeep = [0];
var tempData = [];
if(keep){
for(var c in columnsToKeep){ tempData.push(source_data[n][columnsToKeep[c]])}
resultArray.push(tempData);
}
}
last_row++;
resultArray = resultArray.filter(String); // <--- Added
if(resultArray.length>0){
target_sheet.getRange(last_row,1,resultArray.length,resultArray[0].length).setValues(resultArray);
}
}
[1] Google Script: Append new values in column to another sheet
target_sheet.getRange(last_row,1,resultArray.length,resultArray[0].length)
.setValues(resultArray);
This line
Takes the sheet stored in the variable target_sheet
Accesses the defined range
Sets values
The syntax to access a range used in your code is getRange(row, column, numRows, numColumns)
whereby the start column is numerical - in your case it's 1 which corresponds to column A.
If you want to modify the start column from A to B:
Just change your range definition from
getRange(last_row,1,resultArray.length,resultArray[0].length)
to
getRange(last_row,2,resultArray.length,resultArray[0].length)

Trying to copy a row from one sheet to another based on the value in the first column of each row

I'm trying to move yesterday's data off of one sheet onto another sheet(from one tab at the bottom to another tab at the bottom), then delete it off of the first sheet(while preserving the equations).
Here's an example spreadsheet.
I have a google script running every night just after midnight, and I've tried to iterate through every single row, check if the first column has yesterday's date, then copy the row to the other sheet and delete the original row.
function moveDataNewSheet() {
var DATA_SPREADSHEET_ID = "16cy4ClKYEN_w5_c6KiR2zSjRsUD9ijxQD9DGffNRXtI";
var Sheet1 = SpreadsheetApp.openById(DATA_SPREADSHEET_ID).getSheetByName("Sheet1");
// create sheet with yesterdays date
// if col1 has yesterdays date,
// then copy it to yesterdays sheet
// delete row
//get dateString
var today = new Date();
var yesterday = new Date();
yesterday.setDate(today.getDate()-1);
var dateString = Utilities.formatDate(yesterday, 'EDT', 'yyyy-MM-dd');
// create new sheet with yesterdays date
var activeSpreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var yourNewSheet = activeSpreadsheet.getSheetByName(dateString);
if (yourNewSheet != null) {
activeSpreadsheet.deleteSheet(yourNewSheet);
}
yourNewSheet = activeSpreadsheet.insertSheet();
yourNewSheet.setName(dateString);
//set active sheet to Sheet1
SpreadsheetApp.setActiveSheet(Sheet1);
SpreadsheetApp.getActiveSpreadsheet().moveActiveSheet(1);
// if col1 has yesterdays date copy it to dateString's sheet
// delete row
var ss = SpreadsheetApp.getActiveSpreadsheet();
var source_sheet = SpreadsheetApp.getActiveSpreadsheet();
var target_sheet = activeSpreadsheet.getSheetByName(dateString);
for (var i = 0; i < Sheet1.getLastRow(); i++) {
if (Sheet1.getRange(i,1).getValue() == Sheet1.getRange(i,1).substring(0,10)) {
var last_row = target_sheet.getLastRow();
target_sheet.insertRowAfter(last_row);
source_sheet.getRange(i,1).clear();
}
}
The OP is trying to backup previous day data to a specific sheet and then delete that data from the source. The code is overly complicated lacks clarity and does not delete the rows from the source.
The processing sequence follows the OP's method:
Identify the source spreadsheet and sourcesheet.
Create a variable for to identify "yesterday's date".
Establish whether a sheet by that name exists in the source spreadsheet; if not, then create and rename a sheet.
Assign the new sheet as the targetsheet
get the number of rows of data. Normally getLastRow could be used, but this identified empty 'phantom' rows.
get all the data from the Sourcesheet prior to the loop
loop through the source data
build a header row and then evaluate each timestamp against the variable for "yesterday's date"
where the row date is a match, push values for each column onto a "rowarray", push the "row" array onto a "targetarray", clear the "rowarray" for the next row; increment a row counter.
when finished with the loop, update the target sheet with the "targetarray".
delete the rows from the sourcesheet.
function so_55537340_03() {
// NOTE: code assumes that data is always in contiguous rows in date order
// setup external sheet
var ss = SpreadsheetApp.openById("<insert spreadsheet ID>");
// Logger.log("DEBUG: ss = "+ss.getName());//DEBUG
var sourceSheet = ss.getSheetByName("Sheet1");
//Logger.log("DEBUG: source sheet: "+sourceSheet.getName());//DEBUG
//get dateString
var today = new Date();
var yesterday = new Date();
yesterday.setDate(today.getDate() - 1);
//Logger.log("DEBUG: yesterday: "+yesterday);//DEBUG
var dateString = Utilities.formatDate(yesterday, 'EDT', 'yyyy-MM-dd');
// Logger.log("DEBUG: datestring: "+dateString);//DEBUG
// create new sheet with yesterdays date
// get the sheet, returns null if not exist
var yourNewSheet = ss.getSheetByName(dateString);
// Logger.log("DEBUG: yournewsheet: "+yourNewSheet);//DEBUG
// test if sheet exists
if (yourNewSheet != null) {
// Logger.log("DEBUG: the spreadsheet is NOT null");//DEBUG
// the sheet exists, so what??
//ss.deleteSheet(yourNewSheet);
} else {
// Logger.log("DEBUG: the spreadsheet is null");//DEBUG
// the sheet doesn't exist, so create and rename the sheet
yourNewSheet = ss.insertSheet();
yourNewSheet.setName(dateString);
}
// assign a variable to the target sheet
var targetSheet = ss.getSheetByName(dateString);
// Logger.log("DEBUG: target sheet: "+targetSheet.getName());// DEBUG
// get the number of rows of data in column A for a loop (getLastrow was unreliable)
var Avals = sourceSheet.getRange("A1:A").getValues();
var Alast = Avals.filter(String).length;
// Logger.log("DEBUG: source last row: "+Alast);//DEBUG
// define the source range and get the data
var sourceRange = sourceSheet.getDataRange();
var sourceData = sourceRange.getValues();
// Logger.log("DEBUG: sourcerange = "+sourceRange.getA1Notation()+", and number of rows of data: "+Alast);//DEBUG
// establish arrays for pasting row results and total results to target sheet
var targetData = [];
var rowData = [];
// create variable to count rows to delete
var rowcounter = 0;
// loop through the source data
for (var i = 0; i < Alast; i++) {
// clear rowData before each new row
rowData = [];
// test for row 1/headers
if (i == 0) {
// build the header row in the target
rowData.push(sourceData[i][0]);
rowData.push(sourceData[i][1]);
rowData.push(sourceData[i][2]);
rowData.push(sourceData[i][3]);
rowData.push(sourceData[i][4]);
rowData.push(sourceData[i][5]);
rowData.push(sourceData[i][6]);
rowData.push(sourceData[i][7]);
// push row data on targetData
targetData.push(rowData);
}
// process for rows other than header
if (i > 0) {
//Logger.log("DEBUG: rowdate: "+sourceData[i][0]);//DEBUG
// get the substring
var sourcecelldate = sourceData[i][0].substring(0, 10);
//Logger.log("DEBUG: data substring: "+sourcecelldate);//DEBUG
// if the data substring equals the datestring for yesterday
if (sourcecelldate == dateString) {
//Logger.log("DEBUG: dates align, do something");//DEBUG
// build the data for the row
rowData.push(sourceData[i][0]);
rowData.push(sourceData[i][1]);
rowData.push(sourceData[i][2]);
rowData.push(sourceData[i][3]);
rowData.push(sourceData[i][4]);
rowData.push(sourceData[i][5]);
rowData.push(sourceData[i][6]);
rowData.push(sourceData[i][7]);
// push row data on targetData
targetData.push(rowData);
rowcounter++
Logger.log("Row counter: " + rowcounter);
} else {
//Logger.log("DEBUG: dates DO NOT align, do nothing");//DEBUG
}
}
} // end of loop
// get number of rows of targetdata
//Logger.log("DEBUG: length of targetdata"+targetData.length);//DEBUG
// define the data range for the target sheet
var targetrows = targetSheet.getRange(1, 1, targetData.length, 8);
// paste the data onto the targetsheet
targetrows.setValues(targetData);
// delete the old rows from the source sheet
//Logger.log("DEBUG: number of rows to delete = "+rowcounter);
sourceSheet.deleteRows(2, rowcounter);
}
I have left "Logger" statements in the following code to troubleshoot values at various stages in the process.