使用 Zoho Forms 使用文档模板生成 PDF
Generation of PDF using Docs Template using Zoho Forms
我使用 Zoho 创建了表单,其响应 linked 到 Google Sheet。 googlesheet的link如下。用户提交表单后,数据会自动上传 google sheet。
https://docs.google.com/spreadsheets/d/1bE9RdYs9g3tdawbUzSUz2sG_SknlwfONA9_5MVztJu8/edit?usp=sharing
我在 google 文档中创建了一个模板来生成 PDF。 Template的link如下:
https://docs.google.com/document/d/1NAU9ZpxxIZko0fy8IltgbZ7Em5IMZKq-JRBge9umYHg/edit
表格link如下:
https://zfrmz.in/iqzvLxSYT6yPvyQwkevD
我已经写下了下面的脚本,以便在提交 Zoho 表单后生成 PDF。
function afterFormSubmit(e) {
const info = e.namedValues;
const pdfFile = createPDF(info);
const entryRow = e.range.getRow();
const ws = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet2")
ws.getRange(entryRow, 63).setValue(pdfFile.getUrl());
ws.getRange(entryRow, 64).setValue(pdfFile.getName());
sendEmail(e.namedValues ['Email address'][0],pdfFile);
}
function sendEmail(email,pdfFile){
GmailApp.sendEmail(email, "Daily Transation Summary Report", "Please find the attached DTR", {
attachments : [pdfFile],
name: 'My Company'
});
}
function createPDF(info) {
const pdfFolder = DriveApp.getFolderById("1ebglel6vwMGXByeG4wHBBvJu9u0zJeMJ");
const tempFolder = DriveApp.getFolderById("1ebglel6vwMGXByeG4wHBBvJu9u0zJeMJ");
const templateDoc = DriveApp.getFileById("1kyFimRMdHQZV85F5P6JyCHv1DQ3KJFv1hx5o7lIcNZo");
const newTempFile = templateDoc.makeCopy(tempFolder);
const openDoc = DocumentApp.openById(newTempFile.getId());
const body = openDoc.getBody();
body.replaceText("{Name of Person}", info['Name of Person'][0]);
body.replaceText("{Date of Report}", info['Date of Report'][0]);
body.replaceText("{Name of Outlet}", info['Name of Outlet'][0]);
body.replaceText("{Opening Petty Cash Balance}", info['Opening Petty Cash Balance'][0]);
body.replaceText("{Total Petty Cash Received}", info['Total Petty Cash Received'][0]);
body.replaceText("{Closing Petty Cash Balance}", info['Closing Petty Cash Balance'][0]);
body.replaceText("{Total Petty Cash Paid}", info['Total Petty Cash Paid'][0]);
body.replaceText("{Available Float Cash Balance}", info['Available Float Cash Balance'][0]);
body.replaceText("{Milk For Outlet}", info['Milk For Outlet'][0]);
body.replaceText("{Staff Milk/Toast etc.}", info['Staff Milk/Toast etc.'][0]);
body.replaceText("{Petrol Expenses}", info['Petrol Expenses'][0]);
body.replaceText("{Water Tank Charges}", info['Water Tank Charges'][0]);
body.replaceText("{Repair And Maintenance}", info['Repair And Maintenance'][0]);
body.replaceText("{Freight Paid}", info['Freight Paid'][0]);
body.replaceText("{Purchase of Soya Chaap}", info['Purchase of Soya Chaap'][0]);
body.replaceText("{Purchase of Rumali Roti}", info['Purchase of Rumali Roti'][0]);
body.replaceText("{Purchase of Egg Tray}", info['Purchase of Egg Tray'][0]);
body.replaceText("{Other Expenses}", info['Other Expenses'][0]);
body.replaceText("{Total Tip Amount (PAYTM & CARD)}", info['Total Tip Amount (PAYTM & CARD)'][0]);
body.replaceText("{Cash Sales - Outlet}", info['Cash Sales - Outlet'][0]);
body.replaceText("{Cash Sales - KCCO App}", info['Cash Sales - KCCO App'][0]);
body.replaceText("{Card Sales}", info['Card Sales'][0]);
body.replaceText("{Credit (Udhar) Sales}", info['Credit (Udhar) Sales'][0]);
body.replaceText("{Paytm Sales}", info['Paytm Sales'][0]);
body.replaceText("{KCCO App Sales Razor Pay}", info['KCCO App Sales Razor Pay'][0]);
body.replaceText("{Swiggy Sales}", info['Swiggy Sales'][0]);
body.replaceText("{Zomato Sales}", info['Zomato Sales'][0]);
body.replaceText("{Dunzo Sales}", info['Dunzo Sales'][0]);
body.replaceText("{Zomato Gold Sales}", info['Zomato Gold Sales'][0]);
body.replaceText("{Dine Out Sales}", info['Dine Out Sales'][0]);
body.replaceText("{Total Cancelled Bill Amount}", info['Total Cancelled Bill Amount'][0]);
body.replaceText("{Total Sales of the Day (Total of Above)}", info['Total Sales of the Day (Total of Above)'][0]);
body.replaceText("{Total Sales of the Day (As Per PetPooja)}", info['Total Sales of the Day (As Per PetPooja)'][0]);
body.replaceText("{Old Due Receipts - Cash}", info['Old Due Receipts - Cash'][0]);
body.replaceText("{Old Due Receipts - Other Modes}", info['Old Due Receipts - Other Modes'][0]);
body.replaceText("{Tip Receipts - Card}", info['Tip Receipts - Card'][0]);
body.replaceText("{Tip Receipts - Paytm}", info['Tip Receipts - Paytm'][0]);
body.replaceText("{Currency Note of INR 2000}", info['Currency Note of INR 2000'][0]);
body.replaceText("{Currency Note of INR 500}", info['Currency Note of INR 500'][0]);
body.replaceText("{ Currency Note of INR 200}", info[' Currency Note of INR 200'][0]);
body.replaceText("{Currency Note of INR 100}", info['Currency Note of INR 100'][0]);
body.replaceText("{Currency Note of INR 50}", info['Currency Note of INR 50'][0]);
body.replaceText("{Currency Note of INR 20}", info['Currency Note of INR 20'][0]);
body.replaceText("{Currency Note of INR 10}", info['Currency Note of INR 10'][0]);
body.replaceText("{Other Currency Note/Coins}", info['Other Currency Note/Coins'][0]);
body.replaceText("{Total Available Cash to Handover}", info['Total Available Cash to Handover'][0]);
body.replaceText("{S.No.//Name of Person// Mobile No// Credit Approval By// Bill No// Total Amount of Credit", info['S.No.//Name of Person// Mobile No// Credit Approval By// Bill No// Total Amount of Credit'][0]);
body.replaceText("{Total No. of Bills of Credit Recovery}", info['Total No. of Bills of Credit Recovery'][0]);
body.replaceText("{S.No.//Name of Person// Mobile No// Mode of Payment// Bill No// Total Amount Received}", info['S.No.//Name of Person// Mobile No// Mode of Payment// Bill No// Total Amount Received'][0]);
body.replaceText("{Total No. of Complementary Bills During the Day}", info['Total No. of Complementary Bills During the Day'][0]);
body.replaceText("{S.No.//Name of Person// Mobile No//Reason// Bill No//Amount Settled}", info['S.No.//Name of Person// Mobile No//Reason// Bill No//Amount Settled'][0]);
body.replaceText("{Total No. of Cancelled Bills During the Day}", info['Total No. of Cancelled Bills During the Day'][0]);
body.replaceText("{S.No.//Name of Person// Mobile No// Bill No// Bill Amount}", info['S.No.//Name of Person// Mobile No// Bill No// Bill Amount'][0]);
body.replaceText("{S.No.// Bill No// Mode of Payment//Tip Amount Received}", info['S.No.// Bill No// Mode of Payment//Tip Amount Received'][0]);
body.replaceText("{No. of Cylinders Received at Outlet}", info['No. of Cylinders Received at Outlet'][0]);
openDoc.saveAndClose();
const blobPDF = newTempFile.getAs(MimeType.PDF);
const pdfFile = pdfFolder.createFile(blobPDF).setName(info['Name of Outlet'][0]+ " " + info['Date of Report'][0]);
tempFolder.removeFile(newTempFile);
return pdfFile;
}
建议:
由于您使用的是将数据发送到 Google Sheet 文件的第三方表单(Zoho 表单),因此 afterFormSubmit
或 onFormSubmit
不幸的是在您的设置中不起作用。但是,正如所讨论的,您可能希望在 time-driven 触发器上检查下面这个经过调整的示例脚本作为解决方法:
In this workaround, I have added an identifier on Column CC
, where the code adds a value on last row of CC
column with "Done", indicating that the data has already been sent via email. This way, every time the time driven
trigger kicks in, it won't resend the same data all the time but only new last row that do not have "Done" yet on row CC
.
脚本 - 已更新
function sendEmail(email,pdfFile){
GmailApp.sendEmail(email, "Daily Transation Summary Report", "Please find the attached DTR", {
attachments : [pdfFile],
name: 'My Company'
});
}
function afterFormSubmit() { // Add this function to a time driven trigger
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("DTR - Final Response");
var lastRow = ss.getRange("A1:A").getValues().filter(String).length;
var emailCheck = ss.getRange("CC"+lastRow);
const info = ss.getRange("A"+lastRow+":CB"+lastRow).getValues(); //Get the last submitted row data from the DTR - Final Response.
const ws = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet2");
const entryRow = ws.getLastRow() + 1;
if(emailCheck.getValue()!= "Done"){ // checks if the last row with data on DTR - Final Response sheet on CC column has 'Done" value or not
const pdfFile = createPDF(info);
sendEmail(info[0][78],pdfFile);
emailCheck.setValue("Done"); //Set value on CC column of the data row that has been submitted via email with "Done" indication that it has already been sent via email
ws.getRange(entryRow, 63).setValue(pdfFile.getUrl());
ws.getRange(entryRow, 64).setValue(pdfFile.getName());
Logger.log("PDF sent to email: "+ info[0][78])
}else{
Logger.log("This data has already been sent with PDF name: "+info[0][3]+ " " + info[0][2])
return; //if current row data on CC column has "Done" value it means it was already send via email, so it will be ignored.
}
}
function createPDF(info) {
const pdfFolder = DriveApp.getFolderById("1lxR27p9zVQi416j3Lq2ZRsLGb0xcdQ1a");
const tempFolder = DriveApp.getFolderById("1VaIeCeN6RlqACSXg_MGT7BRa5cyPMpbN");
const templateDoc = DriveApp.getFileById("1Stvxgn76qxti40EeRzgS3UlQJ94Dh9oldYVhfCHUnMY");
const newTempFile = templateDoc.makeCopy(tempFolder);
const openDoc = DocumentApp.openById(newTempFile.getId());
const body = openDoc.getBody();
// made some test on my end to a few data
body.replaceText("{Name of Person}", info[0][4]);
body.replaceText("{Date of Report}", info[0][2]);
body.replaceText("{Outlet}", info[0][3]);
body.replaceText("{Opening Petty Cash Balance}", info[0][57]);
body.replaceText("{Total Petty Cash Received from H.O.}", info[0][24]);
body.replaceText("{Closing Petty Cash Balance}", info[0][28]);
// made some test on my end to a few data
openDoc.saveAndClose();
const blobPDF = newTempFile.getAs(MimeType.PDF);
const pdfFile = pdfFolder.createFile(blobPDF).setName(info[0][3]+ " " + info[0][2]);
tempFolder.removeFile(newTempFile);
return pdfFile;
}
快速测试结果
Sample last row data (the most recent submitted data from Zoho form) to be used on DTR - Final Response
sheet:
Added the afterFormSubmit
function to a time-driven trigger
(in my testing-it, will run every minute):
PDF was sent via email to my test email account added on the last row
Then the PDF URL and and PDF file name was posted on Sheet 2
columns 63 & 64 on the last row data.
Time-Driven trigger logs from the Apps Script Editor:
我使用 Zoho 创建了表单,其响应 linked 到 Google Sheet。 googlesheet的link如下。用户提交表单后,数据会自动上传 google sheet。
https://docs.google.com/spreadsheets/d/1bE9RdYs9g3tdawbUzSUz2sG_SknlwfONA9_5MVztJu8/edit?usp=sharing
我在 google 文档中创建了一个模板来生成 PDF。 Template的link如下:
https://docs.google.com/document/d/1NAU9ZpxxIZko0fy8IltgbZ7Em5IMZKq-JRBge9umYHg/edit
表格link如下:
https://zfrmz.in/iqzvLxSYT6yPvyQwkevD
我已经写下了下面的脚本,以便在提交 Zoho 表单后生成 PDF。
function afterFormSubmit(e) {
const info = e.namedValues;
const pdfFile = createPDF(info);
const entryRow = e.range.getRow();
const ws = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet2")
ws.getRange(entryRow, 63).setValue(pdfFile.getUrl());
ws.getRange(entryRow, 64).setValue(pdfFile.getName());
sendEmail(e.namedValues ['Email address'][0],pdfFile);
}
function sendEmail(email,pdfFile){
GmailApp.sendEmail(email, "Daily Transation Summary Report", "Please find the attached DTR", {
attachments : [pdfFile],
name: 'My Company'
});
}
function createPDF(info) {
const pdfFolder = DriveApp.getFolderById("1ebglel6vwMGXByeG4wHBBvJu9u0zJeMJ");
const tempFolder = DriveApp.getFolderById("1ebglel6vwMGXByeG4wHBBvJu9u0zJeMJ");
const templateDoc = DriveApp.getFileById("1kyFimRMdHQZV85F5P6JyCHv1DQ3KJFv1hx5o7lIcNZo");
const newTempFile = templateDoc.makeCopy(tempFolder);
const openDoc = DocumentApp.openById(newTempFile.getId());
const body = openDoc.getBody();
body.replaceText("{Name of Person}", info['Name of Person'][0]);
body.replaceText("{Date of Report}", info['Date of Report'][0]);
body.replaceText("{Name of Outlet}", info['Name of Outlet'][0]);
body.replaceText("{Opening Petty Cash Balance}", info['Opening Petty Cash Balance'][0]);
body.replaceText("{Total Petty Cash Received}", info['Total Petty Cash Received'][0]);
body.replaceText("{Closing Petty Cash Balance}", info['Closing Petty Cash Balance'][0]);
body.replaceText("{Total Petty Cash Paid}", info['Total Petty Cash Paid'][0]);
body.replaceText("{Available Float Cash Balance}", info['Available Float Cash Balance'][0]);
body.replaceText("{Milk For Outlet}", info['Milk For Outlet'][0]);
body.replaceText("{Staff Milk/Toast etc.}", info['Staff Milk/Toast etc.'][0]);
body.replaceText("{Petrol Expenses}", info['Petrol Expenses'][0]);
body.replaceText("{Water Tank Charges}", info['Water Tank Charges'][0]);
body.replaceText("{Repair And Maintenance}", info['Repair And Maintenance'][0]);
body.replaceText("{Freight Paid}", info['Freight Paid'][0]);
body.replaceText("{Purchase of Soya Chaap}", info['Purchase of Soya Chaap'][0]);
body.replaceText("{Purchase of Rumali Roti}", info['Purchase of Rumali Roti'][0]);
body.replaceText("{Purchase of Egg Tray}", info['Purchase of Egg Tray'][0]);
body.replaceText("{Other Expenses}", info['Other Expenses'][0]);
body.replaceText("{Total Tip Amount (PAYTM & CARD)}", info['Total Tip Amount (PAYTM & CARD)'][0]);
body.replaceText("{Cash Sales - Outlet}", info['Cash Sales - Outlet'][0]);
body.replaceText("{Cash Sales - KCCO App}", info['Cash Sales - KCCO App'][0]);
body.replaceText("{Card Sales}", info['Card Sales'][0]);
body.replaceText("{Credit (Udhar) Sales}", info['Credit (Udhar) Sales'][0]);
body.replaceText("{Paytm Sales}", info['Paytm Sales'][0]);
body.replaceText("{KCCO App Sales Razor Pay}", info['KCCO App Sales Razor Pay'][0]);
body.replaceText("{Swiggy Sales}", info['Swiggy Sales'][0]);
body.replaceText("{Zomato Sales}", info['Zomato Sales'][0]);
body.replaceText("{Dunzo Sales}", info['Dunzo Sales'][0]);
body.replaceText("{Zomato Gold Sales}", info['Zomato Gold Sales'][0]);
body.replaceText("{Dine Out Sales}", info['Dine Out Sales'][0]);
body.replaceText("{Total Cancelled Bill Amount}", info['Total Cancelled Bill Amount'][0]);
body.replaceText("{Total Sales of the Day (Total of Above)}", info['Total Sales of the Day (Total of Above)'][0]);
body.replaceText("{Total Sales of the Day (As Per PetPooja)}", info['Total Sales of the Day (As Per PetPooja)'][0]);
body.replaceText("{Old Due Receipts - Cash}", info['Old Due Receipts - Cash'][0]);
body.replaceText("{Old Due Receipts - Other Modes}", info['Old Due Receipts - Other Modes'][0]);
body.replaceText("{Tip Receipts - Card}", info['Tip Receipts - Card'][0]);
body.replaceText("{Tip Receipts - Paytm}", info['Tip Receipts - Paytm'][0]);
body.replaceText("{Currency Note of INR 2000}", info['Currency Note of INR 2000'][0]);
body.replaceText("{Currency Note of INR 500}", info['Currency Note of INR 500'][0]);
body.replaceText("{ Currency Note of INR 200}", info[' Currency Note of INR 200'][0]);
body.replaceText("{Currency Note of INR 100}", info['Currency Note of INR 100'][0]);
body.replaceText("{Currency Note of INR 50}", info['Currency Note of INR 50'][0]);
body.replaceText("{Currency Note of INR 20}", info['Currency Note of INR 20'][0]);
body.replaceText("{Currency Note of INR 10}", info['Currency Note of INR 10'][0]);
body.replaceText("{Other Currency Note/Coins}", info['Other Currency Note/Coins'][0]);
body.replaceText("{Total Available Cash to Handover}", info['Total Available Cash to Handover'][0]);
body.replaceText("{S.No.//Name of Person// Mobile No// Credit Approval By// Bill No// Total Amount of Credit", info['S.No.//Name of Person// Mobile No// Credit Approval By// Bill No// Total Amount of Credit'][0]);
body.replaceText("{Total No. of Bills of Credit Recovery}", info['Total No. of Bills of Credit Recovery'][0]);
body.replaceText("{S.No.//Name of Person// Mobile No// Mode of Payment// Bill No// Total Amount Received}", info['S.No.//Name of Person// Mobile No// Mode of Payment// Bill No// Total Amount Received'][0]);
body.replaceText("{Total No. of Complementary Bills During the Day}", info['Total No. of Complementary Bills During the Day'][0]);
body.replaceText("{S.No.//Name of Person// Mobile No//Reason// Bill No//Amount Settled}", info['S.No.//Name of Person// Mobile No//Reason// Bill No//Amount Settled'][0]);
body.replaceText("{Total No. of Cancelled Bills During the Day}", info['Total No. of Cancelled Bills During the Day'][0]);
body.replaceText("{S.No.//Name of Person// Mobile No// Bill No// Bill Amount}", info['S.No.//Name of Person// Mobile No// Bill No// Bill Amount'][0]);
body.replaceText("{S.No.// Bill No// Mode of Payment//Tip Amount Received}", info['S.No.// Bill No// Mode of Payment//Tip Amount Received'][0]);
body.replaceText("{No. of Cylinders Received at Outlet}", info['No. of Cylinders Received at Outlet'][0]);
openDoc.saveAndClose();
const blobPDF = newTempFile.getAs(MimeType.PDF);
const pdfFile = pdfFolder.createFile(blobPDF).setName(info['Name of Outlet'][0]+ " " + info['Date of Report'][0]);
tempFolder.removeFile(newTempFile);
return pdfFile;
}
建议:
由于您使用的是将数据发送到 Google Sheet 文件的第三方表单(Zoho 表单),因此 afterFormSubmit
或 onFormSubmit
不幸的是在您的设置中不起作用。但是,正如所讨论的,您可能希望在 time-driven 触发器上检查下面这个经过调整的示例脚本作为解决方法:
In this workaround, I have added an identifier on Column
CC
, where the code adds a value on last row ofCC
column with "Done", indicating that the data has already been sent via email. This way, every time thetime driven
trigger kicks in, it won't resend the same data all the time but only new last row that do not have "Done" yet on rowCC
.
脚本 - 已更新
function sendEmail(email,pdfFile){
GmailApp.sendEmail(email, "Daily Transation Summary Report", "Please find the attached DTR", {
attachments : [pdfFile],
name: 'My Company'
});
}
function afterFormSubmit() { // Add this function to a time driven trigger
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("DTR - Final Response");
var lastRow = ss.getRange("A1:A").getValues().filter(String).length;
var emailCheck = ss.getRange("CC"+lastRow);
const info = ss.getRange("A"+lastRow+":CB"+lastRow).getValues(); //Get the last submitted row data from the DTR - Final Response.
const ws = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet2");
const entryRow = ws.getLastRow() + 1;
if(emailCheck.getValue()!= "Done"){ // checks if the last row with data on DTR - Final Response sheet on CC column has 'Done" value or not
const pdfFile = createPDF(info);
sendEmail(info[0][78],pdfFile);
emailCheck.setValue("Done"); //Set value on CC column of the data row that has been submitted via email with "Done" indication that it has already been sent via email
ws.getRange(entryRow, 63).setValue(pdfFile.getUrl());
ws.getRange(entryRow, 64).setValue(pdfFile.getName());
Logger.log("PDF sent to email: "+ info[0][78])
}else{
Logger.log("This data has already been sent with PDF name: "+info[0][3]+ " " + info[0][2])
return; //if current row data on CC column has "Done" value it means it was already send via email, so it will be ignored.
}
}
function createPDF(info) {
const pdfFolder = DriveApp.getFolderById("1lxR27p9zVQi416j3Lq2ZRsLGb0xcdQ1a");
const tempFolder = DriveApp.getFolderById("1VaIeCeN6RlqACSXg_MGT7BRa5cyPMpbN");
const templateDoc = DriveApp.getFileById("1Stvxgn76qxti40EeRzgS3UlQJ94Dh9oldYVhfCHUnMY");
const newTempFile = templateDoc.makeCopy(tempFolder);
const openDoc = DocumentApp.openById(newTempFile.getId());
const body = openDoc.getBody();
// made some test on my end to a few data
body.replaceText("{Name of Person}", info[0][4]);
body.replaceText("{Date of Report}", info[0][2]);
body.replaceText("{Outlet}", info[0][3]);
body.replaceText("{Opening Petty Cash Balance}", info[0][57]);
body.replaceText("{Total Petty Cash Received from H.O.}", info[0][24]);
body.replaceText("{Closing Petty Cash Balance}", info[0][28]);
// made some test on my end to a few data
openDoc.saveAndClose();
const blobPDF = newTempFile.getAs(MimeType.PDF);
const pdfFile = pdfFolder.createFile(blobPDF).setName(info[0][3]+ " " + info[0][2]);
tempFolder.removeFile(newTempFile);
return pdfFile;
}
快速测试结果
Sample last row data (the most recent submitted data from Zoho form) to be used on
DTR - Final Response
sheet:
Added the
afterFormSubmit
function to atime-driven trigger
(in my testing-it, will run every minute):
PDF was sent via email to my test email account added on the last row
Then the PDF URL and and PDF file name was posted on
Sheet 2
columns 63 & 64 on the last row data.
Time-Driven trigger logs from the Apps Script Editor: