A plugin for generating PDF invoices for placed orders.
- View created invoices via the Vendure admin
- Set templates per channel
- Preview HTML templates before using them
- Download multiple pdf's as zip
- Easily implement your own storage strategy
- Pass custom data to your custom HTML template
- Add the following config to your
vendure-config.ts
:
plugins: [
InvoicePlugin.init({
// Used as host for creating downloadUrls
vendureHost: 'http://localhost:3106',
}),
AdminUiPlugin.init({
port: 3002,
route: 'admin',
app: compileUiExtensions({
outputPath: path.join(__dirname, '__admin-ui'),
extensions: [InvoicePlugin.ui],
devMode: true,
}),
}),
];
- Run a migration, because this plugin adds 2 entities to the db.
- Start Vendure and navigate to the admin
- Make sure you have the permission
AllowInvoicesPermission
- Go to Sales > Invoices.
- Unfold the
settings
accordion. - Check the checkbox to enable invoice generation for the current channel on order placement.
- A default HTML template is set for you. Click the
Preview
button to view a sample PDF invoice.
The bottom table holds an overview of already generated invoices.
# PhantomJS fix https://github.com/bazelbuild/rules_closure/issues/351
ENV OPENSSL_CONF=/dev/null
Invoices are generated via the worker and are not available when order confirmations are send. What you can do is add the following link to your email:
https://<your server>/invoices/e2e-default-channel/[email protected]
.
The server will check if the ordercode belongs to the given channel AND the given customer emailaddress. If so, it will return the invoice.
The Vendure admin can be used to view created invoices and change invoice settings. Make sure you
have AllowInvoicesPermission
for the 'Invoices' menu item to show.
This plugin also includes a strategy for storing invoices in Google Storage. Install the gcloud package and set the following config:
yarn add @google-cloud/storage
InvoicePlugin.init({
vendureHost: 'http://localhost:3050',
storageStrategy: new GoogleStorageInvoiceStrategy({
bucketName: 'bucketname',
}),
});
- Enable the service account IAM in Google Cloud Console
- Add role 'Service account token creator' to the Cloud Run service account
The strategy will use the projectId and credentials in from your environment, so if you are running in Cloud Functions or Cloud Run you should be fine with this config.
InvoicePlugin.init({
vendureHost: 'http://localhost:3050',
storageStrategy: new GoogleStorageInvoiceStrategy({
bucketName: 'bucketname',
storageOptions: {
keyFilename: 'key.json',
},
}),
});
This is needed to generate signedUrls, which are used to give customers temporary access to a file on Storage. See this info for info about locally using signedUrls: googleapis/nodejs-storage#360
Implement your own strategy for storing invoices by implementing one of these interfaces:
RemoteStorageStrategy
for storing PDF files on an external platform like Google Cloud or S3.
It redirects the user to a public/authorized URL for the user to download the invoice PDF.
import { RemoteStorageStrategy, zipFiles } from 'vendure-plugin-invoices';
export class YourRemoteStrategy implements RemoteStorageStrategy {
async save(
tmpFile: string,
invoiceNumber: number,
channelToken: string
): Promise<string> {
// Save the invoice in your favorite cloud storage. The string you return will be saved as unique reference to your invoice.
// You should be able to retrieve the file later with just the unique reference
return 'unique-reference';
}
async getPublicUrl(invoice: InvoiceEntity): Promise<string> {
// Most cloud based storages have the ability to generate a signed URL, which is available for X amount of time.
// This way the downloading of invoices does not go through the vendure service
return 'https://your-signed-url/invoice.pdf';
}
async streamMultiple(
invoices: InvoiceEntity[],
res: Response
): Promise<ReadStream> {
// zip files and return stream
const zipped = zipFiles(files);
return createReadStream(zipped);
}
}
LocalFileStrategy
streams the invoice through the Vendure service to the user.
import { LocalStorageStrategy, zipFiles } from 'vendure-plugin-invoices';
export class YourLocalStrategy implements LocalStorageStrategy {
async save(tmpFile: string, invoiceNumber: number, channelToken: string) {
// save the tmpFile somewhere
return 'new/path.pdf';
}
async streamMultiple(
invoices: InvoiceEntity[],
res: Response
): Promise<ReadStream> {
// make a zip of your files
const zipFile = await zipFiles(files);
return createReadStream(zipFile);
}
async streamFile(invoice: InvoiceEntity, res: Response): Promise<ReadStream> {
// stream a single PDF to the user
return createReadStream(invoice.storageReference);
}
}
Implement the DataStrategy
to pass custom data to your template or generate custom invoicenumbers:
export class DefaultDataStrategy implements DataStrategy {
async getData({
ctx,
injector,
order,
latestInvoiceNumber,
}: DataFnInput): Promise<InvoiceData> {
// Do something with the data
return {
invoiceNumber: String(Math.floor(Math.random() * 90000) + 10000),
customerEmail: 'just used for admin display',
order,
someCustomField: '2022',
};
}
}
You can access this data in your HTML template using Handlebars.js:
<h1>{{ someCustomField }}</h1>
Contributions always welcome! Just create a PR on Github. The commands you need:
# Run e2e test
yarn test
# Start server with auto-reloading admin UI
yarn start
# Serve auto-reloading backend. This sometimes messes with phantom processes
# from admin ui compilation. You might want to disable admin compilation
# in dev-server.ts if you use this
yarn serve
Enjoy the Pinelab Vendure plugins? Consider becoming a sponsor.
Or check out pinelab.studio for more articles about our integrations.