お問い合わせ

【顧客管理RPA】formrun経由の顧客情報をSpread Sheetへ自動登録し、顧客ごとにユニークIDを自動採番するシステム構築

株式会社Giving Firstのコンサルティング事例を紹介します。

本プロジェクトでは、某有名YouTuberが運営するオンラインサロンの運営会社さまからのご依頼で、formrunおよびGoogle Spread Sheet上で管理しているものの、販促活動が盛況で、運用管理に苦難されていることから業務効率化支援として、Google Spread Sheetベースの自動化プログラムを構築し、ご支援させていただきました。

実施概要

formrunで申し込みがあった顧客を手動でSpread Sheet上で登録・管理しており、手作業による工数が肥大化。

formrunでの申込みをトリガーに、ユニークな顧客番号を自動採番したい、初回面談申し込み時のヌケモレを防ぐべく情報の記録を管理したい、流入経路を明示したい(どのLPからの流入なのか判別したい)等を課題に抱えておられました。

  • 顧客:某有名YouTuberが運営するオンラインサロン運営会社
  • メインシステム:formrun, Google App Script(GAS)
  • 関連・周辺システム:Gmail
  • 担当領域
    • 基本設計・詳細設計・開発工程
    • システム開発(Java ScriptベースのGoogle App Script開発による案件管理表への自動転記)
    • RPA(Webhookを活用した社内Chatへの通知自動化)

プロジェクトのゴール

情報共有の円滑化と業務運営の効率化を目的に、社内ポータルサイトと周辺システムの構築、業務フローを定義することをゴールとして設定し、ご支援致しました。

  • LPごと(=販促会社ごと)に分かれているフォームから登録される顧客をひとつのSpread Sheetに統一すること
  • 販促会社が増えるたびに自動(もしくは半自動で)同じ仕組みで横展開できる運用体制を確立すること
  • 顧客を判別できる仕組みを作ること(顧客IDを自動採番しSpread Sheetに登録する)

支援内容

  • formrunからの通知メールをトリガーとしたGoogle Spread Sheetへの情報連携プログラム
  • formrunでの設定変更(複数フォームをひとつの通知メールに設定)
  • 顧客管理簿・案件管理(Google Spread Sheet)の構築

Google Spread Sheetへの自動反映プログラム

弊社で納品したプログラムをサンプルとしてご紹介します。

当該プログラムは、Google App Scriptで実装し、5分程度のポーリング間隔で実行します。

BtoC領域のサービスを展開されている企業様で同じようなお悩みを抱えておられる場合は、弊社のお問合せフォームからご相談ください。

プログラムの処理内容

  • formrunからお問い合わせがあった際に受信する「通知メール」をトリガー
  • 過去のメールを重複して処理しないように、メールIDをキーに重複排除
  • 通知メールから取得した顧客情報を任意のGoogle Spread Sheetに自動転記する
  • 初回面談の予約日時をJSTに変換し、Spread Sheet上で記録
  • 問い合わせがあった顧客にユニークなIDを自動採番(100000を開始番号)
function extractAndWriteToSheet() {
  // スプレッドシートとシートを指定
  var spreadsheetId = 'XXXXX'; // スプレッドシートのIDを指定
  var sheetName = 'Master'; // シート名を指定
  var logSheetName = 'ProcessedEmails'; // 処理済みメールIDを記録するシート名
  var spreadsheet = SpreadsheetApp.openById(spreadsheetId);
  var sheet = spreadsheet.getSheetByName(sheetName);
  var logSheet = spreadsheet.getSheetByName(logSheetName);
  
  // 処理済みメールIDシートが存在しない場合は作成
  if (!logSheet) {
    logSheet = spreadsheet.insertSheet(logSheetName);
    logSheet.appendRow(['メールID']); // ヘッダー行
  }

  // 処理済みメールIDのリストを取得
  var lastRow = logSheet.getLastRow();
  var processedEmailIds = [];
  if (lastRow > 1) { // ヘッダー行があるため、2行目以降にデータがあるかを確認
    processedEmailIds = logSheet.getRange(2, 1, lastRow - 1, 1).getValues().flat();
  }

  // Gmailのフィルタリング条件
  var threads = GmailApp.search('subject:トリガーとするメールタイトル');
  
  // 各スレッド(メール)の処理
  for (var i = 0; i < threads.length; i++) {
    var messages = threads[i].getMessages();
    for (var j = 0; j < messages.length; j++) {
      var message = messages[j];
      var messageId = message.getId();

      // メールが既に処理されているかを確認
      if (processedEmailIds.includes(messageId)) {
        continue; // 既に処理されている場合はスキップ
      }

      var body = message.getPlainBody();
      
      // 正規表現で特定の文字列を抽出
      var teamName = body.match(/チーム名:(.+)/);
      var formName = body.match(/フォーム名:(.+)/);
      var receptionNumber = body.match(/受付番号:(.+)/);
      var responseDate = body.match(/回答日時:(.+)/);
      var email = body.match(/メールアドレス:(.+)/);
      var name = body.match(/名前:(.+)/);
      var phoneNumber = body.match(/電話番号:(.+)/);
      var consultationQuestion = body.match(/個別相談会で特に聞きたいこと:(.+)/);
      var reservationDate = body.match(/予約日程の選択:(.+)/);
      var confirmationStatus = body.match(/日程調整の確定状況:(.+)/);
      var startDate = body.match(/開始日時:(.+)/);
      var endDate = body.match(/終了日時:(.+)/);
      var timeZone = body.match(/タイムゾーン:(.+)/);
      var applicationId = body.match(/申し込みID:(.+)/);
      var reservationDetails = body.match(/予約詳細(※bookrunの管理画面に遷移します):(.+)/);
      var coordinator = body.match(/担当者:(.+)/);
      var meetingFormat = body.match(/受付完了時ミーティング形式を通知:(.+)/);
      var location = body.match(/場所:(.+)/);
      var offlineMeetingPlace = body.match(/オフラインミーティング場所名:(.+)/);
      var offlineMeetingAddress = body.match(/オフラインミーティング住所:(.+)/);
      var offlineMeetingNotes = body.match(/オフラインミーティング補足:(.+)/);
      var videoMeetingUrl = body.match(/ビデオ会議URL:(.+)/);
      var otherMeetingName = body.match(/その他ミーティング名:(.+)/);
      var otherMeetingUrl = body.match(/その他ミーティングURL:(.+)/);
      var otherMeetingNotes = body.match(/その他ミーティング補足:(.+)/);
      var birthdate = body.match(/生年月日:(.+)/);
      var inquiryPurpose = body.match(/お問い合わせ目的:(.+)/);

      // 日時変換関数
      function convertToJST(dateStr) {
        if (!dateStr) return '';
        var date = new Date(dateStr);
        var jstDate = new Date(date.toLocaleString('en-US', { timeZone: 'Asia/Tokyo' }));
        return Utilities.formatDate(jstDate, 'Asia/Tokyo', 'yyyy-MM-dd HH:mm:ss');
      }

      var startDateJST = convertToJST(startDate ? startDate[1].trim() : '');
      var endDateJST = convertToJST(endDate ? endDate[1].trim() : '');

      // ユニークなIDを生成(既存の最後のID + 1)
      var lastId = 100000;
      if (sheet.getLastRow() > 1) { // シートが空でない場合
        var lastIdRange = sheet.getRange("A2:A" + sheet.getLastRow()).getValues();
        lastIdRange = lastIdRange.map(function(row) { return parseInt(row[0], 10); }).filter(function(id) { return !isNaN(id); });
        if (lastIdRange.length > 0) {
          lastId = Math.max(...lastIdRange);
        }
      }
      var newId = lastId + 1;

      // スプレッドシートに書き込む
      var newRow = [
        newId,
        teamName ? teamName[1].trim() : '',
        formName ? formName[1].trim() : '',
        receptionNumber ? receptionNumber[1].trim() : '',
        responseDate ? responseDate[1].trim() : '',
        email ? email[1].trim() : '',
        name ? name[1].trim() : '',
        phoneNumber ? phoneNumber[1].trim() : '',
        consultationQuestion ? consultationQuestion[1].trim() : '',
        reservationDate ? reservationDate[1].trim() : '',
        confirmationStatus ? confirmationStatus[1].trim() : '',
        startDateJST,
        endDateJST,
        timeZone ? timeZone[1].trim() : '',
        applicationId ? applicationId[1].trim() : '',
        reservationDetails ? reservationDetails[1].trim() : '',
        coordinator ? coordinator[1].trim() : '',
        meetingFormat ? meetingFormat[1].trim() : '',
        location ? location[1].trim() : '',
        offlineMeetingPlace ? offlineMeetingPlace[1].trim() : '',
        offlineMeetingAddress ? offlineMeetingAddress[1].trim() : '',
        offlineMeetingNotes ? offlineMeetingNotes[1].trim() : '',
        videoMeetingUrl ? videoMeetingUrl[1].trim() : '',
        otherMeetingName ? otherMeetingName[1].trim() : '',
        otherMeetingUrl ? otherMeetingUrl[1].trim() : '',
        otherMeetingNotes ? otherMeetingNotes[1].trim() : '',
        birthdate ? birthdate[1].trim() : '',
        inquiryPurpose ? inquiryPurpose[1].trim() : ''
      ];
      
      sheet.appendRow(newRow);

      // 処理済みメールIDを記録
      logSheet.appendRow([messageId]);
    }
  }
}

\本件と同様のご相談も受け付けております/

【不動産BPR/RPA】営業体制の構築と業務改善を目的とした顧客管理・自動記録通知プロセス構築(GAS,Spread Sheet等)

株式会社Giving Firstのコンサルティング事例を紹介します。

本プロジェクトでは、不動産仲介会社さまからのご依頼で、SUUMOやホームズといった不動産媒体系からのリードを一元管理し営業体制を強化・効率化することを目的に営業体制の組成とシステム構築をご支援させていただきました。

実施概要

不動産媒体サイトから流入するリードの顧客管理(ホットリード選別・追客・営業マンのステータス管理)の一元化・効率化を目的に、顧客管理を自動化する業務プロセスを構築しました。

  • 顧客:不動産仲介・売買会社
  • メインシステム:Google App Script(GAS)
  • 関連・周辺システム:Google Spread Sheet、Gmail、Lark
  • 担当領域
    • 要求定義・要件定義・基本設計・詳細設計・開発工程
    • BPR(業務フローの分解と新規構築、活用する情報システムの提案・決定、THE MODEL型の営業組織構築)
    • システム開発(Java ScriptベースのGoogle App Script開発による案件管理表への自動転記)
    • RPA(Webhookを活用した社内Chatへの通知自動化)

プロジェクトのゴール

情報共有の円滑化と業務運営の効率化を目的に、社内ポータルサイトと周辺システムの構築、業務フローを定義することをゴールとして設定し、ご支援致しました。

  • リードの自動登録・管理(媒体サイト経由のリードを管理簿に自動登録する仕組み)
  • リード発生時にタイムリーに対応できる仕組み(社内Chatへの即時通知)
  • リード追客の営業体制構築(ISとしてのファーストコンタクト体制、内見フェーズのFSへのトスアップ体制etc)

支援内容

営業フロー図
ヒアリング工程後の課題/改善策一覧
ヒアリング工程後の課題/改善策一覧
KGI/KPI管理指標の分解
KGI/KPI管理指標の分解
  • 業務フロー整理と再構築(BPR)
  • 経営課題・社内組織体制の課題抽出と改善策の提案
  • 顧客管理簿・案件管理(Google Spread Sheet)の構築
  • THE MODEL型の営業体制とシステムフローの構築(IS,FS,CS組成、組織体制に合わせたCRMシステム構築)

SUUMOリード獲得時のGoogle Spread Sheetへの自動反映プログラム

弊社で納品したプログラムをサンプルとしてご紹介します。

当該プログラムは、Google App Scriptで実装し、5分程度のポーリング間隔で実行します。

不動産会社様で同じ悩みを抱えておられる場合は、弊社のお問合せフォームから実装したい旨をご相談ください。

プログラムの処理内容

  • SUUMOからお問い合わせがあった際に受信する「通知メール」をトリガー
  • 通知メール内に記載されている顧客情報を取得
  • 過去のメールを重複して処理しないように、メールIDをキーに重複排除
  • 通知メールから取得した顧客情報を任意のGoogle Spread Sheetに自動転記する
function extractAndWriteToSheet() {
  // スプレッドシートとシートを指定
  var spreadsheetId = 'XXXXX'; // スプレッドシートのIDを指定
  var sheetName = 'XXXXX'; // シート名を指定
  var logSheetName = 'XXXXX'; // 処理済みメールIDを記録するシート名
  var spreadsheet = SpreadsheetApp.openById(spreadsheetId);
  var sheet = spreadsheet.getSheetByName(sheetName);
  var logSheet = spreadsheet.getSheetByName(logSheetName);
  
  // 処理済みメールIDシートが存在しない場合は作成
  if (!logSheet) {
    logSheet = spreadsheet.insertSheet(logSheetName);
    logSheet.appendRow(['メールID']); // ヘッダー行
  }

  // 処理済みメールIDのリストを取得
  var lastRow = logSheet.getLastRow();
  var processedEmailIds = [];
  if (lastRow > 1) { // ヘッダー行があるため、2行目以降にデータがあるかを確認
    processedEmailIds = logSheet.getRange(2, 1, lastRow - 1, 1).getValues().flat();
  }

  // Gmailのフィルタリング条件
  var threads = GmailApp.search('subject:検索するメールの文字列');
  
  // 各スレッド(メール)の処理
  for (var i = 0; i < threads.length; i++) {
    var messages = threads[i].getMessages();
    for (var j = 0; j < messages.length; j++) {
      var message = messages[j];
      var messageId = message.getId();

      // メールが既に処理されているかを確認
      if (processedEmailIds.includes(messageId)) {
        continue; // 既に処理されている場合はスキップ
      }

      var body = message.getPlainBody();
      
      // 正規表現で特定の文字列を抽出
      var inquiryDate = body.match(/お問合せ日時:(.+)/);
      var responseCode = body.match(/反響送付先コード:(.+)/);
      var nameKanji = body.match(/名前(漢字):(.+)/);
      var nameKana = body.match(/名前(カナ):(.+)/);
      var email = body.match(/メールアドレス:(.+)/);
      var tel = body.match(/TEL:(.+)/);
      var fax = body.match(/FAX:(.+)/);
      var contactMethod = body.match(/連絡方法:(.+)/);
      var inquiryContent = body.match(/お問合せ内容:(.+)/);
      var propertyType = body.match(/物件種別:(.+)/);
      var propertyCode = body.match(/物件コード:(.+)/);
      var propertyName = body.match(/物件名:(.+)/);
      var companyPropertyCode = body.match(/貴社物件コード:(.+)/);
      var nearestStation = body.match(/最寄り駅:(.+)/);
      var busWalk = body.match(/バス/歩:(.+)/);
      var location = body.match(/所在地:(.+)/);
      var rent = body.match(/賃料:(.+)/);
      var floorPlan = body.match(/間取り:(.+)/);
      var area = body.match(/専有面積:(.+)/);
      var propertyDetailLink = body.match(/物件詳細画面:(.+)/);
      var inquiryComment = body.match(/お問合せ内容コメント:(.+)/);
      
      // スプレッドシートに書き込む
      var lastRow = sheet.getLastRow() + 1; // 新しい行に書き込む
      sheet.getRange(lastRow, 1).setValue(inquiryDate ? inquiryDate[1].trim() : '');
      sheet.getRange(lastRow, 2).setValue(responseCode ? responseCode[1].trim() : '');
      sheet.getRange(lastRow, 3).setValue(nameKanji ? nameKanji[1].trim() : '');
      sheet.getRange(lastRow, 4).setValue(nameKana ? nameKana[1].trim() : '');
      sheet.getRange(lastRow, 5).setValue(email ? email[1].trim() : '');
      sheet.getRange(lastRow, 6).setValue(tel ? tel[1].trim() : '');
      sheet.getRange(lastRow, 7).setValue(fax ? fax[1].trim() : '');
      sheet.getRange(lastRow, 8).setValue(contactMethod ? contactMethod[1].trim() : '');
      sheet.getRange(lastRow, 9).setValue(inquiryContent ? inquiryContent[1].trim() : '');
      sheet.getRange(lastRow, 10).setValue(propertyType ? propertyType[1].trim() : '');
      sheet.getRange(lastRow, 11).setValue(propertyCode ? propertyCode[1].trim() : '');
      sheet.getRange(lastRow, 12).setValue(propertyName ? propertyName[1].trim() : '');
      sheet.getRange(lastRow, 13).setValue(companyPropertyCode ? companyPropertyCode[1].trim() : '');
      sheet.getRange(lastRow, 14).setValue(nearestStation ? nearestStation[1].trim() : '');
      sheet.getRange(lastRow, 15).setValue(busWalk ? busWalk[1].trim() : '');
      sheet.getRange(lastRow, 16).setValue(location ? location[1].trim() : '');
      sheet.getRange(lastRow, 17).setValue(rent ? rent[1].trim() : '');
      sheet.getRange(lastRow, 18).setValue(floorPlan ? floorPlan[1].trim() : '');
      sheet.getRange(lastRow, 19).setValue(area ? area[1].trim() : '');
      sheet.getRange(lastRow, 20).setValue(propertyDetailLink ? propertyDetailLink[1].trim() : '');
      sheet.getRange(lastRow, 21).setValue(inquiryComment ? inquiryComment[1].trim() : '');

      // 処理済みメールIDを記録
      logSheet.appendRow([messageId]);
    }
  }
}

\本件と同様のご相談も受け付けております/

大手コンサルティングファーム社内向けポータルサイト構築支援(Microsoft Sharepoint Online)

株式会社Giving Firstのコンサルティング事例を紹介します。

本プロジェクトでは、大手コンサルティングファームさまからのご依頼で、Sharepoint Onlineで構築された社内向けポータルサイトの構築(Sharepoint 2013からの一部移行を含む)をご支援させていただきました。

実施概要

社内の情報管理や共有に課題を抱えており、同社で導入されているM365ファミリーのSharepoint Online(SPO)を活用し、社内ポータルサイトを一新するプロジェクト。

プラットフォームとして活用されるSPOだけでなく、関連する周辺システム(ヘルプデスク問い合わせフォーム・チケット管理システム・Chatbot当)の構築〜実装もご支援しました。

プロジェクトのゴール

情報共有の円滑化と業務運営の効率化を目的に、社内ポータルサイトと周辺システムの構築、業務フローを定義することをゴールとして設定し、ご支援致しました。

  • クライアント社内の情報の一元化(新旧ポータルサイトの連動も含む)
  • 情報共有の円滑化(すでにある情報へのアクセスの容易化・UI/UXに配慮した活用されるポータルサイトの構築)
  • 社内問い合わせの業務効率化(受付窓口の一元化・応対の一部自動化・処理フローの簡素化と新規定義)

支援内容

関連システムの一覧定義書
関連システムの一覧定義
システム遷移・関連図
システム遷移・関連図
画面設計・導線設計
画面設計・導線設計

社内にある情報(通達・最新情報・規定類・ヘルプデスクFAQ関連のコンテンツ)に3クリック以内で到達できる画面設計、コンテンツ配置を企画し、ワイヤフレームをご提示。

また、SPOを起点とする周辺システムの設計にも関与し、概念図から詳細の遷移図等も作成。当該プロジェクトで進行している登場人物(構築システム等)を見える化することで、プロジェクトメンバー間での認識を統一し、円滑なプロジェクト運営をご支援しました。

  • SPOおよび周辺システムの設計書
  • 社内ポータルサイトのトップページのワイヤーフレーム書
  • 社内ヘルプデスク問い合わせフォームのシステム概念図・遷移図・関連図
  • ポータルサイトのトップページからのリンク設計・グローバルメニュー設計
  • 上記を用いたユーザー管理者向けの説明会の開催

\本件と同様のご相談も受け付けております/