Google Sheets Script Missing Something - javascript

I'm creating a small database inside a sheet, I need the script to copy data to another sheet tab and I'm getting an empty error.
I'm not expert on javascript so what code I'm missing here?
Basically, when you press a button you get a text modal with an input so the person writes the name and then the script gets all the Rows with a TRUE (checkbox) and copies everything to another sheet with a header saying the data and time with the name of the person. If returns nulled with everything FALSE wont copy and shows a text modal saying that there's no task done today.
Thanks in advance
function moveValuesOnly() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var source = ss.getRange ("RESUMO!A4:J99");
var destSheet = ss.getSheetByName("LOG Resumo");
var row = destSheet.getLastRow()+2; //the starting column of the range
var column = 1; //the starting row of the range
var numRows = 97; //the number of rows to return
var numColumns = 10; //the number of columns to return
var destRange = destSheet.getRange(row, column, numRows, numColumns);
var input_text = Browser.inputBox("Encarregado de Turno","Escreve seu nome:", Browser.Buttons.OK);
var now = new Date();
var active = ss.getSheetByName("RESUMO");
var condition = active.getRange('RESUMO!J4:J99').getValue();
var valueToWatch = "TRUE";
if (condition == valueToWatch) {
destSheet.getRange(row-1,1,1,10).mergeAcross().setBackgroundRGB(224, 102, 102).setFontColor("white");
destSheet.getRange(row-1,1,1).setValue(now + " ~~ ENCARREGADO DE FECHAR TURNO: " + input_text).activate();
source.copyTo(destRange, {contentsOnly: true}).setFontColor("black");
} else {
Browser.msgBox("Erro","Não exite tarefas completas hoje", Browser.Buttons.OK);
}
}

There are following issue with your code:
If you have TRUE and FALSE values as cell contents, Spreadsheets will automatically identify them as booleans, rather than strings. Thus, also valueToWatch needs to be a boolean rather than a string: var valueToWatch = true;
If you want to verify the "TRUE" condition for each row - you need to do it in a loop. Consequently, you code needs to be modified a bit. One way to do so is to push all rows where the cell content in column J is TRUE into an array and then pass the contents of this array to a destination range with the same dimension as the array. This is what the resulting code would look like:
function moveValuesOnly() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var source = ss.getRange ("RESUMO!A4:J99");
var destSheet = ss.getSheetByName("LOG Resumo");
var row = destSheet.getLastRow()+2; //the starting column of the range
var column = 1; //the starting row of the range
var input_text = Browser.inputBox("Encarregado de Turno","Escreve seu nome:", Browser.Buttons.OK);
var now = new Date();
var active = ss.getSheetByName("RESUMO");
var condition = active.getRange('RESUMO!J4:J99').getValues();
destSheet.getRange(row-1,1,1,10).mergeAcross().setBackgroundRGB(224, 102, 102).setFontColor("white");
destSheet.getRange(row-1,1,1).setValue(now + " ~~ ENCARREGADO DE FECHAR TURNO: " + input_text).activate();
var valueToWatch = true;
var values=source.getValues();
var array=[];
for(var i=0;i<condition.length;i++){
if (condition[i][0] == valueToWatch) {
array.push( values[i]);
}
}
if(!array) {
Browser.msgBox("Erro","Não exite tarefas completas hoje", Browser.Buttons.OK);
}
else {
var numRows = array.length; //the number of rows to return
var numColumns = array[0].length; //the number of columns to return
var destRange = destSheet.getRange(row, column,numRows , numColumns);
destRange.setValues(array).setFontColor("black");
}
}

Related

GAS Function not setting value as intended in sheet

This is the Google Sheet, it can be copied: https://docs.google.com/spreadsheets/d/1ffIRGiGkiy5WFzSAvWNOY_3cqNXgTAOtO6o8vxS-BFU/edit?usp=sharing
The Function 'AddNewMembers' does not function, even if "isAdded == "No" it will not setValue(recruit_id)
function AddNewMembers(event){
event = {range: SpreadsheetApp.getActiveRange()}
CheckHandleSteamIDNotation(event)
SpreadsheetApp.flush();
var ss = SpreadsheetApp.getActiveSpreadsheet();
var recruitment_log = ss.getSheetByName('RL1');
var main_roster = ss.getSheetByName('Main Roster');
var isAdded = recruitment_log.getRange('R3').getValue();
if(isAdded == "No") {
var recruit_id = "'" + recruitment_log.getRange('J3').getValue();
main_roster.getRange('I100').setValue(recruit_id);
}
}
function CheckHandleSteamIDNotation(event)
{
let formSheet = SpreadsheetApp.getActiveSheet();
let header = formSheet.getRange(1,1,1,formSheet.getMaxColumns()).getValues();
let formRange = formSheet.getRange(formSheet.getLastRow(), 1, 1, formSheet.getMaxColumns());
let formValues = formRange.getValues();
for(let i = 0; i < header[0].length; i++)
{
if(header[0][i].includes("SteamID"))
{
formValues[0][i] = "'" + formValues[0][i];
}
}
formRange.setValues(formValues);
}
Since the provided script above contains var isAdded = recruitment_log.getRange('R3').getValue(); the value of R3 is currently set to "Yes" that is why the condition for the script below is not running.
if(isAdded == "No") {
var recruit_id = "'" + recruitment_log.getRange('J3').getValue();
main_roster.getRange('I100').setValue(recruit_id);
}
Try this modification:
function AddNewMembers(event) {
event = { range: SpreadsheetApp.getActiveRange() }
CheckHandleSteamIDNotation(event)
SpreadsheetApp.flush();
var ss = SpreadsheetApp.getActiveSpreadsheet();
var recruitment_log = ss.getSheetByName('RL1');
var main_roster = ss.getSheetByName('Main Roster');
//Gets all the data values on recruitment_log
var isAdded = recruitment_log.getRange(3, 1, recruitment_log.getLastRow(), recruitment_log.getLastColumn()).getValues();
//Gets the last row starting I17
var lastrow = main_roster.getRange(17, 9, main_roster.getLastRow() , 1).getValues().filter((x => x > 1)).length
//Sets the value on the last blank row
isAdded.map(x => x[17].toString().toLocaleLowerCase() == "no" ? "'" + main_roster.getRange(17 + lastrow,9).setValue(x[9]) : x)
}
I made modifications on your isAdded variable to the following to get the entire range of data on RL1 sheet.
var isAdded = recruitment_log.getRange(3, 1, recruitment_log.getLastRow(), recruitment_log.getLastColumn()).getValues();
This part of script was only used to get the current length of data for the New Operatives. Using .filter() method to filter out empty array elements, since getValues() gets blank cells if there is formatting applied on the spreadsheet.
var lastrow = main_roster.getRange(17, 9, main_roster.getLastRow() , 1).getValues().filter((x => x > 1)).length
Using ES6 .map() method to create a new array for the data that hasn't been added to the main roster sheet file.
isAdded.map(x => x[17].toString().toLocaleLowerCase() == "no" ? "'" + main_roster.getRange(17 + lastrow,9).setValue(x[9]) : x)
Screenshot:
Reference:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter#description

Log changes on Google Script (Google Spreadsheets)

I'm looking to create a spreadsheet where it logs data and the changes you make on it.
For example in sheet, I used a Google Script to do it but instead of only logging in a particular row when you change the data on Sheet1, it copies all the data you changed since the beginning on Sheet 2. I only want to log a particular row each time I make a change.
Here's my code
* Retrieves all the rows in the active spreadsheet that contain Yes
* in the Include column and copies them to the Report sheet.
*/
function myFunction() {
var sSheet = SpreadsheetApp.getActiveSpreadsheet();
var srcSheet = sSheet.getSheetByName("Sheet1");
var tarSheet = sSheet.getSheetByName("Sheet2");
var lastRow = srcSheet.getLastRow();
for (var i = 2; i <= lastRow; i++) {
var cell = srcSheet.getRange("B" + i);
var val = cell.getValue();
if (val == 'Yes') {
var srcRange = srcSheet.getRange("A" + i + ":D" + i);
var tarRow = tarSheet.getLastRow();
tarSheet.insertRowAfter(tarRow);
var tarRange = tarSheet.getRange("A" + (tarRow+1) + ":D" + (tarRow+1));
srcRange.copyTo(tarRange);
}
}
};
Can anyone please help?
Use onEdit(e)
function onEdit(e) {
var srcSheet = e.source.getActiveSheet();
if (srcSheet.getSheetName() !== 'Sheet1') { return; }
var row = e.range.rowStart;
var cols = 4;
var srcRange = srcSheet.getRange(row, 1, 1, cols);
var values = srcRange.getValues()[0];
if (values[1] == 'Yes') {
var tarSheet = e.source.getSheetByName('Sheet2');
tarSheet.appendRow(values);
}
}

Use an if statement in Javascript to determine if a cell in a google spreadsheet has contents

This code gets values from different locations in my sheet and writes the combined data to another sheet.
Problem is, if the row is blank, the code copies a blank row into the receiving sheet. I would like to first check cell "B8", and if it is blank, skip this function and run the next function.
If cell "B8" has data in it (anything, but it will usually be a date), then I would like to run the function to copy the code to the new sheet.
Here is my code:
function submitButtonClick() {
//Begining of entry8 transfer start.
var ss = SpreadsheetApp.getActive();
var sheet = ss.getActiveSheet();
Logger.log('sheet.getName(): ' + sheet.getName());
if (sheet.getName() !== "TimeCardEntry") {return;};
var targetSheet = ss.getSheetByName("TimeSheetRecord");
var arrayOfData = [];
var weekBegining = sheet.getRange(4, 11).getValue();
var employName = sheet.getRange(4, 4).getValue();
var entry8 = sheet.getRange(8,2,1,9);
var entry8Data = entry8.getValues()[0];//Get inner array of the rows data
Logger.log('entry8Data: ' + entry8Data);
//Add weekBegining and employName values to the array
entry8Data.splice(0, 0, weekBegining, employName);
//Push row data into another, outer array. setValuies() needs a 2D array.
arrayOfData.push(entry8Data);
Logger.log(arrayOfData);
var lastRow = targetSheet.getLastRow();
Logger.log('lastRow: ' + lastRow);
targetSheet.getRange(lastRow+1, 1, 1, entry8Data.length).setValues(arrayOfData);
sheet.getRange('B8:M8').clearContent();
//End of entry8 transfer.
}
You could add some code in between these line:
var arrayOfData = [];
var weekBegining = sheet.getRange(4, 11).getValue();
Change to:
var arrayOfData = [];
var B8_Value = sheet.getRange(8, 2).getValue();
if (B8_Value === "") {return;};//If cell value is empty stop code here
var weekBegining = sheet.getRange(4, 11).getValue();

Use RegEx to replace tags in document with column data from spreadsheet

I've been searching for the answer to this question but have so far been unable to piece together the answer. Please explain any answer you have in really simple terms as I'm fairly new to GAS and RegEx. I've got most of the syntax down but the execution of it in GAS is giving me a hard time.
Basically, I want to write a script that, when the spreadsheet is edited, checks which rows have yet to be merged. Then, on those rows, creates a copy of a template Google Doc and names the document based on the spreadsheet data. From there (this is the hard part), I need it to replace merge tags in the template with the data from the spreadsheet.
The tags in the templates I'll be using look like this: <<mergeTag>>
My idea was to match the whole tag, and replace it with data from the spreadsheet that exists in the column with the same name as what's inside the "<<>>". Ex: <<FooBar>> would be replaced with the data from the column named FooBar. It would obviously be from the current row that needs the merging.
After that, all that's left is to send an email (a few more row-specific personalization) with that document attached (sometimes as a PDF) with the body of the message coming from an HTML file elsewhere in the project.
This is the whole thing I have so far (notice the placeholders here and there that I can personalize for each spreadsheet I use this for):
function onEdit() {
//SPREADSHEET GLOBAL VARIABLES
var ss = SpreadsheetApp.getActiveSpreadsheet();
//get only the merge sheet
var sheet = ss.getSheetByName("Merge Data");
//get all values for later reference
var range = sheet.getActiveRange();
var values = range.getValues();
var lastRow = range.getLastRow();
var lastColumn = range.getLastColumn();
//get merge checker ranges
var urlColumn = range.getLastColumn();
var checkColumn = (urlColumn - 1);
var checkRow = range.getLastRow();
var checkRange = sheet.getRange(2, checkColumn, checkRow);
var check = checkRange.getBackgrounds();
//get template determination range (unique to each project)
var tempConditionRange = sheet.getRange(row, column);
var tempConditionCheck = tempConditionRange.getValues();
//set color variables for status cell
var red = "#FF0000";
var yellow = "#FFCC00";
var green = "#33CC33";
//////////////////////////////////////////////////////////
//DOC GLOBAL VARIABLES
var docTemplate1 = DriveApp.getFileById(id);
var docTemplate2 = DriveApp.getFileById(id);
var docTemplate3 = DriveApp.getFileById(id);
var folderDestination = DriveApp.getFolderById(id);
//////////////////////////////////////////////////////////
//EMAIL GLOBAL VARIABLES
var emailTag = ss.getRangeByName("Merge Data!EmailTag");
var personalizers = "";
var subject = "" + personalizers;
var emailBody = HtmlService.createHtmlOutputFromFile("Email Template");
//////////////////////////////////////////////////////////
// MERGE CODE
for (i = 0; i < check.length; i++) {
//for rows with data, check if they have already been merged
if (check[i] == green) {
continue;
} else {
var statusCell = sheet.getRange((i+2), checkColumn, 1, 1);
var urlCell = sheet.getRange((i+2), urlColumn, 1, 1);
var dataRow = sheet.getRange((i+2), 1, lastRow, (lastColumn - 2))
statusCell.setBackground(red);
//for rows with data, but not yet merged, perform the merge code
//////////////////////////////////////////////////////////
//DOC CREATION
//Determine which template to use
if (tempConditionCheck[i] == "") {
var docToUse = docTemplate1;
}
if (tempConditionCheck[i] == "") {
var docToUse = docTemplate2;
}
if (tempConditionCheck[i] == "") {
var docToUse = docTemplate3;
}
//Create a copy of the template
//Rename the document using data from specific columns, at specific rows
//Move the doc to the correct folder
var docName = "";
var docCopy = docToUse.makeCopy(docName, folderDestination);
var docId = docCopy.getId();
var docURL = docCopy.getUrl();
var docToSend = DriveApp.getFileById(docId);
var docBody = DocumentApp.openById(docId).getBody();
Here's where I need the help
//Locate the Merge Tags
//Match Merge Tags to the column headers of the same name
//Replace the Merge Tags with the data from the matched column, from the correct row
function tagReplace() {
var tagMatch = "/(<{2}(\w+)>{2})/g";
}
statusCell.setBackground(yellow);
urlCell.setValue(docURL);
The rest is just finishing up the process
//////////////////////////////////////////////////////////
//EMAIL CREATION
//Create an email using an HTML template
//Use Merge Tags to personalize email
//Attach the doc we created to the email
//Send email to recipients based on data in the sheet
MailApp.sendEmail(emailTag, subject, emailBody, {
name: "Person McPerson",
attachments: [docToSend], //[docToSend.getAs(MIME.PDF)],
html: emailBody,
});
//////////////////////////////////////////////////////////
//CHECK ROW UPDATE
statusCell.setBackground(green);
}
}
}
My sheets all have a frozen first row that acts as the header row. All my columns will be consistently named the exact same thing as the tags (minus the <<>>).
How do I match the tags to the data?
EDIT
```````````````````
The solution did not work as described when I inserted it into my code as follows:
function formMerge() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Merge Data");
var urlColumn = sheet.getMaxColumns();
var checkColumn = urlColumn - 1;
var lastRow = ss.getSheetByName("Form Responses").getLastRow();
var values = sheet.getDataRange().getValues();
var headers = values[0];
var urlRange = sheet.getRange(2, urlColumn, lastRow);
var checkRange = sheet.getRange(2, checkColumn, lastRow);
var check = checkRange.getBackgrounds();
var red = "#ff0404";
var yellow = "#ffec0a";
var green = "#3bec3b";
var docTemplate = DriveApp.getFileById(id);
var folderDestination = DriveApp.getFolderById(id);
// MERGE CODE
for (i = 0; i < check.length; i++) {
if (check[i] == green) {
continue;
} else {
var statusCell = sheet.getRange((i+2), checkColumn, 1, 1);
var urlCell = sheet.getRange((i+2), urlColumn, 1, 1);
var dataRow = sheet.getRange((i+2), 1, 1, (urlColumn - 2)).getValues();
var clientNameRange = sheet.getRange((i+2), 3);
var clientName = clientNameRange.getValue();
var dateRange = sheet.getRange((i+2), 2);
var datePreFormat = dateRange.getValue();
var timeZone = CalendarApp.getTimeZone();
var date = Utilities.formatDate(new Date(datePreFormat), timeZone, "MM/dd/yyyy");
statusCell.setBackground(red);
//EMAIL VARIABLES
var personalizers = clientName;
var subject = "Post Intake Report for " + personalizers;
var emailBody = "Please see the attached Google Doc for the Post Intake Report for " + clientName + ". The intake was performed on " + date + ".";
var emailTagRange = sheet.getRange((i+2), 24);
var emailTagValue = emailTagRange.getValue();
var emailTag = emailTagValue.split(", ");
//DOC CREATION
var docToUse = docTemplate;
var docName = "Post Intake Report - " + clientName + " [" + date + "]";
var docCopy = docToUse.makeCopy(docName, folderDestination);
var docId = docCopy.getId();
var docURL = docCopy.getUrl();
var docBody = DocumentApp.openById(docId).getBody().editAsText();
for (var j=0; j<headers.length; j++) {
var re = new RegExp("(<<"+headers[j]+">>)","g");
docBody.replaceText(re, dataRow[j]);
}
statusCell.setBackground(yellow);
urlCell.setValue(docURL);
//EMAIL CREATION
MailApp.sendEmail(emailTag, subject, emailBody, {
name: "Christopher Anderson",
attachments: [docCopy],
html: emailBody
});
statusCell.setBackground(green);
}
}
}
Build the RegExp for each tag on the fly, using the header values from your spreadsheet.
Use Body.replaceText() to perform the replacements.
var values = sheet.getDataRange().getValues();
var headers = values[0];
...
// Loop over all columns. Use header names to search for tags.
for (var col=0; col<headers.length; col++) {
// Build RegExp using column header
var re = new RegExp("(<{2}"+headers[col]+">{2})","g");
// Replace tags with data from this column in dataRow
body.replaceText(re, dataRow[col]);
}
This snippet will operate on a single row; the first couple of declarations should appear outside of your row loop. The column looping is then done after you've created and opened the new document, and obtained the body object.
It loops over all the columns in the spreadsheet, using the header names to find the tags you've defined, and replaces them with the corresponding cell contents for the current row.

Sheet Coming in as undefined

I have a spreadsheet with revenue and expense data. Here is a spreadsheet with what it looks like with data redacted: https://docs.google.com/spreadsheets/d/1xYXt_pvhpzCO7D61SoOwcmNNIGaFHkExwLHhl56c-NU/edit?usp=sharing
I want to create a script that validates the data in column C by forcing a user to enter a date. Once a date is entered, I want to return a new row beneath it. The code works in that it validates data and enters a new row, but when I enter a date into C96, the new row pushes the 'Date' cell, currently in C100, down to C101 and says the date is invalid. I set up my code so that it should be rerunning the script and not making C101 data validated. Additionally, I am getting errors with the 'getRange' method. It is saying that 'TypeError: Cannot call method "getRange" of undefined.'
Currently I have triggers for onEdit and onOpen for both dateValidation functions and onEdit triggers for the addRow functions.
For some reason, on my original file, the code still runs despite these errors. I have starred the location of the errors in my code below. I am very new to Google Scripts/ programming in general, so if anyone can help me out it would be greatly appreciated.
Here is my code currently:
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("General Ledger");
// Get row of first revenue
var firstRowRevLookup = 'Revenue';
var firstRowRange = sheet.getRange('B1:B');
var firstRowValues = firstRowRange.getValues();
var x = [];
for (var i = 0; i<firstRowValues.length; i++) {
if(firstRowValues[i] == firstRowRevLookup) {
x.push(i+3);
}
}
var firstRowRevenue = Number(x);
Logger.log(firstRowRevenue);
//Get row of last revenue
var lastRowRevLookup = 'End Revenues';
var lastRowRange = sheet.getRange('C1:C');
var lastRowValues = lastRowRange.getValues();
var y = [];
for (var i = 0; i < lastRowValues.length; i++) {
if(lastRowValues[i] == lastRowRevLookup) {
y.push(i);
}
}
var lastRowRevenue = Number(y);
Logger.log(lastRowRevenue)
//Get row of first expense
var firstRowExLookup = 'Expenses';
var z = [];
for(var i = 0; i<firstRowValues.length; i++) {
if(firstRowValues[i] == firstRowExLookup){
z.push(i+3);
}
}
var firstRowExpense = Number(z);
Logger.log(firstRowExpense);
//Get row of last expense
var lastRowExLookup = 'End Expenses';
var q = [];
for(var i = 0; i <lastRowValues.length; i++) {
if( lastRowValues[i] == lastRowExLookup) {
q.push(i);
}
}
var lastRowExpense = Number(q);
Logger.log(lastRowExpense);
dateValidationExpenses(sheet, firstRowExpense, lastRowExpense);
dateValidationRevenue(sheet, firstRowRevenue, lastRowRevenue);
//User must enter date into column C
function dateValidationExpenses(sheet, firstRowExpense, lastRowExpense) {
// Logger.log(sheet);
**var dateRange = sheet.getRange(firstRowExpense, 3, lastRowExpense-firstRowExpense+1);**
//Logger.log(dateRange);
var rule = SpreadsheetApp.newDataValidation()
.requireDate()
.setAllowInvalid(false)
.setHelpText('Please enter date using format "=date(year,month,day)"')
.build();
dateRange.setDataValidation(rule);
dateRange.setFontWeight('normal');
}
//User must enter date into column C
function dateValidationRevenue(sheet,firstRowRevenue,lastRowRevenue) {
**var dateRange = sheet.getRange(firstRowRevenue, 3, lastRowRevenue-firstRowRevenue+1);**
var rule = SpreadsheetApp.newDataValidation()
.requireDate()
.setAllowInvalid(false)
.setHelpText('Please enter a date using format "=date(year,month,day)"')
.build();
dateRange.setDataValidation(rule);
dateRange.setFontWeight('normal');
}
function addRowExpenses () {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('General Ledger');
var testCell = sheet.getRange(lastRowExpense,3).getValue();
//Logger.log(testCell);
if (testCell !== '') {
sheet.insertRows(lastRowExpense+1);
}
}
function addRowRevenue () {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('General Ledger');
var testCell = sheet.getRange(lastRowRevenue,3).getValue();
//Logger.log(testCell);
if (testCell !== '') {
sheet.insertRows(lastRowRevenue+1);
}
}
function editTrigger1() {
var sheet = SpreadsheetApp.getActive()
ScriptApp.newTrigger('addRowExpenses')
.forSpreadsheet(sheet)
.onEdit()
.create();
}
function editTrigger2() {
var sheet = SpreadsheetApp.getActive()
ScriptApp.newTrigger('addRowRevenue')
.forSpreadsheet(sheet)
.onEdit()
.create();
}
The variable sheet is declared outside of a function, which makes it a "global". You don't need to pass sheet
Currently:
dateValidationExpenses(sheet, firstRowExpense, lastRowExpense);
dateValidationRevenue(sheet, firstRowRevenue, lastRowRevenue);
Change to:
dateValidationExpenses(firstRowExpense, lastRowExpense);
dateValidationRevenue(firstRowRevenue, lastRowRevenue);
sheet is available to the dateValidationExpenses function without the need to pass it. That should solve the issue with the error you are getting on this line:
var dateRange = sheet.getRange(firstRowExpense, 3, lastRowExpense-firstRowExpense+1);

Categories