Data Validation in Apps Script (Between Two Numbers) - google-apps-script

Trying to set a cell's data validation to be between two numbers namely, 1 and the balance of items in a budget. It keeps giving the following error:
Oct 12, 2022, 9:41:14 PM Error Exception: The parameters
(number,String) don't match the method signature for
SpreadsheetApp.DataValidationBuilder.requireNumberBetween.
at applyQtyValidation(Code:242:8)
at getQtyValidationData(Code:233:3)
at onEdit(Code:59:7)
Please help...
//Global variables
var reqSheetName = "Material Requisition";
var colStructure = 1;
var colComponent = 2;
var colWorks = 3;
var colItem = 4;
var colQty = 5;
var colUnit = 6;
var colBalance = 7;
var colBudgetCode = 8;
var colDuplicate = 18;
var ss = SpreadsheetApp.getActiveSpreadsheet();
var wsBudget = ss.getSheetByName("Budget");
var wsRequisition = ss.getSheetByName(reqSheetName);
var rawData = wsBudget.getRange(5, 1, wsBudget.getLastRow(), 7).getValues();
var arrDetails = [];
function onEdit(e) {
//variables needed
var editedCell = e.range;
var sheetName = editedCell.getSheet().getName();
var r = editedCell.getRow();
var c = editedCell.getColumn();
var valFilter = editedCell.getValue();
//Set order qty
if (sheetName === reqSheetName && c === colItem && r > 11) {
getQtyValidationData(r);
}
}
function getQtyValidationData(r) {
var minQty = 1;
var maxQty = wsRequisition.getRange(r, colBalance).getValue();
var cell = wsRequisition.getRange(r, colQty).getA1Notation();
applyQtyValidation(minQty, maxQty, cell);
}
function applyQtyValidation(minQty, maxQty, cell) {
var rule = SpreadsheetApp
.newDataValidation()
.requireNumberBetween(minQty, maxQty)
.setAllowInvalid(false)
.setHelpText("Select a number between " + minQty + " and " + maxQty)
.build();
wsRequisition.getRange(cell).setDataValidation(rule);
}

This is working:
function onEdit(e) {
e.source.toast('Entry');
const sh = e.range.getSheet();
if (sh.getName() == "Sheet1" && e.range.columnStart == 4 && e.range.rowStart > 11) {
e.source.toast('Gate1');
sh.getRange(e.range.rowStart, 5).clearDataValidations();
var maxQty = sh.getRange(e.range.rowStart, 7).getValue();
var rule = SpreadsheetApp.newDataValidation().requireNumberBetween(1, maxQty).setHelpText(`Select a number between 1 and ${maxQty}`).setAllowInvalid(true).build();
sh.getRange(e.range.rowStart, 5).setDataValidation(rule);
}
}
I would have preferred to do it like this:
function onEdit(e) {
e.source.toast('Entry');
const sh = e.range.getSheet();
if (sh.getName() == "Sheet1" && e.range.columnStart == 4 && e.range.rowStart > 11) {
e.source.toast('Gate1');
sh.getRange(e.range.rowStart, 5).clearDataValidations();
var maxQty = sh.getRange(e.range.rowStart, 7).getValue();
let list = [...Array.from(new Array(maxQty).keys(),x => String(Number(x+1)))];
Logger.log(JSON.stringify(list));
var rule = SpreadsheetApp.newDataValidation().requireValueInList(list).setHelpText(`Select a number between 1 and ${maxQty}`).setAllowInvalid(false).build();
sh.getRange(e.range.rowStart, 5).setDataValidation(rule);
}
}
Demo:

Related

How cross out multiple columns on same row and format cell colour when box ticked

My script is able to cross out the first column juste after the the check box, while I'm looking to cross out the whole row.
Also, I'd like to apply a different colour when the box is ticked.
function onEdit(e){
var mySheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var myRange = e.range;
var mySheetName = mySheet.getSheetName();
var myStatus = myRange.getValue();
var currRow = myRange.getRow();
var currCol = myRange.getColumn();
var myItem = mySheet.getRange(currRow, currCol + 1);
if (
mySheetName === 'Feuille1'
&& currCol === 1
) {
if (
myStatus
) {
myItem.setFontLine("line-through");
} else {
myItem.setFontLine(null);
};
};
};
Set Line Through
function onEdit(e) {
//e.source.toast("Entry")
const sh = e.range.getSheet();
if (sh.getName() == 'Feuille1' && e.range.columnStart == 1) {
let rg = sh.getRange(e.range.rowStart, 2, 1, sh.getLastColumn() - 1);
if(e.value == "TRUE") {
rg.setFontLine('line-through')
} else {
rg.setFontLine('none')
}
}
}
Demo:

columnEdit not applying to my range of columns

Script works really well but I have 12 columns and this script only works on A and B.
function onEditTwoWay(e) {
var ss = SpreadsheetApp;
var sheet = ss.getActiveSpreadsheet();
var sheet1 = sheet.getSheetByName("Dep1");
var sheet2 = sheet.getSheetByName("Dep2");
var cell = sheet.getActiveCell();
var value = cell.getValue();
var currentRow = cell.getRow();
var currentColumn = cell.getColumn();
var activeWorksheet = ["Dep1","Dep2"];
var columnEdit = [1,2,12];
if(activeWorksheet.indexOf(ss.getActiveSheet().getName()) > -1 &&
columnEdit.indexOf(currentColumn) > -1 && currentRow > 1) {
sheet1.getRange(currentRow, currentColumn).setValue(value);
sheet2.getRange(currentRow, currentColumn).setValue(value);
};
};
Try this:
function onEdit(e) {
const sh = e.range.getSheet();
var shts = ["Dep1", "Dep2"];
var cols = [1, 2, 12];
if (~shts.indexOf(sh.getName()) && ~cols.indexOf(e.range.columnStart) && e.range.rowStart > 1) {
shts.forEach(name => {
e.source.getSheetByName(name).getRange(e.range.rowStart, e.range.columnStart).setValue(e.value);
});
}
}
All columns need to be entered in columnEdit so changed from
columnEdit = [1,2,12] to columnEdit = [1,2,3,4,5,6,7,8,9,10,11,12]

Change cell color in SheetA when value changes in SheetB in Google Sheets

I have 2 sheets: SheetA and SheetB with identical values in corresponding cells. I'm trying to change the cell color in SheetA when the value of the cell in SheetB changes. For example if cell A5 has value 10 in both sheets and I then change it to 5 in SheetB, I want the background color of the cell in SheetA to change
This is the code so far
function onEdit(e){
var sheetsToWatch = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("SheetA");
var sheetsToEdit = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("SheetB");
var cell = sheetsToEdit.getActiveCell(); // checking the active cell
var bg = '#faec15'
for (let i = 0; i < active_sheet.length; i++) {
if (sheetsToWatch.match(sheetsToEdit[i])) {
sheetsToWatch.getRange(cell.getRow(), cell.getColumn()).setBackground(bg);
} else {
return;
}
}
}
It can be done this way:
function onEdit(e){
var sheetB = e.source.getActiveSheet();
var sheetB_name = sheetB.getName();
if (sheetB_name != 'SheetB') return;
var sheetB_cell_value = e.value;
var sheetB_cell_row = e.range.rowStart;
var sheetB_cell_col = e.range.columnStart;
var sheetA = e.source.getSheetByName('SheetA');
var sheetA_cell = sheetA.getRange(sheetB_cell_row,sheetB_cell_col);
var sheetA_cell_value = sheetA_cell.getValue();
if (sheetB_cell_value != sheetA_cell_value) sheetA_cell.setBackground('#faec15');
}
function onEdit(e){
const sh = e.range.getSheet();
if(sh.getName() == 'SheetB' ) {
e.source.getSheetByName('SheetA').getRange(e.range.rowStart,e.range.columnStart).setBackground("#faec15");
}
}
The code works now. Please find my solution below
function onEdit() {
var sheetsToWatch = ['SheetA'];
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var cell = sheet.getActiveCell();
var row = cell.getRowIndex();
var column= cell.getColumnIndex();
var sheetName = sheet.getName();
var matchFound = false;
for (var i = 0; i < sheetsToWatch.length; i++) {
if (sheetName.match(sheetsToWatch[i])) matchFound = true;
}
if (!matchFound) return;
var sheetToEdit = ss.getSheetByName('SheetB');
var editRow = sheetToEdit.getLastRow();
var editColumn = sheetToEdit.getLastColumn();
for (var j = 1; j <= editRow; j++) {
for (var k = 1; k <= editColumn; k++){
var formula = sheetToEdit.getRange(j,k).getFormula();
var rowi= parseInt(formula.replace(/[^0-9]/g,''),10);
if (rowi == row){
if (k == column){
sheetToEdit.getRange(j,k).setBackground('#faec15');
}
};
}
}
}

Mapping dropdown list dynamically to the conditions in Google sheets [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
Improve this question
The dropdown list I want should be dynamic. So based on the options I choose in columns D and E, the dropdownlist in column E should show only the products that are in column C. Below i added the code where you can see how I have done the other dropdown lists.
var mainWsName = "Inkoop";
var wsVerkoop = "Verkoop"
var sittardWsName = "Sittard";
var merkColumn = 4;
var modelColumn = 5;
var categorieColumn = 6;
var ws = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(mainWsName);
var wVerkoop = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(wsVerkoop);
var wsSittard = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sittardWsName);
var arraySittard = wsSittard.getRange(2,1,wsSittard.getLastRow()-1,3).getValues();
function categorie(){
}
function onEdit(e){
var activeCell = e.range;
var val = activeCell.getValue();
var r = activeCell.getRow();
var c = activeCell.getColumn();
var wsName = activeCell.getSheet().getSheetName()
if(wsName == mainWsName && c == merkColumn && r > 1){
if(val === ""){
ws.getRange(r ,modelColumn).clearContent();
ws.getRange(r ,modelColumn).clearDataValidations();
ws.getRange(r ,categorieColumn).clearContent();
} else{
ws.getRange(r ,modelColumn).clearContent();
var filterarraySittard = arraySittard.filter(function(o){return o[0] === val});
var listToApply = filterarraySittard.map(function(o){ return o[1]});
var cell = ws.getRange(r ,modelColumn);
applyValidationToCell(listToApply, cell);
}
}
if(wsName == wsVerkoop && c == merkColumn && r > 1){
if(val === ""){
wVerkoop.getRange(r ,modelColumn).clearContent();
wVerkoop.getRange(r ,modelColumn).clearDataValidations();
wVerkoop.getRange(r ,categorieColumn).clearContent();
} else{
wVerkoop.getRange(r ,modelColumn).clearContent();
var filterarraySittard = arraySittard.filter(function(o){return o[0] === val});
var listToApply = filterarraySittard.map(function(o){ return o[1]});
var cell = wVerkoop.getRange(r ,modelColumn);
applyValidationToCell(listToApply, cell);
}
}
} // end onEdit
function applyValidationToCell(list,cell){
var rule = SpreadsheetApp.newDataValidation()
.requireValueInList(list)
.setAllowInvalid(false)
.build();
cell.setDataValidation(rule);
}
Try this:
Code:
var dstName = 'Sheet1';
var srcName = 'Sheet2';
var sprdSheet = SpreadsheetApp.getActiveSpreadsheet();
var dstSheet = sprdSheet.getSheetByName(dstName);
var srcSheet = sprdSheet.getSheetByName(srcName);
function populateMerk() {
var merk = dstSheet.getRange("D2:D");
var merkList = getData("", "A");
var rangeRule = SpreadsheetApp.newDataValidation().requireValueInList(merkList);
merk.setDataValidation(rangeRule);
}
function populateModel(id, row, sheetName) {
var model = dstSheet.getRange("E" + row);
var modelList = getData(id, "B");
var rangeRule = SpreadsheetApp.newDataValidation().requireValueInList(modelList);
var category = model.offset(0, 1);
var mValue = model.getValue();
model.clearDataValidations();
model.clearContent();
model.setDataValidation(rangeRule);
if (sheetName == dstName) {
category.clearDataValidations();
category.clearContent();
}
else if (sheetName == srcName) {
model.setValue(mValue);
}
}
function populateCategory(id, row, sheetName) {
var category = dstSheet.getRange("F" + row);
var categoryList = getData(id, "C");
var rangeRule = SpreadsheetApp.newDataValidation().requireValueInList(categoryList);
var cValue = category.getValue();
category.clearDataValidations();
category.clearContent();
category.setDataValidation(rangeRule);
if (sheetName == srcName) {
category.setValue(cValue);
}
}
function getData(srcId, endCol) {
var srcLastRow = srcSheet.getLastRow();
var srcData = srcSheet.getRange("A2:" + endCol + srcLastRow).getValues();
var dataList = [];
if (!srcId) {
// Get unique items for merk
dataList = srcData.flat().filter((item, i, ar) => ar.indexOf(item) === i);
}
else {
srcData.forEach(function(row) {
// Only add unique models
if (srcId == row[0] && !dataList.includes(row[1]) && !row[2]) {
dataList.push(row[1]);
}
// Only add unique categories
else if (srcId == (row[0] + row[1]) && !dataList.includes(row[2])) {
dataList.push(row[2]);
}
});
}
return dataList;
}
function onEdit(e) {
var sheetName = e.source.getActiveSheet().getName();
var range = e.range;
var col = range.getColumn();
var row = range.getRow();
if (row > 1) {
if (sheetName == dstName) {
var merk = dstSheet.getRange(row, 4).getValue();
var model = dstSheet.getRange(row, 5).getValue();
if (col == 4) {
populateModel(merk, row, sheetName);
}
else if (col == 5) {
populateCategory(merk + model, row);
}
}
else if (sheetName == srcName) {
switch (col) {
case 1:
populateMerk();
break;
case 2:
var lastE = dstSheet.getRange("E2:E").getValues().filter(String).length;
var merk;
for (var i = 0; i < lastE; i++) {
merk = dstSheet.getRange("D" + (i + 2)).getValue();
populateModel(merk, i + 2, sheetName);
}
break;
case 3:
var lastF = dstSheet.getRange("F2:F").getValues().filter(String).length;
var merk, model, row;
for (var i = 0; i < lastF; i++) {
row = i + 2;
merk = dstSheet.getRange("D" + row).getValue();
model = dstSheet.getRange("E" + row).getValue();
populateCategory(merk + model, row, sheetName);
}
break;
}
}
}
}
Sample data in Sheet2:
Outputs in Sheet1:
Note:
The code also notes when you edit your source sheet. If updated, will update all dropdowns for that column in the destination sheet. And since there will be a lot of calls when this happens, it will be a bit slow. You can still optimize that but for now, this still works.
Also, please see the references below and learn how we do things in StackOverflow as per Cooper's comment above:
References:
How to Ask
Code Formatting
Minimal Reproducible Example
Learn More

Google Sheets script - code optimization - one script, two sheets, same workbook

I have one workbook with multiple sheets and I've been running some simple scripts on two of those sheets (those sheets are basically copies of one another).
Once script creates a dropdown based on the value entered in one of the cells, while the second script adds a timestamp based on when the new dropdown was edited.
Initially I had two onEdit functions running on the first sheet.
Then I created a copy of these functions, with small amendments, to run on another sheet(same workbook)
Given that I don't really know how to have a separate script per worksheet I now have a one script with 4 similar onEdit functions.
Since I'm a total newbie I'm sure there's a better, more efficient way to write this code.
I'd appreciate your help in optimizing this.
function onEdit(e) {
addTimestamp(e);
addTimestamp2(e);
Dropdown(e);
Dropdown2(e);
}
function addTimestamp(e) {
var startRow = 2;
var targetColumn = 10;
var ws = "Tracker1";
var row = e.range.get.Row();
var col = e.range.getColumn();
if(col === targetColumn && row >= startRow && e.source.getActiveSheet().getName() === ws) {
var currentDate = new Date();
e.source.getActiveSheet().getRange(row,11).setValue(currentDate);
}
}
function addTimestamp2(e) {
var startRow = 2;
var targetColumn = 10;
var ws = "Tracker2";
var row = e.range.get.Row();
var col = e.range.getColumn();
if(col === targetColumn && row >= startRow && e.source.getActiveSheet().getName() === ws) {
var currentDate = new Date();
e.source.getActiveSheet().getRange(row,11).setValue(currentDate);
}
}
function Dropdown(){
var tabLists = "StatusFlow";
var tabValidation = "Tracker1"
var ss = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var datass = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(tabLists);
var activeCell = ss.getActiveCell();
if(activeCell.getColumn() == 9 && activeCell.getRow() > 1 && ss.getSheetName() == tabValidation){
activeCell.offset(o,1).clearContent().clearDataValidations();
var makes = datass.getRange(1,1,1, datass.getLastColumn()).getValues();
var makeIndex = makes[0].indexOf(activeCell.getValue()) +1;
if(makeIndex !=0) {
var validationRange = datass.getRange(2, makeIndex, datass.getLastRow());
var validationRule = SpreadsheetApp.newDataValidation().requireValueInRange(validationRange).setAllowInvalid(false).build();
activeCell.offset(0, 1).setDataValidation(validationRule);
}
}
}
function Dropdown2(){
var tabLists = "StatusFlow";
var tabValidation = "Tracker2"
var ss = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var datass = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(tabLists);
var activeCell = ss.getActiveCell();
if(activeCell.getColumn() == 9 && activeCell.getRow() > 1 && ss.getSheetName() == tabValidation){
activeCell.offset(o,1).clearContent().clearDataValidations();
var makes = datass.getRange(1,1,1, datass.getLastColumn()).getValues();
var makeIndex = makes[0].indexOf(activeCell.getValue()) +1;
if(makeIndex !=0) {
var validationRange = datass.getRange(2, makeIndex, datass.getLastRow());
var validationRule = SpreadsheetApp.newDataValidation().requireValueInRange(validationRange).setAllowInvalid(false).build();
activeCell.offset(0, 1).setDataValidation(validationRule);
}
}
}
here is a more efficient way to replace the two addTimestamp functions:
function onEdit(e) {
var activeSheet = e.source.getActiveSheet();
var activeSName = activeSheet.getName();
var row = e.range.get.Row();
var col = e.range.getColumn();
if ((activeSName == 'Tracker1' || activeSName == 'Tracker2') && col === 10 && row >= 2) {
activeSheet.getRange(row, 11).setValue(new Date());
}
}
You could give the two Dropdown functions a shot and see if you can do something similar. Nothing like learning-by-doing. :-D
Just be careful that in onEdit you are passing e to these Dropdown functions. But not using them in these functions.