I am trying to run the function "onReportOrApprovalSubmit()" when I click on the message box that pops up when I open my google spreadsheet. The function runs perfectly fine when I run it in the script editor. However, when I try to run it using the message box that pops up when I first open the spreadsheet, the function stops running/gets stuck just before the line:
"var approvalsSpreadsheet = SpreadsheetApp.openById(APPROVALS_SPREADSHEET_ID);"
Does anyone have any idea why this is happening?
var APPROVALS_SPREADSHEET_ID = "1NC6mBPRXKCA4Blbn7C9lFt9YnTZioS3_vidbVuPvZos";
var APPROVAL_FORM_URL = "https://docs.google.com/a/londonhydro.com/forms/d/1BKuKdNwsUDXyLdqy18GAcQmE_SzS7Sq_OTxVNwyCH44/viewform";
var STATE_MANAGER_EMAIL = "MANAGER_EMAIL";
var STATE_APPROVED = "APPROVED";
var STATE_DENIED = "DENIED";
var COLUMN_STATE = 6;
var COLUMN_COMMENT = 7;
function onReportOrApprovalSubmit() {
// This is the Expense Report Spreadsheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
// Also open the Approvals Spreadsheet
var ui = SpreadsheetApp.getUi(); // DEBUG
ui.alert("heelo"); // DEBUG
var approvalsSpreadsheet = SpreadsheetApp.openById(APPROVALS_SPREADSHEET_ID);
var ui = SpreadsheetApp.getUi(); // DEBUG
ui.alert('Requests sentsee'); // DEBUG
var approvalsSheet = approvalsSpreadsheet.getSheets()[0];
// Fetch all the data from the Expense Report Spreadsheet
// getRowsData was reused from Reading Spreadsheet Data using JavaScript Objects tutorial
var data = getRowsData(sheet);
// Fetch all the data from the Approvals Spreadsheet
var approvalsData = getApprovalData(approvalsSheet);
// For every expense report
for (var i = 0; i < data.length; i++) {
var row = data[i];
row.rowNumber = i + 2;
Logger.log("Row num: " + row.rowNumber);
Logger.log("row state before: " + row.state);
Logger.log("row email before: " + row.emailAddress);
if (!row.state) {
Logger.log("row state: " + row.state);
sendReportToManager(row);
sheet.getRange(row.rowNumber, COLUMN_STATE).setValue(row.state);
Logger.log("row state: " + row.state);
} else if (row.state == STATE_MANAGER_EMAIL) {
for (var j = 0; j < approvalsData.length; ++j) {
var approval = approvalsData[j];
if (row.rowNumber != approval.expenseReportId) {
Logger.log("failed expenseId: " + approval.expenseReportId + " rowNumber: " + row.rowNumber);
continue;
}
Logger.log("pass");
// Email the employee to notify the Manager's decision about the expense report.
sendApprovalResults(row, approval);
// Update the state of the report to APPROVED or DENIED
sheet.getRange(row.rowNumber, COLUMN_STATE).setValue(row.state);
break;
}
}
}
}
Row = function(){
};
function getRowsData(sheet){
var data = sheet.getDataRange().getValues();
var row = new Row();
var rows = [];
var counter = 0;
for (var i = 0; i < data.length; i++) {
row = new Row();
row.time = data[i][0];
row.emailAddress = data[i][1];
row.amount = data[i][2];
row.description = data[i][3];
row.managersEmailAddress = data[i][4];
row.state = data[i][5];
if(i != 0){
Logger.log("row state1: " + row.state);
rows[counter] = row;
counter++;
}
}
Logger.log("rowsss:");
Logger.log(rows);
Logger.log("data length: " + rows.length);
Logger.log("data display: ");
for(var k = 0; k < rows.length; k++){
Logger.log(rows[k].state);
}
return rows;
}
ApprovalData = function(){
};
function getApprovalData(sheet){
var data = sheet.getDataRange().getValues();
var row = new ApprovalData();
var rows = [];
for (var i = 0; i < data.length; i++) {
row.time = data[i][0];
row.emailAddress = data[i][1];
row.expenseReportId = data[i][2];
row.approveExpenseReport = data[i][3];
row.comments = data[i][4];
rows[i] = row;
}
return rows;
}
// Sends an email to an employee to communicate the manager's decision on a given Expense Report.
function sendApprovalResults(row, approval) {
var approvedOrRejected = (approval.approveExpenseReport == "Yes") ? "approved" : "rejected";
var message = "<HTML><BODY>"
+ "<P>" + approval.emailAddress + " has " + approvedOrRejected + " your expense report."
+ "<P>Amount: $" + row.amount
+ "<P>Description: " + row.description
+ "<P>Report Id: " + row.rowNumber
+ "<P>Manager's comment: " + (approval.comments || "")
+ "</HTML></BODY>";
MailApp.sendEmail(row.emailAddress, "Expense Report Approval Results", "", {htmlBody: message});
if (approval.approveExpenseReport == "Yes") {
row.state = STATE_APPROVED;
} else {
row.state = STATE_DENIED;
}
}
// Sends an email to a manager to request his approval of an employee expense report.
function sendReportToManager(row) {
var message = "<HTML><BODY>"
+ "<P>" + row.emailAddress + " has requested your approval for an expense report."
+ "<P>" + "Amount: $" + row.amount
+ "<P>" + "Description: " + row.description
+ "<P>" + "Report Id: " + row.rowNumber
+ '<P>Please approve or reject the expense report here.'
+ "</HTML></BODY>";
MailApp.sendEmail(row.managersEmailAddress, "Expense Report Approval Request", "", {htmlBody: message});
row.state = STATE_MANAGER_EMAIL;
Logger.log("id: " + row.rowNumber);
}
function onOpen() {
var ui = SpreadsheetApp.getUi(); // Same variations.
var result = ui.alert(
'Do you want to get new requests?',
ui.ButtonSet.YES_NO);
// Process the user's response.
if (result == ui.Button.YES) {
// User clicked "Yes".
onReportOrApprovalSubmit();
ui.alert('Requests sent');
} else {
// User clicked "No" or X in the title bar.
ui.alert('No requests sent');
}
}
The function onOpen can only access the spreadsheet it is bound to, not others. The UI prompt doesn't change that: showing a custom dialog doesn't add any authorization to the function. One cannot access other spreadsheets from a process that is launched by a simple trigger, such as onOpen.
Solution: let onOpen add menu items that will be used to launch any functions requiring authorization.
function onOpen() {
var menu = [{name: "Get New Requests", functionName: "onReportOrApprovalSubmit"}];
SpreadsheetApp.getActiveSpreadsheet().addMenu("Script", menu);
}
Related
Im trying to send an email from google sheets and I've setup the columns to represent the subject, text and email addresses. The problem is that I need to add a hyperlink in the middle of the message and im stuck here. I can't get the paragraph to format correctly AND have the hyperlink replace a word in the middle of the sentence.
This is the code:
function AISEMAIL() {
var ss = SpreadsheetApp.getActiveSpreadsheet()
var sheet1=ss.getSheetByName('Email Addresses');
var sheet2=ss.getSheetByName('Email Fields');
var subject = sheet2.getRange(2,1).getValue();
var message = sheet2.getRange(2,2).getValue();
var calendardisplayname = sheet2.getRange(2,3).getValue();
var calendarlink = sheet2.getRange(2,4).getValue();
var formdisplayname = sheet2.getRange(2,5).getValue();
var formlink = sheet2.getRange(2,6).getValue();
message=message.replace("<calendar>",calendardisplayname).replace("<form>",formdisplayname);
var n=2;
for (var i = 2; i < n+1 ; i++ ) {
var emailAddress = sheet1.getRange(i,1).getValue();
let options = {
htmlBody: message
+ '' + calendardisplayname + ''
+ '' + formdisplayname + ''
}
GmailApp.sendEmail(emailAddress, subject, message,options);
}
}
Problem:
You are always trying to append the links at the end on your options. Also, it becomes redundant. What you need to do is include the links when you replace the values of <calendar> and <form>.
Code:
// Add the links on the replace
message = message
.replace("<calendar>", '' + calendardisplayname + '')
.replace("<form>", '' + formdisplayname + '');
var n = 2;
for (var i = 2; i < n + 1; i++) {
var emailAddress = sheet1.getRange(i, 1).getValue();
let options = {
htmlBody: message
}
GmailApp.sendEmail(emailAddress, subject, message, options);
}
Old Output:
New Output:
This is just a guess, but could you use HtmlService to create your html message?
function AISEMAIL() {
var ss = SpreadsheetApp.getActiveSpreadsheet()
var sheet1=ss.getSheetByName('Email Addresses');
var sheet2=ss.getSheetByName('Email Fields');
var subject = sheet2.getRange(2,1).getValue();
var message = sheet2.getRange(2,2).getValue();
var calendardisplayname = sheet2.getRange(2,3).getValue();
var calendarlink = sheet2.getRange(2,4).getValue();
var formdisplayname = sheet2.getRange(2,5).getValue();
var formlink = sheet2.getRange(2,6).getValue();
message=message.replace("<calendar>",calendardisplayname).replace("<form>",formdisplayname);
var n=2;
for (var i = 2; i < n+1 ; i++ ) {
var emailAddress = sheet1.getRange(i,1).getValue();
//ADDED htmlText VARIABLE
var htmlText;
let options = {
// ADDED HtmlService.createHtmlOutput()
htmlBody: htmlText = HtmlService.createHtmlOutput(message
+ '' + calendardisplayname + ''
+ '' + formdisplayname + '');
}
// CHANGED message TO htmlText
GmailApp.sendEmail(emailAddress, subject, htmlText,options);
}
}
REFERENCES
HtmlService
Also, if you need to create dynamic html for each email, check out these links...
createTemplateFromFile()
.evaluate()
Templated HTML
I have a form that contains 84 questions, not all of them are mandatory.
This is the script I manage to write so far:
function SendEmail() {
var ActiveSheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var StartRow = 2;
var RowRange = ActiveSheet.getLastRow() - StartRow + 1;
var WholeRange = ActiveSheet.getRange(StartRow,1,RowRange,84);
var AllValues = WholeRange.getValues();
var message = "";
for (i in AllValues) {
var CurrentRow = AllValues[i];
var EmailSent = CurrentRow[85];
if (EmailSent == "Sent")
continue;
# I know this part takes only the first 5 column, I wrote them only as an example. In bold the headers of each column.
message =
"<p><b>Kind of content: </b>" + CurrentRow[2] + "</p>" +
"<p><b>Project Name: </b>" + CurrentRow[3] + "</p>" +
"<p><b>Project Description: </b>" + CurrentRow[4] + "</p>" +
"<p><b>Name of your team: </b>" + CurrentRow[5] + "</p>" +
"<p><b>Scope of work: </b>" + CurrentRow[6] + "</p>";
var setRow = parseInt(i) + StartRow;
ActiveSheet.getRange(setRow, 85).setValue("sent");
}
var SendTo = "email#gmail.com";
var Subject = "New"+" " + CurrentRow[2] +" "+"project request";
MailApp.sendEmail({
to: SendTo,
cc: "",
subject: Subject,
htmlBody: message,
});
}
What I want is to send an email every time somebody fills the form and the content of the email should include only the last row and only the columns with data with their header.
The way this script is written will generate an email with 84 rows, most of them empty and not relevant. Can somebody give me a hand with it?
Thank you so much for your help!!
You can use sheet.getLastRow() to get the index of the last row in the sheet that has data.
For finding columns that have data, you can iterate through the row data and look for cell values that are not blank.
var header = sheet
.getRange(1,1,1,sheet.getLastColumn())
.getDisplayValues()[0];
var data = sheet
.getRange(sheet.getLastRow(),1,1,sheet.getLastColumn())
.getDisplayValues()[0];
var output = [];
for (var d=0; d<data.length; d++) {
if (data[d] !== "") {
output.push(header[d] + " = " + data[d]);
}
}
return data.join("\n");
I know you are too naive to coding and Amit is a busy person, so just to help you, I am plugging in the code he has provided to your code with a small correction, so you can just copy the entire code :)
function SendEmail() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var lRow = sheet.getLastRow();
var emailSent = sheet.getRange(lRow, 86).getValue();
var header = sheet
.getRange(1,1,1,sheet.getLastColumn())
.getDisplayValues()[0];
if (emailSent != "Sent"){
var data = sheet
.getRange(lRow,1,1,sheet.getLastColumn())
.getDisplayValues()[0];
var output = [];
for (var d=0; d<data.length; d++) {
if (data[d] !== "") {
output.push(header[d] + " = " + data[d]);
}
}
var message = output.join("\n");
var SendTo = "email#gmail.com";
var Subject = "New"+" " + sheet.getRange(lRow, 3).getValue() +" "+"project request";
MailApp.sendEmail({
to: SendTo,
cc: "",
subject: Subject,
htmlBody: message,
});
sheet.getRange(lRow, 86).setValue("sent");
}
}
You can use filter, for example
var AllValues = WholeRange.getValues().filter( row => row[5] != '');
will reduce AllValues to only those there column 6 isn't empty
The script I have is sending out the information in the format I want. The problem I have is that it is sending out each row as an indiviual email instead of only sending out the latest data. I only want the last row of data to be sent out.
function CustomEmail() {
var sheet = SpreadsheetApp.getActiveSheet();
lastRow = sheet.getLastRow();
startrow= 2;
var range = sheet.getRange("A2:Z1000");
var UserData = range.getValues();
for (i in UserData) {
var row = UserData[i];
var name = row[2];//market
var senderEmail = ''
if (name === 'South')
{senderEmail = 'tom#no.com';}
else if (name === 'West')
{senderEmail = 'bob#bob.com';}
else if (name === 'East')
{senderEmail = 'non#no.com';}
var AgentOwner = row[18];//Agent Owner
var address = row[20];//Address
var City = row[21];//City
var State = row[22]//state
var Incident = row[17]//incident type
var Date = row[4]//date and time
emailBody = "New Security Incident Report from: " +AgentOwner+ "\nAddress: " +address+ "\nCity: " +City+ "\nState: " +State + "\nIncident: " +Incident + "\nDateTime:" +Date
MailApp.sendEmail(senderEmail,"Security Incident Report", emailBody);
}
}
Try this -
function CustomEmail() {
var sheet = SpreadsheetApp.getActiveSheet();
var row = sheet.getRange(sheet.getLastRow(), 1, 1, sheet.getLastColumn())[0];
Logger.log(row);
var name = row[2]; //market
var senderEmail = '';
if (name === 'South') {
senderEmail = 'tom#no.com';
} else if (name === 'West') {
senderEmail = 'bob#bob.com';
} else if (name === 'East') {
senderEmail = 'non#no.com';
}
var AgentOwner = row[18]; //Agent Owner
var address = row[20]; //Address
var City = row[21]; //City
var State = row[22]; //state
var Incident = row[17]; //incident type
var Date = row[4]; //date and time
emailBody =
'New Security Incident Report from: ' +
AgentOwner +
'\nAddress: ' +
address +
'\nCity: ' +
City +
'\nState: ' +
State +
'\nIncident: ' +
Incident +
'\nDateTime:' +
Date;
MailApp.sendEmail(senderEmail, 'Security Incident Report', emailBody);
}
Edit:
Go to script, paste new code, run the function, then in menu, View > Log and see if the row values are logged properly. If any issues with data indices, adjust them accordingly.
First off, let me say that I am not a developer, nor do I really code beyond basic HTML. So I appreciate your patience. :)
I'm working with a script that is for AdWords, but I believe it's more or less written in Javascript. (I've included the script below.)
Basically, I'm receiving the error message 'Parsing Error: Please check your selector. (line XX)' when I preview the script.
I've searched all around for hours and have yet to find a solution.
I think it may be that a query being returned contains either a single or double quote, and may be messing up the code? Though I can't actually prove that.
Also, yes, I was sure to update lines 17-21 with the correct details.
Any help would be much appreciated!
Thanks!
John
/*
// AdWords Script: Put Data From AdWords Report In Google Sheets
// --------------------------------------------------------------
// Copyright 2017 Optmyzr Inc., All Rights Reserved
//
// This script takes a Google spreadsheet as input. Based on the column headers, data filters, and date range specified
// on this sheet, it will generate different reports.
//
// The goal is to let users create custom automatic reports with AdWords data that they can then include in an automated reporting
// tool like the one offered by Optmyzr.
//
//
// For more PPC management tools, visit www.optmyzr.com
//
*/
var DEBUG = 0; // set to 1 to get more details about what the script does while it runs; default = 0
var REPORT_SHEET_NAME = "report"; // the name of the tab where the report data should go
var SETTINGS_SHEET_NAME = "settings"; // the name of the tab where the filters and date range are specified
var SPREADSHEET_URL = "https://docs.google.com/spreadsheets/d/1dttJTb547L81XYKdTQ56LcfO9hHhbb9wm06ZY5mKhEo/edit#gid=0"; // The URL to the Google spreadsheet with your report template
var EMAIL_ADDRESSES = "example#example.com"; // Get notified by email at this address when a new report is ready
function main() {
var currentSetting = new Object();
currentSetting.ss = SPREADSHEET_URL;
// Read Settings Sheet
var settingsSheet = SpreadsheetApp.openByUrl(currentSetting.ss).getSheetByName(SETTINGS_SHEET_NAME);
var rows = settingsSheet.getDataRange();
var numRows = rows.getNumRows();
var numCols = rows.getNumColumns();
var values = rows.getValues();
var numSettingsRows = numRows - 1;
var sortString = "";
var filters = new Array();
for(var i = 0; i < numRows; i++) {
var row = values[i];
var settingName = row[0];
var settingOperator = row[1];
var settingValue = row[2];
var dataType = row[3];
debug(settingName + " " + settingOperator + " " + settingValue);
if(settingName.toLowerCase().indexOf("report type") != -1) {
var reportType = settingValue;
} else if(settingName.toLowerCase().indexOf("date range") != -1) {
var dateRange = settingValue;
} else if(settingName.toLowerCase().indexOf("sort order") != -1) {
var sortDirection = dataType || "DESC";
if(settingValue) var sortString = "ORDER BY " + settingValue + " " + sortDirection;
var sortColumnIndex = 1;
}else {
if(settingOperator && settingValue) {
if(dataType.toLowerCase().indexOf("long") != -1 || dataType.toLowerCase().indexOf("double") != -1 || dataType.toLowerCase().indexOf("money") != -1 || dataType.toLowerCase().indexOf("integer") != -1) {
var filter = settingName + " " + settingOperator + " " + settingValue;
} else {
if(settingValue.indexOf("'") != -1) {
var filter = settingName + " " + settingOperator + ' "' + settingValue + '"';
} else if(settingValue.indexOf("'") != -1) {
var filter = settingName + " " + settingOperator + " '" + settingValue + "'";
} else {
var filter = settingName + " " + settingOperator + " '" + settingValue + "'";
}
}
debug("filter: " + filter)
filters.push(filter);
}
}
}
// Process the report sheet and fill in the data
var reportSheet = SpreadsheetApp.openByUrl(currentSetting.ss).getSheetByName(REPORT_SHEET_NAME);
var rows = reportSheet.getDataRange();
var numRows = rows.getNumRows();
var numCols = rows.getNumColumns();
var values = rows.getValues();
var numSettingsRows = numRows - 1;
// Read Header Row and match names to settings
var headerNames = new Array();
var row = values[0];
for(var i = 0; i < numCols; i++) {
var value = row[i];
headerNames.push(value);
//debug(value);
}
if(reportType.toLowerCase().indexOf("performance") != -1) {
var dateString = ' DURING ' + dateRange;
} else {
var dateString = "";
}
if(filters.length) {
var query = 'SELECT ' + headerNames.join(",") + ' FROM ' + reportType + ' WHERE ' + filters.join(" AND ") + dateString + " " + sortString;
} else {
var query = 'SELECT ' + headerNames.join(",") + ' FROM ' + reportType + dateString + " " + sortString;
}
debug(query);
var report = AdWordsApp.report(query); //THIS IS LINE 103 WITH THE ERROR
try {
report.exportToSheet(reportSheet);
var subject = "Your " + reportType + " for " + dateRange + " for " + AdWordsApp.currentAccount().getName() + " is ready";
var body = "currentSetting.ss<br>You can now add this data to <a href='https://www.optmyzr.com'>Optmyzr</a> or another reporting system.";
MailApp.sendEmail(EMAIL_ADDRESSES, subject, body);
Logger.log("Your report is ready at " + currentSetting.ss);
Logger.log("You can include this in your scheduled Optmyzr reports or another reporting tool.");
} catch (e) {
debug("error: " + e);
}
}
function debug(text) {
if(DEBUG) Logger.log(text);
}
The area between SELECT and FROM is the selector. You're not selecting any fields with that query. That's happening because the headerNames array is empty. Verify the value of REPORT_SHEET_NAME
while click the save button i am running the dynamic table in each function inside handler to save the dynamic row upload file after it save i am saving in json the value.
After the value saved in hiddenfield it clear the hidden field value . its not store in the hidden field.
i need the hiddenfield json value to save in the database
$('#tblPower tbody tr').each(function () {
debugger;
var stre = $(this).children('td:nth-child(4)').find('input[type="file"]').attr("id");
var country = $(this).children('td:nth-child(1)').find('.Country').val() || 0;
var validity = $(this).children('td:nth-child(2)').find('.validity').val() || 0;
var clavalidity = $(this).children('td:nth-child(3)').find('.clavalidity').val();
var POAID = $(this).children('td:nth-child(2)').find('span').text() || 0;
var FileName = "";
var uploadfiles = $("#" + stre + "").get(0);
var uploadedfiles = uploadfiles.files;
var fromdata = new FormData();
for (var i = 0; i < uploadedfiles.length; i++) {
fromdata.append(uploadedfiles[i].name, uploadedfiles[i]);
}
var choice = {};
choice.url = "UploadHandler.ashx";
choice.type = "POST";
choice.data = fromdata;
choice.contentType = false;
choice.processData = false;
choice.success = function (result) {
FileName = result;
if (power == '') {
power = '{"POAID":"' + POAID + '","Country":"' + country + '","Validity":"' + validity + '","Notes":"' + clavalidity + '","UploadId":"' + stre + '","FileName":"' + FileName + '"}';
}
else {
power += ',{"POAID":"' + POAID + '","Country":"' + country + '","Validity":"' + validity + '","Notes":"' + clavalidity + '","UploadId":"' + stre + '","FileName":"' + FileName + '"}';
}
power = '[' + power + ']';
dynamic(power);
**$('#hfAttorney').val(power);**
};
choice.error = function (err) {
alert(err.statusText);
};
$.ajax(choice);
});