RPAとGASを活用した業務プロセスの効率化と業務改善

![AdobeStock_562959968_Preview.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3945076/7a02275d-b544-c375-72d7-23eb895c4dfc.jpeg)

この記事は[スタンバイ Advent Calendar 2024](https://qiita.com/advent-calendar/2024/stanby)の22日目の記事です。

https://qiita.com/advent-calendar/2024/stanby


## はじめに
[株式会社スタンバイ](https://jp.stanby.com/)のBPRグループに所属し、業務プロセスの効率化に取り組んでいるもりたぬです。本記事では、Redashからのデータ収集、スプレッドシートへの反映、Slack通知など、具体的な実装例を通じて、RPAとGASを活用した業務改善の効果を紹介します。また、トラブルシューティングや注意点についてもご紹介します。

## BPRグループとは？
『BPR』（読み：ビーピーアール）は、業務本来の目的に向かって既存の組織や制度を抜本的に見直し、プロセスの視点で職務、業務フロー、管理機構、情報システムをデザインしなおす手法です。
> ※引用：野村総合研究所「BPR（ビジネスプロセス・リエンジニアリング）」
> 【参考リンク】[NRI BPRとは](https://www.nri.com/jp/knowledge/glossary/lst/alphabet/bpr)

BPRの考え方に基づき、BPRグループ（Business Process Reengineering Group）は、以下のミッションを掲げて活動しています：

- **課題の発見と分析**
  - 業務フローのボトルネックを特定し、非効率なプロセスを見直します。
- **最適なプロセス設計とツール導入**
  - RPAやデータ分析ツールを活用し、効率化を推進します。
- **新しいプロセスへの移行支援**
  - 仕組みを受け入れやすい環境を構築し、新プロセスを定着させます。

## 求人数の増減の可視化
### 依頼概要
　現場から「求人数が大幅に増減したアカウントがあればSlackで通知してほしい」との依頼を受けました。これまで手作業で情報を取得していたため、迅速な対応が困難でした。そこで、RPAとGASを組み合わせた自動化を実装しました。
### 解決方針
1. 求人数の自動集計と可視化
    - 増減条件の閾値を設定し、対象アカウントを抽出します。
2. Slack通知による即時共有
    - 閾値以上の変動が発生した場合、Slack通知で増減データを共有します。
### 実装内容
**1. RPAによるデータ抽出**
※RPA（Robotic Process Automation）は、繰り返しの業務プロセスを自動化する技術です。
- ツール: BizRobo!
- 内容: Redashから求人数データを自動抽出し、CSV形式で共有ドライブに保存します。

    > BizRobo!の詳細はこちら：[BizRobo!公式サイト](https://rpa-technologies.com/products/first/) 


 **2. GASによるデータ処理**   
- CSVの読み込み: 最新のCSVをGoogleドライブから取得し、スプレッドシートに反映。
- 重複処理の防止:スプレッドシートの日付を参照し、同日中の再処理を防止。
- 翌日のトリガー再設定: GASのトリガーを翌日の指定時刻（例: 10:45）に再設定。

以下のコードを実行する前に、次の点を確認してください：
- パラメータ（`YOUR_SLACK_WEBHOOK_URL` `YOUR_FOLDER_ID` `YOUR_SPREADSHEET_ID` ）を置き換えて使用してください。
- スプレッドシートの構成が、コードのセル指定と一致していることを確認してください。

```javascript
// SlackのWebhook URL
var slackWebhookUrl = 'YOUR_SLACK_WEBHOOK_URL';

// メイン処理
function main() {
  try {
    // CSVデータを処理
    if (dailyjoblistingsupdated()) {
      Logger.log('求人数の集計が完了しました。');
    }

    // トリガーを再設定
    createTrigger();
  } catch (e) {
    Logger.log('エラーが発生しました: ' + e.message);
    throw e;
  }
}

// CSVデータの取り込みと処理
function dailyjoblistingsupdated() {
  var folderId = 'YOUR_FOLDER_ID'; // GoogleドライブのフォルダID
  var spreadsheetId = 'YOUR_SPREADSHEET_ID'; // スプレッドシートID
  var folder = DriveApp.getFolderById(folderId);
  var files = folder.getFilesByType(MimeType.CSV);
  var sheet = SpreadsheetApp.openById(spreadsheetId).getSheetByName('求人数');

  if (files.hasNext()) {
    var file = files.next();
    var csvData = Utilities.parseCsv(file.getBlob().getDataAsString());

    // 今日の日付
    var today = Utilities.formatDate(new Date(), Session.getScriptTimeZone(), 'yyyy/MM/dd');

    // J列: 最新データの日付（処理済みか確認）
    if (sheet.getRange('J1').getValue() === today) {
      Logger.log('本日のデータは既に処理済みです。');
      return false;
    }

    // スプレッドシートにデータを反映
    sheet.getRange(2, 1, csvData.length, csvData[0].length).setValues(csvData);

    // 差分を計算
    var prevCount = sheet.getRange('F1').getValue(); // F列: 前々日の求人数
    var currentCount = sheet.getRange('H1').getValue(); // H列: 前日の求人数
    var diff = currentCount - prevCount; // 差分を計算
    Logger.log('前々日: ' + prevCount + ', 前日: ' + currentCount + ', 差分: ' + diff);

    // J列: 本日分処理済みと記録
    sheet.getRange('J1').setValue(today);

    // 処理済みのCSVファイルを削除
    file.setTrashed(true);
    return true;
  } else {
    Logger.log('CSVファイルが見つかりません。');
    return false;
  }
}

// 翌日のトリガーを設定
function createTrigger() {
  // 既存のトリガーを削除
  var allTriggers = ScriptApp.getProjectTriggers();
  for (var i = 0; i < allTriggers.length; i++) {
    if (allTriggers[i].getHandlerFunction() === 'main') {
      ScriptApp.deleteTrigger(allTriggers[i]);
    }
  }

  // 翌日10:45に新しいトリガーを設定
  var tomorrow = new Date();
  tomorrow.setDate(tomorrow.getDate() + 1);
  tomorrow.setHours(10, 45, 0, 0);

  ScriptApp.newTrigger('main')
    .timeBased()
    .at(tomorrow)
    .create();

  Logger.log('トリガーが明日の10:45に設定されました。');
}
```
**3. GASによるSlack通知**  
- 通知条件
    - 差分が閾値以上の場合に通知を送信。
    - 通知内容に対象アカウントのリストを含む。
```javascript
// Slack通知メッセージを送信
function sendSlackNotification(message) {
  var slackWebhookUrl = 'YOUR_SLACK_WEBHOOK_URL'; // Webhook URL
  var sheetID = 'YOUR_SPREADSHEET_ID'; // スプレッドシートID
  var spreadsheet = SpreadsheetApp.openById(sheetID);

  // 必要なシートデータを取得
  var adSheet = spreadsheet.getSheetByName('求人数');
  var adCount = adSheet.getRange('J1').getValue(); // J列: 求人数の前日差分
  var prevPrevDayCount = adSheet.getRange('F1').getValue(); // F列: 前々日の求人数
  var prevDayCount = adSheet.getRange('H1').getValue(); // H列: 前日の求人数

  // 通知条件の閾値 (必要に応じて設定)
  var ALERT_THRESHOLD = x; // x 件以上の場合に通知を作成

  if (adCount >= ALERT_THRESHOLD) {
    // 増加通知の作成
    var topSheet = spreadsheet.getSheetByName('増加アカウント');
    var message = createAlertMessage(
      '増加',
      adCount,
      prevPrevDayCount,
      prevDayCount,
      topSheet,
      ALERT_THRESHOLD
    );
    sendToSlack(slackWebhookUrl, message);
  } else if (adCount <= -ALERT_THRESHOLD) {
    // 減少通知の作成
    var topSheet = spreadsheet.getSheetByName('減少アカウント');
    var message = createAlertMessage(
      '減少',
      adCount,
      prevPrevDayCount,
      prevDayCount,
      topSheet,
      ALERT_THRESHOLD
    );
    sendToSlack(slackWebhookUrl, message);
  } else {
    Logger.log('通知条件に該当しませんでした。');
  }
}

// 通知メッセージを作成
function createAlertMessage(type, adCount, prevPrevDayCount, prevDayCount, sheet, threshold) {
  var icon = type === '増加' ? ':arrow_up:' : ':arrow_down:';
  var trend = type === '増加' ? ':chart_with_upwards_trend:' : ':chart_with_downwards_trend:';
  var alertMessage = `${icon}【${type}】求人数が${threshold}件以上${type}しました。\n\n`;
  alertMessage += `前々日: ${prevPrevDayCount}\n前日: ${prevDayCount}\n差分: ${adCount}\n\n`;
  alertMessage += `${trend} 対象アカウント:\n`;
  alertMessage += getAccountData(sheet);
  return alertMessage;
}

// シートデータから対象アカウントを取得
function getAccountData(sheet) {
  var data = '';
  var ids = sheet.getRange('B4:B14').getValues(); // B列: アカウントID
  var names = sheet.getRange('C4:C14').getValues(); // C列: アカウント名
  var counts = sheet.getRange('K4:K14').getValues(); // K列: 求人数の前日差分

  for (var i = 0; i < ids.length; i++) {
    data += `${ids[i]} ${names[i]} ${counts[i]}\n`;
  }
  return data;
}

// Slackに通知を送信
function sendToSlack(webhookUrl, message) {
  var payload = { "text": message };
  var options = {
    method: 'post',
    contentType: 'application/json',
    payload: JSON.stringify(payload)
  };
  UrlFetchApp.fetch(webhookUrl, options);
}


```
### 実装結果
**Slack通知のサンプル**  
![スクリーンショット 2024-12-18 154120.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3945076/1fa3e112-51b9-637a-8e0b-797c63b7b5b0.png)


### RPAとGASのトラブルシューティング
**RPAとGASを組み合わせた自動化**を行う場合、スケジュール設定の順番に注意が必要です。適切に設定しないとGASがRPAの処理前に実行され、空のデータや未処理のファイルを読み込んでしまう可能性があります。

#### 1. トリガー設定の基本ルール
自動化のフローが正しく動作するために、以下の順番でトリガーを設定します。
    1. RPAのトリガーを先に設定
　RPAツール（例: BizRobo!）がデータ抽出や処理を行うタイミングをスケジュールします。
    2. GASのトリガーをRPAの終了後に設定
    
#### 2. 注意点
- RPAの処理時間を確認する
　RPAが安定して完了する時間を見極め、GASがその後に動くよう設定してください。
- ファイルの存在確認
　RPAが生成するファイルやデータが正しく出力されているか、GAS側で確認を行うとより安全です。

 RPAとGASを組み合わせることで高度な自動化が実現できますが、スケジュール設計や処理フローの工夫がシステム全体の安定性を高める鍵となります。

#### 3. スケジュール設定の例
| 処理の流れ | 実行時間 | 使用ツール | 備考 |
|------------|------------|------------|------------|
| RPA処理    | 10:00    | BizRobo!（RPA） |Redashからデータを抽出 → CSV保存|
| GAS処理    | 10:45    | Google Apps Script    |CSVデータを処理・可視化・Slack通知|

#### 4. よくあるトラブルと対策
| トラブル内容 | 原因 | 対策 |
|------------|------------|------------|
| GASが空のデータを処理してしまう    | RPA処理が終わる前にGASが起動    | GASのトリガー時間をRPAの終了後に設定 |
| ファイルが見つからず処理が失敗する    | RPAのファイル出力が遅れている    | ファイル有無確認のロジックを追加する    |
| データ重複の通知が発生する    | 同じ日付でGASが複数回動いてしまう    | スプレッドシートの日付確認を追加する    |

### RPAとGASで実現する業務プロセスの効率化と改善
**RPAとGASを組み合わせることで、手動で行っていた繰り返し作業を自動化し、業務効率化と業務改善を行うことができました。**

具体的には以下の効果を得ることができました：
1. データ収集と処理の自動化
    - RPAがRedashや他のデータソースから必要な情報を自動で取得することで、手動操作の手間を削減できます。
    - GASを活用してデータの整理や計算を行い、迅速な意思決定をサポートします。

2. タイムリーな情報共有
     - 必要な情報をSlackなどのツールでリアルタイムに共有することで、重要なデータの増減や異常値に即座に対応できます。

3. 安定したワークフローの実現
    - 時間や条件に基づくトリガーによって、忘れやすいタスクや時間がかかるプロセスを確実に実行できます。
    - 自動化による正確なデータ処理が可能になり、手動作業に起因するエラーを削減します。

4. リソースの有効活用
    - 手動作業が削減されることで、チーム全体がより付加価値の高い業務に集中できます。
    - 自動化された通知により、全員が最新情報を把握でき、チーム間の連携が向上します。

### おわりに
RPAとGASを活用することで業務の効率化を図った事例を紹介させていただきました。

今後もさらなる改善や新たなツールとの連携を模索し、より効率的な業務プロセスを構築していきたいと考えています。本記事が皆様の業務効率化の一助となれば幸いです。

明日は @kmikami さんで **事業会社に属するGallup認定ストレングスコーチが、活動を通じて得た知見をまとめてみる** です！お楽しみに！