I have a very simple need and I think a script will be able to do this. I have a order tracking sheet with many rows of data and in column Q my team is supposed to enter a date of acknowledgement. Well, at times this process gets overlooked.
I would like to have a script which checks to see which cells in Q are blank, and send me (and my team) a list of the empty cells, but refer to them with labels from the data in A , B , C & D and make a reference to the entry date which is in column P. This way, I am not hunting for cells, but the script is sending me an email of a list which is easy to read.
The email only has to say something like :
"The following orders have not yet been acknowledged:
TextfromA, TextfromB, TextfromC, TextfromD sent to vendor on DatefromP
TextfromA, TextfromB, TextfromC, TextfromD sent to vendor on DatefromP
TextfromA, TextfromB, TextfromC, TextfromD sent to vendor on DatefromP
etc....
Once the Q cell is populated, it should be ignored by the script. I would like the script to email me and the team this current list every 72 hours. Also, if at all possible I would like to ignore certain rows (which are like headers). If we can't, that's fine.
I know it's probably very simple, but I do need some help!!
Thanks in advance!
Here's a hint to get you started:
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName('tracking sheet');
var rg=sh.getDataRange();
var dataA=rg.getValues();
var message=''
for(var i=0;i<dataA.length;i++){
if(!dataA[i][16]){
message+=dataA[i][0] + ', ' + dataA[i][1] + ', ' + dataA[i][2] + ', ' + dataA[i][3] + '\n';
}
}
if(message.length>0){
//Send Emails
}
Related
I have a script that looks into values in column G and if the correspondent cell in column A is empty, sends me an email.
--- WHAT WORKS --
It works ok for static values: it sends one email per each not empty cell in column G for which there is no value in column A
--- WHAT DOESN'T WORK --
It sends several emails for what I assume it's every Column G cell (empty or not) when the column A values are fetched from another tab. That way it's like all G and A cells have data, so I get multiple unwanted emails.
This is the script code:
const ss = SpreadsheetApp.getActive();
const sh = ss.getSheetByName('Sheet to send emails');
const data = sh.getRange('A2:G'+sh.getLastRow()).getValues();
data.forEach(r=>{
let overdueValue = r[0];
if (overdueValue === ""){
let name = r[6];
let message = 'Text ' + name;
let subject = 'TEXT.'
MailApp.sendEmail('myemail#gmail.com', subject, message);
}
});
}
And this is the link to the test sheet:
https://docs.google.com/spreadsheets/d/1OKQlm0PjEjDB7PXvt34Og2fa4vPZWnvLazTEawEtOXg/edit?usp=sharing
In this test case, I "should" only get one email, related to ID 55555. With the script as is, I get one related to 55555 and several others "undefined".
To avoid e-mail spam, I didn't add the script to that sheet but it shows the "Vlookup" idea.
Can anyone give me a hand, please?
Thank you in advance
Issue:
The issue with your original script is that the sh.getLastRow returns 1000 (it also processes those rows that doesn't have contents, result to undefined)
Fix 1: Get specific last row of column G:
const gValues = sh.getRange('G1:G').getValues();
const gLastRow = gValues.filter(String).length;
or
Fix 2: Filter data
const data = sh.getRange('A2:G' + sh.getLastRow()).getValues().filter(r => r[6]);
Note:
As Kris mentioned in the comments, there is a specific case where getting the last row above will fail (same with getNextDataCell). This will not properly get the last row WHEN there are blank rows in between the first and last row of the column. If you have this kind of data, then use the 2nd method which is filtering the data.
If your data in column G does not have blank cells in between the first and last row, then any method should work.
I checked your test sheet, and sh.getLastRow() is 1000.
OPTION 1
If column G won't have empty cells between filled ones, then you can do this:
const ss = SpreadsheetApp.getActive();
const sheet = ss.getSheetByName("Sheet to send emails");
// get the first cell in column G
var gHeader = sheet.getRange(1, 7);
// equivelent of using CTRL + down arrow to find the last da
var lastRow = gcell.getNextDataCell(SpreadsheetApp.Direction.DOWN).getRow();
const data = sheet.getRange(2, 1, lastRow, 7).getValues();
OPTION 2
Add another condition to your code - like this:
data.forEach(r=>{
let overdueValue = r[0];
let name = r[6]
// check if the value in col A is blankd and col G is not blank
if (overdueValue === "" && name !== ""){
let message = 'Text ' + name;
let subject = 'TEXT.'
MailApp.sendEmail('myemail#gmail.com', subject, message);
}
});
And to speed it up, use a named range to limit how many rows it has to iterate through:
const ss = SpreadsheetApp.getActive();
const data = ss.getRangeByName("Your_NamedRange_Here").getValues();
Using Spark/Scala to attempt a "simple" query. I have a file which, after line 1 below runs, looks like this
EmpReg,EmpOT,RegPay,OTPay
Alice,Alice,400,20
Bob,Bob,300,0
Carol,Carol,450,120
Dan,Dan,400,200
Ellen,Ellen,360,40
The first and third columns (EmpReg, RegPay) come from one source and the second and third columns (EmpOT, OTPay) come from a second source. My objective is output that looks like this.
Emp,Pay
Alice,420
Bob,300
Carol,570
Dan,600
Ellen,400
Here is the code that I have been trying, at least what I have saved.
var q2 = q.join(q1, q("EmpReg") === q1("EmpOT"), "fullouter")
//q2 = q2.select("EmpReg", ($"RegPay" + $"OTPay"))
//q2 = q2.groupBy($"EmpReg".sum($"RegPay" + $"OTPay"))
var add = q2.select(($"RegPay" + $"OTPay"))
//q2 = q2.sum("RegPay", "OTPay")
//q2 = q2.groupBy("EmpReg", "EmpOT")
//var q2 = q.join(q1).where("EmpReg") === "EmpOT"))
//q2 = q2.select("EmpReg").sum("RegPay", "OTPay")
//q2.show
add.show
[q] is the first file which represents regular pay. [q1] is the second file which represents overtime pay. [q2] is the combination shown in the first example above. Primary keys are [EmpReg] and [EmpOT]. don't really need to combine [EmpReg] and [EmpOT] since they are the same, and it doesn't make any difference which I use.
I really need to add [RegPay] and [OTPay] to get [Pay], but for the life of me I can't get it to work. The lines commented out return various errors. I can add the two pay columns, and select an appropriate employee column, but can't seem to do it in one query. I am constrained to use Scala on Databricks. Othewise, I might do something like this.
select q.EmpReg as Emp, (q.RegPay + q1.OTPay) as Pay
from q join q1 on q.EmpReg = q1.EmpOT
(Why can't things ever be simple?)
You can use a similar approach as in your SQL query:
val q2 = q.join(q1, q("EmpReg") === q1("EmpOT"), "fullouter")
val add = q2.select(q("EmpReg").as("Emp"), (q("RegPay") + q1("OTPay")).as("Pay"))
Your code has this line
q2.select("EmpReg", ($"RegPay" + $"OTPay"))
which should work if you add $ before "EmpReg". You can't have both strings and columns in the select statement. This works in Python but not Scala.
I've searched everywhere on the web and on this site as well. What I want to do is relatively simple, however I don't know enough about javascript to write it myself but I am learning.
I have a google sheet with email addresses as well as data in 5 columns. Let's say there are 3 email addresses total. Now say there are 3 rows with the same email address and each row has new/different data than the previous row. I want to send JUST 1 email to that address with the 3 rows / 5 columns of data. There also may be an email address but with no data in the rows/columns. Obviously nothing should be sent from that row. Then there's the second email address. Repeat but send to the new email address. Next email, etc.
Here's the sheet to give you a clearer picture. I would like to keep the look/color of the table of column B through Column F in each email.
The following script had me most of the way there (sans the look/color of the table), however when I tried using it a few days ago it was sending a new email for each row of data instead of just one email for several rows of data...but it previously was working! (again sans the look/color of my sheet, it was simple text)
function sendEmails() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
ss.setActiveSheet(ss.getSheetByName("sheet1"));
var sheet = SpreadsheetApp.getActiveSheet();
var dataRange = sheet.getRange("A2:m8");
var data = dataRange.getValues();
// Modified
var temp = [];
var emailAddress, data1, data2, data3, data4, data5;
for (var i in data) {
[emailAddress, data1, data2, data3, data4, data5] = data[i];
var message = '\n\n' + data1 + ' ' + data2 + ' ' + data3 + ' ' + data4 + ' ' + data5;
if (emailAddress || !data1) {
if (temp.length > 0) {
MailApp.sendEmail(temp[0], "Subject", temp.slice(1,temp.length).join("\n"));
var temp = [];
}
temp.push(emailAddress, message);
if (!dimension) break;
} else {
temp.push(message);
}
}
}
The format of the body of the email should look like the following, but again, I also want it to retain the look/feel/color from the sheet:
Once that's working, I would like my email signature from Gmail included in the email. I'm not sure if I can get it exactly as is in my Gmail account, or just simple text via javascript.
I have this line but that's simple text. Any way to get my logo/formatting/etc. to match my Gmail or outlook account?
var signature = "\n---\nsample signature";
Your help is much appreciated. Thank you.
I have a Google Sheet where information from a Google Form is dumped. Two of the columns create a date range (columns C and G) and I would like for the sheet to automatically create a new row of information for every date of the range and copy all the other information from the original row for every row that is created. In the end, every date in the range has it's own row regardless of it being 2 days or 25 and all the the information gathered through the form be present for each day. If there is not a date in column G, it is only a one day trip and there is no need for additional rows. To make things more difficult when someone submits a form, the information is entered into the row directly beneath the last one that it filled, so these new rows filled by the date range will need to be down the sheet, possibly beginning at row 2000 or more as this sheet will have a lot of information in a few months. As you may see in the sample, there is another sheet in the workbook that performs all the sorting. Thanks for any help.
Sample Document
You will need to create a form submit event and attach the following code to it. Also you'll need to create a sheet name 'ResponseReview'.
function formSubmitEvent1(e)
{
var ss=SpreadsheetApp.openById('SpreadsheetID');
var sht=ss.getSheetByName('ResponseReview');
sht.appendRow(e.values);
}
The above code will need the SpreadsheetId in the openById Method. This code will append any new rows to the end of the ResponseReview sheet.
The code below will expand any entrees that have a date in column 3 and 7 and it will also remove the end date in column 7 from that first row. I use the fact that if column 3 is not empty and column 7 is not and column 7 is no equal to column 3 then that's a row that needs to be expanded. So I have to remove the end date so that it won't continue to get expanded when it's run again in the future. We could figure something else out if you need to keep than end date. We could add a don't expand column at the end.
function convertRangetoRows()
{
var ss=SpreadsheetApp.getActive();
var sht=ss.getSheetByName('ResponseReview');
var rng=sht.getDataRange();
var rngA=rng.getValues();
var rngB=[];
var day=86400000;
rngB.push(rngA[0]);
for(var i=1;i<rngA.length;i++)
{
rngB.push(rngA[i]);
if(rngA[i][2] && rngA[i][6] && rngA[i][2]!=rngA[i][6])
{
var row=rngA[i].slice();//returns a new copy of the array by value
rngA[i][6]='';//deletes the end date by reference so it also deletes the one thats already been pushed into rngB
var dt0=new Date(row[2]);
var dt1=new Date(row[6]);
var days=(dt1.valueOf()-dt0.valueOf())/day;
var dt=dt0.valueOf();
for(j=0;j<days;j++)
{
dt+=day;
row[2]=Utilities.formatDate(new Date(dt), Session.getScriptTimeZone(), "MM/dd/yyyy");//original array unchanged
row[6]='';//original array unchanged
rngB.push(row.slice());//push in a copy
}
}
var intermediate='nothing';
}
var outrng=sht.getRange(1,1,rngB.length,rngB[0].length);
outrng.setValues(rngB);
var end='the end is near';
}
This is what my spreadsheet looks like before running the expansion function:
And After:
And now you can leave the sheet linked to the form alone and let it be an archive for submitted data.
Friends,
I have written the following JavaScript to ascertain which row of an tabular form the user is currently on. i.e. they have clicked a select list on row 4. I need the row number to then get the correct value of a field on this same row which I can then perform some further processing on.
What this JavaScript does is get the id of the triggering item, e.g. f02_0004 This tells me that the select list in column 2 of row 4 has been selected. So my Javascript gets just the row information i.e. 0004 and then uses that to reference another field in this row and at the moment just output the value to show I have the correct value.
<script language="JavaScript" type="text/javascript">
function cascade(pThis){
var row = getTheCurrentRow(pThis.id);
var nameAndRow = "f03_" + row;
var costCentre = $x(nameAndRow).value;
alert("the cost centre id is " + costCentre);
}
// the triggerItem has the name fxx_yyyy where xx is the column number and
// yyyy is the row. This function just returns yyyyy
function getTheCurrentRow(triggerItem){
var theRow = triggerItem.slice(4);
return theRow;
}
Whilst this works I can't help feeling that I must be re-inventing the wheel and that
either, there are built-in's that I can use or if not there maybe a "better" way?
In case of need I'm using Apex 4.0
Thanks in advance for any you can provide.
Well, what you have described is exactly what I typically do myself!
An alternative in Apex 4.0 would be to use jQuery to navigate the DOM something like this:
var costCentre = $(pThis).parents('tr').find('input[name="f03"]')[0].value;
I have tested this and it works OK in my test form.