В статье разбираю конкретный пример - как при помощи Google Apps Script на основании шаблона, созданного в Гугл Документах массово создавать договора дарения из строк Гугл Таблицы.

Массово создавать типовые договора можно когда есть документ-шаблон с готовым форматированием и в нём промаркированы ключевые поля, которые идут под замену для каждой новой строчки таблицы. После автозамены копия заполненного шаблона сохраняется с установленным именем в виде Гугл Документа и может сразу автоматически преобразована в PDF или файл Microsoft Word.

Автозаполнение договоров дарения

Во многих организациях бухгалтеры отвечают за составление договоров дарения от организации. Эта задача может быть однотипной, трудоемкой и подверженной ошибкам при выполнении вручную. Все данные скорее всего хранятся в 1С, но из неё можно сделать экспорт в Эксель и дальше скопировать в Гугл Таблицы:

Таблица с исходными данными
Таблица с исходными данными

Используя скрипт Google Apps, этот процесс можно полностью автоматизировать. Скрипт извлекает данные из таблицы Google, содержащей сведения о сотрудниках:

  • Дата заключения договора

  • ФИО

  • Должность

  • Сумма

  • Подарок

  • Паспорт

  • ИНН

Затем составляется однотипный шаблон документа Google для всех сотрудников:

Верхняя часть однотипного шаблона документа Google
Верхняя часть однотипного шаблона документа Google
Нижняя часть однотипного шаблона документа Google
Нижняя часть однотипного шаблона документа Google

После этого время работы гугл скрипта.

Скрипт автозаполнения договоров

Ниже приведён скрипт автозаполнения договоров дарения, написанный на Google Apps Script. Это скриптовый язык программирования, который используется для автоматизации работы с сервисами Google, такими как Gmail, Google Docs, Sheets, Slides, Forms и другими. Он позволяет создавать пользовательские функции, макросы и приложения, которые могут взаимодействовать с данными внутри этих сервисов. Это мощный инструмент для повышения производительности труда и улучшения взаимодействия между различными компонентами экосистемы Google:

function generateDocumentsFromSpreadsheet() {
    // Подробнее: https://habr.com/ru/articles/843488/
    console.log(`Функция generateDocumentsFromSpreadsheet начала работу в ${(new Date()).toLocaleString("ru-RU")}`);
    // Доступ к таблице и получение данных
    const ss = SpreadsheetApp.getActiveSpreadsheet();
    const sheet = ss.getSheetByName("Автозаполнение"); // Измените на название вашего листа
    const data = sheet.getDataRange().getValues(); // Получаем все данные с таблицы

    // Открытие шаблона Google Документа
    const templateDocId = "1y2XQhZBХХХХХХХХХХХХХzyfnXh8yUQ"; // Замените на настоящий идентификатор вашего шаблона
    const templateDoc = DriveApp.getFileById(templateDocId);
    const templateFolder = templateDoc.getParents().next(); // Получаем папку, в которой находится шаблон

    //  Проходим по каждой строке (начиная со второй, чтобы пропустить заголовки)
    for (let i = 1; i < data.length; i++) {
        const [rawDate, fullName, position, rawAmount, gift, passport, tin] = data[i]; // Получаем данные из строки таблицы
        console.log(`Работаем с ${fullName}.`);

        let formattedAmount = '';
        formattedAmount = rawAmount + " (" + FloatToSamplesInWordsRus(rawAmount) + ")" // Форматируем сумму как текст (включая пропись)

        // Форматируем дату
        let formattedDate = '';
        if (rawDate instanceof Date) {
            formattedDate = Utilities.formatDate(rawDate, Session.getScriptTimeZone(), "dd.MM.yyyy"); // Преобразуем дату в формат "дд.мм.гггг"
        }

        // Создание нового документа на основе шаблона
        const copyDoc = templateDoc.makeCopy(`Заполненный - ${fullName}`); // Копируем шаблон и называем новый документ по ФИО
        const copyDocId = copyDoc.getId();

        // Открываем новый документ
        const copyDocAsDoc = DocumentApp.openById(copyDocId);
        const copyDocBody = copyDocAsDoc.getBody();

        // Замена шаблонных полей на данные из таблицы
        copyDocBody.replaceText('Дата', formattedDate);
        copyDocBody.replaceText('ФИО', fullName);
        copyDocBody.replaceText('Должность', position);
        copyDocBody.replaceText('Сумма', formattedAmount);
        copyDocBody.replaceText('Подарок', gift);
        copyDocBody.replaceText('Паспорт', passport);
        copyDocBody.replaceText('ИННномер', tin);
        // Замена текста полей на соответствующие данные

        // Сохранение и закрытие заполненного документа
        copyDocAsDoc.saveAndClose();

        // Преобразование документа в PDF, если надо
        // const pdfBlob = DriveApp.getFileById(copyDocId).getAs("application/pdf");

        // Сохранение копии PDF на Google Диске
        // const pdfFile = templateFolder.createFile(pdfBlob).setName(`Заполненный - ${fullName}.pdf`);

        // Дополнительно: удалите промежуточный Google Документ, если нужен только PDF
        // DriveApp.getFileById(copyDocId).setTrashed(true);
    }
    console.log(`Функция generateDocumentsFromSpreadsheet закончила работу в ${(new Date()).toLocaleString("ru-RU")}`);

}


// Карта для преобразования чисел в слова (рубли) отсюда https://habr.com/ru/articles/104057/
var mapNumbers = {
    0: [2, 1, "ноль"],
    1: [0, 2, "один", "одна"],
    2: [1, 2, "два", "две"],
    3: [1, 1, "три"],
    4: [1, 1, "четыре"],
    5: [2, 1, "пять"],
    6: [2, 1, "шесть"],
    7: [2, 1, "семь"],
    8: [2, 1, "восемь"],
    9: [2, 1, "девять"],
    10: [2, 1, "десять"],
    11: [2, 1, "одиннадцать"],
    12: [2, 1, "двенадцать"],
    13: [2, 1, "тринадцать"],
    14: [2, 1, "четырнадцать"],
    15: [2, 1, "пятнадцать"],
    16: [2, 1, "шестнадцать"],
    17: [2, 1, "семнадцать"],
    18: [2, 1, "восемнадцать"],
    19: [2, 1, "девятнадцать"],
    20: [2, 1, "двадцать"],
    30: [2, 1, "тридцать"],
    40: [2, 1, "сорок"],
    50: [2, 1, "пятьдесят"],
    60: [2, 1, "шестьдесят"],
    70: [2, 1, "семьдесят"],
    80: [2, 1, "восемьдесят"],
    90: [2, 1, "девяносто"],
    100: [2, 1, "сто"],
    200: [2, 1, "двести"],
    300: [2, 1, "триста"],
    400: [2, 1, "четыреста"],
    500: [2, 1, "пятьсот"],
    600: [2, 1, "шестьсот"],
    700: [2, 1, "семьсот"],
    800: [2, 1, "восемьсот"],
    900: [2, 1, "девятьсот"]
};

var mapOrders = [{
        _Gender: true,
        _arrStates: ["рубль", "рубля", "рублей"]
    },
    {
        _Gender: false,
        _arrStates: ["тысяча", "тысячи", "тысяч"]
    },
    {
        _Gender: true,
        _arrStates: ["миллион", "миллиона", "миллионов"]
    },
    {
        _Gender: true,
        _arrStates: ["миллиард", "миллиарда", "миллиардов"]
    },
    {
        _Gender: true,
        _arrStates: ["триллион", "триллиона", "триллионов"]
    }
];

var objKop = {
    _Gender: false,
    _arrStates: ["копейка", "копейки", "копеек"]
};

function Value(dVal, bGender) {
    var xVal = mapNumbers[dVal];
    if (xVal[1] == 1) {
        return xVal[2];
    } else {
        return xVal[2 + (bGender ? 0 : 1)];
    }
}

function From0To999(fValue, oObjDesc, fnAddNum, fnAddDesc) {
    var nCurrState = 2;
    if (Math.floor(fValue / 100) > 0) {
        var fCurr = Math.floor(fValue / 100) * 100;
        fnAddNum(Value(fCurr, oObjDesc._Gender));
        nCurrState = mapNumbers[fCurr][0];
        fValue -= fCurr;
    }

    if (fValue < 20) {
        if (Math.floor(fValue) > 0) {
            fnAddNum(Value(fValue, oObjDesc._Gender));
            nCurrState = mapNumbers[fValue][0];
        }
    } else {
        var fCurr = Math.floor(fValue / 10) * 10;
        fnAddNum(Value(fCurr, oObjDesc._Gender));
        nCurrState = mapNumbers[fCurr][0];
        fValue -= fCurr;

        if (Math.floor(fValue) > 0) {
            fnAddNum(Value(fValue, oObjDesc._Gender));
            nCurrState = mapNumbers[fValue][0];
        }
    }

    fnAddDesc(oObjDesc._arrStates[nCurrState]);
}

function FloatToSamplesInWordsRus(fAmount) {
    var fInt = Math.floor(fAmount + 0.005);
    var fDec = Math.floor(((fAmount - fInt) * 100) + 0.5);

    var arrRet = [];
    var iOrder = 0;
    var arrThousands = [];
    for (; fInt > 0.9999; fInt /= 1000) {
        arrThousands.push(Math.floor(fInt % 1000));
    }
    if (arrThousands.length == 0) {
        arrThousands.push(0);
    }

    function PushToRes(strVal) {
        arrRet.push(strVal);
    }

    for (var iSouth = arrThousands.length - 1; iSouth >= 0; --iSouth) {
        if (arrThousands[iSouth] == 0) {
            continue;
        }
        From0To999(arrThousands[iSouth], mapOrders[iSouth], PushToRes, PushToRes);
    }

    if (arrThousands[0] == 0) {
        //  Handle zero amount
        if (arrThousands.length == 1) {
            PushToRes(Value(0, mapOrders[0]._Gender));
        }

        var nCurrState = 2;
        PushToRes(mapOrders[0]._arrStates[nCurrState]);
    }

    if (arrRet.length > 0) {
        // Capitalize first letter
        arrRet[0] = arrRet[0].match(/^(.)/)[1].toLocaleUpperCase() + arrRet[0].match(/^.(.*)$/)[1];
    }

    arrRet.push((fDec < 10) ? ("0" + fDec) : ("" + fDec));
    From0To999(fDec, objKop, function () {}, PushToRes);

    return arrRet.join(" ");
}

Процесс выполнения:

Скорость заполнения: 3-4 секунды на 1 договор.

Результат

После выполнения скрипта создаётся необходимое количество договоров дарения в папке расположения таблицы:

Как воспользоваться этим кодом?

Чтобы вставить и запустить скрипт Google Apps в таблицу Google, выполните следующие действия:

  1. Откройте Google Таблицы: начните с открытия своей таблицы Google.

  2. Откройте редактор скриптов: нажмите «Расширения» в меню, затем выберите «Скрипт приложений». Это откроет редактор скриптов приложений на новой вкладке.

  3. Вставьте скрипт: в редакторе скриптов удалите код-заполнитель. Затем вставьте скрипт Google Apps выше в редактор.

  4. Сохраните скрипт: нажмите значок дискеты или нажмите «Ctrl+S», чтобы сохранить скрипт. Дайте ему осмысленное имя, если будет предложено.

  5. Авторизуйте скрипт: если ваш скрипт взаимодействует со службами Google (например, Таблицами, Календарем), вам необходимо предоставить ему разрешение. Нажмите кнопку «Запустить» (значок воспроизведения) и следуйте инструкциям по авторизации.

  6. Запустите скрипт: После авторизации снова нажмите «Запустить». Скрипт будет запущен и выполнит действия по автозаполнению договоров дарения.

С этого момента вы можете легко запустить скрипт из редактора скриптов или создать пользовательское меню или кнопку в своей таблице для его запуска.

Где ещё можно использовать?

Подобные скрипты могут использоваться и для других целей:

  • Автоматизированные ведомости по заработной плате для отделов кадров
    Бухгалтерия микроприятий (большие пользуются уже чем-то другим) могут создавать ведомости по заработной плате для каждого работника. Используя подобный скрипт, они смогут автоматически вносить такие сведения, как фамилия, должность, зарплата из главной таблицы, формируя документы по оплате труда.

  • Создание юридических документов
    Юридические отделы часто обрабатывают большой объем стандартизированных договоров и соглашений, в которых от клиента к клиенту меняется лишь несколько деталей. Извлекая данные из электронной таблицы (имя клиента, сумма контракта, даты), этот скрипт может автоматически генерировать соответствующие документы за считанные секунды.

  • Табели успеваемости или сертификаты школьников
    Школы и университеты могут использовать подобный скрипт для создания табелей успеваемости или сертификатов для учащихся. Сохраняя имена, оценки и достижения учащихся в электронной таблице, скрипт может генерировать персонализированный отчет или сертификат для каждого учащегося.

  • Персонализированные приглашения и таблички для мероприятий
    Организаторам мероприятий часто требуется отправлять персонализированные приглашения или создавать именные значки для участников. Используя подобный скрипт можно извлекать данные участников из электронной таблицы и генерировать пригласительные письма или таблички в формате PDF, готовые к печати.

Итоги

Бухгалтерам больше не нужно вручную создавать каждое соглашение, что экономит время и устраняет потенциальные ошибки при вводе данных. Вместо этого они могут положиться на скрипт для создания и хранения всех соглашений в организованной структуре папок на Google Диске, готовых к просмотру и распространению. Такая автоматизация не только оптимизирует административные рабочие процессы, но и обеспечивает согласованность и легкий доступ к записям.

Автор: Михаил Шардин

30 сентября 2024 г.

Комментарии (13)


  1. Wesha
    30.09.2024 01:49

    copyDocBody.replaceText('ФИО', fullName); copyDocBody.replaceText('Должность', position); copyDocBody.replaceText('Сумма', formattedAmount);

    ...А потом приходит к бухгалтерам заключать ентот самый договор некий некий Ахмат из одной маленькой, но очень южной республики — и аффтар такой "Ё-моё, что ж я сделал?"


    1. empenoso Автор
      30.09.2024 01:49

      Немного не понял о чём вопрос.
      В данном конкретном примере не больше сотни договоров за раз было.


      1. Wesha
        30.09.2024 01:49
        +1

        Подумайте головой, что будет, если в тексте документа сначала заменить все включения подстроки "ФИО" на "Ахмат Имярекович Суммаев", а потом все включения подстроки "Сумма" на, скажем, "10000 руб"...


        1. empenoso Автор
          30.09.2024 01:49

          А вы про это. Ну да.

          Можно заменить эти имена на другие хотя бы на английском например


      1. tekerrr
        30.09.2024 01:49
        +1

        ФИО ... Cумма
        Ахмат Суммаев ... Сумма
        Ахмат 0ев ... 0


  1. REPISOT
    30.09.2024 01:49
    +6

    Как я научил бухгалтерию составлять договора дарения со скоростью 1 договор в 4 секунды

    А они вас в ответ научили говорить договора вместо договоры?


  1. MAXH0
    30.09.2024 01:49
    +4

    Что меня в этом умиляет - это бухгалтерия отправляющая в Гугл персональные данные сотрудников без всякого шифрования.


    1. empenoso Автор
      30.09.2024 01:49

      Корпоративный аккаунт


      1. Surrogate
        30.09.2024 01:49

        Корпоративные аккаунты Google ещё работают в России в 2024 году?


    1. Surrogate
      30.09.2024 01:49
      +3

      @empenoso, статья очень интересная!

      Но мне кажется вы заново изобрели, то что в MS Word называется поля слияния (mail merge)? Аналоги есть в LibreOffice и в R7-office. Возможно есть и в других импортозаместительных продуктах.

      На мой взгляд, для бухгалтерии ваш способ сложноват! В нашей конторе, точно бы не осилили...


  1. economist75
    30.09.2024 01:49
    +2

    Скорость слияния в свободном и бесплатном OpenOffice|LibreOffice на похожих документах достигает 0,03 сек/договор. При этом не требуется программирования вовсе, суммы и даты прописью - из коробки, Стили, форматирование - готовые, из других, уже имеющихся документов, и абсолютно всё работает локально. На выбор - формирование одного или тысяч файлов россыпью, в любом из 20-ти популярных форматов, включая PDF c элементами формы (списки, флажки) для ручного дозаполнения.

    Для соблюдения Закона о ПД можно просто создать криптоконтейнер VeraCrypt в котором без установки распаковать весь Portable LibreOffice (не требует установки и прав админа) и не светить паспортными данными вообще нигде. После отмонтирования контейнера на ПК не будет никаких следов ПД, кроме выходных PDF/DOCX/ODT-файлов (вот с ними - да, надо быть аккуратнее).

    Единственное, в чем может потребоваться помощь айтишника - это подсказать что 1С (все ПД лежат там) злонамеренно ограничивает число кодировок при экспорте таблиц (Сохранить как) и что cp1251 (Win) легко заменяется на utf-8 (Unicode) в LibreOffice.

    Конечно же, айтишнику есть что улучшить даже в этой схеме. Например, готовый "слиянием" PDF может содержать не только элементы управления, но и код, который будет его обрабатывать. Это или JavaScript (мехнизм PDF), или StarBasic/Python/Java/JavaScript, внедренный внутрь PDF по установке галочки "гибридный" PDF. По сути вы получаете PDF-контейнер, в котором находится "заряженный" макросами ODT (OpenDocumentFormat), и он может бесконечно перезаполняться слиянием многократно (при открытии в OpenOffice|LibreOffice), но останется "обычным" PDF для всех остальных, кому это не нужно. Ничего подобного ни один платный "офис" и другие свободные пакеты пока не умеют и еще очень долго не смогут.


  1. UncleJonathan
    30.09.2024 01:49
    +2

    Очень актуально (сарказм) с учётом того, что подобные продукты заблокируют в течение нескольких недель или месяцев либо с той, либо с другой стороны. И все усилия насмарку.

    Кроме того, Google и другие корпорации зарабатывают на продаже персональных данных. И вот такие чувствительные данные дарить им для анализа и продажи... Думаю, что даже если это законно (я не юрист), то должно быть незаконно.

    Я бы очень не хотел, чтобы бухгалтерия моей компании работала даже не в Google Docs, а в принципе, в любом облаке (кроме разве что своего собственного).


  1. vedmak3
    30.09.2024 01:49
    +2

    Ммм персоналка в гугл доках это так по нашему.