この記事ではGASからfreeeAPIを使うことで、スプレッドシートで管理しているデータをfreeeに直接、データを送信や登録をする方法を紹介します。
目次
事前準備
- freeeのアカウントを保有している必要があります。
- 他のユーザーが使用する際も実行前にfreee画面での認証作業が必要です。
(認証ページへ遷移するメニューを作成します) - スプレットシートの作成。
freee APIのアクセストークンを取得する
アプリに対してhttpサービスへのアクセスを付与するフレームワーク(OAuth2)を使用し、アクセストークンを用いて保護されたリソースにアクセスします。

freeeの公式ヘルプセンターページの「freee APIのアクセストークンを取得する」を参考に、アクセストークンを取得する設定を行います。参照:freee APIのアクセストークンを取得する
連携アプリの作成
①スクリプトエディタで「設定」 →「 プロジェクトの設定」内の「スクリプトID」で確認します。

②freeeアプリストアへアクセスし、ログイン後、右上の「アプリ管理」をクリックします。(freeeAPI経由でデータサーバーにアクセスするための「クライアントアプリ」を作成します。)

③「開発アプリ一覧」の「新規追加」をクリックします。
④アプリ名・概要を入力し、利用規約を読んで同意にチェックし、「作成」をクリックします。

これは、freeeAPIにアクセスするためのIDとパスワードのようなものです。
⑤コールバックURLの登録
連携アプリに「コールバックURL」(連携完了後の遷移先URL)を設定します。

スクリプトIDの部分に、「スクリプトID」をあてはめ、「下書き保存」をクリックすればコールバックの設定は完了です。
freeeAPI連携アプリ(OAuth)を認証する
スクリプトエディタを開き、「ライブラリを追加」を選択して、認証用ライブラリ(OAuth2)を追加します。

ライブラリのプロジェクトキー1B7FSrk5Zi6L1rSxxTDgDEUsPzlukDsi4KGuTMorsTQHhGBzBkMun4iDF
GoogleAppsScriptでfreeeAPIと連携認証する
①作業をしやすくするため、スプレットシートにメニューを作成します。
/**
* スプレッドシートに独自メニューを作成する
*/
function onOpen(){
const ui = SpreadsheetApp.getUi()
const menu = ui.createMenu('freeeAPIメニュー')
menu.addItem('認証', 'alertAuth');
menu.addItem('見積書の作成', 'procAdd');
menu.addItem('取引先の登録', 'PartnerAdd');
menu.addToUi();
}
②先ほど作成した連携アプリの「Client_ID」と「Client_Secret」をスクリプトエディタに貼り付けて、alertAuth関数を実行します。
//連携アプリ情報
const Client_ID = '**************'//Client IDを設定;
const Client_Secret = '*************'//'Client Secretを設定'
//スクリプトへのアクセスを許可する認証URLを取得
function alertAuth() {
const authUrl = getService().getAuthorizationUrl(); //認証URL
var ui = SpreadsheetApp.getUi();
Logger.log(authorizationUrl);//認証用URLがログ出力される
//ダイアログのボタンから認証URLに遷移するように設定
var dialogHtml = HtmlService.createTemplateFromFile('Authentication');
dialogHtml.text = authUrl;
ui.showModalDialog(dialogHtml.evaluate(), '認証');
}
//freeeAPIのサービスを取得する関数
function getService() {
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)
.setCallbackFunction('authCallback')
.setPropertyStore(PropertiesService.getUserProperties())
}
//認証コールバック関数
function authCallback(request) {
const service = getService();
const isAuthorized = service.handleCallback(request);
if (isAuthorized) {
return HtmlService.createHtmlOutput('認証に成功しました。タブを閉じてください。');
} else {
return HtmlService.createHtmlOutput('認証に失敗しました。');
};
}
③認証ページへ遷移するダイアログを作成します。
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<h1></h1>
<div>
<p>下記の認証ページより認証を行ってください</p>
<input type="button" onclick="window.open('<?=text?>')" value="認証">
</div>
</body>
</html>
④作成したスプレットシートのメニューから、認証作業を行います。

⑤ボタンをクリックして認証URLに遷移し、「許可する」をクリックします。

成功メッセージが表示されれば、認証作業は完了です。
クライアントアプリからfreeeのデータサーバにアクセスできるようになります。
事業所ID、事業所名の確認
freeeAPIを使ってデータ取得、送信をするためには「事業所ID」「事業所名」が必要なので、確認するコードを作成します。(下記はログで確認を行っています。)
確認した事業所の情報は後に記述する見積書作成時などのfreeeから情報を取得する際に必要になります。
/**
* 事業所名を返す
* @return {string} 事業所名
*/
function getCompanyName() {
const accessToken = getService().getAccessToken();
const requestUrl = 'https://api.freee.co.jp/api/1/companies';
const obj = accessfreeeAPI_(accessToken,requestUrl);
const objCompanies = obj.companies;
for(var i=0; i<objCompanies.length; i++){
console.log(objCompanies[i].name)
console.log(getCompanyId_(objCompanies[i].name))
}
return objCompanies[0].name
}
/**
* 事業所名から事業所IDを返す
*
* @param {string} 事業所名
* @return {string} 事業所ID
*/
function getCompanyId_(company_name) {
const accessToken = getService().getAccessToken();
const requestUrl = 'https://api.freee.co.jp/api/1/companies';
let obj = accessfreeeAPI_(accessToken,requestUrl);
obj = obj.companies;
for (const i in obj) {
if (obj[i].name === company_name) {
return obj[i].id;
}
}
}
スプレットシートから操作された際の共通処理を作成する
メニュー項目、ダイアログのボタンをクリックされたときの動作を設定します。
下記では、スプレットシートのシート名に書かれた取引先名で登録する「シート名から見積書作成」と、プルダウンから取引先を選択する「取引先を選択して見積書作成」の2パターンで作成できるようにしています。
/**
* スプレットシートのメニューから「見積書作成」が選択された時の処理
*/
function procAdd() {
const sheet = SpreadsheetApp.getActiveSpreadsheet() //アクティブシート
const sheetName = sheet.getSheetName();//シート名
//ダイアログの作成
var ui = SpreadsheetApp.getUi();
var dialogHtml = HtmlService.createTemplateFromFile('CustomDialog');
dialogHtml.text = sheetName;
ui.showModalDialog(dialogHtml.evaluate(), '見積書作成');
}
/**
* スプレットシートのメニューから「取引先の登録」が選択された時の処理
*/
function PartnerAdd() {
const sheet = SpreadsheetApp.getActiveSpreadsheet() //アクティブシート
const sheetName = sheet.getSheetName();//シート名
//ダイアログの作成
var ui2 = SpreadsheetApp.getUi();
var dialogHtml2 = HtmlService.createTemplateFromFile('CreatePartnerDialog');
dialogHtml2.text = sheetName;
ui2.showModalDialog(dialogHtml2.evaluate(), '取引先登録');
}
/**
* ダイアログで「新規作成」ボタンが押された場合の処理
*
* @param inputText 入力されたテキスト
*/
function onOkButtonClick(inputText) {
//見積書作成
var result = createQuotations(inputText)
// ダイアログ表示
if(result != false){
var ui = SpreadsheetApp.getUi();
ui.alert('見積書を作成しました。\n' +'freee画面をご確認ください。');
}
return;
}
/**
* ダイアログで「取引先追加」ボタンが押された場合の処理
*
* @param inputText 入力されたテキスト
*/
function onOkButtonClick2(inputText2) {
//取引先追加
var result = AddPartner(inputText2)
return;
}
見積書作成や取引先登録時の共通の処理(主にfreeeからの情報取得処理)を記述します。
/**
* 指定のURLにリクエストを送信してレスポンスを返す
*
* @param {string} アクセストークン
* @param {string} リクエストURL
* @return {object} freeeAPIのレスポンス
*/
function accessfreeeAPI_(accessToken,url) {
Utilities.sleep(1000); //短時間の連続アクセスを回避するため待機
const params = {
method : 'get',
headers : {'Authorization':'Bearer ' + accessToken},
muteHttpExceptions : true //エラー全文表示
};
const response = UrlFetchApp.fetch(url,params);
const obj = JSON.parse(response);
return obj;
}
/**
* 取引先名から取引先IDを返す
* @parameter{string} 取引先名
* @return {string} 取引先ID
*/
function getPartnerId_(partner_name,company_ID) {
const accessToken = getService().getAccessToken();
const requestUrl = 'https://api.freee.co.jp/api/1/partners?company_id='+company_ID+'&limit=150';
let obj = accessfreeeAPI_(accessToken,requestUrl);
obj = obj.partners
var partner_id = ""
for (const i in obj) {
if (obj[i].name == partner_name || obj[i].long_name == partner_name) {
partner_id = obj[i].id;
break
}
}
return partner_id
}
/**
* 事業所コードから取引先名を返す
* @parameter{string} 事業所コード
* @return {array} 取引先名
*/
function getPartnersName_(company_ID) {
try{
const accessToken = getService().getAccessToken();
const requestUrl = 'https://api.freee.co.jp/api/1/partners?company_id='+company_ID+'&limit=150';
let obj = accessfreeeAPI_(accessToken,requestUrl);
obj = obj.partners;
let array=[]
for (const i in obj) {
array.push(obj[i].name)
}
return array
}catch(exception){
console.log("エラー発生"+exception)
var ui = SpreadsheetApp.getUi();
ui.alert('【ERROR】'+exception);
}
}
/**
* プルダウン用
* 取引先名一覧を返す
*/
function pertner(){
let list = getPartnersName_(company_id)//取引先一覧
return JSON.stringify(list);
}
スプレットシートから見積書を作成する
スプレットシートにフォーマットを作成し、freeeに送信して見積書を新規作成します。
①見積書用にスプレッドシートの準備します

②見積書作成時のPOST作業を記述します。
/**
* 見積書作成
*
* @param partnername 取引先名
*/
function createQuotations(partnername) {
try{
const sheet = SpreadsheetApp.getActiveSpreadsheet() //アクティブシート
const sheetName = sheet.getSheetName();//シート名
const partnerid = getPartnerId_(partnername,company_id)//取引先ID
if(partnerid == ""){
var test = SpreadsheetApp.getUi();
test.alert('['+partnername+']は登録のない取引先です。\n' +'取引先の登録をしてください');
return false
}
var notes = ""//備考欄
//シートの内容を格納
var input_list = sheet.getSheetByName(sheetName)
var input_list_data = input_list.getDataRange().getValues()
//行の種類の変換
for (var i=1; i<= input_list_data.length; i++) {
if(input_list_data[i][5]=='通常'){
input_list_data[i][5]='normal'
}
else if(input_list_data[i][5]=='テキスト'){
input_list_data[i][5]='text'
}
else if(input_list_data[i][5]=='割引'){
input_list_data[i][5]='discount'
}
if(input_list_data[i][0] == '備考欄'){
//備考欄に記入がある場合(記入がない場合はデフォルトの内容を表示)
if(i == input_list_data.length-2){
notes = input_list_data[i+1][0]
}
break
}
}
const accessToken = getService().getAccessToken();
//詳細内容の設定
var contents=[];
for(c=1;c<i;c++){
contents.push(
{id: 0,
order: 0,
type: input_list_data[c][5],
qty: input_list_data[c][1],
unit: input_list_data[c][2],
unit_price: input_list_data[c][3],
amount: input_list_data[c][4],
description: input_list_data[c][0],
account_item_id: account_item_id,
tax_code: 101
}
)
if(c<i-1){
contents.push(",")
}
}
//取引先・事業所の情報設定
let obj = {
company_id: company_id,
partner_id: partnerid,
partner_display_name: partnername,
partner_title: '御中',
notes: notes,
quotation_contents: contents,
Quotation_status: 'draft'
}
//JSONデータを、POST用に変換する
let objQuotations = convertObjQuotations_(obj);
//見積書作成
const respose = postQuotations_(accessToken, objQuotations);
//var ui = SpreadsheetApp.getUi();
//ui.alert(respose);
}catch(exception){
var ui = SpreadsheetApp.getUi();
ui.alert('見積書の作成でエラーが発生しました。\n' +exception.message);
return false;
}
//return true
}
/**
* 日付オブジェクトをYYYY-MM-DD形式に整形
* @param {date} 日付オブジェクト
* @param {string} 文字例(YYYY-MM-DD)
*/
function yyyyMMdd_(date) {
if (date) {
return Utilities.formatDate(date,'JST','yyyy-MM-dd');
} else {
return '';
}
}
/**
* POST用に変換
* @param {object} JSONデータ
* @param {object} POST用に変換したJSONデータ
*/
function convertObjQuotations_(objQuotations) {
//値無しのキーを削除
for (const key in objQuotations) {
if (objQuotations[key] == null || objQuotations[key] === '') {
delete objQuotations[key];
}
}
//Quotation_contentsプロパティの処理
const length = objQuotations.quotation_contents.length;
//配列の要素を削除するため、逆向きループ
for (let i = length-1; i >= 0; i--){
//「通常・テキスト・割引」以外は行ごと削除してスキップ
let type = objQuotations.quotation_contents[i].type
if(type != 'text' && type !== 'normal' && type !== 'discount'){
objQuotations.quotation_contents.splice(i, 1);
continue;
}
let objContent = objQuotations.quotation_contents[i];
for (const key in objContent) {
//値無しのキーを削除
if (objContent[key] == null || objContent[key] === '') {
delete objQuotations.quotation_contents[i][key];
}
//空配列を削除
if (typeof objContent[key] === 'object' && !objContent[key].length) {
delete objQuotations.quotation_contents[i][key];
}
}
}
return objQuotations;
}
/**
* freeeAPIにリクエストを送信して見積書を作成する
*
* @param {string} freeeAPIアクセストークン
* @param {object} 見積書データ
*/
function postQuotations_(accessToken,obj) {
Utilities.sleep(1000); //過度の連続アクセスを避けるため待機
const params = {
method : 'POST',
contentType : 'application/json',
headers : {Authorization:'Bearer ' + accessToken},
payload : JSON.stringify(obj), //オブジェクトをJSON文字列に変換
muteHttpExceptions : true //エラーが発生した場合、レスポンスにエラー全文を出力する
};
const requestUrl = 'https://api.freee.co.jp/api/1/quotations';
const response = UrlFetchApp.fetch(requestUrl,params);
}
③見積書作成用のダイアログを作成します。
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<script type="text/javascript">
//スプレッドシート側からデータを取得する
google.script.run.withSuccessHandler(onSuccess).pertner();
//取得したデータでドロップダウンメニューを作って設置
function onSuccess(data){
var json = JSON.parse(data);
var datalength = json.length;
var html = "<label></label>";
var datalength = json.length;
html += "<select title='プルダウンより選択' id='pertners'><option>取引先一覧</option>";
for(var i = 0;i<datalength;i++){
//空データの場合ループ脱出
if(json[i] == ""){
break;
}
//オプション項目を追加
html += "<option>" + json[i] + "</option>"
}
html += "</select><p>";
//プルダウンメニューを設置する
document.getElementById("pertner").innerHTML = html;
}
</script>
</head>
<body>
<h1></h1>
<div>
<input type='text' id='inputText' disabled="readonly" value=<?=text?>>
<input type='button' value="シート名で見積書作成" onClick='createA()' />
</div>
<div><p> </p></div>
<div><p> </p></div>
<div style="display: inline-block; _display: inline;" id="pertner">
<img border="0" src="https://officeforest.org/wp/library/ProgressSpinner.gif" width="20" height="20">
</div>
<div>
<input type='button' value="取引先を選択して見積書作成" onClick='createB()' />
</div>
<script>
//入力欄に記入された取引先名を書き込み
function createA() {
var elm = document.getElementById('inputText');
google.script.run
.withFailureHandler(onFailure)
.withSuccessHandler(onSuccess)
.onOkButtonClick(elm.value);
}
//プルダウンで選択された取引先名を入力欄に反映
function createB() {
var elm = document.getElementById('pertners');
google.script.run
.withFailureHandler(onFailure)
.withSuccessHandler(onSuccess)
.onOkButtonClick(elm.value);
}
//エラー処理
function onFailure(error) {
alert(error.message);
google.script.host.close();
}
//クローズ処理
function onSuccess() {
google.script.host.close();
}
</script>
</body>
</html>
実行結果

スプレットシートから取引先登録機能の作成する
スプレットシートのメニューから取引先を新規追加するコードを作成します。
/**
* 取引先登録
*
* @param partnername 取引先名
*/
function AddPartner(partnername) {
try{
const accessToken = getService().getAccessToken();
//取引先・事業所の情報設定
let obj = {
company_id: company_id,
name: partnername
}
//取引先の追加
const respose2 = AddPartner_(accessToken, obj);
}catch(exception){
var ui = SpreadsheetApp.getUi();
ui.alert(exception.message);
return false;
}
}
/**
* freeeAPIにリクエストを送信して取引先を登録
*
* @param {string} freeeAPIアクセストークン
* @param {object} 取引先登録データ
*/
function AddPartner_(accessToken,obj) {
Utilities.sleep(1000); //過度の連続アクセスを避けるため待機
const params = {
method : 'POST',
contentType : 'application/json',
headers : {Authorization:'Bearer ' + accessToken},
payload : JSON.stringify(obj), //オブジェクトをJSON文字列に変換
muteHttpExceptions : true //エラーが発生した場合、レスポンスにエラー全文を出力する
};
const requestUrl = 'https://api.freee.co.jp/api/1/partners';
const response = UrlFetchApp.fetch(requestUrl,params);
var message = SpreadsheetApp.getUi();
if (response.toString().match(/error/)) {
message.alert("【エラー】" + response);
}else{
message.alert("取引先が追加されました")
}
}
取引先追加のダイアログを作成します。
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<h1></h1>
<div>
<input type='text' id='inputText2' value=<?=text?>>
<input type='button' value="取引先の新規登録" onClick='createPartner()' />
</div>
<script>
//入力欄に記入された取引先名を登録
function createPartner() {
var elm2 = document.getElementById('inputText2');
google.script.run
.withFailureHandler(onFailure)
.withSuccessHandler(onSuccess)
.onOkButtonClick2(elm2.value);
}
function onFailure(error) {
alert(error.message);
google.script.host.close();
}
function onSuccess() {
google.script.host.close();
}
</script>
</body>
</html>
実行結果

freee画面での確認
以上の作業でスプレットシートから「見積書作成」と「取引先の登録」ができるようになります。

