こんにちは、もりです!
クラウド会計ソフトfreeeのAPIを使って、freeeの操作を自動化するシリーズです。
合計2回の連載記事で、先月分の請求書を一括コピーして日付変更する「請求書一括作成ツール」を紹介します。前回の記事はこちら。
前回の記事では、スプレッドシートに請求書データを出力するスクリプトを作りました。
このスプレッドシートのデータを元にして、freeeAPI経由で新規の請求書を作成します。
- freeeに作成済みの請求書データをスプレッドシートに取得する【API使用】←前回作成
- スプレッドシート上で「請求書の日付」を書き換える【手作業】
- スプレッドシートのデータをfreeeに送信して請求書を作成する【API使用】←今回はココを作る
Contents
freee請求書一括作成ツールとは
freeeに作成済みの請求書データをスプレッドシートに取得し、その日付を書き換えて、freeeに送信します。
操作イメージをご覧ください(1分25秒)※再生しても音は出ません
スプレッドシートのデータをfreeeに送信して請求書を作成する
すぐに使えるツールを無料で配布してます。ご希望の方はこちら(記事の末尾に移動)をご覧ください。今お使いのスプレッドシートに組み込みたい、という方は、次項の手順に沿って作ってみてください。
【事前準備】コピーする請求書データの日付を変更する
freeeAPIからGETリクエストで取得した請求書データを使用して、新規の請求書を作成します。請求書を一から新しく作るのではなく、既存の請求書をコピーするイメージです。
「請求書ID = xxxxxxx の請求書をコピーして、この日付に書き換えてね」という処理をします。
スプレッドシートのA列で「コピー対象」の請求書を選択し、請求日・期日・売上計上日の3つの日付を書き換えます。
※チェックボックスは、スプレッドシートの「挿入」タブ→「チェックボックス」で作成しています
【処理の流れ】freeeに作成済みの請求書をコピーする
「freeeに作成済みの請求書ID = xxxxxxx の請求書をコピーして、この日付に書き換えてね」の処理は、下記①~④の流れで作ります。
1件の請求書を作るために、リクエストを2回送信します(①GETリクエストと④POSTリクエスト)
【共通処理】取得(GET)と作成(POST)の共通処理
請求書の「取得」と「作成」で共通して使用する変数・関数です。GASのプロジェクト内に記述しておきましょう(前回記事でも紹介済み)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
const sheetname = '請求書'; //シート名を設定 const companycell = 'L2'; //事業所名を設定するセル番地 const invoice_col = { copy: 1, id: 2, issue_date: 3, due_date: 4, booking_date: 5, end: 8, param: 12, } /** * 指定のURLにリクエストを送信してレスポンスを返す * * @param {string} アクセストークン * @param {string} リクエストURL * @return {object} freeeAPIのレスポンス */ function accessfreeeAPI_(accessToken,url) { Utilities.sleep(1000); //短時間の連続アクセスを回避するため待機 const params = { method : 'get', headers : {'Authorization':'Bearer ' + accessToken} }; const response = UrlFetchApp.fetch(url,params); const obj = JSON.parse(response); return obj; } /** * 事業所名から事業所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; } } } |
【スクリプト】freeeに作成済みの請求書をコピーして新規作成する
下記スクリプトの「createInvoices」関数を実行すると、スプレッドシートのA列にチェックの入っている請求書をコピーして新規作成します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 |
//エントリポイント function createInvoices() { const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetname); //請求書IDの最終列を取得 //参考:https://moripro.net/gas-get-specified-lastcol-lastrow/ const lastRow = sheet.getRange('B1') .getNextDataCell(SpreadsheetApp.Direction.DOWN) .getRow(); const company_name = sheet.getRange(companycell).getValue(); const company_id = getCompanyId_(company_name); const values = sheet.getRange(2, 1, lastRow-1, 5).getValues(); const accessToken = getService().getAccessToken(); //請求書の件数分、処理を繰り返す for (let i = 0; i < values.length; i++) { let isCopy = values[i][0]; if (!isCopy) continue; //コピー対象でない場合スキップ let invoice_id = values[i][1]; //請求書ID let issue_date = yyyyMMdd_(values[i][2]); //請求日 let due_date = yyyyMMdd_(values[i][3]); //期日 let booking_date = yyyyMMdd_(values[i][4]); //売上計上日 let arrDate = [issue_date, due_date, booking_date]; //テンプレートリテラルで記述 let requestUrl = `https://api.freee.co.jp/api/1/invoices/${invoice_id}?company_id=${company_id}`; let obj = accessfreeeAPI_(accessToken,requestUrl); //GETした請求書のJSONデータを、POST用に変換する let objInvoice = convertObjInvoice_(obj,arrDate); //請求書作成 postInvoice_(accessToken, objInvoice); } } /** * 日付オブジェクトをYYYY-MM-DD形式に整形する * * @param {date} 日付オブジェクト * @param {string} 文字例(YYYY-MM-DD) */ function yyyyMMdd_(date) { if (date) { //dateが空(未入力)の場合もあり return Utilities.formatDate(date,'JST','yyyy-MM-dd'); } else { return ''; } } /** * freeeAPIからGETしたJSONデータをPOST用に変換する * * @param {object} freeeAPIからGETしたJSONデータ * @param {object} POST用に変換したJSONデータ */ function convertObjInvoice_(obj,arrDate) { let objInvoice = obj.invoice; //ステータスを下書きに戻す objInvoice.invoice_status = 'draft'; //請求書の日付をスプレッドシートの値に変更する objInvoice.issue_date = arrDate[0]; objInvoice.due_date = arrDate[1]; objInvoice.booking_date = arrDate[2]; //請求書番号は重複不可のため、API経由で登録せず自動採番とする delete objInvoice['invoice_number']; //値無しのキーを削除 for (const key in objInvoice) { if (objInvoice[key] == null || objInvoice[key] === '') { delete objInvoice[key]; } } //invoice_contentsプロパティの処理 const length = objInvoice.invoice_contents.length; /* [memo] GETリクエストで取得したデータでは「割引行」「テキスト行」は数量(qty) 0 だが、 POSTリクエストでは、qtyに 1 以上の数値指定が必須でエラーとなってしまうため、 「割引行」「テキスト行」は行ごと削除する */ //配列の要素を削除するため、逆向きループ for (let i = length-1; i >= 0; i--){ //「通常行」以外は「行ごと」削除してスキップ if (objInvoice.invoice_contents[i].type !== 'normal') { objInvoice.invoice_contents.splice(i, 1); continue; } //以下、「通常行」の処理 let objContent = objInvoice.invoice_contents[i]; for (const key in objContent) { //値無しのキーを削除 if (objContent[key] == null || objContent[key] === '') { delete objInvoice.invoice_contents[i][key]; } //空配列を削除 if (typeof objContent[key] === 'object' && !objContent[key].length) { delete objInvoice.invoice_contents[i][key]; } } } return objInvoice; } /** * freeeAPIにリクエストを送信して請求書を作成する * * @param {string} freeeAPIアクセストークン * @param {object} 請求書データ */ function postInvoice_(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/invoices'; const response = UrlFetchApp.fetch(requestUrl,params); Logger.log(response); } |
【実行結果】
スクリプトの実行結果です。スプレッドシート上で「コピー対象」に指定した行の請求書データが一括コピーされ、新規の請求書【NEW】ができているのが確認できます(ここでは5件の請求書を一括コピー)
【注意点】コピーされる行は「通常行」のみ
このツールの仕様では、請求書の各明細行のうち、「通常」の行のみをコピー対象としており、割引・テキスト・源泉税はコピーされません。
freeeに作成済みの請求書のデータを「請求書ID = xxxxxxx」と条件指定してGETリクエストで取得したJSONデータでは、数量(qty)のプロパティに ‘0’ が設定されていますが、POSTリクエストではqtyプロパティに 1 以上の数値指定が必要であり、エラーとなってしまうため、「通常行」以外はPOSTリクエストの対象としていません。
独自メニューを追加するスクリプト
スクリプトエディタを開かずに、スプレッドシートからスクリプトを実行できるよう「独自メニュー」を追加しておくとよいでしょう。
1 2 3 4 5 6 7 8 9 10 11 |
function onOpen(){ const ui = SpreadsheetApp.getUi(); const menu = ui.createMenu('freeeメニュー'); menu.addItem('請求書の取得', 'exportInvoices'); menu.addItem('請求書の作成', 'createInvoices'); menu.addToUi(); } |
【無料】スプレッドシートを配布してます
すぐに使えるスプレッドシート【スクリプト付き】を配布しています。下記のリンクをクリックすると、スプレッドシートが開きます。権限設定は「閲覧のみ」です。コピーしてご利用ください。
[shared]freee請求書一括作成ツール(クリックするとスプレッドシートが開きます)
※連携アプリの作成、認証はご自身で行っていただく必要があります。手順は下記記事をご覧ください。