[freee]会計freeeAPIクラスを作ろう#3 スプレッドシートで楽をしよう

freee

どうも。つじけ(tsujikenzo)です。このシリーズでは、「会計freeeAPIクラスを作ろう」について、全3回でお送りいたします。今日は3回目です。

前回のおさらい

前回は、「クラスで楽をしよう」をお届けしました。

[freee]会計freeeAPIクラスを作ろう#3 スプレッドシートで楽をしよう
どうも。つじけ(tsujikenzo)です。このシリーズでは、「会計freeeAPIクラスを作ろう」について、全3回でお送りいたします。今日は3回目です。前回のおさらい前回は、「クラスで楽をしよう」をお届けしました。...

今回は、「スプレッドシートで楽をしよう」をお届けします。

今日のアジェンダ

  • パラメータ用スプレッドシート
  • リクエストボディ用スプレッドシート
  • 出力用スプレッドシート

パラメータ用スプレッドシート

今回は、コンテナバインドスクリプトでコードを書いています。

せっかくスプレッドシートが使えますので、freeeAPIクラスの手助けをしてくれるシートとして活用したいところです。

よく使うもので、コードに書いたり管理がめんどくさくて、人間の目にやさしいインターフェイスが欲しいのが、「パラメータ」です。

コンテナバインドスクリプトを、パラメータ作成用に活用しましょう。

データ種別ごとのシート

クラスと同じように、シート構造もクラスと一致させるとわかりやすいでしょう。

シート名の先頭には**parameters_**というキーワードをつけておきます。 

クラスParameterを作成します。インスタンス生成時にシート名の後半を受け取るように待ち構えておきます。

/**
 * パラメータを作成するクラス
 */
class Parameters {

  /** シート名を作るコンストラクタ */
  constructor(sheetName) {
    this.sheetName = `parameters_${sheetName}`;
  }


  /**
    * シートを掴むメソッド
    * @return {Object} sheetObject 
    */
  getSheet() {
    return SpreadsheetApp.getActiveSpreadsheet().getSheetByName(this.sheetName);
  }

}

インスタンス生成時に、シート名の後半を渡すというのは、こういうことです。

const p = new Parameters('account_items');

もちろん、クラスAccount_items内に記述されます。

/**
 * 勘定科目に関するクラス
 * @extends {ApiRequests} 
 */
class AccountItems extends ApiRequests {

  /** urlを作るコンストラクタ */
  constructor() {
    super();
    this.url = `${this.baseUrl}account_items?`;
  }


  /**
    * すべての勘定科目を返すメソッド
    * @return {Array} items 
    */
  getAccountItems() {
    const p = new Parameters('account_items');
    //処理
  }

オブジェクト指向っぽいですね。

パラメータ入力用セル

スプレッドシートには、このように、パラメータと入力用の枠を用意しておきます。

公式リファレンスから、項目について解説を貼り付けてあげると親切ですね。 

ここに入力された値を、GASで取得します。

クラスParameterに、getQueries()メソッドを定義してみました。

  /**
    * シートから連結したクエリ文字列を返すメソッド
    * @return {string} queries - e.g 事業所ID&件数&offset 
    */
  getQueries() {
    const sheet = this.getSheet();
    const values = sheet.getDataRange().getValues();
    const noEmptyValues = values.filter(value => value[1] !== ''); //[1]はシートのB列
    const strValues = noEmptyValues.map(value => `&${value[0]}=${value[1]}`)
    const queries = strValues.join('').slice(1); //先頭の&を削除
    return queries;
  }

このようにリクエストURLを生成できます。(実際にはリクエストURLはfetchされるので、内部に組み込まれています)

/**
  * TEST用関数
  */
function testAccountItems() {

  const a = new AccountItems();

  const p = new Parameters('account_items');
  const queries = p.getQueries();

  const url = a.url + queries;
  console.log(url); //https://api.freee.co.jp/api/1/account_items?company_id=3293428

}

リクエストボディ用スプレッドシート

コンテナのスプレッドシートは、パラメータだけでなく、リクエストボディの作成にも応用できるでしょう。 

リクエストボディシートを掴むために、クラスParameterにインスタンス変数とメソッドを追記します。

/**
 * パラメータを作成するクラス
 */
class Parameters {

  /** シート名を作るコンストラクタ */
  constructor(sheetName) {
    this.sheetName = `parameters_${sheetName}`;
    this.requestbodySheetName = `requestbody_${sheetName}`
  }


  /**
    * シートを掴むメソッド
    * @return {Object} sheetObject 
    */
  getSheet() {
    return SpreadsheetApp.getActiveSpreadsheet().getSheetByName(this.sheetName);
  }

  /**
    * リクエストボディシートを掴むメソッド
    * @return {Object} sheetObject 
    */
  getRequestBodySheet() {
    return SpreadsheetApp.getActiveSpreadsheet().getSheetByName(this.requestbodySheetName);
  }

  /**
    * シートから連結したクエリ文字列を返すメソッド
    * @return {string} queries - e.g 事業所ID&件数&offset 
    */
  getQueries() {
    const sheet = this.getSheet();
    const values = sheet.getDataRange().getValues();
    const noEmptyValues = values.filter(value => value[1] !== ''); //[1]はシートのB列
    const strValues = noEmptyValues.map(value => `&${value[0]}=${value[1]}`)
    const queries = strValues.join('').slice(1); //先頭の&を削除
    return queries;
  }


  /**
    * リクエストボディシートからvaluesを返すメソッド
    * @return {Array} values 
    */
  getRequestBodyValues() {
    const sheet = this.getRequestBodySheet();
    return sheet.getDataRange().getValues();
  }

}

パラメータを作成する処理と、リクエストボディを作成する処理は、無関係です。

このように、処理が関わり合っていないことを、疎結合と呼びます。

ソースコードをできるだけ疎結合にすることで、「ここを変更したら、どこに影響でるか分からないから怖いなぁ」ということがなくなります。

疎結合にするために、メソッドもちいさく、(1行でもメソッドに分けたりするそうです。)、1つのメソッドでやることは1つなどのお作法がありますが、いつかまとめたいと思います。

出力用スプレッドシート

最後に、出力用のスプレッドシートです。

出力用のスプレッドシートは、「取引先別」や「収支/支出別」や「支払期限別」など、いくつあってもいいと思います。

取引先がそこまで多くないのなら、あらかじめシートを用意してもいいと思います。 

よく、「取引先が変更になったときでも耐えられるようなソースコードを書きたいな」と思ってしまうところですが、これはわたしの悪いクセです。

取引先の名前が変更になったら、手で変更すればいいのです。

追加されたらシートを増やせばいいのです。

それを、Partnersクラスから、Partners一覧を取得して、一覧からシートを作成・管理しよう、と意気込んでしまうので、本来の目的がなかなか達成できなくなるのです。

プロパティとフィールド名

おなじことが、フィールド名に使うJSONプロパティにも言えると思います。

会計freeeAPIで返されるJSONは、無限ではありませんし、そんなしょっちゅうプロパティが変更されるものではありません。

特に抽出したいプロパティが決まっているなら、フィールド名としてベタ書きしてもかまわないと思います。 

たいせつなのは、配列のインデックスで取らずに、連想配列で処理することです。

dealsクラス

と、ながくなってしまいましたが、実はdealsクラスをきれいに書けていません。。。

とりあえずdealsをスプレッドシートに出力するものは、書きましたのでUPしておきます。

Parameterシートのリクエストボディを利用した、dealsへのPOSTも書けています。

/**
 * 取引(収入/収支)に関するクラス
 * @extends {ApiRequests} 
 */
class Deals extends ApiRequests {

  /** urlを作るコンストラクタ */
  constructor() {
    super();
    this.url = `${this.baseUrl}deals?`;
    this.postParams.payload = this.getRequestBody();
  }

  /**
    * 取引(収入/収支)JSONを取得するメソッド
    * @return {Object} json 
    */
  getDeals() {
    const p = new Parameters('deals');
    const queries = p.getQueries();
    const url = this.url + queries;
    const json = this.fetchRequest(url, this.params); //fetchRequest()とparamsはsuperclassから
    const deals = json.deals;
    return deals;
  }


  /**
    * 取引(収入/収支)を登録するメソッド
    * renews, receiptsには非対応 
    * @return {Object} json 
    */
  getRequestBody() {
    const p = new Parameters('deals');
    const values = p.getRequestBodyValues();

    //最終的なリクエストボディオブジェクトを作るための初期化
    const requestBody = {};

    //details、paymentsの中身オブジェクトを作るための初期化
    const detailsObj = {};
    const paymentsObj = {};

    //requestBodyを作る処理
    values.map(value => {

      //空白セルの排除
      if (value[1] === '') return;

      //value[0]にキーワードが入ってるかどうか
      const isDetails_ = value[0].includes('details_');
      const isPayments_ = value[0].includes('payments_');

      /** detailsを整形 */
      if (isDetails_) {
        const property = value[0].replace('details_', '');
        detailsObj[property] = value[1];
        requestBody['details'] = [detailsObj];
      }

      /** paymentsを整形 */
      if (isPayments_) {
        const property = value[0].replace('payments_', '');
        paymentsObj[property] = value[1];
        requestBody['payments'] = [paymentsObj];
      }

      /** それ以外を整形 */
      if (!(isDetails_) && !(isPayments_)) requestBody[value[0]] = value[1];

    });

    const json = JSON.stringify(requestBody);
    return json;
  }


  /**
    * 取引(収入/収支)を登録するメソッド
    * @return {Object} json 
    */
  postDeal() {
    const json = this.fetchRequest(this.url, this.postParams); //fetchRequest()とpostParamsはsuperclassから
    return json;
  }


  /**
    * 取引(収入/収支)Valuesをすべて取得するメソッド
    * @return {Object} values
    */
  getDealValues() {
    const deals = this.getDeals();
    const values = deals.map(deal => this.getFullArray_(deal)).flat();
    return values;
  }


  /**
    * 単体dealを、2次元配列に変換するサブメソッド
    * @param {Object} 単体deal
    * @return {Array} dealの2次元配列化
    */
  getFullArray_(deal) {

    //最上層のプロパティを格納
    const upperProperties = Object.values(deal);

    //detailsプロパティを格納
    const details = deal['details'];
    const detailsArray = details.map(detail => Object.values(detail));

    //paymentプロパティを格納
    const payments = deal['payments'];

    //deal['payments']プロパティが無い場合、paymentsArrayを作る
    let paymentsArray; //let宣言が必要
    try {
      paymentsArray = payments.map(payment => Object.values(payment)).flat();
    } catch{
      upperProperties.push(''); //配列の要素数を合わせる為
      paymentsArray = ["id", "date", "from_walletable_type", "from_walletable_id", "amount"];//freeeでプロパティの増減仕様変更があると動かなくなる
    }

    //detailsプロパティを基点に、fullArrayを整形
    const fullArray = detailsArray.map(detail => {
      return [...upperProperties, ...detail, ...paymentsArray];
    }
    );

    return fullArray;
  }


}

クラス作成に終わりはありませんので、今後もアップデートしていきたいと思います。

まとめ

以上で、「スプレッドシートで楽をしよう」をお届けしました。

ノンプロ研freee講座をこれから受講されるみなさまを応援いたします!

ソースコードは、Githubで公開しています。

GitHub - tsujike/gas-freeeAPIClasses: 会計freeeAPIクラスです
会計freeeAPIクラスです. Contribute to tsujike/gas-freeeAPIClasses development by creating an account on GitHub.

このシリーズの目次

  1. [freee]会計freeeAPIクラスを作ろう#1 認証を楽にしよう
  2. [freee]会計freeeAPIクラスを作ろう#2 クラスで楽をしよう
  3. [freee]会計freeeAPIクラスを作ろう#3 スプレッドシートで楽をしよう
タイトルとURLをコピーしました