코딩 수준: 초급
소요 시간: 10분
프로젝트 유형: 이벤트 기반 트리거 및 시간 기반 트리거를 사용한 자동화
목표
- 솔루션의 작동 방식을 이해합니다.
- 솔루션 내에서 Apps Script 서비스가 하는 작업을 이해합니다.
- 스크립트를 설정합니다.
- 스크립트를 실행합니다.
이 솔루션 정보
신입 직원에게는 일반적으로 IT의 시스템 액세스 권한과 장비가 필요합니다. 이러한 요청을 관리하려면 사용자가 직원에게 필요한 액세스 권한과 기기를 표시할 수 있는 양식을 Google Forms로 만들 수 있습니다. IT에서 요청을 완료하고 상태를 업데이트하면 요청자에게 이메일 알림이 전송됩니다.
작동 방식
스크립트가 장비 요청 양식을 만듭니다. 샘플 스크립트의 코드에서 양식의 항목을 맞춤설정할 수 있습니다. 사용자가 양식을 제출하면 스크립트가 요청을 위해 지정된 담당자에게 이메일 알림을 전송합니다. 스프레드시트의 요청 상태가 '완료됨'으로 변경되면 스크립트가 양식을 제출한 사용자에게 확인 이메일을 보냅니다.
Apps Script 서비스
이 솔루션은 다음 서비스를 사용합니다.
- 양식 서비스: IT 요청 양식을 만듭니다.
- 스프레드시트 서비스: 중복을 방지하기 위해 요청 양식이 이미 있는지 확인합니다. 필요에 따라 양식 응답을 대기 중 및 완료됨 시트로 이동하여 관리합니다.
- 메일 서비스: 요청 및 완료 알림 이메일을 생성하고 전송합니다.
- 스크립트 서비스: 트리거를 만듭니다. 하나는 양식이 제출될 때 실행되고 다른 하나는 5분마다 실행되어 요청 상태가 '완료됨'으로 표시되는지 확인합니다.
기본 요건
이 샘플을 사용하려면 다음 기본 요건이 필요합니다.
- Google 계정 (Google Workspace 계정의 경우 관리자 승인이 필요할 수 있음)
- 인터넷에 액세스할 수 있는 웹브라우저
스크립트 설정
Apps Script 프로젝트 만들기
- 다음 버튼을 클릭하여 직원 장비 요청 관리 스프레드시트의 사본을 만듭니다. 이 솔루션의 Apps Script 프로젝트가 스프레드시트에 첨부되어 있습니다.
사본 만들기
- 확장 프로그램 >
Apps Script를 클릭합니다.
REQUEST_NOTIFICATION_EMAIL
변수 옆에 있는 샘플 이메일을 이메일로 바꿉니다.
- 저장 를 클릭합니다.
스프레드시트 설정
- 스프레드시트로 돌아가서 장비 요청
> 설정을 클릭합니다. 이 맞춤 메뉴가 표시되도록 페이지를 새로고침해야 할 수도 있습니다.
메시지가 표시되면 스크립트를 승인합니다.
OAuth 동의 화면에 이 앱은 확인되지 않았습니다라는 경고가 표시되면 고급 >
{프로젝트 이름}으로 이동(안전하지 않음)을 선택하여 계속 진행합니다.
장비 요청
> 설정을 다시 클릭합니다.
스크립트 실행
- 도구 > 양식 관리
> 실시간 양식으로 이동을 클릭합니다.
- 양식을 작성하여 제출합니다.
- 이메일에서 장비 요청에 관한 알림을 확인합니다.
- 스프레드시트로 돌아가 대기 중인 요청 시트에서 요청 상태를 완료됨으로 변경합니다.
- 5분 이내에 스크립트에서 요청이 완료되었음을 알리는 이메일을 다시 전송합니다. 스크립트는 요청을 대기 중인 요청 시트에서 완료된 요청 시트로 이동합니다.
코드 검토
이 솔루션의 Apps Script 코드를 검토하려면 아래의 소스 코드 보기를 클릭합니다.
소스 코드 보기
Code.gs
// To learn how to use this script, refer to the documentation:
// https://developers.google.com/apps-script/samples/automations/equipment-requests
/*
Copyright 2022 Google LLC
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Update this variable with the email address you want to send equipment requests to.
const REQUEST_NOTIFICATION_EMAIL = 'request_intake@example.com';
// Update the following variables with your own equipment options.
const AVAILABLE_LAPTOPS = [
'15" high Performance Laptop (OS X)',
'15" high Performance Laptop (Windows)',
'15" high performance Laptop (Linux)',
'13" lightweight laptop (Windows)',
];
const AVAILABLE_DESKTOPS = [
'Standard workstation (Windows)',
'Standard workstation (Linux)',
'High performance workstation (Windows)',
'High performance workstation (Linux)',
'Mac Pro (OS X)',
];
const AVAILABLE_MONITORS = [
'Single 27"',
'Single 32"',
'Dual 24"',
];
// Form field titles, used for creating the form and as keys when handling
// responses.
/**
* Adds a custom menu to the spreadsheet.
*/
function onOpen() {
SpreadsheetApp.getUi().createMenu('Equipment requests')
.addItem('Set up', 'setup_')
.addItem('Clean up', 'cleanup_')
.addToUi();
}
/**
* Creates the form and triggers for the workflow.
*/
function setup_() {
let ss = SpreadsheetApp.getActiveSpreadsheet();
if (ss.getFormUrl()) {
let msg = 'Form already exists. Unlink the form and try again.';
SpreadsheetApp.getUi().alert(msg);
return;
}
let form = FormApp.create('Equipment Requests')
.setCollectEmail(true)
.setDestination(FormApp.DestinationType.SPREADSHEET, ss.getId())
.setLimitOneResponsePerUser(false);
form.addTextItem().setTitle('Employee name').setRequired(true);
form.addTextItem().setTitle('Desk location').setRequired(true);
form.addDateItem().setTitle('Due date').setRequired(true);
form.addListItem().setTitle('Laptop').setChoiceValues(AVAILABLE_LAPTOPS);
form.addListItem().setTitle('Desktop').setChoiceValues(AVAILABLE_DESKTOPS);
form.addListItem().setTitle('Monitor').setChoiceValues(AVAILABLE_MONITORS);
// Hide the raw form responses.
ss.getSheets().forEach(function(sheet) {
if (sheet.getFormUrl() == ss.getFormUrl()) {
sheet.hideSheet();
}
});
// Start workflow on each form submit
ScriptApp.newTrigger('onFormSubmit_')
.forForm(form)
.onFormSubmit()
.create();
// Archive completed items every 5m.
ScriptApp.newTrigger('processCompletedItems_')
.timeBased()
.everyMinutes(5)
.create();
}
/**
* Cleans up the project (stop triggers, form submission, etc.)
*/
function cleanup_() {
let formUrl = SpreadsheetApp.getActiveSpreadsheet().getFormUrl();
if (!formUrl) {
return;
}
ScriptApp.getProjectTriggers().forEach(function(trigger) {
ScriptApp.deleteTrigger(trigger);
});
FormApp.openByUrl(formUrl)
.deleteAllResponses()
.setAcceptingResponses(false);
}
/**
* Handles new form submissions to trigger the workflow.
*
* @param {Object} event - Form submit event
*/
function onFormSubmit_(event) {
let response = mapResponse_(event.response);
sendNewEquipmentRequestEmail_(response);
let equipmentDetails = Utilities.formatString('%s\n%s\n%s',
response['Laptop'],
response['Desktop'],
response['Monitor']);
let row = ['New',
'',
response['Due date'],
response['Employee name'],
response['Desk location'],
equipmentDetails,
response['email']];
let ss = SpreadsheetApp.getActiveSpreadsheet();
let sheet = ss.getSheetByName('Pending requests');
sheet.appendRow(row);
}
/**
* Sweeps completed and cancelled requests, notifying the requestors and archiving them
* to the completed sheet.
*
* @param {Object} event
*/
function processCompletedItems_() {
let ss = SpreadsheetApp.getActiveSpreadsheet();
let pending = ss.getSheetByName('Pending requests');
let completed = ss.getSheetByName('Completed requests');
let rows = pending.getDataRange().getValues();
for (let i = rows.length; i >= 2; i--) {
let row = rows[i -1];
let status = row[0];
if (status === 'Completed' || status == 'Cancelled') {
pending.deleteRow(i);
completed.appendRow(row);
console.log("Deleted row: " + i);
sendEquipmentRequestCompletedEmail_({
'Employee name': row[3],
'Desk location': row[4],
'email': row[6],
});
}
};
}
/**
* Sends an email notification that a new equipment request has been submitted.
*
* @param {Object} request - Request details
*/
function sendNewEquipmentRequestEmail_(request) {
let template = HtmlService.createTemplateFromFile('new-equipment-request.html');
template.request = request;
template.sheetUrl = SpreadsheetApp.getActiveSpreadsheet().getUrl();
let msg = template.evaluate();
MailApp.sendEmail({
to: REQUEST_NOTIFICATION_EMAIL,
subject: 'New equipment request',
htmlBody: msg.getContent(),
});
}
/**
* Sends an email notifying the requestor that the request is complete.
*
* @param {Object} request - Request details
*/
function sendEquipmentRequestCompletedEmail_(request) {
let template = HtmlService.createTemplateFromFile('request-complete.html');
template.request = request;
let msg = template.evaluate();
MailApp.sendEmail({
to: request.email,
subject: 'Equipment request completed',
htmlBody: msg.getContent(),
});
}
/**
* Converts a form response to an object keyed by the item titles. Allows easier
* access to response values.
*
* @param {FormResponse} response
* @return {Object} Form values keyed by question title
*/
function mapResponse_(response) {
let initialValue = {
email: response.getRespondentEmail(),
timestamp: response.getTimestamp(),
};
return response.getItemResponses().reduce(function(obj, itemResponse) {
let key = itemResponse.getItem().getTitle();
obj[key] = itemResponse.getResponse();
return obj;
}, initialValue);
}
new-equipment-request.html
참여자
이 샘플은 Google에서 Google 개발자 전문가의 도움을 받아 유지관리합니다.
다음 단계