Node.js project to create German PDF invoices from a Google Sheets document.
Deployable to Google App Engine and automatable using Apps Script.
See the invoice examples, and start your own accounting spreadsheet.
Related project: Cloud Reports for PayPal
- Track customers, products and orders in a spreadsheet
- Verify order data and generate invoice dates and numbers
- Export invoices in PDF format (only in German language)
- Include VAT notices for Kleinunternehmer or reverse-charge procedures
- Upload invoices to Google Drive
- Run in Google Cloud Platform for a fully automated solution
- VAT calculation
- Invoice translations
- Other invoice formats
⚠️ Disclaimer: I am not a tax or financial advisor, and as stated in the license, I am by no means responsible for the use of this software. Please note that this was made for my own needs and may not fit your use case.
You will need Node.js >16 installed in your environment.
-
Go to the official Node.js website and download the installer. Be sure to have
git
available in your PATH,npm
might need it (you can find git here). -
Install nodejs and npm with apt install, just run the following commands:
$ sudo apt install nodejs $ sudo apt install npm
-
You can find more information about the installation on the official Node.js website and the official NPM website.
If the installation was successful, you should be able to run the following commands:
$ node --version
v18.12.1
$ npm --version
8.19.2
Download the project and install its dependencies:
$ git clone https://github.com/joanroig/cloud-invoicing
$ cd cloud-invoicing
$ npm install
Create a .env
file in the root directory based on the provided .env.example
, then you will need to:
- Create a Google Cloud project, activate the Google Sheets API, and create a service account as explained in this guide.
- Put the
private_key
andclient_email
values from the JSON of the service account you obtained in the previous step in the.env
file. - Do a copy of the provided Google Sheets template.
- Share the spreadsheet with the email of your service account (this allows the service account to access your spreadsheet). To do this, go to your copy of the template and press the share button, then enter the service account email and send the invitation.
- Put the spreadsheet ID from the URL of your Google Sheets document into the
spreadsheet_id
of the.env
file, the ID should look similar to this:1JRJ3KQetNAAPzsJat-JH7iIzc3OGumWQyFL5MZfm5UU
- Create a folder in Drive and share the folder with the email of your service account.
- Put the folder ID from the URL of your Drive folder into the
drive_folder_id
of the.env
file.
Note: You can disable the upload of invoices by setting the property upload-to-drive
to false
in config/default.json
.
Add some data in the spreadsheet and activate the checkboxes of the Run
column in the Orders
tab for each invoice you want to generate.
Then, run the following command and check the console output and the out folder for checking your results:
$ npm start:once
The project can run as a server to execute the invoice generation on demand. You can test it locally by executing the following commands, and then access http://localhost:8080 in your browser every time you want to trigger the generation:
$ npm build
$ npm start
After verifying locally that everything works, you may want to automate the invoice generation in the cloud.
⚠️ Developing and running the project on GCP did not exceed my account's free quotas, but please note that you must enable billing at your own risk. You can configure a Cloud Function to prevent unwanted billings by following the official documentation or this video.
We need to have a Google Cloud project to upload the server to App Engine, then create an Apps Script, secure the communication between Apps Script and App Engine, and finally trigger the invoice generation from the spreadsheet.
To provide a better understanding of all actors, the process flow looks like this:
- The user uses
Google Sheets
to triggerApps Script
Apps Script
sends an HTTP GET request toApp Engine
which verifies the user viaIAP
App Engine
starts the invoice generation, which uses aService Account
to:- Read, verify and update
Google Sheets
data - Upload invoices to
Google Drive
- Read, verify and update
Simplified GCP architecture diagram
Following the guide will require to enable multiple Google Cloud APIs. You will be asked to enable them when needed via CLI or via the Google Cloud website:
- Cloud Logging API
- Cloud Build API
- Google Sheets API
- Google Drive API
- Cloud Identity-Aware Proxy API
Extra APIs used to prevent unwanted billings:
- Cloud Billing API
- Cloud Pub/Sub API
- Cloud Functions API
The App Engine is where the production server will be deployed and where the invoices will be generated. It will be your own secure cloud environment by protecting the access with IAP (Identity-Aware Proxy).
- Enable billing for your Google Cloud project.
- Install the Google Cloud CLI, run
gcloud init
in the project root folder and connect it to your project. - Update the
project_id
of the.env
file with the Project ID of your Google Cloud project (get it here). - Run the command
npm gcloud:deploy
in the root directory to upload the Node.js project. The build folder will be built and then deployed, if you execute the commandgcloud app deploy
remember to build the project before. - Enable IAP for the project (create OAuth consent screen if asked) and toggle IAP for your App Engine app. Then select the App Engine app row and add the Gmail you would like to use to access the Google Sheets document by pressing the "Add Principal" button. Assign the role
IAP-secured Web App User
. Repeat this step for every IAP-allowed user you need.
Now try to access the server URL (shown in the App Engine Dashboard, it ends with .appspot.com
), login with the Gmail used in the previous step, and you should be able to access the deployed server.
The invoice generation will be triggered every time an IAP-allowed user does an HTTP GET request to the server URL. In the next steps, you will configure the trigger from Google Sheets.
An Apps Script of a Google Sheets document acts like a plugin that extends its functionality.
For the purpose of this project, the script will be responsible for sending requests to the App Engine server on behalf of an allowed user, which is needed to go through IAP.
- Open your spreadsheet, in the menu open
Extensions > Apps Script
- Go to the settings of the Apps Script and toggle the
Show "appsscript.json" manifest file in editor
checkbox. - Also in settings, assign the Project Number of the Google Cloud project (get it here).
- Go to credentials and create a new OAuth 2.0 Client ID of type
Web application
. You will need to provide a redirect URL with the formathttps://script.google.com/macros/d/{SCRIPT_ID}/usercallback
, where SCRIPT_ID is the ID provided in the Apps Script settings. More information here. - Copy the
appsscript.json
and theCode.gs
file from the examples/Apps Script folder in the Apps Script files. - Edit the
Code.gs
file to add your own credentials:- Use the Client ID and Client Secret for the values of
CLIENT_ID
andCLIENT_SECRET
respectively. - Use the Client ID of the
IAP-App-Engine-app
(find it in credentials under the OAuth 2.0 Client IDs) for the value ofIAP_CLIENT_ID
. - The
IAP_URL
is the server URL that ends with.appspot.com
.
- Use the Client ID and Client Secret for the values of
Now you should be able to find a menu called Generate Invoices
in your spreadsheet (refresh the page if not). Click the Run now
option.
On the first run you will need to login with your gmail and enable the access and check the Apps Script executions for details (you should find an URL to enable the access in the execution log).
Something is not working? Then check the latest run logs on Google App Engine by running the following command:
$ npm run gcloud:logs
Remember that you can always redeploy by running:
$ npm run gcloud:deploy
⚠️ The provided deploy script always replaces the same version on App Engine to prevent exceeding the Cloud Storage free thresholds. This is managed by adding--version=staging
in the deploy command. If you did some deploys without the flag, you can remove old versions of your project here.
You can fork and further extend the project to adapt it to your needs.
The model of the Node.js application is bounded to the headers of the provided Google Sheets template. Remember to modify the model enums if you change any header in the spreadsheet.
Issue that appeared when launching the invoice generation after deploying the complete source code of the project. It is solved by pre-building the project and uploading only the necessary files specified in .gcloudignore
. More information here.
This may happen if you did not activate the Google Sheets API, if you did not share the spreadsheet document with your service account, or if you did not introduce the proper data in the .env
file. Look carefully if you copied the private_key
value with comma in the end because this will make it invalid.
All trademarks, logos and brand names are the property of their respective owners.
- App Scripts to IAP from: https://github.com/googleworkspace/apps-script-oauth2/blob/master/samples/CloudIdentityAwareProxy.gs
- pdfmake template based on: https://gist.github.com/maxkostinevich/c26bfb09450341ad37c1bd6c2cc51bb2
- Logger based on: https://gist.github.com/euikook/d72a40b3864856af57a6dcbec9d97007