LoginSignup
2

More than 3 years have passed since last update.

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

Last updated at Posted at 2020-07-12

こんにちは、もり(@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を設定します。

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

特記事項: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項目をメール通知しています。

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

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

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

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

alt

そこで必要になるのが、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

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
2