суббота, 11 ноября 2017 г.

Генерируем документы в google из шаблона

Порой возникает необходимость заполнения документов данными клиента. Иногда этих документов бывает слишком много. Да и заполнение представляет собой, чаще всего, простую подстановку ФИО в нужном месте документа. 
Оказывается процесс заполнения документов можно автоматизировать и мало того можно поручить это самому клиенту. 
Представим себе картину - клиент требует договор и счёт на оплату. А мы ему ссылку на google форму где ему необходимо заполнить данные, которые необходимы (включая адрес электронной почты). Скрипт, запущенный в таблицах с ответами, 'анализирует' полученные ответы, в заранее подготовленный шаблон, вставляет необходимые данные, шаблон преобразует в pdf и отправляет полученный файл на электронную почту клиента и на любой другой. И клиент доволен, и нам время не надо тратить на оформление документов. Шик, блеск, красота.

Дело за малым - остаётся создать форму с таблицей, создать шаблон и написать скрипт :) .
Попробуем создать генератор справок об оформлении подписки на мой блог.

1. Создаём форму, в которой нам будет интересны только Фамилия Имя Отчество и e-mail клиента.

Ссылка

2. Создаём таблицу с ответами


добавляем на странице с ответами два служебных столбца - "id" и "отправлено?"


3. Создаём шаблон справки в google docs

Ссылка

Здесь DATA, NUMBER, ФИО_КЛИЕНТА - служебные поля, которые будут заменяться на данные клиента.

4. Открываем таблицу и пишем скрипт

function spravka() {

  var ss=SpreadsheetApp.getActive();

  var sheet=ss.getSheets()[0];

  var lastR=sheet.getLastRow();

  var clientData=sheet.getRange(lastR,1,1,3).getValues();

  var data=new Date(clientData[0][0]);

  var day=data.getDate(),month=data.getMonth()+1,year=data.getFullYear();

  if (month<10){

    month="0"+month;

  }

  var realdata=day+'.'+month+'.'+year;

  var num=lastR-1;

  var fio=clientData[0][2];

  var mail=clientData[0][1];

  var dirTemp=DriveApp.getFolderById('ID вашей директории для временных файлов')

  var template=DriveApp.getFileById('ID вашего файла-шаблона').makeCopy('Справка',dirTemp).getId();

  var docbody=DocumentApp.openById(template).getBody();

  docbody.replaceText('DATA', realdata);

  docbody.replaceText('NUMBER', num);

  docbody.replaceText('ФИО_КЛИЕНТ', fio);

  sheet.getRange(lastR, 4).setValue(template)

 }

function getPdf(){

  var ss=SpreadsheetApp.getActive();

  var sheet=ss.getSheets()[0];

  var dataRange=sheet.getDataRange().getValues();

  for (var i=1;i<dataRange.length;i++){

    if (dataRange[i][4]==""){

      var filepdf=DriveApp.getFileById(dataRange[i][3]).getAs('application/pdf');

      GmailApp.sendEmail(dataRange[i][1], 'Справка готова', 'Смотрите вложенный файл', {

     attachments: [filepdf],

     name: 'Блог "Изучаю google script"'

 });

      sheet.getRange(i+1, 5).setValue('☑')

      DriveApp.getFileById(dataRange[i][3]).setTrashed(true);

    }

  }

}
Получилось 2 функции: spravka - собственно она и генерирует google документ из шаблона, и getPdf - преобразует google doc в pdf и отправляет на почту заполнившего человека. На функцию spravka ставим триггер на отправку формы, на getPdf ставим триггер на каждую минуту.

Почему функций 2? Какой-то глюк google - пока выполняется spravka, генерация pdf документа не происходит так как надо (не происходит замены полей в документе на данные клиента). Вторая функция обходит ответы, генерирует pdf и отправляет на адрес пользователя, заполнившего форму.


Что сейчас необходимо чтобы получить справку на Ваше имя? Заполнить форму, указав Ваш реальный e-mail.

Буду рад ответить на вопросы. 

94 комментария:

  1. Здесь DATA, NUMBER, ФИО_КЛИЕНТА - служебные поля, которые будут заменяться на данные клиента.

    Как это реализиовать?

    ОтветитьУдалить
    Ответы
    1. Здравствуйте.
      Не совсем понятен вопрос. В скрипте уже всё реализовано.

      Удалить
  2. Здравствуйте!
    Использовал ваш урок для попытки создания аналогичного по функционалу решения. Однако у меня не отправляется email, хотя и появляется файл во временной папке, а так же вижу в настройках триггеров выполнение по времени. Не подскажете в чем может быть проблема и куда смотреть.

    ОтветитьУдалить
    Ответы
    1. Здравствуйте.
      Для отладки обычно запускаю функцию скрипта вручную, если есть ошибки то об этом будет сообщено.
      Пробуйте, у Вас всё получится.

      Удалить
    2. Еще появился вопросик. Как сделать так, что бы файлу присваивалось имя с учетом его NUMBER. Допустим Справка 1, Справка 2 и т.д. Для того чтобы файл не удалялся я так понимаю достаточно удалить строчку DriveApp.getFileById(dataRange[i][3]).setTrashed(true); И в догонку по поводу отправки файла. Можно ли его отправить не конвертируя в pdf?

      Удалить
    3. Здравствуйте.
      1. в строке var template=DriveApp.getFileById('ID вашего файла-шаблона').makeCopy('Справка',dirTemp).getId()
      'Справка'+num
      2. Да
      3. Отправлять ссылку или doc? Если ссылка, то в письмо нужно добавить файл.getUrl(), могут появится сложности с доступом к файлу.

      Удалить
    4. Большое спасибо за оперативную помощь!!!
      По поводу отправки меня интересует отправка в формате googledocs.

      Удалить
    5. Тогда можно не городить огород и формировать документ одной функцией, отправляя на e-mail только ссылку.
      В начале, конечно, озаботиться чтобы доступ к каталогу, в который копируется файл, был открыт по ссылке.

      Удалить
  3. Добрый день, Роман!
    А вы могли бы настроить под наши нужды ваш скрипт. Все аналогично, только полей больше.
    Мы готовы заплатить за настройку. Мой скайп: live:9d4046936d84fc24

    ОтветитьУдалить
  4. Добрый день. У меня создается файл в папке, но при попытке получить его функцией openByID пишет ошибку "Не удалось завершить выполнение за 33,368 сек. Документ недоступен. Повторите попытку позже". В чем может быть причина. Файл с таким ID в папке есть.

    ОтветитьУдалить
    Ответы
    1. Доброго дня.
      Даже не представляю какая у Вас может возникать ошибка. Хорошо бы посмотреть на пример таблицы со скриптом.

      Удалить
    2. https://docs.google.com/spreadsheets/d/1AFrJOaQlFX_kDbLEquXZCZjZQ_fa89dZe37UeLEP3Lo/edit?usp=sharing

      function MakeOrder() {

      var ss=SpreadsheetApp.getActive();
      var sheet=ss.getSheets()[0];
      var lastR=sheet.getLastRow();
      var ID_ORDER_TEMPLATE = '1jsa0Gai8OV5X3TLfrn5QNXF89deuex2B';
      var ID_TEMP_DIR = '1Cusi5_w7Qh7Ag8yCjIVPvoXwFYCoyOuK';

      var clientData=sheet.getRange(lastR,1,1,14).getValues();

      var FIO = clientData[0][1];
      var DOG_NAME = clientData[0][2];
      var BREED = clientData[0][3];
      var SEX = clientData[0][4];
      var BDATE = clientData[0][5];
      var PEDIGREE = clientData[0][6];
      var TATOO = clientData[0][7];
      var LICENCE = clientData[0][8];
      var PHONE = clientData[0][9];
      var EMAIL = clientData[0][10];

      var num=lastR-1;

      var dirTemp=DriveApp.getFolderById(ID_TEMP_DIR);

      var template=DriveApp.getFileById(ID_ORDER_TEMPLATE).makeCopy('Заявка '+DOG_NAME,dirTemp).getId();
      var docbody=doc.DocumentApp.openById(template).getBody();

      docbody.replaceText('FIO', FIO);
      docbody.replaceText('DOG_NAME', DOG_NAME);
      docbody.replaceText('BREED', BREED);
      docbody.replaceText('SEX', SEX);
      docbody.replaceText('BDATE', BDATE);
      docbody.replaceText('PEDIGREE', PEDIGREE);
      docbody.replaceText('TATOO', TATOO);
      docbody.replaceText('LICENCE', LICENCE);
      docbody.replaceText('PHONE', PHONE);
      docbody.replaceText('EMAIL', EMAIL);
      sheet.getRange(lastR, 14).setValue(template)

      }

      Удалить
    3. там небольшая ошибка.
      var doc=DocumentApp.openById(template);
      var docbody=doc.openById(template).getBody();

      Удалить
    4. Разобрался. Документ был типа .docx, а не гугл документ.

      Удалить
    5. ссылку не забудьте закрыть от любопытных глаз.

      Удалить
  5. Здравствуйте!!!
    Подскажите пожалуйста, что я делаю не так?

    https://drive.google.com/open?id=1mdzEOfJx0SKWE3HYxt1KeGwvkMO0dWLU2ngmipJCI4s


    function spravka() {

    var ss=SpreadsheetApp.getActive();

    var sheet=ss.getSheets()[0];

    var lastR=sheet.getLastRow();

    var clientData=sheet.getRange(lastR,1,1,15).getValues();

    var data=new Date(clientData[0][0]);

    var day=data.getDate(),month=data.getMonth()+1,year=data.getFullYear();

    if (month<10){

    month="0"+month;

    }

    var realdata=day+'.'+month+'.'+year;

    var num=lastR-1;

    var pos=clientData[0][2];

    var naz=clientData[0][3];

    var fio=clientData[0][1];

    var inn=clientData[0][4];

    var org=clientData[0][5];

    var sni=clientData[0][6];

    var pasp=clientData[0][7];

    var okv=clientData[0][8];

    var adu=clientData[0][9];

    var adp=clientData[1][0];

    var adpp=clientData[1][1];

    var sob=clientData[1][2];

    var pho=clientData[1][3];

    var mail=clientData[1][4];

    var vid=clientData[1][5];


    var dirTemp=DriveApp.getFolderById('1rVHL756s88HHLkMK3fXkrDD-LMCsmYS6')

    var template=DriveApp.getFileById('12_9i7EEODiUPetQNGn0e_WGJr7GOBmMNa1HjoCbzE2s').makeCopy('Заявление',dirTemp).getId();

    var docbody=DocumentApp.openById(template).getBody();

    docbody.replaceText('DATA', realdata);

    docbody.replaceText('NUMBER', num);

    docbody.replaceText('ФИО_КЛИЕНТ', fio);

    docbody.replaceText('ИНН_', inn);

    docbody.replaceText('ОГРН_', org);

    docbody.replaceText('ОКВЭД_', okv);

    docbody.replaceText('Адрес_', adu);

    docbody.replaceText('Адрес_П', adp);

    docbody.replaceText('Должность_', pos);

    docbody.replaceText('Телефон_', pho);

    docbody.replaceText('ПОТЧА_', mail);

    docbody.replaceText('ВИД_', vid);

    docbody.replaceText('ПАСПОРТ_', pasp);

    docbody.replaceText('СОБСТВЕННОСТЬ_', sob);

    docbody.replaceText('НАЗВАНИЕ_', naz);

    docbody.replaceText('СНИЛС_', sni);

    docbody.replaceText('Адрес_П2', adpp);


    sheet.getRange(lastR, 17).setValue(template)

    }

    function getPdf(){

    var ss=SpreadsheetApp.getActive();

    var sheet=ss.getSheets()[0];

    var dataRange=sheet.getDataRange().getValues();

    for (var i=1;i<dataRange.length;i++){

    if (dataRange[i][17]==""){

    var filepdf=DriveApp.getFileById(dataRange[i][14]).getAs('application/pdf');

    GmailApp.sendEmail(dataRange[i][14], 'Заявление готово', 'Смотрите вложенный файл', {

    attachments: [filepdf],

    name: 'Заявление"'

    });

    sheet.getRange(i+1, 17).setValue('☑')

    DriveApp.getFileById(dataRange[i][16]).setTrashed(true);

    }

    }

    }

    ОтветитьУдалить
    Ответы
    1. var adu=clientData[0][9];

      var adp=clientData[0][10];

      var adpp=clientData[0][11];

      var sob=clientData[0][12];

      И т.д.

      Удалить
  6. Не решили проблему с тем что есть разделение на 2 функции? Пытаюсь это об ьединить в одну функцию применяя utilities.sleep(). Но все равно отправляется письмо с незамеченными данными в документе.

    ОтветитьУдалить
    Ответы
    1. И Вам здравствуйте.
      ищите метод saveAndClose()
      https://developers.google.com/apps-script/reference/document/document#saveAndClose()

      Удалить
    2. Спасибо! Все получилось отлично. Теперь могу создавать кучу таких скриптов!

      Удалить
  7. Здравствуйте!
    Подскажите пожалуйста, скопировал скрипт прописал id. Данные с формы приходят а с крипт не срабатывает.

    function spravka() {

    var ss=SpreadsheetApp.getActive();

    var sheet=ss.getSheets()[0];

    var lastR=sheet.getLastRow();

    var clientData=sheet.getRange(lastR,1,1,3).getValues();

    var data=new Date(clientData[0][0]);

    var day=data.getDate(),month=data.getMonth()+1,year=data.getFullYear();

    if (month<10){

    month="0"+month;

    }

    var realdata=day+'.'+month+'.'+year;

    var num=lastR-1;

    var fio=clientData[0][2];

    var mail=clientData[0][1];

    var dirTemp=DriveApp.getFolderById('1lQ_yxB2-0geID9eP6SEJNs45rdRRh31z')

    var template=DriveApp.getFileById('1bgnklduXFtLufifjT0Gq6WHjKeZJhPwI6JNCB81Mb7c').makeCopy('Справка'+num,dirTemp).getId();

    var docbody=DocumentApp.openById(template).getBody();

    docbody.replaceText('DATA', realdata);

    docbody.replaceText('NUMBER', num);

    docbody.replaceText('ФИО_КЛИЕНТ', fio);

    sheet.getRange(lastR, 4).setValue(template)

    }

    function getPdf(){

    var ss=SpreadsheetApp.getActive();

    var sheet=ss.getSheets()[0];

    var dataRange=sheet.getDataRange().getValues();

    for (var i=1;i<dataRange.length;i++){

    if (dataRange[i][4]==""){

    var filepdf=DriveApp.getFileById(dataRange[i][1]).getAs('application/pdf');

    GmailApp.sendEmail(dataRange[i][1], 'Ваш документ', 'Смотрите вложенный файл', {

    attachments: [filepdf],

    name: 'Спасибо'

    });

    sheet.getRange(i+1, 5).setValue('☑')

    DriveApp.getFileById(dataRange[i][3]).setTrashed(true);

    }

    }

    }

    ОтветитьУдалить
    Ответы
    1. Доброго.
      Прочитайте статью ещё раз и настройте триггеры на получение ответов из формы, и ежеминутной триггер.

      Удалить
    2. Спасибо, настроил триггеры скрипт срабатывает, файл создается но pdf не отправляется.
      Пишет:Элемент с заданным кодом не найден или у вас нет прав доступа к нему. (строка 57, файл Код)
      var filepdf=DriveApp.getFileById(dataRange[i][1]).getAs('application/pdf');
      Можете подсказать что делать?

      Удалить
    3. Проверить, в первую очередь, чтобы в принимающем ответы листе не было пустых строчек до последней заполненной.

      Проверить чтобы в dataRange[i][n] вместо n стоял номер столбца в котором генерируется id файла -1.
      Сейчас у Вас столбец номер 2, т.е. B.

      Удалить
  8. Ура! Спасибо вам Роман! Сработало, в dataRenge не тот столбец был.

    ОтветитьУдалить
  9. А где брать id директории моих файлов????И как создать эту директорию

    ОтветитьУдалить
  10. Добрый вечер.У меня в таблице в полях id и отправлено ничего не выводится.Что делать.

    ОтветитьУдалить
  11. И еще пдф файл не отсылается.

    ОтветитьУдалить
    Ответы
    1. Доброго.
      Прочитать статью ещё раз, обратив пристальное внимание на строку

      На функцию spravka ставим триггер на отправку формы, на getPdf ставим триггер на каждую минуту.

      Скорее всего, Вы забыли именно про триггеры.

      Удалить
  12. Ответы
    1. Тогда смотреть в выполнениях какую ошибку Вам выводит.

      Удалить
    2. Никаких ошибок не выводит.

      Удалить
    3. На кофейной гуще не гадаю.)
      Доступ к таблице предоставьте, будет время - посмотрю.

      Удалить
  13. Здравствуйте.
    Скажите, пожалуйста, а если надо сгенерить много справок одновременно? Как сделать так, чтобы скрипт генерил документы для выбранных строк (номеров строк)? Спасибо.

    ОтветитьУдалить
    Ответы
    1. Здравствуйте.
      По идее скрипт состоит из двух частей и вполне можно заставить его перебирать строки таблицы в цикле.
      lastR указывает на последнюю строку, а можно указать, вместо getLastRow(), номер нужной строки.

      Удалить
    2. Спасибо, буду пробовать написать цикл

      Удалить
  14. Роман, большое спасибо за кейс. Один вопрос - писали о триггере на 1 минуту, а по факту письмо получил через 5 минут. Что скажете по этому поводу?

    ОтветитьУдалить
    Ответы
    1. Здравствуйте.
      Возможно, агент ФСБ, проверяющий вашу почту вышел покурить.)
      А если серьёзно, возможны задержки во времени при отсылке почты. И это не секрет. Почта не является средством отправки мгновенных сообщений.

      Удалить
  15. sheet.getRange(i+1, 5).setValue('☑')
    Это последняя заполненная строка +1 (следующая строка?)
    Почему айди документа тогда берется из предпоследней?

    ОтветитьУдалить
    Ответы
    1. И вам доброго времени суток.
      Не понятен Ваш вопрос.
      В скрипте берутся все строки, но письмо отправляется если в столбце 5 пусто.
      Массивы в js нумеруются с ноля, в таблицах строки с номера 1, поэтому +1.

      Удалить
  16. Приветствую! Спасибо за статью, использовал ваш пример для формирования анкеты из шаблона текстового документа.
    Подскажите, каким скриптом можно копировать последнюю созданную строку после отправки формы, в другую таблицу и вставлять в следующую пустую после последней строку? по сути нужно создать копию файла с ответами, но в эту копию данные будут заноситься ещё и вручную.

    ОтветитьУдалить
    Ответы
    1. Доброго.
      смотрите по направлению onFormSubmit(), openById(), setValues()
      https://developers.google.com/apps-script/reference/spreadsheet/spreadsheet-app

      Удалить
    2. Вот так в итоге получилось выполнить задачу, выполняется при отправке формы. Спасибо ещё раз за статью

      function sync() {

      var ss=SpreadsheetApp.getActive();

      var sheet=ss.getSheets()[0];

      var lastR=sheet.getLastRow();

      var rowValues=sheet.getRange(lastR,1,1,23).getValues();

      Logger.log(rowValues);

      var destValues=[];
      destValues.push(rowValues[0][0]);
      destValues.push(rowValues[0][1]);
      destValues.push(rowValues[0][2]);
      destValues.push(rowValues[0][3]);
      destValues.push(rowValues[0][4]);
      destValues.push(rowValues[0][5]);
      destValues.push(rowValues[0][6]);
      destValues.push(rowValues[0][7]);
      destValues.push(rowValues[0][8]);
      destValues.push(rowValues[0][9]);
      destValues.push(rowValues[0][10]);
      destValues.push(rowValues[0][11]);
      destValues.push(rowValues[0][12]);
      destValues.push(rowValues[0][13]);
      destValues.push(rowValues[0][14]);
      destValues.push(rowValues[0][15]);
      destValues.push(rowValues[0][16]);
      destValues.push(rowValues[0][17]);
      destValues.push(rowValues[0][18]);
      destValues.push(rowValues[0][19]);



      var dest=SpreadsheetApp.openById('id').getSheetByName("Лист1");

      dest.getRange(dest.getLastRow()+1,1,1,20).setValues([destValues])
      }

      Удалить
    3. Такая простыня здесь совершенно ни к чему. Достаточно -

      function sync() {

      var ss=SpreadsheetApp.getActive();

      var sheet=ss.getSheets()[0];

      var lastR=sheet.getLastRow();

      var rowValues=sheet.getRange(lastR,1,1,20).getValues();

      var dest=SpreadsheetApp.openById('id').getSheetByName("Лист1");

      dest.getRange(dest.getLastRow()+1,1,1,20).setValues(rowValues)
      }

      Удалить
    4. Действительно) Спасибо, я в этом направлении и шел, но что то не сработало и стал искать примеры в сети, это меня с толку и сбило. Хотя сейчас вижу что на самом деле все логично и просто)

      Удалить
  17. Добрый день! Роман!

    Решил попробывать ваш скрипт, сделал все как в инструкции. Установил триггеры. Но столкнулся со следующей ошибкой: Exception: При вызове метода или свойства getFileById для объекта "DriveApp" произошла ошибка.

    Сам файл во временной папке появляется. Столбец указан правильно в getFileById(dataRange[i][3]). Уже пол дня сижу, никак не могу решить эту проблему. Можете, что нибудь посоветовать пожалуйста?

    Заранее спасибо

    ОтветитьУдалить
    Ответы
    1. Доброго дня.Обратите внимание на наличие пустых строчек.

      Удалить
  18. Требуется консультация на платной основе по настройке Вашего скрипта. Моя телега - https://t.me/mismas_25

    ОтветитьУдалить
  19. Добрый день, а подскажите если не сложно. Скрипт есть, триггеры настроены, в папке документ появляется но: 1. вместо данные - NaNaNA (я так понимаю строки пустые в таблице), 2. не присваивается id в таблице, функции работают без ошибок (проверяла вручную, запускала отладчик). 3. на почтовый адрес не отправляется пдф (хотя в папке он есть). (((
    Может я чего со столбцами напутала(((
    (метка времени(1), имя(2), адрес эл почты(3), комментарий(4), id(5), отправлено(6))


    function spravka() {

    var ss=SpreadsheetApp.getActive();

    var sheet=ss.getSheets()[0];

    var lastR=sheet.getLastRow();

    var clientData=sheet.getRange(lastR,1,1,6).getValues();

    var data=new Date(clientData[0][0]);

    var day=data.getDate(),month=data.getMonth()+1,year=data.getFullYear();

    if (month<10){

    month="0"+month;

    }

    var realdata=day+'.'+month+'.'+year;

    var num=lastR-1;
    var ima=clientData[0][1];
    var mail=clientData[0][2];
    var comm=clientData[0][3];

    var dirTemp=DriveApp.getFolderById('id папки на гугл диске')

    var template=DriveApp.getFileById('id файла шаблона в папке на гугл диске').makeCopy('Справка'+num,dirTemp).getId();

    var docbody=DocumentApp.openById(template).getBody();

    docbody.replaceText('DATA', realdata);

    docbody.replaceText('NUMBER', num);

    docbody.replaceText('ИМЯ', ima);

    docbody.replaceText('КОММЕНТАРИЙ_', comm);

    sheet.getRange(lastR, 6).setValue(template)

    }

    function getPdf(){

    var ss=SpreadsheetApp.getActive();

    var sheet=ss.getSheets()[0];

    var dataRange=sheet.getDataRange().getValues();

    for (var i=1;i<dataRange.length;i++){

    if (dataRange[i][8]==""){

    var filepdf=DriveApp.getFileById(dataRange[i][2]).getAs('application/pdf');

    GmailApp.sendEmail(dataRange[i][2], 'Справка готова', 'Смотрите вложенный файл', {

    attachments: [filepdf],

    name: 'готово"'

    });

    sheet.getRange(i+1, 5).setValue('☑')

    DriveApp.getFileById(dataRange[i][4]).setTrashed(true);

    }

    }

    }

    ОтветитьУдалить
    Ответы
    1. Доброго дня.
      1. Проверьте столбец с датами - принудительно установите формат в дату.
      2. Берётся лист таблицы который первый по списку - вполне может быть что данные попадают в другой лист. Или, как вариант, вы используете формулы в листе с ответами и id документа прописывается в самый низ листа таблицы
      3. Не отправляется потому что см. п.2. Если id документа нет, то и отправлять нечего.

      Удалить
  20. доброго дня, все поправила. Скрипт по заполнению формы срабатывает. все красиво - спасибо. id генерируется. Но функция getPDF ругается вот так:
    Ошибка
    Exception: Unexpected error while getting the method or property getFileById on object DriveApp.
    getPdf
    @ код 84

    вот на эту строку
    var filepdf=DriveApp.getFileById(dataRange[i][13]).getAs('application/pdf');

    может у меня чего со столбцами не так

    сам скрипт
    function getPdf(){

    var ss=SpreadsheetApp.getActive();

    var sheet=ss.getSheets()[0];

    var dataRange=sheet.getDataRange().getValues();

    for (var i=1;i<dataRange.length;i++){

    if (dataRange[i][13]==""){

    var filepdf=DriveApp.getFileById(dataRange[i][13]).getAs('application/pdf');

    GmailApp.sendEmail(dataRange[i][1], 'Талон готов', 'Смотрите вложенный файл', {

    attachments: [filepdf],

    name: 'Гарантийник 2021'

    });

    sheet.getRange(i+1, 15).setValue('отправлено на e-mail')

    DriveApp.getFileById(dataRange[i][13]).setTrashed(true);

    В таблице столбцы:
    отметка времени (A), эл почта (B), ....id (N), отправлено (O)...

    и когда ставлю триггер ежеминутно, то каждую минуту приходит сообщение о том, что триггер не исполнен)))))) спасибо.

    ОтветитьУдалить
    Ответы
    1. Доброго.
      В каком по счёту столбце генерируется id?

      Удалить
  21. Ответы
    1. if (dataRange[i][13]==""){
      ...
      ...
      }
      означает что то что внутри выполняется если id пустое. Вы точно этого хотите?

      Удалить
  22. Ой Роман, не совсем я понимаю,))))))) я не сильный специалист по этому делу)))) насколько я вас поняла, если id пустое - то ничего выполняться и не должно.
    Если id сгенерирован - то шаблон PDF - отправляется адресату.

    ОтветитьУдалить
    Ответы
    1. Вместо == поставьте != , тогда будет выполняться если id есть в таблице.

      Удалить
  23. РОМАН!!! Благодарю вас за ваш труд. Все отлично получается. В столбце где генерируется id (где он сгененирован) доки пришли на почтЫ ))) адресатам. Спасибо большое.

    ОтветитьУдалить
  24. Еще не все))))) скажите как правильно прописать вот, что: если в столбце (отправлено 15(O)) уже стоит отметка об отправке, то повторно этот файл на e-mail не отправлять.
    А то у меня получается, что отправка формы происходит с одного адреса (например 5 раз заполнили форму, и указали один эл. адрес), и после выполнения функции getPDF - на этот адрес приходит не последний созданный шаблон, а все 5 штук (а потом 6, а потом 7 и т.д.), каждый раз при заполнении формы)))))

    ОтветитьУдалить
    Ответы
    1. В статье используется 2 функции:
      1 - обрабатывает ответы приходящие из формы. Формирует копию шаблона с внесёнными данными.
      2 - формирует ещё необработанные шаблоны, проверяя стоит ли знак в определённой ячейке.После отправки файла ставит тот самый знак ☑ в нужной ячейке.
      Советую ещё раз прочитать статью и понять как это происходит.)

      Удалить
  25. Итак))))) получается, что функция spravka формирует документ из шаблона, присваивает id документу, ставит этотм id в столбец 14-1=13 по счету. Функция getPDF формирует файл пдф и отсылает его, с условием, что в столбце 15-1=14 пусто. Значит вот тут if (dataRange[i][13]==""){, должен стоять столбец значка (14), если в нем пусто , то выполняется условие по отправке файла. var filepdf=DriveApp.getFileById(dataRange[i][13]).getAs('application/pdf'); а вот тут должен быть столбец с id (14-1=13). Правильно ли я вас поняла?

    ОтветитьУдалить
  26. При таком моем понимании))))

    выдает ошибку

    Exception: Unexpected error while getting the method or property getFileById on object DriveApp.
    getPdf
    @ код 84

    по
    var filepdf=DriveApp.getFileById(dataRange[i][13]).getAs('application/pdf');
    этой строке((((((((((

    ОтветитьУдалить
    Ответы
    1. Удалите строки где в столбце с id пусто или не id.

      Удалить
  27. Спасибо Роман. Все получилось. Пришлось только забороть/добавить часовой пояс)))))) иначе давало в шаблоне Американское время, а это на день раньше)))))))))))

    ОтветитьУдалить
  28. Добрый день Роман. В таблице есть ссылка в строке на скан(pdf,jpg). Возможно ли отправить в письме с pdf еще и этот скан ?

    ОтветитьУдалить
    Ответы
    1. Доброго.
      Немного непонятно о какой таблице речь.

      Удалить
  29. Например в таблицу с ответами добавить колонку где будет храниться ссылка на pdf или jpg (например: какие то фото данные).
    Как при отправке email с документом "справка" также отправить этот файл(pdf)

    ОтветитьУдалить
    Ответы
    1. Доброго.
      С помощью UrlfetchApp.fetch получать файл, забирать blob, добавлять к письму. Так же как берётся blob справки в коде и добавляется.
      Можно.

      Удалить
  30. Добрый вечер, Роман! Подскажите пожалуйста, возможно ли подставлять фамилию и инициалы в название документа, если в таблице фамилия, имя и отчество прописаны полностью (в отдельных ячейках)? И можно ли использовать формулы в таблице каким-либо образом или в этом случае id всегда будут прописываться в последнюю строку? Буду очень благодарна за подсказку!

    ОтветитьУдалить
    Ответы
    1. Доброго дня.
      Для использования ФИО в названии документа, их нужно из таблицы взять как обычный текст и использовать метод substring(0,2) для получения только первой буквы для имени и отчества.
      Лучше не смешивать формулы и скрипты, либо делать это с большой осторожностью. Данные из таблицы куда складываются ответы из формы можно импортировать на другой лист и там уже применять формулы.

      Удалить
    2. Спасибо Вам большое, Роман! Вытащила инициалы с помощью substring

      Удалить
  31. Роман, а возможно сделать такое без использования гугл-формы? Чтобы в таблице данные заносил администратор и они выгружались в договор? И возможно ли у вас заказать подобную настройку?

    ОтветитьУдалить
  32. Роман, буду признателен, если найдете время ответить на пару глупых вопросов. Мой телеграмм: https://t.me/pashurin

    ОтветитьУдалить
    Ответы
    1. Доброго вечера. Здесь, в блоге можно задавать глупые вопросы.)

      Удалить
  33. Каждый третий день на предприятии сотрудники проходят тест на ковид. (За два года у нас уже 5 человек переболело, но все от сторонних источников). Соответственно каждому отсылают (Email, мессенджер) справку (без справки не пустят на объект). Вот это и делает скрипт.
    А теперь глупый вопрос:
    "var day=data.getDate(),month=data.getMonth()+1,year=data.getFullYear();" в декабре будет 12+1=13? Поясните, пожалуйста, подробно
    И, да, триггер(как направление, о чем прочесть желающим):
    after(durationMilliseconds)
    // Creates a trigger that runs 10 minutes later
    ScriptApp.newTrigger("myFunction")
    .timeBased()
    .after(10 * 60 * 1000)
    .create();

    И, конечно, спасибо автору.

    ОтветитьУдалить
    Ответы
    1. Доброго.
      В javascript getMonth возвращает номер месяца, для января 0, февраля 1......декабря 11. +1 нужен только для того чтобы перевести номер месяца на человеческий.
      Программная установка триггера вещь хорошая, но как уже отвечал в одном из комментариев лучше использовать saveAndClose() для того чтобы объединить два триггера в один и отправлять файлы сразу после получения ответа формы.

      Удалить
  34. Добрый день! Подскажете, в чем проблема?
    При компиляции GS
    var docBody = DocumentApp.openById(template).getBody();
    выдает
    Exception: Вам не разрешено вызывать пользователя DocumentApp.openById. Необходимые разрешения: https://www.googleapis.com/auth/documents. (строка 41, файл Код)СведенияЗакрыть

    ОтветитьУдалить
    Ответы
    1. Доброго дня.
      Скорее всего скопировали мой код в старый какой то свой проект в котором вручную прописаны права. Попробуйте создать новую форму с новой таблицей.

      Удалить
    2. Нет, набирал сам))) Проблема с правами доступа к диску была. Спасибо, разобрался!

      Удалить
  35. Роман, добрый день !
    Это Гугл таблица, которая заполняется Гугл формой.
    A B C D E
    07.04.2022 15:20:44 ООО "Хорошее" Санкт-Петербург, Цветочная, д. 1, лит.Б Иванов И.А. ivanovia@mail.ru

    //Переменная e-mail адреса Абонента
    var emab = abonentData[0][4];

    docBody.replaceText('email', emab);//4 e-mail адрес Абонента

    При выполнении скрипт в документ подставляет
    ivanovia@20ail.ru вместо ivanovia@mail.ru

    Что это может быть?
    Заранее спасибо.

    ОтветитьУдалить
    Ответы
    1. Доброго дня. Искать среди других строчек ivanovia@20ail.ru.

      Удалить
    2. Увы нет других таких строчек (((

      Удалить
    3. Скорее всего в коде меняете слово mail на 20ail? m заменяете на 20? Ещё может быть что после @ стоит пробел в ответе.

      Удалить
    4. Разобрался! Спасибо! Дело было в названиях переменных - длина и совпадение )

      Удалить
  36. Роман, добрый день !
    Подскажите пожалуйста, что я делаю не так?
    Создал всё как описано, форма есть, шаблон есть, таблица со скриптом на месте, триггеры выставлены, при заполнении формы в таблицу вписываются данные, создается документ, но он не преобразуется в pdf, на почту приходит письмо без вложения с текстом "[object Object]". Может что-то упустил.

    ОтветитьУдалить
    Ответы
    1. Доброго дня. Значит что-то упускаете из виду. Проверьте ещё раз код.

      Удалить
  37. Здравствуйте. Как решить такую проблему что при вводе неверного адреса почты или ввода адреса на русском языке ничего не отправляется и далее все ответы, которые получены через гугл форму не отправляются?

    ОтветитьУдалить
    Ответы
    1. Доброго дня. Удалить строку вручную.

      Удалить
    2. На худой конец помещать отправку в try catch.

      Удалить
  38. Роман, здравствуйте! Отличный пример автоматизации. Ваша открытость и желание помогать другим вызывают уважение. Вы молодец! Прекрасная работа! Мы столкнулись с похожей задачей по формированию заявок из данных в таблице. Хотели бы обратиться к Вам за помощью в создании скрипта. Будем рады сотрудничеству: +79035892695. Еще раз спасибо за Ваш труд!

    ОтветитьУдалить
  39. Роман, добрый день!
    Подскажите, возможно написать такую же формулу только на перенос данных с гугл таблице в другую гугл таблицу, с нужного листа в другой нужный лист. Так же не очень понимаю как эти все действия сгенерировать на работу с участием импровизированной иконки с макросом. Как пример: есть исходная таблица, в которую периодически вносятся какие то данные, строки в первой коленке определены флажком. Вот при выборе определенной строки флажком, нажать на иконку с макросом запустить функцию заполнения данных из строк в тело шаблона другой Гугл таблицы.
    Подскажите, такое вообще возможно?

    ОтветитьУдалить
    Ответы
    1. Пока что не понимаю о чём Вы спрашиваете. Может сможете, для начала, более детально обратиться по электронке igro83@gmail.com

      Удалить