Develop a Simple PDF Invoicing App for your Business with CASE
Tags: crud, pdf, typescript, dashboard
Published: 2023-10-02 15:14:11

---
title: Develop a Simple PDF Invoicing App for your Business with CASE
published: true
description: We are using CASE to create a simple web app that generates PDF invoices.
tags: CRUD, pdf, typescript, dashboard
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wa87v5o7ed3pc706bl6q.png
# Use a ratio of 100:42 for best results.
# published_at: 2023-10-02 14:12 +0000
---

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](https://case.app), a simple tool to quickly generate dashboards and add our own custom logic. 

This simple guide will follow 4 steps:

1. Install CASE
2. Generate entities
3. Add properties
4. 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
```

![CASE dashboard login page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oaayi1gxz3cj8zxemds9.png)

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**:

```js
// 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](https://docs.case.app/property-types) of the property
- The [seed function](https://docs.case.app/dummy-data) that generates the dummy data

Let's go with the **invoices** now:

```js
// 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.

![Form to create an invoice](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4zfkt7lampbjc52o14v4.png)

## 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:

```js
// 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](https://www.npmjs.com/package/jspdf) package to create the PDF files. Let's create a `utils/pdf.ts` file with this content: 

```js
// 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](https://raw.githack.com/MrRio/jsPDF/master/docs/index.html) or you can even get directly [invoice templates](https://github.com/edisonneza/jspdf-invoice-template) for it.

You can find all that code in the [Github repo of this project](https://github.com/brunobuddy/case-demo-invoicing-app), Enjoy your coding !

## Wanna give some ❤️ ?

If you enjoyed this tutorial and developing with CASE, [Star us on Github](https://github.com/casejs/CASE) and like or share this post ! We are actively looking for CASE testers and users to improve our project !