支払い請求書の作成はあらゆるビジネスにとって重要な側面であり、デジタル請求書発行はさまざまな業界で一般的な慣行となっています。これに関連して、Web アプリケーション開発者は、PDF 請求書をプログラムで生成して送信するというタスクに直面することがよくあります。
請求書の生成と通知プロセスを自動化する場合でも、未処理の請求書についてクライアントに積極的に通知するためのグラフィカル ユーザー インターフェイス (GUI) を開発する場合でも、最初の技術的な課題は PDF 請求書の生成にあります。 PDF 生成用のカスタム スクリプトを開発することもできますが、それはかなりの作業になります。 Web ベースのサービスは便利ですが、インターネット経由でサードパーティのサービスにデータを送信すると懸念が生じる可能性があるため、クライアントと機密保持契約を結んでいる場合には適さない可能性があります。
幸いなことに、Foxit は
このチュートリアルでは、
このチュートリアルでは、請求部門が未払いの請求書を効果的に管理できるようにする Web アプリケーションを構築するプロセスを説明します。フォローアップ用の内部ツール、未処理の請求書を表示するページ、各請求書のプレビュー ページなど、さまざまな機能を作成します。ユーザーは、請求書を添付してクライアントに電子メールによるリマインダーを送信するオプションを利用できます。
このプロジェクトでは、
前提条件:
新しいボイラープレート Express Web アプリケーションを作成するには、アプリ ジェネレーターを使用する必要があります。
npx express-generator --git --view=hbs
これにより、 .gitignore
ファイルを含む Web アプリが作成されます。
次に、Nodemailer npm パッケージを追加し、Express の依存関係をインストールする必要があります。
npm i nodemailer && npm i
Express によって生成されるデフォルト アプリケーションには、 /routes/index.js
および/routes/users.js
という 2 つのルート ファイルが提供されます。 users.js
ルートを削除し、 invoices.js
という新しいルート ファイルを作成します。この新しいルートをapp.js
ファイルに追加し、 usersRoute
を削除します。
... var indexRouter = require('./routes/index'); var invoicesRouter = require('./routes/invoices'); var app = express(); ... app.use('/', indexRouter); app.use('/invoices', invoicesRouter); ...
このアプリケーションの作業の大部分は、請求書ルーター内で行われます。
ルートを作成する前に、使用できるデータが必要になります。実際のアプリケーションでは、データベースに接続することになるでしょうが、デモの目的では、請求書データを JSON ファイルに追加します。
/data/invoices.json
に新しいファイルを作成し、以下を追加します。
[ { "id": "47427759-9362-4f8e-bfe4-2d3733534e83", "customer": "Bins and Sons", "contact_name": "Verne McKim", "contact_email": "[email protected]", "address": "3 Burning Wood Street", "city_state": "Memphis, TN 38118", "plan_id": "41595-5514", "plan_name": "Starter", "subtotal": 499.99, "fee": 50.00, "total": 549.99 }, { "id": "1afdd2fa-6353-437c-a923-e43baac506f4", customer": "Koepp Group", "contact_name": "Junia Pretious", "contact_email": "[email protected]", "address": "7170 Fairfield Hill", "city_state": "Los Angeles, CA 90026", "plan_id": "43419-355", "plan_name": "Professional", "amount": 999.99, "fee": 50.00, "total": 1049.99 }, { "id": "59c216f8-7471-4ec2-a527-ab3641dc49aa", "customer": "Lynch-Bednar", "contact_name": "Evelin Stollenberg", "contact_email": "[email protected]", "address": "9951 Erie Place", "city_state": "Chicago, IL 60605", "plan_id": "63323-714", "plan_name": "Starter", "amount": 499.99, "fee": 50.00, "total": 549.99 } ]
上に表示されている 3 つの請求書には、顧客、プラン、請求データが含まれており、次のセクションで請求書を生成するのに役立ちます。
routes/invoices.js
ファイルは、アプリケーション内に 3 つの新しいルートを作成します。
/invoices
– 上記のフラット データ ファイルからのすべての請求書のリスト。/invoices/:id
– ユーザーがクライアントに送信する前に請求書がどのようになるかを確認できる請求書のプレビュー。/invoices/:id/email
– PDF 請求書を生成し、ファイル上の連絡先メールアドレスに送信するエンドポイント。
invoices.js
ファイルを開き、次のコード スニペットを追加して最初の 2 つのルートを定義します。
const express = require('express'); const router = express.Router(); const invoices = require('../data/invoices.json'); // Import exec to run the Foxit HTML to PDF executable const { exec } = require('child_process'); // Import nodemailer to send emails const nodemailer = require('nodemailer'); router.get('/', function(req, res) { res.render('invoice-list', { invoices: invoices, // Accepts errors and successes as query string arguments success: req.query['success'], error: req.query['error'], }); }); router.get('/:id', function(req, res) { const invoice = invoices.find(invoice => invoice.id === req.params['id']); // If the invoice doesn't exist, redirect the user back to the list page if (!invoice) { res.redirect('/invoices'); } // Make the date format pretty const date = new Date().toLocaleDateString("en", { year:"numeric", day:"2-digit", month:"2-digit", }); res.render('invoice-single', { invoice, date }); }); router.get('/:id/email', function(req, res) { // Coming soon. }); module.exports = router;
アプリケーションはほぼテストの準備が整いましたが、最初に 2 つのビュー ファイルを作成する必要があります。
Express は、ロジックとプレゼンテーションをroutes/
とviews/
に分離します。次に、2 つの新しいファイルinvoice-list.hbs
とinvoice-single.hbs
をviews/
ディレクトリに追加します。
また、 invoice-list.hbs
ファイルに以下を追加します。
<h1><a href="/invoices">Unpaid Invoices</a></h1> {{#if success}} <p class="success"><strong>Success!</strong> The invoice has been sent to the client.</p> {{/if}} {{#if error}} <p class="error"><strong>Whoops!</strong> Something went wrong and your invoice could not be sent.</p> {{/if}} {{#each invoices}} <h3>{{this.customer}}</h3> <p>ID: {{this.id}} <br/> <a href="/invoices/{{this.id}}">View</a> | <a href="/invoices/{{this.id}}/email">Email Reminder</a> </p> {{/each}}
invoice invoice-single.hbs
ファイルを開き、これを追加します。
<div class="pure-g"> <div class="pure-u-1-2"> <h1>Invoice</h1> </div> <div class="pure-u-1-2" style="text-align: right;"> <p class="muted">Issued on {{ date }}</p> </div> </div> <div class="pure-g"> <div class="pure-u-1-2"> <h3>Provider</h3> <p> <strong>Tiller, Inc.</strong><br/> 1255 S. Clark<br/> Chicago, IL 60608 </p> </div> <div class="pure-u-1-2" style="text-align: right;"> <h3>Billed to</h3> <p> <strong>{{invoice.customer}}</strong><br/> {{invoice.contact_name}}<br/> {{invoice.address}}<br/> {{invoice.city_state}} </p> </div> </div> <table class="pure-table pure-table-horizontal"> <thead> <tr> <th>ID</th> <th>Plan Name</th> <th class="text-right">Amount</th> </tr> </thead> <tbody> <tr> <td>{{invoice.plan_id}}</td> <td>{{invoice.plan_name}}</td> <td class="text-right">${{invoice.subtotal}}</td> </tr> <tr> <td></td> <td class="text-right">Subtotal:</td> <td class="text-right">${{invoice.subtotal}}</td> </tr> <tr> <td></td> <td class="text-right">Taxes and Fees:</td> <td class="text-right">${{invoice.fee}}</td> </tr> <tr class="bold"> <td></td> <td class="text-right">Total:</td> <td class="text-right">${{invoice.total}}</td> </tr> </tbody> </table> <div class="footer"> <p>Please make checks payable to <strong>Tiller, Inc</strong>. Invoices are due 30 days after date issued.</p> <p>Thank you for your business!</p> </div>
アプリケーションのスタイルシートにスタイルを追加し、views/layout.hbs
ファイルを開いて、その内容を次のコード スニペットに置き換えてください。これにより Pure がインポートされ、単一列のグリッド レイアウトが作成されます。
<!DOCTYPE html> <html> <head> <title>{{title}}</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" href="https://unpkg.com/[email protected]/build/pure-min.css" integrity="sha384-cg6SkqEOCV1NbJoCu11+bm0NvBRc8IYLRGXkmNrqUBfTjmMYwNKPWBTIKyw9mHNJ" crossorigin="anonymous"> <link rel='stylesheet' href='/stylesheets/style.css' /> </head> <body> <div class="container"> <div class="pure-g"> <div class="pure-u-1"> {{{body}}} </div> </div> </div> </body> </html>
アプリケーションのpublic/style.css file
を開き、次の CSS コード スニペットを追加します。
body { background-color: #f7f7f7; color: #333333; } a { color: #156d6a; } h1 a, h2 a, h3 a { text-decoration: none; } table { width: 100%; } .container { background-color: #ffffff; max-width: 700px; margin: 0 auto; padding: 30px; } .muted { color: #999999; } .bold { font-weight: bold; } .text-right { text-align: right; } .footer p { margin-top: 30px; } .success { background-color: #c0f5f3; color: #0d928d; padding: 10px; } .error { background-color: #f5c0c0; color: #792525; padding: 10px; }
必須ではありませんが、Foxit は PDF を生成するときに HTML ドキュメントからすべてのスタイルをキャプチャするため、請求書にスタイルを追加すると、プロフェッショナルな外観が向上します。
これで、アプリケーションをテストする準備が整いました。コマンド ライン インターフェイスでrun npm
、Web ブラウザを開いてlocalhost:3000/invoices
にアクセスしてください。次のような請求書のリストが表示されます。
「表示」をクリックして各請求書をプレビューします。
最後の 2 つのステップでは、Foxit__ HTML to PDF ツール__ を利用して PDF 請求書を生成します。請求書が生成されたら、送信する前に Nodemailer を使用して請求書を電子メールに添付します。
Foxit の SDK は、HTML ドキュメントまたは URL から PDF ファイルを生成する機能など、PDF の作成と操作のためのさまざまな機能を提供します。 HTML を PDF 実行ファイルにダウンロードしてコンパイルするプロセスが利用可能です
ノードのchild_process
ライブラリには、コマンドライン関数を実行できるexec()
という関数が含まれています。このメソッドは、Foxit から C++ 実行可能ファイルを実行する場合に便利です。 HTML to PDF 実行可能ファイルを実行するには、次のコードで/:id/email
ルートを更新してください。
... router.get('/:id/email', function(req, res) { // Set the executable path and output folder const htmlToPdfPath = '/path/to/foxit/html2pdf'; const outputFolder = __dirname + '/../invoices/'; // Get the invoice const invoice = invoices.find(invoice => invoice.id === req.params['id']); if (!invoice) { res.redirect('/invoices?error=1'); } // Convert the HTML to PDF exec( `${htmlToPdfPath} -html /invoices/${req.params['id']} -o ${outputFolder}${req.params['id']}.pdf`, (err, stdout, stderr) => { if (err || stderr) { console.error(err, stderr); res.redirect('/invoices?error=1'); } else { // For now: log the output file path console.log(`PDF generated and saved to ${outputFolder}${req.params['id']}.pdf`); res.redirect('/invoices?success=1'); } }); });
このコードを実行する前に、 htmlToPdfPath
変数を更新して、 htmltopdf
実行可能ファイルへの正しいパスを反映していることを確認してください。
請求書のリマインダーをメールで送信するには、請求書のリストに戻り、特定の請求書の [リマインダーをメールで送信] をクリックしてください。このアクションにより、Node アプリがhtmltopdf
実行可能ファイルを呼び出すようになります。次に、実行可能ファイルは、Express によって提供される HTML ドキュメントの請求書を PDF ファイルに変換します。作成された PDF ファイルは、Web アプリケーションのinvoices/
ディレクトリにあります。
PDF 請求書を生成できるようになったので、最後のステップはこれらの請求書を顧客に送信することです。
sendmail
コマンドなどのサービスを利用することもできます。
Nodemailer をテストするには、/invoices/:id/email
ルートの「PDF generated and save to…」 console.log
ステートメントのすぐ下に次のコード スニペットを追加します。
... // Construct the message const message = { from: '[email protected]', to: invoice.contact_email, subject: 'Reminder: Your Invoice from Tiller, Inc. is Due', html: `<p>Hey ${invoice.contact_name},</p><p>I just wanted to remind you that your invoice for last month's services is now due. I've attached it here for your convenience.</p><p>Thanks for your business!</p>`, attachments: [ { filename: 'invoice.pdf', path: `${outputFolder}${req.params['id']}.pdf`, } ] }; // Use mailer to send invoice nodemailer .createTransport({jsonTransport: true}) .sendMail(message, function (err, info) { if (err) { res.redirect('/invoices?error=1'); } else { console.log(info.message); res.redirect('/invoices?success=1'); } }); ...
Node アプリケーションを更新し、いずれかの請求書の [電子メール リマインダー] をクリックしてください。今回は、コンソールに JSON として表示される完全な電子メール データ オブジェクトを観察します。
{ "from": { "address": "[email protected]", "name": "" }, "to": [ { "address": "[email protected]", "name": "" } ], "subject": "Reminder: Your Invoice from Tiller, Inc. is Due", "html": "<p>Hey Junia Pretious,</p><p>I just wanted to remind you that your invoice for last month's services is now due. I've attached it here for your convenience.</p><p>Thanks for your business!</p>", "attachments": [ { "content": "JVBERi0xLjMKJcTl8uXrp...", "filename": "invoice.pdf", "contentType": "application/pdf", "encoding": "base64" } ], "headers": {}, "messageId": "<[email protected]>" }
attachments.content
文字列は、エンコードされた PDF ファイルを表します。簡潔にするために、上の例では省略しています。
実際のメールを使用してメールをテストするにはcreateTransport({jsonTransport: true})
呼び出しを次のコード スニペットに置き換えます。
createTransport({ host: "smtp.mailtrap.io", port: 2525, auth: { user: "<YOUR_MAILTRAP_USERID>", pass: "<YOUR_MAILTRAP_PASS>" } })
請求書を電子メールで送信すると、Mailtrap は出力をキャプチャし、PDF 添付ファイルをダウンロードするオプションを提供します。 Mailtrap アカウントにアクセスすると、次の例のような電子メールが表示されます。
アプリを実稼働環境にデプロイする準備ができたら、Mailtrap SMTP 資格情報を実稼働メール サーバーに置き換えます。当社の Web アプリケーションは、請求チームからのリクエストに応じて PDF 請求書を生成し、クライアントに自動的に送信する機能を提供するようになりました。
請求書をオンラインで表示し、PDF として送信するためのソリューションが必要な場合、当社のアプリケーションは信頼できる基盤を提供します。 Foxit の HTML to PDF ツールは PDF を生成するための便利で効率的なオプションですが、他のさまざまなソリューションも提供していることに注意することが重要です。彼らと一緒に
ここでも公開されています。