【kintone】で請求書を作ってみる
宛名ラベルを作ってみた時のようにHTMLで出力してみる。
ブラウザによってはレイアウトがずれるが、この方法なら比較的簡単に色々な帳票を発行できそう。
ライブラリ
- Handsontable(v6.2.2)
- Underscore.js(v1.9.1)
HTML
アプリの設定→一覧のHTML部分。
<div> <input id="all" type="checkbox">全選択/全解除 </input> <input type="button" id="Abutton"></input> </div> <div id="sheet"></div> <textarea style="display: none;" id="newWin"> <html lang="ja"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>請求書</title> <style type="text/css"> @page { margin: 0 } body { margin: 0 } .sheet { margin: 0; overflow: hidden; position: relative; box-sizing: border-box; page-break-after: always; } /** Paper sizes **/ body.A4 .sheet { width: 210mm; height: 296mm} /** Padding area **/ .sheet.padding-10mm { padding: 10mm } .sheet.padding-15mm { padding: 15mm } .sheet.padding-20mm { padding: 20mm } .sheet.padding-25mm { padding: 25mm } /** For screen preview **/ @media screen { body { background: #e0e0e0} .sheet { background: white; box-shadow: 0 .5mm 2mm rgba(0,0,0,.3); margin: 5mm; } } /** Fix for Chrome issue #273306 **/ @media print { body.A4 { width: 210mm } } /** 請求書CSS省略 **/ </style> </head> <body class="A4" id="out-def"> <!-- テンプレート外部定義--> <script type="text/template" id="myTemplate"> <% _.each(datalist,function(data){ %> <section class="sheet" id="originSheet"> <div class="print"> <div class="title">御請求書</div> <div class="row_1">請求日 <%= data.senddate %> </div> <div class="row_2"> <div class="customer"> <h2><%= data.customer %> 御中</h2> </div> <div class="address"> <h3>○○○株式会社</h3> <ul> <li>〒000-0000 <li>○○県○○市○○○0-0-0 <li>TEL 00-0000-0000 <li>FAX 00-0000-0000 </ul> </div> </div> <p>下記の通り、ご請求申し上げます。<p> <table class="total" border="1"> <tr> <td style="text-align:center;">合計金額</td> <td style="text-align:right;"><%= data.total %>円</td> </tr> </table> <div> <table class="detail" border="1"> <tr><th>内容</th><th>単価</th><th>数量</th><th>金額</th></tr> <%= data.detail %> <tr> <td rowspan="3" colspan="2"></td> <th> 小計 </th><td><%= data.subtotal %></td> </tr> <tr> <th> 消費税 </th><td><%= data.tax %></td> </tr> <tr> <th> 合計 </th><td><%= data.total %></td> </tr> </table> </div> <ul> <li>振込先</li> <li>名義:カ)○○○</li> <li>〇〇銀行 〇〇支店 普通 00000000</li> </ul> <p>※お振込み手数料は御社ご負担にてお願い致します。</p> </div> </div> </section> <% }); %> </script> </body> </html> </textarea>
JavaScript
(function() { "use strict"; let hot; let getRecords = function(callback, errorCallback) { let query = kintone.app.getQuery(); kintone.api('/k/v1/records', 'GET', {app: kintone.app.getId(), query: query}, function(resp) { let myData = {}; myData.records = []; for(let i = 0; i < resp.records.length; i ++) { let record = resp.records[i]; record["リンク"] = '<a href="/k/' + kintone.app.getId() + '/show#record=' + record.$id.value + '&mode=edit">詳細</a>'; myData.records.push(record); } callback(myData); }, function(resp) { errorCallback(resp); } ); }; kintone.events.on(['app.record.index.show'], function(event) { if (event.viewId !== 一覧ID) return; let container = document.getElementById('sheet'); let setting = []; setting = { data: [], colHeaders: ['選択','詳細','顧客名','請求日','合計'], columns: [ {data: "check", type:'checkbox'}, {data: "リンク", renderer: 'html', readOnly: true}, {data: "顧客名.value", type: 'text', readOnly: true}, {data: "請求日.value", type: 'date', dateFormat: 'YYYY-MM-DD', readOnly: true}, {data: "合計.value", type: 'numeric', numericFormat: { pattern: '0,00', culture: 'ja-JP' }, readOnly: true}, ], manualColumnResize: true, fixedRowsTop: 0 }; hot = new Handsontable(container, setting); getRecords(function(resp){ hot.loadData(resp.records); }); let newWin = document.getElementById("newWin").value; function show(datalist){ let obj = window.open(); obj.document.open(); obj.document.write(newWin); obj.document.close(); // underscore.js のテンプレート機能でデータを流し込む let template = obj.document.getElementById('myTemplate').textContent; let compiled = _.template(template); let out_def = obj.document.getElementById('out-def'); out_def.innerHTML = compiled({"datalist":datalist}); obj; } function create(record){ let sendDate = record['請求日'].value.split('-'); let subTable = record.Table.value; let detail = ''; let total = 0; for(let i =0; i < subTable.length; i ++){ detail += '<tr><td>' + subTable[i].value['品名'].value + '</td><td>' + parseInt(subTable[i].value['単価'].value).toLocaleString() + '</td><td>' + subTable[i].value['数量'].value + '</td><td>' + parseInt(subTable[i].value['金額'].value).toLocaleString() + '</td></tr>'; } for(let i =0; i < 11 - subTable.length; i ++){ detail += '<tr><td></td><td></td><td></td><td></td></tr>'; } let list = { "customer": record['顧客名'].value, "senddate": sendDate[0] + '年' + sendDate[1] + '月' + sendDate[2] + '日', "total": parseInt(record['合計'].value).toLocaleString(), "detail": detail, "subtotal":parseInt(record['小計'].value).toLocaleString(), "tax":parseInt(record['消費税'].value).toLocaleString(), }; return list; } // 全選択/全削除チェック処理 document.getElementById('all').onchange = function (check) { let col = hot.propToCol('check'); hot.populateFromArray(0, col, [[check.target.checked]], hot.countRows() - 1, col, null, null, 'down'); }; //発行ボタン let button = document.getElementById("Abutton"); button.value = "発行"; button.addEventListener("click", function() { let records = hot.getSourceData(); let checkedRows = []; for(let i = 0;i < records.length;i++){ if(records[i].check){ checkedRows.push(records[i]); } } let datalist = []; for(let i = 0; i < checkedRows.length; i++){ let data = create(checkedRows[i]); datalist.push(data); } show(datalist); }); }); })();
別アプリにファイルを保存しておけばロゴもつけられそう。