Search code examples
google-sheetsgoogle-apps-script

How to execute a Google Apps Script ONLY on the edited row


I have an auto-mailing script written in the Google Apps Script language. It's linked to an activated installed edit trigger and sends an email to the address in the spreadsheet if the value in col G is changed to "Yes". Here is the spreadsheet: https://docs.google.com/spreadsheets/d/176ld8VOkIksSuSWBErhD5jiZbhCWpZX7wUFwqFJJ-Gs/edit?gid=0#gid=0

and here is the script:

function sendEmails(e) {
  const source = e.source.getActiveSheet(); 
  const dataRange = e.source.getRange("A2:G");
  const data = dataRange.getValues();

console.log(data);
console.log(data.length);
  for (let i = 0; i < data.length; i++) {
    const row = data[i];
    const businessName = row[0];
    const emailAddress = row[1];
    const invoiceNumber = row[2];
    const invoiceAmount = row[3];
    const dueDate = row[4];
    const subject = row[5];
    const edval = row[6];

    console.log(`Row ${i + 2}: ${businessName}, ${emailAddress}, ${invoiceNumber}, ${invoiceAmount}, ${dueDate}, ${subject}`); // Logging the data

    if (edval=="Yes"){

      if (!emailAddress) {
      console.log(`Row ${i + 2}: Email address missing.`);
      continue;
      }
      const message = createEmailMessage(businessName, invoiceNumber, invoiceAmount, dueDate);

      function createEmailMessage(businessName, invoiceNumber, invoiceAmount, dueDate) {
      const message = `Dear ${businessName},

        Your ACME order number ${invoiceNumber} with an amount of $${invoiceAmount} is due on ${dueDate}.

        Send us our money or we'll send a huge boxing glove on a a spring to pop you in the nose!!

        Thank you for your prompt attention to this matter.`;

      return message;
      }

      try {
       GmailApp.sendEmail(emailAddress, subject, message,{replyTo: "[email protected]"});
        console.log(`Row ${i + 2}: Email sent to ${emailAddress}`);
      } catch (error) {
    console.log(`Row ${i + 2}: Error sending email to ${emailAddress}. Error message: ${error.message}`);
      }

    }


  }
}

The problem is that emails are sent to every row with "yes" in col G. How can I make this so that it will only send an email to the address in the row where the edit occurred?

Note: sometimes it seems like the edit trigger is working correctly and that only edited rows get an email. Then I test it again and see emails sent to all rows with "Yes" in col G. Is there a time frame where edits are recorded? Am I testing too quickly? Man, I hope that's it but I doubt it.

Citation: https://developers.google.com/


Solution

  • Modification points:

    • In your script, all rows are searched when the script is run. By this, when the value of column "G" is "Yes", the email is sent. This is the reason for your current issue.
    • From It's linked to an activated installed edit trigger, in this case, when the event object e is used, the edited row can be easily obtained. By this, getRange("A2:G") can be modified to getRange(A${row}:G${row}). And, you can get only the edited row. In this modification, the value of column "G" is checked at the top of the script. So, it will be getRange(A${row}:F${row}).
    • In your script, getValues is used to retrieve the values. In this case, the date value of column "E" is retrieved as the date object. If you want to retrieve the displaying value of the cell, you can use getDisplayValues instead of getValues. But I'm not sure about your actual expected result. So, in this modification, getValues is used.

    When these points are reflected in your script, it becomes as follows.

    Modified script:

    When you use this script, please install the OnEdit trigger to the function sendEmails and edit column "G" to "Yes". By this, the script is automatically run.

    In this modification, the sheet name is also checked. In your provided Spreadsheet, "Sheet1" is used. If your actual situation is different from it, please modify the sheet name in the if statement.

    function sendEmails(e) {
      if (!e) {
        console.warn("No event object 'e'. In this case, please edit column 'G' to 'Yes'.");
        return;
      }
      const { range } = e;
      const sheet = range.getSheet();
      const value = range.getValue();
      if (sheet.getName() != "Sheet1" || range.rowStart == 1 || range.columnStart != 7 || value != "Yes") return;
      const row = range.rowStart;
      const [[businessName, emailAddress, invoiceNumber, invoiceAmount, dueDate, subject]] = sheet.getRange(`A${row}:F${row}`).getValues(); // or sheet.getRange(`A${row}:F${row}`).getDisplayValues()
      console.log(`Row ${row}: ${businessName}, ${emailAddress}, ${invoiceNumber}, ${invoiceAmount}, ${dueDate}, ${subject}`);
      if (!emailAddress) {
        console.log(`Row ${row}: Email address missing.`);
        return;
      }
      const message = createEmailMessage(businessName, invoiceNumber, invoiceAmount, dueDate);
      function createEmailMessage(businessName, invoiceNumber, invoiceAmount, dueDate) {
        const message = [
          `Dear ${businessName}`,
          `Your ACME order number ${invoiceNumber} with an amount of $${invoiceAmount} is due on ${dueDate}.`,
          `Send us our money or we'll send a huge boxing glove on a a spring to pop you in the nose!!`,
          `Thank you for your prompt attention to this matter.`,
        ].join("\n\n");
        return message;
      }
      try {
        GmailApp.sendEmail(emailAddress, subject, message, { replyTo: "[email protected]" });
        console.log(`Row ${row}: Email sent to ${emailAddress}`);
      } catch (error) {
        console.log(`Row ${row}: Error sending email to ${emailAddress}. Error message: ${error.message}`);
      }
    }
    

    In this script, when the cell value of column "G" is changed to "Yes", an email is sent.

    References: