BUTTON to move data from multiple google sheets into one master - javascript

My boss told to move same set of data from 40 multiple google sheets into one master sheets which are filled by 40 people separately currently am working on it with an ADD on function
is there any way to put a submit button on each sheets so that when user fill the data and click on submit button the data go direct to the master sheets ?
I have the script which is working fine with sheets but the only cons of that is the script only combine the tabs into same spreadsheet
here is code :
const masterSheet = "ASM-A30";
const masterSheetFormulaCell = "A2";
const ignoreSheets = ["Verified NDRx","Business Tracker","NDRX","PMT_EBx","PMT_EBx.","NDRx PMT Business Tracker.","Analysis"];
const dataRange = "A2:AA";
const checkRange = "A2:A" ;
//end set variables
const ss = SpreadsheetApp.getActiveSpreadsheet() ;
ignoreSheets.push(masterSheet) ;
const allsheets = ss.getSheets() ;
const filteredListofSheets = allsheets.filter(s => ignoreSheets.indexOf(s.getSheetName()) == -1);
let formulaArray = filteredListofSheets.map(s => `FILTER({'${s.getSheetName()}'!${dataRange},"${s.getSheetName()} - Row "&ROW('${s.getSheetName()}'!${dataRange})}, '${s.getSheetName()}'!${checkRange}<>"")`);
let formulaText = "={" + formulaArray.join(";")+ "}"
//console.1og( formulaText) ;
ss. getSheetByName(masterSheet).getRange(masterSheetFormulaCell) .setFormula(formulaText) ;
}

Try this
const ignoreSheets = ["Verified NDRx","Business Tracker","NDRX","PMT_EBx","PMT_EBx.","NDRx PMT Business Tracker.","Analysis"];
const masterSheet = "ASM-A30";
function compile() {
let result = []
SpreadsheetApp.getActiveSpreadsheet().getSheets().filter(s => ignoreSheets.indexOf(s.getSheetName()) == -1).forEach((s,i) => {
let [headers, ...data] = s.getDataRange().getValues()
if (i == 0) { result.push(headers.flat()) }
data.forEach(r => result.push(r))
})
if(result.length) {
var master = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(masterSheet)
master.clearContents()
master.getRange(1, 1, result.length, result[0].length).setValues(result)
SpreadsheetApp.getActiveSpreadsheet().toast('Updated!');
}
}
to add rows, change
if (i == 0) { }
and try
var master = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(masterSheet)
// master.clearContents()
master.getRange(+master.getLastRow()+1, 1, result.length, result[0].length).setValues(result)
SpreadsheetApp.getActiveSpreadsheet().toast('Updated!');

Related

How to compare a value with each item of range

for the last 3 days (my first 3 days of coding) i've been trying to code an script to get my google contacts that have certain keyword, it being "Catamarca", on their name and also to delete that keyword after they've been added to the spreadsheet leaving only their name.
I've been succesfull in all of this. But now i want to only run the script on an existing database, and only run it if the new contacts are not on the sheet already, and not write over the existing ones.
Here is my code so far:
function impContacts() {
// variables
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("GContacts");
var grupo = ContactsApp.getContactGroupById("http://www.google.com/m8/feeds/groups/email#gmail.com/base/6")
const contactos = grupo.getContacts();
const arraycontacts = [];
// get the last row on B with data on it
const lastRow = sheet.getLastRow();
const Avals = sheet.getRange("B2:B" + lastRow).getValues();
const Alast = lastRow - Avals.reverse().findIndex(c => c[0] != '');
Logger.log(Alast);
var rangeInteres = sheet.getRange(2, 1, Alast, sheet.getLastColumn()).getValues();
// look for contacts that has in their name the word "Catamarca", and save them with their id, name and phone.
contactos.forEach(contacto => {
var phone = ""
if (contacto.getPhones()[0]) {
phone = contacto.getPhones()[0].getPhoneNumber()
};
var name = ""
if (contacto.getFullName().match("Catamarca")){
name = contacto.getFullName();
};
var idcont = ""
if (name == rangeInteres.forEach(namme =>
{
oldName = namme[1];
Logger.log(oldName);}))
{
idcont =
rangeInteres.forEach(id => {
oldId= id[0];
Logger.log(oldId);})
}
else
{
idcont = contacto.getId().replace(/\D/g, '')
};
const datoscont = [idcont, name, phone];
arraycontacts.push(datoscont);
})
// save new contact
sheet.getRange(2, 1, arraycontacts.length, 3).setValues(arraycontacts);
// look for "catamarca"
range = sheet.getRange("B2:B")
var textFind = range.createTextFinder("Catamarca");
textFind.matchEntireCell(false);
textFind.ignoreDiacritics(true);
textFind.matchCase(false);
var textFound = textFind.findNext();
// Si encuentra coincidencia reemplazar por ""
if (textFound !== null) {
var vals = textFound.getValues();
textFind.replaceAllWith("");
}
}
What i need to keep the most is the ID of the existing contacts (the IDs are different from the ones coming from google contacts but the names are the same), because they are linked to an app created using AppSheet.
I believe there should be a way to accomplish this by editing this part of the code
var idcont = ""
if (name == rangeInteres.forEach(namme =>
{
oldName = namme[1];
Logger.log(oldName);}))
{
idcont =
rangeInteres.forEach(id => {
oldId= id[0];
Logger.log(oldId);})
}
else
{
idcont = contacto.getId().replace(/\D/g, '')
};
In my head and with my current knowledge, the code should be working, but it's not, I mean, it runs, but overwrites everything.

Send email for each project owner including all notes and subtasks

I am trying to write an Appscript/Javascript that can match a project to its tasks and notes relationship. Grab all the tasks + notes for each project and email the owner of the project. Im not sure how to do this at all. Since they are in three diffrent sheets.
Right now I have a script to send Weekly emails for each persons tasks assigned to them that works becasue it only requires the single IPTM_Task sheet.
My spreadsheet looks like this Tasks:
Task Sheet
SubTasks:
Subtasks
Notes:
Notes
Script to email task owners
function sendEmails() {
let s = '';
const sh = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("ITPM_Tasks");
const lastRow = sh.getLastRow();
const startRow = 2; // First row of data to process
const numRows = lastRow - 1; // Number of rows to process
const rg = sh.getRange(startRow, 3, numRows, 6);
const vs = rg.getValues();
let oners = {pA:[]};
vs.forEach((r,i) => {
let [name,desc,status,owner,due] = r;
if(status != 'Complete') {
if(!oners.hasOwnProperty(owner)) {
oners[owner]=[];
oners[owner].push(r);
oners.pA.push(owner)
} else {
oners[owner].push(r);
}
}
});
let subject = 'Weekly Reminder: The following tasks are assigned to you.';
oners.pA.forEach(p => {
let msg = `These Tasks below are assigned to you:\n`
oners[p].forEach((r,i) => {
let [name,desc,status,owner,due] = r;
msg += `Task - ${i+1}\n`;
msg += `Description: ${desc}\n`;
msg += `Due Date: ${due.toDateString()}\n\n`
});
msg += `some message to task owners`;
MailApp.sendEmail(oners[p][0][3], subject, msg);
});
}
EDIT:
Basically, I would like the script to see the Project ID on the Task Sheet grab the project name (Project) and the Owner
Then find the related "SubTask" Project Names" and related "Notes" to that project name
and send the Project owner an email with
Project Name
Subtask 1
subtask 2...
Note 1
Note 2
idealoutput
Makes sense to break it down, e.g.like this:
const ss = SpreadsheetApp.getActive()
const projects = ss.getSheetByName("Task Sheet").getDataRange().getValues()
projects.forEach( project => {
const projectId = project[0]
const status = project[2]
if( status.toLowerCase() == "completed") return
const notes = getNotesByProjectId(projectId)
const subtasks = getSubtasksByProjectId(projectId)
// here you can build your message
let message = ""
notes.forEach( note => {
// add conditions here
message += note[2] + "\n"
})
subtasks.forEach( subtask => {
// add conditions here
message += subtask[3] + "\n"
})
// and then send it
})
function getNotesByProjectId( projectId ){
const allNotes = ss.getSheetByName("Notes").getDataRange().getValues()
// Filter where Column 2 (index 1) is the project
const filteredNotes = allNotes.filter( note => note[1] == projectId )
return filteredNotes
}
function getSubtasksByProjectId( projectId ){
const allNotes = ss.getSheetByName("Subtasks").getDataRange().getValues()
// Filter where Column 3 (index 2) is the project
const filteredNotes = allNotes.filter( note => note[2] == projectId )
return filteredNotes
}
Reference
Sheet.getDataRange

combine duplicate rows in apps script

Current sheet
Required sheet
I have an addon that combines duplicate rows, but I want apps script to run onEdit trigger.
Here is the script:
const HDR = 1 // header height
const GROUP_COL = 1 // in which column is the grouping value
function onEdit(e){
// we are interested only if value is not empty and the content of colum GROUP_COL has been changed
if(!e.value || e.value===e.oldValue || e.range.columnStart!==GROUP_COL || e.range.rowStart<=HDR){
return
}
const sh = SpreadsheetApp.getActiveSheet()
const values = sh.getDataRange().getValues().slice(HDR-1)
const existingRows = values.map((r,i)=>({ row:HDR+i, cells:r, found:r[GROUP_COL-1]==e.value })).filter(x=>x.found)
console.log(existingRows)
if(existingRows.length===0){
return
}
const destRange = sh.getRange(e.range.rowStart,1,1,existingRows[0].cells.length)
const destRow = destRange.getValues()[0]
// merge content into the new row
existingRows.forEach(er=>{
er.cells.forEach((c,i)=>{
if(i===GROUP_COL-1 || er.row===e.range.rowStart){
return
}
destRow[i] = (destRow[i] + "\n" + er.cells[i]).trim()
})
})
destRange.setValues([destRow])
// delete extra rows
existingRows.forEach((er,i)=>{
if(er.row!==e.range.rowStart){
sh.deleteRow(er.row-i)
}
})
}

Want to use my google sheet custom search menu for multiple tab sheets

I have been trying to make a custom menu for searching data from my another tab Sheet but I want to make that for multiple sheets please can anyone help me so that I can fix this problem I am sharing my code here I tried by using sheet array and looping around but then multiple times that search pop-up coming.
function onOpen() {
let ui=SpreadsheetApp.getUi();
let menu= ui.createMenu('menu')
menu.addItem('Search','showInputBox');
menu.addItem('Clear','ClearCells');
menu.addToUi();
}
function ClearCells() {
var sheet = SpreadsheetApp.getActive().getActiveSheet();
sheet.getRange('A2:O500').clearContent();
}
function showInputBox() {
let ui = SpreadsheetApp.getUi()
let input = ui.prompt("Enter the searching item", ui.ButtonSet.OK_CANCEL);
if (input.getSelectedButton() == ui.Button.OK) {
let ss = SpreadsheetApp.getActiveSpreadsheet();
let ws = ss.getSheetByName('APAC');
let data = ws.getRange("A2:O47").getValues();
let userText = input.getResponseText();
let newData = data.filter(r => {
return r.some(cell => String(cell ? cell : "").trim() === userText);
});
let newWs = ss.getSheets()[0];
newWs.getRange(2,1, newData.length, newData[0].length).setValues(newData);
}
else if (input.getSelectedButton() == ui.Button.CANCEL) {
//Do something
}
else if (input.getSelectedButton() == ui.Button.CLOSE) {
//Do something
}
else {
}
}
I tried this code but need little help
var sheetListArray = ["Sheet1", "Sheet2"]
var ss = SpreadsheetApp.getActiveSpreadsheet();
for( var i = 1 ; i <= sheetListArray.length ; i++)
showInputBox(sheetListArray[i]); }
function showInputBox() {
let ui = SpreadsheetApp.getUi()
let input = ui.prompt("Enter the searching item", ui.ButtonSet.OK_CANCEL);
if (input.getSelectedButton() == ui.Button.OK) {
let ss = SpreadsheetApp.getActiveSpreadsheet();
let ws = ss.getSheetByName('APAC');
let data = ws.getRange("A2:O47").getValues();
let tf = ws.createTextFinder(input.getResponseText()).findAll();
let html = '';
tf.forEach(f => {
html += `row: ${f.getRow()} col: ${f.getColumn()}`
});
ui.showModelessDialog(HtmlService.createHtmlOutput(html),'Results')
} else {
us.showModelessDialog(HtmlService.createHtmlOutput('No results found'));
}
}

Placing a timestamp on a cell accross multiple sheets on edit

I tried many different variations of this script and can't figure out why it's not working.
I want to update the same cell "J3" with a timestamp across multiple tabs onedit. I have another function on the spreadsheet already using onEdit(e), so i set a new function with an onEdit trigger.
I retrieve the names of the tabs I want to update and store them in an array.
function Overview_timestamp(e) //function to use in the onEdit trigger
{
var activeSheet = e.source.getActiveSheet();
var range = e.range;
var cellToStamp = "J3" ;
var name_range = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Structure').getRange(2, 5, 14, 1).getValues(); //sheets i want to update
var flattened_name_range = [] ; //converting into 1D array
for (var i = 0; i < name_range.length; i++) {
flattened_name_range.push(name_range[i][0]);
}
if(flattened_name_range.indexOf(activeSheet())!==-1 ) {
activeSheet.getRange(cellToStamp).setValue(new Date()).setNumberFormat("yyyy-MM-dd hh:mm:ss");}
}
Try this:
function Overview_timestamp(e) {
const sh = e.range.getSheet();
if (sh.getName() == 'Sheet name') {//just to limit action to one sheet but it would be nice to have additional control like clicking a checkbox or selecting a value on a validated cell.
const shts = e.source.getSheetByName('Structure').getRange(2, 5, 14).getValues().flat();
shts.forEach(n => {
e.source.getSheetByName(n).getRange('J3').setValue(new Date()).setNumberFormat("yyyy-MM-dd hh:mm:ss")
});
}
}
I did it this way on my sheet using a checkbox at A1 on Sheet1
function onEdit(e) {
//e.source.toast('entry');
const sh = e.range.getSheet();
if (sh.getName() == 'Sheet1' && e.range.columnStart==1 && e.range.rowStart==1 && e.value=='TRUE') {
//e.source.toast('cond');
const shts = e.source.getSheetByName('Sheet2').getRange(2, 5, 14).getValues().flat().filter(e=>e);
shts.forEach(n => {
e.source.getSheetByName(n).getRange('J3').setValue(new Date()).setNumberFormat("yyyy-MM-dd hh:mm:ss")
});
e.range.setValue('FALSE');
}
}

Categories