I've been trying to understand this email on edit function.
I just cannot figure it out.
I need to send informations in Columns A, B, C, F, G, H and J. The email trigger is in column K and would be selected from data validation.
I have the following function which works great. It send the email but I cannot adjust which rows to show here.
What am I doing wrong?
I want body of the Email to appear as follows when I choose email in K101
Headers in A3 : Data in cell A101
Headers in B3 : Data in cell B101
Headers in C3 : Data in cell C101
Headers in F3 : Data in cell F101
Headers in G3 : Data in cell G101
Headers in H3 : Data in cell H101
Headers in J3 : Data in cell J101
Help???
function mailOnEdit(e) {
var sheet = e.source.getActiveSheet();
if (sheet.getName() !== 'Registry Sheet' || e.range.columnStart !== 11 || e.range.rowStart < 4 || !e.value) return;
var h = sheet.getRange(1, 1, 1, 8)
.getValues()[0],
val = sheet.getRange(e.range.rowStart, 1, 1, 8)
.getValues()[0],
i = 0,
body = "",
notCols = [3, 4];
while (i < 7) {
if (notCols.indexOf(i) == -1) {
body += h[i] + ": " + val[i] + "\n";
}
i += 1;
}
body += "\n\n Contact customer within 30 minutes and reply. Thanks";
MailApp.sendEmail(e.value, "New job", body)
}
Seems like your headers are in row three and not in row one. So
Change
var h = sheet.getRange(1, 1, 1, 8).getValues()[0]
to
var h = sheet.getRange(3, 1, 1, 8).getValues()[0]
Related
I keep track of trading data in a private spreadsheet and, once a trade is closed, I push a button to run some AppsScript to copy the data across to a publicly visible spreadsheet. It all works well except the following point. I put the dates and times a trade is opened in two different columns and the same with the dates and times trades are closed. When I copy the dates across to the target spreadsheet, instead of "10 Jul 2022" it is showing as "10/07/2022 02:00:00". When I sort the spreadsheet on these date and time columns, this can often produce unwated results. What I want is for the date column to just display as "10 Jul 2022" and for that data to recognized as a date for sorting purposes. Is that possible?
function updatePublicSheet(){
var coinBought;
var dateBought;
var timeBought;
var priceBought;
var dateSold;
var timeSold;
var priceSold;
var sLrIndex;
var tLrIndex;
var sss = SpreadsheetApp.openByUrl("https://docs.google.com/spreadsheets/d/1cBGpD0nUqCGtQqq78svw4fxA4ZgwBqV2yzIbgY1tY2o/edit#gid=1107419239");
var sSheet = sss.getSheetByName('SS Open Trades');
var sRng = sSheet.getRange("H3:J").getValues();
// Find last entry in columns H, I or J in the Open Trades sheet
for (var i = sRng.length-1;i>=0;i--){
var sLrIndex = i;
if (!sRng[i].every(function(c){return c == "";})){
break;
}
}
Logger.log("Last source row is %s", sLrIndex + 3);
// Check that the closed information has been added to all columns
// index 8 = 8th column = H = dateSold
// index 9 = 9th column = I = timeSold
// index 10 = 10th column = J = priceSold
if ((sSheet.getRange((sLrIndex + 3),8).getValue().length) == 0 ||
(sSheet.getRange((sLrIndex + 3),9).getValue().length) == 0 ||
(sSheet.getRange((sLrIndex + 3),10).getValue().length) == 0){
SpreadsheetApp.getUi().alert("You cannot process partially completed entries");
return;
}
// If there is a complete closed entry, copy the values to the variables
if (sLrIndex > 0)
{
coinBought = sSheet.getRange((sLrIndex + 3),2).getValue();
dateBought = toUtcString(sSheet.getRange((sLrIndex + 3),4).getValue());
timeBought = sSheet.getRange((sLrIndex + 3),5).getValue();
priceBought = sSheet.getRange((sLrIndex + 3),6).getValue();
dateSold = toUtcString(sSheet.getRange((sLrIndex + 3),8).getValue());
timeSold = sSheet.getRange((sLrIndex + 3),9).getValue();
priceSold = sSheet.getRange((sLrIndex + 3),10).getValue();
//Logger.log(priceSold);
}
else
{
SpreadsheetApp.getUi().alert("There are no completed entries to process");
return;
}
var tss = SpreadsheetApp.openByUrl("https://docs.google.com/spreadsheets/d/1umkTCr95FZUrZzv0e_ZDD9QZYiiYuH1fJohPYQzNE9Q/edit#gid=1644116137");
var tSheet = tss.getSheetByName('Trade Tracker');
var tRng = tSheet.getRange("B3:J").getValues();
// Create a new row at Row 5 on the Trades Tracker Sheet
tSheet.insertRows(5, 1);//shift all rows down by one from row 5
// Copy values from row 4 to row 5, including the formulae
var tRange = tSheet.getRange(4, 1, 1, 26);
tRange.copyTo(tSheet.getRange(5, 1, 1, 26), {contentsOnly:false});
// Populate row 4, first 7 table columns, from the variables
//Logger.log(coinBought);
tSheet.getRange(4,2).setValue(coinBought);
tSheet.getRange(4,3).setValue(dateBought);
tSheet.getRange(4,4).setValue(timeBought);
tSheet.getRange(4,5).setValue(priceBought);
tSheet.getRange(4,6).setValue(dateSold);
tSheet.getRange(4,7).setValue(timeSold);
tSheet.getRange(4,8).setValue(priceSold);
// Format the cells with dates and times
tSheet.getRange(4,3).setNumberFormat("dd MMM yyyy"); // Short Date
tSheet.getRange(4,4).setNumberFormat("HH:mm"); // Short Time
tSheet.getRange(4,6).setNumberFormat("dd MMM yyyy"); // Short Date
tSheet.getRange(4,7).setNumberFormat("HH:mm"); // Short Time
// Sort the sheet by date/time closed
// Find last entry in Trade Tracker sheet
for (var i = tRng.length-1;i>=0;i--){
var tLrIndex = i;
if (!tRng[i].every(function(c){return c == "";})){
break;
}
}
Logger.log("Last target row is %s", tLrIndex + 4);
var tRange = tSheet.getRange(4, 2, tLrIndex + 4, 8)
tRange.sort([{column: 6, ascending: false}, {column: 7, ascending: false}]);
// On the source sheet, delete the row just copied and add another blank row at the bottom of the table
sSheet.deleteRows(sLrIndex + 3, 1);
var rowLast = sSheet.getLastRow();
sSheet.insertRowAfter(rowLast - 1);
// Copy values from the new last row to the new previous to last row, including the formulae
var sRange = sSheet.getRange(rowLast + 1, 1, 1, 10);
sRange.copyTo(sSheet.getRange(rowLast - 1, 1, 1, 10), {contentsOnly:false});
SpreadsheetApp.getUi().alert("One completed entry processed");
}
I would like to split a string in a column to n rows in Talend.
For example :
column
2aabbccdd
The first number is the "n" which I use to define the row lenght, so the expected result should be :
row 1 = aa
row 2 = bb
row 3 = cc
row 4 = dd
The idea here is to iterate on the string and cut it every 2 characters.
Any idea please ?
I would use a tJavaFlex to split the string, with a trick to have n rows coming out of it.
tJavaFlex's main code:
int n = Integer.parseInt(row1.str.substring(0, 4)); //get n from the first 4 characters
String str2 = row1.str.substring(4); //get the string after n
int nbParts = (str2.length() + 1) / n;
System.out.println("number of parts = " + nbParts);
for (int i = 0; i < nbParts; i++)
{
String part = str2.substring(i * n);
if(part.length() > n)
{
part = part.substring(0, n);
}
row2.str = part;
And tJavaFlex's end code is just a closing brace:
}
The trick is to use a for loop in the main code, but only close it in the end code.
tFixedFlowInput contains just one column holding the input string.
I'm using ag grid with angularjs and the filter does not work with formatted numbers. I use formatted numbers with currency values.
Below is the columndef code:
{ headerName:"GBO", field: "GBO", width: 200, editable:true, cellClass: "number-cell",filter:'agNumberColumnFilter',
cellRenderer : function(params){
if(params.value == "" || params.value == null)
return '-';
else return params.value;
}
}
Before assigning the data to the grid, I format the numbers using :
$scope.formatNumberOnly = function(num,c, d, t){
//console.log(num );
var n = getNumber(num);
//var n = this,
c = isNaN(c = Math.abs(c)) ? 2 : c,
d = d == undefined ? "." : d,
t = t == undefined ? "," : t,
s = n < 0 ? "-" : "",
i = parseInt(n = Math.abs(+n || 0).toFixed(c)) + "",
j = (j = i.length) > 3 ? j % 3 : 0;
return s + (j ? i.substr(0, j) + t : "") + i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + t) + (c ? d + Math.abs(n - i).toFixed(c).slice(2) : "");
};
});
The problem here is that the filter doesn't work with these formatted numbers and only seems to be working for values upto 999.
Can anyone please help me with a solution to this filtering problem?
If you want the filter to work on these formatted values, you should use a valueGetter instead of a valueFormatter
You should implement the above formatter function as a valueGetter in column Definition.
Also a number filter won't work as in order for your formatted number to be interpreted, it should be a text filter.
Here is an example from official docs.
I was wondering if you could please help. I am trying to use the MailApp.sendEmail method to send emails when a date is entered into column M. I currently use a button on the spreadsheet to trigger the sending of emails and this works fine: emails are sent.
I naturally wish for duplicate emails not to be sent and as such I wish for the "EMAIL_SENT" value to entered into Column N at the moment I trigger the sending of emails, but this does not work. This is despite my code pretty much replicating what is shown on google's own page (https://developers.google.com/apps-script/articles/sending_emails) et al.
I also wish it to generate a timestamp into Column O when emails are sent. Again, this does not work, but I can believe my approach is not correct. I'll be honest and say I am new to this!
Please advise me on where the errors are in my code.
var EMAIL_SENT = "EMAIL_SENT";
function SEWEmailReminder() {
var sheet = SpreadsheetApp.getActiveSheet();
var numRows = sheet.getLastRow(); // nothing to change
var startRow = "9"; // put the row number where the formula should start
var range = sheet.getRange(startRow, 1, numRows, 22) // Fetch the range of cells A9:Vx
var data = range.getValues();
for (var i = 0; i < data.length; ++i) {
var row = data[i];
var programmeid = row[0]; // 1st column A
var programmename = row[1]; // 2nd column B
var author = row[2]; // 3rd column C
var authoremail = row[3]; // 4th column D
var comments = row[4]; // 5th column E
var programmestatus = row[5]; // 6th column F
var datadate = row[6]; // 7th column G
var plannedcompletiondate = row[7]; // 8th column H
var completiondate = row[8]; // 9th column I
var presubjointreviewdate = row[9]; // 10th column J
var plannedsubdate = row[10]; // 11th column K
var jvissuedate = row[11]; // 12th column L
var sewresponsedate = row[12]; // 13th column M
var emailsent = row[13]; // 14th column N
var emailtimestamp = row[14]; // 15th column O
var sewreviewdate = row[15]; // 16th column P
var jvreceiveddate = row[16]; // 17th column Q
var difference = row[17]; // 18th column R
var rejectedaccepted = row[18]; // 19th column S
var recipients = row[19]; // 20th column T
var forclientacceptance = row[20]; // 21st column U
var jvreceiveddate = row[21]; // 22nd column V
var subject = "SEW Notification Email";
var message = "Dear " + author + ",\nYou have received this email to notify you that the SEW Response Date is " + sewresponsedate + " for Programme ID " + programmeid + ", titled " + programmename + ".\nKind Regards,\nAdmin"
if (row[12] != "") {
if (emailsent != EMAIL_SENT) { // Prevents sending duplicates
MailApp.sendEmail(authoremail, subject, message);
sheet.getRange(startRow + i, 14).setValue(EMAIL_SENT);
sheet.getRange(startRow + i, 15).setValue(new Date()); // set date & time stamp
// Make sure the cell is updated right away in case the script is interrupted
SpreadsheetApp.flush();
}
}
}
}
I figured out how to compare dates in Google Sheets but when I try to enter more dates for some reason all the cells that were green and red become all red. Also how can I make two cells red if only one cell has a date?
Example: In cell D18 the Due date is 4-18-2014 and in cell E18 the cell is blank. I want to make both cells red so I would know that I should find out why is that cell red.
This is the code I have so far:
function onEdit() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = ss.getSheetByName('Copy of Project Sheet 1');
var values1Rule1 = s.getRange('E2:E1000').getValues();
var values2Rule1 = s.getRange('D2:D1000').getValues();
var range3Rule1 = s.getRange('D2:E2');
var color1 = 'Red';
var color2 = 'Green';
for (var row in values1Rule1) {
for (var col in values1Rule1[row]) {
if (values1Rule1[row][col] > values2Rule1[row][col]) s.getRange(s.getRange('D2').offset(row, col, 1, 2).getA1Notation()).setBackgroundColor(color1);
else if (values1Rule1[row][col] < values2Rule1[row][col]) s.getRange(s.getRange('D2').offset(row, col, 1, 2).getA1Notation()).setBackgroundColor(color2);
else s.getRange(s.getRange('D2').offset(row, col, 1, 2).getA1Notation()).setBackgroundColor('white'); }}
};
All you need to do is add this condition as an OR clause in your red condition, e.g.
if (values1Rule1[row][col] > values2Rule1[row][col] || values1Rule1[row][col] === '')
But there's lots of "minor" problems with your code. First of all, you're doing way too many API calls unnecessarily. This is a big performance issue. For example, when you offset, you already have the new range, there's no need to getA1Notation then get the range again, you could do:
s.getRange('D2').offset(row, col, 1, 2).setBackgroundColor(color1);
But that's still two calls, getting D2, then offseting. You could get the desired range at once:
s.getRange(row+1, 4, 1, 2).setBackgroundColor(color1);
I'd go even further and build a matrix of colors and set it all at once after the loop:
s.getRange('D2:E1000').setBackgroundColors(colors);
But even better, inside an onEdit you should only work on what has just being edited, instead of triggering a full recalculation of your colors because the user edited something on another column or another sheet entirely.
I think your code should be something like this:
function onEdit(e) {
var ss = e.source;
var s = ss.getActiveSheet();
if( s.getName() !== 'Copy of Project Sheet 1' ) return; //only interested in one sheet
var r = s.getActiveRange();
var c = r.getColumn();
if( c !== 4 && c !== 5 ) return; //only interested in changes on columns D or E
r = r.offset(0, c === 4 ? 0 : -1, 1, 2);
var v = r.getValues()[0];
r.setBackgroundColor(v[1] === '' || v[1] > v[0] ? 'red' : v[1] < v[0] ? 'green' : 'white');
}
--edit
You can not run this function manually directly, because it needs a parameter that is passed only when it runs automatically. But you can emulate it with a test function, like this:
function testEdit() { onEdit({source:SpreadsheetApp.getActive()}); }