תוספים ל-Google Workspace מעודדים נקיטת פעולות בקלות באימייל

1. סקירה כללית

ב-codelab הזה, תשתמשו ב-Google Apps Script כדי לכתוב תוסף ל-Google Workspace ל-Gmail, שיאפשר למשתמשים להוסיף נתוני קבלות מאימייל לגיליון אלקטרוני ישירות מתוך Gmail. כשמשתמש מקבל קבלה באימייל, הוא פותח את התוסף, שמקבל באופן אוטומטי את פרטי ההוצאות הרלוונטיים מהאימייל. המשתמש יכול לערוך את פרטי ההוצאה ואז לשלוח אותם כדי לתעד את ההוצאה בגיליון אלקטרוני ב-Google Sheets.

מה תלמדו

  • יצירת תוסף ל-Gmail ל-Google Workspace באמצעות Google Apps Script
  • ניתוח אימייל באמצעות Google Apps Script
  • אינטראקציה עם Google Sheets באמצעות 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 ופותחים את קובץ המניפסט שלו.

  1. עוברים אל script.google.com. כאן אפשר ליצור, לנהל ולעקוב אחרי פרויקטים של Apps Script.
  2. כדי ליצור פרויקט חדש, לוחצים על פרויקט חדש בפינה הימנית העליונה. הפרויקט החדש נפתח עם קובץ ברירת מחדל בשם Code.gs. אל תתייחסו ל-Code.gs כרגע, תצטרכו אותו בהמשך.
  3. לוחצים על Untitled project, נותנים לפרויקט את השם Expense It! ולוחצים על Rename.
  4. בצד ימין, לוחצים על הגדרות הפרויקט הגדרות הפרויקט.
  5. מסמנים את התיבה הצגת קובץ המניפסט 'Appscript.json' בעורך.
  6. לוחצים על עריכה עריכה.
  7. כדי לפתוח את קובץ המניפסט, לוחצים על 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"
  }
}

חשוב לשים לב במיוחד לחלק במניפסט שנקרא contextualTriggers. החלק הזה במניפסט מזהה את הפונקציה בהגדרת המשתמש (UDF), שאליה מתבצעת קריאה כשהתוסף מופעל בפעם הראשונה. במקרה הזה, היא קוראת ל-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 מורכב מכרטיסים שמחולקים לקטע אחד או יותר, וכל קטע מכיל ווידג'טים שיכולים להציג מידע מהמשתמש ולקבל ממנו מידע. הפונקציה getContextualAddOn יוצרת כרטיס יחיד שמקבל פרטים על הוצאה שנמצאה באימייל. בכרטיס יש קטע אחד שמכיל שדות להזנת טקסט של נתונים רלוונטיים. הפונקציה מחזירה מערך של כרטיסים של התוסף. במקרה כזה, המערך שמוחזר כולל רק כרטיס אחד.

לפני שמבצעים פריסה של התוסף 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. בחלונית הצדדית שמשמאל, לוחצים על הסמל של Expense It! מופיע התוסף סמל הקבלה של Expense It!. יכול להיות שיהיה צריך ללחוץ על סמל האפשרויות הנוספות עוד תוספים כדי למצוא אותו.
  3. פותחים אימייל, רצוי קבלה עם הוצאות.
  4. כדי לפתוח את התוסף, לוחצים על Expense It! בחלונית הצדדית השמאלית. סמל הקבלה של Expense It!.
  5. כדי להעניק ל-Expense It! גישה לחשבון Google, לוחצים על Authorize Access (אישור גישה) ופועלים לפי ההנחיות.

התוסף מציג טופס פשוט לצד הודעה פתוחה ב-Gmail. הוא לא עושה שום דבר נוסף בשלב הזה, אבל בקטע הבא נסביר איך להוסיף לו פונקציונליות.

כדי לראות את העדכונים בתוסף במהלך התרגיל הזה, צריך רק לשמור את הקוד ולרענן את Gmail. אין צורך בפריסות נוספות.

4. גישה להודעות אימייל

מוסיפים קוד שמביא את תוכן האימייל ומחלקים את הקוד למודולים כדי לארגן אותו קצת יותר.

לצד Files (קבצים), לוחצים על סמל ההוספה הוסף קובץ > Script (סקריפט) ויוצרים קובץ בשם Cards. יוצרים קובץ סקריפט שני בשם 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 מקבלת מערך של ערכים למילוי מראש של הטופס כארגומנט אופציונלי. הפונקציה יכולה להציג הודעת סטטוס אופציונלית, שמוצגת באדום אם הסטטוס מתחיל ב-"Error:" (שגיאה), אחרת היא מוצגת בירוק. במקום להוסיף כל שדה לטופס באופן ידני, פונקציית עזר בשם 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';
}

הפונקציות ב-Helpers.gs מופעלות על ידי getContextualAddon כדי לקבוע את הערכים שמולאו מראש בטופס. בשלב הזה, הפונקציות האלה יחזירו רק את המחרוזת 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 כדי לקרוא את ההודעה שפתוחה כרגע אצל המשתמש. כדי שהפונקציה הזו תפעל, צריך להוסיף למניפסט של הסקריפט היקף נוסף שמאפשר גישה לקריאה בלבד להודעות ב-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 Sheets

בתוסף Expense It! יש טופס שבו המשתמש יכול להזין פרטים על הוצאה, אבל אין לאן לשלוח את הפרטים האלה. נוסיף לחצן שישלח את נתוני הטופס לגיליון אלקטרוני ב-Google Sheets.

כדי להוסיף כפתור, נשתמש במחלקה ButtonSet. כדי ליצור אינטראקציה עם Google Sheets, נשתמש בשירות Google Sheets.

משנים את 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 Sheets.

"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 של הגיליון האלקטרוני בטופס.

6. שמירת ערכים באמצעות שירות המאפיינים

לרוב, משתמשים ירשמו הרבה הוצאות באותו גיליון אלקטרוני, ולכן יהיה נוח להציע את כתובת ה-URL של הגיליון האלקטרוני האחרון כערך ברירת מחדל בכרטיס. כדי לדעת מה כתובת ה-URL של הגיליון האלקטרוני האחרון, נצטרך לשמור את המידע הזה בכל פעם שמשתמשים בתוסף.

שירות המאפיינים מאפשר לנו לאחסן צמדי מפתח/ערך. בדוגמה שלנו, מפתח סביר יהיה SPREADSHEET_URL והערך יהיה כתובת ה-URL עצמה. כדי לאחסן ערך כזה, צריך לשנות את submitForm ב-Cards.gs כך שכתובת ה-URL של הגיליון האלקטרוני תאוחסן כמאפיין אחרי הוספה של שורה חדשה לגיליון.

חשוב לדעת שלמאפיינים יכולים להיות אחד משלושה היקפים: סקריפט, משתמש או מסמך. ההיקף document לא חל על תוספים ל-Gmail, אבל הוא רלוונטי לסוג אחר של תוספים כשמאחסנים מידע שספציפי למסמך Google Docs או לגיליון אלקטרוני מסוים ב-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');
}

לבסוף, כדי לגשת לשירות Property, צריך גם לתת הרשאה לסקריפט. כדי לאפשר לתוסף לקרוא ולכתוב מידע על נכסים, מוסיפים את היקף ההרשאות https://www.googleapis.com/auth/script.storage למניפסט כמו קודם.

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();
}

מטמיעים את שתי הפונקציות שנותרו:

  1. getExpenseDescription יכול להיות שזה יכלול צירוף של שם השולח ונושא ההודעה, אבל יש דרכים מתוחכמות יותר לנתח את גוף ההודעה ולספק תיאור מדויק יותר.
  2. במקרה של getLargestAmount, כדאי לחפש סמלים ספציפיים שקשורים לכסף. בדרך כלל מופיעים בקבלות כמה ערכים, כמו מיסים ועמלות אחרות. נסו לחשוב איך אפשר לזהות את הסכום הנכון. יכול להיות שגם ביטויים רגולריים יהיו שימושיים.

אם אתם זקוקים להשראה נוספת, תוכלו לעיין במסמכי העזרה של GmailMessage או בקוד הפתרון שהורדתם בתחילת ה-codelab. אחרי שתגדירו הטמעות משלכם לכל הפונקציות ב-Helpers.gs, תוכלו לנסות את התוסף. פותחים את הקבלות ומתחילים לתעד אותן בגיליון אלקטרוני.

8. ניקוי הטופס באמצעות פעולות בכרטיס

מה קורה אם Expense It! מזהה בטעות הוצאה באימייל פתוח וממלא מראש את הטופס במידע שגוי? המשתמש מוחק את הטופס. המחלקות CardAction מאפשרות לנו לציין פונקציה שמופעלת כשלוחצים על הפעולה. נשתמש בו כדי לתת למשתמש דרך מהירה לנקות את הטופס.

משנים את createExpensesCard כך שהכרטיס שהוא מחזיר יכלול פעולת כרטיס עם התווית 'ניקוי הטופס', וכשלוקחים עליה יופעל הפונקציה clearForm הבאה, שאפשר להדביק ב-Cards.gs. כדי להבטיח שהודעת הסטטוס תישאר גם אחרי שהטופס ינוקה, צריך להעביר את הערך opt_status כפרמטר בשם Status לפעולה. חשוב לזכור שפרמטרים אופציונליים לפעולות צריכים להיות מסוג Object.<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 כדי ליצור ממשק עם כמה ממשקי API של Google, ושמרתם נתונים בין כמה הפעלות של התוסף.

שיפורים אפשריים

תנו לדמיון שלכם להנחות אתכם כשאתם משפרים את Expense It!, אבל הנה כמה רעיונות ליצירת מוצר שימושי עוד יותר:

  • קישור לגיליון האלקטרוני אחרי שהמשתמש רשם הוצאה
  • הוספת אפשרות לערוך או לבטל את הרישום של הוצאה
  • הטמעה של ממשקי API חיצוניים כדי לאפשר למשתמשים לבצע תשלומים ולבקש כסף

מידע נוסף