서비스
home

5~10장 앱스 스크립트 실전

5장 실전! 거래처별 월정산서 자동으로 만들기

5장 그림 보기

[예제 05-01] 판매데이터 탭의 데이터 전체를 가져와서 로그를 표시

function filterSalesData() { const SS = SpreadsheetApp.getActiveSpreadsheet() const T_SALESDATA = SS.getSheetByName('판매데이터') const salesData = T_SALESDATA.getDataRange().getValues() Logger.log(`${salesData.length - 1} 행의 판매데이터가 있습니다.`) Logger.log(salesData[0]) Logger.log(salesData[0][1]) Logger.log(salesData) }
JavaScript
복사

전체 데이터를 거래처100으로 필터링

function filterSalesData() { const SS = SpreadsheetApp.getActiveSpreadsheet() const T_SALESDATA = SS.getSheetByName('판매데이터') const salesData = T_SALESDATA.getDataRange().getValues() const salesDataFiltered = salesData.filter(row => row[1] == '거래처100') Logger.log(salesDataFiltered.length) Logger.log(salesDataFiltered) }
JavaScript
복사

[예제 05-02] 데이터를 거래처명과 날짜 범위로 필터링하기

function filterSalesDataByCustomerAndDate() { const SS = SpreadsheetApp.getActiveSpreadsheet() const T_SALESDATA = SS.getSheetByName('판매데이터') const salesData = T_SALESDATA.getDataRange().getValues() const startDate = new Date('2023-06-01') const endDate = new Date('2023-06-30') Logger.log(startDate) Logger.log(endDate) const salesDataFiltered = salesData.filter(row => row[1] == '거래처100' && row[0] >= startDate && row[0] <= endDate) Logger.log(salesDataFiltered.length) Logger.log(salesDataFiltered) }
JavaScript
복사

데이터를 거래처명과 시분초를 포함한 날짜 범위로 필터링하기

function filterSalesDataByCustomerAndDate() { const SS = SpreadsheetApp.getActiveSpreadsheet() const T_SALESDATA = SS.getSheetByName('판매데이터') const salesData = T_SALESDATA.getDataRange().getValues() const startDate = new Date('2023-06-01 00:00:00') const endDate = new Date('2023-06-30 23:59:59') Logger.log(startDate) Logger.log(endDate) const salesDataFiltered = salesData.filter(row => row[1] == '거래처100' && row[0] >= startDate && row[0] <= endDate) Logger.log(salesDataFiltered.length) Logger.log(salesDataFiltered) }
JavaScript
복사

map으로 원하는 열을 가져오고, 순번 매기기

function filterSalesDataByCustomerAndDate() { const SS = SpreadsheetApp.getActiveSpreadsheet() const T_SALESDATA = SS.getSheetByName('판매데이터') const salesData = T_SALESDATA.getDataRange().getValues() const startDate = new Date('2023-06-01 00:00:00') const endDate = new Date('2023-06-30 23:59:59') Logger.log(startDate) Logger.log(endDate) const salesDataFiltered = salesData.filter(row => row[1] == '거래처100' && row[0] >= startDate && row[0] <= endDate) Logger.log(salesDataFiltered.length) Logger.log(salesDataFiltered) const salesDataResult = salesDataFiltered.map((item, index) => [index + 1, item[0], item[4], item[5], item[6], item[7], item[8], item[9], item[10], item[11]]) Logger.log(salesDataResult) }
JavaScript
복사

filter, map 결과를 newSheet 파일에 붙여넣기 최종 코드

function filterSalesDataByCustomerAndDate() { const SS = SpreadsheetApp.getActiveSpreadsheet() const T_SALESDATA = SS.getSheetByName('판매데이터') const salesData = T_SALESDATA.getDataRange().getValues() const startDate = new Date('2023-06-01 00:00:00') const endDate = new Date('2023-06-30 23:59:59') const salesDataFiltered = salesData.filter(row => row[1] == '거래처100' && row[0] >= startDate && row[0] <= endDate) const salesDataResult = salesDataFiltered.map((item, index) => [index + 1, item[0], item[4], item[5], item[6], item[7], item[8], item[9], item[10], item[11]]) const NEW_SS = SpreadsheetApp.create('newSheet') NEW_SS.getActiveSheet().getRange(1, 1, salesDataResult.length, salesDataResult[0].length).setValues(salesDataResult) const newSheetUrl = NEW_SS.getUrl() Logger.log(newSheetUrl) }
JavaScript
복사

[예제 05-03] for문을 적용해서 4개 거래처 필터링 결과를 파일로 만들기

function createSheetsByCustomers() { const SS = SpreadsheetApp.getActiveSpreadsheet() const T_SALESDATA = SS.getSheetByName('판매데이터') const salesData = T_SALESDATA.getDataRange().getValues() const customers = ['거래처100', '거래처20', '거래처793', '거래처142'] const startDate = new Date('2023-06-01 00:00:00') const endDate = new Date('2023-06-30 23:59:59') for (i = 0; i < customers.length; i++) { const salesDataFiltered = salesData.filter(row => row[1] == customers[i] && row[0] >= startDate && row[0] <= endDate) const salesDataResult = salesDataFiltered.map((item, index) => [index + 1, item[0], item[4], item[5], item[6], item[7], item[8], item[9], item[10], item[11]]) const NEW_SS = SpreadsheetApp.create('newSheet') NEW_SS.getActiveSheet().getRange(1, 1, salesDataResult.length, salesDataResult[0].length).setValues(salesDataResult) const newSheetUrl = NEW_SS.getUrl() Logger.log(newSheetUrl) } }
JavaScript
복사

Utilities.formatDate()를 사용해서 날짜 형식 변경하기

function createSheetsByCustomers() { const SS = SpreadsheetApp.getActiveSpreadsheet() const T_SALESDATA = SS.getSheetByName('판매데이터') const salesData = T_SALESDATA.getDataRange().getValues() const customers = ['거래처100', '거래처20', '거래처793', '거래처142'] const startDate = new Date('2023-06-01 00:00:00') const endDate = new Date('2023-06-30 23:59:59') const startDateForName = Utilities.formatDate(startDate, 'GMT+9', 'yyyy-MM-dd') const endDateForName = Utilities.formatDate(endDate, 'GMT+9', 'yyyy-MM-dd') Logger.log(startDateForName) Logger.log(endDateForName) /* for (i = 0; i < customers.length; i++) { const salesDataFiltered = salesData.filter(row => row[1] == customers[i] && row[0] >= startDate && row[0] <= endDate) const salesDataResult = salesDataFiltered.map((item, index) => [index + 1, item[0], item[4], item[5], item[6], item[7], item[8], item[9], item[10], item[11]]) const NEW_SS = SpreadsheetApp.create('newSheet') NEW_SS.getActiveSheet().getRange(1, 1, salesDataResult.length, salesDataResult[0].length).setValues(salesDataResult) const newSheetUrl = NEW_SS.getUrl() Logger.log(newSheetUrl) } */ }
JavaScript
복사

[예제 05-04] 파일명과 저장되는 폴더를 지정하는 최종 코드

function createSheetsByCustomersMoveToFolder() { const SS = SpreadsheetApp.getActiveSpreadsheet() const T_SALESDATA = SS.getSheetByName('판매데이터') const salesData = T_SALESDATA.getDataRange().getValues() const customers = ['거래처100', '거래처20', '거래처793', '거래처142'] const startDate = new Date('2023-06-01 00:00:00') const endDate = new Date('2023-06-30 23:59:59') const startDateForName = Utilities.formatDate(startDate, 'GMT+9', 'yyyy-MM-dd') const endDateForName = Utilities.formatDate(endDate, 'GMT+9', 'yyyy-MM-dd') const resultFolderId = '폴더ID' const resultFolder = DriveApp.getFolderById(resultFolderId) for (i = 0; i < customers.length; i++) { Logger.log(`${customers[i]}의 작업을 시작합니다.`) const salesDataFiltered = salesData.filter(row => row[1] == customers[i] && row[0] >= startDate && row[0] <= endDate) const salesDataResult = salesDataFiltered.map((item, index) => [index + 1, item[0], item[4], item[5], item[6], item[7], item[8], item[9], item[10], item[11]]) const NEW_SS = SpreadsheetApp.create('newSheet') NEW_SS.getActiveSheet().getRange(1, 1, salesDataResult.length, salesDataResult[0].length).setValues(salesDataResult) const newSheetUrl = NEW_SS.getUrl() const newSheetName = `${customers[i]}_${startDateForName}_${endDateForName}` const newSheetId = NEW_SS.getId() DriveApp.getFileById(newSheetId).moveTo(resultFolder).setName(newSheetName) Logger.log(newSheetUrl) } }
JavaScript
복사

[예제 05-05] 월정산서를 생성하는 중간 코드

function createMonthlyInvoice() { const SS = SpreadsheetApp.getActiveSpreadsheet() const T_SALESDATA = SS.getSheetByName('판매데이터') const salesData = T_SALESDATA.getDataRange().getValues() const T_CUSTOMER = SS.getSheetByName('거래처') const customers = T_CUSTOMER.getDataRange().getValues().filter(row => row[7] == true) Logger.log(customers) const startDate = new Date('2023-06-01 00:00:00') const endDate = new Date('2023-06-30 23:59:59') const startDateForName = Utilities.formatDate(startDate, 'GMT+9', 'yyyy-MM-dd') const endDateForName = Utilities.formatDate(endDate, 'GMT+9', 'yyyy-MM-dd') const resultFolderId = '폴더 ID' const resultFolder = DriveApp.getFolderById(resultFolderId) const templateFile = DriveApp.getFileById('파일 ID') const templateCopyName = `월정산서_${startDateForName}_${endDateForName}` const templateCopyId = templateFile.makeCopy().moveTo(resultFolder).setName(templateCopyName).getId() const templateCopy = SpreadsheetApp.openById(templateCopyId) for (i = 0; i < customers.length; i++) { Logger.log(`${customers[i][1]}의 작업을 시작합니다.`) const salesDataFiltered = salesData.filter(row => row[1] == customers[i][1] && row[0] >= startDate && row[0] <= endDate) const salesDataResult = salesDataFiltered.map((item, index) => [index + 1, item[0], item[4], item[5], item[6], item[7], item[8], item[9], item[10], item[11]]) // 여기에 템플릿 탭을 복사하고 위치에 맞추어 데이터를 입력하는 코드가 들어갈 예정 } }
JavaScript
복사

[예제 05-06] 월정산서 생성하는 최종 코드

function createMonthlyInvoice() { const SS = SpreadsheetApp.getActiveSpreadsheet() const T_SALESDATA = SS.getSheetByName('판매데이터') const salesData = T_SALESDATA.getDataRange().getValues() const T_CUSTOMER = SS.getSheetByName('거래처') const customers = T_CUSTOMER.getDataRange().getValues().filter(row => row[7] == true) Logger.log(customers) const startDate = new Date('2023-06-01 00:00:00') const endDate = new Date('2023-06-30 23:59:59') const startDateForName = Utilities.formatDate(startDate, 'GMT+9', 'yyyy-MM-dd') const endDateForName = Utilities.formatDate(endDate, 'GMT+9', 'yyyy-MM-dd') const resultFolderId = '폴더 ID' const resultFolder = DriveApp.getFolderById(resultFolderId) const templateFile = DriveApp.getFileById('파일 ID') const templateCopyName = `월정산서_${startDateForName}_${endDateForName}` const templateCopyId = templateFile.makeCopy().moveTo(resultFolder).setName(templateCopyName).getId() const templateCopy = SpreadsheetApp.openById(templateCopyId) for (i = 0; i < customers.length; i++) { Logger.log(`${customers[i][1]}의 작업을 시작합니다.`) const salesDataFiltered = salesData.filter(row => row[1] == customers[i][1] && row[0] >= startDate && row[0] <= endDate) const salesDataResult = salesDataFiltered.map((item, index) => [index + 1, item[0], item[4], item[5], item[6], item[7], item[8], item[9], item[10], item[11]]) const newTabName = `${customers[i][1]}_${startDateForName}_${endDateForName}` const newTab = templateCopy.getSheetByName('템플릿').copyTo(templateCopy).setName(newTabName) newTab.getRange(17, 2, salesDataResult.length, salesDataResult[0].length).setValues(salesDataResult) newTab.getRange('C8').setValue(customers[i][1]) // 거래처명 newTab.getRange('C9').setValue(customers[i][4]) // 담당자 newTab.getRange('C10').setValue(customers[i][5]) // 연락처 newTab.getRange('E10').setValue(customers[i][6]) // 이메일 newTab.getRange('H13').setValue(startDateForName) newTab.getRange('K13').setValue(endDateForName) newTab.getRange('B13').setValue(new Date()) const salesDataSum = salesDataResult.map(item => item[9]).reduce((acc, cur) => acc + cur, 0) newTab.getRange('H9').setValue(salesDataSum) } Logger.log(templateCopy.getUrl()) }
JavaScript
복사

[예제 05-07] 월정산서 생성하는 최종 코드에 주석 추가하기

/* [거래처] 탭에 선택된 거래처의 월정산서를 생성하는 함수 최종 업데이트: YYYY년 MM월 DD일 작성자: 오토오피스 */ function createMonthlyInvoice () { const SS = SpreadsheetApp.getActiveSpreadsheet() const T_SALESDATA = SS.getSheetByName('판매데이터') const salesData = T_SALESDATA.getDataRange().getValues() const T_CUSTOMER = SS.getSheetByName('거래처') const customers = T_CUSTOMER.getDataRange().getValues().filter(row => row[7] == true) Logger.log(customers)
JavaScript
복사

[예제 05-08] 코드 위치 변경 및 블록 주석 추가하기

/* [거래처] 탭에 선택된 거래처의 월정산서를 생성하는 함수 최종 업데이트: YYYY년 MM월 DD일 작성자: 오토오피스 */ function createMonthlyInvoice() { // 탭, 템플릿과 결과물 저장 폴더 변수 선언 및 할당 const SS = SpreadsheetApp.getActiveSpreadsheet() const T_SALESDATA = SS.getSheetByName('판매데이터') const T_CUSTOMER = SS.getSheetByName('거래처') const resultFolderId = '폴더 ID' const resultFolder = DriveApp.getFolderById(resultFolderId) const templateFile = DriveApp.getFileById('파일 ID') // 판매데이터, 대상 거래처 변수 선언 및 할당 const salesData = T_SALESDATA.getDataRange().getValues() const customers = T_CUSTOMER.getDataRange().getValues().filter(row => row[7] == true) Logger.log(customers) // 판매데이터 필터링 기간 및 템플릿 복사본 정의 시작 const startDate = new Date('2023-06-01 00:00:00') const endDate = new Date('2023-06-30 23:59:59') const startDateForName = Utilities.formatDate(startDate, 'GMT+9', 'yyyy-MM-dd') const endDateForName = Utilities.formatDate(endDate, 'GMT+9', 'yyyy-MM-dd') const templateCopyName = `월정산서_${startDateForName}_${endDateForName}` const templateCopyId = templateFile.makeCopy().moveTo(resultFolder).setName(templateCopyName).getId() const templateCopy = SpreadsheetApp.openById(templateCopyId) // 판매데이터 필터링 기간 및 템플릿 복사본 정의 종료
JavaScript
복사

[예제 05-09] customer 변수를 객체로 추가해서 활용하기

// 거래처별 정산서탭 생성 및 데이터 입력 for (i = 0; i < customers.length; i++) { // 거래처 정보 정의 const customer = { customerName: customers[i][1], ceoName: customers[i][4], contactNo: customers[i][5], email: customers[i][6] } Logger.log(`${customer.customerName}의 작업을 시작합니다.`) // salesData를 거래처명과 판매일시로 필터링하고, 정산서의 양식에 맞춰 열을 재배치 const salesDataFiltered = salesData.filter(row => row[1] == customer.customerName && row[0] >= startDate && row[0] <= endDate) const salesDataResult = salesDataFiltered.map((item, index) => [index + 1, item[0], item[4], item[5], item[6], item[7], item[8], item[9], item[10], item[11]]) // 탭이름을 정의하고, 템플릿 탭을 복사해서 탭이름으로 설정 const newTabName = `${customer.customerName}_${startDateForName}_${endDateForName}` const newTab = templateCopy.getSheetByName('템플릿').copyTo(templateCopy).setName(newTabName) // 새로운 탭에 해당 거래처의 판매데이터 입력 newTab.getRange(17, 2, salesDataResult.length, salesDataResult[0].length).setValues(salesDataResult) (중략) }
JavaScript
복사

for문으로 셀 배열에 맞추어 값 입력하기

// 정산서에 거래처 정보 및 정산 금액 입력 시작 const salesDataSum = salesDataResult.map(item => item[9]).reduce((acc, cur) => acc + cur, 0) const cells = ['C8', 'C9', 'C10', 'E10', 'H13', 'K13', 'B13', 'H9'] const customerValues = [customer.customerName, customer.ceoName, customer.contactNo, customer.email, startDateForName, endDateForName, new Date(), salesDataSum] for (let j = 0; j < cells.length; j++) { newTab.getRange(cells[j]).setValue(customerValues[j]) } // 정산서에 거래처 정보 및 정산 금액 입력 종료
JavaScript
복사

[예제 05-10] 주석을 추가하고 리팩터링한 최종 코드

/* [거래처] 탭에 선택된 거래처의 월정산서를 생성하는 함수 최종 업데이트: YYYY년 MM월 DD일 작성자: 오토오피스 */ function createMonthlyInvoice() { // 탭, 템플릿과 결과물 저장 폴더 변수 선언 및 할당 const SS = SpreadsheetApp.getActiveSpreadsheet() const T_SALESDATA = SS.getSheetByName('판매데이터') const T_CUSTOMER = SS.getSheetByName('거래처') const resultFolderId = '폴더 ID' const resultFolder = DriveApp.getFolderById(resultFolderId) const templateFile = DriveApp.getFileById('파일 ID') // 판매데이터, 대상 거래처 변수 선언 및 할당 const salesData = T_SALESDATA.getDataRange().getValues() const customers = T_CUSTOMER.getDataRange().getValues().filter(row => row[7] == true) Logger.log(customers) // 판매데이터 필터링 기간 및 템플릿 복사본 정의 시작 const startDate = new Date('2023-06-01 00:00:00') const endDate = new Date('2023-06-30 23:59:59') const startDateForName = Utilities.formatDate(startDate, 'GMT+9', 'yyyy-MM-dd') const endDateForName = Utilities.formatDate(endDate, 'GMT+9', 'yyyy-MM-dd') const templateCopyName = `월정산서_${startDateForName}_${endDateForName}` const templateCopyId = templateFile.makeCopy().moveTo(resultFolder).setName(templateCopyName).getId() const templateCopy = SpreadsheetApp.openById(templateCopyId) // 판매데이터 필터링 기간 및 템플릿 복사본 정의 종료 // 거래처별 정산서탭 생성 및 데이터 입력 for (i = 0; i < customers.length; i++) { // 거래처 정보 정의 const customer = { customerName: customers[i][1], ceoName: customers[i][4], contactNo: customers[i][5], email: customers[i][6] } Logger.log(`${customer.customerName}의 작업을 시작합니다.`) // salesData를 거래처명과 판매일시로 필터링하고, 정산서의 양식에 맞춰 열을 재배치 const salesDataFiltered = salesData.filter(row => row[1] == customer.customerName && row[0] >= startDate && row[0] <= endDate) const salesDataResult = salesDataFiltered.map((item, index) => [index + 1, item[0], item[4], item[5], item[6], item[7], item[8], item[9], item[10], item[11]]) // 탭이름을 정의하고, 템플릿 탭을 복사해서 탭이름으로 설정 const newTabName = `${customer.customerName}_${startDateForName}_${endDateForName}` const newTab = templateCopy.getSheetByName('템플릿').copyTo(templateCopy).setName(newTabName) // 새로운 탭에 해당 거래처의 판매데이터 입력 newTab.getRange(17, 2, salesDataResult.length, salesDataResult[0].length).setValues(salesDataResult) // 정산서에 거래처 정보 및 정산 금액 입력 시작 const salesDataSum = salesDataResult.map(item => item[9]).reduce((acc, cur) => acc + cur, 0) const cells = ['C8', 'C9', 'C10', 'E10', 'H13', 'K13', 'B13', 'H9'] const customerValues = [customer.customerName, customer.ceoName, customer.contactNo, customer.email, startDateForName, endDateForName, new Date(), salesDataSum] for (j = 0; j < cells.length; j++) { newTab.getRange(cells[j]).setValue(customerValues[j]) } // 정산서에 거래처 정보 및 정산 금액 입력 종료 } Logger.log(templateCopy.getUrl()) }
JavaScript
복사

익숙해지기

// 1) resultFolderId를 알고 있을 때, 이 ID로 폴더를 지정하려면 어떻게 해야할까요? const resultFolderId = '폴더 ID' const resultFolder = DriveApp.getFolderById(resultFolderId)
JavaScript
복사
// 2) '2023-06-01 00:00:00' 문자열로 날짜를 생성하려면 어떻게 해야할까요? const startDate = new Date('2023-06-01 00:00:00')
JavaScript
복사
// 3) arr의 크기(길이)를 알기 위해서는 어떻게 할 수 있을까요? 크기는 몇일까요? function myFunction() { const arr = [[1, 2], [3, 4], [5]] Logger.log(arr.length) // 실행로그 3 }
JavaScript
복사
// 4) 메서드 체이닝을 사용해서 다음의 코드를 1행으로 줄여보세요. file.moveTo(resultFolder) file.setName(newSheetName) file.moveTo(resultFolder).setName(newSheetName)
JavaScript
복사
// 5) startDate의 형식을 yy-MM-dd로 변경해서 startDateForName에 할당하려면 어떻게 해야할까요? const startDate = new Date('2023-06-01 00:00:00') const startDateForName = Utilities.formatDate(startDate, 'GMT+9', 'yy-MM-dd')
JavaScript
복사

6장 실전! 월정산서를 PDF로 변환하고 구글 드라이브에 저장하기

6장 그림 보기

[예제 06-01] A4용지에 맞춰 페이지 단위로 행을 조절하는 함수

function adjustTabRowsByPage(salesDataResult, newTab) { const pageNo = Math.ceil((salesDataResult.length - 56) / 73 + 1) const deleteRowStart = pageNo * 73 const deleteRows = 1000 - deleteRowStart + 1 newTab.deleteRows(deleteRowStart, deleteRows) }
JavaScript
복사

[예제 06-02] 데이터에 맞춰 행을 조절하는 함수

function adjustTabRowsByData(salesDataResult, newTab) { const deleteRowStart = 17 + salesDataResult.length const deleteRows = 1000 - deleteRowStart + 1 newTab.deleteRows(deleteRowStart, deleteRows) }
JavaScript
복사

[예제 06-03] 전체 탭을 포함한 통합문서 PDF를 다운로드하는 HtmlService를 추가한 함수

function pdfDownloadWhole(templateCopyId){ const ui = SpreadsheetApp.getUi() const pdfUrl = `https://docs.google.com/spreadsheets/d/${templateCopyId}/export?format=pdf&size=A4` const html = `<script> window.location.href="${pdfUrl}" setTimeout(function(){google.script.host.close()}, 10000) </script>` const htmlOutput = HtmlService.createHtmlOutput(html) ui.showModalDialog(htmlOutput, 'PDF를 다운로드 합니다.') }
JavaScript
복사

createMonthlyInvoice_WholeDN 함수

/* [거래처] 탭에 선택된 거래처의 월정산서를 생성하는 함수 최종 업데이트: YYYY년 MM월 DD일 작성자: 오토오피스 */ function createMonthlyInvoice_WholeDN() { // 탭, 템플릿과 결과물 저장 폴더 변수 선언 및 할당 const SS = SpreadsheetApp.getActiveSpreadsheet() const T_SALESDATA = SS.getSheetByName('판매데이터') const T_CUSTOMER = SS.getSheetByName('거래처') const resultFolderId = '폴더 ID' const resultFolder = DriveApp.getFolderById(resultFolderId) const templateFile = DriveApp.getFileById('파일 ID') // 판매데이터, 대상 거래처 변수 선언 및 할당 const salesData = T_SALESDATA.getDataRange().getValues() const customers = T_CUSTOMER.getDataRange().getValues().filter(row => row[7] == true) Logger.log(customers) // 판매데이터 필터링 기간 및 템플릿 복사본 정의 시작 const startDate = new Date('2023-06-01 00:00:00') const endDate = new Date('2023-06-30 23:59:59') const startDateForName = Utilities.formatDate(startDate, 'GMT+9', 'yyyy-MM-dd') const endDateForName = Utilities.formatDate(endDate, 'GMT+9', 'yyyy-MM-dd') const templateCopyName = `월정산서_${startDateForName}_${endDateForName}` const templateCopyId = templateFile.makeCopy().moveTo(resultFolder).setName(templateCopyName).getId() const templateCopy = SpreadsheetApp.openById(templateCopyId) // 판매데이터 필터링 기간 및 템플릿 복사본 정의 종료 // 거래처별 정산서탭 생성 및 데이터 입력 for (i = 0; i < customers.length; i++) { // 거래처 정보 정의 const customer = { customerName: customers[i][1], ceoName: customers[i][4], contactNo: customers[i][5], email: customers[i][6] } Logger.log(`${customer.customerName}의 작업을 시작합니다.`) // salesData를 거래처명과 판매일시로 필터링하고, 정산서의 양식에 맞춰 열을 재배치 const salesDataFiltered = salesData.filter(row => row[1] == customer.customerName && row[0] >= startDate && row[0] <= endDate) const salesDataResult = salesDataFiltered.map((item, index) => [index + 1, item[0], item[4], item[5], item[6], item[7], item[8], item[9], item[10], item[11]]) // 탭이름을 정의하고, 템플릿 탭을 복사해서 탭이름으로 설정 const newTabName = `${customer.customerName}_${startDateForName}_${endDateForName}` const newTab = templateCopy.getSheetByName('템플릿').copyTo(templateCopy).setName(newTabName) // 새로운 탭에 해당 거래처의 판매데이터 입력 newTab.getRange(17, 2, salesDataResult.length, salesDataResult[0].length).setValues(salesDataResult) // 정산서에 거래처 정보 및 정산 금액 입력 시작 const salesDataSum = salesDataResult.map(item => item[9]).reduce((acc, cur) => acc + cur, 0) const cells = ['C8', 'C9', 'C10', 'E10', 'H13', 'K13', 'B13', 'H9'] const customerValues = [customer.customerName, customer.ceoName, customer.contactNo, customer.email, startDateForName, endDateForName, new Date(), salesDataSum] for (j = 0; j < cells.length; j++) { newTab.getRange(cells[j]).setValue(customerValues[j]) } // 정산서에 거래처 정보 및 정산 금액 입력 종료 // 페이지 행 조절 adjustTabRowsByPage(salesDataResult, newTab) } Logger.log(templateCopy.getUrl()) pdfDownloadWhole(templateCopyId) }
JavaScript
복사

[예제 06-04] 각 탭의 PDF를 다운로드받는 HtmlService를 추가한 코드

function pdfDownloadTab(templateCopyId, tabId){ const ui = SpreadsheetApp.getUi() const pdfUrl = `https://docs.google.com/spreadsheets/d/${templateCopyId}/export?format=pdf&size=A4&gid=${tabId}` const html = `<script> window.location.href="${pdfUrl}" setTimeout(function(){google.script.host.close()},5000) </script>` const htmlOutput = HtmlService.createHtmlOutput(html) ui.showModalDialog(htmlOutput, 'PDF를 다운로드 합니다.') Utilities.sleep(7000) }
JavaScript
복사

createMonthlyInvoice_TabDN 함수

/* [거래처] 탭에 선택된 거래처의 월정산서를 생성하는 함수 최종 업데이트: YYYY년 MM월 DD일 작성자: 오토오피스 */ function createMonthlyInvoice_TabDN() { // 탭, 템플릿과 결과물 저장 폴더 변수 선언 및 할당 const SS = SpreadsheetApp.getActiveSpreadsheet() const T_SALESDATA = SS.getSheetByName('판매데이터') const T_CUSTOMER = SS.getSheetByName('거래처') const resultFolderId = '폴더 ID' const resultFolder = DriveApp.getFolderById(resultFolderId) const templateFile = DriveApp.getFileById('파일 ID') // 판매데이터, 대상 거래처 변수 선언 및 할당 const salesData = T_SALESDATA.getDataRange().getValues() const customers = T_CUSTOMER.getDataRange().getValues().filter(row => row[7] == true) Logger.log(customers) // 판매데이터 필터링 기간 및 템플릿 복사본 정의 시작 const startDate = new Date('2023-06-01 00:00:00') const endDate = new Date('2023-06-30 23:59:59') const startDateForName = Utilities.formatDate(startDate, 'GMT+9', 'yyyy-MM-dd') const endDateForName = Utilities.formatDate(endDate, 'GMT+9', 'yyyy-MM-dd') const templateCopyName = `월정산서_${startDateForName}_${endDateForName}` const templateCopyId = templateFile.makeCopy().moveTo(resultFolder).setName(templateCopyName).getId() const templateCopy = SpreadsheetApp.openById(templateCopyId) // 판매데이터 필터링 기간 및 템플릿 복사본 정의 종료 // 거래처별 정산서탭 생성 및 데이터 입력 for (i = 0; i < customers.length; i++) { // 거래처 정보 정의 const customer = { customerName: customers[i][1], ceoName: customers[i][4], contactNo: customers[i][5], email: customers[i][6] } Logger.log(`${customer.customerName}의 작업을 시작합니다.`) // salesData를 거래처명과 판매일시로 필터링하고, 정산서의 양식에 맞춰 열을 재배치 const salesDataFiltered = salesData.filter(row => row[1] == customer.customerName && row[0] >= startDate && row[0] <= endDate) const salesDataResult = salesDataFiltered.map((item, index) => [index + 1, item[0], item[4], item[5], item[6], item[7], item[8], item[9], item[10], item[11]]) // 탭이름을 정의하고, 템플릿 탭을 복사해서 탭이름으로 설정 const newTabName = `${customer.customerName}_${startDateForName}_${endDateForName}` const newTab = templateCopy.getSheetByName('템플릿').copyTo(templateCopy).setName(newTabName) // 새로운 탭에 해당 거래처의 판매데이터 입력 newTab.getRange(17, 2, salesDataResult.length, salesDataResult[0].length).setValues(salesDataResult) // 정산서에 거래처 정보 및 정산 금액 입력 시작 const salesDataSum = salesDataResult.map(item => item[9]).reduce((acc, cur) => acc + cur, 0) const cells = ['C8', 'C9', 'C10', 'E10', 'H13', 'K13', 'B13', 'H9'] const customerValues = [customer.customerName, customer.ceoName, customer.contactNo, customer.email, startDateForName, endDateForName, new Date(), salesDataSum] for (j = 0; j < cells.length; j++) { newTab.getRange(cells[j]).setValue(customerValues[j]) } // 정산서에 거래처 정보 및 정산 금액 입력 종료 // 페이지 행 조절 adjustTabRowsByPage(salesDataResult, newTab) // 월정산서 탭 다운로드 const tabId = newTab.getSheetId() pdfDownloadTab(templateCopyId, tabId) } Logger.log(templateCopy.getUrl()) }
JavaScript
복사

[예제 06-05] 구글 홈페이지를 PDF로 변환해서 드라이브에 저장하기

function getPdfTest(){ const blob = UrlFetchApp.fetch('https://www.google.com/').getAs('application/pdf') DriveApp.createFile(blob).setName('test.pdf') }
JavaScript
복사

[예제 06-06] 전체 페이지 PDF를 구글 드라이브에 저장하는 함수

createMonthlyInvoice_WholeDrive함수

/* [거래처] 탭에 선택된 거래처의 월정산서를 생성하는 함수 최종 업데이트: YYYY년 MM월 DD일 작성자: 오토오피스 */ function createMonthlyInvoice_WholeDrive() { // 탭, 템플릿과 결과물 저장 폴더 변수 선언 및 할당 const SS = SpreadsheetApp.getActiveSpreadsheet() const T_SALESDATA = SS.getSheetByName('판매데이터') const T_CUSTOMER = SS.getSheetByName('거래처') const resultFolderId = '폴더 ID' const resultFolder = DriveApp.getFolderById(resultFolderId) const templateFile = DriveApp.getFileById('파일 ID') // 판매데이터, 대상 거래처 변수 선언 및 할당 const salesData = T_SALESDATA.getDataRange().getValues() const customers = T_CUSTOMER.getDataRange().getValues().filter(row => row[7] == true) Logger.log(customers) // 판매데이터 필터링 기간 및 템플릿 복사본 정의 시작 const startDate = new Date('2023-06-01 00:00:00') const endDate = new Date('2023-06-30 23:59:59') const startDateForName = Utilities.formatDate(startDate, 'GMT+9', 'yyyy-MM-dd') const endDateForName = Utilities.formatDate(endDate, 'GMT+9', 'yyyy-MM-dd') const templateCopyName = `월정산서_${startDateForName}_${endDateForName}` const templateCopyId = templateFile.makeCopy().moveTo(resultFolder).setName(templateCopyName).getId() const templateCopy = SpreadsheetApp.openById(templateCopyId) // 판매데이터 필터링 기간 및 템플릿 복사본 정의 종료 // 거래처별 정산서탭 생성 및 데이터 입력 for (i = 0; i < customers.length; i++) { // 거래처 정보 정의 const customer = { customerName: customers[i][1], ceoName: customers[i][4], contactNo: customers[i][5], email: customers[i][6] } Logger.log(`${customer.customerName}의 작업을 시작합니다.`) // salesData를 거래처명과 판매일시로 필터링하고, 정산서의 양식에 맞춰 열을 재배치 const salesDataFiltered = salesData.filter(row => row[1] == customer.customerName && row[0] >= startDate && row[0] <= endDate) const salesDataResult = salesDataFiltered.map((item, index) => [index + 1, item[0], item[4], item[5], item[6], item[7], item[8], item[9], item[10], item[11]]) // 탭이름을 정의하고, 템플릿 탭을 복사해서 탭이름으로 설정 const newTabName = `${customer.customerName}_${startDateForName}_${endDateForName}` const newTab = templateCopy.getSheetByName('템플릿').copyTo(templateCopy).setName(newTabName) // 새로운 탭에 해당 거래처의 판매데이터 입력 newTab.getRange(17, 2, salesDataResult.length, salesDataResult[0].length).setValues(salesDataResult) // 정산서에 거래처 정보 및 정산 금액 입력 시작 const salesDataSum = salesDataResult.map(item => item[9]).reduce((acc, cur) => acc + cur, 0) const cells = ['C8', 'C9', 'C10', 'E10', 'H13', 'K13', 'B13', 'H9'] const customerValues = [customer.customerName, customer.ceoName, customer.contactNo, customer.email, startDateForName, endDateForName, new Date(), salesDataSum] for (j = 0; j < cells.length; j++) { newTab.getRange(cells[j]).setValue(customerValues[j]) } // 정산서에 거래처 정보 및 정산 금액 입력 종료 // 페이지 행 조절 adjustTabRowsByPage(salesDataResult, newTab) } Logger.log(templateCopy.getUrl()) pdfSaveWhole(templateCopyId, resultFolder, templateCopyName) }
JavaScript
복사

[예제 06-07] 월정산 탭을 PDF로 변환해서 드라이브에 저장하는 함수

function pdfSaveTab(templateCopyId, resultFolder, newTabName, tabId) { const pdfUrl = `https://docs.google.com/spreadsheets/d/${templateCopyId}/export?format=pdf&size=A4&gid=${tabId}` const options = { headers: { 'Authorization': `Bearer ${ScriptApp.getOAuthToken()}` } } const blob = UrlFetchApp.fetch(pdfUrl, options).getAs('application/pdf') DriveApp.createFile(blob).moveTo(resultFolder).setName(newTabName) }
JavaScript
복사

createMonthlyInvoice_TabDrive함수

/* [거래처] 탭에 선택된 거래처의 월정산서를 생성하는 함수 최종 업데이트: YYYY년 MM월 DD일 작성자: 오토오피스 */ function createMonthlyInvoice_TabDrive() { // 탭, 템플릿과 결과물 저장 폴더 변수 선언 및 할당 const SS = SpreadsheetApp.getActiveSpreadsheet() const T_SALESDATA = SS.getSheetByName('판매데이터') const T_CUSTOMER = SS.getSheetByName('거래처') const resultFolderId = '폴더 ID' const resultFolder = DriveApp.getFolderById(resultFolderId) const templateFile = DriveApp.getFileById('파일 ID') // 판매데이터, 대상 거래처 변수 선언 및 할당 const salesData = T_SALESDATA.getDataRange().getValues() const customers = T_CUSTOMER.getDataRange().getValues().filter(row => row[7] == true) Logger.log(customers) // 판매데이터 필터링 기간 및 템플릿 복사본 정의 시작 const startDate = new Date('2023-06-01 00:00:00') const endDate = new Date('2023-06-30 23:59:59') const startDateForName = Utilities.formatDate(startDate, 'GMT+9', 'yyyy-MM-dd') const endDateForName = Utilities.formatDate(endDate, 'GMT+9', 'yyyy-MM-dd') const templateCopyName = `월정산서_${startDateForName}_${endDateForName}` const templateCopyId = templateFile.makeCopy().moveTo(resultFolder).setName(templateCopyName).getId() const templateCopy = SpreadsheetApp.openById(templateCopyId) // 판매데이터 필터링 기간 및 템플릿 복사본 정의 종료 // 거래처별 정산서탭 생성 및 데이터 입력 for (i = 0; i < customers.length; i++) { // 거래처 정보 정의 const customer = { customerName: customers[i][1], ceoName: customers[i][4], contactNo: customers[i][5], email: customers[i][6] } Logger.log(`${customer.customerName}의 작업을 시작합니다.`) // salesData를 거래처명과 판매일시로 필터링하고, 정산서의 양식에 맞춰 열을 재배치 const salesDataFiltered = salesData.filter(row => row[1] == customer.customerName && row[0] >= startDate && row[0] <= endDate) const salesDataResult = salesDataFiltered.map((item, index) => [index + 1, item[0], item[4], item[5], item[6], item[7], item[8], item[9], item[10], item[11]]) // 탭이름을 정의하고, 템플릿 탭을 복사해서 탭이름으로 설정 const newTabName = `${customer.customerName}_${startDateForName}_${endDateForName}` const newTab = templateCopy.getSheetByName('템플릿').copyTo(templateCopy).setName(newTabName) // 새로운 탭에 해당 거래처의 판매데이터 입력 newTab.getRange(17, 2, salesDataResult.length, salesDataResult[0].length).setValues(salesDataResult) // 정산서에 거래처 정보 및 정산 금액 입력 시작 const salesDataSum = salesDataResult.map(item => item[9]).reduce((acc, cur) => acc + cur, 0) const cells = ['C8', 'C9', 'C10', 'E10', 'H13', 'K13', 'B13', 'H9'] const customerValues = [customer.customerName, customer.ceoName, customer.contactNo, customer.email, startDateForName, endDateForName, new Date(), salesDataSum] for (j = 0; j < cells.length; j++) { newTab.getRange(cells[j]).setValue(customerValues[j]) } // 정산서에 거래처 정보 및 정산 금액 입력 종료 // 페이지 행 조절 adjustTabRowsByPage(salesDataResult, newTab) // 월정산서 탭 드라이브 저장 const tabId = newTab.getSheetId() pdfSaveTab(templateCopyId, resultFolder, newTabName, tabId) } Logger.log(templateCopy.getUrl()) }
JavaScript
복사

월정산서 템플릿 탭 숨기거나 삭제하기

// 템플릿 탭 숨기기 templateCopy.getSheetByName('템플릿').hideSheet() //템플릿 탭 삭제하기 templateCopy.deleteSheet(templateCopy.getSheetByName('템플릿'))
JavaScript
복사

createMonthlyInvoice_WholeDrive에 템플릿 숨기기 적용

/* [거래처] 탭에 선택된 거래처의 월정산서를 생성하는 함수 최종 업데이트: YYYY년 MM월 DD일 작성자: 오토오피스 */ function createMonthlyInvoice_WholeDrive() { // 탭, 템플릿과 결과물 저장 폴더 변수 선언 및 할당 const SS = SpreadsheetApp.getActiveSpreadsheet() const T_SALESDATA = SS.getSheetByName('판매데이터') const T_CUSTOMER = SS.getSheetByName('거래처') const resultFolderId = '폴더 ID' const resultFolder = DriveApp.getFolderById(resultFolderId) const templateFile = DriveApp.getFileById('파일 ID') // 판매데이터, 대상 거래처 변수 선언 및 할당 const salesData = T_SALESDATA.getDataRange().getValues() const customers = T_CUSTOMER.getDataRange().getValues().filter(row => row[7] == true) Logger.log(customers) // 판매데이터 필터링 기간 및 템플릿 복사본 정의 시작 const startDate = new Date('2023-06-01 00:00:00') const endDate = new Date('2023-06-30 23:59:59') const startDateForName = Utilities.formatDate(startDate, 'GMT+9', 'yyyy-MM-dd') const endDateForName = Utilities.formatDate(endDate, 'GMT+9', 'yyyy-MM-dd') const templateCopyName = `월정산서_${startDateForName}_${endDateForName}` const templateCopyId = templateFile.makeCopy().moveTo(resultFolder).setName(templateCopyName).getId() const templateCopy = SpreadsheetApp.openById(templateCopyId) // 판매데이터 필터링 기간 및 템플릿 복사본 정의 종료 // 거래처별 정산서탭 생성 및 데이터 입력 for (i = 0; i < customers.length; i++) { // 거래처 정보 정의 const customer = { customerName: customers[i][1], ceoName: customers[i][4], contactNo: customers[i][5], email: customers[i][6] } Logger.log(`${customer.customerName}의 작업을 시작합니다.`) // salesData를 거래처명과 판매일시로 필터링하고, 정산서의 양식에 맞춰 열을 재배치 const salesDataFiltered = salesData.filter(row => row[1] == customer.customerName && row[0] >= startDate && row[0] <= endDate) const salesDataResult = salesDataFiltered.map((item, index) => [index + 1, item[0], item[4], item[5], item[6], item[7], item[8], item[9], item[10], item[11]]) // 탭이름을 정의하고, 템플릿 탭을 복사해서 탭이름으로 설정 const newTabName = `${customer.customerName}_${startDateForName}_${endDateForName}` const newTab = templateCopy.getSheetByName('템플릿').copyTo(templateCopy).setName(newTabName) // 새로운 탭에 해당 거래처의 판매데이터 입력 newTab.getRange(17, 2, salesDataResult.length, salesDataResult[0].length).setValues(salesDataResult) // 정산서에 거래처 정보 및 정산 금액 입력 시작 const salesDataSum = salesDataResult.map(item => item[9]).reduce((acc, cur) => acc + cur, 0) const cells = ['C8', 'C9', 'C10', 'E10', 'H13', 'K13', 'B13', 'H9'] const customerValues = [customer.customerName, customer.ceoName, customer.contactNo, customer.email, startDateForName, endDateForName, new Date(), salesDataSum] for (j = 0; j < cells.length; j++) { newTab.getRange(cells[j]).setValue(customerValues[j]) } // 정산서에 거래처 정보 및 정산 금액 입력 종료 // 페이지 행 조절 adjustTabRowsByPage(salesDataResult, newTab) } Logger.log(templateCopy.getUrl()) templateCopy.getSheetByName('템플릿').hideSheet() pdfSaveWhole(templateCopyId, resultFolder, templateCopyName) }
JavaScript
복사

익숙해지기

// 1) 숫자의 올림 값을 반환하는 메서드는 무엇일까요? const number = 1.5 const roundup = Math.ceil(number)
JavaScript
복사
// 2) www.google.com 의 페이지 소스를 가져오는 코드를 완성해보세요. UrlFetchApp.fetch('www.google.com')
JavaScript
복사
// 3) 구글 시트를 PDF로 변환하기 위해서는 권한 정보를 전달해야 합니다. 권한 정보를 담은 매개변수를 options로 선언할 때, 다음의 코드에서 틀린 부분을 두 군데를 찾아보세요. const options = { head: { 'Authorization': `Bearer${ScriptApp.getOAuthToken()}` } } // 1. head => headers // 2. Bearer 뒤에 스페이스바 추가 필요
JavaScript
복사

7장 실전! 월정산서 PDF를 거래처 이메일로 전송하기

7장 그림 보기

이메일 전송가능 횟수 조회하기

function getRemainingEmailQuota(){ Logger.log(MailApp.getRemainingDailyQuota()) }
JavaScript
복사

[예제 07-01] sendEmail을 사용해 이메일 전송 테스트

function testEmail() { MailApp.sendEmail('수신자이메일', '회신용이메일', '이메일 테스트', '안녕하세요?'); }
JavaScript
복사

[예제 07-02] 매개변수 객체를 사용하여 이메일 전송하기 (body)

function sendEmailBody() { const attachBlob1 = DriveApp.getFileById('파일ID').getAs('application/pdf') const attachBlob2 = DriveApp.getFileById('파일ID').getAs('application/pdf') const attachBlob = [attachBlob1, attachBlob2] const message = { to: '수신자이메일', replyTo: '회신용이메일', name: '발신자명', cc: 'CC용이메일1, CC용이메일2', bcc: 'BCC용이메일', subject: '테스트 이메일', body: ` 안녕하세요? 오토오피스입니다. 이메일 발송을 테스트합니다. 감사합니다.`, attachments: attachBlob } MailApp.sendEmail(message) Logger.log(`이메일이 전송되었습니다. 남은 이메일 발송 가능 수는 ${MailApp.getRemainingDailyQuota()}입니다.`) }
JavaScript
복사

[예제 07-03] 매개변수 객체를 사용하여 이메일 전송하기 (htmlBody)

function sendEmailHtmlBody() { const attachBlob1 = DriveApp.getFileById('파일ID').getAs('application/pdf') const attachBlob2 = DriveApp.getFileById('파일ID').getAs('application/pdf') const attachBlob = [attachBlob1, attachBlob2] const autoofficeLogoBlob = UrlFetchApp.fetch('이미지URL').getBlob() const davidBlob = UrlFetchApp.fetch('이미지URL').getBlob() const message = { to: '수신자이메일', replyTo: '회신용이메일', name: '오토오피스', cc: 'CC용이메일1, CC용이메일2', bcc: 'BCC용이메일', subject: '테스트 이메일', htmlBody: ` <img src='cid:autoofficeLogo' height='20'> <h1>안녕하세요?</h1> <b>오토오피스입니다.</b><br> <span style='color:red'>이메일을 테스트합니다.</span><br> 감사합니다.<br> <img src='cid:davidPhoto' height='50'> `, inlineImages: { autoofficeLogo: autoofficeLogoBlob, davidPhoto: davidBlob }, attachments: attachBlob } MailApp.sendEmail(message) Logger.log(`이메일이 전송되었습니다. 남은 이메일 발송 가능 수는 ${MailApp.getRemainingDailyQuota()}입니다.`) }
JavaScript
복사

[예제 07-04] 매개변수 객체를 사용하여 이메일 전송하기 (htmlBody)

function sendEmailHtmlDriveImg() { const attachBlob1 = DriveApp.getFileById('파일ID').getAs('application/pdf') const attachBlob2 = DriveApp.getFileById('파일ID').getAs('application/pdf') const attachBlob = [attachBlob1, attachBlob2] const message = { to: '수신자이메일', replyTo: '회신용이메일', name: '오토오피스', cc: 'CC용이메일1, CC용이메일2', bcc: 'BCC용이메일', subject: '테스트 이메일', htmlBody: ` <img src='https://drive.google.com/uc?id=1qQUTdgG0e2gD8hkGU72w-tH96MDj-DKt' height='20'> <h1>안녕하세요?</h1> <b>오토오피스입니다.</b><br> <span style='color:red'>이메일을 테스트합니다.</span><br> 감사합니다.<br> <img src='https://drive.google.com/uc?id=1HfUTUXuGjJYSSMlHNM6WO8XO26bXB-LC' height='50'> `, attachments: attachBlob } MailApp.sendEmail(message) Logger.log(`이메일이 전송되었습니다. 남은 이메일 발송 가능 수는 ${MailApp.getRemainingDailyQuota()}입니다.`) }
JavaScript
복사

[예제 07-05] 디자인된 HTML 템플릿 (htmlBodyInline.html)

<!DOCTYPE html> <html> <head></head> <body> <table style="width: 800px; background-color: gainsboro; margin-left: auto; margin-right: auto;"> <tr> <td> <table style="width: 600px; background-color: white; margin-left: auto; margin-right: auto; margin-top: 60px; margin-bottom: 60px; border-collapse: collapse;"> <tr bgColor="gainsboro" border="none"> <td> <img src="https://autooffice.co.kr/img/autooffice-logo.png" style="margin-top: 30px; margin-bottom: 30px; height: 30px;"> </td> <td> <div style="margin-top: 30px; margin-bottom: 30px; height: 30px; text-align:right; font-size: 14px;"> <!-- 치환코드 위치:--> </div> </td> </tr> <tr> <td colspan=2> <img src="https://autooffice.co.kr/img/autooffice-work.png" style="width: 600px;"> </td> </tr> <tr> <td colspan=2> <div style="font-size: 30px; font-weight: normal; text-align: center; margin-top: 20px; margin-bottom: 20px;">월정산서가 도착했습니다.</div> </td> </tr> <tr> <td colspan=2> <div style="font-size: 18px; font-weight: normal; text-align: center; color: darkslategray;"> <b><!-- 치환코드 위치: 거래처명 --></b><b><!-- 치환코드 위치: 담당자명 --></b> 점주님,<br> <b><!-- 치환코드 위치: 정산시작일 --></b>부터 <b><!-- 치환코드 위치: 정산종료일 --></b>까지의 월정산서를 보내드립니다.<br> 아래의 버튼을 클릭해서 다운로드하실 수 있습니다. <br> </div> <div style="font-size: 18px; font-weight: normal; text-align: center; margin-top: 30px; margin-bottom: 30px;"> <a href="<!-- 치환코드 위치: 파일URL -->" target="_blank"> <span style=" display:inline-block; background-color: deeppink; color: white; padding: 10px">월정산서 다운로드</span> </a> </div> </td> </tr> </table> </td> </tr> </table> </body> </html>
JavaScript
복사

[예제 07-06] 디자인된 HTML 템플릿에 변수를 치환

<!DOCTYPE html> <html> <head></head> <body> <table style="width: 800px; background-color: gainsboro; margin-left: auto; margin-right: auto;"> <tr> <td> <table style="width: 600px; background-color: white; margin-left: auto; margin-right: auto; margin-top: 60px; margin-bottom: 60px; border-collapse: collapse;"> <tr bgColor="gainsboro" border="none"> <td> <img src="https://autooffice.co.kr/img/autooffice-logo.png" style="margin-top: 30px; margin-bottom: 30px; height: 30px;"> </td> <td> <div style="margin-top: 30px; margin-bottom: 30px; height: 30px; text-align:right; font-size: 14px;"> <?!= content.invoiceMonth ?> </div> </td> </tr> <tr> <td colspan=2> <img src="https://autooffice.co.kr/img/autooffice-work.png" style="width: 600px;"> </td> </tr> <tr> <td colspan=2> <div style="font-size: 30px; font-weight: normal; text-align: center; margin-top: 20px; margin-bottom: 20px;">월정산서가 도착했습니다.</div> </td> </tr> <tr> <td colspan=2> <div style="font-size: 18px; font-weight: normal; text-align: center; color: darkslategray;"> <b><?!= content.customerName ?></b><b><?!= content.ceoName ?></b> 점주님,<br> <b><?!= content.startDateForName ?></b>부터 <b><?!= content.endDateForName ?></b>까지의 월정산서를 보내드립니다.<br> 아래의 버튼을 클릭해서 다운로드하실 수 있습니다. <br> </div> <div style="font-size: 18px; font-weight: normal; text-align: center; margin-top: 30px; margin-bottom: 30px;"> <a href="<?!= content.shareUrl ?>" target="_blank"> <span style=" display:inline-block; background-color: deeppink; color: white; padding: 10px">월정산서 다운로드</span> </a> </div> </td> </tr> </table> </td> </tr> </table> </body> </html>
JavaScript
복사

[예제 07-07] HTML 파일에 변수를 치환해서 최종 HTML 페이지를 생성하는 함수

function evalEmailTemplate(templateName, content) { const template = HtmlService.createTemplateFromFile(templateName) template.content = content const htmlBody = template.evaluate().getContent() return htmlBody }
JavaScript
복사

[예제 07-08] 디자인된 이메일 전송하기 최종 코드

function sendEmailHtmlBodyFileShare() { const shareFile = DriveApp.getFileById('파일ID') shareFile.setSharing(DriveApp.Access.ANYONE_WITH_LINK, DriveApp.Permission.VIEW) const shareUrl = shareFile.getUrl() const content = { customerName: '거래처793', ceoName: '김석창', startDateForName: '2023-06-01', endDateForName: '2023-06-30', shareUrl: shareUrl, invoiceMonth: '2023년 6월' } const htmlBodyTemplate = evalEmailTemplate('htmlBodyInline', content) const message = { to: 'autooffice@naver.com', name: '오토오피스', subject: '오토오피스 월정산서', htmlBody: htmlBodyTemplate } MailApp.sendEmail(message) Logger.log(`이메일이 전송되었습니다. 남은 이메일 발송 가능 수는 ${MailApp.getRemainingDailyQuota()}입니다.`) }
JavaScript
복사

[예제 07-09] 거래처별 월정산서를 구글 드라이브에 저장하는 함수

/* [거래처] 탭에 선택된 거래처의 월정산서를 생성하는 함수 최종 업데이트: YYYY년 MM월 DD일 작성자: 오토오피스 */ function createMonthlyInvoice_TabDrive() { // 탭, 템플릿과 결과물 저장 폴더 변수 선언 및 할당 const SS = SpreadsheetApp.getActiveSpreadsheet() const T_SALESDATA = SS.getSheetByName('판매데이터') const T_CUSTOMER = SS.getSheetByName('거래처') const resultFolderId = '폴더 ID' const resultFolder = DriveApp.getFolderById(resultFolderId) const templateFile = DriveApp.getFileById('파일 ID') // 판매데이터, 대상 거래처 변수 선언 및 할당 const salesData = T_SALESDATA.getDataRange().getValues() const customers = T_CUSTOMER.getDataRange().getValues().filter(row => row[7] == true) Logger.log(customers) // 판매데이터 필터링 기간 및 템플릿 복사본 정의 시작 const startDate = new Date('2023-06-01 00:00:00') const endDate = new Date('2023-06-30 23:59:59') const startDateForName = Utilities.formatDate(startDate, 'GMT+9', 'yyyy-MM-dd') const endDateForName = Utilities.formatDate(endDate, 'GMT+9', 'yyyy-MM-dd') const templateCopyName = `월정산서_${startDateForName}_${endDateForName}` const templateCopyId = templateFile.makeCopy().moveTo(resultFolder).setName(templateCopyName).getId() const templateCopy = SpreadsheetApp.openById(templateCopyId) // 판매데이터 필터링 기간 및 템플릿 복사본 정의 종료 // 거래처별 정산서탭 생성 및 데이터 입력 for (i = 0; i < customers.length; i++) { // 거래처 정보 정의 const customer = { customerName: customers[i][1], ceoName: customers[i][4], contactNo: customers[i][5], email: customers[i][6] } Logger.log(`${customer.customerName}의 작업을 시작합니다.`) // salesData를 거래처명과 판매일시로 필터링하고, 정산서의 양식에 맞춰 열을 재배치 const salesDataFiltered = salesData.filter(row => row[1] == customer.customerName && row[0] >= startDate && row[0] <= endDate) const salesDataResult = salesDataFiltered.map((item, index) => [index + 1, item[0], item[4], item[5], item[6], item[7], item[8], item[9], item[10], item[11]]) // 탭이름을 정의하고, 템플릿 탭을 복사해서 탭이름으로 설정 const newTabName = `${customer.customerName}_${startDateForName}_${endDateForName}` const newTab = templateCopy.getSheetByName('템플릿').copyTo(templateCopy).setName(newTabName) // 새로운 탭에 해당 거래처의 판매데이터 입력 newTab.getRange(17, 2, salesDataResult.length, salesDataResult[0].length).setValues(salesDataResult) // 정산서에 거래처 정보 및 정산 금액 입력 시작 const salesDataSum = salesDataResult.map(item => item[9]).reduce((acc, cur) => acc + cur, 0) const cells = ['C8', 'C9', 'C10', 'E10', 'H13', 'K13', 'B13', 'H9'] const customerValues = [customer.customerName, customer.ceoName, customer.contactNo, customer.email, startDateForName, endDateForName, new Date(), salesDataSum] for (j = 0; j < cells.length; j++) { newTab.getRange(cells[j]).setValue(customerValues[j]) } // 정산서에 거래처 정보 및 정산 금액 입력 종료 // 페이지 행 조절 adjustTabRowsByPage(salesDataResult, newTab) // 월정산서 탭 드라이브 저장 const tabId = newTab.getSheetId() pdfSaveTab(templateCopyId, resultFolder, newTabName, tabId) } Logger.log(templateCopy.getUrl()) }
JavaScript
복사

[예제 07-10] 월정산 탭을 PDF로 변환해서 드라이브에 저장하고 파일 ID를 반환하는 함수

function pdfSaveTabReturnFileID(templateCopyId, resultFolder, newTabName, tabId) { const pdfUrl = `https://docs.google.com/spreadsheets/d/${templateCopyId}/export?format=pdf&size=A4&gid=${tabId}` const options = { headers: { 'Authorization': `Bearer ${ScriptApp.getOAuthToken()}` } } const blob = UrlFetchApp.fetch(pdfUrl, options).getAs('application/pdf') const fileId = DriveApp.createFile(blob).moveTo(resultFolder).setName(newTabName).getId() return fileId }
JavaScript
복사

[예제 07-11] 날짜에서 연도와 월 추출하기

function getDate() { const startDate = new Date('2023-06-01 00:00:00') const year = startDate.getFullYear() const month = startDate.getMonth() Logger.log(year) Logger.log(month) }
JavaScript
복사

[예제 07-12] startDate의 다음달로 정산월 값 생성하기

function getInvoiceMonth() { const startDate = new Date('2023-06-01 00:00:00') const year = startDate.getFullYear() const month = startDate.getMonth() const invoiceMonthDate = new Date(year, month + 1, 1) const invoiceMonth = Utilities.formatDate(invoiceMonthDate, 'GMT+9', 'yyyy년 MM월') Logger.log(invoiceMonthDate) Logger.log(invoiceMonth) }
JavaScript
복사

[예제 07-13] 매개변수를 받을 수 있도록 수정한 이메일 전송 함수

function sendEmailParam(fileId, customer, startDateForName, endDateForName, invoiceMonth) { const shareFile = DriveApp.getFileById(fileId) shareFile.setSharing(DriveApp.Access.ANYONE_WITH_LINK, DriveApp.Permission.VIEW) const shareUrl = shareFile.getUrl() const content = { customerName: customer.customerName, ceoName: customer.ceoName, startDateForName: startDateForName, endDateForName: endDateForName, shareUrl: shareUrl, invoiceMonth: invoiceMonth } const htmlBodyTemplate = evalEmailTemplate('htmlBodyInline', content) const message = { to: customer.email, name: '오토오피스', subject: '오토오피스 월정산서', htmlBody: htmlBodyTemplate } MailApp.sendEmail(message) Logger.log(`이메일이 전송되었습니다. 남은 이메일 발송 가능 수는 ${MailApp.getRemainingDailyQuota()}입니다.`) }
JavaScript
복사

[예제 07-14] 거래처별 월정산서를 구글 드라이브에 저장하고 이메일로 전송하는 함수

/* [거래처] 탭에 선택된 거래처의 월정산서를 생성하는 함수 최종 업데이트: YYYY년 MM월 DD일 작성자: 오토오피스 */ function createMonthlyInvoiceAndSendEmail() { // 탭, 템플릿과 결과물 저장 폴더 변수 선언 및 할당 const SS = SpreadsheetApp.getActiveSpreadsheet() const T_SALESDATA = SS.getSheetByName('판매데이터') const T_CUSTOMER = SS.getSheetByName('거래처') const resultFolderId = '폴더 ID' const resultFolder = DriveApp.getFolderById(resultFolderId) const templateFile = DriveApp.getFileById('파일 ID') // 판매데이터, 대상 거래처 변수 선언 및 할당 const salesData = T_SALESDATA.getDataRange().getValues() const customers = T_CUSTOMER.getDataRange().getValues().filter(row => row[7] == true) Logger.log(customers) // 판매데이터 필터링 기간 및 템플릿 복사본 정의 시작 const startDate = new Date('2023-06-01 00:00:00') const endDate = new Date('2023-06-30 23:59:59') const startDateForName = Utilities.formatDate(startDate, 'GMT+9', 'yyyy-MM-dd') const endDateForName = Utilities.formatDate(endDate, 'GMT+9', 'yyyy-MM-dd') const invoiceMonthDate = new Date(startDate.getFullYear(), startDate.getMonth() + 1, 1) const invoiceMonth = Utilities.formatDate(invoiceMonthDate, 'GMT+9', 'yyyy년 MM월') const templateCopyName = `월정산서_${startDateForName}_${endDateForName}` const templateCopyId = templateFile.makeCopy().moveTo(resultFolder).setName(templateCopyName).getId() const templateCopy = SpreadsheetApp.openById(templateCopyId) // 판매데이터 필터링 기간 및 템플릿 복사본 정의 종료 // 거래처별 정산서탭 생성 및 데이터 입력 for (i = 0; i < customers.length; i++) { // 거래처 정보 정의 const customer = { customerName: customers[i][1], ceoName: customers[i][4], contactNo: customers[i][5], email: customers[i][6] } Logger.log(`${customer.customerName}의 작업을 시작합니다.`) // salesData를 거래처명과 판매일시로 필터링하고, 정산서의 양식에 맞춰 열을 재배치 const salesDataFiltered = salesData.filter(row => row[1] == customer.customerName && row[0] >= startDate && row[0] <= endDate) const salesDataResult = salesDataFiltered.map((item, index) => [index + 1, item[0], item[4], item[5], item[6], item[7], item[8], item[9], item[10], item[11]]) // 탭이름을 정의하고, 템플릿 탭을 복사해서 탭이름으로 설정 const newTabName = `${customer.customerName}_${startDateForName}_${endDateForName}` const newTab = templateCopy.getSheetByName('템플릿').copyTo(templateCopy).setName(newTabName) // 새로운 탭에 해당 거래처의 판매데이터 입력 newTab.getRange(17, 2, salesDataResult.length, salesDataResult[0].length).setValues(salesDataResult) // 정산서에 거래처 정보 및 정산 금액 입력 시작 const salesDataSum = salesDataResult.map(item => item[9]).reduce((acc, cur) => acc + cur, 0) const cells = ['C8', 'C9', 'C10', 'E10', 'H13', 'K13', 'B13', 'H9'] const customerValues = [customer.customerName, customer.ceoName, customer.contactNo, customer.email, startDateForName, endDateForName, new Date(), salesDataSum] for (j = 0; j < cells.length; j++) { newTab.getRange(cells[j]).setValue(customerValues[j]) } // 정산서에 거래처 정보 및 정산 금액 입력 종료 // 페이지 행 조절 adjustTabRowsByPage(salesDataResult, newTab) // 월정산서 탭 드라이브 저장 const tabId = newTab.getSheetId() const fileId = pdfSaveTabReturnFileID(templateCopyId, resultFolder, newTabName, tabId) // 월정산서 PDF 이메일 발송 sendEmailParam(fileId, customer, startDateForName, endDateForName, invoiceMonth) } Logger.log(templateCopy.getUrl()) }
JavaScript
복사

[예제 07-15] try…catch 로 알아보는 오류 예시

function tryTest() { const arr = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] try { for (i = 0; i < 3; i++) { Logger.log(`i는 ${i}입니다.`) arr.pop() Logger.log(`arr는 ${arr}`) Logger.log(`arr[0]은 ${arr[0]}`) Logger.log(`arr[0] === undefined 입니까? ${arr[0] === undefined}`) Logger.log(`arr[0] === null 입니까? ${arr[0] === null}`) Logger.log(`arr[0].length는 ${arr[0].length}`) } } catch (e) { Logger.log(`오류가 발생했습니다: ${e}`) } }
JavaScript
복사

null과 undefined의 실행 로그

function nullAndUndefined(){ let a = null let b Logger.log(a) Logger.log(b) Logger.log(a === undefined) Logger.log(b === undefined) Logger.log(typeof a) Logger.log(typeof b) }
JavaScript
복사

[예제 07-16] try…catch를 적용한 거래처별 월정산서 이메일 전송 함수

/* [거래처] 탭에 선택된 거래처의 월정산서를 생성하는 함수 최종 업데이트: YYYY년 MM월 DD일 작성자: 오토오피스 */ function createMonthlyInvoiceAndSendEmail() { // 탭, 템플릿과 결과물 저장 폴더 변수 선언 및 할당 const SS = SpreadsheetApp.getActiveSpreadsheet() const T_SALESDATA = SS.getSheetByName('판매데이터') const T_CUSTOMER = SS.getSheetByName('거래처') const resultFolderId = '폴더 ID' const resultFolder = DriveApp.getFolderById(resultFolderId) const templateFile = DriveApp.getFileById('파일 ID') // 판매데이터, 대상 거래처 변수 선언 및 할당 const salesData = T_SALESDATA.getDataRange().getValues() const customers = T_CUSTOMER.getDataRange().getValues().filter(row => row[7] == true) Logger.log(customers) // 판매데이터 필터링 기간 및 템플릿 복사본 정의 시작 const startDate = new Date('2023-06-01 00:00:00') const endDate = new Date('2023-06-30 23:59:59') const startDateForName = Utilities.formatDate(startDate, 'GMT+9', 'yyyy-MM-dd') const endDateForName = Utilities.formatDate(endDate, 'GMT+9', 'yyyy-MM-dd') const invoiceMonthDate = new Date(startDate.getFullYear(), startDate.getMonth() + 1, 1) const invoiceMonth = Utilities.formatDate(invoiceMonthDate, 'GMT+9', 'yyyy년 MM월') const templateCopyName = `월정산서_${startDateForName}_${endDateForName}` const templateCopyId = templateFile.makeCopy().moveTo(resultFolder).setName(templateCopyName).getId() const templateCopy = SpreadsheetApp.openById(templateCopyId) // 판매데이터 필터링 기간 및 템플릿 복사본 정의 종료 // 거래처별 정산서탭 생성 및 데이터 입력 for (i = 0; i < customers.length; i++) { try { // 거래처 정보 정의 const customer = { customerName: customers[i][1], ceoName: customers[i][4], contactNo: customers[i][5], email: customers[i][6] } Logger.log(`${customer.customerName}의 작업을 시작합니다.`) // salesData를 거래처명과 판매일시로 필터링하고, 정산서의 양식에 맞춰 열을 재배치 const salesDataFiltered = salesData.filter(row => row[1] == customer.customerName && row[0] >= startDate && row[0] <= endDate) const salesDataResult = salesDataFiltered.map((item, index) => [index + 1, item[0], item[4], item[5], item[6], item[7], item[8], item[9], item[10], item[11]]) // 탭이름을 정의하고, 템플릿 탭을 복사해서 탭이름으로 설정 const newTabName = `${customer.customerName}_${startDateForName}_${endDateForName}` const newTab = templateCopy.getSheetByName('템플릿').copyTo(templateCopy).setName(newTabName) // 새로운 탭에 해당 거래처의 판매데이터 입력 newTab.getRange(17, 2, salesDataResult.length, salesDataResult[0].length).setValues(salesDataResult) // 정산서에 거래처 정보 및 정산 금액 입력 시작 const salesDataSum = salesDataResult.map(item => item[9]).reduce((acc, cur) => acc + cur, 0) const cells = ['C8', 'C9', 'C10', 'E10', 'H13', 'K13', 'B13', 'H9'] const customerValues = [customer.customerName, customer.ceoName, customer.contactNo, customer.email, startDateForName, endDateForName, new Date(), salesDataSum] for (j = 0; j < cells.length; j++) { newTab.getRange(cells[j]).setValue(customerValues[j]) } // 정산서에 거래처 정보 및 정산 금액 입력 종료 // 페이지 행 조절 adjustTabRowsByPage(salesDataResult, newTab) // 월정산서 탭 드라이브 저장 const tabId = newTab.getSheetId() const fileId = pdfSaveTabReturnFileID(templateCopyId, resultFolder, newTabName, tabId) // 월정산서 PDF 이메일 발송 sendEmailParam(fileId, customer, startDateForName, endDateForName, invoiceMonth) } catch (e) { Logger.log(`오류가 발생했습니다: ${e}`) } } Logger.log(templateCopy.getUrl()) }
JavaScript
복사

익숙해지기

// 1) 발송가능한 이메일 잔여 수량을 확인하는 방법은 무엇일까요? MailApp.getRemainingDailyQuota()
JavaScript
복사
// 2) 파일을 두 개 첨부하기 위해서 attachBlob에 어떻게 할당해야 할까요? function testEmail() { const attachBlob1 = DriveApp.getFileById('파일ID').getAs('application/pdf') const attachBlob2 = DriveApp.getFileById('파일ID').getAs('application/pdf') const attachBlob = [attachBlob1, attachBlob2] const message = { to: 'autooffice@naver.com', subject: '테스트 이메일', attachments: attachBlob } MailApp.sendEmail(message) }
JavaScript
복사
// 3) 오류가 발생했을 때 예외 처리를 하면서 오류 내용을 실행 로그에 표시하는 코드를 작성해보세요. try { // 실행할 코드 } catch (e) { Logger.log(`오류가 발생했습니다: ${e}`) }
JavaScript
복사

8장 실전! 월정산 시작일과 종료일 설정하기

8장 그림 보기

세팅 탭에서 날짜를 불러오도록 코드를 추가

const T_SETTING = SS.getSheetByName('세팅') const startDate = T_SETTING.getRange('A2').getValue() const endDate = T_SETTING.getRange('B2').getValue()
JavaScript
복사

종료일자에 23:59:59 추가하기

const endDate = T_SETTING.getRange('B2').getValue() const endDateFormat = Utilities.formatDate(endDate, 'GMT+9', 'yyyy-MM-dd 23:59:59') const endDateNew = new Date(endDateFormat) // 1줄로 쓰기 const endDate = new Date(Utilities.formatDate(T_SETTING.getRange('B2').getValue(), 'GMT+9', 'yyyy-MM-dd 23:59:59'))
JavaScript
복사

세팅 탭에서 날짜를 불러오는 createMonthlyInvoiceAndSendEmail 함수

/* [거래처] 탭에 선택된 거래처의 월정산서를 생성하는 함수 최종 업데이트: YYYY년 MM월 DD일 작성자: 오토오피스 */ function createMonthlyInvoiceAndSendEmail() { // 탭, 템플릿과 결과물 저장 폴더 변수 선언 및 할당 const SS = SpreadsheetApp.getActiveSpreadsheet() const T_SALESDATA = SS.getSheetByName('판매데이터') const T_CUSTOMER = SS.getSheetByName('거래처') const T_SETTING = SS.getSheetByName('세팅') const resultFolderId = '폴더 ID' const resultFolder = DriveApp.getFolderById(resultFolderId) const templateFile = DriveApp.getFileById('파일 ID') // 판매데이터, 대상 거래처 변수 선언 및 할당 const salesData = T_SALESDATA.getDataRange().getValues() const customers = T_CUSTOMER.getDataRange().getValues().filter(row => row[7] == true) Logger.log(customers) // 판매데이터 필터링 기간 및 템플릿 복사본 정의 시작 const startDate = T_SETTING.getRange('A2').getValue() const endDate = new Date(Utilities.formatDate(T_SETTING.getRange('B2').getValue(), 'GMT+9', 'yyyy-MM-dd 23:59:59')) const startDateForName = Utilities.formatDate(startDate, 'GMT+9', 'yyyy-MM-dd') const endDateForName = Utilities.formatDate(endDate, 'GMT+9', 'yyyy-MM-dd') const invoiceMonthDate = new Date(startDate.getFullYear(), startDate.getMonth() + 1, 1) const invoiceMonth = Utilities.formatDate(invoiceMonthDate, 'GMT+9', 'yyyy년 MM월') const templateCopyName = `월정산서_${startDateForName}_${endDateForName}` const templateCopyId = templateFile.makeCopy().moveTo(resultFolder).setName(templateCopyName).getId() const templateCopy = SpreadsheetApp.openById(templateCopyId) // 판매데이터 필터링 기간 및 템플릿 복사본 정의 종료 // 거래처별 정산서탭 생성 및 데이터 입력 for (i = 0; i < customers.length; i++) { try { // 거래처 정보 정의 const customer = { customerName: customers[i][1], ceoName: customers[i][4], contactNo: customers[i][5], email: customers[i][6] } Logger.log(`${customer.customerName}의 작업을 시작합니다.`) // salesData를 거래처명과 판매일시로 필터링하고, 정산서의 양식에 맞춰 열을 재배치 const salesDataFiltered = salesData.filter(row => row[1] == customer.customerName && row[0] >= startDate && row[0] <= endDate) const salesDataResult = salesDataFiltered.map((item, index) => [index + 1, item[0], item[4], item[5], item[6], item[7], item[8], item[9], item[10], item[11]]) // 탭이름을 정의하고, 템플릿 탭을 복사해서 탭이름으로 설정 const newTabName = `${customer.customerName}_${startDateForName}_${endDateForName}` const newTab = templateCopy.getSheetByName('템플릿').copyTo(templateCopy).setName(newTabName) // 새로운 탭에 해당 거래처의 판매데이터 입력 newTab.getRange(17, 2, salesDataResult.length, salesDataResult[0].length).setValues(salesDataResult) // 정산서에 거래처 정보 및 정산 금액 입력 시작 const salesDataSum = salesDataResult.map(item => item[9]).reduce((acc, cur) => acc + cur, 0) const cells = ['C8', 'C9', 'C10', 'E10', 'H13', 'K13', 'B13', 'H9'] const customerValues = [customer.customerName, customer.ceoName, customer.contactNo, customer.email, startDateForName, endDateForName, new Date(), salesDataSum] for (j = 0; j < cells.length; j++) { newTab.getRange(cells[j]).setValue(customerValues[j]) } // 정산서에 거래처 정보 및 정산 금액 입력 종료 // 페이지 행 조절 adjustTabRowsByPage(salesDataResult, newTab) // 월정산서 탭 드라이브 저장 const tabId = newTab.getSheetId() const fileId = pdfSaveTabReturnFileID(templateCopyId, resultFolder, newTabName, tabId) // 월정산서 PDF 이메일 발송 sendEmailParam(fileId, customer, startDateForName, endDateForName, invoiceMonth) } catch (e) { Logger.log(`오류가 발생했습니다: ${e}`) } } Logger.log(templateCopy.getUrl()) }
JavaScript
복사

[예제 08-01] 프롬프트 테스트 함수

function promptTest() { const ui = SpreadsheetApp.getUi() const response = ui.prompt('아무거나 입력해보세요') Logger.log(response.getResponseText()) }
JavaScript
복사

parseInt를 사용해 문자열을 정수로 변환하고 계산을 수행

function promptTestAddNum() { const ui = SpreadsheetApp.getUi() const response = ui.prompt('아무거나 입력해보세요') Logger.log(response.getResponseText()) const addNumber = parseInt(response.getResponseText()) + 100 Logger.log(addNumber) }
JavaScript
복사

[예제 08-02] 프롬프트 응답을 날짜로 변환하기

function getPromptAsDate() { const ui = SpreadsheetApp.getUi() const response = ui.prompt('날짜를 입력해보세요. yyyy-MM-dd의 형식을 지켜주세요.') const resText = response.getResponseText() Logger.log(resText) const date = new Date(`${resText} 00:00:00`) Logger.log(date) }
JavaScript
복사

[예제 08-03] 프롬프트 창 두 번 표시해서 시작일과 종료일 입력받기

function getPromptDates() { const ui = SpreadsheetApp.getUi() const responseStart = ui.prompt('정산 시작일자를 입력하세요') const resStartText = responseStart.getResponseText() Logger.log(resStartText) const startDate = new Date(`${resStartText} 00:00:00`) // 시작일자에 자정을 설정 Logger.log(startDate) const responseEnd = ui.prompt('정산 종료일자를 입력하세요') const resEndText = responseEnd.getResponseText() Logger.log(resEndText) const endDate = new Date(`${resEndText} 23:59:59`) // 종료일자에 23:59:59를 설정 Logger.log(endDate) }
JavaScript
복사

[예제 08-04] 정규 표현식으로 가장 먼저 나오는 숫자 1개 추출하기

function regexTest() { const date = '2023-06-01' const regex = new RegExp(/\d/) const dateRegex = regex.exec(date) Logger.log(dateRegex) }
JavaScript
복사

정규표현식으로 숫자 4개 추출하기

function regexTest() { const date = '2023-06-01' const regex = new RegExp(/\d{4}/) const dateRegex = regex.exec(date) Logger.log(dateRegex) }
JavaScript
복사

정규표현식으로 yyyy-MM-dd 추출하기

function regexTest() { const date = '2023-06-31' const regex = new RegExp(/\d{4}-\d{2}-\d{2}/) const dateRegex = regex.exec(date) Logger.log(dateRegex) }
JavaScript
복사

정규표현식으로 yyyy-MM-dd 추출하기 (시작과 종료 제한)

function regexTest() { const date = '2023-06-31' const regex = new RegExp(/^\d{4}-\d{2}-\d{2}$/) const dateRegex = regex.exec(date) Logger.log(dateRegex) }
JavaScript
복사

[예제 08-05] 정규표현식으로 정산 시작일자 검증하기

function getPromptDateRegex() { const ui = SpreadsheetApp.getUi() const regex = new RegExp(/^\d{4}-\d{2}-\d{2}$/) const response = ui.prompt('정산 시작일자를 입력하세요') const resText = response.getResponseText() Logger.log(resText) const resTextRegex = regex.exec(resText) Logger.log(resTextRegex) const date = new Date(`${resTextRegex[0]} 00:00:00`) Logger.log(date) }
JavaScript
복사

[예제 08-06] while문으로 올바른 날짜를 입력받을 때까지 프롬프트 표시하기

function getPromptDateRegex() { const ui = SpreadsheetApp.getUi() const regex = new RegExp(/^\d{4}-\d{2}-\d{2}$/) // while문의 초기값 let resTextRegex = null Logger.log(`resTextRegex의 초기값은 ${resTextRegex}`) // 정규 표현식에 해당하는 값이 입력되지 않으면 while문을 계속 실행 while (resTextRegex == null) { const response = ui.prompt('정산 시작일자를 입력하세요') const resText = response.getResponseText() resTextRegex = regex.exec(resText) Logger.log(resText) Logger.log(`정규표현식을 적용한 resTextRegex는 ${resTextRegex}`) Logger.log(`resTextRegex == null? ${resTextRegex == null}`) } // 정규 표현식에 해당하는 값이 입력되면 while문을 종료하고 날짜로 변환 const date = new Date(`${resTextRegex[0]} 00:00:00`) Logger.log(date) }
JavaScript
복사

[예제 08-07] 공백 또는 0을 입력하면 프롬프트 종료하기 추가한 함수

function getPromptDateRegex() { const ui = SpreadsheetApp.getUi() const regex = new RegExp(/^\d{4}-\d{2}-\d{2}$/) // while문의 초기값 let resTextRegex = null let resText Logger.log(`resTextRegex의 초기값은 ${resTextRegex}`) // 정규 표현식에 해당하는 값이 입력되지 않으면 while문을 계속 실행 while (resTextRegex == null && resText != 0) { const response = ui.prompt('정산 시작일자를 입력하세요') resText = response.getResponseText() resTextRegex = regex.exec(resText) Logger.log(resText) Logger.log(`정규표현식을 적용한 resTextRegex는는 ${resTextRegex}`) Logger.log(`resTextRegex == null? ${resTextRegex == null}`) Logger.log(`resText != 0? ${resText != 0}`) } // 올바른 날짜가 입력된 경우에만 startDate에 값을 할당 let date if (resTextRegex != null) { date = new Date(`${resTextRegex[0]} 00:00:00`) } Logger.log(date) }
JavaScript
복사

[예제 08-08] 매개변수를 입력받는 프롬프트 함수

function getPromptDateRegex(promptTitle = '정산 시작일자를 입력하세요', startDatebool = true) { const ui = SpreadsheetApp.getUi() const regex = new RegExp(/^\d{4}-\d{2}-\d{2}$/) // while문의 초기값 let resTextRegex = null let resText Logger.log(`resTextRegex의 초기값은 ${resTextRegex}`) // 정규 표현식에 해당하는 값이 입력되지 않으면 while문을 계속 실행 while (resTextRegex == null && resText != 0) { const response = ui.prompt(promptTitle) resText = response.getResponseText() resTextRegex = regex.exec(resText) Logger.log(resText) Logger.log(`정규표현식을 적용한 resTextRegex는는 ${resTextRegex}`) Logger.log(`resTextRegex == null? ${resTextRegex == null}`) Logger.log(`resText != 0? ${resText != 0}`) } // 올바른 날짜가 입력된 경우에만 date에 값을 할당 let date if (resTextRegex != null) { time = startDatebool == true ? '00:00:00' : '23:59:59' date = new Date(`${resTextRegex[0]} ${time}`) } Logger.log(date) return date }
JavaScript
복사

[예제 08-09] 오류 발생시 프로세스를 중단시키는 프롬프트 함수

function getPromptDateRegex(promptTitle = '정산 시작일자를 입력하세요', startDateBool = true) { const ui = SpreadsheetApp.getUi() const regex = new RegExp(/^\d{4}-\d{2}-\d{2}$/) // while문의 초기값 let resTextRegex = null let resText Logger.log(`resTextRegex의 초기값은 ${resTextRegex}`) // 정규 표현식에 해당하는 값이 입력되지 않으면 while문을 계속 실행 while (resTextRegex == null && resText != 0) { const response = ui.prompt(promptTitle) resText = response.getResponseText() resTextRegex = regex.exec(resText) Logger.log(resText) Logger.log(`정규표현식을 적용한 resTextRegex는 ${resTextRegex}`) Logger.log(`resTextRegex == null? ${resTextRegex == null}`) Logger.log(`resText != 0? ${resText != 0}`) } // 올바른 날짜가 입력된 경우에만 date에 값을 할당 let date if (resTextRegex != null) { const time = startDateBool == true ? '00:00:00' : '23:59:59' date = new Date(`${resTextRegex[0]} ${time}`) } else { throw new Error('올바른 날짜 값이 입력되지 않아서 종료합니다.') } Logger.log(date) return date }
JavaScript
복사

프롬프트 입력을 받는 createMonthlyInvoiceAndSendEmailPrompt 함수

/* [거래처] 탭에 선택된 거래처의 월정산서를 생성하는 함수 최종 업데이트: YYYY년 MM월 DD일 작성자: 오토오피스 */ function createMonthlyInvoiceAndSendEmailPrompt() { // 탭, 템플릿과 결과물 저장 폴더 변수 선언 및 할당 const SS = SpreadsheetApp.getActiveSpreadsheet() const T_SALESDATA = SS.getSheetByName('판매데이터') const T_CUSTOMER = SS.getSheetByName('거래처') const resultFolderId = '폴더 ID' const resultFolder = DriveApp.getFolderById(resultFolderId) const templateFile = DriveApp.getFileById('파일 ID') // 판매데이터, 대상 거래처 변수 선언 및 할당 const salesData = T_SALESDATA.getDataRange().getValues() const customers = T_CUSTOMER.getDataRange().getValues().filter(row => row[7] == true) Logger.log(customers) // 판매데이터 필터링 기간 및 템플릿 복사본 정의 시작 const startDate = getPromptDateRegex(promptTitle = '정산 시작일자를 입력하세요', startDateBool = true) const endDate = getPromptDateRegex(promptTitle = '정산 종료일자를 입력하세요', startDateBool = false) const startDateForName = Utilities.formatDate(startDate, 'GMT+9', 'yyyy-MM-dd') const endDateForName = Utilities.formatDate(endDate, 'GMT+9', 'yyyy-MM-dd') const invoiceMonthDate = new Date(startDate.getFullYear(), startDate.getMonth() + 1, 1) const invoiceMonth = Utilities.formatDate(invoiceMonthDate, 'GMT+9', 'yyyy년 MM월') const templateCopyName = `월정산서_${startDateForName}_${endDateForName}` const templateCopyId = templateFile.makeCopy().moveTo(resultFolder).setName(templateCopyName).getId() const templateCopy = SpreadsheetApp.openById(templateCopyId) // 판매데이터 필터링 기간 및 템플릿 복사본 정의 종료 // 거래처별 정산서탭 생성 및 데이터 입력 for (i = 0; i < customers.length; i++) { try { // 거래처 정보 정의 const customer = { customerName: customers[i][1], ceoName: customers[i][4], contactNo: customers[i][5], email: customers[i][6] } Logger.log(`${customer.customerName}의 작업을 시작합니다.`) // salesData를 거래처명과 판매일시로 필터링하고, 정산서의 양식에 맞춰 열을 재배치 const salesDataFiltered = salesData.filter(row => row[1] == customer.customerName && row[0] >= startDate && row[0] <= endDate) const salesDataResult = salesDataFiltered.map((item, index) => [index + 1, item[0], item[4], item[5], item[6], item[7], item[8], item[9], item[10], item[11]]) // 탭이름을 정의하고, 템플릿 탭을 복사해서 탭이름으로 설정 const newTabName = `${customer.customerName}_${startDateForName}_${endDateForName}` const newTab = templateCopy.getSheetByName('템플릿').copyTo(templateCopy).setName(newTabName) // 새로운 탭에 해당 거래처의 판매데이터 입력 newTab.getRange(17, 2, salesDataResult.length, salesDataResult[0].length).setValues(salesDataResult) // 정산서에 거래처 정보 및 정산 금액 입력 시작 const salesDataSum = salesDataResult.map(item => item[9]).reduce((acc, cur) => acc + cur, 0) const cells = ['C8', 'C9', 'C10', 'E10', 'H13', 'K13', 'B13', 'H9'] const customerValues = [customer.customerName, customer.ceoName, customer.contactNo, customer.email, startDateForName, endDateForName, new Date(), salesDataSum] for (j = 0; j < cells.length; j++) { newTab.getRange(cells[j]).setValue(customerValues[j]) } // 정산서에 거래처 정보 및 정산 금액 입력 종료 // 페이지 행 조절 adjustTabRowsByPage(salesDataResult, newTab) // 월정산서 탭 드라이브 저장 const tabId = newTab.getSheetId() const fileId = pdfSaveTabReturnFileID(templateCopyId, resultFolder, newTabName, tabId) // 월정산서 PDF 이메일 발송 sendEmailParam(fileId, customer, startDateForName, endDateForName, invoiceMonth) } catch (e) { Logger.log(`오류가 발생했습니다: ${e}`) } } Logger.log(templateCopy.getUrl()) }
JavaScript
복사

[예제 08-10] 확인 버튼만 있는 알림 메시지 표시하기

function alert() { const ui = SpreadsheetApp.getUi() ui.alert('알림 메시지입니다.') }
JavaScript
복사

알림을 추가한 프롬프트 함수

function getPromptDateRegex(promptTitle = '정산 시작일자를 입력하세요', startDateBool = true) { const ui = SpreadsheetApp.getUi() const regex = new RegExp(/^\d{4}-\d{2}-\d{2}$/) // while문의 초기값 let resTextRegex = null let resText Logger.log(`resTextRegex의 초기값은 ${resTextRegex}`) ui.alert('날짜는 yyyy-MM-dd 서식으로 입력하셔야 합니다.') // 정규 표현식에 해당하는 값이 입력되지 않으면 while문을 계속 실행 while (resTextRegex == null && resText != 0) { const response = ui.prompt(promptTitle) resText = response.getResponseText() resTextRegex = regex.exec(resText) Logger.log(resText) Logger.log(`정규표현식을 적용한 resTextRegex는 ${resTextRegex}`) Logger.log(`resTextRegex == null? ${resTextRegex == null}`) Logger.log(`resText != 0? ${resText != 0}`) } // 올바른 날짜가 입력된 경우에만 date에 값을 할당 let date if (resTextRegex != null) { const time = startDateBool == true ? '00:00:00' : '23:59:59' date = new Date(`${resTextRegex[0]} ${time}`) } else { ui.alert('공백 또는 0이 입력되어서 종료합니다.') throw new Error('올바른 날짜 값이 입력되지 않아서 종료합니다.') } Logger.log(date) return date }
JavaScript
복사

[예제 08-11] 확인, 취소가 선택가능한 알림메시지 표시하기

function alertSelect() { const ui = SpreadsheetApp.getUi() const response = ui.alert('프로세스를 진행할까요?', ui.ButtonSet.OK_CANCEL) Logger.log(response) if(response == ui.Button.OK){ Logger.log('확인을 클릭하셨습니다.') }else{ Logger.log('취소를 클릭하셨습니다.') } }
JavaScript
복사

[예제 08-12] 확인, 취소가 선택가능한 프롬프트 표시하기

function promptSelect(){ const ui = SpreadsheetApp.getUi() const response = ui.prompt('알림','날짜를 입력해주세요', ui.ButtonSet.OK_CANCEL) Logger.log(response.getResponseText()) Logger.log(response.getSelectedButton()) if(response.getSelectedButton() == 'OK'){ Logger.log(`응답: ${response.getResponseText()}`) Logger.log('확인을 클릭하셨습니다.') }else{ Logger.log(`응답: ${response.getResponseText()}`) Logger.log('취소를 클릭하셨습니다.') } }
JavaScript
복사

[예제 08-13] 토스트 메시지

function toast() { const SS = SpreadsheetApp.getActiveSpreadsheet() SS.toast('토스트 메시지입니다.') }
JavaScript
복사

익숙해지기

// 1) 프롬프트의 응답을 로그에 표시하려면 어떻게 해야 할까요? function promptTest() { const ui = SpreadsheetApp.getUi() const response = ui.prompt('아무거나 입력해보세요') Logger.log(response.getResponseText()) }
JavaScript
복사
// 2) yyyy-MM-dd 형식의 값을 추출하는 정규 표현식을 작성해보세요. const regex = new RegExp(/^\d{4}-\d{2}-\d{2}$/)
JavaScript
복사
// 3) 확인, 취소버튼이 있는 알림 메시지를 띄우려면 어떻게 해야 할까요? function alertSelect() { const ui = SpreadsheetApp.getUi() const response = ui.alert('프로세스를 진행할까요?', ui.ButtonSet.OK_CANCEL) Logger.log(response) if(response == ui.Button.OK){ Logger.log('확인을 클릭하셨습니다.') }else{ Logger.log('취소를 클릭하셨습니다.') } }
JavaScript
복사

9장 트리거 사용하기

9장 그림 보기

[예제 09-01] onOpen(e)를 사용한 메뉴 추가

function onOpen(e) { SpreadsheetApp.getUi() .createMenu('자동화 함수') .addItem('월정산서 이메일 발송', 'createMonthlyInvoiceAndSendEmailPrompt') .addSeparator() .addSubMenu(SpreadsheetApp.getUi().createMenu('서브 메뉴') .addItem('서브메뉴 1', '서브 함수 1') .addItem('서브메뉴 2', '서브 함수 2')) .addToUi() }
JavaScript
복사

[예제 09-02] onEdit(e)의 매개변수 e 파악하기

function onEdit(e){ Logger.log(e) Logger.log(`e.source는 ${e.source}`) Logger.log(`e.source의 이름은 ${e.source.getName()}`) Logger.log(`e.range는 ${e.range}`) Logger.log(`e.range의 A1노테이션은 ${e.range.getA1Notation()}`) Logger.log(`e.oldValue는 ${e.oldValue}`) Logger.log(`e.value는 ${e.value}`) Logger.log(`e.user는 ${e.user}`) }
JavaScript
복사

[예제 09-03] onEdit(e)로 거래처 체크박스 토글하기

function onEdit(e) { const SS = e.source const T_ACTIVE = SS.getActiveSheet() const T_ACTIVE_NAME = T_ACTIVE.getName() const editCell = e.range.getA1Notation() const editCellValue = e.value const T_ACTIVE_LASTROW = getLastRowOfCol(T_ACTIVE, 'A') const checkArr = new Array(T_ACTIVE_LASTROW - 1) Logger.log(T_ACTIVE_NAME) Logger.log(editCell) Logger.log(editCellValue) if (T_ACTIVE_NAME == '거래처' && editCell == 'H1' && editCellValue == 'TRUE') { checkArr.fill([true]) T_ACTIVE.getRange(`H2:H${T_ACTIVE_LASTROW}`).setValues(checkArr) } else if (T_ACTIVE_NAME == '거래처' && editCell == 'H1' && editCellValue == 'FALSE') { checkArr.fill([false]) T_ACTIVE.getRange(`H2:H${T_ACTIVE_LASTROW}`).setValues(checkArr) } else { Logger.log('거래처의 H1셀이 변경되지 않았습니다.') } }
JavaScript
복사

지난달 월정산서 이메일로 보내기 함수 (트리거)

/* [거래처] 탭에 선택된 거래처의 월정산서를 생성하는 함수 최종 업데이트: YYYY년 MM월 DD일 작성자: 오토오피스 */ function createMonthlyInvoiceAndSendEmailTrigger() { // 탭, 템플릿과 결과물 저장 폴더 변수 선언 및 할당 const SS = SpreadsheetApp.getActiveSpreadsheet() const T_SALESDATA = SS.getSheetByName('판매데이터') const T_CUSTOMER = SS.getSheetByName('거래처') const resultFolderId = '폴더ID' const resultFolder = DriveApp.getFolderById(resultFolderId) const templateFile = DriveApp.getFileById('파일ID') // 판매데이터, 대상 거래처 변수 선언 및 할당 const salesData = T_SALESDATA.getDataRange().getValues() const customers = T_CUSTOMER.getDataRange().getValues().filter(row => row[7] == true) Logger.log(customers) // 판매데이터 필터링 기간 및 템플릿 복사본 정의 시작 const today = new Date() const startDate = new Date(today.getFullYear(), today.getMonth() - 1, 1, 00, 00, 00) const endDate = new Date(today.getFullYear(), today.getMonth(), 0, 23, 59, 59) const startDateForName = Utilities.formatDate(startDate, 'GMT+9', 'yyyy-MM-dd') const endDateForName = Utilities.formatDate(endDate, 'GMT+9', 'yyyy-MM-dd') const invoiceMonthDate = new Date(startDate.getFullYear(), startDate.getMonth() + 1, 1) const invoiceMonth = Utilities.formatDate(invoiceMonthDate, 'GMT+9', 'yyyy년 MM월') const templateCopyName = `월정산서_${startDateForName}_${endDateForName}` const templateCopyId = templateFile.makeCopy().moveTo(resultFolder).setName(templateCopyName).getId() const templateCopy = SpreadsheetApp.openById(templateCopyId) // 판매데이터 필터링 기간 및 템플릿 복사본 정의 종료 // 거래처별 정산서탭 생성 및 데이터 입력 for (i = 0; i < customers.length; i++) { try { // 거래처 정보 정의 const customer = { customerName: customers[i][1], ceoName: customers[i][4], contactNo: customers[i][5], email: customers[i][6] } Logger.log(`${customer.customerName}의 작업을 시작합니다.`) // salesData를 거래처명과 판매일시로 필터링하고, 정산서의 양식에 맞춰 열을 재배치 const salesDataFiltered = salesData.filter(row => row[1] == customer.customerName && row[0] >= startDate && row[0] <= endDate) const salesDataResult = salesDataFiltered.map((item, index) => [index + 1, item[0], item[4], item[5], item[6], item[7], item[8], item[9], item[10], item[11]]) // 탭이름을 정의하고, 템플릿 탭을 복사해서 탭이름으로 설정 const newTabName = `${customer.customerName}_${startDateForName}_${endDateForName}` const newTab = templateCopy.getSheetByName('템플릿').copyTo(templateCopy).setName(newTabName) // 새로운 탭에 해당 거래처의 판매데이터 입력 newTab.getRange(17, 2, salesDataResult.length, salesDataResult[0].length).setValues(salesDataResult) // 정산서에 거래처 정보 및 정산 금액 입력 시작 const salesDataSum = salesDataResult.map(item => item[9]).reduce((acc, cur) => acc + cur, 0) const cells = ['C8', 'C9', 'C10', 'E10', 'H13', 'K13', 'B13', 'H9'] const customerValues = [customer.customerName, customer.ceoName, customer.contactNo, customer.email, startDateForName, endDateForName, new Date(), salesDataSum] for (j = 0; j < cells.length; j++) { newTab.getRange(cells[j]).setValue(customerValues[j]) } // 정산서에 거래처 정보 및 정산 금액 입력 종료 // 페이지 행 조절 adjustTabRowsByPage(salesDataResult, newTab) // 월정산서 탭 드라이브 저장 const tabId = newTab.getSheetId() const fileId = pdfSaveTabReturnFileID(templateCopyId, resultFolder, newTabName, tabId) // 월정산서 PDF 이메일 발송 sendEmailParam(fileId, customer, startDateForName, endDateForName, invoiceMonth) } catch (e) { Logger.log(`오류가 발생했습니다: ${e}`) } } Logger.log(templateCopy.getUrl()) }
JavaScript
복사

[예제 09-04] 매달 1일 01시 00분 부근에 함수를 실행하는 트리거 추가하기

function addTrigger() { ScriptApp.newTrigger('createMonthlyInvoiceAndSendEmailTrigger') .timeBased() .onMonthDay(1) .atHour(01) .nearMinute(00) .create() }
JavaScript
복사

[예제 09-05] 트리거 정보 확인하기

function checkTriggers() { const triggers = ScriptApp.getProjectTriggers() Logger.log(triggers) triggers.forEach(trigger => { const triggerId = trigger.getUniqueId() const triggerHF = trigger.getHandlerFunction() const triggerET = trigger.getEventType() const triggerSource = trigger.getTriggerSource() const triggerSourceId = trigger.getTriggerSourceId() Logger.log(triggerId) Logger.log(triggerHF) Logger.log(triggerET) Logger.log(triggerSource) Logger.log(triggerSourceId) }) }
JavaScript
복사

[예제 09-06] 고유 식별자로 트리거 삭제하기

function deleteTrigger() { const triggers = ScriptApp.getProjectTriggers() triggers.forEach((trigger, index) => { const triggerId = trigger.getUniqueId() Logger.log([triggerId, index]) if (triggerId == '6886916666966533390') { ScriptApp.deleteTrigger(triggers[index]) Logger.log('트리거가 삭제되었습니다.') } }) }
JavaScript
복사

[예제 09-07] 함수명 createMonthlyInvoiceAndSendEmailTrigger만 제외하고 모든 트리거 삭제하기

function deleteTriggerExcept() { const triggers = ScriptApp.getProjectTriggers() triggers.forEach((trigger, index) => { const triggerId = trigger.getUniqueId() const triggerHF = trigger.getHandlerFunction() Logger.log([triggerId, index, triggerHF]) if (triggerHF != 'createMonthlyInvoiceAndSendEmailTrigger') { ScriptApp.deleteTrigger(triggers[index]) Logger.log('트리거가 삭제되었습니다.') } }) }
JavaScript
복사

[예제 09-08] 거래처를 그룹으로 만들기

function createTriggers() { const SS = SpreadsheetApp.getActiveSpreadsheet() const T_CUSTOMER = SS.getSheetByName('거래처') const customersSize = T_CUSTOMER.getDataRange().getValues().slice(1, 51).length Logger.log(customersSize) const groupSize = 15 const quotient = Math.floor(customersSize / groupSize) const remainder = customersSize % groupSize let customerGroups = [] for (i = 0; i < quotient; i++) { const groupStartRow = groupSize * i const groupEndRow = groupSize * (i + 1) - 1 const customerGroupIndexes = [groupStartRow, groupEndRow] customerGroups.push(customerGroupIndexes) } if (remainder != 0) { const customerGroupIndexesLast = [groupSize * i, groupSize * i + remainder - 1] customerGroups.push(customerGroupIndexesLast) } Logger.log(customerGroups) }
JavaScript
복사

일정 그룹 만들기

let scheduleGroups = [] for (j = 0; j < customerGroups.length; j++) { const schedule = 360000 * j + 1000 scheduleGroups.push(schedule) }
JavaScript
복사

그룹별로 트리거 생성하기

for (k = 0; k < customerGroups.length; k++) { const trigger = ScriptApp.newTrigger('triggerHF') .timeBased() .after(scheduleGroups[k]) .create() const triggerId = trigger.getUniqueId() let groupInfo = {} groupInfo.customerGroup = customerGroups[k] const cache = CacheService.getDocumentCache() cache.put(triggerId, JSON.stringify(groupInfo), 1800) Logger.log(triggerId) }
JavaScript
복사

[예제 09-09] createTriggers 함수

function createTriggers() { const SS = SpreadsheetApp.getActiveSpreadsheet() const T_CUSTOMER = SS.getSheetByName('거래처') const customersSize = T_CUSTOMER.getDataRange().getValues().slice(1, 51).length Logger.log(customersSize) const groupSize = 15 const quotient = Math.floor(customersSize / groupSize) const remainder = customersSize % groupSize let customerGroups = [] for (i = 0; i < quotient; i++) { const groupStartRow = groupSize * i const groupEndRow = groupSize * (i + 1) - 1 const customerGroupIndexes = [groupStartRow, groupEndRow] customerGroups.push(customerGroupIndexes) } if (remainder != 0) { const customerGroupIndexesLast = [groupSize * i, groupSize * i + remainder - 1] customerGroups.push(customerGroupIndexesLast) } Logger.log(customerGroups) let scheduleGroups = [] for (j = 0; j < customerGroups.length; j++) { const schedule = 1000 scheduleGroups.push(schedule) } Logger.log(scheduleGroups) for (k = 0; k < customerGroups.length; k++) { const trigger = ScriptApp.newTrigger('triggerHF') .timeBased() .after(scheduleGroups[k]) .create() const triggerId = trigger.getUniqueId() let groupInfo = {} groupInfo[triggerId] = customerGroups[k] const cache = CacheService.getDocumentCache() cache.put(triggerId, JSON.stringify(groupInfo), 1800) Logger.log(triggerId) } }
JavaScript
복사

[예제 09-10] triggerHF 함수

function triggerHF(e) { Logger.log(e) const triggerId = e.triggerUid const cache = CacheService.getDocumentCache() const groupInfo = JSON.parse(cache.get(triggerId)) Logger.log(groupInfo) createMonthlyInvoiceGroup(groupInfo) }
JavaScript
복사

[예제 09-11] createMonthlyInvoiceGroup 함수

/* [거래처] 탭에 선택된 거래처의 월정산서를 생성하는 함수 최종 업데이트: YYYY년 MM월 DD일 작성자: 오토오피스 */ function createMonthlyInvoiceGroup(groupInfo) { // 탭, 템플릿과 결과물 저장 폴더 변수 선언 및 할당 const SS = SpreadsheetApp.getActiveSpreadsheet() const T_SALESDATA = SS.getSheetByName('판매데이터') const T_CUSTOMER = SS.getSheetByName('거래처') const resultFolderId = '폴더ID' const resultFolder = DriveApp.getFolderById(resultFolderId) const templateFile = DriveApp.getFileById('파일ID') // 판매데이터, 대상 거래처 변수 선언 및 할당 const groupStartRow = groupInfo.customerGroup[0] const groupEndRow = groupInfo.customerGroup[1] const salesData = T_SALESDATA.getDataRange().getValues() const customers = T_CUSTOMER.getDataRange().getValues().slice(1).slice(groupStartRow, groupEndRow + 1) Logger.log(customers) // 판매데이터 필터링 기간 및 템플릿 복사본 정의 시작 const today = new Date() const startDate = new Date(today.getFullYear(), today.getMonth() - 1, 1, 00, 00, 00) const endDate = new Date(today.getFullYear(), today.getMonth(), 0, 23, 59, 59) const startDateForName = Utilities.formatDate(startDate, 'GMT+9', 'yyyy-MM-dd') const endDateForName = Utilities.formatDate(endDate, 'GMT+9', 'yyyy-MM-dd') const invoiceMonthDate = new Date(startDate.getFullYear(), startDate.getMonth() + 1, 1) const invoiceMonth = Utilities.formatDate(invoiceMonthDate, 'GMT+9', 'yyyy년 MM월') const templateCopyName = `월정산서_${startDateForName}_${endDateForName}` const templateCopyId = templateFile.makeCopy().moveTo(resultFolder).setName(templateCopyName).getId() const templateCopy = SpreadsheetApp.openById(templateCopyId) // 판매데이터 필터링 기간 및 템플릿 복사본 정의 종료 // 거래처별 정산서탭 생성 및 데이터 입력 for (i = 0; i < customers.length; i++) { try { // 거래처 정보 정의 const customer = { customerName: customers[i][1], ceoName: customers[i][4], contactNo: customers[i][5], email: customers[i][6] } Logger.log(`${customer.customerName}의 작업을 시작합니다.`) // salesData를 거래처명과 판매일시로 필터링하고, 정산서의 양식에 맞춰 열을 재배치 const salesDataFiltered = salesData.filter(row => row[1] == customer.customerName && row[0] >= startDate && row[0] <= endDate) const salesDataResult = salesDataFiltered.map((item, index) => [index + 1, item[0], item[4], item[5], item[6], item[7], item[8], item[9], item[10], item[11]]) // 탭이름을 정의하고, 템플릿 탭을 복사해서 탭이름으로 설정 const newTabName = `${customer.customerName}_${startDateForName}_${endDateForName}` const newTab = templateCopy.getSheetByName('템플릿').copyTo(templateCopy).setName(newTabName) // 새로운 탭에 해당 거래처의 판매데이터 입력 newTab.getRange(17, 2, salesDataResult.length, salesDataResult[0].length).setValues(salesDataResult) // 정산서에 거래처 정보 및 정산 금액 입력 시작 const salesDataSum = salesDataResult.map(item => item[9]).reduce((acc, cur) => acc + cur, 0) const cells = ['C8', 'C9', 'C10', 'E10', 'H13', 'K13', 'B13', 'H9'] const customerValues = [customer.customerName, customer.ceoName, customer.contactNo, customer.email, startDateForName, endDateForName, new Date(), salesDataSum] for (j = 0; j < cells.length; j++) { newTab.getRange(cells[j]).setValue(customerValues[j]) } // 정산서에 거래처 정보 및 정산 금액 입력 종료 // 페이지 행 조절 adjustTabRowsByPage(salesDataResult, newTab) // 월정산서 탭 드라이브 저장 const tabId = newTab.getSheetId() const fileId = pdfSaveTabReturnFileID(templateCopyId, resultFolder, newTabName, tabId) // 월정산서 PDF 이메일 발송 sendEmailParam(fileId, customer, startDateForName, endDateForName, invoiceMonth) } catch (e) { Logger.log(`오류가 발생했습니다: ${e}`) } } Logger.log(templateCopy.getUrl()) }
JavaScript
복사

10장 스크립트 로그 기록하기

10장 그림 보기

[예제 10-01] 10자리의 영문과 숫자의 랜덤한 조합 생성하기

function getRandomId() { const idLength = 10 const charPool = 'abcdefghijklmnopqrstuvwxyz0123456789' let randomId = '' for (let i = 0; i < idLength; i++) { randomId += charPool.charAt(Math.floor(Math.random() * charPool.length)) } return randomId }
JavaScript
복사

[예제 10-02] 로그 배열 생성을 추가한 createTriggersWithLog 함수

function createTriggersWithLog() { // 로그를 위한 변수 정의 const logId = getRandomId() const scriptName = '월정산서 발송 스크립트' const scriptStartTime = new Date() const userEmail = Session.getActiveUser().getEmail() const today = new Date('2023-07-01') const startDate = new Date(today.getFullYear(), today.getMonth() - 1, 1, 00, 00, 00) const endDate = new Date(today.getFullYear(), today.getMonth(), 0, 23, 59, 59) const startDateForName = Utilities.formatDate(startDate, 'GMT+9', 'yyyy-MM-dd') const endDateForName = Utilities.formatDate(endDate, 'GMT+9', 'yyyy-MM-dd') const invoiceMonthDate = new Date(startDate.getFullYear(), startDate.getMonth() + 1, 1) const invoiceMonth = Utilities.formatDate(invoiceMonthDate, 'GMT+9', 'yyyy년 MM월') // 거래처 목록 정의 const SS = SpreadsheetApp.getActiveSpreadsheet() const T_CUSTOMER = SS.getSheetByName('거래처') const customersSize = T_CUSTOMER.getDataRange().getValues().slice(1, 11).length Logger.log(customersSize) // 거래처를 나눌 그룹 사이즈 정의 const groupSize = 3 const quotient = Math.floor(customersSize / groupSize) const remainder = customersSize % groupSize // 거래처 그룹의 시작과 종료 순번을 배열로 생성 let customerGroups = [] for (i = 0; i < quotient; i++) { const groupStartRow = groupSize * i const groupEndRow = groupSize * (i + 1) - 1 const customerGroupIndexes = [groupStartRow, groupEndRow] customerGroups.push(customerGroupIndexes) } if (remainder != 0) { const customerGroupIndexesLast = [groupSize * i, groupSize * i + remainder - 1] customerGroups.push(customerGroupIndexesLast) } Logger.log(customerGroups) // 거래처 그룹의 수만큼 트리거 예약 일정을 배열로 생성 let scheduleGroups = [] for (j = 0; j < customerGroups.length; j++) { const schedule = 1000 scheduleGroups.push(schedule) } Logger.log(scheduleGroups) // 거래처 그룹의 수만큼 트리거를 예약 for (k = 0; k < customerGroups.length; k++) { const trigger = ScriptApp.newTrigger('triggerHF') .timeBased() .after(scheduleGroups[k]) .create() const triggerId = trigger.getUniqueId() let groupInfo = {} groupInfo.customerGroup = customerGroups[k] const cache = CacheService.getDocumentCache() cache.put(triggerId, JSON.stringify(groupInfo), 1800) Logger.log(triggerId) } // 로그를 위한 변수 정의 const scriptEndTime = new Date() const scriptDuration = (scriptEndTime.getTime() - scriptStartTime.getTime()) / 1000 const triggerCounts = customerGroups.length // 로그 배열 정의 const logArr = [logId, scriptName, scriptStartTime, scriptEndTime, scriptDuration, userEmail, invoiceMonth, startDateForName, endDateForName, customersSize, triggerCounts] Logger.log(logArr) }
JavaScript
복사

로그 배열을 appendRow로 입력하는 createTriggersWithLog 함수

function createTriggersWithLog() { // 로그를 위한 변수 정의 const logId = getRandomId() const scriptName = '월정산서 발송 스크립트' const scriptStartTime = new Date() const userEmail = Session.getActiveUser().getEmail() const today = new Date('2023-07-01') const startDate = new Date(today.getFullYear(), today.getMonth() - 1, 1, 00, 00, 00) const endDate = new Date(today.getFullYear(), today.getMonth(), 0, 23, 59, 59) const startDateForName = Utilities.formatDate(startDate, 'GMT+9', 'yyyy-MM-dd') const endDateForName = Utilities.formatDate(endDate, 'GMT+9', 'yyyy-MM-dd') const invoiceMonthDate = new Date(startDate.getFullYear(), startDate.getMonth() + 1, 1) const invoiceMonth = Utilities.formatDate(invoiceMonthDate, 'GMT+9', 'yyyy년 MM월') // 거래처 목록 정의 const SS = SpreadsheetApp.getActiveSpreadsheet() const T_LOG = SS.getSheetByName('실행로그') const T_CUSTOMER = SS.getSheetByName('거래처') const customersSize = T_CUSTOMER.getDataRange().getValues().slice(1, 11).length Logger.log(customersSize) // 거래처를 나눌 그룹 사이즈 정의 const groupSize = 3 const quotient = Math.floor(customersSize / groupSize) const remainder = customersSize % groupSize // 거래처 그룹의 시작과 종료 순번을 배열로 생성 let customerGroups = [] for (i = 0; i < quotient; i++) { const groupStartRow = groupSize * i const groupEndRow = groupSize * (i + 1) - 1 const customerGroupIndexes = [groupStartRow, groupEndRow] customerGroups.push(customerGroupIndexes) } if (remainder != 0) { const customerGroupIndexesLast = [groupSize * i, groupSize * i + remainder - 1] customerGroups.push(customerGroupIndexesLast) } Logger.log(customerGroups) // 거래처 그룹의 수만큼 트리거 예약 일정을 배열로 생성 let scheduleGroups = [] for (j = 0; j < customerGroups.length; j++) { const schedule = 1000 scheduleGroups.push(schedule) } Logger.log(scheduleGroups) // 거래처 그룹의 수만큼 트리거를 예약 for (k = 0; k < customerGroups.length; k++) { const trigger = ScriptApp.newTrigger('triggerHF') .timeBased() .after(scheduleGroups[k]) .create() const triggerId = trigger.getUniqueId() let groupInfo = {} groupInfo.customerGroup = customerGroups[k] const cache = CacheService.getDocumentCache() cache.put(triggerId, JSON.stringify(groupInfo), 1800) Logger.log(triggerId) } // 로그를 위한 변수 정의 const scriptEndTime = new Date() const scriptDuration = (scriptEndTime.getTime() - scriptStartTime.getTime()) / 1000 const triggerCounts = customerGroups.length // 로그 배열 정의 const logArr = [logId, scriptName, scriptStartTime, scriptEndTime, scriptDuration, userEmail, invoiceMonth, startDateForName, endDateForName, customersSize, triggerCounts] Logger.log(logArr) T_LOG.appendRow(logArr) }
JavaScript
복사

[예제 10-03] getLastRowOfCol을 활용한 로그 기록 함수 writeLog

function writeLog(T_LOG, logArr) { const lastRow = getLastRowOfCol(T_LOG, 'A') T_LOG.getRange(lastRow + 1, 1, logArr.length, logArr[0].length).setValues(logArr) }
JavaScript
복사

writeLog를 사용하는 createTriggersWithLog 함수

function createTriggersWithLog() { // 로그를 위한 변수 정의 const logId = getRandomId() const scriptName = '월정산서 발송 스크립트' const scriptStartTime = new Date() const userEmail = Session.getActiveUser().getEmail() const today = new Date('2023-07-01') const startDate = new Date(today.getFullYear(), today.getMonth() - 1, 1, 00, 00, 00) const endDate = new Date(today.getFullYear(), today.getMonth(), 0, 23, 59, 59) const startDateForName = Utilities.formatDate(startDate, 'GMT+9', 'yyyy-MM-dd') const endDateForName = Utilities.formatDate(endDate, 'GMT+9', 'yyyy-MM-dd') const invoiceMonthDate = new Date(startDate.getFullYear(), startDate.getMonth() + 1, 1) const invoiceMonth = Utilities.formatDate(invoiceMonthDate, 'GMT+9', 'yyyy년 MM월') // 거래처 목록 정의 const SS = SpreadsheetApp.getActiveSpreadsheet() const T_LOG = SS.getSheetByName('실행로그') const T_CUSTOMER = SS.getSheetByName('거래처') const customersSize = T_CUSTOMER.getDataRange().getValues().slice(1, 11).length Logger.log(customersSize) // 거래처를 나눌 그룹 사이즈 정의 const groupSize = 3 const quotient = Math.floor(customersSize / groupSize) const remainder = customersSize % groupSize // 거래처 그룹의 시작과 종료 순번을 배열로 생성 let customerGroups = [] for (i = 0; i < quotient; i++) { const groupStartRow = groupSize * i const groupEndRow = groupSize * (i + 1) - 1 const customerGroupIndexes = [groupStartRow, groupEndRow] customerGroups.push(customerGroupIndexes) } if (remainder != 0) { const customerGroupIndexesLast = [groupSize * i, groupSize * i + remainder - 1] customerGroups.push(customerGroupIndexesLast) } Logger.log(customerGroups) // 거래처 그룹의 수만큼 트리거 예약 일정을 배열로 생성 let scheduleGroups = [] for (j = 0; j < customerGroups.length; j++) { const schedule = 1000 scheduleGroups.push(schedule) } Logger.log(scheduleGroups) // 거래처 그룹의 수만큼 트리거를 예약 for (k = 0; k < customerGroups.length; k++) { const trigger = ScriptApp.newTrigger('triggerHF') .timeBased() .after(scheduleGroups[k]) .create() const triggerId = trigger.getUniqueId() let groupInfo = {} groupInfo.customerGroup = customerGroups[k] const cache = CacheService.getDocumentCache() cache.put(triggerId, JSON.stringify(groupInfo), 1800) Logger.log(triggerId) } // 로그를 위한 변수 정의 const scriptEndTime = new Date() const scriptDuration = (scriptEndTime.getTime() - scriptStartTime.getTime()) / 1000 const triggerCounts = customerGroups.length // 로그 배열 정의 const logArr = [[logId, scriptName, scriptStartTime, scriptEndTime, scriptDuration, userEmail, invoiceMonth, startDateForName, endDateForName, customersSize, triggerCounts]] Logger.log(logArr) writeLog(T_LOG, logArr) }
JavaScript
복사

logArrDetail을 기록하기 위한 createTriggersWithLogDetail

function createTriggersWithLogDetail() { // 로그를 위한 변수 정의 const logId = getRandomId() const scriptName = '월정산서 발송 스크립트' const scriptStartTime = new Date() const userEmail = Session.getActiveUser().getEmail() const today = new Date('2023-07-01') const startDate = new Date(today.getFullYear(), today.getMonth() - 1, 1, 00, 00, 00) const endDate = new Date(today.getFullYear(), today.getMonth(), 0, 23, 59, 59) const startDateForName = Utilities.formatDate(startDate, 'GMT+9', 'yyyy-MM-dd') const endDateForName = Utilities.formatDate(endDate, 'GMT+9', 'yyyy-MM-dd') const invoiceMonthDate = new Date(startDate.getFullYear(), startDate.getMonth() + 1, 1) const invoiceMonth = Utilities.formatDate(invoiceMonthDate, 'GMT+9', 'yyyy년 MM월') // 거래처 목록 정의 const SS = SpreadsheetApp.getActiveSpreadsheet() const T_LOG = SS.getSheetByName('실행로그') const T_CUSTOMER = SS.getSheetByName('거래처') const customersSize = T_CUSTOMER.getDataRange().getValues().slice(1, 11).length Logger.log(customersSize) // 거래처를 나눌 그룹 사이즈 정의 const groupSize = 3 const quotient = Math.floor(customersSize / groupSize) const remainder = customersSize % groupSize // 거래처 그룹의 시작과 종료 순번을 배열로 생성 let customerGroups = [] for (i = 0; i < quotient; i++) { const groupStartRow = groupSize * i const groupEndRow = groupSize * (i + 1) - 1 const customerGroupIndexes = [groupStartRow, groupEndRow] customerGroups.push(customerGroupIndexes) } if (remainder != 0) { const customerGroupIndexesLast = [groupSize * i, groupSize * i + remainder - 1] customerGroups.push(customerGroupIndexesLast) } Logger.log(customerGroups) // 거래처 그룹의 수만큼 트리거 예약 일정을 배열로 생성 let scheduleGroups = [] for (j = 0; j < customerGroups.length; j++) { const schedule = 1000 scheduleGroups.push(schedule) } Logger.log(scheduleGroups) // 거래처 그룹의 수만큼 트리거를 예약 for (k = 0; k < customerGroups.length; k++) { const trigger = ScriptApp.newTrigger('triggerHF') .timeBased() .after(scheduleGroups[k]) .create() const triggerId = trigger.getUniqueId() let groupInfo = {} groupInfo.customerGroup = customerGroups[k] groupInfo.logId = logId groupInfo.triggerId = triggerId const cache = CacheService.getDocumentCache() cache.put(triggerId, JSON.stringify(groupInfo), 1800) Logger.log(triggerId) } // 로그를 위한 변수 정의 const scriptEndTime = new Date() const scriptDuration = (scriptEndTime.getTime() - scriptStartTime.getTime()) / 1000 const triggerCounts = customerGroups.length // 로그 배열 정의 const logArr = [[logId, scriptName, scriptStartTime, scriptEndTime, scriptDuration, userEmail, invoiceMonth, startDateForName, endDateForName, customersSize, triggerCounts]] Logger.log(logArr) writeLog(T_LOG, logArr) }
JavaScript
복사

[예제 10-04] 상세로그를 기록하는 createMonthlyInvoiceGroupWithLog 함수

/* [거래처] 탭에 선택된 거래처의 월정산서를 생성하는 함수 최종 업데이트: YYYY년 MM월 DD일 작성자: 오토오피스 */ function createMonthlyInvoiceGroupWithLog(groupInfo) { // 로그를 위한 변수 정의 const logId = groupInfo.logId const triggerId = groupInfo.triggerId // 탭, 템플릿과 결과물 저장 폴더 변수 선언 및 할당 const SS = SpreadsheetApp.getActiveSpreadsheet() const T_SALESDATA = SS.getSheetByName('판매데이터') const T_CUSTOMER = SS.getSheetByName('거래처') const T_LOGDETAIL = SS.getSheetByName('상세로그') const resultFolderId = '폴더 ID' const resultFolder = DriveApp.getFolderById(resultFolderId) const templateFile = DriveApp.getFileById('파일 ID') // 판매데이터, 대상 거래처 변수 선언 및 할당 const groupStartRow = groupInfo.customerGroup[0] const groupEndRow = groupInfo.customerGroup[1] const salesData = T_SALESDATA.getDataRange().getValues() const customers = T_CUSTOMER.getDataRange().getValues().slice(1).slice(groupStartRow, groupEndRow + 1) Logger.log(customers) // 판매데이터 필터링 기간 및 템플릿 복사본 정의 시작 const today = new Date('2023-07-01') const startDate = new Date(today.getFullYear(), today.getMonth() - 1, 1, 00, 00, 00) const endDate = new Date(today.getFullYear(), today.getMonth(), 0, 23, 59, 59) const startDateForName = Utilities.formatDate(startDate, 'GMT+9', 'yyyy-MM-dd') const endDateForName = Utilities.formatDate(endDate, 'GMT+9', 'yyyy-MM-dd') const invoiceMonthDate = new Date(startDate.getFullYear(), startDate.getMonth() + 1, 1) const invoiceMonth = Utilities.formatDate(invoiceMonthDate, 'GMT+9', 'yyyy년 MM월') const templateCopyName = `월정산서_${startDateForName}_${endDateForName}` const templateCopyId = templateFile.makeCopy().moveTo(resultFolder).setName(templateCopyName).getId() const templateCopy = SpreadsheetApp.openById(templateCopyId) // 판매데이터 필터링 기간 및 템플릿 복사본 정의 종료 let logArrDetail = [] // 거래처별 정산서탭 생성 및 데이터 입력 for (i = 0; i < customers.length; i++) { const customerStartTime = new Date() const logDetailId = getRandomId() const customerOrder = groupStartRow + i + 1 // 거래처 정보 정의 const customer = { customerName: customers[i][1], ceoName: customers[i][4], contactNo: customers[i][5], email: customers[i][6] } Logger.log(`${customer.customerName}의 작업을 시작합니다.`) try { // salesData를 거래처명과 판매일시로 필터링하고, 정산서의 양식에 맞춰 열을 재배치 const salesDataFiltered = salesData.filter(row => row[1] == customer.customerName && row[0] >= startDate && row[0] <= endDate) const salesDataResult = salesDataFiltered.map((item, index) => [index + 1, item[0], item[4], item[5], item[6], item[7], item[8], item[9], item[10], item[11]]) // 탭이름을 정의하고, 템플릿 탭을 복사해서 탭이름으로 설정 const newTabName = `${customer.customerName}_${startDateForName}_${endDateForName}` const newTab = templateCopy.getSheetByName('템플릿').copyTo(templateCopy).setName(newTabName) // 새로운 탭에 해당 거래처의 판매데이터 입력 newTab.getRange(17, 2, salesDataResult.length, salesDataResult[0].length).setValues(salesDataResult) // 정산서에 거래처 정보 및 정산 금액 입력 시작 const salesDataSum = salesDataResult.map(item => item[9]).reduce((acc, cur) => acc + cur, 0) const cells = ['C8', 'C9', 'C10', 'E10', 'H13', 'K13', 'B13', 'H9'] const customerValues = [customer.customerName, customer.ceoName, customer.contactNo, customer.email, startDateForName, endDateForName, new Date(), salesDataSum] for (j = 0; j < cells.length; j++) { newTab.getRange(cells[j]).setValue(customerValues[j]) } // 정산서에 거래처 정보 및 정산 금액 입력 종료 // 페이지 행 조절 adjustTabRowsByPage(salesDataResult, newTab) // 월정산서 탭 드라이브 저장 const tabId = newTab.getSheetId() const fileId = pdfSaveTabReturnFileID(templateCopyId, resultFolder, newTabName, tabId) // 월정산서 PDF 이메일 발송 sendEmailParam(fileId, customer, startDateForName, endDateForName, invoiceMonth) // 로그 종료를 위한 변수 정의 const customerEndTime = new Date() const customerDuration = (customerEndTime.getTime() - customerStartTime.getTime()) / 1000 const downloadPdfUrl = DriveApp.getFileById(fileId).getUrl() const successYN = '성공' const emailRemaining = MailApp.getRemainingDailyQuota() const logArrDetailCustomer = [logId, logDetailId, triggerId, customerOrder, customer.customerName, customer.email, customerStartTime, customerEndTime, customerDuration, downloadPdfUrl, successYN, emailRemaining] logArrDetail.push(logArrDetailCustomer) } catch (e) { Logger.log(`오류가 발생했습니다: ${e}`) // 로그 종료를 위한 변수 정의 const customerEndTime = new Date() const customerDuration = (customerEndTime.getTime() - customerStartTime.getTime()) / 1000 const successYN = '실패' const logArrDetailCustomer = [logId, logDetailId, triggerId, customerOrder, customer.customerName, customer.email, customerStartTime, customerEndTime, customerDuration, null, successYN, null] logArrDetail.push(logArrDetailCustomer) } } Logger.log(templateCopy.getUrl()) writeLog(T_LOGDETAIL,logArrDetail) }
JavaScript
복사

상세로그를 기록하는 함수를 호출하는 triggerHF

function triggerHF(e) { Logger.log(e) const triggerId = e.triggerUid const cache = CacheService.getDocumentCache() const groupInfo = JSON.parse(cache.get(triggerId)) Logger.log(groupInfo) createMonthlyInvoiceGroupWithLog(groupInfo) }
JavaScript
복사