こんにちは、もり(@moripro3)です!
夏だ!祭りだ!わっしょいわっしょい!
暑い夏はサクッと仕事を終わらせて一杯飲みたいですね。GASとfreeeAPIを使って、毎日の手作業を自動化しちゃいましょう。
この記事の対象読者
このような方に向けて書いています。
- WebサービスのAPIを少しさわったことある(リクエスト・レスポンスの意味がわかる)
- GASでJSONデータを扱ったことがある
- まずはGASだけを使ってfreeeAPIを操作してみたい
freeeAPIは「いろいろなWebサービス・アプリと連携できる」のが利点ですが、この記事ではシンプルに「GASだけ」を使用します。
複数アプリとの連携はハードルが高そう…と思っている方は、まず「GASだけ」でできる自動化から始めてみましょう!
事前準備:GASとfreeeAPI連携アプリの認証
この記事で紹介するスクリプトを動作させるには、事前に連携アプリの作成と認証が必要です。
手順はこちらの記事をご覧ください。
【freee×GAS】GoogleAppsScriptでfreeeAPIと連携認証する
途中で困ったことがあれば、Twitter(@moripro3)にご連絡ください。
支払期日が1週間以内の未決済取引データをメール通知する
今月も取引先への支払いがたくさん…夏休みに入る前に全部片づけておかなきゃ…
そんな時に「支払い漏れ」があったら大変ですね。
freeeに登録済みの取引データから、下記3条件に合致する取引一覧を抽出して、メールで通知するスクリプトを紹介します。
- 支払期日:スクリプト実行日から「1週間以内」
- ステータス:「未決済」
- 収支区分:「支出」
メール通知ではなくSlackやChatworkに飛ばすこともできますが、この記事は、なるべくシンプルに「GASだけ」を使う方針なのでメール送信にします(SlackAPIやChatworkAPIの話が出てきてしまうので)
使用するエンドポイント
freeeAPIの公式リファレンスがこちら→会計APIリファレンス Version: 2020-06-15
下記4つのエンドポイントにアクセスします。
項目 | メソッド | エンドポイント | 用途 | |
---|---|---|---|---|
① | 取引(収入/支出) | GET | /api/1/deals | 対象取引の取得 |
② | 勘定科目 | GET | /api/1/account_items | ID→名称変換用 |
③ | 品目 | GET | /api/1/items | ID→名称変換用 |
④ | 取引先 | GET | /api/1/partners | ID→名称変換用 |
※②~④は後述
スクリプト
グローバル変数company_id
に事業所IDを設定します。
//事業所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」です
実行結果
対象の取引データが存在する場合、このようなメールが届きます。これでうっかり支払い忘れも防げますね!
特記事項:V8ランタイムを使用
このスクリプトはV8ランタイムで動作します。
古いランタイムを使用している方は、スクリプトエディタのメニュー「実行」→「Chrome V8 を搭載した新しい Apps Scriptランタイムを有効にする」をクリックして、V8ランタイムを有効にしましょう。
カスタマイズしたい
お好みでスクリプトを変更してみてください。
①10日以内のデータを通知したい
Dateオブジェクトの日付設定を変更しましょう。
const date = new Date();
date.setDate(date.getDate() + 10); //n日後の日付
②収入・支出の両方を通知したい
上記のスクリプトは「支出」のみを取得しています。
リクエストURLのtypeパラメータを削除します。
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
に変更します。
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項目をメール通知しています。
- 支払期日
- 勘定科目
- 金額
- 取引先
- 品目
上記5項目のうち、3項目は、JSONデータに「ID」しか含まれません。
- partner_id : 取引先ID
- account_item_id : 勘定科目ID
- item_id : 品目ID
「ID」をメール通知してもわからないので、convertArrayData関数で「ID」を「名称」に変換しています。
そこで必要になるのが、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秒です。
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