ทำให้อีเมลใช้งานได้จริงยิ่งขึ้นด้วยส่วนเสริมของ Google Workspace

1. ภาพรวม

ใน Codelab นี้ คุณจะใช้ Google Apps Script เพื่อเขียนส่วนเสริม Google Workspace สำหรับ Gmail ซึ่งช่วยให้ผู้ใช้เพิ่มข้อมูลใบเสร็จจากอีเมลไปยังสเปรดชีตภายใน Gmail ได้โดยตรง เมื่อได้รับใบเสร็จทางอีเมล ผู้ใช้จะเปิดส่วนเสริมซึ่งจะได้รับข้อมูลค่าใช้จ่ายที่เกี่ยวข้องจากอีเมลโดยอัตโนมัติ ผู้ใช้จะแก้ไขข้อมูลค่าใช้จ่ายแล้วส่งข้อมูลนั้นเพื่อบันทึกค่าใช้จ่ายลงในสเปรดชีต Google ชีตได้

สิ่งที่คุณจะได้เรียนรู้

  • สร้างส่วนเสริม Google Workspace สำหรับ Gmail โดยใช้ Google Apps Script
  • แยกวิเคราะห์อีเมลด้วย Google Apps Script
  • โต้ตอบกับ Google ชีตผ่าน Google Apps Script
  • จัดเก็บค่าของผู้ใช้โดยใช้บริการคุณสมบัติของ Google Apps Script

สิ่งที่ต้องมี

  • การเข้าถึงอินเทอร์เน็ตและเว็บเบราว์เซอร์
  • บัญชี Google
  • ข้อความบางส่วนใน Gmail (ควรเป็นใบตอบรับอีเมล)

2. รับโค้ดตัวอย่าง

ขณะทำงานใน Codelab นี้ เราขอแนะนำให้คุณอ้างอิงเวอร์ชันที่ใช้งานได้ของโค้ดที่คุณจะเขียนด้วย ที่เก็บ GitHub มีโค้ดตัวอย่างที่คุณใช้เป็นข้อมูลอ้างอิงได้

หากต้องการรับโค้ดตัวอย่าง ให้เรียกใช้คำสั่งต่อไปนี้จากบรรทัดคำสั่ง

git clone https://github.com/googleworkspace/gmail-add-on-codelab.git

3. สร้างส่วนเสริมพื้นฐาน

เริ่มด้วยการเขียนโค้ดสำหรับส่วนเสริมเวอร์ชันง่ายๆ ที่แสดงแบบฟอร์มค่าใช้จ่ายควบคู่ไปกับอีเมล

ก่อนอื่น ให้สร้างโปรเจ็กต์ Apps Script ใหม่ แล้วเปิดไฟล์ Manifest

  1. ไปที่ script.google.com คุณจะสร้าง จัดการ และตรวจสอบโปรเจ็กต์ Apps Script ได้จากที่นี่
  2. หากต้องการสร้างโปรเจ็กต์ใหม่ ให้คลิกโปรเจ็กต์ใหม่ที่ด้านซ้ายบน โปรเจ็กต์ใหม่จะเปิดขึ้นด้วยไฟล์เริ่มต้นชื่อ Code.gs อย่าเพิ่งไปไหนกับ Code.gs เพราะคุณจะทำงานทีหลัง
  3. คลิกโปรเจ็กต์ที่ไม่มีชื่อ ตั้งชื่อโปรเจ็กต์ของคุณว่า Expense It! แล้วคลิกเปลี่ยนชื่อ
  4. คลิกการตั้งค่าโปรเจ็กต์ การตั้งค่าโปรเจ็กต์ ทางด้านซ้าย
  5. เลือกช่องทำเครื่องหมาย Show "appscript.json" "ไฟล์ Manifest ในตัวแก้ไข"
  6. คลิกตัดต่อวิดีโอ ผู้แก้ไข
  7. หากต้องการเปิดไฟล์ Manifest ให้คลิก appscript.json ทางด้านซ้าย

ใน appscript.json ให้ระบุข้อมูลเมตาที่เชื่อมโยงกับส่วนเสริม เช่น ชื่อส่วนเสริมและสิทธิ์ที่ต้องใช้ แทนที่เนื้อหาของ appsscript.json ด้วยการตั้งค่าการกำหนดค่าเหล่านี้:

{
  "timeZone": "GMT",
  "oauthScopes": [
    "https://www.googleapis.com/auth/gmail.addons.execute"
  ],
  "gmail": {
    "name": "Expense It!",
    "logoUrl": "https://www.gstatic.com/images/icons/material/system/1x/receipt_black_24dp.png",
    "contextualTriggers": [{
      "unconditional": {
      },
      "onTriggerFunction": "getContextualAddOn"
    }],
    "primaryColor": "#41f470",
    "secondaryColor": "#94f441"
  }
}

ให้ความสนใจเป็นพิเศษกับส่วนของไฟล์ Manifest ที่ชื่อ contextualTriggers ส่วนนี้ของไฟล์ Manifest จะระบุฟังก์ชันที่ผู้ใช้กำหนดที่จะเรียกใช้เมื่อเปิดใช้งานส่วนเสริมเป็นครั้งแรก ในกรณีนี้ ระบบจะโทรหา getContextualAddOn ซึ่งจะรับรายละเอียดเกี่ยวกับอีเมลที่เปิดอยู่และแสดงผลชุดการ์ดเพื่อแสดงต่อผู้ใช้

หากต้องการสร้างฟังก์ชัน getContextualAddOn ให้ทำตามขั้นตอนต่อไปนี้

  1. ทางด้านซ้าย ให้วางเมาส์ไว้เหนือ Code.gs แล้วคลิกเมนู เมนูเพิ่มเติม > เปลี่ยนชื่อ
  2. พิมพ์ GetContextualAddOn และกดแป้น Enter Apps Script จะเพิ่ม .gs ต่อท้ายชื่อไฟล์โดยอัตโนมัติ คุณจึงไม่ต้องพิมพ์นามสกุลไฟล์ หากคุณพิมพ์ GetContextualAddOn.gs แล้ว Apps Script จะตั้งชื่อไฟล์ของคุณว่า GetContextualAddOn.gs.gs
  3. ใน GetContextualAddOn.gs ให้แทนที่รหัสเริ่มต้นด้วยฟังก์ชัน getContextualAddOn:
/**
 * Returns the contextual add-on data that should be rendered for
 * the current e-mail thread. This function satisfies the requirements of
 * an 'onTriggerFunction' and is specified in the add-on's manifest.
 *
 * @param {Object} event Event containing the message ID and other context.
 * @returns {Card[]}
 */
function getContextualAddOn(event) {
  var card = CardService.newCardBuilder();
  card.setHeader(CardService.newCardHeader().setTitle('Log Your Expense'));

  var section = CardService.newCardSection();
  section.addWidget(CardService.newTextInput()
    .setFieldName('Date')
    .setTitle('Date'));
  section.addWidget(CardService.newTextInput()
    .setFieldName('Amount')
    .setTitle('Amount'));
  section.addWidget(CardService.newTextInput()
    .setFieldName('Description')
    .setTitle('Description'));
  section.addWidget(CardService.newTextInput()
    .setFieldName('Spreadsheet URL')
    .setTitle('Spreadsheet URL'));

  card.addSection(section);

  return [card.build()];
}

อินเทอร์เฟซผู้ใช้ของส่วนเสริมทุกรายการของ Google Workspace ประกอบด้วยการ์ดที่แบ่งออกเป็น 1 ส่วนขึ้นไป โดยแต่ละส่วนจะมีวิดเจ็ตที่แสดงและรับข้อมูลจากผู้ใช้ได้ ฟังก์ชัน getContextualAddOn จะสร้างการ์ดใบเดียวที่มีรายละเอียดเกี่ยวกับค่าใช้จ่ายที่พบในอีเมล การ์ดมี 1 ส่วนที่มีช่องป้อนข้อความสำหรับข้อมูลที่เกี่ยวข้อง ฟังก์ชันจะแสดงผลอาร์เรย์ของการ์ดของส่วนเสริม ในกรณีนี้ อาร์เรย์ที่แสดงผลจะมีการ์ดเพียง 1 รายการ

ก่อนใช้งาน Expense It! ส่วนเสริม คุณต้องมีโปรเจ็กต์ Google Cloud Platform (GCP) ซึ่งโปรเจ็กต์ Apps Script ใช้เพื่อจัดการการให้สิทธิ์ บริการขั้นสูง และรายละเอียดอื่นๆ โปรดดูข้อมูลเพิ่มเติมที่หัวข้อโปรเจ็กต์ Google Cloud Platform

ทำตามขั้นตอนต่อไปนี้เพื่อเรียกใช้และเรียกใช้ส่วนเสริม

  1. เปิดโปรเจ็กต์ GCP แล้วคัดลอกหมายเลขโปรเจ็กต์
  2. จากโปรเจ็กต์ Apps Script ให้คลิกการตั้งค่าโปรเจ็กต์ การตั้งค่าโปรเจ็กต์ ทางด้านซ้าย
  3. ในส่วน "โปรเจ็กต์ Google Cloud Platform (GCP)" ให้คลิกเปลี่ยนโปรเจ็กต์
  4. ป้อนหมายเลขโปรเจ็กต์ GCP แล้วคลิกตั้งค่าโปรเจ็กต์
  5. คลิกทำให้ใช้งานได้ > ทดสอบการทำให้ใช้งานได้
  6. ตรวจสอบว่าประเภทการทำให้ใช้งานได้คือส่วนเสริมของ Google Workspace หากจำเป็น ให้คลิกเปิดใช้ประเภทการทำให้ใช้งานได้ เปิดใช้ประเภทการทำให้ใช้งานได้ ที่ด้านบนของกล่องโต้ตอบ แล้วเลือกส่วนเสริม Google Workspace เป็นประเภทการทำให้ใช้งานได้
  7. ถัดจากแอปพลิเคชัน: Gmail ให้คลิกติดตั้ง
  8. คลิกเสร็จสิ้น

ตอนนี้คุณจะดูส่วนเสริมในกล่องจดหมาย Gmail ได้แล้ว

  1. เปิด Gmail ในคอมพิวเตอร์
  2. ในแผงด้านขวา ให้เลือก "ค่าใช้จ่าย!" ส่วนเสริม ค่าใช้จ่าย ไอคอนใบเสร็จ จะปรากฏขึ้น คุณอาจต้องคลิกส่วนเสริมเพิ่มเติม ส่วนเสริมอื่นๆ เพื่อค้นหา
  3. เปิดอีเมล (ควรเป็นใบเสร็จที่ระบุค่าใช้จ่าย)
  4. หากต้องการเปิดส่วนเสริม ให้คลิกค่าใช้จ่าย! ค่าใช้จ่าย ไอคอนใบเสร็จ
  5. ให้ค่าใช้จ่าย ให้เข้าถึงบัญชี Google ของคุณโดยคลิกให้สิทธิ์เข้าถึงและทำตามข้อความแจ้ง

ส่วนเสริมจะแสดงแบบฟอร์มง่ายๆ ควบคู่ไปกับข้อความ Gmail ที่เปิดอยู่ ยังไม่ได้ทำอะไรเลย แต่คุณจะสร้างฟังก์ชันการทำงานในส่วนถัดไป

หากต้องการดูการอัปเดตส่วนเสริมขณะดำเนินการในห้องทดลองนี้ คุณเพียงแค่บันทึกรหัสและรีเฟรช Gmail โดยไม่จำเป็นต้องติดตั้งใช้งานเพิ่มเติม

4. เข้าถึงข้อความอีเมล

เพิ่มโค้ดที่ดึงเนื้อหาอีเมลและปรับโค้ดเป็นโมดูลเพื่อให้เป็นระเบียบมากขึ้น

คลิกเพิ่ม เพิ่มไฟล์ ข้าง Files > เขียนสคริปต์และสร้างไฟล์ชื่อ Cards สร้างไฟล์สคริปต์ไฟล์ที่ 2 ชื่อ Helpers Cards.gs จะสร้างการ์ดและใช้ฟังก์ชันจาก Helpers.gs เพื่อเติมข้อมูลในช่องในแบบฟอร์มโดยอิงตามเนื้อหาของอีเมล

แทนที่รหัสเริ่มต้นใน Cards.gs ด้วยรหัสนี้:

var FIELDNAMES = ['Date', 'Amount', 'Description', 'Spreadsheet URL'];

/**
 * Creates the main card users see with form inputs to log expenses.
 * Form can be prefilled with values.
 *
 * @param {String[]} opt_prefills Default values for each input field.
 * @param {String} opt_status Optional status displayed at top of card.
 * @returns {Card}
 */
function createExpensesCard(opt_prefills, opt_status) {
  var card = CardService.newCardBuilder();
  card.setHeader(CardService.newCardHeader().setTitle('Log Your Expense'));
  
  if (opt_status) {
    if (opt_status.indexOf('Error: ') == 0) {
      opt_status = '<font color=\'#FF0000\'>' + opt_status + '</font>';
    } else {
      opt_status = '<font color=\'#228B22\'>' + opt_status + '</font>';
    }
    var statusSection = CardService.newCardSection();
    statusSection.addWidget(CardService.newTextParagraph()
      .setText('<b>' + opt_status + '</b>'));
    card.addSection(statusSection);
  }
  
  var formSection = createFormSection(CardService.newCardSection(),
                                      FIELDNAMES, opt_prefills);
  card.addSection(formSection);
  
  return card;
}

/**
 * Creates form section to be displayed on card.
 *
 * @param {CardSection} section The card section to which form items are added.
 * @param {String[]} inputNames Names of titles for each input field.
 * @param {String[]} opt_prefills Default values for each input field.
 * @returns {CardSection}
 */
function createFormSection(section, inputNames, opt_prefills) {
  for (var i = 0; i < inputNames.length; i++) {
    var widget = CardService.newTextInput()
      .setFieldName(inputNames[i])
      .setTitle(inputNames[i]);
    if (opt_prefills && opt_prefills[i]) {
      widget.setValue(opt_prefills[i]);
    }
    section.addWidget(widget);
  }
  return section;
}

ฟังก์ชัน createExpensesCard ใช้อาร์เรย์ของค่าเพื่อป้อนข้อมูลในแบบฟอร์มล่วงหน้าเป็นอาร์กิวเมนต์ที่ไม่บังคับ ฟังก์ชันดังกล่าวสามารถแสดงข้อความสถานะที่ไม่บังคับ ซึ่งจะเป็นสีแดงหากสถานะเริ่มต้นด้วย "ข้อผิดพลาด:" หรือเป็นสีเขียว แทนที่จะเพิ่มแต่ละช่องในแบบฟอร์มด้วยตนเอง ฟังก์ชันตัวช่วยที่เรียกว่า createFormSection จะวนซ้ำกระบวนการสร้างวิดเจ็ตการป้อนข้อความ ตั้งค่าเริ่มต้นแต่ละรายการด้วย setValue แล้วเพิ่มวิดเจ็ตดังกล่าวลงในส่วนที่เกี่ยวข้องบนการ์ด

ตอนนี้ แทนที่รหัสเริ่มต้นใน Helpers.gs ด้วยโค้ดนี้:

/**
 * Finds largest dollar amount from email body.
 * Returns null if no dollar amount is found.
 *
 * @param {Message} message An email message.
 * @returns {String}
 */
function getLargestAmount(message) {
  return 'TODO';
}

/**
 * Determines date the email was received.
 *
 * @param {Message} message An email message.
 * @returns {String}
 */
function getReceivedDate(message) {
  return 'TODO';
}

/**
 * Determines expense description by joining sender name and message subject.
 *
 * @param {Message} message An email message.
 * @returns {String}
 */
function getExpenseDescription(message) {
  return 'TODO';
}

/**
 * Determines most recent spreadsheet URL.
 * Returns null if no URL was previously submitted.
 *
 * @returns {String}
 */
function getSheetUrl() {
  return 'TODO';
}

getContextualAddon จะเรียกฟังก์ชันใน Helpers.gs เพื่อระบุค่าที่กรอกไว้ล่วงหน้าในแบบฟอร์ม ในตอนนี้ ฟังก์ชันเหล่านี้จะแสดงผลสตริง "TODO" เท่านั้น เนื่องจากคุณจะนำตรรกะที่กรอกไว้ล่วงหน้าไปใช้ในขั้นตอนถัดไป

ต่อไป ให้อัปเดตโค้ดใน GetContextualAddon.gs เพื่อให้ใช้ประโยชน์จากโค้ดใน Cards.gs และ Helpers.gs แทนที่รหัสใน GetContextualAddon.gs ด้วยโค้ดนี้:

/**
 * Copyright 2017 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * Returns the contextual add-on data that should be rendered for
 * the current e-mail thread. This function satisfies the requirements of
 * an 'onTriggerFunction' and is specified in the add-on's manifest.
 *
 * @param {Object} event Event containing the message ID and other context.
 * @returns {Card[]}
 */
function getContextualAddOn(event) {
  var message = getCurrentMessage(event);
  var prefills = [getReceivedDate(message),
                  getLargestAmount(message),
                  getExpenseDescription(message),
                  getSheetUrl()];
  var card = createExpensesCard(prefills);

  return [card.build()];
}

/**
 * Retrieves the current message given an action event object.
 * @param {Event} event Action event object
 * @return {Message}
 */
function getCurrentMessage(event) {
  var accessToken = event.messageMetadata.accessToken;
  var messageId = event.messageMetadata.messageId;
  GmailApp.setCurrentMessageAccessToken(accessToken);
  return GmailApp.getMessageById(messageId);
}

สังเกตฟังก์ชัน getCurrentMessage ใหม่ ซึ่งจะใช้เหตุการณ์ที่ Gmail จัดเตรียมไว้เพื่ออ่านข้อความที่เปิดอยู่ของผู้ใช้ในปัจจุบัน เพื่อให้ฟังก์ชันนี้ทำงานได้ ให้เพิ่มขอบเขตเพิ่มเติมในไฟล์ Manifest ของสคริปต์ที่อนุญาตการเข้าถึงข้อความ Gmail แบบอ่านอย่างเดียว

ใน appscript.json ให้อัปเดต oauthScopes เพื่อขอขอบเขต https://www.googleapis.com/auth/gmail.addons.current.message.readonly ด้วย

"oauthScopes": [
  "https://www.googleapis.com/auth/gmail.addons.execute",
   "https://www.googleapis.com/auth/gmail.addons.current.message.readonly"
],

เรียกใช้ส่วนเสริมใน Gmail และให้สิทธิ์เข้าถึงสำหรับ Expense It! เพื่อดูข้อความอีเมล ตอนนี้ช่องของแบบฟอร์มมีการระบุด้วย "TODO" ไว้ล่วงหน้าแล้ว

5. โต้ตอบกับ Google ชีต

ค่าใช้จ่ายก็คุ้มค่า ส่วนเสริมมีแบบฟอร์มให้ผู้ใช้ป้อนรายละเอียดเกี่ยวกับค่าใช้จ่าย แต่รายละเอียดเหล่านั้นไม่จำเป็นต้องอยู่ที่นั่น มาเพิ่มปุ่มสำหรับส่งข้อมูลแบบฟอร์มไปยัง Google ชีตกัน

ในการเพิ่มปุ่ม เราจะใช้คลาส ButtonSet ในการเชื่อมต่อกับ Google ชีต เราจะใช้บริการ Google ชีต

แก้ไข createFormSection เพื่อแสดงปุ่ม "ส่ง" ในฐานะส่วนหนึ่งของส่วนแบบฟอร์มของการ์ด โปรดทำตามขั้นตอนต่อไปนี้

  1. สร้างปุ่มข้อความโดยใช้ CardService.newTextButton() โดยติดป้ายกำกับปุ่ม "ส่ง" ด้วย CardService.TextButton.setText()
  2. ออกแบบปุ่มให้ได้รับการเรียกใช้การดำเนินการ submitForm ต่อไปนี้ผ่าน CardService.TextButton.setOnClickAction() เมื่อคลิก
/**
 * Logs form inputs into a spreadsheet given by URL from form.
 * Then displays edit card.
 *
 * @param {Event} e An event object containing form inputs and parameters.
 * @returns {Card}
 */
function submitForm(e) {
  var res = e['formInput'];
  try {
    FIELDNAMES.forEach(function(fieldName) {
      if (! res[fieldName]) {
        throw 'incomplete form';
      }
    });
    var sheet = SpreadsheetApp
      .openByUrl((res['Spreadsheet URL']))
      .getActiveSheet();
    sheet.appendRow(objToArray(res, FIELDNAMES.slice(0, FIELDNAMES.length - 1)));
    return createExpensesCard(null, 'Logged expense successfully!').build();
  }
  catch (err) {
    if (err == 'Exception: Invalid argument: url') {
      err = 'Invalid URL';
      res['Spreadsheet URL'] = null;
    }
    return createExpensesCard(objToArray(res, FIELDNAMES), 'Error: ' + err).build();
  }
}

/**
 * Returns an array corresponding to the given object and desired ordering of keys.
 *
 * @param {Object} obj Object whose values will be returned as an array.
 * @param {String[]} keys An array of key names in the desired order.
 * @returns {Object[]}
 */
function objToArray(obj, keys) {
  return keys.map(function(key) {
    return obj[key];
  });
}
  1. สร้างวิดเจ็ตชุดปุ่มโดยใช้ CardService.newButtonSet() และเพิ่มข้อความลงในชุดปุ่มที่มี CardService.ButtonSet.addButton()
  2. เพิ่มวิดเจ็ตชุดปุ่มลงในส่วนแบบฟอร์มของการ์ดโดยใช้ CardService.CardSection.addWidget()

เพียงเขียนโค้ดเพียงไม่กี่บรรทัด เราก็เปิดสเปรดชีตด้วย URL แล้วเพิ่มแถวข้อมูลต่อท้ายชีตนั้นได้ โปรดทราบว่าระบบจะส่งผ่านอินพุตฟอร์มไปยังฟังก์ชันซึ่งเป็นส่วนหนึ่งของเหตุการณ์ e และเราจะตรวจสอบว่าผู้ใช้ได้ระบุช่องทั้งหมดแล้ว หากไม่มีข้อผิดพลาดเกิดขึ้น เราจะสร้างการ์ดค่าใช้จ่ายเปล่าซึ่งมีสถานะที่น่าพอใจ ในกรณีที่ตรวจพบข้อผิดพลาด เราจะคืนบัตรเดิมที่กรอกไว้พร้อมกับข้อความแสดงข้อผิดพลาด ฟังก์ชันตัวช่วยของ objToArray ช่วยให้แปลงคำตอบในแบบฟอร์มเป็นอาร์เรย์ได้ง่ายขึ้น ซึ่งจะนำไปต่อท้ายสเปรดชีตได้

สุดท้าย ให้อัปเดตส่วน oauthScopes ใน appsscript.json ขอขอบเขต https://www.googleapis.com/auth/spreadsheets อีกครั้ง เมื่อให้สิทธิ์ขอบเขตนี้จะทำให้ส่วนเสริมอ่านและแก้ไข Google ชีตของผู้ใช้ได้

"oauthScopes": [
  "https://www.googleapis.com/auth/gmail.addons.execute",
  "https://www.googleapis.com/auth/gmail.addons.current.message.readonly",
  "https://www.googleapis.com/auth/spreadsheets"
],

หากยังไม่ได้สร้างสเปรดชีตใหม่ คุณสามารถสร้างสเปรดชีตได้ที่ https://docs.google.com/spreadsheets/

เรียกใช้ส่วนเสริมอีกครั้งและลองส่งแบบฟอร์ม ตรวจสอบว่าคุณป้อน URL แบบเต็มของ URL ปลายทางลงในช่อง URL ของสเปรดชีต

6. จัดเก็บค่ากับบริการพร็อพเพอร์ตี้

บ่อยครั้งที่ผู้ใช้จะบันทึกค่าใช้จ่ายจำนวนมากไว้ในสเปรดชีตเดียวกัน ดังนั้นจึงสะดวกที่จะเสนอ URL ของสเปรดชีตล่าสุดเป็นค่าเริ่มต้นในการ์ด เราต้องเก็บข้อมูลนั้นไว้ทุกครั้งที่มีการใช้ส่วนเสริม จึงจะทราบ URL ล่าสุดของสเปรดชีตได้

บริการพร็อพเพอร์ตี้ช่วยให้เราจัดเก็บคู่คีย์-ค่าได้ ในกรณีของเรา คีย์ที่สมเหตุสมผลคือ "SPREADSHEET_URL" ในขณะที่ค่าจะเป็น URL เอง ในการจัดเก็บค่าดังกล่าว คุณจะต้องแก้ไข submitForm ใน Cards.gs ให้จัดเก็บ URL ของสเปรดชีตเป็นพร็อพเพอร์ตี้เมื่อนำแถวใหม่มาต่อท้ายชีต

โปรดทราบว่าพร็อพเพอร์ตี้อาจมีขอบเขต 1 จาก 3 ขอบเขต ได้แก่ สคริปต์ ผู้ใช้ หรือเอกสาร ขอบเขตเอกสารจะไม่มีผลกับส่วนเสริม Gmail แม้ว่าจะเกี่ยวข้องกับส่วนเสริมประเภทหนึ่งเมื่อจัดเก็บข้อมูลที่เจาะจงสำหรับ Google เอกสารหรือชีตบางรายการ สำหรับส่วนเสริมของเรา ลักษณะการทำงานที่ต้องการคือให้ผู้ใช้แต่ละคนเห็นสเปรดชีตล่าสุดของตนเอง (แทนที่จะเป็นของคนอื่น) เป็นตัวเลือกเริ่มต้นในแบบฟอร์ม เราจึงเลือกขอบเขตผู้ใช้แทนขอบเขตสคริปต์

ใช้ PropertiesService.getUserProperties().setProperty() เพื่อจัดเก็บ URL ของสเปรดชีต เพิ่มข้อมูลต่อไปนี้ลงใน submitForm ใน Cards.gs:

PropertiesService.getUserProperties().setProperty('SPREADSHEET_URL', 
    res['Spreadsheet URL']);

จากนั้นแก้ไขฟังก์ชัน getSheetUrl ใน Helpers.gs เพื่อแสดงพร็อพเพอร์ตี้ที่จัดเก็บไว้เพื่อให้ผู้ใช้เห็น URL ล่าสุดทุกครั้งที่ใช้ส่วนเสริม ใช้ PropertiesService.getUserProperties().getProperty() เพื่อหาค่าของพร็อพเพอร์ตี้

/**
 * Determines most recent spreadsheet URL.
 * Returns null if no URL was previously submitted.
 *
 * @returns {String}
 */
function getSheetUrl() {
  return PropertiesService.getUserProperties().getProperty('SPREADSHEET_URL');
}

ประการสุดท้าย เพื่อเข้าถึงบริการพร็อพเพอร์ตี้ สคริปต์จะต้องได้รับอนุญาตด้วย เพิ่มขอบเขต https://www.googleapis.com/auth/script.storage ลงในไฟล์ Manifest ตามเดิมเพื่ออนุญาตให้ส่วนเสริมอ่านและเขียนข้อมูลพร็อพเพอร์ตี้ได้

7. แยกวิเคราะห์ข้อความ Gmail

เพื่อบันทึกผู้ใช้ ให้กรอกข้อมูลในแบบฟอร์มล่วงหน้าเกี่ยวกับค่าใช้จ่ายจากอีเมล เราได้สร้างฟังก์ชันใน Helpers.gs ที่มีบทบาทนี้แล้ว แต่จนถึงตอนนี้เราได้แสดงผลเฉพาะ "TODO" สำหรับวันที่ จำนวนเงิน และคำอธิบายค่าใช้จ่าย

ตัวอย่างเช่น เราสามารถดูวันที่ที่ได้รับอีเมลและใช้ค่าเริ่มต้นนั้นเป็นวันที่เริ่มบันทึกได้

/**
 * Determines date the email was received.
 *
 * @param {Message} message - The message currently open.
 * @returns {String}
 */
function getReceivedDate(message) {
  return message.getDate().toLocaleDateString();
}

ใช้ฟังก์ชัน 2 อย่างที่เหลือ ดังนี้

  1. getExpenseDescription อาจเชื่อมโยงทั้งชื่อและเรื่องของข้อความของผู้ส่ง แม้ว่าจะมีวิธีการที่ซับซ้อนกว่าในการแยกวิเคราะห์เนื้อหาข้อความและแสดงรายละเอียดที่แม่นยำยิ่งขึ้นก็ตาม
  2. สำหรับ getLargestAmount ให้ลองมองหาสัญลักษณ์เฉพาะที่เกี่ยวข้องกับเงิน ใบเสร็จมักจะมีค่าหลายค่า เช่น ภาษีและค่าธรรมเนียมอื่นๆ ลองคิดดูว่าคุณจะระบุจำนวนเงินที่ถูกต้องได้อย่างไร คุณอาจใช้นิพจน์ทั่วไปได้

หากต้องการแรงบันดาลใจเพิ่มเติม โปรดดูเอกสารอ้างอิงสำหรับ GmailMessage หรือดูโค้ดโซลูชันที่คุณดาวน์โหลดไว้ตอนต้นของ Codelab เมื่อออกแบบการใช้งานสำหรับฟังก์ชันทั้งหมดใน Helpers.gs แล้ว ก็ลองใช้ส่วนเสริมได้เลย เปิดใบเสร็จและเริ่มบันทึกลงในสเปรดชีต

8. ล้างแบบฟอร์มด้วยการทำงานของการ์ด

จะเกิดอะไรขึ้นหากรายจ่าย หากระบุค่าใช้จ่ายในอีเมลที่เปิดอยู่ผิด แล้วระบบกรอกข้อมูลที่ไม่ถูกต้องในแบบฟอร์ม ผู้ใช้ล้างแบบฟอร์ม คลาส CardAction ช่วยให้เราระบุฟังก์ชันที่จะเรียกใช้เมื่อมีการคลิกการดำเนินการ มาลองใช้ฟีเจอร์นี้เพื่อให้ผู้ใช้ล้างแบบฟอร์มได้อย่างรวดเร็วกัน

แก้ไข createExpensesCard ให้การ์ดที่แสดงผลมีการทำงานของการ์ดที่มีป้ายกำกับ "ล้างแบบฟอร์ม" และเมื่อคลิกจะเรียกฟังก์ชัน clearForm ต่อไปนี้ ซึ่งคุณสามารถวางลงใน Cards.gs คุณจะต้องส่งใน opt_status เป็นพารามิเตอร์ชื่อ "สถานะ" การดำเนินการเพื่อให้มั่นใจว่าเมื่อล้างฟอร์มแล้ว ข้อความสถานะจะยังอยู่ โปรดทราบว่าพารามิเตอร์ที่ไม่บังคับสำหรับการดำเนินการต้องเป็นประเภทออบเจ็กต์<string, string> ดังนั้นหาก opt_status ไม่พร้อมใช้งาน คุณควรส่ง {'Status' : ''}

/**
 * Recreates the main card without prefilled data.
 *
 * @param {Event} e An event object containing form inputs and parameters.
 * @returns {Card}
 */
function clearForm(e) {
  return createExpensesCard(null, e['parameters']['Status']).build();
}

9. สร้างสเปรดชีต

นอกเหนือจากการใช้ Google Apps Script เพื่อแก้ไขสเปรดชีตที่มีอยู่แล้ว คุณยังสามารถสร้างสเปรดชีตใหม่ทั้งหมดด้วยการเขียนโปรแกรมได้ สำหรับส่วนเสริม เราจะอนุญาตให้ผู้ใช้สร้างสเปรดชีตสำหรับค่าใช้จ่าย หากต้องการเริ่มต้นใช้งาน ให้เพิ่มส่วนการ์ดต่อไปนี้ลงในการ์ดที่ createExpensesCard แสดงผล

var newSheetSection = CardService.newCardSection();
var sheetName = CardService.newTextInput()
  .setFieldName('Sheet Name')
  .setTitle('Sheet Name');
var createExpensesSheet = CardService.newAction()
  .setFunctionName('createExpensesSheet');
var newSheetButton = CardService.newTextButton()
  .setText('New Sheet')
  .setOnClickAction(createExpensesSheet);
newSheetSection.addWidget(sheetName);
newSheetSection.addWidget(CardService.newButtonSet().addButton(newSheetButton));
card.addSection(newSheetSection);

ทีนี้ เมื่อผู้ใช้คลิก "ชีตใหม่" ส่วนเสริมจะสร้างสเปรดชีตใหม่ที่จัดรูปแบบโดยมีแถวส่วนหัวที่ตรึงแถวอยู่เพื่อให้มองเห็นได้ตลอดเวลา ผู้ใช้ระบุชื่อสำหรับสเปรดชีตใหม่ในแบบฟอร์ม แม้ว่าการใส่ค่าเริ่มต้นในกรณีที่แบบฟอร์มว่างเปล่าอาจเป็นตัวเลือกที่ดี ในการใช้งาน createExpensesSheet ให้ส่งการ์ดที่แทบจะเหมือนกันทั้งหมดไปยังการ์ดที่มีอยู่ โดยเพิ่มข้อความแสดงสถานะที่เหมาะสมและกรอก URL ของสเปรดชีตใหม่ในช่อง URL ไว้ล่วงหน้า

10. ยินดีด้วย

คุณได้ออกแบบและใช้งานส่วนเสริม Gmail ที่พบปัญหาในอีเมลเรียบร้อยแล้ว และช่วยให้ผู้ใช้บันทึกค่าใช้จ่ายลงในสเปรดชีตได้ในเวลาไม่กี่วินาที คุณได้ใช้ Google Apps Script เพื่อเชื่อมต่อกับ Google API หลายรายการและเก็บข้อมูลที่คงไว้ระหว่างการดำเนินการตามหลายครั้งของส่วนเสริม

สิ่งที่ปรับปรุงได้

ให้จินตนาการของคุณนำทางคุณไปพร้อมๆ กับปรับปรุง Expense It! แต่เรามีไอเดียบางส่วนที่จะช่วยให้ผลิตภัณฑ์มีประโยชน์ยิ่งขึ้นไปอีก

  • ลิงก์ไปยังสเปรดชีตเมื่อผู้ใช้บันทึกค่าใช้จ่ายแล้ว
  • เพิ่มความสามารถในการแก้ไข/เลิกทำการบันทึกค่าใช้จ่าย
  • ผสานรวม API ภายนอกเพื่อให้ผู้ใช้ชำระเงินและขอรับเงินได้

ดูข้อมูลเพิ่มเติม