【问题标题】:Problems generating PDFs from Google Forms after it is submitted提交后从 Google 表单生成 PDF 的问题
【发布时间】:2021-04-05 01:08:31
【问题描述】:

我正在创建一个 Google 脚本,它根据 Google 表单的字段填充 Google Doc 模板,并通过电子邮件将生成的 PDF 发送给用户。

这里详细解释了我遵循的所有步骤:Hacking it: Generate PDFs from Google Forms

脚本(从文章中获得)是:

function onSubmit(e) {
  const rg = e.range;
  const sh = rg.getSheet();
  
  //Get all the form submitted data
  //Note: This data is dependent on the headers. If headers, are changed update these as well.
  const cName = e.namedValues['Client Name'][0];
  const cEmail = e.namedValues['Client Email'][0];
  const cAddress = e.namedValues['Client Address'][0];
  const cMobile = e.namedValues['Client Mobile'][0];
  const sendCopy = e.namedValues['Send client a copy?'][0];
  const paymentType = e.namedValues['What is your agreed upon payment schedule?'][0];
  const fixedCost = e.namedValues['What was your agreed upon cost for the project?'][0];
  const hourlyRate = e.namedValues['Hourly Rate'][0];
  const manHours = e.namedValues['Total man hours'][0];
  const services = e.namedValues['Select the services'][0];
  
  //Consequential Data
  const tax = 18.5
  var subtotal = 0;
  var taxAmt = 0;
  var payableAmt = 0;
      
  //if the user has selected hourly payment model
  //Note: Be careful that the responses match the elements on the actual form
  switch (paymentType ){
    case 'Hourly Rate':
      subtotal = hourlyRate*manHours;
      taxAmt = subtotal * (tax/100);
      payableAmt = +subtotal + +taxAmt;
      break;
    case 'Fixed Cost':
      subtotal = fixedCost;
      taxAmt = fixedCost * (tax/100)
      payableAmt = +fixedCost + +taxAmt;
      break;            
  }
  
  const invoiceID = 'IN' + Math.random().toString().substr(2, 9);
  var formattedDate = Utilities.formatDate(new Date(), "IST", "dd-MMM-yyyy");
  
  //Set the consequential data in the columns of the spreadsheet for record keeping
  //Note: These variable are dependent on the sheet's columns so if that changes, please update.
  const row = rg.getRow();
  
  const payableAmtCol = 2; //B
  const invoiceIDCol = 3; //C
  
  sh.getRange(row,payableAmtCol).setValue(payableAmt);
  sh.getRange(row,invoiceIDCol).setValue(invoiceID); 
  
  
  //Build a new invoice from the file
  //Folder and file IDs
  const invoiceFolderID = '<invoice-folder-id>';
  const invoiceFolder = DriveApp.getFolderById(invoiceFolderID);
  
  const templateFileID = '<template-id>';
  const newFilename = 'Invoice_' + invoiceID;
  
  //Make a copy of the template file
  const newInvoiceFileID = DriveApp.getFileById(templateFileID).makeCopy(newFilename, invoiceFolder).getId();;
  
  //Get the invoice body into a variable
  var document = DocumentApp.openById(newInvoiceFileID);
  var body = document.getBody();
  
  //Replace all the {{ }} text in the invoice body
  body.replaceText('{{Invoice num}}', invoiceID);
  body.replaceText('{{Date}}', formattedDate);
  body.replaceText('{{Client Name}}', cName);
  body.replaceText('{{Client Address}}', cAddress);
  body.replaceText('{{Client Mobile}}', cMobile);
  body.replaceText('{{Client Email}}', cEmail);
  body.replaceText('{{Services}}', services.split(', ').join('\n'));
  
  body.replaceText('{{Subtotal}}', subtotal);
  body.replaceText('{{Tax Value}}', taxAmt);
  body.replaceText('{{Total}}', payableAmt);
  
  //In the case of hourly rate payment type, let's add an additional message giving the rate and the man hours.
  if(paymentType.includes('Hourly Rate')){
     //It should look something like this on the invoice
     //Hourly Rate
     //Rate of Rs.1200/hour
     //Completed 50 man hours
     const message = paymentType + '\nRate of Rs.' + hourlyRate + '/hour\nCompleted ' + manHours + ' man hours';
     body.replaceText('{{Payment Type}}', message);
  } else {
    body.replaceText('{{Payment Type}}', paymentType);
  }
  
  document.saveAndClose();
  
    //send email with the file
  var attachment = DriveApp.getFileById(newInvoiceFileID);
    GmailApp.sendEmail(cEmail, '<subject>, 
                     '<body>', 
                     {attachments: [attachment.getAs(MimeType.PDF)]});
}

代码运行良好。现在我需要用户在 Google 表单上按“发送表单”后可以编辑其回复。所以我决定选中“回复者可以在提交后进行编辑”。然后我需要通过GmailApp 再次发送带有编辑字段的文档。所以我创建了一个新触发器:Edit(来自电子表格)。另一个触发器是Form submit

但是我有一个问题。 当用户编辑字段并再次按“发送表单”时,触发器“编辑”被激活,并出现以下错误:Failed to send email: no recipient

如果我转到电子表格回复,我可以看到已编辑的行(因为单元格有注释“回复的人已更新此值”),并且列邮件未编辑但仍抛出异常。

如果cEmail 从未被编辑,我们如何解决这个问题?

搜索

我可以找到一些有趣的答案:

他们似乎描述了当触发器“编辑”被激活时可以生成一个空白行。但是我不明白为什么会发生这种情况,以及我该如何解决它,因为电子表格响应会在新用户提交答案后自动编辑。

【问题讨论】:

    标签: google-apps-script google-sheets triggers gmail google-forms


    【解决方案1】:

    编辑表单响应时,表单提交事件对象属性 valuesnamedValues 仅包含已编辑问题的值。

    要修复错误Failed to send email: no recipient,请替换

     GmailApp.sendEmail(cEmail, '<subject>, 
                         '<body>', 
                         {attachments: [attachment.getAs(MimeType.PDF)]});
    

    通过

    const recipientIdx = 1; // This is the 0 based index of the column having the recipient email address
    const recipient = cEmail ? cEmail : e.range.getValues().flat()[recipientIdx]
     GmailApp.sendEmail(recipient , '<subject>', 
                         '<body>', 
                         {attachments: [attachment.getAs(MimeType.PDF)]});
    

    附:除了硬编码分配给recipientIdx 的值之外,您还可以使用一些代码根据列标题来获取它。

    注意:以上仅会防止问题中提到的错误。为了使脚本正常工作,您必须对所有字段应用相同的想法:使用 e.range.getValues().flat() 从电子表格中读取缺失值。

    相关

    【讨论】:

    • 哦,我明白了。这就说得通了。如果用户错误不在电子邮件字段中,您会对脚本进行什么更改以便它可以工作?例如移动字段。
    • 谢谢!至少它不会抛出异常。但是现在有些字段在用户编辑表单后没有“考虑”,因此它们不包含在最终的 PDF 中。在电子表格中,第 1 列是“时间戳”,第 2 列是“电子邮件地址”,第 3 列是“姓氏”,第 4 列是“名字”。我得到了名字,但姓氏为空。也许我们需要“使用一些代码根据列标题获取recipientIdx”?我们怎么能这样做?
    • 对不起,我不是指“某些字段”,而是 所有 未更改的字段,现在为空白。所以我只得到一个带有编辑字段的 PDF,而不是所有字段。电子表格中的用户编辑行很好,问题是我得到空白字段。
    • 类似的东西。为了使您的脚本高效,请使用 const values = e.range.getValues().flat() 然后 const cAddress = e.namedValues['Client Address'][0] ? e.namedValues['Client Address'][0] : values[2]; 之类的东西,假设 Cliente Address 值位于 C 列中。
    • 再次感谢。我有一个与保存变量(日期字段的日、月和年)有关的问题。你可以在这里看到这些问题和新问题:stackoverflow.com/q/66948874/11617040
    猜你喜欢
    • 2021-11-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-29
    • 1970-01-01
    • 2018-04-17
    相关资源
    最近更新 更多