freee APIを使ってやりたいこと１

#freeeの本番環境から開発環境に「部門」のレコードを簡単にコピーしたい

先ほどまではSalesforceでfreee APIを使う準備でした。
弊社ではfreee for Salesforceを使っています。
本当にやりたかったことは、まずはSalesforceとの連携テストのための環境整備です。

freeeの本番環境には経理の方が、部門を設定してくれています。
同じようにfreeeの開発環境にも打ち込めばいいだけですが、楽をしたい。
また開発環境を作り直す時に毎回入力するのは避けたいところです。

ということで、
1.Salesforceの画面からfreeeの本番環境にある部門を呼び出して、一覧表示する。
2.一覧表したものを選択して、追加ボタンを押すとfreeeの開発環境に、インサートする。
　複数回ボタンを押しても同じ部門が増えないようにアップサーとしたいところですが
APIのリファレンスを読んでみるとSalesforceのupsertはなさそう。

ここでは、既に登録されていたら(部門Idで検索して存在したら）、部門を更新、
無かったら新規作成という処理を実行するようにします。

画面のイメージは以下。
使い方に慣れていないので、Jsonの中身を確認できるようにしておきます。
(注意：この画面はSalesforceの標準機能ではありません。）

検索ボタンを押す前の画面
![freee連携画面.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328136/e845accd-73ac-d0d3-ff8f-817b89c717ac.png)

検索ボタンを押した結果の画面
![freee連携画面2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328136/4bc6640c-662c-7028-b0ba-b9e387c0f928.png)



#Salesforce側の処理の検討
一覧リストが表示され、その行にある追加ボタンが押されたら、そのレコードをfreee APIを使って
freeeの開発環境に新規登録あるいは更新するようしようと思います。
この処理を楽にするために、freee APIを使う時にはfreee本番環境の部門Idと部門Idと部門データすべての
Map型変数を渡すこととします。

ApexではこのMap型変数を部門Idで呼び出せば簡単にfreee本番環境のレコードにアクセスできます。
(Salesforce上のオブジェクトにデータがないので、この方法で1件のレコードの取得を簡単に行えるようにします）

部門の一覧を取得する時にList型変数でなく、わざわざMap型変数にしたのはこのためです。

```
        Map<Integer,String> SectionMap = new Map<Integer,String>();
        for (section s: sectionList) {        
            SectionMap.put(s.id,JSON.serialize(s));//ここのことです。            
        }
```

さて、Salesforce側の部門一覧リストは以下のように定義します。（cmpの抜粋）

今回は複雑な一覧でないのでlightning:datatableを使わずに、tableタグで実装します。

```
                        <div class="slds-size_12-of-12 fkd-button">
                            <div class="slds-m-around_xx-small">
                                
                                <div style="width:100%;">
                                    <div class="slds-scrollable" style="height:100%;">
                                        <table class="slds-table--bordered                                                                    
                                                      slds-table_cell-buffer" style="table-layout:fixed;">
                                            <thead> 
                                                <tr>
                                                    <th style="width: 80px;">id</th>
                                                    <th style="width: 80px;">company_id</th>
                                                    <th style="width: 100px;">name</th>
                                                    <th style="width: 100px;">long_name</th>
                                                    <th style="width: 100px;">shortcut1</th>
                                                    <th style="width: 100px;">shortcut2</th>
                                                    <th style="width: 100px;">indent_count</th>
                                                    <th style="width: 100px;">parent_id</th>
                                                    <th style="width: 20px;"></th>
                                                </tr>
                                            </thead>
                                            <tbody>
                                                <aura:iteration items="{!v.sections}" var="detail" >
                                                    <tr>
                                                        <td style="width: 80px;">{!detail.id}</td>
                                                        <td style="width: 80px;">{!detail.company_id}</td>
                                                        <td style="width: 100px;">{!detail.name}</td>
                                                        <td style="width: 100px;">{!detail.long_name}</td>
                                                        <td style="width: 100px;">{!detail.shortcut1}</td>
                                                        <td style="width: 100px;">{!detail.shortcut2}</td>
                                                        <td style="width: 100px;">{!detail.indent_count}</td>
                                                        <td style="width: 100px;">{!detail.parent_id}</td>
                                                        <td style="width: 20px;">
                                                            <lightning:buttonIcon iconName="utility:send" variant="bare" 
                                                                                  alternativeText="追加" iconClass="dark" 
                                                                                  title="追加" onclick="{!c.handleClick}" value="{!detail.id}" />
                                                        </td>
                                                    </tr>
                                                </aura:iteration>
                                            </tbody>
                                        </table>
                                    </div>
                                </div> 

注:fkd-buttonはlightning:buttonIconの表示位置を調整するためのカスタムcss

```

追加ボタンが押された時に呼び出されるコントローラは以下のようにします。

```
    handleClick : function (component, event, helper) {
        var sendId = event.getSource().get("v.value");//追加ボタンが押された部門のId
        var SectionMap = component.get("v.SectionMap");//部門が格納されているMap型変数
        var action = component.get("c.Sections2");//Apexの関数
        action.setParams({"SectionMap":SectionMap,
                          "id" :sendId
                         });    
        action.setCallback(this, function(response){            
            if (action.getState() == "SUCCESS") {    
                var myVal = response.getReturnValue();
                //component.set("v.oTextarea2_1",myVal);
            }  else if (action.getState() == "ERROR" ) {
                var errors = response.getError();                
                if (errors[0] && errors[0].message) {
                    // サーバーサイドでcatchできなかったパターン
                    component.set("v.message", errors[0].message);
                } 
            }
        });
        $A.enqueueAction(action);        
    },

```

Apex側の処理は以下です。ここでfreee APIを使います。

```
    public virtual class BaseException extends Exception {}
    public class OtherException extends BaseException {}

    /**
     * freeeの部門を登録main処理
     */
    @AuraEnabled
    public static String Sections2(Map<Integer,String> SectionMap,Integer id) {
        String myRet = '';        
        section s = (section)JSON.deserializeStrict(SectionMap.get(id),section.class);
        Integer companyId = Integer.valueOf(getfreeeCompanyId());//freee開発環境の会社Idのセット
        Boolean isExsist = isfree_section(companyId,id);
        if (isExsist == true) {
            //既に存在している-->更新処理
        } else {
            //存在していない-->追加処理
            myRet = registSection(companyId,s);
        }
        
        return myRet;
    }

    //存在確認（既にfreee開発環境に登録されていないか）
    private static Boolean isfree_section(Integer companyId, Integer sectionId) {
        Http http = new Http();
        String path = 'callout:freeeAPI/sections/' + sectionId + '';
        String parameters = 'company_id=' + companyId + '&';
      
        HttpRequest req = new HttpRequest();
        req.setEndpoint(path + '?' + parameters);
        req.setMethod('GET');

        HttpResponse res = http.send(req);
        switch on res.getStatusCode() {        
            when 200 {
                // 部門情報が存在すればtrue、存在しなければfalseを返す
                Map<String, Object> mapBody = (Map<String, Object>)JSON.deserializeUntyped(res.getBody());
                List<Object> lstBody = (List<Object>)mapBody.get('section');
                if (lstBody.size() == 0) {
                    return false;
                }
                else {
                    return true;
                }
            }
            when 404 {
                //指定したidがない時
                return false;
            }            
            when else {
                // エラーだったら例外をスローする
                throw new OtherException(getApiErrorMessage(res.getBody()));//                
            }    
        }//end of switch on
    }

    /**
     * 部門をFreee に登録する 
     */    
    private static String registSection(Integer companyId, section mySection) {
        String myRet = '';
        Http http = new Http();
        String path = 'callout:freeeAPI/sections';        
        
        mySection.company_id = companyId;     
        Integer null_int;//直接代入できないので一旦nullの変数を作って使う。
        mySection.id = null_int;//idが変わっているので、入力しない
        mySection.parent_id = null_int;//idが変わっているので、入力しない
        mySection.indent_count = null_int;        
        String section_json = JSON.serialize(mySection,true);
        
        HttpRequest req = new HttpRequest();
        req.setEndpoint(path);
        req.setMethod('POST');
        req.setHeader('Content-Type', 'application/json');
        req.setBody(section_json);

        HttpResponse res = http.send(req);
        if (res.getStatusCode() != 201) {
            myRet = res.getBody();
            throw new OtherException(res.getBody());
        } else {
            //登録成功すると、結果のデータが返ってくる
            //section_get section_get = (section_get)JSON.deserializeStrict(res.getBody(),section_get.class);//
            //section mySection2 = (section)section_get.section;
            //system.debug(Logginglevel.INFO,'========= mySection2 =======> ' + mySection2);
            myRet = '登録処理完了';
        }
        
        return myRet;
    }
```

#課題について

freeeの開発環境に部門が既に存在しているかは、Idが一致しているかで行っている。
部門Idを指定して１件呼び出すAPIを利用しているため。
そのため、本番環境のIdの順番でデータを追加していかないときちんと部門を作ることはできない。
また、部門の階層構造は考慮していないので、親子関係はfreee開発環境でハンドで設定する必要があります。

何故か？
事前に作ってもらっていたfreee本番環境の部門が先に親部門から作らていなかったので
親を見つけて登録するのが面倒だったので、あきらめて手動で登録しています。
（単純に部門Idの昇順に並べて処理しているためです。この辺りに改善の余地があります）

#aura component側から見るとfreee APIからなのかSalesfroceのオブジェクトからのデータなのかは関係ない
一番いいのは、データの入手先に関わらず、aura componentの画面からは同じであるということです。
ApexでList型変数や、Map型変数にしてしまえば、いつもと同じように画面を作ることができます。
それも、PCだけでなく、スマートデバイスにも簡単に対応できます。

今回freee APIとの連携をしてみて思ったのですが、Json フォーマットのデータを返すAPIをうまくつくれば
既存のシステムのデータを使って、SalesforceのUIを利用することも可能かと思います。