Limiting the results of drop-down in Google Sheets - drop-down-menu

I work as an operations manager at at small business and I'm trying to set up an order sheet that is easy for the salesman to use. In the order sheet, I've used the OFFSET function to refer to a master list containing customers and prices. Under Customer, I type the customer and it draws it from the master list via an auto-complete drop-down. The same happens with the product.
Here is the order sheet:
Order Sheet Example
My issue is I'll begin typing in the product e.g. 'prawn'. We have over a dozen prawn lines, but a particular customer will only take one. All of the other prawn results have no prices for that customer. However, the auto-complete function will offer up all the 'prawn' results.
In the Master list, I've entered prices for only the products that the particular customer uses. Take a look at what the Master List looks like:
Master List look
Without an excellent memory of what customer wants what, it's an exercise in trial and error. In the above example, I could type 'topside' and if I select the wrong one, no price comes up.
This is frustrating.
I was hoping for a way to limit the auto-complete so that when I type 'prawn' or 'topside' for that customer, it only comes up with auto-complete fields that have the price in it. Can anyone help? Or does anyone know of any work-arounds? I'd be really thankful, the current order system is quite difficult.

I believe this will do what you want. It is a little difficult to tell from your sample data. It uses google apps script and sheet names and/or columms may need to be changed in the script for your data. I am attaching a sample spreadsheet you can copy then try. You will have to approve the script in the copy you make.
function onEdit(e) {
var cust=e.value //The value of the edited cell
var sh=e.source.getActiveSheet().getSheetName()//Name of the active sheet.
var col=e.range.getColumn()//The edited column
var r=e.range.getRow()//The edited row
var row=e.range.offset(0,1).getA1Notation()//Cell A1 notation of cell in same row one column to the right.
var ss=SpreadsheetApp.getActiveSpreadsheet()
var s=ss.getActiveSheet().getSheetName()//Name of active sheet
var s1=ss.getSheetByName("Sheet1")//Variables for sheets
var s2=ss.getSheetByName("Sheet2")
var s3=ss.getSheetByName("TEMP")
var rng=s2.getDataRange().getValues()//Customer/Products
if(sh=="Sheet1" && col==1){//If sheet1 is active sheet and Customer (column A) is edited.
var array=[]//Array to hold customers products
for (var i=0;i<rng.length;i++){
if(rng[i][0]==cust ){
for(var j=1;j<rng[0].length-1;j++){
if(rng[i][j]!="" ){//If customers product has $ entry add to Array.
array.push([rng[0][j]])
}}}}
s3.clearContents()//Clear old product list from TEMP.
s1.getRange(row).clearContent()//Clear product dropdown
s3.getRange(1,1,array.length,1).setValues(array)//Set new customer product list in TEMP.
drop(row,cust)// Call drop function to build new dropdown.
}
if(sh=="Sheet1" && col==2){//If sheet1 is active sheet and Product (column B) is edited.
var cust1=e.range.offset(0,-1).getValue()//Get customer in A
var prod=e.range.getValue()//Get selected product
for (var i=0;i<rng.length;i++){//Get the customer/product price
if(rng[i][0]==cust1 ){
for(var j=1;j<rng[0].length-1;j++){
if(rng[0][j]==prod){
price=rng[i][j]
s1.getRange(r,5).setValue(price)//Set the price in column E
s1.getRange(r,2).clearDataValidations() //Remove the data validation dropdown in column B
}
}}
}}}
function drop(row,cust){
var ss=SpreadsheetApp.getActiveSpreadsheet()
var s3=ss.getSheetByName("TEMP")
var s1=ss.getSheetByName("Sheet1")
var cell = s1.getRange(row);//set validation in B
var ocell=s1.getRange(row).offset(0, -1).getValue()//evaluate value in A
var cellVal=cell.getValue()
if(ocell==cust){
var lr= ss.getSheetByName("TEMP").getLastRow()
var range = ss.getSheetByName("TEMP").getRange(1, 1, lr, 1)
var rule = SpreadsheetApp.newDataValidation().requireValueInRange(range).build();//Build the dropdown
cell.setDataValidation(rule)}//Set the validation rules (Customers
products)
}
Test spreadsheet:
https://docs.google.com/spreadsheets/d/1u86sdf1_mO-Mv7hQM_hRZl3Gma-Y2lsu9ZvxSW4jA1U/edit?usp=sharing

Related

Display a dropdown menu only when a different cell contains a certain value

I'm very new to writing script for Google Sheets, and I'm attempting to create a spreadsheet that will only display a dropdown in a column ("Provisional Notes") if the value in a column ("Certified or Provisional" is "Provisional." If it is "Certified," the user is be able to enter data freely. I'm also wanting to remove the validation if the value changes from "Provisional". I also need the solution to run on the Google Sheets App, as this spreadsheet will be run on an iPad and/or a smartphone.
I've done quite a bit of searching, and only seem to find dropdowns that depend on other dropdowns, rather than leaving a cell blank if the initial dropdown is a certain value.
What I have come up with so far only runs once (even though I've attempted to have it occur on every edit?) Also, if the value changes from Provisional to Certified, the validation does not get removed.
For my practice, I've applied it to only 2 specific cells, rather than the entire 2 columns.
function onEdit2(e){
var dropdownCell = SpreadsheetApp.getActive().getRange('P2');
var certCell = SpreadsheetApp.getActive().getRange('J2');
var rule = SpreadsheetApp.newDataValidation().requireValueInList(['Test 1', 'Test 2'], true).build();
function insertDropdown(){
if(e.certCell.getValue() === "P"){
dropdownCell.setDataValidation(rule);
}
}
}
I greatly appreciate all suggestions!
To delete a data validation you just need to use the method setDataValidation() and set its value to null to delete the data validation present on that cell.
In the following code example, depending on the value inserted I am adding or deleting the data validation. This code example has self explanatory comments:
function onEdit(e) {
// get sheet
var sheet = SpreadsheetApp.getActive().getSheetByName('Sheet1');
// if modifications happen in A
if(e.range.getColumn()==1){
// check inserted value
if(e.range.getValue()=='Provisional'){
// create rule from a list range and set it to the same row in column B
var rule = SpreadsheetApp.newDataValidation().requireValueInList(['A','B']).setAllowInvalid(false).build();
sheet.getRange(e.range.getRow(),2).setDataValidation(rule);
}
// if it is set to certified
if(e.range.getValue()=='Certified'){
// delete any data validation that may have been in its adjacent cell
sheet.getRange(e.range.getRow(),2).setDataValidation(null);
}
}
}

Google Sheets - QUERY, adding a cell to hide a row

I'm hoping to use a Google Form to have my students sign up for activities. I added a column to the right of the form data where I can put an "x" once I have met with them. I have a separate tab called "Ordered" where I use QUERY to sort and show only the entries without an x. Once I meet with a student, I can put an x on the original data tab, and the entry will hide from the Ordered tab (kind of a queue for my students).
I would love to not have to switch between the 2 tabs every time I have to check someone off. Is there a way to add a similar column on the Ordered tab that will hide the finished entry?
Here is the sheet I'm referring to:
https://docs.google.com/spreadsheets/d/1fQHF0EoGLk5NEI6GvyRk4InBl-uEq0jq7qqmyOmFjy8/edit#gid=379798836
I'm sharing this sheet with other teachers who aren't familiar with spreadsheets, so the solution has to be ludite friendly.
As #Aerials said, you are likely to run into circular dependency issues here. Because of this, I'd suggest using Apps Script and get rid of the QUERY formula. You want to do the following:
Every time someone submits the form, the Ordered sheet gets updated with the new submission data.
Every time an x is added to the Form Responses 1 sheet, the corresponding row in Ordered gets removed.
Every time an x is added to the Ordered sheet, the corresponding row in Ordered gets removed, and an x gets added to the corresponding row in Form Responses 1.
A possible way to go would be something along the following lines (open the script bound to your spreadsheet by clicking Tools > Script editor):
Install an onFormSubmit trigger so that Ordered gets updated with new data every time the form is submitted. The trigger can be installed manually or programmatically, copying this function to your script and running it once:
function onFormSubmit(e) {
var ss = SpreadsheetApp.getActive();
ScriptApp.newTrigger("copyAndFilterData")
.forSpreadsheet(ss)
.onFormSubmit()
.create();
}
Once the trigger is installed, the function copyAndFilterData will execute every time the form is submitted. This function should updated Ordered based on the data in Form Responses 1, removing all the submission that have been marked as complete x. This function could be the following:
function copyAndFilterData() {
var ss = SpreadsheetApp.getActive();
var sheet1 = ss.getSheetByName("Form Responses 1");
var sheet2 = ss.getSheetByName("Ordered");
var sourceData = sheet1.getDataRange().getValues();
var filteredData = sourceData.filter(sourceRow => sourceRow[5] !== "x")
.sort((a, b) => a - b)
.map(sourceRow => {
sourceRow.pop();
sourceRow.splice(1, 1);
return sourceRow;
});
sheet2.getRange(1, 1, sheet2.getLastRow(), 4).clear({contentsOnly: true});
var destRange = sheet2.getRange(1, 1, filteredData.length, filteredData[0].length)
destRange.setValues(filteredData);
}
At this point, another piece of functionality would be needed: update the spreadsheet based on the addition or removal of x on any of both sheets. For this, an onEdit trigger would be needed, like this:
function onEdit(e) {
var editedSheet = e.range.getSheet();
if (editedSheet.getName() === "Form Responses 1") copyAndFilterData();
else if (editedSheet.getName() === "Ordered") removeRows(e, editedSheet);
}
Once this function is copied and the project saved, every time Form Responses 1 is edited, the Ordered sheets gets updated (function copyAndFilterData), and every time Ordered is edited, the function removeRows is fired. This function should do two things: (1) remove the row which was marked as complete in Ordered (x added) and (2) add the corresponding x to Form Responses 1. The submission dates can be compared to identify the row:
function removeRows(e, editedSheet) {
var range = e.range;
var column = range.getColumn();
var row = range.getRow();
var value = range.getValue();
if (column == 5 && row > 1 && value == "x") {
var date = editedSheet.getRange(row, 1).getValue();
var formSheet = e.source.getSheetByName("Form Responses 1")
var values = formSheet.getRange(2, 1, formSheet.getLastRow() - 1).getValues();
var markedRow = values.findIndex(value => value[0].getTime() === date.getTime());
formSheet.getRange(markedRow + 2, 6).setValue("x");
editedSheet.deleteRow(row);
}
}
Reference:
Overview of Google Apps Script
Simple Triggers
Installable Triggers

Using cell contents to modify drop-down menu list

I have a Google Sheets where I am saving information about universities and I have a column for regions and countries (regions or continents, however you want to see it) and what I'd like to do is have the regions column contain a drop-down menu (data validation) of all the continents and depending on what continent you select, the countries column will have the corresponding countries.
I have another sheet containing the required data with the first row being the continent names and the remaining rows being the countries in that continent.
I've tried custom formulas but ended up having something too complex that didn't work so I deleted it.
If anyone knows how I could do this with either the script editor or directly in data validation, that'd be great!
Continent and Country Dropdowns
I apologize but this is a little involved. Let's start with where I got the data. This is the website: https://datahub.io/JohnSnowLabs/country-and-continent-codes-list
I actually just copied and paste it into a Sheet named "C&C". I sorted it by continents and then by countries and I depend upon it remaining like that since it reduces the amount of effort to get the countries.
And here is the Google Script Code:
function onEdit(e) {
var dlm=" - ";
var msg="Start" + dlm;
var sh=e.range.getSheet();
if(sh.getName()!='DropDown')return;
if(e.range.getA1Notation()=='A2' && e.value) {
var dsh=e.source.getSheetByName('C&C');
var drg=dsh.getRange(1,1,dsh.getLastRow(),1);
var dvA=drg.getValues();
var cA=[];
var cStart=0;
var cEnd=0;
for(var i=0;i<dvA.length;i++) {
if(!cEnd && !cStart && dvA[i][0]==e.value) {
cStart=i+1;
msg+="cStart: " + cStart + dlm;
//e.source.toast(msg);
}
if(!cEnd && cStart && dvA[i][0]!=e.value || i==dvA.length-1) {
cEnd=i+1;
msg+="cEnd: " + cEnd + dlm;
//e.source.toast(msg);
break;
}
}
var crg=dsh.getRange(cStart,3,cEnd-cStart+1,1);
var cvA=crg.getValues();
e.source.getRangeByName('Countries').clearContent();
sh.getRange('B2').clearContent();
e.source.getSheetByName('NamedRanges').getRange(2,2,cvA.length,1).setValues(cvA);
}
msg+="Exit" + dlm;
e.source.toast(msg);
}
I left the debug toasts in there in case you want them.
Here's what the C&C page looks like:
And here's the page where I placed the NamedRanges. I named that page NamedRanges:
And here's the page with the two dropdowns:
It's displaying the South America continent selection.
Here's the NamedRanges setups:
And here's the Data Validation Setups:

Dynamic data validation based on other cell values, and copying/filling validation criteria relatively down each row (Google Sheets)

I am trying to achieve the following situation using Data Validation in Google Sheets. I've provided a truncated version of situation in the image below.
I would like to set up data validation in Column B that automatically checks the options in column C (or multiple columns in the same row) to populate the dropdown menu for that row.
I did notice that Google Sheets has an option for formulas in the data validation screen and I tried writing an array formula in this area but I have not been having any luck getting any sort of output.
If it can't be done through this menu I would appreciate any idea how to achieve this through scripts.
One way to achieve this is to select list from a range in the "Criteria" section of the Data Validation editor. Then, select the cells whose values you want to appear in your dropdown.
The only issue is that when you try to fill this down across the column, Google Sheets will not update the criteria range. Say you do the following:
In cell B2, use data validation based on list from a range and select D2:F2 as the criteria range. Now the dropdown in B2 will have the options John's Mom, John's Dad, John's Grandma
Then, fill down or copy this cell down across the whole column
When you open the dropdown for cell B3, it will still have the options John's Mom, John's Dad, John's Grandma
Unfortunately Google Sheets does not currently have a built-in solution for copying/filling data validation references or formulas relatively, as far as I know. But it looks like somebody already wrote a nice script in this Google Docs forum post. To avoid just a link as an answer, I'm going to copy in the script and instructions here. Credit to AD:AM from Google Docs forum.
How to use their script:
Select a range of cells across which you want to copy a data validation rule, relatively
From the Validation+ custom menu, select the appropriate option (all references relative, columns absolute, or rows absolute)
The validation of the upper-left cell will be copied to the rest of the range
Link to original solution's example Google Sheets with script already included - you can save your own copy and then start using.
Or to recreate from scratch, here is the script.
function onOpen()
{
SpreadsheetApp.getActiveSpreadsheet().addMenu
(
"Validation+",
[
{name: "Copy validation (all relative references)", functionName: "copyValidation"},
{name: "Copy validation (relative rows, absolute columns)", functionName: "copyValidationColumnsAbsolute"},
{name: "Copy validation (absolute rows, relative columns)", functionName: "copyValidationRowsAbsolute"}
]
);
}
function copyValidation(rowsAbsolute, columnsAbsolute)
{
var ss = SpreadsheetApp.getActiveSpreadsheet();
var r = ss.getActiveRange();
var dv = r.getDataValidations();
var dvt = dv[0][0].getCriteriaType();
if (dvt != SpreadsheetApp.DataValidationCriteria.VALUE_IN_RANGE) return;
var dvv = dv[0][0].getCriteriaValues();
Logger.log(dvv);
for (var i = 0; i < dv.length; i++)
{
for (var j = i ? 0 : 1; j < dv[0].length; j++)
{
dv[i][j] = dv[0][0].copy().withCriteria(dvt, [dvv[0].offset(rowsAbsolute ? 0 : i, columnsAbsolute ? 0 : j), dvv[1]]).build();
}
}
r.setDataValidations(dv);
}
function copyValidationRowsAbsolute()
{
copyValidation(true, false);
}
function copyValidationColumnsAbsolute()
{
copyValidation(false, true);
}

How to select a row in kendo grid by data item ID?

I need to select a specific row in kendoGrid but NOT by data-uid (as data-uid is changed when the grid dataSource is loaded again) but by the row itemID. I saw posts but they only select the row by uid which is not what I needed, I actually need to restart the HTML5 application and when grid is loaded, a specific item should be selected. This is what I've been seeing
Demo: jsfiddle.net/rusev/qvKRk/3/
e.g. the object has OrderID as ID, and every time the grid is loaded, it will be the same, unlike uid, I want to know how will I be able to select a row with OrderID, instead of uid.
You cam mix row itemID and data.uid, I guess.
var grid = $("#Grid").data("kendoGrid");
var dataItem = $("#Grid").data("kendoGrid").dataSource.get(itemID);
var row = $("#Grid").data("kendoGrid").tbody.find("tr[data-uid='" + dataItem.uid + "']");
Going along with what umais has mentioned, the better approach, since there is no built in functionality for this as of yet, would be to iterate through all the records to find the one you need. The function that I built will work even if there are pages of data. The only other way that I can think of doing this would be do do a secondary ajax call; But this works well. Note that i haven't tested it with more than 2000 records.
var dataGrid = $("#GATIPS").data("kendoGrid").dataSource;
var numOfRows = dataGrid.total();
var currentPageSize = dataGrid.pageSize();
dataGrid.pageSize(numOfRows);
var dataGridData = dataGrid.data();
for (var i = 0; i < numOfRows; i++) {
if (dataGridData[i].uid == e)
return dataGridData[i];
}
dataGrid.pageSize(currentPageSize); // reset the view
e is the UID. However this can be substituted for what ever variable you need just replace the check.
a work around that I managed to have, was to go through all rows and check which row model has that ID equal to the parameter, and then get that row data-uid and select the item through data-uid. It's working fine for me, since there were no suggestion, it's the better answer for now.
Well, accordingly to what I have done (and worked for me), and even though the work around isn't the prettiest, set one more Column, with your model id and with ClientTemplate then create any html object (div in my case) inside it give it a html id of your id, so when ever you need it, you just have to go and look with something like:
grid.dataItem($("td div#id").closest("tr"));
Because remember that the dataItem method is waiting for a selector then you get your selectedItem as regular one.
EDIT:
I forgot to say, that you should (or could) use the style property
display:none
If you don't want to display that col.

Resources