I have a macro running through a forEach loop that works but always ends on the last sheet in my workbook.
How do I get it to finish on a specified sheet?
I tried a few variations of getSheetbyname
but I don't really know where to put it.
This is the code:
function ResetPage() {
var sheet = SpreadsheetApp.getActive();
var racenumber = sheet.getRange('B2');
var venue = sheet.getRange('E2');
venue.clearContent();
racenumber.activate();
sheet.getCurrentCell().setValue('1');
}
function doForAllTabs() {
var spreadsheet = SpreadsheetApp.getActive();
var allsheets = spreadsheet.getSheets();
allsheets.forEach(function(workbook) {
if(workbook.getSheetName() !== "Venues") {
workbook.activate();
ResetPage();
}
});
}
You don't need to set each sheet active to change the values. The macro recorder does it but it makes the process slower.
This should do the updates without changing your active cell:
function ResetPage(sheet) {
var racenumber = sheet.getRange('B2');
var venue = sheet.getRange('E2');
racenumber.setValue(1);
venue.clearContent();
}
function doForAllTabs() {
var spreadsheet = SpreadsheetApp.getActive();
var allsheets = spreadsheet.getSheets();
allsheets.forEach(function(workbook) {
if(workbook.getSheetName() !== "Venues") {
ResetPage(workbook);
}
});
}
function doForAllTabs() {
SpreadsheetApp.getActive().getSheets().forEach(sh => {
if (sh.getName() != "Venues") {
sh.getRange('B2').setValue(1);
sh.getRange('E2').setValue('');
}
});
}
Related
Our company works at different properties repairing appliances, I would like to build a database to search up the information on each appliance at specific properties and in their specific apt/units, I created a form to start this process, but I need help with some complex coding.
I first created a box for the property, then I created an "Apt/Unit" box. The idea is when I select a property, the units tied to that property are shown in dropdown/type searchable list in the Apt/Unit box.
I then created an "Appliance type" box. The idea is when the "Apt/Unit" is selected, it will display the dropdown/type searchable list of the appliances tied to that specific "Apt/Unit".
Then I created boxes for the info for the appliance (Brand, Model #, Serial #, & Color), this is a bit more self-explanatory - once the appliance type is selected, it will display the respective information for each box for that appliance.
Here's the link to the Google sheet: https://docs.google.com/spreadsheets/d/1JZhEYjk5xVN3uOc_Ucb8HFr6d96XQ2Q_ehAd-d_o0ME/edit?usp=sharing
Any help is appreciated!
non-scripted solution:
=IFERROR({INDEX(IFERROR(Data!A1:G1/0)); Data!A1:G1; QUERY({Data!A2:G}, "where 1=1 "&
IF(C10="",,"and lower(Col1) contains '"&LOWER(C10)&"'")&
IF(C12="",,"and Col2 = "&C12)&
IF(C14="",,"and lower(Col3) contains '"&LOWER(C14)&"'")&
IF(C16="",,"and lower(Col4) contains '"&LOWER(C16)&"'")&
IF(C18="",,"and lower(Col5) contains '"&LOWER(C18)&"'")&
IF(C20="",,"and lower(Col6) contains '"&LOWER(C20)&"'")&
IF(C22="",,"and lower(Col7) contains '"&LOWER(C22)&"'"), 0)}, {"";"no data"})
demo sheet
Here is third variant of the script:
// global variables
var SS = SpreadsheetApp.getActiveSpreadsheet();
var SHEET_USERFACE = SS.getSheetByName('Userface');
var SHEET_DATA = SS.getSheetByName('Data');
function onLoad() { reset() }
function reset() {
SS.toast('Please wait...');
SHEET_USERFACE.getRange('c9:c21').clearContent();
SHEET_USERFACE.getRange('c9:c13').clearDataValidations();
var obj = make_obj_from_data();
update_menu_prop(obj);
update_menu_unit(obj);
update_menu_type(obj);
SS.toast('The sheet has been reset');
}
function onEdit(e) {
if (e.range.getSheet().getName() != 'Userface') return;
if (e.range.columnStart != 3) return;
// Property menu
if (e.range.rowStart == 9) {
e.source.toast('Please, wait...');
SHEET_USERFACE.getRange('c11:c21').clearContent();
SHEET_USERFACE.getRange('c11:c13').clearDataValidations();
var obj = make_obj_from_data();
update_menu_unit(obj);
update_menu_type(obj);
e.source.toast('The sheet has been updated');
}
// Apt/Unit menu
if (e.range.rowStart == 11) {
e.source.toast('Please, wait...');
SHEET_USERFACE.getRange('c13:c21').clearContent();
SHEET_USERFACE.getRange('c13').clearDataValidations();
var obj = make_obj_from_data();
update_menu_type(obj);
e.source.toast('The sheet has been updated');
}
// Applicance type menu
if (e.range.rowStart == 13) {
e.source.toast('Please, wait...');
SHEET_USERFACE.getRange('c15:c21').clearContent();
var obj = make_obj_from_data();
update_brand_model_serial_color(obj);
e.source.toast('The sheet has been updated');
}
}
function make_obj_from_data() {
var data = SHEET_DATA.getDataRange().getValues().slice(1);
var obj = {};
for (let row of data) {
var [prop, unit, type, ...etc] = row;
try {
obj[prop][unit][type] = etc;
}
catch(e) {
try {
obj[prop][unit] = {}; obj[prop][unit][type] = etc;
}
catch(e) {
obj[prop] = {}; obj[prop][unit] = {}; obj[prop][unit][type] = etc;
}
}
}
return obj;
}
function update_menu_prop(obj) {
var cell = SHEET_USERFACE.getRange('c9');
try {
var list = Object.keys(obj);
set_data_validation(cell, list);
} catch(e) {
console.log('update_menu_prop(obj)');
console.log(e);
}
}
function update_menu_unit(obj) {
var prop = SHEET_USERFACE.getRange('c9').getValue();
var cell = SHEET_USERFACE.getRange('c11');
try {
var list = Object.keys(obj[prop]);
set_data_validation(cell, list);
} catch(e) {
console.log('update_menu_unit(obj)');
console.log(e);
}
}
function update_menu_type(obj) {
var prop = SHEET_USERFACE.getRange('c9').getValue();
var unit = SHEET_USERFACE.getRange('c11').getValue();
var cell = SHEET_USERFACE.getRange('c13');
try {
var list = Object.keys(obj[prop][unit]);
set_data_validation(cell, list);
if (list.length == 1) update_brand_model_serial_color(obj)
} catch(e) {
console.log('update_menu_type(obj)');
console.log(e);
}
}
function update_brand_model_serial_color(obj) {
var [prop,,unit,,type] = SHEET_USERFACE.getRange('c9:c13').getValues();
try {
var [brand, model, serial, color] = obj[prop][unit][type];
var arr = [[brand],[''],[model],[''],[serial],[''],[color]];
SHEET_USERFACE.getRange('c15:c21').setValues(arr);
} catch(e) {
console.log('update_brand_model_serial_color(obj)');
console.log(e);
}
}
function set_data_validation(cell, list) {
var rule = SpreadsheetApp.newDataValidation().requireValueInList(list).build();
cell.setDataValidation(rule);
// put the value in the cell if there is just one element in the list
if (list.length == 1) cell.setValue(list[0]);
}
Here is my sheet.
It works about that way as it does any similar interface. You select the first menu and it changes data validation for the second menu and cleans the third menu. Then you select the second menu and it changes the third one. As soon as you change the third menu it fills the rest fields.
Since you're using just the three menus and they supposed to be changed step by step I decided to 'hardcode' them. It's not the best practice and there can be problems if/when you decide to change the functionality. But for this particular case I think the 'hardcoding' is forgivable. It works relatively fast and the code is relatively readable.
Just for fun I've made it. But this is overkill:
// global variables
var SS = SpreadsheetApp.getActiveSpreadsheet();
var SHEET_USERFACE = SS.getSheetByName('Userface');
var SHEET_DATA = SS.getSheetByName('Data');
function onLoad() { reset() }
function onEdit(e) {
if (e.range.getSheet().getName() != 'Userface') return;
if (e.range.columnStart != 3) return;
if (![9,11,13,15,17,19,21].includes(e.range.rowStart)) return;
e.source.toast('Please, wait...');
set_filter(e.range.offset(0,-1).getValue(), e.value);
set_all_menus();
e.source.toast('The sheet has been updated');
}
function reset() {
SS.toast('Please wait...');
try { SHEET_DATA.getFilter().remove() } catch(e) {}
SHEET_USERFACE.getRange('c9:c21').clearContent().clearDataValidations();
set_all_menus();
SS.toast('The sheet has been updated');
}
function set_all_menus() {
var data = SHEET_DATA.getDataRange().getDisplayValues().filter((_,i) => !SHEET_DATA.isRowHiddenByFilter(i+1));
set_menu(data, 'b9', 'c9');
set_menu(data, 'b11', 'c11');
set_menu(data, 'b13', 'c13');
set_menu(data, 'b15', 'c15');
set_menu(data, 'b17', 'c17');
set_menu(data, 'b19', 'c19');
set_menu(data, 'b21', 'c21');
}
function set_menu(data, title, cell) {
var menu_title = SHEET_USERFACE.getRange(title).getValue();
var menu_cell = SHEET_USERFACE.getRange(cell);
var col_index = data[0].indexOf(menu_title);
var menu_list = [...new Set([...data.map(e => e[col_index])])].slice(1);
var menu_rule = SpreadsheetApp.newDataValidation().requireValueInList(menu_list).build();
menu_cell.setDataValidation(menu_rule);
}
function set_filter(column_title, value) {
// get all the data and col index
var [header, ...data] = SHEET_DATA.getDataRange().getValues();
var col_index = header.indexOf(column_title);
// unhide all values of the given column
var clear = SpreadsheetApp.newFilterCriteria().setHiddenValues([]).build();
var range = SHEET_DATA.getDataRange();
var filter = range.getFilter() || range.createFilter()
filter.setColumnFilterCriteria(col_index+1, clear);
// get the values to hide
var col_data = data.map(e => e[col_index]);
var filtered = col_data.filter( (e, i) => e != value && SHEET_DATA.isRowHiddenByFilter(i+1) );
var to_hide = col_data.filter( e => e != value );
var hidden = [...new Set([...filtered, ...to_hide])];
// hide the values with the filter
var criteria = SpreadsheetApp.newFilterCriteria().setHiddenValues(hidden).build();
var range = SHEET_DATA.getDataRange();
var filter = range.getFilter() || range.createFilter()
filter.setColumnFilterCriteria(col_index+1, criteria);
}
Here is the sheet.
It works quite slow. I'd propose to use the native filters instead. Basically the script turns on and off the filters an changes data validation for the dropdown menus respectively.
Update
Here another version of the script. It works much faster but it uses the 'helper sheet' to store temporary data (the filtered table). You can hide the 'helper sheet' if you want.
// global variables
var SS = SpreadsheetApp.getActiveSpreadsheet();
var SHEET_USERFACE = SS.getSheetByName('Userface');
var SHEET_DATA = SS.getSheetByName('Data');
var SHEET_HELPER = SS.getSheetByName('Helper'); // the hidden sheet with temp data
var PROPERTY_LIST = [...new Set(SHEET_DATA.getRange('a2:a').getValues().flat())]; // 'Property' list
var DATA_OBJ = {};
function onLoad() { reset() }
function onEdit(e) {
var {range, source, value} = e;
if (range.getSheet().getName() != 'Userface') return;
if (range.columnStart != 3) return;
if (![9,11,13,15,17,19,21].includes(range.rowStart)) return;
source.toast('Please, wait...');
// reset whenever the first menu is changing
if (range.rowStart == 9) {
reset();
source.getRange('c9').setValue(value);
}
var col_header = range.offset(0,-1).getValue();
update_sheet_helper(col_header, value);
update_all_dropdown_menus();
source.toast('The sheet has been updated');
}
function reset() {
SS.toast('Please wait...');
// copy data from SHEET_DATA to SHEET_HELPER
SHEET_USERFACE.getRange('c9:c21').clearContent().clearDataValidations();
SHEET_DATA.getDataRange().copyTo(SHEET_HELPER.clearContents().getRange(1,1));
update_data_obj();
update_all_dropdown_menus();
SS.toast('The sheet has been updated');
}
// make DATA_OBJECT from SHEET_HELPER
function update_data_obj() {
DATA_OBJ = {};
var [header, ...data] = SHEET_HELPER.getDataRange().getValues();
for (let i in header) DATA_OBJ[header[i]] = data.map(e => e[i]);
DATA_OBJ['Property'] = PROPERTY_LIST; // let 'Property' list will be full always
}
// remove from SHEET_DATA_HELPER all the rows
// that have no given value in column with given title
function update_sheet_helper(col_title, value) {
var [header, ...data] = SHEET_HELPER.getDataRange().getValues();
var col_index = header.indexOf(col_title);
data = data.filter(k => k[col_index] == value);
var table = [header, ...data];
SHEET_HELPER.clearContents().getRange(1,1,table.length, table[0].length).setValues(table);
update_data_obj();
}
function update_all_dropdown_menus() {
SHEET_USERFACE.getRange('b9:c21').getValues().forEach((row,i) => {
if (row[0] != '') set_data_validation(DATA_OBJ[row[0]], 'c' + (i+9));
});
function set_data_validation(data, cell_address) {
var menu_list = [...new Set([...data])]; // remove duplicates from the array
var menu_rule = SpreadsheetApp.newDataValidation().requireValueInList(menu_list).build();
var cell_range = SHEET_USERFACE.getRange(cell_address)
cell_range.setDataValidation(menu_rule);
if (menu_list.length == 1) cell_range.setValue(menu_list[0]);
}
}
The sheet is here.
I am trying to implement the following function on Open sheet but I want to change the tab color on all the sheets. With this function, it allows me to change color only to single tab.
function getFirstEmptyRow() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheetname = "Section A";
var sheet = ss.getSheetByName(sheetname);
var column = sheet.getRange('F:F');
var values = column.getValues(); // get all data in one call
var ct = 0;
while ( values[ct][0] != "" ) {
ct++;
}
Logger.log("Row "+ ct);
var ax=sheet.getRange(ct, 7).getValue();
if(ax == ""){
sheet.setTabColor("ff0000")
} else {
sheet.setTabColor(null)
}
}
Try this:
function onOpen() {
var sheets = SpreadsheetApp.getActiveSpreadsheet().getSheets();
sheets.forEach(s => s.setTabColor("ff0000"));
}
It hard to tell from the provided code, probably you need this:
function onOpen() {
var sheets = SpreadsheetApp.getActiveSpreadsheet().getSheets();
for (var sheet of sheets) {
var data = sheet.getDataRange().getValues(); // get all data
var col_G = data.map(x => x[6]); // get column G
var last_cell = col_G.pop(); // get last cell of column F
if (last_cell == '') sheet.setTabColor("ff0000");
}
}
Or even shorter:
function onOpen() {
var sheets = SpreadsheetApp.getActiveSpreadsheet().getSheets();
for (var sheet of sheets) {
var data = sheet.getDataRange().getValues();
if (data.pop()[6] == '') sheet.setTabColor("ff0000");
}
}
It will make a tab red if in last row that of the table the cell in column G is empty.
If you have several sheets that you want to change you can filter them by names this way:
var names = ['Sheet1', 'Sheet2', 'Sheet3'];
var sheets = SpreadsheetApp.getActiveSpreadsheet().getSheets();
sheets = sheets.filter(s => names.includes(s.getName()));
// the rest of a code
I'm assuming you want to change the tab color for all sheets if the value in column F of the last row of any sheet is blank. There are other values in that row. I've include onOPen, onEdit and onChange.
function onOpen(e) {
// onOpen is a simple trigger and doesn't need to be installed
// don't really use e in this scenario
getFirstEmptyRow();
}
function onEdit(e) {
// onEdit is a simple trigger and doesn't need to be installed
var sheet = e.range.getSheet();
if( e.range.getRow() === sheet.getDataRange().getLastRow() ) {
if( e.range.getColumn() === 6 ) {
if( e.value === "" ) getFirstEmptyRow();
}
}
}
function onChange(e) {
// onChange is an installed trigger
// don't really use e in this scenario
getFirstEmptyRow();
}
function getFirstEmptyRow() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheets = ss.getSheets();
var i=0;
var sheet = null;
var value = null;
var color = null;
for( i=0; i<sheets.length; i++ ) {
sheet = sheets[i];
value = sheet.getRange(sheet.getDataRange().getLastRow(),7).getValue();
color = value === "" ? "ff0000" : null;
sheet.setTabColor(color);
}
}
I found this amazing masterpiece of #Cooper to search for duplicates based on values in 1 column. Now instead of removing these duplicates as the code does, I want to move them to another sheet. Any idea how this can be done?
Thanks :).
Here is the code:
function removeDuplicates() {
var sh=SpreadsheetApp.getActiveSheet();
var dt=sh.getDataRange().getValues();
var uA=[];
var d=0;
for(var i=0;i<dt.length;i++) {
if(uA.indexOf(dt[i][0])==-1) {
uA.push(dt[i][0]);
}else{
sh.deleteRow(i+1-d++);
}
}
}
Move row
function removeDuplicates() {
const ss = SpreadsheetApp.getActive();
const dsh = ss.getSheetByName('Destination');
var sh=SpreadsheetApp.getActiveSheet();
var dt=sh.getDataRange().getValues();
var uA=[];
let d = 0;
for(var i=0;i<dt.length;i++) {
if(uA.indexOf(dt[i][0])==-1) {
uA.push(dt[i][0]);
}else{
dsh.appendRow(dt[i]);//append
sh.deleteRow(i+1-d++);//delete
}
}
}
Sample of the page I am usingI use a conditional format across my sheets but want to change it to Google Script instead.
For every check out date that does not have a Y, highlight yellow.
I managed to modify a script to highlight a cell based on two conditions for A:D but cannot figure out how to extend it to the other ranges in my sheet.
Here is what I have so far that I found :
function onEdit(e) {
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getRange("A:D");
var values = range.getValues();
//for each row that data is present
for(var i = 0; i < values.length; i++) {
var cell = sheet.getRange(i + 1, 4);
if(values[i][1] !== "") {
if(values[i][3] === "") {
cell.setBackground('yellow');
} else {
cell.setBackground(null);
}
} else {
cell.setBackground(null);
}
}
}
Links: Google App Script - Conditional Formatting Based on Another Cell
Try this:
function onEdit(e) {
//e.source.toast('Entry');
var sh=e.range.getSheet();
if(sh.getName()=="Sheet2") {
//e.source.toast('Conditional');
var vs=sh.getRange(1,1,sh.getLastRow(),4).getValues();
vs.forEach(function(r,i){
let rg=sh.getRange(i+1,4);
if(r[1]=="") {
if(r[3]=="") {
//e.source.toast('yellow');
rg.setBackground('#ffff00');
}else{
//e.source.toast('white');
rg.setBackground('#ffffff');
}
}else{
//e.source.toast('r[1] not null');
rg.setBackground('#ffffff');
}
});
}
}
I tested this a little and it seems to work
function onEdit(e) {
//e.source.toast('Entry');
var sh=e.range.getSheet();
if(sh.getName()=="Sheet2") {
//e.source.toast('Conditional');
var v=sh.getRange(6,1,sh.getLastRow()-5,sh.getLastColumn()).getValues();
var r0=sh.getRange(6,4,sh.getLastRow()-5,1);
var c0=r0.getBackgrounds();
var r1=sh.getRange(6,9,sh.getLastRow()-5,1);
var c1=r1.getBackgrounds();
var r2=sh.getRange(6,14,sh.getLastRow()-5,1);
var c2=r2.getBackgrounds();
var r3=sh.getRange(6,19,sh.getLastRow()-5,1);
var c3=r3.getBackgrounds();
var r4=sh.getRange(6,24,sh.getLastRow()-5,1);
var c4=r4.getBackgrounds();
v.forEach(function(r,i){
c0[i][0]=(r[1]=="" && r[3]=="")?'#ffff00':'#ffffff';
c1[i][0]=(r[6]=="" && r[8]=="")?'#ffff00':'#ffffff';
c2[i][0]=(r[11]=="" && r[13]=="")?'#ffff00':'#ffffff';
c3[i][0]=(r[16]=="" && r[18]=="")?'#ffff00':'#ffffff';
c4[i][0]=(r[21]=="" && r[23]=="")?'#ffff00':'#ffffff';
});
r0.setBackgrounds(c0);
r1.setBackgrounds(c1);
r2.setBackgrounds(c2);
r3.setBackgrounds(c3);
r4.setBackgrounds(c4);
}
}
Try this for the entire spreadsheet:
function onEdit(e) {
//e.source.toast('Entry');
var sh=e.range.getSheet();
//e.source.toast('Conditional');
var v=sh.getRange(6,1,sh.getLastRow()-5,sh.getLastColumn()).getValues();
var r0=sh.getRange(6,4,sh.getLastRow()-5,1);
var c0=r0.getBackgrounds();
var r1=sh.getRange(6,9,sh.getLastRow()-5,1);
var c1=r1.getBackgrounds();
var r2=sh.getRange(6,14,sh.getLastRow()-5,1);
var c2=r2.getBackgrounds();
var r3=sh.getRange(6,19,sh.getLastRow()-5,1);
var c3=r3.getBackgrounds();
var r4=sh.getRange(6,24,sh.getLastRow()-5,1);
var c4=r4.getBackgrounds();
v.forEach(function(r,i){
c0[i][0]=(r[1]=="" && r[3]=="")?'#ffff00':'#ffffff';
c1[i][0]=(r[6]=="" && r[8]=="")?'#ffff00':'#ffffff';
c2[i][0]=(r[11]=="" && r[13]=="")?'#ffff00':'#ffffff';
c3[i][0]=(r[16]=="" && r[18]=="")?'#ffff00':'#ffffff';
c4[i][0]=(r[21]=="" && r[23]=="")?'#ffff00':'#ffffff';
});
r0.setBackgrounds(c0);
r1.setBackgrounds(c1);
r2.setBackgrounds(c2);
r3.setBackgrounds(c3);
r4.setBackgrounds(c4);
}
Keep in mind if you have multiple people editing the sheet then you are going to be missing some edits because the script will not be able to keep up.
I hire out cameras to film students within a university. To keep track of what I have in stock and which students have a camera, I have created a google sheet.
The extended version of this example includes over a hundred cameras, so to make my life easier, I'd like to enter into the BASKET the name of the cameras being hired, then type the name of the student hiring it out into the NAME cell.
Students may hire out more than one camera at a time, and so, upon clicking the 'Check Out' button which in turn triggers the function 'checkOut', I would like to change the cell next to the cameras referenced from 'Available' to the students name.
I'm new to Google Apps Script but I know the first step to this is listing each variable needed, then find the basket values within the Equipment List and replace the offset cell with the Name value.
Here are my variables:
var ss = SpreadsheetApp.getActiveSpreadsheet();
var basket = ss.getRange("A2:A");
var name = ss.getRange("C2");
var equipmentlist = ss.getRange("E2:E11");
function checkOut() {
}
I hope all this makes sense. I have not been able to find anything online about finding matching values of two different ranges and changing the cell next to those values.
Much appreciated!
Try this:
function checkOut() {
var ss = SpreadsheetApp.getActive();
var sh = ss.getActiveSheet();
var name = sh.getRange("C2").getValue();
if(name && name.length>0 && getColumnHeight(1)>1) {
var basketrg = sh.getRange(2,1,getColumnHeight(1)-1,1);
var basket = basketrg.getValues();
var equipmentlist = sh.getRange(2,5,getColumnHeight(5)-1,1).getValues();
var availabilitylist = sh.getRange(2,6,getColumnHeight(6)-1,1).getValues();
for(var i=0;i<equipmentlist.length;i++) {
for(var j=0;j<basket.length;j++) {
if(equipmentlist[i][0]==basket[j][0]){
availabilitylist[i][0]=name;
}
}
}
sh.getRange(2,6,availabilitylist.length,1).setValues(availabilitylist);
setCameraList();
basketrg.clearContent();
sh.getRange('C2').clearContent()
}else{
SpreadsheetApp.getUi().alert('Invalid Input: No name provided or no cameras selected in checkOut() function.');
}
}
function getColumnHeight(col,sh,ss){
var ss=ss || SpreadsheetApp.getActive();
var sh=sh || ss.getActiveSheet();
var col=col || sh.getActiveCell().getColumn();
var rg=sh.getRange(1,col,sh.getLastRow(),1);
var vA=rg.getValues();
while(vA[vA.length-1][0].length==0){
vA.splice(vA.length-1,1);
}
return vA.length;
}
A Simple CheckOut System
To setup this system you will want to copy all of the scripts bellow into the Code.gs file in the script editor of a Spreadsheet. Name your sheet 'CheckOut'. You will also want to created a named range named "AvailableCameras" and the range will be 'CheckOut!K2:K1000' and a Data Validation for 'CheckOut!A2:A20' or however large you want your basket to be.
You can add as many cameras as you wish (well within reason... the more you add the slower it runs). As you checkout cameras the list of camera choices in your basket gets smaller. When cameras are returned you can clear out the names from the available list and the system will add the available label back for you and it will also increase the number of available cameras.
This is what the Spreadsheet looks like:
Code.gs:
function onOpen() {
makeMenu();
}
function makeMenu(){
SpreadsheetApp.getUi().createMenu('My Tools')
.addItem('Check Out', 'checkOut')
.addItem('Set Camera List', 'setCameraList')
.addItem('Set Available List', 'setAvailableList')
.addToUi();
}
function checkOut() {
var ss = SpreadsheetApp.getActive();
var sh = ss.getActiveSheet();
var name = sh.getRange("C2").getValue();
if(name && name.length>0) {
var basketrg = sh.getRange(2,1,getColumnHeight(1)-1,1);
var basket = basketrg.getValues();
var equipmentlist = sh.getRange(2,5,getColumnHeight(5)-1,1).getValues();
var availabilitylist = sh.getRange(2,6,getColumnHeight(6)-1,1).getValues();
for(var i=0;i<equipmentlist.length;i++) {
for(var j=0;j<basket.length;j++) {
if(equipmentlist[i][0]==basket[j][0]){
availabilitylist[i][0]=name;
}
}
}
sh.getRange(2,6,availabilitylist.length,1).setValues(availabilitylist);
setCameraList();
basketrg.clearContent();
sh.getRange('C2').clearContent()
}else{
SpreadsheetApp.getUi().alert('Invalid Input: No name provided in checkOut() function.');
}
}
function setCameraList() {
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName('CheckOut');
var LISTrg=ss.getRangeByName('AvailableCameras');
var rg=sh.getRange(2,5,getColumnHeight(5)-1,2);
var vA=rg.getValues();
LISTrg.clear();
var listA=[];
for(var i=0;i<vA.length;i++) {
if(vA[i][1]=='Available') {
listA.push([vA[i][0]]);
}
}
sh.getRange(2,11,listA.length,1).setValues(listA);
}
function onEdit(e) {
var rg=e.range;
var sh=rg.getSheet();
var name=sh.getName();
if(name!='CheckOut'){return;}
if(rg.columnStart==1 && rg.rowStart>1) {
setCameraList();
}
if(rg.columnStart==6 && rg.rowStart>1) {
setAvailableList();
setCameraList();
}
}
function setAvailableList() {
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName('CheckOut');
var rg=sh.getRange(2,6,getColumnHeight(5)-1,1);
var vA=rg.getValues();
for(var i=0;i<vA.length;i++) {
if(!vA[i][0] && vA[i][0].length==0) {
vA[i][0]='Available';
}
}
rg.setValues(vA);
}
function getColumnHeight(col,sh,ss){
var ss=ss || SpreadsheetApp.getActive();
var sh=sh || ss.getActiveSheet();
var col=col || sh.getActiveCell().getColumn();
var rg=sh.getRange(1,col,sh.getLastRow(),1);
var vA=rg.getValues();
while(vA[vA.length-1][0].length==0){
vA.splice(vA.length-1,1);
}
return vA.length;
}