A geração de faturas de pagamento é um aspecto crucial de qualquer negócio, e o faturamento digital tornou-se uma prática comum em vários setores. Nesse contexto, os desenvolvedores de aplicativos da Web geralmente enfrentam a tarefa de gerar e enviar faturas em PDF programaticamente.
Esteja você automatizando o processo de geração e notificação de faturas ou desenvolvendo uma interface gráfica do usuário (GUI) para lembrar proativamente os clientes sobre faturas pendentes, o desafio técnico inicial está na geração de faturas em PDF. Embora você possa desenvolver um script personalizado para geração de PDF, isso seria uma tarefa significativa. Embora os serviços baseados na Web sejam convenientes, eles podem não ser adequados se você tiver acordos de confidencialidade com seus clientes, pois o envio de dados para um serviço de terceiros pela Internet pode causar problemas.
Felizmente, a Foxit
Este tutorial irá guiá-lo através do processo de criação de um
Neste tutorial, orientaremos você no processo de criação de um aplicativo da Web que permite que seu departamento de cobrança gerencie faturas não pagas com eficiência. Você criará vários recursos, incluindo uma ferramenta interna para acompanhamento, uma página de exibição de faturas pendentes e uma página de visualização de cada fatura. Os usuários terão a opção de enviar lembretes por e-mail aos clientes com a fatura anexada.
Para este projeto, utilizaremos o
Pré-requisitos:
Para criar um novo aplicativo da web padrão do Express, você precisa usar o gerador de aplicativos :
npx express-generator --git --view=hbs
Isso criará um aplicativo da web com um arquivo .gitignore
e
Em seguida, você precisa adicionar o pacote Nodemailer npm e instalar as dependências do Express:
npm i nodemailer && npm i
O aplicativo padrão gerado pelo Express fornece dois arquivos de rota: /routes/index.js
e /routes/users.js
. Remova a rota users.js
e crie um novo arquivo de rota chamado invoices.js
. Adicione esta nova rota ao seu arquivo app.js
e remova o usersRoute
:
... var indexRouter = require('./routes/index'); var invoicesRouter = require('./routes/invoices'); var app = express(); ... app.use('/', indexRouter); app.use('/invoices', invoicesRouter); ...
A maior parte do trabalho neste aplicativo está dentro do roteador de faturas.
Antes de criar a rota, você precisará de alguns dados que possa usar. Em um aplicativo real, você provavelmente se conectará a um banco de dados, mas, para fins de demonstração, adicione os dados da fatura a um arquivo JSON.
Crie um novo arquivo em /data/invoices.json
e adicione o seguinte:
[ { "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 } ]
As três faturas que você pode ver acima contêm dados de cliente, plano e cobrança que o ajudarão a gerar uma fatura na próxima seção.
O arquivo routes/invoices.js
criará três novas rotas em seu aplicativo:
/invoices
– Uma lista de todas as faturas do arquivo de dados simples acima./invoices/:id
– Uma visualização da fatura para que os usuários possam ver como será a fatura antes de enviá-la ao cliente./invoices/:id/email
– Um terminal que gera e envia a fatura em PDF para o e-mail de contato que está arquivado.
Abra o arquivo invoices.js
e adicione o seguinte trecho de código para definir as duas primeiras rotas:
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;
Seu aplicativo está quase pronto para teste, mas primeiro você precisa criar os dois arquivos de exibição.
O Express separa a lógica e a apresentação em routes/
e views/
. Agora adicione dois novos arquivos ao diretório views/
: invoice-list.hbs
e invoice-single.hbs
.
Além disso, adicione o seguinte ao seu arquivo 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}}
Abra o arquivo invoice-single.hbs
e adicione isto:
<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>
Para adicionar estilos à folha de estilo do seu aplicativo e incorporar oviews/layout.hbs
e substitua seu conteúdo pelo trecho de código a seguir. Isso importará Pure e criará um layout de grade de coluna única:
<!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>
Abra public/style.css file
de seu aplicativo e adicione o seguinte trecho de código 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; }
Embora não seja obrigatório, adicionar estilos às suas faturas pode melhorar sua aparência profissional, pois o Foxit captura todo o estilo do seu documento HTML ao gerar PDFs.
Agora, você está pronto para testar seu aplicativo. run npm
start em sua interface de linha de comando e abra seu navegador da Web em localhost:3000/invoices
Você verá uma lista de faturas semelhante à seguinte:
Clique em “Visualizar” para visualizar cada fatura.
Nas duas etapas finais, você utilizará a ferramenta Foxit__ HTML para PDF __ para gerar faturas em PDF. Assim que as faturas forem geradas, você irá anexá-las a um e-mail usando o Nodemailer antes de enviá-las.
O SDK da Foxit oferece uma variedade de funcionalidades para criação e manipulação de PDF, incluindo a capacidade de gerar um arquivo PDF a partir de um documento HTML ou URL. O processo de download e compilação do executável HTML para PDF está disponível
A biblioteca child_process
do Node inclui uma função chamada exec()
que permite executar funções de linha de comando. Este método é útil para executar executáveis C++ da Foxit. Para executar o executável HTML para PDF, atualize sua rota /:id/email
com o seguinte código:
... 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'); } }); });
Antes de executar este código, certifique-se de atualizar a variável htmlToPdfPath
para refletir o caminho correto para seu executável htmltopdf
.
Para enviar um lembrete por e-mail para uma fatura, volte à sua lista de faturas e clique em “Lembrete por e-mail” para qualquer fatura específica. Essa ação acionará o aplicativo Node para chamar o executável htmltopdf
. O executável, por sua vez, converterá a fatura do documento HTML servido pelo Express em um arquivo PDF. Você pode localizar o arquivo PDF resultante no diretório invoices/
de seu aplicativo da web.
Agora que você pode gerar faturas em PDF, a etapa final é enviar essas faturas para seus clientes.
sendmail
do seu servidor como alternativas.
Para testar o Nodemailer, você pode utilizar oconsole.log
em sua rota /invoices/:id/email
:
... // 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'); } }); ...
Atualize seu aplicativo Node e clique em “Email Reminder” para qualquer uma das faturas. Desta vez, você observará o objeto de dados de email completo exibido como JSON em seu console.
{ "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]>" }
A string attachments.content
representa o arquivo PDF codificado. Por uma questão de brevidade, trunquei-o no exemplo acima.
Para testar o e-mail usando um realcreateTransport({jsonTransport: true})
pelo seguinte trecho de código:
createTransport({ host: "smtp.mailtrap.io", port: 2525, auth: { user: "<YOUR_MAILTRAP_USERID>", pass: "<YOUR_MAILTRAP_PASS>" } })
Ao enviar a fatura por e-mail, o Mailtrap capturará a saída e fornecerá a você a opção de baixar o anexo em PDF. Ao acessar sua conta Mailtrap, você encontrará um e-mail semelhante ao exemplo abaixo:
Quando estiver pronto para implantar seu aplicativo na produção, substitua as credenciais Mailtrap SMTP por um servidor de e-mail de produção. Nosso aplicativo da web agora oferece a capacidade de gerar e enviar automaticamente faturas em PDF para clientes mediante solicitação de nossa equipe de cobrança.
Se você precisar de uma solução para apresentar faturas online e enviá-las como PDFs, nosso aplicativo oferece uma base confiável. Embora a ferramenta HTML para PDF da Foxit seja uma opção conveniente e eficiente para gerar PDFs, é importante observar que eles também oferecem várias outras soluções. Com seus
Publicado também aqui .