【freeeAPI×GAS】支払期日が1週間以内の未決済取引データ（支出のみ）をメール通知する

こんにちは、もり[(@moripro3)](https://twitter.com/moripro3)です！

夏だ！祭りだ！わっしょいわっしょい！

暑い夏はサクッと仕事を終わらせて一杯飲みたいですね。GASとfreeeAPIを使って、毎日の手作業を自動化しちゃいましょう。

# この記事の対象読者

このような方に向けて書いています。

- WebサービスのAPIを少しさわったことある（リクエスト・レスポンスの意味がわかる）
- GASでJSONデータを扱ったことがある
- **まずはGASだけを使ってfreeeAPIを操作してみたい**

freeeAPIは「いろいろなWebサービス・アプリと連携できる」のが利点ですが、**この記事ではシンプルに「GASだけ」を使用します。**

**複数アプリとの連携はハードルが高そう…と思っている方は、まず「GASだけ」でできる自動化から始めてみましょう！**


# 事前準備：GASとfreeeAPI連携アプリの認証

この記事で紹介するスクリプトを動作させるには、事前に連携アプリの作成と認証が必要です。
手順はこちらの記事をご覧ください。

[【freee×GAS】GoogleAppsScriptでfreeeAPIと連携認証する](https://moripro.net/freee-gas-api/)

途中で困ったことがあれば、Twitter[(@moripro3)](https://twitter.com/moripro3)にご連絡ください。

# 支払期日が1週間以内の未決済取引データをメール通知する

今月も取引先への支払いがたくさん…夏休みに入る前に全部片づけておかなきゃ…

そんな時に「支払い漏れ」があったら大変ですね。

freeeに登録済みの取引データから、下記3条件に合致する取引一覧を抽出して、メールで通知するスクリプトを紹介します。

- **支払期日：スクリプト実行日から「1週間以内」**
- **ステータス：「未決済」**
- **収支区分：「支出」**

メール通知ではなくSlackやChatworkに飛ばすこともできますが、**この記事は、なるべくシンプルに「GASだけ」を使う方針なのでメール送信にします**（SlackAPIやChatworkAPIの話が出てきてしまうので）


## 使用するエンドポイント

freeeAPIの公式リファレンスがこちら→[会計APIリファレンス Version: 2020-06-15](https://developer.freee.co.jp/docs/accounting/reference)

下記4つのエンドポイントにアクセスします。

||項目|メソッド|エンドポイント|用途|
|:-:|:-:|:-:|:-:|:-:|
|①| **取引（収入／支出）**|GET|**/api/1/deals**|対象取引の取得|
|②|勘定科目|GET|/api/1/account_items|ID→名称変換用|
|③|品目|GET|/api/1/items|ID→名称変換用|
|④|取引先|GET|/api/1/partners|ID→名称変換用|

※②～④は後述

## スクリプト

グローバル変数`company_id`に事業所IDを設定します。

```javascript:freee.gs
//事業所IDを設定
const company_id = '*******';

//エントリポイント
function main() {
  
  const date = new Date();
  date.setDate(date.getDate() + 7); //1週間後の日付
  const end_due_date = Utilities.formatDate(date,'JST','yyyy-MM-dd');
  
  const accessToken = getService().getAccessToken();
  const requestUrl = `https://api.freee.co.jp/api/1/deals?company_id=${company_id}&status=unsettled&type=expense&end_due_date=${end_due_date}&limit=100`;
  const obj = accessfreeeAPI_(accessToken,requestUrl);
  
  if (obj.meta.total_count) {
    
    const values = convertObjDealToArray_(obj.deals);
    const body = createAlertBody_(values);
    
    sendMail_(body);
    
  }
  
}

/**
* freeeAPIの指定URLにリクエストを送信してレスポンスを返す
*
* @param {string} アクセストークン
* @param {string} リクエストURL
* @return {Object} freeeAPIのレスポンス
*/
function accessfreeeAPI_(accessToken,url) {
  
  Utilities.sleep(1000); //短時間の連続アクセスを回避するため待機
  
  const params = {
    method : 'get',
    headers : {'Authorization':'Bearer ' + accessToken,
               'X-Api-Version': '2020-06-15'}
  };
  
  const response = UrlFetchApp.fetch(url,params);
  const obj = JSON.parse(response);
  
  return obj;
  
}

/**
* 取引データのオブジェクトを二次元配列に変換する
* (IDは日本語名称に変換)
*
* @param {Object} freeeAPIから取得した取引データ
* @return {Object[]} 取引データ
*/
function convertObjDealToArray_(objDeals) {
  
  let values = [];
  
  for (const i in objDeals) {
    
    //勘定科目ID,品目IDは明細行の1番目を取得    
    const {account_item_id,item_id} = objDeals[i].details[0];
    const {due_date,amount,partner_id} = objDeals[i];
    
    values.push([due_date,account_item_id,amount,partner_id,item_id]);
    
  }
  
  //IDを名称に変換する
  values = convertArrayData_(values);
  
  //支払い期日でソートする
  values.sort();
  
  return values;
  
}

/**
* 取引データに含まれるIDを日本語名称に変換する
*
* @param {Object[]} ID変換前の取引データ
* @return {Object[]} ID変換後の取引データ
*/
function convertArrayData_(values) {
  
  const index = {
    account_item: 1, //勘定科目
    partner: 3, //取引先
    item: 4 //品目
  };
  
  //freeeAPIから各種マスタを取得 
  const [objAccountItems,objPartners,objItems] = getfreeeMasterData_();
  
  for (const arr of values) {
    
    //勘定科目ID -> 勘定科目名
    const account_item_id = arr[index.account_item];
    arr[index.account_item] = fromIdToName_(objAccountItems,account_item_id);
    
    //取引先ID -> 取引先名
    const partner_id = arr[index.partner];
    arr[index.partner] = fromIdToName_(objPartners,partner_id);
    
    //品目ID -> 品目名
    const item_id = arr[index.item];
    arr[index.item] = fromIdToName_(objItems,item_id);
    
  }
  
  return values;
  
}

/**
* ID→名称変換用のマスタをfreeeAPIから取得する
*
* @return {Object[]} ID→名称変換用マスタデータ
*/
function getfreeeMasterData_() {
  
  //勘定科目一覧,取引先一覧,品目一覧
  const accountsUrl = `https://api.freee.co.jp/api/1/account_items?company_id=${company_id}`;
  const partnersUrl = `https://api.freee.co.jp/api/1/partners?company_id=${company_id}`;
  const itemsUrl = `https://api.freee.co.jp/api/1/items?company_id=${company_id}`;
  
  const arrUrls = [accountsUrl,partnersUrl,itemsUrl];
  
  const accessToken = getService().getAccessToken();
  
  const arrObj = [];
  
  for (const url of arrUrls) {
    
    const obj = accessfreeeAPI_(accessToken,url);
    arrObj.push(obj);
    
  }
  
  const objAccountItems = arrObj[0].account_items;
  const objPartners = arrObj[1].partners;
  const objItems = arrObj[2].items;
  
  return [objAccountItems,objPartners,objItems];
  
}

/**
*　IDから名称を返す(例:勘定科目ID -> 勘定科目名)
*
* @param {string} ID
* @return {string} 日本語名称
*/
function fromIdToName_(obj,id) {
  
  let name = '';
  
  for (let i in obj) {
    if (obj[i].id === id) {
      name = obj[i].name;
      break;
    }    
  }
  
  return name;
  
}

/**
* 対象の取引データを元に通知用の本文を作成する
*
* @param {Object[]} 通知対象の取引データ
* @return {string} 通知本文
*/
function createAlertBody_(values) {
  
  let body = '';
  
  for (let i = 0; i < values.length; i++) {
    
    const due_date = values[i][0]; //支払期日
    const account_item = values[i][1]; //勘定科目
    const amount = values[i][2]; //金額
    const partner = values[i][3]; //取引先
    const item = values[i][4]; //品目
    
    body += `${due_date}, ${account_item}, ${amount}, ${partner},${item}\n`;
    
  }
  
  const cnt = values.length;
  const alert = '支払期日が1週間以内、かつ、未決済の取引が【' + cnt + '】件あります\n\n';
  const header = '支払期日, 勘定科目, 金額, 取引先, 品目\n';
  body = alert + header + body;
  
  return body;
  
}

/**
* メール送信する
*
* @param {string} メール本文
*/
function sendMail_(body) {
  
  const recipient = '*********@test.jp'; //送信先
  const subject = '【会計freee】支払期日1週間以内の未決済取引一覧';
  
  const options = {
    name: 'freee通知' //差出人名
  };
  
  GmailApp.sendEmail(recipient, subject, body, options);
  
}
```
## トリガーで自動実行する

GASの強みといえば「トリガーによる自動実行」です！毎朝スクリプトが実行されるよう、任意の時刻にトリガーを設定しておきましょう。

※実行する関数は「main」です


## 実行結果

対象の取引データが存在する場合、このようなメールが届きます。これでうっかり支払い忘れも防げますね！

![alt](https://moripro.net/wp-content/uploads/2020/07/qiita-freee4.png)


## 特記事項：V8ランタイムを使用

このスクリプトはV8ランタイムで動作します。

古いランタイムを使用している方は、スクリプトエディタのメニュー「実行」→「Chrome V8 を搭載した新しい Apps Scriptランタイムを有効にする」をクリックして、V8ランタイムを有効にしましょう。

## カスタマイズしたい

お好みでスクリプトを変更してみてください。

### ①10日以内のデータを通知したい

Dateオブジェクトの日付設定を変更しましょう。

```javascript
const date = new Date();
date.setDate(date.getDate() + 10); //n日後の日付
```

### ②収入・支出の両方を通知したい

上記のスクリプトは「支出」のみを取得しています。

リクエストURLのtypeパラメータを削除します。

```javascript
const requestUrl = `https://api.freee.co.jp/api/1/deals?company_id=${company_id}&status=unsettled&end_due_date=${end_due_date}&limit=100`;
```
### ③収入のみを通知したい

リクエストURLのtypeパラメータの値を`income`に変更します。

```javascript
const requestUrl = `https://api.freee.co.jp/api/1/deals?company_id=${company_id}&status=unsettled&type=income&end_due_date=${end_due_date}&limit=100`;
```


## 工夫したところ

このスクリプトを書くうえで工夫した点をまとめます。

### ①レスポンスのJSONデータに含まれる「ID」を「名称」に変換する

取引（収入／支出）(/api/1/deals) で取得したデータの中から、下記の5項目をメール通知しています。

1. 支払期日
2. **勘定科目**
3. 金額
4. **取引先**
5. **品目**

上記5項目のうち、3項目は、JSONデータに「ID」しか含まれません。

- partner_id : 取引先ID
- account_item_id : 勘定科目ID
- item_id : 品目ID

「ID」をメール通知してもわからないので、convertArrayData関数で「ID」を「名称」に変換しています。

![alt](https://moripro.net/wp-content/uploads/2020/07/qiita-freee2.png)

そこで必要になるのが、3つのマスタデータ。getfreeeMasterData関数でマスタデータ（JSON）を取得しています。

- 勘定科目 : /api/1/account_items
- 品目 : /api/1/items
- 取引先 : /api/1/partners

「3つのJSONデータ」と「IDから名称を返すfromIdToName関数」で、IDを名称に変換します。


### ②短時間に過度のAPIアクセスをしない

短時間に過度のAPIアクセスをするとサーバーに負担がかかってしまうので、WebサービスのAPIを使う時のお作法(?)として、accessfreeeAPI関数内に「待機処理」を加えています。

1000ミリ秒 = 1秒です。

```javascript
Utilities.sleep(1000);
```

freeeAPI公式リファレンスにもこのような記載があるので、APIを使う際は気をつけましょう。

>freeeは一定期間に過度のアクセスを検知した場合、APIアクセスをコントロールする場合があります。

### ③1関数1処理！

このスクリプトは8個の関数で構成されています。1つの関数が長くならないよう「1関数1処理」を目標として書いてみました。

また、**部品化できる処理はなるべく切り出して、1つの関数としてまとめています。**

（例）accessfreeeAPI関数

「リクエストURL」と「アクセストークン」を渡すと、「レスポンス」を返す関数です。スクリプト全体で合計4回freeeAPIにアクセスするので、部品化しておくとスッキリしますね。

# まとめ

**freeeAPI×GASで「支払期日が1週間以内の未決済取引データをメール通知する」スクリプトを紹介しました。**

人間はうっかり忘れてしまう生きものです。**GASのトリガーを使った「リマインドBot」**は大いに役立つので、ぜひfreeeAPIを使う最初の一歩としてチャレンジしてみましょう！

**この記事は「GAS×freeeAPIでデータを取得すること」をメインテーマとしたので、**通知方法は最もシンプルな「メール」としました。SlackやChatworkなどに通知したい方は、ぜひカスタマイズしてみてください。

個人ブログでもいくつかfreeeAPI×GASのネタを書いているので、よかったら見ていってください→[「もりさんのプログラミング手帳：会計freee](https://moripro.net/category/freee/)