Copy/Append dynamic range of rows to another sheet - google-apps-script

I have asked if there is a way to loop an app script to copy header base information to multiple rows based on the amount of line items entered by a user:
Solution is using arrayformulas with an intermediary sheet "OrderKey" that can eventually be hidden.
I have updated a working solution thanks to Aurielle Perlmann (see below).
The SHEET system is very close to completion
See my example Sheet here:https://docs.google.com/spreadsheets/d/151h1XjB98NOBnO0otNaql3ASjK84CccZZ399dX4BMBM/edit#gid=0
function onOpen() {
var ui = SpreadsheetApp.getUi();
// Or DocumentApp or FormApp.
ui.createMenu('Custom Menu')
// creates a menu item "Submit Sales Order"
.addItem('Submit Sales Order', 'menuItem1')
.addToUi();
}
function menuItem1() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var salesheet = ss.getSheetByName("salesOrder");
var source = ss.getSheetByName(" OrderKey");
// sets the 'OrderKey' Sheet as source
var target = ss.getSheetByName("Orders");
// sets 'Orders' sheet as the target for copying data to.
var sourceData = source.getSheetValues(2,1,source.getLastRow(),16);
// sets range to gather source 'OrderKey' data by finding last row, and Line 2 to Column 16
target.getRange(target.getLastRow()+1, 1, sourceData.length,16).setValues(sourceData);
// finds last row of target 'Orders' and writes to +1 row past last row up to column 16 using setValues of sourceData
// Following simply clears Sales Order Sheet so new data can be entered
salesheet.getRange('B4:B5').clearContent();
salesheet.getRange('B8').clearContent();
salesheet.getRange('G6:G8').clearContent();
salesheet.getRange('F10:G10').clearContent();
salesheet.getRange('A13:C76').clearContent();
salesheet.getRange('J13:J76').clearContent();
// Following gets seed number from cell I1, and increases value by +1 so next Sales Order ID is incremented by 1
var cell = salesheet.getRange("I1");
var cellValue = cell.getValue();
cell.setValue(cellValue + 1);
var lastID = salesheet.getRange("F1");
var nextID = salesheet.getRange("G1");
var lastIDValue = lastID.getValue();
nextID.setValue(lastIDValue + 1);
}
UPDATED.

I went ahead and added the formulas on your sheet for you, this formula only needs to be added to the very first line after the header and the rest will fill in automatically
the two formulas used are:
=arrayformula(if(istext(K2:K),salesOrder!B4,))
and
=ARRAYFORMULA(if(istext(salesOrder!A13:A),salesOrder!A13:A,))
the cell references change depending on which fields you are trying to import.
after doing that sheet - i added a script which I then attached to a button called "submit" which gets the last rows and appends the value onto a separate sheet you can use for archiving:
function testing(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet1 = ss.getSheetByName(" OrderKey");
var sheet2 = ss.getSheetByName("testing - aurielle");
var sourceData = sheet1.getSheetValues(2,1,sheet1.getLastRow(),14);
sheet2.getRange(sheet2.getLastRow()+1, 1, sourceData.length,14).setValues(sourceData);
}

Related

GoogleSheets CopyPaste Column's formatting and formula to newly created Column

I have a Google Sheet with a sheet [Summary] with a button that when clicked, performs a macro to add a new column to a different sheet, [May 2022], and then, take a value from the button's original sheet, add that value to the _1 cell of that new column, and also paste in some values from the page's original column. It also adds a new row in the base sheet, to log the particular name in place, to create a growing list of collected names & matching dates. My code has a prompt to confirm this with a Yes/No.
I'd like to have this macro copy the entirety of the column's range in the new sheet's (C2:C) range, into the newly added column under the _1 cell, so that both formatting, conditional formatting, and formulas for each cell, are added in the (_2:_) range. The process should be that it creates a new column, adds the value of the name, and underneath, adds all of the formatting and formulae from (C2:C), all into the new columns as they're generated. I do not want values copy/pasted; it needs the formula pasted.
function Add_Patient_Button(){
// Display a dialog box with a message and "Yes" and "No" buttons. The user
//can also close the
// dialog by clicking the close button in its title bar.
var ui = SpreadsheetApp.getUi();
var response = ui.alert(
'Please confirm that you are about to add a new patient.',
ui.ButtonSet.YES_NO);
// Process the user's response.
if (response == ui.Button.YES) {
Add_New_Patient_Column();
}
if (response == ui.Button.NO) {
}
else {
}
}
function Add_New_Patient_Column()
{
var spreadsheet = SpreadsheetApp.getActive();
spreadsheet.getRange('9:9').activate();
spreadsheet.getActiveSheet().insertRowsBefore(spreadsheet.getActiveRange().getRow(), 1);
spreadsheet.getActiveRange().offset(0, 0, 1, spreadsheet.getActiveRange().getNumColumns()).activate();
spreadsheet.getRange('A9').activate();
spreadsheet.getRange('E3:F3').copyTo(spreadsheet.getActiveRange(), SpreadsheetApp.CopyPasteType.PASTE_VALUES, false);
spreadsheet.getRange('C9').activate();
spreadsheet.getCurrentCell().setValue('0');
var ss = SpreadsheetApp.getActive();
var date = ss.getSheetByName('Summary').getRange('E3').getDisplayValue();
var namesSheet = ss.getSheetByName('May 2022')
var names = namesSheet.getRange('C2:C').getValues().filter(function (r) {return r[0]})
names.unshift([date])
namesSheet.getRange(1, namesSheet.getLastColumn() +1, names.length, 1).setValues(names)
var spreadsheet = SpreadsheetApp.getActive();
}
Here is a copy of my sheet. https://docs.google.com/spreadsheets/d/1GbdoAY24Y1AMuDsXbRdUnDx4RuPzSN4krl-qcaCwC5o/edit?usp=sharing
What do I need to change in my macro to make this happen?
Not sure if I understand the task a whole. I'd propose to clone the last existed column on the sheet 'May 2022' and to change its first cell according the name in cell E3 of the sheet 'Summary'.
It can be done if the function Add_New_Patient_Column() will look as follows:
function Add_New_Patient_Column() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('May 2022');
var last_col = sheet.getLastColumn();
var last_row = sheet.getLastRow();
// add a new empty column
sheet.insertColumnAfter(last_col);
// copy all cells from the last column into the new column
sheet.getRange(1,last_col, last_row, 1).copyTo(sheet.getRange(1,last_col+1));
// change the header of the new column
var src_sheet = ss.getSheetByName('Summary');
var [name, date] = src_sheet.getRange('E3:F3').getValues().flat();
sheet.getRange(1,last_col+1).setValue(name);
// append a new row at the 'Summary' sheet
var new_row = [name, date, 0, '???'];
src_sheet.appendRow(new_row);
}
Update
If you want to add the new row above the row 9 you have to replace the line:
src_sheet.appendRow(new_row);
with:
src_sheet.insertRowBefore(9);
src_sheet.getRange('a9:d9').setValues([new_row]);

How do I expand or reduce formulas depending on cell content

I have a table consisting of several sheets. The table should be processed daily by several users at the same time. The main editing is done in Sheet1. Sheet2 consists only of a query and formulas.
Example Sheet 1:
Sheet 1 consists of 2500 rows and 17 columns.
Starting with row 12 (row 11 is the header row. The rows above contain formula references), columns A, B are to be filled in by the user. The columns C-H contain various formulas (e.g. GoogleFinance queries). The columns G-M must be filled in again by the user. Finally, there are formulas in the columns N-Q again.
Goal: Automatic expansion / deletion of formulas in columns C-H and N-Q, depending on whether content has been added / removed in columns A and B. The deletion process should be performed line by line to also delete remaining user content in the columns G-M.
The sheet schould also be sortable.
Example Sheet 2:
Sheet 2 consists of 1500 rows and 11 columns.
Starting with line 9 (line 9 is also the header line. The rows above contain formula references), the columns A-I are filled with results of a query (which is located in cell A9). The query obtains certain contents from Sheet 1 and is dynamic. The columns J-K contain formulas.
Goal: Automatically extend / delete the formulas in the columns J-K, depending on whether content was added / removed in the columns A-I (by the query).
For extending, deleting and sorting I used the code examples below.
Unfortunately, the codes shown do not meet the requirements described in the constellation. I would therefore be very grateful for a better solution.
function fillDownFormulaTD(){
Sheet = "sheet1";
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(Sheet);
var lr = ss.getLastRow();
var Rng1 = ss.getRange(1, 2, lr-1);
ss.getRange("").setFormula('');
ss.getRange("").copyTo(Rng1);
}
function removeEmptyRows(){
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Ticker-Datenbank');
var maxRows = sheet.getMaxRows();
var lastRow = sheet.getLastRow();
sheet.deleteRows(lastRow+1, maxRows-lastRow-20);
}
function Sortieren(){
var spreadsheet = SpreadsheetApp.getActive();
spreadsheet.getRange('A11:Q11')
var currentCell = spreadsheet.getCurrentCell();
spreadsheet.getSelection().getNextDataRange(SpreadsheetApp.Direction.DOWN).activate();
currentCell.activateAsCurrentCell();
spreadsheet.getActiveRange().sort({column: 3, ascending: true});
spreadsheet.getRange('A11').activate();
}
This is a quick solution for Sheet2 (an answer for Sheet#1 is in the works).
For Sheet2, rather than manually populate every row of Columns J and I with formula, use an arrayformula that will "automatically" expand/contract according to the rows with data.
Delete ALL the formula (cells that are blank and non-blank) for every row>9 in Column J and Column I.
In Cell J10, insert this formula
=arrayformula(IF($C10:$C="";"";HYPERLINK($J$1&$A10:$A&$J$2&$B10:$B;$J$3)))
In cell I10, insert this formula
=arrayformula(IFERROR(VLOOKUP($C10:$C;'Geprüfte Ticker'!$C:$P;14;FALSE);""))
This code addresses the Sheet1 scenario.
The code is technically accurate and will work but there are two other issues the OP may wish to consider.
The spreadsheet is "processed daily by several users at the same time". There is a risk that a blank cell that detected in Column A or Column B, and which would be an indicator to delete the while row, is only temporary - the user may intend to correct spelling or enter new data. An "ideal" basis for deleting a row would be to detect a blank cell in both Column A **and ** Column B, but this is not always possible.
Even with only test data, recalculation time for the spreadsheet is long - #around 10+ seconds - the time for the live spreadsheet is not known. It's not known what effect this code will have on recalculation.
function onEdit(e) {
// setup spreadsheet and sheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
var tsName = "onedittest";
var targetsheet = ss.getSheetByName(tsName);
// list event data
// Logger.log(JSON.stringify(e)); //DEBUG
//get the formukla ranges and formulas
// set 1 = Column C-H
// set 2 = Column N-P
// Column Q = arrayformula - leave as is
var rR1 = targetsheet.getRange("C12:H12");
var fR1 = rR1.getFormulasR1C1();
var rR2 = targetsheet.getRange("N12:P12");
var fR2 = rR2.getFormulasR1C1();
// collect event data
var editR = e.range.getRow();
var editRS = e.range.rowStart;
var editRE = e.range.rowEnd;
var editC = e.range.getColumn();
var editCS = e.range.columnStart;
var editCE = e.range.columnEnd;
//Logger.log("DEBUG: edited rowstart = "+editRS+", rowend = "+editRE+", columnstart = "+editCS+", columnend = "+editCE);
//Logger.log("DEBUG: edited row = "+editR+", edited column = "+editC);
var editedsheet = e.range.getSheet().getSheetName();
// Logger.log("DEBUG: sheet name = "+editedsheet);
if (editedsheet === tsName && editC>= 1 && editC<= 2 && editR>12) {
// this range/cell is a trigger
// Logger.log("DEBUG: match");
var editCell = targetsheet.getRange(editRS, editCS);
// Logger.log("DEBUG: the edited cell is "+editCell.getA1Notation());
//Logger.log("DEBUG: the old value = "+e.oldValue+", and the new value = "+e.value);
//now split Logic#1 and Logic#2
// Logic#1 = AND there is data in the edited cell (that is, the edit did not make the cell blank),
// then copy (or recopy) the formulas from row 12 Columns C-H and N-Q onto the edited row.
if (editCell.length !=0){
// copy formulas
// Logger.log("DEBUG: copy the formulas");
var formulaset1 = targetsheet.getRange(editRS,3,1,6);
formulaset1.setFormulasR1C1(fR1);
// Logger.log("DEBUG: set formulas for range 1")
var formulaset2 = targetsheet.getRange(editRS,14,1,3);
formulaset2.setFormulasR1C1(fR2);
// Logger.log("DEBUG: set formulas for range 2")
// Sort the data
// get the number of rows of data
var lastheaderRow = 11;
var Avals = ss.getRange("A12:A").getValues();
var Alast = Avals.filter(String).length;
//Logger.log("DEBUG: Number of rows of data = "+Alast+", so last row = "+(Alast+lastheaderRow));
// define the sort range
var sortRange = targetsheet.getRange(lastheaderRow, 1, Alast+1, 17);
// Logger.log("DEBUG: the range = "+range.getA1Notation());
// sort by Column C, ascending
sortRange.sort({column: 3, ascending: true});
}
else{
// Logic#2 - delete the row
// Logger.log("DEBUG: delete the row");
targetsheet.deleteRow(editRS);
}
}
else
{
//not the right sheet, not the column, not the right row
// Logger.log("DEBUG: do nothing - not matched");
}
}

Sheets script: button activated, copies data from a cell to sheet, then clears range of data

I'm pretty good using formulas with google sheets to do a substantial amount of work. Right now, though, I'm finding that I need a system that is "button operated" and scripting is something I am just beginning to dabble in.
I'm trying to create a script that will read the contents of a cell (D2) on sheet called 'Tester Page' and duplicate cell D2 onto a separate sheet in the same document. Once copied, I need the sheet to clear range B5:B16 and D5:d16 on the 'Tester Page.' The data in those two ranges are selected from dropdown menus using data validation.
The final kicker is that it needs to be button activated.
So, in my initial attempts, I'm trying right now to copy the data from cell D2 and just append it to the bottom of the page. It doesn't work. Any help on this step or other steps would be huge. Here's what I have so far:
function addProduct() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Tester Page');
var value = sheet.getRange(2,4)
sheet.appendRow(value);
}
Edit
So I got the script up and running (including using a clickable button). Everything went well until we decided to make some changes to how the page would work. Rather than copying the contents of 1 cell onto the second page (called 'Transactions'), we decided we want to take an entire row of data (row 22). I've added my script as it is right now, which will copy a single cell 20 times...not a string of 20 columns. I really would like the entire row, not just 20 columns worth, but can't figure it out.
function mainThing() {
//get content
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Tester Page');
var cell = sheet.getRange(22,1).getValues();
//copy content
var destination = ss.getSheetByName('Transactions');//whatever page
var destCell = destination.getRange(2, 1, 1, 20)
destCell.setValue(cell);
//Add clean row
var add = ss.getSheetByName('Transactions')
add.insertRowBefore(2)
Thanks again for the help. You guys are awesome.
Based on changes, it'd be something like this ...
function mainThing() {
//get content
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Tester Page');
var lastCol = ss.getLastColumn();//get last column in the ss
var cell = sheet.getRange(2, 1, 1, lastCol).getValues(); //get contents of row
//copy content
var destination = ss.getSheetByName('Transactions');
var destRange = destination.getRange(2, 1, 1, lastCol);
destination.insertRowBefore(2);
destRange.setValues(cell);
//clear content
clearThis("B5:B16");
clearThis("D5:D16");
}
//content clearer
function clearThis(range){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Tester Page');
var theRange = sheet.getRange(range);
theRange.clearContent();
}
//straight from https://developers.google.com/apps-script/guides/menus
function onOpen() {
var ui = SpreadsheetApp.getUi();
// Or DocumentApp or FormApp.
ui.createMenu('Do Magic')
.addItem('Presto', 'mainThing')
.addToUi();
}
I got everything working the way I wanted. It duplicates a row of cells to a new sheet, inserts a blank row at the top of the new sheet (so my duplicated data doesn't get covered over), and clears out the data in the ranges of cells I need cleaned. I also have it run when the user clicks a button on screen. Overall, I'm happy that it works.
Here it is in all of its glory:
function mainThing() {
//get content
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Store Page');
var cell = sheet.getRange("A22:Z22").getValues();
//copy content
var destination = ss.getSheetByName('Transactions');//whatever page
var destCell = destination.getRange("A3:Z3");
destCell.setValues(cell);
//Add clean row
var add = ss.getSheetByName('Transactions')
add.insertRowBefore(3)
//clear content
clearThis("C5:C16");
clearThis("E5:E16");
clearThis("C2");
}
//content clearer
function clearThis(range){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Store Page');
var theRange = sheet.getRange("C5:C16");
var theRange2 = sheet.getRange("E5:E16");
var theRangeName = sheet.getRange(2, 3);
theRange.clearContent();
theRange2.clearContent();
theRangeName.clearContent();
}

What do I change in the following code to identify already existing sheet and add data to it corresponding to data added in master sheet?

[1]I have various sheets with the name of my clients in a spreadsheet. And I have one sheet where payments done by them are added using form. Now, I want this code to insert payment details into the clients' respective sheets by matching the sheet name to the name of client in payment details sheet.
Sample worksheet for your reference
var ss=SpreadsheetApp.getActiveSpreadsheet();
var master = ss.getSheetByName('Payment Details');
var sheets = ss.getSheets(); // number of sheets
var colWidth = master.getLastColumn();
function onOpen() {
var menuEntries = [ {name: "Copy selected Rows to sheets", functionName: "copyRowsOnConditionV2"},];
ss.addMenu("Copy functions",menuEntries);// custom menu
}
function copyRowsOnConditionV2() {
var sheetNames = [];// array of existing sheet names
var sheets = ss.getSheets();// number of sheets
for(s=0;s<sheets.length;++s){sheetNames.push(sheets[s].getName())};
ss.getActiveSelection().setBackground('#ffffbb');
var selectedfirstRow = ss.getActiveSelection().getRowIndex();
var selectedHeigth = ss.getActiveSelection().getHeight()
var selectedFullRange = master.getRange(selectedfirstRow,1,selectedHeigth,colWidth);
var data = selectedFullRange.getValues();
for(n=0;n<data.length;++n){
if(data[n][2].length<60){
// Check if there is a sheet with this name already (uses names without the space)
if (sheetNames.indexOf(data[n][2])) {
ss.insertSheet(data[n][2]) // Insert a new sheet WITH THE SPACE
sheetNames.push(data[n][2]) // Add new sheet name to array of sheet names
}
var dest = ss.getSheetByName(data[n][2]);//find the destination sheet
Logger.log(data[n][2])
// Find the last column with data
var lastcol = dest.getLastColumn();
// Set the range to the next col over
dest.getRange(31, (lastcol +1)).setValue(data[n][3]);
dest.getRange(32, (lastcol +1)).setValue(data[n][4]);
var destRange = dest.getRange('h33').setFormula("=h30-h31-i31-j31-k31-l31-m31-n31");
}
}
}
[1]: https://i.stack.imgur.com/yGmwk.png [Getting this error when running the code] I want it to add the data to the same sheet instead of creating new one.
Was not completely sure what you wanted with the spaces - or didnt want. Commented out are the lines to use with spaces. Also for the d30,d31,d32 empty column question, you can use getLastColumn() to find the last column with data and add a +1 for the range's column position to move over to the next empty column.
function copyRowsOnConditionV2() {
var sheetNames = [];// array of existing sheet names
var sheets = ss.getSheets();// number of sheets
for(s=0;s<sheets.length;++s){sheetNames.push(sheets[s].getName())};
ss.getActiveSelection().setBackground('#ffffbb');
...
var data = selectedFullRange.getValues();
for(n=0;n<data.length;++n){
if(data[n][2].length<60){
// Check if there is a sheet with this name already (uses names without the space)
if (sheetNames.indexOf(data[n][2]) == -1) {
ss.insertSheet(data[n][2]); // Insert a new sheet WITH THE SPACE
sheetNames.push(data[n][2]); // Add new sheet name to array of sheet names
// Apply pending Spreadsheet changes
SpreadsheetApp.flush();
}
var dest = ss.getSheetByName(data[n][2]);//find the destination sheet
// Find the last column with data
var lastcol = dest.getLastColumn();
// Set the range to the next col over
dest.getRange(30, (lastcol +1)).setValue(data[n][3]);
dest.getRange(31, (lastcol +1)).setValue(data[n][4]);
dest.getRange(32, (lastcol +1)).setFormula("=d29-d30-e30-f30");
...
}
}
}
edit Updated code to reflect that spaces were wanted

Copy a row to new sheet based on value in a cell

I'm trying to create a Google Script that copies rows from one Google Sheet into different sheets based on the value of a cell.
The cell value are states in the United States. The master spreadsheet has data from a registration form that is being imported into it all the time. When a new registration happens (and the data is imported into the master sheet), I'd like the script to run and copy that row of data into the appropriate state sheet.
Here's where I'm at:
Get master sheet
Find the last row
Get the value of the cell in the column "state"
Copy that row into one of 50 different sheets depending on what state it is.
Run the script every time the master sheet is updated (via an API).
Any help would be appreciated. I'm definitely a newbie when it comes to scripting and this is just hurting my head.
Here's the code I have so far:
function myFunction() {
// Get Source Spreadsheet
var source = SpreadsheetApp.getActiveSpreadsheet();
// Get Source Sheet from Spreadsheet
var source_sheet = source.getActiveSheet();
// Get Active Range from Sheet
var lastRow = sheet.getLastRow();
// Get the Value of the State Cell
var cellValue = Range.getCell(lastrow,3);
// Copy Last Row to Appropriate State Sheet
if ( cellValue == 'Alaska') {
var target = SpreadsheetApp.openById("");
var target_sheet = target.getSheetByName("Sheet1");
target_sheet.appendRow(lastRow);
}
}
With this code:
// Get Active Range from Sheet
var lastRow = sheet.getLastRow();
The getLastRow() method returns an integer. Then further down in your code, you are using the lastRow variable as the data for appendrow() which won't work;
target_sheet.appendRow(lastRow);
The code should probably be something like this:
var target = SpreadsheetApp.openById("");
var target_sheet = target.getSheetByName("Sheet1");
var lastRowOfData = source_sheet
.getRange(source_sheet.getLastRow(), 1, 1, source_sheet.getLastColumn())
.getValues(); //Returns a two dimensional array
var oneD_Array = lastRowOfData.join().split(","); //Creates a one D array
target_sheet.appendRow(oneD_Array);
The appendRow() method takes a one dimensional array. The getValues() method returns a 2 Dimensional array, so it must be converted. Because you are only getting one row of data, it's easy to convert. The outer array only has one inner array. The one inner array has all the cell values of the row.
Here's the answer for what I came up with for the code:
function myFunction() {
// Get Source Spreadsheet
var source = SpreadsheetApp.getActiveSpreadsheet();
// Get Source Sheet from Spreadsheet
var source_sheet = source.getActiveSheet();
// Get Last Row
var lastRow = source_sheet.getLastRow();
// Get Last Column
var lastColumn = source_sheet.getLastColumn();
// Get Last Row of Data
var lastRowOfData = source_sheet.getRange(lastRow, 1, 1, lastColumn).getValues();
// Creates a one dimensional array
var oneD_array = lastRowOfData.join().split(",");
// Get the Value of the State Cell
var cellValue = source_sheet.getRange(2,7).getValue();
// Copy Last Row to Appropriate State Sheet
if ( cellValue == "New York" ) {
var target = SpreadsheetApp.openById("");
var target_sheet = target.getSheetByName("Sheet1");
target_sheet.appendRow(oneD_array);
}
}