Develop a Simple PDF Invoicing App for your Business with CASE
Bruno Pérez
Posted on October 2, 2023
In our example, we want to develop a simple invoicing web app as fast as possible for our business. 💰💰💰
For that motive, we are going to use CASE, a simple tool to quickly generate dashboards and add our own custom logic.
This simple guide will follow 4 steps:
- Install CASE
- Generate entities
- Add properties
- Generate a PDF on invoice creation
1. Install CASE
Let's start generating the app with this simple command ✨✨
npx create-case-app my-invoicing-app
This will launch your invoicing app on your browser. You can go to the my-invoicing-app
folder and open the folder with your favorite IDE.
That's it for the install.
2. Generate entities
Our project will need 2 entities: a Customer and an Invoice. Invoices belongs to Customers, and a Customer can have many invoices.
The command to generate a new entity is straightforward:
npm run case:entity customer
and the second one:
npm run case:entity invoice
You should see that the customer.entity.ts
and the invoice.entity.ts
files have been created ! Check out the UI to see those entities in action.
3. Add properties
By default, entities only come with a name
property. We are going to add more to it. Let's start with the customers:
// entites/customer.entity.ts
@Entity({
nameSingular: "customer",
namePlural: "customers",
propIdentifier: "name",
slug: "customers",
})
export class Customer extends CaseEntity {
// Customer logo.
@Prop({
type: PropType.Image,
})
logo: string;
// Customer name.
@Prop({
type: PropType.Text,
seed: () => faker.company.name(),
})
name: string;
// Customer address with a nice seeder for the dummy data.
@Prop({
type: PropType.Text,
seed: () =>
faker.location.streetAddress() +
", " +
faker.location.city() +
", " +
faker.location.state() +
" " +
faker.location.zipCode(),
})
address: string;
}
The @Prop
decorator is getting:
- The type of the property
- The seed function that generates the dummy data
Let's go with the invoices now:
// entities/invoice.entity.ts
@Entity({
nameSingular: "invoice",
namePlural: "invoices",
propIdentifier: "label",
slug: "invoices",
})
export class Invoice extends CaseEntity {
@Prop({
type: PropType.Text,
})
label: string;
@Prop({
type: PropType.Date,
label: "Issue Date",
})
issueDate: string;
// Represents the relation with the Customer entity.
@Prop({
type: PropType.Relation,
options: {
entity: Customer,
},
})
customer: Customer;
// PropType.Currency accepts all currencies, default is USD.
@Prop({
type: PropType.Currency,
})
amount: number;
@Prop({
type: PropType.Currency,
})
taxes: number;
// This prop will store the path of the generated PDF file.
@Prop({
type: PropType.File,
options: {
isHiddenInCreateEdit: true,
},
})
path: string;
}
Now that our props are filled, we can add some dummy data to see it in action:
npm run seed
This will generate a bunch of customers and invoices with the dummy properties. As you can see, we did not specify a seed function for all of those props, by default CASE tries to generate a mock data that corresponds to your type.
4. Generate PDF on insert
Last but not least, we need to generate a PDF on save and store its path as the invoice.path
.
We are going to use the @BeforeInsert() decorator that triggers a function just before our item is inserted to the database:
// entities/invoice.entity.ts
@BeforeInsert()
generatePDF() {
// We need a feature that generates a PDF based on our values and that stores it in the disk and returns the path.
this.path = generateInvoiceInPDF({
reference: this.reference,
label: this.label,
issueDate: this.issueDate,
customerName: this._relations.customer.name,
customerAddress: this._relations.customer.address,
amount: this.amount,
taxes: this.taxes
})
}
Let's create that generateInvoiceInPDF()
function !
We are going to use the jspdf package to create the PDF files. Let's create a utils/pdf.ts
file with this content:
// utils/pdf.ts
import { jsPDF } from "jspdf";
import * as fs from "fs";
export const saveInvoiceInPDF = (content: {
reference: string;
label: string;
issueDate: string;
customerName: string;
customerAddress: string;
amount: number;
taxes: number;
}): string => {
var doc = new jsPDF();
// Title.
doc.setFontSize(28);
doc.text(`Invoice: ${content.reference}`, 20, 25);
// Other content.
const fontSize: number = 14;
const x: number = 35;
doc.setFontSize(fontSize);
doc.text(content.label, 20, x);
doc.text(content.customerName, 20, x + fontSize);
doc.text(content.customerAddress, 20, x + fontSize * 2);
doc.text(content.issueDate, 20, x + fontSize * 3);
doc.text(`Amount: ${content.amount}`, 20, x + fontSize * 4);
doc.text(`Taxes: ${content.taxes}`, 20, x + fontSize * 5);
var data = doc.output();
const storagePath = "./public/storage";
const path = `/invoices/${content.reference}.pdf`;
fs.writeFileSync(storagePath + path, data, "binary");
return path;
};
This code is producing a basic PDF will the invoice information. I am not digging to much on that part in this tutorial but if you want to create a nice PDF, checkout jspdf doc or you can even get directly invoice templates for it.
You can find all that code in the Github repo of this project, Enjoy your coding !
Wanna give some ❤️ ?
If you enjoyed this tutorial and developing with CASE, Star us on Github and like or share this post ! We are actively looking for CASE testers and users to improve our project !
Posted on October 2, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.