google script Prevent duplicate data Submit - google-apps-script

I'm a beginner of learning google script.
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 previously entered data, code must stop. If it is new entry, data mast sent to "output" sheet. Can anybody help me to figure out my error?
EDIT: I prepared a bio data format. I will share this google sheet among my school friends and asking to fill their details and click SUBMIT button. I want them to stop sending duplicate data. Their Identity Number is to be mention in "A3" cell. These data stored in "output" sheet. Identity number is stored in Column B. "A3" cell data match with Column B data in OUTPUT file and, if duplicate, msg prompt "duplicate".
Now When I run it , Data submitting to "output" sheet even DUPLICATE data entered in "A3" Cell.
function submitData() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var formSS = ss.getSheetByName("input"); //Data entry Sheet
var datasheet = ss.getSheetByName("output");
var ttt = formSS.getRange("A3").getValue();
var values = ss.getSheetByName("output").getDataRange().getValues();
for (var i = 0; i < values.length; i++) {
var row = values[i];
if (row[1] == ttt) { //column B of the "output" sheet has project IDs (Unique Number)
SpreadsheetApp.getUi().alert('Duplicate data. you need to click update');
}
else
{
var values = [[formSS.getRange("E2").getValue(),
formSS.getRange("A3").getValue()]],
datasheet.getRange(datasheet.getLastRow()+1, 1, 1, 2).setValues(values);
}
}
}

The "error" is that there isn't anything that stop the script execution when row[1] == ttt is true. One possible fis is to add
return;
immediately after
SpreadsheetApp.getUi().alert('Duplicate data. you need to click update');

This function looks to see if an id matches with col B and if it does it updates and if it doesn't in adds a new row.
function submitOrUpdateData() {
const ss = SpreadsheetApp.getActive();
const ui = SpreadsheetApp.getUi();
const fsh = ss.getSheetByName("input");
const dsh = ss.getSheetByName("output");
const did = fsh.getRange("A3").getValue();//data id
const colB = dsh.getRange(1, 2, dsh.getLastRow(), 1).getValues();
let idx = colB.indexOf(did);
if (~idx) {
dsh.getRange(idx, 1, 1, 2).setValues([[fsh.getRange("E2").getValue(), fsh.getRange("A3").getValue()]]);//update
} else {
dsh.getRange(dsh.getLastRow() + 1, 1, 1, 2).setValues([[fsh.getRange("E2").getValue(), fsh.getRange("A3").getValue()]]);//new data
}
}
Bitwise Not (~)

Related

Submit, Search, and Update from one spreadsheet to another spreadsheet (ID)

Submit values from userform (selected cells) to another google spreadsheet and then find them (search), modify and post it again.
I have a source sheet (userform), I need to get values from the selected cells and ranges, submit the values to the destination sheet (another spreadsheet) using ID.
I have attached a Screenshot explaining with color code where the data should go. https://i.stack.imgur.com/Yrfje.jpg
[SCREENSHOT][1]
Also Shared two spreadsheets with actual data (Userform and Datasheet) for your reference
https://docs.google.com/spreadsheets/d/1NY_ckzEWxU7DCGro5tTqzpiOi6iG5PAQFxpZg0OKodY/edit?usp=sharing
https://docs.google.com/spreadsheets/d/1QL0jaNts2YRkZTlxmS0bk7V1fVVHBsJFmxS5C05PEmA/edit?usp=sharing
Please help to enhance this below code (thanks to #[Yuri Khristich][2])
var ss = SpreadsheetApp.getActiveSpreadsheet();
var form_sheet = ss.getSheetByName('UserForm');
var data1 = form_sheet.getRange(['c3:c8']).getValues().flat();
var [num, date, name, id, project,group] = data1;
var data2 = form_sheet.getRange('b10:e20').getValues();
var data_sheet = ss.getSheetByName('DataSheet');
var nums = data_sheet.getRange('a:a').getValues().flat();
var row = nums.indexOf(num);
if (row < 0 ) {
var new_row = [num, date, name, id, project, group, JSON.stringify(data2)];
data_sheet.appendRow(new_row)
} else {
var range = data_sheet.getRange('g' + ++row);
range.setValue(JSON.stringify(data2));
}
}
function searchRecord() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var form_sheet = ss.getSheetByName('UserForm');
var num = form_sheet.getRange('b1').getValue();
var data_sheet = ss.getSheetByName('DataSheet');
var nums = data_sheet.getRange('a:a').getValues().flat();
var row = nums.indexOf(num);
if (row < 0) {
ss.toast('Nothing was found')
return;
}
row++;
var data1 = data_sheet.getRange('a' + row + ':g' + row).getValues().flat();
var data2 = JSON.parse(data1.pop());
form_sheet.getRange('c3:c8').clearContent().setValues(data1.map(x => [x]));
form_sheet.getRange('b10:e20').clearContent().setValues(data2)
}
// Function to submit the data to DataSheet sheet
function oldsubmitData() {
var myGooglSheet= SpreadsheetApp.getActiveSpreadsheet(); //declare a variable and set with active google sheet
var shUserForm= myGooglSheet.getSheetByName("UserForm"); //delcare a variable and set with the User Form worksheet
var datasheet = myGooglSheet.getSheetByName("DataSheet"); ////delcare a variable and set with the DataSheet worksheet
var namedRanges = SpreadsheetApp.getActiveSheet().getNamedRanges();
if (namedRanges.length > 1) {
Logger.log(namedRanges[0].getName());
}
//to create the instance of the user-interface environment to use the messagebox features
var ui = SpreadsheetApp.getUi();
// Display a dialog box with a title, message, and "Yes" and "No" buttons. The user can also
// close the dialog by clicking the close button in its title bar.
var response = ui.alert("Submit", 'Do you want to submit the data?',ui.ButtonSet.YES_NO);
// Checking the user response and proceed with clearing the form if user selects Yes
if (response == ui.Button.NO)
{return;//exit from this function
}
//Validating the entry. If validation is true then proceed with transferring the data to DataSheet sheet
// if (validateEntry()==true)
{
var blankRow=datasheet.getLastRow()+1; //identify the next blank row
datasheet.getRange(blankRow, 1).setValue(shUserForm.getRange("C3").getValue()); //Date
datasheet.getRange(blankRow, 2).setValue(shUserForm.getRange("C4").getValue()); //UserForm Number
datasheet.getRange(blankRow, 3).setValue(shUserForm.getRange("C5").getValue()); //Student Name
datasheet.getRange(blankRow, 4).setValue(shUserForm.getRange("C6").getValue()); //ID
datasheet.getRange(blankRow, 5).setValue(shUserForm.getRange("C7").getValue()); //Project
datasheet.getRange(blankRow, 6).setValue(shUserForm.getRange("C8").getValue()); //Group Name
//I stuck here, get values from moving row (E,13 to 16)
//datasheet.getRange(blankRow, 8).setValue(shUserForm.getNamedRanges("TotalPresent").getValue());// TotalPresent
//datasheet.getRange(blankRow, 8).setValue(shUserForm.getNamedRanges("SoundRoomDay").getValue());// SoundRoomDAy
//datasheet.getRange(blankRow, 9).setValue(shUserForm.getNamedRanges("GroupDay").getValue());// GroupDay
//datasheet.getRange(blankRow, 10).setValue(shUserForm.getNamedRanges("TotalDays").getValue());// TotalDays
// date function to update the current date and time as submittted on
datasheet.getRange(blankRow, 12).setValue(new Date()).setNumberFormat('yyyy-mm-dd h:mm'); //Submitted On
//get the email address of the person running the script and update as Submitted By
datasheet.getRange(blankRow, 13).setValue(Session.getActiveUser().getEmail()); //Submitted By
var ss = SpreadsheetApp.getActiveSpreadsheet();
var form_sheet = ss.getSheetByName('UserForm');
var num = form_sheet.getRange('c3').getValue();
var data = form_sheet.getRange('b10:e20').getValues();
var datasheet = ss.getSheetByName('DataSheet');
var nums = datasheet.getRange('a:a').getValues().flat();
var row = nums.indexOf(num);
if (row < 0) return;
var range = datasheet.getRange('g' + ++row);
range.setValue(JSON.stringify(data));
ui.alert(' "New Data Saved - StudentID #' + shUserForm.getRange("C5").getValue() +' "');
}
}```
-code by-----------------------
[1]: https://i.stack.imgur.com/Yrfje.jpg
[2]: https://stackoverflow.com/users/14265469/yuri-khristich
I guessed your goal is as follows.
You want to copy the values from UserForm of PRINTNOTE_A to DataSheet of WB-Datasheet 2.
You want to copy the values of cells C3,C7,C8,D8,D6,D4,E8,E19,E20,E21,E22 and B10:E18 of UserForm sheet to the same row.
You want to search the column "A" of DataSheet sheet using the value of cell B1 of UserForm sheet, you want to put the values in the same row.
You want to achieve this using Google Apps Script.
By guessing your question, I propose a sample script as follows.
In order to retrieve the values from the distributed cells, in this sample script, I used Sheets API.
Sample script:
Before you use this script, please enable Sheets API at Advanced Google Services.
From your script, this script is used as the container-bound script of PRINTNOTE_A. So, please copy and paste this script to the script editor of PRINTNOTE_A Spreadsheet.
function sample() {
const dstSpreadsheetId = "###"; // Please set the destination Spreadsheet ID (WB-DataSheet_B).
const dstSheetName = "DataSheet";
const srcSheetName = "UserForm";
// Retrieve values from source sheet and create an array and search value.
const srcSpreadsheet = SpreadsheetApp.getActiveSpreadsheet();
const ranges = ["B1", "C3", "C7", "C8", "D8", "D6", "D4", "E8", "E8", "E19", "E20", "E21", "E22", "B10:E18"];
const [search, ...values] = Sheets.Spreadsheets.Values.batchGet(srcSpreadsheet.getId(), { ranges: ranges.map(r => `'${srcSheetName}'!${r}`) }).valueRanges.flatMap(({ values, range }) => {
if (range.includes("B10:E18")) return values.flat();
return values[0][0];
});
// Put the array to the destination sheet using the search value.
const dstSheet = SpreadsheetApp.openById(dstSpreadsheetId).getSheetByName(dstSheetName);
const range = dstSheet.getRange("A2:A" + dstSheet.getLastRow()).createTextFinder(search).findNext();
if (range) {
dstSheet.getRange(range.getRow(), 2, 1, values.length).setValues([values]);
} else {
dstSheet.getRange(dstSheet.getLastRow() + 1, 1, 1, values.length + 1).setValues([[search, ...values]]);
}
}
From your replying of but new values are not getting submitted in the next blank row., I updated my proposed answer. In the current script, when the search value is not found, the values are appended to the destination sheet.
Note:
This sample script is for your provided 2 Spreadsheets. And, I tested this script using your provided Spreadsheets. So when you changed the Spreadsheet and your actual Spreadsheets are different from your provided sample Spreadsheet, this script might not be able to be used. Please be careful about this.
References:
Method: spreadsheets.values.batchGet
createTextFinder(findText) of Class Range

How do I get cycle through a column and when a certain value is hit, an email is sent based on values in the same row, doing this for a set range?

I am new to coding, specifically using google script. I have a spreadsheet with documents and document ids, and when overdue the due date changes to red and done like a traffic light system.
However what I need to set up for work is an automatic email to be sent out when a value in column L = 1 (based on the red and other values in the row being true) and if the value is 0 I want the script to pass over it and move down through the column.
I have tried reading and testing codes around using do and while and for etc, but I just can’t get it to work.
So what I have set up is inefficient and rudimentary I’m sure, but it works, just slowly - see code below.
When it does hit a 1, it then uses activate cells and offsets to get values from cells in the corresponding row and package up an email and send. The script then moves down and checks the next cell, but because I’m using activate and calling multiple functions it is taking a long time to cycle through the column.
The column is L and the range in use is L2:L60
I’d be so grateful if anyone can point me in the right direction of what the best method would be for this please.
function Findfirstcell()
{
SpreadsheetApp.getActive().getRange('l2').activate();
GetLvalue();
}
function GetLvalue()
{
var st= SpreadsheetApp.getActive().getActiveSheet().getActiveCell().getValue();
if (st < 1)
Movetonext();
else
Createemail();
}
function Createemail()
{
var ss= SpreadsheetApp.getActive()
var x = ss.getActiveSheet()
const s= x.getActiveCell();
var y= s.offset(0,-5);
var v= y.getValue();
var t= s.offset(0,-10);
var z= s.offset(0,-11);
var p= t.getValue();
var w= z.getValue();
var emailAddress = v+'#gmail.com'
var message = p+" is overdue - Document ID "+w
var subject = w+" is Overdue"
MailApp.sendEmail(emailAddress, subject, message);
var w= s.offset(0,-3);
w.setValue('Sent')
var end= s.offset(1,0);
end.activate();
GetLvalue();
}
function Movetonext()
{
var ss= SpreadsheetApp.getActive();
var s= ss.getActiveSheet().getActiveCell();
var end= s.offset(1,0);
end.activate();
var v= s.getValue();
if (v < 0)
ss.getRange('a1').activate();
else
GetLvalue();
}
I have tried several other methods with no success as mentioned, one being below, where it does it for the first row (which may be because the value is 1), but it does not then continue down the column.
I assumed as the condition had failed, triggering function createemail and there is nothing to loop it back
function Findfirstcell()
{
var ss= SpreadsheetApp.getActiveSheet().getActiveCell('l2').activate;
// Or
var ss= SpreadsheetApp.getActiveSheet().getRange('l2:l60');
for(ss<0; ss>0; ss++){createemail();}
}
function createemail()
{
var st= SpreadsheetApp.getActive().getCurrentCell();
var y= st.offset(0,-5);
var v= y.getValue();
var t= st.offset(0,-10);
var z= st.offset(0,-11);
var p= t.getValue();
var w= z.getValue();
var emailAddress = v+'#gmail.com'
var message = p+" is overdue - Document ID "+w
var subject = w+" is Overdue"
MailApp.sendEmail(emailAddress, subject, message);
}
Any guidance would be greatly appreciated
I believe your goal is as follows.
You want to check whether the value of the column "L" is 0 or 1.
When the value is 1 and the value of column "I" is not Sent, you want to send an email using the values of columns "A", "B" and "G".
When the email is sent, you want to put the value of Sent to the column "I".
You want to reduce the process cost of your script.
In this case, how about the following flow?
Retrieve values from the active sheet.
Check the value of column "I" and "L" and send the email and return the range list.
When the email is sent, the value of "Sent" is put to the column "I".
When this flow is reflected to a Google Apps Script, it becomes as follows.
Sample script:
function myFunction() {
// 1. Retrieve values from the active sheet.
const sheet = SpreadsheetApp.getActiveSheet();
const [, ...values] = sheet.getDataRange().getValues();
// 2. Check the value of column "I" and "L" and send the email and return the range list.
const rangeList = values.reduce((ar, [a, b, , , , , g, , i, , , l], r) => {
if (l > 0 && i != "Sent") {
var emailAddress = g + '#gmail.com';
var message = b + " is overdue - Document ID " + a;
var subject = a + " is Overdue";
MailApp.sendEmail(emailAddress, subject, message);
ar.push(`i${r + 2}`);
}
return ar;
}, []);
// 3. When the email is sent, the value of "Sent" is put to the column "I".
if (rangeList.length > 0) sheet.getRangeList(rangeList).setValue("Sent");
}
References:
reduce()
getRangeList(a1Notations)

Data entry form that selects the next open row per specified column in google sheets

I am trying to create a data entry form that submits data to a data sheets first open row. The problem is that the data sheet has formula in one of the columns so it is not truly empty. This is causing the current script to take the cells with formula into consideration and only selecting the rows after it.
Could you guys please assist me with a workaround to the issue.
Current script looks like this:
function submitData() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var formSS = ss.getSheetByName("FORM"); //Form Sheet
var datasheet = ss.getSheetByName("DATA"); //Data Sheet
//Input Values
var values = [[formSS.getRange("D4").getValue(),
formSS.getRange("D8").getValue(),
formSS.getRange("D12").getValue(),
formSS.getRange("D16").getValue(),
formSS.getRange("D20").getValue(),
formSS.getRange("D24").getValue(),
formSS.getRange("D28").getValue(),
formSS.getRange("L32").getValue()]];
datasheet.getRange(datasheet.getLastRow()+1, 1, 1, 8).setValues(values);
}
You can find the first free row by evaluating the row contents
Sample
var freeRow;
var columnI = datasheet.getRange("I1:I" + datasheet.getLastRow()).getDisplayValues().flat();
for(var i = 0; i < columnI.length; i++){
if(columnI[i] == "") {
freeRow = i + 1;
break;
}
}
datasheet.getRange(freeRow, 1, 1, 8).setValues(values);
In addition, you are using a Form submit trigger, you use event objects
Sample
function submitData(e) {
var range = e.range;
var row = range.getRow();
// this is the row into which the latest form response has been inserted - do with it what you need
...
}

How can I update an existing row with new data from another sheet?

I have a Google Sheet (example here) with two sheets - Form and Data. I have the Form sheet set up so that entries can be manually input, and once the UPDATE button is clicked, the data is sent to the bottom of the Data sheet as a new row, and the Form fields are cleared, ready for the next input.
I'm trying to take this one step further - if the UPDATE button is clicked, and the Part Number field contains a value which already exists in Column A of the Data sheet (meaning it has already been entered at some point), a new row should not be added, but the existing row with the matching Part Number should be updated with the newly submitted data instead, leaving any unchanged fields at their previous values.
I've had this set up and working well years ago in Excel, but Google Sheets is a new ballgame altogether, and I have been unable to find any helpful information on how this might work with Google Sheets.
Here is the code that is working great for adding new data:
function submitData() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var formSS = ss.getSheetByName("Form");
var datasheet = ss.getSheetByName("Data");
var values = [[formSS.getRange("B2").getValue(),
formSS.getRange("B3").getValue(),
formSS.getRange("B4").getValue(),
formSS.getRange("B5").getValue()]];
datasheet.getRange(datasheet.getLastRow()+1, 1, 1, 4).setValues(values);
formSS.getRange("B2").clearContent(),
formSS.getRange("B3").clearContent(),
formSS.getRange("B4").clearContent(),
formSS.getRange("B5").clearContent();
}
How can I update this code so that any already existing part numbers submitted in the Form sheet update the corresponding row on the Data sheet, instead of it being entered as a new row?
See if this works
function submitData() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var formSS = ss.getSheetByName("Form");
var dataSheet = ss.getSheetByName("Data");
var values = formSS.getRange("B2:B5").getValues().reduce(function(a, b) {
return a.concat(b)
});
var partNum = values[0];
var row;
dataSheet.getDataRange().getValues().forEach(function(r, i) {
if (r[0] === partNum) {
row = i + 1
}
})
row = row ? row : dataSheet.getLastRow() + 1;
var data = dataSheet.getRange(row, 1, 1, 4).getValues()[0].map(function (el, ind){
return el = values[ind] ? values[ind] : el;
})
dataSheet.getRange(row, 1, 1, 4).setValues([data]);
formSS.getRange("B2:B5").clearContent()
}

Google Script: InputBox with drop down list spreadsheet (Not FORM)

I have this simple script:
function myFunction() {
var ssh = SpreadsheetApp.getActiveSpreadsheet();
var ss = ssh.getActiveSheet();
var s = ss.getActiveRange();
var rowIndex = s.getRowIndex();
var colIndex = s.getColumnIndex();
// Get the number of columns in the active sheet.
var colNumber = ss.getLastColumn();
if (colIndex == 1 && rowIndex != 1) {
//Get User Name from inputBox
var name = Browser.inputBox('Owner', 'Enter your Name', Browser.Buttons.OK_CANCEL);
var r1 = ss.getActiveRange().getRow();
//Insert the Name in the active row in the column 6
ss.getRange(r1, 6).setValue(name)
//Here I have other part of the code but is for copy rows to other sheet.
}
}
In column 6 have active data validation (not permit insert values ​​that are not in the list of items).
Example: Charles, Oscar, Paul, Other. If I enter the names all in lower case do not enter. If the value entered is identical as "Charles" the value is entered.
Now, is possible these values ​​may appear as a drop down list in the inputbox? I mean, the user can select from that list and then press Enter or Ok and the value insert into the cell.
Note: This is not a form. This is the spreadsheet.
Thanks,
UPDATED: 01/09/2014
function Historic(e) { //CopyRows
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = ss.getActiveSheet();
if (s.getName() == 'ISP1'){
var r = SpreadsheetApp.getActiveRange();
// Get the row and column of the active cell.
var rowIndex = r.getRowIndex();
var colIndex = r.getColumnIndex();
// Get the number of columns in the active sheet.
var colNumber = s.getLastColumn();
// Move row based on criteria in column 1, and if row is not the header.
if (colIndex == 1 && rowIndex != 1) { // 1 Is the Comment Column
//Call the Function Menu List
showNamePicker();
//Get values for the active row bases on the criteria.
var status = s.getRange(rowIndex, colIndex).getValue();
// --------------- Copy ROW only when someone modify the Column1 --------------------------
// Do nothing if criteria value is not actually changed to something else.
if (s.getName() != 'Historic') {
// The target sheet is the one with the same name as the criteria value.
var targetSheet = ss.getSheetByName('Historic');
var target = targetSheet.getRange(targetSheet.getLastRow() + 1, 1);
//=====Copy the Row from ISP1 to Historic=======
s.getRange(rowIndex, 1, 1, colNumber).copyTo(target);
}
}
}
}
function showNamePicker() {
var app = UiApp.createApplication().setHeight(100).setWidth(180);
var handler = app.createServerHandler('setName');
var list = app.createListBox().setName('list').addChangeHandler(handler);
var names = ['Choose a name here','Charles', 'Oscar', 'Paul', 'Other'];
for(var n in names){
list.addItem(names[n]);
}
handler.addCallbackElement(list);
app.add(list);
SpreadsheetApp.getActive().show(app);
}
function setName(e){
var ssh = SpreadsheetApp.getActiveSpreadsheet();
var ss = ssh.getActiveSheet();
var r1 = ss.getActiveRange().getRow();
ss.getRange(r1, 6).setValue(e.parameter.list);
}
Function Historic: If someone modify the Column 1, the script will copy all data in the row that was modify to historic.
Needed:
The script should be insert the name in the column 6 for that row before the script copy the row to the historic.
Issue:
When someone modify the column 1 the user press ENTER and the new active row change because is not the same and the script not insert the name in the row that was modify.
For example:
I modify the A2 and press ENTER the new row is A3, and the script will insert the information into F3 and not in F2.
I tried to call the function before this line:
//Get values for the active row bases on the criteria.
var status = s.getRange(rowIndex, colIndex).getValue();
but I'm still newbie... I this is very easy but I can't get with the solution.
I will appreciate if you can help me.
EXAMPLE:
Change Cell A2 = ID#12578
The script should be insert the name in the column F2 and then copy all row include the F2 in the sheet called Historic. (The script is not inserting the name into F2 is inserting in F3
You will have to replace you Browser.inputBox with a dialog box that will hold a list box with the desired values.
You can build this dialog box either using UiApp or HTML service.
The problem will be that nothing will prevent to enter a value directly in the cell but it was already the case in your code...
You can store the list values wherever you want, in a hidden column, another sheet, another spreadsheet or in ScriptProperties or in the code itself... that's a matter of choice :-)
Edit : a small example using UiApp :
function showNamePicker() {
var app = UiApp.createApplication().setHeight(100).setWidth(120);
var handler = app.createServerHandler('setName');
var list = app.createListBox().setName('list').addChangeHandler(handler);
var names = ['Choose a name here','Charles', 'Oscar', 'Paul', 'Other'];
for(var n in names){
list.addItem(names[n]);
}
handler.addCallbackElement(list);
app.add(list);
SpreadsheetApp.getActive().show(app);
}
function setName(e){
var ssh = SpreadsheetApp.getActiveSpreadsheet();
var ss = ssh.getActiveSheet();
var r1 = ss.getActiveRange().getRow();
ss.getRange(r1-1, 6).setValue(e.parameter.list);
return UiApp.getActiveApplication().close();// close the Ui
}