ВЕДЕНИЕ СПРАВОЧНИКА УНИКАЛЬНЫХ КЛЮЧЕЙ ИЗ ТАБЛИЦЫ ОТВЕТОВ ФОРМЫ -------------------------------------------------------------------------------- НЕКОТОРЫЕ ОБЩИЕ КОММЕНТАРИИ: -------------------------------------------------------------------------------- Есть таблица ответов формы "Заявки" (будем считать ее исходной - "source") и таблица уникальных комбинаций ключевых полей ответов, т.е. по сути "справочник" (будем считать ее целевой - "target"). В рассматриваемом примере ключевых полей - три (колонки B, C, D таблицы "source"). Далее я в основном буду называть эти таблицы именно как "source" и "target" (возможно, лучше было бы даже "child" и "parent" соответственно, но поскольку тогда по алгоритму получится, что "дитя" порождает "родителя", то остановимся всё же на "source" и "target") Для обеспечения связи записей двух таблиц создадим систему перекрестных ссылок. Эта система будет обеспечиваться несколькими служебными полями (колонками) в обеих таблицах. Эти поля удобнее всего расположить в правой части таблиц, после полей основных данных. В таблице ответов "source" служебных полей будет три: - Идентификатор строки - уникальный номер строки внутри таблицы ответов "source". - Идентификатор ключа - номер соответствующей записи справочника "target". - Индикатор коллизий - числовой код, содержащий в себе информацию о наличии дубликатов среди записей справочника, а также потерях фокуса записью справочника в момент ее обновления (информация актуальна на момент добавления новой строки в таблицу ответов). В таблице-справочнике "target" служебных полей будет два: - Идентификатор ключа - уникальный номер записи внутри таблицы справочника "target". - Идентификатор модифицирующей строки - ссылка на строку ответов "source", информация которой последней использовалась для модификации неключевых полей записи справочника и, соответственно, сама строка была последней, которая была "привязана" к данной строке справочника. -------------------------------------------------------------------------------- КОД GOOGLE SCRIPT: -------------------------------------------------------------------------------- function onSubmit(e) { Logger.log("Скрипт работает: " + e.range.getA1Notation()); // см. в Инструменты/ Редактор скриптов/ Вид/ Журналы processSubmit(e.range); } function processSubmit(erange) { // служебные колонки таблицы формы - справа, после данных формы (в таблице "Заявки" = "Source" в концептуальной модели) var colSrcIdRec = "G"; // колонка Номер строки (как ID) "Заявки" - для наглядной ссылки на запись в справочнике var colSrcIdKey = "H"; // колонка ID строки справочника (для новых ключей G и H совпадают, для существующих H - номер строки первой регистрации данного ключа var colSrcCntDup = "I"; // колонка Кол-во строк ключа в справочнике - 0 - для новой, 1 - для единственной, >1 - для дубликатов // служебные колонки справочника - справа, после данных ключа и других доп.данных (в таблице "Таблица" = "Target" в концептуальной модели) var colTrgKey1 = "A"; var colTrgKey2 = "B"; var colTrgKey3 = "C"; var colTrgIdKey = "G"; var colTrgIdModRec = "H"; var collisionFactor = 1; // некоторая дельта в нумерации строк, которая может понадобится, если из таблицы заявок по каким-то причинам удалялись строки // (в этом случае дельту следует задать чуть больше кол-ва удаленных строк) // т.е. дельта нужна чтобы обеспечить сквозную уникальность всех строк, когда-либо появившихся в заявках var deltaRow = 1; var rowNew = erange.getRow(); // номер добавляемой строки в таблице ответов формы var srcIdRec = rowNew + deltaRow; // Id строки в таблице ответов (может отличаться от номера) var sheetSrc = erange.getSheet(); // лист с таблицей ответов формы (Src="Source") sheetSrc.getRange("E"+rowNew).setValue(true); // отметка "Заявка перенесена" sheetSrc.getRange(colSrcIdRec+rowNew).setValue(srcIdRec); // Номер строки (как ID) var keyToSearch = [erange.getCell(1, 2).getValue(), erange.getCell(1, 3).getValue(), erange.getCell(1, 4).getValue()]; var ssTarget = SpreadsheetApp.openById("страшенный_Id_таблицы_Google_из_44_символов_"); var sheetTrg = ssTarget.getSheetByName('Лист1'); // лист с таблицей уникальных "ключей" - справочник (Trg="Target") // считываем таблицу справочника в ее текущем состоянии -> в массив; var values = sheetTrg.getDataRange().getValues(); // имеем в виду, что пока мы будем обрабатывать считанное (в мозгах), // набор записей справочника на сервере может измениться: // как по количеству - из-за записей, добавленных другими процессами, // так и по порядку - из-за сортировок, выполненных другими пользователями // начинаем проверку ключа новой записи (3 поля): "есть ли уже такой ключ в справочнике?" var cnt = 0; var rowFound = 0; for (var row in values) { var keyCurrent = [values[row][0], values[row][1], values[row][2]]; if (twoRecordsAreEqual(keyCurrent, keyToSearch)) { cnt++; // в процессе считаем кол-во дубликатов // и запоминаем первую встретившуюся строку с таким ключом if (!rowFound) { rowFound = Number(row) + 1; // проверим, что запись "не ушла из-под ног" (не потеряла фокус - из-за чужих сортировок) // (на этот раз беря данные не из массива, а прямо с листа) keyCurrent = [sheetTrg.getRange(colTrgKey1+rowFound).getValue(), sheetTrg.getRange(colTrgKey2+rowFound).getValue(), sheetTrg.getRange(colTrgKey3+rowFound).getValue()]; // и делаем проверку еще раз if (twoRecordsAreEqual(keyCurrent, keyToSearch)) { // если всё-таки это ТА существующая запись, которая надо - решаемся прописать в неё // номер добавленной строки Заявок - как последнюю изменившую справочник sheetTrg.getRange(colTrgIdModRec+rowFound).setValue(srcIdRec); // в этом же месте следует, если надо, перепрописывать другие аттрибуты записи справочника: // например, уточненная фамилия клиента, "пришедшая" с последней введенной заявкой и т.п. // и в источнике - исходный ID записи справочника (номер породившей строки Заявок) sheetSrc.getRange(colSrcIdKey+rowNew).setValue( sheetTrg.getRange(colTrgIdKey+rowFound).getValue() ); // т.е. получается мощная ВЗАИМО-ССЫЛИСТОСТЬ } else { // иначе - считаем, что она потеряла фокус ("ушла из-под ног") // при этом в источнике ссылку на справочник всё же можем прописать - просто прочитаем ее из массива values sheetSrc.getRange(colSrcIdKey+rowNew).setValue( values[rowFound-1][sheetTrg.getRange(colTrgIdKey+rowFound).getColumn()-1] ); // "номер добавленной строки Заявок - как последнюю изменившую справочник" - ВОТ ЭТО ЗДЕСЬ НЕ СМОЖЕМ СДЕЛАТЬ collisionFactor = -1000. // появление множителя -1000 говорит о том, что несмотря на то, что запись справочника была найдена, // в нее не удалось прописать номер новой строки формы (и, возможно, другие неключевые аттрибуты) // из-за того, что запись справочника потеряла фокус; } } } } sheetSrc.getRange(colSrcCntDup+rowNew).setValue(cnt * collisionFactor); // значение cnt > 1 здесь говорит о наличии в справочнике записей-дубликатов с данной комбинацией ключей; // ситуация может возникнуть при одновременном (очень близким по времени) вводе в таблицу формы одних и тех же ключей; // необходимо вмешательство администратора для коррекции ситуации вручную, в ходе которой в справочнике // должна остаться одна запись с данным сочетанием ключей и на неё должны быть перенастроены все ссылки в таблице Заявок, // которые ранее "успели" настроиться на удаляемые из справочника дубликаты // большие отрицательные значения (<= -1000) говорят о потере фокуса записи справочника в процессе обработки // и о необходимости вмешательства администратора для коррекции ситуации вручную, // а именно прописывания: colTrgIdModRec = colSrcIdRec // а также прописать неключевые аттрибуты записи справочника, "пришедшие" с последней заявкой if (cnt==0) { // новый уникальный ключ добавляется в справочник sheetTrg.appendRow([erange.getCell(1, 2).getValue(), // A erange.getCell(1, 3).getValue(), // B erange.getCell(1, 4).getValue(), // C null, // D null, // E null, // F srcIdRec, // G = colTrgIdKey - фактически становится ID в справочнике! srcIdRec]); // H = colTrgIdModRec - номер последней модифицирующей записи sheetSrc.getRange(colSrcIdKey+rowNew).setValue(srcIdRec); } } /** * сравнивает 2 массива поэлементно (по полям записи) * * @param {Array} * Первый массив * @param {Array} * Второй массив * @return {Boolean} */ function twoRecordsAreEqual(arr, arr2){ if(arr.length != arr2.length) return false var on = 0; for( var i = 0; i < arr.length; i++ ){ if(arr[i] === arr2[i]) on++; } return on==arr.length?true:false } function testSubmit() { var erange = SpreadsheetApp.getActive().getSheetByName("Ответы на форму (1)").getRange("A16:Z16") processSubmit(erange); }