Build an automated invoice generator application
Fullstack Developer and Tech Author
As a software engineer and technical content creator, I work with a lot of companies on many different contracts. To get paid for my work, most companies require that I send an invoice. Sometimes they want one daily, at the end of the week, or even when the project has been completed.
Sending an invoice to my clients is crucial because it determines when and if I will get paid on time. If this sounds like a repetitive task that can eat deep into my productive hours, you are right. To keep my focus on getting my tasks done, I decided to set up an automated invoice workflow.
In this tutorial, I will show you how you can effectively generate and send an invoice to your client using the Invoice generator API and scheduled pipelines.
Prerequisites
For this tutorial, you will need:
- Node.js installed on your system
- A CircleCI account
- A GitHub account
- A Mailtrap account
Cloning the demo application
For this tutorial, I have set up a simple Node.js project with an existing generateInvoice()
function defined within the invoice.js
file. To get started, run this command:
git clone https://github.com/yemiwebby/automated-invoice-starter.git automated-invoice
This clones the project into an automated-invoice
folder within your development directory (or whenever you ran the command from).
Next, go to the newly created project and install its dependencies:
cd automated-invoice
npm install
Generating an invoice locally
To confirm that the project works as expected, run the application locally. Generate an invoice by running:
node invoice.js
The output should show that an invoice has been generated and saved within your project’s directory.
Saved invoice to invoice.pdf
Now you can set up sending the invoice as an attachment.
Sending the invoice as an attachment
Sending emails from any application deployed on the internet requires an SMTP server and a few other configurations. For this tutorial, I used Mailtrap, which offers free SMTP services for experimenting with email-sending functionality. If you have not yet done so, create a Mailtrap account here.
Next, go to the inbox page to view your SMTP and POP3 credentials.
Creating an environment variable
Create a .env
file at the root of the project using this command:
cp .env.sample .env
Replace the placeholders MAILTRAP_USERNAME
and MAILTRAP_PASSWORD
with the values on your mailtrap.io dashboard.
Sending email with the invoice as an attachment
The application uses the nodemailer module to send email messages with the invoice as an attachment. The module is already installed. All you need to do is open the invoice.js
file and replace its content with this:
var https = require("https");
var fs = require("fs");
require("dotenv").config();
var nodemailer = require("nodemailer");
const generateInvoice = (invoice, filename, success, error) => {
var postData = JSON.stringify(invoice);
var options = {
hostname: "invoice-generator.com",
port: 443,
path: "/",
method: "POST",
headers: {
"Content-Type": "application/json",
"Content-Length": Buffer.byteLength(postData),
},
};
var file = fs.createWriteStream(filename);
var req = https.request(options, function (res) {
res
.on("data", function (chunk) {
file.write(chunk);
})
.on("end", function () {
file.end();
if (typeof success === "function") {
success();
sendEmail(filename);
}
});
});
req.write(postData);
req.end();
if (typeof error === "function") {
req.on("error", error);
}
};
const sendEmail = (file) => {
var transport = nodemailer.createTransport({
host: "smtp.mailtrap.io",
port: 2525,
auth: {
user: process.env.MAILTRAP_USERNAME,
pass: process.env.MAILTRAP_PASSWORD,
},
});
var mailOptions = {
from: "invoice@me.com",
to: "sample@me.com",
subject: "Invoice for weekly payments via Node.js",
text: "Find attached the weekly invoice from me. Thanks",
attachments: [
{
path: file,
},
],
};
transport.sendMail(mailOptions, function (error, info) {
if (error) {
console.log(error);
} else {
console.log("Email sent: " + info.response);
}
});
};
let invoice = {
logo: "http://invoiced.com/img/logo-invoice.png",
from: "Invoiced\n701 Brazos St\nAustin, TX 78748",
to: "Awesome Company / Client",
currency: "usd",
number: "INV-0001",
payment_terms: "Due for payment",
items: [
{
name: "Weekly technical content",
quantity: 1,
unit_cost: 500,
},
{
name: "Employee Portal Management",
quantity: 1,
unit_cost: 1000,
},
],
fields: {
tax: "%",
},
tax: 5,
notes: "Thanks for being an awesome customer!",
terms: "Looking forward to the payments",
};
generateInvoice(
invoice,
"invoice.pdf",
() => console.log("Saved invoice to invoice.pdf"),
(err) => console.log(err)
);
The change to this code block modifies the file by requiring the nodemailer
module. A sendEmail()
function is created, which takes the generated file as an argument and sends it as an attachment. The attachment is sent to the recipient defined with the mailOptions
variable. The *generateInvoice()*
function calls the sendEmail()
method and passes the appropriate argument to it.
Now, run the application again using:
node invoice.js
The output is:
Saved invoice to invoice.pdf
Email sent: 250 2.0.0 Ok: queued
This output shows that the invoice was generated and sent successfully. Go to your Mailtrap inbox to view the message.
Click Attachments to open the invoice.
Adding the CircleCI configuration file
Next, you need to add the pipeline configuration for CircleCI to automate creating and sending invoices.
At the root of your project, create a folder named .circleci
. In that folder, create a file named config.yml
. In the newly created file, add this configuration:
version: 2.1
jobs:
build:
working_directory: ~/project
docker:
- image: cimg/node:17.4.0
steps:
- checkout
- run:
name: Update NPM
command: "sudo npm install -g npm"
- restore_cache:
key: dependency-cache-{{ checksum "package-lock.json" }}
- run:
name: Install Dependencies
command: npm install
- save_cache:
key: dependency-cache-{{ checksum "package-lock.json" }}
paths:
- ./node_modules
- run:
name: Run the application
command: node invoice.js
background: true
This script pulls in a Node.js Docker image and installs the project’s dependencies. The last step runs the command to generate and send out the invoice.
Next, set up a repository on GitHub and link the project to CircleCI. Review Pushing a project to GitHub for step-by-step instructions.
Log in to your CircleCI account. If you signed up with your GitHub account, all your repositories will be available on your project’s dashboard.
Click Set Up Project for your automated-invoice
project
You will be prompted with a couple of options regarding the configuration file. Select the use the .circleci/config.yml in my repo
option. Enter the name of the branch where your code is housed on GitHub, then click the Set Up Project button.
Your first workflow will start running, but it will fail. This is because you have not provided configuration details for Mailtrap. You can fix that now.
Click the Project Settings button, then click Environment Variables. Add these two new variables:
MAILTRAP_USERNAME
MAILTRAP_PASSWORD
Rerun the workflow. The invoice will be generated and the email will be sent.
Setting up a scheduled pipeline
Often, a CircleCI workflow is executed automatically once you push your code to a repository. For this tutorial, though, the goal is to run this pipeline at a specific interval, preferably every week. Using scheduled pipelines on CircleCI, you can configure your pipeline like a cron
job and run it at an interval. There are two ways to do this:
- Using API
- Using project settings
For this tutorial, we will configure pipeline triggers using the project settings. From your project, go to Project Settings. Click Triggers from the menu on the left sidebar.
On the Triggers page, click Add Scheduled Trigger. Fill in the trigger form to configure the pipeline.
Here is what to enter for these fields:
Trigger name
is the unique schedule name.Trigger Source
indicates the source that will trigger the pipeline (for this tutorial it isscheduled
).Trigger description
is an optional field you can use to add more information about the trigger.Timetable
defines when and how often to run the scheduled pipelines.Pipeline Parameters
are variables declared within the parameter key. They can be used to check when to run the pipeline, but you do not need them for this tutorial.Attribution
specifies the user associated with the schedule. It can be the system for a neutral actor. Or it can be current, which takes your current user’s permissions (as per the token you are using).
These settings configure the pipeline to run every five minutes, every day. Click Save Trigger to activate it.
You can view the created trigger from the Project Triggers page.
Return to the Pipelines page and wait for at least five minutes. Your pipeline will be triggered by the CircleCI system.
You can also check your Mailtrap inbox to review the generated invoices.
Conclusion
In this tutorial, you were able to generate an invoice on the fly and send it to a specific recipient without much hassle. To automate the invoicing process, we used the scheduled pipeline features from CircleCI.
Because what you implemented here is minimal, it can be easily included in an existing project or a new one. Another advantage of this workflow is that you can maintain continuous integration and deployments for your project while CircleCI handles invoicing automatically. This addition removes a tedious manual task and automates it so that you can spend more time developing and deploying applications.
I hope that you found this tutorial helpful. The complete source code can be found here on GitHub.