GASを使ってFreee会計の取引先一覧をスプレッドシートに出力

Freee会計は、会計データを自動化・簡略化するための優れたクラウドサービスです。APIを利用することで、会計データの取得や操作が可能となり、業務の効率化を図ることができます。

この記事では、Google Apps Script (GAS)を使用して、Freee会計APIから特定の期間の取引先一覧を取得する方法を解説します。

### 前提条件

#### Freee APIの使用準備

FreeeのAPIにアクセスするためには、まずFreeeのAPIキーと認証情報を準備します。

1. Freee Developers（開発者向け）ページにアクセスし、アプリケーションを作成します。
1. クライアントIDとクライアントシークレットを取得します。
1. OAuth 2.0を使ってアクセストークンを取得し、認証を行います。

#### Google Apps Scriptのプロジェクト作成

- Googleドライブにアクセスし、新規スプレッドシートを作成します。
- 拡張機能 > Apps Script をクリックして、スクリプトエディタを開き、新しいGAS（Google Apps Script）プロジェクトを作成します。
    ※GASプロジェクトのIDは、FreeeのコールバックURLに含まれる{GAS_SCRIPT_ID}を使用します。

#### ※注意点

- 今回にはテストを実施するため、特定の期間（2024年6月1日～6月30日）のみを対象に取引先名を取得する。
- 事業所ID(company_id)が必須ですので、事前に準備お願いします。


### ステップ1: Freee APIのアプリ作成

FreeeのAPIにアクセスするためには、まずAPIキーと認証情報を準備する必要があります。

- [Freee Developers](https://app.secure.freee.co.jp/developers/applications/new)（開発者向け）ページにアクセスし、アプリケーションを作成します。

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155256/28ff5d31-135f-9c1c-eafe-6fb212cabbb2.png)
    
   リダイレクトURIには、後ほどGoogle Apps Scriptで使用するコールバックURL``urn:ietf:wg:oauth:2.0:oob``を(https://script.google.com/macros/d/{GAS_SCRIPT_ID}/usercallback)に書き換えます。
    {GAS_SCRIPT_ID} はGASで作成するプロジェクトのIDに差し替えます。

- API権限を選択し、「[会計] 取引先」など、必要な権限を指定します。

     ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155256/fa1aa79b-5149-b461-41ed-94b436223c00.png)

- アプリが作成されると、クライアントID（CLIENT_ID）とクライアントシークレット（CLIENT_SECRET）が発行されます。これらの情報は後でGoogle Apps Scriptで使用します。

### ステップ2: GASプロジェクトでFreee APIにアクセスするためのOAuth設定

Freee APIを使ってデータにアクセスするためには、OAuth 2.0を使用して認証を行う必要があります。

1. OAuth 2.0ライブラリの追加

    まず、OAuth2.0ライブラリを利用してFreee APIに認証を行う部分を実装します。GASでは、OAuth2を簡単に扱えるライブラリが提供されています。

    Google Apps Scriptエディタで、ライブラリから「OAuth2」を検索して追加します。
    スクリプトIDに次のID（``1B7FSrk5Zi6L1rSxxTDgDEUsPzlukDsi4KGuTMorsTQHhGBzBkMun4iDF``）を入力して「追加」をクリックします。
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155256/45089e7b-acde-0dde-eafe-719953ddca84.png)

1. 認証フローの設定

    次に、認証を行うためのコードを設定します。
    
    ```javascript
    const CLIENT_ID = '' // Freee APIのクライアントID
    const CLIENT_SECRET = '' // Freee APIのクライアントシークレット
    const COMPANY_ID= '' // 事業番号
    
    function authorize() {
      const service = getOAuthService();
      if (!service.hasAccess()) {
        const authorizationUrl = service.getAuthorizationUrl();
        Logger.log('次のURLにアクセスして認証を行ってください: %s', authorizationUrl);
      } else {
        Logger.log('認証済みです。');
      }
    }
    
    function getOAuthService() {
      return OAuth2.createService('freee')
        .setAuthorizationBaseUrl('https://accounts.secure.freee.co.jp/public_api/authorize')
        .setTokenUrl('https://accounts.secure.freee.co.jp/public_api/token')
        .setClientId(CLIENT_ID)
        .setClientSecret(CLIENT_SECRET)
        .setScope('read companies:read')
        .setCallbackFunction('authCallback')
        .setPropertyStore(PropertiesService.getUserProperties())
    }
    
    function authCallback(request) {
      const service = getOAuthService();
      const isAuthorized = service.handleCallback(request);
      if (isAuthorized) {
        return HtmlService.createHtmlOutput('認証に成功しました！');
      } else {
        return HtmlService.createHtmlOutput('認証に失敗しました...');
      }
    }
    
    ```
    
1. Freeeへの認証を行う
    
    - すべてのコードが準備できたら、authorizeを実行する関数を実行する
    
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155256/8b14df81-bf7b-210d-c43e-50a02e6b36d1.png)
    
    - ブラウザで、下記のURLをクリックし、WEB認証URLを許可する

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155256/06312f13-84fd-e593-b719-af492fa4acef.png)
    
### ステップ3: Freee APIから取引先に関する取引一覧を取得

認証が完了したら、Freee APIを使って取引先一覧を取得します。

1. 取引先を取得するためのAPI呼び出し
    Freeeの取引データを取得するAPIエンドポイントは以下のとおりです。

    エンドポイント: https://api.freee.co.jp/api/1/partners
    以下のコードでは、取引一覧を取得し、取引名のみをフィルタリングしています。
    ```javascript
    function getPartners() {
      const service = getOAuthService();
      if (!service.hasAccess()) {
        Logger.log('認証が必要です。');
        return;
      }
    
      // テストのため、一ヶ月のみ取得
      const period = "&start_issue_date=2024-06-01&end_due_date=2024-06-30";
    
      const url = 'https://api.freee.co.jp/api/1/partners?company_id=' + COMPANY_ID + `&${period}`
      const headers = {
        Authorization: 'Bearer ' + service.getAccessToken()
      };
    
      const options = {
        method: 'get',
        headers: headers,
        muteHttpExceptions: true
      };
    
      const response = UrlFetchApp.fetch(url, options);
      const data = JSON.parse(response.getContentText());
    
      // partner_name のみを取得
      const partners = data.partners.map(partner => partner.name).filter(Boolean);
      // 重複を排除
      const uniquePartners = [...new Set(partners)];
      return uniquePartners;
    }
    ```

1. 取得した取引先をスプレッドシートに出力

    取得した取引先をGoogleスプレッドシートに出力します。以下のコードを追加して、取引データをスプレッドシートに書き出します。
    
    ```javascript
    function outputPartnersToSheet() {
      const partners = getPartners();
      const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
      // 既存データをクリア
      sheet.clear(); 
    
      // ヘッダーを書き込み
      sheet.appendRow(['取引先名']);
    
      // 取引先名をスプレッドシートに出力
      partners.forEach(partner => {
        sheet.appendRow([partner]);
      });
    }
    ```

### ステップ3: 実行と確認

すべてのコードが準備できたら、outputPartnersToSheet関数を実行して、取得した取引先をスプレッドシートに書き出します。スプレッドシートには、取引先が表示されます。

### 最後

この記事では、Google Apps Script (GAS) を使用してFreee会計APIから取引先一覧を取得する方法を紹介しました。GASとFreee APIの組み合わせにより、スプレッドシート上でデータを簡単に操作できるため、業務の効率化に大きく貢献できます。


### 参照

- [Freee API仕様](https://developer.freee.co.jp/reference/accounting/reference)
- [Freee Partners 取引先仕様 ](https://developer.freee.co.jp/reference/accounting/reference#operations-tag-Partners
) 

### サンプルコード
サンプルコードですので、ご使用際に、調整お願いします。

```javascript
    
    const CLIENT_ID = '' // Freee APIのクライアントID
    const CLIENT_SECRET = '' // Freee APIのクライアントシークレット
    const COMPANY_ID= ''
    
    
    function authorize() {
      const service = getOAuthService();
      if (!service.hasAccess()) {
        const authorizationUrl = service.getAuthorizationUrl();
        Logger.log('次のURLにアクセスして認証を行ってください: %s', authorizationUrl);
      } else {
        Logger.log('認証済みです。');
      }
    }
    
    function getOAuthService() {
      return OAuth2.createService('freee')
        .setAuthorizationBaseUrl('https://accounts.secure.freee.co.jp/public_api/authorize')
        .setTokenUrl('https://accounts.secure.freee.co.jp/public_api/token')
        .setClientId(CLIENT_ID)
        .setClientSecret(CLIENT_SECRET)
        .setScope('read companies:read')
        .setCallbackFunction('authCallback')
        .setPropertyStore(PropertiesService.getUserProperties())
    }
    
    function authCallback(request) {
      const service = getOAuthService();
      const isAuthorized = service.handleCallback(request);
      if (isAuthorized) {
        return HtmlService.createHtmlOutput('認証に成功しました！');
      } else {
        return HtmlService.createHtmlOutput('認証に失敗しました...');
      }
    }
    
    function outputPartnersToSheet() {
      const partners = getPartners();
      const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
      // 既存データをクリア
      sheet.clear(); 
    
      // ヘッダーを書き込み
      sheet.appendRow(['取引先名']);
    
      // 取引先名をスプレッドシートに出力
      partners.forEach(partner => {
        sheet.appendRow([partner]);
      });
    }
    
    function getPartners() {
      const service = getOAuthService();
      if (!service.hasAccess()) {
        Logger.log('認証が必要です。');
        return;
      }
    
      // テストのため、一ヶ月のみ取得
      const period = "&start_issue_date=2024-06-01&end_due_date=2024-06-30";
    
      const url = 'https://api.freee.co.jp/api/1/partners?company_id=' + COMPANY_ID + `&${period}`
      const headers = {
        Authorization: 'Bearer ' + service.getAccessToken()
      };
    
      const options = {
        method: 'get',
        headers: headers,
        muteHttpExceptions: true
      };
    
      const response = UrlFetchApp.fetch(url, options);
      const data = JSON.parse(response.getContentText());
    
      // partner_name のみを取得
      const partners = data.partners.map(partner => partner.name).filter(Boolean);
      // 重複を排除
      const uniquePartners = [...new Set(partners)];
      return uniquePartners;
    }
    
```