Populating a Doc with Google Sheets - google-sheets

I don't know why the text shows up in the Google Doc for First Name and Last Name columns but no text for the notes column. I'm not sure why, I'd also like for row 9, 10, and 11 to populate text depending on which row is selected.
function onOpen() {
const ui = SpreadsheetApp.getUi();
const menu = ui.createMenu('AutoFill Docs');
menu.addItem('Create New Docs', 'createNewGoogleDocs');
menu.addToUi();
}
function createNewGoogleDocs() {
const googleDocTemplate = DriveApp.getFileById('1ybOPZ-Y3wIZMyZpCW3HUojb72nDF8D9ruipdphpWvBE');
const destinationFolder = DriveApp.getFolderById('1jtR9DjOY7wvu2I9NQvZtrWBkZtDlK2c0');
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Enrollments");
const rows = sheet.getDataRange().getDisplayValues();
rows.forEach(function(row, index) {
if (index === 0) return;
if (row[14]) return;
const copy = googleDocTemplate.makeCopy(`${row[1]}, ${row[2]} ABC Bartending and Casino
School`, destinationFolder);
const doc = DocumentApp.openById(copy.getId());
const body = doc.getBody();
const friendlyDate = new Date(row[1]).toLocaleDateString();
body.replaceText('{{Student ID}}', row[0]);
body.replaceText('{{Date}}', friendlyDate);
body.replaceText('{{First Name}}', row[3]);
body.replaceText('{{Last Name}}', row[4]);
body.replaceText('{{Phone}}', row[5]);
body.replaceText('{{Amount Paid}}', row[6]);
body.replaceText('{{Balance}}', row[7]);
body.replaceText('{{Start}}', friendlyDate);
body.replaceText('{{am}}', row[9]);
body.replaceText('{{pm}}', row[10]);
body.replaceText('{{we}}', row[11]);
body.replaceText('{{Email}}', row[12]);
body.replaceText('{{Notes}}', row[13]);
doc.saveAndClose();
const url = doc.getUrl();
sheet.getRange(index + 1, 15).setValue(url);
})
}

Related

Google Sheets - shift the cell value according to the name beside the cell

Problem: The details of item can't move together when latest stock update.
The picture below shows the initial input:
The picture below shows the problem when the stock changes:
Desired Output:
The formula of Name column for repeat the item:
=QUERY(ArrayFormula(FLATTEN(SPLIT(REPT($AL11:$AL13&"#",$AM11:$AM13),"#"))),"where Col1 is not null")
Suggestion
Using apps script
Not sure if it is possible using formula, but here's a script you can use. This of course still needs some tweaking to align with your spreadsheet.
Also what I've done with the script is it deletes both the last name and detail if you change it to lower stock since it makes more sense rather than keeping it in same spot.
try:
function onEdit(e) {
const ss = e.source;
const range = e.range;
if(range.getColumn() == 2 && range.getRow() > 1) {
const sheet = ss.getActiveSheet();
const lastRow = sheet.getLastRow();
const dataRange = sheet.getRange(2, 1, lastRow - 1, 2);
const dataValues = removeEmptyRows(dataRange.getValues());
const outputRange = dataRange.offset(0, 2);
const outputData = removeEmptyRows(outputRange.getValues());
let output = [];
dataValues.filter(x => x[1] && x[1] > 0).forEach(row => {
const [fruit, count] = row;
let currentFruit = outputData.filter(x => x[0] == fruit);
let currentCount = currentFruit.length, rows;
if(currentCount < count) {
rows = count - currentCount;
currentFruit.push(...Array(rows).fill([fruit, '']));
}
else if(count < currentCount) {
rows = currentCount - count;
currentFruit = currentFruit.slice(0, -1 * rows);
}
output.push(...currentFruit);
});
outputRange.clearContent();
sheet.getRange(2, 3, output.length, output[0].length).setValues(output);
}
}
function removeEmptyRows(array) {
return array.filter(x => x.filter(String).length > 0)
}
Result:
Let me know if this works or if you have other questions.
EDIT - Modified code
function onEdit(e) {
const ss = e.source;
const range = e.range;
if(range.getColumn() == 2 && range.getRow() > 1) {
// Needed values to modify
// Input: Data that holds how many items are in each category
const inputRowStart = 2; // Input starts at:
const inputColStart = 1; // ROW 2, COL 1 (A2)
// Output: Data where the output is written
const outputRowStart = 2; // Output starts at:
const outputColStart = 3; // ROW 2, COL 3 (C2)
const outputCols = 3; // 3 COLS (Fruits, Details, Date)
const sheet = ss.getActiveSheet();
const lastRow = sheet.getLastRow();
const inputRange = sheet.getRange(inputRowStart, inputColStart, lastRow - 1, 2);
const inputValues = removeEmptyRows(inputRange.getValues());
const outputRange = sheet.getRange(outputRowStart, outputColStart, lastRow - 1, outputCols);
const outputData = removeEmptyRows(outputRange.getValues());
let output = [];
inputValues.filter(row => row[1] > 0).forEach(row => {
const [fruit, count] = row;
let currentFruit = outputData.filter(row => row[0] == fruit);
let currentCount = currentFruit.length, rows;
if(currentCount < count) {
rows = count - currentCount;
currentFruit.push(...Array(rows).fill([fruit, ...Array(outputCols - 1).fill('')]));
}
else if(count < currentCount) {
currentFruit = currentFruit.slice(0, count);
}
output.push(...currentFruit);
});
outputRange.clearContent();
sheet.getRange(outputRowStart, outputColStart, output.length, output[0].length).setValues(output);
}
}
function removeEmptyRows(array) {
return array.filter(row => row.filter(String).length > 0)
}

Data from a Google Sheet "form" to a Google sheet spreadsheet

I have a Google sheet that is in a “Form” format. I need to program a button that once the sender completes the form, will send the data to another sheet in a spreadsheet format and erase the data from the “form” making it ready for another form entry.
enable goole sheets api service, and adapt this script (names of source and destination sheets, and cellsA1Notation of data in source sheet).
function onOpen() {
SpreadsheetApp.getUi().createMenu('⇩ M E N U ⇩')
.addItem('👉 Validate Unit Registration (FORM)', 'copyDataRegistrationForm')
.addItem('👉 Reset Unit Registration (FORM)', 'initRegistrationForm')
.addToUi();
}
function copyDataRegistrationForm() {
const ssId = SpreadsheetApp.getActiveSpreadsheet().getId();
const srcSheet = "Unit Registration (FORM)";
const dstSheet = "Master Data";
const rngA1Notation = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(dstSheet).getRange(1, 1, 1, 74).getValues().flat()
const src = rngA1Notation.map(e => `'${srcSheet}'!${e}`);
const values = Sheets.Spreadsheets.Values.batchGet(ssId, { ranges: src })
var data = []
values.valueRanges.forEach(e => data.push(e.values ? e.values.flat().toString() : ""))
SpreadsheetApp.getActiveSpreadsheet().getSheetByName(dstSheet).appendRow(data)
}
function initRegistrationForm() {
const srcSheet = "Unit Registration (FORM)";
const dstSheet = "Master Data";
const rngA1Notation = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(dstSheet).getRange(1, 1, 1, 74).getValues().flat()
var rng = rngA1Notation.filter(r => SpreadsheetApp.getActiveSpreadsheet().getRange(r).getFormula() == '')
SpreadsheetApp.getActiveSpreadsheet().getSheetByName(srcSheet).getRangeList(rng).clearContent()
}

Send email when value in cell changes in more than one cell on the same spreadsheet

I have searched and search stackoverflow for the answer to my question but have not found it. I am looking for anyone to help me fix my script below (which as is does work):
function sendEmail() {
const sheet = SpreadsheetApp.getActive().getSheetByName('IDP & Observation');
if (sheet.getRange('H20').getValue()!== 2){
return;
}
const emailAddress = sheet.getRange('C46').getValue();
const subject = sheet.getRange('C47').getValue();
const body = sheet.getRange('C48').getValue();
MailApp.sendEmail(emailAddress, subject, body);
}
What I need to do is add another cell 'H34' which I have on the same sheet and need the same email sent when 'H34' changes to a value of 2 - and potentially I may want to do the same when a cell changes on another sheet within the google workbook to send the same message and so on.
So how can I add 'H34' as a value to trigger the same email??
Would I just add a line like this?
(sheet.getRange('H34').getValue()!== 2)
What your code means is:
If the value of cell H20 doesn't equal two.
Stop the function and return nothing.
if (sheet.getRange('H20').getValue()!== 2){
return;
}
You can combine multiple conditions using logical operators.
&& is AND.
|| is OR.
So this means:
Create variables (const) to store the value of h20 and h34
If the value of h20 doesn't equal 2 AND the value of h34 doesn't equal two
Stop running the function and return.
const h20 = sheet.getRange('H20').getValue();
const h34 = sheet.getRange('H34').getValue();
if (h20 !== 2 && h34 !== 2){
return;
}
What if I did this? Would this work?
function sendEmail() {
const sheet1 = SpreadsheetApp.getActive().getSheetByName('IDP & Observation');
const h20 = sheet1.getRange('H20').getValue();
const h34 = sheet1.getRange('H34').getValue();
if (h20 !== 2 || h34 !== 2){
return;
}
const emailAddress = sheet1.getRange('C46').getValue();
const subject = sheet1.getRange('C47').getValue();
const body = sheet1.getRange('C48').getValue();
MailApp.sendEmail(emailAddress, subject, body);
const sheet2 = SpreadsheetApp.getActive().getSheetByName('Yr 1 CSTP Co-Assessment');
const k63 = sheet2.getRange('K63').getValue();
const k68 = sheet2.getRange('K68').getValue();
const k73 = sheet2.getRange('K73').getValue();
if (k63 !== 2 || k68 !== 2 || k73 !== 2){
return;
}
const emailAddress2 = sheet2.getRange('C147').getValue();
const subject2 = sheet2.getRange('C148').getValue();
const body2 = sheet2.getRange('C149').getValue();
MailApp.sendEmail(emailAddress2, subject2, body2);
}

Google Script to Parse/Filter Data and Set Several sub SpreadSheets from master Spreadsheet

I have a master spreadsheet with several drivers and their routes for the day. This needs to be parsed out to several driver sheets. I have one sheet with all the data in the master and another with all the drivers names and spreadsheet ids to run a loop through for all drivers listed. Just runs...no action. Last three comments need to be fixed as well as it errors here and does not like .openById.
function setRoutes(){
var ScheduleSheetURL = "https://docs.google.com/spreadsheets/...";
var ActiveSpreadSheet = SpreadsheetApp.openByUrl(ScheduleSheetURL);
var AllRoutesSheetName = "RoutesIn";
var ActiveSheet = ActiveSpreadSheet.setActiveSheet(ActiveSpreadSheet.getSheetByName(AllRoutesSheetName));
var AllRouteData = ActiveSheet.getRange('A:AZ').getValues();
ActiveSheet = SpreadsheetApp.setActiveSheet(ActiveSpreadSheet.getSheetByName("Drivers"));
//var ActiveDriverCounter = ActiveSheet.getRange("A1:A").getValues();
//var DriverCount = ActiveDriverCounter.filter(String).length;
var DriverCount = ActiveSheet.getLastRow()-1;
var idrvSheetID = "";
var ActiveDriverName = "";
var RouteData = [];
var i = 1;
for (i = 1; i++; i <= DriverCount){
//Get Route Name
ActiveSpreadSheet = SpreadsheetApp.openByUrl(ScheduleSheetURL);
ActiveSheet = ActiveSpreadSheet.setActiveSheet(ActiveSpreadSheet.getSheetByName("Drivers"));
ActiveDriverName = ActiveSheet.getRange(i,1).getValue();
Logger.log(ActiveDriverName);
//Get Drivers Route
ActiveSheet = ActiveSpreadSheet.setActiveSheet(ActiveSpreadSheet.getSheetByName(AllRoutesSheetName));
for (var i = 0; i< AllRouteData.length; i++){
if(AllRouteData[i][1] == ActiveDriverName){
RouteData.push(AllRouteData[i])
Logger.log(RouteData[i]);
}
}
ActiveSpreadSheet = SpreadsheetApp.openByUrl(ScheduleSheetURL);
//Open & Write Driver Sheet
ActiveSheet = SpreadsheetApp.setActiveSheet(ActiveSpreadSheet.getSheetByName("Drivers"));
idrvSheetID = ActiveSheet.getRange(i,2).getValue();
// ActiveSpreadSheet = SpreadsheetApp.openById(idrvSheetID);
// ActiveSheet = ActiveSpreadSheet.setActiveSheet(ActiveSpreadSheet.getSheets()[0]);
// ActiveSheet.getRange(ActiveSheet.getLastRow()+1,1,RouteData.length,RouteData[0].length).setValues(RouteData);
}
}
Issues:
There are several problems with your script:
Every time you want to work on a spreadsheet or sheet, you are using some variant of setActive. I guess you are under the assumption that you need to "activate" a sheet in order to work on it, but that's not true! If you remove all this, your code can be minimized considerably.
You are not defining the outer for loop correctly: for (i = 1; i++; i <= DriverCount){. Second parameter should be the condition, and third one should be what to do at the end of each loop iteration. You have it the other way around.
When trying to filter the routes according to the Driver (if(AllRouteData[i][1] == ActiveDriverName){), you are not specifying the correct array index (arrays are 0-indexed, so 1 here would refer to column B, which doesn't contain the driver's names, so the script won't find any match there.
You are reusing the same loop counter variable (i) in a nested loop (for (var i = 0; i< AllRouteData.length; i++){). This will mess with your outer loop.
Solution:
Considering all these, I've wrote a small script that accomplishes your purpose. I have changed some of the variables names so that they are more meaningful (please check inline comments):
function setRoutes() {
const ScheduleSheetURL = "YOUR-MAIN-SPREADSHEET-URL"; // Change to your spreadsheet URL
const SourceSpreadSheet = SpreadsheetApp.openByUrl(ScheduleSheetURL);
const RoutesSheet = SourceSpreadSheet.getSheetByName("RoutesIn");
const AllRouteData = RoutesSheet.getRange('A:AZ').getValues();
const DriversSheet = SourceSpreadSheet.getSheetByName("Drivers");
const DriverCount = DriversSheet.getLastRow()-1;
const DriversData = DriversSheet.getRange(2, 1, DriverCount, 2).getValues(); // Get all Drivers data
for (let i = 0; i < DriversData.length; i++) { // Loop through all Drivers data
const DriverData = DriversData[i]; // Specific Driver data
const DriverName = DriverData[0]; // Driver name (column A)
const DriverSpreadsheetId = DriverData[1]; // Driver spreadsheet ID (column B)
const DriverRoutes = AllRouteData.filter(routeData => routeData[0] === DriverName); //Filter routes according to driver
const DriverSpreadsheet = SpreadsheetApp.openById(DriverSpreadsheetId); // Get specific Driver spreadsheet
const DriverSheet = DriverSpreadsheet.getSheets()[0]; // Get first sheet in Driver spreadsheet
DriverSheet.getRange(DriverSheet.getLastRow() + 1, 1, DriverRoutes.length, DriverRoutes[0].length)
.setValues(DriverRoutes); // Add routes to driver spreadsheet
}
}

while using header option with XLSX.utils.json_to_sheet , headers not overriding

I'm trying to change header titles by passing an array of titles to options but it does not override the headers. Instead it inserts new headers before the original data. I am passing the same numbers of header titles.
Here is my code:
const ws: XLSX.WorkSheet = XLSX.utils.json_to_sheet(
json,
{header: headerColumns}
);
const wb: XLSX.WorkBook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, 'Transactions');
const excelBuffer: any = XLSX.write(wb, { bookType: 'xlsx', type: 'array' });
this.saveAsExcelFile(excelBuffer, excelFileName);
And output looks like below:
The basic job of the "header" option is not to override, rather just shift the starting option of the columns.
i.e. any value passed in the header option will be treated as the first column, provided the value should match with existing keys you have in the data.
XLSX.utils.json_to_sheet([{A:1,B:2}, {B:2,C:3}], {header:['C']});
Here column "C" will be the first column in the excel.
For more look out for detailed description here: https://docs.sheetjs.com/#sheetjs-js-xlsx
This is how I have achieved similar behavior:
const XLSX = require('xlsx');
const wb = XLSX.utils.book_new();
const Heading = [
['Sr No', 'User Name', 'Department', 'Bank', 'Country', 'Region', 'Amount']
];
// creating sheet and adding data from 2nd row of column A.
// leaving first row to add Heading
const ws = XLSX.utils.json_to_sheet(data, { origin: 'A2', skipHeader: true });
// adding heading to the first row of the created sheet.
// sheet already have contents from above statement.
XLSX.utils.sheet_add_aoa(ws, Heading, { origin: 'A1' });
// appending sheet with a name
XLSX.utils.book_append_sheet(wb, ws, 'Records');
const fileContent = XLSX.write(wb, { bookType: 'xlsx', type: 'buffer' });
Very traditional approach but working, please see complete code below:
const worksheet: XLSX.WorkSheet = XLSX.utils.json_to_sheet(
this.releaseDateWiseCountList
);
worksheet.A1.v = "Pick Release Date";
worksheet.B1.v = "Task Type";
worksheet.C1.v = "First Shift";
worksheet.D1.v = "Second Shift";
worksheet.E1.v = "Total";
worksheet.F1.v = "Grand Total";
worksheet.G1.v = "Pick %";
const workbook: XLSX.WorkBook = {
Sheets: { 'data': worksheet }, SheetNames: ['data']
};
const excelBuffer: any = XLSX.write(
workbook, { bookType: 'xlsx', type: 'array' }
);
const data: Blob = new Blob([buffer], {type: EXCEL_TYPE});
FileSaver.saveAs(data, 'Result_export_' + new Date().getTime() + EXCEL_EXTENSION);

Resources