Google Apps Script Prevent duplicate data Submit on Google Sheets Form - javascript

I need to submit data from one sheet to another using google script. When I click submitData, it should check whether it is previously entered data or not. If there was no same data, the code must add new data. But, if ther was same data, the code must update the existing.
Their Identity Number is mentioned in "H11" cell on "Form" sheet. The identity number is stored in "Column D" on "Data" sheet. Now When I run it , it's submitting to "Data" sheet even DUPLICATE data entered in "H11" Cell
function submitData() {
var ss = SpreadsheetApp.getActive();
var formSS = ss.getSheetByName("Form"); //Form Sheet
var datasheet = ss.getSheetByName("Data"); //Data Sheet
var ui = SpreadsheetApp.getUi(); //get UI
var IDform = formSS.getRange("H11").getValue();
var IDdata = datasheet.getRange(2, 4, datasheet.getLastRow()-1, 1).getValues();
var peringatan = formSS.getRange("H8").getDisplayValue() +"'s Passenger" + ' on ' + formSS.getRange("H10").getDisplayValue() + ' is ' + formSS.getRange("H12").getDisplayValue() + ' passenger?'
// Input Values
var values = [[ formSS.getRange("H4").getValue(),
formSS.getRange("H8").getValue(),
formSS.getRange("H10").getValue(),
formSS.getRange("H11").getValue(),
formSS.getRange("H12").getValue()]];
var clform = [[ formSS.getRange("H4").clear(),
formSS.getRange("H6").clear(),
formSS.getRange("H8").clear(),
formSS.getRange("H10").clear(),
formSS.getRange("H12").clear()]];
// Save New Data
if (formSS.getRange("H4").getValue() == "") {
ui.alert('Data Tidak Boleh Kosong');
} {
let idx = IDdata.indexOf(IDform);
if (~idx) {
if (ui.alert('Data akan diperbarui - ' + peringatan, ui.ButtonSet.OK_CANCEL) == ui.Button.OK) {
datasheet.getRange(idx, 1, 1, 5).setValues(values); //update data
formSS.getRange("H4").clear();
formSS.getRange("H6").clear();
formSS.getRange("H8").clear();
formSS.getRange("H10").clear();
formSS.getRange("H12").clear();
}
} else {
if (ui.alert('Data baru akan ditambahkan - ' + peringatan, ui.ButtonSet.OK_CANCEL) == ui.Button.OK) {
datasheet.getRange(datasheet.getLastRow()+1, 1, 1, 5).setValues(values); //new data
formSS.getRange("H4").clear();
formSS.getRange("H6").clear();
formSS.getRange("H8").clear();
formSS.getRange("H10").clear();
formSS.getRange("H12").clear();
}
}
}
}
I've tried to remove the Bitwise Not (~), so it tried to call the //update data condition but it's alerted Exception: The starting row of the range is too small.

Try flattening your nested array this way:
var IDdata = datasheet.getRange(2, 4, datasheet.getLastRow()-1, 1).getValues().flat();
Because you're reading a column, each element in the array is itself an array with one element that contains the value. Without flattening, you're searching for a value inside a list of arrays.

Related

Google Sheets, stack report from multiple workbooks

Goal: To stack data from 90+ google workbooks, all with the same sheet name, into the one master sheet for reporting
Info:
All worksheets have the same number of columns.
I have the following script but it does not run properly, I think the issue is with how I am caching / Pushing the data to the array before pasting to the output sheet.
I am trying to build an array then paste it in one go.
The tables I am stacking have 47 columns, unknown number of rows.
The part that opens the sheets is all working perfectly.
// Get the data from the worksheets
var indexsheet = SpreadsheetApp.getActive().getSheetByName("Index");
var outputsheet = SpreadsheetApp.getActive().getSheetByName("Output");
var response = SpreadsheetApp.getUi().prompt('Current Cycle', 'Enter Cycle Name Exactly in YY-MMM-Cycle# format', SpreadsheetApp.getUi().ButtonSet.OK_CANCEL)
var CurrentCycleName = response.getResponseText()
// Assign datasets to variables
var indexdata = indexsheet.getDataRange().getValues();
// For each workbook in the index sheet, open it and copy the data to a cache
indexdata.forEach(function(row, r) {
try {
//open Entity specific workbook
var workbookid = indexsheet.getRange(r + 1, 7, 1, 1).getValues();
var Entityworkbook = SpreadsheetApp.openById(workbookid)
// Open workhseet
Entitysheet.getSheetByName(CurrentCycleName)
// Add PR Data to cache - stacking for all countrys
var PRDataCache = Entitysheet.getDataRange().push()
} catch {}
})
// Set the all values of the sheet at once
outputsheet.getRange(r + 1, 14).setValue('Issue Splitting Data')
Entitysheet.getRange(2, 1, PRDataCache.length || 1, 47).setValues(PRDataCache)
};
This is the index tab where we are getting the workbookid from to open each file
This is the output file, we are stacking all data from each country
I believe your goal is as follows.
You want to retrieve the Spreadsheet IDs from the column "G" of "Index" sheet.
You want to give the specific sheet name using a dialog.
You want to retrieve all values from the specification sheet in all Spreadsheets. In this case, you want to remove the header row.
You want to put the retrieved values on "Output" sheet.
In this case, how about the following sample script?
Sample script:
function myFunction() {
var ss = SpreadsheetApp.getActive();
var indexsheet = ss.getSheetByName("Index");
var outputsheet = ss.getSheetByName("Output");
var response = SpreadsheetApp.getUi().prompt('Current Cycle', 'Enter Cycle Name Exactly in YY-MMM-Cycle# format', SpreadsheetApp.getUi().ButtonSet.OK_CANCEL);
var CurrentCycleName = response.getResponseText();
var ids = indexsheet.getRange("G1:G" + indexsheet.getLastRow()).getValues();
var values = ids.reduce((ar, [id]) => {
try {
var [, ...values] = SpreadsheetApp.openById(id).getSheetByName(CurrentCycleName).getDataRange().getValues();
ar = [...ar, ...values];
} catch (e) {
console.log(`"${id}" was not found.`);
}
return ar;
}, []);
if (values.length == 0) return;
// If the number of columns is different in all Spreadsheets, please use the following script.
// var maxLen = Math.max(...values.map(r => r.length));
// values = values.map(r => r.length < maxLen ? [...r, ...Array(maxLen - r.length).fill("")] : r);
outputsheet.getRange(outputsheet.getLastRow() + 1, 1, values.length, values[1].length).setValues(values);
}
Note:
When the number of Spreadsheet IDs is large, the processing time might be over 6 minutes. I'm worried about this. At that time, how about separating the Spreadsheet IDs?
Reference:
reduce()

Copying multiple non empty rows to a new sheet

I am trying to save the daily feedback that i get from a website from the "feedback" sheet into another google sheet that i called "Feedback Database"
Column A: the Date
Column B: Time
Column C: The email address
Column D: the feedback
The data rows start from row 3.
What i am currently doing to save the data based on the basic knowledge that i have is:
copying all the data in row 3 in the "feedback" sheet
Saving it to the "database" sheet
Getting back to the "feedback" sheet and deleting row 3 so that data in row 4 becomes in row 3.
I have another function that count the number of rows and run the saveFeedback_Data() function based on the number of rows.
I know that what i am doing in step 3 isn't a proper way, I would really appreciate it if you guys recommend a better way to perform this function with a commentary in order to understand.
Appreciate your help,
function saveFeedback_Data() {
var spreadsheet = SpreadsheetApp.getActive();
spreadsheet.setActiveSheet(spreadsheet.getSheetByName('feedback'), true);
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
var Date= sheet.getRange('feedback !A3').getValue();
var Time = sheet.getRange('feedback !B3').getValue();
var Email = sheet.getRange('feedback !C3').getValue();
var Feedback = sheet.getRange('feedback !D3').getValue();
var sheet_dest = ss.getSheetByName("Database");
sheet_dest.appendRow([Date,Time, Email,Feedback]);
Logger.log('Issue Date : ' + Date + ' Issue time : ' + Time + ' Email Address : ' + Email + ' Feedback : ' + Feedback + '\n');
//now deleting the row
spreadsheet.setActiveSheet(spreadsheet.getSheetByName('feedback'), true);
spreadsheet.getRange('3:3').activate();
spreadsheet.getActiveSheet().deleteRows(spreadsheet.getActiveRange().getRow(), spreadsheet.getActiveRange().getNumRows());
spreadsheet.getRange('A10').activate();
}
Save Feedback Data
This will run about ten times faster
function saveFeedback_Data() {
const ss = SpreadsheetApp.getActive();
const sh = ss.getSheetByName('Feedback');
const shsr = 3;
const dbsh = ss.getSheetByName('Database');
const rg = sh.getRange(shsr, 1, sh.getLastRow() - shsr + 1, 4);
const vs = rg.getValues().filter(r => !r.every(e => !e));//gets all non blank rows at one time
dbsh.getRange(dbsh.getLastRow() + 1, 1, vs.length, vs[0].length).setValues(vs);//moves all data at one time
rg.clearContent();//clears all data at one time
}
You can goto to Google Apps Script Reference and using the search box find any function that you don't understand. If it's a pure JavaScript function the go here

How can I fix/link/connect Telegram bot inline button to cell in Google Sheet?

So I have Telegram Bot that works with Google App Script.
My bot sends the last row that was edited in my Sheet in any List ( more then 10 list).
And the message from bot goes with inline button "Accepted" - and I'm receiving an array with call_back data.
But I don't know how can I fix "Accepted" to the row ( fixed cell ) where I got message from.
Func - message to telegram
function onEdit(e) {
sendTelegram(e)
}
function sendTelegram(e){
var row = e.range.getRow();
var col = e.range.getColumn();
var startRow = 2; // Starting row
var targetColumn = 2; // Row, where placed trigger to send msg
var ws = sheetName; //List name
var sheet = e.source.getActiveSheet();
var sheetName = e.source.getActiveSheet().getName(); //Takes list name
let Company = e.source.getActiveSheet().getRange(row,13).getValue(); //Takes data from row 2 to 13 Column in every list
var firstCol = 2; // Starting from 2 column
var numOfCols = 13;//Ending in 13 column
var fullRowValues = sheet.getRange(row, firstCol, 1, numOfCols).getValues();
var fullRowString = fullRowValues.flat().toString(); // Makes array toString.
let chatId = "ChatId";// Telegram groupId
var text = encodeURIComponent(Company + " New Document has Been added" + ws)
if(e.source.getActiveSheet().getRange(row,2).getValue() == "Yanson"){ //Yanson- trigger.If in column 2 "Yanson" is set - sends row to group chat.
sendText(chatId,"[New Doc Added!] " + Company + sheetName + " , " + fullRowString,accepted);} //accepted - inline button
Send Text Func.
function sendText(chatId, text, keyBoard) {
let data = {
method: 'post',
payload: {
method: 'sendMessage',
chat_id: String(chatId),
text: text,
parse_mode: 'HTML',
reply_markup: JSON.stringify(keyBoard)
}
}
UrlFetchApp.fetch('https://api.telegram.org/bot' + token + '/', data);
}
And doPost func that track when button was clicked and saving it in "Log" list as array of data.
function doPost(e) {
let contents = JSON.parse(e.postData.contents);
SpreadsheetApp.openById("SheetId").getSheetByName("Log").appendRow([contents]);
So what I just need is to make When someone click on the button "Accepted" - text "Accepted" appears in 15 column near edited row that just was sent by bot.
This is how one of my list looks (example).
And this is how looks array that I'm receiving from bot[Contents].
I'm sure we can connect list and button by the Case Name (111/111/111) but don't know how :(
1.Yanson - trigger to send msg
2.Bot sends Msg with row.
3.Pressing inline button in bot msg.
4. Accepted appears near the row that was sent. Thats what I'm trying to get and searching for the solution for step 4.

Cannot Read Sheet Range From Null

I have a function that allows me to import spreadsheet data from my gmail to Google Sheets. Previously, the spreadsheet data had only had 6 columns to import. Now, some new changes are made and a 7th column was added. After this change was implemented, my function no longer works and Google Sheets throws this error. May I please have some assistance?
So, as I'm looking at this, the intended functionality looks right to me. Skip the first 3 rows (netdata) and take everything below. Could it be the + 1, 1 ?
The Error:
TypeError: Cannot read property 'getRange' of null
My import function:
function importCSVFromGmail() {
var sheetName = "SHEET_NAME"; // Name of sheet tab.
var threads = GmailApp.search("from:EMAIL HERE label:LABEL HERE"); // "from:recipient email here label:name of your filter/label here"
var messages = threads[0].getMessages();
var message = messages[messages.length - 1];
var attachment = message.getAttachments()[0]; // [0] will by default look for only 1 attachment. If there are more than two attachment increase value. ex: [1] , [2]
var data = [];
if (attachment.getContentType() == MimeType.CSV) { // This will look for a CSV file type first
data = Utilities.parseCsv(attachment.getDataAsString(), ",");
} else if (attachment.getContentType() == MimeType.MICROSOFT_EXCEL || attachment.getContentType() == MimeType.MICROSOFT_EXCEL_LEGACY) { // If attachment is an xls, this line will look at the content to determine and convert accordingly.
var tempFile = Drive.Files.insert({title: "temp", mimeType: MimeType.GOOGLE_SHEETS}, attachment).id;
data = SpreadsheetApp.openById(tempFile).getSheets()[0].getDataRange().getValues();
Drive.Files.trash(tempFile);
}
if (data.length > 0) {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName);
var netdata = data.slice(3); // This will skip the number of rows starting from the top.
sheet.getRange(sheet.getLastRow() + 1, 1, netdata.length, netdata[0].length).setValues(netdata );
}
}
I'm think the issue is here:
sheet.getRange(sheet.getLastRow() + 1, 1, netdata.length, netdata[0].length).setValues(netdata );
The error message indicates that the issue is with the sheet variable being null. That happens when there is no sheet by the name SHEET_NAME in the spreadsheet.
To fix the error, replace SHEET_NAME with the name of the sheet you want the function to work with. Check for things like leading and trailing whitespace in the sheet's name.

Make a copy of google sheet into google drive folder

I have two Spreadsheets. The first spreadsheet contains my raw data that indicates the employee # and name of employees. The second spreadsheet is the spreadsheet I want to copy into a google drive folder. I want to update the specific fields on the 2nd spreadsheet based on the employee number and employee name from the 1st spreadsheet. Everytime it updates the cells in the second spreadsheet, it will create copy of the 2nd spreadsheet into google drive folder.
However, it keeps on setting just one value inside the replicated spreadsheets. It doesn't loop the employee names and employee numbers from the 1st spreadsheet.
My code is already replicating the 2nd spreadsheet. It's just the values aren't updating.
function replicateCards() {
var ss = SpreadsheetApp.openById('xxxxxxxx');
var copyCard = SpreadsheetApp.openById('zzzzzzzzz');
var getID = DriveApp.getFileById(copyCard.getId())
var card = copyCard.getSheetByName("Card");
var mastersheet = ss.getSheetByName("Mastersheet");
var getLastRow = mastersheet.getLastRow();
var destinationFolder = DriveApp.getFolderById('yyyyyyyyyy');
;
var changeColorToGrayList = card.getRangeList(['C7', 'E7', 'G7', 'I7', 'K7', 'M7', 'O7', 'Q7',
'C9', 'E9', 'G9', 'I9', 'K9', 'M9', 'O9', 'Q9',
'C11', 'E11', 'G11', 'I11', 'K11', 'M11', 'O11', 'Q11']);
var setValueToZero = card.getRangeList(['C8', 'E8', 'G8', 'I8', 'K8', 'M8', 'O8', 'Q8',
'C10', 'E10', 'G10', 'I10', 'K10', 'M10', 'O10', 'Q10',
'C12', 'E12', 'G12', 'I12', 'K12', 'M12', 'O12', 'Q12']);
for (i = 1; i < getLastRow; i++) {
var employeeNumber = mastersheet.getRange(i + 1, 1).getValue();
var employeeName = mastersheet.getRange(i + 1, 2).getValue();
card.getRange("C3").setValue(employeeName);
card.getRange("H3").setValue(employeeNumber);
card.setActiveRangeList(changeColorToGrayList).setBackground("gray");
card.setActiveRangeList(setValueToZero).setValue(0);
// var getID = DriveApp.getFileById(card).getId();
getID.makeCopy(employeeNumber + " High Flyer Card", destinationFolder);
}
}
I expect the output of getID.makeCopy(employeeNumber + " High Flyer Card", destinationFolder); contains different employee # and employee names, not just one value inside the google folder.
Your code should work as intended, unless a bulky file makes the code overlap.
If so, implementation of flush() will make your code run sequentially, see here for a detailed explanation.
In your case, modifying the for loop to
for (i = 1; i < getLastRow; i++) {
var employeeNumber = mastersheet.getRange(i + 1, 1).getValue();
var employeeName = mastersheet.getRange(i + 1, 2).getValue();
card.getRange("C3").setValue(employeeName);
card.getRange("H3").setValue(employeeNumber);
card.setActiveRangeList(changeColorToGrayList).setBackground("gray");
card.setActiveRangeList(setValueToZero).setValue(0);
getID.makeCopy(employeeNumber + " High Flyer Card", destinationFolder);
SpreadsheetApp.flush();
}
should solve the issue.

Categories

Resources