• Как пересчитать формулы по запросу?

    oshliaer
    @oshliaer Куратор тега Google Apps Script
    Google Products Expert
    Нет. Все формулы участвуют в одном цикле событий.

    Вы можете только заставить полностью пересчитаться всю Таблицу, вызвав SpreadsheetApp.flush().

    В пользовательских формулах от flush толку не будет. В этих формулах так же не работают set-операторы.

    Использование CacheService мало полезно

    5ecf2d47878ae972525511.png

    Перевел весь скрипт в удобоваримый вид с сделал небольшой пример работы такой функции

    /**
     * @OnlyCurrentDoc
     */
    
    /**
     *
     * The TESTCACHESERVICE function
     *
     * @param {param} param
     * @return {number}
     * @customfunction
     */
    function TESTCACHESERVICE(param) {
      const cache = CacheService.getScriptCache();
      const cached = param === 'break' ? 0 : +cache.get('cached') + 1 || 0;
      cache.put('cached', cached);
      return cached;
    }


    Пример в Таблице https://docs.google.com/spreadsheets/d/11Z1pT2y28B...
    Ответ написан
    7 комментариев
  • С помощью какого дополнения можно построить графики для финансового и бухгалтерского анализа?

    oshliaer
    @oshliaer Куратор тега Google Sheets
    Google Products Expert
    Не видел автоматических генераторов отчетов. Предполагаю, что это сопряжено с проблемой разнообразия данных.

    Вообще, обычно, заполненную Таблицу поставляют как источник в Data Studio, в которой, в зависимости от вашего умения, можно создать или подключить готовый отчет.
    Ответ написан
    3 комментария
  • Можно ли указать в качестве решения программу которую написал сам?

    oshliaer
    @oshliaer
    Google Products Expert
    Этот ответ может быть нарушением п.п. 5.6. правил сервиса.

    Но если вы в ответе приведете доводы против вашей объективной заинтересованности (другой программы не нашлось) и опишите реальные и конкурентные преимущества именно вашей программы (меньше кнопок больше дела), то это не будет нарушением.

    В любом случае, если администратор посчитает ваше сообщение "любым элементом рекламы", то он может пожаловаться или удалить пост.

    Нет ничего плохого в одноразовом упоминании. Это даже рекламой не назовешь.
    Ответ написан
    1 комментарий
  • Как настроить отправку уведомлений другому пользователю?

    oshliaer
    @oshliaer Куратор тега Google Sheets
    Google Products Expert
    Есть основные 4 способа это сделать.

    Превый способ

    Не делайте этого. Это самый верный и надежный способ. Человек и так получает кучу уведомлений, а тут еще и вы со своим спамом. Даже если важное уведомление, но пользователь не был подписан по своему желанию - это будет спам. Подписаться на изменения Таблицы пользователь может в меню Инструменты - Правила уведомлений

    5ec88fe5382b5314160161.png

    Второй способ

    Используйте дополнение. Например, Magic Cell Notifications - G Suite Marketplace или Check Sheet Notifications - G Suite Marketplace

    Третий способ

    Обычно приложения из третьего способа не всегда понятны. Тогда можно подыскать внешнее приложение или сервис, которые умеют это делать.

    Четвертый способ

    Наконец, можно использовать Google Apps Script для написания своего собственного скрипта, который будет отслеживать необходимые вам изменения.
    Ответ написан
    Комментировать
  • Как автоматически создать копию листа Таблицы?

    oshliaer
    @oshliaer Куратор тега Google Apps Script
    Google Products Expert
    Вам потребуется Google Apps Script для запуска программы из меню Таблицы.

    Создаем меню

    function onOpen() {
      SpreadsheetApp.getUi()
        .createMenu('Reset sheet')
        .addItem('Make a copy before reset', 'userActionMakeCopyBeforeReset')
        .addToUi();
    }


    Действие пользователя

    function userActionMakeCopyBeforeReset() {
      const book = SpreadsheetApp.getActive();
      const sheet = book.getActiveSheet();
      makeCopyBeforeReset_(sheet, book);
    }


    Копирование и сброс листа-шаблона

    function makeCopyBeforeReset_(sheet, book) {
      const copy = sheet.copyTo(book);
      const rangesAddressesList = ['B5', 'B7', 'B9', 'B11'];
      resetByRangesList_(sheet, rangesAddressesList);
      return copy;
    }


    Основной метод сброса данных

    function resetByRangesList_(sheet, rangesAddressesList) {
      sheet.getRangeList(rangesAddressesList).clearContent();
    }


    Собрав это все, вы должны получить программу как на видео https://twitter.com/i/status/1260851838942957574
    Пример в Таблице https://docs.google.com/spreadsheets/d/1g8cCxofljF...
    Ответ написан
    4 комментария
  • Как получить получить текст Гугл Документа?

    oshliaer
    @oshliaer
    Google Products Expert
    Вероятно, самый простой способ получить текст Документа - это использовать Google Drive API.

    Метод
    GET https://www.googleapis.com/drive/v3/files/fileId
    возвращает список возможных экспортов

    {
      "exportLinks": {
        "application/rtf": "https://docs.google.com/feeds/download/documents/export/Export?id=ABC&exportFormat=rtf",
        "application/vnd.oasis.opendocument.text": "https://docs.google.com/feeds/download/documents/export/Export?id=ABC&exportFormat=odt",
        "text/html": "https://docs.google.com/feeds/download/documents/export/Export?id=ABC&exportFormat=html",
        "application/pdf": "https://docs.google.com/feeds/download/documents/export/Export?id=ABC&exportFormat=pdf",
        "application/epub+zip": "https://docs.google.com/feeds/download/documents/export/Export?id=ABC&exportFormat=epub",
        "application/zip": "https://docs.google.com/feeds/download/documents/export/Export?id=ABC&exportFormat=zip",
        "application/vnd.openxmlformats-officedocument.wordprocessingml.document": "https://docs.google.com/feeds/download/documents/export/Export?id=ABC&exportFormat=docx",
        "text/plain": "https://docs.google.com/feeds/download/documents/export/Export?id=ABC&exportFormat=txt"
      }
    }


    Для своего файла с id "ABC" авторизуйтесь с правами https://www.googleapis.com/auth/drive.readonly, и вызовите

    GET https://docs.google.com/feeds/download/documents/export/Export?id=ABC&exportFormat=txt
    Ответ написан
    Комментировать
  • Как в Utilites.formatDate() задать русскую локаль?

    oshliaer
    @oshliaer Куратор тега Google Apps Script
    Google Products Expert
    Не помню, чтобы Utilities поддерживали локали.

    Для V8 можно использовать DateTimeFormat

    function myFormatDate(date) {
      const y = new Intl.DateTimeFormat('ru',{
        year: 'numeric'
      }).format(date)
      const m = new Intl.DateTimeFormat('ru',{
        month: 'long'
      }).format(date)
      return `${m}-${y}`
    }
    
    function test(){
      const date = new Date();
      console.log(Utilities.formatDate(date, "GMT+3", "MMM-yyyy"));
      console.log(myFormatDate(date));
    }


    По идее, не нужно беспокоиться о производительности в таких местах, в основном они хорошо оптимизируются системой.

    Обратите внимание на свой пример, "MMM-d" не вернет год - вернет дату.
    Ответ написан
    Комментировать
  • Как настроить автоподбор высоты строки для объединенных ячеек?

    oshliaer
    @oshliaer Куратор тега Google Sheets
    Google Products Expert
    Хороший вопрос.

    На данный момент никак.

    Пишите предложения по улучшению из меню Помощь. Будем надеяться, что нас таких много.
    Ответ написан
    Комментировать
  • Как форматировать результаты скрипта для вывода?

    oshliaer
    @oshliaer Куратор тега Google Apps Script
    Google Products Expert
    Рекомендую использовать метод getDisplayValues()

    var drr = sheet.getRange(sheet.getLastRow(), 1, 1, 3).getDisplayValues()[0];


    т.о. вам не придется беспокоится о типах данных в коде, и будет достаточно привести тип данных в ячейке. Если в ячейке будет видно 0.00%, то вернет именно 0.00%, если 1-02-20, то 1-02-20.

    Это наименьшее и наиболее надежное решение в данном случае.
    Ответ написан
    1 комментарий
  • Как исправить ситуацию: при заполнении формы и подтягивании результатов в формуле значения съезжают на одну строчку вниз?

    oshliaer
    @oshliaer Куратор тега Google Sheets
    Google Products Expert
    Вам нужно использовать ARRAYFORMULA для адресного расчета массива.

    =ARRAYFORMULA('Ответы на форму (4)'!B2:B * 13)

    Дополнительно в комментариях https://qna.habr.com/answer?answer_id=1616129#comm...
    Ответ написан
  • Как обратиться к параметру в квадратных скобках POST запроса?

    oshliaer
    @oshliaer Куратор тега Google Apps Script
    Google Products Expert
    Обратите внимание, что передается x-www-form-urlencoded. При получении аргумента формы вы должны "преобразовать" его к требуемому формату. В вашем случае

    const payments = JSON.parse(e.parameter.payments);

    Следующий код возвращает первый элемент массива

    /**
     *
     * @param {GoogleAppsScript.Events.DoPost} e
     */
    function doPost(e) {
      const payments = JSON.parse(e.parameter.payments);
      return ContentService.createTextOutput(JSON.stringify(payments.products[0]));
    }


    5eb3a269ca4ea797180580.png

    Если строка запроса более интересная, то лучше всего использовать сборку для gas из https://github.com/sindresorhus/query-string.

    Более простое представление можно взять тут form2json.js

    Соответственно, чтобы получить в doPost объект нужно сделать
    form2Json(e.postData.contents)

    Пример моего приложения
    /* global form2Json */
    /* exported doPost */
    /**
     *
     * @param {GoogleAppsScript.Events.DoPost} e
     */
    function doPost(e) {
      // const payments = JSON.parse(e.parameter.payments);
      return ContentService.createTextOutput(
        JSON.stringify(form2Json(e.postData.contents), null, '  ')
      );
    }


    5eb51b8925e51758421765.png

    Обратите внимание, что возвращается не массив, а объект с индексами. Но это не мешает работать

    const data = form2Json(contents);
    console.log(JSON.stringify(data.payment.products[0]));
    Ответ написан
    3 комментария
  • Как сделать так, чтобы результат в ячейке отображался только при наличии необходимых данных в другой?

    oshliaer
    @oshliaer Куратор тега Google Sheets
    Google Products Expert
    Для проверки данных можно использовать формулу IF https://support.google.com/docs/answer/3093364?hl=ru

    Например, если в A1 что-то есть, то нужно B1 умножить на C1, наче вывести пустую строку

    =IF(A1<>"";B1*C1;"")

    Для длинных формул "лучше" использовать проверку от обратного - сначала пустая строка, потом расчет

    =IF(A1="";"";B1*C1) это обычная практика для большей читаемости.
    Ответ написан
    Комментировать
  • Как быстрее всего получить данные из сторонних таблиц?

    oshliaer
    @oshliaer Куратор тега Google Sheets
    Google Products Expert
    К сожалению нельзя дать однозначный ответ на этот вопрос. Я бы разделил по способам применения.

    Например, используя формулы мы не получаем набор данных, мы видим лишь их представление. Т.н. представление по ссылке. Мы не можем менять эти данные. Накладывает большие ограничения.

    Программное получение данных является хорошей альтернативой. Но недешевой в плане эксплуатации и поддержки.

    Использование стороннего сервиса. Из адекватных предпочитаю sheetgo.

    Использование Google Apps Script. Там возможны варианты: SpreadsheetApp или Sheet Advanced Service. Для больших данных второй предпочтительнее, но не такой удобный как первый.

    Использование Google Sheets API. Очень напоминает Sheet Advanced Service, но требует своей исполнительной среды и прочих накладных расходов.

    У всех этих способов есть один большой недостаток - это все не мгновенно, потому что это сетевые приложения. К тому же требуется либо ждать события системы либо самому какие-то кнопки жмякать. Куча проблем, одним словом.

    Но без худа не бывает чуда. Попробуйте какой-нибудь более-менее пригодный пример для Sheet Advanced Service или подобрать сниппет тут google-apps-script-snippets

    С уважением.
    Ответ написан
    Комментировать
  • Как убрать незадействованные строки и столбцы?

    oshliaer
    @oshliaer Куратор тега Google Sheets
    Google Products Expert
    Мне больше всего нравится дополнение Crop Sheet.
    Ответ написан
    1 комментарий
  • Как сделать, чтобы результат формулы умещался в одну ячейку?

    oshliaer
    @oshliaer Куратор тега Google Sheets
    Google Products Expert
    Без примера сложно что-то сказать определенное.

    Скорее всего

    =JOIN(CHAR(10);IMPORTXML("https://yandex.ru/search/?text=test";"//h2/text()"))


    Если заголовки умещаются в одну строку, то запрос xPath можно сделать и без /text()

    Для вашего случая

    5ead238c24c74063282484.png

    =JOIN(CHAR(10);importxml(A2;"//h2"))
    Ответ написан
    2 комментария
  • Как в функции закрыть и открыть доступ на редактирования в листе ОДНОЙ ячейки?

    oshliaer
    @oshliaer Куратор тега Google Apps Script
    Google Products Expert
    Этого нельзя сделать. Ну, или можно, но совсем ерунда получится.

    Для перехвата "даблклика" вы должны блокировать повторное исполнение кода до момента исполнения текущего. Я обычно для этого использую LockService.

    Скрипты - это REST. REST - это пошаговое изменение состояния. Никаких "кликов" "setTimeout" и пр. в Таблицах нет. А еще там нет доступа к интерфейсу, лишь небольшие вкрапления фреймов на GAS в разрешенные области приложения (алерт, диалог, сайдбар, меню, тост).
    Ответ написан
    Комментировать
  • Вопрос о правах доступа, или как защищаются google табличные скрипты?

    oshliaer
    @oshliaer Куратор тега Google Apps Script
    Google Products Expert
    Скрипты никак не связаны с ограничением на ввод в ячейку, потому что скрипты это REST.
    • Если вы удалите скрипт, то защита ячеек никак не изменится.
    • Если вы попробуете изменить скрипт так, чтобы значение защищенных (условно от вас) ячеек изменилось, то сработает защита.
    • Если вы измените скрипт пользовательской функции так, чтобы она возвращала другое значение, то в защищенной ячейке она будет возвращать другое значение, но само значение ячейки по прежнему не изменить.


    По моему мнению (а ровно и по мнению Гугл) никаких логических противоречий.

    Ни в каких ячейках никаких скриптов не находится. Google Apps Script - это синтаксический сахар для Google REST API.

    К сожалению, вопрос поставлен так, что тут нельзя дать и хороший и правильный ответ. Если коротко, то вам нужно изменить свое отношение к этой парадигме. Это просто работает не так, как вы себе представляете. Для познающего это нормально.

    Если конкретно о защите кода, вне свойств Таблиц, то существуют механизмы сокрытия и распространения защищенного кода. Например, дополнения позволяют "встраивать код в Таблицу", при этом пользователь не может его изменить, он даже не видит этот код.

    Особенно отмечу бессмысленность сравнения VBA и GAS.
    Ответ написан
  • Скрипты в Google Sheets, как начать с нуля?

    oshliaer
    @oshliaer Куратор тега Google Apps Script
    Google Products Expert
    Ответ написан
    Комментировать
  • Почему условное форматирование не всегда срабатывает?

    oshliaer
    @oshliaer Куратор тега Google Sheets
    Google Products Expert
    Потому что у вас в одной ячейке строка, а в другой число.
    • Либо приведите все значения к одному типу
    • Либо приводите значения к одному типу при условном форматировании


    Приведение U2 к числу:

    5ea8f99bbc5c7357875514.png

    =VALUE(LEFT(S1;2))
    Ответ написан
    1 комментарий
  • Почему программа не срабатывает так, как ожидается при изменении всего одной типовой команды?

    oshliaer
    @oshliaer Куратор тега Google Apps Script
    Google Products Expert
    Перед тем, как копировать формулы в значения вам нужно завершить все расчеты в Таблице.

    Вызовите flush()

    SpreadsheetApp.flush();

    Например,

    /**
     *
     * @param {GoogleAppsScript.Events.SheetsOnEdit} e
     */
    function onEdit(e) {
      var activeSheet = e.source.getActiveSheet();
      SpreadsheetApp.getActive().toast(['Лист'].indexOf(activeSheet.getName()));
      if (['Лист'].indexOf(activeSheet.getName()) == -1) return;
      var r = SpreadsheetApp.getActiveRange();
      var cols = r.getColumn();
      if (cols == 6) {
        // если изменяем 6 колонку, то тригер работает и:
        var cell1 = activeSheet.getRange(2, 2); // во второй колонке берет данные из второй строки (там лежит формула)
        var destination1 = activeSheet.getRange(activeSheet.getLastRow(), 2); // находит последнюю строку с данными и выбирает вторую колонку
    
        cell1.copyTo(destination1); // копирует из второй строчки в последнюю
        var cell2 = activeSheet.getRange(2, 3); // тоже самое но только для 3 колонки
        var destination2 = activeSheet.getRange(activeSheet.getLastRow(), 3); // тоже самое но только для 3 колонки
        cell2.copyTo(destination2); // тоже самое но только для 3 колонки
        var cell3 = activeSheet.getRange(2, 5); // тоже самое но только для 5 колонки
        var destination3 = activeSheet.getRange(activeSheet.getLastRow(), 5); // тоже самое но только для 5 колонки
        cell3.copyTo(destination3); // тоже самое но только для 5 колонки
        var cell4 = activeSheet.getRange(2, 8); // тоже самое но только для 8 колонки
        var destination4 = activeSheet.getRange(activeSheet.getLastRow(), 8); // тоже самое но только для 8 колонки
        cell4.copyTo(destination4); // тоже самое но только для 8 колонки
    
        SpreadsheetApp.flush();
    
        destination1.copyTo(
          destination1,
          SpreadsheetApp.CopyPasteType.PASTE_VALUES,
          false
        ); // во второй колонке меняет формулу на значение
        destination2.copyTo(
          destination2,
          SpreadsheetApp.CopyPasteType.PASTE_VALUES,
          false
        ); // в третьей колонке меняет формулу на значение
        destination3.copyTo(
          destination3,
          SpreadsheetApp.CopyPasteType.PASTE_VALUES,
          false
        ); // в пятой колонке меняет формулу на значение
        destination4.copyTo(
          destination4,
          SpreadsheetApp.CopyPasteType.PASTE_VALUES,
          false
        ); // в восьмой колонке меняет формулу на значение
        SpreadsheetApp.getActive().toast('Готово');
      }
    }
    Ответ написан
    4 комментария